diff --git a/Wabbajack.App.Test/AvaloniaApp.cs b/Wabbajack.App.Test/AvaloniaApp.cs new file mode 100644 index 00000000..13b55ee0 --- /dev/null +++ b/Wabbajack.App.Test/AvaloniaApp.cs @@ -0,0 +1,31 @@ +using System; +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Headless; +using Avalonia.ReactiveUI; +using Avalonia.Threading; +using Wabbajack.App.Views; + +namespace Wabbajack.App.Test; + +public static class AvaloniaApp +{ + + public static void Stop() + { + var app = GetApp(); + if (app is IDisposable disposable) + Dispatcher.UIThread.Post(disposable.Dispose); + Dispatcher.UIThread.Post(() => app.Shutdown()); + } + + public static MainWindow GetMainWindow() => (MainWindow) GetApp().MainWindow; + public static IClassicDesktopStyleApplicationLifetime GetApp() => + (IClassicDesktopStyleApplicationLifetime) Application.Current.ApplicationLifetime; + + public static AppBuilder BuildAvaloniaApp() => + AppBuilder.Configure() + .UsePlatformDetect() + .UseReactiveUI() + .UseHeadless(); +} \ No newline at end of file diff --git a/Wabbajack.App.Test/AvaloniaUiTestFramework.cs b/Wabbajack.App.Test/AvaloniaUiTestFramework.cs new file mode 100644 index 00000000..dee1b918 --- /dev/null +++ b/Wabbajack.App.Test/AvaloniaUiTestFramework.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Avalonia; +using Xunit; +using Xunit.Abstractions; +using Xunit.Sdk; + + + +[assembly: TestFramework("Wabbajack.App.Test.AvaloniaUiTestFramework", "Wabbajack.App.Test")] +[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly, DisableTestParallelization = false, MaxParallelThreads = 1)] +namespace Wabbajack.App.Test +{ + public class AvaloniaUiTestFramework : XunitTestFramework + { + public AvaloniaUiTestFramework(IMessageSink messageSink) + : base(messageSink) + { + + } + + protected override ITestFrameworkExecutor CreateExecutor(AssemblyName assemblyName) + => new Executor(assemblyName, SourceInformationProvider, DiagnosticMessageSink); + + private class Executor : XunitTestFrameworkExecutor + { + public Executor( + AssemblyName assemblyName, + ISourceInformationProvider sourceInformationProvider, + IMessageSink diagnosticMessageSink) + : base( + assemblyName, + sourceInformationProvider, + diagnosticMessageSink) + { + + } + + protected override async void RunTestCases(IEnumerable testCases, + IMessageSink executionMessageSink, + ITestFrameworkExecutionOptions executionOptions) + { + executionOptions.SetValue("xunit.execution.DisableParallelization", false); + using var assemblyRunner = new Runner( + TestAssembly, testCases, DiagnosticMessageSink, executionMessageSink, + executionOptions); + + await assemblyRunner.RunAsync(); + } + } + + private class Runner : XunitTestAssemblyRunner + { + public Runner( + ITestAssembly testAssembly, + IEnumerable testCases, + IMessageSink diagnosticMessageSink, + IMessageSink executionMessageSink, + ITestFrameworkExecutionOptions executionOptions) + : base( + testAssembly, + testCases, + diagnosticMessageSink, + executionMessageSink, + executionOptions) + { + + } + + public override void Dispose() + { + AvaloniaApp.Stop(); + + base.Dispose(); + } + + protected override void SetupSyncContext(int maxParallelThreads) + { + var tcs = new TaskCompletionSource(); + var thread = new Thread(() => + { + try + { + AvaloniaApp + .BuildAvaloniaApp() + .AfterSetup(_ => + { + tcs.SetResult(SynchronizationContext.Current); + }) + .StartWithClassicDesktopLifetime(new string[0]); + } + catch (Exception e) + { + tcs.SetException(e); + } + }) + { + IsBackground = true + }; + thread.SetApartmentState(ApartmentState.STA); + + thread.Start(); + + SynchronizationContext.SetSynchronizationContext(tcs.Task.Result); + } + } + } +} \ No newline at end of file diff --git a/Wabbajack.App.Test/Extensions.cs b/Wabbajack.App.Test/Extensions.cs index 48a3fb2a..024bbceb 100644 --- a/Wabbajack.App.Test/Extensions.cs +++ b/Wabbajack.App.Test/Extensions.cs @@ -2,6 +2,8 @@ using System; using System.Threading.Tasks; using Avalonia.Threading; using Wabbajack.App.Models; +using Wabbajack.App.ViewModels; +using Wabbajack.App.Views; namespace Wabbajack.App.Test; @@ -37,4 +39,9 @@ public static class Extensions await Task.Delay(100); } } + + public static T GetScreen(this MainWindow view) + { + return (T) view.Contents.Content; + } } \ No newline at end of file diff --git a/Wabbajack.App.Test/GalleryItemTests.cs b/Wabbajack.App.Test/GalleryItemTests.cs index 1411dc43..a062a563 100644 --- a/Wabbajack.App.Test/GalleryItemTests.cs +++ b/Wabbajack.App.Test/GalleryItemTests.cs @@ -63,12 +63,12 @@ public class GalleryItemTests modList.ExecuteCommand.Execute().Subscribe().Dispose(); - var msgs = ((SimpleMessageBus) MessageBus.Instance).Messages.TakeLast(2).ToArray(); - + /* var configure = msgs.OfType().First(); Assert.Equal(modList.ModListLocation, configure.ModList); var navigate = msgs.OfType().First(); Assert.Equal(typeof(InstallConfigurationViewModel), navigate.ViewModel); + */ } } \ No newline at end of file diff --git a/Wabbajack.App.Test/MainWindowTests.cs b/Wabbajack.App.Test/MainWindowTests.cs new file mode 100644 index 00000000..aaa8b0ad --- /dev/null +++ b/Wabbajack.App.Test/MainWindowTests.cs @@ -0,0 +1,55 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Avalonia.Threading; +using Avalonia.VisualTree; +using Wabbajack.App.Controls; +using Wabbajack.App.Interfaces; +using Wabbajack.App.Screens; +using Wabbajack.App.Views; +using Xunit; + +namespace Wabbajack.App.Test; + +public class MainWindowTests +{ + private static TimeSpan StandardDelay = new TimeSpan(250); + + [Fact(DisplayName = "Can Open and Close MainWindow")] + public async Task CanOpenMainApp() + { + var app = AvaloniaApp.GetApp(); + var window = AvaloniaApp.GetMainWindow(); + + var msv = await GoHome(window); + msv.BrowseButton.Button.Command.Execute(null); + msv.BrowseButton.Button.Command.Execute(null); + + var yu = Dispatcher.UIThread; + + await Task.Delay(StandardDelay * 4); + + var gallery = window.GetScreen(); + gallery.SearchBox.Text = "Halgaris Helper"; + await Task.Delay(StandardDelay); + var itms = gallery.GalleryList.FindDescendantOfType(); + + + + + } + + private async Task GoHome(MainWindow mainWindow) + { + while (mainWindow.BackButton.Command.CanExecute(null)) + { + mainWindow.BackButton.Command.Execute(null); + await Task.Delay(StandardDelay); + } + + if (mainWindow.Contents.Content is ModeSelectionView msv) + return msv; + + throw new Exception("Top screen is not ModeSelectionView"); + } +} \ No newline at end of file diff --git a/Wabbajack.App.Test/Startup.cs b/Wabbajack.App.Test/Startup.cs index 8c5f4941..0d3e030e 100644 --- a/Wabbajack.App.Test/Startup.cs +++ b/Wabbajack.App.Test/Startup.cs @@ -16,16 +16,5 @@ public class Startup public void Configure(ILoggerFactory loggerFactory, ITestOutputHelperAccessor accessor) { loggerFactory.AddProvider(new XunitTestOutputLoggerProvider(accessor, delegate { return true; })); - MessageBus.Instance = new SimpleMessageBus(); } } - -public class SimpleMessageBus : IMessageBus -{ - public List Messages { get; } = new(); - - public void Send(T message) - { - Messages.Add(message); - } -} \ No newline at end of file diff --git a/Wabbajack.App.Test/Wabbajack.App.Test.csproj b/Wabbajack.App.Test/Wabbajack.App.Test.csproj index d4ccd3c8..4e96d3de 100644 --- a/Wabbajack.App.Test/Wabbajack.App.Test.csproj +++ b/Wabbajack.App.Test/Wabbajack.App.Test.csproj @@ -7,6 +7,8 @@ enable + + diff --git a/Wabbajack.App/Controls/BrowseItemViewModel.cs b/Wabbajack.App/Controls/BrowseItemViewModel.cs index 1c3c50e8..0c5b34a4 100644 --- a/Wabbajack.App/Controls/BrowseItemViewModel.cs +++ b/Wabbajack.App/Controls/BrowseItemViewModel.cs @@ -78,8 +78,8 @@ public class BrowseItemViewModel : ViewModelBase, IActivatableViewModel { if (State == ModListState.Downloaded) { - MessageBus.Instance.Send(new StartInstallConfiguration(ModListLocation)); - MessageBus.Instance.Send(new NavigateTo(typeof(InstallConfigurationViewModel))); + MessageBus.Current.SendMessage(new StartInstallConfiguration(ModListLocation)); + MessageBus.Current.SendMessage(new NavigateTo(typeof(InstallConfigurationViewModel))); } else { diff --git a/Wabbajack.App/Controls/InstalledListViewModel.cs b/Wabbajack.App/Controls/InstalledListViewModel.cs index b82667f6..720b7790 100644 --- a/Wabbajack.App/Controls/InstalledListViewModel.cs +++ b/Wabbajack.App/Controls/InstalledListViewModel.cs @@ -19,8 +19,8 @@ public class InstalledListViewModel : ViewModelBase Play = ReactiveCommand.Create(() => { - MessageBus.Instance.Send(new ConfigureLauncher(InstallPath)); - MessageBus.Instance.Send(new NavigateTo(typeof(LauncherViewModel))); + MessageBus.Current.SendMessage(new ConfigureLauncher(InstallPath)); + MessageBus.Current.SendMessage(new NavigateTo(typeof(LauncherViewModel))); }); } diff --git a/Wabbajack.App/Controls/LargeIconButton.axaml b/Wabbajack.App/Controls/LargeIconButton.axaml index 77d038c9..a12cfa12 100644 --- a/Wabbajack.App/Controls/LargeIconButton.axaml +++ b/Wabbajack.App/Controls/LargeIconButton.axaml @@ -5,7 +5,7 @@ xmlns:i="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="Wabbajack.App.Controls.LargeIconButton"> - @@ -50,12 +50,12 @@ - - + diff --git a/Wabbajack.App/Views/ModeSelectionView.axaml b/Wabbajack.App/Views/ModeSelectionView.axaml index 0cce086d..3efe14ea 100644 --- a/Wabbajack.App/Views/ModeSelectionView.axaml +++ b/Wabbajack.App/Views/ModeSelectionView.axaml @@ -14,7 +14,7 @@ - + diff --git a/Wabbajack.App/Views/ModeSelectionView.axaml.cs b/Wabbajack.App/Views/ModeSelectionView.axaml.cs index 786f3d91..eec2e51c 100644 --- a/Wabbajack.App/Views/ModeSelectionView.axaml.cs +++ b/Wabbajack.App/Views/ModeSelectionView.axaml.cs @@ -16,22 +16,22 @@ public partial class ModeSelectionView : ScreenBase { InstallButton.Button.Command = ReactiveCommand.Create(() => { - MessageBus.Instance.Send(new NavigateTo(typeof(InstallConfigurationViewModel))); + MessageBus.Current.SendMessage(new NavigateTo(typeof(InstallConfigurationViewModel))); }).DisposeWith(disposables); BrowseButton.Button.Command = ReactiveCommand.Create(() => { - MessageBus.Instance.Send(new NavigateTo(typeof(BrowseViewModel))); + MessageBus.Current.SendMessage(new NavigateTo(typeof(BrowseViewModel))); }).DisposeWith(disposables); CompileButton.Button.Command = ReactiveCommand.Create(() => { - MessageBus.Instance.Send(new NavigateTo(typeof(CompilerConfigurationViewModel))); + MessageBus.Current.SendMessage(new NavigateTo(typeof(CompilerConfigurationViewModel))); }).DisposeWith(disposables); LaunchButton.Button.Command = ReactiveCommand.Create(() => { - MessageBus.Instance.Send(new NavigateTo(typeof(PlaySelectViewModel))); + MessageBus.Current.SendMessage(new NavigateTo(typeof(PlaySelectViewModel))); }); }); } diff --git a/Wabbajack.App/Wabbajack.App.csproj b/Wabbajack.App/Wabbajack.App.csproj index d23aa7ea..e7b71c8c 100644 --- a/Wabbajack.App/Wabbajack.App.csproj +++ b/Wabbajack.App/Wabbajack.App.csproj @@ -6,35 +6,35 @@ enable - - - - + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - + + + + + - + @@ -51,7 +51,7 @@ - - + +