utils.spec.jsx 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. import {
  2. countColumns,
  3. divide,
  4. flattenSpans,
  5. formatTime,
  6. getCrumbsByColumn,
  7. relativeTimeInMs,
  8. showPlayerTime,
  9. } from 'sentry/components/replays/utils';
  10. const SECOND = 1000;
  11. describe('formatTime', () => {
  12. it.each([
  13. ['seconds', 15 * 1000, '0:15'],
  14. ['minutes', 2.5 * 60 * 1000, '2:30'],
  15. ['hours', 75 * 60 * 1000, '1:15:00'],
  16. ])('should format a %s long duration into a string', (_desc, duration, expected) => {
  17. expect(formatTime(duration)).toEqual(expected);
  18. });
  19. });
  20. describe('countColumns', () => {
  21. it('should divide 27s by 2700px to find twentyseven 1s columns, with some fraction remaining', () => {
  22. // 2700 allows for up to 27 columns at 100px wide.
  23. // That is what we'd need if we were to render at `1s` granularity, so we can.
  24. const width = 2700;
  25. const duration = 27 * SECOND;
  26. const minWidth = 100;
  27. const {timespan, cols, remaining} = countColumns(duration, width, minWidth);
  28. expect(timespan).toBe(1 * SECOND);
  29. expect(cols).toBe(27);
  30. expect(remaining).toBe(0);
  31. });
  32. it('should divide 27s by 2699px to find five 5s columns, with some fraction remaining', () => {
  33. // 2699px allows for up to 26 columns at 100px wide, with 99px leftover.
  34. // That is less than the 27 cols we'd need if we were to render at `1s` granularity.
  35. // So instead we get 5 cols (wider than 100px) at 5s granularity, and some extra space is remaining.
  36. const width = 2699;
  37. const duration = 27 * SECOND;
  38. const minWidth = 100;
  39. const {timespan, cols, remaining} = countColumns(duration, width, minWidth);
  40. expect(timespan).toBe(5 * SECOND);
  41. expect(cols).toBe(5);
  42. expect(remaining).toBe(0.4);
  43. });
  44. it('should divide 27s by 600px to find five 5s columns, with some fraction column remaining', () => {
  45. // 600px allows for 6 columns at 100px wide to fix within it
  46. // That allows us to get 5 cols (100px wide) at 5s granularity, and an extra 100px for the remainder
  47. const width = 600;
  48. const duration = 27 * SECOND;
  49. const minWidth = 100;
  50. const {timespan, cols, remaining} = countColumns(duration, width, minWidth);
  51. expect(timespan).toBe(5 * SECOND);
  52. expect(cols).toBe(5);
  53. expect(remaining).toBe(0.4);
  54. });
  55. it('should divide 27s by 599px to find five 2s columns, with some fraction column remaining', () => {
  56. // 599px allows for 5 columns at 100px wide, and 99px remaining.
  57. // That allows us to get 2 cols (100px wide) at 10s granularity, and an extra px for the remainder
  58. const width = 599;
  59. const duration = 27 * SECOND;
  60. const minWidth = 100;
  61. const {timespan, cols, remaining} = countColumns(duration, width, minWidth);
  62. expect(timespan).toBe(10 * SECOND);
  63. expect(cols).toBe(2);
  64. expect(remaining).toBe(0.7);
  65. });
  66. });
  67. describe('getCrumbsByColumn', () => {
  68. const startTimestamp = 1649945987.326; // seconds
  69. const duration = 25710; // milliseconds
  70. const CRUMB_1 = {timestamp: '2022-04-14T14:19:47.326000Z'};
  71. const CRUMB_2 = {timestamp: '2022-04-14T14:19:49.249000Z'};
  72. const CRUMB_3 = {timestamp: '2022-04-14T14:19:51.512000Z'};
  73. const CRUMB_4 = {timestamp: '2022-04-14T14:19:57.326000Z'};
  74. const CRUMB_5 = {timestamp: '2022-04-14T14:20:13.036000Z'};
  75. it('should return an empty list when no crumbs exist', () => {
  76. const columnCount = 3;
  77. const columns = getCrumbsByColumn(startTimestamp, duration, [], columnCount);
  78. const expectedEntries = [];
  79. expect(columns).toEqual(new Map(expectedEntries));
  80. });
  81. it('should put a crumbs in the first and last buckets', () => {
  82. const columnCount = 3;
  83. const columns = getCrumbsByColumn(
  84. startTimestamp,
  85. duration,
  86. [CRUMB_1, CRUMB_5],
  87. columnCount
  88. );
  89. const expectedEntries = [
  90. [1, [CRUMB_1]],
  91. [3, [CRUMB_5]],
  92. ];
  93. expect(columns).toEqual(new Map(expectedEntries));
  94. });
  95. it('should group crumbs by bucket', () => {
  96. // 6 columns gives is 5s granularity
  97. const columnCount = 6;
  98. const columns = getCrumbsByColumn(
  99. startTimestamp,
  100. duration,
  101. [CRUMB_1, CRUMB_2, CRUMB_3, CRUMB_4, CRUMB_5],
  102. columnCount
  103. );
  104. const expectedEntries = [
  105. [1, [CRUMB_1, CRUMB_2, CRUMB_3]],
  106. [2, [CRUMB_4]],
  107. [6, [CRUMB_5]],
  108. ];
  109. expect(columns).toEqual(new Map(expectedEntries));
  110. });
  111. });
  112. describe('flattenSpans', () => {
  113. it('should return an empty array if there ar eno spans', () => {
  114. expect(flattenSpans([])).toStrictEqual([]);
  115. });
  116. it('should return the FlattenedSpanRange for a single span', () => {
  117. const span = {
  118. data: {},
  119. span_id: 'abc',
  120. start_timestamp: 10,
  121. timestamp: 30,
  122. trace_id: '',
  123. };
  124. expect(flattenSpans([span])).toStrictEqual([
  125. {
  126. duration: 20000,
  127. endTimestamp: 30000,
  128. spanCount: 1,
  129. spanId: 'abc',
  130. startTimestamp: 10000,
  131. },
  132. ]);
  133. });
  134. it('should return two non-overlapping spans', () => {
  135. const span1 = {
  136. data: {},
  137. span_id: 'abc',
  138. start_timestamp: 10,
  139. timestamp: 30,
  140. trace_id: '',
  141. };
  142. const span2 = {
  143. data: {},
  144. span_id: 'yzx',
  145. start_timestamp: 60,
  146. timestamp: 90,
  147. trace_id: '',
  148. };
  149. expect(flattenSpans([span1, span2])).toStrictEqual([
  150. {
  151. duration: 20000,
  152. endTimestamp: 30000,
  153. spanCount: 1,
  154. spanId: 'abc',
  155. startTimestamp: 10000,
  156. },
  157. {
  158. duration: 30000,
  159. endTimestamp: 90000,
  160. spanCount: 1,
  161. spanId: 'yzx',
  162. startTimestamp: 60000,
  163. },
  164. ]);
  165. });
  166. it('should merge two overlapping spans', () => {
  167. const span1 = {
  168. data: {},
  169. span_id: 'abc',
  170. start_timestamp: 10,
  171. timestamp: 30,
  172. trace_id: '',
  173. };
  174. const span2 = {
  175. data: {},
  176. span_id: 'def',
  177. start_timestamp: 20,
  178. timestamp: 40,
  179. trace_id: '',
  180. };
  181. expect(flattenSpans([span1, span2])).toStrictEqual([
  182. {
  183. duration: 30000,
  184. endTimestamp: 40000,
  185. spanCount: 2,
  186. spanId: 'abc',
  187. startTimestamp: 10000,
  188. },
  189. ]);
  190. });
  191. it('should merge overlapping spans that are not first in the list', () => {
  192. const span0 = {
  193. data: {},
  194. span_id: 'aaa',
  195. start_timestamp: 0,
  196. timestamp: 1,
  197. trace_id: '',
  198. };
  199. const span1 = {
  200. data: {},
  201. span_id: 'abc',
  202. start_timestamp: 10,
  203. timestamp: 30,
  204. trace_id: '',
  205. };
  206. const span2 = {
  207. data: {},
  208. span_id: 'def',
  209. start_timestamp: 20,
  210. timestamp: 40,
  211. trace_id: '',
  212. };
  213. expect(flattenSpans([span0, span1, span2])).toStrictEqual([
  214. {
  215. duration: 1000,
  216. endTimestamp: 1000,
  217. spanCount: 1,
  218. spanId: 'aaa',
  219. startTimestamp: 0,
  220. },
  221. {
  222. duration: 30000,
  223. endTimestamp: 40000,
  224. spanCount: 2,
  225. spanId: 'abc',
  226. startTimestamp: 10000,
  227. },
  228. ]);
  229. });
  230. describe('relativeTimeinMs', () => {
  231. it('returns relative time in MS', () => {
  232. expect(relativeTimeInMs('2022-05-11T23:04:27.576000Z', 347.90000009536743)).toEqual(
  233. 1652309919676
  234. );
  235. });
  236. it('returns invalid date if date string is malformed', () => {
  237. expect(relativeTimeInMs('202223:04:27.576000Z', 347.90000009536743)).toEqual(NaN);
  238. });
  239. });
  240. describe('showPlayerTime', () => {
  241. it('returns time formatted for player', () => {
  242. expect(showPlayerTime('2022-05-11T23:04:27.576000Z', 1652309918.676)).toEqual(
  243. '00:05:48'
  244. );
  245. });
  246. it('returns invalid date if timestamp is malformed', () => {
  247. expect(showPlayerTime('20223:04:27.576000Z', 1652309918.676)).toEqual(
  248. 'Invalid date'
  249. );
  250. });
  251. });
  252. describe('divide', () => {
  253. it('divides numbers safely', () => {
  254. expect(divide(81, 9)).toEqual(9);
  255. });
  256. it('dividing by zero returns zero', () => {
  257. expect(divide(81, 0)).toEqual(0);
  258. });
  259. });
  260. });