import { test, expect, type Page } from '@playwright/test' // 测试账号配置 const TEST_USERNAME = process.env.TEST_USERNAME || 'admin' const TEST_PASSWORD = process.env.TEST_PASSWORD || '123456' // 生成唯一的角色编码 const generateRoleCode = () => `TEST_${Date.now()}` // data-id 选择器辅助函数 const byDataId = (id: string) => `[data-id="${id}"]` test.describe('系统角色管理 CRUD 测试', () => { // 登录辅助函数 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('角色管理页面正确显示', async ({ page }) => { await login(page) await page.goto('/system/role') // 验证页面元素 await expect(page.locator(byDataId('btn-add'))).toBeVisible() await expect(page.locator(byDataId('btn-search'))).toBeVisible() await expect(page.locator(byDataId('btn-reset'))).toBeVisible() await expect(page.locator(byDataId('role-table'))).toBeVisible() // 验证表头 await expect(page.locator('thead').getByText('ID')).toBeVisible() await expect(page.locator('thead').getByText('角色名称')).toBeVisible() await expect(page.locator('thead').getByText('角色编码')).toBeVisible() await expect(page.locator('thead').getByText('用户数')).toBeVisible() await expect(page.locator('thead').getByText('状态')).toBeVisible() await expect(page.locator('thead').getByText('描述')).toBeVisible() }) test('查询和重置功能', async ({ page }) => { await login(page) await page.goto('/system/role') // 等待表格加载 await expect(page.locator(byDataId('role-table'))).toBeVisible() // 输入搜索关键词 await page.locator(byDataId('search-keyword')).fill('ADMIN') // 点击查询按钮 await page.locator(byDataId('btn-search')).click() await page.waitForTimeout(500) // 表格应该仍然可见 await expect(page.locator(byDataId('role-table'))).toBeVisible() // 点击重置按钮 await page.locator(byDataId('btn-reset')).click() await page.waitForTimeout(500) // 验证输入框被清空 await expect(page.locator(byDataId('search-keyword'))).toHaveValue('') // 表格应该仍然可见 await expect(page.locator(byDataId('role-table'))).toBeVisible() }) test('新增角色完整流程', async ({ page }) => { await login(page) await page.goto('/system/role') const roleCode = generateRoleCode() const roleName = `测试角色_${Date.now()}` // 点击新增按钮 await page.locator(byDataId('btn-add')).click() // 验证抽屉打开 const drawer = page.locator(byDataId('role-drawer')) await expect(drawer).toBeVisible() await expect(page.locator('.el-drawer__title')).toContainText('新增角色') // 填写表单 await drawer.locator(byDataId('input-name')).fill(roleName) await drawer.locator(byDataId('input-code')).fill(roleCode) await drawer.locator(byDataId('input-description')).fill('E2E测试创建的角色') // 等待提交按钮可用后点击 const submitBtn = page.locator(byDataId('btn-submit')) await expect(submitBtn).toBeEnabled() await submitBtn.click() // 等待成功消息或抽屉关闭 const successPromise = page .locator('.el-message--success') .waitFor({ timeout: 10000 }) .catch(() => null) const drawerClosePromise = expect(drawer).not.toBeVisible({ timeout: 10000 }) await Promise.race([successPromise, drawerClosePromise]) // 如果抽屉关闭了,说明操作已完成 await expect(drawer).not.toBeVisible({ timeout: 5000 }) }) test('编辑角色功能', async ({ page }) => { await login(page) await page.goto('/system/role') // 等待表格加载 await expect(page.locator(byDataId('role-table'))).toBeVisible() await page.waitForTimeout(1000) // 点击第一行的编辑按钮 const editBtn = page.locator(byDataId('btn-edit')).first() // 等待数据加载 await expect(editBtn).toBeVisible({ timeout: 10000 }) await editBtn.click() // 验证抽屉打开 const drawer = page.locator(byDataId('role-drawer')) await expect(drawer).toBeVisible() await expect(page.locator('.el-drawer__title')).toContainText('编辑角色') // 修改名称 const nameInput = drawer.locator(byDataId('input-name')) await nameInput.clear() await nameInput.fill(`编辑测试角色_${Date.now()}`) // 修改描述 const descInput = drawer.locator(byDataId('input-description')) await descInput.clear() await descInput.fill('E2E编辑测试') // 等待提交按钮可用后点击 const submitBtn = page.locator(byDataId('btn-submit')) await expect(submitBtn).toBeEnabled() await submitBtn.click() // 验证抽屉关闭 await expect(drawer).not.toBeVisible({ timeout: 15000 }) }) test('删除角色功能', async ({ page }) => { await login(page) await page.goto('/system/role') // 等待表格加载 await expect(page.locator(byDataId('role-table'))).toBeVisible() await page.waitForTimeout(1000) // 找到一个可删除的角色(非 ADMIN)的删除按钮 const deleteBtn = page.locator(`${byDataId('btn-delete')}:not([disabled])`).first() // 如果没有可删除的角色,跳过此测试 const count = await deleteBtn.count() if (count === 0) { test.skip() return } await deleteBtn.click() // 确认删除对话框出现 await expect(page.locator('.el-message-box')).toBeVisible() await page.locator('.el-message-box').getByRole('button', { name: '确定' }).click() // 等待确认对话框关闭 await expect(page.locator('.el-message-box')).not.toBeVisible({ timeout: 10000 }) // 验证表格仍然可见 await expect(page.locator(byDataId('role-table'))).toBeVisible() }) test('新增角色表单验证', async ({ page }) => { await login(page) await page.goto('/system/role') // 点击新增按钮 await page.locator(byDataId('btn-add')).click() await expect(page.locator(byDataId('role-drawer'))).toBeVisible() // 直接点击确定,不填写任何内容 await page.locator(byDataId('btn-submit')).click() // 验证显示验证错误 const drawer = page.locator(byDataId('role-drawer')) await expect(drawer.getByText('请输入角色名称')).toBeVisible() await expect(drawer.getByText('请输入角色编码')).toBeVisible() }) test('角色编码格式验证', async ({ page }) => { await login(page) await page.goto('/system/role') // 点击新增按钮 await page.locator(byDataId('btn-add')).click() const drawer = page.locator(byDataId('role-drawer')) await expect(drawer).toBeVisible() // 填写角色名称 await drawer.locator(byDataId('input-name')).fill('测试角色') // 填写不合法的角色编码(小写字母) await drawer.locator(byDataId('input-code')).fill('test_role') // 点击提交 await page.locator(byDataId('btn-submit')).click() // 验证显示格式错误 await expect(drawer.getByText(/大写字母/)).toBeVisible({ timeout: 5000 }) }) test('取消新增角色', async ({ page }) => { await login(page) await page.goto('/system/role') // 点击新增按钮 await page.locator(byDataId('btn-add')).click() const drawer = page.locator(byDataId('role-drawer')) await expect(drawer).toBeVisible() // 填写部分内容 await drawer.locator(byDataId('input-name')).fill('测试取消') await drawer.locator(byDataId('input-code')).fill('TEST_CANCEL') // 点击取消 await page.locator(byDataId('btn-cancel')).click() // 验证抽屉关闭 await expect(drawer).not.toBeVisible() }) test('从侧边栏导航到角色管理', async ({ page }) => { await login(page) // 展开系统管理菜单 await page.getByText('系统管理').first().click() await page.waitForTimeout(300) // 点击角色管理菜单项 await page.getByText('角色管理').first().click() // 验证跳转到角色管理页面 await expect(page).toHaveURL(/\/system\/role/) await expect(page.locator(byDataId('btn-add'))).toBeVisible() }) test('状态筛选功能', async ({ page }) => { await login(page) await page.goto('/system/role') // 等待表格加载 await expect(page.locator(byDataId('role-table'))).toBeVisible() // 选择启用状态 await page.locator(byDataId('search-status')).click() await page.getByText('启用').click() // 点击查询 await page.locator(byDataId('btn-search')).click() await page.waitForTimeout(500) // 表格应该仍然可见 await expect(page.locator(byDataId('role-table'))).toBeVisible() // 选择禁用状态 await page.locator(byDataId('search-status')).click() await page.getByText('禁用').click() // 点击查询 await page.locator(byDataId('btn-search')).click() await page.waitForTimeout(500) // 表格应该仍然可见 await expect(page.locator(byDataId('role-table'))).toBeVisible() }) test('ADMIN 角色不能删除', async ({ page }) => { await login(page) await page.goto('/system/role') // 等待表格加载 await expect(page.locator(byDataId('role-table'))).toBeVisible() await page.waitForTimeout(1000) // 找到 ADMIN 角色行的删除按钮 const adminRow = page.locator('tr').filter({ hasText: 'ADMIN' }) const adminDeleteBtn = adminRow.locator(byDataId('btn-delete')) // 如果找到了 ADMIN 角色,验证其删除按钮是禁用的 const count = await adminDeleteBtn.count() if (count > 0) { await expect(adminDeleteBtn).toBeDisabled() } }) })