Files
bun.sh/test/js/web/fetch/headers.test.ts
Jarred Sumner 3a0735e164 Pretty formatter for Headers & URLSearchParams (#3081)
* Pretty formatter for `Headers` & `URLSearchParams`

* cleanup

* console.log on Headers, FormData, URLSearchParams will always quote the keys now

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-05-26 16:28:09 -07:00

495 lines
16 KiB
TypeScript

import { beforeAll, describe, test, expect } from "bun:test";
beforeAll(() => {
// expect(Headers).toBeDefined();
});
describe("Headers", () => {
describe("constructor", () => {
test("can create headers from no arguments", () => {
expect(() => new Headers()).not.toThrow();
expect(() => new Headers(undefined)).not.toThrow();
});
test("cannot create headers from null", () => {
// @ts-expect-error
expect(() => new Headers(null)).toThrow(TypeError);
});
test("can create headers from empty object", () => {
expect(() => new Headers({})).not.toThrow();
});
test("can create headers from object", () => {
const headers = new Headers({
"content-type": "text/plain",
});
expect(headers.get("content-type")).toBe("text/plain");
});
test("can create headers from object with duplicates", () => {
const headers = new Headers({
"accept": "*/*",
"Accept": "text/html",
});
expect(headers.get("accept")).toBe("*/*, text/html");
});
test("can create headers from object with non-strings", () => {
// @ts-expect-error
const headers = new Headers({
"age": 60,
});
expect(headers.get("age")).toBe("60");
});
test("can create headers from empty array", () => {
expect(() => new Headers([])).not.toThrow();
});
test("can create headers from array", () => {
const headers = new Headers([["cache-control", "no-cache"]]);
expect(headers.get("cache-control")).toBe("no-cache");
});
test("can create headers from array with duplicates", () => {
const headers = new Headers([
["accept", "*/*"],
["accept", "text/html"],
["Accept", "text/plain"],
]);
expect(headers.get("accept")).toBe("*/*, text/html, text/plain");
});
test("can create headers from array with non-strings", () => {
const headers = new Headers([
// @ts-expect-error
["age", 60],
]);
expect(headers.get("age")).toBe("60");
});
test("cannot create headers from array with non-entry", () => {
// @ts-expect-error
expect(() => new Headers(["notanentry"])).toThrow(TypeError);
});
test("cannot create headers from array with entry of length 1", () => {
// @ts-expect-error
expect(() => new Headers([["age"]])).toThrow(TypeError);
});
test("cannot create headers from array with entry of length 3", () => {
// @ts-expect-error
expect(() => new Headers([["age", "60", "extra"]])).toThrow(TypeError);
});
test("can create headers from empty headers", () => {
expect(() => new Headers(new Headers())).not.toThrow();
});
test("can create headers from headers", () => {
const headers = new Headers(
new Headers({
"user-agent": "bun",
}),
);
expect(headers.get("user-agent")).toBe("bun");
});
test("can create headers from headers with duplicates", () => {
const headers = new Headers(
new Headers([
["accept", "text/plain"],
["accept", "text/html"],
["accept", "*/*"],
]),
);
expect(headers.get("accept")).toBe("text/plain, text/html, */*");
});
test("can create headers from headers with copying", () => {
const headers = new Headers({
"user-agent": "bun",
});
const copy = new Headers(headers);
headers.delete("user-agent");
expect(copy.get("user-agent")).toBe("bun");
});
test("can create headers from empty iterator", () => {
expect(() => new Headers((function* () {})())).not.toThrow();
});
test("can create headers from iterator", () => {
const headers = new Headers(
(function* () {
yield ["server", "bun"];
yield ["content-type", "application/json"];
})(),
);
expect(headers.get("server")).toBe("bun");
expect(headers.get("content-type")).toBe("application/json");
});
test("cannot create headers from iterator that throws", () => {
const error = new Error("Iterator failed.");
expect(
() =>
new Headers(
(function* () {
throw error;
})(),
),
).toThrow(error);
});
});
describe("append()", () => {
test("can append header", () => {
const headers = new Headers();
headers.append("accept", "*/*");
expect(headers.get("accept")).toBe("*/*");
});
test("can append header with duplicate name", () => {
const headers = new Headers();
headers.append("accept", "*/*");
expect(headers.get("accept")).toBe("*/*");
headers.append("Accept", "text/html");
expect(headers.get("accept")).toBe("*/*, text/html");
});
test("cannot append header with no argument", () => {
const headers = new Headers();
// @ts-expect-error
expect(() => headers.append()).toThrow(TypeError);
});
test("cannot append header with 1 argument", () => {
const headers = new Headers();
// @ts-expect-error
expect(() => headers.append("expires")).toThrow(TypeError);
});
});
describe("set()", () => {
test("can set header", () => {
const headers = new Headers();
headers.set("cache-control", "public");
expect(headers.get("cache-control")).toBe("public");
});
test("can set header with duplicate name", () => {
const headers = new Headers();
for (const value of ["public", "no-transform", "private"]) {
headers.set("cache-control", value);
expect(headers.get("cache-control")).toBe(value);
}
});
test("can set header with non-string value", () => {
const headers = new Headers();
const values = [
[60, "60"],
[60n, "60"],
[true, "true"],
[null, "null"],
[{}, "[object Object]"],
[[], ""],
];
for (const [actual, expected] of values) {
// @ts-expect-error
headers.set("header", actual);
expect(headers.get("header")).toBe(expected);
}
});
test("cannot set header with non-iso-8859-1", () => {
const headers = new Headers();
expect(() => headers.set("emoji", "😃")).toThrow(TypeError);
expect(() => headers.set("🚀", "emoji")).toThrow(TypeError);
});
test("cannot set header with no arguments", () => {
const headers = new Headers();
// @ts-expect-error
expect(() => headers.set()).toThrow(TypeError);
});
test("cannot set header with 1 argument", () => {
const headers = new Headers();
// @ts-expect-error
expect(() => headers.set("user-agent")).toThrow(TypeError);
});
});
describe("delete()", () => {
test("can delete header", () => {
const headers = new Headers({
"user-agent": "bun",
});
headers.delete("user-agent");
expect(headers.get("user-agent")).toBeNull();
});
test("can delete header with non-existent name", () => {
const headers = new Headers();
headers.delete("age");
expect(headers.get("age")).toBeNull();
});
test("can delete header with duplicate name", () => {
const headers = new Headers({
"cache-control": "public",
"Cache-Control": "no-transform",
});
headers.delete("cache-control");
expect(headers.get("cache-control")).toBeNull();
});
test("cannot delete header with no arguments", () => {
const headers = new Headers();
// @ts-expect-error
expect(() => headers.delete()).toThrow(TypeError);
});
});
describe("get()", () => {
test("can get header", () => {
const headers = new Headers({
"user-agent": "bun",
});
expect(headers.get("user-agent")).toBe("bun");
expect(headers.get("User-Agent")).toBe("bun");
expect(headers.get("USER-AGENT")).toBe("bun");
expect(headers.get("user-agen")).toBeNull();
});
const cookies = new Headers([
["Set-Cookie", "__Secure-ID=123; Secure; Domain=example.com"],
["set-cookie", "__Host-ID=123; Secure; Path=/"],
]);
test("can get header with set-cookie", () => {
expect(cookies.get("set-cookie")).toBe(
"__Secure-ID=123; Secure; Domain=example.com, __Host-ID=123; Secure; Path=/",
);
});
const it0 = "getAll" in cookies ? test : test.skip;
it0("can get header with set-cookie using getAll()", () => {
expect(cookies.getAll("Set-Cookie")).toEqual([
"__Secure-ID=123; Secure; Domain=example.com",
"__Host-ID=123; Secure; Path=/",
]);
});
it0("cannot get header with non-set-cookie using getAll()", () => {
// @ts-expect-error
expect(() => cookies.getAll("not-set-cookie")).toThrow(TypeError);
});
const it1 = "getSetCookie" in cookies ? test : test.skip;
it1("can get header with set-cookie using getSetCookie()", () => {
// @ts-expect-error
expect(cookies.getSetCookie()).toEqual([
"__Secure-ID=123; Secure; Domain=example.com",
"__Host-ID=123; Secure; Path=/",
]);
});
test("cannot get header with no arguments", () => {
const headers = new Headers();
// @ts-expect-error
expect(() => headers.get()).toThrow(TypeError);
});
});
describe("has()", () => {
test("can check header", () => {
const headers = new Headers([
["expires", "0"],
["etag", ""],
]);
expect(headers.has("expires")).toBe(true);
expect(headers.has("Expires")).toBe(true);
expect(headers.has("etag")).toBe(true);
expect(headers.has("content-type")).toBe(false);
});
test("cannot check header with no arguments", () => {
const headers = new Headers();
// @ts-expect-error
expect(() => headers.has()).toThrow(TypeError);
});
});
describe("entries()", () => {
test("can get header entries when empty", () => {
const headers = new Headers();
const entries = Array.from(headers.entries());
expect(entries).toHaveLength(0);
});
test("can get header entries", () => {
const headers = new Headers({
"user-agent": "bun",
});
const entries = Array.from(headers.entries());
expect(entries).toEqual([["user-agent", "bun"]]);
});
test("can get header entries when sorted and normalized", () => {
const headers = new Headers([
["Expires", "120"],
["cache-control", "public"],
["Cache-Control", "no-transform"],
["ETag", "\\w0"],
]);
const entries = Array.from(headers.entries());
expect(entries).toEqual([
["cache-control", "public, no-transform"],
["etag", "\\w0"],
["expires", "120"],
]);
});
});
describe("keys()", () => {
test("can get header keys when empty", () => {
const headers = new Headers();
const keys = Array.from(headers.keys());
expect(keys).toHaveLength(0);
});
test("can get header keys", () => {
const headers = new Headers({
"user-agent": "bun",
"User-Agent": "bun",
});
const keys = Array.from(headers.keys());
expect(keys).toEqual(["user-agent"]);
});
test("can get header keys when sorted and normalized", () => {
const headers = new Headers({
"user-agent": "bun",
"User-Agent": "bun",
"Age": "60",
});
const keys = Array.from(headers.keys());
expect(keys).toEqual(["age", "user-agent"]);
});
});
describe("values()", () => {
test("can get header values when empty", () => {
const headers = new Headers();
const values = Array.from(headers.values());
expect(values).toHaveLength(0);
});
test("can get header values", () => {
const headers = new Headers({
"cache-control": "immutable",
});
const values = Array.from(headers.values());
expect(values).toEqual(["immutable"]);
});
test("can get header values when sorted and normalized", () => {
const headers = new Headers([
["Content-Length", "0"],
["Cache-Control", "immutable"],
["cache-control", "private"],
]);
const values = Array.from(headers.values());
expect(values).toEqual(["immutable, private", "0"]);
});
});
describe("forEach()", () => {
test("can iterate over header entries when empty", () => {
const headers = new Headers();
const results: [string, string][] = [];
headers.forEach((value, key, parent) => {
results.push([key, value]);
expect(parent).toBe(headers);
});
expect(results).toHaveLength(0);
});
test("can iterate over header entries when sorted and normalized", () => {
const entries: [string, string][] = [
["user-agent", "bun"],
["Cache-Control", "private"],
["Expires", "0"],
];
const headers = new Headers(entries);
const results: [string, string][] = [];
headers.forEach((value, key, parent) => {
results.push([key, value]);
expect(parent).toBe(headers);
});
expect(results).toEqual([
["cache-control", "private"],
["expires", "0"],
["user-agent", "bun"],
]);
});
});
describe("[Symbol.iterator]", () => {
test("can iterate over header object when empty", () => {
const headers = new Headers();
const entries = Array.from(headers);
expect(entries).toHaveLength(0);
});
test("can iterate over header object", () => {
const headers = new Headers({
"user-agent": "bun",
});
const entries = Array.from(headers);
expect(entries).toEqual([["user-agent", "bun"]]);
});
test("can iterate over header object when sorted and normalized", () => {
const headers = new Headers([
["User-Agent", "bun"],
["Cache-Control", "max-age=60"],
["cache-control", "s-maxage=60"],
]);
const entries = Array.from(headers);
expect(entries).toEqual([
["cache-control", "max-age=60, s-maxage=60"],
["user-agent", "bun"],
]);
});
});
describe("Bun.inspect()", () => {
const it = "toJSON" in new Headers() ? test : test.skip;
it("can convert to json when empty", () => {
const headers = new Headers();
expect(Bun.inspect(headers)).toStrictEqual(`Headers {}`);
});
it("can convert to json", () => {
const headers = new Headers({
"cache-control": "public, immutable",
});
expect(Bun.inspect(headers)).toStrictEqual(
"Headers {" + "\n " + `"cache-control": "public, immutable"` + "\n" + "}",
);
});
it("can convert to json normalized", () => {
const headers = new Headers({
"user-agent": "bun",
"X-Custom-Header": "1",
"cache-control": "public, immutable",
});
expect(Bun.inspect(headers)).toStrictEqual(
"Headers " +
JSON.stringify(
{
"user-agent": "bun",
"cache-control": "public, immutable",
"x-custom-header": "1",
},
null,
2,
),
);
});
});
describe("toJSON()", () => {
// @ts-ignore
const it = new Headers()?.toJSON ? test : test.skip;
it("can convert to json when empty", () => {
const headers = new Headers();
expect(headers.toJSON()).toStrictEqual({});
});
it("can convert to json", () => {
const headers = new Headers({
"cache-control": "public, immutable",
});
expect(headers.toJSON()).toStrictEqual({
"cache-control": "public, immutable",
});
});
it("can convert to json when sorted and normalized", () => {
const headers = new Headers({
"user-agent": "bun",
"X-Custom-Header": "1",
"cache-control": "public, immutable",
});
expect(headers.toJSON()).toStrictEqual({
"cache-control": "public, immutable",
"user-agent": "bun",
"x-custom-header": "1",
});
});
});
describe("count", () => {
// @ts-ignore
const it = typeof new Headers()?.count !== "undefined" ? test : test.skip;
it("can count headers when empty", () => {
const headers = new Headers();
expect(headers.count).toBe(0);
});
it("can count headers", () => {
const headers = new Headers([
["user-agent", "bun"],
["cache-control", "public, immutable"],
["Cache-Control", "no-transform"],
]);
expect(headers.count).toBe(2);
});
});
});