Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 78 additions & 5 deletions packages/core/src/auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
signInWithCredential,
signInAnonymously,
signInWithProvider,
signInWithCustomToken,
generateTotpQrCode,
} from "./auth";

Expand All @@ -19,6 +20,7 @@ vi.mock("firebase/auth", () => ({
sendPasswordResetEmail: vi.fn(),
sendSignInLinkToEmail: vi.fn(),
signInAnonymously: vi.fn(),
signInWithCustomToken: vi.fn(),
signInWithRedirect: vi.fn(),
isSignInWithEmailLink: vi.fn(),
EmailAuthProvider: {
Expand Down Expand Up @@ -49,6 +51,7 @@ import {
sendPasswordResetEmail as _sendPasswordResetEmail,
sendSignInLinkToEmail as _sendSignInLinkToEmail,
signInAnonymously as _signInAnonymously,
signInWithCustomToken as _signInWithCustomToken,
isSignInWithEmailLink as _isSignInWithEmailLink,
UserCredential,
Auth,
Expand Down Expand Up @@ -958,14 +961,11 @@ describe("signInAnonymously", () => {

const result = await signInAnonymously(mockUI);

// Verify state management
expect(vi.mocked(mockUI.setState).mock.calls).toEqual([["pending"], ["idle"]]);

// Verify the Firebase function was called with correct parameters
expect(_signInAnonymously).toHaveBeenCalledWith(mockUI.auth);
expect(_signInAnonymously).toHaveBeenCalledTimes(1);

// Verify the result
expect(result).toEqual(mockUserCredential);
});

Expand All @@ -977,10 +977,83 @@ describe("signInAnonymously", () => {

await signInAnonymously(mockUI);

// Verify error handling
expect(handleFirebaseError).toHaveBeenCalledWith(mockUI, error);

// Verify state management still happens
expect(vi.mocked(mockUI.setState).mock.calls).toEqual([["pending"], ["idle"]]);
});
});

describe("signInWithCustomToken", () => {
beforeEach(() => {
vi.clearAllMocks();
});

it("should update state and call signInWithCustomToken successfully", async () => {
const mockUI = createMockUI();
const customToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...";
const mockUserCredential = {
user: { uid: "custom-user-uid", email: "user@example.com" },
providerId: "custom",
operationType: "signIn",
} as UserCredential;

vi.mocked(_signInWithCustomToken).mockResolvedValue(mockUserCredential);

const result = await signInWithCustomToken(mockUI, customToken);

expect(mockUI.setRedirectError).toHaveBeenCalledWith(undefined);
expect(vi.mocked(mockUI.setState).mock.calls).toEqual([["pending"], ["idle"]]);

expect(_signInWithCustomToken).toHaveBeenCalledWith(mockUI.auth, customToken);
expect(_signInWithCustomToken).toHaveBeenCalledTimes(1);

expect(result).toEqual(mockUserCredential);
});

it("should call handleFirebaseError if an error is thrown", async () => {
const mockUI = createMockUI();
const customToken = "invalid-token";
const error = new FirebaseError("auth/invalid-custom-token", "Invalid custom token");

vi.mocked(_signInWithCustomToken).mockRejectedValue(error);

await signInWithCustomToken(mockUI, customToken);

expect(mockUI.setRedirectError).toHaveBeenCalledWith(undefined);
expect(handleFirebaseError).toHaveBeenCalledWith(mockUI, error);

expect(vi.mocked(mockUI.setState).mock.calls).toEqual([["pending"], ["idle"]]);
});

it("should handle network errors", async () => {
const mockUI = createMockUI();
const customToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...";
const error = new Error("Network error");

vi.mocked(_signInWithCustomToken).mockRejectedValue(error);

await signInWithCustomToken(mockUI, customToken);

// Verify redirect error is cleared even when network error occurs
expect(mockUI.setRedirectError).toHaveBeenCalledWith(undefined);
expect(handleFirebaseError).toHaveBeenCalledWith(mockUI, error);

expect(vi.mocked(mockUI.setState).mock.calls).toEqual([["pending"], ["idle"]]);
});

it("should handle expired custom token", async () => {
const mockUI = createMockUI();
const customToken = "expired-token";
const error = new FirebaseError("auth/custom-token-mismatch", "Custom token expired");

vi.mocked(_signInWithCustomToken).mockRejectedValue(error);

await signInWithCustomToken(mockUI, customToken);

// Verify redirect error is cleared even when token is expired
expect(mockUI.setRedirectError).toHaveBeenCalledWith(undefined);
expect(handleFirebaseError).toHaveBeenCalledWith(mockUI, error);

expect(vi.mocked(mockUI.setState).mock.calls).toEqual([["pending"], ["idle"]]);
});
});
Expand Down
13 changes: 13 additions & 0 deletions packages/core/src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
sendSignInLinkToEmail as _sendSignInLinkToEmail,
signInAnonymously as _signInAnonymously,
signInWithCredential as _signInWithCredential,
signInWithCustomToken as _signInWithCustomToken,
EmailAuthProvider,
linkWithCredential,
PhoneAuthProvider,
Expand Down Expand Up @@ -254,6 +255,18 @@ export async function signInWithCredential(ui: FirebaseUI, credential: AuthCrede
}
}

export async function signInWithCustomToken(ui: FirebaseUI, customToken: string): Promise<UserCredential> {
try {
setPendingState(ui);
const result = await _signInWithCustomToken(ui.auth, customToken);
return handlePendingCredential(ui, result);
} catch (error) {
handleFirebaseError(ui, error);
} finally {
ui.setState("idle");
}
}

export async function signInAnonymously(ui: FirebaseUI): Promise<UserCredential> {
try {
setPendingState(ui);
Expand Down