11cb0ef41Sopenharmony_ci'use strict'; 21cb0ef41Sopenharmony_ci 31cb0ef41Sopenharmony_ciconst common = require('../common'); 41cb0ef41Sopenharmony_ciconst fixtures = require('../common/fixtures'); 51cb0ef41Sopenharmony_ciconst stream = require('stream'); 61cb0ef41Sopenharmony_ciconst REPL = require('repl'); 71cb0ef41Sopenharmony_ciconst assert = require('assert'); 81cb0ef41Sopenharmony_ciconst fs = require('fs'); 91cb0ef41Sopenharmony_ciconst path = require('path'); 101cb0ef41Sopenharmony_ciconst os = require('os'); 111cb0ef41Sopenharmony_ciconst util = require('util'); 121cb0ef41Sopenharmony_ci 131cb0ef41Sopenharmony_cicommon.skipIfDumbTerminal(); 141cb0ef41Sopenharmony_ci 151cb0ef41Sopenharmony_ciconst tmpdir = require('../common/tmpdir'); 161cb0ef41Sopenharmony_citmpdir.refresh(); 171cb0ef41Sopenharmony_ci 181cb0ef41Sopenharmony_ci// Mock os.homedir() 191cb0ef41Sopenharmony_cios.homedir = function() { 201cb0ef41Sopenharmony_ci return tmpdir.path; 211cb0ef41Sopenharmony_ci}; 221cb0ef41Sopenharmony_ci 231cb0ef41Sopenharmony_ci// Create an input stream specialized for testing an array of actions 241cb0ef41Sopenharmony_ciclass ActionStream extends stream.Stream { 251cb0ef41Sopenharmony_ci run(data) { 261cb0ef41Sopenharmony_ci const _iter = data[Symbol.iterator](); 271cb0ef41Sopenharmony_ci const doAction = () => { 281cb0ef41Sopenharmony_ci const next = _iter.next(); 291cb0ef41Sopenharmony_ci if (next.done) { 301cb0ef41Sopenharmony_ci // Close the repl. Note that it must have a clean prompt to do so. 311cb0ef41Sopenharmony_ci setImmediate(() => { 321cb0ef41Sopenharmony_ci this.emit('keypress', '', { ctrl: true, name: 'd' }); 331cb0ef41Sopenharmony_ci }); 341cb0ef41Sopenharmony_ci return; 351cb0ef41Sopenharmony_ci } 361cb0ef41Sopenharmony_ci const action = next.value; 371cb0ef41Sopenharmony_ci 381cb0ef41Sopenharmony_ci if (typeof action === 'object') { 391cb0ef41Sopenharmony_ci this.emit('keypress', '', action); 401cb0ef41Sopenharmony_ci } else { 411cb0ef41Sopenharmony_ci this.emit('data', `${action}\n`); 421cb0ef41Sopenharmony_ci } 431cb0ef41Sopenharmony_ci setImmediate(doAction); 441cb0ef41Sopenharmony_ci }; 451cb0ef41Sopenharmony_ci setImmediate(doAction); 461cb0ef41Sopenharmony_ci } 471cb0ef41Sopenharmony_ci resume() {} 481cb0ef41Sopenharmony_ci pause() {} 491cb0ef41Sopenharmony_ci} 501cb0ef41Sopenharmony_ciActionStream.prototype.readable = true; 511cb0ef41Sopenharmony_ci 521cb0ef41Sopenharmony_ci 531cb0ef41Sopenharmony_ci// Mock keys 541cb0ef41Sopenharmony_ciconst UP = { name: 'up' }; 551cb0ef41Sopenharmony_ciconst DOWN = { name: 'down' }; 561cb0ef41Sopenharmony_ciconst ENTER = { name: 'enter' }; 571cb0ef41Sopenharmony_ciconst CLEAR = { ctrl: true, name: 'u' }; 581cb0ef41Sopenharmony_ci 591cb0ef41Sopenharmony_ci// File paths 601cb0ef41Sopenharmony_ciconst historyFixturePath = fixtures.path('.node_repl_history'); 611cb0ef41Sopenharmony_ciconst historyPath = path.join(tmpdir.path, '.fixture_copy_repl_history'); 621cb0ef41Sopenharmony_ciconst historyPathFail = fixtures.path('nonexistent_folder', 'filename'); 631cb0ef41Sopenharmony_ciconst defaultHistoryPath = path.join(tmpdir.path, '.node_repl_history'); 641cb0ef41Sopenharmony_ciconst emptyHiddenHistoryPath = fixtures.path('.empty-hidden-repl-history-file'); 651cb0ef41Sopenharmony_ciconst devNullHistoryPath = path.join(tmpdir.path, 661cb0ef41Sopenharmony_ci '.dev-null-repl-history-file'); 671cb0ef41Sopenharmony_ci// Common message bits 681cb0ef41Sopenharmony_ciconst prompt = '> '; 691cb0ef41Sopenharmony_ciconst replDisabled = '\nPersistent history support disabled. Set the ' + 701cb0ef41Sopenharmony_ci 'NODE_REPL_HISTORY environment\nvariable to a valid, ' + 711cb0ef41Sopenharmony_ci 'user-writable path to enable.\n'; 721cb0ef41Sopenharmony_ciconst homedirErr = '\nError: Could not get the home directory.\n' + 731cb0ef41Sopenharmony_ci 'REPL session history will not be persisted.\n'; 741cb0ef41Sopenharmony_ciconst replFailedRead = '\nError: Could not open history file.\n' + 751cb0ef41Sopenharmony_ci 'REPL session history will not be persisted.\n'; 761cb0ef41Sopenharmony_ci 771cb0ef41Sopenharmony_ciconst tests = [ 781cb0ef41Sopenharmony_ci { 791cb0ef41Sopenharmony_ci env: { NODE_REPL_HISTORY: '' }, 801cb0ef41Sopenharmony_ci test: [UP], 811cb0ef41Sopenharmony_ci expected: [prompt, replDisabled, prompt] 821cb0ef41Sopenharmony_ci }, 831cb0ef41Sopenharmony_ci { 841cb0ef41Sopenharmony_ci env: { NODE_REPL_HISTORY: ' ' }, 851cb0ef41Sopenharmony_ci test: [UP], 861cb0ef41Sopenharmony_ci expected: [prompt, replDisabled, prompt] 871cb0ef41Sopenharmony_ci }, 881cb0ef41Sopenharmony_ci { 891cb0ef41Sopenharmony_ci env: { NODE_REPL_HISTORY: historyPath }, 901cb0ef41Sopenharmony_ci test: [UP, CLEAR], 911cb0ef41Sopenharmony_ci expected: [prompt, `${prompt}'you look fabulous today'`, prompt] 921cb0ef41Sopenharmony_ci }, 931cb0ef41Sopenharmony_ci { 941cb0ef41Sopenharmony_ci env: {}, 951cb0ef41Sopenharmony_ci test: [UP, '21', ENTER, "'42'", ENTER], 961cb0ef41Sopenharmony_ci expected: [ 971cb0ef41Sopenharmony_ci prompt, 981cb0ef41Sopenharmony_ci // TODO(BridgeAR): The line is refreshed too many times. The double prompt 991cb0ef41Sopenharmony_ci // is redundant and can be optimized away. 1001cb0ef41Sopenharmony_ci '2', '1', '21\n', prompt, prompt, 1011cb0ef41Sopenharmony_ci "'", '4', '2', "'", "'42'\n", prompt, prompt, 1021cb0ef41Sopenharmony_ci ], 1031cb0ef41Sopenharmony_ci clean: false 1041cb0ef41Sopenharmony_ci }, 1051cb0ef41Sopenharmony_ci { // Requires the above test case 1061cb0ef41Sopenharmony_ci env: {}, 1071cb0ef41Sopenharmony_ci test: [UP, UP, UP, DOWN, ENTER], 1081cb0ef41Sopenharmony_ci expected: [ 1091cb0ef41Sopenharmony_ci prompt, 1101cb0ef41Sopenharmony_ci `${prompt}'42'`, 1111cb0ef41Sopenharmony_ci `${prompt}21`, 1121cb0ef41Sopenharmony_ci prompt, 1131cb0ef41Sopenharmony_ci `${prompt}21`, 1141cb0ef41Sopenharmony_ci '21\n', 1151cb0ef41Sopenharmony_ci prompt, 1161cb0ef41Sopenharmony_ci ] 1171cb0ef41Sopenharmony_ci }, 1181cb0ef41Sopenharmony_ci { 1191cb0ef41Sopenharmony_ci env: { NODE_REPL_HISTORY: historyPath, 1201cb0ef41Sopenharmony_ci NODE_REPL_HISTORY_SIZE: 1 }, 1211cb0ef41Sopenharmony_ci test: [UP, UP, DOWN, CLEAR], 1221cb0ef41Sopenharmony_ci expected: [ 1231cb0ef41Sopenharmony_ci prompt, 1241cb0ef41Sopenharmony_ci `${prompt}'you look fabulous today'`, 1251cb0ef41Sopenharmony_ci prompt, 1261cb0ef41Sopenharmony_ci `${prompt}'you look fabulous today'`, 1271cb0ef41Sopenharmony_ci prompt, 1281cb0ef41Sopenharmony_ci ] 1291cb0ef41Sopenharmony_ci }, 1301cb0ef41Sopenharmony_ci { 1311cb0ef41Sopenharmony_ci env: { NODE_REPL_HISTORY: historyPathFail, 1321cb0ef41Sopenharmony_ci NODE_REPL_HISTORY_SIZE: 1 }, 1331cb0ef41Sopenharmony_ci test: [UP], 1341cb0ef41Sopenharmony_ci expected: [prompt, replFailedRead, prompt, replDisabled, prompt] 1351cb0ef41Sopenharmony_ci }, 1361cb0ef41Sopenharmony_ci { 1371cb0ef41Sopenharmony_ci before: function before() { 1381cb0ef41Sopenharmony_ci if (common.isWindows) { 1391cb0ef41Sopenharmony_ci const execSync = require('child_process').execSync; 1401cb0ef41Sopenharmony_ci execSync(`ATTRIB +H "${emptyHiddenHistoryPath}"`, (err) => { 1411cb0ef41Sopenharmony_ci assert.ifError(err); 1421cb0ef41Sopenharmony_ci }); 1431cb0ef41Sopenharmony_ci } 1441cb0ef41Sopenharmony_ci }, 1451cb0ef41Sopenharmony_ci env: { NODE_REPL_HISTORY: emptyHiddenHistoryPath }, 1461cb0ef41Sopenharmony_ci test: [UP], 1471cb0ef41Sopenharmony_ci expected: [prompt] 1481cb0ef41Sopenharmony_ci }, 1491cb0ef41Sopenharmony_ci { 1501cb0ef41Sopenharmony_ci before: function before() { 1511cb0ef41Sopenharmony_ci if (!common.isWindows) 1521cb0ef41Sopenharmony_ci fs.symlinkSync('/dev/null', devNullHistoryPath); 1531cb0ef41Sopenharmony_ci }, 1541cb0ef41Sopenharmony_ci env: { NODE_REPL_HISTORY: devNullHistoryPath }, 1551cb0ef41Sopenharmony_ci test: [UP], 1561cb0ef41Sopenharmony_ci expected: [prompt] 1571cb0ef41Sopenharmony_ci }, 1581cb0ef41Sopenharmony_ci { // Make sure this is always the last test, since we change os.homedir() 1591cb0ef41Sopenharmony_ci before: function before() { 1601cb0ef41Sopenharmony_ci // Mock os.homedir() failure 1611cb0ef41Sopenharmony_ci os.homedir = function() { 1621cb0ef41Sopenharmony_ci throw new Error('os.homedir() failure'); 1631cb0ef41Sopenharmony_ci }; 1641cb0ef41Sopenharmony_ci }, 1651cb0ef41Sopenharmony_ci env: {}, 1661cb0ef41Sopenharmony_ci test: [UP], 1671cb0ef41Sopenharmony_ci expected: [prompt, homedirErr, prompt, replDisabled, prompt] 1681cb0ef41Sopenharmony_ci }, 1691cb0ef41Sopenharmony_ci]; 1701cb0ef41Sopenharmony_ciconst numtests = tests.length; 1711cb0ef41Sopenharmony_ci 1721cb0ef41Sopenharmony_ci 1731cb0ef41Sopenharmony_cifunction cleanupTmpFile() { 1741cb0ef41Sopenharmony_ci try { 1751cb0ef41Sopenharmony_ci // Write over the file, clearing any history 1761cb0ef41Sopenharmony_ci fs.writeFileSync(defaultHistoryPath, ''); 1771cb0ef41Sopenharmony_ci } catch (err) { 1781cb0ef41Sopenharmony_ci if (err.code === 'ENOENT') return true; 1791cb0ef41Sopenharmony_ci throw err; 1801cb0ef41Sopenharmony_ci } 1811cb0ef41Sopenharmony_ci return true; 1821cb0ef41Sopenharmony_ci} 1831cb0ef41Sopenharmony_ci 1841cb0ef41Sopenharmony_ci// Copy our fixture to the tmp directory 1851cb0ef41Sopenharmony_cifs.createReadStream(historyFixturePath) 1861cb0ef41Sopenharmony_ci .pipe(fs.createWriteStream(historyPath)).on('unpipe', () => runTest()); 1871cb0ef41Sopenharmony_ci 1881cb0ef41Sopenharmony_ciconst runTestWrap = common.mustCall(runTest, numtests); 1891cb0ef41Sopenharmony_ci 1901cb0ef41Sopenharmony_cifunction runTest(assertCleaned) { 1911cb0ef41Sopenharmony_ci const opts = tests.shift(); 1921cb0ef41Sopenharmony_ci if (!opts) return; // All done 1931cb0ef41Sopenharmony_ci 1941cb0ef41Sopenharmony_ci console.log('NEW'); 1951cb0ef41Sopenharmony_ci 1961cb0ef41Sopenharmony_ci if (assertCleaned) { 1971cb0ef41Sopenharmony_ci try { 1981cb0ef41Sopenharmony_ci assert.strictEqual(fs.readFileSync(defaultHistoryPath, 'utf8'), ''); 1991cb0ef41Sopenharmony_ci } catch (e) { 2001cb0ef41Sopenharmony_ci if (e.code !== 'ENOENT') { 2011cb0ef41Sopenharmony_ci console.error(`Failed test # ${numtests - tests.length}`); 2021cb0ef41Sopenharmony_ci throw e; 2031cb0ef41Sopenharmony_ci } 2041cb0ef41Sopenharmony_ci } 2051cb0ef41Sopenharmony_ci } 2061cb0ef41Sopenharmony_ci 2071cb0ef41Sopenharmony_ci const test = opts.test; 2081cb0ef41Sopenharmony_ci const expected = opts.expected; 2091cb0ef41Sopenharmony_ci const clean = opts.clean; 2101cb0ef41Sopenharmony_ci const before = opts.before; 2111cb0ef41Sopenharmony_ci const historySize = opts.env.NODE_REPL_HISTORY_SIZE; 2121cb0ef41Sopenharmony_ci const historyFile = opts.env.NODE_REPL_HISTORY; 2131cb0ef41Sopenharmony_ci 2141cb0ef41Sopenharmony_ci if (before) before(); 2151cb0ef41Sopenharmony_ci 2161cb0ef41Sopenharmony_ci const repl = REPL.start({ 2171cb0ef41Sopenharmony_ci input: new ActionStream(), 2181cb0ef41Sopenharmony_ci output: new stream.Writable({ 2191cb0ef41Sopenharmony_ci write(chunk, _, next) { 2201cb0ef41Sopenharmony_ci const output = chunk.toString(); 2211cb0ef41Sopenharmony_ci console.log('INPUT', util.inspect(output)); 2221cb0ef41Sopenharmony_ci 2231cb0ef41Sopenharmony_ci // Ignore escapes and blank lines 2241cb0ef41Sopenharmony_ci if (output.charCodeAt(0) === 27 || /^[\r\n]+$/.test(output)) 2251cb0ef41Sopenharmony_ci return next(); 2261cb0ef41Sopenharmony_ci 2271cb0ef41Sopenharmony_ci try { 2281cb0ef41Sopenharmony_ci assert.strictEqual(output, expected.shift()); 2291cb0ef41Sopenharmony_ci } catch (err) { 2301cb0ef41Sopenharmony_ci console.error(`Failed test # ${numtests - tests.length}`); 2311cb0ef41Sopenharmony_ci throw err; 2321cb0ef41Sopenharmony_ci } 2331cb0ef41Sopenharmony_ci next(); 2341cb0ef41Sopenharmony_ci } 2351cb0ef41Sopenharmony_ci }), 2361cb0ef41Sopenharmony_ci prompt: prompt, 2371cb0ef41Sopenharmony_ci useColors: false, 2381cb0ef41Sopenharmony_ci terminal: true, 2391cb0ef41Sopenharmony_ci historySize: historySize 2401cb0ef41Sopenharmony_ci }); 2411cb0ef41Sopenharmony_ci 2421cb0ef41Sopenharmony_ci repl.setupHistory(historyFile, function(err, repl) { 2431cb0ef41Sopenharmony_ci if (err) { 2441cb0ef41Sopenharmony_ci console.error(`Failed test # ${numtests - tests.length}`); 2451cb0ef41Sopenharmony_ci throw err; 2461cb0ef41Sopenharmony_ci } 2471cb0ef41Sopenharmony_ci 2481cb0ef41Sopenharmony_ci repl.once('close', () => { 2491cb0ef41Sopenharmony_ci if (repl._flushing) { 2501cb0ef41Sopenharmony_ci repl.once('flushHistory', onClose); 2511cb0ef41Sopenharmony_ci return; 2521cb0ef41Sopenharmony_ci } 2531cb0ef41Sopenharmony_ci 2541cb0ef41Sopenharmony_ci onClose(); 2551cb0ef41Sopenharmony_ci }); 2561cb0ef41Sopenharmony_ci 2571cb0ef41Sopenharmony_ci function onClose() { 2581cb0ef41Sopenharmony_ci const cleaned = clean === false ? false : cleanupTmpFile(); 2591cb0ef41Sopenharmony_ci 2601cb0ef41Sopenharmony_ci try { 2611cb0ef41Sopenharmony_ci // Ensure everything that we expected was output 2621cb0ef41Sopenharmony_ci assert.strictEqual(expected.length, 0); 2631cb0ef41Sopenharmony_ci setImmediate(runTestWrap, cleaned); 2641cb0ef41Sopenharmony_ci } catch (err) { 2651cb0ef41Sopenharmony_ci console.error(`Failed test # ${numtests - tests.length}`); 2661cb0ef41Sopenharmony_ci throw err; 2671cb0ef41Sopenharmony_ci } 2681cb0ef41Sopenharmony_ci } 2691cb0ef41Sopenharmony_ci 2701cb0ef41Sopenharmony_ci repl.inputStream.run(test); 2711cb0ef41Sopenharmony_ci }); 2721cb0ef41Sopenharmony_ci} 273