stacktraceLinkModal.spec.tsx 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. import {
  2. act,
  3. renderGlobalModal,
  4. screen,
  5. userEvent,
  6. waitFor,
  7. } from 'sentry-test/reactTestingLibrary';
  8. import {openModal} from 'sentry/actionCreators/modal';
  9. import StacktraceLinkModal from 'sentry/components/events/interfaces/frame/stacktraceLinkModal';
  10. import * as analytics from 'sentry/utils/integrationUtil';
  11. jest.mock('sentry/utils/analytics/trackAdvancedAnalyticsEvent');
  12. describe('StacktraceLinkModal', () => {
  13. const org = TestStubs.Organization();
  14. const project = TestStubs.Project();
  15. const integration = TestStubs.GitHubIntegration();
  16. const filename = '/sentry/app.py';
  17. const repo = TestStubs.Repository({integrationId: integration.id});
  18. const config = TestStubs.RepositoryProjectPathConfig({project, repo, integration});
  19. const sourceUrl = 'https://github.com/getsentry/sentry/blob/master/src/sentry/app.py';
  20. const configData = {
  21. stackRoot: '',
  22. sourceRoot: 'src/',
  23. integrationId: integration.id,
  24. repositoryId: repo.id,
  25. defaultBranch: 'master',
  26. };
  27. const onSubmit = jest.fn();
  28. const closeModal = jest.fn();
  29. const analyticsSpy = jest.spyOn(analytics, 'trackIntegrationAnalytics');
  30. beforeEach(() => {
  31. MockApiClient.addMockResponse({
  32. url: `/organizations/${org.slug}/code-mappings/`,
  33. method: 'POST',
  34. });
  35. MockApiClient.addMockResponse({
  36. url: `/projects/${org.slug}/${project.slug}/stacktrace-link/`,
  37. body: {config, sourceUrl, integrations: [integration]},
  38. });
  39. MockApiClient.addMockResponse({
  40. url: `/organizations/${org.slug}/derive-code-mappings/`,
  41. body: [],
  42. });
  43. });
  44. afterEach(() => {
  45. MockApiClient.clearMockResponses();
  46. jest.clearAllMocks();
  47. });
  48. it('links to source code with one GitHub integration', () => {
  49. renderGlobalModal();
  50. act(() =>
  51. openModal(modalProps => (
  52. <StacktraceLinkModal
  53. {...modalProps}
  54. filename={filename}
  55. closeModal={closeModal}
  56. integrations={[integration]}
  57. organization={org}
  58. project={project}
  59. onSubmit={onSubmit}
  60. />
  61. ))
  62. );
  63. expect(screen.getByText('Tell us where your source code is')).toBeInTheDocument();
  64. // Links to GitHub with one integration
  65. expect(screen.getByText('GitHub')).toBeInTheDocument();
  66. expect(screen.getByText('GitHub')).toHaveAttribute(
  67. 'href',
  68. 'https://github.com/test-integration'
  69. );
  70. expect(screen.getByRole('dialog')).toSnapshot();
  71. });
  72. it('closes modal after successful quick setup', async () => {
  73. MockApiClient.addMockResponse({
  74. url: `/projects/${org.slug}/${project.slug}/repo-path-parsing/`,
  75. method: 'POST',
  76. body: {...configData},
  77. });
  78. renderGlobalModal();
  79. act(() =>
  80. openModal(modalProps => (
  81. <StacktraceLinkModal
  82. {...modalProps}
  83. filename={filename}
  84. closeModal={closeModal}
  85. integrations={[integration]}
  86. organization={org}
  87. project={project}
  88. onSubmit={onSubmit}
  89. />
  90. ))
  91. );
  92. userEvent.paste(screen.getByRole('textbox', {name: 'Repository URL'}), 'sourceUrl');
  93. userEvent.click(screen.getByRole('button', {name: 'Save'}));
  94. await waitFor(() => {
  95. expect(closeModal).toHaveBeenCalled();
  96. });
  97. expect(onSubmit).toHaveBeenCalledTimes(1);
  98. });
  99. it('keeps modal open on unsuccessful quick setup', async () => {
  100. MockApiClient.addMockResponse({
  101. url: `/projects/${org.slug}/${project.slug}/repo-path-parsing/`,
  102. method: 'POST',
  103. body: {sourceUrl: ['Could not find repo']},
  104. statusCode: 400,
  105. });
  106. renderGlobalModal({context: TestStubs.routerContext()});
  107. act(() =>
  108. openModal(modalProps => (
  109. <StacktraceLinkModal
  110. {...modalProps}
  111. filename={filename}
  112. closeModal={closeModal}
  113. integrations={[integration]}
  114. organization={org}
  115. project={project}
  116. onSubmit={onSubmit}
  117. />
  118. ))
  119. );
  120. userEvent.type(
  121. screen.getByRole('textbox', {name: 'Repository URL'}),
  122. 'sourceUrl{enter}'
  123. );
  124. userEvent.click(screen.getByRole('button', {name: 'Save'}));
  125. await waitFor(() => {
  126. expect(closeModal).not.toHaveBeenCalled();
  127. });
  128. expect(
  129. screen.getByText('We don’t have access to that', {exact: false})
  130. ).toBeInTheDocument();
  131. expect(screen.getByRole('link', {name: 'add your repo.'})).toHaveAttribute(
  132. 'href',
  133. '/settings/org-slug/integrations/github/1/'
  134. );
  135. });
  136. it('displays suggestions from code mappings', async () => {
  137. MockApiClient.addMockResponse({
  138. url: `/organizations/${org.slug}/derive-code-mappings/`,
  139. body: [
  140. {
  141. filename: 'stack/root/file/stack/root/file/stack/root/file.py',
  142. repo_name: 'getsentry/codemap',
  143. repo_branch: 'master',
  144. stacktrace_root: '/stack/root',
  145. source_path: '/source/root/',
  146. },
  147. {
  148. filename: 'stack/root/file.py',
  149. repo_name: 'getsentry/codemap',
  150. repo_branch: 'master',
  151. stacktrace_root: '/stack/root',
  152. source_path: '/source/root/',
  153. },
  154. ],
  155. });
  156. MockApiClient.addMockResponse({
  157. url: `/projects/${org.slug}/${project.slug}/repo-path-parsing/`,
  158. method: 'POST',
  159. body: {...configData},
  160. });
  161. renderGlobalModal();
  162. act(() =>
  163. openModal(modalProps => (
  164. <StacktraceLinkModal
  165. {...modalProps}
  166. filename={filename}
  167. closeModal={closeModal}
  168. integrations={[integration]}
  169. organization={org}
  170. project={project}
  171. onSubmit={onSubmit}
  172. />
  173. ))
  174. );
  175. expect(
  176. await screen.findByText(
  177. 'Select from one of these suggestions or paste your URL below'
  178. )
  179. ).toBeInTheDocument();
  180. const suggestion =
  181. 'https://github.com/getsentry/codemap/blob/master/stack/root/file.py';
  182. expect(screen.getByText(suggestion)).toBeInTheDocument();
  183. expect(screen.getByRole('dialog')).toSnapshot();
  184. // Paste and save suggestion
  185. userEvent.paste(screen.getByRole('textbox', {name: 'Repository URL'}), suggestion);
  186. userEvent.click(screen.getByRole('button', {name: 'Save'}));
  187. await waitFor(() => {
  188. expect(closeModal).toHaveBeenCalled();
  189. });
  190. expect(analyticsSpy).toHaveBeenCalledWith(
  191. 'integrations.stacktrace_complete_setup',
  192. expect.objectContaining({
  193. is_suggestion: true,
  194. })
  195. );
  196. });
  197. });