contextSummary.spec.tsx 12 KB

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