SHA256
feat(android): align native preview with measured PWA design
This commit is contained in:
1 parent
bd0e4cc4fb
commit
7ac9882307
30 files changed
+2793
-750
No files matched your search
@@ -0,0 +1,53 @@
|
||||
// Development-only side-by-side evidence. This deliberately asserts no pixel parity.
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { createRequire } from 'node:module';
|
||||
import { createHash } from 'node:crypto';
|
||||
|
||||
const [modulePath, nativeDirectory, pwaDirectory, destination] = process.argv.slice(2);
|
||||
if (!destination) throw new Error('Usage: node compare-native-pwa.mjs PLAYWRIGHT_MODULE NATIVE_DIR PWA_DIR OUTPUT_DIR');
|
||||
const { chromium } = createRequire(import.meta.url)(path.resolve(modulePath));
|
||||
const viewport = JSON.parse(fs.readFileSync(path.join(nativeDirectory, 'viewport.json')));
|
||||
if (viewport.fontScale !== 1) throw new Error('This CSS reference comparison requires fontScale 1');
|
||||
const cssWidth = viewport.widthPx / viewport.density;
|
||||
const cssHeight = viewport.heightPx / viewport.density;
|
||||
const reference = JSON.parse(fs.readFileSync(path.join(pwaDirectory, 'measurements.json')));
|
||||
if (!reference.screens.every(screen => screen.viewport.width === cssWidth && screen.viewport.height === cssHeight)) {
|
||||
throw new Error('PWA and native content viewports do not match');
|
||||
}
|
||||
function image(directory, name) {
|
||||
const bytes = fs.readFileSync(path.join(directory, name));
|
||||
if (bytes.subarray(0, 8).toString('hex') !== '89504e470d0a1a0a' ||
|
||||
bytes.readUInt32BE(16) !== viewport.widthPx || bytes.readUInt32BE(20) !== viewport.heightPx) {
|
||||
throw new Error(`Invalid or mismatched PNG: ${name}`);
|
||||
}
|
||||
return { uri: `data:image/png;base64,${bytes.toString('base64')}`, sha256: createHash('sha256').update(bytes).digest('hex') };
|
||||
}
|
||||
fs.mkdirSync(destination, { recursive: true });
|
||||
const browser = await chromium.launch({ headless: true, args: process.getuid?.() === 0 ? ['--no-sandbox'] : [] });
|
||||
const records = [];
|
||||
try {
|
||||
const page = await browser.newPage({ viewport: { width: viewport.widthPx * 2, height: viewport.heightPx + 48 }, deviceScaleFactor: 1 });
|
||||
await page.route('**/*', route => route.abort());
|
||||
for (const name of ['login', 'dashboard', 'tasks', 'notes', 'shopping']) {
|
||||
for (const theme of ['light', 'dark']) {
|
||||
const native = image(nativeDirectory, `${name}-${theme}.png`);
|
||||
const pwa = image(pwaDirectory, `${name}-${theme}-${cssWidth}.png`);
|
||||
await page.setContent(`<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src data:; style-src 'unsafe-inline'">
|
||||
<style>body{margin:0;background:#eee;font:20px sans-serif}main{display:flex}section{width:${viewport.widthPx}px}
|
||||
header{height:48px;display:grid;place-items:center}img{display:block;width:100%}</style>
|
||||
<main><section><header>PWA 2.78.0 · ${name} · ${theme}</header><img src="${pwa.uri}"></section>
|
||||
<section><header>Android nativ · ${name} · ${theme} · keine 1:1-Abnahme</header><img src="${native.uri}"></section></main>`);
|
||||
await page.locator('img').evaluateAll(images => Promise.all(images.map(image => image.decode())));
|
||||
const file = `${name}-${theme}-comparison.png`;
|
||||
await page.screenshot({ path: path.join(destination, file) });
|
||||
records.push({ file, nativeSha256: native.sha256, pwaSha256: pwa.sha256 });
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
fs.writeFileSync(path.join(destination, 'comparison.json'), JSON.stringify({
|
||||
status: 'Side-by-side evidence only; no visual acceptance', viewport, upstreamRevision: reference.upstreamRevision, records,
|
||||
}, null, 2) + '\n');
|
||||
console.log(`Wrote ${records.length} matched-viewport comparisons; no parity assertion.`);
|
||||
Reference in new issue
Block a user