diff --git a/src/plugins/condition/pluginSpec.js b/src/plugins/condition/pluginSpec.js index 85b6563ce2..012aee12cc 100644 --- a/src/plugins/condition/pluginSpec.js +++ b/src/plugins/condition/pluginSpec.js @@ -422,7 +422,7 @@ describe('the plugin', function () { "telemetry": "any", "operation": "isStale", "input": [ - "1" + "0.2" ], "metadata": "dataReceived" } @@ -481,12 +481,12 @@ describe('the plugin', function () { utc: undefined }); done(); - }, 1500); + }, 300); }); it('should not evaluate as stale when telemetry is received in the allotted time', (done) => { const date = Date.now(); - conditionSetDomainObject.configuration.conditionCollection[0].configuration.criteria[0].input = ["2"]; + conditionSetDomainObject.configuration.conditionCollection[0].configuration.criteria[0].input = ["0.4"]; let conditionMgr = new ConditionManager(conditionSetDomainObject, openmct); conditionMgr.on('conditionSetResultUpdated', mockListener); conditionMgr.telemetryObjects = { @@ -507,7 +507,7 @@ describe('the plugin', function () { utc: undefined }); done(); - }, 1500); + }, 300); }); }); }); diff --git a/src/plugins/displayLayout/DisplayLayoutToolbar.js b/src/plugins/displayLayout/DisplayLayoutToolbar.js index 35fe9956ec..cedbd28b19 100644 --- a/src/plugins/displayLayout/DisplayLayoutToolbar.js +++ b/src/plugins/displayLayout/DisplayLayoutToolbar.js @@ -550,7 +550,11 @@ define(['lodash'], function (_) { function unitsOnly(items) { let results = items.filter((item) => { let currentItem = item[0]; - let metadata = this.openmct.telemetry.getMetadata(currentItem.context.item); + let metadata = openmct.telemetry.getMetadata(currentItem.context.item); + if (!metadata) { + return false; + } + let hasUnits = metadata .valueMetadatas .filter((metadatum) => metadatum.unit) diff --git a/src/plugins/displayLayout/pluginSpec.js b/src/plugins/displayLayout/pluginSpec.js new file mode 100644 index 0000000000..60331b79ec --- /dev/null +++ b/src/plugins/displayLayout/pluginSpec.js @@ -0,0 +1,350 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2020, 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. + *****************************************************************************/ + +import { createOpenMct, resetApplicationState } from 'utils/testing'; +import DisplayLayoutPlugin from './plugin'; + +describe('the plugin', function () { + let element; + let child; + let openmct; + let displayLayoutDefinition; + + beforeAll(() => { + resetApplicationState(openmct); + }); + + beforeEach((done) => { + openmct = createOpenMct(); + openmct.install(new DisplayLayoutPlugin({ + showAsView: [] + })); + displayLayoutDefinition = openmct.types.get('layout'); + + element = document.createElement('div'); + child = document.createElement('div'); + element.appendChild(child); + + openmct.on('start', done); + openmct.startHeadless(); + }); + + afterEach(() => { + resetApplicationState(openmct); + }); + + it('defines a display layout object type with the correct key', () => { + expect(displayLayoutDefinition.definition.name).toEqual('Display Layout'); + }); + + it('provides a view', () => { + const testViewObject = { + id: 'test-object', + type: 'layout', + configuration: { + items: [ + { + + 'identifier': { + 'namespace': '', + 'key': '55122607-e65e-44d5-9c9d-9c31a914ca89' + }, + 'x': 8, + 'y': 3, + 'width': 10, + 'height': 5, + 'displayMode': 'all', + 'value': 'sin', + 'stroke': '', + 'fill': '', + 'color': '', + 'size': '13px', + 'type': 'telemetry-view', + 'id': 'deb9f839-80ad-4ccf-a152-5c763ceb7d7e' + + } + ], + layoutGrid: [10, 10] + } + }; + + const applicableViews = openmct.objectViews.get(testViewObject); + let displayLayoutViewProvider = applicableViews.find((viewProvider) => viewProvider.key === 'layout.view'); + expect(displayLayoutViewProvider).toBeDefined(); + }); + + describe('the alpha numeric format view', () => { + let displayLayoutItem; + let telemetryItem; + let selection; + + beforeEach(() => { + displayLayoutItem = { + 'composition': [ + ], + 'configuration': { + 'items': [ + { + + 'identifier': { + 'namespace': '', + 'key': '55122607-e65e-44d5-9c9d-9c31a914ca89' + }, + 'x': 8, + 'y': 3, + 'width': 10, + 'height': 5, + 'displayMode': 'all', + 'value': 'sin', + 'stroke': '', + 'fill': '', + 'color': '', + 'size': '13px', + 'type': 'telemetry-view', + 'id': 'deb9f839-80ad-4ccf-a152-5c763ceb7d7e' + + } + ], + 'layoutGrid': [ + 10, + 10 + ] + }, + 'name': 'Display Layout', + 'type': 'layout', + 'identifier': { + 'namespace': '', + 'key': 'c5e636c1-6771-4c9c-b933-8665cab189b3' + } + }; + telemetryItem = { + 'telemetry': { + 'period': 5, + 'amplitude': 5, + 'offset': 5, + 'dataRateInHz': 5, + 'phase': 5, + 'randomness': 0 + }, + 'name': 'Sine Wave Generator', + 'type': 'generator', + 'modified': 1592851063871, + 'location': 'mine', + 'persisted': 1592851063871, + 'id': '55122607-e65e-44d5-9c9d-9c31a914ca89', + 'identifier': { + 'namespace': '', + 'key': '55122607-e65e-44d5-9c9d-9c31a914ca89' + } + }; + selection = [ + [{ + context: { + 'layoutItem': displayLayoutItem.configuration.items[0], + 'item': telemetryItem, + 'index': 1 + } + }, + { + context: { + 'item': displayLayoutItem, + 'supportsMultiSelect': true + } + }] + ]; + }); + + it('provides an alphanumeric format view', () => { + const displayLayoutAlphaNumFormatView = openmct.inspectorViews.get(selection); + expect(displayLayoutAlphaNumFormatView.length).toBeDefined(); + }); + }); + + describe('the toolbar', () => { + let displayLayoutItem; + let selection; + + beforeEach(() => { + displayLayoutItem = { + 'composition': [ + ], + 'configuration': { + 'items': [ + { + 'fill': '#717171', + 'stroke': '', + 'x': 1, + 'y': 1, + 'width': 10, + 'height': 5, + 'type': 'box-view', + 'id': '89b88746-d325-487b-aec4-11b79afff9e8' + }, + { + 'x': 18, + 'y': 9, + 'x2': 23, + 'y2': 4, + 'stroke': '#717171', + 'type': 'line-view', + 'id': '57d49a28-7863-43bd-9593-6570758916f0' + }, + { + + 'identifier': { + 'namespace': '', + 'key': '55122607-e65e-44d5-9c9d-9c31a914ca89' + }, + 'x': 8, + 'y': 3, + 'width': 10, + 'height': 5, + 'displayMode': 'all', + 'value': 'sin', + 'stroke': '', + 'fill': '', + 'color': '', + 'size': '13px', + 'type': 'telemetry-view', + 'id': 'deb9f839-80ad-4ccf-a152-5c763ceb7d7e' + + }, + { + + 'width': 32, + 'height': 18, + 'x': 78, + 'y': 8, + 'identifier': { + 'namespace': '', + 'key': 'bdeb91ab-3a7e-4a71-9dd2-39d73644e136' + }, + 'hasFrame': true, + 'type': 'subobject-view', + 'id': 'c0ff485a-344c-4e70-8d83-a9d9998a69fc' + + } + ], + 'layoutGrid': [ + 10, + 10 + ] + }, + 'name': 'Display Layout', + 'type': 'layout', + 'identifier': { + 'namespace': '', + 'key': 'c5e636c1-6771-4c9c-b933-8665cab189b3' + } + }; + selection = [ + [{ + context: { + 'layoutItem': displayLayoutItem.configuration.items[1], + 'index': 1 + } + }, + { + context: { + 'item': displayLayoutItem, + 'supportsMultiSelect': true + } + }], + [{ + context: { + 'layoutItem': displayLayoutItem.configuration.items[0], + 'index': 0 + } + }, + { + context: { + item: displayLayoutItem, + 'supportsMultiSelect': true + } + }], + [{ + context: { + 'layoutItem': displayLayoutItem.configuration.items[2], + 'item': displayLayoutItem.configuration.items[2], + 'index': 2 + } + }, + { + context: { + item: displayLayoutItem, + 'supportsMultiSelect': true + } + }], + [{ + context: { + 'item': { + + 'composition': [ + { + 'namespace': '', + 'key': '55122607-e65e-44d5-9c9d-9c31a914ca89' + } + ], + 'configuration': { + 'series': [ + { + 'identifier': { + 'namespace': '', + 'key': '55122607-e65e-44d5-9c9d-9c31a914ca89' + } + } + ], + 'yAxis': { + }, + 'xAxis': { + } + }, + 'name': 'Unnamed Overlay Plot', + 'type': 'telemetry.plot.overlay', + 'modified': 1594142141929, + 'location': 'mine', + 'identifier': { + 'namespace': '', + 'key': 'bdeb91ab-3a7e-4a71-9dd2-39d73644e136' + }, + 'persisted': 1594142141929 + + }, + 'layoutItem': displayLayoutItem.configuration.items[3], + 'index': 3 + } + }, + { + context: { + item: displayLayoutItem, + 'supportsMultiSelect': true + } + }] + ]; + }); + + it('provides controls including separators', () => { + const displayLayoutToolbar = openmct.toolbars.get(selection); + expect(displayLayoutToolbar.length).toBe(9); + }); + }); +}); diff --git a/src/plugins/timeConductor/Conductor.vue b/src/plugins/timeConductor/Conductor.vue index cebf7018ae..ef7607c689 100644 --- a/src/plugins/timeConductor/Conductor.vue +++ b/src/plugins/timeConductor/Conductor.vue @@ -143,7 +143,7 @@ @@ -190,6 +190,10 @@ export default { start: offsets && durationFormatter.format(Math.abs(offsets.start)), end: offsets && durationFormatter.format(Math.abs(offsets.end)) }, + bounds: { + start: bounds.start, + end: bounds.end + }, formattedBounds: { start: timeFormatter.format(bounds.start), end: timeFormatter.format(bounds.end) @@ -210,7 +214,7 @@ export default { document.addEventListener('keydown', this.handleKeyDown); document.addEventListener('keyup', this.handleKeyUp); this.setTimeSystem(JSON.parse(JSON.stringify(this.openmct.time.timeSystem()))); - this.openmct.time.on('bounds', this.setViewFromBounds); + this.openmct.time.on('bounds', this.handleNewBounds); this.openmct.time.on('timeSystem', this.setTimeSystem); this.openmct.time.on('clock', this.setViewFromClock); this.openmct.time.on('clockOffsets', this.setViewFromOffsets); @@ -220,6 +224,13 @@ export default { document.removeEventListener('keyup', this.handleKeyUp); }, methods: { + handleNewBounds(bounds) { + this.setBounds(bounds); + this.setViewFromBounds(bounds); + }, + setBounds(bounds) { + this.bounds = bounds; + }, handleKeyDown(event) { if (event.key === 'Alt') { this.altPressed = true; @@ -246,10 +257,13 @@ export default { this.formattedBounds.end = this.timeFormatter.format(bounds.end); }, endZoom(bounds) { - const _bounds = bounds ? bounds : this.openmct.time.bounds(); this.isZooming = false; - this.openmct.time.bounds(_bounds); + if (bounds) { + this.handleNewBounds(bounds); + } else { + this.setViewFromBounds(this.bounds); + } }, setTimeSystem(timeSystem) { this.timeSystem = timeSystem; diff --git a/src/plugins/timeConductor/ConductorAxis.vue b/src/plugins/timeConductor/ConductorAxis.vue index 46cdcfb360..8dbeb2c746 100644 --- a/src/plugins/timeConductor/ConductorAxis.vue +++ b/src/plugins/timeConductor/ConductorAxis.vue @@ -207,7 +207,7 @@ export default { this.$emit('panAxis', panBounds); }, endPan() { - const panBounds = this.dragStartX && this.dragX && this.dragStartX !== this.dragX + const panBounds = this.isChangingViewBounds() ? this.getPanBounds() : undefined; this.$emit('endPan', panBounds); @@ -251,16 +251,14 @@ export default { }); }, endZoom() { - const zoomRange = this.dragStartX && this.dragX && this.dragStartX !== this.dragX - ? this.getZoomRange() - : undefined; - - const zoomBounds = zoomRange - ? { + let zoomBounds; + if (this.isChangingViewBounds()) { + const zoomRange = this.getZoomRange(); + zoomBounds = { start: this.scaleToBounds(zoomRange.start), end: this.scaleToBounds(zoomRange.end) - } - : this.openmct.time.bounds(); + }; + } this.zoomStyle = {}; this.$emit('endZoom', zoomBounds); @@ -290,6 +288,9 @@ export default { return bounds.start + offset; }, + isChangingViewBounds() { + return this.dragStartX && this.dragX && this.dragStartX !== this.dragX; + }, resize() { if (this.$refs.axisHolder.clientWidth !== this.width) { this.setAxisDimensions(); diff --git a/src/plugins/timeConductor/ConductorHistory.vue b/src/plugins/timeConductor/ConductorHistory.vue index ff4288e177..d11c24cb5b 100644 --- a/src/plugins/timeConductor/ConductorHistory.vue +++ b/src/plugins/timeConductor/ConductorHistory.vue @@ -84,7 +84,12 @@ export default { }, data() { return { - history: {}, // contains arrays of timespans {start, end}, array key is time system key + /** + * previous bounds entries available for easy re-use + * @history array of timespans + * @timespans {start, end} number representing timestamp + */ + history: this.getHistoryFromLocalStorage(), presets: [] }; }, @@ -111,22 +116,20 @@ export default { this.addTimespan(); }, deep: true - }, - history: { - handler() { - this.persistHistoryToLocalStorage(); - }, - deep: true } }, mounted() { - this.getHistoryFromLocalStorage(); + this.initializeHistoryIfNoHistory(); }, methods: { getHistoryFromLocalStorage() { - if (localStorage.getItem(LOCAL_STORAGE_HISTORY_KEY)) { - this.history = JSON.parse(localStorage.getItem(LOCAL_STORAGE_HISTORY_KEY)); - } else { + const localStorageHistory = localStorage.getItem(LOCAL_STORAGE_HISTORY_KEY); + const history = localStorageHistory ? JSON.parse(localStorageHistory) : undefined; + + return history; + }, + initializeHistoryIfNoHistory() { + if (!this.history) { this.history = {}; this.persistHistoryToLocalStorage(); } @@ -157,6 +160,8 @@ export default { currentHistory.unshift(timespan); this.history[key] = currentHistory; + + this.persistHistoryToLocalStorage(); }, selectTimespan(timespan) { this.openmct.time.bounds(timespan);