From 221b81628affcbbe5c01b3522ae9d7fa83d19c20 Mon Sep 17 00:00:00 2001 From: gramps Date: Mon, 2 Nov 2026 11:14:15 -0800 Subject: [PATCH] test(scoreboard): assert the themed pill switch --- HANDOFF.md | 16 +++++++++------- docs/ai/ui-surface.md | 2 +- ytLive.Tests/ScoreboardPropertiesTests.cs | 6 +++--- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/HANDOFF.md b/HANDOFF.md index 867081f..7e5d3b3 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -3,20 +3,22 @@ ## State - `main`; Logs tabs committed as `9fa06c6`, local and not pushed. -- First test-failure task: `ChannelStatsServiceTests` now uses dates relative to UTC so the 90-day - upload assertion does not expire with the calendar. Focused test passes. -- Clean app build is 0 warnings. Full suite: 12 failed / 510 passed of 522; the remaining UI/content - failures are being handled one task and one local commit at a time. +- `ChannelStatsServiceTests` uses dates relative to UTC so its 90-day upload assertion does not + expire with the calendar; fix committed as `3985a41`. +- `ScoreboardPropertiesTests` now checks for the themed `PropertyPillCheck` style instead of stale + `YtCheckBox` markup; focused test passes. This task is ready to commit. +- Clean app build is 0 warnings. Full suite: 11 failed / 511 passed of 522; remaining failures are + being handled one task and one local commit at a time. ## Next action -Commit the date-relative YPP test fix, then take the next remaining failure. Do not push absent a -milestone/checkpoint request. +Commit the scoreboard-properties assertion update, then take the next remaining failure. Do not +push absent a milestone/checkpoint request. ## Landmines - Build/test with Windows `dotnet.exe`; the full suite can hang in WASAPI teardown. -- `verify.sh` cannot pass until the remaining 12 suite failures are resolved; scope checks are run +- `verify.sh` cannot pass until the remaining 11 suite failures are resolved; scope checks are run separately when that gate exits early. - Any integration test constructing `MainWindow` must set `LayoutPathOverride` to a temporary DB and clear it in `finally`. diff --git a/docs/ai/ui-surface.md b/docs/ai/ui-surface.md index 5e2b862..6092e88 100644 --- a/docs/ai/ui-surface.md +++ b/docs/ai/ui-surface.md @@ -41,7 +41,7 @@ Placement math itself is covered by `WindowPlacementTests` (pure function, no HW - Layout persists to SQLite (`Microsoft.Data.Sqlite`); scenes/sources/asset bytes stored in the DB, asset identity is a SHA-256 content hash (1:M reuse, no file paths — assets are always available). Loaded sources always derive `IsBackground` from `Type` (OR'd with the persisted column, so legacy DisplayCapture backdrops keep their flag) — pre-derivation rows with `IsBackground=0` heal on load - **Element z-order is a unified space, not per-table (2026-09-20):** `Source` and `WebcamSceneConfig` live in different tables, and a webcam can be interleaved between sources in a scene. Both tables' `SortOrder` is stamped from the SAME counter — the element's index within `scene.Elements` — and `Load` merges the two tables' rows per scene by that shared z (tie-break sources-first, so legacy per-table rows read back stacked the way they were saved). Never re-introduce per-type sort counters + "append all Sources then all configs", or a webcam slotted mid-stack reverts on reload (creator-reproduced bug). - **Five-scene catalog (`Models/SceneCatalog.cs`):** the product is exactly Starting/Live/BRB/Chat/Ending — work with less, never more (the escape hatch for "more" is OBS). Scenes are matched **by name** (`SceneCatalog.Is`, case-insensitive trim). Empty DBs seed all five; the scenes-header "+" (`ShowAddScene`/`MissingScenes` on `MainViewModel`) only appears while ≥1 canonical scene is missing and its menu lists only the missing ones, re-adding them by name (`AddSceneCommand`). Renaming a canonical scene makes it missing again; `AddScene` rejects non-canonical names. -- Theming: all custom styles live in `Themes/Controls.xaml`, merged in `App.xaml` — never duplicate styles per-window (dialog duplicates were consolidated into this dictionary). **Every control in the app carries a theme style (TASK 58, 2026-10-21):** an unstyled `ComboBox`/`CheckBox` falls back to the **light WPF system template** (grey border, system arrow, white popup list) and sticks out of the dark panels — the properties panel's drop-downs had been hand-painted with a `Background`/`Foreground` instead of a style (the chat Font drop-down was a copy of that), and every check box was default. The rule now: `ComboBox` → `YtComboBox`, `CheckBox` → `YtCheckBox`, the dense properties fields → `YtTextBoxCompact` (`BasedOn YtTextBox`: 11px, padding 4,2) — **no hand-painted `Background`/`BorderBrush` on a themed control**. `ytLive.Tests/PropertiesStyleTests.cs` is the guard: it fails if an unstyled drop-down, check box or properties field comes back — **but that guard sees only those three control types**, so a picker built as something else (a `Button` + a code-built `ContextMenu`) slipped through it and the creator had to ask twice (TASK 63) +- Theming: all custom styles live in `Themes/Controls.xaml`, merged in `App.xaml` — never duplicate styles per-window (dialog duplicates were consolidated into this dictionary). **Every control in the app carries a theme style (TASK 58, 2026-10-21):** an unstyled `ComboBox`/`CheckBox` falls back to the **light WPF system template** (grey border, system arrow, white popup list) and sticks out of the dark panels — the properties panel's drop-downs had been hand-painted with a `Background`/`Foreground` instead of a style (the chat Font drop-down was a copy of that), and every check box was default. The rule now: `ComboBox` → `YtComboBox`, property-panel switches → `PropertyPillCheck` (plain check boxes may use `YtCheckBox`), the dense properties fields → `YtTextBoxCompact` (`BasedOn YtTextBox`: 11px, padding 4,2) — **no hand-painted `Background`/`BorderBrush` on a themed control**. `ytLive.Tests/PropertiesStyleTests.cs` is the guard: it fails if an unstyled drop-down, check box or properties field comes back — **but that guard sees only those three control types**, so a picker built as something else (a `Button` + a code-built `ContextMenu`) slipped through it and the creator had to ask twice (TASK 63) - **The properties panel is a HIERARCHY (TASK 59, 2026-10-21):** a **title owns its content, and the content is indented one level (10px) under it** — the creator's ruling: *"the 'drag to move' help text needs to be indented under 'Mirror Rect' to show parentage … same with Geometry, Border Color, Border Thickness … Remove should be indented under Background as should Blur … Strength should be indented under Blur."* So the webcam's block reads `Shape` (the mirror switch + the shape choice) → `Geometry` → `Border Color` → `Border Thickness` → `Background` (Remove, Blur, and Strength one level deeper under Blur), each title SemiBold at 11px `#a0a0b0` like the existing ones. **The shape is a CHOICE, not a toggle button:** `IsRectShape`/`IsRoundShape` on `SceneElement` are two settable bools and the panel shows them as two `SegmentToggle` options (Rect | Circle) with **Rect selected by default** (an element that never picked a shape is a rect) — the old single button showed the *next* action ("Round"), which is why the creator could not tell what the camera was. The mutual exclusion lives in the MODEL (each setter flips through `ToggleClipShape()` and both props are re-raised), **not** in a WPF `RadioButton` group — a RadioButton group unchecks its siblings by setting `IsChecked` in code, which silently kills the binding (stackoverflow 7376373). Each setter always re-raises, so a refused write (the vertical Chat layout is rect-only) makes the UI snap back. Guard: `ytLive.Tests/PropertiesHierarchyTests.cs` - **One list for the offered colours (TASK 60):** the border-colour drop-down builds its menu from `Models/BorderColorPalette.cs` (`NamedColor(Hex, Name)` + `NameOf(hex)` — **empty** for a colour diff --git a/ytLive.Tests/ScoreboardPropertiesTests.cs b/ytLive.Tests/ScoreboardPropertiesTests.cs index a3cda0d..5861de5 100644 --- a/ytLive.Tests/ScoreboardPropertiesTests.cs +++ b/ytLive.Tests/ScoreboardPropertiesTests.cs @@ -63,10 +63,10 @@ public class ScoreboardPropertiesTests }) Assert.Contains(binding, panel); - // Theme widgets only: the colour pickers are the app's drop-down (YtComboBox), never a - // hand-built menu, and the toggles are YtCheckBox. + // Theme widgets only: colour pickers use YtComboBox and the property-panel toggles use + // the themed pill-switch style. Assert.Contains("Style=\"{StaticResource YtComboBox}\"", block); - Assert.Contains("Style=\"{StaticResource YtCheckBox}\"", block); + Assert.Contains("Style=\"{StaticResource PropertyPillCheck}\"", block); Assert.DoesNotContain("ContextMenu", block); // The VM surface exists for every binding above (a typo would bind to nothing silently).