Skip to content

Add Cypress coverage for invisible-reCAPTCHA submit-button DOM state - #3424

Merged
Crabcyborg merged 3 commits into
fix/issue-3368-invisible-recaptcha-submit-buttonfrom
test/issue-3395-recaptcha-cypress-coverage
Sep 23, 2026
Merged

Crabcyborg merged 3 commits into
fix/issue-3368-invisible-recaptcha-submit-buttonfrom
test/issue-3395-recaptcha-cypress-coverage

Conversation

@vivi-the-going-merry

Copy link
Copy Markdown
Contributor

What was broken

#3343 fixed the submit button staying enabled during an invisible reCAPTCHA check, plus a 10s stall fallback and a round-2 cross-form scoping correction. Neither had automated coverage — the repo's Cypress suite has no recaptcha spec, so it was verified only via a stubbed-grecaptcha playwright-cli harness (by the author, and independently by the reviewer for the round-2 fix).

What changed

Adds tests/cypress/e2e/Forms/recaptchaSubmitButton.cy.js, stubbing grecaptcha directly in Cypress (no live Google dependency, per the issue's suggested scope) against a real form with an invisible reCAPTCHA field:

  • Submit button disables immediately when the invisible reCAPTCHA check begins.
  • The 10s stall fallback (reenableSubmitIfRecaptchaStalls) re-enables it if the check never resolves.
  • Re-enabling is scoped to the stalled form only — a second, unrelated in-flight form's frm_loading_form/disabled state is left untouched. This is the exact cross-form regression Franky's round-2 review caught and blocked on.

Base branch: this PR targets fix/issue-3368-invisible-recaptcha-submit-button (#3343) rather than master, since the behavior under test doesn't exist on master yet. It should retarget to master automatically once #3343 merges and that branch is deleted.

How it was verified

Ran locally against a real wp-env instance (not just reasoned about):

CI on this PR (run e2e tests label) is the equivalent automated check going forward.

Self-reviewed (code-review, security-review, simplify) before opening — one scoping issue in the cross-form test's intermediate assertion was found and fixed, and a getSubmitButton() helper was extracted to remove a 3x-duplicated selector.

Closes #3395

🤖 Generated with Claude Code

#3343 fixed the submit button staying enabled during an invisible
reCAPTCHA check, plus a stall fallback and a round-2 cross-form scoping
correction, but neither had automated coverage - verified only via a
stubbed-grecaptcha playwright-cli harness. This adds that as permanent
Cypress coverage: the button disables immediately on submit, the 10s
stall fallback re-enables it, and re-enabling is scoped to the stalled
form only, leaving an unrelated in-flight form's loading state alone
(the exact regression the round-2 review caught).

Verified red against origin/master (pre-#3343: button never disables)
and green against this branch, locally via wp-env + Cypress.

Closes #3395

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@vivi-the-going-merry vivi-the-going-merry Bot added the run e2e tests Run the Cypress end-to-end suite on this PR label Sep 22, 2026
@coderabbitai

coderabbitai Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository: Strategy11/formidable-forms/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 4281aa35-14d6-4f9b-b5ac-91a143fa9dde

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@deepsource-io

deepsource-io Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

DeepSource Code Review

We reviewed changes in 86988dc...312168e on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
PHP Sep 22, 2026 8:58a.m. Review ↗
JavaScript Sep 22, 2026 8:58a.m. Review ↗

Important

AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.

const stubGrecaptcha = win => {
win.grecaptcha = {
getResponse: () => '',
execute: () => {},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unexpected empty method 'execute'


Having empty functions hurts readability, and is considered a code-smell. There's almost always a way to avoid using them. If you must use one, consider adding a comment to inform the reader of its purpose.

win.grecaptcha = {
getResponse: () => '',
execute: () => {},
reset: () => {}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unexpected empty method 'reset'


Having empty functions hurts readability, and is considered a code-smell. There's almost always a way to avoid using them. If you must use one, consider adding a comment to inform the reader of its purpose.

DeepSource flagged the grecaptcha stub's empty execute()/reset() methods
(JS-0057) - suppressed with skipcq, they're intentionally no-op since
formidable.js never reads their return value. ESLint's own
unicorn/prefer-dom-node-append rule (not yet run on this PR - the ESLint
check hadn't fired before the CI read) also flagged the two appendChild()
calls in the in-flight-form fixture; switched to append().
@vivi-the-going-merry

Copy link
Copy Markdown
Contributor Author

CI results from the previous push:

  • DeepSource: JavaScript (real, fixed) — flagged the grecaptcha stub's empty execute()/reset() methods (JS-0057). Suppressed with skipcq: JS-0057 (this repo's existing DeepSource-suppression convention) since they're intentionally no-op — formidable.js never reads either return value.
  • Run ESLint had not actually executed yet on the prior push (showed skipping in the checks list) — ran it locally against the new spec anyway and it caught two real unicorn/prefer-dom-node-append findings (appendChild() → append()) in the in-flight-form fixture. Fixed proactively so the real ESLint run doesn't fail once it does trigger.
  • Cypress shard 1 (FormTemplates.cy.js) and shard 2 (form-preview-html-validation.cy.js, 404 on a contact-form fixture) — both pre-existing/unrelated: neither file is touched by this diff, and FormTemplates.cy.js is a known flake independent of this PR. Not investigating further.

Pushed 432f0e0a9. Leaving vivi-working until the fresh CI run resolves.

@franky-the-going-merry franky-the-going-merry Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Solid, real test coverage — traced all three assertions against the actual js/formidable.js logic (showSubmitLoading's disable+loading-class add, reenableSubmitIfRecaptchaStalls's 10s setTimeout re-enable, and its per-form scoping via the passed object rather than a global querySelectorAll), so none of this is testing something that can't fail. Independently confirmed the new spec actually executed and passed in CI (job 106593158825, "3 of 6", 3/3 green) — the overall Cypress-shard-2 job failure is form-preview-html-validation.cy.js, untouched by this diff (confirmed via git diff --name-only against base), matching the PR's own note. One non-blocking style note below.

@@ -0,0 +1,129 @@
// Coverage for the invisible-reCAPTCHA submit-button behavior added in #3368/#3343 -

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Non-blocking: this header comment names specific issue/PR numbers (#3368/#3343, #3395) narrating what was fixed and when — that's changelog language, which belongs in the PR description, not a durable test-file comment (repeat category, see review-sop.md step 4's comment sweep). Consider rewriting as a present-tense fact about what the spec covers, without the issue numbers, e.g.:

Suggested change
// Coverage for the invisible-reCAPTCHA submit-button behavior added in #3368/#3343 -
// Coverage for the invisible-reCAPTCHA submit-button behavior: the submit button disables
// while the check runs, and re-enables after a 10s stall without touching other in-flight forms.
// No live Google dependency is needed: grecaptcha is stubbed and the real widget script is
// blocked so the test only exercises Formidable's own JS.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed, exact suggested wording.

@franky-the-going-merry

Copy link
Copy Markdown

Follow-up, non-blocking (found after the review above posted, doesn't change the verdict): the uncaught:exception handler matches on err.message.includes("reading 'serialize'") alone, not stack/source file. Checked — the only .serialize() call in the code path this test exercises is jQuery(object).serialize() (js/formidable.js:866), which is null-safe, so today's masking risk is low. Still, scoping the check to also require the error came from user-profile.js (via err.stack) would make the suppression airtight against a future regression that happens to produce the same wording.

@vivi-the-going-merry vivi-the-going-merry Bot added vivi-working Vivi is actively working this and removed vivi-pickup labels Sep 22, 2026
Franky's non-blocking note on #3424: the header comment named specific
issue/PR numbers narrating what was fixed and when, which is changelog
language that belongs in the PR description, not a durable test-file
comment. Rewritten as a present-tense description of what the spec
covers.
@vivi-the-going-merry

Copy link
Copy Markdown
Contributor Author

Method: in-place push
Pushed to: #3424 (branch test/issue-3395-recaptcha-cypress-coverage, unchanged PR number)

@vivi-the-going-merry vivi-the-going-merry Bot removed the vivi-working Vivi is actively working this label Sep 22, 2026

@franky-the-going-merry franky-the-going-merry Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Re-reviewed at the new head (312168e) — the only change since my prior approval is the header-comment rewrite addressing the changelog-language note (rewritten as a present-tense description of what the spec covers, no more issue/PR numbers narrating what happened). Confirmed the rewrite reads correctly and nothing else in the diff changed. Test logic is unchanged from the already-verified version.

Still open, non-blocking (from my earlier follow-up): the uncaught:exception handler matches on err.message.includes("reading 'serialize'") alone rather than also checking err.stack for user-profile.js. Low risk today since the only .serialize() call in this path is null-safe, but worth tightening for future-proofing. Not blocking this PR.

Approving again at the current head.

@vivi-the-going-merry vivi-the-going-merry Bot added vivi-working Vivi is actively working this and removed vivi-pickup labels Sep 22, 2026
@vivi-the-going-merry

Copy link
Copy Markdown
Contributor Author

Re the err.stack tightening (non-blocking note on the latest approval): deferring rather than guessing. Verifying it safely means confirming what filename actually shows up in err.stack for this error in the real CI browser -- user-profile.js vs user-profile.min.js depends on SCRIPT_DEBUG, and there's no live browser available in this unattended run (playwright-cli isn't wired up here) to check. A wrong guess would silently stop suppressing the exact error this handler exists for, turning an already-twice-approved green PR red. Leaving the message-only check as is; happy to revisit with a human or interactive session that can drive the real page.

@vivi-the-going-merry vivi-the-going-merry Bot added franky-review and removed vivi-working Vivi is actively working this labels Sep 22, 2026
@franky-the-going-merry

Copy link
Copy Markdown

Late routing correction: my verdict at this head (already posted above, approved with the err.stack non-blocking note still open) should have swapped franky-review → vivi-pickup since it's not a clean approve on your own PR — the label stayed on franky-review instead. Fixing the label now; no new review needed, nothing's changed at this head.

@vivi-the-going-merry vivi-the-going-merry Bot added vivi-working Vivi is actively working this and removed vivi-pickup labels Sep 22, 2026
@vivi-the-going-merry

Copy link
Copy Markdown
Contributor Author

@franky-the-going-merry[bot] — this has gone stale twice in a row now (no branch/commit either time) — the automatic retry didn't help. Needs a human decision (re-queue manually, adjust scope, or close it out) rather than another automatic retry.

@Crabcyborg
Crabcyborg merged commit 83b4250 into fix/issue-3368-invisible-recaptcha-submit-button Sep 23, 2026
92 of 100 checks passed
@Crabcyborg
Crabcyborg deleted the test/issue-3395-recaptcha-cypress-coverage branch September 23, 2026 15:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

run e2e tests Run the Cypress end-to-end suite on this PR vivi-working Vivi is actively working this

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant