Files
fusion/packages/dashboard/app/components/__tests__/TrackingRepoSelect.test.tsx
Fusion (runfusion.ai) 51c60fe1b5 feat(FN-4731): add tracking repo dropdown to settings modal
Adds a `TrackingRepoSelect` dropdown component to the SettingsModal (with co-located CSS and full test coverage), updates the settings reference docs, and includes the FN-4731 changeset for the `@runfusion/fusion` package.

Fusion-Task-Id: FN-4731
2026-05-16 09:20:33 -07:00

133 lines
3.6 KiB
TypeScript

import React from "react";
import { describe, expect, it, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { TrackingRepoSelect } from "../TrackingRepoSelect";
describe("TrackingRepoSelect", () => {
it("renders suggestions and selected option", () => {
render(
<TrackingRepoSelect
id="tracking"
value="octo/widgets"
onChange={vi.fn()}
options={[
{ value: "octo/widgets", label: "octo/widgets" },
{ value: "acme/cli", label: "acme/cli" },
]}
/>,
);
const select = screen.getByRole("combobox");
expect(select).toHaveValue("octo/widgets");
expect(screen.queryByRole("textbox")).toBeNull();
});
it("fires onChange when selecting suggestion", async () => {
const user = userEvent.setup();
const onChange = vi.fn();
render(
<TrackingRepoSelect
id="tracking"
value=""
onChange={onChange}
options={[
{ value: "octo/widgets", label: "octo/widgets" },
{ value: "acme/cli", label: "acme/cli" },
]}
/>,
);
await user.selectOptions(screen.getByRole("combobox"), "acme/cli");
expect(onChange).toHaveBeenCalledWith("acme/cli");
});
it("shows custom input and edits custom value", async () => {
const user = userEvent.setup();
const onChange = vi.fn();
function Harness() {
const [value, setValue] = React.useState("");
return (
<TrackingRepoSelect
id="tracking"
value={value}
onChange={(next) => {
setValue(next);
onChange(next);
}}
options={[{ value: "octo/widgets", label: "octo/widgets" }]}
/>
);
}
render(<Harness />);
await user.selectOptions(screen.getByRole("combobox"), "__custom__");
const input = screen.getByRole("textbox");
await user.type(input, "custom/repo");
expect(onChange).toHaveBeenLastCalledWith("custom/repo");
});
it("treats non-suggestion value as custom", () => {
render(
<TrackingRepoSelect
id="tracking"
value="other/repo"
onChange={vi.fn()}
options={[{ value: "octo/widgets", label: "octo/widgets" }]}
/>,
);
expect(screen.getByRole("combobox")).toHaveValue("__custom__");
expect(screen.getByRole("textbox")).toHaveValue("other/repo");
});
it("deduplicates duplicate suggestions", () => {
render(
<TrackingRepoSelect
id="tracking"
value=""
onChange={vi.fn()}
options={[
{ value: "octo/widgets", label: "octo/widgets", source: "A" },
{ value: "octo/widgets", label: "octo/widgets", source: "B" },
]}
/>,
);
const options = screen.getAllByRole("option");
expect(options.filter((option) => option.getAttribute("value") === "octo/widgets")).toHaveLength(1);
});
it("renders loading/error hints without disabling custom entry", () => {
const { rerender } = render(
<TrackingRepoSelect
id="tracking"
value=""
onChange={vi.fn()}
options={[]}
loading
/>,
);
expect(screen.getByText("Loading detected GitHub remotes…")).toBeInTheDocument();
expect(screen.getByRole("textbox")).toBeInTheDocument();
rerender(
<TrackingRepoSelect
id="tracking"
value=""
onChange={vi.fn()}
options={[]}
error="Unable to load remotes"
/>,
);
expect(screen.getByText("Unable to load remotes")).toBeInTheDocument();
expect(screen.getByRole("textbox")).toBeInTheDocument();
});
});