1
1
import { 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" ;
4
4
import { createMockUI } from "~/tests/utils" ;
5
+ import { getBehavior } from "~/behaviors" ;
5
6
6
7
vi . mock ( "firebase/auth" , ( ) => ( {
7
8
linkWithCredential : vi . fn ( ) ,
8
9
linkWithRedirect : vi . fn ( ) ,
9
10
} ) ) ;
10
11
12
+ vi . mock ( "~/behaviors" , ( ) => ( {
13
+ getBehavior : vi . fn ( ) ,
14
+ } ) ) ;
15
+
11
16
beforeEach ( ( ) => {
12
17
vi . clearAllMocks ( ) ;
13
18
} ) ;
@@ -30,6 +35,38 @@ describe("autoUpgradeAnonymousCredentialHandler", () => {
30
35
expect ( result ) . toBe ( mockResult ) ;
31
36
} ) ;
32
37
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
+
33
70
it ( "should not upgrade when user is not anonymous" , async ( ) => {
34
71
const mockUser = { isAnonymous : false , uid : "regular-user-123" } as User ;
35
72
const mockAuth = { currentUser : mockUser } as Auth ;
@@ -62,14 +99,55 @@ describe("autoUpgradeAnonymousProviderHandler", () => {
62
99
const mockAuth = { currentUser : mockUser } as Auth ;
63
100
const mockUI = createMockUI ( { auth : mockAuth } ) ;
64
101
const mockProvider = { providerId : "google.com" } as AuthProvider ;
102
+ const mockResult = { user : { uid : "upgraded-123" } } as UserCredential ;
65
103
66
- vi . mocked ( linkWithRedirect ) . mockResolvedValue ( { } as never ) ;
104
+ const mockProviderLinkStrategy = vi . fn ( ) . mockResolvedValue ( mockResult ) ;
105
+ vi . mocked ( getBehavior ) . mockReturnValue ( mockProviderLinkStrategy ) ;
67
106
68
- await autoUpgradeAnonymousProviderHandler ( mockUI , mockProvider ) ;
107
+ const localStorageSpy = vi . spyOn ( Storage . prototype , 'setItem' ) ;
108
+ const localStorageRemoveSpy = vi . spyOn ( Storage . prototype , 'removeItem' ) ;
69
109
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" ) ;
73
151
} ) ;
74
152
75
153
it ( "should not upgrade when user is not anonymous" , async ( ) => {
@@ -95,3 +173,77 @@ describe("autoUpgradeAnonymousProviderHandler", () => {
95
173
expect ( mockUI . setState ) . not . toHaveBeenCalled ( ) ;
96
174
} ) ;
97
175
} ) ;
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