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

[jest-each] Add pretty format #6357

Merged
merged 8 commits into from
Jun 2, 2018
Merged
Show file tree
Hide file tree
Changes from 4 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### Features

* `[jest-each]` Add pretty-format serialising to each titles ([#6357](https://github.com/facebook/jest/pull/6357))
* `[jest-cli]` shouldRunTestSuite watch hook now receives an object with `config`, `testPath` and `duration` ([#6350](https://github.com/facebook/jest/pull/6350))
* `[jest-each]` Support one dimensional array of data ([#6351](https://github.com/facebook/jest/pull/6351))
* `[jest-watch]` create new package `jest-watch` to ease custom watch plugin development ([#6318](https://github.com/facebook/jest/pull/6318))
Expand Down
2 changes: 2 additions & 0 deletions docs/GlobalAPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ Use `describe.each` if you keep duplicating the same test suites with different
* `%j` - JSON.
* `%o` - Object.
* `%%` - single percent sign ('%'). This does not consume an argument.
* `%p` - [pretty-format](https://www.npmjs.com/package/pretty-format).
* `fn`: `Function` the suite of tests to be ran, this is the function that will receive the parameters in each row as function arguments.

Example:
Expand Down Expand Up @@ -484,6 +485,7 @@ Use `test.each` if you keep duplicating the same test with different data. `test
* `%j` - JSON.
* `%o` - Object.
* `%%` - single percent sign ('%'). This does not consume an argument.
* `%p` - [pretty-format](https://www.npmjs.com/package/pretty-format).
* `fn`: `Function` the test to be ran, this is the function that will receive the parameters in each row as function arguments.

Example:
Expand Down
42 changes: 38 additions & 4 deletions e2e/__tests__/__snapshots__/each.test.js.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,39 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`formats args with pretty format when given %p 1`] = `
"PASS __tests__/pretty.test.js
array
✓ \\"hello\\" == \\"hello\\"
✓ 1 == 1
✓ null == null
✓ undefined == undefined
✓ 1.2 == 1.2
✓ {\\"foo\\": \\"bar\\"} == {\\"foo\\": \\"bar\\"}
✓ {\\"foo\\": [Object]} == {\\"foo\\": [Object]}
✓ [Function noop] == [Function noop]
✓ [] == []
✓ [[Object]] == [[Object]]
✓ Infinity == Infinity
✓ -Infinity == -Infinity
✓ NaN == NaN
template
✓ \\"hello\\" == \\"hello\\"
✓ 1 == 1
✓ null == null
✓ undefined == undefined
✓ 1.2 == 1.2
✓ {\\"foo\\": \\"bar\\"} == {\\"foo\\": \\"bar\\"}
✓ {\\"foo\\": [Object]} == {\\"foo\\": [Object]}
✓ [Function noop] == [Function noop]
✓ [] == []
✓ [[Object]] == [[Object]]
✓ Infinity == Infinity
✓ -Infinity == -Infinity
✓ NaN == NaN

"
`;

exports[`runs only the describe.only.each tests 1`] = `
"PASS __tests__/describe-only.test.js
passes all rows expected true == true
Expand Down Expand Up @@ -79,9 +113,9 @@ exports[`shows the correct errors in stderr when failing tests 1`] = `
✕ The word red contains the letter 'z'
✕ The word green contains the letter 'z'
✕ The word bean contains the letter 'z'
template table describe fails on all rows expected a == b
template table describe fails on all rows expected \\"a\\" == \\"b\\"
✕ fails
template table describe fails on all rows expected c == d
template table describe fails on all rows expected \\"c\\" == \\"d\\"
✕ fails
array table describe fails on all rows expected a == b
✕ fails
Expand Down Expand Up @@ -241,7 +275,7 @@ exports[`shows the correct errors in stderr when failing tests 1`] = `

at __tests__/failure.test.js:47:28

● template table describe fails on all rows expected a == b › fails
● template table describe fails on all rows expected \\"a\\" == \\"b\\" › fails

expect(received).toBe(expected) // Object.is equality

Expand All @@ -258,7 +292,7 @@ exports[`shows the correct errors in stderr when failing tests 1`] = `

at __tests__/failure.test.js:59:20

● template table describe fails on all rows expected c == d › fails
● template table describe fails on all rows expected \\"c\\" == \\"d\\" › fails

expect(received).toBe(expected) // Object.is equality

Expand Down
7 changes: 7 additions & 0 deletions e2e/__tests__/each.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,10 @@ test('runs only the describe.only.each tests', () => {
expect(rest).toMatchSnapshot();
expect(result.status).toBe(0);
});

test('formats args with pretty format when given %p', () => {
const result = runJest(dir, ['pretty.test.js']);
const {rest} = extractSummary(result.stderr);
expect(rest).toMatchSnapshot();
expect(result.status).toBe(0);
});
49 changes: 49 additions & 0 deletions e2e/each/__tests__/pretty.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* Copyright (c) 2018-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

const noop = () => {};

describe('array', () => {
it.each([
['hello', 'hello'],
[1, 1],
[null, null],
[undefined, undefined],
[1.2, 1.2],
[{foo: 'bar'}, {foo: 'bar'}],
[{foo: {bar: 'baz'}}, {foo: {bar: 'baz'}}],
[noop, noop],
[[], []],
[[{foo: {bar: 'baz'}}], [{foo: {bar: 'baz'}}]],
[Infinity, Infinity],
[-Infinity, -Infinity],
[NaN, NaN],
])('%p == %p', (left, right) => {
expect(left).toEqual(right);
});
});

describe('template', () => {
it.each`
left | right
${'hello'} | ${'hello'}
${1} | ${1}
${null} | ${null}
${undefined} | ${undefined}
${1.2} | ${1.2}
${{foo: 'bar'}} | ${{foo: 'bar'}}
${{foo: {bar: 'baz'}}} | ${{foo: {bar: 'baz'}}}
${noop} | ${noop}
${[]} | ${[]}
${[{foo: {bar: 'baz'}}]} | ${[{foo: {bar: 'baz'}}]}
${Infinity} | ${Infinity}
${-Infinity} | ${-Infinity}
${NaN} | ${NaN}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is so amazing! 😍

`('$left == $right', ({left, right}) => {
expect(left).toEqual(right);
});
});
3 changes: 3 additions & 0 deletions packages/jest-each/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ jest-each allows you to provide multiple arguments to your `test`/`describe` whi
* `%j` - JSON.
* `%o` - Object.
* `%%` - single percent sign ('%'). This does not consume an argument.
* `%p` - [pretty-format](https://www.npmjs.com/package/pretty-format).
* 🖖 Spock like data tables with [Tagged Template Literals](#tagged-template-literal-of-rows)

---
Expand Down Expand Up @@ -107,6 +108,7 @@ const each = require('jest-each');
* `%j` - JSON.
* `%o` - Object.
* `%%` - single percent sign ('%'). This does not consume an argument.
* `%p` - [pretty-format](https://www.npmjs.com/package/pretty-format).
* testFn: `Function` the test logic, this is the function that will receive the parameters of each row as function arguments

#### `each([parameters]).describe(name, suiteFn)`
Expand All @@ -126,6 +128,7 @@ const each = require('jest-each');
* `%j` - JSON.
* `%o` - Object.
* `%%` - single percent sign ('%'). This does not consume an argument.
* `%p` - [pretty-format](https://www.npmjs.com/package/pretty-format).
* suiteFn: `Function` the suite of `test`/`it`s to be ran, this is the function that will receive the parameters in each row as function arguments

### Usage
Expand Down
47 changes: 47 additions & 0 deletions packages/jest-each/src/__tests__/array.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*
*/

import pretty from 'pretty-format';
import each from '../';

const noop = () => {};
Expand Down Expand Up @@ -140,6 +141,52 @@ describe('jest-each', () => {
);
});

test('calls global test title with %p placeholder injected at the correct positions', () => {
const globalTestMocks = getGlobalTestMocks();
const eachObject = each.withGlobal(globalTestMocks)([
['string1', 'pretty1', 'string2', 'pretty2'],
['string1', 'pretty1', 'string2', 'pretty2'],
]);
const testFunction = get(eachObject, keyPath);
testFunction('expected string: %s %p %s %p', noop);

const globalMock = get(globalTestMocks, keyPath);
expect(globalMock).toHaveBeenCalledTimes(2);
expect(globalMock).toHaveBeenCalledWith(
`expected string: string1 ${pretty('pretty1')} string2 ${pretty(
'pretty2',
)}`,
expectFunction,
);
expect(globalMock).toHaveBeenCalledWith(
`expected string: string1 ${pretty('pretty1')} string2 ${pretty(
'pretty2',
)}`,
expectFunction,
);
});

test('does not calls global test title with %p placeholder when no data is supplied at given position', () => {
const globalTestMocks = getGlobalTestMocks();
const eachObject = each.withGlobal(globalTestMocks)([
['string1', 'pretty1', 'string2'],
['string1', 'pretty1', 'string2'],
]);
const testFunction = get(eachObject, keyPath);
testFunction('expected string: %s %p %s %p', noop);

const globalMock = get(globalTestMocks, keyPath);
expect(globalMock).toHaveBeenCalledTimes(2);
expect(globalMock).toHaveBeenCalledWith(
`expected string: string1 ${pretty('pretty1')} string2 %p`,
expectFunction,
);
expect(globalMock).toHaveBeenCalledWith(
`expected string: string1 ${pretty('pretty1')} string2 %p`,
expectFunction,
);
});

test('calls global with cb function containing all parameters of each test case when given 1d array', () => {
const globalTestMocks = getGlobalTestMocks();
const testCallBack = jest.fn();
Expand Down
48 changes: 43 additions & 5 deletions packages/jest-each/src/bind.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@ import chalk from 'chalk';
import pretty from 'pretty-format';

type Table = Array<Array<any>>;
type PrettyArgs = {
args: Array<mixed>,
title: string,
};

const EXPECTED_COLOR = chalk.green;
const RECEIVED_COLOR = chalk.red;
const SUPPORTED_PLACEHOLDERS = /%[sdifjoO%]/g;
const SUPPORTED_PLACEHOLDERS = /%[sdifjoOp%]/g;
const PRETTY_PLACEHOLDER = '%p';

export default (cb: Function) => (...args: any) =>
function eachBind(title: string, test: Function): void {
Expand Down Expand Up @@ -59,9 +64,41 @@ export default (cb: Function) => (...args: any) =>
);
};

const arrayFormat = (str, ...args) => {
const matches = (str.match(SUPPORTED_PLACEHOLDERS) || []).length;
return util.format(str, ...args.slice(0, matches));
const getPrettyIndexes = placeholders =>
placeholders.reduce(
(indexes, placeholder, index) =>
placeholder === PRETTY_PLACEHOLDER ? indexes.concat(index) : indexes,
[],
);

const arrayFormat = (title, ...args) => {
const placeholders = title.match(SUPPORTED_PLACEHOLDERS) || [];
const prettyIndexes = getPrettyIndexes(placeholders);

const {title: prettyTitle, args: remainingArgs} = args.reduce(
(acc: PrettyArgs, arg, index) => {
if (prettyIndexes.indexOf(index) !== -1) {
return {
args: acc.args,
title: acc.title.replace(
PRETTY_PLACEHOLDER,
pretty(arg, {maxDepth: 1, min: true}),
),
};
}

return {
args: acc.args.concat([arg]),
title: acc.title,
};
},
{args: [], title},
);

return util.format(
prettyTitle,
...remainingArgs.slice(0, placeholders.length - prettyIndexes.length),
);
};

const applyRestParams = (params: Array<any>, test: Function) => {
Expand Down Expand Up @@ -89,7 +126,8 @@ const buildTable = (

const interpolate = (title: string, data: any) =>
Object.keys(data).reduce(
(acc, key) => acc.replace('$' + key, data[key]),
(acc, key) =>
acc.replace('$' + key, pretty(data[key], {maxDepth: 1, min: true})),
title,
);

Expand Down