diff --git a/src/plugins/clearData/plugin.js b/src/plugins/clearData/plugin.js index 65279b0801..51f3225151 100644 --- a/src/plugins/clearData/plugin.js +++ b/src/plugins/clearData/plugin.js @@ -48,7 +48,7 @@ define([ let indicator = { element: component.$mount().$el, - key: 'clear-data-indicator', + key: 'global-clear-indicator', priority: openmct.priority.DEFAULT }; diff --git a/src/plugins/clearData/pluginSpec.js b/src/plugins/clearData/pluginSpec.js new file mode 100644 index 0000000000..013602ad50 --- /dev/null +++ b/src/plugins/clearData/pluginSpec.js @@ -0,0 +1,228 @@ +/***************************************************************************** + * 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 ClearDataPlugin from './plugin.js'; +import Vue from 'vue'; +import { createOpenMct, resetApplicationState, createMouseEvent } from 'utils/testing'; + +describe('The Clear Data Plugin:', () => { + let clearDataPlugin; + + describe('The clear data action:', () => { + let openmct; + let selection; + let mockObjectPath; + let clearDataAction; + let testViewObject; + beforeEach((done) => { + openmct = createOpenMct(); + + clearDataPlugin = new ClearDataPlugin( + ['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked'], + {indicator: true} + ); + openmct.install(clearDataPlugin); + + clearDataAction = openmct.actions.getAction('clear-data-action'); + testViewObject = [{ + identifier: { + key: "foo-table", + namespace: '' + }, + type: "table" + }]; + openmct.router.path = testViewObject; + mockObjectPath = [ + { + name: 'Mock Table', + type: 'table', + identifier: { + key: "foo-table", + namespace: '' + } + } + ]; + selection = [ + { + context: { + item: mockObjectPath[0] + } + } + ]; + + openmct.selection.select(selection); + + openmct.on('start', done); + openmct.startHeadless(); + }); + + afterEach(() => { + openmct.router.path = null; + + return resetApplicationState(openmct); + }); + it('is installed', () => { + expect(clearDataAction).toBeDefined(); + }); + + it('is applicable on applicable objects', () => { + const gatheredActions = openmct.actions.getActionsCollection(mockObjectPath); + expect(gatheredActions.applicableActions['clear-data-action']).toBeDefined(); + }); + + it('is not applicable on inapplicable objects', () => { + testViewObject = [{ + identifier: { + key: "foo-widget", + namespace: '' + }, + type: "widget" + }]; + mockObjectPath = [ + { + name: 'Mock Widget', + type: 'widget', + identifier: { + key: "foo-widget", + namespace: '' + } + } + ]; + selection = [ + { + context: { + item: mockObjectPath[0] + } + } + ]; + openmct.selection.select(selection); + const gatheredActions = openmct.actions.getActionsCollection(mockObjectPath); + expect(gatheredActions.applicableActions['clear-data-action']).toBeUndefined(); + }); + + it('is not applicable if object not in the selection path and not a layout', () => { + selection = [ + { + context: { + item: { + name: 'Some Random Widget', + type: 'not-in-path-widget', + identifier: { + key: "something-else-widget", + namespace: '' + } + } + } + } + ]; + openmct.selection.select(selection); + const gatheredActions = openmct.actions.getActionsCollection(mockObjectPath); + expect(gatheredActions.applicableActions['clear-data-action']).toBeUndefined(); + }); + + it('is applicable if object not in the selection path and is a layout', () => { + selection = [ + { + context: { + item: { + name: 'Some Random Widget', + type: 'not-in-path-widget', + identifier: { + key: "something-else-widget", + namespace: '' + } + } + } + } + ]; + + openmct.selection.select(selection); + + testViewObject = [{ + identifier: { + key: "foo-layout", + namespace: '' + }, + type: "layout" + }]; + openmct.router.path = testViewObject; + const gatheredActions = openmct.actions.getActionsCollection(mockObjectPath); + expect(gatheredActions.applicableActions['clear-data-action']).toBeDefined(); + }); + + it('fires an event upon invocation', (done) => { + openmct.objectViews.on('clearData', (domainObject) => { + expect(domainObject).toEqual(testViewObject[0]); + done(); + }); + clearDataAction.invoke(testViewObject); + }); + }); + + describe('The clear data indicator:', () => { + let openmct; + let appHolder; + + beforeEach((done) => { + openmct = createOpenMct(); + + clearDataPlugin = new ClearDataPlugin( + ['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked'], + {indicator: true} + ); + openmct.install(clearDataPlugin); + appHolder = document.createElement('div'); + document.body.appendChild(appHolder); + openmct.on('start', done); + openmct.start(appHolder); + }); + + it('installs', () => { + const globalClearIndicator = openmct.indicators.indicatorObjects + .find(indicator => indicator.key === 'global-clear-indicator').element; + expect(globalClearIndicator).toBeDefined(); + }); + + it("renders its major elements", async () => { + await Vue.nextTick(); + const indicatorClass = appHolder.querySelector('.c-indicator'); + const iconClass = appHolder.querySelector('.icon-clear-data'); + const indicatorLabel = appHolder.querySelector('.c-indicator__label'); + const buttonElement = indicatorLabel.querySelector('button'); + const hasMajorElements = Boolean(indicatorClass && iconClass && buttonElement); + + expect(hasMajorElements).toBe(true); + expect(buttonElement.innerText).toEqual('Clear Data'); + }); + + it("clicking the button fires the global clear", (done) => { + const indicatorLabel = appHolder.querySelector('.c-indicator__label'); + const buttonElement = indicatorLabel.querySelector('button'); + const clickEvent = createMouseEvent('click'); + openmct.objectViews.on('clearData', () => { + // when we click the button, this event should fire + done(); + }); + buttonElement.dispatchEvent(clickEvent); + }); + }); +}); diff --git a/src/plugins/clearData/test/ClearDataActionSpec.js b/src/plugins/clearData/test/ClearDataActionSpec.js deleted file mode 100644 index 022d37d942..0000000000 --- a/src/plugins/clearData/test/ClearDataActionSpec.js +++ /dev/null @@ -1,143 +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 ClearDataActionPlugin from '../plugin.js'; -import ClearDataAction from '../ClearDataAction.js'; - -describe('When the Clear Data Plugin is installed,', () => { - const mockObjectViews = jasmine.createSpyObj('objectViews', ['emit']); - const mockIndicatorProvider = jasmine.createSpyObj('indicators', ['add']); - const mockActionsProvider = jasmine.createSpyObj('actions', ['register']); - const goodMockSelectionPath = [[{ - context: { - item: { - identifier: { - key: 'apple', - namespace: '' - } - } - } - }]]; - - const openmct = { - objectViews: mockObjectViews, - indicators: mockIndicatorProvider, - priority: { - DEFAULT: 0 - }, - actions: mockActionsProvider, - install: function (plugin) { - plugin(this); - }, - selection: { - get: function () { - return goodMockSelectionPath; - } - }, - objects: { - areIdsEqual: function (obj1, obj2) { - return true; - } - } - }; - - const mockObjectPath = [ - { - name: 'mockObject1', - type: 'apple' - }, - { - name: 'mockObject2', - type: 'banana' - } - ]; - - it('Global Clear Indicator is installed', () => { - openmct.install(ClearDataActionPlugin(openmct, {indicator: true})); - - expect(mockIndicatorProvider.add).toHaveBeenCalled(); - }); - - it('Clear Data context menu action is installed', () => { - openmct.install(ClearDataActionPlugin(openmct, [])); - - expect(mockActionsProvider.register).toHaveBeenCalled(); - }); - - it('clear data action emits a clearData event when invoked', () => { - const action = new ClearDataAction(openmct); - - action.invoke(mockObjectPath); - - expect(mockObjectViews.emit).toHaveBeenCalledWith('clearData', mockObjectPath[0]); - }); - - it('clears data on applicable objects', () => { - let action = new ClearDataAction(openmct, ['apple']); - - const actionApplies = action.appliesTo(mockObjectPath); - - expect(actionApplies).toBe(true); - }); - - it('does not clear data on inapplicable objects', () => { - let action = new ClearDataAction(openmct, ['pineapple']); - - const actionApplies = action.appliesTo(mockObjectPath); - - expect(actionApplies).toBe(false); - }); - - it('does not clear data if not in the selection path and not a layout', () => { - openmct.objects = { - areIdsEqual: function (obj1, obj2) { - return false; - } - }; - openmct.router = { - path: [{type: 'not-a-layout'}] - }; - - let action = new ClearDataAction(openmct, ['apple']); - - const actionApplies = action.appliesTo(mockObjectPath); - - expect(actionApplies).toBe(false); - }); - - it('does clear data if not in the selection path and is a layout', () => { - openmct.objects = { - areIdsEqual: function (obj1, obj2) { - return false; - } - }; - openmct.router = { - path: [{type: 'layout'}] - }; - - let action = new ClearDataAction(openmct, ['apple']); - - const actionApplies = action.appliesTo(mockObjectPath); - - expect(actionApplies).toBe(true); - }); -}); diff --git a/src/plugins/conditionWidget/pluginSpec.js b/src/plugins/conditionWidget/pluginSpec.js index 228e2a8c2d..033516f138 100644 --- a/src/plugins/conditionWidget/pluginSpec.js +++ b/src/plugins/conditionWidget/pluginSpec.js @@ -3,14 +3,17 @@ import ConditionWidgetPlugin from "./plugin"; import Vue from 'vue'; describe('the plugin', function () { + const CONDITION_WIDGET_KEY = 'conditionWidget'; let objectDef; let element; let child; let openmct; - let mockObjectPath; + let mockConditionObjectDefinition; + let mockConditionObject; + let mockConditionObjectPath; beforeEach((done) => { - mockObjectPath = [ + mockConditionObjectPath = [ { name: 'mock folder', type: 'fake-folder', @@ -29,6 +32,61 @@ describe('the plugin', function () { } ]; + mockConditionObjectDefinition = { + name: 'Condition Widget', + key: 'conditionWidget', + creatable: true + }; + + mockConditionObject = { + "conditionWidget": { + "identifier": { + "namespace": "", + "key": "condition-widget-object" + }, + "url": "https://nasa.github.io/openmct/", + "label": "Foo Widget", + "type": "conditionWidget", + "composition": [] + }, + "telemetry": { + "identifier": { + "namespace": "", + "key": "telemetry-object" + }, + "type": "test-telemetry-object", + "name": "Test Telemetry Object", + "telemetry": { + "values": [ + { + "key": "name", + "name": "Name", + "format": "string" + }, + { + "key": "utc", + "name": "Time", + "format": "utc", + "hints": { + "domain": 1 + } + }, + { + "name": "Some attribute 1", + "key": "some-key-1", + "hints": { + "range": 1 + } + }, + { + "name": "Some attribute 2", + "key": "some-key-2" + } + ] + } + } + }; + const timeSystem = { timeSystemKey: 'utc', bounds: { @@ -58,19 +116,13 @@ describe('the plugin', function () { return resetApplicationState(openmct); }); - let mockObject = { - name: 'Condition Widget', - key: 'conditionWidget', - creatable: true - }; - it('defines a conditionWidget object type with the correct key', () => { - expect(objectDef.key).toEqual(mockObject.key); + expect(objectDef.key).toEqual(mockConditionObjectDefinition.key); }); describe('the conditionWidget object', () => { it('is creatable', () => { - expect(objectDef.creatable).toEqual(mockObject.creatable); + expect(objectDef.creatable).toEqual(mockConditionObjectDefinition.creatable); }); }); @@ -88,7 +140,7 @@ describe('the plugin', function () { type: "conditionWidget" }; - const applicableViews = openmct.objectViews.get(testViewObject, mockObjectPath); + const applicableViews = openmct.objectViews.get(testViewObject, mockConditionObjectPath); conditionWidgetView = applicableViews.find((viewProvider) => viewProvider.key === 'conditionWidget'); let view = conditionWidgetView.view(testViewObject, element); view.show(child, true); @@ -100,4 +152,44 @@ describe('the plugin', function () { expect(conditionWidgetView).toBeDefined(); }); }); + + it("should have a view provider for condition widget objects", () => { + const applicableViews = openmct.objectViews.get(mockConditionObject[CONDITION_WIDGET_KEY], []); + + const conditionWidgetViewProvider = applicableViews.find( + (viewProvider) => viewProvider.key === CONDITION_WIDGET_KEY + ); + + expect(applicableViews.length).toEqual(1); + expect(conditionWidgetViewProvider).toBeDefined(); + }); + + it("should render a view with a URL and label", async () => { + const urlParent = document.createElement('div'); + const urlChild = document.createElement('div'); + urlParent.appendChild(urlChild); + + const applicableViews = openmct.objectViews.get(mockConditionObject[CONDITION_WIDGET_KEY], []); + + const conditionWidgetViewProvider = applicableViews.find( + (viewProvider) => viewProvider.key === CONDITION_WIDGET_KEY + ); + + const conditionWidgetView = conditionWidgetViewProvider.view(mockConditionObject[CONDITION_WIDGET_KEY], [mockConditionObject[CONDITION_WIDGET_KEY]]); + conditionWidgetView.show(urlChild); + + await Vue.nextTick(); + + const domainUrl = mockConditionObject[CONDITION_WIDGET_KEY].url; + expect(urlParent.innerHTML).toContain(`'); + + const conditionWidgetLabel = conditionWidgetRender.querySelector('.c-condition-widget__label'); + expect(conditionWidgetLabel).toBeDefined(); + const domainLabel = mockConditionObject[CONDITION_WIDGET_KEY].label; + expect(conditionWidgetLabel.textContent).toContain(domainLabel); + }); });