Open History to see the last commit
1import { PrimaryConnectionTarget } from "@t3tools/client-runtime/connection";
2import { EnvironmentId } from "@t3tools/contracts";
3import * as Option from "effect/Option";
4import { expect, it, vi } from "vite-plus/test";
5import { page, userEvent } from "vite-plus/test/browser";
6import { render } from "../../../test/render";
7import "~/index.css";
8import { chooseAudioDestination } from "../../speech/voiceConsent";
9import { useEnvironmentScope, usePreparedConnection } from "../../state/session";
10import { VoiceSettingsPanel } from "./VoiceSettingsPanel";
11import { SettingsScopeProvider } from "./SettingsScopeContext";
12
13vi.mock(import("../../state/session"), { spy: true });
14vi.mock("../../env", async (original) => ({
15 ...(await original<typeof import("../../env")>()),
16 isSecretsFeatureEnabled: true,
17}));
18vi.mock("../../state/environments", async (importOriginal) => ({
19 ...(await importOriginal<typeof import("../../state/environments")>()),
20 useEnvironments: () => ({
21 environments: [
22 {
23 environmentId: "voice-host",
24 label: "Laptop",
25 connection: { phase: "available" },
26 serverConfig: {
27 secretReferences: true,
28 settings: {
29 secretProviders: {
30 work: { kind: "one_password", label: "Work vault", config: { account: "work" } },
31 },
32 },
33 environment: {
34 platform: { os: "linux", arch: "x64" },
35 capabilities: { voiceTranscription: true, cloudVoiceTranscription: true },
36 },
37 },
38 },
39 ],
40 }),
41}));
42
43const id = EnvironmentId.make("voice-host");
44const grantKey = "t3code:voice-audio-grant:v1";
45
46async function withVoicePanel(
47 route: (url: URL, request: RequestInfo | URL, init?: RequestInit) => Promise<Response> | null,
48 body: (screen: Awaited<ReturnType<typeof render>>) => Promise<void>,
49) {
50 const target = new PrimaryConnectionTarget({
51 environmentId: id,
52 label: "Laptop",
53 httpBaseUrl: window.location.origin,
54 wsBaseUrl: "ws://localhost",
55 });
56 const prepared = Option.some({
57 ...target,
58 target,
59 socketUrl: target.wsBaseUrl,
60 httpAuthorization: null,
61 });
62 using cleanup = new DisposableStack();
63 cleanup.defer(() => chooseAudioDestination(null));
64 const fetch = globalThis.fetch;
65 using _network = vi.spyOn(globalThis, "fetch").mockImplementation((request, init) => {
66 const url = new URL(request instanceof Request ? request.url : request, window.location.href);
67 return route(url, request, init) ?? fetch(request, init);
68 });
69 await vi.mocked(useEnvironmentScope).withImplementation(
70 () => true,
71 async () => {
72 await vi.mocked(usePreparedConnection).withImplementation(
73 () => prepared,
74 async () => {
75 await using screen = await render(
76 <SettingsScopeProvider search={{}} onChange={() => {}}>
77 <VoiceSettingsPanel />
78 </SettingsScopeProvider>,
79 );
80 await body(screen);
81 },
82 );
83 },
84 );
85}
86
87it("cancelling a provider's setup keeps the current destination and writes nothing", async () => {
88 chooseAudioDestination({ kind: "environment", environmentId: id });
89 const writes: unknown[] = [];
90 await withVoicePanel(
91 (url, request, init) => {
92 if (url.pathname === "/api/voice/cloud/keys")
93 return Promise.resolve(Response.json({ openai: false, xai: true }));
94 if (url.pathname !== "/api/voice/cloud/openai/key") return null;
95 return new Request(request, init).json().then((body) => {
96 writes.push(body);
97 return new Response(null, { status: 204 });
98 });
99 },
100 async (screen) => {
101 const destination = screen.getByRole("combobox", { name: "Transcribe with" });
102 await expect.element(destination).toHaveTextContent("Laptop");
103 await destination.click();
104 await page.getByRole("option", { name: "OpenAI via Laptop" }).click();
105 const dialog = page.getByRole("dialog");
106 await dialog.getByRole("button", { name: "Cancel" }).click();
107 await expect.element(dialog).not.toBeInTheDocument();
108 await expect.element(destination).toHaveTextContent("Laptop");
109 expect(writes).toEqual([]);
110 },
111 );
112});
113
114it("enables the chosen provider only after a successful setup", async () => {
115 chooseAudioDestination({ kind: "environment", environmentId: id });
116 const writes: unknown[] = [];
117 let rejectNextWrite = true;
118 await withVoicePanel(
119 (url, request, init) => {
120 if (url.pathname === "/api/voice/cloud/keys")
121 return Promise.resolve(Response.json({ openai: writes.length > 0, xai: true }));
122 if (url.pathname !== "/api/voice/cloud/openai/key") return null;
123 if (rejectNextWrite) {
124 rejectNextWrite = false;
125 return Promise.resolve(new Response(null, { status: 403 }));
126 }
127 return new Request(request, init).json().then((body) => {
128 writes.push(body);
129 return new Response(null, { status: 204 });
130 });
131 },
132 async (screen) => {
133 const destination = screen.getByRole("combobox", { name: "Transcribe with" });
134 await destination.click();
135 await page.getByRole("option", { name: "OpenAI via Laptop" }).click();
136 const dialog = page.getByRole("dialog");
137 await dialog.getByRole("combobox", { name: "OpenAI API key source" }).click();
138 await page.getByRole("option", { name: "Work vault" }).click();
139 await dialog.getByLabelText("OpenAI API key reference").fill("op://Work/Voice/key");
140 await userEvent.keyboard("{Enter}");
141 await expect.element(dialog.getByRole("alert")).toBeVisible();
142 expect(writes).toEqual([]);
143 await expect.element(dialog.getByRole("button", { name: "Save and enable" })).toBeEnabled();
144 await dialog.getByLabelText("OpenAI API key reference").click();
145 await userEvent.keyboard("{Enter}");
146 await expect.element(dialog).not.toBeInTheDocument();
147 await expect.element(destination).toHaveTextContent("OpenAI via Laptop");
148 expect(writes).toEqual([{ key: { provider: "work", reference: "op://Work/Voice/key" } }]);
149 },
150 );
151});
152
153it("keeps the active provider's grant when editing its key", async () => {
154 chooseAudioDestination({ kind: "cloud", provider: "openai", environmentId: id });
155 const grant = window.localStorage.getItem(grantKey);
156 const writes: unknown[] = [];
157 await withVoicePanel(
158 (url, request, init) => {
159 if (url.pathname === "/api/voice/cloud/keys")
160 return Promise.resolve(Response.json({ openai: true, xai: true }));
161 if (url.pathname !== "/api/voice/cloud/openai/key") return null;
162 return new Request(request, init).json().then((body) => {
163 writes.push(body);
164 return new Response(null, { status: 204 });
165 });
166 },
167 async (screen) => {
168 await screen.getByRole("button", { name: "Manage key" }).click();
169 const dialog = page.getByRole("dialog");
170 await dialog.getByLabelText("OpenAI API key", { exact: true }).fill("replacement");
171 await userEvent.keyboard("{Enter}");
172 await expect.element(dialog).not.toBeInTheDocument();
173 expect(writes).toEqual([{ key: "replacement" }]);
174 expect(window.localStorage.getItem(grantKey)).toBe(grant);
175 },
176 );
177});
178
179it("uses a provider's saved key without writing it, and turns dictation off", async () => {
180 chooseAudioDestination({ kind: "environment", environmentId: id });
181 await withVoicePanel(
182 (url) =>
183 url.pathname === "/api/voice/cloud/keys"
184 ? Promise.resolve(Response.json({ openai: false, xai: true }))
185 : null,
186 async (screen) => {
187 const destination = screen.getByRole("combobox", { name: "Transcribe with" });
188 await destination.click();
189 await page.getByRole("option", { name: "Grok Voice Transcribe via Laptop" }).click();
190 await page
191 .getByRole("dialog")
192 .getByRole("button", { name: "Use Grok Voice Transcribe" })
193 .click();
194 await expect.element(destination).toHaveTextContent("Grok Voice Transcribe via Laptop");
195 await destination.click();
196 await page.getByRole("option", { name: "Off", exact: true }).click();
197 await expect.element(destination).toHaveTextContent("Off");
198 },
199 );
200});
201
202it("OpenRouter needs an exact model, and changing it writes no credential", async () => {
203 chooseAudioDestination(null);
204 const writes: unknown[] = [];
205 const grantModel = () => {
206 const raw = window.localStorage.getItem(grantKey);
207 const parsed = raw ? (JSON.parse(raw) as { destination?: { model?: string } }) : null;
208 return parsed?.destination?.model ?? null;
209 };
210 await withVoicePanel(
211 (url, request, init) => {
212 if (url.pathname === "/api/voice/cloud/keys")
213 return Promise.resolve(Response.json({ openai: false, xai: false, openrouter: true }));
214 if (url.pathname === "/api/voice/cloud/openrouter/models")
215 return Promise.resolve(
216 Response.json({
217 models: [
218 { id: "acme/transcribe-1", name: "Acme: Transcribe 1" },
219 { id: "other/listen-2", name: "Other: Listen 2" },
220 ],
221 }),
222 );
223 if (url.pathname === "/api/voice/cloud/openrouter/key")
224 return new Request(request, init).json().then((body) => {
225 writes.push(body);
226 return new Response(null, { status: 204 });
227 });
228 return null;
229 },
230 async (screen) => {
231 const destination = screen.getByRole("combobox", { name: "Transcribe with" });
232 await destination.click();
233 await page.getByRole("option", { name: "OpenRouter via Laptop" }).click();
234 const dialog = page.getByRole("dialog");
235 const save = dialog.getByRole("button", { name: "Save and enable" });
236 await expect.element(save).toBeDisabled();
237 const picker = dialog.getByRole("combobox", { name: "OpenRouter model" });
238 await expect.element(picker).toBeVisible();
239 await picker.fill("listen");
240 await expect.element(page.getByRole("option", { name: /Other: Listen 2/ })).toBeVisible();
241 await page.getByRole("option", { name: /Other: Listen 2/ }).click();
242 await expect.element(save).toBeEnabled();
243 const key = dialog.getByLabelText("OpenRouter API key", { exact: true });
244 await key.fill("op://not-a-literal-key");
245 await expect.element(save).toBeDisabled();
246 await key.fill("");
247 await save.click();
248 await expect.element(dialog).not.toBeInTheDocument();
249 await expect.element(destination).toHaveTextContent("OpenRouter via Laptop");
250 expect(grantModel()).toBe("other/listen-2");
251 expect(writes).toEqual([]);
252 await screen.getByRole("button", { name: "Manage OpenRouter" }).click();
253 const current = dialog.getByRole("combobox", { name: "OpenRouter model" });
254 await expect.element(current).toHaveValue("Other: Listen 2");
255 await current.fill("acme");
256 await page.getByRole("option", { name: /Acme: Transcribe 1/ }).click();
257 await dialog.getByRole("button", { name: "Save" }).click();
258 await expect.element(dialog).not.toBeInTheDocument();
259 await expect.element(destination).toHaveTextContent("OpenRouter via Laptop");
260 expect(grantModel()).toBe("acme/transcribe-1");
261 expect(writes).toEqual([]);
262 },
263 );
264});