index.spec.tsx 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  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 {trackAnalytics} from 'sentry/utils/analytics';
  15. import OrganizationGeneralSettings from 'sentry/views/settings/organizationGeneralSettings';
  16. jest.mock('sentry/utils/analytics');
  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. MockApiClient.addMockResponse({
  36. url: `/organizations/${organization.slug}/integrations/?provider_key=github`,
  37. method: 'GET',
  38. body: [TestStubs.GitHubIntegration()],
  39. });
  40. });
  41. it('can enable "early adopter"', async function () {
  42. render(<OrganizationGeneralSettings {...defaultProps} />);
  43. const mock = MockApiClient.addMockResponse({
  44. url: ENDPOINT,
  45. method: 'PUT',
  46. });
  47. await userEvent.click(screen.getByRole('checkbox', {name: /early adopter/i}));
  48. await waitFor(() => {
  49. expect(mock).toHaveBeenCalledWith(
  50. ENDPOINT,
  51. expect.objectContaining({
  52. data: {isEarlyAdopter: true},
  53. })
  54. );
  55. });
  56. });
  57. it('can enable "codecov access"', async function () {
  58. defaultProps.organization.features.push('codecov-integration');
  59. organization.codecovAccess = false;
  60. render(<OrganizationGeneralSettings {...defaultProps} />);
  61. const mock = MockApiClient.addMockResponse({
  62. url: ENDPOINT,
  63. method: 'PUT',
  64. });
  65. await userEvent.click(
  66. screen.getByRole('checkbox', {name: /Enable Code Coverage Insights/i})
  67. );
  68. await waitFor(() => {
  69. expect(mock).toHaveBeenCalledWith(
  70. ENDPOINT,
  71. expect.objectContaining({
  72. data: {codecovAccess: true},
  73. })
  74. );
  75. });
  76. expect(trackAnalytics).toHaveBeenCalled();
  77. });
  78. it('changes org slug and redirects to new slug', async function () {
  79. render(<OrganizationGeneralSettings {...defaultProps} />);
  80. const mock = MockApiClient.addMockResponse({
  81. url: ENDPOINT,
  82. method: 'PUT',
  83. body: {...organization, slug: 'new-slug'},
  84. });
  85. await userEvent.clear(screen.getByRole('textbox', {name: /slug/i}));
  86. await userEvent.type(screen.getByRole('textbox', {name: /slug/i}), 'new-slug');
  87. await userEvent.click(screen.getByLabelText('Save'));
  88. await waitFor(() => {
  89. expect(mock).toHaveBeenCalledWith(
  90. ENDPOINT,
  91. expect.objectContaining({
  92. data: {slug: 'new-slug'},
  93. })
  94. );
  95. expect(browserHistory.replace).toHaveBeenCalledWith('/settings/new-slug/');
  96. });
  97. });
  98. it('changes org slug and redirects to new customer-domain', async function () {
  99. const org = TestStubs.Organization({features: ['customer-domains']});
  100. const updateMock = MockApiClient.addMockResponse({
  101. url: `/organizations/${organization.slug}/`,
  102. method: 'PUT',
  103. body: {...org, slug: 'acme', links: {organizationUrl: 'https://acme.sentry.io'}},
  104. });
  105. render(<OrganizationGeneralSettings {...defaultProps} organization={org} />);
  106. const input = screen.getByRole('textbox', {name: /slug/i});
  107. await userEvent.clear(input);
  108. await userEvent.type(input, 'acme');
  109. await userEvent.click(screen.getByLabelText('Save'));
  110. await waitFor(() => {
  111. expect(updateMock).toHaveBeenCalledWith(
  112. '/organizations/org-slug/',
  113. expect.objectContaining({
  114. data: {
  115. slug: 'acme',
  116. },
  117. })
  118. );
  119. expect(window.location.replace).toHaveBeenCalledWith(
  120. 'https://acme.sentry.io/settings/organization/'
  121. );
  122. });
  123. });
  124. it('disables the entire form if user does not have write access', function () {
  125. const readOnlyOrg = TestStubs.Organization({access: ['org:read']});
  126. render(<OrganizationGeneralSettings {...defaultProps} organization={readOnlyOrg} />, {
  127. organization: readOnlyOrg,
  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. });