Open History to see the last commit
1import { EnvironmentId, ProviderDriverKind, type AcpRegistrySearchAgent } from "@t3tools/contracts";
2import { expect, it, vi } from "vite-plus/test";
3import { page, userEvent } from "vite-plus/test/browser";
4
5import { render } from "../../../test/render";
6
7const state = vi.hoisted(() => ({
8 result: null as { readonly agents: ReadonlyArray<AcpRegistrySearchAgent> } | null,
9 error: null as string | null,
10 isPending: false,
11 canManage: true,
12 refresh: vi.fn(),
13 search: vi.fn(),
14 prepare: vi.fn(),
15}));
16
17vi.mock(import("../../state/server"), async (original) => {
18 const actual = await original();
19 return {
20 ...actual,
21 serverEnvironment: {
22 ...actual.serverEnvironment,
23 searchAcpRegistry: ((input: unknown) => {
24 state.search(input);
25 return "search";
26 }) as never,
27 },
28 };
29});
30vi.mock(import("../../state/session"), async (original) => ({
31 ...(await original()),
32 useEnvironmentScope: () => state.canManage,
33 readEnvironmentScope: () => state.canManage,
34}));
35vi.mock(import("../../state/query"), async (original) => ({
36 ...(await original()),
37 useEnvironmentQuery: (() => ({
38 data: state.result,
39 error: state.error,
40 isPending: state.isPending,
41 refresh: state.refresh,
42 })) as never,
43}));
44vi.mock(import("../../state/use-atom-command"), async (original) => ({
45 ...(await original()),
46 useAtomCommand: (() => state.prepare) as never,
47}));
48
49import { AcpRegistrySearchStep, useAcpRegistrySearch } from "./AcpRegistrySearchStep";
50
51const environmentId = EnvironmentId.make("remote-device");
52const gemini: AcpRegistrySearchAgent = {
53 id: "gemini",
54 name: "Gemini CLI",
55 version: "1.2.3",
56 description: "Google's agent",
57 authors: ["Google <gemini-cli@google.com>", "Contributor"],
58 license: "Apache-2.0",
59 website: "https://example.com/docs",
60 repository: "https://example.com/source",
61 icon: null,
62 distribution: "npx",
63 integrity: "registry",
64};
65
66function Step({
67 configured = false,
68 onPrepared = vi.fn(),
69}: {
70 readonly configured?: boolean;
71 readonly onPrepared?: (agent: AcpRegistrySearchAgent) => void;
72}) {
73 const registry = useAcpRegistrySearch(environmentId, true);
74 return (
75 <AcpRegistrySearchStep
76 environmentId={environmentId}
77 registry={registry}
78 providerInstances={
79 configured
80 ? {
81 acpRegistry_gemini: {
82 driver: ProviderDriverKind.make("acpRegistry"),
83 config: { agentId: "gemini" },
84 },
85 }
86 : {}
87 }
88 onPrepared={onPrepared}
89 onManualConfiguration={vi.fn()}
90 />
91 );
92}
93
94function setup() {
95 state.canManage = true;
96 state.result = null;
97 state.error = null;
98 state.isPending = false;
99 state.refresh.mockReset();
100 state.search.mockReset();
101 state.prepare.mockReset().mockResolvedValue({
102 _tag: "Success",
103 value: { agentId: "gemini", version: "1.2.3", distribution: "npx", prepared: true },
104 });
105}
106
107it("rechecks the grant before preparing an agent", async () => {
108 setup();
109 state.result = { agents: [gemini] };
110 const onPrepared = vi.fn();
111 await using _screen = await render(<Step onPrepared={onPrepared} />);
112 state.canManage = false;
113
114 await page.getByRole("button", { name: "Add Gemini CLI" }).click();
115
116 expect(state.prepare).not.toHaveBeenCalled();
117 expect(onPrepared).not.toHaveBeenCalled();
118});
119
120it("bounds pasted registry searches before debounce and submission", async () => {
121 setup();
122 await using screen = await render(<Step />);
123 const searchbox = screen.getByRole("searchbox", { name: "Search ACP Registry" });
124
125 await searchbox.fill("agent".repeat(26));
126 await vi.waitFor(() => {
127 expect(state.search).toHaveBeenLastCalledWith({
128 environmentId,
129 input: { query: "agent".repeat(24) },
130 });
131 });
132 await userEvent.keyboard("{Enter}");
133 expect(state.refresh).toHaveBeenCalledTimes(1);
134 await expect.element(searchbox).toHaveValue("agent".repeat(24));
135});
136
137it("leaves out Registry agents that T3 Code supports as their own providers", async () => {
138 setup();
139 state.result = { agents: [gemini, { ...gemini, id: "codex-acp", name: "Codex" }] };
140 await using _screen = await render(<Step />);
141
142 expect(page.getByRole("button", { name: "Add Gemini CLI" }).query()).not.toBeNull();
143 expect(page.getByRole("button", { name: "Add Codex" }).query()).toBeNull();
144});