Premiere commit - Omika

This commit is contained in:
2026-10-23 18:46:50 +02:00
commit 8ea76c1b24
2155 changed files with 984301 additions and 0 deletions
@@ -0,0 +1,241 @@
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;
/// <summary>
/// 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é.
/// </summary>
/// <remarks>
/// 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).
/// </remarks>
[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<string> 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<string> 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<string> 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<string> 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();
/// <summary>
/// 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).
/// </summary>
private static IReadOnlyList<string> TextesAffiches(Window fenetre)
{
Dispatcher.UIThread.RunJobs();
var textes = fenetre.GetVisualDescendants()
.OfType<TextBlock>()
.Where(t => t.IsEffectivelyVisible && !string.IsNullOrWhiteSpace(t.Text))
.Select(t => t.Text!)
.ToArray();
Assert.NotEmpty(textes);
return textes;
}
}