sentryAppExternalIssueActions.spec.jsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import {Fragment} from 'react';
  2. import {mountWithTheme} from 'sentry-test/enzyme';
  3. import GlobalModal from 'sentry/components/globalModal';
  4. import SentryAppExternalIssueActions from 'sentry/components/group/sentryAppExternalIssueActions';
  5. describe('SentryAppExternalIssueActions', () => {
  6. let group;
  7. let component;
  8. let sentryApp;
  9. let install;
  10. let submitUrl;
  11. let externalIssue;
  12. let wrapper;
  13. beforeEach(() => {
  14. group = TestStubs.Group();
  15. sentryApp = TestStubs.SentryApp();
  16. component = TestStubs.SentryAppComponent({
  17. sentryApp: {
  18. uuid: sentryApp.uuid,
  19. slug: sentryApp.slug,
  20. name: sentryApp.name,
  21. },
  22. });
  23. // unable to use the selectByValue here so remove the select option
  24. component.schema.create.required_fields.pop();
  25. install = TestStubs.SentryAppInstallation({sentryApp});
  26. submitUrl = `/sentry-app-installations/${install.uuid}/external-issue-actions/`;
  27. externalIssue = TestStubs.PlatformExternalIssue({
  28. groupId: group.id,
  29. serviceType: component.sentryApp.slug,
  30. });
  31. MockApiClient.addMockResponse({
  32. url: `/sentry-apps/${sentryApp.slug}/interaction/`,
  33. method: 'POST',
  34. });
  35. });
  36. describe('without an external issue linked', () => {
  37. beforeEach(() => {
  38. wrapper = mountWithTheme(
  39. <Fragment>
  40. <GlobalModal />
  41. <SentryAppExternalIssueActions
  42. group={group}
  43. sentryAppInstallation={install}
  44. sentryAppComponent={component}
  45. />
  46. </Fragment>
  47. );
  48. });
  49. it('renders a link to open the modal', () => {
  50. expect(wrapper.find('StyledIntegrationLink a').text()).toEqual(
  51. `${component.sentryApp.name} Issue`
  52. );
  53. });
  54. it('renders the add icon', () => {
  55. expect(wrapper.find('StyledIcon IconAdd')).toHaveLength(1);
  56. });
  57. it('opens the modal', async () => {
  58. wrapper.find('StyledIntegrationLink a').simulate('click');
  59. await tick();
  60. wrapper.update();
  61. expect(wrapper.find('GlobalModal[visible=true]').exists()).toEqual(true);
  62. });
  63. it('renders the Create Issue form fields, based on schema', async () => {
  64. wrapper.find('StyledIntegrationLink a').simulate('click');
  65. await tick();
  66. wrapper.update();
  67. wrapper.find('Modal NavTabs li.create a').first().simulate('click'); // Create
  68. component.schema.create.required_fields.forEach(field => {
  69. expect(wrapper.exists(`SentryAppExternalIssueForm #${field.name}`)).toBe(true);
  70. });
  71. (component.schema.create.optional_fields || []).forEach(field => {
  72. expect(wrapper.exists(`SentryAppExternalIssueForm #${field.name}`)).toBe(true);
  73. });
  74. });
  75. it('renders the Link Issue form fields, based on schema', async () => {
  76. wrapper.find('StyledIntegrationLink a').simulate('click');
  77. await tick();
  78. wrapper.update();
  79. wrapper.find('Modal NavTabs li.link a').first().simulate('click'); // Link
  80. component.schema.link.required_fields.forEach(field => {
  81. expect(wrapper.exists(`SentryAppExternalIssueForm #${field.name}`)).toBe(true);
  82. });
  83. (component.schema.link.optional_fields || []).forEach(field => {
  84. expect(wrapper.exists(`SentryAppExternalIssueForm #${field.name}`)).toBe(true);
  85. });
  86. });
  87. it('links to an existing Issue', async () => {
  88. const request = MockApiClient.addMockResponse({
  89. url: submitUrl,
  90. method: 'POST',
  91. body: externalIssue,
  92. });
  93. wrapper.find('StyledIntegrationLink a').simulate('click');
  94. await tick();
  95. wrapper.update();
  96. wrapper.find('NavTabs li.link a').simulate('click');
  97. wrapper.find('Input#issue').simulate('change', {target: {value: '99'}});
  98. wrapper.find('Form form').simulate('submit');
  99. expect(request).toHaveBeenCalledWith(
  100. submitUrl,
  101. expect.objectContaining({
  102. data: expect.objectContaining({
  103. action: 'link',
  104. issue: '99',
  105. groupId: group.id,
  106. }),
  107. })
  108. );
  109. });
  110. it('creates a new Issue', async () => {
  111. const request = MockApiClient.addMockResponse({
  112. url: submitUrl,
  113. method: 'POST',
  114. body: externalIssue,
  115. });
  116. wrapper.find('StyledIntegrationLink a').simulate('click');
  117. await tick();
  118. wrapper.update();
  119. wrapper.find('NavTabs li.create a').simulate('click');
  120. wrapper.find('Input#title').simulate('change', {target: {value: 'foo'}});
  121. wrapper.find('TextArea#description').simulate('change', {target: {value: 'bar'}});
  122. wrapper.find('Form form').simulate('submit');
  123. expect(request).toHaveBeenCalledWith(
  124. submitUrl,
  125. expect.objectContaining({
  126. data: expect.objectContaining({
  127. action: 'create',
  128. title: 'foo',
  129. description: 'bar',
  130. groupId: group.id,
  131. }),
  132. })
  133. );
  134. });
  135. });
  136. describe('with an external issue linked', () => {
  137. beforeEach(() => {
  138. wrapper = mountWithTheme(
  139. <SentryAppExternalIssueActions
  140. group={group}
  141. sentryAppComponent={component}
  142. sentryAppInstallation={install}
  143. externalIssue={externalIssue}
  144. />
  145. );
  146. });
  147. it('renders a link to the external issue', () => {
  148. expect(wrapper.find('StyledIntegrationLink a').text()).toEqual(
  149. externalIssue.displayName
  150. );
  151. });
  152. it('links to the issue', () => {
  153. expect(wrapper.find('StyledIntegrationLink').first().prop('href')).toEqual(
  154. externalIssue.webUrl
  155. );
  156. });
  157. it('renders the remove issue button', () => {
  158. expect(wrapper.find('StyledIcon IconClose')).toHaveLength(1);
  159. });
  160. it('deletes a Linked Issue', () => {
  161. const request = MockApiClient.addMockResponse({
  162. url: `/issues/${group.id}/external-issues/${externalIssue.id}/`,
  163. method: 'DELETE',
  164. });
  165. wrapper.find('StyledIcon').simulate('click');
  166. expect(request).toHaveBeenCalled();
  167. });
  168. });
  169. });