183 lines
4.5 KiB
TypeScript
183 lines
4.5 KiB
TypeScript
|
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||
|
|
import { render, screen, fireEvent, waitFor } from "@solidjs/testing-library";
|
||
|
|
import { createSignal } from "solid-js";
|
||
|
|
import ProfessionAdminListPage from "~/components/admin/ProfessionAdminListPage";
|
||
|
|
|
||
|
|
// Mock fetch globally
|
||
|
|
const mockFetch = vi.fn();
|
||
|
|
global.fetch = mockFetch;
|
||
|
|
|
||
|
|
describe("ProfessionAdminListPage", () => {
|
||
|
|
beforeEach(() => {
|
||
|
|
mockFetch.mockClear();
|
||
|
|
});
|
||
|
|
|
||
|
|
it("renders page title and subtitle", async () => {
|
||
|
|
mockFetch.mockResolvedValueOnce({
|
||
|
|
ok: true,
|
||
|
|
json: async () => [
|
||
|
|
{
|
||
|
|
id: "1",
|
||
|
|
first_name: "John",
|
||
|
|
last_name: "Doe",
|
||
|
|
email: "john@example.com",
|
||
|
|
phone: "1234567890",
|
||
|
|
status: "ACTIVE",
|
||
|
|
created_at: "2024-01-01T00:00:00Z",
|
||
|
|
},
|
||
|
|
],
|
||
|
|
});
|
||
|
|
|
||
|
|
render(() =>
|
||
|
|
ProfessionAdminListPage({
|
||
|
|
endpoint: "/api/admin/test",
|
||
|
|
title: "Test Management",
|
||
|
|
subtitle: "Manage test records",
|
||
|
|
emptyLabel: "No test records found.",
|
||
|
|
viewHref: (id) => `/admin/test/${id}`,
|
||
|
|
})
|
||
|
|
);
|
||
|
|
|
||
|
|
await waitFor(() => {
|
||
|
|
expect(screen.getByText("Test Management")).toBeInTheDocument();
|
||
|
|
expect(screen.getByText("Manage test records")).toBeInTheDocument();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
it("filters by search query", async () => {
|
||
|
|
const mockData = [
|
||
|
|
{
|
||
|
|
id: "1",
|
||
|
|
first_name: "Alice",
|
||
|
|
last_name: "Smith",
|
||
|
|
email: "alice@example.com",
|
||
|
|
phone: "",
|
||
|
|
status: "ACTIVE",
|
||
|
|
created_at: "2024-01-01T00:00:00Z",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: "2",
|
||
|
|
first_name: "Bob",
|
||
|
|
last_name: "Jones",
|
||
|
|
email: "bob@example.com",
|
||
|
|
phone: "",
|
||
|
|
status: "INACTIVE",
|
||
|
|
created_at: "2024-01-02T00:00:00Z",
|
||
|
|
},
|
||
|
|
];
|
||
|
|
|
||
|
|
mockFetch.mockResolvedValueOnce({
|
||
|
|
ok: true,
|
||
|
|
json: async () => mockData,
|
||
|
|
});
|
||
|
|
|
||
|
|
render(() =>
|
||
|
|
ProfessionAdminListPage({
|
||
|
|
endpoint: "/api/admin/test",
|
||
|
|
title: "Test",
|
||
|
|
subtitle: "",
|
||
|
|
emptyLabel: "Empty",
|
||
|
|
viewHref: (id) => `/admin/test/${id}`,
|
||
|
|
})
|
||
|
|
);
|
||
|
|
|
||
|
|
await waitFor(() => {
|
||
|
|
expect(screen.getByText("Alice Smith")).toBeInTheDocument();
|
||
|
|
expect(screen.getByText("Bob Jones")).toBeInTheDocument();
|
||
|
|
});
|
||
|
|
|
||
|
|
const input = screen.getByPlaceholderText("Search by name or email...");
|
||
|
|
fireEvent.input(input, { target: { value: "alice" } });
|
||
|
|
|
||
|
|
await waitFor(() => {
|
||
|
|
expect(screen.getByText("Alice Smith")).toBeInTheDocument();
|
||
|
|
expect(screen.queryByText("Bob Jones")).not.toBeInTheDocument();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
it("shows empty state when no data", async () => {
|
||
|
|
mockFetch.mockResolvedValueOnce({
|
||
|
|
ok: true,
|
||
|
|
json: async () => [],
|
||
|
|
});
|
||
|
|
|
||
|
|
render(() =>
|
||
|
|
ProfessionAdminListPage({
|
||
|
|
endpoint: "/api/admin/test",
|
||
|
|
title: "Test",
|
||
|
|
subtitle: "",
|
||
|
|
emptyLabel: "No records.",
|
||
|
|
viewHref: (id) => `/admin/test/${id}`,
|
||
|
|
})
|
||
|
|
);
|
||
|
|
|
||
|
|
await waitFor(() => {
|
||
|
|
expect(screen.getByText("No records.")).toBeInTheDocument();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
it("handles fetch error gracefully", async () => {
|
||
|
|
mockFetch.mockResolvedValueOnce({
|
||
|
|
ok: false,
|
||
|
|
status: 500,
|
||
|
|
});
|
||
|
|
|
||
|
|
render(() =>
|
||
|
|
ProfessionAdminListPage({
|
||
|
|
endpoint: "/api/admin/test",
|
||
|
|
title: "Test",
|
||
|
|
subtitle: "",
|
||
|
|
emptyLabel: "Empty",
|
||
|
|
viewHref: (id) => `/admin/test/${id}`,
|
||
|
|
})
|
||
|
|
);
|
||
|
|
|
||
|
|
await waitFor(() => {
|
||
|
|
expect(screen.getByText(/Failed to load/)).toBeInTheDocument();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
it("export button calls exportCsv and downloads file", async () => {
|
||
|
|
const mockData = [
|
||
|
|
{
|
||
|
|
id: "1",
|
||
|
|
first_name: "Alice",
|
||
|
|
last_name: "Smith",
|
||
|
|
email: "alice@example.com",
|
||
|
|
phone: "123",
|
||
|
|
status: "ACTIVE",
|
||
|
|
created_at: "2024-01-01T00:00:00Z",
|
||
|
|
},
|
||
|
|
];
|
||
|
|
|
||
|
|
mockFetch.mockResolvedValueOnce({
|
||
|
|
ok: true,
|
||
|
|
json: async () => mockData,
|
||
|
|
});
|
||
|
|
|
||
|
|
// Mock URL.createObjectURL and link.click
|
||
|
|
const mockClick = vi.fn();
|
||
|
|
global.URL.createObjectURL = vi.fn(() => "blob:test");
|
||
|
|
global.document.createElement = vi.fn(() => ({ click: mockClick, href: "" }));
|
||
|
|
|
||
|
|
render(() =>
|
||
|
|
ProfessionAdminListPage({
|
||
|
|
endpoint: "/api/admin/test",
|
||
|
|
title: "Test",
|
||
|
|
subtitle: "",
|
||
|
|
emptyLabel: "Empty",
|
||
|
|
viewHref: (id) => `/admin/test/${id}`,
|
||
|
|
})
|
||
|
|
);
|
||
|
|
|
||
|
|
await waitFor(() => {
|
||
|
|
expect(screen.getByText("Alice Smith")).toBeInTheDocument();
|
||
|
|
});
|
||
|
|
|
||
|
|
const exportBtn = screen.getByText("Export");
|
||
|
|
fireEvent.click(exportBtn);
|
||
|
|
|
||
|
|
expect(mockClick).toHaveBeenCalled();
|
||
|
|
});
|
||
|
|
});
|