diff --git a/tests/org-data-isolation.spec.ts b/tests/org-data-isolation.spec.ts index 9016c99..55a130a 100644 --- a/tests/org-data-isolation.spec.ts +++ b/tests/org-data-isolation.spec.ts @@ -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_UNIONID(Secret 或 .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, + }); }); });