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);
}
}