import { test, expect, type Page } from '@playwright/test' // 测试账号配置 const TEST_USERNAME = process.env.TEST_USERNAME || 'admin' const TEST_PASSWORD = process.env.TEST_PASSWORD || '123456' // 登录辅助函数 async function login(page: Page) { await page.goto('/login') await page.evaluate(() => { localStorage.clear() document.cookie.split(';').forEach((c) => { document.cookie = c.replace(/^ +/, '').replace(/=.*/, '=;expires=' + new Date().toUTCString() + ';path=/') }) }) await page.reload() await page.getByPlaceholder('用户名').fill(TEST_USERNAME) await page.getByPlaceholder('密码').fill(TEST_PASSWORD) await page.getByRole('button', { name: '登录' }).click() await expect(page).not.toHaveURL(/\/login/, { timeout: 15000 }) } test.describe('LiveStream 管理 - 搜索功能测试', () => { /** * 按 Stream SN 搜索 */ test('按 Stream SN 搜索 - 验证参数传递', async ({ page }) => { await login(page) await page.goto('/live-stream') // 等待表格加载 await page.waitForSelector('tbody tr', { timeout: 10000 }) // 设置 API 拦截来验证参数 let requestBody: any = null await page.route('**/admin/live-stream/list', async (route) => { const request = route.request() if (request.method() === 'POST') { requestBody = request.postDataJSON() } await route.continue() }) // 在 stream sn 搜索框输入 await page.getByPlaceholder('stream sn').fill('stream_123') // 点击查询 await page.getByRole('button', { name: '查询' }).click() await page.waitForTimeout(1000) // 验证请求参数中包含 streamSn expect(requestBody).not.toBeNull() expect(requestBody.streamSn).toBe('stream_123') }) /** * 按 Name 搜索 */ test('按 Name 搜索 - 验证参数传递', async ({ page }) => { await login(page) await page.goto('/live-stream') // 等待表格加载 await page.waitForSelector('tbody tr', { timeout: 10000 }) // 设置 API 拦截 let requestBody: any = null await page.route('**/admin/live-stream/list', async (route) => { const request = route.request() if (request.method() === 'POST') { requestBody = request.postDataJSON() } await route.continue() }) // 在 name 搜索框输入 await page.getByPlaceholder('name').fill('测试流') // 点击查询 await page.getByRole('button', { name: '查询' }).click() await page.waitForTimeout(1000) // 验证请求参数中包含 name expect(requestBody).not.toBeNull() expect(requestBody.name).toBe('测试流') }) /** * 按 LSS 搜索 */ test('按 LSS 搜索 - 验证参数传递', async ({ page }) => { await login(page) await page.goto('/live-stream') // 等待表格加载 await page.waitForSelector('tbody tr', { timeout: 10000 }) // 设置 API 拦截 let requestBody: any = null await page.route('**/admin/live-stream/list', async (route) => { const request = route.request() if (request.method() === 'POST') { requestBody = request.postDataJSON() } await route.continue() }) // 点击 LSS 下拉框 await page.locator('.el-select').filter({ hasText: 'LSS' }).click() await page.waitForTimeout(300) // 选择第一个 LSS 选项(如果有) const firstOption = page.locator('.el-select-dropdown__item').first() if (await firstOption.isVisible()) { const lssValue = await firstOption.textContent() await firstOption.click() // 点击查询 await page.getByRole('button', { name: '查询' }).click() await page.waitForTimeout(1000) // 验证请求参数中包含 lssId expect(requestBody).not.toBeNull() expect(requestBody.lssId).toBe(lssValue?.trim()) } }) /** * 按设备ID搜索 */ test('按设备ID搜索 - 验证参数传递', async ({ page }) => { await login(page) await page.goto('/live-stream') // 等待表格加载 await page.waitForSelector('tbody tr', { timeout: 10000 }) // 设置 API 拦截 let requestBody: any = null await page.route('**/admin/live-stream/list', async (route) => { const request = route.request() if (request.method() === 'POST') { requestBody = request.postDataJSON() } await route.continue() }) // 在设备ID搜索框输入 await page.getByPlaceholder('设备ID').fill('EEE1') // 点击查询 await page.getByRole('button', { name: '查询' }).click() await page.waitForTimeout(1000) // 验证请求参数中包含 cameraId expect(requestBody).not.toBeNull() expect(requestBody.cameraId).toBe('EEE1') }) /** * 组合搜索 - 所有字段 */ test('组合搜索 - 多字段同时搜索', async ({ page }) => { await login(page) await page.goto('/live-stream') // 等待表格加载 await page.waitForSelector('tbody tr', { timeout: 10000 }) // 设置 API 拦截 let requestBody: any = null await page.route('**/admin/live-stream/list', async (route) => { const request = route.request() if (request.method() === 'POST') { requestBody = request.postDataJSON() } await route.continue() }) // 填入所有搜索条件 await page.getByPlaceholder('stream sn').fill('stream_001') await page.getByPlaceholder('name').fill('测试') await page.getByPlaceholder('设备ID').fill('EEE1') // 点击查询 await page.getByRole('button', { name: '查询' }).click() await page.waitForTimeout(1000) // 验证请求参数包含所有字段 expect(requestBody).not.toBeNull() expect(requestBody.streamSn).toBe('stream_001') expect(requestBody.name).toBe('测试') expect(requestBody.cameraId).toBe('EEE1') }) /** * 重置搜索条件 */ test('重置搜索条件 - 清空所有输入', async ({ page }) => { await login(page) await page.goto('/live-stream') // 填入搜索条件 await page.getByPlaceholder('stream sn').fill('test-sn') await page.getByPlaceholder('name').fill('test-name') await page.getByPlaceholder('设备ID').fill('test-device') // 点击重置 await page.getByRole('button', { name: '重置' }).click() await page.waitForTimeout(300) // 验证搜索条件已清空 await expect(page.getByPlaceholder('stream sn')).toHaveValue('') await expect(page.getByPlaceholder('name')).toHaveValue('') await expect(page.getByPlaceholder('设备ID')).toHaveValue('') }) }) test.describe('LiveStream 管理 - BUG 回归测试', () => { /** * BUG 回归测试:按设备ID搜索应该只返回匹配的记录 * * 问题描述: * - 前端已修复:searchForm.cameraId 参数现在会正确传递给 API * - 后端待修复:API 目前未实现 cameraId 过滤,返回所有数据 * * 预期行为: * - 搜索设备ID "EEE1" 应该只返回 1 条记录 * - 该记录的设备ID列应该显示 "EEE1" * * 当前状态:测试会失败,等待后端实现过滤 */ test('BUG: 按设备ID搜索 EEE1 应该只返回 1 条匹配记录', async ({ page }) => { await login(page) await page.goto('/live-stream') // 等待表格加载 await page.waitForSelector('tbody tr', { timeout: 10000 }) await page.waitForTimeout(500) // 在设备ID搜索框输入 "EEE1" await page.getByPlaceholder('设备ID').fill('EEE1') // 点击查询按钮 await page.getByRole('button', { name: '查询' }).click() // 等待搜索结果加载 await page.waitForTimeout(1000) // 验证:应该只有 1 条记录 const tableRows = page.locator('tbody tr') const rowCount = await tableRows.count() expect(rowCount).toBe(1) // 验证:该记录的设备ID应该是 "EEE1" const firstRowDeviceId = await tableRows.first().locator('td').nth(3).textContent() expect(firstRowDeviceId?.trim()).toBe('EEE1') // 验证:分页显示 Total 1 await expect(page.locator('.el-pagination')).toContainText('Total 1') }) }) test.describe('LiveStream 管理 - 页面功能测试', () => { test('LiveStream 管理页面正确显示', async ({ page }) => { await login(page) await page.goto('/live-stream') // 验证页面标题 await expect(page.locator('text=LiveStream 管理')).toBeVisible() // 验证搜索表单元素 await expect(page.getByPlaceholder('stream sn')).toBeVisible() await expect(page.getByPlaceholder('name')).toBeVisible() await expect(page.getByPlaceholder('设备ID')).toBeVisible() await expect(page.getByRole('button', { name: '查询' })).toBeVisible() await expect(page.getByRole('button', { name: '重置' })).toBeVisible() await expect(page.getByRole('button', { name: '新增' })).toBeVisible() // 验证表头 await expect(page.locator('th:has-text("Stream SN"), th:has-text("stream sn")')).toBeVisible() await expect(page.locator('th:has-text("Name"), th:has-text("名称")')).toBeVisible() await expect(page.locator('th:has-text("LSS")')).toBeVisible() await expect(page.locator('th:has-text("Device ID"), th:has-text("设备ID")')).toBeVisible() }) test('打开新增 LiveStream 抽屉', async ({ page }) => { await login(page) await page.goto('/live-stream') // 点击新增按钮 await page.getByRole('button', { name: '新增' }).click() // 验证抽屉打开 const drawer = page.locator('.el-drawer').filter({ hasText: '新增 Live Stream' }) await expect(drawer).toBeVisible({ timeout: 5000 }) // 验证表单元素 await expect(drawer.locator('label:has-text("名称")')).toBeVisible() await expect(drawer.locator('label:has-text("LSS 节点")')).toBeVisible() await expect(drawer.locator('label:has-text("摄像头")')).toBeVisible() // 关闭抽屉 await drawer.getByRole('button', { name: '取消' }).click() await expect(drawer).not.toBeVisible({ timeout: 5000 }) }) test('分页功能正常', async ({ page }) => { await login(page) await page.goto('/live-stream') // 等待表格加载 await page.waitForTimeout(1000) // 验证分页组件存在 const pagination = page.locator('.el-pagination') await expect(pagination).toBeVisible() // 验证 Total 显示 await expect(pagination.locator('text=/Total \\d+/')).toBeVisible() }) test('从侧边栏导航到 LiveStream 管理', async ({ page }) => { await login(page) // 点击侧边栏 LiveStream 管理菜单项 await page.getByText('LiveStream 管理').first().click() // 验证跳转到 LiveStream 管理页面 await expect(page).toHaveURL(/\/live-stream/) await expect(page.locator('text=LiveStream 管理')).toBeVisible() }) })