// The add-on's answers: what a failed request says to the person using the page. import { afterEach, describe, expect, it, vi } from "vitest"; import { getJson, send } from "../src/api"; const answer = (status: number, body: string, type = "application/json") => vi.stubGlobal("fetch", vi.fn(async () => new Response(body, { status, headers: { "Content-Type": type } }))); afterEach(() => vi.unstubAllGlobals()); describe("api", () => { it("shows the add-on's own sentence for any failed answer with a JSON error (app 0.2.78)", async () => { answer(400, JSON.stringify({ error: "Home Assistant refused light.turn_on: the entity is unavailable." })); await expect(send("screens/x", "PUT", {})).rejects.toThrow("Home Assistant refused light.turn_on: the entity is unavailable."); answer(503, JSON.stringify({ error: "Home Assistant didn't answer in time. Try again in a moment." })); await expect(getJson("capabilities?entity=light.a")).rejects.toThrow("Home Assistant didn't answer in time. Try again in a moment."); answer(502, JSON.stringify({ error: "Bad gateway from Home Assistant." })); await expect(getJson("states")).rejects.toThrow("Bad gateway from Home Assistant."); }); it("keeps the general sentence for an answer that isn't JSON or has no error in it", async () => { const general = "That didn't work. Refresh the page and try again."; answer(502, "502 Bad Gateway", "text/html"); await expect(getJson("inventory")).rejects.toThrow(general); answer(500, JSON.stringify({ detail: "nope" })); await expect(getJson("inventory")).rejects.toThrow(general); answer(500, JSON.stringify({ error: "" })); await expect(getJson("inventory")).rejects.toThrow(general); }); it("sends relative URLs with the CSRF header and reads an empty answer as null", async () => { answer(200, ""); expect(await send("screens/x/identify", "POST")).toBeNull(); const call = (fetch as unknown as ReturnType).mock.calls[0]; expect(call[0]).toBe("api/screens/x/identify"); expect(call[1].headers).toHaveProperty("X-Screen-CSRF"); }); });