From 18c512149dc39a3b53c647cf546031eeb8f3a4e6 Mon Sep 17 00:00:00 2001 From: Dan Schomburg Date: Wed, 12 Aug 2026 13:05:36 -0700 Subject: [PATCH] bug(settings): Fix broken integration tests Because: - TimeoutAndCancel page had broken tests This Commit: - Fixes tests --- .../TimeoutAndCancel/index.test.tsx | 142 +++++++++++------- .../Supplicant/TimeoutAndCancel/index.tsx | 30 +++- 2 files changed, 111 insertions(+), 61 deletions(-) diff --git a/packages/fxa-settings/src/pages/Pair2/Supplicant/TimeoutAndCancel/index.test.tsx b/packages/fxa-settings/src/pages/Pair2/Supplicant/TimeoutAndCancel/index.test.tsx index 09bf107797f..8f04e490941 100644 --- a/packages/fxa-settings/src/pages/Pair2/Supplicant/TimeoutAndCancel/index.test.tsx +++ b/packages/fxa-settings/src/pages/Pair2/Supplicant/TimeoutAndCancel/index.test.tsx @@ -12,76 +12,110 @@ import { Subject } from './mocks'; const VARIANTS: Array<{ reason: PairingInterruptionReason; heading: string; + descriptionFtlId: string; description: string; }> = [ { reason: 'timeout', heading: 'Looks like we timed out', + descriptionFtlId: 'pair2-supplicant-timeout-and-cancel-timeout-description', description: 'To connect your mobile device and sync your Firefox data, visit firefox.com/pair on your computer.', }, { reason: 'canceled', heading: 'Canceled', + descriptionFtlId: + 'pair2-supplicant-timeout-and-cancel-canceled-description', description: - 'To connect a device anytime, visit firefox.com/pair on your computer.', + 'To connect a device anytime, visit firefox.com/pair on your computer.', }, ]; +const getDescription = (ftlId: string) => + screen.getAllByTestId('ftlmsg-mock').find((el) => el.id === ftlId)!; + describe('Pair2/Supplicant/TimeoutAndCancel page', () => { - describe.each(VARIANTS)('$reason', ({ reason, heading, description }) => { - // Guards against drift between the fallback text in the component and the - // actual Fluent bundle, including a rename of either variant's ids. - it.skip('renders every message with text matching the Fluent bundle', async () => { - const bundle: FluentBundle = await getFtlBundle('settings'); - renderWithLocalizationProvider(); - - const messages = screen - .getAllByTestId('ftlmsg-mock') - // The jest SVG stub renders the file name as the element's text, so - // image messages can never match. Covered by - // components/images/index.test.tsx. - .filter((el) => !el.textContent?.endsWith('.svg')); - - expect(messages.length).toBeGreaterThan(0); - messages.forEach((el) => testL10n(el, bundle)); - }); - - it('renders the heading and description for this state', () => { - renderWithLocalizationProvider(); - - expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent( - heading - ); - screen.getByText(description); - }); - - it('exposes the brand lockup and illustration to assistive technology', () => { - renderWithLocalizationProvider(); - - expect( - screen - .getAllByRole('img') - .map( - (img) => img.getAttribute('alt') ?? img.getAttribute('aria-label') - ) - ).toEqual([ - // AppLayout's page header, then the two images this card renders. - 'Mozilla logo', - 'Firefox logo', - ]); - }); - - // Both states are dead ends by design — the user restarts from their - // computer. Fail loudly if an unwired action is ever added here. - it('renders no action', () => { - renderWithLocalizationProvider(); - - expect(screen.queryByRole('button')).not.toBeInTheDocument(); - // AppLayout's Mozilla logo is the only link on the page. - expect(screen.getAllByRole('link')).toHaveLength(1); - }); - }); + describe.each(VARIANTS)( + '$reason', + ({ reason, heading, descriptionFtlId, description }) => { + // Guards against drift between the fallback text in the component and the + // actual Fluent bundle, including a rename of either variant's ids. + it('renders every message with text matching the Fluent bundle', async () => { + const bundle: FluentBundle = await getFtlBundle('settings'); + renderWithLocalizationProvider(); + + const messages = screen + .getAllByTestId('ftlmsg-mock') + // The jest SVG stub renders the file name as the element's text, so + // image messages can never match. Covered by + // components/images/index.test.tsx. + .filter((el) => !el.textContent?.endsWith('.svg')) + // `testL10n` compares rendered text against the raw Fluent source, so + // the description's DOM overlay tag can never match it. It is checked + // tag-stripped below instead. + .filter((el) => el.id !== descriptionFtlId); + + expect(messages.length).toBeGreaterThan(0); + messages.forEach((el) => testL10n(el, bundle)); + }); + + it('keeps the description fallback text in step with the Fluent message', async () => { + const bundle: FluentBundle = await getFtlBundle('settings'); + renderWithLocalizationProvider(); + + const message = bundle.getMessage(descriptionFtlId); + const source = bundle.formatPattern(message!.value!); + + expect(getDescription(descriptionFtlId).textContent).toEqual( + source.replace(/<\/?b>/g, '') + ); + }); + + it('renders the heading and description for this state', () => { + renderWithLocalizationProvider(); + + expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent( + heading + ); + expect(getDescription(descriptionFtlId)).toHaveTextContent(description); + }); + + // The URL is a placeable inside the sentence, not a fragment glued on, so + // the emphasis has to come from an element the message can wrap. + it('emphasises the pairing URL with an element rather than literal tags', () => { + renderWithLocalizationProvider(); + + expect(screen.getByText('firefox.com/pair').tagName).toEqual('B'); + }); + + it('exposes the brand lockup and illustration to assistive technology', () => { + renderWithLocalizationProvider(); + + expect( + screen + .getAllByRole('img') + .map( + (img) => img.getAttribute('alt') ?? img.getAttribute('aria-label') + ) + ).toEqual([ + // AppLayout's page header, then the two images this card renders. + 'Mozilla logo', + 'Firefox logo', + ]); + }); + + // Both states are dead ends by design — the user restarts from their + // computer. Fail loudly if an unwired action is ever added here. + it('renders no action', () => { + renderWithLocalizationProvider(); + + expect(screen.queryByRole('button')).not.toBeInTheDocument(); + // AppLayout's Mozilla logo is the only link on the page. + expect(screen.getAllByRole('link')).toHaveLength(1); + }); + } + ); it('shows different copy for each state', () => { const { unmount } = renderWithLocalizationProvider( diff --git a/packages/fxa-settings/src/pages/Pair2/Supplicant/TimeoutAndCancel/index.tsx b/packages/fxa-settings/src/pages/Pair2/Supplicant/TimeoutAndCancel/index.tsx index c6e5083e32b..41035db3ab5 100644 --- a/packages/fxa-settings/src/pages/Pair2/Supplicant/TimeoutAndCancel/index.tsx +++ b/packages/fxa-settings/src/pages/Pair2/Supplicant/TimeoutAndCancel/index.tsx @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import React from 'react'; +import React, { ReactElement } from 'react'; import { FtlMsg } from 'fxa-react/lib/utils'; import AppLayout from '../../../../components/AppLayout'; import { @@ -29,23 +29,36 @@ const COPY: Record< headingFtlId: string; heading: string; descriptionFtlId: string; - description: string; + /** + * Fallback markup rather than a string: both sentences emphasise the pairing + * URL through a `` the Fluent message wraps, so the fallback has to carry + * a real element instead of the tags as text. + */ + description: ReactElement; } > = { timeout: { headingFtlId: 'pair2-supplicant-timeout-and-cancel-timeout-heading', heading: 'Looks like we timed out', descriptionFtlId: 'pair2-supplicant-timeout-and-cancel-timeout-description', - description: - 'To connect your mobile device and sync your Firefox data, visit firefox.com/pair on your computer.', + description: ( + <> + To connect your mobile device and sync your Firefox data, visit{' '} + firefox.com/pair on your computer. + + ), }, canceled: { headingFtlId: 'pair2-supplicant-timeout-and-cancel-canceled-heading', heading: 'Canceled', descriptionFtlId: 'pair2-supplicant-timeout-and-cancel-canceled-description', - description: - 'To connect a device anytime, visit firefox.com/pair on your computer.', + description: ( + <> + To connect a device anytime, visit{' '} + firefox.com/pair on your computer. + + ), }, }; @@ -73,7 +86,10 @@ const TimeoutAndCancel = ({ reason }: TimeoutAndCancelProps) => {

{heading}

- + }} + >

{description}