diff --git a/packages/fxa-settings/src/pages/Pair/AuthComplete/index.test.tsx b/packages/fxa-settings/src/pages/Pair/AuthComplete/index.test.tsx index 1b1729a96c6..124d06a6031 100644 --- a/packages/fxa-settings/src/pages/Pair/AuthComplete/index.test.tsx +++ b/packages/fxa-settings/src/pages/Pair/AuthComplete/index.test.tsx @@ -3,9 +3,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import React from 'react'; -import { screen } from '@testing-library/react'; +import { screen, waitFor } from '@testing-library/react'; import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider'; -import AuthComplete, { viewName } from '.'; +import AuthComplete, { PAIR_DEVICE_INFO_KEY, viewName } from '.'; import { MOCK_METADATA_UNKNOWN_LOCATION } from '../../../components/DeviceInfoBlock/mocks'; import { usePageViewEvent } from '../../../lib/metrics'; import { REACT_ENTRYPOINT } from '../../../constants'; @@ -86,6 +86,7 @@ describe('AuthComplete page', () => { ); mockIntegration = new PAI(); mockIntegration.data = { entrypoint: undefined }; + sessionStorage.clear(); }); it('calls complete() on mount and destroy() on unmount', () => { @@ -96,6 +97,46 @@ describe('AuthComplete page', () => { unmount(); expect(mockIntegration.destroy).toHaveBeenCalled(); }); + + it('restores device info from sessionStorage after refresh', () => { + const cachedInfo = { + deviceFamily: 'Firefox', + deviceOS: 'Windows', + ipAddress: '1.2.3.4', + }; + sessionStorage.setItem(PAIR_DEVICE_INFO_KEY, JSON.stringify(cachedInfo)); + + renderWithLocalizationProvider( + + ); + + expect(screen.getByRole('heading', { level: 2 })).toHaveTextContent( + 'You are now syncing with: Firefox on Windows' + ); + expect(mockIntegration.getSupplicantMetadata).not.toHaveBeenCalled(); + }); + + it('persists fetched device info to sessionStorage', async () => { + const fetchedInfo = { + deviceFamily: 'Firefox', + deviceOS: 'Android', + ipAddress: '1.2.3.4', + }; + mockIntegration.getSupplicantMetadata.mockResolvedValue(fetchedInfo); + + renderWithLocalizationProvider( + + ); + + await waitFor(() => + expect(sessionStorage.getItem(PAIR_DEVICE_INFO_KEY)).toBe( + JSON.stringify(fetchedInfo) + ) + ); + expect(screen.getByRole('heading', { level: 2 })).toHaveTextContent( + 'You are now syncing with: Firefox on Android' + ); + }); }); describe('Send Tab variant', () => { diff --git a/packages/fxa-settings/src/pages/Pair/AuthComplete/index.tsx b/packages/fxa-settings/src/pages/Pair/AuthComplete/index.tsx index 26d730f36d4..443cf2d26f8 100644 --- a/packages/fxa-settings/src/pages/Pair/AuthComplete/index.tsx +++ b/packages/fxa-settings/src/pages/Pair/AuthComplete/index.tsx @@ -19,6 +19,8 @@ import { isSendTabEntrypoint } from '../../../lib/utilities'; export const viewName = 'pair.auth.complete'; +export const PAIR_DEVICE_INFO_KEY = 'pair.supp.device-info'; + type AuthCompleteProps = { suppDeviceInfo?: RemoteMetadata; supportsFirefoxView?: boolean; @@ -37,7 +39,15 @@ const AuthComplete = ({ usePageViewEvent(viewName, REACT_ENTRYPOINT); const ftlMsgResolver = useFtlMsgResolver(); const [deviceInfo, setDeviceInfo] = useState( - suppDeviceInfoProp + () => { + if (suppDeviceInfoProp) return suppDeviceInfoProp; + try { + const cached = sessionStorage.getItem(PAIR_DEVICE_INFO_KEY); + return cached ? (JSON.parse(cached) as RemoteMetadata) : undefined; + } catch { + return undefined; + } + } ); const authorityIntegration = @@ -49,16 +59,21 @@ const AuthComplete = ({ const deviceOS = deviceInfo?.deviceOS || 'Unknown'; const isSendTab = isSendTabEntrypoint(integration?.data.entrypoint); - // Fetch supplicant metadata if not provided via props + // Fetch supplicant metadata if not already available from props or sessionStorage useEffect(() => { - if (suppDeviceInfoProp) { + if (suppDeviceInfoProp || deviceInfo) { return; } authorityIntegration ?.getSupplicantMetadata() - .then(setDeviceInfo) + .then((info) => { + try { + sessionStorage.setItem(PAIR_DEVICE_INFO_KEY, JSON.stringify(info)); + } catch {} + setDeviceInfo(info); + }) .catch(() => {}); - }, [authorityIntegration, suppDeviceInfoProp]); + }, [authorityIntegration, suppDeviceInfoProp, deviceInfo]); // Signal pairing complete to Firefox on mount useEffect(() => {