Skip to content

Commit

Permalink
chore: fix types of few expect tests (#13361)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrazauskas authored Oct 3, 2022
1 parent 6675975 commit 9ba555b
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 47 deletions.
76 changes: 51 additions & 25 deletions packages/expect/src/__tests__/asymmetricMatchers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,10 @@ test('Any.toAsymmetricMatcher() with function name', () => {
});

test('Any throws when called with empty constructor', () => {
jestExpect(() => any()).toThrow();
// @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', () => {
Expand Down Expand Up @@ -150,8 +153,9 @@ test('ArrayContaining does not match', () => {

test('ArrayContaining throws for non-arrays', () => {
jestExpect(() => {
// @ts-expect-error: Testing runtime error
arrayContaining('foo').asymmetricMatch([]);
}).toThrow();
}).toThrow("You must provide an array to ArrayContaining, not 'string'.");
});

test('ArrayNotContaining matches', () => {
Expand All @@ -171,8 +175,9 @@ test('ArrayNotContaining does not match', () => {

test('ArrayNotContaining throws for non-arrays', () => {
jestExpect(() => {
// @ts-expect-error: Testing runtime error
arrayNotContaining('foo').asymmetricMatch([]);
}).toThrow();
}).toThrow("You must provide an array to ArrayNotContaining, not 'string'.");
});

test('ObjectContaining matches', () => {
Expand Down Expand Up @@ -224,13 +229,16 @@ test('ObjectContaining matches prototype properties', () => {
function Foo() {}
Foo.prototype = prototypeObject;
Foo.prototype.constructor = Foo;
obj = new Foo();
obj = new (Foo as any)();
}
jestExpect(objectContaining({foo: 'bar'}).asymmetricMatch(obj)).toBe(true);
});

test('ObjectContaining throws for non-objects', () => {
jestExpect(() => objectContaining(1337).asymmetricMatch()).toThrow();
// @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', () => {
Expand All @@ -243,6 +251,8 @@ test('ObjectContaining does not mutate the sample', () => {

test('ObjectNotContaining matches', () => {
[
objectContaining({}).asymmetricMatch(null),
objectContaining({}).asymmetricMatch(undefined),
objectNotContaining({foo: 'foo'}).asymmetricMatch({bar: 'bar'}),
objectNotContaining({foo: 'foo'}).asymmetricMatch({foo: 'foox'}),
objectNotContaining({foo: undefined}).asymmetricMatch({}),
Expand Down Expand Up @@ -278,32 +288,40 @@ test('ObjectNotContaining does not match', () => {
first: objectContaining({second: {}}),
}).asymmetricMatch({first: {second: {}}}),
objectNotContaining({}).asymmetricMatch(null),
objectNotContaining({}).asymmetricMatch(undefined),
objectNotContaining({}).asymmetricMatch({}),
].forEach(test => {
jestExpect(test).toEqual(false);
});
});

test('ObjectNotContaining inverts ObjectContaining', () => {
[
[{}, 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}],
].forEach(([sample, received]) => {
(
[
[{}, 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
).forEach(([sample, received]) => {
jestExpect(objectNotContaining(sample).asymmetricMatch(received)).toEqual(
!objectContaining(sample).asymmetricMatch(received),
);
});
});

test('ObjectNotContaining throws for non-objects', () => {
jestExpect(() => objectNotContaining(1337).asymmetricMatch()).toThrow();
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', () => {
Expand All @@ -313,8 +331,9 @@ test('StringContaining matches string against string', () => {

test('StringContaining throws if expected value is not string', () => {
jestExpect(() => {
// @ts-expect-error: Testing runtime error
stringContaining([1]).asymmetricMatch('queen');
}).toThrow();
}).toThrow('Expected is not a string');
});

test('StringContaining returns false if received value is not string', () => {
Expand All @@ -328,8 +347,9 @@ test('StringNotContaining matches string against string', () => {

test('StringNotContaining throws if expected value is not string', () => {
jestExpect(() => {
// @ts-expect-error: Testing runtime error
stringNotContaining([1]).asymmetricMatch('queen');
}).toThrow();
}).toThrow('Expected is not a string');
});

test('StringNotContaining returns true if received value is not string', () => {
Expand All @@ -348,8 +368,9 @@ test('StringMatching matches string against string', () => {

test('StringMatching throws if expected value is neither string nor regexp', () => {
jestExpect(() => {
// @ts-expect-error: Testing runtime error
stringMatching([1]).asymmetricMatch('queen');
}).toThrow();
}).toThrow('Expected is not a String or a RegExp');
});

test('StringMatching returns false if received value is not string', () => {
Expand All @@ -372,8 +393,9 @@ test('StringNotMatching matches string against string', () => {

test('StringNotMatching throws if expected value is neither string nor regexp', () => {
jestExpect(() => {
// @ts-expect-error: Testing runtime error
stringNotMatching([1]).asymmetricMatch('queen');
}).toThrow();
}).toThrow('Expected is not a String or a RegExp');
});

test('StringNotMatching returns true if received value is not string', () => {
Expand Down Expand Up @@ -451,26 +473,30 @@ describe('closeTo', () => {

test('closeTo throw if expected is not number', () => {
jestExpect(() => {
// @ts-expect-error: Testing runtime error
closeTo('a');
}).toThrow();
}).toThrow('Expected is not a Number');
});

test('notCloseTo throw if expected is not number', () => {
jestExpect(() => {
// @ts-expect-error: Testing runtime error
notCloseTo('a');
}).toThrow();
}).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();
}).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();
}).toThrow('Precision is not a Number');
});

test('closeTo return false if received is not number', () => {
Expand Down
47 changes: 34 additions & 13 deletions packages/expect/src/__tests__/extend.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ expect.addSnapshotSerializer(alignedAnsiStyleSerializer);
jestExpect.extend({
toBeDivisibleBy(actual: number, expected: number) {
const pass = actual % expected === 0;
const message = pass
const message: () => string = pass
? () =>
`expected ${this.utils.printReceived(
actual,
Expand Down Expand Up @@ -51,29 +51,47 @@ jestExpect.extend({
},
});

declare module '../types' {
interface AsymmetricMatchers {
toBeDivisibleBy(expected: number): void;
toBeSymbol(expected: symbol): void;
toBeWithinRange(floor: number, ceiling: number): void;
}
interface Matchers<R> {
toBeDivisibleBy(expected: number): R;
toBeSymbol(expected: symbol): R;
toBeWithinRange(floor: number, ceiling: number): R;

shouldNotError(): R;
toFailWithoutMessage(): R;
toBeOne(): R;
toAllowOverridingExistingMatcher(): R;
}
}

it('is available globally when matcher is unary', () => {
jestExpect(15).toBeDivisibleBy(5);
jestExpect(15).toBeDivisibleBy(3);
jestExpect(15).not.toBeDivisibleBy(6);

jestExpect(() =>
expect(() =>
jestExpect(15).toBeDivisibleBy(2),
).toThrowErrorMatchingSnapshot();
});

it('is available globally when matcher is variadic', () => {
jestExpect(15).toBeWithinRange(10, 20);
jestExpect(15).not.toBeWithinRange(6);
jestExpect(15).not.toBeWithinRange(6, 10);

jestExpect(() =>
expect(() =>
jestExpect(15).toBeWithinRange(1, 3),
).toThrowErrorMatchingSnapshot();
});

it('exposes matcherUtils in context', () => {
jestExpect.extend({
_shouldNotError(_actual: unknown, _expected: unknown) {
const pass = this.equals(
shouldNotError(_actual: unknown) {
const pass: boolean = this.equals(
this.utils,
Object.assign(matcherUtils, {
iterableEquality,
Expand All @@ -88,13 +106,13 @@ it('exposes matcherUtils in context', () => {
},
});

jestExpect()._shouldNotError();
jestExpect('test').shouldNotError();
});

it('is ok if there is no message specified', () => {
jestExpect.extend({
toFailWithoutMessage(_expected: unknown) {
return {pass: false};
return {message: () => '', pass: false};
},
});

Expand All @@ -107,13 +125,13 @@ it('exposes an equality function to custom matchers', () => {
// jestExpect and expect share the same global state
expect.assertions(3);
jestExpect.extend({
toBeOne() {
toBeOne(_expected: unknown) {
expect(this.equals).toBe(equals);
return {pass: !!this.equals(1, 1)};
return {message: () => '', pass: !!this.equals(1, 1)};
},
});

expect(() => jestExpect().toBeOne()).not.toThrow();
expect(() => jestExpect('test').toBeOne()).not.toThrow();
});

it('defines asymmetric unary matchers', () => {
Expand Down Expand Up @@ -170,15 +188,15 @@ it('prints the Symbol into the error message', () => {
it('allows overriding existing extension', () => {
jestExpect.extend({
toAllowOverridingExistingMatcher(_expected: unknown) {
return {pass: _expected === 'bar'};
return {message: () => '', pass: _expected === 'bar'};
},
});

jestExpect('foo').not.toAllowOverridingExistingMatcher();

jestExpect.extend({
toAllowOverridingExistingMatcher(_expected: unknown) {
return {pass: _expected === 'foo'};
return {message: () => '', pass: _expected === 'foo'};
},
});

Expand All @@ -188,20 +206,23 @@ it('allows overriding existing extension', () => {
it('throws descriptive errors for invalid matchers', () => {
expect(() =>
jestExpect.extend({
// @ts-expect-error: Testing runtime error
default: undefined,
}),
).toThrow(
'expect.extend: `default` is not a valid matcher. Must be a function, is "undefined"',
);
expect(() =>
jestExpect.extend({
// @ts-expect-error: Testing runtime error
default: 42,
}),
).toThrow(
'expect.extend: `default` is not a valid matcher. Must be a function, is "number"',
);
expect(() =>
jestExpect.extend({
// @ts-expect-error: Testing runtime error
default: 'foobar',
}),
).toThrow(
Expand Down
28 changes: 19 additions & 9 deletions packages/expect/src/__tests__/stacktrace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,42 @@
import jestExpect from '../';

jestExpect.extend({
toCustomMatch(callback: () => unknown, expectation: unknown) {
toCustomMatch(callback: () => unknown, expected: unknown) {
const actual = callback();

if (actual !== expectation) {
if (actual !== expected) {
return {
message: () => `Expected "${expectation}" but got "${actual}"`,
message: () => `Expected "${expected}" but got "${actual}"`,
pass: false,
};
}

return {pass: true};
return {
message: () => '',
pass: true,
};
},
toMatchPredicate(received: unknown, argument: (a: unknown) => void) {
argument(received);
toMatchPredicate(received: unknown, expected: (a: unknown) => void) {
expected(received);
return {
message: () => '',
pass: true,
};
},
});

declare module '../types' {
interface Matchers<R> {
toCustomMatch(expected: unknown): R;
toMatchPredicate(expected: (a: unknown) => void): R;
}
}

it('stack trace points to correct location when using matchers', () => {
try {
jestExpect(true).toBe(false);
} catch (error: any) {
expect(error.stack).toContain('stacktrace.test.ts:35');
expect(error.stack).toContain('stacktrace.test.ts:45:22');
}
});

Expand All @@ -44,7 +54,7 @@ it('stack trace points to correct location when using nested matchers', () => {
jestExpect(value).toBe(false);
});
} catch (error: any) {
expect(error.stack).toContain('stacktrace.test.ts:44');
expect(error.stack).toContain('stacktrace.test.ts:54:25');
}
});

Expand All @@ -60,6 +70,6 @@ it('stack trace points to correct location when throwing from a custom matcher',
foo();
}).toCustomMatch('bar');
} catch (error: any) {
expect(error.stack).toContain('stacktrace.test.ts:57');
expect(error.stack).toContain('stacktrace.test.ts:67:15');
}
});

0 comments on commit 9ba555b

Please sign in to comment.