metricScratchpad.spec.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import {initializeOrg} from 'sentry-test/initializeOrg';
  2. import {render, screen, waitForElementToBeRemoved} from 'sentry-test/reactTestingLibrary';
  3. import type {MetricsQueryApiResponse, PageFilters} from 'sentry/types';
  4. import {VirtualMetricsContextProvider} from 'sentry/utils/metrics/virtualMetricsContext';
  5. import importedUsePageFilters from 'sentry/utils/usePageFilters';
  6. import {MetricsContextProvider} from 'sentry/views/metrics/context';
  7. import {MetricScratchpad} from 'sentry/views/metrics/scratchpad';
  8. jest.mock('sentry/components/metrics/chart/chart');
  9. jest.mock('echarts/core', () => {
  10. return {
  11. connect: jest.fn(),
  12. use: jest.fn(),
  13. };
  14. });
  15. jest.mock('sentry/utils/usePageFilters');
  16. const usePageFilters = jest.mocked(importedUsePageFilters);
  17. const makeFilterProps = (
  18. filters: Partial<PageFilters>
  19. ): ReturnType<typeof importedUsePageFilters> => {
  20. return {
  21. isReady: true,
  22. shouldPersist: true,
  23. desyncedFilters: new Set(),
  24. pinnedFilters: new Set(),
  25. selection: {
  26. projects: [1],
  27. environments: ['prod'],
  28. datetime: {start: new Date(), end: new Date(), period: '14d', utc: true},
  29. ...filters,
  30. },
  31. };
  32. };
  33. function renderMockRequests({
  34. orgSlug,
  35. projectId,
  36. metricsQueryApiResponse,
  37. }: {
  38. orgSlug: string;
  39. projectId: string;
  40. metricsQueryApiResponse?: Partial<MetricsQueryApiResponse>;
  41. }) {
  42. MockApiClient.addMockResponse({
  43. url: `/organizations/${orgSlug}/metrics/meta/`,
  44. body: [
  45. {
  46. type: 'd',
  47. name: 'duration',
  48. unit: 'millisecond',
  49. mri: 'd:transactions/duration@millisecond',
  50. operations: ['avg', 'count'],
  51. projectIds: [projectId],
  52. blockingStatus: [],
  53. },
  54. {
  55. type: 'd',
  56. name: 'duration',
  57. unit: 'millisecond',
  58. mri: 'd:spans/duration@millisecond',
  59. operations: ['avg', 'count'],
  60. projectIds: [projectId],
  61. blockingStatus: [],
  62. },
  63. ],
  64. });
  65. MockApiClient.addMockResponse({
  66. url: `/organizations/${orgSlug}/metrics/extraction-rules/`,
  67. method: 'GET',
  68. body: [
  69. {
  70. aggregates: ['count'],
  71. conditions: [{id: 102, value: '', mris: ['c:custom/span_attribute_102@none']}],
  72. createdById: 3142223,
  73. dateAdded: '2024-07-29T12:04:23.196785Z',
  74. dateUpdated: '2024-07-29T12:04:23.197008Z',
  75. projectId,
  76. spanAttribute: 'A',
  77. tags: ['release', 'environment'],
  78. unit: 'none',
  79. },
  80. ],
  81. });
  82. MockApiClient.addMockResponse({
  83. url: `/organizations/${orgSlug}/releases/stats/`,
  84. body: [],
  85. });
  86. MockApiClient.addMockResponse({
  87. url: `/organizations/${orgSlug}/metrics/query/`,
  88. method: 'POST',
  89. body: metricsQueryApiResponse ?? {
  90. data: [
  91. [
  92. {
  93. by: {},
  94. totals: 1000.0,
  95. series: [null, 1000.0],
  96. },
  97. ],
  98. ],
  99. meta: [
  100. [
  101. {
  102. name: 'aggregate_value',
  103. type: 'Float64',
  104. },
  105. {
  106. group_bys: [],
  107. order: 'DESC',
  108. limit: 715,
  109. has_more: false,
  110. unit_family: null,
  111. unit: null,
  112. scaling_factor: null,
  113. },
  114. ],
  115. ],
  116. start: '2024-04-25T00:00:00Z',
  117. end: '2024-08-01T00:00:00Z',
  118. intervals: ['2024-07-18T00:00:00Z', '2024-07-25T00:00:00Z'],
  119. },
  120. });
  121. }
  122. describe('metric Scratchpad', function () {
  123. it('render summary table if data', async function () {
  124. const {organization, project} = initializeOrg();
  125. usePageFilters.mockImplementation(() =>
  126. makeFilterProps({projects: [Number(project.id)]})
  127. );
  128. renderMockRequests({orgSlug: organization.slug, projectId: project.id});
  129. render(
  130. <MetricsContextProvider>
  131. <VirtualMetricsContextProvider>
  132. <MetricScratchpad />
  133. </VirtualMetricsContextProvider>
  134. </MetricsContextProvider>
  135. );
  136. await waitForElementToBeRemoved(() => screen.queryAllByTestId('loading-indicator'));
  137. expect(screen.getByTestId('summary-table')).toBeInTheDocument();
  138. });
  139. it('do NOT render summary table if there is no data', async function () {
  140. const {organization, project} = initializeOrg();
  141. usePageFilters.mockImplementation(() =>
  142. makeFilterProps({projects: [Number(project.id)]})
  143. );
  144. renderMockRequests({
  145. orgSlug: organization.slug,
  146. projectId: project.id,
  147. metricsQueryApiResponse: {
  148. data: [],
  149. meta: [],
  150. },
  151. });
  152. render(
  153. <MetricsContextProvider>
  154. <VirtualMetricsContextProvider>
  155. <MetricScratchpad />
  156. </VirtualMetricsContextProvider>
  157. </MetricsContextProvider>
  158. );
  159. await waitForElementToBeRemoved(() => screen.queryAllByTestId('loading-indicator'));
  160. expect(screen.queryByTestId('summary-table')).not.toBeInTheDocument();
  161. });
  162. });