Ionite
1 year ago
20 changed files with 929 additions and 43 deletions
@ -0,0 +1,21 @@
|
||||
using System.Linq.Expressions; |
||||
|
||||
namespace StabilityMatrix.Core.Helper; |
||||
|
||||
|
||||
/*/// <summary> |
||||
/// Context helper for setting properties to one value on entry and another on dispose. |
||||
/// </summary> |
||||
public class ContextManager<T, TProperty> : IDisposable |
||||
{ |
||||
private Accessor accessor; |
||||
|
||||
public ContextManager(Expression<Func<T, TProperty>> expression, T context, TProperty value) |
||||
{ |
||||
var accessorInfo = ((MemberExpression) expression.Body).Member; |
||||
accessor = Accessors.Find(accessorInfo) ?? throw new ArgumentException("Accessor not found", nameof(expression)); |
||||
|
||||
originalValue = (TProperty)propertyInfo.GetValue(context); |
||||
propertyInfo.SetValue(context, value); |
||||
} |
||||
}*/ |
@ -0,0 +1,97 @@
|
||||
using Avalonia.Controls; |
||||
using Avalonia.Controls.Primitives; |
||||
using Avalonia.VisualTree; |
||||
using FluentAvalonia.UI.Controls; |
||||
using FluentAvalonia.UI.Windowing; |
||||
using Microsoft.Extensions.DependencyInjection; |
||||
using StabilityMatrix.Avalonia; |
||||
using StabilityMatrix.Avalonia.Controls; |
||||
using StabilityMatrix.Avalonia.ViewModels; |
||||
using StabilityMatrix.Avalonia.Views; |
||||
using StabilityMatrix.Avalonia.Views.Dialogs; |
||||
|
||||
namespace StabilityMatrix.UITests; |
||||
|
||||
[UsesVerify] |
||||
public class MainWindowTests |
||||
{ |
||||
private static IServiceProvider Services => App.Services; |
||||
|
||||
private static VerifySettings Settings |
||||
{ |
||||
get |
||||
{ |
||||
var settings = new VerifySettings(); |
||||
settings.IgnoreMembers<MainWindowViewModel>( |
||||
vm => vm.Pages, |
||||
vm => vm.FooterPages, |
||||
vm => vm.CurrentPage |
||||
); |
||||
return settings; |
||||
} |
||||
} |
||||
|
||||
private static (AppWindow, MainWindowViewModel) GetMainWindow() |
||||
{ |
||||
var window = Services.GetRequiredService<MainWindow>(); |
||||
var viewModel = Services.GetRequiredService<MainWindowViewModel>(); |
||||
window.DataContext = viewModel; |
||||
|
||||
window.SetDefaultFonts(); |
||||
|
||||
App.VisualRoot = window; |
||||
App.StorageProvider = window.StorageProvider; |
||||
App.Clipboard = window.Clipboard ?? throw new NullReferenceException("Clipboard is null"); |
||||
|
||||
return (window, viewModel); |
||||
} |
||||
|
||||
private static BetterContentDialog? GetWindowDialog(Visual window) |
||||
{ |
||||
return window |
||||
.FindDescendantOfType<VisualLayerManager>() |
||||
?.FindDescendantOfType<OverlayLayer>() |
||||
?.FindDescendantOfType<DialogHost>() |
||||
?.FindDescendantOfType<LayoutTransformControl>() |
||||
?.FindDescendantOfType<VisualLayerManager>() |
||||
?.FindDescendantOfType<BetterContentDialog>(); |
||||
} |
||||
|
||||
[AvaloniaFact] |
||||
public Task MainWindowViewModel_ShouldOk() |
||||
{ |
||||
var viewModel = Services.GetRequiredService<MainWindowViewModel>(); |
||||
|
||||
return Verify(viewModel, Settings); |
||||
} |
||||
|
||||
[AvaloniaFact] |
||||
public async Task MainWindow_ShouldOpen() |
||||
{ |
||||
var (window, vm) = GetMainWindow(); |
||||
|
||||
window.Show(); |
||||
|
||||
// Find the select data directory dialog |
||||
var selectDataDirectoryDialog = await WaitHelper.WaitForNotNullAsync( |
||||
() => GetWindowDialog(window) |
||||
); |
||||
Assert.NotNull(selectDataDirectoryDialog); |
||||
|
||||
// Click continue button |
||||
var continueButton = selectDataDirectoryDialog |
||||
.GetVisualDescendants() |
||||
.OfType<Button>() |
||||
.First(b => b.Content as string == "Continue"); |
||||
continueButton.Command?.Execute(null); |
||||
|
||||
// Find the one click install dialog |
||||
var oneClickDialog = await WaitHelper.WaitForConditionAsync( |
||||
() => GetWindowDialog(window), |
||||
d => d?.Content is OneClickInstallDialog |
||||
); |
||||
Assert.NotNull(oneClickDialog); |
||||
|
||||
await Verify(window, Settings); |
||||
} |
||||
} |
@ -0,0 +1,25 @@
|
||||
using System.Runtime.CompilerServices; |
||||
|
||||
namespace StabilityMatrix.UITests; |
||||
|
||||
public static class ModuleInit |
||||
{ |
||||
[ModuleInitializer] |
||||
public static void Init() => VerifyAvalonia.Initialize(); |
||||
|
||||
[ModuleInitializer] |
||||
public static void InitOther() => VerifierSettings.InitializePlugins(); |
||||
|
||||
[ModuleInitializer] |
||||
public static void ConfigureVerify() |
||||
{ |
||||
DerivePathInfo( |
||||
(sourceFile, projectDirectory, type, method) => |
||||
new PathInfo( |
||||
directory: Path.Combine(projectDirectory, "Snapshots"), |
||||
typeName: type.Name, |
||||
methodName: method.Name |
||||
) |
||||
); |
||||
} |
||||
} |
@ -0,0 +1,39 @@
|
||||
{ |
||||
Greeting: Welcome to Avalonia!, |
||||
ProgressManagerViewModel: { |
||||
Title: Download Manager, |
||||
IconSource: { |
||||
Type: SymbolIconSource |
||||
}, |
||||
IsOpen: false, |
||||
CanNavigateNext: false, |
||||
CanNavigatePrevious: false, |
||||
HasErrors: false |
||||
}, |
||||
UpdateViewModel: { |
||||
IsUpdateAvailable: true, |
||||
UpdateInfo: { |
||||
Version: { |
||||
Major: 2, |
||||
Minor: 999, |
||||
Prerelease: , |
||||
IsPrerelease: false, |
||||
IsRelease: true, |
||||
Metadata: |
||||
}, |
||||
ReleaseDate: DateTimeOffset_1, |
||||
Channel: Stable, |
||||
Type: Normal, |
||||
DownloadUrl: https://example.org, |
||||
ChangelogUrl: https://example.org, |
||||
HashBlake3: 46e11a5216c55d4c9d3c54385f62f3e1022537ae191615237f05e06d6f8690d0, |
||||
Signature: IX5/CCXWJQG0oGkYWVnuF34gTqF/dJSrDrUd6fuNMYnncL39G3HSvkXrjvJvR18MA2rQNB5z13h3/qBSf9c7DA== |
||||
}, |
||||
ShowProgressBar: false, |
||||
CurrentVersionText: v1.0.0, |
||||
NewVersionText: v2.999.0, |
||||
InstallUpdateCommand: UpdateViewModel.InstallUpdate(), |
||||
HasErrors: false |
||||
}, |
||||
HasErrors: false |
||||
} |
After Width: | Height: | Size: 68 KiB |
@ -0,0 +1,444 @@
|
||||
{ |
||||
Type: MainWindow, |
||||
Title: Stability Matrix, |
||||
Icon: {}, |
||||
Content: { |
||||
Type: Grid, |
||||
Children: [ |
||||
{ |
||||
Type: Grid, |
||||
Background: Transparent, |
||||
Height: 32.0, |
||||
Name: TitleBarHost, |
||||
Children: [ |
||||
{ |
||||
Type: Image, |
||||
Source: { |
||||
Dpi: { |
||||
X: 96.0, |
||||
Y: 96.0, |
||||
Length: 135.7645019878171, |
||||
SquaredLength: 18432.0 |
||||
}, |
||||
Size: { |
||||
AspectRatio: 1.0, |
||||
Width: 256.0, |
||||
Height: 256.0 |
||||
}, |
||||
PixelSize: { |
||||
AspectRatio: 1.0, |
||||
Width: 256, |
||||
Height: 256 |
||||
}, |
||||
Format: { |
||||
BitsPerPixel: 32 |
||||
} |
||||
}, |
||||
IsHitTestVisible: false, |
||||
Width: 18.0, |
||||
Height: 18.0, |
||||
Margin: 12,4,12,4, |
||||
IsVisible: true, |
||||
Name: WindowIcon |
||||
}, |
||||
{ |
||||
Type: TextBlock, |
||||
FontSize: 12.0, |
||||
Text: Stability Matrix, |
||||
IsHitTestVisible: false, |
||||
VerticalAlignment: Center, |
||||
IsVisible: true |
||||
}, |
||||
{ |
||||
Type: Border, |
||||
Padding: 6 |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
Type: NavigationView, |
||||
Content: { |
||||
Type: Frame, |
||||
Content: { |
||||
Type: LaunchPageView, |
||||
Content: { |
||||
Type: Grid, |
||||
Children: [ |
||||
{ |
||||
Type: Grid, |
||||
Margin: 0,8,0,8, |
||||
Children: [ |
||||
{ |
||||
Type: Grid, |
||||
Margin: 16,8,0,0, |
||||
Children: [ |
||||
{ |
||||
Type: Grid, |
||||
Column: 0, |
||||
Row: 0, |
||||
Name: LaunchButtonGrid, |
||||
Children: [ |
||||
{ |
||||
Type: Button, |
||||
Command: LaunchPageViewModel.LaunchAsync(string command), |
||||
Content: Launch, |
||||
Width: 95.0, |
||||
HorizontalAlignment: Left, |
||||
VerticalAlignment: Stretch, |
||||
IsVisible: false |
||||
}, |
||||
{ |
||||
Type: SplitButton, |
||||
Command: LaunchPageViewModel.LaunchAsync(string command), |
||||
Flyout: { |
||||
Type: FAMenuFlyout |
||||
}, |
||||
Content: Launch, |
||||
Width: 104.0, |
||||
HorizontalAlignment: Left, |
||||
VerticalAlignment: Stretch, |
||||
IsVisible: false |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
Type: TeachingTip, |
||||
Name: TeachingTip1 |
||||
}, |
||||
{ |
||||
Type: Grid, |
||||
Column: 0, |
||||
Row: 0, |
||||
IsVisible: false, |
||||
Name: StopButtonGrid, |
||||
Children: [ |
||||
{ |
||||
Type: Button, |
||||
Command: {}, |
||||
Content: Stop, |
||||
Width: 95.0, |
||||
HorizontalAlignment: Left, |
||||
VerticalAlignment: Stretch, |
||||
IsVisible: false |
||||
}, |
||||
{ |
||||
Type: Button, |
||||
Command: {}, |
||||
Content: Stop, |
||||
Width: 104.0, |
||||
HorizontalAlignment: Left, |
||||
VerticalAlignment: Stretch, |
||||
IsVisible: false |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
Type: Button, |
||||
Command: LaunchPageViewModel.Config(), |
||||
Content: { |
||||
Type: SymbolIcon |
||||
}, |
||||
FontSize: 16.0, |
||||
Width: 48.0, |
||||
Margin: 8,0,0,0, |
||||
HorizontalAlignment: Left, |
||||
VerticalAlignment: Stretch |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
Type: ComboBox, |
||||
SelectedIndex: -1, |
||||
Selection: { |
||||
SingleSelect: true, |
||||
SelectedIndex: -1, |
||||
AnchorIndex: -1 |
||||
}, |
||||
SelectionMode: Single, |
||||
ItemTemplate: { |
||||
DataType: InstalledPackage, |
||||
Content: { |
||||
Type: Func<IServiceProvider, object>, |
||||
Target: XamlIlRuntimeHelpers.<>c__DisplayClass1_0<Control>, |
||||
Method: System.Object DeferredTransformationFactoryV2(System.IServiceProvider) |
||||
} |
||||
}, |
||||
IsEnabled: true, |
||||
Margin: 8,8,0,0, |
||||
HorizontalAlignment: Stretch, |
||||
VerticalAlignment: Top, |
||||
Name: SelectPackageComboBox |
||||
}, |
||||
{ |
||||
Type: ToggleButton, |
||||
IsChecked: true, |
||||
Content: { |
||||
Type: Icon, |
||||
Template: { |
||||
Content: { |
||||
Type: Func<IServiceProvider, object>, |
||||
Target: XamlIlRuntimeHelpers.<>c__DisplayClass1_0<Control>, |
||||
Method: System.Object DeferredTransformationFactoryV2(System.IServiceProvider) |
||||
} |
||||
}, |
||||
RenderTransform: { |
||||
Type: TransformGroup, |
||||
Children: [ |
||||
{ |
||||
Type: RotateTransform |
||||
} |
||||
] |
||||
} |
||||
}, |
||||
FontSize: 16.0, |
||||
Width: 48.0, |
||||
Margin: 8,8,0,0, |
||||
HorizontalAlignment: Left, |
||||
VerticalAlignment: Stretch |
||||
}, |
||||
{ |
||||
Type: ToggleButton, |
||||
IsChecked: false, |
||||
Content: { |
||||
Type: SymbolIcon |
||||
}, |
||||
FontSize: 16.0, |
||||
Width: 48.0, |
||||
Margin: 8,8,16,0, |
||||
HorizontalAlignment: Left, |
||||
VerticalAlignment: Stretch |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
Type: TextEditor, |
||||
FontFamily: Cascadia Code, |
||||
Margin: 8,8,16,10, |
||||
DataContext: { |
||||
IsUpdatesRunning: false, |
||||
WriteCursorLockTimeout: 00:00:00.1000000, |
||||
Document: { |
||||
_undoStack: { |
||||
IsOriginalFile: true, |
||||
AcceptChanges: true, |
||||
CanUndo: false, |
||||
CanRedo: false, |
||||
SizeLimit: 2147483647 |
||||
}, |
||||
Text: , |
||||
Version: {}, |
||||
IsInUpdate: false, |
||||
Lines: [ |
||||
{ |
||||
IsDeleted: false, |
||||
LineNumber: 1 |
||||
} |
||||
], |
||||
LineTrackers: [ |
||||
{} |
||||
], |
||||
UndoStack: { |
||||
IsOriginalFile: true, |
||||
AcceptChanges: true, |
||||
CanUndo: false, |
||||
CanRedo: false, |
||||
SizeLimit: 2147483647 |
||||
}, |
||||
LineCount: 1 |
||||
} |
||||
}, |
||||
Name: Console |
||||
}, |
||||
{ |
||||
Type: Grid, |
||||
Row: 1, |
||||
Children: [ |
||||
{ |
||||
Type: StackPanel, |
||||
Spacing: 4.0, |
||||
Margin: 8, |
||||
Children: [ |
||||
{ |
||||
Type: InfoBar, |
||||
Margin: 0 |
||||
}, |
||||
{ |
||||
Type: InfoBar, |
||||
Margin: 0 |
||||
} |
||||
] |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
Type: Button, |
||||
Command: {}, |
||||
Content: Open Web UI, |
||||
FontSize: 12.0, |
||||
Margin: 24,0,24,8, |
||||
HorizontalAlignment: Stretch, |
||||
IsVisible: false |
||||
} |
||||
] |
||||
}, |
||||
DataContext: { |
||||
Title: Launch, |
||||
IconSource: { |
||||
Type: SymbolIconSource |
||||
}, |
||||
Console: { |
||||
IsUpdatesRunning: false, |
||||
WriteCursorLockTimeout: 00:00:00.1000000, |
||||
Document: { |
||||
_undoStack: { |
||||
IsOriginalFile: true, |
||||
AcceptChanges: true, |
||||
CanUndo: false, |
||||
CanRedo: false, |
||||
SizeLimit: 2147483647 |
||||
}, |
||||
Text: , |
||||
Version: {}, |
||||
IsInUpdate: false, |
||||
Lines: [ |
||||
{ |
||||
IsDeleted: false, |
||||
LineNumber: 1 |
||||
} |
||||
], |
||||
LineTrackers: [ |
||||
{} |
||||
], |
||||
UndoStack: { |
||||
IsOriginalFile: true, |
||||
AcceptChanges: true, |
||||
CanUndo: false, |
||||
CanRedo: false, |
||||
SizeLimit: 2147483647 |
||||
}, |
||||
LineCount: 1 |
||||
} |
||||
}, |
||||
LaunchButtonVisibility: false, |
||||
StopButtonVisibility: false, |
||||
IsLaunchTeachingTipsOpen: false, |
||||
ShowWebUiButton: false, |
||||
AutoScrollToEnd: true, |
||||
ShowManualInputPrompt: false, |
||||
ShowConfirmInputPrompt: false, |
||||
LaunchCommand: LaunchPageViewModel.LaunchAsync(string command), |
||||
ConfigCommand: LaunchPageViewModel.Config(), |
||||
SendConfirmInputCommand: LaunchPageViewModel.SendConfirmInput(bool value), |
||||
SendManualInputCommand: LaunchPageViewModel.SendManualInput(string input), |
||||
CanNavigateNext: false, |
||||
CanNavigatePrevious: false, |
||||
HasErrors: false |
||||
} |
||||
}, |
||||
Name: FrameView |
||||
}, |
||||
Name: NavigationView |
||||
}, |
||||
{ |
||||
Type: TeachingTip, |
||||
Name: UpdateAvailableTeachingTip |
||||
} |
||||
] |
||||
}, |
||||
FontFamily: Segoe UI Variable Text, |
||||
Width: 1100.0, |
||||
Height: 750.0, |
||||
IsVisible: true, |
||||
DataContext: { |
||||
Greeting: Welcome to Avalonia!, |
||||
ProgressManagerViewModel: { |
||||
Title: Download Manager, |
||||
IconSource: { |
||||
Type: SymbolIconSource |
||||
}, |
||||
IsOpen: false, |
||||
CanNavigateNext: false, |
||||
CanNavigatePrevious: false, |
||||
HasErrors: false |
||||
}, |
||||
UpdateViewModel: { |
||||
IsUpdateAvailable: true, |
||||
UpdateInfo: { |
||||
Version: { |
||||
Major: 2, |
||||
Minor: 999, |
||||
Prerelease: , |
||||
IsPrerelease: false, |
||||
IsRelease: true, |
||||
Metadata: |
||||
}, |
||||
ReleaseDate: DateTimeOffset_1, |
||||
Channel: Stable, |
||||
Type: Normal, |
||||
DownloadUrl: https://example.org, |
||||
ChangelogUrl: https://example.org, |
||||
HashBlake3: 46e11a5216c55d4c9d3c54385f62f3e1022537ae191615237f05e06d6f8690d0, |
||||
Signature: IX5/CCXWJQG0oGkYWVnuF34gTqF/dJSrDrUd6fuNMYnncL39G3HSvkXrjvJvR18MA2rQNB5z13h3/qBSf9c7DA== |
||||
}, |
||||
ShowProgressBar: false, |
||||
CurrentVersionText: v1.0.0, |
||||
NewVersionText: v2.999.0, |
||||
InstallUpdateCommand: UpdateViewModel.InstallUpdate(), |
||||
HasErrors: false |
||||
}, |
||||
SelectedCategory: { |
||||
Title: Launch, |
||||
IconSource: { |
||||
Type: SymbolIconSource |
||||
}, |
||||
Console: { |
||||
IsUpdatesRunning: false, |
||||
WriteCursorLockTimeout: 00:00:00.1000000, |
||||
Document: { |
||||
_undoStack: { |
||||
IsOriginalFile: true, |
||||
AcceptChanges: true, |
||||
CanUndo: false, |
||||
CanRedo: false, |
||||
SizeLimit: 2147483647 |
||||
}, |
||||
Text: , |
||||
Version: {}, |
||||
IsInUpdate: false, |
||||
Lines: [ |
||||
{ |
||||
IsDeleted: false, |
||||
LineNumber: 1 |
||||
} |
||||
], |
||||
LineTrackers: [ |
||||
{} |
||||
], |
||||
UndoStack: { |
||||
IsOriginalFile: true, |
||||
AcceptChanges: true, |
||||
CanUndo: false, |
||||
CanRedo: false, |
||||
SizeLimit: 2147483647 |
||||
}, |
||||
LineCount: 1 |
||||
} |
||||
}, |
||||
LaunchButtonVisibility: false, |
||||
StopButtonVisibility: false, |
||||
IsLaunchTeachingTipsOpen: false, |
||||
ShowWebUiButton: false, |
||||
AutoScrollToEnd: true, |
||||
ShowManualInputPrompt: false, |
||||
ShowConfirmInputPrompt: false, |
||||
LaunchCommand: LaunchPageViewModel.LaunchAsync(string command), |
||||
ConfigCommand: LaunchPageViewModel.Config(), |
||||
SendConfirmInputCommand: LaunchPageViewModel.SendConfirmInput(bool value), |
||||
SendManualInputCommand: LaunchPageViewModel.SendManualInput(string input), |
||||
CanNavigateNext: false, |
||||
CanNavigatePrevious: false, |
||||
HasErrors: false |
||||
}, |
||||
HasErrors: false |
||||
} |
||||
} |
@ -0,0 +1 @@
|
||||
|
@ -0,0 +1,37 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
||||
<PropertyGroup> |
||||
<TargetFramework>net7.0</TargetFramework> |
||||
<ImplicitUsings>enable</ImplicitUsings> |
||||
<Nullable>enable</Nullable> |
||||
|
||||
<IsPackable>false</IsPackable> |
||||
<IsTestProject>true</IsTestProject> |
||||
</PropertyGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="Avalonia.Headless.XUnit" Version="11.0.5" /> |
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0"/> |
||||
<PackageReference Include="NSubstitute" Version="5.1.0" /> |
||||
<PackageReference Include="Verify" Version="22.1.4" /> |
||||
<PackageReference Include="Verify.CommunityToolkit.Mvvm" Version="0.1.0" /> |
||||
<PackageReference Include="Verify.Avalonia" Version="1.0.1" /> |
||||
<PackageReference Include="Verify.CommunityToolkit.Mvvm" Version="0.1.0" /> |
||||
<PackageReference Include="Verify.Xunit" Version="22.1.4" /> |
||||
<PackageReference Include="xunit" Version="2.6.1"/> |
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"> |
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> |
||||
<PrivateAssets>all</PrivateAssets> |
||||
</PackageReference> |
||||
<PackageReference Include="coverlet.collector" Version="3.2.0"> |
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> |
||||
<PrivateAssets>all</PrivateAssets> |
||||
</PackageReference> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<ProjectReference Include="..\StabilityMatrix.Avalonia\StabilityMatrix.Avalonia.csproj" /> |
||||
<ProjectReference Include="..\StabilityMatrix.Core\StabilityMatrix.Core.csproj" /> |
||||
</ItemGroup> |
||||
|
||||
</Project> |
@ -0,0 +1,35 @@
|
||||
using System.Runtime.CompilerServices; |
||||
|
||||
namespace StabilityMatrix.UITests; |
||||
|
||||
public class TempDirFixture : IDisposable |
||||
{ |
||||
public static string ModuleTempDir { get; set; } |
||||
|
||||
static TempDirFixture() |
||||
{ |
||||
var tempDir = Path.Combine(Path.GetTempPath(), "StabilityMatrixTest"); |
||||
Directory.CreateDirectory(tempDir); |
||||
ModuleTempDir = tempDir; |
||||
|
||||
// ReSharper disable once LocalizableElement |
||||
Console.WriteLine($"Using temp dir: {ModuleTempDir}"); |
||||
} |
||||
|
||||
/// <inheritdoc /> |
||||
[MethodImpl(MethodImplOptions.Synchronized)] |
||||
public void Dispose() |
||||
{ |
||||
if (Directory.Exists(ModuleTempDir)) |
||||
{ |
||||
// ReSharper disable once LocalizableElement |
||||
Console.WriteLine($"Deleting temp dir: {ModuleTempDir}"); |
||||
Directory.Delete(ModuleTempDir, true); |
||||
} |
||||
|
||||
GC.SuppressFinalize(this); |
||||
} |
||||
} |
||||
|
||||
[CollectionDefinition("TempDir")] |
||||
public class TempDirCollection : ICollectionFixture<TempDirFixture> { } |
@ -0,0 +1,78 @@
|
||||
using Microsoft.Extensions.DependencyInjection; |
||||
using NSubstitute; |
||||
using NSubstitute.Extensions; |
||||
using Semver; |
||||
using StabilityMatrix.Avalonia; |
||||
using StabilityMatrix.Avalonia.ViewModels.Dialogs; |
||||
using StabilityMatrix.Core.Helper; |
||||
using StabilityMatrix.Core.Models.Update; |
||||
using StabilityMatrix.Core.Services; |
||||
using StabilityMatrix.Core.Updater; |
||||
using StabilityMatrix.UITests; |
||||
|
||||
[assembly: AvaloniaTestApplication(typeof(TestAppBuilder))] |
||||
|
||||
namespace StabilityMatrix.UITests; |
||||
|
||||
public static class TestAppBuilder |
||||
{ |
||||
public static AppBuilder BuildAvaloniaApp() |
||||
{ |
||||
ConfigureGlobals(); |
||||
|
||||
Program.SetupAvaloniaApp(); |
||||
|
||||
App.BeforeBuildServiceProvider += (_, x) => ConfigureAppServices(x); |
||||
|
||||
return AppBuilder |
||||
.Configure<App>() |
||||
.UseSkia() |
||||
.UseHeadless(new AvaloniaHeadlessPlatformOptions { UseHeadlessDrawing = false }); |
||||
} |
||||
|
||||
private static void ConfigureGlobals() |
||||
{ |
||||
var tempDir = TempDirFixture.ModuleTempDir; |
||||
var globalSettings = Path.Combine(tempDir, "AppDataHome"); |
||||
|
||||
Compat.SetAppDataHome(globalSettings); |
||||
} |
||||
|
||||
private static void ConfigureAppServices(IServiceCollection serviceCollection) |
||||
{ |
||||
// ISettingsManager |
||||
var settingsManager = Substitute.ForPartsOf<SettingsManager>(); |
||||
serviceCollection.AddSingleton<ISettingsManager>(settingsManager); |
||||
|
||||
// IUpdateHelper |
||||
var mockUpdateInfo = new UpdateInfo( |
||||
SemVersion.Parse("2.999.0"), |
||||
DateTimeOffset.UnixEpoch, |
||||
UpdateChannel.Stable, |
||||
UpdateType.Normal, |
||||
"https://example.org", |
||||
"https://example.org", |
||||
"46e11a5216c55d4c9d3c54385f62f3e1022537ae191615237f05e06d6f8690d0", |
||||
"IX5/CCXWJQG0oGkYWVnuF34gTqF/dJSrDrUd6fuNMYnncL39G3HSvkXrjvJvR18MA2rQNB5z13h3/qBSf9c7DA==" |
||||
); |
||||
|
||||
var updateHelper = Substitute.For<IUpdateHelper>(); |
||||
updateHelper |
||||
.Configure() |
||||
.StartCheckingForUpdates() |
||||
.Returns(Task.CompletedTask) |
||||
.AndDoes(_ => EventManager.Instance.OnUpdateAvailable(mockUpdateInfo)); |
||||
|
||||
serviceCollection.AddSingleton(updateHelper); |
||||
|
||||
// UpdateViewModel |
||||
var updateViewModel = Substitute.ForPartsOf<UpdateViewModel>( |
||||
settingsManager, |
||||
null, |
||||
updateHelper |
||||
); |
||||
updateViewModel.Configure().GetReleaseNotes("").Returns("Test"); |
||||
|
||||
serviceCollection.AddSingleton(updateViewModel); |
||||
} |
||||
} |
@ -0,0 +1,5 @@
|
||||
global using Xunit; |
||||
global using Avalonia; |
||||
global using Avalonia.Headless; |
||||
global using Avalonia.Headless.XUnit; |
||||
global using Avalonia.Input; |
@ -0,0 +1,14 @@
|
||||
using AsyncAwaitBestPractices; |
||||
|
||||
namespace StabilityMatrix.UITests; |
||||
|
||||
internal static class VerifyConfig |
||||
{ |
||||
public static VerifySettings Default { get; } |
||||
|
||||
static VerifyConfig() |
||||
{ |
||||
Default = new VerifySettings(); |
||||
Default.IgnoreMembersWithType<WeakEventManager>(); |
||||
} |
||||
} |
@ -0,0 +1,51 @@
|
||||
namespace StabilityMatrix.UITests; |
||||
|
||||
public static class WaitHelper |
||||
{ |
||||
public static async Task<T> WaitForConditionAsync<T>( |
||||
Func<T> getter, |
||||
Func<T, bool> condition, |
||||
int delayMs = 50, |
||||
int maxAttempts = 20, |
||||
int initialDelayMs = 100 |
||||
) |
||||
{ |
||||
await Task.Delay(initialDelayMs); |
||||
|
||||
for (var i = 0; i < maxAttempts; i++) |
||||
{ |
||||
await Task.Delay(delayMs); |
||||
|
||||
var result = getter(); |
||||
|
||||
if (condition(result)) |
||||
{ |
||||
return result; |
||||
} |
||||
} |
||||
|
||||
throw new TimeoutException("Waited too long for a condition to be met"); |
||||
} |
||||
|
||||
public static async Task<T> WaitForNotNullAsync<T>( |
||||
Func<T?> getter, |
||||
int delayMs = 50, |
||||
int maxAttempts = 20, |
||||
int initialDelayMs = 100 |
||||
) |
||||
{ |
||||
await Task.Delay(initialDelayMs); |
||||
|
||||
for (var i = 0; i < maxAttempts; i++) |
||||
{ |
||||
await Task.Delay(delayMs); |
||||
|
||||
if (getter() is { } result) |
||||
{ |
||||
return result; |
||||
} |
||||
} |
||||
|
||||
throw new TimeoutException("Waited too long for a non-null value"); |
||||
} |
||||
} |
Loading…
Reference in new issue