import { captureScreenshot, expect, test } from './fixtures' import { loginAsAdmin } from './helpers' const navigationModes = [ { label: '顶部一级 + 左侧二级', value: 'top-side' }, { label: '左侧分组菜单', value: 'side' }, { label: '顶部下拉菜单', value: 'top' }, ] as const const waitForDashboard = async (page: import('@playwright/test').Page) => { await expect(page.locator('.platform-shell')).toBeVisible() await expect(page.getByRole('heading', { name: '前后端联调状态' })).toBeVisible() await expect(page.getByText('2/2', { exact: true })).toBeVisible({ timeout: 15_000 }) await expect(page.locator('.dashboard-metrics article').first().locator('b')).not.toHaveText('0', { timeout: 15_000 }) } const contrastRatio = (foreground: string, background: string) => { const channels = (value: string) => (value.match(/[\d.]+/g) ?? []).slice(0, 3).map(Number) const luminance = (value: string) => channels(value).reduce((sum, channel, index) => { const normalized = channel / 255 const linear = normalized <= .03928 ? normalized / 12.92 : ((normalized + .055) / 1.055) ** 2.4 return sum + linear * [.2126, .7152, .0722][index]! }, 0) const [lighter, darker] = [luminance(foreground), luminance(background)].sort((left, right) => right - left) return (lighter! + .05) / (darker! + .05) } test('三种导航模式与三种主题可以持久化', async ({ page }, testInfo) => { await page.emulateMedia({ colorScheme: 'dark' }) await loginAsAdmin(page) await expect(page.locator('.platform-shell')).toHaveAttribute('data-navigation-mode', 'top-side') await expect(page.locator('html')).toHaveAttribute('data-theme', 'light') await expect(page.locator('.platform-header')).toHaveCSS('background-color', 'rgb(255, 255, 255)') expect(await page.locator('html').evaluate((element) => getComputedStyle(element).getPropertyValue('--primary').trim())).toBe('#0b7a70') for (const mode of navigationModes) { await test.step(`导航模式:${mode.label}`, async () => { await page.getByRole('button', { name: '界面与导航设置' }).click() const drawer = page.locator('.el-drawer:visible') await drawer.getByRole('button', { name: new RegExp(mode.label.replace('+', '\\+')) }).click() await expect(page.locator('.platform-shell')).toHaveAttribute('data-navigation-mode', mode.value) await page.keyboard.press('Escape') await page.reload() await expect(page.locator('.platform-shell')).toHaveAttribute('data-navigation-mode', mode.value) await waitForDashboard(page) await captureScreenshot(page, testInfo, `navigation-${mode.value}`) }) } await test.step('桌面侧栏收起、刷新持久化与展开恢复', async () => { await page.getByRole('button', { name: '界面与导航设置' }).click() const drawer = page.locator('.el-drawer:visible') await drawer.getByRole('button', { name: /顶部一级 \+ 左侧二级/ }).click() await page.keyboard.press('Escape') await expect(drawer).toBeHidden() const shell = page.locator('.platform-shell') await expect(shell).toHaveAttribute('data-navigation-mode', 'top-side') const collapseButton = page.getByRole('button', { name: '收起左侧导航' }) await expect(collapseButton).toBeVisible() await collapseButton.click() await expect(shell).toHaveClass(/sidebar-collapsed/) await expect(page.getByRole('button', { name: '展开左侧导航' })).toBeVisible() await captureScreenshot(page, testInfo, 'sidebar-collapsed') await page.reload() await waitForDashboard(page) await expect(shell).toHaveClass(/sidebar-collapsed/) const expandButton = page.getByRole('button', { name: '展开左侧导航' }) await expect(expandButton).toBeVisible() await expandButton.click() await expect(shell).not.toHaveClass(/sidebar-collapsed/) await expect(page.getByRole('button', { name: '收起左侧导航' })).toBeVisible() await captureScreenshot(page, testInfo, 'sidebar-expanded') await page.reload() await waitForDashboard(page) await expect(shell).not.toHaveClass(/sidebar-collapsed/) }) const themes = [ { label: '明亮', value: 'light', expected: 'light' }, { label: '暗黑', value: 'dark', expected: 'dark' }, { label: '跟随系统', value: 'system', expected: 'dark' }, ] as const for (const theme of themes) { await test.step(`主题模式:${theme.label}`, async () => { await page.getByRole('button', { name: '界面与导航设置' }).click() const drawer = page.locator('.el-drawer:visible') await drawer.getByRole('button', { name: new RegExp(theme.label) }).click() await expect(page.locator('html')).toHaveAttribute('data-theme', theme.expected) await page.keyboard.press('Escape') await page.reload() await expect(page.locator('html')).toHaveAttribute('data-theme', theme.expected) await waitForDashboard(page) if (theme.expected === 'dark') { const colors = await page.locator('.dashboard-metrics article').first().evaluate((element) => ({ foreground: getComputedStyle(element).color, background: getComputedStyle(element).backgroundColor, })) expect(contrastRatio(colors.foreground, colors.background)).toBeGreaterThanOrEqual(4.5) expect(await page.locator('html').evaluate((element) => getComputedStyle(element).getPropertyValue('--primary').trim())).toBe('#55d0bd') } await captureScreenshot(page, testInfo, `theme-${theme.value}`) }) } // 保持默认值,避免 headed 模式下人工继续检查时继承最后一次设置。 await page.getByRole('button', { name: '界面与导航设置' }).click() const drawer = page.locator('.el-drawer:visible') await drawer.getByRole('button', { name: /明亮/ }).click() await drawer.getByRole('button', { name: /顶部一级 \+ 左侧二级/ }).click() })