diff --git a/test/common/repl.js b/test/common/repl.js new file mode 100644 index 00000000000000..223050c76e1df0 --- /dev/null +++ b/test/common/repl.js @@ -0,0 +1,31 @@ +'use strict'; + +const ArrayStream = require('../common/arraystream'); +const repl = require('node:repl'); +const assert = require('node:assert'); + +function startNewREPLServer(replOpts = {}, testingOpts = {}) { + const input = new ArrayStream(); + const output = new ArrayStream(); + + output.accumulator = ''; + output.write = (data) => (output.accumulator += `${data}`.replaceAll('\r', '')); + + const replServer = repl.start({ + prompt: '', + input, + output, + terminal: true, + allowBlockingCompletions: true, + ...replOpts, + }); + + if (!testingOpts.disableDomainErrorAssert) { + // Some errors are passed to the domain, but do not callback + replServer._domain.on('error', assert.ifError); + } + + return { replServer, input, output }; +} + +module.exports = { startNewREPLServer }; diff --git a/test/parallel/test-repl-autolibs.js b/test/parallel/test-repl-autolibs.js index a1eb476ef530b1..5a34e1aecb1c8c 100644 --- a/test/parallel/test-repl-autolibs.js +++ b/test/parallel/test-repl-autolibs.js @@ -22,12 +22,12 @@ 'use strict'; const common = require('../common'); const ArrayStream = require('../common/arraystream'); +const { startNewREPLServer } = require('../common/repl'); const assert = require('assert'); const util = require('util'); -const repl = require('repl'); const putIn = new ArrayStream(); -repl.start('', putIn, null, true); +startNewREPLServer({ input: putIn, output: putIn, useGlobal: true, terminal: false }); test1(); diff --git a/test/parallel/test-repl-completion-on-getters-disabled.js b/test/parallel/test-repl-completion-on-getters-disabled.js index 2b80f717f685d4..9d97d611388749 100644 --- a/test/parallel/test-repl-completion-on-getters-disabled.js +++ b/test/parallel/test-repl-completion-on-getters-disabled.js @@ -4,19 +4,11 @@ const common = require('../common'); const assert = require('node:assert'); const { describe, test } = require('node:test'); -const ArrayStream = require('../common/arraystream'); - -const repl = require('node:repl'); +const { startNewREPLServer } = require('../common/repl'); function runCompletionTests(replInit, tests) { - const stream = new ArrayStream(); - const testRepl = repl.start({ stream }); - - // Some errors are passed to the domain - testRepl._domain.on('error', assert.ifError); - - testRepl.write(replInit); - testRepl.write('\n'); + const { replServer: testRepl, input } = startNewREPLServer(); + input.run([replInit]); tests.forEach(([query, expectedCompletions]) => { testRepl.complete(query, common.mustCall((error, data) => { diff --git a/test/parallel/test-repl-context.js b/test/parallel/test-repl-context.js index 88bd47a9281a9c..97847aac6db312 100644 --- a/test/parallel/test-repl-context.js +++ b/test/parallel/test-repl-context.js @@ -1,38 +1,28 @@ 'use strict'; require('../common'); -const ArrayStream = require('../common/arraystream'); const assert = require('assert'); -const repl = require('repl'); const vm = require('vm'); - -// Create a dummy stream that does nothing. -const stream = new ArrayStream(); +const { startNewREPLServer } = require('../common/repl'); // Test context when useGlobal is false. { - const r = repl.start({ - input: stream, - output: stream, + const { replServer, output } = startNewREPLServer({ + terminal: false, useGlobal: false }); - let output = ''; - stream.write = function(d) { - output += d; - }; - // Ensure that the repl context gets its own "console" instance. - assert(r.context.console); + assert(replServer.context.console); // Ensure that the repl console instance is not the global one. - assert.notStrictEqual(r.context.console, console); - assert.notStrictEqual(r.context.Object, Object); + assert.notStrictEqual(replServer.context.console, console); + assert.notStrictEqual(replServer.context.Object, Object); - stream.run(['({} instanceof Object)']); + replServer.write('({} instanceof Object)\n'); - assert.strictEqual(output, 'true\n> '); + assert.strictEqual(output.accumulator, 'true\n'); - const context = r.createContext(); + const context = replServer.createContext(); // Ensure that the repl context gets its own "console" instance. assert(context.console instanceof require('console').Console); @@ -41,44 +31,46 @@ const stream = new ArrayStream(); // Ensure that the repl console instance is writable. context.console = 'foo'; - r.close(); + replServer.close(); } // Test for context side effects. { - const server = repl.start({ input: stream, output: stream }); + const { replServer } = startNewREPLServer({ + useGlobal: false + }); - assert.ok(!server.underscoreAssigned); - assert.strictEqual(server.lines.length, 0); + assert.ok(!replServer.underscoreAssigned); + assert.strictEqual(replServer.lines.length, 0); // An assignment to '_' in the repl server - server.write('_ = 500;\n'); - assert.ok(server.underscoreAssigned); - assert.strictEqual(server.lines.length, 1); - assert.strictEqual(server.lines[0], '_ = 500;'); - assert.strictEqual(server.last, 500); + replServer.write('_ = 500;\n'); + assert.ok(replServer.underscoreAssigned); + assert.strictEqual(replServer.lines.length, 1); + assert.strictEqual(replServer.lines[0], '_ = 500;'); + assert.strictEqual(replServer.last, 500); // Use the server to create a new context - const context = server.createContext(); + const context = replServer.createContext(); // Ensure that creating a new context does not // have side effects on the server - assert.ok(server.underscoreAssigned); - assert.strictEqual(server.lines.length, 1); - assert.strictEqual(server.lines[0], '_ = 500;'); - assert.strictEqual(server.last, 500); + assert.ok(replServer.underscoreAssigned); + assert.strictEqual(replServer.lines.length, 1); + assert.strictEqual(replServer.lines[0], '_ = 500;'); + assert.strictEqual(replServer.last, 500); // Reset the server context - server.resetContext(); - assert.ok(!server.underscoreAssigned); - assert.strictEqual(server.lines.length, 0); + replServer.resetContext(); + assert.ok(!replServer.underscoreAssigned); + assert.strictEqual(replServer.lines.length, 0); // Ensure that assigning to '_' in the new context // does not change the value in our server. - assert.ok(!server.underscoreAssigned); + assert.ok(!replServer.underscoreAssigned); vm.runInContext('_ = 1000;\n', context); - assert.ok(!server.underscoreAssigned); - assert.strictEqual(server.lines.length, 0); - server.close(); + assert.ok(!replServer.underscoreAssigned); + assert.strictEqual(replServer.lines.length, 0); + replServer.close(); } diff --git a/test/parallel/test-repl-custom-eval-previews.js b/test/parallel/test-repl-custom-eval-previews.js index 1d709109aeb194..303115dd6fdfb1 100644 --- a/test/parallel/test-repl-custom-eval-previews.js +++ b/test/parallel/test-repl-custom-eval-previews.js @@ -1,44 +1,35 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); const assert = require('assert'); const { describe, it } = require('node:test'); common.skipIfInspectorDisabled(); -const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); const testingReplPrompt = '_REPL_TESTING_PROMPT_>'; // Processes some input in a REPL instance and returns a promise that // resolves to the produced output (as a string). -function getReplRunOutput(input, replOptions) { +function getReplRunOutput(inputStr, replOptions) { return new Promise((resolve) => { - const inputStream = new ArrayStream(); - const outputStream = new ArrayStream(); + const { replServer, input, output } = startNewREPLServer({ prompt: testingReplPrompt, ...replOptions }); - const replServer = repl.start({ - input: inputStream, - output: outputStream, - prompt: testingReplPrompt, - ...replOptions, - }); + output.accumulator = ''; - let output = ''; - - outputStream.write = (chunk) => { - output += chunk; + output.write = (chunk) => { + output.accumulator += chunk; // The prompt appears after the input has been processed - if (output.includes(testingReplPrompt)) { + if (output.accumulator.includes(testingReplPrompt)) { replServer.close(); - resolve(output); + resolve(output.accumulator); } }; - inputStream.emit('data', input); + input.emit('data', inputStr); - inputStream.run(['']); + input.run(['']); }); } diff --git a/test/parallel/test-repl-custom-eval.js b/test/parallel/test-repl-custom-eval.js index 46890b2b5d1c84..190e1c573aa18f 100644 --- a/test/parallel/test-repl-custom-eval.js +++ b/test/parallel/test-repl-custom-eval.js @@ -1,42 +1,32 @@ 'use strict'; require('../common'); -const ArrayStream = require('../common/arraystream'); +const { startNewREPLServer } = require('../common/repl'); const assert = require('assert'); const { describe, it } = require('node:test'); -const repl = require('repl'); +const testingReplPrompt = '_REPL_TESTING_PROMPT_>'; // Processes some input in a REPL instance and returns a promise that // resolves to the produced output (as a string). -function getReplRunOutput(input, replOptions) { +function getReplRunOutput(inputStr, replOptions) { return new Promise((resolve) => { - const inputStream = new ArrayStream(); - const outputStream = new ArrayStream(); + const { replServer, input, output } = startNewREPLServer({ prompt: testingReplPrompt, ...replOptions }); - const testingReplPrompt = '_REPL_TESTING_PROMPT_>'; + output.accumulator = ''; - const replServer = repl.start({ - input: inputStream, - output: outputStream, - prompt: testingReplPrompt, - ...replOptions, - }); - - let output = ''; - - outputStream.write = (chunk) => { - output += chunk; + output.write = (chunk) => { + output.accumulator += chunk; // The prompt appears after the input has been processed - if (output.includes(testingReplPrompt)) { + if (output.accumulator.includes(testingReplPrompt)) { replServer.close(); - resolve(output); + resolve(output.accumulator); } }; - inputStream.emit('data', input); + input.emit('data', inputStr); - inputStream.run(['']); + input.run(['']); }); } @@ -62,23 +52,23 @@ describe('repl with custom eval', { concurrency: true }, () => { it('provides a repl context to the eval callback', async () => { const context = await new Promise((resolve) => { - const r = repl.start({ + const { replServer } = startNewREPLServer({ eval: (_cmd, context) => resolve(context), }); - r.context = { foo: 'bar' }; - r.write('\n.exit\n'); + replServer.context = { foo: 'bar' }; + replServer.write('\n.exit\n'); }); assert.strictEqual(context.foo, 'bar'); }); it('provides the global context to the eval callback', async () => { const context = await new Promise((resolve) => { - const r = repl.start({ - useGlobal: true, + const { replServer } = startNewREPLServer({ eval: (_cmd, context) => resolve(context), + useGlobal: true }); global.foo = 'global_foo'; - r.write('\n.exit\n'); + replServer.write('\n.exit\n'); }); assert.strictEqual(context.foo, 'global_foo'); @@ -88,12 +78,12 @@ describe('repl with custom eval', { concurrency: true }, () => { it('inherits variables from the global context but does not use it afterwords if `useGlobal` is false', async () => { global.bar = 'global_bar'; const context = await new Promise((resolve) => { - const r = repl.start({ + const { replServer } = startNewREPLServer({ useGlobal: false, eval: (_cmd, context) => resolve(context), }); global.baz = 'global_baz'; - r.write('\n.exit\n'); + replServer.write('\n.exit\n'); }); assert.strictEqual(context.bar, 'global_bar'); @@ -111,10 +101,10 @@ describe('repl with custom eval', { concurrency: true }, () => { */ it('preserves the original input', async () => { const cmd = await new Promise((resolve) => { - const r = repl.start({ + const { replServer } = startNewREPLServer({ eval: (cmd) => resolve(cmd), }); - r.write('function f() {}\n.exit\n'); + replServer.write('function f() {}\n.exit\n'); }); assert.strictEqual(cmd, 'function f() {}\n'); }); diff --git a/test/parallel/test-repl-definecommand.js b/test/parallel/test-repl-definecommand.js index f3973f25d69412..b54f99a5511f27 100644 --- a/test/parallel/test-repl-definecommand.js +++ b/test/parallel/test-repl-definecommand.js @@ -1,25 +1,20 @@ 'use strict'; require('../common'); +const { startNewREPLServer } = require('../common/repl'); const stream = require('stream'); const assert = require('assert'); -const repl = require('repl'); let output = ''; -const inputStream = new stream.PassThrough(); const outputStream = new stream.PassThrough(); outputStream.on('data', function(d) { output += d; }); -const r = repl.start({ - input: inputStream, - output: outputStream, - terminal: true -}); +const { replServer: replServer, input } = startNewREPLServer({ prompt: '> ', terminal: true, output: outputStream }); -r.defineCommand('say1', { +replServer.defineCommand('say1', { help: 'help for say1', action: function(thing) { output = ''; @@ -28,21 +23,21 @@ r.defineCommand('say1', { } }); -r.defineCommand('say2', function() { +replServer.defineCommand('say2', function() { output = ''; this.output.write('hello from say2\n'); this.displayPrompt(); }); -inputStream.write('.help\n'); +input.run(['.help\n']); assert.match(output, /\n\.say1 {5}help for say1\n/); assert.match(output, /\n\.say2\n/); -inputStream.write('.say1 node developer\n'); +input.run(['.say1 node developer\n']); assert.ok(output.startsWith('hello node developer\n'), `say1 output starts incorrectly: "${output}"`); assert.ok(output.includes('> '), `say1 output does not include prompt: "${output}"`); -inputStream.write('.say2 node developer\n'); +input.run(['.say2 node developer\n']); assert.ok(output.startsWith('hello from say2\n'), `say2 output starts incorrectly: "${output}"`); assert.ok(output.includes('> '), diff --git a/test/parallel/test-repl-domain.js b/test/parallel/test-repl-domain.js index 462677d114c13b..b7c8d95dd26c0e 100644 --- a/test/parallel/test-repl-domain.js +++ b/test/parallel/test-repl-domain.js @@ -21,14 +21,18 @@ 'use strict'; require('../common'); +const { startNewREPLServer } = require('../common/repl'); const ArrayStream = require('../common/arraystream'); -const repl = require('repl'); +const stream = new ArrayStream(); -const putIn = new ArrayStream(); -repl.start('', putIn); +startNewREPLServer({ + input: stream, + output: stream, + terminal: false, +}); -putIn.write = function(data) { +stream.write = function(data) { // Don't use assert for this because the domain might catch it, and // give a false negative. Don't throw, just print and exit. if (data === 'OK\n') { @@ -39,7 +43,7 @@ putIn.write = function(data) { } }; -putIn.run([ +stream.run([ 'require("domain").create().on("error", function() { console.log("OK") })' + '.run(function() { throw new Error("threw") })', ]); diff --git a/test/parallel/test-repl-editor.js b/test/parallel/test-repl-editor.js index fee647d0478e50..8f41b3d4e70401 100644 --- a/test/parallel/test-repl-editor.js +++ b/test/parallel/test-repl-editor.js @@ -2,8 +2,7 @@ const common = require('../common'); const assert = require('assert'); -const repl = require('repl'); -const ArrayStream = require('../common/arraystream'); +const { startNewREPLServer } = require('../common/repl'); if (process.env.TERM === 'dumb') { common.skip('skipping - dumb terminal'); @@ -15,30 +14,24 @@ if (process.env.TERM === 'dumb') { const terminalCode = '\u001b[1G\u001b[0J> \u001b[3G'; const terminalCodeRegex = new RegExp(terminalCode.replace(/\[/g, '\\['), 'g'); -function run({ input, output, event, checkTerminalCodes = true }) { - const stream = new ArrayStream(); - let found = ''; - - stream.write = (msg) => found += msg.replace('\r', ''); - +function run({ input: inputStr, output: outputStr, event, checkTerminalCodes = true }) { let expected = `${terminalCode}.editor\n` + '// Entering editor mode (Ctrl+D to finish, Ctrl+C to cancel)\n' + - `${input}${output}\n${terminalCode}`; + `${inputStr}${outputStr}\n${terminalCode}`; - const replServer = repl.start({ + const { replServer, input, output } = startNewREPLServer({ prompt: '> ', terminal: true, - input: stream, - output: stream, useColors: false }); - stream.emit('data', '.editor\n'); - stream.emit('data', input); + input.emit('data', '.editor\n'); + input.emit('data', inputStr); replServer.write('', event); replServer.close(); + let found = output.accumulator; if (!checkTerminalCodes) { found = found.replace(terminalCodeRegex, '').replace(/\n/g, ''); expected = expected.replace(terminalCodeRegex, '').replace(/\n/g, ''); @@ -79,22 +72,15 @@ const tests = [ tests.forEach(run); // Auto code alignment for .editor mode -function testCodeAlignment({ input, cursor = 0, line = '' }) { - const stream = new ArrayStream(); - const outputStream = new ArrayStream(); - - stream.write = () => { throw new Error('Writing not allowed!'); }; - - const replServer = repl.start({ +function testCodeAlignment({ input: inputStr, cursor = 0, line = '' }) { + const { replServer, input } = startNewREPLServer({ prompt: '> ', terminal: true, - input: stream, - output: outputStream, useColors: false }); - stream.emit('data', '.editor\n'); - input.split('').forEach((ch) => stream.emit('data', ch)); + input.emit('data', '.editor\n'); + inputStr.split('').forEach((ch) => input.emit('data', ch)); // Test the content of current line and the cursor position assert.strictEqual(line, replServer.line); assert.strictEqual(cursor, replServer.cursor); diff --git a/test/parallel/test-repl-empty.js b/test/parallel/test-repl-empty.js index 44281f117f0bba..97d8c9bf05ce22 100644 --- a/test/parallel/test-repl-empty.js +++ b/test/parallel/test-repl-empty.js @@ -1,27 +1,23 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); -const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); -{ - let evalCalledWithExpectedArgs = false; +let evalCalledWithExpectedArgs = false; - const options = { - eval: common.mustCall((cmd, context) => { - // Assertions here will not cause the test to exit with an error code - // so set a boolean that is checked later instead. - evalCalledWithExpectedArgs = (cmd === '\n'); - }) - }; +const { replServer } = startNewREPLServer({ + eval: common.mustCall((cmd, context) => { + // Assertions here will not cause the test to exit with an error code + // so set a boolean that is checked later instead. + evalCalledWithExpectedArgs = (cmd === '\n'); + }) +}); - const r = repl.start(options); - - try { - // Empty strings should be sent to the repl's eval function - r.write('\n'); - } finally { - r.write('.exit\n'); - } - - assert(evalCalledWithExpectedArgs); +try { + // Empty strings should be sent to the repl's eval function + replServer.write('\n'); +} finally { + replServer.write('.exit\n'); } + +assert(evalCalledWithExpectedArgs); diff --git a/test/parallel/test-repl-end-emits-exit.js b/test/parallel/test-repl-end-emits-exit.js index 4e1f3d84f5e056..1a869933dc5b94 100644 --- a/test/parallel/test-repl-end-emits-exit.js +++ b/test/parallel/test-repl-end-emits-exit.js @@ -21,28 +21,20 @@ 'use strict'; require('../common'); -const ArrayStream = require('../common/arraystream'); +const { startNewREPLServer } = require('../common/repl'); const assert = require('assert'); -const repl = require('repl'); let terminalExit = 0; let regularExit = 0; -// Create a dummy stream that does nothing -const stream = new ArrayStream(); - function testTerminalMode() { - const r1 = repl.start({ - input: stream, - output: stream, - terminal: true - }); + const { replServer, input } = startNewREPLServer({ terminal: true }); process.nextTick(function() { // Manually fire a ^D keypress - stream.emit('data', '\u0004'); + input.emit('data', '\u0004'); }); - r1.on('exit', function() { + replServer.on('exit', function() { // Should be fired from the simulated ^D keypress terminalExit++; testRegularMode(); @@ -50,17 +42,13 @@ function testTerminalMode() { } function testRegularMode() { - const r2 = repl.start({ - input: stream, - output: stream, - terminal: false - }); + const { replServer, input } = startNewREPLServer({ terminal: true }); process.nextTick(function() { - stream.emit('end'); + input.emit('end'); }); - r2.on('exit', function() { + replServer.on('exit', function() { // Should be fired from the simulated 'end' event regularExit++; }); diff --git a/test/parallel/test-repl-eval-error-after-close.js b/test/parallel/test-repl-eval-error-after-close.js index 6940f12ce009bd..5fe330b49c236e 100644 --- a/test/parallel/test-repl-eval-error-after-close.js +++ b/test/parallel/test-repl-eval-error-after-close.js @@ -1,8 +1,7 @@ 'use strict'; const common = require('../common'); -const repl = require('node:repl'); -const stream = require('node:stream'); +const { startNewREPLServer } = require('../common/repl'); const assert = require('node:assert'); // This test checks that an eval function returning an error in its callback @@ -13,26 +12,18 @@ const assert = require('node:assert'); const close$ = Promise.withResolvers(); const eval$ = Promise.withResolvers(); - const input = new stream.PassThrough(); - const output = new stream.PassThrough(); - - const replServer = repl.start({ - input, - output, + const { replServer, output } = startNewREPLServer({ eval(_cmd, _context, _file, cb) { close$.promise.then(() => { cb(new Error('Error returned from the eval callback')); eval$.resolve(); }); }, + }, { + disableDomainErrorAssert: true, }); - let outputStr = ''; - output.on('data', (data) => { - outputStr += data; - }); - - input.write('\n'); + replServer.write('\n'); replServer.close(); close$.resolve(); @@ -41,5 +32,5 @@ const assert = require('node:assert'); await eval$.promise; - assert.match(outputStr, /Uncaught Error: Error returned from the eval callback/); + assert.match(output.accumulator, /Uncaught Error: Error returned from the eval callback/); })().then(common.mustCall()); diff --git a/test/parallel/test-repl-function-definition-edge-case.js b/test/parallel/test-repl-function-definition-edge-case.js index 128066e368a5af..4e73d9bcdd26bc 100644 --- a/test/parallel/test-repl-function-definition-edge-case.js +++ b/test/parallel/test-repl-function-definition-edge-case.js @@ -2,36 +2,18 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); -const repl = require('repl'); -const stream = require('stream'); +const { startNewREPLServer } = require('../common/repl'); -const r = initRepl(); +const { replServer } = startNewREPLServer({ + useColors: false, + terminal: false +}); -r.input.emit('data', 'function a() { return 42; } (1)\n'); -r.input.emit('data', 'a\n'); -r.input.emit('data', '.exit\n'); -r.once('exit', common.mustCall()); +replServer.input.emit('data', 'function a() { return 42; } (1)\n'); +replServer.input.emit('data', 'a\n'); +replServer.input.emit('data', '.exit\n'); +replServer.once('exit', common.mustCall()); const expected = '1\n[Function: a]\n'; -const got = r.output.accumulator.join(''); +const got = replServer.output.accumulator; assert.strictEqual(got, expected); - -function initRepl() { - const input = new stream(); - input.write = input.pause = input.resume = () => {}; - input.readable = true; - - const output = new stream(); - output.writable = true; - output.accumulator = []; - - output.write = (data) => output.accumulator.push(data); - - return repl.start({ - input, - output, - useColors: false, - terminal: false, - prompt: '' - }); -} diff --git a/test/parallel/test-repl-inspector.js b/test/parallel/test-repl-inspector.js index 1fff9031d500fd..323874df36b494 100644 --- a/test/parallel/test-repl-inspector.js +++ b/test/parallel/test-repl-inspector.js @@ -1,29 +1,22 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); const assert = require('assert'); -const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); common.skipIfInspectorDisabled(); // This test verifies that the V8 inspector API is usable in the REPL. -const putIn = new ArrayStream(); -let output = ''; -putIn.write = function(data) { - output += data; -}; +const { replServer, input, output } = startNewREPLServer(); -const testMe = repl.start('', putIn); +input.run(['const myVariable = 42']); -putIn.run(['const myVariable = 42']); - -testMe.complete('myVar', common.mustCall((error, data) => { +replServer.complete('myVar', common.mustCall((error, data) => { assert.deepStrictEqual(data, [['myVariable'], 'myVar']); })); -putIn.run([ +input.run([ 'const inspector = require("inspector")', 'const session = new inspector.Session()', 'session.connect()', @@ -31,5 +24,5 @@ putIn.run([ 'session.disconnect()', ]); -assert(output.includes( +assert(output.accumulator.includes( "null { result: { type: 'number', value: 2, description: '2' } }")); diff --git a/test/parallel/test-repl-let-process.js b/test/parallel/test-repl-let-process.js index d0524953d74650..eb6cbc6a472c72 100644 --- a/test/parallel/test-repl-let-process.js +++ b/test/parallel/test-repl-let-process.js @@ -1,9 +1,7 @@ 'use strict'; require('../common'); -const ArrayStream = require('../common/arraystream'); -const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); // Regression test for https://github.com/nodejs/node/issues/6802 -const input = new ArrayStream(); -repl.start({ input, output: process.stdout, useGlobal: true }); +const { input } = startNewREPLServer({ useGlobal: true }, { disableDomainErrorAssert: true }); input.run(['let process']); diff --git a/test/parallel/test-repl-load-multiline-no-trailing-newline.js b/test/parallel/test-repl-load-multiline-no-trailing-newline.js index 8fda91e35d1030..b67139c1efca5b 100644 --- a/test/parallel/test-repl-load-multiline-no-trailing-newline.js +++ b/test/parallel/test-repl-load-multiline-no-trailing-newline.js @@ -1,9 +1,8 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); const fixtures = require('../common/fixtures'); const assert = require('assert'); -const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); if (process.env.TERM === 'dumb') { common.skip('skipping - dumb terminal'); @@ -24,21 +23,11 @@ const eat = (food) => ''; undefined `; -let accum = ''; +const { replServer, output } = startNewREPLServer(); -const inputStream = new ArrayStream(); -const outputStream = new ArrayStream(); - -outputStream.write = (data) => accum += data.replace('\r', ''); - -const r = repl.start({ - prompt: '', - input: inputStream, - output: outputStream, - terminal: true, - useColors: false -}); - -r.write(`${command}\n`); -assert.strictEqual(accum.replace(terminalCodeRegex, ''), expected); -r.close(); +replServer.write(`${command}\n`); +assert.strictEqual( + output.accumulator.replace(terminalCodeRegex, ''), + expected +); +replServer.close(); diff --git a/test/parallel/test-repl-load-multiline.js b/test/parallel/test-repl-load-multiline.js index 920f4b1c25d144..c2bf5635c61c30 100644 --- a/test/parallel/test-repl-load-multiline.js +++ b/test/parallel/test-repl-load-multiline.js @@ -1,9 +1,8 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); const fixtures = require('../common/fixtures'); const assert = require('assert'); -const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); if (process.env.TERM === 'dumb') { common.skip('skipping - dumb terminal'); @@ -24,21 +23,8 @@ const eat = (food) => ''; undefined `; -let accum = ''; +const { replServer, output } = startNewREPLServer(); -const inputStream = new ArrayStream(); -const outputStream = new ArrayStream(); - -outputStream.write = (data) => accum += data.replace('\r', ''); - -const r = repl.start({ - prompt: '', - input: inputStream, - output: outputStream, - terminal: true, - useColors: false -}); - -r.write(`${command}\n`); -assert.strictEqual(accum.replace(terminalCodeRegex, ''), expected); -r.close(); +replServer.write(`${command}\n`); +assert.strictEqual(output.accumulator.replace(terminalCodeRegex, ''), expected); +replServer.close(); diff --git a/test/parallel/test-repl-mode.js b/test/parallel/test-repl-mode.js index f8a54d34089b00..fbc9c18cba4998 100644 --- a/test/parallel/test-repl-mode.js +++ b/test/parallel/test-repl-mode.js @@ -1,8 +1,8 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); -const Stream = require('stream'); const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); if (process.env.TERM === 'dumb') { common.skip('skipping - dumb terminal'); @@ -20,26 +20,27 @@ tests.forEach(function(test) { }); function testSloppyMode() { - const cli = initRepl(repl.REPL_MODE_SLOPPY); + const { input, output } = startNewREPLServer({ replMode: repl.REPL_MODE_SLOPPY, terminal: false, prompt: '> ' }); - cli.input.emit('data', 'x = 3\n'); - assert.strictEqual(cli.output.accumulator.join(''), '> 3\n> '); - cli.output.accumulator.length = 0; + input.emit('data', 'x = 3\n'); + assert.strictEqual(output.accumulator, '> 3\n> '); + output.accumulator = ''; - cli.input.emit('data', 'let y = 3\n'); - assert.strictEqual(cli.output.accumulator.join(''), 'undefined\n> '); + input.emit('data', 'let y = 3\n'); + assert.strictEqual(output.accumulator, 'undefined\n> '); } function testStrictMode() { - const cli = initRepl(repl.REPL_MODE_STRICT); + const { input, output } = startNewREPLServer({ replMode: repl.REPL_MODE_STRICT, terminal: false, prompt: '> ' }, { + disableDomainErrorAssert: true, + }); - cli.input.emit('data', 'x = 3\n'); - assert.match(cli.output.accumulator.join(''), - /ReferenceError: x is not defined/); - cli.output.accumulator.length = 0; + input.emit('data', 'x = 3\n'); + assert.match(output.accumulator, /ReferenceError: x is not defined/); + output.accumulator = ''; - cli.input.emit('data', 'let y = 3\n'); - assert.strictEqual(cli.output.accumulator.join(''), 'undefined\n> '); + input.emit('data', 'let y = 3\n'); + assert.strictEqual(output.accumulator, 'undefined\n> '); } function testStrictModeTerminal() { @@ -48,45 +49,21 @@ function testStrictModeTerminal() { return; } // Verify that ReferenceErrors are reported in strict mode previews. - const cli = initRepl(repl.REPL_MODE_STRICT, { - terminal: true - }); + const { input, output } = startNewREPLServer({ replMode: repl.REPL_MODE_STRICT, prompt: '> ' }); - cli.input.emit('data', 'xyz '); + input.emit('data', 'xyz '); assert.ok( - cli.output.accumulator.includes('\n// ReferenceError: xyz is not defined') + output.accumulator.includes('\n// ReferenceError: xyz is not defined') ); } function testAutoMode() { - const cli = initRepl(repl.REPL_MODE_MAGIC); + const { input, output } = startNewREPLServer({ replMode: repl.REPL_MODE_MAGIC, terminal: false, prompt: '> ' }); - cli.input.emit('data', 'x = 3\n'); - assert.strictEqual(cli.output.accumulator.join(''), '> 3\n> '); - cli.output.accumulator.length = 0; - - cli.input.emit('data', 'let y = 3\n'); - assert.strictEqual(cli.output.accumulator.join(''), 'undefined\n> '); -} + input.emit('data', 'x = 3\n'); + assert.strictEqual(output.accumulator, '> 3\n> '); + output.accumulator = ''; -function initRepl(mode, options) { - const input = new Stream(); - input.write = input.pause = input.resume = () => {}; - input.readable = true; - - const output = new Stream(); - output.write = output.pause = output.resume = function(buf) { - output.accumulator.push(buf); - }; - output.accumulator = []; - output.writable = true; - - return repl.start({ - input: input, - output: output, - useColors: false, - terminal: false, - replMode: mode, - ...options - }); + input.emit('data', 'let y = 3\n'); + assert.strictEqual(output.accumulator, 'undefined\n> '); } diff --git a/test/parallel/test-repl-multiline.js b/test/parallel/test-repl-multiline.js index 0a2f95668a1683..6aecb670114484 100644 --- a/test/parallel/test-repl-multiline.js +++ b/test/parallel/test-repl-multiline.js @@ -1,27 +1,15 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); const assert = require('assert'); -const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); + const input = ['const foo = {', '};', 'foo']; function run({ useColors }) { - const inputStream = new ArrayStream(); - const outputStream = new ArrayStream(); - let output = ''; - - outputStream.write = (data) => { output += data.replace('\r', ''); }; - - const r = repl.start({ - prompt: '', - input: inputStream, - output: outputStream, - terminal: true, - useColors - }); + const { replServer, output } = startNewREPLServer({ useColors }); - r.on('exit', common.mustCall(() => { - const actual = output.split('\n'); + replServer.on('exit', common.mustCall(() => { + const actual = output.accumulator.split('\n'); // Validate the output, which contains terminal escape codes. assert.strictEqual(actual.length, 6); @@ -33,8 +21,8 @@ function run({ useColors }) { assert.strictEqual(actual[4], '{}'); })); - inputStream.run(input); - r.close(); + input.forEach((line) => replServer.write(`${line}\n`)); + replServer.close(); } run({ useColors: true }); diff --git a/test/parallel/test-repl-no-terminal.js b/test/parallel/test-repl-no-terminal.js index f569adcc6322cf..803c8b519f69f7 100644 --- a/test/parallel/test-repl-no-terminal.js +++ b/test/parallel/test-repl-no-terminal.js @@ -1,11 +1,8 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); -const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); -const stream = new ArrayStream(); - -const replServer = repl.start({ terminal: false, input: stream, output: stream }); +const { replServer } = startNewREPLServer(); replServer.setupHistory('/nonexistent/file', common.mustSucceed(() => { replServer.close(); diff --git a/test/parallel/test-repl-null-thrown.js b/test/parallel/test-repl-null-thrown.js index 2d28aa2d820ac2..4babbab1e3989e 100644 --- a/test/parallel/test-repl-null-thrown.js +++ b/test/parallel/test-repl-null-thrown.js @@ -1,23 +1,13 @@ 'use strict'; require('../common'); -const repl = require('repl'); const assert = require('assert'); -const Stream = require('stream'); +const { startNewREPLServer } = require('../common/repl'); -const output = new Stream(); -let text = ''; -output.write = output.pause = output.resume = function(buf) { - text += buf.toString(); -}; +const { replServer, output } = startNewREPLServer(); -const replserver = repl.start({ - output: output, - input: process.stdin -}); - -replserver.emit('line', 'process.nextTick(() => { throw null; })'); -replserver.emit('line', '.exit'); +replServer.emit('line', 'process.nextTick(() => { throw null; })'); +replServer.emit('line', '.exit'); setTimeout(() => { - assert(text.includes('Uncaught null')); + assert(output.accumulator.includes('Uncaught null')); }, 0); diff --git a/test/parallel/test-repl-paste-big-data.js b/test/parallel/test-repl-paste-big-data.js index fd247b4b063192..5abc195cecb968 100644 --- a/test/parallel/test-repl-paste-big-data.js +++ b/test/parallel/test-repl-paste-big-data.js @@ -1,36 +1,18 @@ 'use strict'; const common = require('../common'); -const repl = require('repl'); -const stream = require('stream'); const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); // Pasting big input should not cause the process to have a huge CPU usage. const cpuUsage = process.cpuUsage(); -const r = initRepl(); -r.input.emit('data', 'a'.repeat(2e4) + '\n'); -r.input.emit('data', '.exit\n'); +const { replServer } = startNewREPLServer({}, { disableDomainErrorAssert: true }); +replServer.input.emit('data', 'a'.repeat(2e4) + '\n'); +replServer.input.emit('data', '.exit\n'); -r.once('exit', common.mustCall(() => { +replServer.once('exit', common.mustCall(() => { const diff = process.cpuUsage(cpuUsage); assert.ok(diff.user < 1e6); })); - -function initRepl() { - const input = new stream(); - input.write = input.pause = input.resume = () => {}; - input.readable = true; - - const output = new stream(); - output.write = () => {}; - output.writable = true; - - return repl.start({ - input, - output, - terminal: true, - prompt: '' - }); -} diff --git a/test/parallel/test-repl-pretty-custom-stack.js b/test/parallel/test-repl-pretty-custom-stack.js index f5697c2362e261..82df8ff4fc6335 100644 --- a/test/parallel/test-repl-pretty-custom-stack.js +++ b/test/parallel/test-repl-pretty-custom-stack.js @@ -1,41 +1,32 @@ 'use strict'; require('../common'); -const ArrayStream = require('../common/arraystream'); const fixtures = require('../common/fixtures'); const assert = require('assert'); -const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); const stackRegExp = /(REPL\d+):[0-9]+:[0-9]+/g; function run({ command, expected }) { - let accum = ''; - - const inputStream = new ArrayStream(); - const outputStream = new ArrayStream(); - - outputStream.write = (data) => accum += data.replace('\r', ''); - - const r = repl.start({ - prompt: '', - input: inputStream, - output: outputStream, + const { replServer, output } = startNewREPLServer({ terminal: false, useColors: false + }, { + disableDomainErrorAssert: true, }); - r.write(`${command}\n`); + replServer.write(`${command}\n`); if (typeof expected === 'string') { assert.strictEqual( - accum.replace(stackRegExp, '$1:*:*'), + output.accumulator.replace(stackRegExp, '$1:*:*'), expected.replace(stackRegExp, '$1:*:*') ); } else { assert.match( - accum.replace(stackRegExp, '$1:*:*'), + output.accumulator.replace(stackRegExp, '$1:*:*'), expected ); } - r.close(); + replServer.close(); } const origPrepareStackTrace = Error.prepareStackTrace; diff --git a/test/parallel/test-repl-pretty-stack-custom-writer.js b/test/parallel/test-repl-pretty-stack-custom-writer.js index 877f8cb8077597..2d39633030d775 100644 --- a/test/parallel/test-repl-pretty-stack-custom-writer.js +++ b/test/parallel/test-repl-pretty-stack-custom-writer.js @@ -1,23 +1,18 @@ 'use strict'; require('../common'); -const { PassThrough } = require('stream'); const assert = require('assert'); -const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); -{ - const input = new PassThrough(); - const output = new PassThrough(); +const testingReplPrompt = '_REPL_TESTING_PROMPT_>'; - const r = repl.start({ - prompt: '', - input, - output, - writer: String, - terminal: false, - useColors: false - }); +const { replServer, output } = startNewREPLServer( + { prompt: testingReplPrompt }, + { disableDomainErrorAssert: true } +); - r.write('throw new Error("foo[a]")\n'); - r.close(); - assert.strictEqual(output.read().toString(), 'Uncaught Error: foo[a]\n'); -} +replServer.write('throw new Error("foo[a]")\n'); + +assert.strictEqual( + output.accumulator.split('\n').filter((line) => !line.includes(testingReplPrompt)).join(''), + 'Uncaught Error: foo[a]' +); diff --git a/test/parallel/test-repl-pretty-stack.js b/test/parallel/test-repl-pretty-stack.js index 2aad0e09b6d9ba..a7f13dea75aaea 100644 --- a/test/parallel/test-repl-pretty-stack.js +++ b/test/parallel/test-repl-pretty-stack.js @@ -1,43 +1,34 @@ 'use strict'; require('../common'); -const ArrayStream = require('../common/arraystream'); const fixtures = require('../common/fixtures'); const assert = require('assert'); -const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); const stackRegExp = /(at .*REPL\d+:)[0-9]+:[0-9]+/g; function run({ command, expected, ...extraREPLOptions }, i) { - let accum = ''; + const { replServer, output } = startNewREPLServer( + { + terminal: false, + useColors: false, + ...extraREPLOptions + }, + { disableDomainErrorAssert: true } + ); - const inputStream = new ArrayStream(); - const outputStream = new ArrayStream(); - - outputStream.write = (data) => accum += data.replace('\r', ''); - - const r = repl.start({ - prompt: '', - input: inputStream, - output: outputStream, - terminal: false, - useColors: false, - ...extraREPLOptions - }); - - r.write(`${command}\n`); - console.log(i); + replServer.write(`${command}\n`); if (typeof expected === 'string') { assert.strictEqual( - accum.replace(stackRegExp, '$1*:*'), + output.accumulator.replace(stackRegExp, '$1*:*'), expected.replace(stackRegExp, '$1*:*') ); } else { assert.match( - accum.replace(stackRegExp, '$1*:*'), + output.accumulator.replace(stackRegExp, '$1*:*'), expected ); } - r.close(); + replServer.close(); } const tests = [ diff --git a/test/parallel/test-repl-preview-newlines.js b/test/parallel/test-repl-preview-newlines.js index 02d294032acd13..22ffe0db108590 100644 --- a/test/parallel/test-repl-preview-newlines.js +++ b/test/parallel/test-repl-preview-newlines.js @@ -1,29 +1,21 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); const assert = require('assert'); -const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); common.skipIfInspectorDisabled(); -const inputStream = new ArrayStream(); -const outputStream = new ArrayStream(); -repl.start({ - input: inputStream, - output: outputStream, - useGlobal: false, - terminal: true, - useColors: true -}); +const { input, output } = startNewREPLServer( + { useColors: true }, { disableDomainErrorAssert: true } +); -let output = ''; -outputStream.write = (chunk) => output += chunk; +output.accumulator = ''; for (const char of ['\\n', '\\v', '\\r']) { - inputStream.emit('data', `"${char}"()`); + input.emit('data', `"${char}"()`); // Make sure the output is on a single line - assert.strictEqual(output, `"${char}"()\n\x1B[90mTypeError: "\x1B[39m\x1B[9G\x1B[1A`); - inputStream.run(['']); - output = ''; + assert.strictEqual(output.accumulator, `"${char}"()\n\x1B[90mTypeError: "\x1B[39m\x1B[7G\x1B[1A`); + input.run(['']); + output.accumulator = ''; } diff --git a/test/parallel/test-repl-preview-timeout.js b/test/parallel/test-repl-preview-timeout.js index df6a8cf2b1cba7..cf2f244c814713 100644 --- a/test/parallel/test-repl-preview-timeout.js +++ b/test/parallel/test-repl-preview-timeout.js @@ -1,27 +1,17 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); const assert = require('assert'); -const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); common.skipIfInspectorDisabled(); -const inputStream = new ArrayStream(); -const outputStream = new ArrayStream(); -repl.start({ - input: inputStream, - output: outputStream, - useGlobal: false, - terminal: true, - useColors: true -}); +const { output, input } = startNewREPLServer(); -let output = ''; -outputStream.write = (chunk) => output += chunk; +output.accumulator = ''; // Input without '\n' triggering actual run. -const input = 'while (true) {}'; -inputStream.emit('data', input); +const inputStr = 'while (true) {}'; +input.emit('data', inputStr); // No preview available when timed out. -assert.strictEqual(output, input); +assert.strictEqual(output.accumulator, inputStr); diff --git a/test/parallel/test-repl-reset-event.js b/test/parallel/test-repl-reset-event.js index 1f1347547e95f8..195ff581fe7803 100644 --- a/test/parallel/test-repl-reset-event.js +++ b/test/parallel/test-repl-reset-event.js @@ -21,27 +21,19 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); const assert = require('assert'); -const repl = require('repl'); const util = require('util'); +const { startNewREPLServer } = require('../common/repl'); common.allowGlobals(42); -// Create a dummy stream that does nothing -const dummy = new ArrayStream(); - function testReset(cb) { - const r = repl.start({ - input: dummy, - output: dummy, - useGlobal: false - }); - r.context.foo = 42; - r.on('reset', common.mustCall(function(context) { + const { replServer } = startNewREPLServer(); + replServer.context.foo = 42; + replServer.on('reset', common.mustCall(function(context) { assert(!!context, 'REPL did not emit a context with reset event'); - assert.strictEqual(context, r.context, 'REPL emitted incorrect context. ' + - `context is ${util.inspect(context)}, expected ${util.inspect(r.context)}`); + assert.strictEqual(context, replServer.context, 'REPL emitted incorrect context. ' + + `context is ${util.inspect(context)}, expected ${util.inspect(replServer.context)}`); assert.strictEqual( context.foo, undefined, @@ -51,17 +43,13 @@ function testReset(cb) { context.foo = 42; cb(); })); - r.resetContext(); + replServer.resetContext(); } function testResetGlobal() { - const r = repl.start({ - input: dummy, - output: dummy, - useGlobal: true - }); - r.context.foo = 42; - r.on('reset', common.mustCall(function(context) { + const { replServer } = startNewREPLServer({ useGlobal: true }); + replServer.context.foo = 42; + replServer.on('reset', common.mustCall(function(context) { assert.strictEqual( context.foo, 42, @@ -69,7 +57,7 @@ function testResetGlobal() { `context.foo is ${context.foo}, expected 42.` ); })); - r.resetContext(); + replServer.resetContext(); } testReset(common.mustCall(testResetGlobal)); diff --git a/test/parallel/test-repl-save-load-editor-mode.js b/test/parallel/test-repl-save-load-editor-mode.js index c8f76c6894b08d..83a57cdaa55a77 100644 --- a/test/parallel/test-repl-save-load-editor-mode.js +++ b/test/parallel/test-repl-save-load-editor-mode.js @@ -1,30 +1,17 @@ 'use strict'; require('../common'); -const ArrayStream = require('../common/arraystream'); - const assert = require('node:assert'); const fs = require('node:fs'); -const repl = require('node:repl'); const path = require('node:path'); +const { startNewREPLServer } = require('../common/repl'); const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); // Test for saving a REPL session in editor mode -const input = new ArrayStream(); - -const replServer = repl.start({ - prompt: '', - input, - output: new ArrayStream(), - allowBlockingCompletions: true, - terminal: true, -}); - -// Some errors are passed to the domain, but do not callback -replServer._domain.on('error', assert.ifError); +const { replServer, input } = startNewREPLServer(); input.run(['.editor']); diff --git a/test/parallel/test-repl-save-load-invalid-save.js b/test/parallel/test-repl-save-load-invalid-save.js index 2eabb96e84c399..60039ee5b15a3a 100644 --- a/test/parallel/test-repl-save-load-invalid-save.js +++ b/test/parallel/test-repl-save-load-invalid-save.js @@ -1,28 +1,15 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); - const assert = require('node:assert'); -const repl = require('node:repl'); +const { startNewREPLServer } = require('../common/repl'); const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); // Test for the appropriate handling of cases in which REPL saves fail -const input = new ArrayStream(); -const output = new ArrayStream(); - -const replServer = repl.start({ - prompt: '', - input, - output, - allowBlockingCompletions: true, -}); - -// Some errors are passed to the domain, but do not callback -replServer._domain.on('error', assert.ifError); +const { replServer, input, output } = startNewREPLServer({ terminal: false }); // NUL (\0) is disallowed in filenames in UNIX-like operating systems and // Windows so we can use that to test failed saves. diff --git a/test/parallel/test-repl-save-load-load-dir.js b/test/parallel/test-repl-save-load-load-dir.js index 0cdba7a5e8c417..20bfae436a9cb4 100644 --- a/test/parallel/test-repl-save-load-load-dir.js +++ b/test/parallel/test-repl-save-load-load-dir.js @@ -1,28 +1,15 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); - +const { startNewREPLServer } = require('../common/repl'); const assert = require('node:assert'); -const repl = require('node:repl'); const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); // Tests that an appropriate error is displayed if the user tries to load a directory instead of a file -const input = new ArrayStream(); -const output = new ArrayStream(); - -const replServer = repl.start({ - prompt: '', - input, - output, - allowBlockingCompletions: true, -}); - -// Some errors are passed to the domain, but do not callback -replServer._domain.on('error', assert.ifError); +const { replServer, input, output } = startNewREPLServer({ terminal: false }); const dirPath = tmpdir.path; diff --git a/test/parallel/test-repl-save-load-load-non-existent.js b/test/parallel/test-repl-save-load-load-non-existent.js index fe676a0986c4cd..1456316ae4479c 100644 --- a/test/parallel/test-repl-save-load-load-non-existent.js +++ b/test/parallel/test-repl-save-load-load-non-existent.js @@ -1,28 +1,15 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); - +const { startNewREPLServer } = require('../common/repl'); const assert = require('node:assert'); -const repl = require('node:repl'); const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); // Tests that an appropriate error is displayed if the user tries to load a non existent file -const input = new ArrayStream(); -const output = new ArrayStream(); - -const replServer = repl.start({ - prompt: '', - input, - output, - allowBlockingCompletions: true, -}); - -// Some errors are passed to the domain, but do not callback -replServer._domain.on('error', assert.ifError); +const { replServer, input, output } = startNewREPLServer({ terminal: false }); const filePath = tmpdir.resolve('file.does.not.exist'); diff --git a/test/parallel/test-repl-save-load-load-without-name.js b/test/parallel/test-repl-save-load-load-without-name.js index 60d409e28442fb..b52b7f37015a98 100644 --- a/test/parallel/test-repl-save-load-load-without-name.js +++ b/test/parallel/test-repl-save-load-load-without-name.js @@ -1,28 +1,15 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); - +const { startNewREPLServer } = require('../common/repl'); const assert = require('node:assert'); -const repl = require('node:repl'); const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); // Tests that an appropriate error is displayed if .load is called without a filename -const input = new ArrayStream(); -const output = new ArrayStream(); - -const replServer = repl.start({ - prompt: '', - input, - output, - allowBlockingCompletions: true, -}); - -// Some errors are passed to the domain, but do not callback -replServer._domain.on('error', assert.ifError); +const { replServer, input, output } = startNewREPLServer({ terminal: false }); output.write = common.mustCall(function(data) { assert.strictEqual(data, 'The "file" argument must be specified\n'); diff --git a/test/parallel/test-repl-save-load-save-without-name.js b/test/parallel/test-repl-save-load-save-without-name.js index 316f41b853ae42..d12e5f2dd4599d 100644 --- a/test/parallel/test-repl-save-load-save-without-name.js +++ b/test/parallel/test-repl-save-load-save-without-name.js @@ -1,28 +1,15 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); - +const { startNewREPLServer } = require('../common/repl'); const assert = require('node:assert'); -const repl = require('node:repl'); const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); // Tests that an appropriate error is displayed if .save is called without a filename -const input = new ArrayStream(); -const output = new ArrayStream(); - -const replServer = repl.start({ - prompt: '', - input, - output, - allowBlockingCompletions: true, -}); - -// Some errors are passed to the domain, but do not callback -replServer._domain.on('error', assert.ifError); +const { replServer, input, output } = startNewREPLServer({ terminal: false }); output.write = common.mustCall(function(data) { assert.strictEqual(data, 'The "file" argument must be specified\n'); diff --git a/test/parallel/test-repl-save-load.js b/test/parallel/test-repl-save-load.js index 0d5263749d5b46..d8401c2d427893 100644 --- a/test/parallel/test-repl-save-load.js +++ b/test/parallel/test-repl-save-load.js @@ -22,11 +22,9 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); - +const { startNewREPLServer } = require('../common/repl'); const assert = require('node:assert'); const fs = require('node:fs'); -const repl = require('node:repl'); const path = require('node:path'); const tmpdir = require('../common/tmpdir'); @@ -34,17 +32,7 @@ tmpdir.refresh(); // Tests that a REPL session data can be saved to and loaded from a file -const input = new ArrayStream(); - -const replServer = repl.start({ - prompt: '', - input, - output: new ArrayStream(), - allowBlockingCompletions: true, -}); - -// Some errors are passed to the domain, but do not callback -replServer._domain.on('error', assert.ifError); +const { replServer, input } = startNewREPLServer({ terminal: false }); const filePath = path.resolve(tmpdir.path, 'test.save.js'); diff --git a/test/parallel/test-repl-syntax-error-stack.js b/test/parallel/test-repl-syntax-error-stack.js index 2794ded4924a97..1b6e3fb6e879f2 100644 --- a/test/parallel/test-repl-syntax-error-stack.js +++ b/test/parallel/test-repl-syntax-error-stack.js @@ -1,32 +1,32 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); const fixtures = require('../common/fixtures'); const assert = require('assert'); -const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); + let found = false; process.on('exit', () => { assert.strictEqual(found, true); }); -ArrayStream.prototype.write = function(output) { +const { input, output } = startNewREPLServer({}, { disableDomainErrorAssert: true }); + +output.write = (data) => { // Matching only on a minimal piece of the stack because the string will vary // greatly depending on the JavaScript engine. V8 includes `;` because it // displays the line of code (`var foo bar;`) that is causing a problem. // ChakraCore does not display the line of code but includes `;` in the phrase // `Expected ';' `. - if (/;/.test(output)) + if (/;/.test(data)) found = true; }; -const putIn = new ArrayStream(); -repl.start('', putIn); let file = fixtures.path('syntax', 'bad_syntax'); if (common.isWindows) file = file.replace(/\\/g, '\\\\'); -putIn.run(['.clear']); -putIn.run([`require('${file}');`]); +input.run(['.clear']); +input.run([`require('${file}');`]); diff --git a/test/parallel/test-repl-tab-complete-buffer.js b/test/parallel/test-repl-tab-complete-buffer.js index df288937df00c8..72c5e26cb799ae 100644 --- a/test/parallel/test-repl-tab-complete-buffer.js +++ b/test/parallel/test-repl-tab-complete-buffer.js @@ -1,22 +1,11 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); const { hijackStderr, restoreStderr } = require('../common/hijackstdio'); const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); -const repl = require('repl'); - -const input = new ArrayStream(); -const replServer = repl.start({ - prompt: '', - input, - output: process.stdout, - allowBlockingCompletions: true, -}); - -// Some errors are passed to the domain, but do not callback -replServer._domain.on('error', assert.ifError); +const { replServer, input } = startNewREPLServer(); for (const type of [ Array, diff --git a/test/parallel/test-repl-tab-complete-computed-props.js b/test/parallel/test-repl-tab-complete-computed-props.js index aee348b91223b2..418dc5059e9132 100644 --- a/test/parallel/test-repl-tab-complete-computed-props.js +++ b/test/parallel/test-repl-tab-complete-computed-props.js @@ -1,27 +1,10 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); +const { startNewREPLServer } = require('../common/repl'); const { describe, it, before, after } = require('node:test'); const assert = require('assert'); -const repl = require('repl'); - -function prepareREPL() { - const input = new ArrayStream(); - const replServer = repl.start({ - prompt: '', - input, - output: process.stdout, - allowBlockingCompletions: true, - }); - - // Some errors are passed to the domain, but do not callback - replServer._domain.on('error', assert.ifError); - - return { replServer, input }; -} - function testCompletion(replServer, { input, expectedCompletions }) { replServer.complete( input, @@ -36,7 +19,7 @@ describe('REPL tab object completion on computed properties', () => { let replServer; before(() => { - const { replServer: server, input } = prepareREPL(); + const { replServer: server, input } = startNewREPLServer(); replServer = server; input.run([ @@ -97,7 +80,7 @@ describe('REPL tab object completion on computed properties', () => { let replServer; before(() => { - const { replServer: server, input } = prepareREPL(); + const { replServer: server, input } = startNewREPLServer(); replServer = server; input.run([ diff --git a/test/parallel/test-repl-tab-complete-crash.js b/test/parallel/test-repl-tab-complete-crash.js index 95dfe0bd4c4e13..58628eb85b2a38 100644 --- a/test/parallel/test-repl-tab-complete-crash.js +++ b/test/parallel/test-repl-tab-complete-crash.js @@ -1,28 +1,21 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); const assert = require('assert'); -const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); -ArrayStream.prototype.write = () => {}; - -const putIn = new ArrayStream(); -const testMe = repl.start('', putIn); +const { replServer, input } = startNewREPLServer({}, { disableDomainErrorAssert: true }); // https://github.com/nodejs/node/issues/3346 // Tab-completion should be empty -putIn.run(['.clear']); -putIn.run(['function () {']); -testMe.complete('arguments.', common.mustCall((err, completions) => { +input.run(['.clear', 'function () {']); +replServer.complete('arguments.', common.mustCall((err, completions) => { assert.strictEqual(err, null); assert.deepStrictEqual(completions, [[], 'arguments.']); })); -putIn.run(['.clear']); -putIn.run(['function () {']); -putIn.run(['undef;']); -testMe.complete('undef.', common.mustCall((err, completions) => { +input.run(['.clear', 'function () {', 'undef;']); +replServer.complete('undef.', common.mustCall((err, completions) => { assert.strictEqual(err, null); assert.deepStrictEqual(completions, [[], 'undef.']); })); diff --git a/test/parallel/test-repl-tab-complete-custom-completer.js b/test/parallel/test-repl-tab-complete-custom-completer.js index aee9538fc807ad..1599331f2a2b2c 100644 --- a/test/parallel/test-repl-tab-complete-custom-completer.js +++ b/test/parallel/test-repl-tab-complete-custom-completer.js @@ -1,21 +1,14 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); const assert = require('assert'); - -const repl = require('repl'); - -const putIn = new ArrayStream(); +const { startNewREPLServer } = require('../common/repl'); // To test custom completer function. // Sync mode. { const customCompletions = 'aaa aa1 aa2 bbb bb1 bb2 bb3 ccc ddd eee'.split(' '); - const testCustomCompleterSyncMode = repl.start({ - prompt: '', - input: putIn, - output: putIn, + const { replServer } = startNewREPLServer({ completer: function completer(line) { const hits = customCompletions.filter((c) => c.startsWith(line)); // Show all completions if none found. @@ -25,7 +18,7 @@ const putIn = new ArrayStream(); // On empty line should output all the custom completions // without complete anything. - testCustomCompleterSyncMode.complete('', common.mustCall((error, data) => { + replServer.complete('', common.mustCall((error, data) => { assert.deepStrictEqual(data, [ customCompletions, '', @@ -33,7 +26,7 @@ const putIn = new ArrayStream(); })); // On `a` should output `aaa aa1 aa2` and complete until `aa`. - testCustomCompleterSyncMode.complete('a', common.mustCall((error, data) => { + replServer.complete('a', common.mustCall((error, data) => { assert.deepStrictEqual(data, [ 'aaa aa1 aa2'.split(' '), 'a', @@ -45,10 +38,7 @@ const putIn = new ArrayStream(); // Async mode. { const customCompletions = 'aaa aa1 aa2 bbb bb1 bb2 bb3 ccc ddd eee'.split(' '); - const testCustomCompleterAsyncMode = repl.start({ - prompt: '', - input: putIn, - output: putIn, + const { replServer } = startNewREPLServer({ completer: function completer(line, callback) { const hits = customCompletions.filter((c) => c.startsWith(line)); // Show all completions if none found. @@ -58,7 +48,7 @@ const putIn = new ArrayStream(); // On empty line should output all the custom completions // without complete anything. - testCustomCompleterAsyncMode.complete('', common.mustCall((error, data) => { + replServer.complete('', common.mustCall((error, data) => { assert.deepStrictEqual(data, [ customCompletions, '', @@ -66,7 +56,7 @@ const putIn = new ArrayStream(); })); // On `a` should output `aaa aa1 aa2` and complete until `aa`. - testCustomCompleterAsyncMode.complete('a', common.mustCall((error, data) => { + replServer.complete('a', common.mustCall((error, data) => { assert.deepStrictEqual(data, [ 'aaa aa1 aa2'.split(' '), 'a', diff --git a/test/parallel/test-repl-tab-complete-files.js b/test/parallel/test-repl-tab-complete-files.js index 35da204709c103..ddb3df07176be1 100644 --- a/test/parallel/test-repl-tab-complete-files.js +++ b/test/parallel/test-repl-tab-complete-files.js @@ -1,9 +1,9 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); const assert = require('assert'); const path = require('path'); +const { startNewREPLServer } = require('../common/repl'); const { isMainThread } = require('worker_threads'); @@ -11,17 +11,7 @@ if (!isMainThread) { common.skip('process.chdir is not available in Workers'); } -const repl = require('repl'); - -const replServer = repl.start({ - prompt: '', - input: new ArrayStream(), - output: process.stdout, - allowBlockingCompletions: true, -}); - -// Some errors are passed to the domain, but do not callback -replServer._domain.on('error', assert.ifError); +const { replServer } = startNewREPLServer(); // Tab completion for files/directories { diff --git a/test/parallel/test-repl-tab-complete-getter-error.js b/test/parallel/test-repl-tab-complete-getter-error.js index cebe008247c3a8..1a3f9aedad1ef8 100644 --- a/test/parallel/test-repl-tab-complete-getter-error.js +++ b/test/parallel/test-repl-tab-complete-getter-error.js @@ -1,29 +1,15 @@ 'use strict'; const common = require('../common'); -const repl = require('repl'); -const ArrayStream = require('../common/arraystream'); const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); (async function() { await runTest(); })().then(common.mustCall()); async function runTest() { - const input = new ArrayStream(); - const output = new ArrayStream(); - - const replServer = repl.start({ - prompt: '', - input, - output: output, - allowBlockingCompletions: true, - terminal: true - }); - - replServer._domain.on('error', (e) => { - assert.fail(`Error in REPL domain: ${e}`); - }); + const { replServer } = startNewREPLServer(); await new Promise((resolve, reject) => { replServer.eval(` diff --git a/test/parallel/test-repl-tab-complete-import.js b/test/parallel/test-repl-tab-complete-import.js index 3ce48ac45b1f1e..1f6cf7bff94b22 100644 --- a/test/parallel/test-repl-tab-complete-import.js +++ b/test/parallel/test-repl-tab-complete-import.js @@ -1,7 +1,6 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); const fixtures = require('../common/fixtures'); const assert = require('assert'); const { builtinModules } = require('module'); @@ -19,20 +18,12 @@ if (!isMainThread) { process.chdir(fixtures.fixturesDir); const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); -const putIn = new ArrayStream(); -const testMe = repl.start({ - prompt: '', - input: putIn, - output: process.stdout, - allowBlockingCompletions: true -}); - -// Some errors are passed to the domain, but do not callback -testMe._domain.on('error', assert.ifError); +const { replServer, input } = startNewREPLServer(); // Tab complete provides built in libs for import() -testMe.complete('import(\'', common.mustSucceed((data) => { +replServer.complete('import(\'', common.mustSucceed((data) => { publicUnprefixedModules.forEach((lib) => { assert( data[0].includes(lib) && data[0].includes(`node:${lib}`), @@ -42,14 +33,14 @@ testMe.complete('import(\'', common.mustSucceed((data) => { const newModule = 'foobar'; assert(!builtinModules.includes(newModule)); repl.builtinModules.push(newModule); - testMe.complete('import(\'', common.mustSucceed(([modules]) => { + replServer.complete('import(\'', common.mustSucceed(([modules]) => { assert.strictEqual(data[0].length + 1, modules.length); assert(modules.includes(newModule) && !modules.includes(`node:${newModule}`)); })); })); -testMe.complete("import\t( 'n", common.mustSucceed((data) => { +replServer.complete("import\t( 'n", common.mustSucceed((data) => { assert.strictEqual(data.length, 2); assert.strictEqual(data[1], 'n'); const completions = data[0]; @@ -74,38 +65,37 @@ testMe.complete("import\t( 'n", common.mustSucceed((data) => { const expected = ['@nodejsscope', '@nodejsscope/']; // Import calls should handle all types of quotation marks. for (const quotationMark of ["'", '"', '`']) { - putIn.run(['.clear']); - testMe.complete('import(`@nodejs', common.mustSucceed((data) => { + input.run(['.clear']); + replServer.complete('import(`@nodejs', common.mustSucceed((data) => { assert.deepStrictEqual(data, [expected, '@nodejs']); })); - putIn.run(['.clear']); + input.run(['.clear']); // Completions should not be greedy in case the quotation ends. - const input = `import(${quotationMark}@nodejsscope${quotationMark}`; - testMe.complete(input, common.mustSucceed((data) => { + replServer.complete(`import(${quotationMark}@nodejsscope${quotationMark}`, common.mustSucceed((data) => { assert.deepStrictEqual(data, [[], undefined]); })); } } { - putIn.run(['.clear']); + input.run(['.clear']); // Completions should find modules and handle whitespace after the opening // bracket. - testMe.complete('import \t("no_ind', common.mustSucceed((data) => { + replServer.complete('import \t("no_ind', common.mustSucceed((data) => { assert.deepStrictEqual(data, [['no_index', 'no_index/'], 'no_ind']); })); } // Test tab completion for import() relative to the current directory { - putIn.run(['.clear']); + input.run(['.clear']); const cwd = process.cwd(); process.chdir(__dirname); ['import(\'.', 'import(".'].forEach((input) => { - testMe.complete(input, common.mustSucceed((data) => { + replServer.complete(input, common.mustSucceed((data) => { assert.strictEqual(data.length, 2); assert.strictEqual(data[1], '.'); assert.strictEqual(data[0].length, 2); @@ -115,14 +105,14 @@ testMe.complete("import\t( 'n", common.mustSucceed((data) => { }); ['import(\'..', 'import("..'].forEach((input) => { - testMe.complete(input, common.mustSucceed((data) => { + replServer.complete(input, common.mustSucceed((data) => { assert.deepStrictEqual(data, [['../'], '..']); })); }); ['./', './test-'].forEach((path) => { [`import('${path}`, `import("${path}`].forEach((input) => { - testMe.complete(input, common.mustSucceed((data) => { + replServer.complete(input, common.mustSucceed((data) => { assert.strictEqual(data.length, 2); assert.strictEqual(data[1], path); assert.ok(data[0].includes('./test-repl-tab-complete.js')); @@ -132,7 +122,7 @@ testMe.complete("import\t( 'n", common.mustSucceed((data) => { ['../parallel/', '../parallel/test-'].forEach((path) => { [`import('${path}`, `import("${path}`].forEach((input) => { - testMe.complete(input, common.mustSucceed((data) => { + replServer.complete(input, common.mustSucceed((data) => { assert.strictEqual(data.length, 2); assert.strictEqual(data[1], path); assert.ok(data[0].includes('../parallel/test-repl-tab-complete.js')); @@ -142,7 +132,7 @@ testMe.complete("import\t( 'n", common.mustSucceed((data) => { { const path = '../fixtures/repl-folder-extensions/f'; - testMe.complete(`import('${path}`, common.mustSucceed((data) => { + replServer.complete(`import('${path}`, common.mustSucceed((data) => { assert.strictEqual(data.length, 2); assert.strictEqual(data[1], path); assert.ok(data[0].includes( diff --git a/test/parallel/test-repl-tab-complete-no-warn.js b/test/parallel/test-repl-tab-complete-no-warn.js index 7aedee69c31cb7..df995aba6a4ed3 100644 --- a/test/parallel/test-repl-tab-complete-no-warn.js +++ b/test/parallel/test-repl-tab-complete-no-warn.js @@ -1,21 +1,17 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); -const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); const DEFAULT_MAX_LISTENERS = require('events').defaultMaxListeners; -ArrayStream.prototype.write = () => {}; - -const putIn = new ArrayStream(); -const testMe = repl.start('', putIn); +const { replServer, input } = startNewREPLServer(); // https://github.com/nodejs/node/issues/18284 // Tab-completion should not repeatedly add the // `Runtime.executionContextCreated` listener process.on('warning', common.mustNotCall()); -putIn.run(['async function test() {']); +input.run(['async function test() {']); for (let i = 0; i < DEFAULT_MAX_LISTENERS; i++) { - testMe.complete('await Promise.resolve()', () => {}); + replServer.complete('await Promise.resolve()', () => {}); } diff --git a/test/parallel/test-repl-tab-complete-nosideeffects.js b/test/parallel/test-repl-tab-complete-nosideeffects.js index 93cd9752925ced..54562e2f1d2954 100644 --- a/test/parallel/test-repl-tab-complete-nosideeffects.js +++ b/test/parallel/test-repl-tab-complete-nosideeffects.js @@ -1,26 +1,9 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); const { describe, it } = require('node:test'); const assert = require('assert'); - -const repl = require('repl'); - -function prepareREPL() { - const input = new ArrayStream(); - const replServer = repl.start({ - prompt: '', - input, - output: process.stdout, - allowBlockingCompletions: true, - }); - - // Some errors are passed to the domain, but do not callback - replServer._domain.on('error', assert.ifError); - - return { replServer, input }; -} +const { startNewREPLServer } = require('../common/repl'); function getNoResultsFunction() { return common.mustSucceed((data) => { @@ -44,7 +27,7 @@ describe('REPL tab completion without side effects', () => { 'arr[incCounter()].b', ]) { it(`does not evaluate with side effects (${code})`, async () => { - const { replServer, input } = prepareREPL(); + const { replServer, input } = startNewREPLServer(); input.run(setup); replServer.complete(code, getNoResultsFunction()); diff --git a/test/parallel/test-repl-tab-complete-on-editor-mode.js b/test/parallel/test-repl-tab-complete-on-editor-mode.js index a8a21ff8b13304..6e2ef8b5670db4 100644 --- a/test/parallel/test-repl-tab-complete-on-editor-mode.js +++ b/test/parallel/test-repl-tab-complete-on-editor-mode.js @@ -2,41 +2,28 @@ const common = require('../common'); const assert = require('assert'); -const ArrayStream = require('../common/arraystream'); -const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); // Tab completion in editor mode { - const editorStream = new ArrayStream(); - const editor = repl.start({ - stream: editorStream, - terminal: true, - useColors: false - }); + const { replServer, input } = startNewREPLServer(); - editorStream.run(['.clear']); - editorStream.run(['.editor']); + input.run(['.clear', '.editor']); - editor.completer('Uin', common.mustCall((_error, data) => { + replServer.completer('Uin', common.mustCall((_error, data) => { assert.deepStrictEqual(data, [['Uint'], 'Uin']); })); - editorStream.run(['.clear']); - editorStream.run(['.editor']); + input.run(['.clear', '.editor']); - editor.completer('var log = console.l', common.mustCall((_error, data) => { + replServer.completer('var log = console.l', common.mustCall((_error, data) => { assert.deepStrictEqual(data, [['console.log'], 'console.l']); })); } // Regression test for https://github.com/nodejs/node/issues/43528 { - const stream = new ArrayStream(); - const replServer = repl.start({ - input: stream, - output: stream, - terminal: true, - }); + const { replServer } = startNewREPLServer(); // Editor mode replServer.write('.editor\n'); diff --git a/test/parallel/test-repl-tab-complete-require.js b/test/parallel/test-repl-tab-complete-require.js index 9e9521af4d7de0..12be39c7f6aca3 100644 --- a/test/parallel/test-repl-tab-complete-require.js +++ b/test/parallel/test-repl-tab-complete-require.js @@ -1,7 +1,6 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); const assert = require('assert'); const fixtures = require('../common/fixtures'); const { builtinModules } = require('module'); @@ -19,24 +18,11 @@ if (!isMainThread) { process.chdir(fixtures.fixturesDir); const repl = require('repl'); - -function prepareREPL() { - const replServer = repl.start({ - prompt: '', - input: new ArrayStream(), - output: process.stdout, - allowBlockingCompletions: true, - }); - - // Some errors are passed to the domain, but do not callback - replServer._domain.on('error', assert.ifError); - - return replServer; -} +const { startNewREPLServer } = require('../common/repl'); // Tab completion on require on builtin modules works { - const replServer = prepareREPL(); + const { replServer } = startNewREPLServer(); replServer.complete( "require('", @@ -65,7 +51,7 @@ function prepareREPL() { // Tab completion on require on builtin modules works (with extra spaces and "n" prefix) { - const replServer = prepareREPL(); + const { replServer } = startNewREPLServer(); replServer.complete( "require\t( 'n", @@ -98,7 +84,7 @@ function prepareREPL() { { const expected = ['@nodejsscope', '@nodejsscope/']; - const replServer = prepareREPL(); + const { replServer } = startNewREPLServer(); // Require calls should handle all types of quotation marks. for (const quotationMark of ["'", '"', '`']) { @@ -124,7 +110,7 @@ function prepareREPL() { { // Completions should find modules and handle whitespace after the opening bracket. - const replServer = prepareREPL(); + const { replServer } = startNewREPLServer(); replServer.complete( 'require \t("no_ind', @@ -137,7 +123,7 @@ function prepareREPL() { // Test tab completion for require() relative to the current directory { - const replServer = prepareREPL(); + const { replServer } = startNewREPLServer(); const cwd = process.cwd(); process.chdir(__dirname); diff --git a/test/parallel/test-repl-tab-complete-unary-expressions.js b/test/parallel/test-repl-tab-complete-unary-expressions.js index d84f0672b98151..2b09ae651d2508 100644 --- a/test/parallel/test-repl-tab-complete-unary-expressions.js +++ b/test/parallel/test-repl-tab-complete-unary-expressions.js @@ -2,7 +2,7 @@ const common = require('../common'); const assert = require('assert'); -const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); const { describe, it } = require('node:test'); // This test verifies that tab completion works correctly with unary expressions @@ -12,25 +12,20 @@ const { describe, it } = require('node:test'); describe('REPL tab completion with unary expressions', () => { it('should handle delete operator correctly', (t, done) => { - const r = repl.start({ - prompt: '', - input: process.stdin, - output: process.stdout, - terminal: false, - }); + const { replServer } = startNewREPLServer({ terminal: false }); // Test delete with member expression - r.complete( + replServer.complete( 'delete globalThis._', common.mustSucceed((completions) => { assert.strictEqual(completions[1], 'globalThis._'); // Test delete with identifier - r.complete( + replServer.complete( 'delete globalThis', common.mustSucceed((completions) => { assert.strictEqual(completions[1], 'globalThis'); - r.close(); + replServer.close(); done(); }) ); @@ -39,48 +34,33 @@ describe('REPL tab completion with unary expressions', () => { }); it('should handle typeof operator correctly', (t, done) => { - const r = repl.start({ - prompt: '', - input: process.stdin, - output: process.stdout, - terminal: false, - }); - - r.complete( + const { replServer } = startNewREPLServer({ terminal: false }); + + replServer.complete( 'typeof globalThis', common.mustSucceed((completions) => { assert.strictEqual(completions[1], 'globalThis'); - r.close(); + replServer.close(); done(); }) ); }); it('should handle void operator correctly', (t, done) => { - const r = repl.start({ - prompt: '', - input: process.stdin, - output: process.stdout, - terminal: false, - }); - - r.complete( + const { replServer } = startNewREPLServer({ terminal: false }); + + replServer.complete( 'void globalThis', common.mustSucceed((completions) => { assert.strictEqual(completions[1], 'globalThis'); - r.close(); + replServer.close(); done(); }) ); }); it('should handle other unary operators correctly', (t, done) => { - const r = repl.start({ - prompt: '', - input: process.stdin, - output: process.stdout, - terminal: false, - }); + const { replServer } = startNewREPLServer({ terminal: false }); const unaryOperators = [ '!globalThis', @@ -93,13 +73,13 @@ describe('REPL tab completion with unary expressions', () => { function testNext() { if (testIndex >= unaryOperators.length) { - r.close(); + replServer.close(); done(); return; } const testCase = unaryOperators[testIndex++]; - r.complete( + replServer.complete( testCase, common.mustSucceed((completions) => { assert.strictEqual(completions[1], 'globalThis'); @@ -112,26 +92,21 @@ describe('REPL tab completion with unary expressions', () => { }); it('should still evaluate globalThis correctly after unary expression completion', (t, done) => { - const r = repl.start({ - prompt: '', - input: process.stdin, - output: process.stdout, - terminal: false, - }); + const { replServer } = startNewREPLServer({ terminal: false }); // First trigger completion with delete - r.complete( + replServer.complete( 'delete globalThis._', common.mustSucceed(() => { // Then evaluate globalThis - r.eval( + replServer.eval( 'globalThis', - r.context, + replServer.context, 'test.js', common.mustSucceed((result) => { assert.strictEqual(typeof result, 'object'); assert.ok(result !== null); - r.close(); + replServer.close(); done(); }) ); diff --git a/test/parallel/test-repl-tab-complete.js b/test/parallel/test-repl-tab-complete.js index f37916e30d8411..d4df6c31787968 100644 --- a/test/parallel/test-repl-tab-complete.js +++ b/test/parallel/test-repl-tab-complete.js @@ -22,42 +22,25 @@ 'use strict'; const common = require('../common'); -const ArrayStream = require('../common/arraystream'); +const { startNewREPLServer } = require('../common/repl'); const { describe, it } = require('node:test'); const assert = require('assert'); -const repl = require('repl'); - function getNoResultsFunction() { return common.mustSucceed((data) => { assert.deepStrictEqual(data[0], []); }); } -function prepareREPL() { - const input = new ArrayStream(); - const replServer = repl.start({ - prompt: '', - input, - output: process.stdout, - allowBlockingCompletions: true, - }); - - // Some errors are passed to the domain, but do not callback - replServer._domain.on('error', assert.ifError); - - return { replServer, input }; -} - describe('REPL tab completion (core functionality)', () => { it('does not break with variable declarations without an initialization', () => { - const { replServer } = prepareREPL(); + const { replServer } = startNewREPLServer(); replServer.complete('let a', getNoResultsFunction()); replServer.close(); }); it('does not break in an object literal', () => { - const { replServer, input } = prepareREPL(); + const { replServer, input } = startNewREPLServer(); input.run(['var inner = {', 'one:1']); @@ -74,7 +57,7 @@ describe('REPL tab completion (core functionality)', () => { }); it('works with optional chaining', () => { - const { replServer } = prepareREPL(); + const { replServer } = startNewREPLServer(); replServer.complete( 'console?.lo', @@ -102,7 +85,7 @@ describe('REPL tab completion (core functionality)', () => { }); it('returns object completions', () => { - const { replServer, input } = prepareREPL(); + const { replServer, input } = startNewREPLServer(); input.run(['var inner = {', 'one:1']); @@ -119,7 +102,7 @@ describe('REPL tab completion (core functionality)', () => { }); it('does not break in a ternary operator with ()', () => { - const { replServer, input } = prepareREPL(); + const { replServer, input } = startNewREPLServer(); input.run(['var inner = ( true ', '?', '{one: 1} : ']); @@ -129,7 +112,7 @@ describe('REPL tab completion (core functionality)', () => { }); it('works on literals', () => { - const { replServer } = prepareREPL(); + const { replServer } = startNewREPLServer(); replServer.complete( '``.a', @@ -172,7 +155,7 @@ describe('REPL tab completion (core functionality)', () => { }); it("does not return a function's local variable", () => { - const { replServer, input } = prepareREPL(); + const { replServer, input } = startNewREPLServer(); input.run(['var top = function() {', 'var inner = {one:1};', '}']); @@ -182,7 +165,7 @@ describe('REPL tab completion (core functionality)', () => { }); it("does not return a function's local variable even when the function has parameters", () => { - const { replServer, input } = prepareREPL(); + const { replServer, input } = startNewREPLServer(); input.run([ 'var top = function(one, two) {', @@ -198,7 +181,7 @@ describe('REPL tab completion (core functionality)', () => { it("does not return a function's local variable" + 'even if the scope is nested inside an immediately executed function', () => { - const { replServer, input } = prepareREPL(); + const { replServer, input } = startNewREPLServer(); input.run([ 'var top = function() {', @@ -216,7 +199,7 @@ describe('REPL tab completion (core functionality)', () => { it("does not return a function's local variable" + 'even if the scope is nested inside an immediately executed function' + '(the definition has the params and { on a separate line)', () => { - const { replServer, input } = prepareREPL(); + const { replServer, input } = startNewREPLServer(); input.run([ 'var top = function() {', @@ -233,7 +216,7 @@ describe('REPL tab completion (core functionality)', () => { }); it('currently does not work, but should not break (local inner)', () => { - const { replServer, input } = prepareREPL(); + const { replServer, input } = startNewREPLServer(); input.run([ 'var top = function() {', @@ -250,7 +233,7 @@ describe('REPL tab completion (core functionality)', () => { }); it('currently does not work, but should not break (local inner parens next line)', () => { - const { replServer, input } = prepareREPL(); + const { replServer, input } = startNewREPLServer(); input.run([ 'var top = function() {', @@ -268,7 +251,7 @@ describe('REPL tab completion (core functionality)', () => { }); it('works on non-Objects', () => { - const { replServer, input } = prepareREPL(); + const { replServer, input } = startNewREPLServer(); input.run(['var str = "test";']); @@ -283,7 +266,7 @@ describe('REPL tab completion (core functionality)', () => { }); it('should be case-insensitive if member part is lower-case', () => { - const { replServer, input } = prepareREPL(); + const { replServer, input } = startNewREPLServer(); input.run(['var foo = { barBar: 1, BARbuz: 2, barBLA: 3 };']); @@ -301,7 +284,7 @@ describe('REPL tab completion (core functionality)', () => { }); it('should be case-insensitive if member part is upper-case', () => { - const { replServer, input } = prepareREPL(); + const { replServer, input } = startNewREPLServer(); input.run(['var foo = { barBar: 1, BARbuz: 2, barBLA: 3 };']); @@ -319,7 +302,7 @@ describe('REPL tab completion (core functionality)', () => { }); it('should not break on spaces', () => { - const { replServer } = prepareREPL(); + const { replServer } = startNewREPLServer(); const spaceTimeout = setTimeout(function() { throw new Error('timeout'); @@ -338,7 +321,7 @@ describe('REPL tab completion (core functionality)', () => { }); it(`should pick up the global "toString" object, and any other properties up the "global" object's prototype chain`, () => { - const { replServer } = prepareREPL(); + const { replServer } = startNewREPLServer(); replServer.complete( 'toSt', @@ -351,7 +334,7 @@ describe('REPL tab completion (core functionality)', () => { }); it('should make own properties shadow properties on the prototype', () => { - const { replServer, input } = prepareREPL(); + const { replServer, input } = startNewREPLServer(); input.run([ 'var x = Object.create(null);', @@ -373,7 +356,7 @@ describe('REPL tab completion (core functionality)', () => { }); it('works on context properties', () => { - const { replServer, input } = prepareREPL(); + const { replServer, input } = startNewREPLServer(); input.run(['var custom = "test";']); @@ -388,7 +371,7 @@ describe('REPL tab completion (core functionality)', () => { }); it("doesn't crash REPL with half-baked proxy objects", () => { - const { replServer, input } = prepareREPL(); + const { replServer, input } = startNewREPLServer(); input.run([ 'var proxy = new Proxy({}, {ownKeys: () => { throw new Error(); }});', @@ -406,7 +389,7 @@ describe('REPL tab completion (core functionality)', () => { }); it('does not include integer members of an Array', () => { - const { replServer, input } = prepareREPL(); + const { replServer, input } = startNewREPLServer(); input.run(['var ary = [1,2,3];']); @@ -423,7 +406,7 @@ describe('REPL tab completion (core functionality)', () => { }); it('does not include integer keys in an object', () => { - const { replServer, input } = prepareREPL(); + const { replServer, input } = startNewREPLServer(); input.run(['var obj = {1:"a","1a":"b",a:"b"};']); @@ -440,7 +423,7 @@ describe('REPL tab completion (core functionality)', () => { }); it('does not try to complete results of non-simple expressions', () => { - const { replServer, input } = prepareREPL(); + const { replServer, input } = startNewREPLServer(); input.run(['function a() {}']); @@ -450,7 +433,7 @@ describe('REPL tab completion (core functionality)', () => { }); it('works when prefixed with spaces', () => { - const { replServer, input } = prepareREPL(); + const { replServer, input } = startNewREPLServer(); input.run(['var obj = {1:"a","1a":"b",a:"b"};']); @@ -467,7 +450,7 @@ describe('REPL tab completion (core functionality)', () => { }); it('works inside assignments', () => { - const { replServer } = prepareREPL(); + const { replServer } = startNewREPLServer(); replServer.complete( 'var log = console.lo', @@ -480,7 +463,7 @@ describe('REPL tab completion (core functionality)', () => { }); it('works for defined commands', () => { - const { replServer, input } = prepareREPL(); + const { replServer, input } = startNewREPLServer(); replServer.complete( '.b', @@ -503,7 +486,7 @@ describe('REPL tab completion (core functionality)', () => { }); it('does not include __defineSetter__ and friends', () => { - const { replServer, input } = prepareREPL(); + const { replServer, input } = startNewREPLServer(); input.run(['var obj = {};']); @@ -522,7 +505,7 @@ describe('REPL tab completion (core functionality)', () => { }); it('works with builtin values', () => { - const { replServer } = prepareREPL(); + const { replServer } = startNewREPLServer(); replServer.complete( 'I', @@ -555,7 +538,7 @@ describe('REPL tab completion (core functionality)', () => { }); it('works with lexically scoped variables', () => { - const { replServer, input } = prepareREPL(); + const { replServer, input } = startNewREPLServer(); input.run([ 'let lexicalLet = true;', diff --git a/test/parallel/test-repl-uncaught-exception-async.js b/test/parallel/test-repl-uncaught-exception-async.js index 24710e062e0b75..8e0d9b4075ada4 100644 --- a/test/parallel/test-repl-uncaught-exception-async.js +++ b/test/parallel/test-repl-uncaught-exception-async.js @@ -5,40 +5,37 @@ // should throw. require('../common'); -const ArrayStream = require('../common/arraystream'); -const repl = require('repl'); const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); -let accum = ''; - -const output = new ArrayStream(); -output.write = (data) => accum += data.replace('\r', ''); - -const r = repl.start({ - prompt: '', - input: new ArrayStream(), - output, - terminal: false, - useColors: false, - global: false -}); +const { replServer, output } = startNewREPLServer( + { + prompt: '', + terminal: false, + useColors: false, + global: false, + }, + { + disableDomainErrorAssert: true + }, +); -r.write( +replServer.write( 'process.nextTick(() => {\n' + ' process.on("uncaughtException", () => console.log("Foo"));\n' + ' throw new TypeError("foobar");\n' + '});\n' ); -r.write( +replServer.write( 'setTimeout(() => {\n' + ' throw new RangeError("abc");\n' + '}, 1);console.log()\n' ); setTimeout(() => { - r.close(); + replServer.close(); const len = process.listenerCount('uncaughtException'); process.removeAllListeners('uncaughtException'); assert.strictEqual(len, 0); - assert.match(accum, /ERR_INVALID_REPL_INPUT.*(?!Type)RangeError: abc/s); + assert.match(output.accumulator, /ERR_INVALID_REPL_INPUT.*(?!Type)RangeError: abc/s); }, 2); diff --git a/test/parallel/test-repl-uncaught-exception-evalcallback.js b/test/parallel/test-repl-uncaught-exception-evalcallback.js index a6f6e341049d6c..77d03320ee9375 100644 --- a/test/parallel/test-repl-uncaught-exception-evalcallback.js +++ b/test/parallel/test-repl-uncaught-exception-evalcallback.js @@ -1,23 +1,27 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); -const repl = require('repl'); -const { PassThrough } = require('stream'); -const input = new PassThrough(); -const output = new PassThrough(); +const { startNewREPLServer } = require('../common/repl'); -const r = repl.start({ - input, output, - eval: common.mustCall((code, context, filename, cb) => { - r.setPrompt('prompt! '); - cb(new Error('err')); - }) -}); +const { replServer, output } = startNewREPLServer( + { + prompt: '', + terminal: false, + useColors: false, + global: false, + eval: common.mustCall((code, context, filename, cb) => { + replServer.setPrompt('prompt! '); + cb(new Error('err')); + }) + }, + { + disableDomainErrorAssert: true + }, +); -input.end('foo\n'); +replServer.write('foo\n'); // The output includes exactly one post-error prompt. -const out = output.read().toString(); -assert.match(out, /prompt!/); -assert.doesNotMatch(out, /prompt![\S\s]*prompt!/); +assert.match(output.accumulator, /prompt!/); +assert.doesNotMatch(output.accumulator, /prompt![\S\s]*prompt!/); output.on('data', common.mustNotCall()); diff --git a/test/parallel/test-repl-uncaught-exception.js b/test/parallel/test-repl-uncaught-exception.js index d3dbe0acb0106d..7753fe180b07fd 100644 --- a/test/parallel/test-repl-uncaught-exception.js +++ b/test/parallel/test-repl-uncaught-exception.js @@ -1,38 +1,36 @@ 'use strict'; require('../common'); -const ArrayStream = require('../common/arraystream'); const assert = require('assert'); -const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); let count = 0; function run({ command, expected, useColors = false }) { - let accum = ''; + const { replServer, output } = startNewREPLServer( + { + prompt: '', + terminal: false, + useColors, + }, + { + disableDomainErrorAssert: true + }, + ); - const output = new ArrayStream(); - output.write = (data) => accum += data.replace('\r', ''); + replServer.write(`${command}\n`); - const r = repl.start({ - prompt: '', - input: new ArrayStream(), - output, - terminal: false, - useColors - }); - - r.write(`${command}\n`); if (typeof expected === 'string') { - assert.strictEqual(accum, expected); + assert.strictEqual(output.accumulator, expected); } else { - assert.match(accum, expected); + assert.match(output.accumulator, expected); } // Verify that the repl is still working as expected. - accum = ''; - r.write('1 + 1\n'); + output.accumulator = ''; + replServer.write('1 + 1\n'); // eslint-disable-next-line no-control-regex - assert.strictEqual(accum.replace(/\u001b\[[0-9]+m/g, ''), '2\n'); - r.close(); + assert.strictEqual(output.accumulator.replace(/\u001b\[[0-9]+m/g, ''), '2\n'); + replServer.close(); count++; } diff --git a/test/parallel/test-repl-underscore.js b/test/parallel/test-repl-underscore.js index 8ce9de5563acfb..38008fcc04c5d1 100644 --- a/test/parallel/test-repl-underscore.js +++ b/test/parallel/test-repl-underscore.js @@ -3,20 +3,25 @@ require('../common'); const assert = require('assert'); const repl = require('repl'); -const stream = require('stream'); +const { startNewREPLServer } = require('../common/repl'); + +const testingReplPrompt = '_REPL_TESTING_PROMPT_>'; testSloppyMode(); testStrictMode(); +testMagicMode(); testResetContext(); testResetContextGlobal(); -testMagicMode(); testError(); function testSloppyMode() { - const r = initRepl(repl.REPL_MODE_SLOPPY); + const { replServer, output } = startNewREPLServer({ + prompt: testingReplPrompt, + mode: repl.REPL_MODE_SLOPPY, + }); // Cannot use `let` in sloppy mode - r.write(`_; // initial value undefined + replServer.write(`_; // initial value undefined var x = 10; // evaluates to undefined _; // still undefined y = 10; // evaluates to 10 @@ -29,7 +34,7 @@ function testSloppyMode() { _; // remains 30 from user input `); - assertOutput(r.output, [ + assertOutput(output, [ 'undefined', 'undefined', 'undefined', @@ -46,9 +51,12 @@ function testSloppyMode() { } function testStrictMode() { - const r = initRepl(repl.REPL_MODE_STRICT); + const { replServer, output } = startNewREPLServer({ + prompt: testingReplPrompt, + mode: repl.REPL_MODE_STRICT, + }); - r.write(`_; // initial value undefined + replServer.write(`_; // initial value undefined var x = 10; // evaluates to undefined _; // still undefined let _ = 20; // use 'let' only in strict mode - evals to undefined @@ -62,7 +70,7 @@ function testStrictMode() { _; // remains 30 from user input `); - assertOutput(r.output, [ + assertOutput(output, [ 'undefined', 'undefined', 'undefined', @@ -79,9 +87,12 @@ function testStrictMode() { } function testMagicMode() { - const r = initRepl(repl.REPL_MODE_MAGIC); + const { replServer, output } = startNewREPLServer({ + prompt: testingReplPrompt, + mode: repl.REPL_MODE_MAGIC, + }); - r.write(`_; // initial value undefined + replServer.write(`_; // initial value undefined x = 10; // _; // last eval - 10 let _ = 20; // undefined @@ -95,7 +106,7 @@ function testMagicMode() { _; // remains 30 from user input `); - assertOutput(r.output, [ + assertOutput(output, [ 'undefined', '10', '10', @@ -112,9 +123,12 @@ function testMagicMode() { } function testResetContext() { - const r = initRepl(repl.REPL_MODE_SLOPPY); + const { replServer, output } = startNewREPLServer({ + prompt: testingReplPrompt, + mode: repl.REPL_MODE_MAGIC, + }); - r.write(`_ = 10; // explicitly set to 10 + replServer.write(`_ = 10; // explicitly set to 10 _; // 10 from user input .clear // Clearing context... _; // remains 10 @@ -122,7 +136,7 @@ function testResetContext() { _; // expect 20 `); - assertOutput(r.output, [ + assertOutput(output, [ 'Expression assignment to _ now disabled.', '10', '10', @@ -134,15 +148,18 @@ function testResetContext() { } function testResetContextGlobal() { - const r = initRepl(repl.REPL_MODE_STRICT, true); + const { replServer, output } = startNewREPLServer({ + prompt: testingReplPrompt, + useGlobal: true, + }); - r.write(`_ = 10; // explicitly set to 10 + replServer.write(`_ = 10; // explicitly set to 10 _; // 10 from user input .clear // No output because useGlobal is true _; // remains 10 `); - assertOutput(r.output, [ + assertOutput(output, [ 'Expression assignment to _ now disabled.', '10', '10', @@ -155,9 +172,15 @@ function testResetContextGlobal() { } function testError() { - const r = initRepl(repl.REPL_MODE_STRICT); + const { replServer, output } = startNewREPLServer({ + prompt: testingReplPrompt, + replMode: repl.REPL_MODE_STRICT, + preview: false, + }, { + disableDomainErrorAssert: true + }); - r.write(`_error; // initial value undefined + replServer.write(`_error; // initial value undefined throw new Error('foo'); // throws error _error; // shows error fs.readdirSync('/nonexistent?'); // throws error, sync @@ -168,7 +191,9 @@ function testError() { `); setImmediate(() => { - const lines = r.output.accum.trim().split('\n'); + const lines = output.accumulator.trim().split('\n').filter( + (line) => !line.includes(testingReplPrompt) || line.includes('Uncaught Error') + ); const expectedLines = [ 'undefined', @@ -192,7 +217,7 @@ function testError() { 'undefined', // The message from the original throw - 'Uncaught Error: baz', + /Uncaught Error: baz/, ]; for (const line of lines) { const expected = expectedLines.shift(); @@ -204,14 +229,14 @@ function testError() { assert.strictEqual(expectedLines.length, 0); // Reset output, check that '_error' is the asynchronously caught error. - r.output.accum = ''; - r.write(`_error.message // show the message + output.accumulator = ''; + replServer.write(`_error.message // show the message _error = 0; // disable auto-assignment throw new Error('quux'); // new error _error; // should not see the new error `); - assertOutput(r.output, [ + assertOutput(output, [ "'baz'", 'Expression assignment to _error now disabled.', '0', @@ -221,27 +246,7 @@ function testError() { }); } -function initRepl(mode, useGlobal) { - const inputStream = new stream.PassThrough(); - const outputStream = new stream.PassThrough(); - outputStream.accum = ''; - - outputStream.on('data', (data) => { - outputStream.accum += data; - }); - - return repl.start({ - input: inputStream, - output: outputStream, - useColors: false, - terminal: false, - prompt: '', - replMode: mode, - useGlobal: useGlobal - }); -} - function assertOutput(output, expected) { - const lines = output.accum.trim().split('\n'); + const lines = output.accumulator.trim().split('\n').filter((line) => !line.includes(testingReplPrompt)); assert.deepStrictEqual(lines, expected); }