messageFormatter.spec.tsx 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. import {ReplayConsoleFrameFixture} from 'sentry-fixture/replay/replayBreadcrumbFrameData';
  2. import {ReplayRecordFixture} from 'sentry-fixture/replayRecord';
  3. import {render, screen} from 'sentry-test/reactTestingLibrary';
  4. import {BreadcrumbLevelType} from 'sentry/types/breadcrumbs';
  5. import hydrateBreadcrumbs from 'sentry/utils/replays/hydrateBreadcrumbs';
  6. import MessageFormatter from 'sentry/views/replays/detail/console/messageFormatter';
  7. describe('MessageFormatter', () => {
  8. it('Should print console message with placeholders correctly', () => {
  9. const [frame] = hydrateBreadcrumbs(ReplayRecordFixture(), [
  10. ReplayConsoleFrameFixture({
  11. data: {
  12. arguments: ['This is a %s', 'test'],
  13. logger: 'console',
  14. },
  15. level: BreadcrumbLevelType.LOG,
  16. message: 'This is a %s test',
  17. timestamp: new Date('2022-06-22T20:00:39.959Z'),
  18. }),
  19. ]);
  20. render(<MessageFormatter frame={frame} />);
  21. expect(screen.getByText('This is a test')).toBeInTheDocument();
  22. });
  23. it('Should print console message without data', () => {
  24. const [frame] = hydrateBreadcrumbs(ReplayRecordFixture(), [
  25. ReplayConsoleFrameFixture({
  26. level: BreadcrumbLevelType.LOG,
  27. message: 'This is only a test',
  28. timestamp: new Date('2022-06-22T20:00:39.959Z'),
  29. }),
  30. ]);
  31. // Manually delete `data` from the mock.
  32. // This is reasonable because the type, at this point, `frame` is of type
  33. // `BreadcrumbFrame` and not `ConsoleFrame`.
  34. // When the type is narrowed to `ConsoleFrame` the `data` field is forced to exist.
  35. delete frame.data;
  36. render(<MessageFormatter frame={frame} />);
  37. expect(screen.getByText('This is only a test')).toBeInTheDocument();
  38. });
  39. it('Should print console message with objects correctly', () => {
  40. const [frame] = hydrateBreadcrumbs(ReplayRecordFixture(), [
  41. ReplayConsoleFrameFixture({
  42. data: {
  43. arguments: ['test', 1, false, {}],
  44. logger: 'console',
  45. },
  46. level: BreadcrumbLevelType.LOG,
  47. message: 'test 1 false [object Object]',
  48. timestamp: new Date('2022-06-22T16:49:11.198Z'),
  49. }),
  50. ]);
  51. render(<MessageFormatter frame={frame} />);
  52. expect(screen.getByText('test 1 false')).toBeInTheDocument();
  53. expect(screen.getByText('{}')).toBeInTheDocument();
  54. });
  55. it('Should print console message correctly when it is an Error object', () => {
  56. const [frame] = hydrateBreadcrumbs(ReplayRecordFixture(), [
  57. ReplayConsoleFrameFixture({
  58. data: {
  59. arguments: [{}],
  60. logger: 'console',
  61. },
  62. level: BreadcrumbLevelType.ERROR,
  63. message: 'Error: this is my error message',
  64. timestamp: new Date('2022-06-22T20:00:39.958Z'),
  65. }),
  66. ]);
  67. render(<MessageFormatter frame={frame} />);
  68. expect(screen.getByText('this is my error message')).toBeInTheDocument();
  69. });
  70. it('Should print empty object in case there is no message prop', () => {
  71. const [frame] = hydrateBreadcrumbs(ReplayRecordFixture(), [
  72. ReplayConsoleFrameFixture({
  73. data: {
  74. arguments: [{}],
  75. logger: 'console',
  76. },
  77. level: BreadcrumbLevelType.ERROR,
  78. timestamp: new Date('2022-06-22T20:00:39.958Z'),
  79. }),
  80. ]);
  81. render(<MessageFormatter frame={frame} />);
  82. expect(screen.getByText('{}')).toBeInTheDocument();
  83. });
  84. it('Should style "%c" placeholder and print the console message correctly', () => {
  85. const [frame] = hydrateBreadcrumbs(ReplayRecordFixture(), [
  86. ReplayConsoleFrameFixture({
  87. data: {
  88. arguments: [
  89. '%c prev state',
  90. 'color: #9E9E9E; font-weight: bold; background-image: url(foo);',
  91. {
  92. cart: [],
  93. },
  94. ],
  95. logger: 'console',
  96. },
  97. level: BreadcrumbLevelType.LOG,
  98. message:
  99. '%c prev state color: #9E9E9E; font-weight: bold; background-image: url(foo); [object Object]',
  100. timestamp: new Date('2022-06-09T00:50:25.273Z'),
  101. }),
  102. ]);
  103. render(<MessageFormatter frame={frame} />);
  104. const styledEl = screen.getByText('prev state');
  105. expect(styledEl).toBeInTheDocument();
  106. expect(styledEl).toHaveStyle('color: #9E9E9E;');
  107. expect(styledEl).toHaveStyle('font-weight: bold;');
  108. expect(styledEl).not.toHaveStyle('background-image: url(foo);');
  109. expect(screen.getByText('cart')).toBeInTheDocument();
  110. expect(screen.getByText('Array(0)')).toBeInTheDocument();
  111. });
  112. it('Should print arrays correctly', () => {
  113. const [frame] = hydrateBreadcrumbs(ReplayRecordFixture(), [
  114. ReplayConsoleFrameFixture({
  115. data: {
  116. arguments: ['test', ['foo', 'bar']],
  117. logger: 'console',
  118. },
  119. level: BreadcrumbLevelType.LOG,
  120. message: 'test foo,bar',
  121. timestamp: new Date('2022-06-23T17:09:31.158Z'),
  122. }),
  123. ]);
  124. render(<MessageFormatter frame={frame} />);
  125. expect(screen.getByText('test')).toBeInTheDocument();
  126. expect(screen.getByText('(2)')).toBeInTheDocument();
  127. // expect(screen.getByText('[')).toBeInTheDocument();
  128. expect(screen.getByText('"foo"')).toBeInTheDocument();
  129. expect(screen.getByText('"bar"')).toBeInTheDocument();
  130. // expect(screen.getByText(']')).toBeInTheDocument();
  131. });
  132. it('Should print literal %', () => {
  133. const [frame] = hydrateBreadcrumbs(ReplayRecordFixture(), [
  134. ReplayConsoleFrameFixture({
  135. data: {
  136. arguments: ['This is a literal 100%'],
  137. logger: 'console',
  138. },
  139. level: BreadcrumbLevelType.LOG,
  140. message: 'This is a literal 100%',
  141. timestamp: new Date('2022-06-22T20:00:39.959Z'),
  142. }),
  143. ]);
  144. render(<MessageFormatter frame={frame} />);
  145. expect(screen.getByText('This is a literal 100%')).toBeInTheDocument();
  146. });
  147. it('Should print unbound %s placeholder', () => {
  148. const [frame] = hydrateBreadcrumbs(ReplayRecordFixture(), [
  149. ReplayConsoleFrameFixture({
  150. data: {
  151. arguments: ['Unbound placeholder %s'],
  152. logger: 'console',
  153. },
  154. level: BreadcrumbLevelType.LOG,
  155. message: 'Unbound placeholder %s',
  156. timestamp: new Date('2022-06-22T20:00:39.959Z'),
  157. }),
  158. ]);
  159. render(<MessageFormatter frame={frame} />);
  160. expect(screen.getByText('Unbound placeholder %s')).toBeInTheDocument();
  161. });
  162. it('Should print placeholder with literal %', () => {
  163. const [frame] = hydrateBreadcrumbs(ReplayRecordFixture(), [
  164. ReplayConsoleFrameFixture({
  165. data: {
  166. arguments: ['Placeholder %s with 100%', 'myPlaceholder'],
  167. logger: 'console',
  168. },
  169. level: BreadcrumbLevelType.LOG,
  170. message: 'Placeholder %s with 100%',
  171. timestamp: new Date('2022-06-22T20:00:39.959Z'),
  172. }),
  173. ]);
  174. render(<MessageFormatter frame={frame} />);
  175. expect(screen.getByText('Placeholder myPlaceholder with 100%')).toBeInTheDocument();
  176. });
  177. it('should print non-console breadcrumbs', () => {
  178. const [frame] = hydrateBreadcrumbs(ReplayRecordFixture(), [
  179. {
  180. category: 'cypress',
  181. message: 'custom breadcrumb',
  182. timestamp: new Date('2022-06-22T20:00:39.959Z').getTime(),
  183. type: 'info',
  184. },
  185. ]);
  186. render(<MessageFormatter frame={frame} />);
  187. expect(screen.getByText('cypress custom breadcrumb')).toBeInTheDocument();
  188. });
  189. });