85 lines
6.8 KiB
JavaScript
85 lines
6.8 KiB
JavaScript
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { createRequire } from 'node:module';
|
|
const require = createRequire(process.env.HM_PLAYWRIGHT_DIR ? path.join(process.env.HM_PLAYWRIGHT_DIR, 'package.json') : import.meta.url);
|
|
const { chromium } = require('playwright');
|
|
const browser = await chromium.launch({ headless: true, ...(process.env.HM_CHROME_PATH ? { executablePath: process.env.HM_CHROME_PATH } : {}) });
|
|
const out = '_local/quality-20261010';
|
|
const results = [];
|
|
const selected = process.argv.slice(2);
|
|
const base = 'http://127.0.0.1:6322';
|
|
const task = async (name, fn) => { if (selected.length && !selected.includes(name)) return; try { const evidence = await fn(); results.push({ name, status: 'PASS', evidence }); } catch (e) { results.push({ name, status: 'FAIL', error: e.message }); } console.log(JSON.stringify(results.at(-1))); };
|
|
const assert = (ok, message) => { if (!ok) throw Error(message); };
|
|
try {
|
|
for (const [name, route] of [['APP', '/'], ['PUBLISHER', '/publisher/'], ['DASHBOARD', '/dashboard/'], ['RECRUIT', '/recruit/']]) {
|
|
await task(`${name}_RESPONSIVE_RUNTIME`, async () => {
|
|
const evidence = [];
|
|
for (const width of [390, 768, 1280, 1440]) {
|
|
const page = await browser.newPage({ viewport: { width, height: 900 } });
|
|
const errors = [];
|
|
page.on('pageerror', e => errors.push(e.message));
|
|
await page.goto(base + route, { waitUntil: 'networkidle' });
|
|
await page.locator('#root [data-slot]:visible').first().waitFor();
|
|
const result = await page.evaluate(() => {
|
|
const frame = document.querySelector('[data-slot="dashboard-layout"]');
|
|
const scroll = document.querySelector('[data-slot="dashboard-scroll"]');
|
|
return { overflow: document.documentElement.scrollWidth > innerWidth + 1, hmElements: document.querySelectorAll('[data-slot]').length, frameWidth: frame?.getBoundingClientRect().width, headerHeight: document.querySelector('[data-slot="dashboard-header"]')?.getBoundingClientRect().height, internalOverflow: scroll ? scroll.scrollWidth > scroll.clientWidth : null };
|
|
});
|
|
await page.screenshot({ path: `${out}/existing-${name}-${width}.png`, fullPage: true });
|
|
await page.close();
|
|
assert(!result.overflow && !errors.length, `${width}: overflow / browser errors ${JSON.stringify(errors)}`);
|
|
if (result.frameWidth) assert(result.frameWidth >= 960 && result.headerHeight === 48, 'Dashboard geometry');
|
|
evidence.push({ width, ...result, errors });
|
|
}
|
|
return evidence;
|
|
});
|
|
}
|
|
await task('FILTERBAR_REFERENCE_USAGE', async () => {
|
|
const page = await browser.newPage();
|
|
await page.goto(`${base}/storybook/iframe.html?id=patterns-data-filter-bar--all&viewMode=story`, { waitUntil: 'networkidle' });
|
|
const status = page.getByRole('status');
|
|
await page.getByRole('searchbox', { name: '검색어' }).fill('김');
|
|
assert((await status.innerText()).includes('입력한 조건 1개 · 조회 적용 조건 0개'), 'draft / applied distinction');
|
|
await page.getByRole('button', { name: '조회', exact: true }).click(); assert((await status.innerText()).includes('조회 적용 조건 1개'), 'reference query');
|
|
await page.getByRole('button', { name: '초기화', exact: true }).click(); assert((await status.innerText()).includes('입력한 조건 0개 · 조회 적용 조건 0개'), 'reference reset');
|
|
const evidence = [];
|
|
for (const width of [390, 768, 1280, 1440]) {
|
|
await page.setViewportSize({ width, height: 900 });
|
|
const metrics = await page.locator('[data-slot="filter-bar"]').evaluate(e => ({ layout: e.dataset.layout, gap: getComputedStyle(e).gap, justify: getComputedStyle(e).justifyContent, wrap: getComputedStyle(e).flexWrap }));
|
|
assert(metrics.layout === 'list-extended' && metrics.gap === '8px' && metrics.wrap === 'wrap' && metrics.justify === 'flex-start', 'FilterBar composition');
|
|
await page.screenshot({ path: `${out}/filter-reference-${width}.png`, fullPage: true }); evidence.push({ width, ...metrics });
|
|
}
|
|
await page.close(); return evidence;
|
|
});
|
|
await task('THEME_DESIGNER', async () => {
|
|
const page = await browser.newPage();
|
|
await page.goto(`${base}/storybook/iframe.html?id=theme-designer-playground--live-verification&viewMode=story`, { waitUntil: 'networkidle' });
|
|
await page.waitForFunction(() => /^(PASS|FAIL)/.test(document.querySelector('[data-testid="designer-result"]')?.textContent ?? ''), { timeout: 20000 });
|
|
const text = await page.locator('[data-testid="designer-result"]').innerText(); assert(text.startsWith('PASS'), text);
|
|
await page.close(); return { text };
|
|
});
|
|
await task('THEME_JSON_LOADER', async () => {
|
|
const page = await browser.newPage();
|
|
await page.goto(`${base}/storybook/iframe.html?id=theme-designer-playground--editor&viewMode=story`, { waitUntil: 'networkidle' });
|
|
await page.getByRole('button', { name: 'Load Service Example JSON', exact: true }).click();
|
|
await page.waitForFunction(() => document.querySelector('[aria-label="Primary / Brand HEX"]')?.value !== '#123328');
|
|
const changed = await page.getByRole('textbox', { name: 'Primary / Brand HEX', exact: true }).inputValue();
|
|
await page.getByRole('button', { name: 'Load Default JSON', exact: true }).click();
|
|
await page.waitForFunction(() => document.querySelector('[aria-label="Primary / Brand HEX"]')?.value === '#123328');
|
|
await page.close(); return { servicePrimary: changed, defaultPrimary: '#123328' };
|
|
});
|
|
await task('RUNTIME_THEME_KEYBOARD', async () => {
|
|
const page = await browser.newPage();
|
|
await page.goto(`${base}/storybook/iframe.html?id=theme-runtime-contract--verification&viewMode=story`, { waitUntil: 'networkidle' });
|
|
await page.waitForFunction(() => /^(PASS|FAIL)/.test(document.querySelector('[data-testid="result"]')?.textContent ?? ''), { timeout: 20000 });
|
|
const auto = await page.locator('[data-testid="result"]').innerText(); assert(auto.startsWith('PASS'), auto);
|
|
const run = page.getByRole('button', { name: 'Run verification', exact: true });
|
|
await run.focus(); await page.keyboard.press('Tab'); await page.keyboard.press('Shift+Tab'); await page.keyboard.press('Enter');
|
|
await page.waitForFunction(() => /^(PASS|FAIL)/.test(document.querySelector('[data-testid="result"]')?.textContent ?? '') && document.querySelector('[data-testid="result"]')?.dataset.running !== 'true', { timeout: 20000 });
|
|
const keyboard = await page.locator('[data-testid="result"]').innerText(); assert(keyboard.startsWith('PASS') && !keyboard.includes('KEYBOARD_FOCUS: NOT VERIFIED'), keyboard);
|
|
await page.close(); return { auto, keyboard };
|
|
});
|
|
} finally { await browser.close(); }
|
|
fs.writeFileSync(`${out}/${selected.length ? 'reference-' + selected.join('-') : 'reference-runtime'}.json`, JSON.stringify(results, null, 2));
|
|
if (results.some(r => r.status === 'FAIL')) process.exitCode = 1;
|