eventEntries.spec.tsx 12 KB

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