diff --git a/platform/commonUI/browse/test/BrowseControllerSpec.js b/platform/commonUI/browse/test/BrowseControllerSpec.js index 7b8aab90fa..9e7e5decfc 100644 --- a/platform/commonUI/browse/test/BrowseControllerSpec.js +++ b/platform/commonUI/browse/test/BrowseControllerSpec.js @@ -50,6 +50,7 @@ define( } beforeEach(function () { + mockScope = jasmine.createSpyObj( "$scope", [ "$on", "$watch" ] @@ -82,11 +83,11 @@ define( ); mockDomainObject = jasmine.createSpyObj( "domainObject", - [ "getId", "getCapability", "getModel", "useCapability" ] + [ "getId", "hasCapability", "getCapability", "getModel", "useCapability" ] ); mockNextObject = jasmine.createSpyObj( "nextObject", - [ "getId", "getCapability", "getModel", "useCapability" ] + [ "getId", "hasCapability", "getCapability", "getModel", "useCapability" ] ); mockObjectService.getObjects.andReturn(mockPromise({ @@ -98,9 +99,13 @@ define( mockDomainObject.useCapability.andReturn(mockPromise([ mockNextObject ])); + mockNextObject.useCapability.andReturn(undefined); mockNextObject.getId.andReturn("next"); + mockNextObject.hasCapability.andReturn(false); + mockDomainObject.getId.andReturn("mine"); + mockDomainObject.hasCapability.andReturn(false); controller = new BrowseController( mockScope, diff --git a/platform/commonUI/edit/bundle.json b/platform/commonUI/edit/bundle.json index 7da9697140..ca9b5ba480 100644 --- a/platform/commonUI/edit/bundle.json +++ b/platform/commonUI/edit/bundle.json @@ -38,7 +38,7 @@ { "key": "edit", "implementation": "actions/EditAction.js", - "depends": [ "$location", "$q", "navigationService", "$log" ], + "depends": [ "$q", "navigationService", "$log" ], "description": "Edit this object.", "category": "view-control", "glyph": "p" @@ -67,7 +67,7 @@ "implementation": "actions/SaveAction.js", "name": "Save", "description": "Save changes made to these objects.", - "depends": [ "$location", "navigationService" ], + "depends": [ "navigationService" ], "priority": "mandatory" }, { @@ -76,7 +76,7 @@ "implementation": "actions/CancelAction.js", "name": "Cancel", "description": "Discard changes made to these objects.", - "depends": [ "$location", "navigationService" ] + "depends": ["navigationService" ] } ], "policies": [ diff --git a/platform/commonUI/edit/src/actions/CancelAction.js b/platform/commonUI/edit/src/actions/CancelAction.js index 09fe533d48..be201c1c0c 100644 --- a/platform/commonUI/edit/src/actions/CancelAction.js +++ b/platform/commonUI/edit/src/actions/CancelAction.js @@ -33,9 +33,8 @@ define( * @memberof platform/commonUI/edit * @implements {Action} */ - function CancelAction($location, navigationService, context) { + function CancelAction(navigationService, context) { this.domainObject = context.domainObject; - this.$location = $location; this.navigationService = navigationService; } @@ -47,7 +46,6 @@ define( */ CancelAction.prototype.perform = function () { var domainObject = this.domainObject, - $location = this.$location, navigationService = this.navigationService; // Look up the object's "editor.completion" capability; @@ -82,7 +80,7 @@ define( CancelAction.appliesTo = function (context) { var domainObject = (context || {}).domainObject; return domainObject !== undefined && - domainObject.hasCapability("editor"); + domainObject.getCapability('status').get('editing'); }; return CancelAction; diff --git a/platform/commonUI/edit/src/actions/EditAction.js b/platform/commonUI/edit/src/actions/EditAction.js index 18dfa9fbd9..696c99021f 100644 --- a/platform/commonUI/edit/src/actions/EditAction.js +++ b/platform/commonUI/edit/src/actions/EditAction.js @@ -46,7 +46,7 @@ define( * @constructor * @implements {Action} */ - function EditAction($location, $q, navigationService, $log, context) { + function EditAction($q, navigationService, $log, context) { var domainObject = (context || {}).domainObject; // We cannot enter Edit mode if we have no domain object to @@ -63,17 +63,20 @@ define( } this.domainObject = domainObject; - this.$location = $location; this.navigationService = navigationService; this.$q = $q; } + EditAction.prototype.createEditableObject = function(domainObject) { + return new EditableDomainObject(domainObject, this.$q); + }; + /** * Enter edit mode. */ EditAction.prototype.perform = function () { this.domainObject.getCapability('status').set('editing', true); - this.navigationService.setNavigation(new EditableDomainObject(this.domainObject, this.$q)); + this.navigationService.setNavigation(this.createEditableObject(this.domainObject)); }; /** diff --git a/platform/commonUI/edit/src/actions/SaveAction.js b/platform/commonUI/edit/src/actions/SaveAction.js index 62c95abfac..65659b553f 100644 --- a/platform/commonUI/edit/src/actions/SaveAction.js +++ b/platform/commonUI/edit/src/actions/SaveAction.js @@ -34,9 +34,8 @@ define( * @implements {Action} * @memberof platform/commonUI/edit */ - function SaveAction($location, navigationService, context) { + function SaveAction(navigationService, context) { this.domainObject = (context || {}).domainObject; - this.$location = $location; this.navigationService = navigationService; } @@ -49,8 +48,6 @@ define( */ SaveAction.prototype.perform = function () { var domainObject = this.domainObject, - $location = this.$location, - urlService = this.urlService, navigationService = this.navigationService; // Invoke any save behavior introduced by the editor capability; @@ -65,10 +62,6 @@ define( // UI, which will have been pushed atop the Browise UI.) function returnToBrowse(nonEditableDomainObject) { navigationService.setNavigation(nonEditableDomainObject); - /*return $location.path(urlService.urlForLocation( - "browse", - domainObject - ));*/ } return doSave().then(returnToBrowse); @@ -83,7 +76,7 @@ define( SaveAction.appliesTo = function (context) { var domainObject = (context || {}).domainObject; return domainObject !== undefined && - domainObject.hasCapability("editor"); + domainObject.getCapability("status").get("editing"); }; return SaveAction; diff --git a/platform/commonUI/edit/src/capabilities/EditorCapability.js b/platform/commonUI/edit/src/capabilities/EditorCapability.js index 5cbc341d86..c752979100 100644 --- a/platform/commonUI/edit/src/capabilities/EditorCapability.js +++ b/platform/commonUI/edit/src/capabilities/EditorCapability.js @@ -104,7 +104,7 @@ define( return saveChanges().then(function(){ domainObject.getCapability('status').set('editing', false); return domainObject; - }) + }); }; /** @@ -131,7 +131,7 @@ define( EditorCapability.prototype.getDomainObject = function () { return this.domainObject; - } + }; return EditorCapability; } diff --git a/platform/commonUI/edit/test/actions/CancelActionSpec.js b/platform/commonUI/edit/test/actions/CancelActionSpec.js index 1701347165..8d683fe236 100644 --- a/platform/commonUI/edit/test/actions/CancelActionSpec.js +++ b/platform/commonUI/edit/test/actions/CancelActionSpec.js @@ -27,10 +27,11 @@ define( "use strict"; describe("The Cancel action", function () { - var mockLocation, - mockDomainObject, + var mockDomainObject, + mockCapabilities, mockEditorCapability, - mockUrlService, + mockStatusCapability, + mockNavigationService, actionContext, action; @@ -43,45 +44,50 @@ define( } beforeEach(function () { - mockLocation = jasmine.createSpyObj( - "$location", - [ "path" ] - ); mockDomainObject = jasmine.createSpyObj( "domainObject", - [ "getCapability", "hasCapability" ] + [ "getCapability" ] ); mockEditorCapability = jasmine.createSpyObj( "editor", [ "save", "cancel" ] ); - mockUrlService = jasmine.createSpyObj( - "urlService", - ["urlForLocation"] + mockStatusCapability = jasmine.createSpyObj( + "status", + [ "get"] ); - + mockNavigationService = jasmine.createSpyObj( + "navigationService", + ["setNavigation"] + ); + mockCapabilities = { + "editor": mockEditorCapability, + "status": mockStatusCapability + }; actionContext = { domainObject: mockDomainObject }; - mockDomainObject.hasCapability.andReturn(true); - mockDomainObject.getCapability.andReturn(mockEditorCapability); - mockEditorCapability.cancel.andReturn(mockPromise(true)); + mockDomainObject.getCapability.andCallFake(function(capability){ + return mockCapabilities[capability]; + }); - action = new CancelAction(mockLocation, mockUrlService, actionContext); + mockEditorCapability.cancel.andReturn(mockPromise(mockDomainObject)); + mockStatusCapability.get.andReturn(true); + + action = new CancelAction( mockNavigationService, actionContext); }); - it("only applies to domain object with an editor capability", function () { + it("only applies to domain object that is being edited", function () { expect(CancelAction.appliesTo(actionContext)).toBeTruthy(); - expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor"); - mockDomainObject.hasCapability.andReturn(false); - mockDomainObject.getCapability.andReturn(undefined); + mockStatusCapability.get.andReturn(false); expect(CancelAction.appliesTo(actionContext)).toBeFalsy(); }); - it("invokes the editor capability's save functionality when performed", function () { + it("invokes the editor capability's cancel functionality when" + + " performed", function () { // Verify precondition expect(mockEditorCapability.cancel).not.toHaveBeenCalled(); action.perform(); @@ -95,8 +101,8 @@ define( it("returns to browse when performed", function () { action.perform(); - expect(mockLocation.path).toHaveBeenCalledWith( - mockUrlService.urlForLocation("browse", mockDomainObject) + expect(mockNavigationService.setNavigation).toHaveBeenCalledWith( + mockDomainObject ); }); }); diff --git a/platform/commonUI/edit/test/actions/EditActionSpec.js b/platform/commonUI/edit/test/actions/EditActionSpec.js index c890fd7d92..0ba8571185 100644 --- a/platform/commonUI/edit/test/actions/EditActionSpec.js +++ b/platform/commonUI/edit/test/actions/EditActionSpec.js @@ -19,27 +19,41 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/*global define,describe,it,expect,beforeEach,jasmine*/ +/*global define,describe,it,expect,beforeEach,jasmine,spyOn*/ define( - ["../../src/actions/EditAction"], + [ + "../../src/actions/EditAction" + ], function (EditAction) { "use strict"; describe("The Edit action", function () { - var mockLocation, - mockNavigationService, + var mockNavigationService, mockLog, mockDomainObject, + mockStatusCapability, mockType, actionContext, + mockCapabilities, + mockQ, action; + function mockPromise(value) { + return { + then: function (callback) { + return mockPromise(callback(value)); + } + }; + } + beforeEach(function () { - mockLocation = jasmine.createSpyObj( - "$location", - [ "path" ] + + mockStatusCapability = jasmine.createSpyObj( + "statusCapability", + [ "get", "set" ] ); + mockNavigationService = jasmine.createSpyObj( "navigationService", [ "setNavigation", "getNavigation" ] @@ -50,20 +64,41 @@ define( ); mockDomainObject = jasmine.createSpyObj( "domainObject", - [ "getId", "getModel", "getCapability" ] + [ "getId", "getModel", "getCapability", "hasCapability" ] ); mockType = jasmine.createSpyObj( "type", [ "hasFeature" ] ); - mockDomainObject.getCapability.andReturn(mockType); + mockQ = jasmine.createSpyObj( + "q", + [ "when", "all" ] + ); + + mockQ.when.andReturn(function(value){ + return mockPromise(value); + }); + + mockQ.all.andReturn(mockPromise(undefined)); + + mockDomainObject.getCapability.andCallFake(function(capability){ + return mockCapabilities[capability]; + }); + mockDomainObject.getModel.andReturn({}); + mockDomainObject.getId.andReturn("testId"); + mockStatusCapability.get.andReturn(false); mockType.hasFeature.andReturn(true); + mockCapabilities = { + "status": mockStatusCapability, + "type": mockType + }; + actionContext = { domainObject: mockDomainObject }; action = new EditAction( - mockLocation, + mockQ, mockNavigationService, mockLog, actionContext @@ -77,15 +112,30 @@ define( expect(mockType.hasFeature).toHaveBeenCalledWith('creation'); }); - it("changes URL path to edit mode when performed", function () { + it("is only applicable when domain object is not in edit mode", function () { + // Indicates whether object is in edit mode + mockStatusCapability.get.andReturn(false); + expect(EditAction.appliesTo(actionContext)).toBeTruthy(); + mockStatusCapability.get.andReturn(true); + expect(EditAction.appliesTo(actionContext)).toBeFalsy(); + }); + + it("navigates to editable domain object", function () { + spyOn(action, 'createEditableObject'); + action.perform(); - expect(mockLocation.path).toHaveBeenCalledWith("/edit"); + + expect(mockNavigationService.setNavigation).toHaveBeenCalled(); + expect(action.createEditableObject).toHaveBeenCalled(); }); it("ensures that the edited object is navigated-to", function () { + var navigatedObject; action.perform(); - expect(mockNavigationService.setNavigation) - .toHaveBeenCalledWith(mockDomainObject); + navigatedObject = mockNavigationService.setNavigation.mostRecentCall.args[0]; + expect(navigatedObject.getId()) + .toEqual(mockDomainObject.getId()); + expect(navigatedObject).not.toBe(mockDomainObject); }); it("logs a warning if constructed when inapplicable", function () { @@ -94,7 +144,7 @@ define( // Should not have hit an exception... new EditAction( - mockLocation, + mockQ, mockNavigationService, mockLog, {} @@ -104,8 +154,6 @@ define( expect(mockLog.warn).toHaveBeenCalled(); // And should not have had other interactions - expect(mockLocation.path) - .not.toHaveBeenCalled(); expect(mockNavigationService.setNavigation) .not.toHaveBeenCalled(); }); diff --git a/platform/commonUI/edit/test/actions/SaveActionSpec.js b/platform/commonUI/edit/test/actions/SaveActionSpec.js index 74a0923411..0fbee0c68d 100644 --- a/platform/commonUI/edit/test/actions/SaveActionSpec.js +++ b/platform/commonUI/edit/test/actions/SaveActionSpec.js @@ -27,10 +27,11 @@ define( "use strict"; describe("The Save action", function () { - var mockLocation, - mockDomainObject, + var mockDomainObject, + mockNavigationService, + mockStatusCapability, mockEditorCapability, - mockUrlService, + mockCapabilities, actionContext, action; @@ -43,10 +44,6 @@ define( } beforeEach(function () { - mockLocation = jasmine.createSpyObj( - "$location", - [ "path" ] - ); mockDomainObject = jasmine.createSpyObj( "domainObject", [ "getCapability", "hasCapability" ] @@ -55,30 +52,39 @@ define( "editor", [ "save", "cancel" ] ); - mockUrlService = jasmine.createSpyObj( - "urlService", - ["urlForLocation"] + mockStatusCapability = jasmine.createSpyObj( + "statusCapability", + ["get"] ); - + mockNavigationService = jasmine.createSpyObj( + "mockNavigationService", + [ "setNavigation"] + ); + mockCapabilities = { + "editor": mockEditorCapability, + "status": mockStatusCapability + }; actionContext = { domainObject: mockDomainObject }; mockDomainObject.hasCapability.andReturn(true); - mockDomainObject.getCapability.andReturn(mockEditorCapability); - mockEditorCapability.save.andReturn(mockPromise(true)); + mockDomainObject.getCapability.andCallFake(function(capability){ + return mockCapabilities[capability]; + }); - action = new SaveAction(mockLocation, mockUrlService, actionContext); + mockEditorCapability.save.andReturn(mockPromise(mockDomainObject)); + mockStatusCapability.get.andReturn(true); + + action = new SaveAction(mockNavigationService, actionContext); }); - it("only applies to domain object with an editor capability", function () { + it("only applies to domain object that is being edited", function () { expect(SaveAction.appliesTo(actionContext)).toBeTruthy(); - expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor"); - mockDomainObject.hasCapability.andReturn(false); - mockDomainObject.getCapability.andReturn(undefined); + mockStatusCapability.get.andReturn(false); expect(SaveAction.appliesTo(actionContext)).toBeFalsy(); }); @@ -96,8 +102,8 @@ define( it("returns to browse when performed", function () { action.perform(); - expect(mockLocation.path).toHaveBeenCalledWith( - mockUrlService.urlForLocation("browse", mockDomainObject) + expect(mockNavigationService.setNavigation).toHaveBeenCalledWith( + mockDomainObject ); }); }); diff --git a/platform/commonUI/edit/test/capabilities/EditorCapabilitySpec.js b/platform/commonUI/edit/test/capabilities/EditorCapabilitySpec.js index 5d7c79399b..c72dacf238 100644 --- a/platform/commonUI/edit/test/capabilities/EditorCapabilitySpec.js +++ b/platform/commonUI/edit/test/capabilities/EditorCapabilitySpec.js @@ -28,6 +28,8 @@ define( describe("The editor capability", function () { var mockPersistence, + mockStatusCapability, + mockCapabilities, mockEditableObject, mockDomainObject, mockCache, @@ -40,6 +42,14 @@ define( "persistence", [ "persist" ] ); + mockStatusCapability = jasmine.createSpyObj( + "status", + [ "set" ] + ); + mockCapabilities = { + "persistence":mockPersistence, + "status": mockStatusCapability + }; mockEditableObject = { getModel: function () { return model; } }; @@ -53,7 +63,9 @@ define( ); mockCallback = jasmine.createSpy("callback"); - mockDomainObject.getCapability.andReturn(mockPersistence); + mockDomainObject.getCapability.andCallFake(function(capability){ + return mockCapabilities[capability]; + }); model = { someKey: "some value", x: 42 }; @@ -96,6 +108,19 @@ define( }); }); + it("resets the editing status on successful save", function () { + capability.save().then(mockCallback); + + // Wait for promise to resolve + waitsFor(function () { + return mockCallback.calls.length > 0; + }, 250); + + runs(function () { + expect(mockStatusCapability.set).toHaveBeenCalledWith('editing', false); + }); + }); + it("has no interactions on cancel", function () { capability.cancel().then(mockCallback); @@ -111,6 +136,19 @@ define( }); }); + it("resets editing status on cancel", function () { + capability.cancel().then(mockCallback); + + // Wait for promise to resolve + waitsFor(function () { + return mockCallback.calls.length > 0; + }, 250); + + runs(function () { + expect(mockStatusCapability.set).toHaveBeenCalledWith('editing', false); + }); + }); + }); } diff --git a/platform/commonUI/edit/test/representers/EditRepresenterSpec.js b/platform/commonUI/edit/test/representers/EditRepresenterSpec.js index 8b0ef22a7d..abfb3d46c9 100644 --- a/platform/commonUI/edit/test/representers/EditRepresenterSpec.js +++ b/platform/commonUI/edit/test/representers/EditRepresenterSpec.js @@ -32,7 +32,9 @@ define( mockScope, testRepresentation, mockDomainObject, + mockStatusCapability, mockPersistence, + mockCapabilities, representer; function mockPromise(value) { @@ -57,11 +59,21 @@ define( ]); mockPersistence = jasmine.createSpyObj("persistence", ["persist"]); + mockStatusCapability = jasmine.createSpyObj("domainObject", [ + "get"]); + + mockCapabilities = { + "persistence": mockPersistence, + "status": mockStatusCapability + }; mockDomainObject.getModel.andReturn({}); mockDomainObject.hasCapability.andReturn(true); mockDomainObject.useCapability.andReturn(true); - mockDomainObject.getCapability.andReturn(mockPersistence); + mockStatusCapability.get.andReturn(true); + mockDomainObject.getCapability.andCallFake(function(capability){ + return mockCapabilities[capability]; + }); representer = new EditRepresenter(mockQ, mockLog, mockScope); representer.represent(testRepresentation, mockDomainObject); @@ -98,6 +110,14 @@ define( }); }); + it("sets an 'editMode' flag on scope if the object is editable", function() { + expect(mockScope.editMode).toBe(true); + + mockStatusCapability.get.andReturn(false); + representer.represent(testRepresentation, mockDomainObject); + expect(mockScope.editMode).toBeFalsy(); + }); + }); }