11cb0ef41Sopenharmony_ci'use strict';
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_ci// Flags: --expose-internals
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ciconst common = require('../common');
61cb0ef41Sopenharmony_ciconst stream = require('stream');
71cb0ef41Sopenharmony_ciconst REPL = require('internal/repl');
81cb0ef41Sopenharmony_ciconst assert = require('assert');
91cb0ef41Sopenharmony_ciconst fs = require('fs');
101cb0ef41Sopenharmony_ciconst path = require('path');
111cb0ef41Sopenharmony_ciconst { inspect } = require('util');
121cb0ef41Sopenharmony_ci
131cb0ef41Sopenharmony_cicommon.skipIfDumbTerminal();
141cb0ef41Sopenharmony_ci
151cb0ef41Sopenharmony_ciconst tmpdir = require('../common/tmpdir');
161cb0ef41Sopenharmony_citmpdir.refresh();
171cb0ef41Sopenharmony_ci
181cb0ef41Sopenharmony_ciprocess.throwDeprecation = true;
191cb0ef41Sopenharmony_ci
201cb0ef41Sopenharmony_ciconst defaultHistoryPath = path.join(tmpdir.path, '.node_repl_history');
211cb0ef41Sopenharmony_ci
221cb0ef41Sopenharmony_ci// Create an input stream specialized for testing an array of actions
231cb0ef41Sopenharmony_ciclass ActionStream extends stream.Stream {
241cb0ef41Sopenharmony_ci  run(data) {
251cb0ef41Sopenharmony_ci    const _iter = data[Symbol.iterator]();
261cb0ef41Sopenharmony_ci    const doAction = () => {
271cb0ef41Sopenharmony_ci      const next = _iter.next();
281cb0ef41Sopenharmony_ci      if (next.done) {
291cb0ef41Sopenharmony_ci        // Close the repl. Note that it must have a clean prompt to do so.
301cb0ef41Sopenharmony_ci        this.emit('keypress', '', { ctrl: true, name: 'd' });
311cb0ef41Sopenharmony_ci        return;
321cb0ef41Sopenharmony_ci      }
331cb0ef41Sopenharmony_ci      const action = next.value;
341cb0ef41Sopenharmony_ci
351cb0ef41Sopenharmony_ci      if (typeof action === 'object') {
361cb0ef41Sopenharmony_ci        this.emit('keypress', '', action);
371cb0ef41Sopenharmony_ci      } else {
381cb0ef41Sopenharmony_ci        this.emit('data', `${action}`);
391cb0ef41Sopenharmony_ci      }
401cb0ef41Sopenharmony_ci      setImmediate(doAction);
411cb0ef41Sopenharmony_ci    };
421cb0ef41Sopenharmony_ci    doAction();
431cb0ef41Sopenharmony_ci  }
441cb0ef41Sopenharmony_ci  resume() {}
451cb0ef41Sopenharmony_ci  pause() {}
461cb0ef41Sopenharmony_ci}
471cb0ef41Sopenharmony_ciActionStream.prototype.readable = true;
481cb0ef41Sopenharmony_ci
491cb0ef41Sopenharmony_ci// Mock keys
501cb0ef41Sopenharmony_ciconst ENTER = { name: 'enter' };
511cb0ef41Sopenharmony_ciconst UP = { name: 'up' };
521cb0ef41Sopenharmony_ciconst DOWN = { name: 'down' };
531cb0ef41Sopenharmony_ciconst LEFT = { name: 'left' };
541cb0ef41Sopenharmony_ciconst RIGHT = { name: 'right' };
551cb0ef41Sopenharmony_ciconst BACKSPACE = { name: 'backspace' };
561cb0ef41Sopenharmony_ciconst TABULATION = { name: 'tab' };
571cb0ef41Sopenharmony_ciconst WORD_LEFT = { name: 'left', ctrl: true };
581cb0ef41Sopenharmony_ciconst WORD_RIGHT = { name: 'right', ctrl: true };
591cb0ef41Sopenharmony_ciconst GO_TO_END = { name: 'end' };
601cb0ef41Sopenharmony_ciconst SIGINT = { name: 'c', ctrl: true };
611cb0ef41Sopenharmony_ciconst ESCAPE = { name: 'escape', meta: true };
621cb0ef41Sopenharmony_ci
631cb0ef41Sopenharmony_ciconst prompt = '> ';
641cb0ef41Sopenharmony_ci
651cb0ef41Sopenharmony_ciconst tests = [
661cb0ef41Sopenharmony_ci  {
671cb0ef41Sopenharmony_ci    env: { NODE_REPL_HISTORY: defaultHistoryPath },
681cb0ef41Sopenharmony_ci    test: (function*() {
691cb0ef41Sopenharmony_ci      // Deleting Array iterator should not break history feature.
701cb0ef41Sopenharmony_ci      //
711cb0ef41Sopenharmony_ci      // Using a generator function instead of an object to allow the test to
721cb0ef41Sopenharmony_ci      // keep iterating even when Array.prototype[Symbol.iterator] has been
731cb0ef41Sopenharmony_ci      // deleted.
741cb0ef41Sopenharmony_ci      yield 'const ArrayIteratorPrototype =';
751cb0ef41Sopenharmony_ci      yield '  Object.getPrototypeOf(Array.prototype[Symbol.iterator]());';
761cb0ef41Sopenharmony_ci      yield ENTER;
771cb0ef41Sopenharmony_ci      yield 'const {next} = ArrayIteratorPrototype;';
781cb0ef41Sopenharmony_ci      yield ENTER;
791cb0ef41Sopenharmony_ci      yield 'const realArrayIterator = Array.prototype[Symbol.iterator];';
801cb0ef41Sopenharmony_ci      yield ENTER;
811cb0ef41Sopenharmony_ci      yield 'delete Array.prototype[Symbol.iterator];';
821cb0ef41Sopenharmony_ci      yield ENTER;
831cb0ef41Sopenharmony_ci      yield 'delete ArrayIteratorPrototype.next;';
841cb0ef41Sopenharmony_ci      yield ENTER;
851cb0ef41Sopenharmony_ci      yield UP;
861cb0ef41Sopenharmony_ci      yield UP;
871cb0ef41Sopenharmony_ci      yield DOWN;
881cb0ef41Sopenharmony_ci      yield DOWN;
891cb0ef41Sopenharmony_ci      yield 'fu';
901cb0ef41Sopenharmony_ci      yield 'n';
911cb0ef41Sopenharmony_ci      yield RIGHT;
921cb0ef41Sopenharmony_ci      yield BACKSPACE;
931cb0ef41Sopenharmony_ci      yield LEFT;
941cb0ef41Sopenharmony_ci      yield LEFT;
951cb0ef41Sopenharmony_ci      yield 'A';
961cb0ef41Sopenharmony_ci      yield BACKSPACE;
971cb0ef41Sopenharmony_ci      yield GO_TO_END;
981cb0ef41Sopenharmony_ci      yield BACKSPACE;
991cb0ef41Sopenharmony_ci      yield WORD_LEFT;
1001cb0ef41Sopenharmony_ci      yield WORD_RIGHT;
1011cb0ef41Sopenharmony_ci      yield ESCAPE;
1021cb0ef41Sopenharmony_ci      yield ENTER;
1031cb0ef41Sopenharmony_ci      yield 'require("./';
1041cb0ef41Sopenharmony_ci      yield TABULATION;
1051cb0ef41Sopenharmony_ci      yield SIGINT;
1061cb0ef41Sopenharmony_ci      yield 'import("./';
1071cb0ef41Sopenharmony_ci      yield TABULATION;
1081cb0ef41Sopenharmony_ci      yield SIGINT;
1091cb0ef41Sopenharmony_ci      yield 'Array.proto';
1101cb0ef41Sopenharmony_ci      yield RIGHT;
1111cb0ef41Sopenharmony_ci      yield '.pu';
1121cb0ef41Sopenharmony_ci      yield ENTER;
1131cb0ef41Sopenharmony_ci      yield 'ArrayIteratorPrototype.next = next;';
1141cb0ef41Sopenharmony_ci      yield ENTER;
1151cb0ef41Sopenharmony_ci      yield 'Array.prototype[Symbol.iterator] = realArrayIterator;';
1161cb0ef41Sopenharmony_ci      yield ENTER;
1171cb0ef41Sopenharmony_ci    })(),
1181cb0ef41Sopenharmony_ci    expected: [],
1191cb0ef41Sopenharmony_ci    clean: false
1201cb0ef41Sopenharmony_ci  },
1211cb0ef41Sopenharmony_ci];
1221cb0ef41Sopenharmony_ciconst numtests = tests.length;
1231cb0ef41Sopenharmony_ci
1241cb0ef41Sopenharmony_ciconst runTestWrap = common.mustCall(runTest, numtests);
1251cb0ef41Sopenharmony_ci
1261cb0ef41Sopenharmony_cifunction cleanupTmpFile() {
1271cb0ef41Sopenharmony_ci  try {
1281cb0ef41Sopenharmony_ci    // Write over the file, clearing any history
1291cb0ef41Sopenharmony_ci    fs.writeFileSync(defaultHistoryPath, '');
1301cb0ef41Sopenharmony_ci  } catch (err) {
1311cb0ef41Sopenharmony_ci    if (err.code === 'ENOENT') return true;
1321cb0ef41Sopenharmony_ci    throw err;
1331cb0ef41Sopenharmony_ci  }
1341cb0ef41Sopenharmony_ci  return true;
1351cb0ef41Sopenharmony_ci}
1361cb0ef41Sopenharmony_ci
1371cb0ef41Sopenharmony_cifunction runTest() {
1381cb0ef41Sopenharmony_ci  const opts = tests.shift();
1391cb0ef41Sopenharmony_ci  if (!opts) return; // All done
1401cb0ef41Sopenharmony_ci
1411cb0ef41Sopenharmony_ci  const { expected, skip } = opts;
1421cb0ef41Sopenharmony_ci
1431cb0ef41Sopenharmony_ci  // Test unsupported on platform.
1441cb0ef41Sopenharmony_ci  if (skip) {
1451cb0ef41Sopenharmony_ci    setImmediate(runTestWrap, true);
1461cb0ef41Sopenharmony_ci    return;
1471cb0ef41Sopenharmony_ci  }
1481cb0ef41Sopenharmony_ci  const lastChunks = [];
1491cb0ef41Sopenharmony_ci  let i = 0;
1501cb0ef41Sopenharmony_ci
1511cb0ef41Sopenharmony_ci  REPL.createInternalRepl(opts.env, {
1521cb0ef41Sopenharmony_ci    input: new ActionStream(),
1531cb0ef41Sopenharmony_ci    output: new stream.Writable({
1541cb0ef41Sopenharmony_ci      write(chunk, _, next) {
1551cb0ef41Sopenharmony_ci        const output = chunk.toString();
1561cb0ef41Sopenharmony_ci
1571cb0ef41Sopenharmony_ci        if (!opts.showEscapeCodes &&
1581cb0ef41Sopenharmony_ci            (output[0] === '\x1B' || /^[\r\n]+$/.test(output))) {
1591cb0ef41Sopenharmony_ci          return next();
1601cb0ef41Sopenharmony_ci        }
1611cb0ef41Sopenharmony_ci
1621cb0ef41Sopenharmony_ci        lastChunks.push(output);
1631cb0ef41Sopenharmony_ci
1641cb0ef41Sopenharmony_ci        if (expected.length && !opts.checkTotal) {
1651cb0ef41Sopenharmony_ci          try {
1661cb0ef41Sopenharmony_ci            assert.strictEqual(output, expected[i]);
1671cb0ef41Sopenharmony_ci          } catch (e) {
1681cb0ef41Sopenharmony_ci            console.error(`Failed test # ${numtests - tests.length}`);
1691cb0ef41Sopenharmony_ci            console.error('Last outputs: ' + inspect(lastChunks, {
1701cb0ef41Sopenharmony_ci              breakLength: 5, colors: true
1711cb0ef41Sopenharmony_ci            }));
1721cb0ef41Sopenharmony_ci            throw e;
1731cb0ef41Sopenharmony_ci          }
1741cb0ef41Sopenharmony_ci          // TODO(BridgeAR): Auto close on last chunk!
1751cb0ef41Sopenharmony_ci          i++;
1761cb0ef41Sopenharmony_ci        }
1771cb0ef41Sopenharmony_ci
1781cb0ef41Sopenharmony_ci        next();
1791cb0ef41Sopenharmony_ci      }
1801cb0ef41Sopenharmony_ci    }),
1811cb0ef41Sopenharmony_ci    allowBlockingCompletions: true,
1821cb0ef41Sopenharmony_ci    completer: opts.completer,
1831cb0ef41Sopenharmony_ci    prompt,
1841cb0ef41Sopenharmony_ci    useColors: false,
1851cb0ef41Sopenharmony_ci    preview: opts.preview,
1861cb0ef41Sopenharmony_ci    terminal: true
1871cb0ef41Sopenharmony_ci  }, function(err, repl) {
1881cb0ef41Sopenharmony_ci    if (err) {
1891cb0ef41Sopenharmony_ci      console.error(`Failed test # ${numtests - tests.length}`);
1901cb0ef41Sopenharmony_ci      throw err;
1911cb0ef41Sopenharmony_ci    }
1921cb0ef41Sopenharmony_ci
1931cb0ef41Sopenharmony_ci    repl.once('close', () => {
1941cb0ef41Sopenharmony_ci      if (opts.clean)
1951cb0ef41Sopenharmony_ci        cleanupTmpFile();
1961cb0ef41Sopenharmony_ci
1971cb0ef41Sopenharmony_ci      if (opts.checkTotal) {
1981cb0ef41Sopenharmony_ci        assert.deepStrictEqual(lastChunks, expected);
1991cb0ef41Sopenharmony_ci      } else if (expected.length !== i) {
2001cb0ef41Sopenharmony_ci        console.error(tests[numtests - tests.length - 1]);
2011cb0ef41Sopenharmony_ci        throw new Error(`Failed test # ${numtests - tests.length}`);
2021cb0ef41Sopenharmony_ci      }
2031cb0ef41Sopenharmony_ci
2041cb0ef41Sopenharmony_ci      setImmediate(runTestWrap, true);
2051cb0ef41Sopenharmony_ci    });
2061cb0ef41Sopenharmony_ci
2071cb0ef41Sopenharmony_ci    if (opts.columns) {
2081cb0ef41Sopenharmony_ci      Object.defineProperty(repl, 'columns', {
2091cb0ef41Sopenharmony_ci        value: opts.columns,
2101cb0ef41Sopenharmony_ci        enumerable: true
2111cb0ef41Sopenharmony_ci      });
2121cb0ef41Sopenharmony_ci    }
2131cb0ef41Sopenharmony_ci    repl.input.run(opts.test);
2141cb0ef41Sopenharmony_ci  });
2151cb0ef41Sopenharmony_ci}
2161cb0ef41Sopenharmony_ci
2171cb0ef41Sopenharmony_ci// run the tests
2181cb0ef41Sopenharmony_cirunTest();
219