mirror of
https://github.com/nasa/openmct.git
synced 2025-05-24 19:24:20 +00:00
[Time Conductor] Adding tests and fixing failing ones. #933
This commit is contained in:
parent
4cf6126d35
commit
c6eaa3d528
@ -57,7 +57,7 @@ define([
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function getScaledFormat (d) {
|
function getScaledFormat (d) {
|
||||||
var m = moment.utc(d);
|
var momentified = moment.utc(d);
|
||||||
/**
|
/**
|
||||||
* Uses logic from d3 Time-Scales, v3 of the API. See
|
* Uses logic from d3 Time-Scales, v3 of the API. See
|
||||||
* https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Scales.md
|
* https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Scales.md
|
||||||
@ -71,17 +71,17 @@ define([
|
|||||||
["HH", function(m) { return m.hours(); }],
|
["HH", function(m) { return m.hours(); }],
|
||||||
["ddd DD", function(m) {
|
["ddd DD", function(m) {
|
||||||
return m.days() &&
|
return m.days() &&
|
||||||
m.date() != 1;
|
m.date() !== 1;
|
||||||
}],
|
}],
|
||||||
["MMM DD", function(m) { return m.date() != 1; }],
|
["MMM DD", function(m) { return m.date() !== 1; }],
|
||||||
["MMMM", function(m) {
|
["MMMM", function(m) {
|
||||||
return m.month();
|
return m.month();
|
||||||
}],
|
}],
|
||||||
["YYYY", function() { return true; }]
|
["YYYY", function() { return true; }]
|
||||||
].filter(function (row){
|
].filter(function (row){
|
||||||
return row[1](m);
|
return row[1](momentified);
|
||||||
})[0][0];
|
})[0][0];
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
62
platform/commonUI/formats/src/UTCTimeFormatSpec.js
Normal file
62
platform/commonUI/formats/src/UTCTimeFormatSpec.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define([
|
||||||
|
"./UTCTimeFormat",
|
||||||
|
"moment"
|
||||||
|
], function (
|
||||||
|
UTCTimeFormat,
|
||||||
|
moment
|
||||||
|
) {
|
||||||
|
describe("The UTCTimeFormat class", function () {
|
||||||
|
var format;
|
||||||
|
var scale;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
format = new UTCTimeFormat();
|
||||||
|
scale = {min: 0, max: 0};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Provides an appropriately scaled time format based on the input" +
|
||||||
|
" time", function () {
|
||||||
|
var TWO_HUNDRED_MS = 200;
|
||||||
|
var THREE_SECONDS = 3000;
|
||||||
|
var FIVE_MINUTES = 5 * 60 * 1000;
|
||||||
|
var ONE_HOUR_TWENTY_MINS = (1 * 60 * 60 * 1000) + (20 * 60 * 1000);
|
||||||
|
var TEN_HOURS = (10 * 60 * 60 * 1000);
|
||||||
|
|
||||||
|
var JUNE_THIRD = moment.utc("2016-06-03", "YYYY-MM-DD");
|
||||||
|
var APRIL = moment.utc("2016-04", "YYYY-MM");
|
||||||
|
var TWENTY_SIXTEEN = moment.utc("2016", "YYYY");
|
||||||
|
|
||||||
|
expect(format.format(TWO_HUNDRED_MS, scale)).toBe(".200");
|
||||||
|
expect(format.format(THREE_SECONDS, scale)).toBe(":03");
|
||||||
|
expect(format.format(FIVE_MINUTES, scale)).toBe("00:05");
|
||||||
|
expect(format.format(ONE_HOUR_TWENTY_MINS, scale)).toBe("01:20");
|
||||||
|
expect(format.format(TEN_HOURS, scale)).toBe("10");
|
||||||
|
|
||||||
|
expect(format.format(JUNE_THIRD, scale)).toBe("Fri 03");
|
||||||
|
expect(format.format(APRIL, scale)).toBe("April");
|
||||||
|
expect(format.format(TWENTY_SIXTEEN, scale)).toBe("2016");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -78,7 +78,7 @@ define(
|
|||||||
|
|
||||||
this.conductor.on("bounds", this.boundsListener);
|
this.conductor.on("bounds", this.boundsListener);
|
||||||
this.conductor.on("timeSystem", this.timeSystemListener);
|
this.conductor.on("timeSystem", this.timeSystemListener);
|
||||||
this.conductor.on("follow", this.followListener)
|
this.conductor.on("follow", this.followListener);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ define([
|
|||||||
|
|
||||||
ConductorService.prototype.getConductor = function () {
|
ConductorService.prototype.getConductor = function () {
|
||||||
return this.tc;
|
return this.tc;
|
||||||
}
|
};
|
||||||
|
|
||||||
return ConductorService;
|
return ConductorService;
|
||||||
});
|
});
|
||||||
|
@ -79,7 +79,7 @@ define(
|
|||||||
return function() {
|
return function() {
|
||||||
unsubscribeFunc();
|
unsubscribeFunc();
|
||||||
conductor.off('bounds', amendRequests);
|
conductor.off('bounds', amendRequests);
|
||||||
}
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
return ConductorTelemetryDecorator;
|
return ConductorTelemetryDecorator;
|
||||||
|
@ -64,6 +64,7 @@ define([
|
|||||||
"implementation": TimeConductorController,
|
"implementation": TimeConductorController,
|
||||||
"depends": [
|
"depends": [
|
||||||
"$scope",
|
"$scope",
|
||||||
|
"$window",
|
||||||
"timeConductor",
|
"timeConductor",
|
||||||
"timeConductorViewService",
|
"timeConductorViewService",
|
||||||
"timeSystems[]"
|
"timeSystems[]"
|
||||||
|
@ -50,21 +50,21 @@ define(['./TimeConductor'], function (TimeConductor) {
|
|||||||
|
|
||||||
it("Allows setting of valid bounds", function () {
|
it("Allows setting of valid bounds", function () {
|
||||||
bounds = {start: 0, end: 1};
|
bounds = {start: 0, end: 1};
|
||||||
expect(tc.bounds()).not.toBe(bounds);
|
expect(tc.bounds()).not.toEqual(bounds);
|
||||||
expect(tc.bounds.bind(tc, bounds)).not.toThrow();
|
expect(tc.bounds.bind(tc, bounds)).not.toThrow();
|
||||||
expect(tc.bounds()).toBe(bounds);
|
expect(tc.bounds()).toEqual(bounds);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Disallows setting of invalid bounds", function () {
|
it("Disallows setting of invalid bounds", function () {
|
||||||
bounds = {start: 1, end: 0};
|
bounds = {start: 1, end: 0};
|
||||||
expect(tc.bounds()).not.toBe(bounds);
|
expect(tc.bounds()).not.toEqual(bounds);
|
||||||
expect(tc.bounds.bind(tc, bounds)).toThrow();
|
expect(tc.bounds.bind(tc, bounds)).toThrow();
|
||||||
expect(tc.bounds()).not.toBe(bounds);
|
expect(tc.bounds()).not.toEqual(bounds);
|
||||||
|
|
||||||
bounds = {start: 1};
|
bounds = {start: 1};
|
||||||
expect(tc.bounds()).not.toBe(bounds);
|
expect(tc.bounds()).not.toEqual(bounds);
|
||||||
expect(tc.bounds.bind(tc, bounds)).toThrow();
|
expect(tc.bounds.bind(tc, bounds)).toThrow();
|
||||||
expect(tc.bounds()).not.toBe(bounds);
|
expect(tc.bounds()).not.toEqual(bounds);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Allows setting of time system with bounds", function () {
|
it("Allows setting of time system with bounds", function () {
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(["./LocalClock"], function (LocalClock) {
|
||||||
|
describe("The LocalClock class", function () {
|
||||||
|
var clock,
|
||||||
|
mockTimeout,
|
||||||
|
timeoutHandle = {};
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockTimeout = jasmine.createSpy("timeout");
|
||||||
|
mockTimeout.andReturn(timeoutHandle);
|
||||||
|
mockTimeout.cancel = jasmine.createSpy("cancel");
|
||||||
|
|
||||||
|
clock = new LocalClock(mockTimeout, 0);
|
||||||
|
clock.start();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calls listeners on tick with current time", function () {
|
||||||
|
var mockListener = jasmine.createSpy("listener");
|
||||||
|
clock.listen(mockListener);
|
||||||
|
clock.tick();
|
||||||
|
expect(mockListener).toHaveBeenCalledWith(jasmine.any(Number));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("stops ticking when stop is called", function () {
|
||||||
|
clock.stop();
|
||||||
|
expect(mockTimeout.cancel).toHaveBeenCalledWith(timeoutHandle);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -22,10 +22,10 @@
|
|||||||
|
|
||||||
define(
|
define(
|
||||||
[
|
[
|
||||||
"zepto",
|
|
||||||
"d3"
|
"d3"
|
||||||
],
|
],
|
||||||
function ($, d3) {
|
function (d3) {
|
||||||
|
var PADDING = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The mct-conductor-axis renders a horizontal axis with regular
|
* The mct-conductor-axis renders a horizontal axis with regular
|
||||||
@ -33,88 +33,102 @@ define(
|
|||||||
* be specified as attributes.
|
* be specified as attributes.
|
||||||
*/
|
*/
|
||||||
function MCTConductorAxis(conductor, formatService) {
|
function MCTConductorAxis(conductor, formatService) {
|
||||||
function link(scope, element, attrs, ngModelController) {
|
// Dependencies
|
||||||
var target = element[0].firstChild,
|
this.d3 = d3;
|
||||||
height = target.offsetHeight,
|
this.conductor = conductor;
|
||||||
padding = 1;
|
this.formatService = formatService;
|
||||||
|
|
||||||
var vis = d3.select(target)
|
// Runtime properties (set by 'link' function)
|
||||||
.append('svg:svg')
|
this.target = undefined;
|
||||||
.attr('width', '100%')
|
this.xScale = undefined;
|
||||||
.attr('height', height);
|
this.xAxis = undefined;
|
||||||
var xAxis = d3.axisTop();
|
this.axisElement = undefined;
|
||||||
var xScale = d3.scaleUtc();
|
this.setScale = this.setScale.bind(this);
|
||||||
|
this.changeTimeSystem = this.changeTimeSystem.bind(this);
|
||||||
|
|
||||||
// draw x axis with labels and move to the bottom of the chart area
|
// Angular Directive interface
|
||||||
var axisElement = vis.append("g")
|
this.link = this.link.bind(this);
|
||||||
.attr("transform", "translate(0," + (height - padding) + ")");
|
this.restrict = "E";
|
||||||
|
this.template =
|
||||||
|
"<div class=\"l-axis-holder\" mct-resize=\"resize()\"></div>";
|
||||||
|
this.priority = 1000;
|
||||||
|
}
|
||||||
|
|
||||||
function setScale() {
|
MCTConductorAxis.prototype.setScale = function () {
|
||||||
var width = target.offsetWidth;
|
var width = this.target.offsetWidth;
|
||||||
var timeSystem = conductor.timeSystem();
|
var timeSystem = this.conductor.timeSystem();
|
||||||
var bounds = conductor.bounds();
|
var bounds = this.conductor.bounds();
|
||||||
|
|
||||||
if (timeSystem.isUTCBased()) {
|
if (timeSystem.isUTCBased()) {
|
||||||
xScale.domain([new Date(bounds.start), new Date(bounds.end)]);
|
this.xScale = this.xScale || this.d3.scaleUtc();
|
||||||
|
this.xScale.domain([new Date(bounds.start), new Date(bounds.end)]);
|
||||||
} else {
|
} else {
|
||||||
xScale.domain([bounds.start, bounds.end]);
|
this.xScale = this.xScale || this.d3.scaleLinear();
|
||||||
|
this.xScale.domain([bounds.start, bounds.end]);
|
||||||
}
|
}
|
||||||
|
|
||||||
xScale.range([padding, width - padding * 2]);
|
this.xScale.range([PADDING, width - PADDING * 2]);
|
||||||
axisElement.call(xAxis);
|
this.axisElement.call(this.xAxis);
|
||||||
}
|
};
|
||||||
|
|
||||||
function changeTimeSystem(timeSystem) {
|
MCTConductorAxis.prototype.changeTimeSystem = function (timeSystem) {
|
||||||
var key = timeSystem.formats()[0];
|
var key = timeSystem.formats()[0];
|
||||||
if (key !== undefined) {
|
if (key !== undefined) {
|
||||||
var format = formatService.getFormat(key);
|
var format = this.formatService.getFormat(key);
|
||||||
var b = conductor.bounds();
|
var bounds = this.conductor.bounds();
|
||||||
|
|
||||||
if (timeSystem.isUTCBased()) {
|
if (timeSystem.isUTCBased()) {
|
||||||
xScale = d3.scaleUtc();
|
this.xScale = this.d3.scaleUtc();
|
||||||
} else {
|
} else {
|
||||||
xScale = d3.scaleLinear();
|
this.xScale = this.d3.scaleLinear();
|
||||||
}
|
}
|
||||||
|
|
||||||
xAxis.scale(xScale);
|
this.xAxis.scale(this.xScale);
|
||||||
//Define a custom format function
|
//Define a custom format function
|
||||||
xAxis.tickFormat(function (tickValue) {
|
this.xAxis.tickFormat(function (tickValue) {
|
||||||
// Normalize date representations to numbers
|
// Normalize date representations to numbers
|
||||||
if (tickValue instanceof Date){
|
if (tickValue instanceof Date){
|
||||||
tickValue = tickValue.getTime();
|
tickValue = tickValue.getTime();
|
||||||
}
|
}
|
||||||
return format.format(tickValue, {
|
return format.format(tickValue, {
|
||||||
min: b.start,
|
min: bounds.start,
|
||||||
max: b.end
|
max: bounds.end
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
axisElement.call(xAxis);
|
this.axisElement.call(this.xAxis);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
scope.resize = setScale;
|
MCTConductorAxis.prototype.link = function (scope, element) {
|
||||||
|
var conductor = this.conductor;
|
||||||
|
this.target = element[0].firstChild;
|
||||||
|
var height = this.target.offsetHeight;
|
||||||
|
var vis = this.d3.select(this.target)
|
||||||
|
.append('svg:svg')
|
||||||
|
.attr('width', '100%')
|
||||||
|
.attr('height', height);
|
||||||
|
|
||||||
conductor.on('timeSystem', changeTimeSystem);
|
this.xAxis = this.d3.axisTop();
|
||||||
|
|
||||||
|
// draw x axis with labels and move to the bottom of the chart area
|
||||||
|
this.axisElement = vis.append("g")
|
||||||
|
.attr("transform", "translate(0," + (height - PADDING) + ")");
|
||||||
|
|
||||||
|
scope.resize = this.setScale;
|
||||||
|
|
||||||
|
conductor.on('timeSystem', this.changeTimeSystem);
|
||||||
|
|
||||||
//On conductor bounds changes, redraw ticks
|
//On conductor bounds changes, redraw ticks
|
||||||
conductor.on('bounds', function (bounds) {
|
conductor.on('bounds', this.setScale);
|
||||||
setScale();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (conductor.timeSystem() !== undefined) {
|
if (conductor.timeSystem() !== undefined) {
|
||||||
changeTimeSystem(conductor.timeSystem());
|
this.changeTimeSystem(conductor.timeSystem());
|
||||||
setScale();
|
this.setScale();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return function(conductor, formatService) {
|
||||||
restrict: "E",
|
return new MCTConductorAxis(conductor, formatService);
|
||||||
template: "<div class=\"l-axis-holder\" mct-resize=\"resize()\"></div>",
|
|
||||||
priority: 1000,
|
|
||||||
link: link
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return MCTConductorAxis;
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,136 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(['./MctConductorAxis'], function (MctConductorAxis) {
|
||||||
|
describe("The MctConductorAxis directive", function () {
|
||||||
|
var directive,
|
||||||
|
mockConductor,
|
||||||
|
mockFormatService,
|
||||||
|
mockScope,
|
||||||
|
mockElement,
|
||||||
|
mockTarget,
|
||||||
|
mockBounds,
|
||||||
|
d3;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockScope = {};
|
||||||
|
|
||||||
|
//Add some HTML elements
|
||||||
|
mockTarget = {
|
||||||
|
offsetWidth: 0,
|
||||||
|
offsetHeight: 0
|
||||||
|
};
|
||||||
|
mockElement = {
|
||||||
|
firstChild: mockTarget
|
||||||
|
};
|
||||||
|
mockBounds = {
|
||||||
|
start: 100,
|
||||||
|
end: 200
|
||||||
|
};
|
||||||
|
mockConductor = jasmine.createSpyObj("conductor", [
|
||||||
|
"timeSystem",
|
||||||
|
"bounds",
|
||||||
|
"on"
|
||||||
|
]);
|
||||||
|
mockConductor.bounds.andReturn(mockBounds);
|
||||||
|
|
||||||
|
mockFormatService = jasmine.createSpyObj("formatService", [
|
||||||
|
"getFormat"
|
||||||
|
]);
|
||||||
|
|
||||||
|
var d3Functions = [
|
||||||
|
"scale",
|
||||||
|
"scaleUtc",
|
||||||
|
"scaleLinear",
|
||||||
|
"select",
|
||||||
|
"append",
|
||||||
|
"attr",
|
||||||
|
"axisTop",
|
||||||
|
"call",
|
||||||
|
"tickFormat",
|
||||||
|
"domain",
|
||||||
|
"range"
|
||||||
|
];
|
||||||
|
d3 = jasmine.createSpyObj("d3", d3Functions);
|
||||||
|
d3Functions.forEach(function(func) {
|
||||||
|
d3[func].andReturn(d3);
|
||||||
|
});
|
||||||
|
|
||||||
|
directive = new MctConductorAxis(mockConductor, mockFormatService);
|
||||||
|
directive.d3 = d3;
|
||||||
|
directive.link(mockScope, [mockElement]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("listens for changes to time system and bounds", function () {
|
||||||
|
expect(mockConductor.on).toHaveBeenCalledWith("timeSystem", directive.changeTimeSystem);
|
||||||
|
expect(mockConductor.on).toHaveBeenCalledWith("bounds", directive.setScale);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when the time system changes", function () {
|
||||||
|
var mockTimeSystem;
|
||||||
|
var mockFormat;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
mockTimeSystem = jasmine.createSpyObj("timeSystem", [
|
||||||
|
"formats",
|
||||||
|
"isUTCBased"
|
||||||
|
]);
|
||||||
|
mockFormat = jasmine.createSpyObj("format", [
|
||||||
|
"format"
|
||||||
|
]);
|
||||||
|
|
||||||
|
mockTimeSystem.formats.andReturn(["mockFormat"]);
|
||||||
|
mockFormatService.getFormat.andReturn(mockFormat);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses a UTC scale for UTC time systems", function () {
|
||||||
|
mockTimeSystem.isUTCBased.andReturn(true);
|
||||||
|
directive.changeTimeSystem(mockTimeSystem);
|
||||||
|
expect(d3.scaleUtc).toHaveBeenCalled();
|
||||||
|
expect(d3.scaleLinear).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses a linear scale for non-UTC time systems", function () {
|
||||||
|
mockTimeSystem.isUTCBased.andReturn(false);
|
||||||
|
directive.changeTimeSystem(mockTimeSystem);
|
||||||
|
expect(d3.scaleLinear).toHaveBeenCalled();
|
||||||
|
expect(d3.scaleUtc).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sets axis domain to time conductor bounds", function () {
|
||||||
|
mockTimeSystem.isUTCBased.andReturn(false);
|
||||||
|
mockConductor.timeSystem.andReturn(mockTimeSystem);
|
||||||
|
|
||||||
|
directive.setScale();
|
||||||
|
expect(d3.domain).toHaveBeenCalledWith([mockBounds.start, mockBounds.end]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses the format specified by the time system to format tick" +
|
||||||
|
" labels", function () {
|
||||||
|
directive.changeTimeSystem(mockTimeSystem);
|
||||||
|
expect(d3.tickFormat).toHaveBeenCalled();
|
||||||
|
d3.tickFormat.mostRecentCall.args[0]();
|
||||||
|
expect(mockFormat.format).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,49 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(['./NumberFormat'], function (NumberFormat) {
|
||||||
|
describe("The NumberFormat class", function () {
|
||||||
|
var format;
|
||||||
|
beforeEach(function () {
|
||||||
|
format = new NumberFormat();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("The format function takes a string and produces a number", function () {
|
||||||
|
var text = format.format(1);
|
||||||
|
expect(text).toBe("1");
|
||||||
|
expect(typeof(text)).toBe("string");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("The parse function takes a string and produces a number", function () {
|
||||||
|
var number = format.parse("1");
|
||||||
|
expect(number).toBe(1);
|
||||||
|
expect(typeof(number)).toBe("number");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("validates that the input is a number", function () {
|
||||||
|
expect(format.validate("1")).toBe(true);
|
||||||
|
expect(format.validate(1)).toBe(true);
|
||||||
|
expect(format.validate("1.1")).toBe(true);
|
||||||
|
expect(format.validate("abc")).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -26,7 +26,7 @@ define(
|
|||||||
],
|
],
|
||||||
function (TimeConductorValidation) {
|
function (TimeConductorValidation) {
|
||||||
|
|
||||||
function TimeConductorController($scope, timeConductor, conductorViewService, timeSystems) {
|
function TimeConductorController($scope, $window, timeConductor, conductorViewService, timeSystems) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
@ -38,6 +38,7 @@ define(
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.$scope = $scope;
|
this.$scope = $scope;
|
||||||
|
this.$window = $window;
|
||||||
this.conductorViewService = conductorViewService;
|
this.conductorViewService = conductorViewService;
|
||||||
this.conductor = timeConductor;
|
this.conductor = timeConductor;
|
||||||
this.modes = conductorViewService.availableModes();
|
this.modes = conductorViewService.availableModes();
|
||||||
@ -99,7 +100,6 @@ define(
|
|||||||
|
|
||||||
// Watch scope for selection of mode or time system by user
|
// Watch scope for selection of mode or time system by user
|
||||||
this.$scope.$watch('modeModel.selectedKey', this.setMode);
|
this.$scope.$watch('modeModel.selectedKey', this.setMode);
|
||||||
this.$scope.$watch('timeSystem', this.changeTimeSystem);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -113,7 +113,7 @@ define(
|
|||||||
this.$scope.boundsModel.end = bounds.end;
|
this.$scope.boundsModel.end = bounds.end;
|
||||||
if (!this.pendingUpdate) {
|
if (!this.pendingUpdate) {
|
||||||
this.pendingUpdate = true;
|
this.pendingUpdate = true;
|
||||||
requestAnimationFrame(function () {
|
this.$window.requestAnimationFrame(function () {
|
||||||
this.pendingUpdate = false;
|
this.pendingUpdate = false;
|
||||||
this.$scope.$digest();
|
this.$scope.$digest();
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
@ -136,16 +136,8 @@ define(
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
TimeConductorController.prototype.setFormFromDeltas = function (deltas) {
|
TimeConductorController.prototype.setFormFromDeltas = function (deltas) {
|
||||||
/*
|
|
||||||
* If the selected mode defines deltas, set them in the form
|
|
||||||
*/
|
|
||||||
if (deltas !== undefined) {
|
|
||||||
this.$scope.boundsModel.startDelta = deltas.start;
|
this.$scope.boundsModel.startDelta = deltas.start;
|
||||||
this.$scope.boundsModel.endDelta = deltas.end;
|
this.$scope.boundsModel.endDelta = deltas.end;
|
||||||
} else {
|
|
||||||
this.$scope.boundsModel.startDelta = 0;
|
|
||||||
this.$scope.boundsModel.endDelta = 0;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -180,7 +172,7 @@ define(
|
|||||||
var deltas = {
|
var deltas = {
|
||||||
start: boundsFormModel.startDelta,
|
start: boundsFormModel.startDelta,
|
||||||
end: boundsFormModel.endDelta
|
end: boundsFormModel.endDelta
|
||||||
}
|
};
|
||||||
if (this.validation.validateStartDelta(deltas.start) && this.validation.validateEndDelta(deltas.end)) {
|
if (this.validation.validateStartDelta(deltas.start) && this.validation.validateEndDelta(deltas.end)) {
|
||||||
//Sychronize deltas between form and mode
|
//Sychronize deltas between form and mode
|
||||||
this.conductorViewService.deltas(deltas);
|
this.conductorViewService.deltas(deltas);
|
||||||
@ -204,6 +196,8 @@ define(
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Respond to time system selection from UI
|
||||||
|
*
|
||||||
* Allows time system to be changed by key. This supports selection
|
* Allows time system to be changed by key. This supports selection
|
||||||
* from the menu. Resolves a TimeSystem object and then invokes
|
* from the menu. Resolves a TimeSystem object and then invokes
|
||||||
* TimeConductorController#setTimeSystem
|
* TimeConductorController#setTimeSystem
|
||||||
@ -211,13 +205,15 @@ define(
|
|||||||
* @see TimeConductorController#setTimeSystem
|
* @see TimeConductorController#setTimeSystem
|
||||||
*/
|
*/
|
||||||
TimeConductorController.prototype.selectTimeSystemByKey = function(key){
|
TimeConductorController.prototype.selectTimeSystemByKey = function(key){
|
||||||
var selected = this.timeSystems.find(function (timeSystem){
|
var selected = this.timeSystems.filter(function (timeSystem){
|
||||||
return timeSystem.metadata.key === key;
|
return timeSystem.metadata.key === key;
|
||||||
});
|
})[0];
|
||||||
this.conductor.timeSystem(selected, selected.defaults().bounds);
|
this.conductor.timeSystem(selected, selected.defaults().bounds);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Handles time system change from time conductor
|
||||||
|
*
|
||||||
* Sets the selected time system. Will populate form with the default
|
* Sets the selected time system. Will populate form with the default
|
||||||
* bounds and deltas defined in the selected time system.
|
* bounds and deltas defined in the selected time system.
|
||||||
*
|
*
|
||||||
@ -226,7 +222,13 @@ define(
|
|||||||
*/
|
*/
|
||||||
TimeConductorController.prototype.changeTimeSystem = function (newTimeSystem) {
|
TimeConductorController.prototype.changeTimeSystem = function (newTimeSystem) {
|
||||||
if (newTimeSystem && (newTimeSystem !== this.$scope.timeSystemModel.selected)) {
|
if (newTimeSystem && (newTimeSystem !== this.$scope.timeSystemModel.selected)) {
|
||||||
this.setFormFromDeltas((newTimeSystem.defaults() || {}).deltas);
|
if (newTimeSystem.defaults()){
|
||||||
|
var deltas = newTimeSystem.defaults().deltas || {start: 0, end: 0};
|
||||||
|
var bounds = newTimeSystem.defaults().bounds || {start: 0, end: 0};
|
||||||
|
|
||||||
|
this.setFormFromDeltas(deltas);
|
||||||
|
this.setFormFromBounds(bounds);
|
||||||
|
}
|
||||||
this.setFormFromTimeSystem(newTimeSystem);
|
this.setFormFromTimeSystem(newTimeSystem);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,335 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(['./TimeConductorController'], function (TimeConductorController) {
|
||||||
|
describe("The time conductor controller", function () {
|
||||||
|
var mockScope;
|
||||||
|
var mockWindow;
|
||||||
|
var mockTimeConductor;
|
||||||
|
var mockConductorViewService;
|
||||||
|
var mockTimeSystems;
|
||||||
|
var controller;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockScope = jasmine.createSpyObj("$scope", ["$watch"]);
|
||||||
|
mockWindow = jasmine.createSpyObj("$window", ["requestAnimationFrame"]);
|
||||||
|
mockTimeConductor = jasmine.createSpyObj(
|
||||||
|
"TimeConductor",
|
||||||
|
[
|
||||||
|
"bounds",
|
||||||
|
"timeSystem",
|
||||||
|
"on"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
mockTimeConductor.bounds.andReturn({start: undefined, end: undefined});
|
||||||
|
|
||||||
|
mockConductorViewService = jasmine.createSpyObj(
|
||||||
|
"ConductorViewService",
|
||||||
|
[
|
||||||
|
"availableModes",
|
||||||
|
"mode",
|
||||||
|
"availableTimeSystems",
|
||||||
|
"deltas"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
mockConductorViewService.availableModes.andReturn([]);
|
||||||
|
mockConductorViewService.availableTimeSystems.andReturn([]);
|
||||||
|
|
||||||
|
mockTimeSystems = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
function getListener(name) {
|
||||||
|
return mockTimeConductor.on.calls.filter(function (call){
|
||||||
|
return call.args[0] === name;
|
||||||
|
})[0].args[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("", function (){
|
||||||
|
beforeEach(function() {
|
||||||
|
controller = new TimeConductorController(
|
||||||
|
mockScope,
|
||||||
|
mockWindow,
|
||||||
|
mockTimeConductor,
|
||||||
|
mockConductorViewService,
|
||||||
|
mockTimeSystems
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when time conductor state changes", function () {
|
||||||
|
var mockFormat;
|
||||||
|
var mockDeltaFormat;
|
||||||
|
var defaultBounds;
|
||||||
|
var defaultDeltas;
|
||||||
|
var mockDefaults;
|
||||||
|
var timeSystem;
|
||||||
|
var tsListener;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockFormat = {};
|
||||||
|
mockDeltaFormat = {};
|
||||||
|
defaultBounds = {
|
||||||
|
start: 2,
|
||||||
|
end: 3
|
||||||
|
};
|
||||||
|
defaultDeltas = {
|
||||||
|
start: 10,
|
||||||
|
end: 20
|
||||||
|
};
|
||||||
|
mockDefaults = {
|
||||||
|
deltas: defaultDeltas,
|
||||||
|
bounds: defaultBounds
|
||||||
|
};
|
||||||
|
timeSystem = {
|
||||||
|
formats: function () {
|
||||||
|
return [mockFormat];
|
||||||
|
},
|
||||||
|
deltaFormat: function () {
|
||||||
|
return mockDeltaFormat;
|
||||||
|
},
|
||||||
|
defaults: function () {
|
||||||
|
return mockDefaults;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
controller = new TimeConductorController(
|
||||||
|
mockScope,
|
||||||
|
mockWindow,
|
||||||
|
mockTimeConductor,
|
||||||
|
mockConductorViewService,
|
||||||
|
mockTimeSystems
|
||||||
|
);
|
||||||
|
|
||||||
|
tsListener = getListener("timeSystem");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("listens for changes to conductor state", function () {
|
||||||
|
expect(mockTimeConductor.on).toHaveBeenCalledWith("timeSystem", jasmine.any(Function));
|
||||||
|
expect(mockTimeConductor.on).toHaveBeenCalledWith("bounds", jasmine.any(Function));
|
||||||
|
expect(mockTimeConductor.on).toHaveBeenCalledWith("follow", jasmine.any(Function));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when time system changes, sets time system on scope", function () {
|
||||||
|
expect(tsListener).toBeDefined();
|
||||||
|
tsListener(timeSystem);
|
||||||
|
|
||||||
|
expect(mockScope.timeSystemModel).toBeDefined();
|
||||||
|
expect(mockScope.timeSystemModel.selected).toBe(timeSystem);
|
||||||
|
expect(mockScope.timeSystemModel.format).toBe(mockFormat);
|
||||||
|
expect(mockScope.timeSystemModel.deltaFormat).toBe(mockDeltaFormat);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when time system changes, sets defaults on scope", function () {
|
||||||
|
expect(tsListener).toBeDefined();
|
||||||
|
tsListener(timeSystem);
|
||||||
|
|
||||||
|
expect(mockScope.boundsModel.start).toEqual(defaultBounds.start);
|
||||||
|
expect(mockScope.boundsModel.end).toEqual(defaultBounds.end);
|
||||||
|
|
||||||
|
expect(mockScope.boundsModel.startDelta).toEqual(defaultDeltas.start);
|
||||||
|
expect(mockScope.boundsModel.endDelta).toEqual(defaultDeltas.end);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when bounds change, sets them on scope", function () {
|
||||||
|
var bounds = {
|
||||||
|
start: 1,
|
||||||
|
end: 2
|
||||||
|
};
|
||||||
|
|
||||||
|
var boundsListener = getListener("bounds");
|
||||||
|
expect(boundsListener).toBeDefined();
|
||||||
|
boundsListener(bounds);
|
||||||
|
|
||||||
|
expect(mockScope.boundsModel).toBeDefined();
|
||||||
|
expect(mockScope.boundsModel.start).toEqual(bounds.start);
|
||||||
|
expect(mockScope.boundsModel.end).toEqual(bounds.end);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("responds to a change in 'follow' state of the time conductor", function () {
|
||||||
|
var followListener = getListener("follow");
|
||||||
|
expect(followListener).toBeDefined();
|
||||||
|
|
||||||
|
followListener(true);
|
||||||
|
expect(mockScope.followMode).toEqual(true);
|
||||||
|
|
||||||
|
followListener(false);
|
||||||
|
expect(mockScope.followMode).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when user makes changes from UI", function () {
|
||||||
|
var mode = "realtime";
|
||||||
|
var ts1Metadata;
|
||||||
|
var ts2Metadata;
|
||||||
|
var ts3Metadata;
|
||||||
|
var mockTimeSystemConstructors;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mode = "realtime";
|
||||||
|
ts1Metadata = {
|
||||||
|
'key': 'ts1',
|
||||||
|
'name': 'Time System One',
|
||||||
|
'cssClass': 'cssClassOne'
|
||||||
|
};
|
||||||
|
ts2Metadata = {
|
||||||
|
'key': 'ts2',
|
||||||
|
'name': 'Time System Two',
|
||||||
|
'cssClass': 'cssClassTwo'
|
||||||
|
};
|
||||||
|
ts3Metadata = {
|
||||||
|
'key': 'ts3',
|
||||||
|
'name': 'Time System Three',
|
||||||
|
'cssClass': 'cssClassThree'
|
||||||
|
};
|
||||||
|
mockTimeSystems = [
|
||||||
|
{
|
||||||
|
metadata: ts1Metadata
|
||||||
|
},
|
||||||
|
{
|
||||||
|
metadata: ts2Metadata
|
||||||
|
},
|
||||||
|
{
|
||||||
|
metadata: ts3Metadata
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
//Wrap in mock constructors
|
||||||
|
mockTimeSystemConstructors = mockTimeSystems.map(function (mockTimeSystem) {
|
||||||
|
return function () {
|
||||||
|
return mockTimeSystem;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sets the mode on scope", function () {
|
||||||
|
controller = new TimeConductorController(
|
||||||
|
mockScope,
|
||||||
|
mockWindow,
|
||||||
|
mockTimeConductor,
|
||||||
|
mockConductorViewService,
|
||||||
|
mockTimeSystemConstructors
|
||||||
|
);
|
||||||
|
|
||||||
|
mockConductorViewService.availableTimeSystems.andReturn(mockTimeSystems);
|
||||||
|
controller.setMode(mode);
|
||||||
|
|
||||||
|
expect(mockScope.modeModel.selectedKey).toEqual(mode);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sets available time systems on scope when mode changes", function () {
|
||||||
|
controller = new TimeConductorController(
|
||||||
|
mockScope,
|
||||||
|
mockWindow,
|
||||||
|
mockTimeConductor,
|
||||||
|
mockConductorViewService,
|
||||||
|
mockTimeSystemConstructors
|
||||||
|
);
|
||||||
|
|
||||||
|
mockConductorViewService.availableTimeSystems.andReturn(mockTimeSystems);
|
||||||
|
controller.setMode(mode);
|
||||||
|
|
||||||
|
expect(mockScope.timeSystemModel.options.length).toEqual(3);
|
||||||
|
expect(mockScope.timeSystemModel.options[0]).toEqual(ts1Metadata);
|
||||||
|
expect(mockScope.timeSystemModel.options[1]).toEqual(ts2Metadata);
|
||||||
|
expect(mockScope.timeSystemModel.options[2]).toEqual(ts3Metadata);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sets bounds on the time conductor", function () {
|
||||||
|
var formModel = {
|
||||||
|
start: 1,
|
||||||
|
end: 10
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
controller = new TimeConductorController(
|
||||||
|
mockScope,
|
||||||
|
mockWindow,
|
||||||
|
mockTimeConductor,
|
||||||
|
mockConductorViewService,
|
||||||
|
mockTimeSystemConstructors
|
||||||
|
);
|
||||||
|
|
||||||
|
controller.updateBoundsFromForm(formModel);
|
||||||
|
expect(mockTimeConductor.bounds).toHaveBeenCalledWith(formModel);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("applies deltas when they change in form", function () {
|
||||||
|
var deltas = {
|
||||||
|
start: 1000,
|
||||||
|
end: 2000
|
||||||
|
};
|
||||||
|
var formModel = {
|
||||||
|
startDelta: deltas.start,
|
||||||
|
endDelta: deltas.end
|
||||||
|
};
|
||||||
|
|
||||||
|
controller = new TimeConductorController(
|
||||||
|
mockScope,
|
||||||
|
mockWindow,
|
||||||
|
mockTimeConductor,
|
||||||
|
mockConductorViewService,
|
||||||
|
mockTimeSystemConstructors
|
||||||
|
);
|
||||||
|
|
||||||
|
controller.updateDeltasFromForm(formModel);
|
||||||
|
expect(mockConductorViewService.deltas).toHaveBeenCalledWith(deltas);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sets the time system on the time conductor", function () {
|
||||||
|
var defaultBounds = {
|
||||||
|
start: 5,
|
||||||
|
end: 6
|
||||||
|
};
|
||||||
|
var timeSystem = {
|
||||||
|
metadata: {
|
||||||
|
key: 'testTimeSystem'
|
||||||
|
},
|
||||||
|
defaults: function() {
|
||||||
|
return {
|
||||||
|
bounds: defaultBounds
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mockTimeSystems = [
|
||||||
|
// Wrap as constructor function
|
||||||
|
function() {
|
||||||
|
return timeSystem;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
controller = new TimeConductorController(
|
||||||
|
mockScope,
|
||||||
|
mockWindow,
|
||||||
|
mockTimeConductor,
|
||||||
|
mockConductorViewService,
|
||||||
|
mockTimeSystems
|
||||||
|
);
|
||||||
|
|
||||||
|
controller.selectTimeSystemByKey('testTimeSystem');
|
||||||
|
expect(mockTimeConductor.timeSystem).toHaveBeenCalledWith(timeSystem, defaultBounds);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
@ -39,8 +39,8 @@ define(
|
|||||||
this._tickSourceUnlisten = undefined;
|
this._tickSourceUnlisten = undefined;
|
||||||
this._timeSystems = timeSystems;
|
this._timeSystems = timeSystems;
|
||||||
this._availableTickSources = undefined;
|
this._availableTickSources = undefined;
|
||||||
|
|
||||||
this.changeTimeSystem = this.changeTimeSystem.bind(this);
|
this.changeTimeSystem = this.changeTimeSystem.bind(this);
|
||||||
|
this.tick = this.tick.bind(this);
|
||||||
|
|
||||||
//Set the time system initially
|
//Set the time system initially
|
||||||
if (conductor.timeSystem()) {
|
if (conductor.timeSystem()) {
|
||||||
@ -81,6 +81,7 @@ define(
|
|||||||
end: 0
|
end: 0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.conductor.bounds(defaults.bounds);
|
this.conductor.bounds(defaults.bounds);
|
||||||
this.deltas(defaults.deltas);
|
this.deltas(defaults.deltas);
|
||||||
|
|
||||||
@ -130,7 +131,7 @@ define(
|
|||||||
}
|
}
|
||||||
this._tickSource = tickSource;
|
this._tickSource = tickSource;
|
||||||
if (tickSource) {
|
if (tickSource) {
|
||||||
this._tickSourceUnlisten = tickSource.listen(this.tick.bind(this));
|
this._tickSourceUnlisten = tickSource.listen(this.tick);
|
||||||
//Now following a tick source
|
//Now following a tick source
|
||||||
this.conductor.follow(true);
|
this.conductor.follow(true);
|
||||||
} else {
|
} else {
|
||||||
|
@ -0,0 +1,210 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(['./TimeConductorMode'], function (TimeConductorMode) {
|
||||||
|
describe("The Time Conductor Mode", function () {
|
||||||
|
var mockTimeConductor,
|
||||||
|
fixedModeMetaData,
|
||||||
|
mockTimeSystems,
|
||||||
|
fixedTimeSystem,
|
||||||
|
|
||||||
|
realtimeModeMetaData,
|
||||||
|
realtimeTimeSystem,
|
||||||
|
mockTickSource,
|
||||||
|
|
||||||
|
mockBounds,
|
||||||
|
mode;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
fixedModeMetaData = {
|
||||||
|
key: "fixed"
|
||||||
|
};
|
||||||
|
realtimeModeMetaData = {
|
||||||
|
key: "realtime"
|
||||||
|
};
|
||||||
|
mockBounds = {
|
||||||
|
start: 0,
|
||||||
|
end: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
fixedTimeSystem = jasmine.createSpyObj("timeSystem", [
|
||||||
|
"defaults",
|
||||||
|
"tickSources"
|
||||||
|
]);
|
||||||
|
fixedTimeSystem.tickSources.andReturn([]);
|
||||||
|
|
||||||
|
mockTickSource = jasmine.createSpyObj("tickSource", [
|
||||||
|
"listen"
|
||||||
|
]);
|
||||||
|
mockTickSource.metadata = {
|
||||||
|
mode: "realtime"
|
||||||
|
};
|
||||||
|
realtimeTimeSystem = jasmine.createSpyObj("realtimeTimeSystem", [
|
||||||
|
"defaults",
|
||||||
|
"tickSources"
|
||||||
|
]);
|
||||||
|
realtimeTimeSystem.tickSources.andReturn([mockTickSource]);
|
||||||
|
|
||||||
|
//Do not return any time systems initially for a default
|
||||||
|
// construction configuration that works without any additional work
|
||||||
|
mockTimeSystems = [];
|
||||||
|
|
||||||
|
mockTimeConductor = jasmine.createSpyObj("timeConductor", [
|
||||||
|
"bounds",
|
||||||
|
"timeSystem",
|
||||||
|
"on",
|
||||||
|
"off",
|
||||||
|
"follow"
|
||||||
|
]);
|
||||||
|
mockTimeConductor.bounds.andReturn(mockBounds);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Reacts to changes in conductor time system", function () {
|
||||||
|
mode = new TimeConductorMode(fixedModeMetaData, mockTimeConductor, mockTimeSystems);
|
||||||
|
expect(mockTimeConductor.on).toHaveBeenCalledWith("timeSystem", mode.changeTimeSystem);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Stops listening to time system changes on destroy", function () {
|
||||||
|
mode = new TimeConductorMode(fixedModeMetaData, mockTimeConductor, mockTimeSystems);
|
||||||
|
mode.destroy();
|
||||||
|
expect(mockTimeConductor.off).toHaveBeenCalledWith("timeSystem", mode.changeTimeSystem);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Filters available time systems to those with tick sources that" +
|
||||||
|
" support this mode", function () {
|
||||||
|
mockTimeSystems = [fixedTimeSystem, realtimeTimeSystem];
|
||||||
|
mode = new TimeConductorMode(realtimeModeMetaData, mockTimeConductor, mockTimeSystems);
|
||||||
|
|
||||||
|
var availableTimeSystems = mode.availableTimeSystems();
|
||||||
|
expect(availableTimeSystems.length).toBe(1);
|
||||||
|
expect(availableTimeSystems.indexOf(fixedTimeSystem)).toBe(-1);
|
||||||
|
expect(availableTimeSystems.indexOf(realtimeTimeSystem)).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Changing the time system", function () {
|
||||||
|
var defaults;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
defaults = {
|
||||||
|
bounds: {
|
||||||
|
start: 1,
|
||||||
|
end: 2
|
||||||
|
},
|
||||||
|
deltas: {
|
||||||
|
start: 3,
|
||||||
|
end: 4
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fixedTimeSystem.defaults.andReturn(defaults);
|
||||||
|
|
||||||
|
});
|
||||||
|
it ("sets defaults from new time system", function() {
|
||||||
|
mode = new TimeConductorMode(fixedModeMetaData, mockTimeConductor, mockTimeSystems);
|
||||||
|
spyOn(mode, "deltas");
|
||||||
|
mode.deltas.andCallThrough();
|
||||||
|
|
||||||
|
mode.changeTimeSystem(fixedTimeSystem);
|
||||||
|
expect(mockTimeConductor.bounds).toHaveBeenCalledWith(defaults.bounds);
|
||||||
|
expect(mode.deltas).toHaveBeenCalledWith(defaults.deltas);
|
||||||
|
});
|
||||||
|
it ("If a tick source is available, sets the tick source", function() {
|
||||||
|
mode = new TimeConductorMode(realtimeModeMetaData, mockTimeConductor, mockTimeSystems);
|
||||||
|
mode.changeTimeSystem(realtimeTimeSystem);
|
||||||
|
|
||||||
|
var currentTickSource = mode.tickSource();
|
||||||
|
expect(currentTickSource).toBe(mockTickSource);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Setting a tick source", function () {
|
||||||
|
var mockUnlistener;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
mockUnlistener = jasmine.createSpy("unlistener");
|
||||||
|
mockTickSource.listen.andReturn(mockUnlistener);
|
||||||
|
|
||||||
|
mode = new TimeConductorMode(realtimeModeMetaData, mockTimeConductor, mockTimeSystems);
|
||||||
|
mode.tickSource(mockTickSource);
|
||||||
|
});
|
||||||
|
|
||||||
|
it ("Unlistens from old tick source", function() {
|
||||||
|
mode.tickSource(mockTickSource);
|
||||||
|
expect(mockUnlistener).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it ("Listens to new tick source", function() {
|
||||||
|
expect(mockTickSource.listen).toHaveBeenCalledWith(mode.tick);
|
||||||
|
});
|
||||||
|
|
||||||
|
it ("Sets 'follow' state on time conductor", function() {
|
||||||
|
expect(mockTimeConductor.follow).toHaveBeenCalledWith(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it ("on destroy, unlistens from tick source", function() {
|
||||||
|
mode.destroy();
|
||||||
|
expect(mockUnlistener).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("setting deltas", function () {
|
||||||
|
beforeEach(function() {
|
||||||
|
mode = new TimeConductorMode(realtimeModeMetaData, mockTimeConductor, mockTimeSystems);
|
||||||
|
});
|
||||||
|
it ("sets the bounds on the time conductor based on new delta" +
|
||||||
|
" values", function() {
|
||||||
|
var deltas = {
|
||||||
|
start: 20,
|
||||||
|
end: 10
|
||||||
|
};
|
||||||
|
|
||||||
|
mode.deltas(deltas);
|
||||||
|
|
||||||
|
expect(mockTimeConductor.bounds).toHaveBeenCalledWith({
|
||||||
|
start: mockBounds.end - deltas.start,
|
||||||
|
end: mockBounds.end + deltas.end
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("ticking", function () {
|
||||||
|
beforeEach(function() {
|
||||||
|
mode = new TimeConductorMode(realtimeModeMetaData, mockTimeConductor, mockTimeSystems);
|
||||||
|
});
|
||||||
|
it ("sets bounds based on current delta values", function() {
|
||||||
|
var deltas = {
|
||||||
|
start: 20,
|
||||||
|
end: 10
|
||||||
|
};
|
||||||
|
var time = 100;
|
||||||
|
|
||||||
|
mode.deltas(deltas);
|
||||||
|
mode.tick(time);
|
||||||
|
|
||||||
|
expect(mockTimeConductor.bounds).toHaveBeenCalledWith({
|
||||||
|
start: time - deltas.start,
|
||||||
|
end:time + deltas.end
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -31,7 +31,7 @@ define(
|
|||||||
*/
|
*/
|
||||||
function TimeConductorValidation(conductor) {
|
function TimeConductorValidation(conductor) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.conductor = conductor
|
this.conductor = conductor;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Bind all class functions to 'this'
|
* Bind all class functions to 'this'
|
||||||
|
@ -0,0 +1,73 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(['./TimeConductorValidation'], function (TimeConductorValidation) {
|
||||||
|
describe("The Time Conductor Validation class", function () {
|
||||||
|
var timeConductorValidation,
|
||||||
|
mockTimeConductor;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockTimeConductor = jasmine.createSpyObj("timeConductor", [
|
||||||
|
"validateBounds",
|
||||||
|
"bounds"
|
||||||
|
]);
|
||||||
|
timeConductorValidation = new TimeConductorValidation(mockTimeConductor);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Validates start and end values using Time Conductor", function () {
|
||||||
|
beforeEach(function() {
|
||||||
|
var mockBounds = {
|
||||||
|
start: 10,
|
||||||
|
end: 20
|
||||||
|
};
|
||||||
|
|
||||||
|
mockTimeConductor.bounds.andReturn(mockBounds);
|
||||||
|
|
||||||
|
});
|
||||||
|
it("Validates start values using Time Conductor", function () {
|
||||||
|
var startValue = 30;
|
||||||
|
timeConductorValidation.validateStart(startValue);
|
||||||
|
expect(mockTimeConductor.validateBounds).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
it("Validates end values using Time Conductor", function () {
|
||||||
|
var endValue = 40;
|
||||||
|
timeConductorValidation.validateEnd(endValue);
|
||||||
|
expect(mockTimeConductor.validateBounds).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Validates that start delta is valid number > 0", function () {
|
||||||
|
expect(timeConductorValidation.validateStartDelta(-1)).toBe(false);
|
||||||
|
expect(timeConductorValidation.validateStartDelta("abc")).toBe(false);
|
||||||
|
expect(timeConductorValidation.validateStartDelta("1")).toBe(true);
|
||||||
|
expect(timeConductorValidation.validateStartDelta(1)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Validates that end delta is valid number >= 0", function () {
|
||||||
|
expect(timeConductorValidation.validateEndDelta(-1)).toBe(false);
|
||||||
|
expect(timeConductorValidation.validateEndDelta("abc")).toBe(false);
|
||||||
|
expect(timeConductorValidation.validateEndDelta("1")).toBe(true);
|
||||||
|
expect(timeConductorValidation.validateEndDelta(0)).toBe(true);
|
||||||
|
expect(timeConductorValidation.validateEndDelta(1)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -36,7 +36,7 @@ define(
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function TimeConductorViewService(conductor, timeSystems) {
|
function TimeConductorViewService(conductor, timeSystems) {
|
||||||
this._timeSystems = timeSystems = timeSystems.map(
|
this._timeSystems = timeSystems.map(
|
||||||
function (timeSystemConstructor) {
|
function (timeSystemConstructor) {
|
||||||
return timeSystemConstructor();
|
return timeSystemConstructor();
|
||||||
});
|
});
|
||||||
@ -63,34 +63,38 @@ define(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function timeSystemsForMode(sourceType) {
|
function hasTickSource(sourceType, timeSystem) {
|
||||||
return timeSystems.filter(function (timeSystem){
|
|
||||||
return timeSystem.tickSources().some(function (tickSource){
|
return timeSystem.tickSources().some(function (tickSource){
|
||||||
return tickSource.metadata.mode === sourceType;
|
return tickSource.metadata.mode === sourceType;
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var timeSystemsForMode = function (sourceType) {
|
||||||
|
return this._timeSystems.filter(hasTickSource.bind(this, sourceType));
|
||||||
|
}.bind(this);
|
||||||
|
|
||||||
//Only show 'real-time mode' if appropriate time systems available
|
//Only show 'real-time mode' if appropriate time systems available
|
||||||
if (timeSystemsForMode('realtime').length > 0 ) {
|
if (timeSystemsForMode('realtime').length > 0 ) {
|
||||||
this._availableModes['realtime'] = {
|
var realtimeMode = {
|
||||||
key: 'realtime',
|
key: 'realtime',
|
||||||
cssclass: 'icon-clock',
|
cssclass: 'icon-clock',
|
||||||
label: 'Real-time',
|
label: 'Real-time',
|
||||||
name: 'Real-time Mode',
|
name: 'Real-time Mode',
|
||||||
description: 'Monitor real-time streaming data as it comes in. The Time Conductor and displays will automatically advance themselves based on a UTC clock.'
|
description: 'Monitor real-time streaming data as it comes in. The Time Conductor and displays will automatically advance themselves based on a UTC clock.'
|
||||||
};
|
};
|
||||||
|
this._availableModes[realtimeMode.key] = realtimeMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Only show 'LAD mode' if appropriate time systems available
|
//Only show 'LAD mode' if appropriate time systems available
|
||||||
if (timeSystemsForMode('LAD').length > 0) {
|
if (timeSystemsForMode('LAD').length > 0) {
|
||||||
this._availableModes['latest'] = {
|
var ladMode = {
|
||||||
key: 'LAD',
|
key: 'LAD',
|
||||||
cssclass: 'icon-database',
|
cssclass: 'icon-database',
|
||||||
label: 'LAD',
|
label: 'LAD',
|
||||||
name: 'LAD Mode',
|
name: 'LAD Mode',
|
||||||
description: 'Latest Available Data mode monitors real-time streaming data as it comes in. The Time Conductor and displays will only advance when data becomes available.'
|
description: 'Latest Available Data mode monitors real-time streaming data as it comes in. The Time Conductor and displays will only advance when data becomes available.'
|
||||||
};
|
};
|
||||||
|
this._availableModes[ladMode.key] = ladMode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,6 +118,12 @@ define(
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
TimeConductorViewService.prototype.mode = function (newModeKey) {
|
TimeConductorViewService.prototype.mode = function (newModeKey) {
|
||||||
|
function contains(timeSystems, ts) {
|
||||||
|
return timeSystems.filter(function (t) {
|
||||||
|
return t.metadata.key === ts.metadata.key;
|
||||||
|
}).length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (arguments.length === 1) {
|
if (arguments.length === 1) {
|
||||||
var timeSystem = this._conductor.timeSystem();
|
var timeSystem = this._conductor.timeSystem();
|
||||||
var modes = this.availableModes();
|
var modes = this.availableModes();
|
||||||
@ -122,13 +132,10 @@ define(
|
|||||||
if (this._mode) {
|
if (this._mode) {
|
||||||
this._mode.destroy();
|
this._mode.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
function contains(timeSystems, timeSystem) {
|
|
||||||
return timeSystems.find(function (t) {
|
|
||||||
return t.metadata.key === timeSystem.metadata.key;
|
|
||||||
}) !== undefined;
|
|
||||||
}
|
|
||||||
this._mode = new TimeConductorMode(modeMetaData, this._conductor, this._timeSystems);
|
this._mode = new TimeConductorMode(modeMetaData, this._conductor, this._timeSystems);
|
||||||
|
|
||||||
|
// If no time system set on time conductor, or the currently selected time system is not available in
|
||||||
|
// the new mode, default to first available time system
|
||||||
if (!timeSystem || !contains(this._mode.availableTimeSystems(), timeSystem)) {
|
if (!timeSystem || !contains(this._mode.availableTimeSystems(), timeSystem)) {
|
||||||
timeSystem = this._mode.availableTimeSystems()[0];
|
timeSystem = this._mode.availableTimeSystems()[0];
|
||||||
this._conductor.timeSystem(timeSystem, timeSystem.defaults().bounds);
|
this._conductor.timeSystem(timeSystem, timeSystem.defaults().bounds);
|
||||||
@ -169,7 +176,7 @@ define(
|
|||||||
*/
|
*/
|
||||||
TimeConductorViewService.prototype.deltas = function () {
|
TimeConductorViewService.prototype.deltas = function () {
|
||||||
//Deltas stored on mode. Use .apply to preserve arguments
|
//Deltas stored on mode. Use .apply to preserve arguments
|
||||||
return this._mode.deltas.apply(this._mode, arguments)
|
return this._mode.deltas.apply(this._mode, arguments);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,185 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(['./TimeConductorViewService'], function (TimeConductorViewService) {
|
||||||
|
describe("The Time Conductor view service", function () {
|
||||||
|
var mockTimeConductor;
|
||||||
|
var basicTimeSystem;
|
||||||
|
var tickingTimeSystem;
|
||||||
|
var viewService;
|
||||||
|
var tickingTimeSystemDefaults;
|
||||||
|
|
||||||
|
function mockConstructor(object) {
|
||||||
|
return function () {
|
||||||
|
return object;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockTimeConductor = jasmine.createSpyObj("timeConductor", [
|
||||||
|
"timeSystem",
|
||||||
|
"bounds",
|
||||||
|
"follow",
|
||||||
|
"on",
|
||||||
|
"off"
|
||||||
|
]);
|
||||||
|
|
||||||
|
basicTimeSystem = jasmine.createSpyObj("basicTimeSystem", [
|
||||||
|
"tickSources",
|
||||||
|
"defaults"
|
||||||
|
]);
|
||||||
|
basicTimeSystem.metadata = {
|
||||||
|
key: "basic"
|
||||||
|
};
|
||||||
|
basicTimeSystem.tickSources.andReturn([]);
|
||||||
|
basicTimeSystem.defaults.andReturn({
|
||||||
|
bounds: {
|
||||||
|
start: 0,
|
||||||
|
end: 1
|
||||||
|
},
|
||||||
|
deltas: {
|
||||||
|
start: 0,
|
||||||
|
end: 0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//Initialize conductor
|
||||||
|
mockTimeConductor.timeSystem.andReturn(basicTimeSystem);
|
||||||
|
mockTimeConductor.bounds.andReturn({start: 0, end: 1});
|
||||||
|
|
||||||
|
tickingTimeSystem = jasmine.createSpyObj("tickingTimeSystem", [
|
||||||
|
"tickSources",
|
||||||
|
"defaults"
|
||||||
|
]);
|
||||||
|
tickingTimeSystem.metadata = {
|
||||||
|
key: "ticking"
|
||||||
|
};
|
||||||
|
tickingTimeSystemDefaults = {
|
||||||
|
bounds: {
|
||||||
|
start: 100,
|
||||||
|
end: 200
|
||||||
|
},
|
||||||
|
deltas: {
|
||||||
|
start: 1000,
|
||||||
|
end: 500
|
||||||
|
}
|
||||||
|
};
|
||||||
|
tickingTimeSystem.defaults.andReturn(tickingTimeSystemDefaults);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("At a minimum supports fixed mode", function () {
|
||||||
|
var mockTimeSystems = [mockConstructor(basicTimeSystem)];
|
||||||
|
viewService = new TimeConductorViewService(mockTimeConductor, mockTimeSystems);
|
||||||
|
|
||||||
|
var availableModes = viewService.availableModes();
|
||||||
|
expect(availableModes.fixed).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Supports realtime mode if appropriate tick source(s) availables", function () {
|
||||||
|
var mockTimeSystems = [mockConstructor(tickingTimeSystem)];
|
||||||
|
var mockRealtimeTickSource = {
|
||||||
|
metadata: {
|
||||||
|
mode: 'realtime'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
tickingTimeSystem.tickSources.andReturn([mockRealtimeTickSource]);
|
||||||
|
|
||||||
|
viewService = new TimeConductorViewService(mockTimeConductor, mockTimeSystems);
|
||||||
|
|
||||||
|
var availableModes = viewService.availableModes();
|
||||||
|
expect(availableModes.realtime).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Supports LAD mode if appropriate tick source(s) available", function () {
|
||||||
|
var mockTimeSystems = [mockConstructor(tickingTimeSystem)];
|
||||||
|
var mockLADTickSource = {
|
||||||
|
metadata: {
|
||||||
|
mode: 'LAD'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
tickingTimeSystem.tickSources.andReturn([mockLADTickSource]);
|
||||||
|
|
||||||
|
viewService = new TimeConductorViewService(mockTimeConductor, mockTimeSystems);
|
||||||
|
|
||||||
|
var availableModes = viewService.availableModes();
|
||||||
|
expect(availableModes.LAD).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when mode is changed", function () {
|
||||||
|
|
||||||
|
it("destroys previous mode", function () {
|
||||||
|
var mockTimeSystems = [mockConstructor(basicTimeSystem)];
|
||||||
|
|
||||||
|
var oldMode = jasmine.createSpyObj("conductorMode", [
|
||||||
|
"destroy"
|
||||||
|
]);
|
||||||
|
|
||||||
|
viewService = new TimeConductorViewService(mockTimeConductor, mockTimeSystems);
|
||||||
|
viewService._mode = oldMode;
|
||||||
|
viewService.mode('fixed');
|
||||||
|
expect(oldMode.destroy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("the time system", function () {
|
||||||
|
it("is retained if available in new mode", function () {
|
||||||
|
var mockTimeSystems = [mockConstructor(basicTimeSystem), mockConstructor(tickingTimeSystem)];
|
||||||
|
var mockRealtimeTickSource = {
|
||||||
|
metadata: {
|
||||||
|
mode: 'realtime'
|
||||||
|
},
|
||||||
|
listen: function() {}
|
||||||
|
};
|
||||||
|
tickingTimeSystem.tickSources.andReturn([mockRealtimeTickSource]);
|
||||||
|
|
||||||
|
viewService = new TimeConductorViewService(mockTimeConductor, mockTimeSystems);
|
||||||
|
|
||||||
|
//Set time system to one known to support realtime mode
|
||||||
|
mockTimeConductor.timeSystem.andReturn(tickingTimeSystem);
|
||||||
|
|
||||||
|
//Select realtime mode
|
||||||
|
mockTimeConductor.timeSystem.reset();
|
||||||
|
viewService.mode('realtime');
|
||||||
|
expect(mockTimeConductor.timeSystem).not.toHaveBeenCalledWith(tickingTimeSystem, tickingTimeSystemDefaults.bounds);
|
||||||
|
});
|
||||||
|
it("is defaulted if selected time system not available in new mode", function () {
|
||||||
|
var mockTimeSystems = [mockConstructor(basicTimeSystem), mockConstructor(tickingTimeSystem)];
|
||||||
|
var mockRealtimeTickSource = {
|
||||||
|
metadata: {
|
||||||
|
mode: 'realtime'
|
||||||
|
},
|
||||||
|
listen: function() {}
|
||||||
|
};
|
||||||
|
tickingTimeSystem.tickSources.andReturn([mockRealtimeTickSource]);
|
||||||
|
|
||||||
|
viewService = new TimeConductorViewService(mockTimeConductor, mockTimeSystems);
|
||||||
|
|
||||||
|
//Set time system to one known to not support realtime mode
|
||||||
|
mockTimeConductor.timeSystem.andReturn(basicTimeSystem);
|
||||||
|
|
||||||
|
//Select realtime mode
|
||||||
|
mockTimeConductor.timeSystem.reset();
|
||||||
|
viewService.mode('realtime');
|
||||||
|
expect(mockTimeConductor.timeSystem).toHaveBeenCalledWith(tickingTimeSystem, tickingTimeSystemDefaults.bounds);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,46 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(['./UTCTimeSystem'], function (UTCTimeSystem) {
|
||||||
|
describe("The UTCTimeSystem class", function () {
|
||||||
|
var timeSystem,
|
||||||
|
mockTimeout;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockTimeout = jasmine.createSpy("timeout");
|
||||||
|
timeSystem = new UTCTimeSystem(mockTimeout);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("defines at least one format", function () {
|
||||||
|
expect(timeSystem.formats().length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("defines a tick source", function () {
|
||||||
|
var tickSources = timeSystem.tickSources();
|
||||||
|
expect(tickSources.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("defines some defaults", function () {
|
||||||
|
expect(timeSystem.defaults()).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -239,7 +239,7 @@ define(
|
|||||||
setBasePanZoom(bounds);
|
setBasePanZoom(bounds);
|
||||||
requery();
|
requery();
|
||||||
}
|
}
|
||||||
self.updateStatus($scope.domainObject, follow);
|
self.setUnsynchedStatus($scope.domainObject, follow && self.isZoomed());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.modeOptions = new PlotModeOptions([], subPlotFactory);
|
this.modeOptions = new PlotModeOptions([], subPlotFactory);
|
||||||
@ -370,9 +370,9 @@ define(
|
|||||||
return this.pending;
|
return this.pending;
|
||||||
};
|
};
|
||||||
|
|
||||||
PlotController.prototype.updateStatus = function (domainObject, follow) {
|
PlotController.prototype.setUnsynchedStatus = function (domainObject, status) {
|
||||||
if (domainObject.hasCapability('status')) {
|
if (domainObject.hasCapability('status')) {
|
||||||
domainObject.getCapability('status').set('timeconductor-unsynced', follow && this.isZoomed());
|
domainObject.getCapability('status').set('timeconductor-unsynced', status);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ define(
|
|||||||
mockHandle,
|
mockHandle,
|
||||||
mockDomainObject,
|
mockDomainObject,
|
||||||
mockSeries,
|
mockSeries,
|
||||||
|
mockStatusCapability,
|
||||||
controller;
|
controller;
|
||||||
|
|
||||||
function bind(method, thisObj) {
|
function bind(method, thisObj) {
|
||||||
@ -71,7 +72,7 @@ define(
|
|||||||
);
|
);
|
||||||
mockDomainObject = jasmine.createSpyObj(
|
mockDomainObject = jasmine.createSpyObj(
|
||||||
"domainObject",
|
"domainObject",
|
||||||
["getId", "getModel", "getCapability"]
|
["getId", "getModel", "getCapability", "hasCapability"]
|
||||||
);
|
);
|
||||||
mockHandler = jasmine.createSpyObj(
|
mockHandler = jasmine.createSpyObj(
|
||||||
"telemetrySubscriber",
|
"telemetrySubscriber",
|
||||||
@ -95,6 +96,11 @@ define(
|
|||||||
['getPointCount', 'getDomainValue', 'getRangeValue']
|
['getPointCount', 'getDomainValue', 'getRangeValue']
|
||||||
);
|
);
|
||||||
|
|
||||||
|
mockStatusCapability = jasmine.createSpyObj(
|
||||||
|
"statusCapability",
|
||||||
|
["set"]
|
||||||
|
);
|
||||||
|
|
||||||
mockHandler.handle.andReturn(mockHandle);
|
mockHandler.handle.andReturn(mockHandle);
|
||||||
mockThrottle.andCallFake(function (fn) {
|
mockThrottle.andCallFake(function (fn) {
|
||||||
return fn;
|
return fn;
|
||||||
@ -230,6 +236,34 @@ define(
|
|||||||
expect(bind(controller.unzoom, controller)).not.toThrow();
|
expect(bind(controller.unzoom, controller)).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("sets status when plot becomes detached from time conductor", function () {
|
||||||
|
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
||||||
|
|
||||||
|
function boundsEvent(){
|
||||||
|
fireEvent("telemetry:display:bounds", [
|
||||||
|
{},
|
||||||
|
{ start: 10, end: 100 },
|
||||||
|
true
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
mockDomainObject.hasCapability.andCallFake(function (name) {
|
||||||
|
return name === "status";
|
||||||
|
});
|
||||||
|
mockDomainObject.getCapability.andReturn(mockStatusCapability);
|
||||||
|
spyOn(controller, "isZoomed");
|
||||||
|
|
||||||
|
//Mock zoomed in state
|
||||||
|
controller.isZoomed.andReturn(true);
|
||||||
|
boundsEvent();
|
||||||
|
expect(mockStatusCapability.set).toHaveBeenCalledWith("timeconductor-unsynced", true);
|
||||||
|
|
||||||
|
//"Reset" zoom
|
||||||
|
controller.isZoomed.andReturn(false);
|
||||||
|
boundsEvent();
|
||||||
|
expect(mockStatusCapability.set).toHaveBeenCalledWith("timeconductor-unsynced", false);
|
||||||
|
});
|
||||||
|
|
||||||
it("indicates if a request is pending", function () {
|
it("indicates if a request is pending", function () {
|
||||||
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
||||||
expect(controller.isRequestPending()).toBeTruthy();
|
expect(controller.isRequestPending()).toBeTruthy();
|
||||||
@ -286,7 +320,6 @@ define(
|
|||||||
expect(mockHandle.request.calls.length).toEqual(2);
|
expect(mockHandle.request.calls.length).toEqual(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it("maintains externally-provided domain axis bounds after data is received", function () {
|
it("maintains externally-provided domain axis bounds after data is received", function () {
|
||||||
mockSeries.getPointCount.andReturn(3);
|
mockSeries.getPointCount.andReturn(3);
|
||||||
mockSeries.getRangeValue.andReturn(42);
|
mockSeries.getRangeValue.andReturn(42);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user