diff --git a/public/app/core/utils/promiseToDigest.test.ts b/public/app/core/utils/promiseToDigest.test.ts new file mode 100644 index 00000000000..037dfbb3e6d --- /dev/null +++ b/public/app/core/utils/promiseToDigest.test.ts @@ -0,0 +1,27 @@ +import { IScope } from 'angular'; +import { promiseToDigest } from './promiseToDigest'; + +describe('promiseToDigest', () => { + describe('when called with a promise that resolves', () => { + it('then evalAsync should be called on $scope', async () => { + const $scope: IScope = ({ $evalAsync: jest.fn() } as any) as IScope; + + await promiseToDigest($scope)(Promise.resolve(123)); + + expect($scope.$evalAsync).toHaveBeenCalledTimes(1); + }); + }); + + describe('when called with a promise that rejects', () => { + it('then evalAsync should be called on $scope', async () => { + const $scope: IScope = ({ $evalAsync: jest.fn() } as any) as IScope; + + try { + await promiseToDigest($scope)(Promise.reject(123)); + } catch (error) { + expect(error).toEqual(123); + expect($scope.$evalAsync).toHaveBeenCalledTimes(1); + } + }); + }); +}); diff --git a/public/app/core/utils/promiseToDigest.ts b/public/app/core/utils/promiseToDigest.ts new file mode 100644 index 00000000000..b06a87db467 --- /dev/null +++ b/public/app/core/utils/promiseToDigest.ts @@ -0,0 +1,3 @@ +import { IScope } from 'angular'; + +export const promiseToDigest = ($scope: IScope) => (promise: Promise) => promise.finally($scope.$evalAsync); diff --git a/public/app/features/templating/editor_ctrl.ts b/public/app/features/templating/editor_ctrl.ts index bc3aeeb79bd..e2bfe2335f9 100644 --- a/public/app/features/templating/editor_ctrl.ts +++ b/public/app/features/templating/editor_ctrl.ts @@ -6,6 +6,7 @@ import DatasourceSrv from '../plugins/datasource_srv'; import { VariableSrv } from './all'; import { TemplateSrv } from './template_srv'; import { AppEvents } from '@grafana/data'; +import { promiseToDigest } from '../../core/utils/promiseToDigest'; export class VariableEditorCtrl { /** @ngInject */ @@ -122,11 +123,13 @@ export class VariableEditorCtrl { $scope.infoText = ''; if ($scope.current.type === 'adhoc' && $scope.current.datasource !== null) { $scope.infoText = 'Adhoc filters are applied automatically to all queries that target this datasource'; - datasourceSrv.get($scope.current.datasource).then(ds => { - if (!ds.getTagKeys) { - $scope.infoText = 'This datasource does not support adhoc filters yet.'; - } - }); + promiseToDigest($scope)( + datasourceSrv.get($scope.current.datasource).then(ds => { + if (!ds.getTagKeys) { + $scope.infoText = 'This datasource does not support adhoc filters yet.'; + } + }) + ); } }; @@ -154,9 +157,11 @@ export class VariableEditorCtrl { $scope.currentIsNew = false; $scope.mode = 'edit'; $scope.validate(); - datasourceSrv.get($scope.current.datasource).then(ds => { - $scope.currentDatasource = ds; - }); + promiseToDigest($scope)( + datasourceSrv.get($scope.current.datasource).then(ds => { + $scope.currentDatasource = ds; + }) + ); }; $scope.duplicate = (variable: { getSaveModel: () => void; name: string }) => { @@ -168,11 +173,13 @@ export class VariableEditorCtrl { $scope.update = () => { if ($scope.isValid()) { - $scope.runQuery().then(() => { - $scope.reset(); - $scope.mode = 'list'; - templateSrv.updateIndex(); - }); + promiseToDigest($scope)( + $scope.runQuery().then(() => { + $scope.reset(); + $scope.mode = 'list'; + templateSrv.updateIndex(); + }) + ); } }; @@ -218,10 +225,12 @@ export class VariableEditorCtrl { }; $scope.datasourceChanged = async () => { - datasourceSrv.get($scope.current.datasource).then(ds => { - $scope.current.query = ''; - $scope.currentDatasource = ds; - }); + promiseToDigest($scope)( + datasourceSrv.get($scope.current.datasource).then(ds => { + $scope.current.query = ''; + $scope.currentDatasource = ds; + }) + ); }; } }