Added tests

This commit is contained in:
Henry 2016-06-30 20:50:03 -07:00
parent 8d2c489fa9
commit 5b656faa9d
2 changed files with 280 additions and 138 deletions

View File

@ -1,151 +1,183 @@
define(['EventEmitter'], /*****************************************************************************
function (EventEmitter) { * 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(['EventEmitter'], function (EventEmitter) {
* The public API for setting and querying time conductor state. The
* TimeConductor is an event emitter and extends the EventEmitter
* class. A number of events are fired when properties of the time
* conductor change, and these are documented below.
* @constructor
*/
function TimeConductor() {
EventEmitter.call(this);
//The Time System /**
this.system = undefined; * The public API for setting and querying time conductor state. The
//The Time Of Interest * time conductor is the means by which the temporal bounds of a view
this.toi = undefined; * are controlled. Time-sensitive views will typically respond to
* changes to bounds or other properties of the time conductor and
* update the data displayed based on the time conductor state.
*
* The TimeConductor extends the EventEmitter class. A number of events are
* fired when properties of the time conductor change, which are
* documented below.
* @constructor
*/
function TimeConductor() {
EventEmitter.call(this);
this.boundsVal = { //The Time System
start: undefined, this.system = undefined;
end: undefined //The Time Of Interest
}; this.toi = undefined;
//Default to fixed mode this.boundsVal = {
this.followMode = false; start: undefined,
end: undefined
};
//Default to fixed mode
this.followMode = false;
}
TimeConductor.prototype = Object.create(EventEmitter.prototype);
/**
* Validate the given bounds. This can be used for pre-validation of
* bounds, for example by views validating user inputs.
* @param bounds The start and end time of the conductor.
* @returns {string | true} A validation error, or true if valid
*/
TimeConductor.prototype.validateBounds = function (bounds) {
if ((bounds.start === undefined) ||
(bounds.end === undefined) ||
isNaN(bounds.start) ||
isNaN(bounds.end)
) {
return "Start and end must be specified as integer values";
} else if (bounds.start > bounds.end) {
return "Specified start date exceeds end bound";
} }
return true;
};
TimeConductor.prototype = Object.create(EventEmitter.prototype); function throwOnError(validationResult) {
if (validationResult !== true) {
/** throw new Error(validationResult);
* Validate the given bounds. This can be used for pre-validation of
* bounds, for example by views validating user inputs.
* @param bounds The start and end time of the conductor.
* @returns {string | true} A validation error, or true if valid
*/
TimeConductor.prototype.validateBounds = function (bounds) {
if (!bounds.start ||
!bounds.end ||
isNaN(bounds.start) ||
isNaN(bounds.end)
) {
return "Start and end must be specified as integer values";
} else if (bounds.start > bounds.end){
return "Specified start date exceeds end bound";
}
return true;
};
function throwOnError(validationResult) {
if (validationResult !== true) {
throw validationResult;
}
} }
}
/** /**
* Switch the time conductor between follow and fixed modes. In * Get or set the follow mode of the time conductor. In follow mode the
* follow mode the time conductor ticks. * time conductor ticks, regularly updating the bounds from a timing
* @fires TimeConductor#follow * source appropriate to the selected time system and mode of the time
* @param followMode * conductor.
* @returns {*} * @fires TimeConductor#follow
*/ * @param {boolean} followMode
TimeConductor.prototype.follow = function (followMode) { * @returns {boolean}
if (arguments.length === 1) { */
this.followMode = followMode; TimeConductor.prototype.follow = function (followMode) {
/** if (arguments.length > 0) {
* @event TimeConductor#follow The TimeConductor has toggled this.followMode = followMode;
* into or out of follow mode. /**
* @property {boolean} followMode true if follow mode is * @event TimeConductor#follow The TimeConductor has toggled
* enabled, otherwise false. * into or out of follow mode.
*/ * @property {boolean} followMode true if follow mode is
this.emit('follow', followMode); * enabled, otherwise false.
} */
return this.followMode; this.emit('follow', this.followMode);
}; }
return this.followMode;
};
/** /**
* @typedef {Object} TimeConductorBounds * @typedef {Object} TimeConductorBounds
* @property {number} start The start time displayed by the time conductor in ms since epoch. Epoch determined by current time system * @property {number} start The start time displayed by the time conductor in ms since epoch. Epoch determined by current time system
* @property {number} end The end time displayed by the time conductor in ms since epoch. * @property {number} end The end time displayed by the time conductor in ms since epoch.
*/ */
/** /**
* Set the start and end time of the time conductor. Basic validation of bounds is performed. * Get or set the start and end time of the time conductor. Basic validation
* * of bounds is performed.
* @param {TimeConductorBounds} newBounds *
* @param {TimeConductorBounds} should this change trigger a refresh? * @param {TimeConductorBounds} newBounds
* @throws {string} Validation error * @throws {Error} Validation error
* @fires TimeConductor#bounds * @fires TimeConductor#bounds
* @returns {TimeConductorBounds} * @returns {TimeConductorBounds}
*/ */
TimeConductor.prototype.bounds = function (newBounds) { TimeConductor.prototype.bounds = function (newBounds) {
if (arguments.length === 1) { if (arguments.length > 0) {
throwOnError(this.validateBounds(newBounds)); throwOnError(this.validateBounds(newBounds));
this.boundsVal = newBounds; this.boundsVal = newBounds;
/** /**
* @event TimeConductor#bounds The start time, end time, or * @event TimeConductor#bounds The start time, end time, or
* both have been updated * both have been updated
* @property {TimeConductorBounds} bounds * @property {TimeConductorBounds} bounds
*/ */
this.emit('bounds', this.boundsVal); this.emit('bounds', this.boundsVal);
} }
return this.boundsVal; return this.boundsVal;
}; };
/** /**
* Set the time system of the TimeConductor. Time systems determine units, epoch, and other aspects of time representation. * Get or set the time system of the TimeConductor. Time systems determine
* @param newTimeSystem * units, epoch, and other aspects of time representation. When changing
* @fires TimeConductor#timeSystem * the time system in use, new valid bounds must also be provided.
* @returns {TimeSystem} The currently applied time system * @param {TimeSystem} newTimeSystem
*/ * @param {TimeConductorBounds} bounds
TimeConductor.prototype.timeSystem = function (newTimeSystem, bounds) { * @fires TimeConductor#timeSystem
if (arguments.length === 2) { * @returns {TimeSystem} The currently applied time system
this.system = newTimeSystem; */
/** TimeConductor.prototype.timeSystem = function (newTimeSystem, bounds) {
* @event TimeConductor#timeSystem The time system used by the time if (arguments.length >= 2) {
* conductor has changed. A change in Time System will always be this.system = newTimeSystem;
* followed by a bounds event specifying new query bounds /**
* @property {TimeSystem} The value of the currently applied * @event TimeConductor#timeSystem The time system used by the time
* Time System * conductor has changed. A change in Time System will always be
* */ * followed by a bounds event specifying new query bounds
this.emit('timeSystem', this.system); * @property {TimeSystem} The value of the currently applied
// Do something with bounds here. Try and convert between * Time System
// time systems? Or just set defaults when time system changes? * */
// eg. this.emit('timeSystem', this.system);
this.bounds(bounds); // Do something with bounds here. Try and convert between
} else if (arguments.length === 1) { // time systems? Or just set defaults when time system changes?
throw new Error('Must set bounds when changing time system'); // eg.
} this.bounds(bounds);
return this.system; } else if (arguments.length === 1) {
}; throw new Error('Must set bounds when changing time system');
}
return this.system;
};
/** /**
* The Time of Interest is the temporal focus of the current view. It can be manipulated by the user from the time * Get or set the Time of Interest. The Time of Interest is the temporal
* conductor or from other views. * focus of the current view. It can be manipulated by the user from the
* @fires TimeConductor#timeOfInterest * time conductor or from other views.
* @param newTOI * @fires TimeConductor#timeOfInterest
* @returns {*} * @param newTOI
*/ * @returns {number} the current time of interest
TimeConductor.prototype.timeOfInterest = function (newTOI) { */
if (arguments.length === 1) { TimeConductor.prototype.timeOfInterest = function (newTOI) {
this.toi = newTOI; if (arguments.length > 0) {
/** this.toi = newTOI;
* @event TimeConductor#timeOfInterest The Time of Interest has moved. /**
* @property {number} Current time of interest * @event TimeConductor#timeOfInterest The Time of Interest has moved.
*/ * @property {number} Current time of interest
this.emit('timeOfInterest'); */
} this.emit('timeOfInterest', this.toi);
return this.toi; }
}; return this.toi;
};
return TimeConductor; return TimeConductor;
}); });

View File

@ -0,0 +1,110 @@
/*****************************************************************************
* 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(['./TimeConductor'], function (TimeConductor) {
describe("The Time Conductor", function () {
var tc,
timeSystem,
bounds,
eventListener,
toi,
follow;
beforeEach(function () {
tc = new TimeConductor();
timeSystem = {};
bounds = {start: 0, end: 0};
eventListener = jasmine.createSpy("eventListener");
toi = 111;
follow = true;
});
it("Supports setting and querying of time of interest and and follow mode", function () {
expect(tc.timeOfInterest()).not.toBe(toi);
tc.timeOfInterest(toi);
expect(tc.timeOfInterest()).toBe(toi);
expect(tc.follow()).not.toBe(follow);
tc.follow(follow);
expect(tc.follow()).toBe(follow);
});
it("Allows setting of valid bounds", function () {
bounds = {start: 0, end: 1};
expect(tc.bounds()).not.toBe(bounds);
expect(tc.bounds.bind(tc, bounds)).not.toThrow();
expect(tc.bounds()).toBe(bounds);
});
it("Disallows setting of invalid bounds", function () {
bounds = {start: 1, end: 0};
expect(tc.bounds()).not.toBe(bounds);
expect(tc.bounds.bind(tc, bounds)).toThrow();
expect(tc.bounds()).not.toBe(bounds);
bounds = {start: 1};
expect(tc.bounds()).not.toBe(bounds);
expect(tc.bounds.bind(tc, bounds)).toThrow();
expect(tc.bounds()).not.toBe(bounds);
});
it("Allows setting of time system with bounds", function () {
expect(tc.timeSystem()).not.toBe(timeSystem);
expect(tc.timeSystem.bind(tc, timeSystem, bounds)).not.toThrow();
expect(tc.timeSystem()).toBe(timeSystem);
});
it("Disallows setting of time system without bounds", function () {
expect(tc.timeSystem()).not.toBe(timeSystem);
expect(tc.timeSystem.bind(tc, timeSystem)).toThrow();
expect(tc.timeSystem()).not.toBe(timeSystem);
});
it("Emits an event when time system changes", function () {
expect(eventListener).not.toHaveBeenCalled();
tc.on("timeSystem", eventListener);
tc.timeSystem(timeSystem, bounds);
expect(eventListener).toHaveBeenCalledWith(timeSystem);
});
it("Emits an event when time of interest changes", function () {
expect(eventListener).not.toHaveBeenCalled();
tc.on("timeOfInterest", eventListener);
tc.timeOfInterest(toi);
expect(eventListener).toHaveBeenCalledWith(toi);
});
it("Emits an event when bounds change", function () {
expect(eventListener).not.toHaveBeenCalled();
tc.on("bounds", eventListener);
tc.bounds(bounds);
expect(eventListener).toHaveBeenCalledWith(bounds);
});
it("Emits an event when follow mode changes", function () {
expect(eventListener).not.toHaveBeenCalled();
tc.on("follow", eventListener);
tc.follow(follow);
expect(eventListener).toHaveBeenCalledWith(follow);
});
});
});