diff --git a/lib/internal/util/inspector.js b/lib/internal/util/inspector.js index 0d9580c83224e4..6ff042af71f124 100644 --- a/lib/internal/util/inspector.js +++ b/lib/internal/util/inspector.js @@ -12,6 +12,7 @@ const { } = primordials; const { validatePort } = require('internal/validators'); +const permission = require('internal/process/permission'); const kMinPort = 1024; const kMaxPort = 65535; @@ -47,6 +48,10 @@ let session; function sendInspectorCommand(cb, onError) { const { hasInspector } = internalBinding('config'); if (!hasInspector) return onError(); + // Do not preview when the permission model is enabled + // because this feature require access to the inspector, + // which is unavailable in this case. + if (permission.isEnabled()) return onError(); const inspector = require('inspector'); if (session === undefined) session = new inspector.Session(); session.connect(); diff --git a/test/parallel/test-repl-permission-model.js b/test/parallel/test-repl-permission-model.js new file mode 100644 index 00000000000000..66f2a147652f8d --- /dev/null +++ b/test/parallel/test-repl-permission-model.js @@ -0,0 +1,135 @@ +'use strict'; + +// Flags: --expose-internals --experimental-permission --allow-fs-read=* + +const common = require('../common'); +const stream = require('stream'); +const REPL = require('internal/repl'); +const assert = require('assert'); +const { inspect } = require('util'); + +common.skipIfDumbTerminal(); + +// Create an input stream specialized for testing an array of actions +class ActionStream extends stream.Stream { + run(data) { + const _iter = data[Symbol.iterator](); + const doAction = () => { + const next = _iter.next(); + if (next.done) { + // Close the repl. Note that it must have a clean prompt to do so. + this.emit('keypress', '', { ctrl: true, name: 'd' }); + return; + } + const action = next.value; + + if (typeof action === 'object') { + this.emit('keypress', '', action); + } else { + this.emit('data', `${action}`); + } + setImmediate(doAction); + }; + doAction(); + } + resume() {} + pause() {} +} +ActionStream.prototype.readable = true; + +// Mock keys +const ENTER = { name: 'enter' }; +const TABULATION = { name: 'tab' }; + +const prompt = '> '; + +const tests = [ + { + test: (function*() { + yield 'f'; + yield TABULATION; + yield ENTER; + })(), + expected: [], + env: {} + }, +]; + +const numtests = tests.length; + +const runTestWrap = common.mustCall(runTest, numtests); + +function runTest() { + const opts = tests.shift(); + if (!opts) return; // All done + + const { expected, skip } = opts; + + // Test unsupported on platform. + if (skip) { + setImmediate(runTestWrap, true); + return; + } + const lastChunks = []; + let i = 0; + + REPL.createInternalRepl(opts.env, { + input: new ActionStream(), + output: new stream.Writable({ + write(chunk, _, next) { + const output = chunk.toString(); + + if (!opts.showEscapeCodes && + (output[0] === '\x1B' || /^[\r\n]+$/.test(output))) { + return next(); + } + + lastChunks.push(output); + + if (expected.length && !opts.checkTotal) { + try { + assert.strictEqual(output, expected[i]); + } catch (e) { + console.error(`Failed test # ${numtests - tests.length}`); + console.error('Last outputs: ' + inspect(lastChunks, { + breakLength: 5, colors: true + })); + throw e; + } + // TODO(BridgeAR): Auto close on last chunk! + i++; + } + + next(); + } + }), + allowBlockingCompletions: true, + completer: opts.completer, + prompt, + useColors: false, + preview: opts.preview, + terminal: true + }, function(err, repl) { + if (err) { + console.error(`Failed test # ${numtests - tests.length}`); + throw err; + } + + repl.once('close', () => { + + if (opts.checkTotal) { + assert.deepStrictEqual(lastChunks, expected); + } else if (expected.length !== i) { + console.error(tests[numtests - tests.length - 1]); + throw new Error(`Failed test # ${numtests - tests.length}`); + } + + setImmediate(runTestWrap, true); + }); + + repl.input.run(opts.test); + }); +} + +// run the tests +runTest();