index.spec.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. import {browserHistory} from 'react-router';
  2. import {initializeOrg} from 'sentry-test/initializeOrg';
  3. import {
  4. act,
  5. render,
  6. renderGlobalModal,
  7. screen,
  8. userEvent,
  9. waitFor,
  10. within,
  11. } from 'sentry-test/reactTestingLibrary';
  12. import OrganizationsStore from 'sentry/stores/organizationsStore';
  13. import ProjectsStore from 'sentry/stores/projectsStore';
  14. import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
  15. import OrganizationGeneralSettings from 'sentry/views/settings/organizationGeneralSettings';
  16. jest.mock('sentry/utils/analytics/trackAdvancedAnalyticsEvent');
  17. describe('OrganizationGeneralSettings', function () {
  18. const ENDPOINT = '/organizations/org-slug/';
  19. const {organization, router} = initializeOrg();
  20. const defaultProps = {
  21. organization,
  22. router,
  23. location: router.location,
  24. params: {orgId: organization.slug},
  25. routes: router.routes,
  26. route: {},
  27. routeParams: router.params,
  28. };
  29. beforeEach(function () {
  30. OrganizationsStore.addOrReplace(organization);
  31. MockApiClient.addMockResponse({
  32. url: `/organizations/${organization.slug}/auth-provider/`,
  33. method: 'GET',
  34. });
  35. });
  36. it('can enable "early adopter"', async function () {
  37. render(<OrganizationGeneralSettings {...defaultProps} />);
  38. const mock = MockApiClient.addMockResponse({
  39. url: ENDPOINT,
  40. method: 'PUT',
  41. });
  42. await userEvent.click(screen.getByRole('checkbox', {name: /early adopter/i}));
  43. await waitFor(() => {
  44. expect(mock).toHaveBeenCalledWith(
  45. ENDPOINT,
  46. expect.objectContaining({
  47. data: {isEarlyAdopter: true},
  48. })
  49. );
  50. });
  51. });
  52. it('can enable "codecov access"', async function () {
  53. defaultProps.organization.features.push(
  54. 'codecov-stacktrace-integration',
  55. 'codecov-integration'
  56. );
  57. organization.codecovAccess = false;
  58. render(<OrganizationGeneralSettings {...defaultProps} />);
  59. const mock = MockApiClient.addMockResponse({
  60. url: ENDPOINT,
  61. method: 'PUT',
  62. });
  63. await userEvent.click(
  64. screen.getByRole('checkbox', {name: /Enable Code Coverage Insights/i})
  65. );
  66. await waitFor(() => {
  67. expect(mock).toHaveBeenCalledWith(
  68. ENDPOINT,
  69. expect.objectContaining({
  70. data: {codecovAccess: true},
  71. })
  72. );
  73. });
  74. expect(trackAdvancedAnalyticsEvent).toHaveBeenCalled();
  75. });
  76. it('changes org slug and redirects to new slug', async function () {
  77. render(<OrganizationGeneralSettings {...defaultProps} />);
  78. const mock = MockApiClient.addMockResponse({
  79. url: ENDPOINT,
  80. method: 'PUT',
  81. body: {...organization, slug: 'new-slug'},
  82. });
  83. await userEvent.clear(screen.getByRole('textbox', {name: /slug/i}));
  84. await userEvent.type(screen.getByRole('textbox', {name: /slug/i}), 'new-slug');
  85. await userEvent.click(screen.getByLabelText('Save'));
  86. await waitFor(() => {
  87. expect(mock).toHaveBeenCalledWith(
  88. ENDPOINT,
  89. expect.objectContaining({
  90. data: {slug: 'new-slug'},
  91. })
  92. );
  93. expect(browserHistory.replace).toHaveBeenCalledWith('/settings/new-slug/');
  94. });
  95. });
  96. it('changes org slug and redirects to new customer-domain', async function () {
  97. const org = TestStubs.Organization({features: ['customer-domains']});
  98. const updateMock = MockApiClient.addMockResponse({
  99. url: `/organizations/${organization.slug}/`,
  100. method: 'PUT',
  101. body: {...org, slug: 'acme', links: {organizationUrl: 'https://acme.sentry.io'}},
  102. });
  103. render(<OrganizationGeneralSettings {...defaultProps} organization={org} />);
  104. const input = screen.getByRole('textbox', {name: /slug/i});
  105. await userEvent.clear(input);
  106. await userEvent.type(input, 'acme');
  107. await userEvent.click(screen.getByLabelText('Save'));
  108. await waitFor(() => {
  109. expect(updateMock).toHaveBeenCalledWith(
  110. '/organizations/org-slug/',
  111. expect.objectContaining({
  112. data: {
  113. slug: 'acme',
  114. },
  115. })
  116. );
  117. expect(window.location.replace).toHaveBeenCalledWith(
  118. 'https://acme.sentry.io/settings/organization/'
  119. );
  120. });
  121. });
  122. it('disables the entire form if user does not have write access', function () {
  123. render(
  124. <OrganizationGeneralSettings
  125. {...defaultProps}
  126. organization={TestStubs.Organization({access: ['org:read']})}
  127. />
  128. );
  129. const formElements = [
  130. ...screen.getAllByRole('textbox'),
  131. ...screen.getAllByRole('button'),
  132. ...screen.getAllByRole('checkbox'),
  133. ];
  134. for (const formElement of formElements) {
  135. expect(formElement).toBeDisabled();
  136. }
  137. expect(
  138. screen.getByText(
  139. 'These settings can only be edited by users with the organization owner or manager role.'
  140. )
  141. ).toBeInTheDocument();
  142. });
  143. it('does not have remove organization button without org:admin permission', function () {
  144. render(
  145. <OrganizationGeneralSettings
  146. {...defaultProps}
  147. organization={TestStubs.Organization({
  148. access: ['org:write'],
  149. })}
  150. />
  151. );
  152. expect(
  153. screen.queryByRole('button', {name: /remove organization/i})
  154. ).not.toBeInTheDocument();
  155. });
  156. it('can remove organization when org admin', async function () {
  157. act(() => ProjectsStore.loadInitialData([TestStubs.Project({slug: 'project'})]));
  158. render(
  159. <OrganizationGeneralSettings
  160. {...defaultProps}
  161. organization={TestStubs.Organization({access: ['org:admin']})}
  162. />
  163. );
  164. renderGlobalModal();
  165. const mock = MockApiClient.addMockResponse({
  166. url: ENDPOINT,
  167. method: 'DELETE',
  168. });
  169. await userEvent.click(screen.getByRole('button', {name: /remove organization/i}));
  170. const modal = screen.getByRole('dialog');
  171. expect(
  172. within(modal).getByText('This will also remove the following associated projects:')
  173. ).toBeInTheDocument();
  174. expect(within(modal).getByText('project')).toBeInTheDocument();
  175. await userEvent.click(
  176. within(modal).getByRole('button', {name: /remove organization/i})
  177. );
  178. await waitFor(() => {
  179. expect(mock).toHaveBeenCalledWith(
  180. ENDPOINT,
  181. expect.objectContaining({
  182. method: 'DELETE',
  183. })
  184. );
  185. });
  186. });
  187. });