Browse Source

test(web): assembled session-log download e2e and refreshed golden

The navigation-panes replay scenario clicks the trajectory toolbar export
button and asserts the real host-streamed download: filename, a single
session.jsonl entry, and byte-verbatim seed content. The toolbar golden
refreshes for the localized export button.
_Kerman 1 month ago
parent
commit
2a34ca51a3

+ 2 - 1
apps/web/package.json

@@ -36,6 +36,7 @@
     "playwright": "^1.49.0",
     "typescript": "^6.0.3",
     "vite": "^6.0.0",
-    "vitest": "^4.1.8"
+    "vitest": "^4.1.8",
+    "fflate": "^0.8.2"
   }
 }

+ 18 - 0
apps/web/tests/navigation-panes.e2e.ts

@@ -11,6 +11,7 @@ import { fileURLToPath } from 'node:url'
 import { join } from 'node:path'
 import type { Browser, Page, Response } from 'playwright'
 import { chromium } from 'playwright'
+import { strFromU8, unzipSync } from 'fflate'
 import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, onTestFailed } from 'vitest'
 import { parseSessionLog } from '@deepseek-ai/dsh-llm-replay'
 import type { SessionEvent } from '@deepseek-ai/dsh-session'
@@ -274,6 +275,23 @@ describe('web e2e: navigation & panes over a rich seeded session', () => {
     await details.getByRole('button', { name: 'Close details' }).click()
   }, 60_000)
 
+  it.skipIf(MODE === 'record')('downloads the session-log ZIP from the trajectory toolbar', async () => {
+    onTestFailed(() => saveFailureShot(page, 'web-e2e-navigation-export'))
+    await ensureSeedOpen(page)
+    await page.getByRole('tab', { name: 'Trajectory' }).click()
+    const downloadPromise = page.waitForEvent('download', { timeout: 30_000 })
+    await page.getByRole('button', { name: 'Export session log' }).click()
+    const download = await downloadPromise
+    expect(download.suggestedFilename()).toMatch(/^dsh-session-.+\.zip$/)
+    // The real host streamed the ZIP; its root entry is the persisted log
+    // text verbatim (the assembled seam: real route, real persistence read).
+    const files = unzipSync(await readFile(await download.path()))
+    expect(Object.keys(files)).toEqual(['session.jsonl'])
+    const content = strFromU8(files['session.jsonl'] as Uint8Array)
+    expect(content.split('\n')[0]).toContain(SEED_ID)
+    expect(content).toContain('FIRST_DONE')
+  }, 60_000)
+
   it.skipIf(MODE === 'record')('focuses the ledger by dragging an overview interval', async () => {
     onTestFailed(() => saveFailureShot(page, 'web-e2e-navigation-timeline'))
     await ensureSeedOpen(page)

+ 1 - 1
apps/web/tests/snapshots/navigation-panes/trajectory.expected.md

@@ -2,7 +2,7 @@
   - button "Use actual duration": Duration
   - button "Collapse turns": Turns
   - button "Collapse calls": Calls
-  - button "导出会话日志": 导出
+  - button "Export session log": Export
   - img
   - searchbox "Search trajectory"
 - region "Trajectory timeline":

+ 3 - 0
pnpm-lock.yaml

@@ -364,6 +364,9 @@ importers:
       '@vitejs/plugin-react':
         specifier: ^4.0.0
         version: 4.7.0(vite@6.4.3(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.22.4)(yaml@2.9.0))
+      fflate:
+        specifier: ^0.8.2
+        version: 0.8.3
       playwright:
         specifier: ^1.49.0
         version: 1.61.1