import { createReadStream, existsSync } from 'node:fs' import { writeFile } from 'node:fs/promises' import { createServer } from 'node:http' import { extname, normalize, resolve } from 'node:path' import { firefox, webkit } from '../cnc_wams_gpt6/linuxcnc-master/web/node_modules/playwright/index.mjs' const root = resolve(import.meta.dirname, '..') const dist = resolve(root, 'dist') const reportPath = resolve(root, 'config/browser-matrix-verification.json') if (!existsSync(resolve(dist, 'index.html'))) throw new Error('Browser matrix requires a production build in dist/. Run npm run build first.') const contentTypes = { '.css': 'text/css; charset=utf-8', '.html': 'text/html; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.json': 'application/json; charset=utf-8', '.wasm': 'application/wasm', } const server = createServer((request, response) => { response.setHeader('Cross-Origin-Opener-Policy', 'same-origin') response.setHeader('Cross-Origin-Embedder-Policy', 'require-corp') response.setHeader('Cross-Origin-Resource-Policy', 'same-origin') const requestPath = decodeURIComponent((request.url || '/').split('?')[0]) const candidate = requestPath === '/' ? resolve(dist, 'index.html') : normalize(resolve(dist, `.${requestPath}`)) const file = candidate.startsWith(dist) && existsSync(candidate) ? candidate : resolve(dist, 'index.html') response.setHeader('Content-Type', contentTypes[extname(file)] || 'application/octet-stream') createReadStream(file).on('error', (error) => { response.writeHead(500); response.end(String(error)) }).pipe(response) }) await new Promise((resolveServer, rejectServer) => { server.once('error', rejectServer) server.listen(0, '127.0.0.1', resolveServer) }) const baseUrl = `http://127.0.0.1:${server.address().port}` const requested = process.argv.find((argument) => argument.startsWith('--browser='))?.slice('--browser='.length) const engines = requested ? requested.split(',') : ['firefox', 'webkit'] const browserTypes = { firefox, webkit } const executablePaths = { firefox: process.env.FIREFOX_BIN, webkit: process.env.WEBKIT_BIN } const results = [] try { for (const engine of engines) { const browserType = browserTypes[engine] if (!browserType) throw new Error(`Unsupported browser matrix engine: ${engine}`) const browser = await browserType.launch({ headless: true, ...(executablePaths[engine] ? { executablePath: executablePaths[engine] } : {}) }) const page = await browser.newPage({ viewport: { width: 1280, height: 900 } }) const pageErrors = [] page.on('pageerror', (error) => pageErrors.push(error.message)) try { await page.goto(`${baseUrl}/workspace/pump-housing`, { waitUntil: 'domcontentloaded', timeout: 60_000 }) await page.waitForSelector('.app-shell', { timeout: 30_000 }) await page.waitForFunction(() => document.querySelector('.three-viewport-host canvas')?.dataset.geometrySource === 'bitbybit-occt', undefined, { timeout: 120_000 }) const state = await page.evaluate(async () => { const canvas = document.querySelector('.three-viewport-host canvas') let opfs = false try { opfs = Boolean(await navigator.storage?.getDirectory?.()) } catch { opfs = false } return { userAgent: navigator.userAgent, crossOriginIsolated, sharedArrayBuffer: typeof SharedArrayBuffer === 'function', opfs, persistence: opfs ? 'opfs' : 'memory-fallback', shell: Boolean(document.querySelector('.app-shell')), workspace: Boolean(document.querySelector('.workspace-page')), workbench: document.querySelector('select[aria-label="Workbench"]')?.value || '', canvas: { width: canvas?.width || 0, height: canvas?.height || 0, geometrySource: canvas?.dataset.geometrySource || '', }, } }) const screenshot = await page.screenshot({ type: 'png' }) const persistencePass = engine === 'firefox' ? state.opfs && state.persistence === 'opfs' : ['opfs', 'memory-fallback'].includes(state.persistence) const pass = pageErrors.length === 0 && state.shell && state.workspace && state.crossOriginIsolated && state.sharedArrayBuffer && persistencePass && state.workbench === 'Part Design' && state.canvas.width >= 100 && state.canvas.height >= 100 && state.canvas.geometrySource === 'bitbybit-occt' && screenshot.byteLength > 10_000 results.push({ engine, status: pass ? 'pass' : 'fail', ...state, screenshotBytes: screenshot.byteLength, pageErrors }) } catch (error) { results.push({ engine, status: 'fail', pageErrors: [...pageErrors, error instanceof Error ? error.message : String(error)] }) } finally { await browser.close() } } } finally { await new Promise((resolveClose) => server.close(resolveClose)) } const report = { schemaVersion: 1, generatedAt: new Date().toISOString(), status: results.every((entry) => entry.status === 'pass') ? 'pass' : 'fail', browsers: results } await writeFile(reportPath, `${JSON.stringify(report, null, 2)}\n`) console.log(JSON.stringify(report, null, 2)) if (report.status !== 'pass') throw new Error('Non-Chrome browser matrix failed.')