kindergarten_java/reading-platform-frontend/tests/e2e/admin/course-package-comprehensive.spec.ts

527 lines
21 KiB
TypeScript
Raw Normal View History

/**
* E2E
*
*
* 1. - 7
* 2. - 1+ 5
* 3. - //
* 4. -
*/
import { test, expect } from '@playwright/test';
import { loginAsAdmin, waitForTable, waitForSuccess } from './helpers';
// 测试数据
const TEST_COURSE_COMPLETE = {
name: '完整测试课程包-' + Date.now(),
theme: '社会认知',
grades: ['小班'],
bookName: '折耳兔奇奇',
coreContent: '通过折耳兔奇奇的故事,帮助孩子理解友谊和分享的重要性,培养社交能力。',
domainTags: ['倾听与表达'],
// 课程介绍
introSummary: '这是一门专为小班幼儿设计的课程,通过有趣的故事情节,培养孩子的语言表达能力和社交技能。',
introHighlights: '1. 故事情节生动有趣\n2. 互动性强\n3. 培养分享意识',
introGoals: '1. 理解故事内容\n2. 学会分享\n3. 培养语言表达能力',
// 排课参考
totalHours: '8',
hourDuration: '30',
scheduleAdvice: '建议每周 1-2 课时,可根据实际情况调整',
// 集体课
collectiveName: '集体课——折耳兔奇奇绘本阅读',
collectiveDuration: 25,
collectiveObjectives: '1. 认知:完整观看绘本动画,理解故事主要情节\n2. 情感:感受故事的趣味性,产生阅读兴趣',
collectivePreparation: '1. 绘本 PPT\n2. 相关图片\n3. 教学道具',
// 环创建设
environmentConstruction: '1. 夸夸卡展示区\n2. "我的特别之处"展示墙\n3. 主题墙布置',
};
const TEST_COURSE_MINIMAL = {
name: '最小化测试课程包-' + Date.now(),
theme: '社会认知',
grades: ['中班'],
coreContent: '最小化测试课程,仅包含必填项。',
domainTags: ['人际交往'],
};
test.describe('课程包创建 - 完整流程测试', () => {
test('完整流程:填写所有 7 个步骤创建课程包', async ({ page }) => {
test.setTimeout(180000);
const courseName = TEST_COURSE_COMPLETE.name;
// 步骤 1: 登录
await loginAsAdmin(page);
await page.waitForTimeout(1000);
// 步骤 2: 进入课程管理页面
await page.goto('/admin/courses', { timeout: 30000, waitUntil: 'commit' });
// 等待页面加载完成
await page.waitForLoadState('networkidle', { timeout: 30000 }).catch(() => {});
await page.waitForTimeout(3000);
// 步骤 3: 点击新建课程包按钮
const createButton = page.getByRole('button', { name: '新建课程包' });
await createButton.click();
await page.waitForTimeout(1000);
// 验证进入创建页面
await expect(page.getByText('创建课程包')).toBeVisible({ timeout: 5000 });
// ==================== 步骤 1: 基本信息 ====================
await page.getByPlaceholder('请输入课程包名称').fill(courseName);
// 选择关联主题
await page.locator('.ant-select-selector').first().click();
await page.waitForTimeout(500);
await page.getByText(TEST_COURSE_COMPLETE.theme, { exact: true }).first().click();
await page.waitForTimeout(300);
// 选择适用年级
await page.getByRole('checkbox', { name: TEST_COURSE_COMPLETE.grades[0] }).click();
await page.waitForTimeout(300);
// 填写核心内容
await page.getByPlaceholder('请输入课程包核心内容').fill(TEST_COURSE_COMPLETE.coreContent);
// 填写绘本名称
await page.getByPlaceholder('请输入关联绘本名称(可选)').fill(TEST_COURSE_COMPLETE.bookName);
// 选择领域标签
const domainSelector = page.locator('.ant-select-selector').last();
await domainSelector.click();
await page.waitForTimeout(800);
for (const tag of TEST_COURSE_COMPLETE.domainTags) {
await page.getByText(tag, { exact: true }).first().click({ force: true });
await page.waitForTimeout(300);
}
await page.keyboard.press('Escape');
await page.waitForTimeout(300);
// 点击下一步
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(1000);
// ==================== 步骤 2: 课程介绍 ====================
// 课程简介 - 点击标签页并填写
await page.getByText('课程简介').click();
await page.waitForTimeout(500);
await page.locator('textarea[placeholder*="课程整体简介"]').first().fill(TEST_COURSE_COMPLETE.introSummary);
await page.waitForTimeout(300);
// 课程亮点
await page.getByText('课程亮点').click();
await page.waitForTimeout(500);
await page.locator('textarea[placeholder*="课程特色亮点"]').first().fill(TEST_COURSE_COMPLETE.introHighlights);
await page.waitForTimeout(300);
// 课程总目标
await page.getByText('课程总目标').click();
await page.waitForTimeout(500);
await page.locator('textarea[placeholder*="课程总体目标"]').first().fill(TEST_COURSE_COMPLETE.introGoals);
await page.waitForTimeout(300);
// 点击下一步
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(1000);
// ==================== 步骤 3: 排课参考 ====================
// 步骤 3 使用表格形式,直接跳过(可选步骤)
console.log(' - 跳过排课参考(可选)');
// 点击下一步
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(1000);
// ==================== 步骤 4: 导入课 ====================
// 跳过导入课配置(可选步骤)
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(1000);
// ==================== 步骤 5: 集体课 ====================
// 创建集体课
const createCollectiveBtn = page.getByRole('button', { name: '创建集体课' });
await createCollectiveBtn.click();
// 等待 Vue 组件渲染完成(响应式更新需要时间)
await page.waitForTimeout(2000);
// 等待课程名称输入框可见并填写
const nameInput = page.locator('input[placeholder="请输入课程名称"]').first();
await nameInput.waitFor({ state: 'visible', timeout: 10000 });
await nameInput.scrollIntoViewIfNeeded();
await nameInput.fill(TEST_COURSE_COMPLETE.collectiveName);
await page.waitForTimeout(300);
// 填写课程时长 - 使用 a-input-number 的选择器(等待组件完全渲染)
await page.waitForTimeout(1000);
// 使用键盘导航来聚焦输入框
await page.keyboard.press('Tab');
await page.waitForTimeout(300);
// 直接填写数值
await page.keyboard.type(TEST_COURSE_COMPLETE.collectiveDuration.toString());
await page.waitForTimeout(300);
// 填写教学目标
const objectivesTextarea = page.locator('textarea[placeholder*="教学目标"]').first();
await objectivesTextarea.scrollIntoViewIfNeeded();
await objectivesTextarea.waitFor({ state: 'visible', timeout: 5000 });
await objectivesTextarea.fill(TEST_COURSE_COMPLETE.collectiveObjectives);
await page.waitForTimeout(300);
// 填写教学准备
const preparationTextarea = page.locator('textarea[placeholder*="教学准备"]').first();
await preparationTextarea.scrollIntoViewIfNeeded();
await preparationTextarea.waitFor({ state: 'visible', timeout: 5000 });
await preparationTextarea.fill(TEST_COURSE_COMPLETE.collectivePreparation);
await page.waitForTimeout(300);
// 点击下一步
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(1000);
// ==================== 步骤 6: 领域课 ====================
// 跳过领域课配置(可选步骤)
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(1000);
// ==================== 步骤 7: 环创建设 ====================
// 等待元素可见并滚动到视图中
await page.waitForTimeout(500);
const envTextarea = page.locator('textarea[placeholder*="环创"]').first();
await envTextarea.scrollIntoViewIfNeeded();
await envTextarea.waitFor({ state: 'visible', timeout: 10000 }).catch(async () => {
// 如果找不到,尝试其他选择器
await page.locator('textarea[placeholder*="主题墙"]').first().scrollIntoViewIfNeeded();
});
await envTextarea.fill(TEST_COURSE_COMPLETE.environmentConstruction);
await page.waitForTimeout(300);
// 点击创建按钮
await page.getByRole('button', { name: '创建' }).click();
// ==================== 验证创建成功 ====================
// 等待成功提示或页面跳转
try {
await page.waitForSelector('.ant-message-success', { timeout: 5000 });
} catch {
// 如果没有成功提示,可能是页面直接跳转了
console.log(' - 未检测到成功提示,检查页面跳转...');
}
// 等待跳转到列表页
await page.waitForURL('**/admin/courses**', { timeout: 15000 });
await page.waitForTimeout(1000);
// 验证新课程包在列表中显示
await waitForTable(page, 15000);
const table = page.locator('.ant-table').first();
const courseRow = table.getByText(courseName);
// 如果找不到,刷新页面后再试一次
try {
await expect(courseRow).toBeVisible({ timeout: 10000 });
} catch {
console.log(' - 第一次尝试未找到课程包,刷新页面...');
await page.goto('/admin/courses', { waitUntil: 'networkidle' });
await page.waitForTimeout(5000);
await waitForTable(page, 15000);
await expect(table.getByText(courseName)).toBeVisible({ timeout: 10000 });
}
console.log(`✅ 完整流程测试通过 - 课程包 "${courseName}" 创建成功`);
});
});
test.describe('课程包创建 - 最小化测试', () => {
test('最小化流程:只填写必填项创建课程包', async ({ page }) => {
test.setTimeout(120000);
const courseName = TEST_COURSE_MINIMAL.name;
// 步骤 1: 登录
await loginAsAdmin(page);
await page.waitForTimeout(1000);
// 步骤 2: 进入课程管理页面
await page.goto('/admin/courses', { timeout: 30000, waitUntil: 'commit' });
// 等待页面加载完成
await page.waitForLoadState('networkidle', { timeout: 30000 }).catch(() => {});
await page.waitForTimeout(3000);
// 步骤 3: 点击新建课程包按钮
const createButton = page.getByRole('button', { name: '新建课程包' });
await createButton.click();
await page.waitForTimeout(1000);
// 验证进入创建页面
await expect(page.getByText('创建课程包')).toBeVisible({ timeout: 5000 });
// ==================== 步骤 1: 基本信息(只填必填项)====================
await page.getByPlaceholder('请输入课程包名称').fill(courseName);
// 选择关联主题
await page.locator('.ant-select-selector').first().click();
await page.waitForTimeout(500);
await page.getByText(TEST_COURSE_MINIMAL.theme, { exact: true }).first().click();
await page.waitForTimeout(300);
// 选择适用年级
await page.getByRole('checkbox', { name: TEST_COURSE_MINIMAL.grades[0] }).click();
await page.waitForTimeout(300);
// 填写核心内容
await page.getByPlaceholder('请输入课程包核心内容').fill(TEST_COURSE_MINIMAL.coreContent);
// 点击下一步
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(1000);
// ==================== 步骤 2-6: 全部跳过 ====================
// 步骤 2: 课程介绍(跳过)
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(500);
// 步骤 3: 排课参考(跳过)
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(500);
// 步骤 4: 导入课(跳过)
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(500);
// 步骤 5: 集体课(跳过)
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(500);
// 步骤 6: 领域课(跳过)
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(500);
// ==================== 步骤 7: 环创建设(跳过)====================
// 直接点击创建按钮
await page.getByRole('button', { name: '创建' }).click();
// ==================== 验证创建成功 ====================
// 等待成功提示或页面跳转
try {
await page.waitForSelector('.ant-message-success', { timeout: 5000 });
} catch {
// 如果没有成功提示,可能是页面直接跳转了
console.log(' - 未检测到成功提示,检查页面跳转...');
}
// 等待跳转到列表页
await page.waitForURL('**/admin/courses**', { timeout: 15000 });
// 等待网络请求完成
await page.waitForLoadState('networkidle', { timeout: 30000 }).catch(() => {});
// 额外等待确保数据加载完成
await page.waitForTimeout(5000);
// 验证新课程包在列表中显示
// 使用 page.getByText 而不是 table.getByText 来增加查找范围
const courseRow = page.getByText(courseName).first();
// 增加等待时间,允许数据加载
try {
await expect(courseRow).toBeVisible({ timeout: 20000 });
} catch {
// 如果找不到,尝试刷新页面
console.log('未找到课程包,尝试刷新页面...');
await page.goto('/admin/courses', { waitUntil: 'networkidle' });
await page.waitForTimeout(5000);
await expect(courseRow).toBeVisible({ timeout: 10000 });
}
console.log(`✅ 最小化测试通过 - 课程包 "${courseName}" 创建成功`);
});
});
test.describe('课程包创建 - 验证逻辑测试', () => {
test('必填项验证:未填必填项时无法进入下一步', async ({ page }) => {
test.setTimeout(60000);
// 步骤 1: 登录
await loginAsAdmin(page);
await page.waitForTimeout(1000);
// 步骤 2: 进入课程管理页面
await page.goto('/admin/courses', { timeout: 30000, waitUntil: 'commit' });
// 等待页面加载完成
await page.waitForLoadState('networkidle', { timeout: 30000 }).catch(() => {});
await page.waitForTimeout(3000);
// 步骤 3: 点击新建课程包按钮
const createButton = page.getByRole('button', { name: '新建课程包' });
await createButton.click();
await page.waitForTimeout(1000);
// 验证进入创建页面
await expect(page.getByText('创建课程包')).toBeVisible({ timeout: 5000 });
// ==================== 测试必填项验证 ====================
// 不填写任何内容,直接点击下一步
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(1000);
// 验证应该显示错误提示(通过表单验证样式判断)
// 课程包名称必填项应该显示红色边框
const nameInput = page.getByPlaceholder('请输入课程包名称');
await expect(nameInput).toBeVisible();
// 验证仍停留在步骤 1
await expect(page.getByText('基本信息')).toBeVisible();
await expect(page.getByRole('button', { name: '下一步' })).toBeVisible();
console.log('✅ 必填项验证测试通过 - 未填必填项时无法进入下一步');
});
test('部分填写验证:只填部分必填项时无法进入下一步', async ({ page }) => {
test.setTimeout(60000);
const partialCourseName = '部分填写测试-' + Date.now();
// 步骤 1: 登录
await loginAsAdmin(page);
await page.waitForTimeout(1000);
// 步骤 2: 进入课程管理页面
await page.goto('/admin/courses', { timeout: 30000, waitUntil: 'commit' });
// 等待页面加载完成
await page.waitForLoadState('networkidle', { timeout: 30000 }).catch(() => {});
await page.waitForTimeout(3000);
// 步骤 3: 点击新建课程包按钮
const createButton = page.getByRole('button', { name: '新建课程包' });
await createButton.click();
await page.waitForTimeout(1000);
// 只填写课程名称,其他必填项不填
await page.getByPlaceholder('请输入课程包名称').fill(partialCourseName);
// 直接点击下一步
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(1000);
// 验证应该仍停留在步骤 1因为主题和年级未选
await expect(page.getByText('基本信息')).toBeVisible();
console.log('✅ 部分填写验证测试通过 - 只填部分必填项时无法进入下一步');
});
});
test.describe('课程包创建 - 数据持久化测试', () => {
test('数据验证:创建后查看详情数据完整', async ({ page }) => {
test.setTimeout(180000);
const courseName = '数据验证测试-' + Date.now();
// 步骤 1: 登录
await loginAsAdmin(page);
await page.waitForTimeout(1000);
// 步骤 2: 进入课程管理页面
await page.goto('/admin/courses', { timeout: 30000, waitUntil: 'commit' });
// 等待页面加载完成
await page.waitForLoadState('networkidle', { timeout: 30000 }).catch(() => {});
await page.waitForTimeout(3000);
// 步骤 3: 点击新建课程包按钮
const createButton = page.getByRole('button', { name: '新建课程包' });
await createButton.click();
await page.waitForTimeout(1000);
// ==================== 步骤 1: 基本信息 ====================
await page.getByPlaceholder('请输入课程包名称').fill(courseName);
await page.locator('.ant-select-selector').first().click();
await page.waitForTimeout(500);
await page.getByText('社会认知', { exact: true }).first().click();
await page.waitForTimeout(300);
await page.getByRole('checkbox', { name: '大班' }).click();
await page.waitForTimeout(300);
await page.getByPlaceholder('请输入课程包核心内容').fill('数据持久化测试内容');
await page.getByPlaceholder('请输入关联绘本名称(可选)').fill('测试绘本');
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(1000);
// ==================== 步骤 2: 课程介绍 ====================
await page.getByText('课程简介').click();
await page.waitForTimeout(500);
await page.locator('textarea[placeholder*="课程整体简介"]').first().fill('测试课程简介');
await page.waitForTimeout(300);
// 步骤 3 排课参考使用表格,直接跳过
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(1000);
// ==================== 步骤 4: 导入课 ====================
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(1000);
// ==================== 步骤 5: 集体课 ====================
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(1000);
// ==================== 步骤 6: 领域课 ====================
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(1000);
// ==================== 步骤 7: 环创建设 ====================
// 等待步骤面板稳定
await page.waitForTimeout(2000);
// 使用 force 选项直接填写,忽略 visible 检查
const envTextarea = page.locator('textarea[placeholder*="环创建设内容"]').first();
await envTextarea.fill('测试环创内容', { force: true });
await page.waitForTimeout(300);
// 点击创建按钮
await page.getByRole('button', { name: '创建' }).click();
// ==================== 验证创建成功 ====================
// 等待成功提示或页面跳转
try {
await page.waitForSelector('.ant-message-success', { timeout: 5000 });
} catch {
console.log(' - 未检测到成功提示,检查页面跳转...');
}
// 等待跳转到列表页
await page.waitForURL('**/admin/courses**', { timeout: 15000 });
await page.waitForLoadState('networkidle', { timeout: 30000 }).catch(() => {});
await page.waitForTimeout(2000);
// ==================== 查看课程包详情 ====================
// 找到新课程包所在行 - 使用更宽松的等待
const table = page.locator('.ant-table').first();
await table.waitFor({ state: 'visible', timeout: 30000 });
// 等待表格内容加载
await page.waitForFunction(async () => {
const tableElement = document.querySelector('.ant-table');
if (!tableElement) return false;
const rows = tableElement.querySelectorAll('tbody tr[data-row-key]');
return rows.length > 0;
}, { timeout: 15000 }).catch(() => {});
const courseRow = table.locator('tr').filter({ hasText: courseName });
// 点击"查看"或"详情"按钮
await courseRow.getByRole('button', { name: /查看 | 详情/ }).click();
await page.waitForTimeout(2000);
// 验证详情页数据
await expect(page.getByText(courseName)).toBeVisible({ timeout: 5000 });
// 验证基本信息
await expect(page.getByText('社会认知')).toBeVisible();
await expect(page.getByText('大班')).toBeVisible();
await expect(page.getByText('测试绘本')).toBeVisible();
console.log(`✅ 数据持久化测试通过 - 课程包 "${courseName}" 数据完整`);
});
});