Skip to content

fix: harden E2E auth validation and response guard#92

Open
kraftbj wants to merge 1 commit intotest-coverage-auditfrom
test-coverage-audit-fixes
Open

fix: harden E2E auth validation and response guard#92
kraftbj wants to merge 1 commit intotest-coverage-auditfrom
test-coverage-audit-fixes

Conversation

@kraftbj
Copy link
Collaborator

@kraftbj kraftbj commented Mar 7, 2026

Summary

Addresses remaining Copilot review feedback on #88:

  • Stale auth state: ensureAdminAuth() now validates stored auth by navigating to /wp-admin/ instead of only checking file existence. Deletes stale state and re-authenticates when needed.
  • Null response guard: The subscriber access test now guards against page.goto() returning null and accepts HTTP 200 (WordPress wp_die() error page) alongside 302/403.

Test plan

  • npm run test:e2e passes with fresh .auth/ state
  • npm run test:e2e passes after deleting wp-env containers (stale auth scenario)

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 existing storageState by attempting to load /wp-admin/, and re-authenticate if redirected to login.
  • Guard against page.goto() returning null in the subscriber access test and broaden acceptable HTTP statuses to include 200 (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.

Comment on lines +36 to +49
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 );
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Suggested change
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 );
}

Copilot uses AI. Check for mistakes.
Comment on lines +47 to 52
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() );
}

Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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() );

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants