123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781 |
- import React from 'react';
- import { expect } from 'test/lib/common';
- import { DataSourceRef, getDefaultTimeRange, LoadingState } from '@grafana/data';
- import { setDataSourceSrv } from '@grafana/runtime';
- import { reduxTester } from '../../../../test/core/redux/reduxTester';
- import { silenceConsoleOutput } from '../../../../test/core/utils/silenceConsoleOutput';
- import { notifyApp } from '../../../core/reducers/appNotification';
- import { getTimeSrv, setTimeSrv, TimeSrv } from '../../dashboard/services/TimeSrv';
- import { variableAdapters } from '../adapters';
- import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE } from '../constants';
- import { LegacyVariableQueryEditor } from '../editor/LegacyVariableQueryEditor';
- import {
- addVariableEditorError,
- changeVariableEditorExtended,
- initialVariableEditorState,
- removeVariableEditorError,
- setIdInEditor,
- } from '../editor/reducer';
- import { updateOptions } from '../state/actions';
- import { getPreloadedState, getRootReducer, RootReducerType } from '../state/helpers';
- import { toKeyedAction } from '../state/keyedVariablesReducer';
- import {
- addVariable,
- changeVariableProp,
- setCurrentVariableValue,
- variableStateCompleted,
- variableStateFailed,
- variableStateFetching,
- } from '../state/sharedReducer';
- import { variablesInitTransaction } from '../state/transactionReducer';
- import { QueryVariableModel, VariableHide, VariableQueryEditorProps, VariableRefresh, VariableSort } from '../types';
- import { toKeyedVariableIdentifier, toVariablePayload } from '../utils';
- import { setVariableQueryRunner, VariableQueryRunner } from './VariableQueryRunner';
- import {
- changeQueryVariableDataSource,
- changeQueryVariableQuery,
- flattenQuery,
- hasSelfReferencingQuery,
- initQueryVariableEditor,
- updateQueryVariableOptions,
- } from './actions';
- import { createQueryVariableAdapter } from './adapter';
- import { updateVariableOptions } from './reducer';
- const mocks: Record<string, any> = {
- datasource: {
- metricFindQuery: jest.fn().mockResolvedValue([]),
- },
- dataSourceSrv: {
- get: (ref: DataSourceRef) => Promise.resolve(mocks[ref.uid!]),
- getList: jest.fn().mockReturnValue([]),
- },
- pluginLoader: {
- importDataSourcePlugin: jest.fn().mockResolvedValue({ components: {} }),
- },
- VariableQueryEditor(props: VariableQueryEditorProps) {
- return <div>this is a variable query editor</div>;
- },
- };
- setDataSourceSrv(mocks.dataSourceSrv as any);
- jest.mock('../../plugins/plugin_loader', () => ({
- importDataSourcePlugin: () => mocks.pluginLoader.importDataSourcePlugin(),
- }));
- jest.mock('../../templating/template_srv', () => ({
- replace: jest.fn().mockReturnValue(''),
- }));
- describe('query actions', () => {
- let originalTimeSrv: TimeSrv;
- beforeEach(() => {
- originalTimeSrv = getTimeSrv();
- setTimeSrv({
- timeRange: jest.fn().mockReturnValue(getDefaultTimeRange()),
- } as unknown as TimeSrv);
- setVariableQueryRunner(new VariableQueryRunner());
- });
- afterEach(() => {
- setTimeSrv(originalTimeSrv);
- });
- variableAdapters.setInit(() => [createQueryVariableAdapter()]);
- describe('when updateQueryVariableOptions is dispatched but there is no ongoing transaction', () => {
- it('then correct actions are dispatched', async () => {
- const variable = createVariable({ includeAll: false });
- const optionsMetrics = [createMetric('A'), createMetric('B')];
- mockDatasourceMetrics(variable, optionsMetrics);
- const tester = await reduxTester<RootReducerType>()
- .givenRootReducer(getRootReducer())
- .whenActionIsDispatched(
- toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
- )
- .whenAsyncActionIsDispatched(updateQueryVariableOptions(toKeyedVariableIdentifier(variable)), true);
- tester.thenNoActionsWhereDispatched();
- });
- });
- describe('when updateQueryVariableOptions is dispatched for variable without both tags and includeAll', () => {
- it('then correct actions are dispatched', async () => {
- const variable = createVariable({ includeAll: false });
- const optionsMetrics = [createMetric('A'), createMetric('B')];
- mockDatasourceMetrics(variable, optionsMetrics);
- const tester = await reduxTester<RootReducerType>()
- .givenRootReducer(getRootReducer())
- .whenActionIsDispatched(
- toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
- )
- .whenActionIsDispatched(toKeyedAction('key', variablesInitTransaction({ uid: 'key' })))
- .whenAsyncActionIsDispatched(updateQueryVariableOptions(toKeyedVariableIdentifier(variable)), true);
- const option = createOption('A');
- const update = { results: optionsMetrics, templatedRegex: '' };
- tester.thenDispatchedActionsShouldEqual(
- toKeyedAction('key', updateVariableOptions(toVariablePayload(variable, update))),
- toKeyedAction('key', setCurrentVariableValue(toVariablePayload(variable, { option })))
- );
- });
- });
- describe('when updateQueryVariableOptions is dispatched for variable with includeAll but without tags', () => {
- it('then correct actions are dispatched', async () => {
- const variable = createVariable({ includeAll: true });
- const optionsMetrics = [createMetric('A'), createMetric('B')];
- mockDatasourceMetrics(variable, optionsMetrics);
- const tester = await reduxTester<RootReducerType>()
- .givenRootReducer(getRootReducer())
- .whenActionIsDispatched(
- toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
- )
- .whenActionIsDispatched(toKeyedAction('key', variablesInitTransaction({ uid: 'key' })))
- .whenAsyncActionIsDispatched(updateQueryVariableOptions(toKeyedVariableIdentifier(variable)), true);
- const option = createOption(ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE);
- const update = { results: optionsMetrics, templatedRegex: '' };
- tester.thenDispatchedActionsShouldEqual(
- toKeyedAction('key', updateVariableOptions(toVariablePayload(variable, update))),
- toKeyedAction('key', setCurrentVariableValue(toVariablePayload(variable, { option })))
- );
- });
- });
- describe('when updateQueryVariableOptions is dispatched for variable open in editor', () => {
- it('then correct actions are dispatched', async () => {
- const variable = createVariable({ includeAll: true });
- const optionsMetrics = [createMetric('A'), createMetric('B')];
- mockDatasourceMetrics(variable, optionsMetrics);
- const tester = await reduxTester<RootReducerType>()
- .givenRootReducer(getRootReducer())
- .whenActionIsDispatched(
- toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
- )
- .whenActionIsDispatched(toKeyedAction('key', variablesInitTransaction({ uid: 'key' })))
- .whenActionIsDispatched(toKeyedAction('key', setIdInEditor({ id: variable.id })))
- .whenAsyncActionIsDispatched(updateQueryVariableOptions(toKeyedVariableIdentifier(variable)), true);
- const option = createOption(ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE);
- const update = { results: optionsMetrics, templatedRegex: '' };
- tester.thenDispatchedActionsShouldEqual(
- toKeyedAction('key', removeVariableEditorError({ errorProp: 'update' })),
- toKeyedAction('key', updateVariableOptions(toVariablePayload(variable, update))),
- toKeyedAction('key', setCurrentVariableValue(toVariablePayload(variable, { option })))
- );
- });
- });
- describe('when updateQueryVariableOptions is dispatched for variable with searchFilter', () => {
- it('then correct actions are dispatched', async () => {
- const variable = createVariable({ includeAll: true });
- const optionsMetrics = [createMetric('A'), createMetric('B')];
- mockDatasourceMetrics(variable, optionsMetrics);
- const tester = await reduxTester<RootReducerType>()
- .givenRootReducer(getRootReducer())
- .whenActionIsDispatched(
- toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
- )
- .whenActionIsDispatched(toKeyedAction('key', variablesInitTransaction({ uid: 'key' })))
- .whenActionIsDispatched(toKeyedAction('key', setIdInEditor({ id: variable.id })))
- .whenAsyncActionIsDispatched(updateQueryVariableOptions(toKeyedVariableIdentifier(variable), 'search'), true);
- const update = { results: optionsMetrics, templatedRegex: '' };
- tester.thenDispatchedActionsShouldEqual(
- toKeyedAction('key', removeVariableEditorError({ errorProp: 'update' })),
- toKeyedAction('key', updateVariableOptions(toVariablePayload(variable, update)))
- );
- });
- });
- describe('when updateQueryVariableOptions is dispatched and fails for variable open in editor', () => {
- silenceConsoleOutput();
- it('then correct actions are dispatched', async () => {
- const variable = createVariable({ includeAll: true });
- const error = { message: 'failed to fetch metrics' };
- mocks[variable.datasource!.uid!].metricFindQuery = jest.fn(() => Promise.reject(error));
- const tester = await reduxTester<RootReducerType>()
- .givenRootReducer(getRootReducer())
- .whenActionIsDispatched(
- toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
- )
- .whenActionIsDispatched(toKeyedAction('key', variablesInitTransaction({ uid: 'key' })))
- .whenActionIsDispatched(toKeyedAction('key', setIdInEditor({ id: variable.id })))
- .whenAsyncActionIsDispatched(updateOptions(toKeyedVariableIdentifier(variable)), true);
- tester.thenDispatchedActionsPredicateShouldEqual((dispatchedActions) => {
- const expectedNumberOfActions = 5;
- expect(dispatchedActions[0]).toEqual(toKeyedAction('key', variableStateFetching(toVariablePayload(variable))));
- expect(dispatchedActions[1]).toEqual(toKeyedAction('key', removeVariableEditorError({ errorProp: 'update' })));
- expect(dispatchedActions[2]).toEqual(
- toKeyedAction('key', addVariableEditorError({ errorProp: 'update', errorText: error.message }))
- );
- expect(dispatchedActions[3]).toEqual(
- toKeyedAction(
- 'key',
- variableStateFailed(toVariablePayload(variable, { error: { message: 'failed to fetch metrics' } }))
- )
- );
- expect(dispatchedActions[4].type).toEqual(notifyApp.type);
- expect(dispatchedActions[4].payload.title).toEqual('Templating [0]');
- expect(dispatchedActions[4].payload.text).toEqual('Error updating options: failed to fetch metrics');
- expect(dispatchedActions[4].payload.severity).toEqual('error');
- return dispatchedActions.length === expectedNumberOfActions;
- });
- });
- });
- describe('when initQueryVariableEditor is dispatched', () => {
- it('then correct actions are dispatched', async () => {
- const variable = createVariable({ includeAll: true });
- const testMetricSource = { name: 'test', value: 'test', meta: {} };
- const editor = mocks.VariableQueryEditor;
- mocks.dataSourceSrv.getList = jest.fn().mockReturnValue([testMetricSource]);
- mocks.pluginLoader.importDataSourcePlugin = jest.fn().mockResolvedValue({
- components: { VariableQueryEditor: editor },
- });
- const tester = await reduxTester<RootReducerType>()
- .givenRootReducer(getRootReducer())
- .whenActionIsDispatched(
- toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
- )
- .whenActionIsDispatched(toKeyedAction('key', variablesInitTransaction({ uid: 'key' })))
- .whenAsyncActionIsDispatched(initQueryVariableEditor(toKeyedVariableIdentifier(variable)), true);
- tester.thenDispatchedActionsShouldEqual(
- toKeyedAction(
- 'key',
- changeVariableEditorExtended({ dataSource: mocks.datasource, VariableQueryEditor: editor })
- )
- );
- });
- });
- describe('when initQueryVariableEditor is dispatched and metricsource without value is available', () => {
- it('then correct actions are dispatched', async () => {
- const variable = createVariable({ includeAll: true });
- const testMetricSource = { name: 'test', value: null as unknown as string, meta: {} };
- const editor = mocks.VariableQueryEditor;
- mocks.dataSourceSrv.getList = jest.fn().mockReturnValue([testMetricSource]);
- mocks.pluginLoader.importDataSourcePlugin = jest.fn().mockResolvedValue({
- components: { VariableQueryEditor: editor },
- });
- const tester = await reduxTester<RootReducerType>()
- .givenRootReducer(getRootReducer())
- .whenActionIsDispatched(
- toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
- )
- .whenActionIsDispatched(toKeyedAction('key', variablesInitTransaction({ uid: 'key' })))
- .whenAsyncActionIsDispatched(initQueryVariableEditor(toKeyedVariableIdentifier(variable)), true);
- tester.thenDispatchedActionsShouldEqual(
- toKeyedAction(
- 'key',
- changeVariableEditorExtended({ dataSource: mocks.datasource, VariableQueryEditor: editor })
- )
- );
- });
- });
- describe('when initQueryVariableEditor is dispatched and no metric sources was found', () => {
- it('then correct actions are dispatched', async () => {
- const variable = createVariable({ includeAll: true });
- const editor = mocks.VariableQueryEditor;
- mocks.dataSourceSrv.getList = jest.fn().mockReturnValue([]);
- mocks.pluginLoader.importDataSourcePlugin = jest.fn().mockResolvedValue({
- components: { VariableQueryEditor: editor },
- });
- const tester = await reduxTester<RootReducerType>()
- .givenRootReducer(getRootReducer())
- .whenActionIsDispatched(
- toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
- )
- .whenActionIsDispatched(toKeyedAction('key', variablesInitTransaction({ uid: 'key' })))
- .whenAsyncActionIsDispatched(initQueryVariableEditor(toKeyedVariableIdentifier(variable)), true);
- tester.thenDispatchedActionsShouldEqual(
- toKeyedAction(
- 'key',
- changeVariableEditorExtended({ dataSource: mocks.datasource, VariableQueryEditor: editor })
- )
- );
- });
- });
- describe('when changeQueryVariableDataSource is dispatched', () => {
- it('then correct actions are dispatched', async () => {
- const variable = createVariable({ datasource: { uid: 'other' } });
- const editor = mocks.VariableQueryEditor;
- mocks.pluginLoader.importDataSourcePlugin = jest.fn().mockResolvedValue({
- components: { VariableQueryEditor: editor },
- });
- const tester = await reduxTester<RootReducerType>()
- .givenRootReducer(getRootReducer())
- .whenActionIsDispatched(
- toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
- )
- .whenActionIsDispatched(toKeyedAction('key', variablesInitTransaction({ uid: 'key' })))
- .whenAsyncActionIsDispatched(
- changeQueryVariableDataSource(toKeyedVariableIdentifier(variable), { uid: 'datasource' }),
- true
- );
- tester.thenDispatchedActionsShouldEqual(
- toKeyedAction(
- 'key',
- changeVariableEditorExtended({ dataSource: mocks.datasource, VariableQueryEditor: editor })
- )
- );
- });
- describe('and data source type changed', () => {
- it('then correct actions are dispatched', async () => {
- const variable = createVariable({ datasource: { uid: 'other' } });
- const editor = mocks.VariableQueryEditor;
- const previousDataSource: any = { type: 'previous' };
- const templatingState = {
- editor: {
- ...initialVariableEditorState,
- extended: { dataSource: previousDataSource, VariableQueryEditor: editor },
- },
- };
- const preloadedState = getPreloadedState('key', templatingState);
- mocks.pluginLoader.importDataSourcePlugin = jest.fn().mockResolvedValue({
- components: { VariableQueryEditor: editor },
- });
- const tester = await reduxTester<RootReducerType>({ preloadedState })
- .givenRootReducer(getRootReducer())
- .whenActionIsDispatched(
- toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
- )
- .whenActionIsDispatched(toKeyedAction('key', variablesInitTransaction({ uid: 'key' })))
- .whenAsyncActionIsDispatched(
- changeQueryVariableDataSource(toKeyedVariableIdentifier(variable), { uid: 'datasource' }),
- true
- );
- tester.thenDispatchedActionsShouldEqual(
- toKeyedAction('key', changeVariableProp(toVariablePayload(variable, { propName: 'query', propValue: '' }))),
- toKeyedAction(
- 'key',
- changeVariableEditorExtended({ dataSource: mocks.datasource, VariableQueryEditor: editor })
- )
- );
- });
- });
- });
- describe('when changeQueryVariableDataSource is dispatched and editor is not configured', () => {
- it('then correct actions are dispatched', async () => {
- const variable = createVariable({ datasource: { uid: 'other' } });
- const editor = LegacyVariableQueryEditor;
- mocks.pluginLoader.importDataSourcePlugin = jest.fn().mockResolvedValue({
- components: {},
- });
- const tester = await reduxTester<RootReducerType>()
- .givenRootReducer(getRootReducer())
- .whenActionIsDispatched(
- toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
- )
- .whenActionIsDispatched(toKeyedAction('key', variablesInitTransaction({ uid: 'key' })))
- .whenAsyncActionIsDispatched(
- changeQueryVariableDataSource(toKeyedVariableIdentifier(variable), { uid: 'datasource' }),
- true
- );
- tester.thenDispatchedActionsShouldEqual(
- toKeyedAction(
- 'key',
- changeVariableEditorExtended({ dataSource: mocks.datasource, VariableQueryEditor: editor })
- )
- );
- });
- });
- describe('when changeQueryVariableQuery is dispatched', () => {
- it('then correct actions are dispatched', async () => {
- const optionsMetrics = [createMetric('A'), createMetric('B')];
- const variable = createVariable({ datasource: { uid: 'datasource' }, includeAll: true });
- const query = '$datasource';
- const definition = 'depends on datasource variable';
- mockDatasourceMetrics({ ...variable, query }, optionsMetrics);
- const tester = await reduxTester<RootReducerType>()
- .givenRootReducer(getRootReducer())
- .whenActionIsDispatched(
- toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
- )
- .whenActionIsDispatched(toKeyedAction('key', variablesInitTransaction({ uid: 'key' })))
- .whenAsyncActionIsDispatched(
- changeQueryVariableQuery(toKeyedVariableIdentifier(variable), query, definition),
- true
- );
- const option = createOption(ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE);
- const update = { results: optionsMetrics, templatedRegex: '' };
- tester.thenDispatchedActionsShouldEqual(
- toKeyedAction('key', removeVariableEditorError({ errorProp: 'query' })),
- toKeyedAction('key', changeVariableProp(toVariablePayload(variable, { propName: 'query', propValue: query }))),
- toKeyedAction(
- 'key',
- changeVariableProp(toVariablePayload(variable, { propName: 'definition', propValue: definition }))
- ),
- toKeyedAction('key', variableStateFetching(toVariablePayload(variable))),
- toKeyedAction('key', updateVariableOptions(toVariablePayload(variable, update))),
- toKeyedAction('key', setCurrentVariableValue(toVariablePayload(variable, { option }))),
- toKeyedAction('key', variableStateCompleted(toVariablePayload(variable)))
- );
- });
- });
- describe('when changeQueryVariableQuery is dispatched for variable without tags', () => {
- it('then correct actions are dispatched', async () => {
- const optionsMetrics = [createMetric('A'), createMetric('B')];
- const variable = createVariable({ datasource: { uid: 'datasource' }, includeAll: true });
- const query = '$datasource';
- const definition = 'depends on datasource variable';
- mockDatasourceMetrics({ ...variable, query }, optionsMetrics);
- const tester = await reduxTester<RootReducerType>()
- .givenRootReducer(getRootReducer())
- .whenActionIsDispatched(
- toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
- )
- .whenActionIsDispatched(toKeyedAction('key', variablesInitTransaction({ uid: 'key' })))
- .whenAsyncActionIsDispatched(
- changeQueryVariableQuery(toKeyedVariableIdentifier(variable), query, definition),
- true
- );
- const option = createOption(ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE);
- const update = { results: optionsMetrics, templatedRegex: '' };
- tester.thenDispatchedActionsShouldEqual(
- toKeyedAction('key', removeVariableEditorError({ errorProp: 'query' })),
- toKeyedAction('key', changeVariableProp(toVariablePayload(variable, { propName: 'query', propValue: query }))),
- toKeyedAction(
- 'key',
- changeVariableProp(toVariablePayload(variable, { propName: 'definition', propValue: definition }))
- ),
- toKeyedAction('key', variableStateFetching(toVariablePayload(variable))),
- toKeyedAction('key', updateVariableOptions(toVariablePayload(variable, update))),
- toKeyedAction('key', setCurrentVariableValue(toVariablePayload(variable, { option }))),
- toKeyedAction('key', variableStateCompleted(toVariablePayload(variable)))
- );
- });
- });
- describe('when changeQueryVariableQuery is dispatched for variable without tags and all', () => {
- it('then correct actions are dispatched', async () => {
- const optionsMetrics = [createMetric('A'), createMetric('B')];
- const variable = createVariable({ datasource: { uid: 'datasource' }, includeAll: false });
- const query = '$datasource';
- const definition = 'depends on datasource variable';
- mockDatasourceMetrics({ ...variable, query }, optionsMetrics);
- const tester = await reduxTester<RootReducerType>()
- .givenRootReducer(getRootReducer())
- .whenActionIsDispatched(
- toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
- )
- .whenActionIsDispatched(toKeyedAction('key', variablesInitTransaction({ uid: 'key' })))
- .whenAsyncActionIsDispatched(
- changeQueryVariableQuery(toKeyedVariableIdentifier(variable), query, definition),
- true
- );
- const option = createOption('A');
- const update = { results: optionsMetrics, templatedRegex: '' };
- tester.thenDispatchedActionsShouldEqual(
- toKeyedAction('key', removeVariableEditorError({ errorProp: 'query' })),
- toKeyedAction('key', changeVariableProp(toVariablePayload(variable, { propName: 'query', propValue: query }))),
- toKeyedAction(
- 'key',
- changeVariableProp(toVariablePayload(variable, { propName: 'definition', propValue: definition }))
- ),
- toKeyedAction('key', variableStateFetching(toVariablePayload(variable))),
- toKeyedAction('key', updateVariableOptions(toVariablePayload(variable, update))),
- toKeyedAction('key', setCurrentVariableValue(toVariablePayload(variable, { option }))),
- toKeyedAction('key', variableStateCompleted(toVariablePayload(variable)))
- );
- });
- });
- describe('when changeQueryVariableQuery is dispatched with invalid query', () => {
- it('then correct actions are dispatched', async () => {
- const variable = createVariable({ datasource: { uid: 'datasource' }, includeAll: false });
- const query = `$${variable.name}`;
- const definition = 'depends on datasource variable';
- const tester = await reduxTester<RootReducerType>()
- .givenRootReducer(getRootReducer())
- .whenActionIsDispatched(
- toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
- )
- .whenActionIsDispatched(toKeyedAction('key', variablesInitTransaction({ uid: 'key' })))
- .whenAsyncActionIsDispatched(
- changeQueryVariableQuery(toKeyedVariableIdentifier(variable), query, definition),
- true
- );
- const errorText = 'Query cannot contain a reference to itself. Variable: $' + variable.name;
- tester.thenDispatchedActionsShouldEqual(
- toKeyedAction('key', addVariableEditorError({ errorProp: 'query', errorText }))
- );
- });
- });
- describe('hasSelfReferencingQuery', () => {
- it('when called with a string', () => {
- const query = '$query';
- const name = 'query';
- expect(hasSelfReferencingQuery(name, query)).toBe(true);
- });
- it('when called with an array', () => {
- const query = ['$query'];
- const name = 'query';
- expect(hasSelfReferencingQuery(name, query)).toBe(true);
- });
- it('when called with a simple object', () => {
- const query = { a: '$query' };
- const name = 'query';
- expect(hasSelfReferencingQuery(name, query)).toBe(true);
- });
- it('when called with a complex object', () => {
- const query = {
- level2: {
- level3: {
- query: 'query3',
- refId: 'C',
- num: 2,
- bool: true,
- arr: [
- { query: 'query4', refId: 'D', num: 4, bool: true },
- {
- query: 'query5',
- refId: 'E',
- num: 5,
- bool: true,
- arr: [{ query: '$query', refId: 'F', num: 6, bool: true }],
- },
- ],
- },
- query: 'query2',
- refId: 'B',
- num: 1,
- bool: false,
- },
- query: 'query1',
- refId: 'A',
- num: 0,
- bool: true,
- arr: [
- { query: 'query7', refId: 'G', num: 7, bool: true },
- {
- query: 'query8',
- refId: 'H',
- num: 8,
- bool: true,
- arr: [{ query: 'query9', refId: 'I', num: 9, bool: true }],
- },
- ],
- };
- const name = 'query';
- expect(hasSelfReferencingQuery(name, query)).toBe(true);
- });
- it('when called with a number', () => {
- const query = 1;
- const name = 'query';
- expect(hasSelfReferencingQuery(name, query)).toBe(false);
- });
- });
- describe('flattenQuery', () => {
- it('when called with a complex object', () => {
- const query = {
- level2: {
- level3: {
- query: '${query3}',
- refId: 'C',
- num: 2,
- bool: true,
- arr: [
- { query: '${query4}', refId: 'D', num: 4, bool: true },
- {
- query: '${query5}',
- refId: 'E',
- num: 5,
- bool: true,
- arr: [{ query: '${query6}', refId: 'F', num: 6, bool: true }],
- },
- ],
- },
- query: '${query2}',
- refId: 'B',
- num: 1,
- bool: false,
- },
- query: '${query1}',
- refId: 'A',
- num: 0,
- bool: true,
- arr: [
- { query: '${query7}', refId: 'G', num: 7, bool: true },
- {
- query: '${query8}',
- refId: 'H',
- num: 8,
- bool: true,
- arr: [{ query: '${query9}', refId: 'I', num: 9, bool: true }],
- },
- ],
- };
- expect(flattenQuery(query)).toEqual({
- query: '${query1}',
- refId: 'A',
- num: 0,
- bool: true,
- level2_query: '${query2}',
- level2_refId: 'B',
- level2_num: 1,
- level2_bool: false,
- level2_level3_query: '${query3}',
- level2_level3_refId: 'C',
- level2_level3_num: 2,
- level2_level3_bool: true,
- level2_level3_arr_0_query: '${query4}',
- level2_level3_arr_0_refId: 'D',
- level2_level3_arr_0_num: 4,
- level2_level3_arr_0_bool: true,
- level2_level3_arr_1_query: '${query5}',
- level2_level3_arr_1_refId: 'E',
- level2_level3_arr_1_num: 5,
- level2_level3_arr_1_bool: true,
- level2_level3_arr_1_arr_0_query: '${query6}',
- level2_level3_arr_1_arr_0_refId: 'F',
- level2_level3_arr_1_arr_0_num: 6,
- level2_level3_arr_1_arr_0_bool: true,
- arr_0_query: '${query7}',
- arr_0_refId: 'G',
- arr_0_num: 7,
- arr_0_bool: true,
- arr_1_query: '${query8}',
- arr_1_refId: 'H',
- arr_1_num: 8,
- arr_1_bool: true,
- arr_1_arr_0_query: '${query9}',
- arr_1_arr_0_refId: 'I',
- arr_1_arr_0_num: 9,
- arr_1_arr_0_bool: true,
- });
- });
- });
- });
- function mockDatasourceMetrics(variable: QueryVariableModel, optionsMetrics: any[]) {
- const metrics: Record<string, any[]> = {
- [variable.query]: optionsMetrics,
- };
- const { metricFindQuery } = mocks[variable.datasource?.uid!];
- metricFindQuery.mockReset();
- metricFindQuery.mockImplementation((query: string) => Promise.resolve(metrics[query] ?? []));
- }
- function createVariable(extend?: Partial<QueryVariableModel>): QueryVariableModel {
- return {
- type: 'query',
- id: '0',
- rootStateKey: 'key',
- global: false,
- current: createOption(''),
- options: [],
- query: 'options-query',
- name: 'Constant',
- label: '',
- hide: VariableHide.dontHide,
- skipUrlSync: false,
- index: 0,
- datasource: { uid: 'datasource' },
- definition: '',
- sort: VariableSort.alphabeticalAsc,
- refresh: VariableRefresh.onDashboardLoad,
- regex: '',
- multi: true,
- includeAll: true,
- state: LoadingState.NotStarted,
- error: null,
- description: null,
- ...(extend ?? {}),
- };
- }
- function createOption(text: string, value?: string) {
- const metric = createMetric(text);
- return {
- ...metric,
- value: value ?? metric.text,
- selected: false,
- };
- }
- function createMetric(value: string) {
- return {
- text: value,
- };
- }
|