Skip to content

test(functional-tests): decouple smoke locators from CMS-overridable copy - #20973

Merged
vpomerleau merged 1 commit into
mainfrom
FXA-13790
Aug 7, 2026
Merged

test(functional-tests): decouple smoke locators from CMS-overridable copy#20973
vpomerleau merged 1 commit into
mainfrom
FXA-13790

Conversation

@vpomerleau

@vpomerleau vpomerleau commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Because

  • The Sync send-tab smoke test failed against production in 342.2. That relying party's CMS config renders the confirm-signup-code CTA as "Continue", but the page object matched only "Confirm" or "Start syncing".
  • A locator that matches default English copy fails precisely when the CMS is working as intended.
  • This is the third time this class of failure has been patched per-incident, with the convention written down nowhere.

This pull request

  • Adds structural formSubmitButton and pageHeading helpers in packages/functional-tests/pages/layout.ts and baseTokenCode.ts, and routes the CTA getters through them.
  • Replaces the five copy-matched heading getters with the elements that identify each step: the email and password fields, whose labels are FTL-only, and a new data-testid="cached-signin-submit" for the cached step, which shares /signin with the password step.
  • Locates the signup-confirmed-sync pair CTA by its Glean id, since that button renders outside a form with no type.
  • Matches only the brand-free portion of the Relay set-password copy, which Fluent renders with bidi isolation marks around the brand term.
  • Moves the copy assertions down to the fxa-settings component tests and constrains MOCK_CMS_INFO with satisfies RelierCmsInfo.
  • Records the convention, including the strict-mode hazard, in .claude/rules/testing/functional-pages.md.

Issue that this pull request solves

Closes: FXA-13790

Checklist

Put an x in the boxes that apply

  • My commit is GPG signed.
  • If applicable, I have modified or added tests which pass locally.
  • I have added necessary documentation (if appropriate).
  • I have verified that my changes render correctly in RTL (if appropriate).
  • I have manually reviewed all AI generated code.

How to review (Optional)

  • Key files/areas to focus on: pages/layout.ts and pages/baseTokenCode.ts for the shared getters, and SigninCached/index.tsx for the only production change.
  • Suggested review order: .claude/rules/testing/functional-pages.md for the reasoning, then the page objects, then the spec sweep.
  • Risky or complex parts: the spec migration is broad but mechanical.

Screenshots (Optional)

Please attach the screenshots of the changes made in case of change in user interface.

Other information (Optional)

Full local functional run is green, including the two specs that previously failed deterministically. Not yet validated against a CMS-configured relying party on stage or prod.

SigninTokenCode never passes primaryButtonText to its CTA, so CMS button text is silently ignored there. Pre-existing and out of scope; worth its own ticket.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 makes the Playwright functional-test suite resilient to relying-party CMS overrides by removing locators that match default English copy and replacing them with structural, route-, or telemetry-based hooks. It also shifts copy assertions into fxa-settings unit tests and documents the locator convention for future changes.

Changes:

  • Add reusable structural helpers for functional-test POMs (formSubmitButton, pageHeading) and migrate affected page objects/specs off CMS-overridable copy.
  • Introduce a stable data-testid="cached-signin-submit" hook for the cached sign-in step (shared /signin route) and update functional tests to use it.
  • Expand fxa-settings component tests to assert CMS-provided headlines and primary CTA text; constrain MOCK_CMS_INFO with satisfies RelierCmsInfo; document the convention in .claude rules and POM README.

Reviewed changes

Copilot reviewed 54 out of 54 changed files in this pull request and generated no comments.

Show a summary per file
File Description
packages/fxa-settings/src/pages/Signup/SignupConfirmedSync/index.test.tsx Adds unit test coverage for CMS headline + primary CTA text on confirmed-sync page.
packages/fxa-settings/src/pages/Signup/index.test.tsx Asserts CMS primaryButtonText renders on signup set-password page.
packages/fxa-settings/src/pages/Signin/SigninUnblock/index.test.tsx Adds unit test asserting CMS headline + primary CTA text on unblock page.
packages/fxa-settings/src/pages/Signin/SigninTotpCode/index.test.tsx Adds CMS primary CTA assertion; removes unnecessary optional chaining from mock usage.
packages/fxa-settings/src/pages/Signin/SigninTokenCode/index.test.tsx Adds unit test asserting CMS headline rendering (button text noted as out of scope).
packages/fxa-settings/src/pages/Signin/SigninRecoveryPhone/index.test.tsx Adds CMS primary CTA assertion; formatting-only test wrapper changes.
packages/fxa-settings/src/pages/Signin/SigninRecoveryCode/index.test.tsx Adds CMS primary CTA assertion for recovery-code page.
packages/fxa-settings/src/pages/Signin/SigninRecoveryChoice/index.test.tsx Adds CMS primary CTA assertion for recovery-choice page.
packages/fxa-settings/src/pages/Signin/SigninPasswordlessCode/index.test.tsx Adds local CMS fixture to assert CMS headline + primary CTA for passwordless code page.
packages/fxa-settings/src/pages/Signin/components/SigninCached/index.tsx Adds data-testid="cached-signin-submit" to cached-step submit button (prod behavior unchanged).
packages/fxa-settings/src/pages/Signin/components/SigninCached/index.test.tsx Asserts testid contract and adds CMS headline + primary CTA coverage for cached step.
packages/fxa-settings/src/pages/mocks.tsx Exports MOCK_CMS_INFO with satisfies RelierCmsInfo to keep literal types and tighten shape.
packages/functional-tests/tests/syncV3/signinCached.spec.ts Replaces heading-based waits with field/testid-based waits.
packages/functional-tests/tests/syncV3/oauthSignIn.spec.ts Replaces cached/email-first heading asserts with structural locators.
packages/functional-tests/tests/syncV3/fxDesktopHandshakeNonSync.spec.ts Replaces cached/password heading asserts with structural locators.
packages/functional-tests/tests/syncV3/fxDesktopHandshake.spec.ts Replaces sync/email/password heading asserts with field-based waits.
packages/functional-tests/tests/signin/signinCached.spec.ts Migrates cached-step gating from heading to submit testid.
packages/functional-tests/tests/signin/signIn.spec.ts Migrates email-first heading gating to email field gating.
packages/functional-tests/tests/signin/redirect.spec.ts Uses email field hidden/visible checks instead of heading.
packages/functional-tests/tests/settings/redirect.spec.ts Fixes missing await on navigation; uses email field gate on redirect.
packages/functional-tests/tests/settings/changeEmail.spec.ts Uses email field gating after sign-out instead of heading.
packages/functional-tests/tests/react-conversion/syncV3SignIn.spec.ts Migrates sync-signin heading gate to email field gate.
packages/functional-tests/tests/react-conversion/signup.spec.ts Migrates signup heading gate to password field gate.
packages/functional-tests/tests/react-conversion/signInCached.spec.ts Migrates cached heading gate to submit testid.
packages/functional-tests/tests/react-conversion/signIn.spec.ts Migrates email-first heading gate to email field gate.
packages/functional-tests/tests/react-conversion/oauthSignup.spec.ts Migrates email/signup heading gates to email/password field gates.
packages/functional-tests/tests/react-conversion/oauthSignin.spec.ts Migrates cached/signup heading gates to submit testid / password field gates.
packages/functional-tests/tests/react-conversion/oauthPromptNone.spec.ts Migrates signup/password heading gates to password textbox gating.
packages/functional-tests/tests/react-conversion/forceAuth.spec.ts Adds URL guard for signup vs signin ambiguity; switches gating to structural locators.
packages/functional-tests/tests/passwordless/signinPasswordless.spec.ts Migrates cached/password heading gates to submit testid / password textbox.
packages/functional-tests/tests/passkeyAuth/passkey-signin.spec.ts Migrates cached/password heading gates to submit testid / password textbox.
packages/functional-tests/tests/oauth/syncSignIn.spec.ts Migrates cached/password heading gates to submit testid / password textbox.
packages/functional-tests/tests/oauth/signin.spec.ts Migrates cached heading gates to submit testid.
packages/functional-tests/tests/oauth/serverSideScopeResolution.spec.ts Migrates cached heading gate to submit testid.
packages/functional-tests/tests/oauth/loginHintCachedCredentials.spec.ts Migrates password heading gate to password textbox gate.
packages/functional-tests/tests/oauth/loginHint.spec.ts Migrates email/signup/password heading gates to field-based gating.
packages/functional-tests/tests/misc/vpnIntegration.spec.ts Migrates cached heading gate to submit testid.
packages/functional-tests/tests/misc/relayIntegration.spec.ts Makes Relay copy assertion resilient to Fluent bidi isolates via partial regex match.
packages/functional-tests/tests/misc/errorViews.spec.ts Migrates email-first heading gates to email textbox gates.
packages/functional-tests/tests/key-stretching-v2/signInTokenCode.spec.ts Migrates email-first heading gate to email textbox gate.
packages/functional-tests/pages/signupConfirmedSync.ts Locates pair CTA via data-glean-id instead of copy.
packages/functional-tests/pages/signup.ts Routes submit actions through structural submit helper; replaces heading-gated waits with field-gated waits.
packages/functional-tests/pages/signinUnblock.ts Uses pageHeading helper instead of headline copy for h1-only pages.
packages/functional-tests/pages/signinTokenCode.ts Uses pageHeading helper instead of headline copy for h1-only pages.
packages/functional-tests/pages/signinRecoveryPhone.ts Uses structural submit button instead of CMS-overridable “Confirm” label.
packages/functional-tests/pages/signinRecoveryChoice.ts Uses formSubmitButton helper for Continue action and removes unused import.
packages/functional-tests/pages/signinPasswordlessCode.ts Uses pageHeading helper; removes CMS-overridable submit-button copy match.
packages/functional-tests/pages/signin.ts Replaces cached/email/password/sync heading gating with field/testid-based locators; submits via structural helper.
packages/functional-tests/pages/README.md Documents “never match CMS-overridable string” locator strategy and points to the detailed rule.
packages/functional-tests/pages/layout.ts Adds shared formSubmitButton helper for stable CTA selection.
packages/functional-tests/pages/confirmSignupCode.ts Uses pageHeading helper instead of headline copy for h1-only pages.
packages/functional-tests/pages/baseTokenCode.ts Adds pageHeading helper and routes submit via formSubmitButton instead of copy match.
CLAUDE.md Updates test-rule auto-load documentation to include functional-tests locator rules.
.claude/rules/testing/functional-pages.md Adds documented convention for CMS-safe locators and strict-mode hazards.

@vpomerleau
vpomerleau marked this pull request as ready for review August 6, 2026 17:57
@vpomerleau
vpomerleau requested a review from a team as a code owner August 6, 2026 17:57
…copy

Because:
- Page copy is CMS-overridable per relying party, so page objects matching on
  default English text break whenever a relying party customizes it. This took
  down the confirm-signup-code step of the Sync send-tab smoke test in
  production, where the CTA renders as "Continue".
- Matching on copy also asserts the opposite of the contract: it fails
  precisely when CMS is working as intended.
- CMS overrides content but never routes, so page identity belongs on the URL,
  on form fields whose labels come from FTL, or on an explicit test id.
- The convention was recorded nowhere, so the pattern keeps being reintroduced.

This commit:
- Adds BaseLayout.formSubmitButton and BaseTokenCodePage.pageHeading, matching
  the submit button and heading structurally instead of by label, and routes the
  CTA getters across nine page objects through them.
- Scopes formSubmitButton to `form` so the getter stays single-match on pages
  that render more than one submit button, since every page object inherits it.
- Drops the redundant submitButton overrides on signinUnblock and
  signinPasswordlessCode, and the confirmButton override on signinRecoveryPhone.
- Replaces the copy-matched heading getters with the elements that actually
  identify each step: the email and password fields, whose labels are FTL-only,
  and a new `cached-signin-submit` test id for the cached step, which shares
  /signin with the password step and has no distinguishing field.
- Applies pageHeading only to the code pages, which render a single h1 behind a
  checkPath() guard. Level matching is unsafe elsewhere: a strict-mode violation
  throws instead of retrying, so such assertions stop working as waits for a
  navigation.
- Locates the signup-confirmed-sync pair CTA by its Glean id, since that button
  renders outside a form with no type attribute.
- Matches only the brand-free portion of the Relay set-password copy, which
  Fluent renders with bidi isolation marks around the brand term.
- Deletes the unused fillOutAuthenticationForm and the getters only it reached.
- Covers CMS headline and primaryButtonText in the component unit tests, which
  is the layer that should own copy assertions, guards the new test id there
  too, and constrains MOCK_CMS_INFO with `satisfies RelierCmsInfo`.
- Records the locator rules, including the strict-mode hazard, in
  .claude/rules/testing/functional-pages.md, which loads whenever a page object
  or spec is edited.

@vbudhram vbudhram left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@vpomerleau I started CI for playwright tests. Have you ran the previous failing tests against stage? Assuming those pass this should be good to merge 👍🏽

@vpomerleau

Copy link
Copy Markdown
Contributor Author

@vpomerleau I started CI for playwright tests. Have you ran the previous failing tests against stage? Assuming those pass this should be good to merge 👍🏽

Good call, just ran the send-tab test (most recently failing test) against stage and prod, both passed.

@vpomerleau
vpomerleau merged commit 07b247c into main Aug 7, 2026
13 checks passed
@vpomerleau
vpomerleau deleted the FXA-13790 branch August 7, 2026 15:29
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.

3 participants