Fixes: 1. media.ts: wrap placeholder generation in try-catch 2. toolbar.ts: check r.ok, display error message in popover
245 lines
7.5 KiB
TypeScript
245 lines
7.5 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 "../utils/render.tsx";
|
|
|
|
// Mock API
|
|
let mockSeedInfo: any = null;
|
|
|
|
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",
|
|
...(mockSeedInfo ? { seedInfo: mockSeedInfo } : {}),
|
|
},
|
|
}),
|
|
{
|
|
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();
|
|
mockSeedInfo = null;
|
|
});
|
|
|
|
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("Sign In")).toBeInTheDocument();
|
|
});
|
|
|
|
it("prefills title and tagline from seedInfo", async () => {
|
|
mockSeedInfo = {
|
|
name: "Blog Template",
|
|
description: "A blog template",
|
|
collections: 2,
|
|
hasContent: true,
|
|
title: "My Awesome Blog",
|
|
tagline: "Thoughts and tutorials",
|
|
};
|
|
|
|
const screen = await render(
|
|
<QueryWrapper>
|
|
<SetupWizard />
|
|
</QueryWrapper>,
|
|
);
|
|
|
|
await expect.element(screen.getByText("Set up your site")).toBeInTheDocument();
|
|
|
|
const titleInput = screen.getByPlaceholder("My Awesome Blog");
|
|
const taglineInput = screen.getByPlaceholder("Thoughts, tutorials, and more");
|
|
|
|
await vi.waitFor(() => {
|
|
expect((titleInput.element() as HTMLInputElement).value).toBe("My Awesome Blog");
|
|
});
|
|
await vi.waitFor(() => {
|
|
expect((taglineInput.element() as HTMLInputElement).value).toBe("Thoughts and tutorials");
|
|
});
|
|
});
|
|
|
|
it("uses empty string for title and tagline when not in seedInfo", async () => {
|
|
mockSeedInfo = {
|
|
name: "Blank Template",
|
|
description: "A blank template",
|
|
collections: 0,
|
|
hasContent: false,
|
|
// title and tagline not provided
|
|
};
|
|
|
|
const screen = await render(
|
|
<QueryWrapper>
|
|
<SetupWizard />
|
|
</QueryWrapper>,
|
|
);
|
|
|
|
await expect.element(screen.getByText("Set up your site")).toBeInTheDocument();
|
|
|
|
const titleInput = screen.getByPlaceholder("My Awesome Blog");
|
|
const taglineInput = screen.getByPlaceholder("Thoughts, tutorials, and more");
|
|
|
|
await vi.waitFor(() => {
|
|
expect((titleInput.element() as HTMLInputElement).value).toBe("");
|
|
});
|
|
await vi.waitFor(() => {
|
|
expect((taglineInput.element() as HTMLInputElement).value).toBe("");
|
|
});
|
|
});
|
|
|
|
it("prefilled title can be edited and submitted", async () => {
|
|
mockSeedInfo = {
|
|
name: "Blog Template",
|
|
description: "A blog template",
|
|
collections: 2,
|
|
hasContent: true,
|
|
title: "My Awesome Blog",
|
|
tagline: "Thoughts and tutorials",
|
|
};
|
|
|
|
const screen = await render(
|
|
<QueryWrapper>
|
|
<SetupWizard />
|
|
</QueryWrapper>,
|
|
);
|
|
|
|
await expect.element(screen.getByText("Set up your site")).toBeInTheDocument();
|
|
|
|
const titleInput = screen.getByPlaceholder("My Awesome Blog");
|
|
await vi.waitFor(() => {
|
|
expect((titleInput.element() as HTMLInputElement).value).toBe("My Awesome Blog");
|
|
});
|
|
|
|
// Edit the title
|
|
await titleInput.fill("My Custom Blog");
|
|
await vi.waitFor(() => {
|
|
expect((screen.getByPlaceholder("My Awesome Blog").element() as HTMLInputElement).value).toBe(
|
|
"My Custom Blog",
|
|
);
|
|
});
|
|
|
|
// Should be able to advance with the edited value
|
|
await screen.getByText("Continue →").click();
|
|
await expect.element(screen.getByText("Create your account")).toBeInTheDocument();
|
|
});
|
|
});
|