|
- import type { Page, Route } from '@playwright/test'
-
- import { captureScreenshot, expect, test } from './fixtures'
-
- /**
- * 内容制作列表工具栏回归。
- *
- * 工具栏用显式筛选:关键词与三个下拉都要点「搜索」或在输入框回车才生效,「重置」清空全部条件;
- * 不放「刷新」按钮,也不重复显示总条数(条数由底部分页承担)。
- * 关键词框比全局 390px 收窄三分之一到 260px,给下拉与按钮留横向空间。
- * 「新建」动作落在工具栏右端,页面不使用 SystemPageHeader,白底内容区有最小高度。
- */
-
- const json = (route: Route, data: unknown) => route.fulfill({
- status: 200,
- contentType: 'application/json',
- body: JSON.stringify({ code: 0, message: '成功', data, timestamp: Date.now(), requestId: 'e2e-toolbar' }),
- })
-
- /** 记录每次列表查询的 keyword 与 status,用于断言即时筛选与防抖。 */
- type ListQuery = { keyword: string; status: string }
-
- async function mockContentListApi(page: Page, queries: ListQuery[]) {
- await page.route(/\/api\/(?:auth|tran)\//, async (route) => {
- const url = new URL(route.request().url())
- const path = url.pathname
-
- if (path.endsWith('/auth/me')) {
- return json(route, {
- userId: '1',
- username: 'admin',
- displayName: '系统管理员',
- departmentId: '110',
- departmentName: '信息中心',
- activeRoleId: '1',
- authorizationMode: 'SINGLE_ACTIVE',
- mustChangePassword: false,
- roles: [{ id: '1', code: 'ADMIN', name: '管理员', shortName: '管', enabled: true, builtIn: true, isSuperAdmin: true, dataScopeCode: 'ALL', version: 1 }],
- // 组件按 content.create / content.update / content.publish 判断动作权限,
- // 页面权限用 content.model;两套都要给,否则「新建」会退化成只读标签
- permissions: ['content.model', 'content.create', 'content.update', 'content.delete', 'content.publish'],
- })
- }
- if (path.endsWith('/content/projects/summary')) {
- return json(route, { total: 3, draft: 1, review: 0, published: 2, byType: [], byTrainingMode: [] })
- }
- if (path.endsWith('/content/projects')) {
- queries.push({
- keyword: url.searchParams.get('keyword') ?? '',
- status: url.searchParams.get('status') ?? '',
- })
- return json(route, { records: [], total: 0, page: 1, size: 10 })
- }
- if (path.endsWith('/auth/ping')) return json(route, { service: 'ut-auth', port: 6101, status: 'UP' })
- if (path.endsWith('/tran/ping')) return json(route, { service: 'ut-tran', port: 6102, status: 'UP' })
- return json(route, {})
- })
- }
-
- test('内容列表工具栏显式搜索重置、无刷新按钮且筛选项左对齐', async ({ page }, testInfo) => {
- const queries: ListQuery[] = []
- await page.setViewportSize({ width: 1920, height: 900 })
- await mockContentListApi(page, queries)
- await page.addInitScript(() => {
- window.sessionStorage.setItem('unreal-tran:web:access-token:v1', 'e2e-mock-token')
- })
-
- await page.goto('/content/models')
- // 页头卡已去掉,没有 h1 可等;以列表卡片作为就绪锚点
- await expect(page.locator('.content-list-card')).toBeVisible()
-
- const toolbar = page.locator('.content-filter')
- // 工具栏用显式的搜索与重置,不放刷新按钮
- await expect(toolbar.getByRole('button', { name: '刷新' })).toHaveCount(0)
- const searchButton = toolbar.getByRole('button', { name: '搜索' })
- const resetButton = toolbar.getByRole('button', { name: '重置' })
- await expect(searchButton).toBeVisible()
- await expect(resetButton).toBeVisible()
-
- // 页头卡已去掉,「新建」动作落在工具条右端;总条数由底部分页承担,工具条里不再重复
- await expect(page.locator('.system-page-header')).toHaveCount(0)
- await expect(toolbar.locator('.list-result-count')).toHaveCount(0)
- const createButton = toolbar.getByRole('button', { name: '新建模型' })
- await expect(createButton).toBeVisible()
-
- const keywordInput = toolbar.locator('.el-input').first()
- const [toolbarBox, keywordBox, searchBox, resetBox, createBox] = await Promise.all([
- toolbar.boundingBox(),
- keywordInput.boundingBox(),
- searchButton.boundingBox(),
- resetButton.boundingBox(),
- createButton.boundingBox(),
- ])
- for (const box of [toolbarBox, keywordBox, searchBox, resetBox, createBox]) expect(box).not.toBeNull()
- // 关键词框比全局 390px 收窄三分之一
- expect(keywordBox!.width).toBeGreaterThan(230)
- expect(keywordBox!.width).toBeLessThanOrEqual(270)
- // 搜索、重置按内容宽度显示,且和筛选控件一起靠左,不被 flex-grow 撑开
- expect(searchBox!.width).toBeLessThanOrEqual(110)
- expect(resetBox!.width).toBeLessThanOrEqual(110)
- expect(resetBox!.x + resetBox!.width).toBeLessThan(toolbarBox!.x + toolbarBox!.width - 200)
- // 新建贴住工具条右端
- expect(createBox!.x + createBox!.width).toBeGreaterThan(toolbarBox!.x + toolbarBox!.width - 40)
-
- // 白底内容区有最小高度,数据少时下方不出现大片空白
- const cardBox = await page.locator('.content-list-card').boundingBox()
- expect(cardBox!.height).toBeGreaterThan(600)
-
- // 空态提供「清除筛选」,与工具栏的重置同一行为
- const empty = page.locator('.asset-row-empty')
- await expect(empty).toContainText('没有符合条件的模型')
- await expect(empty.getByRole('button', { name: '清除筛选' })).toBeVisible()
-
- await captureScreenshot(page, testInfo, 'content-filter-explicit')
-
- // 只输入关键词不查询,必须点搜索或回车才发请求
- const keywordRequests = () => queries.filter((item) => item.keyword === '挖掘机').length
- await toolbar.locator('input').first().fill('挖掘机')
- await page.waitForTimeout(700)
- expect(keywordRequests(), '未点搜索前不应发出查询').toBe(0)
- await searchButton.click()
- await expect.poll(keywordRequests, { timeout: 5_000 }).toBeGreaterThanOrEqual(1)
-
- // 下拉切换同样要点搜索才生效
- const statusRequests = () => queries.filter((item) => item.status === 'PUBLISHED').length
- await toolbar.locator('.el-select').first().click()
- await page.getByRole('option', { name: '已发布', exact: true }).click()
- await page.waitForTimeout(500)
- expect(statusRequests(), '未点搜索前状态筛选不应生效').toBe(0)
- await searchButton.click()
- await expect.poll(statusRequests, { timeout: 5_000 }).toBeGreaterThanOrEqual(1)
-
- // 重置清空关键词并重新查询
- await resetButton.click()
- await expect(toolbar.locator('input').first()).toHaveValue('')
- await expect.poll(() => queries.at(-1)?.keyword ?? 'x', { timeout: 5_000 }).toBe('')
-
- await page.setViewportSize({ width: 760, height: 900 })
- await page.reload()
- await expect(page.locator('.content-list-card')).toBeVisible()
- const responsiveToolbar = page.locator('.content-filter')
- const hasHorizontalOverflow = await responsiveToolbar.evaluate((element) => element.scrollWidth > element.clientWidth + 1)
- expect(hasHorizontalOverflow).toBe(false)
- const responsiveSearchWidth = await responsiveToolbar.getByRole('button', { name: '搜索' })
- .evaluate((element) => element.getBoundingClientRect().width)
- expect(responsiveSearchWidth).toBeLessThanOrEqual(110)
- await captureScreenshot(page, testInfo, 'content-filter-responsive')
- })
|