Fix course-package:610 flaky — use expect.poll for post-save API check

Replace fixed waitForTimeout(2000) with expect.poll({ timeout: 15_000 })
so the API verification retries until the created package and its item
association are queryable. CI is slower than local, causing false failures.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-01 22:09:38 +08:00
parent 92cce7612e
commit a2b7825188
2 changed files with 30 additions and 24 deletions
+8 -4
View File
@@ -42,19 +42,23 @@ npx playwright test --ui
### 等待 CI 完成 ### 等待 CI 完成
使用 `gh run watch` 在后台检测,不要轮询 使用 Monitor 工具配合 `gh run watch`CI 完成后会自动通知
```bash ```bash
# 获取最新 CI run ID # 获取最新 CI run ID
gh run list --repo alchemy-studio/huike-e2e-moicen --limit 1 --json databaseId --jq '.[0].databaseId' gh run list --repo alchemy-studio/huike-e2e-moicen --limit 1 --json databaseId --jq '.[0].databaseId'
# 后台 watch(用 & 或 run_in_background),完成后会自动通知 # Monitor 会自动通知每次状态刷新和最终完成
gh run watch <run-id> --repo alchemy-studio/huike-e2e-moicen 2>&1 & Monitor:
command: gh run watch <run-id> --repo alchemy-studio/huike-e2e-moicen 2>&1 | grep -E --line-buffered '(✓ master|✘ master| master|X master)|Process completed'
description: Watch CI run completion
``` ```
注意:**不要用 `Bash run_in_background` + `gh run watch`**,因为 Bash 后台任务不会在完成时通知,你会卡住等不到结果。必须用 **Monitor** 工具,它会在每行 stdout 输出时通知你。
也可以用 `gh run watch --job=<job-id>` 监控特定 job。 也可以用 `gh run watch --job=<job-id>` 监控特定 job。
注意:`gh run watch` 依赖当前目录的 git remote 确定仓库,所以要在 `huike-e2e-moicen` 目录下跑,或者用 `--repo` 参数显式指定。 `gh run watch` 依赖当前目录的 git remote 确定仓库,所以要在 `huike-e2e-moicen` 目录下跑,或者用 `--repo` 参数显式指定。
### 两个 Workflow ### 两个 Workflow
+22 -20
View File
@@ -708,27 +708,29 @@ test.describe('音乐教室端(huike-front)课包 UI', () => {
await page.getByText('保存').click(); await page.getByText('保存').click();
// ---- Verify via API that package was created with this group ---- // ---- Verify via API that package was created with this group ----
await page.waitForTimeout(2000); // Use poll to retry — save may not be immediately reflected on CI
const myRes = await request.get( await expect(async () => {
`${kcBase}/api/v1/clazz/course-package/my-packages?page=1&page_size=50`, const myRes = await request.get(
{ headers }, `${kcBase}/api/v1/clazz/course-package/my-packages?page=1&page_size=50`,
); { headers },
expect(myRes.ok()).toBeTruthy(); );
const myBody = await myRes.json(); expect(myRes.ok()).toBeTruthy();
const myList: any[] = myBody.d?.[0] ?? []; const myBody = await myRes.json();
const newPkg = myList.find((p: any) => p.package_name === pkgName); const myList: any[] = myBody.d?.[0] ?? [];
expect(newPkg, '创建的课包应出现在我的课包列表中').toBeTruthy(); const newPkg = myList.find((p: any) => p.package_name === pkgName);
createdPkgId = newPkg.id; expect(newPkg, '创建的课包应出现在我的课包列表中').toBeTruthy();
createdPkgId = newPkg!.id;
const itemsRes = await request.get( const itemsRes = await request.get(
`${kcBase}/api/v1/clazz/course-package/item/list/${createdPkgId}`, `${kcBase}/api/v1/clazz/course-package/item/list/${createdPkgId}`,
{ headers }, { headers },
); );
expect(itemsRes.ok()).toBeTruthy(); expect(itemsRes.ok()).toBeTruthy();
const itemsBody = await itemsRes.json(); const itemsBody = await itemsRes.json();
const items: any[] = itemsBody.d ?? []; const items: any[] = itemsBody.d ?? [];
const matched = items.some((g: any) => g.course_group_id === groupId); const matched = items.some((g: any) => g.course_group_id === groupId);
expect(matched, '保存后课包应包含选中的课节分组').toBe(true); expect(matched, '保存后课包应包含选中的课节分组').toBe(true);
}).toPass({ timeout: 15_000 });
} finally { } finally {
// ---- Cleanup ---- // ---- Cleanup ----
if (createdPkgId) { if (createdPkgId) {