diff --git a/frontend/app/chat/[chatId]/components/ChatInput/__tests__/ChatInput.test.tsx b/frontend/app/chat/[chatId]/components/ChatInput/__tests__/ChatInput.test.tsx
index 167a870fe..5578b6a12 100644
--- a/frontend/app/chat/[chatId]/components/ChatInput/__tests__/ChatInput.test.tsx
+++ b/frontend/app/chat/[chatId]/components/ChatInput/__tests__/ChatInput.test.tsx
@@ -19,9 +19,7 @@ afterEach(() => {
describe("ChatInput", () => {
it("should render correctly", () => {
// Rendering the ChatInput component
- const { getByTestId, baseElement } = render();
-
- console.log({ baseElement });
+ const { getByTestId } = render();
const chatInputForm = getByTestId("chat-input-form");
expect(chatInputForm).toBeDefined();
diff --git a/frontend/app/config/components/ApiKeyConfig/hooks/__tests__/useApiKeyConfig.test.ts b/frontend/app/config/components/ApiKeyConfig/hooks/__tests__/useApiKeyConfig.test.ts
new file mode 100644
index 000000000..ae755f11f
--- /dev/null
+++ b/frontend/app/config/components/ApiKeyConfig/hooks/__tests__/useApiKeyConfig.test.ts
@@ -0,0 +1,67 @@
+import { act, renderHook } from "@testing-library/react";
+import { afterEach, describe, expect, it, vi } from "vitest";
+
+import { useApiKeyConfig } from "../useApiKeyConfig";
+
+const createApiKeyMock = vi.fn(() => "dummyApiKey");
+const trackMock = vi.fn((props: unknown) => ({ props }));
+
+const useAuthApiMock = vi.fn(() => ({
+ createApiKey: () => createApiKeyMock(),
+}));
+
+const useEventTrackingMock = vi.fn(() => ({
+ track: (props: unknown) => trackMock(props),
+}));
+
+vi.mock("@/lib/api/auth/useAuthApi", () => ({
+ useAuthApi: () => useAuthApiMock(),
+}));
+vi.mock("@/services/analytics/useEventTracking", () => ({
+ useEventTracking: () => useEventTrackingMock(),
+}));
+
+describe("useApiKeyConfig", () => {
+ afterEach(() => {
+ vi.restoreAllMocks();
+ });
+
+ it("should set the apiKey when handleCreateClick is called", async () => {
+ const { result } = renderHook(() => useApiKeyConfig());
+
+ await act(async () => {
+ await result.current.handleCreateClick();
+ });
+
+ expect(createApiKeyMock).toHaveBeenCalledTimes(1);
+ expect(trackMock).toHaveBeenCalledWith("CREATE_API_KEY");
+ expect(result.current.apiKey).toBe("dummyApiKey");
+ });
+
+ it("should call copyToClipboard when handleCopyClick is called with a non-empty apiKey", () => {
+ vi.mock("react", async () => {
+ const actual = await vi.importActual("react");
+
+ return {
+ ...actual,
+ useState: () => ["dummyApiKey", vi.fn()],
+ };
+ });
+ //@ts-ignore - clipboard is not actually readonly
+ global.navigator.clipboard = {
+ writeText: vi.fn(),
+ };
+
+ const { result } = renderHook(() => useApiKeyConfig());
+
+ act(() => result.current.handleCopyClick());
+
+ expect(trackMock).toHaveBeenCalledTimes(1);
+ expect(trackMock).toHaveBeenCalledWith("COPY_API_KEY");
+
+ // eslint-disable-next-line @typescript-eslint/unbound-method
+ expect(global.navigator.clipboard.writeText).toHaveBeenCalledWith(
+ "dummyApiKey"
+ );
+ });
+});
diff --git a/frontend/app/config/components/ApiKeyConfig/hooks/useApiKeyConfig.ts b/frontend/app/config/components/ApiKeyConfig/hooks/useApiKeyConfig.ts
index d4d81fb29..6d9b3d064 100644
--- a/frontend/app/config/components/ApiKeyConfig/hooks/useApiKeyConfig.ts
+++ b/frontend/app/config/components/ApiKeyConfig/hooks/useApiKeyConfig.ts
@@ -1,21 +1,19 @@
import { useState } from "react";
-import { useAxios } from "@/lib/hooks";
+import { useAuthApi } from "@/lib/api/auth/useAuthApi";
import { useEventTracking } from "@/services/analytics/useEventTracking";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const useApiKeyConfig = () => {
const [apiKey, setApiKey] = useState("");
- const { axiosInstance } = useAxios();
const { track } = useEventTracking();
+ const { createApiKey } = useAuthApi();
const handleCreateClick = async () => {
try {
void track("CREATE_API_KEY");
- const response = await axiosInstance.post<{ api_key: string }>(
- "/api-key"
- ); // replace with your api-key endpoint URL
- setApiKey(response.data.api_key);
+ const createdApiKey = await createApiKey();
+ setApiKey(createdApiKey);
} catch (error) {
console.error("Error creating API key: ", error);
}
diff --git a/frontend/app/config/components/BackendConfig.tsx b/frontend/app/config/components/BackendConfig/BackendConfig.tsx
similarity index 100%
rename from frontend/app/config/components/BackendConfig.tsx
rename to frontend/app/config/components/BackendConfig/BackendConfig.tsx
diff --git a/frontend/app/config/components/BackendConfig/__tests__/BackendConfig.test.tsx b/frontend/app/config/components/BackendConfig/__tests__/BackendConfig.test.tsx
new file mode 100644
index 000000000..481fe7499
--- /dev/null
+++ b/frontend/app/config/components/BackendConfig/__tests__/BackendConfig.test.tsx
@@ -0,0 +1,24 @@
+import { render } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+
+import { BackendConfig } from "../BackendConfig";
+
+const registerMock = vi.fn(() => void 0);
+
+describe("BackendConfig", () => {
+ it("renders the component with fields and labels", () => {
+ //@ts-expect-error we don't need registerMock to return all `register` keys
+ const { getByText } = render();
+
+ expect(getByText("Backend config")).toBeDefined();
+ expect(getByText("Backend URL")).toBeDefined();
+ expect(getByText("Supabase URL")).toBeDefined();
+ expect(getByText("Supabase key")).toBeDefined();
+ expect(getByText("Keep in local")).toBeDefined();
+ expect(getByText("Keep in local")).toBeDefined();
+ expect(registerMock).toHaveBeenCalledWith("backendUrl");
+ expect(registerMock).toHaveBeenCalledWith("supabaseUrl");
+ expect(registerMock).toHaveBeenCalledWith("supabaseKey");
+ expect(registerMock).toHaveBeenCalledWith("backendUrl");
+ });
+});
diff --git a/frontend/app/config/components/ConfigForm.tsx b/frontend/app/config/components/ConfigForm.tsx
index 2d06f2b3a..fc499954e 100644
--- a/frontend/app/config/components/ConfigForm.tsx
+++ b/frontend/app/config/components/ConfigForm.tsx
@@ -6,7 +6,7 @@ import { useRouter } from "next/navigation";
import Button from "@/lib/components/ui/Button";
import { useConfig } from "../hooks/useConfig";
-import { BackendConfig } from "./BackendConfig";
+import { BackendConfig } from "./BackendConfig/BackendConfig";
import { ModelConfig } from "./ModelConfig";
import { UserAccountSection } from "./UserAccountSection";
diff --git a/frontend/lib/api/auth/__tests__/useAuthApi.test.ts b/frontend/lib/api/auth/__tests__/useAuthApi.test.ts
new file mode 100644
index 000000000..36d1cd022
--- /dev/null
+++ b/frontend/lib/api/auth/__tests__/useAuthApi.test.ts
@@ -0,0 +1,31 @@
+import { renderHook } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+
+import { useAuthApi } from "../useAuthApi";
+
+const axiosPostMock = vi.fn(() => ({
+ data: {
+ api_key: "",
+ },
+}));
+
+vi.mock("@/lib/hooks", () => ({
+ useAxios: () => ({
+ axiosInstance: {
+ post: axiosPostMock,
+ },
+ }),
+}));
+
+describe("useAuthApi", () => {
+ it("should call createApiKey with the correct parameters", async () => {
+ const {
+ result: {
+ current: { createApiKey },
+ },
+ } = renderHook(() => useAuthApi());
+ await createApiKey();
+ expect(axiosPostMock).toHaveBeenCalledTimes(1);
+ expect(axiosPostMock).toHaveBeenCalledWith("/api-key");
+ });
+});
diff --git a/frontend/lib/api/auth/auth.ts b/frontend/lib/api/auth/auth.ts
new file mode 100644
index 000000000..4c170aa5f
--- /dev/null
+++ b/frontend/lib/api/auth/auth.ts
@@ -0,0 +1,9 @@
+import { AxiosInstance } from "axios";
+
+export const createApiKey = async (
+ axiosInstance: AxiosInstance
+): Promise => {
+ const response = await axiosInstance.post<{ api_key: string }>("/api-key");
+
+ return response.data.api_key;
+};
diff --git a/frontend/lib/api/auth/useAuthApi.ts b/frontend/lib/api/auth/useAuthApi.ts
new file mode 100644
index 000000000..68a4ed0bd
--- /dev/null
+++ b/frontend/lib/api/auth/useAuthApi.ts
@@ -0,0 +1,12 @@
+import { useAxios } from "@/lib/hooks";
+
+import { createApiKey } from "./auth";
+
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+export const useAuthApi = () => {
+ const { axiosInstance } = useAxios();
+
+ return {
+ createApiKey: async () => createApiKey(axiosInstance),
+ };
+};