[Edit] Add notifications to Save & SaveAs (#1258)

* [Edit] Added notifications to SaveAsAction

* [Edit] Added notifications to SaveAction

* [Edit] Update SaveAsActionSpec

* [Edit] No error notif when user cancels SaveAs
This commit is contained in:
Bogdan Alexandru Marginean 2016-12-19 20:59:26 +02:00 committed by Andrew Henry
parent 90a7ca8ae5
commit 532f7a76f9
7 changed files with 132 additions and 26 deletions

View File

@ -211,7 +211,8 @@ define([
"cssclass": "icon-save labeled", "cssclass": "icon-save labeled",
"description": "Save changes made to these objects.", "description": "Save changes made to these objects.",
"depends": [ "depends": [
"dialogService" "dialogService",
"notificationService"
] ]
}, },
{ {
@ -222,7 +223,8 @@ define([
"cssclass": "icon-save labeled", "cssclass": "icon-save labeled",
"description": "Save changes made to these objects.", "description": "Save changes made to these objects.",
"depends": [ "depends": [
"dialogService" "dialogService",
"notificationService"
] ]
}, },
{ {
@ -236,7 +238,8 @@ define([
"$injector", "$injector",
"policyService", "policyService",
"dialogService", "dialogService",
"copyService" "copyService",
"notificationService"
], ],
"priority": "mandatory" "priority": "mandatory"
}, },

View File

@ -33,10 +33,12 @@ define(
*/ */
function SaveAction( function SaveAction(
dialogService, dialogService,
notificationService,
context context
) { ) {
this.domainObject = (context || {}).domainObject; this.domainObject = (context || {}).domainObject;
this.dialogService = dialogService; this.dialogService = dialogService;
this.notificationService = notificationService;
} }
/** /**
@ -47,7 +49,8 @@ define(
* @memberof platform/commonUI/edit.SaveAction# * @memberof platform/commonUI/edit.SaveAction#
*/ */
SaveAction.prototype.perform = function () { SaveAction.prototype.perform = function () {
var domainObject = this.domainObject, var self = this,
domainObject = this.domainObject,
dialog = new SaveInProgressDialog(this.dialogService); dialog = new SaveInProgressDialog(this.dialogService);
// Invoke any save behavior introduced by the editor capability; // Invoke any save behavior introduced by the editor capability;
@ -58,15 +61,21 @@ define(
return domainObject.getCapability("editor").save(); return domainObject.getCapability("editor").save();
} }
function hideBlockingDialog() { function onSuccess() {
dialog.hide(); dialog.hide();
self.notificationService.info("Save Succeeded");
}
function onFailure() {
dialog.hide();
self.notificationService.error("Save Failed");
} }
dialog.show(); dialog.show();
return doSave() return doSave()
.then(hideBlockingDialog) .then(onSuccess)
.catch(hideBlockingDialog); .catch(onFailure);
}; };
/** /**

View File

@ -33,11 +33,13 @@ define(
*/ */
function SaveAndStopEditingAction( function SaveAndStopEditingAction(
dialogService, dialogService,
notificationService,
context context
) { ) {
this.context = context; this.context = context;
this.domainObject = (context || {}).domainObject; this.domainObject = (context || {}).domainObject;
this.dialogService = dialogService; this.dialogService = dialogService;
this.notificationService = notificationService;
} }
/** /**
@ -49,7 +51,7 @@ define(
*/ */
SaveAndStopEditingAction.prototype.perform = function () { SaveAndStopEditingAction.prototype.perform = function () {
var domainObject = this.domainObject, var domainObject = this.domainObject,
saveAction = new SaveAction(this.dialogService, this.context); saveAction = new SaveAction(this.dialogService, this.notificationService, this.context);
function closeEditor() { function closeEditor() {
return domainObject.getCapability("editor").finish(); return domainObject.getCapability("editor").finish();

View File

@ -43,6 +43,7 @@ define([
policyService, policyService,
dialogService, dialogService,
copyService, copyService,
notificationService,
context context
) { ) {
this.domainObject = (context || {}).domainObject; this.domainObject = (context || {}).domainObject;
@ -52,6 +53,7 @@ define([
this.policyService = policyService; this.policyService = policyService;
this.dialogService = dialogService; this.dialogService = dialogService;
this.copyService = copyService; this.copyService = copyService;
this.notificationService = notificationService;
} }
/** /**
@ -117,8 +119,10 @@ define([
return self.dialogService return self.dialogService
.getUserInput(wizard.getFormStructure(true), .getUserInput(wizard.getFormStructure(true),
wizard.getInitialFormValue() wizard.getInitialFormValue())
).then(wizard.populateObjectFromInput.bind(wizard)); .then(wizard.populateObjectFromInput.bind(wizard), function (failureReason) {
return Promise.reject("user canceled");
});
} }
function showBlockingDialog(object) { function showBlockingDialog(object) {
@ -176,8 +180,16 @@ define([
}); });
} }
function onFailure() { function onSuccess(object) {
self.notificationService.info("Save Succeeded");
return object;
}
function onFailure(reason) {
hideBlockingDialog(); hideBlockingDialog();
if (reason !== "user canceled") {
self.notificationService.error("Save Failed");
}
return false; return false;
} }
@ -190,6 +202,7 @@ define([
.then(saveAfterClone) .then(saveAfterClone)
.then(finishEditing) .then(finishEditing)
.then(hideBlockingDialog) .then(hideBlockingDialog)
.then(onSuccess)
.catch(onFailure); .catch(onFailure);
}; };

View File

@ -19,6 +19,7 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
/*global describe,it,expect,beforeEach,jasmine,waitsFor,runs*/
define( define(
["../../src/actions/SaveAction"], ["../../src/actions/SaveAction"],
@ -28,7 +29,8 @@ define(
var mockDomainObject, var mockDomainObject,
mockEditorCapability, mockEditorCapability,
actionContext, actionContext,
dialogService, mockDialogService,
mockNotificationService,
mockActionCapability, mockActionCapability,
capabilities = {}, capabilities = {},
action; action;
@ -68,11 +70,17 @@ define(
actionContext = { actionContext = {
domainObject: mockDomainObject domainObject: mockDomainObject
}; };
dialogService = jasmine.createSpyObj(
mockDialogService = jasmine.createSpyObj(
"dialogService", "dialogService",
["showBlockingMessage"] ["showBlockingMessage"]
); );
mockNotificationService = jasmine.createSpyObj(
"notificationService",
["info", "error"]
);
mockDomainObject.hasCapability.andReturn(true); mockDomainObject.hasCapability.andReturn(true);
mockDomainObject.getCapability.andCallFake(function (capability) { mockDomainObject.getCapability.andCallFake(function (capability) {
return capabilities[capability]; return capabilities[capability];
@ -81,7 +89,7 @@ define(
mockEditorCapability.save.andReturn(mockPromise(true)); mockEditorCapability.save.andReturn(mockPromise(true));
mockEditorCapability.isEditContextRoot.andReturn(true); mockEditorCapability.isEditContextRoot.andReturn(true);
action = new SaveAction(dialogService, actionContext); action = new SaveAction(mockDialogService, mockNotificationService, actionContext);
}); });
it("only applies to domain object with an editor capability", function () { it("only applies to domain object with an editor capability", function () {
@ -105,30 +113,54 @@ define(
expect(mockEditorCapability.save).toHaveBeenCalled(); expect(mockEditorCapability.save).toHaveBeenCalled();
}); });
describe("a blocking dialog", function () { describe("in order to keep the user in the loop", function () {
var mockDialogHandle; var mockDialogHandle;
beforeEach(function () { beforeEach(function () {
mockDialogHandle = jasmine.createSpyObj("dialogHandle", ["dismiss"]); mockDialogHandle = jasmine.createSpyObj("dialogHandle", ["dismiss"]);
dialogService.showBlockingMessage.andReturn(mockDialogHandle); mockDialogService.showBlockingMessage.andReturn(mockDialogHandle);
}); });
it("shows a dialog while saving", function () { it("shows a dialog while saving", function () {
mockEditorCapability.save.andReturn(new Promise(function () { mockEditorCapability.save.andReturn(new Promise(function () {
})); }));
action.perform(); action.perform();
expect(dialogService.showBlockingMessage).toHaveBeenCalled(); expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
expect(mockDialogHandle.dismiss).not.toHaveBeenCalled(); expect(mockDialogHandle.dismiss).not.toHaveBeenCalled();
}); });
it("hides a dialog when saving is complete", function () { it("hides the dialog when saving is complete", function () {
action.perform(); action.perform();
expect(dialogService.showBlockingMessage).toHaveBeenCalled(); expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
expect(mockDialogHandle.dismiss).toHaveBeenCalled(); expect(mockDialogHandle.dismiss).toHaveBeenCalled();
}); });
});
it("notifies if saving succeeded", function () {
var mockCallback = jasmine.createSpy("callback");
mockEditorCapability.save.andReturn(Promise.resolve("success"));
action.perform().then(mockCallback);
waitsFor(function () {
return mockCallback.calls.length > 0;
});
runs(function () {
expect(mockNotificationService.info).toHaveBeenCalled();
expect(mockNotificationService.error).not.toHaveBeenCalled();
});
});
it("notifies if saving failed", function () {
var mockCallback = jasmine.createSpy("callback");
mockEditorCapability.save.andReturn(Promise.reject("some failure reason"));
action.perform().then(mockCallback);
waitsFor(function () {
return mockCallback.calls.length > 0;
});
runs(function () {
expect(mockNotificationService.error).toHaveBeenCalled();
expect(mockNotificationService.info).not.toHaveBeenCalled();
});
});
});
}); });
} }
); );

View File

@ -19,6 +19,7 @@
* this source code distribution or the Licensing information page available * this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
/*global describe,it,expect,beforeEach,jasmine*/
define( define(
["../../src/actions/SaveAndStopEditingAction"], ["../../src/actions/SaveAndStopEditingAction"],
@ -35,6 +36,7 @@ define(
mockEditorCapability, mockEditorCapability,
actionContext, actionContext,
dialogService, dialogService,
notificationService,
mockActionCapability, mockActionCapability,
capabilities = {}, capabilities = {},
action; action;
@ -79,6 +81,11 @@ define(
["showBlockingMessage"] ["showBlockingMessage"]
); );
notificationService = jasmine.createSpyObj(
"notificationService",
["info", "error"]
);
mockDomainObject.hasCapability.andReturn(true); mockDomainObject.hasCapability.andReturn(true);
mockDomainObject.getCapability.andCallFake(function (capability) { mockDomainObject.getCapability.andCallFake(function (capability) {
return capabilities[capability]; return capabilities[capability];
@ -87,7 +94,7 @@ define(
mockEditorCapability.save.andReturn(mockPromise(true)); mockEditorCapability.save.andReturn(mockPromise(true));
mockEditorCapability.isEditContextRoot.andReturn(true); mockEditorCapability.isEditContextRoot.andReturn(true);
action = new SaveAndStopEditingAction(dialogService, actionContext); action = new SaveAndStopEditingAction(dialogService, notificationService, actionContext);
}); });

View File

@ -32,6 +32,7 @@ define(
mockObjectService, mockObjectService,
mockDialogService, mockDialogService,
mockCopyService, mockCopyService,
mockNotificationService,
mockParent, mockParent,
actionContext, actionContext,
capabilities = {}, capabilities = {},
@ -112,11 +113,25 @@ define(
] ]
); );
mockNotificationService = jasmine.createSpyObj(
"notificationService",
[
"info",
"error"
]
);
actionContext = { actionContext = {
domainObject: mockDomainObject domainObject: mockDomainObject
}; };
action = new SaveAsAction(undefined, undefined, mockDialogService, mockCopyService, actionContext); action = new SaveAsAction(
undefined,
undefined,
mockDialogService,
mockCopyService,
mockNotificationService,
actionContext);
spyOn(action, "getObjectService"); spyOn(action, "getObjectService");
action.getObjectService.andReturn(mockObjectService); action.getObjectService.andReturn(mockObjectService);
@ -186,7 +201,7 @@ define(
expect(mockDialogService.getUserInput).toHaveBeenCalled(); expect(mockDialogService.getUserInput).toHaveBeenCalled();
}); });
describe("a blocking dialog", function () { describe("in order to keep the user in the loop", function () {
var mockDialogHandle; var mockDialogHandle;
beforeEach(function () { beforeEach(function () {
@ -194,14 +209,14 @@ define(
mockDialogService.showBlockingMessage.andReturn(mockDialogHandle); mockDialogService.showBlockingMessage.andReturn(mockDialogHandle);
}); });
it("indicates that a save is taking place", function () { it("shows a blocking dialog indicating that saving is in progress", function () {
mockEditorCapability.save.andReturn(new Promise(function () {})); mockEditorCapability.save.andReturn(new Promise(function () {}));
action.perform(); action.perform();
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled(); expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
expect(mockDialogHandle.dismiss).not.toHaveBeenCalled(); expect(mockDialogHandle.dismiss).not.toHaveBeenCalled();
}); });
it("is hidden after saving", function () { it("hides the blocking dialog after saving finishes", function () {
var mockCallback = jasmine.createSpy(); var mockCallback = jasmine.createSpy();
action.perform().then(mockCallback); action.perform().then(mockCallback);
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled(); expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
@ -212,6 +227,31 @@ define(
expect(mockDialogHandle.dismiss).toHaveBeenCalled(); expect(mockDialogHandle.dismiss).toHaveBeenCalled();
}); });
}); });
it("notifies if saving succeeded", function () {
var mockCallback = jasmine.createSpy();
action.perform().then(mockCallback);
waitsFor(function () {
return mockCallback.calls.length > 0;
});
runs(function () {
expect(mockNotificationService.info).toHaveBeenCalled();
expect(mockNotificationService.error).not.toHaveBeenCalled();
});
});
it("notifies if saving failed", function () {
mockCopyService.perform.andReturn(Promise.reject("some failure reason"));
var mockCallback = jasmine.createSpy();
action.perform().then(mockCallback);
waitsFor(function () {
return mockCallback.calls.length > 0;
});
runs(function () {
expect(mockNotificationService.error).toHaveBeenCalled();
expect(mockNotificationService.info).not.toHaveBeenCalled();
});
});
}); });
}); });
} }