diff --git a/tests/unit/utils/net/http-response.spec.ts b/tests/unit/utils/net/http-response.spec.ts new file mode 100644 index 0000000..5d4dab0 --- /dev/null +++ b/tests/unit/utils/net/http-response.spec.ts @@ -0,0 +1,267 @@ +import { getHeader } from "@/utils/net"; +import { Blob } from "@/utils/net/blob"; +import { FormData } from "@/utils/net/form-data"; +import { HttpResponse } from "@/utils/net/http-response"; + +describe("HttpResponse", () => { + describe("cache", () => { + test("returns a cached HttpResponse with a reusable arrayBuffer body", async () => { + const blob = new Blob(["blob body"]); + const body = await blob.arrayBuffer(); + const response = HttpResponse.blob(blob); + + const cachedResponse = HttpResponse.cache(response); + + const firstRead = await cachedResponse.arrayBuffer(); + const secondRead = await cachedResponse.arrayBuffer(); + + expect(firstRead).toEqual(body); + expect(secondRead).toEqual(body); + }); + + test("returns a cached HttpResponse with a reusable blob body", async () => { + const body = new Blob(["blob body"]); + const response = HttpResponse.blob(body); + + const cachedResponse = HttpResponse.cache(response); + + const firstRead = await cachedResponse.blob(); + const secondRead = await cachedResponse.blob(); + + expect(firstRead).toEqual(body); + expect(secondRead).toEqual(body); + }); + + test("returns a cached HttpResponse with a reusable formData body", async () => { + const body = new FormData(); + body.append("key", "value"); + const response = HttpResponse.formData(body); + + const cachedResponse = HttpResponse.cache(response); + + const firstRead = await cachedResponse.formData(); + const secondRead = await cachedResponse.formData(); + + expect(firstRead).toEqual(body); + expect(secondRead).toEqual(body); + }); + + test("returns a cached HttpResponse with a reusable json body", async () => { + const body = { key: "value" }; + const response = HttpResponse.json(body); + + const cachedResponse = HttpResponse.cache(response); + + const firstRead = await cachedResponse.json(); + const secondRead = await cachedResponse.json(); + + expect(firstRead).toEqual(body); + expect(secondRead).toEqual(body); + }); + + test("returns a cached HttpResponse with a reusable text body", async () => { + const body = "response body"; + const response = HttpResponse.text(body); + + const cachedResponse = HttpResponse.cache(response); + + const firstRead = await cachedResponse.text(); + const secondRead = await cachedResponse.text(); + + expect(firstRead).toBe(body); + expect(secondRead).toBe(body); + }); + }); + + describe("blob", () => { + test("creates a new HttpResponse with a Blob body", async () => { + const blob = new Blob(["blob body"]); + + const response = HttpResponse.blob(blob); + const body = await response.blob(); + + expect(body).toEqual(blob); + }); + + test("sets the Content-Type header to 'application/octet-stream'", async () => { + const blob = new Blob(["blob body"]); + + const response = HttpResponse.blob(blob); + const body = await response.blob(); + + expect(body).toEqual(blob); + expect(getHeader(response.headers, "Content-Type")).toBe("application/octet-stream"); + }); + + test("uses the provided options to configure the response", async () => { + const blob = new Blob(["blob body"]); + const options = { status: 200, statusText: "OK", headers: { "X-Custom-Header": "Custom Value" } }; + + const response = HttpResponse.blob(blob, options); + const body = await response.blob(); + + expect(body).toEqual(blob); + expect(response.status).toBe(options.status); + expect(response.statusText).toBe(options.statusText); + expect(getHeader(response.headers, "X-Custom-Header")).toBe(options.headers["X-Custom-Header"]); + }); + }); + + describe("formData", () => { + test("creates a new HttpResponse with a FormData body", async () => { + const formData = new FormData(); + formData.append("key", "value"); + + const response = HttpResponse.formData(formData); + const body = await response.formData(); + + expect(body).toEqual(formData); + }); + + test("sets the Content-Type header to 'multipart/form-data'", async () => { + const formData = new FormData(); + formData.append("key", "value"); + + const response = HttpResponse.formData(formData); + const body = await response.formData(); + + expect(body).toEqual(formData); + expect(getHeader(response.headers, "Content-Type")).toMatch("multipart/form-data"); + }); + + test("uses the provided options to configure the response", async () => { + const formData = new FormData(); + formData.append("key", "value"); + const options = { status: 200, statusText: "OK", headers: { "X-Custom-Header": "Custom Value" } }; + + const response = HttpResponse.formData(formData, options); + const body = await response.formData(); + + expect(body).toEqual(formData); + expect(response.status).toBe(options.status); + expect(response.statusText).toBe(options.statusText); + expect(getHeader(response.headers, "X-Custom-Header")).toBe(options.headers["X-Custom-Header"]); + }); + }); + + describe("json", () => { + test("creates a new HttpResponse with a JSON body", async () => { + const data = { key: "value" }; + + const response = HttpResponse.json(data); + const body = await response.json(); + + expect(body).toEqual(data); + }); + + test("sets the Content-Type header to 'application/json'", async () => { + const data = { key: "value" }; + + const response = HttpResponse.json(data); + const body = await response.json(); + + expect(body).toEqual(data); + expect(getHeader(response.headers, "Content-Type")).toBe("application/json"); + }); + + test("uses the provided options to configure the response", async () => { + const data = { key: "value" }; + const options = { status: 200, statusText: "OK", headers: { "X-Custom-Header": "Custom Value" } }; + + const response = HttpResponse.json(data, options); + const body = await response.json(); + + expect(body).toEqual(data); + expect(response.status).toBe(options.status); + expect(response.statusText).toBe(options.statusText); + expect(getHeader(response.headers, "X-Custom-Header")).toBe(options.headers["X-Custom-Header"]); + }); + + test("serializes non-string data as JSON", async () => { + const data = { key: "value" }; + + const response = HttpResponse.json(data); + const body = await response.text(); + + expect(body).toBe(JSON.stringify(data)); + }); + + test("does not serialize string data", async () => { + const data = JSON.stringify({ key: "value" }); + + const response = HttpResponse.json(data); + const body = await response.text(); + + expect(body).toBe(data); + }); + }); + + describe("text", () => { + test("creates a new HttpResponse with a text body", async () => { + const text = "response text"; + + const response = HttpResponse.text(text); + const body = await response.text(); + + expect(body).toBe(text); + }); + + test("sets the Content-Type header to 'text/plain'", async () => { + const text = "response text"; + + const response = HttpResponse.text(text); + const body = await response.text(); + + expect(body).toBe(text); + expect(getHeader(response.headers, "Content-Type")).toBe("text/plain"); + }); + + test("uses the provided options to configure the response", async () => { + const text = "response text"; + const options = { status: 200, statusText: "OK", headers: { "X-Custom-Header": "Custom Value" } }; + + const response = HttpResponse.text(text, options); + const body = await response.text(); + + expect(body).toBe(text); + expect(response.status).toBe(options.status); + expect(response.statusText).toBe(options.statusText); + expect(getHeader(response.headers, "X-Custom-Header")).toBe(options.headers["X-Custom-Header"]); + }); + }); + + describe("redirect", () => { + test("creates a new HttpResponse with a redirection status", () => { + const url = "http://example.com"; + const options = { status: 302 }; + + const response = HttpResponse.redirect(url, options); + + expect(response.status).toBe(options.status); + expect(getHeader(response.headers, "Location")).toBe(url); + }); + + test("uses the provided options to configure the response", () => { + const url = "http://example.com"; + const options = { status: 301, statusText: "Moved Permanently", headers: { "X-Custom-Header": "Custom Value" } }; + + const response = HttpResponse.redirect(url, options); + + expect(response.status).toBe(options.status); + expect(response.statusText).toBe(options.statusText); + expect(getHeader(response.headers, "Location")).toBe(url); + expect(getHeader(response.headers, "X-Custom-Header")).toBe(options.headers["X-Custom-Header"]); + }); + }); + + describe("error", () => { + test("creates a new HttpResponse representing an error", async () => { + const response = HttpResponse.error(); + + await expect(response.text).rejects.toThrow(); + expect(response.ok).toBe(false); + expect(response.status).toBe(0); + expect(response.statusText).toBe(""); + }); + }); +});