feat: integrate latest HMDesign design system updates

This commit is contained in:
DESKTOP-KPCP6AL\ㅅㅎ committed 2026-10-11 11:09:03 +09:00
1 parent 99faec189e
commit 1c4e0bb9a1
209 files changed
+8845 -552

No files matched your search

+84
View File
@@ -0,0 +1,84 @@
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;