Skip to content

feat(functional-tests): add pairing E2E test with marionette authority#20119

Open
vbudhram wants to merge 1 commit intomainfrom
react-pair
Open

feat(functional-tests): add pairing E2E test with marionette authority#20119
vbudhram wants to merge 1 commit intomainfrom
react-pair

Conversation

@vbudhram
Copy link
Contributor

@vbudhram vbudhram commented Feb 27, 2026

Because

  • We have no way currently of testing the pairing flow locally
  • We used to have a functional test that verified the basic flow worked via desktop

This pull request

  • Adds a minimal Marionette TCP protocol client (lib/marionette.ts) to drive a real Firefox instance for the authority side of pairing
  • Adds a Firefox process manager (lib/marionette-firefox.ts) that launches Playwright's bundled Firefox with Marionette enabled, manages temp profiles, and cleans up on teardown
  • Adds a Playwright fixture (lib/fixtures/pairing.ts) that wires the Marionette authority into the test lifecycle using firefox.executablePath()
  • Adds the pairing E2E spec (tests/pairing/pairingFlow.spec.ts) exercising: OAuth sign-in via beginOAuthFlow → start pairing → channel handshake → authority approval → supplicant confirm → both sides complete
  • Extracts shared constants (lib/pairing-constants.ts) and helpers (lib/pairing-helpers.ts) with condition-based waits (no hardcoded sleeps)
  • Injects CI_WAF_TOKEN bypass header in CI for stage/production runs

Issue that this pull request solves

Closes: https://mozilla-hub.atlassian.net/browse/FXA-9501

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

Other information (Optional)

How to test:

# 1. Start the FXA stack
yarn start

# 2. Run pairing test against local (uses Playwright's bundled Firefox)
npx playwright test tests/pairing/pairingFlow.spec.ts --project=local

# 3. Run headed (watch the Marionette Firefox + Playwright browser)
MARIONETTE_HEADLESS=false \
  npx playwright test tests/pairing/pairingFlow.spec.ts --project=local --headed

# 4. Run against stage (requires WAF token from 1Password)
CI_WAF_TOKEN=<token> \
  npx playwright test tests/pairing/pairingFlow.spec.ts --project=stage

# 5. Run against stage, headed
CI_WAF_TOKEN=<token> MARIONETTE_HEADLESS=false \
  npx playwright test tests/pairing/pairingFlow.spec.ts --project=stage --headed

# 6. Override Firefox binary (optional)
FIREFOX_BINARY="/Applications/Firefox Nightly.app/Contents/MacOS/firefox" \
  npx playwright test tests/pairing/pairingFlow.spec.ts --project=local

# 7. Flakiness check (run 5x)
for i in {1..5}; do
  npx playwright test tests/pairing/pairingFlow.spec.ts --project=local
done

Note: Expect ~30-35s per run. MARIONETTE_HEADLESS=false shows the authority Firefox window; --headed shows the Playwright supplicant browser.est.

@vbudhram vbudhram force-pushed the react-pair branch 2 times, most recently from de2e451 to 23dda8b Compare February 27, 2026 18:59
expect(pairUrl).toBeTruthy();
expect(pairUrl).toContain('/pair#');
expect(pairUrl).toContain('channel_id=');
if (!pairUrl) throw new Error('pairUrl is null');
Copy link
Contributor

Choose a reason for hiding this comment

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

remove?

@vbudhram vbudhram force-pushed the react-pair branch 4 times, most recently from 4a66af3 to ee5412b Compare February 27, 2026 19:54
@vbudhram vbudhram self-assigned this Feb 27, 2026
@vbudhram vbudhram force-pushed the react-pair branch 3 times, most recently from a9e9176 to a1dc25e Compare February 28, 2026 00:42
Add end-to-end test for the Firefox device pairing flow using a
Marionette-driven Firefox authority and Playwright supplicant.

- Minimal Marionette TCP client (marionette.ts)
- Firefox process manager with temp profile lifecycle (marionette-firefox.ts)
- Playwright fixture using bundled Firefox (125+) as Marionette authority
- Uses beginOAuthFlow for proper PKCE/keys_jwk registration
- Condition-based waits replacing hardcoded sleeps
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
* End-to-end pairing flow test.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the most important comment of the PR, we need marionette to access FF internals to get the QR code url. While iterating it turns out you don't need to take a picture and decode the url, you can use the internal libs to get the url.

Once you have the url (channel_id & channel_key) you can build the pairing url and get the flow connected.

} from '../../lib/pairing-helpers';

// Increase timeout — pairing involves launching a separate Firefox + channel negotiation
test.setTimeout(120_000);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Locally this test takes about 60s to complete,=

* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
* Minimal Marionette protocol client over raw TCP.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I probably would have never thought of doing it this way, but I like it. This introduces zero deps and just talks to marionette via tcp.

export { expect } from '@playwright/test';

/**
* Fetch the pairing channel server URI from the target's well-known config.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Lots of tokens were spent trying to figure this out. Turns out that the channel server on local/stage/prod are all different 🤷🏽

@vbudhram vbudhram marked this pull request as ready for review February 28, 2026 01:50
@vbudhram vbudhram requested a review from a team as a code owner February 28, 2026 01:50
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