diff options
Diffstat (limited to 'test/parallel')
-rw-r--r-- | test/parallel/test-repl-history-navigation.js | 28 | ||||
-rw-r--r-- | test/parallel/test-repl-multiline.js | 62 | ||||
-rw-r--r-- | test/parallel/test-repl-preview.js | 131 | ||||
-rw-r--r-- | test/parallel/test-repl-top-level-await.js | 101 |
4 files changed, 255 insertions, 67 deletions
diff --git a/test/parallel/test-repl-history-navigation.js b/test/parallel/test-repl-history-navigation.js index b00f932aa9..3bd198880f 100644 --- a/test/parallel/test-repl-history-navigation.js +++ b/test/parallel/test-repl-history-navigation.js @@ -46,28 +46,50 @@ ActionStream.prototype.readable = true; const ENTER = { name: 'enter' }; const UP = { name: 'up' }; const DOWN = { name: 'down' }; +const LEFT = { name: 'left' }; +const DELETE = { name: 'delete' }; const prompt = '> '; +const prev = process.features.inspector; + const tests = [ { // Creates few history to navigate for env: { NODE_REPL_HISTORY: defaultHistoryPath }, test: [ 'let ab = 45', ENTER, '555 + 909', ENTER, - '{key : {key2 :[] }}', ENTER], + '{key : {key2 :[] }}', ENTER, + 'Array(100).fill(1).map((e, i) => i ** i)', LEFT, LEFT, DELETE, + '2', ENTER], expected: [], clean: false }, { env: { NODE_REPL_HISTORY: defaultHistoryPath }, - test: [UP, UP, UP, UP, DOWN, DOWN, DOWN], + test: [UP, UP, UP, UP, UP, DOWN, DOWN, DOWN, DOWN], expected: [prompt, + `${prompt}Array(100).fill(1).map((e, i) => i ** 2)`, + prev && '\n// [ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, ' + + '144, 169, 196, 225, 256, 289, 324, 361, 400, 441, 484, 529,' + + ' 576, 625, 676, 729, 784, 841, 900, 961, 1024, 1089, 1156, ' + + '1225, 1296, 1369, 1444, 1521, 1600, 1681, 1764, 1849, 1936,' + + ' 2025, 2116, 2209, ...', `${prompt}{key : {key2 :[] }}`, + prev && '\n// { key: { key2: [] } }', `${prompt}555 + 909`, + prev && '\n// 1464', `${prompt}let ab = 45`, `${prompt}555 + 909`, + prev && '\n// 1464', `${prompt}{key : {key2 :[] }}`, - prompt], + prev && '\n// { key: { key2: [] } }', + `${prompt}Array(100).fill(1).map((e, i) => i ** 2)`, + prev && '\n// [ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, ' + + '144, 169, 196, 225, 256, 289, 324, 361, 400, 441, 484, 529,' + + ' 576, 625, 676, 729, 784, 841, 900, 961, 1024, 1089, 1156, ' + + '1225, 1296, 1369, 1444, 1521, 1600, 1681, 1764, 1849, 1936,' + + ' 2025, 2116, 2209, ...', + prompt].filter((e) => typeof e === 'string'), clean: true } ]; diff --git a/test/parallel/test-repl-multiline.js b/test/parallel/test-repl-multiline.js index 454d5b1019..6498923b62 100644 --- a/test/parallel/test-repl-multiline.js +++ b/test/parallel/test-repl-multiline.js @@ -3,34 +3,44 @@ const common = require('../common'); const ArrayStream = require('../common/arraystream'); const assert = require('assert'); const repl = require('repl'); -const inputStream = new ArrayStream(); -const outputStream = new ArrayStream(); -const input = ['const foo = {', '};', 'foo;']; -let output = ''; +const input = ['const foo = {', '};', 'foo']; -outputStream.write = (data) => { output += data.replace('\r', ''); }; +function run({ useColors }) { + const inputStream = new ArrayStream(); + const outputStream = new ArrayStream(); + let output = ''; -const r = repl.start({ - prompt: '', - input: inputStream, - output: outputStream, - terminal: true, - useColors: false -}); + outputStream.write = (data) => { output += data.replace('\r', ''); }; -r.on('exit', common.mustCall(() => { - const actual = output.split('\n'); + const r = repl.start({ + prompt: '', + input: inputStream, + output: outputStream, + terminal: true, + useColors + }); - // Validate the output, which contains terminal escape codes. - assert.strictEqual(actual.length, 6); - assert.ok(actual[0].endsWith(input[0])); - assert.ok(actual[1].includes('... ')); - assert.ok(actual[1].endsWith(input[1])); - assert.strictEqual(actual[2], 'undefined'); - assert.ok(actual[3].endsWith(input[2])); - assert.strictEqual(actual[4], '{}'); - // Ignore the last line, which is nothing but escape codes. -})); + r.on('exit', common.mustCall(() => { + const actual = output.split('\n'); -inputStream.run(input); -r.close(); + // Validate the output, which contains terminal escape codes. + assert.strictEqual(actual.length, 6 + process.features.inspector); + assert.ok(actual[0].endsWith(input[0])); + assert.ok(actual[1].includes('... ')); + assert.ok(actual[1].endsWith(input[1])); + assert.ok(actual[2].includes('undefined')); + assert.ok(actual[3].endsWith(input[2])); + if (process.features.inspector) { + assert.ok(actual[4].includes(actual[5])); + assert.strictEqual(actual[4].includes('//'), !useColors); + } + assert.strictEqual(actual[4 + process.features.inspector], '{}'); + // Ignore the last line, which is nothing but escape codes. + })); + + inputStream.run(input); + r.close(); +} + +run({ useColors: true }); +run({ useColors: false }); diff --git a/test/parallel/test-repl-preview.js b/test/parallel/test-repl-preview.js new file mode 100644 index 0000000000..92e73dd245 --- /dev/null +++ b/test/parallel/test-repl-preview.js @@ -0,0 +1,131 @@ +'use strict'; + +const common = require('../common'); +const ArrayStream = require('../common/arraystream'); +const assert = require('assert'); +const Repl = require('repl'); + +common.skipIfInspectorDisabled(); + +const PROMPT = 'repl > '; + +class REPLStream extends ArrayStream { + constructor() { + super(); + this.lines = ['']; + } + write(chunk) { + const chunkLines = chunk.toString('utf8').split('\n'); + this.lines[this.lines.length - 1] += chunkLines[0]; + if (chunkLines.length > 1) { + this.lines.push(...chunkLines.slice(1)); + } + this.emit('line'); + return true; + } + wait() { + this.lines = ['']; + return new Promise((resolve, reject) => { + const onError = (err) => { + this.removeListener('line', onLine); + reject(err); + }; + const onLine = () => { + if (this.lines[this.lines.length - 1].includes(PROMPT)) { + this.removeListener('error', onError); + this.removeListener('line', onLine); + resolve(this.lines); + } + }; + this.once('error', onError); + this.on('line', onLine); + }); + } +} + +function runAndWait(cmds, repl) { + const promise = repl.inputStream.wait(); + for (const cmd of cmds) { + repl.inputStream.run([cmd]); + } + return promise; +} + +async function tests(options) { + const repl = Repl.start({ + prompt: PROMPT, + stream: new REPLStream(), + ignoreUndefined: true, + useColors: true, + ...options + }); + + repl.inputStream.run([ + 'function foo(x) { return x; }', + 'function koo() { console.log("abc"); }', + 'a = undefined;' + ]); + const testCases = [ + ['foo', [2, 4], '[Function: foo]', + 'foo', + '\x1B[90m[Function: foo]\x1B[39m\x1B[1A\x1B[11G\x1B[1B\x1B[2K\x1B[1A\r', + '\x1B[36m[Function: foo]\x1B[39m', + '\x1B[1G\x1B[0Jrepl > \x1B[8G'], + ['koo', [2, 4], '[Function: koo]', + 'koo', + '\x1B[90m[Function: koo]\x1B[39m\x1B[1A\x1B[11G\x1B[1B\x1B[2K\x1B[1A\r', + '\x1B[36m[Function: koo]\x1B[39m', + '\x1B[1G\x1B[0Jrepl > \x1B[8G'], + ['a', [1, 2], undefined], + ['{ a: true }', [2, 3], '{ a: \x1B[33mtrue\x1B[39m }', + '{ a: true }\r', + '{ a: \x1B[33mtrue\x1B[39m }', + '\x1B[1G\x1B[0Jrepl > \x1B[8G'], + ['1n + 2n', [2, 5], '\x1B[33m3n\x1B[39m', + '1n + 2', + '\x1B[90mType[39m\x1B[1A\x1B[14G\x1B[1B\x1B[2K\x1B[1An', + '\x1B[90m3n\x1B[39m\x1B[1A\x1B[15G\x1B[1B\x1B[2K\x1B[1A\r', + '\x1B[33m3n\x1B[39m', + '\x1B[1G\x1B[0Jrepl > \x1B[8G'], + ['{ a: true };', [2, 4], '\x1B[33mtrue\x1B[39m', + '{ a: true };', + '\x1B[90mtrue\x1B[39m\x1B[1A\x1B[20G\x1B[1B\x1B[2K\x1B[1A\r', + '\x1B[33mtrue\x1B[39m', + '\x1B[1G\x1B[0Jrepl > \x1B[8G'], + [' \t { a: true};', [2, 5], '\x1B[33mtrue\x1B[39m', + ' \t { a: true}', + '\x1B[90m{ a: true }\x1B[39m\x1B[1A\x1B[21G\x1B[1B\x1B[2K\x1B[1A;', + '\x1B[90mtrue\x1B[39m\x1B[1A\x1B[22G\x1B[1B\x1B[2K\x1B[1A\r', + '\x1B[33mtrue\x1B[39m', + '\x1B[1G\x1B[0Jrepl > \x1B[8G'] + ]; + + const hasPreview = repl.terminal && + (options.preview !== undefined ? !!options.preview : true); + + for (const [input, length, expected, ...preview] of testCases) { + console.log(`Testing ${input}`); + + const toBeRun = input.split('\n'); + let lines = await runAndWait(toBeRun, repl); + + assert.strictEqual(lines.length, length[+hasPreview]); + if (expected === undefined) { + assert(!lines.some((e) => e.includes('undefined'))); + } else if (hasPreview) { + // Remove error messages. That allows the code to run in different + // engines. + // eslint-disable-next-line no-control-regex + lines = lines.map((line) => line.replace(/Error: .+?\x1B/, '')); + assert.deepStrictEqual(lines, preview); + } else { + assert.ok(lines[0].includes(expected), lines); + } + } +} + +tests({ terminal: false }); // No preview +tests({ terminal: true }); // Preview +tests({ terminal: false, preview: false }); // No preview +tests({ terminal: false, preview: true }); // No preview +tests({ terminal: true, preview: true }); // Preview diff --git a/test/parallel/test-repl-top-level-await.js b/test/parallel/test-repl-top-level-await.js index 5c8aa95d40..cecbd3ab45 100644 --- a/test/parallel/test-repl-top-level-await.js +++ b/test/parallel/test-repl-top-level-await.js @@ -1,11 +1,13 @@ 'use strict'; -require('../common'); +const common = require('../common'); const ArrayStream = require('../common/arraystream'); const assert = require('assert'); const { stripVTControlCharacters } = require('internal/readline/utils'); const repl = require('repl'); +common.skipIfInspectorDisabled(); + // Flags: --expose-internals --experimental-repl-await const PROMPT = 'await repl > '; @@ -84,61 +86,84 @@ async function ordinaryTests() { const testCases = [ [ 'await Promise.resolve(0)', '0' ], [ '{ a: await Promise.resolve(1) }', '{ a: 1 }' ], - [ '_', '{ a: 1 }' ], - [ 'let { a, b } = await Promise.resolve({ a: 1, b: 2 }), f = 5;', + [ '_', '// { a: 1 }\r', { line: 0 } ], + [ 'let { aa, bb } = await Promise.resolve({ aa: 1, bb: 2 }), f = 5;', 'undefined' ], - [ 'a', '1' ], - [ 'b', '2' ], - [ 'f', '5' ], - [ 'let c = await Promise.resolve(2)', 'undefined' ], - [ 'c', '2' ], - [ 'let d;', 'undefined' ], - [ 'd', 'undefined' ], - [ 'let [i, { abc: { k } }] = [0, { abc: { k: 1 } }];', 'undefined' ], - [ 'i', '0' ], - [ 'k', '1' ], - [ 'var l = await Promise.resolve(2);', 'undefined' ], - [ 'l', '2' ], - [ 'foo(await koo())', '4' ], - [ '_', '4' ], - [ 'const m = foo(await koo());', 'undefined' ], - [ 'm', '4' ], - [ 'const n = foo(await\nkoo());', 'undefined' ], - [ 'n', '4' ], + [ 'aa', ['// 1\r', '1'] ], + [ 'bb', ['// 2\r', '2'] ], + [ 'f', ['// 5\r', '5'] ], + [ 'let cc = await Promise.resolve(2)', 'undefined' ], + [ 'cc', ['// 2\r', '2'] ], + [ 'let dd;', 'undefined' ], + [ 'dd', 'undefined' ], + [ 'let [ii, { abc: { kk } }] = [0, { abc: { kk: 1 } }];', 'undefined' ], + [ 'ii', ['// 0\r', '0'] ], + [ 'kk', ['// 1\r', '1'] ], + [ 'var ll = await Promise.resolve(2);', 'undefined' ], + [ 'll', ['// 2\r', '2'] ], + [ 'foo(await koo())', + [ 'f', '// 5oo', '// [Function: foo](await koo())\r', '4' ] ], + [ '_', ['// 4\r', '4'] ], + [ 'const m = foo(await koo());', + [ 'const m = foo(await koo());\r', 'undefined' ] ], + [ 'm', ['// 4\r', '4' ] ], + [ 'const n = foo(await\nkoo());', + [ 'const n = foo(await\r', '... koo());\r', 'undefined' ] ], + [ 'n', ['// 4\r', '4' ] ], // eslint-disable-next-line no-template-curly-in-string [ '`status: ${(await Promise.resolve({ status: 200 })).status}`', "'status: 200'"], - [ 'for (let i = 0; i < 2; ++i) await i', 'undefined' ], - [ 'for (let i = 0; i < 2; ++i) { await i }', 'undefined' ], - [ 'await 0', '0' ], - [ 'await 0; function foo() {}', 'undefined' ], - [ 'foo', '[Function: foo]' ], - [ 'class Foo {}; await 1;', '1' ], - [ 'Foo', '[Function: Foo]' ], - [ 'if (await true) { function bar() {}; }', 'undefined' ], - [ 'bar', '[Function: bar]' ], + [ 'for (let i = 0; i < 2; ++i) await i', + ['f', '// 5or (let i = 0; i < 2; ++i) await i\r', 'undefined'] ], + [ 'for (let i = 0; i < 2; ++i) { await i }', + [ 'f', '// 5or (let i = 0; i < 2; ++i) { await i }\r', 'undefined' ] ], + [ 'await 0', ['await 0\r', '0'] ], + [ 'await 0; function foo() {}', + ['await 0; function foo() {}\r', 'undefined'] ], + [ 'foo', + ['f', '// 5oo', '// [Function: foo]\r', '[Function: foo]'] ], + [ 'class Foo {}; await 1;', ['class Foo {}; await 1;\r', '1'] ], + [ 'Foo', ['// [Function: Foo]\r', '[Function: Foo]'] ], + [ 'if (await true) { function bar() {}; }', + ['if (await true) { function bar() {}; }\r', 'undefined'] ], + [ 'bar', ['// [Function: bar]\r', '[Function: bar]'] ], [ 'if (await true) { class Bar {}; }', 'undefined' ], [ 'Bar', 'Uncaught ReferenceError: Bar is not defined' ], [ 'await 0; function* gen(){}', 'undefined' ], - [ 'for (var i = 0; i < 10; ++i) { await i; }', 'undefined' ], - [ 'i', '10' ], - [ 'for (let j = 0; j < 5; ++j) { await j; }', 'undefined' ], - [ 'j', 'Uncaught ReferenceError: j is not defined' ], - [ 'gen', '[GeneratorFunction: gen]' ], + [ 'for (var i = 0; i < 10; ++i) { await i; }', + ['f', '// 5or (var i = 0; i < 10; ++i) { await i; }\r', 'undefined'] ], + [ 'i', ['// 10\r', '10'] ], + [ 'for (let j = 0; j < 5; ++j) { await j; }', + ['f', '// 5or (let j = 0; j < 5; ++j) { await j; }\r', 'undefined'] ], + [ 'j', 'Uncaught ReferenceError: j is not defined', { line: 0 } ], + [ 'gen', ['// [GeneratorFunction: gen]\r', '[GeneratorFunction: gen]'] ], [ 'return 42; await 5;', 'Uncaught SyntaxError: Illegal return statement', { line: 3 } ], [ 'let o = await 1, p', 'undefined' ], [ 'p', 'undefined' ], [ 'let q = 1, s = await 2', 'undefined' ], - [ 's', '2' ], - [ 'for await (let i of [1,2,3]) console.log(i)', 'undefined', { line: 3 } ] + [ 's', ['// 2\r', '2'] ], + [ 'for await (let i of [1,2,3]) console.log(i)', + [ + 'f', + '// 5or await (let i of [1,2,3]) console.log(i)\r', + '1', + '2', + '3', + 'undefined' + ] + ] ]; for (const [input, expected, options = {}] of testCases) { console.log(`Testing ${input}`); const toBeRun = input.split('\n'); const lines = await runAndWait(toBeRun); - if ('line' in options) { + if (Array.isArray(expected)) { + if (lines[0] === input) + lines.shift(); + assert.deepStrictEqual(lines, [...expected, PROMPT]); + } else if ('line' in options) { assert.strictEqual(lines[toBeRun.length + options.line], expected); } else { const echoed = toBeRun.map((a, i) => `${i > 0 ? '... ' : ''}${a}\r`); |