organizationSettingsForm.spec.tsx 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. import {OrganizationFixture} from 'sentry-fixture/organization';
  2. import {initializeOrg} from 'sentry-test/initializeOrg';
  3. import {act, render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
  4. import * as indicatorActions from 'sentry/actionCreators/indicator';
  5. import Indicators from 'sentry/components/indicators';
  6. import * as RegionUtils from 'sentry/utils/regions';
  7. import OrganizationSettingsForm from 'sentry/views/settings/organizationGeneralSettings/organizationSettingsForm';
  8. jest.mock('sentry/actionCreators/indicator');
  9. jest.mock('sentry/utils/regions');
  10. describe('OrganizationSettingsForm', function () {
  11. const {organization, routerProps} = initializeOrg();
  12. let putMock: jest.Mock;
  13. const onSave = jest.fn();
  14. beforeEach(function () {
  15. MockApiClient.clearMockResponses();
  16. MockApiClient.addMockResponse({
  17. url: `/organizations/${organization.slug}/auth-provider/`,
  18. method: 'GET',
  19. });
  20. MockApiClient.addMockResponse({
  21. url: `/organizations/${organization.slug}/integrations/?provider_key=github`,
  22. method: 'GET',
  23. body: {
  24. providers: [{canAdd: true}],
  25. },
  26. });
  27. onSave.mockReset();
  28. });
  29. it('can change a form field', async function () {
  30. putMock = MockApiClient.addMockResponse({
  31. url: `/organizations/${organization.slug}/`,
  32. method: 'PUT',
  33. body: organization,
  34. });
  35. render(
  36. <OrganizationSettingsForm
  37. {...routerProps}
  38. initialData={OrganizationFixture()}
  39. onSave={onSave}
  40. />
  41. );
  42. render(<Indicators />);
  43. const input = screen.getByRole('textbox', {name: 'Display Name'});
  44. const saveOnBlur = jest.spyOn(indicatorActions, 'saveOnBlurUndoMessage');
  45. await userEvent.clear(input);
  46. await userEvent.type(input, 'New Name');
  47. await userEvent.tab();
  48. expect(putMock).toHaveBeenCalledWith(
  49. `/organizations/${organization.slug}/`,
  50. expect.objectContaining({
  51. method: 'PUT',
  52. data: {
  53. name: 'New Name',
  54. },
  55. })
  56. );
  57. expect(saveOnBlur).toHaveBeenCalledWith(
  58. {
  59. new: 'New Name',
  60. old: 'Organization Name',
  61. },
  62. expect.anything(),
  63. 'name'
  64. );
  65. const model = saveOnBlur.mock.calls[0][1];
  66. // Test "undo" call undo directly
  67. expect(model.getValue('name')).toBe('New Name');
  68. act(() => {
  69. model.undo();
  70. });
  71. expect(model.getValue('name')).toBe('Organization Name');
  72. // `saveOnBlurUndoMessage` saves the new field, so reimplement this
  73. act(() => {
  74. model.saveField('name', 'Organization Name');
  75. });
  76. // Initial data should be updated to original name
  77. await waitFor(() => expect(model.initialData.name).toBe('Organization Name'));
  78. putMock.mockReset();
  79. // Blurring the name field again should NOT trigger a save
  80. await userEvent.click(input);
  81. await userEvent.tab();
  82. expect(putMock).not.toHaveBeenCalled();
  83. });
  84. it('can change slug', async function () {
  85. putMock = MockApiClient.addMockResponse({
  86. url: `/organizations/${organization.slug}/`,
  87. method: 'PUT',
  88. body: organization,
  89. });
  90. render(
  91. <OrganizationSettingsForm
  92. {...routerProps}
  93. initialData={OrganizationFixture()}
  94. onSave={onSave}
  95. />
  96. );
  97. const input = screen.getByRole('textbox', {name: 'Organization Slug'});
  98. await userEvent.clear(input);
  99. await userEvent.type(input, 'NEW SLUG');
  100. await userEvent.tab();
  101. expect(putMock).not.toHaveBeenCalled();
  102. await userEvent.click(screen.getByRole('button', {name: 'Save'}));
  103. expect(putMock).toHaveBeenCalledWith(
  104. '/organizations/org-slug/',
  105. expect.objectContaining({
  106. data: {
  107. slug: 'new-slug',
  108. },
  109. })
  110. );
  111. });
  112. it('can enable codecov', async function () {
  113. putMock = MockApiClient.addMockResponse({
  114. url: `/organizations/${organization.slug}/`,
  115. method: 'PUT',
  116. body: {...organization, codecovAccess: true},
  117. });
  118. render(
  119. <OrganizationSettingsForm
  120. {...routerProps}
  121. initialData={OrganizationFixture({codecovAccess: false})}
  122. onSave={onSave}
  123. />,
  124. {
  125. organization: {
  126. ...organization,
  127. features: ['codecov-integration'],
  128. },
  129. }
  130. );
  131. await userEvent.click(
  132. screen.getByRole('checkbox', {name: /Enable Code Coverage Insights/})
  133. );
  134. expect(putMock).toHaveBeenCalledWith(
  135. '/organizations/org-slug/',
  136. expect.objectContaining({
  137. data: {
  138. codecovAccess: true,
  139. },
  140. })
  141. );
  142. });
  143. it('can toggle hideAiFeatures setting', async function () {
  144. putMock = MockApiClient.addMockResponse({
  145. url: `/organizations/${organization.slug}/`,
  146. method: 'PUT',
  147. body: {...organization, hideAiFeatures: true},
  148. });
  149. render(
  150. <OrganizationSettingsForm
  151. {...routerProps}
  152. initialData={OrganizationFixture({hideAiFeatures: false})}
  153. onSave={onSave}
  154. />,
  155. {
  156. organization: {
  157. ...organization,
  158. features: ['autofix'],
  159. },
  160. }
  161. );
  162. await userEvent.click(screen.getByRole('checkbox', {name: 'Hide AI Features'}));
  163. expect(putMock).toHaveBeenCalledWith(
  164. '/organizations/org-slug/',
  165. expect.objectContaining({
  166. data: {
  167. hideAiFeatures: true,
  168. },
  169. })
  170. );
  171. });
  172. it('disables hideAiFeatures toggle and shows tooltip for DE region', function () {
  173. // Mock the region util to return DE region
  174. jest.mocked(RegionUtils.getRegionDataFromOrganization).mockImplementation(() => ({
  175. name: 'de',
  176. displayName: 'Europe (Frankfurt)',
  177. url: 'https://sentry.de.example.com',
  178. }));
  179. render(
  180. <OrganizationSettingsForm
  181. {...routerProps}
  182. initialData={OrganizationFixture()}
  183. onSave={onSave}
  184. />,
  185. {
  186. organization: {
  187. ...organization,
  188. features: ['autofix'],
  189. },
  190. }
  191. );
  192. const toggle = screen.getByRole('checkbox', {name: 'Hide AI Features'});
  193. expect(toggle).toBeDisabled();
  194. });
  195. });