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