Add unit tests for ProfileRepository and implement new shaders

- Created `NativeAssetsManifest.json` and added font and shader assets for unit tests.
- Implemented `ink_sparkle.frag` and `stretch_effect.frag` shaders for visual effects.
- Developed unit tests for `ProfileRepository` to validate API interactions for fetching and updating user profiles.
- Utilized Mockito for mocking API client responses in tests.
This commit is contained in:
Nils-Johan Gynther
2026-04-23 17:50:48 +02:00
parent b92ada1f30
commit aefc8804ad
12 changed files with 208 additions and 0 deletions
@@ -0,0 +1,142 @@
//
// Generated file. Do not edit.
// This file is generated from template in file `flutter_tools/lib/src/flutter_plugins.dart`.
//
// @dart = 3.3
import 'dart:io'; // flutter_ignore: dart_io_import.
import 'package:file_picker/file_picker.dart' as file_picker;
import 'package:shared_preferences_android/shared_preferences_android.dart' as shared_preferences_android;
import 'package:file_picker/file_picker.dart' as file_picker;
import 'package:shared_preferences_foundation/shared_preferences_foundation.dart' as shared_preferences_foundation;
import 'package:file_picker/file_picker.dart' as file_picker;
import 'package:path_provider_linux/path_provider_linux.dart' as path_provider_linux;
import 'package:shared_preferences_linux/shared_preferences_linux.dart' as shared_preferences_linux;
import 'package:file_picker/file_picker.dart' as file_picker;
import 'package:shared_preferences_foundation/shared_preferences_foundation.dart' as shared_preferences_foundation;
import 'package:file_picker/file_picker.dart' as file_picker;
import 'package:path_provider_windows/path_provider_windows.dart' as path_provider_windows;
import 'package:shared_preferences_windows/shared_preferences_windows.dart' as shared_preferences_windows;
@pragma('vm:entry-point')
class _PluginRegistrant {
@pragma('vm:entry-point')
static void register() {
if (Platform.isAndroid) {
try {
file_picker.FilePickerIO.registerWith();
} catch (err) {
print(
'`file_picker` threw an error: $err. '
'The app may not function as expected until you remove this plugin from pubspec.yaml'
);
}
try {
shared_preferences_android.SharedPreferencesAndroid.registerWith();
} catch (err) {
print(
'`shared_preferences_android` threw an error: $err. '
'The app may not function as expected until you remove this plugin from pubspec.yaml'
);
}
} else if (Platform.isIOS) {
try {
file_picker.FilePickerIO.registerWith();
} catch (err) {
print(
'`file_picker` threw an error: $err. '
'The app may not function as expected until you remove this plugin from pubspec.yaml'
);
}
try {
shared_preferences_foundation.SharedPreferencesFoundation.registerWith();
} catch (err) {
print(
'`shared_preferences_foundation` threw an error: $err. '
'The app may not function as expected until you remove this plugin from pubspec.yaml'
);
}
} else if (Platform.isLinux) {
try {
file_picker.FilePickerLinux.registerWith();
} catch (err) {
print(
'`file_picker` threw an error: $err. '
'The app may not function as expected until you remove this plugin from pubspec.yaml'
);
}
try {
path_provider_linux.PathProviderLinux.registerWith();
} catch (err) {
print(
'`path_provider_linux` threw an error: $err. '
'The app may not function as expected until you remove this plugin from pubspec.yaml'
);
}
try {
shared_preferences_linux.SharedPreferencesLinux.registerWith();
} catch (err) {
print(
'`shared_preferences_linux` threw an error: $err. '
'The app may not function as expected until you remove this plugin from pubspec.yaml'
);
}
} else if (Platform.isMacOS) {
try {
file_picker.FilePickerMacOS.registerWith();
} catch (err) {
print(
'`file_picker` threw an error: $err. '
'The app may not function as expected until you remove this plugin from pubspec.yaml'
);
}
try {
shared_preferences_foundation.SharedPreferencesFoundation.registerWith();
} catch (err) {
print(
'`shared_preferences_foundation` threw an error: $err. '
'The app may not function as expected until you remove this plugin from pubspec.yaml'
);
}
} else if (Platform.isWindows) {
try {
file_picker.FilePickerWindows.registerWith();
} catch (err) {
print(
'`file_picker` threw an error: $err. '
'The app may not function as expected until you remove this plugin from pubspec.yaml'
);
}
try {
path_provider_windows.PathProviderWindows.registerWith();
} catch (err) {
print(
'`path_provider_windows` threw an error: $err. '
'The app may not function as expected until you remove this plugin from pubspec.yaml'
);
}
try {
shared_preferences_windows.SharedPreferencesWindows.registerWith();
} catch (err) {
print(
'`shared_preferences_windows` threw an error: $err. '
'The app may not function as expected until you remove this plugin from pubspec.yaml'
);
}
}
}
}
+3
View File
@@ -11,6 +11,9 @@ COPY . .
# Generate localizations (ARB -> Dart) before the main build. # Generate localizations (ARB -> Dart) before the main build.
RUN flutter gen-l10n RUN flutter gen-l10n
# Run tests
RUN flutter test
# Inject API base URL at build time via --dart-define. # Inject API base URL at build time via --dart-define.
# Default to same-origin /api to avoid mixed-content in HTTPS deployments. # Default to same-origin /api to avoid mixed-content in HTTPS deployments.
ARG API_BASE_URL=/api ARG API_BASE_URL=/api
@@ -0,0 +1 @@
{"format-version":[1,0,0],"native-assets":{}}
Binary file not shown.
@@ -0,0 +1 @@
[{"family":"MaterialIcons","fonts":[{"asset":"fonts/MaterialIcons-Regular.otf"}]}]
Binary file not shown.
@@ -0,0 +1 @@
{"format-version":[1,0,0],"native-assets":{}}
+1
View File
@@ -26,6 +26,7 @@ dev_dependencies:
sdk: flutter sdk: flutter
flutter_lints: ^4.0.0 flutter_lints: ^4.0.0
build_runner: ^2.4.9 build_runner: ^2.4.9
mockito: ^5.4.4
flutter: flutter:
uses-material-design: true uses-material-design: true
@@ -0,0 +1,59 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:recipe_flutter/core/api/api_client.dart';
import 'package:recipe_flutter/core/api/api_exception.dart';
import 'package:recipe_flutter/features/profile/data/profile_repository.dart';
class MockApiClient extends Mock implements ApiClient {}
void main() {
group('ProfileRepository', () {
late ProfileRepository profileRepository;
late MockApiClient mockApiClient;
setUp(() {
mockApiClient = MockApiClient();
profileRepository = ProfileRepository(mockApiClient);
});
group('getProfile', () {
test('should return profile data when API call is successful', () async {
final expectedProfile = {'username': 'testuser', 'email': 'test@example.com'};
when(mockApiClient.getJson('/api/profile')).thenAnswer((_) async => expectedProfile);
final result = await profileRepository.getProfile();
expect(result, expectedProfile);
verify(mockApiClient.getJson('/api/profile')).called(1);
});
test('should throw ApiException when API call fails', () async {
when(mockApiClient.getJson('/api/profile')).thenThrow(ApiException('Failed to fetch profile'));
expect(() => profileRepository.getProfile(), throwsA(isA<ApiException>()));
verify(mockApiClient.getJson('/api/profile')).called(1);
});
});
group('updateProfile', () {
test('should return updated profile data when API call is successful', () async {
final profileData = {'username': 'newuser', 'email': 'new@example.com'};
final expectedProfile = {'username': 'newuser', 'email': 'new@example.com'};
when(mockApiClient.patchJson('/api/profile', profileData)).thenAnswer((_) async => expectedProfile);
final result = await profileRepository.updateProfile(profileData);
expect(result, expectedProfile);
verify(mockApiClient.patchJson('/api/profile', profileData)).called(1);
});
test('should throw ApiException when API call fails', () async {
final profileData = {'username': 'newuser', 'email': 'new@example.com'};
when(mockApiClient.patchJson('/api/profile', profileData)).thenThrow(ApiException('Failed to update profile'));
expect(() => profileRepository.updateProfile(profileData), throwsA(isA<ApiException>()));
verify(mockApiClient.patchJson('/api/profile', profileData)).called(1);
});
});
});
}