PageRenderTime 5378ms CodeModel.GetById 34ms RepoModel.GetById 1ms app.codeStats 0ms

/test/do-fetch.test.js

https://github.com/jamesplease/react-request
JavaScript | 498 lines | 419 code | 38 blank | 41 comment | 22 complexity | 9a420231beab94af1d51e48026e25d25 MD5 | raw file
Possible License(s): MIT
  1. import React from 'react';
  2. import fetchMock from 'fetch-mock';
  3. import { mount } from 'enzyme';
  4. import { jsonResponse } from './responses';
  5. import { Fetch, clearRequestCache, clearResponseCache } from '../src';
  6. // Some time for the mock fetches to resolve
  7. const networkTimeout = 10;
  8. beforeEach(() => {
  9. clearRequestCache();
  10. clearResponseCache();
  11. });
  12. describe('doFetch()', () => {
  13. test('`doFetch()` returns a promise that resolves with the same object as `afterFetch`', done => {
  14. fetchMock.get(
  15. '/test/succeeds/dofetch-promise',
  16. new Promise(resolve => {
  17. resolve(jsonResponse());
  18. })
  19. );
  20. expect.assertions(2);
  21. const afterFetchMock = jest.fn();
  22. const childrenMock = jest.fn();
  23. mount(
  24. <Fetch
  25. url="/test/succeeds/dofetch-promise"
  26. afterFetch={afterFetchMock}
  27. children={childrenMock}
  28. lazy
  29. />
  30. );
  31. const { doFetch } = childrenMock.mock.calls[0][0];
  32. doFetch().then(afterFetchInfo => {
  33. setTimeout(() => {
  34. expect(afterFetchMock).toHaveBeenCalledTimes(1);
  35. expect(afterFetchMock).toBeCalledWith(afterFetchInfo);
  36. done();
  37. });
  38. });
  39. });
  40. test('`doFetch()` returns a promise that resolves _even_ when there was an error', done => {
  41. fetchMock.get(
  42. '/test/fails/dofetch-promise',
  43. new Promise((resolve, reject) => {
  44. reject({
  45. message: 'Network error',
  46. });
  47. })
  48. );
  49. expect.assertions(1);
  50. const childrenMock = jest.fn();
  51. mount(<Fetch url="/test/fails/dofetch-promise" children={childrenMock} />);
  52. const { doFetch } = childrenMock.mock.calls[0][0];
  53. doFetch().then(afterFetchInfo => {
  54. expect(afterFetchInfo).toMatchObject({
  55. url: '/test/fails/dofetch-promise',
  56. error: {
  57. message: 'Network error',
  58. },
  59. failed: true,
  60. didUnmount: false,
  61. data: null,
  62. });
  63. done();
  64. });
  65. });
  66. });
  67. describe('same-component doFetch() with caching (gh-151)', () => {
  68. test('doFetch() with URL and another HTTP method', done => {
  69. // expect.assertions(8);
  70. const onResponseMock = jest.fn();
  71. const beforeFetchMock = jest.fn();
  72. const afterFetchMock = jest.fn();
  73. let run = 1;
  74. let renderCount = 0;
  75. mount(
  76. <Fetch
  77. url="/test/succeeds/json-one"
  78. requestKey="1"
  79. beforeFetch={beforeFetchMock}
  80. afterFetch={afterFetchMock}
  81. onResponse={onResponseMock}>
  82. {options => {
  83. renderCount++;
  84. // Wait for things to be placed in the cache.
  85. // This occurs on the third render:
  86. // 1st. component mounts
  87. // 2nd. fetch begins
  88. // 3rd. fetch ends
  89. if (run === 1 && renderCount === 3) {
  90. expect(options).toEqual(
  91. expect.objectContaining({
  92. fetching: false,
  93. data: {
  94. books: [1, 42, 150],
  95. },
  96. error: null,
  97. failed: false,
  98. requestName: 'anonymousRequest',
  99. url: '/test/succeeds/json-one',
  100. })
  101. );
  102. // We need a timeout here to prevent a race condition
  103. // with the assertions after the component mounts.
  104. setTimeout(() => {
  105. run++;
  106. renderCount = 0;
  107. options.doFetch({
  108. method: 'patch',
  109. url: '/test/succeeds/patch',
  110. });
  111. // Now we need another timeout to allow for the fetch
  112. // to occur.
  113. setTimeout(() => {
  114. done();
  115. }, networkTimeout);
  116. }, networkTimeout * 2);
  117. }
  118. if (run === 2) {
  119. if (renderCount === 1) {
  120. expect(options).toEqual(
  121. expect.objectContaining({
  122. fetching: true,
  123. data: {
  124. books: [1, 42, 150],
  125. },
  126. error: null,
  127. failed: false,
  128. requestName: 'anonymousRequest',
  129. url: '/test/succeeds/patch',
  130. })
  131. );
  132. }
  133. if (renderCount === 2) {
  134. expect(fetchMock.calls('/test/succeeds/patch').length).toBe(1);
  135. expect(options).toEqual(
  136. expect.objectContaining({
  137. fetching: false,
  138. data: {
  139. movies: [1],
  140. },
  141. error: null,
  142. failed: false,
  143. requestName: 'anonymousRequest',
  144. url: '/test/succeeds/patch',
  145. })
  146. );
  147. }
  148. if (renderCount === 3) {
  149. done.fail();
  150. }
  151. }
  152. }}
  153. </Fetch>
  154. );
  155. setTimeout(() => {
  156. // NOTE: this is for adding stuff to the cache.
  157. // This DOES NOT test the cache-only behavior!
  158. expect(fetchMock.calls('/test/succeeds/json-one').length).toBe(1);
  159. expect(beforeFetchMock).toHaveBeenCalledTimes(1);
  160. expect(afterFetchMock).toHaveBeenCalledTimes(1);
  161. expect(afterFetchMock).toBeCalledWith(
  162. expect.objectContaining({
  163. url: '/test/succeeds/json-one',
  164. error: null,
  165. failed: false,
  166. didUnmount: false,
  167. data: {
  168. books: [1, 42, 150],
  169. },
  170. })
  171. );
  172. expect(onResponseMock).toHaveBeenCalledTimes(1);
  173. expect(onResponseMock).toBeCalledWith(
  174. null,
  175. expect.objectContaining({
  176. ok: true,
  177. status: 200,
  178. statusText: 'OK',
  179. data: {
  180. books: [1, 42, 150],
  181. },
  182. })
  183. );
  184. }, networkTimeout);
  185. });
  186. test('doFetch() with request key and URL', done => {
  187. // expect.assertions(8);
  188. const onResponseMock = jest.fn();
  189. const beforeFetchMock = jest.fn();
  190. const afterFetchMock = jest.fn();
  191. let run = 1;
  192. let renderCount = 0;
  193. mount(
  194. <Fetch
  195. url="/test/succeeds/json-one"
  196. requestKey="1"
  197. beforeFetch={beforeFetchMock}
  198. afterFetch={afterFetchMock}
  199. onResponse={onResponseMock}>
  200. {options => {
  201. renderCount++;
  202. // Wait for things to be placed in the cache.
  203. // This occurs on the third render:
  204. // 1st. component mounts
  205. // 2nd. fetch begins
  206. // 3rd. fetch ends
  207. if (run === 1 && renderCount === 3) {
  208. expect(options).toEqual(
  209. expect.objectContaining({
  210. fetching: false,
  211. data: {
  212. books: [1, 42, 150],
  213. },
  214. error: null,
  215. failed: false,
  216. requestName: 'anonymousRequest',
  217. url: '/test/succeeds/json-one',
  218. })
  219. );
  220. // We need a timeout here to prevent a race condition
  221. // with the assertions after the component mounts.
  222. setTimeout(() => {
  223. run++;
  224. renderCount = 0;
  225. options.doFetch({
  226. requestKey: 'sandwiches',
  227. url: '/test/succeeds/json-two',
  228. });
  229. // Now we need another timeout to allow for the fetch
  230. // to occur.
  231. setTimeout(() => {
  232. done();
  233. }, networkTimeout);
  234. }, networkTimeout * 2);
  235. }
  236. if (run === 2) {
  237. if (renderCount === 1) {
  238. expect(options).toEqual(
  239. expect.objectContaining({
  240. fetching: true,
  241. data: {
  242. books: [1, 42, 150],
  243. },
  244. error: null,
  245. failed: false,
  246. requestKey: 'sandwiches',
  247. requestName: 'anonymousRequest',
  248. url: '/test/succeeds/json-two',
  249. })
  250. );
  251. }
  252. if (renderCount === 2) {
  253. expect(options).toEqual(
  254. expect.objectContaining({
  255. fetching: false,
  256. data: {
  257. authors: [22, 13],
  258. },
  259. error: null,
  260. failed: false,
  261. requestKey: 'sandwiches',
  262. requestName: 'anonymousRequest',
  263. url: '/test/succeeds/json-two',
  264. })
  265. );
  266. }
  267. if (renderCount === 3) {
  268. done.fail();
  269. }
  270. }
  271. }}
  272. </Fetch>
  273. );
  274. setTimeout(() => {
  275. // NOTE: this is for adding stuff to the cache.
  276. // This DOES NOT test the cache-only behavior!
  277. expect(fetchMock.calls('/test/succeeds/json-one').length).toBe(1);
  278. expect(beforeFetchMock).toHaveBeenCalledTimes(1);
  279. expect(afterFetchMock).toHaveBeenCalledTimes(1);
  280. expect(afterFetchMock).toBeCalledWith(
  281. expect.objectContaining({
  282. url: '/test/succeeds/json-one',
  283. error: null,
  284. failed: false,
  285. didUnmount: false,
  286. data: {
  287. books: [1, 42, 150],
  288. },
  289. })
  290. );
  291. expect(onResponseMock).toHaveBeenCalledTimes(1);
  292. expect(onResponseMock).toBeCalledWith(
  293. null,
  294. expect.objectContaining({
  295. ok: true,
  296. status: 200,
  297. statusText: 'OK',
  298. data: {
  299. books: [1, 42, 150],
  300. },
  301. })
  302. );
  303. }, networkTimeout);
  304. });
  305. // Note: this does not test dedupe due to the fact that the requests
  306. // resolve too quickly.
  307. test('doFetch(); testing cancelation', done => {
  308. // expect.assertions(8);
  309. const onResponseMock = jest.fn();
  310. const beforeFetchMock = jest.fn();
  311. const afterFetchMock = jest.fn();
  312. let run = 1;
  313. let renderCount = 0;
  314. mount(
  315. <Fetch
  316. url="/test/succeeds/json-one"
  317. requestKey="1"
  318. beforeFetch={beforeFetchMock}
  319. afterFetch={afterFetchMock}
  320. onResponse={onResponseMock}>
  321. {options => {
  322. renderCount++;
  323. // Wait for things to be placed in the cache.
  324. // This occurs on the third render:
  325. // 1st. component mounts
  326. // 2nd. fetch begins
  327. // 3rd. fetch ends
  328. if (run === 1 && renderCount === 3) {
  329. expect(options).toEqual(
  330. expect.objectContaining({
  331. fetching: false,
  332. data: {
  333. books: [1, 42, 150],
  334. },
  335. error: null,
  336. failed: false,
  337. requestName: 'anonymousRequest',
  338. url: '/test/succeeds/json-one',
  339. })
  340. );
  341. // We need a timeout here to prevent a race condition
  342. // with the assertions after the component mounts.
  343. setTimeout(() => {
  344. run++;
  345. renderCount = 0;
  346. options.doFetch({
  347. requestKey: 'sandwiches',
  348. url: '/test/succeeds/json-two',
  349. });
  350. options.doFetch({
  351. requestKey: 'sandwiches',
  352. url: '/test/succeeds/json-two',
  353. });
  354. // Now we need another timeout to allow for the fetch
  355. // to occur.
  356. setTimeout(() => {
  357. done();
  358. }, networkTimeout);
  359. }, networkTimeout * 2);
  360. }
  361. if (run === 2) {
  362. if (renderCount === 1) {
  363. expect(options).toEqual(
  364. expect.objectContaining({
  365. fetching: true,
  366. data: {
  367. books: [1, 42, 150],
  368. },
  369. error: null,
  370. failed: false,
  371. requestKey: 'sandwiches',
  372. requestName: 'anonymousRequest',
  373. url: '/test/succeeds/json-two',
  374. })
  375. );
  376. }
  377. if (renderCount === 2) {
  378. expect(options).toEqual(
  379. expect.objectContaining({
  380. fetching: false,
  381. data: {
  382. books: [1, 42, 150],
  383. },
  384. failed: true,
  385. requestKey: 'sandwiches',
  386. requestName: 'anonymousRequest',
  387. url: '/test/succeeds/json-two',
  388. })
  389. );
  390. }
  391. // This is the 2nd doFetch(). It is difficult to update
  392. // the `run` for that fetch, so we just use the renderCounts.
  393. else if (renderCount === 3) {
  394. expect(options).toEqual(
  395. expect.objectContaining({
  396. fetching: true,
  397. data: {
  398. books: [1, 42, 150],
  399. },
  400. error: null,
  401. failed: false,
  402. requestKey: 'sandwiches',
  403. requestName: 'anonymousRequest',
  404. url: '/test/succeeds/json-two',
  405. })
  406. );
  407. } else if (renderCount === 4) {
  408. expect(options).toEqual(
  409. expect.objectContaining({
  410. fetching: false,
  411. data: {
  412. authors: [22, 13],
  413. },
  414. error: null,
  415. failed: false,
  416. requestKey: 'sandwiches',
  417. requestName: 'anonymousRequest',
  418. url: '/test/succeeds/json-two',
  419. })
  420. );
  421. }
  422. if (renderCount > 4) {
  423. done.fail();
  424. }
  425. }
  426. }}
  427. </Fetch>
  428. );
  429. setTimeout(() => {
  430. // NOTE: this is for adding stuff to the cache.
  431. // This DOES NOT test the cache-only behavior!
  432. expect(fetchMock.calls('/test/succeeds/json-one').length).toBe(1);
  433. expect(beforeFetchMock).toHaveBeenCalledTimes(1);
  434. expect(afterFetchMock).toHaveBeenCalledTimes(1);
  435. expect(afterFetchMock).toBeCalledWith(
  436. expect.objectContaining({
  437. url: '/test/succeeds/json-one',
  438. error: null,
  439. failed: false,
  440. didUnmount: false,
  441. data: {
  442. books: [1, 42, 150],
  443. },
  444. })
  445. );
  446. expect(onResponseMock).toHaveBeenCalledTimes(1);
  447. expect(onResponseMock).toBeCalledWith(
  448. null,
  449. expect.objectContaining({
  450. ok: true,
  451. status: 200,
  452. statusText: 'OK',
  453. data: {
  454. books: [1, 42, 150],
  455. },
  456. })
  457. );
  458. }, networkTimeout);
  459. });
  460. });