@@ -24,6 +24,7 @@ import { IndexQueryParams } from '../../models/pages/index';
2424import { GenericData , ModelValidationErrors } from '../../lib/model-data' ;
2525import { mockUseFxAStatus } from '../../lib/hooks/useFxAStatus/mocks' ;
2626import { firefox } from '../../lib/channels/firefox' ;
27+ import * as storageUtils from '../../lib/storage-utils' ;
2728
2829let mockLocationState = { } ;
2930let mockNavigate = jest . fn ( ) ;
@@ -58,9 +59,19 @@ jest.mock('../../lib/channels/firefox', () => ({
5859 ...jest . requireActual ( '../../lib/channels/firefox' ) ,
5960 firefox : {
6061 fxaCanLinkAccount : jest . fn ( ) ,
62+ requestSignedInUser : jest . fn ( ) . mockResolvedValue ( undefined ) ,
6163 } ,
6264} ) ) ;
6365
66+ jest . mock ( '../../lib/storage-utils' , ( ) => {
67+ const actual = jest . requireActual ( '../../lib/storage-utils' ) ;
68+ return {
69+ ...actual ,
70+ persistAccount : jest . fn ( ) ,
71+ setCurrentAccount : jest . fn ( ) ,
72+ } ;
73+ } ) ;
74+
6475jest . mock ( '../../lib/email-domain-validator' , ( ) => ( {
6576 checkEmailDomain : jest . fn ( ) ,
6677} ) ) ;
@@ -606,6 +617,172 @@ describe('IndexContainer', () => {
606617 } ) ;
607618 } ) ;
608619
620+ describe ( 'WebChannel browser-user fallback' , ( ) => {
621+ let isProbablyFirefoxSpy : jest . SpyInstance ;
622+
623+ beforeEach ( ( ) => {
624+ isProbablyFirefoxSpy = jest
625+ . spyOn ( ModelsModule , 'isProbablyFirefox' )
626+ . mockReturnValue ( true ) ;
627+ ( firefox . requestSignedInUser as jest . Mock ) . mockReset ( ) ;
628+ ( storageUtils . persistAccount as jest . Mock ) . mockReset ( ) ;
629+ ( storageUtils . setCurrentAccount as jest . Mock ) . mockReset ( ) ;
630+ } ) ;
631+
632+ afterEach ( ( ) => {
633+ isProbablyFirefoxSpy . mockRestore ( ) ;
634+ } ) ;
635+
636+ it ( 'persists browser user (without sessionToken) when verified is false' , async ( ) => {
637+ jest . spyOn ( cache , 'currentAccount' ) . mockReturnValue ( undefined ) ;
638+ jest . spyOn ( cache , 'lastStoredAccount' ) . mockReturnValue ( undefined ) ;
639+ ( firefox . requestSignedInUser as jest . Mock ) . mockResolvedValue ( {
640+ uid : 'browseruid123' ,
641+ 642+ sessionToken : 'untrusted-token' ,
643+ verified : false ,
644+ } ) ;
645+
646+ renderWithLocalizationProvider (
647+ < LocationProvider >
648+ < IndexContainer
649+ { ...{
650+ integration,
651+ serviceName : MozServices . Default ,
652+ useFxAStatusResult : mockUseFxAStatusResult ,
653+ } }
654+ />
655+ </ LocationProvider >
656+ ) ;
657+
658+ await waitFor ( ( ) => {
659+ expect ( firefox . requestSignedInUser ) . toHaveBeenCalled ( ) ;
660+ } ) ;
661+ await waitFor ( ( ) => {
662+ expect ( storageUtils . persistAccount ) . toHaveBeenCalledWith (
663+ expect . objectContaining ( {
664+ uid : 'browseruid123' ,
665+ 666+ } )
667+ ) ;
668+ } ) ;
669+ const persisted = ( storageUtils . persistAccount as jest . Mock ) . mock
670+ . calls [ 0 ] [ 0 ] ;
671+ expect ( persisted . sessionToken ) . toBeUndefined ( ) ;
672+ expect ( storageUtils . setCurrentAccount ) . toHaveBeenCalledWith (
673+ 'browseruid123'
674+ ) ;
675+ } ) ;
676+
677+ it ( 'persists browser sessionToken when verified is true' , async ( ) => {
678+ jest . spyOn ( cache , 'currentAccount' ) . mockReturnValue ( undefined ) ;
679+ jest . spyOn ( cache , 'lastStoredAccount' ) . mockReturnValue ( undefined ) ;
680+ ( firefox . requestSignedInUser as jest . Mock ) . mockResolvedValue ( {
681+ uid : 'browseruid123' ,
682+ 683+ sessionToken : 'trusted-token' ,
684+ verified : true ,
685+ } ) ;
686+
687+ renderWithLocalizationProvider (
688+ < LocationProvider >
689+ < IndexContainer
690+ { ...{
691+ integration,
692+ serviceName : MozServices . Default ,
693+ useFxAStatusResult : mockUseFxAStatusResult ,
694+ } }
695+ />
696+ </ LocationProvider >
697+ ) ;
698+
699+ await waitFor ( ( ) => {
700+ expect ( storageUtils . persistAccount ) . toHaveBeenCalledWith (
701+ expect . objectContaining ( {
702+ uid : 'browseruid123' ,
703+ 704+ sessionToken : 'trusted-token' ,
705+ } )
706+ ) ;
707+ } ) ;
708+ } ) ;
709+
710+ it ( 'does not query the browser when localStorage already has an account' , async ( ) => {
711+ jest . spyOn ( cache , 'currentAccount' ) . mockReturnValue ( {
712+ uid : 'cacheduid' ,
713+ 714+ lastLogin : Date . now ( ) ,
715+ } ) ;
716+ jest . spyOn ( cache , 'lastStoredAccount' ) . mockReturnValue ( undefined ) ;
717+
718+ renderWithLocalizationProvider (
719+ < LocationProvider >
720+ < IndexContainer
721+ { ...{
722+ integration,
723+ serviceName : MozServices . Default ,
724+ useFxAStatusResult : mockUseFxAStatusResult ,
725+ } }
726+ />
727+ </ LocationProvider >
728+ ) ;
729+
730+ // Cached account triggers auto-submit; navigation proves we skipped the WebChannel call.
731+ await waitFor ( ( ) => {
732+ expect ( mockNavigate ) . toHaveBeenCalled ( ) ;
733+ } ) ;
734+ expect ( firefox . requestSignedInUser ) . not . toHaveBeenCalled ( ) ;
735+ expect ( storageUtils . persistAccount ) . not . toHaveBeenCalled ( ) ;
736+ } ) ;
737+
738+ it ( 'does not query the browser when not running in Firefox' , async ( ) => {
739+ isProbablyFirefoxSpy . mockReturnValue ( false ) ;
740+ jest . spyOn ( cache , 'currentAccount' ) . mockReturnValue ( undefined ) ;
741+ jest . spyOn ( cache , 'lastStoredAccount' ) . mockReturnValue ( undefined ) ;
742+
743+ renderWithLocalizationProvider (
744+ < LocationProvider >
745+ < IndexContainer
746+ { ...{
747+ integration,
748+ serviceName : MozServices . Default ,
749+ useFxAStatusResult : mockUseFxAStatusResult ,
750+ } }
751+ />
752+ </ LocationProvider >
753+ ) ;
754+
755+ await waitFor ( ( ) => {
756+ expect ( IndexModule . default ) . toHaveBeenCalled ( ) ;
757+ } ) ;
758+ expect ( firefox . requestSignedInUser ) . not . toHaveBeenCalled ( ) ;
759+ } ) ;
760+
761+ it ( 'does not persist when the browser returns no signed-in user' , async ( ) => {
762+ jest . spyOn ( cache , 'currentAccount' ) . mockReturnValue ( undefined ) ;
763+ jest . spyOn ( cache , 'lastStoredAccount' ) . mockReturnValue ( undefined ) ;
764+ ( firefox . requestSignedInUser as jest . Mock ) . mockResolvedValue ( undefined ) ;
765+
766+ renderWithLocalizationProvider (
767+ < LocationProvider >
768+ < IndexContainer
769+ { ...{
770+ integration,
771+ serviceName : MozServices . Default ,
772+ useFxAStatusResult : mockUseFxAStatusResult ,
773+ } }
774+ />
775+ </ LocationProvider >
776+ ) ;
777+
778+ await waitFor ( ( ) => {
779+ expect ( firefox . requestSignedInUser ) . toHaveBeenCalled ( ) ;
780+ } ) ;
781+ expect ( storageUtils . persistAccount ) . not . toHaveBeenCalled ( ) ;
782+ expect ( storageUtils . setCurrentAccount ) . not . toHaveBeenCalled ( ) ;
783+ } ) ;
784+ } ) ;
785+
609786 describe ( 'processEmailSubmission' , ( ) => {
610787 describe ( 'success' , ( ) => {
611788 it ( 'with a new valid email' , async ( ) => {
0 commit comments