react-relay-network-modern
Version:
Network Layer for React Relay and Express (Batch Queries, AuthToken, Logging, Retry)
219 lines (199 loc) • 6.44 kB
JavaScript
import fetchMock from 'fetch-mock';
import RelayNetworkLayer from '../../RelayNetworkLayer';
import { mockReq, mockMutationReq, mockFormDataReq } from '../../__mocks__/mockReq';
import cacheMiddleware from '../cache';
async function sleep(timeout) {
return new Promise(resolve => {
setTimeout(resolve, timeout);
});
}
describe('middlewares/cache', () => {
beforeEach(() => {
fetchMock.restore();
});
it('check `size` option', async () => {
fetchMock.mock({
matcher: '/graphql',
response: {
status: 200,
body: {
data: 'PAYLOAD'
}
},
method: 'POST'
});
const rnl = new RelayNetworkLayer([cacheMiddleware({
size: 2
})]);
// data from fetch
const res1 = await mockReq('FirstQuery').execute(rnl);
expect(res1.data).toBe('PAYLOAD');
expect(fetchMock.calls('/graphql')).toHaveLength(1);
// data from cache
const res2 = await mockReq('FirstQuery').execute(rnl);
expect(res2.data).toBe('PAYLOAD');
expect(fetchMock.calls('/graphql')).toHaveLength(1);
// data from fetch
const res3 = await mockReq('SecondQuery').execute(rnl);
expect(res3.data).toBe('PAYLOAD');
expect(fetchMock.calls('/graphql')).toHaveLength(2);
// data from cache
const res4 = await mockReq('SecondQuery').execute(rnl);
expect(res4.data).toBe('PAYLOAD');
expect(fetchMock.calls('/graphql')).toHaveLength(2);
// data from fetch
const res5 = await mockReq('ThirdQuery').execute(rnl);
expect(res5.data).toBe('PAYLOAD');
expect(fetchMock.calls('/graphql')).toHaveLength(3);
// first request should be removed from cache, cause size = 2
const res6 = await mockReq('FirstQuery').execute(rnl);
expect(res6.data).toBe('PAYLOAD');
expect(fetchMock.calls('/graphql')).toHaveLength(4);
});
it('check `ttl` option', async () => {
fetchMock.mock({
matcher: '/graphql',
response: {
status: 200,
body: {
data: 'PAYLOAD'
}
},
method: 'POST'
});
const rnl = new RelayNetworkLayer([cacheMiddleware({
ttl: 20
})]);
// data from fetch
const res1 = await mockReq('FirstQuery').execute(rnl);
expect(res1.data).toBe('PAYLOAD');
expect(fetchMock.calls('/graphql')).toHaveLength(1);
// data from cache
const res2 = await mockReq('FirstQuery').execute(rnl);
expect(res2.data).toBe('PAYLOAD');
expect(fetchMock.calls('/graphql')).toHaveLength(1);
await sleep(50);
// first request should be removed from cache, cause ttl = 20
const res3 = await mockReq('FirstQuery').execute(rnl);
expect(res3.data).toBe('PAYLOAD');
expect(fetchMock.calls('/graphql')).toHaveLength(2);
});
it('do not use cache for mutations', async () => {
fetchMock.mock({
matcher: '/graphql',
response: {
status: 200,
body: {
data: 'PAYLOAD'
}
},
method: 'POST'
});
const rnl = new RelayNetworkLayer([cacheMiddleware()]);
// data from fetch
const res1 = await mockMutationReq('FirstQuery').execute(rnl);
expect(res1.data).toBe('PAYLOAD');
expect(fetchMock.calls('/graphql')).toHaveLength(1);
// data from cache
const res2 = await mockMutationReq('FirstQuery').execute(rnl);
expect(res2.data).toBe('PAYLOAD');
expect(fetchMock.calls('/graphql')).toHaveLength(2);
});
it('do not use cache for FormData', async () => {
fetchMock.mock({
matcher: '/graphql',
response: {
status: 200,
body: {
data: 'PAYLOAD'
}
},
method: 'POST'
});
const rnl = new RelayNetworkLayer([cacheMiddleware()]);
// data from fetch
const res1 = await mockFormDataReq('FirstQuery').execute(rnl);
expect(res1.data).toBe('PAYLOAD');
expect(fetchMock.calls('/graphql')).toHaveLength(1);
// data from cache
const res2 = await mockFormDataReq('FirstQuery').execute(rnl);
expect(res2.data).toBe('PAYLOAD');
expect(fetchMock.calls('/graphql')).toHaveLength(2);
});
it('do not use cache for responses with errors', async () => {
fetchMock.mock({
matcher: '/graphql',
response: {
status: 200,
body: {
data: 'PAYLOAD',
errors: [{
type: 'timeout'
}]
}
},
method: 'POST'
});
const rnl = new RelayNetworkLayer([cacheMiddleware()]);
// try fetch
await expect(mockReq('FirstQuery').execute(rnl)).rejects.toThrow();
expect(fetchMock.calls('/graphql')).toHaveLength(1);
// try fetch again
await expect(mockReq('FirstQuery').execute(rnl)).rejects.toThrow();
expect(fetchMock.calls('/graphql')).toHaveLength(2);
});
it('use cache for responses with errors and cacheErrors flag', async () => {
fetchMock.mock({
matcher: '/graphql',
response: {
status: 200,
body: {
data: 'PAYLOAD',
errors: [{
type: 'timeout'
}]
}
},
method: 'POST'
});
const rnl = new RelayNetworkLayer([cacheMiddleware({
cacheErrors: true
})]);
// try fetch
await expect(mockReq('FirstQuery').execute(rnl)).rejects.toThrow();
expect(fetchMock.calls('/graphql')).toHaveLength(1);
// try fetch again
await expect(mockReq('FirstQuery').execute(rnl)).rejects.toThrow();
expect(fetchMock.calls('/graphql')).toHaveLength(1);
});
it('updates ttl on get if updateTTLOnGet is set', async () => {
fetchMock.mock({
matcher: '/graphql',
response: {
status: 200,
body: {
data: 'PAYLOAD'
}
},
method: 'POST'
});
const rnl = new RelayNetworkLayer([cacheMiddleware({
ttl: 40,
updateTTLOnGet: true
})]);
// data from fetch
const res1 = await mockReq('FirstQuery').execute(rnl);
expect(res1.data).toBe('PAYLOAD');
expect(fetchMock.calls('/graphql')).toHaveLength(1);
await sleep(20);
// data from cache
const res2 = await mockReq('FirstQuery').execute(rnl);
expect(res2.data).toBe('PAYLOAD');
expect(fetchMock.calls('/graphql')).toHaveLength(1);
await sleep(20);
// data from cache 40ms after 30ms initial cache
const res3 = await mockReq('FirstQuery').execute(rnl);
expect(res3.data).toBe('PAYLOAD');
expect(fetchMock.calls('/graphql')).toHaveLength(1);
});
});