import { afterEach, describe, expect, it, rs } from "@rstest/core";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import {
act,
cleanup,
fireEvent,
render,
screen,
waitFor,
} from "@testing-library/react";
import type { PropsWithChildren } from "react";
import { KnowledgeScopeSelector } from "@/components/workspace/knowledge-scope-selector";
import { I18nProvider } from "@/core/i18n/context";
import type { KnowledgeScopeSelection } from "@/core/knowledge";
afterEach(() => {
cleanup();
rs.restoreAllMocks();
});
function renderSelector(selection: KnowledgeScopeSelection) {
const queryClient = new QueryClient({
defaultOptions: { queries: { retry: false } },
});
function Wrapper({ children }: PropsWithChildren) {
return (
{children}
);
}
return render(
undefined}
/>,
{ wrapper: Wrapper },
);
}
function requestUrl(input: RequestInfo | URL): string {
if (typeof input === "string") return input;
if (input instanceof URL) return input.href;
return input.url;
}
describe("KnowledgeScopeSelector trigger", () => {
it("renders only the icon and stays highlighted while retrieval is active", () => {
renderSelector({ mode: "all" });
const trigger = screen.getByRole("button", { name: "Knowledge · All" });
expect(trigger.textContent).toBe("");
expect(trigger.getAttribute("aria-pressed")).toBe("true");
expect(trigger.className).toContain("text-foreground");
expect(trigger.className).not.toContain("bg-primary/10");
expect(trigger.className).not.toContain("border-primary/20");
expect(trigger.querySelector("svg")).not.toBeNull();
});
it("returns to the neutral icon state when retrieval is off", () => {
renderSelector({ mode: "disabled" });
const trigger = screen.getByRole("button", { name: "Knowledge · Off" });
expect(trigger.textContent).toBe("");
expect(trigger.getAttribute("aria-pressed")).toBe("false");
expect(trigger.className).not.toContain("bg-primary/10");
expect(trigger.querySelector("svg")).not.toBeNull();
});
it("does not load documents while an expanded dataset still uses all files", async () => {
const fetch = rs.spyOn(globalThis, "fetch").mockImplementation((input) => {
const url = requestUrl(input);
return Promise.resolve(
Response.json({
items: url.includes("/documents?")
? [{ id: "document-1", name: "Guide", selectable: true }]
: [{ id: "dataset-1", name: "Policies", selectable: true }],
page: 1,
page_size: 100,
total: 1,
}),
);
});
renderSelector({
mode: "selected",
datasets: [
{
id: "dataset-1",
name: "Policies",
documents: { mode: "all" },
},
],
});
fireEvent.click(screen.getByRole("button", { name: "Knowledge · 1 base" }));
await screen.findByText("Policies");
fireEvent.click(screen.getByRole("button", { name: "Files" }));
await act(async () => {
await new Promise((resolve) => setTimeout(resolve, 0));
});
expect(
fetch.mock.calls.some(([input]) =>
requestUrl(input).includes("/documents?"),
),
).toBe(false);
fireEvent.click(screen.getByRole("radio", { name: "Selected files" }));
await waitFor(() => {
expect(
fetch.mock.calls.some(([input]) =>
requestUrl(input).includes("/documents?"),
),
).toBe(true);
});
});
});