index.spec.tsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
  2. import {textWithMarkupMatcher} from 'sentry-test/utils';
  3. import ContextSummary from 'sentry/components/events/contextSummary';
  4. import {ContextSummaryGPU} from 'sentry/components/events/contextSummary/contextSummaryGPU';
  5. import {ContextSummaryOS} from 'sentry/components/events/contextSummary/contextSummaryOS';
  6. import {ContextSummaryUser} from 'sentry/components/events/contextSummary/contextSummaryUser';
  7. import {FILTER_MASK} from 'sentry/constants';
  8. const CONTEXT_USER = {
  9. email: 'mail@example.org',
  10. id: '1',
  11. };
  12. const CONTEXT_DEVICE = {
  13. arch: 'x86',
  14. family: 'iOS',
  15. model: 'iPhone10,5',
  16. type: 'device',
  17. };
  18. const CONTEXT_OS = {
  19. kernel_version: '17.5.0',
  20. version: '10.13.4',
  21. type: 'os',
  22. build: '17E199',
  23. name: 'Mac OS X',
  24. };
  25. const CONTEXT_OS_SERVER = {
  26. kernel_version: '4.3.0',
  27. version: '4.3.0',
  28. type: 'os',
  29. build: '123123123',
  30. name: 'Linux',
  31. };
  32. const CONTEXT_RUNTIME = {
  33. version: '1.7.13',
  34. type: 'runtime',
  35. name: 'Electron',
  36. };
  37. const CONTEXT_BROWSER = {
  38. version: '65.0.3325',
  39. name: 'Chrome',
  40. };
  41. describe('ContextSummary', function () {
  42. describe('render()', function () {
  43. it('renders nothing without contexts', function () {
  44. const event = {
  45. ...TestStubs.Event(),
  46. id: '',
  47. contexts: {},
  48. };
  49. const {container} = render(<ContextSummary event={event} />);
  50. expect(container).toSnapshot();
  51. });
  52. it('renders nothing with a single user context', function () {
  53. const event = {
  54. ...TestStubs.Event(),
  55. id: '',
  56. user: CONTEXT_USER,
  57. contexts: {},
  58. };
  59. const {container} = render(<ContextSummary event={event} />);
  60. expect(container).toSnapshot();
  61. });
  62. it('should bail out with empty contexts', function () {
  63. const event = {
  64. ...TestStubs.Event(),
  65. id: '',
  66. user: CONTEXT_USER,
  67. contexts: {
  68. device: {},
  69. os: {},
  70. },
  71. };
  72. const {container} = render(<ContextSummary event={event} />);
  73. expect(container).toSnapshot();
  74. });
  75. it('renders at least three contexts', function () {
  76. const event = {
  77. ...TestStubs.Event(),
  78. id: '',
  79. user: CONTEXT_USER,
  80. contexts: {
  81. device: CONTEXT_DEVICE,
  82. },
  83. };
  84. const {container} = render(<ContextSummary event={event} />);
  85. expect(container).toSnapshot();
  86. });
  87. it('renders up to four contexts', function () {
  88. const event = {
  89. ...TestStubs.Event(),
  90. id: '',
  91. user: CONTEXT_USER,
  92. contexts: {
  93. os: CONTEXT_OS,
  94. browser: CONTEXT_BROWSER,
  95. runtime: CONTEXT_RUNTIME,
  96. device: CONTEXT_DEVICE, // must be omitted
  97. },
  98. };
  99. const {container} = render(<ContextSummary event={event} />);
  100. expect(container).toSnapshot();
  101. });
  102. it('should prefer client_os over os', function () {
  103. const event = {
  104. ...TestStubs.Event(),
  105. id: '',
  106. user: CONTEXT_USER,
  107. contexts: {
  108. client_os: CONTEXT_OS,
  109. os: CONTEXT_OS_SERVER,
  110. browser: CONTEXT_BROWSER,
  111. runtime: CONTEXT_RUNTIME,
  112. },
  113. };
  114. const {container} = render(<ContextSummary event={event} />);
  115. expect(container).toSnapshot();
  116. });
  117. it('renders client_os too', function () {
  118. const event = {
  119. ...TestStubs.Event(),
  120. id: '',
  121. user: CONTEXT_USER,
  122. contexts: {
  123. client_os: CONTEXT_OS,
  124. browser: CONTEXT_BROWSER,
  125. runtime: CONTEXT_RUNTIME,
  126. },
  127. };
  128. const {container} = render(<ContextSummary event={event} />);
  129. expect(container).toSnapshot();
  130. });
  131. it('should skip non-default named contexts', function () {
  132. const event = {
  133. ...TestStubs.Event(),
  134. id: '',
  135. user: CONTEXT_USER,
  136. contexts: {
  137. os: CONTEXT_OS,
  138. chrome: CONTEXT_BROWSER, // non-standard context
  139. runtime: CONTEXT_RUNTIME,
  140. device: CONTEXT_DEVICE,
  141. },
  142. };
  143. const {container} = render(<ContextSummary event={event} />);
  144. expect(container).toSnapshot();
  145. });
  146. it('should skip a missing user context', function () {
  147. const event = {
  148. ...TestStubs.Event(),
  149. id: '',
  150. contexts: {
  151. os: CONTEXT_OS,
  152. chrome: CONTEXT_BROWSER, // non-standard context
  153. runtime: CONTEXT_RUNTIME,
  154. device: CONTEXT_DEVICE,
  155. },
  156. };
  157. const {container} = render(<ContextSummary event={event} />);
  158. expect(container).toSnapshot();
  159. });
  160. });
  161. });
  162. describe('OsSummary', function () {
  163. describe('render()', function () {
  164. it('renders the version string', function () {
  165. const {container} = render(
  166. <ContextSummaryOS
  167. data={{
  168. kernel_version: '17.5.0',
  169. version: '10.13.4',
  170. name: 'Mac OS X',
  171. }}
  172. meta={{}}
  173. />
  174. );
  175. expect(container).toSnapshot();
  176. });
  177. it('renders the kernel version when no version', function () {
  178. const {container} = render(
  179. <ContextSummaryOS
  180. data={{
  181. kernel_version: '17.5.0',
  182. name: 'Mac OS X',
  183. }}
  184. meta={{}}
  185. />
  186. );
  187. expect(container).toSnapshot();
  188. });
  189. it('renders unknown when no version', function () {
  190. const {container} = render(
  191. <ContextSummaryOS
  192. data={{
  193. name: 'Mac OS X',
  194. }}
  195. meta={{}}
  196. />
  197. );
  198. expect(container).toSnapshot();
  199. });
  200. it('display redacted name', async function () {
  201. render(
  202. <ContextSummaryOS
  203. data={{
  204. name: '',
  205. version: '10',
  206. }}
  207. meta={{
  208. name: {
  209. '': {
  210. rem: [['project:0', 's', 0, 0]],
  211. len: 19,
  212. },
  213. },
  214. }}
  215. />
  216. );
  217. await userEvent.hover(screen.getByText(/redacted/));
  218. expect(
  219. await screen.findByText(
  220. textWithMarkupMatcher(
  221. "Replaced because of a data scrubbing rule in your project's settings"
  222. ) // Fall back case
  223. )
  224. ).toBeInTheDocument(); // tooltip description
  225. });
  226. it('handles invalid data', async function () {
  227. render(
  228. <ContextSummaryOS
  229. data={{
  230. name: false,
  231. version: false,
  232. }}
  233. meta={{
  234. name: {
  235. '': {
  236. rem: [['project:0', 's', 0, 0]],
  237. len: 19,
  238. },
  239. },
  240. }}
  241. />
  242. );
  243. await userEvent.hover(screen.getByText(/redacted/));
  244. expect(
  245. await screen.findByText(
  246. textWithMarkupMatcher(
  247. "Replaced because of a data scrubbing rule in your project's settings"
  248. ) // Fall back case
  249. )
  250. ).toBeInTheDocument(); // tooltip description
  251. });
  252. });
  253. });
  254. describe('GpuSummary', function () {
  255. describe('render()', function () {
  256. it('renders name and vendor', function () {
  257. const {container} = render(
  258. <ContextSummaryGPU
  259. data={{
  260. name: 'Mali-T880',
  261. vendor_name: 'ARM',
  262. }}
  263. meta={{}}
  264. />
  265. );
  266. expect(container).toSnapshot();
  267. });
  268. it('renders unknown when no vendor', function () {
  269. const {container} = render(
  270. <ContextSummaryGPU
  271. data={{
  272. name: 'Apple A8 GPU',
  273. }}
  274. meta={{}}
  275. />
  276. );
  277. expect(container).toSnapshot();
  278. });
  279. it('display redacted name', async function () {
  280. render(
  281. <ContextSummaryGPU
  282. data={{
  283. name: '',
  284. }}
  285. meta={{
  286. name: {
  287. '': {
  288. rem: [['project:0', 's', 0, 0]],
  289. len: 19,
  290. },
  291. },
  292. }}
  293. />
  294. );
  295. await userEvent.hover(screen.getByText(/redacted/));
  296. expect(
  297. await screen.findByText(
  298. textWithMarkupMatcher(
  299. "Replaced because of a data scrubbing rule in your project's settings"
  300. )
  301. ) // Fall back case)
  302. ).toBeInTheDocument(); // tooltip description
  303. });
  304. });
  305. });
  306. describe('UserSummary', function () {
  307. describe('render', function () {
  308. it('prefers email, then IP, then id, then username for title', function () {
  309. const user1 = {
  310. email: 'maisey@dogsrule.com',
  311. ip_address: '12.31.20.12',
  312. id: '26',
  313. username: 'maiseythedog',
  314. name: 'Maisey Dog',
  315. };
  316. const {rerender} = render(<ContextSummaryUser data={user1} meta={{}} />);
  317. expect(screen.getByText(user1.email)).toBeInTheDocument();
  318. const user2 = {
  319. ip_address: '12.31.20.12',
  320. id: '26',
  321. username: 'maiseythedog',
  322. name: 'Maisey Dog',
  323. };
  324. rerender(<ContextSummaryUser data={user2} meta={{}} />);
  325. expect(screen.getByTestId('user-title')?.textContent).toEqual(user2.ip_address);
  326. const user3 = {
  327. id: '26',
  328. username: 'maiseythedog',
  329. name: 'Maisey Dog',
  330. };
  331. rerender(
  332. <ContextSummaryUser
  333. data={{
  334. id: '26',
  335. username: 'maiseythedog',
  336. name: 'Maisey Dog',
  337. }}
  338. meta={{}}
  339. />
  340. );
  341. expect(screen.getByTestId('user-title')?.textContent).toEqual(user3.id);
  342. const user4 = {
  343. username: 'maiseythedog',
  344. name: 'Maisey Dog',
  345. };
  346. rerender(<ContextSummaryUser data={user4} meta={{}} />);
  347. expect(screen.getByTestId('user-title')).toHaveTextContent(user4.username);
  348. });
  349. it('renders NoSummary if no email, IP, id, or username', function () {
  350. render(
  351. <ContextSummaryUser
  352. data={{
  353. name: 'Maisey Dog',
  354. }}
  355. meta={{}}
  356. />
  357. );
  358. expect(screen.queryByTestId('user-title')).not.toBeInTheDocument();
  359. expect(screen.getByTestId('no-summary-title')).toHaveTextContent('Unknown User');
  360. });
  361. it('does not use filtered values for title', function () {
  362. const {rerender} = render(
  363. <ContextSummaryUser
  364. data={{
  365. email: FILTER_MASK,
  366. }}
  367. meta={{}}
  368. />
  369. );
  370. expect(screen.queryByTestId('user-title')).not.toBeInTheDocument();
  371. expect(screen.getByTestId('no-summary-title')).toHaveTextContent('Unknown User');
  372. // TODO: currently, the IP filter just eliminates IP addresses rather than
  373. // filtering them like other user data, so here, where you'd expect a filtered
  374. // IP address, there isn't one. Add a similar entry to the above and below
  375. // if/when that changes.
  376. rerender(
  377. <ContextSummaryUser
  378. data={{
  379. id: FILTER_MASK,
  380. }}
  381. meta={{}}
  382. />
  383. );
  384. expect(screen.queryByTestId('user-title')).not.toBeInTheDocument();
  385. expect(screen.getByTestId('no-summary-title')).toHaveTextContent('Unknown User');
  386. rerender(
  387. <ContextSummaryUser
  388. data={{
  389. username: FILTER_MASK,
  390. }}
  391. meta={{}}
  392. />
  393. );
  394. expect(screen.queryByTestId('user-title')).not.toBeInTheDocument();
  395. expect(screen.getByTestId('no-summary-title')).toHaveTextContent('Unknown User');
  396. });
  397. it('does not use filtered values for avatar', function () {
  398. // id is never used for avatar purposes, but is enough to keep us from
  399. // ending up with a NoSummary component where the UserSummary component
  400. // should be
  401. const {rerender} = render(
  402. <ContextSummaryUser
  403. data={{
  404. id: '26',
  405. name: FILTER_MASK,
  406. }}
  407. meta={{}}
  408. />
  409. );
  410. expect(screen.getByText('?')).toBeInTheDocument();
  411. rerender(
  412. <ContextSummaryUser
  413. data={{
  414. id: '26',
  415. email: FILTER_MASK,
  416. }}
  417. meta={{}}
  418. />
  419. );
  420. expect(screen.getByText('?')).toBeInTheDocument();
  421. rerender(
  422. <ContextSummaryUser
  423. data={{
  424. id: '26',
  425. username: FILTER_MASK,
  426. }}
  427. meta={{}}
  428. />
  429. );
  430. expect(screen.getByText('?')).toBeInTheDocument();
  431. });
  432. it('display redacted email', async function () {
  433. render(
  434. <ContextSummaryUser
  435. data={{
  436. name: 'Maisey Dog',
  437. email: '',
  438. }}
  439. meta={{
  440. email: {
  441. '': {
  442. rem: [['project:0', 's', 0, 0]],
  443. len: 19,
  444. },
  445. },
  446. }}
  447. />
  448. );
  449. await userEvent.hover(screen.getByText(/redacted/));
  450. expect(
  451. await screen.findByText(
  452. textWithMarkupMatcher(
  453. "Replaced because of a data scrubbing rule in your project's settings"
  454. ) // Fall back case
  455. )
  456. ).toBeInTheDocument(); // tooltip description
  457. });
  458. });
  459. });