Compare commits

..

6 Commits

Author SHA1 Message Date
John Hill
18137fe125 Merge branch 'master' into coverage-for-mct3930 2021-12-29 16:39:04 -08:00
unlikelyzero
51a6ff7825 rename files to e2e spec 2021-12-27 15:33:45 -08:00
unlikelyzero
f989733e81 update visual config for tests dir 2021-12-27 15:31:53 -08:00
unlikelyzero
5a6162eb4c localstorage tests 2021-12-27 15:31:25 -08:00
unlikelyzero
b0a2f8dd8b update playwright version 2021-12-27 15:28:29 -08:00
unlikelyzero
54bed23267 No need for allure reporting in visual tests 2021-12-23 11:11:51 -08:00
67 changed files with 737 additions and 689 deletions

View File

@@ -22,7 +22,7 @@ commands:
node-version: << parameters.node-version >>
- node/install:
install-npm: true
node-version: << parameters.node-version >>
node-version: lts/fermium
- run: npm install
restore_cache_cmd:
description: "Custom command for restoring cache with the ability to bust cache. When BUST_CACHE is set to true, jobs will not restore cache"

View File

@@ -24,7 +24,7 @@ assignees: ''
- [ ] Regression? Did this used to work or has it always been broken?
- [ ] Is there a workaround available?
- [ ] Does this impact a critical component?
- [ ] Is this just a visual bug with no functional impact?
- [ ] Is this just a visual bug?
#### Steps to Reproduce
<!--- Provide a link to a live example, or an unambiguous set of steps to -->

View File

@@ -16,7 +16,6 @@ updates:
- dependency-name: "*jasmine*"
- dependency-name: "*playwright*"
- dependency-name: "*percy*"
- dependency-name: "*vue-loader*"
- package-ecosystem: "github-actions"
directory: "/"

1
e2e/localstorage.json Normal file
View File

@@ -0,0 +1 @@
{"tcHistory":"{\"utc\":[{\"start\":1640401741152,\"end\":1640403541152}]}","mct":"{\"mine\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"fbb74cd3-bc17-4913-a47b-6ec7b620c26d\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"persisted\":1640403542253,\"modified\":1640403542253},\"fbb74cd3-bc17-4913-a47b-6ec7b620c26d\":{\"identifier\":{\"key\":\"fbb74cd3-bc17-4913-a47b-6ec7b620c26d\",\"namespace\":\"\"},\"name\":\"All DomainObjects\",\"type\":\"folder\",\"composition\":[{\"key\":\"cbee96e6-ec97-4059-a5bb-5a81b834ff3d\",\"namespace\":\"\"},{\"key\":\"15631105-127c-44a0-8d54-e3800943a98b\",\"namespace\":\"\"},{\"key\":\"31c2c6e1-55bd-4339-86f9-8cb1693566d0\",\"namespace\":\"\"}],\"modified\":1640403603547,\"location\":\"mine\",\"persisted\":1640403603547},\"cbee96e6-ec97-4059-a5bb-5a81b834ff3d\":{\"identifier\":{\"key\":\"cbee96e6-ec97-4059-a5bb-5a81b834ff3d\",\"namespace\":\"\"},\"name\":\"Unnamed Timer\",\"type\":\"timer\",\"configuration\":{\"timerFormat\":\"long\",\"timezone\":\"UTC\",\"timerState\":\"stopped\"},\"modified\":1640403543115,\"location\":\"fbb74cd3-bc17-4913-a47b-6ec7b620c26d\",\"persisted\":1640403543115},\"15631105-127c-44a0-8d54-e3800943a98b\":{\"identifier\":{\"key\":\"15631105-127c-44a0-8d54-e3800943a98b\",\"namespace\":\"\"},\"name\":\"Notebook\",\"type\":\"notebook\",\"configuration\":{\"defaultSort\":\"oldest\",\"entries\":{},\"imageMigrationVer\":\"v1\",\"pageTitle\":\"Page\",\"sections\":[{\"id\":\"ef4092ba-b6d1-4275-a659-bfae80b6ec9a\",\"isDefault\":false,\"isSelected\":true,\"name\":\"Unnamed Section\",\"pages\":[{\"id\":\"b187e90c-4759-47d5-af16-4a347520c0a6\",\"isDefault\":false,\"isSelected\":true,\"name\":\"Unnamed Page\",\"pageTitle\":\"Page\"}],\"sectionTitle\":\"Section\"}],\"sectionTitle\":\"Section\",\"type\":\"General\"},\"modified\":1640403544367,\"location\":\"fbb74cd3-bc17-4913-a47b-6ec7b620c26d\",\"persisted\":1640403544368},\"31c2c6e1-55bd-4339-86f9-8cb1693566d0\":{\"name\":\"Mega Display Layout\",\"type\":\"layout\",\"identifier\":{\"key\":\"31c2c6e1-55bd-4339-86f9-8cb1693566d0\",\"namespace\":\"\"},\"composition\":[],\"configuration\":{\"items\":[],\"layoutGrid\":[10,10]},\"modified\":1640403603545,\"location\":\"fbb74cd3-bc17-4913-a47b-6ec7b620c26d\",\"persisted\":1640403603545}}","mct-tree-expanded":"[]"}

View File

@@ -5,7 +5,7 @@
/** @type {import('@playwright/test').PlaywrightTestConfig} */
const config = {
retries: 0,
testDir: 'tests',
testDir: 'tests/visual',
timeout: 90 * 1000,
workers: 1,
webServer: {
@@ -26,7 +26,6 @@ const config = {
reporter: [
['list'],
['junit', { outputFile: 'test-results/results.xml' }],
['allure-playwright']
]
};

View File

@@ -0,0 +1,122 @@
/*****************************************************************************
* 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.
*****************************************************************************/
/*
This test suite is used for generating localStorage artifacts for visual and performance
tests. This must be generated against app.js
*/
const { test, expect } = require('@playwright/test');
const fs = require('fs');
test('Generate domainObjects and store localstorage as localstorage.json', async ({ page }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
//Click the Create button
await page.click('button:has-text("Create")');
// Click :nth-match(:text("Folder"), 2)
await page.click(':nth-match(:text("Folder"), 2)');
// Fill text=Properties Title Notes >> input[type="text"]
await page.fill('text=Properties Title Notes >> input[type="text"]', 'All DomainObjects');
//await page.fill('input[type="text"]', 'All DomainObjects');
// Click text=OK
await Promise.all([
page.waitForNavigation(/*{ url: 'http://localhost:8080/#/browse/mine/07fa1bd8-1e7d-4b74-bc40-f561f8c00535?tc.mode=fixed&tc.startBound=1640392618958&tc.endBound=1640394418958&tc.timeSystem=utc&view=grid' }*/),
page.click('text=OK')
]);
// Click button:has-text("Create")
await page.click('button:has-text("Create")');
// Click text=Timer
await page.click('text=Timer');
// Click text=Save In My Items All DomainObjects >> input[type="search"]
await page.click('text=Save In My Items All DomainObjects >> input[type="search"]');
// Fill text=Save In My Items All DomainObjects >> input[type="search"]
await page.fill('text=Save In My Items All DomainObjects >> input[type="search"]', 'All DomainObjects');
// Click text=OK
await Promise.all([
page.waitForNavigation(/*{ url: 'http://localhost:8080/#/browse/mine/07fa1bd8-1e7d-4b74-bc40-f561f8c00535/810cc308-76d6-4e64-ab85-cb543c655698?tc.mode=fixed&tc.startBound=1640392618958&tc.endBound=1640394418958&tc.timeSystem=utc&view=timer.view' }*/),
page.click('text=OK')
]);
// Click button:has-text("Create")
await page.click('button:has-text("Create")');
// Click text=Notebook
await page.click('text=Notebook');
// Fill text=Properties Title Notes Entry Sorting Newest First Oldest First Note book Type Se >> input[type="text"]
await page.fill('text=Properties Title Notes Entry Sorting Newest First Oldest First Note book Type Se >> input[type="text"]', 'Notebook');
// Click text=Save In My Items All DomainObjects Unnamed Timer >> input[type="search"]
//await page.click('text=Save In My Items All DomainObjects Unnamed Timer >> input[type="search"]');
// Fill text=Save In My Items All DomainObjects Unnamed Timer >> input[type="search"]
await page.fill('text=Save In My Items All DomainObjects Unnamed Timer >> input[type="search"]', 'All DomainObjects');
// Click ul:nth-child(3) .c-tree__item-h .c-tree__item
await page.click('ul:nth-child(3) .c-tree__item-h .c-tree__item');
// Click button:has-text("OK")
await Promise.all([
page.waitForNavigation(/*{ url: 'http://localhost:8080/#/browse/ROOT/mine/07fa1bd8-1e7d-4b74-bc40-f561f8c00535/6b10425e-2d7f-4f65-83de-35317fbc2493?tc.mode=fixed&tc.startBound=1640392618958&tc.endBound=1640394418958&tc.timeSystem=utc&view=notebook-vue&pageId=9ad5b4ea-8dec-4cc0-b303-43a4f50ac7fa&sectionId=d298175b-d715-426e-8036-b87056e14525' }*/),
page.click('button:has-text("OK")')
]);
await page.pause();
const localStorage = await page.evaluate(() => JSON.stringify(window.localStorage));
fs.writeFileSync('e2e/localstorage.json', localStorage);
});
test('Load localstorage.json and verify contents', async ({ page }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
const localStorage = fs.readFileSync('e2e/localstorage.json', 'utf8')
const deserializedStorage = JSON.parse(localStorage)
await page.evaluate(deserializedStorage => {
for (const key in deserializedStorage) {
localStorage.setItem(key, deserializedStorage[key]);
}
}, deserializedStorage);
await page.reload();
// Expand Default Folder
await page.click('a:has-text("All DomainObjects Folder")');
await expect(page.locator('a:has-text("Unnamed Timer Timer")')).toBeEnabled();
await expect(page.locator('a:has-text("Notebook Notebook")')).toBeEnabled();
});

View File

@@ -44,6 +44,5 @@ test('Verify that the create button appears and that the Folder Domain Object is
await page.click('button:has-text("Create")');
// Verify that Create Folder appears in the dropdown
const locator = page.locator(':nth-match(:text("Folder"), 2)');
await expect(locator).toBeEnabled();
await expect(page.locator(':nth-match(:text("Folder"), 2)')).toBeEnabled();
});

View File

@@ -36,12 +36,13 @@ const { test, expect } = require('@playwright/test');
const percySnapshot = require('@percy/playwright');
const path = require('path');
const sinon = require('sinon');
const fs = require('fs');
const VISUAL_GRACE_PERIOD = 5 * 1000; //Lets the application "simmer" before the snapshot is taken
const VISUAL_GRACE_PERIOD = 1 * 1000; //Lets the application "simmer" before the snapshot is taken
// Snippet from https://github.com/microsoft/playwright/issues/6347#issuecomment-965887758
// Will replace with cy.clock() equivalent
test.beforeEach(async ({ context }) => {
test.beforeEach(async ({ context,page }) => {
await context.addInitScript({
// eslint-disable-next-line no-undef
path: path.join(__dirname, '../../..', './node_modules/sinon/pkg/sinon.js')
@@ -49,11 +50,10 @@ test.beforeEach(async ({ context }) => {
await context.addInitScript(() => {
window.__clock = sinon.useFakeTimers(); //Set browser clock to UNIX Epoch
});
await page.goto('/', { waitUntil: 'networkidle' });
});
test('Visual - Root and About', async ({ page }) => {
// Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
// Verify that Create button is actionable
const createButtonLocator = page.locator('button:has-text("Create")');
@@ -77,8 +77,6 @@ test('Visual - Root and About', async ({ page }) => {
});
test('Visual - Default Condition Set', async ({ page }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
//Click the Create button
await page.click('button:has-text("Create")');
@@ -95,8 +93,6 @@ test('Visual - Default Condition Set', async ({ page }) => {
});
test('Visual - Default Condition Widget', async ({ page }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
//Click the Create button
await page.click('button:has-text("Create")');
@@ -111,3 +107,35 @@ test('Visual - Default Condition Widget', async ({ page }) => {
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
await percySnapshot(page, 'Default Condition Widget');
});
test('Visual - Default Timer', async ({ page }) => {
//Click the Create button
await page.click('button:has-text("Create")');
// Click text=Timer
await page.click('text=Timer');
// Click text=OK
await page.click('text=OK');
// Take a snapshot of the newly created Condition Widget object
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
await percySnapshot(page, 'Default Timer');
});
test('Visual - Default Display Layout', async ({ page }) => {
//Click the Create button
await page.click('button:has-text("Create")');
// Click text=Timer
await page.click('text=Display Layout');
// Click text=OK
await page.click('text=OK');
// Take a snapshot of the newly created Condition Widget object
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
await percySnapshot(page, 'Default Display Layout');
});

View File

@@ -0,0 +1,82 @@
/*****************************************************************************
* 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.
*****************************************************************************/
/*
Collection of Visual Tests set to run from a pre-generated . 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');
const fs = require('fs');
const VISUAL_GRACE_PERIOD = 1 * 1000; //Lets the application "simmer" before the snapshot is taken
// Snippet from https://github.com/microsoft/playwright/issues/6347#issuecomment-965887758
// Will replace with cy.clock() equivalent
test.beforeEach(async ({ context,page }) => {
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(); //Set browser clock to UNIX Epoch
});
await page.goto('/', { waitUntil: 'networkidle' });
const localStorage = fs.readFileSync('e2e/localstorage.json', 'utf8')
const deserializedStorage = JSON.parse(localStorage)
await page.evaluate(deserializedStorage => {
for (const key in deserializedStorage) {
localStorage.setItem(key, deserializedStorage[key]);
}
}, deserializedStorage);
await page.reload();
});
test('Visual - Combined Display Layout', async ({ page }) => {
//Click the Create button
await page.click('button:has-text("Create")');
// Click text=Timer
await page.click('text=Display Layout');
// Click text=OK
await page.click('text=OK');
// Take a snapshot of the newly created Condition Widget object
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
await percySnapshot(page, 'Default Display Layout');
});

View File

@@ -75,6 +75,11 @@
const TWO_HOURS = ONE_HOUR * 2;
const ONE_DAY = ONE_HOUR * 24;
[
'example/eventGenerator'
].forEach(
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
);
openmct.install(openmct.plugins.LocalStorage());

View File

@@ -6,7 +6,7 @@
"@braintree/sanitize-url": "^5.0.2",
"@percy/cli": "^1.0.0-beta.70",
"@percy/playwright": "^1.0.1",
"@playwright/test": "^1.16.3",
"@playwright/test": "^1.17.1",
"allure-playwright": "^2.0.0-beta.14",
"angular": ">=1.8.0",
"angular-route": "1.4.14",
@@ -34,7 +34,7 @@
"html2canvas": "^1.0.0-rc.7",
"imports-loader": "^0.8.0",
"istanbul-instrumenter-loader": "^3.0.1",
"jasmine-core": "^4.0.0",
"jasmine-core": "^3.7.1",
"jsdoc": "^3.3.2",
"karma": "6.3.9",
"karma-chrome-launcher": "3.1.0",
@@ -58,7 +58,7 @@
"moment-timezone": "0.5.28",
"node-bourbon": "^4.2.3",
"painterro": "^1.2.56",
"playwright": "^1.16.3",
"playwright": "^1.17.1",
"plotly.js-basic-dist": "^2.5.0",
"plotly.js-gl2d-dist": "^2.5.0",
"printj": "^1.2.1",
@@ -74,7 +74,7 @@
"v8-compile-cache": "^1.1.0",
"vue": "2.5.6",
"vue-eslint-parser": "8.0.1",
"vue-loader": "15.9.8",
"vue-loader": "^15.2.6",
"vue-template-compiler": "2.5.6",
"webpack": "^5.53.0",
"webpack-cli": "^4.0.0",
@@ -97,8 +97,9 @@
"test:coverage": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" COVERAGE=true karma start --single-run",
"test:coverage:firefox": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run --browsers=FirefoxHeadless",
"test:e2e:ci": "npx playwright test --config=e2e/playwright-ci.config.js smoke",
"test:e2e:generate": "npx playwright test --config=e2e/playwright-local.config.js generateLocalStorage",
"test:e2e:local": "npx playwright test --config=e2e/playwright-local.config.js",
"test:e2e:visual": "percy exec -- npx playwright test --config=e2e/playwright-visual.config.js default",
"test:e2e:visual": "percy exec -- npx playwright test --config=e2e/playwright-visual.config.js",
"test:e2e:full": "npx playwright test --config=e2e/playwright-ci.config.js",
"test:watch": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --no-single-run",
"verify": "concurrently 'npm:test' 'npm:lint'",

View File

@@ -22,17 +22,24 @@
define([
'EventEmitter',
'./BundleRegistry',
'./installDefaultBundles',
'./api/api',
'./api/overlays/OverlayAPI',
'./selection/Selection',
'objectUtils',
'./plugins/plugins',
'./adapter/indicators/legacy-indicators-plugin',
'./ui/registries/ViewRegistry',
'./plugins/imagery/plugin',
'./ui/registries/InspectorViewRegistry',
'./ui/registries/ToolbarRegistry',
'./ui/router/ApplicationRouter',
'./ui/router/Browse',
'../platform/framework/src/Main',
'./ui/layout/Layout.vue',
'../platform/core/src/objects/DomainObjectImpl',
'../platform/core/src/capabilities/ContextualDomainObject',
'./ui/preview/plugin',
'./api/Branding',
'./plugins/licenses/plugin',
@@ -45,17 +52,24 @@ define([
'vue'
], function (
EventEmitter,
BundleRegistry,
installDefaultBundles,
api,
OverlayAPI,
Selection,
objectUtils,
plugins,
LegacyIndicatorsPlugin,
ViewRegistry,
ImageryPlugin,
InspectorViewRegistry,
ToolbarRegistry,
ApplicationRouter,
Browse,
Main,
Layout,
DomainObjectImpl,
ContextualDomainObject,
PreviewPlugin,
BrandingAPI,
LicensesPlugin,
@@ -92,6 +106,23 @@ define([
revision: __OPENMCT_REVISION__,
branch: __OPENMCT_BUILD_BRANCH__
};
/* eslint-enable no-undef */
this.legacyBundle = {
extensions: {
services: [
{
key: "openmct",
implementation: function ($injector) {
this.$injector = $injector;
return this;
}.bind(this),
depends: ['$injector']
}
]
}
};
this.destroy = this.destroy.bind(this);
/**
@@ -231,12 +262,16 @@ define([
this.branding = BrandingAPI.default;
this.legacyRegistry = new BundleRegistry();
installDefaultBundles(this.legacyRegistry);
// Plugins that are installed by default
this.install(this.plugins.Plot());
this.install(this.plugins.Chart());
this.install(this.plugins.TelemetryTable.default());
this.install(PreviewPlugin.default());
this.install(LegacyIndicatorsPlugin());
this.install(LicensesPlugin.default());
this.install(RemoveActionPlugin.default());
this.install(MoveActionPlugin.default());
@@ -268,6 +303,51 @@ define([
MCT.prototype.MCT = MCT;
MCT.prototype.legacyExtension = function (category, extension) {
this.legacyBundle.extensions[category] =
this.legacyBundle.extensions[category] || [];
this.legacyBundle.extensions[category].push(extension);
};
/**
* Return a legacy object, for compatibility purposes only. This method
* will be deprecated and removed in the future.
* @private
*/
MCT.prototype.legacyObject = function (domainObject) {
let capabilityService = this.$injector.get('capabilityService');
function instantiate(model, keyString) {
const capabilities = capabilityService.getCapabilities(model, keyString);
model.id = keyString;
return new DomainObjectImpl(keyString, model, capabilities);
}
if (Array.isArray(domainObject)) {
// an array of domain objects. [object, ...ancestors] representing
// a single object with a given chain of ancestors. We instantiate
// as a single contextual domain object.
return domainObject
.map((o) => {
let keyString = objectUtils.makeKeyString(o.identifier);
let oldModel = objectUtils.toOldFormat(o);
return instantiate(oldModel, keyString);
})
.reverse()
.reduce((parent, child) => {
return new ContextualDomainObject(child, parent);
});
} else {
let keyString = objectUtils.makeKeyString(domainObject.identifier);
let oldModel = objectUtils.toOldFormat(domainObject);
return instantiate(oldModel, keyString);
}
};
/**
* Set path to where assets are hosted. This should be the path to main.js.
* @memberof module:openmct.MCT#
@@ -313,6 +393,25 @@ define([
this.element = domElement;
this.legacyExtension('runs', {
depends: ['navigationService'],
implementation: function (navigationService) {
navigationService
.addListener(this.emit.bind(this, 'navigation'));
}.bind(this)
});
// TODO: remove with legacy types.
this.types.listKeys().forEach(function (typeKey) {
const type = this.types.get(typeKey);
const legacyDefinition = type.toLegacyDefinition();
legacyDefinition.key = typeKey;
this.legacyExtension('types', legacyDefinition);
}.bind(this));
this.legacyRegistry.register('adapter', this.legacyBundle);
this.legacyRegistry.enable('adapter');
this.router.route(/^\/$/, () => {
this.router.setPath('/browse/');
});
@@ -323,27 +422,35 @@ define([
* @event start
* @memberof module:openmct.MCT~
*/
const startPromise = new Main();
startPromise.run(this)
.then(function (angular) {
this.$angular = angular;
// OpenMCT Object provider doesn't operate properly unless
// something has depended upon objectService. Cool, right?
this.$injector.get('objectService');
if (!isHeadlessMode) {
const appLayout = new Vue({
components: {
'Layout': Layout.default
},
provide: {
openmct: this
},
template: '<Layout ref="layout"></Layout>'
});
domElement.appendChild(appLayout.$mount().$el);
if (!isHeadlessMode) {
const appLayout = new Vue({
components: {
'Layout': Layout.default
},
provide: {
openmct: this
},
template: '<Layout ref="layout"></Layout>'
});
domElement.appendChild(appLayout.$mount().$el);
this.layout = appLayout.$refs.layout;
Browse(this);
}
this.layout = appLayout.$refs.layout;
Browse(this);
}
window.addEventListener('beforeunload', this.destroy);
window.addEventListener('beforeunload', this.destroy);
this.router.start();
this.emit('start');
this.router.start();
this.emit('start');
}.bind(this));
};
MCT.prototype.startHeadless = function () {

View File

@@ -22,18 +22,21 @@
define([
'./plugins/plugins',
'legacyRegistry',
'utils/testing'
], function (plugins, testUtils) {
], function (plugins, legacyRegistry, testUtils) {
describe("MCT", function () {
let openmct;
let mockPlugin;
let mockPlugin2;
let mockListener;
let oldBundles;
beforeEach(function () {
mockPlugin = jasmine.createSpy('plugin');
mockPlugin2 = jasmine.createSpy('plugin2');
mockListener = jasmine.createSpy('listener');
oldBundles = legacyRegistry.list();
openmct = testUtils.createOpenMct();
@@ -44,6 +47,12 @@ define([
// Clean up the dirty singleton.
afterEach(function () {
legacyRegistry.list().forEach(function (bundle) {
if (oldBundles.indexOf(bundle) === -1) {
legacyRegistry.delete(bundle);
}
});
return testUtils.resetApplicationState(openmct);
});
@@ -102,6 +111,10 @@ define([
describe("setAssetPath", function () {
let testAssetPath;
beforeEach(function () {
openmct.legacyExtension = jasmine.createSpy('legacyExtension');
});
it("configures the path for assets", function () {
testAssetPath = "some/path/";
openmct.setAssetPath(testAssetPath);

View File

@@ -133,9 +133,5 @@ define([
});
};
CompositionAPI.prototype.supportsComposition = function (domainObject) {
return this.get(domainObject) !== undefined;
};
return CompositionAPI;
});

View File

@@ -87,12 +87,6 @@ define([
expect(composition).toEqual(jasmine.any(CompositionCollection));
});
it('correctly reflects composability', function () {
expect(compositionAPI.supportsComposition(domainObject)).toBe(true);
delete domainObject.composition;
expect(compositionAPI.supportsComposition(domainObject)).toBe(false);
});
it('loads composition from domain object', function () {
const listener = jasmine.createSpy('addListener');
composition.on('add', listener);

View File

@@ -49,10 +49,8 @@ define([
this.onMutation = this.onMutation.bind(this);
this.cannotContainItself = this.cannotContainItself.bind(this);
this.supportsComposition = this.supportsComposition.bind(this);
compositionAPI.addPolicy(this.cannotContainItself);
compositionAPI.addPolicy(this.supportsComposition);
}
/**
@@ -63,13 +61,6 @@ define([
&& parent.identifier.key === child.identifier.key);
};
/**
* @private
*/
DefaultCompositionProvider.prototype.supportsComposition = function (parent, child) {
return this.publicAPI.composition.supportsComposition(parent);
};
/**
* Check if this provider should be used to load composition for a
* particular domain object.

View File

@@ -1,6 +1,5 @@
import AutoCompleteField from './components/controls/AutoCompleteField.vue';
import ClockDisplayFormatField from './components/controls/ClockDisplayFormatField.vue';
import CheckBoxField from './components/controls/CheckBoxField.vue';
import Datetime from './components/controls/Datetime.vue';
import FileInput from './components/controls/FileInput.vue';
import Locator from './components/controls/Locator.vue';
@@ -13,7 +12,6 @@ import Vue from 'vue';
export const DEFAULT_CONTROLS_MAP = {
'autocomplete': AutoCompleteField,
'checkbox': CheckBoxField,
'composite': ClockDisplayFormatField,
'datetime': Datetime,
'file-input': FileInput,

View File

@@ -172,9 +172,7 @@ export default class FormsAPI {
function onFormSave(save) {
return () => {
if (overlay) {
overlay.dismiss();
}
overlay.dismiss();
if (save) {
save(changes);

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2021, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -24,60 +24,49 @@
<div class="c-form">
<div class="c-overlay__top-bar c-form__top-bar">
<div class="c-overlay__dialog-title">{{ model.title }}</div>
<div
v-if="hasRequiredFields"
class="c-overlay__dialog-hint hint"
>All fields marked <span class="req icon-asterisk"></span> are required.</div>
<div class="c-overlay__dialog-hint hint">All fields marked <span class="req icon-asterisk"></span> are required.</div>
</div>
<form
name="mctForm"
class="c-form__contents"
autocomplete="off"
@submit.prevent
<form name="mctForm"
class="c-form__contents"
autocomplete="off"
@submit.prevent
>
<div
v-for="section in formSections"
:key="section.id"
class="c-form__section"
:class="section.cssClass"
<div v-for="section in formSections"
:key="section.id"
class="c-form__section"
:class="section.cssClass"
>
<h2
v-if="section.name"
<h2 v-if="section.name"
class="c-form__section-header"
>
{{ section.name }}
</h2>
<div
v-for="(row, index) in section.rows"
:key="row.id"
class="u-contents"
<div v-for="(row, index) in section.rows"
:key="row.id"
class="u-contents"
>
<FormRow
:css-class="section.cssClass"
:first="index < 1"
:row="row"
@onChange="onChange"
<FormRow :css-class="section.cssClass"
:first="index < 1"
:row="row"
@onChange="onChange"
/>
</div>
</div>
</form>
<div class="mct-form__controls c-overlay__button-bar c-form__bottom-bar">
<button
tabindex="0"
:disabled="isInvalid"
class="c-button c-button--major"
@click="onSave"
<button tabindex="0"
:disabled="isInvalid"
class="c-button c-button--major"
@click="onSave"
>
{{ submitLabel }}
OK
</button>
<button
v-if="!hideCancel"
tabindex="0"
class="c-button"
@click="onDismiss"
<button tabindex="0"
class="c-button"
@click="onDismiss"
>
{{ cancelLabel }}
Cancel
</button>
</div>
</div>
@@ -111,42 +100,11 @@ export default {
};
},
computed: {
hasRequiredFields() {
return this.model.sections.some(section =>
section.rows.some(row => row.required));
},
isInvalid() {
return Object.entries(this.invalidProperties)
.some(([key, value]) => {
return value;
});
},
submitLabel() {
if (
this.model.buttons
&& this.model.buttons.submit
&& this.model.buttons.submit.label
) {
return this.model.buttons.submit.label;
}
return 'OK';
},
cancelLabel() {
if (
this.model.buttons
&& this.model.buttons.cancel
&& this.model.buttons.cancel.label
) {
return this.model.buttons.submit.label;
}
return 'Cancel';
},
hideCancel() {
return this.model.buttons
&& this.model.buttons.cancel
&& this.model.buttons.cancel.hide === true;
}
},
mounted() {

View File

@@ -75,12 +75,10 @@ export default {
rowClass() {
let cssClass = this.cssClass;
if (!this.row.required) {
return;
if (this.row.required) {
cssClass = `${cssClass} req`;
}
cssClass = `${cssClass} req`;
if (this.visited && this.valid !== undefined) {
if (this.valid === true) {
cssClass = `${cssClass} valid`;

View File

@@ -1,62 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
<template>
<span class="form-control shell">
<span
class="field control"
:class="model.cssClass"
>
<input
type="checkbox"
:checked="isChecked"
@input="toggleCheckBox"
>
</span>
</span>
</template>
<script>
export default {
props: {
model: {
type: Object,
required: true
}
},
data() {
return {
isChecked: this.model.value
};
},
methods: {
toggleCheckBox() {
this.isChecked = !this.isChecked;
const data = {
model: this.model,
value: this.isChecked
};
this.$emit('onChange', data);
}
}
};
</script>

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2021, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -36,14 +36,13 @@ class InMemorySearchProvider {
*/
this.MAX_CONCURRENT_REQUESTS = 100;
/**
* If max results is not specified in query, use this as default.
*/
* If max results is not specified in query, use this as default.
*/
this.DEFAULT_MAX_RESULTS = 100;
this.openmct = openmct;
this.indexedIds = {};
this.indexedCompositions = {};
this.idsToIndex = [];
this.pendingIndex = {};
this.pendingRequests = 0;
@@ -59,6 +58,7 @@ class InMemorySearchProvider {
this.onWorkerMessageError = this.onWorkerMessageError.bind(this);
this.onerror = this.onWorkerError.bind(this);
this.startIndexing = this.startIndexing.bind(this);
this.onMutationOfIndexedObject = this.onMutationOfIndexedObject.bind(this);
this.openmct.on('start', this.startIndexing);
this.openmct.on('destroy', () => {
@@ -68,9 +68,6 @@ class InMemorySearchProvider {
this.worker.port.onmessageerror = null;
this.worker.port.close();
}
this.destroyObservers(this.indexedIds);
this.destroyObservers(this.indexedCompositions);
});
}
@@ -140,7 +137,7 @@ class InMemorySearchProvider {
};
modelResults.hits = await Promise.all(event.data.results.map(async (hit) => {
const identifier = this.openmct.objects.parseKeyString(hit.keyString);
const domainObject = await this.openmct.objects.get(identifier);
const domainObject = await this.openmct.objects.get(identifier.key);
return domainObject;
}));
@@ -216,52 +213,29 @@ class InMemorySearchProvider {
}
}
onNameMutation(domainObject, name) {
onMutationOfIndexedObject(domainObject) {
const provider = this;
domainObject.name = name;
provider.index(domainObject);
}
onCompositionMutation(domainObject, composition) {
const provider = this;
const indexedComposition = domainObject.composition;
const identifiersToIndex = composition
.filter(identifier => !indexedComposition
.some(indexedIdentifier => this.openmct.objects
.areIdsEqual([identifier, indexedIdentifier])));
identifiersToIndex.forEach(identifier => {
this.openmct.objects.get(identifier).then(objectToIndex => provider.index(objectToIndex));
});
provider.index(domainObject.identifier, domainObject);
}
/**
* Pass a domainObject to the worker to be indexed.
* If the object has composition, schedule those ids for later indexing.
* Watch for object changes and re-index object and children if so
* Pass an id and model to the worker to be indexed. If the model has
* composition, schedule those ids for later indexing.
*
* @private
* @param domainObject a domainObject
* @param id a model id
* @param model a model
*/
async index(domainObject) {
async index(id, domainObject) {
const provider = this;
const keyString = this.openmct.objects.makeKeyString(domainObject.identifier);
const keyString = this.openmct.objects.makeKeyString(id);
if (!this.indexedIds[keyString]) {
this.indexedIds[keyString] = this.openmct.objects.observe(
domainObject,
'name',
this.onNameMutation.bind(this, domainObject)
);
this.indexedCompositions[keyString] = this.openmct.objects.observe(
domainObject,
'composition',
this.onCompositionMutation.bind(this, domainObject)
);
this.openmct.objects.observe(domainObject, `*`, this.onMutationOfIndexedObject);
}
if ((keyString !== 'ROOT')) {
this.indexedIds[keyString] = true;
if ((id.key !== 'ROOT')) {
if (this.worker) {
this.worker.port.postMessage({
request: 'index',
@@ -273,12 +247,15 @@ class InMemorySearchProvider {
}
}
const composition = this.openmct.composition.get(domainObject);
const composition = this.openmct.composition.registry.find(foundComposition => {
return foundComposition.appliesTo(domainObject);
});
if (composition !== undefined) {
const children = await composition.load();
children.forEach(child => provider.scheduleForIndexing(child.identifier));
if (composition) {
const childIdentifiers = await composition.load(domainObject);
childIdentifiers.forEach(function (childIdentifier) {
provider.scheduleForIndexing(childIdentifier);
});
}
}
@@ -294,12 +271,12 @@ class InMemorySearchProvider {
const provider = this;
this.pendingRequests += 1;
const domainObject = await this.openmct.objects.get(keyString);
const identifier = await this.openmct.objects.parseKeyString(keyString);
const domainObject = await this.openmct.objects.get(identifier.key);
delete provider.pendingIndex[keyString];
try {
if (domainObject) {
await provider.index(domainObject);
await provider.index(identifier, domainObject);
}
} catch (error) {
console.warn('Failed to index domain object ' + keyString, error);
@@ -328,9 +305,9 @@ class InMemorySearchProvider {
}
/**
* A local version of the same SharedWorker function
* if we don't have SharedWorkers available (e.g., iOS)
*/
* A local version of the same SharedWorker function
* if we don't have SharedWorkers available (e.g., iOS)
*/
localIndexItem(keyString, model) {
this.localIndexedItems[keyString] = {
type: model.type,
@@ -370,16 +347,6 @@ class InMemorySearchProvider {
};
this.onWorkerMessage(eventToReturn);
}
destroyObservers(observers) {
Object.entries(observers).forEach(([keyString, unobserve]) => {
if (typeof unobserve === 'function') {
unobserve();
}
delete observers[keyString];
});
}
}
export default InMemorySearchProvider;

View File

@@ -33,10 +33,8 @@
port.onmessage = function (event) {
if (event.data.request === 'index') {
console.log('onmessage index: ', event.data);
indexItem(event.data.keyString, event.data.model);
} else if (event.data.request === 'search') {
console.log('onmessage search: ', event.data);
port.postMessage(search(event.data));
}
};
@@ -79,8 +77,6 @@
queryId: data.queryId
};
console.log('indexed on search: ', indexedItems);
results = Object.values(indexedItems).filter((indexedItem) => {
return indexedItem.name.toLowerCase().includes(input);
});

View File

@@ -55,11 +55,6 @@ define([
*/
function parseKeyString(keyString) {
if (isIdentifier(keyString)) {
// hack to workaround a bug mashing keyString into identifier.key
if (!keyString.namespace && keyString.key.includes(':')) {
return parseKeyString(keyString.key);
}
return keyString;
}

View File

@@ -25,7 +25,6 @@ import EventEmitter from 'EventEmitter';
const ERRORS = {
TIMESYSTEM_KEY: 'All telemetry metadata must have a telemetry value with a key that matches the key of the active time system.',
TIMESYSTEM_KEY_NOTIFICATION: 'Telemetry metadata does not match the active time system.',
LOADED: 'Telemetry Collection has already been loaded.'
};
@@ -267,10 +266,6 @@ export class TelemetryCollection extends EventEmitter {
this.lastBounds = bounds;
if (isTick) {
if (this.timeKey === undefined) {
return;
}
// need to check futureBuffer and need to check
// if anything has fallen out of bounds
let startIndex = 0;
@@ -310,6 +305,7 @@ export class TelemetryCollection extends EventEmitter {
if (added.length > 0) {
this.emit('add', added);
}
} else {
// user bounds change, reset
this._reset();
@@ -329,14 +325,12 @@ export class TelemetryCollection extends EventEmitter {
let domains = this.metadata.valuesForHints(['domain']);
let domain = domains.find((d) => d.key === timeSystem.key);
if (domain !== undefined) {
// timeKey is used to create a dummy datum used for sorting
this.timeKey = domain.source;
} else {
this._warn(ERRORS.TIMESYSTEM_KEY);
this.openmct.notifications.alert(ERRORS.TIMESYSTEM_KEY_NOTIFICATION)
if (domain === undefined) {
this._error(ERRORS.TIMESYSTEM_KEY);
}
// timeKey is used to create a dummy datum used for sorting
this.timeKey = domain.source; // this defaults to key if no source is set
let metadataValue = this.metadata.value(timeSystem.key) || { format: timeSystem.key };
let valueFormatter = this.openmct.telemetry.getValueFormatter(metadataValue);
@@ -406,8 +400,4 @@ export class TelemetryCollection extends EventEmitter {
_error(message) {
throw new Error(message);
}
_warn(message) {
console.warn(message);
}
}

View File

@@ -43,42 +43,42 @@ const DEFAULTS = [
];
define([
'../../adapter/bundle',
'../../../example/eventGenerator/bundle',
'../../../example/export/bundle',
'../../../example/forms/bundle',
'../../../example/identity/bundle',
'../../../example/mobile/bundle',
'../../../example/msl/bundle',
'../../../example/notifications/bundle',
'../../../example/persistence/bundle',
'../../../example/policy/bundle',
'../../../example/profiling/bundle',
'../../../example/scratchpad/bundle',
'../../../example/styleguide/bundle',
'../../../platform/commonUI/browse/bundle',
'../../../platform/commonUI/dialog/bundle',
'../../../platform/commonUI/edit/bundle',
'../../../platform/commonUI/general/bundle',
'../../../platform/commonUI/inspect/bundle',
'../../../platform/commonUI/mobile/bundle',
'../../../platform/commonUI/notification/bundle',
'../../../platform/commonUI/regions/bundle',
'../../../platform/containment/bundle',
'../../../platform/core/bundle',
'../../../platform/entanglement/bundle',
'../../../platform/exporters/bundle',
'../../../platform/features/static-markup/bundle',
'../../../platform/framework/bundle',
'../../../platform/framework/src/load/Bundle',
'../../../platform/identity/bundle',
'../../../platform/persistence/aggregator/bundle',
'../../../platform/persistence/elastic/bundle',
'../../../platform/persistence/queue/bundle',
'../../../platform/policy/bundle',
'../../../platform/representation/bundle',
'../../../platform/status/bundle',
'../../../platform/telemetry/bundle'
'../src/adapter/bundle',
'../example/eventGenerator/bundle',
'../example/export/bundle',
'../example/forms/bundle',
'../example/identity/bundle',
'../example/mobile/bundle',
'../example/msl/bundle',
'../example/notifications/bundle',
'../example/persistence/bundle',
'../example/policy/bundle',
'../example/profiling/bundle',
'../example/scratchpad/bundle',
'../example/styleguide/bundle',
'../platform/commonUI/browse/bundle',
'../platform/commonUI/dialog/bundle',
'../platform/commonUI/edit/bundle',
'../platform/commonUI/general/bundle',
'../platform/commonUI/inspect/bundle',
'../platform/commonUI/mobile/bundle',
'../platform/commonUI/notification/bundle',
'../platform/commonUI/regions/bundle',
'../platform/containment/bundle',
'../platform/core/bundle',
'../platform/entanglement/bundle',
'../platform/exporters/bundle',
'../platform/features/static-markup/bundle',
'../platform/framework/bundle',
'../platform/framework/src/load/Bundle',
'../platform/identity/bundle',
'../platform/persistence/aggregator/bundle',
'../platform/persistence/elastic/bundle',
'../platform/persistence/queue/bundle',
'../platform/policy/bundle',
'../platform/representation/bundle',
'../platform/status/bundle',
'../platform/telemetry/bundle'
], function () {
const LEGACY_BUNDLES = Array.from(arguments);

View File

@@ -31,7 +31,7 @@ export default class LADTableViewProvider {
}
canView(domainObject) {
const supportsComposition = this.openmct.composition.supportsComposition(domainObject);
const supportsComposition = this.openmct.composition.get(domainObject) !== undefined;
const providesTelemetry = this.openmct.telemetry.isTelemetryObject(domainObject);
return domainObject.type === 'LadTable'

View File

@@ -130,6 +130,14 @@ describe("the plugin", function () {
let mockComposition;
beforeEach(async () => {
const getFunc = openmct.$injector.get;
spyOn(openmct.$injector, "get")
.withArgs("exportImageService").and.returnValue({
exportPNG: () => {},
exportJPG: () => {}
})
.and.callFake(getFunc);
barGraphObject = {
identifier: {
namespace: "",

View File

@@ -87,7 +87,6 @@ describe("Clock plugin:", () => {
spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve(clockViewObject));
spyOn(openmct.objects, 'save').and.returnValue(Promise.resolve(true));
spyOn(openmct.objects, 'supportsMutation').and.returnValue(true);
const applicableViews = openmct.objectViews.get(clockViewObject, [clockViewObject]);
clockViewProvider = applicableViews.find(viewProvider => viewProvider.key === 'clock.view');

View File

@@ -0,0 +1,34 @@
/*****************************************************************************
* 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.
*****************************************************************************/
function ConditionSetViewPolicy() {
}
ConditionSetViewPolicy.prototype.allow = function (view, domainObject) {
if (domainObject.getModel().type === 'conditionSet') {
return view.key === 'conditionSet.view';
}
return true;
};
export default ConditionSetViewPolicy;

View File

@@ -39,10 +39,8 @@ export default class ConditionSetViewProvider {
return isConditionSet && this.openmct.router.isNavigatedObject(objectPath);
}
canEdit(domainObject, objectPath) {
const isConditionSet = domainObject.type === 'conditionSet';
return isConditionSet && this.openmct.router.isNavigatedObject(objectPath);
canEdit(domainObject) {
return domainObject.type === 'conditionSet';
}
view(domainObject, objectPath) {

View File

@@ -23,6 +23,7 @@ import ConditionSetViewProvider from './ConditionSetViewProvider.js';
import ConditionSetCompositionPolicy from "./ConditionSetCompositionPolicy";
import ConditionSetMetadataProvider from './ConditionSetMetadataProvider';
import ConditionSetTelemetryProvider from './ConditionSetTelemetryProvider';
import ConditionSetViewPolicy from './ConditionSetViewPolicy';
import uuid from "uuid";
export default function ConditionPlugin() {
@@ -54,8 +55,11 @@ export default function ConditionPlugin() {
domainObject.telemetry = {};
}
});
let compositionPolicy = new ConditionSetCompositionPolicy(openmct);
openmct.composition.addPolicy(compositionPolicy.allow.bind(compositionPolicy));
openmct.legacyExtension('policies', {
category: 'view',
implementation: ConditionSetViewPolicy
});
openmct.composition.addPolicy(new ConditionSetCompositionPolicy(openmct).allow);
openmct.telemetry.addProvider(new ConditionSetMetadataProvider(openmct));
openmct.telemetry.addProvider(new ConditionSetTelemetryProvider(openmct));
openmct.objectViews.addProvider(new ConditionSetViewProvider(openmct));

View File

@@ -56,7 +56,7 @@ a.c-condition-widget {
}
// When the widget is in the main view, center it in the space
.l-shell__main-container > * > .c-condition-widget {
.l-shell__main-container > .c-condition-widget {
position: absolute;
top: 50%;
left: 50%;

View File

@@ -66,11 +66,10 @@ export default class CreateAction extends PropertiesAction {
});
const parentDomainObject = parentDomainObjectPath[0];
const namespace = parentDomainObject.identifier.namespace || parentDomainObject.key || '';
this.domainObject.modified = Date.now();
this.domainObject.location = this.openmct.objects.makeKeyString(parentDomainObject.identifier);
this.domainObject.identifier.namespace = namespace;
this.domainObject.identifier.namespace = parentDomainObject.identifier.namespace;
// Show saving progress dialog
let dialog = this.openmct.overlays.progressDialog({
@@ -100,7 +99,6 @@ export default class CreateAction extends PropertiesAction {
*/
async _navigateAndEdit(domainObject, parentDomainObjectpath) {
let objectPath;
let self = this;
if (parentDomainObjectpath) {
objectPath = parentDomainObjectpath && [domainObject].concat(parentDomainObjectpath);
} else {
@@ -108,22 +106,17 @@ export default class CreateAction extends PropertiesAction {
}
const url = '#/browse/' + objectPath
.map(object => object && this.openmct.objects.makeKeyString(object.identifier))
.map(object => object && this.openmct.objects.makeKeyString(object.identifier.key))
.reverse()
.join('/');
function editObject() {
const objectView = self.openmct.objectViews.get(domainObject, objectPath)[0];
const canEdit = objectView && objectView.canEdit && objectView.canEdit(domainObject, objectPath);
if (canEdit) {
self.openmct.editor.edit();
}
}
this.openmct.router.once('afterNavigation', editObject);
this.openmct.router.navigate(url);
const objectView = this.openmct.objectViews.get(domainObject, objectPath)[0];
const canEdit = objectView && objectView.canEdit && objectView.canEdit(domainObject, objectPath);
if (canEdit) {
this.openmct.editor.edit();
}
}
/**

View File

@@ -65,8 +65,13 @@ export default {
keyString: undefined
};
},
computed: {
imageHistorySize() {
return this.imageHistory.length;
}
},
watch: {
imageHistory(newHistory, oldHistory) {
imageHistorySize(newSize, oldSize) {
this.updatePlotImagery();
}
},

View File

@@ -240,6 +240,9 @@ export default {
};
},
computed: {
imageHistorySize() {
return this.imageHistory.length;
},
compassRoseSizingClasses() {
let compassRoseSizingClasses = '';
if (this.sizedImageDimensions.width < 300) {
@@ -406,23 +409,19 @@ export default {
}
},
watch: {
imageHistory: {
handler(newHistory, oldHistory) {
const newSize = newHistory.length;
let imageIndex;
if (this.focusedImageTimestamp !== undefined) {
const foundImageIndex = this.imageHistory.findIndex(image => {
return image.time === this.focusedImageTimestamp;
});
imageIndex = foundImageIndex > -1 ? foundImageIndex : newSize - 1;
} else {
imageIndex = newSize > 0 ? newSize - 1 : undefined;
}
imageHistorySize(newSize, oldSize) {
let imageIndex;
if (this.focusedImageTimestamp !== undefined) {
const foundImageIndex = this.imageHistory.findIndex(image => {
return image.time === this.focusedImageTimestamp;
});
imageIndex = foundImageIndex > -1 ? foundImageIndex : newSize - 1;
} else {
imageIndex = newSize > 0 ? newSize - 1 : undefined;
}
this.setFocusedImage(imageIndex, false);
this.scrollToRight();
},
deep: true
this.setFocusedImage(imageIndex, false);
this.scrollToRight();
},
focusedImageIndex() {
this.trackDuration();
@@ -511,6 +510,12 @@ export default {
this.timeContext.off("clock", this.trackDuration);
}
},
boundsChange(bounds, isTick) {
if (!isTick) {
this.previousFocusedImage = this.focusedImage ? JSON.parse(JSON.stringify(this.focusedImage)) : undefined;
this.requestHistory();
}
},
expand() {
const actionCollection = this.openmct.actions.getActionsCollection(this.objectPath, this.currentView);
const visibleActions = actionCollection.getVisibleActions();
@@ -685,32 +690,22 @@ export default {
return;
}
if (this.previousFocusedImage) {
// determine if the previous image exists in the new bounds of imageHistory
const matchIndex = this.matchIndexOfPreviousImage(
this.previousFocusedImage,
this.imageHistory
);
focusedIndex = matchIndex > -1 ? matchIndex : this.imageHistory.length - 1;
delete this.previousFocusedImage;
}
if (thumbnailClick) {
//We use the props till the user changes what they want to see
this.focusedImageTimestamp = undefined;
//set the previousFocusedImage when a user chooses an image
this.previousFocusedImage = this.imageHistory[focusedIndex] ? JSON.parse(JSON.stringify(this.imageHistory[focusedIndex])) : undefined;
}
if (this.previousFocusedImage) {
// determine if the previous image exists in the new bounds of imageHistory
if (!thumbnailClick) {
const matchIndex = this.matchIndexOfPreviousImage(
this.previousFocusedImage,
this.imageHistory
);
focusedIndex = matchIndex > -1 ? matchIndex : this.imageHistory.length - 1;
}
if (!(this.isPaused || thumbnailClick)
|| focusedIndex === this.imageHistory.length - 1) {
delete this.previousFocusedImage;
}
}
this.focusedImageIndex = focusedIndex;
//TODO: do we even need this anymore?
if (this.isPaused && !thumbnailClick && this.focusedImageTimestamp === undefined) {
this.nextImageIndex = focusedIndex;
//this could happen if bounds changes
@@ -721,6 +716,8 @@ export default {
return;
}
this.focusedImageIndex = focusedIndex;
if (thumbnailClick && !this.isPaused) {
this.paused(true);
}

View File

@@ -120,15 +120,9 @@ export default {
return this.timeFormatter.parse(datum);
},
boundsChange(bounds, isTick) {
if (isTick) {
return;
if (!isTick) {
this.requestHistory();
}
// forcibly reset the imageContainer size to prevent an aspect ratio distortion
delete this.imageContainerWidth;
delete this.imageContainerHeight;
return this.requestHistory();
},
async requestHistory() {
let bounds = this.timeContext.bounds();

View File

@@ -1,126 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
import installDefaultBundles from './installDefaultBundles';
import BundleRegistry from './BundleRegistry';
import Main from '../../../platform/framework/src/Main';
import objectUtils from '../../api/objects/object-utils';
import DomainObjectImpl from '../../../platform/core/src/objects/DomainObjectImpl';
import ContextualDomainObject from '../../../platform/core/src/capabilities/ContextualDomainObject';
export default function LegacySupportPlugin() {
return function install(openmct) {
openmct.legacyBundle = {
extensions: {
services: [
{
key: "openmct",
implementation: function ($injector) {
openmct.$injector = $injector;
return openmct;
},
depends: ['$injector']
}
]
}
};
openmct.legacyExtension = function (category, extension) {
this.legacyBundle.extensions[category] =
this.legacyBundle.extensions[category] || [];
this.legacyBundle.extensions[category].push(extension);
}.bind(openmct);
/**
* Return a legacy object, for compatibility purposes only. This method
* will be deprecated and removed in the future.
* @private
*/
openmct.legacyObject = function (domainObject) {
let capabilityService = this.$injector.get('capabilityService');
function instantiate(model, keyString) {
const capabilities = capabilityService.getCapabilities(model, keyString);
model.id = keyString;
return new DomainObjectImpl(keyString, model, capabilities);
}
if (Array.isArray(domainObject)) {
// an array of domain objects. [object, ...ancestors] representing
// a single object with a given chain of ancestors. We instantiate
// as a single contextual domain object.
return domainObject
.map((o) => {
let keyString = objectUtils.makeKeyString(o.identifier);
let oldModel = objectUtils.toOldFormat(o);
return instantiate(oldModel, keyString);
})
.reverse()
.reduce((parent, child) => {
return new ContextualDomainObject(child, parent);
});
} else {
let keyString = objectUtils.makeKeyString(domainObject.identifier);
let oldModel = objectUtils.toOldFormat(domainObject);
return instantiate(oldModel, keyString);
}
}.bind(openmct);
openmct.legacyRegistry = new BundleRegistry();
installDefaultBundles(openmct.legacyRegistry);
const patchedStart = openmct.start.bind(openmct);
openmct.start = async () => {
openmct.legacyRegistry.register('adapter', openmct.legacyBundle);
openmct.legacyRegistry.enable('adapter');
openmct.legacyExtension('runs', {
depends: ['navigationService'],
implementation: function (navigationService) {
navigationService
.addListener(openmct.emit.bind(openmct, 'navigation'));
}
});
// TODO: remove with legacy types.
openmct.types.listKeys().forEach(function (typeKey) {
const type = openmct.types.get(typeKey);
const legacyDefinition = type.toLegacyDefinition();
legacyDefinition.key = typeKey;
openmct.legacyExtension('types', legacyDefinition);
});
const main = new Main();
const angularInstance = await main.run(openmct);
openmct.$angular = angularInstance;
openmct.$injector.get('objectService');
return patchedStart();
};
};
}

View File

@@ -91,11 +91,11 @@ export default class LinkAction {
}
validate(currentParent) {
return (data) => {
const parentCandidate = data.value[0];
return (object, data) => {
const parentCandidate = data.value;
const currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier);
const parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier);
const objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier);
const objectKeystring = this.openmct.objects.makeKeyString(object.identifier);
if (!parentCandidateKeystring || !currentParentKeystring) {
return false;
@@ -114,7 +114,7 @@ export default class LinkAction {
return false;
}
return parentCandidate && this.openmct.composition.checkPolicy(parentCandidate, this.object);
return parentCandidate && this.openmct.composition.checkPolicy(parentCandidate, object);
};
}
}

View File

@@ -140,13 +140,11 @@ export default class MoveAction {
}
validate(currentParent) {
return (data) => {
const parentCandidatePath = data.value;
const parentCandidate = parentCandidatePath[0];
return (object, data) => {
const parentCandidate = data.value;
let currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier);
let parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier);
let objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier);
let objectKeystring = this.openmct.objects.makeKeyString(object.identifier);
if (!parentCandidateKeystring || !currentParentKeystring) {
return false;
@@ -165,7 +163,7 @@ export default class MoveAction {
return false;
}
return parentCandidate && this.openmct.composition.checkPolicy(parentCandidate, this.object);
return parentCandidate && this.openmct.composition.checkPolicy(parentCandidate, object);
};
}

View File

@@ -22,14 +22,14 @@
import { MY_ITEMS_KEY } from "./createMyItemsIdentifier";
function myItemsInterceptor(openmct, identifierObject, name) {
function myItemsInterceptor(identifierObject, openmct) {
const myItemsModel = {
identifier: identifierObject,
name,
type: "folder",
composition: [],
location: "ROOT"
"name": "My Items",
"type": "folder",
"composition": [],
"location": "ROOT"
};
return {

View File

@@ -23,13 +23,11 @@
import { createMyItemsIdentifier } from "./createMyItemsIdentifier";
import myItemsInterceptor from "./myItemsInterceptor";
const MY_ITEMS_DEFAULT_NAME = 'My Items';
export default function MyItemsPlugin(name = MY_ITEMS_DEFAULT_NAME, namespace = '') {
export default function MyItemsPlugin(namespace = '') {
return function install(openmct) {
const identifier = createMyItemsIdentifier(namespace);
openmct.objects.addGetInterceptor(myItemsInterceptor(openmct, identifier, name));
openmct.objects.addGetInterceptor(myItemsInterceptor(identifier, openmct));
openmct.objects.addRoot(identifier);
};
}

View File

@@ -30,8 +30,6 @@ import {
} from './createMyItemsIdentifier';
const MISSING_NAME = `Missing: ${MY_ITEMS_KEY}`;
const DEFAULT_NAME = 'My Items';
const FANCY_NAME = 'Fancy Items';
const myItemsIdentifier = createMyItemsIdentifier();
describe("the plugin", () => {
@@ -42,82 +40,53 @@ describe("the plugin", () => {
name: MISSING_NAME
};
describe('with no arguments passed in', () => {
beforeEach((done) => {
openmct = createOpenMct();
beforeEach((done) => {
openmct = createOpenMct();
openmct.install(openmct.plugins.MyItems());
openmct.on('start', done);
openmct.startHeadless();
});
afterEach(() => {
return resetApplicationState(openmct);
});
it('when installed, adds "My Items" to the root', async () => {
const root = await openmct.objects.get('ROOT');
const rootCompostionCollection = openmct.composition.get(root);
const rootCompostion = await rootCompostionCollection.load();
let myItems = rootCompostion.filter((domainObject) => {
return openmct.objects.areIdsEqual(domainObject.identifier, myItemsIdentifier);
})[0];
expect(myItems.name).toBe(DEFAULT_NAME);
expect(myItems).toBeDefined();
});
describe('adds an interceptor that returns a "My Items" model for', () => {
let myItemsMissing;
let mockMissingProvider;
let activeProvider;
beforeEach(async () => {
mockMissingProvider = {
get: () => Promise.resolve(missingObj),
create: () => Promise.resolve(missingObj),
update: () => Promise.resolve(missingObj)
};
activeProvider = mockMissingProvider;
spyOn(openmct.objects, 'getProvider').and.returnValue(activeProvider);
myItemsMissing = await openmct.objects.get(myItemsIdentifier);
});
it('missing objects', () => {
let idsMatchMissing = openmct.objects.areIdsEqual(myItemsMissing.identifier, myItemsIdentifier);
expect(myItemsMissing).toBeDefined();
expect(idsMatchMissing).toBeTrue();
});
});
openmct.install(openmct.plugins.MyItems());
openmct.on('start', done);
openmct.startHeadless();
});
describe('with a name argument passed in', () => {
afterEach(() => {
return resetApplicationState(openmct);
});
beforeEach((done) => {
openmct = createOpenMct();
openmct.install(openmct.plugins.MyItems(FANCY_NAME));
it('when installed, adds "My Items" to the root', async () => {
const root = await openmct.objects.get('ROOT');
const rootCompostionCollection = openmct.composition.get(root);
const rootCompostion = await rootCompostionCollection.load();
let myItems = rootCompostion.filter((domainObject) => {
return openmct.objects.areIdsEqual(domainObject.identifier, myItemsIdentifier);
})[0];
spyOn(openmct.objects, 'isMissing').and.returnValue(true);
expect(myItems).toBeDefined();
});
openmct.on('start', done);
openmct.startHeadless();
describe('adds an interceptor that returns a "My Items" model for', () => {
let myItemsMissing;
let mockMissingProvider;
let activeProvider;
beforeEach(async () => {
mockMissingProvider = {
get: () => Promise.resolve(missingObj),
create: () => Promise.resolve(missingObj),
update: () => Promise.resolve(missingObj)
};
activeProvider = mockMissingProvider;
spyOn(openmct.objects, 'getProvider').and.returnValue(activeProvider);
myItemsMissing = await openmct.objects.get(myItemsIdentifier);
});
afterEach(() => {
return resetApplicationState(openmct);
it('missing objects', () => {
let idsMatchMissing = openmct.objects.areIdsEqual(myItemsMissing.identifier, myItemsIdentifier);
expect(myItemsMissing).toBeDefined();
expect(idsMatchMissing).toBeTrue();
});
it('when installed, uses the passed in name', async () => {
let myItems = await openmct.objects.get(myItemsIdentifier);
expect(myItems.name).toBe(FANCY_NAME);
expect(myItems).toBeDefined();
});
});
});

View File

@@ -142,6 +142,7 @@ import { clearDefaultNotebook, getDefaultNotebook, setDefaultNotebook, setDefaul
import { addNotebookEntry, createNewEmbed, getEntryPosById, getNotebookEntries, mutateObject } from '../utils/notebook-entries';
import { saveNotebookImageDomainObject, updateNamespaceOfDomainObject } from '../utils/notebook-image';
import { NOTEBOOK_VIEW_TYPE } from '../notebook-constants';
import objectUtils from 'objectUtils';
import { debounce } from 'lodash';
import objectLink from '../../../ui/mixins/object-link';
@@ -454,6 +455,11 @@ export default {
? getDefaultNotebook().defaultSectionId
: undefined;
},
getDefaultNotebookObject() {
const defaultNotebook = getDefaultNotebook();
return defaultNotebook && this.openmct.objects.get(defaultNotebook.identifier);
},
getLinktoNotebook() {
const objectPath = this.openmct.router.path;
const link = objectLink.computed.objectLink.call({
@@ -613,12 +619,12 @@ export default {
this.sectionsChanged({ sections });
},
removeDefaultClass(defaultNotebookIdentifier) {
if (!defaultNotebookIdentifier) {
removeDefaultClass(domainObject) {
if (!domainObject) {
return;
}
this.openmct.status.delete(defaultNotebookIdentifier);
this.openmct.status.delete(domainObject.identifier);
},
resetSearch() {
this.search = '';
@@ -627,16 +633,15 @@ export default {
toggleNav() {
this.showNav = !this.showNav;
},
updateDefaultNotebook(notebookStorage) {
const defaultNotebook = getDefaultNotebook();
const defaultNotebookIdentifier = defaultNotebook && defaultNotebook.identifier;
const isSameNotebook = defaultNotebookIdentifier
&& this.openmct.objects.areIdsEqual(defaultNotebookIdentifier, notebookStorage.identifier);
async updateDefaultNotebook(notebookStorage) {
const defaultNotebookObject = await this.getDefaultNotebookObject();
const isSameNotebook = defaultNotebookObject
&& objectUtils.makeKeyString(defaultNotebookObject.identifier) === objectUtils.makeKeyString(notebookStorage.identifier);
if (!isSameNotebook) {
this.removeDefaultClass(defaultNotebookIdentifier);
this.removeDefaultClass(defaultNotebookObject);
}
if (!defaultNotebookIdentifier || !isSameNotebook) {
if (!defaultNotebookObject || !isSameNotebook) {
setDefaultNotebook(this.openmct, notebookStorage, this.domainObject);
}

View File

@@ -110,8 +110,7 @@ export default class Snapshot {
}
return () => {
const path = window.location.href.split('#');
window.location.href = path[0] + url;
window.location.href = window.location.origin + url;
};
}
}

View File

@@ -105,6 +105,11 @@ export function addNotebookEntry(openmct, domainObject, notebookStorage, embed =
const date = Date.now();
const configuration = domainObject.configuration;
const entries = configuration.entries || {};
if (!entries) {
return;
}
const embeds = embed
? [embed]
: [];
@@ -120,8 +125,7 @@ export function addNotebookEntry(openmct, domainObject, notebookStorage, embed =
const newEntries = addEntryIntoPage(notebookStorage, entries, entry);
addDefaultClass(domainObject, openmct);
mutateObject(openmct, domainObject, 'configuration.entries', newEntries);
domainObject.configuration.entries = newEntries;
return id;
}

View File

@@ -30,6 +30,7 @@ import {
describe('the plugin', () => {
let notificationIndicatorPlugin;
let openmct;
let indicatorObject;
let indicatorElement;
let parentElement;
let mockMessages = ['error', 'test', 'notifications'];
@@ -42,6 +43,9 @@ describe('the plugin', () => {
parentElement = document.createElement('div');
indicatorObject = openmct.indicators.indicatorObjects.find(indicator => indicator.key === 'notifications-indicator');
indicatorElement = indicatorObject.element;
openmct.on('start', () => {
mockMessages.forEach(message => {
openmct.notifications.error(message);
@@ -49,7 +53,7 @@ describe('the plugin', () => {
done();
});
openmct.start();
openmct.startHeadless();
});
afterEach(() => {
@@ -64,7 +68,7 @@ describe('the plugin', () => {
});
it('notifies the user of the number of notifications', () => {
let notificationCountElement = document.querySelector('.c-indicator__count');
let notificationCountElement = parentElement.querySelector('.c-indicator__count');
expect(notificationCountElement.innerText).toEqual(mockMessages.length.toString());
});

View File

@@ -278,7 +278,7 @@ export default {
// Have to throw away the old canvas elements and replace with new
// canvas elements in order to get new drawing contexts.
const div = document.createElement('div');
div.innerHTML = this.canvasTemplate + this.canvasTemplate;
div.innerHTML = this.TEMPLATE;
const mainCanvas = div.querySelectorAll("canvas")[1];
const overlayCanvas = div.querySelectorAll("canvas")[0];
this.canvas.parentNode.replaceChild(mainCanvas, this.canvas);

View File

@@ -533,6 +533,13 @@ describe("the plugin", function () {
let plotViewComponentObject;
beforeEach(() => {
const getFunc = openmct.$injector.get;
spyOn(openmct.$injector, "get")
.withArgs("exportImageService").and.returnValue({
exportPNG: () => {},
exportJPG: () => {}
})
.and.callFake(getFunc);
stackedPlotObject = {
identifier: {

View File

@@ -74,9 +74,7 @@ define([
'./clock/plugin',
'./DeviceClassifier/plugin',
'./timer/plugin',
'./localStorage/plugin',
'./legacySupport/plugin.js',
'../adapter/indicators/legacy-indicators-plugin'
'./localStorage/plugin'
], function (
_,
UTCTimeSystem,
@@ -131,9 +129,7 @@ define([
Clock,
DeviceClassifier,
Timer,
LocalStorage,
LegacySupportPlugin,
LegacyIndicatorsPlugin
LocalStorage
) {
const bundleMap = {
Elasticsearch: 'platform/persistence/elastic'
@@ -241,8 +237,6 @@ define([
plugins.Timer = Timer.default;
plugins.DeviceClassifier = DeviceClassifier.default;
plugins.LocalStorage = LocalStorage.default;
plugins.LegacySupport = LegacySupportPlugin.default;
plugins.LegacyIndicators = LegacyIndicatorsPlugin;
return plugins;
});

View File

@@ -29,9 +29,10 @@ define(
}
SummaryWidgetsCompositionPolicy.prototype.allow = function (parent, child) {
const parentType = parent.type;
const parentType = parent.getCapability('type');
const newStyleChild = child.useCapability('adapter');
if (parentType === 'summary-widget' && !this.openmct.telemetry.isTelemetryObject(child)) {
if (parentType.instanceOf('summary-widget') && !this.openmct.telemetry.isTelemetryObject(newStyleChild)) {
return false;
}

View File

@@ -17,6 +17,7 @@ define([
const widgetType = {
name: 'Summary Widget',
description: 'A compact status update for collections of telemetry-producing items',
creatable: true,
cssClass: 'icon-summary-widget',
initialize: function (domainObject) {
domainObject.composition = [];
@@ -84,8 +85,16 @@ define([
return function install(openmct) {
openmct.types.addType('summary-widget', widgetType);
let compositionPolicy = new SummaryWidgetsCompositionPolicy(openmct);
openmct.composition.addPolicy(compositionPolicy.allow.bind(compositionPolicy));
openmct.legacyExtension('policies', {
category: 'composition',
implementation: SummaryWidgetsCompositionPolicy,
depends: ['openmct']
});
openmct.legacyExtension('policies', {
category: 'view',
implementation: SummaryWidgetViewPolicy,
depends: ['openmct']
});
openmct.telemetry.addProvider(new SummaryWidgetMetadataProvider(openmct));
openmct.telemetry.addProvider(new SummaryWidgetTelemetryProvider(openmct));
openmct.objectViews.addProvider(new SummaryWidgetViewProvider(openmct));

View File

@@ -80,6 +80,7 @@ define([
this.addHyperlink(domainObject.url, domainObject.openNewTab);
this.watchForChanges(openmct, domainObject);
const id = objectUtils.makeKeyString(this.domainObject.identifier);
const self = this;
/**
@@ -104,6 +105,8 @@ define([
this.listenTo(this.toggleRulesControl, 'click', toggleRules);
openmct.$injector.get('objectService')
.getObjects([id]);
}
/**

View File

@@ -191,7 +191,7 @@ export default {
});
});
},
beforeDestroy() {
destroyed() {
this.active = false;
if (this.unlisten) {
this.unlisten();

View File

@@ -60,8 +60,6 @@ describe("Timer plugin:", () => {
timerDefinition = openmct.types.get('timer').definition;
timerDefinition.initialize(timerDomainObject);
spyOn(openmct.objects, 'supportsMutation').and.returnValue(true);
openmct.on('start', resolve);
openmct.start(appHolder);
});
@@ -95,8 +93,6 @@ describe("Timer plugin:", () => {
const applicableViews = openmct.objectViews.get(timerViewObject, [timerViewObject]);
timerViewProvider = applicableViews.find(viewProvider => viewProvider.key === 'timer.view');
spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve(timerViewObject));
mutableTimerObject = await openmct.objects.getMutable(timerViewObject.identifier);
timerObjectPath = [mutableTimerObject];
@@ -106,10 +102,6 @@ describe("Timer plugin:", () => {
await Vue.nextTick();
});
afterEach(() => {
timerView.destroy();
});
it("should migrate old object properties to the configuration section", () => {
openmct.objects.applyGetInterceptors(timerViewObject.identifier, timerViewObject);
expect(timerViewObject.configuration.timerFormat).toBe('short');

View File

@@ -21,7 +21,7 @@
*****************************************************************************/
export const COLOR_PALETTE = [
[0x43, 0xB0, 0xFF],
[0x00, 0x37, 0xFF],
[0xF0, 0x60, 0x00],
[0x00, 0x70, 0x40],
[0xFB, 0x49, 0x49],
@@ -30,25 +30,25 @@ export const COLOR_PALETTE = [
[0xFF, 0xA6, 0x3D],
[0x05, 0xA3, 0x00],
[0xF0, 0x00, 0x6C],
[0xAC, 0x54, 0xAE],
[0x77, 0x17, 0x7A],
[0x23, 0xA9, 0xDB],
[0xC7, 0xBE, 0x52],
[0x5A, 0xBD, 0x56],
[0xFA, 0xF0, 0x6F],
[0x4E, 0xF0, 0x48],
[0xAD, 0x50, 0x72],
[0x94, 0x25, 0xEA],
[0x21, 0x87, 0x82],
[0x8F, 0x6E, 0x47],
[0xf0, 0x59, 0xcb],
[0x34, 0xB6, 0x7D],
[0x7F, 0x52, 0xFF],
[0x46, 0xC7, 0xC0],
[0x6A, 0x36, 0xFF],
[0x56, 0xF0, 0xE8],
[0xA1, 0x8C, 0x1C],
[0x95, 0xB1, 0x26],
[0xCB, 0xE1, 0x44],
[0xFF, 0x84, 0x9E],
[0xB7, 0x79, 0xE7],
[0x8C, 0xC9, 0xFD],
[0xDB, 0xAA, 0x6E],
[0x93, 0xB5, 0x77],
[0xB8, 0xDF, 0x97],
[0xFF, 0xBC, 0xDA],
[0xD3, 0xB6, 0xDE]
];

View File

@@ -9,7 +9,7 @@
/>
</div>
<div ref="objectViewWrapper"
class="c-object-view"
:class="objectViewStyle"
></div>
</div>
</template>
@@ -64,6 +64,13 @@ export default {
},
font() {
return this.objectFontStyle ? this.objectFontStyle.font : this.layoutFont;
},
objectViewStyle() {
if (this.domainObject && this.domainObject.type === 'time-strip') {
return 'l-shell__main-object-view';
} else {
return 'u-contents';
}
}
},
destroyed() {
@@ -141,15 +148,11 @@ export default {
this.openmct.objectViews.off('clearData', this.clearData);
},
getStyleReceiver() {
let styleReceiver;
let styleReceiver = this.$refs.objectViewWrapper.querySelector('.js-style-receiver')
|| this.$refs.objectViewWrapper.querySelector(':first-child');
if (this.$refs.objectViewWrapper !== undefined) {
styleReceiver = this.$refs.objectViewWrapper.querySelector('.js-style-receiver')
|| this.$refs.objectViewWrapper.querySelector(':first-child');
if (styleReceiver === null) {
styleReceiver = undefined;
}
if (styleReceiver === null) {
styleReceiver = undefined;
}
return styleReceiver;

View File

@@ -186,10 +186,6 @@ export default {
return {
name: field.name,
value: field.path.reduce((object, key) => {
if (object === undefined) {
return object;
}
return object[key];
}, this.domainObject)
};

View File

@@ -233,6 +233,7 @@
/******************************* MAIN AREA */
&__main-container {
// Wrapper for main views
//display: flex; NEEDS REGRESSION TESTING!!!
display: flex;
flex-direction: column;
flex: 1 1 auto !important;
@@ -242,11 +243,11 @@
> * + * {
margin-top: $interiorMargin;
}
}
> .c-object-view {
flex: 1 1 auto;
overflow: auto;
}
&__main-object-view {
flex: 1 1 auto;
overflow: auto;
}
&__tree {
@@ -316,12 +317,6 @@
}
}
.c-object-view {
display: block;
height: 100%;
overflow: auto;
}
.is-editing {
.l-shell__main-container {
$m: 3px;

View File

@@ -445,10 +445,6 @@ export default {
}
// sorting composition items
if (!a.name || !b.name) {
return 0;
}
if (a.name.toLowerCase()
> b.name.toLowerCase()) {
return 1;

View File

@@ -1,4 +1,4 @@
import objectPathToUrl from '../../tools/url';
import objectPathToUrl from '/src/tools/url';
export default {
inject: ['openmct'],

View File

@@ -24,6 +24,8 @@ class Ticker {
constructor() {
this.callbacks = [];
this.last = new Date() - 1000;
this.tick();
}
/**
@@ -45,7 +47,7 @@ class Ticker {
}
// Try to update at exactly the next second
this.timeoutHandle = setTimeout(() => {
setTimeout(() => {
this.tick();
}, 1000 - millis, true);
}
@@ -60,10 +62,6 @@ class Ticker {
* @returns {Function} a function to unregister this listener
*/
listen(callback) {
if (this.callbacks.length === 0) {
this.tick();
}
this.callbacks.push(callback);
// Provide immediate feedback
@@ -74,10 +72,6 @@ class Ticker {
this.callbacks = this.callbacks.filter(function (cb) {
return cb !== callback;
});
if (this.callbacks.length === 0) {
clearTimeout(this.timeoutHandle);
}
};
}
}

View File

@@ -127,5 +127,5 @@ module.exports = {
}
]
},
stats: 'errors-warnings'
stats: 'detailed'
};