11cb0ef41Sopenharmony_ci// Flags: --expose-internals
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_ci// This tests Node.js-specific behaviors of TextDecoder
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci'use strict';
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ciconst common = require('../common');
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_ciconst assert = require('assert');
101cb0ef41Sopenharmony_ciconst { customInspectSymbol: inspect } = require('internal/util');
111cb0ef41Sopenharmony_ciconst util = require('util');
121cb0ef41Sopenharmony_ci
131cb0ef41Sopenharmony_ciconst buf = Buffer.from([0xef, 0xbb, 0xbf, 0x74, 0x65,
141cb0ef41Sopenharmony_ci                         0x73, 0x74, 0xe2, 0x82, 0xac]);
151cb0ef41Sopenharmony_ci
161cb0ef41Sopenharmony_ci// Make Sure TextDecoder exist
171cb0ef41Sopenharmony_ciassert(TextDecoder);
181cb0ef41Sopenharmony_ci
191cb0ef41Sopenharmony_ci// Test TextDecoder, UTF-8, fatal: false, ignoreBOM: false
201cb0ef41Sopenharmony_ci{
211cb0ef41Sopenharmony_ci  ['unicode-1-1-utf-8', 'utf8', 'utf-8'].forEach((i) => {
221cb0ef41Sopenharmony_ci    const dec = new TextDecoder(i);
231cb0ef41Sopenharmony_ci    assert.strictEqual(dec.encoding, 'utf-8');
241cb0ef41Sopenharmony_ci    const res = dec.decode(buf);
251cb0ef41Sopenharmony_ci    assert.strictEqual(res, 'test€');
261cb0ef41Sopenharmony_ci  });
271cb0ef41Sopenharmony_ci
281cb0ef41Sopenharmony_ci  ['unicode-1-1-utf-8', 'utf8', 'utf-8'].forEach((i) => {
291cb0ef41Sopenharmony_ci    const dec = new TextDecoder(i);
301cb0ef41Sopenharmony_ci    let res = '';
311cb0ef41Sopenharmony_ci    res += dec.decode(buf.slice(0, 8), { stream: true });
321cb0ef41Sopenharmony_ci    res += dec.decode(buf.slice(8));
331cb0ef41Sopenharmony_ci    assert.strictEqual(res, 'test€');
341cb0ef41Sopenharmony_ci  });
351cb0ef41Sopenharmony_ci}
361cb0ef41Sopenharmony_ci
371cb0ef41Sopenharmony_ci// Test TextDecoder, UTF-8, fatal: false, ignoreBOM: true
381cb0ef41Sopenharmony_ci{
391cb0ef41Sopenharmony_ci  ['unicode-1-1-utf-8', 'utf8', 'utf-8'].forEach((i) => {
401cb0ef41Sopenharmony_ci    const dec = new TextDecoder(i, { ignoreBOM: true });
411cb0ef41Sopenharmony_ci    const res = dec.decode(buf);
421cb0ef41Sopenharmony_ci    assert.strictEqual(res, '\ufefftest€');
431cb0ef41Sopenharmony_ci  });
441cb0ef41Sopenharmony_ci
451cb0ef41Sopenharmony_ci  ['unicode-1-1-utf-8', 'utf8', 'utf-8'].forEach((i) => {
461cb0ef41Sopenharmony_ci    const dec = new TextDecoder(i, { ignoreBOM: true });
471cb0ef41Sopenharmony_ci    let res = '';
481cb0ef41Sopenharmony_ci    res += dec.decode(buf.slice(0, 8), { stream: true });
491cb0ef41Sopenharmony_ci    res += dec.decode(buf.slice(8));
501cb0ef41Sopenharmony_ci    assert.strictEqual(res, '\ufefftest€');
511cb0ef41Sopenharmony_ci  });
521cb0ef41Sopenharmony_ci}
531cb0ef41Sopenharmony_ci
541cb0ef41Sopenharmony_ci// Invalid encoders
551cb0ef41Sopenharmony_ci{
561cb0ef41Sopenharmony_ci  ['meow', 'nonunicode', 'foo', 'bar'].forEach((fakeEncoding) => {
571cb0ef41Sopenharmony_ci    assert.throws(
581cb0ef41Sopenharmony_ci      () => { new TextDecoder(fakeEncoding); },
591cb0ef41Sopenharmony_ci      {
601cb0ef41Sopenharmony_ci        code: 'ERR_ENCODING_NOT_SUPPORTED',
611cb0ef41Sopenharmony_ci        name: 'RangeError'
621cb0ef41Sopenharmony_ci      }
631cb0ef41Sopenharmony_ci    );
641cb0ef41Sopenharmony_ci  });
651cb0ef41Sopenharmony_ci}
661cb0ef41Sopenharmony_ci
671cb0ef41Sopenharmony_ci// Test TextDecoder, UTF-8, fatal: true, ignoreBOM: false
681cb0ef41Sopenharmony_ciif (common.hasIntl) {
691cb0ef41Sopenharmony_ci  ['unicode-1-1-utf-8', 'utf8', 'utf-8'].forEach((i) => {
701cb0ef41Sopenharmony_ci    const dec = new TextDecoder(i, { fatal: true });
711cb0ef41Sopenharmony_ci    assert.throws(() => dec.decode(buf.slice(0, 8)),
721cb0ef41Sopenharmony_ci                  {
731cb0ef41Sopenharmony_ci                    code: 'ERR_ENCODING_INVALID_ENCODED_DATA',
741cb0ef41Sopenharmony_ci                    name: 'TypeError',
751cb0ef41Sopenharmony_ci                    message: 'The encoded data was not valid ' +
761cb0ef41Sopenharmony_ci                          'for encoding utf-8'
771cb0ef41Sopenharmony_ci                  });
781cb0ef41Sopenharmony_ci  });
791cb0ef41Sopenharmony_ci
801cb0ef41Sopenharmony_ci  ['unicode-1-1-utf-8', 'utf8', 'utf-8'].forEach((i) => {
811cb0ef41Sopenharmony_ci    const dec = new TextDecoder(i, { fatal: true });
821cb0ef41Sopenharmony_ci    dec.decode(buf.slice(0, 8), { stream: true });
831cb0ef41Sopenharmony_ci    dec.decode(buf.slice(8));
841cb0ef41Sopenharmony_ci  });
851cb0ef41Sopenharmony_ci} else {
861cb0ef41Sopenharmony_ci  assert.throws(
871cb0ef41Sopenharmony_ci    () => new TextDecoder('utf-8', { fatal: true }),
881cb0ef41Sopenharmony_ci    {
891cb0ef41Sopenharmony_ci      code: 'ERR_NO_ICU',
901cb0ef41Sopenharmony_ci      name: 'TypeError',
911cb0ef41Sopenharmony_ci      message: '"fatal" option is not supported on Node.js compiled without ICU'
921cb0ef41Sopenharmony_ci    });
931cb0ef41Sopenharmony_ci}
941cb0ef41Sopenharmony_ci
951cb0ef41Sopenharmony_ci// Test TextDecoder, label undefined, options null
961cb0ef41Sopenharmony_ci{
971cb0ef41Sopenharmony_ci  const dec = new TextDecoder(undefined, null);
981cb0ef41Sopenharmony_ci  assert.strictEqual(dec.encoding, 'utf-8');
991cb0ef41Sopenharmony_ci  assert.strictEqual(dec.fatal, false);
1001cb0ef41Sopenharmony_ci  assert.strictEqual(dec.ignoreBOM, false);
1011cb0ef41Sopenharmony_ci  assert.strictEqual(dec[Symbol.toStringTag], 'TextDecoder');
1021cb0ef41Sopenharmony_ci}
1031cb0ef41Sopenharmony_ci
1041cb0ef41Sopenharmony_ci// Test TextDecoder, UTF-16le
1051cb0ef41Sopenharmony_ci{
1061cb0ef41Sopenharmony_ci  const dec = new TextDecoder('utf-16le');
1071cb0ef41Sopenharmony_ci  const res = dec.decode(Buffer.from('test€', 'utf-16le'));
1081cb0ef41Sopenharmony_ci  assert.strictEqual(res, 'test€');
1091cb0ef41Sopenharmony_ci}
1101cb0ef41Sopenharmony_ci
1111cb0ef41Sopenharmony_ci// Test TextDecoder, UTF-16be
1121cb0ef41Sopenharmony_ciif (common.hasIntl) {
1131cb0ef41Sopenharmony_ci  const dec = new TextDecoder('utf-16be');
1141cb0ef41Sopenharmony_ci  const res = dec.decode(Buffer.from('test€', 'utf-16le').swap16());
1151cb0ef41Sopenharmony_ci  assert.strictEqual(res, 'test€');
1161cb0ef41Sopenharmony_ci}
1171cb0ef41Sopenharmony_ci
1181cb0ef41Sopenharmony_ci// Test TextDecoder inspect with hidden fields
1191cb0ef41Sopenharmony_ci{
1201cb0ef41Sopenharmony_ci  const dec = new TextDecoder('utf-8', { ignoreBOM: true });
1211cb0ef41Sopenharmony_ci  if (common.hasIntl) {
1221cb0ef41Sopenharmony_ci    assert.strictEqual(
1231cb0ef41Sopenharmony_ci      util.inspect(dec, { showHidden: true }),
1241cb0ef41Sopenharmony_ci      'TextDecoder {\n' +
1251cb0ef41Sopenharmony_ci      '  encoding: \'utf-8\',\n' +
1261cb0ef41Sopenharmony_ci      '  fatal: false,\n' +
1271cb0ef41Sopenharmony_ci      '  ignoreBOM: true,\n' +
1281cb0ef41Sopenharmony_ci      '  [Symbol(flags)]: 4,\n' +
1291cb0ef41Sopenharmony_ci      '  [Symbol(handle)]: undefined\n' +
1301cb0ef41Sopenharmony_ci      '}'
1311cb0ef41Sopenharmony_ci    );
1321cb0ef41Sopenharmony_ci  } else {
1331cb0ef41Sopenharmony_ci    assert.strictEqual(
1341cb0ef41Sopenharmony_ci      util.inspect(dec, { showHidden: true }),
1351cb0ef41Sopenharmony_ci      'TextDecoder {\n' +
1361cb0ef41Sopenharmony_ci      "  encoding: 'utf-8',\n" +
1371cb0ef41Sopenharmony_ci      '  fatal: false,\n' +
1381cb0ef41Sopenharmony_ci      '  ignoreBOM: true,\n' +
1391cb0ef41Sopenharmony_ci      '  [Symbol(flags)]: 4,\n' +
1401cb0ef41Sopenharmony_ci      '  [Symbol(handle)]: StringDecoder {\n' +
1411cb0ef41Sopenharmony_ci      "    encoding: 'utf8',\n" +
1421cb0ef41Sopenharmony_ci      '    [Symbol(kNativeDecoder)]: <Buffer 00 00 00 00 00 00 01>\n' +
1431cb0ef41Sopenharmony_ci      '  }\n' +
1441cb0ef41Sopenharmony_ci      '}'
1451cb0ef41Sopenharmony_ci    );
1461cb0ef41Sopenharmony_ci  }
1471cb0ef41Sopenharmony_ci}
1481cb0ef41Sopenharmony_ci
1491cb0ef41Sopenharmony_ci
1501cb0ef41Sopenharmony_ci// Test TextDecoder inspect without hidden fields
1511cb0ef41Sopenharmony_ci{
1521cb0ef41Sopenharmony_ci  const dec = new TextDecoder('utf-8', { ignoreBOM: true });
1531cb0ef41Sopenharmony_ci  assert.strictEqual(
1541cb0ef41Sopenharmony_ci    util.inspect(dec, { showHidden: false }),
1551cb0ef41Sopenharmony_ci    'TextDecoder { encoding: \'utf-8\', fatal: false, ignoreBOM: true }'
1561cb0ef41Sopenharmony_ci  );
1571cb0ef41Sopenharmony_ci}
1581cb0ef41Sopenharmony_ci
1591cb0ef41Sopenharmony_ci// Test TextDecoder inspect with negative depth
1601cb0ef41Sopenharmony_ci{
1611cb0ef41Sopenharmony_ci  const dec = new TextDecoder();
1621cb0ef41Sopenharmony_ci  assert.strictEqual(util.inspect(dec, { depth: -1 }), '[TextDecoder]');
1631cb0ef41Sopenharmony_ci}
1641cb0ef41Sopenharmony_ci
1651cb0ef41Sopenharmony_ci{
1661cb0ef41Sopenharmony_ci  const inspectFn = TextDecoder.prototype[inspect];
1671cb0ef41Sopenharmony_ci  const decodeFn = TextDecoder.prototype.decode;
1681cb0ef41Sopenharmony_ci  const {
1691cb0ef41Sopenharmony_ci    encoding: { get: encodingGetter },
1701cb0ef41Sopenharmony_ci    fatal: { get: fatalGetter },
1711cb0ef41Sopenharmony_ci    ignoreBOM: { get: ignoreBOMGetter },
1721cb0ef41Sopenharmony_ci  } = Object.getOwnPropertyDescriptors(TextDecoder.prototype);
1731cb0ef41Sopenharmony_ci
1741cb0ef41Sopenharmony_ci  const instance = new TextDecoder();
1751cb0ef41Sopenharmony_ci
1761cb0ef41Sopenharmony_ci  const expectedError = {
1771cb0ef41Sopenharmony_ci    code: 'ERR_INVALID_THIS',
1781cb0ef41Sopenharmony_ci    name: 'TypeError',
1791cb0ef41Sopenharmony_ci    message: 'Value of "this" must be of type TextDecoder'
1801cb0ef41Sopenharmony_ci  };
1811cb0ef41Sopenharmony_ci
1821cb0ef41Sopenharmony_ci  inspectFn.call(instance, Infinity, {});
1831cb0ef41Sopenharmony_ci  decodeFn.call(instance);
1841cb0ef41Sopenharmony_ci  encodingGetter.call(instance);
1851cb0ef41Sopenharmony_ci  fatalGetter.call(instance);
1861cb0ef41Sopenharmony_ci  ignoreBOMGetter.call(instance);
1871cb0ef41Sopenharmony_ci
1881cb0ef41Sopenharmony_ci  const invalidThisArgs = [{}, [], true, 1, '', new TextEncoder()];
1891cb0ef41Sopenharmony_ci  invalidThisArgs.forEach((i) => {
1901cb0ef41Sopenharmony_ci    assert.throws(() => inspectFn.call(i, Infinity, {}), expectedError);
1911cb0ef41Sopenharmony_ci    assert.throws(() => decodeFn.call(i), expectedError);
1921cb0ef41Sopenharmony_ci    assert.throws(() => encodingGetter.call(i), expectedError);
1931cb0ef41Sopenharmony_ci    assert.throws(() => fatalGetter.call(i), expectedError);
1941cb0ef41Sopenharmony_ci    assert.throws(() => ignoreBOMGetter.call(i), expectedError);
1951cb0ef41Sopenharmony_ci  });
1961cb0ef41Sopenharmony_ci}
1971cb0ef41Sopenharmony_ci
1981cb0ef41Sopenharmony_ci{
1991cb0ef41Sopenharmony_ci  assert.throws(
2001cb0ef41Sopenharmony_ci    () => new TextDecoder('utf-8', 1),
2011cb0ef41Sopenharmony_ci    {
2021cb0ef41Sopenharmony_ci      code: 'ERR_INVALID_ARG_TYPE',
2031cb0ef41Sopenharmony_ci      name: 'TypeError'
2041cb0ef41Sopenharmony_ci    }
2051cb0ef41Sopenharmony_ci  );
2061cb0ef41Sopenharmony_ci}
2071cb0ef41Sopenharmony_ci
2081cb0ef41Sopenharmony_ci// Test TextDecoder for incomplete UTF-8 byte sequence.
2091cb0ef41Sopenharmony_ci{
2101cb0ef41Sopenharmony_ci  const decoder = new TextDecoder();
2111cb0ef41Sopenharmony_ci  const chunk = new Uint8Array([0x66, 0x6f, 0x6f, 0xed]);
2121cb0ef41Sopenharmony_ci  const str = decoder.decode(chunk);
2131cb0ef41Sopenharmony_ci  assert.strictEqual(str, 'foo\ufffd');
2141cb0ef41Sopenharmony_ci}
2151cb0ef41Sopenharmony_ci
2161cb0ef41Sopenharmony_ciif (common.hasIntl) {
2171cb0ef41Sopenharmony_ci  try {
2181cb0ef41Sopenharmony_ci    const decoder = new TextDecoder('Shift_JIS');
2191cb0ef41Sopenharmony_ci    const chunk = new Uint8Array([-1]);
2201cb0ef41Sopenharmony_ci    const str = decoder.decode(chunk);
2211cb0ef41Sopenharmony_ci    assert.strictEqual(str, '\ufffd');
2221cb0ef41Sopenharmony_ci  } catch (e) {
2231cb0ef41Sopenharmony_ci    // Encoding may not be available, e.g. small-icu builds
2241cb0ef41Sopenharmony_ci    assert.strictEqual(e.code, 'ERR_ENCODING_NOT_SUPPORTED');
2251cb0ef41Sopenharmony_ci  }
2261cb0ef41Sopenharmony_ci}
2271cb0ef41Sopenharmony_ci
2281cb0ef41Sopenharmony_ci{
2291cb0ef41Sopenharmony_ci  const buffer = new ArrayBuffer(1);
2301cb0ef41Sopenharmony_ci  new MessageChannel().port1.postMessage(buffer, [buffer]); // buffer is detached
2311cb0ef41Sopenharmony_ci  const decoder = new TextDecoder();
2321cb0ef41Sopenharmony_ci  assert.strictEqual(decoder.decode(buffer), '');
2331cb0ef41Sopenharmony_ci}
234