test(sw): align browser tests and theme story with Noto Sans KR and Bold

Font expectations move back to Noto Sans KR (Dotum becomes the theme
inheritance verification value), viewer labels expect 700, and the new
text-weight.browser.mjs checks the requested Bold scope and outlines.
This commit is contained in:
shkim4 committed 2026-10-20 18:17:50 +09:00
1 parent 033e24246a
commit ef69f5143b
9 files changed
+205 -38

No files matched your search

+19 -19
View File
@@ -1,7 +1,7 @@
// SW theme token inheritance (2026-10-20, A-1).
// 1) SW/Theme › Theme Inheritance: a token set through `style` on the outermost SW scope reaches the header, dropdowns,
// More, panels, footer command line and tooltips; portals get it only when wrapped in a themed <SWTheme>; areas do
// not mix; unthemed components keep the SW default Dotum (2026-10-20). Noto Sans KR is a verification value only.
// not mix; unthemed components keep Noto Sans KR. Dotum is a verification value only (not the SW default).
// 2) Regression: every existing SW Story renders identically with the new cascade and with the legacy cascade
// (token blocks re-declared on every `.hm-sw-header, .hm-sw-theme`, i.e. the code before this change): computed
// font / color / geometry of every SW element and an element screenshot comparison. No Story sets an override,
@@ -22,9 +22,9 @@ const [storybook = 'http://127.0.0.1:6024', outArg] = process.argv.slice(2)
const out = path.resolve(outArg ?? 'docs/sw/theme-inheritance-qa')
await mkdir(out, { recursive: true })
const VERIFY = '"Noto Sans KR", sans-serif'
const VERIFY = 'Dotum, 돋움, sans-serif'
const NESTED = '"Malgun Gothic", sans-serif'
const DEFAULT = 'Dotum, 돋움, "Malgun Gothic", "Apple SD Gothic Neo", sans-serif'
const DEFAULT = '"Noto Sans KR", "Malgun Gothic", "Apple SD Gothic Neo", sans-serif'
const results = []
const check = (name, pass, detail = '') => { results.push({ name, result: pass ? 'PASS' : 'FAIL', detail }); console.log(`${pass ? 'PASS' : 'FAIL'} ${name}${detail ? ` — ${detail}` : ''}`) }
const errors = []
@@ -67,52 +67,52 @@ const fmt = map => Object.entries(map).map(([family, count]) => `${count}× ${fa
// Default (no theme): header, then its sample dropdown and More.
const def = scope('default')
check('기본: 단독 SWViewerHeader = 기본 Dotum', only(await fonts(page, `${def} .hm-sw-viewer-header`), DEFAULT), fmt(await fonts(page, `${def} .hm-sw-viewer-header`)))
check('기본: 단독 SWViewerHeader = Noto Sans KR', only(await fonts(page, `${def} .hm-sw-viewer-header`), DEFAULT), fmt(await fonts(page, `${def} .hm-sw-viewer-header`)))
await page.locator(`${def} .hm-sw-viewer-header [aria-haspopup="menu"]`).first().click()
await page.waitForSelector(`${def} .hm-sw-viewer-menu`)
check('기본: Dropdown = 기본 Dotum', only(await fonts(page, `${def} .hm-sw-viewer-menu`), DEFAULT), fmt(await fonts(page, `${def} .hm-sw-viewer-menu`)))
check('기본: Dropdown = Noto Sans KR', only(await fonts(page, `${def} .hm-sw-viewer-menu`), DEFAULT), fmt(await fonts(page, `${def} .hm-sw-viewer-menu`)))
await page.keyboard.press('Escape')
await page.locator(`${def} .hm-sw-viewer-header button[aria-label="추가 도구"]`).click()
await page.waitForSelector(`${def} .hm-sw-viewer-menu[data-surface="beige"]`)
check('기본: More = 기본 Dotum', only(await fonts(page, `${def} .hm-sw-viewer-menu`), DEFAULT), fmt(await fonts(page, `${def} .hm-sw-viewer-menu`)))
check('기본: More = Noto Sans KR', only(await fonts(page, `${def} .hm-sw-viewer-menu`), DEFAULT), fmt(await fonts(page, `${def} .hm-sw-viewer-menu`)))
await page.keyboard.press('Escape')
// Viewer, themed through <SWTheme style>.
const viewer = scope('viewer-themed')
check('뷰어 테마: SWViewerHeader 전체 = 검증 글꼴', only(await fonts(page, `${viewer} .hm-sw-viewer-header`), VERIFY), fmt(await fonts(page, `${viewer} .hm-sw-viewer-header`)))
check('뷰어 테마: SWViewerHeader 전체 = Dotum', only(await fonts(page, `${viewer} .hm-sw-viewer-header`), VERIFY), fmt(await fonts(page, `${viewer} .hm-sw-viewer-header`)))
await page.locator(`${viewer} .hm-sw-viewer-header [aria-haspopup="menu"]`).first().click()
await page.waitForSelector(`${viewer} .hm-sw-viewer-menu`)
check('뷰어 테마: Dropdown = 검증 글꼴', only(await fonts(page, `${viewer} .hm-sw-viewer-menu`), VERIFY), fmt(await fonts(page, `${viewer} .hm-sw-viewer-menu`)))
check('뷰어 테마: Dropdown = Dotum', only(await fonts(page, `${viewer} .hm-sw-viewer-menu`), VERIFY), fmt(await fonts(page, `${viewer} .hm-sw-viewer-menu`)))
await page.keyboard.press('Escape')
await page.locator(`${viewer} .hm-sw-viewer-header button[aria-label="추가 도구"]`).click()
await page.waitForSelector(`${viewer} .hm-sw-viewer-menu[data-surface="beige"]`)
check('뷰어 테마: More = 검증 글꼴', only(await fonts(page, `${viewer} .hm-sw-viewer-menu`), VERIFY), fmt(await fonts(page, `${viewer} .hm-sw-viewer-menu`)))
check('뷰어 테마: More = Dotum', only(await fonts(page, `${viewer} .hm-sw-viewer-menu`), VERIFY), fmt(await fonts(page, `${viewer} .hm-sw-viewer-menu`)))
// A submenu of More, if any, is a nested menu inside a nested menu.
const sub = page.locator(`${viewer} .hm-sw-viewer-menu[data-surface="beige"] [aria-haspopup="menu"]`).first()
if (await sub.count()) {
await sub.hover()
await page.waitForTimeout(150)
check('뷰어 테마: More 서브메뉴 = 검증 글꼴', only(await fonts(page, `${viewer} .hm-sw-viewer-menu`), VERIFY), fmt(await fonts(page, `${viewer} .hm-sw-viewer-menu`)))
check('뷰어 테마: More 서브메뉴 = Dotum', only(await fonts(page, `${viewer} .hm-sw-viewer-menu`), VERIFY), fmt(await fonts(page, `${viewer} .hm-sw-viewer-menu`)))
}
await page.keyboard.press('Escape')
const panels = await page.locator(`${viewer} .hm-sw-viewer-panel`).count()
check('뷰어 테마: SWViewerPanel(제목·입력·버튼·스위치·힌트) = 검증 글꼴', panels === 2 && only(await fonts(page, `${viewer} .hm-sw-viewer-panel`), VERIFY), `${panels} panels · ${fmt(await fonts(page, `${viewer} .hm-sw-viewer-panel`))}`)
check('뷰어 테마: SWViewerPanel(제목·입력·버튼·스위치·힌트) = Dotum', panels === 2 && only(await fonts(page, `${viewer} .hm-sw-viewer-panel`), VERIFY), `${panels} panels · ${fmt(await fonts(page, `${viewer} .hm-sw-viewer-panel`))}`)
await page.screenshot({ path: path.join(out, 'viewer-themed-more.png'), clip: await page.locator(viewer).boundingBox() })
// General mode: SWHeader (+ open layer dropdown, window-move tooltip), SWFooter (+ command line, guide tooltip).
const general = scope('general-themed')
const headerFonts = await fonts(page, `${general} .hm-sw-header`, '.hm-sw-header__motion-tooltip')
check('일반 테마: SWHeader + 열린 레이어 Dropdown = 검증 글꼴 (창 이동 Tooltip 별도)', only(headerFonts, VERIFY), fmt(headerFonts))
check('일반 테마: SWHeader + 열린 레이어 Dropdown = Dotum (창 이동 Tooltip 별도)', only(headerFonts, VERIFY), fmt(headerFonts))
const tooltip = await page.locator(`${general} .hm-sw-header__motion-tooltip`).evaluate(element => getComputedStyle(element).fontFamily)
check('일반 테마: 창 이동 Tooltip = Inter → 테마 글꼴', tooltip === `Inter, ${VERIFY}`, tooltip)
const layerMenu = await page.locator(`${general} .hm-sw-header`).getByText('A-WALL').count()
check('일반 테마: 레이어 Dropdown이 열린 상태(목록 행 A-WALL 표시)', layerMenu > 0, `${layerMenu} row`)
check('일반 테마: SWFooter = 검증 글꼴', only(await fonts(page, `${general} .hm-sw-footer`), VERIFY), fmt(await fonts(page, `${general} .hm-sw-footer`)))
check('일반 테마: 명령창(SWFooterCommandLine, 중첩 hm-sw-theme) = 검증 글꼴', only(await fonts(page, `${general} .hm-sw-footer-command`), VERIFY), fmt(await fonts(page, `${general} .hm-sw-footer-command`)))
check('일반 테마: SWFooter = Dotum', only(await fonts(page, `${general} .hm-sw-footer`), VERIFY), fmt(await fonts(page, `${general} .hm-sw-footer`)))
check('일반 테마: 명령창(SWFooterCommandLine, 중첩 hm-sw-theme) = Dotum', only(await fonts(page, `${general} .hm-sw-footer-command`), VERIFY), fmt(await fonts(page, `${general} .hm-sw-footer-command`)))
await page.locator(`${general} .hm-sw-footer-add`).first().focus()
await page.waitForTimeout(150)
const guide = page.locator(`${general} .hm-sw-footer [role="tooltip"]`)
check('일반 테마: 명령 등록 안내(Tooltip) = 검증 글꼴', (await guide.count()) > 0 && only(await fonts(page, `${general} .hm-sw-footer [role="tooltip"]`), VERIFY), fmt(await fonts(page, `${general} .hm-sw-footer [role="tooltip"]`)))
check('일반 테마: 명령 등록 안내(Tooltip) = Dotum', (await guide.count()) > 0 && only(await fonts(page, `${general} .hm-sw-footer [role="tooltip"]`), VERIFY), fmt(await fonts(page, `${general} .hm-sw-footer [role="tooltip"]`)))
// Portals.
const [plain, themed, insideSource] = await page.evaluate(() => {
@@ -122,15 +122,15 @@ const fmt = map => Object.entries(map).map(([family, count]) => `${count}× ${fa
!!document.querySelector('[data-theme-case="portal-source"] [data-portal]')]
})
check('Portal: DOM이 테마 영역 밖에 렌더링됨', !insideSource)
check('Portal: SWTheme만 감싸면 기본 글꼴(테마 미전달)', plain === DEFAULT, plain)
check('Portal: SWTheme만 감싸면 기본 글꼴(테마 미전달)', plain.startsWith('"Noto Sans KR"'), plain)
check('Portal: 같은 theme style의 SWTheme으로 감싸면 테마 적용', themed === VERIFY, themed)
// Areas.
const area = name => fonts(page, `${scope(name)} .hm-sw-viewer-hint`)
check('영역 A(검증 글꼴)', only(await area('area-a'), VERIFY), fmt(await area('area-a')))
check('영역 A(Dotum)', only(await area('area-a'), VERIFY), fmt(await area('area-a')))
check('영역 B(기본) — 옆 영역 값이 섞이지 않음', only(await area('area-b'), DEFAULT), fmt(await area('area-b')))
const outer = await page.locator(`${scope('area-c')} > .hm-sw-viewer-hint`).evaluate(element => getComputedStyle(element).fontFamily)
check('영역 C 바깥(검증 글꼴)', outer === VERIFY, outer)
check('영역 C 바깥(Dotum)', outer === VERIFY, outer)
check('영역 C 안쪽 SWTheme style(Malgun Gothic)만 안쪽에 적용', only(await area('area-c-nested'), NESTED), fmt(await area('area-c-nested')))
await page.screenshot({ path: path.join(out, 'theme-inheritance-full.png'), fullPage: true })
await page.close()
@@ -213,7 +213,7 @@ for (const width of [1920, 1440, 1180, 900, 560]) for (const id of ids) {
await page.waitForSelector(`${viewer} .hm-sw-viewer-menu[data-surface="beige"]`)
const more = await fonts(page, `${viewer} .hm-sw-viewer-menu`)
const command = await fonts(page, '[data-theme-case="general-themed"] .hm-sw-footer-command')
check('대조: 기존 구조 주입 시 More·명령창이 기본 글꼴로 되돌아감(결함 재현)', only(more, DEFAULT) && only(command, DEFAULT), `More ${fmt(more)} · 명령창 ${fmt(command)}`)
check('대조: 기존 구조 주입 시 More·명령창이 Noto로 되돌아감(결함 재현)', only(more, DEFAULT) && only(command, DEFAULT), `More ${fmt(more)} · 명령창 ${fmt(command)}`)
await page.close()
}
const failedRegression = regression.filter(item => item.result !== 'PASS')