// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. import { unitTest, assert, assertEquals, assertStringContains, } from "./test_util.ts"; const { stringifyArgs, // @ts-expect-error TypeScript (as of 3.7) does not support indexing namespaces by symbol } = Deno[Deno.internal]; unitTest(function headersHasCorrectNameProp(): void { assertEquals(Headers.name, "Headers"); }); // Logic heavily copied from web-platform-tests, make // sure pass mostly header basic test // ref: https://github.com/web-platform-tests/wpt/blob/7c50c216081d6ea3c9afe553ee7b64534020a1b2/fetch/api/headers/headers-basic.html unitTest(function newHeaderTest(): void { new Headers(); new Headers(undefined); new Headers({}); try { // eslint-disable-next-line @typescript-eslint/no-explicit-any new Headers(null as any); } catch (e) { assertEquals( e.message, "Failed to construct 'Headers'; The provided value was not valid" ); } }); const headerDict: Record = { name1: "value1", name2: "value2", name3: "value3", // eslint-disable-next-line @typescript-eslint/no-explicit-any name4: undefined as any, "Content-Type": "value4", }; // eslint-disable-next-line @typescript-eslint/no-explicit-any const headerSeq: any[] = []; for (const name in headerDict) { headerSeq.push([name, headerDict[name]]); } unitTest(function newHeaderWithSequence(): void { const headers = new Headers(headerSeq); for (const name in headerDict) { assertEquals(headers.get(name), String(headerDict[name])); } assertEquals(headers.get("length"), null); }); unitTest(function newHeaderWithRecord(): void { const headers = new Headers(headerDict); for (const name in headerDict) { assertEquals(headers.get(name), String(headerDict[name])); } }); unitTest(function newHeaderWithHeadersInstance(): void { const headers = new Headers(headerDict); const headers2 = new Headers(headers); for (const name in headerDict) { assertEquals(headers2.get(name), String(headerDict[name])); } }); unitTest(function headerAppendSuccess(): void { const headers = new Headers(); for (const name in headerDict) { headers.append(name, headerDict[name]); assertEquals(headers.get(name), String(headerDict[name])); } }); unitTest(function headerSetSuccess(): void { const headers = new Headers(); for (const name in headerDict) { headers.set(name, headerDict[name]); assertEquals(headers.get(name), String(headerDict[name])); } }); unitTest(function headerHasSuccess(): void { const headers = new Headers(headerDict); for (const name in headerDict) { assert(headers.has(name), "headers has name " + name); assert( !headers.has("nameNotInHeaders"), "headers do not have header: nameNotInHeaders" ); } }); unitTest(function headerDeleteSuccess(): void { const headers = new Headers(headerDict); for (const name in headerDict) { assert(headers.has(name), "headers have a header: " + name); headers.delete(name); assert(!headers.has(name), "headers do not have anymore a header: " + name); } }); unitTest(function headerGetSuccess(): void { const headers = new Headers(headerDict); for (const name in headerDict) { assertEquals(headers.get(name), String(headerDict[name])); assertEquals(headers.get("nameNotInHeaders"), null); } }); unitTest(function headerEntriesSuccess(): void { const headers = new Headers(headerDict); const iterators = headers.entries(); for (const it of iterators) { const key = it[0]; const value = it[1]; assert(headers.has(key)); assertEquals(value, headers.get(key)); } }); unitTest(function headerKeysSuccess(): void { const headers = new Headers(headerDict); const iterators = headers.keys(); for (const it of iterators) { assert(headers.has(it)); } }); unitTest(function headerValuesSuccess(): void { const headers = new Headers(headerDict); const iterators = headers.values(); const entries = headers.entries(); const values = []; for (const pair of entries) { values.push(pair[1]); } for (const it of iterators) { assert(values.includes(it)); } }); const headerEntriesDict: Record = { name1: "value1", Name2: "value2", name: "value3", "content-Type": "value4", "Content-Typ": "value5", "Content-Types": "value6", }; unitTest(function headerForEachSuccess(): void { const headers = new Headers(headerEntriesDict); const keys = Object.keys(headerEntriesDict); keys.forEach((key): void => { const value = headerEntriesDict[key]; const newkey = key.toLowerCase(); headerEntriesDict[newkey] = value; }); let callNum = 0; headers.forEach((value, key, container): void => { assertEquals(headers, container); assertEquals(value, headerEntriesDict[key]); callNum++; }); assertEquals(callNum, keys.length); }); unitTest(function headerSymbolIteratorSuccess(): void { assert(Symbol.iterator in Headers.prototype); const headers = new Headers(headerEntriesDict); for (const header of headers) { const key = header[0]; const value = header[1]; assert(headers.has(key)); assertEquals(value, headers.get(key)); } }); unitTest(function headerTypesAvailable(): void { function newHeaders(): Headers { return new Headers(); } const headers = newHeaders(); assert(headers instanceof Headers); }); // Modified from https://github.com/bitinn/node-fetch/blob/7d3293200a91ad52b5ca7962f9d6fd1c04983edb/test/test.js#L2001-L2014 // Copyright (c) 2016 David Frank. MIT License. unitTest(function headerIllegalReject(): void { let errorCount = 0; try { new Headers({ "He y": "ok" }); } catch (e) { errorCount++; } try { new Headers({ "Hé-y": "ok" }); } catch (e) { errorCount++; } try { new Headers({ "He-y": "ăk" }); } catch (e) { errorCount++; } const headers = new Headers(); try { headers.append("Hé-y", "ok"); } catch (e) { errorCount++; } try { headers.delete("Hé-y"); } catch (e) { errorCount++; } try { headers.get("Hé-y"); } catch (e) { errorCount++; } try { headers.has("Hé-y"); } catch (e) { errorCount++; } try { headers.set("Hé-y", "ok"); } catch (e) { errorCount++; } try { headers.set("", "ok"); } catch (e) { errorCount++; } assertEquals(errorCount, 9); // 'o k' is valid value but invalid name new Headers({ "He-y": "o k" }); }); // If pair does not contain exactly two items,then throw a TypeError. unitTest(function headerParamsShouldThrowTypeError(): void { let hasThrown = 0; try { new Headers(([["1"]] as unknown) as Array<[string, string]>); hasThrown = 1; } catch (err) { if (err instanceof TypeError) { hasThrown = 2; } else { hasThrown = 3; } } assertEquals(hasThrown, 2); }); unitTest(function headerParamsArgumentsCheck(): void { const methodRequireOneParam = ["delete", "get", "has", "forEach"] as const; const methodRequireTwoParams = ["append", "set"] as const; methodRequireOneParam.forEach((method): void => { const headers = new Headers(); let hasThrown = 0; let errMsg = ""; try { // eslint-disable-next-line @typescript-eslint/no-explicit-any (headers as any)[method](); hasThrown = 1; } catch (err) { errMsg = err.message; if (err instanceof TypeError) { hasThrown = 2; } else { hasThrown = 3; } } assertEquals(hasThrown, 2); assertStringContains( errMsg, `${method} requires at least 1 argument, but only 0 present` ); }); methodRequireTwoParams.forEach((method): void => { const headers = new Headers(); let hasThrown = 0; let errMsg = ""; try { // eslint-disable-next-line @typescript-eslint/no-explicit-any (headers as any)[method](); hasThrown = 1; } catch (err) { errMsg = err.message; if (err instanceof TypeError) { hasThrown = 2; } else { hasThrown = 3; } } assertEquals(hasThrown, 2); assertStringContains( errMsg, `${method} requires at least 2 arguments, but only 0 present` ); hasThrown = 0; errMsg = ""; try { // eslint-disable-next-line @typescript-eslint/no-explicit-any (headers as any)[method]("foo"); hasThrown = 1; } catch (err) { errMsg = err.message; if (err instanceof TypeError) { hasThrown = 2; } else { hasThrown = 3; } } assertEquals(hasThrown, 2); assertStringContains( errMsg, `${method} requires at least 2 arguments, but only 1 present` ); }); }); unitTest(function headersInitMultiple(): void { const headers = new Headers([ ["Set-Cookie", "foo=bar"], ["Set-Cookie", "bar=baz"], ["X-Deno", "foo"], ["X-Deno", "bar"], ]); const actual = [...headers]; assertEquals(actual, [ ["set-cookie", "foo=bar"], ["set-cookie", "bar=baz"], ["x-deno", "foo, bar"], ]); }); unitTest(function headersAppendMultiple(): void { const headers = new Headers([ ["Set-Cookie", "foo=bar"], ["X-Deno", "foo"], ]); headers.append("set-Cookie", "bar=baz"); headers.append("x-Deno", "bar"); const actual = [...headers]; assertEquals(actual, [ ["set-cookie", "foo=bar"], ["x-deno", "foo, bar"], ["set-cookie", "bar=baz"], ]); }); unitTest(function headersAppendDuplicateSetCookieKey(): void { const headers = new Headers([["Set-Cookie", "foo=bar"]]); headers.append("set-Cookie", "foo=baz"); headers.append("Set-cookie", "baz=bar"); const actual = [...headers]; assertEquals(actual, [ ["set-cookie", "foo=baz"], ["set-cookie", "baz=bar"], ]); }); unitTest(function headersSetDuplicateCookieKey(): void { const headers = new Headers([["Set-Cookie", "foo=bar"]]); headers.set("set-Cookie", "foo=baz"); headers.set("set-cookie", "bar=qat"); const actual = [...headers]; assertEquals(actual, [ ["set-cookie", "foo=baz"], ["set-cookie", "bar=qat"], ]); }); unitTest(function headersGetSetCookie(): void { const headers = new Headers([ ["Set-Cookie", "foo=bar"], ["set-Cookie", "bar=qat"], ]); assertEquals(headers.get("SET-COOKIE"), "foo=bar, bar=qat"); }); unitTest(function toStringShouldBeWebCompatibility(): void { const headers = new Headers(); assertEquals(headers.toString(), "[object Headers]"); }); function stringify(...args: unknown[]): string { return stringifyArgs(args).replace(/\n$/, ""); } unitTest(function customInspectReturnsCorrectHeadersFormat(): void { const blankHeaders = new Headers(); assertEquals(stringify(blankHeaders), "Headers {}"); const singleHeader = new Headers([["Content-Type", "application/json"]]); assertEquals( stringify(singleHeader), "Headers { content-type: application/json }" ); const multiParamHeader = new Headers([ ["Content-Type", "application/json"], ["Content-Length", "1337"], ]); assertEquals( stringify(multiParamHeader), "Headers { content-type: application/json, content-length: 1337 }" ); });