183 lines
5.6 KiB
TypeScript
183 lines
5.6 KiB
TypeScript
|
|
/**
|
|||
|
|
* 学校端 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 });
|
|||
|
|
}
|