Premiere commit - Omika
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Headless;
|
||||
using Avalonia.VisualTree;
|
||||
using Omika.App.Controls;
|
||||
using Omika.App.Local.Abstractions;
|
||||
using Xunit;
|
||||
|
||||
namespace Omika.Tests.Mobile;
|
||||
|
||||
/// <summary>
|
||||
/// Bouton « Envoyer un email » : un email exige le réseau, donc le bouton devient
|
||||
/// INACTIF hors ligne — dans TOUS les écrans, puisque la règle est portée par le
|
||||
/// contrôle réutilisable et non répétée sur chaque vue.
|
||||
///
|
||||
/// <para>
|
||||
/// Le bouton reste VISIBLE et à sa place : l'action existe, elle est seulement
|
||||
/// momentanément impossible, et l'info-bulle le dit. Une détection de connectivité
|
||||
/// en échec ne bloque jamais l'action (repli « considéré en ligne »).
|
||||
/// </para>
|
||||
/// </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 EmailButtonTests : IDisposable
|
||||
{
|
||||
private readonly HeadlessUnitTestSession _session =
|
||||
HeadlessUnitTestSession.StartNew(typeof(LayoutTestAppBuilder));
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Le point d'injection est statique : on le remet à zéro entre les classes.
|
||||
EmailButton.Connectivite = null;
|
||||
_session.Dispose();
|
||||
}
|
||||
|
||||
private sealed class FauxReseau : INetworkConnectivity
|
||||
{
|
||||
public bool Online { get; set; } = true;
|
||||
|
||||
public Task<bool> IsOnlineAsync(CancellationToken cancellationToken = default)
|
||||
=> Task.FromResult(Online);
|
||||
}
|
||||
|
||||
private sealed class FauxReseauEnPanne : INetworkConnectivity
|
||||
{
|
||||
public Task<bool> IsOnlineAsync(CancellationToken cancellationToken = default)
|
||||
=> throw new InvalidOperationException("capteur réseau indisponible");
|
||||
}
|
||||
|
||||
/// <summary>Bouton réellement rendu à l'intérieur du contrôle.</summary>
|
||||
private static Button BoutonInterne(EmailButton email)
|
||||
=> email.GetVisualDescendants().OfType<Button>().Single();
|
||||
|
||||
private void Executer(Action scenario)
|
||||
=> _session.Dispatch(
|
||||
() =>
|
||||
{
|
||||
scenario();
|
||||
return true;
|
||||
},
|
||||
CancellationToken.None).GetAwaiter().GetResult();
|
||||
|
||||
[Fact]
|
||||
public void EnLigne_BoutonActif()
|
||||
{
|
||||
Executer(() =>
|
||||
{
|
||||
var reseau = new FauxReseau { Online = true };
|
||||
EmailButton.Connectivite = reseau;
|
||||
|
||||
var email = new EmailButton();
|
||||
Window fenetre = MesureGeometrique.Ouvrir(email, 400);
|
||||
|
||||
Button bouton = BoutonInterne(email);
|
||||
Assert.True(bouton.IsEnabled);
|
||||
Assert.Equal(EmailButton.InfoBulleDisponible, email.InfoBulle);
|
||||
|
||||
fenetre.Close();
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HorsLigne_BoutonInactifVisibleEtExplique()
|
||||
{
|
||||
Executer(() =>
|
||||
{
|
||||
var reseau = new FauxReseau { Online = false };
|
||||
EmailButton.Connectivite = reseau;
|
||||
|
||||
var email = new EmailButton();
|
||||
Window fenetre = MesureGeometrique.Ouvrir(email, 400);
|
||||
|
||||
Button bouton = BoutonInterne(email);
|
||||
|
||||
// Inactif : aucun clic ne peut partir.
|
||||
Assert.False(bouton.IsEnabled);
|
||||
Assert.False(bouton.IsEffectivelyEnabled);
|
||||
|
||||
// Mais toujours VISIBLE et à sa place : l'action ne disparaît pas.
|
||||
Assert.True(email.IsVisible);
|
||||
Assert.True(bouton.IsVisible);
|
||||
Assert.True(bouton.Bounds.Width > 0);
|
||||
|
||||
// L'info-bulle explique la raison au lieu d'un bouton muet.
|
||||
Assert.Equal(EmailButton.InfoBulleHorsLigne, email.InfoBulle);
|
||||
Assert.Contains("Hors ligne", email.InfoBulle);
|
||||
|
||||
fenetre.Close();
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RetourDuReseau_BoutonReactiveSansChangerDEcran()
|
||||
{
|
||||
Executer(() =>
|
||||
{
|
||||
var reseau = new FauxReseau { Online = false };
|
||||
EmailButton.Connectivite = reseau;
|
||||
|
||||
var email = new EmailButton();
|
||||
Window fenetre = MesureGeometrique.Ouvrir(email, 400);
|
||||
Assert.False(BoutonInterne(email).IsEnabled);
|
||||
|
||||
// Le réseau revient : la veille (ou une synchronisation manuelle)
|
||||
// réévalue l'état, le bouton redevient actif.
|
||||
reseau.Online = true;
|
||||
EmailButton.RafraichirTousAsync().GetAwaiter().GetResult();
|
||||
|
||||
Assert.True(BoutonInterne(email).IsEnabled);
|
||||
Assert.Equal(EmailButton.InfoBulleDisponible, email.InfoBulle);
|
||||
|
||||
fenetre.Close();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Une détection de connectivité en ÉCHEC ne doit jamais bloquer l'envoi : le
|
||||
/// bouton reste actif. Bloquer sur un capteur muet serait une régression pire
|
||||
/// que le problème d'origine.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DetectionEnEchec_NeBloquePasLeBouton()
|
||||
{
|
||||
Executer(() =>
|
||||
{
|
||||
EmailButton.Connectivite = new FauxReseauEnPanne();
|
||||
|
||||
var email = new EmailButton();
|
||||
Window fenetre = MesureGeometrique.Ouvrir(email, 400);
|
||||
|
||||
Assert.True(BoutonInterne(email).IsEnabled);
|
||||
Assert.Equal(EmailButton.InfoBulleDisponible, email.InfoBulle);
|
||||
|
||||
fenetre.Close();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aucune source de connectivité installée (aperçu de design, tests de mise en
|
||||
/// page) : le bouton reste actif, comportement d'origine préservé.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void SansSourceDeConnectivite_ResteActif()
|
||||
{
|
||||
Executer(() =>
|
||||
{
|
||||
EmailButton.Connectivite = null;
|
||||
|
||||
var email = new EmailButton();
|
||||
Window fenetre = MesureGeometrique.Ouvrir(email, 400);
|
||||
|
||||
Assert.True(BoutonInterne(email).IsEnabled);
|
||||
|
||||
fenetre.Close();
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user