mirror of
https://github.com/nasa/openmct.git
synced 2024-12-23 15:02:23 +00:00
Resolved merge conflicts
This commit is contained in:
parent
5bf750c90c
commit
44f4a82fa1
@ -87,10 +87,9 @@ define(
|
||||
newObject;
|
||||
|
||||
newModel.type = this.type.getKey();
|
||||
newModel.location = parentObject.getId();
|
||||
newObject = parentObject.useCapability('instantiation', newModel);
|
||||
newObject.useCapability('mutation', function(model){
|
||||
model.location = parentObject.getId();
|
||||
});
|
||||
|
||||
editorCapability = newObject.getCapability("editor");
|
||||
|
||||
if (countEditableViews(newObject) > 0 && newObject.hasCapability('composition')) {
|
||||
@ -101,7 +100,7 @@ define(
|
||||
return newObject.useCapability("action").perform("save").then(function () {
|
||||
return editorCapability.save();
|
||||
}, function () {
|
||||
return editorCapability.cancel()
|
||||
return editorCapability.cancel();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -41,9 +41,8 @@ define([
|
||||
"./src/representers/EditRepresenter",
|
||||
"./src/representers/EditToolbarRepresenter",
|
||||
"./src/capabilities/EditorCapability",
|
||||
"./src/capabilities/TransactionDecorator",
|
||||
"./src/capabilities/TransactionCapabilityDecorator",
|
||||
"./src/services/TransactionService",
|
||||
"./src/services/DirtyModelCache",
|
||||
"text!./res/templates/library.html",
|
||||
"text!./res/templates/edit-object.html",
|
||||
"text!./res/templates/edit-action-buttons.html",
|
||||
@ -71,9 +70,8 @@ define([
|
||||
EditRepresenter,
|
||||
EditToolbarRepresenter,
|
||||
EditorCapability,
|
||||
TransactionDecorator,
|
||||
TransactionCapabilityDecorator,
|
||||
TransactionService,
|
||||
DirtyModelCache,
|
||||
libraryTemplate,
|
||||
editObjectTemplate,
|
||||
editActionButtonsTemplate,
|
||||
@ -136,8 +134,7 @@ define([
|
||||
"depends": [
|
||||
"$location",
|
||||
"navigationService",
|
||||
"$log",
|
||||
"$q"
|
||||
"$log"
|
||||
],
|
||||
"description": "Edit this object.",
|
||||
"category": "view-control",
|
||||
@ -270,11 +267,10 @@ define([
|
||||
{
|
||||
"type": "decorator",
|
||||
"provides": "capabilityService",
|
||||
"implementation": TransactionDecorator,
|
||||
"implementation": TransactionCapabilityDecorator,
|
||||
"depends": [
|
||||
"$q",
|
||||
"transactionService",
|
||||
"dirtyModelCache"
|
||||
"transactionService"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -283,15 +279,7 @@ define([
|
||||
"implementation": TransactionService,
|
||||
"depends": [
|
||||
"$q",
|
||||
"dirtyModelCache"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "provider",
|
||||
"provides": "dirtyModelCache",
|
||||
"implementation": DirtyModelCache,
|
||||
"depends": [
|
||||
"topic"
|
||||
"$log"
|
||||
]
|
||||
}
|
||||
],
|
||||
@ -324,8 +312,7 @@ define([
|
||||
"description": "Provides transactional editing capabilities",
|
||||
"implementation": EditorCapability,
|
||||
"depends": [
|
||||
"transactionService",
|
||||
"dirtyModelCache"
|
||||
"transactionService"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
@ -46,11 +46,20 @@ define(
|
||||
|
||||
function returnToBrowse () {
|
||||
var parent;
|
||||
|
||||
//If the object existed already, navigate to refresh view
|
||||
// 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()
|
||||
.then(returnToBrowse);
|
||||
};
|
||||
@ -64,7 +73,8 @@ define(
|
||||
CancelAction.appliesTo = function (context) {
|
||||
var domainObject = (context || {}).domainObject;
|
||||
return domainObject !== undefined &&
|
||||
domainObject.getCapability("status").get("editing");
|
||||
domainObject.hasCapability('editor') &&
|
||||
domainObject.getCapability('editor').isEditContextRoot();
|
||||
};
|
||||
|
||||
return CancelAction;
|
||||
|
@ -44,7 +44,7 @@ define(
|
||||
* @constructor
|
||||
* @implements {Action}
|
||||
*/
|
||||
function EditAction($location, navigationService, $log, $q, context) {
|
||||
function EditAction($location, navigationService, $log, context) {
|
||||
var domainObject = (context || {}).domainObject;
|
||||
|
||||
// We cannot enter Edit mode if we have no domain object to
|
||||
@ -63,7 +63,6 @@ define(
|
||||
this.domainObject = domainObject;
|
||||
this.$location = $location;
|
||||
this.navigationService = navigationService;
|
||||
this.$q = $q;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -89,8 +88,11 @@ define(
|
||||
var domainObject = (context || {}).domainObject,
|
||||
type = domainObject && domainObject.getCapability('type');
|
||||
|
||||
// Only allow creatable types to be edited
|
||||
return type && type.hasFeature('creation') && !domainObject.getCapability('status').get('editing');
|
||||
// Only allow editing of types that support it and are not already
|
||||
// being edited
|
||||
return type && type.hasFeature('creation') &&
|
||||
domainObject.hasCapability('editor') &&
|
||||
!domainObject.getCapability('editor').isEditContextRoot();
|
||||
};
|
||||
|
||||
return EditAction;
|
||||
|
@ -85,8 +85,9 @@ define(
|
||||
SaveAction.appliesTo = function (context) {
|
||||
var domainObject = (context || {}).domainObject;
|
||||
return domainObject !== undefined &&
|
||||
domainObject.getModel().persisted !== undefined &&
|
||||
domainObject.getCapability("status").get("editing");
|
||||
domainObject.hasCapability('editor') &&
|
||||
domainObject.getCapability('editor').isEditContextRoot() &&
|
||||
domainObject.getModel().persisted !== undefined;
|
||||
};
|
||||
|
||||
return SaveAction;
|
||||
|
@ -135,8 +135,8 @@ define(
|
||||
return copyService.perform(domainObject, parent, allowClone);
|
||||
}
|
||||
|
||||
function cancelEditingAfterClone(clonedObject) {
|
||||
return domainObject.getCapability("editor").cancel()
|
||||
function commitEditingAfterClone(clonedObject) {
|
||||
return domainObject.getCapability("editor").save()
|
||||
.then(resolveWith(clonedObject));
|
||||
}
|
||||
|
||||
@ -144,7 +144,7 @@ define(
|
||||
.then(doWizardSave)
|
||||
.then(getParent)
|
||||
.then(cloneIntoParent)
|
||||
.then(cancelEditingAfterClone)
|
||||
.then(commitEditingAfterClone)
|
||||
.catch(resolveWith(false));
|
||||
};
|
||||
|
||||
@ -157,7 +157,8 @@ define(
|
||||
SaveAsAction.appliesTo = function (context) {
|
||||
var domainObject = (context || {}).domainObject;
|
||||
return domainObject !== undefined &&
|
||||
domainObject.getCapability("status").get("editing") &&
|
||||
domainObject.hasCapability('editor') &&
|
||||
domainObject.getCapability('editor').isEditContextRoot() &&
|
||||
domainObject.getModel().persisted === undefined;
|
||||
};
|
||||
|
||||
|
@ -24,24 +24,42 @@ define(
|
||||
[],
|
||||
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(
|
||||
transactionService,
|
||||
dirtyModelCache,
|
||||
domainObject
|
||||
) {
|
||||
this.transactionService = transactionService;
|
||||
this.dirtyModelCache = dirtyModelCache;
|
||||
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 () {
|
||||
this.transactionService.startTransaction();
|
||||
this.domainObject.getCapability('status').set('editing', true);
|
||||
};
|
||||
|
||||
function isEditContextRoot (domainObject) {
|
||||
return domainObject.getCapability('status').get('editing');
|
||||
}
|
||||
|
||||
function isEditing (domainObject) {
|
||||
return domainObject.getCapability('status').get('editing') ||
|
||||
domainObject.hasCapability('context') && isEditing(domainObject.getCapability('context').getParent());
|
||||
return isEditContextRoot(domainObject) ||
|
||||
domainObject.hasCapability('context') &&
|
||||
isEditing(domainObject.getCapability('context').getParent());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -53,6 +71,20 @@ define(
|
||||
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 () {
|
||||
var domainObject = this.domainObject;
|
||||
return this.transactionService.commit().then(function() {
|
||||
@ -62,6 +94,11 @@ define(
|
||||
|
||||
EditorCapability.prototype.invoke = EditorCapability.prototype.edit;
|
||||
|
||||
/**
|
||||
* Cancel the current editing session. This will discard any pending
|
||||
* persist operations
|
||||
* @returns {*}
|
||||
*/
|
||||
EditorCapability.prototype.cancel = function () {
|
||||
var domainObject = this.domainObject;
|
||||
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 () {
|
||||
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;
|
||||
}
|
||||
);
|
||||
|
@ -26,23 +26,30 @@ define(
|
||||
function (TransactionalPersistenceCapability) {
|
||||
'use strict';
|
||||
|
||||
function TransactionDecorator(
|
||||
/**
|
||||
* Wraps the [PersistenceCapability]{@link PersistenceCapability} with
|
||||
* transactional capabilities.
|
||||
* @param $q
|
||||
* @param transactionService
|
||||
* @param capabilityService
|
||||
* @see TransactionalPersistenceCapability
|
||||
* @constructor
|
||||
*/
|
||||
function TransactionCapabilityDecorator(
|
||||
$q,
|
||||
transactionService,
|
||||
dirtyModelCache,
|
||||
capabilityService
|
||||
) {
|
||||
this.capabilityService = capabilityService;
|
||||
this.transactionService = transactionService;
|
||||
this.dirtyModelCache = dirtyModelCache;
|
||||
this.$q = $q;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorate PersistenceCapability to ignore persistence calls when a
|
||||
* Decorate PersistenceCapability to queue persistence calls when a
|
||||
* transaction is in progress.
|
||||
*/
|
||||
TransactionDecorator.prototype.getCapabilities = function (model) {
|
||||
TransactionCapabilityDecorator.prototype.getCapabilities = function (model) {
|
||||
var self = this,
|
||||
capabilities = this.capabilityService.getCapabilities(model),
|
||||
persistenceCapability = capabilities.persistence;
|
||||
@ -55,7 +62,6 @@ define(
|
||||
return new TransactionalPersistenceCapability(
|
||||
self.$q,
|
||||
self.transactionService,
|
||||
self.dirtyModelCache,
|
||||
original,
|
||||
domainObject
|
||||
);
|
||||
@ -63,6 +69,6 @@ define(
|
||||
return capabilities;
|
||||
};
|
||||
|
||||
return TransactionDecorator;
|
||||
return TransactionCapabilityDecorator;
|
||||
}
|
||||
);
|
@ -26,44 +26,51 @@ define(
|
||||
function () {
|
||||
'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(
|
||||
$q,
|
||||
transactionService,
|
||||
dirtyModelCache,
|
||||
persistenceCapability,
|
||||
domainObject
|
||||
) {
|
||||
this.transactionService = transactionService;
|
||||
this.dirtyModelCache = dirtyModelCache;
|
||||
this.persistenceCapability = Object.create(persistenceCapability);
|
||||
this.persistenceCapability = persistenceCapability;
|
||||
this.domainObject = domainObject;
|
||||
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 () {
|
||||
var domainObject = this.domainObject,
|
||||
dirtyModelCache = this.dirtyModelCache;
|
||||
if (this.transactionService.isActive() && !this.transactionService.isCommitting()) {
|
||||
dirtyModelCache.markDirty(domainObject);
|
||||
//Using $q here because need to return something
|
||||
// from which 'catch' can be chained
|
||||
if (this.transactionService.isActive()) {
|
||||
this.transactionService.addToTransaction(
|
||||
this.persistenceCapability.persist.bind(this.persistenceCapability),
|
||||
this.persistenceCapability.refresh.bind(this.persistenceCapability)
|
||||
);
|
||||
//Need to return a promise from this function
|
||||
return this.$q.when(true);
|
||||
} else {
|
||||
return this.persistenceCapability.persist().then(function (result) {
|
||||
dirtyModelCache.markClean(domainObject);
|
||||
return result;
|
||||
});
|
||||
return this.persistenceCapability.persist();
|
||||
}
|
||||
};
|
||||
|
||||
TransactionalPersistenceCapability.prototype.refresh = function () {
|
||||
var domainObject = this.domainObject,
|
||||
dirtyModelCache = this.dirtyModelCache;
|
||||
|
||||
return this.persistenceCapability.refresh().then(function (result) {
|
||||
dirtyModelCache.markClean(domainObject);
|
||||
return result;
|
||||
});
|
||||
return this.persistenceCapability.refresh();
|
||||
};
|
||||
|
||||
TransactionalPersistenceCapability.prototype.getSpace = function () {
|
||||
|
@ -73,7 +73,8 @@ define(
|
||||
function isEditing(context) {
|
||||
var domainObject = (context || {}).domainObject;
|
||||
return domainObject
|
||||
&& domainObject.getCapability('status').get('editing');
|
||||
&& domainObject.hasCapability('editor')
|
||||
&& domainObject.getCapability('editor').isEditContextRoot();
|
||||
}
|
||||
|
||||
EditActionPolicy.prototype.allow = function (action, context) {
|
||||
|
@ -34,6 +34,11 @@ define(
|
||||
* from context menu of non-editable objects, when navigated object
|
||||
* is being edited
|
||||
* @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>}
|
||||
*/
|
||||
function EditContextualActionPolicy(navigationService, editModeBlacklist, nonEditContextBlacklist) {
|
||||
@ -51,7 +56,7 @@ define(
|
||||
navigatedObject = this.navigationService.getNavigation(),
|
||||
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()){
|
||||
//Target is within the editing context
|
||||
return this.editBlacklist.indexOf(actionMetadata.key) === -1;
|
||||
|
@ -41,12 +41,11 @@ define(
|
||||
EditNavigationPolicy.prototype.isDirty = function(domainObject) {
|
||||
var navigatedObject = domainObject,
|
||||
editorCapability = navigatedObject &&
|
||||
navigatedObject.getCapability("editor"),
|
||||
statusCapability = navigatedObject &&
|
||||
navigatedObject.getCapability("status");
|
||||
navigatedObject.getCapability("editor");
|
||||
|
||||
return statusCapability && statusCapability.get('editing') &&
|
||||
editorCapability && editorCapability.dirty();
|
||||
return editorCapability &&
|
||||
editorCapability.isEditContextRoot() &&
|
||||
editorCapability.dirty();
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -35,10 +35,13 @@ define([], function () {
|
||||
EditableMovePolicy.prototype.allow = function (action, context) {
|
||||
var domainObject = context.domainObject,
|
||||
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()) {
|
||||
return !!selectedObject && selectedObject.hasCapability('editor') && selectedObject.getCapability('editor').inEditContext();
|
||||
if (key === 'move' && isDomainObjectEditing) {
|
||||
return !!selectedObject && selectedObject.hasCapability('editor') &&
|
||||
selectedObject.getCapability('editor').inEditContext();
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
};
|
||||
|
@ -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,63 +25,89 @@ define(
|
||||
function() {
|
||||
/**
|
||||
* 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
|
||||
* TransactionService.commit() is made.
|
||||
* [TransactionService.commit]{@link TransactionService#commit} is made.
|
||||
*
|
||||
* @memberof platform/commonUI/edit/services
|
||||
* @param $q
|
||||
* @constructor
|
||||
*/
|
||||
function TransactionService($q, dirtyModelCache) {
|
||||
function TransactionService($q, $log) {
|
||||
this.$q = $q;
|
||||
this.$log = $log;
|
||||
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 () {
|
||||
if (this.transaction)
|
||||
console.error("Transaction already in progress")
|
||||
if (this.transaction) {
|
||||
//Log error because this is a programming error if it occurs.
|
||||
this.$log.error("Transaction already in progress");
|
||||
}
|
||||
this.transaction = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {boolean} If true, indicates that a transaction is in progress
|
||||
*/
|
||||
TransactionService.prototype.isActive = function () {
|
||||
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
|
||||
* will be committed. Any failures will be reported via a promise
|
||||
* rejection.
|
||||
* @returns {*}
|
||||
* will be committed.
|
||||
*
|
||||
* @returns {Promise} resolved when all persist operations have
|
||||
* completed. Will reject if any commit operations fail
|
||||
*/
|
||||
TransactionService.prototype.commit = function () {
|
||||
var self = this;
|
||||
cache = this.cache.get();
|
||||
var self = this,
|
||||
promises = [],
|
||||
onCommit;
|
||||
|
||||
this.committing = true;
|
||||
|
||||
function keyToObject(key) {
|
||||
return cache[key];
|
||||
while (this.onCommits.length > 0) { // ...using a while in case some onCommit adds to transaction
|
||||
onCommit = this.onCommits.pop();
|
||||
try { // ...also don't want to fail mid-loop...
|
||||
promises.push(onCommit());
|
||||
} catch (e) {
|
||||
this.$log.error("Error committing transaction.");
|
||||
}
|
||||
|
||||
function objectToPromise(object) {
|
||||
return object.getCapability('persistence').persist();
|
||||
}
|
||||
|
||||
return this.$q.all(
|
||||
Object.keys(cache)
|
||||
.map(keyToObject)
|
||||
.map(objectToPromise))
|
||||
.then(function () {
|
||||
return this.$q.all(promises).then( function () {
|
||||
self.transaction = false;
|
||||
this.committing = false;
|
||||
}).catch(function() {
|
||||
return this.committing = false;
|
||||
|
||||
self.onCommits = [];
|
||||
self.onCancels = [];
|
||||
});
|
||||
};
|
||||
|
||||
@ -95,22 +121,22 @@ define(
|
||||
*/
|
||||
TransactionService.prototype.cancel = function () {
|
||||
var self = this,
|
||||
cache = this.cache.get();
|
||||
results = [],
|
||||
onCancel;
|
||||
|
||||
function keyToObject(key) {
|
||||
return cache[key];
|
||||
while (this.onCancels.length > 0) {
|
||||
onCancel = this.onCancels.pop();
|
||||
try {
|
||||
results.push(onCancel());
|
||||
} catch (error) {
|
||||
this.$log.error("Error committing transaction.");
|
||||
}
|
||||
|
||||
function objectToPromise(object) {
|
||||
return self.$q.when(object.getModel().persisted && object.getCapability('persistence').refresh());
|
||||
}
|
||||
|
||||
return this.$q.all(Object.keys(cache)
|
||||
.map(keyToObject)
|
||||
.map(objectToPromise))
|
||||
.then(function () {
|
||||
return this.$q.all(results).then(function () {
|
||||
self.transaction = false;
|
||||
this.committing = false;
|
||||
|
||||
self.onCommits = [];
|
||||
self.onCancels = [];
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -30,7 +30,9 @@ define(
|
||||
mockLog,
|
||||
mockDomainObject,
|
||||
mockType,
|
||||
mockEditor,
|
||||
actionContext,
|
||||
capabilities,
|
||||
action;
|
||||
|
||||
beforeEach(function () {
|
||||
@ -40,7 +42,7 @@ define(
|
||||
);
|
||||
mockNavigationService = jasmine.createSpyObj(
|
||||
"navigationService",
|
||||
[ "setNavigation", "getNavigation" ]
|
||||
[ "setNavigation", "getNavigation", "addListener", "removeListener" ]
|
||||
);
|
||||
mockLog = jasmine.createSpyObj(
|
||||
"$log",
|
||||
@ -48,14 +50,26 @@ define(
|
||||
);
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
[ "getId", "getModel", "getCapability" ]
|
||||
[ "getId", "getModel", "getCapability", "hasCapability", "useCapability" ]
|
||||
);
|
||||
mockType = jasmine.createSpyObj(
|
||||
"type",
|
||||
[ "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);
|
||||
|
||||
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({})).toBeFalsy();
|
||||
|
||||
expect(mockDomainObject.hasCapability).toHaveBeenCalledWith('editor');
|
||||
// Should have checked for creatability
|
||||
expect(mockType.hasFeature).toHaveBeenCalledWith('creation');
|
||||
});
|
||||
|
||||
//TODO: Disabled for NEM Beta
|
||||
xit("changes URL path to edit mode when performed", function () {
|
||||
it("is only applicable to objects not already in edit mode", 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();
|
||||
expect(mockLocation.path).toHaveBeenCalledWith("/edit");
|
||||
expect(mockNavigationService.addListener).toHaveBeenCalled();
|
||||
mockNavigationService.addListener.mostRecentCall.args[0]();
|
||||
expect(mockEditor.cancel).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
//TODO: Disabled for NEM Beta
|
||||
xit("ensures that the edited object is navigated-to", function () {
|
||||
it ("invokes the Edit capability on the object", function () {
|
||||
action.perform();
|
||||
expect(mockNavigationService.setNavigation)
|
||||
.toHaveBeenCalledWith(mockDomainObject);
|
||||
expect(mockDomainObject.useCapability).toHaveBeenCalledWith("editor");
|
||||
});
|
||||
|
||||
//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(
|
||||
"editor",
|
||||
[ "save", "cancel" ]
|
||||
[ "save", "cancel", "isEditContextRoot" ]
|
||||
);
|
||||
mockActionCapability = jasmine.createSpyObj(
|
||||
"actionCapability",
|
||||
@ -71,7 +71,7 @@ define(
|
||||
});
|
||||
mockDomainObject.getModel.andReturn({persisted: 0});
|
||||
mockEditorCapability.save.andReturn(mockPromise(true));
|
||||
mockDomainObject.getOriginalObject.andReturn(mockDomainObject);
|
||||
mockEditorCapability.isEditContextRoot.andReturn(true);
|
||||
|
||||
action = new SaveAction(actionContext);
|
||||
|
||||
@ -97,6 +97,13 @@ define(
|
||||
action.perform();
|
||||
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(
|
||||
"editor",
|
||||
[ "save", "cancel" ]
|
||||
[ "save", "cancel", "isEditContextRoot" ]
|
||||
);
|
||||
mockEditorCapability.cancel.andReturn(mockPromise(undefined));
|
||||
mockEditorCapability.save.andReturn(mockPromise(true));
|
||||
mockEditorCapability.isEditContextRoot.andReturn(true);
|
||||
capabilities.editor = mockEditorCapability;
|
||||
|
||||
mockActionCapability = jasmine.createSpyObj(
|
||||
|
@ -25,94 +25,150 @@ define(
|
||||
function (EditorCapability) {
|
||||
|
||||
describe("The editor capability", function () {
|
||||
var mockPersistence,
|
||||
mockEditableObject,
|
||||
mockDomainObject,
|
||||
mockCache,
|
||||
mockCallback,
|
||||
model,
|
||||
var mockDomainObject,
|
||||
capabilities,
|
||||
mockParentObject,
|
||||
mockTransactionService,
|
||||
mockStatusCapability,
|
||||
mockParentStatus,
|
||||
mockContextCapability,
|
||||
capability;
|
||||
|
||||
beforeEach(function () {
|
||||
mockPersistence = jasmine.createSpyObj(
|
||||
"persistence",
|
||||
[ "persist" ]
|
||||
);
|
||||
mockEditableObject = {
|
||||
getModel: function () { return model; }
|
||||
function fastPromise(val) {
|
||||
return {
|
||||
then: function (callback) {
|
||||
return callback(val);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
[ "getId", "getModel", "getCapability", "useCapability" ]
|
||||
["getId", "getModel", "hasCapability", "getCapability", "useCapability"]
|
||||
);
|
||||
mockCache = jasmine.createSpyObj(
|
||||
"cache",
|
||||
[ "saveAll", "markClean" ]
|
||||
mockParentObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
["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(
|
||||
mockPersistence,
|
||||
mockEditableObject,
|
||||
mockDomainObject,
|
||||
mockCache
|
||||
mockTransactionService,
|
||||
mockDomainObject
|
||||
);
|
||||
});
|
||||
|
||||
//TODO: Disabled for NEM Beta
|
||||
xit("mutates the real domain object on nonrecursive save", function () {
|
||||
capability.save(true).then(mockCallback);
|
||||
it("starts a transaction when edit is invoked", function () {
|
||||
capability.edit();
|
||||
expect(mockTransactionService.startTransaction).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// Wait for promise to resolve
|
||||
waitsFor(function () {
|
||||
return mockCallback.calls.length > 0;
|
||||
}, 250);
|
||||
it("sets editing status on object", function () {
|
||||
capability.edit();
|
||||
expect(mockStatusCapability.set).toHaveBeenCalledWith("editing", true);
|
||||
});
|
||||
|
||||
runs(function () {
|
||||
expect(mockDomainObject.useCapability)
|
||||
.toHaveBeenCalledWith("mutation", jasmine.any(Function));
|
||||
// We should get the model from the editable object back
|
||||
expect(
|
||||
mockDomainObject.useCapability.mostRecentCall.args[1]()
|
||||
).toEqual(model);
|
||||
it("uses editing status to determine editing context root", function () {
|
||||
capability.edit();
|
||||
mockStatusCapability.get.andReturn(false);
|
||||
expect(capability.isEditContextRoot()).toBe(false);
|
||||
mockStatusCapability.get.andReturn(true);
|
||||
expect(capability.isEditContextRoot()).toBe(true);
|
||||
});
|
||||
|
||||
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
|
||||
xit("tells the cache to save others", function () {
|
||||
capability.save().then(mockCallback);
|
||||
|
||||
// Wait for promise to resolve
|
||||
waitsFor(function () {
|
||||
return mockCallback.calls.length > 0;
|
||||
}, 250);
|
||||
|
||||
runs(function () {
|
||||
expect(mockCache.saveAll).toHaveBeenCalled();
|
||||
describe("cancel", function() {
|
||||
beforeEach(function() {
|
||||
capability.edit();
|
||||
capability.cancel();
|
||||
});
|
||||
it("cancels the transaction", function () {
|
||||
expect(mockTransactionService.cancel).toHaveBeenCalled();
|
||||
});
|
||||
it("resets the edit state", function () {
|
||||
expect(mockStatusCapability.set).toHaveBeenCalledWith('editing', false);
|
||||
});
|
||||
});
|
||||
|
||||
//TODO: Disabled for NEM Beta
|
||||
xit("has no interactions on cancel", function () {
|
||||
capability.cancel().then(mockCallback);
|
||||
describe("dirty", function() {
|
||||
var model = {};
|
||||
|
||||
// Wait for promise to resolve
|
||||
waitsFor(function () {
|
||||
return mockCallback.calls.length > 0;
|
||||
}, 250);
|
||||
beforeEach(function() {
|
||||
mockDomainObject.getModel.andReturn(model);
|
||||
capability.edit();
|
||||
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 () {
|
||||
expect(mockDomainObject.useCapability).not.toHaveBeenCalled();
|
||||
expect(mockCache.markClean).not.toHaveBeenCalled();
|
||||
expect(mockCache.saveAll).not.toHaveBeenCalled();
|
||||
model.modified = 1;
|
||||
expect(capability.dirty()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -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,
|
||||
mockPropertiesAction,
|
||||
mockTypeCapability,
|
||||
mockStatusCapability,
|
||||
mockEditorCapability,
|
||||
capabilities,
|
||||
plotView,
|
||||
policy;
|
||||
@ -48,11 +48,10 @@ define(
|
||||
'getCapability'
|
||||
]
|
||||
);
|
||||
mockStatusCapability = jasmine.createSpyObj('statusCapability', ['get']);
|
||||
mockStatusCapability.get.andReturn(false);
|
||||
mockEditorCapability = jasmine.createSpyObj('editorCapability', ['isEditContextRoot']);
|
||||
mockTypeCapability = jasmine.createSpyObj('type', ['getKey']);
|
||||
capabilities = {
|
||||
'status': mockStatusCapability,
|
||||
'editor': mockEditorCapability,
|
||||
'type': mockTypeCapability
|
||||
};
|
||||
|
||||
@ -112,7 +111,7 @@ define(
|
||||
it("disallows the edit action when object is already being" +
|
||||
" edited", function () {
|
||||
testViews = [ editableView ];
|
||||
mockStatusCapability.get.andReturn(true);
|
||||
mockEditorCapability.isEditContextRoot.andReturn(true);
|
||||
expect(policy.allow(mockEditAction, testContext)).toBe(false);
|
||||
});
|
||||
|
||||
|
@ -32,16 +32,22 @@ define(
|
||||
context,
|
||||
navigatedObject,
|
||||
mockDomainObject,
|
||||
mockEditorCapability,
|
||||
metadata,
|
||||
editModeBlacklist = ["copy", "follow", "window", "link", "locate"],
|
||||
nonEditContextBlacklist = ["copy", "follow", "properties", "move", "link", "remove", "locate"];
|
||||
|
||||
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);
|
||||
|
||||
|
||||
mockDomainObject = jasmine.createSpyObj("domainObject", ["hasCapability", "getCapability"]);
|
||||
mockDomainObject.hasCapability.andReturn(false);
|
||||
mockDomainObject.getCapability.andReturn(mockEditorCapability);
|
||||
|
||||
navigationService = jasmine.createSpyObj("navigationService", ["getNavigation"]);
|
||||
navigationService.getNavigation.andReturn(navigatedObject);
|
||||
@ -62,6 +68,7 @@ define(
|
||||
it('Allows "window" action when navigated object in edit mode,' +
|
||||
' but selected object not in edit mode ', function() {
|
||||
navigatedObject.hasCapability.andReturn(true);
|
||||
mockEditorCapability.isEditContextRoot.andReturn(true);
|
||||
metadata.key = "window";
|
||||
expect(policy.allow(mockAction, context)).toBe(true);
|
||||
});
|
||||
@ -91,6 +98,8 @@ define(
|
||||
it('Disallows "move" action when navigated object in edit mode,' +
|
||||
' but selected object not in edit mode ', function() {
|
||||
navigatedObject.hasCapability.andReturn(true);
|
||||
mockEditorCapability.isEditContextRoot.andReturn(true);
|
||||
mockEditorCapability.inEditContext.andReturn(false);
|
||||
metadata.key = "move";
|
||||
expect(policy.allow(mockAction, context)).toBe(false);
|
||||
});
|
||||
@ -99,6 +108,9 @@ define(
|
||||
' selected object in edit mode', function() {
|
||||
navigatedObject.hasCapability.andReturn(true);
|
||||
mockDomainObject.hasCapability.andReturn(true);
|
||||
mockEditorCapability.isEditContextRoot.andReturn(true);
|
||||
mockEditorCapability.inEditContext.andReturn(true);
|
||||
|
||||
metadata.key = "copy";
|
||||
expect(policy.allow(mockAction, context)).toBe(false);
|
||||
});
|
||||
|
@ -33,8 +33,13 @@ define(
|
||||
testMode = true; // Act as if we're in Edit mode by default
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
'domainObject',
|
||||
['hasCapability']
|
||||
['hasCapability', 'getCapability']
|
||||
);
|
||||
mockDomainObject.getCapability.andReturn({
|
||||
inEditContext: function () {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
mockDomainObject.hasCapability.andCallFake(function (c) {
|
||||
return (c === 'editor') && testMode;
|
||||
});
|
||||
|
@ -32,6 +32,7 @@ define(
|
||||
mockDomainObject,
|
||||
mockPersistence,
|
||||
mockStatusCapability,
|
||||
mockEditorCapability,
|
||||
mockCapabilities,
|
||||
representer;
|
||||
|
||||
@ -58,11 +59,14 @@ define(
|
||||
mockPersistence =
|
||||
jasmine.createSpyObj("persistence", ["persist"]);
|
||||
mockStatusCapability =
|
||||
jasmine.createSpyObj("statusCapability", ["get", "listen"]);
|
||||
mockStatusCapability.get.andReturn(false);
|
||||
jasmine.createSpyObj("statusCapability", ["listen"]);
|
||||
mockEditorCapability =
|
||||
jasmine.createSpyObj("editorCapability", ["isEditContextRoot"]);
|
||||
|
||||
mockCapabilities = {
|
||||
'persistence': mockPersistence,
|
||||
'status': mockStatusCapability
|
||||
'status': mockStatusCapability,
|
||||
'editor': mockEditorCapability
|
||||
};
|
||||
|
||||
mockDomainObject.getModel.andReturn({});
|
||||
@ -82,6 +86,7 @@ define(
|
||||
|
||||
it("Sets edit view template on edit mode", function () {
|
||||
mockStatusCapability.listen.mostRecentCall.args[0](['editing']);
|
||||
mockEditorCapability.isEditContextRoot.andReturn(true);
|
||||
expect(mockScope.viewObjectTemplate).toEqual('edit-object');
|
||||
});
|
||||
|
||||
|
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){
|
||||
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 it is marked editable
|
||||
return regionPart.modes.indexOf('edit') !== -1;
|
||||
|
@ -28,7 +28,7 @@ define(
|
||||
|
||||
var editableRegionPolicy,
|
||||
mockDomainObject,
|
||||
mockStatusCapability,
|
||||
mockEditorCapability,
|
||||
mockBrowseRegionPart = {
|
||||
modes: 'browse'
|
||||
},
|
||||
@ -40,31 +40,32 @@ define(
|
||||
beforeEach(function(){
|
||||
editableRegionPolicy = new EditableRegionPolicy();
|
||||
|
||||
mockStatusCapability = jasmine.createSpyObj("statusCapability", [
|
||||
"get"
|
||||
mockEditorCapability = jasmine.createSpyObj("editorCapability", [
|
||||
"inEditContext"
|
||||
]);
|
||||
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() {
|
||||
mockStatusCapability.get.andReturn(false);
|
||||
mockEditorCapability.inEditContext.andReturn(false);
|
||||
expect(editableRegionPolicy.allow(mockBrowseRegionPart, mockDomainObject)).toBe(true);
|
||||
expect(editableRegionPolicy.allow(mockEditRegionPart, mockDomainObject)).toBe(false);
|
||||
});
|
||||
|
||||
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(mockEditRegionPart, mockDomainObject)).toBe(true);
|
||||
});
|
||||
|
||||
it("includes region parts with no mode specification", function() {
|
||||
mockStatusCapability.get.andReturn(false);
|
||||
mockEditorCapability.inEditContext.andReturn(false);
|
||||
expect(editableRegionPolicy.allow(mockAllModesRegionPart, mockDomainObject)).toBe(true);
|
||||
mockStatusCapability.get.andReturn(true);
|
||||
mockEditorCapability.inEditContext.andReturn(true);
|
||||
expect(editableRegionPolicy.allow(mockAllModesRegionPart, mockDomainObject)).toBe(true);
|
||||
});
|
||||
|
||||
|
@ -155,18 +155,6 @@ define(
|
||||
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" +
|
||||
" persistence", function () {
|
||||
persistence.persist();
|
||||
|
@ -40,7 +40,7 @@ define(
|
||||
}
|
||||
|
||||
// Check if we are in edit mode (also check parents)
|
||||
function inEditMode(swimlane) {
|
||||
function inEditMode() {
|
||||
return swimlane.domainObject.hasCapability('editor') &&
|
||||
swimlane.domainObject.getCapability('editor').inEditContext();
|
||||
}
|
||||
@ -174,7 +174,7 @@ define(
|
||||
* @returns {boolean} true if this should be allowed
|
||||
*/
|
||||
allowDropIn: function (id, domainObject) {
|
||||
return inEditMode(swimlane) &&
|
||||
return inEditMode() &&
|
||||
!pathContains(swimlane, id) &&
|
||||
!contains(swimlane, id) &&
|
||||
canDrop(swimlane.domainObject, domainObject);
|
||||
@ -189,7 +189,7 @@ define(
|
||||
allowDropAfter: function (id, domainObject) {
|
||||
var target = expandedForDropInto() ?
|
||||
swimlane : swimlane.parent;
|
||||
return inEditMode(swimlane) &&
|
||||
return inEditMode() &&
|
||||
target &&
|
||||
!pathContains(target, id) &&
|
||||
canDrop(target.domainObject, domainObject);
|
||||
|
@ -82,11 +82,18 @@ define(
|
||||
it("persists zoom changes in Edit mode", function () {
|
||||
mockScope.domainObject = jasmine.createSpyObj(
|
||||
'domainObject',
|
||||
['hasCapability']
|
||||
['hasCapability', 'getCapability']
|
||||
);
|
||||
mockScope.domainObject.hasCapability.andCallFake(function (c) {
|
||||
return c === 'editor';
|
||||
});
|
||||
mockScope.domainObject.getCapability.andCallFake(function (c) {
|
||||
if (c === 'editor') {
|
||||
return {
|
||||
inEditContext: function () {return true;}
|
||||
};
|
||||
}
|
||||
});
|
||||
controller.zoom(1);
|
||||
expect(mockScope.commit).toHaveBeenCalled();
|
||||
expect(mockScope.configuration.zoomLevel)
|
||||
|
@ -28,6 +28,7 @@ define(
|
||||
var mockSwimlane,
|
||||
mockOtherObject,
|
||||
mockActionCapability,
|
||||
mockEditorCapability,
|
||||
mockPersistence,
|
||||
mockContext,
|
||||
mockAction,
|
||||
@ -36,6 +37,8 @@ define(
|
||||
beforeEach(function () {
|
||||
var mockPromise = jasmine.createSpyObj('promise', ['then']);
|
||||
|
||||
mockEditorCapability = jasmine.createSpyObj('editorCapability', ['inEditContext']);
|
||||
|
||||
mockSwimlane = jasmine.createSpyObj(
|
||||
"swimlane",
|
||||
[ "highlight", "highlightBottom" ]
|
||||
@ -86,19 +89,22 @@ define(
|
||||
mockSwimlane.domainObject.getCapability.andCallFake(function (c) {
|
||||
return {
|
||||
action: mockActionCapability,
|
||||
persistence: mockPersistence
|
||||
persistence: mockPersistence,
|
||||
editor: mockEditorCapability
|
||||
}[c];
|
||||
});
|
||||
mockSwimlane.parent.domainObject.getCapability.andCallFake(function (c) {
|
||||
return {
|
||||
action: mockActionCapability,
|
||||
persistence: mockPersistence
|
||||
persistence: mockPersistence,
|
||||
editor: mockEditorCapability
|
||||
}[c];
|
||||
});
|
||||
mockOtherObject.getCapability.andCallFake(function (c) {
|
||||
return {
|
||||
action: mockActionCapability,
|
||||
context: mockContext
|
||||
context: mockContext,
|
||||
editor: mockEditorCapability
|
||||
}[c];
|
||||
});
|
||||
mockContext.getParent.andReturn(mockOtherObject);
|
||||
@ -109,13 +115,14 @@ define(
|
||||
});
|
||||
|
||||
it("disallows drop outside of edit mode", function () {
|
||||
mockEditorCapability.inEditContext.andReturn(true);
|
||||
// Verify precondition
|
||||
expect(handler.allowDropIn('d', mockSwimlane.domainObject))
|
||||
.toBeTruthy();
|
||||
expect(handler.allowDropAfter('d', mockSwimlane.domainObject))
|
||||
.toBeTruthy();
|
||||
// Act as if we're not in edit mode
|
||||
mockSwimlane.domainObject.hasCapability.andReturn(false);
|
||||
mockEditorCapability.inEditContext.andReturn(false);
|
||||
// Now, they should be disallowed
|
||||
expect(handler.allowDropIn('d', mockSwimlane.domainObject))
|
||||
.toBeFalsy();
|
||||
|
@ -53,9 +53,7 @@ define(
|
||||
* @param {ViewDefinition[]} views an array of view extensions
|
||||
*/
|
||||
function MCTRepresentation(representations, views, representers, $q, templateLinker, $log) {
|
||||
var representationMap = {},
|
||||
listeners = 0;
|
||||
domainObjectListener;
|
||||
var representationMap = {};
|
||||
|
||||
// Assemble all representations and views
|
||||
// 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) {
|
||||
if (domainObject!==oldDomainObject){
|
||||
$scope.$watch("domainObject", function (domainObject, oldDomainObject) {
|
||||
if (domainObject !== oldDomainObject){
|
||||
listenForStatusChange(domainObject);
|
||||
}
|
||||
});
|
||||
|
@ -103,13 +103,18 @@ define(
|
||||
// the change.
|
||||
if (id) {
|
||||
e.preventDefault();
|
||||
if (domainObjectType!=='folder') {
|
||||
|
||||
//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 () {
|
||||
broadcastDrop(id, event);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ define(
|
||||
testViews,
|
||||
testUrls,
|
||||
mockRepresenters,
|
||||
mockStatusCapability,
|
||||
mockQ,
|
||||
mockLinker,
|
||||
mockLog,
|
||||
@ -118,6 +119,8 @@ define(
|
||||
mockChangeTemplate = jasmine.createSpy('changeTemplate');
|
||||
mockLog = jasmine.createSpyObj("$log", LOG_FUNCTIONS);
|
||||
|
||||
mockStatusCapability = jasmine.createSpyObj("statusCapability", ["listen"]);
|
||||
|
||||
mockScope = jasmine.createSpyObj("scope", [ "$watch", "$on" ]);
|
||||
mockElement = jasmine.createSpyObj("element", JQLITE_FUNCTIONS);
|
||||
mockDomainObject = jasmine.createSpyObj("domainObject", DOMAIN_OBJECT_METHODS);
|
||||
@ -128,6 +131,10 @@ define(
|
||||
return testUrls[ext.key];
|
||||
});
|
||||
|
||||
mockDomainObject.getCapability.andCallFake(function (c) {
|
||||
return c === 'status' && mockStatusCapability;
|
||||
});
|
||||
|
||||
mctRepresentation = new MCTRepresentation(
|
||||
testRepresentations,
|
||||
testViews,
|
||||
|
@ -120,8 +120,8 @@ define([
|
||||
provider = this;
|
||||
|
||||
mutationTopic.listen(function (mutatedObject) {
|
||||
var status = mutatedObject.getCapability('status');
|
||||
if (!status || !status.get('editing')) {
|
||||
var editor = mutatedObject.getCapability('editor');
|
||||
if (!editor || !editor.inEditContext()) {
|
||||
provider.index(
|
||||
mutatedObject.getId(),
|
||||
mutatedObject.getModel()
|
||||
|
Loading…
Reference in New Issue
Block a user