Skip to content

Commit ceee8d2

Browse files
Fishrock123rvagg
authored andcommitted
test: add tests for persistent repl history
PR-URL: #2224 Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Chris Dickinson <[email protected]> Reviewed-By: Roman Reiss <[email protected]>
1 parent 1721968 commit ceee8d2

File tree

5 files changed

+207
-2
lines changed

5 files changed

+207
-2
lines changed

.eslintrc

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ ecmaFeatures:
1010
generators: true
1111
forOf: true
1212
objectLiteralShorthandProperties: true
13+
objectLiteralShorthandMethods: true
14+
classes: true
1315

1416
rules:
1517
# Possible Errors

lib/internal/repl.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@ function replStart() {
1717
return REPL.start.apply(REPL, arguments);
1818
}
1919

20-
function createRepl(env, cb) {
21-
const opts = {
20+
function createRepl(env, opts, cb) {
21+
if (typeof opts === 'function') {
22+
cb = opts;
23+
opts = null;
24+
}
25+
opts = opts || {
2226
ignoreUndefined: false,
2327
terminal: process.stdout.isTTY,
2428
useGlobal: true

test/fixtures/.node_repl_history

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
'you look fabulous today'
2+
'Stay Fresh~'
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[
2+
"'=^.^='",
3+
"'hello world'"
4+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
'use strict';
2+
3+
// Flags: --expose-internals
4+
5+
const common = require('../common');
6+
const stream = require('stream');
7+
const REPL = require('internal/repl');
8+
const assert = require('assert');
9+
const fs = require('fs');
10+
const util = require('util');
11+
const path = require('path');
12+
const os = require('os');
13+
14+
common.refreshTmpDir();
15+
16+
// Mock os.homedir()
17+
os.homedir = function() {
18+
return common.tmpDir;
19+
};
20+
21+
// Create an input stream specialized for testing an array of actions
22+
class ActionStream extends stream.Stream {
23+
run(data) {
24+
const _iter = data[Symbol.iterator]();
25+
const self = this;
26+
27+
function doAction() {
28+
const next = _iter.next();
29+
if (next.done) {
30+
// Close the repl. Note that it must have a clean prompt to do so.
31+
setImmediate(function() {
32+
self.emit('keypress', '', { ctrl: true, name: 'd' });
33+
});
34+
return;
35+
}
36+
const action = next.value;
37+
38+
if (typeof action === 'object') {
39+
self.emit('keypress', '', action);
40+
} else {
41+
self.emit('data', action + '\n');
42+
}
43+
setImmediate(doAction);
44+
}
45+
setImmediate(doAction);
46+
}
47+
resume() {}
48+
pause() {}
49+
}
50+
ActionStream.prototype.readable = true;
51+
52+
53+
// Mock keys
54+
const UP = { name: 'up' };
55+
const ENTER = { name: 'enter' };
56+
const CLEAR = { ctrl: true, name: 'u' };
57+
// Common message bits
58+
const prompt = '> ';
59+
const replDisabled = '\nPersistent history support disabled. Set the ' +
60+
'NODE_REPL_HISTORY environment\nvariable to a valid, ' +
61+
'user-writable path to enable.\n';
62+
const convertMsg = '\nConverting old JSON repl history to line-separated ' +
63+
'history.\nThe new repl history file can be found at ' +
64+
path.join(common.tmpDir, '.node_repl_history') + '.\n';
65+
const homedirErr = '\nError: Could not get the home directory.\n' +
66+
'REPL session history will not be persisted.\n';
67+
// File paths
68+
const fixtures = path.join(common.testDir, 'fixtures');
69+
const historyFixturePath = path.join(fixtures, '.node_repl_history');
70+
const historyPath = path.join(common.tmpDir, '.fixture_copy_repl_history');
71+
const oldHistoryPath = path.join(fixtures, 'old-repl-history-file.json');
72+
73+
74+
const tests = [{
75+
env: { NODE_REPL_HISTORY: '' },
76+
test: [UP],
77+
expected: [prompt, replDisabled, prompt]
78+
},
79+
{
80+
env: { NODE_REPL_HISTORY: '',
81+
NODE_REPL_HISTORY_FILE: oldHistoryPath },
82+
test: [UP],
83+
expected: [prompt, replDisabled, prompt]
84+
},
85+
{
86+
env: { NODE_REPL_HISTORY: historyPath },
87+
test: [UP, CLEAR],
88+
expected: [prompt, prompt + '\'you look fabulous today\'', prompt]
89+
},
90+
{
91+
env: { NODE_REPL_HISTORY: historyPath,
92+
NODE_REPL_HISTORY_FILE: oldHistoryPath },
93+
test: [UP, CLEAR],
94+
expected: [prompt, prompt + '\'you look fabulous today\'', prompt]
95+
},
96+
{
97+
env: { NODE_REPL_HISTORY: historyPath,
98+
NODE_REPL_HISTORY_FILE: '' },
99+
test: [UP, CLEAR],
100+
expected: [prompt, prompt + '\'you look fabulous today\'', prompt]
101+
},
102+
{
103+
env: {},
104+
test: [UP],
105+
expected: [prompt]
106+
},
107+
{
108+
env: { NODE_REPL_HISTORY_FILE: oldHistoryPath },
109+
test: [UP, CLEAR, '\'42\'', ENTER/*, function(cb) {
110+
// XXX(Fishrock123) Allow the REPL to save to disk.
111+
// There isn't a way to do this programmatically right now.
112+
setTimeout(cb, 50);
113+
}*/],
114+
expected: [prompt, convertMsg, prompt, prompt + '\'=^.^=\'', prompt, '\'',
115+
'4', '2', '\'', '\'42\'\n', prompt, prompt],
116+
after: function ensureHistoryFixture() {
117+
// XXX(Fishrock123) Make sure nothing weird happened to our fixture
118+
// or it's temporary copy.
119+
// Sometimes this test used to erase the fixture and I'm not sure why.
120+
const history = fs.readFileSync(historyFixturePath, 'utf8');
121+
assert.strictEqual(history,
122+
'\'you look fabulous today\'\n\'Stay Fresh~\'\n');
123+
const historyCopy = fs.readFileSync(historyPath, 'utf8');
124+
assert.strictEqual(historyCopy, '\'you look fabulous today\'' + os.EOL +
125+
'\'Stay Fresh~\'' + os.EOL);
126+
}
127+
},
128+
{
129+
env: {},
130+
test: [UP, UP, ENTER],
131+
expected: [prompt, prompt + '\'42\'', prompt + '\'=^.^=\'', '\'=^.^=\'\n',
132+
prompt]
133+
},
134+
{ // Make sure this is always the last test, since we change os.homedir()
135+
before: function mockHomedirFailure() {
136+
// Mock os.homedir() failure
137+
os.homedir = function() {
138+
throw new Error('os.homedir() failure');
139+
};
140+
},
141+
env: {},
142+
test: [UP],
143+
expected: [prompt, homedirErr, prompt, replDisabled, prompt]
144+
}];
145+
146+
147+
// Copy our fixture to the tmp directory
148+
fs.createReadStream(historyFixturePath)
149+
.pipe(fs.createWriteStream(historyPath)).on('unpipe', runTest);
150+
151+
function runTest() {
152+
const opts = tests.shift();
153+
if (!opts) return; // All done
154+
155+
const env = opts.env;
156+
const test = opts.test;
157+
const expected = opts.expected;
158+
const after = opts.after;
159+
const before = opts.before;
160+
161+
if (before) before();
162+
163+
REPL.createInternalRepl(env, {
164+
input: new ActionStream(),
165+
output: new stream.Writable({
166+
write(chunk, _, next) {
167+
const output = chunk.toString();
168+
169+
// Ignore escapes and blank lines
170+
if (output.charCodeAt(0) === 27 || /^[\r\n]+$/.test(output))
171+
return next();
172+
173+
assert.strictEqual(output, expected.shift());
174+
next();
175+
}
176+
}),
177+
prompt: prompt,
178+
useColors: false,
179+
terminal: true
180+
}, function(err, repl) {
181+
if (err) throw err;
182+
183+
if (after) repl.on('close', after);
184+
185+
repl.on('close', function() {
186+
// Ensure everything that we expected was output
187+
assert.strictEqual(expected.length, 0);
188+
setImmediate(runTest);
189+
});
190+
191+
repl.inputStream.run(test);
192+
});
193+
}

0 commit comments

Comments
 (0)