waybetter-for-x/apps/desktop/scripts/gallery-shots.mjs
2026-10-01 06:01:13 +07:00

52 lines
3.5 KiB
JavaScript

// Screenshots and checks of the store gallery pages (/themes/, /plugins/) of the built site.
// Usage: (cd apps/web && bun run build && npx vite preview --port 1481) then
// node scripts/gallery-shots.mjs [baseUrl] [outDir]
import { chromium } from 'playwright';
import { mkdirSync } from 'node:fs';
import { join } from 'node:path';
const base = process.argv[2] ?? 'http://127.0.0.1:1481';
const out = process.argv[3] ?? join(import.meta.dirname, '..', '..', '..', 'verification', 'store');
mkdirSync(out, { recursive: true });
const failures = [];
const check = (label, ok, detail = '') => { console.log(`${ok ? 'ok ' : 'FAIL'} ${label}${detail ? ` (${detail})` : ''}`); if (!ok) failures.push(label); };
const browser = await chromium.launch();
for (const [page, kind] of [['themes', 'theme'], ['plugins', 'plugin']]) {
for (const [label, viewport] of [['desktop', { width: 1360, height: 1000 }], ['mobile', { width: 390, height: 844 }]]) {
const tab = await browser.newPage({ viewport });
const broken = [];
tab.on('response', res => { if (res.status() >= 400) broken.push(`${res.status()} ${res.url()}`); });
tab.on('pageerror', error => failures.push(`${page}: ${error.message}`));
await tab.goto(`${base}/${page}/`);
await tab.waitForLoadState('networkidle');
const cards = await tab.locator('.gal-card').count();
const links = await tab.locator('a[href^="waybetter://install?kind="]').evaluateAll(as => as.map(a => a.getAttribute('href')));
const images = await tab.locator('.gal-img').evaluateAll(imgs => imgs.filter(img => img.naturalWidth === 0).length);
if (label === 'desktop') {
check(`${page}: cards`, cards >= (kind === 'theme' ? 13 : 14), `${cards}`);
check(`${page}: every card has an install link`, links.length === cards && links.every(href => href.startsWith(`waybetter://install?kind=${kind}&url=https%3A%2F%2Fwaybetter.fyi%2Fstore%2F`)));
check(`${page}: previews load`, images === 0, `${images} broken`);
check(`${page}: no 4xx/5xx`, broken.filter(b => !b.includes('/v1/')).length === 0, broken.join(', '));
check(`${page}: downloads point at the store files`, (await tab.locator('a[download]').count()) === cards);
if (kind === 'plugin') check('plugins: permission chips', (await tab.locator('.gal-perms li').count()) >= cards);
}
check(`${page} ${label}: no horizontal scroll`, await tab.evaluate(() => document.documentElement.scrollWidth <= innerWidth + 1));
await tab.screenshot({ path: join(out, `site-${page}-${label}.png`), fullPage: label === 'desktop' ? false : false });
if (label === 'desktop') {
await tab.locator('.gal-filters button').nth(1).click();
const shown = await tab.locator('.gal-card:not([hidden])').count();
check(`${page}: category filter`, shown > 0 && shown < cards, `${shown}`);
await tab.locator('.gal-filters button').first().click();
await tab.locator('[data-gallery-search]').fill(kind === 'theme' ? 'nord' : 'regex');
check(`${page}: search`, (await tab.locator('.gal-card:not([hidden])').count()) === 1);
await tab.locator('[data-gallery-search]').fill('');
await tab.evaluate(() => scrollTo(0, 560));
await tab.waitForTimeout(200);
await tab.screenshot({ path: join(out, `site-${page}-grid.png`) });
}
await tab.close();
}
}
await browser.close();
console.log(failures.length ? `\n${failures.length} failure(s):\n- ${failures.join('\n- ')}` : '\nall checks passed');
process.exit(failures.length ? 1 : 0);