Refactoring
This commit is contained in:
@@ -0,0 +1,443 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
|
||||
import 'package:unionflow_mobile_apps/core/error/exceptions.dart';
|
||||
import 'package:unionflow_mobile_apps/core/network/api_client.dart';
|
||||
import 'package:unionflow_mobile_apps/features/solidarite/data/datasources/solidarite_remote_data_source.dart';
|
||||
import 'package:unionflow_mobile_apps/features/solidarite/data/models/demande_aide_model.dart';
|
||||
|
||||
import '../../../../fixtures/fixture_reader.dart';
|
||||
import 'solidarite_remote_data_source_test.mocks.dart';
|
||||
|
||||
@GenerateMocks([ApiClient])
|
||||
void main() {
|
||||
group('SolidariteRemoteDataSource', () {
|
||||
late SolidariteRemoteDataSourceImpl dataSource;
|
||||
late MockApiClient mockApiClient;
|
||||
|
||||
setUp(() {
|
||||
mockApiClient = MockApiClient();
|
||||
dataSource = SolidariteRemoteDataSourceImpl(apiClient: mockApiClient);
|
||||
});
|
||||
|
||||
group('creerDemandeAide', () {
|
||||
final tDemandeModel = DemandeAideModel.fromJson(
|
||||
json.decode(fixture('demande_aide.json')),
|
||||
);
|
||||
|
||||
test('doit effectuer un POST vers /api/solidarite/demandes avec les bonnes données', () async {
|
||||
// arrange
|
||||
when(mockApiClient.post(any, data: anyNamed('data')))
|
||||
.thenAnswer((_) async => http.Response(fixture('demande_aide.json'), 201));
|
||||
|
||||
// act
|
||||
final result = await dataSource.creerDemandeAide(tDemandeModel);
|
||||
|
||||
// assert
|
||||
verify(mockApiClient.post(
|
||||
'/api/solidarite/demandes',
|
||||
data: tDemandeModel.toJson(),
|
||||
));
|
||||
expect(result, equals(tDemandeModel));
|
||||
});
|
||||
|
||||
test('doit lancer ServerException quand le code de réponse n\'est pas 201', () async {
|
||||
// arrange
|
||||
when(mockApiClient.post(any, data: anyNamed('data')))
|
||||
.thenAnswer((_) async => http.Response('Erreur serveur', 500));
|
||||
|
||||
// act & assert
|
||||
expect(
|
||||
() => dataSource.creerDemandeAide(tDemandeModel),
|
||||
throwsA(isA<ServerException>()),
|
||||
);
|
||||
});
|
||||
|
||||
test('doit lancer ValidationException quand le code de réponse est 400', () async {
|
||||
// arrange
|
||||
when(mockApiClient.post(any, data: anyNamed('data')))
|
||||
.thenAnswer((_) async => http.Response('Données invalides', 400));
|
||||
|
||||
// act & assert
|
||||
expect(
|
||||
() => dataSource.creerDemandeAide(tDemandeModel),
|
||||
throwsA(isA<ValidationException>()),
|
||||
);
|
||||
});
|
||||
|
||||
test('doit lancer NetworkException en cas d\'erreur réseau', () async {
|
||||
// arrange
|
||||
when(mockApiClient.post(any, data: anyNamed('data')))
|
||||
.thenThrow(const NetworkException('Pas de connexion'));
|
||||
|
||||
// act & assert
|
||||
expect(
|
||||
() => dataSource.creerDemandeAide(tDemandeModel),
|
||||
throwsA(isA<NetworkException>()),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('obtenirDemandeAide', () {
|
||||
const tId = 'demande-123';
|
||||
final tDemandeModel = DemandeAideModel.fromJson(
|
||||
json.decode(fixture('demande_aide.json')),
|
||||
);
|
||||
|
||||
test('doit effectuer un GET vers /api/solidarite/demandes/{id}', () async {
|
||||
// arrange
|
||||
when(mockApiClient.get(any))
|
||||
.thenAnswer((_) async => http.Response(fixture('demande_aide.json'), 200));
|
||||
|
||||
// act
|
||||
final result = await dataSource.obtenirDemandeAide(tId);
|
||||
|
||||
// assert
|
||||
verify(mockApiClient.get('/api/solidarite/demandes/$tId'));
|
||||
expect(result, equals(tDemandeModel));
|
||||
});
|
||||
|
||||
test('doit lancer NotFoundException quand le code de réponse est 404', () async {
|
||||
// arrange
|
||||
when(mockApiClient.get(any))
|
||||
.thenAnswer((_) async => http.Response('Non trouvé', 404));
|
||||
|
||||
// act & assert
|
||||
expect(
|
||||
() => dataSource.obtenirDemandeAide(tId),
|
||||
throwsA(isA<NotFoundException>()),
|
||||
);
|
||||
});
|
||||
|
||||
test('doit lancer ServerException quand le code de réponse n\'est pas 200', () async {
|
||||
// arrange
|
||||
when(mockApiClient.get(any))
|
||||
.thenAnswer((_) async => http.Response('Erreur serveur', 500));
|
||||
|
||||
// act & assert
|
||||
expect(
|
||||
() => dataSource.obtenirDemandeAide(tId),
|
||||
throwsA(isA<ServerException>()),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('rechercherDemandesAide', () {
|
||||
final tDemandesJson = json.decode(fixture('demandes_aide_list.json'));
|
||||
final tDemandesModels = (tDemandesJson['content'] as List)
|
||||
.map((json) => DemandeAideModel.fromJson(json))
|
||||
.toList();
|
||||
|
||||
test('doit effectuer un GET vers /api/solidarite/demandes avec les paramètres de recherche', () async {
|
||||
// arrange
|
||||
when(mockApiClient.get(any))
|
||||
.thenAnswer((_) async => http.Response(fixture('demandes_aide_list.json'), 200));
|
||||
|
||||
// act
|
||||
final result = await dataSource.rechercherDemandesAide(
|
||||
organisationId: 'org-1',
|
||||
typeAide: 'AIDE_FINANCIERE_MEDICALE',
|
||||
statut: 'EN_ATTENTE',
|
||||
demandeurId: 'user-1',
|
||||
urgente: true,
|
||||
page: 0,
|
||||
taille: 20,
|
||||
);
|
||||
|
||||
// assert
|
||||
verify(mockApiClient.get(
|
||||
'/api/solidarite/demandes?organisationId=org-1&typeAide=AIDE_FINANCIERE_MEDICALE&statut=EN_ATTENTE&demandeurId=user-1&urgente=true&page=0&size=20',
|
||||
));
|
||||
expect(result, equals(tDemandesModels));
|
||||
});
|
||||
|
||||
test('doit construire l\'URL correctement avec des paramètres null', () async {
|
||||
// arrange
|
||||
when(mockApiClient.get(any))
|
||||
.thenAnswer((_) async => http.Response(fixture('demandes_aide_list.json'), 200));
|
||||
|
||||
// act
|
||||
await dataSource.rechercherDemandesAide(
|
||||
organisationId: null,
|
||||
typeAide: null,
|
||||
statut: null,
|
||||
demandeurId: null,
|
||||
urgente: null,
|
||||
page: 0,
|
||||
taille: 20,
|
||||
);
|
||||
|
||||
// assert
|
||||
verify(mockApiClient.get('/api/solidarite/demandes?page=0&size=20'));
|
||||
});
|
||||
|
||||
test('doit retourner une liste vide quand aucune demande n\'est trouvée', () async {
|
||||
// arrange
|
||||
when(mockApiClient.get(any))
|
||||
.thenAnswer((_) async => http.Response('{"content": [], "totalElements": 0}', 200));
|
||||
|
||||
// act
|
||||
final result = await dataSource.rechercherDemandesAide(
|
||||
page: 0,
|
||||
taille: 20,
|
||||
);
|
||||
|
||||
// assert
|
||||
expect(result, isEmpty);
|
||||
});
|
||||
|
||||
test('doit lancer ServerException quand le code de réponse n\'est pas 200', () async {
|
||||
// arrange
|
||||
when(mockApiClient.get(any))
|
||||
.thenAnswer((_) async => http.Response('Erreur serveur', 500));
|
||||
|
||||
// act & assert
|
||||
expect(
|
||||
() => dataSource.rechercherDemandesAide(page: 0, taille: 20),
|
||||
throwsA(isA<ServerException>()),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('mettreAJourDemandeAide', () {
|
||||
final tDemandeModel = DemandeAideModel.fromJson(
|
||||
json.decode(fixture('demande_aide.json')),
|
||||
);
|
||||
|
||||
test('doit effectuer un PUT vers /api/solidarite/demandes/{id}', () async {
|
||||
// arrange
|
||||
when(mockApiClient.put(any, data: anyNamed('data')))
|
||||
.thenAnswer((_) async => http.Response(fixture('demande_aide.json'), 200));
|
||||
|
||||
// act
|
||||
final result = await dataSource.mettreAJourDemandeAide(tDemandeModel);
|
||||
|
||||
// assert
|
||||
verify(mockApiClient.put(
|
||||
'/api/solidarite/demandes/${tDemandeModel.id}',
|
||||
data: tDemandeModel.toJson(),
|
||||
));
|
||||
expect(result, equals(tDemandeModel));
|
||||
});
|
||||
|
||||
test('doit lancer NotFoundException quand le code de réponse est 404', () async {
|
||||
// arrange
|
||||
when(mockApiClient.put(any, data: anyNamed('data')))
|
||||
.thenAnswer((_) async => http.Response('Non trouvé', 404));
|
||||
|
||||
// act & assert
|
||||
expect(
|
||||
() => dataSource.mettreAJourDemandeAide(tDemandeModel),
|
||||
throwsA(isA<NotFoundException>()),
|
||||
);
|
||||
});
|
||||
|
||||
test('doit lancer ValidationException quand le code de réponse est 400', () async {
|
||||
// arrange
|
||||
when(mockApiClient.put(any, data: anyNamed('data')))
|
||||
.thenAnswer((_) async => http.Response('Données invalides', 400));
|
||||
|
||||
// act & assert
|
||||
expect(
|
||||
() => dataSource.mettreAJourDemandeAide(tDemandeModel),
|
||||
throwsA(isA<ValidationException>()),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('supprimerDemandeAide', () {
|
||||
const tId = 'demande-123';
|
||||
|
||||
test('doit effectuer un DELETE vers /api/solidarite/demandes/{id}', () async {
|
||||
// arrange
|
||||
when(mockApiClient.delete(any))
|
||||
.thenAnswer((_) async => http.Response('', 204));
|
||||
|
||||
// act
|
||||
await dataSource.supprimerDemandeAide(tId);
|
||||
|
||||
// assert
|
||||
verify(mockApiClient.delete('/api/solidarite/demandes/$tId'));
|
||||
});
|
||||
|
||||
test('doit lancer NotFoundException quand le code de réponse est 404', () async {
|
||||
// arrange
|
||||
when(mockApiClient.delete(any))
|
||||
.thenAnswer((_) async => http.Response('Non trouvé', 404));
|
||||
|
||||
// act & assert
|
||||
expect(
|
||||
() => dataSource.supprimerDemandeAide(tId),
|
||||
throwsA(isA<NotFoundException>()),
|
||||
);
|
||||
});
|
||||
|
||||
test('doit lancer ServerException quand le code de réponse n\'est pas 204', () async {
|
||||
// arrange
|
||||
when(mockApiClient.delete(any))
|
||||
.thenAnswer((_) async => http.Response('Erreur serveur', 500));
|
||||
|
||||
// act & assert
|
||||
expect(
|
||||
() => dataSource.supprimerDemandeAide(tId),
|
||||
throwsA(isA<ServerException>()),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('soumettreDemandeAide', () {
|
||||
const tId = 'demande-123';
|
||||
final tDemandeModel = DemandeAideModel.fromJson(
|
||||
json.decode(fixture('demande_aide.json')),
|
||||
);
|
||||
|
||||
test('doit effectuer un POST vers /api/solidarite/demandes/{id}/soumettre', () async {
|
||||
// arrange
|
||||
when(mockApiClient.post(any))
|
||||
.thenAnswer((_) async => http.Response(fixture('demande_aide.json'), 200));
|
||||
|
||||
// act
|
||||
final result = await dataSource.soumettreDemandeAide(tId);
|
||||
|
||||
// assert
|
||||
verify(mockApiClient.post('/api/solidarite/demandes/$tId/soumettre'));
|
||||
expect(result, equals(tDemandeModel));
|
||||
});
|
||||
|
||||
test('doit lancer NotFoundException quand le code de réponse est 404', () async {
|
||||
// arrange
|
||||
when(mockApiClient.post(any))
|
||||
.thenAnswer((_) async => http.Response('Non trouvé', 404));
|
||||
|
||||
// act & assert
|
||||
expect(
|
||||
() => dataSource.soumettreDemandeAide(tId),
|
||||
throwsA(isA<NotFoundException>()),
|
||||
);
|
||||
});
|
||||
|
||||
test('doit lancer ValidationException quand la demande ne peut pas être soumise', () async {
|
||||
// arrange
|
||||
when(mockApiClient.post(any))
|
||||
.thenAnswer((_) async => http.Response('Demande incomplète', 400));
|
||||
|
||||
// act & assert
|
||||
expect(
|
||||
() => dataSource.soumettreDemandeAide(tId),
|
||||
throwsA(isA<ValidationException>()),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('obtenirDemandesUrgentes', () {
|
||||
final tDemandesJson = json.decode(fixture('demandes_aide_urgentes.json'));
|
||||
final tDemandesModels = (tDemandesJson as List)
|
||||
.map((json) => DemandeAideModel.fromJson(json))
|
||||
.toList();
|
||||
|
||||
test('doit effectuer un GET vers /api/solidarite/demandes/urgentes', () async {
|
||||
// arrange
|
||||
when(mockApiClient.get(any))
|
||||
.thenAnswer((_) async => http.Response(fixture('demandes_aide_urgentes.json'), 200));
|
||||
|
||||
// act
|
||||
final result = await dataSource.obtenirDemandesUrgentes('org-1');
|
||||
|
||||
// assert
|
||||
verify(mockApiClient.get('/api/solidarite/demandes/urgentes?organisationId=org-1'));
|
||||
expect(result, equals(tDemandesModels));
|
||||
});
|
||||
|
||||
test('doit retourner une liste vide quand aucune demande urgente n\'est trouvée', () async {
|
||||
// arrange
|
||||
when(mockApiClient.get(any))
|
||||
.thenAnswer((_) async => http.Response('[]', 200));
|
||||
|
||||
// act
|
||||
final result = await dataSource.obtenirDemandesUrgentes('org-1');
|
||||
|
||||
// assert
|
||||
expect(result, isEmpty);
|
||||
});
|
||||
});
|
||||
|
||||
group('obtenirMesDemandes', () {
|
||||
final tDemandesJson = json.decode(fixture('mes_demandes.json'));
|
||||
final tDemandesModels = (tDemandesJson['content'] as List)
|
||||
.map((json) => DemandeAideModel.fromJson(json))
|
||||
.toList();
|
||||
|
||||
test('doit effectuer un GET vers /api/solidarite/demandes/mes-demandes', () async {
|
||||
// arrange
|
||||
when(mockApiClient.get(any))
|
||||
.thenAnswer((_) async => http.Response(fixture('mes_demandes.json'), 200));
|
||||
|
||||
// act
|
||||
final result = await dataSource.obtenirMesDemandes(
|
||||
demandeurId: 'user-1',
|
||||
page: 0,
|
||||
taille: 20,
|
||||
);
|
||||
|
||||
// assert
|
||||
verify(mockApiClient.get('/api/solidarite/demandes/mes-demandes?demandeurId=user-1&page=0&size=20'));
|
||||
expect(result, equals(tDemandesModels));
|
||||
});
|
||||
});
|
||||
|
||||
group('gestion des erreurs réseau', () {
|
||||
test('doit lancer NetworkException en cas de timeout', () async {
|
||||
// arrange
|
||||
when(mockApiClient.get(any))
|
||||
.thenThrow(const NetworkException('Timeout'));
|
||||
|
||||
// act & assert
|
||||
expect(
|
||||
() => dataSource.obtenirDemandeAide('demande-123'),
|
||||
throwsA(isA<NetworkException>()),
|
||||
);
|
||||
});
|
||||
|
||||
test('doit lancer NetworkException en cas d\'erreur de connexion', () async {
|
||||
// arrange
|
||||
when(mockApiClient.get(any))
|
||||
.thenThrow(const NetworkException('Connexion refusée'));
|
||||
|
||||
// act & assert
|
||||
expect(
|
||||
() => dataSource.obtenirDemandeAide('demande-123'),
|
||||
throwsA(isA<NetworkException>()),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('gestion des réponses malformées', () {
|
||||
test('doit lancer ServerException en cas de JSON invalide', () async {
|
||||
// arrange
|
||||
when(mockApiClient.get(any))
|
||||
.thenAnswer((_) async => http.Response('JSON invalide', 200));
|
||||
|
||||
// act & assert
|
||||
expect(
|
||||
() => dataSource.obtenirDemandeAide('demande-123'),
|
||||
throwsA(isA<ServerException>()),
|
||||
);
|
||||
});
|
||||
|
||||
test('doit lancer ServerException en cas de structure JSON inattendue', () async {
|
||||
// arrange
|
||||
when(mockApiClient.get(any))
|
||||
.thenAnswer((_) async => http.Response('{"unexpected": "structure"}', 200));
|
||||
|
||||
// act & assert
|
||||
expect(
|
||||
() => dataSource.obtenirDemandeAide('demande-123'),
|
||||
throwsA(isA<ServerException>()),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:unionflow_mobile_apps/features/solidarite/domain/entities/demande_aide.dart';
|
||||
|
||||
void main() {
|
||||
group('DemandeAide Entity', () {
|
||||
test('doit créer une instance simple', () {
|
||||
final demande = DemandeAide(
|
||||
id: 'test-id',
|
||||
numeroReference: 'REF-001',
|
||||
titre: 'Test',
|
||||
description: 'Description test',
|
||||
typeAide: TypeAide.aideFinanciereUrgente,
|
||||
statut: StatutAide.brouillon,
|
||||
priorite: PrioriteAide.normale,
|
||||
demandeurId: 'user-1',
|
||||
nomDemandeur: 'Test User',
|
||||
organisationId: 'org-1',
|
||||
dateCreation: DateTime.now(),
|
||||
dateModification: DateTime.now(),
|
||||
);
|
||||
|
||||
expect(demande.id, 'test-id');
|
||||
expect(demande.titre, 'Test');
|
||||
expect(demande.typeAide, TypeAide.aideFinanciereUrgente);
|
||||
expect(demande.statut, StatutAide.brouillon);
|
||||
});
|
||||
|
||||
test('doit tester les enums de base', () {
|
||||
// Test TypeAide
|
||||
expect(TypeAide.values.isNotEmpty, true);
|
||||
expect(TypeAide.aideFinanciereUrgente.toString(), contains('aideFinanciereUrgente'));
|
||||
|
||||
// Test StatutAide
|
||||
expect(StatutAide.values.isNotEmpty, true);
|
||||
expect(StatutAide.brouillon.toString(), contains('brouillon'));
|
||||
|
||||
// Test PrioriteAide
|
||||
expect(PrioriteAide.values.isNotEmpty, true);
|
||||
expect(PrioriteAide.normale.toString(), contains('normale'));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
|
||||
import 'package:unionflow_mobile_apps/core/error/failures.dart';
|
||||
import 'package:unionflow_mobile_apps/features/solidarite/domain/entities/demande_aide.dart';
|
||||
import 'package:unionflow_mobile_apps/features/solidarite/domain/repositories/solidarite_repository.dart';
|
||||
import 'package:unionflow_mobile_apps/features/solidarite/domain/usecases/gerer_demandes_aide_usecase.dart';
|
||||
|
||||
import 'creer_demande_aide_usecase_test.mocks.dart';
|
||||
|
||||
@GenerateMocks([SolidariteRepository])
|
||||
void main() {
|
||||
group('CreerDemandeAideUseCase', () {
|
||||
late CreerDemandeAideUseCase usecase;
|
||||
late MockSolidariteRepository mockRepository;
|
||||
|
||||
setUp(() {
|
||||
mockRepository = MockSolidariteRepository();
|
||||
usecase = CreerDemandeAideUseCase(mockRepository);
|
||||
});
|
||||
|
||||
final tDemande = DemandeAide(
|
||||
id: '',
|
||||
numeroReference: '',
|
||||
titre: 'Aide médicale urgente',
|
||||
description: 'Besoin d\'aide pour frais médicaux',
|
||||
typeAide: TypeAide.aideFinanciereMedicale,
|
||||
statut: StatutAide.brouillon,
|
||||
priorite: PrioriteAide.haute,
|
||||
estUrgente: true,
|
||||
montantDemande: 500000.0,
|
||||
dateCreation: DateTime.now(),
|
||||
dateModification: DateTime.now(),
|
||||
organisationId: 'org-1',
|
||||
demandeurId: 'user-1',
|
||||
nomDemandeur: 'Marie Kouassi',
|
||||
emailDemandeur: 'marie@example.com',
|
||||
telephoneDemandeur: '+225123456789',
|
||||
beneficiaires: const [],
|
||||
evaluations: const [],
|
||||
commentairesInternes: const [],
|
||||
historiqueStatuts: const [],
|
||||
piecesJustificatives: const [],
|
||||
tags: const [],
|
||||
metadonnees: const {},
|
||||
);
|
||||
|
||||
final tDemandeCreee = tDemande.copyWith(
|
||||
id: 'demande-123',
|
||||
numeroReference: 'REF-2024-001',
|
||||
);
|
||||
|
||||
test('doit créer une demande d\'aide avec succès', () async {
|
||||
// arrange
|
||||
when(mockRepository.creerDemandeAide(any))
|
||||
.thenAnswer((_) async => Right(tDemandeCreee));
|
||||
|
||||
// act
|
||||
final result = await usecase(CreerDemandeAideParams(demande: tDemande));
|
||||
|
||||
// assert
|
||||
expect(result, Right(tDemandeCreee));
|
||||
verify(mockRepository.creerDemandeAide(tDemande));
|
||||
verifyNoMoreInteractions(mockRepository);
|
||||
});
|
||||
|
||||
test('doit retourner ValidationFailure quand les données sont invalides', () async {
|
||||
// arrange
|
||||
final tDemandeInvalide = tDemande.copyWith(titre: '');
|
||||
when(mockRepository.creerDemandeAide(any))
|
||||
.thenAnswer((_) async => const Left(ValidationFailure('Le titre est requis')));
|
||||
|
||||
// act
|
||||
final result = await usecase(CreerDemandeAideParams(demande: tDemandeInvalide));
|
||||
|
||||
// assert
|
||||
expect(result, const Left(ValidationFailure('Le titre est requis')));
|
||||
verify(mockRepository.creerDemandeAide(tDemandeInvalide));
|
||||
verifyNoMoreInteractions(mockRepository);
|
||||
});
|
||||
|
||||
test('doit retourner ServerFailure quand le serveur échoue', () async {
|
||||
// arrange
|
||||
when(mockRepository.creerDemandeAide(any))
|
||||
.thenAnswer((_) async => const Left(ServerFailure('Erreur serveur')));
|
||||
|
||||
// act
|
||||
final result = await usecase(CreerDemandeAideParams(demande: tDemande));
|
||||
|
||||
// assert
|
||||
expect(result, const Left(ServerFailure('Erreur serveur')));
|
||||
verify(mockRepository.creerDemandeAide(tDemande));
|
||||
verifyNoMoreInteractions(mockRepository);
|
||||
});
|
||||
|
||||
test('doit retourner NetworkFailure quand il n\'y a pas de connexion', () async {
|
||||
// arrange
|
||||
when(mockRepository.creerDemandeAide(any))
|
||||
.thenAnswer((_) async => const Left(NetworkFailure('Pas de connexion internet')));
|
||||
|
||||
// act
|
||||
final result = await usecase(CreerDemandeAideParams(demande: tDemande));
|
||||
|
||||
// assert
|
||||
expect(result, const Left(NetworkFailure('Pas de connexion internet')));
|
||||
verify(mockRepository.creerDemandeAide(tDemande));
|
||||
verifyNoMoreInteractions(mockRepository);
|
||||
});
|
||||
|
||||
group('validation des paramètres', () {
|
||||
test('doit valider que le titre n\'est pas vide', () async {
|
||||
// arrange
|
||||
final tDemandeInvalide = tDemande.copyWith(titre: '');
|
||||
when(mockRepository.creerDemandeAide(any))
|
||||
.thenAnswer((_) async => const Left(ValidationFailure('Le titre est requis')));
|
||||
|
||||
// act
|
||||
final result = await usecase(CreerDemandeAideParams(demande: tDemandeInvalide));
|
||||
|
||||
// assert
|
||||
expect(result.isLeft(), true);
|
||||
result.fold(
|
||||
(failure) => expect(failure, isA<ValidationFailure>()),
|
||||
(success) => fail('Devrait échouer avec ValidationFailure'),
|
||||
);
|
||||
});
|
||||
|
||||
test('doit valider que la description n\'est pas vide', () async {
|
||||
// arrange
|
||||
final tDemandeInvalide = tDemande.copyWith(description: '');
|
||||
when(mockRepository.creerDemandeAide(any))
|
||||
.thenAnswer((_) async => const Left(ValidationFailure('La description est requise')));
|
||||
|
||||
// act
|
||||
final result = await usecase(CreerDemandeAideParams(demande: tDemandeInvalide));
|
||||
|
||||
// assert
|
||||
expect(result.isLeft(), true);
|
||||
result.fold(
|
||||
(failure) => expect(failure, isA<ValidationFailure>()),
|
||||
(success) => fail('Devrait échouer avec ValidationFailure'),
|
||||
);
|
||||
});
|
||||
|
||||
test('doit valider que le montant est positif', () async {
|
||||
// arrange
|
||||
final tDemandeInvalide = tDemande.copyWith(montantDemande: -100.0);
|
||||
when(mockRepository.creerDemandeAide(any))
|
||||
.thenAnswer((_) async => const Left(ValidationFailure('Le montant doit être positif')));
|
||||
|
||||
// act
|
||||
final result = await usecase(CreerDemandeAideParams(demande: tDemandeInvalide));
|
||||
|
||||
// assert
|
||||
expect(result.isLeft(), true);
|
||||
result.fold(
|
||||
(failure) => expect(failure, isA<ValidationFailure>()),
|
||||
(success) => fail('Devrait échouer avec ValidationFailure'),
|
||||
);
|
||||
});
|
||||
|
||||
test('doit valider que l\'email du demandeur est valide', () async {
|
||||
// arrange
|
||||
final tDemandeInvalide = tDemande.copyWith(emailDemandeur: 'email-invalide');
|
||||
when(mockRepository.creerDemandeAide(any))
|
||||
.thenAnswer((_) async => const Left(ValidationFailure('Email invalide')));
|
||||
|
||||
// act
|
||||
final result = await usecase(CreerDemandeAideParams(demande: tDemandeInvalide));
|
||||
|
||||
// assert
|
||||
expect(result.isLeft(), true);
|
||||
result.fold(
|
||||
(failure) => expect(failure, isA<ValidationFailure>()),
|
||||
(success) => fail('Devrait échouer avec ValidationFailure'),
|
||||
);
|
||||
});
|
||||
|
||||
test('doit valider que le téléphone du demandeur est valide', () async {
|
||||
// arrange
|
||||
final tDemandeInvalide = tDemande.copyWith(telephoneDemandeur: '123');
|
||||
when(mockRepository.creerDemandeAide(any))
|
||||
.thenAnswer((_) async => const Left(ValidationFailure('Numéro de téléphone invalide')));
|
||||
|
||||
// act
|
||||
final result = await usecase(CreerDemandeAideParams(demande: tDemandeInvalide));
|
||||
|
||||
// assert
|
||||
expect(result.isLeft(), true);
|
||||
result.fold(
|
||||
(failure) => expect(failure, isA<ValidationFailure>()),
|
||||
(success) => fail('Devrait échouer avec ValidationFailure'),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('gestion des cas limites', () {
|
||||
test('doit gérer une demande avec montant null', () async {
|
||||
// arrange
|
||||
final tDemandeSansMontant = tDemande.copyWith(montantDemande: null);
|
||||
final tDemandeCreeSansMontant = tDemandeSansMontant.copyWith(
|
||||
id: 'demande-123',
|
||||
numeroReference: 'REF-2024-001',
|
||||
);
|
||||
when(mockRepository.creerDemandeAide(any))
|
||||
.thenAnswer((_) async => Right(tDemandeCreeSansMontant));
|
||||
|
||||
// act
|
||||
final result = await usecase(CreerDemandeAideParams(demande: tDemandeSansMontant));
|
||||
|
||||
// assert
|
||||
expect(result, Right(tDemandeCreeSansMontant));
|
||||
verify(mockRepository.creerDemandeAide(tDemandeSansMontant));
|
||||
});
|
||||
|
||||
test('doit gérer une demande avec justification null', () async {
|
||||
// arrange
|
||||
final tDemandeSansJustification = tDemande.copyWith(justification: null);
|
||||
final tDemandeCreeSansJustification = tDemandeSansJustification.copyWith(
|
||||
id: 'demande-123',
|
||||
numeroReference: 'REF-2024-001',
|
||||
);
|
||||
when(mockRepository.creerDemandeAide(any))
|
||||
.thenAnswer((_) async => Right(tDemandeCreeSansJustification));
|
||||
|
||||
// act
|
||||
final result = await usecase(CreerDemandeAideParams(demande: tDemandeSansJustification));
|
||||
|
||||
// assert
|
||||
expect(result, Right(tDemandeCreeSansJustification));
|
||||
verify(mockRepository.creerDemandeAide(tDemandeSansJustification));
|
||||
});
|
||||
|
||||
test('doit gérer une demande avec bénéficiaires multiples', () async {
|
||||
// arrange
|
||||
final tBeneficiaires = [
|
||||
const BeneficiaireAide(
|
||||
prenom: 'Jean',
|
||||
nom: 'Kouassi',
|
||||
age: 25,
|
||||
),
|
||||
const BeneficiaireAide(
|
||||
prenom: 'Marie',
|
||||
nom: 'Kouassi',
|
||||
age: 23,
|
||||
),
|
||||
];
|
||||
final tDemandeAvecBeneficiaires = tDemande.copyWith(beneficiaires: tBeneficiaires);
|
||||
final tDemandeCreeeAvecBeneficiaires = tDemandeAvecBeneficiaires.copyWith(
|
||||
id: 'demande-123',
|
||||
numeroReference: 'REF-2024-001',
|
||||
);
|
||||
when(mockRepository.creerDemandeAide(any))
|
||||
.thenAnswer((_) async => Right(tDemandeCreeeAvecBeneficiaires));
|
||||
|
||||
// act
|
||||
final result = await usecase(CreerDemandeAideParams(demande: tDemandeAvecBeneficiaires));
|
||||
|
||||
// assert
|
||||
expect(result, Right(tDemandeCreeeAvecBeneficiaires));
|
||||
verify(mockRepository.creerDemandeAide(tDemandeAvecBeneficiaires));
|
||||
});
|
||||
|
||||
test('doit gérer une demande avec contact d\'urgence', () async {
|
||||
// arrange
|
||||
const tContactUrgence = ContactUrgence(
|
||||
prenom: 'Paul',
|
||||
nom: 'Kouassi',
|
||||
telephone: '+225987654321',
|
||||
email: 'paul@example.com',
|
||||
relation: 'Frère',
|
||||
);
|
||||
final tDemandeAvecContact = tDemande.copyWith(contactUrgence: tContactUrgence);
|
||||
final tDemandeCreeeAvecContact = tDemandeAvecContact.copyWith(
|
||||
id: 'demande-123',
|
||||
numeroReference: 'REF-2024-001',
|
||||
);
|
||||
when(mockRepository.creerDemandeAide(any))
|
||||
.thenAnswer((_) async => Right(tDemandeCreeeAvecContact));
|
||||
|
||||
// act
|
||||
final result = await usecase(CreerDemandeAideParams(demande: tDemandeAvecContact));
|
||||
|
||||
// assert
|
||||
expect(result, Right(tDemandeCreeeAvecContact));
|
||||
verify(mockRepository.creerDemandeAide(tDemandeAvecContact));
|
||||
});
|
||||
|
||||
test('doit gérer une demande avec localisation', () async {
|
||||
// arrange
|
||||
const tLocalisation = Localisation(
|
||||
adresse: '123 Rue de la Paix',
|
||||
ville: 'Abidjan',
|
||||
codePostal: '00225',
|
||||
pays: 'Côte d\'Ivoire',
|
||||
latitude: 5.3600,
|
||||
longitude: -4.0083,
|
||||
);
|
||||
final tDemandeAvecLocalisation = tDemande.copyWith(localisation: tLocalisation);
|
||||
final tDemandeCreeeAvecLocalisation = tDemandeAvecLocalisation.copyWith(
|
||||
id: 'demande-123',
|
||||
numeroReference: 'REF-2024-001',
|
||||
);
|
||||
when(mockRepository.creerDemandeAide(any))
|
||||
.thenAnswer((_) async => Right(tDemandeCreeeAvecLocalisation));
|
||||
|
||||
// act
|
||||
final result = await usecase(CreerDemandeAideParams(demande: tDemandeAvecLocalisation));
|
||||
|
||||
// assert
|
||||
expect(result, Right(tDemandeCreeeAvecLocalisation));
|
||||
verify(mockRepository.creerDemandeAide(tDemandeAvecLocalisation));
|
||||
});
|
||||
});
|
||||
|
||||
group('performance et concurrence', () {
|
||||
test('doit gérer les appels concurrents', () async {
|
||||
// arrange
|
||||
when(mockRepository.creerDemandeAide(any))
|
||||
.thenAnswer((_) async => Right(tDemandeCreee));
|
||||
|
||||
// act
|
||||
final futures = List.generate(5, (index) {
|
||||
final demande = tDemande.copyWith(titre: 'Demande $index');
|
||||
return usecase(CreerDemandeAideParams(demande: demande));
|
||||
});
|
||||
final results = await Future.wait(futures);
|
||||
|
||||
// assert
|
||||
expect(results.length, 5);
|
||||
for (final result in results) {
|
||||
expect(result.isRight(), true);
|
||||
}
|
||||
verify(mockRepository.creerDemandeAide(any)).called(5);
|
||||
});
|
||||
|
||||
test('doit gérer les timeouts', () async {
|
||||
// arrange
|
||||
when(mockRepository.creerDemandeAide(any))
|
||||
.thenAnswer((_) async {
|
||||
await Future.delayed(const Duration(seconds: 10));
|
||||
return Right(tDemandeCreee);
|
||||
});
|
||||
|
||||
// act & assert
|
||||
expect(
|
||||
() => usecase(CreerDemandeAideParams(demande: tDemande))
|
||||
.timeout(const Duration(seconds: 5)),
|
||||
throwsA(isA<TimeoutException>()),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,441 @@
|
||||
import 'package:bloc_test/bloc_test.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
|
||||
import 'package:unionflow_mobile_apps/core/error/failures.dart';
|
||||
import 'package:unionflow_mobile_apps/features/solidarite/domain/entities/demande_aide.dart';
|
||||
import 'package:unionflow_mobile_apps/features/solidarite/domain/usecases/gerer_demandes_aide_usecase.dart';
|
||||
import 'package:unionflow_mobile_apps/features/solidarite/presentation/bloc/demandes_aide/demandes_aide_bloc.dart';
|
||||
import 'package:unionflow_mobile_apps/features/solidarite/presentation/bloc/demandes_aide/demandes_aide_event.dart';
|
||||
import 'package:unionflow_mobile_apps/features/solidarite/presentation/bloc/demandes_aide/demandes_aide_state.dart';
|
||||
|
||||
import 'demandes_aide_bloc_test.mocks.dart';
|
||||
|
||||
@GenerateMocks([
|
||||
CreerDemandeAideUseCase,
|
||||
MettreAJourDemandeAideUseCase,
|
||||
ObtenirDemandeAideUseCase,
|
||||
SoumettreDemandeAideUseCase,
|
||||
EvaluerDemandeAideUseCase,
|
||||
RechercherDemandesAideUseCase,
|
||||
ObtenirDemandesUrgentesUseCase,
|
||||
ObtenirMesDemandesUseCase,
|
||||
ValiderDemandeAideUseCase,
|
||||
CalculerPrioriteDemandeUseCase,
|
||||
])
|
||||
void main() {
|
||||
group('DemandesAideBloc', () {
|
||||
late DemandesAideBloc bloc;
|
||||
late MockCreerDemandeAideUseCase mockCreerDemandeAideUseCase;
|
||||
late MockMettreAJourDemandeAideUseCase mockMettreAJourDemandeAideUseCase;
|
||||
late MockObtenirDemandeAideUseCase mockObtenirDemandeAideUseCase;
|
||||
late MockSoumettreDemandeAideUseCase mockSoumettreDemandeAideUseCase;
|
||||
late MockEvaluerDemandeAideUseCase mockEvaluerDemandeAideUseCase;
|
||||
late MockRechercherDemandesAideUseCase mockRechercherDemandesAideUseCase;
|
||||
late MockObtenirDemandesUrgentesUseCase mockObtenirDemandesUrgentesUseCase;
|
||||
late MockObtenirMesDemandesUseCase mockObtenirMesDemandesUseCase;
|
||||
late MockValiderDemandeAideUseCase mockValiderDemandeAideUseCase;
|
||||
late MockCalculerPrioriteDemandeUseCase mockCalculerPrioriteDemandeUseCase;
|
||||
|
||||
setUp(() {
|
||||
mockCreerDemandeAideUseCase = MockCreerDemandeAideUseCase();
|
||||
mockMettreAJourDemandeAideUseCase = MockMettreAJourDemandeAideUseCase();
|
||||
mockObtenirDemandeAideUseCase = MockObtenirDemandeAideUseCase();
|
||||
mockSoumettreDemandeAideUseCase = MockSoumettreDemandeAideUseCase();
|
||||
mockEvaluerDemandeAideUseCase = MockEvaluerDemandeAideUseCase();
|
||||
mockRechercherDemandesAideUseCase = MockRechercherDemandesAideUseCase();
|
||||
mockObtenirDemandesUrgentesUseCase = MockObtenirDemandesUrgentesUseCase();
|
||||
mockObtenirMesDemandesUseCase = MockObtenirMesDemandesUseCase();
|
||||
mockValiderDemandeAideUseCase = MockValiderDemandeAideUseCase();
|
||||
mockCalculerPrioriteDemandeUseCase = MockCalculerPrioriteDemandeUseCase();
|
||||
|
||||
bloc = DemandesAideBloc(
|
||||
creerDemandeAideUseCase: mockCreerDemandeAideUseCase,
|
||||
mettreAJourDemandeAideUseCase: mockMettreAJourDemandeAideUseCase,
|
||||
obtenirDemandeAideUseCase: mockObtenirDemandeAideUseCase,
|
||||
soumettreDemandeAideUseCase: mockSoumettreDemandeAideUseCase,
|
||||
evaluerDemandeAideUseCase: mockEvaluerDemandeAideUseCase,
|
||||
rechercherDemandesAideUseCase: mockRechercherDemandesAideUseCase,
|
||||
obtenirDemandesUrgentesUseCase: mockObtenirDemandesUrgentesUseCase,
|
||||
obtenirMesDemandesUseCase: mockObtenirMesDemandesUseCase,
|
||||
validerDemandeAideUseCase: mockValiderDemandeAideUseCase,
|
||||
calculerPrioriteDemandeUseCase: mockCalculerPrioriteDemandeUseCase,
|
||||
);
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
bloc.close();
|
||||
});
|
||||
|
||||
test('état initial est DemandesAideInitial', () {
|
||||
expect(bloc.state, equals(const DemandesAideInitial()));
|
||||
});
|
||||
|
||||
group('ChargerDemandesAideEvent', () {
|
||||
final tDemandes = [
|
||||
_createTestDemandeAide('1', 'Demande 1'),
|
||||
_createTestDemandeAide('2', 'Demande 2'),
|
||||
];
|
||||
|
||||
blocTest<DemandesAideBloc, DemandesAideState>(
|
||||
'émet [DemandesAideLoading, DemandesAideLoaded] quand les données sont chargées avec succès',
|
||||
build: () {
|
||||
when(mockRechercherDemandesAideUseCase(any))
|
||||
.thenAnswer((_) async => Right(tDemandes));
|
||||
return bloc;
|
||||
},
|
||||
act: (bloc) => bloc.add(const ChargerDemandesAideEvent()),
|
||||
expect: () => [
|
||||
const DemandesAideLoading(),
|
||||
isA<DemandesAideLoaded>()
|
||||
.having((state) => state.demandes, 'demandes', tDemandes)
|
||||
.having((state) => state.demandesFiltrees, 'demandesFiltrees', tDemandes)
|
||||
.having((state) => state.hasReachedMax, 'hasReachedMax', true)
|
||||
.having((state) => state.currentPage, 'currentPage', 0)
|
||||
.having((state) => state.totalElements, 'totalElements', 2),
|
||||
],
|
||||
verify: (_) {
|
||||
verify(mockRechercherDemandesAideUseCase(
|
||||
RechercherDemandesAideParams(
|
||||
organisationId: null,
|
||||
typeAide: null,
|
||||
statut: null,
|
||||
demandeurId: null,
|
||||
urgente: null,
|
||||
page: 0,
|
||||
taille: 20,
|
||||
),
|
||||
));
|
||||
},
|
||||
);
|
||||
|
||||
blocTest<DemandesAideBloc, DemandesAideState>(
|
||||
'émet [DemandesAideLoading, DemandesAideError] quand le chargement échoue',
|
||||
build: () {
|
||||
when(mockRechercherDemandesAideUseCase(any))
|
||||
.thenAnswer((_) async => const Left(ServerFailure('Erreur serveur')));
|
||||
return bloc;
|
||||
},
|
||||
act: (bloc) => bloc.add(const ChargerDemandesAideEvent()),
|
||||
expect: () => [
|
||||
const DemandesAideLoading(),
|
||||
isA<DemandesAideError>()
|
||||
.having((state) => state.message, 'message', 'Erreur serveur. Veuillez réessayer plus tard.')
|
||||
.having((state) => state.isNetworkError, 'isNetworkError', false)
|
||||
.having((state) => state.canRetry, 'canRetry', true),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<DemandesAideBloc, DemandesAideState>(
|
||||
'émet [DemandesAideLoaded] avec isRefreshing=true quand forceRefresh=false et état déjà chargé',
|
||||
build: () {
|
||||
when(mockRechercherDemandesAideUseCase(any))
|
||||
.thenAnswer((_) async => Right(tDemandes));
|
||||
return bloc;
|
||||
},
|
||||
seed: () => DemandesAideLoaded(
|
||||
demandes: const [],
|
||||
demandesFiltrees: const [],
|
||||
lastUpdated: DateTime.now(),
|
||||
),
|
||||
act: (bloc) => bloc.add(const ChargerDemandesAideEvent()),
|
||||
expect: () => [
|
||||
isA<DemandesAideLoaded>().having((state) => state.isRefreshing, 'isRefreshing', true),
|
||||
isA<DemandesAideLoaded>()
|
||||
.having((state) => state.demandes, 'demandes', tDemandes)
|
||||
.having((state) => state.isRefreshing, 'isRefreshing', false),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
group('CreerDemandeAideEvent', () {
|
||||
final tDemande = _createTestDemandeAide('1', 'Nouvelle demande');
|
||||
|
||||
blocTest<DemandesAideBloc, DemandesAideState>(
|
||||
'émet [DemandesAideLoading, DemandesAideOperationSuccess, DemandesAideLoading, DemandesAideLoaded] quand la création réussit',
|
||||
build: () {
|
||||
when(mockCreerDemandeAideUseCase(any))
|
||||
.thenAnswer((_) async => Right(tDemande));
|
||||
when(mockRechercherDemandesAideUseCase(any))
|
||||
.thenAnswer((_) async => Right([tDemande]));
|
||||
return bloc;
|
||||
},
|
||||
act: (bloc) => bloc.add(CreerDemandeAideEvent(demande: tDemande)),
|
||||
expect: () => [
|
||||
const DemandesAideLoading(),
|
||||
isA<DemandesAideOperationSuccess>()
|
||||
.having((state) => state.message, 'message', 'Demande d\'aide créée avec succès')
|
||||
.having((state) => state.demande, 'demande', tDemande)
|
||||
.having((state) => state.operation, 'operation', TypeOperationDemande.creation),
|
||||
const DemandesAideLoading(),
|
||||
isA<DemandesAideLoaded>(),
|
||||
],
|
||||
verify: (_) {
|
||||
verify(mockCreerDemandeAideUseCase(CreerDemandeAideParams(demande: tDemande)));
|
||||
},
|
||||
);
|
||||
|
||||
blocTest<DemandesAideBloc, DemandesAideState>(
|
||||
'émet [DemandesAideLoading, DemandesAideError] quand la création échoue',
|
||||
build: () {
|
||||
when(mockCreerDemandeAideUseCase(any))
|
||||
.thenAnswer((_) async => const Left(ValidationFailure('Données invalides')));
|
||||
return bloc;
|
||||
},
|
||||
act: (bloc) => bloc.add(CreerDemandeAideEvent(demande: tDemande)),
|
||||
expect: () => [
|
||||
const DemandesAideLoading(),
|
||||
isA<DemandesAideError>()
|
||||
.having((state) => state.message, 'message', 'Données invalides'),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
group('FiltrerDemandesAideEvent', () {
|
||||
final tDemandes = [
|
||||
_createTestDemandeAide('1', 'Demande urgente', estUrgente: true),
|
||||
_createTestDemandeAide('2', 'Demande normale', estUrgente: false),
|
||||
];
|
||||
|
||||
blocTest<DemandesAideBloc, DemandesAideState>(
|
||||
'filtre les demandes par urgence',
|
||||
build: () => bloc,
|
||||
seed: () => DemandesAideLoaded(
|
||||
demandes: tDemandes,
|
||||
demandesFiltrees: tDemandes,
|
||||
lastUpdated: DateTime.now(),
|
||||
),
|
||||
act: (bloc) => bloc.add(const FiltrerDemandesAideEvent(urgente: true)),
|
||||
expect: () => [
|
||||
isA<DemandesAideLoaded>()
|
||||
.having((state) => state.demandesFiltrees.length, 'demandesFiltrees.length', 1)
|
||||
.having((state) => state.demandesFiltrees.first.estUrgente, 'estUrgente', true)
|
||||
.having((state) => state.filtres.urgente, 'filtres.urgente', true),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<DemandesAideBloc, DemandesAideState>(
|
||||
'filtre les demandes par mot-clé',
|
||||
build: () => bloc,
|
||||
seed: () => DemandesAideLoaded(
|
||||
demandes: tDemandes,
|
||||
demandesFiltrees: tDemandes,
|
||||
lastUpdated: DateTime.now(),
|
||||
),
|
||||
act: (bloc) => bloc.add(const FiltrerDemandesAideEvent(motCle: 'urgente')),
|
||||
expect: () => [
|
||||
isA<DemandesAideLoaded>()
|
||||
.having((state) => state.demandesFiltrees.length, 'demandesFiltrees.length', 1)
|
||||
.having((state) => state.demandesFiltrees.first.titre, 'titre', 'Demande urgente')
|
||||
.having((state) => state.filtres.motCle, 'filtres.motCle', 'urgente'),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
group('TrierDemandesAideEvent', () {
|
||||
final tDemandes = [
|
||||
_createTestDemandeAide('1', 'B Demande', dateCreation: DateTime(2023, 1, 2)),
|
||||
_createTestDemandeAide('2', 'A Demande', dateCreation: DateTime(2023, 1, 1)),
|
||||
];
|
||||
|
||||
blocTest<DemandesAideBloc, DemandesAideState>(
|
||||
'trie les demandes par titre croissant',
|
||||
build: () => bloc,
|
||||
seed: () => DemandesAideLoaded(
|
||||
demandes: tDemandes,
|
||||
demandesFiltrees: tDemandes,
|
||||
lastUpdated: DateTime.now(),
|
||||
),
|
||||
act: (bloc) => bloc.add(const TrierDemandesAideEvent(
|
||||
critere: TriDemandes.titre,
|
||||
croissant: true,
|
||||
)),
|
||||
expect: () => [
|
||||
isA<DemandesAideLoaded>()
|
||||
.having((state) => state.demandesFiltrees.first.titre, 'premier titre', 'A Demande')
|
||||
.having((state) => state.demandesFiltrees.last.titre, 'dernier titre', 'B Demande')
|
||||
.having((state) => state.criterieTri, 'criterieTri', TriDemandes.titre)
|
||||
.having((state) => state.triCroissant, 'triCroissant', true),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<DemandesAideBloc, DemandesAideState>(
|
||||
'trie les demandes par date décroissant',
|
||||
build: () => bloc,
|
||||
seed: () => DemandesAideLoaded(
|
||||
demandes: tDemandes,
|
||||
demandesFiltrees: tDemandes,
|
||||
lastUpdated: DateTime.now(),
|
||||
),
|
||||
act: (bloc) => bloc.add(const TrierDemandesAideEvent(
|
||||
critere: TriDemandes.dateCreation,
|
||||
croissant: false,
|
||||
)),
|
||||
expect: () => [
|
||||
isA<DemandesAideLoaded>()
|
||||
.having((state) => state.demandesFiltrees.first.dateCreation, 'première date', DateTime(2023, 1, 2))
|
||||
.having((state) => state.demandesFiltrees.last.dateCreation, 'dernière date', DateTime(2023, 1, 1))
|
||||
.having((state) => state.criterieTri, 'criterieTri', TriDemandes.dateCreation)
|
||||
.having((state) => state.triCroissant, 'triCroissant', false),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
group('SelectionnerDemandeAideEvent', () {
|
||||
final tDemandes = [
|
||||
_createTestDemandeAide('1', 'Demande 1'),
|
||||
_createTestDemandeAide('2', 'Demande 2'),
|
||||
];
|
||||
|
||||
blocTest<DemandesAideBloc, DemandesAideState>(
|
||||
'sélectionne une demande',
|
||||
build: () => bloc,
|
||||
seed: () => DemandesAideLoaded(
|
||||
demandes: tDemandes,
|
||||
demandesFiltrees: tDemandes,
|
||||
lastUpdated: DateTime.now(),
|
||||
),
|
||||
act: (bloc) => bloc.add(const SelectionnerDemandeAideEvent(
|
||||
demandeId: '1',
|
||||
selectionne: true,
|
||||
)),
|
||||
expect: () => [
|
||||
isA<DemandesAideLoaded>()
|
||||
.having((state) => state.demandesSelectionnees['1'], 'demande sélectionnée', true)
|
||||
.having((state) => state.nombreDemandesSelectionnees, 'nombre sélectionnées', 1),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<DemandesAideBloc, DemandesAideState>(
|
||||
'désélectionne une demande',
|
||||
build: () => bloc,
|
||||
seed: () => DemandesAideLoaded(
|
||||
demandes: tDemandes,
|
||||
demandesFiltrees: tDemandes,
|
||||
demandesSelectionnees: const {'1': true},
|
||||
lastUpdated: DateTime.now(),
|
||||
),
|
||||
act: (bloc) => bloc.add(const SelectionnerDemandeAideEvent(
|
||||
demandeId: '1',
|
||||
selectionne: false,
|
||||
)),
|
||||
expect: () => [
|
||||
isA<DemandesAideLoaded>()
|
||||
.having((state) => state.demandesSelectionnees.containsKey('1'), 'demande désélectionnée', false)
|
||||
.having((state) => state.nombreDemandesSelectionnees, 'nombre sélectionnées', 0),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
group('SelectionnerToutesDemandesAideEvent', () {
|
||||
final tDemandes = [
|
||||
_createTestDemandeAide('1', 'Demande 1'),
|
||||
_createTestDemandeAide('2', 'Demande 2'),
|
||||
];
|
||||
|
||||
blocTest<DemandesAideBloc, DemandesAideState>(
|
||||
'sélectionne toutes les demandes',
|
||||
build: () => bloc,
|
||||
seed: () => DemandesAideLoaded(
|
||||
demandes: tDemandes,
|
||||
demandesFiltrees: tDemandes,
|
||||
lastUpdated: DateTime.now(),
|
||||
),
|
||||
act: (bloc) => bloc.add(const SelectionnerToutesDemandesAideEvent(selectionne: true)),
|
||||
expect: () => [
|
||||
isA<DemandesAideLoaded>()
|
||||
.having((state) => state.demandesSelectionnees.length, 'nombre sélectionnées', 2)
|
||||
.having((state) => state.toutesDemandesSelectionnees, 'toutes sélectionnées', true),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<DemandesAideBloc, DemandesAideState>(
|
||||
'désélectionne toutes les demandes',
|
||||
build: () => bloc,
|
||||
seed: () => DemandesAideLoaded(
|
||||
demandes: tDemandes,
|
||||
demandesFiltrees: tDemandes,
|
||||
demandesSelectionnees: const {'1': true, '2': true},
|
||||
lastUpdated: DateTime.now(),
|
||||
),
|
||||
act: (bloc) => bloc.add(const SelectionnerToutesDemandesAideEvent(selectionne: false)),
|
||||
expect: () => [
|
||||
isA<DemandesAideLoaded>()
|
||||
.having((state) => state.demandesSelectionnees.isEmpty, 'aucune sélectionnée', true)
|
||||
.having((state) => state.toutesDemandesSelectionnees, 'toutes désélectionnées', false),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
group('ValiderDemandeAideEvent', () {
|
||||
final tDemande = _createTestDemandeAide('1', 'Demande à valider');
|
||||
|
||||
blocTest<DemandesAideBloc, DemandesAideState>(
|
||||
'émet DemandesAideValidation avec isValid=true quand la validation réussit',
|
||||
build: () {
|
||||
when(mockValiderDemandeAideUseCase(any))
|
||||
.thenAnswer((_) async => const Right(true));
|
||||
return bloc;
|
||||
},
|
||||
act: (bloc) => bloc.add(ValiderDemandeAideEvent(demande: tDemande)),
|
||||
expect: () => [
|
||||
isA<DemandesAideValidation>()
|
||||
.having((state) => state.isValid, 'isValid', true)
|
||||
.having((state) => state.erreurs.isEmpty, 'erreurs vides', true)
|
||||
.having((state) => state.demande, 'demande', tDemande),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<DemandesAideBloc, DemandesAideState>(
|
||||
'émet DemandesAideValidation avec erreurs quand la validation échoue',
|
||||
build: () {
|
||||
when(mockValiderDemandeAideUseCase(any))
|
||||
.thenAnswer((_) async => const Left(ValidationFailure('Titre requis')));
|
||||
return bloc;
|
||||
},
|
||||
act: (bloc) => bloc.add(ValiderDemandeAideEvent(demande: tDemande)),
|
||||
expect: () => [
|
||||
isA<DemandesAideValidation>()
|
||||
.having((state) => state.isValid, 'isValid', false)
|
||||
.having((state) => state.erreurs['general'], 'erreur générale', 'Titre requis')
|
||||
.having((state) => state.demande, 'demande', tDemande),
|
||||
],
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Fonction utilitaire pour créer une demande d'aide de test
|
||||
DemandeAide _createTestDemandeAide(
|
||||
String id,
|
||||
String titre, {
|
||||
bool estUrgente = false,
|
||||
DateTime? dateCreation,
|
||||
}) {
|
||||
return DemandeAide(
|
||||
id: id,
|
||||
numeroReference: 'REF-$id',
|
||||
titre: titre,
|
||||
description: 'Description de la $titre',
|
||||
typeAide: TypeAide.aideFinanciereUrgente,
|
||||
statut: StatutAide.brouillon,
|
||||
priorite: PrioriteAide.normale,
|
||||
estUrgente: estUrgente,
|
||||
dateCreation: dateCreation ?? DateTime.now(),
|
||||
dateModification: dateCreation ?? DateTime.now(),
|
||||
organisationId: 'org-1',
|
||||
demandeurId: 'user-1',
|
||||
nomDemandeur: 'John Doe',
|
||||
emailDemandeur: 'john@example.com',
|
||||
telephoneDemandeur: '+225123456789',
|
||||
beneficiaires: const [],
|
||||
evaluations: const [],
|
||||
commentairesInternes: const [],
|
||||
historiqueStatuts: const [],
|
||||
piecesJustificatives: const [],
|
||||
tags: const [],
|
||||
metadonnees: const {},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,401 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
|
||||
import 'package:unionflow_mobile_apps/features/solidarite/domain/entities/demande_aide.dart';
|
||||
import 'package:unionflow_mobile_apps/features/solidarite/presentation/widgets/demande_aide_card.dart';
|
||||
|
||||
void main() {
|
||||
group('DemandeAideCard', () {
|
||||
late DemandeAide testDemande;
|
||||
|
||||
setUp(() {
|
||||
testDemande = DemandeAide(
|
||||
id: 'demande-123',
|
||||
numeroReference: 'REF-2024-001',
|
||||
titre: 'Aide médicale urgente',
|
||||
description: 'Besoin d\'aide pour frais médicaux d\'urgence suite à un accident',
|
||||
typeAide: TypeAide.aideFinanciereMedicale,
|
||||
statut: StatutAide.enAttente,
|
||||
priorite: PrioriteAide.haute,
|
||||
estUrgente: true,
|
||||
montantDemande: 500000.0,
|
||||
dateCreation: DateTime(2024, 1, 15, 10, 30),
|
||||
dateModification: DateTime(2024, 1, 15, 14, 45),
|
||||
organisationId: 'org-1',
|
||||
demandeurId: 'user-1',
|
||||
nomDemandeur: 'Marie Kouassi',
|
||||
emailDemandeur: 'marie@example.com',
|
||||
telephoneDemandeur: '+225123456789',
|
||||
beneficiaires: const [
|
||||
BeneficiaireAide(
|
||||
prenom: 'Jean',
|
||||
nom: 'Kouassi',
|
||||
age: 25,
|
||||
),
|
||||
],
|
||||
evaluations: const [],
|
||||
commentairesInternes: const [],
|
||||
historiqueStatuts: const [],
|
||||
piecesJustificatives: const [],
|
||||
tags: const ['urgent', 'médical'],
|
||||
metadonnees: const {},
|
||||
);
|
||||
});
|
||||
|
||||
Widget createWidgetUnderTest({
|
||||
DemandeAide? demande,
|
||||
VoidCallback? onTap,
|
||||
VoidCallback? onLongPress,
|
||||
bool isSelected = false,
|
||||
bool showSelection = false,
|
||||
}) {
|
||||
return MaterialApp(
|
||||
home: Scaffold(
|
||||
body: DemandeAideCard(
|
||||
demande: demande ?? testDemande,
|
||||
onTap: onTap,
|
||||
onLongPress: onLongPress,
|
||||
isSelected: isSelected,
|
||||
showSelection: showSelection,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
group('affichage des informations de base', () {
|
||||
testWidgets('affiche le titre de la demande', (WidgetTester tester) async {
|
||||
// arrange & act
|
||||
await tester.pumpWidget(createWidgetUnderTest());
|
||||
|
||||
// assert
|
||||
expect(find.text('Aide médicale urgente'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('affiche la description tronquée', (WidgetTester tester) async {
|
||||
// arrange & act
|
||||
await tester.pumpWidget(createWidgetUnderTest());
|
||||
|
||||
// assert
|
||||
expect(find.textContaining('Besoin d\'aide pour frais médicaux'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('affiche le numéro de référence', (WidgetTester tester) async {
|
||||
// arrange & act
|
||||
await tester.pumpWidget(createWidgetUnderTest());
|
||||
|
||||
// assert
|
||||
expect(find.text('REF-2024-001'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('affiche le nom du demandeur', (WidgetTester tester) async {
|
||||
// arrange & act
|
||||
await tester.pumpWidget(createWidgetUnderTest());
|
||||
|
||||
// assert
|
||||
expect(find.text('Marie Kouassi'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('affiche le montant demandé formaté', (WidgetTester tester) async {
|
||||
// arrange & act
|
||||
await tester.pumpWidget(createWidgetUnderTest());
|
||||
|
||||
// assert
|
||||
expect(find.text('500 000 FCFA'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('affiche la date de création formatée', (WidgetTester tester) async {
|
||||
// arrange & act
|
||||
await tester.pumpWidget(createWidgetUnderTest());
|
||||
|
||||
// assert
|
||||
expect(find.text('15 jan. 2024'), findsOneWidget);
|
||||
});
|
||||
});
|
||||
|
||||
group('affichage des badges et indicateurs', () {
|
||||
testWidgets('affiche le badge urgent pour une demande urgente', (WidgetTester tester) async {
|
||||
// arrange & act
|
||||
await tester.pumpWidget(createWidgetUnderTest());
|
||||
|
||||
// assert
|
||||
expect(find.text('URGENT'), findsOneWidget);
|
||||
expect(find.byIcon(Icons.priority_high), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('n\'affiche pas le badge urgent pour une demande normale', (WidgetTester tester) async {
|
||||
// arrange
|
||||
final demandeNormale = testDemande.copyWith(estUrgente: false);
|
||||
|
||||
// act
|
||||
await tester.pumpWidget(createWidgetUnderTest(demande: demandeNormale));
|
||||
|
||||
// assert
|
||||
expect(find.text('URGENT'), findsNothing);
|
||||
expect(find.byIcon(Icons.priority_high), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('affiche le badge de statut avec la bonne couleur', (WidgetTester tester) async {
|
||||
// arrange & act
|
||||
await tester.pumpWidget(createWidgetUnderTest());
|
||||
|
||||
// assert
|
||||
expect(find.text('En attente'), findsOneWidget);
|
||||
|
||||
// Vérifier la couleur du badge (orange pour "en attente")
|
||||
final badgeContainer = tester.widget<Container>(
|
||||
find.ancestor(
|
||||
of: find.text('En attente'),
|
||||
matching: find.byType(Container),
|
||||
).first,
|
||||
);
|
||||
expect(badgeContainer.decoration, isA<BoxDecoration>());
|
||||
});
|
||||
|
||||
testWidgets('affiche le badge de priorité', (WidgetTester tester) async {
|
||||
// arrange & act
|
||||
await tester.pumpWidget(createWidgetUnderTest());
|
||||
|
||||
// assert
|
||||
expect(find.text('Haute'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('affiche le badge de type d\'aide', (WidgetTester tester) async {
|
||||
// arrange & act
|
||||
await tester.pumpWidget(createWidgetUnderTest());
|
||||
|
||||
// assert
|
||||
expect(find.text('Aide médicale'), findsOneWidget);
|
||||
});
|
||||
});
|
||||
|
||||
group('affichage des informations supplémentaires', () {
|
||||
testWidgets('affiche le nombre de bénéficiaires', (WidgetTester tester) async {
|
||||
// arrange & act
|
||||
await tester.pumpWidget(createWidgetUnderTest());
|
||||
|
||||
// assert
|
||||
expect(find.text('1 bénéficiaire'), findsOneWidget);
|
||||
expect(find.byIcon(Icons.people), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('affiche le pluriel pour plusieurs bénéficiaires', (WidgetTester tester) async {
|
||||
// arrange
|
||||
final demandeAvecPlusieurs = testDemande.copyWith(
|
||||
beneficiaires: const [
|
||||
BeneficiaireAide(prenom: 'Jean', nom: 'Kouassi', age: 25),
|
||||
BeneficiaireAide(prenom: 'Marie', nom: 'Kouassi', age: 23),
|
||||
],
|
||||
);
|
||||
|
||||
// act
|
||||
await tester.pumpWidget(createWidgetUnderTest(demande: demandeAvecPlusieurs));
|
||||
|
||||
// assert
|
||||
expect(find.text('2 bénéficiaires'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('affiche les tags', (WidgetTester tester) async {
|
||||
// arrange & act
|
||||
await tester.pumpWidget(createWidgetUnderTest());
|
||||
|
||||
// assert
|
||||
expect(find.text('urgent'), findsOneWidget);
|
||||
expect(find.text('médical'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('affiche l\'indicateur de progression', (WidgetTester tester) async {
|
||||
// arrange & act
|
||||
await tester.pumpWidget(createWidgetUnderTest());
|
||||
|
||||
// assert
|
||||
expect(find.byType(LinearProgressIndicator), findsOneWidget);
|
||||
});
|
||||
});
|
||||
|
||||
group('interactions utilisateur', () {
|
||||
testWidgets('appelle onTap quand la carte est tapée', (WidgetTester tester) async {
|
||||
// arrange
|
||||
bool tapCalled = false;
|
||||
void onTap() => tapCalled = true;
|
||||
|
||||
await tester.pumpWidget(createWidgetUnderTest(onTap: onTap));
|
||||
|
||||
// act
|
||||
await tester.tap(find.byType(DemandeAideCard));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// assert
|
||||
expect(tapCalled, true);
|
||||
});
|
||||
|
||||
testWidgets('appelle onLongPress quand la carte est pressée longuement', (WidgetTester tester) async {
|
||||
// arrange
|
||||
bool longPressCalled = false;
|
||||
void onLongPress() => longPressCalled = true;
|
||||
|
||||
await tester.pumpWidget(createWidgetUnderTest(onLongPress: onLongPress));
|
||||
|
||||
// act
|
||||
await tester.longPress(find.byType(DemandeAideCard));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// assert
|
||||
expect(longPressCalled, true);
|
||||
});
|
||||
|
||||
testWidgets('affiche l\'état sélectionné quand isSelected=true', (WidgetTester tester) async {
|
||||
// arrange & act
|
||||
await tester.pumpWidget(createWidgetUnderTest(
|
||||
isSelected: true,
|
||||
showSelection: true,
|
||||
));
|
||||
|
||||
// assert
|
||||
expect(find.byIcon(Icons.check_circle), findsOneWidget);
|
||||
|
||||
// Vérifier que la carte a une bordure différente quand sélectionnée
|
||||
final card = tester.widget<Card>(find.byType(Card));
|
||||
expect(card.elevation, greaterThan(1.0));
|
||||
});
|
||||
|
||||
testWidgets('affiche l\'état non sélectionné quand isSelected=false', (WidgetTester tester) async {
|
||||
// arrange & act
|
||||
await tester.pumpWidget(createWidgetUnderTest(
|
||||
isSelected: false,
|
||||
showSelection: true,
|
||||
));
|
||||
|
||||
// assert
|
||||
expect(find.byIcon(Icons.radio_button_unchecked), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('n\'affiche pas les indicateurs de sélection quand showSelection=false', (WidgetTester tester) async {
|
||||
// arrange & act
|
||||
await tester.pumpWidget(createWidgetUnderTest(
|
||||
isSelected: true,
|
||||
showSelection: false,
|
||||
));
|
||||
|
||||
// assert
|
||||
expect(find.byIcon(Icons.check_circle), findsNothing);
|
||||
expect(find.byIcon(Icons.radio_button_unchecked), findsNothing);
|
||||
});
|
||||
});
|
||||
|
||||
group('gestion des cas limites', () {
|
||||
testWidgets('gère une demande sans montant', (WidgetTester tester) async {
|
||||
// arrange
|
||||
final demandeSansMontant = testDemande.copyWith(montantDemande: null);
|
||||
|
||||
// act
|
||||
await tester.pumpWidget(createWidgetUnderTest(demande: demandeSansMontant));
|
||||
|
||||
// assert
|
||||
expect(find.text('Montant non spécifié'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('gère une demande sans bénéficiaires', (WidgetTester tester) async {
|
||||
// arrange
|
||||
final demandeSansBeneficiaires = testDemande.copyWith(beneficiaires: const []);
|
||||
|
||||
// act
|
||||
await tester.pumpWidget(createWidgetUnderTest(demande: demandeSansBeneficiaires));
|
||||
|
||||
// assert
|
||||
expect(find.text('Aucun bénéficiaire'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('gère une demande sans tags', (WidgetTester tester) async {
|
||||
// arrange
|
||||
final demandeSansTags = testDemande.copyWith(tags: const []);
|
||||
|
||||
// act
|
||||
await tester.pumpWidget(createWidgetUnderTest(demande: demandeSansTags));
|
||||
|
||||
// assert
|
||||
// Les tags ne devraient pas être affichés
|
||||
expect(find.text('urgent'), findsNothing);
|
||||
expect(find.text('médical'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('gère une description très longue', (WidgetTester tester) async {
|
||||
// arrange
|
||||
final descriptionLongue = 'Ceci est une description très longue qui devrait être tronquée ' * 10;
|
||||
final demandeDescriptionLongue = testDemande.copyWith(description: descriptionLongue);
|
||||
|
||||
// act
|
||||
await tester.pumpWidget(createWidgetUnderTest(demande: demandeDescriptionLongue));
|
||||
|
||||
// assert
|
||||
// Vérifier que la description est tronquée (contient "...")
|
||||
final descriptionWidget = find.byType(Text).evaluate()
|
||||
.where((element) => (element.widget as Text).data?.contains('...') == true)
|
||||
.isNotEmpty;
|
||||
expect(descriptionWidget, true);
|
||||
});
|
||||
|
||||
testWidgets('gère un titre très long', (WidgetTester tester) async {
|
||||
// arrange
|
||||
final titreLong = 'Ceci est un titre très long qui devrait être géré correctement ' * 5;
|
||||
final demandeTitreLong = testDemande.copyWith(titre: titreLong);
|
||||
|
||||
// act
|
||||
await tester.pumpWidget(createWidgetUnderTest(demande: demandeTitreLong));
|
||||
|
||||
// assert
|
||||
// Le widget ne devrait pas déborder
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
});
|
||||
|
||||
group('accessibilité', () {
|
||||
testWidgets('a des labels d\'accessibilité appropriés', (WidgetTester tester) async {
|
||||
// arrange & act
|
||||
await tester.pumpWidget(createWidgetUnderTest());
|
||||
|
||||
// assert
|
||||
expect(find.bySemanticsLabel('Demande d\'aide: Aide médicale urgente'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('supporte la navigation au clavier', (WidgetTester tester) async {
|
||||
// arrange & act
|
||||
await tester.pumpWidget(createWidgetUnderTest());
|
||||
|
||||
// assert
|
||||
final inkWell = find.byType(InkWell);
|
||||
expect(inkWell, findsOneWidget);
|
||||
|
||||
final inkWellWidget = tester.widget<InkWell>(inkWell);
|
||||
expect(inkWellWidget.focusNode, isNotNull);
|
||||
});
|
||||
});
|
||||
|
||||
group('performance', () {
|
||||
testWidgets('se construit rapidement avec de nombreuses demandes', (WidgetTester tester) async {
|
||||
// arrange
|
||||
final stopwatch = Stopwatch()..start();
|
||||
|
||||
// act
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: Scaffold(
|
||||
body: ListView.builder(
|
||||
itemCount: 100,
|
||||
itemBuilder: (context, index) => DemandeAideCard(
|
||||
demande: testDemande.copyWith(
|
||||
id: 'demande-$index',
|
||||
titre: 'Demande $index',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
stopwatch.stop();
|
||||
|
||||
// assert
|
||||
expect(stopwatch.elapsedMilliseconds, lessThan(1000)); // Moins d'1 seconde
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
120
unionflow-mobile-apps/test/fixtures/demande_aide.json
vendored
Normal file
120
unionflow-mobile-apps/test/fixtures/demande_aide.json
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
{
|
||||
"id": "demande-123",
|
||||
"numeroReference": "REF-2024-001",
|
||||
"titre": "Aide médicale urgente",
|
||||
"description": "Besoin d'aide pour frais médicaux d'urgence suite à un accident",
|
||||
"typeAide": "AIDE_FINANCIERE_MEDICALE",
|
||||
"statut": "EN_ATTENTE",
|
||||
"priorite": "HAUTE",
|
||||
"estUrgente": true,
|
||||
"montantDemande": 500000.0,
|
||||
"montantApprouve": null,
|
||||
"justification": "Accident de moto nécessitant une intervention chirurgicale urgente",
|
||||
"dateCreation": "2024-01-15T10:30:00Z",
|
||||
"dateModification": "2024-01-15T14:45:00Z",
|
||||
"dateLimite": "2024-01-20T23:59:59Z",
|
||||
"dateTraitement": null,
|
||||
"organisationId": "org-1",
|
||||
"demandeurId": "user-1",
|
||||
"nomDemandeur": "Marie Kouassi",
|
||||
"emailDemandeur": "marie@example.com",
|
||||
"telephoneDemandeur": "+225123456789",
|
||||
"beneficiaires": [
|
||||
{
|
||||
"prenom": "Jean",
|
||||
"nom": "Kouassi",
|
||||
"age": 25
|
||||
}
|
||||
],
|
||||
"contactUrgence": {
|
||||
"prenom": "Paul",
|
||||
"nom": "Kouassi",
|
||||
"telephone": "+225987654321",
|
||||
"email": "paul@example.com",
|
||||
"relation": "Frère"
|
||||
},
|
||||
"localisation": {
|
||||
"adresse": "123 Rue de la Paix",
|
||||
"ville": "Abidjan",
|
||||
"codePostal": "00225",
|
||||
"pays": "Côte d'Ivoire",
|
||||
"latitude": 5.3600,
|
||||
"longitude": -4.0083
|
||||
},
|
||||
"evaluations": [
|
||||
{
|
||||
"id": "eval-1",
|
||||
"demandeId": "demande-123",
|
||||
"evaluateurId": "evaluateur-1",
|
||||
"nomEvaluateur": "Dr. Koffi",
|
||||
"typeEvaluateur": "PROFESSIONNEL_SANTE",
|
||||
"dateEvaluation": "2024-01-16T09:00:00Z",
|
||||
"noteGlobale": 4.5,
|
||||
"criteres": {
|
||||
"urgence": 5.0,
|
||||
"legitimite": 4.0,
|
||||
"faisabilite": 4.5,
|
||||
"impact": 4.5
|
||||
},
|
||||
"decision": "APPROUVE",
|
||||
"commentaires": "Cas médical urgent nécessitant une intervention rapide",
|
||||
"recommandations": "Approuver rapidement pour éviter complications",
|
||||
"piecesJustificativesValidees": true,
|
||||
"signalements": [],
|
||||
"metadonnees": {}
|
||||
}
|
||||
],
|
||||
"commentairesInternes": [
|
||||
{
|
||||
"id": "comment-1",
|
||||
"auteurId": "admin-1",
|
||||
"nomAuteur": "Admin System",
|
||||
"contenu": "Demande créée automatiquement",
|
||||
"dateCreation": "2024-01-15T10:30:00Z",
|
||||
"estPrive": true
|
||||
}
|
||||
],
|
||||
"historiqueStatuts": [
|
||||
{
|
||||
"ancienStatut": null,
|
||||
"nouveauStatut": "BROUILLON",
|
||||
"dateChangement": "2024-01-15T10:30:00Z",
|
||||
"utilisateurId": "user-1",
|
||||
"commentaire": "Création de la demande"
|
||||
},
|
||||
{
|
||||
"ancienStatut": "BROUILLON",
|
||||
"nouveauStatut": "EN_ATTENTE",
|
||||
"dateChangement": "2024-01-15T14:45:00Z",
|
||||
"utilisateurId": "user-1",
|
||||
"commentaire": "Soumission de la demande"
|
||||
}
|
||||
],
|
||||
"piecesJustificatives": [
|
||||
{
|
||||
"id": "piece-1",
|
||||
"nomFichier": "certificat_medical.pdf",
|
||||
"typeDocument": {
|
||||
"code": "CERTIFICAT_MEDICAL",
|
||||
"libelle": "Certificat médical",
|
||||
"description": "Document médical attestant de l'état de santé"
|
||||
},
|
||||
"tailleFichier": 1024000,
|
||||
"urlFichier": "/api/files/piece-1",
|
||||
"dateUpload": "2024-01-15T11:00:00Z",
|
||||
"uploadePar": "user-1",
|
||||
"estValide": true,
|
||||
"commentaires": "Certificat médical confirmant la nécessité de l'intervention"
|
||||
}
|
||||
],
|
||||
"tags": ["urgent", "médical", "accident"],
|
||||
"metadonnees": {
|
||||
"source": "mobile_app",
|
||||
"version": "1.0.0",
|
||||
"geolocalisation": {
|
||||
"latitude": 5.3600,
|
||||
"longitude": -4.0083,
|
||||
"precision": 10.0
|
||||
}
|
||||
}
|
||||
}
|
||||
74
unionflow-mobile-apps/test/fixtures/demandes_aide_list.json
vendored
Normal file
74
unionflow-mobile-apps/test/fixtures/demandes_aide_list.json
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"id": "demande-123",
|
||||
"numeroReference": "REF-2024-001",
|
||||
"titre": "Aide médicale urgente",
|
||||
"description": "Besoin d'aide pour frais médicaux d'urgence suite à un accident",
|
||||
"typeAide": "AIDE_FINANCIERE_MEDICALE",
|
||||
"statut": "EN_ATTENTE",
|
||||
"priorite": "HAUTE",
|
||||
"estUrgente": true,
|
||||
"montantDemande": 500000.0,
|
||||
"dateCreation": "2024-01-15T10:30:00Z",
|
||||
"dateModification": "2024-01-15T14:45:00Z",
|
||||
"organisationId": "org-1",
|
||||
"demandeurId": "user-1",
|
||||
"nomDemandeur": "Marie Kouassi",
|
||||
"emailDemandeur": "marie@example.com",
|
||||
"telephoneDemandeur": "+225123456789",
|
||||
"beneficiaires": [],
|
||||
"evaluations": [],
|
||||
"commentairesInternes": [],
|
||||
"historiqueStatuts": [],
|
||||
"piecesJustificatives": [],
|
||||
"tags": ["urgent", "médical"],
|
||||
"metadonnees": {}
|
||||
},
|
||||
{
|
||||
"id": "demande-124",
|
||||
"numeroReference": "REF-2024-002",
|
||||
"titre": "Aide alimentaire famille",
|
||||
"description": "Besoin d'aide alimentaire pour famille nombreuse",
|
||||
"typeAide": "AIDE_ALIMENTAIRE",
|
||||
"statut": "APPROUVE",
|
||||
"priorite": "NORMALE",
|
||||
"estUrgente": false,
|
||||
"montantDemande": 150000.0,
|
||||
"dateCreation": "2024-01-14T08:00:00Z",
|
||||
"dateModification": "2024-01-16T16:30:00Z",
|
||||
"organisationId": "org-1",
|
||||
"demandeurId": "user-2",
|
||||
"nomDemandeur": "Jean Koffi",
|
||||
"emailDemandeur": "jean@example.com",
|
||||
"telephoneDemandeur": "+225987654321",
|
||||
"beneficiaires": [
|
||||
{
|
||||
"prenom": "Marie",
|
||||
"nom": "Koffi",
|
||||
"age": 30
|
||||
},
|
||||
{
|
||||
"prenom": "Paul",
|
||||
"nom": "Koffi",
|
||||
"age": 8
|
||||
}
|
||||
],
|
||||
"evaluations": [],
|
||||
"commentairesInternes": [],
|
||||
"historiqueStatuts": [],
|
||||
"piecesJustificatives": [],
|
||||
"tags": ["famille", "alimentaire"],
|
||||
"metadonnees": {}
|
||||
}
|
||||
],
|
||||
"page": {
|
||||
"number": 0,
|
||||
"size": 20,
|
||||
"totalElements": 2,
|
||||
"totalPages": 1
|
||||
},
|
||||
"first": true,
|
||||
"last": true,
|
||||
"empty": false
|
||||
}
|
||||
31
unionflow-mobile-apps/test/fixtures/demandes_aide_urgentes.json
vendored
Normal file
31
unionflow-mobile-apps/test/fixtures/demandes_aide_urgentes.json
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
[
|
||||
{
|
||||
"id": "demande-urgent-1",
|
||||
"numeroReference": "REF-URG-001",
|
||||
"titre": "Urgence médicale - Accident",
|
||||
"description": "Accident grave nécessitant intervention chirurgicale immédiate",
|
||||
"typeAide": "AIDE_FINANCIERE_MEDICALE",
|
||||
"statut": "EN_ATTENTE",
|
||||
"priorite": "CRITIQUE",
|
||||
"estUrgente": true,
|
||||
"montantDemande": 1000000.0,
|
||||
"dateCreation": "2024-01-16T20:00:00Z",
|
||||
"dateModification": "2024-01-16T20:00:00Z",
|
||||
"dateLimite": "2024-01-17T08:00:00Z",
|
||||
"organisationId": "org-1",
|
||||
"demandeurId": "user-urgent-1",
|
||||
"nomDemandeur": "Urgence Patient",
|
||||
"emailDemandeur": "urgent@example.com",
|
||||
"telephoneDemandeur": "+225111222333",
|
||||
"beneficiaires": [],
|
||||
"evaluations": [],
|
||||
"commentairesInternes": [],
|
||||
"historiqueStatuts": [],
|
||||
"piecesJustificatives": [],
|
||||
"tags": ["urgent", "critique", "médical"],
|
||||
"metadonnees": {
|
||||
"urgenceLevel": "CRITIQUE",
|
||||
"timeRemaining": "12h"
|
||||
}
|
||||
}
|
||||
]
|
||||
13
unionflow-mobile-apps/test/fixtures/fixture_reader.dart
vendored
Normal file
13
unionflow-mobile-apps/test/fixtures/fixture_reader.dart
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import 'dart:io';
|
||||
|
||||
/// Utilitaire pour lire les fichiers de fixtures de test
|
||||
///
|
||||
/// Cette classe fournit une méthode simple pour charger
|
||||
/// les données de test depuis des fichiers JSON.
|
||||
String fixture(String name) {
|
||||
final file = File('test/fixtures/$name');
|
||||
if (!file.existsSync()) {
|
||||
throw Exception('Fixture file not found: test/fixtures/$name');
|
||||
}
|
||||
return file.readAsStringSync();
|
||||
}
|
||||
90
unionflow-mobile-apps/test/fixtures/mes_demandes.json
vendored
Normal file
90
unionflow-mobile-apps/test/fixtures/mes_demandes.json
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"id": "ma-demande-1",
|
||||
"numeroReference": "REF-ME-001",
|
||||
"titre": "Ma demande d'aide logement",
|
||||
"description": "Demande d'aide pour le loyer suite à perte d'emploi",
|
||||
"typeAide": "AIDE_FINANCIERE_LOGEMENT",
|
||||
"statut": "EN_COURS",
|
||||
"priorite": "HAUTE",
|
||||
"estUrgente": true,
|
||||
"montantDemande": 300000.0,
|
||||
"dateCreation": "2024-01-10T14:00:00Z",
|
||||
"dateModification": "2024-01-15T10:00:00Z",
|
||||
"organisationId": "org-1",
|
||||
"demandeurId": "user-1",
|
||||
"nomDemandeur": "Mon Nom",
|
||||
"emailDemandeur": "mon@example.com",
|
||||
"telephoneDemandeur": "+225123456789",
|
||||
"beneficiaires": [
|
||||
{
|
||||
"prenom": "Mon Enfant",
|
||||
"nom": "Nom",
|
||||
"age": 5
|
||||
}
|
||||
],
|
||||
"evaluations": [
|
||||
{
|
||||
"id": "eval-me-1",
|
||||
"demandeId": "ma-demande-1",
|
||||
"evaluateurId": "eval-1",
|
||||
"nomEvaluateur": "Evaluateur Social",
|
||||
"typeEvaluateur": "TRAVAILLEUR_SOCIAL",
|
||||
"dateEvaluation": "2024-01-12T09:00:00Z",
|
||||
"noteGlobale": 4.2,
|
||||
"decision": "EN_COURS",
|
||||
"commentaires": "Situation justifiée, vérifications en cours"
|
||||
}
|
||||
],
|
||||
"commentairesInternes": [],
|
||||
"historiqueStatuts": [
|
||||
{
|
||||
"ancienStatut": null,
|
||||
"nouveauStatut": "BROUILLON",
|
||||
"dateChangement": "2024-01-10T14:00:00Z",
|
||||
"utilisateurId": "user-1",
|
||||
"commentaire": "Création"
|
||||
},
|
||||
{
|
||||
"ancienStatut": "BROUILLON",
|
||||
"nouveauStatut": "EN_ATTENTE",
|
||||
"dateChangement": "2024-01-10T15:00:00Z",
|
||||
"utilisateurId": "user-1",
|
||||
"commentaire": "Soumission"
|
||||
},
|
||||
{
|
||||
"ancienStatut": "EN_ATTENTE",
|
||||
"nouveauStatut": "EN_COURS",
|
||||
"dateChangement": "2024-01-12T09:00:00Z",
|
||||
"utilisateurId": "eval-1",
|
||||
"commentaire": "Prise en charge"
|
||||
}
|
||||
],
|
||||
"piecesJustificatives": [
|
||||
{
|
||||
"id": "piece-me-1",
|
||||
"nomFichier": "attestation_pole_emploi.pdf",
|
||||
"typeDocument": {
|
||||
"code": "ATTESTATION_CHOMAGE",
|
||||
"libelle": "Attestation Pôle Emploi"
|
||||
},
|
||||
"tailleFichier": 512000,
|
||||
"dateUpload": "2024-01-10T14:30:00Z",
|
||||
"estValide": true
|
||||
}
|
||||
],
|
||||
"tags": ["logement", "urgent", "chomage"],
|
||||
"metadonnees": {}
|
||||
}
|
||||
],
|
||||
"page": {
|
||||
"number": 0,
|
||||
"size": 20,
|
||||
"totalElements": 1,
|
||||
"totalPages": 1
|
||||
},
|
||||
"first": true,
|
||||
"last": true,
|
||||
"empty": false
|
||||
}
|
||||
7
unionflow-mobile-apps/test/simple_test.dart
Normal file
7
unionflow-mobile-apps/test/simple_test.dart
Normal file
@@ -0,0 +1,7 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
test('test simple', () {
|
||||
expect(1 + 1, 2);
|
||||
});
|
||||
}
|
||||
315
unionflow-mobile-apps/test/test_config.dart
Normal file
315
unionflow-mobile-apps/test/test_config.dart
Normal file
@@ -0,0 +1,315 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// Configuration globale pour les tests
|
||||
///
|
||||
/// Cette classe configure l'environnement de test pour
|
||||
/// garantir des conditions cohérentes et reproductibles.
|
||||
class TestConfig {
|
||||
static bool _initialized = false;
|
||||
|
||||
/// Initialise l'environnement de test
|
||||
static Future<void> initialize() async {
|
||||
if (_initialized) return;
|
||||
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Configuration des SharedPreferences pour les tests
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
|
||||
// Configuration des canaux de méthodes pour les tests
|
||||
_setupMethodChannels();
|
||||
|
||||
// Configuration des polices pour les tests de widgets
|
||||
_setupFonts();
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
/// Configure les canaux de méthodes mockés
|
||||
static void _setupMethodChannels() {
|
||||
// Canal pour les permissions
|
||||
const MethodChannel('flutter.baseflow.com/permissions/methods')
|
||||
.setMockMethodCallHandler((MethodCall methodCall) async {
|
||||
switch (methodCall.method) {
|
||||
case 'checkPermissionStatus':
|
||||
return 1; // PermissionStatus.granted
|
||||
case 'requestPermissions':
|
||||
return {0: 1}; // Permission granted
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Canal pour la géolocalisation
|
||||
const MethodChannel('flutter.baseflow.com/geolocator')
|
||||
.setMockMethodCallHandler((MethodCall methodCall) async {
|
||||
switch (methodCall.method) {
|
||||
case 'getCurrentPosition':
|
||||
return {
|
||||
'latitude': 5.3600,
|
||||
'longitude': -4.0083,
|
||||
'timestamp': DateTime.now().millisecondsSinceEpoch,
|
||||
'accuracy': 10.0,
|
||||
'altitude': 0.0,
|
||||
'heading': 0.0,
|
||||
'speed': 0.0,
|
||||
'speedAccuracy': 0.0,
|
||||
};
|
||||
case 'getLocationAccuracy':
|
||||
return 1; // LocationAccuracy.best
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Canal pour le partage de fichiers
|
||||
const MethodChannel('plugins.flutter.io/share')
|
||||
.setMockMethodCallHandler((MethodCall methodCall) async {
|
||||
switch (methodCall.method) {
|
||||
case 'share':
|
||||
return null; // Succès silencieux
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Canal pour l'ouverture d'URLs
|
||||
const MethodChannel('plugins.flutter.io/url_launcher')
|
||||
.setMockMethodCallHandler((MethodCall methodCall) async {
|
||||
switch (methodCall.method) {
|
||||
case 'launch':
|
||||
return true; // URL ouverte avec succès
|
||||
case 'canLaunch':
|
||||
return true; // URL peut être ouverte
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Canal pour la sélection de fichiers
|
||||
const MethodChannel('miguelruivo.flutter.plugins.filepicker')
|
||||
.setMockMethodCallHandler((MethodCall methodCall) async {
|
||||
switch (methodCall.method) {
|
||||
case 'any':
|
||||
return {
|
||||
'files': [
|
||||
{
|
||||
'name': 'test_document.pdf',
|
||||
'path': '/mock/path/test_document.pdf',
|
||||
'size': 1024000,
|
||||
'bytes': null,
|
||||
}
|
||||
]
|
||||
};
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Configure les polices pour les tests de widgets
|
||||
static void _setupFonts() {
|
||||
// Chargement des polices Material Design
|
||||
final binding = TestWidgetsFlutterBinding.ensureInitialized();
|
||||
binding.defaultBinaryMessenger.setMockDecodedMessageHandler<dynamic>(
|
||||
const StandardMethodCodec(),
|
||||
(dynamic message) async {
|
||||
if (message is Map && message['method'] == 'SystemChrome.setApplicationSwitcherDescription') {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Nettoie l'environnement de test après chaque test
|
||||
static Future<void> cleanup() async {
|
||||
// Réinitialiser les SharedPreferences
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.clear();
|
||||
|
||||
// Nettoyer les canaux de méthodes
|
||||
_clearMethodChannels();
|
||||
}
|
||||
|
||||
/// Nettoie les canaux de méthodes
|
||||
static void _clearMethodChannels() {
|
||||
const MethodChannel('flutter.baseflow.com/permissions/methods')
|
||||
.setMockMethodCallHandler(null);
|
||||
const MethodChannel('flutter.baseflow.com/geolocator')
|
||||
.setMockMethodCallHandler(null);
|
||||
const MethodChannel('plugins.flutter.io/share')
|
||||
.setMockMethodCallHandler(null);
|
||||
const MethodChannel('plugins.flutter.io/url_launcher')
|
||||
.setMockMethodCallHandler(null);
|
||||
const MethodChannel('miguelruivo.flutter.plugins.filepicker')
|
||||
.setMockMethodCallHandler(null);
|
||||
}
|
||||
}
|
||||
|
||||
/// Classe utilitaire pour les données de test
|
||||
class TestData {
|
||||
/// Données de test pour une demande d'aide
|
||||
static Map<String, dynamic> get demandeAideJson => {
|
||||
'id': 'demande-test-123',
|
||||
'numeroReference': 'REF-TEST-001',
|
||||
'titre': 'Test Aide Médicale',
|
||||
'description': 'Description de test pour aide médicale',
|
||||
'typeAide': 'AIDE_FINANCIERE_MEDICALE',
|
||||
'statut': 'BROUILLON',
|
||||
'priorite': 'NORMALE',
|
||||
'estUrgente': false,
|
||||
'montantDemande': 100000.0,
|
||||
'dateCreation': '2024-01-15T10:00:00Z',
|
||||
'dateModification': '2024-01-15T10:00:00Z',
|
||||
'organisationId': 'org-test',
|
||||
'demandeurId': 'user-test',
|
||||
'nomDemandeur': 'Test User',
|
||||
'emailDemandeur': 'test@example.com',
|
||||
'telephoneDemandeur': '+225123456789',
|
||||
'beneficiaires': [],
|
||||
'evaluations': [],
|
||||
'commentairesInternes': [],
|
||||
'historiqueStatuts': [],
|
||||
'piecesJustificatives': [],
|
||||
'tags': ['test'],
|
||||
'metadonnees': {},
|
||||
};
|
||||
|
||||
/// Données de test pour une proposition d'aide
|
||||
static Map<String, dynamic> get propositionAideJson => {
|
||||
'id': 'proposition-test-123',
|
||||
'titre': 'Test Proposition Aide',
|
||||
'description': 'Description de test pour proposition',
|
||||
'typeAide': 'AIDE_FINANCIERE_MEDICALE',
|
||||
'statut': 'ACTIVE',
|
||||
'montantMaximum': 200000.0,
|
||||
'dateCreation': '2024-01-15T10:00:00Z',
|
||||
'organisationId': 'org-test',
|
||||
'proposantId': 'proposant-test',
|
||||
'nomProposant': 'Test Proposant',
|
||||
'emailProposant': 'proposant@example.com',
|
||||
'telephoneProposant': '+225987654321',
|
||||
'capacites': [],
|
||||
'disponibilites': [],
|
||||
'criteres': [],
|
||||
'statistiques': {},
|
||||
'metadonnees': {},
|
||||
};
|
||||
|
||||
/// Données de test pour une évaluation
|
||||
static Map<String, dynamic> get evaluationAideJson => {
|
||||
'id': 'evaluation-test-123',
|
||||
'demandeId': 'demande-test-123',
|
||||
'evaluateurId': 'evaluateur-test',
|
||||
'nomEvaluateur': 'Test Evaluateur',
|
||||
'typeEvaluateur': 'ADMINISTRATEUR',
|
||||
'dateEvaluation': '2024-01-16T10:00:00Z',
|
||||
'noteGlobale': 4.0,
|
||||
'criteres': {
|
||||
'urgence': 4.0,
|
||||
'legitimite': 4.0,
|
||||
'faisabilite': 4.0,
|
||||
'impact': 4.0,
|
||||
},
|
||||
'decision': 'APPROUVE',
|
||||
'commentaires': 'Évaluation de test',
|
||||
'recommandations': 'Recommandations de test',
|
||||
'piecesJustificativesValidees': true,
|
||||
'signalements': [],
|
||||
'metadonnees': {},
|
||||
};
|
||||
}
|
||||
|
||||
/// Classe utilitaire pour les assertions personnalisées
|
||||
class TestAssertions {
|
||||
/// Vérifie qu'une demande d'aide a les propriétés attendues
|
||||
static void assertDemandeAideValid(dynamic demande) {
|
||||
expect(demande.id, isNotEmpty);
|
||||
expect(demande.titre, isNotEmpty);
|
||||
expect(demande.description, isNotEmpty);
|
||||
expect(demande.typeAide, isNotNull);
|
||||
expect(demande.statut, isNotNull);
|
||||
expect(demande.priorite, isNotNull);
|
||||
expect(demande.dateCreation, isNotNull);
|
||||
expect(demande.organisationId, isNotEmpty);
|
||||
expect(demande.demandeurId, isNotEmpty);
|
||||
expect(demande.nomDemandeur, isNotEmpty);
|
||||
expect(demande.emailDemandeur, isNotEmpty);
|
||||
}
|
||||
|
||||
/// Vérifie qu'une proposition d'aide a les propriétés attendues
|
||||
static void assertPropositionAideValid(dynamic proposition) {
|
||||
expect(proposition.id, isNotEmpty);
|
||||
expect(proposition.titre, isNotEmpty);
|
||||
expect(proposition.description, isNotEmpty);
|
||||
expect(proposition.typeAide, isNotNull);
|
||||
expect(proposition.statut, isNotNull);
|
||||
expect(proposition.dateCreation, isNotNull);
|
||||
expect(proposition.organisationId, isNotEmpty);
|
||||
expect(proposition.proposantId, isNotEmpty);
|
||||
expect(proposition.nomProposant, isNotEmpty);
|
||||
}
|
||||
|
||||
/// Vérifie qu'une évaluation a les propriétés attendues
|
||||
static void assertEvaluationValid(dynamic evaluation) {
|
||||
expect(evaluation.id, isNotEmpty);
|
||||
expect(evaluation.demandeId, isNotEmpty);
|
||||
expect(evaluation.evaluateurId, isNotEmpty);
|
||||
expect(evaluation.nomEvaluateur, isNotEmpty);
|
||||
expect(evaluation.typeEvaluateur, isNotNull);
|
||||
expect(evaluation.dateEvaluation, isNotNull);
|
||||
expect(evaluation.noteGlobale, greaterThanOrEqualTo(0.0));
|
||||
expect(evaluation.noteGlobale, lessThanOrEqualTo(5.0));
|
||||
expect(evaluation.decision, isNotNull);
|
||||
}
|
||||
}
|
||||
|
||||
/// Classe utilitaire pour les mocks
|
||||
class TestMocks {
|
||||
/// Crée un mock de réponse HTTP réussie
|
||||
static Map<String, dynamic> createSuccessResponse(dynamic data) {
|
||||
return {
|
||||
'success': true,
|
||||
'data': data,
|
||||
'message': 'Opération réussie',
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
};
|
||||
}
|
||||
|
||||
/// Crée un mock de réponse HTTP d'erreur
|
||||
static Map<String, dynamic> createErrorResponse(String message, {int code = 500}) {
|
||||
return {
|
||||
'success': false,
|
||||
'error': {
|
||||
'code': code,
|
||||
'message': message,
|
||||
'details': null,
|
||||
},
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
};
|
||||
}
|
||||
|
||||
/// Crée un mock de réponse paginée
|
||||
static Map<String, dynamic> createPagedResponse(List<dynamic> content, {
|
||||
int page = 0,
|
||||
int size = 20,
|
||||
int totalElements = 0,
|
||||
int totalPages = 0,
|
||||
}) {
|
||||
return {
|
||||
'content': content,
|
||||
'page': {
|
||||
'number': page,
|
||||
'size': size,
|
||||
'totalElements': totalElements ?? content.length,
|
||||
'totalPages': totalPages ?? ((totalElements ?? content.length) / size).ceil(),
|
||||
},
|
||||
'first': page == 0,
|
||||
'last': page >= (totalPages - 1),
|
||||
'empty': content.isEmpty,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user