@@ -34,6 +34,11 @@ function checkBadPath(err, response) {
34
34
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 ) ) ;
35
35
}
36
36
37
+ function checkException ( message ) {
38
+ assert . strictEqual ( message [ 'exceptionDetails' ] , undefined ,
39
+ 'An exception occurred during execution' ) ;
40
+ }
41
+
37
42
function expectMainScriptSource ( result ) {
38
43
const expected = helper . mainScriptSource ( ) ;
39
44
const source = result [ 'scriptSource' ] ;
@@ -209,6 +214,142 @@ function testI18NCharacters(session) {
209
214
] ) ;
210
215
}
211
216
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
+
212
353
function testWaitsForFrontendDisconnect ( session , harness ) {
213
354
console . log ( '[test]' , 'Verify node waits for the frontend to disconnect' ) ;
214
355
session . sendInspectorCommands ( { 'method' : 'Debugger.resume' } )
@@ -231,6 +372,7 @@ function runTests(harness) {
231
372
testSetBreakpointAndResume ,
232
373
testInspectScope ,
233
374
testI18NCharacters ,
375
+ testCommandLineAPI ,
234
376
testWaitsForFrontendDisconnect
235
377
] ) . expectShutDown ( 55 ) ;
236
378
}
0 commit comments