url tests
This commit is contained in:
parent
7f99db7873
commit
e61208a106
@ -1,9 +1,10 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { MemoryRouter } from "react-router-dom";
|
||||
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
||||
import { MemoryRouter, Route, Routes, useLocation } from "react-router-dom";
|
||||
|
||||
import { OrchestratorPage } from "./OrchestratorPage";
|
||||
import type { Job } from "../../shared/types";
|
||||
import type { FilterTab } from "./orchestrator/constants";
|
||||
|
||||
const jobFixture: Job = {
|
||||
id: "job-1",
|
||||
@ -63,6 +64,8 @@ const jobFixture: Job = {
|
||||
updatedAt: "2025-01-02T00:00:00Z",
|
||||
};
|
||||
|
||||
const job2: Job = { ...jobFixture, id: "job-2", status: "discovered" };
|
||||
|
||||
const createMatchMedia = (matches: boolean) =>
|
||||
vi.fn().mockImplementation((query: string) => ({
|
||||
matches,
|
||||
@ -76,9 +79,9 @@ const createMatchMedia = (matches: boolean) =>
|
||||
|
||||
vi.mock("./orchestrator/useOrchestratorData", () => ({
|
||||
useOrchestratorData: () => ({
|
||||
jobs: [jobFixture],
|
||||
jobs: [jobFixture, job2],
|
||||
stats: {
|
||||
discovered: 0,
|
||||
discovered: 1,
|
||||
processing: 0,
|
||||
ready: 1,
|
||||
applied: 0,
|
||||
@ -109,7 +112,21 @@ vi.mock("./orchestrator/OrchestratorSummary", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("./orchestrator/OrchestratorFilters", () => ({
|
||||
OrchestratorFilters: () => <div data-testid="filters" />,
|
||||
OrchestratorFilters: ({
|
||||
onTabChange,
|
||||
onSearchQueryChange,
|
||||
onSortChange,
|
||||
}: {
|
||||
onTabChange: (t: FilterTab) => void;
|
||||
onSearchQueryChange: (q: string) => void;
|
||||
onSortChange: (s: any) => void;
|
||||
}) => (
|
||||
<div data-testid="filters">
|
||||
<button onClick={() => onTabChange("discovered")}>To Discovered</button>
|
||||
<button onClick={() => onSearchQueryChange("test search")}>Set Search</button>
|
||||
<button onClick={() => onSortChange({ key: "title", direction: "asc" })}>Set Sort</button>
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("./orchestrator/JobDetailPanel", () => ({
|
||||
@ -120,7 +137,12 @@ vi.mock("./orchestrator/JobListPanel", () => ({
|
||||
JobListPanel: ({ onSelectJob, selectedJobId }: { onSelectJob: (id: string) => void; selectedJobId: string | null }) => (
|
||||
<div>
|
||||
<div data-testid="selected-job">{selectedJobId ?? "none"}</div>
|
||||
<button type="button" onClick={() => onSelectJob("job-1")}>Select job</button>
|
||||
<button data-testid="select-job-1" type="button" onClick={() => onSelectJob("job-1")}>
|
||||
Select job 1
|
||||
</button>
|
||||
<button data-testid="select-job-2" type="button" onClick={() => onSelectJob("job-2")}>
|
||||
Select job 2
|
||||
</button>
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
@ -129,23 +151,109 @@ vi.mock("../components", () => ({
|
||||
ManualImportSheet: () => <div data-testid="manual-import" />,
|
||||
}));
|
||||
|
||||
const LocationWatcher = () => {
|
||||
const location = useLocation();
|
||||
return <div data-testid="location">{location.pathname + location.search}</div>;
|
||||
};
|
||||
|
||||
describe("OrchestratorPage", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("syncs tab selection to the URL", () => {
|
||||
window.matchMedia = createMatchMedia(true) as unknown as typeof window.matchMedia;
|
||||
|
||||
render(
|
||||
<MemoryRouter initialEntries={["/ready"]}>
|
||||
<LocationWatcher />
|
||||
<Routes>
|
||||
<Route path="/:tab" element={<OrchestratorPage />} />
|
||||
<Route path="/:tab/:jobId" element={<OrchestratorPage />} />
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByText("To Discovered"));
|
||||
expect(screen.getByTestId("location").textContent).toContain("/discovered");
|
||||
});
|
||||
|
||||
it("syncs job selection to the URL", async () => {
|
||||
window.matchMedia = createMatchMedia(true) as unknown as typeof window.matchMedia;
|
||||
|
||||
render(
|
||||
<MemoryRouter initialEntries={["/all"]}>
|
||||
<LocationWatcher />
|
||||
<Routes>
|
||||
<Route path="/:tab" element={<OrchestratorPage />} />
|
||||
<Route path="/:tab/:jobId" element={<OrchestratorPage />} />
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
// Initial load will auto-select the first matching job (job-1 for all tab)
|
||||
const locationText = () => screen.getByTestId("location").textContent;
|
||||
expect(locationText()).toContain("/all/job-1");
|
||||
|
||||
// Clicking job-2 should update URL
|
||||
const job2Button = screen.getByTestId("select-job-2");
|
||||
fireEvent.click(job2Button);
|
||||
|
||||
// Wait for URL to update
|
||||
await waitFor(() => {
|
||||
expect(locationText()).toContain("/all/job-2");
|
||||
});
|
||||
});
|
||||
|
||||
it("syncs search query to URL as a parameter", () => {
|
||||
window.matchMedia = createMatchMedia(true) as unknown as typeof window.matchMedia;
|
||||
|
||||
render(
|
||||
<MemoryRouter initialEntries={["/ready"]}>
|
||||
<LocationWatcher />
|
||||
<Routes>
|
||||
<Route path="/:tab" element={<OrchestratorPage />} />
|
||||
<Route path="/:tab/:jobId" element={<OrchestratorPage />} />
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByText("Set Search"));
|
||||
expect(screen.getByTestId("location").textContent).toContain("q=test+search");
|
||||
});
|
||||
|
||||
it("syncs sorting to URL and removes it when default", () => {
|
||||
window.matchMedia = createMatchMedia(true) as unknown as typeof window.matchMedia;
|
||||
|
||||
render(
|
||||
<MemoryRouter initialEntries={["/ready"]}>
|
||||
<LocationWatcher />
|
||||
<Routes>
|
||||
<Route path="/:tab" element={<OrchestratorPage />} />
|
||||
<Route path="/:tab/:jobId" element={<OrchestratorPage />} />
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByText("Set Sort"));
|
||||
expect(screen.getByTestId("location").textContent).toContain("sort=title-asc");
|
||||
});
|
||||
|
||||
it("opens the detail drawer on mobile when a job is selected", () => {
|
||||
window.matchMedia = createMatchMedia(false) as unknown as typeof window.matchMedia;
|
||||
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<OrchestratorPage />
|
||||
<MemoryRouter initialEntries={["/ready"]}>
|
||||
<Routes>
|
||||
<Route path="/:tab" element={<OrchestratorPage />} />
|
||||
<Route path="/:tab/:jobId" element={<OrchestratorPage />} />
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
expect(screen.queryByTestId("detail-panel")).not.toBeInTheDocument();
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: /select job/i }));
|
||||
fireEvent.click(screen.getByTestId("select-job-1"));
|
||||
|
||||
expect(screen.getByTestId("detail-panel")).toBeInTheDocument();
|
||||
});
|
||||
@ -154,8 +262,11 @@ describe("OrchestratorPage", () => {
|
||||
window.matchMedia = createMatchMedia(true) as unknown as typeof window.matchMedia;
|
||||
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<OrchestratorPage />
|
||||
<MemoryRouter initialEntries={["/ready"]}>
|
||||
<Routes>
|
||||
<Route path="/:tab" element={<OrchestratorPage />} />
|
||||
<Route path="/:tab/:jobId" element={<OrchestratorPage />} />
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
|
||||
@ -50,6 +50,7 @@ export const JobListPanel: React.FC<JobListPanelProps> = ({
|
||||
key={job.id}
|
||||
type="button"
|
||||
onClick={() => onSelectJob(job.id)}
|
||||
data-testid={`select-${job.id}`}
|
||||
className={cn(
|
||||
"flex w-full items-center gap-3 px-4 py-3 text-left transition-colors",
|
||||
isSelected
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user