test: add detail page permission, preview lock, and status E2E tests
This commit is contained in:
@@ -209,7 +209,7 @@ test.describe('课包商店(已登录学生查看老师主页)', () => {
|
||||
|
||||
// Handle org select if needed
|
||||
if (page.url().includes('/org/select')) {
|
||||
if (await page.getByText('请返回微信小程序完成登录').isVisible().catch(() => false)) {
|
||||
if (await page.locator('.course-package-store').isVisible().catch(() => false)) {
|
||||
test.skip(true, '会话不可用');
|
||||
return;
|
||||
}
|
||||
@@ -253,3 +253,234 @@ test.describe('课包商店(已登录学生查看老师主页)', () => {
|
||||
expect(await previewBtns.count()).toBe(cardCount);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('课包详情页权限与预览', () => {
|
||||
|
||||
test('非创建者查看课包详情不显示管理按钮', async ({ page }) => {
|
||||
const testPkgId = 'pkg-e2e-perm';
|
||||
|
||||
// Only mock the specific package detail URL
|
||||
await page.route(`**/course-package/${testPkgId}`, async route => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
r: true,
|
||||
d: {
|
||||
id: testPkgId,
|
||||
package_name: '课包-权限验证',
|
||||
description: '非创建者不应看到管理按钮',
|
||||
total_lessons: 10,
|
||||
package_status: 'ACTIVE',
|
||||
created_by: 'someone-else-id',
|
||||
published_at: '2026-01-01T00:00:00Z',
|
||||
published_snapshot: {
|
||||
course_groups: [
|
||||
{
|
||||
id: 'group-1',
|
||||
group_name: '基础训练',
|
||||
course_sections: [
|
||||
{ id: 'sec-1', course_name: '钢琴', section_name: '第一课-基础指法' },
|
||||
{ id: 'sec-2', course_name: '钢琴', section_name: '第二课-音阶练习' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
e: null,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
// Set a fake token so isLoggedIn is true
|
||||
await page.goto('/', { waitUntil: 'domcontentloaded', timeout: 60_000 });
|
||||
await page.evaluate(() => {
|
||||
localStorage.setItem('Authorization', 'fake-jwt-token');
|
||||
});
|
||||
|
||||
await page.goto(`/course/course-package/detail?id=${testPkgId}`, {
|
||||
waitUntil: 'domcontentloaded',
|
||||
timeout: 60_000,
|
||||
});
|
||||
await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 });
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
// Management buttons should NOT be visible (non-owner)
|
||||
await expect(page.locator('button').filter({ hasText: '编辑' })).not.toBeVisible();
|
||||
await expect(page.locator('button').filter({ hasText: '删除' })).not.toBeVisible();
|
||||
await expect(page.locator('button').filter({ hasText: '发布上架' })).not.toBeVisible();
|
||||
|
||||
// Package name should still be visible
|
||||
await expect(page.locator('.title')).toContainText('课包-权限验证');
|
||||
});
|
||||
|
||||
test('非创建者仅第一节课节可试看,其余已锁定', async ({ page }) => {
|
||||
const testPkgId = 'pkg-e2e-preview';
|
||||
|
||||
await page.route(`**/course-package/${testPkgId}`, async route => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
r: true,
|
||||
d: {
|
||||
id: testPkgId,
|
||||
package_name: '课包-预览验证',
|
||||
description: '仅第一节课节可试看',
|
||||
total_lessons: 3,
|
||||
package_status: 'ACTIVE',
|
||||
created_by: 'someone-else-id',
|
||||
published_at: '2026-01-01T00:00:00Z',
|
||||
published_snapshot: {
|
||||
course_groups: [
|
||||
{
|
||||
id: 'group-1',
|
||||
group_name: '基础训练',
|
||||
course_sections: [
|
||||
{ id: 'sec-p1', course_name: '钢琴', section_name: '第一课-基础指法' },
|
||||
{ id: 'sec-p2', course_name: '钢琴', section_name: '第二课-音阶练习' },
|
||||
{ id: 'sec-p3', course_name: '钢琴', section_name: '第三课-和弦' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
e: null,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto('/', { waitUntil: 'domcontentloaded', timeout: 60_000 });
|
||||
await page.evaluate(() => {
|
||||
localStorage.setItem('Authorization', 'fake-jwt-token');
|
||||
});
|
||||
|
||||
await page.goto(`/course/course-package/detail?id=${testPkgId}`, {
|
||||
waitUntil: 'domcontentloaded',
|
||||
timeout: 60_000,
|
||||
});
|
||||
await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 });
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
// First section should have the "试看" badge
|
||||
const firstSection = page.locator('.section-item').first();
|
||||
await expect(firstSection.locator('.preview-badge')).toBeVisible();
|
||||
await expect(firstSection.locator('.lock-label')).not.toBeVisible();
|
||||
|
||||
// Second section should show "已锁定"
|
||||
const secondSection = page.locator('.section-item').nth(1);
|
||||
await expect(secondSection.locator('.lock-label')).toBeVisible();
|
||||
await expect(secondSection.locator('.preview-badge')).not.toBeVisible();
|
||||
|
||||
// Third section should also be locked
|
||||
const thirdSection = page.locator('.section-item').nth(2);
|
||||
await expect(thirdSection.locator('.lock-label')).toBeVisible();
|
||||
|
||||
// Clicking locked section shows purchase dialog
|
||||
await secondSection.click();
|
||||
await page.waitForTimeout(500);
|
||||
const dialog = page.locator('.van-dialog');
|
||||
await expect(dialog).toBeVisible({ timeout: 5000 });
|
||||
await expect(dialog).toContainText('购买课包');
|
||||
await page.locator('.van-dialog__confirm').click();
|
||||
});
|
||||
|
||||
test('已上架课包显示状态标签', async ({ page }) => {
|
||||
const testPkgId = 'pkg-e2e-active';
|
||||
|
||||
await page.route(`**/course-package/${testPkgId}`, async route => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
r: true,
|
||||
d: {
|
||||
id: testPkgId,
|
||||
package_name: '课包-已上架',
|
||||
description: '已上架的课包',
|
||||
total_lessons: 10,
|
||||
package_status: 'ACTIVE',
|
||||
created_by: 'someone-else-id',
|
||||
published_at: '2026-01-01T00:00:00Z',
|
||||
published_snapshot: null,
|
||||
},
|
||||
e: null,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto('/', { waitUntil: 'domcontentloaded', timeout: 60_000 });
|
||||
await page.evaluate(() => {
|
||||
localStorage.setItem('Authorization', 'fake-jwt-token');
|
||||
});
|
||||
|
||||
await page.goto(`/course/course-package/detail?id=${testPkgId}`, {
|
||||
waitUntil: 'domcontentloaded',
|
||||
timeout: 60_000,
|
||||
});
|
||||
await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 });
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
// Status tag should show "上架中"
|
||||
await expect(page.locator('.header').getByText('上架中')).toBeVisible();
|
||||
});
|
||||
|
||||
test('已登录学生从商店进入课包详情无管理权限', async ({ page }) => {
|
||||
test.setTimeout(180_000);
|
||||
|
||||
if (!moicenUnionid) {
|
||||
test.skip(true, 'MOICEN_E2E_UNIONID 未设置');
|
||||
return;
|
||||
}
|
||||
|
||||
// Login as student
|
||||
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.waitForTimeout(5000);
|
||||
|
||||
// Select student role if prompted
|
||||
const roleSelect = page.getByText('请选择您的登录身份');
|
||||
if (await roleSelect.isVisible().catch(() => false)) {
|
||||
const studentRole = page.locator('.van-grid-item').filter({ hasText: '学生' });
|
||||
if (await studentRole.isVisible().catch(() => false)) {
|
||||
await studentRole.click();
|
||||
await page.waitForTimeout(5000);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle org select if needed
|
||||
if (page.url().includes('/org/select')) {
|
||||
if (await page.locator('.course-package-store').isVisible().catch(() => false)) {
|
||||
test.skip(true, '会话不可用');
|
||||
return;
|
||||
}
|
||||
const orgCell = page.locator('#app .van-cell-group .van-cell').first();
|
||||
if (await orgCell.isVisible().catch(() => false)) {
|
||||
await orgCell.click();
|
||||
await page.waitForTimeout(5000);
|
||||
}
|
||||
}
|
||||
|
||||
// Navigate to a published package detail (owned by 周晓慧, not by 阿难)
|
||||
await page.goto('/course/course-package/detail?id=pkg-mc-001', {
|
||||
waitUntil: 'domcontentloaded',
|
||||
timeout: 60_000,
|
||||
});
|
||||
await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 });
|
||||
await page.waitForTimeout(5000);
|
||||
|
||||
// Should see the package name
|
||||
await expect(page.locator('.title')).toBeVisible({ timeout: 10_000 });
|
||||
|
||||
// Should NOT see management buttons
|
||||
await expect(page.locator('button').filter({ hasText: '编辑' })).not.toBeVisible();
|
||||
await expect(page.locator('button').filter({ hasText: '删除' })).not.toBeVisible();
|
||||
await expect(page.locator('button').filter({ hasText: '发布上架' })).not.toBeVisible();
|
||||
await expect(page.locator('button').filter({ hasText: '下架' })).not.toBeVisible();
|
||||
|
||||
// Should see section list (even if empty)
|
||||
const sectionList = page.locator('.group-sections');
|
||||
await expect(sectionList.first()).toBeVisible({ timeout: 10_000 });
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user