Преглед изворни кода

test(web): await restoration before workspace actions

Tianyi Cui пре 1 месец
родитељ
комит
43d00069a9

+ 2 - 2
.agents/notes/implemented/testing/2026-09-08-ci-completion-observations.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/testing/2026-09-08-ci-completion-observations.md
-2026-09-08-ci-completion-observations.md: ffdbf05e724f9eeb8ea85ee3d44c398a6fbf4f6a
-2026-09-08-ci-completion-observations.zh.md: dd071c7a7327b5ecaae588e3b32bfb39e85d89c1
+2026-09-08-ci-completion-observations.md: 51f31f40ebf0bdf1cd79297423200ae1f1a37c57
+2026-09-08-ci-completion-observations.zh.md: bcf4208c503a78d4b6a06055874cab4af196c0ef

+ 2 - 0
.agents/notes/implemented/testing/2026-09-08-ci-completion-observations.md

@@ -32,6 +32,8 @@ The [LSP backpressure test](../../../../packages/lsp/lsp-stdio/tests/instance.sp
 
 The [whole-queue steering test](../../../../apps/web/tests/steering.e2e.ts) waits for both enabled steering actions and the composer’s queue-steering hint before invoking the shortcut. Optimistic rows can already contain the expected text while the Host queue remains empty. A deferred model-stream barrier keeps question-composer takeover after steering; teardown releases that barrier before closing the browser.
 
+The [workspace-management test](../../../../apps/web/tests/workspace-management.e2e.ts) waits for restored composer focus before the next directory-dialog gesture, because workspace listing can render before Session restoration changes focus. Archive actions stay scoped to the seeded row rather than a captured fallback title that cold loading can replace; durable archive assertions retain the exact seed id.
+
 ### Built-client import classification
 
 The [master Windows run](https://github.com/deepseek-harness/deepseek-harness/actions/runs/34204779455/job/101996934534) also rejects the intentional CSS import exposed by `ui-dockkit`. The [Node import sweep](../../../../packages/experimental/webworker-runtime/tests/compile/transform-corpus-check.ts) admits that exact bundle only when Node reports `ERR_UNKNOWN_FILE_EXTENSION` for its `dockkit.module.css`. Other errors at the same entry still fail, and an entry that imports successfully reports a stale exemption. This preserves the import sweep without requiring a browser-only component library to load its stylesheet under bare Node.

+ 2 - 0
.agents/notes/implemented/testing/2026-09-08-ci-completion-observations.zh.md

@@ -32,6 +32,8 @@ Status: implemented
 
 [整队转向测试](../../../../apps/web/tests/steering.e2e.ts)在触发快捷键前等待两个已启用的转向操作,以及输入框的队列转向提示。Host 队列仍为空时,乐观行可能已经包含预期文本。延迟模型流屏障使问题输入框在转向后才接管;清理在关闭浏览器前释放该屏障。
 
+[工作区管理测试](../../../../apps/web/tests/workspace-management.e2e.ts)在下一次目录对话框操作前等待恢复后的输入框焦点,因为工作区列表可能早于 Session 恢复引发的焦点变化完成渲染。归档操作始终限定在种子行内,不依赖冷加载可能替换的已捕获备用标题;持久化归档断言保留精确种子 id。
+
 ### 已构建客户端的导入分类
 
 [master Windows 运行](https://github.com/deepseek-harness/deepseek-harness/actions/runs/34204779455/job/101996934534)还拒绝了 `ui-dockkit` 有意暴露的 CSS 导入。[Node 导入检查](../../../../packages/experimental/webworker-runtime/tests/compile/transform-corpus-check.ts)仅在 Node 针对其 `dockkit.module.css` 报告 `ERR_UNKNOWN_FILE_EXTENSION` 时,才允许这个精确的 bundle。相同入口的其他错误仍然失败,成功导入则报告豁免已过期。这保留了导入检查,同时不要求仅面向浏览器的组件库在裸 Node 中加载样式表。

+ 9 - 5
apps/web/tests/workspace-management.e2e.ts

@@ -121,7 +121,7 @@ describe('web e2e: workspace management (create / rename / flat view / hover aff
    * Reveal and click a row action, re-hovering if a projection update replaces
    * the row before its hover-only button becomes visible.
    */
-  async function clickHoverAction(row: Locator, name: string): Promise<void> {
+  async function clickHoverAction(row: Locator, name: string | RegExp): Promise<void> {
     const button = row.getByRole('button', { name })
     await expect.poll(async () => {
       await row.hover()
@@ -210,6 +210,10 @@ describe('web e2e: workspace management (create / rename / flat view / hover aff
     await page.waitForSelector('[class*="frame"]', { timeout: 30_000 })
     acknowledgeReloadConnectionLoss(tripwire, warningStart)
     await expect.poll(() => page.getByText('gamma-ws', { exact: true }).count(), { timeout: 15_000 }).toBeGreaterThanOrEqual(1)
+    // Session restoration focuses the composer; the Workspace list can arrive
+    // first. Do not let that focus cancel the next directory dialog's path draft.
+    const composer = page.locator('[data-composer-input][contenteditable="true"]')
+    await expect.poll(() => composer.evaluate(element => document.activeElement === element), { timeout: 10_000 }).toBe(true)
     expect(tripwire.pageErrors).toEqual([])
   }, 90_000)
 
@@ -596,14 +600,14 @@ describe('web e2e: workspace management (create / rename / flat view / hover aff
     await expect.poll(() => sessionRow.getAttribute('aria-selected'), { timeout: 10_000 }).toBe('true')
     const ungroupedSection = page.getByText('Ungrouped', { exact: true }).locator('..').locator('..').locator('..')
     await expect.poll(() => ungroupedSection.locator('[role="treeitem"]').count(), { timeout: 10_000 }).toBe(2)
-    const rowTitle = await sessionRow.locator('[class*="title"]').innerText()
     // Row menu: hover reveals the actions button; Archive session commits
     // without a confirmation dialog (non-destructive: log + accounting stay).
-    await clickHoverAction(sessionRow, `Session actions for ${rowTitle}`)
+    // Opening a cold Session can replace its fallback title after selection.
+    await clickHoverAction(sessionRow, /^Session actions for /)
     await page.getByRole('menuitem', { name: 'Archive session' }).click()
     // The row disappears on the archive-set echo; with no other visible
     // stray, the whole Ungrouped bucket withdraws.
-    await expect.poll(() => page.getByText(rowTitle, { exact: true }).count(), { timeout: 10_000 }).toBe(0)
+    await expect.poll(() => sessionRow.count(), { timeout: 10_000 }).toBe(0)
     await expect.poll(() => page.getByText('Ungrouped', { exact: true }).count(), { timeout: 10_000 }).toBe(0)
     // Durable on the host: the registry-global set carries the id while the
     // session log itself stays in persistence untouched.
@@ -618,7 +622,7 @@ describe('web e2e: workspace management (create / rename / flat view / hover aff
     // The archived row must not resurface (the Ungrouped bucket itself may
     // reappear if selection restore lands on another stray — not this test's
     // concern).
-    expect(await page.getByText(rowTitle, { exact: true }).count()).toBe(0)
+    expect(await sessionRow.count()).toBe(0)
     expect(tripwire.pageErrors).toEqual([])
   }, 90_000)