Skip to content

Commit

Permalink
Merge pull request #880 from hanseltime/feat-types-for-jest
Browse files Browse the repository at this point in the history
fix: adding types to jest matchers
  • Loading branch information
wheresrhys authored Nov 29, 2024
2 parents d1ccb8a + 68b24e7 commit 9071395
Show file tree
Hide file tree
Showing 4 changed files with 250 additions and 68 deletions.
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

0 comments on commit 9071395

Please sign in to comment.