Skip to content

Commit

Permalink
Jest Console: Add new matchers for console.log and console.info (#137)
Browse files Browse the repository at this point in the history
* Jest Console: Add new matchers for console.log and console.info

* Jest-console: Update CHANGELOG with braking changes details
  • Loading branch information
gziolo authored Jun 26, 2018
1 parent c24da3a commit 39314f7
Show file tree
Hide file tree
Showing 9 changed files with 193 additions and 129 deletions.
4 changes: 4 additions & 0 deletions packages/jest-console/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## Unreleased (2.0.0)

- Breaking: Add new API methods `toHaveInformed`, `toHaveInformedWith`, `toHaveLogged` and `toHaveLoggedWith` ([137](https://github.com/WordPress/packages/pull/137)). If the code under test calls `console.log` or `console.info` it will fail, unless one of the newly introduced methods is explicitly used to verify it.

## 1.0.7 (2018-05-18)

- Fix: Standardized `package.json` format ([#119](https://github.com/WordPress/packages/pull/119))
Expand Down
30 changes: 28 additions & 2 deletions packages/jest-console/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
Custom [Jest](http://facebook.github.io/jest/) matchers for the [Console](https://developer.mozilla.org/en-US/docs/Web/API/Console)
object to test JavaScript code in WordPress.

This package converts `console.error` and `console.warn` functions into mocks and tracks their calls.
This package converts `console.error`, `console.info`, `console.log` and `console.warn` functions into mocks and tracks their calls.
It also enforces usage of one of the related matchers whenever tested code calls one of the mentioned `console` methods.
It means that you need to assert with `.toHaveErrored()` or `.toHaveErroredWith( arg1, arg2, ... )` when `console.error`
gets executed, and `.toHaveWarned()` or `.toHaveWarnedWith( arg1, arg2, ... )` when `console.warn` is called.
gets executed, and use the corresponding methods when `console.info`, `console.log` or `console.warn` are called.
Your test will fail otherwise! This is a conscious design decision which helps to detect deprecation warnings when
upgrading dependent libraries or smaller errors when refactoring code.

Expand Down Expand Up @@ -76,6 +76,32 @@ describe( 'drinkAll', () => {
} );
```

### `.toHaveInformed()`

Use `.toHaveInformed` to ensure that `console.info` function was called.

Almost identical usage as `.toHaveErrored()`.

### `.toHaveInformedWith( arg1, arg2, ... )`

Use `.toHaveInformedWith` to ensure that `console.info` function was called with
specific arguments.

Almost identical usage as `.toHaveErroredWith()`.

### `.toHaveLogged()`

Use `.toHaveLogged` to ensure that `console.log` function was called.

Almost identical usage as `.toHaveErrored()`.

### `.toHaveLoggedWith( arg1, arg2, ... )`

Use `.toHaveLoggedWith` to ensure that `console.log` function was called with
specific arguments.

Almost identical usage as `.toHaveErroredWith()`.

### `.toHaveWarned()`

Use `.toHaveWarned` to ensure that `console.warn` function was called.
Expand Down
9 changes: 5 additions & 4 deletions packages/jest-console/src/index.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
/**
* External dependencies
*/
import { upperFirst } from 'lodash';
import { forEach, upperFirst } from 'lodash';

/**
* Internal dependencies
*/
import './matchers';
import supportedMatchers from './supported-matchers';

/**
* Sets spy on the console object's method to make it possible to fail test when method called without assertion.
*
* @param {String} matcherName Name of Jest matcher.
* @param {String} methodName Name of console method.
*/
const setConsoleMethodSpy = methodName => {
const setConsoleMethodSpy = ( matcherName, methodName ) => {
const spy = jest.spyOn( console, methodName ).mockName( `console.${ methodName }` );

beforeEach( () => {
Expand All @@ -23,10 +25,9 @@ const setConsoleMethodSpy = methodName => {

afterEach( () => {
if ( spy.assertionsNumber === 0 && spy.mock.calls.length > 0 ) {
const matcherName = `toHave${ upperFirst( methodName ) }ed`;
expect( console ).not[ matcherName ]();
}
} );
};

[ 'error', 'warn' ].forEach( setConsoleMethodSpy );
forEach( supportedMatchers, setConsoleMethodSpy );
24 changes: 17 additions & 7 deletions packages/jest-console/src/matchers.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
* External dependencies
*/
import { matcherHint, printExpected, printReceived } from 'jest-matcher-utils';
import { isEqual, some } from 'lodash';
import { isEqual, reduce, some } from 'lodash';

/**
* Internal dependencies
*/
import supportedMatchers from './supported-matchers';

const createToBeCalledMatcher = ( matcherName, methodName ) =>
( received ) => {
Expand Down Expand Up @@ -58,9 +63,14 @@ const createToBeCalledWithMatcher = ( matcherName, methodName ) =>
};
};

expect.extend( {
toHaveErrored: createToBeCalledMatcher( '.toHaveErrored', 'error' ),
toHaveErroredWith: createToBeCalledWithMatcher( '.toHaveErroredWith', 'error' ),
toHaveWarned: createToBeCalledMatcher( '.toHaveWarned', 'warn' ),
toHaveWarnedWith: createToBeCalledWithMatcher( '.toHaveWarnedWith', 'warn' ),
} );
expect.extend(
reduce( supportedMatchers, ( result, matcherName, methodName ) => {
const matcherNameWith = `${ matcherName }With`;

return {
...result,
[ matcherName ]: createToBeCalledMatcher( `.${ matcherName }`, methodName ),
[ matcherNameWith ]: createToBeCalledWithMatcher( `.${ matcherNameWith }`, methodName ),
};
}, {} )
);
8 changes: 8 additions & 0 deletions packages/jest-console/src/supported-matchers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const supportedMatchers = {
error: 'toHaveErrored',
info: 'toHaveInformed',
log: 'toHaveLogged',
warn: 'toHaveWarned',
};

export default supportedMatchers;
56 changes: 52 additions & 4 deletions packages/jest-console/src/test/__snapshots__/index.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,54 @@ but it was called with:
[\\"Unknown message.\\"],[\\"This is error!\\", \\"Unknown param.\\"]"
`;

exports[`jest-console console.info toHaveInformed works when not called 1`] = `
"expect(jest.fn()).toHaveInformed(expected)
Expected mock function to be called."
`;

exports[`jest-console console.info toHaveInformedWith works when not called 1`] = `
"expect(jest.fn()).toHaveInformedWith(expected)
Expected mock function to be called with:
[\\"This is info!\\"]
but it was called with:
"
`;

exports[`jest-console console.info toHaveInformedWith works with many arguments that do not match 1`] = `
"expect(jest.fn()).toHaveInformedWith(expected)
Expected mock function to be called with:
[\\"This is info!\\"]
but it was called with:
[\\"Unknown message.\\"],[\\"This is info!\\", \\"Unknown param.\\"]"
`;

exports[`jest-console console.log toHaveLogged works when not called 1`] = `
"expect(jest.fn()).toHaveLogged(expected)
Expected mock function to be called."
`;

exports[`jest-console console.log toHaveLoggedWith works when not called 1`] = `
"expect(jest.fn()).toHaveLoggedWith(expected)
Expected mock function to be called with:
[\\"This is log!\\"]
but it was called with:
"
`;

exports[`jest-console console.log toHaveLoggedWith works with many arguments that do not match 1`] = `
"expect(jest.fn()).toHaveLoggedWith(expected)
Expected mock function to be called with:
[\\"This is log!\\"]
but it was called with:
[\\"Unknown message.\\"],[\\"This is log!\\", \\"Unknown param.\\"]"
`;

exports[`jest-console console.warn toHaveWarned works when not called 1`] = `
"expect(jest.fn()).toHaveWarned(expected)
Expand All @@ -34,16 +82,16 @@ exports[`jest-console console.warn toHaveWarnedWith works when not called 1`] =
"expect(jest.fn()).toHaveWarnedWith(expected)
Expected mock function to be called with:
[32m[\\"This is warning!\\"][39m
[32m[\\"This is warn!\\"][39m
but it was called with:
"
`;

exports[`jest-console console.warn toHaveWarnedWith works with arguments that do not match 1`] = `
exports[`jest-console console.warn toHaveWarnedWith works with many arguments that do not match 1`] = `
"expect(jest.fn()).toHaveWarnedWith(expected)
Expected mock function to be called with:
[32m[\\"This is warning!\\"][39m
[32m[\\"This is warn!\\"][39m
but it was called with:
[31m[\\"Unknown message.\\"][39m,[31m[\\"This is warning!\\", \\"Unknown param.\\"][39m"
[31m[\\"Unknown message.\\"][39m,[31m[\\"This is warn!\\", \\"Unknown param.\\"][39m"
`;
179 changes: 69 additions & 110 deletions packages/jest-console/src/test/index.test.js
Original file line number Diff line number Diff line change
@@ -1,117 +1,76 @@
/**
* External dependencies
*/
import { forEach } from 'lodash';

/**
* Internal dependencies
*/
import '..';

describe( 'jest-console', () => {
describe( 'console.error', () => {
const message = 'This is error!';

test( 'toHaveErrored works', () => {
console.error( message );

expect( console ).toHaveErrored();
} );

test( 'toHaveErrored works when not called', () => {
expect( console ).not.toHaveErrored();
expect(
() => expect( console ).toHaveErrored()
).toThrowErrorMatchingSnapshot();
} );

test( 'toHaveErroredWith works with arguments that match', () => {
console.error( message );

expect( console ).toHaveErroredWith( message );
} );

test( 'toHaveErroredWith works when not called', () => {
expect( console ).not.toHaveErroredWith( message );
expect(
() => expect( console ).toHaveErroredWith( message )
).toThrowErrorMatchingSnapshot();
} );

test( 'toHaveErroredWith works with many arguments that do not match', () => {
console.error( 'Unknown message.' );
console.error( message, 'Unknown param.' );

expect( console ).not.toHaveErroredWith( message );
expect(
() => expect( console ).toHaveErroredWith( message )
).toThrowErrorMatchingSnapshot();
} );

test( 'assertions number gets incremented after every matcher call', () => {
const spy = console.error;

expect( spy.assertionsNumber ).toBe( 0 );

console.error( message );

expect( console ).toHaveErrored();
expect( spy.assertionsNumber ).toBe( 1 );

expect( console ).toHaveErroredWith( message );
expect( spy.assertionsNumber ).toBe( 2 );
} );
} );

describe( 'console.warn', () => {
const message = 'This is warning!';

test( 'toHaveWarned works', () => {
console.warn( message );

expect( console ).toHaveWarned();
} );

test( 'toHaveWarned works when not called', () => {
expect( console ).not.toHaveWarned();

expect(
() => expect( console ).toHaveWarned()
).toThrowErrorMatchingSnapshot();
} );

test( 'toHaveWarnedWith works with arguments that match', () => {
console.warn( message );

expect( console ).toHaveWarnedWith( message );
} );

test( 'toHaveWarnedWith works when not called', () => {
expect( console ).not.toHaveWarnedWith( message );

expect(
() => expect( console ).toHaveWarnedWith( message )
).toThrowErrorMatchingSnapshot();
} );

test( 'toHaveWarnedWith works with arguments that do not match', () => {
console.warn( 'Unknown message.' );
console.warn( message, 'Unknown param.' );

expect( console ).not.toHaveWarnedWith( message );

expect(
() => expect( console ).toHaveWarnedWith( message )
).toThrowErrorMatchingSnapshot();
} );

test( 'assertions number gets incremented after every matcher call', () => {
const spy = console.warn;

expect( spy.assertionsNumber ).toBe( 0 );

console.warn( message );

expect( console ).toHaveWarned();
expect( spy.assertionsNumber ).toBe( 1 );

expect( console ).toHaveWarnedWith( message );
expect( spy.assertionsNumber ).toBe( 2 );
} );
} );
forEach(
{
error: 'toHaveErrored',
info: 'toHaveInformed',
log: 'toHaveLogged',
warn: 'toHaveWarned',
},
( matcherName, methodName ) => {
describe( `console.${ methodName }`, () => {
const matcherNameWith = `${ matcherName}With`;
const message = `This is ${ methodName }!`;

test( '${ matcherName } works', () => {
console[ methodName ]( message );

expect( console )[ matcherName ]();
} );

test( `${ matcherName } works when not called`, () => {
expect( console ).not[ matcherName ]();
expect(
() => expect( console )[ matcherName ]()
).toThrowErrorMatchingSnapshot();
} );

test( `${ matcherNameWith } works with arguments that match`, () => {
console[ methodName ]( message );

expect( console )[ matcherNameWith ]( message );
} );

test( `${ matcherNameWith } works when not called`, () => {
expect( console ).not[ matcherNameWith ]( message );
expect(
() => expect( console )[ matcherNameWith ]( message )
).toThrowErrorMatchingSnapshot();
} );

test( `${ matcherNameWith } works with many arguments that do not match`, () => {
console[ methodName ]( 'Unknown message.' );
console[ methodName ]( message, 'Unknown param.' );

expect( console ).not[ matcherNameWith ]( message );
expect(
() => expect( console )[ matcherNameWith ]( message )
).toThrowErrorMatchingSnapshot();
} );

test( 'assertions number gets incremented after every matcher call', () => {
const spy = console[ methodName ];

expect( spy.assertionsNumber ).toBe( 0 );

console[ methodName ]( message );

expect( console )[ matcherName ]();
expect( spy.assertionsNumber ).toBe( 1 );

expect( console )[ matcherNameWith ]( message );
expect( spy.assertionsNumber ).toBe( 2 );
} );
} );
}
);
} );
Loading

0 comments on commit 39314f7

Please sign in to comment.