using System.Text.RegularExpressions; using System.Xml.Linq; using Xunit; namespace FieldLogger.Tests; /// /// Headless guardrails for the Sprint 3 MAUI screen integration. These deliberately inspect /// XAML source so the QA gate can verify token use and accessibility basics without a MAUI /// simulator or device workload. /// public sealed class Sprint3UiContractTests { private static readonly string[] MigratedScreens = [ "FieldLogger/Views/HomePage.xaml", "FieldLogger/Views/JobsPage.xaml", "FieldLogger/Views/JobDetailPage.xaml", "FieldLogger/Views/MapPage.xaml", ]; [Fact] public void Migrated_screens_use_only_semantic_color_resources() { var hardCodedColor = new Regex(@"#[0-9a-fA-F]{3,8}\b", RegexOptions.CultureInvariant); foreach (var path in MigratedScreens) { var text = File.ReadAllText(FromRoot(path)); Assert.False(hardCodedColor.IsMatch(text), $"Page-level color literal found in {path}"); Assert.DoesNotContain("TextColor=\"Gray\"", text, StringComparison.OrdinalIgnoreCase); Assert.DoesNotContain("DodgerBlue", text, StringComparison.OrdinalIgnoreCase); Assert.DoesNotContain("IndianRed", text, StringComparison.OrdinalIgnoreCase); } } [Fact] public void Every_referenced_Survey_Teal_token_exists_in_generated_resources() { var resourceText = string.Concat( File.ReadAllText(FromRoot("FieldLogger/Resources/Styles/GeneratedTokens.xaml")), File.ReadAllText(FromRoot("FieldLogger/Resources/Styles/Styles.xaml"))); var defined = Regex.Matches(resourceText, "x:Key=\\\"(?Ul[^\\\"]+)\\\"") .Select(match => match.Groups["key"].Value) .ToHashSet(StringComparer.Ordinal); var resourceReference = new Regex(@"\{(?:Static|Dynamic)Resource (?Ul[^}\s]+)\}"); foreach (var path in MigratedScreens.Append("FieldLogger/AppShell.xaml")) { foreach (Match match in resourceReference.Matches(File.ReadAllText(FromRoot(path)))) { var key = match.Groups["key"].Value; Assert.Contains(key, defined); } } } [Fact] public void App_loads_generated_tokens_before_shared_component_styles() { var app = File.ReadAllText(FromRoot("FieldLogger/App.xaml")); var generatedIndex = app.IndexOf("GeneratedTokens.xaml", StringComparison.Ordinal); var stylesIndex = app.IndexOf("Styles.xaml", StringComparison.Ordinal); Assert.True(generatedIndex >= 0, "App.xaml must merge GeneratedTokens.xaml."); Assert.True(stylesIndex > generatedIndex, "Generated tokens must load before component styles."); } [Fact] public void Migrated_screens_consume_shared_component_styles() { var migrated = string.Concat(MigratedScreens.Select(path => File.ReadAllText(FromRoot(path)))); Assert.Contains("UlCard", migrated, StringComparison.Ordinal); Assert.Contains("UlJobRow", migrated, StringComparison.Ordinal); Assert.Contains("UlStatusBadge", migrated, StringComparison.Ordinal); Assert.Contains("UlButtonSecondary", migrated, StringComparison.Ordinal); } [Fact] public void Every_page_button_declares_at_least_the_minimum_touch_target() { var accepted = new[] { "{StaticResource UlTouchMinimum}", "{StaticResource UlTouchComfortable}", "{StaticResource UlTouchPrimary}", }; foreach (var path in MigratedScreens) { var document = XDocument.Load(FromRoot(path)); foreach (var button in document.Descendants().Where(e => e.Name.LocalName == "Button")) { var target = button.Attribute("MinimumHeightRequest")?.Value; Assert.Contains(target, accepted); } } } [Fact] public void Production_shell_does_not_expose_Debug_as_a_primary_tab() { var shell = File.ReadAllText(FromRoot("FieldLogger/AppShell.xaml")); Assert.DoesNotContain("Title=\"Debug\"", shell, StringComparison.Ordinal); Assert.DoesNotContain("views:DebugPage", shell, StringComparison.Ordinal); Assert.DoesNotContain("= 0, "Device deployment must clean stale partial MAUI outputs."); Assert.True(runIndex > cleanIndex, "The clean must run before the device build."); Assert.Contains("-p:RuntimeIdentifier=ios-arm64", script, StringComparison.Ordinal); } private static string FromRoot(string relativePath) => Path.Combine(FindRepositoryRoot(), relativePath); private static string FindRepositoryRoot() { for (var directory = new DirectoryInfo(AppContext.BaseDirectory); directory is not null; directory = directory.Parent) { if (File.Exists(Path.Combine(directory.FullName, "FieldLogger.sln"))) return directory.FullName; } throw new DirectoryNotFoundException("Could not find the FieldLogger repository root."); } }