queryBatcher.spec.tsx 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. import {Fragment} from 'react';
  2. import {initializeData as _initializeData} from 'sentry-test/performance/initializePerformanceData';
  3. import {render, screen} from 'sentry-test/reactTestingLibrary';
  4. import {GenericQueryBatcher} from 'sentry/utils/performance/contexts/genericQueryBatcher';
  5. import {MEPSettingProvider} from 'sentry/utils/performance/contexts/metricsEnhancedSetting';
  6. import {PerformanceDisplayProvider} from 'sentry/utils/performance/contexts/performanceDisplayContext';
  7. import {OrganizationContext} from 'sentry/views/organizationContext';
  8. import WidgetContainer from 'sentry/views/performance/landing/widgets/components/widgetContainer';
  9. import {PerformanceWidgetSetting} from 'sentry/views/performance/landing/widgets/widgetDefinitions';
  10. import {PROJECT_PERFORMANCE_TYPE} from 'sentry/views/performance/utils';
  11. const initializeData = () => {
  12. const data = _initializeData({
  13. query: {statsPeriod: '7d', environment: ['prod'], project: [-42]},
  14. });
  15. data.eventView.additionalConditions.addFilterValues('transaction.op', ['pageload']);
  16. return data;
  17. };
  18. const BASIC_QUERY_PARAMS = {
  19. interval: '1h',
  20. partial: '1',
  21. query: 'transaction.op:pageload',
  22. statsPeriod: '14d',
  23. };
  24. const WrappedComponent = ({data, ...rest}) => {
  25. return (
  26. <OrganizationContext.Provider value={data.organization}>
  27. <MEPSettingProvider>
  28. <PerformanceDisplayProvider
  29. value={{performanceType: PROJECT_PERFORMANCE_TYPE.ANY}}
  30. >
  31. <WidgetContainer
  32. allowedCharts={[
  33. PerformanceWidgetSetting.TPM_AREA,
  34. PerformanceWidgetSetting.FAILURE_RATE_AREA,
  35. PerformanceWidgetSetting.USER_MISERY_AREA,
  36. ]}
  37. rowChartSettings={[]}
  38. forceDefaultChartSetting
  39. {...data}
  40. {...rest}
  41. />
  42. </PerformanceDisplayProvider>
  43. </MEPSettingProvider>
  44. </OrganizationContext.Provider>
  45. );
  46. };
  47. describe('Performance > Widgets > Query Batching', function () {
  48. let eventStatsMock;
  49. beforeEach(function () {
  50. eventStatsMock = MockApiClient.addMockResponse({
  51. method: 'GET',
  52. url: `/organizations/org-slug/events-stats/`,
  53. body: {
  54. 'tpm()': {
  55. data: [
  56. [
  57. 1636822800,
  58. [
  59. {
  60. count: 30.0,
  61. },
  62. ],
  63. ],
  64. [
  65. 1636995600,
  66. [
  67. {
  68. count: 60.1,
  69. },
  70. ],
  71. ],
  72. ],
  73. order: 1,
  74. start: 1636822800,
  75. end: 1636995600,
  76. },
  77. 'user_misery()': {
  78. data: [
  79. [
  80. 1636822800,
  81. [
  82. {
  83. count: 0.02,
  84. },
  85. ],
  86. ],
  87. [
  88. 1636995600,
  89. [
  90. {
  91. count: 0.03,
  92. },
  93. ],
  94. ],
  95. ],
  96. order: 1,
  97. start: 1636822800,
  98. end: 1636995600,
  99. },
  100. 'failure_rate()': {
  101. data: [
  102. [
  103. 1636822800,
  104. [
  105. {
  106. count: 0.002,
  107. },
  108. ],
  109. ],
  110. [
  111. 1636995600,
  112. [
  113. {
  114. count: 0.001,
  115. },
  116. ],
  117. ],
  118. ],
  119. order: 2,
  120. start: 1636822800,
  121. end: 1636995600,
  122. },
  123. },
  124. });
  125. });
  126. it('EventsRequest based component fires query without provider', async function () {
  127. const data = initializeData();
  128. render(
  129. <WrappedComponent
  130. data={data}
  131. defaultChartSetting={PerformanceWidgetSetting.TPM_AREA}
  132. isMEPEnabled={false}
  133. />,
  134. {
  135. organization: data.organization,
  136. }
  137. );
  138. expect(await screen.findByTestId('performance-widget-title')).toBeInTheDocument();
  139. expect(eventStatsMock).toHaveBeenCalledTimes(1);
  140. expect(eventStatsMock).toHaveBeenNthCalledWith(
  141. 1,
  142. expect.anything(),
  143. expect.objectContaining({
  144. query: expect.objectContaining({
  145. ...BASIC_QUERY_PARAMS,
  146. yAxis: 'tpm()',
  147. }),
  148. })
  149. );
  150. });
  151. it('Multiple EventsRequest based components fire individual queries without provider', async function () {
  152. const data = initializeData();
  153. render(
  154. <Fragment>
  155. <WrappedComponent
  156. data={data}
  157. defaultChartSetting={PerformanceWidgetSetting.TPM_AREA}
  158. isMEPEnabled={false}
  159. />
  160. <WrappedComponent
  161. data={data}
  162. defaultChartSetting={PerformanceWidgetSetting.FAILURE_RATE_AREA}
  163. isMEPEnabled={false}
  164. />
  165. <WrappedComponent
  166. data={data}
  167. defaultChartSetting={PerformanceWidgetSetting.USER_MISERY_AREA}
  168. isMEPEnabled={false}
  169. />
  170. </Fragment>,
  171. {
  172. organization: data.organization,
  173. }
  174. );
  175. expect(await screen.findAllByTestId('performance-widget-title')).toHaveLength(3);
  176. // Three requests are made
  177. expect(eventStatsMock).toHaveBeenCalledTimes(3);
  178. expect(eventStatsMock).toHaveBeenNthCalledWith(
  179. 1,
  180. expect.anything(),
  181. expect.objectContaining({
  182. query: expect.objectContaining({
  183. ...BASIC_QUERY_PARAMS,
  184. yAxis: 'tpm()',
  185. }),
  186. })
  187. );
  188. });
  189. it('Multiple EventsRequest based component merge queries with provider', async function () {
  190. const data = initializeData();
  191. render(
  192. <GenericQueryBatcher>
  193. <WrappedComponent
  194. data={data}
  195. defaultChartSetting={PerformanceWidgetSetting.TPM_AREA}
  196. isMEPEnabled={false}
  197. />
  198. <WrappedComponent
  199. data={data}
  200. defaultChartSetting={PerformanceWidgetSetting.FAILURE_RATE_AREA}
  201. isMEPEnabled={false}
  202. />
  203. <WrappedComponent
  204. data={data}
  205. defaultChartSetting={PerformanceWidgetSetting.USER_MISERY_AREA}
  206. isMEPEnabled={false}
  207. />
  208. </GenericQueryBatcher>,
  209. {
  210. organization: data.organization,
  211. }
  212. );
  213. expect(await screen.findAllByTestId('performance-widget-title')).toHaveLength(3);
  214. expect(eventStatsMock).toHaveBeenNthCalledWith(
  215. 1,
  216. expect.anything(),
  217. expect.objectContaining({
  218. query: expect.objectContaining({
  219. ...BASIC_QUERY_PARAMS,
  220. yAxis: ['tpm()', 'failure_rate()', 'user_misery()'],
  221. }),
  222. })
  223. );
  224. expect(eventStatsMock).toHaveBeenCalledTimes(1);
  225. expect(await screen.findAllByTestId('widget-state-has-data')).toHaveLength(3);
  226. });
  227. it('Multiple EventsRequest based component merge queries with provider and add MEP', async function () {
  228. const data = initializeData();
  229. render(
  230. <GenericQueryBatcher>
  231. <WrappedComponent
  232. data={data}
  233. defaultChartSetting={PerformanceWidgetSetting.TPM_AREA}
  234. isMEPEnabled
  235. />
  236. <WrappedComponent
  237. data={data}
  238. defaultChartSetting={PerformanceWidgetSetting.FAILURE_RATE_AREA}
  239. isMEPEnabled
  240. />
  241. <WrappedComponent
  242. data={data}
  243. defaultChartSetting={PerformanceWidgetSetting.USER_MISERY_AREA}
  244. isMEPEnabled
  245. />
  246. </GenericQueryBatcher>,
  247. {
  248. organization: data.organization,
  249. }
  250. );
  251. expect(await screen.findAllByTestId('performance-widget-title')).toHaveLength(3);
  252. expect(eventStatsMock).toHaveBeenNthCalledWith(
  253. 1,
  254. expect.anything(),
  255. expect.objectContaining({
  256. query: expect.objectContaining({
  257. ...BASIC_QUERY_PARAMS,
  258. yAxis: ['tpm()', 'failure_rate()', 'user_misery()'],
  259. }),
  260. })
  261. );
  262. expect(eventStatsMock).toHaveBeenCalledTimes(1);
  263. expect(await screen.findAllByTestId('widget-state-has-data')).toHaveLength(3);
  264. });
  265. it('Errors work correctly', async function () {
  266. eventStatsMock = MockApiClient.addMockResponse({
  267. method: 'GET',
  268. url: `/organizations/org-slug/events-stats/`,
  269. statusCode: 404,
  270. body: {},
  271. });
  272. const data = initializeData();
  273. render(
  274. <GenericQueryBatcher>
  275. <WrappedComponent
  276. data={data}
  277. defaultChartSetting={PerformanceWidgetSetting.TPM_AREA}
  278. isMEPEnabled={false}
  279. />
  280. <WrappedComponent
  281. data={data}
  282. defaultChartSetting={PerformanceWidgetSetting.FAILURE_RATE_AREA}
  283. isMEPEnabled={false}
  284. />
  285. <WrappedComponent
  286. data={data}
  287. defaultChartSetting={PerformanceWidgetSetting.USER_MISERY_AREA}
  288. isMEPEnabled={false}
  289. />
  290. </GenericQueryBatcher>,
  291. {
  292. organization: data.organization,
  293. }
  294. );
  295. expect(await screen.findAllByTestId('performance-widget-title')).toHaveLength(3);
  296. expect(eventStatsMock).toHaveBeenNthCalledWith(
  297. 1,
  298. expect.anything(),
  299. expect.objectContaining({
  300. query: expect.objectContaining({
  301. ...BASIC_QUERY_PARAMS,
  302. yAxis: ['tpm()', 'failure_rate()', 'user_misery()'],
  303. }),
  304. })
  305. );
  306. expect(eventStatsMock).toHaveBeenCalledTimes(1);
  307. expect(await screen.findAllByTestId('widget-state-is-errored')).toHaveLength(3);
  308. });
  309. });