PromQueryBuilder.test.tsx 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. import { render, screen, getByText, waitFor } from '@testing-library/react';
  2. import userEvent from '@testing-library/user-event';
  3. import React from 'react';
  4. import { LoadingState, MutableDataFrame, PanelData, TimeRange } from '@grafana/data';
  5. import { PrometheusDatasource } from '../../datasource';
  6. import PromQlLanguageProvider from '../../language_provider';
  7. import { EmptyLanguageProviderMock } from '../../language_provider.mock';
  8. import { getLabelSelects } from '../testUtils';
  9. import { PromVisualQuery } from '../types';
  10. import { PromQueryBuilder } from './PromQueryBuilder';
  11. const defaultQuery: PromVisualQuery = {
  12. metric: 'random_metric',
  13. labels: [],
  14. operations: [],
  15. };
  16. const bugQuery: PromVisualQuery = {
  17. metric: 'random_metric',
  18. labels: [{ label: 'instance', op: '=', value: 'localhost:9090' }],
  19. operations: [
  20. {
  21. id: 'rate',
  22. params: ['auto'],
  23. },
  24. {
  25. id: '__sum_by',
  26. params: ['instance', 'job'],
  27. },
  28. ],
  29. binaryQueries: [
  30. {
  31. operator: '/',
  32. query: {
  33. metric: 'metric2',
  34. labels: [{ label: 'foo', op: '=', value: 'bar' }],
  35. operations: [
  36. {
  37. id: '__avg_by',
  38. params: ['app'],
  39. },
  40. ],
  41. },
  42. },
  43. ],
  44. };
  45. describe('PromQueryBuilder', () => {
  46. it('shows empty just with metric selected', async () => {
  47. setup();
  48. // Add label
  49. expect(screen.getByLabelText('Add')).toBeInTheDocument();
  50. expect(screen.getByTitle('Add operation')).toBeInTheDocument();
  51. });
  52. it('renders all the query sections', async () => {
  53. setup(bugQuery);
  54. expect(screen.getByText('random_metric')).toBeInTheDocument();
  55. expect(screen.getByText('localhost:9090')).toBeInTheDocument();
  56. expect(screen.getByText('Rate')).toBeInTheDocument();
  57. const sumBys = screen.getAllByTestId('operations.1.wrapper');
  58. expect(getByText(sumBys[0], 'instance')).toBeInTheDocument();
  59. expect(getByText(sumBys[0], 'job')).toBeInTheDocument();
  60. const avgBys = screen.getAllByTestId('operations.0.wrapper');
  61. expect(getByText(avgBys[1], 'app')).toBeInTheDocument();
  62. expect(screen.getByText('Operator')).toBeInTheDocument();
  63. expect(screen.getByText('Vector matches')).toBeInTheDocument();
  64. });
  65. it('tries to load metrics without labels', async () => {
  66. const { languageProvider, container } = setup();
  67. await openMetricSelect(container);
  68. await waitFor(() => expect(languageProvider.getLabelValues).toBeCalledWith('__name__'));
  69. });
  70. it('tries to load metrics with labels', async () => {
  71. const { languageProvider, container } = setup({
  72. ...defaultQuery,
  73. labels: [{ label: 'label_name', op: '=', value: 'label_value' }],
  74. });
  75. await openMetricSelect(container);
  76. await waitFor(() => expect(languageProvider.getSeries).toBeCalledWith('{label_name="label_value"}', true));
  77. });
  78. it('tries to load variables in metric field', async () => {
  79. const { datasource, container } = setup();
  80. datasource.getVariables = jest.fn().mockReturnValue([]);
  81. await openMetricSelect(container);
  82. await waitFor(() => expect(datasource.getVariables).toBeCalled());
  83. });
  84. it('tries to load labels when metric selected', async () => {
  85. const { languageProvider } = setup();
  86. await openLabelNameSelect();
  87. await waitFor(() => expect(languageProvider.fetchSeriesLabels).toBeCalledWith('{__name__="random_metric"}'));
  88. });
  89. it('tries to load variables in label field', async () => {
  90. const { datasource } = setup();
  91. datasource.getVariables = jest.fn().mockReturnValue([]);
  92. await openLabelNameSelect();
  93. await waitFor(() => expect(datasource.getVariables).toBeCalled());
  94. });
  95. it('tries to load labels when metric selected and other labels are already present', async () => {
  96. const { languageProvider } = setup({
  97. ...defaultQuery,
  98. labels: [
  99. { label: 'label_name', op: '=', value: 'label_value' },
  100. { label: 'foo', op: '=', value: 'bar' },
  101. ],
  102. });
  103. await openLabelNameSelect(1);
  104. await waitFor(() =>
  105. expect(languageProvider.fetchSeriesLabels).toBeCalledWith('{label_name="label_value", __name__="random_metric"}')
  106. );
  107. });
  108. it('tries to load labels when metric is not selected', async () => {
  109. const { languageProvider } = setup({
  110. ...defaultQuery,
  111. metric: '',
  112. });
  113. await openLabelNameSelect();
  114. await waitFor(() => expect(languageProvider.fetchLabels).toBeCalled());
  115. });
  116. it('shows hints for histogram metrics', async () => {
  117. const { container } = setup({
  118. metric: 'histogram_metric_bucket',
  119. labels: [],
  120. operations: [],
  121. });
  122. await openMetricSelect(container);
  123. await userEvent.click(screen.getByText('histogram_metric_bucket'));
  124. await waitFor(() => expect(screen.getByText('hint: add histogram_quantile()')).toBeInTheDocument());
  125. });
  126. it('shows hints for counter metrics', async () => {
  127. const { container } = setup({
  128. metric: 'histogram_metric_sum',
  129. labels: [],
  130. operations: [],
  131. });
  132. await openMetricSelect(container);
  133. await userEvent.click(screen.getByText('histogram_metric_sum'));
  134. await waitFor(() => expect(screen.getByText('hint: add rate()')).toBeInTheDocument());
  135. });
  136. it('shows hints for counter metrics', async () => {
  137. const { container } = setup({
  138. metric: 'histogram_metric_sum',
  139. labels: [],
  140. operations: [],
  141. });
  142. await openMetricSelect(container);
  143. await userEvent.click(screen.getByText('histogram_metric_sum'));
  144. await waitFor(() => expect(screen.getByText('hint: add rate()')).toBeInTheDocument());
  145. });
  146. it('shows multiple hints', async () => {
  147. const data: PanelData = {
  148. series: [],
  149. state: LoadingState.Done,
  150. timeRange: {} as TimeRange,
  151. };
  152. for (let i = 0; i < 25; i++) {
  153. data.series.push(new MutableDataFrame());
  154. }
  155. const { container } = setup(
  156. {
  157. metric: 'histogram_metric_sum',
  158. labels: [],
  159. operations: [],
  160. },
  161. data
  162. );
  163. await openMetricSelect(container);
  164. await userEvent.click(screen.getByText('histogram_metric_sum'));
  165. await waitFor(() => expect(screen.getAllByText(/hint:/)).toHaveLength(2));
  166. });
  167. });
  168. function setup(query: PromVisualQuery = defaultQuery, data?: PanelData) {
  169. const languageProvider = new EmptyLanguageProviderMock() as unknown as PromQlLanguageProvider;
  170. const datasource = new PrometheusDatasource(
  171. {
  172. url: '',
  173. jsonData: {},
  174. meta: {} as any,
  175. } as any,
  176. undefined,
  177. undefined,
  178. languageProvider
  179. );
  180. const props = {
  181. datasource,
  182. onRunQuery: () => {},
  183. onChange: () => {},
  184. data,
  185. };
  186. const { container } = render(<PromQueryBuilder {...props} query={query} />);
  187. return { languageProvider, datasource, container };
  188. }
  189. async function openMetricSelect(container: HTMLElement) {
  190. const select = container.querySelector('#prometheus-metric-select');
  191. if (select) {
  192. await userEvent.click(select);
  193. }
  194. }
  195. async function openLabelNameSelect(index = 0) {
  196. const { name } = getLabelSelects(index);
  197. await userEvent.click(name);
  198. }