Skip to content

Commit

Permalink
feat: start implementing jest and vitest wrappers
Browse files Browse the repository at this point in the history
  • Loading branch information
wheresrhys committed Aug 3, 2024
1 parent ef0fd52 commit 804f885
Show file tree
Hide file tree
Showing 19 changed files with 2,526 additions and 0 deletions.
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

129 changes: 129 additions & 0 deletions packages/jest/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# fetch-mock-jest

Wrapper around [fetch-mock](http://www.wheresrhys.co.uk/fetch-mock) - a comprehensive, isomorphic mock for the [fetch api](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) - which provides an interface that is more idiomatic when working in [jest](https://jestjs.io).

The example at the bottom of this readme demonstrates the intuitive API, but shows off only a fraction of fetch-mock's functionality. Features include:

- mocks most of the fetch API spec, even advanced behaviours such as streaming and aborting
- declarative matching for most aspects of a http request, including url, headers, body and query parameters
- shorthands for the most commonly used features, such as matching a http method or matching one fetch only
- support for delaying responses, or using your own async functions to define custom race conditions
- can be used as a spy to observe real network requests
- isomorphic, and supports either a global fetch instance or a locally required instanceg

# Requirements

fetch-mock-jest requires the following to run:

- [Node.js](https://Node.js.org/) 8+ for full feature operation
- [Node.js](https://Node.js.org/) 0.12+ with [limitations](http://www.wheresrhys.co.uk/fetch-mock/installation)
- [npm](https://www.npmjs.com/package/npm) (normally comes with Node.js)
- [jest](https://www.npmjs.com/package/jest) 25+ (may work with earlier versions, but untested)
- Either
- [node-fetch](https://www.npmjs.com/package/node-fetch) when testing in Node.js. To allow users a choice over which version to use, `node-fetch` is not included as a dependency of `fetch-mock`.
- A browser that supports the `fetch` API either natively or via a [polyfill/ponyfill](https://ponyfoo.com/articles/polyfills-or-ponyfills)

# Installation

`npm install -D fetch-mock-jest`

## global fetch

`const fetchMock = require('fetch-mock-jest')`

## node-fetch

```
jest.mock('node-fetch', () => require('fetch-mock-jest').sandbox())
const fetchMock = require('node-fetch')
```

# API

## Setting up mocks

Please refer to the [fetch-mock documentation](http://wheresrhys.co.uk/fetch-mock) and [cheatsheet](https://github.com/wheresrhys/fetch-mock/blob/master/docs/cheatsheet.md)

All jest methods for configuring mock functions are disabled as fetch-mock's own methods should always be used

## Inspecting mocks

All the built in jest function inspection assertions can be used, e.g. `expect(fetchMock).toHaveBeenCalledWith('http://example.com')`.

`fetchMock.mock.calls` and `fetchMock.mock.results` are also exposed, giving access to manually inspect the calls.

The following custom jest expectation methods, proxying through to `fetch-mock`'s inspection methods are also available. They can all be prefixed with the `.not` helper for negative assertions.

- `expect(fetchMock).toHaveFetched(filter, options)`
- `expect(fetchMock).toHaveLastFetched(filter, options)`
- `expect(fetchMock).toHaveNthFetched(n, filter, options)`
- `expect(fetchMock).toHaveFetchedTimes(n, filter, options)`
- `expect(fetchMock).toBeDone(filter)`

### Notes

- `filter` and `options` are the same as those used by [`fetch-mock`'s inspection methods](http://www.wheresrhys.co.uk/fetch-mock/#api-inspectionfundamentals)
- The obove methods can have `Fetched` replaced by any of the following verbs to scope to a particular method: + Got + Posted + Put + Deleted + FetchedHead + Patched

e.g. `expect(fetchMock).toHaveLastPatched(filter, options)`

## Tearing down mocks

`fetchMock.mockClear()` can be used to reset the call history

`fetchMock.mockReset()` can be used to remove all configured mocks

Please report any bugs in resetting mocks on the [issues board](https://github.com/wheresrhys/fetch-mock-jest/issues)

# Example

```js
const fetchMock = require('fetch-mock-jest');
const userManager = require('../src/user-manager');

test(async () => {
const users = [{ name: 'bob' }];
fetchMock
.get('http://example.com/users', users)
.post('http://example.com/user', (url, options) => {
if (typeof options.body.name === 'string') {
users.push(options.body);
return 202;
}
return 400;
})
.patch(
{
url: 'http://example.com/user'
},
405
);

expect(await userManager.getAll()).toEqual([{ name: 'bob' }]);
expect(fetchMock).toHaveLastFetched('http://example.com/users
get');
await userManager.create({ name: true });
expect(fetchMock).toHaveLastFetched(
{
url: 'http://example.com/user',
body: { name: true }
},
'post'
);
expect(await userManager.getAll()).toEqual([{ name: 'bob' }]);
fetchMock.mockClear();
await userManager.create({ name: 'sarah' });
expect(fetchMock).toHaveLastFetched(
{
url: 'http://example.com/user',
body: { name: 'sarah' }
},
'post'
);
expect(await userManager.getAll()).toEqual([
{ name: 'bob' },
{ name: 'sarah' }
]);
fetchMock.mockReset();
});
```
176 changes: 176 additions & 0 deletions packages/jest/__tests__/jest-built-ins.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/*global jest, beforeAll, afterAll */
jest.mock('node-fetch', () => require('../server').sandbox());
const fetch = require('node-fetch');
describe('jest built-ins', () => {
describe('exposing mock internals', () => {
beforeAll(() => {
fetch.mock('http://example.com', 200).mock('http://example2.com', 201);
fetch('http://example.com', {
headers: {
test: 'header',
},
});
fetch('http://example2.com', {
headers: {
test: 'header2',
},
});
});

afterAll(() => fetch.reset());
it('exposes `calls` property', () => {
expect(fetch.mock.calls).toBeDefined();
expect(fetch.mock.calls.length).toBe(2);
expect(fetch.mock.calls).toMatchObject([
[
'http://example.com',
{
headers: {
test: 'header',
},
},
],
[
'http://example2.com',
{
headers: {
test: 'header2',
},
},
],
]);
});
it('exposes `results` property', async () => {
expect(fetch.mock.results).toBeDefined();
expect(fetch.mock.results.length).toEqual(2);
expect(await fetch.mock.results[0].value).toMatchObject({
status: 200,
});
expect(await fetch.mock.results[1].value).toMatchObject({
status: 201,
});
});
});

describe('clearing', () => {
beforeEach(() => {
fetch.mock('http://example.com', 200).mock('http://example2.com', 201);
fetch('http://example.com', {
headers: {
test: 'header',
},
});
fetch('http://example2.com', {
headers: {
test: 'header2',
},
});
});

afterEach(() => fetch.reset());
it('mockClear', () => {
expect(fetch.mockClear).toBeDefined();
fetch.mockClear();
expect(fetch.mock.calls.length).toEqual(0);
expect(fetch._calls.length).toEqual(0);
expect(fetch.routes.length).toEqual(2);
});
it('mockReset', () => {
expect(fetch.mockReset).toBeDefined();
fetch.mockReset();
expect(fetch.mock.calls.length).toEqual(0);
expect(fetch._calls.length).toEqual(0);
expect(fetch.routes.length).toEqual(0);
});
it('mockRestore', () => {
expect(() => fetch.mockRestore()).toThrow(
"mockRestore not supported on fetch-mock. Use fetch-mock's methods to manage mock responses"
);
});
it('mockImplementation', () => {
expect(() => fetch.mockImplementation()).toThrow(
"mockImplementation not supported on fetch-mock. Use fetch-mock's methods to manage mock responses"
);
});
it('mockImplementationOnce', () => {
expect(() => fetch.mockImplementationOnce()).toThrow(
"mockImplementationOnce not supported on fetch-mock. Use fetch-mock's methods to manage mock responses"
);
});
it('mockName', () => {
expect(() => fetch.mockName()).toThrow(
"mockName not supported on fetch-mock. Use fetch-mock's methods to manage mock responses"
);
});
it('mockReturnThis', () => {
expect(() => fetch.mockReturnThis()).toThrow(
"mockReturnThis not supported on fetch-mock. Use fetch-mock's methods to manage mock responses"
);
});
it('mockReturnValue', () => {
expect(() => fetch.mockReturnValue()).toThrow(
"mockReturnValue not supported on fetch-mock. Use fetch-mock's methods to manage mock responses"
);
});
it('mockReturnValueOnce', () => {
expect(() => fetch.mockReturnValueOnce()).toThrow(
"mockReturnValueOnce not supported on fetch-mock. Use fetch-mock's methods to manage mock responses"
);
});
it('mockResolvedValue', () => {
expect(() => fetch.mockResolvedValue()).toThrow(
"mockResolvedValue not supported on fetch-mock. Use fetch-mock's methods to manage mock responses"
);
});
it('mockResolvedValueOnce', () => {
expect(() => fetch.mockResolvedValueOnce()).toThrow(
"mockResolvedValueOnce not supported on fetch-mock. Use fetch-mock's methods to manage mock responses"
);
});
it('mockRejectedValue', () => {
expect(() => fetch.mockRejectedValue()).toThrow(
"mockRejectedValue not supported on fetch-mock. Use fetch-mock's methods to manage mock responses"
);
});
it('mockRejectedValueOnce', () => {
expect(() => fetch.mockRejectedValueOnce()).toThrow(
"mockRejectedValueOnce not supported on fetch-mock. Use fetch-mock's methods to manage mock responses"
);
});
});
describe('native jest mock function inspectors', () => {
it('.toHaveBeenCalled()', () => {
expect(() => expect(fetch).toHaveBeenCalled()).not.toThrow();
});
// Just want to get the fix out for calling fetch methods
// These will all work as the basic mechanism is fixed, but
// no time to set up all th etest cases now
it.skip('.toHaveBeenCalledTimes(number)', () => {
expect(() => expect(fetch).toHaveBeenCalledTimes(1)).not.toThrow();
});
it.skip('.toHaveBeenCalledWith(arg1, arg2, ...)', () => {
expect(() => expect(fetch).toHaveBeenCalledWith(1)).not.toThrow();
});
it.skip('.toHaveBeenLastCalledWith(arg1, arg2, ...)', () => {
expect(() => expect(fetch).toHaveBeenLastCalledWith(1)).not.toThrow();
});
it.skip('.toHaveBeenNthCalledWith(nthCall, arg1, arg2, ....)', () => {
expect(() => expect(fetch).toHaveBeenNthCalledWith(1, 1)).not.toThrow();
});
it.skip('.toHaveReturned()', () => {
expect(() => expect(fetch).toHaveReturned()).not.toThrow();
});
it.skip('.toHaveReturnedTimes(number)', () => {
expect(() => expect(fetch).toHaveReturnedTimes(1)).not.toThrow();
});
it.skip('.toHaveReturnedWith(value)', () => {
expect(() => expect(fetch).toHaveReturnedWith(1)).not.toThrow();
});
it.skip('.toHaveLastReturnedWith(value)', () => {
expect(() => expect(fetch).toHaveLastReturnedWith(1)).not.toThrow();
});
it.skip('.toHaveNthReturnedWith(nthCall, value)', () => {
expect(() => expect(fetch).toHaveNthReturnedWith(1, 1)).not.toThrow();
});
});
});
Loading

0 comments on commit 804f885

Please sign in to comment.