diff --git a/.changeset/better-lights-lay.md b/.changeset/better-lights-lay.md new file mode 100644 index 00000000..cc406995 --- /dev/null +++ b/.changeset/better-lights-lay.md @@ -0,0 +1,6 @@ +--- +'jest-extended': major +--- + +Add BigInt support to number matchers +and add new toBeBigInt matchers diff --git a/all.js b/all.js index 9be97d79..5a3bab7a 100644 --- a/all.js +++ b/all.js @@ -1,4 +1,3 @@ // override rule to allow running linting before build (dist directory won't exist yet) -// eslint-disable-next-line import/no-unresolved require('./dist/all'); diff --git a/src/matchers/index.ts b/src/matchers/index.ts index ac420f62..064008b2 100644 --- a/src/matchers/index.ts +++ b/src/matchers/index.ts @@ -7,6 +7,7 @@ export { toBeArrayOfSize } from './toBeArrayOfSize'; export { toBeBefore } from './toBeBefore'; export { toBeBeforeOrEqualTo } from './toBeBeforeOrEqualTo'; export { toBeBetween } from './toBeBetween'; +export { toBeBigInt } from './toBeBigInt'; export { toBeBoolean } from './toBeBoolean'; export { toBeDate } from './toBeDate'; export { toBeDateString } from './toBeDateString'; diff --git a/src/matchers/toBeBigInt.ts b/src/matchers/toBeBigInt.ts new file mode 100644 index 00000000..600c63d0 --- /dev/null +++ b/src/matchers/toBeBigInt.ts @@ -0,0 +1,20 @@ +export function toBeBigInt(actual: unknown) { + // @ts-expect-error OK to have implicit any for this.utils + const { printReceived, matcherHint } = this.utils; + + const pass = typeof actual === 'bigint'; + + return { + pass, + message: () => + pass + ? matcherHint('.not.toBeBigInt', 'received', '') + + '\n\n' + + 'Expected value to not be an BigInt received:\n' + + ` ${printReceived(actual)}` + : matcherHint('.toBeBigInt', 'received', '') + + '\n\n' + + 'Expected value to be a BigInt received:\n' + + ` ${printReceived(actual)}`, + }; +} diff --git a/src/matchers/toBeEven.ts b/src/matchers/toBeEven.ts index 785bc161..93723fb4 100644 --- a/src/matchers/toBeEven.ts +++ b/src/matchers/toBeEven.ts @@ -2,7 +2,9 @@ export function toBeEven(actual: unknown) { // @ts-expect-error OK to have implicit any for this.utils const { printReceived, matcherHint } = this.utils; - const pass = isNumber(actual) && isEven(actual); + const pass = + (typeof actual === 'number' && !isNaN(actual) && actual % 2 === 0) || + (typeof actual === 'bigint' && actual % BigInt(2) === BigInt(0)); return { pass, @@ -18,6 +20,3 @@ export function toBeEven(actual: unknown) { ` ${printReceived(actual)}`, }; } - -const isNumber = (expected: any) => !isNaN(parseInt(expected)); -const isEven = (expected: any) => expected % 2 === 0; diff --git a/src/matchers/toBeFinite.ts b/src/matchers/toBeFinite.ts index f87bddab..1389b4df 100644 --- a/src/matchers/toBeFinite.ts +++ b/src/matchers/toBeFinite.ts @@ -2,7 +2,7 @@ export function toBeFinite(actual: unknown) { // @ts-expect-error OK to have implicit any for this.utils const { printReceived, matcherHint } = this.utils; - const pass = Number.isFinite(actual); + const pass = Number.isFinite(actual) || typeof actual === 'bigint'; return { pass, diff --git a/src/matchers/toBeInRange.ts b/src/matchers/toBeInRange.ts index 8be5c205..5e65ac7f 100644 --- a/src/matchers/toBeInRange.ts +++ b/src/matchers/toBeInRange.ts @@ -1,8 +1,13 @@ -export function toBeInRange(actual: unknown[], min: number, max: number) { +export function toBeInRange(actual: unknown[], min: number | bigint, max: number | bigint) { // @ts-expect-error OK to have implicit any for this.utils const { printReceived, printExpected, matcherHint } = this.utils; - const element = actual.find((option: any) => option < min || option >= max); + const element = actual.find((option: any) => { + if (typeof option === 'bigint' && typeof min === 'bigint' && typeof max === 'bigint') { + return option < min || option >= max; + } + return option < min || option >= max; + }); const pass = element === undefined; diff --git a/src/matchers/toBeNegative.ts b/src/matchers/toBeNegative.ts index c77ffb99..06615ec7 100644 --- a/src/matchers/toBeNegative.ts +++ b/src/matchers/toBeNegative.ts @@ -2,7 +2,8 @@ export function toBeNegative(actual: unknown) { // @ts-expect-error OK to have implicit any for this.utils const { printReceived, matcherHint } = this.utils; - const pass = isNumber(actual) && isNegative(actual); + const pass = + (typeof actual === 'number' && !isNaN(actual) && actual < 0) || (typeof actual === 'bigint' && actual < BigInt(0)); return { pass, @@ -18,6 +19,3 @@ export function toBeNegative(actual: unknown) { ` ${printReceived(actual)}`, }; } - -const isNumber = (value: any) => !isNaN(parseInt(value)); -const isNegative = (value: any) => value < 0; diff --git a/src/matchers/toBeOdd.ts b/src/matchers/toBeOdd.ts index d6bf0ee7..3ff6ab2d 100644 --- a/src/matchers/toBeOdd.ts +++ b/src/matchers/toBeOdd.ts @@ -2,7 +2,7 @@ export function toBeOdd(actual: unknown) { // @ts-expect-error OK to have implicit any for this.utils const { printReceived, matcherHint } = this.utils; - const pass = isNumber(actual) && isOdd(actual); + const pass = (isNumber(actual) || isBigInt(actual)) && isOdd(actual); return { pass, @@ -19,5 +19,9 @@ export function toBeOdd(actual: unknown) { }; } -const isNumber = (expected: any) => !isNaN(parseInt(expected)); -const isOdd = (expected: any) => expected % 2 === 1; +const isNumber = (expected: any) => typeof expected === 'number' && Number.isFinite(expected); +const isBigInt = (expected: any) => typeof expected === 'bigint'; +const isOdd = (expected: any) => { + if (isBigInt(expected)) return expected % 2n === 1n || expected % 2n === -1n; + return Math.abs(expected % 2) === 1; +}; diff --git a/src/matchers/toBePositive.ts b/src/matchers/toBePositive.ts index 3094d6cc..63021870 100644 --- a/src/matchers/toBePositive.ts +++ b/src/matchers/toBePositive.ts @@ -2,7 +2,7 @@ export function toBePositive(actual: unknown) { // @ts-expect-error OK to have implicit any for this.utils const { printReceived, matcherHint } = this.utils; - const pass = isNumber(actual) && isPositive(actual); + const pass = (isNumber(actual) || isBigInt(actual)) && isPositive(actual); return { pass, @@ -19,5 +19,9 @@ export function toBePositive(actual: unknown) { }; } -const isNumber = (value: any) => !isNaN(parseInt(value)); -const isPositive = (value: any) => value > 0; +const isNumber = (value: any) => typeof value === 'number' && !isNaN(value) && isFinite(value); +const isBigInt = (value: any) => typeof value === 'bigint'; +const isPositive = (value: any) => { + if (typeof value === 'bigint') return value > 0n; + return value > 0; +}; diff --git a/src/matchers/toChangeBy.ts b/src/matchers/toChangeBy.ts index 1ba7de0d..4c3a734a 100644 --- a/src/matchers/toChangeBy.ts +++ b/src/matchers/toChangeBy.ts @@ -1,13 +1,20 @@ /** * Use `.toChangeBy` when checking if a value changed by an amount. */ -export function toChangeBy(mutator: () => unknown | void, checker: () => number, by: number = 1) { +export function toChangeBy(mutator: () => unknown | void, checker: () => number | bigint, by: number | bigint = 1) { // @ts-expect-error OK to have implicit any for this.utils const { printReceived: print, matcherHint: hint } = this.utils; const before = checker(); mutator(); - const received = checker() - before; + const after = checker(); + + let received; + if (typeof before === 'bigint' && typeof after === 'bigint') { + received = after - before; + } else { + received = Number(after) - Number(before); + } const passMessage = ` ${hint('.not.toChangeBy', 'received', '')}\n\nExpected value to not change by ${by}, received:\n ${print(received)} diff --git a/src/matchers/toChangeTo.ts b/src/matchers/toChangeTo.ts index 32f3361f..4e16d192 100644 --- a/src/matchers/toChangeTo.ts +++ b/src/matchers/toChangeTo.ts @@ -1,7 +1,7 @@ /** * Use `.toChangeTo` when checking if a value changed to a specific value. */ -export function toChangeTo(mutator: () => unknown | void, checker: () => number, to: number) { +export function toChangeTo(mutator: () => unknown | void, checker: () => E, to: E) { // @ts-expect-error OK to have implicit any for this.utils const { printReceived: print, matcherHint: hint } = this.utils; diff --git a/test/matchers/__snapshots__/toBeBigInt.test.ts.snap b/test/matchers/__snapshots__/toBeBigInt.test.ts.snap new file mode 100644 index 00000000..176f601a --- /dev/null +++ b/test/matchers/__snapshots__/toBeBigInt.test.ts.snap @@ -0,0 +1,22 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`.not.toBeInteger fails when given integer 1`] = ` +"expect(received).not.toBeBigInt() + +Expected value to not be an BigInt received: + 1n" +`; + +exports[`.toBeBigInt fails when given fraction 1`] = ` +"expect(received).toBeBigInt() + +Expected value to be a BigInt received: + 1.5" +`; + +exports[`.toBeBigInt fails when given integer 1`] = ` +"expect(received).toBeBigInt() + +Expected value to be a BigInt received: + 1" +`; diff --git a/test/matchers/__snapshots__/toBeEven.test.ts.snap b/test/matchers/__snapshots__/toBeEven.test.ts.snap index 19697a2f..e662b6cc 100644 --- a/test/matchers/__snapshots__/toBeEven.test.ts.snap +++ b/test/matchers/__snapshots__/toBeEven.test.ts.snap @@ -1,5 +1,12 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`.not.toBeEven fails when given an even BigInt 1`] = ` +"expect(received).not.toBeEven() + +Expected value to not be an even number received: + 2n" +`; + exports[`.not.toBeEven fails when given an even number 1`] = ` "expect(received).not.toBeEven() @@ -7,6 +14,20 @@ Expected value to not be an even number received: 2" `; +exports[`.toBeEven fails when given odd BigInt 1`] = ` +"expect(received).toBeEven() + +Expected value to be an even number received: + 1n" +`; + +exports[`.toBeEven fails when given odd BigInt 2`] = ` +"expect(received).toBeEven() + +Expected value to be an even number received: + -3n" +`; + exports[`.toBeEven fails when not given an even number 1`] = ` "expect(received).toBeEven() diff --git a/test/matchers/__snapshots__/toBeFinite.test.ts.snap b/test/matchers/__snapshots__/toBeFinite.test.ts.snap index 56772ef0..f2b40837 100644 --- a/test/matchers/__snapshots__/toBeFinite.test.ts.snap +++ b/test/matchers/__snapshots__/toBeFinite.test.ts.snap @@ -1,5 +1,12 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`.not.toBeFinite fails when given a BigInt value 1`] = ` +"expect(received).not.toBeFinite() + +Expected value to not be finite received: + 1n" +`; + exports[`.not.toBeFinite fails when given a finite number 1`] = ` "expect(received).not.toBeFinite() diff --git a/test/matchers/__snapshots__/toBeInRange.test.ts.snap b/test/matchers/__snapshots__/toBeInRange.test.ts.snap index c04eec1a..c1a3cce6 100644 --- a/test/matchers/__snapshots__/toBeInRange.test.ts.snap +++ b/test/matchers/__snapshots__/toBeInRange.test.ts.snap @@ -1,5 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`.not.toBeInRange fails when given BigInt array is in the given range 1`] = ` +"expect(received).not.toBeInRange(expected) + +Expected Array to not be in range 4n, 10n +Received: + [4n, 5n, 7n, 9n]" +`; + exports[`.not.toBeInRange fails when given array is in the given range 1`] = ` "expect(received).not.toBeInRange(expected) @@ -8,6 +16,13 @@ Received: [4, 5, 7, 9]" `; +exports[`.toBeInRange fails when given BigInt array is not in a given range 1`] = ` +"expect(received).toBeInRange(expected) + +Expected: Array elements to be in range (4n, 8n) +Received: Array element out of range 9n" +`; + exports[`.toBeInRange fails when given array is not in a given range 1`] = ` "expect(received).toBeInRange(expected) diff --git a/test/matchers/__snapshots__/toBeNegative.test.ts.snap b/test/matchers/__snapshots__/toBeNegative.test.ts.snap index c7613c03..8e0ed2ed 100644 --- a/test/matchers/__snapshots__/toBeNegative.test.ts.snap +++ b/test/matchers/__snapshots__/toBeNegative.test.ts.snap @@ -1,5 +1,12 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`.not.toBeNegative fails when given negative BigInt 1`] = ` +"expect(received).not.toBeNegative() + +Expected value to not be a negative number received: + -1n" +`; + exports[`.not.toBeNegative fails when given negative number 1`] = ` "expect(received).not.toBeNegative() @@ -14,9 +21,23 @@ Expected value to be a negative number received: Infinity" `; +exports[`.toBeNegative fails when given positive BigInt 1`] = ` +"expect(received).toBeNegative() + +Expected value to be a negative number received: + 1n" +`; + exports[`.toBeNegative fails when given positive number 1`] = ` "expect(received).toBeNegative() Expected value to be a negative number received: 1" `; + +exports[`.toBeNegative fails when given zero BigInt 1`] = ` +"expect(received).toBeNegative() + +Expected value to be a negative number received: + 0n" +`; diff --git a/test/matchers/__snapshots__/toBeOdd.test.ts.snap b/test/matchers/__snapshots__/toBeOdd.test.ts.snap index 7286266a..7fc9f97c 100644 --- a/test/matchers/__snapshots__/toBeOdd.test.ts.snap +++ b/test/matchers/__snapshots__/toBeOdd.test.ts.snap @@ -39,33 +39,47 @@ exports[`.toBeOdd fails when given not given an odd number 5`] = ` "expect(received).toBeOdd() Expected value to be odd received: - {}" + 2n" `; exports[`.toBeOdd fails when given not given an odd number 6`] = ` "expect(received).toBeOdd() Expected value to be odd received: - [Function anonymous]" + {}" `; exports[`.toBeOdd fails when given not given an odd number 7`] = ` "expect(received).toBeOdd() Expected value to be odd received: - undefined" + [Function anonymous]" `; exports[`.toBeOdd fails when given not given an odd number 8`] = ` "expect(received).toBeOdd() Expected value to be odd received: - null" + undefined" `; exports[`.toBeOdd fails when given not given an odd number 9`] = ` "expect(received).toBeOdd() +Expected value to be odd received: + null" +`; + +exports[`.toBeOdd fails when given not given an odd number 10`] = ` +"expect(received).toBeOdd() + Expected value to be odd received: NaN" `; + +exports[`.toBeOdd fails when given not given an odd number 11`] = ` +"expect(received).toBeOdd() + +Expected value to be odd received: + Symbol(foo)" +`; diff --git a/test/matchers/__snapshots__/toBePositive.test.ts.snap b/test/matchers/__snapshots__/toBePositive.test.ts.snap index 70143b4e..7b9d7735 100644 --- a/test/matchers/__snapshots__/toBePositive.test.ts.snap +++ b/test/matchers/__snapshots__/toBePositive.test.ts.snap @@ -1,5 +1,12 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`.not.toBePositive fails when given a positive BigInt 1`] = ` +"expect(received).not.toBePositive() + +Expected value to not be positive received: + 5n" +`; + exports[`.not.toBePositive fails when given a positive number 1`] = ` "expect(received).not.toBePositive() @@ -7,6 +14,13 @@ Expected value to not be positive received: 5" `; +exports[`.toBePositive fails when not given a positive BigInt 1`] = ` +"expect(received).toBePositive() + +Expected value to be positive received: + -1n" +`; + exports[`.toBePositive fails when not given a positive number 1`] = ` "expect(received).toBePositive() diff --git a/test/matchers/__snapshots__/toChangeBy.test.ts.snap b/test/matchers/__snapshots__/toChangeBy.test.ts.snap index f08d5d5f..4e2d5009 100644 --- a/test/matchers/__snapshots__/toChangeBy.test.ts.snap +++ b/test/matchers/__snapshots__/toChangeBy.test.ts.snap @@ -7,6 +7,13 @@ Expected value to not change by 1, received: 1" `; +exports[`.not.toChangeBy fails when given a BigInt mutator that does change the value 1`] = ` +"expect(received).not.toChangeBy() + +Expected value to not change by 1, received: + 1n" +`; + exports[`.not.toChangeBy fails when mutating a counter 1`] = ` "expect(received).not.toChangeBy() @@ -14,6 +21,13 @@ Expected value to not change by 1, received: 1" `; +exports[`.toChangeBy fails when a given BigInt value does not change 1`] = ` +"expect(received).toChangeBy() + +Expected value to change by 1, received: + 0n" +`; + exports[`.toChangeBy fails when a given value does not increment 1`] = ` "expect(received).toChangeBy() diff --git a/test/matchers/__snapshots__/toChangeTo.test.ts.snap b/test/matchers/__snapshots__/toChangeTo.test.ts.snap index da8cb132..c13807d2 100644 --- a/test/matchers/__snapshots__/toChangeTo.test.ts.snap +++ b/test/matchers/__snapshots__/toChangeTo.test.ts.snap @@ -1,19 +1,33 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`.not.toChange fails when a value expected not to change defies all expectations and changes 1`] = ` +exports[`.not.toChangeTo fails when a BigInt value changes to the expected value 1`] = ` +"expect(received).not.toChangeTo() + +Expected value to not change to 42, received: + 42n" +`; + +exports[`.not.toChangeTo fails when a value expected not to change defies all expectations and changes 1`] = ` "expect(received).not.toChangeTo() Expected value to not change to 1, received: 1" `; -exports[`.not.toChange fails when mutating a counter 1`] = ` +exports[`.not.toChangeTo fails when mutating a counter 1`] = ` "expect(received).not.toChangeTo() Expected value to not change to 1, received: 1" `; +exports[`.toChangeTo fails when a BigInt value does not change to expected value 1`] = ` +"expect(received).toChangeto() + +Expected value to change to 42, received: + 10n" +`; + exports[`.toChangeTo fails when a given value does not increment 1`] = ` "expect(received).toChangeto() @@ -21,6 +35,12 @@ Expected value to change to 1, received: 0" `; +exports[`.toChangeTo fails when expecting a BigInt to change to its original value 1`] = ` +"expect(received).toChangeTo() + +Cannot expect a value to change to its original state, received: 42n" +`; + exports[`.toChangeTo fails when not mutating a counter 1`] = ` "expect(received).toChangeto() diff --git a/test/matchers/toBeBigInt.test.ts b/test/matchers/toBeBigInt.test.ts new file mode 100644 index 00000000..59811c90 --- /dev/null +++ b/test/matchers/toBeBigInt.test.ts @@ -0,0 +1,31 @@ +import * as matcher from 'src/matchers/toBeBigInt'; + +expect.extend(matcher); + +describe('.toBeBigInt', () => { + test('passes when given bigint', () => { + expect(BigInt(1)).toBeBigInt(); + }); + + test('fails when given integer', () => { + expect(() => expect(1).toBeBigInt()).toThrowErrorMatchingSnapshot(); + }); + + test('fails when given fraction', () => { + expect(() => expect(1.5).toBeBigInt()).toThrowErrorMatchingSnapshot(); + }); +}); + +describe('.not.toBeInteger', () => { + test('passes when given integer', () => { + expect(1).not.toBeBigInt(); + }); + + test('passes when given fraction', () => { + expect(1.5).not.toBeBigInt(); + }); + + test('fails when given integer', () => { + expect(() => expect(BigInt(1)).not.toBeBigInt()).toThrowErrorMatchingSnapshot(); + }); +}); diff --git a/test/matchers/toBeEven.test.ts b/test/matchers/toBeEven.test.ts index 672cfac6..f833e6e2 100644 --- a/test/matchers/toBeEven.test.ts +++ b/test/matchers/toBeEven.test.ts @@ -7,12 +7,23 @@ describe('.toBeEven', () => { expect(2).toBeEven(); }); + test('passes when given even BigInt', () => { + expect(BigInt(2)).toBeEven(); + expect(BigInt(0)).toBeEven(); + expect(BigInt(-4)).toBeEven(); + }); + test.each([[false], [true], [''], [1], [{}], [() => {}], [undefined], [null], [NaN]])( 'fails when not given an even number', given => { expect(() => expect(given).toBeEven()).toThrowErrorMatchingSnapshot(); }, ); + + test('fails when given odd BigInt', () => { + expect(() => expect(BigInt(1)).toBeEven()).toThrowErrorMatchingSnapshot(); + expect(() => expect(BigInt(-3)).toBeEven()).toThrowErrorMatchingSnapshot(); + }); }); describe('.not.toBeEven', () => { @@ -20,10 +31,19 @@ describe('.not.toBeEven', () => { expect(() => expect(2).not.toBeEven()).toThrowErrorMatchingSnapshot(); }); + test('fails when given an even BigInt', () => { + expect(() => expect(BigInt(2)).not.toBeEven()).toThrowErrorMatchingSnapshot(); + }); + test.each([[false], [true], [''], [1], [[]], [{}], [() => {}], [undefined], [null], [NaN]])( 'passes when not given an even number: %s', given => { expect(given).not.toBeEven(); }, ); + + test('passes when given odd BigInt', () => { + expect(BigInt(1)).not.toBeEven(); + expect(BigInt(-3)).not.toBeEven(); + }); }); diff --git a/test/matchers/toBeFinite.test.ts b/test/matchers/toBeFinite.test.ts index 6e4acabc..7d2cd859 100644 --- a/test/matchers/toBeFinite.test.ts +++ b/test/matchers/toBeFinite.test.ts @@ -23,6 +23,13 @@ describe('.toBeFinite', () => { expect(0).toBeFinite(); }); + test('passes when given a BigInt value', () => { + expect(1n).toBeFinite(); + expect(-1n).toBeFinite(); + expect(0n).toBeFinite(); + expect(9007199254740991n).toBeFinite(); // BigInt equivalent of MAX_SAFE_INTEGER + }); + test('fails when not given NaN', () => { expect(() => expect(NaN).toBeFinite()).toThrowErrorMatchingSnapshot(); }); @@ -37,6 +44,10 @@ describe('.not.toBeFinite', () => { expect(() => expect(1).not.toBeFinite()).toThrowErrorMatchingSnapshot(); }); + test('fails when given a BigInt value', () => { + expect(() => expect(1n).not.toBeFinite()).toThrowErrorMatchingSnapshot(); + }); + test('passes when given NaN', () => { expect(NaN).not.toBeFinite(); }); diff --git a/test/matchers/toBeInRange.test.ts b/test/matchers/toBeInRange.test.ts index 7743cdea..27a9cc2a 100644 --- a/test/matchers/toBeInRange.test.ts +++ b/test/matchers/toBeInRange.test.ts @@ -7,9 +7,17 @@ describe('.toBeInRange', () => { expect([4, 5, 7, 9]).toBeInRange(4, 10); }); + test('passes when given BigInt array is in range', () => { + expect([4n, 5n, 7n, 9n]).toBeInRange(4n, 10n); + }); + test('fails when given array is not in a given range', () => { expect(() => expect([4, 5, 7, 9]).toBeInRange(4, 8)).toThrowErrorMatchingSnapshot(); }); + + test('fails when given BigInt array is not in a given range', () => { + expect(() => expect([4n, 5n, 7n, 9n]).toBeInRange(4n, 8n)).toThrowErrorMatchingSnapshot(); + }); }); describe('.not.toBeInRange', () => { @@ -17,7 +25,15 @@ describe('.not.toBeInRange', () => { expect([12, 13, 15, 17]).not.toBeInRange(4, 9); }); + test('passes when given BigInt array is not in the given range', () => { + expect([12n, 13n, 15n, 17n]).not.toBeInRange(4n, 9n); + }); + test('fails when given array is in the given range', () => { expect(() => expect([4, 5, 7, 9]).not.toBeInRange(4, 10)).toThrowErrorMatchingSnapshot(); }); + + test('fails when given BigInt array is in the given range', () => { + expect(() => expect([4n, 5n, 7n, 9n]).not.toBeInRange(4n, 10n)).toThrowErrorMatchingSnapshot(); + }); }); diff --git a/test/matchers/toBeNegative.test.ts b/test/matchers/toBeNegative.test.ts index 52013620..212473ca 100644 --- a/test/matchers/toBeNegative.test.ts +++ b/test/matchers/toBeNegative.test.ts @@ -7,10 +7,23 @@ describe('.toBeNegative', () => { expect(-1).toBeNegative(); }); + test('passes when given negative BigInt', () => { + expect(BigInt(-1)).toBeNegative(); + expect(BigInt(-123456789)).toBeNegative(); + }); + test('fails when given positive number', () => { expect(() => expect(1).toBeNegative()).toThrowErrorMatchingSnapshot(); }); + test('fails when given positive BigInt', () => { + expect(() => expect(BigInt(1)).toBeNegative()).toThrowErrorMatchingSnapshot(); + }); + + test('fails when given zero BigInt', () => { + expect(() => expect(BigInt(0)).toBeNegative()).toThrowErrorMatchingSnapshot(); + }); + test('fails when given Infinity', () => { expect(() => expect(Infinity).toBeNegative()).toThrowErrorMatchingSnapshot(); }); @@ -21,6 +34,11 @@ describe('.not.toBeNegative', () => { expect(1).not.toBeNegative(); }); + test('passes when given positive BigInt', () => { + expect(BigInt(1)).not.toBeNegative(); + expect(BigInt(0)).not.toBeNegative(); + }); + test('passes when given Infinity', () => { expect(Infinity).not.toBeNegative(); }); @@ -32,4 +50,8 @@ describe('.not.toBeNegative', () => { test('fails when given negative number', () => { expect(() => expect(-1).not.toBeNegative()).toThrowErrorMatchingSnapshot(); }); + + test('fails when given negative BigInt', () => { + expect(() => expect(BigInt(-1)).not.toBeNegative()).toThrowErrorMatchingSnapshot(); + }); }); diff --git a/test/matchers/toBeOdd.test.ts b/test/matchers/toBeOdd.test.ts index d464a816..b4fb2a75 100644 --- a/test/matchers/toBeOdd.test.ts +++ b/test/matchers/toBeOdd.test.ts @@ -7,7 +7,7 @@ describe('.toBeOdd', () => { expect(1).toBeOdd(); }); - test.each([[false], [true], [''], [2], [{}], [() => {}], [undefined], [null], [NaN]])( + test.each([[false], [true], [''], [2], [2n], [{}], [() => {}], [undefined], [null], [NaN], [Symbol('foo')]])( 'fails when given not given an odd number', given => { expect(() => expect(given).toBeOdd()).toThrowErrorMatchingSnapshot(); @@ -16,7 +16,7 @@ describe('.toBeOdd', () => { }); describe('.not.toBeOdd', () => { - test.each([[false], [true], [''], [2], [[]], [{}], [() => {}], [undefined], [null], [NaN]])( + test.each([[false], [true], [''], [2], [2n], [[]], [{}], [() => {}], [undefined], [null], [NaN], [Symbol('foo')]])( 'passes when not given an odd number: %s', given => { expect(given).not.toBeOdd(); diff --git a/test/matchers/toBePositive.test.ts b/test/matchers/toBePositive.test.ts index d30e4e9a..c849e1b4 100644 --- a/test/matchers/toBePositive.test.ts +++ b/test/matchers/toBePositive.test.ts @@ -7,13 +7,21 @@ describe('.toBePositive', () => { expect(1).toBePositive(); }); + test('passes when given a positive BigInt', () => { + expect(1n).toBePositive(); + }); + test('fails when not given a positive number', () => { expect(() => expect(-1).toBePositive()).toThrowErrorMatchingSnapshot(); }); + + test('fails when not given a positive BigInt', () => { + expect(() => expect(-1n).toBePositive()).toThrowErrorMatchingSnapshot(); + }); }); describe('.not.toBePositive', () => { - test.each([[false], [''], [-1], [0], [{}], [[]], [() => {}], [undefined], [null], [NaN], [Infinity]])( + test.each([[false], [''], [-1], [0], [{}], [[]], [() => {}], [undefined], [null], [NaN], [Infinity], [-1n], [0n]])( 'passes when not given a positive number: %s', given => { expect(given).not.toBePositive(); @@ -23,4 +31,8 @@ describe('.not.toBePositive', () => { test('fails when given a positive number', () => { expect(() => expect(5).not.toBePositive()).toThrowErrorMatchingSnapshot(); }); + + test('fails when given a positive BigInt', () => { + expect(() => expect(5n).not.toBePositive()).toThrowErrorMatchingSnapshot(); + }); }); diff --git a/test/matchers/toChangeBy.test.ts b/test/matchers/toChangeBy.test.ts index 2464b669..c580d0c7 100644 --- a/test/matchers/toChangeBy.test.ts +++ b/test/matchers/toChangeBy.test.ts @@ -40,6 +40,18 @@ describe('.toChangeBy', () => { counter.increment(); expect(() => counter.reset()).toChangeBy(() => counter.count(), -3); }); + + test('passes when given a BigInt value that the mutator decrements', () => { + let value = 10n; + expect(() => { + value = value - 1n; + }).toChangeBy(() => value, -1n); + }); + + test('fails when a given BigInt value does not change', () => { + const value = 5n; + expect(() => expect(() => value).toChangeBy(() => value, 1n)).toThrowErrorMatchingSnapshot(); + }); }); describe('.not.toChangeBy', () => { @@ -72,4 +84,18 @@ describe('.not.toChangeBy', () => { let value = 0; expect(() => expect(() => value++).not.toChangeBy(() => value, 1)).toThrowErrorMatchingSnapshot(); }); + + test('passes when given a BigInt mutator that does not change the value', () => { + const value = 10n; + expect(() => value).not.toChangeBy(() => value, 1n); + }); + + test('fails when given a BigInt mutator that does change the value', () => { + let value = 10n; + expect(() => + expect(() => { + value = value + 1n; + }).not.toChangeBy(() => value, 1n), + ).toThrowErrorMatchingSnapshot(); + }); }); diff --git a/test/matchers/toChangeTo.test.ts b/test/matchers/toChangeTo.test.ts index da7d140c..f198e3b5 100644 --- a/test/matchers/toChangeTo.test.ts +++ b/test/matchers/toChangeTo.test.ts @@ -51,9 +51,29 @@ describe('.toChangeTo', () => { special = 'meatloaf'; }).toChangeTo(() => special, 'meatloaf'); }); + + test('passes when using BigInt values', () => { + let value = 0n; + expect(() => { + value = 42n; + }).toChangeTo(() => value, 42n); + }); + + test('fails when a BigInt value does not change to expected value', () => { + let value = 0n; + expect(() => { + value = 10n; + expect(() => value).toChangeTo(() => value, 42n); + }).toThrowErrorMatchingSnapshot(); + }); + + test('fails when expecting a BigInt to change to its original value', () => { + const value = 42n; + expect(() => expect(() => {}).toChangeTo(() => value, 42n)).toThrowErrorMatchingSnapshot(); + }); }); -describe('.not.toChange', () => { +describe('.not.toChangeTo', () => { test('passes when given a mutator that does not increment the value', () => { const value = 0; expect(() => value).not.toChangeTo(() => value, 1); @@ -82,4 +102,20 @@ describe('.not.toChange', () => { let value = 0; expect(() => expect(() => value++).not.toChangeTo(() => value, 1)).toThrowErrorMatchingSnapshot(); }); + + test('passes when a BigInt value changes to something other than the expected value', () => { + let value = 0n; + expect(() => { + value = 10n; + }).not.toChangeTo(() => value, 42n); + }); + + test('fails when a BigInt value changes to the expected value', () => { + let value = 0n; + expect(() => + expect(() => { + value = 42n; + }).not.toChangeTo(() => value, 42n), + ).toThrowErrorMatchingSnapshot(); + }); }); diff --git a/types/index.d.ts b/types/index.d.ts index dc6a2752..e4c4c49c 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -60,6 +60,11 @@ interface CustomMatchers extends Record { */ toBeBefore(date: Date): R; + /** + * Use `.toBeBigInt` when checking if a value is a `BigInt`. + */ + toBeBigInt(): R; + /** * Use `.toIncludeAllMembers` when checking if an `Array` contains all of the same members of a given set. * @param {Array.<*>} members @@ -179,7 +184,7 @@ interface CustomMatchers extends Record { toHaveBeenCalledExactlyOnceWith(...args: unknown[]): R; /** - * Use `.toBeNumber` when checking if a value is a `Number`. + * Use `.toBeNumber` when checking if a value is a `Number` or `BigInt`. */ toBeNumber(): R; @@ -189,27 +194,27 @@ interface CustomMatchers extends Record { toBeNaN(): R; /** - * Use `.toBeFinite` when checking if a value is a `Number`, not `NaN` or `Infinity`. + * Use `.toBeFinite` when checking if a value is a `Number`, not `NaN` or `Infinity`, or a `BigInt`. */ toBeFinite(): R; /** - * Use `.toBePositive` when checking if a value is a positive `Number`. + * Use `.toBePositive` when checking if a value is a positive `Number` or `BigInt`. */ toBePositive(): R; /** - * Use `.toBeNegative` when checking if a value is a negative `Number`. + * Use `.toBeNegative` when checking if a value is a negative `Number` or `BigInt`. */ toBeNegative(): R; /** - * Use `.toBeEven` when checking if a value is an even `Number`. + * Use `.toBeEven` when checking if a value is an even `Number` or `BigInt`. */ toBeEven(): R; /** - * Use `.toBeOdd` when checking if a value is an odd `Number`. + * Use `.toBeOdd` when checking if a value is an odd `Number` or `BigInt`. */ toBeOdd(): R; @@ -223,11 +228,12 @@ interface CustomMatchers extends Record { /** * Use `.toBeInRange` when checking if an array has elements in range min (inclusive) and max (exclusive). + * Supports both number and BigInt values. * * @param min * @param max */ - toBeInRange(min: number, max: number): R; + toBeInRange(min: number | bigint, max: number | bigint): R; /** * Use `.toBeObject` when checking if a value is an `Object`. @@ -243,9 +249,9 @@ interface CustomMatchers extends Record { /** * Use `.toChangeBy` when checking if a value changed by an amount. * @param {Function} checker - * @param {Number} by + * @param {Number|BigInt} by */ - toChangeBy(checker: () => number, by?: number): R; + toChangeBy(checker: () => number | bigint, by?: number | bigint): R; /** * Use `.toChangeTo` when checking if a value changed to a specific value. @@ -523,6 +529,11 @@ declare namespace jest { */ toBeBefore(date: Date): R; + /** + * Use `.toBeBigInt` when checking if a value is a `BigInt`. + */ + toBeBigInt(): R; + /** * Use `.toIncludeAllMembers` when checking if an `Array` contains all the same members of a given set. * @param {Array.<*>} members @@ -642,7 +653,7 @@ declare namespace jest { toHaveBeenCalledExactlyOnceWith(...args: unknown[]): R; /** - * Use `.toBeNumber` when checking if a value is a `Number`. + * Use `.toBeNumber` when checking if a value is a `Number` or `BigInt`. */ toBeNumber(): R; @@ -652,27 +663,27 @@ declare namespace jest { toBeNaN(): R; /** - * Use `.toBeFinite` when checking if a value is a `Number`, not `NaN` or `Infinity`. + * Use `.toBeFinite` when checking if a value is a `Number`, not `NaN` or `Infinity`, or a `BigInt`. */ toBeFinite(): R; /** - * Use `.toBePositive` when checking if a value is a positive `Number`. + * Use `.toBePositive` when checking if a value is a positive `Number` or `BigInt`. */ toBePositive(): R; /** - * Use `.toBeNegative` when checking if a value is a negative `Number`. + * Use `.toBeNegative` when checking if a value is a negative `Number` or `BigInt`. */ toBeNegative(): R; /** - * Use `.toBeEven` when checking if a value is an even `Number`. + * Use `.toBeEven` when checking if a value is an even `Number` or `BigInt`. */ toBeEven(): R; /** - * Use `.toBeOdd` when checking if a value is an odd `Number`. + * Use `.toBeOdd` when checking if a value is an odd `Number` or `BigInt`. */ toBeOdd(): R; @@ -686,11 +697,12 @@ declare namespace jest { /** * Use `.toBeInRange` when checking if an array has elements in range min (inclusive) and max (exclusive). + * Supports both number and BigInt values. * * @param min * @param max */ - toBeInRange(min: number, max: number): R; + toBeInRange(min: number | bigint, max: number | bigint): R; /** * Use `.toBeInteger` when checking if a value is an integer. @@ -711,9 +723,9 @@ declare namespace jest { /** * Use `.toChangeBy` when checking if a value changed by an amount. * @param {Function} checker - * @param {Number} by + * @param {Number|BigInt} by */ - toChangeBy(checker: () => number, by?: number): R; + toChangeBy(checker: () => number | bigint, by?: number | bigint): R; /** * Use `.toChangeTo` when checking if a value changed to a specific value. diff --git a/website/docs/matchers/number.mdx b/website/docs/matchers/number.mdx index 331a05c8..9137605e 100644 --- a/website/docs/matchers/number.mdx +++ b/website/docs/matchers/number.mdx @@ -26,11 +26,12 @@ Use `.toBeNaN` when checking a value is `NaN`. ### .toBeFinite() -Use `.toBeFinite` when checking if a value is a `Number`, not `NaN` or `Infinity`. +Use `.toBeFinite` when checking if a value is a `Number`, not `NaN` or `Infinity`, or a `BigInt`. - {`test('passes when value is a finite number', () => { + {`test('passes when value is a finite number or bigint', () => { expect(1).toBeFinite(); + expect(BigInt(123456789)).toBeFinite(); expect(Infinity).not.toBeFinite(); expect(NaN).not.toBeFinite(); });`} @@ -38,50 +39,60 @@ Use `.toBeFinite` when checking if a value is a `Number`, not `NaN` or `Infinity ### .toBePositive() -Use `.toBePositive` when checking if a value is a positive `Number`. +Use `.toBePositive` when checking if a value is a positive `Number` or `BigInt`. - {`test('passes when value is a positive number', () => { + {`test('passes when value is a positive number or bigint', () => { expect(1).toBePositive(); + expect(BigInt(42)).toBePositive(); expect(Infinity).not.toBePositive(); expect(-1).not.toBePositive(); + expect(-BigInt(42)).not.toBePositive(); expect(NaN).not.toBePositive(); });`} ### .toBeNegative() -Use `.toBeNegative` when checking if a value is a negative `Number`. +Use `.toBeNegative` when checking if a value is a negative `Number` or `BigInt`. - {`test('passes when value is a negative number', () => { + {`test('passes when value is a negative number or bigint', () => { expect(-1).toBeNegative(); + expect(BigInt(-123)).toBeNegative(); expect(-Infinity).not.toBeNegative(); expect(1).not.toBeNegative(); + expect(BigInt(123)).not.toBeNegative(); expect(NaN).not.toBeNegative(); });`} ### .toBeEven() -Use `.toBeEven` when checking if a value is an even `Number`. +Use `.toBeEven` when checking if a value is an even `Number` or `BigInt`. - {`test('passes when value is an even number', () => { + {`test('passes when value is an even number or bigint', () => { expect(2).toBeEven(); + expect(BigInt(2)).toBeEven(); + expect(BigInt(-4)).toBeEven(); expect(1).not.toBeEven(); + expect(BigInt(1)).not.toBeEven(); expect(NaN).not.toBeEven(); });`} ### .toBeOdd() -Use `.toBeOdd` when checking if a value is an odd `Number`. +Use `.toBeOdd` when checking if a value is an odd `Number` or `BigInt`. - {`test('passes when value is an odd number', () => { + {`test('passes when value is an odd number or bigint', () => { expect(1).toBeOdd(); + expect(BigInt(1)).toBeOdd(); + expect(BigInt(-3)).toBeOdd(); expect(2).not.toBeOdd(); + expect(BigInt(2)).not.toBeOdd(); expect(NaN).not.toBeOdd(); });`} @@ -100,12 +111,54 @@ Use `.toBeWithin` when checking if a number is in between the given bounds of: s ### .toBeInteger() -Use `.toBeInteger` when checking if a number is an integer. +Use `.toBeInteger` when checking if a value is an integer `Number` or any `BigInt`. - {`test('passes when value is an integer', () => { + {`test('passes when value is an integer or bigint', () => { expect(1).toBeInteger(); expect(1.0).toBeInteger(); + expect(BigInt(42)).toBeInteger(); expect(1.1).not.toBeInteger(); });`} + +### .toBeInRange() + +Use `.toBeInRange` when checking if an array's elements all fall within a range (min inclusive, max exclusive). Now supports `BigInt` values. + + + {`test('passes when all array elements are in range', () => { + expect([4, 5, 7, 9]).toBeInRange(4, 10); + expect([4n, 5n, 7n, 9n]).toBeInRange(4n, 10n); + expect([4, 5, 10]).not.toBeInRange(4, 10); +});`} + + +### .toChangeBy() + +Use `.toChangeBy` when checking if a function call changes a value by a specified amount. Now supports `BigInt` values. + + + {`test('passes when value changes by the specified amount', () => { + let value = 0; + let bigValue = BigInt(0); + expect(() => { value += 2 }).toChangeBy(() => value, 2); + expect(() => { bigValue += 2n }).toChangeBy(() => bigValue, 2n); + expect(() => { value += 1 }).not.toChangeBy(() => value, 2); +});`} + + +### .toChangeTo() + +Use `.toChangeTo` when checking if a function call changes a value to a specified value. + + + {`test('passes when value changes to the specified value', () => { + let value = 0; + let bigValue = BigInt(0); + expect(() => { value = 2 }).toChangeTo(() => value, 2); + expect(() => { bigValue = 42n }).toChangeTo(() => bigValue, 42n); + expect(() => { value = 3 }).not.toChangeTo(() => value, 2); +});`} + +