feat(session-export): add command and Header action
This commit is contained in:
@@ -283,14 +283,29 @@ 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 () => {
|
||||
it.skipIf(MODE === 'record')('downloads through the Session Header and /export with one dialog', async () => {
|
||||
onTestFailed(() => saveFailureShot(page, 'web-e2e-navigation-export'))
|
||||
await ensureSeedOpen(page)
|
||||
await page.getByRole('tab', { name: 'Trajectory' }).click()
|
||||
const exportButton = page.getByRole('button', { name: 'Session log' })
|
||||
expect(await exportButton.isDisabled()).toBe(false)
|
||||
const header = exportButton.locator('xpath=ancestor::header[1]')
|
||||
const [buttonBox, headerBox] = await Promise.all([
|
||||
exportButton.boundingBox(), header.boundingBox(),
|
||||
])
|
||||
if (buttonBox === null || headerBox === null) {
|
||||
throw new Error('Session Header export geometry is unavailable')
|
||||
}
|
||||
expect(headerBox.x + headerBox.width - (buttonBox.x + buttonBox.width)).toBeLessThanOrEqual(32)
|
||||
const responsePromise = page.waitForResponse(response =>
|
||||
new URL(response.url()).pathname === '/api/session.export', { timeout: 30_000 })
|
||||
const downloadPromise = page.waitForEvent('download', { timeout: 30_000 })
|
||||
await page.getByRole('button', { name: 'Export session log' }).click()
|
||||
await exportButton.click()
|
||||
const response = await responsePromise
|
||||
expect(response.status()).toBe(200)
|
||||
const download = await downloadPromise
|
||||
expect(download.suggestedFilename()).toMatch(/^dsh-session-.+\.zip$/)
|
||||
const dialog = page.getByRole('dialog', { name: 'Session download started' })
|
||||
await dialog.waitFor({ timeout: 30_000 })
|
||||
// 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()))
|
||||
@@ -298,6 +313,18 @@ describe('web e2e: navigation & panes over a rich seeded session', () => {
|
||||
const content = strFromU8(files['session.jsonl'] as Uint8Array)
|
||||
expect(content.split('\n')[0]).toContain(SEED_ID)
|
||||
expect(content).toContain('FIRST_DONE')
|
||||
await dialog.getByText('Close', { exact: true }).click()
|
||||
|
||||
const input = page.locator('textarea').first()
|
||||
const slashDownloadPromise = page.waitForEvent('download', { timeout: 30_000 })
|
||||
await input.fill('/export')
|
||||
await page.getByRole('option', { name: /export/u }).waitFor({ timeout: 10_000 })
|
||||
await input.press('Enter')
|
||||
const slashDownload = await slashDownloadPromise
|
||||
expect(slashDownload.suggestedFilename()).toBe(download.suggestedFilename())
|
||||
await page.getByRole('dialog', { name: 'Session download started' }).waitFor({ timeout: 30_000 })
|
||||
await page.getByRole('dialog', { name: 'Session download started' })
|
||||
.getByText('Close', { exact: true }).click()
|
||||
}, 60_000)
|
||||
|
||||
it.skipIf(MODE === 'record')('focuses the ledger by dragging an overview interval', async () => {
|
||||
|
||||
Reference in New Issue
Block a user