projects.spec.jsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. import React from 'react';
  2. import {mount} from 'enzyme';
  3. import Projects from 'app/utils/projects';
  4. import ProjectsStore from 'app/stores/projectsStore';
  5. describe('utils.projects', function() {
  6. const renderer = jest.fn(() => null);
  7. const createWrapper = props =>
  8. mount(<Projects orgId="org-slug" children={renderer} {...props} />); // eslint-disable-line
  9. beforeEach(function() {
  10. renderer.mockClear();
  11. MockApiClient.clearMockResponses();
  12. ProjectsStore.loadInitialData([
  13. TestStubs.Project({id: '1', slug: 'foo'}),
  14. TestStubs.Project({id: '2', slug: 'bar'}),
  15. ]);
  16. });
  17. afterEach(async function() {
  18. ProjectsStore.loadInitialData([]);
  19. await tick();
  20. });
  21. describe('with predefined list of slugs', function() {
  22. it('gets projects that are in the ProjectsStore ', async function() {
  23. const wrapper = createWrapper({slugs: ['foo', 'bar']});
  24. // This is initial state
  25. expect(renderer).toHaveBeenCalledWith(
  26. expect.objectContaining({
  27. fetching: false,
  28. isIncomplete: null,
  29. hasMore: null,
  30. projects: [
  31. expect.objectContaining({
  32. id: '1',
  33. slug: 'foo',
  34. }),
  35. expect.objectContaining({
  36. id: '2',
  37. slug: 'bar',
  38. }),
  39. ],
  40. })
  41. );
  42. await tick();
  43. wrapper.update();
  44. expect(renderer).toHaveBeenCalledWith(
  45. expect.objectContaining({
  46. fetching: false,
  47. isIncomplete: null,
  48. hasMore: null,
  49. projects: [
  50. expect.objectContaining({
  51. id: '1',
  52. slug: 'foo',
  53. }),
  54. expect.objectContaining({
  55. id: '2',
  56. slug: 'bar',
  57. }),
  58. ],
  59. })
  60. );
  61. });
  62. it('fetches projects from API if not found in store', async function() {
  63. const request = MockApiClient.addMockResponse({
  64. url: '/organizations/org-slug/projects/',
  65. query: {
  66. query: 'slug:a slug:b',
  67. },
  68. body: [
  69. TestStubs.Project({
  70. id: '100',
  71. slug: 'a',
  72. }),
  73. TestStubs.Project({
  74. id: '101',
  75. slug: 'b',
  76. }),
  77. ],
  78. });
  79. const wrapper = createWrapper({slugs: ['foo', 'a', 'b']});
  80. // This is initial state
  81. expect(renderer).toHaveBeenCalledWith(
  82. expect.objectContaining({
  83. fetching: true,
  84. isIncomplete: null,
  85. hasMore: null,
  86. projects: [
  87. {slug: 'a'},
  88. {slug: 'b'},
  89. expect.objectContaining({
  90. id: '1',
  91. slug: 'foo',
  92. }),
  93. ],
  94. })
  95. );
  96. await tick();
  97. wrapper.update();
  98. expect(request).toHaveBeenCalledWith(
  99. expect.anything(),
  100. expect.objectContaining({
  101. query: {
  102. query: 'slug:a slug:b',
  103. },
  104. })
  105. );
  106. expect(renderer).toHaveBeenCalledWith(
  107. expect.objectContaining({
  108. fetching: false,
  109. isIncomplete: false,
  110. hasMore: null,
  111. projects: [
  112. expect.objectContaining({
  113. id: '100',
  114. slug: 'a',
  115. }),
  116. expect.objectContaining({
  117. id: '101',
  118. slug: 'b',
  119. }),
  120. expect.objectContaining({
  121. id: '1',
  122. slug: 'foo',
  123. }),
  124. ],
  125. })
  126. );
  127. });
  128. it('only has partial results from API', async function() {
  129. const request = MockApiClient.addMockResponse({
  130. url: '/organizations/org-slug/projects/',
  131. body: [
  132. TestStubs.Project({
  133. id: '100',
  134. slug: 'a',
  135. }),
  136. ],
  137. });
  138. const wrapper = createWrapper({slugs: ['foo', 'a', 'b']});
  139. // This is initial state
  140. expect(renderer).toHaveBeenCalledWith(
  141. expect.objectContaining({
  142. fetching: true,
  143. isIncomplete: null,
  144. hasMore: null,
  145. projects: [
  146. {slug: 'a'},
  147. {slug: 'b'},
  148. expect.objectContaining({
  149. id: '1',
  150. slug: 'foo',
  151. }),
  152. ],
  153. })
  154. );
  155. await tick();
  156. wrapper.update();
  157. expect(request).toHaveBeenCalledWith(
  158. expect.anything(),
  159. expect.objectContaining({
  160. query: {
  161. query: 'slug:a slug:b',
  162. },
  163. })
  164. );
  165. expect(renderer).toHaveBeenCalledWith(
  166. expect.objectContaining({
  167. fetching: false,
  168. isIncomplete: true,
  169. hasMore: null,
  170. projects: [
  171. expect.objectContaining({
  172. id: '100',
  173. slug: 'a',
  174. }),
  175. {
  176. slug: 'b',
  177. },
  178. expect.objectContaining({
  179. id: '1',
  180. slug: 'foo',
  181. }),
  182. ],
  183. })
  184. );
  185. });
  186. });
  187. describe('with no pre-defined projects', function() {
  188. let request;
  189. beforeEach(async function() {
  190. request = MockApiClient.addMockResponse({
  191. url: '/organizations/org-slug/projects/',
  192. body: [
  193. TestStubs.Project({
  194. id: '100',
  195. slug: 'a',
  196. }),
  197. TestStubs.Project({
  198. id: '101',
  199. slug: 'b',
  200. }),
  201. ],
  202. headers: {
  203. Link:
  204. '<http://127.0.0.1:8000/api/0/organizations/org-slug/projects/?cursor=1443575731:0:1>; rel="previous"; results="true"; cursor="1443575731:0:1", ' +
  205. '<http://127.0.0.1:8000/api/0/organizations/org-slug/projects/?cursor=1443575731:0:0>; rel="next"; results="true"; cursor="1443575731:0:0',
  206. },
  207. });
  208. ProjectsStore.loadInitialData([]);
  209. await tick();
  210. });
  211. it('fetches projects from API', async function() {
  212. const wrapper = createWrapper();
  213. // This is initial state
  214. expect(renderer).toHaveBeenCalledWith(
  215. expect.objectContaining({
  216. fetching: true,
  217. isIncomplete: null,
  218. hasMore: null,
  219. projects: [],
  220. })
  221. );
  222. await tick();
  223. wrapper.update();
  224. expect(request).toHaveBeenCalledWith(
  225. expect.anything(),
  226. expect.objectContaining({
  227. query: {},
  228. })
  229. );
  230. expect(renderer).toHaveBeenCalledWith(
  231. expect.objectContaining({
  232. fetching: false,
  233. isIncomplete: null,
  234. hasMore: true,
  235. projects: [
  236. expect.objectContaining({
  237. id: '100',
  238. slug: 'a',
  239. }),
  240. expect.objectContaining({
  241. id: '101',
  242. slug: 'b',
  243. }),
  244. ],
  245. })
  246. );
  247. });
  248. it('queries API for more projects and replaces results', async function() {
  249. const myRenderer = jest.fn(({onSearch}) => (
  250. <input onChange={({target}) => onSearch(target.value)} />
  251. ));
  252. const wrapper = createWrapper({children: myRenderer});
  253. // This is initial state
  254. expect(myRenderer).toHaveBeenCalledWith(
  255. expect.objectContaining({
  256. fetching: true,
  257. isIncomplete: null,
  258. hasMore: null,
  259. projects: [],
  260. })
  261. );
  262. await tick();
  263. wrapper.update();
  264. request.mockClear();
  265. request = MockApiClient.addMockResponse({
  266. url: '/organizations/org-slug/projects/',
  267. body: [
  268. TestStubs.Project({
  269. id: '102',
  270. slug: 'test1',
  271. }),
  272. TestStubs.Project({
  273. id: '103',
  274. slug: 'test2',
  275. }),
  276. ],
  277. });
  278. wrapper.find('input').simulate('change', {target: {value: 'test'}});
  279. expect(request).toHaveBeenCalledWith(
  280. expect.anything(),
  281. expect.objectContaining({
  282. query: {
  283. query: 'test',
  284. },
  285. })
  286. );
  287. await tick();
  288. wrapper.update();
  289. expect(myRenderer).toHaveBeenLastCalledWith(
  290. expect.objectContaining({
  291. fetching: false,
  292. isIncomplete: null,
  293. hasMore: false,
  294. projects: [
  295. expect.objectContaining({
  296. id: '102',
  297. slug: 'test1',
  298. }),
  299. expect.objectContaining({
  300. id: '103',
  301. slug: 'test2',
  302. }),
  303. ],
  304. })
  305. );
  306. });
  307. it('queries API for more projects and appends results', async function() {
  308. const myRenderer = jest.fn(({onSearch}) => (
  309. <input onChange={({target}) => onSearch(target.value, {append: true})} />
  310. ));
  311. const wrapper = createWrapper({children: myRenderer});
  312. await tick();
  313. wrapper.update();
  314. request.mockClear();
  315. request = MockApiClient.addMockResponse({
  316. url: '/organizations/org-slug/projects/',
  317. body: [
  318. TestStubs.Project({
  319. id: '102',
  320. slug: 'test1',
  321. }),
  322. TestStubs.Project({
  323. id: '103',
  324. slug: 'test2',
  325. }),
  326. ],
  327. });
  328. wrapper.find('input').simulate('change', {target: {value: 'test'}});
  329. expect(request).toHaveBeenCalledWith(
  330. expect.anything(),
  331. expect.objectContaining({
  332. query: {
  333. query: 'test',
  334. },
  335. })
  336. );
  337. await tick();
  338. wrapper.update();
  339. expect(myRenderer).toHaveBeenLastCalledWith(
  340. expect.objectContaining({
  341. fetching: false,
  342. isIncomplete: null,
  343. hasMore: false,
  344. projects: [
  345. expect.objectContaining({
  346. id: '100',
  347. slug: 'a',
  348. }),
  349. expect.objectContaining({
  350. id: '101',
  351. slug: 'b',
  352. }),
  353. expect.objectContaining({
  354. id: '102',
  355. slug: 'test1',
  356. }),
  357. expect.objectContaining({
  358. id: '103',
  359. slug: 'test2',
  360. }),
  361. ],
  362. })
  363. );
  364. // Should not have duplicates
  365. wrapper.find('input').simulate('change', {target: {value: 'test'}});
  366. await tick();
  367. wrapper.update();
  368. expect(myRenderer).toHaveBeenLastCalledWith(
  369. expect.objectContaining({
  370. projects: [
  371. expect.objectContaining({
  372. id: '100',
  373. slug: 'a',
  374. }),
  375. expect.objectContaining({
  376. id: '101',
  377. slug: 'b',
  378. }),
  379. expect.objectContaining({
  380. id: '102',
  381. slug: 'test1',
  382. }),
  383. expect.objectContaining({
  384. id: '103',
  385. slug: 'test2',
  386. }),
  387. ],
  388. })
  389. );
  390. });
  391. });
  392. });