From 116303baf711df37304986897582eb8078aa24d8 Mon Sep 17 00:00:00 2001 From: Josh Rosenstein Date: Fri, 6 Sep 2019 09:41:05 -0500 Subject: [PATCH] feat: BigInt Support (#8382) * feat(jest-get-type) include bigint * (jest-matcher-utils) add ensureActualIsNumberOrBigInit Originally planned on just including biginit in ensurenumbers, but the only built in matcher that cant accept biginit would be toBeCloseTo * (expect) add biginit support * Revert gettype to ifelse * Add tests mixing numbers and bigint * bigDumbTypo * babel-plugin-jest-hoist add to whitelist * Wrap BigInt Tests with guard * temp fix test suite must contain at least one test * remove MatchingSnapshot tests due to unsupported env having obsoletes * remove unnecessary bigint check in equals * Remove ensure*NumbersOrBigInt, include BigInt in originals, add `or bigint` to errors * Update snapshots with new error message `number or bigint` * add bigint support to toBeCloseTo, replace ensureNumbersOrBigInt to ensureNumbers * tests(expect) remove temp_test, add bigint tests to matchers.test * tests(jest-matcher-utils) remove temp_tests, add bigint tests to index,test * Update CHANGELOG.md * Update CHANGELOG.md * (babel-preset-jest)-add @babel/plugin-syntax-bigint * Add tests for bigint syntax * Update CHANGELOG.md * :rage2: :abc: * Checking if node6 breaks with lookup * Revert "Checking if node6 breaks with lookup" This reverts commit 592d6f3d815a7038d7d30e0f705463598ec79c32. * Revert "Add tests for bigint syntax" This reverts commit be5cbb615fd0e3ccb76ac11edd972b8c09abbf4a. * Update CHANGELOG.md Co-Authored-By: JoshRosenstein * Remove Default Precision Value and update error msg via code review https://github.com/facebook/jest/pull/8382#discussion_r279918058 https://github.com/facebook/jest/pull/8382#discussion_r279919941 * Replace ensureExpectedIsNumber with ensureExpectedIsNonNegativeInteger for spy matchers https://github.com/facebook/jest/pull/8382/files/fb6655d33f5efac890b77f199bd5b3f7684b1421#r280160170 * Update Matchers interface * Revert Bigint Support for 'toBeCloseTo' * Update CHANGELOG.md * pedrottimark's CR fixes in tests :eyes: :dart: * Update CHANGELOG.md * move changelog entries * fix changelog formatting * Delete unnecessary disable of eslint rules * Update ExpectAPI.md * Update CHANGELOG.md * Edit ExpectAPI.md --- CHANGELOG.md | 9 +- docs/ExpectAPI.md | 28 +- packages/babel-plugin-jest-hoist/src/index.ts | 1 + packages/babel-preset-jest/index.js | 1 + packages/babel-preset-jest/package.json | 1 + .../__snapshots__/spyMatchers.test.js.snap | 96 +++---- .../expect/src/__tests__/matchers.test.js | 243 ++++++++++++++++++ packages/expect/src/matchers.ts | 43 +++- packages/expect/src/spyMatchers.ts | 6 +- packages/expect/src/types.ts | 8 +- .../src/__tests__/getType.test.ts | 4 + .../src/__tests__/isPrimitive.test.ts | 2 + packages/jest-get-type/src/index.ts | 3 + .../__snapshots__/index.test.ts.snap | 16 +- .../src/__tests__/index.test.ts | 18 ++ packages/jest-matcher-utils/src/index.ts | 20 +- yarn.lock | 7 + 17 files changed, 419 insertions(+), 87 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f83f547771a8..574f3c5ac7e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,12 +3,17 @@ ### Features - `[babel-plugin-jest-hoist]` Show codeframe on static hoisting issues ([#8865](https://github.com/facebook/jest/pull/8865)) +- `[babel-plugin-jest-hoist]` Add `BigInt` to `WHITELISTED_IDENTIFIERS` ([#8382](https://github.com/facebook/jest/pull/8382)) +- `[babel-preset-jest]` Add `@babel/plugin-syntax-bigint` ([#8382](https://github.com/facebook/jest/pull/8382)) +- `[expect]` Add `BigInt` support to `toBeGreaterThan`, `toBeGreaterThanOrEqual`, `toBeLessThan` and `toBeLessThanOrEqual` ([#8382](https://github.com/facebook/jest/pull/8382)) - `[jest-config]` [**BREAKING**] Set default display name color based on runner ([#8689](https://github.com/facebook/jest/pull/8689)) - `[jest-diff]` Add options for colors and symbols ([#8841](https://github.com/facebook/jest/pull/8841)) - `[jest-diff]` [**BREAKING**] Export as ECMAScript module ([#8873](https://github.com/facebook/jest/pull/8873)) - `[jest-diff]` Add `includeChangeCounts` and rename `Indicator` options ([#8881](https://github.com/facebook/jest/pull/8881)) - `[jest-diff]` Add `changeColor` and `patchColor` options ([#8911](https://github.com/facebook/jest/pull/8911)) - `[@jest/fake-timers]` Add Lolex as implementation of fake timers ([#8897](https://github.com/facebook/jest/pull/8897)) +- `[jest-get-type]` Add `BigInt` support. ([#8382](https://github.com/facebook/jest/pull/8382)) +- `[jest-matcher-utils]` Add `BigInt` support to `ensureNumbers` `ensureActualIsNumber`, `ensureExpectedIsNumber` ([#8382](https://github.com/facebook/jest/pull/8382)) - `[jest-runner]` Warn if a worker had to be force exited ([#8206](https://github.com/facebook/jest/pull/8206)) - `[@jest/test-result]` Create method to create empty `TestResult` ([#8867](https://github.com/facebook/jest/pull/8867)) - `[jest-worker]` [**BREAKING**] Return a promise from `end()`, resolving with the information whether workers exited gracefully ([#8206](https://github.com/facebook/jest/pull/8206)) @@ -447,7 +452,7 @@ We skipped 24.2.0 because a draft was accidentally published. Please use `24.3.0 - `[jest-haste-map]` Add `getCacheFilePath` to get the path to the cache file for a `HasteMap` instance ([#7217](https://github.com/facebook/jest/pull/7217)) - `[jest-runtime]` Remove `cacheDirectory` from `ignorePattern` for `HasteMap` if not necessary ([#7166](https://github.com/facebook/jest/pull/7166)) - `[jest-validate]` Add syntax to validate multiple permitted types ([#7207](https://github.com/facebook/jest/pull/7207)) -- `[jest-config]` Accept an array as as well as a string for `testRegex`([#7209]https://github.com/facebook/jest/pull/7209)) +- `[jest-config]` Accept an array as as well as a string for `testRegex` ([#7209]https://github.com/facebook/jest/pull/7209)) - `[expect/jest-matcher-utils]` Improve report when assertion fails, part 4 ([#7241](https://github.com/facebook/jest/pull/7241)) - `[expect/jest-matcher-utils]` Improve report when assertion fails, part 5 ([#7557](https://github.com/facebook/jest/pull/7557)) - `[expect]` Check constructor equality in .toStrictEqual() ([#7005](https://github.com/facebook/jest/pull/7005)) @@ -496,7 +501,7 @@ We skipped 24.2.0 because a draft was accidentally published. Please use `24.3.0 - `[jest-jasmine2]` Better error message when a describe block is empty ([#6372](https://github.com/facebook/jest/pull/6372)) - `[jest-jasmine2]` Pending calls inside async tests are reported as pending not failed ([#6782](https://github.com/facebook/jest/pull/6782)) - `[jest-circus]` Better error message when a describe block is empty ([#6372](https://github.com/facebook/jest/pull/6372)) -- `[jest-jasmine2]` Add missing testLocationResults for `xit` and `fit`([#6482](https://github.com/facebook/jest/pull/6482)) +- `[jest-jasmine2]` Add missing testLocationResults for `xit` and `fit` ([#6482](https://github.com/facebook/jest/pull/6482)) - `[expect]` Return false from asymmetric matchers if received value isn’t string ([#7107](https://github.com/facebook/jest/pull/7107)) - `[jest-cli]` Fix unhandled error when a bad revision is provided to `changedSince` ([#7115](https://github.com/facebook/jest/pull/7115)) - `[jest-config]` Moved dynamically assigned `cwd` from `jest-cli` to default configuration in `jest-config` ([#7146](https://github.com/facebook/jest/pull/7146)) diff --git a/docs/ExpectAPI.md b/docs/ExpectAPI.md index 1fcdb23cf921..2dcb31acd31c 100644 --- a/docs/ExpectAPI.md +++ b/docs/ExpectAPI.md @@ -831,7 +831,11 @@ test('this house has my desired features', () => { ### `.toBeCloseTo(number, numDigits?)` -Using exact equality with floating point numbers is a bad idea. Rounding means that intuitive things fail. For example, this test fails: +Use `toBeCloseTo` to compare floating point numbers for approximate equality. + +The optional `numDigits` argument limits the number of digits to check **after** the decimal point. For the default value `2`, the test criterion is `Math.abs(expected - received) < 0.005` (that is, `10 ** -2 / 2`). + +Intuitive equality comparisons often fail, because arithmetic on decimal (base 10) values often have rounding errors in limited precision binary (base 2) representation. For example, this test fails: ```js test('adding works sanely with simple decimals', () => { @@ -839,9 +843,9 @@ test('adding works sanely with simple decimals', () => { }); ``` -It fails because in JavaScript, `0.2 + 0.1` is actually `0.30000000000000004`. Sorry. +It fails because in JavaScript, `0.2 + 0.1` is actually `0.30000000000000004`. -Instead, use `.toBeCloseTo`. Use `numDigits` to control how many digits after the decimal point to check. For example, if you want to be sure that `0.2 + 0.1` is equal to `0.3` with a precision of 5 decimal digits, you can use this test: +For example, this test passes with a precision of 5 digits: ```js test('adding works sanely with simple decimals', () => { @@ -849,7 +853,7 @@ test('adding works sanely with simple decimals', () => { }); ``` -The optional `numDigits` argument has default value `2` which means the criterion is `Math.abs(expected - received) < 0.005` (that is, `10 ** -2 / 2`). +Because floating point errors are the problem that `toBeCloseTo` solves, it does not support big integer values. ### `.toBeDefined()` @@ -885,9 +889,9 @@ test('drinking La Croix does not lead to errors', () => { In JavaScript, there are six falsy values: `false`, `0`, `''`, `null`, `undefined`, and `NaN`. Everything else is truthy. -### `.toBeGreaterThan(number)` +### `.toBeGreaterThan(number | bigint)` -To compare floating point numbers, you can use `toBeGreaterThan`. For example, if you want to test that `ouncesPerCan()` returns a value of more than 10 ounces, write: +Use `toBeGreaterThan` to compare `received > expected` for number or big integer values. For example, test that `ouncesPerCan()` returns a value of more than 10 ounces: ```js test('ounces per can is more than 10', () => { @@ -895,9 +899,9 @@ test('ounces per can is more than 10', () => { }); ``` -### `.toBeGreaterThanOrEqual(number)` +### `.toBeGreaterThanOrEqual(number | bigint)` -To compare floating point numbers, you can use `toBeGreaterThanOrEqual`. For example, if you want to test that `ouncesPerCan()` returns a value of at least 12 ounces, write: +Use `toBeGreaterThanOrEqual` to compare `received >= expected` for number or big integer values. For example, test that `ouncesPerCan()` returns a value of at least 12 ounces: ```js test('ounces per can is at least 12', () => { @@ -905,9 +909,9 @@ test('ounces per can is at least 12', () => { }); ``` -### `.toBeLessThan(number)` +### `.toBeLessThan(number | bigint)` -To compare floating point numbers, you can use `toBeLessThan`. For example, if you want to test that `ouncesPerCan()` returns a value of less than 20 ounces, write: +Use `toBeLessThan` to compare `received < expected` for number or big integer values. For example, test that `ouncesPerCan()` returns a value of less than 20 ounces: ```js test('ounces per can is less than 20', () => { @@ -915,9 +919,9 @@ test('ounces per can is less than 20', () => { }); ``` -### `.toBeLessThanOrEqual(number)` +### `.toBeLessThanOrEqual(number | bigint)` -To compare floating point numbers, you can use `toBeLessThanOrEqual`. For example, if you want to test that `ouncesPerCan()` returns a value of at most 12 ounces, write: +Use `toBeLessThanOrEqual` to compare `received <= expected` for number or big integer values. For example, test that `ouncesPerCan()` returns a value of at most 12 ounces: ```js test('ounces per can is at most 12', () => { diff --git a/packages/babel-plugin-jest-hoist/src/index.ts b/packages/babel-plugin-jest-hoist/src/index.ts index 5a5d51b99fc3..e949ed6e0b9f 100644 --- a/packages/babel-plugin-jest-hoist/src/index.ts +++ b/packages/babel-plugin-jest-hoist/src/index.ts @@ -20,6 +20,7 @@ const WHITELISTED_IDENTIFIERS = new Set( 'Array', 'ArrayBuffer', 'Boolean', + 'BigInt', 'DataView', 'Date', 'Error', diff --git a/packages/babel-preset-jest/index.js b/packages/babel-preset-jest/index.js index 3a6e9a89cba6..c8c235eae479 100644 --- a/packages/babel-preset-jest/index.js +++ b/packages/babel-preset-jest/index.js @@ -9,5 +9,6 @@ module.exports = () => ({ plugins: [ require.resolve('babel-plugin-jest-hoist'), require.resolve('@babel/plugin-syntax-object-rest-spread'), + require.resolve('@babel/plugin-syntax-bigint'), ], }); diff --git a/packages/babel-preset-jest/package.json b/packages/babel-preset-jest/package.json index a3f9869f12ad..797cc217f86b 100644 --- a/packages/babel-preset-jest/package.json +++ b/packages/babel-preset-jest/package.json @@ -9,6 +9,7 @@ "license": "MIT", "main": "index.js", "dependencies": { + "@babel/plugin-syntax-bigint": "^7.0.0", "@babel/plugin-syntax-object-rest-spread": "^7.0.0", "babel-plugin-jest-hoist": "^24.9.0" }, diff --git a/packages/expect/src/__tests__/__snapshots__/spyMatchers.test.js.snap b/packages/expect/src/__tests__/__snapshots__/spyMatchers.test.js.snap index 1bf242fcd488..5236afb7b8d8 100644 --- a/packages/expect/src/__tests__/__snapshots__/spyMatchers.test.js.snap +++ b/packages/expect/src/__tests__/__snapshots__/spyMatchers.test.js.snap @@ -743,7 +743,7 @@ Received has value: [Function fn]" exports[`toBeCalledTimes .not only accepts a number argument 1`] = ` "expect(received).not.toBeCalledTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: object Expected has value: {}" @@ -752,7 +752,7 @@ Expected has value: {}" exports[`toBeCalledTimes .not only accepts a number argument 2`] = ` "expect(received).not.toBeCalledTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: array Expected has value: []" @@ -761,7 +761,7 @@ Expected has value: []" exports[`toBeCalledTimes .not only accepts a number argument 3`] = ` "expect(received).not.toBeCalledTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: boolean Expected has value: true" @@ -770,7 +770,7 @@ Expected has value: true" exports[`toBeCalledTimes .not only accepts a number argument 4`] = ` "expect(received).not.toBeCalledTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: string Expected has value: \\"a\\"" @@ -779,7 +779,7 @@ Expected has value: \\"a\\"" exports[`toBeCalledTimes .not only accepts a number argument 5`] = ` "expect(received).not.toBeCalledTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: map Expected has value: Map {}" @@ -788,7 +788,7 @@ Expected has value: Map {}" exports[`toBeCalledTimes .not only accepts a number argument 6`] = ` "expect(received).not.toBeCalledTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: function Expected has value: [Function anonymous]" @@ -827,7 +827,7 @@ Received number of calls: 1" exports[`toBeCalledTimes only accepts a number argument 1`] = ` "expect(received).toBeCalledTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: object Expected has value: {}" @@ -836,7 +836,7 @@ Expected has value: {}" exports[`toBeCalledTimes only accepts a number argument 2`] = ` "expect(received).toBeCalledTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: array Expected has value: []" @@ -845,7 +845,7 @@ Expected has value: []" exports[`toBeCalledTimes only accepts a number argument 3`] = ` "expect(received).toBeCalledTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: boolean Expected has value: true" @@ -854,7 +854,7 @@ Expected has value: true" exports[`toBeCalledTimes only accepts a number argument 4`] = ` "expect(received).toBeCalledTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: string Expected has value: \\"a\\"" @@ -863,7 +863,7 @@ Expected has value: \\"a\\"" exports[`toBeCalledTimes only accepts a number argument 5`] = ` "expect(received).toBeCalledTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: map Expected has value: Map {}" @@ -872,7 +872,7 @@ Expected has value: Map {}" exports[`toBeCalledTimes only accepts a number argument 6`] = ` "expect(received).toBeCalledTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: function Expected has value: [Function anonymous]" @@ -1068,7 +1068,7 @@ Received has value: [Function fn]" exports[`toHaveBeenCalledTimes .not only accepts a number argument 1`] = ` "expect(received).not.toHaveBeenCalledTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: object Expected has value: {}" @@ -1077,7 +1077,7 @@ Expected has value: {}" exports[`toHaveBeenCalledTimes .not only accepts a number argument 2`] = ` "expect(received).not.toHaveBeenCalledTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: array Expected has value: []" @@ -1086,7 +1086,7 @@ Expected has value: []" exports[`toHaveBeenCalledTimes .not only accepts a number argument 3`] = ` "expect(received).not.toHaveBeenCalledTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: boolean Expected has value: true" @@ -1095,7 +1095,7 @@ Expected has value: true" exports[`toHaveBeenCalledTimes .not only accepts a number argument 4`] = ` "expect(received).not.toHaveBeenCalledTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: string Expected has value: \\"a\\"" @@ -1104,7 +1104,7 @@ Expected has value: \\"a\\"" exports[`toHaveBeenCalledTimes .not only accepts a number argument 5`] = ` "expect(received).not.toHaveBeenCalledTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: map Expected has value: Map {}" @@ -1113,7 +1113,7 @@ Expected has value: Map {}" exports[`toHaveBeenCalledTimes .not only accepts a number argument 6`] = ` "expect(received).not.toHaveBeenCalledTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: function Expected has value: [Function anonymous]" @@ -1152,7 +1152,7 @@ Received number of calls: 1" exports[`toHaveBeenCalledTimes only accepts a number argument 1`] = ` "expect(received).toHaveBeenCalledTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: object Expected has value: {}" @@ -1161,7 +1161,7 @@ Expected has value: {}" exports[`toHaveBeenCalledTimes only accepts a number argument 2`] = ` "expect(received).toHaveBeenCalledTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: array Expected has value: []" @@ -1170,7 +1170,7 @@ Expected has value: []" exports[`toHaveBeenCalledTimes only accepts a number argument 3`] = ` "expect(received).toHaveBeenCalledTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: boolean Expected has value: true" @@ -1179,7 +1179,7 @@ Expected has value: true" exports[`toHaveBeenCalledTimes only accepts a number argument 4`] = ` "expect(received).toHaveBeenCalledTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: string Expected has value: \\"a\\"" @@ -1188,7 +1188,7 @@ Expected has value: \\"a\\"" exports[`toHaveBeenCalledTimes only accepts a number argument 5`] = ` "expect(received).toHaveBeenCalledTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: map Expected has value: Map {}" @@ -1197,7 +1197,7 @@ Expected has value: Map {}" exports[`toHaveBeenCalledTimes only accepts a number argument 6`] = ` "expect(received).toHaveBeenCalledTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: function Expected has value: [Function anonymous]" @@ -2135,7 +2135,7 @@ Received has value: [Function spy]" exports[`toHaveReturnedTimes .not only accepts a number argument 1`] = ` "expect(received).not.toHaveReturnedTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: object Expected has value: {}" @@ -2144,7 +2144,7 @@ Expected has value: {}" exports[`toHaveReturnedTimes .not only accepts a number argument 2`] = ` "expect(received).not.toHaveReturnedTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: array Expected has value: []" @@ -2153,7 +2153,7 @@ Expected has value: []" exports[`toHaveReturnedTimes .not only accepts a number argument 3`] = ` "expect(received).not.toHaveReturnedTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: boolean Expected has value: true" @@ -2162,7 +2162,7 @@ Expected has value: true" exports[`toHaveReturnedTimes .not only accepts a number argument 4`] = ` "expect(received).not.toHaveReturnedTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: string Expected has value: \\"a\\"" @@ -2171,7 +2171,7 @@ Expected has value: \\"a\\"" exports[`toHaveReturnedTimes .not only accepts a number argument 5`] = ` "expect(received).not.toHaveReturnedTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: map Expected has value: Map {}" @@ -2180,7 +2180,7 @@ Expected has value: Map {}" exports[`toHaveReturnedTimes .not only accepts a number argument 6`] = ` "expect(received).not.toHaveReturnedTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: function Expected has value: [Function anonymous]" @@ -2240,7 +2240,7 @@ Received number of calls: 4" exports[`toHaveReturnedTimes only accepts a number argument 1`] = ` "expect(received).toHaveReturnedTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: object Expected has value: {}" @@ -2249,7 +2249,7 @@ Expected has value: {}" exports[`toHaveReturnedTimes only accepts a number argument 2`] = ` "expect(received).toHaveReturnedTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: array Expected has value: []" @@ -2258,7 +2258,7 @@ Expected has value: []" exports[`toHaveReturnedTimes only accepts a number argument 3`] = ` "expect(received).toHaveReturnedTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: boolean Expected has value: true" @@ -2267,7 +2267,7 @@ Expected has value: true" exports[`toHaveReturnedTimes only accepts a number argument 4`] = ` "expect(received).toHaveReturnedTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: string Expected has value: \\"a\\"" @@ -2276,7 +2276,7 @@ Expected has value: \\"a\\"" exports[`toHaveReturnedTimes only accepts a number argument 5`] = ` "expect(received).toHaveReturnedTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: map Expected has value: Map {}" @@ -2285,7 +2285,7 @@ Expected has value: Map {}" exports[`toHaveReturnedTimes only accepts a number argument 6`] = ` "expect(received).toHaveReturnedTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: function Expected has value: [Function anonymous]" @@ -2560,7 +2560,7 @@ Received has value: [Function spy]" exports[`toReturnTimes .not only accepts a number argument 1`] = ` "expect(received).not.toReturnTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: object Expected has value: {}" @@ -2569,7 +2569,7 @@ Expected has value: {}" exports[`toReturnTimes .not only accepts a number argument 2`] = ` "expect(received).not.toReturnTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: array Expected has value: []" @@ -2578,7 +2578,7 @@ Expected has value: []" exports[`toReturnTimes .not only accepts a number argument 3`] = ` "expect(received).not.toReturnTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: boolean Expected has value: true" @@ -2587,7 +2587,7 @@ Expected has value: true" exports[`toReturnTimes .not only accepts a number argument 4`] = ` "expect(received).not.toReturnTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: string Expected has value: \\"a\\"" @@ -2596,7 +2596,7 @@ Expected has value: \\"a\\"" exports[`toReturnTimes .not only accepts a number argument 5`] = ` "expect(received).not.toReturnTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: map Expected has value: Map {}" @@ -2605,7 +2605,7 @@ Expected has value: Map {}" exports[`toReturnTimes .not only accepts a number argument 6`] = ` "expect(received).not.toReturnTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: function Expected has value: [Function anonymous]" @@ -2665,7 +2665,7 @@ Received number of calls: 4" exports[`toReturnTimes only accepts a number argument 1`] = ` "expect(received).toReturnTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: object Expected has value: {}" @@ -2674,7 +2674,7 @@ Expected has value: {}" exports[`toReturnTimes only accepts a number argument 2`] = ` "expect(received).toReturnTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: array Expected has value: []" @@ -2683,7 +2683,7 @@ Expected has value: []" exports[`toReturnTimes only accepts a number argument 3`] = ` "expect(received).toReturnTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: boolean Expected has value: true" @@ -2692,7 +2692,7 @@ Expected has value: true" exports[`toReturnTimes only accepts a number argument 4`] = ` "expect(received).toReturnTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: string Expected has value: \\"a\\"" @@ -2701,7 +2701,7 @@ Expected has value: \\"a\\"" exports[`toReturnTimes only accepts a number argument 5`] = ` "expect(received).toReturnTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: map Expected has value: Map {}" @@ -2710,7 +2710,7 @@ Expected has value: Map {}" exports[`toReturnTimes only accepts a number argument 6`] = ` "expect(received).toReturnTimes(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: function Expected has value: [Function anonymous]" diff --git a/packages/expect/src/__tests__/matchers.test.js b/packages/expect/src/__tests__/matchers.test.js index a4cbb0800e9b..487cf9cc8fd8 100644 --- a/packages/expect/src/__tests__/matchers.test.js +++ b/packages/expect/src/__tests__/matchers.test.js @@ -19,6 +19,9 @@ afterAll(() => { chalk.enabled = chalkEnabled; }); +/* global BigInt */ +const isBigIntDefined = typeof BigInt === 'function'; + it('should throw if passed two arguments', () => { expect(() => jestExpect('foo', 'bar')).toThrow( new Error('Expect takes at most one argument.'), @@ -197,6 +200,11 @@ describe('.toBe()', () => { jestExpect(null).toBe(null); jestExpect(undefined).toBe(undefined); jestExpect(NaN).toBe(NaN); + if (isBigIntDefined) { + jestExpect(BigInt(1)).not.toBe(BigInt(2)); + jestExpect(BigInt(1)).not.toBe(1); + jestExpect(BigInt(1)).toBe(BigInt(1)); + } }); [ @@ -227,12 +235,30 @@ describe('.toBe()', () => { }); }); + if (isBigIntDefined) { + [[BigInt(1), BigInt(2)], [{a: BigInt(1)}, {a: BigInt(1)}]].forEach( + ([a, b]) => { + it(`fails for: ${stringify(a)} and ${stringify(b)}`, () => { + expect(() => jestExpect(a).toBe(b)).toThrowError('toBe'); + }); + }, + ); + } + [false, 1, 'a', undefined, null, {}, []].forEach(v => { it(`fails for '${stringify(v)}' with '.not'`, () => { expect(() => jestExpect(v).not.toBe(v)).toThrowErrorMatchingSnapshot(); }); }); + if (isBigIntDefined) { + [BigInt(1), BigInt('1')].forEach(v => { + it(`fails for '${stringify(v)}' with '.not'`, () => { + expect(() => jestExpect(v).not.toBe(v)).toThrowError('toBe'); + }); + }); + } + it('does not crash on circular references', () => { const obj = {}; obj.circular = obj; @@ -488,6 +514,17 @@ describe('.toEqual()', () => { }); }); + if (isBigIntDefined) { + [[BigInt(1), BigInt(2)], [BigInt(1), 1]].forEach(([a, b]) => { + test(`{pass: false} expect(${stringify(a)}).toEqual(${stringify( + b, + )})`, () => { + expect(() => jestExpect(a).toEqual(b)).toThrowError('toEqual'); + jestExpect(a).not.toEqual(b); + }); + }); + } + [ [true, true], [1, 1], @@ -619,6 +656,25 @@ describe('.toEqual()', () => { }); }); + if (isBigIntDefined) { + [ + [BigInt(1), BigInt(1)], + [BigInt(0), BigInt('0')], + [[BigInt(1)], [BigInt(1)]], + [[BigInt(1), 2], [BigInt(1), 2]], + [Immutable.List([BigInt(1)]), Immutable.List([BigInt(1)])], + [{a: BigInt(99)}, {a: BigInt(99)}], + [new Set([BigInt(1), BigInt(2)]), new Set([BigInt(1), BigInt(2)])], + ].forEach(([a, b]) => { + test(`{pass: true} expect(${stringify(a)}).not.toEqual(${stringify( + b, + )})`, () => { + jestExpect(a).toEqual(b); + expect(() => jestExpect(a).not.toEqual(b)).toThrowError('toEqual'); + }); + }); + } + test('assertion error matcherResult property contains matcher name, expected and actual values', () => { const actual = {a: 1}; const expected = {a: 2}; @@ -821,6 +877,19 @@ describe('.toBeTruthy(), .toBeFalsy()', () => { }); }); + if (isBigIntDefined) { + [BigInt(1)].forEach(v => { + test(`'${stringify(v)}' is truthy`, () => { + jestExpect(v).toBeTruthy(); + jestExpect(v).not.toBeFalsy(); + + expect(() => jestExpect(v).not.toBeTruthy()).toThrowError('toBeTruthy'); + + expect(() => jestExpect(v).toBeFalsy()).toThrowError('toBeFalsy'); + }); + }); + } + [false, null, NaN, 0, '', undefined].forEach(v => { test(`'${stringify(v)}' is falsy`, () => { jestExpect(v).toBeFalsy(); @@ -833,6 +902,19 @@ describe('.toBeTruthy(), .toBeFalsy()', () => { ).toThrowErrorMatchingSnapshot(); }); }); + + if (isBigIntDefined) { + [BigInt(0)].forEach(v => { + test(`'${stringify(v)}' is falsy`, () => { + jestExpect(v).toBeFalsy(); + jestExpect(v).not.toBeTruthy(); + + expect(() => jestExpect(v).toBeTruthy()).toThrowError('toBeTruthy'); + + expect(() => jestExpect(v).not.toBeFalsy()).toThrowError('toBeFalsy'); + }); + }); + } }); describe('.toBeNaN()', () => { @@ -889,6 +971,23 @@ describe('.toBeDefined(), .toBeUndefined()', () => { }); }); + if (isBigIntDefined) { + [BigInt(1)].forEach(v => { + test(`'${stringify(v)}' is defined`, () => { + jestExpect(v).toBeDefined(); + jestExpect(v).not.toBeUndefined(); + + expect(() => jestExpect(v).not.toBeDefined()).toThrowError( + 'toBeDefined', + ); + + expect(() => jestExpect(v).toBeUndefined()).toThrowError( + 'toBeUndefined', + ); + }); + }); + } + test('undefined is undefined', () => { jestExpect(undefined).toBeUndefined(); jestExpect(undefined).not.toBeDefined(); @@ -983,6 +1082,105 @@ describe( }); }); + if (isBigIntDefined) { + test('can compare BigInt to Numbers', () => { + const a = BigInt(2); + jestExpect(a).toBeGreaterThan(1); + jestExpect(a).toBeGreaterThanOrEqual(2); + jestExpect(2).toBeLessThanOrEqual(a); + jestExpect(a).toBeLessThan(3); + jestExpect(a).toBeLessThanOrEqual(2); + }); + + [ + [BigInt(1), BigInt(2)], + [BigInt(0x11), BigInt(0x22)], + [-1, BigInt(2)], + ].forEach(([small, big]) => { + it(`{pass: true} expect(${stringify(small)}).toBeLessThan(${stringify( + big, + )})`, () => { + jestExpect(small).toBeLessThan(big); + }); + + it(`{pass: false} expect(${stringify(big)}).toBeLessThan(${stringify( + small, + )})`, () => { + jestExpect(big).not.toBeLessThan(small); + }); + + it(`{pass: true} expect(${stringify(big)}).toBeGreaterThan(${stringify( + small, + )})`, () => { + jestExpect(big).toBeGreaterThan(small); + }); + + it(`{pass: false} expect(${stringify( + small, + )}).toBeGreaterThan(${stringify(big)})`, () => { + jestExpect(small).not.toBeGreaterThan(big); + }); + + it(`{pass: true} expect(${stringify( + small, + )}).toBeLessThanOrEqual(${stringify(big)})`, () => { + jestExpect(small).toBeLessThanOrEqual(big); + }); + + it(`{pass: false} expect(${stringify( + big, + )}).toBeLessThanOrEqual(${stringify(small)})`, () => { + jestExpect(big).not.toBeLessThanOrEqual(small); + }); + + it(`{pass: true} expect(${stringify( + big, + )}).toBeGreaterThanOrEqual(${stringify(small)})`, () => { + jestExpect(big).toBeGreaterThanOrEqual(small); + }); + + it(`{pass: false} expect(${stringify( + small, + )}).toBeGreaterThanOrEqual(${stringify(big)})`, () => { + jestExpect(small).not.toBeGreaterThanOrEqual(big); + }); + + it(`throws: [${stringify(small)}, ${stringify(big)}]`, () => { + expect(() => jestExpect(small).toBeGreaterThan(big)).toThrowError( + 'toBeGreaterThan', + ); + + expect(() => jestExpect(small).not.toBeLessThan(big)).toThrowError( + 'toBeLessThan', + ); + + expect(() => jestExpect(big).not.toBeGreaterThan(small)).toThrowError( + 'toBeGreaterThan', + ); + + expect(() => jestExpect(big).toBeLessThan(small)).toThrowError( + 'toBeLessThan', + ); + + expect(() => + jestExpect(small).toBeGreaterThanOrEqual(big), + ).toThrowError('toBeGreaterThanOrEqual'); + + expect(() => + jestExpect(small).not.toBeLessThanOrEqual(big), + ).toThrowError('toBeLessThanOrEqual'); + + expect(() => + jestExpect(big).not.toBeGreaterThanOrEqual(small), + ).toThrowError('toBeGreaterThanOrEqual'); + + expect(() => jestExpect(big).toBeLessThanOrEqual(small)).toThrowError( + 'toBeLessThanOrEqual', + ); + }); + }); + } + [ [1, 1], [Number.MIN_VALUE, Number.MIN_VALUE], @@ -1003,6 +1201,26 @@ describe( ).toThrowErrorMatchingSnapshot(); }); }); + + if (isBigIntDefined) { + [ + [BigInt(1), BigInt(1)], + [BigInt(Number.MAX_SAFE_INTEGER), BigInt(Number.MAX_SAFE_INTEGER)], + ].forEach(([n1, n2]) => { + test(`equal numbers: [${n1}, ${n2}]`, () => { + jestExpect(n1).toBeGreaterThanOrEqual(n2); + jestExpect(n1).toBeLessThanOrEqual(n2); + + expect(() => + jestExpect(n1).not.toBeGreaterThanOrEqual(n2), + ).toThrowError('toBeGreaterThanOrEqual'); + + expect(() => jestExpect(n1).not.toBeLessThanOrEqual(n2)).toThrowError( + 'toBeLessThanOrEqual', + ); + }); + }); + } }, ); @@ -1052,6 +1270,21 @@ describe('.toContain(), .toContainEqual()', () => { }); }); + if (isBigIntDefined) { + [ + [[BigInt(1), BigInt(2), BigInt(3), BigInt(4)], BigInt(1)], + [[1, 2, 3, BigInt(3), 4], BigInt(3)], + ].forEach(([list, v]) => { + it(`'${stringify(list)}' contains '${stringify(v)}'`, () => { + jestExpect(list).toContain(v); + + expect(() => jestExpect(list).not.toContain(v)).toThrowError( + 'toContain', + ); + }); + }); + } + [ [[1, 2, 3], 4], [[null, undefined], 1], @@ -1067,6 +1300,16 @@ describe('.toContain(), .toContainEqual()', () => { }); }); + if (isBigIntDefined) { + [[[BigInt(1), BigInt(2), BigInt(3)], 3]].forEach(([list, v]) => { + it(`'${stringify(list)}' does not contain '${stringify(v)}'`, () => { + jestExpect(list).not.toContain(v); + + expect(() => jestExpect(list).toContain(v)).toThrowError('toContain'); + }); + }); + } + test('error cases', () => { expect(() => jestExpect(null).toContain(1)).toThrowErrorMatchingSnapshot(); }); diff --git a/packages/expect/src/matchers.ts b/packages/expect/src/matchers.ts index 05f05da91c06..ca51e52291ba 100644 --- a/packages/expect/src/matchers.ts +++ b/packages/expect/src/matchers.ts @@ -137,7 +137,26 @@ const matchers: MatchersObject = { secondArgument, secondArgumentColor: (arg: string) => arg, }; - ensureNumbers(received, expected, matcherName, options); + + if (typeof expected !== 'number') { + throw new Error( + matcherErrorMessage( + matcherHint(matcherName, undefined, undefined, options), + `${EXPECTED_COLOR('expected')} value must be a number`, + printWithType('Expected', expected, printExpected), + ), + ); + } + + if (typeof received !== 'number') { + throw new Error( + matcherErrorMessage( + matcherHint(matcherName, undefined, undefined, options), + `${RECEIVED_COLOR('received')} value must be a number`, + printWithType('Received', received, printReceived), + ), + ); + } let pass = false; let expectedDiff = 0; @@ -210,7 +229,11 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeGreaterThan(this: MatcherState, received: number, expected: number) { + toBeGreaterThan( + this: MatcherState, + received: number | bigint, + expected: number | bigint, + ) { const matcherName = 'toBeGreaterThan'; const isNot = this.isNot; const options: MatcherHintOptions = { @@ -232,8 +255,8 @@ const matchers: MatchersObject = { toBeGreaterThanOrEqual( this: MatcherState, - received: number, - expected: number, + received: number | bigint, + expected: number | bigint, ) { const matcherName = 'toBeGreaterThanOrEqual'; const isNot = this.isNot; @@ -305,7 +328,11 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeLessThan(this: MatcherState, received: number, expected: number) { + toBeLessThan( + this: MatcherState, + received: number | bigint, + expected: number | bigint, + ) { const matcherName = 'toBeLessThan'; const isNot = this.isNot; const options: MatcherHintOptions = { @@ -325,7 +352,11 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeLessThanOrEqual(this: MatcherState, received: number, expected: number) { + toBeLessThanOrEqual( + this: MatcherState, + received: number | bigint, + expected: number | bigint, + ) { const matcherName = 'toBeLessThanOrEqual'; const isNot = this.isNot; const options: MatcherHintOptions = { diff --git a/packages/expect/src/spyMatchers.ts b/packages/expect/src/spyMatchers.ts index b299b01d8b5d..6a2b116ea4f7 100644 --- a/packages/expect/src/spyMatchers.ts +++ b/packages/expect/src/spyMatchers.ts @@ -12,7 +12,7 @@ import { MatcherHintOptions, RECEIVED_COLOR, diff, - ensureExpectedIsNumber, + ensureExpectedIsNonNegativeInteger, ensureNoExpected, matcherErrorMessage, matcherHint, @@ -472,7 +472,7 @@ const createToBeCalledTimesMatcher = (matcherName: string) => isNot: this.isNot, promise: this.promise, }; - ensureExpectedIsNumber(expected, matcherName, options); + ensureExpectedIsNonNegativeInteger(expected, matcherName, options); ensureMockOrSpy(received, matcherName, expectedArgument, options); const receivedIsSpy = isSpy(received); @@ -508,7 +508,7 @@ const createToReturnTimesMatcher = (matcherName: string) => isNot: this.isNot, promise: this.promise, }; - ensureExpectedIsNumber(expected, matcherName, options); + ensureExpectedIsNonNegativeInteger(expected, matcherName, options); ensureMock(received, matcherName, expectedArgument, options); const receivedName = received.getMockName(); diff --git a/packages/expect/src/types.ts b/packages/expect/src/types.ts index 04f3950cbbf9..529d12014df4 100644 --- a/packages/expect/src/types.ts +++ b/packages/expect/src/types.ts @@ -150,11 +150,11 @@ export interface Matchers { /** * For comparing floating point numbers. */ - toBeGreaterThan(expected: number): R; + toBeGreaterThan(expected: number | bigint): R; /** * For comparing floating point numbers. */ - toBeGreaterThanOrEqual(expected: number): R; + toBeGreaterThanOrEqual(expected: number | bigint): R; /** * Ensure that an object is an instance of a class. * This matcher uses `instanceof` underneath. @@ -163,11 +163,11 @@ export interface Matchers { /** * For comparing floating point numbers. */ - toBeLessThan(expected: number): R; + toBeLessThan(expected: number | bigint): R; /** * For comparing floating point numbers. */ - toBeLessThanOrEqual(expected: number): R; + toBeLessThanOrEqual(expected: number | bigint): R; /** * This is the same as `.toBe(null)` but the error messages are a bit nicer. * So use `.toBeNull()` when you want to check that something is null. diff --git a/packages/jest-get-type/src/__tests__/getType.test.ts b/packages/jest-get-type/src/__tests__/getType.test.ts index de4bec59d8db..0f66f444316f 100644 --- a/packages/jest-get-type/src/__tests__/getType.test.ts +++ b/packages/jest-get-type/src/__tests__/getType.test.ts @@ -22,4 +22,8 @@ describe('.getType()', () => { test('map', () => expect(getType(new Map())).toBe('map')); test('set', () => expect(getType(new Set())).toBe('set')); test('date', () => expect(getType(new Date())).toBe('date')); + /* global BigInt */ + if (typeof BigInt === 'function') { + test('bigint', () => expect(getType(BigInt(1))).toBe('bigint')); + } }); diff --git a/packages/jest-get-type/src/__tests__/isPrimitive.test.ts b/packages/jest-get-type/src/__tests__/isPrimitive.test.ts index 9d0a6ab663c4..ecc7edcee1ea 100644 --- a/packages/jest-get-type/src/__tests__/isPrimitive.test.ts +++ b/packages/jest-get-type/src/__tests__/isPrimitive.test.ts @@ -8,6 +8,7 @@ // eslint-disable-next-line import/named import {isPrimitive} from '..'; +/* global BigInt */ describe('.isPrimitive()', () => { test.each([ @@ -20,6 +21,7 @@ describe('.isPrimitive()', () => { 0, NaN, Infinity, + typeof BigInt === 'function' ? BigInt(1) : 1, ])('returns true when given primitive value of: %s', primitive => { expect(isPrimitive(primitive)).toBe(true); }); diff --git a/packages/jest-get-type/src/index.ts b/packages/jest-get-type/src/index.ts index fde928fcc337..c2ceeaf9b604 100644 --- a/packages/jest-get-type/src/index.ts +++ b/packages/jest-get-type/src/index.ts @@ -7,6 +7,7 @@ type ValueType = | 'array' + | 'bigint' | 'boolean' | 'function' | 'null' @@ -37,6 +38,8 @@ function getType(value: unknown): ValueType { return 'number'; } else if (typeof value === 'string') { return 'string'; + } else if (typeof value === 'bigint') { + return 'bigint'; } else if (typeof value === 'object') { if (value != null) { if (value.constructor === RegExp) { diff --git a/packages/jest-matcher-utils/src/__tests__/__snapshots__/index.test.ts.snap b/packages/jest-matcher-utils/src/__tests__/__snapshots__/index.test.ts.snap index b495afe03d41..63845c6c1b4a 100644 --- a/packages/jest-matcher-utils/src/__tests__/__snapshots__/index.test.ts.snap +++ b/packages/jest-matcher-utils/src/__tests__/__snapshots__/index.test.ts.snap @@ -21,7 +21,7 @@ Expected has value: {\\"a\\": 1}" exports[`.ensureNumbers() throws error when expected is not a number (backward compatibility) 1`] = ` "expect(received)[.not].toBeCloseTo(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a number or bigint Expected has type: string Expected has value: \\"not_a_number\\"" @@ -30,7 +30,7 @@ Expected has value: \\"not_a_number\\"" exports[`.ensureNumbers() throws error when received is not a number (backward compatibility) 1`] = ` "expect(received)[.not].toBeCloseTo(expected) -Matcher error: received value must be a number +Matcher error: received value must be a number or bigint Received has type: string Received has value: \\"not_a_number\\"" @@ -39,7 +39,7 @@ Received has value: \\"not_a_number\\"" exports[`.ensureNumbers() with options promise empty isNot false received 1`] = ` "expect(received).toBeCloseTo(expected, precision) -Matcher error: received value must be a number +Matcher error: received value must be a number or bigint Received has type: string Received has value: \\"\\"" @@ -48,7 +48,7 @@ Received has value: \\"\\"" exports[`.ensureNumbers() with options promise empty isNot true expected 1`] = ` "expect(received).not.toBeCloseTo(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a number or bigint Expected has value: undefined" `; @@ -56,7 +56,7 @@ Expected has value: undefined" exports[`.ensureNumbers() with options promise rejects isNot false expected 1`] = ` "expect(received).rejects.toBeCloseTo(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a number or bigint Expected has type: string Expected has value: \\"0\\"" @@ -65,7 +65,7 @@ Expected has value: \\"0\\"" exports[`.ensureNumbers() with options promise rejects isNot true received 1`] = ` "expect(received).rejects.not.toBeCloseTo(expected) -Matcher error: received value must be a number +Matcher error: received value must be a number or bigint Received has type: symbol Received has value: Symbol(0.1)" @@ -74,7 +74,7 @@ Received has value: Symbol(0.1)" exports[`.ensureNumbers() with options promise resolves isNot false received 1`] = ` "expect(received).resolves.toBeCloseTo(expected) -Matcher error: received value must be a number +Matcher error: received value must be a number or bigint Received has type: boolean Received has value: false" @@ -83,7 +83,7 @@ Received has value: false" exports[`.ensureNumbers() with options promise resolves isNot true expected 1`] = ` "expect(received).resolves.not.toBeCloseTo(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a number or bigint Expected has value: null" `; diff --git a/packages/jest-matcher-utils/src/__tests__/index.test.ts b/packages/jest-matcher-utils/src/__tests__/index.test.ts index 2178cecae6cc..8ab83984bdfb 100644 --- a/packages/jest-matcher-utils/src/__tests__/index.test.ts +++ b/packages/jest-matcher-utils/src/__tests__/index.test.ts @@ -18,6 +18,9 @@ import { stringify, } from '../'; +/* global BigInt */ +const isBigIntDefined = typeof BigInt === 'function'; + describe('.stringify()', () => { [ [[], '[]'], @@ -33,6 +36,8 @@ describe('.stringify()', () => { [Infinity, 'Infinity'], [-Infinity, '-Infinity'], [/ab\.c/gi, '/ab\\.c/gi'], + isBigIntDefined ? [BigInt(1), '1n'] : [12, '12'], + isBigIntDefined ? [BigInt(0), '0n'] : [123, '123'], ].forEach(([v, s]) => { test(stringify(v), () => { expect(stringify(v)).toBe(s); @@ -99,6 +104,12 @@ describe('.ensureNumbers()', () => { // @ts-ignore ensureNumbers(1, 2); }).not.toThrow(); + if (isBigIntDefined) { + expect(() => { + // @ts-ignore + ensureNumbers(BigInt(1), BigInt(2)); + }).not.toThrow(); + } }); test('throws error when expected is not a number (backward compatibility)', () => { @@ -219,6 +230,7 @@ describe('diff', () => { ['a', 1], ['a', true], [1, true], + [isBigIntDefined ? BigInt(1) : 1, true], ].forEach(([actual, expected]) => expect(diff(actual, expected)).toBe('diff output'), ); @@ -231,6 +243,12 @@ describe('diff', () => { test('two numbers', () => { expect(diff(1, 2)).toBe(null); }); + + if (isBigIntDefined) { + test('two bigints', () => { + expect(diff(BigInt(1), BigInt(2))).toBe(null); + }); + } }); describe('.pluralize()', () => { diff --git a/packages/jest-matcher-utils/src/index.ts b/packages/jest-matcher-utils/src/index.ts index 2d03613e4a6a..c1526694b77d 100644 --- a/packages/jest-matcher-utils/src/index.ts +++ b/packages/jest-matcher-utils/src/index.ts @@ -152,42 +152,51 @@ export const ensureNoExpected = ( } }; +/** + * Ensures that `actual` is of type `number | bigint` + */ export const ensureActualIsNumber = ( actual: unknown, matcherName: string, options?: MatcherHintOptions, ) => { - if (typeof actual !== 'number') { + if (typeof actual !== 'number' && typeof actual !== 'bigint') { // Prepend maybe not only for backward compatibility. const matcherString = (options ? '' : '[.not]') + matcherName; throw new Error( matcherErrorMessage( matcherHint(matcherString, undefined, undefined, options), - `${RECEIVED_COLOR('received')} value must be a number`, + `${RECEIVED_COLOR('received')} value must be a number or bigint`, printWithType('Received', actual, printReceived), ), ); } }; +/** + * Ensures that `expected` is of type `number | bigint` + */ export const ensureExpectedIsNumber = ( expected: unknown, matcherName: string, options?: MatcherHintOptions, ) => { - if (typeof expected !== 'number') { + if (typeof expected !== 'number' && typeof expected !== 'bigint') { // Prepend maybe not only for backward compatibility. const matcherString = (options ? '' : '[.not]') + matcherName; throw new Error( matcherErrorMessage( matcherHint(matcherString, undefined, undefined, options), - `${EXPECTED_COLOR('expected')} value must be a number`, + `${EXPECTED_COLOR('expected')} value must be a number or bigint`, printWithType('Expected', expected, printExpected), ), ); } }; +/** + * Ensures that `actual` & `expected` are of type `number | bigint` + */ export const ensureNumbers = ( actual: unknown, expected: unknown, @@ -371,6 +380,9 @@ const shouldPrintDiff = (actual: unknown, expected: unknown) => { if (typeof actual === 'number' && typeof expected === 'number') { return false; } + if (typeof actual === 'bigint' && typeof expected === 'bigint') { + return false; + } if (typeof actual === 'boolean' && typeof expected === 'boolean') { return false; } diff --git a/yarn.lock b/yarn.lock index 3a41a9a133f3..3f0c0c749558 100644 --- a/yarn.lock +++ b/yarn.lock @@ -385,6 +385,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-syntax-bigint@^7.0.0": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.4.4.tgz#3c6f77ba5f4aefbabfd5183117278c9695971093" + integrity sha512-tXyL/mlth1QBl5OwpsABCmAWm4u9xvUIk5nsLd2wWKrC6C4Qm9JvmN18IAGlIXkMvWZTRJVuGRXSLCuz+1hG9Q== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-class-properties@^7.0.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.2.0.tgz#23b3b7b9bcdabd73672a9149f728cd3be6214812"