console-testing-library
Version:
Testing console the right way
256 lines (207 loc) • 7.15 kB
JavaScript
exports.__esModule = true;
exports.createConsole = createConsole;
exports.mockConsole = mockConsole;
exports.getLog = getLog;
exports.silenceConsole = silenceConsole;
exports.restore = restore;
var _console = require("console");
var _stream = require("stream");
var _util = _interopRequireDefault(require("util"));
var _jestSnapshot = require("jest-snapshot");
var _prettyFormat = _interopRequireDefault(require("pretty-format"));
var _stripAnsi = _interopRequireDefault(require("strip-ansi"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const INSPECT_SYMBOL = _util.default.inspect.custom;
const instances = new WeakMap();
const LEVELS = {
log: ['log', 'trace', 'dir', 'dirxml', 'group', 'groupCollapsed', 'debug', 'timeLog'],
info: ['count', 'info', 'timeEnd'],
warn: ['warn', 'countReset'],
error: ['error', 'assert']
};
function createConsole({
isSilent: defaultIsSilent = true,
stripAnsi: defaultStripAnsi = false
} = {}) {
let logs = [];
let records = {};
let levels = {
log: '',
info: '',
warn: '',
error: ''
};
let currentLevel = undefined;
let currentMethod = undefined;
let isSilent = defaultIsSilent;
let stripAnsi = defaultStripAnsi;
let targetConsole = undefined;
const stringifyLogs = logs => {
return logs.map(log => log[1]).join('\n');
};
const writable = new _stream.Writable({
write(chunk, encoding, callback) {
const message = chunk.toString('utf8') // Strip out the new line character in the end
.slice(0, -1);
const normalizedMessage = stripAnsi ? (0, _stripAnsi.default)(message) : message;
logs.push([currentLevel, normalizedMessage]);
if (currentLevel && currentLevel in levels) {
levels[currentLevel] = [levels[currentLevel], normalizedMessage].filter(Boolean).join('\n');
}
records[currentMethod] = [records[currentMethod], normalizedMessage].filter(Boolean).join('\n');
callback();
}
});
const testingConsole = new _console.Console(writable, writable);
testingConsole.clear = function clear() {
logs = [];
records = {};
levels = {
log: '',
info: '',
warn: '',
error: ''
};
currentLevel = undefined;
currentMethod = undefined;
};
Object.getOwnPropertyNames(testingConsole).forEach(property => {
if (typeof testingConsole[property] === 'function' && // Internal properties like `_stdoutErrorHandler` and `_stderrErrorHandler`
!property.startsWith('_')) {
if (property !== 'clear') {
const originalFunction = testingConsole[property];
testingConsole[property] = function (...args) {
currentLevel = undefined;
currentMethod = property;
Object.keys(LEVELS).forEach(level => {
if (LEVELS[level].includes(property)) {
currentLevel = level;
}
});
const prettiedArguments = args.map(argv => {
if ((typeof argv === 'object' || typeof argv === 'function') && argv !== null) {
// We return an object with inspect symbol here because string substitutions requires objects
return {
[INSPECT_SYMBOL]: () => (0, _prettyFormat.default)(argv)
};
}
return argv;
});
const returnValue = originalFunction.apply(this, prettiedArguments);
if (!isSilent && targetConsole) {
targetConsole[property].apply(this, args);
}
return returnValue;
};
}
Object.defineProperty(testingConsole[property], 'name', {
value: property
});
testingConsole[property].testingConsole = testingConsole;
if (typeof jest === 'object' && typeof jest.spyOn === 'function') {
jest.spyOn(testingConsole, property);
Object.defineProperty(testingConsole[property], 'name', {
value: property
});
testingConsole[property].testingConsole = testingConsole;
}
}
});
instances.set(testingConsole, {
get log() {
return stringifyLogs(logs);
},
get logs() {
return logs;
},
levels: {
get log() {
return levels.log;
},
get info() {
return levels.info;
},
get warn() {
return levels.warn;
},
get error() {
return levels.error;
}
},
get stderr() {
return stringifyLogs(logs.filter(([logLevel]) => ['warn', 'error'].includes(logLevel)));
},
get stdout() {
return stringifyLogs(logs.filter(([logLevel]) => ['log', 'info'].includes(logLevel)));
},
getRecord(method) {
return records[method] || '';
},
get silence() {
return isSilent;
},
set silence(shouldSilent = true) {
isSilent = !!shouldSilent;
},
get _targetConsole() {
return targetConsole;
},
set _targetConsole(target) {
targetConsole = target;
}
});
return testingConsole;
}
function mockConsole(testingConsole, targetConsoleParent = global, targetConsoleKey = 'console') {
const targetConsole = targetConsoleParent[targetConsoleKey];
targetConsoleParent[targetConsoleKey] = testingConsole;
const instance = instances.get(testingConsole);
instance._targetConsole = targetConsole;
return () => {
targetConsoleParent[targetConsoleKey] = targetConsole;
instance._targetConsole = undefined;
};
}
function getLog(testingConsole = global.console) {
return instances.get(testingConsole);
}
function silenceConsole(testingConsole = global.console, shouldSilent = true) {
if (typeof arguments[0] === 'boolean') {
testingConsole = global.console;
shouldSilent = arguments[0];
}
instances.get(testingConsole).silence = !!shouldSilent;
}
function restore() {
global.console = global.originalConsole;
}
if (typeof expect === 'function' && typeof expect.extend === 'function') {
expect.extend({
toMatchInlineSnapshot(received, ...args) {
/* ------- Workaround for custom inline snapshot matchers ------- */
const error = new Error();
const stacks = error.stack.split('\n');
for (let i = 1; i < stacks.length; i += 1) {
if (stacks[i].includes(__filename)) {
stacks.splice(1, i);
break;
}
}
error.stack = stacks.join('\n');
const context = Object.assign(this, {
error
});
/* -------------------------------------------------------------- */
const testingConsoleInstance = received && received.testingConsole || received;
if (!testingConsoleInstance || !instances.has(testingConsoleInstance)) {
return _jestSnapshot.toMatchInlineSnapshot.call(context, received, ...args);
}
if (typeof received === 'function') {
return _jestSnapshot.toMatchInlineSnapshot.call(context, getLog().getRecord(received.name), ...args);
} else if (typeof received === 'object') {
return _jestSnapshot.toMatchInlineSnapshot.call(context, getLog(received).log, ...args);
}
}
});
}
;