listContent.spec.tsx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import {OrganizationFixture} from 'sentry-fixture/organization';
  2. import {RouterContextFixture} from 'sentry-fixture/routerContextFixture';
  3. import {render, screen, waitFor} from 'sentry-test/reactTestingLibrary';
  4. import type {Organization as TOrganization} from 'sentry/types';
  5. import useDeadRageSelectors from 'sentry/utils/replays/hooks/useDeadRageSelectors';
  6. import {
  7. useHaveSelectedProjectsSentAnyReplayEvents,
  8. useReplayOnboardingSidebarPanel,
  9. } from 'sentry/utils/replays/hooks/useReplayOnboarding';
  10. import useProjectSdkNeedsUpdate from 'sentry/utils/useProjectSdkNeedsUpdate';
  11. import ListPage from 'sentry/views/replays/list/listContent';
  12. jest.mock('sentry/utils/replays/hooks/useDeadRageSelectors');
  13. jest.mock('sentry/utils/replays/hooks/useReplayOnboarding');
  14. jest.mock('sentry/utils/replays/hooks/useReplayPageview');
  15. jest.mock('sentry/utils/useProjectSdkNeedsUpdate');
  16. const mockUseDeadRageSelectors = jest.mocked(useDeadRageSelectors);
  17. const mockUseHaveSelectedProjectsSentAnyReplayEvents = jest.mocked(
  18. useHaveSelectedProjectsSentAnyReplayEvents
  19. );
  20. const mockUseProjectSdkNeedsUpdate = jest.mocked(useProjectSdkNeedsUpdate);
  21. const mockUseReplayOnboardingSidebarPanel = jest.mocked(useReplayOnboardingSidebarPanel);
  22. mockUseReplayOnboardingSidebarPanel.mockReturnValue({activateSidebar: jest.fn()});
  23. const AM1_FEATURES = [];
  24. const AM2_FEATURES = ['session-replay'];
  25. function getMockOrganizationFixture({features}: {features: string[]}) {
  26. const mockOrg = OrganizationFixture({
  27. features,
  28. access: [],
  29. });
  30. return mockOrg;
  31. }
  32. function getMockContext(mockOrg: TOrganization) {
  33. return RouterContextFixture([{organization: mockOrg}]);
  34. }
  35. describe('ReplayList', () => {
  36. let mockFetchReplayListRequest;
  37. beforeEach(() => {
  38. mockUseHaveSelectedProjectsSentAnyReplayEvents.mockClear();
  39. mockUseProjectSdkNeedsUpdate.mockClear();
  40. mockUseDeadRageSelectors.mockClear();
  41. MockApiClient.clearMockResponses();
  42. MockApiClient.addMockResponse({
  43. url: '/organizations/org-slug/tags/',
  44. body: [],
  45. });
  46. mockFetchReplayListRequest = MockApiClient.addMockResponse({
  47. url: `/organizations/org-slug/replays/`,
  48. body: {},
  49. });
  50. });
  51. it('should render the onboarding panel when the org is on AM1', async () => {
  52. const mockOrg = getMockOrganizationFixture({features: AM1_FEATURES});
  53. mockUseHaveSelectedProjectsSentAnyReplayEvents.mockReturnValue({
  54. fetching: false,
  55. hasSentOneReplay: false,
  56. });
  57. mockUseProjectSdkNeedsUpdate.mockReturnValue({
  58. isError: false,
  59. isFetching: false,
  60. needsUpdate: false,
  61. });
  62. render(<ListPage />, {
  63. context: getMockContext(mockOrg),
  64. organization: mockOrg,
  65. });
  66. await waitFor(() =>
  67. expect(screen.getByText('Get to the root cause faster')).toBeInTheDocument()
  68. );
  69. expect(mockFetchReplayListRequest).not.toHaveBeenCalled();
  70. });
  71. it('should render the onboarding panel when the org is on AM1 and has sent some replays', async () => {
  72. const mockOrg = getMockOrganizationFixture({features: AM1_FEATURES});
  73. mockUseHaveSelectedProjectsSentAnyReplayEvents.mockReturnValue({
  74. fetching: false,
  75. hasSentOneReplay: true,
  76. });
  77. mockUseProjectSdkNeedsUpdate.mockReturnValue({
  78. isError: false,
  79. isFetching: false,
  80. needsUpdate: false,
  81. });
  82. render(<ListPage />, {
  83. context: getMockContext(mockOrg),
  84. organization: mockOrg,
  85. });
  86. await waitFor(() =>
  87. expect(screen.getByText('Get to the root cause faster')).toBeInTheDocument()
  88. );
  89. expect(mockFetchReplayListRequest).not.toHaveBeenCalled();
  90. });
  91. it('should render the onboarding panel when the org is on AM2 and has never sent a replay', async () => {
  92. const mockOrg = getMockOrganizationFixture({features: AM2_FEATURES});
  93. mockUseHaveSelectedProjectsSentAnyReplayEvents.mockReturnValue({
  94. fetching: false,
  95. hasSentOneReplay: false,
  96. });
  97. mockUseProjectSdkNeedsUpdate.mockReturnValue({
  98. isError: false,
  99. isFetching: false,
  100. needsUpdate: false,
  101. });
  102. render(<ListPage />, {
  103. context: getMockContext(mockOrg),
  104. organization: mockOrg,
  105. });
  106. await waitFor(() =>
  107. expect(screen.getByText('Get to the root cause faster')).toBeInTheDocument()
  108. );
  109. expect(mockFetchReplayListRequest).not.toHaveBeenCalled();
  110. });
  111. it('should render the rage-click sdk update banner when the org is AM2, has sent replays, but the sdk version is low', async () => {
  112. const mockOrg = getMockOrganizationFixture({features: AM2_FEATURES});
  113. mockUseHaveSelectedProjectsSentAnyReplayEvents.mockReturnValue({
  114. fetching: false,
  115. hasSentOneReplay: true,
  116. });
  117. mockUseProjectSdkNeedsUpdate.mockReturnValue({
  118. isError: false,
  119. isFetching: false,
  120. needsUpdate: true,
  121. });
  122. render(<ListPage />, {
  123. context: getMockContext(mockOrg),
  124. organization: mockOrg,
  125. });
  126. await waitFor(() => {
  127. expect(screen.queryByText('Introducing Rage and Dead Clicks')).toBeInTheDocument();
  128. expect(screen.queryByTestId('replay-table')).toBeInTheDocument();
  129. });
  130. expect(mockFetchReplayListRequest).toHaveBeenCalled();
  131. });
  132. it('should fetch the replay table and show selector tables when the org is on AM2, has sent some replays, and has a newer SDK version', async () => {
  133. const mockOrg = getMockOrganizationFixture({features: AM2_FEATURES});
  134. mockUseHaveSelectedProjectsSentAnyReplayEvents.mockReturnValue({
  135. fetching: false,
  136. hasSentOneReplay: true,
  137. });
  138. mockUseProjectSdkNeedsUpdate.mockReturnValue({
  139. isError: false,
  140. isFetching: false,
  141. needsUpdate: false,
  142. });
  143. mockUseDeadRageSelectors.mockReturnValue({
  144. isLoading: false,
  145. isError: false,
  146. data: [],
  147. pageLinks: undefined,
  148. });
  149. render(<ListPage />, {
  150. context: getMockContext(mockOrg),
  151. organization: mockOrg,
  152. });
  153. await waitFor(() => expect(screen.queryAllByTestId('replay-table')).toHaveLength(1));
  154. await waitFor(() =>
  155. expect(screen.queryAllByTestId('selector-widget')).toHaveLength(2)
  156. );
  157. expect(mockFetchReplayListRequest).toHaveBeenCalled();
  158. });
  159. });