kindergarten_java/reading-platform-frontend/tests/e2e/admin/package-create-from-template.spec.ts

553 lines
26 KiB
TypeScript
Raw Normal View History

/**
* E2E - "折耳兔奇奇测试课程 01"
*
*
* 1.
* 2.
* 3.
* 4. 7
* 5.
*
* JSON
*/
import { test, expect } from '@playwright/test';
import { loginAsAdmin, waitForTable, waitForSuccess } from './helpers';
// 测试数据 - 基于 JSON 模板
const TEST_COURSE = {
// 基本信息
name: '折耳兔奇奇测试课程 01',
theme: '社会认知', // 使用实际存在的主题
grades: ['小班'] as string[],
domainTags: ['社会适应'] as string[],
pictureBookName: '折耳兔奇奇',
coreContent: '通过折耳兔奇奇的故事,帮助孩子理解友谊和分享的重要性',
duration: 25,
// 课程介绍
introSummary: '本课程以绘本《折耳兔奇奇》为核心载体,面向幼儿园小班幼儿,紧扣"接受认同自己"的核心主题,帮助幼儿建立自信、乐观的心态。',
introHighlights: '1. 主题鲜明,情感真挚\n2. 领域融合,全面发展\n3. 形式多样,趣味十足\n4. 家园协同,全程联动\n5. 环创赋能,延伸教育',
introGoals: '1. 认知目标:理解《折耳兔奇奇》绘本核心情节\n2. 能力目标:提升幼儿语言表达、社会交往能力\n3. 情感目标:感受奇奇从自卑到自信的情绪变化',
introSchedule: '周一:导入课认识奇奇;周二:语言领域绘本解读;周三:社会领域夸夸我和你;周四:健康领域保护耳朵;周五:科学领域兔子的小秘密',
introKeyPoints: '1. 掌握绘本核心内容\n2. 落实五大领域目标\n3. 推动家校协同',
introMethods: '1. 绘本阅读法\n2. 课件演示法\n3. 互动游戏法\n4. 情境教学法',
introEvaluation: '1. 过程性评价\n2. 作品评价\n3. 家园评价\n4. 总结性评价',
introNotes: '1. 尊重幼儿个体差异\n2. 严格控制各环节课时时长\n3. 注重安全保障',
// 排课参考
totalLessons: 5,
lessonDuration: 25,
scheduleAdvice: '建议每周 1-2 课时,按导入课→集体课→五大领域主题课顺序推进',
// 导入课
introLessonName: '导入课——认识奇奇,发现我的特别之处',
introLessonDuration: 10,
introLessonObjectives: '1. 认知目标:认识绘本主角折耳兔奇奇,能准确说出奇奇最明显的外形特点(折耳朵)\n2. 能力目标:能大胆、主动地说出自己的一个特别之处\n3. 情感目标:乐于参与集体互动,愿意分享自己的特点',
introLessonPreparation: '1. 物质准备:折耳兔奇奇毛绒玩具 1 个、绘本封面放大图 1 张、轻音乐\n2. 经验准备:幼儿有观察自己和同伴外貌、爱好的经验',
// 集体课
collectiveLessonName: '集体课——折耳兔奇奇绘本阅读',
collectiveLessonDuration: 25,
collectiveLessonObjectives: '1. 认知:完整观看绘本动画,熟悉绘本主要情节和角色\n2. 能力:能跟随教师朗读简单的绘本对话\n3. 情感:感受绘本的趣味性,初步体会奇奇的烦恼',
// 领域课 - 健康领域
healthLessonName: '健康领域——保护我的"小耳朵"',
healthLessonDuration: 25,
healthLessonObjectives: '1. 认知:知道耳朵是我们的重要器官,了解耳朵的作用\n2. 能力:学习保护耳朵的正确方法\n3. 情感:愿意主动保护自己的身体器官',
// 领域课 - 科学领域
scienceLessonName: '科学领域——兔子的小秘密',
scienceLessonDuration: 25,
scienceLessonObjectives: '1. 认知:了解兔子的基本特征和生活习性\n2. 能力:能通过观察、对比发现不同兔子的外形区别\n3. 情感:对兔子产生探究兴趣,爱护小动物',
// 环创建设
environmentConstruction: '1. 夸夸卡展示区:展示幼儿制作的夸夸卡\n2. "我的特别之处"展示墙:张贴幼儿分享的特别之处作品\n3. 兔子探秘墙:张贴兔子图片和观察记录\n4. 音乐角环创:张贴儿歌歌词图谱、兔子头饰\n5. 健康小卫士展示区:张贴保护耳朵方法海报',
};
test.describe('课程包创建 - 折耳兔奇奇测试课程 01', () => {
test('完整流程:创建课程包并验证', async ({ page }) => {
test.setTimeout(300000); // 5 分钟超时
// 监听浏览器控制台日志
page.on('console', msg => {
console.log(`[浏览器日志] ${msg.type()}: ${msg.text()}`);
});
// 监听页面错误
page.on('pageerror', error => {
console.error(`[页面错误] ${error.message}`);
});
// ==================== 步骤 1: 登录 ====================
console.log('\n========== 开始登录 ==========');
await loginAsAdmin(page);
await page.waitForURL('**/admin/**', { timeout: 15000 }).catch(() => {
console.log('警告URL 等待超时,但继续执行');
});
await page.waitForTimeout(2000); // 增加等待时间确保页面完全加载
console.log('✅ 登录成功');
// ==================== 步骤 2: 进入课程管理页面 ====================
console.log('\n========== 进入课程管理页面 ==========');
// 直接导航到课程列表页
await page.goto('/admin/courses', { timeout: 30000, waitUntil: 'networkidle' });
await page.waitForTimeout(3000); // 等待数据加载
// 验证页面元素(不强制要求表格存在)
const pageContainer = page.locator('.package-list-page, .course-list-page, [class*="course"], [class*="package"]').first();
if (await pageContainer.isVisible({ timeout: 5000 })) {
console.log('✅ 课程管理页面加载完成');
} else {
console.log('⚠️ 未找到预期的页面容器,但继续执行');
}
// ==================== 步骤 3: 点击新建课程包按钮 ====================
console.log('\n========== 点击新建课程包 ==========');
// 使用更精确的选择器 - 按钮可能是一个 a 标签或带有 click 事件的元素
const createButton = page.locator('button:has-text("新建课程包"), .ant-btn:has-text("新建课程包"), a:has-text("新建课程包")').first();
await createButton.click();
await page.waitForTimeout(1000);
console.log('✅ 点击新建课程包');
// 验证进入创建页面
await expect(page.getByText('创建课程包')).toBeVisible({ timeout: 5000 });
await expect(page.getByText('基本信息')).toBeVisible();
console.log('✅ 进入课程创建页面');
// ==================== 步骤 1: 基本信息 ====================
console.log('\n========== 步骤 1: 基本信息 ==========');
// 填写课程包名称
const nameInput = page.getByPlaceholder('请输入课程包名称');
await expect(nameInput).toBeVisible({ timeout: 3000 });
await nameInput.fill(TEST_COURSE.name);
console.log(` - 填写课程名称:${TEST_COURSE.name}`);
// 选择关联主题
await page.locator('.ant-select-selector').first().click();
await page.waitForTimeout(500);
await page.getByText(TEST_COURSE.theme, { exact: true }).first().click();
await page.waitForTimeout(300);
console.log(` - 选择主题:${TEST_COURSE.theme}`);
// 选择适用年级
await page.getByRole('checkbox', { name: TEST_COURSE.grades[0] }).click();
await page.waitForTimeout(300);
console.log(` - 选择年级:${TEST_COURSE.grades[0]}`);
// 填写核心内容
const coreContentInput = page.getByPlaceholder('请输入课程包核心内容');
await expect(coreContentInput).toBeVisible({ timeout: 3000 });
await coreContentInput.fill(TEST_COURSE.coreContent);
console.log(` - 填写核心内容:${TEST_COURSE.coreContent}`);
// 填写绘本名称
const bookNameInput = page.getByPlaceholder('请输入关联绘本名称(可选)');
await expect(bookNameInput).toBeVisible({ timeout: 3000 });
await bookNameInput.fill(TEST_COURSE.pictureBookName);
console.log(` - 填写绘本名称:${TEST_COURSE.pictureBookName}`);
// 选择领域标签
const domainSelector = page.locator('.ant-select-selector').last();
await domainSelector.click();
await page.waitForTimeout(800);
await page.getByText(TEST_COURSE.domainTags[0], { exact: true }).first().click({ force: true });
await page.waitForTimeout(300);
await page.keyboard.press('Escape');
console.log(` - 选择领域标签:${TEST_COURSE.domainTags[0]}`);
// 点击下一步
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(1000);
console.log('✅ 步骤 1 完成 - 基本信息');
// ==================== 步骤 2: 课程介绍 ====================
console.log('\n========== 步骤 2: 课程介绍 ==========');
await expect(page.locator('.intro-header .title:has-text("课程介绍")')).toBeVisible({ timeout: 3000 });
// 填写课程简介 - 使用更精确的定位
await page.getByRole('tab', { name: '课程简介' }).click();
await page.waitForTimeout(1000);
// 使用 visible 选择器找到可见的 textarea
const summaryTextarea = page.locator('.tab-content textarea:visible').first();
await summaryTextarea.waitFor({ state: 'visible', timeout: 10000 });
await summaryTextarea.fill(TEST_COURSE.introSummary);
console.log(' - 填写课程简介');
// 填写课程亮点
await page.getByRole('tab', { name: '课程亮点' }).click();
await page.waitForTimeout(500);
await page.locator('.tab-content textarea:visible').first().fill(TEST_COURSE.introHighlights);
console.log(' - 填写课程亮点');
// 填写课程总目标
await page.getByRole('tab', { name: '课程总目标' }).click();
await page.waitForTimeout(500);
await page.locator('.tab-content textarea:visible').first().fill(TEST_COURSE.introGoals);
console.log(' - 填写课程目标');
// 填写内容安排
await page.getByRole('tab', { name: '内容安排' }).click();
await page.waitForTimeout(500);
await page.locator('.tab-content textarea:visible').first().fill(TEST_COURSE.introSchedule);
console.log(' - 填写内容安排');
// 填写重难点
await page.getByRole('tab', { name: '重难点' }).click();
await page.waitForTimeout(500);
await page.locator('.tab-content textarea:visible').first().fill(TEST_COURSE.introKeyPoints);
console.log(' - 填写重难点');
// 填写教学方法
await page.getByRole('tab', { name: '教学方法' }).click();
await page.waitForTimeout(500);
await page.locator('.tab-content textarea:visible').first().fill(TEST_COURSE.introMethods);
console.log(' - 填写教学方法');
// 填写评价方式
await page.getByRole('tab', { name: '评价方式' }).click();
await page.waitForTimeout(500);
await page.locator('.tab-content textarea:visible').first().fill(TEST_COURSE.introEvaluation);
console.log(' - 填写评价方式');
// 填写注意事项
await page.getByRole('tab', { name: '注意事项' }).click();
await page.waitForTimeout(500);
await page.locator('.tab-content textarea:visible').first().fill(TEST_COURSE.introNotes);
console.log(' - 填写注意事项');
// 点击下一步
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(1000);
console.log('✅ 步骤 2 完成 - 课程介绍');
// ==================== 步骤 3: 排课参考 ====================
console.log('\n========== 步骤 3: 排课参考 ==========');
await expect(page.locator('.step3-schedule-ref .title:has-text("排课计划参考")')).toBeVisible({ timeout: 3000 });
// 使用快速填充模板(可选步骤,跳过详细填写)
const fillWeekButton = page.getByText('填充一周模板');
if (await fillWeekButton.isVisible({ timeout: 5000 })) {
await fillWeekButton.click();
await page.waitForTimeout(500);
console.log(' - 使用快速填充模板');
}
// 点击下一步
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(1000);
console.log('✅ 步骤 3 完成 - 排课参考');
// ==================== 步骤 4: 导入课 ====================
console.log('\n========== 步骤 4: 导入课 ==========');
await expect(page.locator('.step4-intro-lesson .title:has-text("导入课")')).toBeVisible({ timeout: 3000 });
// 点击创建导入课按钮(如果需要)
const createIntroButton = page.getByText('创建导入课');
if (await createIntroButton.isVisible({ timeout: 3000 })) {
await createIntroButton.click();
await page.waitForTimeout(1500); // 增加等待时间让组件渲染
console.log(' - 点击创建导入课');
}
// 等待输入框加载完成
await page.waitForTimeout(500);
// 填写导入课名称 - 使用更精确的选择器
const introLessonNameInput = page.locator('input[placeholder="请输入课程名称"]').first();
if (await introLessonNameInput.isVisible({ timeout: 5000 })) {
await introLessonNameInput.fill(TEST_COURSE.introLessonName);
console.log(` - 填写导入课名称:${TEST_COURSE.introLessonName}`);
} else {
console.log(' ⚠️ 未找到导入课名称输入框');
}
// 填写课时时长 - 使用 aria-label 或 class 选择器
const introLessonDurationInput = page.locator('.ant-input-number-input').first();
if (await introLessonDurationInput.isVisible({ timeout: 5000 })) {
await introLessonDurationInput.fill(String(TEST_COURSE.introLessonDuration));
console.log(` - 填写导入课时长:${TEST_COURSE.introLessonDuration}分钟`);
} else {
console.log(' ⚠️ 未找到导入课时长输入框');
}
// 填写教学目标
const introLessonObjectivesTextarea = page.locator('textarea[placeholder*="教学目标"]').first();
if (await introLessonObjectivesTextarea.isVisible({ timeout: 3000 })) {
await introLessonObjectivesTextarea.fill(TEST_COURSE.introLessonObjectives);
console.log(' - 填写导入课教学目标');
}
// 填写教学准备
const introLessonPreparationTextarea = page.locator('textarea[placeholder*="教学准备"]').first();
if (await introLessonPreparationTextarea.isVisible({ timeout: 3000 })) {
await introLessonPreparationTextarea.fill(TEST_COURSE.introLessonPreparation);
console.log(' - 填写导入课教学准备');
}
// 点击下一步
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(1000);
console.log('✅ 步骤 4 完成 - 导入课');
// ==================== 步骤 5: 集体课 ====================
console.log('\n========== 步骤 5: 集体课 ==========');
await expect(page.locator('.step5-collective-lesson .title:has-text("集体课")')).toBeVisible({ timeout: 3000 });
// 点击创建集体课按钮(如果需要)
const createCollectiveButton = page.getByText('创建集体课');
if (await createCollectiveButton.isVisible({ timeout: 3000 })) {
await createCollectiveButton.click();
await page.waitForTimeout(1500); // 增加等待时间让组件渲染
console.log(' - 点击创建集体课');
}
// 等待输入框加载完成
await page.waitForTimeout(500);
// 填写集体课名称 - 使用更精确的选择器
const collectiveLessonNameInput = page.locator('input[placeholder="请输入课程名称"]').first();
if (await collectiveLessonNameInput.isVisible({ timeout: 5000 })) {
await collectiveLessonNameInput.fill(TEST_COURSE.collectiveLessonName);
console.log(` - 填写集体课名称:${TEST_COURSE.collectiveLessonName}`);
} else {
console.log(' ⚠️ 未找到集体课名称输入框');
}
// 填写课时时长 - 使用 aria-label 或 class 选择器
const collectiveLessonDurationInput = page.locator('.ant-input-number-input').nth(1);
if (await collectiveLessonDurationInput.isVisible({ timeout: 5000 })) {
await collectiveLessonDurationInput.fill(String(TEST_COURSE.collectiveLessonDuration));
console.log(` - 填写集体课时长:${TEST_COURSE.collectiveLessonDuration}分钟`);
} else {
console.log(' ⚠️ 未找到集体课时长输入框');
}
// 填写教学目标
const collectiveLessonObjectivesTextarea = page.locator('textarea[placeholder*="教学目标"]').first();
if (await collectiveLessonObjectivesTextarea.isVisible({ timeout: 3000 })) {
await collectiveLessonObjectivesTextarea.fill(TEST_COURSE.collectiveLessonObjectives);
console.log(' - 填写集体课教学目标');
}
// 填写教学准备
const collectiveLessonPreparationTextarea = page.locator('textarea[placeholder*="教学准备"]').first();
if (await collectiveLessonPreparationTextarea.isVisible({ timeout: 3000 })) {
await collectiveLessonPreparationTextarea.fill(TEST_COURSE.collectiveLessonObjectives);
console.log(' - 填写集体课教学准备');
}
// 点击下一步
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(1000);
console.log('✅ 步骤 5 完成 - 集体课');
// ==================== 步骤 6: 领域课 ====================
console.log('\n========== 步骤 6: 领域课 ==========');
await expect(page.locator('.step6-domain-lessons .title:has-text("五大领域课")')).toBeVisible({ timeout: 3000 });
// 填写健康领域课程
console.log(' - 填写健康领域课程');
// 展开健康领域卡片
const healthDomainCard = page.locator('.domain-card').filter({ hasText: /健康/ }).first();
const healthSwitch = healthDomainCard.locator('.ant-switch').first();
if (await healthSwitch.isVisible({ timeout: 3000 })) {
await healthSwitch.click();
await page.waitForTimeout(1000); // 增加等待时间让面板展开
}
// 等待输入框加载完成
await page.waitForTimeout(500);
// 填写健康领域课程名称 - 在 domain-card 内查找
const healthLessonNameInput = healthDomainCard.locator('input[placeholder="请输入课程名称"]').first();
if (await healthLessonNameInput.isVisible({ timeout: 5000 })) {
await healthLessonNameInput.fill(TEST_COURSE.healthLessonName);
console.log(` 课程名称:${TEST_COURSE.healthLessonName}`);
} else {
console.log(' ⚠️ 未找到健康领域课程名称输入框');
}
// 填写课时时长 - 在 domain-card 内查找第一个 input-number
const healthLessonDurationInput = healthDomainCard.locator('.ant-input-number-input').first();
if (await healthLessonDurationInput.isVisible({ timeout: 5000 })) {
await healthLessonDurationInput.fill(String(TEST_COURSE.healthLessonDuration));
console.log(` 课时时长:${TEST_COURSE.healthLessonDuration}分钟`);
} else {
console.log(' ⚠️ 未找到健康领域课程时长输入框');
}
// 填写教学目标
const healthLessonObjectivesTextarea = healthDomainCard.locator('textarea[placeholder*="教学目标"]').first();
if (await healthLessonObjectivesTextarea.isVisible({ timeout: 3000 })) {
await healthLessonObjectivesTextarea.fill(TEST_COURSE.healthLessonObjectives);
console.log(' 教学目标:已填写');
}
// 填写科学领域课程
console.log(' - 填写科学领域课程');
// 展开科学领域卡片
const scienceDomainCard = page.locator('.domain-card').filter({ hasText: /科学/ }).first();
const scienceSwitch = scienceDomainCard.locator('.ant-switch').first();
if (await scienceSwitch.isVisible({ timeout: 3000 })) {
await scienceSwitch.click();
await page.waitForTimeout(1000); // 增加等待时间让面板展开
}
// 等待输入框加载完成
await page.waitForTimeout(500);
// 填写科学领域课程名称 - 在 domain-card 内查找
const scienceLessonNameInput = scienceDomainCard.locator('input[placeholder="请输入课程名称"]').first();
if (await scienceLessonNameInput.isVisible({ timeout: 5000 })) {
await scienceLessonNameInput.fill(TEST_COURSE.scienceLessonName);
console.log(` 课程名称:${TEST_COURSE.scienceLessonName}`);
} else {
console.log(' ⚠️ 未找到科学领域课程名称输入框');
}
// 填写课时时长 - 在 domain-card 内查找 input-number
const scienceLessonDurationInput = scienceDomainCard.locator('.ant-input-number-input').first();
if (await scienceLessonDurationInput.isVisible({ timeout: 5000 })) {
await scienceLessonDurationInput.fill(String(TEST_COURSE.scienceLessonDuration));
console.log(` 课时时长:${TEST_COURSE.scienceLessonDuration}分钟`);
} else {
console.log(' ⚠️ 未找到科学领域课程时长输入框');
}
// 填写教学目标
const scienceLessonObjectivesTextarea = scienceDomainCard.locator('textarea[placeholder*="教学目标"]').first();
if (await scienceLessonObjectivesTextarea.isVisible({ timeout: 3000 })) {
await scienceLessonObjectivesTextarea.fill(TEST_COURSE.scienceLessonObjectives);
console.log(' 教学目标:已填写');
}
// 点击下一步
await page.getByRole('button', { name: '下一步' }).click();
await page.waitForTimeout(1000);
console.log('✅ 步骤 6 完成 - 领域课');
// ==================== 步骤 7: 环创建设 ====================
console.log('\n========== 步骤 7: 环创建设 ==========');
await expect(page.locator('.step7-environment .title:has-text("环创建设")')).toBeVisible({ timeout: 3000 });
// 填写环创建设内容
const envTextarea = page.locator('.step7-environment textarea').first();
await expect(envTextarea).toBeVisible({ timeout: 3000 });
await envTextarea.fill(TEST_COURSE.environmentConstruction);
console.log(' - 填写环创建设内容');
// 点击提交/创建按钮 - 使用 CSS 选择器定位
const submitButton = page.locator('.step-actions button.ant-btn-primary').last();
await submitButton.waitFor({ state: 'visible', timeout: 10000 });
// 监听网络请求
console.log('⏳ 开始监听网络请求...');
const [response] = await Promise.all([
page.waitForResponse(res =>
res.url().includes('/api/v1/admin/courses') &&
res.request().method() === 'POST'
).catch(() => null),
submitButton.click()
]);
console.log(' - 点击创建按钮');
console.log(' - 课程创建请求:', response ? response.status() : '未发送');
console.log('✅ 步骤 7 完成 - 环创建设');
// 等待一段时间让保存操作完成
await page.waitForTimeout(5000);
// ==================== 验证创建成功 ====================
console.log('\n========== 验证创建结果 ==========');
// 检查浏览器控制台日志
console.log('📋 准备检查页面状态...');
// 等待成功提示
try {
await page.waitForSelector('.ant-message-success', { timeout: 10000 });
console.log('✅ 成功提示显示');
} catch (e) {
console.log('⚠️ 未找到成功提示,检查是否有错误提示');
const errorMsg = await page.locator('.ant-message-error').textContent().catch(() => '');
if (errorMsg) {
console.error('❌ 错误信息:', errorMsg);
}
}
// 给页面一些时间执行跳转逻辑
await page.waitForTimeout(2000);
// 等待跳转到列表页 - 使用精确匹配(排除 edit 页面)
console.log('⏳ 等待页面跳转到课程列表...');
await page.waitForURL((url) => {
const urlStr = url.toString();
if (urlStr.includes('/edit')) {
console.log(' - 仍在编辑页面URL:', urlStr);
return false;
}
if (urlStr.includes('/admin/courses')) {
console.log(' - 已跳转到课程列表页URL:', urlStr);
return true;
}
return false;
}, { timeout: 15000 });
console.log('✅ 页面跳转到课程列表');
// 等待表格加载完成
await page.waitForTimeout(3000);
// 检查当前 URL
const currentUrl = page.url();
console.log('📍 当前 URL:', currentUrl);
// 使用更宽松的等待策略
const table = page.locator('.ant-table').first();
await table.waitFor({ state: 'attached', timeout: 10000 });
await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {});
await page.waitForTimeout(2000);
// 检查表格是否为空
const isEmpty = await table.locator('.ant-empty').isVisible().catch(() => false);
console.log('📊 表格是否为空:', isEmpty);
// 获取表格行数
const rowCount = await table.locator('.ant-table-tbody tr').count();
console.log('📊 表格行数:', rowCount);
// 获取所有课程包名称
const courseNames = await page.locator('.ant-table-tbody tr td:first-child').allTextContents();
console.log('📊 当前课程包列表:', courseNames);
// 查找新课程包
const courseRow = table.getByText(TEST_COURSE.name);
try {
await expect(courseRow).toBeVisible({ timeout: 10000 });
console.log(`✅ 新课程包 "${TEST_COURSE.name}" 在列表中显示`);
} catch {
// 如果找不到,刷新页面后再试一次
console.log(' - 第一次尝试未找到,刷新页面...');
await page.reload({ waitUntil: 'networkidle' });
await page.waitForTimeout(2000);
// 再次检查课程包列表
const courseNamesAfterRefresh = await page.locator('.ant-table-tbody tr td:first-child').allTextContents();
console.log('📊 刷新后课程包列表:', courseNamesAfterRefresh);
await expect(table.getByText(TEST_COURSE.name)).toBeVisible({ timeout: 10000 });
console.log(`✅ 新课程包 "${TEST_COURSE.name}" 在列表中显示(刷新后)`);
}
console.log('\n🎉 课程包创建测试全部通过!');
});
});