Files
deepseek-harness/apps/web/tests/support.ts
T

48 lines
1.9 KiB
TypeScript

// Shared plumbing for the web smoke tests (dist location, free port, failure shots).
import { existsSync, mkdirSync } from 'node:fs'
import { createServer } from 'node:net'
import { fileURLToPath } from 'node:url'
import type { Page } from 'playwright'
/** The built page under test; `pnpm run test:web` rebuilds it before running. */
export const DIST_INDEX = fileURLToPath(new URL('../dist/index.html', import.meta.url))
export const REPO_ROOT = fileURLToPath(new URL('../../..', import.meta.url))
/** Fail loud on a stale checkout instead of testing yesterday's bundle. */
export function requireDist(): void {
if (!existsSync(DIST_INDEX)) {
throw new Error('web app dist not built — run `pnpm --filter @deepseek-ai/dsh-frontend build` (pnpm run test:web does this first)')
}
}
/**
* OS-assigned free port, released before use. startWebServer echoes
* options.port instead of the bound one, so passing 0 directly is unusable.
*/
export function probeFreePort(): Promise<number> {
return new Promise((resolvePort, reject) => {
const probe = createServer()
probe.once('error', reject)
probe.listen(0, '127.0.0.1', () => {
const address = probe.address()
if (address === null || typeof address === 'string') {
probe.close(() => { reject(new Error('port probe returned no address')) })
return
}
probe.close(() => { resolvePort(address.port) })
})
})
}
/** Failure evidence goes to the gitignored .artifacts/ (repo convention). */
export async function saveFailureShot(page: Page, name: string): Promise<void> {
const dir = fileURLToPath(new URL('../../../.artifacts', import.meta.url))
mkdirSync(dir, { recursive: true })
try {
await page.screenshot({ path: `${dir}/${name}.png`, fullPage: true })
} catch {
// Best-effort evidence: a dead page/browser at failure time must not mask the real assertion error.
}
}