diff options
Diffstat (limited to 'test')
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¶m2"); +    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; +}); | 
