Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: adding types to jest matchers #880

Merged
merged 3 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 84 additions & 47 deletions packages/jest/src/__tests__/extensions.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,24 @@ import { describe, it, beforeAll, afterAll, expect } from '@jest/globals';
import fetchMockModule from '../index';
const fetchMock = fetchMockModule.default;

describe('expect extensions', () => {
[
'Fetched',
'Got:get',
'Posted:post',
'Put:put',
'Deleted:delete',
'FetchedHead:head',
'Patched:patch',
].forEach((verbs) => {
const humanVerbToMethods = [
'Fetched',
'Got:get',
'Posted:post',
'Put:put',
'Deleted:delete',
'FetchedHead:head',
'Patched:patch',
];

// initialize a mock here so fetch is patched across all tests
fetchMock.mockGlobal();

describe.each([
['patched fetch input', fetch],
['fetchMock input', fetchMock],
])('expect extensions %s', (_str, expectInput) => {
humanVerbToMethods.forEach((verbs) => {
const [humanVerb, method] = verbs.split(':');
describe(`${humanVerb} expectations`, () => {
describe('when no calls', () => {
Expand All @@ -21,24 +29,26 @@ describe('expect extensions', () => {
});
afterAll(() => fetchMock.mockReset());
it(`toHave${humanVerb} should be falsy`, () => {
expect(fetch).not[`toHave${humanVerb}`]('http://example.com/path');
expect(expectInput).not[`toHave${humanVerb}`](
'http://example.com/path',
);
});

it(`toHaveLast${humanVerb} should be falsy`, () => {
expect(fetch).not[`toHaveLast${humanVerb}`](
expect(expectInput).not[`toHaveLast${humanVerb}`](
'http://example.com/path',
);
});

it(`toHaveNth${humanVerb} should be falsy`, () => {
expect(fetch).not[`toHaveNth${humanVerb}`](
expect(expectInput).not[`toHaveNth${humanVerb}`](
1,
'http://example.com/path',
);
});

it(`toHave${humanVerb}Times should be falsy`, () => {
expect(fetch).not[`toHave${humanVerb}Times`](
expect(expectInput).not[`toHave${humanVerb}Times`](
1,
'http://example.com/path',
);
Expand All @@ -63,31 +73,36 @@ describe('expect extensions', () => {
afterAll(() => fetchMock.mockReset());

it('matches with just url', () => {
expect(fetch)[`toHave${humanVerb}`]('http://example.com/path');
expect(expectInput)[`toHave${humanVerb}`]('http://example.com/path');
});

it('matches with fetch-mock matcher', () => {
expect(fetch)[`toHave${humanVerb}`]('begin:http://example.com/path');
expect(expectInput)[`toHave${humanVerb}`](
'begin:http://example.com/path',
);
});

it('matches with matcher and options', () => {
expect(fetch)[`toHave${humanVerb}`]('http://example.com/path', {
expect(expectInput)[`toHave${humanVerb}`]('http://example.com/path', {
headers: {
test: 'header',
},
});
});

it("doesn't match if matcher but not options is correct", () => {
expect(fetch).not[`toHave${humanVerb}`]('http://example.com/path', {
headers: {
test: 'not-header',
expect(expectInput).not[`toHave${humanVerb}`](
'http://example.com/path',
{
headers: {
test: 'not-header',
},
},
});
);
});

it("doesn't match if options but not matcher is correct", () => {
expect(fetch).not[`toHave${humanVerb}`](
expect(expectInput).not[`toHave${humanVerb}`](
'http://example-no.com/path',
{
headers: {
Expand All @@ -110,25 +125,30 @@ describe('expect extensions', () => {
afterAll(() => fetchMock.mockReset());

it('matches with just url', () => {
expect(fetch)[`toHaveLast${humanVerb}`]('http://example.com/path');
expect(expectInput)[`toHaveLast${humanVerb}`](
'http://example.com/path',
);
});

it('matches with fetch-mock matcher', () => {
expect(fetch)[`toHaveLast${humanVerb}`](
expect(expectInput)[`toHaveLast${humanVerb}`](
'begin:http://example.com/path',
);
});

it('matches with matcher and options', () => {
expect(fetch)[`toHaveLast${humanVerb}`]('http://example.com/path', {
headers: {
test: 'header',
expect(expectInput)[`toHaveLast${humanVerb}`](
'http://example.com/path',
{
headers: {
test: 'header',
},
},
});
);
});

it("doesn't match if matcher but not options is correct", () => {
expect(fetch).not[`toHaveLast${humanVerb}`](
expect(expectInput).not[`toHaveLast${humanVerb}`](
'http://example.com/path',
{
headers: {
Expand All @@ -139,7 +159,7 @@ describe('expect extensions', () => {
});

it("doesn't match if options but not matcher is correct", () => {
expect(fetch).not[`toHaveLast${humanVerb}`](
expect(expectInput).not[`toHaveLast${humanVerb}`](
'http://example-no.com/path',
{
headers: {
Expand Down Expand Up @@ -169,18 +189,21 @@ describe('expect extensions', () => {
afterAll(() => fetchMock.mockReset());

it('matches with just url', () => {
expect(fetch)[`toHaveNth${humanVerb}`](2, 'http://example2.com/path');
expect(expectInput)[`toHaveNth${humanVerb}`](
2,
'http://example2.com/path',
);
});

it('matches with fetch-mock matcher', () => {
expect(fetch)[`toHaveNth${humanVerb}`](
expect(expectInput)[`toHaveNth${humanVerb}`](
2,
'begin:http://example2.com/path',
);
});

it('matches with matcher and options', () => {
expect(fetch)[`toHaveNth${humanVerb}`](
expect(expectInput)[`toHaveNth${humanVerb}`](
2,
'http://example2.com/path',
{
Expand All @@ -192,7 +215,7 @@ describe('expect extensions', () => {
});

it("doesn't match if matcher but not options is correct", () => {
expect(fetch).not[`toHaveNth${humanVerb}`](
expect(expectInput).not[`toHaveNth${humanVerb}`](
2,
'http://example2.com/path',
{
Expand All @@ -204,7 +227,7 @@ describe('expect extensions', () => {
});

it("doesn't match if options but not matcher is correct", () => {
expect(fetch).not[`toHaveNth${humanVerb}`](
expect(expectInput).not[`toHaveNth${humanVerb}`](
2,
'http://example-no.com/path',
{
Expand All @@ -216,7 +239,7 @@ describe('expect extensions', () => {
});

it("doesn't match if wrong n", () => {
expect(fetch).not[`toHaveNth${humanVerb}`](
expect(expectInput).not[`toHaveNth${humanVerb}`](
1,
'http://example2.com/path',
);
Expand All @@ -242,21 +265,21 @@ describe('expect extensions', () => {
afterAll(() => fetchMock.mockReset());

it('matches with just url', () => {
expect(fetch)[`toHave${humanVerb}Times`](
expect(expectInput)[`toHave${humanVerb}Times`](
2,
'http://example.com/path',
);
});

it('matches with fetch-mock matcher', () => {
expect(fetch)[`toHave${humanVerb}Times`](
expect(expectInput)[`toHave${humanVerb}Times`](
2,
'begin:http://example.com/path',
);
});

it('matches with matcher and options', () => {
expect(fetch)[`toHave${humanVerb}Times`](
expect(expectInput)[`toHave${humanVerb}Times`](
2,
'http://example.com/path',
{
Expand All @@ -268,7 +291,7 @@ describe('expect extensions', () => {
});

it("doesn't match if matcher but not options is correct", () => {
expect(fetch).not[`toHave${humanVerb}Times`](
expect(expectInput).not[`toHave${humanVerb}Times`](
2,
'http://example.com/path',
{
Expand All @@ -280,7 +303,7 @@ describe('expect extensions', () => {
});

it("doesn't match if options but not matcher is correct", () => {
expect(fetch).not[`toHave${humanVerb}Times`](
expect(expectInput).not[`toHave${humanVerb}Times`](
2,
'http://example-no.com/path',
{
Expand All @@ -292,14 +315,14 @@ describe('expect extensions', () => {
});

it("doesn't match if too few calls", () => {
expect(fetch).not[`toHave${humanVerb}Times`](
expect(expectInput).not[`toHave${humanVerb}Times`](
1,
'http://example.com/path',
);
});

it("doesn't match if too many calls", () => {
expect(fetch).not[`toHave${humanVerb}Times`](
expect(expectInput).not[`toHave${humanVerb}Times`](
3,
'http://example.com/path',
);
Expand All @@ -326,15 +349,29 @@ describe('expect extensions', () => {
});
afterAll(() => fetchMock.mockReset());
// it('toBeDone should be falsy only if routes defined', () => {
// expect(fetch).not.toBeDone();
// expect(fetch).not.toBeDone('my-route');
// expect(expectInput).not.toBeDone();
// expect(expectInput).not.toBeDone('my-route');
// });
it('matches with just url', () => {
expect(fetch).toBeDone('route1');
expect(expectInput).toBeDone('route1');
});

it("doesn't match if too few calls", () => {
expect(fetch).not.toBeDone('route2');
expect(expectInput).not.toBeDone('route2');
});
});
});

describe('expect extensions: bad inputs', () => {
humanVerbToMethods.forEach((verbs) => {
const [humanVerb] = verbs.split(':');
it(`${humanVerb} - throws an error if we the input is not patched with fetchMock`, () => {
expect(() => {
// This simulates a "fetch" implementation that doesn't have fetchMock
expect({})[`toHave${humanVerb}`]('http://example.com/path');
}).toThrow(
'Unable to get fetchMock instance! Please make sure you passed a patched fetch or fetchMock!',
);
});
});
});
21 changes: 21 additions & 0 deletions packages/jest/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
} from 'fetch-mock';
import './jest-extensions.js';
import type { Jest } from '@jest/environment';
import type { FetchMockMatchers } from './types.js';
export { FetchMockMatchers } from './types.js';

type MockResetOptions = {
includeSticky: boolean;
Expand Down Expand Up @@ -55,3 +57,22 @@ const fetchMockJest = new FetchMockJest({
});

export default fetchMockJest;

/* eslint-disable @typescript-eslint/no-namespace */
/**
* Export types on the expect object
*/
declare global {
namespace jest {
// Type-narrow expect for FetchMock
interface Expect {
(actual: FetchMock): FetchMockMatchers & {
not: FetchMockMatchers;
};
(actual: typeof fetch): FetchMockMatchers & {
not: FetchMockMatchers;
};
}
}
}
/* eslint-enable @typescript-eslint/no-namespace */
Loading