homepage.spec.tsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. import {browserHistory} from 'react-router';
  2. import {initializeOrg} from 'sentry-test/initializeOrg';
  3. import {mountGlobalModal} from 'sentry-test/modal';
  4. import {act, render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
  5. import * as pageFilterUtils from 'sentry/components/organizations/pageFilters/persistence';
  6. import ProjectsStore from 'sentry/stores/projectsStore';
  7. import EventView from 'sentry/utils/discover/eventView';
  8. import {DEFAULT_EVENT_VIEW} from './data';
  9. import Homepage from './homepage';
  10. describe('Discover > Homepage', () => {
  11. const features = [
  12. 'global-views',
  13. 'discover-query',
  14. 'discover-query-builder-as-landing-page',
  15. ];
  16. let initialData, organization, mockHomepage;
  17. beforeEach(() => {
  18. organization = TestStubs.Organization({
  19. features,
  20. });
  21. initialData = initializeOrg({
  22. ...initializeOrg(),
  23. organization,
  24. router: {
  25. location: TestStubs.location(),
  26. },
  27. });
  28. MockApiClient.addMockResponse({
  29. url: '/organizations/org-slug/eventsv2/',
  30. body: [],
  31. });
  32. MockApiClient.addMockResponse({
  33. url: '/organizations/org-slug/events-meta/',
  34. body: {
  35. count: 2,
  36. },
  37. });
  38. MockApiClient.addMockResponse({
  39. url: '/organizations/org-slug/events-stats/',
  40. body: {data: [[123, []]]},
  41. });
  42. MockApiClient.addMockResponse({
  43. url: '/organizations/org-slug/tags/',
  44. body: [],
  45. });
  46. MockApiClient.addMockResponse({
  47. url: '/organizations/org-slug/releases/stats/',
  48. body: [],
  49. });
  50. mockHomepage = MockApiClient.addMockResponse({
  51. url: '/organizations/org-slug/discover/homepage/',
  52. method: 'GET',
  53. statusCode: 200,
  54. body: {
  55. id: '2',
  56. name: 'homepage query',
  57. projects: [],
  58. version: 2,
  59. expired: false,
  60. dateCreated: '2021-04-08T17:53:25.195782Z',
  61. dateUpdated: '2021-04-09T12:13:18.567264Z',
  62. createdBy: {
  63. id: '2',
  64. },
  65. environment: ['alpha'],
  66. fields: ['environment'],
  67. widths: ['-1'],
  68. range: '24h',
  69. orderby: '-environment',
  70. display: 'previous',
  71. query: 'event.type:error',
  72. },
  73. });
  74. });
  75. it('renders the Discover banner', async () => {
  76. render(
  77. <Homepage
  78. organization={organization}
  79. location={initialData.router.location}
  80. router={initialData.router}
  81. setSavedQuery={jest.fn()}
  82. loading={false}
  83. />,
  84. {context: initialData.routerContext, organization: initialData.organization}
  85. );
  86. await screen.findByText('Discover Trends');
  87. screen.getByText('Get a Tour');
  88. expect(screen.queryByText('Build a new query')).not.toBeInTheDocument();
  89. });
  90. it('fetches from the homepage URL and renders fields, page filters, and chart information', async () => {
  91. render(
  92. <Homepage
  93. organization={organization}
  94. location={initialData.router.location}
  95. router={initialData.router}
  96. setSavedQuery={jest.fn()}
  97. loading={false}
  98. />,
  99. {context: initialData.routerContext, organization: initialData.organization}
  100. );
  101. expect(mockHomepage).toHaveBeenCalled();
  102. await screen.findByText('environment');
  103. // Only the environment field
  104. expect(screen.getAllByTestId('grid-head-cell').length).toEqual(1);
  105. screen.getByText('Previous Period');
  106. screen.getByText('event.type:error');
  107. });
  108. it('renders event view from URL params over homepage query', async () => {
  109. initialData = initializeOrg({
  110. ...initializeOrg(),
  111. organization,
  112. router: {
  113. location: {
  114. ...TestStubs.location(),
  115. query: {
  116. ...EventView.fromSavedQuery(DEFAULT_EVENT_VIEW).generateQueryStringObject(),
  117. field: ['project'],
  118. },
  119. },
  120. },
  121. });
  122. render(
  123. <Homepage
  124. organization={organization}
  125. location={initialData.router.location}
  126. router={initialData.router}
  127. setSavedQuery={jest.fn()}
  128. loading={false}
  129. />,
  130. {context: initialData.routerContext, organization: initialData.organization}
  131. );
  132. expect(mockHomepage).toHaveBeenCalled();
  133. await screen.findByText('project');
  134. // This is the field in the mocked response for the homepage
  135. expect(screen.queryByText('environment')).not.toBeInTheDocument();
  136. });
  137. it('applies URL changes with the homepage pathname', async () => {
  138. render(
  139. <Homepage
  140. organization={organization}
  141. location={initialData.router.location}
  142. router={initialData.router}
  143. setSavedQuery={jest.fn()}
  144. loading={false}
  145. />,
  146. {context: initialData.routerContext, organization: initialData.organization}
  147. );
  148. userEvent.click(screen.getByText('Columns'));
  149. await act(async () => {
  150. await mountGlobalModal();
  151. });
  152. userEvent.click(screen.getByTestId('label'));
  153. userEvent.click(screen.getByText('event.type'));
  154. userEvent.click(screen.getByText('Apply'));
  155. expect(browserHistory.push).toHaveBeenCalledWith(
  156. expect.objectContaining({
  157. pathname: '/organizations/org-slug/discover/homepage/',
  158. query: expect.objectContaining({
  159. field: ['event.type'],
  160. }),
  161. })
  162. );
  163. });
  164. it('does not show an editable header or author information', () => {
  165. render(
  166. <Homepage
  167. organization={organization}
  168. location={initialData.router.location}
  169. router={initialData.router}
  170. setSavedQuery={jest.fn()}
  171. loading={false}
  172. />,
  173. {context: initialData.routerContext, organization: initialData.organization}
  174. );
  175. // 'Discover' is the header for the homepage
  176. expect(screen.getByText('Discover')).toBeInTheDocument();
  177. expect(screen.queryByText(/Created by:/)).not.toBeInTheDocument();
  178. expect(screen.queryByText(/Last edited:/)).not.toBeInTheDocument();
  179. });
  180. it('shows the Remove Default button on initial load', async () => {
  181. MockApiClient.addMockResponse({
  182. url: '/organizations/org-slug/discover/homepage/',
  183. method: 'GET',
  184. statusCode: 200,
  185. body: {
  186. id: '2',
  187. name: 'homepage query',
  188. projects: [],
  189. version: 2,
  190. expired: false,
  191. dateCreated: '2021-04-08T17:53:25.195782Z',
  192. dateUpdated: '2021-04-09T12:13:18.567264Z',
  193. createdBy: {
  194. id: '2',
  195. },
  196. environment: [],
  197. fields: ['environment'],
  198. widths: ['-1'],
  199. range: '14d',
  200. orderby: '-environment',
  201. display: 'previous',
  202. query: 'event.type:error',
  203. topEvents: '5',
  204. },
  205. });
  206. render(
  207. <Homepage
  208. organization={organization}
  209. location={initialData.router.location}
  210. router={initialData.router}
  211. setSavedQuery={jest.fn()}
  212. loading={false}
  213. />,
  214. {context: initialData.routerContext, organization: initialData.organization}
  215. );
  216. expect(await screen.findByText('Remove Default')).toBeInTheDocument();
  217. expect(screen.queryByText('Set as Default')).not.toBeInTheDocument();
  218. });
  219. it('Disables the Set as Default button when no saved homepage', () => {
  220. initialData = initializeOrg({
  221. ...initializeOrg(),
  222. organization,
  223. router: {
  224. location: {
  225. ...TestStubs.location(),
  226. query: {
  227. ...EventView.fromSavedQuery(DEFAULT_EVENT_VIEW).generateQueryStringObject(),
  228. },
  229. },
  230. },
  231. });
  232. mockHomepage = MockApiClient.addMockResponse({
  233. url: '/organizations/org-slug/discover/homepage/',
  234. method: 'GET',
  235. statusCode: 200,
  236. });
  237. render(
  238. <Homepage
  239. organization={organization}
  240. location={initialData.router.location}
  241. router={initialData.router}
  242. setSavedQuery={jest.fn()}
  243. loading={false}
  244. />,
  245. {context: initialData.routerContext, organization: initialData.organization}
  246. );
  247. expect(mockHomepage).toHaveBeenCalled();
  248. expect(screen.getByRole('button', {name: /set as default/i})).toBeDisabled();
  249. });
  250. it('follows absolute date selection', async () => {
  251. initialData = initializeOrg({
  252. ...initializeOrg(),
  253. organization,
  254. router: {
  255. location: {
  256. ...TestStubs.location(),
  257. query: {
  258. ...EventView.fromSavedQuery(DEFAULT_EVENT_VIEW).generateQueryStringObject(),
  259. },
  260. },
  261. },
  262. });
  263. MockApiClient.addMockResponse({
  264. url: '/organizations/org-slug/discover/homepage/',
  265. method: 'GET',
  266. statusCode: 200,
  267. });
  268. render(
  269. <Homepage
  270. organization={organization}
  271. location={initialData.router.location}
  272. router={initialData.router}
  273. setSavedQuery={jest.fn()}
  274. loading={false}
  275. />,
  276. {context: initialData.routerContext, organization: initialData.organization}
  277. );
  278. userEvent.click(await screen.findByText('24H'));
  279. userEvent.click(await screen.findByText('Absolute date'));
  280. userEvent.click(screen.getByText('Apply'));
  281. expect(screen.queryByText('14D')).not.toBeInTheDocument();
  282. });
  283. // eslint-disable-next-line jest/no-disabled-tests
  284. it.skip('flaky: DD-1151: renders changes to the discover query when no homepage', async () => {
  285. initialData = initializeOrg({
  286. ...initializeOrg(),
  287. organization,
  288. router: {
  289. location: {
  290. ...TestStubs.location(),
  291. query: {
  292. ...EventView.fromSavedQuery(DEFAULT_EVENT_VIEW).generateQueryStringObject(),
  293. field: ['title'],
  294. },
  295. },
  296. },
  297. });
  298. MockApiClient.addMockResponse({
  299. url: '/organizations/org-slug/discover/homepage/',
  300. method: 'GET',
  301. statusCode: 200,
  302. body: '',
  303. });
  304. const {rerender} = render(
  305. <Homepage
  306. organization={organization}
  307. location={initialData.router.location}
  308. router={initialData.router}
  309. setSavedQuery={jest.fn()}
  310. loading={false}
  311. />,
  312. {context: initialData.routerContext, organization: initialData.organization}
  313. );
  314. userEvent.click(screen.getByText('Columns'));
  315. await act(async () => {
  316. await mountGlobalModal();
  317. });
  318. userEvent.click(screen.getByTestId('label'));
  319. userEvent.click(screen.getByText('event.type'));
  320. userEvent.click(screen.getByText('Apply'));
  321. const rerenderData = initializeOrg({
  322. ...initializeOrg(),
  323. organization,
  324. router: {
  325. location: {
  326. ...TestStubs.location(),
  327. query: {
  328. ...EventView.fromSavedQuery(DEFAULT_EVENT_VIEW).generateQueryStringObject(),
  329. field: ['event.type'],
  330. },
  331. },
  332. },
  333. });
  334. rerender(
  335. <Homepage
  336. organization={organization}
  337. location={rerenderData.router.location}
  338. router={rerenderData.router}
  339. setSavedQuery={jest.fn()}
  340. loading={false}
  341. />
  342. );
  343. await waitFor(() =>
  344. expect(screen.queryByText('Edit Columns')).not.toBeInTheDocument()
  345. );
  346. expect(screen.getByText('event.type')).toBeInTheDocument();
  347. });
  348. it('renders changes to the discover query when loaded with valid event view in url params', async () => {
  349. initialData = initializeOrg({
  350. ...initializeOrg(),
  351. organization,
  352. router: {
  353. location: {
  354. ...TestStubs.location(),
  355. query: {
  356. ...EventView.fromSavedQuery(DEFAULT_EVENT_VIEW).generateQueryStringObject(),
  357. field: ['title'],
  358. },
  359. },
  360. },
  361. });
  362. const {rerender} = render(
  363. <Homepage
  364. organization={organization}
  365. location={initialData.router.location}
  366. router={initialData.router}
  367. setSavedQuery={jest.fn()}
  368. loading={false}
  369. />,
  370. {context: initialData.routerContext, organization: initialData.organization}
  371. );
  372. userEvent.click(screen.getByText('Columns'));
  373. await act(async () => {
  374. await mountGlobalModal();
  375. });
  376. userEvent.click(screen.getByTestId('label'));
  377. userEvent.click(screen.getByText('event.type'));
  378. userEvent.click(screen.getByText('Apply'));
  379. const rerenderData = initializeOrg({
  380. ...initializeOrg(),
  381. organization,
  382. router: {
  383. location: {
  384. ...TestStubs.location(),
  385. query: {
  386. ...EventView.fromSavedQuery(DEFAULT_EVENT_VIEW).generateQueryStringObject(),
  387. field: ['event.type'],
  388. },
  389. },
  390. },
  391. });
  392. rerender(
  393. <Homepage
  394. organization={organization}
  395. location={rerenderData.router.location}
  396. router={rerenderData.router}
  397. setSavedQuery={jest.fn()}
  398. loading={false}
  399. />
  400. );
  401. await waitFor(() =>
  402. expect(screen.queryByText('Edit Columns')).not.toBeInTheDocument()
  403. );
  404. expect(screen.getByText('event.type')).toBeInTheDocument();
  405. });
  406. it('overrides homepage filters with pinned filters if they exist', () => {
  407. ProjectsStore.loadInitialData([TestStubs.Project({id: 2})]);
  408. jest.spyOn(pageFilterUtils, 'getPageFilterStorage').mockReturnValueOnce({
  409. pinnedFilters: new Set(['projects']),
  410. state: {
  411. project: [2],
  412. environment: [],
  413. start: null,
  414. end: null,
  415. period: '14d',
  416. utc: null,
  417. },
  418. });
  419. render(
  420. <Homepage
  421. organization={organization}
  422. location={initialData.router.location}
  423. router={initialData.router}
  424. setSavedQuery={jest.fn()}
  425. loading={false}
  426. />,
  427. {context: initialData.routerContext, organization: initialData.organization}
  428. );
  429. expect(screen.getByText('project-slug')).toBeInTheDocument();
  430. });
  431. it('allows users to set the All Events query as default', async () => {
  432. initialData = initializeOrg({
  433. ...initializeOrg(),
  434. organization,
  435. router: {
  436. location: {
  437. ...TestStubs.location(),
  438. query: {
  439. ...EventView.fromSavedQuery(DEFAULT_EVENT_VIEW).generateQueryStringObject(),
  440. },
  441. },
  442. },
  443. });
  444. mockHomepage = MockApiClient.addMockResponse({
  445. url: '/organizations/org-slug/discover/homepage/',
  446. method: 'GET',
  447. statusCode: 200,
  448. });
  449. render(
  450. <Homepage
  451. organization={organization}
  452. location={initialData.router.location}
  453. router={initialData.router}
  454. setSavedQuery={jest.fn()}
  455. loading={false}
  456. />,
  457. {context: initialData.routerContext, organization: initialData.organization}
  458. );
  459. await waitFor(() => expect(screen.getByTestId('set-as-default')).toBeEnabled());
  460. });
  461. });