|
|
@@ -0,0 +1,334 @@
|
|
|
+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()
|
|
|
+ })
|
|
|
+})
|