|
|
@@ -11,7 +11,7 @@ import { fileURLToPath } from 'node:url'
|
|
|
import { join } from 'node:path'
|
|
|
import type { Browser, Page } from 'playwright'
|
|
|
import { chromium } from 'playwright'
|
|
|
-import { afterEach, describe, expect, it, onTestFailed } from 'vitest'
|
|
|
+import { afterEach, describe, expect, it, onTestFailed, onTestFinished } from 'vitest'
|
|
|
import { deriveReplayScript, parseSessionLog, type ReplayEntry } from '@deepseek-ai/dsh-llm-replay'
|
|
|
import type { SessionEvent } from '@deepseek-ai/dsh-session'
|
|
|
import {
|
|
|
@@ -97,18 +97,50 @@ describe('web e2e: queued image submission', () => {
|
|
|
await page.locator('[data-composer-input][contenteditable="true"]').first().waitFor({ timeout: 10_000 })
|
|
|
await pasteImage(page, await readFile(PNG))
|
|
|
await page.getByRole('img', { name: 'queued.png' }).waitFor({ timeout: 10_000 })
|
|
|
- await input.fill(QUEUED_TEXT)
|
|
|
- await input.press('Enter')
|
|
|
-
|
|
|
- // Admission replaces the local preview; the durable row loads its own thumbnail.
|
|
|
- await page.getByRole('button', { name: 'Remove queued message', disabled: false }).waitFor({ timeout: 15_000 })
|
|
|
- const dockThumb = page.locator('[data-queue-dock] li:not([data-submission-echo]) img[alt="Queued message image"]')
|
|
|
- await dockThumb.waitFor({ timeout: 15_000 })
|
|
|
- await expect.poll(() => dockThumb.getAttribute('src')).toMatch(/^blob:/)
|
|
|
- await expect.poll(() => dockThumb.evaluate((image: HTMLImageElement) => image.complete && image.naturalWidth > 0)).toBe(true)
|
|
|
- await page.getByText(QUEUED_TEXT, { exact: true }).waitFor()
|
|
|
- const queuedSnapshot = await captureStableAria(page, '[class*="centerCol"]', scaffold.workspaceCwd)
|
|
|
- await compareOrRefreshGolden(QUEUED_EXPECTED, queuedSnapshot, MODE)
|
|
|
+ const releasePrompt = Promise.withResolvers<undefined>()
|
|
|
+ const releaseImage = Promise.withResolvers<undefined>()
|
|
|
+ onTestFinished(async () => {
|
|
|
+ releasePrompt.resolve(undefined)
|
|
|
+ releaseImage.resolve(undefined)
|
|
|
+ // afterEach closes this test's browser before onTestFinished runs.
|
|
|
+ if (!page.isClosed()) await page.unrouteAll({ behavior: 'wait' })
|
|
|
+ }, 15_000)
|
|
|
+ let imageRequested = false
|
|
|
+ await page.route('**/api/session/prompt', async (route) => {
|
|
|
+ await releasePrompt.promise
|
|
|
+ await route.continue()
|
|
|
+ }, { times: 1 })
|
|
|
+ await page.route('**/api/session/attachment', async (route) => {
|
|
|
+ imageRequested = true
|
|
|
+ await releaseImage.promise
|
|
|
+ await route.continue()
|
|
|
+ }, { times: 1 })
|
|
|
+ const dockThumb = page.locator('[data-queue-dock] img[alt="Queued message image"]')
|
|
|
+ try {
|
|
|
+ await input.fill(QUEUED_TEXT)
|
|
|
+ await input.press('Enter')
|
|
|
+ await dockThumb.waitFor({ timeout: 15_000 })
|
|
|
+ await expect.poll(() => dockThumb.getAttribute('src'), { timeout: 15_000 }).toMatch(/^blob:/)
|
|
|
+ expect(await page.locator('[data-queue-dock] [data-submission-echo]').count()).toBe(1)
|
|
|
+ releasePrompt.resolve(undefined)
|
|
|
+ await expect.poll(() => imageRequested, { timeout: 15_000 }).toBe(true)
|
|
|
+ await page.getByText(QUEUED_TEXT, { exact: true }).waitFor()
|
|
|
+ await page.getByRole('button', { name: 'Remove queued message', disabled: false }).waitFor({ timeout: 15_000 })
|
|
|
+ expect(await page.locator('[data-queue-dock] [data-submission-echo]').count()).toBe(0)
|
|
|
+ expect(await dockThumb.count()).toBe(0)
|
|
|
+ releaseImage.resolve(undefined)
|
|
|
+ // Admission replaces the optimistic image; wait for the durable row's own thumbnail.
|
|
|
+ const durableThumb = page.locator('[data-queue-dock] li:not([data-submission-echo]) img[alt="Queued message image"]')
|
|
|
+ await durableThumb.waitFor({ timeout: 15_000 })
|
|
|
+ await expect.poll(() => durableThumb.getAttribute('src'), { timeout: 15_000 }).toMatch(/^blob:/)
|
|
|
+ await expect.poll(() => durableThumb.evaluate((image: HTMLImageElement) => image.complete && image.naturalWidth > 0)).toBe(true)
|
|
|
+ const queuedSnapshot = await captureStableAria(page, '[class*="centerCol"]', scaffold.workspaceCwd)
|
|
|
+ await compareOrRefreshGolden(QUEUED_EXPECTED, queuedSnapshot, MODE)
|
|
|
+ } finally {
|
|
|
+ releasePrompt.resolve(undefined)
|
|
|
+ releaseImage.resolve(undefined)
|
|
|
+ await page.unrouteAll({ behavior: 'wait' })
|
|
|
+ }
|
|
|
|
|
|
// Stop parks the accepted queue; the next waking send delivers the image
|
|
|
// message first (FIFO), then its own text as the following turn.
|