Merge remote-tracking branch 'github-open/open97' into open-master
This commit is contained in:
@@ -32,7 +32,8 @@ define(
|
||||
* @private
|
||||
*/
|
||||
/**
|
||||
* Change the composition of the specified objects.
|
||||
* Change the composition of the specified objects. Note that this
|
||||
* should only be invoked after successfully validating.
|
||||
*
|
||||
* @param {DomainObject} domainObject the domain object to
|
||||
* move, copy, or link.
|
||||
@@ -43,7 +44,8 @@ define(
|
||||
* @method platform/entanglement.AbstractComposeService#perform
|
||||
*/
|
||||
/**
|
||||
* Check if one object can be composed into another.
|
||||
* Check if this composition change is valid for these objects.
|
||||
*
|
||||
* @param {DomainObject} domainObject the domain object to
|
||||
* move, copy, or link.
|
||||
* @param {DomainObject} parent the domain object whose composition
|
||||
|
||||
@@ -64,6 +64,12 @@ define(
|
||||
return self.perform(domainObject, parent);
|
||||
}
|
||||
|
||||
if (!this.validate(domainObject, parent)) {
|
||||
throw new Error(
|
||||
"Tried to copy objects without validating first."
|
||||
);
|
||||
}
|
||||
|
||||
if (domainObject.hasCapability('composition')) {
|
||||
model.composition = [];
|
||||
}
|
||||
|
||||
@@ -45,6 +45,9 @@ define(
|
||||
if (parentCandidate.getId() === object.getId()) {
|
||||
return false;
|
||||
}
|
||||
if (!parentCandidate.hasCapability('composition')) {
|
||||
return false;
|
||||
}
|
||||
if (parentCandidate.getModel().composition.indexOf(object.getId()) !== -1) {
|
||||
return false;
|
||||
}
|
||||
@@ -56,26 +59,18 @@ define(
|
||||
};
|
||||
|
||||
LinkService.prototype.perform = function (object, parentObject) {
|
||||
function findChild(children) {
|
||||
var i;
|
||||
for (i = 0; i < children.length; i += 1) {
|
||||
if (children[i].getId() === object.getId()) {
|
||||
return children[i];
|
||||
}
|
||||
}
|
||||
if (!this.validate(object, parentObject)) {
|
||||
throw new Error(
|
||||
"Tried to link objects without validating first."
|
||||
);
|
||||
}
|
||||
|
||||
return parentObject.useCapability('mutation', function (model) {
|
||||
if (model.composition.indexOf(object.getId()) === -1) {
|
||||
model.composition.push(object.getId());
|
||||
}
|
||||
}).then(function () {
|
||||
return parentObject.getCapability('persistence').persist();
|
||||
}).then(function getObjectWithNewContext() {
|
||||
return parentObject
|
||||
.useCapability('composition')
|
||||
.then(findChild);
|
||||
});
|
||||
return parentObject.getCapability('composition').add(object)
|
||||
.then(function (objectInNewContext) {
|
||||
return parentObject.getCapability('persistence')
|
||||
.persist()
|
||||
.then(function () { return objectInNewContext; });
|
||||
});
|
||||
};
|
||||
|
||||
return LinkService;
|
||||
|
||||
@@ -82,6 +82,12 @@ define(
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.validate(object, parentObject)) {
|
||||
throw new Error(
|
||||
"Tried to move objects without validating first."
|
||||
);
|
||||
}
|
||||
|
||||
return this.linkService
|
||||
.perform(object, parentObject)
|
||||
.then(relocate)
|
||||
|
||||
@@ -41,19 +41,23 @@ define(
|
||||
}
|
||||
|
||||
describe("CopyService", function () {
|
||||
var policyService;
|
||||
|
||||
beforeEach(function () {
|
||||
policyService = jasmine.createSpyObj(
|
||||
'policyService',
|
||||
['allow']
|
||||
);
|
||||
});
|
||||
|
||||
describe("validate", function () {
|
||||
|
||||
var policyService,
|
||||
copyService,
|
||||
var copyService,
|
||||
object,
|
||||
parentCandidate,
|
||||
validate;
|
||||
|
||||
beforeEach(function () {
|
||||
policyService = jasmine.createSpyObj(
|
||||
'policyService',
|
||||
['allow']
|
||||
);
|
||||
copyService = new CopyService(
|
||||
null,
|
||||
null,
|
||||
@@ -126,6 +130,16 @@ define(
|
||||
copyResult,
|
||||
copyFinished;
|
||||
|
||||
beforeEach(function () {
|
||||
creationService = jasmine.createSpyObj(
|
||||
'creationService',
|
||||
['createObject']
|
||||
);
|
||||
createObjectPromise = synchronousPromise(undefined);
|
||||
creationService.createObject.andReturn(createObjectPromise);
|
||||
policyService.allow.andReturn(true);
|
||||
});
|
||||
|
||||
describe("on domain object without composition", function () {
|
||||
beforeEach(function () {
|
||||
object = domainObjectFactory({
|
||||
@@ -142,13 +156,7 @@ define(
|
||||
composition: []
|
||||
}
|
||||
});
|
||||
creationService = jasmine.createSpyObj(
|
||||
'creationService',
|
||||
['createObject']
|
||||
);
|
||||
createObjectPromise = synchronousPromise(undefined);
|
||||
creationService.createObject.andReturn(createObjectPromise);
|
||||
copyService = new CopyService(null, creationService);
|
||||
copyService = new CopyService(null, creationService, policyService);
|
||||
copyResult = copyService.perform(object, newParent);
|
||||
copyFinished = jasmine.createSpy('copyFinished');
|
||||
copyResult.then(copyFinished);
|
||||
@@ -180,7 +188,8 @@ define(
|
||||
});
|
||||
|
||||
describe("on domainObject with composition", function () {
|
||||
var childObject,
|
||||
var newObject,
|
||||
childObject,
|
||||
compositionCapability,
|
||||
compositionPromise;
|
||||
|
||||
@@ -216,6 +225,17 @@ define(
|
||||
composition: compositionCapability
|
||||
}
|
||||
});
|
||||
newObject = domainObjectFactory({
|
||||
name: 'object',
|
||||
id: 'abc2',
|
||||
model: {
|
||||
name: 'some object',
|
||||
composition: []
|
||||
},
|
||||
capabilities: {
|
||||
composition: compositionCapability
|
||||
}
|
||||
});
|
||||
newParent = domainObjectFactory({
|
||||
name: 'newParent',
|
||||
id: '456',
|
||||
@@ -223,13 +243,10 @@ define(
|
||||
composition: []
|
||||
}
|
||||
});
|
||||
creationService = jasmine.createSpyObj(
|
||||
'creationService',
|
||||
['createObject']
|
||||
);
|
||||
createObjectPromise = synchronousPromise(undefined);
|
||||
|
||||
createObjectPromise = synchronousPromise(newObject);
|
||||
creationService.createObject.andReturn(createObjectPromise);
|
||||
copyService = new CopyService(mockQ, creationService);
|
||||
copyService = new CopyService(mockQ, creationService, policyService);
|
||||
copyResult = copyService.perform(object, newParent);
|
||||
copyFinished = jasmine.createSpy('copyFinished');
|
||||
copyResult.then(copyFinished);
|
||||
@@ -266,6 +283,38 @@ define(
|
||||
});
|
||||
});
|
||||
|
||||
describe("on invalid inputs", function () {
|
||||
beforeEach(function () {
|
||||
object = domainObjectFactory({
|
||||
name: 'object',
|
||||
capabilities: {
|
||||
type: { type: 'object' }
|
||||
}
|
||||
});
|
||||
newParent = domainObjectFactory({
|
||||
name: 'parentCandidate',
|
||||
capabilities: {
|
||||
type: { type: 'parentCandidate' }
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("throws an error", function () {
|
||||
var copyService =
|
||||
new CopyService(mockQ, creationService, policyService);
|
||||
|
||||
function perform() {
|
||||
copyService.perform(object, newParent);
|
||||
}
|
||||
|
||||
spyOn(copyService, "validate");
|
||||
copyService.validate.andReturn(true);
|
||||
expect(perform).not.toThrow();
|
||||
copyService.validate.andReturn(false);
|
||||
expect(perform).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/*global define,describe,beforeEach,it,jasmine,expect */
|
||||
/*global define,describe,beforeEach,it,jasmine,expect,spyOn */
|
||||
|
||||
define(
|
||||
[
|
||||
@@ -41,6 +41,7 @@ define(
|
||||
'policyService',
|
||||
['allow']
|
||||
);
|
||||
mockPolicyService.allow.andReturn(true);
|
||||
linkService = new LinkService(mockPolicyService);
|
||||
});
|
||||
|
||||
@@ -55,7 +56,13 @@ define(
|
||||
name: 'object'
|
||||
});
|
||||
parentCandidate = domainObjectFactory({
|
||||
name: 'parentCandidate'
|
||||
name: 'parentCandidate',
|
||||
capabilities: {
|
||||
composition: jasmine.createSpyObj(
|
||||
'composition',
|
||||
['invoke', 'add']
|
||||
)
|
||||
}
|
||||
});
|
||||
validate = function () {
|
||||
return linkService.validate(object, parentCandidate);
|
||||
@@ -81,6 +88,18 @@ define(
|
||||
expect(validate()).toBe(false);
|
||||
});
|
||||
|
||||
it("does not allow parents without composition", function () {
|
||||
parentCandidate = domainObjectFactory({
|
||||
name: 'parentCandidate'
|
||||
});
|
||||
object.id = 'abc';
|
||||
parentCandidate.id = 'xyz';
|
||||
parentCandidate.hasCapability.andCallFake(function (c) {
|
||||
return c !== 'composition';
|
||||
});
|
||||
expect(validate()).toBe(false);
|
||||
});
|
||||
|
||||
describe("defers to policyService", function () {
|
||||
beforeEach(function () {
|
||||
object.id = 'abc';
|
||||
@@ -121,16 +140,16 @@ define(
|
||||
linkedObject,
|
||||
parentModel,
|
||||
parentObject,
|
||||
mutationPromise,
|
||||
compositionPromise,
|
||||
persistencePromise,
|
||||
addPromise,
|
||||
compositionCapability,
|
||||
persistenceCapability;
|
||||
|
||||
beforeEach(function () {
|
||||
mutationPromise = new ControlledPromise();
|
||||
compositionPromise = new ControlledPromise();
|
||||
persistencePromise = new ControlledPromise();
|
||||
addPromise = new ControlledPromise();
|
||||
persistenceCapability = jasmine.createSpyObj(
|
||||
'persistenceCapability',
|
||||
['persist']
|
||||
@@ -138,9 +157,10 @@ define(
|
||||
persistenceCapability.persist.andReturn(persistencePromise);
|
||||
compositionCapability = jasmine.createSpyObj(
|
||||
'compositionCapability',
|
||||
['invoke']
|
||||
['invoke', 'add']
|
||||
);
|
||||
compositionCapability.invoke.andReturn(compositionPromise);
|
||||
compositionCapability.add.andReturn(addPromise);
|
||||
parentModel = {
|
||||
composition: []
|
||||
};
|
||||
@@ -151,7 +171,7 @@ define(
|
||||
mutation: {
|
||||
invoke: function (mutator) {
|
||||
mutator(parentModel);
|
||||
return mutationPromise;
|
||||
return new ControlledPromise();
|
||||
}
|
||||
},
|
||||
persistence: persistenceCapability,
|
||||
@@ -172,20 +192,17 @@ define(
|
||||
});
|
||||
|
||||
|
||||
it("modifies parent model composition", function () {
|
||||
expect(parentModel.composition.length).toBe(0);
|
||||
it("adds to the parent's composition", function () {
|
||||
expect(compositionCapability.add).not.toHaveBeenCalled();
|
||||
linkService.perform(object, parentObject);
|
||||
expect(parentObject.useCapability).toHaveBeenCalledWith(
|
||||
'mutation',
|
||||
jasmine.any(Function)
|
||||
);
|
||||
expect(parentModel.composition).toContain('xyz');
|
||||
expect(compositionCapability.add)
|
||||
.toHaveBeenCalledWith(object);
|
||||
});
|
||||
|
||||
it("persists parent", function () {
|
||||
linkService.perform(object, parentObject);
|
||||
expect(mutationPromise.then).toHaveBeenCalled();
|
||||
mutationPromise.resolve();
|
||||
expect(addPromise.then).toHaveBeenCalled();
|
||||
addPromise.resolve(linkedObject);
|
||||
expect(parentObject.getCapability)
|
||||
.toHaveBeenCalledWith('persistence');
|
||||
expect(persistenceCapability.persist).toHaveBeenCalled();
|
||||
@@ -197,11 +214,23 @@ define(
|
||||
whenComplete = jasmine.createSpy('whenComplete');
|
||||
returnPromise.then(whenComplete);
|
||||
|
||||
mutationPromise.resolve();
|
||||
addPromise.resolve(linkedObject);
|
||||
persistencePromise.resolve();
|
||||
compositionPromise.resolve([linkedObject]);
|
||||
expect(whenComplete).toHaveBeenCalledWith(linkedObject);
|
||||
});
|
||||
|
||||
it("throws an error when performed on invalid inputs", function () {
|
||||
function perform() {
|
||||
linkService.perform(object, parentObject);
|
||||
}
|
||||
|
||||
spyOn(linkService, 'validate');
|
||||
linkService.validate.andReturn(true);
|
||||
expect(perform).not.toThrow();
|
||||
linkService.validate.andReturn(false);
|
||||
expect(perform).toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/*global define,describe,beforeEach,it,jasmine,expect */
|
||||
/*global define,describe,beforeEach,it,jasmine,expect,spyOn */
|
||||
define(
|
||||
[
|
||||
'../../src/services/MoveService',
|
||||
@@ -40,58 +40,57 @@ define(
|
||||
|
||||
var moveService,
|
||||
policyService,
|
||||
object,
|
||||
objectContextCapability,
|
||||
currentParent,
|
||||
parentCandidate,
|
||||
linkService;
|
||||
|
||||
beforeEach(function () {
|
||||
objectContextCapability = jasmine.createSpyObj(
|
||||
'objectContextCapability',
|
||||
[
|
||||
'getParent'
|
||||
]
|
||||
);
|
||||
|
||||
object = domainObjectFactory({
|
||||
name: 'object',
|
||||
id: 'a',
|
||||
capabilities: {
|
||||
context: objectContextCapability,
|
||||
type: { type: 'object' }
|
||||
}
|
||||
});
|
||||
|
||||
currentParent = domainObjectFactory({
|
||||
name: 'currentParent',
|
||||
id: 'b'
|
||||
});
|
||||
|
||||
objectContextCapability.getParent.andReturn(currentParent);
|
||||
|
||||
parentCandidate = domainObjectFactory({
|
||||
name: 'parentCandidate',
|
||||
model: { composition: [] },
|
||||
id: 'c',
|
||||
capabilities: {
|
||||
type: { type: 'parentCandidate' }
|
||||
}
|
||||
});
|
||||
policyService = jasmine.createSpyObj(
|
||||
'policyService',
|
||||
['allow']
|
||||
);
|
||||
linkService = new MockLinkService();
|
||||
policyService.allow.andReturn(true);
|
||||
moveService = new MoveService(policyService, linkService);
|
||||
});
|
||||
|
||||
describe("validate", function () {
|
||||
var object,
|
||||
objectContextCapability,
|
||||
currentParent,
|
||||
parentCandidate,
|
||||
validate;
|
||||
var validate;
|
||||
|
||||
beforeEach(function () {
|
||||
|
||||
objectContextCapability = jasmine.createSpyObj(
|
||||
'objectContextCapability',
|
||||
[
|
||||
'getParent'
|
||||
]
|
||||
);
|
||||
|
||||
object = domainObjectFactory({
|
||||
name: 'object',
|
||||
id: 'a',
|
||||
capabilities: {
|
||||
context: objectContextCapability,
|
||||
type: { type: 'object' }
|
||||
}
|
||||
});
|
||||
|
||||
currentParent = domainObjectFactory({
|
||||
name: 'currentParent',
|
||||
id: 'b'
|
||||
});
|
||||
|
||||
objectContextCapability.getParent.andReturn(currentParent);
|
||||
|
||||
parentCandidate = domainObjectFactory({
|
||||
name: 'parentCandidate',
|
||||
model: { composition: [] },
|
||||
id: 'c',
|
||||
capabilities: {
|
||||
type: { type: 'parentCandidate' }
|
||||
}
|
||||
});
|
||||
|
||||
validate = function () {
|
||||
return moveService.validate(object, parentCandidate);
|
||||
};
|
||||
@@ -145,14 +144,15 @@ define(
|
||||
|
||||
describe("perform", function () {
|
||||
|
||||
var object,
|
||||
newParent,
|
||||
actionCapability,
|
||||
var actionCapability,
|
||||
locationCapability,
|
||||
locationPromise,
|
||||
newParent,
|
||||
moveResult;
|
||||
|
||||
beforeEach(function () {
|
||||
newParent = parentCandidate;
|
||||
|
||||
actionCapability = jasmine.createSpyObj(
|
||||
'actionCapability',
|
||||
['perform']
|
||||
@@ -175,7 +175,9 @@ define(
|
||||
name: 'object',
|
||||
capabilities: {
|
||||
action: actionCapability,
|
||||
location: locationCapability
|
||||
location: locationCapability,
|
||||
context: objectContextCapability,
|
||||
type: { type: 'object' }
|
||||
}
|
||||
});
|
||||
|
||||
@@ -194,6 +196,18 @@ define(
|
||||
.toHaveBeenCalledWith(jasmine.any(Function));
|
||||
});
|
||||
|
||||
it("throws an error when performed on invalid inputs", function () {
|
||||
function perform() {
|
||||
moveService.perform(object, newParent);
|
||||
}
|
||||
|
||||
spyOn(moveService, "validate");
|
||||
moveService.validate.andReturn(true);
|
||||
expect(perform).not.toThrow();
|
||||
moveService.validate.andReturn(false);
|
||||
expect(perform).toThrow();
|
||||
});
|
||||
|
||||
describe("when moving an original", function () {
|
||||
beforeEach(function () {
|
||||
locationCapability.getContextualLocation
|
||||
|
||||
Reference in New Issue
Block a user