#!/usr/bin/env node /** * run-site-smoke.mjs — browser smoke test for the documentation language switch. * Start node site/dev-server.js first, then run this file. */ import { chromium } from 'playwright'; const BASE = process.env.REG_BASE || 'http://127.0.0.1:3311'; const url = `${BASE}/site`; /* 路由是无 # 的 History-API 路径,站点根就是 /site */ const failures = []; function assert(condition, message) { if (!condition) failures.push(message); console.log(`[site-smoke] ${condition ? 'OK' : 'FAIL'} ${message}`); } const browser = await chromium.launch(); const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } }); try { await page.goto(`${url}/component/button`, { waitUntil: 'networkidle', timeout: 15000 }); await page.evaluate(() => localStorage.removeItem('kole-lang')); await page.reload({ waitUntil: 'networkidle' }); await page.waitForSelector('#content .detail-head', { timeout: 10000 }); assert(await page.locator('html').getAttribute('lang') === 'zh-CN', 'default language is zh-CN'); assert((await page.locator('#lang-cur').textContent()).trim() === '简体中文', 'language trigger shows the current language'); assert(await page.locator('#lang-trigger').getAttribute('aria-expanded') === 'false', 'language menu starts collapsed'); assert(await page.locator('#lang-menu .lang-opt[data-lang="zh"]').getAttribute('aria-selected') === 'true', 'zh option is marked selected'); assert((await page.locator('.topnav a[data-nav="overview"]').textContent()).trim() === '组件', 'Chinese navigation is visible'); assert((await page.title()) === 'Kole UI · 组件库文档', 'Chinese document title is visible'); assert(await page.locator('#fw-select').inputValue() === 'h5', 'default framework is h5'); // 下拉行为:展开 → Esc 关闭 → 方向键展开并聚焦当前语言 → 选中英语 await page.locator('#lang-trigger').click(); assert(await page.locator('#lang-trigger').getAttribute('aria-expanded') === 'true', 'trigger reports aria-expanded=true'); assert(await page.locator('#lang-menu').isVisible(), 'menu is visible after clicking the trigger'); await page.keyboard.press('Escape'); assert(!(await page.locator('#lang-menu').isVisible()), 'Escape closes the menu'); assert(await page.locator('#lang-trigger').getAttribute('aria-expanded') === 'false', 'Escape resets aria-expanded'); await page.locator('#fw-select').selectOption('react'); await page.locator('#lang-trigger').focus(); await page.keyboard.press('ArrowDown'); assert(await page.evaluate(() => document.activeElement?.getAttribute('data-lang')) === 'zh', 'ArrowDown opens the menu and focuses the current language'); await page.keyboard.press('ArrowDown'); assert(await page.evaluate(() => document.activeElement?.getAttribute('data-lang')) === 'en', 'ArrowDown moves focus to the next language'); await page.keyboard.press('Enter'); await page.waitForFunction(() => document.documentElement.lang === 'en'); await page.waitForSelector('#content .detail-head'); assert(await page.locator('html').getAttribute('lang') === 'en', 'switch updates html lang to en'); assert((await page.locator('#lang-cur').textContent()).trim() === 'English', 'language trigger shows English after switching'); assert(await page.locator('#lang-menu .lang-opt[data-lang="en"]').getAttribute('aria-selected') === 'true', 'en option is marked selected'); assert(await page.evaluate(() => document.activeElement?.id) === 'lang-trigger', 'focus returns to the trigger after choosing'); assert((await page.locator('.topnav a[data-nav="overview"]').textContent()).trim() === 'Components', 'English navigation is visible'); assert((await page.title()) === 'Kole UI · Component Library', 'English document title is visible'); assert(await page.evaluate(() => localStorage.getItem('kole-lang')) === 'en', 'English preference is persisted'); assert(await page.locator('#fw-select').inputValue() === 'react', 'framework selection survives language switch'); assert((await page.locator('#content .detail-head h1').textContent()).includes('Button'), 'component name switches to English'); assert((await page.locator('#toc').textContent()).includes('When to Use'), 'component TOC switches to English'); await page.locator('#fab-theme').click(); assert((await page.locator('#theme-panel .tp-head').textContent()).trim() === 'Theme', 'theme panel switches to English'); await page.locator('#fab-theme').click(); await page.goto(`${url}/overview`, { waitUntil: 'networkidle' }); await page.waitForSelector('#content .page-title'); assert((await page.locator('#content .page-title').textContent()).trim() === 'All Components', 'English route navigation remains active'); await page.reload({ waitUntil: 'networkidle' }); await page.waitForSelector('#content .page-title'); assert(await page.locator('html').getAttribute('lang') === 'en', 'English preference survives reload'); assert((await page.locator('#content .page-title').textContent()).trim() === 'All Components', 'English route content survives reload'); await page.locator('#lang-trigger').click(); await page.locator('#lang-menu .lang-opt[data-lang="zh"]').click(); await page.waitForFunction(() => document.documentElement.lang === 'zh-CN'); assert((await page.locator('#lang-cur').textContent()).trim() === '简体中文', 'language trigger shows the current language again'); assert(await page.evaluate(() => localStorage.getItem('kole-lang')) === 'zh', 'Chinese preference is persisted after switching back'); assert((await page.locator('#content .page-title').textContent()).trim() === '组件总览', 'Chinese content restores after switching back'); } finally { await browser.close(); } if (failures.length) { console.error(`\n[site-smoke] ${failures.length} check(s) failed`); process.exit(1); } console.log('\n[site-smoke] OK — all checks passed');