node/test/sequential/test-inspector.js
Rich Trott bb898ca39d test: improve assertion in test-inspector.js
Remove an unecessary string literal from assert.strictEqual() call in
test-inspector.js. The string literal is printed instead of the value
that causes an error. Removing the string literal allows the value that
caused the error to be printed. This improves the troubleshooting
experience when the test fails due to that assertion.

PR-URL: https://github.com/nodejs/node/pull/22849
Reviewed-By: Teddy Katz <teddy.katz@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
2018-09-16 20:37:28 -07:00

295 lines
9.5 KiB
JavaScript

// Flags: --expose-internals
'use strict';
const common = require('../common');
common.skipIfInspectorDisabled();
const assert = require('assert');
const { NodeInstance } = require('../common/inspector-helper.js');
function checkListResponse(response) {
assert.strictEqual(1, response.length);
assert.ok(response[0].devtoolsFrontendUrl);
assert.ok(
/ws:\/\/localhost:\d+\/[0-9A-Fa-f]{8}-/
.test(response[0].webSocketDebuggerUrl),
response[0].webSocketDebuggerUrl);
}
function checkVersion(response) {
assert.ok(response);
const expected = {
'Browser': `node.js/${process.version}`,
'Protocol-Version': '1.1',
};
assert.strictEqual(JSON.stringify(response),
JSON.stringify(expected));
}
function checkBadPath(err) {
assert(err instanceof SyntaxError);
assert(/Unexpected token/.test(err.message), err.message);
assert(/WebSockets request was expected/.test(err.body), err.body);
}
function checkException(message) {
assert.strictEqual(message.exceptionDetails, undefined);
}
function assertScopeValues({ result }, expected) {
const unmatched = new Set(Object.keys(expected));
for (const actual of result) {
const value = expected[actual.name];
if (value) {
assert.strictEqual(value, actual.value.value);
unmatched.delete(actual.name);
}
}
if (unmatched.size)
assert.fail(Array.from(unmatched.values()));
}
async function testBreakpointOnStart(session) {
console.log('[test]',
'Verifying debugger stops on start (--inspect-brk option)');
const commands = [
{ 'method': 'Runtime.enable' },
{ 'method': 'Debugger.enable' },
{ 'method': 'Debugger.setPauseOnExceptions',
'params': { 'state': 'none' } },
{ 'method': 'Debugger.setAsyncCallStackDepth',
'params': { 'maxDepth': 0 } },
{ 'method': 'Profiler.enable' },
{ 'method': 'Profiler.setSamplingInterval',
'params': { 'interval': 100 } },
{ 'method': 'Debugger.setBlackboxPatterns',
'params': { 'patterns': [] } },
{ 'method': 'Runtime.runIfWaitingForDebugger' }
];
await session.send(commands);
await session.waitForBreakOnLine(0, session.scriptURL());
}
async function testBreakpoint(session) {
console.log('[test]', 'Setting a breakpoint and verifying it is hit');
const commands = [
{ 'method': 'Debugger.setBreakpointByUrl',
'params': { 'lineNumber': 5,
'url': session.scriptURL(),
'columnNumber': 0,
'condition': ''
}
},
{ 'method': 'Debugger.resume' },
];
await session.send(commands);
const { scriptSource } = await session.send({
'method': 'Debugger.getScriptSource',
'params': { 'scriptId': session.mainScriptId } });
assert(scriptSource && (scriptSource.includes(session.script())),
`Script source is wrong: ${scriptSource}`);
await session.waitForConsoleOutput('log', ['A message', 5]);
const paused = await session.waitForBreakOnLine(5, session.scriptURL());
const scopeId = paused.params.callFrames[0].scopeChain[0].object.objectId;
console.log('[test]', 'Verify we can read current application state');
const response = await session.send({
'method': 'Runtime.getProperties',
'params': {
'objectId': scopeId,
'ownProperties': false,
'accessorPropertiesOnly': false,
'generatePreview': true
}
});
assertScopeValues(response, { t: 1001, k: 1 });
let { result } = await session.send({
'method': 'Debugger.evaluateOnCallFrame', 'params': {
'callFrameId': '{"ordinal":0,"injectedScriptId":1}',
'expression': 'k + t',
'objectGroup': 'console',
'includeCommandLineAPI': true,
'silent': false,
'returnByValue': false,
'generatePreview': true
}
});
assert.strictEqual(1002, result.value);
result = (await session.send({
'method': 'Runtime.evaluate', 'params': {
'expression': '5 * 5'
}
})).result;
assert.strictEqual(25, result.value);
}
async function testI18NCharacters(session) {
console.log('[test]', 'Verify sending and receiving UTF8 characters');
const chars = 'טֶ字и';
session.send({
'method': 'Debugger.evaluateOnCallFrame', 'params': {
'callFrameId': '{"ordinal":0,"injectedScriptId":1}',
'expression': `console.log("${chars}")`,
'objectGroup': 'console',
'includeCommandLineAPI': true,
'silent': false,
'returnByValue': false,
'generatePreview': true
}
});
await session.waitForConsoleOutput('log', [chars]);
}
async function testCommandLineAPI(session) {
const testModulePath = require.resolve('../fixtures/empty.js');
const testModuleStr = JSON.stringify(testModulePath);
const printAModulePath = require.resolve('../fixtures/printA.js');
const printAModuleStr = JSON.stringify(printAModulePath);
const printBModulePath = require.resolve('../fixtures/printB.js');
const printBModuleStr = JSON.stringify(printBModulePath);
// we can use `require` outside of a callframe with require in scope
let result = await session.send(
{
'method': 'Runtime.evaluate', 'params': {
'expression': 'typeof require("fs").readFile === "function"',
'includeCommandLineAPI': true
}
});
checkException(result);
assert.strictEqual(result.result.value, true);
// the global require has the same properties as a normal `require`
result = await session.send(
{
'method': 'Runtime.evaluate', 'params': {
'expression': [
'typeof require.resolve === "function"',
'typeof require.extensions === "object"',
'typeof require.cache === "object"'
].join(' && '),
'includeCommandLineAPI': true
}
});
checkException(result);
assert.strictEqual(result.result.value, true);
// `require` twice returns the same value
result = await session.send(
{
'method': 'Runtime.evaluate', 'params': {
// 1. We require the same module twice
// 2. We mutate the exports so we can compare it later on
'expression': `
Object.assign(
require(${testModuleStr}),
{ old: 'yes' }
) === require(${testModuleStr})`,
'includeCommandLineAPI': true
}
});
checkException(result);
assert.strictEqual(result.result.value, true);
// after require the module appears in require.cache
result = await session.send(
{
'method': 'Runtime.evaluate', 'params': {
'expression': `JSON.stringify(
require.cache[${testModuleStr}].exports
)`,
'includeCommandLineAPI': true
}
});
checkException(result);
assert.deepStrictEqual(JSON.parse(result.result.value),
{ old: 'yes' });
// remove module from require.cache
result = await session.send(
{
'method': 'Runtime.evaluate', 'params': {
'expression': `delete require.cache[${testModuleStr}]`,
'includeCommandLineAPI': true
}
});
checkException(result);
assert.strictEqual(result.result.value, true);
// require again, should get fresh (empty) exports
result = await session.send(
{
'method': 'Runtime.evaluate', 'params': {
'expression': `JSON.stringify(require(${testModuleStr}))`,
'includeCommandLineAPI': true
}
});
checkException(result);
assert.deepStrictEqual(JSON.parse(result.result.value), {});
// require 2nd module, exports an empty object
result = await session.send(
{
'method': 'Runtime.evaluate', 'params': {
'expression': `JSON.stringify(require(${printAModuleStr}))`,
'includeCommandLineAPI': true
}
});
checkException(result);
assert.deepStrictEqual(JSON.parse(result.result.value), {});
// both modules end up with the same module.parent
result = await session.send(
{
'method': 'Runtime.evaluate', 'params': {
'expression': `JSON.stringify({
parentsEqual:
require.cache[${testModuleStr}].parent ===
require.cache[${printAModuleStr}].parent,
parentId: require.cache[${testModuleStr}].parent.id,
})`,
'includeCommandLineAPI': true
}
});
checkException(result);
assert.deepStrictEqual(JSON.parse(result.result.value), {
parentsEqual: true,
parentId: '<inspector console>'
});
// the `require` in the module shadows the command line API's `require`
result = await session.send(
{
'method': 'Debugger.evaluateOnCallFrame', 'params': {
'callFrameId': '{"ordinal":0,"injectedScriptId":1}',
'expression': `(
require(${printBModuleStr}),
require.cache[${printBModuleStr}].parent.id
)`,
'includeCommandLineAPI': true
}
});
checkException(result);
assert.notStrictEqual(result.result.value,
'<inspector console>');
}
async function runTest() {
const child = new NodeInstance();
checkListResponse(await child.httpGet(null, '/json'));
checkListResponse(await child.httpGet(null, '/json/list'));
checkVersion(await child.httpGet(null, '/json/version'));
await child.httpGet(null, '/json/activate').catch(checkBadPath);
await child.httpGet(null, '/json/activate/boom').catch(checkBadPath);
await child.httpGet(null, '/json/badpath').catch(checkBadPath);
const session = await child.connectInspectorSession();
await testBreakpointOnStart(session);
await testBreakpoint(session);
await testI18NCharacters(session);
await testCommandLineAPI(session);
await session.runToCompletion();
assert.strictEqual(55, (await child.expectShutdown()).exitCode);
}
runTest();