diff --git a/packages/grafana-runtime/src/config.ts b/packages/grafana-runtime/src/config.ts
index dde91c87a1f..dc44658ef6c 100644
--- a/packages/grafana-runtime/src/config.ts
+++ b/packages/grafana-runtime/src/config.ts
@@ -71,7 +71,7 @@ export class GrafanaBootConfig {
expressions: false,
newEdit: false,
meta: false,
- newVariables: false,
+ newVariables: true,
tracingIntegration: false,
};
licenseInfo: LicenseInfo = {} as LicenseInfo;
diff --git a/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap b/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap
index 408c3d92222..3b45648dca4 100644
--- a/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap
+++ b/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap
@@ -29,6 +29,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
},
},
"getVariables": [Function],
+ "getVariablesFromState": [Function],
"gnetId": null,
"graphTooltip": 0,
"id": null,
@@ -118,7 +119,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
-
- ({}));
@@ -31,7 +32,7 @@ describe('given dashboard with panel repeat', () => {
],
},
};
- dashboard = new DashboardModel(dashboardJSON);
+ dashboard = getDashboardModel(dashboardJSON);
dashboard.processRepeats();
});
@@ -59,7 +60,7 @@ describe('given dashboard with panel repeat in horizontal direction', () => {
let dashboard: any;
beforeEach(() => {
- dashboard = new DashboardModel({
+ const dashboardJSON = {
panels: [
{
id: 2,
@@ -85,7 +86,8 @@ describe('given dashboard with panel repeat in horizontal direction', () => {
},
],
},
- });
+ };
+ dashboard = getDashboardModel(dashboardJSON);
dashboard.processRepeats();
});
@@ -191,7 +193,7 @@ describe('given dashboard with panel repeat in vertical direction', () => {
let dashboard: any;
beforeEach(() => {
- dashboard = new DashboardModel({
+ const dashboardJSON = {
panels: [
{ id: 1, type: 'row', gridPos: { x: 0, y: 0, h: 1, w: 24 } },
{ id: 2, repeat: 'apps', repeatDirection: 'v', gridPos: { x: 5, y: 1, h: 2, w: 8 } },
@@ -214,7 +216,8 @@ describe('given dashboard with panel repeat in vertical direction', () => {
},
],
},
- });
+ };
+ dashboard = getDashboardModel(dashboardJSON);
dashboard.processRepeats();
});
@@ -230,7 +233,7 @@ describe('given dashboard with panel repeat in vertical direction', () => {
});
describe('given dashboard with row repeat and panel repeat in horizontal direction', () => {
- let dashboard: any, dashboardJSON;
+ let dashboard: any, dashboardJSON: any;
beforeEach(() => {
dashboardJSON = {
@@ -269,7 +272,7 @@ describe('given dashboard with row repeat and panel repeat in horizontal directi
],
},
};
- dashboard = new DashboardModel(dashboardJSON);
+ dashboard = getDashboardModel(dashboardJSON);
dashboard.processRepeats(false);
});
@@ -348,7 +351,7 @@ describe('given dashboard with row repeat', () => {
],
},
};
- dashboard = new DashboardModel(dashboardJSON);
+ dashboard = getDashboardModel(dashboardJSON);
dashboard.processRepeats();
});
@@ -359,7 +362,7 @@ describe('given dashboard with row repeat', () => {
it('should set scopedVars for each panel', () => {
dashboardJSON.templating.list[0].options[2].selected = true;
- dashboard = new DashboardModel(dashboardJSON);
+ dashboard = getDashboardModel(dashboardJSON);
dashboard.processRepeats();
expect(dashboard.panels[1].scopedVars).toMatchObject({
@@ -399,7 +402,7 @@ describe('given dashboard with row repeat', () => {
{ id: 4, type: 'row', gridPos: { x: 0, y: 1, h: 1, w: 24 } },
{ id: 5, type: 'graph', gridPos: { x: 0, y: 2, h: 1, w: 12 } },
];
- dashboard = new DashboardModel(dashboardJSON);
+ dashboard = getDashboardModel(dashboardJSON);
dashboard.processRepeats();
const panelTypes = _.map(dashboard.panels, 'type');
@@ -441,7 +444,7 @@ describe('given dashboard with row repeat', () => {
{ text: 'backend03', value: 'backend03', selected: false },
],
});
- dashboard = new DashboardModel(dashboardJSON);
+ dashboard = getDashboardModel(dashboardJSON);
dashboard.processRepeats();
const panelTypes = _.map(dashboard.panels, 'type');
@@ -488,7 +491,7 @@ describe('given dashboard with row repeat', () => {
{ id: 4, type: 'row', gridPos: { x: 0, y: 1, h: 1, w: 24 } },
{ id: 5, type: 'graph', gridPos: { x: 0, y: 2, h: 1, w: 12 } },
];
- dashboard = new DashboardModel(dashboardJSON);
+ dashboard = getDashboardModel(dashboardJSON);
dashboard.processRepeats();
const panelIds = _.flattenDeep(
@@ -511,7 +514,7 @@ describe('given dashboard with row repeat', () => {
{ id: 3, type: 'graph', gridPos: { x: 6, y: 1, h: 4, w: 12 } },
{ id: 4, type: 'graph', gridPos: { x: 0, y: 5, h: 2, w: 12 } },
];
- dashboard = new DashboardModel(dashboardJSON);
+ dashboard = getDashboardModel(dashboardJSON);
dashboard.processRepeats();
const panelTypes = _.map(dashboard.panels, 'type');
@@ -564,7 +567,7 @@ describe('given dashboard with row and panel repeat', () => {
],
},
};
- dashboard = new DashboardModel(dashboardJSON);
+ dashboard = getDashboardModel(dashboardJSON);
dashboard.processRepeats();
});
@@ -592,7 +595,7 @@ describe('given dashboard with row and panel repeat', () => {
},
{ id: 12, type: 'graph', repeatPanelId: 2, repeatIteration: 101, gridPos: { x: 0, y: 3, h: 1, w: 6 } },
];
- dashboard = new DashboardModel(dashboardJSON);
+ dashboard = getDashboardModel(dashboardJSON);
dashboard.processRepeats();
const panelTypes = _.map(dashboard.panels, 'type');
@@ -600,7 +603,7 @@ describe('given dashboard with row and panel repeat', () => {
});
it('should set scopedVars for each row', () => {
- dashboard = new DashboardModel(dashboardJSON);
+ dashboard = getDashboardModel(dashboardJSON);
dashboard.processRepeats();
expect(dashboard.panels[0].scopedVars).toMatchObject({
@@ -612,7 +615,7 @@ describe('given dashboard with row and panel repeat', () => {
});
it('should set panel-repeat variable for each panel', () => {
- dashboard = new DashboardModel(dashboardJSON);
+ dashboard = getDashboardModel(dashboardJSON);
dashboard.processRepeats();
expect(dashboard.panels[1].scopedVars).toMatchObject({
@@ -631,7 +634,7 @@ describe('given dashboard with row and panel repeat', () => {
});
it('should set row-repeat variable for each panel', () => {
- dashboard = new DashboardModel(dashboardJSON);
+ dashboard = getDashboardModel(dashboardJSON);
dashboard.processRepeats();
expect(dashboard.panels[1].scopedVars).toMatchObject({
@@ -650,7 +653,7 @@ describe('given dashboard with row and panel repeat', () => {
});
it('should repeat panels when row is expanding', () => {
- dashboard = new DashboardModel(dashboardJSON);
+ dashboard = getDashboardModel(dashboardJSON);
dashboard.processRepeats();
expect(dashboard.panels.length).toBe(6);
diff --git a/public/app/features/dashboard/state/DashboardModel.test.ts b/public/app/features/dashboard/state/DashboardModel.test.ts
index 042c99923ab..c647d8cb350 100644
--- a/public/app/features/dashboard/state/DashboardModel.test.ts
+++ b/public/app/features/dashboard/state/DashboardModel.test.ts
@@ -1,8 +1,13 @@
import _ from 'lodash';
import { DashboardModel } from '../state/DashboardModel';
import { PanelModel } from '../state/PanelModel';
+import { getDashboardModel } from '../../../../test/helpers/getDashboardModel';
+import { variableAdapters } from '../../variables/adapters';
+import { createAdHocVariableAdapter } from '../../variables/adhoc/adapter';
+import { createQueryVariableAdapter } from '../../variables/query/adapter';
jest.mock('app/core/services/context_srv', () => ({}));
+variableAdapters.setInit(() => [createQueryVariableAdapter(), createAdHocVariableAdapter()]);
describe('DashboardModel', () => {
describe('when creating new dashboard model defaults only', () => {
@@ -498,7 +503,7 @@ describe('DashboardModel', () => {
let model: DashboardModel;
beforeEach(() => {
- model = new DashboardModel({
+ const json = {
templating: {
list: [
{
@@ -512,7 +517,8 @@ describe('DashboardModel', () => {
},
],
},
- });
+ };
+ model = getDashboardModel(json);
expect(model.hasVariableValuesChanged()).toBeFalsy();
});
@@ -562,7 +568,7 @@ describe('DashboardModel', () => {
let model: DashboardModel;
beforeEach(() => {
- model = new DashboardModel({
+ const json = {
templating: {
list: [
{
@@ -578,7 +584,8 @@ describe('DashboardModel', () => {
},
],
},
- });
+ };
+ model = getDashboardModel(json);
expect(model.hasVariableValuesChanged()).toBeFalsy();
});
diff --git a/public/app/features/dashboard/state/DashboardModel.ts b/public/app/features/dashboard/state/DashboardModel.ts
index 6af16e6ee40..65703152229 100644
--- a/public/app/features/dashboard/state/DashboardModel.ts
+++ b/public/app/features/dashboard/state/DashboardModel.ts
@@ -14,7 +14,7 @@ import { AppEvent, dateTime, DateTimeInput, isDateTime, PanelEvents, TimeRange,
import { UrlQueryValue } from '@grafana/runtime';
import { CoreEvents, DashboardMeta, KIOSK_MODE_TV } from 'app/types';
import { getConfig } from '../../../core/config';
-import { getVariables } from 'app/features/variables/state/selectors';
+import { GetVariables, getVariables } from 'app/features/variables/state/selectors';
import { variableAdapters } from 'app/features/variables/adapters';
import { onTimeRangeUpdated } from 'app/features/variables/state/actions';
import { dispatch } from '../../../store/store';
@@ -69,9 +69,10 @@ export class DashboardModel {
originalTime: true,
originalTemplating: true,
panelInEdit: true,
+ getVariablesFromState: true,
};
- constructor(data: any, meta?: DashboardMeta) {
+ constructor(data: any, meta?: DashboardMeta, private getVariablesFromState: GetVariables = getVariables) {
if (!data) {
data = {};
}
@@ -238,7 +239,7 @@ export class DashboardModel {
defaults: { saveTimerange: boolean; saveVariables: boolean } & CloneOptions
) {
const originalVariables = this.originalTemplating;
- const currentVariables = getVariables();
+ const currentVariables = this.getVariablesFromState();
copy.templating = {
list: currentVariables.map(variable => variableAdapters.get(variable.type).getSaveModel(variable)),
@@ -940,12 +941,12 @@ export class DashboardModel {
return;
}
- this.originalTemplating = this.cloneVariablesFrom(getVariables());
+ this.originalTemplating = this.cloneVariablesFrom(this.getVariablesFromState());
}
hasVariableValuesChanged() {
if (getConfig().featureToggles.newVariables) {
- return this.hasVariablesChanged(this.originalTemplating, getVariables());
+ return this.hasVariablesChanged(this.originalTemplating, this.getVariablesFromState());
}
return this.hasVariablesChanged(this.originalTemplating, this.templating.list);
@@ -1019,7 +1020,7 @@ export class DashboardModel {
getVariables = () => {
if (getConfig().featureToggles.newVariables) {
- return getVariables();
+ return this.getVariablesFromState();
}
return this.templating.list;
};
@@ -1029,7 +1030,7 @@ export class DashboardModel {
return _.find(this.templating.list, { name: panel.repeat } as any);
}
- return getVariables().find(variable => variable.name === panel.repeat);
+ return this.getVariablesFromState().find(variable => variable.name === panel.repeat);
}
private isSnapshotTruthy() {
@@ -1038,7 +1039,7 @@ export class DashboardModel {
private hasVariables() {
if (getConfig().featureToggles.newVariables) {
- return getVariables().length > 0;
+ return this.getVariablesFromState().length > 0;
}
return this.templating.list.length > 0;
}
diff --git a/public/app/features/dashboard/state/initDashboard.test.ts b/public/app/features/dashboard/state/initDashboard.test.ts
index adb0f395ec4..6cce0b21662 100644
--- a/public/app/features/dashboard/state/initDashboard.test.ts
+++ b/public/app/features/dashboard/state/initDashboard.test.ts
@@ -7,9 +7,30 @@ import { dashboardInitCompleted, dashboardInitFetching, dashboardInitServices }
import { updateLocation } from '../../../core/actions';
import { setEchoSrv } from '@grafana/runtime';
import { Echo } from '../../../core/services/echo/Echo';
+import { getConfig } from 'app/core/config';
+import { variableAdapters } from 'app/features/variables/adapters';
+import { createConstantVariableAdapter } from 'app/features/variables/constant/adapter';
+import { addVariable } from 'app/features/variables/state/sharedReducer';
+import { constantBuilder } from 'app/features/variables/shared/testing/builders';
jest.mock('app/core/services/backend_srv');
+jest.mock('app/features/dashboard/services/TimeSrv', () => {
+ const original = jest.requireActual('app/features/dashboard/services/TimeSrv');
+ return {
+ ...original,
+ getTimeSrv: () => ({
+ ...original.getTimeSrv(),
+ timeRange: jest.fn().mockReturnValue(undefined),
+ }),
+ };
+});
+jest.mock('app/core/services/context_srv', () => ({
+ contextSrv: {
+ user: { orgId: 1, orgName: 'TestOrg' },
+ },
+}));
+variableAdapters.register(createConstantVariableAdapter());
const mockStore = configureMockStore([thunk]);
interface ScenarioContext {
@@ -61,6 +82,9 @@ function describeInitScenario(description: string, scenarioFn: ScenarioFn) {
],
},
],
+ templating: {
+ list: [constantBuilder().build()],
+ },
},
})),
};
@@ -118,6 +142,9 @@ function describeInitScenario(description: string, scenarioFn: ScenarioFn) {
queries: [],
},
},
+ templating: {
+ variables: {},
+ },
},
setup: (fn: () => void) => {
setupFn = fn;
@@ -166,11 +193,17 @@ describeInitScenario('Initializing new dashboard', ctx => {
it('Should initialize services', () => {
expect(ctx.timeSrv.init).toBeCalled();
expect(ctx.annotationsSrv.init).toBeCalled();
- expect(ctx.variableSrv.init).toBeCalled();
expect(ctx.unsavedChangesSrv.init).toBeCalled();
expect(ctx.keybindingSrv.setupDashboardBindings).toBeCalled();
expect(ctx.dashboardSrv.setCurrent).toBeCalled();
});
+
+ it('Should initialize variableSrv if newVariables is disabled', () => {
+ if (getConfig().featureToggles.newVariables) {
+ return expect.assertions(0);
+ }
+ expect(ctx.variableSrv.init).toBeCalled();
+ });
});
describeInitScenario('Initializing home dashboard', ctx => {
@@ -224,16 +257,30 @@ describeInitScenario('Initializing existing dashboard', ctx => {
});
it('Should send action dashboardInitCompleted', () => {
- expect(ctx.actions[3].type).toBe(dashboardInitCompleted.type);
- expect(ctx.actions[3].payload.title).toBe('My cool dashboard');
+ const index = getConfig().featureToggles.newVariables ? 4 : 3;
+ expect(ctx.actions[index].type).toBe(dashboardInitCompleted.type);
+ expect(ctx.actions[index].payload.title).toBe('My cool dashboard');
});
it('Should initialize services', () => {
expect(ctx.timeSrv.init).toBeCalled();
expect(ctx.annotationsSrv.init).toBeCalled();
- expect(ctx.variableSrv.init).toBeCalled();
expect(ctx.unsavedChangesSrv.init).toBeCalled();
expect(ctx.keybindingSrv.setupDashboardBindings).toBeCalled();
expect(ctx.dashboardSrv.setCurrent).toBeCalled();
});
+
+ it('Should initialize variableSrv if newVariables is disabled', () => {
+ if (getConfig().featureToggles.newVariables) {
+ return expect.assertions(0);
+ }
+ expect(ctx.variableSrv.init).toBeCalled();
+ });
+
+ it('Should initialize redux variables if newVariables is enabled', () => {
+ if (!getConfig().featureToggles.newVariables) {
+ return expect.assertions(0);
+ }
+ expect(ctx.actions[3].type).toBe(addVariable.type);
+ });
});
diff --git a/public/app/features/dashboard/state/initDashboard.ts b/public/app/features/dashboard/state/initDashboard.ts
index 9909002795e..9dc010a2cca 100644
--- a/public/app/features/dashboard/state/initDashboard.ts
+++ b/public/app/features/dashboard/state/initDashboard.ts
@@ -23,7 +23,7 @@ import { DashboardDTO, DashboardRouteInfo, StoreState, ThunkDispatch, ThunkResul
import { DashboardModel } from './DashboardModel';
import { DataQuery } from '@grafana/data';
import { getConfig } from '../../../core/config';
-import { initDashboardTemplating, processVariables } from '../../variables/state/actions';
+import { initDashboardTemplating, processVariables, completeDashboardTemplating } from '../../variables/state/actions';
import { emitDashboardViewEvent } from './analyticsProcessor';
export interface InitDashboardArgs {
@@ -185,8 +185,9 @@ export function initDashboard(args: InitDashboardArgs): ThunkResult {
await variableSrv.init(dashboard);
}
if (getConfig().featureToggles.newVariables) {
- await dispatch(initDashboardTemplating(dashboard.templating.list));
+ dispatch(initDashboardTemplating(dashboard.templating.list));
await dispatch(processVariables());
+ dispatch(completeDashboardTemplating(dashboard));
}
} catch (err) {
dispatch(notifyApp(createErrorNotification('Templating init failed', err)));
diff --git a/public/app/features/templating/specs/template_srv.test.ts b/public/app/features/templating/specs/template_srv.test.ts
index f3fb61873f6..635c97b5443 100644
--- a/public/app/features/templating/specs/template_srv.test.ts
+++ b/public/app/features/templating/specs/template_srv.test.ts
@@ -1,10 +1,14 @@
import { TemplateSrv } from '../template_srv';
+import { convertToStoreState } from 'test/helpers/convertToStoreState';
+import { getTemplateSrvDependencies } from '../../../../test/helpers/getTemplateSrvDependencies';
describe('templateSrv', () => {
let _templateSrv: any;
- function initTemplateSrv(variables: any) {
- _templateSrv = new TemplateSrv();
+ function initTemplateSrv(variables: any[]) {
+ const state = convertToStoreState(variables);
+
+ _templateSrv = new TemplateSrv(getTemplateSrvDependencies(state));
_templateSrv.init(variables);
}
diff --git a/public/app/features/templating/template_srv.ts b/public/app/features/templating/template_srv.ts
index 22db8b38ddd..7d6d7402604 100644
--- a/public/app/features/templating/template_srv.ts
+++ b/public/app/features/templating/template_srv.ts
@@ -16,6 +16,18 @@ interface FieldAccessorCache {
[key: string]: (obj: any) => any;
}
+export interface TemplateSrvDependencies {
+ getFilteredVariables: typeof getFilteredVariables;
+ getVariables: typeof getVariables;
+ getVariableWithName: typeof getVariableWithName;
+}
+
+const runtimeDependencies: TemplateSrvDependencies = {
+ getFilteredVariables,
+ getVariables,
+ getVariableWithName,
+};
+
export class TemplateSrv {
private _variables: any[];
private regex = variableRegex;
@@ -25,7 +37,7 @@ export class TemplateSrv {
private timeRange?: TimeRange | null = null;
private fieldAccessorCache: FieldAccessorCache = {};
- constructor() {
+ constructor(private dependencies: TemplateSrvDependencies = runtimeDependencies) {
this.builtIns['__interval'] = { text: '1s', value: '1s' };
this.builtIns['__interval_ms'] = { text: '100', value: '100' };
this._variables = [];
@@ -53,7 +65,7 @@ export class TemplateSrv {
getVariables(): VariableModel[] {
if (getConfig().featureToggles.newVariables) {
- return getVariables();
+ return this.dependencies.getVariables();
}
return this._variables;
@@ -419,7 +431,7 @@ export class TemplateSrv {
}
if (getConfig().featureToggles.newVariables && !this.index[name]) {
- return getVariableWithName(name);
+ return this.dependencies.getVariableWithName(name);
}
return this.index[name];
@@ -427,7 +439,7 @@ export class TemplateSrv {
private getAdHocVariables = (): any[] => {
if (getConfig().featureToggles.newVariables) {
- return getFilteredVariables(isAdHoc);
+ return this.dependencies.getFilteredVariables(isAdHoc);
}
if (Array.isArray(this._variables)) {
return this._variables.filter(isAdHoc);
diff --git a/public/app/features/variables/constant/actions.test.ts b/public/app/features/variables/constant/actions.test.ts
index 9df353157ce..75af63c31d3 100644
--- a/public/app/features/variables/constant/actions.test.ts
+++ b/public/app/features/variables/constant/actions.test.ts
@@ -3,12 +3,11 @@ import { createConstantVariableAdapter } from './adapter';
import { reduxTester } from '../../../../test/core/redux/reduxTester';
import { TemplatingState } from 'app/features/variables/state/reducers';
import { updateConstantVariableOptions } from './actions';
-import { getTemplatingRootReducer } from '../state/helpers';
+import { getRootReducer } from '../state/helpers';
import { ConstantVariableModel, VariableHide, VariableOption } from '../../templating/types';
import { toVariablePayload } from '../state/types';
import { createConstantOptionsFromQuery } from './reducer';
-import { setCurrentVariableValue } from '../state/sharedReducer';
-import { initDashboardTemplating } from '../state/actions';
+import { setCurrentVariableValue, addVariable } from '../state/sharedReducer';
describe('constant actions', () => {
variableAdapters.setInit(() => [createConstantVariableAdapter()]);
@@ -40,8 +39,8 @@ describe('constant actions', () => {
};
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(updateConstantVariableOptions(toVariablePayload(variable)), true);
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
diff --git a/public/app/features/variables/custom/actions.test.ts b/public/app/features/variables/custom/actions.test.ts
index 78ce61214e4..47363b4b9df 100644
--- a/public/app/features/variables/custom/actions.test.ts
+++ b/public/app/features/variables/custom/actions.test.ts
@@ -2,11 +2,10 @@ import { variableAdapters } from '../adapters';
import { updateCustomVariableOptions } from './actions';
import { createCustomVariableAdapter } from './adapter';
import { reduxTester } from '../../../../test/core/redux/reduxTester';
-import { getTemplatingRootReducer } from '../state/helpers';
+import { getRootReducer } from '../state/helpers';
import { CustomVariableModel, VariableHide, VariableOption } from '../../templating/types';
import { toVariablePayload } from '../state/types';
-import { setCurrentVariableValue } from '../state/sharedReducer';
-import { initDashboardTemplating } from '../state/actions';
+import { setCurrentVariableValue, addVariable } from '../state/sharedReducer';
import { TemplatingState } from '../state/reducers';
import { createCustomOptionsFromQuery } from './reducer';
@@ -53,8 +52,8 @@ describe('custom actions', () => {
};
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(updateCustomVariableOptions(toVariablePayload(variable)), true);
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
diff --git a/public/app/features/variables/datasource/actions.test.ts b/public/app/features/variables/datasource/actions.test.ts
index f7e68ec24f8..f652545aefe 100644
--- a/public/app/features/variables/datasource/actions.test.ts
+++ b/public/app/features/variables/datasource/actions.test.ts
@@ -1,7 +1,6 @@
import { reduxTester } from '../../../../test/core/redux/reduxTester';
import { TemplatingState } from '../state/reducers';
-import { getTemplatingRootReducer } from '../state/helpers';
-import { initDashboardTemplating } from '../state/actions';
+import { getRootReducer } from '../state/helpers';
import { toVariableIdentifier, toVariablePayload } from '../state/types';
import { variableAdapters } from '../adapters';
import { createDataSourceVariableAdapter } from './adapter';
@@ -13,7 +12,7 @@ import {
import { DataSourcePluginMeta, DataSourceSelectItem } from '@grafana/data';
import { getMockPlugin } from '../../plugins/__mocks__/pluginMocks';
import { createDataSourceOptions } from './reducer';
-import { setCurrentVariableValue } from '../state/sharedReducer';
+import { setCurrentVariableValue, addVariable } from '../state/sharedReducer';
import { changeVariableEditorExtended } from '../editor/reducer';
import { datasourceBuilder } from '../shared/testing/builders';
@@ -47,8 +46,10 @@ describe('data source actions', () => {
.build();
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([datasource]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(
+ addVariable(toVariablePayload(datasource, { global: false, index: 0, model: datasource }))
+ )
.whenAsyncActionIsDispatched(
updateDataSourceVariableOptions(toVariableIdentifier(datasource), dependencies),
true
@@ -98,8 +99,10 @@ describe('data source actions', () => {
.withRegEx('/.*(second-name).*/')
.build();
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([datasource]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(
+ addVariable(toVariablePayload(datasource, { global: false, index: 0, model: datasource }))
+ )
.whenAsyncActionIsDispatched(
updateDataSourceVariableOptions(toVariableIdentifier(datasource), dependencies),
true
@@ -156,7 +159,7 @@ describe('data source actions', () => {
const dependencies: DataSourceVariableActionDependencies = { getDatasourceSrv: getDatasourceSrvMock };
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
+ .givenRootReducer(getRootReducer())
.whenAsyncActionIsDispatched(initDataSourceVariableEditor(dependencies));
await tester.thenDispatchedActionsShouldEqual(
diff --git a/public/app/features/variables/interval/actions.test.ts b/public/app/features/variables/interval/actions.test.ts
index c47d391f213..356c980c33e 100644
--- a/public/app/features/variables/interval/actions.test.ts
+++ b/public/app/features/variables/interval/actions.test.ts
@@ -1,8 +1,7 @@
-import { getTemplatingRootReducer } from '../state/helpers';
+import { getRootReducer } from '../state/helpers';
import { reduxTester } from '../../../../test/core/redux/reduxTester';
import { TemplatingState } from '../state/reducers';
-import { initDashboardTemplating } from '../state/actions';
-import { toVariableIdentifier } from '../state/types';
+import { toVariableIdentifier, toVariablePayload } from '../state/types';
import {
updateAutoValue,
UpdateAutoValueDependencies,
@@ -10,7 +9,7 @@ import {
UpdateIntervalVariableOptionsDependencies,
} from './actions';
import { createIntervalOptions } from './reducer';
-import { setCurrentVariableValue } from '../state/sharedReducer';
+import { setCurrentVariableValue, addVariable } from '../state/sharedReducer';
import { variableAdapters } from '../adapters';
import { createIntervalVariableAdapter } from './adapter';
import { Emitter } from 'app/core/core';
@@ -31,8 +30,8 @@ describe('interval actions', () => {
.build();
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([interval]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(interval, { global: false, index: 0, model: interval })))
.whenAsyncActionIsDispatched(updateIntervalVariableOptions(toVariableIdentifier(interval)), true);
tester.thenDispatchedActionsShouldEqual(
@@ -74,8 +73,8 @@ describe('interval actions', () => {
const dependencies: UpdateIntervalVariableOptionsDependencies = { appEvents: appEventMock };
await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([interval]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(interval, { global: false, index: 0, model: interval })))
.whenAsyncActionIsDispatched(updateIntervalVariableOptions(toVariableIdentifier(interval), dependencies), true);
expect(appEventMock.emit).toHaveBeenCalledTimes(1);
@@ -119,8 +118,10 @@ describe('interval actions', () => {
};
await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([interval]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(
+ addVariable(toVariablePayload(interval, { global: false, index: 0, model: interval }))
+ )
.whenAsyncActionIsDispatched(updateAutoValue(toVariableIdentifier(interval), dependencies), true);
expect(dependencies.kbn.calculateInterval).toHaveBeenCalledTimes(0);
@@ -163,8 +164,10 @@ describe('interval actions', () => {
};
await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([interval]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(
+ addVariable(toVariablePayload(interval, { global: false, index: 0, model: interval }))
+ )
.whenAsyncActionIsDispatched(updateAutoValue(toVariableIdentifier(interval), dependencies), true);
expect(dependencies.kbn.calculateInterval).toHaveBeenCalledTimes(1);
diff --git a/public/app/features/variables/pickers/OptionsPicker/actions.test.ts b/public/app/features/variables/pickers/OptionsPicker/actions.test.ts
index 4d15d8cda8f..2f39de386e3 100644
--- a/public/app/features/variables/pickers/OptionsPicker/actions.test.ts
+++ b/public/app/features/variables/pickers/OptionsPicker/actions.test.ts
@@ -1,6 +1,5 @@
import { reduxTester } from '../../../../../test/core/redux/reduxTester';
-import { getTemplatingRootReducer } from '../../state/helpers';
-import { initDashboardTemplating } from '../../state/actions';
+import { getRootReducer } from '../../state/helpers';
import { TemplatingState } from '../../state/reducers';
import { QueryVariableModel, VariableHide, VariableRefresh, VariableSort } from '../../../templating/types';
import {
@@ -20,9 +19,10 @@ import {
} from './actions';
import { NavigationKey } from '../types';
import { toVariablePayload } from '../../state/types';
-import { changeVariableProp, setCurrentVariableValue } from '../../state/sharedReducer';
+import { changeVariableProp, setCurrentVariableValue, addVariable } from '../../state/sharedReducer';
import { variableAdapters } from '../../adapters';
import { createQueryVariableAdapter } from '../../query/adapter';
+import { updateLocation } from 'app/core/actions';
const datasource = {
metricFindQuery: jest.fn(() => Promise.resolve([])),
@@ -50,8 +50,8 @@ describe('options picker actions', () => {
const key = NavigationKey.cancel;
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
.whenAsyncActionIsDispatched(navigateOptions(key, clearOthers), true);
@@ -63,14 +63,15 @@ describe('options picker actions', () => {
};
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
- const [setCurrentValue, changeQueryValue, updateOption, hideAction] = actions;
- const expectedNumberOfActions = 4;
+ const [setCurrentValue, changeQueryValue, updateOption, locationAction, hideAction] = actions;
+ const expectedNumberOfActions = 5;
expect(setCurrentValue).toEqual(setCurrentVariableValue(toVariablePayload(variable, { option })));
expect(changeQueryValue).toEqual(
changeVariableProp(toVariablePayload(variable, { propName: 'queryValue', propValue: '' }))
);
expect(updateOption).toEqual(setCurrentVariableValue(toVariablePayload(variable, { option })));
+ expect(locationAction).toEqual(updateLocation({ query: { 'var-Constant': ['A'] } }));
expect(hideAction).toEqual(hideOptions());
return actions.length === expectedNumberOfActions;
@@ -87,8 +88,8 @@ describe('options picker actions', () => {
const key = NavigationKey.select;
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
.whenActionIsDispatched(navigateOptions(NavigationKey.moveDown, false))
.whenAsyncActionIsDispatched(navigateOptions(key, clearOthers), true);
@@ -112,8 +113,8 @@ describe('options picker actions', () => {
const key = NavigationKey.select;
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
.whenActionIsDispatched(navigateOptions(NavigationKey.moveDown, clearOthers))
.whenAsyncActionIsDispatched(navigateOptions(key, clearOthers), true);
@@ -137,8 +138,8 @@ describe('options picker actions', () => {
const key = NavigationKey.select;
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
.whenActionIsDispatched(navigateOptions(NavigationKey.moveDown, clearOthers))
.whenActionIsDispatched(navigateOptions(NavigationKey.moveDown, clearOthers))
@@ -164,8 +165,8 @@ describe('options picker actions', () => {
const key = NavigationKey.select;
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
.whenActionIsDispatched(navigateOptions(NavigationKey.moveDown, clearOthers))
.whenActionIsDispatched(navigateOptions(NavigationKey.moveDown, clearOthers))
@@ -192,8 +193,8 @@ describe('options picker actions', () => {
const key = NavigationKey.selectAndClose;
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
.whenActionIsDispatched(navigateOptions(NavigationKey.moveDown, clearOthers))
.whenActionIsDispatched(navigateOptions(NavigationKey.moveDown, clearOthers))
@@ -209,8 +210,15 @@ describe('options picker actions', () => {
};
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
- const [toggleOptionAction, setCurrentValue, changeQueryValue, updateOption, hideAction] = actions;
- const expectedNumberOfActions = 5;
+ const [
+ toggleOptionAction,
+ setCurrentValue,
+ changeQueryValue,
+ updateOption,
+ locationAction,
+ hideAction,
+ ] = actions;
+ const expectedNumberOfActions = 6;
expect(toggleOptionAction).toEqual(toggleOption({ option: options[1], forceSelect: false, clearOthers }));
expect(setCurrentValue).toEqual(setCurrentVariableValue(toVariablePayload(variable, { option })));
@@ -218,6 +226,7 @@ describe('options picker actions', () => {
changeVariableProp(toVariablePayload(variable, { propName: 'queryValue', propValue: '' }))
);
expect(updateOption).toEqual(setCurrentVariableValue(toVariablePayload(variable, { option })));
+ expect(locationAction).toEqual(updateLocation({ query: { 'var-Constant': ['B'] } }));
expect(hideAction).toEqual(hideOptions());
return actions.length === expectedNumberOfActions;
@@ -232,8 +241,8 @@ describe('options picker actions', () => {
const filter = 'A';
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
.whenAsyncActionIsDispatched(filterOrSearchOptions(filter), true);
@@ -255,8 +264,8 @@ describe('options picker actions', () => {
const variable = createVariable({ options, includeAll: false });
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
.whenAsyncActionIsDispatched(commitChangesToVariable(), true);
@@ -289,8 +298,8 @@ describe('options picker actions', () => {
const clearOthers = false;
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
.whenActionIsDispatched(navigateOptions(NavigationKey.moveDown, clearOthers))
.whenActionIsDispatched(toggleOptionByHighlight(clearOthers))
@@ -304,14 +313,15 @@ describe('options picker actions', () => {
};
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
- const [setCurrentValue, changeQueryValue, updateOption, hideAction] = actions;
- const expectedNumberOfActions = 4;
+ const [setCurrentValue, changeQueryValue, updateOption, locationAction, hideAction] = actions;
+ const expectedNumberOfActions = 5;
expect(setCurrentValue).toEqual(setCurrentVariableValue(toVariablePayload(variable, { option })));
expect(changeQueryValue).toEqual(
changeVariableProp(toVariablePayload(variable, { propName: 'queryValue', propValue: '' }))
);
expect(updateOption).toEqual(setCurrentVariableValue(toVariablePayload(variable, { option })));
+ expect(locationAction).toEqual(updateLocation({ query: { 'var-Constant': ['A'] } }));
expect(hideAction).toEqual(hideOptions());
return actions.length === expectedNumberOfActions;
@@ -326,8 +336,8 @@ describe('options picker actions', () => {
const clearOthers = false;
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
.whenActionIsDispatched(navigateOptions(NavigationKey.moveDown, clearOthers))
.whenActionIsDispatched(toggleOptionByHighlight(clearOthers), true);
@@ -351,8 +361,8 @@ describe('options picker actions', () => {
const variable = createVariable({ options, includeAll: false, tags: [tag] });
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
.whenAsyncActionIsDispatched(toggleAndFetchTag(tag), true);
@@ -378,8 +388,8 @@ describe('options picker actions', () => {
datasource.metricFindQuery.mockImplementation(() => Promise.resolve(values));
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
.whenAsyncActionIsDispatched(toggleAndFetchTag(tag), true);
diff --git a/public/app/features/variables/query/actions.test.ts b/public/app/features/variables/query/actions.test.ts
index 26748a1dc3f..e585e23b385 100644
--- a/public/app/features/variables/query/actions.test.ts
+++ b/public/app/features/variables/query/actions.test.ts
@@ -1,11 +1,10 @@
import { variableAdapters } from '../adapters';
import { createQueryVariableAdapter } from './adapter';
import { reduxTester } from '../../../../test/core/redux/reduxTester';
-import { getTemplatingRootReducer } from '../state/helpers';
+import { getRootReducer } from '../state/helpers';
import { QueryVariableModel, VariableHide, VariableRefresh, VariableSort } from '../../templating/types';
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, toVariablePayload } from '../state/types';
-import { changeVariableProp, setCurrentVariableValue } from '../state/sharedReducer';
-import { initDashboardTemplating } from '../state/actions';
+import { changeVariableProp, setCurrentVariableValue, addVariable } from '../state/sharedReducer';
import { TemplatingState } from '../state/reducers';
import {
changeQueryVariableDataSource,
@@ -62,8 +61,8 @@ describe('query actions', () => {
mockDatasourceMetrics(variable, optionsMetrics, tagsMetrics);
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(updateQueryVariableOptions(toVariablePayload(variable)), true);
const option = createOption(ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE);
@@ -90,8 +89,8 @@ describe('query actions', () => {
mockDatasourceMetrics(variable, optionsMetrics, tagsMetrics);
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(updateQueryVariableOptions(toVariablePayload(variable)), true);
const option = createOption('A');
@@ -117,8 +116,8 @@ describe('query actions', () => {
mockDatasourceMetrics(variable, optionsMetrics, []);
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(updateQueryVariableOptions(toVariablePayload(variable)), true);
const option = createOption('A');
@@ -143,8 +142,8 @@ describe('query actions', () => {
mockDatasourceMetrics(variable, optionsMetrics, []);
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(updateQueryVariableOptions(toVariablePayload(variable)), true);
const option = createOption(ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE);
@@ -169,8 +168,8 @@ describe('query actions', () => {
mockDatasourceMetrics(variable, optionsMetrics, []);
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(setIdInEditor({ id: variable.id! }))
.whenAsyncActionIsDispatched(updateQueryVariableOptions(toVariablePayload(variable)), true);
@@ -197,8 +196,8 @@ describe('query actions', () => {
mocks[variable.datasource!].metricFindQuery = jest.fn(() => Promise.reject(error));
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(setIdInEditor({ id: variable.id! }))
.whenAsyncActionIsDispatched(updateQueryVariableOptions(toVariablePayload(variable)), true);
@@ -226,8 +225,8 @@ describe('query actions', () => {
});
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(initQueryVariableEditor(toVariablePayload(variable)), true);
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
@@ -259,8 +258,8 @@ describe('query actions', () => {
});
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(initQueryVariableEditor(toVariablePayload(variable)), true);
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
@@ -291,8 +290,8 @@ describe('query actions', () => {
});
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(initQueryVariableEditor(toVariablePayload(variable)), true);
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
@@ -317,8 +316,8 @@ describe('query actions', () => {
const ds = { name: '', value: '', meta: {}, sort: '' };
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(initQueryVariableEditor(toVariablePayload(variable)), true);
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
@@ -341,8 +340,8 @@ describe('query actions', () => {
});
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(changeQueryVariableDataSource(toVariablePayload(variable), 'datasource'), true);
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
@@ -371,8 +370,8 @@ describe('query actions', () => {
});
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(changeQueryVariableDataSource(toVariablePayload(variable), 'datasource'), true);
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
@@ -403,8 +402,8 @@ describe('query actions', () => {
mockDatasourceMetrics({ ...variable, query }, optionsMetrics, tagsMetrics);
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(changeQueryVariableQuery(toVariablePayload(variable), query, definition), true);
const option = createOption(ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE);
@@ -441,8 +440,8 @@ describe('query actions', () => {
mockDatasourceMetrics({ ...variable, query }, optionsMetrics, []);
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(changeQueryVariableQuery(toVariablePayload(variable), query, definition), true);
const option = createOption(ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE);
@@ -477,8 +476,8 @@ describe('query actions', () => {
mockDatasourceMetrics({ ...variable, query }, optionsMetrics, []);
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(changeQueryVariableQuery(toVariablePayload(variable), query, definition), true);
const option = createOption('A');
@@ -510,8 +509,8 @@ describe('query actions', () => {
const definition = 'depends on datasource variable';
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(changeQueryVariableQuery(toVariablePayload(variable), query, definition), true);
const errorText = 'Query cannot contain a reference to itself. Variable: $' + variable.name;
diff --git a/public/app/features/variables/state/actions.test.ts b/public/app/features/variables/state/actions.test.ts
index df1440553ce..27e22d4db5e 100644
--- a/public/app/features/variables/state/actions.test.ts
+++ b/public/app/features/variables/state/actions.test.ts
@@ -43,6 +43,12 @@ variableAdapters.setInit(() => [
createConstantVariableAdapter(),
]);
+jest.mock('app/features/dashboard/services/TimeSrv', () => ({
+ getTimeSrv: () => ({
+ timeRange: jest.fn().mockReturnValue(undefined),
+ }),
+}));
+
describe('shared actions', () => {
describe('when initDashboardTemplating is dispatched', () => {
it('then correct actions are dispatched', () => {
diff --git a/public/app/features/variables/state/actions.ts b/public/app/features/variables/state/actions.ts
index 631f984f51e..5fe5c51dd01 100644
--- a/public/app/features/variables/state/actions.ts
+++ b/public/app/features/variables/state/actions.ts
@@ -25,10 +25,13 @@ import {
changeVariableProp,
} from './sharedReducer';
import { toVariableIdentifier, toVariablePayload, VariableIdentifier } from './types';
-import { appEvents } from '../../../core/core';
+import { appEvents } from 'app/core/core';
+import { contextSrv } from 'app/core/services/context_srv';
import templateSrv from '../../templating/template_srv';
import { alignCurrentWithMulti } from '../shared/multiOptions';
import { isMulti } from '../guard';
+import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
+import { DashboardModel } from 'app/features/dashboard/state';
// process flow queryVariable
// thunk => processVariables
@@ -72,12 +75,37 @@ export const initDashboardTemplating = (list: VariableModel[]): ThunkResult => {
+ return (dispatch, getState) => {
+ templateSrv.setGlobalVariable('__dashboard', {
+ value: {
+ name: dashboard.title,
+ uid: dashboard.uid,
+ toString: function() {
+ return this.uid;
+ },
+ },
+ });
+ templateSrv.setGlobalVariable('__org', {
+ value: {
+ name: contextSrv.user.orgName,
+ id: contextSrv.user.id,
+ toString: function() {
+ return this.id;
+ },
+ },
+ });
+ };
+};
+
export const changeVariableMultiValue = (identifier: VariableIdentifier, multi: boolean): ThunkResult => {
return (dispatch, getState) => {
const variable = getVariable(identifier.id!, getState());
@@ -106,7 +134,10 @@ export const processVariableDependencies = async (variable: VariableModel, state
await Promise.all(dependencies);
};
-export const processVariable = (identifier: VariableIdentifier, queryParams: UrlQueryMap): ThunkResult => {
+export const processVariable = (
+ identifier: VariableIdentifier,
+ queryParams: UrlQueryMap
+): ThunkResult> => {
return async (dispatch, getState) => {
const variable = getVariable(identifier.id!, getState());
await processVariableDependencies(variable, getState());
@@ -114,7 +145,7 @@ export const processVariable = (identifier: VariableIdentifier, queryParams: Url
const urlValue = queryParams['var-' + variable.name];
if (urlValue !== void 0) {
await variableAdapters.get(variable.type).setValueFromUrl(variable, urlValue ?? '');
- await dispatch(resolveInitLock(toVariablePayload(variable)));
+ dispatch(resolveInitLock(toVariablePayload(variable)));
return;
}
@@ -125,16 +156,16 @@ export const processVariable = (identifier: VariableIdentifier, queryParams: Url
refreshableVariable.refresh === VariableRefresh.onTimeRangeChanged
) {
await variableAdapters.get(variable.type).updateOptions(refreshableVariable);
- await dispatch(resolveInitLock(toVariablePayload(variable)));
+ dispatch(resolveInitLock(toVariablePayload(variable)));
return;
}
}
- await dispatch(resolveInitLock(toVariablePayload(variable)));
+ dispatch(resolveInitLock(toVariablePayload(variable)));
};
};
-export const processVariables = (): ThunkResult => {
+export const processVariables = (): ThunkResult> => {
return async (dispatch, getState) => {
const queryParams = getState().location.query;
const promises = getVariables(getState()).map(
@@ -144,12 +175,15 @@ export const processVariables = (): ThunkResult => {
await Promise.all(promises);
for (let index = 0; index < getVariables(getState()).length; index++) {
- await dispatch(removeInitLock(toVariablePayload(getVariables(getState())[index])));
+ dispatch(removeInitLock(toVariablePayload(getVariables(getState())[index])));
}
};
};
-export const setOptionFromUrl = (identifier: VariableIdentifier, urlValue: UrlQueryValue): ThunkResult => {
+export const setOptionFromUrl = (
+ identifier: VariableIdentifier,
+ urlValue: UrlQueryValue
+): ThunkResult> => {
return async (dispatch, getState) => {
const variable = getVariable(identifier.id!, getState());
if (variable.hasOwnProperty('refresh') && (variable as QueryVariableModel).refresh !== VariableRefresh.never) {
@@ -233,8 +267,8 @@ export const selectOptionsForCurrentValue = (variable: VariableWithOptions): Var
export const validateVariableSelectionState = (
identifier: VariableIdentifier,
defaultValue?: string
-): ThunkResult => {
- return async (dispatch, getState) => {
+): ThunkResult> => {
+ return (dispatch, getState) => {
const variableInState = getVariable(identifier.id!, getState());
const current = variableInState.current || (({} as unknown) as VariableOption);
const setValue = variableAdapters.get(variableInState.type).setValue;
@@ -287,8 +321,8 @@ export const setOptionAsCurrent = (
identifier: VariableIdentifier,
current: VariableOption,
emitChanges: boolean
-): ThunkResult => {
- return async dispatch => {
+): ThunkResult> => {
+ return dispatch => {
dispatch(setCurrentVariableValue(toVariablePayload(identifier, { option: current })));
return dispatch(variableUpdated(identifier, emitChanges));
};
@@ -316,7 +350,10 @@ const createGraph = (variables: VariableModel[]) => {
return g;
};
-export const variableUpdated = (identifier: VariableIdentifier, emitChangeEvents: boolean): ThunkResult => {
+export const variableUpdated = (
+ identifier: VariableIdentifier,
+ emitChangeEvents: boolean
+): ThunkResult> => {
return (dispatch, getState) => {
// if there is a variable lock ignore cascading update because we are in a boot up scenario
const variable = getVariable(identifier.id!, getState());
@@ -358,7 +395,7 @@ export interface OnTimeRangeUpdatedDependencies {
export const onTimeRangeUpdated = (
timeRange: TimeRange,
dependencies: OnTimeRangeUpdatedDependencies = { templateSrv: templateSrv, appEvents: appEvents }
-): ThunkResult => async (dispatch, getState) => {
+): ThunkResult> => async (dispatch, getState) => {
dependencies.templateSrv.updateTimeRange(timeRange);
const variablesThatNeedRefresh = getVariables(getState()).filter(variable => {
if (variable.hasOwnProperty('refresh') && variable.hasOwnProperty('options')) {
diff --git a/public/app/features/variables/state/selectors.ts b/public/app/features/variables/state/selectors.ts
index 5f387edaf2d..583508ed5c3 100644
--- a/public/app/features/variables/state/selectors.ts
+++ b/public/app/features/variables/state/selectors.ts
@@ -19,21 +19,21 @@ export const getVariable = (
};
export const getFilteredVariables = (filter: (model: VariableModel) => boolean, state: StoreState = getState()) => {
- return Object.values(state.templating.variables).filter(filter);
+ return Object.values(state.templating.variables)
+ .filter(filter)
+ .sort((s1, s2) => s1.index! - s2.index!);
};
-export const getVariableWithName = (name: string) => {
- return getVariable(name, getState(), false);
+export const getVariableWithName = (name: string, state: StoreState = getState()) => {
+ return getVariable(name, state, false);
};
export const getVariables = (state: StoreState = getState(), includeNewVariable = false): VariableModel[] => {
- const variables = getFilteredVariables(
- variable => (includeNewVariable ? true : variable.id! !== NEW_VARIABLE_ID),
- state
- );
- return variables.sort((s1, s2) => s1.index! - s2.index!);
+ return getFilteredVariables(variable => (includeNewVariable ? true : variable.id! !== NEW_VARIABLE_ID), state);
};
+export type GetVariables = typeof getVariables;
+
export const getNewVariabelIndex = (state: StoreState = getState()): number => {
return Object.values(state.templating.variables).length;
};
diff --git a/public/app/features/variables/textbox/actions.test.ts b/public/app/features/variables/textbox/actions.test.ts
index 1c3555c7a1f..bfa9f8a76d6 100644
--- a/public/app/features/variables/textbox/actions.test.ts
+++ b/public/app/features/variables/textbox/actions.test.ts
@@ -3,12 +3,12 @@ import { createTextBoxVariableAdapter } from './adapter';
import { reduxTester } from '../../../../test/core/redux/reduxTester';
import { TemplatingState } from 'app/features/variables/state/reducers';
import { updateTextBoxVariableOptions } from './actions';
-import { getTemplatingRootReducer } from '../state/helpers';
+import { getRootReducer } from '../state/helpers';
import { TextBoxVariableModel, VariableHide, VariableOption } from '../../templating/types';
import { toVariablePayload } from '../state/types';
import { createTextBoxOptions } from './reducer';
-import { setCurrentVariableValue } from '../state/sharedReducer';
-import { initDashboardTemplating } from '../state/actions';
+import { setCurrentVariableValue, addVariable } from '../state/sharedReducer';
+import { updateLocation } from 'app/core/actions';
describe('textbox actions', () => {
variableAdapters.setInit(() => [createTextBoxVariableAdapter()]);
@@ -40,16 +40,18 @@ describe('textbox actions', () => {
};
const tester = await reduxTester<{ templating: TemplatingState }>()
- .givenRootReducer(getTemplatingRootReducer())
- .whenActionIsDispatched(initDashboardTemplating([variable]))
+ .givenRootReducer(getRootReducer())
+ .whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(updateTextBoxVariableOptions(toVariablePayload(variable)), true);
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
- const [createAction, setCurrentAction] = actions;
- const expectedNumberOfActions = 2;
+ const [createAction, setCurrentAction, locationAction] = actions;
+ const expectedNumberOfActions = 3;
expect(createAction).toEqual(createTextBoxOptions(toVariablePayload(variable)));
expect(setCurrentAction).toEqual(setCurrentVariableValue(toVariablePayload(variable, { option })));
+ expect(locationAction).toEqual(updateLocation({ query: { 'var-textbox': 'A' } }));
+
return actions.length === expectedNumberOfActions;
});
});
diff --git a/public/app/plugins/datasource/cloudwatch/specs/datasource.test.ts b/public/app/plugins/datasource/cloudwatch/specs/datasource.test.ts
index bff7304c9ae..fd69052b11a 100644
--- a/public/app/plugins/datasource/cloudwatch/specs/datasource.test.ts
+++ b/public/app/plugins/datasource/cloudwatch/specs/datasource.test.ts
@@ -1,14 +1,14 @@
import '../datasource';
import CloudWatchDatasource from '../datasource';
import * as redux from 'app/store/store';
-import { dateMath } from '@grafana/data';
+import { DataSourceInstanceSettings, dateMath } from '@grafana/data';
import { TemplateSrv } from 'app/features/templating/template_srv';
import { CustomVariable } from 'app/features/templating/all';
-import _ from 'lodash';
import { CloudWatchQuery } from '../types';
-import { DataSourceInstanceSettings } from '@grafana/data';
import { backendSrv } from 'app/core/services/backend_srv'; // will use the version in __mocks__
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
+import { convertToStoreState } from '../../../../../test/helpers/convertToStoreState';
+import { getTemplateSrvDependencies } from 'test/helpers/getTemplateSrvDependencies';
jest.mock('@grafana/runtime', () => ({
...jest.requireActual('@grafana/runtime'),
@@ -23,7 +23,7 @@ describe('CloudWatchDatasource', () => {
name: 'TestDatasource',
} as DataSourceInstanceSettings;
- const templateSrv = new TemplateSrv();
+ let templateSrv = new TemplateSrv();
const start = 1483196400 * 1000;
const defaultTimeRange = { from: new Date(start), to: new Date(start + 3600 * 1000) };
@@ -465,7 +465,7 @@ describe('CloudWatchDatasource', () => {
describe('When performing CloudWatch query with template variables', () => {
let requestParams: { queries: CloudWatchQuery[] };
beforeEach(() => {
- templateSrv.init([
+ const variables = [
new CustomVariable(
{
name: 'var1',
@@ -516,7 +516,11 @@ describe('CloudWatchDatasource', () => {
},
{} as any
),
- ]);
+ ];
+ const state = convertToStoreState(variables);
+ const _templateSrv = new TemplateSrv(getTemplateSrvDependencies(state));
+ _templateSrv.init(variables);
+ ctx.ds = new CloudWatchDatasource(instanceSettings, _templateSrv, timeSrv);
datasourceRequestMock.mockImplementation(params => {
requestParams = params.data;
diff --git a/public/app/plugins/datasource/stackdriver/components/VariableQueryEditor.test.tsx b/public/app/plugins/datasource/stackdriver/components/VariableQueryEditor.test.tsx
index b7c3ef66df8..1f4c2949903 100644
--- a/public/app/plugins/datasource/stackdriver/components/VariableQueryEditor.test.tsx
+++ b/public/app/plugins/datasource/stackdriver/components/VariableQueryEditor.test.tsx
@@ -11,6 +11,21 @@ jest.mock('../functions', () => ({
extractServicesFromMetricDescriptors: (): any[] => [],
}));
+jest.mock('../../../../core/config', () => {
+ console.warn('[This test uses old variable system, needs a rewrite]');
+ const original = jest.requireActual('../../../../core/config');
+ const config = original.getConfig();
+ return {
+ getConfig: () => ({
+ ...config,
+ featureToggles: {
+ ...config.featureToggles,
+ newVariables: false,
+ },
+ }),
+ };
+});
+
const props: VariableQueryProps = {
onChange: (query, definition) => {},
query: {},
diff --git a/public/app/store/store.ts b/public/app/store/store.ts
index c133cff1d5f..a00442011fa 100644
--- a/public/app/store/store.ts
+++ b/public/app/store/store.ts
@@ -9,7 +9,11 @@ export function setStore(newStore: Store) {
export function getState(): StoreState {
if (!store || !store.getState) {
- return {} as StoreState; // used by tests
+ return {
+ templating: {
+ variables: {},
+ },
+ } as StoreState; // used by tests
}
return store.getState();
diff --git a/public/test/helpers/convertToStoreState.ts b/public/test/helpers/convertToStoreState.ts
new file mode 100644
index 00000000000..c8c51dc6b9d
--- /dev/null
+++ b/public/test/helpers/convertToStoreState.ts
@@ -0,0 +1,12 @@
+import { StoreState } from '../../app/types';
+
+export const convertToStoreState = (variables: any[]): StoreState => {
+ return {
+ templating: {
+ variables: variables.reduce((byName, variable) => {
+ byName[variable.name] = variable;
+ return byName;
+ }, {}),
+ },
+ } as StoreState;
+};
diff --git a/public/test/helpers/getDashboardModel.ts b/public/test/helpers/getDashboardModel.ts
new file mode 100644
index 00000000000..f7c0b54cdee
--- /dev/null
+++ b/public/test/helpers/getDashboardModel.ts
@@ -0,0 +1,6 @@
+import { DashboardModel } from '../../app/features/dashboard/state';
+
+export const getDashboardModel = (json: any, meta: any = {}) => {
+ const getVariablesFromState = () => json.templating.list;
+ return new DashboardModel(json, meta, getVariablesFromState);
+};
diff --git a/public/test/helpers/getTemplateSrvDependencies.ts b/public/test/helpers/getTemplateSrvDependencies.ts
new file mode 100644
index 00000000000..13f30777e8a
--- /dev/null
+++ b/public/test/helpers/getTemplateSrvDependencies.ts
@@ -0,0 +1,9 @@
+import { getFilteredVariables, getVariables, getVariableWithName } from '../../app/features/variables/state/selectors';
+import { StoreState } from '../../app/types';
+import { TemplateSrvDependencies } from 'app/features/templating/template_srv';
+
+export const getTemplateSrvDependencies = (state: StoreState): TemplateSrvDependencies => ({
+ getFilteredVariables: filter => getFilteredVariables(filter, state),
+ getVariableWithName: name => getVariableWithName(name, state),
+ getVariables: () => getVariables(state),
+});