index.spec.jsx 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. import {
  2. render,
  3. renderGlobalModal,
  4. screen,
  5. userEvent,
  6. waitFor,
  7. within,
  8. } from 'sentry-test/reactTestingLibrary';
  9. import {Client} from 'sentry/api';
  10. import OrganizationDeveloperSettings from 'sentry/views/settings/organizationDeveloperSettings/index';
  11. describe('Organization Developer Settings', function () {
  12. const org = TestStubs.Organization();
  13. const sentryApp = TestStubs.SentryApp({
  14. scopes: [
  15. 'team:read',
  16. 'project:releases',
  17. 'event:read',
  18. 'event:write',
  19. 'org:read',
  20. 'org:write',
  21. ],
  22. });
  23. beforeEach(() => {
  24. Client.clearMockResponses();
  25. });
  26. describe('when no Apps exist', () => {
  27. it('displays empty state', async () => {
  28. Client.addMockResponse({
  29. url: `/organizations/${org.slug}/sentry-apps/`,
  30. body: [],
  31. });
  32. const {container} = render(<OrganizationDeveloperSettings organization={org} />);
  33. await waitFor(() => {
  34. expect(
  35. screen.getByText('No internal integrations have been created yet.')
  36. ).toBeInTheDocument();
  37. });
  38. expect(container).toSnapshot();
  39. });
  40. });
  41. describe('with unpublished apps', () => {
  42. beforeEach(() => {
  43. Client.addMockResponse({
  44. url: `/organizations/${org.slug}/sentry-apps/`,
  45. body: [sentryApp],
  46. });
  47. });
  48. it('internal integrations list is empty', () => {
  49. render(<OrganizationDeveloperSettings organization={org} />, {organization: org});
  50. expect(
  51. screen.getByText('No internal integrations have been created yet.')
  52. ).toBeInTheDocument();
  53. });
  54. it('public integrations list contains 1 item', () => {
  55. render(
  56. <OrganizationDeveloperSettings
  57. organization={org}
  58. location={{query: {type: 'public'}}}
  59. />,
  60. {organization: org}
  61. );
  62. expect(screen.getByText('Sample App')).toBeInTheDocument();
  63. expect(screen.getByText('unpublished')).toBeInTheDocument();
  64. });
  65. it('allows for deletion', async () => {
  66. Client.addMockResponse({
  67. url: `/sentry-apps/${sentryApp.slug}/`,
  68. method: 'DELETE',
  69. body: [],
  70. });
  71. render(
  72. <OrganizationDeveloperSettings
  73. organization={org}
  74. location={{query: {type: 'public'}}}
  75. />
  76. );
  77. const deleteButton = await screen.findByRole('button', {name: 'Delete'});
  78. expect(deleteButton).toHaveAttribute('aria-disabled', 'false');
  79. await userEvent.click(deleteButton);
  80. renderGlobalModal();
  81. const dialog = await screen.findByRole('dialog');
  82. expect(dialog).toBeInTheDocument();
  83. const input = await within(dialog).findByPlaceholderText('sample-app');
  84. await userEvent.type(input, 'sample-app');
  85. const confirmDeleteButton = await screen.findByRole('button', {name: 'Confirm'});
  86. await userEvent.click(confirmDeleteButton);
  87. await screen.findByText('No public integrations have been created yet.');
  88. });
  89. it('can make a request to publish an integration', async () => {
  90. const mock = Client.addMockResponse({
  91. url: `/sentry-apps/${sentryApp.slug}/publish-request/`,
  92. method: 'POST',
  93. });
  94. render(
  95. <OrganizationDeveloperSettings
  96. organization={org}
  97. location={{query: {type: 'public'}}}
  98. />
  99. );
  100. const publishButton = await screen.findByRole('button', {name: 'Publish'});
  101. expect(publishButton).toHaveAttribute('aria-disabled', 'false');
  102. await userEvent.click(publishButton);
  103. renderGlobalModal();
  104. const dialog = await screen.findByRole('dialog');
  105. expect(dialog).toBeInTheDocument();
  106. const questionnaire = [
  107. {
  108. answer: 'Answer 0',
  109. question: 'What does your integration do? Please be as detailed as possible.',
  110. },
  111. {answer: 'Answer 1', question: 'What value does it offer customers?'},
  112. {
  113. answer: 'Answer 2',
  114. question: 'Do you operate the web service your integration communicates with?',
  115. },
  116. {
  117. answer: 'Answer 3',
  118. question:
  119. 'Please justify why you are requesting each of the following permissions: Team Read, Release Admin, Event Write, Organization Write.',
  120. },
  121. ];
  122. for (const {question, answer} of questionnaire) {
  123. const element = within(dialog).getByRole('textbox', {name: question});
  124. await userEvent.type(element, answer);
  125. }
  126. const requestPublishButton = await within(dialog).findByLabelText(
  127. 'Request Publication'
  128. );
  129. expect(requestPublishButton).toHaveAttribute('aria-disabled', 'false');
  130. await userEvent.click(requestPublishButton);
  131. expect(mock).toHaveBeenCalledWith(
  132. `/sentry-apps/${sentryApp.slug}/publish-request/`,
  133. expect.objectContaining({
  134. data: {questionnaire},
  135. })
  136. );
  137. });
  138. });
  139. describe('with published apps', () => {
  140. beforeEach(() => {
  141. const publishedSentryApp = TestStubs.SentryApp({status: 'published'});
  142. Client.addMockResponse({
  143. url: `/organizations/${org.slug}/sentry-apps/`,
  144. body: [publishedSentryApp],
  145. });
  146. });
  147. it('shows the published status', () => {
  148. render(
  149. <OrganizationDeveloperSettings
  150. organization={org}
  151. location={{query: {type: 'public'}}}
  152. />
  153. );
  154. expect(screen.getByText('published')).toBeInTheDocument();
  155. });
  156. it('trash button is disabled', async () => {
  157. render(
  158. <OrganizationDeveloperSettings
  159. organization={org}
  160. location={{query: {type: 'public'}}}
  161. />
  162. );
  163. const deleteButton = await screen.findByRole('button', {name: 'Delete'});
  164. expect(deleteButton).toHaveAttribute('aria-disabled', 'true');
  165. });
  166. it('publish button is disabled', async () => {
  167. render(
  168. <OrganizationDeveloperSettings
  169. organization={org}
  170. location={{query: {type: 'public'}}}
  171. />
  172. );
  173. const publishButton = await screen.findByRole('button', {name: 'Publish'});
  174. expect(publishButton).toHaveAttribute('aria-disabled', 'true');
  175. });
  176. });
  177. describe('with Internal Integrations', () => {
  178. beforeEach(() => {
  179. const internalIntegration = TestStubs.SentryApp({status: 'internal'});
  180. Client.addMockResponse({
  181. url: `/organizations/${org.slug}/sentry-apps/`,
  182. body: [internalIntegration],
  183. });
  184. });
  185. it('allows deleting', async () => {
  186. render(<OrganizationDeveloperSettings organization={org} />);
  187. const deleteButton = await screen.findByRole('button', {name: 'Delete'});
  188. expect(deleteButton).toHaveAttribute('aria-disabled', 'false');
  189. });
  190. it('publish button does not exist', () => {
  191. render(<OrganizationDeveloperSettings organization={org} />);
  192. expect(screen.queryByText('Publish')).not.toBeInTheDocument();
  193. });
  194. });
  195. describe('without Owner permissions', () => {
  196. const newOrg = TestStubs.Organization({access: ['org:read']});
  197. beforeEach(() => {
  198. Client.addMockResponse({
  199. url: `/organizations/${newOrg.slug}/sentry-apps/`,
  200. body: [sentryApp],
  201. });
  202. });
  203. it('trash button is disabled', async () => {
  204. render(
  205. <OrganizationDeveloperSettings
  206. organization={newOrg}
  207. location={{query: {type: 'public'}}}
  208. />,
  209. {organization: newOrg}
  210. );
  211. const deleteButton = await screen.findByRole('button', {name: 'Delete'});
  212. expect(deleteButton).toHaveAttribute('aria-disabled', 'true');
  213. });
  214. it('publish button is disabled', async () => {
  215. render(
  216. <OrganizationDeveloperSettings
  217. organization={newOrg}
  218. location={{query: {type: 'public'}}}
  219. />,
  220. {organization: newOrg}
  221. );
  222. const publishButton = await screen.findByRole('button', {name: 'Publish'});
  223. expect(publishButton).toHaveAttribute('aria-disabled', 'true');
  224. });
  225. });
  226. });