Files
novalon-manage-system/novalon-manage-web/e2e/auth-exceptions.spec.ts
T
张翔 e2ad1331cc feat: 添加测试框架和覆盖率报告功能
feat(测试): 新增Playwright和Vitest测试配置
feat(测试): 添加测试覆盖率报告生成功能
feat(测试): 实现前后端测试脚本集成

fix(测试): 修复测试密码不匹配问题
fix(测试): 修正URL等待策略
fix(测试): 调整错误消息选择器

refactor(测试): 重构测试目录结构
refactor(测试): 优化测试用例组织方式

docs: 更新测试报告文档
docs: 添加测试覆盖率报告模板

ci: 添加Docker测试环境配置
ci: 实现测试自动化脚本

chore: 更新依赖版本
chore: 添加测试相关配置文件
2026-03-25 09:03:37 +08:00

408 lines
15 KiB
TypeScript

import { test, expect } from '@playwright/test';
import { LoginPage } from './pages/LoginPage';
import { DashboardPage } from './pages/DashboardPage';
import { TestHelper } from './utils/testHelper';
test.describe('认证异常场景测试', () => {
let loginPage: LoginPage;
let dashboardPage: DashboardPage;
test.beforeEach(async ({ page }) => {
loginPage = new LoginPage(page);
dashboardPage = new DashboardPage(page);
await loginPage.goto();
});
test.afterEach(async ({ page }) => {
await TestHelper.clearAllStorage(page);
});
test('登录失败 - 用户名为空', async ({ page }) => {
await test.step('尝试使用空用户名登录', async () => {
await loginPage.usernameInput.fill('');
await loginPage.passwordInput.fill('admin123');
await loginPage.loginButton.click();
});
await test.step('验证错误提示', async () => {
await TestHelper.waitForElementVisible(page, '.el-form-item__error');
const errorMessage = await TestHelper.getElementText(page, '.el-form-item__error');
expect(errorMessage).toBeTruthy();
});
});
test('登录失败 - 密码为空', async ({ page }) => {
await test.step('尝试使用空密码登录', async () => {
await loginPage.usernameInput.fill('admin');
await loginPage.passwordInput.fill('');
await loginPage.loginButton.click();
});
await test.step('验证错误提示', async () => {
await TestHelper.waitForElementVisible(page, '.el-form-item__error');
const errorMessage = await TestHelper.getElementText(page, '.el-form-item__error');
expect(errorMessage).toBeTruthy();
});
});
test('登录失败 - 用户名和密码都为空', async ({ page }) => {
await test.step('尝试使用空用户名和密码登录', async () => {
await loginPage.usernameInput.fill('');
await loginPage.passwordInput.fill('');
await loginPage.loginButton.click();
});
await test.step('验证错误提示', async () => {
const errorMessages = await page.locator('.el-form-item__error').all();
expect(errorMessages.length).toBeGreaterThan(0);
});
});
test('登录失败 - 用户名不存在', async ({ page }) => {
await test.step('尝试使用不存在的用户名登录', async () => {
await loginPage.usernameInput.fill('nonexistentuser123456');
await loginPage.passwordInput.fill('admin123');
await loginPage.loginButton.click();
});
await test.step('验证错误消息', async () => {
await TestHelper.waitForErrorMessage(page);
const errorMessage = await TestHelper.getElementText(page, '.el-message__content');
expect(errorMessage).toContain('用户名或密码错误');
});
});
test('登录失败 - 密码错误', async ({ page }) => {
await test.step('尝试使用错误的密码登录', async () => {
await loginPage.usernameInput.fill('admin');
await loginPage.passwordInput.fill('wrongpassword');
await loginPage.loginButton.click();
});
await test.step('验证错误消息', async () => {
await TestHelper.waitForErrorMessage(page);
const errorMessage = await TestHelper.getElementText(page, '.el-message__content');
expect(errorMessage).toContain('用户名或密码错误');
});
});
test('登录失败 - 账户被锁定', async ({ page }) => {
await test.step('连续多次登录失败', async () => {
for (let i = 0; i < 5; i++) {
await loginPage.usernameInput.fill('admin');
await loginPage.passwordInput.fill('wrongpassword');
await loginPage.loginButton.click();
await page.waitForTimeout(1000);
await loginPage.usernameInput.fill('');
await loginPage.passwordInput.fill('');
}
});
await test.step('验证账户锁定提示', async () => {
await loginPage.usernameInput.fill('admin');
await loginPage.passwordInput.fill('admin123');
await loginPage.loginButton.click();
await TestHelper.waitForErrorMessage(page);
const errorMessage = await TestHelper.getElementText(page, '.el-message__content');
expect(errorMessage).toContain('账户已被锁定');
});
});
test('登录失败 - 账户被禁用', async ({ page, request }) => {
await test.step('禁用admin账户', async () => {
await request.put('http://localhost:8084/api/users/admin/status', {
data: { status: '0' }
});
});
await test.step('尝试使用被禁用的账户登录', async () => {
await loginPage.usernameInput.fill('admin');
await loginPage.passwordInput.fill('admin123');
await loginPage.loginButton.click();
});
await test.step('验证账户禁用提示', async () => {
await TestHelper.waitForErrorMessage(page);
const errorMessage = await TestHelper.getElementText(page, '.el-message__content');
expect(errorMessage).toContain('账户已被禁用');
});
await test.step('恢复admin账户状态', async () => {
await request.put('http://localhost:8084/api/users/admin/status', {
data: { status: '1' }
});
});
});
test('登录失败 - Token过期', async ({ page }) => {
await test.step('正常登录', async () => {
await loginPage.usernameInput.fill('admin');
await loginPage.passwordInput.fill('admin123');
await loginPage.loginButton.click();
await TestHelper.waitForUrl(page, /.*dashboard/);
});
await test.step('设置过期的Token', async () => {
await TestHelper.setLocalStorage(page, 'token', 'expired_token_123456');
await TestHelper.setLocalStorage(page, 'token_expires', '0');
});
await test.step('刷新页面验证Token过期', async () => {
await page.reload();
await TestHelper.waitForPageLoad(page);
});
await test.step('验证自动跳转到登录页面', async () => {
await TestHelper.waitForUrl(page, /.*login/);
await expect(page).toHaveURL(/.*login/);
});
});
test('登录失败 - 无效的Token格式', async ({ page }) => {
await test.step('设置无效的Token', async () => {
await TestHelper.setLocalStorage(page, 'token', 'invalid_token_format');
});
await test.step('尝试访问需要认证的页面', async () => {
await page.goto('/users');
await TestHelper.waitForPageLoad(page);
});
await test.step('验证自动跳转到登录页面', async () => {
await TestHelper.waitForUrl(page, /.*login/);
await expect(page).toHaveURL(/.*login/);
});
});
test('登出失败 - Token已失效', async ({ page }) => {
await test.step('正常登录', async () => {
await loginPage.usernameInput.fill('admin');
await loginPage.passwordInput.fill('admin123');
await loginPage.loginButton.click();
await TestHelper.waitForUrl(page, /.*dashboard/);
});
await test.step('清除Token', async () => {
await TestHelper.clearLocalStorage(page);
});
await test.step('尝试登出', async () => {
const avatar = page.locator('.el-avatar');
if (await avatar.count() > 0) {
await avatar.click();
await TestHelper.waitForElementVisible(page, '.el-dropdown-menu');
const logoutButton = page.locator('.el-dropdown-menu').getByText('退出登录');
if (await logoutButton.count() > 0) {
await logoutButton.click();
}
}
});
await test.step('验证跳转到登录页面', async () => {
await TestHelper.waitForUrl(page, /.*login/);
await expect(page).toHaveURL(/.*login/);
});
});
test('登录成功 - 记住我功能', async ({ page }) => {
await test.step('启用记住我功能并登录', async () => {
await loginPage.usernameInput.fill('admin');
await loginPage.passwordInput.fill('admin123');
const rememberMeCheckbox = page.locator('.remember-me-checkbox');
if (await rememberMeCheckbox.count() > 0) {
await rememberMeCheckbox.check();
}
await loginPage.loginButton.click();
await TestHelper.waitForUrl(page, /.*dashboard/);
});
await test.step('验证Token持久化', async () => {
const token = await TestHelper.getLocalStorage(page, 'token');
expect(token).toBeTruthy();
const rememberMe = await TestHelper.getLocalStorage(page, 'remember_me');
expect(rememberMe).toBe('true');
});
});
test('登录成功 - 自动填充上次登录用户名', async ({ page }) => {
await test.step('首次登录', async () => {
await loginPage.usernameInput.fill('testuser');
await loginPage.passwordInput.fill('testpassword');
await loginPage.loginButton.click();
await TestHelper.waitForUrl(page, /.*dashboard/);
});
await test.step('登出', async () => {
await loginPage.logout();
await TestHelper.waitForUrl(page, /.*login/);
});
await test.step('验证自动填充上次登录用户名', async () => {
const usernameInput = page.locator('input[placeholder*="用户名"]');
const usernameValue = await usernameInput.inputValue();
expect(usernameValue).toBe('testuser');
});
});
test('登录失败 - SQL注入攻击', async ({ page }) => {
await test.step('尝试SQL注入攻击', async () => {
const sqlInjection = "' OR '1'='1";
await loginPage.usernameInput.fill(sqlInjection);
await loginPage.passwordInput.fill('admin123');
await loginPage.loginButton.click();
});
await test.step('验证登录失败', async () => {
await TestHelper.waitForErrorMessage(page);
const currentUrl = page.url();
expect(currentUrl).toContain('/login');
});
});
test('登录失败 - XSS攻击', async ({ page }) => {
await test.step('尝试XSS攻击', async () => {
const xssAttack = '<script>alert("XSS")</script>';
await loginPage.usernameInput.fill(xssAttack);
await loginPage.passwordInput.fill('admin123');
await loginPage.loginButton.click();
});
await test.step('验证XSS被过滤', async () => {
await TestHelper.waitForErrorMessage(page);
const currentUrl = page.url();
expect(currentUrl).toContain('/login');
const usernameInput = page.locator('input[placeholder*="用户名"]');
const usernameValue = await usernameInput.inputValue();
expect(usernameValue).not.toContain('<script>');
});
});
test('登录失败 - 暴力破解防护', async ({ page }) => {
await test.step('快速连续登录失败', async () => {
const loginAttempts = 10;
for (let i = 0; i < loginAttempts; i++) {
await loginPage.usernameInput.fill('admin');
await loginPage.passwordInput.fill(`wrongpassword${i}`);
await loginPage.loginButton.click();
await page.waitForTimeout(500);
await loginPage.usernameInput.fill('');
await loginPage.passwordInput.fill('');
}
});
await test.step('验证账户被临时锁定', async () => {
await loginPage.usernameInput.fill('admin');
await loginPage.passwordInput.fill('admin123');
await loginPage.loginButton.click();
await TestHelper.waitForErrorMessage(page);
const errorMessage = await TestHelper.getElementText(page, '.el-message__content');
expect(errorMessage).toContain('登录尝试次数过多');
});
});
test('登录失败 - 网络错误', async ({ page }) => {
await test.step('模拟网络错误', async () => {
await page.route('**/api/auth/login', route => route.abort('failed'));
});
await test.step('尝试登录', async () => {
await loginPage.usernameInput.fill('admin');
await loginPage.passwordInput.fill('admin123');
await loginPage.loginButton.click();
});
await test.step('验证网络错误提示', async () => {
await TestHelper.waitForErrorMessage(page);
const errorMessage = await TestHelper.getElementText(page, '.el-message__content');
expect(errorMessage).toContain('网络连接失败');
});
});
test('登录失败 - 服务器错误', async ({ page }) => {
await test.step('模拟服务器错误', async () => {
await page.route('**/api/auth/login', route => {
route.fulfill({
status: 500,
contentType: 'application/json',
body: JSON.stringify({ message: 'Internal Server Error' })
});
});
});
await test.step('尝试登录', async () => {
await loginPage.usernameInput.fill('admin');
await loginPage.passwordInput.fill('admin123');
await loginPage.loginButton.click();
});
await test.step('验证服务器错误提示', async () => {
await TestHelper.waitForErrorMessage(page);
const errorMessage = await TestHelper.getElementText(page, '.el-message__content');
expect(errorMessage).toContain('服务器错误');
});
});
test('登录成功 - 验证重定向保护', async ({ page }) => {
await test.step('访问受保护页面', async () => {
await page.goto('/users');
await TestHelper.waitForPageLoad(page);
});
await test.step('验证重定向到登录页面', async () => {
await TestHelper.waitForUrl(page, /.*login/);
await expect(page).toHaveURL(/.*login/);
});
await test.step('登录后验证重定向回原页面', async () => {
await loginPage.usernameInput.fill('admin');
await loginPage.passwordInput.fill('admin123');
await loginPage.loginButton.click();
await TestHelper.waitForUrl(page, /.*users/);
await expect(page).toHaveURL(/.*users/);
});
});
test('登录成功 - 验证会话管理', async ({ page, context }) => {
await test.step('正常登录', async () => {
await loginPage.usernameInput.fill('admin');
await loginPage.passwordInput.fill('admin123');
await loginPage.loginButton.click();
await TestHelper.waitForUrl(page, /.*dashboard/);
});
await test.step('验证Session Cookie存在', async () => {
const cookies = await context.cookies();
const sessionCookie = cookies.find(c => c.name === 'SESSION' || c.name === 'JSESSIONID');
expect(sessionCookie).toBeDefined();
});
await test.step('验证Token存储在localStorage', async () => {
const token = await TestHelper.getLocalStorage(page, 'token');
expect(token).toBeTruthy();
});
});
test('登录失败 - 验证CSRF保护', async ({ page }) => {
await test.step('检查CSRF Token', async () => {
const csrfToken = page.locator('input[name="csrf_token"]');
const hasCsrfToken = await csrfToken.count() > 0;
if (hasCsrfToken) {
const csrfValue = await csrfToken.inputValue();
expect(csrfValue).toBeTruthy();
expect(csrfValue.length).toBeGreaterThan(10);
}
});
});
});