123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924 |
- import { AnyAction } from 'redux';
- import { LoadingState } from '@grafana/data';
- import * as runtime from '@grafana/runtime';
- import { reduxTester } from '../../../../test/core/redux/reduxTester';
- import { toAsyncOfResult } from '../../query/state/DashboardQueryRunner/testHelpers';
- import { variableAdapters } from '../adapters';
- import { createConstantVariableAdapter } from '../constant/adapter';
- import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, NEW_VARIABLE_ID } from '../constants';
- import { createCustomVariableAdapter } from '../custom/adapter';
- import { changeVariableName } from '../editor/actions';
- import {
- changeVariableNameFailed,
- changeVariableNameSucceeded,
- cleanEditorState,
- setIdInEditor,
- } from '../editor/reducer';
- import { cleanPickerState } from '../pickers/OptionsPicker/reducer';
- import { setVariableQueryRunner, VariableQueryRunner } from '../query/VariableQueryRunner';
- import { createQueryVariableAdapter } from '../query/adapter';
- import { updateVariableOptions } from '../query/reducer';
- import {
- constantBuilder,
- customBuilder,
- datasourceBuilder,
- queryBuilder,
- textboxBuilder,
- } from '../shared/testing/builders';
- import { createTextBoxVariableAdapter } from '../textbox/adapter';
- import { ConstantVariableModel, VariableRefresh } from '../types';
- import { toKeyedVariableIdentifier, toVariablePayload } from '../utils';
- import {
- cancelVariables,
- changeVariableMultiValue,
- cleanUpVariables,
- fixSelectedInconsistency,
- initDashboardTemplating,
- isVariableUrlValueDifferentFromCurrent,
- processVariables,
- validateVariableSelectionState,
- } from './actions';
- import { getPreloadedState, getTemplatingRootReducer, TemplatingReducerType } from './helpers';
- import { toKeyedAction } from './keyedVariablesReducer';
- import {
- addVariable,
- changeVariableProp,
- removeVariable,
- setCurrentVariableValue,
- variableStateCompleted,
- variableStateFetching,
- variableStateNotStarted,
- } from './sharedReducer';
- import { variablesClearTransaction, variablesInitTransaction } from './transactionReducer';
- import { cleanVariables } from './variablesReducer';
- variableAdapters.setInit(() => [
- createQueryVariableAdapter(),
- createCustomVariableAdapter(),
- createTextBoxVariableAdapter(),
- createConstantVariableAdapter(),
- ]);
- const metricFindQuery = jest
- .fn()
- .mockResolvedValueOnce([{ text: 'responses' }, { text: 'timers' }])
- .mockResolvedValue([{ text: '200' }, { text: '500' }]);
- const getMetricSources = jest.fn().mockReturnValue([]);
- const getDatasource = jest.fn().mockResolvedValue({ metricFindQuery });
- jest.mock('app/features/dashboard/services/TimeSrv', () => ({
- getTimeSrv: () => ({
- timeRange: jest.fn().mockReturnValue(undefined),
- }),
- }));
- runtime.setDataSourceSrv({
- get: getDatasource,
- getList: getMetricSources,
- } as any);
- describe('shared actions', () => {
- describe('when initDashboardTemplating is dispatched', () => {
- it('then correct actions are dispatched', () => {
- const key = 'key';
- const query = queryBuilder().build();
- const constant = constantBuilder().build();
- const datasource = datasourceBuilder().build();
- const custom = customBuilder().build();
- const textbox = textboxBuilder().build();
- const list = [query, constant, datasource, custom, textbox];
- const dashboard: any = { templating: { list } };
- reduxTester<TemplatingReducerType>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating(key, dashboard))
- .thenDispatchedActionsPredicateShouldEqual((dispatchedActions) => {
- expect(dispatchedActions.length).toEqual(8);
- expect(dispatchedActions[0]).toEqual(
- toKeyedAction(key, addVariable(toVariablePayload(query, { global: false, index: 0, model: query })))
- );
- expect(dispatchedActions[1]).toEqual(
- toKeyedAction(key, addVariable(toVariablePayload(constant, { global: false, index: 1, model: constant })))
- );
- expect(dispatchedActions[2]).toEqual(
- toKeyedAction(key, addVariable(toVariablePayload(custom, { global: false, index: 2, model: custom })))
- );
- expect(dispatchedActions[3]).toEqual(
- toKeyedAction(key, addVariable(toVariablePayload(textbox, { global: false, index: 3, model: textbox })))
- );
- // because uuid are dynamic we need to get the uuid from the resulting state
- // an alternative would be to add our own uuids in the model above instead
- expect(dispatchedActions[4]).toEqual(
- toKeyedAction(
- key,
- variableStateNotStarted(
- toVariablePayload({ ...query, id: dispatchedActions[4].payload.action.payload.id })
- )
- )
- );
- expect(dispatchedActions[5]).toEqual(
- toKeyedAction(
- key,
- variableStateNotStarted(
- toVariablePayload({ ...constant, id: dispatchedActions[5].payload.action.payload.id })
- )
- )
- );
- expect(dispatchedActions[6]).toEqual(
- toKeyedAction(
- key,
- variableStateNotStarted(
- toVariablePayload({ ...custom, id: dispatchedActions[6].payload.action.payload.id })
- )
- )
- );
- expect(dispatchedActions[7]).toEqual(
- toKeyedAction(
- key,
- variableStateNotStarted(
- toVariablePayload({ ...textbox, id: dispatchedActions[7].payload.action.payload.id })
- )
- )
- );
- return true;
- });
- });
- });
- describe('when processVariables is dispatched', () => {
- it('then circular dependencies fail gracefully', async () => {
- const key = 'key';
- const var1 = queryBuilder().withName('var1').withQuery('$var2').build();
- const var2 = queryBuilder().withName('var2').withQuery('$var1').build();
- const dashboard: any = { templating: { list: [var1, var2] } };
- const preloadedState = getPreloadedState(key, {});
- await expect(async () => {
- await reduxTester<TemplatingReducerType>({ preloadedState })
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(toKeyedAction(key, variablesInitTransaction({ uid: key })))
- .whenActionIsDispatched(initDashboardTemplating(key, dashboard))
- .whenAsyncActionIsDispatched(processVariables(key), true);
- }).rejects.toThrow(/circular dependency in dashboard variables detected/i);
- });
- it('then correct actions are dispatched', async () => {
- const key = 'key';
- const query = queryBuilder().build();
- const constant = constantBuilder().build();
- const datasource = datasourceBuilder().build();
- const custom = customBuilder().build();
- const textbox = textboxBuilder().build();
- const list = [query, constant, datasource, custom, textbox];
- const dashboard: any = { templating: { list } };
- const preloadedState = getPreloadedState(key, {});
- const locationService: any = { getSearchObject: () => ({}) };
- runtime.setLocationService(locationService);
- const variableQueryRunner: any = {
- cancelRequest: jest.fn(),
- queueRequest: jest.fn(),
- getResponse: () => toAsyncOfResult({ state: LoadingState.Done, identifier: toKeyedVariableIdentifier(query) }),
- destroy: jest.fn(),
- };
- setVariableQueryRunner(variableQueryRunner);
- const tester = await reduxTester<TemplatingReducerType>({ preloadedState })
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(toKeyedAction(key, variablesInitTransaction({ uid: key })))
- .whenActionIsDispatched(initDashboardTemplating(key, dashboard))
- .whenAsyncActionIsDispatched(processVariables(key), true);
- await tester.thenDispatchedActionsPredicateShouldEqual((dispatchedActions) => {
- expect(dispatchedActions.length).toEqual(5);
- expect(dispatchedActions[0]).toEqual(
- toKeyedAction(
- key,
- variableStateFetching(toVariablePayload({ ...query, id: dispatchedActions[0].payload.action.payload.id }))
- )
- );
- expect(dispatchedActions[1]).toEqual(
- toKeyedAction(
- key,
- variableStateCompleted(
- toVariablePayload({ ...constant, id: dispatchedActions[1].payload.action.payload.id })
- )
- )
- );
- expect(dispatchedActions[2]).toEqual(
- toKeyedAction(
- key,
- variableStateCompleted(toVariablePayload({ ...custom, id: dispatchedActions[2].payload.action.payload.id }))
- )
- );
- expect(dispatchedActions[3]).toEqual(
- toKeyedAction(
- key,
- variableStateCompleted(
- toVariablePayload({ ...textbox, id: dispatchedActions[3].payload.action.payload.id })
- )
- )
- );
- expect(dispatchedActions[4]).toEqual(
- toKeyedAction(
- key,
- variableStateCompleted(toVariablePayload({ ...query, id: dispatchedActions[4].payload.action.payload.id }))
- )
- );
- return true;
- });
- });
- // Fix for https://github.com/grafana/grafana/issues/28791
- it('fix for https://github.com/grafana/grafana/issues/28791', async () => {
- setVariableQueryRunner(new VariableQueryRunner());
- const key = 'key';
- const stats = queryBuilder()
- .withId('stats')
- .withRootStateKey(key)
- .withName('stats')
- .withQuery('stats.*')
- .withRefresh(VariableRefresh.onDashboardLoad)
- .withCurrent(['response'], ['response'])
- .withMulti()
- .withIncludeAll()
- .build();
- const substats = queryBuilder()
- .withId('substats')
- .withRootStateKey(key)
- .withName('substats')
- .withQuery('stats.$stats.*')
- .withRefresh(VariableRefresh.onDashboardLoad)
- .withCurrent([ALL_VARIABLE_TEXT], [ALL_VARIABLE_VALUE])
- .withMulti()
- .withIncludeAll()
- .build();
- const list = [stats, substats];
- const dashboard: any = { templating: { list } };
- const query = { orgId: '1', 'var-stats': 'response', 'var-substats': ALL_VARIABLE_TEXT };
- const locationService: any = { getSearchObject: () => query };
- runtime.setLocationService(locationService);
- const preloadedState = getPreloadedState(key, {});
- const tester = await reduxTester<TemplatingReducerType>({ preloadedState })
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(toKeyedAction(key, variablesInitTransaction({ uid: key })))
- .whenActionIsDispatched(initDashboardTemplating(key, dashboard))
- .whenAsyncActionIsDispatched(processVariables(key), true);
- await tester.thenDispatchedActionsShouldEqual(
- toKeyedAction(key, variableStateFetching(toVariablePayload(stats))),
- toKeyedAction(
- key,
- updateVariableOptions(
- toVariablePayload(stats, { results: [{ text: 'responses' }, { text: 'timers' }], templatedRegex: '' })
- )
- ),
- toKeyedAction(
- key,
- setCurrentVariableValue(
- toVariablePayload(stats, {
- option: { text: ALL_VARIABLE_TEXT, value: ALL_VARIABLE_VALUE, selected: false },
- })
- )
- ),
- toKeyedAction(key, variableStateCompleted(toVariablePayload(stats))),
- toKeyedAction(
- key,
- setCurrentVariableValue(
- toVariablePayload(stats, { option: { text: ['response'], value: ['response'], selected: false } })
- )
- ),
- toKeyedAction(key, variableStateFetching(toVariablePayload(substats))),
- toKeyedAction(
- key,
- updateVariableOptions(
- toVariablePayload(substats, { results: [{ text: '200' }, { text: '500' }], templatedRegex: '' })
- )
- ),
- toKeyedAction(
- key,
- setCurrentVariableValue(
- toVariablePayload(substats, {
- option: { text: [ALL_VARIABLE_TEXT], value: [ALL_VARIABLE_VALUE], selected: true },
- })
- )
- ),
- toKeyedAction(key, variableStateCompleted(toVariablePayload(substats))),
- toKeyedAction(
- key,
- setCurrentVariableValue(
- toVariablePayload(substats, {
- option: { text: [ALL_VARIABLE_TEXT], value: [ALL_VARIABLE_VALUE], selected: false },
- })
- )
- )
- );
- });
- });
- describe('when validateVariableSelectionState is dispatched with a custom variable (no dependencies)', () => {
- describe('and not multivalue', () => {
- it.each`
- withOptions | withCurrent | defaultValue | expected
- ${['A', 'B', 'C']} | ${undefined} | ${undefined} | ${'A'}
- ${['A', 'B', 'C']} | ${'B'} | ${undefined} | ${'B'}
- ${['A', 'B', 'C']} | ${'B'} | ${'C'} | ${'B'}
- ${['A', 'B', 'C']} | ${'X'} | ${undefined} | ${'A'}
- ${['A', 'B', 'C']} | ${'X'} | ${'C'} | ${'C'}
- ${undefined} | ${'B'} | ${undefined} | ${'should not dispatch setCurrentVariableValue'}
- `('then correct actions are dispatched', async ({ withOptions, withCurrent, defaultValue, expected }) => {
- let custom;
- const key = 'key';
- if (!withOptions) {
- custom = customBuilder().withId('0').withRootStateKey(key).withCurrent(withCurrent).withoutOptions().build();
- } else {
- custom = customBuilder()
- .withId('0')
- .withRootStateKey(key)
- .withOptions(...withOptions)
- .withCurrent(withCurrent)
- .build();
- }
- const tester = await reduxTester<TemplatingReducerType>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(
- toKeyedAction(key, addVariable(toVariablePayload(custom, { global: false, index: 0, model: custom })))
- )
- .whenAsyncActionIsDispatched(
- validateVariableSelectionState(toKeyedVariableIdentifier(custom), defaultValue),
- true
- );
- await tester.thenDispatchedActionsPredicateShouldEqual((dispatchedActions) => {
- const expectedActions: AnyAction[] = withOptions
- ? [
- toKeyedAction(
- key,
- setCurrentVariableValue(
- toVariablePayload(
- { type: 'custom', id: '0' },
- { option: { text: expected, value: expected, selected: false } }
- )
- )
- ),
- ]
- : [];
- expect(dispatchedActions).toEqual(expectedActions);
- return true;
- });
- });
- });
- describe('and multivalue', () => {
- it.each`
- withOptions | withCurrent | defaultValue | expectedText | expectedSelected
- ${['A', 'B', 'C']} | ${['B']} | ${undefined} | ${['B']} | ${true}
- ${['A', 'B', 'C']} | ${['B']} | ${'C'} | ${['B']} | ${true}
- ${['A', 'B', 'C']} | ${['B', 'C']} | ${undefined} | ${['B', 'C']} | ${true}
- ${['A', 'B', 'C']} | ${['B', 'C']} | ${'C'} | ${['B', 'C']} | ${true}
- ${['A', 'B', 'C']} | ${['X']} | ${undefined} | ${'A'} | ${false}
- ${['A', 'B', 'C']} | ${['X']} | ${'C'} | ${'A'} | ${false}
- `(
- 'then correct actions are dispatched',
- async ({ withOptions, withCurrent, defaultValue, expectedText, expectedSelected }) => {
- let custom;
- const key = 'key';
- if (!withOptions) {
- custom = customBuilder()
- .withId('0')
- .withRootStateKey(key)
- .withMulti()
- .withCurrent(withCurrent)
- .withoutOptions()
- .build();
- } else {
- custom = customBuilder()
- .withId('0')
- .withRootStateKey(key)
- .withMulti()
- .withOptions(...withOptions)
- .withCurrent(withCurrent)
- .build();
- }
- const tester = await reduxTester<TemplatingReducerType>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(
- toKeyedAction(key, addVariable(toVariablePayload(custom, { global: false, index: 0, model: custom })))
- )
- .whenAsyncActionIsDispatched(
- validateVariableSelectionState(toKeyedVariableIdentifier(custom), defaultValue),
- true
- );
- await tester.thenDispatchedActionsPredicateShouldEqual((dispatchedActions) => {
- const expectedActions: AnyAction[] = withOptions
- ? [
- toKeyedAction(
- key,
- setCurrentVariableValue(
- toVariablePayload(
- { type: 'custom', id: '0' },
- { option: { text: expectedText, value: expectedText, selected: expectedSelected } }
- )
- )
- ),
- ]
- : [];
- expect(dispatchedActions).toEqual(expectedActions);
- return true;
- });
- }
- );
- });
- });
- describe('changeVariableName', () => {
- describe('when changeVariableName is dispatched with the same name', () => {
- it('then the correct actions are dispatched', () => {
- const key = 'key';
- const textbox = textboxBuilder().withId('textbox').withRootStateKey(key).withName('textbox').build();
- const constant = constantBuilder().withId('constant').withRootStateKey(key).withName('constant').build();
- reduxTester<TemplatingReducerType>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(
- toKeyedAction(key, addVariable(toVariablePayload(textbox, { global: false, index: 0, model: textbox })))
- )
- .whenActionIsDispatched(
- toKeyedAction(key, addVariable(toVariablePayload(constant, { global: false, index: 1, model: constant })))
- )
- .whenActionIsDispatched(changeVariableName(toKeyedVariableIdentifier(constant), constant.name), true)
- .thenDispatchedActionsShouldEqual(
- toKeyedAction(
- key,
- changeVariableNameSucceeded({ type: 'constant', id: 'constant', data: { newName: 'constant' } })
- )
- );
- });
- });
- describe('when changeVariableName is dispatched with an unique name', () => {
- it('then the correct actions are dispatched', () => {
- const key = 'key';
- const textbox = textboxBuilder().withId('textbox').withRootStateKey(key).withName('textbox').build();
- const constant = constantBuilder().withId('constant').withRootStateKey(key).withName('constant').build();
- reduxTester<TemplatingReducerType>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(
- toKeyedAction(key, addVariable(toVariablePayload(textbox, { global: false, index: 0, model: textbox })))
- )
- .whenActionIsDispatched(
- toKeyedAction(key, addVariable(toVariablePayload(constant, { global: false, index: 1, model: constant })))
- )
- .whenActionIsDispatched(changeVariableName(toKeyedVariableIdentifier(constant), 'constant1'), true)
- .thenDispatchedActionsShouldEqual(
- toKeyedAction(
- key,
- addVariable({
- type: 'constant',
- id: 'constant1',
- data: {
- global: false,
- index: 1,
- model: {
- ...constant,
- name: 'constant1',
- id: 'constant1',
- global: false,
- index: 1,
- current: { selected: true, text: '', value: '' },
- options: [{ selected: true, text: '', value: '' }],
- } as ConstantVariableModel,
- },
- })
- ),
- toKeyedAction(
- key,
- changeVariableNameSucceeded({ type: 'constant', id: 'constant1', data: { newName: 'constant1' } })
- ),
- toKeyedAction(key, setIdInEditor({ id: 'constant1' })),
- toKeyedAction(key, removeVariable({ type: 'constant', id: 'constant', data: { reIndex: false } }))
- );
- });
- });
- describe('when changeVariableName is dispatched with an unique name for a new variable', () => {
- it('then the correct actions are dispatched', () => {
- const key = 'key';
- const textbox = textboxBuilder().withId('textbox').withRootStateKey(key).withName('textbox').build();
- const constant = constantBuilder().withId(NEW_VARIABLE_ID).withRootStateKey(key).withName('constant').build();
- reduxTester<TemplatingReducerType>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(
- toKeyedAction(key, addVariable(toVariablePayload(textbox, { global: false, index: 0, model: textbox })))
- )
- .whenActionIsDispatched(
- toKeyedAction(key, addVariable(toVariablePayload(constant, { global: false, index: 1, model: constant })))
- )
- .whenActionIsDispatched(changeVariableName(toKeyedVariableIdentifier(constant), 'constant1'), true)
- .thenDispatchedActionsShouldEqual(
- toKeyedAction(
- key,
- addVariable({
- type: 'constant',
- id: 'constant1',
- data: {
- global: false,
- index: 1,
- model: {
- ...constant,
- name: 'constant1',
- id: 'constant1',
- global: false,
- index: 1,
- current: { selected: true, text: '', value: '' },
- options: [{ selected: true, text: '', value: '' }],
- } as ConstantVariableModel,
- },
- })
- ),
- toKeyedAction(
- key,
- changeVariableNameSucceeded({ type: 'constant', id: 'constant1', data: { newName: 'constant1' } })
- ),
- toKeyedAction(key, setIdInEditor({ id: 'constant1' })),
- toKeyedAction(key, removeVariable({ type: 'constant', id: NEW_VARIABLE_ID, data: { reIndex: false } }))
- );
- });
- });
- describe('when changeVariableName is dispatched with __newName', () => {
- it('then the correct actions are dispatched', () => {
- const key = 'key';
- const textbox = textboxBuilder().withId('textbox').withRootStateKey(key).withName('textbox').build();
- const constant = constantBuilder().withId('constant').withRootStateKey(key).withName('constant').build();
- reduxTester<TemplatingReducerType>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(
- toKeyedAction(key, addVariable(toVariablePayload(textbox, { global: false, index: 0, model: textbox })))
- )
- .whenActionIsDispatched(
- toKeyedAction(key, addVariable(toVariablePayload(constant, { global: false, index: 1, model: constant })))
- )
- .whenActionIsDispatched(changeVariableName(toKeyedVariableIdentifier(constant), '__newName'), true)
- .thenDispatchedActionsShouldEqual(
- toKeyedAction(
- key,
- changeVariableNameFailed({
- newName: '__newName',
- errorText: "Template names cannot begin with '__', that's reserved for Grafana's global variables",
- })
- )
- );
- });
- });
- describe('when changeVariableName is dispatched with illegal characters', () => {
- it('then the correct actions are dispatched', () => {
- const key = 'key';
- const textbox = textboxBuilder().withId('textbox').withRootStateKey(key).withName('textbox').build();
- const constant = constantBuilder().withId('constant').withRootStateKey(key).withName('constant').build();
- reduxTester<TemplatingReducerType>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(
- toKeyedAction(key, addVariable(toVariablePayload(textbox, { global: false, index: 0, model: textbox })))
- )
- .whenActionIsDispatched(
- toKeyedAction(key, addVariable(toVariablePayload(constant, { global: false, index: 1, model: constant })))
- )
- .whenActionIsDispatched(changeVariableName(toKeyedVariableIdentifier(constant), '#constant!'), true)
- .thenDispatchedActionsShouldEqual(
- toKeyedAction(
- key,
- changeVariableNameFailed({
- newName: '#constant!',
- errorText: 'Only word and digit characters are allowed in variable names',
- })
- )
- );
- });
- });
- describe('when changeVariableName is dispatched with a name that is already used', () => {
- it('then the correct actions are dispatched', () => {
- const key = 'key';
- const textbox = textboxBuilder().withId('textbox').withRootStateKey(key).withName('textbox').build();
- const constant = constantBuilder().withId('constant').withRootStateKey(key).withName('constant').build();
- reduxTester<TemplatingReducerType>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(
- toKeyedAction(key, addVariable(toVariablePayload(textbox, { global: false, index: 0, model: textbox })))
- )
- .whenActionIsDispatched(
- toKeyedAction(key, addVariable(toVariablePayload(constant, { global: false, index: 1, model: constant })))
- )
- .whenActionIsDispatched(changeVariableName(toKeyedVariableIdentifier(constant), 'textbox'), true)
- .thenDispatchedActionsShouldEqual(
- toKeyedAction(
- key,
- changeVariableNameFailed({
- newName: 'textbox',
- errorText: 'Variable with the same name already exists',
- })
- )
- );
- });
- });
- });
- describe('changeVariableMultiValue', () => {
- describe('when changeVariableMultiValue is dispatched for variable with multi enabled', () => {
- it('then correct actions are dispatched', () => {
- const key = 'key';
- const custom = customBuilder()
- .withId('custom')
- .withRootStateKey(key)
- .withMulti(true)
- .withCurrent(['A'], ['A'])
- .build();
- reduxTester<TemplatingReducerType>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(
- toKeyedAction(key, addVariable(toVariablePayload(custom, { global: false, index: 0, model: custom })))
- )
- .whenActionIsDispatched(changeVariableMultiValue(toKeyedVariableIdentifier(custom), false), true)
- .thenDispatchedActionsShouldEqual(
- toKeyedAction(
- key,
- changeVariableProp(
- toVariablePayload(custom, {
- propName: 'multi',
- propValue: false,
- })
- )
- ),
- toKeyedAction(
- key,
- changeVariableProp(
- toVariablePayload(custom, {
- propName: 'current',
- propValue: {
- value: 'A',
- text: 'A',
- selected: true,
- },
- })
- )
- )
- );
- });
- });
- describe('when changeVariableMultiValue is dispatched for variable with multi disabled', () => {
- it('then correct actions are dispatched', () => {
- const key = 'key';
- const custom = customBuilder()
- .withId('custom')
- .withRootStateKey(key)
- .withMulti(false)
- .withCurrent(['A'], ['A'])
- .build();
- reduxTester<TemplatingReducerType>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(
- toKeyedAction(key, addVariable(toVariablePayload(custom, { global: false, index: 0, model: custom })))
- )
- .whenActionIsDispatched(changeVariableMultiValue(toKeyedVariableIdentifier(custom), true), true)
- .thenDispatchedActionsShouldEqual(
- toKeyedAction(
- key,
- changeVariableProp(
- toVariablePayload(custom, {
- propName: 'multi',
- propValue: true,
- })
- )
- ),
- toKeyedAction(
- key,
- changeVariableProp(
- toVariablePayload(custom, {
- propName: 'current',
- propValue: {
- value: ['A'],
- text: ['A'],
- selected: true,
- },
- })
- )
- )
- );
- });
- });
- });
- describe('cleanUpVariables', () => {
- describe('when called', () => {
- it('then correct actions are dispatched', async () => {
- const key = 'key';
- reduxTester<TemplatingReducerType>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(cleanUpVariables(key))
- .thenDispatchedActionsShouldEqual(
- toKeyedAction(key, cleanVariables()),
- toKeyedAction(key, cleanEditorState()),
- toKeyedAction(key, cleanPickerState()),
- toKeyedAction(key, variablesClearTransaction())
- );
- });
- });
- });
- describe('cancelVariables', () => {
- const cancelAllInFlightRequestsMock = jest.fn();
- const backendSrvMock: any = {
- cancelAllInFlightRequests: cancelAllInFlightRequestsMock,
- };
- describe('when called', () => {
- it('then cancelAllInFlightRequests should be called and correct actions are dispatched', async () => {
- const key = 'key';
- reduxTester<TemplatingReducerType>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(cancelVariables(key, { getBackendSrv: () => backendSrvMock }))
- .thenDispatchedActionsShouldEqual(
- toKeyedAction(key, cleanVariables()),
- toKeyedAction(key, cleanEditorState()),
- toKeyedAction(key, cleanPickerState()),
- toKeyedAction(key, variablesClearTransaction())
- );
- expect(cancelAllInFlightRequestsMock).toHaveBeenCalledTimes(1);
- });
- });
- });
- describe('fixSelectedInconsistency', () => {
- describe('when called for a single value variable', () => {
- describe('and there is an inconsistency between current and selected in options', () => {
- it('then it should set the correct selected', () => {
- const variable = customBuilder().withId('custom').withCurrent('A').withOptions('A', 'B', 'C').build();
- variable.options[1].selected = true;
- expect(variable.options).toEqual([
- { text: 'A', value: 'A', selected: false },
- { text: 'B', value: 'B', selected: true },
- { text: 'C', value: 'C', selected: false },
- ]);
- fixSelectedInconsistency(variable);
- expect(variable.options).toEqual([
- { text: 'A', value: 'A', selected: true },
- { text: 'B', value: 'B', selected: false },
- { text: 'C', value: 'C', selected: false },
- ]);
- });
- });
- describe('and there is no matching option in options', () => {
- it('then the first option should be selected', () => {
- const variable = customBuilder().withId('custom').withCurrent('A').withOptions('X', 'Y', 'Z').build();
- expect(variable.options).toEqual([
- { text: 'X', value: 'X', selected: false },
- { text: 'Y', value: 'Y', selected: false },
- { text: 'Z', value: 'Z', selected: false },
- ]);
- fixSelectedInconsistency(variable);
- expect(variable.options).toEqual([
- { text: 'X', value: 'X', selected: true },
- { text: 'Y', value: 'Y', selected: false },
- { text: 'Z', value: 'Z', selected: false },
- ]);
- });
- });
- });
- describe('when called for a multi value variable', () => {
- describe('and there is an inconsistency between current and selected in options', () => {
- it('then it should set the correct selected', () => {
- const variable = customBuilder().withId('custom').withCurrent(['A', 'C']).withOptions('A', 'B', 'C').build();
- variable.options[1].selected = true;
- expect(variable.options).toEqual([
- { text: 'A', value: 'A', selected: false },
- { text: 'B', value: 'B', selected: true },
- { text: 'C', value: 'C', selected: false },
- ]);
- fixSelectedInconsistency(variable);
- expect(variable.options).toEqual([
- { text: 'A', value: 'A', selected: true },
- { text: 'B', value: 'B', selected: false },
- { text: 'C', value: 'C', selected: true },
- ]);
- });
- });
- describe('and there is no matching option in options', () => {
- it('then the first option should be selected', () => {
- const variable = customBuilder().withId('custom').withCurrent(['A', 'C']).withOptions('X', 'Y', 'Z').build();
- expect(variable.options).toEqual([
- { text: 'X', value: 'X', selected: false },
- { text: 'Y', value: 'Y', selected: false },
- { text: 'Z', value: 'Z', selected: false },
- ]);
- fixSelectedInconsistency(variable);
- expect(variable.options).toEqual([
- { text: 'X', value: 'X', selected: true },
- { text: 'Y', value: 'Y', selected: false },
- { text: 'Z', value: 'Z', selected: false },
- ]);
- });
- });
- });
- });
- describe('isVariableUrlValueDifferentFromCurrent', () => {
- describe('when called with a single valued variable', () => {
- describe('and values are equal', () => {
- it('then it should return false', () => {
- const variable = queryBuilder().withMulti(false).withCurrent('A', 'A').build();
- const urlValue = 'A';
- expect(isVariableUrlValueDifferentFromCurrent(variable, urlValue)).toBe(false);
- });
- });
- describe('and values are different', () => {
- it('then it should return true', () => {
- const variable = queryBuilder().withMulti(false).withCurrent('A', 'A').build();
- const urlValue = 'B';
- expect(isVariableUrlValueDifferentFromCurrent(variable, urlValue)).toBe(true);
- });
- });
- });
- describe('when called with a multi valued variable', () => {
- describe('and values are equal', () => {
- it('then it should return false', () => {
- const variable = queryBuilder().withMulti(true).withCurrent(['A'], ['A']).build();
- const urlValue = ['A'];
- expect(isVariableUrlValueDifferentFromCurrent(variable, urlValue)).toBe(false);
- });
- describe('but urlValue is not an array', () => {
- it('then it should return false', () => {
- const variable = queryBuilder().withMulti(true).withCurrent(['A'], ['A']).build();
- const urlValue = 'A';
- expect(isVariableUrlValueDifferentFromCurrent(variable, urlValue)).toBe(false);
- });
- });
- });
- describe('and values are different', () => {
- it('then it should return true', () => {
- const variable = queryBuilder().withMulti(true).withCurrent(['A'], ['A']).build();
- const urlValue = ['C'];
- expect(isVariableUrlValueDifferentFromCurrent(variable, urlValue)).toBe(true);
- });
- describe('but urlValue is not an array', () => {
- it('then it should return true', () => {
- const variable = queryBuilder().withMulti(true).withCurrent(['A'], ['A']).build();
- const urlValue = 'C';
- expect(isVariableUrlValueDifferentFromCurrent(variable, urlValue)).toBe(true);
- });
- });
- });
- });
- });
- });
|