@userfront/core
Version:
Userfront core JS library
249 lines (203 loc) • 6.88 kB
JavaScript
import { vi } from "vitest";
import axios from "axios";
import {
createAccessToken,
createIdToken,
resetStore,
mockWindow,
} from "./config/utils.js";
import { noMfaHeaders } from "./config/assertions.js";
import Userfront from "../src/index.js";
import { setMode } from "../src/mode.js";
import { store } from "../src/store.js";
import { apiUrl } from "../src/constants.js";
const tenantId = "abcd5432";
const domain = "com.example.myapp";
vi.mock("axios");
describe("init() method with domain option", () => {
beforeEach(() => {
// Mock the axios POST response (used for signup & login)
const mockAccessToken = createAccessToken();
const mockIdToken = createIdToken();
axios.post.mockResolvedValue({
status: 200,
data: {
tokens: {
access: {
value: mockAccessToken,
secure: true,
sameSite: "Lax",
expires: 30,
},
id: {
value: mockIdToken,
secure: true,
sameSite: "Lax",
expires: 30,
},
},
},
});
});
afterEach(() => {
resetStore(Userfront);
vi.resetAllMocks();
});
it("should include the x-application-id and x-origin headers for mode", async () => {
store.mode = "test";
Userfront.init(tenantId, { domain });
axios.get.mockResolvedValue({
status: 200,
data: {
mode: "live",
authentication: {
firstFactors: [],
secondFactors: [],
},
},
});
await setMode();
const url = `https://${domain}`;
expect(axios.defaults.headers.common["x-application-id"]).toEqual(url);
expect(axios.defaults.headers.common["x-origin"]).toEqual(url);
expect(axios.get).toHaveBeenCalledWith(
`https://api.userfront.com/v0/tenants/${tenantId}/mode`,
undefined
);
expect(store.mode).toEqual("live");
});
it("should include the x-application-id and x-origin headers for signup", async () => {
Userfront.init(tenantId, { domain });
const email = "user@example.com";
const password = "foobar";
await Userfront.signup({ method: "password", email, password });
const url = `https://${domain}`;
expect(axios.defaults.headers.common["x-application-id"]).toEqual(url);
expect(axios.defaults.headers.common["x-origin"]).toEqual(url);
expect(axios.post).toHaveBeenCalledWith(
`https://api.userfront.com/v0/tenants/${tenantId}/auth/create`,
{
email,
password,
username: undefined,
name: undefined,
data: undefined,
},
noMfaHeaders
);
});
it("should include the x-application-id and x-origin headers for login", async () => {
Userfront.init(tenantId, { domain });
const email = "user@example.com";
const password = "foobar";
await Userfront.login({ method: "password", email, password });
const url = `https://${domain}`;
expect(axios.defaults.headers.common["x-application-id"]).toEqual(url);
expect(axios.defaults.headers.common["x-origin"]).toEqual(url);
expect(axios.post).toHaveBeenCalledWith(
`https://api.userfront.com/v0/tenants/${tenantId}/auth/basic`,
{
emailOrUsername: email,
password,
},
noMfaHeaders
);
});
it("should include the x-application-id and x-origin headers for logout", async () => {
Userfront.init(tenantId, { domain });
axios.get.mockResolvedValue({
status: 200,
data: {
message: "ok",
},
});
const accessTokenBeforeLogout = Userfront.tokens.accessToken;
await Userfront.logout();
const url = `https://${domain}`;
expect(axios.defaults.headers.common["x-application-id"]).toEqual(url);
expect(axios.defaults.headers.common["x-origin"]).toEqual(url);
expect(axios.get).toHaveBeenCalledWith(
"https://api.userfront.com/v0/auth/logout",
{
headers: {
authorization: `Bearer ${accessTokenBeforeLogout}`,
},
}
);
expect(Userfront.tokens.accessToken).toEqual(undefined);
});
});
describe("init() method with baseUrl option", () => {
it("should add baseUrl to store when provided", async () => {
const baseUrl = "https://custom.example.com/api/v1/";
Userfront.init(tenantId, { baseUrl });
expect(store.baseUrl).toEqual(baseUrl);
});
it("should default baseUrl to 'https://api.userfront.com/v0' if empty or not provided", async () => {
// Modify baseUrl to be sure it's changed again in `init`
store.baseUrl = "https://example.com";
Userfront.init(tenantId, { baseUrl: "" });
expect(store.baseUrl).toEqual(apiUrl);
store.baseUrl = "https://example.com";
Userfront.init(tenantId);
expect(store.baseUrl).toEqual(apiUrl);
});
it("should support a baseUrl with trailing slash", async () => {
const customBaseUrl = "https://custom.example.com/";
Userfront.init(tenantId, {
baseUrl: customBaseUrl,
});
expect(store.baseUrl).toEqual(customBaseUrl);
store.tokens.accessToken = "foobar";
await Userfront.logout();
expect(axios.get).toHaveBeenCalledWith(`${customBaseUrl}auth/logout`, {
headers: {
authorization: `Bearer foobar`,
},
});
});
it("should support a baseUrl without trailing slash", async () => {
const customBaseUrl = "https://custom.example.com";
Userfront.init(tenantId, {
baseUrl: customBaseUrl,
});
// Check trailing slash was appended
expect(store.baseUrl).toEqual(customBaseUrl + "/");
store.tokens.accessToken = "foobar";
await Userfront.logout();
// Check trailing slash is included when used in request
expect(axios.get).toHaveBeenCalledWith(`${customBaseUrl}/auth/logout`, {
headers: {
authorization: `Bearer foobar`,
},
});
});
});
describe("addInitCallback() method", () => {
beforeAll(() => {
mockWindow({
origin: "https://example.com",
href: "https://example.com/login",
});
});
it("should add callbacks that are fired when Userfront.init(tenantId) is called", () => {
const tenantId = "a9b8c7d6";
const callbackA = vi.fn();
const callbackB = vi.fn();
// Add callbacks
Userfront.addInitCallback(callbackA);
Userfront.addInitCallback(callbackB);
// Call Userfront.init()
Userfront.init(tenantId);
// Assert that callbacks were called
expect(callbackA).toHaveBeenCalled();
expect(callbackA).toHaveBeenCalledWith({ tenantId });
expect(callbackB).toHaveBeenCalled();
expect(callbackB).toHaveBeenCalledWith({ tenantId });
// Calling Userfront.init() again should not call the callbacks again
vi.clearAllMocks();
Userfront.init(tenantId);
expect(callbackA).not.toHaveBeenCalled();
expect(callbackB).not.toHaveBeenCalled();
});
});