Merge remote-tracking branch 'github/master' into open245b

...in preparation to complete merge nasa/openmctweb#257

Conflicts:
	platform/entanglement/src/actions/CopyAction.js
	platform/entanglement/src/actions/LinkAction.js
	platform/entanglement/src/actions/MoveAction.js
This commit is contained in:
Victor Woeltjen
2015-11-21 07:02:04 -08:00
67 changed files with 2779 additions and 1718 deletions

View File

@@ -96,7 +96,7 @@
"provides": "actionService",
"type": "provider",
"implementation": "actions/ActionProvider.js",
"depends": [ "actions[]" ]
"depends": [ "actions[]", "$log" ]
},
{
"provides": "actionService",
@@ -222,7 +222,8 @@
},
{
"key": "topic",
"implementation": "services/Topic.js"
"implementation": "services/Topic.js",
"depends": [ "$log" ]
},
{
"key": "contextualize",

View File

@@ -39,9 +39,11 @@ define(
* @imeplements {ActionService}
* @constructor
*/
function ActionProvider(actions) {
function ActionProvider(actions, $log) {
var self = this;
this.$log = $log;
// Build up look-up tables
this.actions = actions;
this.actionsByKey = {};
@@ -74,6 +76,7 @@ define(
var context = (actionContext || {}),
category = context.category,
key = context.key,
$log = this.$log,
candidates;
// Instantiate an action; invokes the constructor and
@@ -103,12 +106,31 @@ define(
// appliesTo method of given actions (if defined), and
// instantiate those applicable actions.
function createIfApplicable(actions, context) {
return (actions || []).filter(function (Action) {
return Action.appliesTo ?
Action.appliesTo(context) : true;
}).map(function (Action) {
return instantiateAction(Action, context);
});
function isApplicable(Action) {
return Action.appliesTo ? Action.appliesTo(context) : true;
}
function instantiate(Action) {
try {
return instantiateAction(Action, context);
} catch (e) {
$log.error([
"Could not instantiate action",
Action.key,
"due to:",
e.message
].join(" "));
return undefined;
}
}
function isDefined(action) {
return action !== undefined;
}
return (actions || []).filter(isApplicable)
.map(instantiate)
.filter(isDefined);
}
// Match actions to the provided context by comparing "key"

View File

@@ -26,6 +26,8 @@ define(
function () {
"use strict";
var ERROR_PREFIX = "Error when notifying listener: ";
/**
* The `topic` service provides a way to create both named,
* shared listeners and anonymous, private listeners.
@@ -46,7 +48,7 @@ define(
* @returns {Function}
* @memberof platform/core
*/
function Topic() {
function Topic($log) {
var topics = {};
function createTopic() {
@@ -63,7 +65,11 @@ define(
},
notify: function (message) {
listeners.forEach(function (listener) {
listener(message);
try {
listener(message);
} catch (e) {
$log.error(ERROR_PREFIX + e.message);
}
});
}
};

View File

@@ -156,8 +156,15 @@ define(
});
};
/**
* Returns the default model for an object of this type. Note that
* this method returns a clone of the original model, so if using this
* method heavily, consider caching the result to optimize performance.
*
* @return {object} The default model for an object of this type.
*/
TypeImpl.prototype.getInitialModel = function () {
return this.typeDef.model || {};
return JSON.parse(JSON.stringify(this.typeDef.model || {}));
};
TypeImpl.prototype.getDefinition = function () {

View File

@@ -30,7 +30,8 @@ define(
"use strict";
describe("The action provider", function () {
var actions,
var mockLog,
actions,
actionProvider;
function SimpleAction() {
@@ -62,6 +63,10 @@ define(
MetadataAction.key = "metadata";
beforeEach(function () {
mockLog = jasmine.createSpyObj(
'$log',
['error', 'warn', 'info', 'debug']
);
actions = [
SimpleAction,
CategorizedAction,
@@ -137,6 +142,42 @@ define(
expect(provided[0].getMetadata()).toEqual("custom metadata");
});
describe("when actions throw errors during instantiation", function () {
var errorText,
provided;
beforeEach(function () {
errorText = "some error text";
function BadAction() {
throw new Error(errorText);
}
provided = new ActionProvider(
[ SimpleAction, BadAction ],
mockLog
).getActions();
});
it("logs an error", function () {
expect(mockLog.error)
.toHaveBeenCalledWith(jasmine.any(String));
});
it("reports the error's message", function () {
expect(
mockLog.error.mostRecentCall.args[0].indexOf(errorText)
).not.toEqual(-1);
});
it("still provides valid actions", function () {
expect(provided.length).toEqual(1);
expect(provided[0].perform()).toEqual("simple");
});
});
});
}
);
);

View File

@@ -28,13 +28,18 @@ define(
describe("The 'topic' service", function () {
var topic,
mockLog,
testMessage,
mockCallback;
beforeEach(function () {
testMessage = { someKey: "some value"};
mockLog = jasmine.createSpyObj(
'$log',
[ 'error', 'warn', 'info', 'debug' ]
);
mockCallback = jasmine.createSpy('callback');
topic = new Topic();
topic = new Topic(mockLog);
});
it("notifies listeners on a topic", function () {
@@ -65,6 +70,21 @@ define(
expect(mockCallback).toHaveBeenCalledWith(testMessage);
});
it("is robust against errors thrown by listeners", function () {
var mockBadCallback = jasmine.createSpy("bad-callback"),
t = topic();
mockBadCallback.andCallFake(function () {
throw new Error("I'm afraid I can't do that.");
});
t.listen(mockBadCallback);
t.listen(mockCallback);
t.notify(testMessage);
expect(mockCallback).toHaveBeenCalledWith(testMessage);
});
});
}
);

View File

@@ -101,6 +101,12 @@ define(
expect(type.getInitialModel().someKey).toEqual("some value");
});
it("provides a fresh initial model each time", function () {
var model = type.getInitialModel();
model.someKey = "some other value";
expect(type.getInitialModel().someKey).toEqual("some value");
});
it("provides type properties", function () {
expect(type.getProperties().length).toEqual(1);
});