111 lines
5.4 KiB
JavaScript
111 lines
5.4 KiB
JavaScript
import http from 'node:http';
|
|
import fs from 'node:fs/promises';
|
|
import fsSync from 'node:fs';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { chromium } from 'file:///C:/Users/toshio/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright/index.mjs';
|
|
|
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
const workspace = path.resolve(here, '../../..');
|
|
const previewDir = path.resolve(here, '../preview');
|
|
const qaPath = path.resolve(here, '../qa-results.json');
|
|
const screenshotScreens = ['board','battle','gear','shop','dig','bubble','revive','victory'];
|
|
const expectedScreens = ['board','dice','third-die','battle','gear','shop','tile-install','chest','campfire','smithy','blessing','dig','legendary','lotto','blackjack','claw','carriage','dice-machine','bubble','revive','victory','defeat','pause'];
|
|
|
|
const mime = new Map([
|
|
['.html','text/html; charset=utf-8'],['.css','text/css; charset=utf-8'],['.js','text/javascript; charset=utf-8'],
|
|
['.json','application/json; charset=utf-8'],['.png','image/png'],['.jpg','image/jpeg'],['.jpeg','image/jpeg']
|
|
]);
|
|
|
|
const server = http.createServer(async (req,res) => {
|
|
try {
|
|
const url = new URL(req.url, 'http://127.0.0.1');
|
|
const decoded = decodeURIComponent(url.pathname);
|
|
const relative = decoded === '/' ? 'design/ue-ui/prototype/index.html' : decoded.replace(/^\//,'');
|
|
let target = path.resolve(workspace, relative);
|
|
if (!target.startsWith(workspace)) throw new Error('Path outside workspace');
|
|
const stat = await fs.stat(target);
|
|
if (stat.isDirectory()) target = path.join(target, 'index.html');
|
|
const data = await fs.readFile(target);
|
|
res.writeHead(200, {'Content-Type': mime.get(path.extname(target).toLowerCase()) || 'application/octet-stream','Cache-Control':'no-store'});
|
|
res.end(data);
|
|
} catch {
|
|
res.writeHead(404, {'Content-Type':'text/plain; charset=utf-8'});
|
|
res.end('Not found');
|
|
}
|
|
});
|
|
|
|
await new Promise(resolve => server.listen(0,'127.0.0.1',resolve));
|
|
const port = server.address().port;
|
|
const candidates = [
|
|
'C:/Program Files/Google/Chrome/Application/chrome.exe',
|
|
'C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe',
|
|
'C:/Program Files/Microsoft/Edge/Application/msedge.exe'
|
|
];
|
|
const executablePath = candidates.find(p => fsSync.existsSync(p));
|
|
if (!executablePath) throw new Error('No local Chrome or Edge executable found.');
|
|
|
|
await fs.mkdir(previewDir,{recursive:true});
|
|
const browser = await chromium.launch({headless:true,executablePath});
|
|
const context = await browser.newContext({viewport:{width:750,height:1334},deviceScaleFactor:1});
|
|
const page = await context.newPage();
|
|
const requestFailures = [];
|
|
const pageErrors = [];
|
|
page.on('requestfailed',r => requestFailures.push({url:r.url(),error:r.failure()?.errorText || 'unknown'}));
|
|
page.on('pageerror',e => pageErrors.push(String(e)));
|
|
|
|
const results = [];
|
|
try {
|
|
for (const id of screenshotScreens) {
|
|
await page.goto(`http://127.0.0.1:${port}/design/ue-ui/prototype/?screen=${id}&capture=1`,{waitUntil:'networkidle'});
|
|
await page.waitForFunction(() => [...document.images].every(img => img.complete));
|
|
const metrics = await page.evaluate(() => {
|
|
const phone=document.querySelector('#phone');
|
|
const rect=phone.getBoundingClientRect();
|
|
const broken=[...document.images].filter(img=>!img.naturalWidth).map(img=>img.getAttribute('src'));
|
|
return {width:rect.width,height:rect.height,imageCount:document.images.length,broken};
|
|
});
|
|
await page.screenshot({path:path.join(previewDir,`${id}.png`),fullPage:false});
|
|
results.push({screen:id,...metrics,screenshot:`preview/${id}.png`});
|
|
}
|
|
|
|
await page.goto(`http://127.0.0.1:${port}/design/ue-ui/prototype/?screen=board&capture=1`,{waitUntil:'networkidle'});
|
|
const coverage = await page.evaluate(async ids => {
|
|
const rows=[];
|
|
for (const id of ids) {
|
|
window.HEROLL_UI.selectScreen(id);
|
|
await new Promise(r=>requestAnimationFrame(()=>requestAnimationFrame(r)));
|
|
const root=document.querySelector('.game-screen');
|
|
const imgs=[...document.images];
|
|
rows.push({id,rendered:Boolean(root),images:imgs.length,broken:imgs.filter(i=>!i.naturalWidth).map(i=>i.getAttribute('src')),primary:document.querySelector('.primary,.result-button')?.textContent?.trim() || ''});
|
|
}
|
|
return rows;
|
|
}, expectedScreens);
|
|
|
|
const qa = {
|
|
generatedAt:new Date().toISOString(),
|
|
target:{width:750,height:1334,aspect:'9:16 portrait'},
|
|
browser:{executablePath},
|
|
summary:{
|
|
expectedScreenCount:expectedScreens.length,
|
|
renderedScreenCount:coverage.filter(r=>r.rendered).length,
|
|
screenshotCount:results.length,
|
|
brokenImageCount:[...results.flatMap(r=>r.broken),...coverage.flatMap(r=>r.broken)].length,
|
|
requestFailureCount:requestFailures.length,
|
|
pageErrorCount:pageErrors.length,
|
|
dimensionsPass:results.every(r=>r.width===750 && r.height===1334)
|
|
},
|
|
screenshots:results,
|
|
coverage,
|
|
requestFailures,
|
|
pageErrors
|
|
};
|
|
qa.summary.pass = qa.summary.renderedScreenCount===qa.summary.expectedScreenCount && qa.summary.brokenImageCount===0 && qa.summary.requestFailureCount===0 && qa.summary.pageErrorCount===0 && qa.summary.dimensionsPass;
|
|
await fs.writeFile(qaPath,JSON.stringify(qa,null,2),'utf8');
|
|
console.log(JSON.stringify(qa.summary,null,2));
|
|
if (!qa.summary.pass) process.exitCode=1;
|
|
} finally {
|
|
await browser.close();
|
|
await new Promise(resolve=>server.close(resolve));
|
|
}
|