From a8f3624b4c08d0b85b0f2af5b7898d1cba1f7198 Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 23 Feb 2026 22:30:04 +0000 Subject: [PATCH] story-kit: merge 110_story_add_test_coverage_for_api_settings_ts --- frontend/src/api/settings.test.ts | 134 ++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 frontend/src/api/settings.test.ts diff --git a/frontend/src/api/settings.test.ts b/frontend/src/api/settings.test.ts new file mode 100644 index 0000000..963799e --- /dev/null +++ b/frontend/src/api/settings.test.ts @@ -0,0 +1,134 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { settingsApi } from "./settings"; + +const mockFetch = vi.fn(); + +beforeEach(() => { + vi.stubGlobal("fetch", mockFetch); +}); + +afterEach(() => { + vi.restoreAllMocks(); +}); + +function okResponse(body: unknown) { + return new Response(JSON.stringify(body), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); +} + +function errorResponse(status: number, text: string) { + return new Response(text, { status }); +} + +describe("settingsApi", () => { + describe("getEditorCommand", () => { + it("sends GET to /settings/editor and returns editor settings", async () => { + const expected = { editor_command: "zed" }; + mockFetch.mockResolvedValueOnce(okResponse(expected)); + + const result = await settingsApi.getEditorCommand(); + + expect(mockFetch).toHaveBeenCalledWith( + "/api/settings/editor", + expect.objectContaining({ + headers: expect.objectContaining({ + "Content-Type": "application/json", + }), + }), + ); + expect(result).toEqual(expected); + }); + + it("returns null editor_command when not configured", async () => { + const expected = { editor_command: null }; + mockFetch.mockResolvedValueOnce(okResponse(expected)); + + const result = await settingsApi.getEditorCommand(); + expect(result.editor_command).toBeNull(); + }); + + it("uses custom baseUrl when provided", async () => { + mockFetch.mockResolvedValueOnce(okResponse({ editor_command: "code" })); + + await settingsApi.getEditorCommand("http://localhost:4000/api"); + + expect(mockFetch).toHaveBeenCalledWith( + "http://localhost:4000/api/settings/editor", + expect.anything(), + ); + }); + }); + + describe("setEditorCommand", () => { + it("sends PUT to /settings/editor with command body", async () => { + const expected = { editor_command: "zed" }; + mockFetch.mockResolvedValueOnce(okResponse(expected)); + + const result = await settingsApi.setEditorCommand("zed"); + + expect(mockFetch).toHaveBeenCalledWith( + "/api/settings/editor", + expect.objectContaining({ + method: "PUT", + body: JSON.stringify({ editor_command: "zed" }), + }), + ); + expect(result).toEqual(expected); + }); + + it("sends PUT with null to clear the editor command", async () => { + const expected = { editor_command: null }; + mockFetch.mockResolvedValueOnce(okResponse(expected)); + + const result = await settingsApi.setEditorCommand(null); + + expect(mockFetch).toHaveBeenCalledWith( + "/api/settings/editor", + expect.objectContaining({ + method: "PUT", + body: JSON.stringify({ editor_command: null }), + }), + ); + expect(result.editor_command).toBeNull(); + }); + + it("uses custom baseUrl when provided", async () => { + mockFetch.mockResolvedValueOnce(okResponse({ editor_command: "vim" })); + + await settingsApi.setEditorCommand("vim", "http://localhost:4000/api"); + + expect(mockFetch).toHaveBeenCalledWith( + "http://localhost:4000/api/settings/editor", + expect.objectContaining({ method: "PUT" }), + ); + }); + }); + + describe("error handling", () => { + it("throws with response body text on non-ok response", async () => { + mockFetch.mockResolvedValueOnce(errorResponse(400, "Bad Request")); + + await expect(settingsApi.getEditorCommand()).rejects.toThrow( + "Bad Request", + ); + }); + + it("throws with status code message when response body is empty", async () => { + mockFetch.mockResolvedValueOnce(errorResponse(500, "")); + + await expect(settingsApi.getEditorCommand()).rejects.toThrow( + "Request failed (500)", + ); + }); + + it("throws on setEditorCommand error", async () => { + mockFetch.mockResolvedValueOnce(errorResponse(403, "Forbidden")); + + await expect(settingsApi.setEditorCommand("code")).rejects.toThrow( + "Forbidden", + ); + }); + }); +});