replayDataUtils.spec.tsx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import {Event} from 'fixtures/js-stubs/event';
  2. import {
  3. breadcrumbFactory,
  4. isMemorySpan,
  5. isNetworkSpan,
  6. // breadcrumbValuesFromEvents,
  7. rrwebEventListFactory,
  8. // spanDataFromEvents,
  9. // spanEntryFactory,
  10. } from 'sentry/utils/replays/replayDataUtils';
  11. import type {ReplayRecord, ReplaySpan} from 'sentry/views/replays/types';
  12. function createSpan(extra: {
  13. op: string;
  14. data?: Record<string, any>;
  15. description?: string;
  16. }): ReplaySpan<Record<string, any>> {
  17. return {
  18. data: {},
  19. endTimestamp: 2,
  20. id: '0',
  21. startTimestamp: 1,
  22. timestamp: 1,
  23. ...extra,
  24. };
  25. }
  26. const fooSpan = createSpan({
  27. op: 'foo',
  28. data: {},
  29. });
  30. const lcpSpan = createSpan({
  31. op: 'largest-contentful-paint',
  32. data: {
  33. nodeId: 2,
  34. },
  35. });
  36. const navigateSpan = createSpan({
  37. op: 'navigation.navigate',
  38. description: 'http://test.com',
  39. });
  40. const cssSpan = createSpan({
  41. op: 'resource.css',
  42. description: 'http://test.com/static/media/glyphicons-halflings-regular.448c34a5.woff2',
  43. });
  44. const memorySpan = createSpan({
  45. op: 'memory',
  46. description: 'memory',
  47. data: {
  48. jsHeapSizeLimit: 4294705152,
  49. totalJSHeapSize: 19203353,
  50. usedJSHeapSize: 16119217,
  51. },
  52. });
  53. describe('breadcrumbFactory', () => {
  54. it('adds LCP as a breadcrumb', () => {
  55. const rawSpans = [fooSpan, lcpSpan];
  56. const results = breadcrumbFactory(
  57. Event({
  58. startedAt: new Date(0),
  59. }),
  60. [],
  61. [],
  62. rawSpans
  63. );
  64. expect(results).toMatchInlineSnapshot(`
  65. [
  66. {
  67. "color": "gray300",
  68. "data": {
  69. "action": "replay-init",
  70. "label": "Start recording",
  71. "url": undefined,
  72. },
  73. "description": "Default",
  74. "id": 0,
  75. "level": "info",
  76. "message": undefined,
  77. "timestamp": "1970-01-01T00:00:00.000Z",
  78. "type": "init",
  79. },
  80. {
  81. "category": "default",
  82. "color": "purple300",
  83. "data": {
  84. "action": "largest-contentful-paint",
  85. "label": "LCP",
  86. "nodeId": 2,
  87. },
  88. "description": "Debug",
  89. "id": 1,
  90. "level": "info",
  91. "timestamp": "1970-01-01T00:00:01.000Z",
  92. "type": "debug",
  93. },
  94. ]
  95. `);
  96. });
  97. it('adds navigation as a breadcrumb', () => {
  98. const rawSpans = [fooSpan, navigateSpan];
  99. const results = breadcrumbFactory(
  100. Event({
  101. startedAt: new Date(0),
  102. }),
  103. [],
  104. [],
  105. rawSpans
  106. );
  107. expect(results).toMatchInlineSnapshot(`
  108. [
  109. {
  110. "action": "navigate",
  111. "category": "default",
  112. "color": "green300",
  113. "data": {
  114. "label": "Page load",
  115. "to": "http://test.com",
  116. },
  117. "description": "Navigation",
  118. "id": 0,
  119. "level": "info",
  120. "message": "http://test.com",
  121. "timestamp": "1970-01-01T00:00:01.000Z",
  122. "type": "navigation",
  123. },
  124. ]
  125. `);
  126. });
  127. });
  128. describe('isMemorySpan', () => {
  129. it('should identify memory spans by the op field', () => {
  130. expect(isMemorySpan(memorySpan)).toBeTruthy();
  131. });
  132. it('should reject spans which are not op=memory', () => {
  133. expect(isMemorySpan(cssSpan)).toBeFalsy();
  134. expect(isMemorySpan(fooSpan)).toBeFalsy();
  135. expect(isMemorySpan(lcpSpan)).toBeFalsy();
  136. expect(isMemorySpan(navigateSpan)).toBeFalsy();
  137. });
  138. });
  139. describe('isNetworkSpan', () => {
  140. it('should identify network spans by the op field', () => {
  141. expect(isNetworkSpan(cssSpan)).toBeTruthy();
  142. expect(isNetworkSpan(navigateSpan)).toBeTruthy();
  143. });
  144. it('should reject spans which are not some kind of op=navigation', () => {
  145. expect(isNetworkSpan(fooSpan)).toBeFalsy();
  146. expect(isNetworkSpan(lcpSpan)).toBeFalsy();
  147. expect(isNetworkSpan(memorySpan)).toBeFalsy();
  148. });
  149. });
  150. describe('rrwebEventListFactory', () => {
  151. it('returns a list of replay events for highlights', function () {
  152. const replayRecord = {
  153. startedAt: new Date(13),
  154. finishedAt: new Date(213),
  155. } as ReplayRecord;
  156. const results = rrwebEventListFactory(replayRecord, []);
  157. expect(results).toMatchInlineSnapshot(`
  158. [
  159. {
  160. "data": {
  161. "tag": "replay-end",
  162. },
  163. "timestamp": 13,
  164. "type": 5,
  165. },
  166. ]
  167. `);
  168. });
  169. it('merges and sorts rrweb-events and span data', function () {
  170. const startTimestampMs = 0;
  171. const endTimestampMs = 10_000;
  172. const replayRecord = {
  173. startedAt: new Date(startTimestampMs),
  174. finishedAt: new Date(endTimestampMs),
  175. } as ReplayRecord;
  176. expect(
  177. rrwebEventListFactory(replayRecord, [
  178. {type: 0, timestamp: 5_000, data: {}},
  179. {type: 1, timestamp: 1_000, data: {}},
  180. {type: 2, timestamp: 3_000, data: {}},
  181. ])
  182. ).toEqual([
  183. {type: 1, timestamp: 0, data: {}},
  184. {type: 2, timestamp: 3_000, data: {}},
  185. {type: 0, timestamp: 5_000, data: {}},
  186. {type: 5, timestamp: 10_000, data: {tag: 'replay-end'}},
  187. ]);
  188. });
  189. });