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>
484 lines
15 KiB
JavaScript
484 lines
15 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 {
|
|
createMouseEvent,
|
|
createOpenMct,
|
|
resetApplicationState,
|
|
spyOnBuiltins
|
|
} from 'utils/testing';
|
|
import { nextTick } from 'vue';
|
|
|
|
import TablePlugin from './plugin.js';
|
|
|
|
class MockDataTransfer {
|
|
constructor() {
|
|
this.data = {};
|
|
}
|
|
get types() {
|
|
return Object.keys(this.data);
|
|
}
|
|
setData(format, data) {
|
|
this.data[format] = data;
|
|
}
|
|
getData(format) {
|
|
return this.data[format];
|
|
}
|
|
}
|
|
|
|
describe('the plugin', () => {
|
|
let openmct;
|
|
let tablePlugin;
|
|
let element;
|
|
let child;
|
|
let historicalProvider;
|
|
let originalRouterPath;
|
|
let unlistenConfigMutation;
|
|
|
|
beforeEach((done) => {
|
|
openmct = createOpenMct();
|
|
|
|
// Table Plugin is actually installed by default, but because installing it
|
|
// again is harmless it is left here as an example for non-default plugins.
|
|
tablePlugin = new TablePlugin();
|
|
openmct.install(tablePlugin);
|
|
|
|
historicalProvider = {
|
|
request: () => {
|
|
return Promise.resolve([]);
|
|
}
|
|
};
|
|
spyOn(openmct.telemetry, 'findRequestProvider').and.returnValue(historicalProvider);
|
|
|
|
element = document.createElement('div');
|
|
child = document.createElement('div');
|
|
element.appendChild(child);
|
|
|
|
openmct.time.timeSystem('utc', {
|
|
start: 0,
|
|
end: 4
|
|
});
|
|
|
|
openmct.types.addType('test-object', {
|
|
creatable: true
|
|
});
|
|
|
|
spyOnBuiltins(['requestAnimationFrame']);
|
|
window.requestAnimationFrame.and.callFake((callBack) => {
|
|
callBack();
|
|
});
|
|
|
|
originalRouterPath = openmct.router.path;
|
|
|
|
openmct.on('start', done);
|
|
openmct.startHeadless();
|
|
});
|
|
|
|
afterEach(() => {
|
|
openmct.time.timeSystem('utc', {
|
|
start: 0,
|
|
end: 1
|
|
});
|
|
|
|
if (unlistenConfigMutation) {
|
|
unlistenConfigMutation();
|
|
}
|
|
|
|
return resetApplicationState(openmct);
|
|
});
|
|
|
|
describe('defines a table object', function () {
|
|
it('that is creatable', () => {
|
|
let tableType = openmct.types.get('table');
|
|
expect(tableType.definition.creatable).toBe(true);
|
|
});
|
|
});
|
|
|
|
it('provides a table view for objects with telemetry', () => {
|
|
const testTelemetryObject = {
|
|
id: 'test-object',
|
|
type: 'test-object',
|
|
telemetry: {
|
|
values: [
|
|
{
|
|
key: 'some-key'
|
|
}
|
|
]
|
|
}
|
|
};
|
|
|
|
const applicableViews = openmct.objectViews.get(testTelemetryObject, []);
|
|
let tableView = applicableViews.find((viewProvider) => viewProvider.key === 'table');
|
|
expect(tableView).toBeDefined();
|
|
});
|
|
|
|
describe('The table view', () => {
|
|
let testTelemetryObject;
|
|
let applicableViews;
|
|
let tableViewProvider;
|
|
let tableView;
|
|
let tableInstance;
|
|
let mockClock;
|
|
|
|
beforeEach(async () => {
|
|
openmct.time.timeSystem('utc', {
|
|
start: 0,
|
|
end: 4
|
|
});
|
|
|
|
mockClock = jasmine.createSpyObj('clock', ['on', 'off', 'currentValue']);
|
|
mockClock.key = 'mockClock';
|
|
mockClock.currentValue.and.returnValue(1);
|
|
|
|
openmct.time.addClock(mockClock);
|
|
openmct.time.clock('mockClock', {
|
|
start: 0,
|
|
end: 4
|
|
});
|
|
|
|
testTelemetryObject = {
|
|
identifier: {
|
|
namespace: '',
|
|
key: 'test-object'
|
|
},
|
|
type: 'test-object',
|
|
name: 'Test Object',
|
|
telemetry: {
|
|
values: [
|
|
{
|
|
key: 'utc',
|
|
format: 'utc',
|
|
name: 'Time',
|
|
hints: {
|
|
domain: 1
|
|
}
|
|
},
|
|
{
|
|
key: 'some-key',
|
|
name: 'Some attribute',
|
|
hints: {
|
|
range: 1
|
|
}
|
|
},
|
|
{
|
|
key: 'some-other-key',
|
|
name: 'Another attribute',
|
|
hints: {
|
|
range: 2
|
|
}
|
|
}
|
|
]
|
|
},
|
|
configuration: {
|
|
hiddenColumns: {
|
|
name: false,
|
|
utc: false,
|
|
'some-key': false,
|
|
'some-other-key': false
|
|
}
|
|
}
|
|
};
|
|
const testTelemetry = [
|
|
{
|
|
utc: 1,
|
|
'some-key': 'some-value 1',
|
|
'some-other-key': 'some-other-value 1'
|
|
},
|
|
{
|
|
utc: 2,
|
|
'some-key': 'some-value 2',
|
|
'some-other-key': 'some-other-value 2'
|
|
},
|
|
{
|
|
utc: 3,
|
|
'some-key': 'some-value 3',
|
|
'some-other-key': 'some-other-value 3'
|
|
}
|
|
];
|
|
|
|
historicalProvider.request = () => Promise.resolve(testTelemetry);
|
|
|
|
openmct.router.path = [testTelemetryObject];
|
|
|
|
applicableViews = openmct.objectViews.get(testTelemetryObject, []);
|
|
tableViewProvider = applicableViews.find((viewProvider) => viewProvider.key === 'table');
|
|
tableView = tableViewProvider.view(testTelemetryObject, [testTelemetryObject]);
|
|
tableView.show(child, true);
|
|
|
|
tableInstance = tableView.getTable();
|
|
|
|
await nextTick();
|
|
});
|
|
|
|
afterEach(() => {
|
|
openmct.router.path = originalRouterPath;
|
|
openmct.time.setClock('local');
|
|
});
|
|
|
|
it('Shows no progress bar initially', () => {
|
|
let progressBar = element.querySelector('.c-progress-bar');
|
|
|
|
expect(tableInstance.outstandingRequests).toBe(0);
|
|
expect(progressBar).toBeNull();
|
|
});
|
|
|
|
it('Shows a progress bar while making requests', async () => {
|
|
tableInstance.incrementOutstandingRequests();
|
|
await nextTick();
|
|
|
|
let progressBar = element.querySelector('.c-progress-bar');
|
|
|
|
expect(tableInstance.outstandingRequests).toBe(1);
|
|
expect(progressBar).not.toBeNull();
|
|
});
|
|
|
|
it('Renders a row for every telemetry datum returned', async () => {
|
|
let rows = element.querySelectorAll('table.c-telemetry-table__body tr');
|
|
await nextTick();
|
|
expect(rows.length).toBe(3);
|
|
});
|
|
|
|
it('Renders a column for every item in telemetry metadata', () => {
|
|
let headers = element.querySelectorAll('span.c-telemetry-table__headers__label');
|
|
expect(headers.length).toBe(4);
|
|
expect(headers[0].innerText).toBe('Name');
|
|
expect(headers[1].innerText).toBe('Time');
|
|
expect(headers[2].innerText).toBe('Some attribute');
|
|
expect(headers[3].innerText).toBe('Another attribute');
|
|
});
|
|
|
|
it('Supports column reordering via drag and drop', async () => {
|
|
let columns = element.querySelectorAll('tr.c-telemetry-table__headers__labels th');
|
|
let fromColumn = columns[0];
|
|
let toColumn = columns[1];
|
|
let fromColumnText = fromColumn.querySelector(
|
|
'span.c-telemetry-table__headers__label'
|
|
).innerText;
|
|
let toColumnText = toColumn.querySelector('span.c-telemetry-table__headers__label').innerText;
|
|
|
|
let dragStartEvent = createMouseEvent('dragstart');
|
|
let dragOverEvent = createMouseEvent('dragover');
|
|
let dropEvent = createMouseEvent('drop');
|
|
|
|
dragStartEvent.dataTransfer =
|
|
dragOverEvent.dataTransfer =
|
|
dropEvent.dataTransfer =
|
|
new MockDataTransfer();
|
|
|
|
fromColumn.dispatchEvent(dragStartEvent);
|
|
toColumn.dispatchEvent(dragOverEvent);
|
|
toColumn.dispatchEvent(dropEvent);
|
|
|
|
await nextTick();
|
|
columns = element.querySelectorAll('tr.c-telemetry-table__headers__labels th');
|
|
let firstColumn = columns[0];
|
|
let secondColumn = columns[1];
|
|
let firstColumnText = firstColumn.querySelector(
|
|
'span.c-telemetry-table__headers__label'
|
|
).innerText;
|
|
let secondColumnText = secondColumn.querySelector(
|
|
'span.c-telemetry-table__headers__label'
|
|
).innerText;
|
|
expect(fromColumnText).not.toEqual(firstColumnText);
|
|
expect(fromColumnText).toEqual(secondColumnText);
|
|
expect(toColumnText).not.toEqual(secondColumnText);
|
|
expect(toColumnText).toEqual(firstColumnText);
|
|
});
|
|
|
|
it('Supports filtering telemetry by regular text search', async () => {
|
|
tableInstance.tableRows.setColumnFilter('some-key', '1');
|
|
await nextTick();
|
|
let filteredRowElements = element.querySelectorAll('table.c-telemetry-table__body tr');
|
|
|
|
expect(filteredRowElements.length).toEqual(1);
|
|
tableInstance.tableRows.setColumnFilter('some-key', '');
|
|
await nextTick();
|
|
|
|
let allRowElements = element.querySelectorAll('table.c-telemetry-table__body tr');
|
|
expect(allRowElements.length).toEqual(3);
|
|
});
|
|
|
|
it('Supports filtering using Regex', async () => {
|
|
tableInstance.tableRows.setColumnRegexFilter('some-key', '^some-value$');
|
|
await nextTick();
|
|
let filteredRowElements = element.querySelectorAll('table.c-telemetry-table__body tr');
|
|
|
|
expect(filteredRowElements.length).toEqual(0);
|
|
|
|
tableInstance.tableRows.setColumnRegexFilter('some-key', '^some-value');
|
|
await nextTick();
|
|
let allRowElements = element.querySelectorAll('table.c-telemetry-table__body tr');
|
|
|
|
expect(allRowElements.length).toEqual(3);
|
|
});
|
|
|
|
it('displays the correct number of column headers when the configuration is mutated', async () => {
|
|
const tableInstanceConfiguration = tableInstance.domainObject.configuration;
|
|
tableInstanceConfiguration.hiddenColumns['some-key'] = true;
|
|
unlistenConfigMutation = tableInstance.openmct.objects.mutate(
|
|
tableInstance.domainObject,
|
|
'configuration',
|
|
tableInstanceConfiguration
|
|
);
|
|
|
|
await nextTick();
|
|
let tableHeaderElements = element.querySelectorAll('.c-telemetry-table__headers__label');
|
|
expect(tableHeaderElements.length).toEqual(3);
|
|
|
|
tableInstanceConfiguration.hiddenColumns['some-key'] = false;
|
|
unlistenConfigMutation = tableInstance.openmct.objects.mutate(
|
|
tableInstance.domainObject,
|
|
'configuration',
|
|
tableInstanceConfiguration
|
|
);
|
|
|
|
await nextTick();
|
|
tableHeaderElements = element.querySelectorAll('.c-telemetry-table__headers__label');
|
|
expect(tableHeaderElements.length).toEqual(4);
|
|
});
|
|
|
|
it('displays the correct number of table cells in a row when the configuration is mutated', async () => {
|
|
const tableInstanceConfiguration = tableInstance.domainObject.configuration;
|
|
tableInstanceConfiguration.hiddenColumns['some-key'] = true;
|
|
unlistenConfigMutation = tableInstance.openmct.objects.mutate(
|
|
tableInstance.domainObject,
|
|
'configuration',
|
|
tableInstanceConfiguration
|
|
);
|
|
|
|
await nextTick();
|
|
let tableRowCells = element.querySelectorAll(
|
|
'table.c-telemetry-table__body > tbody > tr:first-child td'
|
|
);
|
|
expect(tableRowCells.length).toEqual(3);
|
|
|
|
tableInstanceConfiguration.hiddenColumns['some-key'] = false;
|
|
unlistenConfigMutation = tableInstance.openmct.objects.mutate(
|
|
tableInstance.domainObject,
|
|
'configuration',
|
|
tableInstanceConfiguration
|
|
);
|
|
|
|
await nextTick();
|
|
tableRowCells = element.querySelectorAll(
|
|
'table.c-telemetry-table__body > tbody > tr:first-child td'
|
|
);
|
|
expect(tableRowCells.length).toEqual(4);
|
|
});
|
|
|
|
it('Pauses the table when a row is marked', async () => {
|
|
let firstRow = element.querySelector('table.c-telemetry-table__body > tbody > tr');
|
|
let clickEvent = createMouseEvent('click');
|
|
|
|
// Mark a row
|
|
firstRow.dispatchEvent(clickEvent);
|
|
|
|
await nextTick();
|
|
|
|
// Verify table is paused
|
|
expect(element.querySelector('div.c-table.is-paused')).not.toBeNull();
|
|
});
|
|
|
|
it('Unpauses the table on user bounds change', async () => {
|
|
let firstRow = element.querySelector('table.c-telemetry-table__body > tbody > tr');
|
|
let clickEvent = createMouseEvent('click');
|
|
|
|
// Mark a row
|
|
firstRow.dispatchEvent(clickEvent);
|
|
|
|
await nextTick();
|
|
|
|
// Verify table is paused
|
|
expect(element.querySelector('div.c-table.is-paused')).not.toBeNull();
|
|
|
|
const currentBounds = openmct.time.bounds();
|
|
await nextTick();
|
|
const newBounds = {
|
|
start: currentBounds.start,
|
|
end: currentBounds.end - 3
|
|
};
|
|
|
|
// Manually change the time bounds
|
|
openmct.time.bounds(newBounds);
|
|
await nextTick();
|
|
|
|
// Verify table is no longer paused
|
|
expect(element.querySelector('div.c-table.is-paused')).toBeNull();
|
|
});
|
|
|
|
it('Unpauses the table on user bounds change if paused by button', async () => {
|
|
const viewContext = tableView.getViewContext();
|
|
|
|
// Pause by button
|
|
viewContext.togglePauseByButton();
|
|
await nextTick();
|
|
|
|
// Verify table is paused
|
|
expect(element.querySelector('div.c-table.is-paused')).not.toBeNull();
|
|
|
|
const currentBounds = openmct.time.bounds();
|
|
await nextTick();
|
|
|
|
const newBounds = {
|
|
start: currentBounds.start,
|
|
end: currentBounds.end - 1
|
|
};
|
|
// Manually change the time bounds
|
|
openmct.time.bounds(newBounds);
|
|
|
|
await nextTick();
|
|
|
|
// Verify table is no longer paused
|
|
expect(element.querySelector('div.c-table.is-paused')).toBeNull();
|
|
});
|
|
|
|
it('Does not unpause the table on tick', async () => {
|
|
const viewContext = tableView.getViewContext();
|
|
|
|
// Pause by button
|
|
viewContext.togglePauseByButton();
|
|
|
|
await nextTick();
|
|
|
|
// Verify table displays the correct number of rows
|
|
let tableRows = element.querySelectorAll('table.c-telemetry-table__body > tbody > tr');
|
|
expect(tableRows.length).toEqual(3);
|
|
|
|
// Verify table is paused
|
|
expect(element.querySelector('div.c-table.is-paused')).not.toBeNull();
|
|
|
|
// Tick the clock
|
|
openmct.time.tick(1);
|
|
|
|
await nextTick();
|
|
|
|
// Verify table is still paused
|
|
expect(element.querySelector('div.c-table.is-paused')).not.toBeNull();
|
|
|
|
await nextTick();
|
|
|
|
// Verify table displays the correct number of rows
|
|
tableRows = element.querySelectorAll('table.c-telemetry-table__body > tbody > tr');
|
|
expect(tableRows.length).toEqual(3);
|
|
});
|
|
});
|
|
});
|