changeExplorer.spec.tsx 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. import React from 'react';
  2. import moment from 'moment';
  3. import {initializeData} from 'sentry-test/performance/initializePerformanceData';
  4. import {act, render, screen, waitFor} from 'sentry-test/reactTestingLibrary';
  5. import ProjectsStore from 'sentry/stores/projectsStore';
  6. import {PerformanceChangeExplorer} from 'sentry/views/performance/trends/changeExplorer';
  7. import {
  8. COLUMNS,
  9. MetricsTable,
  10. renderBodyCell,
  11. } from 'sentry/views/performance/trends/changeExplorerUtils/metricsTable';
  12. import {
  13. NormalizedTrendsTransaction,
  14. TrendChangeType,
  15. TrendFunctionField,
  16. } from 'sentry/views/performance/trends/types';
  17. import {TRENDS_PARAMETERS} from 'sentry/views/performance/trends/utils';
  18. async function waitForMockCall(mock: jest.Mock) {
  19. await waitFor(() => {
  20. expect(mock).toHaveBeenCalled();
  21. });
  22. }
  23. const transaction: NormalizedTrendsTransaction = {
  24. aggregate_range_1: 78.2757131147541,
  25. aggregate_range_2: 110.50465131578949,
  26. breakpoint: 1687262400,
  27. project: 'sentry',
  28. transaction: 'sentry.tasks.store.save_event',
  29. trend_difference: 32.22893820103539,
  30. trend_percentage: 1.411736117354651,
  31. count: 3459,
  32. received_at: moment(1601251200000),
  33. };
  34. describe('Performance > Trends > Performance Change Explorer', function () {
  35. let eventsMockBefore;
  36. beforeEach(function () {
  37. eventsMockBefore = MockApiClient.addMockResponse({
  38. url: '/organizations/org-slug/events/',
  39. body: {
  40. data: [
  41. {
  42. 'p95()': 1010.9232499999998,
  43. 'p50()': 47.34580982348902,
  44. 'tps()': 3.7226926286168966,
  45. 'count()': 345,
  46. },
  47. ],
  48. meta: {
  49. fields: {
  50. 'p95()': 'duration',
  51. '950()': 'duration',
  52. 'tps()': 'number',
  53. 'count()': 'number',
  54. },
  55. units: {
  56. 'p95()': 'millisecond',
  57. 'p50()': 'millisecond',
  58. 'tps()': null,
  59. 'count()': null,
  60. },
  61. isMetricsData: true,
  62. tips: {},
  63. dataset: 'metrics',
  64. },
  65. },
  66. });
  67. });
  68. afterEach(function () {
  69. MockApiClient.clearMockResponses();
  70. act(() => ProjectsStore.reset());
  71. });
  72. it('renders basic UI elements', async function () {
  73. const data = initializeData();
  74. const statsData = {
  75. ['/organizations/:orgId/performance/']: {
  76. data: [],
  77. order: 0,
  78. },
  79. };
  80. render(
  81. <PerformanceChangeExplorer
  82. collapsed={false}
  83. transaction={transaction}
  84. onClose={() => {}}
  85. trendChangeType={TrendChangeType.REGRESSION}
  86. trendFunction={TrendFunctionField.P50}
  87. trendParameter={TRENDS_PARAMETERS[0]}
  88. trendView={data.eventView}
  89. statsData={statsData}
  90. isLoading={false}
  91. organization={data.organization}
  92. projects={data.projects}
  93. location={data.location}
  94. />,
  95. {
  96. context: data.routerContext,
  97. organization: data.organization,
  98. }
  99. );
  100. await waitForMockCall(eventsMockBefore);
  101. await waitFor(() => {
  102. expect(screen.getByTestId('pce-header')).toBeInTheDocument();
  103. expect(screen.getByTestId('pce-graph')).toBeInTheDocument();
  104. expect(screen.getByTestId('grid-editable')).toBeInTheDocument();
  105. expect(screen.getAllByTestId('pce-metrics-chart-row-metric')).toHaveLength(4);
  106. expect(screen.getAllByTestId('pce-metrics-chart-row-before')).toHaveLength(4);
  107. expect(screen.getAllByTestId('pce-metrics-chart-row-after')).toHaveLength(4);
  108. expect(screen.getAllByTestId('pce-metrics-chart-row-change')).toHaveLength(4);
  109. });
  110. });
  111. it('shows correct change notation for no change', async () => {
  112. const data = initializeData();
  113. render(
  114. <MetricsTable
  115. isLoading={false}
  116. location={data.location}
  117. trendFunction={TrendFunctionField.P50}
  118. transaction={transaction}
  119. trendView={data.eventView}
  120. organization={data.organization}
  121. />
  122. );
  123. await waitForMockCall(eventsMockBefore);
  124. await waitFor(() => {
  125. expect(screen.getAllByText('3.7 ps')).toHaveLength(2);
  126. expect(screen.getAllByTestId('pce-metrics-text-change')[0]).toHaveTextContent('-');
  127. });
  128. });
  129. it('shows correct change notation for positive change', async () => {
  130. const data = initializeData();
  131. render(
  132. <MetricsTable
  133. isLoading={false}
  134. location={data.location}
  135. trendFunction={TrendFunctionField.P50}
  136. transaction={transaction}
  137. trendView={data.eventView}
  138. organization={data.organization}
  139. />
  140. );
  141. await waitForMockCall(eventsMockBefore);
  142. await waitFor(() => {
  143. expect(screen.getAllByTestId('pce-metrics-text-before')[1]).toHaveTextContent(
  144. '78.3 ms'
  145. );
  146. expect(screen.getAllByTestId('pce-metrics-text-after')[1]).toHaveTextContent(
  147. '110.5 ms'
  148. );
  149. expect(screen.getAllByTestId('pce-metrics-text-change')[1]).toHaveTextContent(
  150. '+41.2%'
  151. );
  152. });
  153. });
  154. it('shows correct change notation for negative change', async () => {
  155. const data = initializeData();
  156. const negativeTransaction = {
  157. ...transaction,
  158. aggregate_range_1: 110.50465131578949,
  159. aggregate_range_2: 78.2757131147541,
  160. trend_percentage: 0.588263882645349,
  161. };
  162. render(
  163. <MetricsTable
  164. isLoading={false}
  165. location={data.location}
  166. trendFunction={TrendFunctionField.P50}
  167. transaction={negativeTransaction}
  168. trendView={data.eventView}
  169. organization={data.organization}
  170. />
  171. );
  172. await waitForMockCall(eventsMockBefore);
  173. await waitFor(() => {
  174. expect(screen.getAllByTestId('pce-metrics-text-after')[1]).toHaveTextContent(
  175. '78.3 ms'
  176. );
  177. expect(screen.getAllByTestId('pce-metrics-text-before')[1]).toHaveTextContent(
  178. '110.5 ms'
  179. );
  180. expect(screen.getAllByTestId('pce-metrics-text-change')[1]).toHaveTextContent(
  181. '-41.2%'
  182. );
  183. });
  184. });
  185. it('shows correct change notation for no results', async () => {
  186. const data = initializeData();
  187. const nullEventsMock = MockApiClient.addMockResponse({
  188. url: '/organizations/org-slug/events/',
  189. body: {
  190. data: [
  191. {
  192. 'p95()': 1010.9232499999998,
  193. 'p50()': 47.34580982348902,
  194. 'count()': 345,
  195. },
  196. ],
  197. meta: {
  198. fields: {
  199. 'p95()': 'duration',
  200. '950()': 'duration',
  201. 'count()': 'number',
  202. },
  203. units: {
  204. 'p95()': 'millisecond',
  205. 'p50()': 'millisecond',
  206. 'count()': null,
  207. },
  208. isMetricsData: true,
  209. tips: {},
  210. dataset: 'metrics',
  211. },
  212. },
  213. });
  214. render(
  215. <MetricsTable
  216. isLoading={false}
  217. location={data.location}
  218. trendFunction={TrendFunctionField.P50}
  219. transaction={transaction}
  220. trendView={data.eventView}
  221. organization={data.organization}
  222. />
  223. );
  224. await waitForMockCall(nullEventsMock);
  225. await waitFor(() => {
  226. expect(screen.getAllByTestId('pce-metrics-text-after')[0]).toHaveTextContent('-');
  227. expect(screen.getAllByTestId('pce-metrics-text-before')[0]).toHaveTextContent('-');
  228. expect(screen.getAllByTestId('pce-metrics-text-change')[0]).toHaveTextContent('-');
  229. });
  230. });
  231. it('returns correct null formatting for change column', () => {
  232. render(
  233. <React.Fragment>
  234. {renderBodyCell(COLUMNS.change, {
  235. metric: null,
  236. before: null,
  237. after: null,
  238. change: '0%',
  239. })}
  240. {renderBodyCell(COLUMNS.change, {
  241. metric: null,
  242. before: null,
  243. after: null,
  244. change: '+NaN%',
  245. })}
  246. {renderBodyCell(COLUMNS.change, {
  247. metric: null,
  248. before: null,
  249. after: null,
  250. change: '-',
  251. })}
  252. </React.Fragment>
  253. );
  254. expect(screen.getAllByTestId('pce-metrics-text-change')[0]).toHaveTextContent('-');
  255. expect(screen.getAllByTestId('pce-metrics-text-change')[1]).toHaveTextContent('-');
  256. expect(screen.getAllByTestId('pce-metrics-text-change')[2]).toHaveTextContent('-');
  257. });
  258. it('returns correct positive formatting for change column', () => {
  259. render(
  260. renderBodyCell(COLUMNS.change, {
  261. metric: null,
  262. before: null,
  263. after: null,
  264. change: '40.3%',
  265. })
  266. );
  267. expect(screen.getByText('+40.3%')).toBeInTheDocument();
  268. });
  269. });