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.");
}
}