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