feat(unionflow): ajout Spec-Kit, constitution, mission mutuelles
- Config Spec-Kit pour Spec-Driven Development - CONSTITUTION.md + .specify/memory/constitution.md - Commandes Cursor /speckit.*, règles projet - Mission: associations + mutuelles d'épargne et de financement - .gitignore: versionner config spec-kit unionflow Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
|
||||
// Imports du dashboard (à adapter selon la structure réelle)
|
||||
// import 'package:unionflow_mobile_apps/features/dashboard/domain/entities/dashboard_entity.dart';
|
||||
// import 'package:unionflow_mobile_apps/features/dashboard/domain/usecases/get_dashboard_data.dart';
|
||||
// import 'package:unionflow_mobile_apps/features/dashboard/presentation/bloc/dashboard_bloc.dart';
|
||||
// import 'package:unionflow_mobile_apps/core/error/failures.dart';
|
||||
|
||||
/// Tests unitaires pour le Dashboard UnionFlow
|
||||
void main() {
|
||||
group('Dashboard Tests', () {
|
||||
|
||||
group('DashboardEntity', () {
|
||||
test('should create dashboard entity with correct properties', () {
|
||||
// TODO: Implémenter le test d'entité
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
|
||||
test('should calculate today events count correctly', () {
|
||||
// TODO: Implémenter le test de calcul d'événements
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
|
||||
test('should format contribution amount correctly', () {
|
||||
// TODO: Implémenter le test de formatage
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
});
|
||||
|
||||
group('DashboardRepository', () {
|
||||
test('should return dashboard data when call is successful', () async {
|
||||
// TODO: Implémenter le test de repository
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
|
||||
test('should return failure when call fails', () async {
|
||||
// TODO: Implémenter le test d'échec
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
});
|
||||
|
||||
group('GetDashboardData UseCase', () {
|
||||
test('should get dashboard data from repository', () async {
|
||||
// TODO: Implémenter le test de use case
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
|
||||
test('should return failure when repository fails', () async {
|
||||
// TODO: Implémenter le test d'échec use case
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
});
|
||||
|
||||
group('DashboardBloc', () {
|
||||
// TODO: Implémenter les tests BLoC quand les mocks seront prêts
|
||||
test('should be implemented', () {
|
||||
// Placeholder test
|
||||
expect(true, isTrue);
|
||||
});
|
||||
});
|
||||
|
||||
group('DashboardMockDataSource', () {
|
||||
test('should generate realistic mock stats', () async {
|
||||
// TODO: Tester la génération de données mock
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
|
||||
test('should generate mock activities with correct format', () async {
|
||||
// TODO: Tester la génération d'activités
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
|
||||
test('should generate mock events with future dates', () async {
|
||||
// TODO: Tester la génération d'événements
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
});
|
||||
|
||||
group('DashboardConfig', () {
|
||||
test('should have correct default values', () {
|
||||
// TODO: Tester la configuration par défaut
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
|
||||
test('should return correct API endpoints', () {
|
||||
// TODO: Tester les endpoints API
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
|
||||
test('should return correct theme colors', () {
|
||||
// TODO: Tester les couleurs du thème
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
});
|
||||
|
||||
group('DashboardTheme', () {
|
||||
test('should have royal blue and teal blue colors', () {
|
||||
// TODO: Tester les couleurs du design system
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
|
||||
test('should have correct spacing values', () {
|
||||
// TODO: Tester les espacements
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
|
||||
test('should have correct typography styles', () {
|
||||
// TODO: Tester la typographie
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
});
|
||||
|
||||
group('Performance Tests', () {
|
||||
test('should handle large datasets efficiently', () async {
|
||||
// TODO: Tester les performances avec de gros datasets
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
|
||||
test('should not exceed memory limits', () async {
|
||||
// TODO: Tester l'utilisation mémoire
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
|
||||
test('should complete operations within time limits', () async {
|
||||
// TODO: Tester les performances temporelles
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
});
|
||||
|
||||
group('Integration Tests', () {
|
||||
test('should integrate with backend API correctly', () async {
|
||||
// TODO: Tester l'intégration backend
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
|
||||
test('should handle network errors gracefully', () async {
|
||||
// TODO: Tester la gestion d'erreurs réseau
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
|
||||
test('should cache data appropriately', () async {
|
||||
// TODO: Tester le cache
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
});
|
||||
|
||||
group('Widget Tests', () {
|
||||
testWidgets('ConnectedStatsCard should display stats correctly', (tester) async {
|
||||
// TODO: Tester le widget de statistiques
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
|
||||
testWidgets('DashboardChartWidget should render charts', (tester) async {
|
||||
// TODO: Tester le widget de graphiques
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
|
||||
testWidgets('RealTimeMetricsWidget should animate correctly', (tester) async {
|
||||
// TODO: Tester les animations des métriques
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
|
||||
testWidgets('DashboardSearchWidget should handle search input', (tester) async {
|
||||
// TODO: Tester le widget de recherche
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
});
|
||||
|
||||
group('Error Handling Tests', () {
|
||||
test('should handle server errors gracefully', () async {
|
||||
// TODO: Tester la gestion d'erreurs serveur
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
|
||||
test('should handle network timeouts', () async {
|
||||
// TODO: Tester les timeouts réseau
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
|
||||
test('should handle malformed data', () async {
|
||||
// TODO: Tester les données malformées
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
});
|
||||
|
||||
group('Accessibility Tests', () {
|
||||
testWidgets('should have proper semantic labels', (tester) async {
|
||||
// TODO: Tester l'accessibilité
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
|
||||
testWidgets('should support screen readers', (tester) async {
|
||||
// TODO: Tester le support des lecteurs d'écran
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
|
||||
testWidgets('should have sufficient color contrast', (tester) async {
|
||||
// TODO: Tester le contraste des couleurs
|
||||
expect(true, true); // Placeholder
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Mocks pour les tests
|
||||
class MockDashboardRepository extends Mock {
|
||||
// TODO: Implémenter les mocks
|
||||
}
|
||||
|
||||
class MockGetDashboardData extends Mock {
|
||||
// TODO: Implémenter les mocks
|
||||
}
|
||||
|
||||
class MockDashboardRemoteDataSource extends Mock {
|
||||
// TODO: Implémenter les mocks
|
||||
}
|
||||
|
||||
class MockNetworkInfo extends Mock {
|
||||
// TODO: Implémenter les mocks
|
||||
}
|
||||
|
||||
/// Helpers pour les tests
|
||||
class DashboardTestHelpers {
|
||||
static createMockDashboardEntity() {
|
||||
// TODO: Créer une entité mock pour les tests
|
||||
return null;
|
||||
}
|
||||
|
||||
static createMockDashboardStats() {
|
||||
// TODO: Créer des stats mock pour les tests
|
||||
return null;
|
||||
}
|
||||
|
||||
static createMockActivities() {
|
||||
// TODO: Créer des activités mock pour les tests
|
||||
return [];
|
||||
}
|
||||
|
||||
static createMockEvents() {
|
||||
// TODO: Créer des événements mock pour les tests
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// Matchers personnalisés pour les tests
|
||||
class DashboardMatchers {
|
||||
static Matcher hasValidDashboardData() {
|
||||
return predicate((dynamic data) {
|
||||
// TODO: Implémenter la validation des données dashboard
|
||||
return true;
|
||||
}, 'has valid dashboard data');
|
||||
}
|
||||
|
||||
static Matcher hasCorrectThemeColors() {
|
||||
return predicate((dynamic theme) {
|
||||
// TODO: Implémenter la validation des couleurs
|
||||
return true;
|
||||
}, 'has correct theme colors');
|
||||
}
|
||||
|
||||
static Matcher isWithinPerformanceLimits() {
|
||||
return predicate((dynamic metrics) {
|
||||
// TODO: Implémenter la validation des performances
|
||||
return true;
|
||||
}, 'is within performance limits');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
/// Tests unitaires pour ErrorHandler
|
||||
library error_handler_test;
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:unionflow_mobile_apps/core/error/error_handler.dart';
|
||||
|
||||
void main() {
|
||||
group('ErrorHandler', () {
|
||||
group('getErrorMessage', () {
|
||||
test('retourne message pour String', () {
|
||||
const error = 'Erreur personnalisée';
|
||||
final message = ErrorHandler.getErrorMessage(error);
|
||||
expect(message, equals('Erreur personnalisée'));
|
||||
});
|
||||
|
||||
test('retourne message pour Exception', () {
|
||||
final error = Exception('Erreur test');
|
||||
final message = ErrorHandler.getErrorMessage(error);
|
||||
expect(message, equals('Erreur test'));
|
||||
});
|
||||
|
||||
test('retourne message par défaut pour erreur inconnue', () {
|
||||
final error = Object();
|
||||
final message = ErrorHandler.getErrorMessage(error);
|
||||
expect(message, equals('Une erreur inattendue s\'est produite.'));
|
||||
});
|
||||
|
||||
test('gère DioException connectionTimeout', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.connectionTimeout,
|
||||
);
|
||||
final message = ErrorHandler.getErrorMessage(error);
|
||||
expect(message, contains('Délai de connexion dépassé'));
|
||||
});
|
||||
|
||||
test('gère DioException sendTimeout', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.sendTimeout,
|
||||
);
|
||||
final message = ErrorHandler.getErrorMessage(error);
|
||||
expect(message, contains('Délai d\'envoi dépassé'));
|
||||
});
|
||||
|
||||
test('gère DioException receiveTimeout', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.receiveTimeout,
|
||||
);
|
||||
final message = ErrorHandler.getErrorMessage(error);
|
||||
expect(message, contains('Délai de réception dépassé'));
|
||||
});
|
||||
|
||||
test('gère DioException cancel', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.cancel,
|
||||
);
|
||||
final message = ErrorHandler.getErrorMessage(error);
|
||||
expect(message, equals('Requête annulée.'));
|
||||
});
|
||||
|
||||
test('gère DioException connectionError', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.connectionError,
|
||||
);
|
||||
final message = ErrorHandler.getErrorMessage(error);
|
||||
expect(message, contains('Erreur de connexion'));
|
||||
});
|
||||
|
||||
test('gère HTTP 400 Bad Request', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.badResponse,
|
||||
response: Response(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
statusCode: 400,
|
||||
),
|
||||
);
|
||||
final message = ErrorHandler.getErrorMessage(error);
|
||||
expect(message, contains('Requête invalide'));
|
||||
});
|
||||
|
||||
test('gère HTTP 401 Unauthorized', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.badResponse,
|
||||
response: Response(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
statusCode: 401,
|
||||
),
|
||||
);
|
||||
final message = ErrorHandler.getErrorMessage(error);
|
||||
expect(message, contains('Non authentifié'));
|
||||
});
|
||||
|
||||
test('gère HTTP 403 Forbidden', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.badResponse,
|
||||
response: Response(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
statusCode: 403,
|
||||
),
|
||||
);
|
||||
final message = ErrorHandler.getErrorMessage(error);
|
||||
expect(message, contains('Accès refusé'));
|
||||
});
|
||||
|
||||
test('gère HTTP 404 Not Found', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.badResponse,
|
||||
response: Response(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
statusCode: 404,
|
||||
),
|
||||
);
|
||||
final message = ErrorHandler.getErrorMessage(error);
|
||||
expect(message, contains('Ressource non trouvée'));
|
||||
});
|
||||
|
||||
test('gère HTTP 500 Internal Server Error', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.badResponse,
|
||||
response: Response(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
statusCode: 500,
|
||||
),
|
||||
);
|
||||
final message = ErrorHandler.getErrorMessage(error);
|
||||
expect(message, contains('Erreur serveur'));
|
||||
});
|
||||
|
||||
test('extrait message personnalisé du body', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.badResponse,
|
||||
response: Response(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
statusCode: 400,
|
||||
data: {'message': 'Message personnalisé du serveur'},
|
||||
),
|
||||
);
|
||||
final message = ErrorHandler.getErrorMessage(error);
|
||||
expect(message, equals('Message personnalisé du serveur'));
|
||||
});
|
||||
});
|
||||
|
||||
group('isNetworkError', () {
|
||||
test('retourne true pour connectionTimeout', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.connectionTimeout,
|
||||
);
|
||||
expect(ErrorHandler.isNetworkError(error), isTrue);
|
||||
});
|
||||
|
||||
test('retourne true pour connectionError', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.connectionError,
|
||||
);
|
||||
expect(ErrorHandler.isNetworkError(error), isTrue);
|
||||
});
|
||||
|
||||
test('retourne false pour badResponse', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.badResponse,
|
||||
response: Response(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
statusCode: 400,
|
||||
),
|
||||
);
|
||||
expect(ErrorHandler.isNetworkError(error), isFalse);
|
||||
});
|
||||
|
||||
test('retourne false pour non-DioException', () {
|
||||
final error = Exception('Test');
|
||||
expect(ErrorHandler.isNetworkError(error), isFalse);
|
||||
});
|
||||
});
|
||||
|
||||
group('isAuthError', () {
|
||||
test('retourne true pour HTTP 401', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.badResponse,
|
||||
response: Response(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
statusCode: 401,
|
||||
),
|
||||
);
|
||||
expect(ErrorHandler.isAuthError(error), isTrue);
|
||||
});
|
||||
|
||||
test('retourne false pour HTTP 403', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.badResponse,
|
||||
response: Response(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
statusCode: 403,
|
||||
),
|
||||
);
|
||||
expect(ErrorHandler.isAuthError(error), isFalse);
|
||||
});
|
||||
});
|
||||
|
||||
group('isPermissionError', () {
|
||||
test('retourne true pour HTTP 403', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.badResponse,
|
||||
response: Response(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
statusCode: 403,
|
||||
),
|
||||
);
|
||||
expect(ErrorHandler.isPermissionError(error), isTrue);
|
||||
});
|
||||
|
||||
test('retourne false pour HTTP 401', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.badResponse,
|
||||
response: Response(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
statusCode: 401,
|
||||
),
|
||||
);
|
||||
expect(ErrorHandler.isPermissionError(error), isFalse);
|
||||
});
|
||||
});
|
||||
|
||||
group('isValidationError', () {
|
||||
test('retourne true pour HTTP 400', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.badResponse,
|
||||
response: Response(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
statusCode: 400,
|
||||
),
|
||||
);
|
||||
expect(ErrorHandler.isValidationError(error), isTrue);
|
||||
});
|
||||
|
||||
test('retourne true pour HTTP 422', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.badResponse,
|
||||
response: Response(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
statusCode: 422,
|
||||
),
|
||||
);
|
||||
expect(ErrorHandler.isValidationError(error), isTrue);
|
||||
});
|
||||
|
||||
test('retourne false pour HTTP 500', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.badResponse,
|
||||
response: Response(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
statusCode: 500,
|
||||
),
|
||||
);
|
||||
expect(ErrorHandler.isValidationError(error), isFalse);
|
||||
});
|
||||
});
|
||||
|
||||
group('isServerError', () {
|
||||
test('retourne true pour HTTP 500', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.badResponse,
|
||||
response: Response(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
statusCode: 500,
|
||||
),
|
||||
);
|
||||
expect(ErrorHandler.isServerError(error), isTrue);
|
||||
});
|
||||
|
||||
test('retourne true pour HTTP 503', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.badResponse,
|
||||
response: Response(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
statusCode: 503,
|
||||
),
|
||||
);
|
||||
expect(ErrorHandler.isServerError(error), isTrue);
|
||||
});
|
||||
|
||||
test('retourne false pour HTTP 400', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.badResponse,
|
||||
response: Response(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
statusCode: 400,
|
||||
),
|
||||
);
|
||||
expect(ErrorHandler.isServerError(error), isFalse);
|
||||
});
|
||||
});
|
||||
|
||||
group('ErrorHandlerExtension', () {
|
||||
test('toErrorMessage fonctionne', () {
|
||||
const error = 'Test error';
|
||||
expect(error.toErrorMessage(), equals('Test error'));
|
||||
});
|
||||
|
||||
test('isNetworkError fonctionne', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.connectionTimeout,
|
||||
);
|
||||
expect(error.isNetworkError, isTrue);
|
||||
});
|
||||
|
||||
test('isAuthError fonctionne', () {
|
||||
final error = DioException(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
type: DioExceptionType.badResponse,
|
||||
response: Response(
|
||||
requestOptions: RequestOptions(path: '/test'),
|
||||
statusCode: 401,
|
||||
),
|
||||
);
|
||||
expect(error.isAuthError, isTrue);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user