1import { getRootReducer, RootReducerType } from './helpers'; 2import { variableAdapters } from '../adapters'; 3import { createQueryVariableAdapter } from '../query/adapter'; 4import { createConstantVariableAdapter } from '../constant/adapter'; 5import { reduxTester } from '../../../../test/core/redux/reduxTester'; 6import { 7 addVariable, 8 changeVariableProp, 9 setCurrentVariableValue, 10 variableStateCompleted, 11 variableStateFetching, 12 variableStateNotStarted, 13} from './sharedReducer'; 14import { toVariablePayload } from './types'; 15import { adHocBuilder, constantBuilder, datasourceBuilder, queryBuilder } from '../shared/testing/builders'; 16import { cleanEditorState, initialVariableEditorState } from '../editor/reducer'; 17import { 18 TransactionStatus, 19 variablesClearTransaction, 20 variablesCompleteTransaction, 21 variablesInitTransaction, 22} from './transactionReducer'; 23import { cleanPickerState, initialState } from '../pickers/OptionsPicker/reducer'; 24import { cleanVariables } from './variablesReducer'; 25import { createAdHocVariableAdapter } from '../adhoc/adapter'; 26import { createDataSourceVariableAdapter } from '../datasource/adapter'; 27import { DataSourceRef, LoadingState } from '@grafana/data/src'; 28import { setDataSourceSrv } from '@grafana/runtime/src'; 29import { VariableModel } from '../types'; 30import { toAsyncOfResult } from '../../query/state/DashboardQueryRunner/testHelpers'; 31import { setVariableQueryRunner } from '../query/VariableQueryRunner'; 32import { createDataSourceOptions } from '../datasource/reducer'; 33import { initVariablesTransaction } from './actions'; 34 35variableAdapters.setInit(() => [ 36 createQueryVariableAdapter(), 37 createConstantVariableAdapter(), 38 createAdHocVariableAdapter(), 39 createDataSourceVariableAdapter(), 40]); 41 42function getTestContext(variables?: VariableModel[]) { 43 const uid = 'uid'; 44 const constant = constantBuilder().withId('constant').withName('constant').build(); 45 const templating = { list: variables ?? [constant] }; 46 const getInstanceSettingsMock = jest.fn().mockReturnValue(undefined); 47 setDataSourceSrv({ 48 get: jest.fn().mockResolvedValue({}), 49 getList: jest.fn().mockReturnValue([]), 50 getInstanceSettings: getInstanceSettingsMock, 51 }); 52 const variableQueryRunner: any = { 53 cancelRequest: jest.fn(), 54 queueRequest: jest.fn(), 55 getResponse: () => toAsyncOfResult({ state: LoadingState.Done, identifier: { type: 'query', id: 'query' } }), 56 destroy: jest.fn(), 57 }; 58 setVariableQueryRunner(variableQueryRunner); 59 60 const dashboard: any = { title: 'Some dash', uid, templating }; 61 62 return { constant, getInstanceSettingsMock, templating, uid, dashboard }; 63} 64 65describe('initVariablesTransaction', () => { 66 describe('when called and the previous dashboard has completed', () => { 67 it('then correct actions are dispatched', async () => { 68 const { constant, uid, dashboard } = getTestContext(); 69 const tester = await reduxTester<RootReducerType>() 70 .givenRootReducer(getRootReducer()) 71 .whenAsyncActionIsDispatched(initVariablesTransaction(uid, dashboard)); 72 73 tester.thenDispatchedActionsPredicateShouldEqual((dispatchedActions) => { 74 expect(dispatchedActions[0]).toEqual(variablesInitTransaction({ uid })); 75 expect(dispatchedActions[1].type).toEqual(addVariable.type); 76 expect(dispatchedActions[1].payload.id).toEqual('__dashboard'); 77 expect(dispatchedActions[2].type).toEqual(addVariable.type); 78 expect(dispatchedActions[2].payload.id).toEqual('__org'); 79 expect(dispatchedActions[3].type).toEqual(addVariable.type); 80 expect(dispatchedActions[3].payload.id).toEqual('__user'); 81 expect(dispatchedActions[4]).toEqual( 82 addVariable(toVariablePayload(constant, { global: false, index: 0, model: constant })) 83 ); 84 expect(dispatchedActions[5]).toEqual(variableStateNotStarted(toVariablePayload(constant))); 85 expect(dispatchedActions[6]).toEqual(variableStateCompleted(toVariablePayload(constant))); 86 87 expect(dispatchedActions[7]).toEqual(variablesCompleteTransaction({ uid })); 88 return dispatchedActions.length === 8; 89 }); 90 }); 91 92 describe('and there are variables that have data source that need to be migrated', () => { 93 it('then correct actions are dispatched', async () => { 94 const legacyDs = ('${ds}' as unknown) as DataSourceRef; 95 const ds = datasourceBuilder().withId('ds').withName('ds').withQuery('prom').build(); 96 const query = queryBuilder().withId('query').withName('query').withDatasource(legacyDs).build(); 97 const adhoc = adHocBuilder().withId('adhoc').withName('adhoc').withDatasource(legacyDs).build(); 98 const { uid, dashboard } = getTestContext([ds, query, adhoc]); 99 const tester = await reduxTester<RootReducerType>() 100 .givenRootReducer(getRootReducer()) 101 .whenAsyncActionIsDispatched(initVariablesTransaction(uid, dashboard)); 102 103 tester.thenDispatchedActionsPredicateShouldEqual((dispatchedActions) => { 104 expect(dispatchedActions[0]).toEqual(variablesInitTransaction({ uid })); 105 expect(dispatchedActions[1].type).toEqual(addVariable.type); 106 expect(dispatchedActions[1].payload.id).toEqual('__dashboard'); 107 expect(dispatchedActions[2].type).toEqual(addVariable.type); 108 expect(dispatchedActions[2].payload.id).toEqual('__org'); 109 expect(dispatchedActions[3].type).toEqual(addVariable.type); 110 expect(dispatchedActions[3].payload.id).toEqual('__user'); 111 expect(dispatchedActions[4]).toEqual( 112 addVariable(toVariablePayload(ds, { global: false, index: 0, model: ds })) 113 ); 114 expect(dispatchedActions[5]).toEqual( 115 addVariable(toVariablePayload(query, { global: false, index: 1, model: query })) 116 ); 117 expect(dispatchedActions[6]).toEqual( 118 addVariable(toVariablePayload(adhoc, { global: false, index: 2, model: adhoc })) 119 ); 120 expect(dispatchedActions[7]).toEqual(variableStateNotStarted(toVariablePayload(ds))); 121 expect(dispatchedActions[8]).toEqual(variableStateNotStarted(toVariablePayload(query))); 122 expect(dispatchedActions[9]).toEqual(variableStateNotStarted(toVariablePayload(adhoc))); 123 expect(dispatchedActions[10]).toEqual( 124 changeVariableProp(toVariablePayload(query, { propName: 'datasource', propValue: { uid: '${ds}' } })) 125 ); 126 expect(dispatchedActions[11]).toEqual( 127 changeVariableProp(toVariablePayload(adhoc, { propName: 'datasource', propValue: { uid: '${ds}' } })) 128 ); 129 expect(dispatchedActions[12]).toEqual(variableStateFetching(toVariablePayload(ds))); 130 expect(dispatchedActions[13]).toEqual(variableStateCompleted(toVariablePayload(adhoc))); 131 expect(dispatchedActions[14]).toEqual( 132 createDataSourceOptions(toVariablePayload(ds, { sources: [], regex: undefined })) 133 ); 134 expect(dispatchedActions[15]).toEqual( 135 setCurrentVariableValue( 136 toVariablePayload(ds, { option: { selected: false, text: 'No data sources found', value: '' } }) 137 ) 138 ); 139 expect(dispatchedActions[16]).toEqual(variableStateCompleted(toVariablePayload(ds))); 140 expect(dispatchedActions[17]).toEqual(variableStateFetching(toVariablePayload(query))); 141 expect(dispatchedActions[18]).toEqual(variableStateCompleted(toVariablePayload(query))); 142 expect(dispatchedActions[19]).toEqual(variablesCompleteTransaction({ uid })); 143 144 return dispatchedActions.length === 20; 145 }); 146 }); 147 }); 148 }); 149 150 describe('when called and the previous dashboard is still processing variables', () => { 151 it('then correct actions are dispatched', async () => { 152 const { constant, uid, dashboard } = getTestContext(); 153 const transactionState = { uid: 'previous-uid', status: TransactionStatus.Fetching }; 154 155 const tester = await reduxTester<RootReducerType>({ 156 preloadedState: ({ 157 templating: { 158 transaction: transactionState, 159 variables: {}, 160 optionsPicker: { ...initialState }, 161 editor: { ...initialVariableEditorState }, 162 }, 163 } as unknown) as RootReducerType, 164 }) 165 .givenRootReducer(getRootReducer()) 166 .whenAsyncActionIsDispatched(initVariablesTransaction(uid, dashboard)); 167 168 tester.thenDispatchedActionsPredicateShouldEqual((dispatchedActions) => { 169 expect(dispatchedActions[0]).toEqual(cleanVariables()); 170 expect(dispatchedActions[1]).toEqual(cleanEditorState()); 171 expect(dispatchedActions[2]).toEqual(cleanPickerState()); 172 expect(dispatchedActions[3]).toEqual(variablesClearTransaction()); 173 expect(dispatchedActions[4]).toEqual(variablesInitTransaction({ uid })); 174 expect(dispatchedActions[5].type).toEqual(addVariable.type); 175 expect(dispatchedActions[5].payload.id).toEqual('__dashboard'); 176 expect(dispatchedActions[6].type).toEqual(addVariable.type); 177 expect(dispatchedActions[6].payload.id).toEqual('__org'); 178 expect(dispatchedActions[7].type).toEqual(addVariable.type); 179 expect(dispatchedActions[7].payload.id).toEqual('__user'); 180 expect(dispatchedActions[8]).toEqual( 181 addVariable(toVariablePayload(constant, { global: false, index: 0, model: constant })) 182 ); 183 expect(dispatchedActions[9]).toEqual(variableStateNotStarted(toVariablePayload(constant))); 184 expect(dispatchedActions[10]).toEqual(variableStateCompleted(toVariablePayload(constant))); 185 expect(dispatchedActions[11]).toEqual(variablesCompleteTransaction({ uid })); 186 return dispatchedActions.length === 12; 187 }); 188 }); 189 }); 190}); 191