feat: 重构测试框架并优化代码结构
refactor(tests): 将e2e_tests迁移到tests_suite和api_integration_tests style: 为Java类添加文档注释 docs: 更新.gitignore和配置文件 test: 添加性能测试和Playwright测试脚本 chore: 清理旧测试文件和配置
This commit is contained in:
@@ -0,0 +1,311 @@
|
||||
"""
|
||||
端到端业务流程测试用例
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import time
|
||||
from test_utils.api_client.api.auth_api import AuthAPI
|
||||
from test_utils.api_client.api.user_api import UserAPI
|
||||
from test_utils.api_client.api.role_api import RoleAPI
|
||||
from test_utils.api_client.api.notice_api import SysNoticeAPI
|
||||
|
||||
|
||||
@pytest.mark.e2e
|
||||
@pytest.mark.regression
|
||||
class TestBusinessFlow:
|
||||
"""端到端业务流程测试类"""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_complete_user_lifecycle(self, authenticated_client):
|
||||
"""测试完整用户生命周期"""
|
||||
auth_api = AuthAPI(authenticated_client)
|
||||
user_api = UserAPI(authenticated_client)
|
||||
|
||||
timestamp = int(time.time() * 1000)
|
||||
|
||||
new_user_data = {
|
||||
"username": f"e2e_user_{timestamp}",
|
||||
"password": "Test123!@#",
|
||||
"email": f"e2e_{timestamp}@example.com",
|
||||
"phone": "13800138000",
|
||||
"status": 1
|
||||
}
|
||||
|
||||
create_response = await user_api.create_user(new_user_data)
|
||||
assert create_response.status_code == 201
|
||||
user_id = create_response.json()["id"]
|
||||
|
||||
get_response = await user_api.get_user_by_id(user_id)
|
||||
assert get_response.status_code == 200
|
||||
user_data = get_response.json()
|
||||
assert user_data["username"] == new_user_data["username"]
|
||||
|
||||
update_data = {"email": f"updated_{timestamp}@example.com"}
|
||||
update_response = await user_api.update_user(user_id, update_data)
|
||||
assert update_response.status_code == 200
|
||||
|
||||
delete_response = await user_api.delete_user(user_id)
|
||||
assert delete_response.status_code in [200, 204]
|
||||
|
||||
final_get_response = await user_api.get_user_by_id(user_id)
|
||||
assert final_get_response.status_code == 404
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_role_assignment_workflow(self, authenticated_client):
|
||||
"""测试角色分配工作流"""
|
||||
user_api = UserAPI(authenticated_client)
|
||||
role_api = RoleAPI(authenticated_client)
|
||||
|
||||
timestamp = int(time.time() * 1000)
|
||||
|
||||
role_data = {
|
||||
"roleName": f"E2E_Role_{timestamp}",
|
||||
"roleKey": f"e2e_role_{timestamp}",
|
||||
"roleSort": 1,
|
||||
"status": 1
|
||||
}
|
||||
|
||||
role_response = await role_api.create_role(role_data)
|
||||
assert role_response.status_code == 201
|
||||
role_id = role_response.json()["id"]
|
||||
|
||||
user_data = {
|
||||
"username": f"e2e_user_{timestamp}",
|
||||
"password": "Test123!@#",
|
||||
"email": f"e2e_{timestamp}@example.com",
|
||||
"status": 1
|
||||
}
|
||||
|
||||
user_response = await user_api.create_user(user_data)
|
||||
assert user_response.status_code == 201
|
||||
user_id = user_response.json()["id"]
|
||||
|
||||
assign_response = await user_api.update_user(user_id, {"roleId": role_id})
|
||||
assert assign_response.status_code == 200
|
||||
|
||||
verify_response = await user_api.get_user_by_id(user_id)
|
||||
assert verify_response.json()["roleId"] == role_id
|
||||
|
||||
await user_api.delete_user(user_id)
|
||||
await role_api.delete_role(role_id)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_notification_workflow(self, authenticated_client):
|
||||
"""测试通知工作流"""
|
||||
notice_api = SysNoticeAPI(authenticated_client)
|
||||
user_api = UserAPI(authenticated_client)
|
||||
|
||||
timestamp = int(time.time() * 1000)
|
||||
|
||||
notice_data = {
|
||||
"noticeTitle": f"E2E_Notice_{timestamp}",
|
||||
"noticeType": "1",
|
||||
"noticeContent": "This is an E2E test notice",
|
||||
"status": "0"
|
||||
}
|
||||
|
||||
create_response = await notice_api.create(notice_data)
|
||||
assert create_response.status_code == 201
|
||||
notice_data_response = create_response.json()
|
||||
|
||||
notice_id = notice_data_response.get("id")
|
||||
if not notice_id:
|
||||
notice_title = notice_data_response.get("noticeTitle")
|
||||
all_notices = await notice_api.get_all()
|
||||
notices = all_notices.json()
|
||||
notice = next((n for n in notices if n["noticeTitle"] == notice_title), None)
|
||||
notice_id = notice["id"] if notice else None
|
||||
|
||||
assert notice_id is not None
|
||||
|
||||
get_response = await notice_api.get_by_id(notice_id)
|
||||
assert get_response.status_code == 200
|
||||
|
||||
all_notices = await notice_api.get_all()
|
||||
assert all_notices.status_code == 200
|
||||
notices = all_notices.json()
|
||||
assert any(notice["id"] == notice_id for notice in notices)
|
||||
|
||||
update_data = {"noticeTitle": f"Updated_Notice_{timestamp}"}
|
||||
update_response = await notice_api.update(notice_id, update_data)
|
||||
assert update_response.status_code == 200
|
||||
|
||||
await notice_api.delete(notice_id)
|
||||
|
||||
final_get = await notice_api.get_by_id(notice_id)
|
||||
assert final_get.status_code == 404
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_multi_role_user_management(self, authenticated_client):
|
||||
"""测试多角色用户管理"""
|
||||
user_api = UserAPI(authenticated_client)
|
||||
role_api = RoleAPI(authenticated_client)
|
||||
|
||||
timestamp = int(time.time() * 1000)
|
||||
|
||||
admin_role_data = {
|
||||
"roleName": f"Admin_{timestamp}",
|
||||
"roleKey": f"admin_{timestamp}",
|
||||
"roleSort": 1,
|
||||
"status": 1
|
||||
}
|
||||
admin_role = await role_api.create_role(admin_role_data)
|
||||
admin_role_id = admin_role.json()["id"]
|
||||
|
||||
user_role_data = {
|
||||
"roleName": f"User_{timestamp}",
|
||||
"roleKey": f"user_{timestamp}",
|
||||
"roleSort": 2,
|
||||
"status": 1
|
||||
}
|
||||
user_role = await role_api.create_role(user_role_data)
|
||||
user_role_id = user_role.json()["id"]
|
||||
|
||||
admin_user_data = {
|
||||
"username": f"admin_{timestamp}",
|
||||
"password": "Admin123!@#",
|
||||
"email": f"admin_{timestamp}@example.com",
|
||||
"status": 1
|
||||
}
|
||||
admin_user = await user_api.create_user(admin_user_data)
|
||||
admin_user_id = admin_user.json()["id"]
|
||||
|
||||
regular_user_data = {
|
||||
"username": f"regular_{timestamp}",
|
||||
"password": "User123!@#",
|
||||
"email": f"regular_{timestamp}@example.com",
|
||||
"status": 1
|
||||
}
|
||||
regular_user = await user_api.create_user(regular_user_data)
|
||||
regular_user_id = regular_user.json()["id"]
|
||||
|
||||
await user_api.update_user(admin_user_id, {"roleId": admin_role_id})
|
||||
await user_api.update_user(regular_user_id, {"roleId": user_role_id})
|
||||
|
||||
admin_verify = await user_api.get_user_by_id(admin_user_id)
|
||||
assert admin_verify.json()["roleId"] == admin_role_id
|
||||
|
||||
regular_verify = await user_api.get_user_by_id(regular_user_id)
|
||||
assert regular_verify.json()["roleId"] == user_role_id
|
||||
|
||||
all_users = await user_api.get_all_users()
|
||||
users = all_users.json()
|
||||
assert len(users) >= 2
|
||||
|
||||
await user_api.delete_user(admin_user_id)
|
||||
await user_api.delete_user(regular_user_id)
|
||||
await role_api.delete_role(admin_role_id)
|
||||
await role_api.delete_role(user_role_id)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_user_role_cascade_operations(self, authenticated_client):
|
||||
"""测试用户角色级联操作"""
|
||||
user_api = UserAPI(authenticated_client)
|
||||
role_api = RoleAPI(authenticated_client)
|
||||
|
||||
timestamp = int(time.time() * 1000)
|
||||
|
||||
role_data = {
|
||||
"roleName": f"Cascade_Role_{timestamp}",
|
||||
"roleKey": f"cascade_role_{timestamp}",
|
||||
"roleSort": 1,
|
||||
"status": 1
|
||||
}
|
||||
role_response = await role_api.create_role(role_data)
|
||||
role_id = role_response.json()["id"]
|
||||
|
||||
user_ids = []
|
||||
for i in range(3):
|
||||
user_data = {
|
||||
"username": f"cascade_user_{timestamp}_{i}",
|
||||
"password": "Test123!@#",
|
||||
"email": f"cascade_{timestamp}_{i}@example.com",
|
||||
"status": 1
|
||||
}
|
||||
user_response = await user_api.create_user(user_data)
|
||||
user_id = user_response.json()["id"]
|
||||
user_ids.append(user_id)
|
||||
await user_api.update_user(user_id, {"roleId": role_id})
|
||||
|
||||
await role_api.update_role(role_id, {"status": 0})
|
||||
|
||||
for user_id in user_ids:
|
||||
user_data = await user_api.get_user_by_id(user_id)
|
||||
assert user_data.json()["roleId"] == role_id
|
||||
|
||||
for user_id in user_ids:
|
||||
await user_api.delete_user(user_id)
|
||||
await role_api.delete_role(role_id)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_and_filter_workflow(self, authenticated_client):
|
||||
"""测试搜索和过滤工作流"""
|
||||
user_api = UserAPI(authenticated_client)
|
||||
role_api = RoleAPI(authenticated_client)
|
||||
|
||||
timestamp = int(time.time() * 1000)
|
||||
|
||||
role_data = {
|
||||
"roleName": f"Search_Role_{timestamp}",
|
||||
"roleKey": f"search_role_{timestamp}",
|
||||
"roleSort": 1,
|
||||
"status": 1
|
||||
}
|
||||
role_response = await role_api.create_role(role_data)
|
||||
role_id = role_response.json()["id"]
|
||||
|
||||
user_ids = []
|
||||
for i in range(5):
|
||||
user_data = {
|
||||
"username": f"search_{timestamp}_{i}",
|
||||
"password": "Test123!@#",
|
||||
"email": f"search_{timestamp}_{i}@example.com",
|
||||
"status": 1
|
||||
}
|
||||
user_response = await user_api.create_user(user_data)
|
||||
user_id = user_response.json()["id"]
|
||||
user_ids.append(user_id)
|
||||
|
||||
search_response = await user_api.get_users_by_page(keyword=f"search_{timestamp}")
|
||||
assert search_response.status_code == 200
|
||||
search_data = search_response.json()
|
||||
assert len(search_data["content"]) >= 5
|
||||
|
||||
all_users = await user_api.get_all_users()
|
||||
assert all_users.status_code == 200
|
||||
|
||||
for user_id in user_ids:
|
||||
await user_api.delete_user(user_id)
|
||||
await role_api.delete_role(role_id)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_error_recovery_workflow(self, authenticated_client):
|
||||
"""测试错误恢复工作流"""
|
||||
user_api = UserAPI(authenticated_client)
|
||||
|
||||
timestamp = int(time.time() * 1000)
|
||||
|
||||
invalid_user_data = {
|
||||
"username": "",
|
||||
"password": "123",
|
||||
"email": "invalid-email"
|
||||
}
|
||||
|
||||
invalid_response = await user_api.create_user(invalid_user_data)
|
||||
assert invalid_response.status_code in [400, 409, 422]
|
||||
|
||||
valid_user_data = {
|
||||
"username": f"recovery_{timestamp}",
|
||||
"password": "Valid123!@#",
|
||||
"email": f"recovery_{timestamp}@example.com",
|
||||
"status": 1
|
||||
}
|
||||
|
||||
valid_response = await user_api.create_user(valid_user_data)
|
||||
assert valid_response.status_code == 201
|
||||
user_id = valid_response.json()["id"]
|
||||
|
||||
get_response = await user_api.get_user_by_id(user_id)
|
||||
assert get_response.status_code == 200
|
||||
|
||||
await user_api.delete_user(user_id)
|
||||
@@ -0,0 +1,50 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('用户认证 E2E 测试', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
});
|
||||
|
||||
test('成功登录流程', async ({ page }) => {
|
||||
await expect(page).toHaveTitle(/登录/);
|
||||
|
||||
await page.fill('input[placeholder*="用户名"]', 'admin');
|
||||
await page.fill('input[type="password"]', 'admin123');
|
||||
|
||||
await page.click('button:has-text("登录")');
|
||||
|
||||
await page.waitForURL('**/dashboard');
|
||||
await expect(page.locator('.user-info')).toContainText('admin');
|
||||
});
|
||||
|
||||
test('登录失败 - 无效凭证', async ({ page }) => {
|
||||
await page.fill('input[placeholder*="用户名"]', 'invalid');
|
||||
await page.fill('input[type="password"]', 'invalid');
|
||||
|
||||
await page.click('button:has-text("登录")');
|
||||
|
||||
await expect(page.locator('.error-message')).toBeVisible();
|
||||
await expect(page.locator('.error-message')).toContainText('用户名或密码错误');
|
||||
});
|
||||
|
||||
test('登录失败 - 缺少必填字段', async ({ page }) => {
|
||||
await page.fill('input[name="username"]', 'admin');
|
||||
|
||||
await page.click('button[type="submit"]');
|
||||
|
||||
await expect(page.locator('.error-message')).toBeVisible();
|
||||
});
|
||||
|
||||
test('登出流程', async ({ page }) => {
|
||||
await page.fill('input[name="username"]', 'admin');
|
||||
await page.fill('input[name="password"]', 'admin123');
|
||||
await page.click('button[type="submit"]');
|
||||
|
||||
await page.waitForURL('**/');
|
||||
|
||||
await page.click('text=登出');
|
||||
|
||||
await page.waitForURL('**/login');
|
||||
await expect(page).toHaveTitle(/登录/);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('系统基础功能 E2E 测试', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
});
|
||||
|
||||
test('首页加载测试', async ({ page }) => {
|
||||
await expect(page).toHaveTitle(/Novalon 管理系统/);
|
||||
await expect(page.locator('#app')).toBeVisible();
|
||||
});
|
||||
|
||||
test('登录页面访问测试', async ({ page }) => {
|
||||
await page.click('text=登录');
|
||||
await expect(page).toHaveURL(/.*login/);
|
||||
await expect(page.locator('input[type="text"]')).toBeVisible();
|
||||
await expect(page.locator('input[type="password"]')).toBeVisible();
|
||||
});
|
||||
|
||||
test('后端健康检查', async ({ request }) => {
|
||||
const response = await request.get('http://localhost:8084/actuator/health');
|
||||
expect(response.status()).toBe(200);
|
||||
const body = await response.json();
|
||||
expect(body.status).toBe('UP');
|
||||
});
|
||||
|
||||
test('数据库连接检查', async ({ request }) => {
|
||||
const response = await request.get('http://localhost:8084/actuator/health');
|
||||
expect(response.status()).toBe(200);
|
||||
const body = await response.json();
|
||||
expect(body.components.r2dbc.status).toBe('UP');
|
||||
expect(body.components.r2dbc.details.database).toBe('PostgreSQL');
|
||||
});
|
||||
|
||||
test('前端页面可访问性', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await expect(page.locator('#app')).toBeVisible();
|
||||
const title = await page.title();
|
||||
expect(title).toContain('Novalon 管理系统');
|
||||
});
|
||||
|
||||
test('API代理配置验证', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
const response = await page.request.get('http://localhost:3002/api/actuator/health');
|
||||
expect(response.status()).toBe(401);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,79 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('角色管理 E2E 测试', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
await page.fill('input[placeholder*="用户名"]', 'admin');
|
||||
await page.fill('input[type="password"]', 'admin123');
|
||||
await page.click('button:has-text("登录")');
|
||||
await page.waitForURL('**/dashboard');
|
||||
});
|
||||
|
||||
test('创建角色完整流程', async ({ page }) => {
|
||||
await page.click('text=角色管理');
|
||||
await page.waitForURL('**/roles');
|
||||
|
||||
await page.click('text=创建角色');
|
||||
|
||||
const timestamp = Date.now();
|
||||
const roleName = `测试角色_${timestamp}`;
|
||||
const roleKey = `test_role_${timestamp}`;
|
||||
|
||||
await page.fill('input[name="roleName"]', roleName);
|
||||
await page.fill('input[name="roleKey"]', roleKey);
|
||||
await page.fill('input[name="roleSort"]', '1');
|
||||
|
||||
await page.click('input[type="checkbox"][value="user:view"]');
|
||||
await page.click('input[type="checkbox"][value="user:create"]');
|
||||
|
||||
await page.click('button[type="submit"]');
|
||||
|
||||
await expect(page.locator('.success-message')).toBeVisible();
|
||||
await expect(page.locator('table')).toContainText(roleName);
|
||||
});
|
||||
|
||||
test('编辑角色流程', async ({ page }) => {
|
||||
await page.click('text=角色管理');
|
||||
await page.waitForURL('**/roles');
|
||||
|
||||
await page.click('table tbody tr:first-child .edit-button');
|
||||
|
||||
await page.fill('input[name="roleName"]', '更新后的角色名称');
|
||||
|
||||
await page.click('button[type="submit"]');
|
||||
|
||||
await expect(page.locator('.success-message')).toBeVisible();
|
||||
await expect(page.locator('table')).toContainText('更新后的角色名称');
|
||||
});
|
||||
|
||||
test('分配权限流程', async ({ page }) => {
|
||||
await page.click('text=角色管理');
|
||||
await page.waitForURL('**/roles');
|
||||
|
||||
await page.click('table tbody tr:first-child .permission-button');
|
||||
|
||||
await page.click('input[type="checkbox"][value="user:edit"]');
|
||||
await page.click('input[type="checkbox"][value="user:delete"]');
|
||||
|
||||
await page.click('.permission-dialog .save-button');
|
||||
|
||||
await expect(page.locator('.success-message')).toBeVisible();
|
||||
});
|
||||
|
||||
test('删除角色流程', async ({ page }) => {
|
||||
await page.click('text=角色管理');
|
||||
await page.waitForURL('**/roles');
|
||||
|
||||
const firstRow = page.locator('table tbody tr:first-child');
|
||||
const roleName = await firstRow.locator('td:first-child').textContent();
|
||||
|
||||
await firstRow.locator('.delete-button').click();
|
||||
|
||||
await page.click('.confirm-dialog .confirm-button');
|
||||
|
||||
await expect(page.locator('.success-message')).toBeVisible();
|
||||
|
||||
await page.reload();
|
||||
await expect(page.locator('table')).not.toContainText(roleName);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('系统配置 E2E 测试', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
await page.fill('input[placeholder*="用户名"]', 'admin');
|
||||
await page.fill('input[type="password"]', 'admin123');
|
||||
await page.click('button:has-text("登录")');
|
||||
await page.waitForURL('**/dashboard');
|
||||
});
|
||||
|
||||
test('查看系统配置', async ({ page }) => {
|
||||
await page.click('text=系统配置');
|
||||
await page.waitForURL('**/config');
|
||||
|
||||
await expect(page.locator('table')).toBeVisible();
|
||||
await expect(page.locator('table tbody tr')).toHaveCount(10);
|
||||
});
|
||||
|
||||
test('编辑系统配置', async ({ page }) => {
|
||||
await page.click('text=系统配置');
|
||||
await page.waitForURL('**/config');
|
||||
|
||||
await page.click('table tbody tr:first-child .edit-button');
|
||||
|
||||
await page.fill('input[name="configValue"]', 'test_value_123');
|
||||
|
||||
await page.click('button[type="submit"]');
|
||||
|
||||
await expect(page.locator('.success-message')).toBeVisible();
|
||||
});
|
||||
|
||||
test('搜索配置项', async ({ page }) => {
|
||||
await page.click('text=系统配置');
|
||||
await page.waitForURL('**/config');
|
||||
|
||||
await page.fill('input[name="keyword"]', 'system');
|
||||
await page.click('button[type="search"]');
|
||||
|
||||
await expect(page.locator('table')).toContainText('system');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,292 @@
|
||||
"""
|
||||
真实的端到端(E2E)测试 - 使用Playwright测试前后端联通
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import time
|
||||
from playwright.async_api import async_playwright, Page, Browser, BrowserContext
|
||||
from httpx import AsyncClient
|
||||
|
||||
from config import settings
|
||||
|
||||
|
||||
@pytest.mark.e2e
|
||||
@pytest.mark.playwright
|
||||
class TestRealE2E:
|
||||
"""真实的端到端测试类"""
|
||||
|
||||
@pytest.fixture
|
||||
async def browser(self):
|
||||
"""浏览器fixture - headless模式"""
|
||||
async with async_playwright() as p:
|
||||
browser = await p.chromium.launch(headless=True)
|
||||
yield browser
|
||||
await browser.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def context(self, browser):
|
||||
"""浏览器上下文fixture"""
|
||||
context = await browser.new_context()
|
||||
yield context
|
||||
await context.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def page(self, context):
|
||||
"""页面fixture"""
|
||||
page = await context.new_page()
|
||||
page.set_default_timeout(30000)
|
||||
yield page
|
||||
await page.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def authenticated_client(self):
|
||||
"""已认证的HTTP客户端"""
|
||||
async with AsyncClient(base_url=settings.API_BASE_URL) as client:
|
||||
response = await client.post(
|
||||
"/api/auth/login",
|
||||
json={
|
||||
"username": settings.TEST_USERNAME,
|
||||
"password": settings.TEST_PASSWORD
|
||||
}
|
||||
)
|
||||
assert response.status_code == 200
|
||||
token = response.json().get("token")
|
||||
client.headers.update({"Authorization": f"Bearer {token}"})
|
||||
yield client
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_complete_user_lifecycle_e2e(self, page, authenticated_client):
|
||||
"""测试完整的用户生命周期 - 前后端联通"""
|
||||
timestamp = int(time.time() * 1000)
|
||||
username = f"e2e_user_{timestamp}"
|
||||
email = f"e2e_{timestamp}@example.com"
|
||||
|
||||
# 1. 通过前端登录
|
||||
await page.goto("http://localhost:3002/login")
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
await page.fill('input[name="username"]', settings.TEST_USERNAME)
|
||||
await page.fill('input[name="password"]', settings.TEST_PASSWORD)
|
||||
await page.click('button[type="submit"]')
|
||||
|
||||
await page.wait_for_url("**/")
|
||||
assert await page.title() != ""
|
||||
|
||||
# 2. 通过前端创建用户
|
||||
await page.click('text=用户管理')
|
||||
await page.wait_for_url("**/users")
|
||||
|
||||
await page.click('text=创建用户')
|
||||
|
||||
await page.fill('input[name="username"]', username)
|
||||
await page.fill('input[name="email"]', email)
|
||||
await page.fill('input[name="phone"]', '13800138000')
|
||||
await page.fill('input[name="password"]', 'Test123!@#')
|
||||
await page.fill('input[name="confirmPassword"]', 'Test123!@#')
|
||||
|
||||
await page.click('button[type="submit"]')
|
||||
|
||||
await page.wait_for_selector('.success-message', timeout=10000)
|
||||
success_message = await page.text_content('.success-message')
|
||||
assert '成功' in success_message or 'success' in success_message.lower()
|
||||
|
||||
# 3. 通过API验证用户已创建
|
||||
response = await authenticated_client.get("/api/users")
|
||||
assert response.status_code == 200
|
||||
users = response.json()
|
||||
user_exists = any(user['username'] == username for user in users)
|
||||
assert user_exists, f"User {username} not found in API response"
|
||||
|
||||
# 4. 通过前端验证用户显示
|
||||
await page.reload()
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
page_content = await page.content()
|
||||
assert username in page_content, f"Username {username} not found in page content"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_role_assignment_e2e(self, page, authenticated_client):
|
||||
"""测试角色分配 - 前后端联通"""
|
||||
timestamp = int(time.time() * 1000)
|
||||
role_name = f"E2E_Role_{timestamp}"
|
||||
role_key = f"e2e_role_{timestamp}"
|
||||
|
||||
# 1. 通过API创建角色
|
||||
role_response = await authenticated_client.post(
|
||||
"/api/roles",
|
||||
json={
|
||||
"roleName": role_name,
|
||||
"roleKey": role_key,
|
||||
"roleSort": 1,
|
||||
"status": 1
|
||||
}
|
||||
)
|
||||
assert role_response.status_code == 201
|
||||
role_id = role_response.json()["id"]
|
||||
|
||||
# 2. 通过前端登录
|
||||
await page.goto("http://localhost:3002/login")
|
||||
await page.fill('input[name="username"]', settings.TEST_USERNAME)
|
||||
await page.fill('input[name="password"]', settings.TEST_PASSWORD)
|
||||
await page.click('button[type="submit"]')
|
||||
await page.wait_for_url("**/")
|
||||
|
||||
# 3. 通过前端创建用户
|
||||
await page.click('text=用户管理')
|
||||
await page.wait_for_url("**/users")
|
||||
|
||||
await page.click('text=创建用户')
|
||||
|
||||
username = f"e2e_user_{timestamp}"
|
||||
await page.fill('input[name="username"]', username)
|
||||
await page.fill('input[name="email"]', f"e2e_{timestamp}@example.com")
|
||||
await page.fill('input[name="password"]', 'Test123!@#')
|
||||
await page.fill('input[name="confirmPassword"]', 'Test123!@#')
|
||||
|
||||
await page.click('button[type="submit"]')
|
||||
await page.wait_for_selector('.success-message', timeout=10000)
|
||||
|
||||
# 4. 通过API获取用户ID并分配角色
|
||||
users_response = await authenticated_client.get("/api/users")
|
||||
users = users_response.json()
|
||||
user = next((u for u in users if u['username'] == username), None)
|
||||
assert user is not None
|
||||
|
||||
await authenticated_client.put(
|
||||
f"/api/users/{user['id']}",
|
||||
json={"roleId": role_id}
|
||||
)
|
||||
|
||||
# 5. 通过API验证角色分配
|
||||
user_response = await authenticated_client.get(f"/api/users/{user['id']}")
|
||||
assert user_response.status_code == 200
|
||||
user_data = user_response.json()
|
||||
assert user_data["roleId"] == role_id
|
||||
|
||||
# 6. 清理测试数据
|
||||
await authenticated_client.delete(f"/api/users/{user['id']}")
|
||||
await authenticated_client.delete(f"/api/roles/{role_id}")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_login_and_navigation_e2e(self, page):
|
||||
"""测试登录和导航 - 前后端联通"""
|
||||
# 1. 访问登录页面
|
||||
await page.goto("http://localhost:3002/login")
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
title = await page.title()
|
||||
assert "登录" in title or "Login" in title.lower()
|
||||
|
||||
# 2. 填写登录表单
|
||||
await page.fill('input[name="username"]', settings.TEST_USERNAME)
|
||||
await page.fill('input[name="password"]', settings.TEST_PASSWORD)
|
||||
|
||||
# 3. 点击登录按钮
|
||||
await page.click('button[type="submit"]')
|
||||
|
||||
# 4. 等待跳转到首页
|
||||
await page.wait_for_url("**/", timeout=10000)
|
||||
|
||||
# 5. 验证用户信息显示
|
||||
user_info = await page.query_selector('.user-info')
|
||||
assert user_info is not None, "User info element not found"
|
||||
|
||||
user_text = await user_info.text_content()
|
||||
assert settings.TEST_USERNAME in user_text
|
||||
|
||||
# 6. 测试导航到不同页面
|
||||
await page.click('text=用户管理')
|
||||
await page.wait_for_url("**/users")
|
||||
|
||||
await page.click('text=角色管理')
|
||||
await page.wait_for_url("**/roles")
|
||||
|
||||
await page.click('text=系统配置')
|
||||
await page.wait_for_url("**/config")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_system_config_e2e(self, page, authenticated_client):
|
||||
"""测试系统配置 - 前后端联通"""
|
||||
# 1. 通过前端登录
|
||||
await page.goto("http://localhost:3002/login")
|
||||
await page.fill('input[name="username"]', settings.TEST_USERNAME)
|
||||
await page.fill('input[name="password"]', settings.TEST_PASSWORD)
|
||||
await page.click('button[type="submit"]')
|
||||
await page.wait_for_url("**/")
|
||||
|
||||
# 2. 通过前端访问系统配置
|
||||
await page.click('text=系统配置')
|
||||
await page.wait_for_url("**/config")
|
||||
|
||||
# 3. 验证配置列表显示
|
||||
table = await page.query_selector('table')
|
||||
assert table is not None, "Config table not found"
|
||||
|
||||
# 4. 通过API获取配置
|
||||
config_response = await authenticated_client.get("/api/config")
|
||||
assert config_response.status_code == 200
|
||||
configs = config_response.json()
|
||||
|
||||
# 5. 验证前后端数据一致
|
||||
page_content = await page.content()
|
||||
for config in configs[:3]:
|
||||
assert config['configKey'] in page_content or config['configName'] in page_content
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_and_filter_e2e(self, page, authenticated_client):
|
||||
"""测试搜索和过滤 - 前后端联通"""
|
||||
timestamp = int(time.time() * 1000)
|
||||
|
||||
# 1. 通过API创建多个测试用户
|
||||
user_ids = []
|
||||
for i in range(3):
|
||||
username = f"search_{timestamp}_{i}"
|
||||
response = await authenticated_client.post(
|
||||
"/api/users",
|
||||
json={
|
||||
"username": username,
|
||||
"password": "Test123!@#",
|
||||
"email": f"search_{timestamp}_{i}@example.com",
|
||||
"status": 1
|
||||
}
|
||||
)
|
||||
assert response.status_code == 201
|
||||
user_ids.append(response.json()["id"])
|
||||
|
||||
try:
|
||||
# 2. 通过前端登录
|
||||
await page.goto("http://localhost:3002/login")
|
||||
await page.fill('input[name="username"]', settings.TEST_USERNAME)
|
||||
await page.fill('input[name="password"]', settings.TEST_PASSWORD)
|
||||
await page.click('button[type="submit"]')
|
||||
await page.wait_for_url("**/")
|
||||
|
||||
# 3. 通过前端搜索用户
|
||||
await page.click('text=用户管理')
|
||||
await page.wait_for_url("**/users")
|
||||
|
||||
await page.fill('input[name="keyword"]', f"search_{timestamp}")
|
||||
await page.click('button[type="search"]')
|
||||
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
# 4. 验证搜索结果显示
|
||||
page_content = await page.content()
|
||||
assert f"search_{timestamp}" in page_content
|
||||
|
||||
# 5. 通过API验证搜索结果
|
||||
search_response = await authenticated_client.get(
|
||||
"/api/users/page",
|
||||
params={"keyword": f"search_{timestamp}", "page": 0, "size": 10}
|
||||
)
|
||||
assert search_response.status_code == 200
|
||||
search_data = search_response.json()
|
||||
assert len(search_data["content"]) >= 3
|
||||
|
||||
finally:
|
||||
# 6. 清理测试数据
|
||||
for user_id in user_ids:
|
||||
try:
|
||||
await authenticated_client.delete(f"/api/users/{user_id}")
|
||||
except Exception:
|
||||
pass
|
||||
@@ -0,0 +1,82 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('用户管理 E2E 测试', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
await page.fill('input[placeholder*="用户名"]', 'admin');
|
||||
await page.fill('input[type="password"]', 'admin123');
|
||||
await page.click('button:has-text("登录")');
|
||||
await page.waitForURL('**/dashboard');
|
||||
});
|
||||
|
||||
test('创建用户完整流程', async ({ page }) => {
|
||||
await page.click('text=用户管理');
|
||||
await page.waitForURL('**/users');
|
||||
|
||||
await page.click('text=创建用户');
|
||||
|
||||
const timestamp = Date.now();
|
||||
const username = `testuser_${timestamp}`;
|
||||
|
||||
await page.fill('input[name="username"]', username);
|
||||
await page.fill('input[name="email"]', `test_${timestamp}@example.com`);
|
||||
await page.fill('input[name="phone"]', '13800138000');
|
||||
await page.fill('input[name="password"]', 'Test123!@#');
|
||||
await page.fill('input[name="confirmPassword"]', 'Test123!@#');
|
||||
|
||||
await page.click('button[type="submit"]');
|
||||
|
||||
await expect(page.locator('.success-message')).toBeVisible();
|
||||
await expect(page.locator('table')).toContainText(username);
|
||||
});
|
||||
|
||||
test('编辑用户流程', async ({ page }) => {
|
||||
await page.click('text=用户管理');
|
||||
await page.waitForURL('**/users');
|
||||
|
||||
await page.click('table tbody tr:first-child .edit-button');
|
||||
|
||||
await page.fill('input[name="email"]', 'updated@example.com');
|
||||
|
||||
await page.click('button[type="submit"]');
|
||||
|
||||
await expect(page.locator('.success-message')).toBeVisible();
|
||||
await expect(page.locator('table')).toContainText('updated@example.com');
|
||||
});
|
||||
|
||||
test('删除用户流程', async ({ page }) => {
|
||||
await page.click('text=用户管理');
|
||||
await page.waitForURL('**/users');
|
||||
|
||||
const firstRow = page.locator('table tbody tr:first-child');
|
||||
const username = await firstRow.locator('td:first-child').textContent();
|
||||
|
||||
await firstRow.locator('.delete-button').click();
|
||||
|
||||
await page.click('.confirm-dialog .confirm-button');
|
||||
|
||||
await expect(page.locator('.success-message')).toBeVisible();
|
||||
|
||||
await page.reload();
|
||||
await expect(page.locator('table')).not.toContainText(username);
|
||||
});
|
||||
|
||||
test('搜索用户功能', async ({ page }) => {
|
||||
await page.click('text=用户管理');
|
||||
await page.waitForURL('**/users');
|
||||
|
||||
await page.fill('input[name="keyword"]', 'admin');
|
||||
await page.click('button[type="search"]');
|
||||
|
||||
await expect(page.locator('table')).toContainText('admin');
|
||||
});
|
||||
|
||||
test('分页功能', async ({ page }) => {
|
||||
await page.click('text=用户管理');
|
||||
await page.waitForURL('**/users');
|
||||
|
||||
await page.click('.pagination .next-page');
|
||||
|
||||
await expect(page.locator('.pagination .current-page')).toContainText('2');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user