using Avalonia; using Avalonia.Headless; using Omika.App.Local.Abstractions; using Omika.App.Local.Models; using Omika.App.Navigation; using Omika.App.Services; using Omika.App.ViewModels; using Omika.App.Views; using Omika.Core.Contracts.Common; using Omika.Core.Contracts.Mobile.Interventions; using Omika.Core.Contracts.Mobile.Sync; using Omika.Tests.Mobile; namespace Omika.Tests.Mobile.Local; /// /// Tests de construction de la vue Interventions (régression runtime du /// micro-lot 36 : la page doit pouvoir être construite avec zéro donnée, /// sans exception d'InitializeComponent ni de binding). /// /// /// La construction d'une vue Avalonia exige les services de plateforme : les /// tests s'exécutent donc DANS une session Avalonia headless (sinon ils échouent /// sur « Unable to locate Avalonia.Platform.IPlatformRenderInterface »), et la /// classe rejoint la collection « ApplicationRuntimeContext » pour ne jamais /// s'exécuter en parallèle d'une autre session headless. /// [Collection("ApplicationRuntimeContext")] public class InterventionsViewConstructionTests : IDisposable { private readonly HeadlessUnitTestSession _session = HeadlessUnitTestSession.StartNew(typeof(LayoutTestAppBuilder)); public void Dispose() => _session.Dispose(); [Fact] public void InterventionsView_Constructible_SansException() { // Dispatch générique : cette surcharge ATTEND la fin de l'action, ce qui // n'est pas le cas de Dispatch(Action) — les assertions seraient alors // évaluées hors du test. _session.Dispatch( () => { var view = new InterventionsView(); Assert.NotNull(view); Assert.NotNull(view.Content); return true; }, CancellationToken.None).GetAwaiter().GetResult(); } [Fact] public void InterventionsView_AvecViewModelVide_AucuneException() { _session.Dispatch( () => { var viewModel = new InterventionsViewModel( new FakeInterventionService(), new FakeParametreMetierService(), new NavigationService(), new PermissionService(), Omika.Core.Observability.NullTelemetryService.Instance); var view = new InterventionsView { DataContext = viewModel }; Assert.NotNull(view.Content); return true; }, CancellationToken.None).GetAwaiter().GetResult(); } private sealed class FakeInterventionService : IInterventionLocalService { public Task> GetInterventionsAsync(CancellationToken cancellationToken = default) => Task.FromResult>([]); public Task GetInterventionByIdAsync(Guid interventionGuid, CancellationToken cancellationToken = default) => Task.FromResult(null); public Task CreateAsync(InterventionSyncPayload payload, CancellationToken cancellationToken = default) => throw new NotSupportedException(); public Task UpdateAsync(Guid interventionGuid, InterventionSyncPayload payload, CancellationToken cancellationToken = default) => throw new NotSupportedException(); public Task ArchiveAsync(Guid interventionGuid, CancellationToken cancellationToken = default) => throw new NotSupportedException(); public Task> SearchAsync(MobileInterventionSearchRequest request, CancellationToken cancellationToken = default) => Task.FromResult>([]); public Task> GetLastSearchAsync(CancellationToken cancellationToken = default) => Task.FromResult>([]); public Task> GetPinnedAsync(CancellationToken cancellationToken = default) => Task.FromResult>([]); public Task SetPinnedAsync(Guid interventionGuid, bool isPinned, CancellationToken cancellationToken = default) => Task.CompletedTask; public Task RefreshPinnedAsync(CancellationToken cancellationToken = default) => Task.CompletedTask; public Task RefreshInterventionAsync(Guid interventionGuid, CancellationToken cancellationToken = default) => Task.CompletedTask; } private sealed class FakeParametreMetierService : IParametreMetierLocalService { public Task> GetTypeTempsAsync(CancellationToken ct = default) => Task.FromResult>([]); public Task GetTypeTempsParametresAsync(string typeTempsKey, CancellationToken ct = default) => Task.FromResult(null); public Task GetTypeRdvParametresAsync(string typeRdvKey, CancellationToken ct = default) => Task.FromResult(null); public Task GetStatutRdvParametresAsync(string statutRdvKey, CancellationToken ct = default) => Task.FromResult(null); public Task> GetInterventionTypesAsync(CancellationToken ct = default) => Task.FromResult>([]); public Task> GetInterventionEtatsAsync(CancellationToken ct = default) => Task.FromResult>([]); public Task> GetDossierTypesAsync(CancellationToken ct = default) => Task.FromResult>([]); public Task> GetDossierStatutsAsync(CancellationToken ct = default) => Task.FromResult>([]); public Task> GetClientTypesAsync(CancellationToken ct = default) => Task.FromResult>([]); public Task> GetClientStatutsAsync(CancellationToken ct = default) => Task.FromResult>([]); public Task> GetSiteTypesAsync(CancellationToken ct = default) => Task.FromResult>([]); public Task> GetTypeSignatairesAsync(CancellationToken cancellationToken = default) => Task.FromResult>([]); } private sealed class FakeClientService : IClientLocalService { public Task> GetClientsAsync(CancellationToken cancellationToken = default) => Task.FromResult>([]); public Task GetClientByIdAsync(Guid clientGuid, CancellationToken cancellationToken = default) => Task.FromResult(null); } private sealed class FakeSiteService : ISiteLocalService { public Task> GetSitesAsync(CancellationToken cancellationToken = default) => Task.FromResult>([]); public Task GetSiteByIdAsync(Guid siteGuid, CancellationToken cancellationToken = default) => Task.FromResult(null); } private sealed class FakeCollaborateurService : ICollaborateurLocalService { public Task> GetCollaborateursAsync(CancellationToken cancellationToken = default) => Task.FromResult>([]); public Task GetCollaborateurByIdAsync(Guid collaborateurGuid, CancellationToken cancellationToken = default) => Task.FromResult(null); } }