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

Fix pretty print for closeTo matcher #12626

Merged
merged 15 commits into from
Apr 14, 2022
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
- `[expect]` Move typings of `.not`, `.rejects` and `.resolves` modifiers outside of `Matchers` interface ([#12346](https://github.com/facebook/jest/pull/12346))
- `[expect]` Throw useful error if `expect.extend` is called with invalid matchers ([#12488](https://github.com/facebook/jest/pull/12488))
- `[expect]` Fix `iterableEquality` ignores other properties ([#8359](https://github.com/facebook/jest/pull/8359))
- `[expect]` Fix print for the `closeTo` matcher ([#12626](https://github.com/facebook/jest/pull/12626))
- `[jest-circus, @jest/types]` Disallow undefined value in `TestContext` type ([#12507](https://github.com/facebook/jest/pull/12507))
- `[jest-config]` Correctly detect CI environment and update snapshots accordingly ([#12378](https://github.com/facebook/jest/pull/12378))
- `[jest-config]` Pass `moduleTypes` to `ts-node` to enforce CJS when transpiling ([#12397](https://github.com/facebook/jest/pull/12397))
Expand Down
9 changes: 9 additions & 0 deletions packages/expect/src/asymmetricMatchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
subsetEquality,
} from '@jest/expect-utils';
import * as matcherUtils from 'jest-matcher-utils';
import {pluralize} from 'jest-util';
import {getState} from './jestMatchersObject';
import type {
AsymmetricMatcher as AsymmetricMatcherInterface,
Expand Down Expand Up @@ -329,6 +330,14 @@ class CloseTo extends AsymmetricMatcher<number> {
override getExpectedType() {
return 'number';
}

override toAsymmetricMatcher(): string {
return [
this.toString(),
this.sample,
`(${pluralize('digit', this.precision)})`,
].join(' ');
}
}

export const any = (expectedObject: unknown): Any => new Any(expectedObject);
Expand Down
1 change: 1 addition & 0 deletions packages/expect/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import type {
ThrowingMatcherFn,
} from './types';

export {AsymmetricMatcher} from './asymmetricMatchers';
export type {
AsymmetricMatchers,
BaseExpect,
Expand Down
1 change: 1 addition & 0 deletions packages/pretty-format/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@types/react": "*",
"@types/react-is": "^17.0.0",
"@types/react-test-renderer": "*",
"expect": "^28.0.0-alpha.8",
"immutable": "^4.0.0",
"jest-util": "^28.0.0-alpha.8",
"react": "*",
Expand Down
55 changes: 55 additions & 0 deletions packages/pretty-format/src/__tests__/AsymmetricMatcher.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/

import {AsymmetricMatcher as AbstractAsymmetricMatcher} from 'expect';
import prettyFormat, {plugins} from '../';
import type {OptionsReceived} from '../types';

Expand Down Expand Up @@ -131,6 +132,42 @@ test('stringNotMatching(string)', () => {
expect(result).toEqual('StringNotMatching /jest/');
});

test('closeTo(number, precision)', () => {
const result = prettyFormat(expect.closeTo(1.2345, 4), options);
expect(result).toEqual('NumberCloseTo 1.2345 (4 digits)');
});

test('notCloseTo(number, precision)', () => {
const result = prettyFormat(expect.not.closeTo(1.2345, 1), options);
expect(result).toEqual('NumberNotCloseTo 1.2345 (1 digit)');
});

test('closeTo(number)', () => {
const result = prettyFormat(expect.closeTo(1.2345), options);
expect(result).toEqual('NumberCloseTo 1.2345 (2 digits)');
});

test('closeTo(Infinity)', () => {
const result = prettyFormat(expect.closeTo(-Infinity), options);
expect(result).toEqual('NumberCloseTo -Infinity (2 digits)');
});

test('closeTo(scientific number)', () => {
const result = prettyFormat(expect.closeTo(1.56e-3, 4), options);
expect(result).toEqual('NumberCloseTo 0.00156 (4 digits)');
});

test('closeTo(very small scientific number)', () => {
const result = prettyFormat(expect.closeTo(1.56e-10, 4), options);
expect(result).toEqual('NumberCloseTo 1.56e-10 (4 digits)');
});

test('correctly handles inability to pretty-print matcher', () => {
expect(() => prettyFormat(new DummyMatcher(1), options)).toThrow(
'Asymmetric matcher DummyMatcher does not implement toAsymmetricMatcher()',
);
});

test('supports multiple nested asymmetric matchers', () => {
const result = prettyFormat(
{
Expand Down Expand Up @@ -311,3 +348,21 @@ test('min option', () => {
'{"test": {"nested": ObjectContaining {"a": ArrayContaining [1], "b": Anything, "c": Any<String>, "d": StringContaining "jest", "e": StringMatching /jest/, "f": ObjectContaining {"test": "case"}}}}',
);
});

class DummyMatcher extends AbstractAsymmetricMatcher<number> {
constructor(sample: number) {
super(sample);
}

asymmetricMatch(other: number) {
return this.sample === other;
}

toString() {
return 'DummyMatcher';
}

override getExpectedType() {
return 'number';
}
}
6 changes: 6 additions & 0 deletions packages/pretty-format/src/plugins/AsymmetricMatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ export const serialize: NewPlugin['serialize'] = (
);
}

if (typeof val.toAsymmetricMatcher !== 'function') {
throw new Error(
`Asymmetric matcher ${val.constructor.name} does not implement toAsymmetricMatcher()`,
SimenB marked this conversation as resolved.
Show resolved Hide resolved
);
}

return val.toAsymmetricMatcher();
};

Expand Down
1 change: 1 addition & 0 deletions packages/pretty-format/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
},
"include": ["./src/**/*"],
"exclude": ["./**/__tests__/**/*"],
// no `expect`, only used in tests
"references": [{"path": "../jest-schemas"}, {"path": "../jest-util"}]
}
9 changes: 8 additions & 1 deletion scripts/buildTs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ import {getPackages} from './buildUtils.mjs';
}
}

// dev dep
if (pkg.name === 'pretty-format') {
if (dep === 'expect') {
return false;
}
}

return true;
})
.map(dep =>
Expand All @@ -84,7 +91,7 @@ import {getPackages} from './buildUtils.mjs';
assert.deepStrictEqual(
references,
jestDependenciesOfPackage,
`Expected declared references to match dependencies in packages ${
`Expected declared references to match dependencies in package ${
pkg.name
}. Got:\n\n${references.join(
'\n',
Expand Down
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -17974,6 +17974,7 @@ __metadata:
"@types/react-test-renderer": "*"
ansi-regex: ^5.0.1
ansi-styles: ^5.0.0
expect: ^28.0.0-alpha.8
immutable: ^4.0.0
jest-util: ^28.0.0-alpha.8
react: "*"
Expand Down