This commit is contained in:
oneao committed 2026-10-10 17:33:59 +08:00
1 parent 5f28a10d89
commit 32d423b759
59 files changed
+5101 -1098

No files matched your search

@@ -0,0 +1,135 @@
import { expect, test } from '@playwright/test'
/**
* 双列菜单:第一列选中项样式回归 —— 针对 ERP 应用。
*
* 需求:layout-mixed-rail__item.is-active 的背景要比原来"深一点"、文字白色。
*
* 对照 vben(menu-ui/src/components/menu.vue):
* @mixin menu-item-active (375-379):
* color: var(--menu-item-active-color);
* background: var(--menu-item-active-background-color);
* 深色档赋值 (470-471):
* --menu-item-active-color: hsl(var(--accent-foreground)) ← 高对比文字
* --menu-item-active-background-color: hsl(var(--accent)) ← 强调底色
*
* 判定:选中项背景应比所在列背景明显更深,且文字为亮色(对比度足够)。
*/
const ERP_URL = process.env.ERP_URL ?? 'http://localhost:5090'
async function useMixedNavLayout(page: import('@playwright/test').Page) {
await page.goto(`${ERP_URL}/`)
await page.evaluate(() => {
const KEY = 'g3erp:preferences'
const raw = localStorage.getItem(KEY)
const pref: Record<string, Record<string, unknown>> = raw ? JSON.parse(raw) : {}
const app = pref.app || {}
app.layout = 'sidebar-mixed-nav'
pref.app = app
const sidebar = pref.sidebar || {}
sidebar.enable = true
sidebar.extraCollapse = false
pref.sidebar = sidebar
// 浅色模式,确保测的是浅色档
const theme = pref.theme || {}
theme.mode = 'light'
pref.theme = theme
localStorage.setItem(KEY, JSON.stringify(pref))
for (const key of Object.keys(localStorage)) {
if (key.indexOf('g3erp:app') === 0) localStorage.removeItem(key)
}
})
await page.reload()
await page.waitForTimeout(400)
}
/** 解析 rgb(a) 字符串 → [r,g,b] */
function parseRgb(value: string): [number, number, number] {
const m = value.match(/rgba?\(([^)]+)\)/)
if (!m) throw new Error(`无法解析颜色: ${value}`)
const parts = m[1].split(',').map((s) => Number(s.trim()))
return [parts[0], parts[1], parts[2]]
}
/** 相对亮度(WCAG) */
function luminance([r, g, b]: [number, number, number]): number {
const f = (c: number) => {
const s = c / 255
return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4
}
return 0.2126 * f(r) + 0.7152 * f(g) + 0.0722 * f(b)
}
/** 对比度 */
function contrast(a: [number, number, number], b: [number, number, number]): number {
const la = luminance(a)
const lb = luminance(b)
const [hi, lo] = la > lb ? [la, lb] : [lb, la]
return (hi + 0.05) / (lo + 0.05)
}
test.describe('ERP 双列菜单 - 图标栏选中态', () => {
test('选中项背景比列背景更深,且文字是亮色(对比度足够)', async ({ page }) => {
await useMixedNavLayout(page)
await page.goto(`${ERP_URL}/ocean/booking`)
await page.waitForTimeout(400)
const rail = page.locator('.layout-mixed-rail')
const active = page.locator('.layout-mixed-rail__item.is-active')
await expect(active, '应有一个选中项').toHaveCount(1)
const styles = await page.evaluate(() => {
const railEl = document.querySelector('.layout-mixed-rail') as HTMLElement
const itemEl = document.querySelector('.layout-mixed-rail__item.is-active') as HTMLElement
const cs = getComputedStyle(itemEl)
return {
railBg: getComputedStyle(railEl).backgroundColor,
itemBg: cs.backgroundColor,
itemColor: cs.color,
fontWeight: cs.fontWeight,
}
})
// eslint-disable-next-line no-console
console.log('[active-style]', JSON.stringify(styles))
const railBg = parseRgb(styles.railBg)
const itemBg = parseRgb(styles.itemBg)
const itemColor = parseRgb(styles.itemColor)
// 1) 选中背景确实比列背景更深(亮度更低)
expect(
luminance(itemBg),
`选中背景应比列背景更深:item=${styles.itemBg} rail=${styles.railBg}`,
).toBeLessThan(luminance(railBg))
// 2) 背景不是透明(否则测的是列背景)
expect(styles.itemBg, '选中背景不应是透明').not.toMatch(/rgba\(0,\s*0,\s*0,\s*0\)/)
// 3) 文字与选中背景的对比度达标(可读)
const ratio = contrast(itemColor, itemBg)
expect(ratio, `文字/背景对比度应 >= 4.5,实际 ${ratio.toFixed(2)}`).toBeGreaterThanOrEqual(4.5)
// 4) 文字比背景亮("白色/亮色文字")
expect(
luminance(itemColor),
`文字应比选中背景更亮:text=${styles.itemColor} bg=${styles.itemBg}`,
).toBeGreaterThan(luminance(itemBg))
})
test('未选中项没有深色底(对照)', async ({ page }) => {
await useMixedNavLayout(page)
await page.goto(`${ERP_URL}/ocean/booking`)
await page.waitForTimeout(400)
const inactive = page.locator('.layout-mixed-rail__item:not(.is-active)').first()
const bg = await inactive.evaluate((el) => getComputedStyle(el).backgroundColor)
// 未选中项是透明(继承列的底色)
expect(bg, `未选中项不应有背景填充,实际 ${bg}`).toMatch(/rgba\(0,\s*0,\s*0,\s*0\)/)
})
})