addCodeOwnerModal.spec.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import {GitHubIntegrationFixture} from 'sentry-fixture/githubIntegration';
  2. import {OrganizationFixture} from 'sentry-fixture/organization';
  3. import {ProjectFixture} from 'sentry-fixture/project';
  4. import {RepositoryFixture} from 'sentry-fixture/repository';
  5. import {RepositoryProjectPathConfigFixture} from 'sentry-fixture/repositoryProjectPathConfig';
  6. import {render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
  7. import selectEvent from 'sentry-test/selectEvent';
  8. import {
  9. makeClosableHeader,
  10. makeCloseButton,
  11. ModalBody,
  12. ModalFooter,
  13. } from 'sentry/components/globalModal/components';
  14. import AddCodeOwnerModal from 'sentry/views/settings/project/projectOwnership/addCodeOwnerModal';
  15. describe('AddCodeOwnerModal', function () {
  16. const org = OrganizationFixture({features: ['integrations-codeowners']});
  17. const project = ProjectFixture();
  18. const integration = GitHubIntegrationFixture();
  19. const repo = RepositoryFixture({
  20. integrationId: integration.id,
  21. id: '5',
  22. name: 'example/hello-there',
  23. });
  24. const codeMapping = RepositoryProjectPathConfigFixture({
  25. project,
  26. repo,
  27. integration,
  28. stackRoot: 'stack/root',
  29. sourceRoot: 'source/root',
  30. });
  31. beforeEach(function () {
  32. MockApiClient.addMockResponse({
  33. url: `/organizations/${org.slug}/code-mappings/`,
  34. method: 'GET',
  35. body: [codeMapping],
  36. });
  37. MockApiClient.addMockResponse({
  38. url: `/organizations/${org.slug}/integrations/`,
  39. method: 'GET',
  40. body: [integration],
  41. });
  42. });
  43. it('renders', async function () {
  44. render(
  45. <AddCodeOwnerModal
  46. Body={ModalBody}
  47. closeModal={jest.fn()}
  48. CloseButton={makeCloseButton(jest.fn())}
  49. Header={makeClosableHeader(jest.fn())}
  50. Footer={ModalFooter}
  51. organization={org}
  52. project={project}
  53. />
  54. );
  55. await waitFor(() =>
  56. expect(screen.getByRole('button', {name: 'Add File'})).toBeDisabled()
  57. );
  58. });
  59. it('renders codeowner file', async function () {
  60. MockApiClient.addMockResponse({
  61. url: `/organizations/${org.slug}/code-mappings/${codeMapping.id}/codeowners/`,
  62. method: 'GET',
  63. body: {html_url: 'blah', filepath: 'CODEOWNERS', raw: '* @MeredithAnya\n'},
  64. });
  65. render(
  66. <AddCodeOwnerModal
  67. Body={ModalBody}
  68. closeModal={jest.fn()}
  69. CloseButton={makeCloseButton(jest.fn())}
  70. Header={makeClosableHeader(jest.fn())}
  71. Footer={ModalFooter}
  72. organization={org}
  73. project={project}
  74. />
  75. );
  76. await waitFor(() =>
  77. selectEvent.select(
  78. screen.getByText('--'),
  79. `Repo Name: ${codeMapping.repoName}, Stack Trace Root: ${codeMapping.stackRoot}, Source Code Root: ${codeMapping.sourceRoot}`
  80. )
  81. );
  82. expect(screen.getByTestId('icon-check-mark')).toBeInTheDocument();
  83. expect(screen.getByRole('button', {name: 'Preview File'})).toHaveAttribute(
  84. 'href',
  85. 'blah'
  86. );
  87. });
  88. it('renders no codeowner file found', async function () {
  89. MockApiClient.addMockResponse({
  90. url: `/organizations/${org.slug}/code-mappings/${codeMapping.id}/codeowners/`,
  91. method: 'GET',
  92. statusCode: 200,
  93. });
  94. render(
  95. <AddCodeOwnerModal
  96. Body={ModalBody}
  97. closeModal={jest.fn()}
  98. CloseButton={makeCloseButton(jest.fn())}
  99. Header={makeClosableHeader(jest.fn())}
  100. Footer={ModalFooter}
  101. organization={org}
  102. project={project}
  103. />
  104. );
  105. await waitFor(() =>
  106. selectEvent.select(
  107. screen.getByText('--'),
  108. `Repo Name: ${codeMapping.repoName}, Stack Trace Root: ${codeMapping.stackRoot}, Source Code Root: ${codeMapping.sourceRoot}`
  109. )
  110. );
  111. expect(screen.getByText('No codeowner file found.')).toBeInTheDocument();
  112. });
  113. it('adds codeowner file', async function () {
  114. MockApiClient.addMockResponse({
  115. url: `/organizations/${org.slug}/code-mappings/${codeMapping.id}/codeowners/`,
  116. method: 'GET',
  117. body: {html_url: 'blah', filepath: 'CODEOWNERS', raw: '* @MeredithAnya\n'},
  118. });
  119. const addFileRequest = MockApiClient.addMockResponse({
  120. url: `/projects/${org.slug}/${project.slug}/codeowners/`,
  121. method: 'POST',
  122. body: {},
  123. });
  124. const handleCloseModal = jest.fn();
  125. render(
  126. <AddCodeOwnerModal
  127. Body={ModalBody}
  128. closeModal={handleCloseModal}
  129. CloseButton={makeCloseButton(jest.fn())}
  130. Header={makeClosableHeader(jest.fn())}
  131. Footer={ModalFooter}
  132. organization={org}
  133. project={project}
  134. />
  135. );
  136. await waitFor(() =>
  137. selectEvent.select(
  138. screen.getByText('--'),
  139. `Repo Name: ${codeMapping.repoName}, Stack Trace Root: ${codeMapping.stackRoot}, Source Code Root: ${codeMapping.sourceRoot}`
  140. )
  141. );
  142. await userEvent.click(screen.getByRole('button', {name: 'Add File'}));
  143. await waitFor(() => {
  144. expect(addFileRequest).toHaveBeenCalledWith(
  145. `/projects/${org.slug}/${project.slug}/codeowners/`,
  146. expect.objectContaining({
  147. data: {codeMappingId: '2', raw: '* @MeredithAnya\n'},
  148. })
  149. );
  150. });
  151. expect(handleCloseModal).toHaveBeenCalled();
  152. });
  153. });