using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Headless.XUnit; using Avalonia.Media; using Avalonia.Styling; using Avalonia.Threading; using Avalonia.VisualTree; namespace AvParser.UI.HeadlessTests; public class StyleTests { private static T Show(T control, ThemeVariant variant) where T : Control { Application.Current!.RequestedThemeVariant = variant; var window = new Window { Width = 400, Height = 200, Content = control, }; window.Show(); Dispatcher.UIThread.RunJobs(); return control; } private static Color TokenColor(string key, ThemeVariant variant) { Application.Current!.TryFindResource(key, variant, out var value).ShouldBeTrue(); return value.ShouldBeOfType().Color; } private static Color? RenderedBackground(Control control) => ( control.GetVisualDescendants().OfType().FirstOrDefault()?.Background ?? control.GetValue(TemplatedControl.BackgroundProperty) ) is SolidColorBrush brush ? brush.Color : null; [AvaloniaTheory] [InlineData("Light")] [InlineData("Dark")] public void The_primary_button_is_filled_with_the_accent_colour(string variantName) { var variant = variantName == "Light" ? ThemeVariant.Light : ThemeVariant.Dark; var button = Show(new Button { Classes = { "primary" }, Content = "Go" }, variant); // If this fails the button paints itself white-on-white and simply disappears. RenderedBackground(button).ShouldBe(TokenColor("AppAccentBrush", variant)); } [AvaloniaTheory] [InlineData("Light")] [InlineData("Dark")] public void A_card_is_distinguishable_from_the_page_behind_it(string variantName) { var variant = variantName == "Light" ? ThemeVariant.Light : ThemeVariant.Dark; var page = TokenColor("AppSurfaceBrush", variant); var card = TokenColor("AppSurfaceRaisedBrush", variant); var border = TokenColor("AppBorderBrush", variant); // Summed across three channels, 24 is roughly a 3% per-channel step — the point at which // a card stops being a rectangle you have to squint for. The first light palette here // scored 33 and was still visually indistinguishable, so the bar is deliberately high. Distance(page, card).ShouldBeGreaterThan(30); Distance(page, border).ShouldBeGreaterThan(24); } [AvaloniaTheory] [InlineData("Light")] [InlineData("Dark")] public void The_card_style_reaches_a_bare_border(string variantName) { var variant = variantName == "Light" ? ThemeVariant.Light : ThemeVariant.Dark; var card = Show(new Border { Classes = { "card" } }, variant); card.BorderThickness.ShouldBe(new Thickness(1)); card.Padding.ShouldBe(new Thickness(16)); // The value a DynamicResource actually resolved to for this element's variant — not what // TryFindResource can dig out when asked for a variant explicitly. (card.Background as SolidColorBrush)?.Color.ShouldBe(TokenColor("AppSurfaceRaisedBrush", variant)); } private static int Distance(Color a, Color b) => Math.Abs(a.R - b.R) + Math.Abs(a.G - b.G) + Math.Abs(a.B - b.B); }