mirror of
https://github.com/nasa/openmct.git
synced 2025-05-29 13:44:21 +00:00
* chore: remove custom `compatConfig` settings * chore: remove `vue-compat` and adjust webpack config * chore: explicitly define Vue feature flags * fix: `_data` property moved to `_.data` * fix(e2e): revert to original test procedures * refactor: replace final instances of `$set` * refactor: remove `Vue` imports from tests * refactor: `Vue.ref()` -> `ref()` * refactor: actually push the changes... * test: replace unit test with e2e test * test: remove test as it's already covered by e2e * fix(test): use `$ref`s instead of `$children` * test(fix): more `$refs` * fix(test): more `$refs` * fix(test): use `$refs` in InspectorStyles tests * fix(SearchComponent): use `$attrs` correctly --------- Co-authored-by: Scott Bell <scott@traclabs.com>
367 lines
12 KiB
JavaScript
367 lines
12 KiB
JavaScript
/*****************************************************************************
|
|
* Open MCT, Copyright (c) 2014-2023, United States Government
|
|
* as represented by the Administrator of the National Aeronautics and Space
|
|
* Administration. All rights reserved.
|
|
*
|
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
* Open MCT includes source code licensed under additional open source
|
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
|
* this source code distribution or the Licensing information page available
|
|
* at runtime from the About dialog for additional information.
|
|
*****************************************************************************/
|
|
import { createOpenMct, resetApplicationState, spyOnBuiltins } from 'utils/testing';
|
|
import { nextTick } from 'vue';
|
|
|
|
import AutoflowTabularConstants from './AutoflowTabularConstants';
|
|
import AutoflowTabularPlugin from './AutoflowTabularPlugin';
|
|
import DOMObserver from './dom-observer';
|
|
|
|
// TODO lots of its without expects
|
|
xdescribe('AutoflowTabularPlugin', () => {
|
|
let testType;
|
|
let testObject;
|
|
let mockmct;
|
|
|
|
beforeEach(() => {
|
|
testType = 'some-type';
|
|
testObject = { type: testType };
|
|
mockmct = createOpenMct();
|
|
spyOn(mockmct.composition, 'get');
|
|
spyOn(mockmct.objectViews, 'addProvider');
|
|
spyOn(mockmct.telemetry, 'getMetadata');
|
|
spyOn(mockmct.telemetry, 'getValueFormatter');
|
|
spyOn(mockmct.telemetry, 'limitEvaluator');
|
|
spyOn(mockmct.telemetry, 'request');
|
|
spyOn(mockmct.telemetry, 'subscribe');
|
|
|
|
const plugin = new AutoflowTabularPlugin({ type: testType });
|
|
plugin(mockmct);
|
|
});
|
|
|
|
afterEach(() => {
|
|
return resetApplicationState(mockmct);
|
|
});
|
|
|
|
it('installs a view provider', () => {
|
|
expect(mockmct.objectViews.addProvider).toHaveBeenCalled();
|
|
});
|
|
|
|
describe('installs a view provider which', () => {
|
|
let provider;
|
|
|
|
beforeEach(() => {
|
|
provider = mockmct.objectViews.addProvider.calls.mostRecent().args[0];
|
|
});
|
|
|
|
it('applies its view to the type from options', () => {
|
|
expect(provider.canView(testObject, [])).toBe(true);
|
|
});
|
|
|
|
it('does not apply to other types', () => {
|
|
expect(provider.canView({ type: 'foo' }, [])).toBe(false);
|
|
});
|
|
|
|
describe('provides a view which', () => {
|
|
let testKeys;
|
|
let testChildren;
|
|
let testContainer;
|
|
let testHistories;
|
|
let mockComposition;
|
|
let mockMetadata;
|
|
let mockEvaluator;
|
|
let mockUnsubscribes;
|
|
let callbacks;
|
|
let view;
|
|
let domObserver;
|
|
|
|
function waitsForChange() {
|
|
return new Promise(function (resolve) {
|
|
window.requestAnimationFrame(resolve);
|
|
});
|
|
}
|
|
|
|
function emitEvent(mockEmitter, type, event) {
|
|
mockEmitter.on.calls.all().forEach((call) => {
|
|
if (call.args[0] === type) {
|
|
call.args[1](event);
|
|
}
|
|
});
|
|
}
|
|
|
|
beforeEach(() => {
|
|
callbacks = {};
|
|
|
|
spyOnBuiltins(['requestAnimationFrame']);
|
|
window.requestAnimationFrame.and.callFake((callBack) => {
|
|
callBack();
|
|
});
|
|
|
|
testObject = { type: 'some-type' };
|
|
testKeys = ['abc', 'def', 'xyz'];
|
|
testChildren = testKeys.map((key) => {
|
|
return {
|
|
identifier: {
|
|
namespace: 'test',
|
|
key: key
|
|
},
|
|
name: 'Object ' + key
|
|
};
|
|
});
|
|
testContainer = document.createElement('div');
|
|
domObserver = new DOMObserver(testContainer);
|
|
|
|
testHistories = testKeys.reduce((histories, key, index) => {
|
|
histories[key] = {
|
|
key: key,
|
|
range: index + 10,
|
|
domain: key + index
|
|
};
|
|
|
|
return histories;
|
|
}, {});
|
|
|
|
mockComposition = jasmine.createSpyObj('composition', ['load', 'on', 'off']);
|
|
mockMetadata = jasmine.createSpyObj('metadata', ['valuesForHints']);
|
|
|
|
mockEvaluator = jasmine.createSpyObj('evaluator', ['evaluate']);
|
|
mockUnsubscribes = testKeys.reduce((map, key) => {
|
|
map[key] = jasmine.createSpy('unsubscribe-' + key);
|
|
|
|
return map;
|
|
}, {});
|
|
|
|
mockmct.composition.get.and.returnValue(mockComposition);
|
|
mockComposition.load.and.callFake(() => {
|
|
testChildren.forEach(emitEvent.bind(null, mockComposition, 'add'));
|
|
|
|
return Promise.resolve(testChildren);
|
|
});
|
|
|
|
mockmct.telemetry.getMetadata.and.returnValue(mockMetadata);
|
|
mockmct.telemetry.getValueFormatter.and.callFake((metadatum) => {
|
|
const mockFormatter = jasmine.createSpyObj('formatter', ['format']);
|
|
mockFormatter.format.and.callFake((datum) => {
|
|
return datum[metadatum.hint];
|
|
});
|
|
|
|
return mockFormatter;
|
|
});
|
|
mockmct.telemetry.limitEvaluator.and.returnValue(mockEvaluator);
|
|
mockmct.telemetry.subscribe.and.callFake((obj, callback) => {
|
|
const key = obj.identifier.key;
|
|
callbacks[key] = callback;
|
|
|
|
return mockUnsubscribes[key];
|
|
});
|
|
mockmct.telemetry.request.and.callFake((obj, request) => {
|
|
const key = obj.identifier.key;
|
|
|
|
return Promise.resolve([testHistories[key]]);
|
|
});
|
|
mockMetadata.valuesForHints.and.callFake((hints) => {
|
|
return [{ hint: hints[0] }];
|
|
});
|
|
|
|
view = provider.view(testObject, [testObject]);
|
|
view.show(testContainer);
|
|
|
|
return nextTick();
|
|
});
|
|
|
|
afterEach(() => {
|
|
domObserver.destroy();
|
|
});
|
|
|
|
it('populates its container', () => {
|
|
expect(testContainer.children.length > 0).toBe(true);
|
|
});
|
|
|
|
describe('when rows have been populated', () => {
|
|
function rowsMatch() {
|
|
const rows = testContainer.querySelectorAll('.l-autoflow-row').length;
|
|
|
|
return rows === testChildren.length;
|
|
}
|
|
|
|
it('shows one row per child object', () => {
|
|
return domObserver.when(rowsMatch);
|
|
});
|
|
|
|
// it("adds rows on composition change", () => {
|
|
// const child = {
|
|
// identifier: {
|
|
// namespace: "test",
|
|
// key: "123"
|
|
// },
|
|
// name: "Object 123"
|
|
// };
|
|
// testChildren.push(child);
|
|
// emitEvent(mockComposition, 'add', child);
|
|
|
|
// return domObserver.when(rowsMatch);
|
|
// });
|
|
|
|
it('removes rows on composition change', () => {
|
|
const child = testChildren.pop();
|
|
emitEvent(mockComposition, 'remove', child.identifier);
|
|
|
|
return domObserver.when(rowsMatch);
|
|
});
|
|
});
|
|
|
|
it('removes subscriptions when destroyed', () => {
|
|
testKeys.forEach((key) => {
|
|
expect(mockUnsubscribes[key]).not.toHaveBeenCalled();
|
|
});
|
|
view.destroy();
|
|
testKeys.forEach((key) => {
|
|
expect(mockUnsubscribes[key]).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
it('provides a button to change column width', () => {
|
|
const initialWidth = AutoflowTabularConstants.INITIAL_COLUMN_WIDTH;
|
|
const nextWidth = initialWidth + AutoflowTabularConstants.COLUMN_WIDTH_STEP;
|
|
|
|
expect(testContainer.querySelector('.l-autoflow-col').css('width')).toEqual(
|
|
initialWidth + 'px'
|
|
);
|
|
|
|
testContainer.querySelector('.change-column-width').click();
|
|
|
|
function widthHasChanged() {
|
|
const width = testContainer.querySelector('.l-autoflow-col').css('width');
|
|
|
|
return width !== initialWidth + 'px';
|
|
}
|
|
|
|
return domObserver.when(widthHasChanged).then(() => {
|
|
expect(testContainer.querySelector('.l-autoflow-col').css('width')).toEqual(
|
|
nextWidth + 'px'
|
|
);
|
|
});
|
|
});
|
|
|
|
it('subscribes to all child objects', () => {
|
|
testKeys.forEach((key) => {
|
|
expect(callbacks[key]).toEqual(jasmine.any(Function));
|
|
});
|
|
});
|
|
|
|
it('displays historical telemetry', () => {
|
|
function rowTextDefined() {
|
|
return testContainer.querySelector('.l-autoflow-item').filter('.r').text() !== '';
|
|
}
|
|
|
|
return domObserver.when(rowTextDefined).then(() => {
|
|
testKeys.forEach((key, index) => {
|
|
const datum = testHistories[key];
|
|
const $cell = testContainer.querySelector('.l-autoflow-row').eq(index).find('.r');
|
|
expect($cell.text()).toEqual(String(datum.range));
|
|
});
|
|
});
|
|
});
|
|
|
|
it('displays incoming telemetry', () => {
|
|
const testData = testKeys.map((key, index) => {
|
|
return {
|
|
key: key,
|
|
range: index * 100,
|
|
domain: key + index
|
|
};
|
|
});
|
|
|
|
testData.forEach((datum) => {
|
|
callbacks[datum.key](datum);
|
|
});
|
|
|
|
return waitsForChange().then(() => {
|
|
testData.forEach((datum, index) => {
|
|
const $cell = testContainer.querySelector('.l-autoflow-row').eq(index).find('.r');
|
|
expect($cell.text()).toEqual(String(datum.range));
|
|
});
|
|
});
|
|
});
|
|
|
|
it('updates classes for limit violations', () => {
|
|
const testClass = 'some-limit-violation';
|
|
mockEvaluator.evaluate.and.returnValue({ cssClass: testClass });
|
|
testKeys.forEach((key) => {
|
|
callbacks[key]({
|
|
range: 'foo',
|
|
domain: 'bar'
|
|
});
|
|
});
|
|
|
|
return waitsForChange().then(() => {
|
|
testKeys.forEach((datum, index) => {
|
|
const $cell = testContainer.querySelector('.l-autoflow-row').eq(index).find('.r');
|
|
expect($cell.hasClass(testClass)).toBe(true);
|
|
});
|
|
});
|
|
});
|
|
|
|
it('automatically flows to new columns', () => {
|
|
const rowHeight = AutoflowTabularConstants.ROW_HEIGHT;
|
|
const sliderHeight = AutoflowTabularConstants.SLIDER_HEIGHT;
|
|
const count = testKeys.length;
|
|
const $container = testContainer;
|
|
let promiseChain = Promise.resolve();
|
|
|
|
function columnsHaveAutoflowed() {
|
|
const itemsHeight = $container.querySelector('.l-autoflow-items').height();
|
|
const availableHeight = itemsHeight - sliderHeight;
|
|
const availableRows = Math.max(Math.floor(availableHeight / rowHeight), 1);
|
|
const columns = Math.ceil(count / availableRows);
|
|
|
|
return $container.querySelector('.l-autoflow-col').length === columns;
|
|
}
|
|
|
|
$container.find('.abs').css({
|
|
position: 'absolute',
|
|
left: '0px',
|
|
right: '0px',
|
|
top: '0px',
|
|
bottom: '0px'
|
|
});
|
|
$container.css({ position: 'absolute' });
|
|
|
|
$container.appendTo(document.body);
|
|
|
|
function setHeight(height) {
|
|
$container.css('height', height + 'px');
|
|
|
|
return domObserver.when(columnsHaveAutoflowed);
|
|
}
|
|
|
|
for (let height = 0; height < rowHeight * count * 2; height += rowHeight / 2) {
|
|
// eslint-disable-next-line no-invalid-this
|
|
promiseChain = promiseChain.then(setHeight.bind(this, height));
|
|
}
|
|
|
|
return promiseChain.then(() => {
|
|
$container.remove();
|
|
});
|
|
});
|
|
|
|
it('loads composition exactly once', () => {
|
|
const testObj = testChildren.pop();
|
|
emitEvent(mockComposition, 'remove', testObj.identifier);
|
|
testChildren.push(testObj);
|
|
emitEvent(mockComposition, 'add', testObj);
|
|
expect(mockComposition.load.calls.count()).toEqual(1);
|
|
});
|
|
});
|
|
});
|
|
});
|