sentryAppExternalIssueActions.spec.jsx 6.1 KB

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