diff --git a/apps/web/tests/agent-preset-authoring.e2e.ts b/apps/web/tests/agent-preset-authoring.e2e.ts index 72b8317fe4..4a3570e1ee 100644 --- a/apps/web/tests/agent-preset-authoring.e2e.ts +++ b/apps/web/tests/agent-preset-authoring.e2e.ts @@ -44,17 +44,6 @@ describe('web e2e: agent-preset authoring is a host-side copy', () => { return page.getByRole('dialog', { name: '设置' }) } - /** Tokenize the lane-owned preset root after general aria normalization. */ - function withPresetRoot(snapshot: string): string { - const rootSuffix = `/${userRoot.split('/').pop()!}` - return snapshot.split('\n').map((line) => { - const rootStart = line.indexOf(rootSuffix) - if (rootStart === -1) return line - const pathStart = line.lastIndexOf(' ', rootStart) + 1 - return `${line.slice(0, pathStart)}{{presetRoot}}${line.slice(rootStart + rootSuffix.length)}` - }).join('\n') - } - beforeAll(async () => { userRoot = await realpath(await mkdtemp(join(tmpdir(), 'dsh-web-e2e-presets-'))) scaffold = await launchWebScaffold({ @@ -146,8 +135,9 @@ describe('web e2e: agent-preset authoring is a host-side copy', () => { // The copy dialog is detached, so the settings dialog is the only one // left (it names itself via aria-labelledby, which a CSS attribute // selector cannot address). - const snapshot = withPresetRoot( - await captureStableAria(page, '[role="dialog"]', scaffold.workspaceCwd)) + const snapshot = await captureStableAria(page, '[role="dialog"]', scaffold.workspaceCwd, { + replacements: [[userRoot, '{{presetRoot}}']], + }) await compareOrRefreshGolden(CREATED_EXPECTED, snapshot, MODE) expect(snapshot).toContain('{{presetRoot}}/my-agent') @@ -196,8 +186,9 @@ describe('web e2e: agent-preset authoring is a host-side copy', () => { await dialog.getByRole('button', { name: 'Agent 预设' }).click() await dialog.getByText('加载失败').first().waitFor({ timeout: 10_000 }) - const snapshot = withPresetRoot( - await captureStableAria(page, '[role="dialog"]', scaffold.workspaceCwd)) + const snapshot = await captureStableAria(page, '[role="dialog"]', scaffold.workspaceCwd, { + replacements: [[userRoot, '{{presetRoot}}']], + }) await compareOrRefreshGolden(DAMAGED_EXPECTED, snapshot, MODE) // Both damage shapes surface as marked, unselectable, uncopyable cards // that still carry their metadata and the discovery-reported reason. diff --git a/apps/web/tests/scaffold.ts b/apps/web/tests/scaffold.ts index d53e18f5ee..46bd878dea 100644 --- a/apps/web/tests/scaffold.ts +++ b/apps/web/tests/scaffold.ts @@ -1177,20 +1177,30 @@ function normalizeAria(snapshot: string, workspaceCwd: string, age: boolean): st * @param selector - the region locator selector. * @param workspaceCwd - normalization input. * @param options - `normalizeAge` collapses relative-time buckets to `{{age}}` - * for a region whose rows are dated from live wall-clock state. + * for a region whose rows are dated from live wall-clock state; + * `replacements` tokenizes scenario-owned values before generic normalization. * @returns the stable normalized snapshot. */ export async function captureStableAria( page: Page, selector: string, workspaceCwd: string, - options: { normalizeAge?: boolean } = {}, + options: { + normalizeAge?: boolean + replacements?: readonly (readonly [value: string, token: string])[] + } = {}, ): Promise { const region = page.locator(selector).first() const age = options.normalizeAge === true - let previous = normalizeAria(await region.ariaSnapshot(), workspaceCwd, age) + const normalize = (snapshot: string): string => { + for (const [value, token] of options.replacements ?? []) { + snapshot = snapshot.split(value).join(token) + } + return normalizeAria(snapshot, workspaceCwd, age) + } + let previous = normalize(await region.ariaSnapshot()) await expect.poll(async () => { - const current = normalizeAria(await region.ariaSnapshot(), workspaceCwd, age) + const current = normalize(await region.ariaSnapshot()) const stable = current === previous previous = current return stable diff --git a/packages/api/session-controller/tests/test-remote.ts b/packages/api/session-controller/tests/test-remote.ts index f48527161f..9bc52b3a5c 100644 --- a/packages/api/session-controller/tests/test-remote.ts +++ b/packages/api/session-controller/tests/test-remote.ts @@ -3,7 +3,11 @@ import { SessionLogOffset } from '@deepseek-ai/dsh-session' import type { Context } from '@deepseek-ai/cordis' import type { ModelSelection as AgentModelSelection } from '@deepseek-ai/dsh-agent' -import type { AdmittedPromptContentPart, AttachmentAdmissionPart } from '@deepseek-ai/dsh-attachment' +import type { + AdmittedPromptContentPart, + AttachmentAdmissionPart, + ImageAttachmentLimits, +} from '@deepseek-ai/dsh-attachment' import type { SessionEvent, SessionHeader, SessionId } from '@deepseek-ai/dsh-session' import { SessionPersistenceNotFoundError, @@ -94,6 +98,15 @@ export interface TestSessionRemoteDefaults { const installed = new WeakMap() +const TEST_IMAGE_LIMITS: ImageAttachmentLimits = Object.freeze({ + maxImageBytes: 5 * 1024 * 1024, + maxImagesPerMessage: 20, + maxMessageImageBytes: 100 * 1024 * 1024, + maxImagePixels: 40_000_000, + maxImageDimension: 2000, + mediaTypes: Object.freeze(['image/png'] as const), +}) + /** Compact header-and-events point read a persistence double declares per session. */ interface TestSessionInspection { readonly meta: SessionHeader @@ -239,6 +252,7 @@ function installControllers( } if (ctx.get('attachments') === undefined) { ctx.provide('attachments', { + imageLimits: TEST_IMAGE_LIMITS, admitPromptContent: async ( content: readonly AttachmentAdmissionPart[], ): Promise => {