test(ci): stabilize attachment fixtures

This commit is contained in:
creatixchu committed 2026-09-03 21:25:45 +08:00
1 parent 0516edb037
commit de022388ea
3 files changed
+35 -20

No files matched your search

+6 -15
View File
@@ -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.
+14 -4
View File
@@ -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<string> {
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
@@ -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<Context, SessionController>()
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<AdmittedPromptContentPart[]> => {