diff --git a/Uint8Array.prototype.toBase64/implementation.js b/Uint8Array.prototype.toBase64/implementation.js index f7bfff8..ec66165 100644 --- a/Uint8Array.prototype.toBase64/implementation.js +++ b/Uint8Array.prototype.toBase64/implementation.js @@ -3,8 +3,9 @@ var $TypeError = require('es-errors/type'); var Get = require('es-abstract/2023/Get'); -var ValidateUint8Array = require('../aos/ValidateUint8Array'); var GetOptionsObject = require('../aos/GetOptionsObject'); +var GetUint8ArrayBytes = require('../aos/GetUint8ArrayBytes'); +var ValidateUint8Array = require('../aos/ValidateUint8Array'); var alphabetFromIdentifier = require('../aos/helpers/alphabetFromIdentifier'); @@ -27,13 +28,16 @@ module.exports = function toBase64() { } if (typeof alphabet !== 'string') { - throw new $TypeError('`alphabet` is not a string: ' + alphabet); // step 6 + throw new $TypeError('`alphabet` is not a string: ' + typeof alphabet); // step 6 } if (alphabet !== 'base64' && alphabet !== 'base64url') { throw new $TypeError('Invalid alphabet'); // step 7 } + // eslint-disable-next-line no-unused-vars + var toEncode = GetUint8ArrayBytes(O); // step 8 + // if (alphabet === 'base64') { // step 8 // a. Let outAscii be the sequence of code points which results from encoding toEncode according to the base64 encoding specified in section 4 of RFC 4648. // } else { // step 9 diff --git a/package.json b/package.json index accd189..c2cefb8 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "@ljharb/eslint-config": "^21.1.0", "aud": "^2.0.4", "auto-changelog": "^2.4.0", + "available-typed-arrays": "^1.0.7", "eslint": "=8.8.0", "evalmd": "^0.0.19", "for-each": "^0.3.3", diff --git a/test/Uint8Array.prototype.toBase64.js b/test/Uint8Array.prototype.toBase64.js index cfffa6c..0a2b525 100644 --- a/test/Uint8Array.prototype.toBase64.js +++ b/test/Uint8Array.prototype.toBase64.js @@ -1,8 +1,15 @@ 'use strict'; +var availableTypedArrays = require('available-typed-arrays')(); +var callBind = require('call-bind'); var defineProperties = require('define-properties'); +var DetachArrayBuffer = require('es-abstract/2023/DetachArrayBuffer'); +var forEach = require('for-each'); +var isCore = require('is-core-module'); var test = require('tape'); -var callBind = require('call-bind'); + +/* globals postMessage: false */ +var canDetach = typeof structuredClone === 'function' || typeof postMessage === 'function' || isCore('worker_threads'); var index = require('../Uint8Array.prototype.toBase64'); var impl = require('../Uint8Array.prototype.toBase64/implementation'); @@ -55,6 +62,171 @@ module.exports = { 'invalid alphabet throws' ); + st.test('test262: test/built-ins/Uint8Array/prototype/toBase64/alphabet.js', function (s2t) { + s2t.equal(method(new Uint8Array([199, 239, 242])), 'x+/y'); + + s2t.equal(method(new Uint8Array([199, 239, 242]), { alphabet: 'base64' }), 'x+/y'); + + s2t.equal(method(new Uint8Array([199, 239, 242]), { alphabet: 'base64url' }), 'x-_y'); + + s2t['throws']( + function () { method(new Uint8Array([199, 239, 242]), { alphabet: 'other' }); }, + TypeError + ); + + s2t.end(); + }); + + st.test('test262: test/built-ins/Uint8Array/prototype/toBase64/receiver-not-uint8array.js', { + skip: defineProperties.supportsDescriptors + }, function (s2t) { + var options = {}; + s2t.intercept(options, 'alphabet', { + get: function () { + throw new EvalError('options.alphabet accessed despite incompatible receiver'); + } + }); + + forEach(availableTypedArrays, function (taName) { + if (taName === 'Uint8Array') { return; } + var TA = global[taName]; + var sample = new TA(2); + s2t['throws']( + function () { method(sample, options); }, + TypeError, + 'throws with ' + taName + ); + }); + + s2t['throws']( + function () { method([], options); }, + TypeError + ); + + s2t['throws']( + function () { method(options); }, + TypeError + ); + + s2t.end(); + }); + + st.test('test262: test/built-ins/Uint8Array/prototype/toBase64/detached-buffer.js', { + skip: !canDetach || !defineProperties.supportsDescriptors + }, function (s2t) { + var arr = new Uint8Array(2); + var receiverDetachingOptions = {}; + var results = s2t.intercept(receiverDetachingOptions, 'alphabet', { + get: function () { + DetachArrayBuffer(arr.buffer); + return 'base64'; + } + }); + + s2t['throws']( + function () { method(arr, receiverDetachingOptions); }, + TypeError + ); + s2t.deepEqual(results(), [ + { + type: 'get', + success: true, + value: 'base64', + args: [], + receiver: receiverDetachingOptions + } + ]); + + var detached = new Uint8Array(2); + DetachArrayBuffer(detached.buffer); + var sideEffectingOptions = {}; + var results2 = s2t.intercept(sideEffectingOptions, 'alphabet', { + get: function () { + return 'base64'; + } + }); + + s2t['throws']( + function () { method(detached, sideEffectingOptions); }, + TypeError + ); + s2t.deepEqual(results2(), [ + { + type: 'get', + success: true, + value: 'base64', + args: [], + receiver: sideEffectingOptions + } + ]); + + s2t.end(); + }); + + st.test('test262: test/built-ins/Uint8Array/prototype/toBase64/option-coercion.js', function (s2t) { + var throwyToString = {}; + var results = s2t.intercept(throwyToString, 'toString', { + value: function () { + throw new EvalError('toString called on alphabet value'); + } + }); + s2t['throws']( + function () { method(new Uint8Array(2), { alphabet: throwyToString }); }, + TypeError + ); + s2t.deepEqual(results(), []); + + var base64UrlOptions = {}; + var results2 = s2t.intercept(base64UrlOptions, 'alphabet', { + get: function () { + return 'base64url'; + } + }); + s2t.equal(method(new Uint8Array([199, 239, 242]), base64UrlOptions), 'x-_y'); + s2t.deepEqual(results2(), [ + { + type: 'get', + success: true, + value: 'base64url', + args: [], + receiver: base64UrlOptions + } + ]); + + // side-effects from the getter on the receiver are reflected in the result + var arr = new Uint8Array([0]); + var receiverMutatingOptions = {}; + var results3 = s2t.intercept(receiverMutatingOptions, 'alphabet', { + get: function () { + arr[0] = 255; + return 'base64'; + } + }); + var result = method(arr, receiverMutatingOptions); + s2t.equal(result, '/w=='); + s2t.equal(arr[0], 255); + s2t.deepEqual(results3(), [ + { + type: 'get', + success: true, + value: 'base64', + args: [], + receiver: receiverMutatingOptions + } + ]); + + s2t.end(); + }); + + // standard test vectors from https://datatracker.ietf.org/doc/html/rfc4648#section-10 + st.equal(method(new Uint8Array([])), ''); + st.equal(method(new Uint8Array([102])), 'Zg=='); + st.equal(method(new Uint8Array([102, 111])), 'Zm8='); + st.equal(method(new Uint8Array([102, 111, 111])), 'Zm9v'); + st.equal(method(new Uint8Array([102, 111, 111, 98])), 'Zm9vYg=='); + st.equal(method(new Uint8Array([102, 111, 111, 98, 97])), 'Zm9vYmE='); + st.equal(method(new Uint8Array([102, 111, 111, 98, 97, 114])), 'Zm9vYmFy'); + st.end(); }); },