aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Ashcon Partovi <ashcon@partovi.net> 2023-03-08 15:36:04 -0800
committerGravatar Ashcon Partovi <ashcon@partovi.net> 2023-03-08 15:36:16 -0800
commitebb42bb67b378921fb5ad001c4b7a1d4628b92d9 (patch)
tree8679b364ce3809c5a0d3e8dde102f985ae983297
parent42edcaae8cffa64af76828a9b970de2081e0db86 (diff)
downloadbun-ebb42bb67b378921fb5ad001c4b7a1d4628b92d9.tar.gz
bun-ebb42bb67b378921fb5ad001c4b7a1d4628b92d9.tar.zst
bun-ebb42bb67b378921fb5ad001c4b7a1d4628b92d9.zip
Add so many more tests, it's not even funny
-rw-r--r--test/js/deno/abort/abort-controller.test.ts1
-rw-r--r--test/js/deno/crypto/random.test.ts54
-rw-r--r--test/js/deno/crypto/webcrypto.test.ts2673
-rw-r--r--test/js/deno/encoding/encoding.test.ts343
-rw-r--r--test/js/deno/event/custom-event.test.ts27
-rw-r--r--test/js/deno/event/event-target.test.ts225
-rw-r--r--test/js/deno/event/event.test.ts124
-rw-r--r--test/js/deno/fetch/blob.test.ts (renamed from test/js/deno/html/blob.test.ts)1
-rw-r--r--test/js/deno/fetch/body.test.ts1
-rw-r--r--test/js/deno/fetch/file.test.ts124
-rw-r--r--test/js/deno/fetch/headers.test.ts459
-rw-r--r--test/js/deno/fetch/request.test.ts59
-rw-r--r--test/js/deno/fetch/response.test.ts105
-rw-r--r--test/js/deno/harness.ts22
-rw-r--r--test/js/deno/harness/fixture.ts18
-rw-r--r--test/js/deno/harness/test.ts (renamed from test/js/deno/harness/global.ts)24
-rw-r--r--test/js/deno/harness/util.ts45
-rw-r--r--test/js/deno/resources/imports.json6
-rw-r--r--test/js/deno/resources/tests.json73
-rw-r--r--test/js/deno/resources/url.json1
-rw-r--r--test/js/deno/scripts/postinstall.ts18
-rw-r--r--test/js/deno/url/url.test.ts362
-rw-r--r--test/js/deno/url/urlsearchparams.test.ts364
-rw-r--r--test/js/deno/v8/error.test.ts39
-rw-r--r--test/js/deno/websocket/websocket.test.ts19
25 files changed, 5150 insertions, 37 deletions
diff --git a/test/js/deno/abort/abort-controller.test.ts b/test/js/deno/abort/abort-controller.test.ts
index 651cadf31..32f33a15d 100644
--- a/test/js/deno/abort/abort-controller.test.ts
+++ b/test/js/deno/abort/abort-controller.test.ts
@@ -1,6 +1,5 @@
// Copyright 2018+ the Deno authors. All rights reserved. MIT license.
// https://raw.githubusercontent.com/denoland/deno/main/cli/tests/unit/abort_controller_test.ts
-
import { assert, assertEquals } from "deno:harness";
Deno.test(function basicAbortController() {
const controller = new AbortController();
diff --git a/test/js/deno/crypto/random.test.ts b/test/js/deno/crypto/random.test.ts
new file mode 100644
index 000000000..09258b3f5
--- /dev/null
+++ b/test/js/deno/crypto/random.test.ts
@@ -0,0 +1,54 @@
+// Copyright 2018+ the Deno authors. All rights reserved. MIT license.
+// https://raw.githubusercontent.com/denoland/deno/main/cli/tests/unit/get_random_values_test.ts
+import { assertNotEquals, assertStrictEquals } from "deno:harness";
+Deno.test(function getRandomValuesInt8Array() {
+ const arr = new Int8Array(32);
+ crypto.getRandomValues(arr);
+ assertNotEquals(arr, new Int8Array(32));
+});
+Deno.test(function getRandomValuesUint8Array() {
+ const arr = new Uint8Array(32);
+ crypto.getRandomValues(arr);
+ assertNotEquals(arr, new Uint8Array(32));
+});
+Deno.test(function getRandomValuesUint8ClampedArray() {
+ const arr = new Uint8ClampedArray(32);
+ crypto.getRandomValues(arr);
+ assertNotEquals(arr, new Uint8ClampedArray(32));
+});
+Deno.test(function getRandomValuesInt16Array() {
+ const arr = new Int16Array(4);
+ crypto.getRandomValues(arr);
+ assertNotEquals(arr, new Int16Array(4));
+});
+Deno.test(function getRandomValuesUint16Array() {
+ const arr = new Uint16Array(4);
+ crypto.getRandomValues(arr);
+ assertNotEquals(arr, new Uint16Array(4));
+});
+Deno.test(function getRandomValuesInt32Array() {
+ const arr = new Int32Array(8);
+ crypto.getRandomValues(arr);
+ assertNotEquals(arr, new Int32Array(8));
+});
+Deno.test(function getRandomValuesBigInt64Array() {
+ const arr = new BigInt64Array(8);
+ crypto.getRandomValues(arr);
+ assertNotEquals(arr, new BigInt64Array(8));
+});
+Deno.test(function getRandomValuesUint32Array() {
+ const arr = new Uint32Array(8);
+ crypto.getRandomValues(arr);
+ assertNotEquals(arr, new Uint32Array(8));
+});
+Deno.test(function getRandomValuesBigUint64Array() {
+ const arr = new BigUint64Array(8);
+ crypto.getRandomValues(arr);
+ assertNotEquals(arr, new BigUint64Array(8));
+});
+Deno.test(function getRandomValuesReturnValue() {
+ const arr = new Uint32Array(8);
+ const rtn = crypto.getRandomValues(arr);
+ assertNotEquals(arr, new Uint32Array(8));
+ assertStrictEquals(rtn, arr);
+});
diff --git a/test/js/deno/crypto/webcrypto.test.ts b/test/js/deno/crypto/webcrypto.test.ts
new file mode 100644
index 000000000..406892ebd
--- /dev/null
+++ b/test/js/deno/crypto/webcrypto.test.ts
@@ -0,0 +1,2673 @@
+// Copyright 2018+ the Deno authors. All rights reserved. MIT license.
+// https://raw.githubusercontent.com/denoland/deno/main/cli/tests/unit/webcrypto_test.ts
+import { assert, assertEquals, assertNotEquals, assertRejects } from "deno:harness";
+Deno.test(async function testImportArrayBufferKey() {
+ const subtle = window.crypto.subtle;
+ assert(subtle);
+ const key = new Uint8Array([
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15,
+ 16
+ ]);
+ const cryptoKey = await subtle.importKey("raw", key.buffer, {
+ name: "HMAC",
+ hash: "SHA-1"
+ }, true, [
+ "sign"
+ ]);
+ assert(cryptoKey);
+ await subtle.sign({
+ name: "HMAC"
+ }, cryptoKey, new Uint8Array(8));
+});
+Deno.test(async function testSignVerify() {
+ const subtle = window.crypto.subtle;
+ assert(subtle);
+ for (const algorithm of [
+ "RSA-PSS",
+ "RSASSA-PKCS1-v1_5"
+ ]){
+ for (const hash of [
+ "SHA-1",
+ "SHA-256",
+ "SHA-384",
+ "SHA-512"
+ ]){
+ const keyPair = await subtle.generateKey({
+ name: algorithm,
+ modulusLength: 2048,
+ publicExponent: new Uint8Array([
+ 1,
+ 0,
+ 1
+ ]),
+ hash
+ }, true, [
+ "sign",
+ "verify"
+ ]);
+ const data = new Uint8Array([
+ 1,
+ 2,
+ 3
+ ]);
+ const signAlgorithm = {
+ name: algorithm,
+ saltLength: 32
+ };
+ const signature = await subtle.sign(signAlgorithm, keyPair.privateKey, data);
+ assert(signature);
+ assert(signature.byteLength > 0);
+ assert(signature.byteLength % 8 == 0);
+ assert(signature instanceof ArrayBuffer);
+ const verified = await subtle.verify(signAlgorithm, keyPair.publicKey, signature, data);
+ assert(verified);
+ }
+ }
+});
+const plainText = new Uint8Array([
+ 95,
+ 77,
+ 186,
+ 79,
+ 50,
+ 12,
+ 12,
+ 232,
+ 118,
+ 114,
+ 90,
+ 252,
+ 229,
+ 251,
+ 210,
+ 91,
+ 248,
+ 62,
+ 90,
+ 113,
+ 37,
+ 160,
+ 140,
+ 175,
+ 231,
+ 60,
+ 62,
+ 186,
+ 196,
+ 33,
+ 119,
+ 157,
+ 249,
+ 213,
+ 93,
+ 24,
+ 12,
+ 58,
+ 233,
+ 148,
+ 38,
+ 69,
+ 225,
+ 216,
+ 47,
+ 238,
+ 140,
+ 157,
+ 41,
+ 75,
+ 60,
+ 177,
+ 160,
+ 138,
+ 153,
+ 49,
+ 32,
+ 27,
+ 60,
+ 14,
+ 129,
+ 252,
+ 71,
+ 202,
+ 207,
+ 131,
+ 21,
+ 162,
+ 175,
+ 102,
+ 50,
+ 65,
+ 19,
+ 195,
+ 182,
+ 98,
+ 48,
+ 195,
+ 70,
+ 8,
+ 196,
+ 244,
+ 89,
+ 54,
+ 52,
+ 206,
+ 2,
+ 178,
+ 103,
+ 54,
+ 34,
+ 119,
+ 240,
+ 168,
+ 64,
+ 202,
+ 116,
+ 188,
+ 61,
+ 26,
+ 98,
+ 54,
+ 149,
+ 44,
+ 94,
+ 215,
+ 170,
+ 248,
+ 168,
+ 254,
+ 203,
+ 221,
+ 250,
+ 117,
+ 132,
+ 230,
+ 151,
+ 140,
+ 234,
+ 93,
+ 42,
+ 91,
+ 159,
+ 183,
+ 241,
+ 180,
+ 140,
+ 139,
+ 11,
+ 229,
+ 138,
+ 48,
+ 82,
+ 2,
+ 117,
+ 77,
+ 131,
+ 118,
+ 16,
+ 115,
+ 116,
+ 121,
+ 60,
+ 240,
+ 38,
+ 170,
+ 238,
+ 83,
+ 0,
+ 114,
+ 125,
+ 131,
+ 108,
+ 215,
+ 30,
+ 113,
+ 179,
+ 69,
+ 221,
+ 178,
+ 228,
+ 68,
+ 70,
+ 255,
+ 197,
+ 185,
+ 1,
+ 99,
+ 84,
+ 19,
+ 137,
+ 13,
+ 145,
+ 14,
+ 163,
+ 128,
+ 152,
+ 74,
+ 144,
+ 25,
+ 16,
+ 49,
+ 50,
+ 63,
+ 22,
+ 219,
+ 204,
+ 157,
+ 107,
+ 225,
+ 104,
+ 184,
+ 72,
+ 133,
+ 56,
+ 76,
+ 160,
+ 62,
+ 18,
+ 96,
+ 10,
+ 193,
+ 194,
+ 72,
+ 2,
+ 138,
+ 243,
+ 114,
+ 108,
+ 201,
+ 52,
+ 99,
+ 136,
+ 46,
+ 168,
+ 192,
+ 42,
+ 171
+]);
+const hashPlainTextVector = [
+ {
+ hash: "SHA-1",
+ plainText: plainText.slice(0, 214)
+ },
+ {
+ hash: "SHA-256",
+ plainText: plainText.slice(0, 190)
+ },
+ {
+ hash: "SHA-384",
+ plainText: plainText.slice(0, 158)
+ },
+ {
+ hash: "SHA-512",
+ plainText: plainText.slice(0, 126)
+ }
+];
+Deno.test(async function testEncryptDecrypt() {
+ const subtle = window.crypto.subtle;
+ assert(subtle);
+ for (const { hash , plainText } of hashPlainTextVector){
+ const keyPair = await subtle.generateKey({
+ name: "RSA-OAEP",
+ modulusLength: 2048,
+ publicExponent: new Uint8Array([
+ 1,
+ 0,
+ 1
+ ]),
+ hash
+ }, true, [
+ "encrypt",
+ "decrypt"
+ ]);
+ const encryptAlgorithm = {
+ name: "RSA-OAEP"
+ };
+ const cipherText = await subtle.encrypt(encryptAlgorithm, keyPair.publicKey, plainText);
+ assert(cipherText);
+ assert(cipherText.byteLength > 0);
+ assertEquals(cipherText.byteLength * 8, 2048);
+ assert(cipherText instanceof ArrayBuffer);
+ const decrypted = await subtle.decrypt(encryptAlgorithm, keyPair.privateKey, cipherText);
+ assert(decrypted);
+ assert(decrypted instanceof ArrayBuffer);
+ assertEquals(new Uint8Array(decrypted), plainText);
+ const badPlainText = new Uint8Array(plainText.byteLength + 1);
+ badPlainText.set(plainText, 0);
+ badPlainText.set(new Uint8Array([
+ 32
+ ]), plainText.byteLength);
+ await assertRejects(async ()=>{
+ await subtle.encrypt(encryptAlgorithm, keyPair.publicKey, badPlainText);
+ throw new TypeError("unreachable");
+ }, DOMException);
+ }
+});
+Deno.test(async function testGenerateRSAKey() {
+ const subtle = window.crypto.subtle;
+ assert(subtle);
+ const keyPair = await subtle.generateKey({
+ name: "RSA-PSS",
+ modulusLength: 2048,
+ publicExponent: new Uint8Array([
+ 1,
+ 0,
+ 1
+ ]),
+ hash: "SHA-256"
+ }, true, [
+ "sign",
+ "verify"
+ ]);
+ assert(keyPair.privateKey);
+ assert(keyPair.publicKey);
+ assertEquals(keyPair.privateKey.extractable, true);
+ assert(keyPair.privateKey.usages.includes("sign"));
+});
+Deno.test(async function testGenerateHMACKey() {
+ const key = await window.crypto.subtle.generateKey({
+ name: "HMAC",
+ hash: "SHA-512"
+ }, true, [
+ "sign",
+ "verify"
+ ]);
+ assert(key);
+ assertEquals(key.extractable, true);
+ assert(key.usages.includes("sign"));
+});
+Deno.test(async function testECDSASignVerify() {
+ const key = await window.crypto.subtle.generateKey({
+ name: "ECDSA",
+ namedCurve: "P-384"
+ }, true, [
+ "sign",
+ "verify"
+ ]);
+ const encoder = new TextEncoder();
+ const encoded = encoder.encode("Hello, World!");
+ const signature = await window.crypto.subtle.sign({
+ name: "ECDSA",
+ hash: "SHA-384"
+ }, key.privateKey, encoded);
+ assert(signature);
+ assert(signature instanceof ArrayBuffer);
+ const verified = await window.crypto.subtle.verify({
+ hash: {
+ name: "SHA-384"
+ },
+ name: "ECDSA"
+ }, key.publicKey, signature, encoded);
+ assert(verified);
+});
+Deno.test(async function testECDSASignVerifyFail() {
+ const key = await window.crypto.subtle.generateKey({
+ name: "ECDSA",
+ namedCurve: "P-384"
+ }, true, [
+ "sign",
+ "verify"
+ ]);
+ const encoded = new Uint8Array([
+ 1
+ ]);
+ await assertRejects(async ()=>{
+ await window.crypto.subtle.sign({
+ name: "ECDSA",
+ hash: "SHA-384"
+ }, key.publicKey, new Uint8Array([
+ 1
+ ]));
+ throw new TypeError("unreachable");
+ }, DOMException);
+ const signature = await window.crypto.subtle.sign({
+ name: "ECDSA",
+ hash: "SHA-384"
+ }, key.privateKey, encoded);
+ await assertRejects(async ()=>{
+ await window.crypto.subtle.verify({
+ hash: {
+ name: "SHA-384"
+ },
+ name: "ECDSA"
+ }, key.privateKey, signature, encoded);
+ throw new TypeError("unreachable");
+ }, DOMException);
+});
+Deno.test(async function testSignRSASSAKey() {
+ const subtle = window.crypto.subtle;
+ assert(subtle);
+ const keyPair = await subtle.generateKey({
+ name: "RSASSA-PKCS1-v1_5",
+ modulusLength: 2048,
+ publicExponent: new Uint8Array([
+ 1,
+ 0,
+ 1
+ ]),
+ hash: "SHA-256"
+ }, true, [
+ "sign",
+ "verify"
+ ]);
+ assert(keyPair.privateKey);
+ assert(keyPair.publicKey);
+ assertEquals(keyPair.privateKey.extractable, true);
+ assert(keyPair.privateKey.usages.includes("sign"));
+ const encoder = new TextEncoder();
+ const encoded = encoder.encode("Hello, World!");
+ const signature = await window.crypto.subtle.sign({
+ name: "RSASSA-PKCS1-v1_5"
+ }, keyPair.privateKey, encoded);
+ assert(signature);
+});
+const rawKey = new Uint8Array([
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15,
+ 16
+]);
+const jwk: JsonWebKey = {
+ kty: "oct",
+ k: "AQIDBAUGBwgJCgsMDQ4PEA",
+ alg: "HS256",
+ ext: true,
+ "key_ops": [
+ "sign"
+ ]
+};
+Deno.test(async function subtleCryptoHmacImportExport() {
+ const key1 = await crypto.subtle.importKey("raw", rawKey, {
+ name: "HMAC",
+ hash: "SHA-256"
+ }, true, [
+ "sign"
+ ]);
+ const key2 = await crypto.subtle.importKey("jwk", jwk, {
+ name: "HMAC",
+ hash: "SHA-256"
+ }, true, [
+ "sign"
+ ]);
+ const actual1 = await crypto.subtle.sign({
+ name: "HMAC"
+ }, key1, new Uint8Array([
+ 1,
+ 2,
+ 3,
+ 4
+ ]));
+ const actual2 = await crypto.subtle.sign({
+ name: "HMAC"
+ }, key2, new Uint8Array([
+ 1,
+ 2,
+ 3,
+ 4
+ ]));
+ const expected = new Uint8Array([
+ 59,
+ 170,
+ 255,
+ 216,
+ 51,
+ 141,
+ 51,
+ 194,
+ 213,
+ 48,
+ 41,
+ 191,
+ 184,
+ 40,
+ 216,
+ 47,
+ 130,
+ 165,
+ 203,
+ 26,
+ 163,
+ 43,
+ 38,
+ 71,
+ 23,
+ 122,
+ 222,
+ 1,
+ 146,
+ 46,
+ 182,
+ 87
+ ]);
+ assertEquals(new Uint8Array(actual1), expected);
+ assertEquals(new Uint8Array(actual2), expected);
+ const exportedKey1 = await crypto.subtle.exportKey("raw", key1);
+ assertEquals(new Uint8Array(exportedKey1), rawKey);
+ const exportedKey2 = await crypto.subtle.exportKey("jwk", key2);
+ assertEquals(exportedKey2, jwk);
+});
+Deno.test(async function generateImportHmacJwk() {
+ const key = await crypto.subtle.generateKey({
+ name: "HMAC",
+ hash: "SHA-512"
+ }, true, [
+ "sign"
+ ]);
+ assert(key);
+ assertEquals(key.type, "secret");
+ assertEquals(key.extractable, true);
+ assertEquals(key.usages, [
+ "sign"
+ ]);
+ const exportedKey = await crypto.subtle.exportKey("jwk", key);
+ assertEquals(exportedKey.kty, "oct");
+ assertEquals(exportedKey.alg, "HS512");
+ assertEquals(exportedKey.key_ops, [
+ "sign"
+ ]);
+ assertEquals(exportedKey.ext, true);
+ assert(typeof exportedKey.k == "string");
+ assertEquals(exportedKey.k.length, 171);
+});
+const pkcs8TestVectors = [
+ {
+ pem: "cli/tests/testdata/webcrypto/id_rsaEncryption.pem",
+ hash: "SHA-256"
+ }
+];
+Deno.test({
+ permissions: {
+ read: true
+ }
+}, async function importRsaPkcs8() {
+ const pemHeader = "-----BEGIN PRIVATE KEY-----";
+ const pemFooter = "-----END PRIVATE KEY-----";
+ for (const { pem , hash } of pkcs8TestVectors){
+ const keyFile = await Deno.readTextFile(pem);
+ const pemContents = keyFile.substring(pemHeader.length, keyFile.length - pemFooter.length);
+ const binaryDerString = atob(pemContents);
+ const binaryDer = new Uint8Array(binaryDerString.length);
+ for(let i = 0; i < binaryDerString.length; i++){
+ binaryDer[i] = binaryDerString.charCodeAt(i);
+ }
+ const key = await crypto.subtle.importKey("pkcs8", binaryDer, {
+ name: "RSA-PSS",
+ hash
+ }, true, [
+ "sign"
+ ]);
+ assert(key);
+ assertEquals(key.type, "private");
+ assertEquals(key.extractable, true);
+ assertEquals(key.usages, [
+ "sign"
+ ]);
+ const algorithm = key.algorithm as RsaHashedKeyAlgorithm;
+ assertEquals(algorithm.name, "RSA-PSS");
+ assertEquals(algorithm.hash.name, hash);
+ assertEquals(algorithm.modulusLength, 2048);
+ assertEquals(algorithm.publicExponent, new Uint8Array([
+ 1,
+ 0,
+ 1
+ ]));
+ }
+});
+const nonInteroperableVectors = [
+ {
+ pem: "cli/tests/testdata/webcrypto/id_rsassaPss.pem",
+ hash: "SHA-256"
+ },
+ {
+ pem: "cli/tests/testdata/webcrypto/id_rsassaPss_default.pem",
+ hash: "SHA-1"
+ },
+ {
+ pem: "cli/tests/testdata/webcrypto/id_rsassaPss_saltLen_30.pem",
+ hash: "SHA-1"
+ }
+];
+Deno.test({
+ permissions: {
+ read: true
+ }
+}, async function importNonInteroperableRsaPkcs8() {
+ const pemHeader = "-----BEGIN PRIVATE KEY-----";
+ const pemFooter = "-----END PRIVATE KEY-----";
+ for (const { pem , hash } of nonInteroperableVectors){
+ const keyFile = await Deno.readTextFile(pem);
+ const pemContents = keyFile.substring(pemHeader.length, keyFile.length - pemFooter.length);
+ const binaryDerString = atob(pemContents);
+ const binaryDer = new Uint8Array(binaryDerString.length);
+ for(let i = 0; i < binaryDerString.length; i++){
+ binaryDer[i] = binaryDerString.charCodeAt(i);
+ }
+ await assertRejects(()=>crypto.subtle.importKey("pkcs8", binaryDer, {
+ name: "RSA-PSS",
+ hash
+ }, true, [
+ "sign"
+ ]), DOMException, "unsupported algorithm");
+ }
+});
+const asn1AlgorithmIdentifier = new Uint8Array([
+ 0x02,
+ 0x01,
+ 0x00,
+ 0x30,
+ 0x0d,
+ 0x06,
+ 0x09,
+ 0x2a,
+ 0x86,
+ 0x48,
+ 0x86,
+ 0xf7,
+ 0x0d,
+ 0x01,
+ 0x01,
+ 0x01,
+ 0x05,
+ 0x00
+]);
+Deno.test(async function rsaExport() {
+ for (const algorithm of [
+ "RSASSA-PKCS1-v1_5",
+ "RSA-PSS",
+ "RSA-OAEP"
+ ]){
+ const keyPair = await crypto.subtle.generateKey({
+ name: algorithm,
+ modulusLength: 2048,
+ publicExponent: new Uint8Array([
+ 1,
+ 0,
+ 1
+ ]),
+ hash: "SHA-256"
+ }, true, algorithm !== "RSA-OAEP" ? [
+ "sign",
+ "verify"
+ ] : [
+ "encrypt",
+ "decrypt"
+ ]);
+ assert(keyPair.privateKey);
+ assert(keyPair.publicKey);
+ assertEquals(keyPair.privateKey.extractable, true);
+ const exportedPrivateKey = await crypto.subtle.exportKey("pkcs8", keyPair.privateKey);
+ assert(exportedPrivateKey);
+ assert(exportedPrivateKey instanceof ArrayBuffer);
+ const pkcs8 = new Uint8Array(exportedPrivateKey);
+ assert(pkcs8.length > 0);
+ assertEquals(pkcs8.slice(4, asn1AlgorithmIdentifier.byteLength + 4), asn1AlgorithmIdentifier);
+ const exportedPublicKey = await crypto.subtle.exportKey("spki", keyPair.publicKey);
+ const spki = new Uint8Array(exportedPublicKey);
+ assert(spki.length > 0);
+ assertEquals(spki.slice(4, asn1AlgorithmIdentifier.byteLength + 1), asn1AlgorithmIdentifier.slice(3));
+ }
+});
+Deno.test(async function testHkdfDeriveBits() {
+ const rawKey = crypto.getRandomValues(new Uint8Array(16));
+ const key = await crypto.subtle.importKey("raw", rawKey, {
+ name: "HKDF",
+ hash: "SHA-256"
+ }, false, [
+ "deriveBits"
+ ]);
+ const salt = crypto.getRandomValues(new Uint8Array(16));
+ const info = crypto.getRandomValues(new Uint8Array(16));
+ const result = await crypto.subtle.deriveBits({
+ name: "HKDF",
+ hash: "SHA-256",
+ salt: salt,
+ info: info
+ }, key, 128);
+ assertEquals(result.byteLength, 128 / 8);
+});
+Deno.test(async function testHkdfDeriveBitsWithLargeKeySize() {
+ const key = await crypto.subtle.importKey("raw", new Uint8Array([
+ 0x00
+ ]), "HKDF", false, [
+ "deriveBits"
+ ]);
+ await assertRejects(()=>crypto.subtle.deriveBits({
+ name: "HKDF",
+ hash: "SHA-1",
+ salt: new Uint8Array(),
+ info: new Uint8Array()
+ }, key, ((20 * 255) << 3) + 8), DOMException, "The length provided for HKDF is too large");
+});
+Deno.test(async function testEcdhDeriveBitsWithShorterLength() {
+ const keypair = await crypto.subtle.generateKey({
+ name: "ECDH",
+ namedCurve: "P-384"
+ }, true, [
+ "deriveBits",
+ "deriveKey"
+ ]);
+ const result = await crypto.subtle.deriveBits({
+ name: "ECDH",
+ public: keypair.publicKey
+ }, keypair.privateKey, 256);
+ assertEquals(result.byteLength * 8, 256);
+});
+Deno.test(async function testEcdhDeriveBitsWithLongerLength() {
+ const keypair = await crypto.subtle.generateKey({
+ name: "ECDH",
+ namedCurve: "P-384"
+ }, true, [
+ "deriveBits",
+ "deriveKey"
+ ]);
+ await assertRejects(()=>crypto.subtle.deriveBits({
+ name: "ECDH",
+ public: keypair.publicKey
+ }, keypair.privateKey, 512), DOMException, "Invalid length");
+});
+Deno.test(async function testEcdhDeriveBitsWithNullLength() {
+ const keypair = await crypto.subtle.generateKey({
+ name: "ECDH",
+ namedCurve: "P-384"
+ }, true, [
+ "deriveBits",
+ "deriveKey"
+ ]);
+ const result = await crypto.subtle.deriveBits({
+ name: "ECDH",
+ public: keypair.publicKey
+ }, keypair.privateKey, null);
+ assertEquals(result.byteLength * 8, 384);
+});
+Deno.test(async function testDeriveKey() {
+ const rawKey = crypto.getRandomValues(new Uint8Array(16));
+ const key = await crypto.subtle.importKey("raw", rawKey, "PBKDF2", false, [
+ "deriveKey",
+ "deriveBits"
+ ]);
+ const salt = crypto.getRandomValues(new Uint8Array(16));
+ const derivedKey = await crypto.subtle.deriveKey({
+ name: "PBKDF2",
+ salt,
+ iterations: 1000,
+ hash: "SHA-256"
+ }, key, {
+ name: "HMAC",
+ hash: "SHA-256"
+ }, true, [
+ "sign"
+ ]);
+ assert(derivedKey instanceof CryptoKey);
+ assertEquals(derivedKey.type, "secret");
+ assertEquals(derivedKey.extractable, true);
+ assertEquals(derivedKey.usages, [
+ "sign"
+ ]);
+ const algorithm = derivedKey.algorithm as HmacKeyAlgorithm;
+ assertEquals(algorithm.name, "HMAC");
+ assertEquals(algorithm.hash.name, "SHA-256");
+ assertEquals(algorithm.length, 512);
+});
+Deno.test(async function testAesCbcEncryptDecrypt() {
+ const key = await crypto.subtle.generateKey({
+ name: "AES-CBC",
+ length: 128
+ }, true, [
+ "encrypt",
+ "decrypt"
+ ]);
+ const iv = crypto.getRandomValues(new Uint8Array(16));
+ const encrypted = await crypto.subtle.encrypt({
+ name: "AES-CBC",
+ iv
+ }, key as CryptoKey, new Uint8Array([
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6
+ ]));
+ assert(encrypted instanceof ArrayBuffer);
+ assertEquals(encrypted.byteLength, 16);
+ const decrypted = await crypto.subtle.decrypt({
+ name: "AES-CBC",
+ iv
+ }, key as CryptoKey, encrypted);
+ assert(decrypted instanceof ArrayBuffer);
+ assertEquals(decrypted.byteLength, 6);
+ assertEquals(new Uint8Array(decrypted), new Uint8Array([
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6
+ ]));
+});
+Deno.test(async function testAesCtrEncryptDecrypt() {
+ async function aesCtrRoundTrip(key: CryptoKey, counter: Uint8Array, length: number, plainText: Uint8Array) {
+ const cipherText = await crypto.subtle.encrypt({
+ name: "AES-CTR",
+ counter,
+ length
+ }, key, plainText);
+ assert(cipherText instanceof ArrayBuffer);
+ assertEquals(cipherText.byteLength, plainText.byteLength);
+ assertNotEquals(new Uint8Array(cipherText), plainText);
+ const decryptedText = await crypto.subtle.decrypt({
+ name: "AES-CTR",
+ counter,
+ length
+ }, key, cipherText);
+ assert(decryptedText instanceof ArrayBuffer);
+ assertEquals(decryptedText.byteLength, plainText.byteLength);
+ assertEquals(new Uint8Array(decryptedText), plainText);
+ }
+ for (const keySize of [
+ 128,
+ 192,
+ 256
+ ]){
+ const key = await crypto.subtle.generateKey({
+ name: "AES-CTR",
+ length: keySize
+ }, true, [
+ "encrypt",
+ "decrypt"
+ ]) as CryptoKey;
+ for (const length of [
+ 128
+ ]){
+ const counter = crypto.getRandomValues(new Uint8Array(16));
+ await aesCtrRoundTrip(key, counter, length, new Uint8Array([
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6
+ ]));
+ }
+ for (const length of [
+ 32,
+ 64,
+ 128
+ ]){
+ const plaintext1 = crypto.getRandomValues(new Uint8Array(32));
+ const counter = new Uint8Array(16);
+ for(let off = 0; off < 16 - (length / 8); ++off){
+ counter[off] = off;
+ }
+ const ciphertext1 = await crypto.subtle.encrypt({
+ name: "AES-CTR",
+ counter,
+ length
+ }, key, plaintext1);
+ for(let off = 16 - (length / 8); off < 16; ++off){
+ counter[off] = 0xff;
+ }
+ const plaintext2 = new Uint8Array(48);
+ plaintext2.set(plaintext1, 16);
+ const ciphertext2 = await crypto.subtle.encrypt({
+ name: "AES-CTR",
+ counter,
+ length
+ }, key, plaintext2);
+ assertEquals(new Uint8Array(ciphertext1), new Uint8Array(ciphertext2).slice(16));
+ }
+ }
+});
+Deno.test(async function testECDH() {
+ for (const keySize of [
+ 256,
+ 384
+ ]){
+ const keyPair = await crypto.subtle.generateKey({
+ name: "ECDH",
+ namedCurve: "P-" + keySize
+ }, true, [
+ "deriveBits"
+ ]);
+ const derivedKey = await crypto.subtle.deriveBits({
+ name: "ECDH",
+ public: keyPair.publicKey
+ }, keyPair.privateKey, keySize);
+ assert(derivedKey instanceof ArrayBuffer);
+ assertEquals(derivedKey.byteLength, keySize / 8);
+ }
+});
+Deno.test(async function testWrapKey() {
+ const key = await crypto.subtle.generateKey({
+ name: "RSA-OAEP",
+ modulusLength: 4096,
+ publicExponent: new Uint8Array([
+ 1,
+ 0,
+ 1
+ ]),
+ hash: "SHA-256"
+ }, true, [
+ "wrapKey",
+ "unwrapKey"
+ ]);
+ const hmacKey = await crypto.subtle.generateKey({
+ name: "HMAC",
+ hash: "SHA-256",
+ length: 128
+ }, true, [
+ "sign"
+ ]);
+ const wrappedKey = await crypto.subtle.wrapKey("raw", hmacKey, key.publicKey, {
+ name: "RSA-OAEP",
+ label: new Uint8Array(8)
+ });
+ assert(wrappedKey instanceof ArrayBuffer);
+ assertEquals(wrappedKey.byteLength, 512);
+});
+Deno.test(async function testAesKeyGen() {
+ const key = await crypto.subtle.generateKey({
+ name: "AES-GCM",
+ length: 256
+ }, true, [
+ "encrypt",
+ "decrypt"
+ ]);
+ assert(key);
+ assertEquals(key.type, "secret");
+ assertEquals(key.extractable, true);
+ assertEquals(key.usages, [
+ "encrypt",
+ "decrypt"
+ ]);
+ const algorithm = key.algorithm as AesKeyAlgorithm;
+ assertEquals(algorithm.name, "AES-GCM");
+ assertEquals(algorithm.length, 256);
+});
+Deno.test(async function testUnwrapKey() {
+ const subtle = crypto.subtle;
+ const AES_KEY: AesKeyAlgorithm & AesCbcParams = {
+ name: "AES-CBC",
+ length: 128,
+ iv: new Uint8Array(16)
+ };
+ const RSA_KEY: RsaHashedKeyGenParams & RsaOaepParams = {
+ name: "RSA-OAEP",
+ modulusLength: 2048,
+ publicExponent: new Uint8Array([
+ 1,
+ 0,
+ 1
+ ]),
+ hash: "SHA-1"
+ };
+ const aesKey = await subtle.generateKey(AES_KEY, true, [
+ "encrypt",
+ "decrypt"
+ ]);
+ const rsaKeyPair = await subtle.generateKey({
+ name: "RSA-OAEP",
+ hash: "SHA-1",
+ publicExponent: new Uint8Array([
+ 1,
+ 0,
+ 1
+ ]),
+ modulusLength: 2048
+ }, false, [
+ "wrapKey",
+ "encrypt",
+ "unwrapKey",
+ "decrypt"
+ ]);
+ const enc = await subtle.wrapKey("raw", aesKey, rsaKeyPair.publicKey, RSA_KEY);
+ const unwrappedKey = await subtle.unwrapKey("raw", enc, rsaKeyPair.privateKey, RSA_KEY, AES_KEY, false, [
+ "encrypt",
+ "decrypt"
+ ]);
+ assert(unwrappedKey instanceof CryptoKey);
+ assertEquals(unwrappedKey.type, "secret");
+ assertEquals(unwrappedKey.extractable, false);
+ assertEquals(unwrappedKey.usages, [
+ "encrypt",
+ "decrypt"
+ ]);
+});
+Deno.test(async function testDecryptWithInvalidIntializationVector() {
+ const data = new Uint8Array([
+ 42,
+ 42,
+ 42,
+ 42,
+ 42,
+ 42,
+ 42,
+ 42,
+ 42,
+ 42,
+ 42,
+ 42,
+ 42,
+ 42,
+ 42
+ ]);
+ const key = await crypto.subtle.importKey("raw", new Uint8Array(16), {
+ name: "AES-CBC",
+ length: 256
+ }, true, [
+ "encrypt",
+ "decrypt"
+ ]);
+ const initVector = new Uint8Array([
+ 0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15
+ ]);
+ const encrypted = await crypto.subtle.encrypt({
+ name: "AES-CBC",
+ iv: initVector
+ }, key, data);
+ const initVector2 = new Uint8Array([
+ 15,
+ 14,
+ 13,
+ 12,
+ 11,
+ 10,
+ 9,
+ 8,
+ 7,
+ 6,
+ 5,
+ 4,
+ 3,
+ 2,
+ 1,
+ 0
+ ]);
+ await assertRejects(async ()=>{
+ await crypto.subtle.decrypt({
+ name: "AES-CBC",
+ iv: initVector2
+ }, key, encrypted);
+ }, DOMException);
+});
+const jwtRSAKeys = {
+ "1024": {
+ size: 1024,
+ publicJWK: {
+ kty: "RSA",
+ n: "zZn4sRGfjQos56yL_Qy1R9NI-THMnFynn94g5RxA6wGrJh4BJT3x6I9x0IbpS3q-d4ORA6R2vuDMh8dDFRr9RDH6XY-gUScc9U5Jz3UA2KmVfsCbnUPvcAmMV_ENA7_TF0ivVjuIFodyDTx7EKHNVTrHHSlrbt7spbmcivs23Zc",
+ e: "AQAB"
+ },
+ privateJWK: {
+ kty: "RSA",
+ n: "zZn4sRGfjQos56yL_Qy1R9NI-THMnFynn94g5RxA6wGrJh4BJT3x6I9x0IbpS3q-d4ORA6R2vuDMh8dDFRr9RDH6XY-gUScc9U5Jz3UA2KmVfsCbnUPvcAmMV_ENA7_TF0ivVjuIFodyDTx7EKHNVTrHHSlrbt7spbmcivs23Zc",
+ e: "AQAB",
+ d: "YqIK_GdH85F-GWZdgfgmv15NE78gOaL5h2g4v7DeM9-JC7A5PHSLKNYn87HFGcC4vv0PBIBRtyCA_mJJfEaGWORVCOXSBpWNepMYpio52n3w5uj5UZEsBnbtZc0EtWhVF2Auqa7VbiKrWcQUEgEI8V0gE5D4tyBg8GXv9975dQE",
+ p: "9BrAg5L1zfqGPuWJDuDCBX-TmtZdrOI3Ys4ZaN-yMPlTjwWSEPO0qnfjEZcw2VgXHgJJmbVco6TxckJCmEYqeQ",
+ q: "157jDJ1Ya5nmQvTPbhKAPAeMWogxCyaQTkBrp30pEKd6mGSB385hqr4BIk8s3f7MdXpM-USpaZgUoT4o_2VEjw",
+ dp: "qdd_QUzcaB-6jkKo1Ug-1xKIAgDLFsIjJUUfWt_iHL8ti2Kl2dOnTcCypgebPm5TT1bqHN-agGYAdK5zpX2UiQ",
+ dq: "hNRfwOSplNfhLvxLUN7a2qA3yYm-1MSz_1DWQP7srlLORlUcYPht2FZmsnEeDcAqynBGPQUcbG2Av_hgHz2OZw",
+ qi: "zbpJQAhinrxSbVKxBQ2EZGFUD2e3WCXbAJRYpk8HVQ5AA52OhKTicOye2hEHnrgpFKzC8iznTsCG3FMkvwcj4Q"
+ }
+ },
+ "2048": {
+ size: 2048,
+ publicJWK: {
+ kty: "RSA",
+ n: "09eVwAhT9SPBxdEN-74BBeEANGaVGwqH-YglIc4VV7jfhR2by5ivzVq8NCeQ1_ACDIlTDY8CTMQ5E1c1SEXmo_T7q84XUGXf8U9mx6uRg46sV7fF-hkwJR80BFVsvWxp4ahPlVJYj__94ft7rIVvchb5tyalOjrYFCJoFnSgq-i3ZjU06csI9XnO5klINucD_Qq0vUhO23_Add2HSYoRjab8YiJJR_Eths7Pq6HHd2RSXmwYp5foRnwe0_U75XmesHWDJlJUHYbwCZo0kP9G8g4QbucwU-MSNBkZOO2x2ZtZNexpHd0ThkATbnNlpVG_z2AGNORp_Ve3rlXwrGIXXw",
+ e: "AQAB"
+ },
+ privateJWK: {
+ kty: "RSA",
+ n: "09eVwAhT9SPBxdEN-74BBeEANGaVGwqH-YglIc4VV7jfhR2by5ivzVq8NCeQ1_ACDIlTDY8CTMQ5E1c1SEXmo_T7q84XUGXf8U9mx6uRg46sV7fF-hkwJR80BFVsvWxp4ahPlVJYj__94ft7rIVvchb5tyalOjrYFCJoFnSgq-i3ZjU06csI9XnO5klINucD_Qq0vUhO23_Add2HSYoRjab8YiJJR_Eths7Pq6HHd2RSXmwYp5foRnwe0_U75XmesHWDJlJUHYbwCZo0kP9G8g4QbucwU-MSNBkZOO2x2ZtZNexpHd0ThkATbnNlpVG_z2AGNORp_Ve3rlXwrGIXXw",
+ e: "AQAB",
+ d: "H4xboN2co0VP9kXL71G8lUOM5EDis8Q9u8uqu_4U75t4rjpamVeD1vFMVfgOehokM_m_hKVnkkcmuNqj9L90ObaiRFPM5QxG7YkFpXbHlPAKeoXD1hsqMF0VQg_2wb8DhberInHA_rEA_kaVhHvavQLu7Xez45gf1d_J4I4931vjlCB6cupbLL0H5hHsxbMsX_5nnmAJdL_U3gD-U7ZdQheUPhDBJR2KeGzvnTm3KVKpOnwn-1Cd45MU4-KDdP0FcBVEuBsSrsQHliTaciBgkbyj__BangPj3edDxTkb-fKkEvhkXRjAoJs1ixt8nfSGDce9cM_GqAX9XGb4s2QkAQ",
+ dp: "mM82RBwzGzi9LAqjGbi-badLtHRRBoH9sfMrJuOtzxRnmwBFccg_lwy-qAhUTqnN9kvD0H1FzXWzoFPFJbyi-AOmumYGpWm_PvzQGldne5CPJ02pYaeg-t1BePsT3OpIq0Am8E2Kjf9polpRJwIjO7Kx8UJKkhg5bISnsy0V8wE",
+ dq: "ZlM4AvrWIpXwqsH_5Q-6BsLJdbnN_GypFCXoT9VXniXncSBZIWCkgDndBdWkSzyzIN65NiMRBfZaf9yduTFj4kvOPwb3ch3J0OxGJk0Ary4OGSlS1zNwMl93ALGal1FzpWUuiia9L9RraGqXAUr13L7TIIMRobRjpAV-z7M-ruM",
+ p: "7VwGt_tJcAFQHrmDw5dM1EBru6fidM45NDv6VVOEbxKuD5Sh2EfAHfm5c6oouA1gZqwvKH0sn_XpB1NsyYyHEQd3sBVdK0zRjTo-E9mRP-1s-LMd5YDXVq6HE339nxpXsmO25slQEF6zBrj1bSNNXBFc7fgDnlq-HIeleMvsY_E",
+ q: "5HqMHLzb4IgXhUl4pLz7E4kjY8PH2YGzaQfK805zJMbOXzmlZK0hizKo34Qqd2nB9xos7QgzOYQrNfSWheARwVsSQzAE0vGvw3zHIPP_lTtChBlCTPctQcURjw4dXcnK1oQ-IT321FNOW3EO-YTsyGcypJqJujlZrLbxYjOjQE8",
+ qi: "OQXzi9gypDnpdHatIi0FaUGP8LSzfVH0AUugURJXs4BTJpvA9y4hcpBQLrcl7H_vq6kbGmvC49V-9I5HNVX_AuxGIXKuLZr5WOxPq8gLTqHV7X5ZJDtWIP_nq2NNgCQQyNNRrxebiWlwGK9GnX_unewT6jopI_oFhwp0Q13rBR0"
+ }
+ },
+ "4096": {
+ size: 4096,
+ publicJWK: {
+ kty: "RSA",
+ n: "2qr2TL2c2JmbsN0OLIRnaAB_ZKb1-Gh9H0qb4lrBuDaqkW_eFPwT-JIsvnNJvDT7BLJ57tTMIj56ZMtv6efSSTWSk9MOoW2J1K_iEretZ2cegB_aRX7qQVjnoFsz9U02BKfAIUT0o_K7b9G08d1rrAUohi_SVQhwObodg7BddMbKUmz70QNIS487LN44WUVnn9OgE9atTYUARNukT0DuQb3J-K20ksTuVujXbSelohDmLobqlGoi5sY_548Qs9BtFmQ2nGuEHNB2zdlZ5EvEqbUFVZ2QboG6jXdoos6qcwdgUvAhj1Hz10Ngic_RFqL7bNDoIOzNp66hdA35uxbwuaygZ16ikxoPj7eTYud1hrkyQCgeGw2YhCiKIE6eos_U5dL7WHRD5aSkkzsgXtnF8pVmStsuf0QcdAoC-eeCex0tSTgRw9AtGTz8Yr1tGQD9l_580zAXnE6jmrwRRQ68EEA7vohGov3tnG8pGyg_zcxeADLtPlfTc1tEwmh3SGrioDClioYCipm1JvkweEgP9eMPpEC8SgRU1VNDSVe1SF4uNsH8vA7PHFKfg6juqJEc5ht-l10FYER-Qq6bZXsU2oNcfE5SLDeLTWmxiHmxK00M8ABMFIV5gUkPoMiWcl87O6XwzA2chsIERp7Vb-Vn2O-EELiXzv7lPhc6fTGQ0Nc",
+ e: "AQAB"
+ },
+ privateJWK: {
+ kty: "RSA",
+ n: "2qr2TL2c2JmbsN0OLIRnaAB_ZKb1-Gh9H0qb4lrBuDaqkW_eFPwT-JIsvnNJvDT7BLJ57tTMIj56ZMtv6efSSTWSk9MOoW2J1K_iEretZ2cegB_aRX7qQVjnoFsz9U02BKfAIUT0o_K7b9G08d1rrAUohi_SVQhwObodg7BddMbKUmz70QNIS487LN44WUVnn9OgE9atTYUARNukT0DuQb3J-K20ksTuVujXbSelohDmLobqlGoi5sY_548Qs9BtFmQ2nGuEHNB2zdlZ5EvEqbUFVZ2QboG6jXdoos6qcwdgUvAhj1Hz10Ngic_RFqL7bNDoIOzNp66hdA35uxbwuaygZ16ikxoPj7eTYud1hrkyQCgeGw2YhCiKIE6eos_U5dL7WHRD5aSkkzsgXtnF8pVmStsuf0QcdAoC-eeCex0tSTgRw9AtGTz8Yr1tGQD9l_580zAXnE6jmrwRRQ68EEA7vohGov3tnG8pGyg_zcxeADLtPlfTc1tEwmh3SGrioDClioYCipm1JvkweEgP9eMPpEC8SgRU1VNDSVe1SF4uNsH8vA7PHFKfg6juqJEc5ht-l10FYER-Qq6bZXsU2oNcfE5SLDeLTWmxiHmxK00M8ABMFIV5gUkPoMiWcl87O6XwzA2chsIERp7Vb-Vn2O-EELiXzv7lPhc6fTGQ0Nc",
+ e: "AQAB",
+ d: "uXPRXBhcE5-DWabBRKQuhxgU8ype5gTISWefeYP7U96ZHqu_sBByZ5ihdgyU9pgAZGVx4Ep9rnVKnH2lNr2zrP9Qhyqy99nM0aMxmypIWLAuP__DwLj4t99M4sU29c48CAq1egHfccSFjzpNuetOTCA71EJuokt70pm0OmGzgTyvjuR7VTLxd5PMXitBowSn8_cphmnFpT8tkTiuy8CH0R3DU7MOuINomDD1s8-yPBcVAVTPUnwJiauNuzestLQKMLlhT5wn-cAbYk36XRKdgkjSc2AkhHRl4WDqT1nzWYdh_DVIYSLiKSktkPO9ovMrRYiPtozfhl0m9SR9Ll0wXtcnnDlWXc_MSGpw18vmUBSJ4PIhkiFsvLn-db3wUkA8uve-iqqfk0sxlGWughWx03kGmZDmprWbXugCBHfsI4X93w4exznXH_tapxPnmjbhVUQR6p41MvO2lcHWPLwGJgLIoejBHpnn3TmMN0UjFZki7q9B_dJ3fXh0mX9DzAlC0sil1NgCPhMPq02393_giinQquMknrBvgKxGSfGUrDKuflCx611ZZlRM3R7YMX2OIy1g4DyhPzBVjxRMtm8PnIs3m3Hi-O-C_PHF93w9J8Wqd0yIw7SpavDqZXLPC6Cqi8K7MBZyVECXHtRj1bBqT-h_xZmFCDjSU0NqfOdgApE",
+ p: "9NrXwq4kY9kBBOwLoFZVQc4kJI_NbKa_W9FLdQdRIbMsZZHXJ3XDUR9vJAcaaR75WwIC7X6N55nVtWTq28Bys9flJ9RrCTfciOntHEphBhYaL5ZTUl-6khYmsOf_psff2VaOOCvHGff5ejuOmBQxkw2E-cv7knRgWFHoLWpku2NJIMuGHt9ks7OAUfIZVYl9YJnw4FYUzhgaxemknjLeZ8XTkGW2zckzF-d95YI9i8zD80Umubsw-YxriSfqFQ0rGHBsbQ8ZOTd_KJju42BWnXIjNDYmjFUqdzVjI4XQ8EGrCEf_8_iwphGyXD7LOJ4fqd97B3bYpoRTPnCgY_SEHQ",
+ q: "5J758_NeKr1XPZiLxXohYQQnh0Lb4QtGZ1xzCgjhBQLcIBeTOG_tYjCues9tmLt93LpJfypSJ-SjDLwkR2s069_IByYGpxyeGtV-ulqYhSw1nD2CXKMDGyO5jXDs9tJrS_UhfobXKQH03CRdFugyPkSNmXY-AafFynG7xLr7oYBC05FnhUXPm3VBTPt9K-BpqwYd_h9vkAWeprSPo83UlwcLMupSJY9LaHxhRdz2yi0ZKNwXXHRwcszGjDBvvzUcCYbqWqjzbEvFY6KtH8Jh4LhM46rHaoEOTernJsDF6a6W8Df88RthqTExcwnaQf0O_dlbjSxEIPfbxx8t1EQugw",
+ dp: "4Y7Hu5tYAnLhMXuQqj9dgqU3PkcKYdCp7xc6f7Ah2P2JJHfYz4z4RD7Ez1eLyNKzulZ8A_PVHUjlSZiRkaYTBAEaJDrV70P6cFWuC6WpA0ZREQ1V7EgrQnANbGILa8QsPbYyhSQu4YlB1IwQq5_OmzyVBtgWA7AZIMMzMsMT0FuB_if-gWohBjmRN-vh0p45VUf6UW568-_YmgDFmMYbg1UFs7s_TwrNenPR0h7MO4CB8hP9vJLoZrooRczzIjljPbwy5bRG9CJfjTJ0vhj9MUT3kR1hHV1HJVGU5iBbfTfBKnvJGSI6-IDM4ZUm-B0R5hbs6s9cfOjhFmACIJIbMQ",
+ dq: "gT4iPbfyHyVEwWyQb4X4grjvg7bXSKSwG1SXMDAOzV9tg7LwJjKYNy8gJAtJgNNVdsfVLs-E_Epzpoph1AIWO9YZZXkov6Yc9zyEVONMX9S7ReU74hTBd8E9b2lMfMg9ogYk9jtSPTt-6kigW4fOh4cHqZ6_tP3cgfLD3JZ8FDPHE4WaySvLDq49yUBO5dQKyIU_xV6OGhQjOUjP_yEoMmzn9tOittsIHTxbXTxqQ6c1FvU9O6YTv8Jl5_Cl66khfX1I1RG38xvurcHULyUbYgeuZ_Iuo9XreT73h9_owo9RguGT29XH4vcNZmRGf5GIvRb4e5lvtleIZkwJA3u78w",
+ qi: "JHmVKb1zwW5iRR6RCeexYnh2fmY-3DrPSdM8Dxhr0F8dayi-tlRqEdnG0hvp45n8gLUskWWcB9EXlUJObZGKDfGuxgMa3g_xeLA2vmFQ12MxPsyH4iCNZvsgmGxx7TuOHrnDh5EBVnM4_de63crEJON2sYI8Ozi-xp2OEmAr2seWKq4sxkFni6exLhqb-NE4m9HMKlng1EtQh2rLBFG1VYD3SYYpMLc5fxzqGvSxn3Fa-Xgg-IZPY3ubrcm52KYgmLUGmnYStfVqGSWSdhDXHlNgI5pdAA0FzpyBk3ZX-JsxhwcnneKrYBBweq06kRMGWgvdbdAQ-7wSeGqqj5VPwA"
+ }
+ }
+};
+Deno.test(async function testImportRsaJwk() {
+ const subtle = window.crypto.subtle;
+ assert(subtle);
+ for (const [_key, jwkData] of Object.entries(jwtRSAKeys)){
+ const { size , publicJWK , privateJWK } = jwkData;
+ if (size < 2048) {
+ continue;
+ }
+ for (const hash of [
+ "SHA-1",
+ "SHA-256",
+ "SHA-384",
+ "SHA-512"
+ ]){
+ const hashMapPSS: Record<string, string> = {
+ "SHA-1": "PS1",
+ "SHA-256": "PS256",
+ "SHA-384": "PS384",
+ "SHA-512": "PS512"
+ };
+ if (size == 1024 && hash == "SHA-512") {
+ continue;
+ }
+ const privateKeyPSS = await crypto.subtle.importKey("jwk", {
+ alg: hashMapPSS[hash],
+ ...privateJWK,
+ ext: true,
+ "key_ops": [
+ "sign"
+ ]
+ }, {
+ name: "RSA-PSS",
+ hash
+ }, true, [
+ "sign"
+ ]);
+ const publicKeyPSS = await crypto.subtle.importKey("jwk", {
+ alg: hashMapPSS[hash],
+ ...publicJWK,
+ ext: true,
+ "key_ops": [
+ "verify"
+ ]
+ }, {
+ name: "RSA-PSS",
+ hash
+ }, true, [
+ "verify"
+ ]);
+ const signaturePSS = await crypto.subtle.sign({
+ name: "RSA-PSS",
+ saltLength: 32
+ }, privateKeyPSS, new Uint8Array([
+ 1,
+ 2,
+ 3,
+ 4
+ ]));
+ const verifyPSS = await crypto.subtle.verify({
+ name: "RSA-PSS",
+ saltLength: 32
+ }, publicKeyPSS, signaturePSS, new Uint8Array([
+ 1,
+ 2,
+ 3,
+ 4
+ ]));
+ assert(verifyPSS);
+ }
+ for (const hash of [
+ "SHA-1",
+ "SHA-256",
+ "SHA-384",
+ "SHA-512"
+ ]){
+ const hashMapPKCS1: Record<string, string> = {
+ "SHA-1": "RS1",
+ "SHA-256": "RS256",
+ "SHA-384": "RS384",
+ "SHA-512": "RS512"
+ };
+ if (size == 1024 && hash == "SHA-512") {
+ continue;
+ }
+ const privateKeyPKCS1 = await crypto.subtle.importKey("jwk", {
+ alg: hashMapPKCS1[hash],
+ ...privateJWK,
+ ext: true,
+ "key_ops": [
+ "sign"
+ ]
+ }, {
+ name: "RSASSA-PKCS1-v1_5",
+ hash
+ }, true, [
+ "sign"
+ ]);
+ const publicKeyPKCS1 = await crypto.subtle.importKey("jwk", {
+ alg: hashMapPKCS1[hash],
+ ...publicJWK,
+ ext: true,
+ "key_ops": [
+ "verify"
+ ]
+ }, {
+ name: "RSASSA-PKCS1-v1_5",
+ hash
+ }, true, [
+ "verify"
+ ]);
+ const signaturePKCS1 = await crypto.subtle.sign({
+ name: "RSASSA-PKCS1-v1_5",
+ saltLength: 32
+ }, privateKeyPKCS1, new Uint8Array([
+ 1,
+ 2,
+ 3,
+ 4
+ ]));
+ const verifyPKCS1 = await crypto.subtle.verify({
+ name: "RSASSA-PKCS1-v1_5",
+ saltLength: 32
+ }, publicKeyPKCS1, signaturePKCS1, new Uint8Array([
+ 1,
+ 2,
+ 3,
+ 4
+ ]));
+ assert(verifyPKCS1);
+ }
+ for (const { hash , plainText } of hashPlainTextVector){
+ const hashMapOAEP: Record<string, string> = {
+ "SHA-1": "RSA-OAEP",
+ "SHA-256": "RSA-OAEP-256",
+ "SHA-384": "RSA-OAEP-384",
+ "SHA-512": "RSA-OAEP-512"
+ };
+ if (size == 1024 && hash == "SHA-512") {
+ continue;
+ }
+ const encryptAlgorithm = {
+ name: "RSA-OAEP"
+ };
+ const privateKeyOAEP = await crypto.subtle.importKey("jwk", {
+ alg: hashMapOAEP[hash],
+ ...privateJWK,
+ ext: true,
+ "key_ops": [
+ "decrypt"
+ ]
+ }, {
+ ...encryptAlgorithm,
+ hash
+ }, true, [
+ "decrypt"
+ ]);
+ const publicKeyOAEP = await crypto.subtle.importKey("jwk", {
+ alg: hashMapOAEP[hash],
+ ...publicJWK,
+ ext: true,
+ "key_ops": [
+ "encrypt"
+ ]
+ }, {
+ ...encryptAlgorithm,
+ hash
+ }, true, [
+ "encrypt"
+ ]);
+ const cipherText = await subtle.encrypt(encryptAlgorithm, publicKeyOAEP, plainText);
+ assert(cipherText);
+ assert(cipherText.byteLength > 0);
+ assertEquals(cipherText.byteLength * 8, size);
+ assert(cipherText instanceof ArrayBuffer);
+ const decrypted = await subtle.decrypt(encryptAlgorithm, privateKeyOAEP, cipherText);
+ assert(decrypted);
+ assert(decrypted instanceof ArrayBuffer);
+ assertEquals(new Uint8Array(decrypted), plainText);
+ }
+ }
+});
+const jwtECKeys = {
+ "256": {
+ size: 256,
+ algo: "ES256",
+ publicJWK: {
+ kty: "EC",
+ crv: "P-256",
+ x: "0hCwpvnZ8BKGgFi0P6T0cQGFQ7ugDJJQ35JXwqyuXdE",
+ y: "zgN1UtSBRQzjm00QlXAbF1v6s0uObAmeGPHBmDWDYeg"
+ },
+ privateJWK: {
+ kty: "EC",
+ crv: "P-256",
+ x: "0hCwpvnZ8BKGgFi0P6T0cQGFQ7ugDJJQ35JXwqyuXdE",
+ y: "zgN1UtSBRQzjm00QlXAbF1v6s0uObAmeGPHBmDWDYeg",
+ d: "E9M6LVq_nPnrsh_4YNSu_m5W53eQ9N7ptAiE69M1ROo"
+ }
+ },
+ "384": {
+ size: 384,
+ algo: "ES384",
+ publicJWK: {
+ kty: "EC",
+ crv: "P-384",
+ x: "IZwU1mYXs27G2IVrOFtzp000T9iude8EZDXdpU47RL1fvevR0I3Wni19wdwhjLQ1",
+ y: "vSgTjMd4M3qEL2vWGyQOdCSfJGZ8KlgQp2v8KOAzX4imUB3sAZdtqFr7AIactqzo"
+ },
+ privateJWK: {
+ kty: "EC",
+ crv: "P-384",
+ x: "IZwU1mYXs27G2IVrOFtzp000T9iude8EZDXdpU47RL1fvevR0I3Wni19wdwhjLQ1",
+ y: "vSgTjMd4M3qEL2vWGyQOdCSfJGZ8KlgQp2v8KOAzX4imUB3sAZdtqFr7AIactqzo",
+ d: "RTe1mQeE08LSLpao-S-hqkku6HPldqQVguFEGDyYiNEOa560ztSyzEAS5KxeqEBz"
+ }
+ }
+};
+type JWK = Record<string, string>;
+function equalJwk(expected: JWK, got: JWK): boolean {
+ const fields = Object.keys(expected);
+ for(let i = 0; i < fields.length; i++){
+ const fieldName = fields[i];
+ if (!(fieldName in got)) {
+ return false;
+ }
+ if (expected[fieldName] !== got[fieldName]) {
+ return false;
+ }
+ }
+ return true;
+}
+Deno.test(async function testImportExportEcDsaJwk() {
+ const subtle = crypto.subtle;
+ assert(subtle);
+ for (const [_key, keyData] of Object.entries(jwtECKeys)){
+ const { publicJWK , privateJWK , algo } = keyData;
+ const privateKeyECDSA = await subtle.importKey("jwk", {
+ alg: algo,
+ ...privateJWK,
+ ext: true,
+ "key_ops": [
+ "sign"
+ ]
+ }, {
+ name: "ECDSA",
+ namedCurve: privateJWK.crv
+ }, true, [
+ "sign"
+ ]);
+ const expPrivateKeyJWK = await subtle.exportKey("jwk", privateKeyECDSA);
+ assert(equalJwk(privateJWK, expPrivateKeyJWK as JWK));
+ const publicKeyECDSA = await subtle.importKey("jwk", {
+ alg: algo,
+ ...publicJWK,
+ ext: true,
+ "key_ops": [
+ "verify"
+ ]
+ }, {
+ name: "ECDSA",
+ namedCurve: publicJWK.crv
+ }, true, [
+ "verify"
+ ]);
+ const expPublicKeyJWK = await subtle.exportKey("jwk", publicKeyECDSA);
+ assert(equalJwk(publicJWK, expPublicKeyJWK as JWK));
+ const signatureECDSA = await subtle.sign({
+ name: "ECDSA",
+ hash: `SHA-${keyData.size}`
+ }, privateKeyECDSA, new Uint8Array([
+ 1,
+ 2,
+ 3,
+ 4
+ ]));
+ const verifyECDSA = await subtle.verify({
+ name: "ECDSA",
+ hash: `SHA-${keyData.size}`
+ }, publicKeyECDSA, signatureECDSA, new Uint8Array([
+ 1,
+ 2,
+ 3,
+ 4
+ ]));
+ assert(verifyECDSA);
+ }
+});
+Deno.test(async function testImportEcDhJwk() {
+ const subtle = crypto.subtle;
+ assert(subtle);
+ for (const [_key, jwkData] of Object.entries(jwtECKeys)){
+ const { size , publicJWK , privateJWK } = jwkData;
+ const privateKeyECDH = await subtle.importKey("jwk", {
+ ...privateJWK,
+ ext: true,
+ "key_ops": [
+ "deriveBits"
+ ]
+ }, {
+ name: "ECDH",
+ namedCurve: privateJWK.crv
+ }, true, [
+ "deriveBits"
+ ]);
+ const expPrivateKeyJWK = await subtle.exportKey("jwk", privateKeyECDH);
+ assert(equalJwk(privateJWK, expPrivateKeyJWK as JWK));
+ const publicKeyECDH = await subtle.importKey("jwk", {
+ ...publicJWK,
+ ext: true,
+ "key_ops": []
+ }, {
+ name: "ECDH",
+ namedCurve: publicJWK.crv
+ }, true, []);
+ const expPublicKeyJWK = await subtle.exportKey("jwk", publicKeyECDH);
+ assert(equalJwk(publicJWK, expPublicKeyJWK as JWK));
+ const derivedKey = await subtle.deriveBits({
+ name: "ECDH",
+ public: publicKeyECDH
+ }, privateKeyECDH, size);
+ assert(derivedKey instanceof ArrayBuffer);
+ assertEquals(derivedKey.byteLength, size / 8);
+ }
+});
+const ecTestKeys = [
+ {
+ size: 256,
+ namedCurve: "P-256",
+ signatureLength: 64,
+ raw: new Uint8Array([
+ 4,
+ 210,
+ 16,
+ 176,
+ 166,
+ 249,
+ 217,
+ 240,
+ 18,
+ 134,
+ 128,
+ 88,
+ 180,
+ 63,
+ 164,
+ 244,
+ 113,
+ 1,
+ 133,
+ 67,
+ 187,
+ 160,
+ 12,
+ 146,
+ 80,
+ 223,
+ 146,
+ 87,
+ 194,
+ 172,
+ 174,
+ 93,
+ 209,
+ 206,
+ 3,
+ 117,
+ 82,
+ 212,
+ 129,
+ 69,
+ 12,
+ 227,
+ 155,
+ 77,
+ 16,
+ 149,
+ 112,
+ 27,
+ 23,
+ 91,
+ 250,
+ 179,
+ 75,
+ 142,
+ 108,
+ 9,
+ 158,
+ 24,
+ 241,
+ 193,
+ 152,
+ 53,
+ 131,
+ 97,
+ 232
+ ]),
+ spki: new Uint8Array([
+ 48,
+ 89,
+ 48,
+ 19,
+ 6,
+ 7,
+ 42,
+ 134,
+ 72,
+ 206,
+ 61,
+ 2,
+ 1,
+ 6,
+ 8,
+ 42,
+ 134,
+ 72,
+ 206,
+ 61,
+ 3,
+ 1,
+ 7,
+ 3,
+ 66,
+ 0,
+ 4,
+ 210,
+ 16,
+ 176,
+ 166,
+ 249,
+ 217,
+ 240,
+ 18,
+ 134,
+ 128,
+ 88,
+ 180,
+ 63,
+ 164,
+ 244,
+ 113,
+ 1,
+ 133,
+ 67,
+ 187,
+ 160,
+ 12,
+ 146,
+ 80,
+ 223,
+ 146,
+ 87,
+ 194,
+ 172,
+ 174,
+ 93,
+ 209,
+ 206,
+ 3,
+ 117,
+ 82,
+ 212,
+ 129,
+ 69,
+ 12,
+ 227,
+ 155,
+ 77,
+ 16,
+ 149,
+ 112,
+ 27,
+ 23,
+ 91,
+ 250,
+ 179,
+ 75,
+ 142,
+ 108,
+ 9,
+ 158,
+ 24,
+ 241,
+ 193,
+ 152,
+ 53,
+ 131,
+ 97,
+ 232
+ ]),
+ pkcs8: new Uint8Array([
+ 48,
+ 129,
+ 135,
+ 2,
+ 1,
+ 0,
+ 48,
+ 19,
+ 6,
+ 7,
+ 42,
+ 134,
+ 72,
+ 206,
+ 61,
+ 2,
+ 1,
+ 6,
+ 8,
+ 42,
+ 134,
+ 72,
+ 206,
+ 61,
+ 3,
+ 1,
+ 7,
+ 4,
+ 109,
+ 48,
+ 107,
+ 2,
+ 1,
+ 1,
+ 4,
+ 32,
+ 19,
+ 211,
+ 58,
+ 45,
+ 90,
+ 191,
+ 156,
+ 249,
+ 235,
+ 178,
+ 31,
+ 248,
+ 96,
+ 212,
+ 174,
+ 254,
+ 110,
+ 86,
+ 231,
+ 119,
+ 144,
+ 244,
+ 222,
+ 233,
+ 180,
+ 8,
+ 132,
+ 235,
+ 211,
+ 53,
+ 68,
+ 234,
+ 161,
+ 68,
+ 3,
+ 66,
+ 0,
+ 4,
+ 210,
+ 16,
+ 176,
+ 166,
+ 249,
+ 217,
+ 240,
+ 18,
+ 134,
+ 128,
+ 88,
+ 180,
+ 63,
+ 164,
+ 244,
+ 113,
+ 1,
+ 133,
+ 67,
+ 187,
+ 160,
+ 12,
+ 146,
+ 80,
+ 223,
+ 146,
+ 87,
+ 194,
+ 172,
+ 174,
+ 93,
+ 209,
+ 206,
+ 3,
+ 117,
+ 82,
+ 212,
+ 129,
+ 69,
+ 12,
+ 227,
+ 155,
+ 77,
+ 16,
+ 149,
+ 112,
+ 27,
+ 23,
+ 91,
+ 250,
+ 179,
+ 75,
+ 142,
+ 108,
+ 9,
+ 158,
+ 24,
+ 241,
+ 193,
+ 152,
+ 53,
+ 131,
+ 97,
+ 232
+ ])
+ },
+ {
+ size: 384,
+ namedCurve: "P-384",
+ signatureLength: 96,
+ raw: new Uint8Array([
+ 4,
+ 118,
+ 64,
+ 176,
+ 165,
+ 100,
+ 177,
+ 112,
+ 49,
+ 254,
+ 58,
+ 53,
+ 158,
+ 63,
+ 73,
+ 200,
+ 148,
+ 248,
+ 242,
+ 216,
+ 186,
+ 80,
+ 92,
+ 160,
+ 53,
+ 64,
+ 232,
+ 157,
+ 19,
+ 1,
+ 12,
+ 226,
+ 115,
+ 51,
+ 42,
+ 143,
+ 98,
+ 206,
+ 55,
+ 220,
+ 108,
+ 78,
+ 24,
+ 71,
+ 157,
+ 21,
+ 120,
+ 126,
+ 104,
+ 157,
+ 86,
+ 48,
+ 226,
+ 110,
+ 96,
+ 52,
+ 48,
+ 77,
+ 170,
+ 9,
+ 231,
+ 159,
+ 26,
+ 165,
+ 200,
+ 26,
+ 164,
+ 99,
+ 46,
+ 227,
+ 169,
+ 105,
+ 172,
+ 225,
+ 60,
+ 102,
+ 141,
+ 145,
+ 139,
+ 165,
+ 47,
+ 72,
+ 53,
+ 17,
+ 17,
+ 246,
+ 161,
+ 220,
+ 26,
+ 21,
+ 23,
+ 219,
+ 1,
+ 107,
+ 185,
+ 163,
+ 215
+ ]),
+ spki: new Uint8Array([
+ 48,
+ 118,
+ 48,
+ 16,
+ 6,
+ 7,
+ 42,
+ 134,
+ 72,
+ 206,
+ 61,
+ 2,
+ 1,
+ 6,
+ 5,
+ 43,
+ 129,
+ 4,
+ 0,
+ 34,
+ 3,
+ 98,
+ 0,
+ 4,
+ 118,
+ 64,
+ 176,
+ 165,
+ 100,
+ 177,
+ 112,
+ 49,
+ 254,
+ 58,
+ 53,
+ 158,
+ 63,
+ 73,
+ 200,
+ 148,
+ 248,
+ 242,
+ 216,
+ 186,
+ 80,
+ 92,
+ 160,
+ 53,
+ 64,
+ 232,
+ 157,
+ 19,
+ 1,
+ 12,
+ 226,
+ 115,
+ 51,
+ 42,
+ 143,
+ 98,
+ 206,
+ 55,
+ 220,
+ 108,
+ 78,
+ 24,
+ 71,
+ 157,
+ 21,
+ 120,
+ 126,
+ 104,
+ 157,
+ 86,
+ 48,
+ 226,
+ 110,
+ 96,
+ 52,
+ 48,
+ 77,
+ 170,
+ 9,
+ 231,
+ 159,
+ 26,
+ 165,
+ 200,
+ 26,
+ 164,
+ 99,
+ 46,
+ 227,
+ 169,
+ 105,
+ 172,
+ 225,
+ 60,
+ 102,
+ 141,
+ 145,
+ 139,
+ 165,
+ 47,
+ 72,
+ 53,
+ 17,
+ 17,
+ 246,
+ 161,
+ 220,
+ 26,
+ 21,
+ 23,
+ 219,
+ 1,
+ 107,
+ 185,
+ 163,
+ 215
+ ]),
+ pkcs8: new Uint8Array([
+ 48,
+ 129,
+ 182,
+ 2,
+ 1,
+ 0,
+ 48,
+ 16,
+ 6,
+ 7,
+ 42,
+ 134,
+ 72,
+ 206,
+ 61,
+ 2,
+ 1,
+ 6,
+ 5,
+ 43,
+ 129,
+ 4,
+ 0,
+ 34,
+ 4,
+ 129,
+ 158,
+ 48,
+ 129,
+ 155,
+ 2,
+ 1,
+ 1,
+ 4,
+ 48,
+ 202,
+ 7,
+ 195,
+ 169,
+ 124,
+ 170,
+ 81,
+ 169,
+ 253,
+ 127,
+ 56,
+ 28,
+ 98,
+ 90,
+ 255,
+ 165,
+ 72,
+ 142,
+ 133,
+ 138,
+ 237,
+ 200,
+ 176,
+ 92,
+ 179,
+ 192,
+ 83,
+ 28,
+ 47,
+ 118,
+ 157,
+ 152,
+ 47,
+ 65,
+ 133,
+ 140,
+ 50,
+ 83,
+ 182,
+ 191,
+ 224,
+ 96,
+ 216,
+ 179,
+ 59,
+ 150,
+ 15,
+ 233,
+ 161,
+ 100,
+ 3,
+ 98,
+ 0,
+ 4,
+ 118,
+ 64,
+ 176,
+ 165,
+ 100,
+ 177,
+ 112,
+ 49,
+ 254,
+ 58,
+ 53,
+ 158,
+ 63,
+ 73,
+ 200,
+ 148,
+ 248,
+ 242,
+ 216,
+ 186,
+ 80,
+ 92,
+ 160,
+ 53,
+ 64,
+ 232,
+ 157,
+ 19,
+ 1,
+ 12,
+ 226,
+ 115,
+ 51,
+ 42,
+ 143,
+ 98,
+ 206,
+ 55,
+ 220,
+ 108,
+ 78,
+ 24,
+ 71,
+ 157,
+ 21,
+ 120,
+ 126,
+ 104,
+ 157,
+ 86,
+ 48,
+ 226,
+ 110,
+ 96,
+ 52,
+ 48,
+ 77,
+ 170,
+ 9,
+ 231,
+ 159,
+ 26,
+ 165,
+ 200,
+ 26,
+ 164,
+ 99,
+ 46,
+ 227,
+ 169,
+ 105,
+ 172,
+ 225,
+ 60,
+ 102,
+ 141,
+ 145,
+ 139,
+ 165,
+ 47,
+ 72,
+ 53,
+ 17,
+ 17,
+ 246,
+ 161,
+ 220,
+ 26,
+ 21,
+ 23,
+ 219,
+ 1,
+ 107,
+ 185,
+ 163,
+ 215
+ ])
+ }
+];
+Deno.test(async function testImportEcSpkiPkcs8() {
+ const subtle = window.crypto.subtle;
+ assert(subtle);
+ for (const { namedCurve , raw , spki , pkcs8 , signatureLength } of ecTestKeys){
+ const rawPublicKeyECDSA = await subtle.importKey("raw", raw, {
+ name: "ECDSA",
+ namedCurve
+ }, true, [
+ "verify"
+ ]);
+ const expPublicKeyRaw = await subtle.exportKey("raw", rawPublicKeyECDSA);
+ assertEquals(new Uint8Array(expPublicKeyRaw), raw);
+ const privateKeyECDSA = await subtle.importKey("pkcs8", pkcs8, {
+ name: "ECDSA",
+ namedCurve
+ }, true, [
+ "sign"
+ ]);
+ const expPrivateKeyPKCS8 = await subtle.exportKey("pkcs8", privateKeyECDSA);
+ assertEquals(new Uint8Array(expPrivateKeyPKCS8), pkcs8);
+ const expPrivateKeyJWK = await subtle.exportKey("jwk", privateKeyECDSA);
+ assertEquals(expPrivateKeyJWK.crv, namedCurve);
+ const publicKeyECDSA = await subtle.importKey("spki", spki, {
+ name: "ECDSA",
+ namedCurve
+ }, true, [
+ "verify"
+ ]);
+ const expPublicKeySPKI = await subtle.exportKey("spki", publicKeyECDSA);
+ assertEquals(new Uint8Array(expPublicKeySPKI), spki);
+ const expPublicKeyJWK = await subtle.exportKey("jwk", publicKeyECDSA);
+ assertEquals(expPublicKeyJWK.crv, namedCurve);
+ for (const hash of [
+ "SHA-1",
+ "SHA-256",
+ "SHA-384",
+ "SHA-512"
+ ]){
+ if ((hash == "SHA-256" && namedCurve == "P-256") || (hash == "SHA-384" && namedCurve == "P-384")) {
+ const signatureECDSA = await subtle.sign({
+ name: "ECDSA",
+ hash
+ }, privateKeyECDSA, new Uint8Array([
+ 1,
+ 2,
+ 3,
+ 4
+ ]));
+ const verifyECDSA = await subtle.verify({
+ name: "ECDSA",
+ hash
+ }, publicKeyECDSA, signatureECDSA, new Uint8Array([
+ 1,
+ 2,
+ 3,
+ 4
+ ]));
+ assert(verifyECDSA);
+ } else {
+ await assertRejects(async ()=>{
+ await subtle.sign({
+ name: "ECDSA",
+ hash
+ }, privateKeyECDSA, new Uint8Array([
+ 1,
+ 2,
+ 3,
+ 4
+ ]));
+ }, DOMException, "Not implemented");
+ await assertRejects(async ()=>{
+ await subtle.verify({
+ name: "ECDSA",
+ hash
+ }, publicKeyECDSA, new Uint8Array(signatureLength), new Uint8Array([
+ 1,
+ 2,
+ 3,
+ 4
+ ]));
+ }, DOMException, "Not implemented");
+ }
+ }
+ }
+});
+Deno.test(async function testAesGcmEncrypt() {
+ const key = await crypto.subtle.importKey("raw", new Uint8Array(16), {
+ name: "AES-GCM",
+ length: 256
+ }, true, [
+ "encrypt",
+ "decrypt"
+ ]);
+ const nonces = [
+ {
+ iv: new Uint8Array([
+ 0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11
+ ]),
+ ciphertext: new Uint8Array([
+ 50,
+ 223,
+ 112,
+ 178,
+ 166,
+ 156,
+ 255,
+ 110,
+ 125,
+ 138,
+ 95,
+ 141,
+ 82,
+ 47,
+ 14,
+ 164,
+ 134,
+ 247,
+ 22
+ ])
+ },
+ {
+ iv: new Uint8Array([
+ 0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15
+ ]),
+ ciphertext: new Uint8Array([
+ 210,
+ 101,
+ 81,
+ 216,
+ 151,
+ 9,
+ 192,
+ 197,
+ 62,
+ 254,
+ 28,
+ 132,
+ 89,
+ 106,
+ 40,
+ 29,
+ 175,
+ 232,
+ 201
+ ])
+ }
+ ];
+ for (const { iv , ciphertext: fixture } of nonces){
+ const data = new Uint8Array([
+ 1,
+ 2,
+ 3
+ ]);
+ const cipherText = await crypto.subtle.encrypt({
+ name: "AES-GCM",
+ iv
+ }, key, data);
+ assert(cipherText instanceof ArrayBuffer);
+ assertEquals(cipherText.byteLength, 19);
+ assertEquals(new Uint8Array(cipherText), fixture);
+ const plainText = await crypto.subtle.decrypt({
+ name: "AES-GCM",
+ iv
+ }, key, cipherText);
+ assert(plainText instanceof ArrayBuffer);
+ assertEquals(plainText.byteLength, 3);
+ assertEquals(new Uint8Array(plainText), data);
+ }
+});
+async function roundTripSecretJwk(jwk: JsonWebKey, algId: AlgorithmIdentifier | HmacImportParams, ops: KeyUsage[], validateKeys: (key: CryptoKey, originalJwk: JsonWebKey, exportedJwk: JsonWebKey) => void) {
+ const key = await crypto.subtle.importKey("jwk", jwk, algId, true, ops);
+ assert(key instanceof CryptoKey);
+ assertEquals(key.type, "secret");
+ const exportedKey = await crypto.subtle.exportKey("jwk", key);
+ validateKeys(key, jwk, exportedKey);
+}
+Deno.test(async function testSecretJwkBase64Url() {
+ const keyData = `{
+ "kty": "oct",
+ "k": "xxx",
+ "alg": "HS512",
+ "key_ops": ["sign", "verify"],
+ "ext": true
+ }`;
+ await roundTripSecretJwk(JSON.parse(keyData), {
+ name: "HMAC",
+ hash: "SHA-512"
+ }, [
+ "sign",
+ "verify"
+ ], (key, _orig, exp)=>{
+ assertEquals((key.algorithm as HmacKeyAlgorithm).length, 16);
+ assertEquals(exp.k, "xxw");
+ });
+ await roundTripSecretJwk({
+ kty: "oct",
+ k: "HnZXRyDKn-_G5Fx4JWR1YA",
+ alg: "HS256",
+ "key_ops": [
+ "sign",
+ "verify"
+ ],
+ ext: true
+ }, {
+ name: "HMAC",
+ hash: "SHA-256"
+ }, [
+ "sign",
+ "verify"
+ ], (key, orig, exp)=>{
+ assertEquals((key.algorithm as HmacKeyAlgorithm).length, 128);
+ assertEquals(orig.k, exp.k);
+ });
+ await roundTripSecretJwk({
+ kty: "oct",
+ k: "a-_AlFa-2-OmEGa_-z==",
+ alg: "HS384",
+ "key_ops": [
+ "sign",
+ "verify"
+ ],
+ ext: true
+ }, {
+ name: "HMAC",
+ hash: "SHA-384"
+ }, [
+ "sign",
+ "verify"
+ ], (key, _orig, exp)=>{
+ assertEquals((key.algorithm as HmacKeyAlgorithm).length, 104);
+ assertEquals("a-_AlFa-2-OmEGa_-w", exp.k);
+ });
+ await roundTripSecretJwk({
+ kty: "oct",
+ k: "_u3K_gEjRWf-7cr-ASNFZw",
+ alg: "A128CBC",
+ "key_ops": [
+ "encrypt",
+ "decrypt"
+ ],
+ ext: true
+ }, {
+ name: "AES-CBC"
+ }, [
+ "encrypt",
+ "decrypt"
+ ], (_key, orig, exp)=>{
+ assertEquals(orig.k, exp.k);
+ });
+ await roundTripSecretJwk({
+ kty: "oct",
+ k: "_____________________w==",
+ alg: "A128CBC",
+ "key_ops": [
+ "encrypt",
+ "decrypt"
+ ],
+ ext: true
+ }, {
+ name: "AES-CBC"
+ }, [
+ "encrypt",
+ "decrypt"
+ ], (_key, _orig, exp)=>{
+ assertEquals(exp.k, "_____________________w");
+ });
+});
+Deno.test(async function testAESWrapKey() {
+ const key = await crypto.subtle.generateKey({
+ name: "AES-KW",
+ length: 128
+ }, true, [
+ "wrapKey",
+ "unwrapKey"
+ ]);
+ const hmacKey = await crypto.subtle.generateKey({
+ name: "HMAC",
+ hash: "SHA-256",
+ length: 128
+ }, true, [
+ "sign"
+ ]);
+ const wrappedKey = await crypto.subtle.wrapKey("raw", hmacKey, key, {
+ name: "AES-KW"
+ });
+ assert(wrappedKey instanceof ArrayBuffer);
+ assertEquals(wrappedKey.byteLength, 16 + 8);
+ const unwrappedKey = await crypto.subtle.unwrapKey("raw", wrappedKey, key, {
+ name: "AES-KW"
+ }, {
+ name: "HMAC",
+ hash: "SHA-256"
+ }, true, [
+ "sign"
+ ]);
+ assert(unwrappedKey instanceof CryptoKey);
+ assertEquals((unwrappedKey.algorithm as HmacKeyAlgorithm).length, 128);
+ const hmacKeyBytes = await crypto.subtle.exportKey("raw", hmacKey);
+ const unwrappedKeyBytes = await crypto.subtle.exportKey("raw", unwrappedKey);
+ assertEquals(new Uint8Array(hmacKeyBytes), new Uint8Array(unwrappedKeyBytes));
+});
+Deno.test(async function testAesGcmTagLength() {
+ const key = await crypto.subtle.importKey("raw", new Uint8Array(32), "AES-GCM", false, [
+ "encrypt",
+ "decrypt"
+ ]);
+ const iv = crypto.getRandomValues(new Uint8Array(12));
+ const encrypted = await crypto.subtle.encrypt({
+ name: "AES-GCM",
+ iv,
+ tagLength: 96
+ }, key, new Uint8Array(32));
+ await assertRejects(async ()=>{
+ await crypto.subtle.decrypt({
+ name: "AES-GCM",
+ iv,
+ tagLength: 96
+ }, key, encrypted);
+ });
+});
+Deno.test(async function ecPrivateKeyMaterialExportSpki() {
+ const keys = await crypto.subtle.generateKey({
+ name: "ECDSA",
+ namedCurve: "P-256"
+ }, true, [
+ "sign",
+ "verify"
+ ]);
+ assert(keys.privateKey instanceof CryptoKey);
+ assert(keys.publicKey instanceof CryptoKey);
+ const spki = await crypto.subtle.exportKey("spki", keys.publicKey);
+ assert(spki instanceof ArrayBuffer);
+});
+Deno.test(async function importJwkWithUse() {
+ const jwk = {
+ "kty": "EC",
+ "use": "sig",
+ "crv": "P-256",
+ "x": "FWZ9rSkLt6Dx9E3pxLybhdM6xgR5obGsj5_pqmnz5J4",
+ "y": "_n8G69C-A2Xl4xUW2lF0i8ZGZnk_KPYrhv4GbTGu5G4"
+ };
+ const algorithm = {
+ name: "ECDSA",
+ namedCurve: "P-256"
+ };
+ const key = await crypto.subtle.importKey("jwk", jwk, algorithm, true, [
+ "verify"
+ ]);
+ assert(key instanceof CryptoKey);
+});
+Deno.test(async function exportKeyNotExtractable() {
+ const key = await crypto.subtle.generateKey({
+ name: "HMAC",
+ hash: "SHA-512"
+ }, false, [
+ "sign",
+ "verify"
+ ]);
+ assert(key);
+ assertEquals(key.extractable, false);
+ await assertRejects(async ()=>{
+ await crypto.subtle.exportKey("raw", key);
+ }, DOMException);
+});
+Deno.test(async function testImportLeadingZeroesKey() {
+ const alg = {
+ name: "ECDSA",
+ namedCurve: "P-256"
+ };
+ const jwk = {
+ kty: "EC",
+ crv: "P-256",
+ alg: "ES256",
+ x: "EvidcdFB1xC6tgfakqZsU9aIURxAJkcX62zHe1Nt6xU",
+ y: "AHsk6BioGM7MZWeXOE_49AGmtuaXFT3Ill3DYtz9uYg",
+ d: "WDeYo4o1heCF9l_2VIaClRyIeO16zsMlN8UG6Le9dU8",
+ "key_ops": [
+ "sign"
+ ],
+ ext: true
+ };
+ const key = await crypto.subtle.importKey("jwk", jwk, alg, true, [
+ "sign"
+ ]);
+ assert(key instanceof CryptoKey);
+ assertEquals(key.type, "private");
+});
+Deno.test(async function testECspkiRoundTrip() {
+ const alg = {
+ name: "ECDH",
+ namedCurve: "P-256"
+ };
+ const { publicKey } = await crypto.subtle.generateKey(alg, true, [
+ "deriveBits"
+ ]);
+ const spki = await crypto.subtle.exportKey("spki", publicKey);
+ await crypto.subtle.importKey("spki", spki, alg, true, []);
+});
+Deno.test(async function testHmacJwkImport() {
+ await crypto.subtle.importKey("jwk", {
+ kty: "oct",
+ use: "sig",
+ alg: "HS256",
+ k: "hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg"
+ }, {
+ name: "HMAC",
+ hash: "SHA-256"
+ }, false, [
+ "sign",
+ "verify"
+ ]);
+});
diff --git a/test/js/deno/encoding/encoding.test.ts b/test/js/deno/encoding/encoding.test.ts
new file mode 100644
index 000000000..18700eddb
--- /dev/null
+++ b/test/js/deno/encoding/encoding.test.ts
@@ -0,0 +1,343 @@
+// Copyright 2018+ the Deno authors. All rights reserved. MIT license.
+// https://raw.githubusercontent.com/denoland/deno/main/cli/tests/unit/text_encoding_test.ts
+import { assert, assertEquals, assertThrows } from "deno:harness";
+Deno.test(function btoaSuccess() {
+ const text = "hello world";
+ const encoded = btoa(text);
+ assertEquals(encoded, "aGVsbG8gd29ybGQ=");
+});
+Deno.test(function atobSuccess() {
+ const encoded = "aGVsbG8gd29ybGQ=";
+ const decoded = atob(encoded);
+ assertEquals(decoded, "hello world");
+});
+Deno.test(function atobWithAsciiWhitespace() {
+ const encodedList = [
+ " aGVsbG8gd29ybGQ=",
+ " aGVsbG8gd29ybGQ=",
+ "aGVsbG8gd29ybGQ= ",
+ "aGVsbG8gd29ybGQ=\n",
+ "aGVsbG\t8gd29ybGQ=",
+ `aGVsbG\t8g
+ d29ybGQ=`
+ ];
+ for (const encoded of encodedList){
+ const decoded = atob(encoded);
+ assertEquals(decoded, "hello world");
+ }
+});
+Deno.test(function atobThrows() {
+ let threw = false;
+ try {
+ atob("aGVsbG8gd29ybGQ==");
+ } catch (_e) {
+ threw = true;
+ }
+ assert(threw);
+});
+Deno.test(function atobThrows2() {
+ let threw = false;
+ try {
+ atob("aGVsbG8gd29ybGQ===");
+ } catch (_e) {
+ threw = true;
+ }
+ assert(threw);
+});
+Deno.test(function atobThrows3() {
+ let threw = false;
+ try {
+ atob("foobar!!");
+ } catch (e) {
+ if (e instanceof DOMException && e.toString().startsWith("InvalidCharacterError:")) {
+ threw = true;
+ }
+ }
+ assert(threw);
+});
+Deno.test(function btoaFailed() {
+ const text = "你好";
+ assertThrows(()=>{
+ btoa(text);
+ }, DOMException);
+});
+Deno.test(function textDecoder2() {
+ const fixture = new Uint8Array([
+ 0xf0,
+ 0x9d,
+ 0x93,
+ 0xbd,
+ 0xf0,
+ 0x9d,
+ 0x93,
+ 0xae,
+ 0xf0,
+ 0x9d,
+ 0x94,
+ 0x81,
+ 0xf0,
+ 0x9d,
+ 0x93,
+ 0xbd
+ ]);
+ const decoder = new TextDecoder();
+ assertEquals(decoder.decode(fixture), "𝓽𝓮𝔁𝓽");
+});
+Deno.test(function textDecoderASCII() {
+ const fixture = new Uint8Array([
+ 0x89,
+ 0x95,
+ 0x9f,
+ 0xbf
+ ]);
+ const decoder = new TextDecoder("ascii");
+ assertEquals(decoder.decode(fixture), "‰•Ÿ¿");
+});
+Deno.test(function textDecoderErrorEncoding() {
+ let didThrow = false;
+ try {
+ new TextDecoder("Foo");
+ } catch (e) {
+ didThrow = true;
+ assert(e instanceof Error);
+ assertEquals(e.message, "The encoding label provided ('Foo') is invalid.");
+ }
+ assert(didThrow);
+});
+Deno.test(function textEncoder() {
+ const fixture = "𝓽𝓮𝔁𝓽";
+ const encoder = new TextEncoder();
+ assertEquals(Array.from(encoder.encode(fixture)), [
+ 0xf0,
+ 0x9d,
+ 0x93,
+ 0xbd,
+ 0xf0,
+ 0x9d,
+ 0x93,
+ 0xae,
+ 0xf0,
+ 0x9d,
+ 0x94,
+ 0x81,
+ 0xf0,
+ 0x9d,
+ 0x93,
+ 0xbd
+ ]);
+});
+Deno.test(function textEncodeInto() {
+ const fixture = "text";
+ const encoder = new TextEncoder();
+ const bytes = new Uint8Array(5);
+ const result = encoder.encodeInto(fixture, bytes);
+ assertEquals(result.read, 4);
+ assertEquals(result.written, 4);
+ assertEquals(Array.from(bytes), [
+ 0x74,
+ 0x65,
+ 0x78,
+ 0x74,
+ 0x00
+ ]);
+});
+Deno.test(function textEncodeInto2() {
+ const fixture = "𝓽𝓮𝔁𝓽";
+ const encoder = new TextEncoder();
+ const bytes = new Uint8Array(17);
+ const result = encoder.encodeInto(fixture, bytes);
+ assertEquals(result.read, 8);
+ assertEquals(result.written, 16);
+ assertEquals(Array.from(bytes), [
+ 0xf0,
+ 0x9d,
+ 0x93,
+ 0xbd,
+ 0xf0,
+ 0x9d,
+ 0x93,
+ 0xae,
+ 0xf0,
+ 0x9d,
+ 0x94,
+ 0x81,
+ 0xf0,
+ 0x9d,
+ 0x93,
+ 0xbd,
+ 0x00
+ ]);
+});
+Deno.test(function textEncodeInto3() {
+ const fixture = "𝓽𝓮𝔁𝓽";
+ const encoder = new TextEncoder();
+ const bytes = new Uint8Array(5);
+ const result = encoder.encodeInto(fixture, bytes);
+ assertEquals(result.read, 2);
+ assertEquals(result.written, 4);
+ assertEquals(Array.from(bytes), [
+ 0xf0,
+ 0x9d,
+ 0x93,
+ 0xbd,
+ 0x00
+ ]);
+});
+Deno.test(function loneSurrogateEncodeInto() {
+ const fixture = "lone𝄞\ud888surrogate";
+ const encoder = new TextEncoder();
+ const bytes = new Uint8Array(20);
+ const result = encoder.encodeInto(fixture, bytes);
+ assertEquals(result.read, 16);
+ assertEquals(result.written, 20);
+ assertEquals(Array.from(bytes), [
+ 0x6c,
+ 0x6f,
+ 0x6e,
+ 0x65,
+ 0xf0,
+ 0x9d,
+ 0x84,
+ 0x9e,
+ 0xef,
+ 0xbf,
+ 0xbd,
+ 0x73,
+ 0x75,
+ 0x72,
+ 0x72,
+ 0x6f,
+ 0x67,
+ 0x61,
+ 0x74,
+ 0x65
+ ]);
+});
+Deno.test(function loneSurrogateEncodeInto2() {
+ const fixture = "\ud800";
+ const encoder = new TextEncoder();
+ const bytes = new Uint8Array(3);
+ const result = encoder.encodeInto(fixture, bytes);
+ assertEquals(result.read, 1);
+ assertEquals(result.written, 3);
+ assertEquals(Array.from(bytes), [
+ 0xef,
+ 0xbf,
+ 0xbd
+ ]);
+});
+Deno.test(function loneSurrogateEncodeInto3() {
+ const fixture = "\udc00";
+ const encoder = new TextEncoder();
+ const bytes = new Uint8Array(3);
+ const result = encoder.encodeInto(fixture, bytes);
+ assertEquals(result.read, 1);
+ assertEquals(result.written, 3);
+ assertEquals(Array.from(bytes), [
+ 0xef,
+ 0xbf,
+ 0xbd
+ ]);
+});
+Deno.test(function swappedSurrogatePairEncodeInto4() {
+ const fixture = "\udc00\ud800";
+ const encoder = new TextEncoder();
+ const bytes = new Uint8Array(8);
+ const result = encoder.encodeInto(fixture, bytes);
+ assertEquals(result.read, 2);
+ assertEquals(result.written, 6);
+ assertEquals(Array.from(bytes), [
+ 0xef,
+ 0xbf,
+ 0xbd,
+ 0xef,
+ 0xbf,
+ 0xbd,
+ 0x00,
+ 0x00
+ ]);
+});
+Deno.test(function textDecoderSharedUint8Array() {
+ const ab = new SharedArrayBuffer(6);
+ const dataView = new DataView(ab);
+ const charCodeA = "A".charCodeAt(0);
+ for(let i = 0; i < ab.byteLength; i++){
+ dataView.setUint8(i, charCodeA + i);
+ }
+ const ui8 = new Uint8Array(ab);
+ const decoder = new TextDecoder();
+ const actual = decoder.decode(ui8);
+ assertEquals(actual, "ABCDEF");
+});
+Deno.test(function textDecoderSharedInt32Array() {
+ const ab = new SharedArrayBuffer(8);
+ const dataView = new DataView(ab);
+ const charCodeA = "A".charCodeAt(0);
+ for(let i = 0; i < ab.byteLength; i++){
+ dataView.setUint8(i, charCodeA + i);
+ }
+ const i32 = new Int32Array(ab);
+ const decoder = new TextDecoder();
+ const actual = decoder.decode(i32);
+ assertEquals(actual, "ABCDEFGH");
+});
+Deno.test(function toStringShouldBeWebCompatibility() {
+ const encoder = new TextEncoder();
+ assertEquals(encoder.toString(), "[object TextEncoder]");
+ const decoder = new TextDecoder();
+ assertEquals(decoder.toString(), "[object TextDecoder]");
+});
+Deno.test(function textEncoderShouldCoerceToString() {
+ const encoder = new TextEncoder();
+ const fixutreText = "text";
+ const fixture = {
+ toString () {
+ return fixutreText;
+ }
+ };
+ const bytes = encoder.encode(fixture as unknown as string);
+ const decoder = new TextDecoder();
+ const decoded = decoder.decode(bytes);
+ assertEquals(decoded, fixutreText);
+});
+Deno.test(function binaryEncode() {
+ const ops = Deno[Deno.internal].core.ops;
+ function asBinaryString(bytes: Uint8Array): string {
+ return Array.from(bytes).map((v: number)=>String.fromCodePoint(v)).join("");
+ }
+ function decodeBinary(binaryString: string) {
+ const chars: string[] = Array.from(binaryString);
+ return chars.map((v: string): number | undefined =>v.codePointAt(0));
+ }
+ const invalid = new Uint8Array([
+ 0xC0
+ ]);
+ assertEquals(ops.op_encode_binary_string(invalid), asBinaryString(invalid));
+ const invalid2 = new Uint8Array([
+ 0xC1
+ ]);
+ assertEquals(ops.op_encode_binary_string(invalid2), asBinaryString(invalid2));
+ for(let i = 0, j = 255; i <= 255; i++, j--){
+ const bytes = new Uint8Array([
+ i,
+ j
+ ]);
+ const binaryString = ops.op_encode_binary_string(bytes);
+ assertEquals(binaryString, asBinaryString(bytes));
+ assertEquals(Array.from(bytes), decodeBinary(binaryString));
+ }
+ const inputs = [
+ "σ😀",
+ "Кириллица is Cyrillic",
+ "𝓽𝓮𝔁𝓽",
+ "lone𝄞\ud888surrogate",
+ "\udc00\ud800",
+ "\ud800"
+ ];
+ for (const input of inputs){
+ const bytes = new TextEncoder().encode(input);
+ const binaryString = ops.op_encode_binary_string(bytes);
+ assertEquals(binaryString, asBinaryString(bytes));
+ assertEquals(Array.from(bytes), decodeBinary(binaryString));
+ }
+});
diff --git a/test/js/deno/event/custom-event.test.ts b/test/js/deno/event/custom-event.test.ts
new file mode 100644
index 000000000..29bb0dbc9
--- /dev/null
+++ b/test/js/deno/event/custom-event.test.ts
@@ -0,0 +1,27 @@
+// Copyright 2018+ the Deno authors. All rights reserved. MIT license.
+// https://raw.githubusercontent.com/denoland/deno/main/cli/tests/unit/custom_event_test.ts
+import { assertEquals } from "deno:harness";
+Deno.test(function customEventInitializedWithDetail() {
+ const type = "touchstart";
+ const detail = {
+ message: "hello"
+ };
+ const customEventInit = {
+ bubbles: true,
+ cancelable: true,
+ detail
+ } as CustomEventInit;
+ const event = new CustomEvent(type, customEventInit);
+ assertEquals(event.bubbles, true);
+ assertEquals(event.cancelable, true);
+ assertEquals(event.currentTarget, null);
+ assertEquals(event.detail, detail);
+ assertEquals(event.isTrusted, false);
+ assertEquals(event.target, null);
+ assertEquals(event.type, type);
+});
+Deno.test(function toStringShouldBeWebCompatibility() {
+ const type = "touchstart";
+ const event = new CustomEvent(type, {});
+ assertEquals(event.toString(), "[object CustomEvent]");
+});
diff --git a/test/js/deno/event/event-target.test.ts b/test/js/deno/event/event-target.test.ts
new file mode 100644
index 000000000..69791b93c
--- /dev/null
+++ b/test/js/deno/event/event-target.test.ts
@@ -0,0 +1,225 @@
+// Copyright 2018+ the Deno authors. All rights reserved. MIT license.
+// https://raw.githubusercontent.com/denoland/deno/main/cli/tests/unit/event_target_test.ts
+import { assertEquals, assertThrows } from "deno:harness";
+Deno.test(function addEventListenerTest() {
+ const document = new EventTarget();
+ assertEquals(document.addEventListener("x", null, false), undefined);
+ assertEquals(document.addEventListener("x", null, true), undefined);
+ assertEquals(document.addEventListener("x", null), undefined);
+});
+Deno.test(function constructedEventTargetCanBeUsedAsExpected() {
+ const target = new EventTarget();
+ const event = new Event("foo", {
+ bubbles: true,
+ cancelable: false
+ });
+ let callCount = 0;
+ const listener = (e: Event)=>{
+ assertEquals(e, event);
+ ++callCount;
+ };
+ target.addEventListener("foo", listener);
+ target.dispatchEvent(event);
+ assertEquals(callCount, 1);
+ target.dispatchEvent(event);
+ assertEquals(callCount, 2);
+ target.removeEventListener("foo", listener);
+ target.dispatchEvent(event);
+ assertEquals(callCount, 2);
+});
+Deno.test(function anEventTargetCanBeSubclassed() {
+ class NicerEventTarget extends EventTarget {
+ on(type: string, callback: ((e: Event) => void) | null, options?: AddEventListenerOptions) {
+ this.addEventListener(type, callback, options);
+ }
+ off(type: string, callback: ((e: Event) => void) | null, options?: EventListenerOptions) {
+ this.removeEventListener(type, callback, options);
+ }
+ }
+ const target = new NicerEventTarget();
+ new Event("foo", {
+ bubbles: true,
+ cancelable: false
+ });
+ let callCount = 0;
+ const listener = ()=>{
+ ++callCount;
+ };
+ target.on("foo", listener);
+ assertEquals(callCount, 0);
+ target.off("foo", listener);
+ assertEquals(callCount, 0);
+});
+Deno.test(function removingNullEventListenerShouldSucceed() {
+ const document = new EventTarget();
+ assertEquals(document.removeEventListener("x", null, false), undefined);
+ assertEquals(document.removeEventListener("x", null, true), undefined);
+ assertEquals(document.removeEventListener("x", null), undefined);
+});
+Deno.test(function constructedEventTargetUseObjectPrototype() {
+ const target = new EventTarget();
+ const event = new Event("toString", {
+ bubbles: true,
+ cancelable: false
+ });
+ let callCount = 0;
+ const listener = (e: Event)=>{
+ assertEquals(e, event);
+ ++callCount;
+ };
+ target.addEventListener("toString", listener);
+ target.dispatchEvent(event);
+ assertEquals(callCount, 1);
+ target.dispatchEvent(event);
+ assertEquals(callCount, 2);
+ target.removeEventListener("toString", listener);
+ target.dispatchEvent(event);
+ assertEquals(callCount, 2);
+});
+Deno.test(function toStringShouldBeWebCompatible() {
+ const target = new EventTarget();
+ assertEquals(target.toString(), "[object EventTarget]");
+});
+Deno.test(function dispatchEventShouldNotThrowError() {
+ let hasThrown = false;
+ try {
+ const target = new EventTarget();
+ const event = new Event("hasOwnProperty", {
+ bubbles: true,
+ cancelable: false
+ });
+ const listener = ()=>{};
+ target.addEventListener("hasOwnProperty", listener);
+ target.dispatchEvent(event);
+ } catch {
+ hasThrown = true;
+ }
+ assertEquals(hasThrown, false);
+});
+Deno.test(function eventTargetThisShouldDefaultToWindow() {
+ const { addEventListener , dispatchEvent , removeEventListener } = EventTarget.prototype;
+ let n = 1;
+ const event = new Event("hello");
+ const listener = ()=>{
+ n = 2;
+ };
+ addEventListener("hello", listener);
+ window.dispatchEvent(event);
+ assertEquals(n, 2);
+ n = 1;
+ removeEventListener("hello", listener);
+ window.dispatchEvent(event);
+ assertEquals(n, 1);
+ window.addEventListener("hello", listener);
+ dispatchEvent(event);
+ assertEquals(n, 2);
+ n = 1;
+ window.removeEventListener("hello", listener);
+ dispatchEvent(event);
+ assertEquals(n, 1);
+});
+Deno.test(function eventTargetShouldAcceptEventListenerObject() {
+ const target = new EventTarget();
+ const event = new Event("foo", {
+ bubbles: true,
+ cancelable: false
+ });
+ let callCount = 0;
+ const listener = {
+ handleEvent (e: Event) {
+ assertEquals(e, event);
+ ++callCount;
+ }
+ };
+ target.addEventListener("foo", listener);
+ target.dispatchEvent(event);
+ assertEquals(callCount, 1);
+ target.dispatchEvent(event);
+ assertEquals(callCount, 2);
+ target.removeEventListener("foo", listener);
+ target.dispatchEvent(event);
+ assertEquals(callCount, 2);
+});
+Deno.test(function eventTargetShouldAcceptAsyncFunction() {
+ const target = new EventTarget();
+ const event = new Event("foo", {
+ bubbles: true,
+ cancelable: false
+ });
+ let callCount = 0;
+ const listener = (e: Event)=>{
+ assertEquals(e, event);
+ ++callCount;
+ };
+ target.addEventListener("foo", listener);
+ target.dispatchEvent(event);
+ assertEquals(callCount, 1);
+ target.dispatchEvent(event);
+ assertEquals(callCount, 2);
+ target.removeEventListener("foo", listener);
+ target.dispatchEvent(event);
+ assertEquals(callCount, 2);
+});
+Deno.test(function eventTargetShouldAcceptAsyncFunctionForEventListenerObject() {
+ const target = new EventTarget();
+ const event = new Event("foo", {
+ bubbles: true,
+ cancelable: false
+ });
+ let callCount = 0;
+ const listener = {
+ handleEvent (e: Event) {
+ assertEquals(e, event);
+ ++callCount;
+ }
+ };
+ target.addEventListener("foo", listener);
+ target.dispatchEvent(event);
+ assertEquals(callCount, 1);
+ target.dispatchEvent(event);
+ assertEquals(callCount, 2);
+ target.removeEventListener("foo", listener);
+ target.dispatchEvent(event);
+ assertEquals(callCount, 2);
+});
+Deno.test(function eventTargetDispatchShouldSetTargetNoListener() {
+ const target = new EventTarget();
+ const event = new Event("foo");
+ assertEquals(event.target, null);
+ target.dispatchEvent(event);
+ assertEquals(event.target, target);
+});
+Deno.test(function eventTargetDispatchShouldSetTargetInListener() {
+ const target = new EventTarget();
+ const event = new Event("foo");
+ assertEquals(event.target, null);
+ let called = false;
+ target.addEventListener("foo", (e)=>{
+ assertEquals(e.target, target);
+ called = true;
+ });
+ target.dispatchEvent(event);
+ assertEquals(called, true);
+});
+Deno.test(function eventTargetAddEventListenerGlobalAbort() {
+ return new Promise((resolve)=>{
+ const c = new AbortController();
+ c.signal.addEventListener("abort", ()=>resolve());
+ addEventListener("test", ()=>{}, {
+ signal: c.signal
+ });
+ c.abort();
+ });
+});
+Deno.test(function eventTargetBrandChecking() {
+ const self = {};
+ assertThrows(()=>{
+ EventTarget.prototype.addEventListener.call(self, "test", null);
+ }, TypeError);
+ assertThrows(()=>{
+ EventTarget.prototype.removeEventListener.call(self, "test", null);
+ }, TypeError);
+ assertThrows(()=>{
+ EventTarget.prototype.dispatchEvent.call(self, new Event("test"));
+ }, TypeError);
+});
diff --git a/test/js/deno/event/event.test.ts b/test/js/deno/event/event.test.ts
new file mode 100644
index 000000000..cd23b5215
--- /dev/null
+++ b/test/js/deno/event/event.test.ts
@@ -0,0 +1,124 @@
+// Copyright 2018+ the Deno authors. All rights reserved. MIT license.
+// https://raw.githubusercontent.com/denoland/deno/main/cli/tests/unit/event_test.ts
+import { assert, assertEquals, assertStringIncludes } from "deno:harness";
+Deno.test(function eventInitializedWithType() {
+ const type = "click";
+ const event = new Event(type);
+ assertEquals(event.isTrusted, false);
+ assertEquals(event.target, null);
+ assertEquals(event.currentTarget, null);
+ assertEquals(event.type, "click");
+ assertEquals(event.bubbles, false);
+ assertEquals(event.cancelable, false);
+});
+Deno.test(function eventInitializedWithTypeAndDict() {
+ const init = "submit";
+ const eventInit = {
+ bubbles: true,
+ cancelable: true
+ } as EventInit;
+ const event = new Event(init, eventInit);
+ assertEquals(event.isTrusted, false);
+ assertEquals(event.target, null);
+ assertEquals(event.currentTarget, null);
+ assertEquals(event.type, "submit");
+ assertEquals(event.bubbles, true);
+ assertEquals(event.cancelable, true);
+});
+Deno.test(function eventComposedPathSuccess() {
+ const type = "click";
+ const event = new Event(type);
+ const composedPath = event.composedPath();
+ assertEquals(composedPath, []);
+});
+Deno.test(function eventStopPropagationSuccess() {
+ const type = "click";
+ const event = new Event(type);
+ assertEquals(event.cancelBubble, false);
+ event.stopPropagation();
+ assertEquals(event.cancelBubble, true);
+});
+Deno.test(function eventStopImmediatePropagationSuccess() {
+ const type = "click";
+ const event = new Event(type);
+ assertEquals(event.cancelBubble, false);
+ event.stopImmediatePropagation();
+ assertEquals(event.cancelBubble, true);
+});
+Deno.test(function eventPreventDefaultSuccess() {
+ const type = "click";
+ const event = new Event(type);
+ assertEquals(event.defaultPrevented, false);
+ event.preventDefault();
+ assertEquals(event.defaultPrevented, false);
+ const eventInit = {
+ bubbles: true,
+ cancelable: true
+ } as EventInit;
+ const cancelableEvent = new Event(type, eventInit);
+ assertEquals(cancelableEvent.defaultPrevented, false);
+ cancelableEvent.preventDefault();
+ assertEquals(cancelableEvent.defaultPrevented, true);
+});
+Deno.test(function eventInitializedWithNonStringType() {
+ const type: any = undefined;
+ const event = new Event(type);
+ assertEquals(event.isTrusted, false);
+ assertEquals(event.target, null);
+ assertEquals(event.currentTarget, null);
+ assertEquals(event.type, "undefined");
+ assertEquals(event.bubbles, false);
+ assertEquals(event.cancelable, false);
+});
+Deno.test(function eventIsTrusted() {
+ const desc1 = Object.getOwnPropertyDescriptor(new Event("x"), "isTrusted");
+ assert(desc1);
+ assertEquals(typeof desc1.get, "function");
+ const desc2 = Object.getOwnPropertyDescriptor(new Event("x"), "isTrusted");
+ assert(desc2);
+ assertEquals(typeof desc2!.get, "function");
+ assertEquals(desc1!.get, desc2!.get);
+});
+Deno.test(function eventInspectOutput() {
+ const cases: Array<[any, (event: any) => string]> = [
+ [
+ new Event("test"),
+ (event: Event)=>`Event {\n bubbles: false,\n cancelable: false,\n composed: false,\n currentTarget: null,\n defaultPrevented: false,\n eventPhase: 0,\n srcElement: null,\n target: null,\n returnValue: true,\n timeStamp: ${event.timeStamp},\n type: "test"\n}`
+ ],
+ [
+ new ErrorEvent("error"),
+ (event: Event)=>`ErrorEvent {\n bubbles: false,\n cancelable: false,\n composed: false,\n currentTarget: null,\n defaultPrevented: false,\n eventPhase: 0,\n srcElement: null,\n target: null,\n returnValue: true,\n timeStamp: ${event.timeStamp},\n type: "error",\n message: "",\n filename: "",\n lineno: 0,\n colno: 0,\n error: undefined\n}`
+ ],
+ [
+ new CloseEvent("close"),
+ (event: Event)=>`CloseEvent {\n bubbles: false,\n cancelable: false,\n composed: false,\n currentTarget: null,\n defaultPrevented: false,\n eventPhase: 0,\n srcElement: null,\n target: null,\n returnValue: true,\n timeStamp: ${event.timeStamp},\n type: "close",\n wasClean: false,\n code: 0,\n reason: ""\n}`
+ ],
+ [
+ new CustomEvent("custom"),
+ (event: Event)=>`CustomEvent {\n bubbles: false,\n cancelable: false,\n composed: false,\n currentTarget: null,\n defaultPrevented: false,\n eventPhase: 0,\n srcElement: null,\n target: null,\n returnValue: true,\n timeStamp: ${event.timeStamp},\n type: "custom",\n detail: undefined\n}`
+ ],
+ [
+ new ProgressEvent("progress"),
+ (event: Event)=>`ProgressEvent {\n bubbles: false,\n cancelable: false,\n composed: false,\n currentTarget: null,\n defaultPrevented: false,\n eventPhase: 0,\n srcElement: null,\n target: null,\n returnValue: true,\n timeStamp: ${event.timeStamp},\n type: "progress",\n lengthComputable: false,\n loaded: 0,\n total: 0\n}`
+ ]
+ ];
+ for (const [event, outputProvider] of cases){
+ assertEquals(Deno.inspect(event), outputProvider(event));
+ }
+});
+Deno.test(function inspectEvent() {
+ assertEquals(Deno.inspect(Event.prototype), `Event {
+ bubbles: [Getter],
+ cancelable: [Getter],
+ composed: [Getter],
+ currentTarget: [Getter],
+ defaultPrevented: [Getter],
+ eventPhase: [Getter],
+ srcElement: [Getter/Setter],
+ target: [Getter],
+ returnValue: [Getter/Setter],
+ timeStamp: [Getter],
+ type: [Getter]
+}`);
+ assertStringIncludes(Deno.inspect(new Event("test")), `Event {\n bubbles: false,\n cancelable: false,`);
+});
diff --git a/test/js/deno/html/blob.test.ts b/test/js/deno/fetch/blob.test.ts
index 306d9ca59..8c9c7318c 100644
--- a/test/js/deno/html/blob.test.ts
+++ b/test/js/deno/fetch/blob.test.ts
@@ -1,6 +1,5 @@
// Copyright 2018+ the Deno authors. All rights reserved. MIT license.
// https://raw.githubusercontent.com/denoland/deno/main/cli/tests/unit/blob_test.ts
-
import { assert, assertEquals, assertStringIncludes } from "deno:harness";
import { concat } from "deno:harness";
Deno.test(function blobString() {
diff --git a/test/js/deno/fetch/body.test.ts b/test/js/deno/fetch/body.test.ts
index 92ad70c0a..87966bc48 100644
--- a/test/js/deno/fetch/body.test.ts
+++ b/test/js/deno/fetch/body.test.ts
@@ -1,6 +1,5 @@
// Copyright 2018+ the Deno authors. All rights reserved. MIT license.
// https://raw.githubusercontent.com/denoland/deno/main/cli/tests/unit/body_test.ts
-
import { assert, assertEquals } from "deno:harness";
function buildBody(body: any, headers?: Headers): Body {
const stub = new Request("http://foo/", {
diff --git a/test/js/deno/fetch/file.test.ts b/test/js/deno/fetch/file.test.ts
new file mode 100644
index 000000000..a74cabc64
--- /dev/null
+++ b/test/js/deno/fetch/file.test.ts
@@ -0,0 +1,124 @@
+// Copyright 2018+ the Deno authors. All rights reserved. MIT license.
+// https://raw.githubusercontent.com/denoland/deno/main/cli/tests/unit/file_test.ts
+import { assert, assertEquals } from "deno:harness";
+function testFirstArgument(arg1: any[], expectedSize: number) {
+ const file = new File(arg1, "name");
+ assert(file instanceof File);
+ assertEquals(file.name, "name");
+ assertEquals(file.size, expectedSize);
+ assertEquals(file.type, "");
+}
+Deno.test(function fileEmptyFileBits() {
+ testFirstArgument([], 0);
+});
+Deno.test(function fileStringFileBits() {
+ testFirstArgument([
+ "bits"
+ ], 4);
+});
+Deno.test(function fileUnicodeStringFileBits() {
+ testFirstArgument([
+ "𝓽𝓮𝔁𝓽"
+ ], 16);
+});
+Deno.test(function fileStringObjectFileBits() {
+ testFirstArgument([
+ new String("string object")
+ ], 13);
+});
+Deno.test(function fileEmptyBlobFileBits() {
+ testFirstArgument([
+ new Blob()
+ ], 0);
+});
+Deno.test(function fileBlobFileBits() {
+ testFirstArgument([
+ new Blob([
+ "bits"
+ ])
+ ], 4);
+});
+Deno.test(function fileEmptyFileFileBits() {
+ testFirstArgument([
+ new File([], "world.txt")
+ ], 0);
+});
+Deno.test(function fileFileFileBits() {
+ testFirstArgument([
+ new File([
+ "bits"
+ ], "world.txt")
+ ], 4);
+});
+Deno.test(function fileArrayBufferFileBits() {
+ testFirstArgument([
+ new ArrayBuffer(8)
+ ], 8);
+});
+Deno.test(function fileTypedArrayFileBits() {
+ testFirstArgument([
+ new Uint8Array([
+ 0x50,
+ 0x41,
+ 0x53,
+ 0x53
+ ])
+ ], 4);
+});
+Deno.test(function fileVariousFileBits() {
+ testFirstArgument([
+ "bits",
+ new Blob([
+ "bits"
+ ]),
+ new Blob(),
+ new Uint8Array([
+ 0x50,
+ 0x41
+ ]),
+ new Uint16Array([
+ 0x5353
+ ]),
+ new Uint32Array([
+ 0x53534150
+ ])
+ ], 16);
+});
+Deno.test(function fileNumberInFileBits() {
+ testFirstArgument([
+ 12
+ ], 2);
+});
+Deno.test(function fileArrayInFileBits() {
+ testFirstArgument([
+ [
+ 1,
+ 2,
+ 3
+ ]
+ ], 5);
+});
+Deno.test(function fileObjectInFileBits() {
+ testFirstArgument([
+ {}
+ ], 15);
+});
+function testSecondArgument(arg2: any, expectedFileName: string) {
+ const file = new File([
+ "bits"
+ ], arg2);
+ assert(file instanceof File);
+ assertEquals(file.name, expectedFileName);
+}
+Deno.test(function fileUsingFileName() {
+ testSecondArgument("dummy", "dummy");
+});
+Deno.test(function fileUsingNullFileName() {
+ testSecondArgument(null, "null");
+});
+Deno.test(function fileUsingNumberFileName() {
+ testSecondArgument(1, "1");
+});
+Deno.test(function fileUsingEmptyStringFileName() {
+ testSecondArgument("", "");
+});
diff --git a/test/js/deno/fetch/headers.test.ts b/test/js/deno/fetch/headers.test.ts
new file mode 100644
index 000000000..1bac1b93c
--- /dev/null
+++ b/test/js/deno/fetch/headers.test.ts
@@ -0,0 +1,459 @@
+// Copyright 2018+ the Deno authors. All rights reserved. MIT license.
+// https://raw.githubusercontent.com/denoland/deno/main/cli/tests/unit/headers_test.ts
+import { assert, assertEquals, assertThrows } from "deno:harness";
+const { inspectArgs } = Deno[Deno.internal];
+Deno.test(function headersHasCorrectNameProp() {
+ assertEquals(Headers.name, "Headers");
+});
+Deno.test(function newHeaderTest() {
+ new Headers();
+ new Headers(undefined);
+ new Headers({});
+ try {
+ new Headers(null as any);
+ } catch (e) {
+ assert(e instanceof TypeError);
+ }
+});
+const headerDict: Record<string, string> = {
+ name1: "value1",
+ name2: "value2",
+ name3: "value3",
+ name4: undefined as any,
+ "Content-Type": "value4"
+};
+const headerSeq: any[] = [];
+for (const [name, value] of Object.entries(headerDict)){
+ headerSeq.push([
+ name,
+ value
+ ]);
+}
+Deno.test(function newHeaderWithSequence() {
+ const headers = new Headers(headerSeq);
+ for (const [name, value] of Object.entries(headerDict)){
+ assertEquals(headers.get(name), String(value));
+ }
+ assertEquals(headers.get("length"), null);
+});
+Deno.test(function newHeaderWithRecord() {
+ const headers = new Headers(headerDict);
+ for (const [name, value] of Object.entries(headerDict)){
+ assertEquals(headers.get(name), String(value));
+ }
+});
+Deno.test(function newHeaderWithHeadersInstance() {
+ const headers = new Headers(headerDict);
+ const headers2 = new Headers(headers);
+ for (const [name, value] of Object.entries(headerDict)){
+ assertEquals(headers2.get(name), String(value));
+ }
+});
+Deno.test(function headerAppendSuccess() {
+ const headers = new Headers();
+ for (const [name, value] of Object.entries(headerDict)){
+ headers.append(name, value);
+ assertEquals(headers.get(name), String(value));
+ }
+});
+Deno.test(function headerSetSuccess() {
+ const headers = new Headers();
+ for (const [name, value] of Object.entries(headerDict)){
+ headers.set(name, value);
+ assertEquals(headers.get(name), String(value));
+ }
+});
+Deno.test(function headerHasSuccess() {
+ const headers = new Headers(headerDict);
+ for (const name of Object.keys(headerDict)){
+ assert(headers.has(name), "headers has name " + name);
+ assert(!headers.has("nameNotInHeaders"), "headers do not have header: nameNotInHeaders");
+ }
+});
+Deno.test(function headerDeleteSuccess() {
+ const headers = new Headers(headerDict);
+ for (const name of Object.keys(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);
+ }
+});
+Deno.test(function headerGetSuccess() {
+ const headers = new Headers(headerDict);
+ for (const [name, value] of Object.entries(headerDict)){
+ assertEquals(headers.get(name), String(value));
+ assertEquals(headers.get("nameNotInHeaders"), null);
+ }
+});
+Deno.test(function headerEntriesSuccess() {
+ 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));
+ }
+});
+Deno.test(function headerKeysSuccess() {
+ const headers = new Headers(headerDict);
+ const iterators = headers.keys();
+ for (const it of iterators){
+ assert(headers.has(it));
+ }
+});
+Deno.test(function headerValuesSuccess() {
+ 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<string, string> = {
+ name1: "value1",
+ Name2: "value2",
+ name: "value3",
+ "content-Type": "value4",
+ "Content-Typ": "value5",
+ "Content-Types": "value6"
+};
+Deno.test(function headerForEachSuccess() {
+ const headers = new Headers(headerEntriesDict);
+ const keys = Object.keys(headerEntriesDict);
+ keys.forEach((key)=>{
+ const value = headerEntriesDict[key];
+ const newkey = key.toLowerCase();
+ headerEntriesDict[newkey] = value;
+ });
+ let callNum = 0;
+ headers.forEach((value, key, container)=>{
+ assertEquals(headers, container);
+ assertEquals(value, headerEntriesDict[key]);
+ callNum++;
+ });
+ assertEquals(callNum, keys.length);
+});
+Deno.test(function headerSymbolIteratorSuccess() {
+ 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));
+ }
+});
+Deno.test(function headerTypesAvailable() {
+ function newHeaders(): Headers {
+ return new Headers();
+ }
+ const headers = newHeaders();
+ assert(headers instanceof Headers);
+});
+Deno.test(function headerIllegalReject() {
+ 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);
+ new Headers({
+ "He-y": "o k"
+ });
+});
+Deno.test(function headerParamsShouldThrowTypeError() {
+ 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);
+});
+Deno.test(function headerParamsArgumentsCheck() {
+ const methodRequireOneParam = [
+ "delete",
+ "get",
+ "has",
+ "forEach"
+ ] as const;
+ const methodRequireTwoParams = [
+ "append",
+ "set"
+ ] as const;
+ methodRequireOneParam.forEach((method)=>{
+ const headers = new Headers();
+ let hasThrown = 0;
+ try {
+ (headers as any)[method]();
+ hasThrown = 1;
+ } catch (err) {
+ if (err instanceof TypeError) {
+ hasThrown = 2;
+ } else {
+ hasThrown = 3;
+ }
+ }
+ assertEquals(hasThrown, 2);
+ });
+ methodRequireTwoParams.forEach((method)=>{
+ const headers = new Headers();
+ let hasThrown = 0;
+ try {
+ (headers as any)[method]();
+ hasThrown = 1;
+ } catch (err) {
+ if (err instanceof TypeError) {
+ hasThrown = 2;
+ } else {
+ hasThrown = 3;
+ }
+ }
+ assertEquals(hasThrown, 2);
+ hasThrown = 0;
+ try {
+ (headers as any)[method]("foo");
+ hasThrown = 1;
+ } catch (err) {
+ if (err instanceof TypeError) {
+ hasThrown = 2;
+ } else {
+ hasThrown = 3;
+ }
+ }
+ assertEquals(hasThrown, 2);
+ });
+});
+Deno.test(function headersInitMultiple() {
+ 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"
+ ]
+ ]);
+});
+Deno.test(function headerInitWithPrototypePollution() {
+ const originalExec = RegExp.prototype.exec;
+ try {
+ RegExp.prototype.exec = ()=>{
+ throw Error();
+ };
+ new Headers([
+ [
+ "X-Deno",
+ "foo"
+ ],
+ [
+ "X-Deno",
+ "bar"
+ ]
+ ]);
+ } finally{
+ RegExp.prototype.exec = originalExec;
+ }
+});
+Deno.test(function headersAppendMultiple() {
+ 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"
+ ],
+ [
+ "set-cookie",
+ "bar=baz"
+ ],
+ [
+ "x-deno",
+ "foo, bar"
+ ]
+ ]);
+});
+Deno.test(function headersAppendDuplicateSetCookieKey() {
+ 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=bar"
+ ],
+ [
+ "set-cookie",
+ "foo=baz"
+ ],
+ [
+ "set-cookie",
+ "baz=bar"
+ ]
+ ]);
+});
+Deno.test(function headersGetSetCookie() {
+ const headers = new Headers([
+ [
+ "Set-Cookie",
+ "foo=bar"
+ ],
+ [
+ "set-Cookie",
+ "bar=qat"
+ ]
+ ]);
+ assertEquals(headers.get("SET-COOKIE"), "foo=bar, bar=qat");
+});
+Deno.test(function toStringShouldBeWebCompatibility() {
+ const headers = new Headers();
+ assertEquals(headers.toString(), "[object Headers]");
+});
+function stringify(...args: unknown[]): string {
+ return inspectArgs(args).replace(/\n$/, "");
+}
+Deno.test.ignore(function customInspectReturnsCorrectHeadersFormat() {
+ 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-length": "1337", "content-type": "application/json" }`);
+});
+Deno.test(function invalidHeadersFlaky() {
+ assertThrows(()=>new Headers([
+ [
+ "x",
+ "\u0000x"
+ ]
+ ]), TypeError, "Header value is not valid.");
+ assertThrows(()=>new Headers([
+ [
+ "x",
+ "\u0000x"
+ ]
+ ]), TypeError, "Header value is not valid.");
+});
diff --git a/test/js/deno/fetch/request.test.ts b/test/js/deno/fetch/request.test.ts
new file mode 100644
index 000000000..d1a444196
--- /dev/null
+++ b/test/js/deno/fetch/request.test.ts
@@ -0,0 +1,59 @@
+// Copyright 2018+ the Deno authors. All rights reserved. MIT license.
+// https://raw.githubusercontent.com/denoland/deno/main/cli/tests/unit/request_test.ts
+import { assertEquals, assertStringIncludes } from "deno:harness";
+Deno.test(async function fromInit() {
+ const req = new Request("http://foo/", {
+ body: "ahoyhoy",
+ method: "POST",
+ headers: {
+ "test-header": "value"
+ }
+ });
+ assertEquals("ahoyhoy", await req.text());
+ assertEquals(req.url, "http://foo/");
+ assertEquals(req.headers.get("test-header"), "value");
+});
+Deno.test(function requestNonString() {
+ const nonString = {
+ toString () {
+ return "http://foo/";
+ }
+ };
+ assertEquals(new Request(nonString).url, "http://foo/");
+});
+Deno.test(function methodNonString() {
+ assertEquals(new Request("http://foo/", {
+ method: undefined
+ }).method, "GET");
+});
+Deno.test.ignore(function requestRelativeUrl() {
+ assertEquals(new Request("relative-url").url, "http://js-unit-tests/foo/relative-url");
+});
+Deno.test(async function cloneRequestBodyStream() {
+ const stream = new Request("http://foo/", {
+ body: "a test body",
+ method: "POST"
+ }).body;
+ const r1 = new Request("http://foo/", {
+ body: stream,
+ method: "POST"
+ });
+ const r2 = r1.clone();
+ const b1 = await r1.text();
+ const b2 = await r2.text();
+ assertEquals(b1, b2);
+});
+Deno.test.ignore(function customInspectFunction() {
+ const request = new Request("https://example.com");
+ assertEquals(Deno.inspect(request), `Request {
+ bodyUsed: false,
+ headers: Headers {},
+ method: "GET",
+ redirect: "follow",
+ url: "https://example.com/"
+}`);
+ assertStringIncludes(Deno.inspect(Request.prototype), "Request");
+});
+Deno.test(function requestConstructorTakeURLObjectAsParameter() {
+ assertEquals(new Request(new URL("http://foo/")).url, "http://foo/");
+});
diff --git a/test/js/deno/fetch/response.test.ts b/test/js/deno/fetch/response.test.ts
new file mode 100644
index 000000000..2e83ed0d8
--- /dev/null
+++ b/test/js/deno/fetch/response.test.ts
@@ -0,0 +1,105 @@
+// Copyright 2018+ the Deno authors. All rights reserved. MIT license.
+// https://raw.githubusercontent.com/denoland/deno/main/cli/tests/unit/response_test.ts
+import { assert, assertEquals, assertStringIncludes, assertThrows } from "deno:harness";
+Deno.test(async function responseText() {
+ const response = new Response("hello world");
+ const textPromise = response.text();
+ assert(textPromise instanceof Promise);
+ const text = await textPromise;
+ assert(typeof text === "string");
+ assertEquals(text, "hello world");
+});
+Deno.test(async function responseArrayBuffer() {
+ const response = new Response(new Uint8Array([
+ 1,
+ 2,
+ 3
+ ]));
+ const arrayBufferPromise = response.arrayBuffer();
+ assert(arrayBufferPromise instanceof Promise);
+ const arrayBuffer = await arrayBufferPromise;
+ assert(arrayBuffer instanceof ArrayBuffer);
+ assertEquals(new Uint8Array(arrayBuffer), new Uint8Array([
+ 1,
+ 2,
+ 3
+ ]));
+});
+Deno.test(async function responseJson() {
+ const response = new Response('{"hello": "world"}');
+ const jsonPromise = response.json();
+ assert(jsonPromise instanceof Promise);
+ const json = await jsonPromise;
+ assert(json instanceof Object);
+ assertEquals(json, {
+ hello: "world"
+ });
+});
+Deno.test(async function responseBlob() {
+ const response = new Response(new Uint8Array([
+ 1,
+ 2,
+ 3
+ ]));
+ const blobPromise = response.blob();
+ assert(blobPromise instanceof Promise);
+ const blob = await blobPromise;
+ assert(blob instanceof Blob);
+ assertEquals(blob.size, 3);
+ assertEquals(await blob.arrayBuffer(), new Uint8Array([
+ 1,
+ 2,
+ 3
+ ]).buffer);
+});
+Deno.test(async function responseFormData() {
+ const input = new FormData();
+ input.append("hello", "world");
+ const response = new Response(input);
+ const contentType = response.headers.get("content-type")!;
+ assert(contentType.startsWith("multipart/form-data"));
+ const formDataPromise = response.formData();
+ assert(formDataPromise instanceof Promise);
+ const formData = await formDataPromise;
+ assert(formData instanceof FormData);
+ assertEquals([
+ ...formData
+ ], [
+ ...input
+ ]);
+});
+Deno.test(function responseInvalidInit() {
+ assertThrows(()=>new Response("", 0));
+ assertThrows(()=>new Response("", {
+ status: 0
+ }));
+ assertThrows(()=>new Response("", {
+ status: null
+ }));
+});
+Deno.test(function responseNullInit() {
+ const response = new Response("", null);
+ assertEquals(response.status, 200);
+});
+Deno.test.ignore(function customInspectFunction() {
+ const response = new Response();
+ assertEquals(Deno.inspect(response), `Response {
+ body: null,
+ bodyUsed: false,
+ headers: Headers {},
+ ok: true,
+ redirected: false,
+ status: 200,
+ statusText: "",
+ url: ""
+}`);
+ assertStringIncludes(Deno.inspect(Response.prototype), "Response");
+});
+Deno.test(async function responseBodyUsed() {
+ const response = new Response("body");
+ assert(!response.bodyUsed);
+ await response.text();
+ assert(response.bodyUsed);
+ response.body;
+ assert(response.bodyUsed);
+});
diff --git a/test/js/deno/harness.ts b/test/js/deno/harness.ts
index 8634d5261..70f0ceede 100644
--- a/test/js/deno/harness.ts
+++ b/test/js/deno/harness.ts
@@ -1,4 +1,24 @@
-export * from "./harness/global.js";
+export * from "./harness/test.js";
export * from "./harness/util.js";
export * from "./harness/assert.js";
export * from "./harness/fixture.js";
+
+import { readTextFile } from "./harness/fixture.js";
+import { test } from "./harness/test.js";
+
+export const Deno = {
+ test,
+ readTextFile,
+ internal: "[internal]",
+ ["[internal]"]: {},
+};
+
+// @ts-expect-error
+globalThis["Deno"] = Deno;
+
+export const window = {
+ crypto: crypto,
+};
+
+// @ts-expect-error
+globalThis["window"] = window;
diff --git a/test/js/deno/harness/fixture.ts b/test/js/deno/harness/fixture.ts
index 13a531e52..6917fc893 100644
--- a/test/js/deno/harness/fixture.ts
+++ b/test/js/deno/harness/fixture.ts
@@ -1,6 +1,7 @@
import type { Server } from "bun";
import { serve } from "bun";
import { afterAll, beforeAll } from "bun:test";
+import baseUrl from "../resources/url.json";
let server: Server;
@@ -10,10 +11,10 @@ beforeAll(() => {
fetch(request: Request): Response {
const { url } = request;
const { pathname, search } = new URL(url);
- const redirect = new URL(
- `${pathname}?${search}`,
- "https://raw.githubusercontent.com/denoland/deno/main/cli/tests/testdata/",
- );
+ if (pathname === "/echo_server") {
+ return new Response(request.body, request);
+ }
+ const redirect = new URL(`${pathname}?${search}`, baseUrl);
return Response.redirect(redirect.toString());
},
});
@@ -24,3 +25,12 @@ afterAll(() => {
server.stop(true);
}
});
+
+export async function readTextFile(path: string): Promise<string> {
+ const url = new URL(path, baseUrl);
+ const response = await fetch(url);
+ if (response.ok) {
+ return response.text();
+ }
+ throw new Error(`${response.status}: ${response.url}`);
+}
diff --git a/test/js/deno/harness/global.ts b/test/js/deno/harness/test.ts
index 4992f026f..8e31c3d4d 100644
--- a/test/js/deno/harness/global.ts
+++ b/test/js/deno/harness/test.ts
@@ -2,17 +2,23 @@ import { test as bunTest } from "bun:test";
type Fn = () => void | Promise<unknown>;
type Options = {
- permissions?: {
+ permissions?: "none" | {
net?: boolean;
+ read?: boolean;
};
ignore?: boolean;
};
-function test(arg0: Fn | Options, arg1?: Fn): void {
+export function test(arg0: Fn | Options, arg1?: Fn): void {
if (typeof arg0 === "function") {
bunTest(arg0.name, arg0);
} else if (typeof arg1 === "function") {
- if (arg0?.ignore === true || arg0?.permissions?.net === false) {
+ if (
+ arg0?.ignore === true
+ || arg0?.permissions === "none"
+ || arg0?.permissions?.net === false
+ || arg0?.permissions?.read === false
+ ) {
bunTest.skip(arg1.name, arg1);
} else {
bunTest(arg1.name, arg1);
@@ -31,15 +37,3 @@ test.ignore = (arg0: Fn | Options, arg1?: Fn) => {
throw new Error("Unimplemented");
}
};
-
-export function inspect(...args: unknown[]): string {
- return Bun.inspect(...args);
-}
-
-export const Deno = {
- test,
- inspect,
-};
-
-// @ts-expect-error
-globalThis["Deno"] = Deno;
diff --git a/test/js/deno/harness/util.ts b/test/js/deno/harness/util.ts
index a7fbf7fce..ecd8a6953 100644
--- a/test/js/deno/harness/util.ts
+++ b/test/js/deno/harness/util.ts
@@ -1,5 +1,50 @@
+// Copyright 2018+ the Deno authors. All rights reserved. MIT license.
+// https://github.com/denoland/deno/blob/main/ext/node/polyfills/_util/async.ts
+
import { concatArrayBuffers } from "bun";
export function concat(...buffers: Uint8Array[]): Uint8Array {
return new Uint8Array(concatArrayBuffers(buffers));
}
+
+export function deferred<T>() {
+ let methods;
+ let state = "pending";
+ const promise = new Promise<T>((resolve, reject) => {
+ methods = {
+ async resolve(value: T | PromiseLike<T>) {
+ await value;
+ state = "fulfilled";
+ resolve(value);
+ },
+ reject(reason?: any) {
+ state = "rejected";
+ reject(reason);
+ },
+ };
+ });
+ Object.defineProperty(promise, "state", { get: () => state });
+ return Object.assign(promise, methods);
+}
+
+export function delay(
+ ms: number,
+ options: { signal?: AbortSignal } = {},
+): Promise<void> {
+ const { signal } = options;
+ if (signal?.aborted) {
+ return Promise.reject(new DOMException("Delay was aborted.", "AbortError"));
+ }
+ return new Promise((resolve, reject) => {
+ const abort = () => {
+ clearTimeout(i);
+ reject(new DOMException("Delay was aborted.", "AbortError"));
+ };
+ const done = () => {
+ signal?.removeEventListener("abort", abort);
+ resolve();
+ };
+ const i = setTimeout(done, ms);
+ signal?.addEventListener("abort", abort, { once: true });
+ });
+}
diff --git a/test/js/deno/resources/imports.json b/test/js/deno/resources/imports.json
index 6b044a9a5..182555661 100644
--- a/test/js/deno/resources/imports.json
+++ b/test/js/deno/resources/imports.json
@@ -1 +1,5 @@
-["test_util.ts", "test_util/std/bytes/concat.ts"]
+[
+ "test_util.ts",
+ "test_util/std/bytes/concat.ts",
+ "test_util/std/io/buffer.ts"
+]
diff --git a/test/js/deno/resources/tests.json b/test/js/deno/resources/tests.json
index 7de35dd78..5a7b10530 100644
--- a/test/js/deno/resources/tests.json
+++ b/test/js/deno/resources/tests.json
@@ -1,15 +1,78 @@
[
{
"path": "abort/abort-controller.test.ts",
- "remotePath": "unit/abort_controller_test.ts"
+ "remotePath": "cli/tests/unit/abort_controller_test.ts"
},
{
- "path": "html/blob.test.ts",
- "remotePath": "unit/blob_test.ts",
- "skip": ["blobCustomInspectFunction"]
+ "path": "url/url.test.ts",
+ "remotePath": "cli/tests/unit/url_test.ts",
+ "skip": ["customInspectFunction"]
+ },
+ {
+ "path": "url/urlsearchparams.test.ts",
+ "remotePath": "cli/tests/unit/url_search_params_test.ts"
+ },
+ {
+ "path": "event/event.test.ts",
+ "remotePath": "cli/tests/unit/event_test.ts"
+ },
+ {
+ "path": "event/event-target.test.ts",
+ "remotePath": "cli/tests/unit/event_target_test.ts"
+ },
+ {
+ "path": "event/custom-event.test.ts",
+ "remotePath": "cli/tests/unit/custom_event_test.ts"
},
{
"path": "fetch/body.test.ts",
- "remotePath": "unit/body_test.ts"
+ "remotePath": "cli/tests/unit/body_test.ts"
+ },
+ {
+ "path": "fetch/blob.test.ts",
+ "remotePath": "cli/tests/unit/blob_test.ts",
+ "skip": ["blobCustomInspectFunction"]
+ },
+ {
+ "path": "fetch/file.test.ts",
+ "remotePath": "cli/tests/unit/file_test.ts"
+ },
+ {
+ "path": "fetch/headers.test.ts",
+ "remotePath": "cli/tests/unit/headers_test.ts",
+ "skip": ["customInspectReturnsCorrectHeadersFormat"]
+ },
+ {
+ "path": "fetch/request.test.ts",
+ "remotePath": "cli/tests/unit/request_test.ts",
+ "skip": [
+ "customInspectFunction",
+ "requestRelativeUrl"
+ ]
+ },
+ {
+ "path": "fetch/response.test.ts",
+ "remotePath": "cli/tests/unit/response_test.ts",
+ "skip": ["customInspectFunction"]
+ },
+ {
+ "path": "crypto/random.test.ts",
+ "remotePath": "cli/tests/unit/get_random_values_test.ts"
+ },
+ {
+ "path": "crypto/webcrypto.test.ts",
+ "remotePath": "cli/tests/unit/webcrypto_test.ts"
+ },
+ {
+ "path": "encoding/encoding.test.ts",
+ "remotePath": "cli/tests/unit/text_encoding_test.ts"
+ },
+ {
+ "path": "websocket/websocket.test.ts",
+ "remotePath": "cli/tests/unit/websocket_test.ts"
+ },
+ {
+ "path": "v8/error.test.ts",
+ "remotePath": "cli/tests/unit/error_stack_test.ts"
}
]
diff --git a/test/js/deno/resources/url.json b/test/js/deno/resources/url.json
new file mode 100644
index 000000000..348c6daf7
--- /dev/null
+++ b/test/js/deno/resources/url.json
@@ -0,0 +1 @@
+"https://raw.githubusercontent.com/denoland/deno/main/"
diff --git a/test/js/deno/scripts/postinstall.ts b/test/js/deno/scripts/postinstall.ts
index 65fd5a04e..11368585b 100644
--- a/test/js/deno/scripts/postinstall.ts
+++ b/test/js/deno/scripts/postinstall.ts
@@ -4,13 +4,14 @@ import { parse, print } from "@swc/core";
import type { ImportDeclaration, ExpressionStatement, CallExpression } from "@swc/core";
import imports from "../resources/imports.json";
import tests from "../resources/tests.json";
+import baseUrl from "../resources/url.json";
// FIXME: https://github.com/oven-sh/bun/issues/2350
// import * as harness from "deno:harness";
for (const test of tests) {
const path = join(import.meta.dir, "..", test.path);
- const url = new URL(test.remotePath, "https://raw.githubusercontent.com/denoland/deno/main/cli/tests/");
+ const url = new URL(test.remotePath, baseUrl);
const response = await fetch(url);
console.log(response.status, url.toString(), "->", test.path);
if (!response.ok) {
@@ -36,12 +37,11 @@ async function visit(src: string, test: any): Promise<string> {
visitImport(item);
}
if (item.type === "ExpressionStatement") {
- visitExpression(item, test);
+ visitExpression(item);
}
}
- const header = `// Copyright 2018+ the Deno authors. All rights reserved. MIT license.
-// https://raw.githubusercontent.com/denoland/deno/main/cli/tests/${test.remotePath}
-\n`;
+ const url = new URL(test.remotePath, baseUrl);
+ const header = `// Copyright 2018+ the Deno authors. All rights reserved. MIT license.\n// ${url}\n`;
const { code } = await print(ast, {
isModule: true,
});
@@ -75,7 +75,7 @@ function visitImport(item: ImportDeclaration): void {
}*/
}
-function visitExpression(item: ExpressionStatement, test: any): void {
+function visitExpression(item: ExpressionStatement): void {
if (
item.expression.type === "CallExpression" &&
item.expression.callee.type === "MemberExpression" &&
@@ -92,8 +92,10 @@ function visitTest(item: CallExpression): void {
for (const argument of item.arguments) {
if (argument.expression.type === "FunctionExpression") {
const fn = argument.expression.identifier?.value;
- for (const test of tests) {
- if (test.skip && test.skip.includes(fn)) {
+ if (fn) {
+ const pattern = new RegExp(tests.flatMap((test) => test.skip ?? []).join("|"), "i");
+ if (pattern.test(fn)) {
+ // @ts-ignore
item.callee.property.value = "test.ignore";
}
}
diff --git a/test/js/deno/url/url.test.ts b/test/js/deno/url/url.test.ts
new file mode 100644
index 000000000..66d6e853d
--- /dev/null
+++ b/test/js/deno/url/url.test.ts
@@ -0,0 +1,362 @@
+// Copyright 2018+ the Deno authors. All rights reserved. MIT license.
+// https://raw.githubusercontent.com/denoland/deno/main/cli/tests/unit/url_test.ts
+import { assert, assertEquals, assertStrictEquals, assertThrows } from "deno:harness";
+Deno.test(function urlParsing() {
+ const url = new URL("https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat");
+ assertEquals(url.hash, "#qat");
+ assertEquals(url.host, "baz.qat:8000");
+ assertEquals(url.hostname, "baz.qat");
+ assertEquals(url.href, "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat");
+ assertEquals(url.origin, "https://baz.qat:8000");
+ assertEquals(url.password, "bar");
+ assertEquals(url.pathname, "/qux/quux");
+ assertEquals(url.port, "8000");
+ assertEquals(url.protocol, "https:");
+ assertEquals(url.search, "?foo=bar&baz=12");
+ assertEquals(url.searchParams.getAll("foo"), [
+ "bar"
+ ]);
+ assertEquals(url.searchParams.getAll("baz"), [
+ "12"
+ ]);
+ assertEquals(url.username, "foo");
+ assertEquals(String(url), "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat");
+});
+Deno.test(function urlProtocolParsing() {
+ assertEquals(new URL("Aa+-.1://foo").protocol, "aa+-.1:");
+ assertEquals(new URL("aA+-.1://foo").protocol, "aa+-.1:");
+ assertThrows(()=>new URL("1://foo"), TypeError, "Invalid URL: '1://foo'");
+ assertThrows(()=>new URL("+://foo"), TypeError, "Invalid URL: '+://foo'");
+ assertThrows(()=>new URL("-://foo"), TypeError, "Invalid URL: '-://foo'");
+ assertThrows(()=>new URL(".://foo"), TypeError, "Invalid URL: '.://foo'");
+ assertThrows(()=>new URL("_://foo"), TypeError, "Invalid URL: '_://foo'");
+ assertThrows(()=>new URL("=://foo"), TypeError, "Invalid URL: '=://foo'");
+ assertThrows(()=>new URL("!://foo"), TypeError, "Invalid URL: '!://foo'");
+ assertThrows(()=>new URL(`"://foo`), TypeError, `Invalid URL: '"://foo'`);
+ assertThrows(()=>new URL("$://foo"), TypeError, "Invalid URL: '$://foo'");
+ assertThrows(()=>new URL("%://foo"), TypeError, "Invalid URL: '%://foo'");
+ assertThrows(()=>new URL("^://foo"), TypeError, "Invalid URL: '^://foo'");
+ assertThrows(()=>new URL("*://foo"), TypeError, "Invalid URL: '*://foo'");
+ assertThrows(()=>new URL("*://foo"), TypeError, "Invalid URL: '*://foo'");
+ assertThrows(()=>new URL("!:", "*://foo"), TypeError, "Invalid URL: '!:' with base '*://foo'");
+});
+Deno.test(function urlAuthenticationParsing() {
+ const specialUrl = new URL("http://foo:bar@baz");
+ assertEquals(specialUrl.username, "foo");
+ assertEquals(specialUrl.password, "bar");
+ assertEquals(specialUrl.hostname, "baz");
+ assertThrows(()=>new URL("file://foo:bar@baz"), TypeError, "Invalid URL");
+ const nonSpecialUrl = new URL("abcd://foo:bar@baz");
+ assertEquals(nonSpecialUrl.username, "foo");
+ assertEquals(nonSpecialUrl.password, "bar");
+ assertEquals(nonSpecialUrl.hostname, "baz");
+});
+Deno.test(function urlHostnameParsing() {
+ assertEquals(new URL("http://[::1]").hostname, "[::1]");
+ assertEquals(new URL("file://[::1]").hostname, "[::1]");
+ assertEquals(new URL("abcd://[::1]").hostname, "[::1]");
+ assertEquals(new URL("http://[0:f:0:0:f:f:0:0]").hostname, "[0:f::f:f:0:0]");
+ assertThrows(()=>new URL("http:// a"), TypeError, "Invalid URL");
+ assertThrows(()=>new URL("file:// a"), TypeError, "Invalid URL");
+ assertThrows(()=>new URL("abcd:// a"), TypeError, "Invalid URL");
+ assertThrows(()=>new URL("http://%"), TypeError, "Invalid URL");
+ assertThrows(()=>new URL("file://%"), TypeError, "Invalid URL");
+ assertEquals(new URL("abcd://%").hostname, "%");
+ assertEquals(new URL("http://%21").hostname, "!");
+ assertEquals(new URL("file://%21").hostname, "!");
+ assertEquals(new URL("abcd://%21").hostname, "%21");
+ assertEquals(new URL("http://260").hostname, "0.0.1.4");
+ assertEquals(new URL("file://260").hostname, "0.0.1.4");
+ assertEquals(new URL("abcd://260").hostname, "260");
+ assertEquals(new URL("http://255.0.0.0").hostname, "255.0.0.0");
+ assertThrows(()=>new URL("http://256.0.0.0"), TypeError, "Invalid URL");
+ assertEquals(new URL("http://0.255.0.0").hostname, "0.255.0.0");
+ assertThrows(()=>new URL("http://0.256.0.0"), TypeError, "Invalid URL");
+ assertEquals(new URL("http://0.0.255.0").hostname, "0.0.255.0");
+ assertThrows(()=>new URL("http://0.0.256.0"), TypeError, "Invalid URL");
+ assertEquals(new URL("http://0.0.0.255").hostname, "0.0.0.255");
+ assertThrows(()=>new URL("http://0.0.0.256"), TypeError, "Invalid URL");
+ assertEquals(new URL("http://0.0.65535").hostname, "0.0.255.255");
+ assertThrows(()=>new URL("http://0.0.65536"), TypeError, "Invalid URL");
+ assertEquals(new URL("http://0.16777215").hostname, "0.255.255.255");
+ assertThrows(()=>new URL("http://0.16777216"), TypeError, "Invalid URL");
+ assertEquals(new URL("http://4294967295").hostname, "255.255.255.255");
+ assertThrows(()=>new URL("http://4294967296"), TypeError, "Invalid URL");
+});
+Deno.test(function urlPortParsing() {
+ const specialUrl = new URL("http://foo:8000");
+ assertEquals(specialUrl.hostname, "foo");
+ assertEquals(specialUrl.port, "8000");
+ assertThrows(()=>new URL("file://foo:8000"), TypeError, "Invalid URL");
+ const nonSpecialUrl = new URL("abcd://foo:8000");
+ assertEquals(nonSpecialUrl.hostname, "foo");
+ assertEquals(nonSpecialUrl.port, "8000");
+});
+Deno.test(function urlModifications() {
+ const url = new URL("https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat");
+ url.hash = "";
+ assertEquals(url.href, "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12");
+ url.host = "qat.baz:8080";
+ assertEquals(url.href, "https://foo:bar@qat.baz:8080/qux/quux?foo=bar&baz=12");
+ url.hostname = "foo.bar";
+ assertEquals(url.href, "https://foo:bar@foo.bar:8080/qux/quux?foo=bar&baz=12");
+ url.password = "qux";
+ assertEquals(url.href, "https://foo:qux@foo.bar:8080/qux/quux?foo=bar&baz=12");
+ url.pathname = "/foo/bar%qat";
+ assertEquals(url.href, "https://foo:qux@foo.bar:8080/foo/bar%qat?foo=bar&baz=12");
+ url.port = "";
+ assertEquals(url.href, "https://foo:qux@foo.bar/foo/bar%qat?foo=bar&baz=12");
+ url.protocol = "http:";
+ assertEquals(url.href, "http://foo:qux@foo.bar/foo/bar%qat?foo=bar&baz=12");
+ url.search = "?foo=bar&foo=baz";
+ assertEquals(url.href, "http://foo:qux@foo.bar/foo/bar%qat?foo=bar&foo=baz");
+ assertEquals(url.searchParams.getAll("foo"), [
+ "bar",
+ "baz"
+ ]);
+ url.username = "foo@bar";
+ assertEquals(url.href, "http://foo%40bar:qux@foo.bar/foo/bar%qat?foo=bar&foo=baz");
+ url.searchParams.set("bar", "qat");
+ assertEquals(url.href, "http://foo%40bar:qux@foo.bar/foo/bar%qat?foo=bar&foo=baz&bar=qat");
+ url.searchParams.delete("foo");
+ assertEquals(url.href, "http://foo%40bar:qux@foo.bar/foo/bar%qat?bar=qat");
+ url.searchParams.append("foo", "bar");
+ assertEquals(url.href, "http://foo%40bar:qux@foo.bar/foo/bar%qat?bar=qat&foo=bar");
+});
+Deno.test(function urlModifyHref() {
+ const url = new URL("http://example.com/");
+ url.href = "https://foo:bar@example.com:8080/baz/qat#qux";
+ assertEquals(url.protocol, "https:");
+ assertEquals(url.username, "foo");
+ assertEquals(url.password, "bar");
+ assertEquals(url.host, "example.com:8080");
+ assertEquals(url.hostname, "example.com");
+ assertEquals(url.pathname, "/baz/qat");
+ assertEquals(url.hash, "#qux");
+});
+Deno.test(function urlNormalize() {
+ const url = new URL("http://example.com");
+ assertEquals(url.pathname, "/");
+ assertEquals(url.href, "http://example.com/");
+});
+Deno.test(function urlModifyPathname() {
+ const url = new URL("http://foo.bar/baz%qat/qux%quux");
+ assertEquals(url.pathname, "/baz%qat/qux%quux");
+ url.pathname = url.pathname;
+ assertEquals(url.pathname, "/baz%qat/qux%quux");
+ url.pathname = "baz#qat qux";
+ assertEquals(url.pathname, "/baz%23qat%20qux");
+ url.pathname = url.pathname;
+ assertEquals(url.pathname, "/baz%23qat%20qux");
+ url.pathname = "\\a\\b\\c";
+ assertEquals(url.pathname, "/a/b/c");
+});
+Deno.test(function urlModifyHash() {
+ const url = new URL("http://foo.bar");
+ url.hash = "%foo bar/qat%qux#bar";
+ assertEquals(url.hash, "#%foo%20bar/qat%qux#bar");
+ url.hash = url.hash;
+ assertEquals(url.hash, "#%foo%20bar/qat%qux#bar");
+});
+Deno.test(function urlSearchParamsReuse() {
+ const url = new URL("https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat");
+ const sp = url.searchParams;
+ url.host = "baz.qat";
+ assert(sp === url.searchParams, "Search params should be reused.");
+});
+Deno.test(function urlBackSlashes() {
+ const url = new URL("https:\\\\foo:bar@baz.qat:8000\\qux\\quux?foo=bar&baz=12#qat");
+ assertEquals(url.href, "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat");
+});
+Deno.test(function urlProtocolSlashes() {
+ assertEquals(new URL("http:foo").href, "http://foo/");
+ assertEquals(new URL("http://foo").href, "http://foo/");
+ assertEquals(new URL("file:foo").href, "file:///foo");
+ assertEquals(new URL("file://foo").href, "file://foo/");
+ assertEquals(new URL("abcd:foo").href, "abcd:foo");
+ assertEquals(new URL("abcd://foo").href, "abcd://foo");
+});
+Deno.test(function urlRequireHost() {
+ assertEquals(new URL("file:///").href, "file:///");
+ assertThrows(()=>new URL("ftp:///"), TypeError, "Invalid URL");
+ assertThrows(()=>new URL("http:///"), TypeError, "Invalid URL");
+ assertThrows(()=>new URL("https:///"), TypeError, "Invalid URL");
+ assertThrows(()=>new URL("ws:///"), TypeError, "Invalid URL");
+ assertThrows(()=>new URL("wss:///"), TypeError, "Invalid URL");
+});
+Deno.test(function urlDriveLetter() {
+ assertEquals(new URL("file:///C:").href, "file:///C:");
+ assertEquals(new URL("file:///C:/").href, "file:///C:/");
+ assertEquals(new URL("file:///C:/..").href, "file:///C:/");
+ assertEquals(new URL("file://foo/C:").href, "file:///C:");
+});
+Deno.test(function urlHostnameUpperCase() {
+ assertEquals(new URL("http://EXAMPLE.COM").href, "http://example.com/");
+ assertEquals(new URL("abcd://EXAMPLE.COM").href, "abcd://EXAMPLE.COM");
+});
+Deno.test(function urlEmptyPath() {
+ assertEquals(new URL("http://foo").pathname, "/");
+ assertEquals(new URL("file://foo").pathname, "/");
+ assertEquals(new URL("abcd://foo").pathname, "");
+});
+Deno.test(function urlPathRepeatedSlashes() {
+ assertEquals(new URL("http://foo//bar//").pathname, "//bar//");
+ assertEquals(new URL("file://foo///bar//").pathname, "/bar//");
+ assertEquals(new URL("abcd://foo//bar//").pathname, "//bar//");
+});
+Deno.test(function urlTrim() {
+ assertEquals(new URL(" http://example.com ").href, "http://example.com/");
+});
+Deno.test(function urlEncoding() {
+ assertEquals(new URL("http://a !$&*()=,;+'\"@example.com").username, "a%20!$&*()%3D,%3B+'%22");
+ assertEquals(new URL("http://:a !$&*()=,;+'\"@example.com").password, "a%20!$&*()%3D,%3B+'%22");
+ assertEquals(new URL("http://mañana/c?d#e").hostname, "xn--maana-pta");
+ assertEquals(new URL("abcd://mañana/c?d#e").hostname, "ma%C3%B1ana");
+ assertEquals(new URL("http://example.com/a ~!@$&*()=:/,;+'\"\\").pathname, "/a%20~!@$&*()=:/,;+'%22/");
+ assertEquals(new URL("http://example.com?a ~!@$&*()=:/,;?+'\"\\").search, "?a%20~!@$&*()=:/,;?+%27%22\\");
+ assertEquals(new URL("abcd://example.com?a ~!@$&*()=:/,;?+'\"\\").search, "?a%20~!@$&*()=:/,;?+'%22\\");
+ assertEquals(new URL("http://example.com#a ~!@#$&*()=:/,;?+'\"\\").hash, "#a%20~!@#$&*()=:/,;?+'%22\\");
+});
+Deno.test(function urlBase() {
+ assertEquals(new URL("d", new URL("http://foo/a?b#c")).href, "http://foo/d");
+ assertEquals(new URL("", "http://foo/a/b?c#d").href, "http://foo/a/b?c");
+ assertEquals(new URL("", "file://foo/a/b?c#d").href, "file://foo/a/b?c");
+ assertEquals(new URL("", "abcd://foo/a/b?c#d").href, "abcd://foo/a/b?c");
+ assertEquals(new URL("#e", "http://foo/a/b?c#d").href, "http://foo/a/b?c#e");
+ assertEquals(new URL("#e", "file://foo/a/b?c#d").href, "file://foo/a/b?c#e");
+ assertEquals(new URL("#e", "abcd://foo/a/b?c#d").href, "abcd://foo/a/b?c#e");
+ assertEquals(new URL("?e", "http://foo/a/b?c#d").href, "http://foo/a/b?e");
+ assertEquals(new URL("?e", "file://foo/a/b?c#d").href, "file://foo/a/b?e");
+ assertEquals(new URL("?e", "abcd://foo/a/b?c#d").href, "abcd://foo/a/b?e");
+ assertEquals(new URL("e", "http://foo/a/b?c#d").href, "http://foo/a/e");
+ assertEquals(new URL("e", "file://foo/a/b?c#d").href, "file://foo/a/e");
+ assertEquals(new URL("e", "abcd://foo/a/b?c#d").href, "abcd://foo/a/e");
+ assertEquals(new URL(".", "http://foo/a/b?c#d").href, "http://foo/a/");
+ assertEquals(new URL(".", "file://foo/a/b?c#d").href, "file://foo/a/");
+ assertEquals(new URL(".", "abcd://foo/a/b?c#d").href, "abcd://foo/a/");
+ assertEquals(new URL("..", "http://foo/a/b?c#d").href, "http://foo/");
+ assertEquals(new URL("..", "file://foo/a/b?c#d").href, "file://foo/");
+ assertEquals(new URL("..", "abcd://foo/a/b?c#d").href, "abcd://foo/");
+ assertEquals(new URL("/e", "http://foo/a/b?c#d").href, "http://foo/e");
+ assertEquals(new URL("/e", "file://foo/a/b?c#d").href, "file://foo/e");
+ assertEquals(new URL("/e", "abcd://foo/a/b?c#d").href, "abcd://foo/e");
+ assertEquals(new URL("//bar", "http://foo/a/b?c#d").href, "http://bar/");
+ assertEquals(new URL("//bar", "file://foo/a/b?c#d").href, "file://bar/");
+ assertEquals(new URL("//bar", "abcd://foo/a/b?c#d").href, "abcd://bar");
+ assertEquals(new URL("efgh:", "http://foo/a/b?c#d").href, "efgh:");
+ assertEquals(new URL("efgh:", "file://foo/a/b?c#d").href, "efgh:");
+ assertEquals(new URL("efgh:", "abcd://foo/a/b?c#d").href, "efgh:");
+ assertEquals(new URL("/foo", "abcd:/").href, "abcd:/foo");
+});
+Deno.test(function urlDriveLetterBase() {
+ assertEquals(new URL("/b", "file:///C:/a/b").href, "file:///C:/b");
+ assertEquals(new URL("/D:", "file:///C:/a/b").href, "file:///D:");
+});
+Deno.test(function urlSameProtocolBase() {
+ assertEquals(new URL("http:", "http://foo/a").href, "http://foo/a");
+ assertEquals(new URL("file:", "file://foo/a").href, "file://foo/a");
+ assertEquals(new URL("abcd:", "abcd://foo/a").href, "abcd:");
+ assertEquals(new URL("http:b", "http://foo/a").href, "http://foo/b");
+ assertEquals(new URL("file:b", "file://foo/a").href, "file://foo/b");
+ assertEquals(new URL("abcd:b", "abcd://foo/a").href, "abcd:b");
+});
+Deno.test(function deletingAllParamsRemovesQuestionMarkFromURL() {
+ const url = new URL("http://example.com/?param1&param2");
+ url.searchParams.delete("param1");
+ url.searchParams.delete("param2");
+ assertEquals(url.href, "http://example.com/");
+ assertEquals(url.search, "");
+});
+Deno.test(function removingNonExistentParamRemovesQuestionMarkFromURL() {
+ const url = new URL("http://example.com/?");
+ assertEquals(url.href, "http://example.com/?");
+ url.searchParams.delete("param1");
+ assertEquals(url.href, "http://example.com/");
+ assertEquals(url.search, "");
+});
+Deno.test(function sortingNonExistentParamRemovesQuestionMarkFromURL() {
+ const url = new URL("http://example.com/?");
+ assertEquals(url.href, "http://example.com/?");
+ url.searchParams.sort();
+ assertEquals(url.href, "http://example.com/");
+ assertEquals(url.search, "");
+});
+Deno.test.ignore(function customInspectFunction() {
+ const url = new URL("http://example.com/?");
+ assertEquals(Deno.inspect(url), `URL {
+ href: "http://example.com/?",
+ origin: "http://example.com",
+ protocol: "http:",
+ username: "",
+ password: "",
+ host: "example.com",
+ hostname: "example.com",
+ port: "",
+ pathname: "/",
+ hash: "",
+ search: ""
+}`);
+});
+Deno.test(function protocolNotHttpOrFile() {
+ const url = new URL("about:blank");
+ assertEquals(url.href, "about:blank");
+ assertEquals(url.protocol, "about:");
+ assertEquals(url.origin, "null");
+});
+Deno.test(function throwForInvalidPortConstructor() {
+ const urls = [
+ `https://baz.qat:${2 ** 16}`,
+ "https://baz.qat:-32",
+ "https://baz.qat:deno",
+ "https://baz.qat:9land",
+ "https://baz.qat:10.5"
+ ];
+ for (const url of urls){
+ assertThrows(()=>new URL(url), TypeError, "Invalid URL");
+ }
+ new URL("https://baz.qat:65535");
+ new URL("https://baz.qat:0");
+});
+Deno.test(function doNotOverridePortIfInvalid() {
+ const initialPort = "3000";
+ const url = new URL(`https://deno.land:${initialPort}`);
+ url.port = `${2 ** 16}`;
+ assertEquals(url.port, initialPort);
+});
+Deno.test(function emptyPortForSchemeDefaultPort() {
+ const nonDefaultPort = "3500";
+ const url = new URL("ftp://baz.qat:21");
+ assertEquals(url.port, "");
+ url.port = nonDefaultPort;
+ assertEquals(url.port, nonDefaultPort);
+ url.port = "21";
+ assertEquals(url.port, "");
+ url.protocol = "http";
+ assertEquals(url.port, "");
+ const url2 = new URL("https://baz.qat:443");
+ assertEquals(url2.port, "");
+ url2.port = nonDefaultPort;
+ assertEquals(url2.port, nonDefaultPort);
+ url2.port = "443";
+ assertEquals(url2.port, "");
+ url2.protocol = "http";
+ assertEquals(url2.port, "");
+});
+Deno.test(function assigningPortPropertyAffectsReceiverOnly() {
+ const u1 = new URL("http://google.com/");
+ const u2 = new URL(u1 as any);
+ u2.port = "123";
+ assertStrictEquals(u1.port, "");
+ assertStrictEquals(u2.port, "123");
+});
+Deno.test(function urlSearchParamsIdentityPreserved() {
+ const u = new URL("http://foo.com/");
+ const sp1 = u.searchParams;
+ u.href = "http://bar.com/?baz=42";
+ const sp2 = u.searchParams;
+ assertStrictEquals(sp1, sp2);
+});
+Deno.test(function urlTakeURLObjectAsParameter() {
+ const url = new URL(new URL("https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat"));
+ assertEquals(url.href, "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat");
+});
diff --git a/test/js/deno/url/urlsearchparams.test.ts b/test/js/deno/url/urlsearchparams.test.ts
new file mode 100644
index 000000000..792a92a54
--- /dev/null
+++ b/test/js/deno/url/urlsearchparams.test.ts
@@ -0,0 +1,364 @@
+// Copyright 2018+ the Deno authors. All rights reserved. MIT license.
+// https://raw.githubusercontent.com/denoland/deno/main/cli/tests/unit/url_search_params_test.ts
+import { assert, assertEquals } from "deno:harness";
+Deno.test(function urlSearchParamsWithMultipleSpaces() {
+ const init = {
+ str: "this string has spaces in it"
+ };
+ const searchParams = new URLSearchParams(init).toString();
+ assertEquals(searchParams, "str=this+string+has+spaces+in+it");
+});
+Deno.test(function urlSearchParamsWithExclamation() {
+ const init = [
+ [
+ "str",
+ "hello, world!"
+ ]
+ ];
+ const searchParams = new URLSearchParams(init).toString();
+ assertEquals(searchParams, "str=hello%2C+world%21");
+});
+Deno.test(function urlSearchParamsWithQuotes() {
+ const init = [
+ [
+ "str",
+ "'hello world'"
+ ]
+ ];
+ const searchParams = new URLSearchParams(init).toString();
+ assertEquals(searchParams, "str=%27hello+world%27");
+});
+Deno.test(function urlSearchParamsWithBraket() {
+ const init = [
+ [
+ "str",
+ "(hello world)"
+ ]
+ ];
+ const searchParams = new URLSearchParams(init).toString();
+ assertEquals(searchParams, "str=%28hello+world%29");
+});
+Deno.test(function urlSearchParamsWithTilde() {
+ const init = [
+ [
+ "str",
+ "hello~world"
+ ]
+ ];
+ const searchParams = new URLSearchParams(init).toString();
+ assertEquals(searchParams, "str=hello%7Eworld");
+});
+Deno.test(function urlSearchParamsInitString() {
+ const init = "c=4&a=2&b=3&%C3%A1=1";
+ const searchParams = new URLSearchParams(init);
+ assert(init === searchParams.toString(), "The init query string does not match");
+});
+Deno.test(function urlSearchParamsInitStringWithPlusCharacter() {
+ let params = new URLSearchParams("q=a+b");
+ assertEquals(params.toString(), "q=a+b");
+ assertEquals(params.get("q"), "a b");
+ params = new URLSearchParams("q=a+b+c");
+ assertEquals(params.toString(), "q=a+b+c");
+ assertEquals(params.get("q"), "a b c");
+});
+Deno.test(function urlSearchParamsInitStringWithMalformedParams() {
+ let params = new URLSearchParams("id=0&value=%");
+ assert(params != null, "constructor returned non-null value.");
+ assert(params.has("id"), 'Search params object has name "id"');
+ assert(params.has("value"), 'Search params object has name "value"');
+ assertEquals(params.get("id"), "0");
+ assertEquals(params.get("value"), "%");
+ params = new URLSearchParams("b=%2sf%2a");
+ assert(params != null, "constructor returned non-null value.");
+ assert(params.has("b"), 'Search params object has name "b"');
+ assertEquals(params.get("b"), "%2sf*");
+ params = new URLSearchParams("b=%2%2af%2a");
+ assert(params != null, "constructor returned non-null value.");
+ assert(params.has("b"), 'Search params object has name "b"');
+ assertEquals(params.get("b"), "%2*f*");
+ params = new URLSearchParams("b=%%2a");
+ assert(params != null, "constructor returned non-null value.");
+ assert(params.has("b"), 'Search params object has name "b"');
+ assertEquals(params.get("b"), "%*");
+});
+Deno.test(function urlSearchParamsInitIterable() {
+ const init = [
+ [
+ "a",
+ "54"
+ ],
+ [
+ "b",
+ "true"
+ ]
+ ];
+ const searchParams = new URLSearchParams(init);
+ assertEquals(searchParams.toString(), "a=54&b=true");
+});
+Deno.test(function urlSearchParamsInitRecord() {
+ const init = {
+ a: "54",
+ b: "true"
+ };
+ const searchParams = new URLSearchParams(init);
+ assertEquals(searchParams.toString(), "a=54&b=true");
+});
+Deno.test(function urlSearchParamsInit() {
+ const params1 = new URLSearchParams("a=b");
+ assertEquals(params1.toString(), "a=b");
+ const params2 = new URLSearchParams(params1);
+ assertEquals(params2.toString(), "a=b");
+});
+Deno.test(function urlSearchParamsAppendSuccess() {
+ const searchParams = new URLSearchParams();
+ searchParams.append("a", "true");
+ assertEquals(searchParams.toString(), "a=true");
+});
+Deno.test(function urlSearchParamsDeleteSuccess() {
+ const init = "a=54&b=true";
+ const searchParams = new URLSearchParams(init);
+ searchParams.delete("b");
+ assertEquals(searchParams.toString(), "a=54");
+});
+Deno.test(function urlSearchParamsGetAllSuccess() {
+ const init = "a=54&b=true&a=true";
+ const searchParams = new URLSearchParams(init);
+ assertEquals(searchParams.getAll("a"), [
+ "54",
+ "true"
+ ]);
+ assertEquals(searchParams.getAll("b"), [
+ "true"
+ ]);
+ assertEquals(searchParams.getAll("c"), []);
+});
+Deno.test(function urlSearchParamsGetSuccess() {
+ const init = "a=54&b=true&a=true";
+ const searchParams = new URLSearchParams(init);
+ assertEquals(searchParams.get("a"), "54");
+ assertEquals(searchParams.get("b"), "true");
+ assertEquals(searchParams.get("c"), null);
+});
+Deno.test(function urlSearchParamsHasSuccess() {
+ const init = "a=54&b=true&a=true";
+ const searchParams = new URLSearchParams(init);
+ assert(searchParams.has("a"));
+ assert(searchParams.has("b"));
+ assert(!searchParams.has("c"));
+});
+Deno.test(function urlSearchParamsSetReplaceFirstAndRemoveOthers() {
+ const init = "a=54&b=true&a=true";
+ const searchParams = new URLSearchParams(init);
+ searchParams.set("a", "false");
+ assertEquals(searchParams.toString(), "a=false&b=true");
+});
+Deno.test(function urlSearchParamsSetAppendNew() {
+ const init = "a=54&b=true&a=true";
+ const searchParams = new URLSearchParams(init);
+ searchParams.set("c", "foo");
+ assertEquals(searchParams.toString(), "a=54&b=true&a=true&c=foo");
+});
+Deno.test(function urlSearchParamsSortSuccess() {
+ const init = "c=4&a=2&b=3&a=1";
+ const searchParams = new URLSearchParams(init);
+ searchParams.sort();
+ assertEquals(searchParams.toString(), "a=2&a=1&b=3&c=4");
+});
+Deno.test(function urlSearchParamsForEachSuccess() {
+ const init = [
+ [
+ "a",
+ "54"
+ ],
+ [
+ "b",
+ "true"
+ ]
+ ];
+ const searchParams = new URLSearchParams(init);
+ let callNum = 0;
+ searchParams.forEach((value, key, parent)=>{
+ assertEquals(searchParams, parent);
+ assertEquals(value, init[callNum][1]);
+ assertEquals(key, init[callNum][0]);
+ callNum++;
+ });
+ assertEquals(callNum, init.length);
+});
+Deno.test(function urlSearchParamsMissingName() {
+ const init = "=4";
+ const searchParams = new URLSearchParams(init);
+ assertEquals(searchParams.get(""), "4");
+ assertEquals(searchParams.toString(), "=4");
+});
+Deno.test(function urlSearchParamsMissingValue() {
+ const init = "4=";
+ const searchParams = new URLSearchParams(init);
+ assertEquals(searchParams.get("4"), "");
+ assertEquals(searchParams.toString(), "4=");
+});
+Deno.test(function urlSearchParamsMissingEqualSign() {
+ const init = "4";
+ const searchParams = new URLSearchParams(init);
+ assertEquals(searchParams.get("4"), "");
+ assertEquals(searchParams.toString(), "4=");
+});
+Deno.test(function urlSearchParamsMissingPair() {
+ const init = "c=4&&a=54&";
+ const searchParams = new URLSearchParams(init);
+ assertEquals(searchParams.toString(), "c=4&a=54");
+});
+Deno.test(function urlSearchParamsForShortEncodedChar() {
+ const init = {
+ linefeed: "\n",
+ tab: "\t"
+ };
+ const searchParams = new URLSearchParams(init);
+ assertEquals(searchParams.toString(), "linefeed=%0A&tab=%09");
+});
+Deno.test(function urlSearchParamsShouldThrowTypeError() {
+ let hasThrown = 0;
+ try {
+ new URLSearchParams([
+ [
+ "1"
+ ]
+ ]);
+ hasThrown = 1;
+ } catch (err) {
+ if (err instanceof TypeError) {
+ hasThrown = 2;
+ } else {
+ hasThrown = 3;
+ }
+ }
+ assertEquals(hasThrown, 2);
+ try {
+ new URLSearchParams([
+ [
+ "1",
+ "2",
+ "3"
+ ]
+ ]);
+ hasThrown = 1;
+ } catch (err) {
+ if (err instanceof TypeError) {
+ hasThrown = 2;
+ } else {
+ hasThrown = 3;
+ }
+ }
+ assertEquals(hasThrown, 2);
+});
+Deno.test(function urlSearchParamsAppendArgumentsCheck() {
+ const methodRequireOneParam = [
+ "delete",
+ "getAll",
+ "get",
+ "has",
+ "forEach"
+ ];
+ const methodRequireTwoParams = [
+ "append",
+ "set"
+ ];
+ methodRequireOneParam.concat(methodRequireTwoParams).forEach((method: string)=>{
+ const searchParams = new URLSearchParams();
+ let hasThrown = 0;
+ try {
+ (searchParams as any)[method]();
+ hasThrown = 1;
+ } catch (err) {
+ if (err instanceof TypeError) {
+ hasThrown = 2;
+ } else {
+ hasThrown = 3;
+ }
+ }
+ assertEquals(hasThrown, 2);
+ });
+ methodRequireTwoParams.forEach((method: string)=>{
+ const searchParams = new URLSearchParams();
+ let hasThrown = 0;
+ try {
+ (searchParams as any)[method]("foo");
+ hasThrown = 1;
+ } catch (err) {
+ if (err instanceof TypeError) {
+ hasThrown = 2;
+ } else {
+ hasThrown = 3;
+ }
+ }
+ assertEquals(hasThrown, 2);
+ });
+});
+Deno.test(function urlSearchParamsDeletingAppendedMultiple() {
+ const params = new URLSearchParams();
+ params.append("first", (1 as unknown) as string);
+ assert(params.has("first"));
+ assertEquals(params.get("first"), "1");
+ params.delete("first");
+ assertEquals(params.has("first"), false);
+ params.append("first", (1 as unknown) as string);
+ params.append("first", (10 as unknown) as string);
+ params.delete("first");
+ assertEquals(params.has("first"), false);
+});
+Deno.test(function urlSearchParamsCustomSymbolIterator() {
+ const params = new URLSearchParams();
+ params[Symbol.iterator] = function*(): IterableIterator<[string, string]> {
+ yield [
+ "a",
+ "b"
+ ];
+ };
+ const params1 = new URLSearchParams((params as unknown) as string[][]);
+ assertEquals(params1.get("a"), "b");
+});
+Deno.test(function urlSearchParamsCustomSymbolIteratorWithNonStringParams() {
+ const params = {};
+ (params as any)[Symbol.iterator] = function*(): IterableIterator<[number, number]> {
+ yield [
+ 1,
+ 2
+ ];
+ };
+ const params1 = new URLSearchParams((params as unknown) as string[][]);
+ assertEquals(params1.get("1"), "2");
+});
+Deno.test(function urlSearchParamsOverridingAppendNotChangeConstructorAndSet() {
+ let overridedAppendCalled = 0;
+ class CustomSearchParams extends URLSearchParams {
+ append(name: string, value: string) {
+ ++overridedAppendCalled;
+ super.append(name, value);
+ }
+ }
+ new CustomSearchParams("foo=bar");
+ new CustomSearchParams([
+ [
+ "foo",
+ "bar"
+ ]
+ ]);
+ new CustomSearchParams(new CustomSearchParams({
+ foo: "bar"
+ }));
+ new CustomSearchParams().set("foo", "bar");
+ assertEquals(overridedAppendCalled, 0);
+});
+Deno.test(function urlSearchParamsOverridingEntriesNotChangeForEach() {
+ class CustomSearchParams extends URLSearchParams {
+ *entries(): IterableIterator<[string, string]> {
+ yield* [];
+ }
+ }
+ let loopCount = 0;
+ const params = new CustomSearchParams({
+ foo: "bar"
+ });
+ params.forEach(()=>void ++loopCount);
+ assertEquals(loopCount, 1);
+});
diff --git a/test/js/deno/v8/error.test.ts b/test/js/deno/v8/error.test.ts
new file mode 100644
index 000000000..d036c5eb9
--- /dev/null
+++ b/test/js/deno/v8/error.test.ts
@@ -0,0 +1,39 @@
+// Copyright 2018+ the Deno authors. All rights reserved. MIT license.
+// https://raw.githubusercontent.com/denoland/deno/main/cli/tests/unit/error_stack_test.ts
+import { assertEquals, assertMatch } from "deno:harness";
+Deno.test(function errorStackMessageLine() {
+ const e1 = new Error();
+ e1.name = "Foo";
+ e1.message = "bar";
+ assertMatch(e1.stack!, /^Foo: bar\n/);
+ const e2 = new Error();
+ e2.name = "";
+ e2.message = "bar";
+ assertMatch(e2.stack!, /^bar\n/);
+ const e3 = new Error();
+ e3.name = "Foo";
+ e3.message = "";
+ assertMatch(e3.stack!, /^Foo\n/);
+ const e4 = new Error();
+ e4.name = "";
+ e4.message = "";
+ assertMatch(e4.stack!, /^\n/);
+ const e5 = new Error();
+ e5.name = undefined;
+ e5.message = undefined;
+ assertMatch(e5.stack!, /^Error\n/);
+ const e6 = new Error();
+ e6.name = null;
+ e6.message = null;
+ assertMatch(e6.stack!, /^null: null\n/);
+});
+Deno.test(function captureStackTrace() {
+ function foo() {
+ const error = new Error();
+ const stack1 = error.stack!;
+ Error.captureStackTrace(error, foo);
+ const stack2 = error.stack!;
+ assertEquals(stack2, stack1.replace(/(?<=^[^\n]*\n)[^\n]*\n/, ""));
+ }
+ foo();
+});
diff --git a/test/js/deno/websocket/websocket.test.ts b/test/js/deno/websocket/websocket.test.ts
new file mode 100644
index 000000000..f414d4adf
--- /dev/null
+++ b/test/js/deno/websocket/websocket.test.ts
@@ -0,0 +1,19 @@
+// Copyright 2018+ the Deno authors. All rights reserved. MIT license.
+// https://raw.githubusercontent.com/denoland/deno/main/cli/tests/unit/websocket_test.ts
+import { assertEquals, assertThrows, deferred, fail } from "deno:harness";
+Deno.test({
+ permissions: "none"
+}, function websocketPermissionless() {
+ assertThrows(()=>new WebSocket("ws://localhost"), Deno.errors.PermissionDenied);
+});
+Deno.test(async function websocketConstructorTakeURLObjectAsParameter() {
+ const promise = deferred();
+ const ws = new WebSocket(new URL("ws://localhost:4242/"));
+ assertEquals(ws.url, "ws://localhost:4242/");
+ ws.onerror = ()=>fail();
+ ws.onopen = ()=>ws.close();
+ ws.onclose = ()=>{
+ promise.resolve();
+ };
+ await promise;
+});