eventEntries.spec.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. import {initializeData} from 'sentry-test/performance/initializePerformanceData';
  2. import {act, render, screen, userEvent, within} from 'sentry-test/reactTestingLibrary';
  3. import type {Error} from 'sentry/components/events/errors';
  4. import EventEntries from 'sentry/components/events/eventEntries';
  5. import {Group, IssueCategory} from 'sentry/types';
  6. import {EntryType, Event} from 'sentry/types/event';
  7. const {organization, project, router} = initializeData();
  8. async function renderComponent(event: Event, errors?: Array<Error>) {
  9. render(
  10. <EventEntries
  11. organization={organization}
  12. event={{...event, errors: errors ?? event.errors}}
  13. project={project}
  14. location={router.location}
  15. />
  16. );
  17. const alertSummaryInfo = await screen.findByTestId('event-error-alert');
  18. userEvent.click(alertSummaryInfo);
  19. const errorItems = await screen.findAllByTestId('event-error-item');
  20. return {alertSummaryInfo, errorItem: errorItems};
  21. }
  22. describe('EventEntries', function () {
  23. const event = TestStubs.Event();
  24. beforeEach(() => {
  25. MockApiClient.addMockResponse({
  26. url: `/projects/${organization.slug}/${project.slug}/events/${event.id}/grouping-info/`,
  27. body: {},
  28. });
  29. MockApiClient.addMockResponse({
  30. url: `/projects/${organization.slug}/${project.slug}/files/dsyms/`,
  31. body: [],
  32. });
  33. MockApiClient.addMockResponse({
  34. method: 'GET',
  35. url: `/projects/${organization.slug}/${project.slug}/events/${event.id}/committers/`,
  36. body: {
  37. committers: [],
  38. },
  39. });
  40. });
  41. afterEach(() => {
  42. MockApiClient.clearMockResponses();
  43. });
  44. describe('EventError', function () {
  45. it('renders', async function () {
  46. const errors: Array<Error> = [
  47. {
  48. type: 'invalid_data',
  49. data: {
  50. name: 'logentry',
  51. },
  52. message: 'no message present',
  53. },
  54. {
  55. type: 'invalid_data',
  56. data: {
  57. name: 'breadcrumbs.values.2.data',
  58. },
  59. message: 'expected an object',
  60. },
  61. ];
  62. const {alertSummaryInfo, errorItem} = await renderComponent(event, errors);
  63. expect(alertSummaryInfo).toHaveTextContent(
  64. `There were ${errors.length} problems processing this event`
  65. );
  66. expect(errorItem.length).toBe(2);
  67. expect(screen.getByText(errors[0].data?.name!)).toBeInTheDocument();
  68. expect(screen.getByText(errors[1].data?.name!)).toBeInTheDocument();
  69. });
  70. describe('Proguard erros', function () {
  71. const proGuardUuid = 'a59c8fcc-2f27-49f8-af9e-02661fc3e8d7';
  72. it('Missing mapping file', async function () {
  73. const newEvent = {
  74. ...event,
  75. platform: 'java',
  76. entries: [
  77. {
  78. type: EntryType.DEBUGMETA,
  79. data: {
  80. images: [{type: 'proguard', uuid: proGuardUuid}],
  81. },
  82. },
  83. ],
  84. };
  85. await act(async () => {
  86. const {errorItem, alertSummaryInfo} = await renderComponent(newEvent);
  87. expect(alertSummaryInfo).toHaveTextContent(
  88. 'There was 1 problem processing this event'
  89. );
  90. expect(errorItem.length).toBe(1);
  91. expect(
  92. screen.getByText('A proguard mapping file was missing.')
  93. ).toBeInTheDocument();
  94. userEvent.click(screen.getByRole('button', {name: 'Expand'}));
  95. expect(await screen.findByText(proGuardUuid)).toBeInTheDocument();
  96. });
  97. });
  98. it("Don't display extra proguard errors, if the entry error of an event has an error of type 'proguard_missing_mapping'", async function () {
  99. const newEvent = {
  100. ...event,
  101. platform: 'java',
  102. entries: [
  103. {
  104. type: EntryType.DEBUGMETA,
  105. data: {
  106. images: [{type: 'proguard', uuid: proGuardUuid}],
  107. },
  108. },
  109. ],
  110. errors: [
  111. {
  112. type: 'proguard_missing_mapping',
  113. message: 'A proguard mapping file was missing.',
  114. data: {mapping_uuid: proGuardUuid},
  115. },
  116. ],
  117. };
  118. const {alertSummaryInfo, errorItem} = await renderComponent(newEvent);
  119. expect(alertSummaryInfo).toHaveTextContent(
  120. 'There was 1 problem processing this event'
  121. );
  122. expect(errorItem.length).toBe(1);
  123. expect(
  124. screen.getByText('A proguard mapping file was missing.')
  125. ).toBeInTheDocument();
  126. userEvent.click(screen.getByRole('button', {name: 'Expand'}));
  127. expect(await screen.findByText(proGuardUuid)).toBeInTheDocument();
  128. });
  129. describe('ProGuard Plugin seems to not be correctly configured', function () {
  130. it('find minified data in the exception entry', async function () {
  131. const newEvent = {
  132. ...event,
  133. platform: 'java',
  134. entries: [
  135. {
  136. type: 'exception',
  137. data: {
  138. values: [
  139. {
  140. stacktrace: {
  141. frames: [
  142. {
  143. function: null,
  144. colNo: null,
  145. vars: {},
  146. symbol: null,
  147. module: 'a.$a.a.a',
  148. },
  149. ],
  150. framesOmitted: null,
  151. registers: null,
  152. hasSystemFrames: false,
  153. },
  154. module: null,
  155. rawStacktrace: null,
  156. mechanism: null,
  157. threadId: null,
  158. value: 'Unexpected token else',
  159. type: 'SyntaxError',
  160. },
  161. ],
  162. excOmitted: null,
  163. hasSystemFrames: false,
  164. },
  165. },
  166. ],
  167. };
  168. const {alertSummaryInfo, errorItem} = await renderComponent(newEvent);
  169. expect(alertSummaryInfo).toHaveTextContent(
  170. 'There was 1 problem processing this event'
  171. );
  172. expect(errorItem.length).toBe(1);
  173. expect(
  174. screen.getByText('Some frames appear to be minified. Did you configure the')
  175. ).toBeInTheDocument();
  176. expect(
  177. screen.getByText('No additional details are available for this frame.')
  178. ).toBeInTheDocument();
  179. });
  180. it('find minified data in the threads entry', async function () {
  181. const newEvent = {
  182. ...event,
  183. platform: 'java',
  184. entries: [
  185. {
  186. type: 'exception',
  187. data: {
  188. values: [
  189. {
  190. stacktrace: {
  191. frames: [
  192. {
  193. function: null,
  194. colNo: null,
  195. vars: {},
  196. symbol: null,
  197. module: 'a.$a.a.a',
  198. },
  199. ],
  200. framesOmitted: null,
  201. registers: null,
  202. hasSystemFrames: false,
  203. },
  204. module: null,
  205. rawStacktrace: null,
  206. mechanism: null,
  207. threadId: null,
  208. value: 'Unexpected token else',
  209. type: 'SyntaxError',
  210. },
  211. ],
  212. excOmitted: null,
  213. hasSystemFrames: false,
  214. },
  215. },
  216. {
  217. type: 'threads',
  218. data: {
  219. values: [
  220. {
  221. stacktrace: {
  222. frames: [
  223. {
  224. function: 'start',
  225. package: 'libdyld.dylib',
  226. module: 'a.$a.a.a',
  227. },
  228. {
  229. function: 'main',
  230. package: 'iOS-Swift',
  231. module: '',
  232. },
  233. ],
  234. },
  235. },
  236. ],
  237. },
  238. },
  239. ],
  240. };
  241. const {alertSummaryInfo, errorItem} = await renderComponent(newEvent);
  242. expect(alertSummaryInfo).toHaveTextContent(
  243. 'There was 1 problem processing this event'
  244. );
  245. expect(errorItem.length).toBe(1);
  246. expect(
  247. screen.getByText('Some frames appear to be minified. Did you configure the')
  248. ).toBeInTheDocument();
  249. expect(screen.getByText('Sentry Gradle Plugin')).toBeInTheDocument();
  250. });
  251. });
  252. });
  253. });
  254. describe('Rendering', function () {
  255. it('renders the Resources section for Performance Issues', function () {
  256. const group: Group = TestStubs.Group({issueCategory: IssueCategory.PERFORMANCE});
  257. const newEvent = {
  258. ...event,
  259. entries: [{type: EntryType.SPANS, data: []}],
  260. };
  261. render(
  262. <EventEntries
  263. organization={organization}
  264. event={newEvent}
  265. project={project}
  266. location={router.location}
  267. group={group}
  268. />,
  269. {organization}
  270. );
  271. const resourcesHeadingText = screen.getByRole('heading', {
  272. name: /resources and whatever/i,
  273. });
  274. expect(resourcesHeadingText).toBeInTheDocument();
  275. });
  276. it('injects the resources section in the correct spot', function () {
  277. const group: Group = TestStubs.Group({issueCategory: IssueCategory.PERFORMANCE});
  278. group.issueCategory = IssueCategory.PERFORMANCE;
  279. const sampleBreadcrumb = {
  280. type: 'default',
  281. timestamp: '2022-09-19T19:29:32.261000Z',
  282. level: 'info',
  283. message: 'span.css-1hs7lfd.e1b8u3ky1 > svg',
  284. category: 'ui.click',
  285. data: null,
  286. event_id: null,
  287. };
  288. const newEvent = {
  289. ...event,
  290. title: 'test',
  291. perfProblem: {parentSpanIds: ['a'], causeSpanIds: ['a'], offenderSpanIds: ['a']},
  292. entries: [
  293. {type: EntryType.SPANS, data: [{span_id: 'a'}]},
  294. {type: EntryType.BREADCRUMBS, data: {values: [sampleBreadcrumb]}},
  295. {type: EntryType.REQUEST, data: {}},
  296. ],
  297. };
  298. render(
  299. <EventEntries
  300. organization={organization}
  301. event={newEvent}
  302. project={project}
  303. location={router.location}
  304. group={group}
  305. />
  306. );
  307. const eventEntriesContainer = screen.getByTestId('event-entries-loading-false');
  308. const spanEvidenceHeading = within(eventEntriesContainer).getByRole('heading', {
  309. name: /span evidence/i,
  310. });
  311. const breadcrumbsHeading = within(eventEntriesContainer).getByRole('heading', {
  312. name: /breadcrumbs/i,
  313. });
  314. const resourcesHeadingText = screen.getByRole('heading', {
  315. name: /resources and whatever/i,
  316. });
  317. expect(spanEvidenceHeading).toBeInTheDocument();
  318. expect(breadcrumbsHeading).toBeInTheDocument();
  319. expect(resourcesHeadingText).toBeInTheDocument();
  320. expect(
  321. within(eventEntriesContainer.children[0] as HTMLElement).getByRole('heading', {
  322. name: /span evidence/i,
  323. })
  324. ).toBeInTheDocument();
  325. expect(
  326. within(eventEntriesContainer.children[1] as HTMLElement).getByRole('heading', {
  327. name: /breadcrumbs/i,
  328. })
  329. ).toBeInTheDocument();
  330. expect(
  331. within(eventEntriesContainer.children[2] as HTMLElement).getByRole('heading', {
  332. name: /resources and whatever/i,
  333. })
  334. ).toBeInTheDocument();
  335. });
  336. });
  337. });