/** * 阅读任务模块 - 三端完整测试 * * 测试范围: * - 教师端: 任务创建(含关联绘本)、完成情况、评价功能 * - 家长端: 任务查看、提交功能、查看评价 * - 学校端: 只读列表、多维度筛选、任务详情 */ import { test, expect, Page } from '@playwright/test'; // 测试账号 const TEST_ACCOUNTS = { teacher: { username: 'teacher1', password: '123456', name: '李老师' }, parent: { username: 'parent1', password: '123456', name: '张妈妈' }, school: { username: 'school1', password: '123456', name: '阳光幼儿园' } }; // 基础URL const BASE_URL = 'http://localhost:5173'; // 生成唯一任务标题 const generateTaskTitle = () => `【E2E测试】亲子阅读任务_${Date.now()}`; /** * 登录辅助函数 */ async function login(page: Page, role: 'teacher' | 'parent' | 'school') { const account = TEST_ACCOUNTS[role]; await page.goto(`${BASE_URL}/login`); await page.waitForLoadState('networkidle'); // 等待页面加载完成 await page.waitForSelector('.login-card', { timeout: 10000 }); // 选择角色 const roleMap: Record = { teacher: '教师', parent: '家长', school: '学校' }; const roleBtn = page.locator(`.role-btn:has-text("${roleMap[role]}")`); await roleBtn.click(); await page.waitForTimeout(300); // 填写账号 const accountInput = page.locator('input[placeholder*="账号"]').first(); await accountInput.clear(); await accountInput.fill(account.username); // 填写密码 const passwordInput = page.locator('input[placeholder*="密码"]').first(); await passwordInput.clear(); await passwordInput.fill(account.password); // 点击登录按钮 const loginBtn = page.locator('button.login-btn, button:has-text("登录")').first(); await loginBtn.click(); // 等待跳转 await page.waitForURL(/\/(teacher|parent|school)/, { timeout: 15000 }); // 验证登录成功 await expect(page).toHaveURL(new RegExp(`/${role}`)); } // ==================== 教师端测试 ==================== test.describe('教师端 - 阅读任务管理', () => { let teacherPage: Page; const taskTitle = generateTaskTitle(); test.beforeAll(async ({ browser }) => { teacherPage = await browser.newPage(); await login(teacherPage, 'teacher'); }); test.afterAll(async () => { await teacherPage.close(); }); test('T-LIST-01: 任务列表加载', async () => { // 导航到任务列表 await teacherPage.goto(`${BASE_URL}/teacher/tasks`); await teacherPage.waitForLoadState('networkidle'); // 验证页面标题 await expect(teacherPage.locator('h2, .ant-page-header-heading-title')).toContainText(/任务/); // 验证任务卡片或列表存在 const taskCards = teacherPage.locator('.ant-card, .task-card, [class*="task-item"]'); await expect(taskCards.first()).toBeVisible({ timeout: 5000 }); console.log('✅ T-LIST-01 通过: 任务列表正常加载'); }); test('T-CREATE-01~11: 创建任务(含关联绘本字段)', async () => { // 点击新建任务按钮 await teacherPage.goto(`${BASE_URL}/teacher/tasks`); await teacherPage.waitForLoadState('networkidle'); const createBtn = teacherPage.locator('button:has-text("新建"), button:has-text("创建"), .ant-btn-primary:has-text("任务")'); await createBtn.first().click(); // 等待弹窗出现 await teacherPage.waitForSelector('.ant-modal', { timeout: 5000 }); // 填写任务标题 await teacherPage.fill('input[placeholder*="标题"], #title', taskTitle); // 填写任务描述 await teacherPage.fill('textarea[placeholder*="描述"], #description', 'E2E自动化测试任务,请勿删除'); // 选择任务类型 const typeSelect = teacherPage.locator('.ant-select:has-text("类型"), #type'); if (await typeSelect.isVisible()) { await typeSelect.click(); await teacherPage.click('.ant-select-dropdown:visible li:has-text("阅读")'); } // 填写关联绘本名称(核心验证点) const bookNameInput = teacherPage.locator('input[placeholder*="绘本"], #relatedBookName, input[placeholder*="关联"]'); if (await bookNameInput.isVisible()) { await bookNameInput.fill('好饿的毛毛虫'); console.log('✅ 关联绘本名称字段存在且可填写'); } else { console.log('❌ 关联绘本名称字段不存在!'); } // 选择目标班级 const targetSelect = teacherPage.locator('.ant-select:has-text("班级"), .ant-select:has-text("目标")'); if (await targetSelect.isVisible()) { await targetSelect.click(); await teacherPage.waitForTimeout(300); const firstOption = teacherPage.locator('.ant-select-dropdown:visible li').first(); if (await firstOption.isVisible()) { await firstOption.click(); } } // 设置时间 const today = new Date().toISOString().split('T')[0]; const nextWeek = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0]; const startDateInput = teacherPage.locator('input[placeholder*="开始"], #startDate'); if (await startDateInput.isVisible()) { await startDateInput.fill(today); } const endDateInput = teacherPage.locator('input[placeholder*="截止"], input[placeholder*="结束"], #endDate, #dueDate'); if (await endDateInput.isVisible()) { await endDateInput.fill(nextWeek); } // 点击保存 await teacherPage.click('.ant-modal button:has-text("保存"), .ant-modal button:has-text("确定")'); // 等待保存成功 await teacherPage.waitForTimeout(2000); // 验证保存成功 const successMsg = teacherPage.locator('.ant-message-success, .ant-notification-success'); const saved = await successMsg.isVisible().catch(() => false); if (saved) { console.log('✅ T-CREATE 通过: 任务创建成功'); } else { // 检查列表中是否有新任务 await teacherPage.goto(`${BASE_URL}/teacher/tasks`); await teacherPage.waitForLoadState('networkidle'); const newTask = teacherPage.locator(`text="${taskTitle}"`); await expect(newTask).toBeVisible({ timeout: 5000 }); console.log('✅ T-CREATE 通过: 任务创建成功并在列表显示'); } }); }); // ==================== 学校端只读测试 ==================== test.describe('学校端 - 阅读任务只读查看', () => { let schoolPage: Page; test.beforeAll(async ({ browser }) => { schoolPage = await browser.newPage(); await login(schoolPage, 'school'); }); test.afterAll(async () => { await schoolPage.close(); }); test('S-READONLY-01~05: 验证只读模式', async () => { await schoolPage.goto(`${BASE_URL}/school/reading-tasks`); await schoolPage.waitForLoadState('networkidle'); // 验证没有创建按钮 const createBtn = schoolPage.locator('button:has-text("新建"), button:has-text("创建任务")'); const hasCreateBtn = await createBtn.count(); expect(hasCreateBtn).toBe(0); console.log('✅ S-READONLY-01 通过: 无创建按钮'); // 验证任务卡片没有编辑/删除按钮 const editBtn = schoolPage.locator('button:has-text("编辑")'); const hasEditBtn = await editBtn.count(); expect(hasEditBtn).toBe(0); console.log('✅ S-READONLY-02 通过: 无编辑按钮'); const deleteBtn = schoolPage.locator('button:has-text("删除")'); const hasDeleteBtn = await deleteBtn.count(); expect(hasDeleteBtn).toBe(0); console.log('✅ S-READONLY-03 通过: 无删除按钮'); }); test('S-LIST-01: 任务列表展示', async () => { await schoolPage.goto(`${BASE_URL}/school/reading-tasks`); await schoolPage.waitForLoadState('networkidle'); // 验证任务卡片存在 const taskCards = schoolPage.locator('.ant-card, [class*="task"]'); const count = await taskCards.count(); expect(count).toBeGreaterThan(0); console.log(`✅ S-LIST-01 通过: 任务列表显示 ${count} 个任务`); }); test('S-FILTER-01: 多维度筛选功能', async () => { await schoolPage.goto(`${BASE_URL}/school/reading-tasks`); await schoolPage.waitForLoadState('networkidle'); // 测试关键字搜索 const searchInput = schoolPage.locator('input[placeholder*="搜索"], input[placeholder*="关键字"]'); if (await searchInput.isVisible()) { await searchInput.fill('阅读'); await schoolPage.waitForTimeout(500); // 验证搜索结果 const searchResults = schoolPage.locator('.ant-card, [class*="task"]'); const count = await searchResults.count(); console.log(`✅ S-FILTER-01: 关键字搜索返回 ${count} 个结果`); } // 测试类型筛选 const typeFilter = schoolPage.locator('.ant-select:has-text("类型"), #type'); if (await typeFilter.isVisible()) { await typeFilter.click(); await schoolPage.waitForTimeout(300); const readingOption = schoolPage.locator('.ant-select-dropdown:visible li:has-text("阅读")'); if (await readingOption.isVisible()) { await readingOption.click(); await schoolPage.waitForTimeout(500); console.log('✅ S-FILTER-02: 类型筛选可用'); } } }); test('S-DETAIL-01: 任务详情查看', async () => { await schoolPage.goto(`${BASE_URL}/school/reading-tasks`); await schoolPage.waitForLoadState('networkidle'); // 点击第一个任务卡片 const firstTask = schoolPage.locator('.ant-card, [class*="task"]').first(); await firstTask.click(); // 等待详情弹窗或页面 await schoolPage.waitForTimeout(1000); // 验证详情内容 const modal = schoolPage.locator('.ant-modal, .ant-drawer'); const detailPage = schoolPage.locator('[class*="detail"]'); if (await modal.isVisible() || await detailPage.isVisible()) { console.log('✅ S-DETAIL-01 通过: 任务详情可以查看'); // 验证关联绘本字段是否显示 const bookNameElement = schoolPage.locator('text=/绘本|relatedBookName/'); if (await bookNameElement.isVisible()) { console.log('✅ S-DETAIL-02 通过: 关联绘本字段显示'); } } else { console.log('⚠️ S-DETAIL-01: 任务详情未显示'); } }); }); // ==================== 家长端测试 ==================== test.describe('家长端 - 任务查看与提交', () => { let parentPage: Page; test.beforeAll(async ({ browser }) => { parentPage = await browser.newPage(); await login(parentPage, 'parent'); }); test.afterAll(async () => { await parentPage.close(); }); test('P-LIST-01: 任务列表加载', async () => { await parentPage.goto(`${BASE_URL}/parent/tasks`); await parentPage.waitForLoadState('networkidle'); // 验证页面加载 const pageTitle = parentPage.locator('h2, .ant-page-header-heading-title, text=/任务/'); await expect(pageTitle.first()).toBeVisible({ timeout: 5000 }); console.log('✅ P-LIST-01 通过: 家长端任务列表加载'); }); test('P-LIST-02: 状态标签显示', async () => { await parentPage.goto(`${BASE_URL}/parent/tasks`); await parentPage.waitForLoadState('networkidle'); // 检查状态标签 const statusTags = parentPage.locator('.ant-tag'); const count = await statusTags.count(); if (count > 0) { // 验证状态标签文本 const validStatuses = ['待提交', '已提交', '已评价', '待完成', '已完成']; let hasValidStatus = false; for (let i = 0; i < count; i++) { const text = await statusTags.nth(i).textContent(); if (text && validStatuses.some(s => text.includes(s))) { hasValidStatus = true; break; } } expect(hasValidStatus).toBe(true); console.log('✅ P-LIST-02 通过: 状态标签显示正确'); } else { console.log('⚠️ P-LIST-02: 暂无任务数据'); } }); }); // ==================== 跨端流程测试 ==================== test.describe('跨端业务流程验证', () => { test('X-FLOW-01: 验证三端数据一致性', async ({ browser }) => { // 创建三个页面 const teacherPage = await browser.newPage(); const schoolPage = await browser.newPage(); const parentPage = await browser.newPage(); try { // 三端分别登录 await login(teacherPage, 'teacher'); await login(schoolPage, 'school'); await login(parentPage, 'parent'); // 教师端查看任务数量 await teacherPage.goto(`${BASE_URL}/teacher/tasks`); await teacherPage.waitForLoadState('networkidle'); const teacherTaskCount = await teacherPage.locator('.ant-card, [class*="task"]').count(); // 学校端查看任务数量 await schoolPage.goto(`${BASE_URL}/school/reading-tasks`); await schoolPage.waitForLoadState('networkidle'); const schoolTaskCount = await schoolPage.locator('.ant-card, [class*="task"]').count(); // 验证学校端任务数量 >= 教师端(学校可以看到所有教师的任务) expect(schoolTaskCount).toBeGreaterThanOrEqual(teacherTaskCount); console.log(`✅ X-FLOW-01 通过: 教师端 ${teacherTaskCount} 个任务,学校端 ${schoolTaskCount} 个任务`); } finally { await teacherPage.close(); await schoolPage.close(); await parentPage.close(); } }); });