A lot of refactoring opf unsaved changes service so it can be unit tested better

This commit is contained in:
Torkel Ödegaard
2015-04-28 13:45:59 +02:00
parent f6a61c1ec5
commit aaea80e053
5 changed files with 175 additions and 88 deletions

47
' Normal file
View File

@@ -0,0 +1,47 @@
define([
'features/dashboard/unsavedChangesSrv',
'features/dashboard/dashboardSrv'
], function() {
'use strict';
describe("unsavedChangesSrv", function() {
var _unsavedChangesSrv;
var _dashboardSrv;
var _location;
var _contextSrvStub = {
isEditor: true
};
var _rootScope;
var tracker;
beforeEach(module('grafana.services'));
beforeEach(module(function($provide) {
$provide.value('contextSrv', _contextSrvStub);
}));
beforeEach(inject(function(unsavedChangesSrv, $location, $rootScope, dashboardSrv) {
_unsavedChangesSrv = unsavedChangesSrv;
_dashboardSrv = dashboardSrv;
_location = $location;
_rootScope = $rootScope;
}));
describe('when dashboard is modified and route changes', function() {
beforeEach(function() {
var dash = _dashboardSrv.create({});
var scope = _rootScope.$new();
scope.appEvent = sinon.spy();
scope.onAppEvent = sinon.spy();
tracker = _unsavedChangesSrv.constructor(dash, scope);
});
it('No changes should not have changes', function() {
expect(tracker.hasChanges()).to.be(false);
});
});
});
});

View File

@@ -17,6 +17,7 @@ function (angular, $, config) {
templateValuesSrv, templateValuesSrv,
dynamicDashboardSrv, dynamicDashboardSrv,
dashboardSrv, dashboardSrv,
unsavedChangesSrv,
dashboardViewStateSrv, dashboardViewStateSrv,
contextSrv, contextSrv,
$timeout) { $timeout) {
@@ -48,6 +49,7 @@ function (angular, $, config) {
// the rest of the dashboard can load // the rest of the dashboard can load
templateValuesSrv.init(dashboard).finally(function() { templateValuesSrv.init(dashboard).finally(function() {
dynamicDashboardSrv.init(dashboard); dynamicDashboardSrv.init(dashboard);
unsavedChangesSrv.init(dashboard, $scope);
$scope.dashboard = dashboard; $scope.dashboard = dashboard;
$scope.dashboardMeta = dashboard.meta; $scope.dashboardMeta = dashboard.meta;

View File

@@ -1,90 +1,68 @@
define([ define([
'angular', 'angular',
'lodash', 'lodash',
'config',
], ],
function(angular, _, config) { function(angular, _) {
'use strict'; 'use strict';
if (!config.unsaved_changes_warning) {
return;
}
var module = angular.module('grafana.services'); var module = angular.module('grafana.services');
module.service('unsavedChangesSrv', function($rootScope, $modal, $q, $location, $timeout, contextSrv) { module.service('unsavedChangesSrv', function($modal, $q, $location, $timeout, contextSrv, $window) {
var self = this; function Tracker(dashboard, scope) {
var modalScope = $rootScope.$new(); var self = this;
$rootScope.$on("dashboard-loaded", function(event, newDashboard) { this.original = dashboard.getSaveModelClone();
// wait for different services to patch the dashboard (missing properties) this.current = dashboard;
$timeout(function() { this.originalPath = $location.path();
self.original = newDashboard.getSaveModelClone(); this.scope = scope;
self.current = newDashboard;
}, 1200);
});
$rootScope.$on("dashboard-saved", function(event, savedDashboard) { // register events
self.original = savedDashboard.getSaveModelClone(); scope.onAppEvent('dashboard-saved', function() {
self.current = savedDashboard; self.original = self.current.getSaveModelClone();
self.orignalPath = $location.path(); self.originalPath = $location.path();
}); });
$rootScope.$on("$routeChangeSuccess", function() { $window.onbeforeunload = function() {
self.original = null; if (self.ignoreChanges()) { return; }
self.originalPath = $location.path(); if (self.hasChanges()) {
}); return "There are unsaved changes to this dashboard";
}
};
this.ignoreChanges = function() { scope.$on("$locationChangeStart", function(event, next) {
if (!contextSrv.isEditor) { return true; }
if (!self.current || !self.current.meta) { return true; }
var meta = self.current.meta;
return !meta.canSave || meta.fromScript || meta.fromFile;
};
window.onbeforeunload = function() {
if (self.ignoreChanges()) { return; }
if (self.has_unsaved_changes()) {
return "There are unsaved changes to this dashboard";
}
};
this.init = function() {
$rootScope.$on("$locationChangeStart", function(event, next) {
// check if we should look for changes // check if we should look for changes
if (self.originalPath === $location.path()) { return true; } if (self.originalPath === $location.path()) { return true; }
if (self.ignoreChanges()) { return true; } if (self.ignoreChanges()) { return true; }
if (self.has_unsaved_changes()) { if (self.hasChanges()) {
event.preventDefault(); event.preventDefault();
self.next = next; self.next = next;
$timeout(self.open_modal); $timeout(function() {
self.open_modal();
});
} }
}); });
}
var p = Tracker.prototype;
// for some dashboards and users
// changes should be ignored
p.ignoreChanges = function() {
if (!this.original) { return false; }
if (!contextSrv.isEditor) { return true; }
if (!this.current || !this.current.meta) { return true; }
var meta = this.current.meta;
return !meta.canSave || meta.fromScript || meta.fromFile;
}; };
this.open_modal = function() { // remove stuff that should not count in diff
var confirmModal = $modal({ p.cleanDashboardFromIgnoredChanges = function(dash) {
template: './app/partials/unsaved-changes.html',
modalClass: 'confirm-modal',
persist: true,
show: false,
scope: modalScope,
keyboard: false
});
$q.when(confirmModal).then(function(modalEl) {
modalEl.modal('show');
});
};
this.cleanDashboardFromRepeatedPanelsAndRows = function(dash) {
dash.rows = _.filter(dash.rows, function(row) { dash.rows = _.filter(dash.rows, function(row) {
if (row.repeatRowId) { if (row.repeatRowId) {
console.log('filtering out row');
return false; return false;
} }
@@ -101,13 +79,9 @@ function(angular, _, config) {
}); });
}; };
this.has_unsaved_changes = function() { p.hasChanges = function() {
if (!self.original) { var current = this.current.getSaveModelClone();
return false; var original = this.original;
}
var current = self.current.getSaveModelClone();
var original = self.original;
// ignore timespan changes // ignore timespan changes
current.time = original.time = {}; current.time = original.time = {};
@@ -126,8 +100,8 @@ function(angular, _, config) {
} }
}); });
this.cleanDashboardFromRepeatedPanelsAndRows(current); this.cleanDashboardFromIgnoredChanges(current);
this.cleanDashboardFromRepeatedPanelsAndRows(original); this.cleanDashboardFromIgnoredChanges(original);
// ignore some panel and row stuff // ignore some panel and row stuff
current.forEachPanel(function(panel, panelIndex, row, rowIndex) { current.forEachPanel(function(panel, panelIndex, row, rowIndex) {
@@ -165,28 +139,43 @@ function(angular, _, config) {
return false; return false;
}; };
this.goto_next = function() { p.open_modal = function() {
var tracker = this;
var modalScope = this.scope.$new();
modalScope.ignore = function() {
tracker.original = null;
tracker.goto_next();
};
modalScope.save = function() {
tracker.scope.$emit('save-dashboard');
};
var confirmModal = $modal({
template: './app/partials/unsaved-changes.html',
modalClass: 'confirm-modal',
persist: false,
show: false,
scope: modalScope,
keyboard: false
});
$q.when(confirmModal).then(function(modalEl) {
modalEl.modal('show');
});
};
p.goto_next = function() {
var baseLen = $location.absUrl().length - $location.url().length; var baseLen = $location.absUrl().length - $location.url().length;
var nextUrl = self.next.substring(baseLen); var nextUrl = this.next.substring(baseLen);
$location.url(nextUrl); $location.url(nextUrl);
}; };
modalScope.ignore = function() { this.Tracker = Tracker;
self.original = null; this.init = function(dashboard, scope) {
self.goto_next(); // wait for different services to patch the dashboard (missing properties)
$timeout(function() { new Tracker(dashboard, scope); }, 1200);
}; };
modalScope.save = function() {
var unregister = $rootScope.$on('dashboard-saved', function() {
self.goto_next();
});
$timeout(unregister, 2000);
$rootScope.$emit('save-dashboard');
};
}).run(function(unsavedChangesSrv) {
unsavedChangesSrv.init();
}); });
}); });

View File

@@ -0,0 +1,48 @@
define([
'features/dashboard/unsavedChangesSrv',
'features/dashboard/dashboardSrv'
], function() {
'use strict';
describe("unsavedChangesSrv", function() {
var _unsavedChangesSrv;
var _dashboardSrv;
var _location;
var _contextSrvStub = { isEditor: true };
var _rootScope;
var tracker;
var dash;
var scope;
beforeEach(module('grafana.services'));
beforeEach(module(function($provide) {
$provide.value('contextSrv', _contextSrvStub);
}));
beforeEach(inject(function(unsavedChangesSrv, $location, $rootScope, dashboardSrv) {
_unsavedChangesSrv = unsavedChangesSrv;
_dashboardSrv = dashboardSrv;
_location = $location;
_rootScope = $rootScope;
}));
beforeEach(function() {
dash = _dashboardSrv.create({});
scope = _rootScope.$new();
scope.appEvent = sinon.spy();
scope.onAppEvent = sinon.spy();
tracker = new _unsavedChangesSrv.Tracker(dash, scope);
});
it('No changes should not have changes', function() {
expect(tracker.hasChanges()).to.be(false);
});
it('Simple change should be registered', function() {
dash.property = "google";
expect(tracker.hasChanges()).to.be(true);
});
});
});

View File

@@ -141,6 +141,7 @@ require([
'specs/dashboardViewStateSrv-specs', 'specs/dashboardViewStateSrv-specs',
'specs/soloPanelCtrl-specs', 'specs/soloPanelCtrl-specs',
'specs/dynamicDashboardSrv-specs', 'specs/dynamicDashboardSrv-specs',
'specs/unsavedChangesSrv-specs',
]; ];
var pluginSpecs = (config.plugins.specs || []).map(function (spec) { var pluginSpecs = (config.plugins.specs || []).map(function (spec) {