diff --git a/lib/readline.js b/lib/readline.js index 0856d8aa087e61..62565b60701d6e 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -718,7 +718,7 @@ Interface.prototype._historyNext = function() { }; Interface.prototype._historyPrev = function() { - if (this.historyIndex < this.history.length) { + if (this.historyIndex < this.history.length && this.history.length) { const search = this[kSubstringSearch] || ''; let index = this.historyIndex + 1; while (index < this.history.length && @@ -727,9 +727,7 @@ Interface.prototype._historyPrev = function() { index++; } if (index === this.history.length) { - // TODO(BridgeAR): Change this to: - // this.line = search; - return; + this.line = search; } else { this.line = this.history[index]; } diff --git a/test/parallel/test-readline-interface.js b/test/parallel/test-readline-interface.js index d0a9b3589c8354..f141fe125d5224 100644 --- a/test/parallel/test-readline-interface.js +++ b/test/parallel/test-readline-interface.js @@ -462,12 +462,20 @@ function isWarned(emitter) { fi.emit('keypress', '.', { name: 'up' }); // 'baz' assert.strictEqual(rli.historyIndex, 2); assert.strictEqual(rli.line, 'baz'); - fi.emit('keypress', '.', { name: 'up' }); // 'baz' - assert.strictEqual(rli.historyIndex, 2); - assert.strictEqual(rli.line, 'baz'); - fi.emit('keypress', '.', { name: 'up' }); // 'baz' - assert.strictEqual(rli.historyIndex, 2); - assert.strictEqual(rli.line, 'baz'); + fi.emit('keypress', '.', { name: 'up' }); // 'ba' + assert.strictEqual(rli.historyIndex, 4); + assert.strictEqual(rli.line, 'ba'); + fi.emit('keypress', '.', { name: 'up' }); // 'ba' + assert.strictEqual(rli.historyIndex, 4); + assert.strictEqual(rli.line, 'ba'); + // Deactivate substring history search and reset history index. + fi.emit('keypress', '.', { name: 'right' }); // 'ba' + assert.strictEqual(rli.historyIndex, -1); + assert.strictEqual(rli.line, 'ba'); + // Substring history search activated. + fi.emit('keypress', '.', { name: 'up' }); // 'ba' + assert.strictEqual(rli.historyIndex, 0); + assert.strictEqual(rli.line, 'bat'); rli.close(); } diff --git a/test/parallel/test-repl-history-navigation.js b/test/parallel/test-repl-history-navigation.js index 6b0813e1fae36e..b1cdc5cbdfc41e 100644 --- a/test/parallel/test-repl-history-navigation.js +++ b/test/parallel/test-repl-history-navigation.js @@ -78,8 +78,7 @@ const tests = [ }, { env: { NODE_REPL_HISTORY: defaultHistoryPath }, - checkTotal: true, - test: [UP, UP, UP, UP, UP, DOWN, DOWN, DOWN, DOWN], + test: [UP, UP, UP, UP, UP, DOWN, DOWN, DOWN, DOWN, DOWN], expected: [prompt, `${prompt}Array(100).fill(1).map((e, i) => i ** 2)`, prev && '\n// [ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, ' + @@ -92,6 +91,8 @@ const tests = [ `${prompt}555 + 909`, prev && '\n// 1464', `${prompt}let ab = 45`, + prompt, + `${prompt}let ab = 45`, `${prompt}555 + 909`, prev && '\n// 1464', `${prompt}{key : {key2 :[] }}`, @@ -138,9 +139,12 @@ const tests = [ // UP - skipping const foo = true '\x1B[1G', '\x1B[0J', '> 555 + 909', '\x1B[12G', - // UP, UP, ENTER. UPs at the end of the history have no effect. - '\r\n', - '1464\n', + // UP, UP + // UPs at the end of the history reset the line to the original input. + '\x1B[1G', '\x1B[0J', + '> 55', '\x1B[5G', + // ENTER + '\r\n', '55\n', '\x1B[1G', '\x1B[0J', '> ', '\x1B[3G', '\r\n' diff --git a/test/parallel/test-repl-persistent-history.js b/test/parallel/test-repl-persistent-history.js index bb10085eccfcf6..1d1261a3752365 100644 --- a/test/parallel/test-repl-persistent-history.js +++ b/test/parallel/test-repl-persistent-history.js @@ -10,6 +10,7 @@ const assert = require('assert'); const fs = require('fs'); const path = require('path'); const os = require('os'); +const util = require('util'); const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); @@ -51,6 +52,7 @@ ActionStream.prototype.readable = true; // Mock keys const UP = { name: 'up' }; +const DOWN = { name: 'down' }; const ENTER = { name: 'enter' }; const CLEAR = { ctrl: true, name: 'u' }; @@ -90,20 +92,42 @@ const tests = [ }, { env: {}, - test: [UP, '\'42\'', ENTER], - expected: [prompt, '\'', '4', '2', '\'', '\'42\'\n', prompt, prompt], + test: [UP, '21', ENTER, "'42'", ENTER], + expected: [ + prompt, + '2', '1', '21\n', prompt, prompt, + "'", '4', '2', "'", "'42'\n", prompt, prompt + ], clean: false }, { // Requires the above test case env: {}, - test: [UP, UP, ENTER], - expected: [prompt, `${prompt}'42'`, '\'42\'\n', prompt] + test: [UP, UP, CLEAR, ENTER, DOWN, CLEAR, ENTER, UP, ENTER], + expected: [ + prompt, + `${prompt}'42'`, + `${prompt}21`, + prompt, + prompt, + `${prompt}'42'`, + prompt, + prompt, + `${prompt}21`, + '21\n', + prompt, + ] }, { env: { NODE_REPL_HISTORY: historyPath, NODE_REPL_HISTORY_SIZE: 1 }, - test: [UP, UP, CLEAR], - expected: [prompt, `${prompt}'you look fabulous today'`, prompt] + test: [UP, UP, DOWN, CLEAR], + expected: [ + prompt, + `${prompt}'you look fabulous today'`, + prompt, + `${prompt}'you look fabulous today'`, + prompt + ] }, { env: { NODE_REPL_HISTORY: historyPathFail, @@ -169,6 +193,8 @@ function runTest(assertCleaned) { const opts = tests.shift(); if (!opts) return; // All done + console.log('NEW'); + if (assertCleaned) { try { assert.strictEqual(fs.readFileSync(defaultHistoryPath, 'utf8'), ''); @@ -193,6 +219,7 @@ function runTest(assertCleaned) { output: new stream.Writable({ write(chunk, _, next) { const output = chunk.toString(); + console.log('INPUT', util.inspect(output)); // Ignore escapes and blank lines if (output.charCodeAt(0) === 27 || /^[\r\n]+$/.test(output)) @@ -207,7 +234,7 @@ function runTest(assertCleaned) { next(); } }), - prompt: prompt, + prompt, useColors: false, terminal: true }, function(err, repl) { diff --git a/test/parallel/test-repl-programmatic-history.js b/test/parallel/test-repl-programmatic-history.js index 7eda401a7c386b..5307ae0556ae74 100644 --- a/test/parallel/test-repl-programmatic-history.js +++ b/test/parallel/test-repl-programmatic-history.js @@ -8,6 +8,7 @@ const assert = require('assert'); const fs = require('fs'); const path = require('path'); const os = require('os'); +const util = require('util'); const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); @@ -49,6 +50,7 @@ ActionStream.prototype.readable = true; // Mock keys const UP = { name: 'up' }; +const DOWN = { name: 'down' }; const ENTER = { name: 'enter' }; const CLEAR = { ctrl: true, name: 'u' }; @@ -88,20 +90,40 @@ const tests = [ }, { env: {}, - test: [UP, '\'42\'', ENTER], - expected: [prompt, '\'', '4', '2', '\'', '\'42\'\n', prompt, prompt], + test: [UP, '21', ENTER, "'42'", ENTER], + expected: [ + prompt, + // TODO(BridgeAR): The line is refreshed too many times. The double prompt + // is redundant and can be optimized away. + '2', '1', '21\n', prompt, prompt, + "'", '4', '2', "'", "'42'\n", prompt, prompt + ], clean: false }, { // Requires the above test case env: {}, - test: [UP, UP, ENTER], - expected: [prompt, `${prompt}'42'`, '\'42\'\n', prompt] + test: [UP, UP, UP, DOWN, ENTER], + expected: [ + prompt, + `${prompt}'42'`, + `${prompt}21`, + prompt, + `${prompt}21`, + '21\n', + prompt + ] }, { env: { NODE_REPL_HISTORY: historyPath, NODE_REPL_HISTORY_SIZE: 1 }, - test: [UP, UP, CLEAR], - expected: [prompt, `${prompt}'you look fabulous today'`, prompt] + test: [UP, UP, DOWN, CLEAR], + expected: [ + prompt, + `${prompt}'you look fabulous today'`, + prompt, + `${prompt}'you look fabulous today'`, + prompt + ] }, { env: { NODE_REPL_HISTORY: historyPathFail, @@ -167,6 +189,8 @@ function runTest(assertCleaned) { const opts = tests.shift(); if (!opts) return; // All done + console.log('NEW'); + if (assertCleaned) { try { assert.strictEqual(fs.readFileSync(defaultHistoryPath, 'utf8'), ''); @@ -192,6 +216,7 @@ function runTest(assertCleaned) { output: new stream.Writable({ write(chunk, _, next) { const output = chunk.toString(); + console.log('INPUT', util.inspect(output)); // Ignore escapes and blank lines if (output.charCodeAt(0) === 27 || /^[\r\n]+$/.test(output))