Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: add toWarnDev custom Jest matcher #3574

Merged
merged 3 commits into from
Mar 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@
"lint:fix": "eslint . --fix && tslint --project . --fix",
"type-check": "tsc",
"type-check:watch": "yarn type-check --watch",
"test": "jest",
"test:watch": "jest --watch --bail",
"test": "jest --config scripts/jest/config.js",
"test:watch": "jest --config scripts/jest/config.js --watch --bail",
"test:size": "bundlesize",
"test:functional": "wdio functional-tests/wdio.conf.js",
"test:functional:dev": "babel-node scripts/dev-functional-tests",
Expand Down Expand Up @@ -78,6 +78,7 @@
"@types/enzyme": "^3.1.15",
"@types/enzyme-adapter-react-16": "^1.0.3",
"@types/jest": "^24.0.0",
"@types/jest-diff": "^20.0.1",
"@types/lodash": "^4.14.120",
"@types/react": "^16.7.20",
"@types/react-dom": "^16.0.11",
Expand Down Expand Up @@ -113,6 +114,7 @@
"express": "4.16.4",
"inquirer": "6.2.2",
"jest": "24.1.0",
"jest-diff": "24.0.0",
"jest-watch-typeahead": "0.2.1",
"jsdom-global": "3.0.2",
"json": "9.0.6",
Expand Down
3 changes: 2 additions & 1 deletion jest.config.js → scripts/jest/config.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
/* eslint-disable import/no-commonjs */

module.exports = {
rootDir: process.cwd(),
testPathIgnorePatterns: [
'<rootDir>/node_modules/',
'<rootDir>/dist*',
'<rootDir>/functional-tests',
],
setupFilesAfterEnv: ['./scripts/jestInit.js'],
setupFilesAfterEnv: ['./scripts/jest/setupTests.js'],
watchPlugins: [
'jest-watch-typeahead/filename',
'jest-watch-typeahead/testname',
Expand Down
50 changes: 50 additions & 0 deletions scripts/jest/matchers/__tests__/toWarnDev-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/* tslint:disable:no-console */

describe('toWarnDev', () => {
describe('usage', () => {
test('fails with incorrect type of message', () => {
expect(() => {
// @ts-ignore:next-line
tkrugg marked this conversation as resolved.
Show resolved Hide resolved
expect(() => {}).toWarnDev(false);
}).toThrowErrorMatchingInlineSnapshot(
`"toWarnDev() requires a parameter of type string but was given boolean."`
);
});
});

if (__DEV__) {
describe('without message', () => {
test('does not fail if called', () => {
expect(() => {
expect(() => {
console.warn('warning');
}).toWarnDev();
}).not.toThrow();
});

test('fails if not called', () => {
expect(() => {
expect(() => {}).toWarnDev();
tkrugg marked this conversation as resolved.
Show resolved Hide resolved
}).toThrowErrorMatchingInlineSnapshot(`"No warning recorded."`);
});
});

describe('with message', () => {
test('does not fail with correct message', () => {
expect(() => {
expect(() => {
console.warn('warning');
}).toWarnDev('warning');
}).not.toThrow();
});

test('fails if a warning is not correct', () => {
expect(() => {
expect(() => {
console.warn('warning');
}).toWarnDev('another warning');
}).toThrow(/Unexpected warning recorded./);
});
});
}
});
1 change: 1 addition & 0 deletions scripts/jest/matchers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as toWarnDev } from './toWarnDev';
64 changes: 64 additions & 0 deletions scripts/jest/matchers/toWarnDev.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* tslint:disable:no-console */
import jestDiff from 'jest-diff';

declare global {
namespace jest {
interface Matchers<R> {
toWarnDev(expectedMessage?: string): R;
}
}
}

const matcher = {
toWarnDev: (callback: () => void, expectedMessage: string) => {
if (expectedMessage !== undefined && typeof expectedMessage !== 'string') {
throw new Error(
`toWarnDev() requires a parameter of type string but was given ${typeof expectedMessage}.`
);
}

if (!__DEV__) {
callback();

return { pass: true };
}

const originalWarnMethod = console.warn;
let calledTimes = 0;
let actualWarning = '';

console.warn = (message: string) => {
calledTimes++;
actualWarning = message;
};

callback();

console.warn = originalWarnMethod;

// Expectation without any message.
// We only check that `console.warn` was called.
if (expectedMessage === undefined && calledTimes === 0) {
return {
pass: false,
message: () => 'No warning recorded.',
};
}

// Expectation with a message.
if (expectedMessage !== undefined && actualWarning !== expectedMessage) {
return {
pass: false,
message: () => `Unexpected warning recorded.

Difference:

${jestDiff(expectedMessage, actualWarning)}`,
};
}

return { pass: true };
},
};

export default matcher;
2 changes: 2 additions & 0 deletions scripts/jestInit.js → scripts/jest/setupTests.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import { createSerializer } from 'enzyme-to-json';
import { toWarnDev } from './matchers';

Enzyme.configure({ adapter: new Adapter() });
expect.addSnapshotSerializer(createSerializer({ mode: 'deep' }));
expect.extend(toWarnDev);
9 changes: 3 additions & 6 deletions src/connectors/breadcrumb/__tests__/connectBreadcrumb-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,13 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/breadcrumb/
}

// when there is an identical hierarchicalFacets into current configuration
{
const spy = jest.spyOn(global.console, 'warn');
expect(() => {
const config = widget.getConfiguration({
hierarchicalFacets: [{ name: 'category' }],
});

expect(config).toEqual({});
expect(spy).toHaveBeenCalled();
spy.mockReset();
spy.mockRestore();
}
}).toWarnDev();

// when there is already a different hierarchicalFacets into current configuration
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,13 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/hierarchica
}

// when there is an identical hierarchicalFacets into current configuration
{
const spy = jest.spyOn(global.console, 'warn');
expect(() => {
const config = widget.getConfiguration({
hierarchicalFacets: [{ name: 'category' }],
});

expect(config).toEqual({});
expect(spy).toHaveBeenCalled();
spy.mockReset();
spy.mockRestore();
}
}).toWarnDev();

// when there is already a different hierarchicalFacets into current configuration
{
Expand Down
27 changes: 11 additions & 16 deletions src/widgets/hits-per-page/__tests__/hits-per-page-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,9 @@ describe('hitsPerPage()', () => {
let widget;
let helper;
let results;
let consoleWarn;
let state;

beforeEach(() => {
consoleWarn = jest.spyOn(window.console, 'warn');

container = document.createElement('div');
items = [
{ value: 10, label: '10 results' },
Expand Down Expand Up @@ -117,22 +114,24 @@ describe('hitsPerPage()', () => {
expect(helper.search).toHaveBeenCalledTimes(1, 'search called once');
});

it('should throw if there is no name attribute in a passed object', () => {
it('should warn without name attribute in a passed item', () => {
items.length = 0;
items.push({ label: 'Label without a value' });
widget.init({ state: helper.state, helper });
expect(consoleWarn).toHaveBeenCalledTimes(1, 'console.warn called once');
expect(consoleWarn.mock.calls[0][0]).toMatchInlineSnapshot(
`"[InstantSearch.js]: No items in HitsPerPage \`items\` with \`value: hitsPerPage\` (hitsPerPage: 20)"`

expect(() => {
widget.init({ state: helper.state, helper });
}).toWarnDev(
'[InstantSearch.js]: No items in HitsPerPage `items` with `value: hitsPerPage` (hitsPerPage: 20)'
);
});

it('must include the current hitsPerPage at initialization time', () => {
helper.state.hitsPerPage = -1;
widget.init({ state: helper.state, helper });
expect(consoleWarn).toHaveBeenCalledTimes(1, 'console.warn called once');
expect(consoleWarn.mock.calls[0][0]).toMatchInlineSnapshot(
`"[InstantSearch.js]: No items in HitsPerPage \`items\` with \`value: hitsPerPage\` (hitsPerPage: -1)"`

expect(() => {
widget.init({ state: helper.state, helper });
}).toWarnDev(
'[InstantSearch.js]: No items in HitsPerPage `items` with `value: hitsPerPage` (hitsPerPage: -1)'
);
});

Expand All @@ -142,8 +141,4 @@ describe('hitsPerPage()', () => {
widget.init({ state: helper.state, helper });
}).not.toThrow(/No item in `items`/);
});

afterEach(() => {
consoleWarn.mockRestore();
});
});
15 changes: 5 additions & 10 deletions src/widgets/panel/__tests__/panel-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,13 @@ describe('Usage', () => {
});

test('with `hidden` as boolean warns', () => {
const warn = jest.spyOn(global.console, 'warn');
warn.mockImplementation(() => {});

panel({
hidden: true,
});

expect(warn).toHaveBeenCalledWith(
expect(() => {
panel({
hidden: true,
});
}).toWarnDev(
'[InstantSearch.js]: The `hidden` option in the "panel" widget expects a function returning a boolean (received "boolean" type).'
);

warn.mockRestore();
});

test('with a widget without `container` throws', () => {
Expand Down
3 changes: 2 additions & 1 deletion tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"object-literal-sort-keys": false,
"ordered-imports": false,
"prettier": true,
"jsx-no-lambda": false
"jsx-no-lambda": false,
"no-empty": false
}
}
7 changes: 6 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1375,6 +1375,11 @@
"@types/minimatch" "*"
"@types/node" "*"

"@types/jest-diff@^20.0.1":
version "20.0.1"
resolved "https://registry.yarnpkg.com/@types/jest-diff/-/jest-diff-20.0.1.tgz#35cc15b9c4f30a18ef21852e255fdb02f6d59b89"
integrity sha512-yALhelO3i0hqZwhjtcr6dYyaLoCHbAMshwtj6cGxTvHZAKXHsYGdff6E8EPw3xLKY0ELUTQ69Q1rQiJENnccMA==

"@types/jest@^24.0.0":
version "24.0.1"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.0.1.tgz#8945411140e7cdfd5ea76380e5f5fa7bf17646ea"
Expand Down Expand Up @@ -7028,7 +7033,7 @@ jest-config@^24.1.0:
pretty-format "^24.0.0"
realpath-native "^1.0.2"

jest-diff@^24.0.0:
jest-diff@24.0.0, jest-diff@^24.0.0:
version "24.0.0"
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.0.0.tgz#a3e5f573dbac482f7d9513ac9cfa21644d3d6b34"
integrity sha512-XY5wMpRaTsuMoU+1/B2zQSKQ9RdE9gsLkGydx3nvApeyPijLA8GtEvIcPwISRCer+VDf9W1mStTYYq6fPt8ryA==
Expand Down