index.spec.tsx 8.3 KB

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