using System.Text.RegularExpressions; using Avalonia; using Avalonia.Headless.XUnit; using Avalonia.Styling; namespace AvParser.UI.HeadlessTests; /// /// Every resource key the XAML asks for must actually resolve. /// /// /// A DynamicResource naming a key that does not exist fails silently: the property simply /// keeps its default, so a background never paints and a brush is transparent. That shipped twice — /// most recently as a viewer overlay you could see straight through, caught only by looking at a /// screenshot. Both themes are checked, because a key can exist in one dictionary and not the other. /// public partial class ResourceKeyTests { [GeneratedRegex(@"\{(?:Dynamic|Static)Resource\s+([A-Za-z0-9_.]+)\s*\}", RegexOptions.Compiled)] private static partial Regex ResourceMarkup(); /// Keys defined inside a view's own Resources block rather than in a theme. private static readonly HashSet Local = new(StringComparer.Ordinal) { "LocalizedOptionTemplate" }; private static DirectoryInfo RepositoryRoot() { var directory = new DirectoryInfo(AppContext.BaseDirectory); while (directory is not null && !File.Exists(Path.Combine(directory.FullName, "AvParser.slnx"))) { directory = directory.Parent; } return directory ?? throw new InvalidOperationException("Could not find the repository root."); } [AvaloniaTheory] [InlineData("Light")] [InlineData("Dark")] public void Every_resource_key_used_in_xaml_resolves(string theme) { var application = Application.Current.ShouldNotBeNull(); application.RequestedThemeVariant = theme == "Light" ? ThemeVariant.Light : ThemeVariant.Dark; var views = Path.Combine(RepositoryRoot().FullName, "src", "AvParser.UI"); var files = Directory.GetFiles(views, "*.axaml", SearchOption.AllDirectories); files.ShouldNotBeEmpty(); var missing = new SortedSet(StringComparer.Ordinal); foreach (var file in files) { foreach (Match match in ResourceMarkup().Matches(File.ReadAllText(file))) { var key = match.Groups[1].Value; if (Local.Contains(key)) { continue; } if (!application.TryGetResource(key, application.ActualThemeVariant, out _)) { missing.Add($"{key} ({Path.GetFileName(file)})"); } } } missing.ShouldBeEmpty(); } }