|
1 | | -import { getCurrentHub } from '@sentry/core'; |
| 1 | +import { getCurrentHub, Hub } from '@sentry/core'; |
| 2 | +import { Event, Scope } from '@sentry/types'; |
2 | 3 | import { EventType } from 'rrweb'; |
3 | 4 |
|
4 | 5 | import { MAX_SESSION_LIFE, REPLAY_SESSION_KEY, VISIBILITY_CHANGE_TIMEOUT, WINDOW } from '../../src/constants'; |
@@ -741,54 +742,6 @@ describe('Replay', () => { |
741 | 742 | }); |
742 | 743 | }); |
743 | 744 |
|
744 | | - // TODO: ... this doesn't really test anything anymore since replay event and recording are sent in the same envelope |
745 | | - it('does not create replay event if recording upload completely fails', async () => { |
746 | | - const TEST_EVENT = { data: {}, timestamp: BASE_TIMESTAMP, type: 3 }; |
747 | | - // Suppress console.errors |
748 | | - const mockConsole = jest.spyOn(console, 'error').mockImplementation(jest.fn()); |
749 | | - // fail the first and second requests and pass the third one |
750 | | - mockSendReplayRequest.mockImplementationOnce(() => { |
751 | | - throw new Error('Something bad happened'); |
752 | | - }); |
753 | | - mockRecord._emitter(TEST_EVENT); |
754 | | - |
755 | | - await advanceTimers(5000); |
756 | | - |
757 | | - expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); |
758 | | - |
759 | | - // Reset console.error mock to minimize the amount of time we are hiding |
760 | | - // console messages in case an error happens after |
761 | | - mockConsole.mockClear(); |
762 | | - expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); |
763 | | - |
764 | | - mockSendReplayRequest.mockImplementationOnce(() => { |
765 | | - throw new Error('Something bad happened'); |
766 | | - }); |
767 | | - await advanceTimers(5000); |
768 | | - expect(replay.sendReplayRequest).toHaveBeenCalledTimes(2); |
769 | | - |
770 | | - // next tick should retry and fail |
771 | | - mockConsole.mockClear(); |
772 | | - |
773 | | - mockSendReplayRequest.mockImplementationOnce(() => { |
774 | | - throw new Error('Something bad happened'); |
775 | | - }); |
776 | | - await advanceTimers(10000); |
777 | | - expect(replay.sendReplayRequest).toHaveBeenCalledTimes(3); |
778 | | - |
779 | | - mockSendReplayRequest.mockImplementationOnce(() => { |
780 | | - throw new Error('Something bad happened'); |
781 | | - }); |
782 | | - await advanceTimers(30000); |
783 | | - expect(replay.sendReplayRequest).toHaveBeenCalledTimes(4); |
784 | | - |
785 | | - // No activity has occurred, session's last activity should remain the same |
786 | | - expect(replay.session?.lastActivity).toBeGreaterThanOrEqual(BASE_TIMESTAMP); |
787 | | - expect(replay.session?.segmentId).toBe(1); |
788 | | - |
789 | | - // TODO: Recording should stop and next event should do nothing |
790 | | - }); |
791 | | - |
792 | 745 | it('has correct timestamps when there events earlier than initial timestamp', async function () { |
793 | 746 | replay.clearSession(); |
794 | 747 | replay.loadSession({ expiry: 0 }); |
@@ -914,3 +867,77 @@ describe('Replay', () => { |
914 | 867 | expect(replay.flush).toHaveBeenCalledTimes(1); |
915 | 868 | }); |
916 | 869 | }); |
| 870 | + |
| 871 | +describe('eventProcessors', () => { |
| 872 | + let hub: Hub; |
| 873 | + let scope: Scope; |
| 874 | + |
| 875 | + beforeEach(() => { |
| 876 | + hub = getCurrentHub(); |
| 877 | + scope = hub.pushScope(); |
| 878 | + }); |
| 879 | + |
| 880 | + afterEach(() => { |
| 881 | + hub.popScope(); |
| 882 | + jest.resetAllMocks(); |
| 883 | + }); |
| 884 | + |
| 885 | + it('handles event processors properly', async () => { |
| 886 | + const MUTATED_TIMESTAMP = BASE_TIMESTAMP + 3000; |
| 887 | + |
| 888 | + const { mockRecord } = await resetSdkMock({ |
| 889 | + replayOptions: { |
| 890 | + stickySession: false, |
| 891 | + }, |
| 892 | + }); |
| 893 | + |
| 894 | + const client = hub.getClient()!; |
| 895 | + |
| 896 | + jest.runAllTimers(); |
| 897 | + const mockTransportSend = jest.spyOn(client.getTransport()!, 'send'); |
| 898 | + mockTransportSend.mockReset(); |
| 899 | + |
| 900 | + const handler1 = jest.fn((event: Event): Event | null => { |
| 901 | + event.timestamp = MUTATED_TIMESTAMP; |
| 902 | + |
| 903 | + return event; |
| 904 | + }); |
| 905 | + |
| 906 | + const handler2 = jest.fn((): Event | null => { |
| 907 | + return null; |
| 908 | + }); |
| 909 | + |
| 910 | + scope.addEventProcessor(handler1); |
| 911 | + |
| 912 | + const TEST_EVENT = { data: {}, timestamp: BASE_TIMESTAMP, type: 3 }; |
| 913 | + |
| 914 | + mockRecord._emitter(TEST_EVENT); |
| 915 | + jest.runAllTimers(); |
| 916 | + jest.advanceTimersByTime(1); |
| 917 | + await new Promise(process.nextTick); |
| 918 | + |
| 919 | + expect(mockTransportSend).toHaveBeenCalledTimes(1); |
| 920 | + |
| 921 | + scope.addEventProcessor(handler2); |
| 922 | + |
| 923 | + const TEST_EVENT2 = { data: {}, timestamp: BASE_TIMESTAMP, type: 3 }; |
| 924 | + |
| 925 | + mockRecord._emitter(TEST_EVENT2); |
| 926 | + jest.runAllTimers(); |
| 927 | + jest.advanceTimersByTime(1); |
| 928 | + await new Promise(process.nextTick); |
| 929 | + |
| 930 | + expect(mockTransportSend).toHaveBeenCalledTimes(1); |
| 931 | + |
| 932 | + expect(handler1).toHaveBeenCalledTimes(2); |
| 933 | + expect(handler2).toHaveBeenCalledTimes(1); |
| 934 | + |
| 935 | + // This receives an envelope, which is a deeply nested array |
| 936 | + // We only care about the fact that the timestamp was mutated |
| 937 | + expect(mockTransportSend).toHaveBeenCalledWith( |
| 938 | + expect.arrayContaining([ |
| 939 | + expect.arrayContaining([expect.arrayContaining([expect.objectContaining({ timestamp: MUTATED_TIMESTAMP })])]), |
| 940 | + ]), |
| 941 | + ); |
| 942 | + }); |
| 943 | +}); |
0 commit comments