// @ts-ignore import RCCascader from 'rc-cascader'; import React from 'react'; import PromQlLanguageProvider from '../language_provider'; import PromQueryField, { groupMetricsByPrefix, RECORDING_RULES_GROUP } from './PromQueryField'; import { DataSourceInstanceSettings, dateTime } from '@grafana/data'; import { PromOptions } from '../types'; import { fireEvent, render, screen } from '@testing-library/react'; describe('PromQueryField', () => { beforeAll(() => { // @ts-ignore window.getSelection = () => {}; }); it('renders metrics chooser regularly if lookups are not disabled in the datasource settings', () => { const datasource = ({ languageProvider: { start: () => Promise.resolve([]), syntax: () => {}, }, } as unknown) as DataSourceInstanceSettings; const queryField = render( {}} onChange={() => {}} history={[]} /> ); expect(queryField.getAllByRole('button')).toHaveLength(1); }); it('renders a disabled metrics chooser if lookups are disabled in datasource settings', () => { const datasource = ({ languageProvider: { start: () => Promise.resolve([]), syntax: () => {}, }, } as unknown) as DataSourceInstanceSettings; const queryField = render( {}} onChange={() => {}} history={[]} /> ); const bcButton = queryField.getByRole('button'); expect(bcButton).toBeDisabled(); }); it('refreshes metrics when the data source changes', async () => { const defaultProps = { query: { expr: '', refId: '' }, onRunQuery: () => {}, onChange: () => {}, history: [], }; const metrics = ['foo', 'bar']; const queryField = render( ); checkMetricsInCascader(await screen.findByRole('button'), metrics); const changedMetrics = ['baz', 'moo']; queryField.rerender( ); // If we check the cascader right away it should be in loading state let cascader = screen.getByRole('button'); expect(cascader.textContent).toContain('Loading'); checkMetricsInCascader(await screen.findByRole('button'), changedMetrics); }); it('does not refreshes metrics when after rounding to minute time range does not change', async () => { const defaultProps = { query: { expr: '', refId: '' }, onRunQuery: () => {}, onChange: () => {}, history: [], }; const metrics = ['foo', 'bar']; const changedMetrics = ['foo', 'baz']; const range = { from: dateTime('2020-10-28T00:00:00Z'), to: dateTime('2020-10-28T01:00:00Z'), }; const languageProvider = makeLanguageProvider({ metrics: [metrics, changedMetrics] }); const queryField = render( ); checkMetricsInCascader(await screen.findByRole('button'), metrics); const newRange = { from: dateTime('2020-10-28T00:00:01Z'), to: dateTime('2020-10-28T01:00:01Z'), }; queryField.rerender( ); let cascader = screen.getByRole('button'); // Should not show loading expect(cascader.textContent).toContain('Metrics'); checkMetricsInCascader(await screen.findByRole('button'), metrics); }); it('refreshes metrics when time range changes but dont show loading state', async () => { const defaultProps = { query: { expr: '', refId: '' }, onRunQuery: () => {}, onChange: () => {}, history: [], }; const metrics = ['foo', 'bar']; const changedMetrics = ['baz', 'moo']; const range = { from: dateTime('2020-10-28T00:00:00Z'), to: dateTime('2020-10-28T01:00:00Z'), }; const languageProvider = makeLanguageProvider({ metrics: [metrics, changedMetrics] }); const queryField = render( ); checkMetricsInCascader(await screen.findByRole('button'), metrics); const newRange = { from: dateTime('2020-10-28T01:00:00Z'), to: dateTime('2020-10-28T02:00:00Z'), }; queryField.rerender( ); let cascader = screen.getByRole('button'); // Should not show loading expect(cascader.textContent).toContain('Metrics'); checkMetricsInCascader(cascader, metrics); }); }); describe('groupMetricsByPrefix()', () => { it('returns an empty group for no metrics', () => { expect(groupMetricsByPrefix([])).toEqual([]); }); it('returns options grouped by prefix', () => { expect(groupMetricsByPrefix(['foo_metric'])).toMatchObject([ { value: 'foo', children: [ { value: 'foo_metric', }, ], }, ]); }); it('returns options grouped by prefix with metadata', () => { expect(groupMetricsByPrefix(['foo_metric'], { foo_metric: [{ type: 'TYPE', help: 'my help' }] })).toMatchObject([ { value: 'foo', children: [ { value: 'foo_metric', title: 'foo_metric\nTYPE\nmy help', }, ], }, ]); }); it('returns options without prefix as toplevel option', () => { expect(groupMetricsByPrefix(['metric'])).toMatchObject([ { value: 'metric', }, ]); }); it('returns recording rules grouped separately', () => { expect(groupMetricsByPrefix([':foo_metric:'])).toMatchObject([ { value: RECORDING_RULES_GROUP, children: [ { value: ':foo_metric:', }, ], }, ]); }); }); function makeLanguageProvider(options: { metrics: string[][] }) { const metricsStack = [...options.metrics]; return ({ histogramMetrics: [] as any, metrics: [], metricsMetadata: {}, lookupsDisabled: false, start() { this.metrics = metricsStack.shift(); return Promise.resolve([]); }, } as any) as PromQlLanguageProvider; } function checkMetricsInCascader(cascader: HTMLElement, metrics: string[]) { fireEvent.keyDown(cascader, { keyCode: 40 }); let listNodes = screen.getAllByRole('menuitem'); for (const node of listNodes) { expect(metrics).toContain(node.innerHTML); } }