index.spec.tsx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  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. });
  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('codecov-integration');
  54. organization.codecovAccess = false;
  55. render(<OrganizationGeneralSettings {...defaultProps} />);
  56. const mock = MockApiClient.addMockResponse({
  57. url: ENDPOINT,
  58. method: 'PUT',
  59. });
  60. await userEvent.click(
  61. screen.getByRole('checkbox', {name: /Enable Code Coverage Insights/i})
  62. );
  63. await waitFor(() => {
  64. expect(mock).toHaveBeenCalledWith(
  65. ENDPOINT,
  66. expect.objectContaining({
  67. data: {codecovAccess: true},
  68. })
  69. );
  70. });
  71. expect(trackAnalytics).toHaveBeenCalled();
  72. });
  73. it('changes org slug and redirects to new slug', async function () {
  74. render(<OrganizationGeneralSettings {...defaultProps} />);
  75. const mock = MockApiClient.addMockResponse({
  76. url: ENDPOINT,
  77. method: 'PUT',
  78. body: {...organization, slug: 'new-slug'},
  79. });
  80. await userEvent.clear(screen.getByRole('textbox', {name: /slug/i}));
  81. await userEvent.type(screen.getByRole('textbox', {name: /slug/i}), 'new-slug');
  82. await userEvent.click(screen.getByLabelText('Save'));
  83. await waitFor(() => {
  84. expect(mock).toHaveBeenCalledWith(
  85. ENDPOINT,
  86. expect.objectContaining({
  87. data: {slug: 'new-slug'},
  88. })
  89. );
  90. expect(browserHistory.replace).toHaveBeenCalledWith('/settings/new-slug/');
  91. });
  92. });
  93. it('changes org slug and redirects to new customer-domain', async function () {
  94. const org = TestStubs.Organization({features: ['customer-domains']});
  95. const updateMock = MockApiClient.addMockResponse({
  96. url: `/organizations/${organization.slug}/`,
  97. method: 'PUT',
  98. body: {...org, slug: 'acme', links: {organizationUrl: 'https://acme.sentry.io'}},
  99. });
  100. render(<OrganizationGeneralSettings {...defaultProps} organization={org} />);
  101. const input = screen.getByRole('textbox', {name: /slug/i});
  102. await userEvent.clear(input);
  103. await userEvent.type(input, 'acme');
  104. await userEvent.click(screen.getByLabelText('Save'));
  105. await waitFor(() => {
  106. expect(updateMock).toHaveBeenCalledWith(
  107. '/organizations/org-slug/',
  108. expect.objectContaining({
  109. data: {
  110. slug: 'acme',
  111. },
  112. })
  113. );
  114. expect(window.location.replace).toHaveBeenCalledWith(
  115. 'https://acme.sentry.io/settings/organization/'
  116. );
  117. });
  118. });
  119. it('disables the entire form if user does not have write access', function () {
  120. const readOnlyOrg = TestStubs.Organization({access: ['org:read']});
  121. render(<OrganizationGeneralSettings {...defaultProps} organization={readOnlyOrg} />, {
  122. organization: readOnlyOrg,
  123. });
  124. const formElements = [
  125. ...screen.getAllByRole('textbox'),
  126. ...screen.getAllByRole('button'),
  127. ...screen.getAllByRole('checkbox'),
  128. ];
  129. for (const formElement of formElements) {
  130. expect(formElement).toBeDisabled();
  131. }
  132. expect(
  133. screen.getByText(
  134. 'These settings can only be edited by users with the organization owner or manager role.'
  135. )
  136. ).toBeInTheDocument();
  137. });
  138. it('does not have remove organization button without org:admin permission', function () {
  139. render(
  140. <OrganizationGeneralSettings
  141. {...defaultProps}
  142. organization={TestStubs.Organization({
  143. access: ['org:write'],
  144. })}
  145. />
  146. );
  147. expect(
  148. screen.queryByRole('button', {name: /remove organization/i})
  149. ).not.toBeInTheDocument();
  150. });
  151. it('can remove organization when org admin', async function () {
  152. act(() => ProjectsStore.loadInitialData([TestStubs.Project({slug: 'project'})]));
  153. render(
  154. <OrganizationGeneralSettings
  155. {...defaultProps}
  156. organization={TestStubs.Organization({access: ['org:admin']})}
  157. />
  158. );
  159. renderGlobalModal();
  160. const mock = MockApiClient.addMockResponse({
  161. url: ENDPOINT,
  162. method: 'DELETE',
  163. });
  164. await userEvent.click(screen.getByRole('button', {name: /remove organization/i}));
  165. const modal = screen.getByRole('dialog');
  166. expect(
  167. within(modal).getByText('This will also remove the following associated projects:')
  168. ).toBeInTheDocument();
  169. expect(within(modal).getByText('project')).toBeInTheDocument();
  170. await userEvent.click(
  171. within(modal).getByRole('button', {name: /remove organization/i})
  172. );
  173. await waitFor(() => {
  174. expect(mock).toHaveBeenCalledWith(
  175. ENDPOINT,
  176. expect.objectContaining({
  177. method: 'DELETE',
  178. })
  179. );
  180. });
  181. });
  182. });