Ver código fonte

fix(webworker): preserve preview loading sequence

imccyu 1 mês atrás
pai
commit
4f80422595

+ 5 - 4
apps/web/tests/preview-boot.e2e.ts

@@ -283,10 +283,10 @@ async function bootPreview(origin: string, browser: Browser): Promise<void> {
   })
   try {
     await page.goto(`${origin}/preview.html`, { waitUntil: 'domcontentloaded' })
-    await page.getByRole('heading', { name: '选择 Preview 数据源' }).waitFor()
+    await page.getByRole('heading', { name: 'Choose Preview data' }).waitFor()
     expect(await page.locator('input[name="preview-source"][value="vfs-example"]').isChecked()).toBe(true)
-    expect(await page.getByText('空白环境', { exact: true }).count()).toBe(1)
-    expect(await page.getByText('WebFS 目录', { exact: true }).count()).toBe(1)
+    expect(await page.getByText('Empty environment', { exact: true }).count()).toBe(1)
+    expect(await page.getByText('WebFS directory', { exact: true }).count()).toBe(1)
     expect(await page.locator('input[name="preview-source"][value="webfs"]').isDisabled()).toBe(true)
     expect(await page.getByRole('textbox', { name: 'Choose workspace' }).count()).toBe(0)
     await compareOrRefreshGolden(
@@ -294,7 +294,8 @@ async function bootPreview(origin: string, browser: Browser): Promise<void> {
       await captureStableAria(page, '[data-preview-source-card]', '/__preview_no_workspace__'),
       SNAPSHOT_MODE,
     )
-    await page.getByRole('button', { name: '启动 Preview' }).click()
+    await page.getByRole('button', { name: 'Start Preview' }).click()
+    await page.getByText('Loading plugins…', { exact: true }).waitFor({ timeout: 10_000 })
     const bootLine = await within(treeActive, BOOT_TIMEOUT_MS, `preview boot: the worker never reported "${TREE_ACTIVE}"`)
     // The activated tree ran bodies lowered against the contract this
     // checkout's packer emits; a dist built before a contract change would

+ 15 - 15
apps/web/tests/snapshots/preview-boot/source-chooser.expected.md

@@ -1,15 +1,15 @@
-- form "选择 Preview 数据源":
-  - heading "选择 Preview 数据源" [level=1]
-  - paragraph: 数据会在 Worker 和应用启动前挂载;刷新页面可重新选择。
-  - group "文件系统来源":
-    - text: 文件系统来源
-    - radio "空白环境 只加载基础运行时,用于验证首次启动与新建 Workspace。"
-    - strong: 空白环境
-    - text: 只加载基础运行时,用于验证首次启动与新建 Workspace。
-    - radio "内置综合示例 示例 Workspace、工具卡、子代理与分页会话。" [checked]
-    - strong: 内置综合示例
-    - text: 示例 Workspace、工具卡、子代理与分页会话。
-    - radio "WebFS 目录 需要用户授权的目录来源,将在 WebFS provider 接入后开放。" [disabled]
-    - strong: WebFS 目录
-    - text: 需要用户授权的目录来源,将在 WebFS provider 接入后开放。
-  - button "启动 Preview"
+- form "Choose Preview data":
+  - heading "Choose Preview data" [level=1]
+  - paragraph: Data mounts before the Worker and application start. Refresh to choose again.
+  - group "Filesystem source":
+    - text: Filesystem source
+    - radio "Empty environment Load only the base runtime to verify first launch and workspace creation."
+    - strong: Empty environment
+    - text: Load only the base runtime to verify first launch and workspace creation.
+    - radio "Built-in showcase Sample workspace, tool cards, subagents, and paged history." [checked]
+    - strong: Built-in showcase
+    - text: Sample workspace, tool cards, subagents, and paged history.
+    - radio "WebFS directory Requires directory access and will be available after the WebFS provider lands." [disabled]
+    - strong: WebFS directory
+    - text: Requires directory access and will be available after the WebFS provider lands.
+  - button "Start Preview"

+ 2 - 2
packages/experimental/webworker-packer/src/repository.ts

@@ -156,8 +156,8 @@ export function previewFixtures(repoRoot: string): PreviewFixture[] {
   const root = join(repoRoot, PREVIEW_EXAMPLE_ROOT)
   return [{
     id: 'vfs-example',
-    label: '内置综合示例',
-    description: '示例 Workspace、工具卡、子代理与分页会话。',
+    label: 'Built-in showcase',
+    description: 'Sample workspace, tool cards, subagents, and paged history.',
     trees: ['home', 'workspace'].map(mount => ({ mount, directory: join(root, mount) })),
   }]
 }

+ 15 - 15
packages/experimental/webworker-runtime/src/client/source-chooser.ts

@@ -31,7 +31,6 @@ const CHOOSER_STYLE = `
     font-size: 14px;
     line-height: 22px;
   }
-  #root:has(> [data-preview-source-chooser]) > [data-dsh-boot] { display: none; }
   [data-preview-source-card] {
     width: min(600px, 100%);
     max-height: calc(100dvh - 48px);
@@ -206,15 +205,15 @@ export async function choosePreviewSource(manifestUrl: URL): Promise<readonly UR
   const choices: PreviewSourceChoice[] = [
     {
       id: EMPTY_SOURCE,
-      label: '空白环境',
-      description: '只加载基础运行时,用于验证首次启动与新建 Workspace。',
+      label: 'Empty environment',
+      description: 'Load only the base runtime to verify first launch and workspace creation.',
       overlays: [],
     },
     ...fixtureChoices(manifest.fixtures, manifestUrl),
     {
       id: WEBFS_SOURCE,
-      label: 'WebFS 目录',
-      description: '需要用户授权的目录来源,将在 WebFS provider 接入后开放。',
+      label: 'WebFS directory',
+      description: 'Requires directory access and will be available after the WebFS provider lands.',
       overlays: [],
       disabled: true,
     },
@@ -235,18 +234,19 @@ export async function choosePreviewSource(manifestUrl: URL): Promise<readonly UR
   style.textContent = CHOOSER_STYLE
   document.head.append(style)
 
-  root.innerHTML = `<main data-preview-source-chooser>
-    <form data-preview-source-card aria-labelledby="preview-source-title">
-      <h1 id="preview-source-title">选择 Preview 数据源</h1>
-      <p>数据会在 Worker 和应用启动前挂载;刷新页面可重新选择。</p>
+  const chooser = document.createElement('main')
+  chooser.dataset.previewSourceChooser = ''
+  chooser.innerHTML = `<form data-preview-source-card aria-labelledby="preview-source-title">
+      <h1 id="preview-source-title">Choose Preview data</h1>
+      <p>Data mounts before the Worker and application start. Refresh to choose again.</p>
       <fieldset>
-        <legend>文件系统来源</legend>
+        <legend>Filesystem source</legend>
         ${choices.map(choice => optionMarkup(choice, selected)).join('')}
       </fieldset>
-      <button data-preview-source-submit type="submit">启动 Preview</button>
-    </form>
-  </main>`
-  const form = root.querySelector<HTMLFormElement>('[data-preview-source-card]')
+      <button data-preview-source-submit type="submit">Start Preview</button>
+    </form>`
+  root.prepend(chooser)
+  const form = chooser.querySelector<HTMLFormElement>('[data-preview-source-card]')
   if (form === null) throw new Error('preview source chooser: form was not rendered')
   const sourceId = await new Promise<string>((resolve, reject) => {
     form.addEventListener('submit', (event) => {
@@ -258,7 +258,7 @@ export async function choosePreviewSource(manifestUrl: URL): Promise<readonly UR
   })
   const choice = choices.find(candidate => candidate.id === sourceId && candidate.disabled !== true)
   if (choice === undefined) throw new Error(`preview source chooser: unavailable source "${sourceId}"`)
-  root.replaceChildren()
+  chooser.remove()
   style.remove()
   return choice.overlays
 }

+ 10 - 2
packages/experimental/webworker-runtime/tests/client/source-chooser.spec.ts

@@ -80,6 +80,11 @@ describe('Preview source chooser', () => {
 
   it('shows the chooser only when the query is absent and returns its default selection', async () => {
     installManifest()
+    const root = document.getElementById('root')
+    if (root === null) throw new Error('test root is missing')
+    const bootPage = document.createElement('div')
+    bootPage.dataset.dshBoot = ''
+    root.append(bootPage)
 
     const selected = choosePreviewSource(MANIFEST_URL)
     await vi.waitFor(() => {
@@ -97,7 +102,9 @@ describe('Preview source chooser', () => {
       new URL('https://preview.test/preview/fixtures/base.tar.gz'),
       new URL('https://preview.test/preview/fixtures/tail.tar.gz'),
     ])
-    expect(document.getElementById('root')?.childElementCount).toBe(0)
+    expect(root.contains(bootPage)).toBe(true)
+    expect(root.childElementCount).toBe(1)
+    expect(document.querySelector('[data-preview-source-chooser]')).toBeNull()
     expect(document.querySelector('[data-preview-source-style]')).toBeNull()
   })
 
@@ -124,8 +131,9 @@ describe('Preview source chooser', () => {
     document.body.innerHTML = '<div id="root"></div>'
     const root = document.getElementById('root')
     if (root === null) throw new Error('test root is missing')
-    vi.spyOn(root, 'querySelector').mockReturnValueOnce(null)
+    const querySelector = vi.spyOn(HTMLElement.prototype, 'querySelector').mockReturnValueOnce(null)
     await expect(choosePreviewSource(MANIFEST_URL)).rejects.toThrow(/form was not rendered/)
+    querySelector.mockRestore()
 
     document.head.replaceChildren()
     document.body.innerHTML = '<div id="root"></div>'