groupEventEntries.spec.tsx 13 KB

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