@userfront/core
Version:
Userfront core JS library
287 lines (255 loc) • 9.2 kB
JavaScript
import { vi } from "vitest";
import Userfront from "../src/index.js";
import { login } from "../src/login.js";
import { loginWithPassword } from "../src/password.js";
import { loginWithPasswordMigrate } from "../src/password.migrate.js";
import { sendPasswordlessLink, loginWithLink } from "../src/link.js";
import { signonWithSso } from "../src/sso.js";
import { loginWithTotp } from "../src/totp.js";
import { loginWithVerificationCode } from "../src/verificationCode.js";
import { completeSamlLogin } from "../src/saml.js";
import { defaultHandleRedirect } from "../src/url.js";
// Mock all methods to be called
vi.mock("../src/password.js");
vi.mock("../src/password.migrate.js");
vi.mock("../src/link.js");
vi.mock("../src/sso.js");
vi.mock("../src/totp.js");
vi.mock("../src/verificationCode.js");
vi.mock("../src/saml.js");
vi.mock("../src/url.js");
const tenantId = "abcd9876";
describe("login()", () => {
beforeEach(() => {
Userfront.init(tenantId);
vi.resetAllMocks();
});
it(`{ method: undefined } should throw an error`, () => {
expect(login()).rejects.toEqual(
new Error(`Userfront.login called without "method" property.`)
);
expect(defaultHandleRedirect).not.toHaveBeenCalled();
});
describe(`{ method: "password" }`, () => {
it(`should call loginWithPassword()`, () => {
const email = "user@example.com";
const password = "some-password123";
const handleUpstreamResponse = vi.fn();
const handleTokens = vi.fn();
const handleRedirect = vi.fn();
const combos = [
{ email, password },
{ username: "user-name", password },
{ username: "user-name", password, handleUpstreamResponse },
{ username: "user-name", password, handleTokens },
{ username: "user-name", password, handleRedirect },
{ emailOrUsername: email, password },
{ email, password, redirect: "/custom" },
{ email, password, redirect: false, options: { noResetEmail: true } },
];
// Test login for each combo
combos.forEach((combo) => {
// Call login for the combo
Userfront.login({ method: "password", ...combo });
// Assert that loginWithPassword was called correctly
expect(loginWithPassword).toHaveBeenCalledWith(combo);
});
});
});
describe(`{ method: "password-migrate" }`, () => {
it(`should call loginWithPasswordMigrate()`, () => {
const email = "user@example.com";
const password = "some-password123";
const handleUpstreamResponse = vi.fn();
const handleTokens = vi.fn();
const handleRedirect = vi.fn();
const combos = [
{ email, password },
{ username: "user-name", password },
{ username: "user-name", password, handleUpstreamResponse },
{ username: "user-name", password, handleTokens },
{ username: "user-name", password, handleRedirect },
{ emailOrUsername: email, password },
{ email, password, redirect: "/custom" },
{ email, password, redirect: false, options: { noResetEmail: true } },
];
// Test login for each combo
combos.forEach((combo) => {
// Call login for the combo
Userfront.login({ method: "password-migrate", ...combo });
// Assert that loginWithPassword was called correctly
expect(loginWithPasswordMigrate).toHaveBeenCalledWith(combo);
});
});
});
describe(`{ method: "google" } and other SSO providers`, () => {
it(`should call signonWithSso()`, () => {
const combos = [
{ method: "apple" },
{ method: "azure" },
{ method: "facebook" },
{ method: "github" },
{ method: "google", redirect: "/after-google" },
{ method: "linkedin", redirect: false },
{ method: "okta" },
];
// Test login for each provider
combos.forEach((combo) => {
// Call login for the combo
Userfront.login(combo);
// Assert that signonWithSso was called correctly
expect(signonWithSso).toHaveBeenCalledWith({
provider: combo.method,
redirect: combo.redirect,
});
});
});
});
describe(`{ method: "custom" }`, () => {
it(`should call signonWithSso() with provider ID`, () => {
// Call login with the custom provider's ID
const providerId = "fake-provider-id";
Userfront.login({ method: "custom", providerId });
// Assert that signonWithSso was called correctly
expect(signonWithSso).toHaveBeenCalledWith({
provider: "custom",
providerId,
});
});
it(`should call signonWithSso() with redirect`, () => {
// Call login with the custom provider's ID & redirect
const providerId = "fake-provider-id-1";
const redirect = "/custom-path";
Userfront.login({ method: "custom", redirect, providerId });
// Assert that signonWithSso was called correctly
expect(signonWithSso).toHaveBeenCalledWith({
provider: "custom",
redirect,
providerId,
});
});
});
describe(`{ method: "passwordless" }`, () => {
it(`should call sendPasswordlessLink()`, () => {
const email = "user@example.com";
const combos = [
{ email },
{
email,
name: "First Last",
username: "user-name",
data: {
custom: "data",
},
},
];
// Test login for each combo
combos.forEach((combo) => {
// Call login for the combo
Userfront.login({ method: "passwordless", ...combo });
// Assert that sendPasswordlessLink was called with only the email
expect(sendPasswordlessLink).toHaveBeenCalledWith({
email: combo.email,
});
});
});
});
describe(`{ method: "link" }`, () => {
it(`should call loginWithLink()`, () => {
const token = "some-token";
const uuid = "some-uuid";
const handleUpstreamResponse = vi.fn();
const handleTokens = vi.fn();
const handleRedirect = vi.fn();
const combos = [
{ token, uuid },
{ token, uuid, handleUpstreamResponse },
{ token, uuid, handleTokens },
{ token, uuid, handleRedirect },
{ token, uuid, redirect: "/custom" },
{ token, uuid, redirect: false },
];
// Test login for each combo
combos.forEach((combo) => {
// Call login for the combo
Userfront.login({ method: "link", ...combo });
// Assert that loginWithLink was called
expect(loginWithLink).toHaveBeenCalledWith(combo);
});
});
});
describe(`{ method: "totp" }`, () => {
const codeAttrs = [{ totpCode: "991234" }, { backupCode: "11111-aaaaa" }];
const identifierAttrs = [
{ userId: 222 },
{ userUuid: "326381e1-30b8-4280-93b6-ea27b2078966" },
{ emailOrUsername: "myusername" },
{ email: "user@example.com" },
{ username: "ausername" },
{ phoneNumber: "+15558675309" },
];
// Loop over all input combos and ensure that loginWithTotp is called correctly for each
codeAttrs.map((codeAttr) => {
identifierAttrs.map((identifierAttr) => {
it(`should call loginWithTotp with ${codeAttr} and ${identifierAttr}`, () => {
// Call login for the combo
Userfront.login({ method: "totp", ...codeAttr, ...identifierAttr });
// Assert that loginWithTotp was called correctly
expect(loginWithTotp).toHaveBeenCalledWith({
...codeAttr,
...identifierAttr,
});
});
});
});
});
describe(`{ method: "verificationCode" }`, () => {
const handleUpstreamResponse = vi.fn();
const handleTokens = vi.fn();
const handleRedirect = vi.fn();
const combos = [
{
channel: "sms",
phoneNumber: "+15552223344",
verificationCode: "456123",
handleUpstreamResponse,
},
{
channel: "email",
email: "user@example.com",
verificationCode: "456123",
handleTokens,
},
{
channel: "sms",
phoneNumber: "+15552223344",
verificationCode: "456123",
redirect: false,
},
{
channel: "email",
email: "user@example.com",
verificationCode: "456123",
redirect: "/custom",
handleRedirect,
},
];
// Loop over all input combos and ensure that loginWithTotp is called correctly for each
combos.map((combo) => {
it(`should call loginWithVerificationCode() with channel=${combo.channel}`, () => {
// Call login for the combo
Userfront.login({ method: "verificationCode", ...combo });
// Assert that loginWithTotp was called correctly
expect(loginWithVerificationCode).toHaveBeenCalledWith(combo);
});
});
});
describe(`{ method: "saml"}`, () => {
it(`should call loginWithSaml()`, () => {
// Call login for the combo
Userfront.login({ method: "saml" });
// Assert that loginWithLink was called
expect(completeSamlLogin).toHaveBeenCalledWith();
});
});
});