From 99f9203e7146fa590c5178de6eddf6f1b3fa4d6b Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 21 Nov 2014 16:59:03 -0800 Subject: [PATCH] [Core] Complete spec for capailities Complete specs for capabilities introduced in platform/core, part of ongoing transition of this bundle. WTD-573. --- platform/core/bundle.json | 5 +- .../capabilities/CoreCapabilityProvider.js | 8 ++- .../src/capabilities/DelegationCapability.js | 6 +- .../CoreCapabilityProviderSpec.js | 56 ++++++++++++++++ .../capabilities/DelegationCapabilitySpec.js | 64 +++++++++++++++++++ .../capabilities/MutationCapabilitySpec.js | 43 +++++++++++++ .../capabilities/PersistenceCapabilitySpec.js | 39 +++++++++++ 7 files changed, 214 insertions(+), 7 deletions(-) diff --git a/platform/core/bundle.json b/platform/core/bundle.json index c4bd842306..4c2b9d7a1c 100644 --- a/platform/core/bundle.json +++ b/platform/core/bundle.json @@ -14,7 +14,7 @@ "provides": "capabilityService", "type": "provider", "implementation": "capabilities/CoreCapabilityProvider.js", - "depends": [ "capabilities[]" ] + "depends": [ "capabilities[]", "$log" ] }, { "provides": "modelService", @@ -131,7 +131,8 @@ }, { "key": "delegation", - "implementation": "capabilities/DelegationCapability.js" + "implementation": "capabilities/DelegationCapability.js", + "depends": [ "$q" ] } ], "roots": [ diff --git a/platform/core/src/capabilities/CoreCapabilityProvider.js b/platform/core/src/capabilities/CoreCapabilityProvider.js index 16f7739613..145f244592 100644 --- a/platform/core/src/capabilities/CoreCapabilityProvider.js +++ b/platform/core/src/capabilities/CoreCapabilityProvider.js @@ -18,7 +18,7 @@ define( * * @constructor */ - function CoreCapabilityProvider(capabilities) { + function CoreCapabilityProvider(capabilities, $log) { // Filter by invoking the capability's appliesTo method function filterCapabilities(model) { return capabilities.filter(function (capability) { @@ -32,7 +32,11 @@ define( function packageCapabilities(capabilities) { var result = {}; capabilities.forEach(function (capability) { - result[capability.key] = capability; + if (capability.key) { + result[capability.key] = capability; + } else { + $log.warn("No key defined for capability; skipping."); + } }); return result; } diff --git a/platform/core/src/capabilities/DelegationCapability.js b/platform/core/src/capabilities/DelegationCapability.js index 1fa1dbf75a..1f7499edb5 100644 --- a/platform/core/src/capabilities/DelegationCapability.js +++ b/platform/core/src/capabilities/DelegationCapability.js @@ -1,4 +1,4 @@ -/*global define,Promise*/ +/*global define*/ /** * Module defining DelegationCapability. Created by vwoeltje on 11/18/14. @@ -27,7 +27,7 @@ define( * @param domainObject * @constructor */ - function DelegationCapability(domainObject) { + function DelegationCapability($q, domainObject) { var delegateCapabilities = {}, type = domainObject.getCapability("type"); @@ -52,7 +52,7 @@ define( promiseChildren().then( filterObjectsWithCapability(capability) ) : - []; + $q.when([]); } // Generate set for easy lookup of capability delegation diff --git a/platform/core/test/capabilities/CoreCapabilityProviderSpec.js b/platform/core/test/capabilities/CoreCapabilityProviderSpec.js index 9cf7916d54..f6fc4c45ed 100644 --- a/platform/core/test/capabilities/CoreCapabilityProviderSpec.js +++ b/platform/core/test/capabilities/CoreCapabilityProviderSpec.js @@ -9,6 +9,62 @@ define( "use strict"; describe("The core capability provider", function () { + var mockLog, + provider; + + function BasicCapability() { return; } + BasicCapability.key = "basic"; + + function ApplicableCapability() { return; } + ApplicableCapability.key = "applicable"; + ApplicableCapability.appliesTo = function (model) { + return !model.isNotApplicable; + }; + + function KeylessCapability() { return; } + + beforeEach(function () { + mockLog = jasmine.createSpyObj( + "$log", + ["error", "warn", "info", "debug"] + ); + + provider = new CoreCapabilityProvider([ + BasicCapability, + ApplicableCapability, + KeylessCapability + ], mockLog); + }); + + it("returns capabilities for models, from extensions", function () { + expect(provider.getCapabilities({})).toEqual({ + basic: BasicCapability, + applicable: ApplicableCapability + }); + }); + + it("filters out capabilities which do not apply to models", function () { + expect(provider.getCapabilities({ isNotApplicable: true })).toEqual({ + basic: BasicCapability + }); + }); + + it("logs a warning when capability extensions have not defined keys", function () { + // Verify precondition + expect(mockLog.warn).not.toHaveBeenCalled(); + + provider.getCapabilities({}); + + expect(mockLog.warn).toHaveBeenCalled(); + + }); + + it("does not log a warning when all capability extensions are valid", function () { + KeylessCapability.key = "someKey"; + provider.getCapabilities({}); + expect(mockLog.warn).not.toHaveBeenCalled(); + }); + }); } diff --git a/platform/core/test/capabilities/DelegationCapabilitySpec.js b/platform/core/test/capabilities/DelegationCapabilitySpec.js index 28033af701..2457bef1e2 100644 --- a/platform/core/test/capabilities/DelegationCapabilitySpec.js +++ b/platform/core/test/capabilities/DelegationCapabilitySpec.js @@ -9,7 +9,71 @@ define( "use strict"; describe("The delegation capability", function () { + var captured, + typeDef = {}, + type, + capabilities, + children = [], + object = {}, + delegation; + function capture(k) { return function (v) { captured[k] = v; }; } + function TestDomainObject(caps, id) { + return { + getId: function () { + return id; + }, + getCapability: function (name) { + return caps[name]; + }, + useCapability: function (name) { + return this.getCapability(name).invoke(); + }, + hasCapability: function (name) { + return this.getCapability(name) !== undefined; + } + }; + } + + function mockPromise(value) { + return { + then: function (callback) { + return value.then ? + value : mockPromise(callback(value)); + } + }; + } + + + beforeEach(function () { + captured = {}; + typeDef = {}; + typeDef.delegates = [ "foo" ]; + type = { getDefinition: function () { return typeDef; } }; + children = []; + capabilities = { + type: type, + composition: { invoke: function () { return mockPromise(children); } } + }; + object = new TestDomainObject(capabilities); + + delegation = new DelegationCapability({ when: mockPromise }, object); + }); + + it("provides a list of children which expose a desired capability", function () { + + children = [ + new TestDomainObject({ foo: true }, 'has-capability'), + new TestDomainObject({ }, 'does-not-have-capability') + ]; + + // Look up delegates + delegation.getDelegates('foo').then(capture('delegates')); + + // Expect only the first child to be a delegate + expect(captured.delegates.length).toEqual(1); + expect(captured.delegates[0].getId()).toEqual('has-capability'); + }); }); } ); \ No newline at end of file diff --git a/platform/core/test/capabilities/MutationCapabilitySpec.js b/platform/core/test/capabilities/MutationCapabilitySpec.js index ce9db02d76..3c03ba5dce 100644 --- a/platform/core/test/capabilities/MutationCapabilitySpec.js +++ b/platform/core/test/capabilities/MutationCapabilitySpec.js @@ -9,7 +9,50 @@ define( "use strict"; describe("The mutation capability", function () { + var testModel, + domainObject = { getModel: function () { return testModel; } }, + mutation; + function mockPromise(value) { + return { + then: function (callback) { + return (value && value.then) ? + value : mockPromise(callback(value)); + } + }; + } + + beforeEach(function () { + testModel = { number: 6 }; + mutation = new MutationCapability( + { when: mockPromise }, // $q + domainObject + ); + }); + + it("allows mutation of a model", function () { + mutation.invoke(function (m) { + m.number = m.number * 7; + }); + expect(testModel.number).toEqual(42); + }); + + it("allows setting a model", function () { + mutation.invoke(function (m) { + return { someKey: "some value" }; + }); + expect(testModel.number).toBeUndefined(); + expect(testModel.someKey).toEqual("some value"); + }); + + it("allows model mutation to be aborted", function () { + mutation.invoke(function (m) { + m.number = m.number * 7; + return false; // Should abort change + }); + // Number should not have been changed + expect(testModel.number).toEqual(6); + }); }); } ); \ No newline at end of file diff --git a/platform/core/test/capabilities/PersistenceCapabilitySpec.js b/platform/core/test/capabilities/PersistenceCapabilitySpec.js index 5007c9c373..a758745b9d 100644 --- a/platform/core/test/capabilities/PersistenceCapabilitySpec.js +++ b/platform/core/test/capabilities/PersistenceCapabilitySpec.js @@ -9,6 +9,45 @@ define( "use strict"; describe("The persistence capability", function () { + var mockPersistenceService, + mockDomainObject, + id = "object id", + model = { someKey: "some value"}, + SPACE = "some space", + persistence; + + beforeEach(function () { + mockPersistenceService = jasmine.createSpyObj( + "persistenceService", + [ "updateObject" ] + ); + mockDomainObject = { + getId: function () { return id; }, + getModel: function () { return model; } + }; + persistence = new PersistenceCapability( + mockPersistenceService, + SPACE, + mockDomainObject + ); + }); + + it("makes a call to the persistence service when invoked", function () { + // Verify precondition; no call made during constructor + expect(mockPersistenceService.updateObject).not.toHaveBeenCalled(); + + persistence.persist(); + + expect(mockPersistenceService.updateObject).toHaveBeenCalledWith( + SPACE, + id, + model + ); + }); + + it("reports which persistence space an object belongs to", function () { + expect(persistence.getSpace()).toEqual(SPACE); + }); }); }