diff --git a/e2e/appActions.js b/e2e/appActions.js index c3edf98242..07780c0b70 100644 --- a/e2e/appActions.js +++ b/e2e/appActions.js @@ -592,9 +592,6 @@ async function waitForPlotsToRender(page) { * @return {Promise} */ async function getCanvasPixels(page, canvasSelector) { - const getTelemValuePromise = new Promise((resolve) => - page.exposeFunction('getCanvasValue', resolve) - ); const canvasHandle = await page.evaluateHandle( (canvas) => document.querySelector(canvas), canvasSelector @@ -605,7 +602,7 @@ async function getCanvasPixels(page, canvasSelector) { ); await waitForPlotsToRender(page); - await page.evaluate( + return page.evaluate( ([canvas, ctx]) => { // The document canvas is where the plot points and lines are drawn. // The only way to access the canvas is using document (using page.evaluate) @@ -633,12 +630,10 @@ async function getCanvasPixels(page, canvasSelector) { i = i + 4; } - window.getCanvasValue(plotPixels); + return plotPixels; }, [canvasHandle, canvasContextHandle] ); - - return getTelemValuePromise; } /** diff --git a/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js b/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js index 55b97fc3fc..9cfbd63cfd 100644 --- a/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js +++ b/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js @@ -26,6 +26,7 @@ Testsuite for plot autoscale. import { createDomainObjectWithDefaults } from '../../../../appActions.js'; import { expect, test } from '../../../../pluginFixtures.js'; +import { setUserDefinedMinAndMax, turnOffAutoscale } from './plotActions.js'; test.use({ viewport: { width: 1280, @@ -127,26 +128,6 @@ test.describe('Autoscale', () => { }); }); -/** - * @param {import('@playwright/test').Page} page - */ -async function turnOffAutoscale(page) { - // uncheck autoscale - await page.getByRole('checkbox', { name: 'Auto scale' }).uncheck(); -} - -/** - * @param {import('@playwright/test').Page} page - * @param {string} min - * @param {string} max - */ -async function setUserDefinedMinAndMax(page, min, max) { - // set minimum value - await page.getByRole('spinbutton').first().fill(min); - // set maximum value - await page.getByRole('spinbutton').nth(1).fill(max); -} - /** * @param {import('@playwright/test').Page} page */ diff --git a/e2e/tests/functional/plugins/plot/plotActions.js b/e2e/tests/functional/plugins/plot/plotActions.js new file mode 100644 index 0000000000..14e697f3c6 --- /dev/null +++ b/e2e/tests/functional/plugins/plot/plotActions.js @@ -0,0 +1,42 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2024, 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. + *****************************************************************************/ +/** + * @param {import('@playwright/test').Page} page + */ +async function turnOffAutoscale(page) { + // uncheck autoscale + await page.getByRole('checkbox', { name: 'Auto scale' }).uncheck(); +} + +/** + * @param {import('@playwright/test').Page} page + * @param {string} min + * @param {string} max + */ +async function setUserDefinedMinAndMax(page, min, max) { + // set minimum value + await page.getByRole('spinbutton').first().fill(min); + // set maximum value + await page.getByRole('spinbutton').nth(1).fill(max); +} + +export { setUserDefinedMinAndMax, turnOffAutoscale }; diff --git a/e2e/tests/functional/plugins/plot/plotControls.e2e.spec.js b/e2e/tests/functional/plugins/plot/plotControls.e2e.spec.js new file mode 100644 index 0000000000..b66e0a8e65 --- /dev/null +++ b/e2e/tests/functional/plugins/plot/plotControls.e2e.spec.js @@ -0,0 +1,116 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2024, 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. + *****************************************************************************/ + +/* + * This test suite is dedicated to testing the rendering and interaction of plots. + * + */ + +import { + createDomainObjectWithDefaults, + getCanvasPixels, + setEndOffset, + setRealTimeMode, + setStartOffset +} from '../../../../appActions.js'; +import { expect, test } from '../../../../pluginFixtures.js'; +import { setUserDefinedMinAndMax, turnOffAutoscale } from './plotActions.js'; + +test.describe('Plot Controls', () => { + let overlayPlot; + + test.beforeEach(async ({ page }) => { + // Open a browser, navigate to the main page, and wait until all networkevents to resolve + await page.goto('./', { waitUntil: 'domcontentloaded' }); + overlayPlot = await createDomainObjectWithDefaults(page, { + type: 'Overlay Plot' + }); + + // Create an overlay plot with a sine wave generator + await createDomainObjectWithDefaults(page, { + type: 'Sine Wave Generator', + parent: overlayPlot.uuid + }); + await page.goto(`${overlayPlot.url}`); + }); + + test("Plots don't purge data when paused", async ({ page }) => { + // Set realtime mode with 2 second window + const startOffset = { + startMins: '00', + startSecs: '01' + }; + + const endOffset = { + endMins: '00', + endSecs: '01' + }; + + // Switch to real-time mode + await setRealTimeMode(page); + + // Set start time offset + await setStartOffset(page, startOffset); + + // Set end time offset + await setEndOffset(page, endOffset); + // Edit the overlay plot and turn off auto scale, setting the min and max to -1 and 1 + // enter edit mode + await page.getByLabel('Edit Object').click(); + + await page.getByRole('tab', { name: 'Config' }).click(); + await turnOffAutoscale(page); + + await setUserDefinedMinAndMax(page, '-1', '1'); + + // save + await page.click('button[title="Save"]'); + await Promise.all([ + page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(), + //Wait for Save Banner to appear + page.waitForSelector('.c-message-banner__message') + ]); + //Wait until Save Banner is gone + await page.locator('.c-message-banner__close-button').click(); + await page.waitForSelector('.c-message-banner__message', { state: 'detached' }); + // hover over plot for plot controls + await page.getByLabel('Plot Canvas').hover(); + // click on pause control + await page.getByTitle('Pause incoming real-time data').click(); + // expect plot to be paused + await expect(page.getByTitle('Resume displaying real-time data')).toBeVisible(); + // Wait for 2 seconds to stabilize plot data - future timestamp + // eslint-disable-next-line + await page.waitForTimeout(2000); + // Capture the # of plot points + const plotPixels = await getCanvasPixels(page, 'canvas'); + const plotPixelSizeAtPause = plotPixels.length; + // Wait 2 seconds + // eslint-disable-next-line + await page.waitForTimeout(2000); + // Capture the # of plot points + const plotPixelsAfterWait = await getCanvasPixels(page, 'canvas'); + const plotPixelSizeAfterWait = plotPixelsAfterWait.length; + // Expect before and after plot points to match + await expect(plotPixelSizeAtPause).toEqual(plotPixelSizeAfterWait); + }); +}); diff --git a/src/plugins/plot/MctPlot.vue b/src/plugins/plot/MctPlot.vue index 9e6c66ea56..99ec77326c 100644 --- a/src/plugins/plot/MctPlot.vue +++ b/src/plugins/plot/MctPlot.vue @@ -803,12 +803,12 @@ export default { this.synchronizeIfBoundsMatch(); this.loadMoreData(newRange, true); } else { - // If we're not panning or zooming (time conductor and plot x-axis times are not out of sync) + // If we're not paused, panning or zooming (time conductor and plot x-axis times are not out of sync) // Drop any data that is more than 1x (max-min) before min. // Limit these purges to once a second. const isPanningOrZooming = this.isTimeOutOfSync; const purgeRecords = - !isPanningOrZooming && (!this.nextPurge || this.nextPurge < Date.now()); + !this.isFrozen && !isPanningOrZooming && (!this.nextPurge || this.nextPurge < Date.now()); if (purgeRecords) { const keepRange = { min: newRange.min - (newRange.max - newRange.min), diff --git a/src/plugins/timeConductor/TimePopupRealtime.vue b/src/plugins/timeConductor/TimePopupRealtime.vue index 2e8915f849..326f21bd72 100644 --- a/src/plugins/timeConductor/TimePopupRealtime.vue +++ b/src/plugins/timeConductor/TimePopupRealtime.vue @@ -96,6 +96,7 @@ max="59" title="Enter 0 - 59" step="1" + aria-label="End offset minutes" @change="validate()" @keyup="validate()" @focusin="selectAll($event)"