Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 74 additions & 46 deletions .github/workflows/e2e-tests-split.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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: |
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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: |
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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: |
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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: |
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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: |
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
59 changes: 39 additions & 20 deletions tests/settings/user-lifecycle.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { test, expect, loginUser, logoutUser, TEST_PASSWORD } from '../fixtures/auth-fixtures';
import { waitForLoadingComplete, gotoTolerant, reloadTolerant } from '../utils/wait-helpers';
import { suppressChangelogModal } from '../utils/api-helpers';
Expand Down Expand Up @@ -189,29 +189,48 @@
}

async function navigateToLogin(page: import('@playwright/test').Page): Promise<void> {
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(
Expand Down Expand Up @@ -498,7 +517,7 @@
expect(usersAccessResponse.status()).toBe(200);
await page.goto('/users', { waitUntil: 'domcontentloaded' });
await waitForLoadingComplete(page, { timeout: 15000 });
await page.reload({ waitUntil: 'domcontentloaded' });

Check failure on line 520 in tests/settings/user-lifecycle.spec.ts

View workflow job for this annotation

GitHub Actions / E2E WebKit (Shard 4/4)

[webkit] › tests/settings/user-lifecycle.spec.ts:482:3 › Admin-User E2E Workflow › Role change takes effect immediately on user refresh

1) [webkit] › tests/settings/user-lifecycle.spec.ts:482:3 › Admin-User E2E Workflow › Role change takes effect immediately on user refresh › User refreshes page and sees new permissions Error: page.reload: WebKit encountered an internal error Call log: - waiting for navigation until "domcontentloaded" 518 | await page.goto('/users', { waitUntil: 'domcontentloaded' }); 519 | await waitForLoadingComplete(page, { timeout: 15000 }); > 520 | await page.reload({ waitUntil: 'domcontentloaded' }); | ^ 521 | await waitForLoadingComplete(page, { timeout: 15000 }); 522 | const usersAccessAfterReload = await page.request.get('/api/v1/users', { 523 | headers: buildAuthHeaders(token), at /home/runner/work/Charon/Charon/tests/settings/user-lifecycle.spec.ts:520:18 at /home/runner/work/Charon/Charon/tests/settings/user-lifecycle.spec.ts:510:5
await waitForLoadingComplete(page, { timeout: 15000 });
const usersAccessAfterReload = await page.request.get('/api/v1/users', {
headers: buildAuthHeaders(token),
Expand Down
Loading