You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

115 rivejä
5.8 KiB

  1. import { captureScreenshot, expect, test } from './fixtures'
  2. import { loginAsAdmin } from './helpers'
  3. const navigationModes = [
  4. { label: '顶部一级 + 左侧二级', value: 'top-side' },
  5. { label: '左侧分组菜单', value: 'side' },
  6. { label: '顶部下拉菜单', value: 'top' },
  7. ] as const
  8. const waitForDashboard = async (page: import('@playwright/test').Page) => {
  9. await expect(page.locator('.platform-shell')).toBeVisible()
  10. await expect(page.getByRole('heading', { name: '前后端联调状态' })).toBeVisible()
  11. await expect(page.getByText('2/2', { exact: true })).toBeVisible({ timeout: 15_000 })
  12. await expect(page.locator('.dashboard-metrics article').first().locator('b')).not.toHaveText('0', { timeout: 15_000 })
  13. }
  14. const contrastRatio = (foreground: string, background: string) => {
  15. const channels = (value: string) => (value.match(/[\d.]+/g) ?? []).slice(0, 3).map(Number)
  16. const luminance = (value: string) => channels(value).reduce((sum, channel, index) => {
  17. const normalized = channel / 255
  18. const linear = normalized <= .03928 ? normalized / 12.92 : ((normalized + .055) / 1.055) ** 2.4
  19. return sum + linear * [.2126, .7152, .0722][index]!
  20. }, 0)
  21. const [lighter, darker] = [luminance(foreground), luminance(background)].sort((left, right) => right - left)
  22. return (lighter! + .05) / (darker! + .05)
  23. }
  24. test('三种导航模式与三种主题可以持久化', async ({ page }, testInfo) => {
  25. await page.emulateMedia({ colorScheme: 'dark' })
  26. await loginAsAdmin(page)
  27. await expect(page.locator('.platform-shell')).toHaveAttribute('data-navigation-mode', 'top-side')
  28. await expect(page.locator('html')).toHaveAttribute('data-theme', 'light')
  29. await expect(page.locator('.platform-header')).toHaveCSS('background-color', 'rgb(255, 255, 255)')
  30. expect(await page.locator('html').evaluate((element) => getComputedStyle(element).getPropertyValue('--primary').trim())).toBe('#0b7a70')
  31. for (const mode of navigationModes) {
  32. await test.step(`导航模式:${mode.label}`, async () => {
  33. await page.getByRole('button', { name: '界面与导航设置' }).click()
  34. const drawer = page.locator('.el-drawer:visible')
  35. await drawer.getByRole('button', { name: new RegExp(mode.label.replace('+', '\\+')) }).click()
  36. await expect(page.locator('.platform-shell')).toHaveAttribute('data-navigation-mode', mode.value)
  37. await page.keyboard.press('Escape')
  38. await page.reload()
  39. await expect(page.locator('.platform-shell')).toHaveAttribute('data-navigation-mode', mode.value)
  40. await waitForDashboard(page)
  41. await captureScreenshot(page, testInfo, `navigation-${mode.value}`)
  42. })
  43. }
  44. await test.step('桌面侧栏收起、刷新持久化与展开恢复', async () => {
  45. await page.getByRole('button', { name: '界面与导航设置' }).click()
  46. const drawer = page.locator('.el-drawer:visible')
  47. await drawer.getByRole('button', { name: /顶部一级 \+ 左侧二级/ }).click()
  48. await page.keyboard.press('Escape')
  49. await expect(drawer).toBeHidden()
  50. const shell = page.locator('.platform-shell')
  51. await expect(shell).toHaveAttribute('data-navigation-mode', 'top-side')
  52. const collapseButton = page.getByRole('button', { name: '收起左侧导航' })
  53. await expect(collapseButton).toBeVisible()
  54. await collapseButton.click()
  55. await expect(shell).toHaveClass(/sidebar-collapsed/)
  56. await expect(page.getByRole('button', { name: '展开左侧导航' })).toBeVisible()
  57. await captureScreenshot(page, testInfo, 'sidebar-collapsed')
  58. await page.reload()
  59. await waitForDashboard(page)
  60. await expect(shell).toHaveClass(/sidebar-collapsed/)
  61. const expandButton = page.getByRole('button', { name: '展开左侧导航' })
  62. await expect(expandButton).toBeVisible()
  63. await expandButton.click()
  64. await expect(shell).not.toHaveClass(/sidebar-collapsed/)
  65. await expect(page.getByRole('button', { name: '收起左侧导航' })).toBeVisible()
  66. await captureScreenshot(page, testInfo, 'sidebar-expanded')
  67. await page.reload()
  68. await waitForDashboard(page)
  69. await expect(shell).not.toHaveClass(/sidebar-collapsed/)
  70. })
  71. const themes = [
  72. { label: '明亮', value: 'light', expected: 'light' },
  73. { label: '暗黑', value: 'dark', expected: 'dark' },
  74. { label: '跟随系统', value: 'system', expected: 'dark' },
  75. ] as const
  76. for (const theme of themes) {
  77. await test.step(`主题模式:${theme.label}`, async () => {
  78. await page.getByRole('button', { name: '界面与导航设置' }).click()
  79. const drawer = page.locator('.el-drawer:visible')
  80. await drawer.getByRole('button', { name: new RegExp(theme.label) }).click()
  81. await expect(page.locator('html')).toHaveAttribute('data-theme', theme.expected)
  82. await page.keyboard.press('Escape')
  83. await page.reload()
  84. await expect(page.locator('html')).toHaveAttribute('data-theme', theme.expected)
  85. await waitForDashboard(page)
  86. if (theme.expected === 'dark') {
  87. const colors = await page.locator('.dashboard-metrics article').first().evaluate((element) => ({
  88. foreground: getComputedStyle(element).color,
  89. background: getComputedStyle(element).backgroundColor,
  90. }))
  91. expect(contrastRatio(colors.foreground, colors.background)).toBeGreaterThanOrEqual(4.5)
  92. expect(await page.locator('html').evaluate((element) => getComputedStyle(element).getPropertyValue('--primary').trim())).toBe('#55d0bd')
  93. }
  94. await captureScreenshot(page, testInfo, `theme-${theme.value}`)
  95. })
  96. }
  97. // 保持默认值,避免 headed 模式下人工继续检查时继承最后一次设置。
  98. await page.getByRole('button', { name: '界面与导航设置' }).click()
  99. const drawer = page.locator('.el-drawer:visible')
  100. await drawer.getByRole('button', { name: /明亮/ }).click()
  101. await drawer.getByRole('button', { name: /顶部一级 \+ 左侧二级/ }).click()
  102. })