Files
buzz-sheet/components/auth/account-form.test.tsx
T
gunshiz f826051438
CI / Verify (push) Failing after 1m22s
CI / Build immutable images and deploy (push) Skipped
feat : add icon and introduce login with google
2026-10-10 23:33:34 +07:00

98 lines
3.6 KiB
TypeScript

// @vitest-environment happy-dom
import { act, createElement } from "react";
import { createRoot, type Root } from "react-dom/client";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
const mocks = vi.hoisted(() => ({ social: vi.fn(), push: vi.fn(), replace: vi.fn(), refresh: vi.fn() }));
vi.mock("next/navigation", () => ({ useRouter: () => ({
push: mocks.push, replace: mocks.replace, refresh: mocks.refresh,
}) }));
vi.mock("@/lib/auth/client", () => ({ authClient: {
signIn: { social: mocks.social },
signUp: { email: vi.fn() },
emailOtp: { sendVerificationOtp: vi.fn(), verifyEmail: vi.fn() },
} }));
import { AccountForm } from "./account-form";
let container: HTMLDivElement;
let root: Root;
beforeEach(() => {
vi.clearAllMocks();
vi.stubEnv("NODE_ENV", "development");
vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
mocks.social.mockResolvedValue({ error: null });
container = document.createElement("div");
document.body.append(container);
root = createRoot(container);
});
afterEach(async () => {
await act(async () => root.unmount());
container.remove();
Reflect.deleteProperty(window, "turnstile");
vi.unstubAllEnvs();
vi.unstubAllGlobals();
});
async function mount(mode: "login" | "register", nextPath = "/commission/tickets?from=login", googleEnabled = true) {
await act(async () => root.render(createElement(AccountForm, {
mode, nextPath, googleEnabled, siteKey: "test-site-key",
})));
}
function googleButton() {
return [...container.querySelectorAll("button")].find((button) => button.textContent?.includes("Google"));
}
describe("Google sign-in form action", () => {
it("offers Google sign-in on login with safe existing-user and new-user callbacks", async () => {
await mount("login");
const button = googleButton();
expect(button).toBeDefined();
expect(button?.querySelector("img")?.getAttribute("src")).toBe("/icon/google.svg");
expect(button?.nextElementSibling?.textContent).toMatch(/สมัครสมาชิก|มีบัญชีแล้ว/);
await act(async () => button!.click());
expect(mocks.social).toHaveBeenCalledWith(expect.objectContaining({
provider: "google",
callbackURL: "/commission/tickets?from=login",
newUserCallbackURL: "/auth/register?step=profile&next=%2Fcommission%2Ftickets%3Ffrom%3Dlogin",
}));
});
it("hides Google sign-in on registration", async () => {
await mount("register");
expect(googleButton()).toBeUndefined();
expect(mocks.social).not.toHaveBeenCalled();
});
it("hides the provider action when Google credentials are not configured", async () => {
await mount("login", "/", false);
expect(googleButton()).toBeUndefined();
expect(mocks.social).not.toHaveBeenCalled();
});
it("passes the Turnstile token to the protected social sign-in endpoint", async () => {
vi.stubEnv("NODE_ENV", "production");
const turnstile = {
render: vi.fn((_container: HTMLElement, options: { callback: (token: string) => void }) => {
options.callback("turnstile-token");
return "widget-id";
}),
remove: vi.fn(),
reset: vi.fn(),
};
Object.defineProperty(window, "turnstile", { configurable: true, value: turnstile });
await mount("login", "/profile");
const button = googleButton();
expect(button?.disabled).toBe(false);
await act(async () => button!.click());
expect(mocks.social).toHaveBeenCalledWith(expect.objectContaining({
provider: "google",
fetchOptions: { headers: { "x-captcha-response": "turnstile-token" } },
}));
});
});