test(course-package): add org_visible and course_package_item sync E2E tests
- API test: course_group org_visible toggle (create → visible → list → invisible) - API test: course_package_item sync with SUPERVISOR permission check - UI test: "包含课节" picker link on course-package add page - UI test: "开放给机构" switch on course-group add page Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -213,6 +213,159 @@ test.describe('课包(course_package)', () => {
|
||||
expect(typeof orgBody.d?.[2] === 'number').toBe(true);
|
||||
});
|
||||
|
||||
test('course_group org_visible 开关全链路:创建 → 设为机构可见 → 查询可见列表 → 取消可见 → 验证移除', async ({
|
||||
page,
|
||||
request,
|
||||
}) => {
|
||||
const authToken = await loginAndGetJwt(page);
|
||||
const headers: Record<string, string> = {
|
||||
Authorization: authToken,
|
||||
HtySudoerToken: authToken,
|
||||
HtyHost: new URL(kcBase).hostname,
|
||||
};
|
||||
|
||||
const groupName = `e2e-test-org-visible-${Date.now()}`;
|
||||
let groupId: string;
|
||||
|
||||
// ---- CREATE course_group ----
|
||||
const createRes = await request.post(`${kcBase}/api/v1/ws/create_course_group`, {
|
||||
headers: { ...headers, 'Content-Type': 'application/json' },
|
||||
data: { group_name: groupName },
|
||||
});
|
||||
expect(createRes.ok(), `CREATE GROUP HTTP ${createRes.status()}`).toBeTruthy();
|
||||
const createBody = await createRes.json();
|
||||
expect(createBody.r, `CREATE GROUP 业务失败: ${JSON.stringify(createBody)}`).toBe(true);
|
||||
expect(createBody.d?.id).toBeTruthy();
|
||||
expect(createBody.d?.group_name).toBe(groupName);
|
||||
groupId = createBody.d.id;
|
||||
|
||||
try {
|
||||
// ---- UPDATE org_visible=true ----
|
||||
const updateVisibleRes = await request.post(`${kcBase}/api/v1/ws/update_course_group`, {
|
||||
headers: { ...headers, 'Content-Type': 'application/json' },
|
||||
data: { id: groupId, group_name: groupName, org_visible: true },
|
||||
});
|
||||
expect(updateVisibleRes.ok(), `UPDATE VISIBLE HTTP ${updateVisibleRes.status()}`).toBeTruthy();
|
||||
const updateVisibleBody = await updateVisibleRes.json();
|
||||
expect(updateVisibleBody.r, `UPDATE VISIBLE 业务失败: ${JSON.stringify(updateVisibleBody)}`).toBe(true);
|
||||
|
||||
// ---- FIND org-visible groups - should include our group ----
|
||||
const visibleRes = await request.get(`${kcBase}/api/v1/ws/find_org_visible_course_groups`, {
|
||||
headers,
|
||||
});
|
||||
expect(visibleRes.ok(), `FIND VISIBLE HTTP ${visibleRes.status()}`).toBeTruthy();
|
||||
const visibleBody = await visibleRes.json();
|
||||
expect(visibleBody.r, `FIND VISIBLE 业务失败: ${JSON.stringify(visibleBody)}`).toBe(true);
|
||||
const visibleList: any[] = visibleBody.d ?? [];
|
||||
const foundVisible = visibleList.some((g: any) => g.id === groupId);
|
||||
expect(foundVisible, 'org_visible=true 的分组应出现在机构可见列表中').toBe(true);
|
||||
|
||||
// ---- UPDATE org_visible=false ----
|
||||
const updateInvisibleRes = await request.post(`${kcBase}/api/v1/ws/update_course_group`, {
|
||||
headers: { ...headers, 'Content-Type': 'application/json' },
|
||||
data: { id: groupId, group_name: groupName, org_visible: false },
|
||||
});
|
||||
expect(updateInvisibleRes.ok(), `UPDATE INVISIBLE HTTP ${updateInvisibleRes.status()}`).toBeTruthy();
|
||||
|
||||
// ---- FIND org-visible groups - should NOT include our group ----
|
||||
const invisibleRes = await request.get(`${kcBase}/api/v1/ws/find_org_visible_course_groups`, {
|
||||
headers,
|
||||
});
|
||||
expect(invisibleRes.ok(), `FIND INVISIBLE HTTP ${invisibleRes.status()}`).toBeTruthy();
|
||||
const invisibleBody = await invisibleRes.json();
|
||||
expect(invisibleBody.r, `FIND INVISIBLE 业务失败: ${JSON.stringify(invisibleBody)}`).toBe(true);
|
||||
const invisibleList: any[] = invisibleBody.d ?? [];
|
||||
const foundInvisible = invisibleList.some((g: any) => g.id === groupId);
|
||||
expect(foundInvisible, 'org_visible=false 的分组不应出现在机构可见列表中').toBe(false);
|
||||
} finally {
|
||||
// ---- CLEANUP: delete course_group ----
|
||||
await request.post(`${kcBase}/api/v1/ws/delete_course_group/${groupId}`, { headers });
|
||||
}
|
||||
});
|
||||
|
||||
test('course_package_item sync 全链路(SUPERVISOR):创建分组 → 设 org_visible → 创建课包 → sync → 列表验证', async ({
|
||||
page,
|
||||
request,
|
||||
}) => {
|
||||
const authToken = await loginAndGetJwt(page);
|
||||
const payload = decodeJwtPayload(authToken);
|
||||
const roles = extractRoleKeys(payload);
|
||||
const isSupervisor = roles.includes('SUPERVISOR');
|
||||
test.skip(!isSupervisor, '当前账号无 SUPERVISOR 权限,跳过 sync 测试');
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
Authorization: authToken,
|
||||
HtySudoerToken: authToken,
|
||||
HtyHost: new URL(kcBase).hostname,
|
||||
};
|
||||
|
||||
const ts = Date.now();
|
||||
const groupName = `e2e-test-sync-group-${ts}`;
|
||||
const pkgName = `e2e-test-sync-pkg-${ts}`;
|
||||
let groupId: string;
|
||||
let pkgId: string;
|
||||
|
||||
// ---- CREATE course_group with org_visible=true ----
|
||||
const createGroupRes = await request.post(`${kcBase}/api/v1/ws/create_course_group`, {
|
||||
headers: { ...headers, 'Content-Type': 'application/json' },
|
||||
data: { group_name: groupName },
|
||||
});
|
||||
expect(createGroupRes.ok(), `CREATE GROUP HTTP ${createGroupRes.status()}`).toBeTruthy();
|
||||
const createGroupBody = await createGroupRes.json();
|
||||
expect(createGroupBody.r, `CREATE GROUP 业务失败: ${JSON.stringify(createGroupBody)}`).toBe(true);
|
||||
groupId = createGroupBody.d.id;
|
||||
|
||||
await request.post(`${kcBase}/api/v1/ws/update_course_group`, {
|
||||
headers: { ...headers, 'Content-Type': 'application/json' },
|
||||
data: { id: groupId, group_name: groupName, org_visible: true },
|
||||
});
|
||||
|
||||
try {
|
||||
// ---- CREATE course_package ----
|
||||
const createPkgRes = await request.post(`${kcBase}/api/v1/clazz/course-package/create`, {
|
||||
headers: { ...headers, 'Content-Type': 'application/json' },
|
||||
data: {
|
||||
package_name: pkgName,
|
||||
description: 'e2e test sync',
|
||||
package_status: 'ACTIVE',
|
||||
},
|
||||
});
|
||||
expect(createPkgRes.ok(), `CREATE PKG HTTP ${createPkgRes.status()}`).toBeTruthy();
|
||||
const createPkgBody = await createPkgRes.json();
|
||||
expect(createPkgBody.r, `CREATE PKG 业务失败: ${JSON.stringify(createPkgBody)}`).toBe(true);
|
||||
expect(createPkgBody.d?.id).toBeTruthy();
|
||||
pkgId = createPkgBody.d.id;
|
||||
|
||||
// ---- SYNC items ----
|
||||
const syncRes = await request.post(`${kcBase}/api/v1/clazz/course-package/item/sync`, {
|
||||
headers: { ...headers, 'Content-Type': 'application/json' },
|
||||
data: { package_id: pkgId, course_group_ids: [groupId] },
|
||||
});
|
||||
expect(syncRes.ok(), `SYNC HTTP ${syncRes.status()}`).toBeTruthy();
|
||||
const syncBody = await syncRes.json();
|
||||
expect(syncBody.r, `SYNC 业务失败: ${JSON.stringify(syncBody)}`).toBe(true);
|
||||
|
||||
// ---- LIST items ----
|
||||
const listRes = await request.get(`${kcBase}/api/v1/clazz/course-package/item/list/${pkgId}`, {
|
||||
headers,
|
||||
});
|
||||
expect(listRes.ok(), `LIST ITEMS HTTP ${listRes.status()}`).toBeTruthy();
|
||||
const listBody = await listRes.json();
|
||||
expect(listBody.r, `LIST ITEMS 业务失败: ${JSON.stringify(listBody)}`).toBe(true);
|
||||
const items: any[] = listBody.d ?? [];
|
||||
const matched = items.some((g: any) => g.id === groupId);
|
||||
expect(matched, 'sync 后列表应包含该分组').toBe(true);
|
||||
} finally {
|
||||
// ---- CLEANUP ----
|
||||
if (pkgId) {
|
||||
await request.post(`${kcBase}/api/v1/clazz/course-package/delete/${pkgId}`, { headers }).catch(() => {});
|
||||
}
|
||||
if (groupId) {
|
||||
await request.post(`${kcBase}/api/v1/ws/delete_course_group/${groupId}`, { headers }).catch(() => {});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('教师端 UI:导航栏出现"课包"入口,页面加载种子数据', async ({
|
||||
page,
|
||||
}) => {
|
||||
@@ -413,4 +566,76 @@ test.describe('音乐教室端(huike-front)课包 UI', () => {
|
||||
// 验证提交按钮
|
||||
await expect(page.getByText('保存')).toBeVisible({ timeout: 10_000 });
|
||||
});
|
||||
|
||||
test('课包新增页面:包含课节选择器可见', async ({ page }) => {
|
||||
const q = new URLSearchParams({ unionid: moicenUnionid!, status: '2' });
|
||||
await page.goto(`/?${q.toString()}`, {
|
||||
waitUntil: 'domcontentloaded',
|
||||
timeout: 60_000,
|
||||
});
|
||||
await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 });
|
||||
await page.waitForFunction(
|
||||
() => !window.location.search.includes('status=2'),
|
||||
{ timeout: 90_000 },
|
||||
);
|
||||
if (await page.getByText('请选择您的登录身份').isVisible().catch(() => false)) {
|
||||
await page.locator('.van-grid-item').first().click();
|
||||
await page.waitForFunction(
|
||||
() => !window.location.search.includes('status=2'),
|
||||
{ timeout: 30_000 },
|
||||
);
|
||||
}
|
||||
|
||||
const { resolveOrgContextForCoursePage } = await import('./helpers/music-room-session');
|
||||
await resolveOrgContextForCoursePage(page);
|
||||
|
||||
await page.goto('/course/course-package/add', {
|
||||
waitUntil: 'domcontentloaded',
|
||||
timeout: 60_000,
|
||||
});
|
||||
await expect(page.locator('#app')).toBeVisible({ timeout: 30_000 });
|
||||
|
||||
// 验证"包含课节"选择器链接可见
|
||||
const pickerCell = page.locator('.van-cell').filter({ hasText: '包含课节' });
|
||||
await expect(pickerCell).toBeVisible({ timeout: 10_000 });
|
||||
// 验证点击后跳转到选择页
|
||||
await pickerCell.click();
|
||||
await expect(page).toHaveURL(/\/course\/group\/pick/, { timeout: 10_000 });
|
||||
});
|
||||
|
||||
test('课节新增页面:开放给机构开关可见', async ({ page }) => {
|
||||
const q = new URLSearchParams({ unionid: moicenUnionid!, status: '2' });
|
||||
await page.goto(`/?${q.toString()}`, {
|
||||
waitUntil: 'domcontentloaded',
|
||||
timeout: 60_000,
|
||||
});
|
||||
await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 });
|
||||
await page.waitForFunction(
|
||||
() => !window.location.search.includes('status=2'),
|
||||
{ timeout: 90_000 },
|
||||
);
|
||||
if (await page.getByText('请选择您的登录身份').isVisible().catch(() => false)) {
|
||||
await page.locator('.van-grid-item').first().click();
|
||||
await page.waitForFunction(
|
||||
() => !window.location.search.includes('status=2'),
|
||||
{ timeout: 30_000 },
|
||||
);
|
||||
}
|
||||
|
||||
const { resolveOrgContextForCoursePage } = await import('./helpers/music-room-session');
|
||||
await resolveOrgContextForCoursePage(page);
|
||||
|
||||
await page.goto('/course/group/add', {
|
||||
waitUntil: 'domcontentloaded',
|
||||
timeout: 60_000,
|
||||
});
|
||||
await expect(page.locator('#app')).toBeVisible({ timeout: 30_000 });
|
||||
|
||||
// 验证"开放给机构"切换开关可见
|
||||
const orgVisibleCell = page.locator('.van-cell').filter({ hasText: '开放给机构' });
|
||||
await expect(orgVisibleCell).toBeVisible({ timeout: 10_000 });
|
||||
// 验证开关组件存在
|
||||
const switchEl = orgVisibleCell.locator('.van-switch');
|
||||
await expect(switchEl).toBeVisible({ timeout: 10_000 });
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user