惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
V2EX
J
Java Code Geeks
月光博客
月光博客
博客园_首页
The GitHub Blog
The GitHub Blog
Vercel News
Vercel News
B
Blog RSS Feed
博客园 - 聂微东
宝玉的分享
宝玉的分享
T
Tailwind CSS Blog
Jina AI
Jina AI
S
SegmentFault 最新的问题
B
Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
有赞技术团队
有赞技术团队
Hugging Face - Blog
Hugging Face - Blog
Google DeepMind News
Google DeepMind News
阮一峰的网络日志
阮一峰的网络日志
The Cloudflare Blog
量子位
Martin Fowler
Martin Fowler
博客园 - Franky
大猫的无限游戏
大猫的无限游戏
博客园 - 叶小钗

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Playwright versus WordPress's "admin email confirmation" ...
Susumu Takahashi · 2026-06-24 · via DEV Community

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:

  1. Confirmation screen present → click happens: With a mock returning the confirmation HTML, the click() call fires
  2. Confirmation screen absent → click does NOT happen: With normal dashboard HTML, the click is suppressed (no false positive)
  3. 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.