quickTrace.spec.tsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. import {mountWithTheme} from 'sentry-test/enzyme';
  2. import {initializeOrg} from 'sentry-test/initializeOrg';
  3. import QuickTrace from 'sentry/components/quickTrace';
  4. import {Event} from 'sentry/types/event';
  5. import {QuickTraceEvent} from 'sentry/utils/performance/quickTrace/types';
  6. describe('Quick Trace', function () {
  7. let location;
  8. let organization;
  9. const initialize = () => {
  10. const context = initializeOrg();
  11. organization = context.organization;
  12. };
  13. function makeQuickTraceEvents(generation, {n = 1, parentId = null} = {}) {
  14. const events: QuickTraceEvent[] = [];
  15. for (let i = 0; i < n; i++) {
  16. const suffix = n > 1 ? `-${i}` : '';
  17. events.push({
  18. event_id: `e${generation}${suffix}`,
  19. generation,
  20. span_id: `s${generation}${suffix}`,
  21. transaction: `t${generation}${suffix}`,
  22. 'transaction.duration': 1234,
  23. project_id: generation,
  24. project_slug: `p${generation}`,
  25. parent_event_id:
  26. generation === 0 ? null : parentId === null ? `e${generation - 1}` : parentId,
  27. parent_span_id:
  28. generation === 0
  29. ? null
  30. : parentId === null
  31. ? `s${generation - 1}${parentId}`
  32. : `s${parentId}`,
  33. });
  34. }
  35. return events;
  36. }
  37. function makeTransactionEvent(id) {
  38. return {
  39. id: `e${id}`,
  40. type: 'transaction',
  41. startTimestamp: 1615921516.132774,
  42. endTimestamp: 1615921517.924861,
  43. };
  44. }
  45. function makeTransactionTarget(
  46. pid: string,
  47. eid: string,
  48. transaction: string,
  49. project: string
  50. ) {
  51. const query = {transaction, project};
  52. return {
  53. pathname: `/organizations/${organization.slug}/performance/${pid}:${eid}/`,
  54. query,
  55. };
  56. }
  57. beforeEach(function () {
  58. initialize();
  59. location = {
  60. pathname: '/',
  61. query: {},
  62. };
  63. });
  64. describe('Empty Trace', function () {
  65. it('renders nothing for empty trace', function () {
  66. const quickTrace = mountWithTheme(
  67. <QuickTrace
  68. event={makeTransactionEvent(1) as Event}
  69. quickTrace={{
  70. type: 'empty',
  71. trace: [],
  72. }}
  73. anchor="left"
  74. errorDest="issue"
  75. transactionDest="performance"
  76. location={location}
  77. organization={organization}
  78. />
  79. );
  80. expect(quickTrace.text()).toEqual('\u2014');
  81. });
  82. });
  83. describe('Partial Trace', function () {
  84. it('renders nothing when partial trace is empty', function () {
  85. const quickTrace = mountWithTheme(
  86. <QuickTrace
  87. event={makeTransactionEvent(1) as Event}
  88. quickTrace={{
  89. type: 'partial',
  90. trace: null,
  91. }}
  92. anchor="left"
  93. errorDest="issue"
  94. transactionDest="performance"
  95. location={location}
  96. organization={organization}
  97. />
  98. );
  99. expect(quickTrace.text()).toEqual('\u2014');
  100. });
  101. it('renders nothing when partial trace missing current event', function () {
  102. const quickTrace = mountWithTheme(
  103. <QuickTrace
  104. event={makeTransactionEvent('not-1') as Event}
  105. quickTrace={{
  106. type: 'partial',
  107. trace: makeQuickTraceEvents(1),
  108. }}
  109. anchor="left"
  110. errorDest="issue"
  111. transactionDest="performance"
  112. location={location}
  113. organization={organization}
  114. />
  115. );
  116. expect(quickTrace.text()).toEqual('\u2014');
  117. });
  118. it('renders partial trace with no children', function () {
  119. const quickTrace = mountWithTheme(
  120. <QuickTrace
  121. event={makeTransactionEvent(4) as Event}
  122. quickTrace={{
  123. type: 'partial',
  124. trace: makeQuickTraceEvents(4),
  125. }}
  126. anchor="left"
  127. errorDest="issue"
  128. transactionDest="performance"
  129. location={location}
  130. organization={organization}
  131. />
  132. );
  133. const nodes = quickTrace.find('EventNode');
  134. expect(nodes.length).toEqual(1);
  135. expect(nodes.first().text()).toEqual('This Event');
  136. });
  137. it('renders partial trace with single child', function () {
  138. const quickTrace = mountWithTheme(
  139. <QuickTrace
  140. event={makeTransactionEvent(4) as Event}
  141. quickTrace={{
  142. type: 'partial',
  143. trace: [...makeQuickTraceEvents(4), ...makeQuickTraceEvents(5)],
  144. }}
  145. anchor="left"
  146. errorDest="issue"
  147. transactionDest="performance"
  148. location={location}
  149. organization={organization}
  150. />
  151. );
  152. const nodes = quickTrace.find('EventNode');
  153. expect(nodes.length).toEqual(2);
  154. ['This Event', '1 Child'].forEach((text, i) =>
  155. expect(nodes.at(i).text()).toEqual(text)
  156. );
  157. });
  158. it('renders partial trace with multiple children', function () {
  159. const quickTrace = mountWithTheme(
  160. <QuickTrace
  161. event={makeTransactionEvent(4) as Event}
  162. quickTrace={{
  163. type: 'partial',
  164. trace: [...makeQuickTraceEvents(4), ...makeQuickTraceEvents(5, {n: 3})],
  165. }}
  166. anchor="left"
  167. errorDest="issue"
  168. transactionDest="performance"
  169. location={location}
  170. organization={organization}
  171. />
  172. );
  173. const nodes = quickTrace.find('EventNode');
  174. expect(nodes.length).toEqual(2);
  175. ['This Event', '3 Children'].forEach((text, i) =>
  176. expect(nodes.at(i).text()).toEqual(text)
  177. );
  178. });
  179. it('renders full trace with root as parent', function () {
  180. const quickTrace = mountWithTheme(
  181. <QuickTrace
  182. event={makeTransactionEvent(1) as Event}
  183. quickTrace={{
  184. type: 'partial',
  185. trace: [...makeQuickTraceEvents(0), ...makeQuickTraceEvents(1)],
  186. }}
  187. anchor="left"
  188. errorDest="issue"
  189. transactionDest="performance"
  190. location={location}
  191. organization={organization}
  192. />
  193. );
  194. const nodes = quickTrace.find('EventNode');
  195. expect(nodes.length).toEqual(2);
  196. ['Parent', 'This Event'].forEach((text, i) =>
  197. expect(nodes.at(i).text()).toEqual(text)
  198. );
  199. });
  200. });
  201. describe('Full Trace', function () {
  202. it('renders full trace with single ancestor', function () {
  203. const quickTrace = mountWithTheme(
  204. <QuickTrace
  205. event={makeTransactionEvent(3) as Event}
  206. quickTrace={{
  207. type: 'full',
  208. trace: [
  209. ...makeQuickTraceEvents(0),
  210. ...makeQuickTraceEvents(1),
  211. ...makeQuickTraceEvents(2),
  212. ...makeQuickTraceEvents(3),
  213. ],
  214. }}
  215. anchor="left"
  216. errorDest="issue"
  217. transactionDest="performance"
  218. location={location}
  219. organization={organization}
  220. />
  221. );
  222. const nodes = quickTrace.find('EventNode');
  223. expect(nodes.length).toEqual(4);
  224. ['Root', '1 Ancestor', 'Parent', 'This Event'].forEach((text, i) =>
  225. expect(nodes.at(i).text()).toEqual(text)
  226. );
  227. });
  228. it('renders full trace with multiple ancestors', function () {
  229. const quickTrace = mountWithTheme(
  230. <QuickTrace
  231. event={makeTransactionEvent(5) as Event}
  232. quickTrace={{
  233. type: 'full',
  234. trace: [
  235. ...makeQuickTraceEvents(0),
  236. ...makeQuickTraceEvents(1),
  237. ...makeQuickTraceEvents(2),
  238. ...makeQuickTraceEvents(3),
  239. ...makeQuickTraceEvents(4),
  240. ...makeQuickTraceEvents(5),
  241. ],
  242. }}
  243. anchor="left"
  244. errorDest="issue"
  245. transactionDest="performance"
  246. location={location}
  247. organization={organization}
  248. />
  249. );
  250. const nodes = quickTrace.find('EventNode');
  251. expect(nodes.length).toEqual(4);
  252. ['Root', '3 Ancestors', 'Parent', 'This Event'].forEach((text, i) =>
  253. expect(nodes.at(i).text()).toEqual(text)
  254. );
  255. });
  256. it('renders full trace with single descendant', function () {
  257. const quickTrace = mountWithTheme(
  258. <QuickTrace
  259. event={makeTransactionEvent(0) as Event}
  260. quickTrace={{
  261. type: 'full',
  262. trace: [
  263. ...makeQuickTraceEvents(0),
  264. ...makeQuickTraceEvents(1),
  265. ...makeQuickTraceEvents(2),
  266. ],
  267. }}
  268. anchor="left"
  269. errorDest="issue"
  270. transactionDest="performance"
  271. location={location}
  272. organization={organization}
  273. />
  274. );
  275. const nodes = quickTrace.find('EventNode');
  276. expect(nodes.length).toEqual(3);
  277. ['This Event', '1 Child', '1 Descendant'].forEach((text, i) =>
  278. expect(nodes.at(i).text()).toEqual(text)
  279. );
  280. });
  281. it('renders full trace with multiple descendants', function () {
  282. const quickTrace = mountWithTheme(
  283. <QuickTrace
  284. event={makeTransactionEvent(0) as Event}
  285. quickTrace={{
  286. type: 'full',
  287. trace: [
  288. ...makeQuickTraceEvents(0),
  289. ...makeQuickTraceEvents(1),
  290. ...makeQuickTraceEvents(2),
  291. ...makeQuickTraceEvents(3),
  292. ...makeQuickTraceEvents(4),
  293. ],
  294. }}
  295. anchor="left"
  296. errorDest="issue"
  297. transactionDest="performance"
  298. location={location}
  299. organization={organization}
  300. />
  301. );
  302. const nodes = quickTrace.find('EventNode');
  303. expect(nodes.length).toEqual(3);
  304. ['This Event', '1 Child', '3 Descendants'].forEach((text, i) =>
  305. expect(nodes.at(i).text()).toEqual(text)
  306. );
  307. });
  308. it('renders full trace', function () {
  309. const quickTrace = mountWithTheme(
  310. <QuickTrace
  311. event={makeTransactionEvent(5) as Event}
  312. quickTrace={{
  313. type: 'full',
  314. trace: [
  315. ...makeQuickTraceEvents(0),
  316. ...makeQuickTraceEvents(1),
  317. ...makeQuickTraceEvents(2),
  318. ...makeQuickTraceEvents(3),
  319. ...makeQuickTraceEvents(4),
  320. ...makeQuickTraceEvents(5),
  321. ...makeQuickTraceEvents(6),
  322. ...makeQuickTraceEvents(7),
  323. ...makeQuickTraceEvents(8),
  324. ...makeQuickTraceEvents(9),
  325. ],
  326. }}
  327. anchor="left"
  328. errorDest="issue"
  329. transactionDest="performance"
  330. location={location}
  331. organization={organization}
  332. />
  333. );
  334. const nodes = quickTrace.find('EventNode');
  335. expect(nodes.length).toEqual(6);
  336. ['Root', '3 Ancestors', 'Parent', 'This Event', '1 Child', '3 Descendants'].forEach(
  337. (text, i) => expect(nodes.at(i).text()).toEqual(text)
  338. );
  339. });
  340. });
  341. describe('Event Node Clicks', function () {
  342. it('renders single event targets', function () {
  343. const quickTrace = mountWithTheme(
  344. <QuickTrace
  345. event={makeTransactionEvent(3) as Event}
  346. quickTrace={{
  347. type: 'full',
  348. trace: [
  349. ...makeQuickTraceEvents(0),
  350. ...makeQuickTraceEvents(1),
  351. ...makeQuickTraceEvents(2),
  352. ...makeQuickTraceEvents(3),
  353. ...makeQuickTraceEvents(4),
  354. ...makeQuickTraceEvents(5),
  355. ],
  356. }}
  357. anchor="left"
  358. errorDest="issue"
  359. transactionDest="performance"
  360. location={location}
  361. organization={organization}
  362. />
  363. );
  364. const nodes = quickTrace.find('EventNode');
  365. expect(nodes.length).toEqual(6);
  366. [
  367. makeTransactionTarget('p0', 'e0', 't0', '0'),
  368. makeTransactionTarget('p1', 'e1', 't1', '1'),
  369. makeTransactionTarget('p2', 'e2', 't2', '2'),
  370. undefined, // the "This Event" node has no target
  371. makeTransactionTarget('p4', 'e4', 't4', '4'),
  372. makeTransactionTarget('p5', 'e5', 't5', '5'),
  373. ].forEach((target, i) => expect(nodes.at(i).props().to).toEqual(target));
  374. });
  375. it('renders multiple event targets', function () {
  376. const quickTrace = mountWithTheme(
  377. <QuickTrace
  378. event={makeTransactionEvent(0) as Event}
  379. quickTrace={{
  380. type: 'full',
  381. trace: [...makeQuickTraceEvents(0), ...makeQuickTraceEvents(1, {n: 3})],
  382. }}
  383. anchor="left"
  384. errorDest="issue"
  385. transactionDest="performance"
  386. location={location}
  387. organization={organization}
  388. />
  389. );
  390. const items = quickTrace.find('DropdownItem');
  391. expect(items.length).toEqual(3);
  392. // can't easily assert the target is correct since it uses an onClick handler
  393. });
  394. });
  395. });