Download frontend/src/ui/lib/apiFetch.test.ts from ruslanmv/HomePilot: direct link, hf CLI and curl.
- Browser
- Download file 2.65 kB
-
https://huggingface.co/spaces/ruslanmv/HomePilot/resolve/main/frontend/src/ui/lib/apiFetch.test.ts
- Command line
-
hf download hf://spaces/ruslanmv/HomePilot/frontend/src/ui/lib/apiFetch.test.ts
-
curl -L -o apiFetch.test.ts https://huggingface.co/spaces/ruslanmv/HomePilot/resolve/main/frontend/src/ui/lib/apiFetch.test.ts
2.65 kB
| /** | |
| * Step-1 hotfix regression tests for the Settings API fetch guard. | |
| * | |
| * The reported "Unexpected token '<', "<!doctype "... is not valid JSON" error | |
| * came from parsing the SPA's index.html as JSON when a Settings API route was | |
| * not forwarded to the backend. fetchApiJson must turn that into an actionable | |
| * message and never throw a raw JSON.parse error. | |
| */ | |
| import { afterEach, describe, expect, it, vi } from "vitest"; | |
| import { fetchApiJson } from "./apiFetch"; | |
| function mockFetch(res: { ok?: boolean; status?: number; contentType?: string; body?: unknown }) { | |
| const headers = new Headers(); | |
| if (res.contentType) headers.set("content-type", res.contentType); | |
| const ok = res.ok ?? true; | |
| const status = res.status ?? (ok ? 200 : 500); | |
| vi.stubGlobal( | |
| "fetch", | |
| vi.fn(async () => ({ | |
| ok, | |
| status, | |
| headers, | |
| json: async () => { | |
| if (res.body === undefined) throw new SyntaxError("Unexpected token '<'"); | |
| return res.body; | |
| }, | |
| })), | |
| ); | |
| } | |
| afterEach(() => { | |
| vi.unstubAllGlobals(); | |
| vi.restoreAllMocks(); | |
| }); | |
| describe("fetchApiJson", () => { | |
| it("returns parsed JSON on a normal application/json response", async () => { | |
| mockFetch({ ok: true, status: 200, contentType: "application/json", body: { ok: true, providers: {} } }); | |
| await expect(fetchApiJson("https://homepilot.ruslanmv.com/providers")).resolves.toEqual({ | |
| ok: true, | |
| providers: {}, | |
| }); | |
| }); | |
| it("throws an actionable deployment message when HTML is returned (SPA fallback)", async () => { | |
| mockFetch({ ok: true, status: 200, contentType: "text/html; charset=utf-8" }); | |
| await expect(fetchApiJson("https://homepilot.ruslanmv.com/providers")).rejects.toThrow( | |
| /providers API is unavailable on this deployment/i, | |
| ); | |
| }); | |
| it("never surfaces a raw JSON parse error for an HTML body", async () => { | |
| mockFetch({ ok: true, status: 200, contentType: "text/html" }); | |
| await expect(fetchApiJson("https://homepilot.ruslanmv.com/models")).rejects.not.toThrow( | |
| /Unexpected token/i, | |
| ); | |
| }); | |
| it("surfaces the JSON error message on a non-OK JSON response", async () => { | |
| mockFetch({ ok: false, status: 502, contentType: "application/json", body: { message: "cloud down" } }); | |
| await expect(fetchApiJson("https://homepilot.ruslanmv.com/providers")).rejects.toThrow("cloud down"); | |
| }); | |
| it("includes the HTTP status when a non-JSON error has no body message", async () => { | |
| mockFetch({ ok: false, status: 404, contentType: "text/plain" }); | |
| await expect(fetchApiJson("https://homepilot.ruslanmv.com/providers")).rejects.toThrow(/HTTP 404/); | |
| }); | |
| }); | |