eventAttachments.spec.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import {initializeOrg} from 'sentry-test/initializeOrg';
  2. import {
  3. render,
  4. renderGlobalModal,
  5. screen,
  6. userEvent,
  7. waitFor,
  8. within,
  9. } from 'sentry-test/reactTestingLibrary';
  10. import {EventAttachments} from 'sentry/components/events/eventAttachments';
  11. describe('EventAttachments', function () {
  12. const {routerContext, organization, project} = initializeOrg({
  13. organization: {
  14. features: ['event-attachments'],
  15. orgRole: 'member',
  16. attachmentsRole: 'member',
  17. },
  18. } as any);
  19. const event = TestStubs.Event({metadata: {stripped_crash: false}});
  20. const props = {
  21. projectSlug: project.slug,
  22. event,
  23. };
  24. const attachmentsUrl = `/projects/${organization.slug}/${project.slug}/events/${event.id}/attachments/`;
  25. beforeEach(() => {
  26. MockApiClient.clearMockResponses();
  27. });
  28. it('shows attachments limit reached notice with stripped_crash: true', async function () {
  29. MockApiClient.addMockResponse({
  30. url: attachmentsUrl,
  31. body: [],
  32. });
  33. const strippedCrashEvent = {...event, metadata: {stripped_crash: true}};
  34. render(<EventAttachments {...props} event={strippedCrashEvent} />, {
  35. context: routerContext,
  36. organization,
  37. });
  38. expect(await screen.findByText('Attachments (0)')).toBeInTheDocument();
  39. await tick();
  40. expect(screen.getByRole('link', {name: 'View crashes'})).toHaveAttribute(
  41. 'href',
  42. '/organizations/org-slug/issues/1/attachments/?types=event.minidump&types=event.applecrashreport'
  43. );
  44. expect(screen.getByRole('link', {name: 'configure limit'})).toHaveAttribute(
  45. 'href',
  46. `/settings/org-slug/projects/${props.projectSlug}/security-and-privacy/`
  47. );
  48. expect(
  49. screen.getByText(
  50. 'Your limit of stored crash reports has been reached for this issue.',
  51. {exact: false}
  52. )
  53. ).toBeInTheDocument();
  54. });
  55. it('does not render anything if no attachments (nor stripped) are available', async function () {
  56. MockApiClient.addMockResponse({
  57. url: attachmentsUrl,
  58. body: [],
  59. });
  60. const {container} = render(
  61. <EventAttachments
  62. {...props}
  63. event={{...event, metadata: {stripped_crash: false}}}
  64. />,
  65. {context: routerContext, organization}
  66. );
  67. // No loading state to wait for
  68. await tick();
  69. expect(container).toBeEmptyDOMElement();
  70. });
  71. it('displays message when user lacks permission to preview an attachment', async function () {
  72. const {routerContext: newRouterContext, organization: orgWithWrongAttachmentRole} =
  73. initializeOrg({
  74. organization: {
  75. features: ['event-attachments'],
  76. orgRole: 'member',
  77. attachmentsRole: 'admin',
  78. },
  79. } as any);
  80. const attachment = TestStubs.EventAttachment({
  81. name: 'some_file.txt',
  82. headers: {
  83. 'Content-Type': 'text/plain',
  84. },
  85. mimetype: 'text/plain',
  86. size: 100,
  87. });
  88. MockApiClient.addMockResponse({
  89. url: attachmentsUrl,
  90. body: [attachment],
  91. });
  92. MockApiClient.addMockResponse({
  93. url: `/projects/org-slug/events/${event.id}/attachments/?download`,
  94. body: 'file contents',
  95. });
  96. render(<EventAttachments {...props} />, {
  97. context: newRouterContext,
  98. organization: orgWithWrongAttachmentRole,
  99. });
  100. expect(await screen.findByText('Attachments (1)')).toBeInTheDocument();
  101. expect(screen.getByRole('button', {name: /preview/i})).toBeDisabled();
  102. await userEvent.hover(screen.getByRole('button', {name: /preview/i}));
  103. await screen.findByText(/insufficient permissions to preview attachments/i);
  104. });
  105. it('can open attachment previews', async function () {
  106. const attachment = TestStubs.EventAttachment({
  107. name: 'some_file.txt',
  108. headers: {
  109. 'Content-Type': 'text/plain',
  110. },
  111. mimetype: 'text/plain',
  112. size: 100,
  113. });
  114. MockApiClient.addMockResponse({
  115. url: attachmentsUrl,
  116. body: [attachment],
  117. });
  118. MockApiClient.addMockResponse({
  119. url: '/projects/org-slug/project-slug/events/1/attachments/1/?download',
  120. body: 'file contents',
  121. });
  122. render(<EventAttachments {...props} />, {context: routerContext, organization});
  123. expect(await screen.findByText('Attachments (1)')).toBeInTheDocument();
  124. await userEvent.click(screen.getByRole('button', {name: /preview/i}));
  125. expect(screen.getByText('file contents')).toBeInTheDocument();
  126. });
  127. it('can delete attachments', async function () {
  128. const attachment1 = TestStubs.EventAttachment({
  129. id: '1',
  130. name: 'pic_1.png',
  131. });
  132. const attachment2 = TestStubs.EventAttachment({
  133. id: '2',
  134. name: 'pic_2.png',
  135. });
  136. MockApiClient.addMockResponse({
  137. url: attachmentsUrl,
  138. body: [attachment1, attachment2],
  139. });
  140. const deleteMock = MockApiClient.addMockResponse({
  141. url: '/projects/org-slug/project-slug/events/1/attachments/1/',
  142. method: 'DELETE',
  143. });
  144. render(<EventAttachments {...props} />, {context: routerContext, organization});
  145. renderGlobalModal();
  146. expect(await screen.findByText('Attachments (2)')).toBeInTheDocument();
  147. await userEvent.click(screen.getAllByRole('button', {name: 'Delete'})[0]);
  148. await userEvent.click(
  149. within(screen.getByRole('dialog')).getByRole('button', {name: /delete/i})
  150. );
  151. // Should make the delete request and remove the attachment optimistically
  152. await waitFor(() => {
  153. expect(deleteMock).toHaveBeenCalled();
  154. expect(screen.queryByTestId('pic_1.png')).not.toBeInTheDocument();
  155. });
  156. });
  157. });