139 lines
4.8 KiB
TypeScript
139 lines
4.8 KiB
TypeScript
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
import * as React from "react";
|
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
import { render } from "vitest-browser-react";
|
|
|
|
// Mock API
|
|
vi.mock("../../src/lib/api/client", async () => {
|
|
const actual = await vi.importActual("../../src/lib/api/client");
|
|
return {
|
|
...actual,
|
|
apiFetch: vi.fn().mockImplementation((url: string) => {
|
|
if (url.includes("/setup/status")) {
|
|
return Promise.resolve(
|
|
new Response(JSON.stringify({ data: { needsSetup: true, authMode: "passkey" } }), {
|
|
status: 200,
|
|
}),
|
|
);
|
|
}
|
|
if (url.includes("/setup/admin")) {
|
|
return Promise.resolve(
|
|
new Response(JSON.stringify({ data: { success: true } }), { status: 200 }),
|
|
);
|
|
}
|
|
if (url.includes("/setup") && !url.includes("status")) {
|
|
return Promise.resolve(
|
|
new Response(JSON.stringify({ data: { success: true } }), { status: 200 }),
|
|
);
|
|
}
|
|
return Promise.resolve(new Response(JSON.stringify({ data: {} }), { status: 200 }));
|
|
}),
|
|
};
|
|
});
|
|
|
|
// Mock WebAuthn so PasskeyRegistration doesn't bail out
|
|
Object.defineProperty(window, "PublicKeyCredential", {
|
|
value: function PublicKeyCredential() {},
|
|
writable: true,
|
|
});
|
|
|
|
// Import after mocks
|
|
const { SetupWizard } = await import("../../src/components/SetupWizard");
|
|
|
|
function QueryWrapper({ children }: { children: React.ReactNode }) {
|
|
const qc = new QueryClient({
|
|
defaultOptions: { queries: { retry: false }, mutations: { retry: false } },
|
|
});
|
|
return <QueryClientProvider client={qc}>{children}</QueryClientProvider>;
|
|
}
|
|
|
|
describe("SetupWizard", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it("shows site setup step first with title input", async () => {
|
|
const screen = await render(
|
|
<QueryWrapper>
|
|
<SetupWizard />
|
|
</QueryWrapper>,
|
|
);
|
|
await expect.element(screen.getByText("Set up your site")).toBeInTheDocument();
|
|
await expect.element(screen.getByPlaceholder("My Awesome Blog")).toBeInTheDocument();
|
|
});
|
|
|
|
it("empty title prevents advancing and shows validation error", async () => {
|
|
const screen = await render(
|
|
<QueryWrapper>
|
|
<SetupWizard />
|
|
</QueryWrapper>,
|
|
);
|
|
// Wait for setup status to load
|
|
await expect.element(screen.getByText("Set up your site")).toBeInTheDocument();
|
|
// Click Continue without filling title
|
|
await screen.getByText("Continue →").click();
|
|
await expect.element(screen.getByText("Site title is required")).toBeInTheDocument();
|
|
// Should still be on site step
|
|
await expect.element(screen.getByText("Set up your site")).toBeInTheDocument();
|
|
});
|
|
|
|
it("filling title and clicking Next advances to admin step", async () => {
|
|
const screen = await render(
|
|
<QueryWrapper>
|
|
<SetupWizard />
|
|
</QueryWrapper>,
|
|
);
|
|
await expect.element(screen.getByText("Set up your site")).toBeInTheDocument();
|
|
// Fill in the title
|
|
await screen.getByPlaceholder("My Awesome Blog").fill("Test Site");
|
|
// Click continue
|
|
await screen.getByText("Continue →").click();
|
|
// Should advance to admin step
|
|
await expect.element(screen.getByText("Create your account")).toBeInTheDocument();
|
|
});
|
|
|
|
it("admin step shows email input", async () => {
|
|
const screen = await render(
|
|
<QueryWrapper>
|
|
<SetupWizard />
|
|
</QueryWrapper>,
|
|
);
|
|
await expect.element(screen.getByText("Set up your site")).toBeInTheDocument();
|
|
// Fill title and advance
|
|
await screen.getByPlaceholder("My Awesome Blog").fill("Test Site");
|
|
await screen.getByText("Continue →").click();
|
|
// Should see email input on admin step
|
|
await expect.element(screen.getByText("Create your account")).toBeInTheDocument();
|
|
await expect.element(screen.getByPlaceholder("you@example.com")).toBeInTheDocument();
|
|
});
|
|
|
|
it("empty email prevents advancing on admin step", async () => {
|
|
const screen = await render(
|
|
<QueryWrapper>
|
|
<SetupWizard />
|
|
</QueryWrapper>,
|
|
);
|
|
await expect.element(screen.getByText("Set up your site")).toBeInTheDocument();
|
|
// Fill title and advance
|
|
await screen.getByPlaceholder("My Awesome Blog").fill("Test Site");
|
|
await screen.getByText("Continue →").click();
|
|
await expect.element(screen.getByText("Create your account")).toBeInTheDocument();
|
|
// Click Continue without filling email
|
|
await screen.getByText("Continue →").click();
|
|
// Should show validation error
|
|
await expect.element(screen.getByText("Email is required")).toBeInTheDocument();
|
|
});
|
|
|
|
it("step indicator shows three steps", async () => {
|
|
const screen = await render(
|
|
<QueryWrapper>
|
|
<SetupWizard />
|
|
</QueryWrapper>,
|
|
);
|
|
await expect.element(screen.getByText("Set up your site")).toBeInTheDocument();
|
|
// Step indicator labels - use exact matching via role
|
|
await expect.element(screen.getByText("Account")).toBeInTheDocument();
|
|
await expect.element(screen.getByText("Passkey")).toBeInTheDocument();
|
|
});
|
|
});
|