diff --git a/e2e/tests/plugins/telemetryTable/telemetryTable.e2e.spec.js b/e2e/tests/plugins/telemetryTable/telemetryTable.e2e.spec.js new file mode 100644 index 0000000000..ceffa3ae5f --- /dev/null +++ b/e2e/tests/plugins/telemetryTable/telemetryTable.e2e.spec.js @@ -0,0 +1,101 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2022, 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. + *****************************************************************************/ + +const { test } = require('../../../fixtures'); +const { expect } = require('@playwright/test'); + +test.describe('Telemetry Table', () => { + test('unpauses when paused by button and user changes bounds', async ({ page }) => { + test.info().annotations.push({ + type: 'issue', + description: 'https://github.com/nasa/openmct/issues/5113' + }); + + const bannerMessage = '.c-message-banner__message'; + const createButton = 'button:has-text("Create")'; + + await page.goto('/', { waitUntil: 'networkidle' }); + + // Click create button + await page.locator(createButton).click(); + await page.locator('li:has-text("Telemetry Table")').click(); + + // Click on My Items in Tree. Workaround for https://github.com/nasa/openmct/issues/5184 + await page.click('form[name="mctForm"] a:has-text("My Items")'); + + await Promise.all([ + page.waitForNavigation(), + page.locator('text=OK').click(), + // Wait for Save Banner to appear + page.waitForSelector(bannerMessage) + ]); + + // Save (exit edit mode) + await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(3).click(); + await page.locator('text=Save and Finish Editing').click(); + + // Click create button + await page.locator(createButton).click(); + + // add Sine Wave Generator with defaults + await page.locator('li:has-text("Sine Wave Generator")').click(); + + // Click on My Items in Tree. Workaround for https://github.com/nasa/openmct/issues/5184 + await page.click('form[name="mctForm"] a:has-text("My Items")'); + + await Promise.all([ + page.waitForNavigation(), + page.locator('text=OK').click(), + // Wait for Save Banner to appear + page.waitForSelector(bannerMessage) + ]); + + // focus the Telemetry Table + await page.locator('text=Open MCT My Items >> span').nth(3).click(); + await Promise.all([ + page.waitForNavigation(), + page.locator('text=Unnamed Telemetry Table').first().click() + ]); + + // Click pause button + const pauseButton = await page.locator('button.c-button.icon-pause'); + await pauseButton.click(); + + const tableWrapper = await page.locator('div.c-table-wrapper'); + await expect(tableWrapper).toHaveClass(/is-paused/); + + // Arbitrarily change end date to some time in the future + const endTimeInput = page.locator('input[type="text"].c-input--datetime').nth(1); + await endTimeInput.click(); + + let endDate = await endTimeInput.inputValue(); + endDate = new Date(endDate); + endDate.setUTCDate(endDate.getUTCDate() + 1); + endDate = endDate.toISOString().replace(/T.*/, ''); + + await endTimeInput.fill(''); + await endTimeInput.fill(endDate); + await page.keyboard.press('Enter'); + + await expect(tableWrapper).not.toHaveClass(/is-paused/); + }); +}); diff --git a/src/plugins/telemetryTable/components/table.vue b/src/plugins/telemetryTable/components/table.vue index 59172615b2..af34cb0221 100644 --- a/src/plugins/telemetryTable/components/table.vue +++ b/src/plugins/telemetryTable/components/table.vue @@ -499,6 +499,8 @@ export default { this.table.tableRows.on('sort', this.updateVisibleRows); this.table.tableRows.on('filter', this.updateVisibleRows); + this.openmct.time.on('bounds', this.boundsChanged); + //Default sort this.sortOptions = this.table.tableRows.sortBy(); this.scrollable = this.$el.querySelector('.js-telemetry-table__body-w'); @@ -513,7 +515,7 @@ export default { this.table.initialize(); }, - destroyed() { + beforeDestroy() { this.table.off('object-added', this.addObject); this.table.off('object-removed', this.removeObject); this.table.off('historical-rows-processed', this.checkForMarkedRows); @@ -527,6 +529,8 @@ export default { this.table.configuration.off('change', this.updateConfiguration); + this.openmct.time.off('bounds', this.boundsChanged); + clearInterval(this.resizePollHandle); this.table.configuration.destroy(); @@ -823,16 +827,16 @@ export default { this.visibleRows = []; this.$nextTick().then(this.updateVisibleRows); }, - pause(pausedByButton) { - if (pausedByButton) { + pause(byButton) { + if (byButton) { this.pausedByButton = true; } this.paused = true; this.table.pause(); }, - unpause(unpausedByButton) { - if (unpausedByButton) { + unpause(byButtonOrUserBoundsChange) { + if (byButtonOrUserBoundsChange) { this.undoMarkedRows(); this.table.unpause(); this.paused = false; @@ -847,6 +851,16 @@ export default { this.isShowingMarkedRowsOnly = false; }, + boundsChanged(_bounds, isTick) { + if (isTick) { + return; + } + + // User bounds change. + if (this.paused) { + this.unpause(true); + } + }, togglePauseByButton() { if (this.paused) { this.unpause(true); @@ -854,7 +868,7 @@ export default { this.pause(true); } }, - undoMarkedRows(unpause) { + undoMarkedRows() { this.markedRows.forEach(r => r.marked = false); this.markedRows = []; }, diff --git a/src/plugins/telemetryTable/pluginSpec.js b/src/plugins/telemetryTable/pluginSpec.js index 1d29c2e683..9d8892f3c2 100644 --- a/src/plugins/telemetryTable/pluginSpec.js +++ b/src/plugins/telemetryTable/pluginSpec.js @@ -133,6 +133,7 @@ describe("the plugin", () => { let tableViewProvider; let tableView; let tableInstance; + let mockClock; beforeEach(() => { openmct.time.timeSystem('utc', { @@ -140,6 +141,20 @@ describe("the plugin", () => { 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: "", @@ -360,5 +375,115 @@ describe("the plugin", () => { 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 Vue.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 Vue.nextTick(); + + // Verify table is paused + expect(element.querySelector('div.c-table.is-paused')).not.toBeNull(); + + const currentBounds = openmct.time.bounds(); + + const newBounds = { + start: currentBounds.start, + end: currentBounds.end - 3 + }; + + // Manually change the time bounds + openmct.time.bounds(newBounds); + + await Vue.nextTick(); + + // Verify table is no longer paused + expect(element.querySelector('div.c-table.is-paused')).toBeNull(); + + await Vue.nextTick(); + + // Verify table displays the correct number of rows within the new bounds + const tableRows = element.querySelectorAll('table.c-telemetry-table__body > tbody > tr'); + expect(tableRows.length).toEqual(2); + }); + + it("Unpauses the table on user bounds change if paused by button", async () => { + const viewContext = tableView.getViewContext(); + + // Pause by button + viewContext.togglePauseByButton(); + + await Vue.nextTick(); + + // Verify table is paused + expect(element.querySelector('div.c-table.is-paused')).not.toBeNull(); + + const currentBounds = openmct.time.bounds(); + + const newBounds = { + start: currentBounds.start, + end: currentBounds.end - 3 + }; + + // Manually change the time bounds + openmct.time.bounds(newBounds); + + await Vue.nextTick(); + + // Verify table is no longer paused + expect(element.querySelector('div.c-table.is-paused')).toBeNull(); + + await Vue.nextTick(); + + // Verify table displays the correct number of rows within the new bounds + const tableRows = element.querySelectorAll('table.c-telemetry-table__body > tbody > tr'); + expect(tableRows.length).toEqual(2); + }); + + it("Does not unpause the table on tick", async () => { + const viewContext = tableView.getViewContext(); + + // Pause by button + viewContext.togglePauseByButton(); + + await Vue.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 Vue.nextTick(); + + // Verify table is still paused + expect(element.querySelector('div.c-table.is-paused')).not.toBeNull(); + + await Vue.nextTick(); + + // Verify table displays the correct number of rows + tableRows = element.querySelectorAll('table.c-telemetry-table__body > tbody > tr'); + expect(tableRows.length).toEqual(3); + }); }); });