Skip to content

Commit

Permalink
[test] Enable failing unexpected console warn|error in browser tests (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
eps1lon authored Oct 15, 2020
1 parent 777c1a1 commit fd1c050
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 65 deletions.
28 changes: 28 additions & 0 deletions test/karma.tests.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,32 @@
/* eslint-env mocha */
import './utils/init';
import { createMochaHooks } from './utils/mochaHooks';

const mochaHooks = createMochaHooks(window.Mocha);

before(function beforeAllHook() {
mochaHooks.beforeAll.forEach((mochaHook) => {
mochaHook.call(this);
});
});

after(function afterAllHook() {
mochaHooks.afterAll.forEach((mochaHook) => {
mochaHook.call(this);
});
});

beforeEach(function beforeEachHook() {
mochaHooks.beforeEach.forEach((mochaHook) => {
mochaHook.call(this);
});
});

afterEach(function afterEachHook() {
mochaHooks.afterEach.forEach((mochaHook) => {
mochaHook.call(this);
});
});

const integrationContext = require.context(
'../packages/material-ui/test/integration',
Expand Down
80 changes: 80 additions & 0 deletions test/utils/mochaHooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
const formatUtil = require('format-util');

function createUnexpectedConsoleMessagesHooks(Mocha, methodName, expectedMatcher) {
const mochaHooks = {
beforeAll: [],
afterAll: [],
beforeEach: [],
afterEach: [],
};
const unexpectedCalls = [];
const stackTraceFilter = Mocha.utils.stackTraceFilter();

function logUnexpectedConsoleCalls(format, ...args) {
const message = formatUtil(format, ...args);
// Safe stack so that test dev can track where the unexpected console message was created.
const { stack } = new Error();

unexpectedCalls.push([
// first line includes the (empty) error message
// i.e. Remove the `Error:` line
// second line is this frame
stackTraceFilter(stack.split('\n').slice(2).join('\n')),
message,
]);
}

mochaHooks.beforeAll.push(function registerConsoleStub() {
// eslint-disable-next-line no-console
console[methodName] = logUnexpectedConsoleCalls;
});

mochaHooks.afterEach.push(function flushUnexpectedCalls() {
const hadUnexpectedCalls = unexpectedCalls.length > 0;
const formattedCalls = unexpectedCalls.map(([stack, message]) => `${message}\n${stack}`);
unexpectedCalls.length = 0;

// eslint-disable-next-line no-console
if (console[methodName] !== logUnexpectedConsoleCalls) {
throw new Error(`Did not tear down spy or stub of console.${methodName} in your test.`);
}
if (hadUnexpectedCalls) {
// In karma `file` is `null`.
// We still have the stacktrace though
const location = this.currentTest.file ?? '(unknown file)';
const testPath = `"${this.currentTest.parent
.titlePath()
.concat(this.currentTest.title)
.join('" -> "')}"`;
const message =
`Expected test not to call console.${methodName}()\n\n` +
'If the warning is expected, test for it explicitly by ' +
`using the ${expectedMatcher}() matcher.`;

const error = new Error(
`${location}: ${message}\n\n${formattedCalls.join('\n\n')}\n\n` +
`in ${testPath} (${location})`,
);
// The stack of `flushUnexpectedCalls` is irrelevant.
// It includes no clue where the test was triggered
error.stack = '';
throw error;
}
});

return mochaHooks;
}

function createMochaHooks(Mocha) {
const warnHooks = createUnexpectedConsoleMessagesHooks(Mocha, 'warn', 'toWarnDev');
const errorHooks = createUnexpectedConsoleMessagesHooks(Mocha, 'error', 'toErrorDev');

return {
beforeAll: [...warnHooks.beforeAll, ...errorHooks.beforeAll],
afterAll: [...warnHooks.afterAll, ...errorHooks.afterAll],
beforeEach: [...warnHooks.beforeEach, ...errorHooks.beforeEach],
afterEach: [...warnHooks.afterEach, ...errorHooks.afterEach],
};
}

module.exports = { createMochaHooks };
68 changes: 3 additions & 65 deletions test/utils/setupJSDOM.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const formatUtil = require('format-util');
const Mocha = require('mocha');
const testingLibrary = require('@testing-library/dom');
const Mocha = require('mocha');
const createDOM = require('./createDOM');
const { createMochaHooks } = require('./mochaHooks');

// Enable missing act warnings: https://github.com/facebook/react/blob/v16.13.1/packages/react-reconciler/src/ReactFiberHooks.js#L965
// TODO: Revisit once https://github.com/facebook/react/issues/15439 is resolved.
Expand All @@ -15,68 +15,6 @@ testingLibrary.configure({
computedStyleSupportsPseudoElements: false,
});

const mochaHooks = {
beforeAll: [],
beforeEach: [],
afterEach: [],
};

function throwOnUnexpectedConsoleMessages(methodName, expectedMatcher) {
const unexpectedCalls = [];
const stackTraceFilter = Mocha.utils.stackTraceFilter();

function logUnexpectedConsoleCalls(format, ...args) {
const message = formatUtil(format, ...args);
// Safe stack so that test dev can track where the unexpected console message was created.
const { stack } = new Error();

unexpectedCalls.push([
// first line includes the (empty) error message
// i.e. Remove the `Error:` line
// second line is this frame
stackTraceFilter(stack.split('\n').slice(2).join('\n')),
message,
]);
}

mochaHooks.beforeAll.push(function registerConsoleStub() {
// eslint-disable-next-line no-console
console[methodName] = logUnexpectedConsoleCalls;
});

mochaHooks.afterEach.push(function flushUnexpectedCalls() {
const hadUnexpectedCalls = unexpectedCalls.length > 0;
const formattedCalls = unexpectedCalls.map(([stack, message]) => `${message}\n${stack}`);
unexpectedCalls.length = 0;

// eslint-disable-next-line no-console
if (console[methodName] !== logUnexpectedConsoleCalls) {
throw new Error(`Did not tear down spy or stub of console.${methodName} in your test.`);
}
if (hadUnexpectedCalls) {
const location = this.currentTest.file;
const testPath = `"${this.currentTest.parent
.titlePath()
.concat(this.currentTest.title)
.join('" -> "')}"`;
const message =
`Expected test not to call console.${methodName}()\n\n` +
'If the warning is expected, test for it explicitly by ' +
`using the ${expectedMatcher}() matcher.`;

const error = new Error(
`${location}: ${message}\n\n${formattedCalls.join('\n\n')}\n\n` +
`in ${testPath} (${location})`,
);
// The stack of `flushUnexpectedCalls` is irrelevant.
// It includes no clue where the test was triggered
error.stack = '';
throw error;
}
});
}

throwOnUnexpectedConsoleMessages('warn', 'toWarnDev');
throwOnUnexpectedConsoleMessages('error', 'toErrorDev');
const mochaHooks = createMochaHooks(Mocha);

module.exports = { mochaHooks };

0 comments on commit fd1c050

Please sign in to comment.