playwright/tests/expect/matchers.test.ts

2339 lines
65 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 { test, expect } from './fixtures';
import { expect as expectUnderTest, matcherUtils } from '../../packages/playwright/bundles/expect/src/expectBundleImpl';
import Immutable from 'immutable';
const { stringify } = matcherUtils;
const expectUnderTestAsAny = expectUnderTest as any;
expectUnderTest.extend({
optionalFn(fn) {
const pass = fn === undefined || typeof fn === 'function';
return { message: () => 'expect either a function or undefined', pass };
},
});
test('should throw if passed two arguments', () => {
expect(() => expectUnderTestAsAny('foo', 'bar')).toThrow(
new Error('Expect takes at most one argument.'),
);
});
test.describe('.rejects', () => {
test('should reject', async () => {
await expectUnderTest(Promise.reject(4)).rejects.toBe(4);
await expectUnderTest(Promise.reject(4)).rejects.not.toBe(5);
await expectUnderTest(Promise.reject(4.2)).rejects.toBeCloseTo(4.2, 5);
await expectUnderTest(Promise.reject(3)).rejects.not.toBeCloseTo(4.2, 5);
await expectUnderTest(Promise.reject({ a: 1, b: 2 })).rejects.toMatchObject({
a: 1,
});
await expectUnderTest(Promise.reject({ a: 1, b: 2 })).rejects.not.toMatchObject({
c: 1,
});
await expectUnderTest(
Promise.reject(new Error('rejectMessage')),
).rejects.toMatchObject({ message: 'rejectMessage' });
await expectUnderTest(Promise.reject(new Error())).rejects.toThrow();
});
test('should reject with toThrow', async () => {
async function fn() {
throw new Error('some error');
}
await expectUnderTest(fn()).rejects.toThrow('some error');
});
test('should reject async function to toThrow', async () => {
await expectUnderTest(async () => {
throw new Error('Test');
}).rejects.toThrow('Test');
});
['a', [1], () => { }, { a: 1 }].forEach(value => {
test(`fails non-promise value ${stringify(value)} synchronously`, () => {
let error;
try {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
expectUnderTest(value).rejects.toBe(111);
} catch (e) {
error = e;
}
expect(error).toBeDefined();
});
test(`fails non-promise value ${stringify(value)}`, async () => {
let error;
try {
await expectUnderTest(value).rejects.toBeDefined();
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.message).toMatchSnapshot();
});
});
[4, null, true, undefined].forEach(value => {
test(`fails non-promise value ${stringify(value)} synchronously`, () => {
let error;
try {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
expectUnderTest(value).rejects.not.toBe(111);
} catch (e) {
error = e;
}
expect(error).toBeDefined();
});
test(`fails non-promise value ${stringify(value)}`, async () => {
let error;
try {
await expectUnderTest(value).rejects.not.toBeDefined();
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.message).toMatchSnapshot();
});
});
test('fails for promise that resolves', async () => {
let error;
try {
await expectUnderTest(Promise.resolve(4)).rejects.toBe(4);
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.message).toMatchSnapshot();
});
});
test.describe('.resolves', () => {
test('should resolve', async () => {
await expectUnderTest(Promise.resolve(4)).resolves.toBe(4);
await expectUnderTest(Promise.resolve(4)).resolves.not.toBe(5);
await expectUnderTest(Promise.resolve(4.2)).resolves.toBeCloseTo(4.2, 5);
await expectUnderTest(Promise.resolve(3)).resolves.not.toBeCloseTo(4.2, 5);
await expectUnderTest(Promise.resolve({ a: 1, b: 2 })).resolves.toMatchObject({
a: 1,
});
await expectUnderTest(Promise.resolve({ a: 1, b: 2 })).resolves.not.toMatchObject({
c: 1,
});
await expectUnderTest(
Promise.resolve(() => {
throw new Error();
}),
).resolves.toThrow();
});
['a', [1], () => { }, { a: 1 }].forEach(value => {
test(`fails non-promise value ${stringify(value)} synchronously`, () => {
let error;
try {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
expectUnderTest(value).resolves.toBeDefined();
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.message).toMatchSnapshot();
});
test(`fails non-promise value ${stringify(value)}`, async () => {
let error;
try {
await expectUnderTest(value).resolves.toBeDefined();
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.message).toMatchSnapshot();
});
});
[4, null, true, undefined].forEach(value => {
test(`fails non-promise value ${stringify(value)} synchronously`, () => {
let error;
try {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
expectUnderTest(value).resolves.not.toBeDefined();
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.message).toMatchSnapshot();
});
test(`fails non-promise value ${stringify(value)}`, async () => {
let error;
try {
await expectUnderTest(value).resolves.not.toBeDefined();
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.message).toMatchSnapshot();
});
});
test('fails for promise that rejects', async () => {
let error;
try {
await expectUnderTest(Promise.reject(4)).resolves.toBe(4);
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.message).toMatchSnapshot();
});
});
test.describe('.toBe()', () => {
test('does not throw', () => {
expectUnderTest('a').not.toBe('b');
expectUnderTest('a').toBe('a');
expectUnderTest(1).not.toBe(2);
expectUnderTest(1).toBe(1);
expectUnderTest(null).not.toBe(undefined);
expectUnderTest(null).toBe(null);
expectUnderTest(undefined).toBe(undefined);
expectUnderTest(NaN).toBe(NaN);
expectUnderTest(BigInt(1)).not.toBe(BigInt(2));
expectUnderTest(BigInt(1)).not.toBe(1);
expectUnderTest(BigInt(1)).toBe(BigInt(1));
});
[
[1, 2],
[true, false],
[() => { }, () => { }],
[{}, {}],
[{ a: 1 }, { a: 1 }],
[{ a: 1 }, { a: 5 }],
[
{ a: () => { }, b: 2 },
{ a: expect.any(Function), b: 2 },
],
[{ a: undefined, b: 2 }, { b: 2 }],
[new Date('2020-02-20'), new Date('2020-02-20')],
[new Date('2020-02-21'), new Date('2020-02-20')],
[/received/, /expected/],
[Symbol('received'), Symbol('expected')],
[new Error('received'), new Error('expected')],
['abc', 'cde'],
['painless JavaScript testing', 'delightful JavaScript testing'],
['', 'compare one-line string to empty string'],
['with \ntrailing space', 'without trailing space'],
['four\n4\nline\nstring', '3\nline\nstring'],
[[], []],
[null, undefined],
[-0, +0],
].forEach(([a, b]: [a: any, b: any], index) => {
test(`fails for: ${stringify(a)} and ${stringify(b)} (${index})`, () => {
expect(() => expectUnderTest(a).toBe(b)).toThrowErrorMatchingSnapshot();
});
});
[
[BigInt(1), BigInt(2)],
[{ a: BigInt(1) }, { a: BigInt(1) }],
].forEach(([a, b]) => {
test(`fails for: ${stringify(a)} and ${stringify(b)}`, () => {
expect(() => expectUnderTest(a).toBe(b)).toThrow('toBe');
});
});
[false, 1, 'a', undefined, null, {}, []].forEach(v => {
test(`fails for '${stringify(v)}' with '.not'`, () => {
expect(() => expectUnderTest(v).not.toBe(v)).toThrowErrorMatchingSnapshot();
});
});
[BigInt(1), BigInt('1')].forEach((v, index) => {
test(`fails for '${stringify(v)}' with '.not' (${index})}`, () => {
expect(() => expectUnderTest(v).not.toBe(v)).toThrow('toBe');
});
});
test('does not crash on circular references', () => {
const obj: any = {};
obj.circular = obj;
expect(() => expectUnderTest(obj).toBe({})).toThrowErrorMatchingSnapshot();
});
test('assertion error matcherResult property contains matcher name, expected and actual values', () => {
const actual = { a: 1 };
const expected = { a: 2 };
try {
expectUnderTest(actual).toBe(expected);
} catch (error) {
expect(error.matcherResult).toEqual(
expect.objectContaining({
actual,
expected,
name: 'toBe',
}),
);
}
});
});
test.describe('.toStrictEqual()', () => {
class TestClassA {
constructor(public a, public b) { }
}
class TestClassB {
constructor(public a, public b) { }
}
const TestClassC = class Child extends TestClassA {
constructor(a, b) {
super(a, b);
}
};
const TestClassD = class Child extends TestClassB {
constructor(a, b) {
super(a, 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('does not ignore keys with undefined values deep inside an object', () => {
expect([{ a: [{ a: undefined }] }]).not.toStrictEqual([{ a: [{}] }]);
});
test('does not consider holes as undefined in sparse arrays', () => {
expect([, , , 1, , ,]).not.toStrictEqual([, , , 1, undefined, ,]);
});
test('passes when comparing same type', () => {
expect({
test: new TestClassA(1, 2),
}).toStrictEqual({ test: new TestClassA(1, 2) });
});
test('matches the expected snapshot when it fails', () => {
expect(() =>
expectUnderTest({
test: 2,
}).toStrictEqual({ test: new TestClassA(1, 2) }),
).toThrowErrorMatchingSnapshot();
expect(() =>
expectUnderTest({
test: new TestClassA(1, 2),
}).not.toStrictEqual({ test: new TestClassA(1, 2) }),
).toThrowErrorMatchingSnapshot();
});
test('displays substring diff', () => {
const expected =
'Another caveat is that Jest will not typecheck your tests.';
const received =
'Because TypeScript support in Babel is just transpilation, Jest will not type-check your tests as they run.';
expect(() =>
expectUnderTest(received).toStrictEqual(expected),
).toThrowErrorMatchingSnapshot();
});
test('displays substring diff for multiple lines', () => {
const expected = [
' 69 | ',
" 70 | test('assert.doesNotThrow', () => {",
' > 71 | assert.doesNotThrow(() => {',
' | ^',
" 72 | throw Error('err!');",
' 73 | });',
' 74 | });',
' at Object.doesNotThrow (__tests__/assertionError.test.js:71:10)',
].join('\n');
const received = [
' 68 | ',
" 69 | test('assert.doesNotThrow', () => {",
' > 70 | assert.doesNotThrow(() => {',
' | ^',
" 71 | throw Error('err!');",
' 72 | });',
' 73 | });',
' at Object.doesNotThrow (__tests__/assertionError.test.js:70:10)',
].join('\n');
expect(() =>
expectUnderTest(received).toStrictEqual(expected),
).toThrowErrorMatchingSnapshot();
});
test('does not pass for different types', () => {
expect({
test: new TestClassA(1, 2),
}).not.toStrictEqual({ test: new TestClassB(1, 2) });
});
test('does not simply compare constructor names', () => {
const c = new TestClassC(1, 2);
const d = new TestClassD(1, 2);
expect(c.constructor.name).toEqual(d.constructor.name);
expect({ test: c }).not.toStrictEqual({ test: d });
});
test('passes for matching sparse arrays', () => {
expect([, 1]).toStrictEqual([, 1]);
});
test('does not pass when sparseness of arrays do not match', () => {
expect([, 1]).not.toStrictEqual([undefined, 1]);
expect([undefined, 1]).not.toStrictEqual([, 1]);
expect([, , , 1]).not.toStrictEqual([, 1]);
});
test('does not pass when equally sparse arrays have different values', () => {
expect([, 1]).not.toStrictEqual([, 2]);
});
test('does not pass when ArrayBuffers are not equal', () => {
expect(Uint8Array.from([1, 2]).buffer).not.toStrictEqual(
Uint8Array.from([0, 0]).buffer,
);
expect(Uint8Array.from([2, 1]).buffer).not.toStrictEqual(
Uint8Array.from([2, 2]).buffer,
);
expect(Uint8Array.from([]).buffer).not.toStrictEqual(
Uint8Array.from([1]).buffer,
);
});
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('fails for missing keys even if backed by an asymmetric matcher accepting them', () => {
// issue 12463
expect({ a: 1 }).not.toStrictEqual({ a: 1, b: expectUnderTestAsAny.optionalFn() });
expect({ a: 1, b: expectUnderTestAsAny.optionalFn() }).not.toStrictEqual({ a: 1 });
expect([1]).not.toStrictEqual([1, expectUnderTestAsAny.optionalFn()]);
expect([1, expectUnderTestAsAny.optionalFn()]).not.toStrictEqual([1]);
});
test('passes if keys are present and asymmetric matcher accept them', () => {
// issue 12463
// with a proper function
expect({ a: 1, b: () => { } }).toStrictEqual({
a: 1,
b: expectUnderTestAsAny.optionalFn(),
});
expect({ a: 1, b: expectUnderTestAsAny.optionalFn() }).toStrictEqual({
a: 1,
b: () => { },
});
expect([1, () => { }]).toStrictEqual([1, expectUnderTestAsAny.optionalFn()]);
expect([1, expectUnderTestAsAny.optionalFn()]).toStrictEqual([1, () => { }]);
// with undefined
expect({ a: 1, b: undefined }).toStrictEqual({
a: 1,
b: expectUnderTestAsAny.optionalFn(),
});
expect({ a: 1, b: expectUnderTestAsAny.optionalFn() }).toStrictEqual({
a: 1,
b: undefined,
});
expect([1, undefined]).toStrictEqual([1, expectUnderTestAsAny.optionalFn()]);
expect([1, expectUnderTestAsAny.optionalFn()]).toStrictEqual([1, undefined]);
});
});
test.describe('.toEqual()', () => {
[
[true, false],
[1, 2],
[0, -0],
[0, Number.MIN_VALUE], // issues/7941
[Number.MIN_VALUE, 0],
[0, new Number(0)],
[new Number(0), 0],
[new Number(0), new Number(1)],
['abc', new String('abc')],
[new String('abc'), 'abc'],
// @ts-ignore
[/abc/gsy, /abc/g],
[{ a: 1 }, { a: 2 }],
[{ a: 5 }, { b: 6 }],
[Object.freeze({ foo: { bar: 1 } }), { foo: {} }],
[
{
get getterAndSetter() {
return {};
},
set getterAndSetter(value) {
throw new Error('noo');
},
},
{ getterAndSetter: { foo: 'bar' } },
],
[
Object.freeze({
get frozenGetterAndSetter() {
return {};
},
set frozenGetterAndSetter(value) {
throw new Error('noo');
},
}),
{ frozenGetterAndSetter: { foo: 'bar' } },
],
[
{
get getter() {
return {};
},
},
{ getter: { foo: 'bar' } },
],
[
Object.freeze({
get frozenGetter() {
return {};
},
}),
{ frozenGetter: { foo: 'bar' } },
],
[
{
set setter(value) {
throw new Error('noo');
},
},
{ setter: { foo: 'bar' } },
],
[
Object.freeze({
set frozenSetter(value) {
throw new Error('noo');
},
}),
{ frozenSetter: { foo: 'bar' } },
],
['banana', 'apple'],
['1\u{00A0}234,57\u{00A0}$', '1 234,57 $'], // issues/6881
[
'type TypeName<T> = T extends Function ? "function" : "object";',
'type TypeName<T> = T extends Function\n? "function"\n: "object";',
],
[null, undefined],
[[1], [2]],
[
[1, 2],
[2, 1],
],
[Immutable.List([1]), Immutable.List([2])],
[Immutable.List([1, 2]), Immutable.List([2, 1])],
[new Map(), new Set()],
[new Set([1, 2]), new Set()],
[new Set([1, 2]), new Set([1, 2, 3])],
[new Set([[1], [2]]), new Set([[1], [2], [3]])],
[new Set([[1], [2]]), new Set([[1], [2], [2]])],
[
new Set([new Set([1]), new Set([2])]),
new Set([new Set([1]), new Set([3])]),
],
[Immutable.Set([1, 2]), Immutable.Set()],
[Immutable.Set([1, 2]), Immutable.Set([1, 2, 3])],
[Immutable.OrderedSet([1, 2]), Immutable.OrderedSet([2, 1])],
[
new Map([
[1, 'one'],
[2, 'two'],
]),
new Map([[1, 'one']]),
],
[new Map([['a', 0]]), new Map([['b', 0]])],
[new Map([['v', 1]]), new Map([['v', 2]])],
[new Map([[['v'], 1]]), new Map([[['v'], 2]])],
[
new Map([[[1], new Map([[[1], 'one']])]]),
new Map([[[1], new Map([[[1], 'two']])]]),
],
[Immutable.Map({ a: 0 }), Immutable.Map({ b: 0 })],
[Immutable.Map({ v: 1 }), Immutable.Map({ v: 2 })],
[
Immutable.OrderedMap().set(1, 'one').set(2, 'two'),
Immutable.OrderedMap().set(2, 'two').set(1, 'one'),
],
[
Immutable.Map({ 1: Immutable.Map({ 2: { a: 99 } }) }),
Immutable.Map({ 1: Immutable.Map({ 2: { a: 11 } }) }),
],
[new Uint8Array([97, 98, 99]), new Uint8Array([97, 98, 100])],
[{ a: 1, b: 2 }, expectUnderTest.objectContaining({ a: 2 })],
[false, expectUnderTest.objectContaining({ a: 2 })],
[[1, 3], expectUnderTest.arrayContaining([1, 2])],
[1, expectUnderTest.arrayContaining([1, 2])],
['abd', expectUnderTest.stringContaining('bc')],
['abd', expectUnderTest.stringMatching(/bc/i)],
[undefined, expectUnderTest.anything()],
[undefined, expectUnderTest.any(Function)],
[
'Eve',
{
asymmetricMatch: function asymmetricMatch(who) {
return who === 'Alice' || who === 'Bob';
},
},
],
[
{
target: {
nodeType: 1,
value: 'a',
},
},
{
target: {
nodeType: 1,
value: 'b',
},
},
],
[
{
nodeName: 'div',
nodeType: 1,
},
{
nodeName: 'p',
nodeType: 1,
},
],
[
{
[Symbol.for('foo')]: 1,
[Symbol.for('bar')]: 2,
},
{
[Symbol.for('foo')]: expectUnderTest.any(Number),
[Symbol.for('bar')]: 1,
},
],
[
[, , 1, ,],
[, , 2, ,],
],
[
Object.assign([], { 4294967295: 1 }),
Object.assign([], { 4294967295: 2 }), // issue 11056
],
[
Object.assign([], { ['-0']: 1 }),
Object.assign([], { ['0']: 1 }), // issue 11056: also check (-0, 0)
],
[
Object.assign([], { a: 1 }),
Object.assign([], { b: 1 }), // issue 11056: also check strings
],
[
Object.assign([], { [Symbol()]: 1 }),
Object.assign([], { [Symbol()]: 1 }), // issue 11056: also check symbols
],
].forEach(([a, b], index) => {
test(`{pass: false} expect(${stringify(a)}).toEqual(${stringify(
b,
)} (${index}))`, () => {
expect(() => expectUnderTest(a).toEqual(b)).toThrowErrorMatchingSnapshot();
expectUnderTest(a).not.toEqual(b);
});
});
[
[BigInt(1), BigInt(2)],
[BigInt(1), 1],
].forEach(([a, b]) => {
test(`{pass: false} expect(${stringify(a)}).toEqual(${stringify(
b,
)})`, () => {
expect(() => expectUnderTest(a).toEqual(b)).toThrow('toEqual');
expectUnderTest(a).not.toEqual(b);
});
});
[
[true, true],
[1, 1],
[NaN, NaN],
[0, Number(0)],
[Number(0), 0],
[new Number(0), new Number(0)],
['abc', 'abc'],
[String('abc'), 'abc'],
['abc', String('abc')],
[[1], [1]],
[
[1, 2],
[1, 2],
],
[Immutable.List([1]), Immutable.List([1])],
[Immutable.List([1, 2]), Immutable.List([1, 2])],
[{}, {}],
[{ 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 Set([[1], [2]]), new Set([[2], [1]])],
[
new Set([new Set([[1]]), new Set([[2]])]),
new Set([new Set([[2]]), new Set([[1]])]),
],
[new Set([[1], [2], [3], [3]]), new Set([[3], [3], [2], [1]])],
[new Set([{ a: 1 }, { b: 2 }]), new Set([{ b: 2 }, { a: 1 }])],
[Immutable.Set(), Immutable.Set()],
[Immutable.Set([1, 2]), Immutable.Set([1, 2])],
[Immutable.Set([1, 2]), Immutable.Set([2, 1])],
[Immutable.OrderedSet(), Immutable.OrderedSet()],
[Immutable.OrderedSet([1, 2]), Immutable.OrderedSet([1, 2])],
[new Map(), new Map()],
[
new Map([
[1, 'one'],
[2, 'two'],
]),
new Map([
[1, 'one'],
[2, 'two'],
]),
],
[
new Map([
[1, 'one'],
[2, 'two'],
]),
new Map([
[2, 'two'],
[1, 'one'],
]),
],
[
new Map([
[[1], 'one'],
[[2], 'two'],
[[3], 'three'],
[[3], 'four'],
]),
new Map([
[[3], 'three'],
[[3], 'four'],
[[2], 'two'],
[[1], 'one'],
]),
],
[
new Map([
[[1], new Map([[[1], 'one']])],
[[2], new Map([[[2], 'two']])],
]),
new Map([
[[2], new Map([[[2], 'two']])],
[[1], new Map([[[1], 'one']])],
]),
],
[
new Map([
[[1], 'one'],
[[2], 'two'],
]),
new Map([
[[2], 'two'],
[[1], 'one'],
]),
],
[
new Map([
[{ a: 1 }, 'one'],
[{ b: 2 }, 'two'],
]),
new Map([
[{ b: 2 }, 'two'],
[{ a: 1 }, 'one'],
]),
],
[
new Map([
[1, ['one']],
[2, ['two']],
]),
new Map([
[2, ['two']],
[1, ['one']],
]),
],
[Immutable.Map(), Immutable.Map()],
[
Immutable.Map().set(1, 'one').set(2, 'two'),
Immutable.Map().set(1, 'one').set(2, 'two'),
],
[
Immutable.Map().set(1, 'one').set(2, 'two'),
Immutable.Map().set(2, 'two').set(1, 'one'),
],
[
Immutable.OrderedMap().set(1, 'one').set(2, 'two'),
Immutable.OrderedMap().set(1, 'one').set(2, 'two'),
],
[
Immutable.Map({ 1: Immutable.Map({ 2: { a: 99 } }) }),
Immutable.Map({ 1: Immutable.Map({ 2: { a: 99 } }) }),
],
[new Uint8Array([97, 98, 99]), new Uint8Array([97, 98, 99])],
[{ a: 1, b: 2 }, expectUnderTest.objectContaining({ a: 1 })],
[[1, 2, 3], expectUnderTest.arrayContaining([2, 3])],
['abcd', expectUnderTest.stringContaining('bc')],
['abcd', expectUnderTest.stringMatching('bc')],
[true, expectUnderTest.anything()],
[() => { }, expectUnderTest.any(Function)],
[
{
a: 1,
b: function b() { },
c: true,
},
{
a: 1,
b: expectUnderTest.any(Function),
c: expectUnderTest.anything(),
},
],
[
'Alice',
{
asymmetricMatch: function asymmetricMatch(who) {
return who === 'Alice' || who === 'Bob';
},
},
],
[
{
nodeName: 'div',
nodeType: 1,
},
{
nodeName: 'div',
nodeType: 1,
},
],
[
{
[Symbol.for('foo')]: 1,
[Symbol.for('bar')]: 2,
},
{
[Symbol.for('foo')]: expectUnderTest.any(Number),
[Symbol.for('bar')]: 2,
},
],
[
[, , 1, ,],
[, , 1, ,],
],
[
[, , 1, , ,],
[, , 1, undefined, ,], // same length but hole replaced by undefined
],
// issue 12463 - "matcher" vs "proper function"
[
{ a: 1, b: () => { } },
{ a: 1, b: expectUnderTestAsAny.optionalFn() },
],
[
{ a: 1, b: expectUnderTestAsAny.optionalFn() },
{ a: 1, b: () => { } },
],
[
[1, () => { }],
[1, expectUnderTestAsAny.optionalFn()],
],
[
[1, expectUnderTestAsAny.optionalFn()],
[1, () => { }],
],
// issue 12463 - "matcher" vs "undefined"
[
{ a: 1, b: undefined },
{ a: 1, b: expectUnderTestAsAny.optionalFn() },
],
[
{ a: 1, b: expectUnderTestAsAny.optionalFn() },
{ a: 1, b: undefined },
],
[
[1, undefined],
[1, expectUnderTestAsAny.optionalFn()],
],
[
[1, expectUnderTestAsAny.optionalFn()],
[1, undefined],
],
// issue 12463 - "matcher" vs "missing"
[{ a: 1 }, { a: 1, b: expectUnderTestAsAny.optionalFn() }],
[{ a: 1, b: expectUnderTestAsAny.optionalFn() }, { a: 1 }],
[[1], [1, expectUnderTestAsAny.optionalFn()]],
[[1, expectUnderTestAsAny.optionalFn()], [1]],
].forEach(([a, b], index) => {
test(`{pass: true} expect(${stringify(a)}).not.toEqual(${stringify(
b,
)}) (${index})`, () => {
expectUnderTest(a).toEqual(b);
expect(() => expectUnderTest(a).not.toEqual(b)).toThrowErrorMatchingSnapshot();
});
});
[
[BigInt(1), BigInt(1)],
[BigInt(0), BigInt('0')],
[[BigInt(1)], [BigInt(1)]],
[
[BigInt(1), 2],
[BigInt(1), 2],
],
[Immutable.List([BigInt(1)]), Immutable.List([BigInt(1)])],
[{ a: BigInt(99) }, { a: BigInt(99) }],
[new Set([BigInt(1), BigInt(2)]), new Set([BigInt(1), BigInt(2)])],
].forEach(([a, b]) => {
test(`{pass: true} expect(${stringify(a)}).not.toEqual(${stringify(
b,
)})`, () => {
expectUnderTest(a).toEqual(b);
expect(() => expectUnderTest(a).not.toEqual(b)).toThrow('toEqual');
});
});
test('assertion error matcherResult property contains matcher name, expected and actual values', () => {
const actual = { a: 1 };
const expected = { a: 2 };
try {
expectUnderTest(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 = [];
actual1[mySymbol] = 3;
const actual2 = [];
actual2[mySymbol] = 4;
const expected = [];
expected[mySymbol] = 3;
expect(actual1).toEqual(expected);
expect(actual2).not.toEqual(expected);
});
test('non-enumerable members should be skipped during equal', () => {
const actual = {
x: 3,
};
Object.defineProperty(actual, 'test', {
enumerable: false,
value: 5,
});
expect(actual).toEqual({ x: 3 });
});
test('non-enumerable symbolic members should be skipped during equal', () => {
const actual = {
x: 3,
};
const mySymbol = Symbol('test');
Object.defineProperty(actual, mySymbol, {
enumerable: false,
value: 5,
});
expect(actual).toEqual({ x: 3 });
});
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);
const c: any = {};
c.x = a;
const d: any = {};
d.x = b;
expect(c).toEqual(d);
expect(d).toEqual(c);
});
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);
const c: any = {};
c.x = a;
const d: any = {};
d.x = b;
expect(c).not.toEqual(d);
expect(d).not.toEqual(c);
});
test('are not equal if circularity is not on the same property', () => {
const a: any = {};
const b: any = {};
a.a = a;
b.a = {};
b.a.a = a;
expect(a).not.toEqual(b);
expect(b).not.toEqual(a);
const c: any = {};
c.x = { x: c };
const d: any = {};
d.x = d;
expect(c).not.toEqual(d);
expect(d).not.toEqual(c);
});
});
});
test.describe('.toBeInstanceOf()', () => {
class A { }
class B { }
class C extends B { }
class D extends C { }
class E extends D { }
class SubHasStaticNameMethod extends B {
constructor() {
super();
}
static name() { }
}
class HasStaticNameMethod {
constructor() { }
static name() { }
}
function DefinesNameProp() { }
Object.defineProperty(DefinesNameProp, 'name', {
configurable: true,
enumerable: false,
value: '',
writable: true,
});
// @ts-ignore
class SubHasNameProp extends DefinesNameProp { }
[
[new Map(), Map],
[[], Array],
[new A(), A],
[new C(), B], // C extends B
[new E(), B], // E extends … extends B
[new SubHasNameProp(), DefinesNameProp], // omit extends
[new SubHasStaticNameMethod(), B], // Received
[new HasStaticNameMethod(), HasStaticNameMethod], // Expected
].forEach(([a, b], index) => {
test(`passing ${stringify(a)} and ${stringify(b)} (${index})`, () => {
expect(() =>
expectUnderTest(a).not.toBeInstanceOf(b),
).toThrowErrorMatchingSnapshot();
expectUnderTest(a).toBeInstanceOf(b);
});
});
[
['a', String],
[1, Number],
[true, Boolean],
[new A(), B],
[Object.create(null), A],
[undefined, String],
[null, String],
[/\w+/, function() { }],
[new DefinesNameProp(), RegExp],
].forEach(([a, b], index) => {
test(`failing ${stringify(a)} and ${stringify(b)} (${index})`, () => {
expect(() =>
expectUnderTest(a).toBeInstanceOf(b),
).toThrowErrorMatchingSnapshot();
expectUnderTest(a).not.toBeInstanceOf(b);
});
});
test('throws if constructor is not a function', () => {
expect(() =>
expectUnderTest({}).toBeInstanceOf(4),
).toThrowErrorMatchingSnapshot();
});
});
test.describe('.toBeTruthy(), .toBeFalsy()', () => {
test('does not accept arguments', () => {
expect(() => expectUnderTestAsAny(0).toBeTruthy(null)).toThrowErrorMatchingSnapshot();
expect(() =>
expectUnderTestAsAny(0).not.toBeFalsy(null),
).toThrowErrorMatchingSnapshot();
});
[{}, [], true, 1, 'a', 0.5, new Map(), () => { }, Infinity].forEach(v => {
test(`'${stringify(v)}' is truthy`, () => {
expectUnderTest(v).toBeTruthy();
expectUnderTest(v).not.toBeFalsy();
expect(() =>
expectUnderTest(v).not.toBeTruthy(),
).toThrowErrorMatchingSnapshot();
expect(() => expectUnderTest(v).toBeFalsy()).toThrowErrorMatchingSnapshot();
});
});
[BigInt(1)].forEach(v => {
test(`'${stringify(v)}' is truthy`, () => {
expectUnderTest(v).toBeTruthy();
expectUnderTest(v).not.toBeFalsy();
expect(() => expectUnderTest(v).not.toBeTruthy()).toThrow('toBeTruthy');
expect(() => expectUnderTest(v).toBeFalsy()).toThrow('toBeFalsy');
});
});
[false, null, NaN, 0, '', undefined].forEach(v => {
test(`'${stringify(v)}' is falsy`, () => {
expectUnderTest(v).toBeFalsy();
expectUnderTest(v).not.toBeTruthy();
expect(() => expectUnderTest(v).toBeTruthy()).toThrowErrorMatchingSnapshot();
expect(() =>
expectUnderTest(v).not.toBeFalsy(),
).toThrowErrorMatchingSnapshot();
});
});
[BigInt(0)].forEach(v => {
test(`'${stringify(v)}' is falsy`, () => {
expectUnderTest(v).toBeFalsy();
expectUnderTest(v).not.toBeTruthy();
expect(() => expectUnderTest(v).toBeTruthy()).toThrow('toBeTruthy');
expect(() => expectUnderTest(v).not.toBeFalsy()).toThrow('toBeFalsy');
});
});
});
test.describe('.toBeNaN()', () => {
test('{pass: true} expect(NaN).toBeNaN()', () => {
[NaN, Math.sqrt(-1), Infinity - Infinity, 0 / 0].forEach(v => {
expectUnderTest(v).toBeNaN();
expect(() => expectUnderTest(v).not.toBeNaN()).toThrowErrorMatchingSnapshot();
});
});
test('throws', () => {
[1, '', null, undefined, {}, [], 0.2, 0, Infinity, -Infinity].forEach(v => {
expect(() => expectUnderTest(v).toBeNaN()).toThrowErrorMatchingSnapshot();
expectUnderTest(v).not.toBeNaN();
});
});
});
test.describe('.toBeNull()', () => {
[{}, [], true, 1, 'a', 0.5, new Map(), () => { }, Infinity].forEach(v => {
test(`fails for '${stringify(v)}'`, () => {
expectUnderTest(v).not.toBeNull();
expect(() => expectUnderTest(v).toBeNull()).toThrowErrorMatchingSnapshot();
});
});
test('fails for null with .not', () => {
expect(() =>
expectUnderTest(null).not.toBeNull(),
).toThrowErrorMatchingSnapshot();
});
test('pass for null', () => {
expectUnderTest(null).toBeNull();
});
});
test.describe('.toBeDefined(), .toBeUndefined()', () => {
[{}, [], true, 1, 'a', 0.5, new Map(), () => { }, Infinity].forEach(v => {
test(`'${stringify(v)}' is defined`, () => {
expectUnderTest(v).toBeDefined();
expectUnderTest(v).not.toBeUndefined();
expect(() =>
expectUnderTest(v).not.toBeDefined(),
).toThrowErrorMatchingSnapshot();
expect(() =>
expectUnderTest(v).toBeUndefined(),
).toThrowErrorMatchingSnapshot();
});
});
[BigInt(1)].forEach(v => {
test(`'${stringify(v)}' is defined`, () => {
expectUnderTest(v).toBeDefined();
expectUnderTest(v).not.toBeUndefined();
expect(() => expectUnderTest(v).not.toBeDefined()).toThrow('toBeDefined');
expect(() => expectUnderTest(v).toBeUndefined()).toThrow('toBeUndefined');
});
});
test('undefined is undefined', () => {
expectUnderTest(undefined).toBeUndefined();
expectUnderTest(undefined).not.toBeDefined();
expect(() =>
expectUnderTest(undefined).toBeDefined(),
).toThrowErrorMatchingSnapshot();
expect(() =>
expectUnderTest(undefined).not.toBeUndefined(),
).toThrowErrorMatchingSnapshot();
});
});
test.describe(
'.toBeGreaterThan(), .toBeLessThan(), ' +
'.toBeGreaterThanOrEqual(), .toBeLessThanOrEqual()',
() => {
[
[1, 2],
[-Infinity, Infinity],
[Number.MIN_VALUE, Number.MAX_VALUE],
[0x11, 0x22],
[0b11, 0b111],
[0o11, 0o22],
[0.1, 0.2],
].forEach(([small, big]) => {
test(`{pass: true} expect(${small}).toBeLessThan(${big})`, () => {
expectUnderTest(small).toBeLessThan(big);
});
test(`{pass: false} expect(${big}).toBeLessThan(${small})`, () => {
expectUnderTest(big).not.toBeLessThan(small);
});
test(`{pass: true} expect(${big}).toBeGreaterThan(${small})`, () => {
expectUnderTest(big).toBeGreaterThan(small);
});
test(`{pass: false} expect(${small}).toBeGreaterThan(${big})`, () => {
expectUnderTest(small).not.toBeGreaterThan(big);
});
test(`{pass: true} expect(${small}).toBeLessThanOrEqual(${big})`, () => {
expectUnderTest(small).toBeLessThanOrEqual(big);
});
test(`{pass: false} expect(${big}).toBeLessThanOrEqual(${small})`, () => {
expectUnderTest(big).not.toBeLessThanOrEqual(small);
});
test(`{pass: true} expect(${big}).toBeGreaterThanOrEqual(${small})`, () => {
expectUnderTest(big).toBeGreaterThanOrEqual(small);
});
test(`{pass: false} expect(${small}).toBeGreaterThanOrEqual(${big})`, () => {
expectUnderTest(small).not.toBeGreaterThanOrEqual(big);
});
test(`throws: [${small}, ${big}]`, () => {
expect(() =>
expectUnderTest(small).toBeGreaterThan(big),
).toThrowErrorMatchingSnapshot();
expect(() =>
expectUnderTest(small).not.toBeLessThan(big),
).toThrowErrorMatchingSnapshot();
expect(() =>
expectUnderTest(big).not.toBeGreaterThan(small),
).toThrowErrorMatchingSnapshot();
expect(() =>
expectUnderTest(big).toBeLessThan(small),
).toThrowErrorMatchingSnapshot();
expect(() =>
expectUnderTest(small).toBeGreaterThanOrEqual(big),
).toThrowErrorMatchingSnapshot();
expect(() =>
expectUnderTest(small).not.toBeLessThanOrEqual(big),
).toThrowErrorMatchingSnapshot();
expect(() =>
expectUnderTest(big).not.toBeGreaterThanOrEqual(small),
).toThrowErrorMatchingSnapshot();
expect(() =>
expectUnderTest(big).toBeLessThanOrEqual(small),
).toThrowErrorMatchingSnapshot();
});
});
test('can compare BigInt to Numbers', () => {
const a = BigInt(2);
expectUnderTest(a).toBeGreaterThan(1);
expectUnderTest(a).toBeGreaterThanOrEqual(2);
expectUnderTest(2).toBeLessThanOrEqual(a);
expectUnderTest(a).toBeLessThan(3);
expectUnderTest(a).toBeLessThanOrEqual(2);
});
[
[BigInt(1), BigInt(2)],
[BigInt(0x11), BigInt(0x22)],
[-1, BigInt(2)],
].forEach(([small, big]) => {
test(`{pass: true} expect(${stringify(small)}).toBeLessThan(${stringify(
big,
)})`, () => {
expectUnderTest(small).toBeLessThan(big);
});
test(`{pass: false} expect(${stringify(big)}).toBeLessThan(${stringify(
small,
)})`, () => {
expectUnderTest(big).not.toBeLessThan(small);
});
test(`{pass: true} expect(${stringify(big)}).toBeGreaterThan(${stringify(
small,
)})`, () => {
expectUnderTest(big).toBeGreaterThan(small);
});
test(`{pass: false} expect(${stringify(small)}).toBeGreaterThan(${stringify(
big,
)})`, () => {
expectUnderTest(small).not.toBeGreaterThan(big);
});
test(`{pass: true} expect(${stringify(
small,
)}).toBeLessThanOrEqual(${stringify(big)})`, () => {
expectUnderTest(small).toBeLessThanOrEqual(big);
});
test(`{pass: false} expect(${stringify(
big,
)}).toBeLessThanOrEqual(${stringify(small)})`, () => {
expectUnderTest(big).not.toBeLessThanOrEqual(small);
});
test(`{pass: true} expect(${stringify(
big,
)}).toBeGreaterThanOrEqual(${stringify(small)})`, () => {
expectUnderTest(big).toBeGreaterThanOrEqual(small);
});
test(`{pass: false} expect(${stringify(
small,
)}).toBeGreaterThanOrEqual(${stringify(big)})`, () => {
expectUnderTest(small).not.toBeGreaterThanOrEqual(big);
});
test(`throws: [${stringify(small)}, ${stringify(big)}]`, () => {
expect(() => expectUnderTest(small).toBeGreaterThan(big)).toThrow(
'toBeGreaterThan',
);
expect(() => expectUnderTest(small).not.toBeLessThan(big)).toThrow(
'toBeLessThan',
);
expect(() => expectUnderTest(big).not.toBeGreaterThan(small)).toThrow(
'toBeGreaterThan',
);
expect(() => expectUnderTest(big).toBeLessThan(small)).toThrow(
'toBeLessThan',
);
expect(() => expectUnderTest(small).toBeGreaterThanOrEqual(big)).toThrow(
'toBeGreaterThanOrEqual',
);
expect(() => expectUnderTest(small).not.toBeLessThanOrEqual(big)).toThrow(
'toBeLessThanOrEqual',
);
expect(() => expectUnderTest(big).not.toBeGreaterThanOrEqual(small)).toThrow(
'toBeGreaterThanOrEqual',
);
expect(() => expectUnderTest(big).toBeLessThanOrEqual(small)).toThrow(
'toBeLessThanOrEqual',
);
});
});
[
[1, 1],
[Number.MIN_VALUE, Number.MIN_VALUE],
[Number.MAX_VALUE, Number.MAX_VALUE],
[Infinity, Infinity],
[-Infinity, -Infinity],
].forEach(([n1, n2]) => {
test(`equal numbers: [${n1}, ${n2}]`, () => {
expectUnderTest(n1).toBeGreaterThanOrEqual(n2);
expectUnderTest(n1).toBeLessThanOrEqual(n2);
expect(() =>
expectUnderTest(n1).not.toBeGreaterThanOrEqual(n2),
).toThrowErrorMatchingSnapshot();
expect(() =>
expectUnderTest(n1).not.toBeLessThanOrEqual(n2),
).toThrowErrorMatchingSnapshot();
});
});
[
[BigInt(1), BigInt(1)],
[BigInt(Number.MAX_SAFE_INTEGER), BigInt(Number.MAX_SAFE_INTEGER)],
].forEach(([n1, n2], index) => {
test(`equal numbers: [${n1}, ${n2}] (${index})`, () => {
expectUnderTest(n1).toBeGreaterThanOrEqual(n2);
expectUnderTest(n1).toBeLessThanOrEqual(n2);
expect(() => expectUnderTest(n1).not.toBeGreaterThanOrEqual(n2)).toThrow(
'toBeGreaterThanOrEqual',
);
expect(() => expectUnderTest(n1).not.toBeLessThanOrEqual(n2)).toThrow(
'toBeLessThanOrEqual',
);
});
});
},
);
test.describe('.toContain(), .toContainEqual()', () => {
const typedArray = new Int8Array(2);
typedArray[0] = 0;
typedArray[1] = 1;
test('iterable', () => {
// different node versions print iterable differently, so we can't
// use snapshots here.
const iterable = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
},
};
expectUnderTest(iterable).toContain(2);
expectUnderTest(iterable).toContainEqual(2);
expect(() => expectUnderTest(iterable).not.toContain(1)).toThrow('toContain');
expect(() => expectUnderTest(iterable).not.toContainEqual(1)).toThrow(
'toContainEqual',
);
});
[
[[1, 2, 3, 4], 1],
[['a', 'b', 'c', 'd'], 'a'],
[[undefined, null], null],
[[undefined, null], undefined],
[[Symbol.for('a')], Symbol.for('a')],
['abcdef', 'abc'],
['11112111', '2'],
[new Set(['abc', 'def']), 'abc'],
[typedArray, 1],
].forEach(([list, v]) => {
test(`'${stringify(list)}' contains '${stringify(v)}'`, () => {
expectUnderTest(list).toContain(v);
expect(() =>
expectUnderTest(list).not.toContain(v),
).toThrowErrorMatchingSnapshot();
});
});
[
[[BigInt(1), BigInt(2), BigInt(3), BigInt(4)], BigInt(1)],
[[1, 2, 3, BigInt(3), 4], BigInt(3)],
].forEach(([list, v]) => {
test(`'${stringify(list)}' contains '${stringify(v)}'`, () => {
expectUnderTest(list).toContain(v);
expect(() => expectUnderTest(list).not.toContain(v)).toThrow('toContain');
});
});
[
[[1, 2, 3], 4],
[[null, undefined], 1],
[[{}, []], []],
[[{}, []], {}],
].forEach(([list, v]) => {
test(`'${stringify(list)}' does not contain '${stringify(v)}'`, () => {
expectUnderTest(list).not.toContain(v);
expect(() =>
expectUnderTest(list).toContain(v),
).toThrowErrorMatchingSnapshot();
});
});
[[[BigInt(1), BigInt(2), BigInt(3)], 3]].forEach(([list, v]) => {
test(`'${stringify(list)}' does not contain '${stringify(v)}'`, () => {
expectUnderTest(list).not.toContain(v);
expect(() => expectUnderTest(list).toContain(v)).toThrow('toContain');
});
});
test('error cases', () => {
expect(() => expectUnderTest(null).toContain(1)).toThrowErrorMatchingSnapshot();
expect(() => expectUnderTest('-0').toContain(-0)).toThrowErrorMatchingSnapshot();
expect(() =>
expectUnderTest('null').toContain(null),
).toThrowErrorMatchingSnapshot();
expect(() =>
expectUnderTest('undefined').toContain(undefined),
).toThrowErrorMatchingSnapshot();
expect(() =>
expectUnderTest('false').toContain(false),
).toThrowErrorMatchingSnapshot();
expect(() => expectUnderTest('1').toContain(BigInt(1))).toThrow('toContain');
});
[
[[1, 2, 3, 4], 1],
[['a', 'b', 'c', 'd'], 'a'],
[[undefined, null], null],
[[undefined, null], undefined],
[[Symbol.for('a')], Symbol.for('a')],
[[{ a: 'b' }, { a: 'c' }], { a: 'b' }],
[new Set([1, 2, 3, 4]), 1],
[typedArray, 1],
].forEach(([list, v]) => {
test(`'${stringify(list)}' contains a value equal to '${stringify(
v,
)}'`, () => {
expectUnderTest(list).toContainEqual(v);
expect(() =>
expectUnderTest(list).not.toContainEqual(v),
).toThrowErrorMatchingSnapshot();
});
});
[[[{ a: 'b' }, { a: 'c' }], { a: 'd' }]].forEach(([list, v]) => {
test(`'${stringify(list)}' does not contain a value equal to'${stringify(
v,
)}'`, () => {
expectUnderTest(list).not.toContainEqual(v);
expect(() =>
expectUnderTest(list).toContainEqual(v),
).toThrowErrorMatchingSnapshot();
});
});
test('error cases for toContainEqual', () => {
expect(() =>
expectUnderTest(null).toContainEqual(1),
).toThrowErrorMatchingSnapshot();
});
});
test.describe('.toBeCloseTo', () => {
[
[0, 0],
[0, 0.001],
[1.23, 1.229],
[1.23, 1.226],
[1.23, 1.225],
[1.23, 1.234],
[Infinity, Infinity],
[-Infinity, -Infinity],
].forEach(([n1, n2]) => {
test(`{pass: true} expect(${n1}).toBeCloseTo(${n2})`, () => {
expectUnderTest(n1).toBeCloseTo(n2);
expect(() =>
expectUnderTest(n1).not.toBeCloseTo(n2),
).toThrowErrorMatchingSnapshot();
});
});
[
[0, 0.01],
[1, 1.23],
[1.23, 1.2249999],
[Infinity, -Infinity],
[Infinity, 1.23],
[-Infinity, -1.23],
].forEach(([n1, n2]) => {
test(`{pass: false} expect(${n1}).toBeCloseTo(${n2})`, () => {
expectUnderTest(n1).not.toBeCloseTo(n2);
expect(() =>
expectUnderTest(n1).toBeCloseTo(n2),
).toThrowErrorMatchingSnapshot();
});
});
[
[3.141592e-7, 3e-7, 8],
[56789, 51234, -4],
].forEach(([n1, n2, p]) => {
test(`{pass: false} expect(${n1}).toBeCloseTo(${n2}, ${p})`, () => {
expectUnderTest(n1).not.toBeCloseTo(n2, p);
expect(() =>
expectUnderTest(n1).toBeCloseTo(n2, p),
).toThrowErrorMatchingSnapshot();
});
});
[
[0, 0.1, 0],
[0, 0.0001, 3],
[0, 0.000004, 5],
[2.0000002, 2, 5],
].forEach(([n1, n2, p]) => {
test(`{pass: true} expect(${n1}).toBeCloseTo(${n2}, ${p})`, () => {
expectUnderTest(n1).toBeCloseTo(n2, p);
expect(() =>
expectUnderTest(n1).not.toBeCloseTo(n2, p),
).toThrowErrorMatchingSnapshot();
});
});
test.describe('throws: Matcher error', () => {
test('promise empty isNot false received', () => {
const precision = 3;
const expected = 0;
const received = '';
expect(() => {
expectUnderTest(received).toBeCloseTo(expected, precision);
}).toThrowErrorMatchingSnapshot();
});
test('promise empty isNot true expected', () => {
const received = 0.1;
// expected is undefined
expect(() => {
expectUnderTestAsAny(received).not.toBeCloseTo();
}).toThrowErrorMatchingSnapshot();
});
test('promise rejects isNot false expected', () => {
const expected = '0';
const received = Promise.reject(0.01);
return expect(
expectUnderTestAsAny(received).rejects.toBeCloseTo(expected),
// @ts-ignore
).rejects.toThrowErrorMatchingSnapshotAsync();
});
test('promise rejects isNot true received', () => {
const expected = 0;
const received = Promise.reject(Symbol('0.1'));
return expect(
expectUnderTest(received).rejects.not.toBeCloseTo(expected),
// @ts-ignore
).rejects.toThrowErrorMatchingSnapshotAsync();
});
test('promise resolves isNot false received', () => {
const precision = 3;
const expected = 0;
const received = Promise.resolve(false);
return expect(
expectUnderTest(received).resolves.toBeCloseTo(expected, precision),
// @ts-ignore
).rejects.toThrowErrorMatchingSnapshotAsync();
});
test('promise resolves isNot true expected', () => {
const precision = 3;
const expected = null;
const received = Promise.resolve(0.1);
return expect(
expectUnderTest(received).resolves.not.toBeCloseTo(expected, precision),
// @ts-ignore
).rejects.toThrowErrorMatchingSnapshotAsync();
});
});
});
test.describe('.toMatch()', () => {
[
['foo', 'foo'],
['Foo bar', /^foo/i],
].forEach(([n1, n2]) => {
test(`{pass: true} expect(${n1}).toMatch(${n2})`, () => {
expectUnderTest(n1).toMatch(n2);
expect(() =>
expectUnderTest(n1).not.toMatch(n2),
).toThrowErrorMatchingSnapshot();
});
});
[
['bar', 'foo'],
['bar', /foo/],
].forEach(([n1, n2]) => {
test(`throws: [${n1}, ${n2}]`, () => {
expect(() => expectUnderTest(n1).toMatch(n2)).toThrowErrorMatchingSnapshot();
});
});
[
[1, 'foo'],
[{}, 'foo'],
[[], 'foo'],
[true, 'foo'],
[/foo/i, 'foo'],
[() => { }, 'foo'],
[undefined, 'foo'],
].forEach(([n1, n2]) => {
test(
'throws if non String actual value passed:' +
` [${stringify(n1)}, ${stringify(n2)}]`,
() => {
// @ts-ignore
expect(() => expectUnderTest(n1).toMatch(n2)).toThrowErrorMatchingSnapshot();
},
);
});
[
['foo', 1],
['foo', {}],
['foo', []],
['foo', true],
['foo', () => { }],
['foo', undefined],
].forEach(([n1, n2]) => {
test(
'throws if non String/RegExp expected value passed:' +
` [${stringify(n1)}, ${stringify(n2)}]`,
() => {
// @ts-ignore
expect(() => expectUnderTest(n1).toMatch(n2)).toThrowErrorMatchingSnapshot();
},
);
});
test('escapes strings properly', () => {
expectUnderTest('this?: throws').toMatch('this?: throws');
});
test('does not maintain RegExp state between calls', () => {
const regex = /[f]\d+/gi;
expectUnderTest('f123').toMatch(regex);
expectUnderTest('F456').toMatch(regex);
expectUnderTest(regex.lastIndex).toBe(0);
});
});
test.describe('.toHaveLength', () => {
[
[[1, 2], 2],
[[], 0],
[['a', 'b'], 2],
['abc', 3],
['', 0],
[() => { }, 0],
].forEach(([received, length]) => {
test(`{pass: true} expect(${stringify(
received,
)}).toHaveLength(${length})`, () => {
expectUnderTestAsAny(received).toHaveLength(length);
expect(() =>
expectUnderTestAsAny(received).not.toHaveLength(length),
).toThrowErrorMatchingSnapshot();
});
});
[
[[1, 2], 3],
[[], 1],
[['a', 'b'], 99],
['abc', 66],
['', 1],
].forEach(([received, length]) => {
test(`{pass: false} expect(${stringify(
received,
)}).toHaveLength(${length})`, () => {
expectUnderTestAsAny(received).not.toHaveLength(length);
expect(() =>
expectUnderTestAsAny(received).toHaveLength(length),
).toThrowErrorMatchingSnapshot();
});
});
test('error cases', () => {
expect(() =>
expectUnderTest({ a: 9 }).toHaveLength(1),
).toThrowErrorMatchingSnapshot();
expect(() => expectUnderTest(0).toHaveLength(1)).toThrowErrorMatchingSnapshot();
expect(() =>
expectUnderTest(undefined).not.toHaveLength(1),
).toThrowErrorMatchingSnapshot();
});
test.describe('matcher error expected length', () => {
test('not number', () => {
const expected = '3';
const received = 'abc';
expect(() => {
expectUnderTestAsAny(received).not.toHaveLength(expected);
}).toThrowErrorMatchingSnapshot();
});
test('number Infinity', () => {
const expected = Infinity;
const received = Promise.reject('abc');
return expect(
expectUnderTest(received).rejects.toHaveLength(expected),
// @ts-ignore
).rejects.toThrowErrorMatchingSnapshotAsync();
});
test('number NaN', () => {
const expected = NaN;
const received = Promise.reject('abc');
return expect(
expectUnderTest(received).rejects.not.toHaveLength(expected),
// @ts-ignore
).rejects.toThrowErrorMatchingSnapshotAsync();
});
test('number float', () => {
const expected = 0.5;
const received = Promise.resolve('abc');
return expect(
expectUnderTest(received).resolves.toHaveLength(expected),
// @ts-ignore
).rejects.toThrowErrorMatchingSnapshotAsync();
});
test('number negative integer', () => {
const expected = -3;
const received = Promise.resolve('abc');
return expect(
expectUnderTest(received).resolves.not.toHaveLength(expected),
// @ts-ignore
).rejects.toThrowErrorMatchingSnapshotAsync();
});
});
});
test.describe('.toHaveProperty()', () => {
class Foo {
val: any;
get a() {
return undefined;
}
get b() {
return 'b';
}
set setter(val) {
this.val = val;
}
}
class Foo2 extends Foo {
get c() {
return 'c';
}
}
const foo2 = new Foo2();
foo2.setter = true;
function E(nodeName) {
this.nodeName = nodeName.toUpperCase();
}
E.prototype.nodeType = 1;
const memoized = function() { };
memoized.memo = [];
const pathDiff = ['children', 0];
const receivedDiffSingle = {
children: ['"That cartoon"'],
props: null,
type: 'p',
};
const valueDiffSingle = '"That cat cartoon"';
const receivedDiffMultiple = {
children: [
'Roses are red.\nViolets are blue.\nTesting with Jest is good for you.',
],
props: null,
type: 'pre',
};
const valueDiffMultiple =
'Roses are red, violets are blue.\nTesting with Jest\nIs good for you.';
[
[{ 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],
[{ a: { b: [{ c: { d: [{ e: 1 }, { f: 2 }] } }] } }, 'a.b[0].c.d[1].f', 2],
[{ a: { b: [[{ c: [{ d: 1 }] }]] } }, 'a.b[0][0].c[0].d', 1],
[Object.assign(Object.create(null), { property: 1 }), 'property', 1],
[new Foo(), 'a', undefined],
[new Foo(), 'b', 'b'],
[new Foo(), 'setter', undefined],
[foo2, 'a', undefined],
[foo2, 'c', 'c'],
[foo2, 'val', true],
[new E('div'), 'nodeType', 1],
['', 'length', 0],
[memoized, 'memo', []],
[{ '': 1 }, '', 1],
].forEach(([obj, keyPath, value]) => {
test(`{pass: true} expect(${stringify(
obj,
)}).toHaveProperty('${keyPath}', ${stringify(value)})`, () => {
expectUnderTestAsAny(obj).toHaveProperty(keyPath, value);
expect(() =>
expectUnderTestAsAny(obj).not.toHaveProperty(keyPath, value),
).toThrowErrorMatchingSnapshot();
});
});
[
[{ 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.d': 1 }, ['a.b.c.d'], 2],
[receivedDiffSingle, pathDiff, valueDiffSingle],
[receivedDiffMultiple, pathDiff, valueDiffMultiple],
[{ 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],
[1, 'a.b.c', 'test'],
['abc', 'a.b.c', { a: 5 }],
[{ a: { b: { c: 5 } } }, 'a.b', { c: 4 }],
[new Foo(), 'a', 'a'],
[new Foo(), 'b', undefined],
[{ a: {} }, 'a.b', undefined],
].forEach(([obj, keyPath, value], index) => {
test(`{pass: false} expect(${stringify(
obj,
)}).toHaveProperty('${keyPath}', ${stringify(value)}) (${index})`, () => {
expect(() =>
expectUnderTestAsAny(obj).toHaveProperty(keyPath, value),
).toThrowErrorMatchingSnapshot();
expectUnderTestAsAny(obj).not.toHaveProperty(keyPath, value);
});
});
[
[{ 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: { b: [1, 2, 3] } }, ['a', 'b', 1]],
[{ a: 0 }, 'a'],
[{ a: { b: undefined } }, 'a.b'],
].forEach(([obj, keyPath]) => {
test(`{pass: true} expect(${stringify(
obj,
)}).toHaveProperty('${keyPath}')`, () => {
expectUnderTestAsAny(obj).toHaveProperty(keyPath);
expect(() =>
expectUnderTestAsAny(obj).not.toHaveProperty(keyPath),
).toThrowErrorMatchingSnapshot();
});
});
[
[{ a: { b: { c: {} } } }, 'a.b.c.d'],
[{ a: { b: { c: {} } } }, '.a.b.c'],
[{ a: 1 }, 'a.b.c.d'],
[{}, 'a'],
[1, 'a.b.c'],
['abc', 'a.b.c'],
[false, 'key'],
[0, 'key'],
['', 'key'],
[Symbol(), 'key'],
[Object.assign(Object.create(null), { key: 1 }), 'not'],
].forEach(([obj, keyPath]) => {
test(`{pass: false} expect(${stringify(
obj,
)}).toHaveProperty('${keyPath}')`, () => {
expect(() =>
expectUnderTest(obj).toHaveProperty(keyPath),
).toThrowErrorMatchingSnapshot();
expectUnderTest(obj).not.toHaveProperty(keyPath);
});
});
[
[null, 'a.b'],
[undefined, 'a'],
[{ a: { b: {} } }, undefined],
[{ a: { b: {} } }, null],
[{ a: { b: {} } }, 1],
[{}, []], // Residue: pass must be initialized
].forEach(([obj, keyPath]) => {
test(`{error} expect(${stringify(
obj,
)}).toHaveProperty('${keyPath}')`, () => {
expect(() =>
expectUnderTestAsAny(obj).toHaveProperty(keyPath),
).toThrowErrorMatchingSnapshot();
});
});
});
test.describe('toMatchObject()', () => {
class Foo {
get a() {
return undefined;
}
get b() {
return 'b';
}
}
class Sub extends Foo {
get c() {
return 'c';
}
}
const withDefineProperty = (obj, key, val) => {
Object.defineProperty(obj, key, {
get() {
return val;
},
});
return obj;
};
const testNotToMatchSnapshots = tuples => {
tuples.forEach(([n1, n2]) => {
test(`{pass: true} expect(${stringify(n1)}).toMatchObject(${stringify(
n2,
)})`, () => {
expectUnderTest(n1).toMatchObject(n2);
expect(() =>
expectUnderTest(n1).not.toMatchObject(n2),
).toThrowErrorMatchingSnapshot();
});
});
};
const testToMatchSnapshots = tuples => {
tuples.forEach(([n1, n2]) => {
test(`{pass: false} expect(${stringify(n1)}).toMatchObject(${stringify(
n2,
)})`, () => {
expectUnderTest(n1).not.toMatchObject(n2);
expect(() =>
expectUnderTest(n1).toMatchObject(n2),
).toThrowErrorMatchingSnapshot();
});
});
};
test.describe('circular references', () => {
test.describe('simple circular references', () => {
const circularObjA1: any = { a: 'hello' };
circularObjA1.ref = circularObjA1;
const circularObjB: any = { a: 'world' };
circularObjB.ref = circularObjB;
const circularObjA2: any = { a: 'hello' };
circularObjA2.ref = circularObjA2;
const primitiveInsteadOfRef: any = {};
primitiveInsteadOfRef.ref = 'not a ref';
testNotToMatchSnapshots([
[circularObjA1, {}],
[circularObjA2, circularObjA1],
]);
testToMatchSnapshots([
[{}, circularObjA1],
[circularObjA1, circularObjB],
[primitiveInsteadOfRef, circularObjA1],
]);
});
test.describe('transitive circular references', () => {
const transitiveCircularObjA1: any = { a: 'hello' };
transitiveCircularObjA1.nestedObj = { parentObj: transitiveCircularObjA1 };
const transitiveCircularObjA2: any = { a: 'hello' };
transitiveCircularObjA2.nestedObj = {
parentObj: transitiveCircularObjA2,
};
const transitiveCircularObjB: any = { a: 'world' };
transitiveCircularObjB.nestedObj = {
parentObj: transitiveCircularObjB,
};
const primitiveInsteadOfRef: any = {};
primitiveInsteadOfRef.nestedObj = {
parentObj: 'not the parent ref',
};
testNotToMatchSnapshots([
[transitiveCircularObjA1, {}],
[transitiveCircularObjA2, transitiveCircularObjA1],
]);
testToMatchSnapshots([
[{}, transitiveCircularObjA1],
[transitiveCircularObjB, transitiveCircularObjA1],
[primitiveInsteadOfRef, transitiveCircularObjA1],
]);
});
});
testNotToMatchSnapshots([
[{ 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: 'b', t: { x: { r: 'r' }, z: 'z' } }, { t: { x: { r: 'r' } } }],
[{ a: [3, 4, 5], b: 'b' }, { a: [3, 4, 5] }],
[{ a: [3, 4, 5, 'v'], b: 'b' }, { a: [3, 4, 5, 'v'] }],
[{ a: 1, c: 2 }, { a: expectUnderTest.any(Number) }],
[{ a: { x: 'x', y: 'y' } }, { a: { x: expectUnderTest.any(String) } }],
[new Set([1, 2]), new Set([1, 2])],
[new Set([1, 2]), new Set([2, 1])],
[new Date('2015-11-30'), new Date('2015-11-30')],
[{ a: new Date('2015-11-30'), b: 'b' }, { a: new Date('2015-11-30') }],
[{ a: null, b: 'b' }, { a: null }],
[{ a: undefined, b: 'b' }, { a: undefined }],
[{ a: [{ a: 'a', b: 'b' }] }, { a: [{ a: 'a' }] }],
[
[1, 2],
[1, 2],
],
[{ a: undefined }, { a: undefined }],
[[], []],
[new Error('foo'), new Error('foo')],
[new Error('bar'), { message: 'bar' }],
[new Foo(), { a: undefined, b: 'b' }],
[Object.assign(Object.create(null), { a: 'b' }), { a: 'b' }],
[
{ a: 'b', c: 'd', [Symbol.for('jest')]: 'jest' },
{ a: 'b', [Symbol.for('jest')]: 'jest' },
],
[
{ a: 'b', c: 'd', [Symbol.for('jest')]: 'jest' },
{ a: 'b', c: 'd', [Symbol.for('jest')]: 'jest' },
],
// These snapshots will show {} as the object because the properties
// are not enumerable. We will need to somehow make the serialization of
// these keys a little smarter before reporting accurately.
[new Sub(), { a: undefined, b: 'b', c: 'c' }],
[withDefineProperty(new Sub(), 'd', 4), { d: 4 }],
[{ a: 'b', toString() { } }, { toString: expectUnderTest.any(Function) }],
]);
testToMatchSnapshots([
[{ a: 'b', c: 'd' }, { e: 'b' }],
[
{ a: 'b', c: 'd' },
{ a: 'b!', c: 'd' },
],
[{ a: 'a', c: 'd' }, { a: expectUnderTest.any(Number) }],
[
{ a: 'b', t: { x: { r: 'r' }, z: 'z' } },
{ a: 'b', t: { z: [3] } },
],
[{ a: 'b', t: { x: { r: 'r' }, z: 'z' } }, { t: { l: { r: 'r' } } }],
[{ a: [3, 4, 5], b: 'b' }, { a: [3, 4, 5, 6] }],
[{ a: [3, 4, 5], b: 'b' }, { a: [3, 4] }],
[{ a: [3, 4, 'v'], b: 'b' }, { a: ['v'] }],
[{ a: [3, 4, 5], b: 'b' }, { a: { b: 4 } }],
[{ a: [3, 4, 5], b: 'b' }, { a: { b: expectUnderTest.any(String) } }],
[
[1, 2],
[1, 3],
],
[[0], [-0]],
[new Set([1, 2]), new Set([2])],
[new Date('2015-11-30'), new Date('2015-10-10')],
[{ a: new Date('2015-11-30'), b: 'b' }, { a: new Date('2015-10-10') }],
[{ a: null, b: 'b' }, { a: '4' }],
[{ a: null, b: 'b' }, { a: undefined }],
[{ a: undefined }, { a: null }],
[{ a: [{ a: 'a', b: 'b' }] }, { a: [{ a: 'c' }] }],
[{ a: 1, b: 1, c: 1, d: { e: { f: 555 } } }, { d: { e: { f: 222 } } }],
[{}, { a: undefined }],
[
[1, 2, 3],
[2, 3, 1],
],
[
[1, 2, 3],
[1, 2, 2],
],
[new Error('foo'), new Error('bar')],
[Object.assign(Object.create(null), { a: 'b' }), { c: 'd' }],
[
{ a: 'b', c: 'd', [Symbol.for('jest')]: 'jest' },
{ a: 'c', [Symbol.for('jest')]: expect.any(String) },
],
[{ a: 'b' }, { toString: expectUnderTest.any(Function) }],
]);
[
[null, {}],
[4, {}],
['44', {}],
[true, {}],
[undefined, {}],
[{}, null],
[{}, 4],
[{}, 'some string'],
[{}, true],
[{}, undefined],
].forEach(([n1, n2]: [any, any, string]) => {
test(`throws expect(${stringify(n1)}).toMatchObject(${stringify(
n2,
)})`, () => {
expect(() =>
expectUnderTest(n1).toMatchObject(n2),
).toThrowErrorMatchingSnapshot();
});
});
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;
expectUnderTest(b).not.toMatchObject(matcher);
expect(() =>
expectUnderTest(b).toMatchObject(matcher),
).toThrowErrorMatchingSnapshot();
});
test('toMatchObject ignores symbol key properties', () => {
// issue 13638
const sym = Symbol('foo');
const sym2 = Symbol('foo2');
expectUnderTestAsAny({}).not.toMatchObject({ [sym]: true });
expectUnderTestAsAny({ [sym]: true }).not.toMatchObject({ [sym2]: true });
expectUnderTestAsAny({ [sym]: true }).not.toMatchObject({ [sym]: false });
expectUnderTestAsAny({ example: 10, [sym]: true }).not.toMatchObject({
example: 12,
[sym]: true,
});
expectUnderTestAsAny({ [sym]: true }).toMatchObject({ [sym]: true });
expectUnderTestAsAny({ example: 10, [sym]: true }).toMatchObject({
example: 10,
[sym]: true,
});
});
});