Submit
Path:
~
/
/
usr
/
share
/
grafana
/
public
/
app
/
features
/
variables
/
pickers
/
OptionsPicker
/
File Content:
actions.test.ts
import { locationService } from '@grafana/runtime'; import { reduxTester } from '../../../../../test/core/redux/reduxTester'; import { variableAdapters } from '../../adapters'; import { createQueryVariableAdapter } from '../../query/adapter'; import { queryBuilder } from '../../shared/testing/builders'; import { getPreloadedState, getRootReducer, RootReducerType } from '../../state/helpers'; import { toKeyedAction } from '../../state/keyedVariablesReducer'; import { addVariable, changeVariableProp, setCurrentVariableValue } from '../../state/sharedReducer'; import { initialVariableModelState, QueryVariableModel, VariableRefresh, VariableSort } from '../../types'; import { toKeyedVariableIdentifier, toVariablePayload } from '../../utils'; import { NavigationKey } from '../types'; import { commitChangesToVariable, filterOrSearchOptions, navigateOptions, openOptions, toggleOptionByHighlight, } from './actions'; import { hideOptions, initialOptionPickerState, moveOptionsHighlight, showOptions, toggleOption, updateOptionsAndFilter, updateSearchQuery, } from './reducer'; const datasource = { metricFindQuery: jest.fn(() => Promise.resolve([])), }; jest.mock('@grafana/runtime', () => { const original = jest.requireActual('@grafana/runtime'); return { ...original, getDataSourceSrv: jest.fn(() => ({ get: () => datasource, })), locationService: { partial: jest.fn(), getSearchObject: () => ({}), }, }; }); describe('options picker actions', () => { variableAdapters.setInit(() => [createQueryVariableAdapter()]); describe('when navigateOptions is dispatched with navigation key cancel', () => { it('then correct actions are dispatched', async () => { const variable = createMultiVariable({ options: [createOption('A', 'A', true)], current: createOption(['A'], ['A'], true), }); const clearOthers = false; const key = NavigationKey.cancel; const tester = await reduxTester<RootReducerType>() .givenRootReducer(getRootReducer()) .whenActionIsDispatched( toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable }))) ) .whenActionIsDispatched(toKeyedAction('key', showOptions(variable))) .whenAsyncActionIsDispatched(navigateOptions('key', key, clearOthers), true); const option = { ...createOption(['A']), selected: true, value: ['A'], }; tester.thenDispatchedActionsShouldEqual( toKeyedAction('key', setCurrentVariableValue(toVariablePayload(variable, { option }))), toKeyedAction( 'key', changeVariableProp(toVariablePayload(variable, { propName: 'queryValue', propValue: '' })) ), toKeyedAction('key', hideOptions()) ); }); }); describe('when navigateOptions is dispatched with navigation key select without clearOthers', () => { it('then correct actions are dispatched', async () => { const option = createOption('A', 'A', true); const variable = createMultiVariable({ options: [option], current: createOption(['A'], ['A'], true), includeAll: false, }); const clearOthers = false; const key = NavigationKey.select; const tester = await reduxTester<RootReducerType>() .givenRootReducer(getRootReducer()) .whenActionIsDispatched( toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable }))) ) .whenActionIsDispatched(toKeyedAction('key', showOptions(variable))) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveDown, false)) .whenAsyncActionIsDispatched(navigateOptions('key', key, clearOthers), true); tester.thenDispatchedActionsShouldEqual( toKeyedAction('key', toggleOption({ option, forceSelect: false, clearOthers })) ); }); }); describe('when navigateOptions is dispatched with navigation key select with clearOthers', () => { it('then correct actions are dispatched', async () => { const option = createOption('A', 'A', true); const variable = createMultiVariable({ options: [option], current: createOption(['A'], ['A'], true), includeAll: false, }); const clearOthers = true; const key = NavigationKey.select; const tester = await reduxTester<RootReducerType>() .givenRootReducer(getRootReducer()) .whenActionIsDispatched( toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable }))) ) .whenActionIsDispatched(toKeyedAction('key', showOptions(variable))) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveDown, clearOthers)) .whenAsyncActionIsDispatched(navigateOptions('key', key, clearOthers), true); tester.thenDispatchedActionsShouldEqual( toKeyedAction('key', toggleOption({ option, forceSelect: false, clearOthers })) ); }); }); describe('when navigateOptions is dispatched with navigation key select after highlighting the third option', () => { it('then correct actions are dispatched', async () => { const options = [createOption('A'), createOption('B'), createOption('C')]; const variable = createMultiVariable({ options, current: createOption(['A'], ['A'], true), includeAll: false }); const clearOthers = true; const key = NavigationKey.select; const tester = await reduxTester<RootReducerType>() .givenRootReducer(getRootReducer()) .whenActionIsDispatched( toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable }))) ) .whenActionIsDispatched(toKeyedAction('key', showOptions(variable))) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveDown, clearOthers)) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveDown, clearOthers)) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveDown, clearOthers)) .whenAsyncActionIsDispatched(navigateOptions('key', key, clearOthers), true); tester.thenDispatchedActionsShouldEqual( toKeyedAction('key', toggleOption({ option: options[2], forceSelect: false, clearOthers })) ); }); }); describe('when navigateOptions is dispatched with navigation key select after highlighting the second option', () => { it('then correct actions are dispatched', async () => { const options = [createOption('A'), createOption('B'), createOption('C')]; const variable = createMultiVariable({ options, current: createOption(['A'], ['A'], true), includeAll: false }); const clearOthers = true; const key = NavigationKey.select; const tester = await reduxTester<RootReducerType>() .givenRootReducer(getRootReducer()) .whenActionIsDispatched( toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable }))) ) .whenActionIsDispatched(toKeyedAction('key', showOptions(variable))) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveDown, clearOthers)) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveDown, clearOthers)) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveDown, clearOthers)) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveUp, clearOthers)) .whenAsyncActionIsDispatched(navigateOptions('key', key, clearOthers), true); tester.thenDispatchedActionsShouldEqual( toKeyedAction('key', toggleOption({ option: options[1], forceSelect: false, clearOthers })) ); }); }); it('supports having variables with the same label and different values', async () => { const options = [createOption('sameLabel', 'A'), createOption('sameLabel', 'B')]; const variable = createMultiVariable({ options, current: createOption(['sameLabel'], ['A'], true), includeAll: false, }); const clearOthers = false; const key = NavigationKey.selectAndClose; // Open the menu and select the second option const tester = await reduxTester<RootReducerType>() .givenRootReducer(getRootReducer()) .whenActionIsDispatched( toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable }))) ) .whenActionIsDispatched(toKeyedAction('key', showOptions(variable))) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveDown, clearOthers)) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveDown, clearOthers)) .whenAsyncActionIsDispatched(navigateOptions('key', key, clearOthers), true); // Check selecting the second option triggers variables to update tester.thenDispatchedActionsShouldEqual( toKeyedAction('key', toggleOption({ option: options[1], forceSelect: false, clearOthers })) ); }); describe('when navigateOptions is dispatched with navigation key selectAndClose after highlighting the second option', () => { it('then correct actions are dispatched for multi-value variable', async () => { const options = [createOption('A'), createOption('B'), createOption('C')]; const variable = createMultiVariable({ options, current: createOption(['A'], ['A'], true), includeAll: false }); const clearOthers = false; const key = NavigationKey.selectAndClose; const tester = await reduxTester<RootReducerType>() .givenRootReducer(getRootReducer()) .whenActionIsDispatched( toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable }))) ) .whenActionIsDispatched(toKeyedAction('key', showOptions(variable))) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveDown, clearOthers)) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveDown, clearOthers)) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveDown, clearOthers)) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveUp, clearOthers)) .whenAsyncActionIsDispatched(navigateOptions('key', key, clearOthers), true); tester.thenDispatchedActionsShouldEqual( toKeyedAction('key', toggleOption({ option: options[1], forceSelect: false, clearOthers })) ); }); it('then correct actions are dispatched for single-value variable', async () => { const options = [createOption('A'), createOption('B'), createOption('C')]; const variable = createVariable({ options, current: createOption('A', 'A', true), includeAll: false }); const clearOthers = false; const key = NavigationKey.selectAndClose; const tester = await reduxTester<RootReducerType>() .givenRootReducer(getRootReducer()) .whenActionIsDispatched( toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable }))) ) .whenActionIsDispatched(toKeyedAction('key', showOptions(variable))) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveDown, clearOthers)) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveDown, clearOthers)) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveDown, clearOthers)) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveUp, clearOthers)) .whenAsyncActionIsDispatched(navigateOptions('key', key, clearOthers), true); const option = { ...createOption('B'), selected: true, value: 'B', }; tester.thenDispatchedActionsShouldEqual( toKeyedAction('key', toggleOption({ option: options[1], forceSelect: true, clearOthers })), toKeyedAction('key', setCurrentVariableValue(toVariablePayload(variable, { option }))), toKeyedAction( 'key', changeVariableProp(toVariablePayload(variable, { propName: 'queryValue', propValue: '' })) ), toKeyedAction('key', hideOptions()), toKeyedAction('key', setCurrentVariableValue(toVariablePayload(variable, { option }))) ); expect(locationService.partial).toHaveBeenLastCalledWith({ 'var-Constant': 'B' }); }); }); describe('when filterOrSearchOptions is dispatched with simple filter', () => { it('then correct actions are dispatched', async () => { const options = [createOption('A'), createOption('B'), createOption('C')]; const variable = createMultiVariable({ options, current: createOption(['A'], ['A'], true), includeAll: false }); const filter = 'A'; const tester = await reduxTester<RootReducerType>() .givenRootReducer(getRootReducer()) .whenActionIsDispatched( toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable }))) ) .whenActionIsDispatched(toKeyedAction('key', showOptions(variable))) .whenAsyncActionIsDispatched(filterOrSearchOptions(toKeyedVariableIdentifier(variable), filter), true); tester.thenDispatchedActionsShouldEqual( toKeyedAction('key', updateSearchQuery(filter)), toKeyedAction('key', updateOptionsAndFilter(variable.options)) ); }); }); describe('when openOptions is dispatched and there is no picker state yet', () => { it('then correct actions are dispatched', async () => { const variable = queryBuilder() .withId('query0') .withRootStateKey('key') .withName('query0') .withMulti() .withCurrent(['A', 'C']) .withOptions('A', 'B', 'C') .build(); const preloadedState = getPreloadedState('key', { variables: { [variable.id]: { ...variable }, }, optionsPicker: { ...initialOptionPickerState }, }); const tester = await reduxTester<RootReducerType>({ preloadedState }) .givenRootReducer(getRootReducer()) .whenAsyncActionIsDispatched(openOptions(toKeyedVariableIdentifier(variable), undefined)); tester.thenDispatchedActionsShouldEqual(toKeyedAction('key', showOptions(variable))); }); }); describe('when openOptions is dispatched and picker.id is same as variable.id', () => { it('then correct actions are dispatched', async () => { const variable = queryBuilder() .withId('query0') .withRootStateKey('key') .withName('query0') .withMulti() .withCurrent(['A', 'C']) .withOptions('A', 'B', 'C') .build(); const preloadedState = getPreloadedState('key', { variables: { [variable.id]: { ...variable }, }, optionsPicker: { ...initialOptionPickerState, id: variable.id }, }); const tester = await reduxTester<RootReducerType>({ preloadedState }) .givenRootReducer(getRootReducer()) .whenAsyncActionIsDispatched(openOptions(toKeyedVariableIdentifier(variable), undefined)); tester.thenDispatchedActionsShouldEqual(toKeyedAction('key', showOptions(variable))); }); }); describe('when openOptions is dispatched and picker.id is not the same as variable.id', () => { it('then correct actions are dispatched', async () => { const variableInPickerState = queryBuilder() .withId('query1') .withRootStateKey('key') .withName('query1') .withMulti() .withCurrent(['A', 'C']) .withOptions('A', 'B', 'C') .build(); const variable = queryBuilder() .withId('query0') .withRootStateKey('key') .withName('query0') .withMulti() .withCurrent(['A']) .withOptions('A', 'B', 'C') .build(); const preloadedState = getPreloadedState('key', { variables: { [variable.id]: { ...variable }, [variableInPickerState.id]: { ...variableInPickerState }, }, optionsPicker: { ...initialOptionPickerState, id: variableInPickerState.id }, }); const tester = await reduxTester<RootReducerType>({ preloadedState }) .givenRootReducer(getRootReducer()) .whenAsyncActionIsDispatched(openOptions(toKeyedVariableIdentifier(variable), undefined)); tester.thenDispatchedActionsShouldEqual( toKeyedAction('key', setCurrentVariableValue({ type: 'query', id: 'query1', data: { option: undefined } })), toKeyedAction( 'key', changeVariableProp({ type: 'query', id: 'query1', data: { propName: 'queryValue', propValue: '' } }) ), toKeyedAction('key', hideOptions()), toKeyedAction('key', showOptions(variable)) ); }); }); describe('when commitChangesToVariable is dispatched with no changes', () => { it('then correct actions are dispatched', async () => { const options = [createOption('A', 'A', true), createOption('B'), createOption('C')]; const variable = createMultiVariable({ options, current: createOption(['A'], ['A'], true), includeAll: false }); const tester = await reduxTester<RootReducerType>() .givenRootReducer(getRootReducer()) .whenActionIsDispatched( toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable }))) ) .whenActionIsDispatched(toKeyedAction('key', showOptions(variable))) .whenAsyncActionIsDispatched(commitChangesToVariable('key'), true); const option = { ...createOption(['A']), selected: true, value: ['A'], }; tester.thenDispatchedActionsShouldEqual( toKeyedAction('key', setCurrentVariableValue(toVariablePayload(variable, { option }))), toKeyedAction( 'key', changeVariableProp(toVariablePayload(variable, { propName: 'queryValue', propValue: '' })) ), toKeyedAction('key', hideOptions()) ); }); }); describe('when commitChangesToVariable is dispatched with changes', () => { it('then correct actions are dispatched', async () => { const options = [createOption('A', 'A', true), createOption('B'), createOption('C')]; const variable = createMultiVariable({ options, current: createOption(['A'], ['A'], true), includeAll: false }); const clearOthers = false; const tester = await reduxTester<RootReducerType>() .givenRootReducer(getRootReducer()) .whenActionIsDispatched( toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable }))) ) .whenActionIsDispatched(toKeyedAction('key', showOptions(variable))) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveDown, clearOthers)) .whenActionIsDispatched(toggleOptionByHighlight('key', clearOthers)) .whenAsyncActionIsDispatched(commitChangesToVariable('key'), true); const option = { ...createOption([]), selected: true, value: [], }; tester.thenDispatchedActionsShouldEqual( toKeyedAction('key', setCurrentVariableValue(toVariablePayload(variable, { option }))), toKeyedAction( 'key', changeVariableProp(toVariablePayload(variable, { propName: 'queryValue', propValue: '' })) ), toKeyedAction('key', hideOptions()), toKeyedAction('key', setCurrentVariableValue(toVariablePayload(variable, { option }))) ); expect(locationService.partial).toHaveBeenLastCalledWith({ 'var-Constant': [] }); }); }); describe('when commitChangesToVariable is dispatched with changes and list of options is filtered', () => { it('then correct actions are dispatched', async () => { const options = [createOption('A', 'A', true), createOption('B'), createOption('C')]; const variable = createMultiVariable({ options, current: createOption(['A'], ['A'], true), includeAll: false }); const clearOthers = false; const tester = await reduxTester<RootReducerType>() .givenRootReducer(getRootReducer()) .whenActionIsDispatched( toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable }))) ) .whenActionIsDispatched(toKeyedAction('key', showOptions(variable))) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveDown, clearOthers)) .whenActionIsDispatched(toggleOptionByHighlight('key', clearOthers)) .whenActionIsDispatched(filterOrSearchOptions(toKeyedVariableIdentifier(variable), 'C')) .whenAsyncActionIsDispatched(commitChangesToVariable('key'), true); const option = { ...createOption([]), selected: true, value: [], }; tester.thenDispatchedActionsShouldEqual( toKeyedAction('key', setCurrentVariableValue(toVariablePayload(variable, { option }))), toKeyedAction( 'key', changeVariableProp(toVariablePayload(variable, { propName: 'queryValue', propValue: 'C' })) ), toKeyedAction('key', hideOptions()), toKeyedAction('key', setCurrentVariableValue(toVariablePayload(variable, { option }))) ); expect(locationService.partial).toHaveBeenLastCalledWith({ 'var-Constant': [] }); }); }); describe('when toggleOptionByHighlight is dispatched with changes', () => { it('then correct actions are dispatched', async () => { const options = [createOption('A'), createOption('B'), createOption('C')]; const variable = createMultiVariable({ options, current: createOption(['A'], ['A'], true), includeAll: false }); const clearOthers = false; const tester = await reduxTester<RootReducerType>() .givenRootReducer(getRootReducer()) .whenActionIsDispatched( toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable }))) ) .whenActionIsDispatched(toKeyedAction('key', showOptions(variable))) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveDown, clearOthers)) .whenActionIsDispatched(toggleOptionByHighlight('key', clearOthers), true); const option = createOption('A'); tester.thenDispatchedActionsShouldEqual( toKeyedAction('key', toggleOption({ option, forceSelect: false, clearOthers })) ); }); }); describe('when toggleOptionByHighlight is dispatched with changes selected from a filtered options list', () => { it('then correct actions are dispatched', async () => { const options = [createOption('A'), createOption('B'), createOption('BC'), createOption('BD')]; const variable = createMultiVariable({ options, current: createOption(['A'], ['A'], true), includeAll: false }); const clearOthers = false; const tester = await reduxTester<RootReducerType>() .givenRootReducer(getRootReducer()) .whenActionIsDispatched( toKeyedAction('key', addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable }))) ) .whenActionIsDispatched(toKeyedAction('key', showOptions(variable))) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveDown, clearOthers)) .whenActionIsDispatched(toggleOptionByHighlight('key', clearOthers), true) .whenActionIsDispatched(filterOrSearchOptions(toKeyedVariableIdentifier(variable), 'B')) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveDown, clearOthers)) .whenActionIsDispatched(navigateOptions('key', NavigationKey.moveDown, clearOthers)) .whenActionIsDispatched(toggleOptionByHighlight('key', clearOthers)); const optionA = createOption('A'); const optionBD = createOption('BD'); tester.thenDispatchedActionsShouldEqual( toKeyedAction('key', toggleOption({ option: optionA, forceSelect: false, clearOthers })), toKeyedAction('key', updateSearchQuery('B')), toKeyedAction('key', updateOptionsAndFilter(variable.options)), toKeyedAction('key', moveOptionsHighlight(1)), toKeyedAction('key', moveOptionsHighlight(1)), toKeyedAction('key', toggleOption({ option: optionBD, forceSelect: false, clearOthers })) ); }); }); }); function createMultiVariable(extend?: Partial<QueryVariableModel>): QueryVariableModel { return createVariable({ multi: true, includeAll: true, ...(extend ?? {}), }); } function createVariable(extend?: Partial<QueryVariableModel>): QueryVariableModel { return { ...initialVariableModelState, type: 'query', id: '0', rootStateKey: 'key', index: 0, current: createOption([]), options: [], query: 'options-query', name: 'Constant', datasource: { uid: 'datasource' }, definition: '', sort: VariableSort.alphabeticalAsc, refresh: VariableRefresh.never, regex: '', multi: false, includeAll: false, ...(extend ?? {}), }; } function createOption(text: string | string[], value?: string | string[], selected?: boolean) { const metric = createMetric(text); return { ...metric, value: value ?? metric.value, selected: selected ?? false, }; } function createMetric(value: string | string[]) { return { value: value, text: value, }; }
Submit
FILE
FOLDER
INFO
Name
Size
Permission
Action
OptionPicker.test.tsx
4868 bytes
0644
OptionsPicker.tsx
6399 bytes
0644
actions.test.ts
25637 bytes
0644
actions.ts
6894 bytes
0644
reducer.test.ts
33460 bytes
0644
reducer.ts
10763 bytes
0644
N4ST4R_ID | Naxtarrr