11import { describe , it , expect , vi , beforeEach } from "vitest" ;
2- import { Auth , AuthCredential , AuthProvider , linkWithCredential , linkWithRedirect , User } from "firebase/auth" ;
3- import { autoUpgradeAnonymousCredentialHandler , autoUpgradeAnonymousProviderHandler } from "./anonymous-upgrade" ;
2+ import { Auth , AuthCredential , AuthProvider , linkWithCredential , linkWithRedirect , User , UserCredential } from "firebase/auth" ;
3+ import { autoUpgradeAnonymousCredentialHandler , autoUpgradeAnonymousProviderHandler , autoUpgradeAnonymousUserRedirectHandler , OnUpgradeCallback } from "./anonymous-upgrade" ;
44import { createMockUI } from "~/tests/utils" ;
5+ import { getBehavior } from "~/behaviors" ;
56
67vi . mock ( "firebase/auth" , ( ) => ( {
78 linkWithCredential : vi . fn ( ) ,
89 linkWithRedirect : vi . fn ( ) ,
910} ) ) ;
1011
12+ vi . mock ( "~/behaviors" , ( ) => ( {
13+ getBehavior : vi . fn ( ) ,
14+ } ) ) ;
15+
1116beforeEach ( ( ) => {
1217 vi . clearAllMocks ( ) ;
1318} ) ;
@@ -30,6 +35,38 @@ describe("autoUpgradeAnonymousCredentialHandler", () => {
3035 expect ( result ) . toBe ( mockResult ) ;
3136 } ) ;
3237
38+ it ( "should call onUpgrade callback when provided" , async ( ) => {
39+ const mockUser = { isAnonymous : true , uid : "anonymous-123" } as User ;
40+ const mockAuth = { currentUser : mockUser } as Auth ;
41+ const mockUI = createMockUI ( { auth : mockAuth } ) ;
42+ const mockCredential = { providerId : "password" } as AuthCredential ;
43+ const mockResult = { user : { uid : "upgraded-123" } } as UserCredential ;
44+
45+ vi . mocked ( linkWithCredential ) . mockResolvedValue ( mockResult ) ;
46+
47+ const onUpgrade = vi . fn ( ) . mockResolvedValue ( undefined ) ;
48+
49+ const result = await autoUpgradeAnonymousCredentialHandler ( mockUI , mockCredential , onUpgrade ) ;
50+
51+ expect ( onUpgrade ) . toHaveBeenCalledWith ( mockUI , "anonymous-123" , mockResult ) ;
52+ expect ( result ) . toBe ( mockResult ) ;
53+ } ) ;
54+
55+ it ( "should handle onUpgrade callback errors" , async ( ) => {
56+ const mockUser = { isAnonymous : true , uid : "anonymous-123" } as User ;
57+ const mockAuth = { currentUser : mockUser } as Auth ;
58+ const mockUI = createMockUI ( { auth : mockAuth } ) ;
59+ const mockCredential = { providerId : "password" } as AuthCredential ;
60+ const mockResult = { user : { uid : "upgraded-123" } } as UserCredential ;
61+
62+ vi . mocked ( linkWithCredential ) . mockResolvedValue ( mockResult ) ;
63+
64+ const onUpgrade = vi . fn ( ) . mockRejectedValue ( new Error ( "Callback error" ) ) ;
65+
66+ await expect ( autoUpgradeAnonymousCredentialHandler ( mockUI , mockCredential , onUpgrade ) )
67+ . rejects . toThrow ( "Callback error" ) ;
68+ } ) ;
69+
3370 it ( "should not upgrade when user is not anonymous" , async ( ) => {
3471 const mockUser = { isAnonymous : false , uid : "regular-user-123" } as User ;
3572 const mockAuth = { currentUser : mockUser } as Auth ;
@@ -62,14 +99,55 @@ describe("autoUpgradeAnonymousProviderHandler", () => {
6299 const mockAuth = { currentUser : mockUser } as Auth ;
63100 const mockUI = createMockUI ( { auth : mockAuth } ) ;
64101 const mockProvider = { providerId : "google.com" } as AuthProvider ;
102+ const mockResult = { user : { uid : "upgraded-123" } } as UserCredential ;
65103
66- vi . mocked ( linkWithRedirect ) . mockResolvedValue ( { } as never ) ;
104+ const mockProviderLinkStrategy = vi . fn ( ) . mockResolvedValue ( mockResult ) ;
105+ vi . mocked ( getBehavior ) . mockReturnValue ( mockProviderLinkStrategy ) ;
67106
68- await autoUpgradeAnonymousProviderHandler ( mockUI , mockProvider ) ;
107+ const localStorageSpy = vi . spyOn ( Storage . prototype , 'setItem' ) ;
108+ const localStorageRemoveSpy = vi . spyOn ( Storage . prototype , 'removeItem' ) ;
69109
70- expect ( linkWithRedirect ) . toHaveBeenCalledWith ( mockUser , mockProvider ) ;
71- expect ( mockUI . setState ) . toHaveBeenCalledWith ( "pending" ) ;
72- expect ( mockUI . setState ) . not . toHaveBeenCalledWith ( "idle" ) ;
110+ const result = await autoUpgradeAnonymousProviderHandler ( mockUI , mockProvider ) ;
111+
112+ expect ( getBehavior ) . toHaveBeenCalledWith ( mockUI , "providerLinkStrategy" ) ;
113+ expect ( mockProviderLinkStrategy ) . toHaveBeenCalledWith ( mockUI , mockUser , mockProvider ) ;
114+ expect ( localStorageSpy ) . toHaveBeenCalledWith ( "fbui:upgrade:oldUserId" , "anonymous-123" ) ;
115+ expect ( localStorageRemoveSpy ) . toHaveBeenCalledWith ( "fbui:upgrade:oldUserId" ) ;
116+ expect ( result ) . toBe ( mockResult ) ;
117+ } ) ;
118+
119+ it ( "should call onUpgrade callback when provided" , async ( ) => {
120+ const mockUser = { isAnonymous : true , uid : "anonymous-123" } as User ;
121+ const mockAuth = { currentUser : mockUser } as Auth ;
122+ const mockUI = createMockUI ( { auth : mockAuth } ) ;
123+ const mockProvider = { providerId : "google.com" } as AuthProvider ;
124+ const mockResult = { user : { uid : "upgraded-123" } } as UserCredential ;
125+
126+ const mockProviderLinkStrategy = vi . fn ( ) . mockResolvedValue ( mockResult ) ;
127+ vi . mocked ( getBehavior ) . mockReturnValue ( mockProviderLinkStrategy ) ;
128+
129+ const onUpgrade = vi . fn ( ) . mockResolvedValue ( undefined ) ;
130+
131+ const result = await autoUpgradeAnonymousProviderHandler ( mockUI , mockProvider , onUpgrade ) ;
132+
133+ expect ( onUpgrade ) . toHaveBeenCalledWith ( mockUI , "anonymous-123" , mockResult ) ;
134+ expect ( result ) . toBe ( mockResult ) ;
135+ } ) ;
136+
137+ it ( "should handle onUpgrade callback errors" , async ( ) => {
138+ const mockUser = { isAnonymous : true , uid : "anonymous-123" } as User ;
139+ const mockAuth = { currentUser : mockUser } as Auth ;
140+ const mockUI = createMockUI ( { auth : mockAuth } ) ;
141+ const mockProvider = { providerId : "google.com" } as AuthProvider ;
142+ const mockResult = { user : { uid : "upgraded-123" } } as UserCredential ;
143+
144+ const mockProviderLinkStrategy = vi . fn ( ) . mockResolvedValue ( mockResult ) ;
145+ vi . mocked ( getBehavior ) . mockReturnValue ( mockProviderLinkStrategy ) ;
146+
147+ const onUpgrade = vi . fn ( ) . mockRejectedValue ( new Error ( "Callback error" ) ) ;
148+
149+ await expect ( autoUpgradeAnonymousProviderHandler ( mockUI , mockProvider , onUpgrade ) )
150+ . rejects . toThrow ( "Callback error" ) ;
73151 } ) ;
74152
75153 it ( "should not upgrade when user is not anonymous" , async ( ) => {
@@ -95,3 +173,77 @@ describe("autoUpgradeAnonymousProviderHandler", () => {
95173 expect ( mockUI . setState ) . not . toHaveBeenCalled ( ) ;
96174 } ) ;
97175} ) ;
176+
177+ describe ( "autoUpgradeAnonymousUserRedirectHandler" , ( ) => {
178+ beforeEach ( ( ) => {
179+ window . localStorage . clear ( ) ;
180+ } ) ;
181+
182+ it ( "should call onUpgrade callback when oldUserId exists in localStorage" , async ( ) => {
183+ const mockUI = createMockUI ( ) ;
184+ const mockCredential = { user : { uid : "upgraded-123" } } as UserCredential ;
185+ const oldUserId = "anonymous-123" ;
186+
187+ window . localStorage . setItem ( "fbui:upgrade:oldUserId" , oldUserId ) ;
188+
189+ const onUpgrade = vi . fn ( ) . mockResolvedValue ( undefined ) ;
190+
191+ await autoUpgradeAnonymousUserRedirectHandler ( mockUI , mockCredential , onUpgrade ) ;
192+
193+ expect ( onUpgrade ) . toHaveBeenCalledWith ( mockUI , oldUserId , mockCredential ) ;
194+ expect ( window . localStorage . getItem ( "fbui:upgrade:oldUserId" ) ) . toBeNull ( ) ;
195+ } ) ;
196+
197+ it ( "should not call onUpgrade callback when no oldUserId in localStorage" , async ( ) => {
198+ const mockUI = createMockUI ( ) ;
199+ const mockCredential = { user : { uid : "upgraded-123" } } as UserCredential ;
200+
201+ const onUpgrade = vi . fn ( ) . mockResolvedValue ( undefined ) ;
202+
203+ await autoUpgradeAnonymousUserRedirectHandler ( mockUI , mockCredential , onUpgrade ) ;
204+
205+ expect ( onUpgrade ) . not . toHaveBeenCalled ( ) ;
206+ } ) ;
207+
208+ it ( "should not call onUpgrade callback when no credential provided" , async ( ) => {
209+ const mockUI = createMockUI ( ) ;
210+ const oldUserId = "anonymous-123" ;
211+
212+ window . localStorage . setItem ( "fbui:upgrade:oldUserId" , oldUserId ) ;
213+
214+ const onUpgrade = vi . fn ( ) . mockResolvedValue ( undefined ) ;
215+
216+ await autoUpgradeAnonymousUserRedirectHandler ( mockUI , null , onUpgrade ) ;
217+
218+ expect ( onUpgrade ) . not . toHaveBeenCalled ( ) ;
219+ } ) ;
220+
221+ it ( "should not call onUpgrade callback when no onUpgrade callback provided" , async ( ) => {
222+ const mockUI = createMockUI ( ) ;
223+ const mockCredential = { user : { uid : "upgraded-123" } } as UserCredential ;
224+ const oldUserId = "anonymous-123" ;
225+
226+ window . localStorage . setItem ( "fbui:upgrade:oldUserId" , oldUserId ) ;
227+
228+ await autoUpgradeAnonymousUserRedirectHandler ( mockUI , mockCredential ) ;
229+
230+ // Should not throw and should clean up localStorage even when no callback provided
231+ expect ( window . localStorage . getItem ( "fbui:upgrade:oldUserId" ) ) . toBeNull ( ) ;
232+ } ) ;
233+
234+ it ( "should handle onUpgrade callback errors" , async ( ) => {
235+ const mockUI = createMockUI ( ) ;
236+ const mockCredential = { user : { uid : "upgraded-123" } } as UserCredential ;
237+ const oldUserId = "anonymous-123" ;
238+
239+ window . localStorage . setItem ( "fbui:upgrade:oldUserId" , oldUserId ) ;
240+
241+ const onUpgrade = vi . fn ( ) . mockRejectedValue ( new Error ( "Callback error" ) ) ;
242+
243+ await expect ( autoUpgradeAnonymousUserRedirectHandler ( mockUI , mockCredential , onUpgrade ) )
244+ . rejects . toThrow ( "Callback error" ) ;
245+
246+ // Should clean up localStorage even when callback throws error
247+ expect ( window . localStorage . getItem ( "fbui:upgrade:oldUserId" ) ) . toBeNull ( ) ;
248+ } ) ;
249+ } ) ;
0 commit comments