quickTrace.spec.tsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  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. MockApiClient.warnOnMissingMocks();
  120. const quickTrace = mountWithTheme(
  121. <QuickTrace
  122. event={makeTransactionEvent(4) as Event}
  123. quickTrace={{
  124. type: 'partial',
  125. trace: makeQuickTraceEvents(4),
  126. }}
  127. anchor="left"
  128. errorDest="issue"
  129. transactionDest="performance"
  130. location={location}
  131. organization={organization}
  132. />
  133. );
  134. const nodes = quickTrace.find('EventNode');
  135. expect(nodes.length).toEqual(1);
  136. expect(nodes.first().text()).toEqual('This Event');
  137. });
  138. it('renders partial trace with single child', function () {
  139. const quickTrace = mountWithTheme(
  140. <QuickTrace
  141. event={makeTransactionEvent(4) as Event}
  142. quickTrace={{
  143. type: 'partial',
  144. trace: [...makeQuickTraceEvents(4), ...makeQuickTraceEvents(5)],
  145. }}
  146. anchor="left"
  147. errorDest="issue"
  148. transactionDest="performance"
  149. location={location}
  150. organization={organization}
  151. />
  152. );
  153. const nodes = quickTrace.find('EventNode');
  154. expect(nodes.length).toEqual(2);
  155. ['This Event', '1 Child'].forEach((text, i) =>
  156. expect(nodes.at(i).text()).toEqual(text)
  157. );
  158. });
  159. it('renders partial trace with multiple children', function () {
  160. MockApiClient.warnOnMissingMocks();
  161. const quickTrace = mountWithTheme(
  162. <QuickTrace
  163. event={makeTransactionEvent(4) as Event}
  164. quickTrace={{
  165. type: 'partial',
  166. trace: [...makeQuickTraceEvents(4), ...makeQuickTraceEvents(5, {n: 3})],
  167. }}
  168. anchor="left"
  169. errorDest="issue"
  170. transactionDest="performance"
  171. location={location}
  172. organization={organization}
  173. />
  174. );
  175. const nodes = quickTrace.find('EventNode');
  176. expect(nodes.length).toEqual(2);
  177. ['This Event', '3 Children'].forEach((text, i) =>
  178. expect(nodes.at(i).text()).toEqual(text)
  179. );
  180. });
  181. it('renders full trace with root as parent', function () {
  182. const quickTrace = mountWithTheme(
  183. <QuickTrace
  184. event={makeTransactionEvent(1) as Event}
  185. quickTrace={{
  186. type: 'partial',
  187. trace: [...makeQuickTraceEvents(0), ...makeQuickTraceEvents(1)],
  188. }}
  189. anchor="left"
  190. errorDest="issue"
  191. transactionDest="performance"
  192. location={location}
  193. organization={organization}
  194. />
  195. );
  196. const nodes = quickTrace.find('EventNode');
  197. expect(nodes.length).toEqual(2);
  198. ['Parent', 'This Event'].forEach((text, i) =>
  199. expect(nodes.at(i).text()).toEqual(text)
  200. );
  201. });
  202. });
  203. describe('Full Trace', function () {
  204. it('renders full trace with single ancestor', function () {
  205. const quickTrace = mountWithTheme(
  206. <QuickTrace
  207. event={makeTransactionEvent(3) as Event}
  208. quickTrace={{
  209. type: 'full',
  210. trace: [
  211. ...makeQuickTraceEvents(0),
  212. ...makeQuickTraceEvents(1),
  213. ...makeQuickTraceEvents(2),
  214. ...makeQuickTraceEvents(3),
  215. ],
  216. }}
  217. anchor="left"
  218. errorDest="issue"
  219. transactionDest="performance"
  220. location={location}
  221. organization={organization}
  222. />
  223. );
  224. const nodes = quickTrace.find('EventNode');
  225. expect(nodes.length).toEqual(4);
  226. ['Root', '1 Ancestor', 'Parent', 'This Event'].forEach((text, i) =>
  227. expect(nodes.at(i).text()).toEqual(text)
  228. );
  229. });
  230. it('renders full trace with multiple ancestors', function () {
  231. MockApiClient.warnOnMissingMocks();
  232. const quickTrace = mountWithTheme(
  233. <QuickTrace
  234. event={makeTransactionEvent(5) as Event}
  235. quickTrace={{
  236. type: 'full',
  237. trace: [
  238. ...makeQuickTraceEvents(0),
  239. ...makeQuickTraceEvents(1),
  240. ...makeQuickTraceEvents(2),
  241. ...makeQuickTraceEvents(3),
  242. ...makeQuickTraceEvents(4),
  243. ...makeQuickTraceEvents(5),
  244. ],
  245. }}
  246. anchor="left"
  247. errorDest="issue"
  248. transactionDest="performance"
  249. location={location}
  250. organization={organization}
  251. />
  252. );
  253. const nodes = quickTrace.find('EventNode');
  254. expect(nodes.length).toEqual(4);
  255. ['Root', '3 Ancestors', 'Parent', 'This Event'].forEach((text, i) =>
  256. expect(nodes.at(i).text()).toEqual(text)
  257. );
  258. });
  259. it('renders full trace with single descendant', function () {
  260. const quickTrace = mountWithTheme(
  261. <QuickTrace
  262. event={makeTransactionEvent(0) as Event}
  263. quickTrace={{
  264. type: 'full',
  265. trace: [
  266. ...makeQuickTraceEvents(0),
  267. ...makeQuickTraceEvents(1),
  268. ...makeQuickTraceEvents(2),
  269. ],
  270. }}
  271. anchor="left"
  272. errorDest="issue"
  273. transactionDest="performance"
  274. location={location}
  275. organization={organization}
  276. />
  277. );
  278. const nodes = quickTrace.find('EventNode');
  279. expect(nodes.length).toEqual(3);
  280. ['This Event', '1 Child', '1 Descendant'].forEach((text, i) =>
  281. expect(nodes.at(i).text()).toEqual(text)
  282. );
  283. });
  284. it('renders full trace with multiple descendants', function () {
  285. MockApiClient.warnOnMissingMocks();
  286. const quickTrace = mountWithTheme(
  287. <QuickTrace
  288. event={makeTransactionEvent(0) as Event}
  289. quickTrace={{
  290. type: 'full',
  291. trace: [
  292. ...makeQuickTraceEvents(0),
  293. ...makeQuickTraceEvents(1),
  294. ...makeQuickTraceEvents(2),
  295. ...makeQuickTraceEvents(3),
  296. ...makeQuickTraceEvents(4),
  297. ],
  298. }}
  299. anchor="left"
  300. errorDest="issue"
  301. transactionDest="performance"
  302. location={location}
  303. organization={organization}
  304. />
  305. );
  306. const nodes = quickTrace.find('EventNode');
  307. expect(nodes.length).toEqual(3);
  308. ['This Event', '1 Child', '3 Descendants'].forEach((text, i) =>
  309. expect(nodes.at(i).text()).toEqual(text)
  310. );
  311. });
  312. it('renders full trace', function () {
  313. MockApiClient.warnOnMissingMocks();
  314. const quickTrace = mountWithTheme(
  315. <QuickTrace
  316. event={makeTransactionEvent(5) as Event}
  317. quickTrace={{
  318. type: 'full',
  319. trace: [
  320. ...makeQuickTraceEvents(0),
  321. ...makeQuickTraceEvents(1),
  322. ...makeQuickTraceEvents(2),
  323. ...makeQuickTraceEvents(3),
  324. ...makeQuickTraceEvents(4),
  325. ...makeQuickTraceEvents(5),
  326. ...makeQuickTraceEvents(6),
  327. ...makeQuickTraceEvents(7),
  328. ...makeQuickTraceEvents(8),
  329. ...makeQuickTraceEvents(9),
  330. ],
  331. }}
  332. anchor="left"
  333. errorDest="issue"
  334. transactionDest="performance"
  335. location={location}
  336. organization={organization}
  337. />
  338. );
  339. const nodes = quickTrace.find('EventNode');
  340. expect(nodes.length).toEqual(6);
  341. ['Root', '3 Ancestors', 'Parent', 'This Event', '1 Child', '3 Descendants'].forEach(
  342. (text, i) => expect(nodes.at(i).text()).toEqual(text)
  343. );
  344. });
  345. });
  346. describe('Event Node Clicks', function () {
  347. it('renders single event targets', function () {
  348. const quickTrace = mountWithTheme(
  349. <QuickTrace
  350. event={makeTransactionEvent(3) as Event}
  351. quickTrace={{
  352. type: 'full',
  353. trace: [
  354. ...makeQuickTraceEvents(0),
  355. ...makeQuickTraceEvents(1),
  356. ...makeQuickTraceEvents(2),
  357. ...makeQuickTraceEvents(3),
  358. ...makeQuickTraceEvents(4),
  359. ...makeQuickTraceEvents(5),
  360. ],
  361. }}
  362. anchor="left"
  363. errorDest="issue"
  364. transactionDest="performance"
  365. location={location}
  366. organization={organization}
  367. />
  368. );
  369. const nodes = quickTrace.find('EventNode');
  370. expect(nodes.length).toEqual(6);
  371. [
  372. makeTransactionTarget('p0', 'e0', 't0', '0'),
  373. makeTransactionTarget('p1', 'e1', 't1', '1'),
  374. makeTransactionTarget('p2', 'e2', 't2', '2'),
  375. undefined, // the "This Event" node has no target
  376. makeTransactionTarget('p4', 'e4', 't4', '4'),
  377. makeTransactionTarget('p5', 'e5', 't5', '5'),
  378. ].forEach((target, i) => expect(nodes.at(i).props().to).toEqual(target));
  379. });
  380. it('renders multiple event targets', function () {
  381. MockApiClient.warnOnMissingMocks();
  382. const quickTrace = mountWithTheme(
  383. <QuickTrace
  384. event={makeTransactionEvent(0) as Event}
  385. quickTrace={{
  386. type: 'full',
  387. trace: [...makeQuickTraceEvents(0), ...makeQuickTraceEvents(1, {n: 3})],
  388. }}
  389. anchor="left"
  390. errorDest="issue"
  391. transactionDest="performance"
  392. location={location}
  393. organization={organization}
  394. />
  395. );
  396. const items = quickTrace.find('DropdownItem');
  397. expect(items.length).toEqual(3);
  398. // can't easily assert the target is correct since it uses an onClick handler
  399. });
  400. });
  401. });