110 lines
4.4 KiB
TypeScript
110 lines
4.4 KiB
TypeScript
import { expect, test } from '@playwright/test'
|
|
|
|
/**
|
|
* 双列菜单布局(layout = 'sidebar-mixed-nav')渲染回归 —— 针对 ERP 应用。
|
|
*
|
|
* 组件编过 ≠ 页面渲染对,所以这里在真实浏览器里断言 DOM 结构:
|
|
* 1. 切到双列布局后,左侧图标栏 + 第二列都存在;
|
|
* 2. 单列侧栏(.layout-sidebar)不再渲染;
|
|
* 3. 第一列只显示顶级项(图标 + 标题),不含子项;
|
|
* 4. 第二列显示当前顶级分组的子项;
|
|
* 5. 点第一列另一个分组,第二列内容随之切换。
|
|
*
|
|
* 说明:放在组件库 e2e 工程里跑(已有 Playwright 配置),
|
|
* 通过 ERP_URL 指向 ERP dev server,避免给 ERP 应用引入 Playwright 依赖。
|
|
*/
|
|
|
|
const ERP_URL = process.env.ERP_URL ?? 'http://localhost:5090'
|
|
|
|
/** 通过偏好设置把布局切成双列菜单(直接写 localStorage,避免点 UI 的耦合) */
|
|
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
|
|
pref.sidebar = sidebar
|
|
|
|
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)
|
|
}
|
|
|
|
test.describe('ERP 双列菜单布局', () => {
|
|
test('渲染出图标栏 + 第二列,且不再渲染单列侧栏', async ({ page }) => {
|
|
await useMixedNavLayout(page)
|
|
|
|
// 布局根节点带上了对应 class
|
|
await expect(page.locator('.layout--sidebar-mixed-nav')).toHaveCount(1)
|
|
|
|
// 两列都在
|
|
await expect(page.locator('.layout-mixed-rail')).toBeVisible()
|
|
await expect(page.locator('.layout-mixed-column')).toBeVisible()
|
|
|
|
// 单列侧栏不该出现
|
|
await expect(page.locator('.layout-sidebar')).toHaveCount(0)
|
|
})
|
|
|
|
test('第一列只放顶级项,第二列放该分组的子项', async ({ page }) => {
|
|
await useMixedNavLayout(page)
|
|
|
|
// 进入海运下的某个叶子,使「海运」成为当前顶级分组
|
|
await page.goto(`${ERP_URL}/ocean/booking`)
|
|
await page.waitForTimeout(400)
|
|
|
|
const railItems = page.locator('.layout-mixed-rail__item')
|
|
await expect(railItems.first()).toBeVisible()
|
|
|
|
// 第一列项数应等于顶级菜单数(6 个业务域)
|
|
const railCount = await railItems.count()
|
|
expect(railCount, '第一列应为顶级菜单数量').toBeGreaterThanOrEqual(5)
|
|
|
|
// 第一列里不应出现二级标题(如「订舱单」)
|
|
const railText = (await railItems.allTextContents()).join('|')
|
|
expect(railText, '第一列不应包含子项「订舱单」').not.toContain('订舱单')
|
|
expect(railText, '第一列应含顶级项「海运」').toContain('海运')
|
|
|
|
// 第一列里「海运」应是选中态
|
|
const activeRail = page.locator('.layout-mixed-rail__item.is-active')
|
|
await expect(activeRail).toHaveCount(1)
|
|
await expect(activeRail).toContainText('海运')
|
|
|
|
// 第二列显示「海运」的子项
|
|
const columnText = await page.locator('.layout-mixed-column').innerText()
|
|
expect(columnText, '第二列应含「海运」的子项').toContain('订舱单')
|
|
})
|
|
|
|
test('点第一列切换到别的分组,第二列内容随之变化', async ({ page }) => {
|
|
await useMixedNavLayout(page)
|
|
await page.goto(`${ERP_URL}/ocean/booking`)
|
|
await page.waitForTimeout(400)
|
|
|
|
const columnBefore = await page.locator('.layout-mixed-column').innerText()
|
|
expect(columnBefore).toContain('订舱单')
|
|
|
|
// 点第一列的「财务」
|
|
await page.locator('.layout-mixed-rail__item', { hasText: '财务' }).click()
|
|
await page.waitForTimeout(500)
|
|
|
|
const columnAfter = await page.locator('.layout-mixed-column').innerText()
|
|
expect(columnAfter, '切到财务后第二列应显示财务的子项').toContain('应收费用')
|
|
expect(columnAfter, '第二列不应再显示海运的子项').not.toContain('订舱单')
|
|
|
|
// 「财务」应变成选中态
|
|
await expect(page.locator('.layout-mixed-rail__item.is-active')).toContainText('财务')
|
|
})
|
|
})
|