mirror of
https://github.com/nasa/openmct.git
synced 2024-12-19 13:17:53 +00:00
Resolved merge conflicts
This commit is contained in:
parent
5bf750c90c
commit
44f4a82fa1
@ -87,10 +87,9 @@ define(
|
|||||||
newObject;
|
newObject;
|
||||||
|
|
||||||
newModel.type = this.type.getKey();
|
newModel.type = this.type.getKey();
|
||||||
|
newModel.location = parentObject.getId();
|
||||||
newObject = parentObject.useCapability('instantiation', newModel);
|
newObject = parentObject.useCapability('instantiation', newModel);
|
||||||
newObject.useCapability('mutation', function(model){
|
|
||||||
model.location = parentObject.getId();
|
|
||||||
});
|
|
||||||
editorCapability = newObject.getCapability("editor");
|
editorCapability = newObject.getCapability("editor");
|
||||||
|
|
||||||
if (countEditableViews(newObject) > 0 && newObject.hasCapability('composition')) {
|
if (countEditableViews(newObject) > 0 && newObject.hasCapability('composition')) {
|
||||||
@ -101,7 +100,7 @@ define(
|
|||||||
return newObject.useCapability("action").perform("save").then(function () {
|
return newObject.useCapability("action").perform("save").then(function () {
|
||||||
return editorCapability.save();
|
return editorCapability.save();
|
||||||
}, function () {
|
}, function () {
|
||||||
return editorCapability.cancel()
|
return editorCapability.cancel();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -41,9 +41,8 @@ define([
|
|||||||
"./src/representers/EditRepresenter",
|
"./src/representers/EditRepresenter",
|
||||||
"./src/representers/EditToolbarRepresenter",
|
"./src/representers/EditToolbarRepresenter",
|
||||||
"./src/capabilities/EditorCapability",
|
"./src/capabilities/EditorCapability",
|
||||||
"./src/capabilities/TransactionDecorator",
|
"./src/capabilities/TransactionCapabilityDecorator",
|
||||||
"./src/services/TransactionService",
|
"./src/services/TransactionService",
|
||||||
"./src/services/DirtyModelCache",
|
|
||||||
"text!./res/templates/library.html",
|
"text!./res/templates/library.html",
|
||||||
"text!./res/templates/edit-object.html",
|
"text!./res/templates/edit-object.html",
|
||||||
"text!./res/templates/edit-action-buttons.html",
|
"text!./res/templates/edit-action-buttons.html",
|
||||||
@ -71,9 +70,8 @@ define([
|
|||||||
EditRepresenter,
|
EditRepresenter,
|
||||||
EditToolbarRepresenter,
|
EditToolbarRepresenter,
|
||||||
EditorCapability,
|
EditorCapability,
|
||||||
TransactionDecorator,
|
TransactionCapabilityDecorator,
|
||||||
TransactionService,
|
TransactionService,
|
||||||
DirtyModelCache,
|
|
||||||
libraryTemplate,
|
libraryTemplate,
|
||||||
editObjectTemplate,
|
editObjectTemplate,
|
||||||
editActionButtonsTemplate,
|
editActionButtonsTemplate,
|
||||||
@ -136,8 +134,7 @@ define([
|
|||||||
"depends": [
|
"depends": [
|
||||||
"$location",
|
"$location",
|
||||||
"navigationService",
|
"navigationService",
|
||||||
"$log",
|
"$log"
|
||||||
"$q"
|
|
||||||
],
|
],
|
||||||
"description": "Edit this object.",
|
"description": "Edit this object.",
|
||||||
"category": "view-control",
|
"category": "view-control",
|
||||||
@ -270,11 +267,10 @@ define([
|
|||||||
{
|
{
|
||||||
"type": "decorator",
|
"type": "decorator",
|
||||||
"provides": "capabilityService",
|
"provides": "capabilityService",
|
||||||
"implementation": TransactionDecorator,
|
"implementation": TransactionCapabilityDecorator,
|
||||||
"depends": [
|
"depends": [
|
||||||
"$q",
|
"$q",
|
||||||
"transactionService",
|
"transactionService"
|
||||||
"dirtyModelCache"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -283,15 +279,7 @@ define([
|
|||||||
"implementation": TransactionService,
|
"implementation": TransactionService,
|
||||||
"depends": [
|
"depends": [
|
||||||
"$q",
|
"$q",
|
||||||
"dirtyModelCache"
|
"$log"
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "provider",
|
|
||||||
"provides": "dirtyModelCache",
|
|
||||||
"implementation": DirtyModelCache,
|
|
||||||
"depends": [
|
|
||||||
"topic"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -324,8 +312,7 @@ define([
|
|||||||
"description": "Provides transactional editing capabilities",
|
"description": "Provides transactional editing capabilities",
|
||||||
"implementation": EditorCapability,
|
"implementation": EditorCapability,
|
||||||
"depends": [
|
"depends": [
|
||||||
"transactionService",
|
"transactionService"
|
||||||
"dirtyModelCache"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -46,10 +46,19 @@ define(
|
|||||||
|
|
||||||
function returnToBrowse () {
|
function returnToBrowse () {
|
||||||
var parent;
|
var parent;
|
||||||
domainObject.getCapability("location").getOriginal().then(function (original) {
|
|
||||||
parent = original.getCapability("context").getParent();
|
//If the object existed already, navigate to refresh view
|
||||||
parent.getCapability("action").perform("navigate");
|
// with previous object state.
|
||||||
});
|
if (domainObject.getModel().persisted) {
|
||||||
|
domainObject.getCapability("action").perform("navigate");
|
||||||
|
} else {
|
||||||
|
//If the object was new, and user has cancelled, then
|
||||||
|
//navigate back to parent because nothing to show.
|
||||||
|
domainObject.getCapability("location").getOriginal().then(function (original) {
|
||||||
|
parent = original.getCapability("context").getParent();
|
||||||
|
parent.getCapability("action").perform("navigate");
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return this.domainObject.getCapability("editor").cancel()
|
return this.domainObject.getCapability("editor").cancel()
|
||||||
.then(returnToBrowse);
|
.then(returnToBrowse);
|
||||||
@ -64,7 +73,8 @@ define(
|
|||||||
CancelAction.appliesTo = function (context) {
|
CancelAction.appliesTo = function (context) {
|
||||||
var domainObject = (context || {}).domainObject;
|
var domainObject = (context || {}).domainObject;
|
||||||
return domainObject !== undefined &&
|
return domainObject !== undefined &&
|
||||||
domainObject.getCapability("status").get("editing");
|
domainObject.hasCapability('editor') &&
|
||||||
|
domainObject.getCapability('editor').isEditContextRoot();
|
||||||
};
|
};
|
||||||
|
|
||||||
return CancelAction;
|
return CancelAction;
|
||||||
|
@ -44,7 +44,7 @@ define(
|
|||||||
* @constructor
|
* @constructor
|
||||||
* @implements {Action}
|
* @implements {Action}
|
||||||
*/
|
*/
|
||||||
function EditAction($location, navigationService, $log, $q, context) {
|
function EditAction($location, navigationService, $log, context) {
|
||||||
var domainObject = (context || {}).domainObject;
|
var domainObject = (context || {}).domainObject;
|
||||||
|
|
||||||
// We cannot enter Edit mode if we have no domain object to
|
// We cannot enter Edit mode if we have no domain object to
|
||||||
@ -63,7 +63,6 @@ define(
|
|||||||
this.domainObject = domainObject;
|
this.domainObject = domainObject;
|
||||||
this.$location = $location;
|
this.$location = $location;
|
||||||
this.navigationService = navigationService;
|
this.navigationService = navigationService;
|
||||||
this.$q = $q;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,8 +88,11 @@ define(
|
|||||||
var domainObject = (context || {}).domainObject,
|
var domainObject = (context || {}).domainObject,
|
||||||
type = domainObject && domainObject.getCapability('type');
|
type = domainObject && domainObject.getCapability('type');
|
||||||
|
|
||||||
// Only allow creatable types to be edited
|
// Only allow editing of types that support it and are not already
|
||||||
return type && type.hasFeature('creation') && !domainObject.getCapability('status').get('editing');
|
// being edited
|
||||||
|
return type && type.hasFeature('creation') &&
|
||||||
|
domainObject.hasCapability('editor') &&
|
||||||
|
!domainObject.getCapability('editor').isEditContextRoot();
|
||||||
};
|
};
|
||||||
|
|
||||||
return EditAction;
|
return EditAction;
|
||||||
|
@ -85,8 +85,9 @@ define(
|
|||||||
SaveAction.appliesTo = function (context) {
|
SaveAction.appliesTo = function (context) {
|
||||||
var domainObject = (context || {}).domainObject;
|
var domainObject = (context || {}).domainObject;
|
||||||
return domainObject !== undefined &&
|
return domainObject !== undefined &&
|
||||||
domainObject.getModel().persisted !== undefined &&
|
domainObject.hasCapability('editor') &&
|
||||||
domainObject.getCapability("status").get("editing");
|
domainObject.getCapability('editor').isEditContextRoot() &&
|
||||||
|
domainObject.getModel().persisted !== undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
return SaveAction;
|
return SaveAction;
|
||||||
|
@ -135,8 +135,8 @@ define(
|
|||||||
return copyService.perform(domainObject, parent, allowClone);
|
return copyService.perform(domainObject, parent, allowClone);
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancelEditingAfterClone(clonedObject) {
|
function commitEditingAfterClone(clonedObject) {
|
||||||
return domainObject.getCapability("editor").cancel()
|
return domainObject.getCapability("editor").save()
|
||||||
.then(resolveWith(clonedObject));
|
.then(resolveWith(clonedObject));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +144,7 @@ define(
|
|||||||
.then(doWizardSave)
|
.then(doWizardSave)
|
||||||
.then(getParent)
|
.then(getParent)
|
||||||
.then(cloneIntoParent)
|
.then(cloneIntoParent)
|
||||||
.then(cancelEditingAfterClone)
|
.then(commitEditingAfterClone)
|
||||||
.catch(resolveWith(false));
|
.catch(resolveWith(false));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -157,7 +157,8 @@ define(
|
|||||||
SaveAsAction.appliesTo = function (context) {
|
SaveAsAction.appliesTo = function (context) {
|
||||||
var domainObject = (context || {}).domainObject;
|
var domainObject = (context || {}).domainObject;
|
||||||
return domainObject !== undefined &&
|
return domainObject !== undefined &&
|
||||||
domainObject.getCapability("status").get("editing") &&
|
domainObject.hasCapability('editor') &&
|
||||||
|
domainObject.getCapability('editor').isEditContextRoot() &&
|
||||||
domainObject.getModel().persisted === undefined;
|
domainObject.getModel().persisted === undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,24 +24,42 @@ define(
|
|||||||
[],
|
[],
|
||||||
function () {
|
function () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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).
|
||||||
|
* @param transactionService
|
||||||
|
* @param domainObject
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
function EditorCapability(
|
function EditorCapability(
|
||||||
transactionService,
|
transactionService,
|
||||||
dirtyModelCache,
|
|
||||||
domainObject
|
domainObject
|
||||||
) {
|
) {
|
||||||
this.transactionService = transactionService;
|
this.transactionService = transactionService;
|
||||||
this.dirtyModelCache = dirtyModelCache;
|
|
||||||
this.domainObject = domainObject;
|
this.domainObject = domainObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate an editing session. This will start a transaction during
|
||||||
|
* which any persist operations will be deferred until either save()
|
||||||
|
* or cancel() are called.
|
||||||
|
*/
|
||||||
EditorCapability.prototype.edit = function () {
|
EditorCapability.prototype.edit = function () {
|
||||||
this.transactionService.startTransaction();
|
this.transactionService.startTransaction();
|
||||||
this.domainObject.getCapability('status').set('editing', true);
|
this.domainObject.getCapability('status').set('editing', true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function isEditContextRoot (domainObject) {
|
||||||
|
return domainObject.getCapability('status').get('editing');
|
||||||
|
}
|
||||||
|
|
||||||
function isEditing (domainObject) {
|
function isEditing (domainObject) {
|
||||||
return domainObject.getCapability('status').get('editing') ||
|
return isEditContextRoot(domainObject) ||
|
||||||
domainObject.hasCapability('context') && isEditing(domainObject.getCapability('context').getParent());
|
domainObject.hasCapability('context') &&
|
||||||
|
isEditing(domainObject.getCapability('context').getParent());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,6 +71,20 @@ define(
|
|||||||
return isEditing(this.domainObject);
|
return isEditing(this.domainObject);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this the root editing object (ie. the object that the user
|
||||||
|
* clicked 'edit' on)?
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
EditorCapability.prototype.isEditContextRoot = function () {
|
||||||
|
return isEditContextRoot(this.domainObject);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save any changes from this editing session. This will flush all
|
||||||
|
* pending persists and end the current transaction
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
EditorCapability.prototype.save = function () {
|
EditorCapability.prototype.save = function () {
|
||||||
var domainObject = this.domainObject;
|
var domainObject = this.domainObject;
|
||||||
return this.transactionService.commit().then(function() {
|
return this.transactionService.commit().then(function() {
|
||||||
@ -62,6 +94,11 @@ define(
|
|||||||
|
|
||||||
EditorCapability.prototype.invoke = EditorCapability.prototype.edit;
|
EditorCapability.prototype.invoke = EditorCapability.prototype.edit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel the current editing session. This will discard any pending
|
||||||
|
* persist operations
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
EditorCapability.prototype.cancel = function () {
|
EditorCapability.prototype.cancel = function () {
|
||||||
var domainObject = this.domainObject;
|
var domainObject = this.domainObject;
|
||||||
return this.transactionService.cancel().then(function(){
|
return this.transactionService.cancel().then(function(){
|
||||||
@ -70,15 +107,14 @@ define(
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {boolean} true if there have been any domain model
|
||||||
|
* modifications since the last persist, false otherwise.
|
||||||
|
*/
|
||||||
EditorCapability.prototype.dirty = function () {
|
EditorCapability.prototype.dirty = function () {
|
||||||
return this.dirtyModelCache.isDirty(this.domainObject);
|
return (this.domainObject.getModel().modified || 0) > (this.domainObject.getModel().persisted || 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
EditorCapability.prototype.appliesTo = function(context) {
|
|
||||||
var domainObject = context.domainObject;
|
|
||||||
return domainObject && domainObject.getType().hasFeature("creation");
|
|
||||||
}
|
|
||||||
|
|
||||||
return EditorCapability;
|
return EditorCapability;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -26,23 +26,30 @@ define(
|
|||||||
function (TransactionalPersistenceCapability) {
|
function (TransactionalPersistenceCapability) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function TransactionDecorator(
|
/**
|
||||||
|
* Wraps the [PersistenceCapability]{@link PersistenceCapability} with
|
||||||
|
* transactional capabilities.
|
||||||
|
* @param $q
|
||||||
|
* @param transactionService
|
||||||
|
* @param capabilityService
|
||||||
|
* @see TransactionalPersistenceCapability
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function TransactionCapabilityDecorator(
|
||||||
$q,
|
$q,
|
||||||
transactionService,
|
transactionService,
|
||||||
dirtyModelCache,
|
|
||||||
capabilityService
|
capabilityService
|
||||||
) {
|
) {
|
||||||
this.capabilityService = capabilityService;
|
this.capabilityService = capabilityService;
|
||||||
this.transactionService = transactionService;
|
this.transactionService = transactionService;
|
||||||
this.dirtyModelCache = dirtyModelCache;
|
|
||||||
this.$q = $q;
|
this.$q = $q;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decorate PersistenceCapability to ignore persistence calls when a
|
* Decorate PersistenceCapability to queue persistence calls when a
|
||||||
* transaction is in progress.
|
* transaction is in progress.
|
||||||
*/
|
*/
|
||||||
TransactionDecorator.prototype.getCapabilities = function (model) {
|
TransactionCapabilityDecorator.prototype.getCapabilities = function (model) {
|
||||||
var self = this,
|
var self = this,
|
||||||
capabilities = this.capabilityService.getCapabilities(model),
|
capabilities = this.capabilityService.getCapabilities(model),
|
||||||
persistenceCapability = capabilities.persistence;
|
persistenceCapability = capabilities.persistence;
|
||||||
@ -55,7 +62,6 @@ define(
|
|||||||
return new TransactionalPersistenceCapability(
|
return new TransactionalPersistenceCapability(
|
||||||
self.$q,
|
self.$q,
|
||||||
self.transactionService,
|
self.transactionService,
|
||||||
self.dirtyModelCache,
|
|
||||||
original,
|
original,
|
||||||
domainObject
|
domainObject
|
||||||
);
|
);
|
||||||
@ -63,6 +69,6 @@ define(
|
|||||||
return capabilities;
|
return capabilities;
|
||||||
};
|
};
|
||||||
|
|
||||||
return TransactionDecorator;
|
return TransactionCapabilityDecorator;
|
||||||
}
|
}
|
||||||
);
|
);
|
@ -26,44 +26,51 @@ define(
|
|||||||
function () {
|
function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps persistence capability to enable transactions. Transactions
|
||||||
|
* will cause persist calls not to be invoked immediately, but
|
||||||
|
* rather queued until [EditorCapability.save()]{@link EditorCapability#save}
|
||||||
|
* or [EditorCapability.cancel()]{@link EditorCapability#cancel} are
|
||||||
|
* called.
|
||||||
|
* @memberof platform/commonUI/edit/capabilities
|
||||||
|
* @param $q
|
||||||
|
* @param transactionService
|
||||||
|
* @param persistenceCapability
|
||||||
|
* @param domainObject
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
function TransactionalPersistenceCapability(
|
function TransactionalPersistenceCapability(
|
||||||
$q,
|
$q,
|
||||||
transactionService,
|
transactionService,
|
||||||
dirtyModelCache,
|
|
||||||
persistenceCapability,
|
persistenceCapability,
|
||||||
domainObject
|
domainObject
|
||||||
) {
|
) {
|
||||||
this.transactionService = transactionService;
|
this.transactionService = transactionService;
|
||||||
this.dirtyModelCache = dirtyModelCache;
|
this.persistenceCapability = persistenceCapability;
|
||||||
this.persistenceCapability = Object.create(persistenceCapability);
|
|
||||||
this.domainObject = domainObject;
|
this.domainObject = domainObject;
|
||||||
this.$q = $q;
|
this.$q = $q;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The wrapped persist function. If a transaction is active, persist
|
||||||
|
* will be queued until the transaction is committed or cancelled.
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
TransactionalPersistenceCapability.prototype.persist = function () {
|
TransactionalPersistenceCapability.prototype.persist = function () {
|
||||||
var domainObject = this.domainObject,
|
if (this.transactionService.isActive()) {
|
||||||
dirtyModelCache = this.dirtyModelCache;
|
this.transactionService.addToTransaction(
|
||||||
if (this.transactionService.isActive() && !this.transactionService.isCommitting()) {
|
this.persistenceCapability.persist.bind(this.persistenceCapability),
|
||||||
dirtyModelCache.markDirty(domainObject);
|
this.persistenceCapability.refresh.bind(this.persistenceCapability)
|
||||||
//Using $q here because need to return something
|
);
|
||||||
// from which 'catch' can be chained
|
//Need to return a promise from this function
|
||||||
return this.$q.when(true);
|
return this.$q.when(true);
|
||||||
} else {
|
} else {
|
||||||
return this.persistenceCapability.persist().then(function (result) {
|
return this.persistenceCapability.persist();
|
||||||
dirtyModelCache.markClean(domainObject);
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionalPersistenceCapability.prototype.refresh = function () {
|
TransactionalPersistenceCapability.prototype.refresh = function () {
|
||||||
var domainObject = this.domainObject,
|
return this.persistenceCapability.refresh();
|
||||||
dirtyModelCache = this.dirtyModelCache;
|
|
||||||
|
|
||||||
return this.persistenceCapability.refresh().then(function (result) {
|
|
||||||
dirtyModelCache.markClean(domainObject);
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionalPersistenceCapability.prototype.getSpace = function () {
|
TransactionalPersistenceCapability.prototype.getSpace = function () {
|
||||||
|
@ -73,7 +73,8 @@ define(
|
|||||||
function isEditing(context) {
|
function isEditing(context) {
|
||||||
var domainObject = (context || {}).domainObject;
|
var domainObject = (context || {}).domainObject;
|
||||||
return domainObject
|
return domainObject
|
||||||
&& domainObject.getCapability('status').get('editing');
|
&& domainObject.hasCapability('editor')
|
||||||
|
&& domainObject.getCapability('editor').isEditContextRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
EditActionPolicy.prototype.allow = function (action, context) {
|
EditActionPolicy.prototype.allow = function (action, context) {
|
||||||
|
@ -34,6 +34,11 @@ define(
|
|||||||
* from context menu of non-editable objects, when navigated object
|
* from context menu of non-editable objects, when navigated object
|
||||||
* is being edited
|
* is being edited
|
||||||
* @constructor
|
* @constructor
|
||||||
|
* @param navigationService
|
||||||
|
* @param editModeBlacklist A blacklist of actions disallowed from
|
||||||
|
* context menu when navigated object is being edited
|
||||||
|
* @param nonEditContextBlacklist A blacklist of actions disallowed
|
||||||
|
* from context menu of non-editable objects, when navigated object
|
||||||
* @implements {Policy.<Action, ActionContext>}
|
* @implements {Policy.<Action, ActionContext>}
|
||||||
*/
|
*/
|
||||||
function EditContextualActionPolicy(navigationService, editModeBlacklist, nonEditContextBlacklist) {
|
function EditContextualActionPolicy(navigationService, editModeBlacklist, nonEditContextBlacklist) {
|
||||||
@ -51,7 +56,7 @@ define(
|
|||||||
navigatedObject = this.navigationService.getNavigation(),
|
navigatedObject = this.navigationService.getNavigation(),
|
||||||
actionMetadata = action.getMetadata ? action.getMetadata() : {};
|
actionMetadata = action.getMetadata ? action.getMetadata() : {};
|
||||||
|
|
||||||
if (navigatedObject.getCapability("status").get("editing")) {
|
if (navigatedObject.hasCapability("editor") && navigatedObject.getCapability("editor").isEditContextRoot()) {
|
||||||
if (selectedObject.hasCapability("editor") && selectedObject.getCapability("editor").inEditContext()){
|
if (selectedObject.hasCapability("editor") && selectedObject.getCapability("editor").inEditContext()){
|
||||||
//Target is within the editing context
|
//Target is within the editing context
|
||||||
return this.editBlacklist.indexOf(actionMetadata.key) === -1;
|
return this.editBlacklist.indexOf(actionMetadata.key) === -1;
|
||||||
|
@ -41,12 +41,11 @@ define(
|
|||||||
EditNavigationPolicy.prototype.isDirty = function(domainObject) {
|
EditNavigationPolicy.prototype.isDirty = function(domainObject) {
|
||||||
var navigatedObject = domainObject,
|
var navigatedObject = domainObject,
|
||||||
editorCapability = navigatedObject &&
|
editorCapability = navigatedObject &&
|
||||||
navigatedObject.getCapability("editor"),
|
navigatedObject.getCapability("editor");
|
||||||
statusCapability = navigatedObject &&
|
|
||||||
navigatedObject.getCapability("status");
|
|
||||||
|
|
||||||
return statusCapability && statusCapability.get('editing') &&
|
return editorCapability &&
|
||||||
editorCapability && editorCapability.dirty();
|
editorCapability.isEditContextRoot() &&
|
||||||
|
editorCapability.dirty();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,10 +35,13 @@ define([], function () {
|
|||||||
EditableMovePolicy.prototype.allow = function (action, context) {
|
EditableMovePolicy.prototype.allow = function (action, context) {
|
||||||
var domainObject = context.domainObject,
|
var domainObject = context.domainObject,
|
||||||
selectedObject = context.selectedObject,
|
selectedObject = context.selectedObject,
|
||||||
key = action.getMetadata().key;
|
key = action.getMetadata().key,
|
||||||
|
isDomainObjectEditing = domainObject.hasCapability('editor') &&
|
||||||
|
domainObject.getCapability('editor').inEditContext();
|
||||||
|
|
||||||
if (key === 'move' && domainObject.hasCapability('editor') && domainObject.getCapability('editor').inEditContext()) {
|
if (key === 'move' && isDomainObjectEditing) {
|
||||||
return !!selectedObject && selectedObject.hasCapability('editor') && selectedObject.getCapability('editor').inEditContext();
|
return !!selectedObject && selectedObject.hasCapability('editor') &&
|
||||||
|
selectedObject.getCapability('editor').inEditContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Like all policies, allow by default.
|
// Like all policies, allow by default.
|
||||||
|
@ -136,7 +136,7 @@ define(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (representedObject.getCapability('status').get('editing')){
|
if (representedObject.hasCapability('editor') && representedObject.getCapability('editor').isEditContextRoot()){
|
||||||
setEditing();
|
setEditing();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
|
||||||
* Administration. All rights reserved.
|
|
||||||
*
|
|
||||||
* Open MCT Web 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 Web 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.
|
|
||||||
*****************************************************************************/
|
|
||||||
/*global define*/
|
|
||||||
define(
|
|
||||||
[],
|
|
||||||
function() {
|
|
||||||
function DirtyModelCache(topic) {
|
|
||||||
this.cache = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
DirtyModelCache.prototype.get = function () {
|
|
||||||
return this.cache;
|
|
||||||
};
|
|
||||||
|
|
||||||
DirtyModelCache.prototype.isDirty = function (domainObject) {
|
|
||||||
return !!this.cache[domainObject.getId()];
|
|
||||||
};
|
|
||||||
|
|
||||||
DirtyModelCache.prototype.markDirty = function (domainObject) {
|
|
||||||
this.cache[domainObject.getId()] = domainObject;
|
|
||||||
};
|
|
||||||
|
|
||||||
DirtyModelCache.prototype.markClean = function (domainObject) {
|
|
||||||
delete this.cache[domainObject.getId()];
|
|
||||||
};
|
|
||||||
|
|
||||||
return DirtyModelCache;
|
|
||||||
});
|
|
@ -25,64 +25,90 @@ define(
|
|||||||
function() {
|
function() {
|
||||||
/**
|
/**
|
||||||
* Implements an application-wide transaction state. Once a
|
* Implements an application-wide transaction state. Once a
|
||||||
* transaction is started, calls to PersistenceCapability.persist()
|
* transaction is started, calls to
|
||||||
|
* [PersistenceCapability.persist()]{@link PersistenceCapability#persist}
|
||||||
* will be deferred until a subsequent call to
|
* will be deferred until a subsequent call to
|
||||||
* TransactionService.commit() is made.
|
* [TransactionService.commit]{@link TransactionService#commit} is made.
|
||||||
*
|
*
|
||||||
|
* @memberof platform/commonUI/edit/services
|
||||||
* @param $q
|
* @param $q
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function TransactionService($q, dirtyModelCache) {
|
function TransactionService($q, $log) {
|
||||||
this.$q = $q;
|
this.$q = $q;
|
||||||
|
this.$log = $log;
|
||||||
this.transaction = false;
|
this.transaction = false;
|
||||||
this.committing = false;
|
|
||||||
this.cache = dirtyModelCache;
|
this.onCommits = [];
|
||||||
|
this.onCancels = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a transaction. While a transaction is active all calls to
|
||||||
|
* [PersistenceCapability.persist](@link PersistenceCapability#persist)
|
||||||
|
* will be queued until [commit]{@link #commit} or [cancel]{@link
|
||||||
|
* #cancel} are called
|
||||||
|
*/
|
||||||
TransactionService.prototype.startTransaction = function () {
|
TransactionService.prototype.startTransaction = function () {
|
||||||
if (this.transaction)
|
if (this.transaction) {
|
||||||
console.error("Transaction already in progress")
|
//Log error because this is a programming error if it occurs.
|
||||||
|
this.$log.error("Transaction already in progress");
|
||||||
|
}
|
||||||
this.transaction = true;
|
this.transaction = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {boolean} If true, indicates that a transaction is in progress
|
||||||
|
*/
|
||||||
TransactionService.prototype.isActive = function () {
|
TransactionService.prototype.isActive = function () {
|
||||||
return this.transaction;
|
return this.transaction;
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionService.prototype.isCommitting = function () {
|
/**
|
||||||
return this.committing;
|
* Adds provided functions to a queue to be called on
|
||||||
|
* [.commit()]{@link #commit} or
|
||||||
|
* [.cancel()]{@link #commit}
|
||||||
|
* @param onCommit A function to call on commit
|
||||||
|
* @param onCancel A function to call on cancel
|
||||||
|
*/
|
||||||
|
TransactionService.prototype.addToTransaction = function (onCommit, onCancel) {
|
||||||
|
if (this.transaction) {
|
||||||
|
this.onCommits.push(onCommit);
|
||||||
|
if (onCancel) {
|
||||||
|
this.onCancels.push(onCancel);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//Log error because this is a programming error if it occurs.
|
||||||
|
this.$log.error("No transaction in progress");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All persist calls deferred since the beginning of the transaction
|
* All persist calls deferred since the beginning of the transaction
|
||||||
* will be committed. Any failures will be reported via a promise
|
* will be committed.
|
||||||
* rejection.
|
*
|
||||||
* @returns {*}
|
* @returns {Promise} resolved when all persist operations have
|
||||||
|
* completed. Will reject if any commit operations fail
|
||||||
*/
|
*/
|
||||||
TransactionService.prototype.commit = function () {
|
TransactionService.prototype.commit = function () {
|
||||||
var self = this;
|
var self = this,
|
||||||
cache = this.cache.get();
|
promises = [],
|
||||||
|
onCommit;
|
||||||
|
|
||||||
this.committing = true;
|
while (this.onCommits.length > 0) { // ...using a while in case some onCommit adds to transaction
|
||||||
|
onCommit = this.onCommits.pop();
|
||||||
function keyToObject(key) {
|
try { // ...also don't want to fail mid-loop...
|
||||||
return cache[key];
|
promises.push(onCommit());
|
||||||
|
} catch (e) {
|
||||||
|
this.$log.error("Error committing transaction.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return this.$q.all(promises).then( function () {
|
||||||
|
self.transaction = false;
|
||||||
|
|
||||||
function objectToPromise(object) {
|
self.onCommits = [];
|
||||||
return object.getCapability('persistence').persist();
|
self.onCancels = [];
|
||||||
}
|
});
|
||||||
|
|
||||||
return this.$q.all(
|
|
||||||
Object.keys(cache)
|
|
||||||
.map(keyToObject)
|
|
||||||
.map(objectToPromise))
|
|
||||||
.then(function () {
|
|
||||||
self.transaction = false;
|
|
||||||
this.committing = false;
|
|
||||||
}).catch(function() {
|
|
||||||
return this.committing = false;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -95,23 +121,23 @@ define(
|
|||||||
*/
|
*/
|
||||||
TransactionService.prototype.cancel = function () {
|
TransactionService.prototype.cancel = function () {
|
||||||
var self = this,
|
var self = this,
|
||||||
cache = this.cache.get();
|
results = [],
|
||||||
|
onCancel;
|
||||||
|
|
||||||
function keyToObject(key) {
|
while (this.onCancels.length > 0) {
|
||||||
return cache[key];
|
onCancel = this.onCancels.pop();
|
||||||
|
try {
|
||||||
|
results.push(onCancel());
|
||||||
|
} catch (error) {
|
||||||
|
this.$log.error("Error committing transaction.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return this.$q.all(results).then(function () {
|
||||||
|
self.transaction = false;
|
||||||
|
|
||||||
function objectToPromise(object) {
|
self.onCommits = [];
|
||||||
return self.$q.when(object.getModel().persisted && object.getCapability('persistence').refresh());
|
self.onCancels = [];
|
||||||
}
|
});
|
||||||
|
|
||||||
return this.$q.all(Object.keys(cache)
|
|
||||||
.map(keyToObject)
|
|
||||||
.map(objectToPromise))
|
|
||||||
.then(function () {
|
|
||||||
self.transaction = false;
|
|
||||||
this.committing = false;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return TransactionService;
|
return TransactionService;
|
||||||
|
@ -30,7 +30,9 @@ define(
|
|||||||
mockLog,
|
mockLog,
|
||||||
mockDomainObject,
|
mockDomainObject,
|
||||||
mockType,
|
mockType,
|
||||||
|
mockEditor,
|
||||||
actionContext,
|
actionContext,
|
||||||
|
capabilities,
|
||||||
action;
|
action;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
@ -40,7 +42,7 @@ define(
|
|||||||
);
|
);
|
||||||
mockNavigationService = jasmine.createSpyObj(
|
mockNavigationService = jasmine.createSpyObj(
|
||||||
"navigationService",
|
"navigationService",
|
||||||
[ "setNavigation", "getNavigation" ]
|
[ "setNavigation", "getNavigation", "addListener", "removeListener" ]
|
||||||
);
|
);
|
||||||
mockLog = jasmine.createSpyObj(
|
mockLog = jasmine.createSpyObj(
|
||||||
"$log",
|
"$log",
|
||||||
@ -48,14 +50,26 @@ define(
|
|||||||
);
|
);
|
||||||
mockDomainObject = jasmine.createSpyObj(
|
mockDomainObject = jasmine.createSpyObj(
|
||||||
"domainObject",
|
"domainObject",
|
||||||
[ "getId", "getModel", "getCapability" ]
|
[ "getId", "getModel", "getCapability", "hasCapability", "useCapability" ]
|
||||||
);
|
);
|
||||||
mockType = jasmine.createSpyObj(
|
mockType = jasmine.createSpyObj(
|
||||||
"type",
|
"type",
|
||||||
[ "hasFeature" ]
|
[ "hasFeature" ]
|
||||||
);
|
);
|
||||||
|
mockEditor = jasmine.createSpyObj(
|
||||||
|
"editorCapability",
|
||||||
|
["edit", "isEditContextRoot", "cancel"]
|
||||||
|
);
|
||||||
|
|
||||||
mockDomainObject.getCapability.andReturn(mockType);
|
capabilities = {
|
||||||
|
type: mockType,
|
||||||
|
editor: mockEditor
|
||||||
|
};
|
||||||
|
|
||||||
|
mockDomainObject.getCapability.andCallFake( function (name) {
|
||||||
|
return capabilities[name];
|
||||||
|
});
|
||||||
|
mockDomainObject.hasCapability.andReturn(true);
|
||||||
mockType.hasFeature.andReturn(true);
|
mockType.hasFeature.andReturn(true);
|
||||||
|
|
||||||
actionContext = { domainObject: mockDomainObject };
|
actionContext = { domainObject: mockDomainObject };
|
||||||
@ -68,51 +82,34 @@ define(
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("is only applicable when a domain object is present", function () {
|
it("is only applicable when an editable domain object is present", function () {
|
||||||
expect(EditAction.appliesTo(actionContext)).toBeTruthy();
|
expect(EditAction.appliesTo(actionContext)).toBeTruthy();
|
||||||
expect(EditAction.appliesTo({})).toBeFalsy();
|
expect(EditAction.appliesTo({})).toBeFalsy();
|
||||||
|
|
||||||
|
expect(mockDomainObject.hasCapability).toHaveBeenCalledWith('editor');
|
||||||
// Should have checked for creatability
|
// Should have checked for creatability
|
||||||
expect(mockType.hasFeature).toHaveBeenCalledWith('creation');
|
expect(mockType.hasFeature).toHaveBeenCalledWith('creation');
|
||||||
});
|
});
|
||||||
|
|
||||||
//TODO: Disabled for NEM Beta
|
it("is only applicable to objects not already in edit mode", function () {
|
||||||
xit("changes URL path to edit mode when performed", function () {
|
mockEditor.isEditContextRoot.andReturn(false);
|
||||||
|
expect(EditAction.appliesTo(actionContext)).toBe(true);
|
||||||
|
mockEditor.isEditContextRoot.andReturn(true);
|
||||||
|
expect(EditAction.appliesTo(actionContext)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it ("cancels editing when user navigates away", function () {
|
||||||
action.perform();
|
action.perform();
|
||||||
expect(mockLocation.path).toHaveBeenCalledWith("/edit");
|
expect(mockNavigationService.addListener).toHaveBeenCalled();
|
||||||
|
mockNavigationService.addListener.mostRecentCall.args[0]();
|
||||||
|
expect(mockEditor.cancel).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
//TODO: Disabled for NEM Beta
|
it ("invokes the Edit capability on the object", function () {
|
||||||
xit("ensures that the edited object is navigated-to", function () {
|
|
||||||
action.perform();
|
action.perform();
|
||||||
expect(mockNavigationService.setNavigation)
|
expect(mockDomainObject.useCapability).toHaveBeenCalledWith("editor");
|
||||||
.toHaveBeenCalledWith(mockDomainObject);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//TODO: Disabled for NEM Beta
|
|
||||||
xit("logs a warning if constructed when inapplicable", function () {
|
|
||||||
// Verify precondition (ensure warn wasn't called during setup)
|
|
||||||
expect(mockLog.warn).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
// Should not have hit an exception...
|
|
||||||
new EditAction(
|
|
||||||
mockLocation,
|
|
||||||
mockNavigationService,
|
|
||||||
mockLog,
|
|
||||||
{}
|
|
||||||
).perform();
|
|
||||||
|
|
||||||
// ...but should have logged a warning
|
|
||||||
expect(mockLog.warn).toHaveBeenCalled();
|
|
||||||
|
|
||||||
// And should not have had other interactions
|
|
||||||
expect(mockLocation.path)
|
|
||||||
.not.toHaveBeenCalled();
|
|
||||||
expect(mockNavigationService.setNavigation)
|
|
||||||
.not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
@ -52,7 +52,7 @@ define(
|
|||||||
);
|
);
|
||||||
mockEditorCapability = jasmine.createSpyObj(
|
mockEditorCapability = jasmine.createSpyObj(
|
||||||
"editor",
|
"editor",
|
||||||
[ "save", "cancel" ]
|
[ "save", "cancel", "isEditContextRoot" ]
|
||||||
);
|
);
|
||||||
mockActionCapability = jasmine.createSpyObj(
|
mockActionCapability = jasmine.createSpyObj(
|
||||||
"actionCapability",
|
"actionCapability",
|
||||||
@ -71,7 +71,7 @@ define(
|
|||||||
});
|
});
|
||||||
mockDomainObject.getModel.andReturn({persisted: 0});
|
mockDomainObject.getModel.andReturn({persisted: 0});
|
||||||
mockEditorCapability.save.andReturn(mockPromise(true));
|
mockEditorCapability.save.andReturn(mockPromise(true));
|
||||||
mockDomainObject.getOriginalObject.andReturn(mockDomainObject);
|
mockEditorCapability.isEditContextRoot.andReturn(true);
|
||||||
|
|
||||||
action = new SaveAction(actionContext);
|
action = new SaveAction(actionContext);
|
||||||
|
|
||||||
@ -97,6 +97,13 @@ define(
|
|||||||
action.perform();
|
action.perform();
|
||||||
expect(mockEditorCapability.save).toHaveBeenCalled();
|
expect(mockEditorCapability.save).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("navigates to the object after saving",
|
||||||
|
function () {
|
||||||
|
action.perform();
|
||||||
|
expect(mockActionCapability.perform).toHaveBeenCalledWith("navigate");
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
@ -78,10 +78,11 @@ define(
|
|||||||
|
|
||||||
mockEditorCapability = jasmine.createSpyObj(
|
mockEditorCapability = jasmine.createSpyObj(
|
||||||
"editor",
|
"editor",
|
||||||
[ "save", "cancel" ]
|
[ "save", "cancel", "isEditContextRoot" ]
|
||||||
);
|
);
|
||||||
mockEditorCapability.cancel.andReturn(mockPromise(undefined));
|
mockEditorCapability.cancel.andReturn(mockPromise(undefined));
|
||||||
mockEditorCapability.save.andReturn(mockPromise(true));
|
mockEditorCapability.save.andReturn(mockPromise(true));
|
||||||
|
mockEditorCapability.isEditContextRoot.andReturn(true);
|
||||||
capabilities.editor = mockEditorCapability;
|
capabilities.editor = mockEditorCapability;
|
||||||
|
|
||||||
mockActionCapability = jasmine.createSpyObj(
|
mockActionCapability = jasmine.createSpyObj(
|
||||||
|
@ -25,94 +25,150 @@ define(
|
|||||||
function (EditorCapability) {
|
function (EditorCapability) {
|
||||||
|
|
||||||
describe("The editor capability", function () {
|
describe("The editor capability", function () {
|
||||||
var mockPersistence,
|
var mockDomainObject,
|
||||||
mockEditableObject,
|
capabilities,
|
||||||
mockDomainObject,
|
mockParentObject,
|
||||||
mockCache,
|
mockTransactionService,
|
||||||
mockCallback,
|
mockStatusCapability,
|
||||||
model,
|
mockParentStatus,
|
||||||
|
mockContextCapability,
|
||||||
capability;
|
capability;
|
||||||
|
|
||||||
beforeEach(function () {
|
function fastPromise(val) {
|
||||||
mockPersistence = jasmine.createSpyObj(
|
return {
|
||||||
"persistence",
|
then: function (callback) {
|
||||||
[ "persist" ]
|
return callback(val);
|
||||||
);
|
}
|
||||||
mockEditableObject = {
|
|
||||||
getModel: function () { return model; }
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
mockDomainObject = jasmine.createSpyObj(
|
mockDomainObject = jasmine.createSpyObj(
|
||||||
"domainObject",
|
"domainObject",
|
||||||
[ "getId", "getModel", "getCapability", "useCapability" ]
|
["getId", "getModel", "hasCapability", "getCapability", "useCapability"]
|
||||||
);
|
);
|
||||||
mockCache = jasmine.createSpyObj(
|
mockParentObject = jasmine.createSpyObj(
|
||||||
"cache",
|
"domainObject",
|
||||||
[ "saveAll", "markClean" ]
|
["getId", "getModel", "hasCapability", "getCapability", "useCapability"]
|
||||||
);
|
);
|
||||||
mockCallback = jasmine.createSpy("callback");
|
mockTransactionService = jasmine.createSpyObj(
|
||||||
|
"transactionService",
|
||||||
|
[
|
||||||
|
"startTransaction",
|
||||||
|
"commit",
|
||||||
|
"cancel"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
mockTransactionService.commit.andReturn(fastPromise());
|
||||||
|
mockTransactionService.cancel.andReturn(fastPromise());
|
||||||
|
|
||||||
mockDomainObject.getCapability.andReturn(mockPersistence);
|
mockStatusCapability = jasmine.createSpyObj(
|
||||||
|
"statusCapability",
|
||||||
|
["get", "set"]
|
||||||
|
);
|
||||||
|
mockParentStatus = jasmine.createSpyObj(
|
||||||
|
"statusCapability",
|
||||||
|
["get", "set"]
|
||||||
|
);
|
||||||
|
mockContextCapability = jasmine.createSpyObj(
|
||||||
|
"contextCapability",
|
||||||
|
["getParent"]
|
||||||
|
);
|
||||||
|
mockContextCapability.getParent.andReturn(mockParentObject);
|
||||||
|
|
||||||
model = { someKey: "some value", x: 42 };
|
capabilities = {
|
||||||
|
context: mockContextCapability,
|
||||||
|
status: mockStatusCapability
|
||||||
|
};
|
||||||
|
|
||||||
|
mockDomainObject.hasCapability.andCallFake(function(name) {
|
||||||
|
return capabilities[name] !== undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
mockDomainObject.getCapability.andCallFake(function (name) {
|
||||||
|
return capabilities[name];
|
||||||
|
});
|
||||||
|
|
||||||
|
mockParentObject.getCapability.andReturn(mockParentStatus);
|
||||||
|
mockParentObject.hasCapability.andReturn(false);
|
||||||
|
|
||||||
capability = new EditorCapability(
|
capability = new EditorCapability(
|
||||||
mockPersistence,
|
mockTransactionService,
|
||||||
mockEditableObject,
|
mockDomainObject
|
||||||
mockDomainObject,
|
|
||||||
mockCache
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
//TODO: Disabled for NEM Beta
|
it("starts a transaction when edit is invoked", function () {
|
||||||
xit("mutates the real domain object on nonrecursive save", function () {
|
capability.edit();
|
||||||
capability.save(true).then(mockCallback);
|
expect(mockTransactionService.startTransaction).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
// Wait for promise to resolve
|
it("sets editing status on object", function () {
|
||||||
waitsFor(function () {
|
capability.edit();
|
||||||
return mockCallback.calls.length > 0;
|
expect(mockStatusCapability.set).toHaveBeenCalledWith("editing", true);
|
||||||
}, 250);
|
});
|
||||||
|
|
||||||
runs(function () {
|
it("uses editing status to determine editing context root", function () {
|
||||||
expect(mockDomainObject.useCapability)
|
capability.edit();
|
||||||
.toHaveBeenCalledWith("mutation", jasmine.any(Function));
|
mockStatusCapability.get.andReturn(false);
|
||||||
// We should get the model from the editable object back
|
expect(capability.isEditContextRoot()).toBe(false);
|
||||||
expect(
|
mockStatusCapability.get.andReturn(true);
|
||||||
mockDomainObject.useCapability.mostRecentCall.args[1]()
|
expect(capability.isEditContextRoot()).toBe(true);
|
||||||
).toEqual(model);
|
});
|
||||||
|
|
||||||
|
it("inEditingContext returns true if parent object is being" +
|
||||||
|
" edited", function () {
|
||||||
|
mockStatusCapability.get.andReturn(false);
|
||||||
|
mockParentStatus.get.andReturn(false);
|
||||||
|
expect(capability.inEditContext()).toBe(false);
|
||||||
|
mockParentStatus.get.andReturn(true);
|
||||||
|
expect(capability.inEditContext()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("save", function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
capability.edit();
|
||||||
|
capability.save();
|
||||||
|
});
|
||||||
|
it("commits the transaction", function () {
|
||||||
|
expect(mockTransactionService.commit).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
it("resets the edit state", function () {
|
||||||
|
expect(mockStatusCapability.set).toHaveBeenCalledWith('editing', false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
//TODO: Disabled for NEM Beta
|
describe("cancel", function() {
|
||||||
xit("tells the cache to save others", function () {
|
beforeEach(function() {
|
||||||
capability.save().then(mockCallback);
|
capability.edit();
|
||||||
|
capability.cancel();
|
||||||
// Wait for promise to resolve
|
});
|
||||||
waitsFor(function () {
|
it("cancels the transaction", function () {
|
||||||
return mockCallback.calls.length > 0;
|
expect(mockTransactionService.cancel).toHaveBeenCalled();
|
||||||
}, 250);
|
});
|
||||||
|
it("resets the edit state", function () {
|
||||||
runs(function () {
|
expect(mockStatusCapability.set).toHaveBeenCalledWith('editing', false);
|
||||||
expect(mockCache.saveAll).toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
//TODO: Disabled for NEM Beta
|
describe("dirty", function() {
|
||||||
xit("has no interactions on cancel", function () {
|
var model = {};
|
||||||
capability.cancel().then(mockCallback);
|
|
||||||
|
|
||||||
// Wait for promise to resolve
|
beforeEach(function() {
|
||||||
waitsFor(function () {
|
mockDomainObject.getModel.andReturn(model);
|
||||||
return mockCallback.calls.length > 0;
|
capability.edit();
|
||||||
}, 250);
|
capability.cancel();
|
||||||
|
});
|
||||||
|
it("returns true if the object has been modified since it" +
|
||||||
|
" was last persisted", function () {
|
||||||
|
model.modified = 0;
|
||||||
|
model.persisted = 0;
|
||||||
|
expect(capability.dirty()).toBe(false);
|
||||||
|
|
||||||
runs(function () {
|
model.modified = 1;
|
||||||
expect(mockDomainObject.useCapability).not.toHaveBeenCalled();
|
expect(capability.dirty()).toBe(true);
|
||||||
expect(mockCache.markClean).not.toHaveBeenCalled();
|
|
||||||
expect(mockCache.saveAll).not.toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
@ -0,0 +1,59 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web 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 Web 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
/*global define,describe,it,expect,beforeEach,waitsFor,runs,jasmine,xit,xdescribe*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
[
|
||||||
|
"../../src/capabilities/TransactionalPersistenceCapability",
|
||||||
|
"../../src/capabilities/TransactionCapabilityDecorator"
|
||||||
|
],
|
||||||
|
function (TransactionalPersistenceCapability, TransactionCapabilityDecorator) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
describe("The transaction capability decorator", function () {
|
||||||
|
var mockQ,
|
||||||
|
mockTransactionService,
|
||||||
|
mockCapabilityService,
|
||||||
|
provider;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
//mockQ = jasmine.createSpyObj("$q", []);
|
||||||
|
mockQ = {};
|
||||||
|
//mockTransactionService =
|
||||||
|
// jasmine.createSpyObj("transactionService", []);
|
||||||
|
mockTransactionService = {};
|
||||||
|
mockCapabilityService = jasmine.createSpyObj("capabilityService", ["getCapabilities"]);
|
||||||
|
mockCapabilityService.getCapabilities.andReturn({
|
||||||
|
persistence: function() {}
|
||||||
|
});
|
||||||
|
|
||||||
|
provider = new TransactionCapabilityDecorator(mockQ, mockTransactionService, mockCapabilityService);
|
||||||
|
|
||||||
|
});
|
||||||
|
it("decorates the persistence capability", function() {
|
||||||
|
var capabilities = provider.getCapabilities();
|
||||||
|
expect(capabilities.persistence({}) instanceof TransactionalPersistenceCapability).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
@ -0,0 +1,92 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web 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 Web 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
/*global define,describe,it,expect,beforeEach,waitsFor,runs,jasmine,xit,xdescribe*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
[
|
||||||
|
"../../src/capabilities/TransactionalPersistenceCapability"
|
||||||
|
],
|
||||||
|
function (TransactionalPersistenceCapability) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function fastPromise(val) {
|
||||||
|
return {
|
||||||
|
then: function(callback) {
|
||||||
|
return callback(val);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("The transactional persistence decorator", function () {
|
||||||
|
var mockQ,
|
||||||
|
mockTransactionService,
|
||||||
|
mockPersistence,
|
||||||
|
mockDomainObject,
|
||||||
|
capability;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
mockQ = jasmine.createSpyObj("$q", ["when"]);
|
||||||
|
mockQ.when.andCallFake(function (val) {
|
||||||
|
return fastPromise(val);
|
||||||
|
});
|
||||||
|
mockTransactionService = jasmine.createSpyObj(
|
||||||
|
"transactionService",
|
||||||
|
["isActive", "addToTransaction"]
|
||||||
|
);
|
||||||
|
mockPersistence = jasmine.createSpyObj(
|
||||||
|
"persistenceCapability",
|
||||||
|
["persist", "refresh"]
|
||||||
|
);
|
||||||
|
|
||||||
|
capability = new TransactionalPersistenceCapability(mockQ, mockTransactionService, mockPersistence, mockDomainObject);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("if no transaction is active, passes through to persistence" +
|
||||||
|
" provider", function() {
|
||||||
|
mockTransactionService.isActive.andReturn(false);
|
||||||
|
capability.persist();
|
||||||
|
expect(mockPersistence.persist).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("if transaction is active, persist call is queued", function() {
|
||||||
|
mockTransactionService.isActive.andReturn(true);
|
||||||
|
capability.persist();
|
||||||
|
expect(mockTransactionService.addToTransaction).toHaveBeenCalled();
|
||||||
|
|
||||||
|
//Test that it was the persist call that was queued
|
||||||
|
mockTransactionService.addToTransaction.mostRecentCall.args[0]();
|
||||||
|
expect(mockPersistence.persist).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("if transaction is active, refresh call is queued as cancel" +
|
||||||
|
" function", function() {
|
||||||
|
mockTransactionService.isActive.andReturn(true);
|
||||||
|
capability.persist();
|
||||||
|
|
||||||
|
//Test that it was the persist call that was queued
|
||||||
|
mockTransactionService.addToTransaction.mostRecentCall.args[1]();
|
||||||
|
expect(mockPersistence.refresh).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
@ -34,7 +34,7 @@ define(
|
|||||||
mockEditAction,
|
mockEditAction,
|
||||||
mockPropertiesAction,
|
mockPropertiesAction,
|
||||||
mockTypeCapability,
|
mockTypeCapability,
|
||||||
mockStatusCapability,
|
mockEditorCapability,
|
||||||
capabilities,
|
capabilities,
|
||||||
plotView,
|
plotView,
|
||||||
policy;
|
policy;
|
||||||
@ -48,11 +48,10 @@ define(
|
|||||||
'getCapability'
|
'getCapability'
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
mockStatusCapability = jasmine.createSpyObj('statusCapability', ['get']);
|
mockEditorCapability = jasmine.createSpyObj('editorCapability', ['isEditContextRoot']);
|
||||||
mockStatusCapability.get.andReturn(false);
|
|
||||||
mockTypeCapability = jasmine.createSpyObj('type', ['getKey']);
|
mockTypeCapability = jasmine.createSpyObj('type', ['getKey']);
|
||||||
capabilities = {
|
capabilities = {
|
||||||
'status': mockStatusCapability,
|
'editor': mockEditorCapability,
|
||||||
'type': mockTypeCapability
|
'type': mockTypeCapability
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -112,7 +111,7 @@ define(
|
|||||||
it("disallows the edit action when object is already being" +
|
it("disallows the edit action when object is already being" +
|
||||||
" edited", function () {
|
" edited", function () {
|
||||||
testViews = [ editableView ];
|
testViews = [ editableView ];
|
||||||
mockStatusCapability.get.andReturn(true);
|
mockEditorCapability.isEditContextRoot.andReturn(true);
|
||||||
expect(policy.allow(mockEditAction, testContext)).toBe(false);
|
expect(policy.allow(mockEditAction, testContext)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -32,16 +32,22 @@ define(
|
|||||||
context,
|
context,
|
||||||
navigatedObject,
|
navigatedObject,
|
||||||
mockDomainObject,
|
mockDomainObject,
|
||||||
|
mockEditorCapability,
|
||||||
metadata,
|
metadata,
|
||||||
editModeBlacklist = ["copy", "follow", "window", "link", "locate"],
|
editModeBlacklist = ["copy", "follow", "window", "link", "locate"],
|
||||||
nonEditContextBlacklist = ["copy", "follow", "properties", "move", "link", "remove", "locate"];
|
nonEditContextBlacklist = ["copy", "follow", "properties", "move", "link", "remove", "locate"];
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
navigatedObject = jasmine.createSpyObj("navigatedObject", ["hasCapability"]);
|
mockEditorCapability = jasmine.createSpyObj("editorCapability", ["isEditContextRoot", "inEditContext"]);
|
||||||
|
|
||||||
|
navigatedObject = jasmine.createSpyObj("navigatedObject", ["hasCapability", "getCapability"]);
|
||||||
|
navigatedObject.getCapability.andReturn(mockEditorCapability);
|
||||||
navigatedObject.hasCapability.andReturn(false);
|
navigatedObject.hasCapability.andReturn(false);
|
||||||
|
|
||||||
|
|
||||||
mockDomainObject = jasmine.createSpyObj("domainObject", ["hasCapability", "getCapability"]);
|
mockDomainObject = jasmine.createSpyObj("domainObject", ["hasCapability", "getCapability"]);
|
||||||
mockDomainObject.hasCapability.andReturn(false);
|
mockDomainObject.hasCapability.andReturn(false);
|
||||||
|
mockDomainObject.getCapability.andReturn(mockEditorCapability);
|
||||||
|
|
||||||
navigationService = jasmine.createSpyObj("navigationService", ["getNavigation"]);
|
navigationService = jasmine.createSpyObj("navigationService", ["getNavigation"]);
|
||||||
navigationService.getNavigation.andReturn(navigatedObject);
|
navigationService.getNavigation.andReturn(navigatedObject);
|
||||||
@ -62,6 +68,7 @@ define(
|
|||||||
it('Allows "window" action when navigated object in edit mode,' +
|
it('Allows "window" action when navigated object in edit mode,' +
|
||||||
' but selected object not in edit mode ', function() {
|
' but selected object not in edit mode ', function() {
|
||||||
navigatedObject.hasCapability.andReturn(true);
|
navigatedObject.hasCapability.andReturn(true);
|
||||||
|
mockEditorCapability.isEditContextRoot.andReturn(true);
|
||||||
metadata.key = "window";
|
metadata.key = "window";
|
||||||
expect(policy.allow(mockAction, context)).toBe(true);
|
expect(policy.allow(mockAction, context)).toBe(true);
|
||||||
});
|
});
|
||||||
@ -91,6 +98,8 @@ define(
|
|||||||
it('Disallows "move" action when navigated object in edit mode,' +
|
it('Disallows "move" action when navigated object in edit mode,' +
|
||||||
' but selected object not in edit mode ', function() {
|
' but selected object not in edit mode ', function() {
|
||||||
navigatedObject.hasCapability.andReturn(true);
|
navigatedObject.hasCapability.andReturn(true);
|
||||||
|
mockEditorCapability.isEditContextRoot.andReturn(true);
|
||||||
|
mockEditorCapability.inEditContext.andReturn(false);
|
||||||
metadata.key = "move";
|
metadata.key = "move";
|
||||||
expect(policy.allow(mockAction, context)).toBe(false);
|
expect(policy.allow(mockAction, context)).toBe(false);
|
||||||
});
|
});
|
||||||
@ -99,6 +108,9 @@ define(
|
|||||||
' selected object in edit mode', function() {
|
' selected object in edit mode', function() {
|
||||||
navigatedObject.hasCapability.andReturn(true);
|
navigatedObject.hasCapability.andReturn(true);
|
||||||
mockDomainObject.hasCapability.andReturn(true);
|
mockDomainObject.hasCapability.andReturn(true);
|
||||||
|
mockEditorCapability.isEditContextRoot.andReturn(true);
|
||||||
|
mockEditorCapability.inEditContext.andReturn(true);
|
||||||
|
|
||||||
metadata.key = "copy";
|
metadata.key = "copy";
|
||||||
expect(policy.allow(mockAction, context)).toBe(false);
|
expect(policy.allow(mockAction, context)).toBe(false);
|
||||||
});
|
});
|
||||||
|
@ -33,8 +33,13 @@ define(
|
|||||||
testMode = true; // Act as if we're in Edit mode by default
|
testMode = true; // Act as if we're in Edit mode by default
|
||||||
mockDomainObject = jasmine.createSpyObj(
|
mockDomainObject = jasmine.createSpyObj(
|
||||||
'domainObject',
|
'domainObject',
|
||||||
['hasCapability']
|
['hasCapability', 'getCapability']
|
||||||
);
|
);
|
||||||
|
mockDomainObject.getCapability.andReturn({
|
||||||
|
inEditContext: function () {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
mockDomainObject.hasCapability.andCallFake(function (c) {
|
mockDomainObject.hasCapability.andCallFake(function (c) {
|
||||||
return (c === 'editor') && testMode;
|
return (c === 'editor') && testMode;
|
||||||
});
|
});
|
||||||
|
@ -32,6 +32,7 @@ define(
|
|||||||
mockDomainObject,
|
mockDomainObject,
|
||||||
mockPersistence,
|
mockPersistence,
|
||||||
mockStatusCapability,
|
mockStatusCapability,
|
||||||
|
mockEditorCapability,
|
||||||
mockCapabilities,
|
mockCapabilities,
|
||||||
representer;
|
representer;
|
||||||
|
|
||||||
@ -58,11 +59,14 @@ define(
|
|||||||
mockPersistence =
|
mockPersistence =
|
||||||
jasmine.createSpyObj("persistence", ["persist"]);
|
jasmine.createSpyObj("persistence", ["persist"]);
|
||||||
mockStatusCapability =
|
mockStatusCapability =
|
||||||
jasmine.createSpyObj("statusCapability", ["get", "listen"]);
|
jasmine.createSpyObj("statusCapability", ["listen"]);
|
||||||
mockStatusCapability.get.andReturn(false);
|
mockEditorCapability =
|
||||||
|
jasmine.createSpyObj("editorCapability", ["isEditContextRoot"]);
|
||||||
|
|
||||||
mockCapabilities = {
|
mockCapabilities = {
|
||||||
'persistence': mockPersistence,
|
'persistence': mockPersistence,
|
||||||
'status': mockStatusCapability
|
'status': mockStatusCapability,
|
||||||
|
'editor': mockEditorCapability
|
||||||
};
|
};
|
||||||
|
|
||||||
mockDomainObject.getModel.andReturn({});
|
mockDomainObject.getModel.andReturn({});
|
||||||
@ -82,6 +86,7 @@ define(
|
|||||||
|
|
||||||
it("Sets edit view template on edit mode", function () {
|
it("Sets edit view template on edit mode", function () {
|
||||||
mockStatusCapability.listen.mostRecentCall.args[0](['editing']);
|
mockStatusCapability.listen.mostRecentCall.args[0](['editing']);
|
||||||
|
mockEditorCapability.isEditContextRoot.andReturn(true);
|
||||||
expect(mockScope.viewObjectTemplate).toEqual('edit-object');
|
expect(mockScope.viewObjectTemplate).toEqual('edit-object');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
127
platform/commonUI/edit/test/services/TransactionServiceSpec.js
Normal file
127
platform/commonUI/edit/test/services/TransactionServiceSpec.js
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web 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 Web 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
/*global define,describe,it,expect,beforeEach,jasmine*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
["../../src/services/TransactionService"],
|
||||||
|
function (TransactionService) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
describe("The Transaction Service", function () {
|
||||||
|
var mockQ,
|
||||||
|
mockLog,
|
||||||
|
transactionService;
|
||||||
|
|
||||||
|
function fastPromise (val) {
|
||||||
|
return {
|
||||||
|
then: function (callback) {
|
||||||
|
return fastPromise(callback(val));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockQ = jasmine.createSpyObj("$q", ["all"]);
|
||||||
|
mockQ.all.andReturn(fastPromise());
|
||||||
|
mockLog = jasmine.createSpyObj("$log", ["error"]);
|
||||||
|
transactionService = new TransactionService(mockQ, mockLog);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("isActive returns true if a transaction is in progress", function () {
|
||||||
|
expect(transactionService.isActive()).toBe(false);
|
||||||
|
transactionService.startTransaction();
|
||||||
|
expect(transactionService.isActive()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("addToTransaction queues onCommit and onCancel functions", function () {
|
||||||
|
var onCommit = jasmine.createSpy('onCommit'),
|
||||||
|
onCancel = jasmine.createSpy('onCancel');
|
||||||
|
|
||||||
|
transactionService.startTransaction();
|
||||||
|
transactionService.addToTransaction(onCommit, onCancel);
|
||||||
|
expect(transactionService.onCommits.length).toBe(1);
|
||||||
|
expect(transactionService.onCancels.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("commit", function () {
|
||||||
|
var onCommits;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
onCommits = [0, 1, 2].map(function(val) {
|
||||||
|
return jasmine.createSpy("onCommit" + val);
|
||||||
|
});
|
||||||
|
|
||||||
|
transactionService.startTransaction();
|
||||||
|
onCommits.forEach(transactionService.addToTransaction.bind(transactionService));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("commit calls all queued commit functions", function () {
|
||||||
|
expect(transactionService.onCommits.length).toBe(3);
|
||||||
|
transactionService.commit();
|
||||||
|
onCommits.forEach( function (spy) {
|
||||||
|
expect(spy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("commit resets active state and clears queues", function () {
|
||||||
|
transactionService.commit();
|
||||||
|
expect(transactionService.isActive()).toBe(false);
|
||||||
|
expect(transactionService.onCommits.length).toBe(0);
|
||||||
|
expect(transactionService.onCancels.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("cancel", function () {
|
||||||
|
var onCancels;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
onCancels = [0, 1, 2].map(function(val) {
|
||||||
|
return jasmine.createSpy("onCancel" + val);
|
||||||
|
});
|
||||||
|
|
||||||
|
transactionService.startTransaction();
|
||||||
|
onCancels.forEach(function (onCancel) {
|
||||||
|
transactionService.addToTransaction(undefined, onCancel);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("cancel calls all queued cancel functions", function () {
|
||||||
|
expect(transactionService.onCancels.length).toBe(3);
|
||||||
|
transactionService.cancel();
|
||||||
|
onCancels.forEach( function (spy) {
|
||||||
|
expect(spy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("cancel resets active state and clears queues", function () {
|
||||||
|
transactionService.cancel();
|
||||||
|
expect(transactionService.isActive()).toBe(false);
|
||||||
|
expect(transactionService.onCommits.length).toBe(0);
|
||||||
|
expect(transactionService.onCancels.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
@ -39,7 +39,7 @@ define(
|
|||||||
if (!regionPart.modes){
|
if (!regionPart.modes){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (domainObject.getCapability('status').get('editing')){
|
if (domainObject.hasCapability('editor') && domainObject.getCapability('editor').inEditContext()){
|
||||||
//If the domain object is in edit mode, only include a part
|
//If the domain object is in edit mode, only include a part
|
||||||
// if it is marked editable
|
// if it is marked editable
|
||||||
return regionPart.modes.indexOf('edit') !== -1;
|
return regionPart.modes.indexOf('edit') !== -1;
|
||||||
|
@ -28,7 +28,7 @@ define(
|
|||||||
|
|
||||||
var editableRegionPolicy,
|
var editableRegionPolicy,
|
||||||
mockDomainObject,
|
mockDomainObject,
|
||||||
mockStatusCapability,
|
mockEditorCapability,
|
||||||
mockBrowseRegionPart = {
|
mockBrowseRegionPart = {
|
||||||
modes: 'browse'
|
modes: 'browse'
|
||||||
},
|
},
|
||||||
@ -40,31 +40,32 @@ define(
|
|||||||
beforeEach(function(){
|
beforeEach(function(){
|
||||||
editableRegionPolicy = new EditableRegionPolicy();
|
editableRegionPolicy = new EditableRegionPolicy();
|
||||||
|
|
||||||
mockStatusCapability = jasmine.createSpyObj("statusCapability", [
|
mockEditorCapability = jasmine.createSpyObj("editorCapability", [
|
||||||
"get"
|
"inEditContext"
|
||||||
]);
|
]);
|
||||||
mockDomainObject = jasmine.createSpyObj("domainObject", [
|
mockDomainObject = jasmine.createSpyObj("domainObject", [
|
||||||
"getCapability"
|
"hasCapability", "getCapability"
|
||||||
]);
|
]);
|
||||||
mockDomainObject.getCapability.andReturn(mockStatusCapability);
|
mockDomainObject.hasCapability.andReturn(true);
|
||||||
|
mockDomainObject.getCapability.andReturn(mockEditorCapability);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("includes only browse region parts for object not in edit mode", function() {
|
it("includes only browse region parts for object not in edit mode", function() {
|
||||||
mockStatusCapability.get.andReturn(false);
|
mockEditorCapability.inEditContext.andReturn(false);
|
||||||
expect(editableRegionPolicy.allow(mockBrowseRegionPart, mockDomainObject)).toBe(true);
|
expect(editableRegionPolicy.allow(mockBrowseRegionPart, mockDomainObject)).toBe(true);
|
||||||
expect(editableRegionPolicy.allow(mockEditRegionPart, mockDomainObject)).toBe(false);
|
expect(editableRegionPolicy.allow(mockEditRegionPart, mockDomainObject)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("includes only edit region parts for object in edit mode", function() {
|
it("includes only edit region parts for object in edit mode", function() {
|
||||||
mockStatusCapability.get.andReturn(true);
|
mockEditorCapability.inEditContext.andReturn(true);
|
||||||
expect(editableRegionPolicy.allow(mockBrowseRegionPart, mockDomainObject)).toBe(false);
|
expect(editableRegionPolicy.allow(mockBrowseRegionPart, mockDomainObject)).toBe(false);
|
||||||
expect(editableRegionPolicy.allow(mockEditRegionPart, mockDomainObject)).toBe(true);
|
expect(editableRegionPolicy.allow(mockEditRegionPart, mockDomainObject)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("includes region parts with no mode specification", function() {
|
it("includes region parts with no mode specification", function() {
|
||||||
mockStatusCapability.get.andReturn(false);
|
mockEditorCapability.inEditContext.andReturn(false);
|
||||||
expect(editableRegionPolicy.allow(mockAllModesRegionPart, mockDomainObject)).toBe(true);
|
expect(editableRegionPolicy.allow(mockAllModesRegionPart, mockDomainObject)).toBe(true);
|
||||||
mockStatusCapability.get.andReturn(true);
|
mockEditorCapability.inEditContext.andReturn(true);
|
||||||
expect(editableRegionPolicy.allow(mockAllModesRegionPart, mockDomainObject)).toBe(true);
|
expect(editableRegionPolicy.allow(mockAllModesRegionPart, mockDomainObject)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -155,18 +155,6 @@ define(
|
|||||||
expect(model).toEqual(refreshModel);
|
expect(model).toEqual(refreshModel);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not overwrite unpersisted changes on refresh", function () {
|
|
||||||
var refreshModel = {someOtherKey: "some other value"},
|
|
||||||
mockCallback = jasmine.createSpy();
|
|
||||||
model.modified = 2;
|
|
||||||
model.persisted = 1;
|
|
||||||
mockPersistenceService.readObject.andReturn(asPromise(refreshModel));
|
|
||||||
persistence.refresh().then(mockCallback);
|
|
||||||
expect(model).not.toEqual(refreshModel);
|
|
||||||
// Should have also indicated that no changes were actually made
|
|
||||||
expect(mockCallback).toHaveBeenCalledWith(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not trigger error notification on successful" +
|
it("does not trigger error notification on successful" +
|
||||||
" persistence", function () {
|
" persistence", function () {
|
||||||
persistence.persist();
|
persistence.persist();
|
||||||
|
@ -40,7 +40,7 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if we are in edit mode (also check parents)
|
// Check if we are in edit mode (also check parents)
|
||||||
function inEditMode(swimlane) {
|
function inEditMode() {
|
||||||
return swimlane.domainObject.hasCapability('editor') &&
|
return swimlane.domainObject.hasCapability('editor') &&
|
||||||
swimlane.domainObject.getCapability('editor').inEditContext();
|
swimlane.domainObject.getCapability('editor').inEditContext();
|
||||||
}
|
}
|
||||||
@ -174,7 +174,7 @@ define(
|
|||||||
* @returns {boolean} true if this should be allowed
|
* @returns {boolean} true if this should be allowed
|
||||||
*/
|
*/
|
||||||
allowDropIn: function (id, domainObject) {
|
allowDropIn: function (id, domainObject) {
|
||||||
return inEditMode(swimlane) &&
|
return inEditMode() &&
|
||||||
!pathContains(swimlane, id) &&
|
!pathContains(swimlane, id) &&
|
||||||
!contains(swimlane, id) &&
|
!contains(swimlane, id) &&
|
||||||
canDrop(swimlane.domainObject, domainObject);
|
canDrop(swimlane.domainObject, domainObject);
|
||||||
@ -189,7 +189,7 @@ define(
|
|||||||
allowDropAfter: function (id, domainObject) {
|
allowDropAfter: function (id, domainObject) {
|
||||||
var target = expandedForDropInto() ?
|
var target = expandedForDropInto() ?
|
||||||
swimlane : swimlane.parent;
|
swimlane : swimlane.parent;
|
||||||
return inEditMode(swimlane) &&
|
return inEditMode() &&
|
||||||
target &&
|
target &&
|
||||||
!pathContains(target, id) &&
|
!pathContains(target, id) &&
|
||||||
canDrop(target.domainObject, domainObject);
|
canDrop(target.domainObject, domainObject);
|
||||||
|
@ -82,11 +82,18 @@ define(
|
|||||||
it("persists zoom changes in Edit mode", function () {
|
it("persists zoom changes in Edit mode", function () {
|
||||||
mockScope.domainObject = jasmine.createSpyObj(
|
mockScope.domainObject = jasmine.createSpyObj(
|
||||||
'domainObject',
|
'domainObject',
|
||||||
['hasCapability']
|
['hasCapability', 'getCapability']
|
||||||
);
|
);
|
||||||
mockScope.domainObject.hasCapability.andCallFake(function (c) {
|
mockScope.domainObject.hasCapability.andCallFake(function (c) {
|
||||||
return c === 'editor';
|
return c === 'editor';
|
||||||
});
|
});
|
||||||
|
mockScope.domainObject.getCapability.andCallFake(function (c) {
|
||||||
|
if (c === 'editor') {
|
||||||
|
return {
|
||||||
|
inEditContext: function () {return true;}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
controller.zoom(1);
|
controller.zoom(1);
|
||||||
expect(mockScope.commit).toHaveBeenCalled();
|
expect(mockScope.commit).toHaveBeenCalled();
|
||||||
expect(mockScope.configuration.zoomLevel)
|
expect(mockScope.configuration.zoomLevel)
|
||||||
|
@ -28,6 +28,7 @@ define(
|
|||||||
var mockSwimlane,
|
var mockSwimlane,
|
||||||
mockOtherObject,
|
mockOtherObject,
|
||||||
mockActionCapability,
|
mockActionCapability,
|
||||||
|
mockEditorCapability,
|
||||||
mockPersistence,
|
mockPersistence,
|
||||||
mockContext,
|
mockContext,
|
||||||
mockAction,
|
mockAction,
|
||||||
@ -36,6 +37,8 @@ define(
|
|||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
var mockPromise = jasmine.createSpyObj('promise', ['then']);
|
var mockPromise = jasmine.createSpyObj('promise', ['then']);
|
||||||
|
|
||||||
|
mockEditorCapability = jasmine.createSpyObj('editorCapability', ['inEditContext']);
|
||||||
|
|
||||||
mockSwimlane = jasmine.createSpyObj(
|
mockSwimlane = jasmine.createSpyObj(
|
||||||
"swimlane",
|
"swimlane",
|
||||||
[ "highlight", "highlightBottom" ]
|
[ "highlight", "highlightBottom" ]
|
||||||
@ -86,19 +89,22 @@ define(
|
|||||||
mockSwimlane.domainObject.getCapability.andCallFake(function (c) {
|
mockSwimlane.domainObject.getCapability.andCallFake(function (c) {
|
||||||
return {
|
return {
|
||||||
action: mockActionCapability,
|
action: mockActionCapability,
|
||||||
persistence: mockPersistence
|
persistence: mockPersistence,
|
||||||
|
editor: mockEditorCapability
|
||||||
}[c];
|
}[c];
|
||||||
});
|
});
|
||||||
mockSwimlane.parent.domainObject.getCapability.andCallFake(function (c) {
|
mockSwimlane.parent.domainObject.getCapability.andCallFake(function (c) {
|
||||||
return {
|
return {
|
||||||
action: mockActionCapability,
|
action: mockActionCapability,
|
||||||
persistence: mockPersistence
|
persistence: mockPersistence,
|
||||||
|
editor: mockEditorCapability
|
||||||
}[c];
|
}[c];
|
||||||
});
|
});
|
||||||
mockOtherObject.getCapability.andCallFake(function (c) {
|
mockOtherObject.getCapability.andCallFake(function (c) {
|
||||||
return {
|
return {
|
||||||
action: mockActionCapability,
|
action: mockActionCapability,
|
||||||
context: mockContext
|
context: mockContext,
|
||||||
|
editor: mockEditorCapability
|
||||||
}[c];
|
}[c];
|
||||||
});
|
});
|
||||||
mockContext.getParent.andReturn(mockOtherObject);
|
mockContext.getParent.andReturn(mockOtherObject);
|
||||||
@ -109,13 +115,14 @@ define(
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("disallows drop outside of edit mode", function () {
|
it("disallows drop outside of edit mode", function () {
|
||||||
|
mockEditorCapability.inEditContext.andReturn(true);
|
||||||
// Verify precondition
|
// Verify precondition
|
||||||
expect(handler.allowDropIn('d', mockSwimlane.domainObject))
|
expect(handler.allowDropIn('d', mockSwimlane.domainObject))
|
||||||
.toBeTruthy();
|
.toBeTruthy();
|
||||||
expect(handler.allowDropAfter('d', mockSwimlane.domainObject))
|
expect(handler.allowDropAfter('d', mockSwimlane.domainObject))
|
||||||
.toBeTruthy();
|
.toBeTruthy();
|
||||||
// Act as if we're not in edit mode
|
// Act as if we're not in edit mode
|
||||||
mockSwimlane.domainObject.hasCapability.andReturn(false);
|
mockEditorCapability.inEditContext.andReturn(false);
|
||||||
// Now, they should be disallowed
|
// Now, they should be disallowed
|
||||||
expect(handler.allowDropIn('d', mockSwimlane.domainObject))
|
expect(handler.allowDropIn('d', mockSwimlane.domainObject))
|
||||||
.toBeFalsy();
|
.toBeFalsy();
|
||||||
|
@ -53,9 +53,7 @@ define(
|
|||||||
* @param {ViewDefinition[]} views an array of view extensions
|
* @param {ViewDefinition[]} views an array of view extensions
|
||||||
*/
|
*/
|
||||||
function MCTRepresentation(representations, views, representers, $q, templateLinker, $log) {
|
function MCTRepresentation(representations, views, representers, $q, templateLinker, $log) {
|
||||||
var representationMap = {},
|
var representationMap = {};
|
||||||
listeners = 0;
|
|
||||||
domainObjectListener;
|
|
||||||
|
|
||||||
// Assemble all representations and views
|
// Assemble all representations and views
|
||||||
// The distinction between views and representations is
|
// The distinction between views and representations is
|
||||||
@ -250,10 +248,10 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a listener for status changes to the object itself.
|
* Add a listener to the object for status changes.
|
||||||
*/
|
*/
|
||||||
$scope.$watch("domainObject", function(domainObject, oldDomainObject) {
|
$scope.$watch("domainObject", function (domainObject, oldDomainObject) {
|
||||||
if (domainObject!==oldDomainObject){
|
if (domainObject !== oldDomainObject){
|
||||||
listenForStatusChange(domainObject);
|
listenForStatusChange(domainObject);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -103,13 +103,18 @@ define(
|
|||||||
// the change.
|
// the change.
|
||||||
if (id) {
|
if (id) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (domainObjectType!=='folder') {
|
|
||||||
domainObject.getCapability('action').perform('edit');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
//Use scope.apply, drop event is outside digest cycle
|
||||||
|
// and if not applied here causes visual artifacts.
|
||||||
|
scope.$apply( function() {
|
||||||
|
if (domainObjectType !== 'folder') {
|
||||||
|
domainObject.getCapability('action').perform('edit');
|
||||||
|
}
|
||||||
|
});
|
||||||
$q.when(action && action.perform()).then(function () {
|
$q.when(action && action.perform()).then(function () {
|
||||||
broadcastDrop(id, event);
|
broadcastDrop(id, event);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@ define(
|
|||||||
testViews,
|
testViews,
|
||||||
testUrls,
|
testUrls,
|
||||||
mockRepresenters,
|
mockRepresenters,
|
||||||
|
mockStatusCapability,
|
||||||
mockQ,
|
mockQ,
|
||||||
mockLinker,
|
mockLinker,
|
||||||
mockLog,
|
mockLog,
|
||||||
@ -118,6 +119,8 @@ define(
|
|||||||
mockChangeTemplate = jasmine.createSpy('changeTemplate');
|
mockChangeTemplate = jasmine.createSpy('changeTemplate');
|
||||||
mockLog = jasmine.createSpyObj("$log", LOG_FUNCTIONS);
|
mockLog = jasmine.createSpyObj("$log", LOG_FUNCTIONS);
|
||||||
|
|
||||||
|
mockStatusCapability = jasmine.createSpyObj("statusCapability", ["listen"]);
|
||||||
|
|
||||||
mockScope = jasmine.createSpyObj("scope", [ "$watch", "$on" ]);
|
mockScope = jasmine.createSpyObj("scope", [ "$watch", "$on" ]);
|
||||||
mockElement = jasmine.createSpyObj("element", JQLITE_FUNCTIONS);
|
mockElement = jasmine.createSpyObj("element", JQLITE_FUNCTIONS);
|
||||||
mockDomainObject = jasmine.createSpyObj("domainObject", DOMAIN_OBJECT_METHODS);
|
mockDomainObject = jasmine.createSpyObj("domainObject", DOMAIN_OBJECT_METHODS);
|
||||||
@ -128,6 +131,10 @@ define(
|
|||||||
return testUrls[ext.key];
|
return testUrls[ext.key];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
mockDomainObject.getCapability.andCallFake(function (c) {
|
||||||
|
return c === 'status' && mockStatusCapability;
|
||||||
|
});
|
||||||
|
|
||||||
mctRepresentation = new MCTRepresentation(
|
mctRepresentation = new MCTRepresentation(
|
||||||
testRepresentations,
|
testRepresentations,
|
||||||
testViews,
|
testViews,
|
||||||
|
@ -120,8 +120,8 @@ define([
|
|||||||
provider = this;
|
provider = this;
|
||||||
|
|
||||||
mutationTopic.listen(function (mutatedObject) {
|
mutationTopic.listen(function (mutatedObject) {
|
||||||
var status = mutatedObject.getCapability('status');
|
var editor = mutatedObject.getCapability('editor');
|
||||||
if (!status || !status.get('editing')) {
|
if (!editor || !editor.inEditContext()) {
|
||||||
provider.index(
|
provider.index(
|
||||||
mutatedObject.getId(),
|
mutatedObject.getId(),
|
||||||
mutatedObject.getModel()
|
mutatedObject.getModel()
|
||||||
|
Loading…
Reference in New Issue
Block a user