/** * 学校端 E2E 测试 - 通用工具函数 */ import { Page, expect } from '@playwright/test'; import { SCHOOL_CONFIG } from './fixtures'; /** * 使用学校端账号登录 */ export async function loginAsSchool(page: Page) { await page.goto('/login'); // 点击学校角色按钮 await page.locator('.role-btn').filter({ hasText: '学校' }).first().click(); // 输入账号密码 await page.getByPlaceholder('请输入账号').fill(SCHOOL_CONFIG.account); await page.getByPlaceholder('请输入密码').fill(SCHOOL_CONFIG.password); // 点击登录按钮 await page.locator('.login-btn').click(); // 等待登录按钮消失(表示登录请求完成) await page.locator('.login-btn').waitFor({ state: 'hidden', timeout: 10000 }); // 等待页面加载 await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {}); // 等待 URL 包含 school(使用正则表达式) await page.waitForURL(/school/, { timeout: 5000 }).catch(() => {}); } /** * 点击二级菜单项 * @param page 页面对象 * @param parentMenu 一级菜单文本(如"人员管理"、"教学管理"、"数据中心"、"系统管理") * @param childMenu 二级菜单文本(如"教师管理"、"学生管理"等) */ export async function clickSubMenu(page: Page, parentMenu: string, childMenu: string) { // 等待页面加载 await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {}); await page.waitForTimeout(2000); // 检查侧边栏是否折叠,如果折叠则展开 const isCollapsed = await page.locator('.ant-layout-sider-collapsed').count() > 0; if (isCollapsed) { const collapseButton = page.locator('.trigger').first(); await collapseButton.click(); await page.waitForTimeout(1000); } // 点击一级菜单展开,等待菜单动画 const parentMenuItem = page.locator('.ant-menu-submenu-title:has-text("' + parentMenu + '")').first(); await parentMenuItem.click(); // 等待二级菜单DOM 出现(使用 waitForSelector 而不是 visible 检查) await page.waitForSelector('.ant-menu-submenu-open', { timeout: 5000 }).catch(() => {}); await page.waitForTimeout(500); // 使用 evaluate 在浏览器上下文中点击,绕过可见性检查 await page.evaluate((menuText) => { const items = Array.from(document.querySelectorAll('.ant-menu-item')); const target = items.find(item => item.textContent?.includes(menuText)); if (target) { (target as HTMLElement).click(); } }, childMenu); await page.waitForTimeout(1500); } /** * 退出登录 */ export async function logout(page: Page) { // 尝试多种方式找到退出登录按钮 // 方式 1:查找退出登录按钮(常见文本) const logoutBtn1 = page.getByText(/退出登录|退出|logout/i).first(); if (await logoutBtn1.count() > 0) { try { await logoutBtn1.click({ timeout: 3000 }); await page.waitForURL(/.*\/login.*/, { timeout: 10000 }).catch(() => {}); return; } catch (e) { // 如果点击失败,继续尝试其他方式 } } // 方式 2:查找用户头像/菜单按钮并点击 const userMenuBtn = page.locator('.ant-dropdown-trigger, .user-menu, [class*="user"]').first(); if (await userMenuBtn.count() > 0) { try { await userMenuBtn.click({ timeout: 3000 }); await page.waitForTimeout(500); const logoutInMenu = page.getByText(/退出登录|退出|logout/i).first(); if (await logoutInMenu.count() > 0) { await logoutInMenu.click({ timeout: 3000 }); await page.waitForURL(/.*\/login.*/, { timeout: 10000 }).catch(() => {}); return; } } catch (e) { // 如果点击失败,继续尝试其他方式 } } // 方式 3:尝试清空 localStorage 和 sessionStorage 并跳转到登录页 await page.evaluate(() => { localStorage.clear(); sessionStorage.clear(); }); await page.goto('/login'); await page.waitForURL(/.*\/login.*/, { timeout: 10000 }); } /** * 等待表格加载完成 */ export async function waitForTable(page: Page, timeout = 10000) { await page.waitForSelector('table, .ant-table', { timeout }); } /** * 等待弹窗显示 */ export async function waitForModal(page: Page, title?: string, timeout = 5000) { if (title) { await page.getByText(title).waitFor({ timeout }); } else { await page.waitForSelector('.ant-modal', { timeout }); } } /** * 等待成功提示 */ export async function waitForSuccess(page: Page, message?: string, timeout = 5000) { if (message) { await page.getByText(message).waitFor({ timeout }); } else { await page.waitForSelector('.ant-message-success', { timeout }); } } /** * 等待错误提示 */ export async function waitForError(page: Page, message?: string, timeout = 5000) { if (message) { await page.getByText(message).waitFor({ timeout }); } else { await page.waitForSelector('.ant-message-error', { timeout }); } } /** * 在表格中查找并点击操作按钮 */ export async function clickRowAction(page: Page, rowName: string, action: string) { const row = page.getByRole('row').filter({ hasText: rowName }); await row.getByRole('button', { name: action }).click(); } /** * 关闭弹窗 */ export async function closeModal(page: Page) { await page.keyboard.press('Escape'); // 或者点击关闭按钮 const closeBtn = page.locator('.ant-modal-close'); if (await closeBtn.count() > 0) { await closeBtn.click(); } } /** * 等待页面加载完成 */ export async function waitForPageLoad(page: Page, timeout = 10000) { await page.waitForLoadState('networkidle', { timeout }); }