utils.spec.jsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. import {mount} from 'enzyme';
  2. import {
  3. getChartData,
  4. getChartDataWithPercentages,
  5. getChartDataByDay,
  6. getDisplayValue,
  7. getDisplayText,
  8. downloadAsCsv,
  9. } from 'app/views/organizationDiscover/result/utils';
  10. describe('Utils', function() {
  11. describe('getChartData()', function() {
  12. const raw = [
  13. {count: 2, uniq_id: 1, 'project.id': 5, environment: null},
  14. {count: 2, uniq_id: 3, 'project.id': 5, environment: 'staging'},
  15. {count: 2, uniq_id: 4, 'project.id': 5, environment: 'alpha'},
  16. {count: 6, uniq_id: 10, 'project.id': 5, environment: 'production'},
  17. ];
  18. const query = {
  19. aggregations: [['count()', null, 'count'], ['uniq', 'id', 'uniq_id']],
  20. fields: ['project.id', 'environment'],
  21. };
  22. it('returns chart data', function() {
  23. const expected = [
  24. {
  25. seriesName: 'count',
  26. data: [
  27. {value: 2, name: 'project.id 5 environment null'},
  28. {value: 2, name: 'project.id 5 environment staging'},
  29. {value: 2, name: 'project.id 5 environment alpha'},
  30. {value: 6, name: 'project.id 5 environment production'},
  31. ],
  32. },
  33. {
  34. seriesName: 'uniq_id',
  35. data: [
  36. {value: 1, name: 'project.id 5 environment null'},
  37. {value: 3, name: 'project.id 5 environment staging'},
  38. {value: 4, name: 'project.id 5 environment alpha'},
  39. {value: 10, name: 'project.id 5 environment production'},
  40. ],
  41. },
  42. ];
  43. expect(getChartData(raw, query)).toEqual(expected);
  44. });
  45. });
  46. describe('getChartDataWithPercentages()', function() {
  47. const raw = [
  48. {count: 2, uniq_id: 1, 'project.id': 5, environment: null},
  49. {count: 2, uniq_id: 3, 'project.id': 5, environment: 'staging'},
  50. {count: 2, uniq_id: 4, 'project.id': 5, environment: 'alpha'},
  51. {count: 6, uniq_id: 10, 'project.id': 5, environment: 'production'},
  52. ];
  53. const query = {
  54. aggregations: [['count()', null, 'count'], ['uniq', 'id', 'uniq_id']],
  55. fields: ['project.id', 'environment'],
  56. };
  57. it('returns chart data with percentages', function() {
  58. const expected = [
  59. {
  60. seriesName: 'count',
  61. data: [
  62. {value: 2, percentage: 16.67, name: '5 null'},
  63. {value: 2, percentage: 16.67, name: '5 staging'},
  64. {value: 2, percentage: 16.67, name: '5 alpha'},
  65. {value: 6, percentage: 50, name: '5 production'},
  66. ],
  67. },
  68. {
  69. seriesName: 'uniq_id',
  70. data: [
  71. {value: 1, percentage: 5.56, name: '5 null'},
  72. {value: 3, percentage: 16.67, name: '5 staging'},
  73. {value: 4, percentage: 22.22, name: '5 alpha'},
  74. {value: 10, percentage: 55.56, name: '5 production'},
  75. ],
  76. },
  77. ];
  78. expect(getChartDataWithPercentages(raw, query)).toEqual(expected);
  79. });
  80. });
  81. describe('getChartDataByDay()', function() {
  82. const raw = [
  83. {
  84. 'error.type': 'Type Error',
  85. platform: 'javascript',
  86. count: 5,
  87. time: 1532070000,
  88. },
  89. {
  90. 'error.type': 'Exception',
  91. platform: 'php',
  92. count: 8,
  93. time: 1532070000,
  94. },
  95. {
  96. 'error.type': 'SnubaError',
  97. platform: 'python',
  98. count: 30,
  99. time: 1532070000,
  100. },
  101. {
  102. 'error.type': 'ZeroDivisionError',
  103. platform: 'python',
  104. count: 20,
  105. time: 1531180800,
  106. },
  107. {
  108. 'error.type': 'ZeroDivisionError',
  109. platform: 'python',
  110. count: 6,
  111. time: 1531094400,
  112. },
  113. {
  114. 'error.type': 'Type Error',
  115. platform: 'javascript',
  116. count: 6,
  117. time: 1531094400,
  118. },
  119. {
  120. 'error.type': 'Exception',
  121. platform: 'php',
  122. count: 6,
  123. time: 1531094400,
  124. },
  125. {
  126. 'error.type': 'SnubaError',
  127. platform: 'python',
  128. count: 14,
  129. time: 1531094400,
  130. },
  131. ];
  132. const query = {
  133. aggregations: [['count()', null, 'count']],
  134. fields: ['platform', 'error.type'],
  135. };
  136. it('returns chart data grouped by day', function() {
  137. const expected = [
  138. {
  139. data: [
  140. {name: 1531094400000, value: 14},
  141. {name: 1531180800000, value: null},
  142. {name: 1532070000000, value: 30},
  143. ],
  144. seriesName: 'python,SnubaError',
  145. },
  146. {
  147. data: [
  148. {name: 1531094400000, value: 6},
  149. {name: 1531180800000, value: null},
  150. {name: 1532070000000, value: 8},
  151. ],
  152. seriesName: 'php,Exception',
  153. },
  154. {
  155. data: [
  156. {name: 1531094400000, value: 6},
  157. {name: 1531180800000, value: null},
  158. {name: 1532070000000, value: 5},
  159. ],
  160. seriesName: 'javascript,Type Error',
  161. },
  162. {
  163. data: [
  164. {name: 1531094400000, value: 6},
  165. {name: 1531180800000, value: 20},
  166. {name: 1532070000000, value: null},
  167. ],
  168. seriesName: 'python,ZeroDivisionError',
  169. },
  170. ];
  171. expect(getChartDataByDay(raw, query)).toEqual(expected);
  172. });
  173. it('assumes null value as 0', function() {
  174. const expected = [
  175. {
  176. data: [
  177. {name: 1531094400000, value: 14},
  178. {name: 1531180800000, value: 0},
  179. {name: 1532070000000, value: 30},
  180. ],
  181. seriesName: 'python,SnubaError',
  182. },
  183. {
  184. data: [
  185. {name: 1531094400000, value: 6},
  186. {name: 1531180800000, value: 0},
  187. {name: 1532070000000, value: 8},
  188. ],
  189. seriesName: 'php,Exception',
  190. },
  191. {
  192. data: [
  193. {name: 1531094400000, value: 6},
  194. {name: 1531180800000, value: 0},
  195. {name: 1532070000000, value: 5},
  196. ],
  197. seriesName: 'javascript,Type Error',
  198. },
  199. {
  200. data: [
  201. {name: 1531094400000, value: 6},
  202. {name: 1531180800000, value: 20},
  203. {name: 1532070000000, value: 0},
  204. ],
  205. seriesName: 'python,ZeroDivisionError',
  206. },
  207. ];
  208. expect(getChartDataByDay(raw, query, {assumeNullAsZero: true})).toEqual(expected);
  209. });
  210. it('shows only top 10 series by default', function() {
  211. expect(
  212. getChartDataByDay(
  213. [
  214. ...raw,
  215. ...[...new Array(10)].map(() => ({
  216. 'error.type': 'Exeption',
  217. platform: `${Math.random()}`,
  218. count: 10,
  219. time: 1532070000,
  220. })),
  221. ],
  222. query
  223. )
  224. ).toHaveLength(10);
  225. });
  226. it('shows all series', function() {
  227. expect(
  228. getChartDataByDay(
  229. [
  230. ...raw,
  231. ...[...new Array(10)].map(() => ({
  232. 'error.type': 'Exeption',
  233. platform: `${Math.random()}`,
  234. count: 10,
  235. time: 1532070000,
  236. })),
  237. ],
  238. query,
  239. {allSeries: true}
  240. )
  241. ).toHaveLength(14);
  242. });
  243. it('maps field value to label', function() {
  244. const expected = [
  245. {
  246. data: [
  247. {name: 1531094400000, value: 14},
  248. {name: 1531180800000, value: null},
  249. {name: 1532070000000, value: 30},
  250. ],
  251. seriesName: 'SNAKES,SnubaError',
  252. },
  253. {
  254. data: [
  255. {name: 1531094400000, value: 6},
  256. {name: 1531180800000, value: null},
  257. {name: 1532070000000, value: 8},
  258. ],
  259. seriesName: 'PHP,Exception',
  260. },
  261. {
  262. data: [
  263. {name: 1531094400000, value: 6},
  264. {name: 1531180800000, value: null},
  265. {name: 1532070000000, value: 5},
  266. ],
  267. seriesName: 'NOT JAVA,Type Error',
  268. },
  269. {
  270. data: [
  271. {name: 1531094400000, value: 6},
  272. {name: 1531180800000, value: 20},
  273. {name: 1532070000000, value: null},
  274. ],
  275. seriesName: 'SNAKES,ZeroDivisionError',
  276. },
  277. ];
  278. expect(
  279. getChartDataByDay(raw, query, {
  280. fieldLabelMap: {python: 'SNAKES', php: 'PHP', javascript: 'NOT JAVA'},
  281. })
  282. ).toEqual(expected);
  283. });
  284. });
  285. it('getDisplayValue()', function() {
  286. const testData = [
  287. {input: null, expectedText: 'null'},
  288. {
  289. input: 'some thing',
  290. expectedText: '"some thing"',
  291. },
  292. {
  293. input: 12,
  294. expectedText: '12',
  295. },
  296. {
  297. input: ['one', 'two', 'three'],
  298. expectedText: '["one","two","three"]',
  299. },
  300. {
  301. input: 1000000,
  302. expectedText: '1,000,000',
  303. },
  304. ];
  305. testData.forEach(({input, expectedText}) => {
  306. expect(mount(getDisplayValue(input)).text()).toBe(expectedText);
  307. });
  308. });
  309. it('getTextValue()', function() {
  310. const testData = [
  311. {input: null, expectedText: 'null'},
  312. {
  313. input: 'some thing',
  314. expectedText: '"some thing"',
  315. },
  316. {
  317. input: 12,
  318. expectedText: '12',
  319. },
  320. {
  321. input: ['one', 'two', 'three'],
  322. expectedText: '["one","two","three"]',
  323. },
  324. {
  325. input: 1000000,
  326. expectedText: '1,000,000',
  327. },
  328. ];
  329. testData.forEach(({input, expectedText}) => {
  330. expect(getDisplayText(input)).toBe(expectedText);
  331. });
  332. });
  333. describe('downloadAsCsv()', function() {
  334. let locationSpy;
  335. beforeEach(function() {
  336. locationSpy = jest.spyOn(window.location, 'assign').mockImplementation(_ => _);
  337. });
  338. afterEach(function() {
  339. jest.restoreAllMocks();
  340. });
  341. it('handles raw data', function() {
  342. const result = {
  343. meta: [{name: 'message'}, {name: 'environment'}],
  344. data: [
  345. {message: 'test 1', environment: 'prod'},
  346. {message: 'test 2', environment: 'test'},
  347. ],
  348. };
  349. downloadAsCsv(result);
  350. expect(locationSpy).toHaveBeenCalledWith(
  351. expect.stringContaining(
  352. encodeURI('message,environment\r\ntest 1,prod\r\ntest 2,test')
  353. )
  354. );
  355. });
  356. it('handles aggregations', function() {
  357. const result = {
  358. meta: [{type: 'UInt64', name: 'count'}],
  359. data: [{count: 3}],
  360. };
  361. downloadAsCsv(result);
  362. expect(locationSpy).toHaveBeenCalledWith(
  363. expect.stringContaining(encodeURI('count\r\n3'))
  364. );
  365. });
  366. it('quotes unsafe strings', function() {
  367. const result = {
  368. meta: [{name: 'message'}],
  369. data: [{message: '=HYPERLINK(http://some-bad-website)'}],
  370. };
  371. downloadAsCsv(result);
  372. expect(locationSpy).toHaveBeenCalledWith(
  373. expect.stringContaining(
  374. encodeURI("message\r\n'=HYPERLINK(http://some-bad-website)")
  375. )
  376. );
  377. });
  378. });
  379. });