diff --git a/.github/workflows/build-test-publish.yml b/.github/workflows/build-test-publish.yml
index 9280fb2..e16c41d 100644
--- a/.github/workflows/build-test-publish.yml
+++ b/.github/workflows/build-test-publish.yml
@@ -31,6 +31,13 @@ jobs:
run: dotnet build ${{ env.SOLUTION_NAME }} --configuration Release /p:TreatWarningsAsErrors=true
- name: Test
run: dotnet test ${{ env.SOLUTION_NAME }} --no-build --configuration Release
+ - name: Upload Test Results
+ if: failure()
+ uses: actions/upload-artifact@v4
+ with:
+ name: verify-test-results
+ path: |
+ **/*.received.*
- name: Check formatting using csharpier
run: |
diff --git a/Directory.Packages.props b/Directory.Packages.props
index eeb7838..4ce324c 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -7,6 +7,7 @@
+
@@ -26,8 +27,10 @@
+
+
diff --git a/src/AvaloniaExampleProject/App.axaml.cs b/src/AvaloniaExampleProject/App.axaml.cs
index dbca0df..0fe4d0d 100644
--- a/src/AvaloniaExampleProject/App.axaml.cs
+++ b/src/AvaloniaExampleProject/App.axaml.cs
@@ -1,10 +1,9 @@
-using System.Data;
using System.Diagnostics.CodeAnalysis;
-using System.Reflection;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core.Plugins;
+using Avalonia.Input;
using Avalonia.Markup.Xaml;
using AvaloniaExampleProject.Views;
using Microsoft.Extensions.DependencyInjection;
@@ -30,10 +29,6 @@ public App(IServiceProvider provider)
[MemberNotNullWhen(true, nameof(IsDesignMode))]
public static IServiceProvider? ServiceProvider { get; private set; }
- public static string Version { get; } =
- typeof(App).Assembly.GetCustomAttribute()?.InformationalVersion
- ?? throw new VersionNotFoundException("Could not get version");
-
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
@@ -45,8 +40,10 @@ public override void OnFrameworkInitializationCompleted()
{
#if DEBUG
this.AttachDeveloperTools(options =>
- options.AddMicrosoftLoggerObservable(_provider.GetRequiredService())
- );
+ {
+ options.Gesture = new KeyGesture(Key.F11);
+ options.AddMicrosoftLoggerObservable(_provider.GetRequiredService());
+ });
#endif
DisableAvaloniaDataAnnotationValidation();
desktop.MainWindow = new MainWindow(_provider);
diff --git a/src/AvaloniaExampleProject/AvaloniaExampleProject.csproj b/src/AvaloniaExampleProject/AvaloniaExampleProject.csproj
index ec9703d..c7c80d7 100644
--- a/src/AvaloniaExampleProject/AvaloniaExampleProject.csproj
+++ b/src/AvaloniaExampleProject/AvaloniaExampleProject.csproj
@@ -15,6 +15,8 @@
+
+
None
All
@@ -30,4 +32,7 @@
+
+
+
diff --git a/src/AvaloniaExampleProject/Bootstrapper.cs b/src/AvaloniaExampleProject/Bootstrapper.cs
index d3b9c26..26557eb 100644
--- a/src/AvaloniaExampleProject/Bootstrapper.cs
+++ b/src/AvaloniaExampleProject/Bootstrapper.cs
@@ -20,6 +20,7 @@ public static IServiceCollection AddAppServices(this IServiceCollection serviceC
.AddConfigurationFile("config.json", JsonContext.Default.MainConfig)
.AddLocalization()
.AddSingleton()
+ .AddSingleton()
// Configure ViewModels
.AddTransient()
.AddTransient()
diff --git a/src/AvaloniaExampleProject/Business/AppInformationService.cs b/src/AvaloniaExampleProject/Business/AppInformationService.cs
new file mode 100644
index 0000000..b85b2ad
--- /dev/null
+++ b/src/AvaloniaExampleProject/Business/AppInformationService.cs
@@ -0,0 +1,25 @@
+using System.Data;
+using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
+
+namespace AvaloniaExampleProject.Business;
+
+public interface IAppInformationService
+{
+ string Version { get; }
+}
+
+public sealed class AppInformationService : IAppInformationService
+{
+ [field: AllowNull, MaybeNull]
+ public string Version
+ {
+ get
+ {
+ field ??=
+ typeof(App).Assembly.GetCustomAttribute()?.InformationalVersion
+ ?? throw new VersionNotFoundException("Could not get version");
+ return field!;
+ }
+ }
+}
diff --git a/src/AvaloniaExampleProject/Business/ThemeService.cs b/src/AvaloniaExampleProject/Business/ThemeService.cs
index a591201..149a7ac 100644
--- a/src/AvaloniaExampleProject/Business/ThemeService.cs
+++ b/src/AvaloniaExampleProject/Business/ThemeService.cs
@@ -1,5 +1,3 @@
-using System.ComponentModel;
-using System.Reactive.Disposables;
using Avalonia.Styling;
using AvaloniaExampleProject.Models;
using Darp.Utils.Configuration;
diff --git a/src/AvaloniaExampleProject/Models/DesignData.cs b/src/AvaloniaExampleProject/Models/DesignData.cs
index 53b2c40..a7143de 100644
--- a/src/AvaloniaExampleProject/Models/DesignData.cs
+++ b/src/AvaloniaExampleProject/Models/DesignData.cs
@@ -14,4 +14,5 @@ public static IServiceProvider ServiceProvider
public static WelcomeViewModel WelcomeViewModel { get; } = ServiceProvider.GetRequiredService();
public static SettingsViewModel SettingsViewModel { get; } =
ServiceProvider.GetRequiredService();
+ public static MainViewModel MainViewModel { get; } = ServiceProvider.GetRequiredService();
}
diff --git a/src/AvaloniaExampleProject/ViewModels/SettingsViewModel.cs b/src/AvaloniaExampleProject/ViewModels/SettingsViewModel.cs
index 771d96f..5e753bd 100644
--- a/src/AvaloniaExampleProject/ViewModels/SettingsViewModel.cs
+++ b/src/AvaloniaExampleProject/ViewModels/SettingsViewModel.cs
@@ -17,16 +17,19 @@ public sealed partial class SettingsViewModel(
Resources i18N,
IConfigurationService configurationService,
IDialogService dialogService,
+ IAppInformationService appInformationService,
ILogger logger
) : ViewModelBase
{
private readonly IConfigurationService _configurationService = configurationService;
private readonly IDialogService _dialogService = dialogService;
+ private readonly IAppInformationService _appInformationService = appInformationService;
private readonly ILogger _logger = logger;
public IThemeService ThemeService { get; } = themeService;
public Resources I18N { get; } = i18N;
- public IObservable AppVersion => I18N.Observe(x => x.FormatSettings_About_Version(App.Version));
+ public IObservable AppVersion =>
+ I18N.Observe(x => x.FormatSettings_About_Version(_appInformationService.Version));
[ObservableProperty]
public partial string SelectedLanguage { get; set; } = configurationService.Config.UserPreferences.SelectedLanguage;
diff --git a/src/AvaloniaExampleProject/Views/MainView.axaml b/src/AvaloniaExampleProject/Views/MainView.axaml
index 39fcba9..92e978a 100644
--- a/src/AvaloniaExampleProject/Views/MainView.axaml
+++ b/src/AvaloniaExampleProject/Views/MainView.axaml
@@ -4,19 +4,27 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:AvaloniaExampleProject.ViewModels"
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
+ xmlns:models="clr-namespace:AvaloniaExampleProject.Models"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="AvaloniaExampleProject.Views.MainView"
- x:DataType="vm:MainViewModel">
+ x:DataType="vm:MainViewModel"
+ Design.DataContext="{x:Static models:DesignData.MainViewModel}">
0,0,0,0
0,0,0,0
+
+ 8,8,0,0
+
+
+
@@ -26,10 +34,7 @@
Content="{CompiledBinding I18N.Settings_Title}" />
-
-
-
-
+
diff --git a/src/AvaloniaExampleProject/Views/SettingsView.axaml b/src/AvaloniaExampleProject/Views/SettingsView.axaml
index de6fe92..24dccf1 100644
--- a/src/AvaloniaExampleProject/Views/SettingsView.axaml
+++ b/src/AvaloniaExampleProject/Views/SettingsView.axaml
@@ -53,7 +53,8 @@
-
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
diff --git a/test/AvaloniaExampleProject.Tests/Snapshots/SettingsViewModelTests/SettingsViewModelTests.Render.verified.png b/test/AvaloniaExampleProject.Tests/Snapshots/SettingsViewModelTests/SettingsViewModelTests.Render.verified.png
new file mode 100644
index 0000000..7488c3d
Binary files /dev/null and b/test/AvaloniaExampleProject.Tests/Snapshots/SettingsViewModelTests/SettingsViewModelTests.Render.verified.png differ
diff --git a/test/AvaloniaExampleProject.Tests/Snapshots/SettingsViewModelTests/SettingsViewModelTests.Render.verified.txt b/test/AvaloniaExampleProject.Tests/Snapshots/SettingsViewModelTests/SettingsViewModelTests.Render.verified.txt
index 42ccfb0..495623a 100644
--- a/test/AvaloniaExampleProject.Tests/Snapshots/SettingsViewModelTests/SettingsViewModelTests.Render.verified.txt
+++ b/test/AvaloniaExampleProject.Tests/Snapshots/SettingsViewModelTests/SettingsViewModelTests.Render.verified.txt
@@ -39,11 +39,13 @@
},
{
Type: SettingsExpander,
- Header: Avalonia Example Project
+ Header: Avalonia Example Project,
+ Name: AboutSettingsExpander
}
]
}
},
+ FontFamily: Inter,
DataContext: {
ThemeService: {
AvailableThemes: [
diff --git a/test/AvaloniaExampleProject.Tests/Snapshots/SettingsViewModelTests/SettingsViewModelTests.Render_AboutExpanded.verified.png b/test/AvaloniaExampleProject.Tests/Snapshots/SettingsViewModelTests/SettingsViewModelTests.Render_AboutExpanded.verified.png
new file mode 100644
index 0000000..48aa61c
Binary files /dev/null and b/test/AvaloniaExampleProject.Tests/Snapshots/SettingsViewModelTests/SettingsViewModelTests.Render_AboutExpanded.verified.png differ
diff --git a/test/AvaloniaExampleProject.Tests/Snapshots/SettingsViewModelTests/SettingsViewModelTests.Render_AboutExpanded.verified.txt b/test/AvaloniaExampleProject.Tests/Snapshots/SettingsViewModelTests/SettingsViewModelTests.Render_AboutExpanded.verified.txt
new file mode 100644
index 0000000..495623a
--- /dev/null
+++ b/test/AvaloniaExampleProject.Tests/Snapshots/SettingsViewModelTests/SettingsViewModelTests.Render_AboutExpanded.verified.txt
@@ -0,0 +1,64 @@
+{
+ Type: SettingsView,
+ Content: {
+ Type: ScrollViewer,
+ VerticalScrollBarVisibility: Auto,
+ Content: {
+ Type: StackPanel,
+ Spacing: 8.0,
+ MaxWidth: 1024.0,
+ Margin: 0,0,0,48,
+ Children: [
+ {
+ Type: TextBlock,
+ Text: Personalization,
+ Theme: {
+ Type: ControlTheme
+ }
+ },
+ {
+ Type: StackPanel,
+ Spacing: 4.0,
+ Children: [
+ {
+ Type: SettingsExpander,
+ Header: Language
+ },
+ {
+ Type: SettingsExpander,
+ Header: Theme
+ }
+ ]
+ },
+ {
+ Type: TextBlock,
+ Text: About,
+ Theme: {
+ Type: ControlTheme
+ }
+ },
+ {
+ Type: SettingsExpander,
+ Header: Avalonia Example Project,
+ Name: AboutSettingsExpander
+ }
+ ]
+ }
+ },
+ FontFamily: Inter,
+ DataContext: {
+ ThemeService: {
+ AvailableThemes: [
+ Default,
+ Dark,
+ Light
+ ],
+ RequestedThemeVariant: {}
+ },
+ I18N: {Scrubbed},
+ AppVersion: {},
+ SelectedLanguage: en-EN,
+ SelectedTheme: Default,
+ ShowLicensesDialogCommand: SettingsViewModel.ShowLicensesDialogAsync(CancellationToken cancellationToken)
+ }
+}
\ No newline at end of file
diff --git a/test/AvaloniaExampleProject.Tests/Snapshots/WelcomeViewModelTests/WelcomeViewModelTests.Render.verified.png b/test/AvaloniaExampleProject.Tests/Snapshots/WelcomeViewModelTests/WelcomeViewModelTests.Render.verified.png
new file mode 100644
index 0000000..8b5713c
Binary files /dev/null and b/test/AvaloniaExampleProject.Tests/Snapshots/WelcomeViewModelTests/WelcomeViewModelTests.Render.verified.png differ
diff --git a/test/AvaloniaExampleProject.Tests/Snapshots/WelcomeViewModelTests/WelcomeViewModelTests.Render.verified.txt b/test/AvaloniaExampleProject.Tests/Snapshots/WelcomeViewModelTests/WelcomeViewModelTests.Render.verified.txt
index 1bf0ccb..6303292 100644
--- a/test/AvaloniaExampleProject.Tests/Snapshots/WelcomeViewModelTests/WelcomeViewModelTests.Render.verified.txt
+++ b/test/AvaloniaExampleProject.Tests/Snapshots/WelcomeViewModelTests/WelcomeViewModelTests.Render.verified.txt
@@ -15,6 +15,7 @@
}
]
},
+ FontFamily: Inter,
DataContext: {
I18N: {Scrubbed},
ShowInputDialogCommand: WelcomeViewModel.ShowInputDialog(CancellationToken cancellationToken)
diff --git a/test/AvaloniaExampleProject.Tests/TestAppBuilder.cs b/test/AvaloniaExampleProject.Tests/TestAppBuilder.cs
index 13af150..9c50290 100644
--- a/test/AvaloniaExampleProject.Tests/TestAppBuilder.cs
+++ b/test/AvaloniaExampleProject.Tests/TestAppBuilder.cs
@@ -1,7 +1,9 @@
-using System.Reflection;
using System.Runtime.CompilerServices;
using Avalonia;
+using Avalonia.Fonts.Inter;
using Avalonia.Headless;
+using Avalonia.Media;
+using AvaloniaExampleProject.Business;
using AvaloniaExampleProject.Models;
using AvaloniaExampleProject.Tests;
using Darp.Utils.Configuration;
@@ -19,17 +21,8 @@ public class TestAppBuilder
[ModuleInitializer]
public static void Init()
{
+ VerifyImageMagick.RegisterComparers(0.1);
VerifierSettings.InitializePlugins();
- RemovePngFileConverters();
- }
-
- private static void RemovePngFileConverters()
- {
- object? list = typeof(VerifierSettings)
- .GetField("typedConverters", BindingFlags.NonPublic | BindingFlags.Static)
- ?.GetValue(null);
- var clazz = typeof(VerifierSettings).Assembly.GetType("TypeConverter")!;
- typeof(List<>).MakeGenericType(clazz).GetMethod("Clear")!.Invoke(list, null);
}
private static readonly MainConfig MainConfig = new()
@@ -57,21 +50,28 @@ public static IConfigurationService SubstituteForMainConfigService()
public static AppBuilder BuildAvaloniaApp()
{
+ var appInformationService = Substitute.For();
+ appInformationService.Version.Returns("1.2.3-aabbccdd");
IServiceProvider provider = new ServiceCollection()
.AddLogging(builder => builder.AddXUnit())
.AddSingleton(SubstituteForMainConfigService())
.AddAppServices()
+ .AddSingleton(appInformationService)
.BuildServiceProvider();
Services = provider;
return AppBuilder
.Configure(() => new App(provider))
+ .UseSkia()
.UseHeadless(new AvaloniaHeadlessPlatformOptions { UseHeadlessDrawing = false })
+ .WithInterFont()
.AfterSetup(builder =>
{
if (builder.Instance is null)
throw new Exception("Instance is null");
+ var themes = builder.Instance.Styles.OfType();
+ builder.Instance.Styles.RemoveAll(themes);
builder.Instance.Styles.Add(
- new FluentAvaloniaTheme { PreferSystemTheme = true, PreferUserAccentColor = true }
+ new FluentAvaloniaTheme { PreferSystemTheme = false, PreferUserAccentColor = false }
);
});
}
diff --git a/test/AvaloniaExampleProject.Tests/VerifyHelpers.cs b/test/AvaloniaExampleProject.Tests/VerifyHelpers.cs
index 36d2c1a..e6dbc61 100644
--- a/test/AvaloniaExampleProject.Tests/VerifyHelpers.cs
+++ b/test/AvaloniaExampleProject.Tests/VerifyHelpers.cs
@@ -1,13 +1,20 @@
using System.Runtime.CompilerServices;
-using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
+using Avalonia.Media;
using AvaloniaExampleProject.Assets;
+using Shouldly;
namespace AvaloniaExampleProject.Tests;
public static class VerifyHelpers
{
- public static SettingsTask VerifyControl(Control control, [CallerFilePath] string? callerFilePath = null)
+ public static SettingsTask VerifyControl(TemplatedControl control, [CallerFilePath] string? callerFilePath = null)
{
+ var fontFamily = new FontFamily("avares://Avalonia.Fonts.Inter/Assets/Inter-Regular.ttf#Inter");
+ control.FontFamily = fontFamily;
+ control.Resources.Add("ContentControlThemeFontFamily", fontFamily);
+ control.FontFamily.Name.ShouldBe("Inter");
+ control.FontFamily.FamilyTypefaces.Count.ShouldBeGreaterThan(0);
string directory =
Path.GetFileNameWithoutExtension(callerFilePath) ?? throw new ArgumentNullException(nameof(callerFilePath));
return Verify(control).ScrubMembersWithType().UseDirectory(Path.Join("Snapshots", directory));
diff --git a/test/AvaloniaExampleProject.Tests/ViewModels/SettingsViewModelTests.cs b/test/AvaloniaExampleProject.Tests/ViewModels/SettingsViewModelTests.cs
index 30c8dd6..7b39a0e 100644
--- a/test/AvaloniaExampleProject.Tests/ViewModels/SettingsViewModelTests.cs
+++ b/test/AvaloniaExampleProject.Tests/ViewModels/SettingsViewModelTests.cs
@@ -12,8 +12,18 @@ public class SettingsViewModelTests
[AvaloniaFact]
public Task Render()
{
- var viewModel = TestAppBuilder.Services.GetRequiredService();
- var control = new SettingsView { ViewModel = viewModel };
+ var control = new SettingsView { ViewModel = TestAppBuilder.Services.GetRequiredService() };
return VerifyControl(control).ScrubMembersWithType();
}
+
+ [AvaloniaFact]
+ public Task Render_AboutExpanded()
+ {
+ var control = new SettingsView
+ {
+ ViewModel = TestAppBuilder.Services.GetRequiredService(),
+ AboutSettingsExpander = { IsExpanded = true },
+ };
+ return VerifyControl(control);
+ }
}
diff --git a/test/AvaloniaExampleProject.Tests/ViewModels/WelcomeViewModelTests.cs b/test/AvaloniaExampleProject.Tests/ViewModels/WelcomeViewModelTests.cs
index 1e1f9e0..42ccdd6 100644
--- a/test/AvaloniaExampleProject.Tests/ViewModels/WelcomeViewModelTests.cs
+++ b/test/AvaloniaExampleProject.Tests/ViewModels/WelcomeViewModelTests.cs
@@ -1,5 +1,4 @@
using Avalonia.Headless.XUnit;
-using AvaloniaExampleProject.Assets;
using AvaloniaExampleProject.ViewModels;
using AvaloniaExampleProject.Views;
using Microsoft.Extensions.DependencyInjection;
@@ -14,6 +13,6 @@ public Task Render()
{
var viewModel = TestAppBuilder.Services.GetRequiredService();
var control = new WelcomeView { ViewModel = viewModel };
- return VerifyControl(control).ScrubMembersWithType();
+ return VerifyControl(control);
}
}