前后端目录重命名: - reading-platform-java/ → lesingle-edu-reading-platform-backend/ - reading-platform-frontend/ → lesingle-edu-reading-platform-frontend/ 更新相关文件: - 所有 shell 脚本中的目录引用 - pom.xml 和 application.yml 中的项目名称 - package.json 中的项目名称 - .claude/CLAUDE.md 中的路径引用 - README 文档中的路径引用
531 lines
18 KiB
TypeScript
531 lines
18 KiB
TypeScript
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 }) => {
|
||
// 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 prepareButton = page.locator('button:has-text("开始备课")');
|
||
await prepareButton.click();
|
||
|
||
// 4. 验证跳转
|
||
await page.waitForURL('**/prepare', { timeout: 10000 });
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 5. 验证备课模式结构
|
||
await expect(page.locator('aside, [class*="navigation"], [class*="sidebar"]').first()).toBeVisible();
|
||
await expect(page.locator('[class*="preview"], [class*="content"]').first()).toBeVisible();
|
||
|
||
test.info().annotations.push({
|
||
type: 'success',
|
||
description: '成功进入备课模式',
|
||
});
|
||
|
||
await page.screenshot({ path: 'test-results/prepare-mode-layout.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);
|
||
|
||
await page.click('button:has-text("开始备课")');
|
||
await page.waitForURL('**/prepare', { timeout: 10000 });
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 1. 点击课程包概览
|
||
const overviewNav = page.locator('text=课程包概览').or(page.locator('[class*="overview"]'));
|
||
if (await overviewNav.count() > 0) {
|
||
await overviewNav.first().click();
|
||
await page.waitForTimeout(1000);
|
||
}
|
||
|
||
// 2. 验证基本信息展示
|
||
const basicInfo = page.locator('[class*="basic"], [class*="info"]');
|
||
if (await basicInfo.count() > 0) {
|
||
await expect(basicInfo.first()).toBeVisible();
|
||
}
|
||
|
||
// 3. 验证统计数据
|
||
const stats = page.locator('[class*="stat"], [class*="count"]');
|
||
const statCount = await stats.count();
|
||
|
||
test.info().annotations.push({
|
||
type: 'info',
|
||
description: `统计项数量: ${statCount}`,
|
||
});
|
||
|
||
await page.screenshot({ path: 'test-results/prepare-overview.png' });
|
||
});
|
||
|
||
test('测试3: 左侧导航 - 包含课程', 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);
|
||
|
||
await page.click('button:has-text("开始备课")');
|
||
await page.waitForURL('**/prepare', { timeout: 10000 });
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 1. 点击包含课程
|
||
const lessonsNav = page.locator('text=包含课程').or(page.locator('[class*="lessons"]'));
|
||
if (await lessonsNav.count() > 0) {
|
||
await lessonsNav.first().click();
|
||
await page.waitForTimeout(1000);
|
||
}
|
||
|
||
// 2. 验证课程列表
|
||
const lessonList = page.locator('[class*="lesson"], [class*="course-item"]');
|
||
const lessonCount = await lessonList.count();
|
||
|
||
test.info().annotations.push({
|
||
type: 'info',
|
||
description: `课程数量: ${lessonCount}`,
|
||
});
|
||
|
||
expect(lessonCount).toBeGreaterThan(0);
|
||
|
||
// 3. 点击第一个课程
|
||
if (lessonCount > 0) {
|
||
await lessonList.first().click();
|
||
await page.waitForTimeout(1000);
|
||
}
|
||
|
||
await page.screenshot({ path: 'test-results/prepare-lessons.png' });
|
||
});
|
||
|
||
test('测试4: 右侧内容预览 - 课程介绍', 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);
|
||
|
||
await page.click('button:has-text("开始备课")');
|
||
await page.waitForURL('**/prepare', { timeout: 10000 });
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 1. 点击课程介绍Tab
|
||
const introTab = page.locator('text=课程介绍').or(page.locator('[class*="intro"]'));
|
||
if (await introTab.count() > 0) {
|
||
await introTab.first().click();
|
||
await page.waitForTimeout(1000);
|
||
}
|
||
|
||
// 2. 验证介绍内容展示
|
||
const introContent = page.locator('[class*="intro"], [class*="content"]');
|
||
expect(await introContent.count()).toBeGreaterThan(0);
|
||
|
||
// 3. 验证8个介绍字段
|
||
const introFields = ['简介', '亮点', '目标', '安排', '重难点', '方法', '评价', '注意事项'];
|
||
let visibleFieldCount = 0;
|
||
|
||
for (const field of introFields) {
|
||
const fieldLocator = page.locator(`text=${field}`);
|
||
if (await fieldLocator.count() > 0) {
|
||
visibleFieldCount++;
|
||
}
|
||
}
|
||
|
||
test.info().annotations.push({
|
||
type: 'info',
|
||
description: `介绍字段: ${visibleFieldCount}/${introFields.length}`,
|
||
});
|
||
|
||
await page.screenshot({ path: 'test-results/prepare-intro-content.png', fullPage: true });
|
||
});
|
||
|
||
test('测试5: 右侧内容预览 - 排课参考', 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);
|
||
|
||
await page.click('button:has-text("开始备课")');
|
||
await page.waitForURL('**/prepare', { timeout: 10000 });
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 1. 点击排课参考Tab
|
||
const scheduleTab = page.locator('text=排课参考').or(page.locator('[class*="schedule"]'));
|
||
if (await scheduleTab.count() > 0) {
|
||
await scheduleTab.first().click();
|
||
await page.waitForTimeout(1000);
|
||
}
|
||
|
||
// 2. 验证排课表格或内容
|
||
const scheduleTable = page.locator('table').or(page.locator('[class*="table"]'));
|
||
const scheduleContent = page.locator('[class*="schedule-content"]');
|
||
|
||
const hasTable = await scheduleTable.count() > 0;
|
||
const hasContent = await scheduleContent.count() > 0;
|
||
|
||
test.info().annotations.push({
|
||
type: 'info',
|
||
description: hasTable ? '显示排课表格' : (hasContent ? '显示排课内容' : '未找到排课参考'),
|
||
});
|
||
|
||
await page.screenshot({ path: 'test-results/prepare-schedule.png' });
|
||
});
|
||
|
||
test('测试6: 右侧内容预览 - 环创建设', 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);
|
||
|
||
await page.click('button:has-text("开始备课")');
|
||
await page.waitForURL('**/prepare', { timeout: 10000 });
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 1. 点击环创建设Tab
|
||
const envTab = page.locator('text=环创建设').or(page.locator('[class*="environment"]'));
|
||
if (await envTab.count() > 0) {
|
||
await envTab.first().click();
|
||
await page.waitForTimeout(1000);
|
||
}
|
||
|
||
// 2. 验证环创建设内容
|
||
const envContent = page.locator('text=主题环境').or(page.locator('text=区域活动')).or(page.locator('[class*="environment"]'));
|
||
const hasContent = await envContent.count() > 0;
|
||
|
||
test.info().annotations.push({
|
||
type: 'info',
|
||
description: hasContent ? '显示环创建设内容' : '未找到环创建设',
|
||
});
|
||
|
||
await page.screenshot({ path: 'test-results/prepare-environment.png' });
|
||
});
|
||
|
||
test('测试7: 教学目标展示', 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);
|
||
|
||
await page.click('button:has-text("开始备课")');
|
||
await page.waitForURL('**/prepare', { timeout: 10000 });
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 1. 选择导入课
|
||
const introLesson = page.locator('text=导入课').or(page.locator('[class*="introduction"]'));
|
||
if (await introLesson.count() > 0) {
|
||
await introLesson.first().click();
|
||
await page.waitForTimeout(1000);
|
||
}
|
||
|
||
// 2. 验证教学目标展示
|
||
const objectives = page.locator('text=教学目标').or(page.locator('[class*="objective"]'));
|
||
if (await objectives.count() > 0) {
|
||
await expect(objectives.first()).toBeVisible();
|
||
}
|
||
|
||
// 3. 验证教学准备展示
|
||
const preparation = page.locator('text=教学准备').or(page.locator('[class*="preparation"]'));
|
||
if (await preparation.count() > 0) {
|
||
await expect(preparation.first()).toBeVisible();
|
||
}
|
||
|
||
await page.screenshot({ path: 'test-results/prepare-objectives.png' });
|
||
});
|
||
|
||
test('测试8: 教学过程展示', 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);
|
||
|
||
await page.click('button:has-text("开始备课")');
|
||
await page.waitForURL('**/prepare', { timeout: 10000 });
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 1. 选择导入课
|
||
const introLesson = page.locator('text=导入课').or(page.locator('[class*="introduction"]'));
|
||
if (await introLesson.count() > 0) {
|
||
await introLesson.first().click();
|
||
await page.waitForTimeout(1000);
|
||
}
|
||
|
||
// 2. 验证教学过程展示
|
||
const process = page.locator('text=教学过程').or(page.locator('[class*="process"], [class*="steps"]'));
|
||
if (await process.count() > 0) {
|
||
await expect(process.first()).toBeVisible();
|
||
}
|
||
|
||
// 3. 验证环节列表
|
||
const steps = page.locator('[class*="step"], [class*="stage"]');
|
||
const stepCount = await steps.count();
|
||
|
||
test.info().annotations.push({
|
||
type: 'info',
|
||
description: `教学环节数量: ${stepCount}`,
|
||
});
|
||
|
||
// 4. 展开第一个环节查看详情
|
||
if (stepCount > 0) {
|
||
await steps.first().click();
|
||
await page.waitForTimeout(500);
|
||
}
|
||
|
||
await page.screenshot({ path: 'test-results/prepare-process.png', fullPage: true });
|
||
});
|
||
|
||
test('测试9: 核心资源展示', 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);
|
||
|
||
await page.click('button:has-text("开始备课")');
|
||
await page.waitForURL('**/prepare', { timeout: 10000 });
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 1. 选择导入课
|
||
const introLesson = page.locator('text=导入课').or(page.locator('[class*="introduction"]'));
|
||
if (await introLesson.count() > 0) {
|
||
await introLesson.first().click();
|
||
await page.waitForTimeout(1000);
|
||
}
|
||
|
||
// 2. 验证核心资源展示
|
||
const resources = page.locator('text=核心资源').or(page.locator('[class*="resource"]'));
|
||
const resourceCount = await resources.count();
|
||
|
||
test.info().annotations.push({
|
||
type: 'info',
|
||
description: resourceCount > 0 ? `找到 ${resourceCount} 个资源区域` : '未找到核心资源区域',
|
||
});
|
||
|
||
// 3. 验证资源类型(视频、PPT、PDF等)
|
||
const resourceTypes = ['视频', 'PPT', 'PDF', '音频', '图片'];
|
||
const foundTypes: string[] = [];
|
||
|
||
for (const type of resourceTypes) {
|
||
const typeLocator = page.locator(`text=${type}`);
|
||
if (await typeLocator.count() > 0) {
|
||
foundTypes.push(type);
|
||
}
|
||
}
|
||
|
||
test.info().annotations.push({
|
||
type: 'info',
|
||
description: `资源类型: ${foundTypes.join(', ') || '无'}`,
|
||
});
|
||
|
||
await page.screenshot({ path: 'test-results/prepare-resources.png' });
|
||
});
|
||
|
||
test('测试10: 教学延伸和反思', 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);
|
||
|
||
await page.click('button:has-text("开始备课")');
|
||
await page.waitForURL('**/prepare', { timeout: 10000 });
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 1. 选择导入课
|
||
const introLesson = page.locator('text=导入课').or(page.locator('[class*="introduction"]'));
|
||
if (await introLesson.count() > 0) {
|
||
await introLesson.first().click();
|
||
await page.waitForTimeout(1000);
|
||
}
|
||
|
||
// 2. 验证教学延伸
|
||
const extension = page.locator('text=教学延伸').or(page.locator('[class*="extension"]'));
|
||
if (await extension.count() > 0) {
|
||
await expect(extension.first()).toBeVisible();
|
||
}
|
||
|
||
// 3. 验证教学反思
|
||
const reflection = page.locator('text=教学反思').or(page.locator('[class*="reflection"]'));
|
||
if (await reflection.count() > 0) {
|
||
await expect(reflection.first()).toBeVisible();
|
||
}
|
||
|
||
await page.screenshot({ path: 'test-results/prepare-extension.png' });
|
||
});
|
||
|
||
test('测试11: 备课笔记功能', 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);
|
||
|
||
await page.click('button:has-text("开始备课")');
|
||
await page.waitForURL('**/prepare', { timeout: 10000 });
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 1. 点击备课笔记Tab
|
||
const notesTab = page.locator('text=备课笔记').or(page.locator('[class*="notes"]'));
|
||
if (await notesTab.count() > 0) {
|
||
await notesTab.first().click();
|
||
await page.waitForTimeout(1000);
|
||
}
|
||
|
||
// 2. 验证笔记编辑器
|
||
const editor = page.locator('[contenteditable="true"], .ant-input, textarea');
|
||
const hasEditor = await editor.count() > 0;
|
||
|
||
if (hasEditor) {
|
||
// 3. 尝试输入笔记内容
|
||
await editor.first().fill('这是测试笔记内容');
|
||
await page.waitForTimeout(1000);
|
||
|
||
test.info().annotations.push({
|
||
type: 'success',
|
||
description: '备课笔记功能测试完成',
|
||
});
|
||
} else {
|
||
test.info().annotations.push({
|
||
type: 'warning',
|
||
description: '未找到笔记编辑器',
|
||
});
|
||
}
|
||
|
||
await page.screenshot({ path: 'test-results/prepare-notes.png' });
|
||
});
|
||
|
||
test('测试12: 从备课模式进入上课', 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);
|
||
|
||
await page.click('button:has-text("开始备课")');
|
||
await page.waitForURL('**/prepare', { timeout: 10000 });
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 1. 查找开始上课按钮
|
||
const startClassButton = page.locator('button:has-text("开始上课")').or(page.locator('button:has-text("进入上课")'));
|
||
const buttonExists = await startClassButton.count() > 0;
|
||
|
||
if (buttonExists) {
|
||
await startClassButton.first().click();
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 2. 验证跳转到上课模式或课程选择弹窗
|
||
const url = page.url();
|
||
test.info().annotations.push({
|
||
type: 'info',
|
||
description: `点击后URL: ${url}`,
|
||
});
|
||
|
||
// 3. 如果显示课程选择弹窗,选择所有课程
|
||
const selectModal = page.locator('[class*="modal"], [class*="select"]');
|
||
if (await selectModal.count() > 0) {
|
||
const confirmButton = page.locator('button:has-text("确定")').or(page.locator('button:has-text("开始上课")'));
|
||
if (await confirmButton.count() > 0) {
|
||
await confirmButton.first().click();
|
||
await page.waitForTimeout(2000);
|
||
}
|
||
}
|
||
} else {
|
||
test.info().annotations.push({
|
||
type: 'warning',
|
||
description: '未找到"开始上课"按钮',
|
||
});
|
||
}
|
||
|
||
await page.screenshot({ path: 'test-results/prepare-to-class.png' });
|
||
});
|
||
});
|