feat: complete UM Trace iOS and field UI integration
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<!--
|
||||
QA smoke/regression harness for the Field Logger app (S1-f, trace: SRS §6 gate / NFR-8).
|
||||
QA smoke/regression harness for the UM Trace app (S1-f, trace: SRS §6 gate / NFR-8).
|
||||
|
||||
Targets plain net9.0 (NOT the MAUI platform TFMs) so `dotnet test` runs on any dev/CI
|
||||
machine without the MAUI workload, keeping the QA gate fast. It deliberately does NOT
|
||||
|
||||
@@ -101,6 +101,11 @@ public sealed class Sprint3UiContractTests
|
||||
var shell = File.ReadAllText(FromRoot("FieldLogger/AppShell.xaml"));
|
||||
Assert.DoesNotContain("Title=\"Debug\"", shell, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("views:DebugPage", shell, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("<TabBar", shell, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("Title=\"Home\"", shell, StringComparison.Ordinal);
|
||||
Assert.True(shell.IndexOf("Title=\"Jobs\"", StringComparison.Ordinal) <
|
||||
shell.IndexOf("Title=\"Map\"", StringComparison.Ordinal),
|
||||
"Jobs must be the first primary route so it is selected at startup.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -113,6 +118,77 @@ public sealed class Sprint3UiContractTests
|
||||
Assert.DoesNotContain("CaptureCommand", home, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Primary_pages_use_status_chrome_and_floating_menu()
|
||||
{
|
||||
foreach (var path in new[]
|
||||
{
|
||||
"FieldLogger/Views/JobsPage.xaml",
|
||||
"FieldLogger/Views/MapPage.xaml",
|
||||
"FieldLogger/Views/SettingsPage.xaml",
|
||||
})
|
||||
{
|
||||
var page = File.ReadAllText(FromRoot(path));
|
||||
Assert.Contains("controls:AppStatusBar", page, StringComparison.Ordinal);
|
||||
Assert.Contains("controls:FloatingMenuButton", page, StringComparison.Ordinal);
|
||||
Assert.Contains("Shell.NavBarIsVisible=\"False\"", page, StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Settings_hides_manual_map_and_broker_configuration()
|
||||
{
|
||||
var settings = File.ReadAllText(FromRoot("FieldLogger/Views/SettingsPage.xaml"));
|
||||
Assert.DoesNotContain("Google Maps API Key", settings, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("Mqtt", settings, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.DoesNotContain("Durable cloud sync", settings, StringComparison.Ordinal);
|
||||
Assert.Contains("OpenDebugCommand", settings, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Map_has_no_heading_panel_and_fills_its_root_grid()
|
||||
{
|
||||
var map = File.ReadAllText(FromRoot("FieldLogger/Views/MapPage.xaml"));
|
||||
Assert.DoesNotContain("LIVE MAP", map, StringComparison.Ordinal);
|
||||
Assert.Contains("<ContentView x:Name=\"MapHost\"", map, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void User_facing_app_name_is_UM_Trace()
|
||||
{
|
||||
var shell = File.ReadAllText(FromRoot("FieldLogger/AppShell.xaml"));
|
||||
var settings = File.ReadAllText(FromRoot("FieldLogger/Views/SettingsPage.xaml"));
|
||||
Assert.Contains("Title=\"UM Trace\"", shell, StringComparison.Ordinal);
|
||||
Assert.Contains("UM Trace v", settings, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Jobs_uses_a_stable_full_height_refresh_layout()
|
||||
{
|
||||
var page = File.ReadAllText(FromRoot("FieldLogger/Views/JobsPage.xaml"));
|
||||
var viewModel = File.ReadAllText(FromRoot("FieldLogger/ViewModels/JobsViewModel.cs"));
|
||||
|
||||
Assert.Contains("IsRefreshing=\"{Binding IsRefreshing, Mode=OneWay}\"", page, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("IsRefreshing=\"{Binding IsBusy}\"", page, StringComparison.Ordinal);
|
||||
Assert.Contains("ItemsUpdatingScrollMode=\"KeepItemsInView\"", page, StringComparison.Ordinal);
|
||||
Assert.Contains("HorizontalOptions=\"Fill\" VerticalOptions=\"Fill\"", page, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("Padding=\"16,16,16,88\"", page, StringComparison.Ordinal);
|
||||
Assert.Contains("ApplySnapshot(items)", viewModel, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("Jobs.Clear()", viewModel, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Device_deploy_rebuilds_Xaml_and_brand_assets_from_a_clean_iOS_output()
|
||||
{
|
||||
var script = File.ReadAllText(FromRoot("scripts/deploy-ios-device.sh"));
|
||||
var cleanIndex = script.IndexOf("\"${dotnet_bin}\" clean", StringComparison.Ordinal);
|
||||
var runIndex = script.IndexOf("\"${dotnet_bin}\" build", StringComparison.Ordinal);
|
||||
|
||||
Assert.True(cleanIndex >= 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()
|
||||
|
||||
@@ -167,7 +167,7 @@ public class RoundTripTests
|
||||
int triggersSeen = 0;
|
||||
|
||||
// Stand-in App: consume telemetry, and on each trigger apply a simple accuracy gate
|
||||
// and write back a capture-result. This is what Field Logger's Locator Driver does.
|
||||
// and write back a capture-result. This is what UM Trace's Locator Driver does.
|
||||
link.ToApp += frame =>
|
||||
{
|
||||
switch (Frames.PeekType(frame))
|
||||
|
||||
Reference in New Issue
Block a user