From aaea80e0537301b1856c8ba09b2e083b829bb4c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 28 Apr 2015 13:45:59 +0200 Subject: [PATCH] A lot of refactoring opf unsaved changes service so it can be unit tested better --- ' | 47 +++++ .../app/features/dashboard/dashboardCtrl.js | 2 + .../features/dashboard/unsavedChangesSrv.js | 165 ++++++++---------- public/test/specs/unsavedChangesSrv-specs.js | 48 +++++ public/test/test-main.js | 1 + 5 files changed, 175 insertions(+), 88 deletions(-) create mode 100644 ' create mode 100644 public/test/specs/unsavedChangesSrv-specs.js diff --git a/' b/' new file mode 100644 index 00000000000..2b78b586722 --- /dev/null +++ b/' @@ -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); + }); + + }); + + }); + +}); diff --git a/public/app/features/dashboard/dashboardCtrl.js b/public/app/features/dashboard/dashboardCtrl.js index 41453bd77e3..f81b808a0e1 100644 --- a/public/app/features/dashboard/dashboardCtrl.js +++ b/public/app/features/dashboard/dashboardCtrl.js @@ -17,6 +17,7 @@ function (angular, $, config) { templateValuesSrv, dynamicDashboardSrv, dashboardSrv, + unsavedChangesSrv, dashboardViewStateSrv, contextSrv, $timeout) { @@ -48,6 +49,7 @@ function (angular, $, config) { // the rest of the dashboard can load templateValuesSrv.init(dashboard).finally(function() { dynamicDashboardSrv.init(dashboard); + unsavedChangesSrv.init(dashboard, $scope); $scope.dashboard = dashboard; $scope.dashboardMeta = dashboard.meta; diff --git a/public/app/features/dashboard/unsavedChangesSrv.js b/public/app/features/dashboard/unsavedChangesSrv.js index 97fa3027cfc..8c1862674e7 100644 --- a/public/app/features/dashboard/unsavedChangesSrv.js +++ b/public/app/features/dashboard/unsavedChangesSrv.js @@ -1,90 +1,68 @@ define([ 'angular', 'lodash', - 'config', ], -function(angular, _, config) { +function(angular, _) { 'use strict'; - if (!config.unsaved_changes_warning) { - return; - } - 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; - var modalScope = $rootScope.$new(); + function Tracker(dashboard, scope) { + var self = this; - $rootScope.$on("dashboard-loaded", function(event, newDashboard) { - // wait for different services to patch the dashboard (missing properties) - $timeout(function() { - self.original = newDashboard.getSaveModelClone(); - self.current = newDashboard; - }, 1200); - }); + this.original = dashboard.getSaveModelClone(); + this.current = dashboard; + this.originalPath = $location.path(); + this.scope = scope; - $rootScope.$on("dashboard-saved", function(event, savedDashboard) { - self.original = savedDashboard.getSaveModelClone(); - self.current = savedDashboard; - self.orignalPath = $location.path(); - }); + // register events + scope.onAppEvent('dashboard-saved', function() { + self.original = self.current.getSaveModelClone(); + self.originalPath = $location.path(); + }); - $rootScope.$on("$routeChangeSuccess", function() { - self.original = null; - self.originalPath = $location.path(); - }); + $window.onbeforeunload = function() { + if (self.ignoreChanges()) { return; } + if (self.hasChanges()) { + return "There are unsaved changes to this dashboard"; + } + }; - this.ignoreChanges = function() { - 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) { + scope.$on("$locationChangeStart", function(event, next) { // check if we should look for changes if (self.originalPath === $location.path()) { return true; } if (self.ignoreChanges()) { return true; } - if (self.has_unsaved_changes()) { + if (self.hasChanges()) { event.preventDefault(); 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() { - var confirmModal = $modal({ - 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) { + // remove stuff that should not count in diff + p.cleanDashboardFromIgnoredChanges = function(dash) { dash.rows = _.filter(dash.rows, function(row) { if (row.repeatRowId) { - console.log('filtering out row'); return false; } @@ -101,13 +79,9 @@ function(angular, _, config) { }); }; - this.has_unsaved_changes = function() { - if (!self.original) { - return false; - } - - var current = self.current.getSaveModelClone(); - var original = self.original; + p.hasChanges = function() { + var current = this.current.getSaveModelClone(); + var original = this.original; // ignore timespan changes current.time = original.time = {}; @@ -126,8 +100,8 @@ function(angular, _, config) { } }); - this.cleanDashboardFromRepeatedPanelsAndRows(current); - this.cleanDashboardFromRepeatedPanelsAndRows(original); + this.cleanDashboardFromIgnoredChanges(current); + this.cleanDashboardFromIgnoredChanges(original); // ignore some panel and row stuff current.forEachPanel(function(panel, panelIndex, row, rowIndex) { @@ -165,28 +139,43 @@ function(angular, _, config) { 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 nextUrl = self.next.substring(baseLen); + var nextUrl = this.next.substring(baseLen); $location.url(nextUrl); }; - modalScope.ignore = function() { - self.original = null; - self.goto_next(); + this.Tracker = Tracker; + this.init = function(dashboard, scope) { + // 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(); }); }); diff --git a/public/test/specs/unsavedChangesSrv-specs.js b/public/test/specs/unsavedChangesSrv-specs.js new file mode 100644 index 00000000000..33691e17a0b --- /dev/null +++ b/public/test/specs/unsavedChangesSrv-specs.js @@ -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); + }); + + }); +}); diff --git a/public/test/test-main.js b/public/test/test-main.js index 2dc85cc65ef..9f69735d782 100644 --- a/public/test/test-main.js +++ b/public/test/test-main.js @@ -141,6 +141,7 @@ require([ 'specs/dashboardViewStateSrv-specs', 'specs/soloPanelCtrl-specs', 'specs/dynamicDashboardSrv-specs', + 'specs/unsavedChangesSrv-specs', ]; var pluginSpecs = (config.plugins.specs || []).map(function (spec) {