eventSamplesTable.spec.tsx 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. import {LocationFixture} from 'sentry-fixture/locationFixture';
  2. import {RouterFixture} from 'sentry-fixture/routerFixture';
  3. import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
  4. import type {InjectedRouter} from 'sentry/types/legacyReactRouter';
  5. import type {NewQuery} from 'sentry/types/organization';
  6. import EventView from 'sentry/utils/discover/eventView';
  7. import {EventSamplesTable} from 'sentry/views/insights/mobile/screenload/components/tables/eventSamplesTable';
  8. describe('EventSamplesTable', function () {
  9. let mockRouter: InjectedRouter;
  10. let mockLocation: ReturnType<typeof LocationFixture>;
  11. let mockQuery: NewQuery;
  12. let mockEventView: EventView;
  13. beforeEach(function () {
  14. mockRouter = RouterFixture();
  15. mockLocation = LocationFixture({
  16. query: {
  17. statsPeriod: '99d',
  18. },
  19. });
  20. mockQuery = {
  21. name: '',
  22. fields: ['transaction.id'],
  23. query: '',
  24. version: 2,
  25. };
  26. mockEventView = EventView.fromNewQueryWithLocation(mockQuery, mockLocation);
  27. MockApiClient.addMockResponse({
  28. url: `/organizations/org-slug/events/`,
  29. method: 'GET',
  30. match: [
  31. MockApiClient.matchQuery({
  32. referrer: 'api.insights.user-geo-subregion-selector',
  33. }),
  34. ],
  35. body: {
  36. data: [
  37. {'user.geo.subregion': '21', 'count()': 123},
  38. {'user.geo.subregion': '155', 'count()': 123},
  39. ],
  40. meta: {
  41. fields: {'user.geo.subregion': 'string', 'count()': 'integer'},
  42. },
  43. },
  44. });
  45. });
  46. it('uses a column name map to render column names', function () {
  47. mockQuery = {
  48. name: '',
  49. fields: ['rawField'],
  50. query: '',
  51. version: 2,
  52. };
  53. mockEventView = EventView.fromNewQueryWithLocation(mockQuery, mockLocation);
  54. render(
  55. <EventSamplesTable
  56. columnNameMap={{
  57. rawField: 'Readable Column Name',
  58. }}
  59. cursorName=""
  60. eventIdKey="transaction.id"
  61. eventView={mockEventView}
  62. isLoading={false}
  63. profileIdKey="profile.id"
  64. sort={{
  65. field: '',
  66. kind: 'desc',
  67. }}
  68. sortKey=""
  69. />,
  70. {router: mockRouter}
  71. );
  72. expect(screen.getByText('Readable Column Name')).toBeInTheDocument();
  73. expect(screen.queryByText('rawField')).not.toBeInTheDocument();
  74. });
  75. it('uses the event ID key to get the event ID from the data payload', function () {
  76. mockQuery = {
  77. name: '',
  78. fields: ['transaction.id'],
  79. query: '',
  80. version: 2,
  81. };
  82. mockEventView = EventView.fromNewQueryWithLocation(mockQuery, mockLocation);
  83. render(
  84. <EventSamplesTable
  85. eventIdKey="transaction.id"
  86. columnNameMap={{'transaction.id': 'Event ID'}}
  87. cursorName=""
  88. eventView={mockEventView}
  89. isLoading={false}
  90. profileIdKey="profile.id"
  91. sort={{
  92. field: '',
  93. kind: 'desc',
  94. }}
  95. sortKey=""
  96. data={{data: [{id: '1', 'transaction.id': 'abc'}], meta: {}}}
  97. />,
  98. {router: mockRouter}
  99. );
  100. // Test only one column to isolate event ID
  101. expect(screen.getAllByRole('columnheader')).toHaveLength(1);
  102. expect(screen.getByRole('columnheader', {name: 'Event ID'})).toBeInTheDocument();
  103. expect(screen.getByText('abc')).toBeInTheDocument();
  104. expect(screen.queryByText('1')).not.toBeInTheDocument();
  105. });
  106. it('uses the profile ID key to get the profile ID from the data payload and display an icon button', async function () {
  107. mockQuery = {
  108. name: '',
  109. fields: ['profile.id', 'project.name'], // Project name is required to form the profile target
  110. query: '',
  111. version: 2,
  112. };
  113. mockEventView = EventView.fromNewQueryWithLocation(mockQuery, mockLocation);
  114. render(
  115. <EventSamplesTable
  116. profileIdKey="profile.id"
  117. columnNameMap={{'profile.id': 'Profile'}}
  118. eventIdKey="transaction.id"
  119. cursorName=""
  120. eventView={mockEventView}
  121. isLoading={false}
  122. sort={{
  123. field: '',
  124. kind: 'desc',
  125. }}
  126. sortKey=""
  127. data={{
  128. data: [{id: '1', 'profile.id': 'abc', 'project.name': 'project'}],
  129. meta: {fields: {'profile.id': 'string', 'project.name': 'string'}},
  130. }}
  131. />,
  132. {router: mockRouter}
  133. );
  134. // Test only one column to isolate profile column
  135. expect(screen.getAllByRole('columnheader')).toHaveLength(1);
  136. expect(screen.getByRole('columnheader', {name: 'Profile'})).toBeInTheDocument();
  137. expect(await screen.findByRole('button', {name: 'View Profile'})).toBeInTheDocument();
  138. });
  139. it('updates URL params when device class selector is changed', async function () {
  140. mockQuery = {
  141. name: '',
  142. fields: ['transaction.id'],
  143. query: '',
  144. version: 2,
  145. };
  146. mockEventView = EventView.fromNewQueryWithLocation(mockQuery, mockLocation);
  147. render(
  148. <EventSamplesTable
  149. showDeviceClassSelector
  150. eventIdKey="transaction.id"
  151. columnNameMap={{'transaction.id': 'Event ID'}}
  152. cursorName=""
  153. eventView={mockEventView}
  154. isLoading={false}
  155. profileIdKey="profile.id"
  156. sort={{
  157. field: '',
  158. kind: 'desc',
  159. }}
  160. sortKey=""
  161. data={{data: [{id: '1', 'transaction.id': 'abc'}], meta: {}}}
  162. />,
  163. {router: mockRouter}
  164. );
  165. expect(screen.getByRole('button', {name: /device class all/i})).toBeInTheDocument();
  166. await userEvent.click(screen.getByRole('button', {name: /device class all/i}));
  167. await userEvent.click(screen.getByText('Medium'));
  168. expect(mockRouter.push).toHaveBeenCalledWith(
  169. expect.objectContaining({
  170. pathname: '/mock-pathname/',
  171. query: expect.objectContaining({
  172. 'device.class': 'medium',
  173. }),
  174. })
  175. );
  176. });
  177. it('updates URL params when the table is paginated', async function () {
  178. const pageLinks =
  179. '<https://sentry.io/fake/previous>; rel="previous"; results="false"; cursor="0:0:1", ' +
  180. '<https://sentry.io/fake/next>; rel="next"; results="true"; cursor="0:20:0"';
  181. render(
  182. <EventSamplesTable
  183. showDeviceClassSelector
  184. eventIdKey="transaction.id"
  185. columnNameMap={{'transaction.id': 'Event ID'}}
  186. cursorName="customCursorName"
  187. eventView={mockEventView}
  188. isLoading={false}
  189. profileIdKey="profile.id"
  190. sort={{
  191. field: '',
  192. kind: 'desc',
  193. }}
  194. sortKey=""
  195. data={{data: [{id: '1', 'transaction.id': 'abc'}], meta: {}}}
  196. pageLinks={pageLinks}
  197. />,
  198. {router: mockRouter}
  199. );
  200. expect(screen.getByRole('button', {name: 'Next'})).toBeInTheDocument();
  201. await userEvent.click(screen.getByRole('button', {name: 'Next'}));
  202. expect(mockRouter.push).toHaveBeenCalledWith(
  203. expect.objectContaining({
  204. pathname: '/mock-pathname/',
  205. query: expect.objectContaining({
  206. customCursorName: '0:20:0',
  207. }),
  208. })
  209. );
  210. });
  211. it('uses a custom sort key for sortable headers', async function () {
  212. mockQuery = {
  213. name: '',
  214. fields: ['transaction.id', 'duration'],
  215. query: '',
  216. version: 2,
  217. };
  218. mockEventView = EventView.fromNewQueryWithLocation(mockQuery, mockLocation);
  219. render(
  220. <EventSamplesTable
  221. showDeviceClassSelector
  222. eventIdKey="transaction.id"
  223. columnNameMap={{'transaction.id': 'Event ID', duration: 'Duration'}}
  224. cursorName="customCursorName"
  225. eventView={mockEventView}
  226. isLoading={false}
  227. profileIdKey="profile.id"
  228. sort={{
  229. field: 'transaction.id',
  230. kind: 'desc',
  231. }}
  232. sortKey="customSortKey"
  233. data={{data: [{id: '1', 'transaction.id': 'abc', duration: 'def'}], meta: {}}}
  234. />,
  235. {router: mockRouter}
  236. );
  237. // Ascending sort in transaction ID because the default is descending
  238. expect(await screen.findByRole('link', {name: 'Event ID'})).toHaveAttribute(
  239. 'href',
  240. '/mock-pathname/?customSortKey=transaction.id'
  241. );
  242. expect(screen.getByRole('link', {name: 'Duration'})).toHaveAttribute(
  243. 'href',
  244. '/mock-pathname/?customSortKey=-duration'
  245. );
  246. });
  247. it('only displays data for the columns defined in the name map', async function () {
  248. mockQuery = {
  249. name: '',
  250. fields: ['transaction.id', 'duration'],
  251. query: '',
  252. version: 2,
  253. };
  254. mockEventView = EventView.fromNewQueryWithLocation(mockQuery, mockLocation);
  255. render(
  256. <EventSamplesTable
  257. eventIdKey="transaction.id"
  258. columnNameMap={{duration: 'Duration'}}
  259. cursorName="customCursorName"
  260. eventView={mockEventView}
  261. isLoading={false}
  262. profileIdKey="profile.id"
  263. sort={{
  264. field: 'transaction.id',
  265. kind: 'desc',
  266. }}
  267. sortKey="customSortKey"
  268. data={{data: [{id: '1', 'transaction.id': 'abc', duration: 'def'}], meta: {}}}
  269. />,
  270. {router: mockRouter}
  271. );
  272. // Although ID is queried for, because it's not defined in the map
  273. // it isn't rendered
  274. expect(
  275. await screen.findByRole('columnheader', {name: 'Duration'})
  276. ).toBeInTheDocument();
  277. expect(screen.getAllByRole('columnheader')).toHaveLength(1);
  278. });
  279. });