using System; using System.Collections.Generic; using System.Linq; using System.Threading; using Avalonia.Controls; using Avalonia.Headless; using Avalonia.Threading; using Avalonia.VisualTree; using Omika.App.Local.Models; using Omika.App.Navigation; using Omika.App.Services; using Omika.App.ViewModels; using Omika.App.Views; using Omika.Core.Constants; using Xunit; namespace Omika.Tests.Mobile; /// /// Les blocs « Informations » (fiche Client) et « Accès et note » (fiche Site) /// n'ont plus de titre (les champs sont explicites) et ne sont plus affichés du /// tout quand aucune information n'est renseignée : plus de carte vide. /// Ces tests inspectent l'arbre visuel RÉELLEMENT affiché. /// /// /// Session Avalonia headless : la classe rejoint la collection /// « ApplicationRuntimeContext » pour qu'aucune autre session Avalonia ne /// tourne en parallèle (le localisateur d'Avalonia est global au processus). /// [Collection("ApplicationRuntimeContext")] public sealed class BlocsInformationsFicheTests : IDisposable { private readonly HeadlessUnitTestSession _session = HeadlessUnitTestSession.StartNew(typeof(LayoutTestAppBuilder)); public void Dispose() => _session.Dispose(); // ===== Fiche Client ===== [Fact] public void FicheClient_BlocInformations_MasqueQuandVide_EtSansTitre() { Executer(() => { var vueModele = new ClientDetailViewModel(null!, new NavigationService(), Permissions()) { Client = new ClientDetail { ClientGuid = Guid.NewGuid(), Nom = "REPETTO IMMOBILIER", Code = "CLI-00123" } }; Window fenetre = MesureGeometrique.Ouvrir(new ClientDetailView { DataContext = vueModele }, 420); IReadOnlyList textes = TextesAffiches(fenetre); // Aucune information renseignée : le bloc entier disparaît. Assert.DoesNotContain("Informations", textes); Assert.DoesNotContain("Forme juridique", textes); Assert.DoesNotContain("SIREN", textes); Assert.DoesNotContain("SIRET", textes); Assert.DoesNotContain("Pays", textes); Assert.DoesNotContain("Note", textes); // Le reste de la fiche reste affiché. Assert.Contains("REPETTO IMMOBILIER", textes); Assert.Contains("← Retour", textes); fenetre.Close(); }); } [Fact] public void FicheClient_BlocInformations_AfficheSansTitre_QuandRenseigne() { Executer(() => { var vueModele = new ClientDetailViewModel(null!, new NavigationService(), Permissions()) { Client = new ClientDetail { ClientGuid = Guid.NewGuid(), Nom = "REPETTO IMMOBILIER", FormeJuridique = "SARL", InformationsComplementaires = "Client historique depuis 2011." } }; Window fenetre = MesureGeometrique.Ouvrir(new ClientDetailView { DataContext = vueModele }, 420); IReadOnlyList textes = TextesAffiches(fenetre); // Les champs renseignés sont affichés, sans titre de bloc. Assert.Contains("Forme juridique", textes); Assert.Contains("SARL", textes); Assert.Contains("Note", textes); Assert.Contains("Client historique depuis 2011.", textes); Assert.DoesNotContain("Informations", textes); // Les champs non renseignés restent masqués (pas de libellé orphelin). Assert.DoesNotContain("SIREN", textes); Assert.DoesNotContain("SIRET", textes); Assert.DoesNotContain("Pays", textes); fenetre.Close(); }); } // ===== Fiche Site ===== [Fact] public void FicheSite_BlocAccesEtNote_MasqueQuandVide_EtSansTitre() { Executer(() => { var vueModele = new SiteDetailViewModel(null!, new NavigationService(), Permissions(), null!) { Site = new SiteDetail { SiteGuid = Guid.NewGuid(), Nom = "Siège social Marseille", Code = "SIT-0001" } }; Window fenetre = MesureGeometrique.Ouvrir(new SiteDetailView { DataContext = vueModele }, 420); IReadOnlyList textes = TextesAffiches(fenetre); Assert.DoesNotContain("Accès et note", textes); Assert.DoesNotContain("Code d'accès", textes); Assert.DoesNotContain("Note", textes); // Le reste de la fiche reste affiché. Assert.Contains("Siège social Marseille", textes); Assert.Contains("← Retour", textes); fenetre.Close(); }); } [Fact] public void FicheSite_BlocAccesEtNote_AfficheSansTitre_QuandRenseigne() { Executer(() => { var vueModele = new SiteDetailViewModel(null!, new NavigationService(), Permissions(), null!) { Site = new SiteDetail { SiteGuid = Guid.NewGuid(), Nom = "Siège social Marseille", CodeAcces = "4512A", InformationsComplementaires = "Sonner à l'interphone bâtiment B." } }; Window fenetre = MesureGeometrique.Ouvrir(new SiteDetailView { DataContext = vueModele }, 420); IReadOnlyList textes = TextesAffiches(fenetre); Assert.Contains("Code d'accès", textes); Assert.Contains("4512A", textes); Assert.Contains("Note", textes); Assert.Contains("Sonner à l'interphone bâtiment B.", textes); Assert.DoesNotContain("Accès et note", textes); fenetre.Close(); }); } // ===== Calcul « renseigné » ===== [Fact] public void Modele_UneInformationBlanche_CompteCommeAbsente() { var client = new ClientDetail { FormeJuridique = " ", Siren = string.Empty, Siret = null, Pays = "\t", InformationsComplementaires = " " }; Assert.False(client.HasFormeJuridique); Assert.False(client.HasSiren); Assert.False(client.HasSiret); Assert.False(client.HasPays); Assert.False(client.HasInformationsComplementaires); Assert.False(client.HasInformations); var site = new SiteDetail { CodeAcces = " ", InformationsComplementaires = string.Empty }; Assert.False(site.HasCodeAcces); Assert.False(site.HasInformationsComplementaires); Assert.False(site.HasAccesOuNote); Assert.True(new ClientDetail { Siren = "123456789" }.HasInformations); Assert.True(new SiteDetail { CodeAcces = "4512A" }.HasAccesOuNote); Assert.True(new SiteDetail { InformationsComplementaires = "Note seule" }.HasAccesOuNote); } // ===== Outils ===== private static PermissionService Permissions() { var permissions = new PermissionService(); permissions.SetRights(MobilePermissionKeys.All.ToDictionary(k => k, _ => true)); return permissions; } private void Executer(Action scenario) => _session.Dispatch( () => { scenario(); return true; }, CancellationToken.None).GetAwaiter().GetResult(); /// /// Textes réellement affichés : un contrôle dont un parent est masqué est /// exclu (sinon un libellé d'un bloc masqué passerait pour visible). /// private static IReadOnlyList TextesAffiches(Window fenetre) { Dispatcher.UIThread.RunJobs(); var textes = fenetre.GetVisualDescendants() .OfType() .Where(t => t.IsEffectivelyVisible && !string.IsNullOrWhiteSpace(t.Text)) .Select(t => t.Text!) .ToArray(); Assert.NotEmpty(textes); return textes; } }