diff --git a/packages/fxa-settings/src/components/AppLayout/__snapshots__/index.test.tsx.snap b/packages/fxa-settings/src/components/AppLayout/__snapshots__/index.test.tsx.snap index 320f1afc993..6b36ea0a971 100644 --- a/packages/fxa-settings/src/components/AppLayout/__snapshots__/index.test.tsx.snap +++ b/packages/fxa-settings/src/components/AppLayout/__snapshots__/index.test.tsx.snap @@ -17,7 +17,7 @@ exports[` snapshots renders correctly with CMS: header background 1 exports[` snapshots renders correctly with CMS: header logo 1`] = ` custom-header-logo @@ -29,7 +29,7 @@ exports[` snapshots renders correctly with CMS: split layout main 1 /> `; -exports[` snapshots renders correctly with CMS: title 1`] = `"MOCK Shared Title | Mozilla accounts"`; +exports[` snapshots renders correctly with CMS: title 1`] = `"CMS: Shared Title | Mozilla accounts"`; exports[` snapshots renders correctly without CMS: background wrapper 1`] = `
{ ).toBeInTheDocument(); screen.getByRole('heading', { - name: 'Sign up or sign in to your Mozilla account', + name: MOCK_CMS_INFO.EmailFirstPage.headline, + }); + screen.getByText(MOCK_CMS_INFO.EmailFirstPage.description); + screen.getByRole('button', { + name: MOCK_CMS_INFO.EmailFirstPage.primaryButtonText, }); - screen.getByText( - 'Stay protected with continuous data monitoring and automatic data removal.' - ); - screen.getByRole('button', { name: 'Continue' }); }); it('renders as expected with cms info on mobile', () => { diff --git a/packages/fxa-settings/src/pages/PostVerify/SetPassword/index.stories.tsx b/packages/fxa-settings/src/pages/PostVerify/SetPassword/index.stories.tsx index 12c51650c50..08033dd0cfc 100644 --- a/packages/fxa-settings/src/pages/PostVerify/SetPassword/index.stories.tsx +++ b/packages/fxa-settings/src/pages/PostVerify/SetPassword/index.stories.tsx @@ -6,8 +6,8 @@ import { withLocalization } from 'fxa-react/lib/storybooks'; import SetPassword from '.'; import { Meta } from '@storybook/react'; import { SetPasswordProps } from './interfaces'; -import { Subject } from './mocks'; -import { createMockIntegrationWithCms } from '../../mocks'; +import { createMockIntegration, Subject } from './mocks'; +import { createMockIntegrationWithCms, MOCK_CMS_INFO } from '../../mocks'; export default { title: 'Pages/PostVerify/SetPassword', @@ -24,6 +24,18 @@ const storyWithProps = ({ export const Default = storyWithProps(); +export const ThirdPartyAuthDefault = storyWithProps({ + passwordCreationReason: 'third_party_auth', +}); + +export const OtpDefault = storyWithProps({ + passwordCreationReason: 'otp', +}); + +export const PasskeyDefault = storyWithProps({ + passwordCreationReason: 'passkey', +}); + export const WithCms = storyWithProps({ integration: createMockIntegrationWithCms(), }); @@ -32,3 +44,51 @@ export const OtpWithCms = storyWithProps({ passwordCreationReason: 'otp', integration: createMockIntegrationWithCms(), }); + +export const ThirdPartyAuthWithCms = storyWithProps({ + passwordCreationReason: 'third_party_auth', + integration: createMockIntegrationWithCms(), +}); + +export const PasskeyWithCms = storyWithProps({ + passwordCreationReason: 'third_party_auth', + integration: createMockIntegrationWithCms(), +}); + +const cmsIntegrationWithLegalTerms = createMockIntegration( + { + ...MOCK_CMS_INFO, + PostVerifySetPasswordPage: { + ...MOCK_CMS_INFO.SignupSetPasswordPage, + headline: 'VPN Headline (from cms)', + description: 'VPN description (from cms)', + }, + }, + { + legalTerms: { + label: 'Customized:', + termsOfServiceLink: 'https://www.mozilla.org/customized/terms/', + privacyNoticeLink: 'https://www.mozilla.org/customized/privacy/', + fontSize: 'default', + }, + } +); + +export const WithCmsAndLegalTerms = storyWithProps({ + integration: cmsIntegrationWithLegalTerms, +}); + +export const OtpWithCmsAndLegalTerms = storyWithProps({ + passwordCreationReason: 'otp', + integration: cmsIntegrationWithLegalTerms, +}); + +export const ThirdPartyWithAndCmsAndLegalTerms = storyWithProps({ + passwordCreationReason: 'third_party_auth', + integration: cmsIntegrationWithLegalTerms, +}); + +export const PasskeyWithCmsAndLegalTerms = storyWithProps({ + passwordCreationReason: 'passkey', + integration: cmsIntegrationWithLegalTerms, +}); diff --git a/packages/fxa-settings/src/pages/PostVerify/SetPassword/index.test.tsx b/packages/fxa-settings/src/pages/PostVerify/SetPassword/index.test.tsx index bd046e13d12..d581120a766 100644 --- a/packages/fxa-settings/src/pages/PostVerify/SetPassword/index.test.tsx +++ b/packages/fxa-settings/src/pages/PostVerify/SetPassword/index.test.tsx @@ -115,6 +115,51 @@ describe('SetPassword page', () => { ).toBeInTheDocument(); }); + it('renders the description above the email to match the Signup layout', () => { + renderWithLocalizationProvider(); + + const description = screen.getByText( + 'This encrypts your data. It needs to be different from your Google or Apple account password.' + ); + const email = screen.getByText(MOCK_EMAIL); + + // DOCUMENT_POSITION_FOLLOWING means `email` appears after `description`. + expect( + description.compareDocumentPosition(email) & + Node.DOCUMENT_POSITION_FOLLOWING + ).toBeTruthy(); + }); + + describe('terms and privacy agreement', () => { + it('renders the default Mozilla Terms of Service and Privacy Notice links when no custom legal terms are set', () => { + renderWithLocalizationProvider(); + + expect( + screen.getByRole('link', { name: 'Terms of Service' }) + ).toBeInTheDocument(); + expect( + screen.getByRole('link', { name: 'Privacy Notice' }) + ).toBeInTheDocument(); + }); + + it('renders the client-specific legal terms when the integration provides them', () => { + renderWithLocalizationProvider( + + ); + + expect(screen.getByText(/Mozilla VPN:/)).toBeInTheDocument(); + }); + }); + describe('Glean events', () => { // First row asserts the default-arg behaviour (no prop passed); the // others assert explicit pass-through for each non-default value. diff --git a/packages/fxa-settings/src/pages/PostVerify/SetPassword/index.tsx b/packages/fxa-settings/src/pages/PostVerify/SetPassword/index.tsx index 65b7dd338e3..9f17d12cf96 100644 --- a/packages/fxa-settings/src/pages/PostVerify/SetPassword/index.tsx +++ b/packages/fxa-settings/src/pages/PostVerify/SetPassword/index.tsx @@ -15,6 +15,7 @@ import { getCmsHeadlineClassName, getCmsHeadlineStyle, } from '../../../components/CardHeader'; +import TermsPrivacyAgreement from '../../../components/TermsPrivacyAgreement'; import GleanMetrics from '../../../lib/glean'; export const SetPassword = ({ @@ -118,8 +119,6 @@ export const SetPassword = ({

Create password to sync

)} -

{email}

- {bannerErrorText && ( )} @@ -143,6 +142,8 @@ export const SetPassword = ({ )} +

{email}

+ + + ); }; diff --git a/packages/fxa-settings/src/pages/PostVerify/SetPassword/interfaces.ts b/packages/fxa-settings/src/pages/PostVerify/SetPassword/interfaces.ts index 44b9ad344f4..b6a3f88f41f 100644 --- a/packages/fxa-settings/src/pages/PostVerify/SetPassword/interfaces.ts +++ b/packages/fxa-settings/src/pages/PostVerify/SetPassword/interfaces.ts @@ -23,7 +23,7 @@ export type CreatePasswordHandler = ( export type PostVerifySetPasswordIntegration = Pick< Integration, - 'getCmsInfo' | 'isSync' + 'getCmsInfo' | 'isSync' | 'getLegalTerms' >; /** diff --git a/packages/fxa-settings/src/pages/PostVerify/SetPassword/mocks.tsx b/packages/fxa-settings/src/pages/PostVerify/SetPassword/mocks.tsx index 9363663e67b..4ed5e35f9cd 100644 --- a/packages/fxa-settings/src/pages/PostVerify/SetPassword/mocks.tsx +++ b/packages/fxa-settings/src/pages/PostVerify/SetPassword/mocks.tsx @@ -12,14 +12,19 @@ import { import { MOCK_EMAIL } from '../../mocks'; import { mockUseFxAStatus } from '../../../lib/hooks/useFxAStatus/mocks'; import { RelierCmsInfo } from '../../../models'; +import { RelierLegalTerms } from '../../../models/integrations/relier-interfaces'; export function createMockIntegration( cmsInfo?: RelierCmsInfo, - { isSync = true } = {} + { + isSync = true, + legalTerms, + }: { isSync?: boolean; legalTerms?: RelierLegalTerms } = {} ): PostVerifySetPasswordIntegration { return { getCmsInfo: () => cmsInfo, isSync: () => isSync, + getLegalTerms: () => legalTerms, }; } diff --git a/packages/fxa-settings/src/pages/Signin/__snapshots__/index.test.tsx.snap b/packages/fxa-settings/src/pages/Signin/__snapshots__/index.test.tsx.snap index e8cec076f8a..b7878575053 100644 --- a/packages/fxa-settings/src/pages/Signin/__snapshots__/index.test.tsx.snap +++ b/packages/fxa-settings/src/pages/Signin/__snapshots__/index.test.tsx.snap @@ -2,7 +2,7 @@ exports[`Signin component snapshots - CMS renders AppLayout with CMS header logo 1`] = ` custom-header-logo @@ -12,7 +12,7 @@ exports[`Signin component snapshots - CMS renders CardHeader with CMS content wh

- for your Mozilla account + CMS: for your Mozilla account

`; @@ -20,7 +20,7 @@ exports[`Signin component snapshots - CMS renders CardHeader with CMS content wh

- Enter your password + CMS: Enter your password

`; @@ -38,6 +38,6 @@ exports[`Signin component snapshots - CMS renders the CMS-styled submit button 1 style="--cta-bg: #6B4DFB; --cta-border: #6B4DFB; --cta-active: #6B4DFB; --cta-disabled: #6B4DFB; color: rgb(255, 255, 255);" type="submit" > - Sign in + CMS: Sign in `; diff --git a/packages/fxa-settings/src/pages/Signin/index.test.tsx b/packages/fxa-settings/src/pages/Signin/index.test.tsx index 9beb04e7763..24dd61dd9d3 100644 --- a/packages/fxa-settings/src/pages/Signin/index.test.tsx +++ b/packages/fxa-settings/src/pages/Signin/index.test.tsx @@ -1788,7 +1788,9 @@ describe('Signin component', () => { it('renders the CMS-styled submit button', () => { render({ integration: createMockSigninWebIntegration(cmsProps) }); - const submitButton = screen.getByRole('button', { name: 'Sign in' }); + const submitButton = screen.getByRole('button', { + name: cmsProps.cmsInfo.SigninPage.primaryButtonText, + }); expect(submitButton).toMatchSnapshot(); }); diff --git a/packages/fxa-settings/src/pages/Signup/ConfirmSignupCode/index.test.tsx b/packages/fxa-settings/src/pages/Signup/ConfirmSignupCode/index.test.tsx index 11fbb486cee..6ca99108896 100644 --- a/packages/fxa-settings/src/pages/Signup/ConfirmSignupCode/index.test.tsx +++ b/packages/fxa-settings/src/pages/Signup/ConfirmSignupCode/index.test.tsx @@ -240,11 +240,15 @@ describe('ConfirmSignupCode page', () => { // testAllL10n(screen, bundle); const headingEl = screen.getByRole('heading', { level: 1 }); - expect(headingEl).toHaveTextContent('Enter confirmation code'); - screen.getByText('For your Mozilla account'); + expect(headingEl).toHaveTextContent( + MOCK_CMS_INFO.SignupConfirmCodePage.headline + ); + screen.getByText(MOCK_CMS_INFO.SignupConfirmCodePage.description); screen.getByLabelText('Enter 6-digit code'); - screen.getByRole('button', { name: 'Confirm' }); + screen.getByRole('button', { + name: MOCK_CMS_INFO.SignupConfirmCodePage.primaryButtonText, + }); screen.getByRole('button', { name: 'Email new code.' }); screen.getByTestId('cms-logo'); screen.getByText(MOCK_CMS_INFO.shared.additionalAccessibilityInfo); @@ -263,7 +267,10 @@ describe('ConfirmSignupCode page', () => { integration, finishOAuthFlowHandler: jest.fn().mockReturnValueOnce(tryAgainError()), }); - submit(); + submit( + MOCK_SIGNUP_CODE, + MOCK_CMS_INFO.SignupConfirmCodePage.primaryButtonText + ); await waitFor(() => { screen.getByText(OAUTH_ERRORS.TRY_AGAIN.message); @@ -276,7 +283,10 @@ describe('ConfirmSignupCode page', () => { integration, finishOAuthFlowHandler: jest.fn(), }); - submit(); + submit( + MOCK_SIGNUP_CODE, + MOCK_CMS_INFO.SignupConfirmCodePage.primaryButtonText + ); await waitFor(() => { expect(fxaOAuthLoginSpy).not.toHaveBeenCalled(); @@ -315,7 +325,10 @@ describe('ConfirmSignupCode page', () => { integration, origin: 'signup', }); - submit(MOCK_SIGNUP_CODE); + submit( + MOCK_SIGNUP_CODE, + MOCK_CMS_INFO.SignupConfirmCodePage.primaryButtonText + ); await waitFor(() => { expect(ReactUtils.hardNavigate).toHaveBeenCalledWith( @@ -333,7 +346,10 @@ describe('ConfirmSignupCode page', () => { integration, origin: 'signin', }); - submit(MOCK_SIGNUP_CODE); + submit( + MOCK_SIGNUP_CODE, + MOCK_CMS_INFO.SignupConfirmCodePage.primaryButtonText + ); await waitFor(() => { expect(ReactUtils.hardNavigate).toHaveBeenCalledWith( diff --git a/packages/fxa-settings/src/pages/Signup/index.test.tsx b/packages/fxa-settings/src/pages/Signup/index.test.tsx index 722d4170fbb..89b6f13d3d4 100644 --- a/packages/fxa-settings/src/pages/Signup/index.test.tsx +++ b/packages/fxa-settings/src/pages/Signup/index.test.tsx @@ -311,8 +311,10 @@ describe('Signup page', () => { screen.queryByRole('button', { name: /Continue with Apple/ }) ).toBeInTheDocument(); - screen.getByRole('heading', { name: 'Create a password' }); - screen.getByText('to continue'); + screen.getByRole('heading', { + name: MOCK_CMS_INFO.SignupSetPasswordPage.headline, + }); + screen.getByText(MOCK_CMS_INFO.SignupSetPasswordPage.description); }); it('renders as expected when cms enabled and on mobile', async () => { @@ -328,7 +330,9 @@ describe('Signup page', () => { ); // Await to let react-hook-form's internal state updates settle. - await screen.findByRole('heading', { name: 'Create a password' }); + await screen.findByRole('heading', { + name: MOCK_CMS_INFO.SignupSetPasswordPage.headline, + }); expect( screen.queryByAltText(MOCK_CMS_INFO.SignupSetPasswordPage.logoAltText) diff --git a/packages/fxa-settings/src/pages/mocks.tsx b/packages/fxa-settings/src/pages/mocks.tsx index 07bfdb7a2b6..eee763e88a9 100644 --- a/packages/fxa-settings/src/pages/mocks.tsx +++ b/packages/fxa-settings/src/pages/mocks.tsx @@ -106,15 +106,15 @@ export const MOCK_CMS_INFO = { headerLogoUrl: 'https://cdn.accounts.firefox.com/other/firefox-browser-logo.svg', logoAltText: 'logo', - headerLogoAltText: 'custom-header-logo', - pageTitle: 'MOCK Shared Title', + headerLogoAltText: 'CMS: custom-header-logo', + pageTitle: 'CMS: Shared Title', featureFlags: { syncConfirmedPageHideCTA: false, syncHidePromoAfterLogin: false, }, favicon: '', additionalAccessibilityInfo: - 'Firefox will try sending you back to use an email mask after you sign in.', + 'CMS: Firefox will try sending you back to use an email mask after you sign in.', backgrounds: { header: 'linear-gradient(135deg, #6B4DFB 0%, #3A1A78 100%)', defaultLayout: @@ -125,93 +125,93 @@ export const MOCK_CMS_INFO = { EmailFirstPage: { logoUrl: 'https://cdn.accounts.firefox.com/other/firefox-browser-logo.svg', logoAltText: 'custom-email-first-logo', - headline: 'Sign up or sign in to your Mozilla account', + headline: 'CMS: Sign up or sign in to your Mozilla account', description: - 'Stay protected with continuous data monitoring and automatic data removal.', - primaryButtonText: 'Continue', - pageTitle: 'Sign up or sign in to your Mozilla account', + 'Stay protected with continuous data monitoring and automatic data removal. (from cms)', + primaryButtonText: 'CMS: Continue', + pageTitle: 'CMS: Sign up or sign in to your Mozilla account', }, SignupSetPasswordPage: { logoUrl: 'https://cdn.accounts.firefox.com/other/firefox-browser-logo.svg', logoAltText: 'custom-signup-logo', - headline: 'Create a password', - description: 'to continue', - primaryButtonText: 'Continue', - pageTitle: 'Create a password', + headline: 'CMS: Create a password', + description: 'CMS: to continue', + primaryButtonText: 'CMS: Continue', + pageTitle: 'CMS: Create a password', }, SignupConfirmCodePage: { - headline: 'Enter confirmation code', - description: 'For your Mozilla account', - primaryButtonText: 'Confirm', - pageTitle: 'Enter confirmation code', + headline: 'CMS: Enter confirmation code', + description: 'CMS: For your Mozilla account', + primaryButtonText: 'CMS: Confirm', + pageTitle: 'CMS: Enter confirmation code', }, SignupConfirmedSyncPage: { - headline: 'Sync is turned on', + headline: 'CMS: Sync is turned on', description: - 'Your passwords, addresses, bookmarks, history, and more can sync everywhere you use Firefox.', - primaryButtonText: 'Add another device', - pageTitle: 'Sync is turned on', + 'CMS: Your passwords, addresses, bookmarks, history, and more can sync everywhere you use Firefox.', + primaryButtonText: 'CMS: Add another device', + pageTitle: 'CMS: Sync is turned on', primaryImage: { url: MOCK_CMS_PRIMARY_IMAGE_URL, - altText: 'A cartoon fox with a laptop and a smartphone', + altText: 'CMS: A cartoon fox with a laptop and a smartphone', }, }, PostVerifySetPasswordPage: { - headline: 'Create a password to sync', + headline: 'CMS: Create a password to sync', description: - 'A Mozilla account works with sync to help protect your browsing, passwords, and more.', - primaryButtonText: 'Continue', - pageTitle: 'Create a password to sync', + 'CMS: A Mozilla account works with sync to help protect your browsing, passwords, and more.', + primaryButtonText: 'CMS: Continue', + pageTitle: 'CMS: Create a password to sync', }, SigninPage: { - headline: 'Enter your password', - description: 'for your Mozilla account', - primaryButtonText: 'Sign in', - pageTitle: 'Enter your password', + headline: 'CMS: Enter your password', + description: 'CMS: for your Mozilla account', + primaryButtonText: 'CMS: Sign in', + pageTitle: 'CMS: Enter your password', }, SigninTokenCodePage: { - headline: 'Enter confirmation code', - description: 'for your Mozilla account', - primaryButtonText: 'Continue', - pageTitle: 'Enter confirmation code', + headline: 'CMS: Enter confirmation code', + description: 'CMS: for your Mozilla account', + primaryButtonText: 'CMS: Continue', + pageTitle: 'CMS: Enter confirmation code', }, SigninUnblockCodePage: { - headline: 'Authorize this sign-in', - description: 'for your Mozilla account', - primaryButtonText: 'Continue', - pageTitle: 'Authorize this sign-in', + headline: 'CMS: Authorize this sign-in', + description: 'CMS: for your Mozilla account', + primaryButtonText: 'CMS: Continue', + pageTitle: 'CMS: Authorize this sign-in', }, SigninTotpCodePage: { - headline: 'Verify your identity', - description: 'Use your authenticator app to continue', - primaryButtonText: 'Verify code', - pageTitle: 'Two-step verification', + headline: 'CMS: Verify your identity', + description: 'CMS: Use your authenticator app to continue', + primaryButtonText: 'CMS: Verify code', + pageTitle: 'CMS: Two-step verification', }, SigninRecoveryChoicePage: { - headline: 'Choose your recovery', - description: 'Select how you want to verify your identity', - primaryButtonText: 'Continue recovery', - pageTitle: 'Recovery method selection', + headline: 'CMS: Choose your recovery', + description: 'CMS: Select how you want to verify your identity', + primaryButtonText: 'CMS: Continue recovery', + pageTitle: 'CMS: Recovery method selection', }, SigninRecoveryCodePage: { - headline: 'Use a backup code', - description: 'Enter one of your saved backup codes', - primaryButtonText: 'Submit code', - pageTitle: 'Backup code verification', + headline: 'CMS: Use a backup code', + description: 'CMS: Enter one of your saved backup codes', + primaryButtonText: 'CMS: Submit code', + pageTitle: 'CMS: Backup code verification', primaryImage: { url: MOCK_CMS_PRIMARY_IMAGE_URL, - altText: 'A cartoon fox with a laptop and a smartphone', + altText: 'CMS: A cartoon fox with a laptop and a smartphone', }, }, SigninRecoveryPhonePage: { - headline: 'Check your phone', - description: 'We sent a verification code to your phone', - primaryButtonText: 'Verify phone code', - pageTitle: 'Phone recovery verification', + headline: 'CMS: Check your phone', + description: 'CMS: We sent a verification code to your phone', + primaryButtonText: 'CMS: Verify phone code', + pageTitle: 'CMS: Phone recovery verification', primaryImage: { url: MOCK_CMS_PRIMARY_IMAGE_URL, - altText: 'A cartoon fox with a laptop and a smartphone', + altText: 'CMS: A cartoon fox with a laptop and a smartphone', }, }, };