listContent.spec.tsx 6.6 KB

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