diff --git a/platform/commonUI/browse/src/navigation/NavigateAction.js b/platform/commonUI/browse/src/navigation/NavigateAction.js index 775f00c8bb..1da83307e5 100644 --- a/platform/commonUI/browse/src/navigation/NavigateAction.js +++ b/platform/commonUI/browse/src/navigation/NavigateAction.js @@ -44,10 +44,6 @@ define( * navigation has been updated */ NavigateAction.prototype.perform = function () { - var self = this, - navigateTo = this.domainObject, - currentObject = self.navigationService.getNavigation(); - if (this.navigationService.shouldNavigate()) { this.navigationService.setNavigation(this.domainObject, true); return Promise.resolve({}); diff --git a/platform/commonUI/browse/test/BrowseControllerSpec.js b/platform/commonUI/browse/test/BrowseControllerSpec.js index 18a59f0ea9..42490b7de4 100644 --- a/platform/commonUI/browse/test/BrowseControllerSpec.js +++ b/platform/commonUI/browse/test/BrowseControllerSpec.js @@ -24,8 +24,14 @@ * MCTRepresentationSpec. Created by vwoeltje on 11/6/14. */ define( - ["../src/BrowseController"], - function (BrowseController) { + [ + "../src/BrowseController", + "../src/navigation/NavigationService" + ], + function ( + BrowseController, + NavigationService + ) { describe("The browse controller", function () { var mockScope, @@ -44,7 +50,7 @@ define( function waitsForNavigation() { var calls = mockNavigationService.setNavigation.calls.length; waitsFor(function () { - return mockNavigationService.setNavigation.calls.length > calls ; + return mockNavigationService.setNavigation.calls.length > calls; }); } @@ -92,15 +98,16 @@ define( "objectService", ["getObjects"] ); - mockNavigationService = jasmine.createSpyObj( - "navigationService", - [ - "getNavigation", - "setNavigation", - "addListener", - "removeListener" - ] - ); + mockNavigationService = new NavigationService({}); + [ + "getNavigation", + "setNavigation", + "addListener", + "removeListener" + ].forEach(function (method) { + spyOn(mockNavigationService, method) + .andCallThrough(); + }); mockRootObject = jasmine.createSpyObj( "rootObjectContainer", ["getId", "getCapability", "getModel", "useCapability", "hasCapability"] diff --git a/platform/commonUI/browse/test/navigation/NavigateActionSpec.js b/platform/commonUI/browse/test/navigation/NavigateActionSpec.js index f9b756eeaa..1299a0021e 100644 --- a/platform/commonUI/browse/test/navigation/NavigateActionSpec.js +++ b/platform/commonUI/browse/test/navigation/NavigateActionSpec.js @@ -23,145 +23,74 @@ /** * MCTRepresentationSpec. Created by vwoeltje on 11/6/14. */ -define( - ["../../src/navigation/NavigateAction"], - function (NavigateAction) { +define([ + "../../src/navigation/NavigateAction" +], function ( + NavigateAction +) { - describe("The navigate action", function () { - var mockNavigationService, - mockQ, - mockDomainObject, - mockPolicyService, - mockNavigatedObject, - mockWindow, - capabilities, - action; + describe("The navigate action", function () { + var mockNavigationService, + mockDomainObject, + action; - function mockPromise(value) { - return { - then: function (callback) { - return mockPromise(callback(value)); - } - }; - } - beforeEach(function () { - capabilities = {}; - - mockQ = { when: mockPromise }; - mockNavigatedObject = jasmine.createSpyObj( - "domainObject", - [ - "getId", - "getModel", - "hasCapability", - "getCapability" - ] - ); - - capabilities.editor = jasmine.createSpyObj("editorCapability", [ - "isEditContextRoot", - "finish" - ]); - - mockNavigatedObject.getCapability.andCallFake(function (capability) { - return capabilities[capability]; - }); - mockNavigatedObject.hasCapability.andReturn(false); - - mockNavigationService = jasmine.createSpyObj( - "navigationService", - [ - "setNavigation", - "getNavigation" - ] - ); - mockNavigationService.getNavigation.andReturn(mockNavigatedObject); - - mockDomainObject = jasmine.createSpyObj( - "domainObject", - [ - "getId", - "getModel" - ] - ); - - mockPolicyService = jasmine.createSpyObj("policyService", - [ - "allow" - ]); - mockWindow = jasmine.createSpyObj("$window", - [ - "confirm" - ]); - - action = new NavigateAction( - mockNavigationService, - mockQ, - mockPolicyService, - mockWindow, - { domainObject: mockDomainObject } - ); + function waitForCall() { + var called = false; + waitsFor(function () { + return called; }); + return function () { + called = true; + }; + } - it("invokes the policy service to determine if navigation" + - " allowed", function () { - action.perform(); - expect(mockPolicyService.allow) - .toHaveBeenCalledWith("navigation", jasmine.any(Object), jasmine.any(Object), jasmine.any(Function)); - }); + beforeEach(function () { + mockNavigationService = jasmine.createSpyObj( + "navigationService", + [ + "shouldNavigate", + "setNavigation" + ] + ); - it("prompts user if policy rejection", function () { - action.perform(); - expect(mockPolicyService.allow).toHaveBeenCalled(); - mockPolicyService.allow.mostRecentCall.args[3](); - expect(mockWindow.confirm).toHaveBeenCalled(); - }); - - describe("shows a prompt", function () { - beforeEach(function () { - // Ensure the allow callback is called synchronously - mockPolicyService.allow.andCallFake(function () { - return arguments[3](); - }); - }); - it("does not navigate on prompt rejection", function () { - mockWindow.confirm.andReturn(false); - action.perform(); - expect(mockNavigationService.setNavigation) - .not.toHaveBeenCalled(); - }); - - it("does navigate on prompt acceptance", function () { - mockWindow.confirm.andReturn(true); - action.perform(); - expect(mockNavigationService.setNavigation) - .toHaveBeenCalled(); - }); - }); - - describe("in edit mode", function () { - beforeEach(function () { - mockNavigatedObject.hasCapability.andCallFake(function (capability) { - return capability === "editor"; - }); - capabilities.editor.isEditContextRoot.andReturn(true); - }); - - it("finishes editing if in edit mode", function () { - action.perform(); - expect(capabilities.editor.finish) - .toHaveBeenCalled(); - }); - }); - - it("is only applicable when a domain object is in context", function () { - expect(NavigateAction.appliesTo({})).toBeFalsy(); - expect(NavigateAction.appliesTo({ - domainObject: mockDomainObject - })).toBeTruthy(); - }); + mockDomainObject = {}; + action = new NavigateAction( + mockNavigationService, + { domainObject: mockDomainObject } + ); }); - } -); + + it("sets navigation if it is allowed", function () { + mockNavigationService.shouldNavigate.andReturn(true); + action.perform() + .then(waitForCall()); + runs(function () { + expect(mockNavigationService.setNavigation) + .toHaveBeenCalledWith(mockDomainObject, true); + }); + }); + + it("does not set navigation if it is not allowed", function () { + mockNavigationService.shouldNavigate.andReturn(false); + var onSuccess = jasmine.createSpy('onSuccess'); + action.perform() + .then(onSuccess, waitForCall()); + runs(function () { + expect(onSuccess).not.toHaveBeenCalled(); + expect(mockNavigationService.setNavigation) + .not + .toHaveBeenCalledWith(mockDomainObject); + }); + }); + + it("is only applicable when a domain object is in context", function () { + expect(NavigateAction.appliesTo({})).toBeFalsy(); + expect(NavigateAction.appliesTo({ + domainObject: mockDomainObject + })).toBeTruthy(); + }); + + }); +}); diff --git a/platform/commonUI/browse/test/navigation/NavigationServiceSpec.js b/platform/commonUI/browse/test/navigation/NavigationServiceSpec.js index 8f03a3f090..5a2cb63f0b 100644 --- a/platform/commonUI/browse/test/navigation/NavigationServiceSpec.js +++ b/platform/commonUI/browse/test/navigation/NavigationServiceSpec.js @@ -28,10 +28,12 @@ define( function (NavigationService) { describe("The navigation service", function () { - var navigationService; + var $window, + navigationService; beforeEach(function () { - navigationService = new NavigationService(); + $window = jasmine.createSpyObj('$window', ['confirm']); + navigationService = new NavigationService($window); }); it("stores navigation state", function () { diff --git a/platform/commonUI/edit/src/controllers/EditObjectController.js b/platform/commonUI/edit/src/controllers/EditObjectController.js index e2d4b5e60a..86438b1ae9 100644 --- a/platform/commonUI/edit/src/controllers/EditObjectController.js +++ b/platform/commonUI/edit/src/controllers/EditObjectController.js @@ -70,7 +70,7 @@ define( cancelEditing(domainObject); }); - function setViewForDomainObject(domainObject) { + function setViewForDomainObject() { var locationViewKey = $location.search().view; @@ -87,7 +87,7 @@ define( } } - setViewForDomainObject($scope.domainObject); + setViewForDomainObject(); $scope.doAction = function (action) { return $scope[action] && $scope[action](); diff --git a/platform/commonUI/edit/test/controllers/EditControllerSpec.js b/platform/commonUI/edit/test/controllers/EditObjectControllerSpec.js similarity index 61% rename from platform/commonUI/edit/test/controllers/EditControllerSpec.js rename to platform/commonUI/edit/test/controllers/EditObjectControllerSpec.js index c02d694b08..60c3d9a8ee 100644 --- a/platform/commonUI/edit/test/controllers/EditControllerSpec.js +++ b/platform/commonUI/edit/test/controllers/EditObjectControllerSpec.js @@ -24,32 +24,19 @@ define( ["../../src/controllers/EditObjectController"], function (EditObjectController) { - describe("The Edit mode controller", function () { + describe("The Edit Object controller", function () { var mockScope, mockObject, - mockType, + testViews, + mockEditorCapability, mockLocation, + mockNavigationService, + removeCheck, mockStatusCapability, mockCapabilities, - mockPolicyService, 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 () { - mockPolicyService = jasmine.createSpyObj( - "policyService", - [ - "allow" - ] - ); mockScope = jasmine.createSpyObj( "$scope", ["$on", "$watch"] @@ -58,16 +45,16 @@ define( "domainObject", ["getId", "getModel", "getCapability", "hasCapability", "useCapability"] ); - mockType = jasmine.createSpyObj( - "type", - ["hasFeature"] + mockEditorCapability = jasmine.createSpyObj( + "mockEditorCapability", + ["isEditContextRoot", "dirty", "finish"] ); mockStatusCapability = jasmine.createSpyObj('statusCapability', ["get"] ); mockCapabilities = { - "type" : mockType, + "editor" : mockEditorCapability, "status": mockStatusCapability }; @@ -75,52 +62,70 @@ define( ["search"] ); mockLocation.search.andReturn({"view": "fixed"}); + mockNavigationService = jasmine.createSpyObj('navigationService', + ["checkBeforeNavigation"] + ); + + removeCheck = jasmine.createSpy('removeCheck'); + mockNavigationService.checkBeforeNavigation.andReturn(removeCheck); mockObject.getId.andReturn("test"); mockObject.getModel.andReturn({ name: "Test object" }); mockObject.getCapability.andCallFake(function (key) { return mockCapabilities[key]; }); - mockType.hasFeature.andReturn(true); - mockScope.domainObject = mockObject; - - controller = new EditObjectController( - mockScope, - mockLocation, - mockPolicyService - ); - }); - - it("exposes a warning message for unload", function () { - var errorMessage = "Unsaved changes"; - - // Normally, should be undefined - expect(controller.getUnloadWarning()).toBeUndefined(); - - // Override the policy service to prevent navigation - mockPolicyService.allow.andCallFake(function (category, object, context, callback) { - callback(errorMessage); - }); - - // Should have some warning message here now - expect(controller.getUnloadWarning()).toEqual(errorMessage); - }); - - - it("sets the active view from query parameters", function () { - var testViews = [ - { key: 'abc' }, - { key: 'def', someKey: 'some value' }, - { key: 'xyz' } - ]; + 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); + mockScope.domainObject = mockObject; + + controller = new EditObjectController( + mockScope, + mockLocation, + mockNavigationService + ); + }); + + it("adds a check before navigation", function () { + expect(mockNavigationService.checkBeforeNavigation) + .toHaveBeenCalledWith(jasmine.any(Function)); + + var checkFn = mockNavigationService.checkBeforeNavigation.mostRecentCall.args[0]; + + mockEditorCapability.isEditContextRoot.andReturn(false); + mockEditorCapability.dirty.andReturn(false); + + expect(checkFn()).toBe(false); + + mockEditorCapability.isEditContextRoot.andReturn(true); + expect(checkFn()).toBe(false); + + mockEditorCapability.dirty.andReturn(true); + expect(checkFn()) + .toBe("Continuing will cause the loss of any unsaved changes."); + + }); + + it("cleans up on destroy", function () { + expect(mockScope.$on) + .toHaveBeenCalledWith("$destroy", jasmine.any(Function)); + + mockScope.$on.mostRecentCall.args[1](); + + expect(mockEditorCapability.finish).toHaveBeenCalled(); + expect(removeCheck).toHaveBeenCalled(); + }); + + it("sets the active view from query parameters", function () { expect(mockScope.representation.selected) .toEqual(testViews[1]); }); diff --git a/platform/commonUI/edit/test/representers/EditRepresenterSpec.js b/platform/commonUI/edit/test/representers/EditRepresenterSpec.js deleted file mode 100644 index cb766f95a7..0000000000 --- a/platform/commonUI/edit/test/representers/EditRepresenterSpec.js +++ /dev/null @@ -1,122 +0,0 @@ -/***************************************************************************** - * Open MCT, Copyright (c) 2014-2016, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT is licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * Open MCT includes source code licensed under additional open source - * licenses. See the Open Source Licenses file (LICENSES.md) included with - * this source code distribution or the Licensing information page available - * at runtime from the About dialog for additional information. - *****************************************************************************/ - -define( - ["../../src/representers/EditRepresenter"], - function (EditRepresenter) { - - describe("The Edit mode representer", function () { - var mockQ, - mockLog, - mockScope, - testRepresentation, - mockDomainObject, - mockStatusCapability, - mockEditorCapability, - mockCapabilities, - representer; - - function mockPromise(value) { - return { - then: function (callback) { - return mockPromise(callback(value)); - } - }; - } - - beforeEach(function () { - mockQ = { when: mockPromise }; - mockLog = jasmine.createSpyObj("$log", ["info", "debug"]); - mockScope = jasmine.createSpyObj("$scope", ["$watch", "$on"]); - testRepresentation = { key: "test" }; - mockDomainObject = jasmine.createSpyObj("domainObject", [ - "getId", - "getModel", - "getCapability", - "useCapability", - "hasCapability" - ]); - mockStatusCapability = - jasmine.createSpyObj("statusCapability", ["listen"]); - mockEditorCapability = - jasmine.createSpyObj("editorCapability", ["isEditContextRoot"]); - - mockCapabilities = { - 'status': mockStatusCapability, - 'editor': mockEditorCapability - }; - - mockDomainObject.getModel.andReturn({}); - mockDomainObject.hasCapability.andReturn(true); - mockDomainObject.useCapability.andReturn(true); - mockDomainObject.getCapability.andCallFake(function (capability) { - return mockCapabilities[capability]; - }); - - representer = new EditRepresenter(mockQ, mockLog, mockScope); - representer.represent(testRepresentation, mockDomainObject); - }); - - it("provides a commit method in scope", function () { - expect(mockScope.commit).toEqual(jasmine.any(Function)); - }); - - it("Sets edit view template on edit mode", function () { - mockStatusCapability.listen.mostRecentCall.args[0](['editing']); - mockEditorCapability.isEditContextRoot.andReturn(true); - 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 upon observed changes", function () { - mockScope.model = { someKey: "some value" }; - mockScope.configuration = { someConfiguration: "something" }; - - mockScope.commit("Some message"); - - // Should have mutated the object... - expect(mockDomainObject.useCapability).toHaveBeenCalledWith( - "mutation", - jasmine.any(Function) - ); - - // Finally, check that the provided mutation function - // includes both model and configuration - expect( - mockDomainObject.useCapability.mostRecentCall.args[1]() - ).toEqual({ - someKey: "some value", - configuration: { - test: { someConfiguration: "something" } - } - }); - }); - - - }); - } -); diff --git a/platform/commonUI/general/src/directives/MCTTree.js b/platform/commonUI/general/src/directives/MCTTree.js index cae72d497b..d5ab64fc30 100644 --- a/platform/commonUI/general/src/directives/MCTTree.js +++ b/platform/commonUI/general/src/directives/MCTTree.js @@ -29,7 +29,7 @@ define([ if (!scope.allowSelection) { scope.allowSelection = function () { return true; - } + }; } if (!scope.onSelection) { scope.onSelection = function () {}; diff --git a/platform/commonUI/general/test/directives/MCTTreeSpec.js b/platform/commonUI/general/test/directives/MCTTreeSpec.js index bd8e17117a..3512731429 100644 --- a/platform/commonUI/general/test/directives/MCTTreeSpec.js +++ b/platform/commonUI/general/test/directives/MCTTreeSpec.js @@ -19,10 +19,12 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ +/* global console*/ define([ - '../../src/directives/MCTTree' -], function (MCTTree) { + '../../src/directives/MCTTree', + '../../src/ui/TreeView' +], function (MCTTree, TreeView) { describe("The mct-tree directive", function () { var mockParse, mockGestureService, @@ -50,6 +52,7 @@ define([ mockExpr = jasmine.createSpy('expr'); mockExpr.assign = jasmine.createSpy('assign'); mockParse.andReturn(mockExpr); + spyOn(TreeView.prototype, 'observe').andCallThrough(); mctTree = new MCTTree(mockParse, mockGestureService); }); @@ -58,8 +61,13 @@ define([ expect(mctTree.restrict).toEqual("E"); }); - it("two-way binds to mctObject and mctModel", function () { - expect(mctTree.scope).toEqual({ mctObject: "=", mctModel: "=" }); + it("two-way binds", function () { + expect(mctTree.scope).toEqual({ + rootObject: "=", + selectedObject: "=", + allowSelection: "=?", + onSelection: "=?" + }); }); describe("link", function () { @@ -81,16 +89,16 @@ define([ expect(mockElement.append).toHaveBeenCalled(); }); - it("watches for mct-model's expression in the parent", function () { + it("watches for selected-object expression in the parent", function () { expect(mockScope.$watch).toHaveBeenCalledWith( - "mctModel", + "selectedObject", jasmine.any(Function) ); }); - it("watches for changes to mct-object", function () { + it("watches for changes to root-object", function () { expect(mockScope.$watch).toHaveBeenCalledWith( - "mctObject", + "rootObject", jasmine.any(Function) ); }); @@ -102,6 +110,10 @@ define([ ); }); + it("watches for changes in tree view", function () { + + }); + // https://github.com/nasa/openmct/issues/1114 it("does not trigger $apply during $watches", function () { mockScope.mctObject = makeMockDomainObject('root'); @@ -111,14 +123,18 @@ define([ }); expect(mockScope.$apply).not.toHaveBeenCalled(); }); - it("does trigger $apply from other value changes", function () { + it("does trigger $apply from tree manipulation", function () { + if (/PhantomJS/g.test(window.navigator.userAgent)) { + console.log('Unable to run test in PhantomJS due to lack of support for event constructors'); + return; + } // White-boxy; we know this is the setter for the tree's value - var treeValueFn = mockScope.$watch.calls[0].args[1]; + var treeValueFn = TreeView.prototype.observe.calls[0].args[0]; mockScope.mctObject = makeMockDomainObject('root'); mockScope.mctMode = makeMockDomainObject('selection'); - treeValueFn(makeMockDomainObject('other')); + treeValueFn(makeMockDomainObject('other'), new MouseEvent("click")); expect(mockScope.$apply).toHaveBeenCalled(); }); diff --git a/platform/commonUI/general/test/ui/TreeViewSpec.js b/platform/commonUI/general/test/ui/TreeViewSpec.js index 4337bc5bcd..ef8514a60b 100644 --- a/platform/commonUI/general/test/ui/TreeViewSpec.js +++ b/platform/commonUI/general/test/ui/TreeViewSpec.js @@ -289,8 +289,9 @@ define([ }); it("notifies listeners when value is changed", function () { - treeView.value(mockDomainObject); - expect(mockCallback).toHaveBeenCalledWith(mockDomainObject); + treeView.value(mockDomainObject, {some: event}); + expect(mockCallback) + .toHaveBeenCalledWith(mockDomainObject, {some: event}); }); it("does not notify listeners when deactivated", function () {