/** * 登录和认证流程端到端测试 * 使用 Playwright 进行自动化测试 */ import { test, expect, Page } from '@playwright/test'; // 测试配置 const TEST_CONFIG = { baseURL: 'http://localhost:5173', timeout: 30000, // 测试账号信息 testUser: { username: 'admin', password: '111111', }, // 超级管理员账号 superAdmin: { username: 'super_admin', password: 'super123456', }, // 普通用户账号 normalUser: { username: 'test_user', password: 'test123456', }, }; test.describe('登录和认证流程测试', () => { test.beforeEach(async ({ page }) => { // 设置较长的超时时间 test.setTimeout(TEST_CONFIG.timeout); // 访问登录页面 await page.goto(TEST_CONFIG.baseURL); // 等待页面加载完成 await page.waitForLoadState('networkidle'); }); test('应该正确显示登录页面', async ({ page }) => { // 检查页面标题 await expect(page).toHaveTitle(/Telegram Management System|登录/); // 检查登录表单元素是否存在 await expect(page.locator('[data-testid="username-input"]')).toBeVisible(); await expect(page.locator('[data-testid="password-input"]')).toBeVisible(); await expect(page.locator('[data-testid="login-button"]')).toBeVisible(); // 检查记住密码复选框 await expect(page.locator('[data-testid="remember-me"]')).toBeVisible(); // 检查语言切换按钮 await expect( page.locator('[data-testid="language-switcher"]'), ).toBeVisible(); }); test('空用户名密码应该显示验证错误', async ({ page }) => { // 直接点击登录按钮 await page.click('[data-testid="login-button"]'); // 等待验证错误消息出现 await expect(page.locator('.ant-form-item-explain-error')).toContainText( '请输入用户名', ); }); test('错误的用户名密码应该显示错误信息', async ({ page }) => { // 输入错误的用户名和密码 await page.fill('[data-testid="username-input"]', 'wrong_user'); await page.fill('[data-testid="password-input"]', 'wrong_password'); // 点击登录按钮 await page.click('[data-testid="login-button"]'); // 等待错误消息出现 await expect(page.locator('.ant-message-error')).toBeVisible(); await expect(page.locator('.ant-message-error')).toContainText( /用户名或密码错误|登录失败/, ); }); test('正确的用户名密码应该成功登录', async ({ page }) => { await loginWithCredentials(page, TEST_CONFIG.testUser); // 验证登录成功后跳转到首页 await expect(page).toHaveURL(/\/dashboard/); // 检查用户信息是否正确显示 await expect(page.locator('[data-testid="user-avatar"]')).toBeVisible(); await expect( page.locator('[data-testid="username-display"]'), ).toContainText(TEST_CONFIG.testUser.username); }); test('记住密码功能应该正常工作', async ({ page }) => { // 输入用户名密码 await page.fill( '[data-testid="username-input"]', TEST_CONFIG.testUser.username, ); await page.fill( '[data-testid="password-input"]', TEST_CONFIG.testUser.password, ); // 勾选记住密码 await page.check('[data-testid="remember-me"]'); // 登录 await page.click('[data-testid="login-button"]'); // 等待登录完成 await page.waitForURL(/\/dashboard/); // 退出登录 await page.click('[data-testid="user-dropdown"]'); await page.click('[data-testid="logout-button"]'); // 回到登录页面,检查是否记住了用户名 await page.waitForURL(/\/login/); await expect(page.locator('[data-testid="username-input"]')).toHaveValue( TEST_CONFIG.testUser.username, ); }); test('语言切换功能应该正常工作', async ({ page }) => { // 点击语言切换按钮 await page.click('[data-testid="language-switcher"]'); // 选择英文 await page.click('[data-testid="lang-en"]'); // 检查页面是否切换到英文 await expect(page.locator('[data-testid="login-title"]')).toContainText( /Login|Sign In/, ); // 切换回中文 await page.click('[data-testid="language-switcher"]'); await page.click('[data-testid="lang-zh"]'); // 检查页面是否切换回中文 await expect(page.locator('[data-testid="login-title"]')).toContainText( /登录|用户登录/, ); }); test('Token过期应该自动跳转到登录页', async ({ page }) => { // 先正常登录 await loginWithCredentials(page, TEST_CONFIG.testUser); await expect(page).toHaveURL(/\/dashboard/); // 模拟Token过期 - 清除localStorage中的token await page.evaluate(() => { localStorage.removeItem('access_token'); localStorage.removeItem('refresh_token'); }); // 尝试访问需要认证的页面 await page.goto(`${TEST_CONFIG.baseURL}/account/list`); // 应该自动跳转到登录页 await expect(page).toHaveURL(/\/login/); // 应该显示登录过期提示 await expect(page.locator('.ant-message-warning')).toContainText( /登录过期|请重新登录/, ); }); test('退出登录应该清除认证状态', async ({ page }) => { // 先登录 await loginWithCredentials(page, TEST_CONFIG.testUser); await expect(page).toHaveURL(/\/dashboard/); // 退出登录 await page.click('[data-testid="user-dropdown"]'); await page.click('[data-testid="logout-button"]'); // 应该跳转到登录页 await expect(page).toHaveURL(/\/login/); // 验证Token已被清除 const accessToken = await page.evaluate(() => localStorage.getItem('access_token'), ); expect(accessToken).toBeNull(); // 尝试直接访问受保护页面应该被重定向 await page.goto(`${TEST_CONFIG.baseURL}/account/list`); await expect(page).toHaveURL(/\/login/); }); test('刷新页面应该保持登录状态', async ({ page }) => { // 登录 await loginWithCredentials(page, TEST_CONFIG.testUser); await expect(page).toHaveURL(/\/dashboard/); // 刷新页面 await page.reload(); // 应该仍然保持登录状态 await expect(page).toHaveURL(/\/dashboard/); await expect(page.locator('[data-testid="user-avatar"]')).toBeVisible(); }); test('多标签页登录状态应该同步', async ({ context }) => { const page1 = await context.newPage(); const page2 = await context.newPage(); // 在第一个标签页登录 await page1.goto(TEST_CONFIG.baseURL); await loginWithCredentials(page1, TEST_CONFIG.testUser); await expect(page1).toHaveURL(/\/dashboard/); // 在第二个标签页访问应用 await page2.goto(TEST_CONFIG.baseURL); // 第二个标签页应该也是登录状态 await expect(page2).toHaveURL(/\/dashboard/); await expect(page2.locator('[data-testid="user-avatar"]')).toBeVisible(); // 在第一个标签页退出登录 await page1.click('[data-testid="user-dropdown"]'); await page1.click('[data-testid="logout-button"]'); // 刷新第二个标签页,应该也被退出登录 await page2.reload(); await expect(page2).toHaveURL(/\/login/); }); }); test.describe('权限和角色测试', () => { test('超级管理员应该能访问所有页面', async ({ page }) => { await page.goto(TEST_CONFIG.baseURL); await loginWithCredentials(page, TEST_CONFIG.superAdmin); // 检查菜单项是否完整显示 const menuItems = [ '[data-testid="menu-dashboard"]', '[data-testid="menu-account"]', '[data-testid="menu-system"]', '[data-testid="menu-logs"]', '[data-testid="menu-marketing"]', ]; for (const menuItem of menuItems) { await expect(page.locator(menuItem)).toBeVisible(); } // 测试访问系统管理页面 await page.click('[data-testid="menu-system"]'); await page.click('[data-testid="menu-permission-management"]'); await expect(page).toHaveURL(/\/system\/permission/); }); test('普通用户应该只能访问授权页面', async ({ page }) => { await page.goto(TEST_CONFIG.baseURL); await loginWithCredentials(page, TEST_CONFIG.normalUser); // 检查某些管理功能不可见 await expect(page.locator('[data-testid="menu-system"]')).not.toBeVisible(); // 尝试直接访问受限页面应该被拒绝 await page.goto(`${TEST_CONFIG.baseURL}/system/permission`); await expect(page.locator('.ant-result-403')).toBeVisible(); await expect(page.locator('.ant-result-title')).toContainText('403'); }); test('按钮级权限控制应该正常工作', async ({ page }) => { await page.goto(TEST_CONFIG.baseURL); await loginWithCredentials(page, TEST_CONFIG.normalUser); // 访问账号列表页面 await page.goto(`${TEST_CONFIG.baseURL}/account/list`); // 检查删除按钮是否被隐藏(假设普通用户没有删除权限) await expect( page.locator('[data-testid="delete-button"]'), ).not.toBeVisible(); // 但查看按钮应该可见 await expect(page.locator('[data-testid="view-button"]')).toBeVisible(); }); }); test.describe('国际化功能测试', () => { test('语言切换应该影响整个应用', async ({ page }) => { await page.goto(TEST_CONFIG.baseURL); await loginWithCredentials(page, TEST_CONFIG.testUser); // 切换到英文 await page.click('[data-testid="language-switcher"]'); await page.click('[data-testid="lang-en"]'); // 检查导航菜单是否切换到英文 await expect(page.locator('[data-testid="menu-dashboard"]')).toContainText( /Dashboard/, ); await expect(page.locator('[data-testid="menu-account"]')).toContainText( /Account/, ); // 切换回中文 await page.click('[data-testid="language-switcher"]'); await page.click('[data-testid="lang-zh"]'); // 检查是否切换回中文 await expect(page.locator('[data-testid="menu-dashboard"]')).toContainText( /仪表盘|首页/, ); await expect(page.locator('[data-testid="menu-account"]')).toContainText( /账号管理/, ); }); test('语言偏好应该被持久化', async ({ page }) => { await page.goto(TEST_CONFIG.baseURL); // 在登录页切换到英文 await page.click('[data-testid="language-switcher"]'); await page.click('[data-testid="lang-en"]'); // 登录 await loginWithCredentials(page, TEST_CONFIG.testUser); // 刷新页面,语言设置应该保持 await page.reload(); await expect(page.locator('[data-testid="menu-dashboard"]')).toContainText( /Dashboard/, ); // 退出重新登录,语言设置也应该保持 await page.click('[data-testid="user-dropdown"]'); await page.click('[data-testid="logout-button"]'); await page.waitForURL(/\/login/); await expect(page.locator('[data-testid="login-title"]')).toContainText( /Login|Sign In/, ); }); }); // 辅助函数:使用指定凭据登录 async function loginWithCredentials( page: Page, credentials: { username: string; password: string }, ) { await page.fill('[data-testid="username-input"]', credentials.username); await page.fill('[data-testid="password-input"]', credentials.password); await page.click('[data-testid="login-button"]'); // 等待登录完成(等待跳转到仪表盘页面) await page.waitForURL(/\/dashboard/, { timeout: 10000 }); }