threadsV2.spec.tsx 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045
  1. import {initializeOrg} from 'sentry-test/initializeOrg';
  2. import {render, screen, userEvent, within} from 'sentry-test/reactTestingLibrary';
  3. import ThreadsV2 from 'sentry/components/events/interfaces/threadsV2';
  4. import {EventOrGroupType} from 'sentry/types';
  5. import {EntryType, Event} from 'sentry/types/event';
  6. describe('ThreadsV2', function () {
  7. const {project, organization} = initializeOrg();
  8. const org = {...organization, features: ['native-stack-trace-v2']};
  9. describe('non native platform', function () {
  10. describe('other platform', function () {
  11. const event: Event = {
  12. id: '020eb33f6ce64ed6adc60f8993535816',
  13. groupID: '68',
  14. eventID: '020eb33f6ce64ed6adc60f8993535816',
  15. projectID: '2',
  16. size: 3481,
  17. entries: [
  18. {
  19. data: {
  20. values: [
  21. {
  22. type: 'ZeroDivisionError',
  23. value: 'divided by 0',
  24. mechanism: null,
  25. threadId: null,
  26. module: '',
  27. stacktrace: {
  28. frames: [
  29. {
  30. filename: 'puma (3.12.6) lib/puma/thread_pool.rb',
  31. absPath: 'puma (3.12.6) lib/puma/thread_pool.rb',
  32. module: null,
  33. package: null,
  34. platform: null,
  35. instructionAddr: null,
  36. symbolAddr: null,
  37. function: 'block in spawn_thread',
  38. rawFunction: null,
  39. symbol: null,
  40. context: [],
  41. lineNo: 135,
  42. colNo: null,
  43. inApp: false,
  44. trust: null,
  45. errors: null,
  46. vars: null,
  47. },
  48. {
  49. filename: 'puma (3.12.6) lib/puma/server.rb',
  50. absPath: 'puma (3.12.6) lib/puma/server.rb',
  51. module: null,
  52. package: null,
  53. platform: null,
  54. instructionAddr: null,
  55. symbolAddr: null,
  56. function: 'block in run',
  57. rawFunction: null,
  58. symbol: null,
  59. context: [],
  60. lineNo: 334,
  61. colNo: null,
  62. inApp: false,
  63. trust: null,
  64. errors: null,
  65. vars: null,
  66. },
  67. {
  68. filename: 'sentry/controllers/welcome_controller.rb',
  69. absPath: 'sentry/controllers/welcome_controller.rb',
  70. module: null,
  71. package: null,
  72. platform: null,
  73. instructionAddr: null,
  74. symbolAddr: null,
  75. function: 'index',
  76. rawFunction: null,
  77. symbol: null,
  78. context: [
  79. [2, ' before_action :set_sentry_context\n'],
  80. [3, '\n'],
  81. [4, ' def index\n'],
  82. [5, ' 1 / 0\n'],
  83. [6, ' end\n'],
  84. [7, '\n'],
  85. [8, ' def view_error\n'],
  86. ],
  87. lineNo: 5,
  88. colNo: null,
  89. inApp: true,
  90. trust: null,
  91. errors: null,
  92. vars: null,
  93. minGroupingLevel: 1,
  94. },
  95. {
  96. filename: 'sentry/controllers/welcome_controller.rb',
  97. absPath: 'sentry/controllers/welcome_controller.rb',
  98. module: null,
  99. package: null,
  100. platform: null,
  101. instructionAddr: null,
  102. symbolAddr: null,
  103. function: '/',
  104. rawFunction: null,
  105. symbol: null,
  106. context: [
  107. [2, ' before_action :set_sentry_context\n'],
  108. [3, '\n'],
  109. [4, ' def index\n'],
  110. [5, ' 1 / 0\n'],
  111. [6, ' end\n'],
  112. [7, '\n'],
  113. [8, ' def view_error\n'],
  114. ],
  115. lineNo: 5,
  116. colNo: null,
  117. inApp: true,
  118. trust: null,
  119. errors: null,
  120. vars: null,
  121. minGroupingLevel: 0,
  122. },
  123. ],
  124. framesOmitted: null,
  125. registers: null,
  126. hasSystemFrames: true,
  127. },
  128. rawStacktrace: null,
  129. frames: null,
  130. },
  131. ],
  132. hasSystemFrames: true,
  133. excOmitted: null,
  134. },
  135. type: EntryType.EXCEPTION,
  136. },
  137. {
  138. data: {
  139. values: [
  140. {
  141. id: 13920,
  142. current: true,
  143. crashed: true,
  144. name: 'puma 002',
  145. stacktrace: null,
  146. rawStacktrace: null,
  147. },
  148. ],
  149. },
  150. type: EntryType.THREADS,
  151. },
  152. ],
  153. dist: null,
  154. message: '',
  155. title: 'ZeroDivisionError: divided by 0',
  156. location: 'sentry/controllers/welcome_controller.rb',
  157. user: null,
  158. contexts: {},
  159. sdk: null,
  160. context: {},
  161. packages: {},
  162. type: EventOrGroupType.ERROR,
  163. metadata: {
  164. display_title_with_tree_label: false,
  165. filename: 'sentry/controllers/welcome_controller.rb',
  166. finest_tree_label: [
  167. {filebase: 'welcome_controller.rb', function: '/'},
  168. {filebase: 'welcome_controller.rb', function: 'index'},
  169. ],
  170. function: '/',
  171. type: 'ZeroDivisionError',
  172. value: 'divided by 0',
  173. },
  174. tags: [{key: 'level', value: 'error'}],
  175. platform: 'other',
  176. dateReceived: '2021-10-28T12:28:22.318469Z',
  177. errors: [],
  178. crashFile: null,
  179. culprit: 'sentry/controllers/welcome_controller.rb in /',
  180. dateCreated: '2021-10-28T12:28:22.318469Z',
  181. fingerprints: ['58f1f47bea5239ea25397888dc9253d1'],
  182. groupingConfig: {
  183. enhancements: 'eJybzDRxY25-UmZOqpWRgZGhroGJroHRBABbUQb_',
  184. id: 'mobile:2021-02-12',
  185. },
  186. release: null,
  187. userReport: null,
  188. sdkUpdates: [],
  189. nextEventID: null,
  190. previousEventID: null,
  191. };
  192. const props: React.ComponentProps<typeof ThreadsV2> = {
  193. type: EntryType.THREADS,
  194. data: event.entries[1].data as React.ComponentProps<typeof ThreadsV2>['data'],
  195. event,
  196. groupingCurrentLevel: 0,
  197. hasHierarchicalGrouping: true,
  198. projectId: project.id,
  199. };
  200. it('renders', function () {
  201. const {container} = render(<ThreadsV2 {...props} />, {
  202. organization: org,
  203. });
  204. // Title
  205. expect(screen.getByRole('heading', {name: 'Stack Trace'})).toBeInTheDocument();
  206. // Actions
  207. expect(screen.getByRole('checkbox', {name: 'Raw'})).toBeInTheDocument();
  208. expect(screen.getByRole('checkbox', {name: 'Raw'})).not.toBeChecked();
  209. expect(
  210. screen.getByRole('button', {name: 'Options 0 Active'})
  211. ).toBeInTheDocument();
  212. expect(
  213. screen.getByRole('button', {name: 'Sort By Recent first'})
  214. ).toBeInTheDocument();
  215. // Stack Trace
  216. expect(
  217. screen.getByRole('heading', {name: 'ZeroDivisionError'})
  218. ).toBeInTheDocument();
  219. expect(screen.getByText('divided by 0')).toBeInTheDocument();
  220. expect(screen.getByTestId('stack-trace-content-v2')).toBeInTheDocument();
  221. expect(screen.queryAllByTestId('stack-trace-frame')).toHaveLength(3);
  222. expect(container).toSnapshot();
  223. });
  224. it('toggle raw button', function () {
  225. render(<ThreadsV2 {...props} />, {organization: org});
  226. expect(screen.getByRole('checkbox', {name: 'Raw'})).not.toBeChecked();
  227. userEvent.click(screen.getByRole('checkbox', {name: 'Raw'}));
  228. expect(screen.getByRole('checkbox', {name: 'Raw'})).toBeChecked();
  229. // Actions must not be rendered
  230. expect(
  231. screen.queryByRole('button', {name: 'Options 1 Active'})
  232. ).not.toBeInTheDocument();
  233. expect(
  234. screen.queryByRole('button', {name: 'Sort By Recent first'})
  235. ).not.toBeInTheDocument();
  236. // Raw content is displayed
  237. expect(screen.queryByRole('list', {name: 'Stack trace'})).not.toBeInTheDocument();
  238. expect(screen.getByTestId('raw-stack-trace')).toBeInTheDocument();
  239. });
  240. it('toggle sort by', function () {
  241. render(<ThreadsV2 {...props} />, {organization: org});
  242. expect(
  243. within(screen.getAllByTestId('stack-trace-frame')[0]).getByText(
  244. 'sentry/controllers/welcome_controller.rb'
  245. )
  246. ).toBeInTheDocument();
  247. userEvent.click(screen.getByRole('button', {name: 'Sort By Recent first'}));
  248. expect(screen.queryAllByLabelText('Sort option')).toHaveLength(2);
  249. expect(screen.queryAllByLabelText('Sort option')[0]).toHaveTextContent(
  250. 'Recent first'
  251. );
  252. expect(screen.queryAllByLabelText('Sort option')[1]).toHaveTextContent(
  253. 'Recent last'
  254. );
  255. userEvent.click(screen.getByText('Recent last'));
  256. expect(
  257. screen.getByRole('button', {name: 'Sort By Recent last'})
  258. ).toBeInTheDocument();
  259. expect(
  260. within(screen.getAllByTestId('stack-trace-frame')[0]).getByText(
  261. 'puma (3.12.6) lib/puma/server.rb'
  262. )
  263. ).toBeInTheDocument();
  264. userEvent.click(screen.getByRole('button', {name: 'Sort By Recent last'}));
  265. userEvent.click(screen.getByText('Recent first'));
  266. expect(
  267. screen.getByRole('button', {name: 'Sort By Recent first'})
  268. ).toBeInTheDocument();
  269. });
  270. it('check options', async function () {
  271. render(<ThreadsV2 {...props} />, {organization: org});
  272. expect(
  273. screen.getByRole('button', {name: 'Options 0 Active'})
  274. ).toBeInTheDocument();
  275. userEvent.click(screen.getByRole('button', {name: 'Options 0 Active'}));
  276. expect(screen.queryAllByLabelText('Display option')).toHaveLength(2);
  277. const displayOption0 = screen.queryAllByLabelText('Display option')[0];
  278. expect(displayOption0).toHaveTextContent('Minified');
  279. expect(within(displayOption0).getByRole('checkbox')).not.toBeChecked();
  280. expect(within(displayOption0).getByRole('checkbox')).toHaveAttribute(
  281. 'aria-disabled',
  282. 'true'
  283. );
  284. // hover over the Minified option
  285. userEvent.hover(within(displayOption0).getByText('Minified'));
  286. expect(
  287. await screen.findByText('Minified version not available')
  288. ).toBeInTheDocument();
  289. const displayOption1 = screen.queryAllByLabelText('Display option')[1];
  290. expect(displayOption1).toHaveTextContent('Full Stack Trace');
  291. expect(within(displayOption1).getByRole('checkbox')).not.toBeChecked();
  292. expect(within(displayOption1).getByRole('checkbox')).toHaveAttribute(
  293. 'aria-disabled',
  294. 'false'
  295. );
  296. expect(screen.getByTestId('stack-trace-content-v2')).toBeInTheDocument();
  297. expect(screen.queryAllByTestId('stack-trace-frame')).toHaveLength(3);
  298. // Check Full Stack Trace
  299. userEvent.click(screen.queryAllByLabelText('Display option')[1]);
  300. expect(
  301. within(screen.queryAllByLabelText('Display option')[1]).getByRole('checkbox')
  302. ).toBeChecked();
  303. // Display more frames
  304. expect(screen.queryAllByTestId('stack-trace-frame')).toHaveLength(4);
  305. });
  306. });
  307. });
  308. describe('native platform', function () {
  309. describe('cocoa', function () {
  310. const event: Event = {
  311. id: 'bfe4379d82934b2b91d70b1167bcae8d',
  312. groupID: '24',
  313. eventID: 'bfe4379d82934b2b91d70b1167bcae8d',
  314. projectID: '2',
  315. size: 89101,
  316. entries: [
  317. {
  318. data: {
  319. values: [
  320. {
  321. stacktrace: {
  322. frames: [
  323. {
  324. filename: null,
  325. absPath: null,
  326. module: null,
  327. package:
  328. '/private/var/containers/Bundle/Application/575F9D39-D486-4728-B035-84923A0BE206/sentry-ios-cocoapods.app/Frameworks/Sentry.framework/Sentry',
  329. platform: null,
  330. instructionAddr: '0x1000adb08',
  331. symbolAddr: '0x1000ad5c4',
  332. function:
  333. '__44-[SentryBreadcrumbTracker swizzleSendAction]_block_invoke_2',
  334. rawFunction: null,
  335. symbol: null,
  336. context: [],
  337. lineNo: null,
  338. colNo: null,
  339. inApp: false,
  340. trust: null,
  341. errors: null,
  342. vars: null,
  343. },
  344. {
  345. filename: null,
  346. absPath: null,
  347. module: null,
  348. package: '/System/Library/Frameworks/UIKit.framework/UIKit',
  349. platform: null,
  350. instructionAddr: '0x197885c54',
  351. symbolAddr: '0x197885bf4',
  352. function: '<redacted>',
  353. rawFunction: null,
  354. symbol: null,
  355. context: [],
  356. lineNo: null,
  357. colNo: null,
  358. inApp: false,
  359. trust: null,
  360. errors: null,
  361. vars: null,
  362. },
  363. {
  364. filename: null,
  365. absPath: null,
  366. module: null,
  367. package: '/System/Library/Frameworks/UIKit.framework/UIKit',
  368. platform: null,
  369. instructionAddr: '0x197885c54',
  370. symbolAddr: '0x197885bf4',
  371. function: '<redacted>',
  372. rawFunction: null,
  373. symbol: null,
  374. context: [],
  375. lineNo: null,
  376. colNo: null,
  377. inApp: false,
  378. trust: null,
  379. errors: null,
  380. vars: null,
  381. },
  382. {
  383. filename: null,
  384. absPath: null,
  385. module: null,
  386. package:
  387. '/var/containers/Bundle/Application/575F9D39-D486-4728-B035-84923A0BE206/sentry-ios-cocoapods.app/sentry-ios-cocoapods',
  388. platform: null,
  389. instructionAddr: '0x10008c5ac',
  390. symbolAddr: '0x10008c500',
  391. function: 'ViewController.causeCrash',
  392. rawFunction: 'ViewController.causeCrash(Any) -> ()',
  393. symbol: null,
  394. context: [],
  395. lineNo: null,
  396. colNo: null,
  397. inApp: true,
  398. trust: null,
  399. errors: null,
  400. vars: null,
  401. },
  402. {
  403. filename: null,
  404. absPath: null,
  405. module: null,
  406. package:
  407. '/private/var/containers/Bundle/Application/575F9D39-D486-4728-B035-84923A0BE206/sentry-ios-cocoapods.app/Frameworks/Sentry.framework/Sentry',
  408. platform: null,
  409. instructionAddr: '0x1000b0bfc',
  410. symbolAddr: '0x1000b0be4',
  411. function: '-[SentryClient crash]',
  412. rawFunction: null,
  413. symbol: null,
  414. context: [],
  415. lineNo: null,
  416. colNo: null,
  417. inApp: false,
  418. trust: null,
  419. errors: null,
  420. vars: null,
  421. },
  422. ],
  423. framesOmitted: null,
  424. hasSystemFrames: true,
  425. registers: {
  426. cpsr: '0x60000000',
  427. fp: '0x16fd79870',
  428. lr: '0x10008c5ac',
  429. pc: '0x1000b0bfc',
  430. sp: '0x16fd79810',
  431. x0: '0x1700eee80',
  432. x1: '0x10008e49c',
  433. x10: '0x1b7886ff0',
  434. x11: '0x59160100591680',
  435. x12: '0x0',
  436. x13: '0x591600',
  437. x14: '0x591700',
  438. x15: '0x5916c0',
  439. x16: '0x591601',
  440. x17: '0x1000b0be4',
  441. x18: '0x0',
  442. x19: '0x1740eb200',
  443. x2: '0x0',
  444. x20: '0x10fd08db0',
  445. x21: '0x10008e4de',
  446. x22: '0x10fe0a470',
  447. x23: '0x10fe0a470',
  448. x24: '0x174008ba0',
  449. x25: '0x0',
  450. x26: '0x19838eb61',
  451. x27: '0x1',
  452. x28: '0x170046c60',
  453. x29: '0x16fd79870',
  454. x3: '0x1740eb200',
  455. x4: '0x1740eb200',
  456. x5: '0x1740eb200',
  457. x6: '0x0',
  458. x7: '0x2',
  459. x8: '0x0',
  460. x9: '0x1b7886fec',
  461. },
  462. },
  463. threadId: 0,
  464. module: null,
  465. mechanism: null,
  466. rawStacktrace: null,
  467. value:
  468. 'Attempted to dereference null pointer.\nOriginated at or in a subcall of ViewController.causeCrash(Any) -> ()',
  469. type: 'EXC_BAD_ACCESS',
  470. },
  471. ],
  472. hasSystemFrames: true,
  473. excOmitted: null,
  474. },
  475. type: EntryType.EXCEPTION,
  476. },
  477. {
  478. data: {
  479. values: [
  480. {
  481. id: 0,
  482. current: false,
  483. crashed: true,
  484. name: null,
  485. stacktrace: {
  486. frames: [
  487. {
  488. filename: null,
  489. absPath: null,
  490. module: null,
  491. package: '/System/Library/Frameworks/UIKit.framework/UIKit',
  492. platform: null,
  493. instructionAddr: '0x197885c54',
  494. symbolAddr: '0x197885bf4',
  495. function: '<redacted>',
  496. rawFunction: null,
  497. symbol: null,
  498. context: [],
  499. lineNo: null,
  500. colNo: null,
  501. inApp: false,
  502. trust: null,
  503. errors: null,
  504. vars: null,
  505. },
  506. {
  507. filename: null,
  508. absPath: null,
  509. module: null,
  510. package: '/System/Library/Frameworks/UIKit.framework/UIKit',
  511. platform: null,
  512. instructionAddr: '0x197885c54',
  513. symbolAddr: '0x197885bf4',
  514. function: '<redacted>',
  515. rawFunction: null,
  516. symbol: null,
  517. context: [],
  518. lineNo: null,
  519. colNo: null,
  520. inApp: false,
  521. trust: null,
  522. errors: null,
  523. vars: null,
  524. },
  525. {
  526. filename: null,
  527. absPath: null,
  528. module: null,
  529. package:
  530. '/var/containers/Bundle/Application/575F9D39-D486-4728-B035-84923A0BE206/sentry-ios-cocoapods.app/sentry-ios-cocoapods',
  531. platform: null,
  532. instructionAddr: '0x10008c630',
  533. symbolAddr: '0x10008c5e8',
  534. function: 'ViewController.causeCrash',
  535. rawFunction: '@objc ViewController.causeCrash(Any) -> ()',
  536. symbol: null,
  537. context: [],
  538. lineNo: null,
  539. colNo: null,
  540. inApp: true,
  541. trust: null,
  542. errors: null,
  543. vars: null,
  544. },
  545. {
  546. filename: null,
  547. absPath: null,
  548. module: null,
  549. package:
  550. '/var/containers/Bundle/Application/575F9D39-D486-4728-B035-84923A0BE206/sentry-ios-cocoapods.app/sentry-ios-cocoapods',
  551. platform: null,
  552. instructionAddr: '0x10008c5ac',
  553. symbolAddr: '0x10008c500',
  554. function: 'ViewController.causeCrash',
  555. rawFunction: 'ViewController.causeCrash(Any) -> ()',
  556. symbol: null,
  557. context: [],
  558. lineNo: null,
  559. colNo: null,
  560. inApp: true,
  561. trust: null,
  562. errors: null,
  563. vars: null,
  564. },
  565. {
  566. filename: null,
  567. absPath: null,
  568. module: null,
  569. package:
  570. '/private/var/containers/Bundle/Application/575F9D39-D486-4728-B035-84923A0BE206/sentry-ios-cocoapods.app/Frameworks/Sentry.framework/Sentry',
  571. platform: null,
  572. instructionAddr: '0x1000b0bfc',
  573. symbolAddr: '0x1000b0be4',
  574. function: '-[SentryClient crash]',
  575. rawFunction: null,
  576. symbol: null,
  577. context: [],
  578. lineNo: null,
  579. colNo: null,
  580. inApp: false,
  581. trust: null,
  582. errors: null,
  583. vars: null,
  584. },
  585. ],
  586. framesOmitted: null,
  587. registers: {
  588. cpsr: '0x60000000',
  589. fp: '0x16fd79870',
  590. lr: '0x10008c5ac',
  591. pc: '0x1000b0bfc',
  592. sp: '0x16fd79810',
  593. x0: '0x1700eee80',
  594. x1: '0x10008e49c',
  595. x10: '0x1b7886ff0',
  596. x11: '0x59160100591680',
  597. x12: '0x0',
  598. x13: '0x591600',
  599. x14: '0x591700',
  600. x15: '0x5916c0',
  601. x16: '0x591601',
  602. x17: '0x1000b0be4',
  603. x18: '0x0',
  604. x19: '0x1740eb200',
  605. x2: '0x0',
  606. x20: '0x10fd08db0',
  607. x21: '0x10008e4de',
  608. x22: '0x10fe0a470',
  609. x23: '0x10fe0a470',
  610. x24: '0x174008ba0',
  611. x25: '0x0',
  612. x26: '0x19838eb61',
  613. x27: '0x1',
  614. x28: '0x170046c60',
  615. x29: '0x16fd79870',
  616. x3: '0x1740eb200',
  617. x4: '0x1740eb200',
  618. x5: '0x1740eb200',
  619. x6: '0x0',
  620. x7: '0x2',
  621. x8: '0x0',
  622. x9: '0x1b7886fec',
  623. },
  624. hasSystemFrames: true,
  625. },
  626. rawStacktrace: null,
  627. },
  628. {
  629. id: 1,
  630. current: false,
  631. crashed: false,
  632. name: null,
  633. stacktrace: {
  634. frames: [
  635. {
  636. filename: null,
  637. absPath: null,
  638. module: null,
  639. package: '/usr/lib/system/libsystem_pthread.dylib',
  640. platform: null,
  641. instructionAddr: '0x1907df1a4',
  642. symbolAddr: '0x1907decb8',
  643. function: '_pthread_wqthread',
  644. rawFunction: null,
  645. symbol: null,
  646. context: [],
  647. lineNo: null,
  648. colNo: null,
  649. inApp: false,
  650. trust: null,
  651. errors: null,
  652. vars: null,
  653. },
  654. {
  655. filename: null,
  656. absPath: null,
  657. module: null,
  658. package: '/usr/lib/system/libsystem_kernel.dylib',
  659. platform: null,
  660. instructionAddr: '0x190719a88',
  661. symbolAddr: '0x190719a80',
  662. function: '__workq_kernreturn',
  663. rawFunction: null,
  664. symbol: null,
  665. context: [],
  666. lineNo: null,
  667. colNo: null,
  668. inApp: false,
  669. trust: null,
  670. errors: null,
  671. vars: null,
  672. },
  673. ],
  674. framesOmitted: null,
  675. registers: {
  676. cpsr: '0x0',
  677. fp: '0x16dfcaf70',
  678. lr: '0x1907df1a4',
  679. pc: '0x190719a88',
  680. sp: '0x16dfcaef0',
  681. x0: '0x4',
  682. x1: '0x0',
  683. x10: '0x1',
  684. x11: '0x0',
  685. x12: '0x30000400000d03',
  686. x13: '0x0',
  687. x14: '0x1740e9edc',
  688. x15: '0xfffffff100000000',
  689. x16: '0x170',
  690. x17: '0x191619c10',
  691. x18: '0x0',
  692. x19: '0x16dfcb000',
  693. x2: '0x0',
  694. x20: '0x19',
  695. x21: '0x270019',
  696. x22: '0x0',
  697. x23: '0xd03',
  698. x24: '0x1b788d000',
  699. x25: '0x1b788d000',
  700. x26: '0x0',
  701. x27: '0x80000000',
  702. x28: '0x800010ff',
  703. x29: '0x16dfcaf70',
  704. x3: '0x0',
  705. x4: '0x80010ff',
  706. x5: '0x0',
  707. x6: '0x0',
  708. x7: '0x0',
  709. x8: '0x2',
  710. x9: '0x17409b4ec',
  711. },
  712. hasSystemFrames: false,
  713. },
  714. rawStacktrace: null,
  715. },
  716. ],
  717. },
  718. type: EntryType.THREADS,
  719. },
  720. {
  721. data: {
  722. images: [
  723. {
  724. code_file:
  725. '/var/containers/Bundle/Application/575F9D39-D486-4728-B035-84923A0BE206/sentry-ios-cocoapods.app/sentry-ios-cocoapods',
  726. debug_id: '6b77ffb6-5aba-3b5f-9171-434f9660f738',
  727. image_addr: '0x100084000',
  728. image_size: 49152,
  729. image_vmaddr: '0x100000000',
  730. type: 'macho',
  731. },
  732. {
  733. code_file: '/System/Library/Frameworks/UIKit.framework/UIKit',
  734. debug_id: '314063bd-f85f-321d-88d6-e24a0de464a2',
  735. image_addr: '0x197841000',
  736. image_size: 14315520,
  737. image_vmaddr: '0x187769000',
  738. type: 'macho',
  739. },
  740. {
  741. code_file:
  742. '/private/var/containers/Bundle/Application/575F9D39-D486-4728-B035-84923A0BE206/sentry-ios-cocoapods.app/Frameworks/Sentry.framework/Sentry',
  743. debug_id: '141a9203-b953-3cd6-b9fd-7ba60191a36d',
  744. image_addr: '0x1000a4000',
  745. image_size: 163840,
  746. image_vmaddr: '0x0',
  747. type: 'macho',
  748. },
  749. {
  750. code_file:
  751. '/private/var/containers/Bundle/Application/575F9D39-D486-4728-B035-84923A0BE206/sentry-ios-cocoapods.app/Frameworks/Sentry.framework/Sentry',
  752. debug_id: '141a9203-b953-3cd6-b9fd-7ba60191a36d',
  753. image_addr: '0x1000a4000',
  754. image_size: 163840,
  755. image_vmaddr: '0x0',
  756. type: 'macho',
  757. },
  758. ],
  759. },
  760. type: EntryType.DEBUGMETA,
  761. },
  762. ],
  763. dist: '1',
  764. message: '',
  765. title: 'ViewController.causeCrash | main',
  766. location: null,
  767. user: {
  768. id: '1234',
  769. email: 'hello@sentry.io',
  770. username: null,
  771. ip_address: '172.18.0.1',
  772. name: null,
  773. data: null,
  774. },
  775. type: EventOrGroupType.ERROR,
  776. metadata: {
  777. display_title_with_tree_label: true,
  778. finest_tree_label: [
  779. {
  780. function: 'ViewController.causeCrash',
  781. },
  782. {
  783. function: 'main',
  784. },
  785. ],
  786. function: 'ViewController.causeCrash',
  787. value:
  788. 'Attempted to dereference null pointer.\nOriginated at or in a subcall of ViewController.causeCrash(Any) -> ()',
  789. },
  790. platform: 'cocoa',
  791. dateReceived: '2021-11-02T07:33:38.831104Z',
  792. errors: [
  793. {
  794. type: 'invalid_data',
  795. message: 'Discarded invalid value',
  796. data: {
  797. name: 'threads.values.9.stacktrace.frames',
  798. reason: 'expected a non-empty value',
  799. },
  800. },
  801. ],
  802. crashFile: null,
  803. culprit: '',
  804. dateCreated: '2021-11-02T07:33:38.831104Z',
  805. fingerprints: ['852f6cf1ed76d284b95e7d62275088ca'],
  806. groupingConfig: {
  807. enhancements: 'eJybzDRxY25-UmZOqpWRgZGhroGJroHRBABbUQb_',
  808. id: 'mobile:2021-02-12',
  809. },
  810. tags: [],
  811. contexts: {},
  812. userReport: null,
  813. sdkUpdates: [],
  814. nextEventID: null,
  815. previousEventID: null,
  816. };
  817. const props: React.ComponentProps<typeof ThreadsV2> = {
  818. type: EntryType.THREADS,
  819. data: event.entries[1].data as React.ComponentProps<typeof ThreadsV2>['data'],
  820. event,
  821. groupingCurrentLevel: 0,
  822. hasHierarchicalGrouping: true,
  823. projectId: project.id,
  824. };
  825. it('renders', function () {
  826. const {container} = render(<ThreadsV2 {...props} />, {organization: org});
  827. // Title
  828. expect(screen.getByTestId('thread-selector')).toBeInTheDocument();
  829. // Actions
  830. expect(screen.getByRole('checkbox', {name: 'Raw'})).toBeInTheDocument();
  831. expect(screen.getByRole('checkbox', {name: 'Raw'})).not.toBeChecked();
  832. expect(
  833. screen.getByRole('button', {name: 'Options 0 Active'})
  834. ).toBeInTheDocument();
  835. expect(
  836. screen.getByRole('button', {name: 'Sort By Recent first'})
  837. ).toBeInTheDocument();
  838. // Stack Trace
  839. expect(screen.getByRole('heading', {name: 'EXC_BAD_ACCESS'})).toBeInTheDocument();
  840. expect(
  841. screen.getByText(
  842. 'Attempted to dereference null pointer. Originated at or in a subcall of ViewController.causeCrash(Any) -> ()'
  843. )
  844. ).toBeInTheDocument();
  845. expect(screen.getByTestId('stack-trace')).toBeInTheDocument();
  846. expect(screen.queryAllByTestId('stack-trace-frame')).toHaveLength(3);
  847. expect(container).toSnapshot();
  848. });
  849. it('toggle raw button', function () {
  850. render(<ThreadsV2 {...props} />, {organization: org});
  851. expect(screen.getByRole('checkbox', {name: 'Raw'})).not.toBeChecked();
  852. userEvent.click(screen.getByRole('checkbox', {name: 'Raw'}));
  853. expect(screen.getByRole('checkbox', {name: 'Raw'})).toBeChecked();
  854. // Actions must not be rendered
  855. expect(
  856. screen.queryByRole('button', {name: 'Options 1 Active'})
  857. ).not.toBeInTheDocument();
  858. expect(
  859. screen.queryByRole('button', {name: 'Sort By Recent first'})
  860. ).not.toBeInTheDocument();
  861. // Raw content is displayed
  862. expect(screen.queryByTestId('stack-trace')).not.toBeInTheDocument();
  863. expect(screen.getByTestId('raw-stack-trace')).toBeInTheDocument();
  864. });
  865. it('toggle sort by', function () {
  866. render(<ThreadsV2 {...props} />, {organization: org});
  867. expect(
  868. within(screen.getAllByTestId('stack-trace-frame')[0]).getByText(
  869. '-[SentryClient crash]'
  870. )
  871. ).toBeInTheDocument();
  872. userEvent.click(screen.getByRole('button', {name: 'Sort By Recent first'}));
  873. expect(screen.queryAllByLabelText('Sort option')).toHaveLength(2);
  874. expect(screen.queryAllByLabelText('Sort option')[0]).toHaveTextContent(
  875. 'Recent first'
  876. );
  877. expect(screen.queryAllByLabelText('Sort option')[1]).toHaveTextContent(
  878. 'Recent last'
  879. );
  880. userEvent.click(screen.getByText('Recent last'));
  881. expect(
  882. screen.getByRole('button', {name: 'Sort By Recent last'})
  883. ).toBeInTheDocument();
  884. expect(
  885. within(screen.getAllByTestId('stack-trace-frame')[0]).getByText('UIKit')
  886. ).toBeInTheDocument();
  887. userEvent.click(screen.getByRole('button', {name: 'Sort By Recent last'}));
  888. userEvent.click(screen.getByText('Recent first'));
  889. expect(
  890. screen.getByRole('button', {name: 'Sort By Recent first'})
  891. ).toBeInTheDocument();
  892. });
  893. it('check options', function () {
  894. render(<ThreadsV2 {...props} />, {organization: org});
  895. expect(
  896. screen.getByRole('button', {name: 'Options 0 Active'})
  897. ).toBeInTheDocument();
  898. userEvent.click(screen.getByRole('button', {name: 'Options 0 Active'}));
  899. expect(screen.queryAllByLabelText('Display option')).toHaveLength(5);
  900. const displayOption0 = screen.queryAllByLabelText('Display option')[0];
  901. expect(displayOption0).toHaveTextContent('Unsymbolicated');
  902. expect(within(displayOption0).getByRole('checkbox')).not.toBeChecked();
  903. expect(within(displayOption0).getByRole('checkbox')).toHaveAttribute(
  904. 'aria-disabled',
  905. 'true'
  906. );
  907. const displayOption1 = screen.queryAllByLabelText('Display option')[1];
  908. expect(displayOption1).toHaveTextContent('Absolute Addresses');
  909. expect(within(displayOption1).getByRole('checkbox')).not.toBeChecked();
  910. expect(within(displayOption1).getByRole('checkbox')).toHaveAttribute(
  911. 'aria-disabled',
  912. 'false'
  913. );
  914. const displayOption2 = screen.queryAllByLabelText('Display option')[2];
  915. expect(displayOption2).toHaveTextContent('Absolute File Paths');
  916. expect(within(displayOption2).getByRole('checkbox')).not.toBeChecked();
  917. expect(within(displayOption2).getByRole('checkbox')).toHaveAttribute(
  918. 'aria-disabled',
  919. 'true'
  920. );
  921. const displayOption3 = screen.queryAllByLabelText('Display option')[3];
  922. expect(displayOption3).toHaveTextContent('Verbose Function Names');
  923. expect(within(displayOption3).getByRole('checkbox')).not.toBeChecked();
  924. expect(within(displayOption3).getByRole('checkbox')).toHaveAttribute(
  925. 'aria-disabled',
  926. 'false'
  927. );
  928. const displayOption4 = screen.queryAllByLabelText('Display option')[4];
  929. expect(displayOption4).toHaveTextContent('Full Stack Trace');
  930. expect(within(displayOption4).getByRole('checkbox')).not.toBeChecked();
  931. expect(within(displayOption4).getByRole('checkbox')).toHaveAttribute(
  932. 'aria-disabled',
  933. 'false'
  934. );
  935. expect(screen.getByTestId('stack-trace')).toBeInTheDocument();
  936. expect(screen.queryAllByTestId('stack-trace-frame')).toHaveLength(3);
  937. // Check Verbose Function Names
  938. expect(
  939. within(screen.getAllByTestId('stack-trace-frame')[1]).getByText(
  940. 'ViewController.causeCrash'
  941. )
  942. ).toBeInTheDocument();
  943. userEvent.click(screen.queryAllByLabelText('Display option')[3]);
  944. expect(within(displayOption3).getByRole('checkbox')).toBeChecked();
  945. expect(
  946. within(screen.getAllByTestId('stack-trace-frame')[1]).getByText(
  947. 'ViewController.causeCrash(Any) -> ()'
  948. )
  949. ).toBeInTheDocument();
  950. // Check Absolute Path Names
  951. expect(
  952. within(screen.getAllByTestId('stack-trace-frame')[1]).getByText('+0x085ac')
  953. ).toBeInTheDocument();
  954. userEvent.click(screen.queryAllByLabelText('Display option')[1]);
  955. expect(
  956. within(screen.queryAllByLabelText('Display option')[1]).getByRole('checkbox')
  957. ).toBeChecked();
  958. expect(
  959. within(screen.getAllByTestId('stack-trace-frame')[1]).getByText('0x10008c5ac')
  960. ).toBeInTheDocument();
  961. // Check Full Stack Trace
  962. userEvent.click(screen.queryAllByLabelText('Display option')[4]);
  963. expect(
  964. within(screen.queryAllByLabelText('Display option')[4]).getByRole('checkbox')
  965. ).toBeChecked();
  966. // Display more frames
  967. expect(screen.queryAllByTestId('stack-trace-frame')).toHaveLength(4);
  968. });
  969. });
  970. });
  971. });