923 lines
27 KiB
TypeScript
923 lines
27 KiB
TypeScript
|
|
/**
|
||
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates. All Rights Reserved.
|
||
|
|
* Modifications copyright (c) Microsoft Corporation.
|
||
|
|
*
|
||
|
|
* This source code is licensed under the MIT license found here
|
||
|
|
* https://github.com/jestjs/jest/blob/v30.2.0/LICENSE
|
||
|
|
*/
|
||
|
|
|
||
|
|
// Adapted from jest/packages/expect/src/__tests__/matchers.test.js,
|
||
|
|
// asymmetricMatchers.test.ts and toThrowMatchers.test.ts.
|
||
|
|
|
||
|
|
import { test, expect } from './pageTest';
|
||
|
|
|
||
|
|
test.describe('.toBe()', () => {
|
||
|
|
test('does not throw', () => {
|
||
|
|
expect('a').not.toBe('b');
|
||
|
|
expect('a').toBe('a');
|
||
|
|
expect(1).not.toBe(2);
|
||
|
|
expect(1).toBe(1);
|
||
|
|
expect(null).not.toBe(undefined);
|
||
|
|
expect(null).toBe(null);
|
||
|
|
expect(undefined).toBe(undefined);
|
||
|
|
expect(NaN).toBe(NaN);
|
||
|
|
expect(BigInt(1)).not.toBe(BigInt(2));
|
||
|
|
expect(BigInt(1)).not.toBe(1);
|
||
|
|
expect(BigInt(1)).toBe(BigInt(1));
|
||
|
|
});
|
||
|
|
|
||
|
|
for (const [i, [a, b]] of [
|
||
|
|
[1, 2],
|
||
|
|
[true, false],
|
||
|
|
[{}, {}],
|
||
|
|
[{ a: 1 }, { a: 1 }],
|
||
|
|
[{ a: 1 }, { a: 5 }],
|
||
|
|
[{ a: undefined, b: 2 }, { b: 2 }],
|
||
|
|
[new Date('2020-02-20'), new Date('2020-02-20')],
|
||
|
|
[/received/, /expected/],
|
||
|
|
[['abc'], ['cde']],
|
||
|
|
[[], []],
|
||
|
|
[null, undefined],
|
||
|
|
[-0, +0],
|
||
|
|
].entries()) {
|
||
|
|
test(`fails for: ${JSON.stringify(a)} and ${JSON.stringify(b)} #${i}`, () => {
|
||
|
|
expect(() => expect(a).toBe(b)).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const v of [false, 1, 'a', undefined, null, {}, []]) {
|
||
|
|
test(`fails for '${JSON.stringify(v)}' with '.not'`, () => {
|
||
|
|
expect(() => expect(v).not.toBe(v)).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
test('does not crash on circular references', () => {
|
||
|
|
const obj: any = {};
|
||
|
|
obj.circular = obj;
|
||
|
|
expect(() => expect(obj).toBe({})).toThrow();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('matcherResult property contains matcher name, expected and actual values', () => {
|
||
|
|
const actual = { a: 1 };
|
||
|
|
const expected = { a: 2 };
|
||
|
|
try {
|
||
|
|
expect(actual).toBe(expected);
|
||
|
|
} catch (error) {
|
||
|
|
expect(error.matcherResult).toEqual(expect.objectContaining({
|
||
|
|
actual,
|
||
|
|
expected,
|
||
|
|
name: 'toBe',
|
||
|
|
}));
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test.describe('.toStrictEqual()', () => {
|
||
|
|
class TestClassA {
|
||
|
|
a: number;
|
||
|
|
b: number;
|
||
|
|
constructor(a: number, b: number) {
|
||
|
|
this.a = a;
|
||
|
|
this.b = b;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
class TestClassB {
|
||
|
|
a: number;
|
||
|
|
b: number;
|
||
|
|
constructor(a: number, b: number) {
|
||
|
|
this.a = a;
|
||
|
|
this.b = b;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
test('does not ignore keys with undefined values', () => {
|
||
|
|
expect({ a: undefined, b: 2 }).not.toStrictEqual({ b: 2 });
|
||
|
|
});
|
||
|
|
|
||
|
|
test('does not ignore keys with undefined values inside an array', () => {
|
||
|
|
expect([{ a: undefined }]).not.toStrictEqual([{}]);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('passes when comparing same type', () => {
|
||
|
|
expect({ test: new TestClassA(1, 2) }).toStrictEqual({ test: new TestClassA(1, 2) });
|
||
|
|
});
|
||
|
|
|
||
|
|
test('does not pass for different types', () => {
|
||
|
|
expect({ test: new TestClassA(1, 2) }).not.toStrictEqual({ test: new TestClassB(1, 2) });
|
||
|
|
});
|
||
|
|
|
||
|
|
test('passes for matching buffers', () => {
|
||
|
|
expect(Uint8Array.from([1]).buffer).toStrictEqual(Uint8Array.from([1]).buffer);
|
||
|
|
expect(Uint8Array.from([]).buffer).toStrictEqual(Uint8Array.from([]).buffer);
|
||
|
|
expect(Uint8Array.from([9, 3]).buffer).toStrictEqual(Uint8Array.from([9, 3]).buffer);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('does not pass when ArrayBuffers are not equal', () => {
|
||
|
|
expect(Uint8Array.from([1, 2]).buffer).not.toStrictEqual(Uint8Array.from([0, 0]).buffer);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test.describe('.toEqual()', () => {
|
||
|
|
for (const [a, b] of [
|
||
|
|
[true, false],
|
||
|
|
[1, 2],
|
||
|
|
[0, Number.MIN_VALUE],
|
||
|
|
[{ a: 1 }, { a: 2 }],
|
||
|
|
['banana', 'apple'],
|
||
|
|
[null, undefined],
|
||
|
|
[[1], [2]],
|
||
|
|
[new Set([1, 2]), new Set()],
|
||
|
|
[new Set([1, 2]), new Set([1, 2, 3])],
|
||
|
|
[new Map([['a', 0]]), new Map([['b', 0]])],
|
||
|
|
[new Uint8Array([97, 98, 99]), new Uint8Array([97, 98, 100])],
|
||
|
|
[{ a: 1, b: 2 }, expect.objectContaining({ a: 2 })],
|
||
|
|
[[1, 3], expect.arrayContaining([1, 2])],
|
||
|
|
['abd', expect.stringContaining('bc')],
|
||
|
|
['abd', expect.stringMatching(/bc/i)],
|
||
|
|
[undefined, expect.anything()],
|
||
|
|
[undefined, expect.any(Function)],
|
||
|
|
].entries()) {
|
||
|
|
test(`{pass: false} expect().toEqual() #${a}`, () => {
|
||
|
|
expect(() => expect(b[0]).toEqual(b[1])).toThrow();
|
||
|
|
expect(b[0]).not.toEqual(b[1]);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const [a, b] of [
|
||
|
|
[true, true],
|
||
|
|
[1, 1],
|
||
|
|
[NaN, NaN],
|
||
|
|
['abc', 'abc'],
|
||
|
|
[[1], [1]],
|
||
|
|
[{}, {}],
|
||
|
|
[{ a: 99 }, { a: 99 }],
|
||
|
|
[new Set(), new Set()],
|
||
|
|
[new Set([1, 2]), new Set([1, 2])],
|
||
|
|
[new Set([1, 2]), new Set([2, 1])],
|
||
|
|
[new Map(), new Map()],
|
||
|
|
[new Uint8Array([97, 98, 99]), new Uint8Array([97, 98, 99])],
|
||
|
|
[{ a: 1, b: 2 }, expect.objectContaining({ a: 1 })],
|
||
|
|
[[1, 2, 3], expect.arrayContaining([2, 3])],
|
||
|
|
['abcd', expect.stringContaining('bc')],
|
||
|
|
['abcd', expect.stringMatching('bc')],
|
||
|
|
[true, expect.anything()],
|
||
|
|
[() => {}, expect.any(Function)],
|
||
|
|
].entries()) {
|
||
|
|
test(`{pass: true} expect().toEqual() #${a}`, () => {
|
||
|
|
expect(b[0]).toEqual(b[1]);
|
||
|
|
expect(() => expect(b[0]).not.toEqual(b[1])).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
test('assertion error matcherResult property contains matcher name, expected and actual values', () => {
|
||
|
|
const actual = { a: 1 };
|
||
|
|
const expected = { a: 2 };
|
||
|
|
try {
|
||
|
|
expect(actual).toEqual(expected);
|
||
|
|
} catch (error) {
|
||
|
|
expect(error.matcherResult).toEqual(expect.objectContaining({
|
||
|
|
actual,
|
||
|
|
expected,
|
||
|
|
name: 'toEqual',
|
||
|
|
}));
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
test('symbol based keys in arrays are processed correctly', () => {
|
||
|
|
const mySymbol = Symbol('test');
|
||
|
|
const actual1: any[] = [];
|
||
|
|
actual1[mySymbol as any] = 3;
|
||
|
|
const actual2: any[] = [];
|
||
|
|
actual2[mySymbol as any] = 4;
|
||
|
|
const expected: any[] = [];
|
||
|
|
expected[mySymbol as any] = 3;
|
||
|
|
|
||
|
|
expect(actual1).toEqual(expected);
|
||
|
|
expect(actual2).not.toEqual(expected);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('non-enumerable members should be skipped during equal', () => {
|
||
|
|
const actual: any = { x: 3 };
|
||
|
|
Object.defineProperty(actual, 'test', { enumerable: false, value: 5 });
|
||
|
|
expect(actual).toEqual({ x: 3 });
|
||
|
|
});
|
||
|
|
|
||
|
|
test('objectContaining sample can be used multiple times', () => {
|
||
|
|
const expected = expect.objectContaining({ b: 7 });
|
||
|
|
expect({ a: 1, b: 2 }).not.toEqual(expected);
|
||
|
|
expect({ a: 3, b: 7 }).toEqual(expected);
|
||
|
|
});
|
||
|
|
|
||
|
|
test.describe('cyclic object equality', () => {
|
||
|
|
test('properties with the same circularity are equal', () => {
|
||
|
|
const a: any = {};
|
||
|
|
a.x = a;
|
||
|
|
const b: any = {};
|
||
|
|
b.x = b;
|
||
|
|
expect(a).toEqual(b);
|
||
|
|
expect(b).toEqual(a);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('properties with different circularity are not equal', () => {
|
||
|
|
const a: any = {};
|
||
|
|
a.x = { y: a };
|
||
|
|
const b: any = {};
|
||
|
|
const bx: any = {};
|
||
|
|
b.x = bx;
|
||
|
|
bx.y = bx;
|
||
|
|
expect(a).not.toEqual(b);
|
||
|
|
expect(b).not.toEqual(a);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test.describe('.toBeInstanceOf()', () => {
|
||
|
|
class A {}
|
||
|
|
class B {}
|
||
|
|
class C extends B {}
|
||
|
|
class E extends C {}
|
||
|
|
|
||
|
|
for (const [i, [a, b]] of ([
|
||
|
|
[new Map(), Map],
|
||
|
|
[[], Array],
|
||
|
|
[new A(), A],
|
||
|
|
[new C(), B],
|
||
|
|
[new E(), B],
|
||
|
|
] as const).entries()) {
|
||
|
|
test(`passing instanceof #${i}`, () => {
|
||
|
|
expect(() => expect(a).not.toBeInstanceOf(b)).toThrow();
|
||
|
|
expect(a).toBeInstanceOf(b);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const [i, [a, b]] of ([
|
||
|
|
['a', String],
|
||
|
|
[1, Number],
|
||
|
|
[true, Boolean],
|
||
|
|
[new A(), B],
|
||
|
|
] as const).entries()) {
|
||
|
|
test(`failing instanceof #${i}`, () => {
|
||
|
|
expect(() => expect(a).toBeInstanceOf(b)).toThrow();
|
||
|
|
expect(a).not.toBeInstanceOf(b);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
test('throws if constructor is not a function', () => {
|
||
|
|
expect(() => expect({}).toBeInstanceOf(4 as any)).toThrow();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test.describe('.toBeTruthy(), .toBeFalsy()', () => {
|
||
|
|
for (const v of [{}, [], true, 1, 'a', 0.5, new Map(), () => {}, Infinity]) {
|
||
|
|
test(`'${String(v)}' is truthy`, () => {
|
||
|
|
expect(v).toBeTruthy();
|
||
|
|
expect(v).not.toBeFalsy();
|
||
|
|
expect(() => expect(v).not.toBeTruthy()).toThrow();
|
||
|
|
expect(() => expect(v).toBeFalsy()).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const v of [false, null, NaN, 0, '', undefined]) {
|
||
|
|
test(`'${String(v)}' is falsy`, () => {
|
||
|
|
expect(v).toBeFalsy();
|
||
|
|
expect(v).not.toBeTruthy();
|
||
|
|
expect(() => expect(v).toBeTruthy()).toThrow();
|
||
|
|
expect(() => expect(v).not.toBeFalsy()).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
test.describe('.toBeNaN()', () => {
|
||
|
|
test('{pass: true}', () => {
|
||
|
|
for (const v of [NaN, Math.sqrt(-1), Infinity - Infinity, 0 / 0]) {
|
||
|
|
expect(v).toBeNaN();
|
||
|
|
expect(() => expect(v).not.toBeNaN()).toThrow();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
test('throws', () => {
|
||
|
|
for (const v of [1, '', null, undefined, {}, [], 0.2, 0, Infinity, -Infinity]) {
|
||
|
|
expect(() => expect(v).toBeNaN()).toThrow();
|
||
|
|
expect(v).not.toBeNaN();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test.describe('.toBeNull()', () => {
|
||
|
|
for (const v of [{}, [], true, 1, 'a', 0.5, new Map(), () => {}, Infinity]) {
|
||
|
|
test(`fails for '${String(v)}'`, () => {
|
||
|
|
expect(v).not.toBeNull();
|
||
|
|
expect(() => expect(v).toBeNull()).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
test('fails for null with .not', () => {
|
||
|
|
expect(() => expect(null).not.toBeNull()).toThrow();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('pass for null', () => {
|
||
|
|
expect(null).toBeNull();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test.describe('.toBeDefined(), .toBeUndefined()', () => {
|
||
|
|
for (const v of [{}, [], true, 1, 'a', 0.5, new Map(), () => {}, Infinity]) {
|
||
|
|
test(`'${String(v)}' is defined`, () => {
|
||
|
|
expect(v).toBeDefined();
|
||
|
|
expect(v).not.toBeUndefined();
|
||
|
|
expect(() => expect(v).not.toBeDefined()).toThrow();
|
||
|
|
expect(() => expect(v).toBeUndefined()).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
test('undefined is undefined', () => {
|
||
|
|
expect(undefined).toBeUndefined();
|
||
|
|
expect(undefined).not.toBeDefined();
|
||
|
|
expect(() => expect(undefined).toBeDefined()).toThrow();
|
||
|
|
expect(() => expect(undefined).not.toBeUndefined()).toThrow();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test.describe('.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual()', () => {
|
||
|
|
for (const [small, big] of [
|
||
|
|
[1, 2],
|
||
|
|
[-Infinity, Infinity],
|
||
|
|
[Number.MIN_VALUE, Number.MAX_VALUE],
|
||
|
|
[0.1, 0.2],
|
||
|
|
] as const) {
|
||
|
|
test(`comparing ${small} and ${big}`, () => {
|
||
|
|
expect(small).toBeLessThan(big);
|
||
|
|
expect(big).not.toBeLessThan(small);
|
||
|
|
expect(big).toBeGreaterThan(small);
|
||
|
|
expect(small).not.toBeGreaterThan(big);
|
||
|
|
expect(small).toBeLessThanOrEqual(big);
|
||
|
|
expect(big).not.toBeLessThanOrEqual(small);
|
||
|
|
expect(big).toBeGreaterThanOrEqual(small);
|
||
|
|
expect(small).not.toBeGreaterThanOrEqual(big);
|
||
|
|
|
||
|
|
expect(() => expect(small).toBeGreaterThan(big)).toThrow();
|
||
|
|
expect(() => expect(small).not.toBeLessThan(big)).toThrow();
|
||
|
|
expect(() => expect(big).not.toBeGreaterThan(small)).toThrow();
|
||
|
|
expect(() => expect(big).toBeLessThan(small)).toThrow();
|
||
|
|
expect(() => expect(small).toBeGreaterThanOrEqual(big)).toThrow();
|
||
|
|
expect(() => expect(small).not.toBeLessThanOrEqual(big)).toThrow();
|
||
|
|
expect(() => expect(big).not.toBeGreaterThanOrEqual(small)).toThrow();
|
||
|
|
expect(() => expect(big).toBeLessThanOrEqual(small)).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
test('can compare BigInt to Numbers', () => {
|
||
|
|
const a = BigInt(2);
|
||
|
|
expect(a).toBeGreaterThan(1);
|
||
|
|
expect(a).toBeGreaterThanOrEqual(2);
|
||
|
|
expect(2).toBeLessThanOrEqual(a);
|
||
|
|
expect(a).toBeLessThan(3);
|
||
|
|
expect(a).toBeLessThanOrEqual(2);
|
||
|
|
});
|
||
|
|
|
||
|
|
for (const [n1, n2] of [
|
||
|
|
[1, 1],
|
||
|
|
[Number.MIN_VALUE, Number.MIN_VALUE],
|
||
|
|
[Infinity, Infinity],
|
||
|
|
[-Infinity, -Infinity],
|
||
|
|
]) {
|
||
|
|
test(`equal numbers: [${n1}, ${n2}]`, () => {
|
||
|
|
expect(n1).toBeGreaterThanOrEqual(n2);
|
||
|
|
expect(n1).toBeLessThanOrEqual(n2);
|
||
|
|
expect(() => expect(n1).not.toBeGreaterThanOrEqual(n2)).toThrow();
|
||
|
|
expect(() => expect(n1).not.toBeLessThanOrEqual(n2)).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
test.describe('.toContain(), .toContainEqual()', () => {
|
||
|
|
const typedArray = new Int8Array(2);
|
||
|
|
typedArray[0] = 0;
|
||
|
|
typedArray[1] = 1;
|
||
|
|
|
||
|
|
test('iterable', () => {
|
||
|
|
const iterable = {
|
||
|
|
*[Symbol.iterator]() {
|
||
|
|
yield 1;
|
||
|
|
yield 2;
|
||
|
|
yield 3;
|
||
|
|
},
|
||
|
|
};
|
||
|
|
expect(iterable).toContain(2);
|
||
|
|
expect(iterable).toContainEqual(2);
|
||
|
|
expect(() => expect(iterable).not.toContain(1)).toThrow();
|
||
|
|
expect(() => expect(iterable).not.toContainEqual(1)).toThrow();
|
||
|
|
});
|
||
|
|
|
||
|
|
for (const [i, [list, v]] of ([
|
||
|
|
[[1, 2, 3, 4], 1],
|
||
|
|
[['a', 'b', 'c', 'd'], 'a'],
|
||
|
|
[[undefined, null], null],
|
||
|
|
[[undefined, null], undefined],
|
||
|
|
['abcdef', 'abc'],
|
||
|
|
['11112111', '2'],
|
||
|
|
[new Set(['abc', 'def']), 'abc'],
|
||
|
|
[typedArray, 1],
|
||
|
|
] as const).entries()) {
|
||
|
|
test(`contains #${i}`, () => {
|
||
|
|
expect(list).toContain(v);
|
||
|
|
expect(() => expect(list).not.toContain(v)).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const [i, [list, v]] of ([
|
||
|
|
[[1, 2, 3], 4],
|
||
|
|
[[null, undefined], 1],
|
||
|
|
[[{}, []], []],
|
||
|
|
[[{}, []], {}],
|
||
|
|
] as const).entries()) {
|
||
|
|
test(`does not contain #${i}`, () => {
|
||
|
|
expect(list).not.toContain(v);
|
||
|
|
expect(() => expect(list).toContain(v)).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
test('error cases', () => {
|
||
|
|
expect(() => expect(null).toContain(1)).toThrow();
|
||
|
|
});
|
||
|
|
|
||
|
|
for (const [i, [list, v]] of ([
|
||
|
|
[[1, 2, 3, 4], 1],
|
||
|
|
[['a', 'b', 'c', 'd'], 'a'],
|
||
|
|
[[undefined, null], null],
|
||
|
|
[[{ a: 'b' }, { a: 'c' }], { a: 'b' }],
|
||
|
|
[new Set([1, 2, 3, 4]), 1],
|
||
|
|
] as const).entries()) {
|
||
|
|
test(`contains a value equal #${i}`, () => {
|
||
|
|
expect(list).toContainEqual(v);
|
||
|
|
expect(() => expect(list).not.toContainEqual(v)).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
test('error cases for toContainEqual', () => {
|
||
|
|
expect(() => expect(null).toContainEqual(1)).toThrow();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test.describe('.toBeCloseTo', () => {
|
||
|
|
for (const [n1, n2] of [
|
||
|
|
[0, 0],
|
||
|
|
[0, 0.001],
|
||
|
|
[1.23, 1.229],
|
||
|
|
[1.23, 1.226],
|
||
|
|
[1.23, 1.234],
|
||
|
|
[Infinity, Infinity],
|
||
|
|
[-Infinity, -Infinity],
|
||
|
|
]) {
|
||
|
|
test(`{pass: true} expect(${n1}).toBeCloseTo(${n2})`, () => {
|
||
|
|
expect(n1).toBeCloseTo(n2);
|
||
|
|
expect(() => expect(n1).not.toBeCloseTo(n2)).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const [n1, n2] of [
|
||
|
|
[0, 0.01],
|
||
|
|
[1, 1.23],
|
||
|
|
[Infinity, -Infinity],
|
||
|
|
[Infinity, 1.23],
|
||
|
|
[-Infinity, -1.23],
|
||
|
|
]) {
|
||
|
|
test(`{pass: false} expect(${n1}).toBeCloseTo(${n2})`, () => {
|
||
|
|
expect(n1).not.toBeCloseTo(n2);
|
||
|
|
expect(() => expect(n1).toBeCloseTo(n2)).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const [n1, n2, p] of [
|
||
|
|
[0, 0.1, 0],
|
||
|
|
[0, 0.0001, 3],
|
||
|
|
[0, 0.000_004, 5],
|
||
|
|
[2.000_000_2, 2, 5],
|
||
|
|
]) {
|
||
|
|
test(`{pass: true} expect(${n1}).toBeCloseTo(${n2}, ${p})`, () => {
|
||
|
|
expect(n1).toBeCloseTo(n2, p);
|
||
|
|
expect(() => expect(n1).not.toBeCloseTo(n2, p)).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
test.describe('.toMatch()', () => {
|
||
|
|
for (const [n1, n2] of [
|
||
|
|
['foo', 'foo'],
|
||
|
|
['Foo bar', /^foo/i],
|
||
|
|
] as const) {
|
||
|
|
test(`{pass: true} expect(${n1}).toMatch(${n2})`, () => {
|
||
|
|
expect(n1).toMatch(n2);
|
||
|
|
expect(() => expect(n1).not.toMatch(n2)).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const [n1, n2] of [
|
||
|
|
['bar', 'foo'],
|
||
|
|
['bar', /foo/],
|
||
|
|
] as const) {
|
||
|
|
test(`throws: [${n1}, ${n2}]`, () => {
|
||
|
|
expect(() => expect(n1).toMatch(n2)).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const [i, [n1, n2]] of ([
|
||
|
|
[1, 'foo'],
|
||
|
|
[{}, 'foo'],
|
||
|
|
[[], 'foo'],
|
||
|
|
[true, 'foo'],
|
||
|
|
[/foo/i, 'foo'],
|
||
|
|
[() => {}, 'foo'],
|
||
|
|
[undefined, 'foo'],
|
||
|
|
] as const).entries()) {
|
||
|
|
test(`throws if non String actual value passed #${i}`, () => {
|
||
|
|
expect(() => expect(n1).toMatch(n2)).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const [i, [n1, n2]] of ([
|
||
|
|
['foo', 1],
|
||
|
|
['foo', {}],
|
||
|
|
['foo', []],
|
||
|
|
['foo', true],
|
||
|
|
['foo', () => {}],
|
||
|
|
['foo', undefined],
|
||
|
|
] as const).entries()) {
|
||
|
|
test(`throws if non String/RegExp expected value passed #${i}`, () => {
|
||
|
|
expect(() => expect(n1).toMatch(n2 as any)).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
test('escapes strings properly', () => {
|
||
|
|
expect('this?: throws').toMatch('this?: throws');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('does not maintain RegExp state between calls', () => {
|
||
|
|
const regex = /f\d+/gi;
|
||
|
|
expect('f123').toMatch(regex);
|
||
|
|
expect('F456').toMatch(regex);
|
||
|
|
expect(regex.lastIndex).toBe(0);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test.describe('.toHaveLength', () => {
|
||
|
|
for (const [i, [received, length]] of ([
|
||
|
|
[[1, 2], 2],
|
||
|
|
[[], 0],
|
||
|
|
[['a', 'b'], 2],
|
||
|
|
['abc', 3],
|
||
|
|
['', 0],
|
||
|
|
[() => {}, 0],
|
||
|
|
] as const).entries()) {
|
||
|
|
test(`{pass: true} toHaveLength(${length}) #${i}`, () => {
|
||
|
|
expect(received).toHaveLength(length);
|
||
|
|
expect(() => expect(received).not.toHaveLength(length)).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const [i, [received, length]] of ([
|
||
|
|
[[1, 2], 3],
|
||
|
|
[[], 1],
|
||
|
|
[['a', 'b'], 99],
|
||
|
|
['abc', 66],
|
||
|
|
['', 1],
|
||
|
|
] as const).entries()) {
|
||
|
|
test(`{pass: false} toHaveLength(${length}) #${i}`, () => {
|
||
|
|
expect(received).not.toHaveLength(length);
|
||
|
|
expect(() => expect(received).toHaveLength(length)).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
test('error cases', () => {
|
||
|
|
expect(() => expect({ a: 9 }).toHaveLength(1)).toThrow();
|
||
|
|
expect(() => expect(0).toHaveLength(1)).toThrow();
|
||
|
|
expect(() => expect(undefined).not.toHaveLength(1)).toThrow();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test.describe('.toHaveProperty()', () => {
|
||
|
|
for (const [i, [obj, keyPath, value]] of ([
|
||
|
|
[{ a: { b: { c: { d: 1 } } } }, 'a.b.c.d', 1],
|
||
|
|
[{ a: { b: { c: { d: 1 } } } }, ['a', 'b', 'c', 'd'], 1],
|
||
|
|
[{ 'a.b.c.d': 1 }, ['a.b.c.d'], 1],
|
||
|
|
[{ a: { b: [1, 2, 3] } }, ['a', 'b', 1], 2],
|
||
|
|
[{ a: { b: [1, 2, 3] } }, ['a', 'b', 1], expect.any(Number)],
|
||
|
|
[{ a: 0 }, 'a', 0],
|
||
|
|
[{ a: { b: undefined } }, 'a.b', undefined],
|
||
|
|
[{ a: { b: { c: 5 } } }, 'a.b', { c: 5 }],
|
||
|
|
[{ a: { b: [{ c: [{ d: 1 }] }] } }, 'a.b[0].c[0].d', 1],
|
||
|
|
[{ '': 1 }, '', 1],
|
||
|
|
] as const).entries()) {
|
||
|
|
test(`{pass: true} toHaveProperty with value #${i}`, () => {
|
||
|
|
expect(obj).toHaveProperty(keyPath as any, value);
|
||
|
|
expect(() => expect(obj).not.toHaveProperty(keyPath as any, value)).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const [i, [obj, keyPath, value]] of ([
|
||
|
|
[{ a: { b: { c: { d: 1 } } } }, 'a.b.ttt.d', 1],
|
||
|
|
[{ a: { b: { c: { d: 1 } } } }, 'a.b.c.d', 2],
|
||
|
|
[{ 'a.b.c.d': 1 }, 'a.b.c.d', 2],
|
||
|
|
[{ a: { b: { c: {} } } }, 'a.b.c.d', 1],
|
||
|
|
[{ a: 1 }, 'a.b.c.d', 5],
|
||
|
|
[{}, 'a', 'test'],
|
||
|
|
[{ a: { b: 3 } }, 'a.b', undefined],
|
||
|
|
] as const).entries()) {
|
||
|
|
test(`{pass: false} toHaveProperty with value #${i}`, () => {
|
||
|
|
expect(() => expect(obj).toHaveProperty(keyPath, value)).toThrow();
|
||
|
|
expect(obj).not.toHaveProperty(keyPath, value);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const [i, [obj, keyPath]] of ([
|
||
|
|
[{ a: { b: { c: { d: 1 } } } }, 'a.b.c.d'],
|
||
|
|
[{ a: { b: { c: { d: 1 } } } }, ['a', 'b', 'c', 'd']],
|
||
|
|
[{ 'a.b.c.d': 1 }, ['a.b.c.d']],
|
||
|
|
[{ a: 0 }, 'a'],
|
||
|
|
[{ a: { b: undefined } }, 'a.b'],
|
||
|
|
] as const).entries()) {
|
||
|
|
test(`{pass: true} toHaveProperty without value #${i}`, () => {
|
||
|
|
expect(obj).toHaveProperty(keyPath as any);
|
||
|
|
expect(() => expect(obj).not.toHaveProperty(keyPath as any)).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const [i, [obj, keyPath]] of ([
|
||
|
|
[{ a: { b: { c: {} } } }, 'a.b.c.d'],
|
||
|
|
[{ a: 1 }, 'a.b.c.d'],
|
||
|
|
[{}, 'a'],
|
||
|
|
] as const).entries()) {
|
||
|
|
test(`{pass: false} toHaveProperty without value #${i}`, () => {
|
||
|
|
expect(() => expect(obj).toHaveProperty(keyPath)).toThrow();
|
||
|
|
expect(obj).not.toHaveProperty(keyPath);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const [i, [obj, keyPath]] of ([
|
||
|
|
[null, 'a.b'],
|
||
|
|
[undefined, 'a'],
|
||
|
|
[{ a: { b: {} } }, undefined],
|
||
|
|
[{ a: { b: {} } }, null],
|
||
|
|
[{ a: { b: {} } }, 1],
|
||
|
|
] as const).entries()) {
|
||
|
|
test(`{error} toHaveProperty #${i}`, () => {
|
||
|
|
expect(() => expect(obj).toHaveProperty(keyPath as any)).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
test.describe('.toMatchObject()', () => {
|
||
|
|
for (const [i, [a, b]] of ([
|
||
|
|
[{ a: 'b', c: 'd' }, { a: 'b' }],
|
||
|
|
[{ a: 'b', c: 'd' }, { a: 'b', c: 'd' }],
|
||
|
|
[{ a: 'b', t: { x: { r: 'r' }, z: 'z' } }, { a: 'b', t: { z: 'z' } }],
|
||
|
|
[{ a: [3, 4, 5], b: 'b' }, { a: [3, 4, 5] }],
|
||
|
|
[{ a: 1, c: 2 }, { a: expect.any(Number) }],
|
||
|
|
[new Set([1, 2]), new Set([1, 2])],
|
||
|
|
[new Date('2015-11-30'), new Date('2015-11-30')],
|
||
|
|
[{ a: null, b: 'b' }, { a: null }],
|
||
|
|
[{ a: undefined, b: 'b' }, { a: undefined }],
|
||
|
|
[[1, 2], [1, 2]],
|
||
|
|
[new Error('foo'), new Error('foo')],
|
||
|
|
[new Error('bar'), { message: 'bar' }],
|
||
|
|
] as const).entries()) {
|
||
|
|
test(`{pass: true} toMatchObject #${i}`, () => {
|
||
|
|
expect(a).toMatchObject(b as any);
|
||
|
|
expect(() => expect(a).not.toMatchObject(b as any)).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const [i, [a, b]] of ([
|
||
|
|
[{ a: 'b', c: 'd' }, { e: 'b' }],
|
||
|
|
[{ a: 'b', c: 'd' }, { a: 'b!', c: 'd' }],
|
||
|
|
[{ a: 'a', c: 'd' }, { a: expect.any(Number) }],
|
||
|
|
[{ a: [3, 4, 5], b: 'b' }, { a: [3, 4, 5, 6] }],
|
||
|
|
[[1, 2], [1, 3]],
|
||
|
|
[new Set([1, 2]), new Set([2])],
|
||
|
|
[new Error('foo'), new Error('bar')],
|
||
|
|
] as const).entries()) {
|
||
|
|
test(`{pass: false} toMatchObject #${i}`, () => {
|
||
|
|
expect(a).not.toMatchObject(b as any);
|
||
|
|
expect(() => expect(a).toMatchObject(b as any)).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const [i, [a, b]] of ([
|
||
|
|
[null, {}],
|
||
|
|
[4, {}],
|
||
|
|
['44', {}],
|
||
|
|
[true, {}],
|
||
|
|
[undefined, {}],
|
||
|
|
[{}, null],
|
||
|
|
[{}, 4],
|
||
|
|
] as const).entries()) {
|
||
|
|
test(`throws toMatchObject #${i}`, () => {
|
||
|
|
expect(() => expect(a).toMatchObject(b as any)).toThrow();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
test('does not match properties up in the prototype chain', () => {
|
||
|
|
const a: any = {};
|
||
|
|
a.ref = a;
|
||
|
|
const b = Object.create(a);
|
||
|
|
b.other = 'child';
|
||
|
|
const matcher: any = { other: 'child' };
|
||
|
|
matcher.ref = matcher;
|
||
|
|
expect(b).not.toMatchObject(matcher);
|
||
|
|
expect(() => expect(b).toMatchObject(matcher)).toThrow();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test.describe('.toThrow()', () => {
|
||
|
|
class CustomError extends Error {}
|
||
|
|
|
||
|
|
test('to throw or not to throw', () => {
|
||
|
|
expect(() => {
|
||
|
|
throw new CustomError('apple');
|
||
|
|
}).toThrow();
|
||
|
|
expect(() => {}).not.toThrow();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('substring passes', () => {
|
||
|
|
expect(() => {
|
||
|
|
throw new CustomError('apple');
|
||
|
|
}).toThrow('apple');
|
||
|
|
expect(() => {
|
||
|
|
throw new CustomError('banana');
|
||
|
|
}).not.toThrow('apple');
|
||
|
|
expect(() => {}).not.toThrow('apple');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('substring fails when did not throw', () => {
|
||
|
|
expect(() => expect(() => {}).toThrow('apple')).toThrow();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('substring fails when message did not match', () => {
|
||
|
|
expect(() => expect(() => {
|
||
|
|
throw new CustomError('apple');
|
||
|
|
}).toThrow('banana')).toThrow();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('regexp passes', () => {
|
||
|
|
expect(() => {
|
||
|
|
throw new CustomError('apple');
|
||
|
|
}).toThrow(/apple/);
|
||
|
|
expect(() => {
|
||
|
|
throw new CustomError('banana');
|
||
|
|
}).not.toThrow(/apple/);
|
||
|
|
expect(() => {}).not.toThrow(/apple/);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('regexp fails when did not throw', () => {
|
||
|
|
expect(() => expect(() => {}).toThrow(/apple/)).toThrow();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('error class passes', () => {
|
||
|
|
class Err extends CustomError {}
|
||
|
|
class Err2 extends CustomError {}
|
||
|
|
|
||
|
|
expect(() => {
|
||
|
|
throw new Err('apple');
|
||
|
|
}).toThrow(Err);
|
||
|
|
expect(() => {
|
||
|
|
throw new Err('apple');
|
||
|
|
}).toThrow(CustomError);
|
||
|
|
|
||
|
|
expect(() => {
|
||
|
|
throw new Err('apple');
|
||
|
|
}).not.toThrow(Err2);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('object with message passes', () => {
|
||
|
|
expect(() => {
|
||
|
|
throw new CustomError('apple');
|
||
|
|
}).toThrow({ message: 'apple' });
|
||
|
|
expect(() => {
|
||
|
|
throw new CustomError('banana');
|
||
|
|
}).not.toThrow({ message: 'apple' });
|
||
|
|
});
|
||
|
|
|
||
|
|
test('properly escapes strings when matching against errors', () => {
|
||
|
|
expect(() => {
|
||
|
|
throw new TypeError('"this"? throws.');
|
||
|
|
}).toThrow('"this"? throws.');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('error message and cause properties', () => {
|
||
|
|
const errorCause = new Error('cause');
|
||
|
|
const error = new Error('message', { cause: errorCause });
|
||
|
|
expect(() => {
|
||
|
|
throw error;
|
||
|
|
}).toThrow({ message: 'message', cause: errorCause });
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test.describe('asymmetric matchers', () => {
|
||
|
|
test('Any matches primitives', () => {
|
||
|
|
expect('jest').toEqual(expect.any(String));
|
||
|
|
expect(1).toEqual(expect.any(Number));
|
||
|
|
expect(() => {}).toEqual(expect.any(Function));
|
||
|
|
expect(true).toEqual(expect.any(Boolean));
|
||
|
|
expect({}).toEqual(expect.any(Object));
|
||
|
|
expect([]).toEqual(expect.any(Array));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('Any throws when called with empty constructor', () => {
|
||
|
|
expect(() => (expect.any as 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', () => {
|
||
|
|
expect('jest').toEqual(expect.anything());
|
||
|
|
expect(1).toEqual(expect.anything());
|
||
|
|
expect(() => {}).toEqual(expect.anything());
|
||
|
|
expect(true).toEqual(expect.anything());
|
||
|
|
expect({}).toEqual(expect.anything());
|
||
|
|
expect([]).toEqual(expect.anything());
|
||
|
|
});
|
||
|
|
|
||
|
|
test('Anything does not match null and undefined', () => {
|
||
|
|
expect(null).not.toEqual(expect.anything());
|
||
|
|
expect(undefined).not.toEqual(expect.anything());
|
||
|
|
});
|
||
|
|
|
||
|
|
test('ArrayContaining matches', () => {
|
||
|
|
expect(['foo']).toEqual(expect.arrayContaining(['foo']));
|
||
|
|
expect(['foo', 'bar']).toEqual(expect.arrayContaining(['foo']));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('ArrayContaining does not match', () => {
|
||
|
|
expect(['bar']).not.toEqual(expect.arrayContaining(['foo']));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('ObjectContaining matches', () => {
|
||
|
|
expect({ foo: 'foo', jest: 'jest' }).toEqual(expect.objectContaining({ foo: 'foo' }));
|
||
|
|
expect({ foo: undefined }).toEqual(expect.objectContaining({ foo: undefined }));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('ObjectContaining does not match', () => {
|
||
|
|
expect({ bar: 'bar' }).not.toEqual(expect.objectContaining({ foo: 'foo' }));
|
||
|
|
expect({ foo: 'foox' }).not.toEqual(expect.objectContaining({ foo: 'foo' }));
|
||
|
|
expect({}).not.toEqual(expect.objectContaining({ foo: undefined }));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('ObjectContaining throws for non-objects', () => {
|
||
|
|
expect(() => expect({}).toEqual(expect.objectContaining(1337 as any))).toThrow(
|
||
|
|
"You must provide an object to ObjectContaining, not 'number'.");
|
||
|
|
});
|
||
|
|
|
||
|
|
test('StringContaining matches string against string', () => {
|
||
|
|
expect('queen*').toEqual(expect.stringContaining('en*'));
|
||
|
|
expect('queue').not.toEqual(expect.stringContaining('en*'));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('StringMatching matches string against regexp', () => {
|
||
|
|
expect('queen').toEqual(expect.stringMatching(/en/));
|
||
|
|
expect('queue').not.toEqual(expect.stringMatching(/en/));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('StringMatching matches string against string', () => {
|
||
|
|
expect('queen').toEqual(expect.stringMatching('en'));
|
||
|
|
expect('queue').not.toEqual(expect.stringMatching('en'));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('closeTo matches', () => {
|
||
|
|
expect(0).toEqual(expect.closeTo(0));
|
||
|
|
expect(0.001).toEqual(expect.closeTo(0));
|
||
|
|
expect(1.229).toEqual(expect.closeTo(1.23));
|
||
|
|
expect(Infinity).toEqual(expect.closeTo(Infinity));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('closeTo does not match', () => {
|
||
|
|
expect(0.01).not.toEqual(expect.closeTo(0));
|
||
|
|
expect(1.23).not.toEqual(expect.closeTo(1));
|
||
|
|
expect(Infinity).not.toEqual(expect.closeTo(-Infinity));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('closeTo with precision', () => {
|
||
|
|
expect(0.1).toEqual(expect.closeTo(0, 0));
|
||
|
|
expect(0.0001).toEqual(expect.closeTo(0, 3));
|
||
|
|
expect(0.000_004).toEqual(expect.closeTo(0, 5));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('closeTo throws if expected is not number', () => {
|
||
|
|
expect(() => (expect.closeTo as any)('a')).toThrow('Expected is not a Number');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('arrayOf matches', () => {
|
||
|
|
expect([1]).toEqual(expect.arrayOf(1));
|
||
|
|
expect([1, 1, 1]).toEqual(expect.arrayOf(1));
|
||
|
|
expect([{ a: 1 }, { a: 1 }]).toEqual(expect.arrayOf({ a: 1 }));
|
||
|
|
expect(['a', 'b', 'c']).toEqual(expect.arrayOf(expect.any(String)));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('arrayOf does not match', () => {
|
||
|
|
expect([2]).not.toEqual(expect.arrayOf(1));
|
||
|
|
expect([1, 2]).not.toEqual(expect.arrayOf(1));
|
||
|
|
expect('not an array').not.toEqual(expect.arrayOf(1));
|
||
|
|
expect({}).not.toEqual(expect.arrayOf(1));
|
||
|
|
expect([1, 2]).not.toEqual(expect.arrayOf(expect.any(String)));
|
||
|
|
});
|
||
|
|
});
|