|
- import type { Page, Route } from '@playwright/test'
-
- import { captureScreenshot, expect, test } from './fixtures'
-
- const envelope = (data: unknown) => ({
- code: 0,
- message: 'ok',
- data,
- timestamp: Date.now(),
- requestId: 'sidebar-bootstrap-regression',
- })
-
- const profile = {
- user: {
- id: 'sidebar-admin',
- username: 'sidebar-admin',
- displayName: '系统管理员',
- departmentId: 'system',
- departmentName: '系统管理',
- enabled: true,
- mustChangePassword: false,
- version: 1,
- },
- roles: [{
- id: 'admin-role',
- code: 'ADMIN',
- name: '系统管理员',
- shortName: '系统',
- enabled: true,
- builtIn: true,
- isSuperAdmin: true,
- dataScope: 'ALL',
- }],
- activeRoleId: 'admin-role',
- permissions: ['*'],
- authorizationMode: 'SINGLE_ACTIVE',
- loginTime: new Date().toISOString(),
- }
-
- const navigation = [
- {
- id: 'agents', code: 'agents', name: '数字人智能体', type: 'GROUP', sortOrder: 10, enabled: true,
- path: '/agents/manage', icon: 'Platform', permissionCodes: [],
- children: [
- { id: 'agent-management', code: 'agent-management', name: '虚拟教员智能体', type: 'PAGE', sortOrder: 10, enabled: true, path: '/agents/manage', icon: 'Management', permissionCodes: [], children: [] },
- { id: 'agent-projects', code: 'agent-projects', name: '智能体组别管理', type: 'PAGE', sortOrder: 20, enabled: true, path: '/agents/projects', icon: 'Files', permissionCodes: [], children: [] },
- ],
- },
- {
- id: 'assets', code: 'assets', name: '资源中心', type: 'GROUP', sortOrder: 20, enabled: true,
- path: '/assets/avatars', icon: 'Collection', permissionCodes: [],
- children: [
- { id: 'avatars', code: 'avatars', name: '数字形象', type: 'PAGE', sortOrder: 10, enabled: true, path: '/assets/avatars', icon: 'Avatar', permissionCodes: [], children: [] },
- { id: 'ai-models', code: 'ai-models', name: 'AI模型', type: 'PAGE', sortOrder: 20, enabled: true, path: '/assets/ai-models', icon: 'MagicStick', permissionCodes: [], children: [] },
- ],
- },
- {
- id: 'settings', code: 'settings', name: '系统设置', type: 'GROUP', sortOrder: 30, enabled: true,
- path: '/settings/general', icon: 'Setting', permissionCodes: [],
- children: [
- { id: 'settings-general', code: 'settings-general', name: '基础设置', type: 'PAGE', sortOrder: 10, enabled: true, path: '/settings/general', icon: 'Setting', permissionCodes: [], children: [] },
- { id: 'settings-menus', code: 'settings-menus', name: '菜单管理', type: 'PAGE', sortOrder: 20, enabled: true, path: '/settings/menus', icon: 'Menu', permissionCodes: [], children: [] },
- ],
- },
- ]
-
- async function installShellMocks(page: Page, holdBootstrap = false) {
- let releaseBootstrap = () => {}
- const bootstrapGate = holdBootstrap
- ? new Promise<void>((resolve) => { releaseBootstrap = resolve })
- : Promise.resolve()
-
- await page.addInitScript(() => {
- window.localStorage.setItem('ai-person:web:access-token:v1', 'sidebar-bootstrap-token')
- window.localStorage.setItem('ai-person:web:remember:v1', '1')
- })
-
- const fulfillApi = async (route: Route) => {
- const pathname = new URL(route.request().url()).pathname
- let data: unknown = {}
-
- if (pathname.endsWith('/system-config/public') || pathname.endsWith('/auth/me')) {
- await bootstrapGate
- }
- if (pathname.endsWith('/auth/me')) data = profile
- else if (pathname.endsWith('/menus/navigation')) data = navigation
- else if (pathname.endsWith('/overview')) {
- data = { stats: {}, agents: [], videos: [], jobs: [] }
- }
-
- await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(envelope(data)) })
- }
-
- await page.route('**/api/auth/v1/**', fulfillApi)
- await page.route('**/api/v1/**', fulfillApi)
-
- return releaseBootstrap
- }
-
- test('App 模块尚未加载时由 HTML 直接展示启动状态', async ({ page }) => {
- let releaseApp = () => {}
- const appGate = new Promise<void>((resolve) => { releaseApp = resolve })
- await page.route('**/src/App.vue*', async (route) => {
- await appGate
- await route.continue()
- })
-
- try {
- await page.goto('/overview', { waitUntil: 'commit' })
- await expect(page.locator('.static-bootstrap-screen')).toBeVisible()
- await expect(page.getByText('正在启动系统')).toBeVisible()
- await expect(page.getByText('正在加载应用资源,请稍候…')).toBeVisible()
- } finally {
- releaseApp()
- }
-
- await expect(page.locator('.login-shell')).toBeVisible()
- await expect(page.locator('.static-bootstrap-screen')).toHaveCount(0)
- })
-
- test('认证与公开配置请求未完成时展示品牌启动状态', async ({ page }, testInfo) => {
- const releaseBootstrap = await installShellMocks(page, true)
- await page.goto('/overview')
-
- await expect(page.locator('.app-bootstrap-screen')).toBeVisible()
- await expect(page.getByText('正在进入系统')).toBeVisible()
- await expect(page.getByText('正在验证登录状态并加载工作台,请稍候…')).toBeVisible()
- await captureScreenshot(page, testInfo, 'bootstrap-loading-not-blank')
-
- releaseBootstrap()
- await expect(page.locator('.app-shell')).toBeVisible()
- await expect(page.locator('.app-bootstrap-screen')).toHaveCount(0)
- })
-
- test('侧栏使用 Element Plus 菜单并保持手风琴展开', async ({ page }, testInfo) => {
- await installShellMocks(page)
- await page.goto('/overview')
- await expect(page.locator('.app-shell')).toBeVisible()
-
- const menu = page.locator('#main-sidebar .sidebar-menu')
- const groups = menu.locator(':scope > .el-sub-menu')
- await expect(menu).toHaveClass(/el-menu/)
- await expect(groups).toHaveCount(3)
-
- const first = groups.nth(0)
- const second = groups.nth(1)
- const third = groups.nth(2)
-
- await first.locator(':scope > .el-sub-menu__title').click()
- await expect(first).toHaveClass(/is-opened/)
-
- await second.locator(':scope > .el-sub-menu__title').click()
- await expect(second).toHaveClass(/is-opened/)
- await expect(first).not.toHaveClass(/is-opened/)
-
- await third.locator(':scope > .el-sub-menu__title').click()
- await expect(third).toHaveClass(/is-opened/)
- await expect(second).not.toHaveClass(/is-opened/)
- await expect(menu.locator(':scope > .el-sub-menu.is-opened')).toHaveCount(1)
- await captureScreenshot(page, testInfo, 'element-menu-accordion')
- })
|