mirror of https://github.com/facebook/jest.git
515 lines
16 KiB
TypeScript
515 lines
16 KiB
TypeScript
/**
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
*/
|
|
|
|
import jestExpect from '../';
|
|
import {
|
|
any,
|
|
anything,
|
|
arrayContaining,
|
|
arrayNotContaining,
|
|
closeTo,
|
|
notCloseTo,
|
|
objectContaining,
|
|
objectNotContaining,
|
|
stringContaining,
|
|
stringMatching,
|
|
stringNotContaining,
|
|
stringNotMatching,
|
|
} from '../asymmetricMatchers';
|
|
|
|
test('Any.asymmetricMatch()', () => {
|
|
class Thing {}
|
|
|
|
for (const test of [
|
|
any(String).asymmetricMatch('jest'),
|
|
any(Number).asymmetricMatch(1),
|
|
any(Function).asymmetricMatch(() => {}),
|
|
any(Boolean).asymmetricMatch(true),
|
|
any(BigInt).asymmetricMatch(1n),
|
|
any(Symbol).asymmetricMatch(Symbol()),
|
|
any(Object).asymmetricMatch({}),
|
|
any(Object).asymmetricMatch(null),
|
|
any(Array).asymmetricMatch([]),
|
|
any(Thing).asymmetricMatch(new Thing()),
|
|
]) {
|
|
jestExpect(test).toBe(true);
|
|
}
|
|
});
|
|
|
|
test('Any.asymmetricMatch() on primitive wrapper classes', () => {
|
|
for (const test of [
|
|
/* eslint-disable no-new-wrappers, unicorn/new-for-builtins */
|
|
any(String).asymmetricMatch(new String('jest')),
|
|
any(Number).asymmetricMatch(new Number(1)),
|
|
// eslint-disable-next-line no-new-func
|
|
any(Function).asymmetricMatch(new Function('() => {}')),
|
|
any(Boolean).asymmetricMatch(new Boolean(true)),
|
|
any(BigInt).asymmetricMatch(Object(1n)),
|
|
any(Symbol).asymmetricMatch(Object(Symbol())),
|
|
/* eslint-enable */
|
|
]) {
|
|
jestExpect(test).toBe(true);
|
|
}
|
|
});
|
|
|
|
test('Any.toAsymmetricMatcher()', () => {
|
|
jestExpect(any(Number).toAsymmetricMatcher()).toBe('Any<Number>');
|
|
});
|
|
|
|
test('Any.toAsymmetricMatcher() with function name', () => {
|
|
for (const [name, fn] of [
|
|
['someFunc', function someFunc() {}],
|
|
['$someFunc', function $someFunc() {}],
|
|
[
|
|
'$someFunc2',
|
|
(function () {
|
|
function $someFunc2() {}
|
|
Object.defineProperty($someFunc2, 'name', {value: ''});
|
|
return $someFunc2;
|
|
})(),
|
|
],
|
|
[
|
|
'$someAsyncFunc',
|
|
(function () {
|
|
async function $someAsyncFunc() {}
|
|
Object.defineProperty($someAsyncFunc, 'name', {value: ''});
|
|
return $someAsyncFunc;
|
|
})(),
|
|
],
|
|
[
|
|
'$someGeneratorFunc',
|
|
(function () {
|
|
function* $someGeneratorFunc() {}
|
|
Object.defineProperty($someGeneratorFunc, 'name', {value: ''});
|
|
return $someGeneratorFunc;
|
|
})(),
|
|
],
|
|
[
|
|
'$someFuncWithFakeToString',
|
|
(function () {
|
|
function $someFuncWithFakeToString() {}
|
|
$someFuncWithFakeToString.toString = () => 'Fake to string';
|
|
return $someFuncWithFakeToString;
|
|
})(),
|
|
],
|
|
]) {
|
|
jestExpect(any(fn).toAsymmetricMatcher()).toBe(`Any<${name}>`);
|
|
}
|
|
});
|
|
|
|
test('Any throws when called with empty constructor', () => {
|
|
// @ts-expect-error: Testing runtime error
|
|
jestExpect(() => any()).toThrow(
|
|
'any() expects to be passed a constructor function. Please pass one or use anything() to match any object.',
|
|
);
|
|
});
|
|
|
|
test('Anything matches any type', () => {
|
|
for (const test of [
|
|
anything().asymmetricMatch('jest'),
|
|
anything().asymmetricMatch(1),
|
|
anything().asymmetricMatch(() => {}),
|
|
anything().asymmetricMatch(true),
|
|
anything().asymmetricMatch({}),
|
|
anything().asymmetricMatch([]),
|
|
]) {
|
|
jestExpect(test).toBe(true);
|
|
}
|
|
});
|
|
|
|
test('Anything does not match null and undefined', () => {
|
|
for (const test of [
|
|
anything().asymmetricMatch(null),
|
|
anything().asymmetricMatch(undefined),
|
|
]) {
|
|
jestExpect(test).toBe(false);
|
|
}
|
|
});
|
|
|
|
test('Anything.toAsymmetricMatcher()', () => {
|
|
jestExpect(anything().toAsymmetricMatcher()).toBe('Anything');
|
|
});
|
|
|
|
test('ArrayContaining matches', () => {
|
|
for (const test of [
|
|
arrayContaining([]).asymmetricMatch('jest'),
|
|
arrayContaining(['foo']).asymmetricMatch(['foo']),
|
|
arrayContaining(['foo']).asymmetricMatch(['foo', 'bar']),
|
|
arrayContaining([]).asymmetricMatch({}),
|
|
]) {
|
|
jestExpect(test).toEqual(true);
|
|
}
|
|
});
|
|
|
|
test('ArrayContaining does not match', () => {
|
|
jestExpect(arrayContaining(['foo']).asymmetricMatch(['bar'])).toBe(false);
|
|
});
|
|
|
|
test('ArrayContaining throws for non-arrays', () => {
|
|
jestExpect(() => {
|
|
// @ts-expect-error: Testing runtime error
|
|
arrayContaining('foo').asymmetricMatch([]);
|
|
}).toThrow("You must provide an array to ArrayContaining, not 'string'.");
|
|
});
|
|
|
|
test('ArrayNotContaining matches', () => {
|
|
jestExpect(arrayNotContaining(['foo']).asymmetricMatch(['bar'])).toBe(true);
|
|
});
|
|
|
|
test('ArrayNotContaining does not match', () => {
|
|
for (const test of [
|
|
arrayNotContaining([]).asymmetricMatch('jest'),
|
|
arrayNotContaining(['foo']).asymmetricMatch(['foo']),
|
|
arrayNotContaining(['foo']).asymmetricMatch(['foo', 'bar']),
|
|
arrayNotContaining([]).asymmetricMatch({}),
|
|
]) {
|
|
jestExpect(test).toEqual(false);
|
|
}
|
|
});
|
|
|
|
test('ArrayNotContaining throws for non-arrays', () => {
|
|
jestExpect(() => {
|
|
// @ts-expect-error: Testing runtime error
|
|
arrayNotContaining('foo').asymmetricMatch([]);
|
|
}).toThrow("You must provide an array to ArrayNotContaining, not 'string'.");
|
|
});
|
|
|
|
test('ObjectContaining matches', () => {
|
|
const foo = Symbol('foo');
|
|
for (const test of [
|
|
objectContaining({}).asymmetricMatch('jest'),
|
|
objectContaining({foo: 'foo'}).asymmetricMatch({foo: 'foo', jest: 'jest'}),
|
|
objectContaining({foo: undefined}).asymmetricMatch({foo: undefined}),
|
|
objectContaining({first: objectContaining({second: {}})}).asymmetricMatch({
|
|
first: {second: {}},
|
|
}),
|
|
objectContaining({foo: Buffer.from('foo')}).asymmetricMatch({
|
|
foo: Buffer.from('foo'),
|
|
jest: 'jest',
|
|
}),
|
|
objectContaining({[foo]: 'foo'}).asymmetricMatch({[foo]: 'foo'}),
|
|
]) {
|
|
jestExpect(test).toEqual(true);
|
|
}
|
|
});
|
|
|
|
test('ObjectContaining does not match', () => {
|
|
const foo = Symbol('foo');
|
|
const bar = Symbol('bar');
|
|
for (const test of [
|
|
objectContaining({foo: 'foo'}).asymmetricMatch({bar: 'bar'}),
|
|
objectContaining({foo: 'foo'}).asymmetricMatch({foo: 'foox'}),
|
|
objectContaining({foo: undefined}).asymmetricMatch({}),
|
|
objectContaining({
|
|
answer: 42,
|
|
foo: {bar: 'baz', foobar: 'qux'},
|
|
}).asymmetricMatch({foo: {bar: 'baz'}}),
|
|
objectContaining({[foo]: 'foo'}).asymmetricMatch({[bar]: 'bar'}),
|
|
]) {
|
|
jestExpect(test).toEqual(false);
|
|
}
|
|
});
|
|
|
|
test('ObjectContaining matches defined properties', () => {
|
|
const definedPropertyObject = {};
|
|
Object.defineProperty(definedPropertyObject, 'foo', {get: () => 'bar'});
|
|
jestExpect(
|
|
objectContaining({foo: 'bar'}).asymmetricMatch(definedPropertyObject),
|
|
).toBe(true);
|
|
});
|
|
|
|
test('ObjectContaining matches prototype properties', () => {
|
|
const prototypeObject = {foo: 'bar'};
|
|
let obj;
|
|
|
|
if (Object.create) {
|
|
obj = Object.create(prototypeObject);
|
|
} else {
|
|
function Foo() {}
|
|
Foo.prototype = prototypeObject;
|
|
Foo.prototype.constructor = Foo;
|
|
obj = new (Foo as any)();
|
|
}
|
|
jestExpect(objectContaining({foo: 'bar'}).asymmetricMatch(obj)).toBe(true);
|
|
});
|
|
|
|
test('ObjectContaining throws for non-objects', () => {
|
|
// @ts-expect-error: Testing runtime error
|
|
jestExpect(() => objectContaining(1337).asymmetricMatch()).toThrow(
|
|
"You must provide an object to ObjectContaining, not 'number'.",
|
|
);
|
|
});
|
|
|
|
test('ObjectContaining does not mutate the sample', () => {
|
|
const sample = {foo: {bar: {}}};
|
|
const sample_json = JSON.stringify(sample);
|
|
expect({foo: {bar: {}}}).toEqual(expect.objectContaining(sample));
|
|
|
|
expect(JSON.stringify(sample)).toEqual(sample_json);
|
|
});
|
|
|
|
test('ObjectNotContaining matches', () => {
|
|
const foo = Symbol('foo');
|
|
const bar = Symbol('bar');
|
|
for (const test of [
|
|
objectContaining({}).asymmetricMatch(null),
|
|
objectContaining({}).asymmetricMatch(undefined),
|
|
objectNotContaining({[foo]: 'foo'}).asymmetricMatch({[bar]: 'bar'}),
|
|
objectNotContaining({foo: 'foo'}).asymmetricMatch({bar: 'bar'}),
|
|
objectNotContaining({foo: 'foo'}).asymmetricMatch({foo: 'foox'}),
|
|
objectNotContaining({foo: undefined}).asymmetricMatch({}),
|
|
objectNotContaining({
|
|
first: objectNotContaining({second: {}}),
|
|
}).asymmetricMatch({first: {second: {}}}),
|
|
objectNotContaining({first: {second: {}, third: {}}}).asymmetricMatch({
|
|
first: {second: {}},
|
|
}),
|
|
objectNotContaining({first: {second: {}}}).asymmetricMatch({
|
|
first: {second: {}, third: {}},
|
|
}),
|
|
objectNotContaining({foo: 'foo', jest: 'jest'}).asymmetricMatch({
|
|
foo: 'foo',
|
|
}),
|
|
]) {
|
|
jestExpect(test).toEqual(true);
|
|
}
|
|
});
|
|
|
|
test('ObjectNotContaining does not match', () => {
|
|
for (const test of [
|
|
objectNotContaining({}).asymmetricMatch('jest'),
|
|
objectNotContaining({foo: 'foo'}).asymmetricMatch({
|
|
foo: 'foo',
|
|
jest: 'jest',
|
|
}),
|
|
objectNotContaining({foo: undefined}).asymmetricMatch({foo: undefined}),
|
|
objectNotContaining({first: {second: {}}}).asymmetricMatch({
|
|
first: {second: {}},
|
|
}),
|
|
objectNotContaining({
|
|
first: objectContaining({second: {}}),
|
|
}).asymmetricMatch({first: {second: {}}}),
|
|
objectNotContaining({}).asymmetricMatch(null),
|
|
objectNotContaining({}).asymmetricMatch(undefined),
|
|
objectNotContaining({}).asymmetricMatch({}),
|
|
]) {
|
|
jestExpect(test).toEqual(false);
|
|
}
|
|
});
|
|
|
|
test('ObjectNotContaining inverts ObjectContaining', () => {
|
|
for (const [sample, received] of [
|
|
[{}, null],
|
|
[{foo: 'foo'}, {foo: 'foo', jest: 'jest'}],
|
|
[{foo: 'foo', jest: 'jest'}, {foo: 'foo'}],
|
|
[{foo: undefined}, {foo: undefined}],
|
|
[{foo: undefined}, {}],
|
|
[{first: {second: {}}}, {first: {second: {}}}],
|
|
[{first: objectContaining({second: {}})}, {first: {second: {}}}],
|
|
[{first: objectNotContaining({second: {}})}, {first: {second: {}}}],
|
|
[{}, {foo: undefined}],
|
|
] as const) {
|
|
jestExpect(objectNotContaining(sample).asymmetricMatch(received)).toEqual(
|
|
!objectContaining(sample).asymmetricMatch(received),
|
|
);
|
|
}
|
|
});
|
|
|
|
test('ObjectNotContaining throws for non-objects', () => {
|
|
jestExpect(() => {
|
|
// @ts-expect-error: Testing runtime error
|
|
objectNotContaining(1337).asymmetricMatch();
|
|
}).toThrow(
|
|
"You must provide an object to ObjectNotContaining, not 'number'.",
|
|
);
|
|
});
|
|
|
|
test('StringContaining matches string against string', () => {
|
|
jestExpect(stringContaining('en*').asymmetricMatch('queen*')).toBe(true);
|
|
jestExpect(stringContaining('en').asymmetricMatch('queue')).toBe(false);
|
|
});
|
|
|
|
test('StringContaining throws if expected value is not string', () => {
|
|
jestExpect(() => {
|
|
// @ts-expect-error: Testing runtime error
|
|
stringContaining([1]).asymmetricMatch('queen');
|
|
}).toThrow('Expected is not a string');
|
|
});
|
|
|
|
test('StringContaining returns false if received value is not string', () => {
|
|
jestExpect(stringContaining('en*').asymmetricMatch(1)).toBe(false);
|
|
});
|
|
|
|
test('StringNotContaining matches string against string', () => {
|
|
jestExpect(stringNotContaining('en*').asymmetricMatch('queen*')).toBe(false);
|
|
jestExpect(stringNotContaining('en').asymmetricMatch('queue')).toBe(true);
|
|
});
|
|
|
|
test('StringNotContaining throws if expected value is not string', () => {
|
|
jestExpect(() => {
|
|
// @ts-expect-error: Testing runtime error
|
|
stringNotContaining([1]).asymmetricMatch('queen');
|
|
}).toThrow('Expected is not a string');
|
|
});
|
|
|
|
test('StringNotContaining returns true if received value is not string', () => {
|
|
jestExpect(stringNotContaining('en*').asymmetricMatch(1)).toBe(true);
|
|
});
|
|
|
|
test('StringMatching matches string against regexp', () => {
|
|
jestExpect(stringMatching(/en/).asymmetricMatch('queen')).toBe(true);
|
|
jestExpect(stringMatching(/en/).asymmetricMatch('queue')).toBe(false);
|
|
});
|
|
|
|
test('StringMatching matches string against string', () => {
|
|
jestExpect(stringMatching('en').asymmetricMatch('queen')).toBe(true);
|
|
jestExpect(stringMatching('en').asymmetricMatch('queue')).toBe(false);
|
|
});
|
|
|
|
test('StringMatching throws if expected value is neither string nor regexp', () => {
|
|
jestExpect(() => {
|
|
// @ts-expect-error: Testing runtime error
|
|
stringMatching([1]).asymmetricMatch('queen');
|
|
}).toThrow('Expected is not a String or a RegExp');
|
|
});
|
|
|
|
test('StringMatching returns false if received value is not string', () => {
|
|
jestExpect(stringMatching('en').asymmetricMatch(1)).toBe(false);
|
|
});
|
|
|
|
test('StringMatching returns false even if coerced non-string received value matches pattern', () => {
|
|
jestExpect(stringMatching('null').asymmetricMatch(null)).toBe(false);
|
|
});
|
|
|
|
test('StringNotMatching matches string against regexp', () => {
|
|
jestExpect(stringNotMatching(/en/).asymmetricMatch('queen')).toBe(false);
|
|
jestExpect(stringNotMatching(/en/).asymmetricMatch('queue')).toBe(true);
|
|
});
|
|
|
|
test('StringNotMatching matches string against string', () => {
|
|
jestExpect(stringNotMatching('en').asymmetricMatch('queen')).toBe(false);
|
|
jestExpect(stringNotMatching('en').asymmetricMatch('queue')).toBe(true);
|
|
});
|
|
|
|
test('StringNotMatching throws if expected value is neither string nor regexp', () => {
|
|
jestExpect(() => {
|
|
// @ts-expect-error: Testing runtime error
|
|
stringNotMatching([1]).asymmetricMatch('queen');
|
|
}).toThrow('Expected is not a String or a RegExp');
|
|
});
|
|
|
|
test('StringNotMatching returns true if received value is not string', () => {
|
|
jestExpect(stringNotMatching('en').asymmetricMatch(1)).toBe(true);
|
|
});
|
|
|
|
describe('closeTo', () => {
|
|
for (const [expected, received] of [
|
|
[0, 0],
|
|
[0, 0.001],
|
|
[1.23, 1.229],
|
|
[1.23, 1.226],
|
|
[1.23, 1.225],
|
|
[1.23, 1.234],
|
|
[Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY],
|
|
[Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY],
|
|
]) {
|
|
test(`${expected} closeTo ${received} return true`, () => {
|
|
jestExpect(closeTo(expected).asymmetricMatch(received)).toBe(true);
|
|
});
|
|
test(`${expected} notCloseTo ${received} return false`, () => {
|
|
jestExpect(notCloseTo(expected).asymmetricMatch(received)).toBe(false);
|
|
});
|
|
}
|
|
|
|
for (const [expected, received] of [
|
|
[0, 0.01],
|
|
[1, 1.23],
|
|
[1.23, 1.224_999_9],
|
|
[Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY],
|
|
[Number.POSITIVE_INFINITY, 1.23],
|
|
[Number.NEGATIVE_INFINITY, -1.23],
|
|
]) {
|
|
test(`${expected} closeTo ${received} return false`, () => {
|
|
jestExpect(closeTo(expected).asymmetricMatch(received)).toBe(false);
|
|
});
|
|
test(`${expected} notCloseTo ${received} return true`, () => {
|
|
jestExpect(notCloseTo(expected).asymmetricMatch(received)).toBe(true);
|
|
});
|
|
}
|
|
|
|
for (const [expected, received, precision] of [
|
|
[0, 0.1, 0],
|
|
[0, 0.0001, 3],
|
|
[0, 0.000_004, 5],
|
|
[2.000_000_2, 2, 5],
|
|
]) {
|
|
test(`${expected} closeTo ${received} with precision ${precision} return true`, () => {
|
|
jestExpect(closeTo(expected, precision).asymmetricMatch(received)).toBe(
|
|
true,
|
|
);
|
|
});
|
|
test(`${expected} notCloseTo ${received} with precision ${precision} return false`, () => {
|
|
jestExpect(
|
|
notCloseTo(expected, precision).asymmetricMatch(received),
|
|
).toBe(false);
|
|
});
|
|
}
|
|
|
|
for (const [expected, received, precision] of [
|
|
[3.141_592e-7, 3e-7, 8],
|
|
[56_789, 51_234, -4],
|
|
]) {
|
|
test(`${expected} closeTo ${received} with precision ${precision} return false`, () => {
|
|
jestExpect(closeTo(expected, precision).asymmetricMatch(received)).toBe(
|
|
false,
|
|
);
|
|
});
|
|
test(`${expected} notCloseTo ${received} with precision ${precision} return true`, () => {
|
|
jestExpect(
|
|
notCloseTo(expected, precision).asymmetricMatch(received),
|
|
).toBe(true);
|
|
});
|
|
}
|
|
|
|
test('closeTo throw if expected is not number', () => {
|
|
jestExpect(() => {
|
|
// @ts-expect-error: Testing runtime error
|
|
closeTo('a');
|
|
}).toThrow('Expected is not a Number');
|
|
});
|
|
|
|
test('notCloseTo throw if expected is not number', () => {
|
|
jestExpect(() => {
|
|
// @ts-expect-error: Testing runtime error
|
|
notCloseTo('a');
|
|
}).toThrow('Expected is not a Number');
|
|
});
|
|
|
|
test('closeTo throw if precision is not number', () => {
|
|
jestExpect(() => {
|
|
// @ts-expect-error: Testing runtime error
|
|
closeTo(1, 'a');
|
|
}).toThrow('Precision is not a Number');
|
|
});
|
|
|
|
test('notCloseTo throw if precision is not number', () => {
|
|
jestExpect(() => {
|
|
// @ts-expect-error: Testing runtime error
|
|
notCloseTo(1, 'a');
|
|
}).toThrow('Precision is not a Number');
|
|
});
|
|
|
|
test('closeTo return false if received is not number', () => {
|
|
jestExpect(closeTo(1).asymmetricMatch('a')).toBe(false);
|
|
});
|
|
|
|
test('notCloseTo return false if received is not number', () => {
|
|
jestExpect(notCloseTo(1).asymmetricMatch('a')).toBe(false);
|
|
});
|
|
});
|