diff --git a/platform/commonUI/browse/bundle.js b/platform/commonUI/browse/bundle.js index 1fcd810b96..cf4ee5b7a0 100644 --- a/platform/commonUI/browse/bundle.js +++ b/platform/commonUI/browse/bundle.js @@ -111,7 +111,6 @@ define([ "$scope", "$route", "$location", - "$q", "objectService", "navigationService", "urlService", diff --git a/platform/commonUI/browse/res/templates/browse.html b/platform/commonUI/browse/res/templates/browse.html index 08a5cd4d5e..75fcaaeb0a 100644 --- a/platform/commonUI/browse/res/templates/browse.html +++ b/platform/commonUI/browse/res/templates/browse.html @@ -72,7 +72,7 @@
diff --git a/platform/commonUI/browse/src/BrowseController.js b/platform/commonUI/browse/src/BrowseController.js index f65333870d..6013772965 100644 --- a/platform/commonUI/browse/src/BrowseController.js +++ b/platform/commonUI/browse/src/BrowseController.js @@ -49,7 +49,6 @@ define( $scope, $route, $location, - $q, objectService, navigationService, urlService, diff --git a/platform/commonUI/browse/src/navigation/NavigationService.js b/platform/commonUI/browse/src/navigation/NavigationService.js index 1d43971166..858a5d80eb 100644 --- a/platform/commonUI/browse/src/navigation/NavigationService.js +++ b/platform/commonUI/browse/src/navigation/NavigationService.js @@ -57,7 +57,7 @@ define( NavigationService.prototype.setNavigation = function (value) { var canNavigate = true; if (this.navigated !== value) { - canNavigate = (this.callbacks['before'] || []) + canNavigate = (this.callbacks.before || []) .reduce(function (previous, callback) { //Check whether the callback returned a value of // 'false' indicating that navigation should not @@ -67,7 +67,7 @@ define( }, true); if (canNavigate) { this.navigated = value; - this.callbacks['after'].forEach(function (callback) { + (this.callbacks.after || []).forEach(function (callback) { callback(value); }); } diff --git a/platform/commonUI/browse/test/BrowseControllerSpec.js b/platform/commonUI/browse/test/BrowseControllerSpec.js index e4fad10544..caab0ed7d8 100644 --- a/platform/commonUI/browse/test/BrowseControllerSpec.js +++ b/platform/commonUI/browse/test/BrowseControllerSpec.js @@ -29,8 +29,7 @@ define( function (BrowseController) { "use strict"; - //TODO: Disabled for NEM Beta - xdescribe("The browse controller", function () { + describe("The browse controller", function () { var mockScope, mockRoute, mockLocation, @@ -214,7 +213,10 @@ define( // prior to setting $route.current mockLocation.path.andReturn("/browse/"); + mockNavigationService.setNavigation.andReturn(true); + // Exercise the Angular workaround + mockNavigationService.addListener.mostRecentCall.args[0](); mockScope.$on.mostRecentCall.args[1](); expect(mockUnlisten).toHaveBeenCalled(); @@ -225,6 +227,32 @@ define( ); }); + it("after successful navigation event sets the selected tree " + + "object", function () { + mockScope.navigatedObject = mockDomainObject; + mockNavigationService.setNavigation.andReturn(true); + + //Simulate a change in selected tree object + mockScope.treeModel = {selectedObject: mockDomainObject}; + mockScope.$watch.mostRecentCall.args[1](mockNextObject); + + expect(mockScope.treeModel.selectedObject).toBe(mockNextObject); + expect(mockScope.treeModel.selectedObject).not.toBe(mockDomainObject); + }); + + it("after failed navigation event resets the selected tree" + + " object", function () { + mockScope.navigatedObject = mockDomainObject; + mockNavigationService.setNavigation.andReturn(false); + + //Simulate a change in selected tree object + mockScope.treeModel = {selectedObject: mockDomainObject}; + mockScope.$watch.mostRecentCall.args[1](mockNextObject); + + expect(mockScope.treeModel.selectedObject).not.toBe(mockNextObject); + expect(mockScope.treeModel.selectedObject).toBe(mockDomainObject); + }); + }); } ); diff --git a/platform/commonUI/browse/test/navigation/NavigationServiceSpec.js b/platform/commonUI/browse/test/navigation/NavigationServiceSpec.js index 6d7f3fd20d..3a354dee78 100644 --- a/platform/commonUI/browse/test/navigation/NavigationServiceSpec.js +++ b/platform/commonUI/browse/test/navigation/NavigationServiceSpec.js @@ -84,6 +84,24 @@ define( expect(callback).not.toHaveBeenCalled(); }); + it("adds listeners to the 'after' state by default", function(){ + expect(navigationService.callbacks.after).toBeUndefined(); + navigationService.addListener(function(){}); + expect(navigationService.callbacks.after).toBeDefined(); + expect(navigationService.callbacks.after.length).toBe(1); + }); + + it("allows navigationService events to be prevented", function(){ + var callback = jasmine.createSpy("callback"), + navigationResult; + callback.andReturn(false); + navigationService.addListener(callback, "before"); + navigationResult = navigationService.setNavigation({}); + expect(callback).toHaveBeenCalled(); + expect(navigationResult).toBe(false); + + }); + }); } -); \ No newline at end of file +); diff --git a/platform/commonUI/edit/src/representers/EditRepresenter.js b/platform/commonUI/edit/src/representers/EditRepresenter.js index c062e6387f..5d96aeaf78 100644 --- a/platform/commonUI/edit/src/representers/EditRepresenter.js +++ b/platform/commonUI/edit/src/representers/EditRepresenter.js @@ -102,6 +102,11 @@ define( scope.commit = commit; scope.setEditable = setEditable; + // Clean up when the scope is destroyed + scope.$on("$destroy", function () { + self.destroy(); + }); + } // Handle a specific representation of a specific domain object @@ -119,8 +124,7 @@ define( this.destroy(); function setEditing(){ - scope.viewRegionTemplate = 'edit-object'; - scope.inspectorRegionTemplate = 'inspector-edit' + scope.viewObjectTemplate = 'edit-object'; } /** @@ -132,7 +136,7 @@ define( if (statuses.indexOf('editing')!=-1){ setEditing(); } else { - delete scope.viewRegionTemplate; + delete scope.viewObjectTemplate; } }); @@ -144,7 +148,7 @@ define( // Respond to the destruction of the current representation. EditRepresenter.prototype.destroy = function destroy() { // Nothing to clean up - this.listenHandle && this.listenHandle(); + return this.listenHandle && this.listenHandle(); }; return EditRepresenter; diff --git a/platform/commonUI/edit/test/controllers/EditControllerSpec.js b/platform/commonUI/edit/test/controllers/EditControllerSpec.js index 4e66cebd08..0a39874eb8 100644 --- a/platform/commonUI/edit/test/controllers/EditControllerSpec.js +++ b/platform/commonUI/edit/test/controllers/EditControllerSpec.js @@ -22,89 +22,72 @@ /*global define,describe,it,expect,beforeEach,jasmine*/ define( - ["../../src/controllers/EditController"], - function (EditController) { + ["../../src/controllers/EditObjectController"], + function (EditObjectController) { "use strict"; describe("The Edit mode controller", function () { var mockScope, - mockQ, - mockNavigationService, mockObject, mockType, + mockLocation, + mockStatusCapability, + mockCapabilities, controller; + // Utility function; look for a $watch on scope and fire it + function fireWatch(expr, value) { + mockScope.$watch.calls.forEach(function (call) { + if (call.args[0] === expr) { + call.args[1](value); + } + }); + } + beforeEach(function () { mockScope = jasmine.createSpyObj( "$scope", - [ "$on" ] - ); - mockQ = jasmine.createSpyObj('$q', ['when', 'all']); - mockNavigationService = jasmine.createSpyObj( - "navigationService", - [ "getNavigation", "addListener", "removeListener" ] + [ "$on", "$watch" ] ); mockObject = jasmine.createSpyObj( "domainObject", - [ "getId", "getModel", "getCapability", "hasCapability" ] + [ "getId", "getModel", "getCapability", "hasCapability", "useCapability" ] ); mockType = jasmine.createSpyObj( "type", [ "hasFeature" ] ); + mockStatusCapability = jasmine.createSpyObj('statusCapability', + ["get"] + ); + + mockCapabilities = { + "type" : mockType, + "status": mockStatusCapability + }; + + mockLocation = jasmine.createSpyObj('$location', + ["search"] + ); + mockLocation.search.andReturn({"view": "fixed"}); - mockNavigationService.getNavigation.andReturn(mockObject); mockObject.getId.andReturn("test"); mockObject.getModel.andReturn({ name: "Test object" }); mockObject.getCapability.andCallFake(function (key) { - return key === 'type' && mockType; + return mockCapabilities[key]; }); mockType.hasFeature.andReturn(true); - controller = new EditController( + mockScope.domainObject = mockObject; + + controller = new EditObjectController( mockScope, - mockQ, - mockNavigationService + mockLocation ); }); - it("exposes the currently-navigated object", function () { - expect(controller.navigatedObject()).toBeDefined(); - expect(controller.navigatedObject().getId()).toEqual("test"); - }); - - it("adds an editor capability to the navigated object", function () { - // Should provide an editor capability... - expect(controller.navigatedObject().getCapability("editor")) - .toBeDefined(); - // Shouldn't have been the mock capability we provided - expect(controller.navigatedObject().getCapability("editor")) - .not.toEqual(mockType); - }); - - it("detaches its navigation listener when destroyed", function () { - var navCallback = mockNavigationService - .addListener.mostRecentCall.args[0]; - - expect(mockScope.$on).toHaveBeenCalledWith( - "$destroy", - jasmine.any(Function) - ); - - // Verify precondition - expect(mockNavigationService.removeListener) - .not.toHaveBeenCalled(); - - // Trigger destroy - mockScope.$on.mostRecentCall.args[1](); - - // Listener should have been removed - expect(mockNavigationService.removeListener) - .toHaveBeenCalledWith(navCallback); - }); - it("exposes a warning message for unload", function () { - var obj = controller.navigatedObject(), + var obj = mockObject, mockEditor = jasmine.createSpyObj('editor', ['dirty']); // Normally, should be undefined @@ -112,14 +95,32 @@ define( // Override the object's editor capability, make it look // like there are unsaved changes. - obj.getCapability = jasmine.createSpy(); - obj.getCapability.andReturn(mockEditor); + mockCapabilities.editor = mockEditor; mockEditor.dirty.andReturn(true); + mockStatusCapability.get.andReturn(true); // Should have some warning message here now expect(controller.getUnloadWarning()).toEqual(jasmine.any(String)); }); + + it("sets the active view from query parameters", function () { + var testViews = [ + { key: 'abc' }, + { key: 'def', someKey: 'some value' }, + { key: 'xyz' } + ]; + + mockObject.useCapability.andCallFake(function (c) { + return (c === 'view') && testViews; + }); + mockLocation.search.andReturn({ view: 'def' }); + + fireWatch('domainObject', mockObject); + expect(mockScope.representation.selected) + .toEqual(testViews[1]); + }); + }); } ); diff --git a/platform/commonUI/edit/test/directives/MCTBeforeUnloadSpec.js b/platform/commonUI/edit/test/directives/MCTBeforeUnloadSpec.js index 41070d76f5..fd94e3e90d 100644 --- a/platform/commonUI/edit/test/directives/MCTBeforeUnloadSpec.js +++ b/platform/commonUI/edit/test/directives/MCTBeforeUnloadSpec.js @@ -31,6 +31,7 @@ define( mockScope, testAttrs, mockEvent, + mockNavigationService, directive; function fireListener(eventType, value) { @@ -46,7 +47,8 @@ define( mockScope = jasmine.createSpyObj("$scope", ['$eval', '$on']); testAttrs = { mctBeforeUnload: "someExpression" }; mockEvent = jasmine.createSpyObj("event", ["preventDefault"]); - directive = new MCTBeforeUnload(mockWindow); + mockNavigationService = jasmine.createSpyObj("navigationService", ["addListener", "removeListener"]); + directive = new MCTBeforeUnload(mockWindow, mockNavigationService); directive.link(mockScope, {}, testAttrs); }); @@ -65,6 +67,10 @@ define( ); }); + it("listens for navigation changes", function () { + expect(mockNavigationService.addListener).toHaveBeenCalledWith(jasmine.any(Function), "before"); + }); + it("listens for its scope's destroy event", function () { expect(mockScope.$on).toHaveBeenCalledWith( "$destroy", @@ -108,9 +114,10 @@ define( it("cleans up listeners when destroyed", function () { fireListener("$destroy", mockEvent); expect(mockWindow.onbeforeunload).toBeUndefined(); + expect(mockNavigationService.removeListener).toHaveBeenCalled(); }); }); } -); \ No newline at end of file +); diff --git a/platform/commonUI/edit/test/representers/EditRepresenterSpec.js b/platform/commonUI/edit/test/representers/EditRepresenterSpec.js index 79b336202a..3dcaa34627 100644 --- a/platform/commonUI/edit/test/representers/EditRepresenterSpec.js +++ b/platform/commonUI/edit/test/representers/EditRepresenterSpec.js @@ -33,8 +33,8 @@ define( testRepresentation, mockDomainObject, mockPersistence, - mockCapabilities, mockStatusCapability, + mockCapabilities, representer; function mockPromise(value) { @@ -48,7 +48,7 @@ define( beforeEach(function () { mockQ = { when: mockPromise }; mockLog = jasmine.createSpyObj("$log", ["info", "debug"]); - mockScope = jasmine.createSpyObj("$scope", ["$watch"]); + mockScope = jasmine.createSpyObj("$scope", ["$watch", "$on"]); testRepresentation = { key: "test" }; mockDomainObject = jasmine.createSpyObj("domainObject", [ "getId", @@ -60,7 +60,7 @@ define( mockPersistence = jasmine.createSpyObj("persistence", ["persist"]); mockStatusCapability = - jasmine.createSpyObj("status", ["get"]); + jasmine.createSpyObj("statusCapability", ["get", "listen"]); mockStatusCapability.get.andReturn(false); mockCapabilities = { 'persistence': mockPersistence, @@ -82,6 +82,17 @@ define( expect(mockScope.commit).toEqual(jasmine.any(Function)); }); + it("Sets edit view template on edit mode", function () { + mockStatusCapability.listen.mostRecentCall.args[0](['editing']); + expect(mockScope.viewObjectTemplate).toEqual('edit-object'); + }); + + it("Cleans up listeners on scope destroy", function () { + representer.listenHandle = jasmine.createSpy('listen'); + mockScope.$on.mostRecentCall.args[1](); + expect(representer.listenHandle).toHaveBeenCalled(); + }); + it("mutates and persists upon observed changes", function () { mockScope.model = { someKey: "some value" }; mockScope.configuration = { someConfiguration: "something" }; @@ -112,4 +123,4 @@ define( }); } -); \ No newline at end of file +);