feat(SPA): media page T0

This commit is contained in:
fsylvain committed 2026-09-30 19:45:47 +02:00
1 parent 0d99544579
commit 67d456baf6
32 files changed
+4874 -40

No files matched your search

+29 -9
View File
@@ -82,6 +82,12 @@ function parseArgs(argv: string[]) {
return { screen, state: stateWords.join(' ') || undefined, viewports, themes }
}
function tableStates(markdown: string) {
return [...markdown.matchAll(/^\| ([^|]+?) \|/gm)]
.map((match) => match[1]!.trim())
.filter((cell) => cell !== 'State' && !/^-+$/.test(cell))
}
async function readScreen(name: string): Promise<Screen> {
const markdown = await readFile(join(designRoot, 'screens', `${name}.md`), 'utf8')
const reference = /\*\*Visual reference:\*\* `source\/([^`]+)`, group "([^"]+)"/.exec(markdown)
@@ -90,7 +96,10 @@ async function readScreen(name: string): Promise<Screen> {
}
const statesStart = markdown.search(/^## States$/m)
const statesMarkdown = statesStart === -1 ? markdown : markdown.slice(statesStart)
const states = [...statesMarkdown.matchAll(/^### (.+)$/gm)].map((match) => match[1]!.trim())
const headingStates = [...statesMarkdown.matchAll(/^### (.+)$/gm)].map((match) =>
match[1]!.trim(),
)
const states = headingStates.length ? headingStates : tableStates(statesMarkdown)
return { name, board: reference[1]!, group: reference[2]!, states }
}
@@ -296,14 +305,14 @@ async function captureDesign(
await frame.screenshot({ path, animations: 'disabled' })
}
function fulfilment(mock: ApiMock) {
const text = typeof mock.body === 'string'
function fulfilment(mock: ApiMock, url: URL, call: number) {
const body = mock.bodyFor ? mock.bodyFor(url, call) : mock.body
const text = typeof body === 'string'
return {
status: mock.status,
headers: mock.headers,
contentType: text ? 'text/plain; charset=utf-8' : 'application/json',
body:
mock.body === undefined ? '' : text ? (mock.body as string) : JSON.stringify(mock.body),
body: body === undefined ? '' : text ? (body as string) : JSON.stringify(body),
}
}
@@ -324,9 +333,13 @@ async function captureApp(
})
await serveIconsLocally(context)
const unmocked = new Set<string>()
const calls = new Map<string, number>()
await context.route('**/v1/**', async (route) => {
const request = route.request()
const key = `${request.method()} ${new URL(request.url()).pathname}`
const url = new URL(request.url())
const key = `${request.method()} ${url.pathname}`
const call = (calls.get(key) ?? 0) + 1
calls.set(key, call)
const mock = setup.api?.[key]
if (!mock) {
unmocked.add(key)
@@ -336,7 +349,7 @@ async function captureApp(
if (mock.hang) {
return
}
await route.fulfill(fulfilment(mock))
await route.fulfill(fulfilment(mock, url, call))
})
if (setup.origin) {
await serveDistLocally(context, setup.origin)
@@ -440,6 +453,7 @@ async function main() {
const lines: string[] = []
let failures = 0
let skipped = 0
let pending = 0
try {
for (const viewport of args.viewports) {
@@ -447,6 +461,11 @@ async function main() {
const board = await openBoard(browser, design.origin, screen, theme)
for (const state of states) {
const skip = setups[state]?.skip
if (skip === 'pending') {
pending++
lines.push(`PENDING ${state} · ${viewport} · ${theme}: slice B2`)
continue
}
if (skip) {
skipped++
lines.push(`SKIP ${state} · ${viewport} · ${theme}: ${skip}`)
@@ -508,8 +527,9 @@ async function main() {
design.close()
}
const checked = lines.length - skipped
const skippedNote = skipped ? `, ${skipped} skipped` : ''
const checked = lines.length - skipped - pending
const skippedNote =
(skipped ? `, ${skipped} skipped` : '') + (pending ? `, ${pending} pending` : '')
const verdict = failures
? `FAIL: ${failures} of ${checked} captures failed (budget ${maxDiffPercent}% per capture${skippedNote})`
: `PASS: all ${checked} captures within budget (${maxDiffPercent}% unless an approved exception is listed${skippedNote})`