UNPKG

better-auth

Version:

The most comprehensive authentication framework for TypeScript.

98 lines (97 loc) 3.41 kB
import { PACKAGE_VERSION } from "../../version.mjs"; import { createGetAuthHeaders, createGetCookies, createLogin } from "./auth-helpers.mjs"; import { createAddMember, createDeleteOrganization, createDeleteUser, createSaveOrganization, createSaveUser } from "./db-helpers.mjs"; import { createOrganizationFactory, createUserFactory } from "./factories.mjs"; import { createOTPStore } from "./otp-sink.mjs"; //#region src/plugins/test-utils/index.ts /** * Test utilities plugin for Better Auth. * * Provides helpers for integration and E2E testing including: * - User/Organization factories (creates objects without DB writes) * - Database helpers (save, delete) * - Auth helpers (login, getAuthHeaders, getCookies) * - OTP capture (when captureOTP: true) * * This plugin does not register public HTTP routes or API endpoints, but it does * expose privileged helpers on `ctx.test` for creating sessions and mutating data. * Prefer including it in a test-only auth instance such as `auth.test.ts` instead * of a production auth config. * * If you conditionally spread it into `plugins`, TypeScript may stop inferring * `ctx.test` correctly. A separate test-only auth instance keeps the helpers * typed without adding the plugin to your production auth config. * * @example * ```ts * import { betterAuth } from "better-auth"; * import { testUtils } from "better-auth/plugins"; * * export const testAuth = betterAuth({ * plugins: [ * testUtils({ captureOTP: true }), * ], * }); * * // In tests, access helpers via context: * const ctx = await testAuth.$context; * const test = ctx.test; * * const user = test.createUser({ email: "test@example.com" }); * const savedUser = await test.saveUser(user); * const { headers, cookies } = await test.login({ userId: user.id }); * ``` */ const testUtils = (options = {}) => { return { id: "test-utils", version: PACKAGE_VERSION, init(ctx) { const hasOrgPlugin = ctx.hasPlugin("organization"); const helpers = { createUser: createUserFactory(ctx), saveUser: createSaveUser(ctx), deleteUser: createDeleteUser(ctx), login: createLogin(ctx), getAuthHeaders: createGetAuthHeaders(ctx), getCookies: createGetCookies(ctx) }; if (hasOrgPlugin) { helpers.createOrganization = createOrganizationFactory(ctx); helpers.saveOrganization = createSaveOrganization(ctx); helpers.deleteOrganization = createDeleteOrganization(ctx); helpers.addMember = createAddMember(ctx); } const otpStore = createOTPStore(); if (options.captureOTP) { helpers.getOTP = otpStore.get; helpers.clearOTPs = otpStore.clear; } const databaseHooks = options.captureOTP ? { verification: { create: { async after(verification) { if (verification?.value && verification?.identifier) { const otpPart = verification.value.split(":")[0]; if (otpPart) { let identifier = verification.identifier; for (const prefix of [ "email-verification-otp-", "sign-in-otp-", "forget-password-otp-", "phone-verification-otp-" ]) if (identifier.startsWith(prefix)) { identifier = identifier.slice(prefix.length); break; } otpStore.capture(identifier, otpPart); } } } } } } : null; return { context: { test: helpers }, options: databaseHooks ? { databaseHooks } : void 0 }; }, options }; }; //#endregion export { testUtils };