diff --git a/.github/workflows/e2e-tests-split.yml b/.github/workflows/e2e-tests-split.yml index 485fd56dd..9e0d7f1ab 100644 --- a/.github/workflows/e2e-tests-split.yml +++ b/.github/workflows/e2e-tests-split.yml @@ -294,7 +294,10 @@ jobs: docker images | grep charon - name: Generate ephemeral encryption key - run: echo "CHARON_ENCRYPTION_KEY=$(openssl rand -base64 32)" >> "$GITHUB_ENV" + run: | + key="$(openssl rand -base64 32)" + echo "::add-mask::${key}" + echo "CHARON_ENCRYPTION_KEY=${key}" >> "$GITHUB_ENV" - name: Start test environment (Security Tests Profile) run: | @@ -335,17 +338,17 @@ jobs: run: | set -uo pipefail echo "📦 Installing Chromium..." - for attempt in 1 2 3; do - timeout 10m npx playwright install --with-deps chromium && break - if [ "$attempt" -lt 3 ]; then - echo "Attempt ${attempt}/3 failed; retrying in 15s..." >&2 + for attempt in 1 2 3 4 5; do + timeout 3m npx playwright install --with-deps chromium && break + if [ "$attempt" -lt 5 ]; then + echo "Attempt ${attempt}/5 failed; retrying in 15s..." >&2 echo "Cleaning up any orphaned apt/dpkg processes and stale locks..." >&2 sudo pkill -9 -f '(^|/)(apt|apt-get|dpkg)( |$)' 2>/dev/null || true sudo rm -f /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock /var/cache/apt/archives/lock 2>/dev/null || true sudo dpkg --configure -a > /dev/null 2>&1 || true sleep 15 else - echo "ERROR: Playwright chromium install failed after 3 attempts" >&2 + echo "ERROR: Playwright chromium install failed after 5 attempts" >&2 exit 1 fi done @@ -548,7 +551,10 @@ jobs: docker images | grep charon - name: Generate ephemeral encryption key - run: echo "CHARON_ENCRYPTION_KEY=$(openssl rand -base64 32)" >> "$GITHUB_ENV" + run: | + key="$(openssl rand -base64 32)" + echo "::add-mask::${key}" + echo "CHARON_ENCRYPTION_KEY=${key}" >> "$GITHUB_ENV" - name: Start test environment (Security Tests Profile) run: | @@ -589,17 +595,17 @@ jobs: run: | set -uo pipefail echo "📦 Installing Chromium (required by security-tests dependency)..." - for attempt in 1 2 3; do - timeout 10m npx playwright install --with-deps chromium && break - if [ "$attempt" -lt 3 ]; then - echo "Attempt ${attempt}/3 failed; retrying in 15s..." >&2 + for attempt in 1 2 3 4 5; do + timeout 3m npx playwright install --with-deps chromium && break + if [ "$attempt" -lt 5 ]; then + echo "Attempt ${attempt}/5 failed; retrying in 15s..." >&2 echo "Cleaning up any orphaned apt/dpkg processes and stale locks..." >&2 sudo pkill -9 -f '(^|/)(apt|apt-get|dpkg)( |$)' 2>/dev/null || true sudo rm -f /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock /var/cache/apt/archives/lock 2>/dev/null || true sudo dpkg --configure -a > /dev/null 2>&1 || true sleep 15 else - echo "ERROR: Playwright chromium install failed after 3 attempts" >&2 + echo "ERROR: Playwright chromium install failed after 5 attempts" >&2 exit 1 fi done @@ -608,17 +614,17 @@ jobs: run: | set -uo pipefail echo "📦 Installing Firefox..." - for attempt in 1 2 3; do - timeout 10m npx playwright install --with-deps firefox && break - if [ "$attempt" -lt 3 ]; then - echo "Attempt ${attempt}/3 failed; retrying in 15s..." >&2 + for attempt in 1 2 3 4 5; do + timeout 3m npx playwright install --with-deps firefox && break + if [ "$attempt" -lt 5 ]; then + echo "Attempt ${attempt}/5 failed; retrying in 15s..." >&2 echo "Cleaning up any orphaned apt/dpkg processes and stale locks..." >&2 sudo pkill -9 -f '(^|/)(apt|apt-get|dpkg)( |$)' 2>/dev/null || true sudo rm -f /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock /var/cache/apt/archives/lock 2>/dev/null || true sudo dpkg --configure -a > /dev/null 2>&1 || true sleep 15 else - echo "ERROR: Playwright firefox install failed after 3 attempts" >&2 + echo "ERROR: Playwright firefox install failed after 5 attempts" >&2 exit 1 fi done @@ -824,7 +830,10 @@ jobs: docker images | grep charon - name: Generate ephemeral encryption key - run: echo "CHARON_ENCRYPTION_KEY=$(openssl rand -base64 32)" >> "$GITHUB_ENV" + run: | + key="$(openssl rand -base64 32)" + echo "::add-mask::${key}" + echo "CHARON_ENCRYPTION_KEY=${key}" >> "$GITHUB_ENV" - name: Start test environment (Security Tests Profile) run: | @@ -865,17 +874,17 @@ jobs: run: | set -uo pipefail echo "📦 Installing Chromium (required by security-tests dependency)..." - for attempt in 1 2 3; do - timeout 10m npx playwright install --with-deps chromium && break - if [ "$attempt" -lt 3 ]; then - echo "Attempt ${attempt}/3 failed; retrying in 15s..." >&2 + for attempt in 1 2 3 4 5; do + timeout 3m npx playwright install --with-deps chromium && break + if [ "$attempt" -lt 5 ]; then + echo "Attempt ${attempt}/5 failed; retrying in 15s..." >&2 echo "Cleaning up any orphaned apt/dpkg processes and stale locks..." >&2 sudo pkill -9 -f '(^|/)(apt|apt-get|dpkg)( |$)' 2>/dev/null || true sudo rm -f /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock /var/cache/apt/archives/lock 2>/dev/null || true sudo dpkg --configure -a > /dev/null 2>&1 || true sleep 15 else - echo "ERROR: Playwright chromium install failed after 3 attempts" >&2 + echo "ERROR: Playwright chromium install failed after 5 attempts" >&2 exit 1 fi done @@ -884,6 +893,11 @@ jobs: run: | set -uo pipefail echo "📦 Installing WebKit..." + # WebKit's --with-deps pulls the full GStreamer/media-codec stack + # (~170 packages, ~90MB) that Chromium/Firefox don't need, so it + # needs a longer per-attempt budget than the 3m used for the + # lighter browsers below - 3m was observed killing healthy, + # non-stalled WebKit installs mid-download on a slow mirror. for attempt in 1 2 3; do timeout 10m npx playwright install --with-deps webkit && break if [ "$attempt" -lt 3 ]; then @@ -1127,7 +1141,10 @@ jobs: docker images | grep charon - name: Generate ephemeral encryption key - run: echo "CHARON_ENCRYPTION_KEY=$(openssl rand -base64 32)" >> "$GITHUB_ENV" + run: | + key="$(openssl rand -base64 32)" + echo "::add-mask::${key}" + echo "CHARON_ENCRYPTION_KEY=${key}" >> "$GITHUB_ENV" - name: Start test environment (Non-Security Profile) run: | @@ -1168,17 +1185,17 @@ jobs: run: | set -uo pipefail echo "📦 Installing Chromium..." - for attempt in 1 2 3; do - timeout 10m npx playwright install --with-deps chromium && break - if [ "$attempt" -lt 3 ]; then - echo "Attempt ${attempt}/3 failed; retrying in 15s..." >&2 + for attempt in 1 2 3 4 5; do + timeout 3m npx playwright install --with-deps chromium && break + if [ "$attempt" -lt 5 ]; then + echo "Attempt ${attempt}/5 failed; retrying in 15s..." >&2 echo "Cleaning up any orphaned apt/dpkg processes and stale locks..." >&2 sudo pkill -9 -f '(^|/)(apt|apt-get|dpkg)( |$)' 2>/dev/null || true sudo rm -f /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock /var/cache/apt/archives/lock 2>/dev/null || true sudo dpkg --configure -a > /dev/null 2>&1 || true sleep 15 else - echo "ERROR: Playwright chromium install failed after 3 attempts" >&2 + echo "ERROR: Playwright chromium install failed after 5 attempts" >&2 exit 1 fi done @@ -1384,7 +1401,10 @@ jobs: docker images | grep charon - name: Generate ephemeral encryption key - run: echo "CHARON_ENCRYPTION_KEY=$(openssl rand -base64 32)" >> "$GITHUB_ENV" + run: | + key="$(openssl rand -base64 32)" + echo "::add-mask::${key}" + echo "CHARON_ENCRYPTION_KEY=${key}" >> "$GITHUB_ENV" - name: Start test environment (Non-Security Profile) run: | @@ -1425,17 +1445,17 @@ jobs: run: | set -uo pipefail echo "📦 Installing Chromium (required by security-tests dependency)..." - for attempt in 1 2 3; do - timeout 10m npx playwright install --with-deps chromium && break - if [ "$attempt" -lt 3 ]; then - echo "Attempt ${attempt}/3 failed; retrying in 15s..." >&2 + for attempt in 1 2 3 4 5; do + timeout 3m npx playwright install --with-deps chromium && break + if [ "$attempt" -lt 5 ]; then + echo "Attempt ${attempt}/5 failed; retrying in 15s..." >&2 echo "Cleaning up any orphaned apt/dpkg processes and stale locks..." >&2 sudo pkill -9 -f '(^|/)(apt|apt-get|dpkg)( |$)' 2>/dev/null || true sudo rm -f /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock /var/cache/apt/archives/lock 2>/dev/null || true sudo dpkg --configure -a > /dev/null 2>&1 || true sleep 15 else - echo "ERROR: Playwright chromium install failed after 3 attempts" >&2 + echo "ERROR: Playwright chromium install failed after 5 attempts" >&2 exit 1 fi done @@ -1444,17 +1464,17 @@ jobs: run: | set -uo pipefail echo "📦 Installing Firefox..." - for attempt in 1 2 3; do - timeout 10m npx playwright install --with-deps firefox && break - if [ "$attempt" -lt 3 ]; then - echo "Attempt ${attempt}/3 failed; retrying in 15s..." >&2 + for attempt in 1 2 3 4 5; do + timeout 3m npx playwright install --with-deps firefox && break + if [ "$attempt" -lt 5 ]; then + echo "Attempt ${attempt}/5 failed; retrying in 15s..." >&2 echo "Cleaning up any orphaned apt/dpkg processes and stale locks..." >&2 sudo pkill -9 -f '(^|/)(apt|apt-get|dpkg)( |$)' 2>/dev/null || true sudo rm -f /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock /var/cache/apt/archives/lock 2>/dev/null || true sudo dpkg --configure -a > /dev/null 2>&1 || true sleep 15 else - echo "ERROR: Playwright firefox install failed after 3 attempts" >&2 + echo "ERROR: Playwright firefox install failed after 5 attempts" >&2 exit 1 fi done @@ -1660,7 +1680,10 @@ jobs: docker images | grep charon - name: Generate ephemeral encryption key - run: echo "CHARON_ENCRYPTION_KEY=$(openssl rand -base64 32)" >> "$GITHUB_ENV" + run: | + key="$(openssl rand -base64 32)" + echo "::add-mask::${key}" + echo "CHARON_ENCRYPTION_KEY=${key}" >> "$GITHUB_ENV" - name: Start test environment (Non-Security Profile) run: | @@ -1701,17 +1724,17 @@ jobs: run: | set -uo pipefail echo "📦 Installing Chromium (required by security-tests dependency)..." - for attempt in 1 2 3; do - timeout 10m npx playwright install --with-deps chromium && break - if [ "$attempt" -lt 3 ]; then - echo "Attempt ${attempt}/3 failed; retrying in 15s..." >&2 + for attempt in 1 2 3 4 5; do + timeout 3m npx playwright install --with-deps chromium && break + if [ "$attempt" -lt 5 ]; then + echo "Attempt ${attempt}/5 failed; retrying in 15s..." >&2 echo "Cleaning up any orphaned apt/dpkg processes and stale locks..." >&2 sudo pkill -9 -f '(^|/)(apt|apt-get|dpkg)( |$)' 2>/dev/null || true sudo rm -f /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock /var/cache/apt/archives/lock 2>/dev/null || true sudo dpkg --configure -a > /dev/null 2>&1 || true sleep 15 else - echo "ERROR: Playwright chromium install failed after 3 attempts" >&2 + echo "ERROR: Playwright chromium install failed after 5 attempts" >&2 exit 1 fi done @@ -1720,6 +1743,11 @@ jobs: run: | set -uo pipefail echo "📦 Installing WebKit..." + # WebKit's --with-deps pulls the full GStreamer/media-codec stack + # (~170 packages, ~90MB) that Chromium/Firefox don't need, so it + # needs a longer per-attempt budget than the 3m used for the + # lighter browsers below - 3m was observed killing healthy, + # non-stalled WebKit installs mid-download on a slow mirror. for attempt in 1 2 3; do timeout 10m npx playwright install --with-deps webkit && break if [ "$attempt" -lt 3 ]; then diff --git a/tests/settings/user-lifecycle.spec.ts b/tests/settings/user-lifecycle.spec.ts index 3fdbc1f4e..97856b098 100644 --- a/tests/settings/user-lifecycle.spec.ts +++ b/tests/settings/user-lifecycle.spec.ts @@ -189,29 +189,48 @@ async function createUserViaApi( } async function navigateToLogin(page: import('@playwright/test').Page): Promise { - await gotoTolerant(page, '/login'); - - await page.waitForURL(/\/login/, { timeout: 15000 }).catch(() => undefined); const emailInput = page.locator('input[type="email"]').or(page.getByLabel(/email/i)).first(); + const maxAttempts = 3; - if (!(await emailInput.isVisible().catch(() => false))) { - await page.context().clearCookies(); - await page.evaluate(() => { - localStorage.clear(); - sessionStorage.clear(); - }); - // Use reload(), not a second goto('/login'): the page is already on - // /login from the goto() above and may still be mid-hydration. A - // same-URL goto() fired this soon after can race that still-settling - // navigation and never produce a Playwright-trackable event in Firefox. - // reload() always yields a fresh, distinct navigation-commit event, but - // under a slow/contended CI runner it can itself fail to settle in time - // (observed: 3/3 attempts hitting the full 60s test timeout), so it needs - // the same tolerant handling as the goto() above. - await reloadTolerant(page); - } + // Up to 3 attempts: an initial goto, then up to 2 reload-based recovery + // passes. One recovery attempt is not always enough - gotoTolerant's/ + // reloadTolerant's 'domcontentloaded' fix (see their doc comments and + // docs/reports/qa_report_2026-07-26_shard4-reload-hang.md) reduces but + // does not eliminate Firefox's failure to produce a Playwright-trackable + // navigation-commit event, which leaves the next auto-waiting locator + // call hung on "waiting for navigation to finish" for its own full + // timeout even though the page already rendered correctly (reproduced: + // both the goto and the one prior reload attempt hit this race in the + // same run). Short per-attempt timeouts on the non-final passes let a + // stuck navigation fail fast into another reload instead of paying out + // a full 15s hang more than once before giving up for good. + for (let attempt = 1; attempt <= maxAttempts; attempt += 1) { + if (attempt === 1) { + await gotoTolerant(page, '/login'); + } else { + await page.context().clearCookies(); + await page.evaluate(() => { + localStorage.clear(); + sessionStorage.clear(); + }); + await reloadTolerant(page); + } - await expect(emailInput).toBeVisible({ timeout: 15000 }); + await page.waitForURL(/\/login/, { timeout: 15000 }).catch(() => undefined); + + if (attempt === maxAttempts) { + await expect(emailInput).toBeVisible({ timeout: 15000 }); + return; + } + + const visible = await expect(emailInput) + .toBeVisible({ timeout: 5000 }) + .then(() => true) + .catch(() => false); + if (visible) { + return; + } + } } async function loginWithCredentials(