fix(ui): drawers close on any click outside the rail — TEST was the missing third

Creator: 'when the test slide-out is active, then any click off the div should
close the div — this behaviour applies to all tabs, not just TEST.'

MainWindow.Window_PreviewMouseLeftButtonDown already closed the Stream Settings
and YPP drawers on any click outside TextPullOutHost, but its close list skipped
TestSession — so the TEST drawer never dismissed on an outside click.

Fix: run TestSession.CloseDrawerCommand in the same outside-click branch.
All three drawers share the rail host, so the containment check is unchanged.

Good Dog: ONE integration test — the existing TestStream window section now
raises a window-root PreviewMouseLeftButtonDown (source = window => outside the
rail; deterministic, no OS mouse) and asserts the TEST and Stream Settings
drawers both close. Gate: clean build 0 warnings, full vstest 318/318.
This commit is contained in:
2026-09-25 08:18:25 -07:00
parent 0981a72eea
commit ecb329e578
7 changed files with 91 additions and 32 deletions
+26 -30
View File
@@ -1,36 +1,33 @@
# HANDOFF — 2026-09-25 (TASK 45 shipped: TEST-tab insert 400 fix — body was missing `snippet.type`)
# HANDOFF — 2026-09-25 (TASK 46 shipped: drawers close on any outside click, TEST included)
## Branch / Commit State
`main` HEAD = new commit for TASK 45 (chat-insert 400 fix); previous: TASK 44 commit
(TEST-tab chat fix), `94f0150`. Repo is ~12 commits ahead of pushed `197ee81` —
**push is the creator's call** (sub-milestone decision, not per-commit).
`main` HEAD = new commit for TASK 46 (drawer click-outside dismiss for all three tabs);
previous: TASK 45 commit (chat-insert 400 fix), `0981a72`. Repo is ~13 commits ahead of
pushed `197ee81` — **push is the creator's call** (sub-milestone decision, not per-commit).
**Working tree CLEAN after this unit's commit.**
## ⚠️ The unit that just shipped (TASK 44 reopened by the live run)
## ⚠️ The unit that just shipped (creator directive)
The creator ran the TASK 44 fix and the drawer now worked — but every Mock Chat Input
send returned **"YouTube rejected the message (error 400)"**. The runtime log
(`%APPDATA%\ytLlive\startup.log`) carried the body: `400 MISSING_REQUIRED_FIELD`,
`domain: youtube.api.v3.LiveChatMessageInsertResponse.Error`.
"when the test slide-out is active, then any click off the div should close the div —
this behaviour applies to all tabs, not just TEST."
- **Root cause:** the `liveChat/messages.insert` body must declare **`snippet.type`**
(`textMessageEvent` or `pollEvent`) alongside `liveChatId` and
`textMessageDetails.messageText` — the official insert reference lists `type` as a
required property. The TASK 41 body sent only `liveChatId` + `textMessageDetails`.
- **Why the suite didn't catch it:** the TASK 41 Good Dog test asserted the POST body
contained `liveChatId` + `messageText` but **never `type`** — false-green while real
YouTube rejected every send. The fix asserts `type` too, so a dropped field fails now.
- **Fix (TASK 45, `TASKS/task-45-chat-insert-type.md`):** `InsertChatMessageAsync` adds
`type = "textMessageEvent"` to the body; test assertion strengthened; recipe added to
`MyMistakes.md` (MISSING_REQUIRED_FIELD = wrong body shape, not auth).
- **Root cause:** right-rail slides (Stream Settings / YPP / TEST, all inside
`TextPullOutHost`) close on outside clicks via `MainWindow.Window_PreviewMouseLeftButtonDown`
— but that handler only closed `BroadcastForm` + `Ypp`; **`TestSession` was never in the
close list**, so the TEST drawer ignored outside clicks. The XAML comment even said "both".
- **Fix (TASK 46, `TASKS/task-46-drawer-click-outside-close.md`):** add
`TestSession.CloseDrawerCommand` to the same `!IsClickInsideDrawer` branch; corrected
the stale "both" comment. No containment change needed — all three share the rail host.
**Good Dog:** ONE integration test — the strengthened
`TestStream_DockTooling_And_ThreeWayDrawerExclusivity` POST-body assertion.
**Gate: clean build 0 warnings; full vstest 317/318 — the 1 failure is the known
environmental `RealMouseDrag` flake (mouse stolen by a fullscreen window mid-suite);
it PASSES in isolation (3/3), confirmed just now, so our change is green.**
**Good Dog:** ONE integration test — the existing
`TestStream_DockTooling_And_ThreeWayDrawerExclusivity` section-2 window test was extended:
opens TEST and Stream Settings, raises a window-root `PreviewMouseLeftButtonDown` (source =
window ⇒ outside the rail; deterministic, no OS mouse, immune to the fullscreen mouse-steal
flake), asserts each closes.
**Gate: clean build 0 warnings; full vstest 318/318 passed** (the LayerReorder
real-mouse-drag test passed this run too).
## Follow-ups queued (NOT done in this unit)
@@ -53,17 +50,16 @@ it PASSES in isolation (3/3), confirmed just now, so our change is green.**
`ready`) returns nothing by design — missing liveChatId ≈ "broadcast not live yet", not an
auth failure, until ~20s post push. And `400 MISSING_REQUIRED_FIELD` from
`liveChat/messages.insert` = wrong body shape (missing `snippet.type`), NOT auth/scope.
- **WPF test technique (TASK 46):** a `MouseButtonEventArgs { RoutedEvent = Mouse.PreviewMouseLeftButtonDownEvent }` raised on the window root deterministically simulates an "outside the drawer rail" click — InputBindings/MouseBindings are NOT triggered by `RaiseEvent` (only by the real InputManager), so no tab toggles fire.
- Every committed change needs a **close + relaunch** of the running app to be seen — running
builds are live; verify can't run while the exe is locked.
## Next step
1. **Creator verifies the full TEST-tab chat path on the next run**: Test Stream → drawer
opens with a liveChatId; Mock Chat Input enabled ~as soon as the broadcast goes live;
send a message — it should now insert (200) and round-trip through the ~2s poll onto the
overlay. This completes the original creator report ("I still cannot post a chat message
in the TEST tab").
2. Push decision is the creator's (repo ~12 ahead). If pushed, note the commit id in this file.
1. **Creator verifies the drawer behavior on the next run**: open any of Stream Settings /
YPP / TEST and click anywhere outside the rail — the drawer closes (TEST was the only
one that didn't). Clicking the vertical tab again still toggles it.
2. Push decision is the creator's (repo ~13 ahead). If pushed, note the commit id in this file.
3. When scoped: TASK 3 item 20 (RewardEvents persistence half) draws next.
## Critical working rules (unchanged, still binding)