Skip to content

Commit

Permalink
fix(defaultFetcher): Avoid known hang in node-fetch
Browse files Browse the repository at this point in the history
  • Loading branch information
code-forger committed Jul 8, 2021
1 parent 0548dc6 commit d33399c
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 54 deletions.
52 changes: 29 additions & 23 deletions packages/fetchye-core/__tests__/defaultFetcher.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,19 @@ describe('defaultFetcher', () => {
const fetchClient = jest.fn(async () => ({
ok: true,
status: 200,
json: async () => true,
text: async () => JSON.stringify({ jsonBodyMock: 'jsonBodyMock' }),
headers: new global.Headers({
'Content-Type': 'application/json',
}),
clone: () => ({
ok: true,
status: 200,
headers: new global.Headers({
'Content-Type': 'application/json',
}),
}),
}));
const data = await defaultFetcher(fetchClient, 'http://example.com');
expect(data).toMatchInlineSnapshot(`
Object {
"error": undefined,
"payload": Object {
"body": true,
"body": Object {
"jsonBodyMock": "jsonBodyMock",
},
"headers": Object {
"content-type": "application/json",
},
Expand All @@ -53,24 +48,41 @@ describe('defaultFetcher', () => {
}
`);
});
it('should return payload with the raw text body and undefined error when the body cannot be parsed', async () => {
const fetchClient = jest.fn(async () => ({
ok: true,
status: 200,
text: async () => 'unparsableTextBodyMock',
headers: new global.Headers({}),
}));
const data = await defaultFetcher(fetchClient, 'http://example.com');
expect(data).toMatchInlineSnapshot(`
Object {
"error": undefined,
"payload": Object {
"body": "unparsableTextBodyMock",
"headers": Object {},
"ok": true,
"status": 200,
},
}
`);
});
it('should return empty object if header entries does not exist', async () => {
const fetchClient = jest.fn(async () => ({
ok: true,
status: 200,
json: async () => true,
text: async () => JSON.stringify({ jsonBodyMock: 'jsonBodyMock' }),
headers: undefined,
clone: () => ({
ok: true,
status: 200,
headers: undefined,
}),
}));
const data = await defaultFetcher(fetchClient, 'http://example.com');
expect(data).toMatchInlineSnapshot(`
Object {
"error": undefined,
"payload": Object {
"body": true,
"body": Object {
"jsonBodyMock": "jsonBodyMock",
},
"headers": Object {},
"ok": true,
"status": 200,
Expand All @@ -95,14 +107,8 @@ describe('defaultFetcher', () => {
const fetchClient = jest.fn(async () => ({
ok: true,
status: 204,
json: async () => { throw new SyntaxError('Unexpected end of JSON input'); },
text: async () => '',
headers: new global.Headers({}),
clone: () => ({
ok: true,
status: 204,
headers: new global.Headers({}),
text: async () => '',
}),
}));
const data = await defaultFetcher(fetchClient, 'http://example.com');
expect(data).toMatchInlineSnapshot(`
Expand Down
9 changes: 7 additions & 2 deletions packages/fetchye-core/src/defaultFetcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,13 @@ export const defaultFetcher = async (fetchClient, key, options) => {
let error;
try {
res = await fetchClient(key, options);
const responseClone = res.clone();
const body = await res.json().catch(() => responseClone.text());
let body = await res.text();
try {
body = JSON.parse(body);
// eslint-disable-next-line no-empty
} catch (e) {
// body will still be the text from the response, so no action needed here
}
payload = {
body,
ok: res.ok,
Expand Down
9 changes: 1 addition & 8 deletions packages/fetchye/__tests__/makeServerFetchye.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,9 @@ const defaultPayload = {
}),
ok: true,
status: 200,
json: async () => ({
text: async () => JSON.stringify({
fakeData: true,
}),
clone: () => ({
headers: new global.Headers({
'Content-Type': 'application/json',
}),
ok: true,
status: 200,
}),
};

describe('makeServerFetchye', () => {
Expand Down
27 changes: 6 additions & 21 deletions packages/fetchye/__tests__/useFetchye.spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,9 @@ const defaultPayload = {
}),
ok: true,
status: 200,
json: async () => ({
text: async () => JSON.stringify({
fakeData: true,
}),
clone: () => ({
headers: new global.Headers({
'Content-Type': 'application/json',
}),
ok: true,
status: 200,
}),
};

const ReduxSetup = (props) => {
Expand Down Expand Up @@ -125,13 +118,7 @@ describe('useFetchye', () => {
global.fetch = jest.fn(async () => ({
...defaultPayload,
status: 204,
json: async () => { throw new SyntaxError('Unexpected end of JSON input'); },
clone: () => ({
ok: true,
status: 204,
headers: new global.Headers({}),
text: async () => '',
}),
text: async () => '',
}));
render(
<AFetchyeProvider cache={cache}>
Expand Down Expand Up @@ -165,14 +152,14 @@ describe('useFetchye', () => {
if (url === 'http://example.com/one') {
return {
...defaultPayload,
json: async () => ({
text: async () => JSON.stringify({
id: 'abc123',
}),
};
}
return {
...defaultPayload,
json: async () => ({
text: async () => JSON.stringify({
resourceUrl: url,
}),
};
Expand Down Expand Up @@ -271,7 +258,7 @@ describe('useFetchye', () => {
payload: {
ok: res.ok,
status: res.status,
body: await res.json(),
body: JSON.parse(await res.text()),
},
error: undefined,
};
Expand Down Expand Up @@ -340,9 +327,7 @@ describe('useFetchye', () => {
].forEach(([name, AFetchyeProvider]) => {
describe(name, () => {
it('ensures fetch is called once per key', async () => {
const fakeFetchClient = jest.fn({
clone: () => {},
});
const fakeFetchClient = jest.fn();
global.fetch = fakeFetchClient;
render(
<AFetchyeProvider cache={cache}>
Expand Down

0 comments on commit d33399c

Please sign in to comment.