If you drive the WordPress admin via Playwright for long enough, one day a screen you've never seen before will appear after login, and everything downstream stops working.
Is admin@example.com still the correct admin email address?
[ Yes, the email is correct ]
[ Change the address ]
That's WordPress's admin email confirmation screen. Roughly every six months, after the admin user logs in, this confirmation screen gets injected — standard behavior since WP 4.9. A human just clicks once. An automation script can't see it without explicit handling.
Why automation gets stuck
A straightforward Playwright login looks like:
page.fill('#user_login', user)
page.fill('#user_pass', pwd)
page.click('input[type="submit"]')
page.wait_for_load_state('domcontentloaded')
# Assumes we're on the dashboard
page.goto('/wp-admin/plugins.php')
But on a "confirmation day," the URL right after wait_for_load_state is something like /wp-admin/profile.php?...action=confirm_admin_email... — the confirmation screen. You thought you were navigating to the plugins page, but the DOM you expected isn't there. Subsequent selectors fail, and everything downstream cascades into failure.
A specific selector identifies the screen
WordPress's confirmation screen has a uniquely-named submit button:
<input type="submit"
name="correct-admin-email"
value="Yes, the email is correct" />
If input[name="correct-admin-email"] exists on the page, you're on the confirmation screen. The same selector serves as both the detection signal and the click target, so handling is only a few lines:
admin_email_confirm = page.locator(
'input[type="submit"][name="correct-admin-email"]'
)
if admin_email_confirm.count() > 0:
logger.info("Confirmation screen detected — clicking 'email is correct'")
admin_email_confirm.first.click()
page.wait_for_load_state('domcontentloaded', timeout=30000)
Insert this after the post-login wait_for_load_state and before subsequent navigation. It runs transparently whether the screen appears or not.
Handler omission via code duplication
Internally, we had four places running this same login handling:
- The main maintenance login flow ✓
- The login used by
visual_check(pre/post screenshot capture) ✓ - The login used for thumbnail capture ✓
- The login used by browser-based residual update (the path that handles plugins with proprietary updaters) ❌
The last one — added later — forgot to copy the confirmation-screen handler from the existing three. The downstream effect: when the confirmation screen showed up, automated updates for ACF Pro / Yoast SEO Premium / WP Rocket / Elementor Pro could be missed entirely.
A similar structural pattern appeared in our seven-format SSH private-key compat loader work: when the same logic lives in multiple copies, additions to one copy tend to drop functionality from others. The realistic mitigations are either deduplicating after the fact, or — at minimum — running tests that exercise the same behavior across every duplicate.
Regression-proofing through tests
Three tests went in with the fix:
-
Confirmation screen present → click happens: With a mock returning the confirmation HTML, the
click()call fires - Confirmation screen absent → click does NOT happen: With normal dashboard HTML, the click is suppressed (no false positive)
- Click happens but auth still fails → still treated as a login failure: If the confirmation gets clicked but the dashboard never loads, the function does not incorrectly report success
Tests 2 and 3 specifically guard against "the new handler introducing a different kind of misbehavior." Negative tests that run alongside a feature addition are quietly effective at preventing regressions.
Takeaway — WordPress's "six-month trap"
Long-running Playwright operations against WordPress admin keep encountering screens you don't see during implementation but that appear half a year later and break everything. The admin-email confirmation screen is the canonical example: it's invisible during testing on a Monday, but on the day it appears, the whole flow falls over.
Two practices keep WordPress + Playwright automation stable for the long haul: write the confirmation screen into the code explicitly, and if your login code is duplicated across multiple call sites, make sure the same handling is in every one.
For further actions, you may consider blocking this person and/or reporting abuse
