test: fix detail page E2E tests — use real login flow instead of fake JWT
The mocked detail page tests were using localStorage fake JWTs, but the frontend auth guard in router.beforeEach requires BOTH Authorization and HtySudoToken to be present — without both, it redirects to / before the detail component ever mounts, so the route mock was never reached. Rewrite to use real student login first, then navigate with mocks active. Tests 11-13: login → mock API → navigate to detail page → assert. Test 14: fix .group-sections assertion for packages without course items. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -254,13 +254,53 @@ test.describe('课包商店(已登录学生查看老师主页)', () => {
|
||||
});
|
||||
});
|
||||
|
||||
async function loginAsStudent(page: any, moicenUnionid: string) {
|
||||
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)) {
|
||||
return false; // session not usable
|
||||
}
|
||||
const orgCell = page.locator('#app .van-cell-group .van-cell').first();
|
||||
if (await orgCell.isVisible().catch(() => false)) {
|
||||
await orgCell.click();
|
||||
await page.waitForTimeout(5000);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
test.describe('课包详情页权限与预览', () => {
|
||||
test.describe('权限、预览与状态(mock API)', () => {
|
||||
test.beforeEach(({ }, {}) => {
|
||||
if (!moicenUnionid) {
|
||||
test.skip(true, 'MOICEN_E2E_UNIONID 未设置');
|
||||
}
|
||||
});
|
||||
|
||||
test('非创建者查看课包详情不显示管理按钮', async ({ page }) => {
|
||||
const testPkgId = 'pkg-e2e-perm';
|
||||
test.setTimeout(180_000);
|
||||
if (!moicenUnionid) return;
|
||||
|
||||
// Only mock the specific package detail URL
|
||||
await page.route(`**/course-package/${testPkgId}`, async route => {
|
||||
const ok = await loginAsStudent(page, moicenUnionid);
|
||||
if (!ok) { test.skip(true, '会话不可用'); return; }
|
||||
|
||||
const testPkgId = 'pkg-e2e-perm';
|
||||
await page.route(new RegExp(`/api/v1/clazz/course-package/${testPkgId}`), async route => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
@@ -292,32 +332,25 @@ test.describe('课包详情页权限与预览', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// 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 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';
|
||||
test.setTimeout(180_000);
|
||||
if (!moicenUnionid) return;
|
||||
|
||||
await page.route(`**/course-package/${testPkgId}`, async route => {
|
||||
const ok = await loginAsStudent(page, moicenUnionid);
|
||||
if (!ok) { test.skip(true, '会话不可用'); return; }
|
||||
|
||||
const testPkgId = 'pkg-e2e-preview';
|
||||
await page.route(new RegExp(`/api/v1/clazz/course-package/${testPkgId}`), async route => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
@@ -350,29 +383,21 @@ test.describe('课包详情页权限与预览', () => {
|
||||
});
|
||||
});
|
||||
|
||||
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 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
|
||||
// First section should have "试看" badge, not locked
|
||||
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 "已锁定"
|
||||
// Second section should be locked
|
||||
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
|
||||
// Third section should be locked
|
||||
const thirdSection = page.locator('.section-item').nth(2);
|
||||
await expect(thirdSection.locator('.lock-label')).toBeVisible();
|
||||
|
||||
@@ -386,9 +411,14 @@ test.describe('课包详情页权限与预览', () => {
|
||||
});
|
||||
|
||||
test('已上架课包显示状态标签', async ({ page }) => {
|
||||
const testPkgId = 'pkg-e2e-active';
|
||||
test.setTimeout(180_000);
|
||||
if (!moicenUnionid) return;
|
||||
|
||||
await page.route(`**/course-package/${testPkgId}`, async route => {
|
||||
const ok = await loginAsStudent(page, moicenUnionid);
|
||||
if (!ok) { test.skip(true, '会话不可用'); return; }
|
||||
|
||||
const testPkgId = 'pkg-e2e-active';
|
||||
await page.route(new RegExp(`/api/v1/clazz/course-package/${testPkgId}`), async route => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
@@ -409,58 +439,24 @@ test.describe('课包详情页权限与预览', () => {
|
||||
});
|
||||
});
|
||||
|
||||
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 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.describe('真实数据验证', () => {
|
||||
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);
|
||||
}
|
||||
}
|
||||
const ok = await loginAsStudent(page, moicenUnionid);
|
||||
if (!ok) { test.skip(true, '会话不可用'); return; }
|
||||
|
||||
// Navigate to a published package detail (owned by 周晓慧, not by 阿难)
|
||||
await page.goto('/course/course-package/detail?id=pkg-mc-001', {
|
||||
@@ -479,8 +475,10 @@ test.describe('课包详情页权限与预览', () => {
|
||||
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)
|
||||
// Should see section list or empty state
|
||||
const sectionList = page.locator('.group-sections');
|
||||
await expect(sectionList.first()).toBeVisible({ timeout: 10_000 });
|
||||
const emptyState = page.locator('.empty');
|
||||
await expect(sectionList.first().or(emptyState)).toBeVisible({ timeout: 10_000 });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user