E2E: longer waits for slow fixture renders, shorter Playwright artifact retention (#174)

* fix(mobile): align Expo SDK 57 patch deps for install --check

Bump @expo/ui, expo, expo-constants, expo-linking, expo-modules-core,
expo-notifications, expo-router, and expo-updates to the versions
expo install --check expects after upstream patch releases.

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* ci(e2e): harden flaky waits and trim Playwright artifact uploads

Chart fixture reloads and shell readiness can exceed 30s on loaded
self-hosted runners. Align timeouts with the rest of the suite and wait
for gallery mount before counting species.

Stop uploading per-test trace folders to Actions (HTML report only) with
1-day retention so quota errors stay non-blocking.

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>

* evo-deploy: probe health before checking the timeout

With a short timeout the clock could tick past it before the first
probe, so a healthy service was reported as timed out. Probe first,
then check the clock.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy1md9QE1ayJbDCwJ4vAQs

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
cynarAI 2026-10-09 10:56:32 +02:00 committed by GitHub
parent b1b76a846e
commit 6c0fba8cc1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 17 additions and 13 deletions

View File

@ -165,15 +165,14 @@ jobs:
- name: Upload Playwright artifacts
if: always() && inputs.upload_artifacts
continue-on-error: true
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: playwright-artifacts-${{ github.run_id }}-${{ github.run_attempt }}
path: |
playwright-report
playwright-report-www
apps/web/test-results
test-report/e2e
retention-days: 7
retention-days: 1
if-no-files-found: warn
- name: Publish Playwright visual report
if: always() && inputs.publish_report

View File

@ -21,7 +21,7 @@ test.describe
await page.goto(`${fixture}?${new URLSearchParams({ href, inspect })}`);
const circle = page.locator("svg circle").first();
// Every navigation renders Plot again, which takes seconds on a loaded CI runner.
await expect(circle).toBeVisible({ timeout: 30_000 });
await expect(circle).toBeVisible({ timeout: 90_000 });
const assertInert = async () => {
const links = page.locator("svg a");
expect(await links.count()).toBeGreaterThan(0);
@ -43,7 +43,7 @@ test.describe
await assertInert();
await page.getByRole("button", { name: "Expand", exact: true }).click();
const dialog = page.getByRole("dialog");
await expect(dialog.locator("svg circle")).toBeVisible({ timeout: 30_000 });
await expect(dialog.locator("svg circle")).toBeVisible({ timeout: 90_000 });
await dialog.locator("svg circle").click();
await assertInert();
await expect(page).toHaveURL(`${fixture}?${new URLSearchParams({ href, inspect })}`);
@ -62,7 +62,7 @@ test.describe
route.fulfill({ body: "Chart destination" }),
);
await page.goto(`${fixture}?${new URLSearchParams({ href: "#details" })}`);
await expect(page.locator("svg circle").first()).toBeVisible({ timeout: 30_000 });
await expect(page.locator("svg circle").first()).toBeVisible({ timeout: 90_000 });
for (const href of [
"https://example.test/chart?a=1&b=2",
"mailto:chart@example.test",
@ -73,7 +73,7 @@ test.describe
"java%73cript:void(0)",
]) {
await page.goto(`${fixture}?${new URLSearchParams({ href })}`);
await expect(page.locator("svg circle").first()).toBeVisible({ timeout: 30_000 });
await expect(page.locator("svg circle").first()).toBeVisible({ timeout: 90_000 });
const link = page.locator("svg a").first();
await expect(link).toBeAttached();
expect(

View File

@ -337,7 +337,7 @@ export async function openUserSettings(
/** Wait until the shell thread is loaded and the message composer is interactive. */
export async function waitForChatComposer(page: Page) {
await expect(page.getByTestId("shell-root")).toHaveAttribute("data-ready", "true", {
timeout: 30_000,
timeout: 60_000,
});
await expect(page.getByRole("combobox", { name: /^Message$|^Nachricht$/i })).toBeVisible();
}

View File

@ -5,6 +5,10 @@ for (const theme of ["light", "dark"] as const) {
test(`Oo looks render every species with the Oo eyes (${theme})`, async ({ page }, testInfo) => {
await page.goto(`/e2e/fixtures/oo-gallery.html?theme=${theme}`);
const gallery = page.getByTestId("oo-gallery");
await expect(gallery).toBeVisible({ timeout: 60_000 });
await expect(gallery.locator("svg[data-oo-species]").first()).toBeVisible({
timeout: 90_000,
});
await expect(gallery.locator("svg[data-oo-species]")).toHaveCount(12);
for (const species of ["cat", "dog", "bunny", "bear", "robot", "ghost", "frog", "penguin"]) {
await expect(gallery.locator(`svg[data-oo-species="${species}"]`).first()).toBeVisible();

View File

@ -291,17 +291,18 @@ wait_for_url() {
log "[dry-run] would wait for $label at $url"
return 0
fi
# Probe before checking the clock so a slow host still gets at least one attempt.
while true; do
now="$(date +%s)"
if (( now - start >= timeout )); then
log "ERROR: $label health check timed out after ${timeout}s (last: ${last:-unknown})"
return 1
fi
if curl -fsS --max-time 5 "$url" >/dev/null 2>&1; then
log "$label healthy"
return 0
fi
last="$(curl -sS -o /dev/null -w '%{http_code}' --max-time 5 "$url" 2>/dev/null || echo unreachable)"
now="$(date +%s)"
if (( now - start >= timeout )); then
log "ERROR: $label health check timed out after ${timeout}s (last: ${last:-unknown})"
return 1
fi
sleep 2
done
}