All checks were successful
build-and-release / build (push) Successful in 1m19s
- tsconfig.json: add @types/node for playwright/test configs - lib/api.ts: handle both array and wrapped packages response from tracecoin endpoint - lib/server/gateway.ts: use import.meta.env instead of process.env - routes/admin/pricing.tsx: fix packages() usage after api.ts fix - routes/admin/roles/*.tsx: fix Set<string> types and module filter types - routes/admin/users/[id]/edit.tsx: add phone? to local User type - routes/admin/users/details/[id].tsx: fix roleFilter param type narrowing - components/admin/DashboardDesignPreview.tsx: fix budget→budget, responses→responseTag - vite.config.ts: type proxy callbacks as any - test/setup.ts: fix IntersectionObserver mock, add vitest/globals reference - tests/e2e: fix PNG type mismatches, add as any for mock overrides - tests/vitest: fix global.createElement mock type, vitest globals reference - tests/tsconfig.json: test-specific tsconfig with node types Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
183 lines
4.6 KiB
TypeScript
183 lines
4.6 KiB
TypeScript
/// <reference types="vitest/globals" />
|
|
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: "" })) as any;
|
|
|
|
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();
|
|
});
|
|
});
|