index.spec.tsx 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. import {browserHistory} from 'react-router';
  2. import {mountWithTheme} from 'sentry-test/enzyme';
  3. import {initializeData} from 'sentry-test/performance/initializePerformanceData';
  4. import {act} from 'sentry-test/reactTestingLibrary';
  5. import TeamStore from 'sentry/stores/teamStore';
  6. import EventView from 'sentry/utils/discover/eventView';
  7. import {OrganizationContext} from 'sentry/views/organizationContext';
  8. import {PerformanceLanding} from 'sentry/views/performance/landing';
  9. import {REACT_NATIVE_COLUMN_TITLES} from 'sentry/views/performance/landing/data';
  10. import * as utils from 'sentry/views/performance/landing/utils';
  11. import {LandingDisplayField} from 'sentry/views/performance/landing/utils';
  12. const WrappedComponent = ({data}) => {
  13. const eventView = EventView.fromLocation(data.router.location);
  14. return (
  15. <OrganizationContext.Provider value={data.organization}>
  16. <PerformanceLanding
  17. organization={data.organization}
  18. location={data.router.location}
  19. eventView={eventView}
  20. projects={data.projects}
  21. selection={eventView.getPageFilters()}
  22. shouldShowOnboarding={false}
  23. handleSearch={() => {}}
  24. handleTrendsClick={() => {}}
  25. setError={() => {}}
  26. />
  27. </OrganizationContext.Provider>
  28. );
  29. };
  30. describe('Performance > Landing > Index', function () {
  31. let eventStatsMock: any;
  32. let eventsV2Mock: any;
  33. let wrapper: any;
  34. act(() => void TeamStore.loadInitialData([], false, null));
  35. beforeEach(function () {
  36. MockApiClient.addMockResponse({
  37. url: '/organizations/org-slug/sdk-updates/',
  38. body: [],
  39. });
  40. MockApiClient.addMockResponse({
  41. url: '/prompts-activity/',
  42. body: {},
  43. });
  44. MockApiClient.addMockResponse({
  45. method: 'GET',
  46. url: `/organizations/org-slug/key-transactions-list/`,
  47. body: [],
  48. });
  49. MockApiClient.addMockResponse({
  50. method: 'GET',
  51. url: `/organizations/org-slug/legacy-key-transactions-count/`,
  52. body: [],
  53. });
  54. eventStatsMock = MockApiClient.addMockResponse({
  55. method: 'GET',
  56. url: `/organizations/org-slug/events-stats/`,
  57. body: [],
  58. });
  59. MockApiClient.addMockResponse({
  60. method: 'GET',
  61. url: `/organizations/org-slug/events-trends-stats/`,
  62. body: [],
  63. });
  64. eventsV2Mock = MockApiClient.addMockResponse({
  65. method: 'GET',
  66. url: `/organizations/org-slug/eventsv2/`,
  67. body: [],
  68. });
  69. });
  70. afterEach(function () {
  71. MockApiClient.clearMockResponses();
  72. if (wrapper) {
  73. wrapper.unmount();
  74. wrapper = undefined;
  75. }
  76. });
  77. it('renders basic UI elements', async function () {
  78. const data = initializeData();
  79. wrapper = mountWithTheme(<WrappedComponent data={data} />, data.routerContext);
  80. await tick();
  81. wrapper.update();
  82. expect(wrapper.find('div[data-test-id="performance-landing-v3"]').exists()).toBe(
  83. true
  84. );
  85. });
  86. it('renders frontend pageload view', async function () {
  87. const data = initializeData({
  88. query: {landingDisplay: LandingDisplayField.FRONTEND_PAGELOAD},
  89. });
  90. wrapper = mountWithTheme(<WrappedComponent data={data} />, data.routerContext);
  91. await tick();
  92. wrapper.update();
  93. expect(wrapper.find('div[data-test-id="frontend-pageload-view"]').exists()).toBe(
  94. true
  95. );
  96. expect(wrapper.find('Table')).toHaveLength(1);
  97. const titles = wrapper.find('div[data-test-id="performance-widget-title"]');
  98. expect(titles).toHaveLength(5);
  99. expect(titles.at(0).text()).toEqual('p75 LCP');
  100. expect(titles.at(1).text()).toEqual('LCP Distribution');
  101. expect(titles.at(2).text()).toEqual('FCP Distribution');
  102. expect(titles.at(3).text()).toEqual('Worst LCP Web Vitals');
  103. expect(titles.at(4).text()).toEqual('Worst FCP Web Vitals');
  104. });
  105. it('renders frontend other view', async function () {
  106. const data = initializeData({
  107. query: {landingDisplay: LandingDisplayField.FRONTEND_OTHER},
  108. });
  109. wrapper = mountWithTheme(<WrappedComponent data={data} />, data.routerContext);
  110. await tick();
  111. wrapper.update();
  112. expect(wrapper.find('Table').exists()).toBe(true);
  113. });
  114. it('renders backend view', async function () {
  115. const data = initializeData({
  116. query: {landingDisplay: LandingDisplayField.BACKEND},
  117. });
  118. wrapper = mountWithTheme(<WrappedComponent data={data} />, data.routerContext);
  119. await tick();
  120. wrapper.update();
  121. expect(wrapper.find('Table').exists()).toBe(true);
  122. });
  123. it('renders mobile view', async function () {
  124. const data = initializeData({
  125. query: {landingDisplay: LandingDisplayField.MOBILE},
  126. });
  127. wrapper = mountWithTheme(<WrappedComponent data={data} />, data.routerContext);
  128. await tick();
  129. wrapper.update();
  130. expect(wrapper.find('Table').exists()).toBe(true);
  131. });
  132. it('renders react-native table headers in mobile view', async function () {
  133. jest.spyOn(utils, 'checkIsReactNative').mockReturnValueOnce(true);
  134. const data = initializeData({
  135. query: {landingDisplay: LandingDisplayField.MOBILE},
  136. });
  137. wrapper = mountWithTheme(<WrappedComponent data={data} />, data.routerContext);
  138. await tick();
  139. wrapper.update();
  140. const table = wrapper.find('Table');
  141. expect(table.exists()).toBe(true);
  142. expect(table.props().columnTitles).toEqual(REACT_NATIVE_COLUMN_TITLES);
  143. });
  144. it('renders all transactions view', async function () {
  145. const data = initializeData({
  146. query: {landingDisplay: LandingDisplayField.ALL},
  147. });
  148. wrapper = mountWithTheme(<WrappedComponent data={data} />, data.routerContext);
  149. await tick();
  150. wrapper.update();
  151. expect(wrapper.find('Table').exists()).toBe(true);
  152. expect(eventStatsMock).toHaveBeenCalledTimes(1); // Only one request is made since the query batcher is working.
  153. expect(eventStatsMock).toHaveBeenNthCalledWith(
  154. 1,
  155. expect.anything(),
  156. expect.objectContaining({
  157. query: expect.objectContaining({
  158. environment: [],
  159. interval: '1h',
  160. partial: '1',
  161. project: [],
  162. query: '',
  163. referrer: 'api.organization-event-stats',
  164. statsPeriod: '28d',
  165. yAxis: ['user_misery()', 'tpm()', 'failure_rate()'],
  166. }),
  167. })
  168. );
  169. expect(eventsV2Mock).toHaveBeenCalledTimes(1);
  170. const titles = wrapper.find('div[data-test-id="performance-widget-title"]');
  171. expect(titles).toHaveLength(5);
  172. expect(titles.at(0).text()).toEqual('User Misery');
  173. expect(titles.at(1).text()).toEqual('Transactions Per Minute');
  174. expect(titles.at(2).text()).toEqual('Failure Rate');
  175. expect(titles.at(3).text()).toEqual('Most Related Issues');
  176. expect(titles.at(4).text()).toEqual('Most Improved');
  177. });
  178. it('Can switch between landing displays', async function () {
  179. const data = initializeData({
  180. query: {landingDisplay: LandingDisplayField.FRONTEND_PAGELOAD, abc: '123'},
  181. });
  182. wrapper = mountWithTheme(<WrappedComponent data={data} />, data.routerContext);
  183. await tick();
  184. wrapper.update();
  185. expect(wrapper.find('div[data-test-id="frontend-pageload-view"]').exists()).toBe(
  186. true
  187. );
  188. wrapper.find('a[data-test-id="landing-tab-all"]').simulate('click');
  189. await tick();
  190. wrapper.update();
  191. expect(browserHistory.push).toHaveBeenNthCalledWith(
  192. 1,
  193. expect.objectContaining({
  194. pathname: data.location.pathname,
  195. query: {query: '', abc: '123'},
  196. })
  197. );
  198. });
  199. it('Updating projects switches performance view', async function () {
  200. const data = initializeData({
  201. query: {landingDisplay: LandingDisplayField.FRONTEND_PAGELOAD},
  202. });
  203. wrapper = mountWithTheme(<WrappedComponent data={data} />, data.routerContext);
  204. await tick();
  205. wrapper.update();
  206. expect(wrapper.find('div[data-test-id="frontend-pageload-view"]').exists()).toBe(
  207. true
  208. );
  209. const updatedData = initializeData({
  210. projects: [TestStubs.Project({id: 123, platform: 'unknown'})],
  211. project: 123 as any,
  212. });
  213. wrapper.setProps({
  214. data: updatedData,
  215. } as any);
  216. await tick();
  217. wrapper.update();
  218. expect(wrapper.find('div[data-test-id="all-transactions-view"]').exists()).toBe(true);
  219. });
  220. it('View correctly defaults based on project without url param', async function () {
  221. const data = initializeData({
  222. projects: [TestStubs.Project({id: 99, platform: 'javascript-react'})],
  223. project: 99 as any,
  224. });
  225. wrapper = mountWithTheme(<WrappedComponent data={data} />, data.routerContext);
  226. await tick();
  227. wrapper.update();
  228. expect(wrapper.find('div[data-test-id="frontend-pageload-view"]').exists()).toBe(
  229. true
  230. );
  231. });
  232. });