1'use strict'; 2 3require('../common'); 4const assert = require('assert'); 5const repl = require('repl'); 6const stream = require('stream'); 7 8testSloppyMode(); 9testStrictMode(); 10testResetContext(); 11testResetContextGlobal(); 12testMagicMode(); 13testError(); 14 15function testSloppyMode() { 16 const r = initRepl(repl.REPL_MODE_SLOPPY); 17 18 // Cannot use `let` in sloppy mode 19 r.write(`_; // initial value undefined 20 var x = 10; // evaluates to undefined 21 _; // still undefined 22 y = 10; // evaluates to 10 23 _; // 10 from last eval 24 _ = 20; // explicitly set to 20 25 _; // 20 from user input 26 _ = 30; // make sure we can set it twice and no prompt 27 _; // 30 from user input 28 y = 40; // make sure eval doesn't change _ 29 _; // remains 30 from user input 30 `); 31 32 assertOutput(r.output, [ 33 'undefined', 34 'undefined', 35 'undefined', 36 '10', 37 '10', 38 'Expression assignment to _ now disabled.', 39 '20', 40 '20', 41 '30', 42 '30', 43 '40', 44 '30', 45 ]); 46} 47 48function testStrictMode() { 49 const r = initRepl(repl.REPL_MODE_STRICT); 50 51 r.write(`_; // initial value undefined 52 var x = 10; // evaluates to undefined 53 _; // still undefined 54 let _ = 20; // use 'let' only in strict mode - evals to undefined 55 _; // 20 from user input 56 _ = 30; // make sure we can set it twice and no prompt 57 _; // 30 from user input 58 var y = 40; // make sure eval doesn't change _ 59 _; // remains 30 from user input 60 function f() { let _ = 50; } // undefined 61 f(); // undefined 62 _; // remains 30 from user input 63 `); 64 65 assertOutput(r.output, [ 66 'undefined', 67 'undefined', 68 'undefined', 69 'undefined', 70 '20', 71 '30', 72 '30', 73 'undefined', 74 '30', 75 'undefined', 76 'undefined', 77 '30', 78 ]); 79} 80 81function testMagicMode() { 82 const r = initRepl(repl.REPL_MODE_MAGIC); 83 84 r.write(`_; // initial value undefined 85 x = 10; // 86 _; // last eval - 10 87 let _ = 20; // undefined 88 _; // 20 from user input 89 _ = 30; // make sure we can set it twice and no prompt 90 _; // 30 from user input 91 var y = 40; // make sure eval doesn't change _ 92 _; // remains 30 from user input 93 function f() { let _ = 50; return _; } // undefined 94 f(); // 50 95 _; // remains 30 from user input 96 `); 97 98 assertOutput(r.output, [ 99 'undefined', 100 '10', 101 '10', 102 'undefined', 103 '20', 104 '30', 105 '30', 106 'undefined', 107 '30', 108 'undefined', 109 '50', 110 '30', 111 ]); 112} 113 114function testResetContext() { 115 const r = initRepl(repl.REPL_MODE_SLOPPY); 116 117 r.write(`_ = 10; // explicitly set to 10 118 _; // 10 from user input 119 .clear // Clearing context... 120 _; // remains 10 121 x = 20; // but behavior reverts to last eval 122 _; // expect 20 123 `); 124 125 assertOutput(r.output, [ 126 'Expression assignment to _ now disabled.', 127 '10', 128 '10', 129 'Clearing context...', 130 '10', 131 '20', 132 '20', 133 ]); 134} 135 136function testResetContextGlobal() { 137 const r = initRepl(repl.REPL_MODE_STRICT, true); 138 139 r.write(`_ = 10; // explicitly set to 10 140 _; // 10 from user input 141 .clear // No output because useGlobal is true 142 _; // remains 10 143 `); 144 145 assertOutput(r.output, [ 146 'Expression assignment to _ now disabled.', 147 '10', 148 '10', 149 '10', 150 ]); 151 152 // Delete globals leaked by REPL when `useGlobal` is `true` 153 delete global.module; 154 delete global.require; 155} 156 157function testError() { 158 const r = initRepl(repl.REPL_MODE_STRICT); 159 160 r.write(`_error; // initial value undefined 161 throw new Error('foo'); // throws error 162 _error; // shows error 163 fs.readdirSync('/nonexistent?'); // throws error, sync 164 _error.code; // shows error code 165 _error.syscall; // shows error syscall 166 setImmediate(() => { throw new Error('baz'); }); undefined; 167 // throws error, async 168 `); 169 170 setImmediate(() => { 171 const lines = r.output.accum.trim().split('\n'); 172 const expectedLines = [ 173 'undefined', 174 175 // The error, both from the original throw and the `_error` echo. 176 'Uncaught Error: foo', 177 '[Error: foo]', 178 179 // The sync error, with individual property echoes 180 /^Uncaught Error: ENOENT: no such file or directory, scandir '.*nonexistent\?'/, 181 /Object\.readdirSync/, 182 /^ {2}errno: -(2|4058),$/, 183 " syscall: 'scandir',", 184 " code: 'ENOENT',", 185 " path: '/nonexistent?'", 186 '}', 187 "'ENOENT'", 188 "'scandir'", 189 190 // Dummy 'undefined' from the explicit silencer + one from the comment 191 'undefined', 192 'undefined', 193 194 // The message from the original throw 195 'Uncaught Error: baz', 196 ]; 197 for (const line of lines) { 198 const expected = expectedLines.shift(); 199 if (typeof expected === 'string') 200 assert.strictEqual(line, expected); 201 else 202 assert.match(line, expected); 203 } 204 assert.strictEqual(expectedLines.length, 0); 205 206 // Reset output, check that '_error' is the asynchronously caught error. 207 r.output.accum = ''; 208 r.write(`_error.message // show the message 209 _error = 0; // disable auto-assignment 210 throw new Error('quux'); // new error 211 _error; // should not see the new error 212 `); 213 214 assertOutput(r.output, [ 215 "'baz'", 216 'Expression assignment to _error now disabled.', 217 '0', 218 'Uncaught Error: quux', 219 '0', 220 ]); 221 }); 222} 223 224function initRepl(mode, useGlobal) { 225 const inputStream = new stream.PassThrough(); 226 const outputStream = new stream.PassThrough(); 227 outputStream.accum = ''; 228 229 outputStream.on('data', (data) => { 230 outputStream.accum += data; 231 }); 232 233 return repl.start({ 234 input: inputStream, 235 output: outputStream, 236 useColors: false, 237 terminal: false, 238 prompt: '', 239 replMode: mode, 240 useGlobal: useGlobal 241 }); 242} 243 244function assertOutput(output, expected) { 245 const lines = output.accum.trim().split('\n'); 246 assert.deepStrictEqual(lines, expected); 247} 248