@@ -34,6 +34,11 @@ function checkBadPath(err, response) {
3434 assert ( / W e b S o c k e t s r e q u e s t w a s e x p e c t e d / . test ( err . response ) ) ;
3535}
3636
37+ function checkException ( message ) {
38+ assert . strictEqual ( message [ 'exceptionDetails' ] , undefined ,
39+ 'An exception occurred during execution' ) ;
40+ }
41+
3742function expectMainScriptSource ( result ) {
3843 const expected = helper . mainScriptSource ( ) ;
3944 const source = result [ 'scriptSource' ] ;
@@ -209,6 +214,142 @@ function testI18NCharacters(session) {
209214 ] ) ;
210215}
211216
217+ function testCommandLineAPI ( session ) {
218+ const testModulePath = require . resolve ( '../fixtures/empty.js' ) ;
219+ const testModuleStr = JSON . stringify ( testModulePath ) ;
220+ const printAModulePath = require . resolve ( '../fixtures/printA.js' ) ;
221+ const printAModuleStr = JSON . stringify ( printAModulePath ) ;
222+ const printBModulePath = require . resolve ( '../fixtures/printB.js' ) ;
223+ const printBModuleStr = JSON . stringify ( printBModulePath ) ;
224+ session . sendInspectorCommands ( [
225+ [ // we can use `require` outside of a callframe with require in scope
226+ {
227+ 'method' : 'Runtime.evaluate' , 'params' : {
228+ 'expression' : 'typeof require("fs").readFile === "function"' ,
229+ 'includeCommandLineAPI' : true
230+ }
231+ } , ( message ) => {
232+ checkException ( message ) ;
233+ assert . strictEqual ( message [ 'result' ] [ 'value' ] , true ) ;
234+ }
235+ ] ,
236+ [ // the global require has the same properties as a normal `require`
237+ {
238+ 'method' : 'Runtime.evaluate' , 'params' : {
239+ 'expression' : [
240+ 'typeof require.resolve === "function"' ,
241+ 'typeof require.extensions === "object"' ,
242+ 'typeof require.cache === "object"'
243+ ] . join ( ' && ' ) ,
244+ 'includeCommandLineAPI' : true
245+ }
246+ } , ( message ) => {
247+ checkException ( message ) ;
248+ assert . strictEqual ( message [ 'result' ] [ 'value' ] , true ) ;
249+ }
250+ ] ,
251+ [ // `require` twice returns the same value
252+ {
253+ 'method' : 'Runtime.evaluate' , 'params' : {
254+ // 1. We require the same module twice
255+ // 2. We mutate the exports so we can compare it later on
256+ 'expression' : `
257+ Object.assign(
258+ require(${ testModuleStr } ),
259+ { old: 'yes' }
260+ ) === require(${ testModuleStr } )` ,
261+ 'includeCommandLineAPI' : true
262+ }
263+ } , ( message ) => {
264+ checkException ( message ) ;
265+ assert . strictEqual ( message [ 'result' ] [ 'value' ] , true ) ;
266+ }
267+ ] ,
268+ [ // after require the module appears in require.cache
269+ {
270+ 'method' : 'Runtime.evaluate' , 'params' : {
271+ 'expression' : `JSON.stringify(
272+ require.cache[${ testModuleStr } ].exports
273+ )` ,
274+ 'includeCommandLineAPI' : true
275+ }
276+ } , ( message ) => {
277+ checkException ( message ) ;
278+ assert . deepStrictEqual ( JSON . parse ( message [ 'result' ] [ 'value' ] ) ,
279+ { old : 'yes' } ) ;
280+ }
281+ ] ,
282+ [ // remove module from require.cache
283+ {
284+ 'method' : 'Runtime.evaluate' , 'params' : {
285+ 'expression' : `delete require.cache[${ testModuleStr } ]` ,
286+ 'includeCommandLineAPI' : true
287+ }
288+ } , ( message ) => {
289+ checkException ( message ) ;
290+ assert . strictEqual ( message [ 'result' ] [ 'value' ] , true ) ;
291+ }
292+ ] ,
293+ [ // require again, should get fresh (empty) exports
294+ {
295+ 'method' : 'Runtime.evaluate' , 'params' : {
296+ 'expression' : `JSON.stringify(require(${ testModuleStr } ))` ,
297+ 'includeCommandLineAPI' : true
298+ }
299+ } , ( message ) => {
300+ checkException ( message ) ;
301+ assert . deepStrictEqual ( JSON . parse ( message [ 'result' ] [ 'value' ] ) , { } ) ;
302+ }
303+ ] ,
304+ [ // require 2nd module, exports an empty object
305+ {
306+ 'method' : 'Runtime.evaluate' , 'params' : {
307+ 'expression' : `JSON.stringify(require(${ printAModuleStr } ))` ,
308+ 'includeCommandLineAPI' : true
309+ }
310+ } , ( message ) => {
311+ checkException ( message ) ;
312+ assert . deepStrictEqual ( JSON . parse ( message [ 'result' ] [ 'value' ] ) , { } ) ;
313+ }
314+ ] ,
315+ [ // both modules end up with the same module.parent
316+ {
317+ 'method' : 'Runtime.evaluate' , 'params' : {
318+ 'expression' : `JSON.stringify({
319+ parentsEqual:
320+ require.cache[${ testModuleStr } ].parent ===
321+ require.cache[${ printAModuleStr } ].parent,
322+ parentId: require.cache[${ testModuleStr } ].parent.id,
323+ })` ,
324+ 'includeCommandLineAPI' : true
325+ }
326+ } , ( message ) => {
327+ checkException ( message ) ;
328+ assert . deepStrictEqual ( JSON . parse ( message [ 'result' ] [ 'value' ] ) , {
329+ parentsEqual : true ,
330+ parentId : '<inspector console>'
331+ } ) ;
332+ }
333+ ] ,
334+ [ // the `require` in the module shadows the command line API's `require`
335+ {
336+ 'method' : 'Debugger.evaluateOnCallFrame' , 'params' : {
337+ 'callFrameId' : '{"ordinal":0,"injectedScriptId":1}' ,
338+ 'expression' : `(
339+ require(${ printBModuleStr } ),
340+ require.cache[${ printBModuleStr } ].parent.id
341+ )` ,
342+ 'includeCommandLineAPI' : true
343+ }
344+ } , ( message ) => {
345+ checkException ( message ) ;
346+ assert . notStrictEqual ( message [ 'result' ] [ 'value' ] ,
347+ '<inspector console>' ) ;
348+ }
349+ ] ,
350+ ] ) ;
351+ }
352+
212353function testWaitsForFrontendDisconnect ( session , harness ) {
213354 console . log ( '[test]' , 'Verify node waits for the frontend to disconnect' ) ;
214355 session . sendInspectorCommands ( { 'method' : 'Debugger.resume' } )
@@ -231,6 +372,7 @@ function runTests(harness) {
231372 testSetBreakpointAndResume ,
232373 testInspectScope ,
233374 testI18NCharacters ,
375+ testCommandLineAPI ,
234376 testWaitsForFrontendDisconnect
235377 ] ) . expectShutDown ( 55 ) ;
236378}
0 commit comments