newIssueExperienceButton.spec.tsx 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. import {LocationFixture} from 'sentry-fixture/locationFixture';
  2. import {OrganizationFixture} from 'sentry-fixture/organization';
  3. import {UserFixture} from 'sentry-fixture/user';
  4. import {act, render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
  5. import ConfigStore from 'sentry/stores/configStore';
  6. import {trackAnalytics} from 'sentry/utils/analytics';
  7. import {NewIssueExperienceButton} from 'sentry/views/issueDetails/actions/newIssueExperienceButton';
  8. jest.mock('sentry/utils/analytics');
  9. const mockFeedbackForm = jest.fn();
  10. jest.mock('sentry/utils/useFeedbackForm', () => ({
  11. useFeedbackForm: () => mockFeedbackForm(),
  12. }));
  13. describe('NewIssueExperienceButton', function () {
  14. const organization = OrganizationFixture({features: ['issue-details-streamline']});
  15. const user = UserFixture();
  16. user.options.prefersIssueDetailsStreamlinedUI = true;
  17. const location = LocationFixture({query: {streamline: '1'}});
  18. beforeEach(() => {
  19. ConfigStore.init();
  20. jest.clearAllMocks();
  21. });
  22. it('does not appear by default', function () {
  23. render(
  24. <div data-test-id="test-id">
  25. <NewIssueExperienceButton />
  26. </div>
  27. );
  28. expect(screen.getByTestId('test-id')).toBeEmptyDOMElement();
  29. });
  30. it('does not appear when an organization has the single interface option', function () {
  31. const {unmount: unmountOptionTrue} = render(
  32. <div data-test-id="test-id">
  33. <NewIssueExperienceButton />
  34. </div>,
  35. {
  36. organization: {
  37. ...organization,
  38. streamlineOnly: true,
  39. },
  40. }
  41. );
  42. expect(screen.getByTestId('test-id')).toBeEmptyDOMElement();
  43. unmountOptionTrue();
  44. const {unmount: unmountOptionFalse} = render(
  45. <div data-test-id="test-id">
  46. <NewIssueExperienceButton />
  47. </div>,
  48. {
  49. organization: {
  50. ...organization,
  51. streamlineOnly: false,
  52. },
  53. }
  54. );
  55. expect(screen.getByTestId('test-id')).toBeEmptyDOMElement();
  56. unmountOptionFalse();
  57. });
  58. it('does not appear when an organization has the enforce flag', function () {
  59. render(
  60. <div data-test-id="test-id">
  61. <NewIssueExperienceButton />
  62. </div>,
  63. {
  64. organization: {
  65. ...organization,
  66. features: [...organization.features, 'issue-details-streamline-enforce'],
  67. },
  68. }
  69. );
  70. expect(screen.getByTestId('test-id')).toBeEmptyDOMElement();
  71. });
  72. it('appears when organization has flag', function () {
  73. render(
  74. <div data-test-id="test-id">
  75. <NewIssueExperienceButton />
  76. </div>,
  77. {organization}
  78. );
  79. expect(screen.getByTestId('test-id')).not.toBeEmptyDOMElement();
  80. });
  81. it('does not appear even if user prefers this UI', function () {
  82. act(() => ConfigStore.set('user', user));
  83. render(
  84. <div data-test-id="test-id">
  85. <NewIssueExperienceButton />
  86. </div>
  87. );
  88. expect(screen.getByTestId('test-id')).toBeEmptyDOMElement();
  89. });
  90. it('does not appear when query param is set', function () {
  91. render(
  92. <div data-test-id="test-id">
  93. <NewIssueExperienceButton />
  94. </div>,
  95. {router: {location}}
  96. );
  97. expect(screen.getByTestId('test-id')).toBeEmptyDOMElement();
  98. });
  99. it('triggers changes to the user config and location', async function () {
  100. const mockChangeUserSettings = MockApiClient.addMockResponse({
  101. url: '/users/me/',
  102. method: 'PUT',
  103. });
  104. render(<NewIssueExperienceButton />, {organization});
  105. const button = screen.getByRole('button', {
  106. name: 'Switch to the new issue experience',
  107. });
  108. await userEvent.click(button);
  109. // Text should change immediately
  110. expect(
  111. screen.getByRole('button', {name: 'Switch to the old issue experience'})
  112. ).toBeInTheDocument();
  113. // User option should be saved
  114. await waitFor(() => {
  115. expect(mockChangeUserSettings).toHaveBeenCalledWith(
  116. '/users/me/',
  117. expect.objectContaining({
  118. data: {
  119. options: {
  120. prefersIssueDetailsStreamlinedUI: true,
  121. },
  122. },
  123. })
  124. );
  125. });
  126. expect(trackAnalytics).toHaveBeenCalledTimes(1);
  127. // Clicking again toggles it off
  128. await userEvent.click(button);
  129. // Old text should be back
  130. expect(
  131. screen.getByRole('button', {name: 'Switch to the new issue experience'})
  132. ).toBeInTheDocument();
  133. // And save the option as false
  134. await waitFor(() => {
  135. expect(mockChangeUserSettings).toHaveBeenCalledWith(
  136. '/users/me/',
  137. expect.objectContaining({
  138. data: {
  139. options: {
  140. prefersIssueDetailsStreamlinedUI: false,
  141. },
  142. },
  143. })
  144. );
  145. });
  146. expect(trackAnalytics).toHaveBeenCalledTimes(2);
  147. });
  148. it('can switch back to the old UI via dropdown', async function () {
  149. const mockFormCallback = jest.fn();
  150. mockFeedbackForm.mockReturnValue(mockFormCallback);
  151. const mockChangeUserSettings = MockApiClient.addMockResponse({
  152. url: '/users/me/',
  153. method: 'PUT',
  154. });
  155. render(<NewIssueExperienceButton />, {organization});
  156. await userEvent.click(
  157. screen.getByRole('button', {
  158. name: 'Switch to the new issue experience',
  159. })
  160. );
  161. expect(
  162. screen.getByRole('button', {
  163. name: 'Switch issue experience',
  164. })
  165. ).toBeInTheDocument();
  166. const dropdownButton = screen.getByRole('button', {
  167. name: 'Switch issue experience',
  168. });
  169. await userEvent.click(dropdownButton);
  170. await userEvent.click(
  171. await screen.findByRole('menuitemradio', {name: 'Give feedback on new UI'})
  172. );
  173. expect(mockFeedbackForm).toHaveBeenCalled();
  174. await userEvent.click(dropdownButton);
  175. await userEvent.click(
  176. screen.getByRole('menuitemradio', {
  177. name: 'Switch to the old issue experience',
  178. })
  179. );
  180. expect(mockChangeUserSettings).toHaveBeenCalledTimes(2);
  181. });
  182. });