150 lines
4.5 KiB
TypeScript
150 lines
4.5 KiB
TypeScript
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
import { render } from "solid-js/web";
|
|
import type { JSX } from "solid-js";
|
|
|
|
import PasswordInput from "./PasswordInput";
|
|
import SocialAuthButtons from "./SocialAuthButtons";
|
|
import AuthLayout from "./AuthLayout";
|
|
import { Input, Button } from "~/components/ui";
|
|
|
|
function mount(comp: () => JSX.Element): HTMLDivElement {
|
|
const container = document.createElement("div");
|
|
document.body.appendChild(container);
|
|
render(() => comp(), container);
|
|
return container;
|
|
}
|
|
|
|
beforeEach(() => {
|
|
document.body.innerHTML = "";
|
|
if (!globalThis.crypto) {
|
|
Object.defineProperty(globalThis, "crypto", { value: {} });
|
|
}
|
|
(globalThis.crypto as unknown as Record<string, unknown>).randomUUID = vi.fn(
|
|
() => "test-uuid-1234",
|
|
);
|
|
});
|
|
|
|
afterEach(() => {
|
|
document.body.innerHTML = "";
|
|
});
|
|
|
|
describe("PasswordInput", () => {
|
|
it("renders with label", () => {
|
|
mount(() => <PasswordInput label="Password" />);
|
|
expect(document.body.textContent).toContain("Password");
|
|
expect(document.querySelector("label")).toBeTruthy();
|
|
});
|
|
|
|
it("renders password type by default", () => {
|
|
mount(() => <PasswordInput label="Password" />);
|
|
const input = document.querySelector("input")!;
|
|
expect(input.getAttribute("type")).toBe("password");
|
|
});
|
|
|
|
it("toggles visibility when eye icon is clicked", () => {
|
|
mount(() => <PasswordInput label="Password" />);
|
|
const input = document.querySelector("input")!;
|
|
const toggle = document.querySelector("button[aria-label]")!;
|
|
|
|
expect(input.getAttribute("type")).toBe("password");
|
|
expect(toggle.getAttribute("aria-label")).toBe("Show password");
|
|
|
|
toggle.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
|
expect(input.getAttribute("type")).toBe("text");
|
|
expect(toggle.getAttribute("aria-label")).toBe("Hide password");
|
|
|
|
toggle.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
|
expect(input.getAttribute("type")).toBe("password");
|
|
});
|
|
|
|
it("shows error message", () => {
|
|
mount(() => (
|
|
<PasswordInput label="Password" error="Password is required" />
|
|
));
|
|
expect(document.body.textContent).toContain("Password is required");
|
|
});
|
|
|
|
it("shows helper text when no error", () => {
|
|
mount(() => (
|
|
<PasswordInput label="Password" helperText="At least 8 characters" />
|
|
));
|
|
expect(document.body.textContent).toContain("At least 8 characters");
|
|
});
|
|
|
|
it("hides helper text when error is present", () => {
|
|
mount(() => (
|
|
<PasswordInput
|
|
label="Password"
|
|
error="Required"
|
|
helperText="Helper text"
|
|
/>
|
|
));
|
|
expect(document.body.textContent).toContain("Required");
|
|
expect(document.body.textContent).not.toContain("Helper text");
|
|
});
|
|
|
|
it("forwards onInput handler", () => {
|
|
const onInput = vi.fn();
|
|
mount(() => <PasswordInput onInput={onInput} />);
|
|
const input = document.querySelector("input")!;
|
|
input.dispatchEvent(new InputEvent("input", { bubbles: true }));
|
|
expect(onInput).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("SocialAuthButtons", () => {
|
|
it("renders Google and Apple buttons", () => {
|
|
mount(() => <SocialAuthButtons />);
|
|
const buttons = document.querySelectorAll("button");
|
|
expect(buttons.length).toBe(2);
|
|
expect(buttons[0].textContent).toContain("Google");
|
|
expect(buttons[1].textContent).toContain("Apple");
|
|
});
|
|
|
|
it("renders SVG icons in each button", () => {
|
|
mount(() => <SocialAuthButtons />);
|
|
const buttons = document.querySelectorAll("button");
|
|
expect(buttons[0].querySelector("svg")).toBeTruthy();
|
|
expect(buttons[1].querySelector("svg")).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
describe("AuthLayout", () => {
|
|
it("renders children inside the form card", () => {
|
|
mount(() => (
|
|
<AuthLayout>
|
|
<p>Form content</p>
|
|
</AuthLayout>
|
|
));
|
|
expect(document.body.textContent).toContain("Form content");
|
|
});
|
|
|
|
it("renders Kordant branding", () => {
|
|
mount(() => (
|
|
<AuthLayout>
|
|
<p>Content</p>
|
|
</AuthLayout>
|
|
));
|
|
expect(document.body.textContent).toContain("Kordant");
|
|
});
|
|
|
|
it("renders gradient-card wrapper", () => {
|
|
mount(() => (
|
|
<AuthLayout>
|
|
<p>Content</p>
|
|
</AuthLayout>
|
|
));
|
|
expect(document.querySelector(".gradient-card")).toBeTruthy();
|
|
});
|
|
|
|
it("renders testimonial text", () => {
|
|
mount(() => (
|
|
<AuthLayout>
|
|
<p>Content</p>
|
|
</AuthLayout>
|
|
));
|
|
expect(document.body.textContent).toContain("Kordant");
|
|
expect(document.body.textContent).toContain("AI-Powered Identity Protection");
|
|
});
|
|
});
|