using Omika.App.Local.Abstractions; using Omika.App.Local.Models; using Omika.App.Navigation; using Omika.App.ViewModels; namespace Omika.Tests.Mobile.Local; /// /// Tests du ViewModel Détail Contact (lecture seule) : chargement local, /// coordonnées disponibles, actions téléphone / email via des fakes plateforme /// et retour vers la liste des contacts de l'intervention. /// public class ContactDetailViewModelTests { private sealed class FakeContactsService : IInterventionContactsService { public InterventionContact? ContactToReturn { get; set; } public bool ThrowOnLoad { get; set; } public Task> GetContactsForInterventionAsync( Guid interventionGuid, CancellationToken ct = default) => Task.FromResult>([]); public Task GetContactByIdAsync(Guid contactGuid, CancellationToken ct = default) { if (ThrowOnLoad) { throw new InvalidOperationException("Erreur de chargement"); } return Task.FromResult(ContactToReturn); } public Task> GetContactsDisponiblesAsync(Guid interventionGuid, CancellationToken ct = default) => Task.FromResult>([]); } private sealed class FakePhoneLauncher : IPhoneLauncher { public int DialCalls { get; private set; } public string? LastNumber { get; private set; } public bool DialResult { get; set; } = true; public Task DialAsync(string phoneNumber) { DialCalls++; LastNumber = phoneNumber; return Task.FromResult(DialResult); } } private sealed class FakeEmailLauncher : IEmailLauncher { public int ComposeCalls { get; private set; } public string? LastEmail { get; private set; } public bool ComposeResult { get; set; } = true; public Task ComposeAsync(string email) { ComposeCalls++; LastEmail = email; return Task.FromResult(ComposeResult); } } private static InterventionContact CreateContact( Guid id, string? mobile = null, string? phone = null, string? email = null) => new() { ContactGuid = id, DisplayName = "Alice Dupont", Mobile = mobile, Phone = phone, Email = email }; private readonly FakeContactsService _service = new(); private readonly FakePhoneLauncher _phoneLauncher = new(); private readonly FakeEmailLauncher _emailLauncher = new(); private readonly NavigationService _navigation = new(); private readonly ContactDetailViewModel _viewModel; public ContactDetailViewModelTests() { _viewModel = new ContactDetailViewModel(_service, _phoneLauncher, _emailLauncher, _navigation); } private static DetailNavigation Nav(Guid interventionId, Guid contactId) => new(contactId, new NavigationContext(AppPage.InterventionContacts, interventionId)); [Fact] public async Task Load_ChargeLeContact() { // Arrange Guid interventionId = Guid.NewGuid(); Guid contactId = Guid.NewGuid(); _service.ContactToReturn = CreateContact(contactId, mobile: "0600000000", email: "alice@test.fr"); // Act await _viewModel.LoadCommand.ExecuteAsync(Nav(interventionId, contactId)); // Assert Assert.False(_viewModel.IsLoading); Assert.Null(_viewModel.ErrorMessage); Assert.NotNull(_viewModel.Contact); Assert.Equal(contactId, _viewModel.ContactGuid); Assert.True(_viewModel.HasPhone); Assert.True(_viewModel.HasEmail); } [Fact] public async Task Load_ContactIntrouvable_MessageErreur() { // Arrange _service.ContactToReturn = null; // Act await _viewModel.LoadCommand.ExecuteAsync(Nav(Guid.NewGuid(), Guid.NewGuid())); // Assert Assert.False(_viewModel.IsLoading); Assert.NotNull(_viewModel.ErrorMessage); Assert.Null(_viewModel.Contact); } [Fact] public async Task Load_Erreur_MessageAffiché() { // Arrange _service.ThrowOnLoad = true; // Act await _viewModel.LoadCommand.ExecuteAsync(Nav(Guid.NewGuid(), Guid.NewGuid())); // Assert Assert.False(_viewModel.IsLoading); Assert.NotNull(_viewModel.ErrorMessage); } [Fact] public async Task Call_NumeroDisponible_AppelleLeLauncher() { // Arrange Guid contactId = Guid.NewGuid(); _service.ContactToReturn = CreateContact(contactId, mobile: "0600000000"); await _viewModel.LoadCommand.ExecuteAsync(Nav(Guid.NewGuid(), contactId)); // Act await _viewModel.CallCommand.ExecuteAsync(null); // Assert Assert.Equal(1, _phoneLauncher.DialCalls); Assert.Equal("0600000000", _phoneLauncher.LastNumber); } [Fact] public async Task Call_SansNumero_NeAppellePas() { // Arrange Guid contactId = Guid.NewGuid(); _service.ContactToReturn = CreateContact(contactId, email: "alice@test.fr"); await _viewModel.LoadCommand.ExecuteAsync(Nav(Guid.NewGuid(), contactId)); // Act await _viewModel.CallCommand.ExecuteAsync(null); // Assert Assert.Equal(0, _phoneLauncher.DialCalls); Assert.False(_viewModel.HasPhone); } [Fact] public async Task Call_ContactNull_NeAppellePas() { // Act await _viewModel.CallCommand.ExecuteAsync(null); // Assert Assert.Equal(0, _phoneLauncher.DialCalls); } [Fact] public async Task Call_LauncherRetourneFalse_MessageErreur() { // Arrange Guid contactId = Guid.NewGuid(); _service.ContactToReturn = CreateContact(contactId, mobile: "0600000000"); await _viewModel.LoadCommand.ExecuteAsync(Nav(Guid.NewGuid(), contactId)); _phoneLauncher.DialResult = false; // Act await _viewModel.CallCommand.ExecuteAsync(null); // Assert : le résultat false est remonté à l'utilisateur. Assert.Equal(1, _phoneLauncher.DialCalls); Assert.Equal("Aucune application téléphonique disponible.", _viewModel.ErrorMessage); } [Fact] public async Task Email_Disponible_AppelleLeLauncher() { // Arrange Guid contactId = Guid.NewGuid(); _service.ContactToReturn = CreateContact(contactId, email: "alice@test.fr"); await _viewModel.LoadCommand.ExecuteAsync(Nav(Guid.NewGuid(), contactId)); // Act await _viewModel.EmailCommand.ExecuteAsync(null); // Assert Assert.Equal(1, _emailLauncher.ComposeCalls); Assert.Equal("alice@test.fr", _emailLauncher.LastEmail); } [Fact] public async Task Email_SansAdresse_NeAppellePas() { // Arrange Guid contactId = Guid.NewGuid(); _service.ContactToReturn = CreateContact(contactId, mobile: "0600000000"); await _viewModel.LoadCommand.ExecuteAsync(Nav(Guid.NewGuid(), contactId)); // Act await _viewModel.EmailCommand.ExecuteAsync(null); // Assert Assert.Equal(0, _emailLauncher.ComposeCalls); Assert.False(_viewModel.HasEmail); } [Fact] public async Task Email_ContactNull_NeAppellePas() { // Act await _viewModel.EmailCommand.ExecuteAsync(null); // Assert Assert.Equal(0, _emailLauncher.ComposeCalls); } [Fact] public async Task Email_LauncherRetourneFalse_MessageErreur() { // Arrange Guid contactId = Guid.NewGuid(); _service.ContactToReturn = CreateContact(contactId, email: "alice@test.fr"); await _viewModel.LoadCommand.ExecuteAsync(Nav(Guid.NewGuid(), contactId)); _emailLauncher.ComposeResult = false; // Act await _viewModel.EmailCommand.ExecuteAsync(null); // Assert : le résultat false est remonté à l'utilisateur. Assert.Equal(1, _emailLauncher.ComposeCalls); Assert.Equal("Aucune application de messagerie disponible.", _viewModel.ErrorMessage); } [Fact] public async Task Email_LauncherRetourneTrue_AucunMessage() { // Arrange Guid contactId = Guid.NewGuid(); _service.ContactToReturn = CreateContact(contactId, email: "alice@test.fr"); await _viewModel.LoadCommand.ExecuteAsync(Nav(Guid.NewGuid(), contactId)); // Act await _viewModel.EmailCommand.ExecuteAsync(null); // Assert : succès → aucun message d'erreur. Assert.Equal(1, _emailLauncher.ComposeCalls); Assert.Null(_viewModel.ErrorMessage); } [Fact] public async Task Back_RetourneALaListeDesContacts() { // Arrange Guid interventionId = Guid.NewGuid(); Guid contactId = Guid.NewGuid(); _service.ContactToReturn = CreateContact(contactId); // Construit l'historique de navigation : InterventionContacts -> ContactDetail. _navigation.NavigateTo(AppPage.InterventionContacts, interventionId); _navigation.NavigateTo(AppPage.ContactDetail, Nav(interventionId, contactId)); await _viewModel.LoadCommand.ExecuteAsync(Nav(interventionId, contactId)); // Act _viewModel.BackCommand.Execute(null); // Assert : retour vers la page précédente (liste des contacts de l'intervention). Assert.Equal(AppPage.InterventionContacts, _navigation.CurrentPage); Assert.Equal(interventionId, _navigation.CurrentParameter); } }