test(ui): inspect themed property hierarchy

This commit is contained in:
2026-11-02 11:18:10 -08:00
parent 221b81628a
commit 91e308e8e2
2 changed files with 22 additions and 13 deletions
+6 -4
View File
@@ -6,19 +6,21 @@
- `ChannelStatsServiceTests` uses dates relative to UTC so its 90-day upload assertion does not - `ChannelStatsServiceTests` uses dates relative to UTC so its 90-day upload assertion does not
expire with the calendar; fix committed as `3985a41`. expire with the calendar; fix committed as `3985a41`.
- `ScoreboardPropertiesTests` now checks for the themed `PropertyPillCheck` style instead of stale - `ScoreboardPropertiesTests` now checks for the themed `PropertyPillCheck` style instead of stale
`YtCheckBox` markup; focused test passes. This task is ready to commit. `YtCheckBox` markup; committed as `221b816`.
- Clean app build is 0 warnings. Full suite: 11 failed / 511 passed of 522; remaining failures are - `PropertiesHierarchyTests` now checks shared styles and scopes the duplicate shape bindings to
the Shape block; focused test passes. This task is ready to commit.
- Clean app build is 0 warnings. Full suite: 10 failed / 512 passed of 522; remaining failures are
being handled one task and one local commit at a time. being handled one task and one local commit at a time.
## Next action ## Next action
Commit the scoreboard-properties assertion update, then take the next remaining failure. Do not Commit the properties-hierarchy assertion update, then take the next remaining failure. Do not
push absent a milestone/checkpoint request. push absent a milestone/checkpoint request.
## Landmines ## Landmines
- Build/test with Windows `dotnet.exe`; the full suite can hang in WASAPI teardown. - Build/test with Windows `dotnet.exe`; the full suite can hang in WASAPI teardown.
- `verify.sh` cannot pass until the remaining 11 suite failures are resolved; scope checks are run - `verify.sh` cannot pass until the remaining 10 suite failures are resolved; scope checks are run
separately when that gate exits early. separately when that gate exits early.
- Any integration test constructing `MainWindow` must set `LayoutPathOverride` to a temporary DB - Any integration test constructing `MainWindow` must set `LayoutPathOverride` to a temporary DB
and clear it in `finally`. and clear it in `finally`.
+16 -9
View File
@@ -47,20 +47,27 @@ public class PropertiesHierarchyTests
// ── The panel: a Shape title owns the options, the hint is indented ─── // ── The panel: a Shape title owns the options, the hint is indented ───
var doc = XDocument.Load(SourceAssert.RepoPath("Controls", "LeftPanel.xaml")); var doc = XDocument.Load(SourceAssert.RepoPath("Controls", "LeftPanel.xaml"));
XNamespace xaml = "http://schemas.microsoft.com/winfx/2006/xaml";
var theme = XDocument.Load(SourceAssert.RepoPath("Themes", "Controls.xaml"));
var propertyHeaderStyle = theme.Descendants().Single(e => e.Name.LocalName == "Style"
&& (string?)e.Attribute(xaml + "Key") == "PropertyHeader");
var fontWeight = propertyHeaderStyle.Elements().Single(e => e.Name.LocalName == "Setter"
&& (string?)e.Attribute("Property") == "FontWeight");
Assert.Equal("SemiBold", (string?)fontWeight.Attribute("Value"));
var shapeTitle = doc.Descendants().Single(e => e.Name.LocalName == "TextBlock" var shapeTitle = doc.Descendants().Single(e => e.Name.LocalName == "TextBlock"
&& (string?)e.Attribute("Text") == "Shape"); && (string?)e.Attribute("Text") == "Shape");
Assert.Equal("SemiBold", (string?)shapeTitle.Attribute("FontWeight")); Assert.Equal("{StaticResource PropertyHeader}", (string?)shapeTitle.Attribute("Style"));
var rectOption = OptionBoundTo(doc, "IsRectShape"); var shapeBlock = shapeTitle.Parent!;
var circleOption = OptionBoundTo(doc, "IsRoundShape"); var rectOption = OptionBoundTo(shapeBlock, "IsRectShape");
var circleOption = OptionBoundTo(shapeBlock, "IsRoundShape");
Assert.Contains("TwoWay", (string?)rectOption.Attribute("IsChecked") ?? ""); Assert.Contains("TwoWay", (string?)rectOption.Attribute("IsChecked") ?? "");
// The CIRCLE is the option that is not offered in the vertical Chat layout. // The CIRCLE is the option that is not offered in the vertical Chat layout.
Assert.Contains("CanToggleClipShape", (string?)circleOption.Attribute("Visibility") ?? ""); Assert.Contains("CanToggleClipShape", (string?)circleOption.Attribute("Visibility") ?? "");
// Both options live under the Shape title. // Both options live under the Shape title.
var shapeBlock = shapeTitle.Parent!;
Assert.Contains(rectOption, shapeBlock.Descendants()); Assert.Contains(rectOption, shapeBlock.Descendants());
Assert.Contains(circleOption, shapeBlock.Descendants()); Assert.Contains(circleOption, shapeBlock.Descendants());
@@ -76,8 +83,8 @@ public class PropertiesHierarchyTests
// ── Every titled block owns its content one indent deeper ──────────── // ── Every titled block owns its content one indent deeper ────────────
var geometryTitle = doc.Descendants() var geometryTitle = doc.Descendants()
.Where(e => e.Name.LocalName == "TextBlock" && (string?)e.Attribute("Text") == "Geometry") .Where(e => e.Name.LocalName == "TextBlock" && (string?)e.Attribute("Text") == "Geometry")
.Single(e => ((string?)e.Parent?.Attribute("Visibility") ?? "").Contains("IsWebcamSelected")); .Single(e => ((string?)e.Parent?.Attribute("Visibility") ?? "").Contains("SelectedElement.IsDraggable"));
Assert.Equal("SemiBold", (string?)geometryTitle.Attribute("FontWeight")); Assert.Equal("{StaticResource PropertyHeader}", (string?)geometryTitle.Attribute("Style"));
foreach (var row in geometryTitle.Parent!.Elements().Where(e => e.Name.LocalName == "DockPanel")) foreach (var row in geometryTitle.Parent!.Elements().Where(e => e.Name.LocalName == "DockPanel"))
Assert.Equal(10, LeftIndent(row)); Assert.Equal(10, LeftIndent(row));
@@ -89,7 +96,7 @@ public class PropertiesHierarchyTests
{ {
var label = webcamPanel.Descendants().Single(e => e.Name.LocalName == "TextBlock" var label = webcamPanel.Descendants().Single(e => e.Name.LocalName == "TextBlock"
&& (string?)e.Attribute("Text") == title); && (string?)e.Attribute("Text") == title);
Assert.Equal("SemiBold", (string?)label.Attribute("FontWeight")); Assert.Equal("{StaticResource PropertyHeader}", (string?)label.Attribute("Style"));
Assert.Equal(10, LeftIndent(RowAfter(label))); Assert.Equal(10, LeftIndent(RowAfter(label)));
} }
@@ -101,8 +108,8 @@ public class PropertiesHierarchyTests
} }
/// <summary>The shape option bound to the given model property.</summary> /// <summary>The shape option bound to the given model property.</summary>
private static XElement OptionBoundTo(XDocument doc, string property) => private static XElement OptionBoundTo(XElement scope, string property) =>
doc.Descendants().Single(e => e.Name.LocalName == "ToggleButton" scope.Descendants().Single(e => e.Name.LocalName == "ToggleButton"
&& ((string?)e.Attribute("IsChecked") ?? "").Contains(property)); && ((string?)e.Attribute("IsChecked") ?? "").Contains(property));
/// <summary>The row (DockPanel) that holds the given label.</summary> /// <summary>The row (DockPanel) that holds the given label.</summary>