diff --git a/platform/commonUI/browse/src/navigation/NavigateAction.js b/platform/commonUI/browse/src/navigation/NavigateAction.js index 431ad8dadc..6121213a51 100644 --- a/platform/commonUI/browse/src/navigation/NavigateAction.js +++ b/platform/commonUI/browse/src/navigation/NavigateAction.js @@ -64,7 +64,7 @@ define( var editing = currentObject.hasCapability('editor') && currentObject.getCapability('editor').isEditContextRoot(); - return self.$q.when(editing && currentObject.getCapability("editor").cancel()); + return self.$q.when(editing && currentObject.getCapability("editor").finish()); } function navigate() { diff --git a/platform/commonUI/browse/test/navigation/NavigateActionSpec.js b/platform/commonUI/browse/test/navigation/NavigateActionSpec.js index 509e7aef3d..f9b756eeaa 100644 --- a/platform/commonUI/browse/test/navigation/NavigateActionSpec.js +++ b/platform/commonUI/browse/test/navigation/NavigateActionSpec.js @@ -61,7 +61,7 @@ define( capabilities.editor = jasmine.createSpyObj("editorCapability", [ "isEditContextRoot", - "cancel" + "finish" ]); mockNavigatedObject.getCapability.andCallFake(function (capability) { @@ -148,9 +148,9 @@ define( capabilities.editor.isEditContextRoot.andReturn(true); }); - it("cancels editing if in edit mode", function () { + it("finishes editing if in edit mode", function () { action.perform(); - expect(capabilities.editor.cancel) + expect(capabilities.editor.finish) .toHaveBeenCalled(); }); }); diff --git a/platform/commonUI/edit/bundle.js b/platform/commonUI/edit/bundle.js index 33d92f913c..60da900176 100644 --- a/platform/commonUI/edit/bundle.js +++ b/platform/commonUI/edit/bundle.js @@ -31,6 +31,7 @@ define([ "./src/actions/PropertiesAction", "./src/actions/RemoveAction", "./src/actions/SaveAction", + "./src/actions/SaveAndStopEditingAction", "./src/actions/SaveAsAction", "./src/actions/CancelAction", "./src/policies/EditActionPolicy", @@ -70,6 +71,7 @@ define([ PropertiesAction, RemoveAction, SaveAction, + SaveAndStopEditingAction, SaveAsAction, CancelAction, EditActionPolicy, @@ -203,20 +205,30 @@ define([ ] }, { - "key": "save", - "category": "conclude-editing", - "implementation": SaveAction, - "name": "Save", + "key": "save-and-stop-editing", + "category": "save", + "implementation": SaveAndStopEditingAction, + "name": "Save and Finish Editing", "cssclass": "icon-save labeled", "description": "Save changes made to these objects.", "depends": [ "dialogService" - ], - "priority": "mandatory" + ] }, { "key": "save", - "category": "conclude-editing", + "category": "save", + "implementation": SaveAction, + "name": "Save and Continue Editing", + "cssclass": "icon-save labeled", + "description": "Save changes made to these objects.", + "depends": [ + "dialogService" + ] + }, + { + "key": "save-as", + "category": "save", "implementation": SaveAsAction, "name": "Save As...", "cssclass": "icon-save labeled", @@ -225,7 +237,6 @@ define([ "$injector", "policyService", "dialogService", - "creationService", "copyService" ], "priority": "mandatory" diff --git a/platform/commonUI/edit/res/templates/edit-action-buttons.html b/platform/commonUI/edit/res/templates/edit-action-buttons.html index fc46b31f57..4bc020612f 100644 --- a/platform/commonUI/edit/res/templates/edit-action-buttons.html +++ b/platform/commonUI/edit/res/templates/edit-action-buttons.html @@ -20,11 +20,32 @@ at runtime from the About dialog for additional information. --> - + + + + + + + + + + + + + ng-class="{ major: $index === 0 && saveActions.length === 0 }"> {{currentAction.getMetadata().name}} diff --git a/platform/commonUI/edit/src/actions/CancelAction.js b/platform/commonUI/edit/src/actions/CancelAction.js index 13b005958e..a48ceec996 100644 --- a/platform/commonUI/edit/src/actions/CancelAction.js +++ b/platform/commonUI/edit/src/actions/CancelAction.js @@ -62,7 +62,7 @@ define( } function cancel(allowed) { - return allowed && domainObject.getCapability("editor").cancel(); + return allowed && domainObject.getCapability("editor").finish(); } //Do navigation first in order to trigger unsaved changes dialog diff --git a/platform/commonUI/edit/src/actions/SaveAction.js b/platform/commonUI/edit/src/actions/SaveAction.js index 6f96e84192..8d3ea7f509 100644 --- a/platform/commonUI/edit/src/actions/SaveAction.js +++ b/platform/commonUI/edit/src/actions/SaveAction.js @@ -25,9 +25,8 @@ define( function (SaveInProgressDialog) { /** - * The "Save" action; the action triggered by clicking Save from - * Edit Mode. Exits the editing user interface and invokes object - * capabilities to persist the changes that have been made. + * The "Save" action; it invokes object capabilities to persist + * the changes that have been made. * @constructor * @implements {Action} * @memberof platform/commonUI/edit @@ -41,7 +40,7 @@ define( } /** - * Save changes and conclude editing. + * Save changes. * * @returns {Promise} a promise that will be fulfilled when * cancellation has completed @@ -51,40 +50,22 @@ define( var domainObject = this.domainObject, dialog = new SaveInProgressDialog(this.dialogService); - function resolveWith(object) { - return function () { - return object; - }; - } - // Invoke any save behavior introduced by the editor capability; // this is introduced by EditableDomainObject which is // used to insulate underlying objects from changes made // during editing. function doSave() { - return domainObject.getCapability("editor").save() - .then(resolveWith(domainObject)); + return domainObject.getCapability("editor").save(); } - // Discard the current root view (which will be the editing - // UI, which will have been pushed atop the Browse UI.) - function returnToBrowse(object) { - if (object) { - object.getCapability("action").perform("navigate"); - } - return object; - } - - function hideBlockingDialog(object) { + function hideBlockingDialog() { dialog.hide(); - return object; } dialog.show(); return doSave() .then(hideBlockingDialog) - .then(returnToBrowse) .catch(hideBlockingDialog); }; diff --git a/platform/commonUI/edit/src/actions/SaveAndStopEditingAction.js b/platform/commonUI/edit/src/actions/SaveAndStopEditingAction.js new file mode 100644 index 0000000000..a0a0583048 --- /dev/null +++ b/platform/commonUI/edit/src/actions/SaveAndStopEditingAction.js @@ -0,0 +1,73 @@ +/***************************************************************************** + * 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( + ["./SaveAction"], + function (SaveAction) { + + /** + * The "Save and Stop Editing" action performs a [Save action]{@link SaveAction} + * on the object under edit followed by exiting the edit user interface. + * @constructor + * @implements {Action} + * @memberof platform/commonUI/edit + */ + function SaveAndStopEditingAction( + dialogService, + context + ) { + this.context = context; + this.domainObject = (context || {}).domainObject; + this.dialogService = dialogService; + } + + /** + * Trigger a save operation and exit edit mode. + * + * @returns {Promise} a promise that will be fulfilled when + * cancellation has completed + * @memberof platform/commonUI/edit.SaveAndStopEditingAction# + */ + SaveAndStopEditingAction.prototype.perform = function () { + var domainObject = this.domainObject, + saveAction = new SaveAction(this.dialogService, this.context); + + function closeEditor() { + return domainObject.getCapability("editor").finish(); + } + + return saveAction.perform() + .then(closeEditor) + .catch(closeEditor); + }; + + /** + * Check if this action is applicable in a given context. + * This will ensure that a domain object is present in the context, + * and that this domain object is in Edit mode. + * @returns true if applicable + */ + SaveAndStopEditingAction.appliesTo = SaveAction.appliesTo; + + return SaveAndStopEditingAction; + } +); diff --git a/platform/commonUI/edit/src/actions/SaveAsAction.js b/platform/commonUI/edit/src/actions/SaveAsAction.js index 57a055429a..2befea07fd 100644 --- a/platform/commonUI/edit/src/actions/SaveAsAction.js +++ b/platform/commonUI/edit/src/actions/SaveAsAction.js @@ -42,7 +42,6 @@ define([ $injector, policyService, dialogService, - creationService, copyService, context ) { @@ -52,7 +51,6 @@ define([ }; this.policyService = policyService; this.dialogService = dialogService; - this.creationService = creationService; this.copyService = copyService; } @@ -166,11 +164,16 @@ define([ .then(resolveWith(object)); } - function commitEditingAfterClone(clonedObject) { + function saveAfterClone(clonedObject) { return domainObject.getCapability("editor").save() .then(resolveWith(clonedObject)); } + function finishEditing(clonedObject) { + return domainObject.getCapability("editor").finish() + .then(resolveWith(clonedObject)); + } + function onFailure() { hideBlockingDialog(); return false; @@ -182,7 +185,8 @@ define([ .then(getParent) .then(cloneIntoParent) .then(undirtyOriginals) - .then(commitEditingAfterClone) + .then(saveAfterClone) + .then(finishEditing) .then(hideBlockingDialog) .catch(onFailure); }; diff --git a/platform/commonUI/edit/src/capabilities/EditorCapability.js b/platform/commonUI/edit/src/capabilities/EditorCapability.js index aa7670d8aa..1cbf239d7d 100644 --- a/platform/commonUI/edit/src/capabilities/EditorCapability.js +++ b/platform/commonUI/edit/src/capabilities/EditorCapability.js @@ -28,8 +28,8 @@ define( * A capability that implements an editing 'session' for a domain * object. An editing session is initiated via a call to .edit(). * Once initiated, any persist operations will be queued pending a - * subsequent call to [.save()](@link #save) or [.cancel()](@link - * #cancel). + * subsequent call to [.save()](@link #save) or [.finish()](@link + * #finish). * @param transactionService * @param domainObject * @constructor @@ -45,7 +45,7 @@ define( /** * Initiate an editing session. This will start a transaction during * which any persist operations will be deferred until either save() - * or cancel() are called. + * or finish() are called. */ EditorCapability.prototype.edit = function () { this.transactionService.startTransaction(); @@ -81,25 +81,25 @@ define( }; /** - * Save any changes from this editing session. This will flush all - * pending persists and end the current transaction + * Save any unsaved changes from this editing session. This will + * end the current transaction and continue with a new one. * @returns {*} */ EditorCapability.prototype.save = function () { - var domainObject = this.domainObject; - return this.transactionService.commit().then(function () { - domainObject.getCapability('status').set('editing', false); + var transactionService = this.transactionService; + return transactionService.commit().then(function () { + transactionService.startTransaction(); }); }; EditorCapability.prototype.invoke = EditorCapability.prototype.edit; /** - * Cancel the current editing session. This will discard any pending + * Finish the current editing session. This will discard any pending * persist operations * @returns {*} */ - EditorCapability.prototype.cancel = function () { + EditorCapability.prototype.finish = function () { var domainObject = this.domainObject; return this.transactionService.cancel().then(function () { domainObject.getCapability("status").set("editing", false); diff --git a/platform/commonUI/edit/src/controllers/EditActionController.js b/platform/commonUI/edit/src/controllers/EditActionController.js index 19fddf931d..7c33799ebb 100644 --- a/platform/commonUI/edit/src/controllers/EditActionController.js +++ b/platform/commonUI/edit/src/controllers/EditActionController.js @@ -27,7 +27,8 @@ define( [], function () { - var ACTION_CONTEXT = { category: 'conclude-editing' }; + var SAVE_ACTION_CONTEXT = { category: 'save' }; + var OTHERS_ACTION_CONTEXT = { category: 'conclude-editing' }; /** * Controller which supplies action instances for Save/Cancel. @@ -35,11 +36,30 @@ define( * @constructor */ function EditActionController($scope) { - // Maintain all "conclude-editing" actions in the present - // context. + + function actionToMenuOption(action) { + return { + key: action, + name: action.getMetadata().name, + cssclass: action.getMetadata().cssclass + }; + } + + // Maintain all "conclude-editing" and "save" actions in the + // present context. function updateActions() { - $scope.editActions = $scope.action ? - $scope.action.getActions(ACTION_CONTEXT) : + $scope.saveActions = $scope.action ? + $scope.action.getActions(SAVE_ACTION_CONTEXT) : + []; + + $scope.saveActionsAsMenuOptions = $scope.saveActions.map(actionToMenuOption); + + $scope.saveActionMenuClickHandler = function (clickedAction) { + clickedAction.perform(); + }; + + $scope.otherEditActions = $scope.action ? + $scope.action.getActions(OTHERS_ACTION_CONTEXT) : []; } diff --git a/platform/commonUI/edit/src/creation/CreateAction.js b/platform/commonUI/edit/src/creation/CreateAction.js index 14b8443462..b83351544f 100644 --- a/platform/commonUI/edit/src/creation/CreateAction.js +++ b/platform/commonUI/edit/src/creation/CreateAction.js @@ -67,12 +67,17 @@ define( editAction, editorCapability; + function closeEditor() { + return editorCapability.finish(); + } + function onSave() { - return editorCapability.save(); + return editorCapability.save() + .then(closeEditor); } function onCancel() { - return editorCapability.cancel(); + return closeEditor(); } newModel.type = this.type.getKey(); @@ -85,9 +90,9 @@ define( if (editAction) { return editAction.perform(); } else if (editorCapability) { - //otherwise, use the save action + //otherwise, use the save as action editorCapability.edit(); - return newObject.getCapability("action").perform("save").then(onSave, onCancel); + return newObject.getCapability("action").perform("save-as").then(onSave, onCancel); } }; diff --git a/platform/commonUI/edit/test/actions/CancelActionSpec.js b/platform/commonUI/edit/test/actions/CancelActionSpec.js index 3bd0cee72f..7fdc51b0e9 100644 --- a/platform/commonUI/edit/test/actions/CancelActionSpec.js +++ b/platform/commonUI/edit/test/actions/CancelActionSpec.js @@ -63,7 +63,7 @@ define( capabilities.editor = jasmine.createSpyObj( "editor", - ["save", "cancel", "isEditContextRoot"] + ["save", "finish", "isEditContextRoot"] ); capabilities.action = jasmine.createSpyObj( "actionCapability", @@ -105,7 +105,7 @@ define( return !!capabilities[name]; }); - capabilities.editor.cancel.andReturn(mockPromise(true)); + capabilities.editor.finish.andReturn(mockPromise(true)); action = new CancelAction(actionContext); @@ -130,8 +130,8 @@ define( capabilities.action.perform.andReturn(mockPromise(true)); action.perform(); - // Should have called cancel - expect(capabilities.editor.cancel).toHaveBeenCalled(); + // Should have called finish + expect(capabilities.editor.finish).toHaveBeenCalled(); // Definitely shouldn't call save! expect(capabilities.editor.save).not.toHaveBeenCalled(); diff --git a/platform/commonUI/edit/test/actions/EditActionSpec.js b/platform/commonUI/edit/test/actions/EditActionSpec.js index 0e70e571c3..c1a21c83bf 100644 --- a/platform/commonUI/edit/test/actions/EditActionSpec.js +++ b/platform/commonUI/edit/test/actions/EditActionSpec.js @@ -58,7 +58,7 @@ define( ); mockEditor = jasmine.createSpyObj( "editorCapability", - ["edit", "isEditContextRoot", "cancel"] + ["edit", "isEditContextRoot"] ); capabilities = { diff --git a/platform/commonUI/edit/test/actions/SaveActionSpec.js b/platform/commonUI/edit/test/actions/SaveActionSpec.js index 0243f89a11..7230c09448 100644 --- a/platform/commonUI/edit/test/actions/SaveActionSpec.js +++ b/platform/commonUI/edit/test/actions/SaveActionSpec.js @@ -56,7 +56,7 @@ define( ); mockEditorCapability = jasmine.createSpyObj( "editor", - ["save", "cancel", "isEditContextRoot"] + ["save", "isEditContextRoot"] ); mockActionCapability = jasmine.createSpyObj( "actionCapability", @@ -105,12 +105,6 @@ define( expect(mockEditorCapability.save).toHaveBeenCalled(); }); - it("navigates to the object after saving", - function () { - action.perform(); - expect(mockActionCapability.perform).toHaveBeenCalledWith("navigate"); - }); - describe("a blocking dialog", function () { var mockDialogHandle; diff --git a/platform/commonUI/edit/test/actions/SaveAndStopEditingActionSpec.js b/platform/commonUI/edit/test/actions/SaveAndStopEditingActionSpec.js new file mode 100644 index 0000000000..1fb8fa6492 --- /dev/null +++ b/platform/commonUI/edit/test/actions/SaveAndStopEditingActionSpec.js @@ -0,0 +1,123 @@ +/***************************************************************************** + * 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/actions/SaveAndStopEditingAction"], + function (SaveAndStopEditingAction) { + + describe("The Save and Stop Editing action", function () { + + // Some mocks appear unused because the + // underlying SaveAction that this action + // depends on is not mocked, so we mock some + // of SaveAction's own dependencies to make + // it run. + var mockDomainObject, + mockEditorCapability, + actionContext, + dialogService, + mockActionCapability, + capabilities = {}, + action; + + function mockPromise(value) { + return { + then: function (callback) { + return mockPromise(callback(value)); + }, + catch: function (callback) { + return mockPromise(callback(value)); + } + }; + } + + beforeEach(function () { + mockDomainObject = jasmine.createSpyObj( + "domainObject", + [ + "getCapability", + "hasCapability", + "getModel", + "getOriginalObject" + ] + ); + mockEditorCapability = jasmine.createSpyObj( + "editor", + ["save", "finish", "isEditContextRoot"] + ); + mockActionCapability = jasmine.createSpyObj( + "actionCapability", + ["perform"] + ); + capabilities.editor = mockEditorCapability; + capabilities.action = mockActionCapability; + + actionContext = { + domainObject: mockDomainObject + }; + dialogService = jasmine.createSpyObj( + "dialogService", + ["showBlockingMessage"] + ); + + mockDomainObject.hasCapability.andReturn(true); + mockDomainObject.getCapability.andCallFake(function (capability) { + return capabilities[capability]; + }); + mockDomainObject.getModel.andReturn({ persisted: 0 }); + mockEditorCapability.save.andReturn(mockPromise(true)); + mockEditorCapability.isEditContextRoot.andReturn(true); + + action = new SaveAndStopEditingAction(dialogService, actionContext); + }); + + + it("only applies to domain object with an editor capability", function () { + expect(SaveAndStopEditingAction.appliesTo(actionContext)).toBe(true); + expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor"); + + mockDomainObject.hasCapability.andReturn(false); + mockDomainObject.getCapability.andReturn(undefined); + expect(SaveAndStopEditingAction.appliesTo(actionContext)).toBe(false); + }); + + it("only applies to domain object that has already been persisted", function () { + mockDomainObject.getModel.andReturn({ persisted: undefined }); + expect(SaveAndStopEditingAction.appliesTo(actionContext)).toBe(false); + }); + + it("does not close the editor before completing the save", function () { + mockEditorCapability.save.andReturn(new Promise(function () { + })); + action.perform(); + expect(mockEditorCapability.save).toHaveBeenCalled(); + expect(mockEditorCapability.finish).not.toHaveBeenCalled(); + }); + + it("closes the editor after saving", function () { + action.perform(); + expect(mockEditorCapability.save).toHaveBeenCalled(); + expect(mockEditorCapability.finish).toHaveBeenCalled(); + }); + }); + } +); diff --git a/platform/commonUI/edit/test/actions/SaveAsActionSpec.js b/platform/commonUI/edit/test/actions/SaveAsActionSpec.js index fd8ce18c07..f87cf4766f 100644 --- a/platform/commonUI/edit/test/actions/SaveAsActionSpec.js +++ b/platform/commonUI/edit/test/actions/SaveAsActionSpec.js @@ -19,7 +19,7 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/*global describe,it,expect,beforeEach,jasmine*/ +/*global describe,it,expect,beforeEach,jasmine,runs,waitsFor,spyOn*/ define( ["../../src/actions/SaveAsAction"], @@ -33,7 +33,6 @@ define( mockDialogService, mockCopyService, mockParent, - mockUrlService, actionContext, capabilities = {}, action; @@ -78,10 +77,10 @@ define( mockEditorCapability = jasmine.createSpyObj( "editor", - ["save", "cancel", "isEditContextRoot"] + ["save", "finish", "isEditContextRoot"] ); - mockEditorCapability.cancel.andReturn(mockPromise(undefined)); mockEditorCapability.save.andReturn(mockPromise(true)); + mockEditorCapability.finish.andReturn(mockPromise(true)); mockEditorCapability.isEditContextRoot.andReturn(true); capabilities.editor = mockEditorCapability; @@ -113,16 +112,11 @@ define( ] ); - mockUrlService = jasmine.createSpyObj( - "urlService", - ["urlForLocation"] - ); - actionContext = { domainObject: mockDomainObject }; - action = new SaveAsAction(undefined, undefined, mockDialogService, undefined, mockCopyService, actionContext); + action = new SaveAsAction(undefined, undefined, mockDialogService, mockCopyService, actionContext); spyOn(action, "getObjectService"); action.getObjectService.andReturn(mockObjectService); @@ -156,6 +150,28 @@ define( expect(SaveAsAction.appliesTo(actionContext)).toBe(false); }); + it("uses the editor capability to save the object", function () { + mockEditorCapability.save.andReturn(new Promise(function () {})); + runs(function () { + action.perform(); + }); + waitsFor(function () { + return mockEditorCapability.save.calls.length > 0; + }, "perform() should call EditorCapability.save"); + runs(function () { + expect(mockEditorCapability.finish).not.toHaveBeenCalled(); + }); + }); + + it("uses the editor capability to finish editing the object", function () { + runs(function () { + action.perform(); + }); + waitsFor(function () { + return mockEditorCapability.finish.calls.length > 0; + }, "perform() should call EditorCapability.finish"); + }); + it("returns to browse after save", function () { spyOn(action, "save"); action.save.andReturn(mockPromise(mockDomainObject)); diff --git a/platform/commonUI/edit/test/capabilities/EditorCapabilitySpec.js b/platform/commonUI/edit/test/capabilities/EditorCapabilitySpec.js index 3ffb81921d..77fbd9b74f 100644 --- a/platform/commonUI/edit/test/capabilities/EditorCapabilitySpec.js +++ b/platform/commonUI/edit/test/capabilities/EditorCapabilitySpec.js @@ -134,15 +134,15 @@ define( it("commits the transaction", function () { expect(mockTransactionService.commit).toHaveBeenCalled(); }); - it("resets the edit state", function () { - expect(mockStatusCapability.set).toHaveBeenCalledWith('editing', false); + it("begins a new transaction", function () { + expect(mockTransactionService.startTransaction).toHaveBeenCalled(); }); }); - describe("cancel", function () { + describe("finish", function () { beforeEach(function () { capability.edit(); - capability.cancel(); + capability.finish(); }); it("cancels the transaction", function () { expect(mockTransactionService.cancel).toHaveBeenCalled(); @@ -158,7 +158,7 @@ define( beforeEach(function () { mockDomainObject.getModel.andReturn(model); capability.edit(); - capability.cancel(); + capability.finish(); }); it("returns true if the object has been modified since it" + " was last persisted", function () { diff --git a/platform/commonUI/edit/test/controllers/EditActionControllerSpec.js b/platform/commonUI/edit/test/controllers/EditActionControllerSpec.js index 36faf7beb8..bd5c8a9524 100644 --- a/platform/commonUI/edit/test/controllers/EditActionControllerSpec.js +++ b/platform/commonUI/edit/test/controllers/EditActionControllerSpec.js @@ -19,22 +19,51 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ +/*global describe,it,expect,beforeEach,jasmine*/ define( ["../../src/controllers/EditActionController"], function (EditActionController) { describe("The Edit Action controller", function () { + var mockSaveActionMetadata = { + name: "mocked-save-action", + cssclass: "mocked-save-action-css" + }; + + function fakeGetActions(actionContext) { + if (actionContext.category === "save") { + var mockedSaveActions = [ + jasmine.createSpyObj("mockSaveAction", ["getMetadata", "perform"]), + jasmine.createSpyObj("mockSaveAction", ["getMetadata", "perform"]) + ]; + mockedSaveActions.forEach(function (action) { + action.getMetadata.andReturn(mockSaveActionMetadata); + }); + return mockedSaveActions; + } else if (actionContext.category === "conclude-editing") { + return ["a", "b", "c"]; + } else { + throw "EditActionController uses a context that's not covered by tests."; + } + } + var mockScope, mockActions, controller; beforeEach(function () { mockActions = jasmine.createSpyObj("action", ["getActions"]); + mockActions.getActions.andCallFake(fakeGetActions); mockScope = jasmine.createSpyObj("$scope", ["$watch"]); + mockScope.action = mockActions; controller = new EditActionController(mockScope); }); + function makeControllerUpdateActions() { + mockScope.$watch.mostRecentCall.args[1](); + } + it("watches scope that may change applicable actions", function () { // The action capability expect(mockScope.$watch).toHaveBeenCalledWith( @@ -43,16 +72,34 @@ define( ); }); - it("populates the scope with grouped and ungrouped actions", function () { - mockScope.action = mockActions; + it("populates the scope with 'save' actions", function () { + makeControllerUpdateActions(); + expect(mockScope.saveActions.length).toEqual(2); + }); - mockActions.getActions.andReturn(["a", "b", "c"]); + it("converts 'save' actions to their menu counterparts", function () { + makeControllerUpdateActions(); + var menuOptions = mockScope.saveActionsAsMenuOptions; - // Call the watch - mockScope.$watch.mostRecentCall.args[1](); + expect(menuOptions.length).toEqual(2); + expect(menuOptions[0].key).toEqual(mockScope.saveActions[0]); + expect(menuOptions[1].key).toEqual(mockScope.saveActions[1]); + menuOptions.forEach(function (option) { + expect(option.name).toEqual(mockSaveActionMetadata.name); + expect(option.cssclass).toEqual(mockSaveActionMetadata.cssclass); + }); + }); - // Should have grouped and ungrouped actions in scope now - expect(mockScope.editActions.length).toEqual(3); + it("uses a click handler to perform the clicked action", function () { + makeControllerUpdateActions(); + var sampleSaveAction = mockScope.saveActions[0]; + mockScope.saveActionMenuClickHandler(sampleSaveAction); + expect(sampleSaveAction.perform).toHaveBeenCalled(); + }); + + it("populates the scope with other editing actions", function () { + makeControllerUpdateActions(); + expect(mockScope.otherEditActions).toEqual(["a", "b", "c"]); }); }); } diff --git a/platform/commonUI/edit/test/creation/CreateActionSpec.js b/platform/commonUI/edit/test/creation/CreateActionSpec.js index d65621f9a8..cd3d3fbc91 100644 --- a/platform/commonUI/edit/test/creation/CreateActionSpec.js +++ b/platform/commonUI/edit/test/creation/CreateActionSpec.js @@ -103,7 +103,7 @@ define( [ "edit", "save", - "cancel" + "finish" ] ); @@ -142,6 +142,7 @@ define( }); describe("the perform function", function () { + var promise = jasmine.createSpyObj("promise", ["then"]); beforeEach(function () { capabilities.action.getActions.andReturn([mockEditAction]); }); @@ -156,19 +157,20 @@ define( expect(mockEditAction.perform).toHaveBeenCalled(); }); - it("uses the save action if object does not have an edit action" + + it("uses the save-as action if object does not have an edit action" + " available", function () { capabilities.action.getActions.andReturn([]); capabilities.action.perform.andReturn(mockPromise(undefined)); + capabilities.editor.save.andReturn(promise); action.perform(); - expect(capabilities.action.perform).toHaveBeenCalledWith("save"); + expect(capabilities.action.perform).toHaveBeenCalledWith("save-as"); }); describe("uses to editor capability", function () { - var promise = jasmine.createSpyObj("promise", ["then"]); beforeEach(function () { capabilities.action.getActions.andReturn([]); capabilities.action.perform.andReturn(promise); + capabilities.editor.save.andReturn(promise); }); it("to save the edit if user saves dialog", function () { @@ -178,10 +180,10 @@ define( expect(capabilities.editor.save).toHaveBeenCalled(); }); - it("to cancel the edit if user cancels dialog", function () { + it("to finish the edit if user cancels dialog", function () { action.perform(); promise.then.mostRecentCall.args[1](); - expect(capabilities.editor.cancel).toHaveBeenCalled(); + expect(capabilities.editor.finish).toHaveBeenCalled(); }); }); });