diff --git a/platform/commonUI/edit/src/capabilities/EditableLookupCapability.js b/platform/commonUI/edit/src/capabilities/EditableLookupCapability.js index b7636aa5dd..632f4cde39 100644 --- a/platform/commonUI/edit/src/capabilities/EditableLookupCapability.js +++ b/platform/commonUI/edit/src/capabilities/EditableLookupCapability.js @@ -89,7 +89,7 @@ define( } // Wrap all methods; return only editable domain objects. - Object.keys(contextCapability).forEach(wrapFunction); + Object.keys(contextCapability).forEach(wrapMethod); return capability; }; diff --git a/platform/commonUI/edit/src/objects/EditableDomainObject.js b/platform/commonUI/edit/src/objects/EditableDomainObject.js index 8ec46fb3ab..a6b3d503d7 100644 --- a/platform/commonUI/edit/src/objects/EditableDomainObject.js +++ b/platform/commonUI/edit/src/objects/EditableDomainObject.js @@ -13,12 +13,14 @@ define( [ '../capabilities/EditablePersistenceCapability', '../capabilities/EditableContextCapability', + '../capabilities/EditableCompositionCapability', '../capabilities/EditorCapability', './EditableDomainObjectCache' ], function ( EditablePersistenceCapability, EditableContextCapability, + EditableCompositionCapability, EditorCapability, EditableDomainObjectCache ) { @@ -27,7 +29,7 @@ define( var capabilityFactories = { persistence: EditablePersistenceCapability, context: EditableContextCapability, - composition: EditableContextCapability, + composition: EditableCompositionCapability, editor: EditorCapability }; diff --git a/platform/commonUI/edit/test/capabilities/EditableCompositionCapabilitySpec.js b/platform/commonUI/edit/test/capabilities/EditableCompositionCapabilitySpec.js new file mode 100644 index 0000000000..ded1a0c30e --- /dev/null +++ b/platform/commonUI/edit/test/capabilities/EditableCompositionCapabilitySpec.js @@ -0,0 +1,54 @@ +/*global define,describe,it,expect,beforeEach,jasmine*/ + +define( + ["../../src/capabilities/EditableCompositionCapability"], + function (EditableCompositionCapability) { + "use strict"; + + describe("An editable composition capability", function () { + var mockContext, + mockEditableObject, + mockDomainObject, + mockTestObject, + someValue, + mockFactory, + capability; + + beforeEach(function () { + // EditableContextCapability should watch ALL + // methods for domain objects, so give it an + // arbitrary interface to wrap. + mockContext = + jasmine.createSpyObj("context", [ "getDomainObject" ]); + mockTestObject = jasmine.createSpyObj( + "domainObject", + [ "getId", "getModel", "getCapability" ] + ); + mockFactory = + jasmine.createSpyObj("factory", ["getEditableObject"]); + + someValue = { x: 42 }; + + mockContext.getDomainObject.andReturn(mockTestObject); + mockFactory.getEditableObject.andReturn(someValue); + + capability = new EditableCompositionCapability( + mockContext, + mockEditableObject, + mockDomainObject, + mockFactory + ); + + }); + + // Most behavior is tested for EditableLookupCapability, + // so just verify that this isse + it("presumes non-idempotence of its wrapped capability", function () { + expect(capability.getDomainObject()) + .toEqual(capability.getDomainObject()); + expect(mockContext.getDomainObject.calls.length).toEqual(2); + }); + + }); + } +); \ No newline at end of file diff --git a/platform/commonUI/edit/test/capabilities/EditableContextCapabilitySpec.js b/platform/commonUI/edit/test/capabilities/EditableContextCapabilitySpec.js index d79e9e5029..7beeb7c548 100644 --- a/platform/commonUI/edit/test/capabilities/EditableContextCapabilitySpec.js +++ b/platform/commonUI/edit/test/capabilities/EditableContextCapabilitySpec.js @@ -11,63 +11,40 @@ define( mockDomainObject, mockTestObject, someValue, - factory, + mockFactory, capability; beforeEach(function () { // EditableContextCapability should watch ALL // methods for domain objects, so give it an // arbitrary interface to wrap. - mockContext = jasmine.createSpyObj( - "context", - [ - "getSomething", - "getDomainObject", - "getDomainObjectArray" - ] - ); + mockContext = + jasmine.createSpyObj("context", [ "getDomainObject" ]); mockTestObject = jasmine.createSpyObj( "domainObject", [ "getId", "getModel", "getCapability" ] ); - factory = { - getEditableObject: function (v) { - return { - isFromTestFactory: true, - calledWith: v - }; - } - }; + mockFactory = + jasmine.createSpyObj("factory", ["getEditableObject"]); someValue = { x: 42 }; - mockContext.getSomething.andReturn(someValue); mockContext.getDomainObject.andReturn(mockTestObject); - mockContext.getDomainObjectArray.andReturn([mockTestObject]); + mockFactory.getEditableObject.andReturn(someValue); capability = new EditableContextCapability( mockContext, mockEditableObject, mockDomainObject, - factory + mockFactory ); }); - it("wraps retrieved domain objects", function () { - var object = capability.getDomainObject(); - expect(object.isFromTestFactory).toBe(true); - expect(object.calledWith).toEqual(mockTestObject); - }); - - it("wraps retrieved domain object arrays", function () { - var object = capability.getDomainObjectArray()[0]; - expect(object.isFromTestFactory).toBe(true); - expect(object.calledWith).toEqual(mockTestObject); - }); - - it("does not wrap non-domain-objects", function () { - expect(capability.getSomething()).toEqual(someValue); + it("presumes idempotence of its wrapped capability", function () { + expect(capability.getDomainObject()) + .toEqual(capability.getDomainObject()); + expect(mockContext.getDomainObject.calls.length).toEqual(1); }); }); diff --git a/platform/commonUI/edit/test/capabilities/EditableLookupCapabilitySpec.js b/platform/commonUI/edit/test/capabilities/EditableLookupCapabilitySpec.js new file mode 100644 index 0000000000..12046dbedd --- /dev/null +++ b/platform/commonUI/edit/test/capabilities/EditableLookupCapabilitySpec.js @@ -0,0 +1,102 @@ +/*global define,describe,it,expect,beforeEach,jasmine*/ + +define( + ["../../src/capabilities/EditableLookupCapability"], + function (EditableLookupCapability) { + "use strict"; + + describe("An editable lookup capability", function () { + var mockContext, + mockEditableObject, + mockDomainObject, + mockTestObject, + someValue, + factory, + capability; + + beforeEach(function () { + // EditableContextCapability should watch ALL + // methods for domain objects, so give it an + // arbitrary interface to wrap. + mockContext = jasmine.createSpyObj( + "context", + [ + "getSomething", + "getDomainObject", + "getDomainObjectArray" + ] + ); + mockTestObject = jasmine.createSpyObj( + "domainObject", + [ "getId", "getModel", "getCapability" ] + ); + factory = { + getEditableObject: function (v) { + return { + isFromTestFactory: true, + calledWith: v + }; + } + }; + + someValue = { x: 42 }; + + mockContext.getSomething.andReturn(someValue); + mockContext.getDomainObject.andReturn(mockTestObject); + mockContext.getDomainObjectArray.andReturn([mockTestObject]); + + capability = new EditableLookupCapability( + mockContext, + mockEditableObject, + mockDomainObject, + factory, + false + ); + + }); + + it("wraps retrieved domain objects", function () { + var object = capability.getDomainObject(); + expect(object.isFromTestFactory).toBe(true); + expect(object.calledWith).toEqual(mockTestObject); + }); + + it("wraps retrieved domain object arrays", function () { + var object = capability.getDomainObjectArray()[0]; + expect(object.isFromTestFactory).toBe(true); + expect(object.calledWith).toEqual(mockTestObject); + }); + + it("does not wrap non-domain-objects", function () { + expect(capability.getSomething()).toEqual(someValue); + }); + + it("caches idempotent lookups", function () { + capability = new EditableLookupCapability( + mockContext, + mockEditableObject, + mockDomainObject, + factory, + true // idempotent + ); + expect(capability.getDomainObject()) + .toEqual(capability.getDomainObject()); + expect(mockContext.getDomainObject.calls.length).toEqual(1); + }); + + it("does not cache non-idempotent lookups", function () { + capability = new EditableLookupCapability( + mockContext, + mockEditableObject, + mockDomainObject, + factory, + false // Not idempotent + ); + expect(capability.getDomainObject()) + .toEqual(capability.getDomainObject()); + expect(mockContext.getDomainObject.calls.length).toEqual(2); + }); + + }); + } +); \ No newline at end of file diff --git a/platform/commonUI/edit/test/objects/EditableDomainObjectCacheSpec.js b/platform/commonUI/edit/test/objects/EditableDomainObjectCacheSpec.js index 4a606606fc..bdc8cd0d3f 100644 --- a/platform/commonUI/edit/test/objects/EditableDomainObjectCacheSpec.js +++ b/platform/commonUI/edit/test/objects/EditableDomainObjectCacheSpec.js @@ -24,9 +24,10 @@ define( }; } - function WrapObject(domainObject) { + function WrapObject(domainObject, model) { var result = Object.create(domainObject); result.wrapped = true; + result.wrappedModel = model; captured.wraps = (captured.wraps || 0) + 1; return result; } @@ -49,24 +50,30 @@ define( expect(wrappedObject.getId()).toEqual(domainObject.getId()); }); - it("only wraps objects once", function () { + it("wraps objects repeatedly, wraps models once", function () { var domainObject = new TestObject('test-id'), - wrappedObject; + wrappedObjects = []; // Verify precondition expect(captured.wraps).toBeUndefined(); // Invoke a few more times; expect count not to increment - wrappedObject = cache.getEditableObject(domainObject); - expect(captured.wraps).toEqual(1); - wrappedObject = cache.getEditableObject(domainObject); - expect(captured.wraps).toEqual(1); - wrappedObject = cache.getEditableObject(domainObject); + wrappedObjects.push(cache.getEditableObject(domainObject)); expect(captured.wraps).toEqual(1); + wrappedObjects.push(cache.getEditableObject(domainObject)); + expect(captured.wraps).toEqual(2); + wrappedObjects.push(cache.getEditableObject(domainObject)); + expect(captured.wraps).toEqual(3); // Verify that the last call still gave us a wrapped object - expect(wrappedObject.wrapped).toBeTruthy(); - expect(wrappedObject.getId()).toEqual(domainObject.getId()); + expect(wrappedObjects[0].wrapped).toBeTruthy(); + expect(wrappedObjects[0].getId()).toEqual(domainObject.getId()); + + // Verify that objects are distinct but models are identical + expect(wrappedObjects[0].wrappedModel) + .toBe(wrappedObjects[1].wrappedModel); + expect(wrappedObjects[0]).not + .toBe(wrappedObjects[1]); }); it("saves objects that have been marked dirty", function () { diff --git a/platform/commonUI/edit/test/suite.json b/platform/commonUI/edit/test/suite.json index 4bf89661d8..e50fb236d4 100644 --- a/platform/commonUI/edit/test/suite.json +++ b/platform/commonUI/edit/test/suite.json @@ -8,7 +8,9 @@ "actions/PropertiesDialog", "actions/RemoveAction", "actions/SaveAction", + "capabilities/EditableCompositionCapability", "capabilities/EditableContextCapability", + "capabilities/EditableLookupCapability", "capabilities/EditablePersistenceCapability", "capabilities/EditorCapability", "objects/EditableDomainObject",