aboutsummaryrefslogtreecommitdiff
path: root/test/bun.js/fetch.test.ts
diff options
context:
space:
mode:
Diffstat (limited to 'test/bun.js/fetch.test.ts')
-rw-r--r--test/bun.js/fetch.test.ts935
1 files changed, 0 insertions, 935 deletions
diff --git a/test/bun.js/fetch.test.ts b/test/bun.js/fetch.test.ts
deleted file mode 100644
index f5f264dd7..000000000
--- a/test/bun.js/fetch.test.ts
+++ /dev/null
@@ -1,935 +0,0 @@
-import { serve, sleep } from "bun";
-import { afterAll, afterEach, beforeAll, describe, expect, it, beforeEach } from "bun:test";
-import { chmodSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from "fs";
-import { mkfifo } from "mkfifo";
-import { tmpdir } from "os";
-import { join } from "path";
-import { gc, withoutAggressiveGC } from "./gc";
-
-const tmp_dir = mkdtempSync(join(realpathSync(tmpdir()), "fetch.test"));
-
-const fixture = readFileSync(join(import.meta.dir, "fetch.js.txt"), "utf8");
-
-let server;
-function startServer({ fetch, ...options }) {
- server = serve({
- ...options,
- fetch,
- port: 0,
- });
-}
-
-afterEach(() => {
- server?.stop?.(true);
-});
-
-afterAll(() => {
- rmSync(tmp_dir, { force: true, recursive: true });
-});
-
-const payload = new Uint8Array(1024 * 1024 * 2);
-crypto.getRandomValues(payload);
-
-describe("AbortSignal", () => {
- beforeEach(() => {
- startServer({
- async fetch(request) {
- if (request.url.endsWith("/nodelay")) {
- return new Response("Hello");
- }
- if (request.url.endsWith("/stream")) {
- const reader = request.body.getReader();
- const body = new ReadableStream({
- async pull(controller) {
- if (!reader) controller.close();
- const { done, value } = await reader.read();
- // When no more data needs to be consumed, close the stream
- if (done) {
- controller.close();
- return;
- }
- // Enqueue the next data chunk into our target stream
- controller.enqueue(value);
- },
- });
- return new Response(body);
- }
- if (request.method.toUpperCase() === "POST") {
- const body = await request.text();
- return new Response(body);
- }
- await sleep(15);
- return new Response("Hello");
- },
- });
- });
- afterEach(() => {
- server?.stop?.(true);
- });
-
- it("AbortError", async () => {
- const controller = new AbortController();
- const signal = controller.signal;
-
- expect(async () => {
- async function manualAbort() {
- await sleep(1);
- controller.abort();
- }
- await Promise.all([
- fetch(`http://127.0.0.1:${server.port}`, { signal: signal }).then(res => res.text()),
- manualAbort(),
- ]);
- }).toThrow(new DOMException("The operation was aborted."));
- });
-
- it("AbortAfterFinish", async () => {
- const controller = new AbortController();
- const signal = controller.signal;
-
- await fetch(`http://127.0.0.1:${server.port}/nodelay`, { signal: signal }).then(async res =>
- expect(await res.text()).toBe("Hello"),
- );
- controller.abort();
- });
-
- it("AbortErrorWithReason", async () => {
- const controller = new AbortController();
- const signal = controller.signal;
-
- expect(async () => {
- async function manualAbort() {
- await sleep(10);
- controller.abort(new Error("My Reason"));
- }
- await Promise.all([
- fetch(`http://127.0.0.1:${server.port}`, { signal: signal }).then(res => res.text()),
- manualAbort(),
- ]);
- }).toThrow("My Reason");
- });
-
- it("AbortErrorEventListener", async () => {
- const controller = new AbortController();
- const signal = controller.signal;
- signal.addEventListener("abort", ev => {
- const target = ev.currentTarget;
- expect(target).toBeDefined();
- expect(target.aborted).toBe(true);
- expect(target.reason).toBeDefined();
- expect(target.reason.name).toBe("AbortError");
- });
-
- expect(async () => {
- async function manualAbort() {
- await sleep(10);
- controller.abort();
- }
- await Promise.all([
- fetch(`http://127.0.0.1:${server.port}`, { signal: signal }).then(res => res.text()),
- manualAbort(),
- ]);
- }).toThrow(new DOMException("The operation was aborted."));
- });
-
- it("AbortErrorWhileUploading", async () => {
- const controller = new AbortController();
-
- expect(async () => {
- await fetch(`http://localhost:${server.port}`, {
- method: "POST",
- body: new ReadableStream({
- pull(event_controller) {
- event_controller.enqueue(new Uint8Array([1, 2, 3, 4]));
- //this will abort immediately should abort before connected
- controller.abort();
- },
- }),
- signal: controller.signal,
- });
- }).toThrow(new DOMException("The operation was aborted."));
- });
-
- it("TimeoutError", async () => {
- const signal = AbortSignal.timeout(10);
-
- try {
- await fetch(`http://127.0.0.1:${server.port}`, { signal: signal }).then(res => res.text());
- expect(() => {}).toThrow();
- } catch (ex: any) {
- expect(ex.name).toBe("TimeoutError");
- }
- });
-
- it("Request", async () => {
- const controller = new AbortController();
- const signal = controller.signal;
- async function manualAbort() {
- await sleep(10);
- controller.abort();
- }
-
- try {
- const request = new Request(`http://127.0.0.1:${server.port}`, { signal });
- await Promise.all([fetch(request).then(res => res.text()), manualAbort()]);
- expect(() => {}).toThrow();
- } catch (ex: any) {
- expect(ex.name).toBe("AbortError");
- }
- });
-});
-
-describe("Headers", () => {
- it(".toJSON", () => {
- const headers = new Headers({
- "content-length": "123",
- "content-type": "text/plain",
- "x-another-custom-header": "Hello World",
- "x-custom-header": "Hello World",
- });
- expect(JSON.stringify(headers.toJSON(), null, 2)).toBe(
- JSON.stringify(Object.fromEntries(headers.entries()), null, 2),
- );
- });
-
- it(".getSetCookie() with object", () => {
- const headers = new Headers({
- "content-length": "123",
- "content-type": "text/plain",
- "x-another-custom-header": "Hello World",
- "x-custom-header": "Hello World",
- "Set-Cookie": "foo=bar; Path=/; HttpOnly",
- });
- expect(headers.count).toBe(5);
- expect(headers.getAll("set-cookie")).toEqual(["foo=bar; Path=/; HttpOnly"]);
- });
-
- it(".getSetCookie() with array", () => {
- const headers = new Headers([
- ["content-length", "123"],
- ["content-type", "text/plain"],
- ["x-another-custom-header", "Hello World"],
- ["x-custom-header", "Hello World"],
- ["Set-Cookie", "foo=bar; Path=/; HttpOnly"],
- ["Set-Cookie", "foo2=bar2; Path=/; HttpOnly"],
- ]);
- expect(headers.count).toBe(6);
- expect(headers.getAll("set-cookie")).toEqual(["foo=bar; Path=/; HttpOnly", "foo2=bar2; Path=/; HttpOnly"]);
- });
-
- it("Set-Cookies init", () => {
- const headers = new Headers([
- ["Set-Cookie", "foo=bar"],
- ["Set-Cookie", "bar=baz"],
- ["X-bun", "abc"],
- ["X-bun", "def"],
- ]);
- const actual = [...headers];
- expect(actual).toEqual([
- ["set-cookie", "foo=bar"],
- ["set-cookie", "bar=baz"],
- ["x-bun", "abc, def"],
- ]);
- expect([...headers.values()]).toEqual(["foo=bar", "bar=baz", "abc, def"]);
- });
-
- it("Headers append multiple", () => {
- const headers = new Headers([
- ["Set-Cookie", "foo=bar"],
- ["X-bun", "foo"],
- ]);
- headers.append("Set-Cookie", "bar=baz");
- headers.append("x-bun", "bar");
- const actual = [...headers];
-
- // we do not preserve the order
- // which is kind of bad
- expect(actual).toEqual([
- ["set-cookie", "foo=bar"],
- ["set-cookie", "bar=baz"],
- ["x-bun", "foo, bar"],
- ]);
- });
-
- it("append duplicate set cookie key", () => {
- const headers = new Headers([["Set-Cookie", "foo=bar"]]);
- headers.append("set-Cookie", "foo=baz");
- headers.append("Set-cookie", "baz=bar");
- const actual = [...headers];
- expect(actual).toEqual([
- ["set-cookie", "foo=baz"],
- ["set-cookie", "baz=bar"],
- ]);
- });
-
- it("set duplicate cookie key", () => {
- const headers = new Headers([["Set-Cookie", "foo=bar"]]);
- headers.set("set-Cookie", "foo=baz");
- headers.set("set-cookie", "bar=qat");
- const actual = [...headers];
- expect(actual).toEqual([
- ["set-cookie", "foo=baz"],
- ["set-cookie", "bar=qat"],
- ]);
- });
-});
-
-describe("fetch", () => {
- const urls = [
- "https://example.com",
- "http://example.com",
- new URL("https://example.com"),
- new Request({ url: "https://example.com" }),
- { toString: () => "https://example.com" },
- ];
- for (let url of urls) {
- gc();
- let name;
- if (url instanceof URL) {
- name = "URL: " + url;
- } else if (url instanceof Request) {
- name = "Request: " + url.url;
- } else if (url.hasOwnProperty("toString")) {
- name = "Object: " + url.toString();
- } else {
- name = url;
- }
- it(name, async () => {
- gc();
- const response = await fetch(url, { verbose: true });
- gc();
- const text = await response.text();
- gc();
- expect(fixture).toBe(text);
- });
- }
-
- it('redirect: "manual"', async () => {
- startServer({
- fetch(req) {
- return new Response(null, {
- status: 302,
- headers: {
- Location: "https://example.com",
- },
- });
- },
- });
- const response = await fetch(`http://${server.hostname}:${server.port}`, {
- redirect: "manual",
- });
- expect(response.status).toBe(302);
- expect(response.headers.get("location")).toBe("https://example.com");
- expect(response.redirected).toBe(true);
- });
-
- it('redirect: "follow"', async () => {
- startServer({
- fetch(req) {
- return new Response(null, {
- status: 302,
- headers: {
- Location: "https://example.com",
- },
- });
- },
- });
- const response = await fetch(`http://${server.hostname}:${server.port}`, {
- redirect: "follow",
- });
- expect(response.status).toBe(200);
- expect(response.headers.get("location")).toBe(null);
- expect(response.redirected).toBe(true);
- });
-
- it("provide body", async () => {
- startServer({
- fetch(req) {
- return new Response(req.body);
- },
- host: "localhost",
- });
-
- // POST with body
- const url = `http://${server.hostname}:${server.port}`;
- const response = await fetch(url, { method: "POST", body: "buntastic" });
- expect(response.status).toBe(200);
- expect(await response.text()).toBe("buntastic");
- });
-
- ["GET", "HEAD", "OPTIONS"].forEach(method =>
- it(`fail on ${method} with body`, async () => {
- const url = `http://${server.hostname}:${server.port}`;
- expect(async () => {
- await fetch(url, { body: "buntastic" });
- }).toThrow("fetch() request with GET/HEAD/OPTIONS method cannot have body.");
- }),
- );
-});
-
-it("simultaneous HTTPS fetch", async () => {
- const urls = ["https://example.com", "https://www.example.com"];
- for (let batch = 0; batch < 4; batch++) {
- const promises = new Array(20);
- for (let i = 0; i < 20; i++) {
- promises[i] = fetch(urls[i % 2]);
- }
- const result = await Promise.all(promises);
- expect(result.length).toBe(20);
- for (let i = 0; i < 20; i++) {
- expect(result[i].status).toBe(200);
- expect(await result[i].text()).toBe(fixture);
- }
- }
-});
-
-it("website with tlsextname", async () => {
- // irony
- await fetch("https://bun.sh", { method: "HEAD" });
-});
-
-function testBlobInterface(blobbyConstructor, hasBlobFn?) {
- for (let withGC of [false, true]) {
- for (let jsonObject of [
- { hello: true },
- {
- hello: "πŸ˜€ πŸ˜ƒ πŸ˜„ 😁 πŸ˜† πŸ˜… πŸ˜‚ 🀣 πŸ₯² ☺️ 😊 πŸ˜‡ πŸ™‚ πŸ™ƒ πŸ˜‰ 😌 😍 πŸ₯° 😘 πŸ˜— πŸ˜™ 😚 πŸ˜‹ πŸ˜› 😝 😜 πŸ€ͺ 🀨 🧐 πŸ€“ 😎 πŸ₯Έ 🀩 πŸ₯³",
- },
- ]) {
- it(`${jsonObject.hello === true ? "latin1" : "utf16"} json${withGC ? " (with gc) " : ""}`, async () => {
- if (withGC) gc();
- var response = blobbyConstructor(JSON.stringify(jsonObject));
- if (withGC) gc();
- expect(JSON.stringify(await response.json())).toBe(JSON.stringify(jsonObject));
- if (withGC) gc();
- });
-
- it(`${jsonObject.hello === true ? "latin1" : "utf16"} arrayBuffer -> json${
- withGC ? " (with gc) " : ""
- }`, async () => {
- if (withGC) gc();
- var response = blobbyConstructor(new TextEncoder().encode(JSON.stringify(jsonObject)));
- if (withGC) gc();
- expect(JSON.stringify(await response.json())).toBe(JSON.stringify(jsonObject));
- if (withGC) gc();
- });
-
- it(`${jsonObject.hello === true ? "latin1" : "utf16"} arrayBuffer -> invalid json${
- withGC ? " (with gc) " : ""
- }`, async () => {
- if (withGC) gc();
- var response = blobbyConstructor(
- new TextEncoder().encode(JSON.stringify(jsonObject) + " NOW WE ARE INVALID JSON"),
- );
- if (withGC) gc();
- var failed = false;
- try {
- await response.json();
- } catch (e) {
- failed = true;
- }
- expect(failed).toBe(true);
- if (withGC) gc();
- });
-
- it(`${jsonObject.hello === true ? "latin1" : "utf16"} text${withGC ? " (with gc) " : ""}`, async () => {
- if (withGC) gc();
- var response = blobbyConstructor(JSON.stringify(jsonObject));
- if (withGC) gc();
- expect(await response.text()).toBe(JSON.stringify(jsonObject));
- if (withGC) gc();
- });
-
- it(`${jsonObject.hello === true ? "latin1" : "utf16"} arrayBuffer -> text${
- withGC ? " (with gc) " : ""
- }`, async () => {
- if (withGC) gc();
- var response = blobbyConstructor(new TextEncoder().encode(JSON.stringify(jsonObject)));
- if (withGC) gc();
- expect(await response.text()).toBe(JSON.stringify(jsonObject));
- if (withGC) gc();
- });
-
- it(`${jsonObject.hello === true ? "latin1" : "utf16"} arrayBuffer${withGC ? " (with gc) " : ""}`, async () => {
- if (withGC) gc();
-
- var response = blobbyConstructor(JSON.stringify(jsonObject));
- if (withGC) gc();
-
- const bytes = new TextEncoder().encode(JSON.stringify(jsonObject));
- if (withGC) gc();
-
- const compare = new Uint8Array(await response.arrayBuffer());
- if (withGC) gc();
-
- withoutAggressiveGC(() => {
- for (let i = 0; i < compare.length; i++) {
- if (withGC) gc();
-
- expect(compare[i]).toBe(bytes[i]);
- if (withGC) gc();
- }
- });
- if (withGC) gc();
- });
-
- it(`${jsonObject.hello === true ? "latin1" : "utf16"} arrayBuffer -> arrayBuffer${
- withGC ? " (with gc) " : ""
- }`, async () => {
- if (withGC) gc();
-
- var response = blobbyConstructor(new TextEncoder().encode(JSON.stringify(jsonObject)));
- if (withGC) gc();
-
- const bytes = new TextEncoder().encode(JSON.stringify(jsonObject));
- if (withGC) gc();
-
- const compare = new Uint8Array(await response.arrayBuffer());
- if (withGC) gc();
-
- withoutAggressiveGC(() => {
- for (let i = 0; i < compare.length; i++) {
- if (withGC) gc();
-
- expect(compare[i]).toBe(bytes[i]);
- if (withGC) gc();
- }
- });
- if (withGC) gc();
- });
-
- hasBlobFn &&
- it(`${jsonObject.hello === true ? "latin1" : "utf16"} blob${withGC ? " (with gc) " : ""}`, async () => {
- if (withGC) gc();
- const text = JSON.stringify(jsonObject);
- var response = blobbyConstructor(text);
- if (withGC) gc();
- const size = new TextEncoder().encode(text).byteLength;
- if (withGC) gc();
- const blobed = await response.blob();
- if (withGC) gc();
- expect(blobed instanceof Blob).toBe(true);
- if (withGC) gc();
- expect(blobed.size).toBe(size);
- if (withGC) gc();
- blobed.type = "";
- if (withGC) gc();
- expect(blobed.type).toBe("");
- if (withGC) gc();
- blobed.type = "application/json";
- if (withGC) gc();
- expect(blobed.type).toBe("application/json");
- if (withGC) gc();
- const out = await blobed.text();
- expect(out).toBe(text);
- if (withGC) gc();
- await new Promise(resolve => setTimeout(resolve, 1));
- if (withGC) gc();
- expect(out).toBe(text);
- const first = await blobed.arrayBuffer();
- const initial = first[0];
- first[0] = 254;
- const second = await blobed.arrayBuffer();
- expect(second[0]).toBe(initial);
- expect(first[0]).toBe(254);
- });
- }
- }
-}
-
-describe("Bun.file", () => {
- let count = 0;
- testBlobInterface(data => {
- const blob = new Blob([data]);
- const buffer = Bun.peek(blob.arrayBuffer());
- const path = join(tmp_dir, `tmp-${count++}.bytes`);
- writeFileSync(path, buffer);
- const file = Bun.file(path);
- expect(blob.size).toBe(file.size);
- return file;
- });
-
- it("size is Infinity on a fifo", () => {
- const path = join(tmp_dir, "test-fifo");
- mkfifo(path);
- const { size } = Bun.file(path);
- expect(size).toBe(Infinity);
- });
-
- function forEachMethod(fn, skip?) {
- const method = ["arrayBuffer", "text", "json"];
- for (const m of method) {
- (skip ? it.skip : it)(m, fn(m));
- }
- }
-
- describe("bad permissions throws", () => {
- const path = join(tmp_dir, "my-new-file");
- beforeAll(async () => {
- await Bun.write(path, "hey");
- chmodSync(path, 0o000);
- });
-
- forEachMethod(
- m => () => {
- const file = Bun.file(path);
- expect(async () => await file[m]()).toThrow("Permission denied");
- },
- () => {
- try {
- readFileSync(path);
- } catch {
- return false;
- }
- return true;
- },
- );
- });
-
- describe("non-existent file throws", () => {
- const path = join(tmp_dir, "does-not-exist");
-
- forEachMethod(m => async () => {
- const file = Bun.file(path);
- expect(async () => await file[m]()).toThrow("No such file or directory");
- });
- });
-});
-
-describe("Blob", () => {
- testBlobInterface(data => new Blob([data]));
-
- var blobConstructorValues = [
- ["123", "456"],
- ["123", 456],
- ["123", "456", "789"],
- ["123", 456, 789],
- [1, 2, 3, 4, 5, 6, 7, 8, 9],
- [Uint8Array.from([1, 2, 3, 4, 5, 6, 7, 9])],
- [Uint8Array.from([1, 2, 3, 4]), "5678", 9],
- [new Blob([Uint8Array.from([1, 2, 3, 4])]), "5678", 9],
- [
- new Blob([
- new TextEncoder().encode(
- "πŸ˜€ πŸ˜ƒ πŸ˜„ 😁 πŸ˜† πŸ˜… πŸ˜‚ 🀣 πŸ₯² ☺️ 😊 πŸ˜‡ πŸ™‚ πŸ™ƒ πŸ˜‰ 😌 😍 πŸ₯° 😘 πŸ˜— πŸ˜™ 😚 πŸ˜‹ πŸ˜› 😝 😜 πŸ€ͺ 🀨 🧐 πŸ€“ 😎 πŸ₯Έ 🀩 πŸ₯³",
- ),
- ]),
- ],
- [
- new TextEncoder().encode(
- "πŸ˜€ πŸ˜ƒ πŸ˜„ 😁 πŸ˜† πŸ˜… πŸ˜‚ 🀣 πŸ₯² ☺️ 😊 πŸ˜‡ πŸ™‚ πŸ™ƒ πŸ˜‰ 😌 😍 πŸ₯° 😘 πŸ˜— πŸ˜™ 😚 πŸ˜‹ πŸ˜› 😝 😜 πŸ€ͺ 🀨 🧐 πŸ€“ 😎 πŸ₯Έ 🀩 πŸ₯³",
- ),
- ],
- ];
-
- var expected = [
- "123456",
- "123456",
- "123456789",
- "123456789",
- "123456789",
- "\x01\x02\x03\x04\x05\x06\x07\t",
- "\x01\x02\x03\x0456789",
- "\x01\x02\x03\x0456789",
- "πŸ˜€ πŸ˜ƒ πŸ˜„ 😁 πŸ˜† πŸ˜… πŸ˜‚ 🀣 πŸ₯² ☺️ 😊 πŸ˜‡ πŸ™‚ πŸ™ƒ πŸ˜‰ 😌 😍 πŸ₯° 😘 πŸ˜— πŸ˜™ 😚 πŸ˜‹ πŸ˜› 😝 😜 πŸ€ͺ 🀨 🧐 πŸ€“ 😎 πŸ₯Έ 🀩 πŸ₯³",
- "πŸ˜€ πŸ˜ƒ πŸ˜„ 😁 πŸ˜† πŸ˜… πŸ˜‚ 🀣 πŸ₯² ☺️ 😊 πŸ˜‡ πŸ™‚ πŸ™ƒ πŸ˜‰ 😌 😍 πŸ₯° 😘 πŸ˜— πŸ˜™ 😚 πŸ˜‹ πŸ˜› 😝 😜 πŸ€ͺ 🀨 🧐 πŸ€“ 😎 πŸ₯Έ 🀩 πŸ₯³",
- ];
-
- it(`blobConstructorValues`, async () => {
- for (let i = 0; i < blobConstructorValues.length; i++) {
- var response = new Blob(blobConstructorValues[i]);
- const res = await response.text();
- if (res !== expected[i]) {
- throw new Error(
- `Failed: ${expected[i].split("").map(a => a.charCodeAt(0))}, received: ${res
- .split("")
- .map(a => a.charCodeAt(0))}`,
- );
- }
-
- expect(res).toBe(expected[i]);
- }
- });
-
- for (let withGC of [false, true]) {
- it(`Blob.slice() ${withGC ? " with gc" : ""}`, async () => {
- var parts = ["hello", " ", "world"];
- if (withGC) gc();
- var str = parts.join("");
- if (withGC) gc();
- var combined = new Blob(parts);
- if (withGC) gc();
- for (let part of parts) {
- if (withGC) gc();
- expect(await combined.slice(str.indexOf(part), str.indexOf(part) + part.length).text()).toBe(part);
- if (withGC) gc();
- }
- if (withGC) gc();
- for (let part of parts) {
- if (withGC) gc();
- expect(await combined.slice(str.indexOf(part), str.indexOf(part) + part.length).text()).toBe(part);
- if (withGC) gc();
- }
- });
- }
-});
-
-{
- const sample = new TextEncoder().encode("Hello World!");
- const typedArrays = [
- Uint8Array,
- Uint8ClampedArray,
- Int8Array,
- Uint16Array,
- Int16Array,
- Uint32Array,
- Int32Array,
- Float32Array,
- Float64Array,
- ];
- const Constructors = [Blob, Response, Request];
-
- for (let withGC of [false, true]) {
- for (let TypedArray of typedArrays) {
- for (let Constructor of Constructors) {
- it(`${Constructor.name} arrayBuffer() with ${TypedArray.name}${withGC ? " with gc" : ""}`, async () => {
- const data = new TypedArray(sample);
- if (withGC) gc();
- const input = Constructor === Blob ? [data] : Constructor === Request ? { body: data } : data;
- if (withGC) gc();
- const blob = new Constructor(input);
- if (withGC) gc();
- const out = await blob.arrayBuffer();
- if (withGC) gc();
- expect(out instanceof ArrayBuffer).toBe(true);
- if (withGC) gc();
- expect(out.byteLength).toBe(data.byteLength);
- if (withGC) gc();
- });
- }
- }
- }
-}
-
-describe("Response", () => {
- describe("Response.json", () => {
- it("works", async () => {
- const inputs = ["hellooo", [[123], 456, 789], { hello: "world" }, { ok: "πŸ˜‰ 😌 😍 πŸ₯° 😘 " }];
- for (let input of inputs) {
- const output = JSON.stringify(input);
- expect(await Response.json(input).text()).toBe(output);
- }
- // JSON.stringify() returns undefined
- expect(await Response.json().text()).toBe("");
- // JSON.stringify("") returns '""'
- expect(await Response.json("").text()).toBe('""');
- });
- it("sets the content-type header", () => {
- let response = Response.json("hello");
- expect(response.type).toBe("basic");
- expect(response.headers.get("content-type")).toBe("application/json;charset=utf-8");
- expect(response.status).toBe(200);
- });
- it("supports number status code", () => {
- let response = Response.json("hello", 407);
- expect(response.type).toBe("basic");
- expect(response.headers.get("content-type")).toBe("application/json;charset=utf-8");
- expect(response.status).toBe(407);
- });
-
- it("supports headers", () => {
- var response = Response.json("hello", {
- headers: {
- "content-type": "potato",
- "x-hello": "world",
- },
- status: 408,
- });
-
- expect(response.headers.get("x-hello")).toBe("world");
- expect(response.status).toBe(408);
- });
- });
- describe("Response.redirect", () => {
- it("works", () => {
- const inputs = [
- "http://example.com",
- "http://example.com/",
- "http://example.com/hello",
- "http://example.com/hello/",
- "http://example.com/hello/world",
- "http://example.com/hello/world/",
- ];
- for (let input of inputs) {
- expect(Response.redirect(input).headers.get("Location")).toBe(input);
- }
- });
-
- it("supports headers", () => {
- var response = Response.redirect("https://example.com", {
- headers: {
- "content-type": "potato",
- "x-hello": "world",
- Location: "https://wrong.com",
- },
- status: 408,
- });
- expect(response.headers.get("x-hello")).toBe("world");
- expect(response.headers.get("Location")).toBe("https://example.com");
- expect(response.status).toBe(302);
- expect(response.type).toBe("basic");
- expect(response.ok).toBe(false);
- });
- });
- describe("Response.error", () => {
- it("works", () => {
- expect(Response.error().type).toBe("error");
- expect(Response.error().ok).toBe(false);
- expect(Response.error().status).toBe(0);
- });
- });
- it("clone", async () => {
- gc();
- var body = new Response("<div>hello</div>", {
- headers: {
- "content-type": "text/html; charset=utf-8",
- },
- });
- gc();
- var clone = body.clone();
- gc();
- body.headers.set("content-type", "text/plain");
- gc();
- expect(clone.headers.get("content-type")).toBe("text/html; charset=utf-8");
- gc();
- expect(body.headers.get("content-type")).toBe("text/plain");
- gc();
- expect(await clone.text()).toBe("<div>hello</div>");
- gc();
- });
- it("invalid json", async () => {
- gc();
- var body = new Response("<div>hello</div>", {
- headers: {
- "content-type": "text/html; charset=utf-8",
- },
- });
- try {
- await body.json();
- expect(false).toBe(true);
- } catch (exception) {
- expect(exception instanceof SyntaxError).toBe(true);
- }
- });
-
- testBlobInterface(data => new Response(data), true);
-});
-
-describe("Request", () => {
- it("clone", async () => {
- gc();
- var body = new Request("https://hello.com", {
- headers: {
- "content-type": "text/html; charset=utf-8",
- },
- body: "<div>hello</div>",
- });
- gc();
- expect(body.signal).toBeDefined();
- gc();
- expect(body.headers.get("content-type")).toBe("text/html; charset=utf-8");
- gc();
- var clone = body.clone();
- gc();
- expect(clone.signal).toBeDefined();
- gc();
- body.headers.set("content-type", "text/plain");
- gc();
- expect(clone.headers.get("content-type")).toBe("text/html; charset=utf-8");
- gc();
- expect(body.headers.get("content-type")).toBe("text/plain");
- gc();
- expect(await clone.text()).toBe("<div>hello</div>");
- });
-
- it("signal", async () => {
- gc();
- const controller = new AbortController();
- const req = new Request("https://hello.com", { signal: controller.signal });
- expect(req.signal.aborted).toBe(false);
- gc();
- controller.abort();
- gc();
- expect(req.signal.aborted).toBe(true);
- });
-
- it("cloned signal", async () => {
- gc();
- const controller = new AbortController();
- const req = new Request("https://hello.com", { signal: controller.signal });
- expect(req.signal.aborted).toBe(false);
- gc();
- controller.abort();
- gc();
- expect(req.signal.aborted).toBe(true);
- gc();
- const cloned = req.clone();
- expect(cloned.signal.aborted).toBe(true);
- });
-
- testBlobInterface(data => new Request("https://hello.com", { body: data }), true);
-});
-
-describe("Headers", () => {
- it("writes", async () => {
- var headers = new Headers({
- "content-type": "text/html; charset=utf-8",
- });
- gc();
- expect(headers.get("content-type")).toBe("text/html; charset=utf-8");
- gc();
- headers.delete("content-type");
- gc();
- expect(headers.get("content-type")).toBe(null);
- gc();
- headers.append("content-type", "text/plain");
- gc();
- expect(headers.get("content-type")).toBe("text/plain");
- gc();
- headers.append("content-type", "text/plain");
- gc();
- expect(headers.get("content-type")).toBe("text/plain, text/plain");
- gc();
- headers.set("content-type", "text/html; charset=utf-8");
- gc();
- expect(headers.get("content-type")).toBe("text/html; charset=utf-8");
-
- headers.delete("content-type");
- gc();
- expect(headers.get("content-type")).toBe(null);
- gc();
- });
-});
-
-it("body nullable", async () => {
- gc();
- {
- const req = new Request("https://hello.com", { body: null });
- expect(req.body).toBeNull();
- }
- gc();
- {
- const req = new Request("https://hello.com", { body: undefined });
- expect(req.body).toBeNull();
- }
- gc();
- {
- const req = new Request("https://hello.com");
- expect(req.body).toBeNull();
- }
- gc();
- {
- const req = new Request("https://hello.com", { body: "" });
- expect(req.body).not.toBeNull();
- }
-});