import {render, screen} from 'sentry-test/reactTestingLibrary'; import {BreadcrumbLevelType} from 'sentry/types/breadcrumbs'; import hydrateBreadcrumbs from 'sentry/utils/replays/hydrateBreadcrumbs'; import MessageFormatter from 'sentry/views/replays/detail/console/messageFormatter'; describe('MessageFormatter', () => { it('Should print console message with placeholders correctly', () => { const [frame] = hydrateBreadcrumbs(TestStubs.ReplayRecord(), [ TestStubs.Replay.ConsoleFrame({ data: { arguments: ['This is a %s', 'test'], logger: 'console', }, level: BreadcrumbLevelType.LOG, message: 'This is a %s test', timestamp: new Date('2022-06-22T20:00:39.959Z'), }), ]); render(); expect(screen.getByText('This is a test')).toBeInTheDocument(); }); it('Should print console message without data', () => { const [frame] = hydrateBreadcrumbs(TestStubs.ReplayRecord(), [ TestStubs.Replay.ConsoleFrame({ level: BreadcrumbLevelType.LOG, message: 'This is only a test', timestamp: new Date('2022-06-22T20:00:39.959Z'), }), ]); // Manually delete `data` from the mock. // This is reasonable because the type, at this point, `frame` is of type // `BreadcrumbFrame` and not `ConsoleFrame`. // When the type is narrowed to `ConsoleFrame` the `data` field is forced to exist. delete frame.data; render(); expect(screen.getByText('This is only a test')).toBeInTheDocument(); }); it('Should print console message with objects correctly', () => { const [frame] = hydrateBreadcrumbs(TestStubs.ReplayRecord(), [ TestStubs.Replay.ConsoleFrame({ data: { arguments: ['test', 1, false, {}], logger: 'console', }, level: BreadcrumbLevelType.LOG, message: 'test 1 false [object Object]', timestamp: new Date('2022-06-22T16:49:11.198Z'), }), ]); render(); expect(screen.getByText('test 1 false')).toBeInTheDocument(); expect(screen.getByText('{}')).toBeInTheDocument(); }); it('Should print console message correctly when it is an Error object', () => { const [frame] = hydrateBreadcrumbs(TestStubs.ReplayRecord(), [ TestStubs.Replay.ConsoleFrame({ data: { arguments: [{}], logger: 'console', }, level: BreadcrumbLevelType.ERROR, message: 'Error: this is my error message', timestamp: new Date('2022-06-22T20:00:39.958Z'), }), ]); render(); expect(screen.getByText('this is my error message')).toBeInTheDocument(); }); it('Should print empty object in case there is no message prop', () => { const [frame] = hydrateBreadcrumbs(TestStubs.ReplayRecord(), [ TestStubs.Replay.ConsoleFrame({ data: { arguments: [{}], logger: 'console', }, level: BreadcrumbLevelType.ERROR, timestamp: new Date('2022-06-22T20:00:39.958Z'), }), ]); render(); expect(screen.getByText('{}')).toBeInTheDocument(); }); it('Should ignore the "%c" placheholder and print the console message correctly', () => { const [frame] = hydrateBreadcrumbs(TestStubs.ReplayRecord(), [ TestStubs.Replay.ConsoleFrame({ data: { arguments: [ '%c prev state', 'color: #9E9E9E; font-weight: bold', { cart: [], }, ], logger: 'console', }, level: BreadcrumbLevelType.LOG, message: '%c prev state color: #9E9E9E; font-weight: bold [object Object]', timestamp: new Date('2022-06-09T00:50:25.273Z'), }), ]); render(); expect(screen.getByText(/%c prev state/)).toBeInTheDocument(); expect(screen.getByText('cart')).toBeInTheDocument(); expect(screen.getByText('Array(0)')).toBeInTheDocument(); }); it('Should print arrays correctly', () => { const [frame] = hydrateBreadcrumbs(TestStubs.ReplayRecord(), [ TestStubs.Replay.ConsoleFrame({ data: { arguments: ['test', ['foo', 'bar']], logger: 'console', }, level: BreadcrumbLevelType.LOG, message: 'test foo,bar', timestamp: new Date('2022-06-23T17:09:31.158Z'), }), ]); render(); expect(screen.getByText('test')).toBeInTheDocument(); expect(screen.getByText('(2)')).toBeInTheDocument(); // expect(screen.getByText('[')).toBeInTheDocument(); expect(screen.getByText('"foo"')).toBeInTheDocument(); expect(screen.getByText('"bar"')).toBeInTheDocument(); // expect(screen.getByText(']')).toBeInTheDocument(); }); it('Should print literal %', () => { const [frame] = hydrateBreadcrumbs(TestStubs.ReplayRecord(), [ TestStubs.Replay.ConsoleFrame({ data: { arguments: ['This is a literal 100%'], logger: 'console', }, level: BreadcrumbLevelType.LOG, message: 'This is a literal 100%', timestamp: new Date('2022-06-22T20:00:39.959Z'), }), ]); render(); expect(screen.getByText('This is a literal 100%')).toBeInTheDocument(); }); it('Should print unbound %s placeholder', () => { const [frame] = hydrateBreadcrumbs(TestStubs.ReplayRecord(), [ TestStubs.Replay.ConsoleFrame({ data: { arguments: ['Unbound placeholder %s'], logger: 'console', }, level: BreadcrumbLevelType.LOG, message: 'Unbound placeholder %s', timestamp: new Date('2022-06-22T20:00:39.959Z'), }), ]); render(); expect(screen.getByText('Unbound placeholder %s')).toBeInTheDocument(); }); it('Should print placeholder with literal %', () => { const [frame] = hydrateBreadcrumbs(TestStubs.ReplayRecord(), [ TestStubs.Replay.ConsoleFrame({ data: { arguments: ['Placeholder %s with 100%', 'myPlaceholder'], logger: 'console', }, level: BreadcrumbLevelType.LOG, message: 'Placeholder %s with 100%', timestamp: new Date('2022-06-22T20:00:39.959Z'), }), ]); render(); expect(screen.getByText('Placeholder myPlaceholder with 100%')).toBeInTheDocument(); }); });