mirror of
https://github.com/nasa/openmct.git
synced 2025-03-20 02:55:18 +00:00
[Remote Clock] Wait for first tick and recalculate historical request bounds (#5433)
* Updated to ES6 class * added request intercept functionality to telemetry api, added a request interceptor for remote clock * add remoteClock e2e test stub Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov> Co-authored-by: Andrew Henry <akhenry@gmail.com>
This commit is contained in:
parent
a09db30b32
commit
063df721ae
41
e2e/tests/plugins/remoteClock/remoteClock.e2e.spec.js
Normal file
41
e2e/tests/plugins/remoteClock/remoteClock.e2e.spec.js
Normal file
@ -0,0 +1,41 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2022, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
const { test } = require('../../../fixtures.js');
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const { expect } = require('@playwright/test');
|
||||
|
||||
test.describe('Remote Clock', () => {
|
||||
// eslint-disable-next-line require-await
|
||||
test.fixme('blocks historical requests until first tick is received', async ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/5221'
|
||||
});
|
||||
// addInitScript to with remote clock
|
||||
// Switch time conductor mode to 'remote clock'
|
||||
// Navigate to telemetry
|
||||
// Verify that the plot renders historical data within the correct bounds
|
||||
// Refresh the page
|
||||
// Verify again that the plot renders historical data within the correct bounds
|
||||
});
|
||||
});
|
@ -24,7 +24,7 @@ const { test } = require('../../../fixtures');
|
||||
const { expect } = require('@playwright/test');
|
||||
|
||||
test.describe('Telemetry Table', () => {
|
||||
test('unpauses when paused by button and user changes bounds', async ({ page }) => {
|
||||
test('unpauses and filters data when paused by button and user changes bounds', async ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/5113'
|
||||
@ -71,25 +71,34 @@ test.describe('Telemetry Table', () => {
|
||||
]);
|
||||
|
||||
// Click pause button
|
||||
const pauseButton = await page.locator('button.c-button.icon-pause');
|
||||
const pauseButton = page.locator('button.c-button.icon-pause');
|
||||
await pauseButton.click();
|
||||
|
||||
const tableWrapper = await page.locator('div.c-table-wrapper');
|
||||
const tableWrapper = page.locator('div.c-table-wrapper');
|
||||
await expect(tableWrapper).toHaveClass(/is-paused/);
|
||||
|
||||
// Arbitrarily change end date to some time in the future
|
||||
// Subtract 5 minutes from the current end bound datetime and set it
|
||||
const endTimeInput = page.locator('input[type="text"].c-input--datetime').nth(1);
|
||||
await endTimeInput.click();
|
||||
|
||||
let endDate = await endTimeInput.inputValue();
|
||||
endDate = new Date(endDate);
|
||||
endDate.setUTCDate(endDate.getUTCDate() + 1);
|
||||
endDate = endDate.toISOString().replace(/T.*/, '');
|
||||
|
||||
endDate.setUTCMinutes(endDate.getUTCMinutes() - 5);
|
||||
endDate = endDate.toISOString().replace(/T/, ' ');
|
||||
|
||||
await endTimeInput.fill('');
|
||||
await endTimeInput.fill(endDate);
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
await expect(tableWrapper).not.toHaveClass(/is-paused/);
|
||||
|
||||
// Get the most recent telemetry date
|
||||
const latestTelemetryDate = await page.locator('table.c-telemetry-table__body > tbody > tr').last().locator('td').nth(1).getAttribute('title');
|
||||
|
||||
// Verify that it is <= our new end bound
|
||||
const latestMilliseconds = Date.parse(latestTelemetryDate);
|
||||
const endBoundMilliseconds = Date.parse(endDate);
|
||||
expect(latestMilliseconds).toBeLessThanOrEqual(endBoundMilliseconds);
|
||||
});
|
||||
});
|
||||
|
@ -203,7 +203,7 @@ define([
|
||||
* @memberof module:openmct.MCT#
|
||||
* @name telemetry
|
||||
*/
|
||||
this.telemetry = new api.TelemetryAPI(this);
|
||||
this.telemetry = new api.TelemetryAPI.default(this);
|
||||
|
||||
/**
|
||||
* An interface for creating new indicators and changing them dynamically.
|
||||
|
@ -20,122 +20,18 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
const { TelemetryCollection } = require("./TelemetryCollection");
|
||||
import TelemetryCollection from './TelemetryCollection';
|
||||
import TelemetryRequestInterceptorRegistry from './TelemetryRequestInterceptor';
|
||||
import CustomStringFormatter from '../../plugins/displayLayout/CustomStringFormatter';
|
||||
import TelemetryMetadataManager from './TelemetryMetadataManager';
|
||||
import TelemetryValueFormatter from './TelemetryValueFormatter';
|
||||
import DefaultMetadataProvider from './DefaultMetadataProvider';
|
||||
import objectUtils from 'objectUtils';
|
||||
import _ from 'lodash';
|
||||
|
||||
define([
|
||||
'../../plugins/displayLayout/CustomStringFormatter',
|
||||
'./TelemetryMetadataManager',
|
||||
'./TelemetryValueFormatter',
|
||||
'./DefaultMetadataProvider',
|
||||
'objectUtils',
|
||||
'lodash'
|
||||
], function (
|
||||
CustomStringFormatter,
|
||||
TelemetryMetadataManager,
|
||||
TelemetryValueFormatter,
|
||||
DefaultMetadataProvider,
|
||||
objectUtils,
|
||||
_
|
||||
) {
|
||||
/**
|
||||
* A LimitEvaluator may be used to detect when telemetry values
|
||||
* have exceeded nominal conditions.
|
||||
*
|
||||
* @interface LimitEvaluator
|
||||
* @memberof module:openmct.TelemetryAPI~
|
||||
*/
|
||||
export default class TelemetryAPI {
|
||||
|
||||
/**
|
||||
* Check for any limit violations associated with a telemetry datum.
|
||||
* @method evaluate
|
||||
* @param {*} datum the telemetry datum to evaluate
|
||||
* @param {TelemetryProperty} the property to check for limit violations
|
||||
* @memberof module:openmct.TelemetryAPI~LimitEvaluator
|
||||
* @returns {module:openmct.TelemetryAPI~LimitViolation} metadata about
|
||||
* the limit violation, or undefined if a value is within limits
|
||||
*/
|
||||
|
||||
/**
|
||||
* A violation of limits defined for a telemetry property.
|
||||
* @typedef LimitViolation
|
||||
* @memberof {module:openmct.TelemetryAPI~}
|
||||
* @property {string} cssClass the class (or space-separated classes) to
|
||||
* apply to display elements for values which violate this limit
|
||||
* @property {string} name the human-readable name for the limit violation
|
||||
*/
|
||||
|
||||
/**
|
||||
* A TelemetryFormatter converts telemetry values for purposes of
|
||||
* display as text.
|
||||
*
|
||||
* @interface TelemetryFormatter
|
||||
* @memberof module:openmct.TelemetryAPI~
|
||||
*/
|
||||
|
||||
/**
|
||||
* Retrieve the 'key' from the datum and format it accordingly to
|
||||
* telemetry metadata in domain object.
|
||||
*
|
||||
* @method format
|
||||
* @memberof module:openmct.TelemetryAPI~TelemetryFormatter#
|
||||
*/
|
||||
|
||||
/**
|
||||
* Describes a property which would be found in a datum of telemetry
|
||||
* associated with a particular domain object.
|
||||
*
|
||||
* @typedef TelemetryProperty
|
||||
* @memberof module:openmct.TelemetryAPI~
|
||||
* @property {string} key the name of the property in the datum which
|
||||
* contains this telemetry value
|
||||
* @property {string} name the human-readable name for this property
|
||||
* @property {string} [units] the units associated with this property
|
||||
* @property {boolean} [temporal] true if this property is a timestamp, or
|
||||
* may be otherwise used to order telemetry in a time-like
|
||||
* fashion; default is false
|
||||
* @property {boolean} [numeric] true if the values for this property
|
||||
* can be interpreted plainly as numbers; default is true
|
||||
* @property {boolean} [enumerated] true if this property may have only
|
||||
* certain specific values; default is false
|
||||
* @property {string} [values] for enumerated states, an ordered list
|
||||
* of possible values
|
||||
*/
|
||||
|
||||
/**
|
||||
* Describes and bounds requests for telemetry data.
|
||||
*
|
||||
* @typedef TelemetryRequest
|
||||
* @memberof module:openmct.TelemetryAPI~
|
||||
* @property {string} sort the key of the property to sort by. This may
|
||||
* be prefixed with a "+" or a "-" sign to sort in ascending
|
||||
* or descending order respectively. If no prefix is present,
|
||||
* ascending order will be used.
|
||||
* @property {*} start the lower bound for values of the sorting property
|
||||
* @property {*} end the upper bound for values of the sorting property
|
||||
* @property {string[]} strategies symbolic identifiers for strategies
|
||||
* (such as `minmax`) which may be recognized by providers;
|
||||
* these will be tried in order until an appropriate provider
|
||||
* is found
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides telemetry data. To connect to new data sources, new
|
||||
* TelemetryProvider implementations should be
|
||||
* [registered]{@link module:openmct.TelemetryAPI#addProvider}.
|
||||
*
|
||||
* @interface TelemetryProvider
|
||||
* @memberof module:openmct.TelemetryAPI~
|
||||
*/
|
||||
|
||||
/**
|
||||
* An interface for retrieving telemetry data associated with a domain
|
||||
* object.
|
||||
*
|
||||
* @interface TelemetryAPI
|
||||
* @augments module:openmct.TelemetryAPI~TelemetryProvider
|
||||
* @memberof module:openmct
|
||||
*/
|
||||
function TelemetryAPI(openmct) {
|
||||
constructor(openmct) {
|
||||
this.openmct = openmct;
|
||||
|
||||
this.formatMapCache = new WeakMap();
|
||||
@ -148,12 +44,14 @@ define([
|
||||
this.requestProviders = [];
|
||||
this.subscriptionProviders = [];
|
||||
this.valueFormatterCache = new WeakMap();
|
||||
|
||||
this.requestInterceptorRegistry = new TelemetryRequestInterceptorRegistry();
|
||||
}
|
||||
|
||||
TelemetryAPI.prototype.abortAllRequests = function () {
|
||||
abortAllRequests() {
|
||||
this.requestAbortControllers.forEach((controller) => controller.abort());
|
||||
this.requestAbortControllers.clear();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return Custom String Formatter
|
||||
@ -162,9 +60,9 @@ define([
|
||||
* @param {string} format custom formatter string (eg: %.4f, <s etc.)
|
||||
* @returns {CustomStringFormatter}
|
||||
*/
|
||||
TelemetryAPI.prototype.customStringFormatter = function (valueMetadata, format) {
|
||||
customStringFormatter(valueMetadata, format) {
|
||||
return new CustomStringFormatter.default(this.openmct, valueMetadata, format);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given domainObject is a telemetry object. A telemetry
|
||||
@ -174,9 +72,9 @@ define([
|
||||
* @param {module:openmct.DomainObject} domainObject
|
||||
* @returns {boolean} true if the object is a telemetry object.
|
||||
*/
|
||||
TelemetryAPI.prototype.isTelemetryObject = function (domainObject) {
|
||||
isTelemetryObject(domainObject) {
|
||||
return Boolean(this.findMetadataProvider(domainObject));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this provider can supply telemetry data associated with
|
||||
@ -188,10 +86,10 @@ define([
|
||||
* @returns {boolean} true if telemetry can be provided
|
||||
* @memberof module:openmct.TelemetryAPI~TelemetryProvider#
|
||||
*/
|
||||
TelemetryAPI.prototype.canProvideTelemetry = function (domainObject) {
|
||||
canProvideTelemetry(domainObject) {
|
||||
return Boolean(this.findSubscriptionProvider(domainObject))
|
||||
|| Boolean(this.findRequestProvider(domainObject));
|
||||
};
|
||||
|| Boolean(this.findRequestProvider(domainObject));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a telemetry provider with the telemetry service. This
|
||||
@ -201,7 +99,7 @@ define([
|
||||
* @param {module:openmct.TelemetryAPI~TelemetryProvider} provider the new
|
||||
* telemetry provider
|
||||
*/
|
||||
TelemetryAPI.prototype.addProvider = function (provider) {
|
||||
addProvider(provider) {
|
||||
if (provider.supportsRequest) {
|
||||
this.requestProviders.unshift(provider);
|
||||
}
|
||||
@ -217,54 +115,54 @@ define([
|
||||
if (provider.supportsLimits) {
|
||||
this.limitProviders.unshift(provider);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
TelemetryAPI.prototype.findSubscriptionProvider = function () {
|
||||
findSubscriptionProvider() {
|
||||
const args = Array.prototype.slice.apply(arguments);
|
||||
function supportsDomainObject(provider) {
|
||||
return provider.supportsSubscribe.apply(provider, args);
|
||||
}
|
||||
|
||||
return this.subscriptionProviders.filter(supportsDomainObject)[0];
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
TelemetryAPI.prototype.findRequestProvider = function (domainObject) {
|
||||
findRequestProvider(domainObject) {
|
||||
const args = Array.prototype.slice.apply(arguments);
|
||||
function supportsDomainObject(provider) {
|
||||
return provider.supportsRequest.apply(provider, args);
|
||||
}
|
||||
|
||||
return this.requestProviders.filter(supportsDomainObject)[0];
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
TelemetryAPI.prototype.findMetadataProvider = function (domainObject) {
|
||||
findMetadataProvider(domainObject) {
|
||||
return this.metadataProviders.filter(function (p) {
|
||||
return p.supportsMetadata(domainObject);
|
||||
})[0];
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
TelemetryAPI.prototype.findLimitEvaluator = function (domainObject) {
|
||||
findLimitEvaluator(domainObject) {
|
||||
return this.limitProviders.filter(function (p) {
|
||||
return p.supportsLimits(domainObject);
|
||||
})[0];
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
TelemetryAPI.prototype.standardizeRequestOptions = function (options) {
|
||||
standardizeRequestOptions(options) {
|
||||
if (!Object.prototype.hasOwnProperty.call(options, 'start')) {
|
||||
options.start = this.openmct.time.bounds().start;
|
||||
}
|
||||
@ -276,7 +174,47 @@ define([
|
||||
if (!Object.prototype.hasOwnProperty.call(options, 'domain')) {
|
||||
options.domain = this.openmct.time.timeSystem().key;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a request interceptor that transforms a request via module:openmct.TelemetryAPI.request
|
||||
* The request will be modifyed when it is received and will be returned in it's modified state
|
||||
* The request will be transformed only if the interceptor is applicable to that domain object as defined by the RequestInterceptorDef
|
||||
*
|
||||
* @param {module:openmct.RequestInterceptorDef} requestInterceptorDef the request interceptor definition to add
|
||||
* @method addRequestInterceptor
|
||||
* @memberof module:openmct.TelemetryRequestInterceptorRegistry#
|
||||
*/
|
||||
addRequestInterceptor(requestInterceptorDef) {
|
||||
this.requestInterceptorRegistry.addInterceptor(requestInterceptorDef);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the request interceptors for a given domain object.
|
||||
* @private
|
||||
*/
|
||||
#getInterceptorsForRequest(identifier, request) {
|
||||
return this.requestInterceptorRegistry.getInterceptors(identifier, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke interceptors if applicable for a given domain object.
|
||||
*/
|
||||
async applyRequestInterceptors(domainObject, request) {
|
||||
const interceptors = this.#getInterceptorsForRequest(domainObject.identifier, request);
|
||||
|
||||
if (interceptors.length === 0) {
|
||||
return request;
|
||||
}
|
||||
|
||||
let modifiedRequest = { ...request };
|
||||
|
||||
for (let interceptor of interceptors) {
|
||||
modifiedRequest = await interceptor.invoke(modifiedRequest);
|
||||
}
|
||||
|
||||
return modifiedRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request telemetry collection for a domain object.
|
||||
@ -292,13 +230,13 @@ define([
|
||||
* options for this telemetry collection request
|
||||
* @returns {TelemetryCollection} a TelemetryCollection instance
|
||||
*/
|
||||
TelemetryAPI.prototype.requestCollection = function (domainObject, options = {}) {
|
||||
requestCollection(domainObject, options = {}) {
|
||||
return new TelemetryCollection(
|
||||
this.openmct,
|
||||
domainObject,
|
||||
options
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Request historical telemetry for a domain object.
|
||||
@ -315,7 +253,7 @@ define([
|
||||
* @returns {Promise.<object[]>} a promise for an array of
|
||||
* telemetry data
|
||||
*/
|
||||
TelemetryAPI.prototype.request = function (domainObject) {
|
||||
async request(domainObject) {
|
||||
if (this.noRequestProviderForAllObjects) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
@ -330,6 +268,7 @@ define([
|
||||
this.requestAbortControllers.add(abortController);
|
||||
|
||||
this.standardizeRequestOptions(arguments[1]);
|
||||
|
||||
const provider = this.findRequestProvider.apply(this, arguments);
|
||||
if (!provider) {
|
||||
this.requestAbortControllers.delete(abortController);
|
||||
@ -337,6 +276,8 @@ define([
|
||||
return this.handleMissingRequestProvider(domainObject);
|
||||
}
|
||||
|
||||
arguments[1] = await this.applyRequestInterceptors(domainObject, arguments[1]);
|
||||
|
||||
return provider.request.apply(provider, arguments)
|
||||
.catch((rejected) => {
|
||||
if (rejected.name !== 'AbortError') {
|
||||
@ -348,7 +289,7 @@ define([
|
||||
}).finally(() => {
|
||||
this.requestAbortControllers.delete(abortController);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to realtime telemetry for a specific domain object.
|
||||
@ -364,7 +305,7 @@ define([
|
||||
* @returns {Function} a function which may be called to terminate
|
||||
* the subscription
|
||||
*/
|
||||
TelemetryAPI.prototype.subscribe = function (domainObject, callback, options) {
|
||||
subscribe(domainObject, callback, options) {
|
||||
const provider = this.findSubscriptionProvider(domainObject);
|
||||
|
||||
if (!this.subscribeCache) {
|
||||
@ -401,7 +342,7 @@ define([
|
||||
delete this.subscribeCache[keyString];
|
||||
}
|
||||
}.bind(this);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get telemetry metadata for a given domain object. Returns a telemetry
|
||||
@ -410,7 +351,7 @@ define([
|
||||
*
|
||||
* @returns {TelemetryMetadataManager}
|
||||
*/
|
||||
TelemetryAPI.prototype.getMetadata = function (domainObject) {
|
||||
getMetadata(domainObject) {
|
||||
if (!this.metadataCache.has(domainObject)) {
|
||||
const metadataProvider = this.findMetadataProvider(domainObject);
|
||||
if (!metadataProvider) {
|
||||
@ -426,14 +367,14 @@ define([
|
||||
}
|
||||
|
||||
return this.metadataCache.get(domainObject);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of valueMetadatas that are common to all supplied
|
||||
* telemetry objects and match the requested hints.
|
||||
*
|
||||
*/
|
||||
TelemetryAPI.prototype.commonValuesForHints = function (metadatas, hints) {
|
||||
commonValuesForHints(metadatas, hints) {
|
||||
const options = metadatas.map(function (metadata) {
|
||||
const values = metadata.valuesForHints(hints);
|
||||
|
||||
@ -453,14 +394,14 @@ define([
|
||||
});
|
||||
|
||||
return _.sortBy(options, sortKeys);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a value formatter for a given valueMetadata.
|
||||
*
|
||||
* @returns {TelemetryValueFormatter}
|
||||
*/
|
||||
TelemetryAPI.prototype.getValueFormatter = function (valueMetadata) {
|
||||
getValueFormatter(valueMetadata) {
|
||||
if (!this.valueFormatterCache.has(valueMetadata)) {
|
||||
this.valueFormatterCache.set(
|
||||
valueMetadata,
|
||||
@ -469,7 +410,7 @@ define([
|
||||
}
|
||||
|
||||
return this.valueFormatterCache.get(valueMetadata);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a value formatter for a given key.
|
||||
@ -477,9 +418,9 @@ define([
|
||||
*
|
||||
* @returns {Format}
|
||||
*/
|
||||
TelemetryAPI.prototype.getFormatter = function (key) {
|
||||
getFormatter(key) {
|
||||
return this.formatters.get(key);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a format map of all value formatters for a given piece of telemetry
|
||||
@ -487,7 +428,7 @@ define([
|
||||
*
|
||||
* @returns {Object<String, {TelemetryValueFormatter}>}
|
||||
*/
|
||||
TelemetryAPI.prototype.getFormatMap = function (metadata) {
|
||||
getFormatMap(metadata) {
|
||||
if (!metadata) {
|
||||
return {};
|
||||
}
|
||||
@ -502,14 +443,14 @@ define([
|
||||
}
|
||||
|
||||
return this.formatMapCache.get(metadata);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Error Handling: Missing Request provider
|
||||
*
|
||||
* @returns Promise
|
||||
*/
|
||||
TelemetryAPI.prototype.handleMissingRequestProvider = function (domainObject) {
|
||||
handleMissingRequestProvider(domainObject) {
|
||||
this.noRequestProviderForAllObjects = this.requestProviders.every(requestProvider => {
|
||||
const supportsRequest = requestProvider.supportsRequest.apply(requestProvider, arguments);
|
||||
const hasRequestProvider = Object.prototype.hasOwnProperty.call(requestProvider, 'request') && typeof requestProvider.request === 'function';
|
||||
@ -532,15 +473,15 @@ define([
|
||||
console.warn(detailMessage);
|
||||
|
||||
return Promise.resolve([]);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new telemetry data formatter.
|
||||
* @param {Format} format the
|
||||
*/
|
||||
TelemetryAPI.prototype.addFormat = function (format) {
|
||||
addFormat(format) {
|
||||
this.formatters.set(format.key, format);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a limit evaluator for this domain object.
|
||||
@ -558,9 +499,9 @@ define([
|
||||
* @method limitEvaluator
|
||||
* @memberof module:openmct.TelemetryAPI~TelemetryProvider#
|
||||
*/
|
||||
TelemetryAPI.prototype.limitEvaluator = function (domainObject) {
|
||||
limitEvaluator(domainObject) {
|
||||
return this.getLimitEvaluator(domainObject);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a limits for this domain object.
|
||||
@ -578,9 +519,9 @@ define([
|
||||
* @method limits
|
||||
* @memberof module:openmct.TelemetryAPI~TelemetryProvider#
|
||||
*/
|
||||
TelemetryAPI.prototype.limitDefinition = function (domainObject) {
|
||||
limitDefinition(domainObject) {
|
||||
return this.getLimits(domainObject);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a limit evaluator for this domain object.
|
||||
@ -598,7 +539,7 @@ define([
|
||||
* @method limitEvaluator
|
||||
* @memberof module:openmct.TelemetryAPI~TelemetryProvider#
|
||||
*/
|
||||
TelemetryAPI.prototype.getLimitEvaluator = function (domainObject) {
|
||||
getLimitEvaluator(domainObject) {
|
||||
const provider = this.findLimitEvaluator(domainObject);
|
||||
if (!provider) {
|
||||
return {
|
||||
@ -607,7 +548,7 @@ define([
|
||||
}
|
||||
|
||||
return provider.getLimitEvaluator(domainObject);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a limit definitions for this domain object.
|
||||
@ -636,7 +577,7 @@ define([
|
||||
* supported colors are purple, red, orange, yellow and cyan
|
||||
* @memberof module:openmct.TelemetryAPI~TelemetryProvider#
|
||||
*/
|
||||
TelemetryAPI.prototype.getLimits = function (domainObject) {
|
||||
getLimits(domainObject) {
|
||||
const provider = this.findLimitEvaluator(domainObject);
|
||||
if (!provider || !provider.getLimits) {
|
||||
return {
|
||||
@ -647,7 +588,104 @@ define([
|
||||
}
|
||||
|
||||
return provider.getLimits(domainObject);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return TelemetryAPI;
|
||||
});
|
||||
/**
|
||||
* A LimitEvaluator may be used to detect when telemetry values
|
||||
* have exceeded nominal conditions.
|
||||
*
|
||||
* @interface LimitEvaluator
|
||||
* @memberof module:openmct.TelemetryAPI~
|
||||
*/
|
||||
|
||||
/**
|
||||
* Check for any limit violations associated with a telemetry datum.
|
||||
* @method evaluate
|
||||
* @param {*} datum the telemetry datum to evaluate
|
||||
* @param {TelemetryProperty} the property to check for limit violations
|
||||
* @memberof module:openmct.TelemetryAPI~LimitEvaluator
|
||||
* @returns {module:openmct.TelemetryAPI~LimitViolation} metadata about
|
||||
* the limit violation, or undefined if a value is within limits
|
||||
*/
|
||||
|
||||
/**
|
||||
* A violation of limits defined for a telemetry property.
|
||||
* @typedef LimitViolation
|
||||
* @memberof {module:openmct.TelemetryAPI~}
|
||||
* @property {string} cssClass the class (or space-separated classes) to
|
||||
* apply to display elements for values which violate this limit
|
||||
* @property {string} name the human-readable name for the limit violation
|
||||
*/
|
||||
|
||||
/**
|
||||
* A TelemetryFormatter converts telemetry values for purposes of
|
||||
* display as text.
|
||||
*
|
||||
* @interface TelemetryFormatter
|
||||
* @memberof module:openmct.TelemetryAPI~
|
||||
*/
|
||||
|
||||
/**
|
||||
* Retrieve the 'key' from the datum and format it accordingly to
|
||||
* telemetry metadata in domain object.
|
||||
*
|
||||
* @method format
|
||||
* @memberof module:openmct.TelemetryAPI~TelemetryFormatter#
|
||||
*/
|
||||
|
||||
/**
|
||||
* Describes a property which would be found in a datum of telemetry
|
||||
* associated with a particular domain object.
|
||||
*
|
||||
* @typedef TelemetryProperty
|
||||
* @memberof module:openmct.TelemetryAPI~
|
||||
* @property {string} key the name of the property in the datum which
|
||||
* contains this telemetry value
|
||||
* @property {string} name the human-readable name for this property
|
||||
* @property {string} [units] the units associated with this property
|
||||
* @property {boolean} [temporal] true if this property is a timestamp, or
|
||||
* may be otherwise used to order telemetry in a time-like
|
||||
* fashion; default is false
|
||||
* @property {boolean} [numeric] true if the values for this property
|
||||
* can be interpreted plainly as numbers; default is true
|
||||
* @property {boolean} [enumerated] true if this property may have only
|
||||
* certain specific values; default is false
|
||||
* @property {string} [values] for enumerated states, an ordered list
|
||||
* of possible values
|
||||
*/
|
||||
|
||||
/**
|
||||
* Describes and bounds requests for telemetry data.
|
||||
*
|
||||
* @typedef TelemetryRequest
|
||||
* @memberof module:openmct.TelemetryAPI~
|
||||
* @property {string} sort the key of the property to sort by. This may
|
||||
* be prefixed with a "+" or a "-" sign to sort in ascending
|
||||
* or descending order respectively. If no prefix is present,
|
||||
* ascending order will be used.
|
||||
* @property {*} start the lower bound for values of the sorting property
|
||||
* @property {*} end the upper bound for values of the sorting property
|
||||
* @property {string[]} strategies symbolic identifiers for strategies
|
||||
* (such as `minmax`) which may be recognized by providers;
|
||||
* these will be tried in order until an appropriate provider
|
||||
* is found
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides telemetry data. To connect to new data sources, new
|
||||
* TelemetryProvider implementations should be
|
||||
* [registered]{@link module:openmct.TelemetryAPI#addProvider}.
|
||||
*
|
||||
* @interface TelemetryProvider
|
||||
* @memberof module:openmct.TelemetryAPI~
|
||||
*/
|
||||
|
||||
/**
|
||||
* An interface for retrieving telemetry data associated with a domain
|
||||
* object.
|
||||
*
|
||||
* @interface TelemetryAPI
|
||||
* @augments module:openmct.TelemetryAPI~TelemetryProvider
|
||||
* @memberof module:openmct
|
||||
*/
|
||||
|
@ -21,7 +21,7 @@
|
||||
*****************************************************************************/
|
||||
import { createOpenMct, resetApplicationState } from 'utils/testing';
|
||||
import TelemetryAPI from './TelemetryAPI';
|
||||
const { TelemetryCollection } = require("./TelemetryCollection");
|
||||
import TelemetryCollection from './TelemetryCollection';
|
||||
|
||||
describe('Telemetry API', function () {
|
||||
let openmct;
|
||||
|
@ -26,7 +26,7 @@ import { LOADED_ERROR, TIMESYSTEM_KEY_NOTIFICATION, TIMESYSTEM_KEY_WARNING } fro
|
||||
|
||||
/** Class representing a Telemetry Collection. */
|
||||
|
||||
export class TelemetryCollection extends EventEmitter {
|
||||
export default class TelemetryCollection extends EventEmitter {
|
||||
/**
|
||||
* Creates a Telemetry Collection
|
||||
*
|
||||
@ -127,7 +127,8 @@ export class TelemetryCollection extends EventEmitter {
|
||||
this.requestAbort = new AbortController();
|
||||
options.signal = this.requestAbort.signal;
|
||||
this.emit('requestStarted');
|
||||
historicalData = await historicalProvider.request(this.domainObject, options);
|
||||
const modifiedOptions = await this.openmct.telemetry.applyRequestInterceptors(this.domainObject, options);
|
||||
historicalData = await historicalProvider.request(this.domainObject, modifiedOptions);
|
||||
} catch (error) {
|
||||
if (error.name !== 'AbortError') {
|
||||
console.error('Error requesting telemetry data...');
|
||||
|
68
src/api/telemetry/TelemetryRequestInterceptor.js
Normal file
68
src/api/telemetry/TelemetryRequestInterceptor.js
Normal file
@ -0,0 +1,68 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2022, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class TelemetryRequestInterceptorRegistry {
|
||||
/**
|
||||
* A TelemetryRequestInterceptorRegistry maintains the definitions for different interceptors that may be invoked on telemetry
|
||||
* requests.
|
||||
* @interface TelemetryRequestInterceptorRegistry
|
||||
* @memberof module:openmct
|
||||
*/
|
||||
constructor() {
|
||||
this.interceptors = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @interface TelemetryRequestInterceptorDef
|
||||
* @property {function} appliesTo function that determines if this interceptor should be called for the given identifier/request
|
||||
* @property {function} invoke function that transforms the provided request and returns the transformed request
|
||||
* @property {function} priority the priority for this interceptor. A higher number returned has more weight than a lower number
|
||||
* @memberof module:openmct TelemetryRequestInterceptorRegistry#
|
||||
*/
|
||||
|
||||
/**
|
||||
* Register a new telemetry request interceptor.
|
||||
*
|
||||
* @param {module:openmct.RequestInterceptorDef} requestInterceptorDef the interceptor to add
|
||||
* @method addInterceptor
|
||||
* @memberof module:openmct.TelemetryRequestInterceptorRegistry#
|
||||
*/
|
||||
addInterceptor(interceptorDef) {
|
||||
//TODO: sort by priority
|
||||
this.interceptors.push(interceptorDef);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all interceptors applicable to a domain object/request.
|
||||
* @method getInterceptors
|
||||
* @returns [module:openmct.RequestInterceptorDef] the registered interceptors for this identifier/request
|
||||
* @memberof module:openmct.TelemetryRequestInterceptorRegistry#
|
||||
*/
|
||||
getInterceptors(identifier, request) {
|
||||
return this.interceptors.filter(interceptor => {
|
||||
return typeof interceptor.appliesTo === 'function'
|
||||
&& interceptor.appliesTo(identifier, request);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -373,39 +373,30 @@ describe("The Imagery View Layouts", () => {
|
||||
return Vue.nextTick();
|
||||
});
|
||||
|
||||
it("on mount should show the the most recent image", () => {
|
||||
it("on mount should show the the most recent image", async () => {
|
||||
//Looks like we need Vue.nextTick here so that computed properties settle down
|
||||
return Vue.nextTick(() => {
|
||||
const imageInfo = getImageInfo(parent);
|
||||
|
||||
expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 1].timeId)).not.toEqual(-1);
|
||||
});
|
||||
await Vue.nextTick();
|
||||
const imageInfo = getImageInfo(parent);
|
||||
expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 1].timeId)).not.toEqual(-1);
|
||||
});
|
||||
|
||||
it("on mount should show the any image layers", (done) => {
|
||||
it("on mount should show the any image layers", async () => {
|
||||
//Looks like we need Vue.nextTick here so that computed properties settle down
|
||||
Vue.nextTick().then(() => {
|
||||
Vue.nextTick(() => {
|
||||
const layerEls = parent.querySelectorAll('.js-layer-image');
|
||||
console.log(layerEls);
|
||||
expect(layerEls.length).toEqual(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
await Vue.nextTick();
|
||||
const layerEls = parent.querySelectorAll('.js-layer-image');
|
||||
console.log(layerEls);
|
||||
expect(layerEls.length).toEqual(1);
|
||||
});
|
||||
|
||||
it("should show the clicked thumbnail as the main image", (done) => {
|
||||
it("should show the clicked thumbnail as the main image", async () => {
|
||||
//Looks like we need Vue.nextTick here so that computed properties settle down
|
||||
Vue.nextTick(() => {
|
||||
const target = imageTelemetry[5].url;
|
||||
parent.querySelectorAll(`img[src='${target}']`)[0].click();
|
||||
Vue.nextTick(() => {
|
||||
const imageInfo = getImageInfo(parent);
|
||||
await Vue.nextTick();
|
||||
const target = imageTelemetry[5].url;
|
||||
parent.querySelectorAll(`img[src='${target}']`)[0].click();
|
||||
await Vue.nextTick();
|
||||
const imageInfo = getImageInfo(parent);
|
||||
|
||||
expect(imageInfo.url.indexOf(imageTelemetry[5].timeId)).not.toEqual(-1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
expect(imageInfo.url.indexOf(imageTelemetry[5].timeId)).not.toEqual(-1);
|
||||
});
|
||||
|
||||
xit("should show that an image is new", (done) => {
|
||||
@ -424,71 +415,60 @@ describe("The Imagery View Layouts", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("should show that an image is not new", (done) => {
|
||||
Vue.nextTick(() => {
|
||||
const target = imageTelemetry[4].url;
|
||||
parent.querySelectorAll(`img[src='${target}']`)[0].click();
|
||||
it("should show that an image is not new", async () => {
|
||||
await Vue.nextTick();
|
||||
const target = imageTelemetry[4].url;
|
||||
parent.querySelectorAll(`img[src='${target}']`)[0].click();
|
||||
|
||||
Vue.nextTick(() => {
|
||||
const imageIsNew = isNew(parent);
|
||||
await Vue.nextTick();
|
||||
const imageIsNew = isNew(parent);
|
||||
|
||||
expect(imageIsNew).toBeFalse();
|
||||
done();
|
||||
});
|
||||
});
|
||||
expect(imageIsNew).toBeFalse();
|
||||
});
|
||||
|
||||
it("should navigate via arrow keys", (done) => {
|
||||
Vue.nextTick(() => {
|
||||
let keyOpts = {
|
||||
element: parent.querySelector('.c-imagery'),
|
||||
key: 'ArrowLeft',
|
||||
keyCode: 37,
|
||||
type: 'keyup'
|
||||
};
|
||||
it("should navigate via arrow keys", async () => {
|
||||
await Vue.nextTick();
|
||||
const keyOpts = {
|
||||
element: parent.querySelector('.c-imagery'),
|
||||
key: 'ArrowLeft',
|
||||
keyCode: 37,
|
||||
type: 'keyup'
|
||||
};
|
||||
|
||||
simulateKeyEvent(keyOpts);
|
||||
simulateKeyEvent(keyOpts);
|
||||
|
||||
Vue.nextTick(() => {
|
||||
const imageInfo = getImageInfo(parent);
|
||||
|
||||
expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 2].timeId)).not.toEqual(-1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
await Vue.nextTick();
|
||||
const imageInfo = getImageInfo(parent);
|
||||
expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 2].timeId)).not.toEqual(-1);
|
||||
});
|
||||
|
||||
it("should navigate via numerous arrow keys", (done) => {
|
||||
Vue.nextTick(() => {
|
||||
let element = parent.querySelector('.c-imagery');
|
||||
let type = 'keyup';
|
||||
let leftKeyOpts = {
|
||||
element,
|
||||
type,
|
||||
key: 'ArrowLeft',
|
||||
keyCode: 37
|
||||
};
|
||||
let rightKeyOpts = {
|
||||
element,
|
||||
type,
|
||||
key: 'ArrowRight',
|
||||
keyCode: 39
|
||||
};
|
||||
it("should navigate via numerous arrow keys", async () => {
|
||||
await Vue.nextTick();
|
||||
const element = parent.querySelector('.c-imagery');
|
||||
const type = 'keyup';
|
||||
const leftKeyOpts = {
|
||||
element,
|
||||
type,
|
||||
key: 'ArrowLeft',
|
||||
keyCode: 37
|
||||
};
|
||||
const rightKeyOpts = {
|
||||
element,
|
||||
type,
|
||||
key: 'ArrowRight',
|
||||
keyCode: 39
|
||||
};
|
||||
|
||||
// left thrice
|
||||
simulateKeyEvent(leftKeyOpts);
|
||||
simulateKeyEvent(leftKeyOpts);
|
||||
simulateKeyEvent(leftKeyOpts);
|
||||
// right once
|
||||
simulateKeyEvent(rightKeyOpts);
|
||||
// left thrice
|
||||
simulateKeyEvent(leftKeyOpts);
|
||||
simulateKeyEvent(leftKeyOpts);
|
||||
simulateKeyEvent(leftKeyOpts);
|
||||
// right once
|
||||
simulateKeyEvent(rightKeyOpts);
|
||||
|
||||
Vue.nextTick(() => {
|
||||
const imageInfo = getImageInfo(parent);
|
||||
|
||||
expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 3].timeId)).not.toEqual(-1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
await Vue.nextTick();
|
||||
const imageInfo = getImageInfo(parent);
|
||||
expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 3].timeId)).not.toEqual(-1);
|
||||
});
|
||||
it ('shows an auto scroll button when scroll to left', (done) => {
|
||||
Vue.nextTick(() => {
|
||||
|
@ -20,6 +20,7 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
import DefaultClock from '../../utils/clock/DefaultClock';
|
||||
import remoteClockRequestInterceptor from './requestInterceptor';
|
||||
|
||||
/**
|
||||
* A {@link openmct.TimeAPI.Clock} that updates the temporal bounds of the
|
||||
@ -49,6 +50,14 @@ export default class RemoteClock extends DefaultClock {
|
||||
|
||||
this.lastTick = 0;
|
||||
|
||||
this.openmct.telemetry.addRequestInterceptor(
|
||||
remoteClockRequestInterceptor(
|
||||
this.openmct,
|
||||
this.identifier,
|
||||
this.#waitForReady.bind(this)
|
||||
)
|
||||
);
|
||||
|
||||
this._processDatum = this._processDatum.bind(this);
|
||||
}
|
||||
|
||||
@ -129,4 +138,25 @@ export default class RemoteClock extends DefaultClock {
|
||||
return timeFormatter.parse(datum);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the clock to have a non-default tick value.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
#waitForReady() {
|
||||
const waitForInitialTick = (resolve) => {
|
||||
if (this.lastTick > 0) {
|
||||
const offsets = this.openmct.time.clockOffsets();
|
||||
resolve({
|
||||
start: this.lastTick + offsets.start,
|
||||
end: this.lastTick + offsets.end
|
||||
});
|
||||
} else {
|
||||
setTimeout(() => waitForInitialTick(resolve), 100);
|
||||
}
|
||||
};
|
||||
|
||||
return new Promise(waitForInitialTick);
|
||||
}
|
||||
}
|
||||
|
46
src/plugins/remoteClock/requestInterceptor.js
Normal file
46
src/plugins/remoteClock/requestInterceptor.js
Normal file
@ -0,0 +1,46 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2022, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
function remoteClockRequestInterceptor(openmct, remoteClockIdentifier, waitForBounds) {
|
||||
let remoteClockLoaded = false;
|
||||
|
||||
return {
|
||||
appliesTo: () => {
|
||||
// Get the activeClock from the Global Time Context
|
||||
const { activeClock } = openmct.time.getContextForView();
|
||||
|
||||
return activeClock !== undefined
|
||||
&& activeClock.key === 'remote-clock'
|
||||
&& !remoteClockLoaded;
|
||||
},
|
||||
invoke: async (request) => {
|
||||
const { start, end } = await waitForBounds();
|
||||
remoteClockLoaded = true;
|
||||
request[1].start = start;
|
||||
request[1].end = end;
|
||||
|
||||
return request;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default remoteClockRequestInterceptor;
|
@ -135,7 +135,7 @@ describe("the plugin", () => {
|
||||
let tableInstance;
|
||||
let mockClock;
|
||||
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
openmct.time.timeSystem('utc', {
|
||||
start: 0,
|
||||
end: 4
|
||||
@ -210,16 +210,8 @@ describe("the plugin", () => {
|
||||
'some-other-key': 'some-other-value 3'
|
||||
}
|
||||
];
|
||||
let telemetryPromiseResolve;
|
||||
let telemetryPromise = new Promise((resolve) => {
|
||||
telemetryPromiseResolve = resolve;
|
||||
});
|
||||
|
||||
historicalProvider.request = () => {
|
||||
telemetryPromiseResolve(testTelemetry);
|
||||
|
||||
return telemetryPromise;
|
||||
};
|
||||
historicalProvider.request = () => Promise.resolve(testTelemetry);
|
||||
|
||||
openmct.router.path = [testTelemetryObject];
|
||||
|
||||
@ -230,7 +222,7 @@ describe("the plugin", () => {
|
||||
|
||||
tableInstance = tableView.getTable();
|
||||
|
||||
return telemetryPromise.then(() => Vue.nextTick());
|
||||
await Vue.nextTick();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@ -255,13 +247,10 @@ describe("the plugin", () => {
|
||||
|
||||
});
|
||||
|
||||
it("Renders a row for every telemetry datum returned", (done) => {
|
||||
it("Renders a row for every telemetry datum returned", async () => {
|
||||
let rows = element.querySelectorAll('table.c-telemetry-table__body tr');
|
||||
Vue.nextTick(() => {
|
||||
expect(rows.length).toBe(3);
|
||||
|
||||
done();
|
||||
});
|
||||
await Vue.nextTick();
|
||||
expect(rows.length).toBe(3);
|
||||
});
|
||||
|
||||
it("Renders a column for every item in telemetry metadata", () => {
|
||||
@ -273,7 +262,7 @@ describe("the plugin", () => {
|
||||
expect(headers[3].innerText).toBe('Another attribute');
|
||||
});
|
||||
|
||||
it("Supports column reordering via drag and drop", () => {
|
||||
it("Supports column reordering via drag and drop", async () => {
|
||||
let columns = element.querySelectorAll('tr.c-telemetry-table__headers__labels th');
|
||||
let fromColumn = columns[0];
|
||||
let toColumn = columns[1];
|
||||
@ -292,54 +281,43 @@ describe("the plugin", () => {
|
||||
toColumn.dispatchEvent(dragOverEvent);
|
||||
toColumn.dispatchEvent(dropEvent);
|
||||
|
||||
return Vue.nextTick().then(() => {
|
||||
columns = element.querySelectorAll('tr.c-telemetry-table__headers__labels th');
|
||||
let firstColumn = columns[0];
|
||||
let secondColumn = columns[1];
|
||||
let firstColumnText = firstColumn.querySelector('span.c-telemetry-table__headers__label').innerText;
|
||||
let secondColumnText = secondColumn.querySelector('span.c-telemetry-table__headers__label').innerText;
|
||||
|
||||
expect(fromColumnText).not.toEqual(firstColumnText);
|
||||
expect(fromColumnText).toEqual(secondColumnText);
|
||||
expect(toColumnText).not.toEqual(secondColumnText);
|
||||
expect(toColumnText).toEqual(firstColumnText);
|
||||
});
|
||||
await Vue.nextTick();
|
||||
columns = element.querySelectorAll('tr.c-telemetry-table__headers__labels th');
|
||||
let firstColumn = columns[0];
|
||||
let secondColumn = columns[1];
|
||||
let firstColumnText = firstColumn.querySelector('span.c-telemetry-table__headers__label').innerText;
|
||||
let secondColumnText = secondColumn.querySelector('span.c-telemetry-table__headers__label').innerText;
|
||||
expect(fromColumnText).not.toEqual(firstColumnText);
|
||||
expect(fromColumnText).toEqual(secondColumnText);
|
||||
expect(toColumnText).not.toEqual(secondColumnText);
|
||||
expect(toColumnText).toEqual(firstColumnText);
|
||||
});
|
||||
|
||||
it("Supports filtering telemetry by regular text search", () => {
|
||||
it("Supports filtering telemetry by regular text search", async () => {
|
||||
tableInstance.tableRows.setColumnFilter("some-key", "1");
|
||||
await Vue.nextTick();
|
||||
let filteredRowElements = element.querySelectorAll('table.c-telemetry-table__body tr');
|
||||
|
||||
return Vue.nextTick().then(() => {
|
||||
let filteredRowElements = element.querySelectorAll('table.c-telemetry-table__body tr');
|
||||
expect(filteredRowElements.length).toEqual(1);
|
||||
tableInstance.tableRows.setColumnFilter("some-key", "");
|
||||
await Vue.nextTick();
|
||||
|
||||
expect(filteredRowElements.length).toEqual(1);
|
||||
|
||||
tableInstance.tableRows.setColumnFilter("some-key", "");
|
||||
|
||||
return Vue.nextTick().then(() => {
|
||||
let allRowElements = element.querySelectorAll('table.c-telemetry-table__body tr');
|
||||
|
||||
expect(allRowElements.length).toEqual(3);
|
||||
});
|
||||
});
|
||||
let allRowElements = element.querySelectorAll('table.c-telemetry-table__body tr');
|
||||
expect(allRowElements.length).toEqual(3);
|
||||
});
|
||||
|
||||
it("Supports filtering using Regex", () => {
|
||||
it("Supports filtering using Regex", async () => {
|
||||
tableInstance.tableRows.setColumnRegexFilter("some-key", "^some-value$");
|
||||
await Vue.nextTick();
|
||||
let filteredRowElements = element.querySelectorAll('table.c-telemetry-table__body tr');
|
||||
|
||||
return Vue.nextTick().then(() => {
|
||||
let filteredRowElements = element.querySelectorAll('table.c-telemetry-table__body tr');
|
||||
expect(filteredRowElements.length).toEqual(0);
|
||||
|
||||
expect(filteredRowElements.length).toEqual(0);
|
||||
tableInstance.tableRows.setColumnRegexFilter("some-key", "^some-value");
|
||||
await Vue.nextTick();
|
||||
let allRowElements = element.querySelectorAll('table.c-telemetry-table__body tr');
|
||||
|
||||
tableInstance.tableRows.setColumnRegexFilter("some-key", "^some-value");
|
||||
|
||||
return Vue.nextTick().then(() => {
|
||||
let allRowElements = element.querySelectorAll('table.c-telemetry-table__body tr');
|
||||
|
||||
expect(allRowElements.length).toEqual(3);
|
||||
});
|
||||
});
|
||||
expect(allRowElements.length).toEqual(3);
|
||||
});
|
||||
|
||||
it("displays the correct number of column headers when the configuration is mutated", async () => {
|
||||
@ -402,7 +380,7 @@ describe("the plugin", () => {
|
||||
expect(element.querySelector('div.c-table.is-paused')).not.toBeNull();
|
||||
|
||||
const currentBounds = openmct.time.bounds();
|
||||
|
||||
await Vue.nextTick();
|
||||
const newBounds = {
|
||||
start: currentBounds.start,
|
||||
end: currentBounds.end - 3
|
||||
@ -410,17 +388,10 @@ describe("the plugin", () => {
|
||||
|
||||
// Manually change the time bounds
|
||||
openmct.time.bounds(newBounds);
|
||||
|
||||
await Vue.nextTick();
|
||||
|
||||
// Verify table is no longer paused
|
||||
expect(element.querySelector('div.c-table.is-paused')).toBeNull();
|
||||
|
||||
await Vue.nextTick();
|
||||
|
||||
// Verify table displays the correct number of rows within the new bounds
|
||||
const tableRows = element.querySelectorAll('table.c-telemetry-table__body > tbody > tr');
|
||||
expect(tableRows.length).toEqual(2);
|
||||
});
|
||||
|
||||
it("Unpauses the table on user bounds change if paused by button", async () => {
|
||||
@ -428,19 +399,18 @@ describe("the plugin", () => {
|
||||
|
||||
// Pause by button
|
||||
viewContext.togglePauseByButton();
|
||||
|
||||
await Vue.nextTick();
|
||||
|
||||
// Verify table is paused
|
||||
expect(element.querySelector('div.c-table.is-paused')).not.toBeNull();
|
||||
|
||||
const currentBounds = openmct.time.bounds();
|
||||
await Vue.nextTick();
|
||||
|
||||
const newBounds = {
|
||||
start: currentBounds.start,
|
||||
end: currentBounds.end - 3
|
||||
end: currentBounds.end - 1
|
||||
};
|
||||
|
||||
// Manually change the time bounds
|
||||
openmct.time.bounds(newBounds);
|
||||
|
||||
@ -448,12 +418,6 @@ describe("the plugin", () => {
|
||||
|
||||
// Verify table is no longer paused
|
||||
expect(element.querySelector('div.c-table.is-paused')).toBeNull();
|
||||
|
||||
await Vue.nextTick();
|
||||
|
||||
// Verify table displays the correct number of rows within the new bounds
|
||||
const tableRows = element.querySelectorAll('table.c-telemetry-table__body > tbody > tr');
|
||||
expect(tableRows.length).toEqual(2);
|
||||
});
|
||||
|
||||
it("Does not unpause the table on tick", async () => {
|
||||
|
@ -7,12 +7,19 @@ const webpack = require('webpack');
|
||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||
|
||||
const {VueLoaderPlugin} = require('vue-loader');
|
||||
const gitRevision = require('child_process')
|
||||
.execSync('git rev-parse HEAD')
|
||||
.toString().trim();
|
||||
const gitBranch = require('child_process')
|
||||
.execSync('git rev-parse --abbrev-ref HEAD')
|
||||
.toString().trim();
|
||||
let gitRevision = 'error-retrieving-revision';
|
||||
let gitBranch = 'error-retrieving-branch';
|
||||
|
||||
try {
|
||||
gitRevision = require('child_process')
|
||||
.execSync('git rev-parse HEAD')
|
||||
.toString().trim();
|
||||
gitBranch = require('child_process')
|
||||
.execSync('git rev-parse --abbrev-ref HEAD')
|
||||
.toString().trim();
|
||||
} catch (err) {
|
||||
console.warn(err);
|
||||
}
|
||||
|
||||
/** @type {import('webpack').Configuration} */
|
||||
const config = {
|
||||
|
Loading…
x
Reference in New Issue
Block a user