mirror of
https://github.com/nasa/openmct.git
synced 2025-06-18 15:18:12 +00:00
[Representation] Spec for mct-representation
Add spec for the mct-representation; separate out gesture attachment to improve testability and increase cohesion. Part of ongoing initial authorship of representation component, WTD-521.
This commit is contained in:
@ -28,6 +28,13 @@
|
|||||||
"implementation": "gestures/ContextMenuGesture.js",
|
"implementation": "gestures/ContextMenuGesture.js",
|
||||||
"depends": [ "$compile", "$document", "$window", "$rootScope" ]
|
"depends": [ "$compile", "$document", "$window", "$rootScope" ]
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"provides": "gestureService",
|
||||||
|
"type": "provider",
|
||||||
|
"implementation": "gestures/GestureProvider.js"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -12,7 +12,7 @@ define(
|
|||||||
*
|
*
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function MCTRepresentation(representations, views, gestures, $q, $log) {
|
function MCTRepresentation(representations, views, gestureService, $q, $log) {
|
||||||
var pathMap = {},
|
var pathMap = {},
|
||||||
representationMap = {},
|
representationMap = {},
|
||||||
gestureMap = {};
|
gestureMap = {};
|
||||||
@ -32,37 +32,9 @@ define(
|
|||||||
representationMap[representation.key] = representation;
|
representationMap[representation.key] = representation;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Assemble all gestures into a map, similarly
|
|
||||||
gestures.forEach(function (gesture) {
|
|
||||||
gestureMap[gesture.key] = gesture;
|
|
||||||
});
|
|
||||||
|
|
||||||
function findRepresentation(key, domainObject) {
|
|
||||||
return representationMap[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
function createGestures(element, domainObject, gestureKeys) {
|
|
||||||
return gestureKeys.map(function (key) {
|
|
||||||
return gestureMap[key];
|
|
||||||
}).filter(function (Gesture) {
|
|
||||||
return Gesture !== undefined && (Gesture.appliesTo ?
|
|
||||||
Gesture.appliesTo(domainObject) :
|
|
||||||
true);
|
|
||||||
}).map(function (Gesture) {
|
|
||||||
return new Gesture(element, domainObject);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function releaseGestures(gestures) {
|
|
||||||
gestures.forEach(function (gesture) {
|
|
||||||
if (gesture && gesture.destroy) {
|
|
||||||
gesture.destroy();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function link($scope, element) {
|
function link($scope, element) {
|
||||||
var linkedGestures = [];
|
var gestureHandle;
|
||||||
|
|
||||||
function refresh() {
|
function refresh() {
|
||||||
var representation = representationMap[$scope.key],
|
var representation = representationMap[$scope.key],
|
||||||
@ -74,7 +46,9 @@ define(
|
|||||||
$scope.inclusion = pathMap[$scope.key];
|
$scope.inclusion = pathMap[$scope.key];
|
||||||
|
|
||||||
// Any existing gestures are no longer valid; release them.
|
// Any existing gestures are no longer valid; release them.
|
||||||
releaseGestures(linkedGestures);
|
if (gestureHandle) {
|
||||||
|
gestureHandle.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
if (!representation && $scope.key) {
|
if (!representation && $scope.key) {
|
||||||
$log.warn("No representation found for " + $scope.key);
|
$log.warn("No representation found for " + $scope.key);
|
||||||
@ -96,7 +70,7 @@ define(
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
linkedGestures = createGestures(
|
gestureHandle = gestureService.attachGestures(
|
||||||
element,
|
element,
|
||||||
domainObject,
|
domainObject,
|
||||||
gestureKeys
|
gestureKeys
|
||||||
|
57
platform/representation/src/gestures/GestureProvider.js
Normal file
57
platform/representation/src/gestures/GestureProvider.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*global define,Promise*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module defining GestureProvider. Created by vwoeltje on 11/22/14.
|
||||||
|
*/
|
||||||
|
define(
|
||||||
|
[],
|
||||||
|
function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function GestureProvider(gestures) {
|
||||||
|
var gestureMap = {};
|
||||||
|
|
||||||
|
function releaseGestures(gestures) {
|
||||||
|
gestures.forEach(function (gesture) {
|
||||||
|
if (gesture && gesture.destroy) {
|
||||||
|
gesture.destroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function attachGestures(element, domainObject, gestureKeys) {
|
||||||
|
var attachedGestures = gestureKeys.map(function (key) {
|
||||||
|
return gestureMap[key];
|
||||||
|
}).filter(function (Gesture) {
|
||||||
|
return Gesture !== undefined && (Gesture.appliesTo ?
|
||||||
|
Gesture.appliesTo(domainObject) :
|
||||||
|
true);
|
||||||
|
}).map(function (Gesture) {
|
||||||
|
return new Gesture(element, domainObject);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
destroy: function () {
|
||||||
|
releaseGestures(attachedGestures);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assemble all gestures into a map, for easy look up
|
||||||
|
gestures.forEach(function (gesture) {
|
||||||
|
gestureMap[gesture.key] = gesture;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
attachGestures: attachGestures
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return GestureProvider;
|
||||||
|
}
|
||||||
|
);
|
@ -8,8 +8,175 @@ define(
|
|||||||
function (MCTRepresentation) {
|
function (MCTRepresentation) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
describe("", function () {
|
var JQLITE_FUNCTIONS = [ "on", "off", "attr", "removeAttr" ],
|
||||||
|
LOG_FUNCTIONS = [ "error", "warn", "info", "debug"],
|
||||||
|
DOMAIN_OBJECT_METHODS = [ "getId", "getModel", "getCapability", "hasCapability", "useCapability"];
|
||||||
|
|
||||||
|
describe("The mct-representation directive", function () {
|
||||||
|
var testRepresentations,
|
||||||
|
testViews,
|
||||||
|
mockGestureService,
|
||||||
|
mockGestureHandle,
|
||||||
|
mockQ,
|
||||||
|
mockLog,
|
||||||
|
mockScope,
|
||||||
|
mockElement,
|
||||||
|
mockDomainObject,
|
||||||
|
mctRepresentation;
|
||||||
|
|
||||||
|
function mockPromise(value) {
|
||||||
|
return (value && value.then) ? value : {
|
||||||
|
then: function (callback) {
|
||||||
|
return mockPromise(callback(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
testRepresentations = [
|
||||||
|
{
|
||||||
|
key: "abc",
|
||||||
|
bundle: { path: "a", resources: "b" },
|
||||||
|
templateUrl: "c/template.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "def",
|
||||||
|
bundle: { path: "d", resources: "e" },
|
||||||
|
templateUrl: "f/template.html",
|
||||||
|
uses: [ "testCapability", "otherTestCapability" ]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
testViews = [
|
||||||
|
{
|
||||||
|
key: "uvw",
|
||||||
|
bundle: { path: "u", resources: "v" },
|
||||||
|
templateUrl: "w/template.html",
|
||||||
|
gestures: [ "testGesture", "otherTestGesture" ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "xyz",
|
||||||
|
bundle: { path: "x", resources: "y" },
|
||||||
|
templateUrl: "z/template.html"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
mockGestureService = jasmine.createSpyObj("gestureService", [ "attachGestures" ]);
|
||||||
|
mockGestureHandle = jasmine.createSpyObj("gestureHandle", [ "destroy" ]);
|
||||||
|
|
||||||
|
mockGestureService.attachGestures.andReturn(mockGestureHandle);
|
||||||
|
|
||||||
|
mockQ = { when: mockPromise };
|
||||||
|
mockLog = jasmine.createSpyObj("$log", LOG_FUNCTIONS);
|
||||||
|
|
||||||
|
mockScope = jasmine.createSpyObj("scope", [ "$watch" ]);
|
||||||
|
mockElement = jasmine.createSpyObj("element", JQLITE_FUNCTIONS);
|
||||||
|
mockDomainObject = jasmine.createSpyObj("domainObject", DOMAIN_OBJECT_METHODS);
|
||||||
|
|
||||||
|
mctRepresentation = new MCTRepresentation(
|
||||||
|
testRepresentations,
|
||||||
|
testViews,
|
||||||
|
mockGestureService,
|
||||||
|
mockQ,
|
||||||
|
mockLog
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it("has a built-in template, with ng-include src=inclusion", function () {
|
||||||
|
// Not rigorous, but should detect many cases when template is broken.
|
||||||
|
expect(mctRepresentation.template.indexOf("ng-include")).not.toEqual(-1);
|
||||||
|
expect(mctRepresentation.template.indexOf("inclusion")).not.toEqual(-1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("is restricted to elements", function () {
|
||||||
|
expect(mctRepresentation.restrict).toEqual("E");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("watches scope when linked", function () {
|
||||||
|
mctRepresentation.link(mockScope, mockElement);
|
||||||
|
expect(mockScope.$watch).toHaveBeenCalledWith("key", jasmine.any(Function));
|
||||||
|
expect(mockScope.$watch).toHaveBeenCalledWith("domainObject", jasmine.any(Function));
|
||||||
|
expect(mockScope.$watch).toHaveBeenCalledWith("domainObject.getModel().modified", jasmine.any(Function));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("recognizes keys for representations", function () {
|
||||||
|
mctRepresentation.link(mockScope, mockElement);
|
||||||
|
|
||||||
|
mockScope.key = "abc";
|
||||||
|
|
||||||
|
// Trigger the watch
|
||||||
|
mockScope.$watch.mostRecentCall.args[1]();
|
||||||
|
|
||||||
|
expect(mockScope.inclusion).toEqual("a/b/c/template.html");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("recognizes keys for views", function () {
|
||||||
|
mctRepresentation.link(mockScope, mockElement);
|
||||||
|
|
||||||
|
mockScope.key = "xyz";
|
||||||
|
|
||||||
|
// Trigger the watch
|
||||||
|
mockScope.$watch.mostRecentCall.args[1]();
|
||||||
|
|
||||||
|
expect(mockScope.inclusion).toEqual("x/y/z/template.html");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("loads declared capabilities", function () {
|
||||||
|
mctRepresentation.link(mockScope, mockElement);
|
||||||
|
|
||||||
|
mockScope.key = "def";
|
||||||
|
mockScope.domainObject = mockDomainObject;
|
||||||
|
|
||||||
|
// Trigger the watch
|
||||||
|
mockScope.$watch.mostRecentCall.args[1]();
|
||||||
|
|
||||||
|
expect(mockDomainObject.useCapability)
|
||||||
|
.toHaveBeenCalledWith("testCapability");
|
||||||
|
expect(mockDomainObject.useCapability)
|
||||||
|
.toHaveBeenCalledWith("otherTestCapability");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("attaches declared gestures, and detaches on refresh", function () {
|
||||||
|
mctRepresentation.link(mockScope, mockElement);
|
||||||
|
|
||||||
|
mockScope.key = "uvw";
|
||||||
|
mockScope.domainObject = mockDomainObject;
|
||||||
|
|
||||||
|
// Trigger the watch
|
||||||
|
mockScope.$watch.mostRecentCall.args[1]();
|
||||||
|
|
||||||
|
expect(mockGestureService.attachGestures).toHaveBeenCalledWith(
|
||||||
|
mockElement,
|
||||||
|
mockDomainObject,
|
||||||
|
[ "testGesture", "otherTestGesture" ]
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(mockGestureHandle.destroy).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// Refresh, expect a detach
|
||||||
|
mockScope.key = "abc";
|
||||||
|
mockScope.$watch.mostRecentCall.args[1]();
|
||||||
|
|
||||||
|
// Should have destroyed those old gestures
|
||||||
|
expect(mockGestureHandle.destroy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it("logs when no representation is available for a key", function () {
|
||||||
|
mctRepresentation.link(mockScope, mockElement);
|
||||||
|
|
||||||
|
mockScope.key = "someUnknownThing";
|
||||||
|
|
||||||
|
// Verify precondition
|
||||||
|
expect(mockLog.warn).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// Trigger the watch
|
||||||
|
mockScope.$watch.mostRecentCall.args[1]();
|
||||||
|
|
||||||
|
// Should have gotten a warning - that's an unknown key
|
||||||
|
expect(mockLog.warn).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
Reference in New Issue
Block a user