replayDataUtils.spec.tsx 4.9 KB

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