diff --git a/tests/org-data-isolation.spec.ts b/tests/org-data-isolation.spec.ts index 7b222c6..703c285 100644 --- a/tests/org-data-isolation.spec.ts +++ b/tests/org-data-isolation.spec.ts @@ -16,6 +16,25 @@ function decodeJwtPayload(token: string): Record | null { } } +/** 与 `huike-front/src/main.ts` 中 `parseCurrentOrgIdFromToken` 一致:claims 可能在顶层或嵌在 `sub` JSON 内 */ +function extractOrgIdFromTokenPayload( + payload: Record | null +): string | undefined { + if (!payload) return undefined; + const top = payload.current_org_id; + if (typeof top === 'string' && top.length > 0) return top; + const sub = payload.sub; + if (typeof sub !== 'string' || sub.length === 0) return undefined; + try { + const inner = JSON.parse(sub) as Record; + const innerOrg = inner.current_org_id; + if (typeof innerOrg === 'string' && innerOrg.length > 0) return innerOrg; + } catch { + return undefined; + } + return undefined; +} + async function establishSession(page: Page) { const q = new URLSearchParams({ unionid: moicenUnionid!, @@ -48,35 +67,44 @@ async function resolveOrgContextForCoursePage(page: Page) { }); await expect(page.locator('#app')).toBeVisible({ timeout: 90_000 }); - if (!page.url().includes('/org/select')) { + if (page.url().includes('/org/select')) { + await expect(page.locator('.main h4')).toContainText(/请选择机构/, { + timeout: 60_000, + }); + await Promise.race([ + page + .locator('.main .van-cell-group .van-cell') + .first() + .waitFor({ state: 'visible', timeout: 60_000 }), + page.getByText('暂无可用机构').waitFor({ state: 'visible', timeout: 60_000 }), + ]); + + const emptyOrgs = await page + .getByText('暂无可用机构') + .isVisible() + .catch(() => false); + if (emptyOrgs) { + test.skip(true, '账号无可用机构,跳过机构上下文用例'); + } + + const pickCells = page.locator('.main .van-cell-group .van-cell'); + const n = await pickCells.count(); + expect(n, '机构选择页应有可选机构').toBeGreaterThan(0); + await pickCells.first().click(); + await expect(page.locator('#app')).toBeVisible({ timeout: 90_000 }); + continue; + } + + if (page.url().includes('/course')) { return; } - await expect(page.getByText('请选择机构')).toBeVisible({ timeout: 60_000 }); - await Promise.race([ - page - .locator('.main .van-cell-group .van-cell') - .first() - .waitFor({ state: 'visible', timeout: 60_000 }), - page.getByText('暂无可用机构').waitFor({ state: 'visible', timeout: 60_000 }), - ]); - - const emptyOrgs = await page - .getByText('暂无可用机构') - .isVisible() - .catch(() => false); - if (emptyOrgs) { - test.skip(true, '账号无可用机构,跳过机构上下文用例'); - } - - const pickCells = page.locator('.main .van-cell-group .van-cell'); - const n = await pickCells.count(); - expect(n, '机构选择页应有可选机构').toBeGreaterThan(0); - await pickCells.first().click(); - await expect(page.locator('#app')).toBeVisible({ timeout: 90_000 }); + // 守卫可能暂时送回首页「进入工作台」等,下一循环再深链 `/course` } - throw new Error('resolveOrgContextForCoursePage: 无法在合理步数内离开机构选择页'); + throw new Error( + 'resolveOrgContextForCoursePage: 无法在合理步数内进入 /course(可能被重定向离开课程体系)' + ); } test.describe('多机构数据隔离(会话与 token)', () => { @@ -100,10 +128,10 @@ test.describe('多机构数据隔离(会话与 token)', () => { const payload = decodeJwtPayload(storage.authorization!); expect(payload, 'Authorization 应为可解析的 JWT').not.toBeNull(); - const jwtOrgId = payload!.current_org_id; - expect(jwtOrgId, 'JWT 应包含 current_org_id').toBeTruthy(); + const jwtOrgId = extractOrgIdFromTokenPayload(payload); + expect(jwtOrgId, 'JWT(含 sub 嵌套)应能解析出 current_org_id').toBeTruthy(); expect(storage.currentOrgId, '进入业务路由后应写入 CurrentOrgId').toBeTruthy(); - expect(String(jwtOrgId)).toBe(storage.currentOrgId); + expect(jwtOrgId).toBe(storage.currentOrgId); }); test('机构选择页存在多个机构时,切换后 CurrentOrgId 变化', async ({ @@ -123,7 +151,8 @@ test.describe('多机构数据隔离(会话与 token)', () => { timeout: 60_000, }); await expect(page.locator('#app')).toBeVisible({ timeout: 60_000 }); - await expect(page.getByText('请选择机构')).toBeVisible({ + await page.waitForURL(/\/org\/select/, { timeout: 60_000 }); + await expect(page.locator('.main h4')).toContainText(/请选择机构/, { timeout: 60_000, }); @@ -165,8 +194,9 @@ test.describe('多机构数据隔离(会话与 token)', () => { window.localStorage.getItem('Authorization') ); const payloadAfter = decodeJwtPayload(authAfter ?? ''); - expect(payloadAfter?.current_org_id).toBeTruthy(); - expect(String(payloadAfter!.current_org_id)).toBe(afterOrg); + const jwtOrgAfter = extractOrgIdFromTokenPayload(payloadAfter); + expect(jwtOrgAfter, '切换后 JWT 应携带 current_org_id').toBeTruthy(); + expect(jwtOrgAfter).toBe(afterOrg); }); test('课程体系页加载成功且不进入列表错误态(机构维度接口可用)', async ({