using System.Text.Json; using Omika.App.Local.Models; using Omika.App.Local.Services; using Omika.App.Navigation; using Omika.App.ViewModels; using Omika.Core.Contracts.Organisation; namespace Omika.Tests.Mobile.Local; /// /// Tests du ViewModel de la liste des collaborateurs : chargement local, /// états vide/erreur, recherche locale et navigation vers la fiche /// Collaborateur par identité publique. Aucun appel API. /// public class CollaborateursViewModelTests : LocalTestBase { private readonly CollaborateursViewModel _viewModel; private readonly NavigationService _navigation; public CollaborateursViewModelTests() { var service = new CollaborateurLocalService(CollaborateurRepository, RendezVousRepository, ContextInterventionCacheRepository, ApiClient, SessionService); _navigation = new NavigationService(); _viewModel = new CollaborateursViewModel(service, _navigation); } private static LocalCollaborateur CreateCollaborateur( Guid id, string? nom = null, string? prenom = null, string? code = null, string? statut = null, string? email = null) { var dto = new CollaborateurDetailsDto { Id = id.ToString(), Nom = nom, Prenom = prenom, Code = code, Statut = statut is null ? null : new Omika.Core.Contracts.Common.ReferenceValueDto { Label = statut }, Coordonnees = email is null ? null : new Omika.Core.Contracts.Common.ContactDetailsDto { Email = email } }; return new LocalCollaborateur { Id = id, PayloadJson = JsonSerializer.Serialize(dto), UpdatedAtUtc = DateTimeOffset.UtcNow }; } [Fact] public async Task Load_ChargementPuisListe() { // Arrange Guid collaborateurId = Guid.NewGuid(); await CollaborateurRepository.UpsertAsync(CreateCollaborateur(collaborateurId, "Dupont", "Jean", "C001")); // Act await _viewModel.LoadCommand.ExecuteAsync(null); // Assert : après chargement, liste affichée et chargement terminé. Assert.False(_viewModel.IsLoading); Assert.True(_viewModel.HasCollaborateurs); var item = Assert.Single(_viewModel.Collaborateurs); Assert.Equal(collaborateurId, item.CollaborateurGuid); } [Fact] public async Task Load_ListeVide_EmptyState() { // Act await _viewModel.LoadCommand.ExecuteAsync(null); // Assert : liste vide sans erreur. Assert.False(_viewModel.IsLoading); Assert.False(_viewModel.HasCollaborateurs); Assert.True(_viewModel.IsEmpty); Assert.Null(_viewModel.ErrorMessage); } [Fact] public async Task Load_Erreur_MessageAffiche() { // Arrange : un payload invalide ne provoque pas d'erreur (ignoré). await CollaborateurRepository.UpsertAsync(new LocalCollaborateur { Id = Guid.NewGuid(), PayloadJson = "not-json", UpdatedAtUtc = DateTimeOffset.UtcNow }); // Act await _viewModel.LoadCommand.ExecuteAsync(null); // Assert : l'entrée invalide est ignorée, liste vide sans erreur. Assert.False(_viewModel.IsLoading); Assert.False(_viewModel.HasCollaborateurs); Assert.True(_viewModel.IsEmpty); Assert.Null(_viewModel.ErrorMessage); } [Fact] public async Task Recherche_FiltreParNom() { // Arrange await CollaborateurRepository.UpsertAsync(CreateCollaborateur(Guid.NewGuid(), "Dupont", "Jean", "C001")); await CollaborateurRepository.UpsertAsync(CreateCollaborateur(Guid.NewGuid(), "Martin", "Paul", "C002")); await _viewModel.LoadCommand.ExecuteAsync(null); // Act _viewModel.SearchText = "dupont"; // Assert : la recherche filtre par nom (insensible à la casse). var item = Assert.Single(_viewModel.Collaborateurs); Assert.Equal("Jean Dupont", item.DisplayName); } [Fact] public async Task Recherche_SansResultat_ListeVide() { // Arrange await CollaborateurRepository.UpsertAsync(CreateCollaborateur(Guid.NewGuid(), "Dupont", "Jean", "C001")); await _viewModel.LoadCommand.ExecuteAsync(null); // Act _viewModel.SearchText = "introuvable"; // Assert : aucun résultat. Assert.Empty(_viewModel.Collaborateurs); Assert.False(_viewModel.HasCollaborateurs); } [Fact] public async Task Recherche_Reinitialise_RetourneTous() { // Arrange await CollaborateurRepository.UpsertAsync(CreateCollaborateur(Guid.NewGuid(), "Dupont", "Jean", "C001")); await CollaborateurRepository.UpsertAsync(CreateCollaborateur(Guid.NewGuid(), "Martin", "Paul", "C002")); await _viewModel.LoadCommand.ExecuteAsync(null); // Act : recherche puis réinitialisation. _viewModel.SearchText = "dupont"; Assert.Single(_viewModel.Collaborateurs); _viewModel.SearchText = string.Empty; // Assert : tous les collaborateurs sont de nouveau affichés. Assert.Equal(2, _viewModel.Collaborateurs.Count); } [Fact] public async Task OpenCollaborateur_NavigueVersDetail() { // Arrange Guid collaborateurId = Guid.NewGuid(); await CollaborateurRepository.UpsertAsync(CreateCollaborateur(collaborateurId, "Dupont", "Jean", "C001")); await _viewModel.LoadCommand.ExecuteAsync(null); // Act _viewModel.OpenCollaborateurCommand.Execute(_viewModel.Collaborateurs[0]); // Assert : navigation vers la fiche Collaborateur avec le GUID public. Assert.Equal(AppPage.CollaborateurDetail, _navigation.CurrentPage); Assert.Equal(collaborateurId, _navigation.CurrentParameter); } [Fact] public async Task OpenCollaborateur_Null_NeNaviguePas() { // Act _viewModel.OpenCollaborateurCommand.Execute(null); // Assert Assert.Equal(AppPage.Home, _navigation.CurrentPage); } [Fact] public async Task FonctionneOffline_SansAppelApi() { // Arrange : aucune dépendance réseau, uniquement SQLite local. await CollaborateurRepository.UpsertAsync(CreateCollaborateur(Guid.NewGuid(), "Dupont", "Jean", "C001")); // Act await _viewModel.LoadCommand.ExecuteAsync(null); // Assert Assert.True(_viewModel.HasCollaborateurs); } }