1// Flags: --expose-internals 2 3// This tests Node.js-specific behaviors of TextDecoder 4 5'use strict'; 6 7const common = require('../common'); 8 9const assert = require('assert'); 10const { customInspectSymbol: inspect } = require('internal/util'); 11const util = require('util'); 12 13const buf = Buffer.from([0xef, 0xbb, 0xbf, 0x74, 0x65, 14 0x73, 0x74, 0xe2, 0x82, 0xac]); 15 16// Make Sure TextDecoder exist 17assert(TextDecoder); 18 19// Test TextDecoder, UTF-8, fatal: false, ignoreBOM: false 20{ 21 ['unicode-1-1-utf-8', 'utf8', 'utf-8'].forEach((i) => { 22 const dec = new TextDecoder(i); 23 assert.strictEqual(dec.encoding, 'utf-8'); 24 const res = dec.decode(buf); 25 assert.strictEqual(res, 'test€'); 26 }); 27 28 ['unicode-1-1-utf-8', 'utf8', 'utf-8'].forEach((i) => { 29 const dec = new TextDecoder(i); 30 let res = ''; 31 res += dec.decode(buf.slice(0, 8), { stream: true }); 32 res += dec.decode(buf.slice(8)); 33 assert.strictEqual(res, 'test€'); 34 }); 35} 36 37// Test TextDecoder, UTF-8, fatal: false, ignoreBOM: true 38{ 39 ['unicode-1-1-utf-8', 'utf8', 'utf-8'].forEach((i) => { 40 const dec = new TextDecoder(i, { ignoreBOM: true }); 41 const res = dec.decode(buf); 42 assert.strictEqual(res, '\ufefftest€'); 43 }); 44 45 ['unicode-1-1-utf-8', 'utf8', 'utf-8'].forEach((i) => { 46 const dec = new TextDecoder(i, { ignoreBOM: true }); 47 let res = ''; 48 res += dec.decode(buf.slice(0, 8), { stream: true }); 49 res += dec.decode(buf.slice(8)); 50 assert.strictEqual(res, '\ufefftest€'); 51 }); 52} 53 54// Invalid encoders 55{ 56 ['meow', 'nonunicode', 'foo', 'bar'].forEach((fakeEncoding) => { 57 assert.throws( 58 () => { new TextDecoder(fakeEncoding); }, 59 { 60 code: 'ERR_ENCODING_NOT_SUPPORTED', 61 name: 'RangeError' 62 } 63 ); 64 }); 65} 66 67// Test TextDecoder, UTF-8, fatal: true, ignoreBOM: false 68if (common.hasIntl) { 69 ['unicode-1-1-utf-8', 'utf8', 'utf-8'].forEach((i) => { 70 const dec = new TextDecoder(i, { fatal: true }); 71 assert.throws(() => dec.decode(buf.slice(0, 8)), 72 { 73 code: 'ERR_ENCODING_INVALID_ENCODED_DATA', 74 name: 'TypeError', 75 message: 'The encoded data was not valid ' + 76 'for encoding utf-8' 77 }); 78 }); 79 80 ['unicode-1-1-utf-8', 'utf8', 'utf-8'].forEach((i) => { 81 const dec = new TextDecoder(i, { fatal: true }); 82 dec.decode(buf.slice(0, 8), { stream: true }); 83 dec.decode(buf.slice(8)); 84 }); 85} else { 86 assert.throws( 87 () => new TextDecoder('utf-8', { fatal: true }), 88 { 89 code: 'ERR_NO_ICU', 90 name: 'TypeError', 91 message: '"fatal" option is not supported on Node.js compiled without ICU' 92 }); 93} 94 95// Test TextDecoder, label undefined, options null 96{ 97 const dec = new TextDecoder(undefined, null); 98 assert.strictEqual(dec.encoding, 'utf-8'); 99 assert.strictEqual(dec.fatal, false); 100 assert.strictEqual(dec.ignoreBOM, false); 101 assert.strictEqual(dec[Symbol.toStringTag], 'TextDecoder'); 102} 103 104// Test TextDecoder, UTF-16le 105{ 106 const dec = new TextDecoder('utf-16le'); 107 const res = dec.decode(Buffer.from('test€', 'utf-16le')); 108 assert.strictEqual(res, 'test€'); 109} 110 111// Test TextDecoder, UTF-16be 112if (common.hasIntl) { 113 const dec = new TextDecoder('utf-16be'); 114 const res = dec.decode(Buffer.from('test€', 'utf-16le').swap16()); 115 assert.strictEqual(res, 'test€'); 116} 117 118// Test TextDecoder inspect with hidden fields 119{ 120 const dec = new TextDecoder('utf-8', { ignoreBOM: true }); 121 if (common.hasIntl) { 122 assert.strictEqual( 123 util.inspect(dec, { showHidden: true }), 124 'TextDecoder {\n' + 125 ' encoding: \'utf-8\',\n' + 126 ' fatal: false,\n' + 127 ' ignoreBOM: true,\n' + 128 ' [Symbol(flags)]: 4,\n' + 129 ' [Symbol(handle)]: undefined\n' + 130 '}' 131 ); 132 } else { 133 assert.strictEqual( 134 util.inspect(dec, { showHidden: true }), 135 'TextDecoder {\n' + 136 " encoding: 'utf-8',\n" + 137 ' fatal: false,\n' + 138 ' ignoreBOM: true,\n' + 139 ' [Symbol(flags)]: 4,\n' + 140 ' [Symbol(handle)]: StringDecoder {\n' + 141 " encoding: 'utf8',\n" + 142 ' [Symbol(kNativeDecoder)]: <Buffer 00 00 00 00 00 00 01>\n' + 143 ' }\n' + 144 '}' 145 ); 146 } 147} 148 149 150// Test TextDecoder inspect without hidden fields 151{ 152 const dec = new TextDecoder('utf-8', { ignoreBOM: true }); 153 assert.strictEqual( 154 util.inspect(dec, { showHidden: false }), 155 'TextDecoder { encoding: \'utf-8\', fatal: false, ignoreBOM: true }' 156 ); 157} 158 159// Test TextDecoder inspect with negative depth 160{ 161 const dec = new TextDecoder(); 162 assert.strictEqual(util.inspect(dec, { depth: -1 }), '[TextDecoder]'); 163} 164 165{ 166 const inspectFn = TextDecoder.prototype[inspect]; 167 const decodeFn = TextDecoder.prototype.decode; 168 const { 169 encoding: { get: encodingGetter }, 170 fatal: { get: fatalGetter }, 171 ignoreBOM: { get: ignoreBOMGetter }, 172 } = Object.getOwnPropertyDescriptors(TextDecoder.prototype); 173 174 const instance = new TextDecoder(); 175 176 const expectedError = { 177 code: 'ERR_INVALID_THIS', 178 name: 'TypeError', 179 message: 'Value of "this" must be of type TextDecoder' 180 }; 181 182 inspectFn.call(instance, Infinity, {}); 183 decodeFn.call(instance); 184 encodingGetter.call(instance); 185 fatalGetter.call(instance); 186 ignoreBOMGetter.call(instance); 187 188 const invalidThisArgs = [{}, [], true, 1, '', new TextEncoder()]; 189 invalidThisArgs.forEach((i) => { 190 assert.throws(() => inspectFn.call(i, Infinity, {}), expectedError); 191 assert.throws(() => decodeFn.call(i), expectedError); 192 assert.throws(() => encodingGetter.call(i), expectedError); 193 assert.throws(() => fatalGetter.call(i), expectedError); 194 assert.throws(() => ignoreBOMGetter.call(i), expectedError); 195 }); 196} 197 198{ 199 assert.throws( 200 () => new TextDecoder('utf-8', 1), 201 { 202 code: 'ERR_INVALID_ARG_TYPE', 203 name: 'TypeError' 204 } 205 ); 206} 207 208// Test TextDecoder for incomplete UTF-8 byte sequence. 209{ 210 const decoder = new TextDecoder(); 211 const chunk = new Uint8Array([0x66, 0x6f, 0x6f, 0xed]); 212 const str = decoder.decode(chunk); 213 assert.strictEqual(str, 'foo\ufffd'); 214} 215 216if (common.hasIntl) { 217 try { 218 const decoder = new TextDecoder('Shift_JIS'); 219 const chunk = new Uint8Array([-1]); 220 const str = decoder.decode(chunk); 221 assert.strictEqual(str, '\ufffd'); 222 } catch (e) { 223 // Encoding may not be available, e.g. small-icu builds 224 assert.strictEqual(e.code, 'ERR_ENCODING_NOT_SUPPORTED'); 225 } 226} 227 228{ 229 const buffer = new ArrayBuffer(1); 230 new MessageChannel().port1.postMessage(buffer, [buffer]); // buffer is detached 231 const decoder = new TextDecoder(); 232 assert.strictEqual(decoder.decode(buffer), ''); 233} 234