Move all support for the legacy API into a plugin (#4614)

* Make legacy support optional
* Fix order of legacy plugin registration
* Added 'supportComposition' function
* Add composition policy to check that parent supports composition
* Fix memory leaks in timer
This commit is contained in:
Andrew Henry 2022-01-03 14:21:19 -08:00 committed by GitHub
parent 51e4c0c836
commit 3a65f75d21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 231 additions and 241 deletions

View File

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

View File

@ -22,24 +22,17 @@
define([ define([
'EventEmitter', 'EventEmitter',
'./BundleRegistry',
'./installDefaultBundles',
'./api/api', './api/api',
'./api/overlays/OverlayAPI', './api/overlays/OverlayAPI',
'./selection/Selection', './selection/Selection',
'objectUtils',
'./plugins/plugins', './plugins/plugins',
'./adapter/indicators/legacy-indicators-plugin',
'./ui/registries/ViewRegistry', './ui/registries/ViewRegistry',
'./plugins/imagery/plugin', './plugins/imagery/plugin',
'./ui/registries/InspectorViewRegistry', './ui/registries/InspectorViewRegistry',
'./ui/registries/ToolbarRegistry', './ui/registries/ToolbarRegistry',
'./ui/router/ApplicationRouter', './ui/router/ApplicationRouter',
'./ui/router/Browse', './ui/router/Browse',
'../platform/framework/src/Main',
'./ui/layout/Layout.vue', './ui/layout/Layout.vue',
'../platform/core/src/objects/DomainObjectImpl',
'../platform/core/src/capabilities/ContextualDomainObject',
'./ui/preview/plugin', './ui/preview/plugin',
'./api/Branding', './api/Branding',
'./plugins/licenses/plugin', './plugins/licenses/plugin',
@ -52,24 +45,17 @@ define([
'vue' 'vue'
], function ( ], function (
EventEmitter, EventEmitter,
BundleRegistry,
installDefaultBundles,
api, api,
OverlayAPI, OverlayAPI,
Selection, Selection,
objectUtils,
plugins, plugins,
LegacyIndicatorsPlugin,
ViewRegistry, ViewRegistry,
ImageryPlugin, ImageryPlugin,
InspectorViewRegistry, InspectorViewRegistry,
ToolbarRegistry, ToolbarRegistry,
ApplicationRouter, ApplicationRouter,
Browse, Browse,
Main,
Layout, Layout,
DomainObjectImpl,
ContextualDomainObject,
PreviewPlugin, PreviewPlugin,
BrandingAPI, BrandingAPI,
LicensesPlugin, LicensesPlugin,
@ -106,23 +92,6 @@ define([
revision: __OPENMCT_REVISION__, revision: __OPENMCT_REVISION__,
branch: __OPENMCT_BUILD_BRANCH__ 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); this.destroy = this.destroy.bind(this);
/** /**
@ -262,16 +231,12 @@ define([
this.branding = BrandingAPI.default; this.branding = BrandingAPI.default;
this.legacyRegistry = new BundleRegistry();
installDefaultBundles(this.legacyRegistry);
// Plugins that are installed by default // Plugins that are installed by default
this.install(this.plugins.Plot()); this.install(this.plugins.Plot());
this.install(this.plugins.Chart()); this.install(this.plugins.Chart());
this.install(this.plugins.TelemetryTable.default()); this.install(this.plugins.TelemetryTable.default());
this.install(PreviewPlugin.default()); this.install(PreviewPlugin.default());
this.install(LegacyIndicatorsPlugin());
this.install(LicensesPlugin.default()); this.install(LicensesPlugin.default());
this.install(RemoveActionPlugin.default()); this.install(RemoveActionPlugin.default());
this.install(MoveActionPlugin.default()); this.install(MoveActionPlugin.default());
@ -303,51 +268,6 @@ define([
MCT.prototype.MCT = MCT; 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. * Set path to where assets are hosted. This should be the path to main.js.
* @memberof module:openmct.MCT# * @memberof module:openmct.MCT#
@ -393,25 +313,6 @@ define([
this.element = domElement; 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.route(/^\/$/, () => {
this.router.setPath('/browse/'); this.router.setPath('/browse/');
}); });
@ -422,35 +323,27 @@ define([
* @event start * @event start
* @memberof module:openmct.MCT~ * @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) { if (!isHeadlessMode) {
const appLayout = new Vue({ const appLayout = new Vue({
components: { components: {
'Layout': Layout.default 'Layout': Layout.default
}, },
provide: { provide: {
openmct: this openmct: this
}, },
template: '<Layout ref="layout"></Layout>' template: '<Layout ref="layout"></Layout>'
}); });
domElement.appendChild(appLayout.$mount().$el); domElement.appendChild(appLayout.$mount().$el);
this.layout = appLayout.$refs.layout; this.layout = appLayout.$refs.layout;
Browse(this); Browse(this);
} }
window.addEventListener('beforeunload', this.destroy); window.addEventListener('beforeunload', this.destroy);
this.router.start(); this.router.start();
this.emit('start'); this.emit('start');
}.bind(this));
}; };
MCT.prototype.startHeadless = function () { MCT.prototype.startHeadless = function () {

View File

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

View File

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

View File

@ -87,6 +87,12 @@ define([
expect(composition).toEqual(jasmine.any(CompositionCollection)); 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 () { it('loads composition from domain object', function () {
const listener = jasmine.createSpy('addListener'); const listener = jasmine.createSpy('addListener');
composition.on('add', listener); composition.on('add', listener);

View File

@ -49,8 +49,10 @@ define([
this.onMutation = this.onMutation.bind(this); this.onMutation = this.onMutation.bind(this);
this.cannotContainItself = this.cannotContainItself.bind(this); this.cannotContainItself = this.cannotContainItself.bind(this);
this.supportsComposition = this.supportsComposition.bind(this);
compositionAPI.addPolicy(this.cannotContainItself); compositionAPI.addPolicy(this.cannotContainItself);
compositionAPI.addPolicy(this.supportsComposition);
} }
/** /**
@ -61,6 +63,13 @@ define([
&& parent.identifier.key === child.identifier.key); && 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 * Check if this provider should be used to load composition for a
* particular domain object. * particular domain object.

View File

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

View File

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

View File

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

View File

@ -1,34 +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.
*****************************************************************************/
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

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

View File

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

View File

@ -0,0 +1,126 @@
/*****************************************************************************
* 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

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

View File

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

View File

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

View File

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

View File

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

View File

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