results.spec.tsx 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962
  1. import {LocationFixture} from 'sentry-fixture/locationFixture';
  2. import {OrganizationFixture} from 'sentry-fixture/organization';
  3. import {ProjectFixture} from 'sentry-fixture/project';
  4. import {initializeOrg} from 'sentry-test/initializeOrg';
  5. import {render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
  6. import selectEvent from 'sentry-test/selectEvent';
  7. import * as PageFilterPersistence from 'sentry/components/organizations/pageFilters/persistence';
  8. import ProjectsStore from 'sentry/stores/projectsStore';
  9. import {SavedSearchType} from 'sentry/types/group';
  10. import {browserHistory} from 'sentry/utils/browserHistory';
  11. import EventView from 'sentry/utils/discover/eventView';
  12. import Results from 'sentry/views/discover/results';
  13. import {DEFAULT_EVENT_VIEW, getTransactionViews} from './data';
  14. const FIELDS = [
  15. {
  16. field: 'title',
  17. },
  18. {
  19. field: 'timestamp',
  20. },
  21. {
  22. field: 'user',
  23. },
  24. {
  25. field: 'count()',
  26. },
  27. ];
  28. const generateFields = () => ({
  29. field: FIELDS.map(i => i.field),
  30. });
  31. const eventTitle = 'Oh no something bad';
  32. function renderMockRequests() {
  33. MockApiClient.addMockResponse({
  34. url: '/organizations/org-slug/projects/',
  35. body: [],
  36. });
  37. MockApiClient.addMockResponse({
  38. url: '/organizations/org-slug/projects-count/',
  39. body: {myProjects: 10, allProjects: 300},
  40. });
  41. MockApiClient.addMockResponse({
  42. url: '/organizations/org-slug/tags/',
  43. body: [],
  44. });
  45. const eventsStatsMock = MockApiClient.addMockResponse({
  46. url: '/organizations/org-slug/events-stats/',
  47. body: {data: [[123, []]]},
  48. });
  49. MockApiClient.addMockResponse({
  50. url: '/organizations/org-slug/recent-searches/',
  51. body: [],
  52. });
  53. MockApiClient.addMockResponse({
  54. url: '/organizations/org-slug/recent-searches/',
  55. method: 'POST',
  56. body: [],
  57. });
  58. MockApiClient.addMockResponse({
  59. url: '/organizations/org-slug/releases/stats/',
  60. body: [],
  61. });
  62. const measurementsMetaMock = MockApiClient.addMockResponse({
  63. url: '/organizations/org-slug/measurements-meta/',
  64. method: 'GET',
  65. body: {},
  66. });
  67. const eventsResultsMock = MockApiClient.addMockResponse({
  68. url: '/organizations/org-slug/events/',
  69. body: {
  70. meta: {
  71. fields: {
  72. id: 'string',
  73. title: 'string',
  74. 'project.name': 'string',
  75. timestamp: 'date',
  76. 'user.id': 'string',
  77. },
  78. discoverSplitDecision: 'transaction-like',
  79. },
  80. data: [
  81. {
  82. id: 'deadbeef',
  83. 'user.id': 'alberto leal',
  84. title: eventTitle,
  85. 'project.name': 'project-slug',
  86. timestamp: '2019-05-23T22:12:48+00:00',
  87. },
  88. ],
  89. },
  90. });
  91. const eventsMetaMock = MockApiClient.addMockResponse({
  92. url: '/organizations/org-slug/events-meta/',
  93. body: {
  94. count: 2,
  95. },
  96. });
  97. MockApiClient.addMockResponse({
  98. url: '/organizations/org-slug/events/project-slug:deadbeef/',
  99. method: 'GET',
  100. body: {
  101. id: '1234',
  102. size: 1200,
  103. eventID: 'deadbeef',
  104. title: 'Oh no something bad',
  105. message: 'It was not good',
  106. dateCreated: '2019-05-23T22:12:48+00:00',
  107. entries: [
  108. {
  109. type: 'message',
  110. message: 'bad stuff',
  111. data: {},
  112. },
  113. ],
  114. tags: [{key: 'browser', value: 'Firefox'}],
  115. },
  116. });
  117. const eventFacetsMock = MockApiClient.addMockResponse({
  118. url: '/organizations/org-slug/events-facets/',
  119. body: [
  120. {
  121. key: 'release',
  122. topValues: [{count: 3, value: 'abcd123', name: 'abcd123'}],
  123. },
  124. {
  125. key: 'environment',
  126. topValues: [
  127. {count: 2, value: 'dev', name: 'dev'},
  128. {count: 1, value: 'prod', name: 'prod'},
  129. ],
  130. },
  131. {
  132. key: 'foo',
  133. topValues: [
  134. {count: 2, value: 'bar', name: 'bar'},
  135. {count: 1, value: 'baz', name: 'baz'},
  136. ],
  137. },
  138. ],
  139. });
  140. const mockVisit = MockApiClient.addMockResponse({
  141. url: '/organizations/org-slug/discover/saved/1/visit/',
  142. method: 'POST',
  143. body: [],
  144. statusCode: 200,
  145. });
  146. const mockSaved = MockApiClient.addMockResponse({
  147. url: '/organizations/org-slug/discover/saved/1/',
  148. method: 'GET',
  149. statusCode: 200,
  150. body: {
  151. id: '1',
  152. name: 'new',
  153. projects: [],
  154. version: 2,
  155. expired: false,
  156. dateCreated: '2021-04-08T17:53:25.195782Z',
  157. dateUpdated: '2021-04-09T12:13:18.567264Z',
  158. createdBy: {
  159. id: '2',
  160. },
  161. environment: [],
  162. fields: ['title', 'event.type', 'project', 'user.display', 'timestamp'],
  163. widths: ['-1', '-1', '-1', '-1', '-1'],
  164. range: '24h',
  165. orderby: '-user.display',
  166. queryDataset: 'discover',
  167. },
  168. });
  169. MockApiClient.addMockResponse({
  170. url: '/organizations/org-slug/discover/homepage/',
  171. method: 'GET',
  172. statusCode: 200,
  173. body: {
  174. id: '2',
  175. name: '',
  176. projects: [],
  177. version: 2,
  178. expired: false,
  179. dateCreated: '2021-04-08T17:53:25.195782Z',
  180. dateUpdated: '2021-04-09T12:13:18.567264Z',
  181. createdBy: {
  182. id: '2',
  183. },
  184. environment: [],
  185. fields: ['title', 'event.type', 'project', 'user.display', 'timestamp'],
  186. widths: ['-1', '-1', '-1', '-1', '-1'],
  187. range: '24h',
  188. orderby: '-user.display',
  189. queryDataset: 'discover',
  190. },
  191. });
  192. MockApiClient.addMockResponse({
  193. url: '/organizations/org-slug/dynamic-sampling/custom-rules/',
  194. method: 'GET',
  195. statusCode: 204,
  196. body: '',
  197. });
  198. return {
  199. eventsStatsMock,
  200. eventsMetaMock,
  201. eventsResultsMock,
  202. mockVisit,
  203. mockSaved,
  204. eventFacetsMock,
  205. measurementsMetaMock,
  206. };
  207. }
  208. describe('Results', function () {
  209. afterEach(function () {
  210. MockApiClient.clearMockResponses();
  211. ProjectsStore.reset();
  212. });
  213. describe('Events', function () {
  214. const features = ['discover-basic'];
  215. it('loads data when moving from an invalid to valid EventView', function () {
  216. const organization = OrganizationFixture({
  217. features,
  218. });
  219. // Start off with an invalid view (empty is invalid)
  220. const {router} = initializeOrg({
  221. organization,
  222. router: {
  223. location: {query: {query: 'tag:value'}},
  224. },
  225. });
  226. const mockRequests = renderMockRequests();
  227. ProjectsStore.loadInitialData([ProjectFixture()]);
  228. render(
  229. <Results
  230. location={router.location}
  231. router={router}
  232. loading={false}
  233. setSavedQuery={jest.fn()}
  234. />,
  235. {
  236. router: router,
  237. organization,
  238. }
  239. );
  240. // No request as eventview was invalid.
  241. expect(mockRequests.eventsStatsMock).not.toHaveBeenCalled();
  242. // Should redirect and retain the old query value
  243. expect(browserHistory.replace).toHaveBeenCalledWith(
  244. expect.objectContaining({
  245. pathname: '/organizations/org-slug/discover/results/',
  246. query: expect.objectContaining({
  247. query: 'tag:value',
  248. }),
  249. })
  250. );
  251. });
  252. it('pagination cursor should be cleared when making a search', async function () {
  253. const organization = OrganizationFixture({
  254. features,
  255. });
  256. const {router} = initializeOrg({
  257. organization,
  258. router: {
  259. location: {
  260. query: {
  261. ...generateFields(),
  262. cursor: '0%3A50%3A0',
  263. },
  264. },
  265. },
  266. });
  267. const mockRequests = renderMockRequests();
  268. ProjectsStore.loadInitialData([ProjectFixture()]);
  269. render(
  270. <Results
  271. organization={organization}
  272. location={router.location}
  273. router={router}
  274. loading={false}
  275. setSavedQuery={jest.fn()}
  276. />,
  277. {
  278. router: router,
  279. organization,
  280. }
  281. );
  282. // ensure cursor query string is initially present in the location
  283. expect(router.location).toEqual({
  284. query: {
  285. ...generateFields(),
  286. cursor: '0%3A50%3A0',
  287. },
  288. });
  289. await waitFor(() =>
  290. expect(screen.queryByTestId('loading-indicator')).not.toBeInTheDocument()
  291. );
  292. // perform a search
  293. await userEvent.click(
  294. screen.getByPlaceholderText('Search for events, users, tags, and more')
  295. );
  296. await userEvent.paste('geo:canada');
  297. await userEvent.keyboard('{enter}');
  298. // should only be called with saved queries
  299. expect(mockRequests.mockVisit).not.toHaveBeenCalled();
  300. // cursor query string should be omitted from the query string
  301. expect(router.push).toHaveBeenCalledWith({
  302. pathname: undefined,
  303. query: {
  304. ...generateFields(),
  305. query: 'geo:canada',
  306. statsPeriod: '14d',
  307. },
  308. });
  309. });
  310. it('renders a y-axis selector', async function () {
  311. const organization = OrganizationFixture({
  312. features,
  313. });
  314. const {router} = initializeOrg({
  315. organization,
  316. router: {
  317. location: {query: {...generateFields(), yAxis: 'count()'}},
  318. },
  319. });
  320. renderMockRequests();
  321. ProjectsStore.loadInitialData([ProjectFixture()]);
  322. render(
  323. <Results
  324. organization={organization}
  325. location={router.location}
  326. router={router}
  327. loading={false}
  328. setSavedQuery={jest.fn()}
  329. />,
  330. {
  331. router: router,
  332. organization,
  333. }
  334. );
  335. // Click the 'default' option.
  336. await selectEvent.select(
  337. await screen.findByRole('button', {name: 'Y-Axis count()'}),
  338. 'count_unique(user)'
  339. );
  340. });
  341. it('renders a display selector', async function () {
  342. const organization = OrganizationFixture({
  343. features,
  344. });
  345. const {router} = initializeOrg({
  346. organization,
  347. router: {
  348. location: {query: {...generateFields(), display: 'default', yAxis: 'count'}},
  349. },
  350. });
  351. renderMockRequests();
  352. ProjectsStore.loadInitialData([ProjectFixture()]);
  353. render(
  354. <Results
  355. organization={organization}
  356. location={router.location}
  357. router={router}
  358. loading={false}
  359. setSavedQuery={jest.fn()}
  360. />,
  361. {
  362. router: router,
  363. organization,
  364. }
  365. );
  366. // Click the 'default' option.
  367. await selectEvent.select(
  368. await screen.findByRole('button', {name: /Display/}),
  369. 'Total Period'
  370. );
  371. });
  372. it('excludes top5 options when plan does not include discover-query', async function () {
  373. const organization = OrganizationFixture({
  374. features: ['discover-basic'],
  375. });
  376. const {router} = initializeOrg({
  377. organization,
  378. router: {
  379. location: {query: {...generateFields(), display: 'previous'}},
  380. },
  381. });
  382. ProjectsStore.loadInitialData([ProjectFixture()]);
  383. renderMockRequests();
  384. render(
  385. <Results
  386. organization={organization}
  387. location={router.location}
  388. router={router}
  389. loading={false}
  390. setSavedQuery={jest.fn()}
  391. />,
  392. {
  393. router: router,
  394. organization,
  395. }
  396. );
  397. await userEvent.click(await screen.findByRole('button', {name: /Display/}));
  398. expect(screen.queryByText('Top 5 Daily')).not.toBeInTheDocument();
  399. expect(screen.queryByText('Top 5 Period')).not.toBeInTheDocument();
  400. });
  401. it('needs confirmation on long queries', async function () {
  402. const organization = OrganizationFixture({
  403. features: ['discover-basic'],
  404. });
  405. const {router} = initializeOrg({
  406. organization,
  407. router: {
  408. location: {query: {...generateFields(), statsPeriod: '60d', project: '-1'}},
  409. },
  410. });
  411. ProjectsStore.loadInitialData([ProjectFixture()]);
  412. const mockRequests = renderMockRequests();
  413. render(
  414. <Results
  415. organization={organization}
  416. location={router.location}
  417. router={router}
  418. loading={false}
  419. setSavedQuery={jest.fn()}
  420. />,
  421. {
  422. router: router,
  423. organization,
  424. }
  425. );
  426. expect(mockRequests.eventsResultsMock).toHaveBeenCalledTimes(0);
  427. await waitFor(() => {
  428. expect(mockRequests.measurementsMetaMock).toHaveBeenCalled();
  429. });
  430. });
  431. it('needs confirmation on long query with explicit projects', async function () {
  432. const organization = OrganizationFixture({
  433. features: ['discover-basic'],
  434. });
  435. const {router} = initializeOrg({
  436. organization,
  437. router: {
  438. location: {
  439. query: {
  440. ...generateFields(),
  441. statsPeriod: '60d',
  442. project: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].map(String),
  443. },
  444. },
  445. },
  446. });
  447. ProjectsStore.loadInitialData([ProjectFixture()]);
  448. const mockRequests = renderMockRequests();
  449. render(
  450. <Results
  451. organization={organization}
  452. location={router.location}
  453. router={router}
  454. loading={false}
  455. setSavedQuery={jest.fn()}
  456. />,
  457. {
  458. router: router,
  459. organization,
  460. }
  461. );
  462. expect(mockRequests.eventsResultsMock).toHaveBeenCalledTimes(0);
  463. await waitFor(() => {
  464. expect(mockRequests.measurementsMetaMock).toHaveBeenCalled();
  465. });
  466. });
  467. it('does not need confirmation on short queries', async function () {
  468. const organization = OrganizationFixture({
  469. features: ['discover-basic'],
  470. });
  471. const {router} = initializeOrg({
  472. organization,
  473. router: {
  474. location: {query: {...generateFields(), statsPeriod: '30d', project: '-1'}},
  475. },
  476. });
  477. ProjectsStore.loadInitialData([ProjectFixture()]);
  478. const mockRequests = renderMockRequests();
  479. render(
  480. <Results
  481. organization={organization}
  482. location={router.location}
  483. router={router}
  484. loading={false}
  485. setSavedQuery={jest.fn()}
  486. />,
  487. {
  488. router: router,
  489. organization,
  490. }
  491. );
  492. await waitFor(() => {
  493. expect(mockRequests.measurementsMetaMock).toHaveBeenCalled();
  494. });
  495. expect(mockRequests.eventsResultsMock).toHaveBeenCalledTimes(1);
  496. });
  497. it('does not need confirmation with to few projects', async function () {
  498. const organization = OrganizationFixture({
  499. features: ['discover-basic'],
  500. });
  501. const {router} = initializeOrg({
  502. organization,
  503. router: {
  504. location: {
  505. query: {
  506. ...generateFields(),
  507. statsPeriod: '90d',
  508. project: [1, 2, 3, 4].map(String),
  509. },
  510. },
  511. },
  512. });
  513. ProjectsStore.loadInitialData([ProjectFixture()]);
  514. const mockRequests = renderMockRequests();
  515. render(
  516. <Results
  517. organization={organization}
  518. location={router.location}
  519. router={router}
  520. loading={false}
  521. setSavedQuery={jest.fn()}
  522. />,
  523. {
  524. router: router,
  525. organization,
  526. }
  527. );
  528. await waitFor(() => {
  529. expect(mockRequests.measurementsMetaMock).toHaveBeenCalled();
  530. });
  531. expect(mockRequests.eventsResultsMock).toHaveBeenCalledTimes(1);
  532. });
  533. it('creates event view from saved query', async function () {
  534. const organization = OrganizationFixture({
  535. features,
  536. slug: 'org-slug',
  537. });
  538. const {router} = initializeOrg({
  539. organization,
  540. router: {
  541. location: {pathname: '/', query: {id: '1', statsPeriod: '24h'}},
  542. },
  543. });
  544. ProjectsStore.loadInitialData([ProjectFixture()]);
  545. const mockRequests = renderMockRequests();
  546. render(
  547. <Results
  548. organization={organization}
  549. location={router.location}
  550. router={router}
  551. loading={false}
  552. setSavedQuery={jest.fn()}
  553. />,
  554. {
  555. router: router,
  556. organization,
  557. }
  558. );
  559. await waitFor(() => expect(mockRequests.mockVisit).toHaveBeenCalled());
  560. expect(screen.getByRole('link', {name: 'timestamp'})).toHaveAttribute(
  561. 'href',
  562. '/?field=title&field=event.type&field=project&field=user.display&field=timestamp&id=1&name=new&query=&sort=-timestamp&statsPeriod=24h&topEvents=5'
  563. );
  564. expect(screen.getByRole('link', {name: 'project'})).toHaveAttribute(
  565. 'href',
  566. '/?field=title&field=event.type&field=project&field=user.display&field=timestamp&id=1&name=new&query=&sort=-project&statsPeriod=24h&topEvents=5'
  567. );
  568. // NOTE: This uses a legacy redirect for project event to the issue group event link
  569. expect(screen.getByRole('link', {name: 'deadbeef'})).toHaveAttribute(
  570. 'href',
  571. '/org-slug/project-slug/events/deadbeef/?id=1&referrer=discover-events-table&statsPeriod=24h'
  572. );
  573. expect(screen.getByRole('link', {name: 'user.display'})).toHaveAttribute(
  574. 'href',
  575. '/?field=title&field=event.type&field=project&field=user.display&field=timestamp&id=1&name=new&query=&sort=user.display&statsPeriod=24h&topEvents=5'
  576. );
  577. expect(screen.getByRole('link', {name: 'title'})).toHaveAttribute(
  578. 'href',
  579. '/?field=title&field=event.type&field=project&field=user.display&field=timestamp&id=1&name=new&query=&sort=-title&statsPeriod=24h&topEvents=5'
  580. );
  581. });
  582. it('overrides saved query params with location query params', async function () {
  583. const organization = OrganizationFixture({
  584. features,
  585. slug: 'org-slug',
  586. });
  587. const {router} = initializeOrg({
  588. organization,
  589. router: {
  590. location: {
  591. pathname: '/',
  592. query: {
  593. id: '1',
  594. statsPeriod: '7d',
  595. project: ['2'],
  596. environment: ['production'],
  597. },
  598. },
  599. },
  600. });
  601. ProjectsStore.loadInitialData([ProjectFixture()]);
  602. const mockRequests = renderMockRequests();
  603. render(
  604. <Results
  605. organization={organization}
  606. location={router.location}
  607. router={router}
  608. loading={false}
  609. setSavedQuery={jest.fn()}
  610. />,
  611. {
  612. router: router,
  613. organization,
  614. }
  615. );
  616. await waitFor(() => expect(mockRequests.mockVisit).toHaveBeenCalled());
  617. expect(screen.getByRole('link', {name: 'timestamp'})).toHaveAttribute(
  618. 'href',
  619. '/?environment=production&field=title&field=event.type&field=project&field=user.display&field=timestamp&id=1&name=new&project=2&query=&sort=-timestamp&statsPeriod=7d&topEvents=5'
  620. );
  621. });
  622. it('updates chart whenever yAxis parameter changes', async function () {
  623. const organization = OrganizationFixture({
  624. features,
  625. });
  626. const {router} = initializeOrg({
  627. organization,
  628. router: {
  629. location: {query: {...generateFields(), yAxis: 'count()'}},
  630. },
  631. });
  632. ProjectsStore.loadInitialData([ProjectFixture()]);
  633. const {eventsStatsMock, measurementsMetaMock} = renderMockRequests();
  634. const {rerender} = render(
  635. <Results
  636. organization={organization}
  637. location={router.location}
  638. router={router}
  639. loading={false}
  640. setSavedQuery={jest.fn()}
  641. />,
  642. {
  643. router: router,
  644. organization,
  645. }
  646. );
  647. // Should load events once
  648. expect(eventsStatsMock).toHaveBeenCalledTimes(1);
  649. expect(eventsStatsMock).toHaveBeenNthCalledWith(
  650. 1,
  651. '/organizations/org-slug/events-stats/',
  652. expect.objectContaining({
  653. query: expect.objectContaining({
  654. statsPeriod: '14d',
  655. yAxis: ['count()'],
  656. }),
  657. })
  658. );
  659. await waitFor(() => {
  660. expect(measurementsMetaMock).toHaveBeenCalled();
  661. });
  662. // Update location simulating a browser back button action
  663. rerender(
  664. <Results
  665. organization={organization}
  666. location={{
  667. ...router.location,
  668. query: {...generateFields(), yAxis: 'count_unique(user)'},
  669. }}
  670. router={router}
  671. loading={false}
  672. setSavedQuery={jest.fn()}
  673. />
  674. );
  675. // Should load events again
  676. expect(eventsStatsMock).toHaveBeenCalledTimes(2);
  677. expect(eventsStatsMock).toHaveBeenNthCalledWith(
  678. 2,
  679. '/organizations/org-slug/events-stats/',
  680. expect.objectContaining({
  681. query: expect.objectContaining({
  682. statsPeriod: '14d',
  683. yAxis: ['count_unique(user)'],
  684. }),
  685. })
  686. );
  687. await waitFor(() => {
  688. expect(measurementsMetaMock).toHaveBeenCalled();
  689. });
  690. });
  691. it('updates chart whenever display parameter changes', async function () {
  692. const organization = OrganizationFixture({
  693. features,
  694. });
  695. const {router} = initializeOrg({
  696. organization,
  697. router: {
  698. location: {query: {...generateFields(), display: 'default', yAxis: 'count()'}},
  699. },
  700. });
  701. const {eventsStatsMock, measurementsMetaMock} = renderMockRequests();
  702. ProjectsStore.loadInitialData([ProjectFixture()]);
  703. const {rerender} = render(
  704. <Results
  705. organization={organization}
  706. location={router.location}
  707. router={router}
  708. loading={false}
  709. setSavedQuery={jest.fn()}
  710. />,
  711. {
  712. router: router,
  713. organization,
  714. }
  715. );
  716. // Should load events once
  717. expect(eventsStatsMock).toHaveBeenCalledTimes(1);
  718. expect(eventsStatsMock).toHaveBeenNthCalledWith(
  719. 1,
  720. '/organizations/org-slug/events-stats/',
  721. expect.objectContaining({
  722. query: expect.objectContaining({
  723. statsPeriod: '14d',
  724. yAxis: ['count()'],
  725. }),
  726. })
  727. );
  728. await waitFor(() => {
  729. expect(measurementsMetaMock).toHaveBeenCalled();
  730. });
  731. // Update location simulating a browser back button action
  732. rerender(
  733. <Results
  734. organization={organization}
  735. location={{
  736. ...router.location,
  737. query: {...generateFields(), display: 'previous', yAxis: 'count()'},
  738. }}
  739. router={router}
  740. loading={false}
  741. setSavedQuery={jest.fn()}
  742. />
  743. );
  744. // Should load events again
  745. expect(eventsStatsMock).toHaveBeenCalledTimes(2);
  746. expect(eventsStatsMock).toHaveBeenNthCalledWith(
  747. 2,
  748. '/organizations/org-slug/events-stats/',
  749. expect.objectContaining({
  750. query: expect.objectContaining({
  751. statsPeriod: '28d',
  752. yAxis: ['count()'],
  753. }),
  754. })
  755. );
  756. await waitFor(() => {
  757. expect(measurementsMetaMock).toHaveBeenCalled();
  758. });
  759. });
  760. it('updates chart whenever display and yAxis parameters change', async function () {
  761. const organization = OrganizationFixture({
  762. features,
  763. });
  764. const {router} = initializeOrg({
  765. organization,
  766. router: {
  767. location: {query: {...generateFields(), display: 'default', yAxis: 'count()'}},
  768. },
  769. });
  770. const {eventsStatsMock, measurementsMetaMock} = renderMockRequests();
  771. ProjectsStore.loadInitialData([ProjectFixture()]);
  772. const {rerender} = render(
  773. <Results
  774. organization={organization}
  775. location={router.location}
  776. router={router}
  777. loading={false}
  778. setSavedQuery={jest.fn()}
  779. />,
  780. {
  781. router: router,
  782. organization,
  783. }
  784. );
  785. // Should load events once
  786. expect(eventsStatsMock).toHaveBeenCalledTimes(1);
  787. expect(eventsStatsMock).toHaveBeenNthCalledWith(
  788. 1,
  789. '/organizations/org-slug/events-stats/',
  790. expect.objectContaining({
  791. query: expect.objectContaining({
  792. statsPeriod: '14d',
  793. yAxis: ['count()'],
  794. }),
  795. })
  796. );
  797. await waitFor(() => {
  798. expect(measurementsMetaMock).toHaveBeenCalled();
  799. });
  800. // Update location simulating a browser back button action
  801. rerender(
  802. <Results
  803. organization={organization}
  804. location={{
  805. ...router.location,
  806. query: {
  807. ...generateFields(),
  808. display: 'previous',
  809. yAxis: 'count_unique(user)',
  810. },
  811. }}
  812. router={router}
  813. loading={false}
  814. setSavedQuery={jest.fn()}
  815. />
  816. );
  817. // Should load events again
  818. expect(eventsStatsMock).toHaveBeenCalledTimes(2);
  819. expect(eventsStatsMock).toHaveBeenNthCalledWith(
  820. 2,
  821. '/organizations/org-slug/events-stats/',
  822. expect.objectContaining({
  823. query: expect.objectContaining({
  824. statsPeriod: '28d',
  825. yAxis: ['count_unique(user)'],
  826. }),
  827. })
  828. );
  829. await waitFor(() => {
  830. expect(measurementsMetaMock).toHaveBeenCalled();
  831. });
  832. });
  833. it('appends tag value to existing query when clicked', async function () {
  834. const organization = OrganizationFixture({
  835. features,
  836. });
  837. const {router} = initializeOrg({
  838. organization,
  839. router: {
  840. location: {query: {...generateFields(), display: 'default', yAxis: 'count'}},
  841. },
  842. });
  843. const mockRequests = renderMockRequests();
  844. ProjectsStore.loadInitialData([ProjectFixture()]);
  845. render(
  846. <Results
  847. organization={organization}
  848. location={router.location}
  849. router={router}
  850. loading={false}
  851. setSavedQuery={jest.fn()}
  852. />,
  853. {
  854. router: router,
  855. organization,
  856. }
  857. );
  858. await userEvent.click(await screen.findByRole('button', {name: 'Show Tags'}));
  859. await waitFor(() => expect(mockRequests.eventFacetsMock).toHaveBeenCalled());
  860. // TODO(edward): update this to be less generic
  861. await userEvent.click(screen.getByText('environment'));
  862. await userEvent.click(screen.getByText('foo'));
  863. // since environment collides with the environment field, it is wrapped with `tags[...]`
  864. expect(
  865. await screen.findByRole('link', {
  866. name: 'environment, dev, 100% of all events. View events with this tag value.',
  867. })
  868. ).toBeInTheDocument();
  869. expect(
  870. screen.getByRole('link', {
  871. name: 'foo, bar, 100% of all events. View events with this tag value.',
  872. })
  873. ).toBeInTheDocument();
  874. });
  875. it('respects pinned filters for prebuilt queries', async function () {
  876. const organization = OrganizationFixture({
  877. features: [...features, 'global-views'],
  878. });
  879. const {router} = initializeOrg({
  880. organization,
  881. router: {
  882. location: {query: {...generateFields(), display: 'default', yAxis: 'count'}},
  883. },
  884. });
  885. renderMockRequests();
  886. jest.spyOn(PageFilterPersistence, 'getPageFilterStorage').mockReturnValue({
  887. state: {
  888. project: [1],
  889. environment: [],
  890. start: null,
  891. end: null,
  892. period: '14d',
  893. utc: null,
  894. },
  895. pinnedFilters: new Set(['projects']),
  896. });
  897. ProjectsStore.loadInitialData([ProjectFixture({id: '1', slug: 'Pinned Project'})]);
  898. render(
  899. <Results
  900. organization={organization}
  901. location={router.location}
  902. router={router}
  903. loading={false}
  904. setSavedQuery={jest.fn()}
  905. />,
  906. {router: router, organization}
  907. );
  908. const projectPageFilter = await screen.findByTestId('page-filter-project-selector');
  909. expect(projectPageFilter).toHaveTextContent('All Projects');
  910. });
  911. it('displays tip when events response contains a tip', async function () {
  912. renderMockRequests();
  913. MockApiClient.addMockResponse({
  914. url: '/organizations/org-slug/events/',
  915. body: {
  916. meta: {
  917. fields: {},
  918. tips: {query: 'this is a tip'},
  919. },
  920. data: [],
  921. },
  922. });
  923. const organization = OrganizationFixture({
  924. features,
  925. });
  926. const {router} = initializeOrg({
  927. organization,
  928. router: {
  929. location: {query: {...generateFields(), yAxis: 'count()'}},
  930. },
  931. });
  932. ProjectsStore.loadInitialData([ProjectFixture()]);
  933. render(
  934. <Results
  935. organization={organization}
  936. location={router.location}
  937. router={router}
  938. loading={false}
  939. setSavedQuery={jest.fn()}
  940. />,
  941. {router: router, organization}
  942. );
  943. expect(await screen.findByText('this is a tip')).toBeInTheDocument();
  944. });
  945. it('renders metric fallback alert', async function () {
  946. const organization = OrganizationFixture({
  947. features: ['discover-basic'],
  948. });
  949. const {router} = initializeOrg({
  950. organization,
  951. router: {
  952. location: {query: {fromMetric: 'true', id: '1'}},
  953. },
  954. });
  955. ProjectsStore.loadInitialData([ProjectFixture()]);
  956. renderMockRequests();
  957. render(
  958. <Results
  959. organization={organization}
  960. location={router.location}
  961. router={router}
  962. loading={false}
  963. setSavedQuery={jest.fn()}
  964. />,
  965. {
  966. router: router,
  967. organization,
  968. }
  969. );
  970. expect(
  971. await screen.findByText(
  972. /You've navigated to this page from a performance metric widget generated from processed events/
  973. )
  974. ).toBeInTheDocument();
  975. });
  976. it('renders unparameterized data banner', async function () {
  977. const organization = OrganizationFixture({
  978. features: ['discover-basic'],
  979. });
  980. const {router} = initializeOrg({
  981. organization,
  982. router: {
  983. location: {query: {showUnparameterizedBanner: 'true', id: '1'}},
  984. },
  985. });
  986. ProjectsStore.loadInitialData([ProjectFixture()]);
  987. renderMockRequests();
  988. render(
  989. <Results
  990. organization={organization}
  991. location={router.location}
  992. router={router}
  993. loading={false}
  994. setSavedQuery={jest.fn()}
  995. />,
  996. {
  997. router: router,
  998. organization,
  999. }
  1000. );
  1001. expect(
  1002. await screen.findByText(/These are unparameterized transactions/)
  1003. ).toBeInTheDocument();
  1004. });
  1005. it('updates the homepage query with up to date eventView when Set as Default is clicked', async () => {
  1006. const mockHomepageUpdate = MockApiClient.addMockResponse({
  1007. url: '/organizations/org-slug/discover/homepage/',
  1008. method: 'PUT',
  1009. statusCode: 200,
  1010. });
  1011. const organization = OrganizationFixture({
  1012. features: ['discover-basic', 'discover-query'],
  1013. });
  1014. const {router} = initializeOrg({
  1015. organization,
  1016. router: {
  1017. // These fields take priority and should be sent in the request
  1018. location: {query: {field: ['title', 'user'], id: '1'}},
  1019. },
  1020. });
  1021. ProjectsStore.loadInitialData([ProjectFixture()]);
  1022. renderMockRequests();
  1023. render(
  1024. <Results
  1025. organization={organization}
  1026. location={router.location}
  1027. router={router}
  1028. loading={false}
  1029. setSavedQuery={jest.fn()}
  1030. />,
  1031. {router: router, organization}
  1032. );
  1033. await waitFor(() =>
  1034. expect(screen.getByRole('button', {name: /set as default/i})).toBeEnabled()
  1035. );
  1036. await userEvent.click(screen.getByText('Set as Default'));
  1037. expect(mockHomepageUpdate).toHaveBeenCalledWith(
  1038. '/organizations/org-slug/discover/homepage/',
  1039. expect.objectContaining({
  1040. data: expect.objectContaining({
  1041. fields: ['title', 'user'],
  1042. }),
  1043. })
  1044. );
  1045. });
  1046. it('Changes the Use as Discover button to a reset button for saved query', async () => {
  1047. renderMockRequests();
  1048. MockApiClient.addMockResponse({
  1049. url: '/organizations/org-slug/discover/homepage/',
  1050. method: 'PUT',
  1051. statusCode: 200,
  1052. body: {
  1053. id: '2',
  1054. name: '',
  1055. projects: [],
  1056. version: 2,
  1057. expired: false,
  1058. dateCreated: '2021-04-08T17:53:25.195782Z',
  1059. dateUpdated: '2021-04-09T12:13:18.567264Z',
  1060. createdBy: {
  1061. id: '2',
  1062. },
  1063. environment: [],
  1064. fields: ['title', 'event.type', 'project', 'user.display', 'timestamp'],
  1065. widths: ['-1', '-1', '-1', '-1', '-1'],
  1066. range: '24h',
  1067. orderby: '-user.display',
  1068. queryDataset: 'discover',
  1069. },
  1070. });
  1071. const organization = OrganizationFixture({
  1072. features: ['discover-basic', 'discover-query'],
  1073. });
  1074. const {router} = initializeOrg({
  1075. organization,
  1076. router: {
  1077. location: {query: {id: '1'}},
  1078. },
  1079. });
  1080. ProjectsStore.loadInitialData([ProjectFixture()]);
  1081. renderMockRequests();
  1082. const {rerender} = render(
  1083. <Results
  1084. loading={false}
  1085. setSavedQuery={jest.fn()}
  1086. organization={organization}
  1087. location={router.location}
  1088. router={router}
  1089. />,
  1090. {router: router, organization}
  1091. );
  1092. await waitFor(() =>
  1093. expect(screen.getByRole('button', {name: /set as default/i})).toBeEnabled()
  1094. );
  1095. await userEvent.click(screen.getByText('Set as Default'));
  1096. expect(await screen.findByText('Remove Default')).toBeInTheDocument();
  1097. await userEvent.click(screen.getByText('Total Period'));
  1098. await userEvent.click(screen.getByText('Previous Period'));
  1099. const rerenderData = initializeOrg({
  1100. organization,
  1101. router: {
  1102. location: {query: {...router.location.query, display: 'previous'}},
  1103. },
  1104. });
  1105. rerender(
  1106. <Results
  1107. loading={false}
  1108. setSavedQuery={jest.fn()}
  1109. organization={organization}
  1110. location={rerenderData.router.location}
  1111. router={rerenderData.router}
  1112. />
  1113. );
  1114. screen.getByText('Previous Period');
  1115. expect(await screen.findByText('Set as Default')).toBeInTheDocument();
  1116. });
  1117. it('Changes the Use as Discover button to a reset button for prebuilt query', async () => {
  1118. const organization = OrganizationFixture({
  1119. features: ['discover-basic', 'discover-query'],
  1120. });
  1121. MockApiClient.addMockResponse({
  1122. url: '/organizations/org-slug/discover/homepage/',
  1123. method: 'PUT',
  1124. statusCode: 200,
  1125. body: {...getTransactionViews(organization)[0], name: ''},
  1126. });
  1127. const {router} = initializeOrg({
  1128. organization,
  1129. router: {
  1130. location: {
  1131. ...LocationFixture(),
  1132. query: {
  1133. ...EventView.fromNewQueryWithLocation(
  1134. getTransactionViews(organization)[0],
  1135. LocationFixture()
  1136. ).generateQueryStringObject(),
  1137. },
  1138. },
  1139. },
  1140. });
  1141. ProjectsStore.loadInitialData([ProjectFixture()]);
  1142. renderMockRequests();
  1143. const {rerender} = render(
  1144. <Results
  1145. organization={organization}
  1146. location={router.location}
  1147. router={router}
  1148. loading={false}
  1149. setSavedQuery={jest.fn()}
  1150. />,
  1151. {router: router, organization}
  1152. );
  1153. await screen.findAllByText(getTransactionViews(organization)[0].name);
  1154. await userEvent.click(screen.getByText('Set as Default'));
  1155. expect(await screen.findByText('Remove Default')).toBeInTheDocument();
  1156. await userEvent.click(screen.getByText('Total Period'));
  1157. await userEvent.click(screen.getByText('Previous Period'));
  1158. const rerenderData = initializeOrg({
  1159. organization,
  1160. router: {
  1161. location: {query: {...router.location.query, display: 'previous'}},
  1162. },
  1163. });
  1164. rerender(
  1165. <Results
  1166. organization={organization}
  1167. location={rerenderData.router.location}
  1168. router={rerenderData.router}
  1169. loading={false}
  1170. setSavedQuery={jest.fn()}
  1171. />
  1172. );
  1173. screen.getByText('Previous Period');
  1174. expect(await screen.findByText('Set as Default')).toBeInTheDocument();
  1175. });
  1176. it('links back to the homepage through the Discover breadcrumb', async () => {
  1177. const organization = OrganizationFixture({
  1178. features: ['discover-basic', 'discover-query'],
  1179. });
  1180. const {router} = initializeOrg({
  1181. organization,
  1182. router: {
  1183. location: {query: {id: '1'}},
  1184. },
  1185. });
  1186. ProjectsStore.loadInitialData([ProjectFixture()]);
  1187. const {measurementsMetaMock} = renderMockRequests();
  1188. render(
  1189. <Results
  1190. organization={organization}
  1191. location={router.location}
  1192. router={router}
  1193. loading={false}
  1194. setSavedQuery={jest.fn()}
  1195. />,
  1196. {router: router, organization}
  1197. );
  1198. await waitFor(() => {
  1199. expect(measurementsMetaMock).toHaveBeenCalled();
  1200. });
  1201. expect(screen.getByText('Discover')).toHaveAttribute(
  1202. 'href',
  1203. expect.stringMatching(new RegExp('^/organizations/org-slug/discover/homepage/'))
  1204. );
  1205. });
  1206. it('links back to the Saved Queries through the Saved Queries breadcrumb', async () => {
  1207. const organization = OrganizationFixture({
  1208. features: ['discover-basic', 'discover-query'],
  1209. });
  1210. const {router} = initializeOrg({
  1211. organization,
  1212. router: {
  1213. location: {query: {id: '1'}},
  1214. },
  1215. });
  1216. const {measurementsMetaMock} = renderMockRequests();
  1217. render(
  1218. <Results
  1219. organization={organization}
  1220. location={router.location}
  1221. router={router}
  1222. loading={false}
  1223. setSavedQuery={jest.fn()}
  1224. />,
  1225. {router: router, organization}
  1226. );
  1227. await waitFor(() => {
  1228. expect(measurementsMetaMock).toHaveBeenCalled();
  1229. });
  1230. expect(screen.getByRole('link', {name: 'Saved Queries'})).toHaveAttribute(
  1231. 'href',
  1232. expect.stringMatching(new RegExp('^/organizations/org-slug/discover/queries/'))
  1233. );
  1234. });
  1235. it('allows users to Set As Default on the All Events query', async () => {
  1236. const organization = OrganizationFixture({
  1237. features: ['discover-basic', 'discover-query'],
  1238. });
  1239. const {router} = initializeOrg({
  1240. organization,
  1241. router: {
  1242. location: {
  1243. ...LocationFixture(),
  1244. query: {
  1245. ...EventView.fromNewQueryWithLocation(
  1246. DEFAULT_EVENT_VIEW,
  1247. LocationFixture()
  1248. ).generateQueryStringObject(),
  1249. },
  1250. },
  1251. },
  1252. });
  1253. ProjectsStore.loadInitialData([ProjectFixture()]);
  1254. const {measurementsMetaMock} = renderMockRequests();
  1255. render(
  1256. <Results
  1257. organization={organization}
  1258. location={router.location}
  1259. router={router}
  1260. loading={false}
  1261. setSavedQuery={jest.fn()}
  1262. />,
  1263. {router: router, organization}
  1264. );
  1265. await waitFor(() => {
  1266. expect(measurementsMetaMock).toHaveBeenCalled();
  1267. });
  1268. expect(screen.getByTestId('set-as-default')).toBeEnabled();
  1269. });
  1270. it("doesn't render sample data alert", async function () {
  1271. const organization = OrganizationFixture({
  1272. features: ['discover-basic', 'discover-query'],
  1273. });
  1274. const {router} = initializeOrg({
  1275. organization,
  1276. router: {
  1277. location: {
  1278. ...LocationFixture(),
  1279. query: {
  1280. ...EventView.fromNewQueryWithLocation(
  1281. {...DEFAULT_EVENT_VIEW, query: 'event.type:error'},
  1282. LocationFixture()
  1283. ).generateQueryStringObject(),
  1284. },
  1285. },
  1286. },
  1287. });
  1288. const {measurementsMetaMock} = renderMockRequests();
  1289. render(
  1290. <Results
  1291. organization={organization}
  1292. location={router.location}
  1293. router={router}
  1294. loading={false}
  1295. setSavedQuery={jest.fn()}
  1296. />,
  1297. {router: router, organization}
  1298. );
  1299. await waitFor(() => {
  1300. expect(measurementsMetaMock).toHaveBeenCalled();
  1301. });
  1302. expect(screen.queryByText(/Based on your search criteria/)).not.toBeInTheDocument();
  1303. });
  1304. it('uses split decision to populate dataset selector', async function () {
  1305. const organization = OrganizationFixture({
  1306. features: [
  1307. 'discover-basic',
  1308. 'discover-query',
  1309. 'performance-discover-dataset-selector',
  1310. ],
  1311. });
  1312. const {router} = initializeOrg({
  1313. organization,
  1314. router: {
  1315. location: {query: {id: '1'}},
  1316. },
  1317. });
  1318. ProjectsStore.loadInitialData([ProjectFixture()]);
  1319. const mockRequests = renderMockRequests();
  1320. render(
  1321. <Results
  1322. organization={organization}
  1323. location={router.location}
  1324. router={router}
  1325. loading={false}
  1326. setSavedQuery={jest.fn()}
  1327. />,
  1328. {
  1329. router: router,
  1330. organization,
  1331. }
  1332. );
  1333. await waitFor(() => {
  1334. expect(mockRequests.measurementsMetaMock).toHaveBeenCalled();
  1335. });
  1336. expect(mockRequests.eventsResultsMock).toHaveBeenCalledTimes(1);
  1337. await waitFor(() => {
  1338. expect(screen.getByRole('tab', {name: 'Transactions'})).toHaveAttribute(
  1339. 'aria-selected',
  1340. 'true'
  1341. );
  1342. });
  1343. expect(
  1344. screen.getByText(
  1345. "We're splitting our datasets up to make it a bit easier to digest. We defaulted this query to Transactions. Edit as you see fit."
  1346. )
  1347. ).toBeInTheDocument();
  1348. expect(screen.queryByText('Save Changes')).not.toBeInTheDocument();
  1349. });
  1350. it('calls events endpoint with the right dataset', async function () {
  1351. const organization = OrganizationFixture({
  1352. features: [
  1353. 'discover-basic',
  1354. 'discover-query',
  1355. 'performance-discover-dataset-selector',
  1356. ],
  1357. });
  1358. const {router} = initializeOrg({
  1359. organization,
  1360. router: {
  1361. location: {query: {id: '1'}},
  1362. },
  1363. });
  1364. ProjectsStore.loadInitialData([ProjectFixture()]);
  1365. const mockRequests = renderMockRequests();
  1366. MockApiClient.addMockResponse({
  1367. url: '/organizations/org-slug/discover/saved/1/',
  1368. method: 'GET',
  1369. statusCode: 200,
  1370. body: {
  1371. id: '1',
  1372. name: 'new',
  1373. projects: [],
  1374. version: 2,
  1375. expired: false,
  1376. dateCreated: '2021-04-08T17:53:25.195782Z',
  1377. dateUpdated: '2021-04-09T12:13:18.567264Z',
  1378. createdBy: {
  1379. id: '2',
  1380. },
  1381. environment: [],
  1382. fields: ['title', 'event.type', 'project', 'user.display', 'timestamp'],
  1383. widths: ['-1', '-1', '-1', '-1', '-1'],
  1384. range: '24h',
  1385. orderby: '-user.display',
  1386. queryDataset: 'error-events',
  1387. },
  1388. });
  1389. render(
  1390. <Results
  1391. organization={organization}
  1392. location={router.location}
  1393. router={router}
  1394. loading={false}
  1395. setSavedQuery={jest.fn()}
  1396. />,
  1397. {
  1398. router: router,
  1399. organization,
  1400. }
  1401. );
  1402. await waitFor(() => {
  1403. expect(mockRequests.measurementsMetaMock).toHaveBeenCalled();
  1404. });
  1405. expect(mockRequests.eventsResultsMock).toHaveBeenCalledTimes(1);
  1406. expect(screen.getByRole('tab', {name: 'Errors'})).toHaveAttribute(
  1407. 'aria-selected',
  1408. 'true'
  1409. );
  1410. expect(mockRequests.eventsStatsMock).toHaveBeenCalledWith(
  1411. '/organizations/org-slug/events-stats/',
  1412. expect.objectContaining({
  1413. query: expect.objectContaining({
  1414. dataset: 'errors',
  1415. }),
  1416. })
  1417. );
  1418. expect(mockRequests.eventsResultsMock).toHaveBeenCalledWith(
  1419. '/organizations/org-slug/events/',
  1420. expect.objectContaining({
  1421. query: expect.objectContaining({
  1422. dataset: 'errors',
  1423. }),
  1424. })
  1425. );
  1426. expect(mockRequests.eventsMetaMock).toHaveBeenCalledWith(
  1427. '/organizations/org-slug/events-meta/',
  1428. expect.objectContaining({
  1429. query: expect.objectContaining({
  1430. dataset: 'errors',
  1431. }),
  1432. })
  1433. );
  1434. });
  1435. it('does not automatically append dataset with selector feature disabled', async function () {
  1436. const organization = OrganizationFixture({
  1437. features: ['discover-basic', 'discover-query'],
  1438. });
  1439. const {router} = initializeOrg({
  1440. organization,
  1441. router: {
  1442. location: {query: {id: '1'}},
  1443. },
  1444. });
  1445. ProjectsStore.loadInitialData([ProjectFixture()]);
  1446. const mockRequests = renderMockRequests();
  1447. MockApiClient.addMockResponse({
  1448. url: '/organizations/org-slug/discover/saved/1/',
  1449. method: 'GET',
  1450. statusCode: 200,
  1451. body: {
  1452. id: '1',
  1453. name: 'new',
  1454. projects: [],
  1455. version: 2,
  1456. expired: false,
  1457. dateCreated: '2021-04-08T17:53:25.195782Z',
  1458. dateUpdated: '2021-04-09T12:13:18.567264Z',
  1459. createdBy: {
  1460. id: '2',
  1461. },
  1462. environment: [],
  1463. fields: ['title', 'event.type', 'project', 'user.display', 'timestamp'],
  1464. widths: ['-1', '-1', '-1', '-1', '-1'],
  1465. range: '24h',
  1466. orderby: '-user.display',
  1467. queryDataset: 'error-events',
  1468. },
  1469. });
  1470. render(
  1471. <Results
  1472. organization={organization}
  1473. location={router.location}
  1474. router={router}
  1475. loading={false}
  1476. setSavedQuery={jest.fn()}
  1477. />,
  1478. {
  1479. router: router,
  1480. organization,
  1481. }
  1482. );
  1483. await waitFor(() => {
  1484. expect(mockRequests.measurementsMetaMock).toHaveBeenCalled();
  1485. });
  1486. expect(mockRequests.eventsResultsMock).toHaveBeenCalledTimes(1);
  1487. expect(
  1488. screen.queryByRole('button', {name: 'Dataset Errors'})
  1489. ).not.toBeInTheDocument();
  1490. expect(mockRequests.eventsStatsMock).toHaveBeenCalledWith(
  1491. '/organizations/org-slug/events-stats/',
  1492. expect.objectContaining({
  1493. query: expect.not.objectContaining({
  1494. dataset: 'errors',
  1495. }),
  1496. })
  1497. );
  1498. expect(mockRequests.eventsResultsMock).toHaveBeenCalledWith(
  1499. '/organizations/org-slug/events/',
  1500. expect.objectContaining({
  1501. query: expect.not.objectContaining({
  1502. dataset: 'errors',
  1503. }),
  1504. })
  1505. );
  1506. expect(mockRequests.eventsMetaMock).toHaveBeenCalledWith(
  1507. '/organizations/org-slug/events-meta/',
  1508. expect.objectContaining({
  1509. query: expect.not.objectContaining({
  1510. dataset: 'errors',
  1511. }),
  1512. })
  1513. );
  1514. });
  1515. it('shows the search history for the error dataset', async function () {
  1516. const organization = OrganizationFixture({
  1517. features: [
  1518. 'discover-basic',
  1519. 'discover-query',
  1520. 'performance-discover-dataset-selector',
  1521. ],
  1522. });
  1523. const {router} = initializeOrg({
  1524. organization,
  1525. router: {
  1526. location: {query: {id: '1'}},
  1527. },
  1528. });
  1529. ProjectsStore.loadInitialData([ProjectFixture()]);
  1530. renderMockRequests();
  1531. MockApiClient.addMockResponse({
  1532. url: '/organizations/org-slug/recent-searches/',
  1533. body: [
  1534. {
  1535. query: 'event.type:error',
  1536. },
  1537. ],
  1538. match: [
  1539. (_url, options) => {
  1540. return options.query?.type === SavedSearchType.ERROR;
  1541. },
  1542. ],
  1543. });
  1544. MockApiClient.addMockResponse({
  1545. url: '/organizations/org-slug/recent-searches/',
  1546. body: [
  1547. {
  1548. query: 'transaction.status:ok',
  1549. },
  1550. ],
  1551. match: [
  1552. (_url, options) => {
  1553. return options.query?.type === SavedSearchType.TRANSACTION;
  1554. },
  1555. ],
  1556. });
  1557. MockApiClient.addMockResponse({
  1558. url: '/organizations/org-slug/discover/saved/1/',
  1559. method: 'GET',
  1560. statusCode: 200,
  1561. body: {
  1562. id: '1',
  1563. name: 'new',
  1564. projects: [],
  1565. version: 2,
  1566. expired: false,
  1567. dateCreated: '2021-04-08T17:53:25.195782Z',
  1568. dateUpdated: '2021-04-09T12:13:18.567264Z',
  1569. createdBy: {
  1570. id: '2',
  1571. },
  1572. environment: [],
  1573. fields: ['title', 'event.type', 'project', 'user.display', 'timestamp'],
  1574. widths: ['-1', '-1', '-1', '-1', '-1'],
  1575. range: '24h',
  1576. orderby: '-user.display',
  1577. queryDataset: 'error-events',
  1578. },
  1579. });
  1580. render(
  1581. <Results
  1582. organization={organization}
  1583. location={router.location}
  1584. router={router}
  1585. loading={false}
  1586. setSavedQuery={jest.fn()}
  1587. />,
  1588. {
  1589. router: router,
  1590. organization,
  1591. }
  1592. );
  1593. await userEvent.click(
  1594. screen.getByPlaceholderText('Search for events, users, tags, and more')
  1595. );
  1596. expect(screen.getByTestId('filter-token')).toHaveTextContent('event.type:error');
  1597. });
  1598. it('shows the search history for the transaction dataset', async function () {
  1599. const organization = OrganizationFixture({
  1600. features: [
  1601. 'discover-basic',
  1602. 'discover-query',
  1603. 'performance-discover-dataset-selector',
  1604. ],
  1605. });
  1606. const {router} = initializeOrg({
  1607. organization,
  1608. router: {
  1609. location: {query: {id: '1'}},
  1610. },
  1611. });
  1612. ProjectsStore.loadInitialData([ProjectFixture()]);
  1613. renderMockRequests();
  1614. MockApiClient.addMockResponse({
  1615. url: '/organizations/org-slug/recent-searches/',
  1616. body: [
  1617. {
  1618. query: 'event.type:error',
  1619. },
  1620. ],
  1621. match: [
  1622. (_url, options) => {
  1623. return options.query?.type === SavedSearchType.ERROR;
  1624. },
  1625. ],
  1626. });
  1627. MockApiClient.addMockResponse({
  1628. url: '/organizations/org-slug/recent-searches/',
  1629. body: [
  1630. {
  1631. query: 'transaction.status:ok',
  1632. },
  1633. ],
  1634. match: [
  1635. (_url, options) => {
  1636. return options.query?.type === SavedSearchType.TRANSACTION;
  1637. },
  1638. ],
  1639. });
  1640. MockApiClient.addMockResponse({
  1641. url: '/organizations/org-slug/events/',
  1642. body: {
  1643. meta: {
  1644. fields: {
  1645. id: 'string',
  1646. title: 'string',
  1647. 'project.name': 'string',
  1648. timestamp: 'date',
  1649. 'user.id': 'string',
  1650. },
  1651. discoverSplitDecision: 'transaction-like',
  1652. },
  1653. data: [
  1654. {
  1655. trace: 'test',
  1656. id: 'deadbeef',
  1657. 'user.id': 'alberto leal',
  1658. title: eventTitle,
  1659. 'project.name': 'project-slug',
  1660. timestamp: '2019-05-23T22:12:48+00:00',
  1661. },
  1662. ],
  1663. },
  1664. });
  1665. MockApiClient.addMockResponse({
  1666. url: '/organizations/org-slug/discover/saved/1/',
  1667. method: 'GET',
  1668. statusCode: 200,
  1669. body: {
  1670. id: '1',
  1671. name: 'new',
  1672. projects: [],
  1673. version: 2,
  1674. expired: false,
  1675. dateCreated: '2021-04-08T17:53:25.195782Z',
  1676. dateUpdated: '2021-04-09T12:13:18.567264Z',
  1677. createdBy: {
  1678. id: '2',
  1679. },
  1680. environment: [],
  1681. fields: ['title', 'event.type', 'project', 'user.display', 'timestamp'],
  1682. widths: ['-1', '-1', '-1', '-1', '-1'],
  1683. range: '24h',
  1684. orderby: '-user.display',
  1685. queryDataset: 'transaction-like',
  1686. },
  1687. });
  1688. render(
  1689. <Results
  1690. organization={organization}
  1691. location={router.location}
  1692. router={router}
  1693. loading={false}
  1694. setSavedQuery={jest.fn()}
  1695. />,
  1696. {
  1697. router: router,
  1698. organization,
  1699. }
  1700. );
  1701. await userEvent.click(
  1702. screen.getByPlaceholderText('Search for events, users, tags, and more')
  1703. );
  1704. expect(screen.getByTestId('filter-token')).toHaveTextContent(
  1705. 'transaction.status:ok'
  1706. );
  1707. });
  1708. });
  1709. });