waterfallModel.spec.tsx 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043
  1. import {ActiveFilter, noFilter} from 'sentry/components/events/interfaces/spans/filter';
  2. import {EnhancedProcessedSpanType} from 'sentry/components/events/interfaces/spans/types';
  3. import WaterfallModel from 'sentry/components/events/interfaces/spans/waterfallModel';
  4. import {EntryType, EventTransaction} from 'sentry/types/event';
  5. import {assert} from 'sentry/types/utils';
  6. describe('WaterfallModel', () => {
  7. const event = {
  8. id: '2b658a829a21496b87fd1f14a61abf65',
  9. eventID: '2b658a829a21496b87fd1f14a61abf65',
  10. title: '/organizations/:orgId/discover/results/',
  11. type: 'transaction',
  12. startTimestamp: 1622079935.86141,
  13. endTimestamp: 1622079940.032905,
  14. contexts: {
  15. trace: {
  16. trace_id: '8cbbc19c0f54447ab702f00263262726',
  17. span_id: 'a934857184bdf5a6',
  18. op: 'pageload',
  19. status: 'unknown',
  20. type: 'trace',
  21. },
  22. },
  23. entries: [
  24. {
  25. data: [
  26. {
  27. timestamp: 1622079937.227645,
  28. start_timestamp: 1622079936.90689,
  29. description: 'GET /api/0/organizations/?member=1',
  30. op: 'http',
  31. span_id: 'b23703998ae619e7',
  32. parent_span_id: 'a934857184bdf5a6',
  33. trace_id: '8cbbc19c0f54447ab702f00263262726',
  34. status: 'ok',
  35. tags: {
  36. 'http.status_code': '200',
  37. },
  38. data: {
  39. method: 'GET',
  40. type: 'fetch',
  41. url: '/api/0/organizations/?member=1',
  42. },
  43. },
  44. {
  45. timestamp: 1622079938.20331,
  46. start_timestamp: 1622079937.907515,
  47. description: 'GET /api/0/internal/health/',
  48. op: 'http',
  49. span_id: 'a453cc713e5baf9c',
  50. parent_span_id: 'a934857184bdf5a6',
  51. trace_id: '8cbbc19c0f54447ab702f00263262726',
  52. status: 'ok',
  53. tags: {
  54. 'http.status_code': '200',
  55. },
  56. data: {
  57. method: 'GET',
  58. type: 'fetch',
  59. url: '/api/0/internal/health/',
  60. },
  61. },
  62. {
  63. timestamp: 1622079936.05839,
  64. start_timestamp: 1622079936.048125,
  65. description: '/_static/dist/sentry/sentry.541f5b.css',
  66. op: 'resource.link',
  67. span_id: 'a23f26b939d1a735',
  68. parent_span_id: 'a453cc713e5baf9c',
  69. trace_id: '8cbbc19c0f54447ab702f00263262726',
  70. data: {
  71. 'Decoded Body Size': 159248,
  72. 'Encoded Body Size': 159248,
  73. 'Transfer Size': 275,
  74. },
  75. },
  76. {
  77. timestamp: 1622079938.32451,
  78. start_timestamp: 1622079938.31431,
  79. description: 'fonts_bundle.css',
  80. op: 'fonts',
  81. span_id: 'a0e89ce4e0900ad5',
  82. parent_span_id: 'a23f26b939d1a735',
  83. trace_id: '8cbbc19c0f54447ab702f00263262726',
  84. data: {
  85. 'Decoded Body Size': 159248,
  86. 'Encoded Body Size': 159248,
  87. 'Transfer Size': 275,
  88. },
  89. },
  90. {
  91. timestamp: 1622079938.32451,
  92. start_timestamp: 1622079938.31431,
  93. description: 'google-fonts.css',
  94. op: 'fonts',
  95. span_id: 'aa764a4e9d70c907',
  96. parent_span_id: 'a0e89ce4e0900ad5',
  97. trace_id: '8cbbc19c0f54447ab702f00263262726',
  98. data: {
  99. 'Decoded Body Size': 159248,
  100. 'Encoded Body Size': 159248,
  101. 'Transfer Size': 275,
  102. },
  103. },
  104. {
  105. timestamp: 1622079938.32451,
  106. start_timestamp: 1622079938.31431,
  107. description: 'icon-fonts.css',
  108. op: 'fonts',
  109. span_id: '883837a47bc0d836',
  110. parent_span_id: 'a0e89ce4e0900ad5',
  111. trace_id: '8cbbc19c0f54447ab702f00263262726',
  112. data: {
  113. 'Decoded Body Size': 159248,
  114. 'Encoded Body Size': 159248,
  115. 'Transfer Size': 275,
  116. },
  117. },
  118. {
  119. timestamp: 1622079938.32451,
  120. start_timestamp: 1622079938.31431,
  121. description: '/_static/dist/sentry/sentry.fonts.min.css',
  122. op: 'css',
  123. span_id: 'b5795cf4ba68bbb4',
  124. parent_span_id: 'a934857184bdf5a6',
  125. trace_id: '8cbbc19c0f54447ab702f00263262726',
  126. data: {
  127. 'Decoded Body Size': 159248,
  128. 'Encoded Body Size': 159248,
  129. 'Transfer Size': 275,
  130. },
  131. },
  132. {
  133. timestamp: 1622079938.32451,
  134. start_timestamp: 1622079938.31431,
  135. description: 'fonts1.css',
  136. op: 'fonts',
  137. span_id: 'b5795cf4ba68bbb5',
  138. parent_span_id: 'b5795cf4ba68bbb4',
  139. trace_id: '8cbbc19c0f54447ab702f00263262726',
  140. data: {
  141. 'Decoded Body Size': 159248,
  142. 'Encoded Body Size': 159248,
  143. 'Transfer Size': 275,
  144. },
  145. },
  146. {
  147. timestamp: 1622079938.32451,
  148. start_timestamp: 1622079938.31431,
  149. description: 'fonts2.css',
  150. op: 'fonts',
  151. span_id: 'b5795cf4ba68bbb6',
  152. parent_span_id: 'b5795cf4ba68bbb5',
  153. trace_id: '8cbbc19c0f54447ab702f00263262726',
  154. data: {
  155. 'Decoded Body Size': 159248,
  156. 'Encoded Body Size': 159248,
  157. 'Transfer Size': 275,
  158. },
  159. },
  160. {
  161. timestamp: 1622079938.32451,
  162. start_timestamp: 1622079938.31431,
  163. description: 'fonts3.css',
  164. op: 'fonts',
  165. span_id: 'b5795cf4ba68bbb7',
  166. parent_span_id: 'b5795cf4ba68bbb6',
  167. trace_id: '8cbbc19c0f54447ab702f00263262726',
  168. data: {
  169. 'Decoded Body Size': 159248,
  170. 'Encoded Body Size': 159248,
  171. 'Transfer Size': 275,
  172. },
  173. },
  174. ],
  175. type: EntryType.SPANS,
  176. },
  177. ],
  178. } as EventTransaction;
  179. const fullWaterfall: EnhancedProcessedSpanType[] = [
  180. {
  181. type: 'root_span',
  182. span: {
  183. trace_id: '8cbbc19c0f54447ab702f00263262726',
  184. span_id: 'a934857184bdf5a6',
  185. start_timestamp: 1622079935.86141,
  186. timestamp: 1622079940.032905,
  187. description: undefined,
  188. parent_span_id: undefined,
  189. op: 'pageload',
  190. data: {},
  191. status: 'unknown',
  192. },
  193. numOfSpanChildren: 3,
  194. treeDepth: 0,
  195. isLastSibling: true,
  196. continuingTreeDepths: [],
  197. showEmbeddedChildren: false,
  198. toggleEmbeddedChildren: expect.any(Function),
  199. fetchEmbeddedChildrenState: 'idle',
  200. isEmbeddedTransactionTimeAdjusted: false,
  201. },
  202. {
  203. type: 'span',
  204. span: {
  205. timestamp: 1622079937.227645,
  206. start_timestamp: 1622079936.90689,
  207. description: 'GET /api/0/organizations/?member=1',
  208. op: 'http',
  209. span_id: 'b23703998ae619e7',
  210. parent_span_id: 'a934857184bdf5a6',
  211. trace_id: '8cbbc19c0f54447ab702f00263262726',
  212. status: 'ok',
  213. tags: {'http.status_code': '200'},
  214. data: {method: 'GET', type: 'fetch', url: '/api/0/organizations/?member=1'},
  215. },
  216. numOfSpanChildren: 0,
  217. treeDepth: 1,
  218. isLastSibling: false,
  219. continuingTreeDepths: [],
  220. showEmbeddedChildren: false,
  221. toggleEmbeddedChildren: expect.any(Function),
  222. fetchEmbeddedChildrenState: 'idle',
  223. toggleNestedSpanGroup: undefined,
  224. toggleSiblingSpanGroup: undefined,
  225. isEmbeddedTransactionTimeAdjusted: false,
  226. },
  227. {
  228. type: 'gap',
  229. span: {
  230. type: 'gap',
  231. start_timestamp: 1622079937.227645,
  232. timestamp: 1622079937.907515,
  233. description: 'Missing instrumentation',
  234. isOrphan: false,
  235. },
  236. numOfSpanChildren: 0,
  237. treeDepth: 1,
  238. isLastSibling: false,
  239. continuingTreeDepths: [],
  240. showEmbeddedChildren: false,
  241. toggleEmbeddedChildren: undefined,
  242. fetchEmbeddedChildrenState: 'idle',
  243. isEmbeddedTransactionTimeAdjusted: false,
  244. },
  245. {
  246. type: 'span',
  247. span: {
  248. timestamp: 1622079938.20331,
  249. start_timestamp: 1622079937.907515,
  250. description: 'GET /api/0/internal/health/',
  251. op: 'http',
  252. span_id: 'a453cc713e5baf9c',
  253. parent_span_id: 'a934857184bdf5a6',
  254. trace_id: '8cbbc19c0f54447ab702f00263262726',
  255. status: 'ok',
  256. tags: {'http.status_code': '200'},
  257. data: {method: 'GET', type: 'fetch', url: '/api/0/internal/health/'},
  258. },
  259. numOfSpanChildren: 1,
  260. treeDepth: 1,
  261. isLastSibling: false,
  262. continuingTreeDepths: [],
  263. showEmbeddedChildren: false,
  264. toggleEmbeddedChildren: expect.any(Function),
  265. fetchEmbeddedChildrenState: 'idle',
  266. toggleNestedSpanGroup: undefined,
  267. toggleSiblingSpanGroup: undefined,
  268. isEmbeddedTransactionTimeAdjusted: false,
  269. },
  270. {
  271. type: 'span',
  272. span: {
  273. timestamp: 1622079936.05839,
  274. start_timestamp: 1622079936.048125,
  275. description: '/_static/dist/sentry/sentry.541f5b.css',
  276. op: 'resource.link',
  277. span_id: 'a23f26b939d1a735',
  278. parent_span_id: 'a453cc713e5baf9c',
  279. trace_id: '8cbbc19c0f54447ab702f00263262726',
  280. data: {
  281. 'Decoded Body Size': 159248,
  282. 'Encoded Body Size': 159248,
  283. 'Transfer Size': 275,
  284. },
  285. },
  286. numOfSpanChildren: 1,
  287. treeDepth: 2,
  288. isLastSibling: true,
  289. continuingTreeDepths: [1],
  290. showEmbeddedChildren: false,
  291. toggleEmbeddedChildren: expect.any(Function),
  292. fetchEmbeddedChildrenState: 'idle',
  293. toggleNestedSpanGroup: undefined,
  294. toggleSiblingSpanGroup: undefined,
  295. isEmbeddedTransactionTimeAdjusted: false,
  296. },
  297. {
  298. type: 'span',
  299. span: {
  300. timestamp: 1622079938.32451,
  301. start_timestamp: 1622079938.31431,
  302. description: 'fonts_bundle.css',
  303. op: 'fonts',
  304. span_id: 'a0e89ce4e0900ad5',
  305. parent_span_id: 'a23f26b939d1a735',
  306. trace_id: '8cbbc19c0f54447ab702f00263262726',
  307. data: {
  308. 'Decoded Body Size': 159248,
  309. 'Encoded Body Size': 159248,
  310. 'Transfer Size': 275,
  311. },
  312. },
  313. numOfSpanChildren: 2,
  314. treeDepth: 3,
  315. isLastSibling: true,
  316. continuingTreeDepths: [1],
  317. showEmbeddedChildren: false,
  318. toggleEmbeddedChildren: expect.any(Function),
  319. fetchEmbeddedChildrenState: 'idle',
  320. toggleNestedSpanGroup: undefined,
  321. toggleSiblingSpanGroup: undefined,
  322. isEmbeddedTransactionTimeAdjusted: false,
  323. },
  324. {
  325. type: 'span',
  326. span: {
  327. timestamp: 1622079938.32451,
  328. start_timestamp: 1622079938.31431,
  329. description: 'google-fonts.css',
  330. op: 'fonts',
  331. span_id: 'aa764a4e9d70c907',
  332. parent_span_id: 'a0e89ce4e0900ad5',
  333. trace_id: '8cbbc19c0f54447ab702f00263262726',
  334. data: {
  335. 'Decoded Body Size': 159248,
  336. 'Encoded Body Size': 159248,
  337. 'Transfer Size': 275,
  338. },
  339. },
  340. numOfSpanChildren: 0,
  341. treeDepth: 4,
  342. isLastSibling: false,
  343. continuingTreeDepths: [1],
  344. showEmbeddedChildren: false,
  345. toggleEmbeddedChildren: expect.any(Function),
  346. fetchEmbeddedChildrenState: 'idle',
  347. toggleNestedSpanGroup: undefined,
  348. toggleSiblingSpanGroup: undefined,
  349. isEmbeddedTransactionTimeAdjusted: false,
  350. },
  351. {
  352. type: 'span',
  353. span: {
  354. timestamp: 1622079938.32451,
  355. start_timestamp: 1622079938.31431,
  356. description: 'icon-fonts.css',
  357. op: 'fonts',
  358. span_id: '883837a47bc0d836',
  359. parent_span_id: 'a0e89ce4e0900ad5',
  360. trace_id: '8cbbc19c0f54447ab702f00263262726',
  361. data: {
  362. 'Decoded Body Size': 159248,
  363. 'Encoded Body Size': 159248,
  364. 'Transfer Size': 275,
  365. },
  366. },
  367. numOfSpanChildren: 0,
  368. treeDepth: 4,
  369. isLastSibling: true,
  370. continuingTreeDepths: [1],
  371. showEmbeddedChildren: false,
  372. toggleEmbeddedChildren: expect.any(Function),
  373. fetchEmbeddedChildrenState: 'idle',
  374. toggleNestedSpanGroup: undefined,
  375. toggleSiblingSpanGroup: undefined,
  376. isEmbeddedTransactionTimeAdjusted: false,
  377. },
  378. {
  379. type: 'gap',
  380. span: {
  381. type: 'gap',
  382. start_timestamp: 1622079938.20331,
  383. timestamp: 1622079938.31431,
  384. description: 'Missing instrumentation',
  385. isOrphan: false,
  386. },
  387. numOfSpanChildren: 0,
  388. treeDepth: 1,
  389. isLastSibling: false,
  390. continuingTreeDepths: [],
  391. showEmbeddedChildren: false,
  392. toggleEmbeddedChildren: undefined,
  393. fetchEmbeddedChildrenState: 'idle',
  394. isEmbeddedTransactionTimeAdjusted: false,
  395. },
  396. {
  397. type: 'span',
  398. span: {
  399. timestamp: 1622079938.32451,
  400. start_timestamp: 1622079938.31431,
  401. description: '/_static/dist/sentry/sentry.fonts.min.css',
  402. op: 'css',
  403. span_id: 'b5795cf4ba68bbb4',
  404. parent_span_id: 'a934857184bdf5a6',
  405. trace_id: '8cbbc19c0f54447ab702f00263262726',
  406. data: {
  407. 'Decoded Body Size': 159248,
  408. 'Encoded Body Size': 159248,
  409. 'Transfer Size': 275,
  410. },
  411. },
  412. numOfSpanChildren: 1,
  413. treeDepth: 1,
  414. isLastSibling: true,
  415. continuingTreeDepths: [],
  416. showEmbeddedChildren: false,
  417. toggleEmbeddedChildren: expect.any(Function),
  418. fetchEmbeddedChildrenState: 'idle',
  419. toggleNestedSpanGroup: undefined,
  420. toggleSiblingSpanGroup: undefined,
  421. isEmbeddedTransactionTimeAdjusted: false,
  422. },
  423. {
  424. type: 'span_group_chain',
  425. span: {
  426. timestamp: 1622079938.32451,
  427. start_timestamp: 1622079938.31431,
  428. description: 'fonts3.css',
  429. op: 'fonts',
  430. span_id: 'b5795cf4ba68bbb7',
  431. parent_span_id: 'b5795cf4ba68bbb6',
  432. trace_id: '8cbbc19c0f54447ab702f00263262726',
  433. data: {
  434. 'Decoded Body Size': 159248,
  435. 'Encoded Body Size': 159248,
  436. 'Transfer Size': 275,
  437. },
  438. },
  439. treeDepth: 2,
  440. continuingTreeDepths: [],
  441. spanNestedGrouping: [
  442. {
  443. type: 'span',
  444. span: {
  445. timestamp: 1622079938.32451,
  446. start_timestamp: 1622079938.31431,
  447. description: 'fonts1.css',
  448. op: 'fonts',
  449. span_id: 'b5795cf4ba68bbb5',
  450. parent_span_id: 'b5795cf4ba68bbb4',
  451. trace_id: '8cbbc19c0f54447ab702f00263262726',
  452. data: {
  453. 'Decoded Body Size': 159248,
  454. 'Encoded Body Size': 159248,
  455. 'Transfer Size': 275,
  456. },
  457. },
  458. numOfSpanChildren: 1,
  459. treeDepth: 2,
  460. isLastSibling: true,
  461. continuingTreeDepths: [],
  462. showEmbeddedChildren: false,
  463. toggleEmbeddedChildren: expect.any(Function),
  464. fetchEmbeddedChildrenState: 'idle',
  465. toggleNestedSpanGroup: undefined,
  466. toggleSiblingSpanGroup: undefined,
  467. isEmbeddedTransactionTimeAdjusted: false,
  468. },
  469. {
  470. type: 'span',
  471. span: {
  472. timestamp: 1622079938.32451,
  473. start_timestamp: 1622079938.31431,
  474. description: 'fonts2.css',
  475. op: 'fonts',
  476. span_id: 'b5795cf4ba68bbb6',
  477. parent_span_id: 'b5795cf4ba68bbb5',
  478. trace_id: '8cbbc19c0f54447ab702f00263262726',
  479. data: {
  480. 'Decoded Body Size': 159248,
  481. 'Encoded Body Size': 159248,
  482. 'Transfer Size': 275,
  483. },
  484. },
  485. numOfSpanChildren: 1,
  486. treeDepth: 2,
  487. isLastSibling: true,
  488. continuingTreeDepths: [],
  489. showEmbeddedChildren: false,
  490. toggleEmbeddedChildren: expect.any(Function),
  491. fetchEmbeddedChildrenState: 'idle',
  492. toggleNestedSpanGroup: undefined,
  493. toggleSiblingSpanGroup: undefined,
  494. isEmbeddedTransactionTimeAdjusted: false,
  495. },
  496. ],
  497. isNestedSpanGroupExpanded: false,
  498. toggleNestedSpanGroup: expect.any(Function),
  499. toggleSiblingSpanGroup: undefined,
  500. },
  501. {
  502. type: 'span',
  503. span: {
  504. timestamp: 1622079938.32451,
  505. start_timestamp: 1622079938.31431,
  506. description: 'fonts3.css',
  507. op: 'fonts',
  508. span_id: 'b5795cf4ba68bbb7',
  509. parent_span_id: 'b5795cf4ba68bbb6',
  510. trace_id: '8cbbc19c0f54447ab702f00263262726',
  511. data: {
  512. 'Decoded Body Size': 159248,
  513. 'Encoded Body Size': 159248,
  514. 'Transfer Size': 275,
  515. },
  516. },
  517. numOfSpanChildren: 0,
  518. treeDepth: 3,
  519. isLastSibling: true,
  520. continuingTreeDepths: [],
  521. showEmbeddedChildren: false,
  522. toggleEmbeddedChildren: expect.any(Function),
  523. fetchEmbeddedChildrenState: 'idle',
  524. toggleNestedSpanGroup: undefined,
  525. toggleSiblingSpanGroup: undefined,
  526. isEmbeddedTransactionTimeAdjusted: false,
  527. },
  528. ];
  529. it('isEvent', () => {
  530. const waterfallModel = new WaterfallModel(event);
  531. expect(waterfallModel.event).toMatchObject(event);
  532. expect(waterfallModel.isEvent(event)).toBe(true);
  533. expect(
  534. waterfallModel.isEvent({
  535. ...event,
  536. id: 'somethingelse',
  537. })
  538. ).toBe(false);
  539. });
  540. it('get operationNameCounts', () => {
  541. const waterfallModel = new WaterfallModel(event);
  542. expect(Object.fromEntries(waterfallModel.operationNameCounts)).toMatchObject({
  543. http: 2,
  544. pageload: 1,
  545. 'resource.link': 1,
  546. });
  547. });
  548. it('toggleOperationNameFilter', () => {
  549. const waterfallModel = new WaterfallModel(event);
  550. expect(waterfallModel.operationNameFilters).toEqual(noFilter);
  551. // toggle http filter
  552. waterfallModel.toggleOperationNameFilter('http');
  553. const operationNameFilters = waterfallModel.operationNameFilters as ActiveFilter;
  554. expect(operationNameFilters.type).toBe('active_filter');
  555. expect(Array.from(operationNameFilters.operationNames)).toEqual(['http']);
  556. // un-toggle http filter
  557. waterfallModel.toggleOperationNameFilter('http');
  558. expect(waterfallModel.operationNameFilters).toEqual(noFilter);
  559. });
  560. it('toggleAllOperationNameFilters', () => {
  561. const waterfallModel = new WaterfallModel(event);
  562. expect(waterfallModel.operationNameFilters).toEqual(noFilter);
  563. // toggle all operation names
  564. waterfallModel.toggleAllOperationNameFilters();
  565. let operationNameFilters = waterfallModel.operationNameFilters as ActiveFilter;
  566. expect(operationNameFilters.type).toBe('active_filter');
  567. expect(Array.from(operationNameFilters.operationNames)).toEqual([
  568. 'css',
  569. 'fonts',
  570. 'http',
  571. 'pageload',
  572. 'resource.link',
  573. ]);
  574. // toggle http filter
  575. waterfallModel.toggleOperationNameFilter('http');
  576. operationNameFilters = waterfallModel.operationNameFilters as ActiveFilter;
  577. expect(operationNameFilters.type).toBe('active_filter');
  578. expect(Array.from(operationNameFilters.operationNames)).toEqual([
  579. 'css',
  580. 'fonts',
  581. 'pageload',
  582. 'resource.link',
  583. ]);
  584. // toggle all operation names; expect un-toggled operation names to be toggled on
  585. waterfallModel.toggleAllOperationNameFilters();
  586. operationNameFilters = waterfallModel.operationNameFilters as ActiveFilter;
  587. expect(operationNameFilters.type).toBe('active_filter');
  588. expect(Array.from(operationNameFilters.operationNames)).toEqual([
  589. 'css',
  590. 'fonts',
  591. 'http',
  592. 'pageload',
  593. 'resource.link',
  594. ]);
  595. // un-toggle all operation names
  596. waterfallModel.toggleAllOperationNameFilters();
  597. expect(waterfallModel.operationNameFilters).toEqual(noFilter);
  598. });
  599. it('querySpanSearch', async () => {
  600. const waterfallModel = new WaterfallModel(event);
  601. expect(waterfallModel.fuse).toBe(undefined);
  602. // Fuzzy search needs to be loaded asynchronously
  603. await tick();
  604. // expect fuse index to be created
  605. expect(waterfallModel.fuse).not.toBe(undefined);
  606. expect(waterfallModel.filterSpans).toBe(undefined);
  607. expect(waterfallModel.searchQuery).toBe(undefined);
  608. waterfallModel.querySpanSearch('GET /api/0/organizations/?member=1');
  609. expect(Array.from(waterfallModel.filterSpans!.spanIDs).sort()).toEqual(
  610. ['a453cc713e5baf9c', 'b23703998ae619e7'].sort()
  611. );
  612. expect(waterfallModel.searchQuery).toBe('GET /api/0/organizations/?member=1');
  613. });
  614. it('getWaterfall()', async () => {
  615. const waterfallModel = new WaterfallModel(event);
  616. // Fuzzy search needs to be loaded asynchronously
  617. await tick();
  618. // show all spans in the waterfall
  619. let spans = waterfallModel.getWaterfall({
  620. viewStart: 0,
  621. viewEnd: 1,
  622. });
  623. expect(spans).toEqual(fullWaterfall);
  624. // perform a window selection
  625. spans = waterfallModel.getWaterfall({
  626. viewStart: 0.4,
  627. viewEnd: 0.65,
  628. });
  629. let expected = [...fullWaterfall];
  630. expected[1] = {
  631. type: 'out_of_view',
  632. span: fullWaterfall[1].span,
  633. } as EnhancedProcessedSpanType;
  634. expected[4] = {
  635. type: 'out_of_view',
  636. span: fullWaterfall[4].span,
  637. } as EnhancedProcessedSpanType;
  638. expect(spans).toEqual(expected);
  639. // toggle http filter with a window selection
  640. waterfallModel.toggleOperationNameFilter('http');
  641. spans = waterfallModel.getWaterfall({
  642. viewStart: 0.4,
  643. viewEnd: 0.65,
  644. });
  645. assert(
  646. fullWaterfall[10].type === 'span_group_chain' &&
  647. fullWaterfall[10].spanNestedGrouping
  648. );
  649. expected = [
  650. {
  651. type: 'filtered_out',
  652. span: fullWaterfall[0].span,
  653. },
  654. {
  655. type: 'out_of_view',
  656. span: fullWaterfall[1].span,
  657. },
  658. fullWaterfall[2],
  659. fullWaterfall[3],
  660. {
  661. type: 'filtered_out',
  662. span: fullWaterfall[4].span,
  663. },
  664. {
  665. type: 'filtered_out',
  666. span: fullWaterfall[5].span,
  667. },
  668. {
  669. type: 'filtered_out',
  670. span: fullWaterfall[6].span,
  671. },
  672. {
  673. type: 'filtered_out',
  674. span: fullWaterfall[7].span,
  675. },
  676. {
  677. type: 'filtered_out',
  678. span: fullWaterfall[9].span,
  679. },
  680. {
  681. type: 'filtered_out',
  682. span: fullWaterfall[10].spanNestedGrouping[0].span,
  683. },
  684. {
  685. type: 'filtered_out',
  686. span: fullWaterfall[10].spanNestedGrouping[1].span,
  687. },
  688. {
  689. type: 'filtered_out',
  690. span: fullWaterfall[11].span,
  691. },
  692. ] as EnhancedProcessedSpanType[];
  693. expect(spans).toEqual(expected);
  694. // toggle ops filters with a window selection and search query
  695. // NOTE: http was toggled on in the previous case
  696. waterfallModel.toggleOperationNameFilter('pageload');
  697. waterfallModel.querySpanSearch('a453cc713e5baf9c');
  698. expect(waterfallModel.searchQuery).toBe('a453cc713e5baf9c');
  699. spans = waterfallModel.getWaterfall({
  700. viewStart: 0.2,
  701. viewEnd: 0.65,
  702. });
  703. expected[1].type = 'filtered_out';
  704. expect(spans).toEqual(expected);
  705. });
  706. it('toggleSpanSubTree()', () => {
  707. const waterfallModel = new WaterfallModel(event);
  708. let spans = waterfallModel.getWaterfall({
  709. viewStart: 0,
  710. viewEnd: 1,
  711. });
  712. expect(spans).toEqual(fullWaterfall);
  713. // toggle a span to hide their sub-tree
  714. waterfallModel.toggleSpanSubTree('a453cc713e5baf9c');
  715. spans = waterfallModel.getWaterfall({
  716. viewStart: 0,
  717. viewEnd: 1,
  718. });
  719. expect(spans).toEqual(
  720. fullWaterfall.filter((_span, index) => {
  721. // 5th through 8th spans should be hidden
  722. return !(index >= 4 && index <= 7);
  723. })
  724. );
  725. // toggle a span to reveal their sub-tree
  726. waterfallModel.toggleSpanSubTree('a453cc713e5baf9c');
  727. spans = waterfallModel.getWaterfall({
  728. viewStart: 0,
  729. viewEnd: 1,
  730. });
  731. expect(spans).toEqual(fullWaterfall);
  732. });
  733. it('span grouping - only child parent-child chain - root is not grouped', () => {
  734. const event2 = {
  735. ...event,
  736. entries: [],
  737. };
  738. const waterfallModel = new WaterfallModel(event2);
  739. const spans = waterfallModel.getWaterfall({
  740. viewStart: 0,
  741. viewEnd: 1,
  742. });
  743. expect(spans).toEqual([
  744. {
  745. ...fullWaterfall[0],
  746. numOfSpanChildren: 0,
  747. toggleNestedSpanGroup: undefined,
  748. },
  749. ]);
  750. });
  751. it('span grouping - only child parent-child chain - root span and a span (2 spans) are not grouped', () => {
  752. const event2: EventTransaction = {
  753. ...event,
  754. entries: [
  755. {
  756. data: [event.entries[0].data[0]],
  757. type: EntryType.SPANS,
  758. },
  759. ],
  760. };
  761. const waterfallModel = new WaterfallModel(event2);
  762. const spans = waterfallModel.getWaterfall({
  763. viewStart: 0,
  764. viewEnd: 1,
  765. });
  766. expect(spans).toEqual([
  767. {
  768. ...fullWaterfall[0],
  769. numOfSpanChildren: 1,
  770. toggleNestedSpanGroup: undefined,
  771. },
  772. {
  773. ...fullWaterfall[1],
  774. isLastSibling: true,
  775. numOfSpanChildren: 0,
  776. toggleNestedSpanGroup: undefined,
  777. },
  778. ]);
  779. });
  780. it('span grouping - only child parent-child chain - root span and 2 spans (3 spans) are not grouped', () => {
  781. const event2: EventTransaction = {
  782. ...event,
  783. entries: [
  784. {
  785. data: [
  786. event.entries[0].data[0],
  787. {
  788. ...event.entries[0].data[0],
  789. parent_span_id: event.entries[0].data[0].span_id,
  790. span_id: 'foo',
  791. },
  792. ],
  793. type: EntryType.SPANS,
  794. },
  795. ],
  796. };
  797. const waterfallModel = new WaterfallModel(event2);
  798. const spans = waterfallModel.getWaterfall({
  799. viewStart: 0,
  800. viewEnd: 1,
  801. });
  802. expect(spans).toEqual([
  803. {
  804. ...fullWaterfall[0],
  805. treeDepth: 0,
  806. numOfSpanChildren: 1,
  807. toggleNestedSpanGroup: undefined,
  808. },
  809. {
  810. ...fullWaterfall[1],
  811. treeDepth: 1,
  812. isLastSibling: true,
  813. numOfSpanChildren: 1,
  814. toggleNestedSpanGroup: undefined,
  815. },
  816. {
  817. ...fullWaterfall[1],
  818. span: {
  819. ...fullWaterfall[1].span,
  820. parent_span_id: event.entries[0].data[0].span_id,
  821. span_id: 'foo',
  822. },
  823. treeDepth: 2,
  824. isLastSibling: true,
  825. numOfSpanChildren: 0,
  826. toggleNestedSpanGroup: undefined,
  827. },
  828. ]);
  829. });
  830. it('span grouping - only child parent-child chain - root span and 3+ spans (4 spans) are not grouped', () => {
  831. const event2: EventTransaction = {
  832. ...event,
  833. entries: [
  834. {
  835. data: [
  836. event.entries[0].data[0],
  837. {
  838. ...event.entries[0].data[0],
  839. parent_span_id: event.entries[0].data[0].span_id,
  840. span_id: 'foo',
  841. },
  842. {
  843. ...event.entries[0].data[0],
  844. parent_span_id: 'foo',
  845. span_id: 'bar',
  846. },
  847. ],
  848. type: EntryType.SPANS,
  849. },
  850. ],
  851. };
  852. const waterfallModel = new WaterfallModel(event2);
  853. let spans = waterfallModel.getWaterfall({
  854. viewStart: 0,
  855. viewEnd: 1,
  856. });
  857. // expect 1 or more spans are grouped
  858. expect(spans).toHaveLength(3);
  859. assert(fullWaterfall[1].type === 'span');
  860. const collapsedWaterfallExpected = [
  861. {
  862. ...fullWaterfall[0],
  863. numOfSpanChildren: 1,
  864. toggleNestedSpanGroup: undefined,
  865. },
  866. {
  867. type: 'span_group_chain',
  868. treeDepth: 1,
  869. continuingTreeDepths: fullWaterfall[1].continuingTreeDepths,
  870. span: {
  871. ...fullWaterfall[1].span,
  872. parent_span_id: 'foo',
  873. span_id: 'bar',
  874. },
  875. spanNestedGrouping: [
  876. {
  877. ...fullWaterfall[1],
  878. isLastSibling: true,
  879. numOfSpanChildren: 1,
  880. toggleNestedSpanGroup: undefined,
  881. },
  882. {
  883. ...fullWaterfall[1],
  884. span: {
  885. ...fullWaterfall[1].span,
  886. parent_span_id: event.entries[0].data[0].span_id,
  887. span_id: 'foo',
  888. },
  889. isLastSibling: true,
  890. numOfSpanChildren: 1,
  891. toggleNestedSpanGroup: undefined,
  892. },
  893. ],
  894. isNestedSpanGroupExpanded: false,
  895. toggleNestedSpanGroup: expect.any(Function),
  896. },
  897. {
  898. ...fullWaterfall[1],
  899. span: {
  900. ...fullWaterfall[1].span,
  901. parent_span_id: 'foo',
  902. span_id: 'bar',
  903. },
  904. isLastSibling: true,
  905. numOfSpanChildren: 0,
  906. treeDepth: 2,
  907. toggleNestedSpanGroup: undefined,
  908. },
  909. ];
  910. expect(spans).toEqual(collapsedWaterfallExpected);
  911. // Expand span group
  912. assert(spans[1].type === 'span' && spans[1].toggleNestedSpanGroup);
  913. spans[1].toggleNestedSpanGroup();
  914. spans = waterfallModel.getWaterfall({
  915. viewStart: 0,
  916. viewEnd: 1,
  917. });
  918. // expect span group to be expanded
  919. expect(spans).toHaveLength(4);
  920. expect(spans).toEqual([
  921. {
  922. ...fullWaterfall[0],
  923. numOfSpanChildren: 1,
  924. treeDepth: 0,
  925. toggleNestedSpanGroup: undefined,
  926. },
  927. {
  928. ...fullWaterfall[1],
  929. isLastSibling: true,
  930. numOfSpanChildren: 1,
  931. treeDepth: 1,
  932. toggleNestedSpanGroup: expect.any(Function),
  933. },
  934. {
  935. ...fullWaterfall[1],
  936. span: {
  937. ...fullWaterfall[1].span,
  938. parent_span_id: event.entries[0].data[0].span_id,
  939. span_id: 'foo',
  940. },
  941. isLastSibling: true,
  942. numOfSpanChildren: 1,
  943. treeDepth: 2,
  944. toggleNestedSpanGroup: undefined,
  945. },
  946. {
  947. ...fullWaterfall[1],
  948. span: {
  949. ...fullWaterfall[1].span,
  950. parent_span_id: 'foo',
  951. span_id: 'bar',
  952. },
  953. isLastSibling: true,
  954. numOfSpanChildren: 0,
  955. treeDepth: 3,
  956. toggleNestedSpanGroup: undefined,
  957. },
  958. ]);
  959. // Collapse span group
  960. assert(spans[1].type === 'span' && spans[1].toggleNestedSpanGroup);
  961. spans[1].toggleNestedSpanGroup();
  962. spans = waterfallModel.getWaterfall({
  963. viewStart: 0,
  964. viewEnd: 1,
  965. });
  966. expect(spans).toHaveLength(3);
  967. expect(spans).toEqual(collapsedWaterfallExpected);
  968. });
  969. });