import { test, expect } from '@playwright/test'; import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; // Form mode is hosted by the unified tables.html bundle — same bytes the // server returns for //form.html and //.yaml.html. Loading // tables/dist/tables.html via file:// (named form.html in the temp dir // so the URL pathname triggers form-mode in the dispatcher) is the // closest offline mirror of what online callers actually receive. const HTML_PATH = path.resolve('tables/dist/tables.html'); const HTML_RAW = fs.readFileSync(HTML_PATH, 'utf8'); const SAFETY_SCHEMA = { type: 'object', required: ['date', 'location'], additionalProperties: false, properties: { date: { type: 'string', format: 'date' }, location: { type: 'string', enum: ['Site A', 'Site B', 'Site C'] }, hazards: { type: 'array', items: { type: 'object', required: ['kind', 'severity'], properties: { kind: { type: 'string' }, severity: { type: 'integer', minimum: 1, maximum: 5 }, notes: { type: 'string' }, }, }, }, additionalNotes: { type: 'string' }, }, }; const SAFETY_UI = { location: { 'ui:widget': 'radio' }, hazards: { 'ui:options': { addable: true, removable: true } }, additionalNotes: { 'ui:widget': 'textarea' }, }; // Inject a complete form-context into the page before form bootstraps. // Writes a patched copy of form.html to a temp file and navigates via // file:// — page.setContent's about:blank origin doesn't expose // localStorage, which trips up shared/theme.js. page.route can't intercept // file://, so this is the cleanest path. The form is fully self-contained, // so the temp file works without relative-resource resolution. async function loadFormWithContext(page, context) { const ctxJson = JSON.stringify(context).replace(/<\//g, '<\\/'); const replacement = ``; const patched = HTML_RAW.replace( /