fix(e2e): 交替深链 /course/summary 与 /course;移除 networkidle;扩充壳层与超时

Made-with: Cursor
This commit is contained in:
2026-04-28 10:04:19 +08:00
parent 3428204659
commit 4e970f3a50
+40 -27
View File
@@ -38,25 +38,31 @@ function extractOrgIdFromTokenPayload(
/**
* 教学资源相关任意壳层可见即可(`/course/summary` 入口链或课程体系列表)。
* CI 上课程索引偶发长时间无搜索框;summary 页 Cell 更稳定
* CI 上 SPA 长连接会导致 networkidle 不可靠;此处只用 DOM 信号轮询
*/
async function waitForCourseRealmVisible(page: Page) {
const deadline = Date.now() + 95_000;
while (Date.now() < deadline) {
const titleHit = await page.title().catch(() => '');
if (/教学资源库|课程体系/.test(titleHit)) {
return;
}
if (
await page.getByRole('link', { name: '课程体系' }).isVisible().catch(() => false)
) {
return;
}
if (
await page
.locator('.van-cell')
.filter({ hasText: /课程体系/ })
.first()
.isVisible()
.catch(() => false)
) {
return;
for (const label of ['课程体系', '课程', '课节']) {
if (
await page
.locator('.van-cell')
.filter({ hasText: new RegExp(label) })
.first()
.isVisible()
.catch(() => false)
) {
return;
}
}
if (
await page
@@ -73,9 +79,7 @@ async function waitForCourseRealmVisible(page: Page) {
}
await page.waitForTimeout(400);
}
throw new Error(
`教学资源壳层未出现:${page.url()}`
);
throw new Error(`教学资源壳层未出现:${page.url()}`);
}
async function establishSession(page: Page) {
@@ -100,11 +104,15 @@ async function establishSession(page: Page) {
/**
* 停留在 `/` 时 main.ts 不会把 JWT 中的机构写入 CurrentOrgId。
* 深链到 `/course` 会触发守卫;多机构且无上下文时会先进入 `/org/select`,需点选机构后再继续。
* 深链会触发守卫;多机构且无上下文时会先进入 `/org/select`,需点选机构后再继续。
* 交替尝试 summary 与课程索引:部分账号在 CI 上只对其中之一稳定渲染壳层。
*/
async function resolveOrgContextForCoursePage(page: Page) {
for (let attempt = 0; attempt < 6; attempt++) {
await page.goto('/course/summary', {
const deepPaths = ['/course/summary', '/course'] as const;
for (let attempt = 0; attempt < 8; attempt++) {
const targetPath = deepPaths[attempt % deepPaths.length];
await page.goto(targetPath, {
waitUntil: 'domcontentloaded',
timeout: 90_000,
});
@@ -138,13 +146,16 @@ async function resolveOrgContextForCoursePage(page: Page) {
continue;
}
if (
page.url().includes('/course/summary') ||
page.url().includes('/course')
) {
await page.waitForLoadState('networkidle', { timeout: 60_000 }).catch(() => {});
await waitForCourseRealmVisible(page);
return;
try {
const path = new URL(page.url()).pathname || '';
const onCourseRealm =
path === '/course' || path.startsWith('/course/');
if (onCourseRealm) {
await waitForCourseRealmVisible(page);
return;
}
} catch {
/* URL 解析失败则继续重试 */
}
// 守卫可能暂时送回首页「进入工作台」等,下一循环再深链
@@ -156,6 +167,8 @@ async function resolveOrgContextForCoursePage(page: Page) {
}
test.describe('多机构数据隔离(会话与 token', () => {
test.describe.configure({ timeout: 180_000 });
test.skip(!moicenUnionid, '需要 MOICEN_E2E_UNIONIDSecret 或 .env.e2e');
test('本地 CurrentOrgId 与 JWT current_org_id 一致', async ({ page }) => {
@@ -201,7 +214,6 @@ test.describe('多机构数据隔离(会话与 token', () => {
});
await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 });
await page.waitForURL(/\/org\/select/, { timeout: 60_000 });
await page.waitForLoadState('networkidle', { timeout: 60_000 }).catch(() => {});
await expect(page.getByText(/请选择机构|选择机构/)).toBeVisible({
timeout: 60_000,
});
@@ -274,8 +286,9 @@ test.describe('多机构数据隔离(会话与 token', () => {
await expect(page.locator('#app')).toBeVisible({ timeout: 90_000 });
await waitForCourseRealmVisible(page);
await expect(
page.getByText('请求失败,点击重新加载')
).not.toBeVisible({ timeout: 45_000 });
const listError = page.getByText('请求失败,点击重新加载');
await expect(listError).toHaveCount(0, {
timeout: 45_000,
});
});
});