fix: use util.inspect for more reliable logging (#37880)

The tests are probably overkill, but this way we will know if
util.inspect changes dramatically.
pull/37881/head
Oliver Eyton-Williams 2019-12-06 17:37:10 +01:00 committed by Randell Dawson
parent 450ac77e6a
commit d40be9cbf2
4 changed files with 67 additions and 9 deletions

View File

@ -1,6 +1,7 @@
import chai from 'chai';
import '@babel/polyfill';
import __toString from 'lodash/toString';
import { format as __format } from '../../utils/format';
const __utils = (() => {
const MAX_LOGS_SIZE = 64 * 1024;
@ -16,16 +17,9 @@ const __utils = (() => {
}
}
function replacer(key, value) {
if (Number.isNaN(value)) {
return 'NaN';
}
return value;
}
const oldLog = self.console.log.bind(self.console);
function proxyLog(...args) {
logs.push(args.map(arg => '' + JSON.stringify(arg, replacer)).join(' '));
logs.push(args.map(arg => __format(arg)).join(' '));
if (logs.join('\n').length > MAX_LOGS_SIZE) {
flushLogs();
}

View File

@ -1,4 +1,5 @@
import { toString, flow } from 'lodash';
import { format } from '../../../utils/format';
// we use two different frames to make them all essentially pure functions
// main iframe is responsible rendering the preview and is where we proxy the
@ -81,7 +82,7 @@ const mountFrame = document => ({ element, ...rest }) => {
const buildProxyConsole = proxyLogger => ctx => {
const oldLog = ctx.window.console.log.bind(ctx.window.console);
ctx.window.console.log = function proxyConsole(...args) {
proxyLogger(args.map(arg => '' + JSON.stringify(arg)).join(' '));
proxyLogger(args.map(arg => format(arg)).join(' '));
return oldLog(...args);
};
return ctx;

View File

@ -0,0 +1,7 @@
import { inspect } from 'util';
export function format(x) {
// we're trying to mimic console.log, so we avoid wrapping strings in quotes:
if (typeof x === 'string') return x;
return inspect(x);
}

View File

@ -0,0 +1,56 @@
/* global expect BigInt */
const { format } = require('./format');
/* eslint-disable no-unused-vars */
function simpleFun() {
var x = 'y';
}
/* eslint-enable no-unused-vars */
/* format uses util.inspect to do almost everything, the tests are just there
to warn us if util.inspect ever changes */
describe('format', () => {
it('returns a string', () => {
expect(typeof format('')).toBe('string');
expect(typeof format({})).toBe('string');
expect(typeof format([])).toBe('string');
});
it('does not modify strings', () => {
expect(format('')).toBe('');
expect(format('abcde')).toBe('abcde');
expect(format('Case Sensitive')).toBe('Case Sensitive');
});
it('formats shallow objects nicely', () => {
expect(format({})).toBe('{}');
expect(format({ a: 'one', b: 'two' })).toBe(`{ a: 'one', b: 'two' }`);
});
it('formats functions the same way as console.log', () => {
expect(format(simpleFun)).toBe('[Function: simpleFun]');
});
it('recurses into arrays', () => {
const objsInArr = [{ a: 'one' }, 'b', simpleFun];
expect(format(objsInArr)).toBe(
`[ { a: 'one' }, 'b', [Function: simpleFun] ]`
);
});
it('handles all primitive values', () => {
const primitives = [
'str',
57,
BigInt(10),
true,
false,
null,
// eslint-disable-next-line no-undefined
undefined,
Symbol('Sym')
];
expect(format(primitives)).toBe(
`[ 'str', 57, 10n, true, false, null, undefined, Symbol(Sym) ]`
);
});
it(`outputs NaN as 'NaN'`, () => {
expect(format(NaN)).toBe('NaN');
});
});