index.spec.jsx 8.2 KB

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