From b3201b61fbd85aba5e4f829b2865637b4453d2a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=BF=94?= Date: Tue, 7 Apr 2026 09:37:11 +0800 Subject: [PATCH] =?UTF-8?q?fix(e2e):=20=E4=BF=AE=E5=A4=8D=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E5=A4=B1=E8=B4=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 数据库修复: - 添加测试用户 'user'(密码:admin123) 测试代码优化: - 添加页面加载等待逻辑(waitForLoadState) - 添加元素可见性等待(waitFor visible) - 修复用户密码错误(user123 -> admin123) - 改进错误处理和稳定性 --- .../src/main/resources/application-dev.yml | 3 ++ .../db/migration/V2__Insert_initial_data.sql | 16 ++++-- .../V4__Create_permission_tables.sql | 2 +- .../db/migration/V5__Create_indexes.sql | 14 ++--- .../db/migration/V9__Grant_permissions.sql | 13 +++++ .../journeys/admin-complete-workflow.spec.ts | 19 +++++-- .../journeys/user-permission-boundary.spec.ts | 54 +++++++++++++++---- 7 files changed, 97 insertions(+), 24 deletions(-) create mode 100644 novalon-manage-api/manage-db/src/main/resources/db/migration/V9__Grant_permissions.sql diff --git a/novalon-manage-api/manage-app/src/main/resources/application-dev.yml b/novalon-manage-api/manage-app/src/main/resources/application-dev.yml index 228374b..baa3279 100644 --- a/novalon-manage-api/manage-app/src/main/resources/application-dev.yml +++ b/novalon-manage-api/manage-app/src/main/resources/application-dev.yml @@ -5,6 +5,9 @@ spring: password: novalon123 flyway: enabled: true + locations: classpath:db/migration + baseline-on-migrate: true + validate-on-migrate: true rate: limit: diff --git a/novalon-manage-api/manage-db/src/main/resources/db/migration/V2__Insert_initial_data.sql b/novalon-manage-api/manage-db/src/main/resources/db/migration/V2__Insert_initial_data.sql index 62f4d22..faff6d7 100644 --- a/novalon-manage-api/manage-db/src/main/resources/db/migration/V2__Insert_initial_data.sql +++ b/novalon-manage-api/manage-db/src/main/resources/db/migration/V2__Insert_initial_data.sql @@ -3,18 +3,26 @@ -- 描述: 插入必要的初始数据 -- 插入初始角色 -INSERT INTO roles (role_name, role_key, role_sort, status, create_by, update_by) +INSERT INTO sys_role (role_name, role_key, role_sort, status, create_by, update_by) VALUES ('超级管理员', 'admin', 1, 1, 'system', 'system') ON CONFLICT (role_key) DO NOTHING; -- 插入初始管理员用户 -- BCrypt哈希值对应明文密码: admin123 -INSERT INTO users (id, username, password, email, phone, status, create_by, update_by) +INSERT INTO sys_user (id, username, password, email, phone, status, create_by, update_by) VALUES (1, 'admin', '$2b$12$SFefXlGRFMA0fvxIufpWPuIAl0OPLgRDoCZPThCvjpiJGPYS8yNYy', 'admin@novalon.com', '13800138000', 1, 'system', 'system') ON CONFLICT (username) DO UPDATE SET password = EXCLUDED.password, status = EXCLUDED.status; +-- 插入测试用户(用于E2E测试) +-- BCrypt哈希值对应明文密码: admin123 +INSERT INTO sys_user (id, username, password, email, phone, status, create_by, update_by) +VALUES (2, 'user', '$2b$12$SFefXlGRFMA0fvxIufpWPuIAl0OPLgRDoCZPThCvjpiJGPYS8yNYy', 'user@novalon.com', '13800138001', 1, 'system', 'system') +ON CONFLICT (username) DO UPDATE SET + password = EXCLUDED.password, + status = EXCLUDED.status; + -- 插入初始字典类型 INSERT INTO sys_dict_type (dict_name, dict_type, status, remark, create_by, update_by) VALUES @@ -52,8 +60,8 @@ VALUES ON CONFLICT (config_key) DO NOTHING; -- 重置序列值 -SELECT setval('users_id_seq', (SELECT COALESCE(MAX(id), 1) FROM users)); -SELECT setval('roles_id_seq', (SELECT COALESCE(MAX(id), 1) FROM roles)); +SELECT setval('sys_user_id_seq', (SELECT COALESCE(MAX(id), 1) FROM sys_user)); +SELECT setval('sys_role_id_seq', (SELECT COALESCE(MAX(id), 1) FROM sys_role)); SELECT setval('sys_dict_type_id_seq', (SELECT COALESCE(MAX(id), 1) FROM sys_dict_type)); SELECT setval('sys_dict_data_id_seq', (SELECT COALESCE(MAX(id), 1) FROM sys_dict_data)); SELECT setval('sys_config_id_seq', (SELECT COALESCE(MAX(id), 1) FROM sys_config)); \ No newline at end of file diff --git a/novalon-manage-api/manage-db/src/main/resources/db/migration/V4__Create_permission_tables.sql b/novalon-manage-api/manage-db/src/main/resources/db/migration/V4__Create_permission_tables.sql index b0ba4b6..99e82c0 100644 --- a/novalon-manage-api/manage-db/src/main/resources/db/migration/V4__Create_permission_tables.sql +++ b/novalon-manage-api/manage-db/src/main/resources/db/migration/V4__Create_permission_tables.sql @@ -27,7 +27,7 @@ CREATE TABLE IF NOT EXISTS sys_role_permission ( update_by VARCHAR(50), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE, + FOREIGN KEY (role_id) REFERENCES sys_role(id) ON DELETE CASCADE, FOREIGN KEY (permission_id) REFERENCES sys_permission(id) ON DELETE CASCADE, UNIQUE (role_id, permission_id) ); diff --git a/novalon-manage-api/manage-db/src/main/resources/db/migration/V5__Create_indexes.sql b/novalon-manage-api/manage-db/src/main/resources/db/migration/V5__Create_indexes.sql index 47b0aaa..5633553 100644 --- a/novalon-manage-api/manage-db/src/main/resources/db/migration/V5__Create_indexes.sql +++ b/novalon-manage-api/manage-db/src/main/resources/db/migration/V5__Create_indexes.sql @@ -3,15 +3,15 @@ -- 描述: 为表创建必要的索引以提升查询性能 -- 用户表索引 -CREATE INDEX IF NOT EXISTS idx_users_username ON users(username); -CREATE INDEX IF NOT EXISTS idx_users_email ON users(email); -CREATE INDEX IF NOT EXISTS idx_users_status ON users(status); -CREATE INDEX IF NOT EXISTS idx_users_deleted_at ON users(deleted_at); +CREATE INDEX IF NOT EXISTS idx_users_username ON sys_user(username); +CREATE INDEX IF NOT EXISTS idx_users_email ON sys_user(email); +CREATE INDEX IF NOT EXISTS idx_users_status ON sys_user(status); +CREATE INDEX IF NOT EXISTS idx_users_deleted_at ON sys_user(deleted_at); -- 角色表索引 -CREATE INDEX IF NOT EXISTS idx_roles_role_key ON roles(role_key); -CREATE INDEX IF NOT EXISTS idx_roles_status ON roles(status); -CREATE INDEX IF NOT EXISTS idx_roles_deleted_at ON roles(deleted_at); +CREATE INDEX IF NOT EXISTS idx_roles_role_key ON sys_role(role_key); +CREATE INDEX IF NOT EXISTS idx_roles_status ON sys_role(status); +CREATE INDEX IF NOT EXISTS idx_roles_deleted_at ON sys_role(deleted_at); -- 菜单表索引 CREATE INDEX IF NOT EXISTS idx_sys_menu_parent_id ON sys_menu(parent_id); diff --git a/novalon-manage-api/manage-db/src/main/resources/db/migration/V9__Grant_permissions.sql b/novalon-manage-api/manage-db/src/main/resources/db/migration/V9__Grant_permissions.sql new file mode 100644 index 0000000..268dc90 --- /dev/null +++ b/novalon-manage-api/manage-db/src/main/resources/db/migration/V9__Grant_permissions.sql @@ -0,0 +1,13 @@ +-- Novalon管理系统权限授予脚本 +-- 版本: V9 +-- 描述: 为novalon用户授予所有表的访问权限 + +-- 授予所有表的SELECT, INSERT, UPDATE, DELETE权限 +GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO novalon; + +-- 授予所有序列的使用权限 +GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO novalon; + +-- 设置默认权限,使未来创建的表自动授予novalon用户权限 +ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO novalon; +ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE, SELECT ON SEQUENCES TO novalon; diff --git a/novalon-manage-web/e2e/journeys/admin-complete-workflow.spec.ts b/novalon-manage-web/e2e/journeys/admin-complete-workflow.spec.ts index a49b606..542fcb8 100644 --- a/novalon-manage-web/e2e/journeys/admin-complete-workflow.spec.ts +++ b/novalon-manage-web/e2e/journeys/admin-complete-workflow.spec.ts @@ -14,13 +14,26 @@ test.describe('管理员完整工作流', () => { await expect(page).toHaveTitle(/登录/); }); + await test.step('等待页面加载完成', async () => { + await page.waitForLoadState('networkidle'); + await expect(page.locator('input[placeholder*="用户名"]')).toBeVisible({ timeout: 10000 }); + }); + await test.step('输入管理员凭证', async () => { - await page.locator('input[placeholder*="用户名"]').fill('admin'); - await page.locator('input[placeholder*="密码"]').fill('admin123'); + const usernameInput = page.locator('input[placeholder*="用户名"]'); + const passwordInput = page.locator('input[placeholder*="密码"]'); + + await usernameInput.waitFor({ state: 'visible' }); + await usernameInput.fill('admin'); + + await passwordInput.waitFor({ state: 'visible' }); + await passwordInput.fill('admin123'); }); await test.step('点击登录按钮', async () => { - await page.locator('button:has-text("登录")').click(); + const loginButton = page.locator('button:has-text("登录")'); + await loginButton.waitFor({ state: 'visible' }); + await loginButton.click(); }); await test.step('验证登录成功', async () => { diff --git a/novalon-manage-web/e2e/journeys/user-permission-boundary.spec.ts b/novalon-manage-web/e2e/journeys/user-permission-boundary.spec.ts index 0f4a9db..8599fbc 100644 --- a/novalon-manage-web/e2e/journeys/user-permission-boundary.spec.ts +++ b/novalon-manage-web/e2e/journeys/user-permission-boundary.spec.ts @@ -4,9 +4,21 @@ test.describe('用户权限边界验证', () => { test('管理员可以访问所有管理功能', async ({ page }) => { await test.step('管理员登录', async () => { await page.goto('/login'); - await page.locator('input[placeholder*="用户名"]').fill('admin'); - await page.locator('input[placeholder*="密码"]').fill('admin123'); - await page.locator('button:has-text("登录")').click(); + await page.waitForLoadState('networkidle'); + + const usernameInput = page.locator('input[placeholder*="用户名"]'); + const passwordInput = page.locator('input[placeholder*="密码"]'); + const loginButton = page.locator('button:has-text("登录")'); + + await usernameInput.waitFor({ state: 'visible' }); + await usernameInput.fill('admin'); + + await passwordInput.waitFor({ state: 'visible' }); + await passwordInput.fill('admin123'); + + await loginButton.waitFor({ state: 'visible' }); + await loginButton.click(); + await page.waitForURL('**/dashboard', { timeout: 30000 }); }); @@ -34,9 +46,21 @@ test.describe('用户权限边界验证', () => { test('普通用户只能访问个人信息', async ({ page }) => { await test.step('普通用户登录', async () => { await page.goto('/login'); - await page.locator('input[placeholder*="用户名"]').fill('user'); - await page.locator('input[placeholder*="密码"]').fill('user123'); - await page.locator('button:has-text("登录")').click(); + await page.waitForLoadState('networkidle'); + + const usernameInput = page.locator('input[placeholder*="用户名"]'); + const passwordInput = page.locator('input[placeholder*="密码"]'); + const loginButton = page.locator('button:has-text("登录")'); + + await usernameInput.waitFor({ state: 'visible' }); + await usernameInput.fill('user'); + + await passwordInput.waitFor({ state: 'visible' }); + await passwordInput.fill('admin123'); + + await loginButton.waitFor({ state: 'visible' }); + await loginButton.click(); + await page.waitForURL('**/dashboard', { timeout: 30000 }); }); @@ -65,9 +89,21 @@ test.describe('用户权限边界验证', () => { test('权限不足时显示提示信息', async ({ page }) => { await test.step('普通用户登录', async () => { await page.goto('/login'); - await page.locator('input[placeholder*="用户名"]').fill('user'); - await page.locator('input[placeholder*="密码"]').fill('user123'); - await page.locator('button:has-text("登录")').click(); + await page.waitForLoadState('networkidle'); + + const usernameInput = page.locator('input[placeholder*="用户名"]'); + const passwordInput = page.locator('input[placeholder*="密码"]'); + const loginButton = page.locator('button:has-text("登录")'); + + await usernameInput.waitFor({ state: 'visible' }); + await usernameInput.fill('user'); + + await passwordInput.waitFor({ state: 'visible' }); + await passwordInput.fill('admin123'); + + await loginButton.waitFor({ state: 'visible' }); + await loginButton.click(); + await page.waitForURL('**/dashboard', { timeout: 30000 }); });