前后端目录重命名: - 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 文档中的路径引用
322 lines
11 KiB
TypeScript
322 lines
11 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
||
|
||
test.describe('Phase 6: 校本课程包功能测试', () => {
|
||
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();
|
||
|
||
// 等待登录成功 - 跳转到 dashboard 或 courses
|
||
await page.waitForURL('**/dashboard', { timeout: 10000 }).catch(() => {
|
||
// 如果没有跳转到 dashboard,可能已经跳转到其他页面,检查是否成功
|
||
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();
|
||
const cardCount = await firstCourseCard.count();
|
||
|
||
if (cardCount > 0) {
|
||
await firstCourseCard.click();
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 3. 检查是否有"创建校本版本"按钮
|
||
const createSchoolVersionButton = page.locator('button:has-text("创建校本版本")');
|
||
const buttonExists = await createSchoolVersionButton.count();
|
||
|
||
if (buttonExists > 0) {
|
||
// 记录当前URL
|
||
const beforeUrl = page.url();
|
||
|
||
// 4. 点击"创建校本版本"按钮
|
||
await createSchoolVersionButton.click();
|
||
|
||
// 5. 等待自动创建并跳转到编辑页面(系统自动创建并跳转)
|
||
await page.waitForURL('**/school-courses/**/edit', { timeout: 15000 });
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 6. 验证页面标题显示正确
|
||
await expect(page.locator('text=创建校本课程包').or(page.locator('text=编辑校本课程包'))).toBeVisible();
|
||
|
||
// 7. 验证URL包含school-courses编辑页路径
|
||
const currentUrl = page.url();
|
||
expect(currentUrl).toMatch(/\/school-courses\/\d+\/edit/);
|
||
|
||
test.info().annotations.push({
|
||
type: 'result',
|
||
description: `创建成功,从 ${beforeUrl} 跳转到 ${currentUrl}`,
|
||
});
|
||
} else {
|
||
test.info().annotations.push({
|
||
type: 'warning',
|
||
description: '未找到"创建校本版本"按钮',
|
||
});
|
||
}
|
||
} else {
|
||
test.info().annotations.push({
|
||
type: 'warning',
|
||
description: '未找到课程卡片',
|
||
});
|
||
}
|
||
});
|
||
|
||
test('测试2: 个人课程中心列表', async ({ page }) => {
|
||
// 1. 进入校本课程包页面
|
||
await page.click('text=校本课程包');
|
||
await page.waitForURL('**/school-courses', { timeout: 10000 });
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 2. 验证页面标题
|
||
await expect(page.locator('text=我的校本课程包').or(page.locator('text=校本课程包')).first()).toBeVisible({ timeout: 5000 });
|
||
|
||
// 3. 检查保存位置筛选器
|
||
const filterTabs = page.locator('text=全部').or(page.locator('text=个人')).or(page.locator('text=校本'));
|
||
const filterCount = await filterTabs.count();
|
||
|
||
test.info().annotations.push({
|
||
type: 'info',
|
||
description: `筛选器数量: ${filterCount}`,
|
||
});
|
||
|
||
// 4. 检查课程列表
|
||
const courseCards = page.locator('.ant-card, [class*="course"], [class*="Course"]');
|
||
const courseCount = await courseCards.count();
|
||
|
||
test.info().annotations.push({
|
||
type: 'info',
|
||
description: `课程数量: ${courseCount}`,
|
||
});
|
||
|
||
// 截图
|
||
await page.screenshot({ path: 'test-results/school-course-list.png' });
|
||
});
|
||
|
||
test('测试3: 编辑校本课程包', async ({ page }) => {
|
||
test.slow();
|
||
|
||
// 1. 进入校本课程包页面
|
||
await page.click('text=校本课程包');
|
||
await page.waitForURL('**/school-courses', { timeout: 10000 });
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 2. 查找编辑按钮
|
||
const editButton = page.locator('button:has-text("编辑")').first();
|
||
|
||
if (await editButton.count() > 0) {
|
||
await editButton.click();
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 3. 验证进入编辑页面
|
||
const url = page.url();
|
||
expect(url).toContain('/edit');
|
||
|
||
// 4. 检查步骤导航
|
||
const steps = page.locator('.ant-steps-item, [class*="step"]');
|
||
const stepCount = await steps.count();
|
||
|
||
test.info().annotations.push({
|
||
type: 'info',
|
||
description: `编辑步骤数量: ${stepCount}`,
|
||
});
|
||
|
||
// 5. 尝试切换步骤
|
||
const step2 = page.locator('text=课程介绍').or(page.locator('text=步骤2'));
|
||
if (await step2.count() > 0) {
|
||
await step2.first().click();
|
||
await page.waitForTimeout(1000);
|
||
}
|
||
|
||
// 6. 保存修改
|
||
const saveButton = page.locator('button:has-text("保存")').or(page.locator('button:has-text("保存到个人")'));
|
||
if (await saveButton.count() > 0) {
|
||
await saveButton.first().click();
|
||
await page.waitForTimeout(2000);
|
||
}
|
||
|
||
// 截图
|
||
await page.screenshot({ path: 'test-results/school-course-edit.png' });
|
||
} else {
|
||
test.info().annotations.push({
|
||
type: 'warning',
|
||
description: '未找到可编辑的课程',
|
||
});
|
||
}
|
||
});
|
||
|
||
test('测试4: 查看校本课程详情', async ({ page }) => {
|
||
// 1. 进入校本课程包页面
|
||
await page.click('text=校本课程包');
|
||
await page.waitForURL('**/school-courses', { timeout: 10000 });
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 2. 查找查看按钮
|
||
const viewButton = page.locator('button:has-text("查看")').first();
|
||
|
||
if (await viewButton.count() > 0) {
|
||
await viewButton.click();
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 3. 验证详情页面
|
||
const url = page.url();
|
||
expect(url).toContain('/school-courses/');
|
||
|
||
// 4. 检查详情页内容
|
||
const detailElements = page.locator('text=开始备课').or(page.locator('text=课程介绍'));
|
||
await expect(detailElements.first()).toBeVisible({ timeout: 5000 });
|
||
|
||
// 截图
|
||
await page.screenshot({ path: 'test-results/school-course-detail.png' });
|
||
} else {
|
||
test.info().annotations.push({
|
||
type: 'warning',
|
||
description: '未找到可查看的课程',
|
||
});
|
||
}
|
||
});
|
||
|
||
test('测试5: 备课模式', async ({ page }) => {
|
||
test.slow();
|
||
|
||
// 1. 进入校本课程包页面
|
||
await page.click('text=校本课程包');
|
||
await page.waitForURL('**/school-courses', { timeout: 10000 });
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 2. 查找"开始备课"按钮
|
||
const prepareButton = page.locator('button:has-text("开始备课")').first();
|
||
|
||
if (await prepareButton.count() > 0) {
|
||
await prepareButton.click();
|
||
await page.waitForTimeout(3000);
|
||
|
||
// 3. 验证进入备课模式
|
||
const url = page.url();
|
||
expect(url).toContain('/prepare');
|
||
|
||
// 4. 检查备课模式布局
|
||
const navigation = page.locator('aside, [class*="navigation"], [class*="sidebar"]');
|
||
await expect(navigation.first()).toBeVisible({ timeout: 5000 });
|
||
|
||
// 5. 检查课程列表
|
||
const lessonItems = page.locator('[class*="lesson"], [class*="course"]');
|
||
const lessonCount = await lessonItems.count();
|
||
|
||
test.info().annotations.push({
|
||
type: 'info',
|
||
description: `备课模式课程数量: ${lessonCount}`,
|
||
});
|
||
|
||
// 截图
|
||
await page.screenshot({ path: 'test-results/prepare-mode.png' });
|
||
} else {
|
||
test.info().annotations.push({
|
||
type: 'warning',
|
||
description: '未找到"开始备课"按钮',
|
||
});
|
||
}
|
||
});
|
||
|
||
test('测试6: 删除校本课程包', async ({ page }) => {
|
||
// 1. 进入校本课程包页面
|
||
await page.click('text=校本课程包');
|
||
await page.waitForURL('**/school-courses', { timeout: 10000 });
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 2. 记录删除前的课程数量
|
||
const courseCards = page.locator('.ant-card, [class*="course"]');
|
||
const beforeCount = await courseCards.count();
|
||
|
||
// 3. 查找删除按钮
|
||
const deleteButton = page.locator('button:has-text("删除")').first();
|
||
|
||
if (await deleteButton.count() > 0) {
|
||
// 4. 点击删除
|
||
await deleteButton.click();
|
||
await page.waitForTimeout(1000);
|
||
|
||
// 5. 确认删除
|
||
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);
|
||
}
|
||
|
||
// 6. 验证删除成功
|
||
const afterCount = await courseCards.count();
|
||
test.info().annotations.push({
|
||
type: 'info',
|
||
description: `删除前: ${beforeCount}, 删除后: ${afterCount}`,
|
||
});
|
||
} else {
|
||
test.info().annotations.push({
|
||
type: 'warning',
|
||
description: '未找到删除按钮',
|
||
});
|
||
}
|
||
});
|
||
|
||
test('测试7: 筛选功能', async ({ page }) => {
|
||
// 1. 进入校本课程包页面
|
||
await page.click('text=校本课程包');
|
||
await page.waitForURL('**/school-courses', { timeout: 10000 });
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 2. 查找筛选器
|
||
const personalFilter = page.locator('text=个人').or(page.locator('[role="tab"]:has-text("个人")'));
|
||
const schoolFilter = page.locator('text=校本').or(page.locator('[role="tab"]:has-text("校本")'));
|
||
|
||
if (await personalFilter.count() > 0) {
|
||
// 3. 点击"个人"筛选
|
||
await personalFilter.first().click();
|
||
await page.waitForTimeout(1000);
|
||
|
||
// 4. 点击"校本"筛选
|
||
if (await schoolFilter.count() > 0) {
|
||
await schoolFilter.first().click();
|
||
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/school-course-filter.png' });
|
||
});
|
||
});
|