Skip to content

feat: Add BigInt support to number matchers (closes #591) #772

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

Merged
merged 1 commit into from
Jun 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/better-lights-lay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'jest-extended': major
---

Add BigInt support to number matchers
and add new toBeBigInt matchers
1 change: 0 additions & 1 deletion all.js
Original file line number Diff line number Diff line change
@@ -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');
1 change: 1 addition & 0 deletions src/matchers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
20 changes: 20 additions & 0 deletions src/matchers/toBeBigInt.ts
Original file line number Diff line number Diff line change
@@ -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)}`,
};
}
7 changes: 3 additions & 4 deletions src/matchers/toBeEven.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
2 changes: 1 addition & 1 deletion src/matchers/toBeFinite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
9 changes: 7 additions & 2 deletions src/matchers/toBeInRange.ts
Original file line number Diff line number Diff line change
@@ -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;

Expand Down
6 changes: 2 additions & 4 deletions src/matchers/toBeNegative.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -18,6 +19,3 @@ export function toBeNegative(actual: unknown) {
` ${printReceived(actual)}`,
};
}

const isNumber = (value: any) => !isNaN(parseInt(value));
const isNegative = (value: any) => value < 0;
10 changes: 7 additions & 3 deletions src/matchers/toBeOdd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
};
10 changes: 7 additions & 3 deletions src/matchers/toBePositive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
};
11 changes: 9 additions & 2 deletions src/matchers/toChangeBy.ts
Original file line number Diff line number Diff line change
@@ -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)}
Expand Down
2 changes: 1 addition & 1 deletion src/matchers/toChangeTo.ts
Original file line number Diff line number Diff line change
@@ -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<E = unknown>(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;

Expand Down
22 changes: 22 additions & 0 deletions test/matchers/__snapshots__/toBeBigInt.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`.not.toBeInteger fails when given integer 1`] = `
"<dim>expect(</intensity><red>received</color><dim>).not.toBeBigInt()</intensity>

Expected value to not be an BigInt received:
<red>1n</color>"
`;

exports[`.toBeBigInt fails when given fraction 1`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeBigInt()</intensity>

Expected value to be a BigInt received:
<red>1.5</color>"
`;

exports[`.toBeBigInt fails when given integer 1`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeBigInt()</intensity>

Expected value to be a BigInt received:
<red>1</color>"
`;
21 changes: 21 additions & 0 deletions test/matchers/__snapshots__/toBeEven.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,12 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`.not.toBeEven fails when given an even BigInt 1`] = `
"<dim>expect(</intensity><red>received</color><dim>).not.toBeEven()</intensity>

Expected value to not be an even number received:
<red>2n</color>"
`;

exports[`.not.toBeEven fails when given an even number 1`] = `
"<dim>expect(</intensity><red>received</color><dim>).not.toBeEven()</intensity>

Expected value to not be an even number received:
<red>2</color>"
`;

exports[`.toBeEven fails when given odd BigInt 1`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeEven()</intensity>

Expected value to be an even number received:
<red>1n</color>"
`;

exports[`.toBeEven fails when given odd BigInt 2`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeEven()</intensity>

Expected value to be an even number received:
<red>-3n</color>"
`;

exports[`.toBeEven fails when not given an even number 1`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeEven()</intensity>

Expand Down
7 changes: 7 additions & 0 deletions test/matchers/__snapshots__/toBeFinite.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`.not.toBeFinite fails when given a BigInt value 1`] = `
"<dim>expect(</intensity><red>received</color><dim>).not.toBeFinite()</intensity>

Expected value to not be finite received:
<red>1n</color>"
`;

exports[`.not.toBeFinite fails when given a finite number 1`] = `
"<dim>expect(</intensity><red>received</color><dim>).not.toBeFinite()</intensity>

Expand Down
15 changes: 15 additions & 0 deletions test/matchers/__snapshots__/toBeInRange.test.ts.snap
Original file line number Diff line number Diff line change
@@ -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`] = `
"<dim>expect(</intensity><red>received</color><dim>).not.toBeInRange(</intensity><green>expected</color><dim>)</intensity>

Expected Array to not be in range <green>4n</color>, <green>10n</color>
Received:
<red>[4n, 5n, 7n, 9n]</color>"
`;

exports[`.not.toBeInRange fails when given array is in the given range 1`] = `
"<dim>expect(</intensity><red>received</color><dim>).not.toBeInRange(</intensity><green>expected</color><dim>)</intensity>

Expand All @@ -8,6 +16,13 @@ Received:
<red>[4, 5, 7, 9]</color>"
`;

exports[`.toBeInRange fails when given BigInt array is not in a given range 1`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeInRange(</intensity><green>expected</color><dim>)</intensity>

Expected: Array elements to be in range (<green>4n</color>, <green>8n</color>)
Received: Array element out of range <red>9n</color>"
`;

exports[`.toBeInRange fails when given array is not in a given range 1`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeInRange(</intensity><green>expected</color><dim>)</intensity>

Expand Down
21 changes: 21 additions & 0 deletions test/matchers/__snapshots__/toBeNegative.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`.not.toBeNegative fails when given negative BigInt 1`] = `
"<dim>expect(</intensity><red>received</color><dim>).not.toBeNegative()</intensity>

Expected value to not be a negative number received:
<red>-1n</color>"
`;

exports[`.not.toBeNegative fails when given negative number 1`] = `
"<dim>expect(</intensity><red>received</color><dim>).not.toBeNegative()</intensity>

Expand All @@ -14,9 +21,23 @@ Expected value to be a negative number received:
<red>Infinity</color>"
`;

exports[`.toBeNegative fails when given positive BigInt 1`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeNegative()</intensity>

Expected value to be a negative number received:
<red>1n</color>"
`;

exports[`.toBeNegative fails when given positive number 1`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeNegative()</intensity>

Expected value to be a negative number received:
<red>1</color>"
`;

exports[`.toBeNegative fails when given zero BigInt 1`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeNegative()</intensity>

Expected value to be a negative number received:
<red>0n</color>"
`;
22 changes: 18 additions & 4 deletions test/matchers/__snapshots__/toBeOdd.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -39,33 +39,47 @@ exports[`.toBeOdd fails when given not given an odd number 5`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeOdd()</intensity>

Expected value to be odd received:
<red>{}</color>"
<red>2n</color>"
`;

exports[`.toBeOdd fails when given not given an odd number 6`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeOdd()</intensity>

Expected value to be odd received:
<red>[Function anonymous]</color>"
<red>{}</color>"
`;

exports[`.toBeOdd fails when given not given an odd number 7`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeOdd()</intensity>

Expected value to be odd received:
<red>undefined</color>"
<red>[Function anonymous]</color>"
`;

exports[`.toBeOdd fails when given not given an odd number 8`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeOdd()</intensity>

Expected value to be odd received:
<red>null</color>"
<red>undefined</color>"
`;

exports[`.toBeOdd fails when given not given an odd number 9`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeOdd()</intensity>

Expected value to be odd received:
<red>null</color>"
`;

exports[`.toBeOdd fails when given not given an odd number 10`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeOdd()</intensity>

Expected value to be odd received:
<red>NaN</color>"
`;

exports[`.toBeOdd fails when given not given an odd number 11`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeOdd()</intensity>

Expected value to be odd received:
<red>Symbol(foo)</color>"
`;
14 changes: 14 additions & 0 deletions test/matchers/__snapshots__/toBePositive.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`.not.toBePositive fails when given a positive BigInt 1`] = `
"<dim>expect(</intensity><red>received</color><dim>).not.toBePositive()</intensity>

Expected value to not be positive received:
<red>5n</color>"
`;

exports[`.not.toBePositive fails when given a positive number 1`] = `
"<dim>expect(</intensity><red>received</color><dim>).not.toBePositive()</intensity>

Expected value to not be positive received:
<red>5</color>"
`;

exports[`.toBePositive fails when not given a positive BigInt 1`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBePositive()</intensity>

Expected value to be positive received:
<red>-1n</color>"
`;

exports[`.toBePositive fails when not given a positive number 1`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBePositive()</intensity>

Expand Down
Loading