newIssueExperienceButton.spec.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  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('appears when organization has flag', function () {
  31. render(
  32. <div data-test-id="test-id">
  33. <NewIssueExperienceButton />
  34. </div>,
  35. {organization}
  36. );
  37. expect(screen.getByTestId('test-id')).not.toBeEmptyDOMElement();
  38. });
  39. it('does not appear even if user prefers this UI', function () {
  40. act(() => ConfigStore.set('user', user));
  41. render(
  42. <div data-test-id="test-id">
  43. <NewIssueExperienceButton />
  44. </div>
  45. );
  46. expect(screen.getByTestId('test-id')).toBeEmptyDOMElement();
  47. });
  48. it('does not appear when query param is set', function () {
  49. render(
  50. <div data-test-id="test-id">
  51. <NewIssueExperienceButton />
  52. </div>,
  53. {router: {location}}
  54. );
  55. expect(screen.getByTestId('test-id')).toBeEmptyDOMElement();
  56. });
  57. it('triggers changes to the user config and location', async function () {
  58. const mockChangeUserSettings = MockApiClient.addMockResponse({
  59. url: '/users/me/',
  60. method: 'PUT',
  61. });
  62. render(<NewIssueExperienceButton />, {organization});
  63. const button = screen.getByRole('button', {
  64. name: 'Switch to the new issue experience',
  65. });
  66. await userEvent.click(button);
  67. // Text should change immediately
  68. expect(
  69. screen.getByRole('button', {name: 'Switch to the old issue experience'})
  70. ).toBeInTheDocument();
  71. // User option should be saved
  72. await waitFor(() => {
  73. expect(mockChangeUserSettings).toHaveBeenCalledWith(
  74. '/users/me/',
  75. expect.objectContaining({
  76. data: {
  77. options: {
  78. prefersIssueDetailsStreamlinedUI: true,
  79. },
  80. },
  81. })
  82. );
  83. });
  84. expect(trackAnalytics).toHaveBeenCalledTimes(1);
  85. // Clicking again toggles it off
  86. await userEvent.click(button);
  87. // Old text should be back
  88. expect(
  89. screen.getByRole('button', {name: 'Switch to the new issue experience'})
  90. ).toBeInTheDocument();
  91. // And save the option as false
  92. await waitFor(() => {
  93. expect(mockChangeUserSettings).toHaveBeenCalledWith(
  94. '/users/me/',
  95. expect.objectContaining({
  96. data: {
  97. options: {
  98. prefersIssueDetailsStreamlinedUI: false,
  99. },
  100. },
  101. })
  102. );
  103. });
  104. expect(trackAnalytics).toHaveBeenCalledTimes(2);
  105. });
  106. it('can switch back to the old UI via dropdown', async function () {
  107. const mockFormCallback = jest.fn();
  108. mockFeedbackForm.mockReturnValue(mockFormCallback);
  109. const mockChangeUserSettings = MockApiClient.addMockResponse({
  110. url: '/users/me/',
  111. method: 'PUT',
  112. });
  113. render(<NewIssueExperienceButton />, {organization});
  114. await userEvent.click(
  115. screen.getByRole('button', {
  116. name: 'Switch to the new issue experience',
  117. })
  118. );
  119. expect(
  120. screen.getByRole('button', {
  121. name: 'Switch issue experience',
  122. })
  123. ).toBeInTheDocument();
  124. const dropdownButton = screen.getByRole('button', {
  125. name: 'Switch issue experience',
  126. });
  127. await userEvent.click(dropdownButton);
  128. await userEvent.click(
  129. await screen.findByRole('menuitemradio', {name: 'Give feedback on new UI'})
  130. );
  131. expect(mockFeedbackForm).toHaveBeenCalled();
  132. await userEvent.click(dropdownButton);
  133. await userEvent.click(
  134. screen.getByRole('menuitemradio', {
  135. name: 'Switch to the old issue experience',
  136. })
  137. );
  138. expect(mockChangeUserSettings).toHaveBeenCalledTimes(2);
  139. });
  140. });