From 51eb2a4f5952effdba9bdd9482a0cfc53a8037e3 Mon Sep 17 00:00:00 2001 From: Scott Bell Date: Wed, 8 Nov 2023 18:56:59 +0100 Subject: [PATCH] Add static limit values to LAD tables (#7193) * limits shown * use more verbose way for uniqueness * when adding items, check for what columns we need * make header logic much simpler * add test coverage * fix test and lint * Update e2e/tests/functional/plugins/lad/lad.e2e.spec.js Co-authored-by: Jesse Mazzella * Update e2e/tests/functional/plugins/lad/lad.e2e.spec.js Co-authored-by: Jesse Mazzella * change to getByTitle * lint and change to getByLabel --------- Co-authored-by: Jesse Mazzella --- .../functional/plugins/lad/lad.e2e.spec.js | 116 +++++++++++++++-- src/plugins/LADTable/components/LadRow.vue | 37 ++++++ src/plugins/LADTable/components/LadTable.vue | 25 +++- .../components/LadTableConfiguration.vue | 121 +++++------------- 4 files changed, 197 insertions(+), 102 deletions(-) diff --git a/e2e/tests/functional/plugins/lad/lad.e2e.spec.js b/e2e/tests/functional/plugins/lad/lad.e2e.spec.js index c48065ba98..efc61f6c69 100644 --- a/e2e/tests/functional/plugins/lad/lad.e2e.spec.js +++ b/e2e/tests/functional/plugins/lad/lad.e2e.spec.js @@ -25,21 +25,24 @@ const { createDomainObjectWithDefaults, setStartOffset, setFixedTimeMode, - setRealTimeMode + setRealTimeMode, + openObjectTreeContextMenu } = require('../../../../appActions'); test.describe('Testing LAD table configuration', () => { + let ladTable; + let sineWaveObject; test.beforeEach(async ({ page }) => { await page.goto('./', { waitUntil: 'domcontentloaded' }); // Create LAD table - const ladTable = await createDomainObjectWithDefaults(page, { + ladTable = await createDomainObjectWithDefaults(page, { type: 'LAD Table', name: 'Test LAD Table' }); // Create Sine Wave Generator - await createDomainObjectWithDefaults(page, { + sineWaveObject = await createDomainObjectWithDefaults(page, { type: 'Sine Wave Generator', name: 'Test Sine Wave Generator', parent: ladTable.uuid @@ -50,24 +53,28 @@ test.describe('Testing LAD table configuration', () => { test('in edit mode, LAD Tables provide ability to hide columns', async ({ page }) => { // Edit LAD table await page.locator('[title="Edit"]').click(); - - // // Expand the 'My Items' folder in the left tree - // await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click(); - // // Add the Sine Wave Generator to the LAD table and save changes - // await page.dragAndDrop('role=treeitem[name=/Test Sine Wave Generator/]', '.c-lad-table-wrapper'); - // select configuration tab in inspector await page.getByRole('tab', { name: 'LAD Table Configuration' }).click(); // make sure headers are visible initially await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible(); await expect(page.getByRole('cell', { name: 'Units' })).toBeVisible(); await expect(page.getByRole('cell', { name: 'Type' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'WATCH' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible(); // hide timestamp column await page.getByLabel('Timestamp').uncheck(); await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeHidden(); await expect(page.getByRole('cell', { name: 'Units' })).toBeVisible(); await expect(page.getByRole('cell', { name: 'Type' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'WATCH' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible(); // hide units & type column await page.getByLabel('Units').uncheck(); @@ -75,6 +82,22 @@ test.describe('Testing LAD table configuration', () => { await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeHidden(); await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden(); await expect(page.getByRole('cell', { name: 'Type' })).toBeHidden(); + await expect(page.getByRole('cell', { name: 'WATCH' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible(); + + // hide WATCH column + await page.getByLabel('WATCH').uncheck(); + await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeHidden(); + await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden(); + await expect(page.getByRole('cell', { name: 'Type' })).toBeHidden(); + await expect(page.getByRole('cell', { name: 'WATCH' })).toBeHidden(); + await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible(); // save and reload and verify they columns are still hidden await page.locator('button[title="Save"]').click(); @@ -83,6 +106,11 @@ test.describe('Testing LAD table configuration', () => { await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeHidden(); await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden(); await expect(page.getByRole('cell', { name: 'Type' })).toBeHidden(); + await expect(page.getByRole('cell', { name: 'WATCH' })).toBeHidden(); + await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible(); // Edit LAD table await page.locator('[title="Edit"]').click(); @@ -93,25 +121,41 @@ test.describe('Testing LAD table configuration', () => { await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden(); await expect(page.getByRole('cell', { name: 'Type' })).toBeHidden(); await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'WATCH' })).toBeHidden(); + await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible(); - // save and reload and make sure only timestamp is still visible + // save and reload and make sure timestamp is still visible await page.locator('button[title="Save"]').click(); await page.locator('text=Save and Finish Editing').click(); await page.reload(); await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden(); await expect(page.getByRole('cell', { name: 'Type' })).toBeHidden(); await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'WATCH' })).toBeHidden(); + await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible(); // Edit LAD table await page.locator('[title="Edit"]').click(); await page.getByRole('tab', { name: 'LAD Table Configuration' }).click(); - // show units and type columns + // show units, type, and WATCH columns await page.getByLabel('Units').check(); await page.getByLabel('Type').check(); + await page.getByLabel('WATCH').check(); await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible(); await expect(page.getByRole('cell', { name: 'Units' })).toBeVisible(); await expect(page.getByRole('cell', { name: 'Type' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'WATCH' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible(); // save and reload and make sure all columns are still visible await page.locator('button[title="Save"]').click(); @@ -120,6 +164,56 @@ test.describe('Testing LAD table configuration', () => { await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible(); await expect(page.getByRole('cell', { name: 'Units' })).toBeVisible(); await expect(page.getByRole('cell', { name: 'Type' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'WATCH' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible(); + }); + + test('When adding something without Units, do not show Units column', async ({ page }) => { + // Create Sine Wave Generator + await createDomainObjectWithDefaults(page, { + type: 'Event Message Generator', + parent: ladTable.uuid + }); + + await page.goto(ladTable.url); + + // Edit LAD table + await page.getByLabel('Edit').click(); + await page.getByRole('tab', { name: 'LAD Table Configuration' }).click(); + + // make sure Sine Wave headers are visible initially too + await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'Units' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'Type' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'WATCH' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible(); + + // save and reload and verify they columns are still hidden + await page.getByLabel('Save').click(); + await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); + + // Remove Sin Wave Generator + openObjectTreeContextMenu(page, sineWaveObject.url); + await page.getByRole('menuitem', { name: /Remove/ }).click(); + await page.getByRole('button', { name: 'OK' }).click(); + + // Ensure Units & Limit columns are gone + // as Event Generator don't have them + await page.goto(ladTable.url); + await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'Type' })).toBeVisible(); + await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden(); + await expect(page.getByRole('cell', { name: 'WATCH' })).toBeHidden(); + await expect(page.getByRole('cell', { name: 'WARNING' })).toBeHidden(); + await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeHidden(); + await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeHidden(); + await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeHidden(); }); test("LAD Tables don't allow selection of rows but does show context click menus", async ({ diff --git a/src/plugins/LADTable/components/LadRow.vue b/src/plugins/LADTable/components/LadRow.vue index 1bc00008f6..6f9e57b7a0 100644 --- a/src/plugins/LADTable/components/LadRow.vue +++ b/src/plugins/LADTable/components/LadRow.vue @@ -40,6 +40,9 @@ {{ unit }} {{ typeLabel }} + + {{ limit.value }} + @@ -77,6 +80,19 @@ export default { configuration: { type: Object, required: true + }, + limitDefinition: { + type: Object, + default() { + return {}; + } + }, + limitColumnNames: { + // for ordering + type: Array, + default() { + return []; + } } }, emits: ['row-context-click'], @@ -85,6 +101,7 @@ export default { datum: undefined, timestamp: undefined, timestampKey: undefined, + valueKey: null, composition: [], unit: '' }; @@ -97,6 +114,26 @@ export default { return this.formats[this.valueKey].format(this.datum); }, + formattedLimitValues() { + if (!this.valueKey) { + return []; + } + return this.limitColumnNames.map((column) => { + if (this.limitDefinition?.[column.key]) { + const highValue = this.limitDefinition[column.key].high[this.valueKey]; + const lowValue = this.limitDefinition[column.key].low[this.valueKey]; + return { + key: column.key, + value: `${lowValue} → ${highValue}` + }; + } else { + return { + key: column.key, + value: BLANK_VALUE + }; + } + }); + }, typeLabel() { if (this.isAggregate) { return 'Aggregate'; diff --git a/src/plugins/LADTable/components/LadTable.vue b/src/plugins/LADTable/components/LadTable.vue index a9980d3712..1a73e49d97 100644 --- a/src/plugins/LADTable/components/LadTable.vue +++ b/src/plugins/LADTable/components/LadTable.vue @@ -30,6 +30,9 @@ Value Units Type + + {{ limitColumn.label }} + @@ -37,6 +40,8 @@ v-for="ladRow in items" :key="ladRow.key" :domain-object="ladRow.domainObject" + :limit-definition="ladRow.limitDefinition" + :limit-column-names="limitColumnNames" :path-to-table="objectPath" :has-units="hasUnits" :is-stale="staleObjects.includes(ladRow.key)" @@ -89,6 +94,23 @@ export default { return itemsWithUnits.length !== 0 && !this.configuration?.hiddenColumns?.units; }, + limitColumnNames() { + const limitDefinitions = []; + + this.items.forEach((item) => { + if (item.limitDefinition) { + const limits = Object.keys(item.limitDefinition); + limits.forEach((limit) => { + const limitAlreadyAdded = limitDefinitions.some((limitDef) => limitDef.key === limit); + const limitHidden = this.configuration?.hiddenColumns?.[limit]; + if (!limitAlreadyAdded && !limitHidden) { + limitDefinitions.push({ label: `Limit ${limit}`, key: limit }); + } + }); + } + }); + return limitDefinitions; + }, showTimestamp() { return !this.configuration?.hiddenColumns?.timestamp; }, @@ -149,10 +171,11 @@ export default { this.composition.off('reorder', this.reorder); }, methods: { - addItem(domainObject) { + async addItem(domainObject) { let item = {}; item.domainObject = domainObject; item.key = this.openmct.objects.makeKeyString(domainObject.identifier); + item.limitDefinition = await this.openmct.telemetry.limitDefinition(domainObject).limits(); this.items.push(item); this.subscribeToStaleness(domainObject); diff --git a/src/plugins/LADTable/components/LadTableConfiguration.vue b/src/plugins/LADTable/components/LadTableConfiguration.vue index e13d9d5ecf..d7c7f23a16 100644 --- a/src/plugins/LADTable/components/LadTableConfiguration.vue +++ b/src/plugins/LADTable/components/LadTableConfiguration.vue @@ -58,23 +58,37 @@ export default { const ladTableConfiguration = new LADTableConfiguration(domainObject, this.openmct); return { - headers: { - timestamp: 'Timestamp', - units: 'Units', - type: 'Type' - }, ladTableConfiguration, isEditing: this.openmct.editor.isEditing(), configuration: ladTableConfiguration.getConfiguration(), items: [], - ladTableObjects: [], - ladTelemetryObjects: {} + ladTableObjects: [] }; }, + computed: { + headers() { + const fullHeaders = { + timestamp: 'Timestamp', + type: 'Type' + }; + // check hasUnits and limitColumnName and add then to fullHeaders + this.items.forEach((item) => { + if (item.hasUnits) { + fullHeaders.units = 'Units'; + } + if (item.limitDefinition) { + const limits = Object.keys(item.limitDefinition); + limits.forEach((limit) => { + fullHeaders[limit] = limit; + }); + } + }); + return fullHeaders; + } + }, mounted() { this.openmct.editor.on('isEditing', this.toggleEdit); this.composition = this.openmct.composition.get(this.ladTableConfiguration.domainObject); - this.shouldShowUnitsCheckbox(); if (this.ladTableConfiguration.domainObject.type === 'LadTable') { this.composition.on('add', this.addItem); @@ -104,22 +118,25 @@ export default { } }, methods: { - addItem(domainObject) { + async addItem(domainObject) { const item = {}; item.domainObject = domainObject; item.key = this.openmct.objects.makeKeyString(domainObject.identifier); + item.limitDefinition = await this.openmct.telemetry.limitDefinition(domainObject).limits(); + + const metadata = this.openmct.telemetry.getMetadata(domainObject); + const valueMetadatas = metadata ? metadata.valueMetadatas : []; + const metadataWithUnits = valueMetadatas.filter((metadatum) => metadatum.unit); + + item.hasUnits = metadataWithUnits.length > 0; this.items.push(item); - - this.shouldShowUnitsCheckbox(); }, removeItem(identifier) { const keystring = this.openmct.objects.makeKeyString(identifier); const index = this.items.findIndex((item) => keystring === item.key); this.items.splice(index, 1); - - this.shouldShowUnitsCheckbox(); }, addLadTable(domainObject) { let ladTable = {}; @@ -130,20 +147,12 @@ export default { this.ladTableObjects.push(ladTable); const composition = this.openmct.composition.get(ladTable.domainObject); - const addCallback = this.addTelemetryObject(ladTable); - const removeCallback = this.removeTelemetryObject(ladTable); - composition.on('add', addCallback); - composition.on('remove', removeCallback); composition.load(); this.compositions.push({ - composition, - addCallback, - removeCallback + composition }); - - this.shouldShowUnitsCheckbox(); }, removeLadTable(identifier) { const index = this.ladTableObjects.findIndex( @@ -153,36 +162,6 @@ export default { delete this.ladTelemetryObjects[ladTable.key]; this.ladTableObjects.splice(index, 1); - - this.shouldShowUnitsCheckbox(); - }, - addTelemetryObject(ladTable) { - return (domainObject) => { - const telemetryObject = {}; - telemetryObject.key = this.openmct.objects.makeKeyString(domainObject.identifier); - telemetryObject.domainObject = domainObject; - - const telemetryObjects = this.ladTelemetryObjects[ladTable.key]; - telemetryObjects.push(telemetryObject); - - this.ladTelemetryObjects[ladTable.key] = telemetryObjects; - - this.shouldShowUnitsCheckbox(); - }; - }, - removeTelemetryObject(ladTable) { - return (identifier) => { - const keystring = this.openmct.objects.makeKeyString(identifier); - const telemetryObjects = this.ladTelemetryObjects[ladTable.key]; - const index = telemetryObjects.findIndex( - (telemetryObject) => keystring === telemetryObject.key - ); - - telemetryObjects.splice(index, 1); - this.ladTelemetryObjects[ladTable.key] = telemetryObjects; - - this.shouldShowUnitsCheckbox(); - }; }, combineKeys(ladKey, telemetryObjectKey) { return `${ladKey}-${telemetryObjectKey}`; @@ -195,44 +174,6 @@ export default { }, toggleEdit(isEditing) { this.isEditing = isEditing; - }, - shouldShowUnitsCheckbox() { - let showUnitsCheckbox = false; - - if (this.ladTableConfiguration?.domainObject) { - if (this.ladTableConfiguration.domainObject.type === 'LadTable') { - const itemsWithUnits = this.items.filter((item) => { - return this.metadataHasUnits(item.domainObject); - }); - - showUnitsCheckbox = itemsWithUnits.length !== 0; - } else { - const ladTables = Object.values(this.ladTelemetryObjects); - - for (const ladTable of ladTables) { - for (const telemetryObject of ladTable) { - if (this.metadataHasUnits(telemetryObject.domainObject)) { - showUnitsCheckbox = true; - } - } - } - } - } - - if (showUnitsCheckbox && this.headers.units === undefined) { - this.headers.units = 'Units'; - } - - if (!showUnitsCheckbox && this.headers?.units) { - delete this.headers.units; - } - }, - metadataHasUnits(domainObject) { - const metadata = this.openmct.telemetry.getMetadata(domainObject); - const valueMetadatas = metadata ? metadata.valueMetadatas : []; - const metadataWithUnits = valueMetadatas.filter((metadatum) => metadatum.unit); - - return metadataWithUnits.length > 0; } } };