node/test/parallel/test-util-inspect.js
Rich Trott 4faaed69fa test,tools: enable linting for undefined vars
The test directory had linting for undefined variables disabled. It is
enabled everywhere else in the code base. Let's disable the fule for
individual lines in the handful of tests that use undefined variables.

PR-URL: https://github.com/nodejs/node/pull/6255
Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Roman Reiss <me@silverwind.io>
2016-04-20 08:38:41 -07:00

651 lines
21 KiB
JavaScript

'use strict';
require('../common');
const assert = require('assert');
const util = require('util');
const vm = require('vm');
assert.equal(util.inspect(1), '1');
assert.equal(util.inspect(false), 'false');
assert.equal(util.inspect(''), "''");
assert.equal(util.inspect('hello'), "'hello'");
assert.equal(util.inspect(function() {}), '[Function]');
assert.equal(util.inspect(undefined), 'undefined');
assert.equal(util.inspect(null), 'null');
assert.equal(util.inspect(/foo(bar\n)?/gi), '/foo(bar\\n)?/gi');
assert.equal(util.inspect(new Date('Sun, 14 Feb 2010 11:48:40 GMT')),
new Date('2010-02-14T12:48:40+01:00').toISOString());
assert.equal(util.inspect('\n\u0001'), "'\\n\\u0001'");
assert.equal(util.inspect([]), '[]');
assert.equal(util.inspect(Object.create([])), 'Array {}');
assert.equal(util.inspect([1, 2]), '[ 1, 2 ]');
assert.equal(util.inspect([1, [2, 3]]), '[ 1, [ 2, 3 ] ]');
assert.equal(util.inspect({}), '{}');
assert.equal(util.inspect({a: 1}), '{ a: 1 }');
assert.equal(util.inspect({a: function() {}}), '{ a: [Function] }');
assert.equal(util.inspect({a: 1, b: 2}), '{ a: 1, b: 2 }');
assert.equal(util.inspect({'a': {}}), '{ a: {} }');
assert.equal(util.inspect({'a': {'b': 2}}), '{ a: { b: 2 } }');
assert.equal(util.inspect({'a': {'b': { 'c': { 'd': 2 }}}}),
'{ a: { b: { c: [Object] } } }');
assert.equal(util.inspect({'a': {'b': { 'c': { 'd': 2 }}}}, false, null),
'{ a: { b: { c: { d: 2 } } } }');
assert.equal(util.inspect([1, 2, 3], true), '[ 1, 2, 3, [length]: 3 ]');
assert.equal(util.inspect({'a': {'b': { 'c': 2}}}, false, 0),
'{ a: [Object] }');
assert.equal(util.inspect({'a': {'b': { 'c': 2}}}, false, 1),
'{ a: { b: [Object] } }');
assert.equal(util.inspect(Object.create({},
{visible: {value: 1, enumerable: true}, hidden: {value: 2}})),
'{ visible: 1 }'
);
for (const showHidden of [true, false]) {
const ab = new ArrayBuffer(4);
const dv = new DataView(ab, 1, 2);
assert.equal(util.inspect(ab, showHidden), 'ArrayBuffer { byteLength: 4 }');
assert.equal(util.inspect(new DataView(ab, 1, 2), showHidden),
'DataView {\n' +
' byteLength: 2,\n' +
' byteOffset: 1,\n' +
' buffer: ArrayBuffer { byteLength: 4 } }');
assert.equal(util.inspect(ab, showHidden), 'ArrayBuffer { byteLength: 4 }');
assert.equal(util.inspect(dv, showHidden),
'DataView {\n' +
' byteLength: 2,\n' +
' byteOffset: 1,\n' +
' buffer: ArrayBuffer { byteLength: 4 } }');
ab.x = 42;
dv.y = 1337;
assert.equal(util.inspect(ab, showHidden),
'ArrayBuffer { byteLength: 4, x: 42 }');
assert.equal(util.inspect(dv, showHidden),
'DataView {\n' +
' byteLength: 2,\n' +
' byteOffset: 1,\n' +
' buffer: ArrayBuffer { byteLength: 4, x: 42 },\n' +
' y: 1337 }');
}
// Now do the same checks but from a different context
for (const showHidden of [true, false]) {
const ab = vm.runInNewContext('new ArrayBuffer(4)');
const dv = vm.runInNewContext('new DataView(ab, 1, 2)', { ab: ab });
assert.equal(util.inspect(ab, showHidden), 'ArrayBuffer { byteLength: 4 }');
assert.equal(util.inspect(new DataView(ab, 1, 2), showHidden),
'DataView {\n' +
' byteLength: 2,\n' +
' byteOffset: 1,\n' +
' buffer: ArrayBuffer { byteLength: 4 } }');
assert.equal(util.inspect(ab, showHidden), 'ArrayBuffer { byteLength: 4 }');
assert.equal(util.inspect(dv, showHidden),
'DataView {\n' +
' byteLength: 2,\n' +
' byteOffset: 1,\n' +
' buffer: ArrayBuffer { byteLength: 4 } }');
ab.x = 42;
dv.y = 1337;
assert.equal(util.inspect(ab, showHidden),
'ArrayBuffer { byteLength: 4, x: 42 }');
assert.equal(util.inspect(dv, showHidden),
'DataView {\n' +
' byteLength: 2,\n' +
' byteOffset: 1,\n' +
' buffer: ArrayBuffer { byteLength: 4, x: 42 },\n' +
' y: 1337 }');
}
[ Float32Array,
Float64Array,
Int16Array,
Int32Array,
Int8Array,
Uint16Array,
Uint32Array,
Uint8Array,
Uint8ClampedArray ].forEach((constructor) => {
const length = 2;
const byteLength = length * constructor.BYTES_PER_ELEMENT;
const array = new constructor(new ArrayBuffer(byteLength), 0, length);
array[0] = 65;
array[1] = 97;
assert.equal(util.inspect(array, true),
`${constructor.name} [\n` +
` 65,\n` +
` 97,\n` +
` [BYTES_PER_ELEMENT]: ${constructor.BYTES_PER_ELEMENT},\n` +
` [length]: ${length},\n` +
` [byteLength]: ${byteLength},\n` +
` [byteOffset]: 0,\n` +
` [buffer]: ArrayBuffer { byteLength: ${byteLength} } ]`);
assert.equal(util.inspect(array, false), `${constructor.name} [ 65, 97 ]`);
});
// Now check that declaring a TypedArray in a different context works the same
[ Float32Array,
Float64Array,
Int16Array,
Int32Array,
Int8Array,
Uint16Array,
Uint32Array,
Uint8Array,
Uint8ClampedArray ].forEach((constructor) => {
const length = 2;
const byteLength = length * constructor.BYTES_PER_ELEMENT;
const array = vm.runInNewContext('new constructor(new ArrayBuffer(' +
'byteLength), 0, length)',
{ constructor: constructor,
byteLength: byteLength,
length: length
});
array[0] = 65;
array[1] = 97;
assert.equal(util.inspect(array, true),
`${constructor.name} [\n` +
` 65,\n` +
` 97,\n` +
` [BYTES_PER_ELEMENT]: ${constructor.BYTES_PER_ELEMENT},\n` +
` [length]: ${length},\n` +
` [byteLength]: ${byteLength},\n` +
` [byteOffset]: 0,\n` +
` [buffer]: ArrayBuffer { byteLength: ${byteLength} } ]`);
assert.equal(util.inspect(array, false), `${constructor.name} [ 65, 97 ]`);
});
// Due to the hash seed randomization it's not deterministic the order that
// the following ways this hash is displayed.
// See http://codereview.chromium.org/9124004/
{
const out = util.inspect(Object.create({},
{visible: {value: 1, enumerable: true}, hidden: {value: 2}}), true);
if (out !== '{ [hidden]: 2, visible: 1 }' &&
out !== '{ visible: 1, [hidden]: 2 }') {
assert.ok(false);
}
}
// Objects without prototype
{
const out = util.inspect(Object.create(null,
{ name: {value: 'Tim', enumerable: true},
hidden: {value: 'secret'}}), true);
if (out !== "{ [hidden]: 'secret', name: 'Tim' }" &&
out !== "{ name: 'Tim', [hidden]: 'secret' }") {
assert(false);
}
}
assert.equal(
util.inspect(Object.create(null,
{name: {value: 'Tim', enumerable: true},
hidden: {value: 'secret'}})),
'{ name: \'Tim\' }'
);
// Dynamic properties
assert.equal(util.inspect({get readonly() {}}),
'{ readonly: [Getter] }');
assert.equal(util.inspect({get readwrite() {}, set readwrite(val) {}}),
'{ readwrite: [Getter/Setter] }');
assert.equal(util.inspect({set writeonly(val) {}}),
'{ writeonly: [Setter] }');
var value = {};
value['a'] = value;
assert.equal(util.inspect(value), '{ a: [Circular] }');
// Array with dynamic properties
value = [1, 2, 3];
value.__defineGetter__('growingLength', function() {
this.push(true); return this.length;
});
assert.equal(util.inspect(value), '[ 1, 2, 3, growingLength: [Getter] ]');
// Function with properties
value = function() {};
value.aprop = 42;
assert.equal(util.inspect(value), '{ [Function] aprop: 42 }');
// Regular expressions with properties
value = /123/ig;
value.aprop = 42;
assert.equal(util.inspect(value), '{ /123/gi aprop: 42 }');
// Dates with properties
value = new Date('Sun, 14 Feb 2010 11:48:40 GMT');
value.aprop = 42;
assert.equal(util.inspect(value), '{ 2010-02-14T11:48:40.000Z aprop: 42 }'
);
// test the internal isDate implementation
var Date2 = require('vm').runInNewContext('Date');
var d = new Date2();
var orig = util.inspect(d);
Date2.prototype.foo = 'bar';
var after = util.inspect(d);
assert.equal(orig, after);
// test positive/negative zero
assert.equal(util.inspect(0), '0');
assert.equal(util.inspect(-0), '-0');
// test for sparse array
var a = ['foo', 'bar', 'baz'];
assert.equal(util.inspect(a), '[ \'foo\', \'bar\', \'baz\' ]');
delete a[1];
assert.equal(util.inspect(a), '[ \'foo\', , \'baz\' ]');
assert.equal(util.inspect(a, true), '[ \'foo\', , \'baz\', [length]: 3 ]');
assert.equal(util.inspect(new Array(5)), '[ , , , , ]');
// test for Array constructor in different context
{
const Debug = require('vm').runInDebugContext('Debug');
const map = new Map();
map.set(1, 2);
const mirror = Debug.MakeMirror(map.entries(), true);
const vals = mirror.preview();
const valsOutput = [];
for (const o of vals) {
valsOutput.push(o);
}
assert.strictEqual(util.inspect(valsOutput), '[ [ 1, 2 ] ]');
}
// test for other constructors in different context
var obj = require('vm').runInNewContext('(function(){return {}})()', {});
assert.strictEqual(util.inspect(obj), '{}');
obj = require('vm').runInNewContext('var m=new Map();m.set(1,2);m', {});
assert.strictEqual(util.inspect(obj), 'Map { 1 => 2 }');
obj = require('vm').runInNewContext('var s=new Set();s.add(1);s.add(2);s', {});
assert.strictEqual(util.inspect(obj), 'Set { 1, 2 }');
obj = require('vm').runInNewContext('fn=function(){};new Promise(fn,fn)', {});
assert.strictEqual(util.inspect(obj), 'Promise { <pending> }');
// test for property descriptors
var getter = Object.create(null, {
a: {
get: function() { return 'aaa'; }
}
});
var setter = Object.create(null, {
b: {
set: function() {}
}
});
var getterAndSetter = Object.create(null, {
c: {
get: function() { return 'ccc'; },
set: function() {}
}
});
assert.equal(util.inspect(getter, true), '{ [a]: [Getter] }');
assert.equal(util.inspect(setter, true), '{ [b]: [Setter] }');
assert.equal(util.inspect(getterAndSetter, true), '{ [c]: [Getter/Setter] }');
// exceptions should print the error message, not '{}'
const errors = [];
errors.push(new Error());
errors.push(new Error('FAIL'));
errors.push(new TypeError('FAIL'));
errors.push(new SyntaxError('FAIL'));
errors.forEach(function(err) {
assert.equal(util.inspect(err), err.stack);
});
try {
undef(); // eslint-disable-line no-undef
} catch (e) {
assert.equal(util.inspect(e), e.stack);
}
var ex = util.inspect(new Error('FAIL'), true);
assert.ok(ex.indexOf('Error: FAIL') != -1);
assert.ok(ex.indexOf('[stack]') != -1);
assert.ok(ex.indexOf('[message]') != -1);
// Doesn't capture stack trace
function BadCustomError(msg) {
Error.call(this);
Object.defineProperty(this, 'message',
{ value: msg, enumerable: false });
Object.defineProperty(this, 'name',
{ value: 'BadCustomError', enumerable: false });
}
util.inherits(BadCustomError, Error);
assert.equal(util.inspect(new BadCustomError('foo')), '[BadCustomError: foo]');
// GH-1941
// should not throw:
assert.equal(util.inspect(Object.create(Date.prototype)), 'Date {}');
// GH-1944
assert.doesNotThrow(function() {
var d = new Date();
d.toUTCString = null;
util.inspect(d);
});
assert.doesNotThrow(function() {
var d = new Date();
d.toISOString = null;
util.inspect(d);
});
assert.doesNotThrow(function() {
var r = /regexp/;
r.toString = null;
util.inspect(r);
});
// bug with user-supplied inspect function returns non-string
assert.doesNotThrow(function() {
util.inspect([{
inspect: function() { return 123; }
}]);
});
// GH-2225
{
const x = { inspect: util.inspect };
assert.ok(util.inspect(x).indexOf('inspect') != -1);
}
// util.inspect should not display the escaped value of a key.
var w = {
'\\': 1,
'\\\\': 2,
'\\\\\\': 3,
'\\\\\\\\': 4,
};
var y = ['a', 'b', 'c'];
y['\\\\\\'] = 'd';
assert.ok(util.inspect(w),
'{ \'\\\': 1, \'\\\\\': 2, \'\\\\\\\': 3, \'\\\\\\\\\': 4 }');
assert.ok(util.inspect(y), '[ \'a\', \'b\', \'c\', \'\\\\\\\': \'d\' ]');
// util.inspect.styles and util.inspect.colors
function test_color_style(style, input, implicit) {
var color_name = util.inspect.styles[style];
var color = ['', ''];
if (util.inspect.colors[color_name])
color = util.inspect.colors[color_name];
var without_color = util.inspect(input, false, 0, false);
var with_color = util.inspect(input, false, 0, true);
var expect = '\u001b[' + color[0] + 'm' + without_color +
'\u001b[' + color[1] + 'm';
assert.equal(with_color, expect, 'util.inspect color for style ' + style);
}
test_color_style('special', function() {});
test_color_style('number', 123.456);
test_color_style('boolean', true);
test_color_style('undefined', undefined);
test_color_style('null', null);
test_color_style('string', 'test string');
test_color_style('date', new Date());
test_color_style('regexp', /regexp/);
// an object with "hasOwnProperty" overwritten should not throw
assert.doesNotThrow(function() {
util.inspect({
hasOwnProperty: null
});
});
// new API, accepts an "options" object
{
let subject = { foo: 'bar', hello: 31, a: { b: { c: { d: 0 } } } };
Object.defineProperty(subject, 'hidden', { enumerable: false, value: null });
assert(util.inspect(subject, { showHidden: false }).indexOf('hidden') === -1);
assert(util.inspect(subject, { showHidden: true }).indexOf('hidden') !== -1);
assert(util.inspect(subject, { colors: false }).indexOf('\u001b[32m') === -1);
assert(util.inspect(subject, { colors: true }).indexOf('\u001b[32m') !== -1);
assert(util.inspect(subject, { depth: 2 }).indexOf('c: [Object]') !== -1);
assert(util.inspect(subject, { depth: 0 }).indexOf('a: [Object]') !== -1);
assert(util.inspect(subject, { depth: null }).indexOf('{ d: 0 }') !== -1);
// "customInspect" option can enable/disable calling inspect() on objects
subject = { inspect: function() { return 123; } };
assert(util.inspect(subject,
{ customInspect: true }).indexOf('123') !== -1);
assert(util.inspect(subject,
{ customInspect: true }).indexOf('inspect') === -1);
assert(util.inspect(subject,
{ customInspect: false }).indexOf('123') === -1);
assert(util.inspect(subject,
{ customInspect: false }).indexOf('inspect') !== -1);
// custom inspect() functions should be able to return other Objects
subject.inspect = function() { return { foo: 'bar' }; };
assert.equal(util.inspect(subject), '{ foo: \'bar\' }');
subject.inspect = function(depth, opts) {
assert.strictEqual(opts.customInspectOptions, true);
};
util.inspect(subject, { customInspectOptions: true });
}
// util.inspect with "colors" option should produce as many lines as without it
function test_lines(input) {
var count_lines = function(str) {
return (str.match(/\n/g) || []).length;
};
var without_color = util.inspect(input);
var with_color = util.inspect(input, {colors: true});
assert.equal(count_lines(without_color), count_lines(with_color));
}
test_lines([1, 2, 3, 4, 5, 6, 7]);
test_lines(function() {
var big_array = [];
for (var i = 0; i < 100; i++) {
big_array.push(i);
}
return big_array;
}());
test_lines({foo: 'bar', baz: 35, b: {a: 35}});
test_lines({
foo: 'bar',
baz: 35,
b: {a: 35},
very_long_key: 'very_long_value',
even_longer_key: ['with even longer value in array']
});
// test boxed primitives output the correct values
assert.equal(util.inspect(new String('test')), '[String: \'test\']');
assert.equal(util.inspect(new Boolean(false)), '[Boolean: false]');
assert.equal(util.inspect(new Boolean(true)), '[Boolean: true]');
assert.equal(util.inspect(new Number(0)), '[Number: 0]');
assert.equal(util.inspect(new Number(-0)), '[Number: -0]');
assert.equal(util.inspect(new Number(-1.1)), '[Number: -1.1]');
assert.equal(util.inspect(new Number(13.37)), '[Number: 13.37]');
// test boxed primitives with own properties
var str = new String('baz');
str.foo = 'bar';
assert.equal(util.inspect(str), '{ [String: \'baz\'] foo: \'bar\' }');
var bool = new Boolean(true);
bool.foo = 'bar';
assert.equal(util.inspect(bool), '{ [Boolean: true] foo: \'bar\' }');
var num = new Number(13.37);
num.foo = 'bar';
assert.equal(util.inspect(num), '{ [Number: 13.37] foo: \'bar\' }');
// test es6 Symbol
if (typeof Symbol !== 'undefined') {
assert.equal(util.inspect(Symbol()), 'Symbol()');
assert.equal(util.inspect(Symbol(123)), 'Symbol(123)');
assert.equal(util.inspect(Symbol('hi')), 'Symbol(hi)');
assert.equal(util.inspect([Symbol()]), '[ Symbol() ]');
assert.equal(util.inspect({ foo: Symbol() }), '{ foo: Symbol() }');
const options = { showHidden: true };
let subject = {};
subject[Symbol('symbol')] = 42;
assert.equal(util.inspect(subject), '{}');
assert.equal(util.inspect(subject, options), '{ [Symbol(symbol)]: 42 }');
subject = [1, 2, 3];
subject[Symbol('symbol')] = 42;
assert.equal(util.inspect(subject), '[ 1, 2, 3 ]');
assert.equal(util.inspect(subject, options),
'[ 1, 2, 3, [length]: 3, [Symbol(symbol)]: 42 ]');
}
// test Set
assert.equal(util.inspect(new Set()), 'Set {}');
assert.equal(util.inspect(new Set([1, 2, 3])), 'Set { 1, 2, 3 }');
var set = new Set(['foo']);
set.bar = 42;
assert.equal(util.inspect(set, true), 'Set { \'foo\', [size]: 1, bar: 42 }');
// test Map
{
assert.equal(util.inspect(new Map()), 'Map {}');
assert.equal(util.inspect(new Map([[1, 'a'], [2, 'b'], [3, 'c']])),
'Map { 1 => \'a\', 2 => \'b\', 3 => \'c\' }');
const map = new Map([['foo', null]]);
map.bar = 42;
assert.equal(util.inspect(map, true),
'Map { \'foo\' => null, [size]: 1, bar: 42 }');
}
// test Promise
assert.equal(util.inspect(Promise.resolve(3)), 'Promise { 3 }');
assert.equal(util.inspect(Promise.reject(3)), 'Promise { <rejected> 3 }');
assert.equal(util.inspect(new Promise(function() {})), 'Promise { <pending> }');
var promise = Promise.resolve('foo');
promise.bar = 42;
assert.equal(util.inspect(promise), 'Promise { \'foo\', bar: 42 }');
// Make sure it doesn't choke on polyfills. Unlike Set/Map, there is no standard
// interface to synchronously inspect a Promise, so our techniques only work on
// a bonafide native Promise.
var oldPromise = Promise;
global.Promise = function() { this.bar = 42; };
assert.equal(util.inspect(new Promise()), '{ bar: 42 }');
global.Promise = oldPromise;
// Map/Set Iterators
var m = new Map([['foo', 'bar']]);
assert.strictEqual(util.inspect(m.keys()), 'MapIterator { \'foo\' }');
assert.strictEqual(util.inspect(m.values()), 'MapIterator { \'bar\' }');
assert.strictEqual(util.inspect(m.entries()),
'MapIterator { [ \'foo\', \'bar\' ] }');
// make sure the iterator doesn't get consumed
var keys = m.keys();
assert.strictEqual(util.inspect(keys), 'MapIterator { \'foo\' }');
assert.strictEqual(util.inspect(keys), 'MapIterator { \'foo\' }');
var s = new Set([1, 3]);
assert.strictEqual(util.inspect(s.keys()), 'SetIterator { 1, 3 }');
assert.strictEqual(util.inspect(s.values()), 'SetIterator { 1, 3 }');
assert.strictEqual(util.inspect(s.entries()),
'SetIterator { [ 1, 1 ], [ 3, 3 ] }');
// make sure the iterator doesn't get consumed
keys = s.keys();
assert.strictEqual(util.inspect(keys), 'SetIterator { 1, 3 }');
assert.strictEqual(util.inspect(keys), 'SetIterator { 1, 3 }');
// Test alignment of items in container
// Assumes that the first numeric character is the start of an item.
function checkAlignment(container) {
var lines = util.inspect(container).split('\n');
var pos;
lines.forEach(function(line) {
var npos = line.search(/\d/);
if (npos !== -1) {
if (pos !== undefined)
assert.equal(pos, npos, 'container items not aligned');
pos = npos;
}
});
}
var big_array = [];
for (var i = 0; i < 100; i++) {
big_array.push(i);
}
checkAlignment(big_array);
checkAlignment(function() {
var obj = {};
big_array.forEach(function(v) {
obj[v] = null;
});
return obj;
}());
checkAlignment(new Set(big_array));
checkAlignment(new Map(big_array.map(function(y) { return [y, null]; })));
// Test display of constructors
{
class ObjectSubclass {}
class ArraySubclass extends Array {}
class SetSubclass extends Set {}
class MapSubclass extends Map {}
class PromiseSubclass extends Promise {}
const x = new ObjectSubclass();
x.foo = 42;
assert.equal(util.inspect(x),
'ObjectSubclass { foo: 42 }');
assert.equal(util.inspect(new ArraySubclass(1, 2, 3)),
'ArraySubclass [ 1, 2, 3 ]');
assert.equal(util.inspect(new SetSubclass([1, 2, 3])),
'SetSubclass { 1, 2, 3 }');
assert.equal(util.inspect(new MapSubclass([['foo', 42]])),
'MapSubclass { \'foo\' => 42 }');
assert.equal(util.inspect(new PromiseSubclass(function() {})),
'PromiseSubclass { <pending> }');
}
// Corner cases.
{
const x = { constructor: 42 };
assert.equal(util.inspect(x), '{ constructor: 42 }');
}
{
const x = {};
Object.defineProperty(x, 'constructor', {
get: function() {
throw new Error('should not access constructor');
},
enumerable: true
});
assert.equal(util.inspect(x), '{ constructor: [Getter] }');
}
{
const x = new function() {};
assert.equal(util.inspect(x), '{}');
}
{
const x = Object.create(null);
assert.equal(util.inspect(x), '{}');
}