homepage.spec.tsx 14 KB

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