fix: harden E2E auth validation and response guard#92
fix: harden E2E auth validation and response guard#92kraftbj wants to merge 1 commit intotest-coverage-auditfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR tightens the Playwright E2E authentication helpers and makes the subscriber access-control test more resilient to navigation/response quirks, aiming to reduce flakiness and stale .auth/ state failures.
Changes:
- Update
ensureAdminAuth()to validate an existingstorageStateby attempting to load/wp-admin/, and re-authenticate if redirected to login. - Guard against
page.goto()returningnullin the subscriber access test and broaden acceptable HTTP statuses to include200(wp_die error page).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| tests/e2e/utils/auth.js | Validates persisted admin auth state by navigating to /wp-admin/ and regenerates state if stale. |
| tests/e2e/auth-access.spec.js | Avoids crashing on null navigation responses and accepts additional denial patterns. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const context = await browser.newContext( { | ||
| storageState: ADMIN_AUTH_FILE, | ||
| } ); | ||
| const page = await context.newPage(); | ||
| await page.goto( '/wp-admin/' ); | ||
| const url = page.url(); | ||
| await context.close(); | ||
|
|
||
| if ( ! url.includes( 'wp-login.php' ) ) { | ||
| return; | ||
| } | ||
|
|
||
| // Stored state is stale — remove and re-login below. | ||
| fs.unlinkSync( ADMIN_AUTH_FILE ); |
There was a problem hiding this comment.
ensureAdminAuth() creates a new context/page and then closes it only on the success path. If page.goto('/wp-admin/') throws (timeout/server not ready) the context won’t be closed, which can leak resources and make subsequent E2E tests behave unpredictably. Wrap the navigation/url check in a try/finally that always closes the context, and consider treating navigation failures as a signal to delete the stored state and re-login (instead of aborting with a partially-used context).
| const context = await browser.newContext( { | |
| storageState: ADMIN_AUTH_FILE, | |
| } ); | |
| const page = await context.newPage(); | |
| await page.goto( '/wp-admin/' ); | |
| const url = page.url(); | |
| await context.close(); | |
| if ( ! url.includes( 'wp-login.php' ) ) { | |
| return; | |
| } | |
| // Stored state is stale — remove and re-login below. | |
| fs.unlinkSync( ADMIN_AUTH_FILE ); | |
| let shouldRefreshState = false; | |
| const context = await browser.newContext( { | |
| storageState: ADMIN_AUTH_FILE, | |
| } ); | |
| const page = await context.newPage(); | |
| try { | |
| await page.goto( '/wp-admin/' ); | |
| const url = page.url(); | |
| if ( ! url.includes( 'wp-login.php' ) ) { | |
| // Auth state is valid; nothing else to do. | |
| return; | |
| } | |
| // Redirected to login — stored state is stale. | |
| shouldRefreshState = true; | |
| } catch { | |
| // Navigation failed — treat stored state as invalid. | |
| shouldRefreshState = true; | |
| } finally { | |
| await context.close(); | |
| } | |
| if ( shouldRefreshState && fs.existsSync( ADMIN_AUTH_FILE ) ) { | |
| // Stored state is stale — remove and re-login below. | |
| fs.unlinkSync( ADMIN_AUTH_FILE ); | |
| } |
| if ( response ) { | ||
| // WordPress typically returns 403 or redirects for unauthorized access. | ||
| // It may also return 200 with a wp_die() error page. | ||
| expect( [ 200, 302, 403 ] ).toContain( response.status() ); | ||
| } | ||
|
|
There was a problem hiding this comment.
If page.goto() returns null, this test now skips the HTTP status assertion entirely, which can allow false positives (e.g., navigation didn’t actually hit WordPress but #press-this-app is still not visible). Instead of silently skipping, explicitly assert a non-null response (with a helpful message) or add an alternate assertion for the null case (e.g., based on page.url() / page content) so the test still validates access control deterministically.
| if ( response ) { | |
| // WordPress typically returns 403 or redirects for unauthorized access. | |
| // It may also return 200 with a wp_die() error page. | |
| expect( [ 200, 302, 403 ] ).toContain( response.status() ); | |
| } | |
| // Ensure navigation produced an HTTP response so access control is actually tested. | |
| expect( | |
| response, | |
| 'Navigation to /wp-admin/press-this.php returned null; ensure WordPress is reachable and the route is served over HTTP.' | |
| ).not.toBeNull(); | |
| // WordPress typically returns 403 or redirects for unauthorized access. | |
| // It may also return 200 with a wp_die() error page. | |
| expect( [ 200, 302, 403 ] ).toContain( response.status() ); |
Summary
Addresses remaining Copilot review feedback on #88:
ensureAdminAuth()now validates stored auth by navigating to/wp-admin/instead of only checking file existence. Deletes stale state and re-authenticates when needed.page.goto()returningnulland accepts HTTP 200 (WordPresswp_die()error page) alongside 302/403.Test plan
npm run test:e2epasses with fresh.auth/statenpm run test:e2epasses after deleting wp-env containers (stale auth scenario)