messageFormatter.spec.tsx 8.0 KB

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