mirror of
https://github.com/nasa/openmct.git
synced 2025-06-25 10:44:21 +00:00
Compare commits
31 Commits
telemetry-
...
plots-prog
Author | SHA1 | Date | |
---|---|---|---|
4e00a84bd5 | |||
32524f04af | |||
6eafc885f1 | |||
0a48a37be0 | |||
7e5392a6ce | |||
e5fcb6e768 | |||
040b686f96 | |||
e01378aefc | |||
5550f4cef1 | |||
925d6905a5 | |||
bd9ce0e8c4 | |||
9346ff31cd | |||
50db655bae | |||
1b03e5c7df | |||
8ba69b5e08 | |||
d389e928ba | |||
5685a5b393 | |||
164f39695e | |||
c384cf67da | |||
417b225505 | |||
e5e93f311c | |||
39e6d9c90c | |||
60d021ef82 | |||
59880955a2 | |||
b51ed7e844 | |||
7bbaec4006 | |||
c0f24b3925 | |||
4e79725897 | |||
0674c9fc33 | |||
de1b877954 | |||
4db2f547d9 |
22
e2e/test-data/VisualTestData_storage.json
Normal file
22
e2e/test-data/VisualTestData_storage.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"cookies": [],
|
||||
"origins": [
|
||||
{
|
||||
"origin": "http://localhost:8080",
|
||||
"localStorage": [
|
||||
{
|
||||
"name": "tcHistory",
|
||||
"value": "{\"utc\":[{\"start\":1654548551471,\"end\":1654550351471}]}"
|
||||
},
|
||||
{
|
||||
"name": "mct",
|
||||
"value": "{\"mine\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"527856c0-cced-4b64-bb19-f943432326d0\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"persisted\":1654550352296,\"modified\":1654550352296},\"527856c0-cced-4b64-bb19-f943432326d0\":{\"identifier\":{\"key\":\"527856c0-cced-4b64-bb19-f943432326d0\",\"namespace\":\"\"},\"name\":\"Unnamed Overlay Plot\",\"type\":\"telemetry.plot.overlay\",\"composition\":[{\"key\":\"ce88ce37-8bb9-45e1-a85b-bb7e3c8453b9\",\"namespace\":\"\"}],\"configuration\":{\"series\":[{\"identifier\":{\"key\":\"ce88ce37-8bb9-45e1-a85b-bb7e3c8453b9\",\"namespace\":\"\"}}],\"yAxis\":{},\"xAxis\":{}},\"modified\":1654550353356,\"location\":\"mine\",\"persisted\":1654550353357},\"ce88ce37-8bb9-45e1-a85b-bb7e3c8453b9\":{\"name\":\"Unnamed Sine Wave Generator\",\"type\":\"generator\",\"identifier\":{\"key\":\"ce88ce37-8bb9-45e1-a85b-bb7e3c8453b9\",\"namespace\":\"\"},\"telemetry\":{\"period\":10,\"amplitude\":1,\"offset\":0,\"dataRateInHz\":1,\"phase\":0,\"randomness\":0,\"loadDelay\":\"5000\"},\"modified\":1654550353350,\"location\":\"527856c0-cced-4b64-bb19-f943432326d0\",\"persisted\":1654550353350}}"
|
||||
},
|
||||
{
|
||||
"name": "mct-tree-expanded",
|
||||
"value": "[\"/browse/mine\"]"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
22
e2e/test-data/recycled_local_storage.json
Normal file
22
e2e/test-data/recycled_local_storage.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"cookies": [],
|
||||
"origins": [
|
||||
{
|
||||
"origin": "http://localhost:8080",
|
||||
"localStorage": [
|
||||
{
|
||||
"name": "tcHistory",
|
||||
"value": "{\"utc\":[{\"start\":1654537164464,\"end\":1654538964464},{\"start\":1652301954635,\"end\":1652303754635}]}"
|
||||
},
|
||||
{
|
||||
"name": "mct",
|
||||
"value": "{\"mine\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"f64bea3b-58a7-4586-8c05-8b651e5f0bfd\",\"namespace\":\"\"},{\"key\":\"2d02a680-eb7e-4645-bba2-dd298f76efb8\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"persisted\":1654538965703,\"modified\":1654538965703},\"f64bea3b-58a7-4586-8c05-8b651e5f0bfd\":{\"name\":\"Unnamed Condition Set\",\"type\":\"conditionSet\",\"identifier\":{\"key\":\"f64bea3b-58a7-4586-8c05-8b651e5f0bfd\",\"namespace\":\"\"},\"configuration\":{\"conditionTestData\":[],\"conditionCollection\":[{\"isDefault\":true,\"id\":\"73f2d9ae-d1f3-4561-b7fc-ecd5df557249\",\"configuration\":{\"name\":\"Default\",\"output\":\"Default\",\"trigger\":\"all\",\"criteria\":[]},\"summary\":\"Default condition\"}]},\"composition\":[],\"telemetry\":{},\"modified\":1652303755999,\"location\":\"mine\",\"persisted\":1652303756002},\"2d02a680-eb7e-4645-bba2-dd298f76efb8\":{\"name\":\"Unnamed Condition Set\",\"type\":\"conditionSet\",\"identifier\":{\"key\":\"2d02a680-eb7e-4645-bba2-dd298f76efb8\",\"namespace\":\"\"},\"configuration\":{\"conditionTestData\":[],\"conditionCollection\":[{\"isDefault\":true,\"id\":\"4291d80c-303c-4d8d-85e1-10f012b864fb\",\"configuration\":{\"name\":\"Default\",\"output\":\"Default\",\"trigger\":\"all\",\"criteria\":[]},\"summary\":\"Default condition\"}]},\"composition\":[],\"telemetry\":{},\"modified\":1654538965702,\"location\":\"mine\",\"persisted\":1654538965702}}"
|
||||
},
|
||||
{
|
||||
"name": "mct-tree-expanded",
|
||||
"value": "[]"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -40,9 +40,6 @@ test.describe('Move item tests', () => {
|
||||
await page.locator('text=Properties Title Notes >> input[type="text"]').click();
|
||||
await page.locator('text=Properties Title Notes >> input[type="text"]').fill(folder1);
|
||||
|
||||
// Click on My Items in Tree. Workaround for https://github.com/nasa/openmct/issues/5184
|
||||
await page.click('form[name="mctForm"] a:has-text("My Items")');
|
||||
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.locator('text=OK').click(),
|
||||
@ -59,9 +56,6 @@ test.describe('Move item tests', () => {
|
||||
await page.locator('text=Properties Title Notes >> input[type="text"]').click();
|
||||
await page.locator('text=Properties Title Notes >> input[type="text"]').fill(folder2);
|
||||
|
||||
// Click on My Items in Tree. Workaround for https://github.com/nasa/openmct/issues/5184
|
||||
await page.click('form[name="mctForm"] a:has-text("My Items")');
|
||||
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.locator('text=OK').click(),
|
||||
@ -97,9 +91,6 @@ test.describe('Move item tests', () => {
|
||||
await page.locator('text=Properties Title Notes >> input[type="text"]').click();
|
||||
await page.locator('text=Properties Title Notes >> input[type="text"]').fill(telemetryTable);
|
||||
|
||||
// Click on My Items in Tree. Workaround for https://github.com/nasa/openmct/issues/5184
|
||||
await page.click('form[name="mctForm"] a:has-text("My Items")');
|
||||
|
||||
await page.locator('text=OK').click();
|
||||
|
||||
// Finish editing and save Telemetry Table
|
||||
|
@ -52,7 +52,7 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
|
||||
]);
|
||||
|
||||
//Save localStorage for future test execution
|
||||
await context.storageState({ path: './e2e/tests/recycled_storage.json' });
|
||||
await context.storageState({ path: './e2e/test-data/recycled_local_storage.json' });
|
||||
|
||||
//Set object identifier from url
|
||||
conditionSetUrl = await page.url();
|
||||
@ -65,7 +65,7 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
|
||||
await browser.close();
|
||||
});
|
||||
//Load localStorage for subsequent tests
|
||||
test.use({ storageState: './e2e/tests/recycled_storage.json' });
|
||||
test.use({ storageState: './e2e/test-data/recycled_local_storage.json' });
|
||||
//Begin suite of tests again localStorage
|
||||
test('Condition set object properties persist in main view and inspector', async ({ page }) => {
|
||||
//Navigate to baseURL with injected localStorage
|
||||
|
@ -41,9 +41,6 @@ test.describe('Example Imagery', () => {
|
||||
// Click text=Example Imagery
|
||||
await page.click('text=Example Imagery');
|
||||
|
||||
// Click on My Items in Tree. Workaround for https://github.com/nasa/openmct/issues/5184
|
||||
await page.click('form[name="mctForm"] a:has-text("My Items")');
|
||||
|
||||
// Click text=OK
|
||||
await Promise.all([
|
||||
page.waitForNavigation({waitUntil: 'networkidle'}),
|
||||
|
@ -39,9 +39,6 @@ test.describe('Telemetry Table', () => {
|
||||
await page.locator(createButton).click();
|
||||
await page.locator('li:has-text("Telemetry Table")').click();
|
||||
|
||||
// Click on My Items in Tree. Workaround for https://github.com/nasa/openmct/issues/5184
|
||||
await page.click('form[name="mctForm"] a:has-text("My Items")');
|
||||
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.locator('text=OK').click(),
|
||||
@ -59,9 +56,6 @@ test.describe('Telemetry Table', () => {
|
||||
// add Sine Wave Generator with defaults
|
||||
await page.locator('li:has-text("Sine Wave Generator")').click();
|
||||
|
||||
// Click on My Items in Tree. Workaround for https://github.com/nasa/openmct/issues/5184
|
||||
await page.click('form[name="mctForm"] a:has-text("My Items")');
|
||||
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.locator('text=OK').click(),
|
||||
|
70
e2e/tests/visual/controlledClock.visual.spec.js
Normal file
70
e2e/tests/visual/controlledClock.visual.spec.js
Normal file
@ -0,0 +1,70 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
Collection of Visual Tests set to run in a default context. The tests within this suite
|
||||
are only meant to run against openmct's app.js started by `npm run start` within the
|
||||
`./e2e/playwright-visual.config.js` file.
|
||||
|
||||
These should only use functional expect statements to verify assumptions about the state
|
||||
in a test and not for functional verification of correctness. Visual tests are not supposed
|
||||
to "fail" on assertions. Instead, they should be used to detect changes between builds or branches.
|
||||
|
||||
Note: Larger testsuite sizes are OK due to the setup time associated with these tests.
|
||||
*/
|
||||
|
||||
const { test, expect } = require('@playwright/test');
|
||||
const percySnapshot = require('@percy/playwright');
|
||||
const path = require('path');
|
||||
const sinon = require('sinon');
|
||||
|
||||
// Snippet from https://github.com/microsoft/playwright/issues/6347#issuecomment-965887758
|
||||
// Will replace with cy.clock() equivalent
|
||||
test.beforeEach(async ({ context }) => {
|
||||
await context.addInitScript({
|
||||
// eslint-disable-next-line no-undef
|
||||
path: path.join(__dirname, '../../..', './node_modules/sinon/pkg/sinon.js')
|
||||
});
|
||||
await context.addInitScript(() => {
|
||||
window.__clock = sinon.useFakeTimers({
|
||||
now: 0, //Set browser clock to UNIX Epoch
|
||||
shouldAdvanceTime: false, //Don't advance the clock
|
||||
toFake: ["setTimeout", "nextTick"]
|
||||
});
|
||||
});
|
||||
});
|
||||
test.use({ storageState: './e2e/test-data/VisualTestData_storage.json' });
|
||||
|
||||
test('Visual - Overlay Plot Loading Indicator @localstorage', async ({ page }) => {
|
||||
// Go to baseURL
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
|
||||
await page.locator('a:has-text("Unnamed Overlay Plot Overlay Plot")').click();
|
||||
//Ensure that we're on the Unnamed Overlay Plot object
|
||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Overlay Plot');
|
||||
|
||||
//Wait for canvas to be rendered and stop animating
|
||||
await page.locator('canvas >> nth=1').hover({trial: true});
|
||||
|
||||
//Take snapshot of Sine Wave Generator within Overlay Plot
|
||||
await percySnapshot(page, 'SineWaveInOverlayPlot');
|
||||
});
|
95
e2e/tests/visual/generateVisualTestData.e2e.spec.js
Normal file
95
e2e/tests/visual/generateVisualTestData.e2e.spec.js
Normal file
@ -0,0 +1,95 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
This test suite is dedicated to generating LocalStorage via Session Storage to be used
|
||||
in some visual test suites like controlledClock.visual.spec.js. This suite should run to completion
|
||||
and generate an artifact named ./e2e/test-data/VisualTestData_storage.json . This will run
|
||||
on every Commit to ensure that this object still loads into tests correctly and will retain the
|
||||
.e2e.spec.js suffix.
|
||||
|
||||
TODO: Provide additional validation of object properties as it grows.
|
||||
|
||||
*/
|
||||
|
||||
const { test } = require('../../fixtures.js');
|
||||
const { expect } = require('@playwright/test');
|
||||
|
||||
test('Generate Visual Test Data @localStorage', async ({ page, context }) => {
|
||||
//Go to baseURL
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
|
||||
await page.locator('button:has-text("Create")').click();
|
||||
|
||||
// add overlay plot with defaults
|
||||
await page.locator('li:has-text("Overlay Plot")').click();
|
||||
|
||||
// Click on My Items in Tree. Workaround for https://github.com/nasa/openmct/issues/5184
|
||||
await page.click('form[name="mctForm"] a:has-text("My Items")');
|
||||
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.locator('text=OK').click(),
|
||||
//Wait for Save Banner to appear1
|
||||
page.waitForSelector('.c-message-banner__message')
|
||||
]);
|
||||
//Wait until Save Banner is gone
|
||||
await page.locator('.c-message-banner__close-button').click();
|
||||
await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
|
||||
|
||||
// save (exit edit mode)
|
||||
await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
|
||||
await page.locator('text=Save and Finish Editing').click();
|
||||
|
||||
// click create button
|
||||
await page.locator('button:has-text("Create")').click();
|
||||
|
||||
// add sine wave generator with defaults
|
||||
await page.locator('li:has-text("Sine Wave Generator")').click();
|
||||
|
||||
//Add a 5000 ms Delay
|
||||
await page.locator('[aria-label="Loading Delay \\(ms\\)"]').fill('5000');
|
||||
|
||||
// Click on My Items in Tree. Workaround for https://github.com/nasa/openmct/issues/5184
|
||||
await page.click('form[name="mctForm"] a:has-text("Overlay Plot")');
|
||||
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.locator('text=OK').click(),
|
||||
//Wait for Save Banner to appear1
|
||||
page.waitForSelector('.c-message-banner__message')
|
||||
]);
|
||||
//Wait until Save Banner is gone
|
||||
await page.locator('.c-message-banner__close-button').click();
|
||||
await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
|
||||
|
||||
// focus the overlay plot
|
||||
await page.locator('text=Open MCT My Items >> span').nth(3).click();
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.locator('text=Unnamed Overlay Plot').first().click()
|
||||
]);
|
||||
|
||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Overlay Plot');
|
||||
//Save localStorage for future test execution
|
||||
await context.storageState({ path: './e2e/test-data/VisualTestData_storage.json' });
|
||||
});
|
@ -32,7 +32,8 @@ define([
|
||||
offset: 0,
|
||||
dataRateInHz: 1,
|
||||
randomness: 0,
|
||||
phase: 0
|
||||
phase: 0,
|
||||
loadDelay: 0
|
||||
};
|
||||
|
||||
function GeneratorProvider(openmct) {
|
||||
@ -53,8 +54,9 @@ define([
|
||||
'period',
|
||||
'offset',
|
||||
'dataRateInHz',
|
||||
'randomness',
|
||||
'phase',
|
||||
'randomness'
|
||||
'loadDelay'
|
||||
];
|
||||
|
||||
request = request || {};
|
||||
|
@ -116,6 +116,7 @@
|
||||
var dataRateInHz = request.dataRateInHz;
|
||||
var phase = request.phase;
|
||||
var randomness = request.randomness;
|
||||
var loadDelay = Math.max(request.loadDelay, 0);
|
||||
|
||||
var step = 1000 / dataRateInHz;
|
||||
var nextStep = start - (start % step) + step;
|
||||
@ -133,6 +134,14 @@
|
||||
});
|
||||
}
|
||||
|
||||
if (loadDelay === 0) {
|
||||
postOnRequest(message, request, data);
|
||||
} else {
|
||||
setTimeout(() => postOnRequest(message, request, data), loadDelay);
|
||||
}
|
||||
}
|
||||
|
||||
function postOnRequest(message, request, data) {
|
||||
self.postMessage({
|
||||
id: message.id,
|
||||
data: request.spectra ? {
|
||||
|
@ -132,6 +132,17 @@ define([
|
||||
"telemetry",
|
||||
"randomness"
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Loading Delay (ms)",
|
||||
control: "numberfield",
|
||||
cssClass: "l-input-sm l-numeric",
|
||||
key: "loadDelay",
|
||||
required: true,
|
||||
property: [
|
||||
"telemetry",
|
||||
"loadDelay"
|
||||
]
|
||||
}
|
||||
],
|
||||
initialize: function (object) {
|
||||
@ -141,7 +152,8 @@ define([
|
||||
offset: 0,
|
||||
dataRateInHz: 1,
|
||||
phase: 0,
|
||||
randomness: 0
|
||||
randomness: 0,
|
||||
loadDelay: 0
|
||||
};
|
||||
}
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "openmct",
|
||||
"version": "2.1.0-SNAPSHOT",
|
||||
"version": "2.0.5",
|
||||
"description": "The Open MCT core platform",
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "7.18.2",
|
||||
|
@ -60,6 +60,7 @@
|
||||
tabindex="0"
|
||||
:disabled="isInvalid"
|
||||
class="c-button c-button--major"
|
||||
aria-label="Save"
|
||||
@click="onSave"
|
||||
>
|
||||
{{ submitLabel }}
|
||||
@ -67,6 +68,7 @@
|
||||
<button
|
||||
tabindex="0"
|
||||
class="c-button js-cancel-button"
|
||||
aria-label="Cancel"
|
||||
@click="onDismiss"
|
||||
>
|
||||
{{ cancelLabel }}
|
||||
|
@ -28,6 +28,7 @@
|
||||
>
|
||||
<input
|
||||
v-model="field"
|
||||
:aria-label="model.name"
|
||||
type="number"
|
||||
:min="model.min"
|
||||
:max="model.max"
|
||||
|
@ -168,7 +168,7 @@ export default class StatusAPI extends EventEmitter {
|
||||
*/
|
||||
async resetStatusForRole(role) {
|
||||
const provider = this.#userAPI.getProvider();
|
||||
const defaultStatus = await this.getDefaultStatus();
|
||||
const defaultStatus = await this.getDefaultStatusForRole(role);
|
||||
|
||||
if (provider.setStatusForRole) {
|
||||
return provider.setStatusForRole(role, defaultStatus);
|
||||
|
@ -136,8 +136,8 @@ export default {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
const conditionSetIdentifier = domainObject.configuration.objectStyles.conditionSetIdentifier;
|
||||
if (this.conditionSetIdentifier !== conditionSetIdentifier) {
|
||||
const conditionSetIdentifier = domainObject.configuration?.objectStyles?.conditionSetIdentifier;
|
||||
if (conditionSetIdentifier && this.conditionSetIdentifier !== conditionSetIdentifier) {
|
||||
this.conditionSetIdentifier = conditionSetIdentifier;
|
||||
}
|
||||
|
||||
|
@ -128,6 +128,30 @@ export default class ExportAsJSONAction {
|
||||
|
||||
return copyOfChild;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {object} child
|
||||
* @param {object} parent
|
||||
* @returns {object}
|
||||
*/
|
||||
_rewriteLinkForReference(child, parent) {
|
||||
const childId = this._getId(child);
|
||||
this.externalIdentifiers.push(childId);
|
||||
const copyOfChild = JSON.parse(JSON.stringify(child));
|
||||
|
||||
copyOfChild.identifier.key = uuid();
|
||||
const newIdString = this._getId(copyOfChild);
|
||||
const parentId = this._getId(parent);
|
||||
|
||||
this.idMap[childId] = newIdString;
|
||||
copyOfChild.location = null;
|
||||
parent.configuration.objectStyles.conditionSetIdentifier = copyOfChild.identifier;
|
||||
this.tree[newIdString] = copyOfChild;
|
||||
this.tree[parentId].configuration.objectStyles.conditionSetIdentifier = copyOfChild.identifier;
|
||||
|
||||
return copyOfChild;
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@ -159,23 +183,27 @@ export default class ExportAsJSONAction {
|
||||
"rootId": this._getId(this.root)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {object} parent
|
||||
*/
|
||||
_write(parent) {
|
||||
this.calls++;
|
||||
//conditional object styles are not saved on the composition, so we need to check for them
|
||||
let childObjectReferenceId = parent.configuration?.objectStyles?.conditionSetIdentifier;
|
||||
|
||||
const composition = this.openmct.composition.get(parent);
|
||||
if (composition !== undefined) {
|
||||
composition.load()
|
||||
.then((children) => {
|
||||
children.forEach((child, index) => {
|
||||
// Only export if object is creatable
|
||||
// Only export if object is creatable
|
||||
if (this._isCreatableAndPersistable(child)) {
|
||||
// Prevents infinite export of self-contained objs
|
||||
// Prevents infinite export of self-contained objs
|
||||
if (!Object.prototype.hasOwnProperty.call(this.tree, this._getId(child))) {
|
||||
// If object is a link to something absent from
|
||||
// tree, generate new id and treat as new object
|
||||
// If object is a link to something absent from
|
||||
// tree, generate new id and treat as new object
|
||||
if (this._isLinkedObject(child, parent)) {
|
||||
child = this._rewriteLink(child, parent);
|
||||
} else {
|
||||
@ -186,18 +214,41 @@ export default class ExportAsJSONAction {
|
||||
}
|
||||
}
|
||||
});
|
||||
this.calls--;
|
||||
if (this.calls === 0) {
|
||||
this._rewriteReferences();
|
||||
this._saveAs(this._wrapTree());
|
||||
}
|
||||
this._decrementCallsAndSave();
|
||||
});
|
||||
} else {
|
||||
this.calls--;
|
||||
if (this.calls === 0) {
|
||||
this._rewriteReferences();
|
||||
this._saveAs(this._wrapTree());
|
||||
}
|
||||
} else if (!childObjectReferenceId) {
|
||||
this._decrementCallsAndSave();
|
||||
}
|
||||
|
||||
if (childObjectReferenceId) {
|
||||
this.openmct.objects.get(childObjectReferenceId)
|
||||
.then((child) => {
|
||||
// Only export if object is creatable
|
||||
if (this._isCreatableAndPersistable(child)) {
|
||||
// Prevents infinite export of self-contained objs
|
||||
if (!Object.prototype.hasOwnProperty.call(this.tree, this._getId(child))) {
|
||||
// If object is a link to something absent from
|
||||
// tree, generate new id and treat as new object
|
||||
if (this._isLinkedObject(child, parent)) {
|
||||
child = this._rewriteLinkForReference(child, parent);
|
||||
} else {
|
||||
this.tree[this._getId(child)] = child;
|
||||
}
|
||||
|
||||
this._write(child);
|
||||
}
|
||||
}
|
||||
|
||||
this._decrementCallsAndSave();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_decrementCallsAndSave() {
|
||||
this.calls--;
|
||||
if (this.calls === 0) {
|
||||
this._rewriteReferences();
|
||||
this._saveAs(this._wrapTree());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -322,4 +322,57 @@ describe('Export as JSON plugin', () => {
|
||||
|
||||
exportAsJSONAction.invoke([parent]);
|
||||
});
|
||||
|
||||
it('ExportAsJSONAction exports object references from tree', (done) => {
|
||||
const parent = {
|
||||
composition: [],
|
||||
configuration: {
|
||||
objectStyles: {
|
||||
conditionSetIdentifier: {
|
||||
key: 'child',
|
||||
namespace: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
identifier: {
|
||||
key: 'parent',
|
||||
namespace: ''
|
||||
},
|
||||
name: 'Parent',
|
||||
type: 'folder',
|
||||
modified: 1503598129176,
|
||||
location: 'mine',
|
||||
persisted: 1503598129176
|
||||
};
|
||||
|
||||
const child = {
|
||||
composition: [],
|
||||
identifier: {
|
||||
key: 'child',
|
||||
namespace: ''
|
||||
},
|
||||
name: 'Child',
|
||||
type: 'folder',
|
||||
modified: 1503598132428,
|
||||
location: null,
|
||||
persisted: 1503598132428
|
||||
};
|
||||
|
||||
spyOn(openmct.objects, 'get').and.callFake(object => {
|
||||
return Promise.resolve(child);
|
||||
});
|
||||
|
||||
spyOn(exportAsJSONAction, '_saveAs').and.callFake(completedTree => {
|
||||
expect(Object.keys(completedTree).length).toBe(2);
|
||||
const conditionSetId = Object.keys(completedTree.openmct)[1];
|
||||
expect(Object.prototype.hasOwnProperty.call(completedTree, 'openmct')).toBeTruthy();
|
||||
expect(Object.prototype.hasOwnProperty.call(completedTree, 'rootId')).toBeTruthy();
|
||||
expect(Object.prototype.hasOwnProperty.call(completedTree.openmct, 'parent')).toBeTruthy();
|
||||
expect(completedTree.openmct[conditionSetId].name).toBe('Child');
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
exportAsJSONAction.invoke([parent]);
|
||||
});
|
||||
});
|
||||
|
@ -82,12 +82,14 @@ export default class ImportAsJSONAction {
|
||||
* @param {object} seen
|
||||
*/
|
||||
_deepInstantiate(parent, tree, seen) {
|
||||
if (this.openmct.composition.get(parent)) {
|
||||
let objectIdentifiers = this._getObjectReferenceIds(parent);
|
||||
|
||||
if (objectIdentifiers.length) {
|
||||
let newObj;
|
||||
|
||||
seen.push(parent.id);
|
||||
|
||||
parent.composition.forEach(async (childId) => {
|
||||
objectIdentifiers.forEach(async (childId) => {
|
||||
const keystring = this.openmct.objects.makeKeyString(childId);
|
||||
if (!tree[keystring] || seen.includes(keystring)) {
|
||||
return;
|
||||
@ -101,6 +103,27 @@ export default class ImportAsJSONAction {
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
* @param {object} parent
|
||||
* @returns [identifiers]
|
||||
*/
|
||||
_getObjectReferenceIds(parent) {
|
||||
let objectIdentifiers = [];
|
||||
|
||||
let parentComposition = this.openmct.composition.get(parent);
|
||||
if (parentComposition) {
|
||||
objectIdentifiers = Array.from(parentComposition.domainObject.composition);
|
||||
}
|
||||
|
||||
//conditional object styles are not saved on the composition, so we need to check for them
|
||||
let parentObjectReference = parent.configuration?.objectStyles?.conditionSetIdentifier;
|
||||
if (parentObjectReference) {
|
||||
objectIdentifiers.push(parentObjectReference);
|
||||
}
|
||||
|
||||
return objectIdentifiers;
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
* @param {object} tree
|
||||
|
@ -484,7 +484,7 @@ export default {
|
||||
end: range.max,
|
||||
domain: this.config.xAxis.get('key')
|
||||
})
|
||||
.then(this.stopLoading());
|
||||
.then(this.stopLoading.bind(this));
|
||||
if (purge) {
|
||||
plotSeries.purgeRecordsOutsideRange(range);
|
||||
}
|
||||
|
@ -24,16 +24,16 @@
|
||||
ref="plotWrapper"
|
||||
class="c-plot holder holder-plot has-control-bar"
|
||||
>
|
||||
|
||||
<div
|
||||
ref="plotContainer"
|
||||
class="l-view-section u-style-receiver js-style-receiver"
|
||||
:class="{'s-status-timeconductor-unsynced': status && status === 'timeconductor-unsynced'}"
|
||||
>
|
||||
<div
|
||||
<progress-bar
|
||||
v-show="!!loading"
|
||||
class="c-loading--overlay loading"
|
||||
></div>
|
||||
class="c-telemetry-table__progress-bar"
|
||||
:model="{progressPerc: undefined}"
|
||||
/>
|
||||
<mct-plot
|
||||
:init-grid-lines="gridLines"
|
||||
:init-cursor-guide="cursorGuide"
|
||||
@ -49,10 +49,12 @@
|
||||
import eventHelpers from './lib/eventHelpers';
|
||||
import ImageExporter from '../../exporters/ImageExporter';
|
||||
import MctPlot from './MctPlot.vue';
|
||||
import ProgressBar from "../../ui/components/ProgressBar.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MctPlot
|
||||
MctPlot,
|
||||
ProgressBar
|
||||
},
|
||||
inject: ['openmct', 'domainObject', 'path'],
|
||||
props: {
|
||||
@ -89,17 +91,14 @@ export default {
|
||||
destroy() {
|
||||
this.stopListening();
|
||||
},
|
||||
|
||||
exportJPG() {
|
||||
const plotElement = this.$refs.plotContainer;
|
||||
this.imageExporter.exportJPG(plotElement, 'plot.jpg', 'export-plot');
|
||||
},
|
||||
|
||||
exportPNG() {
|
||||
const plotElement = this.$refs.plotContainer;
|
||||
this.imageExporter.exportPNG(plotElement, 'plot.png', 'export-plot');
|
||||
},
|
||||
|
||||
setStatus(status) {
|
||||
this.status = status;
|
||||
},
|
||||
|
@ -64,6 +64,7 @@ import YAxisForm from "./forms/YAxisForm.vue";
|
||||
import LegendForm from "./forms/LegendForm.vue";
|
||||
import eventHelpers from "../lib/eventHelpers";
|
||||
import configStore from "../configuration/ConfigStore";
|
||||
import _ from "lodash";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -125,15 +126,25 @@ export default {
|
||||
});
|
||||
if (index < 0) {
|
||||
index = stackedPlotObject.configuration.series.length;
|
||||
const configPath = `configuration.series[${index}]`;
|
||||
let newConfig = {
|
||||
identifier: config.identifier
|
||||
};
|
||||
_.set(newConfig, `${config.path}`, config.value);
|
||||
this.openmct.objects.mutate(
|
||||
stackedPlotObject,
|
||||
configPath,
|
||||
newConfig
|
||||
);
|
||||
} else {
|
||||
const configPath = `configuration.series[${index}].${config.path}`;
|
||||
this.openmct.objects.mutate(
|
||||
stackedPlotObject,
|
||||
configPath,
|
||||
config.value
|
||||
);
|
||||
}
|
||||
|
||||
const configPath = `configuration.series[${index}].${config.path}`;
|
||||
this.openmct.objects.mutate(
|
||||
stackedPlotObject,
|
||||
configPath,
|
||||
config.value
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -227,8 +227,9 @@ export default {
|
||||
|
||||
const path = objectPath(formField.objectPath);
|
||||
if (!_.isEqual(newVal, oldVal)) {
|
||||
// TODO: Why do we mutate yAxis twice, once directly, once via objects.mutate? Or are they different objects?
|
||||
// We mutate the model for the plots first PlotConfigurationModel - this triggers changes that affects the plot behavior
|
||||
this.yAxis.set(formKey, newVal);
|
||||
// Then we mutate the domain object configuration to persist the settings
|
||||
if (path) {
|
||||
if (!this.domainObject.configuration || !this.domainObject.configuration.series) {
|
||||
this.$emit('seriesUpdated', {
|
||||
|
@ -167,18 +167,6 @@ export default {
|
||||
const id = this.openmct.objects.makeKeyString(child.identifier);
|
||||
|
||||
this.$set(this.tickWidthMap, id, 0);
|
||||
const persistedConfig = this.domainObject.configuration.series && this.domainObject.configuration.series.find((seriesConfig) => {
|
||||
return this.openmct.objects.areIdsEqual(seriesConfig.identifier, child.identifier);
|
||||
});
|
||||
if (persistedConfig === undefined) {
|
||||
this.openmct.objects.mutate(
|
||||
this.domainObject,
|
||||
'configuration.series[' + this.compositionObjects.length + ']',
|
||||
{
|
||||
identifier: child.identifier
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
this.compositionObjects.push(child);
|
||||
},
|
||||
|
@ -29,6 +29,7 @@ import Vue from "vue";
|
||||
import conditionalStylesMixin from "./mixins/objectStyles-mixin";
|
||||
import configStore from "@/plugins/plot/configuration/ConfigStore";
|
||||
import PlotConfigurationModel from "@/plugins/plot/configuration/PlotConfigurationModel";
|
||||
import ProgressBar from "../../../ui/components/ProgressBar.vue";
|
||||
|
||||
export default {
|
||||
mixins: [conditionalStylesMixin],
|
||||
@ -125,7 +126,6 @@ export default {
|
||||
const onConfigLoaded = this.onConfigLoaded;
|
||||
const onCursorGuideChange = this.onCursorGuideChange;
|
||||
const onGridLinesChange = this.onGridLinesChange;
|
||||
const loadingUpdated = this.loadingUpdated;
|
||||
const setStatus = this.setStatus;
|
||||
|
||||
const openmct = this.openmct;
|
||||
@ -140,7 +140,8 @@ export default {
|
||||
this.component = new Vue({
|
||||
el: viewContainer,
|
||||
components: {
|
||||
MctPlot
|
||||
MctPlot,
|
||||
ProgressBar
|
||||
},
|
||||
provide: {
|
||||
openmct,
|
||||
@ -156,11 +157,16 @@ export default {
|
||||
onConfigLoaded,
|
||||
onCursorGuideChange,
|
||||
onGridLinesChange,
|
||||
loadingUpdated,
|
||||
setStatus
|
||||
setStatus,
|
||||
loading: true
|
||||
};
|
||||
},
|
||||
template: '<div ref="plotWrapper" class="l-view-section u-style-receiver js-style-receiver" :class="{\'s-status-timeconductor-unsynced\': status && status === \'timeconductor-unsynced\'}"><div v-show="!!loading" class="c-loading--overlay loading"></div><mct-plot :init-grid-lines="gridLines" :init-cursor-guide="cursorGuide" :plot-tick-width="plotTickWidth" :limit-line-labels="limitLineLabels" :color-palette="colorPalette" :options="options" @plotTickWidth="onTickWidthChange" @lockHighlightPoint="onLockHighlightPointUpdated" @highlights="onHighlightsUpdated" @configLoaded="onConfigLoaded" @cursorGuide="onCursorGuideChange" @gridLines="onGridLinesChange" @statusUpdated="setStatus" @loadingUpdated="loadingUpdated"/></div>'
|
||||
methods: {
|
||||
loadingUpdated(loaded) {
|
||||
this.loading = loaded;
|
||||
},
|
||||
},
|
||||
template: '<div ref="plotWrapper" class="l-view-section u-style-receiver js-style-receiver" :class="{\'s-status-timeconductor-unsynced\': status && status === \'timeconductor-unsynced\'}"><progress-bar v-show="loading !== false" class="c-telemetry-table__progress-bar" :model="{progressPerc: undefined}" /><mct-plot :init-grid-lines="gridLines" :init-cursor-guide="cursorGuide" :plot-tick-width="plotTickWidth" :options="options" @plotTickWidth="onTickWidthChange" @cursorGuide="onCursorGuideChange" @gridLines="onGridLinesChange" @statusUpdated="setStatus" @loadingUpdated="loadingUpdated"/></div>'
|
||||
});
|
||||
|
||||
this.setSelection();
|
||||
@ -198,17 +204,12 @@ export default {
|
||||
this.removeSelectable = this.openmct.selection.selectable(
|
||||
this.$el, this.context);
|
||||
},
|
||||
loadingUpdated(loaded) {
|
||||
this.loading = loaded;
|
||||
this.updateComponentProp('loading', loaded);
|
||||
},
|
||||
getProps() {
|
||||
return {
|
||||
limitLineLabels: this.showLimitLineLabels,
|
||||
gridLines: this.gridLines,
|
||||
cursorGuide: this.cursorGuide,
|
||||
plotTickWidth: this.plotTickWidth,
|
||||
loading: this.loading,
|
||||
options: this.options,
|
||||
status: this.status,
|
||||
colorPalette: this.colorPalette
|
||||
@ -223,33 +224,39 @@ export default {
|
||||
const configId = this.openmct.objects.makeKeyString(this.childObject.identifier);
|
||||
let config = configStore.get(configId);
|
||||
if (!config) {
|
||||
const persistedConfig = this.domainObject.configuration.series.find((seriesConfig) => {
|
||||
let persistedSeriesConfig = this.domainObject.configuration.series.find((seriesConfig) => {
|
||||
return this.openmct.objects.areIdsEqual(seriesConfig.identifier, this.childObject.identifier);
|
||||
});
|
||||
if (persistedConfig) {
|
||||
config = new PlotConfigurationModel({
|
||||
id: configId,
|
||||
domainObject: {
|
||||
...this.childObject,
|
||||
configuration: {
|
||||
series: [
|
||||
{
|
||||
identifier: this.childObject.identifier,
|
||||
...persistedConfig.series
|
||||
}
|
||||
],
|
||||
yAxis: persistedConfig.yAxis
|
||||
|
||||
}
|
||||
},
|
||||
openmct: this.openmct,
|
||||
palette: this.colorPalette,
|
||||
callback: (data) => {
|
||||
this.data = data;
|
||||
}
|
||||
});
|
||||
configStore.add(configId, config);
|
||||
if (!persistedSeriesConfig) {
|
||||
persistedSeriesConfig = {
|
||||
series: {},
|
||||
yAxis: {}
|
||||
};
|
||||
}
|
||||
|
||||
config = new PlotConfigurationModel({
|
||||
id: configId,
|
||||
domainObject: {
|
||||
...this.childObject,
|
||||
configuration: {
|
||||
series: [
|
||||
{
|
||||
identifier: this.childObject.identifier,
|
||||
...persistedSeriesConfig.series
|
||||
}
|
||||
],
|
||||
yAxis: persistedSeriesConfig.yAxis
|
||||
|
||||
}
|
||||
},
|
||||
openmct: this.openmct,
|
||||
palette: this.colorPalette,
|
||||
callback: (data) => {
|
||||
this.data = data;
|
||||
}
|
||||
});
|
||||
configStore.add(configId, config);
|
||||
}
|
||||
|
||||
return this.childObject;
|
||||
|
@ -144,7 +144,7 @@
|
||||
<progress-bar
|
||||
v-if="loading"
|
||||
class="c-telemetry-table__progress-bar"
|
||||
:model="progressLoad"
|
||||
:model="{progressPerc: undefined}"
|
||||
/>
|
||||
|
||||
<!-- Headers table -->
|
||||
@ -385,11 +385,6 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
progressLoad() {
|
||||
return {
|
||||
progressPerc: undefined
|
||||
};
|
||||
},
|
||||
dropTargetStyle() {
|
||||
return {
|
||||
top: this.$refs.headersTable.offsetTop + 'px',
|
||||
|
70
src/plugins/timeline/TimelineCompositionPolicy.js
Normal file
70
src/plugins/timeline/TimelineCompositionPolicy.js
Normal file
@ -0,0 +1,70 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2021, 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 ALLOWED_TYPES = [
|
||||
'telemetry.plot.overlay',
|
||||
'telemetry.plot.stacked',
|
||||
'plan'
|
||||
];
|
||||
const DISALLOWED_TYPES = [
|
||||
'telemetry.plot.bar-graph',
|
||||
'telemetry.plot.scatter-plot'
|
||||
];
|
||||
export default function TimelineCompositionPolicy(openmct) {
|
||||
function hasNumericTelemetry(domainObject, metadata) {
|
||||
const hasTelemetry = openmct.telemetry.isTelemetryObject(domainObject);
|
||||
if (!hasTelemetry || !metadata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return metadata.values().length > 0 && hasDomainAndRange(metadata);
|
||||
}
|
||||
|
||||
function hasDomainAndRange(metadata) {
|
||||
return (metadata.valuesForHints(['range']).length > 0
|
||||
&& metadata.valuesForHints(['domain']).length > 0);
|
||||
}
|
||||
|
||||
function hasImageTelemetry(domainObject, metadata) {
|
||||
if (!metadata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return metadata.valuesForHints(['image']).length > 0;
|
||||
}
|
||||
|
||||
return {
|
||||
allow: function (parent, child) {
|
||||
if (parent.type === 'time-strip') {
|
||||
const metadata = openmct.telemetry.getMetadata(child);
|
||||
|
||||
if (!DISALLOWED_TYPES.includes(child.type)
|
||||
&& (hasNumericTelemetry(child, metadata) || hasImageTelemetry(child, metadata) || ALLOWED_TYPES.includes(child.type))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
@ -22,6 +22,7 @@
|
||||
|
||||
import TimelineViewProvider from './TimelineViewProvider';
|
||||
import timelineInterceptor from "./timelineInterceptor";
|
||||
import TimelineCompositionPolicy from "./TimelineCompositionPolicy";
|
||||
|
||||
export default function () {
|
||||
return function install(openmct) {
|
||||
@ -39,6 +40,8 @@ export default function () {
|
||||
}
|
||||
});
|
||||
timelineInterceptor(openmct);
|
||||
openmct.composition.addPolicy(new TimelineCompositionPolicy(openmct).allow);
|
||||
|
||||
openmct.objectViews.addProvider(new TimelineViewProvider(openmct));
|
||||
};
|
||||
}
|
||||
|
@ -62,6 +62,34 @@ describe('the plugin', function () {
|
||||
})
|
||||
}
|
||||
};
|
||||
let timelineObject = {
|
||||
"composition": [],
|
||||
configuration: {
|
||||
useIndependentTime: false,
|
||||
timeOptions: {
|
||||
mode: {
|
||||
key: 'fixed'
|
||||
},
|
||||
fixedOffsets: {
|
||||
start: 10,
|
||||
end: 11
|
||||
},
|
||||
clockOffsets: {
|
||||
start: -(30 * 60 * 1000),
|
||||
end: (30 * 60 * 1000)
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Some timestrip",
|
||||
"type": "time-strip",
|
||||
"location": "mine",
|
||||
"modified": 1631005183584,
|
||||
"persisted": 1631005183502,
|
||||
"identifier": {
|
||||
"namespace": "",
|
||||
"key": "b78e7e23-f2b8-4776-b1f0-3ff778f5c8a9"
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach((done) => {
|
||||
mockObjectPath = [
|
||||
@ -134,28 +162,7 @@ describe('the plugin', function () {
|
||||
|
||||
beforeEach(() => {
|
||||
testViewObject = {
|
||||
id: "test-object",
|
||||
identifier: {
|
||||
key: "test-object",
|
||||
namespace: ''
|
||||
},
|
||||
type: "time-strip",
|
||||
configuration: {
|
||||
useIndependentTime: false,
|
||||
timeOptions: {
|
||||
mode: {
|
||||
key: 'fixed'
|
||||
},
|
||||
fixedOffsets: {
|
||||
start: 10,
|
||||
end: 11
|
||||
},
|
||||
clockOffsets: {
|
||||
start: -(30 * 60 * 1000),
|
||||
end: (30 * 60 * 1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
...timelineObject
|
||||
};
|
||||
|
||||
const applicableViews = openmct.objectViews.get(testViewObject, mockObjectPath);
|
||||
@ -187,15 +194,7 @@ describe('the plugin', function () {
|
||||
|
||||
beforeEach(() => {
|
||||
timelineDomainObject = {
|
||||
identifier: {
|
||||
key: 'test-object',
|
||||
namespace: ''
|
||||
},
|
||||
type: 'time-strip',
|
||||
id: "test-object",
|
||||
configuration: {
|
||||
useIndependentTime: false
|
||||
},
|
||||
...timelineObject,
|
||||
composition: [
|
||||
{
|
||||
identifier: {
|
||||
@ -236,27 +235,10 @@ describe('the plugin', function () {
|
||||
describe('the independent time conductor', () => {
|
||||
let timelineView;
|
||||
let testViewObject = {
|
||||
id: "test-object",
|
||||
identifier: {
|
||||
key: "test-object",
|
||||
namespace: ''
|
||||
},
|
||||
type: "time-strip",
|
||||
...timelineObject,
|
||||
configuration: {
|
||||
useIndependentTime: true,
|
||||
timeOptions: {
|
||||
mode: {
|
||||
key: 'local'
|
||||
},
|
||||
fixedOffsets: {
|
||||
start: 10,
|
||||
end: 11
|
||||
},
|
||||
clockOffsets: {
|
||||
start: -(30 * 60 * 1000),
|
||||
end: (30 * 60 * 1000)
|
||||
}
|
||||
}
|
||||
...timelineObject.configuration,
|
||||
useIndependentTime: true
|
||||
}
|
||||
};
|
||||
|
||||
@ -284,27 +266,15 @@ describe('the plugin', function () {
|
||||
describe('the independent time conductor - fixed', () => {
|
||||
let timelineView;
|
||||
let testViewObject2 = {
|
||||
...timelineObject,
|
||||
id: "test-object2",
|
||||
identifier: {
|
||||
key: "test-object2",
|
||||
namespace: ''
|
||||
},
|
||||
type: "time-strip",
|
||||
configuration: {
|
||||
useIndependentTime: true,
|
||||
timeOptions: {
|
||||
mode: {
|
||||
key: 'fixed'
|
||||
},
|
||||
fixedOffsets: {
|
||||
start: 10,
|
||||
end: 11
|
||||
},
|
||||
clockOffsets: {
|
||||
start: -(30 * 60 * 1000),
|
||||
end: (30 * 60 * 1000)
|
||||
}
|
||||
}
|
||||
...timelineObject.configuration,
|
||||
useIndependentTime: true
|
||||
}
|
||||
};
|
||||
|
||||
@ -328,4 +298,68 @@ describe('the plugin', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe("The timestrip composition policy", () => {
|
||||
let testObject;
|
||||
beforeEach(() => {
|
||||
testObject = {
|
||||
...timelineObject,
|
||||
composition: []
|
||||
};
|
||||
});
|
||||
|
||||
it("allows composition for plots", () => {
|
||||
const testTelemetryObject = {
|
||||
identifier: {
|
||||
namespace: "",
|
||||
key: "test-object"
|
||||
},
|
||||
type: "test-object",
|
||||
name: "Test Object",
|
||||
telemetry: {
|
||||
values: [{
|
||||
key: "some-key",
|
||||
name: "Some attribute",
|
||||
hints: {
|
||||
domain: 1
|
||||
}
|
||||
}, {
|
||||
key: "some-other-key",
|
||||
name: "Another attribute",
|
||||
hints: {
|
||||
range: 1
|
||||
}
|
||||
}]
|
||||
}
|
||||
};
|
||||
const composition = openmct.composition.get(testObject);
|
||||
expect(() => {
|
||||
composition.add(testTelemetryObject);
|
||||
}).not.toThrow();
|
||||
expect(testObject.composition.length).toBe(1);
|
||||
});
|
||||
|
||||
it("allows composition for plans", () => {
|
||||
const composition = openmct.composition.get(testObject);
|
||||
expect(() => {
|
||||
composition.add(planObject);
|
||||
}).not.toThrow();
|
||||
expect(testObject.composition.length).toBe(1);
|
||||
});
|
||||
|
||||
it("disallows composition for non time-based plots", () => {
|
||||
const barGraphObject = {
|
||||
identifier: {
|
||||
namespace: "",
|
||||
key: "test-object"
|
||||
},
|
||||
type: "telemetry.plot.bar-graph",
|
||||
name: "Test Object"
|
||||
};
|
||||
const composition = openmct.composition.get(testObject);
|
||||
expect(() => {
|
||||
composition.add(barGraphObject);
|
||||
}).toThrow();
|
||||
expect(testObject.composition.length).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Reference in New Issue
Block a user