provider-endpoints-table.test.tsx 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  1. /**
  2. * @vitest-environment happy-dom
  3. */
  4. import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
  5. import type { ReactNode } from "react";
  6. import { act } from "react";
  7. import { createRoot } from "react-dom/client";
  8. import { NextIntlClientProvider } from "next-intl";
  9. import { beforeEach, describe, expect, test, vi } from "vitest";
  10. import {
  11. ProviderEndpointsTable,
  12. AddEndpointButton,
  13. ProviderEndpointsSection,
  14. } from "@/app/[locale]/settings/providers/_components/provider-endpoints-table";
  15. import enMessages from "../../../../messages/en";
  16. vi.mock("next/navigation", () => ({
  17. useRouter: () => ({ refresh: vi.fn() }),
  18. }));
  19. const sonnerMocks = vi.hoisted(() => ({
  20. toast: {
  21. success: vi.fn(),
  22. error: vi.fn(),
  23. },
  24. }));
  25. vi.mock("sonner", () => sonnerMocks);
  26. vi.mock("@/components/ui/tooltip", () => ({
  27. Tooltip: ({ children }: { children: ReactNode }) => <>{children}</>,
  28. TooltipContent: ({ children }: { children: ReactNode }) => <span>{children}</span>,
  29. TooltipProvider: ({ children }: { children: ReactNode }) => <>{children}</>,
  30. TooltipTrigger: ({ children }: { children: ReactNode }) => <>{children}</>,
  31. }));
  32. const providerEndpointsActionMocks = vi.hoisted(() => ({
  33. addProviderEndpoint: vi.fn(async () => ({ ok: true, data: { endpoint: {} } })),
  34. editProviderEndpoint: vi.fn(async () => ({ ok: true, data: { endpoint: {} } })),
  35. getProviderEndpointProbeLogs: vi.fn(async () => ({ ok: true, data: { logs: [] } })),
  36. getProviderEndpoints: vi.fn(async () => [
  37. {
  38. id: 1,
  39. vendorId: 1,
  40. providerType: "claude",
  41. url: "https://api.claude.example.com/v1",
  42. label: null as string | null,
  43. sortOrder: 0,
  44. isEnabled: true,
  45. lastProbedAt: null,
  46. lastProbeOk: null,
  47. lastProbeLatencyMs: null,
  48. createdAt: "2026-01-01",
  49. updatedAt: "2026-01-01",
  50. },
  51. ]),
  52. getProviderEndpointsByVendor: vi.fn(async () => [
  53. {
  54. id: 1,
  55. vendorId: 1,
  56. providerType: "claude",
  57. url: "https://api.claude.example.com/v1",
  58. label: null as string | null,
  59. sortOrder: 0,
  60. isEnabled: true,
  61. lastProbedAt: null,
  62. lastProbeOk: null,
  63. lastProbeLatencyMs: null,
  64. createdAt: "2026-01-01",
  65. updatedAt: "2026-01-01",
  66. },
  67. {
  68. id: 2,
  69. vendorId: 1,
  70. providerType: "openai-compatible",
  71. url: "https://api.openai.example.com/v1",
  72. label: null as string | null,
  73. sortOrder: 0,
  74. isEnabled: false,
  75. lastProbedAt: "2026-01-01T12:00:00Z",
  76. lastProbeOk: true,
  77. lastProbeLatencyMs: 150,
  78. createdAt: "2026-01-01",
  79. updatedAt: "2026-01-01",
  80. },
  81. ]),
  82. getProviderVendors: vi.fn(async () => []),
  83. probeProviderEndpoint: vi.fn(async () => ({ ok: true, data: { result: { ok: true } } })),
  84. removeProviderEndpoint: vi.fn(async () => ({ ok: true })),
  85. removeProviderVendor: vi.fn(async () => ({ ok: true })),
  86. }));
  87. vi.mock("@/actions/provider-endpoints", () => providerEndpointsActionMocks);
  88. function loadMessages() {
  89. return {
  90. common: enMessages.common,
  91. errors: enMessages.errors,
  92. ui: enMessages.ui,
  93. forms: enMessages.forms,
  94. settings: enMessages.settings,
  95. };
  96. }
  97. let queryClient: QueryClient;
  98. function renderWithProviders(node: ReactNode) {
  99. const container = document.createElement("div");
  100. document.body.appendChild(container);
  101. const root = createRoot(container);
  102. act(() => {
  103. root.render(
  104. <QueryClientProvider client={queryClient}>
  105. <NextIntlClientProvider locale="en" messages={loadMessages()} timeZone="UTC">
  106. {node}
  107. </NextIntlClientProvider>
  108. </QueryClientProvider>
  109. );
  110. });
  111. return {
  112. unmount: () => {
  113. act(() => root.unmount());
  114. container.remove();
  115. },
  116. };
  117. }
  118. async function flushTicks(times = 3) {
  119. for (let i = 0; i < times; i++) {
  120. await act(async () => {
  121. await new Promise((r) => setTimeout(r, 0));
  122. });
  123. }
  124. }
  125. describe("ProviderEndpointsTable", () => {
  126. beforeEach(() => {
  127. queryClient = new QueryClient({
  128. defaultOptions: {
  129. queries: { retry: false },
  130. mutations: { retry: false },
  131. },
  132. });
  133. vi.clearAllMocks();
  134. while (document.body.firstChild) {
  135. document.body.removeChild(document.body.firstChild);
  136. }
  137. });
  138. test("renders endpoints from getProviderEndpointsByVendor when no providerType filter", async () => {
  139. const { unmount } = renderWithProviders(<ProviderEndpointsTable vendorId={1} />);
  140. await flushTicks(6);
  141. expect(providerEndpointsActionMocks.getProviderEndpointsByVendor).toHaveBeenCalledWith({
  142. vendorId: 1,
  143. });
  144. expect(document.body.textContent || "").toContain("https://api.claude.example.com/v1");
  145. expect(document.body.textContent || "").toContain("https://api.openai.example.com/v1");
  146. unmount();
  147. });
  148. test("renders endpoints from getProviderEndpoints when providerType filter is set", async () => {
  149. const { unmount } = renderWithProviders(
  150. <ProviderEndpointsTable vendorId={1} providerType="claude" />
  151. );
  152. await flushTicks(6);
  153. expect(providerEndpointsActionMocks.getProviderEndpoints).toHaveBeenCalledWith({
  154. vendorId: 1,
  155. providerType: "claude",
  156. });
  157. unmount();
  158. });
  159. test("hides type column when hideTypeColumn is true", async () => {
  160. const { unmount } = renderWithProviders(
  161. <ProviderEndpointsTable vendorId={1} hideTypeColumn={true} />
  162. );
  163. await flushTicks(6);
  164. const headers = Array.from(document.querySelectorAll("th")).map((th) => th.textContent);
  165. expect(headers).not.toContain("Type");
  166. unmount();
  167. });
  168. test("shows type column by default", async () => {
  169. const { unmount } = renderWithProviders(<ProviderEndpointsTable vendorId={1} />);
  170. await flushTicks(6);
  171. expect(document.body.textContent || "").toContain("Type");
  172. unmount();
  173. });
  174. test("hides actions column in readOnly mode", async () => {
  175. const { unmount } = renderWithProviders(
  176. <ProviderEndpointsTable vendorId={1} readOnly={true} />
  177. );
  178. await flushTicks(6);
  179. const headers = Array.from(document.querySelectorAll("th")).map((th) => th.textContent);
  180. expect(headers).not.toContain("Actions");
  181. const switchElements = document.querySelectorAll("[data-slot='switch']");
  182. expect(switchElements.length).toBe(0);
  183. unmount();
  184. });
  185. test("shows actions column by default", async () => {
  186. const { unmount } = renderWithProviders(<ProviderEndpointsTable vendorId={1} />);
  187. await flushTicks(6);
  188. expect(document.body.textContent || "").toContain("Actions");
  189. unmount();
  190. });
  191. test("toggle switch calls editProviderEndpoint", async () => {
  192. const { unmount } = renderWithProviders(<ProviderEndpointsTable vendorId={1} />);
  193. await flushTicks(6);
  194. const endpointRow = Array.from(document.querySelectorAll("tr")).find((row) =>
  195. row.textContent?.includes("https://api.claude.example.com/v1")
  196. );
  197. expect(endpointRow).toBeDefined();
  198. const switchEl = endpointRow?.querySelector<HTMLElement>("[data-slot='switch']");
  199. expect(switchEl).not.toBeNull();
  200. switchEl?.click();
  201. await flushTicks(2);
  202. expect(providerEndpointsActionMocks.editProviderEndpoint).toHaveBeenCalledWith(
  203. expect.objectContaining({ endpointId: 1, isEnabled: false })
  204. );
  205. unmount();
  206. });
  207. test("probe button calls probeProviderEndpoint", async () => {
  208. const { unmount } = renderWithProviders(<ProviderEndpointsTable vendorId={1} />);
  209. await flushTicks(6);
  210. const probeButtons = document.querySelectorAll("button");
  211. const probeButton = Array.from(probeButtons).find((btn) =>
  212. btn.querySelector("svg.lucide-play")
  213. );
  214. expect(probeButton).toBeDefined();
  215. probeButton?.click();
  216. await flushTicks(2);
  217. expect(providerEndpointsActionMocks.probeProviderEndpoint).toHaveBeenCalledWith({
  218. endpointId: 1,
  219. });
  220. unmount();
  221. });
  222. test("shows empty state when no endpoints", async () => {
  223. providerEndpointsActionMocks.getProviderEndpointsByVendor.mockResolvedValueOnce([]);
  224. const { unmount } = renderWithProviders(<ProviderEndpointsTable vendorId={1} />);
  225. await flushTicks(6);
  226. expect(document.body.textContent || "").toContain("No endpoints");
  227. unmount();
  228. });
  229. test("displays enabled/disabled badge correctly", async () => {
  230. const { unmount } = renderWithProviders(<ProviderEndpointsTable vendorId={1} />);
  231. await flushTicks(6);
  232. expect(document.body.textContent || "").toContain("enabled");
  233. expect(document.body.textContent || "").toContain("disabled");
  234. unmount();
  235. });
  236. test("edit dialog submits with label, sortOrder, and isEnabled", async () => {
  237. providerEndpointsActionMocks.getProviderEndpointsByVendor.mockResolvedValueOnce([
  238. {
  239. id: 10,
  240. vendorId: 1,
  241. providerType: "claude",
  242. url: "https://original.example.com/v1",
  243. label: "Original Label",
  244. sortOrder: 3,
  245. isEnabled: true,
  246. lastProbedAt: null,
  247. lastProbeOk: null,
  248. lastProbeLatencyMs: null,
  249. createdAt: "2026-01-01",
  250. updatedAt: "2026-01-01",
  251. },
  252. ]);
  253. const { unmount } = renderWithProviders(<ProviderEndpointsTable vendorId={1} />);
  254. await flushTicks(6);
  255. const editButtons = document.querySelectorAll("button");
  256. const editButton = Array.from(editButtons).find((btn) => btn.querySelector("svg.lucide-pen"));
  257. expect(editButton).toBeDefined();
  258. act(() => {
  259. editButton?.click();
  260. });
  261. await flushTicks(4);
  262. const urlInput = document.querySelector<HTMLInputElement>('input[name="url"]');
  263. const labelInput = document.querySelector<HTMLInputElement>('input[name="label"]');
  264. const sortOrderInput = document.querySelector<HTMLInputElement>('input[name="sortOrder"]');
  265. expect(urlInput?.value).toBe("https://original.example.com/v1");
  266. expect(labelInput?.value).toBe("Original Label");
  267. expect(sortOrderInput?.value).toBe("3");
  268. act(() => {
  269. if (urlInput) {
  270. urlInput.value = "https://updated.example.com/v1";
  271. urlInput.dispatchEvent(new Event("input", { bubbles: true }));
  272. }
  273. if (labelInput) {
  274. labelInput.value = "Updated Label";
  275. labelInput.dispatchEvent(new Event("input", { bubbles: true }));
  276. }
  277. if (sortOrderInput) {
  278. sortOrderInput.value = "10";
  279. sortOrderInput.dispatchEvent(new Event("input", { bubbles: true }));
  280. }
  281. });
  282. const form = document.querySelector("form");
  283. act(() => {
  284. form?.dispatchEvent(new Event("submit", { bubbles: true, cancelable: true }));
  285. });
  286. await flushTicks(4);
  287. expect(providerEndpointsActionMocks.editProviderEndpoint).toHaveBeenCalledWith(
  288. expect.objectContaining({
  289. endpointId: 10,
  290. url: "https://updated.example.com/v1",
  291. label: "Updated Label",
  292. sortOrder: 10,
  293. isEnabled: true,
  294. })
  295. );
  296. unmount();
  297. });
  298. test("edit dialog shows ONLY success toast on success", async () => {
  299. providerEndpointsActionMocks.getProviderEndpointsByVendor.mockResolvedValueOnce([
  300. {
  301. id: 11,
  302. vendorId: 1,
  303. providerType: "claude",
  304. url: "https://original.example.com/v1",
  305. label: null,
  306. sortOrder: 0,
  307. isEnabled: true,
  308. lastProbedAt: null,
  309. lastProbeOk: null,
  310. lastProbeLatencyMs: null,
  311. createdAt: "2026-01-01",
  312. updatedAt: "2026-01-01",
  313. },
  314. ]);
  315. const { unmount } = renderWithProviders(<ProviderEndpointsTable vendorId={1} />);
  316. await flushTicks(6);
  317. const editButtons = document.querySelectorAll("button");
  318. const editButton = Array.from(editButtons).find((btn) => btn.querySelector("svg.lucide-pen"));
  319. expect(editButton).toBeDefined();
  320. act(() => {
  321. editButton?.click();
  322. });
  323. await flushTicks(4);
  324. const form = document.querySelector("form");
  325. act(() => {
  326. form?.dispatchEvent(new Event("submit", { bubbles: true, cancelable: true }));
  327. });
  328. await flushTicks(4);
  329. expect(sonnerMocks.toast.success).toHaveBeenCalledTimes(1);
  330. expect(sonnerMocks.toast.error).toHaveBeenCalledTimes(0);
  331. unmount();
  332. });
  333. test("edit dialog shows ONLY error toast on failure", async () => {
  334. providerEndpointsActionMocks.getProviderEndpointsByVendor.mockResolvedValueOnce([
  335. {
  336. id: 12,
  337. vendorId: 1,
  338. providerType: "claude",
  339. url: "https://original.example.com/v1",
  340. label: null,
  341. sortOrder: 0,
  342. isEnabled: true,
  343. lastProbedAt: null,
  344. lastProbeOk: null,
  345. lastProbeLatencyMs: null,
  346. createdAt: "2026-01-01",
  347. updatedAt: "2026-01-01",
  348. },
  349. ]);
  350. providerEndpointsActionMocks.editProviderEndpoint.mockResolvedValueOnce({
  351. ok: false,
  352. error: "Update failed",
  353. errorCode: "UPDATE_FAILED",
  354. } as any);
  355. const { unmount } = renderWithProviders(<ProviderEndpointsTable vendorId={1} />);
  356. await flushTicks(6);
  357. const editButtons = document.querySelectorAll("button");
  358. const editButton = Array.from(editButtons).find((btn) => btn.querySelector("svg.lucide-pen"));
  359. expect(editButton).toBeDefined();
  360. act(() => {
  361. editButton?.click();
  362. });
  363. await flushTicks(4);
  364. const form = document.querySelector("form");
  365. act(() => {
  366. form?.dispatchEvent(new Event("submit", { bubbles: true, cancelable: true }));
  367. });
  368. await flushTicks(4);
  369. expect(sonnerMocks.toast.success).toHaveBeenCalledTimes(0);
  370. expect(sonnerMocks.toast.error).toHaveBeenCalledTimes(1);
  371. unmount();
  372. });
  373. });
  374. describe("AddEndpointButton", () => {
  375. beforeEach(() => {
  376. queryClient = new QueryClient({
  377. defaultOptions: {
  378. queries: { retry: false },
  379. mutations: { retry: false },
  380. },
  381. });
  382. vi.clearAllMocks();
  383. while (document.body.firstChild) {
  384. document.body.removeChild(document.body.firstChild);
  385. }
  386. });
  387. test("renders add button", async () => {
  388. const { unmount } = renderWithProviders(<AddEndpointButton vendorId={1} />);
  389. await flushTicks(2);
  390. expect(document.body.textContent || "").toContain("Add Endpoint");
  391. unmount();
  392. });
  393. test("opens dialog on click", async () => {
  394. const { unmount } = renderWithProviders(<AddEndpointButton vendorId={1} />);
  395. await flushTicks(2);
  396. const addButton = document.querySelector("button");
  397. addButton?.click();
  398. await flushTicks(2);
  399. expect(document.body.textContent || "").toContain("URL");
  400. unmount();
  401. });
  402. test("shows type selector when no fixed providerType", async () => {
  403. const { unmount } = renderWithProviders(<AddEndpointButton vendorId={1} />);
  404. await flushTicks(2);
  405. const addButton = document.querySelector("button");
  406. addButton?.click();
  407. await flushTicks(2);
  408. expect(document.body.textContent || "").toContain("Type");
  409. unmount();
  410. });
  411. test("hides type selector when providerType is fixed", async () => {
  412. const { unmount } = renderWithProviders(
  413. <AddEndpointButton vendorId={1} providerType="claude" />
  414. );
  415. await flushTicks(2);
  416. const addButton = document.querySelector("button");
  417. addButton?.click();
  418. await flushTicks(2);
  419. const labels = Array.from(document.querySelectorAll("label")).map((l) => l.textContent);
  420. const hasTypeLabel = labels.some((l) => l === "Type");
  421. expect(hasTypeLabel).toBe(false);
  422. unmount();
  423. });
  424. test("submits with label, sortOrder, and isEnabled fields", async () => {
  425. const { unmount } = renderWithProviders(<AddEndpointButton vendorId={1} />);
  426. await flushTicks(2);
  427. const addButton = document.querySelector("button");
  428. act(() => {
  429. addButton?.click();
  430. });
  431. await flushTicks(2);
  432. const urlInput = document.querySelector<HTMLInputElement>('input[name="url"]');
  433. const labelInput = document.querySelector<HTMLInputElement>('input[name="label"]');
  434. const sortOrderInput = document.querySelector<HTMLInputElement>('input[name="sortOrder"]');
  435. act(() => {
  436. if (urlInput) {
  437. urlInput.value = "https://test.example.com/v1";
  438. urlInput.dispatchEvent(new Event("input", { bubbles: true }));
  439. }
  440. if (labelInput) {
  441. labelInput.value = "Test Label";
  442. labelInput.dispatchEvent(new Event("input", { bubbles: true }));
  443. }
  444. if (sortOrderInput) {
  445. sortOrderInput.value = "5";
  446. sortOrderInput.dispatchEvent(new Event("input", { bubbles: true }));
  447. }
  448. });
  449. const form = document.querySelector("form");
  450. act(() => {
  451. form?.dispatchEvent(new Event("submit", { bubbles: true, cancelable: true }));
  452. });
  453. await flushTicks(4);
  454. expect(providerEndpointsActionMocks.addProviderEndpoint).toHaveBeenCalledWith(
  455. expect.objectContaining({
  456. vendorId: 1,
  457. url: "https://test.example.com/v1",
  458. label: "Test Label",
  459. sortOrder: 5,
  460. isEnabled: true,
  461. })
  462. );
  463. unmount();
  464. });
  465. test("should show ONLY success toast on success", async () => {
  466. const { unmount } = renderWithProviders(<AddEndpointButton vendorId={1} />);
  467. await flushTicks(2);
  468. const addButton = document.querySelector("button");
  469. act(() => {
  470. addButton?.click();
  471. });
  472. await flushTicks(2);
  473. const urlInput = document.querySelector<HTMLInputElement>('input[name="url"]');
  474. act(() => {
  475. if (urlInput) {
  476. urlInput.value = "https://test.example.com/v1";
  477. urlInput.dispatchEvent(new Event("input", { bubbles: true }));
  478. }
  479. });
  480. const form = document.querySelector("form");
  481. act(() => {
  482. form?.dispatchEvent(new Event("submit", { bubbles: true, cancelable: true }));
  483. });
  484. await flushTicks(4);
  485. expect(sonnerMocks.toast.success).toHaveBeenCalledTimes(1);
  486. expect(sonnerMocks.toast.error).toHaveBeenCalledTimes(0);
  487. unmount();
  488. });
  489. test("should show ONLY error toast on failure", async () => {
  490. providerEndpointsActionMocks.addProviderEndpoint.mockResolvedValueOnce({
  491. ok: false,
  492. error: "Some error",
  493. errorCode: "CREATE_FAILED",
  494. } as any);
  495. const { unmount } = renderWithProviders(<AddEndpointButton vendorId={1} />);
  496. await flushTicks(2);
  497. const addButton = document.querySelector("button");
  498. act(() => {
  499. addButton?.click();
  500. });
  501. await flushTicks(2);
  502. const urlInput = document.querySelector<HTMLInputElement>('input[name="url"]');
  503. act(() => {
  504. if (urlInput) {
  505. urlInput.value = "https://test.example.com/v1";
  506. urlInput.dispatchEvent(new Event("input", { bubbles: true }));
  507. }
  508. });
  509. const form = document.querySelector("form");
  510. act(() => {
  511. form?.dispatchEvent(new Event("submit", { bubbles: true, cancelable: true }));
  512. });
  513. await flushTicks(4);
  514. expect(sonnerMocks.toast.success).toHaveBeenCalledTimes(0);
  515. expect(sonnerMocks.toast.error).toHaveBeenCalledTimes(1);
  516. unmount();
  517. });
  518. });
  519. describe("ProviderEndpointsSection", () => {
  520. beforeEach(() => {
  521. queryClient = new QueryClient({
  522. defaultOptions: {
  523. queries: { retry: false },
  524. mutations: { retry: false },
  525. },
  526. });
  527. vi.clearAllMocks();
  528. while (document.body.firstChild) {
  529. document.body.removeChild(document.body.firstChild);
  530. }
  531. });
  532. test("renders section header with endpoints label", async () => {
  533. const { unmount } = renderWithProviders(<ProviderEndpointsSection vendorId={1} />);
  534. await flushTicks(6);
  535. expect(document.body.textContent || "").toContain("Endpoints");
  536. unmount();
  537. });
  538. test("renders add button in section header", async () => {
  539. const { unmount } = renderWithProviders(<ProviderEndpointsSection vendorId={1} />);
  540. await flushTicks(6);
  541. expect(document.body.textContent || "").toContain("Add Endpoint");
  542. unmount();
  543. });
  544. test("hides add button in readOnly mode", async () => {
  545. const { unmount } = renderWithProviders(
  546. <ProviderEndpointsSection vendorId={1} readOnly={true} />
  547. );
  548. await flushTicks(6);
  549. expect(document.body.textContent || "").not.toContain("Add Endpoint");
  550. unmount();
  551. });
  552. test("renders table with endpoints", async () => {
  553. const { unmount } = renderWithProviders(<ProviderEndpointsSection vendorId={1} />);
  554. await flushTicks(6);
  555. expect(document.body.textContent || "").toContain("https://api.claude.example.com/v1");
  556. unmount();
  557. });
  558. test("passes providerType filter to table", async () => {
  559. const { unmount } = renderWithProviders(
  560. <ProviderEndpointsSection vendorId={1} providerType="claude" />
  561. );
  562. await flushTicks(6);
  563. expect(providerEndpointsActionMocks.getProviderEndpoints).toHaveBeenCalledWith({
  564. vendorId: 1,
  565. providerType: "claude",
  566. });
  567. unmount();
  568. });
  569. });