describe("Response", () => {
describe("constructor", () => {
it("should create an empty response", () => {
const response = new Response();
expect(response.ok).toBe(true);
expect(response.status).toBe(200);
expect(response.statusText).toBe("");
expect(response.headers instanceof Headers).toBe(true);
});
it("should create a response with body and init parameters", () => {
const init = {
status: 201,
statusText: "Created",
headers: { "Content-Type": "application/json" },
};
const body = JSON.stringify({ id: 1 });
const response = new Response(body, init);
expect(response.status).toBe(201);
expect(response.statusText).toBe("Created");
expect(response.headers.get("Content-Type")).toBe("application/json");
expect(response.ok).toBe(true);
});
});
describe("status validation", () => {
it("should set ok to true for successful status codes", () => {
const successCodes = [200, 201, 202, 203, 204, 206, 207, 299];
successCodes.forEach((status) => {
const response = new Response(null, { status });
expect(response.ok).toBe(true);
});
});
it("should set ok to false for error status codes", () => {
const errorCodes = [400, 404, 500, 503];
errorCodes.forEach((status) => {
const response = new Response(null, { status });
expect(response.ok).toBe(false);
});
});
it("should throw TypeError for invalid status codes", () => {
expect(() => new Response(null, { status: 99 })).toThrow(TypeError);
expect(() => new Response(null, { status: 600 })).toThrow(TypeError);
});
});
describe("body handling", () => {
it("should handle different body types", async () => {
const testCases = [
{
body: JSON.stringify({ test: "data" }),
type: "application/json",
check: async (response) => {
const data = await response.json();
expect(data.test).toBe("data");
},
},
{
body: "plain text",
type: "text/plain",
check: async (response) => {
const text = await response.text();
expect(text).toBe("plain text");
},
},
{
body: new ArrayBuffer(8),
type: "application/octet-stream",
check: async (response) => {
const buffer = await response.arrayBuffer();
expect(buffer instanceof ArrayBuffer).toBe(true);
expect(buffer.byteLength).toBe(8);
},
},
{
body: "blob content",
type: "text/plain",
check: async (response) => {
const blob = await response.blob();
expect(blob instanceof Blob).toBe(true);
expect(blob.type).toBe("text/plain");
const text = await blob.text();
expect(text).toBe("blob content");
},
},
];
for (const testCase of testCases) {
const response = new Response(testCase.body, {
headers: { "Content-Type": testCase.type },
});
await testCase.check(response);
}
});
it("should return the same body stream object across getter calls (buffered)", () => {
const response = new Response("hello world");
const b1 = response.body;
const b2 = response.body;
expect(b1 === b2).toBe(true);
});
it("should return the same body stream object across getter calls (JS body)", () => {
const buf = new Uint8Array([1, 2, 3]);
const response = new Response(buf);
const b1 = response.body;
const b2 = response.body;
expect(b1 === b2).toBe(true);
});
it("should not allow multiple body reads", async () => {
const response = new Response(JSON.stringify({ ok: true }), {
headers: { "Content-Type": "application/json" },
});
const a = await response.json();
expect(a.ok).toBe(true);
let threw = false;
try {
await response.text();
} catch (err) {
threw = true;
expect(err instanceof TypeError).toBe(true);
expect(/body used already/.test(String(err))).toBe(true);
}
if (!threw) {
throw new Error("Expected second body read to fail");
}
});
it("should parse formData from urlencoded body", async () => {
const params = new URLSearchParams();
params.append("name", "Alice");
params.append("age", "30");
const response = new Response(params.toString(), {
headers: { "Content-Type": "application/x-www-form-urlencoded" },
});
const form = await response.formData();
expect(form.get("name")).toBe("Alice");
expect(form.get("age")).toBe("30");
});
it("should parse formData from multipart body", async () => {
const boundary = "----RongBoundaryTest";
const body =
`--${boundary}\r\n` +
`Content-Disposition: form-data; name=\"field\"\r\n\r\n` +
`value\r\n` +
`--${boundary}\r\n` +
`Content-Disposition: form-data; name=\"file\"; filename=\"test.txt\"\r\n` +
`Content-Type: text/plain\r\n\r\n` +
`hello\r\n` +
`--${boundary}--\r\n`;
const response = new Response(body, {
headers: { "Content-Type": `multipart/form-data; boundary=${boundary}` },
});
const form = await response.formData();
expect(form.get("field")).toBe("value");
const file = form.get("file");
expect(file instanceof File).toBe(true);
expect(file.name).toBe("test.txt");
const text = await file.text();
expect(text).toBe("hello");
});
});
describe("clone", () => {
it("should create an identical copy of the response", async () => {
const original = new Response("test data", {
status: 200,
headers: { "Content-Type": "text/plain" },
});
const clone = original.clone();
expect(clone.status).toBe(original.status);
expect(clone.headers.get("Content-Type")).toBe(
original.headers.get("Content-Type"),
);
expect(clone).not.toBe(original);
const originalText = await original.text();
const cloneText = await clone.text();
expect(cloneText).toBe(originalText);
});
});
describe("error handling", () => {
it("should create error responses", () => {
const response = Response.error();
expect(response.status).toBe(0);
expect(response.statusText).toBe("");
expect(response.ok).toBe(false);
});
it("should create redirect responses", () => {
const url = "https://example.com/new-location";
const response = Response.redirect(url, 301);
expect(response.status).toBe(301);
expect(response.headers.get("Location")).toBe(url);
});
it("should throw TypeError for invalid redirect status", () => {
expect(() => Response.redirect("https://example.com", 200)).toThrow(
TypeError,
);
});
});
});