import {build} from 'esbuild'; import {chromium} from 'playwright'; import {createServer} from 'node:http'; import {once} from 'node:events'; import {readFile, writeFile} from 'node:fs/promises'; import {createHash} from 'node:crypto'; import {fileURLToPath} from 'node:url'; import assert from 'node:assert/strict'; const dir = fileURLToPath(new URL('.', import.meta.url)); const bundle = await build({entryPoints: [dir + 'fixture.jsx'], bundle: true, write: false, define: {'process.env.NODE_ENV': '"development"'}, format: 'iife'}); const server = createServer((req, res) => { if (req.url === '/bundle.js') { res.writeHead(200, {'Content-Type': 'application/javascript'}); res.end(bundle.outputFiles[0].contents); } else if (req.url === '/failure') { res.writeHead(503, {'Content-Type': 'text/plain'}); res.end('synthetic unavailable'); } else { res.writeHead(200, {'Content-Type': 'text/html'}); res.end('Boundary probe
'); } }); server.listen(0, '127.0.0.1'); await once(server, 'listening'); const url = `http://127.0.0.1:${server.address().port}`; let browser; const cases = []; async function probe(id, query, action) { const page = await browser.newPage(); page.setDefaultTimeout(5000); const browserErrors = []; page.on('pageerror', e => browserErrors.push(e.message)); try { await page.goto(url + query); await page.getByRole('button').first().waitFor(); const observed = await action(page); cases.push({id, status: 'pass', ...observed, browserErrors}); } catch (error) { cases.push({id, status: 'fail', error: error.message, observed: await page.evaluate(() => window.observed).catch(() => null), browserErrors}); } finally {await page.close();} } async function active(page, id) { try { await page.waitForFunction(id => document.activeElement?.id === id, id, {timeout: 5000}); } catch (error) { throw new Error(`Expected focus ${id}; got ${await page.evaluate(() => ({id: document.activeElement?.id, tag: document.activeElement?.tagName, dialogs: document.querySelectorAll('[role=dialog]').length})).then(JSON.stringify)}: ${error.message}`); } } try { browser = await chromium.launch({headless: true, ...(process.env.FEP_CHROMIUM_PATH ? {executablePath: process.env.FEP_CHROMIUM_PATH} : {})}); await probe('X01-render-contained', '/', async page => { await page.getByText('Break render', {exact: true}).click(); await page.getByTestId('inner').waitFor(); await page.getByText('Sibling 0', {exact: true}).click(); await page.getByText('Sibling 1', {exact: true}).waitFor(); const o = await page.evaluate(() => window.observed); assert(o.caught.some(x => x.boundary === 'inner' && x.message === 'render fault')); assert.equal(await page.getByTestId('outer').count(), 0); return {observed: o, siblingUsable: true}; }); await probe('X02-event-not-contained', '/', async page => { await page.getByText('Break event', {exact: true}).click(); await page.waitForFunction(() => window.observed.globalErrors.some(x => x.includes('event fault'))); const o = await page.evaluate(() => window.observed); assert.equal(o.caught.length, 0); assert.equal(await page.getByTestId('panel').count(), 1); return {observed: o, panelPresent: true}; }); await probe('X03-request-explicit-state', '/', async page => { await page.getByText('Fail request', {exact: true}).click(); await page.getByRole('status').filter({hasText: 'request failed'}).waitFor(); const o = await page.evaluate(() => window.observed); assert.deepEqual(o.requests, [503]); assert.equal(o.caught.length, 0); return {observed: o, state: 'request failed'}; }); await probe('X04-fallback-escapes-inner', '/?brokenFallback=1', async page => { await page.getByText('Break render', {exact: true}).click(); await page.getByTestId('outer').waitFor(); const o = await page.evaluate(() => window.observed); assert(o.caught.some(x => x.boundary === 'outer' && x.message === 'fallback fault')); return {observed: o, enclosingFallback: true}; }); await probe('X05-focus-existing-invoker', '/?mode=focus', async page => { await page.locator('#invoker').focus(); await page.keyboard.press('Enter'); await active(page, 'remove'); await page.keyboard.press('Escape'); await active(page, 'invoker'); return {activeId: await page.evaluate(() => document.activeElement.id)}; }); await probe('X06-focus-removed-default', '/?mode=focus', async page => { await page.locator('#invoker').focus(); await page.keyboard.press('Enter'); await active(page, 'remove'); await page.keyboard.press('Enter'); await page.getByRole('dialog').waitFor({state: 'detached'}); await page.waitForFunction(() => document.activeElement === document.body); // Negative control: default restoration cannot choose a workflow target for the removed trigger. assert.equal(await page.locator('#invoker').count(), 0); return {activeTag: await page.evaluate(() => document.activeElement.tagName), workflowTargetSelected: false}; }); await probe('X07-focus-removed-explicit', '/?mode=focus&explicit=1', async page => { await page.locator('#invoker').focus(); await page.keyboard.press('Enter'); await active(page, 'remove'); await page.keyboard.press('Enter'); await active(page, 'next'); return {activeId: await page.evaluate(() => document.activeElement.id), applicationPolicy: true}; }); await probe('X08-nested-return', '/?mode=focus', async page => { await page.locator('#invoker').focus(); await page.keyboard.press('Enter'); await active(page, 'remove'); await page.keyboard.press('Tab'); await active(page, 'nested-invoker'); await page.keyboard.press('Enter'); await active(page, 'nested-close'); // Experimental timing variable, not a hidden retry: compare immediate versus settled Escape. if (Number(process.env.FEP_NESTED_SETTLE_MS)) await page.waitForTimeout(Number(process.env.FEP_NESTED_SETTLE_MS)); await page.keyboard.press('Escape'); await active(page, 'nested-invoker'); assert.equal(await page.getByRole('dialog').count(), 1); await page.keyboard.press('Escape'); await active(page, 'invoker'); return {innerReturn: 'nested-invoker', outerReturn: 'invoker', observed: await page.evaluate(() => window.observed)}; }); const files = {}; for (const name of ['fixture.jsx', 'experiment.mjs', 'package.json', 'package-lock.json']) { files[name] = createHash('sha256').update(await readFile(dir + name)).digest('hex'); } const result = {date: new Date().toISOString(), evidence: 'synthetic-browser-fixture-real-libraries', node: process.version, browser: browser.version(), nestedSettleMs: Number(process.env.FEP_NESTED_SETTLE_MS || 0), browserSelection: process.env.FEP_CHROMIUM_PATH ? 'explicit executable override' : 'playwright bundled', packages: JSON.parse(await readFile(dir + 'package.json')).dependencies, files, cases, passed: cases.filter(x => x.status === 'pass').length, total: cases.length}; const output = process.env.FEP_RESULT_PATH || '/tmp/fep-boundary-result.json'; await writeFile(output, JSON.stringify(result, null, 2) + '\n'); console.log(JSON.stringify({output, passed: result.passed, total: result.total, cases}, null, 2)); if (result.passed !== result.total) process.exitCode = 1; } finally { if (browser) await browser.close(); await new Promise(resolve => server.close(resolve)); }