import { test, expect } from '@playwright/test'; test.describe('上课/授课模式流程', () => { let baseURL = 'http://localhost:5173'; test.beforeEach(async ({ page }) => { await page.goto(baseURL); await page.waitForLoadState('networkidle'); await page.click('text=教师'); await page.waitForTimeout(500); const accountInput = page.locator('input[placeholder*="账号"]').or(page.locator('input[name="account"]')); await accountInput.fill('teacher1'); const passwordInput = page.locator('input[placeholder*="密码"]').or(page.locator('input[type="password"]')); await passwordInput.fill('123456'); const loginButton = page.locator('button[type="submit"]').or(page.locator('.login-btn')).or(page.locator('button:has-text("登录")')); await loginButton.click(); await page.waitForURL('**/dashboard', { timeout: 10000 }).catch(() => { return page.waitForURL('**/courses', { timeout: 5000 }); }); await page.waitForTimeout(1000); }); test('测试1: 从课程详情进入上课', async ({ page }) => { test.slow(); // 1. 进入课程中心 await page.click('text=课程中心'); await page.waitForURL('**/courses', { timeout: 10000 }); await page.waitForTimeout(1000); // 2. 选择课程 const firstCourseCard = page.locator('.course-card').or(page.locator('[class*="course-card"]')).first(); await firstCourseCard.click(); await page.waitForTimeout(2000); // 3. 点击"选择课程上课"按钮 const selectLessonsButton = page.locator('button:has-text("选择课程上课")'); if (await selectLessonsButton.count() > 0) { await selectLessonsButton.click(); await page.waitForTimeout(2000); // 4. 验证课程选择弹窗 const modal = page.locator('[class*="modal"], .ant-modal'); expect(await modal.count()).toBeGreaterThan(0); // 5. 选择所有课程 const selectAllCheckbox = page.locator('input[type="checkbox"]').first(); if (await selectAllCheckbox.count() > 0) { await selectAllCheckbox.click(); await page.waitForTimeout(500); } // 6. 确认选择 const confirmButton = page.locator('button:has-text("确定")').or(page.locator('button:has-text("开始上课")')); if (await confirmButton.count() > 0) { await confirmButton.click(); await page.waitForTimeout(2000); } } // 7. 验证进入上课模式 await page.waitForURL('**/lessons/**', { timeout: 10000 }).catch(() => { return page.waitForURL('**/start', { timeout: 5000 }); }); await page.screenshot({ path: 'test-results/teaching-enter.png' }); }); test('测试2: 上课模式布局验证', async ({ page }) => { test.slow(); // 进入上课模式 await page.click('text=课程中心'); await page.waitForURL('**/courses', { timeout: 10000 }); await page.waitForTimeout(1000); const firstCourseCard = page.locator('.course-card').or(page.locator('[class*="course-card"]')).first(); await firstCourseCard.click(); await page.waitForTimeout(2000); const selectLessonsButton = page.locator('button:has-text("选择课程上课")'); if (await selectLessonsButton.count() > 0) { await selectLessonsButton.click(); await page.waitForTimeout(2000); const modal = page.locator('[class*="modal"]'); if (await modal.count() > 0) { const confirmButton = page.locator('button:has-text("确定")').or(page.locator('button:has-text("开始")')); if (await confirmButton.count() > 0) { await confirmButton.click(); await page.waitForTimeout(3000); } } } // 等待上课页面加载 await page.waitForTimeout(3000); const currentUrl = page.url(); // 1. 验证上课模式布局 const mainContent = page.locator('main, [class*="lesson"], [class*="teaching"]'); expect(await mainContent.count()).toBeGreaterThan(0); // 2. 验证课程进度导航 const navigation = page.locator('[class*="nav"], [class*="step"], [class*="progress"]'); const navCount = await navigation.count(); test.info().annotations.push({ type: 'info', description: `导航区域数量: ${navCount}`, }); // 3. 验证内容展示区 const contentArea = page.locator('[class*="content"], [class*="display"]'); expect(await contentArea.count()).toBeGreaterThan(0); // 4. 验证控制按钮 const buttons = page.locator('button'); const buttonCount = await buttons.count(); test.info().annotations.push({ type: 'info', description: `页面按钮数量: ${buttonCount}, URL: ${currentUrl}`, }); await page.screenshot({ path: 'test-results/teaching-layout.png', fullPage: true }); }); test('测试3: 课程切换功能', async ({ page }) => { test.slow(); // 进入上课模式(简化流程) await page.click('text=上课记录'); await page.waitForTimeout(2000); // 查找已有的上课记录并进入 const recordItems = page.locator('[class*="record"], [class*="lesson"]'); if (await recordItems.count() > 0) { await recordItems.first().click(); await page.waitForTimeout(2000); } else { // 如果没有记录,从课程中心创建 await page.click('text=课程中心'); await page.waitForTimeout(1000); const firstCourseCard = page.locator('.course-card').or(page.locator('[class*="course-card"]')).first(); await firstCourseCard.click(); await page.waitForTimeout(2000); const selectLessonsButton = page.locator('button:has-text("选择课程上课")'); if (await selectLessonsButton.count() > 0) { await selectLessonsButton.click(); await page.waitForTimeout(2000); const confirmButton = page.locator('button:has-text("确定")'); if (await confirmButton.count() > 0) { await confirmButton.click(); await page.waitForTimeout(3000); } } } await page.waitForTimeout(2000); // 1. 查找课程切换器 const lessonSelector = page.locator('[class*="selector"], [class*="dropdown"]').or(page.locator('select')); const hasSelector = await lessonSelector.count() > 0; if (hasSelector) { // 2. 尝试切换课程 await lessonSelector.first().click(); await page.waitForTimeout(1000); // 3. 选择其他课程(如果存在) const options = page.locator('[class*="option"], .ant-select-item'); if (await options.count() > 1) { await options.nth(1).click(); await page.waitForTimeout(1500); test.info().annotations.push({ type: 'success', description: '课程切换功能测试完成', }); } } else { test.info().annotations.push({ type: 'warning', description: '未找到课程选择器', }); } await page.screenshot({ path: 'test-results/teaching-switch-lesson.png' }); }); test('测试4: 教学环节切换', async ({ page }) => { test.slow(); // 进入上课模式 await page.click('text=上课记录'); await page.waitForTimeout(2000); const recordItems = page.locator('[class*="record"], [class*="lesson"]'); if (await recordItems.count() > 0) { await recordItems.first().click(); await page.waitForTimeout(2000); } else { await page.click('text=课程中心'); await page.waitForTimeout(1000); const firstCourseCard = page.locator('.course-card').or(page.locator('[class*="course-card"]')).first(); await firstCourseCard.click(); await page.waitForTimeout(2000); const selectLessonsButton = page.locator('button:has-text("选择课程上课")'); if (await selectLessonsButton.count() > 0) { await selectLessonsButton.click(); await page.waitForTimeout(2000); const confirmButton = page.locator('button:has-text("确定")'); if (await confirmButton.count() > 0) { await confirmButton.click(); await page.waitForTimeout(3000); } } } await page.waitForTimeout(2000); // 1. 查找环节导航 const stepNav = page.locator('[class*="step"], [class*="stage"], .ant-steps'); const stepCount = await stepNav.count(); if (stepCount > 0) { // 2. 获取当前环节信息 const currentStep = page.locator('[class*="current"], .ant-steps-item-active'); const currentInfo = await currentStep.count() > 0 ? await currentStep.first().textContent() : '未找到'; // 3. 尝试下一个环节 const nextButton = page.locator('button:has-text("下一")').or(page.locator('button:has-text("下一步")')); if (await nextButton.count() > 0) { await nextButton.first().click(); await page.waitForTimeout(1500); test.info().annotations.push({ type: 'success', description: `切换到下一个环节,之前: ${currentInfo}`, }); } // 4. 尝试上一个环节 const prevButton = page.locator('button:has-text("上一")').or(page.locator('button:has-text("上一步")')); if (await prevButton.count() > 0) { await prevButton.first().click(); await page.waitForTimeout(1500); } } else { test.info().annotations.push({ type: 'warning', description: '未找到环节导航', }); } await page.screenshot({ path: 'test-results/teaching-switch-step.png' }); }); test('测试5: Kids Mode 展示', async ({ page }) => { test.slow(); // 进入上课模式 await page.click('text=上课记录'); await page.waitForTimeout(2000); const recordItems = page.locator('[class*="record"], [class*="lesson"]'); if (await recordItems.count() > 0) { await recordItems.first().click(); await page.waitForTimeout(2000); } else { await page.click('text=课程中心'); await page.waitForTimeout(1000); const firstCourseCard = page.locator('.course-card').or(page.locator('[class*="course-card"]')).first(); await firstCourseCard.click(); await page.waitForTimeout(2000); const selectLessonsButton = page.locator('button:has-text("选择课程上课")'); if (await selectLessonsButton.count() > 0) { await selectLessonsButton.click(); await page.waitForTimeout(2000); const confirmButton = page.locator('button:has-text("确定")'); if (await confirmButton.count() > 0) { await confirmButton.click(); await page.waitForTimeout(3000); } } } await page.waitForTimeout(2000); // 1. 验证 Kids Mode 内容区 const kidsMode = page.locator('[class*="kids"], [class*="child"], [class*="display"]'); const hasKidsMode = await kidsMode.count() > 0; test.info().annotations.push({ type: 'info', description: hasKidsMode ? '找到 Kids Mode 展示区' : '未找到 Kids Mode', }); // 2. 验证内容元素(图片、文字等) if (hasKidsMode) { const images = kidsMode.first().locator('img'); const imageCount = await images.count(); const texts = await kidsMode.first().allTextContents(); test.info().annotations.push({ type: 'info', description: `Kids Mode 图片数: ${imageCount}, 文本段落数: ${texts.length}`, }); } await page.screenshot({ path: 'test-results/teaching-kids-mode.png', fullPage: true }); }); test('测试6: 展播模式', async ({ page }) => { test.slow(); // 进入上课模式 await page.click('text=上课记录'); await page.waitForTimeout(2000); const recordItems = page.locator('[class*="record"], [class*="lesson"]'); if (await recordItems.count() > 0) { await recordItems.first().click(); await page.waitForTimeout(2000); } else { await page.click('text=课程中心'); await page.waitForTimeout(1000); const firstCourseCard = page.locator('.course-card').or(page.locator('[class*="course-card"]')).first(); await firstCourseCard.click(); await page.waitForTimeout(2000); const selectLessonsButton = page.locator('button:has-text("选择课程上课")'); if (await selectLessonsButton.count() > 0) { await selectLessonsButton.click(); await page.waitForTimeout(2000); const confirmButton = page.locator('button:has-text("确定")'); if (await confirmButton.count() > 0) { await confirmButton.click(); await page.waitForTimeout(3000); } } } await page.waitForTimeout(2000); // 1. 查找展播模式按钮 const broadcastButton = page.locator('button:has-text("展播")').or(page.locator('button:has-text("全屏")')).or(page.locator('[class*="broadcast"]')); const buttonExists = await broadcastButton.count() > 0; if (buttonExists) { // 2. 点击展播模式(新标签页打开) const [newPage] = await Promise.all([ page.context().waitForEvent('page'), broadcastButton.first().click() ]); await newPage.waitForLoadState('networkidle'); await newPage.waitForTimeout(2000); // 3. 验证展播模式页面 const isBroadcastMode = await newPage.locator('[class*="broadcast"], [class*="fullscreen"]').count() > 0; const hasContent = await newPage.locator('img, video, [class*="content"]').count() > 0; test.info().annotations.push({ type: 'info', description: isBroadcastMode ? '进入展播模式' : '展播页面已打开', }); test.info().annotations.push({ type: 'info', description: hasContent ? '内容正常显示' : '未找到内容', }); // 4. 截图 await newPage.screenshot({ path: 'test-results/broadcast-mode.png', fullPage: true }); // 5. 关闭新标签页 await newPage.close(); } else { test.info().annotations.push({ type: 'warning', description: '未找到展播模式按钮', }); } }); test('测试7: 键盘快捷键控制', async ({ page }) => { test.slow(); // 进入上课模式 await page.click('text=上课记录'); await page.waitForTimeout(2000); const recordItems = page.locator('[class*="record"], [class*="lesson"]'); if (await recordItems.count() > 0) { await recordItems.first().click(); await page.waitForTimeout(2000); } else { return test.skip('无法进入上课模式'); } // 1. 测试空格键暂停/播放 await page.keyboard.press('Space'); await page.waitForTimeout(1000); // 2. 测试左右箭头切换环节 await page.keyboard.press('ArrowRight'); await page.waitForTimeout(1000); await page.keyboard.press('ArrowLeft'); await page.waitForTimeout(1000); // 3. 测试ESC退出全屏 await page.keyboard.press('Escape'); await page.waitForTimeout(1000); test.info().annotations.push({ type: 'success', description: '键盘快捷键测试完成', }); await page.screenshot({ path: 'test-results/teaching-keyboard.png' }); }); test('测试8: 资源预览功能', async ({ page }) => { test.slow(); // 进入上课模式 await page.click('text=上课记录'); await page.waitForTimeout(2000); const recordItems = page.locator('[class*="record"], [class*="lesson"]'); if (await recordItems.count() > 0) { await recordItems.first().click(); await page.waitForTimeout(2000); } else { return test.skip('无法进入上课模式'); } // 1. 查找资源列表或预览区 const resourceList = page.locator('[class*="resource"], [class*="material"]'); const resourceCount = await resourceList.count(); if (resourceCount > 0) { // 2. 点击第一个资源 await resourceList.first().click(); await page.waitForTimeout(1500); // 3. 验证资源预览弹窗或内容区 const preview = page.locator('[class*="preview"], [class*="modal"]'); const hasPreview = await preview.count() > 0; test.info().annotations.push({ type: 'info', description: hasPreview ? '显示资源预览' : '未找到预览弹窗', }); // 4. 关闭预览(如果有弹窗) if (hasPreview) { const closeButton = page.locator('button:has-text("关闭")').or(page.locator('.ant-modal-close')); if (await closeButton.count() > 0) { await closeButton.first().click(); await page.waitForTimeout(500); } } } else { test.info().annotations.push({ type: 'warning', description: '未找到资源列表', }); } await page.screenshot({ path: 'test-results/teaching-resource-preview.png' }); }); test('测试9: 上课结束流程', async ({ page }) => { test.slow(); // 进入上课模式 await page.click('text=上课记录'); await page.waitForTimeout(2000); const recordItems = page.locator('[class*="record"], [class*="lesson"]'); if (await recordItems.count() > 0) { await recordItems.first().click(); await page.waitForTimeout(2000); } else { return test.skip('无法进入上课模式'); } // 1. 查找结束上课按钮 const endButton = page.locator('button:has-text("结束")').or(page.locator('button:has-text("完成")')).or(page.locator('button:has-text("下课")')); const buttonExists = await endButton.count() > 0; if (buttonExists) { // 2. 点击结束按钮 await endButton.first().click(); await page.waitForTimeout(2000); // 3. 验证确认弹窗 const confirmModal = page.locator('[class*="modal"]'); const hasModal = await confirmModal.count() > 0; if (hasModal) { // 4. 确认结束 const confirmButton = confirmModal.locator('button:has-text("确定")'); if (await confirmButton.count() > 0) { await confirmButton.click(); await page.waitForTimeout(2000); } } // 5. 验证跳转到课后记录页面 const url = page.url(); const isPostClass = url.includes('record') || url.includes('feedback'); test.info().annotations.push({ type: 'info', description: isPostClass ? '成功跳转到课后记录' : `当前URL: ${url}`, }); } else { test.info().annotations.push({ type: 'warning', description: '未找到结束上课按钮', }); } await page.screenshot({ path: 'test-results/teaching-end-class.png' }); }); });