import { createRequire } from 'node:module'; import { mkdir, writeFile, readFile } from 'node:fs/promises'; import assert from 'node:assert/strict'; const require = createRequire(import.meta.url); const { chromium } = require(process.env.PLAYWRIGHT_MODULE || 'playwright'); const browser = await chromium.launch({ channel: 'msedge', headless: true }); const context = await browser.newContext({ viewport: { width: 1600, height: 900 } }); const page = await context.newPage(), errors = [], results = {}; page.on('pageerror', error => errors.push(String(error))); page.on('console', message => { if (message.type() === 'error' && /THREE|shader|WebGL/.test(message.text())) errors.push(message.text()); }); const origin = process.env.REVIEW_URL || 'http://127.0.0.1:5188'; await mkdir('verification/web-integration', { recursive: true }); const stats = () => page.evaluate(() => window.rhine.stats()); const close = async () => { await page.locator('[data-action="close-modal"]').click(); await page.waitForTimeout(350); }; try { await page.goto(origin); await page.waitForFunction(() => window.rhine?.stats().ready); assert.equal((await stats()).startup, 'waiting'); assert.equal(await page.locator('.entry-start').isVisible(), true); await page.locator('.entry-start').click(); await page.waitForFunction(() => window.rhine.stats().startup === 'started'); await page.evaluate(() => window.rhine.archive()); await page.waitForTimeout(1000); assert.equal((await stats()).wallpaper, null); assert.equal(await page.locator('.three-toggle').count(), 0); assert.equal(await page.evaluate(() => typeof window.wallpaperPropertyListener), 'undefined'); await page.locator('[data-action="settings"]').click(); await page.locator('[data-color-theme="dark"]').click(); await page.locator('[data-pref="superPerformance"]').check(); await page.waitForTimeout(2000); results.fast = await stats(); assert.equal(results.fast.superPerformance, true); assert.equal(results.fast.motion.reduced, false); const savedQuality = await page.evaluate(() => JSON.parse(localStorage.getItem('rhine-settings')).rendering); await close(); await page.screenshot({ path: 'verification/web-integration/dark-fast.png' }); await page.reload(); await page.waitForFunction(() => window.rhine?.stats().ready); await page.locator('.entry-start').click(); await page.waitForFunction(() => window.rhine.stats().startup === 'started'); await page.evaluate(() => window.rhine.archive()); await page.waitForTimeout(800); assert.equal((await stats()).superPerformance, true); assert.equal(await page.evaluate(() => document.documentElement.dataset.darkSurface), 'true'); await page.locator('[data-action="settings"]').click(); await page.locator('[data-pref="superPerformance"]').uncheck(); await close(); await page.waitForTimeout(1000); assert.equal((await stats()).superPerformance, false); assert.deepEqual(await page.evaluate(() => JSON.parse(localStorage.getItem('rhine-settings')).rendering), savedQuality); const before = (await stats()).selected; await page.keyboard.press('ArrowRight'); await page.waitForTimeout(700); assert.notEqual((await stats()).selected, before); const moving = await page.evaluate(async () => { for (let i = 0; i < 30; i++) { if (document.querySelector('#clock').getAnimations({ subtree: true }).some(a => a.playState === 'running')) return true; await new Promise(r => setTimeout(r, 60)); } return false; }); assert.equal(moving, true); await page.evaluate(() => window.rhine.detail()); await page.waitForTimeout(2800); assert.equal((await stats()).mode, 'detail'); await page.screenshot({ path: 'verification/web-integration/dark-detail.png' }); await page.locator('[data-action="model-viewer"]').click(); await page.waitForTimeout(1600); assert.ok(await page.locator('canvas').count() >= 2); await page.keyboard.press('Escape'); await page.waitForTimeout(400); results.viewports = []; for (const [width, height] of [[2560,1080],[390,844],[844,390]]) { await page.setViewportSize({ width, height }); await page.waitForTimeout(600); assert.equal((await stats()).ready, true); const overflow = await page.evaluate(() => document.documentElement.scrollWidth > innerWidth + 1); assert.equal(overflow, false); results.viewports.push({ width, height, overflow }); } await page.screenshot({ path: 'verification/web-integration/mobile-landscape.png' }); const html = await readFile('dist/index.html', 'utf8'); assert.match(html, /rel="manifest"/); assert.doesNotMatch(html, /wallpaperPropertyListener/); const worker = await readFile('dist/sw.js', 'utf8'); assert.ok(worker.length > 1000); assert.deepEqual(errors, []); results.startupGesture = results.persistedPerformance = results.restoredQuality = results.numericMotion = results.viewer = results.pwaBuild = true; results.errors = errors; await writeFile('verification/web-integration/results.json', JSON.stringify(results, null, 2)); console.log('Web entry, host isolation, theme, performance persistence/restoration, keyboard, clock animation, detail/viewer, responsive and PWA build passed.'); } finally { await browser.close(); }