Browse Source

fix(web): own the regression file in the host aggregate and type its exit assertion

The browser regression test was excluded from the client graph but never
included in tsconfig.host.json, so no TypeScript program type-checked it
and the lint type service analyzed it without a program — the real cause
of the earlier pre-commit lint failures. The file now joins the host
aggregate like every sibling host-plane web e2e.

The exit-path assertion is a typed discriminant filter over the session
log (the last plan/mode event must flip inactive), replacing the loose
serialized-string check; the type-only dsh-plan-mode import that was dead
in the excluded file now resolves the plan/mode SessionEventMap merge in
the host program. The golden records boolean facts only — viewport
membership and disjoint click areas — never font-dependent pixel values.
Chinesezjc 2 months ago
parent
commit
35105d516c
2 changed files with 13 additions and 0 deletions
  1. 12 0
      apps/web/tests/plan-control-row.e2e.ts
  2. 1 0
      tsconfig.host.json

+ 12 - 0
apps/web/tests/plan-control-row.e2e.ts

@@ -27,6 +27,10 @@ import { join } from 'node:path'
 import type { Browser, Page } from 'playwright'
 import { chromium } from 'playwright'
 import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
+// Type-only: pulls the plan/mode SessionEventMap merge so the discriminant
+// filter below types as the plan-mode event in the host aggregate.
+import type {} from '@deepseek-ai/dsh-plan-mode'
+import type { SessionEvent } from '@deepseek-ai/dsh-session'
 import {
   assertFixtureInventory, compareOrRefreshGolden, fixtureUserPrompts,
   launchWebScaffold, recordFixture, watchConsole, webSnapshotMode, type WebScaffold,
@@ -58,9 +62,11 @@ describe('web e2e: plan chip click area at the narrow viewport', () => {
   let browser: Browser
   let page: Page
   let tripwire: ReturnType<typeof watchConsole>
+  const sessionEvents: SessionEvent[] = []
 
   beforeAll(async () => {
     scaffold = await launchWebScaffold(MODE === 'record' ? {} : { replayFixture: FIXTURE, paceMs: 15 })
+    scaffold.ctx.on('session/event', (_session, event: SessionEvent) => { sessionEvents.push(event) })
     browser = await chromium.launch()
     page = await newEnglishPage(browser, VIEWPORT.height)
     tripwire = watchConsole(page)
@@ -138,6 +144,12 @@ describe('web e2e: plan chip click area at the narrow viewport', () => {
     // coordinate probe.
     await chip.click()
     await expect.poll(() => page.getByRole('button', { name: CHIP_ARIA }).count(), { timeout: 15_000 }).toBe(0)
+    // The click must have committed the exit: the last plan/mode event flips
+    // inactive (the recorded turn's entry event stays active:true earlier in
+    // the log, so the pair proves the exit and not just the entry).
+    const planModes = sessionEvents.filter(event => event.type === 'plan/mode')
+    const lastPlanMode = planModes.at(-1)
+    expect(JSON.stringify(lastPlanMode)).toContain('"active":false')
     expect(tripwire.pageErrors).toEqual([])
     expect(tripwire.warnings).toEqual([])
   }, 200_000)

+ 1 - 0
tsconfig.host.json

@@ -15,6 +15,7 @@
     "apps/web/tests/live-interactions.e2e.ts",
     "apps/web/tests/question-composer.e2e.ts",
     "apps/web/tests/approval-composer.e2e.ts",
+    "apps/web/tests/plan-control-row.e2e.ts",
     "apps/web/tests/plan-review.e2e.ts",
     "apps/web/tests/steering.e2e.ts",
     "apps/web/tests/navigation-panes.e2e.ts",