Merge branch 'open1077' into 1435-integration
This commit is contained in:
@@ -1,80 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT 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 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.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../src/DomainColumn"],
|
||||
function (DomainColumn) {
|
||||
|
||||
var TEST_DOMAIN_VALUE = "some formatted domain value";
|
||||
|
||||
describe("A domain column", function () {
|
||||
var mockDatum,
|
||||
testMetadata,
|
||||
mockFormatter,
|
||||
column;
|
||||
|
||||
beforeEach(function () {
|
||||
|
||||
mockFormatter = jasmine.createSpyObj(
|
||||
"formatter",
|
||||
["formatDomainValue", "formatRangeValue"]
|
||||
);
|
||||
testMetadata = {
|
||||
key: "testKey",
|
||||
name: "Test Name",
|
||||
format: "Test Format"
|
||||
};
|
||||
mockFormatter.formatDomainValue.andReturn(TEST_DOMAIN_VALUE);
|
||||
|
||||
column = new DomainColumn(testMetadata, mockFormatter);
|
||||
});
|
||||
|
||||
it("reports a column header from domain metadata", function () {
|
||||
expect(column.getTitle()).toEqual("Test Name");
|
||||
});
|
||||
|
||||
describe("when given a datum", function () {
|
||||
beforeEach(function () {
|
||||
mockDatum = {
|
||||
testKey: "testKeyValue"
|
||||
};
|
||||
});
|
||||
|
||||
it("looks up data from the given datum", function () {
|
||||
expect(column.getValue(undefined, mockDatum))
|
||||
.toEqual({ text: TEST_DOMAIN_VALUE });
|
||||
});
|
||||
|
||||
it("uses formatter to format domain values as requested", function () {
|
||||
column.getValue(undefined, mockDatum);
|
||||
expect(mockFormatter.formatDomainValue)
|
||||
.toHaveBeenCalledWith("testKeyValue", "Test Format");
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,56 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT 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 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.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../src/NameColumn"],
|
||||
function (NameColumn) {
|
||||
|
||||
describe("A name column", function () {
|
||||
var mockDomainObject,
|
||||
column;
|
||||
|
||||
beforeEach(function () {
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
["getModel"]
|
||||
);
|
||||
mockDomainObject.getModel.andReturn({
|
||||
name: "Test object name"
|
||||
});
|
||||
column = new NameColumn();
|
||||
});
|
||||
|
||||
it("reports a column header", function () {
|
||||
expect(column.getTitle()).toEqual("Name");
|
||||
});
|
||||
|
||||
it("looks up name from an object's model", function () {
|
||||
expect(column.getValue(mockDomainObject).text)
|
||||
.toEqual("Test object name");
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,74 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT 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 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.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../src/RangeColumn"],
|
||||
function (RangeColumn) {
|
||||
|
||||
var TEST_RANGE_VALUE = "some formatted range value";
|
||||
|
||||
describe("A range column", function () {
|
||||
var testDatum,
|
||||
testMetadata,
|
||||
mockFormatter,
|
||||
mockDomainObject,
|
||||
column;
|
||||
|
||||
beforeEach(function () {
|
||||
testDatum = { testKey: 123, otherKey: 456 };
|
||||
mockFormatter = jasmine.createSpyObj(
|
||||
"formatter",
|
||||
["formatDomainValue", "formatRangeValue"]
|
||||
);
|
||||
testMetadata = {
|
||||
key: "testKey",
|
||||
name: "Test Name"
|
||||
};
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
["getModel", "getCapability"]
|
||||
);
|
||||
mockFormatter.formatRangeValue.andReturn(TEST_RANGE_VALUE);
|
||||
|
||||
column = new RangeColumn(testMetadata, mockFormatter);
|
||||
});
|
||||
|
||||
it("reports a column header from range metadata", function () {
|
||||
expect(column.getTitle()).toEqual("Test Name");
|
||||
});
|
||||
|
||||
it("formats range values as numbers", function () {
|
||||
expect(column.getValue(mockDomainObject, testDatum).text)
|
||||
.toEqual(TEST_RANGE_VALUE);
|
||||
|
||||
// Make sure that service interactions were as expected
|
||||
expect(mockFormatter.formatRangeValue)
|
||||
.toHaveBeenCalledWith(testDatum.testKey);
|
||||
expect(mockFormatter.formatDomainValue)
|
||||
.not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -22,13 +22,14 @@
|
||||
|
||||
define(
|
||||
[
|
||||
"../src/TableConfiguration",
|
||||
"../src/DomainColumn"
|
||||
"../src/TableConfiguration"
|
||||
],
|
||||
function (Table, DomainColumn) {
|
||||
function (Table) {
|
||||
|
||||
describe("A table", function () {
|
||||
var mockDomainObject,
|
||||
mockAPI,
|
||||
mockTelemetryAPI,
|
||||
mockTelemetryFormatter,
|
||||
table,
|
||||
mockModel;
|
||||
@@ -49,90 +50,63 @@ define(
|
||||
|
||||
mockTelemetryFormatter = jasmine.createSpyObj('telemetryFormatter',
|
||||
[
|
||||
'formatDomainValue',
|
||||
'formatRangeValue'
|
||||
'format'
|
||||
]);
|
||||
mockTelemetryFormatter.formatDomainValue.andCallFake(function (valueIn) {
|
||||
return valueIn;
|
||||
});
|
||||
mockTelemetryFormatter.formatRangeValue.andCallFake(function (valueIn) {
|
||||
mockTelemetryFormatter.format.andCallFake(function (valueIn) {
|
||||
return valueIn;
|
||||
});
|
||||
|
||||
table = new Table(mockDomainObject, mockTelemetryFormatter);
|
||||
});
|
||||
mockTelemetryAPI = jasmine.createSpyObj('telemetryAPI', [
|
||||
'getValueFormatter'
|
||||
]);
|
||||
mockAPI = {
|
||||
telemetry: mockTelemetryAPI
|
||||
};
|
||||
mockTelemetryAPI.getValueFormatter.andReturn(mockTelemetryFormatter);
|
||||
|
||||
it("Add column with no index adds new column to the end", function () {
|
||||
var firstColumn = {title: 'First Column'},
|
||||
secondColumn = {title: 'Second Column'},
|
||||
thirdColumn = {title: 'Third Column'};
|
||||
|
||||
table.addColumn(firstColumn);
|
||||
table.addColumn(secondColumn);
|
||||
table.addColumn(thirdColumn);
|
||||
|
||||
expect(table.columns).toBeDefined();
|
||||
expect(table.columns.length).toBe(3);
|
||||
expect(table.columns[0]).toBe(firstColumn);
|
||||
expect(table.columns[1]).toBe(secondColumn);
|
||||
expect(table.columns[2]).toBe(thirdColumn);
|
||||
});
|
||||
|
||||
it("Add column with index adds new column at the specified" +
|
||||
" position", function () {
|
||||
var firstColumn = {title: 'First Column'},
|
||||
secondColumn = {title: 'Second Column'},
|
||||
thirdColumn = {title: 'Third Column'};
|
||||
|
||||
table.addColumn(firstColumn);
|
||||
table.addColumn(thirdColumn);
|
||||
table.addColumn(secondColumn, 1);
|
||||
|
||||
expect(table.columns).toBeDefined();
|
||||
expect(table.columns.length).toBe(3);
|
||||
expect(table.columns[0]).toBe(firstColumn);
|
||||
expect(table.columns[1]).toBe(secondColumn);
|
||||
expect(table.columns[2]).toBe(thirdColumn);
|
||||
table = new Table(mockDomainObject, mockAPI);
|
||||
});
|
||||
|
||||
describe("Building columns from telemetry metadata", function () {
|
||||
var metadata = [{
|
||||
ranges: [
|
||||
{
|
||||
name: 'Range 1',
|
||||
key: 'range1'
|
||||
},
|
||||
{
|
||||
name: 'Range 2',
|
||||
key: 'range2'
|
||||
var metadata = [
|
||||
{
|
||||
name: 'Range 1',
|
||||
key: 'range1',
|
||||
hints: {
|
||||
y: 1
|
||||
}
|
||||
],
|
||||
domains: [
|
||||
{
|
||||
name: 'Domain 1',
|
||||
key: 'domain1',
|
||||
format: 'utc'
|
||||
},
|
||||
{
|
||||
name: 'Domain 2',
|
||||
key: 'domain2',
|
||||
format: 'utc'
|
||||
},
|
||||
{
|
||||
name: 'Range 2',
|
||||
key: 'range2',
|
||||
hints: {
|
||||
y: 2
|
||||
}
|
||||
]
|
||||
}];
|
||||
},
|
||||
{
|
||||
name: 'Domain 1',
|
||||
key: 'domain1',
|
||||
format: 'utc',
|
||||
hints: {
|
||||
x: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Domain 2',
|
||||
key: 'domain2',
|
||||
format: 'utc',
|
||||
hints: {
|
||||
x: 2
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
beforeEach(function () {
|
||||
table.populateColumns(metadata);
|
||||
});
|
||||
|
||||
it("populates columns", function () {
|
||||
expect(table.columns.length).toBe(5);
|
||||
});
|
||||
|
||||
it("Build columns populates columns with domains to the left", function () {
|
||||
expect(table.columns[1] instanceof DomainColumn).toBeTruthy();
|
||||
expect(table.columns[2] instanceof DomainColumn).toBeTruthy();
|
||||
expect(table.columns[3] instanceof DomainColumn).toBeFalsy();
|
||||
expect(table.columns.length).toBe(4);
|
||||
});
|
||||
|
||||
it("Produces headers for each column based on title", function () {
|
||||
@@ -141,7 +115,7 @@ define(
|
||||
|
||||
spyOn(firstColumn, 'getTitle');
|
||||
headers = table.getHeaders();
|
||||
expect(headers.length).toBe(5);
|
||||
expect(headers.length).toBe(4);
|
||||
expect(firstColumn.getTitle).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -178,23 +152,33 @@ define(
|
||||
|
||||
beforeEach(function () {
|
||||
datum = {
|
||||
'range1': 'range 1 value',
|
||||
'range2': 'range 2 value',
|
||||
'range1': 10,
|
||||
'range2': 20,
|
||||
'domain1': 0,
|
||||
'domain2': 1
|
||||
};
|
||||
rowValues = table.getRowValues(mockDomainObject, datum);
|
||||
var limitEvaluator = {
|
||||
evaluate: function () {
|
||||
return {
|
||||
"cssClass": "alarm-class"
|
||||
};
|
||||
}
|
||||
};
|
||||
rowValues = table.getRowValues(limitEvaluator, datum);
|
||||
});
|
||||
|
||||
it("Returns a value for every column", function () {
|
||||
expect(rowValues['Range 1'].text).toBeDefined();
|
||||
expect(rowValues['Range 1'].text).toEqual('range 1' +
|
||||
' value');
|
||||
expect(rowValues['Range 1'].text).toEqual(10);
|
||||
});
|
||||
|
||||
it("Uses the telemetry formatter to appropriately format" +
|
||||
it("Applies appropriate css class if limit violated.", function () {
|
||||
expect(rowValues['Range 1'].cssClass).toEqual("alarm-class");
|
||||
});
|
||||
|
||||
it("Uses telemetry formatter to appropriately format" +
|
||||
" telemetry values", function () {
|
||||
expect(mockTelemetryFormatter.formatRangeValue).toHaveBeenCalled();
|
||||
expect(mockTelemetryFormatter.format).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
191
platform/features/table/test/TelemetryCollectionSpec.js
Normal file
191
platform/features/table/test/TelemetryCollectionSpec.js
Normal file
@@ -0,0 +1,191 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT 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 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(
|
||||
[
|
||||
"../src/TelemetryCollection"
|
||||
],
|
||||
function (TelemetryCollection) {
|
||||
|
||||
describe("A telemetry collection", function () {
|
||||
|
||||
var collection;
|
||||
var telemetryObjects;
|
||||
var ms;
|
||||
var integerTextMap = ["ZERO", "ONE", "TWO", "THREE", "FOUR", "FIVE",
|
||||
"SIX", "SEVEN", "EIGHT", "NINE", "TEN", "ELEVEN"];
|
||||
|
||||
beforeEach(function () {
|
||||
telemetryObjects = [0,9,2,4,7,8,5,1,3,6].map(function (number) {
|
||||
ms = number * 1000;
|
||||
return {
|
||||
timestamp: ms,
|
||||
value: {
|
||||
integer: number,
|
||||
text: integerTextMap[number]
|
||||
}
|
||||
};
|
||||
});
|
||||
collection = new TelemetryCollection();
|
||||
});
|
||||
|
||||
it("Sorts inserted telemetry by specified field",
|
||||
function () {
|
||||
collection.sort('value.integer');
|
||||
collection.add(telemetryObjects);
|
||||
expect(collection.telemetry[0].value.integer).toBe(0);
|
||||
expect(collection.telemetry[1].value.integer).toBe(1);
|
||||
expect(collection.telemetry[2].value.integer).toBe(2);
|
||||
expect(collection.telemetry[3].value.integer).toBe(3);
|
||||
|
||||
collection.sort('value.text');
|
||||
expect(collection.telemetry[0].value.text).toBe("EIGHT");
|
||||
expect(collection.telemetry[1].value.text).toBe("FIVE");
|
||||
expect(collection.telemetry[2].value.text).toBe("FOUR");
|
||||
expect(collection.telemetry[3].value.text).toBe("NINE");
|
||||
}
|
||||
);
|
||||
|
||||
describe("on bounds change", function () {
|
||||
var discardedCallback;
|
||||
|
||||
beforeEach(function () {
|
||||
discardedCallback = jasmine.createSpy("discarded");
|
||||
collection.on("discarded", discardedCallback);
|
||||
collection.sort("timestamp");
|
||||
collection.add(telemetryObjects);
|
||||
collection.bounds({start: 5000, end: 8000});
|
||||
});
|
||||
|
||||
|
||||
it("emits an event indicating that telemetry has " +
|
||||
"been discarded", function () {
|
||||
expect(discardedCallback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("discards telemetry data with a time stamp " +
|
||||
"before specified start bound", function () {
|
||||
var discarded = discardedCallback.mostRecentCall.args[0];
|
||||
|
||||
// Expect 5 because as an optimization, the TelemetryCollection
|
||||
// will not consider telemetry values that exceed the upper
|
||||
// bounds. Arbitrary bounds changes in which the end bound is
|
||||
// decreased is assumed to require a new historical query, and
|
||||
// hence re-population of the collection anyway
|
||||
expect(discarded.length).toBe(5);
|
||||
expect(discarded[0].value.integer).toBe(0);
|
||||
expect(discarded[1].value.integer).toBe(1);
|
||||
expect(discarded[4].value.integer).toBe(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when adding telemetry to a collection", function () {
|
||||
var addedCallback;
|
||||
beforeEach(function () {
|
||||
collection.sort("timestamp");
|
||||
collection.add(telemetryObjects);
|
||||
addedCallback = jasmine.createSpy("added");
|
||||
collection.on("added", addedCallback);
|
||||
});
|
||||
|
||||
it("emits an event",
|
||||
function () {
|
||||
var addedObject = {
|
||||
timestamp: 10000,
|
||||
value: {
|
||||
integer: 10,
|
||||
text: integerTextMap[10]
|
||||
}
|
||||
};
|
||||
collection.add([addedObject]);
|
||||
expect(addedCallback).toHaveBeenCalledWith([addedObject]);
|
||||
}
|
||||
);
|
||||
it("inserts in the correct order",
|
||||
function () {
|
||||
var addedObjectA = {
|
||||
timestamp: 10000,
|
||||
value: {
|
||||
integer: 10,
|
||||
text: integerTextMap[10]
|
||||
}
|
||||
};
|
||||
var addedObjectB = {
|
||||
timestamp: 11000,
|
||||
value: {
|
||||
integer: 11,
|
||||
text: integerTextMap[11]
|
||||
}
|
||||
};
|
||||
collection.add([addedObjectB, addedObjectA]);
|
||||
|
||||
expect(collection.telemetry[11]).toBe(addedObjectB);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe("buffers telemetry", function () {
|
||||
var addedObjectA;
|
||||
var addedObjectB;
|
||||
|
||||
beforeEach(function () {
|
||||
collection.sort("timestamp");
|
||||
collection.add(telemetryObjects);
|
||||
|
||||
addedObjectA = {
|
||||
timestamp: 10000,
|
||||
value: {
|
||||
integer: 10,
|
||||
text: integerTextMap[10]
|
||||
}
|
||||
};
|
||||
addedObjectB = {
|
||||
timestamp: 11000,
|
||||
value: {
|
||||
integer: 11,
|
||||
text: integerTextMap[11]
|
||||
}
|
||||
};
|
||||
|
||||
collection.bounds({start: 0, end: 10000});
|
||||
collection.add([addedObjectA, addedObjectB]);
|
||||
});
|
||||
it("when it falls outside of bounds", function () {
|
||||
expect(collection.highBuffer).toBeDefined();
|
||||
expect(collection.highBuffer.length).toBe(1);
|
||||
expect(collection.highBuffer[0]).toBe(addedObjectB);
|
||||
});
|
||||
it("and adds it to collection when it falls within bounds", function () {
|
||||
expect(collection.telemetry.length).toBe(11);
|
||||
collection.bounds({start: 0, end: 11000});
|
||||
expect(collection.telemetry.length).toBe(12);
|
||||
expect(collection.telemetry[11]).toBe(addedObjectB);
|
||||
});
|
||||
it("and removes it from the buffer when it falls within bounds", function () {
|
||||
expect(collection.highBuffer.length).toBe(1);
|
||||
collection.bounds({start: 0, end: 11000});
|
||||
expect(collection.highBuffer.length).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,380 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT 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 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(
|
||||
[
|
||||
"../../src/controllers/HistoricalTableController"
|
||||
],
|
||||
function (TableController) {
|
||||
|
||||
describe('The Table Controller', function () {
|
||||
var mockScope,
|
||||
mockTelemetryHandler,
|
||||
mockTelemetryHandle,
|
||||
mockTelemetryFormatter,
|
||||
mockDomainObject,
|
||||
mockTable,
|
||||
mockConfiguration,
|
||||
mockAngularTimeout,
|
||||
mockTimeoutHandle,
|
||||
watches,
|
||||
mockConductor,
|
||||
controller;
|
||||
|
||||
function promise(value) {
|
||||
return {
|
||||
then: function (callback) {
|
||||
return promise(callback(value));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getCallback(target, event) {
|
||||
return target.calls.filter(function (call) {
|
||||
return call.args[0] === event;
|
||||
})[0].args[1];
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
watches = {};
|
||||
mockScope = jasmine.createSpyObj('scope', [
|
||||
'$on',
|
||||
'$watch',
|
||||
'$watchCollection'
|
||||
]);
|
||||
|
||||
mockScope.$on.andCallFake(function (expression, callback) {
|
||||
watches[expression] = callback;
|
||||
});
|
||||
mockScope.$watch.andCallFake(function (expression, callback) {
|
||||
watches[expression] = callback;
|
||||
});
|
||||
mockScope.$watchCollection.andCallFake(function (expression, callback) {
|
||||
watches[expression] = callback;
|
||||
});
|
||||
|
||||
mockTimeoutHandle = jasmine.createSpy("timeoutHandle");
|
||||
mockAngularTimeout = jasmine.createSpy("$timeout");
|
||||
mockAngularTimeout.andReturn(mockTimeoutHandle);
|
||||
mockAngularTimeout.cancel = jasmine.createSpy("cancelTimeout");
|
||||
|
||||
mockConfiguration = {
|
||||
'range1': true,
|
||||
'range2': true,
|
||||
'domain1': true
|
||||
};
|
||||
|
||||
mockTable = jasmine.createSpyObj('table',
|
||||
[
|
||||
'populateColumns',
|
||||
'buildColumnConfiguration',
|
||||
'getRowValues',
|
||||
'saveColumnConfiguration'
|
||||
]
|
||||
);
|
||||
mockTable.columns = [];
|
||||
mockTable.buildColumnConfiguration.andReturn(mockConfiguration);
|
||||
|
||||
mockDomainObject = jasmine.createSpyObj('domainObject', [
|
||||
'getCapability',
|
||||
'useCapability',
|
||||
'getModel'
|
||||
]);
|
||||
mockDomainObject.getModel.andReturn({});
|
||||
|
||||
mockScope.domainObject = mockDomainObject;
|
||||
|
||||
mockTelemetryHandle = jasmine.createSpyObj('telemetryHandle', [
|
||||
'request',
|
||||
'promiseTelemetryObjects',
|
||||
'getTelemetryObjects',
|
||||
'getMetadata',
|
||||
'getSeries',
|
||||
'unsubscribe',
|
||||
'makeDatum'
|
||||
]);
|
||||
mockTelemetryHandle.promiseTelemetryObjects.andReturn(promise(undefined));
|
||||
mockTelemetryHandle.request.andReturn(promise(undefined));
|
||||
mockTelemetryHandle.getTelemetryObjects.andReturn([]);
|
||||
mockTelemetryHandle.getMetadata.andReturn([]);
|
||||
|
||||
mockTelemetryHandler = jasmine.createSpyObj('telemetryHandler', [
|
||||
'handle'
|
||||
]);
|
||||
mockTelemetryHandler.handle.andReturn(mockTelemetryHandle);
|
||||
|
||||
mockConductor = jasmine.createSpyObj("conductor", [
|
||||
"timeSystem",
|
||||
"on",
|
||||
"off"
|
||||
]);
|
||||
|
||||
controller = new TableController(mockScope, mockTelemetryHandler,
|
||||
mockTelemetryFormatter, mockAngularTimeout, {conductor: mockConductor});
|
||||
|
||||
controller.table = mockTable;
|
||||
controller.handle = mockTelemetryHandle;
|
||||
});
|
||||
|
||||
it('subscribes to telemetry handler for telemetry updates', function () {
|
||||
controller.subscribe();
|
||||
expect(mockTelemetryHandler.handle).toHaveBeenCalled();
|
||||
expect(mockTelemetryHandle.request).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Unsubscribes from telemetry when scope is destroyed', function () {
|
||||
controller.handle = mockTelemetryHandle;
|
||||
watches.$destroy();
|
||||
expect(mockTelemetryHandle.unsubscribe).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('makes use of the table', function () {
|
||||
|
||||
it('to create column definitions from telemetry' +
|
||||
' metadata', function () {
|
||||
controller.setup();
|
||||
expect(mockTable.populateColumns).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('to create column configuration, which is written to the' +
|
||||
' object model', function () {
|
||||
controller.setup();
|
||||
expect(mockTable.buildColumnConfiguration).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('updates the rows on scope when historical telemetry is received', function () {
|
||||
var mockSeries = {
|
||||
getPointCount: function () {
|
||||
return 5;
|
||||
},
|
||||
getDomainValue: function () {
|
||||
return 'Domain Value';
|
||||
},
|
||||
getRangeValue: function () {
|
||||
return 'Range Value';
|
||||
}
|
||||
},
|
||||
mockRow = {'domain': 'Domain Value', 'range': 'Range' +
|
||||
' Value'};
|
||||
|
||||
mockTelemetryHandle.makeDatum.andCallFake(function () {
|
||||
return mockRow;
|
||||
});
|
||||
mockTable.getRowValues.andReturn(mockRow);
|
||||
mockTelemetryHandle.getTelemetryObjects.andReturn([mockDomainObject]);
|
||||
mockTelemetryHandle.getSeries.andReturn(mockSeries);
|
||||
|
||||
controller.addHistoricalData(mockDomainObject, mockSeries);
|
||||
|
||||
// Angular timeout is called a minumum of twice, regardless
|
||||
// of batch size used.
|
||||
expect(mockAngularTimeout).toHaveBeenCalled();
|
||||
mockAngularTimeout.mostRecentCall.args[0]();
|
||||
expect(mockAngularTimeout.calls.length).toEqual(2);
|
||||
mockAngularTimeout.mostRecentCall.args[0]();
|
||||
|
||||
expect(controller.$scope.rows.length).toBe(5);
|
||||
expect(controller.$scope.rows[0]).toBe(mockRow);
|
||||
});
|
||||
|
||||
it('filters the visible columns based on configuration', function () {
|
||||
controller.filterColumns();
|
||||
expect(controller.$scope.headers.length).toBe(3);
|
||||
expect(controller.$scope.headers[2]).toEqual('domain1');
|
||||
|
||||
mockConfiguration.domain1 = false;
|
||||
controller.filterColumns();
|
||||
expect(controller.$scope.headers.length).toBe(2);
|
||||
expect(controller.$scope.headers[2]).toBeUndefined();
|
||||
});
|
||||
|
||||
describe('creates event listeners', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(controller, 'subscribe');
|
||||
spyOn(controller, 'filterColumns');
|
||||
});
|
||||
|
||||
it('triggers telemetry subscription update when domain' +
|
||||
' object changes', function () {
|
||||
controller.registerChangeListeners();
|
||||
//'watches' object is populated by fake scope watch and
|
||||
// watchCollection functions defined above
|
||||
expect(watches.domainObject).toBeDefined();
|
||||
watches.domainObject(mockDomainObject);
|
||||
expect(controller.subscribe).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('triggers telemetry subscription update when domain' +
|
||||
' object composition changes', function () {
|
||||
controller.registerChangeListeners();
|
||||
expect(watches['domainObject.getModel().composition']).toBeDefined();
|
||||
watches['domainObject.getModel().composition']([], []);
|
||||
expect(controller.subscribe).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('triggers telemetry subscription update when time' +
|
||||
' conductor bounds change', function () {
|
||||
controller.registerChangeListeners();
|
||||
expect(watches['telemetry:display:bounds']).toBeDefined();
|
||||
watches['telemetry:display:bounds']();
|
||||
expect(controller.subscribe).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('triggers refiltering of the columns when configuration' +
|
||||
' changes', function () {
|
||||
controller.setup();
|
||||
expect(watches['domainObject.getModel().configuration.table.columns']).toBeDefined();
|
||||
watches['domainObject.getModel().configuration.table.columns']();
|
||||
expect(controller.filterColumns).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
describe('After populating columns', function () {
|
||||
var metadata;
|
||||
beforeEach(function () {
|
||||
metadata = [{domains: [{name: 'time domain 1'}, {name: 'time domain 2'}]}, {domains: [{name: 'time domain 3'}, {name: 'time domain 4'}]}];
|
||||
controller.populateColumns(metadata);
|
||||
});
|
||||
|
||||
it('Automatically identifies time columns', function () {
|
||||
expect(controller.timeColumns.length).toBe(4);
|
||||
expect(controller.timeColumns[0]).toBe('time domain 1');
|
||||
});
|
||||
|
||||
it('Automatically sorts by time column that matches current' +
|
||||
' time system', function () {
|
||||
var key = 'time_domain_1',
|
||||
name = 'time domain 1',
|
||||
mockTimeSystem = {
|
||||
metadata: {
|
||||
key: key
|
||||
}
|
||||
};
|
||||
|
||||
mockTable.columns = [
|
||||
{
|
||||
domainMetadata: {
|
||||
key: key
|
||||
},
|
||||
getTitle: function () {
|
||||
return name;
|
||||
}
|
||||
},
|
||||
{
|
||||
domainMetadata: {
|
||||
key: 'anotherColumn'
|
||||
},
|
||||
getTitle: function () {
|
||||
return 'some other column';
|
||||
}
|
||||
},
|
||||
{
|
||||
domainMetadata: {
|
||||
key: 'thirdColumn'
|
||||
},
|
||||
getTitle: function () {
|
||||
return 'a third column';
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
expect(mockConductor.on).toHaveBeenCalledWith('timeSystem', jasmine.any(Function));
|
||||
getCallback(mockConductor.on, 'timeSystem')(mockTimeSystem);
|
||||
expect(controller.$scope.defaultSort).toBe(name);
|
||||
});
|
||||
});
|
||||
describe('Yields thread', function () {
|
||||
var mockSeries,
|
||||
mockRow;
|
||||
|
||||
beforeEach(function () {
|
||||
mockSeries = {
|
||||
getPointCount: function () {
|
||||
return 5;
|
||||
},
|
||||
getDomainValue: function () {
|
||||
return 'Domain Value';
|
||||
},
|
||||
getRangeValue: function () {
|
||||
return 'Range Value';
|
||||
}
|
||||
};
|
||||
mockRow = {'domain': 'Domain Value', 'range': 'Range Value'};
|
||||
|
||||
mockTelemetryHandle.makeDatum.andCallFake(function () {
|
||||
return mockRow;
|
||||
});
|
||||
mockTable.getRowValues.andReturn(mockRow);
|
||||
mockTelemetryHandle.getTelemetryObjects.andReturn([mockDomainObject]);
|
||||
mockTelemetryHandle.getSeries.andReturn(mockSeries);
|
||||
});
|
||||
it('when row count exceeds batch size', function () {
|
||||
controller.batchSize = 3;
|
||||
controller.addHistoricalData(mockDomainObject, mockSeries);
|
||||
|
||||
//Timeout is called a minimum of two times
|
||||
expect(mockAngularTimeout).toHaveBeenCalled();
|
||||
mockAngularTimeout.mostRecentCall.args[0]();
|
||||
|
||||
expect(mockAngularTimeout.calls.length).toEqual(2);
|
||||
mockAngularTimeout.mostRecentCall.args[0]();
|
||||
|
||||
//Because it yields, timeout will have been called a
|
||||
// third time for the batch.
|
||||
expect(mockAngularTimeout.calls.length).toEqual(3);
|
||||
mockAngularTimeout.mostRecentCall.args[0]();
|
||||
|
||||
expect(controller.$scope.rows.length).toBe(5);
|
||||
expect(controller.$scope.rows[0]).toBe(mockRow);
|
||||
});
|
||||
it('cancelling any outstanding timeouts', function () {
|
||||
controller.batchSize = 3;
|
||||
controller.addHistoricalData(mockDomainObject, mockSeries);
|
||||
|
||||
expect(mockAngularTimeout).toHaveBeenCalled();
|
||||
mockAngularTimeout.mostRecentCall.args[0]();
|
||||
|
||||
controller.addHistoricalData(mockDomainObject, mockSeries);
|
||||
|
||||
expect(mockAngularTimeout.cancel).toHaveBeenCalledWith(mockTimeoutHandle);
|
||||
});
|
||||
it('cancels timeout on scope destruction', function () {
|
||||
controller.batchSize = 3;
|
||||
controller.addHistoricalData(mockDomainObject, mockSeries);
|
||||
|
||||
//Destroy is used by parent class as well, so multiple
|
||||
// calls are made to scope.$on
|
||||
var destroyCalls = mockScope.$on.calls.filter(function (call) {
|
||||
return call.args[0] === '$destroy';
|
||||
});
|
||||
//Call destroy function
|
||||
expect(destroyCalls.length).toEqual(2);
|
||||
|
||||
destroyCalls[0].args[1]();
|
||||
expect(mockAngularTimeout.cancel).toHaveBeenCalledWith(mockTimeoutHandle);
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -39,21 +39,13 @@ define(
|
||||
var controller,
|
||||
mockScope,
|
||||
watches,
|
||||
mockTimeout,
|
||||
mockWindow,
|
||||
mockElement,
|
||||
mockExportService,
|
||||
mockConductor,
|
||||
mockFormatService,
|
||||
mockFormat;
|
||||
|
||||
function promise(value) {
|
||||
return {
|
||||
then: function (callback) {
|
||||
return promise(callback(value));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getCallback(target, event) {
|
||||
return target.calls.filter(function (call) {
|
||||
return call.args[0] === event;
|
||||
@@ -66,7 +58,8 @@ define(
|
||||
mockScope = jasmine.createSpyObj('scope', [
|
||||
'$watch',
|
||||
'$on',
|
||||
'$watchCollection'
|
||||
'$watchCollection',
|
||||
'$digest'
|
||||
]);
|
||||
mockScope.$watchCollection.andCallFake(function (event, callback) {
|
||||
watches[event] = callback;
|
||||
@@ -86,8 +79,11 @@ define(
|
||||
]);
|
||||
|
||||
mockScope.displayHeaders = true;
|
||||
mockTimeout = jasmine.createSpy('$timeout');
|
||||
mockTimeout.andReturn(promise(undefined));
|
||||
mockWindow = jasmine.createSpyObj('$window', ['requestAnimationFrame']);
|
||||
mockWindow.requestAnimationFrame.andCallFake(function (f) {
|
||||
return f();
|
||||
});
|
||||
|
||||
mockFormat = jasmine.createSpyObj('formatter', [
|
||||
'parse',
|
||||
'format'
|
||||
@@ -99,7 +95,7 @@ define(
|
||||
|
||||
controller = new MCTTableController(
|
||||
mockScope,
|
||||
mockTimeout,
|
||||
mockWindow,
|
||||
mockElement,
|
||||
mockExportService,
|
||||
mockFormatService,
|
||||
@@ -114,12 +110,12 @@ define(
|
||||
expect(mockScope.$watch).toHaveBeenCalledWith('rows', jasmine.any(Function));
|
||||
});
|
||||
|
||||
it('destroys listeners on destruction', function () {
|
||||
expect(mockScope.$on).toHaveBeenCalledWith('$destroy', controller.destroyConductorListeners);
|
||||
it('unregisters listeners on destruction', function () {
|
||||
expect(mockScope.$on).toHaveBeenCalledWith('$destroy', jasmine.any(Function));
|
||||
getCallback(mockScope.$on, '$destroy')();
|
||||
|
||||
expect(mockConductor.off).toHaveBeenCalledWith('timeSystem', controller.changeTimeSystem);
|
||||
expect(mockConductor.off).toHaveBeenCalledWith('timeOfInterest', controller.setTimeOfInterest);
|
||||
expect(mockConductor.off).toHaveBeenCalledWith('timeOfInterest', controller.changeTimeOfInterest);
|
||||
expect(mockConductor.off).toHaveBeenCalledWith('bounds', controller.changeBounds);
|
||||
});
|
||||
|
||||
@@ -233,9 +229,20 @@ define(
|
||||
|
||||
//Mock setting the rows on scope
|
||||
var rowsCallback = getCallback(mockScope.$watch, 'rows');
|
||||
rowsCallback(rowsAsc);
|
||||
var setRowsPromise = rowsCallback(rowsAsc);
|
||||
var promiseResolved = false;
|
||||
setRowsPromise.then(function () {
|
||||
promiseResolved = true;
|
||||
});
|
||||
|
||||
waitsFor(function () {
|
||||
return promiseResolved;
|
||||
}, "promise to resolve", 100);
|
||||
|
||||
runs(function () {
|
||||
expect(mockScope.toiRowIndex).toBe(2);
|
||||
});
|
||||
|
||||
expect(mockScope.toiRowIndex).toBe(2);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -287,7 +294,7 @@ define(
|
||||
});
|
||||
|
||||
it('Supports adding rows individually', function () {
|
||||
var addRowFunc = getCallback(mockScope.$on, 'add:row'),
|
||||
var addRowFunc = getCallback(mockScope.$on, 'add:rows'),
|
||||
row4 = {
|
||||
'col1': {'text': 'row3 col1'},
|
||||
'col2': {'text': 'ghi'},
|
||||
@@ -296,15 +303,15 @@ define(
|
||||
controller.setRows(testRows);
|
||||
expect(mockScope.displayRows.length).toBe(3);
|
||||
testRows.push(row4);
|
||||
addRowFunc(undefined, 3);
|
||||
addRowFunc(undefined, [row4]);
|
||||
expect(mockScope.displayRows.length).toBe(4);
|
||||
});
|
||||
|
||||
it('Supports removing rows individually', function () {
|
||||
var removeRowFunc = getCallback(mockScope.$on, 'remove:row');
|
||||
var removeRowFunc = getCallback(mockScope.$on, 'remove:rows');
|
||||
controller.setRows(testRows);
|
||||
expect(mockScope.displayRows.length).toBe(3);
|
||||
removeRowFunc(undefined, 2);
|
||||
removeRowFunc(undefined, [testRows[2]]);
|
||||
expect(mockScope.displayRows.length).toBe(2);
|
||||
expect(controller.setVisibleRows).toHaveBeenCalled();
|
||||
});
|
||||
@@ -366,7 +373,7 @@ define(
|
||||
it('Allows sort column to be changed externally by ' +
|
||||
'setting or changing sortBy attribute', function () {
|
||||
mockScope.displayRows = testRows;
|
||||
var sortByCB = getCallback(mockScope.$watch, 'sortColumn');
|
||||
var sortByCB = getCallback(mockScope.$watch, 'defaultSort');
|
||||
sortByCB('col2');
|
||||
|
||||
expect(mockScope.sortDirection).toEqual('asc');
|
||||
@@ -381,10 +388,21 @@ define(
|
||||
it('updates visible rows in scope', function () {
|
||||
var oldRows;
|
||||
mockScope.rows = testRows;
|
||||
controller.setRows(testRows);
|
||||
var setRowsPromise = controller.setRows(testRows);
|
||||
var promiseResolved = false;
|
||||
setRowsPromise.then(function () {
|
||||
promiseResolved = true;
|
||||
});
|
||||
oldRows = mockScope.visibleRows;
|
||||
mockScope.toggleSort('col2');
|
||||
expect(mockScope.visibleRows).not.toEqual(oldRows);
|
||||
|
||||
waitsFor(function () {
|
||||
return promiseResolved;
|
||||
}, "promise to resolve", 100);
|
||||
|
||||
runs(function () {
|
||||
expect(mockScope.visibleRows).not.toEqual(oldRows);
|
||||
});
|
||||
});
|
||||
|
||||
it('correctly sorts rows of differing types', function () {
|
||||
@@ -464,21 +482,10 @@ define(
|
||||
|
||||
mockScope.displayRows = controller.sortRows(testRows.slice(0));
|
||||
|
||||
mockScope.rows.push(row4);
|
||||
controller.addRow(undefined, mockScope.rows.length - 1);
|
||||
controller.addRows(undefined, [row4, row5, row6, row6]);
|
||||
expect(mockScope.displayRows[0].col2.text).toEqual('xyz');
|
||||
|
||||
mockScope.rows.push(row5);
|
||||
controller.addRow(undefined, mockScope.rows.length - 1);
|
||||
expect(mockScope.displayRows[4].col2.text).toEqual('aaa');
|
||||
|
||||
mockScope.rows.push(row6);
|
||||
controller.addRow(undefined, mockScope.rows.length - 1);
|
||||
expect(mockScope.displayRows[2].col2.text).toEqual('ggg');
|
||||
|
||||
//Add a duplicate row
|
||||
mockScope.rows.push(row6);
|
||||
controller.addRow(undefined, mockScope.rows.length - 1);
|
||||
expect(mockScope.displayRows[6].col2.text).toEqual('aaa');
|
||||
//Added a duplicate row
|
||||
expect(mockScope.displayRows[2].col2.text).toEqual('ggg');
|
||||
expect(mockScope.displayRows[3].col2.text).toEqual('ggg');
|
||||
});
|
||||
@@ -493,13 +500,11 @@ define(
|
||||
mockScope.displayRows = controller.sortRows(testRows.slice(0));
|
||||
mockScope.displayRows = controller.filterRows(testRows);
|
||||
|
||||
mockScope.rows.push(row5);
|
||||
controller.addRow(undefined, mockScope.rows.length - 1);
|
||||
controller.addRows(undefined, [row5]);
|
||||
expect(mockScope.displayRows.length).toBe(2);
|
||||
expect(mockScope.displayRows[1].col2.text).toEqual('aaa');
|
||||
|
||||
mockScope.rows.push(row6);
|
||||
controller.addRow(undefined, mockScope.rows.length - 1);
|
||||
controller.addRows(undefined, [row6]);
|
||||
expect(mockScope.displayRows.length).toBe(2);
|
||||
//Row was not added because does not match filter
|
||||
});
|
||||
@@ -512,12 +517,10 @@ define(
|
||||
|
||||
mockScope.displayRows = testRows.slice(0);
|
||||
|
||||
mockScope.rows.push(row5);
|
||||
controller.addRow(undefined, mockScope.rows.length - 1);
|
||||
controller.addRows(undefined, [row5]);
|
||||
expect(mockScope.displayRows[3].col2.text).toEqual('aaa');
|
||||
|
||||
mockScope.rows.push(row6);
|
||||
controller.addRow(undefined, mockScope.rows.length - 1);
|
||||
controller.addRows(undefined, [row6]);
|
||||
expect(mockScope.displayRows[4].col2.text).toEqual('ggg');
|
||||
});
|
||||
|
||||
@@ -535,8 +538,7 @@ define(
|
||||
|
||||
mockScope.displayRows = testRows.slice(0);
|
||||
|
||||
mockScope.rows.push(row7);
|
||||
controller.addRow(undefined, mockScope.rows.length - 1);
|
||||
controller.addRows(undefined, [row7]);
|
||||
expect(controller.$scope.sizingRow.col2).toEqual({text: 'some longer string'});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,171 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT 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 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(
|
||||
[
|
||||
"../../src/controllers/RealtimeTableController"
|
||||
],
|
||||
function (TableController) {
|
||||
|
||||
describe('The real-time table controller', function () {
|
||||
var mockScope,
|
||||
mockTelemetryHandler,
|
||||
mockTelemetryHandle,
|
||||
mockTelemetryFormatter,
|
||||
mockDomainObject,
|
||||
mockTable,
|
||||
mockConfiguration,
|
||||
watches,
|
||||
mockTableRow,
|
||||
mockConductor,
|
||||
controller;
|
||||
|
||||
function promise(value) {
|
||||
return {
|
||||
then: function (callback) {
|
||||
return promise(callback(value));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
watches = {};
|
||||
mockTableRow = {'col1': 'val1', 'col2': 'row2'};
|
||||
|
||||
mockScope = jasmine.createSpyObj('scope', [
|
||||
'$on',
|
||||
'$watch',
|
||||
'$watchCollection',
|
||||
'$digest',
|
||||
'$broadcast'
|
||||
]);
|
||||
mockScope.$on.andCallFake(function (expression, callback) {
|
||||
watches[expression] = callback;
|
||||
});
|
||||
mockScope.$watch.andCallFake(function (expression, callback) {
|
||||
watches[expression] = callback;
|
||||
});
|
||||
mockScope.$watchCollection.andCallFake(function (expression, callback) {
|
||||
watches[expression] = callback;
|
||||
});
|
||||
|
||||
mockConfiguration = {
|
||||
'range1': true,
|
||||
'range2': true,
|
||||
'domain1': true
|
||||
};
|
||||
|
||||
mockTable = jasmine.createSpyObj('table',
|
||||
[
|
||||
'populateColumns',
|
||||
'buildColumnConfiguration',
|
||||
'getRowValues',
|
||||
'saveColumnConfiguration'
|
||||
]
|
||||
);
|
||||
mockTable.columns = [];
|
||||
mockTable.buildColumnConfiguration.andReturn(mockConfiguration);
|
||||
mockTable.getRowValues.andReturn(mockTableRow);
|
||||
|
||||
mockDomainObject = jasmine.createSpyObj('domainObject', [
|
||||
'getCapability',
|
||||
'useCapability',
|
||||
'getModel'
|
||||
]);
|
||||
mockDomainObject.getModel.andReturn({});
|
||||
mockDomainObject.getCapability.andReturn(
|
||||
{
|
||||
getMetadata: function () {
|
||||
return {ranges: [{format: 'string'}]};
|
||||
}
|
||||
});
|
||||
|
||||
mockScope.domainObject = mockDomainObject;
|
||||
|
||||
mockTelemetryHandle = jasmine.createSpyObj('telemetryHandle', [
|
||||
'getMetadata',
|
||||
'unsubscribe',
|
||||
'getDatum',
|
||||
'promiseTelemetryObjects',
|
||||
'getTelemetryObjects',
|
||||
'request',
|
||||
'getMetadata'
|
||||
]);
|
||||
|
||||
// Arbitrary array with non-zero length, contents are not
|
||||
// used by mocks
|
||||
mockTelemetryHandle.getTelemetryObjects.andReturn([{}]);
|
||||
mockTelemetryHandle.promiseTelemetryObjects.andReturn(promise(undefined));
|
||||
mockTelemetryHandle.getDatum.andReturn({});
|
||||
mockTelemetryHandle.request.andReturn(promise(undefined));
|
||||
mockTelemetryHandle.getMetadata.andReturn([]);
|
||||
|
||||
mockTelemetryHandler = jasmine.createSpyObj('telemetryHandler', [
|
||||
'handle'
|
||||
]);
|
||||
mockTelemetryHandler.handle.andReturn(mockTelemetryHandle);
|
||||
|
||||
mockConductor = jasmine.createSpyObj('conductor', [
|
||||
'on',
|
||||
'off',
|
||||
'bounds',
|
||||
'timeSystem',
|
||||
'timeOfInterest'
|
||||
]);
|
||||
|
||||
controller = new TableController(mockScope, mockTelemetryHandler, mockTelemetryFormatter, {conductor: mockConductor});
|
||||
controller.table = mockTable;
|
||||
controller.handle = mockTelemetryHandle;
|
||||
});
|
||||
|
||||
it('registers for streaming telemetry', function () {
|
||||
controller.subscribe();
|
||||
expect(mockTelemetryHandler.handle).toHaveBeenCalledWith(jasmine.any(Object), jasmine.any(Function), true);
|
||||
});
|
||||
|
||||
describe('receives new telemetry', function () {
|
||||
beforeEach(function () {
|
||||
controller.subscribe();
|
||||
mockScope.rows = [];
|
||||
});
|
||||
|
||||
it('updates table with new streaming telemetry', function () {
|
||||
mockTelemetryHandler.handle.mostRecentCall.args[1]();
|
||||
expect(mockScope.$broadcast).toHaveBeenCalledWith('add:row', 0);
|
||||
});
|
||||
it('observes the row limit', function () {
|
||||
var i = 0;
|
||||
controller.maxRows = 10;
|
||||
|
||||
//Fill rows array with elements
|
||||
for (; i < 10; i++) {
|
||||
mockScope.rows.push({row: i});
|
||||
}
|
||||
mockTelemetryHandler.handle.mostRecentCall.args[1]();
|
||||
expect(mockScope.rows.length).toBe(controller.maxRows);
|
||||
expect(mockScope.rows[mockScope.rows.length - 1]).toBe(mockTableRow);
|
||||
expect(mockScope.rows[0].row).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,364 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT 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 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(
|
||||
[
|
||||
'../../src/controllers/TelemetryTableController',
|
||||
'../../../../../src/api/objects/object-utils',
|
||||
'lodash'
|
||||
],
|
||||
function (TelemetryTableController, objectUtils, _) {
|
||||
|
||||
describe('The TelemetryTableController', function () {
|
||||
|
||||
var controller,
|
||||
mockScope,
|
||||
mockTimeout,
|
||||
mockConductor,
|
||||
mockAPI,
|
||||
mockDomainObject,
|
||||
mockTelemetryAPI,
|
||||
mockObjectAPI,
|
||||
mockCompositionAPI,
|
||||
unobserve,
|
||||
mockBounds;
|
||||
|
||||
function getCallback(target, event) {
|
||||
return target.calls.filter(function (call) {
|
||||
return call.args[0] === event;
|
||||
})[0].args[1];
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockBounds = {
|
||||
start: 0,
|
||||
end: 10
|
||||
};
|
||||
mockConductor = jasmine.createSpyObj("conductor", [
|
||||
"bounds",
|
||||
"follow",
|
||||
"on",
|
||||
"off",
|
||||
"timeSystem"
|
||||
]);
|
||||
mockConductor.bounds.andReturn(mockBounds);
|
||||
mockConductor.follow.andReturn(false);
|
||||
|
||||
mockDomainObject = jasmine.createSpyObj("domainObject", [
|
||||
"getModel",
|
||||
"getId",
|
||||
"useCapability"
|
||||
]);
|
||||
mockDomainObject.getModel.andReturn({});
|
||||
mockDomainObject.getId.andReturn("mockId");
|
||||
mockDomainObject.useCapability.andReturn(true);
|
||||
|
||||
mockCompositionAPI = jasmine.createSpyObj("compositionAPI", [
|
||||
"get"
|
||||
]);
|
||||
|
||||
mockObjectAPI = jasmine.createSpyObj("objectAPI", [
|
||||
"observe"
|
||||
]);
|
||||
unobserve = jasmine.createSpy("unobserve");
|
||||
mockObjectAPI.observe.andReturn(unobserve);
|
||||
|
||||
mockScope = jasmine.createSpyObj("scope", [
|
||||
"$on",
|
||||
"$watch",
|
||||
"$broadcast"
|
||||
]);
|
||||
mockScope.domainObject = mockDomainObject;
|
||||
|
||||
mockTelemetryAPI = jasmine.createSpyObj("telemetryAPI", [
|
||||
"canProvideTelemetry",
|
||||
"subscribe",
|
||||
"getMetadata",
|
||||
"commonValuesForHints",
|
||||
"request",
|
||||
"limitEvaluator",
|
||||
"getValueFormatter"
|
||||
]);
|
||||
mockTelemetryAPI.commonValuesForHints.andReturn([]);
|
||||
mockTelemetryAPI.request.andReturn(Promise.resolve([]));
|
||||
|
||||
|
||||
mockTelemetryAPI.canProvideTelemetry.andReturn(false);
|
||||
|
||||
mockTimeout = jasmine.createSpy("timeout");
|
||||
mockTimeout.andReturn(1); // Return something
|
||||
mockTimeout.cancel = jasmine.createSpy("cancel");
|
||||
|
||||
mockAPI = {
|
||||
conductor: mockConductor,
|
||||
objects: mockObjectAPI,
|
||||
telemetry: mockTelemetryAPI,
|
||||
composition: mockCompositionAPI
|
||||
};
|
||||
controller = new TelemetryTableController(mockScope, mockTimeout, mockAPI);
|
||||
});
|
||||
|
||||
describe('listens for', function () {
|
||||
beforeEach(function () {
|
||||
controller.registerChangeListeners();
|
||||
});
|
||||
it('object mutation', function () {
|
||||
var calledObject = mockObjectAPI.observe.mostRecentCall.args[0];
|
||||
|
||||
expect(mockObjectAPI.observe).toHaveBeenCalled();
|
||||
expect(calledObject.identifier.key).toEqual(mockDomainObject.getId());
|
||||
});
|
||||
it('conductor changes', function () {
|
||||
expect(mockConductor.on).toHaveBeenCalledWith("timeSystem", jasmine.any(Function));
|
||||
expect(mockConductor.on).toHaveBeenCalledWith("bounds", jasmine.any(Function));
|
||||
expect(mockConductor.on).toHaveBeenCalledWith("follow", jasmine.any(Function));
|
||||
});
|
||||
});
|
||||
|
||||
describe('deregisters all listeners on scope destruction', function () {
|
||||
var timeSystemListener,
|
||||
boundsListener,
|
||||
followListener;
|
||||
|
||||
beforeEach(function () {
|
||||
controller.registerChangeListeners();
|
||||
|
||||
timeSystemListener = getCallback(mockConductor.on, "timeSystem");
|
||||
boundsListener = getCallback(mockConductor.on, "bounds");
|
||||
followListener = getCallback(mockConductor.on, "follow");
|
||||
|
||||
var destroy = getCallback(mockScope.$on, "$destroy");
|
||||
destroy();
|
||||
});
|
||||
|
||||
it('object mutation', function () {
|
||||
expect(unobserve).toHaveBeenCalled();
|
||||
});
|
||||
it('conductor changes', function () {
|
||||
expect(mockConductor.off).toHaveBeenCalledWith("timeSystem", timeSystemListener);
|
||||
expect(mockConductor.off).toHaveBeenCalledWith("bounds", boundsListener);
|
||||
expect(mockConductor.off).toHaveBeenCalledWith("follow", followListener);
|
||||
});
|
||||
});
|
||||
|
||||
describe ('Subscribes to new data', function () {
|
||||
var mockComposition,
|
||||
mockTelemetryObject,
|
||||
mockChildren,
|
||||
unsubscribe,
|
||||
done;
|
||||
|
||||
beforeEach(function () {
|
||||
mockComposition = jasmine.createSpyObj("composition", [
|
||||
"load"
|
||||
]);
|
||||
|
||||
mockTelemetryObject = jasmine.createSpyObj("mockTelemetryObject", [
|
||||
"something"
|
||||
]);
|
||||
mockTelemetryObject.identifier = {
|
||||
key: "mockTelemetryObject"
|
||||
};
|
||||
|
||||
unsubscribe = jasmine.createSpy("unsubscribe");
|
||||
mockTelemetryAPI.subscribe.andReturn(unsubscribe);
|
||||
|
||||
mockChildren = [mockTelemetryObject];
|
||||
mockComposition.load.andReturn(Promise.resolve(mockChildren));
|
||||
mockCompositionAPI.get.andReturn(mockComposition);
|
||||
|
||||
mockTelemetryAPI.canProvideTelemetry.andCallFake(function (obj) {
|
||||
return obj.identifier.key === mockTelemetryObject.identifier.key;
|
||||
});
|
||||
|
||||
done = false;
|
||||
controller.getData().then(function () {
|
||||
done = true;
|
||||
});
|
||||
});
|
||||
|
||||
it('fetches historical data', function () {
|
||||
waitsFor(function () {
|
||||
return done;
|
||||
}, "getData to return", 100);
|
||||
|
||||
runs(function () {
|
||||
expect(mockTelemetryAPI.request).toHaveBeenCalledWith(mockTelemetryObject, jasmine.any(Object));
|
||||
});
|
||||
});
|
||||
|
||||
it('fetches historical data for the time period specified by the conductor bounds', function () {
|
||||
waitsFor(function () {
|
||||
return done;
|
||||
}, "getData to return", 100);
|
||||
|
||||
runs(function () {
|
||||
expect(mockTelemetryAPI.request).toHaveBeenCalledWith(mockTelemetryObject, mockBounds);
|
||||
});
|
||||
});
|
||||
|
||||
it('subscribes to new data', function () {
|
||||
waitsFor(function () {
|
||||
return done;
|
||||
}, "getData to return", 100);
|
||||
|
||||
runs(function () {
|
||||
expect(mockTelemetryAPI.subscribe).toHaveBeenCalledWith(mockTelemetryObject, jasmine.any(Function), {});
|
||||
});
|
||||
|
||||
});
|
||||
it('and unsubscribes on view destruction', function () {
|
||||
waitsFor(function () {
|
||||
return done;
|
||||
}, "getData to return", 100);
|
||||
|
||||
runs(function () {
|
||||
var destroy = getCallback(mockScope.$on, "$destroy");
|
||||
destroy();
|
||||
|
||||
expect(unsubscribe).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('When in real-time mode, enables auto-scroll', function () {
|
||||
controller.registerChangeListeners();
|
||||
|
||||
var followCallback = getCallback(mockConductor.on, "follow");
|
||||
//Confirm pre-condition
|
||||
expect(mockScope.autoScroll).toBeFalsy();
|
||||
|
||||
//Mock setting the conductor to 'follow' mode
|
||||
followCallback(true);
|
||||
expect(mockScope.autoScroll).toBe(true);
|
||||
});
|
||||
|
||||
describe('populates table columns', function () {
|
||||
var domainMetadata;
|
||||
var allMetadata;
|
||||
var mockTimeSystem;
|
||||
|
||||
beforeEach(function () {
|
||||
domainMetadata = [{
|
||||
key: "column1",
|
||||
name: "Column 1",
|
||||
hints: {}
|
||||
}];
|
||||
|
||||
allMetadata = [{
|
||||
key: "column1",
|
||||
name: "Column 1",
|
||||
hints: {}
|
||||
}, {
|
||||
key: "column2",
|
||||
name: "Column 2",
|
||||
hints: {}
|
||||
}, {
|
||||
key: "column3",
|
||||
name: "Column 3",
|
||||
hints: {}
|
||||
}];
|
||||
|
||||
mockTimeSystem = {
|
||||
metadata: {
|
||||
key: "column1"
|
||||
}
|
||||
};
|
||||
|
||||
mockTelemetryAPI.commonValuesForHints.andCallFake(function (metadata, hints) {
|
||||
if (_.eq(hints, ["x"])) {
|
||||
return domainMetadata;
|
||||
} else if (_.eq(hints, [])) {
|
||||
return allMetadata;
|
||||
}
|
||||
});
|
||||
|
||||
controller.loadColumns([mockDomainObject]);
|
||||
});
|
||||
|
||||
it('based on metadata for given objects', function () {
|
||||
expect(mockScope.headers).toBeDefined();
|
||||
expect(mockScope.headers.length).toBeGreaterThan(0);
|
||||
expect(mockScope.headers.indexOf(allMetadata[0].name)).not.toBe(-1);
|
||||
expect(mockScope.headers.indexOf(allMetadata[1].name)).not.toBe(-1);
|
||||
expect(mockScope.headers.indexOf(allMetadata[2].name)).not.toBe(-1);
|
||||
});
|
||||
|
||||
it('and sorts by column matching time system', function () {
|
||||
expect(mockScope.defaultSort).not.toEqual("Column 1");
|
||||
controller.sortByTimeSystem(mockTimeSystem);
|
||||
expect(mockScope.defaultSort).toEqual("Column 1");
|
||||
});
|
||||
|
||||
it('batches processing of rows for performance when receiving historical telemetry', function () {
|
||||
var mockHistoricalData = [
|
||||
{
|
||||
"column1": 1,
|
||||
"column2": 2,
|
||||
"column3": 3
|
||||
},{
|
||||
"column1": 4,
|
||||
"column2": 5,
|
||||
"column3": 6
|
||||
}, {
|
||||
"column1": 7,
|
||||
"column2": 8,
|
||||
"column3": 9
|
||||
}
|
||||
];
|
||||
controller.batchSize = 2;
|
||||
mockTelemetryAPI.request.andReturn(Promise.resolve(mockHistoricalData));
|
||||
controller.getHistoricalData([mockDomainObject]);
|
||||
|
||||
waitsFor(function () {
|
||||
return !!controller.timeoutHandle;
|
||||
}, "first batch to be processed", 100);
|
||||
|
||||
runs(function () {
|
||||
//Verify that timeout is being used to yield process
|
||||
expect(mockTimeout).toHaveBeenCalled();
|
||||
mockTimeout.mostRecentCall.args[0]();
|
||||
expect(mockTimeout.calls.length).toBe(2);
|
||||
mockTimeout.mostRecentCall.args[0]();
|
||||
expect(mockScope.rows.length).toBe(3);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Removes telemetry rows from table when they fall out of bounds', function () {
|
||||
var discardedRows = [
|
||||
{"column1": "value 1"},
|
||||
{"column2": "value 2"},
|
||||
{"column3": "value 3"}
|
||||
];
|
||||
|
||||
spyOn(controller.telemetry, "on").andCallThrough();
|
||||
|
||||
controller.registerChangeListeners();
|
||||
expect(controller.telemetry.on).toHaveBeenCalledWith("discarded", jasmine.any(Function));
|
||||
var onDiscard = getCallback(controller.telemetry.on, "discarded");
|
||||
onDiscard(discardedRows);
|
||||
expect(mockScope.$broadcast).toHaveBeenCalledWith("remove:rows", discardedRows);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user