using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Headless; using Avalonia.Threading; using Avalonia.VisualTree; 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.Documents; using Omika.Core.Contracts.Mobile.Sync; using Xunit; namespace Omika.Tests.Mobile; /// /// Écran de composition d'un courriel en MESSAGERIE EXTERNE : le choix de /// configuration SMTP disparaît (l'envoi ne passe pas par Omika), une explication /// le remplace, et le panneau « Le courriel a-t-il été envoyé ? » (Oui / Non) /// apparaît après le retour de l'application de messagerie. /// /// /// Session headless partagée au niveau du processus (localisateur Avalonia global) : /// la classe rejoint la collection « ApplicationRuntimeContext ». /// [Collection("ApplicationRuntimeContext")] public sealed class NouveauEmailViewExterneTests : IDisposable { private readonly HeadlessUnitTestSession _session = HeadlessUnitTestSession.StartNew(typeof(LayoutTestAppBuilder)); public void Dispose() => _session.Dispose(); private void Executer(Action scenario) => _session.Dispatch( () => { scenario(); return true; }, CancellationToken.None).GetAwaiter().GetResult(); /// /// Messagerie externe : la configuration SMTP est MASQUÉE et l'explication /// affichée (l'utilisateur doit comprendre que l'envoi se fera ailleurs). /// [Fact] public void MessagerieExterne_MasqueLaConfigurationSmtpEtExplique() => Executer(() => { NouveauEmailView vue = Ouvrir(messagerieExterne: true); Window fenetre = Fenetre(vue); try { ComboBox? configuration = vue.FindControl("ConfigurationSmtp"); Assert.NotNull(configuration); Assert.False( configuration!.IsEffectivelyVisible, "La configuration SMTP ne doit pas être proposée en messagerie externe."); TextBlock explication = vue.GetVisualDescendants() .OfType() .Single(t => t.Text?.StartsWith("Messagerie externe", StringComparison.Ordinal) == true); Assert.True(explication.IsEffectivelyVisible, "L'explication de la messagerie externe n'est pas affichée."); } finally { fenetre.Close(); } }); /// Messagerie interne (Back-Office) : rien ne change, la configuration reste proposée. [Fact] public void MessagerieInterne_GardeLaConfigurationSmtp() => Executer(() => { NouveauEmailView vue = Ouvrir(messagerieExterne: false); Window fenetre = Fenetre(vue); try { ComboBox? configuration = vue.FindControl("ConfigurationSmtp"); Assert.NotNull(configuration); Assert.True(configuration!.IsEffectivelyVisible); TextBlock? explication = vue.GetVisualDescendants() .OfType() .FirstOrDefault(t => t.Text?.StartsWith("Messagerie externe", StringComparison.Ordinal) == true); Assert.True(explication is null || !explication.IsEffectivelyVisible); } finally { fenetre.Close(); } }); /// /// La question n'est PAS affichée avant l'envoi, puis apparaît avec ses deux /// réponses « Oui » (enregistre comme envoyé) et « Non » (conserve en brouillon). /// [Fact] public void QuestionEnvoiExterne_SafficheApresLeRetourAvecOuiEtNon() => Executer(() => { NouveauEmailView vue = Ouvrir(messagerieExterne: true); Window fenetre = Fenetre(vue); try { var modele = (NouveauEmailViewModel)vue.DataContext!; Button oui = Bouton(vue, "Oui"); Button non = Bouton(vue, "Non"); Assert.False( oui.IsEffectivelyVisible, "La question ne doit pas apparaître avant l'ouverture de la messagerie."); modele.AfficherQuestionEnvoiExterne = true; Dispatcher.UIThread.RunJobs(); Assert.True(oui.IsEffectivelyVisible, "Le bouton « Oui » n'est pas affiché."); Assert.True(non.IsEffectivelyVisible, "Le bouton « Non » n'est pas affiché."); Assert.NotNull(oui.Command); Assert.NotNull(non.Command); Assert.NotSame(oui.Command, non.Command); } finally { fenetre.Close(); } }); // ===================================================================== // Aides // ===================================================================== private static Button Bouton(Control vue, string libelle) => vue.GetVisualDescendants() .OfType