mirror of
https://github.com/nasa/openmct.git
synced 2024-12-18 20:57:53 +00:00
* more readable * unpause explicitly * fix jsdoc * e2e testing multiple image removal * prettier * fix to remove multiple images from history * move tests that use playwright clock api into own file * fix playwright clock tests * add aria-label to element * prevent straggler debounced function call on unmount * clean up and fix tests * update paths * lint fix * lint fix --------- Co-authored-by: John Hill <john.c.hill@nasa.gov>
This commit is contained in:
parent
e792403788
commit
a8fbabe695
33
e2e/helper/imageryUtils.js
Normal file
33
e2e/helper/imageryUtils.js
Normal file
@ -0,0 +1,33 @@
|
||||
import { createDomainObjectWithDefaults } from '../appActions.js';
|
||||
import { expect } from '../pluginFixtures.js';
|
||||
|
||||
const IMAGE_LOAD_DELAY = 5 * 1000;
|
||||
const FIVE_MINUTES = 1000 * 60 * 5;
|
||||
const THIRTY_SECONDS = 1000 * 30;
|
||||
const MOUSE_WHEEL_DELTA_Y = 120;
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
async function createImageryViewWithShortDelay(page, { name, parent }) {
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
name,
|
||||
type: 'Example Imagery',
|
||||
parent
|
||||
});
|
||||
|
||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery');
|
||||
await page.getByLabel('More actions').click();
|
||||
await page.getByLabel('Edit Properties').click();
|
||||
// Clear and set Image load delay to minimum value
|
||||
await page.locator('input[type="number"]').fill(`${IMAGE_LOAD_DELAY}`);
|
||||
await page.getByLabel('Save').click();
|
||||
}
|
||||
|
||||
export {
|
||||
createImageryViewWithShortDelay,
|
||||
FIVE_MINUTES,
|
||||
IMAGE_LOAD_DELAY,
|
||||
MOUSE_WHEEL_DELTA_Y,
|
||||
THIRTY_SECONDS
|
||||
};
|
@ -30,16 +30,19 @@ import {
|
||||
navigateToObjectWithRealTime,
|
||||
setRealTimeMode
|
||||
} from '../../../../appActions.js';
|
||||
import { MISSION_TIME } from '../../../../constants.js';
|
||||
import {
|
||||
createImageryViewWithShortDelay,
|
||||
FIVE_MINUTES,
|
||||
IMAGE_LOAD_DELAY,
|
||||
MOUSE_WHEEL_DELTA_Y,
|
||||
THIRTY_SECONDS
|
||||
} from '../../../../helper/imageryUtils.js';
|
||||
import { expect, test } from '../../../../pluginFixtures.js';
|
||||
|
||||
const panHotkey = process.platform === 'linux' ? ['Shift', 'Alt'] : ['Alt'];
|
||||
const tagHotkey = ['Shift', 'Alt'];
|
||||
const expectedAltText = process.platform === 'linux' ? 'Shift+Alt drag to pan' : 'Alt drag to pan';
|
||||
const thumbnailUrlParamsRegexp = /\?w=100&h=100/;
|
||||
const IMAGE_LOAD_DELAY = 5 * 1000;
|
||||
const MOUSE_WHEEL_DELTA_Y = 120;
|
||||
const FIVE_MINUTES = 1000 * 60 * 5;
|
||||
const THIRTY_SECONDS = 1000 * 30;
|
||||
|
||||
//The following block of tests verifies the basic functionality of example imagery and serves as a template for Imagery objects embedded in other objects.
|
||||
test.describe('Example Imagery Object', () => {
|
||||
@ -357,15 +360,10 @@ test.describe('Example Imagery Object', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Example Imagery in Display Layout @clock', () => {
|
||||
test.describe('Example Imagery in Display Layout', () => {
|
||||
let displayLayout;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// We mock the clock so that we don't need to wait for time driven events
|
||||
// to verify functionality.
|
||||
await page.clock.install({ time: MISSION_TIME });
|
||||
await page.clock.resume();
|
||||
|
||||
// Go to baseURL
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
|
||||
@ -428,12 +426,7 @@ test.describe('Example Imagery in Display Layout @clock', () => {
|
||||
await expect.soft(pausePlayButton).toHaveClass(/is-paused/);
|
||||
});
|
||||
|
||||
test('Imagery View operations @clock', async ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/5265'
|
||||
});
|
||||
|
||||
test('Imagery View operations', async ({ page }) => {
|
||||
// Edit mode
|
||||
await page.getByLabel('Edit Object').click();
|
||||
|
||||
@ -526,14 +519,9 @@ test.describe('Example Imagery in Display Layout @clock', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Example Imagery in Flexible layout @clock', () => {
|
||||
test.describe('Example Imagery in Flexible layout', () => {
|
||||
let flexibleLayout;
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// We mock the clock so that we don't need to wait for time driven events
|
||||
// to verify functionality.
|
||||
await page.clock.install({ time: MISSION_TIME });
|
||||
await page.clock.resume();
|
||||
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
|
||||
flexibleLayout = await createDomainObjectWithDefaults(page, { type: 'Flexible Layout' });
|
||||
@ -562,7 +550,7 @@ test.describe('Example Imagery in Flexible layout @clock', () => {
|
||||
await page.getByRole('button', { name: 'Close' }).click();
|
||||
});
|
||||
|
||||
test('Imagery View operations @clock', async ({ page, browserName }) => {
|
||||
test('Imagery View operations', async ({ page, browserName }) => {
|
||||
test.fixme(browserName === 'firefox', 'This test needs to be updated to work with firefox');
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
@ -573,14 +561,10 @@ test.describe('Example Imagery in Flexible layout @clock', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Example Imagery in Tabs View @clock', () => {
|
||||
test.describe('Example Imagery in Tabs View', () => {
|
||||
let tabsView;
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// We mock the clock so that we don't need to wait for time driven events
|
||||
// to verify functionality.
|
||||
await page.clock.install({ time: MISSION_TIME });
|
||||
await page.clock.resume();
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
|
||||
tabsView = await createDomainObjectWithDefaults(page, { type: 'Tabs View' });
|
||||
@ -607,7 +591,8 @@ test.describe('Example Imagery in Tabs View @clock', () => {
|
||||
// Wait for image thumbnail auto-scroll to complete
|
||||
await expect(page.getByLabel('Image Thumbnail from').last()).toBeInViewport();
|
||||
});
|
||||
test('Imagery View operations @clock', async ({ page }) => {
|
||||
|
||||
test('Imagery View operations', async ({ page }) => {
|
||||
await performImageryViewOperationsAndAssert(page, tabsView);
|
||||
});
|
||||
});
|
||||
@ -668,16 +653,19 @@ test.describe('Example Imagery in Time Strip', () => {
|
||||
* 3. Can pan the image using the pan hotkey + mouse drag
|
||||
* 4. Clicking on the left arrow button pauses imagery and moves to the previous image
|
||||
* 5. Imagery is updated as new images stream in, regardless of pause status
|
||||
* 6. Old images are discarded when new images stream in
|
||||
* 7. Image brightness/contrast can be adjusted by dragging the sliders
|
||||
* 6. Old images are discarded when their timestamps fall out of bounds
|
||||
* 7. Multiple images can be discarded when their timestamps fall out of bounds
|
||||
* 8. Image brightness/contrast can be adjusted by dragging the sliders
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
async function performImageryViewOperationsAndAssert(page, layoutObject) {
|
||||
// Verify that imagery thumbnails use a thumbnail url
|
||||
const thumbnailImages = page.getByLabel('Image thumbnail from').locator('.c-thumb__image');
|
||||
const mainImage = page.locator('.c-imagery__main-image__image');
|
||||
await expect(thumbnailImages.first()).toHaveAttribute('src', thumbnailUrlParamsRegexp);
|
||||
await expect(mainImage).not.toHaveAttribute('src', thumbnailUrlParamsRegexp);
|
||||
await test.step('Verify that imagery thumbnails use a thumbnail url', async () => {
|
||||
const thumbnailImages = page.getByLabel('Image thumbnail from').locator('.c-thumb__image');
|
||||
const mainImage = page.locator('.c-imagery__main-image__image');
|
||||
await expect(thumbnailImages.first()).toHaveAttribute('src', thumbnailUrlParamsRegexp);
|
||||
await expect(mainImage).not.toHaveAttribute('src', thumbnailUrlParamsRegexp);
|
||||
});
|
||||
|
||||
// Click previous image button
|
||||
const previousImageButton = page.getByLabel('Previous image');
|
||||
await expect(previousImageButton).toBeVisible();
|
||||
@ -736,19 +724,6 @@ async function performImageryViewOperationsAndAssert(page, layoutObject) {
|
||||
// Unpause imagery
|
||||
await page.locator('.pause-play').click();
|
||||
|
||||
// verify that old images are discarded
|
||||
const lastImageInBounds = page.getByLabel('Image thumbnail from').first();
|
||||
const lastImageTimestamp = await lastImageInBounds.getAttribute('title');
|
||||
expect(lastImageTimestamp).not.toBeNull();
|
||||
|
||||
// go forward in time to ensure old images are discarded
|
||||
await page.clock.fastForward(IMAGE_LOAD_DELAY);
|
||||
await page.clock.resume();
|
||||
await expect(page.getByLabel(lastImageTimestamp)).toBeHidden();
|
||||
|
||||
//Get background-image url from background-image css prop
|
||||
await assertBackgroundImageUrlFromBackgroundCss(page);
|
||||
|
||||
// Open the image filter menu
|
||||
await page.locator('[role=toolbar] button[title="Brightness and contrast"]').click();
|
||||
|
||||
@ -815,24 +790,6 @@ async function assertBackgroundImageBrightness(page, expected) {
|
||||
expect(actual).toBe(expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
async function assertBackgroundImageUrlFromBackgroundCss(page) {
|
||||
const backgroundImage = page.getByLabel('Focused Image Element');
|
||||
const backgroundImageUrl = await backgroundImage.evaluate((el) => {
|
||||
return window
|
||||
.getComputedStyle(el)
|
||||
.getPropertyValue('background-image')
|
||||
.match(/url\(([^)]+)\)/)[1];
|
||||
});
|
||||
|
||||
// go forward in time to ensure old images are discarded
|
||||
await page.clock.fastForward(IMAGE_LOAD_DELAY);
|
||||
await page.clock.resume();
|
||||
await expect(backgroundImage).not.toHaveJSProperty('background-image', backgroundImageUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
@ -918,62 +875,66 @@ async function mouseZoomOnImageAndAssert(page, factor = 2) {
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
async function buttonZoomOnImageAndAssert(page) {
|
||||
// Lock the zoom and pan so it doesn't reset if a new image comes in
|
||||
await page.getByLabel('Focused Image Element').hover({ trial: true });
|
||||
const lockButton = page.getByRole('button', {
|
||||
name: 'Lock current zoom and pan across all images'
|
||||
});
|
||||
if (!(await lockButton.isVisible())) {
|
||||
await test.step('Can zoom using buttons', async () => {
|
||||
// Lock the zoom and pan so it doesn't reset if a new image comes in
|
||||
await page.getByLabel('Focused Image Element').hover({ trial: true });
|
||||
}
|
||||
await lockButton.click();
|
||||
const lockButton = page.getByRole('button', {
|
||||
name: 'Lock current zoom and pan across all images'
|
||||
});
|
||||
|
||||
await expect(page.getByLabel('Focused Image Element')).toHaveJSProperty(
|
||||
'style.transform',
|
||||
'scale(1) translate(0px, 0px)'
|
||||
);
|
||||
await lockButton.isVisible();
|
||||
// if (!(await lockButton.isVisible())) {
|
||||
// await page.getByLabel('Focused Image Element').hover({ trial: true });
|
||||
// }
|
||||
await lockButton.click();
|
||||
|
||||
// Get initial image dimensions
|
||||
const initialBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||
await expect(page.getByLabel('Focused Image Element')).toHaveJSProperty(
|
||||
'style.transform',
|
||||
'scale(1) translate(0px, 0px)'
|
||||
);
|
||||
|
||||
// Zoom in twice via button
|
||||
await zoomIntoImageryByButton(page);
|
||||
await expect(page.getByLabel('Focused Image Element')).toHaveJSProperty(
|
||||
'style.transform',
|
||||
'scale(2) translate(0px, 0px)'
|
||||
);
|
||||
await zoomIntoImageryByButton(page);
|
||||
await expect(page.getByLabel('Focused Image Element')).toHaveJSProperty(
|
||||
'style.transform',
|
||||
'scale(3) translate(0px, 0px)'
|
||||
);
|
||||
// Get initial image dimensions
|
||||
const initialBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||
|
||||
// Get and assert zoomed in image dimensions
|
||||
const zoomedInBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||
expect(zoomedInBoundingBox.height).toBeGreaterThan(initialBoundingBox.height);
|
||||
expect(zoomedInBoundingBox.width).toBeGreaterThan(initialBoundingBox.width);
|
||||
// Zoom in twice via button
|
||||
await zoomIntoImageryByButton(page);
|
||||
await expect(page.getByLabel('Focused Image Element')).toHaveJSProperty(
|
||||
'style.transform',
|
||||
'scale(2) translate(0px, 0px)'
|
||||
);
|
||||
await zoomIntoImageryByButton(page);
|
||||
await expect(page.getByLabel('Focused Image Element')).toHaveJSProperty(
|
||||
'style.transform',
|
||||
'scale(3) translate(0px, 0px)'
|
||||
);
|
||||
|
||||
// Zoom out once via button
|
||||
await zoomOutOfImageryByButton(page);
|
||||
await expect(page.getByLabel('Focused Image Element')).toHaveJSProperty(
|
||||
'style.transform',
|
||||
'scale(2) translate(0px, 0px)'
|
||||
);
|
||||
// Get and assert zoomed in image dimensions
|
||||
const zoomedInBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||
expect(zoomedInBoundingBox.height).toBeGreaterThan(initialBoundingBox.height);
|
||||
expect(zoomedInBoundingBox.width).toBeGreaterThan(initialBoundingBox.width);
|
||||
|
||||
// Get and assert zoomed out image dimensions
|
||||
const zoomedOutBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||
expect(zoomedOutBoundingBox.height).toBeLessThan(zoomedInBoundingBox.height);
|
||||
expect(zoomedOutBoundingBox.width).toBeLessThan(zoomedInBoundingBox.width);
|
||||
// Zoom out once via button
|
||||
await zoomOutOfImageryByButton(page);
|
||||
await expect(page.getByLabel('Focused Image Element')).toHaveJSProperty(
|
||||
'style.transform',
|
||||
'scale(2) translate(0px, 0px)'
|
||||
);
|
||||
|
||||
// Zoom out again via button, assert against the initial image dimensions
|
||||
await zoomOutOfImageryByButton(page);
|
||||
await expect(page.getByLabel('Focused Image Element')).toHaveJSProperty(
|
||||
'style.transform',
|
||||
'scale(1) translate(0px, 0px)'
|
||||
);
|
||||
// Get and assert zoomed out image dimensions
|
||||
const zoomedOutBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||
expect(zoomedOutBoundingBox.height).toBeLessThan(zoomedInBoundingBox.height);
|
||||
expect(zoomedOutBoundingBox.width).toBeLessThan(zoomedInBoundingBox.width);
|
||||
|
||||
const finalBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||
expect(finalBoundingBox).toEqual(initialBoundingBox);
|
||||
// Zoom out again via button, assert against the initial image dimensions
|
||||
await zoomOutOfImageryByButton(page);
|
||||
await expect(page.getByLabel('Focused Image Element')).toHaveJSProperty(
|
||||
'style.transform',
|
||||
'scale(1) translate(0px, 0px)'
|
||||
);
|
||||
|
||||
const finalBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||
expect(finalBoundingBox).toEqual(initialBoundingBox);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1035,24 +996,6 @@ async function resetImageryPanAndZoom(page) {
|
||||
await expect(page.locator('.c-thumb__viewable-area')).toBeHidden();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
async function createImageryViewWithShortDelay(page, { name, parent }) {
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
name,
|
||||
type: 'Example Imagery',
|
||||
parent
|
||||
});
|
||||
|
||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery');
|
||||
await page.getByLabel('More actions').click();
|
||||
await page.getByLabel('Edit Properties').click();
|
||||
// Clear and set Image load delay to minimum value
|
||||
await page.locator('input[type="number"]').fill(`${IMAGE_LOAD_DELAY}`);
|
||||
await page.getByLabel('Save').click();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
|
@ -0,0 +1,489 @@
|
||||
/*****************************************************************************
|
||||
* 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 how imagery functions over time.
|
||||
It only assumes that example imagery is present.
|
||||
It uses https://playwright.dev/docs/clock to have control over time
|
||||
*/
|
||||
|
||||
import {
|
||||
createDomainObjectWithDefaults,
|
||||
navigateToObjectWithRealTime,
|
||||
setRealTimeMode,
|
||||
setStartOffset
|
||||
} from '../../../../appActions.js';
|
||||
import { MISSION_TIME } from '../../../../constants.js';
|
||||
import {
|
||||
createImageryViewWithShortDelay,
|
||||
FIVE_MINUTES,
|
||||
IMAGE_LOAD_DELAY,
|
||||
THIRTY_SECONDS
|
||||
} from '../../../../helper/imageryUtils.js';
|
||||
import { expect, test } from '../../../../pluginFixtures.js';
|
||||
|
||||
test.describe('Example Imagery Object with Controlled Clock @clock', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// We mock the clock so that we don't need to wait for time driven events
|
||||
// to verify functionality.
|
||||
await page.clock.install({ time: MISSION_TIME });
|
||||
await page.clock.resume();
|
||||
|
||||
//Go to baseURL
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
|
||||
// Create a default 'Example Imagery' object
|
||||
// Click the Create button
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
|
||||
// Click text=Example Imagery
|
||||
await page.getByRole('menuitem', { name: 'Example Imagery' }).click();
|
||||
|
||||
// Clear and set Image load delay to minimum value
|
||||
await page.locator('input[type="number"]').clear();
|
||||
await page.locator('input[type="number"]').fill(`${IMAGE_LOAD_DELAY}`);
|
||||
|
||||
await page.getByLabel('Save').click();
|
||||
|
||||
// Verify that the created object is focused
|
||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText(
|
||||
'Unnamed Example Imagery'
|
||||
);
|
||||
await page.getByLabel('Focused Image Element').hover({ trial: true });
|
||||
|
||||
// set realtime mode
|
||||
await setRealTimeMode(page);
|
||||
await setStartOffset(page, { startMins: '05' });
|
||||
});
|
||||
|
||||
test('Imagery Time Bounding', async ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/5265'
|
||||
});
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/7825'
|
||||
});
|
||||
|
||||
// verify that old images are discarded
|
||||
const lastImageInBounds = page.getByLabel('Image thumbnail from').first();
|
||||
const lastImageTimestamp = await lastImageInBounds.getAttribute('title');
|
||||
expect(lastImageTimestamp).not.toBeNull();
|
||||
|
||||
// go forward in time to ensure old images are discarded
|
||||
await page.clock.fastForward(IMAGE_LOAD_DELAY);
|
||||
await page.clock.resume();
|
||||
await expect(page.getByLabel(lastImageTimestamp)).toBeHidden();
|
||||
|
||||
// go way forward in time to ensure multiple images are discarded
|
||||
const IMAGES_TO_DISCARD_COUNT = 5;
|
||||
|
||||
const lastImageToDiscard = page
|
||||
.getByLabel('Image thumbnail from')
|
||||
.nth(IMAGES_TO_DISCARD_COUNT - 1);
|
||||
const lastImageToDiscardTimestamp = await lastImageToDiscard.getAttribute('title');
|
||||
expect(lastImageToDiscardTimestamp).not.toBeNull();
|
||||
|
||||
const imageAfterLastImageToDiscard = page
|
||||
.getByLabel('Image thumbnail from')
|
||||
.nth(IMAGES_TO_DISCARD_COUNT);
|
||||
const imageAfterLastImageToDiscardTimestamp =
|
||||
await imageAfterLastImageToDiscard.getAttribute('title');
|
||||
expect(imageAfterLastImageToDiscardTimestamp).not.toBeNull();
|
||||
|
||||
await page.clock.fastForward(IMAGE_LOAD_DELAY * IMAGES_TO_DISCARD_COUNT);
|
||||
await page.clock.resume();
|
||||
|
||||
await expect(page.getByLabel(lastImageToDiscardTimestamp)).toBeHidden();
|
||||
await expect(page.getByLabel(imageAfterLastImageToDiscardTimestamp)).toBeVisible();
|
||||
});
|
||||
|
||||
test('Get background-image url from background-image css prop', async ({ page }) => {
|
||||
await assertBackgroundImageUrlFromBackgroundCss(page);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Example Imagery in Display Layout with Controlled Clock @clock', () => {
|
||||
let displayLayout;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// We mock the clock so that we don't need to wait for time driven events
|
||||
// to verify functionality.
|
||||
await page.clock.install({ time: MISSION_TIME });
|
||||
await page.clock.resume();
|
||||
|
||||
// Go to baseURL
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
|
||||
displayLayout = await createDomainObjectWithDefaults(page, { type: 'Display Layout' });
|
||||
|
||||
// Create Example Imagery inside Display Layout
|
||||
await createImageryViewWithShortDelay(page, {
|
||||
name: 'Unnamed Example Imagery',
|
||||
parent: displayLayout.uuid
|
||||
});
|
||||
|
||||
// set realtime mode
|
||||
await navigateToObjectWithRealTime(
|
||||
page,
|
||||
displayLayout.url,
|
||||
`${FIVE_MINUTES}`,
|
||||
`${THIRTY_SECONDS}`
|
||||
);
|
||||
});
|
||||
|
||||
test('Imagery Time Bounding', async ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/5265'
|
||||
});
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/7825'
|
||||
});
|
||||
|
||||
// Edit mode
|
||||
await page.getByLabel('Edit Object').click();
|
||||
|
||||
// Click on example imagery to expose toolbar
|
||||
await page.locator('.c-so-view__header').click();
|
||||
|
||||
// Adjust object height
|
||||
await page.locator('div[title="Resize object height"] > input').click();
|
||||
await page.locator('div[title="Resize object height"] > input').fill('50');
|
||||
|
||||
// Adjust object width
|
||||
await page.locator('div[title="Resize object width"] > input').click();
|
||||
await page.locator('div[title="Resize object width"] > input').fill('50');
|
||||
|
||||
await expect(page.getByLabel('Image Thumbnail from').last()).toBeInViewport();
|
||||
|
||||
// verify that old images are discarded
|
||||
const lastImageInBounds = page.getByLabel('Image thumbnail from').first();
|
||||
const lastImageTimestamp = await lastImageInBounds.getAttribute('title');
|
||||
expect(lastImageTimestamp).not.toBeNull();
|
||||
|
||||
// go forward in time to ensure old images are discarded
|
||||
await page.clock.fastForward(IMAGE_LOAD_DELAY);
|
||||
await page.clock.resume();
|
||||
await expect(page.getByLabel(lastImageTimestamp)).toBeHidden();
|
||||
|
||||
// go way forward in time to ensure multiple images are discarded
|
||||
const IMAGES_TO_DISCARD_COUNT = 5;
|
||||
|
||||
const lastImageToDiscard = page
|
||||
.getByLabel('Image thumbnail from')
|
||||
.nth(IMAGES_TO_DISCARD_COUNT - 1);
|
||||
const lastImageToDiscardTimestamp = await lastImageToDiscard.getAttribute('title');
|
||||
expect(lastImageToDiscardTimestamp).not.toBeNull();
|
||||
|
||||
const imageAfterLastImageToDiscard = page
|
||||
.getByLabel('Image thumbnail from')
|
||||
.nth(IMAGES_TO_DISCARD_COUNT);
|
||||
const imageAfterLastImageToDiscardTimestamp =
|
||||
await imageAfterLastImageToDiscard.getAttribute('title');
|
||||
expect(imageAfterLastImageToDiscardTimestamp).not.toBeNull();
|
||||
|
||||
await page.clock.fastForward(IMAGE_LOAD_DELAY * IMAGES_TO_DISCARD_COUNT);
|
||||
await page.clock.resume();
|
||||
|
||||
await expect(page.getByLabel(lastImageToDiscardTimestamp)).toBeHidden();
|
||||
await expect(page.getByLabel(imageAfterLastImageToDiscardTimestamp)).toBeVisible();
|
||||
});
|
||||
|
||||
test('Get background-image url from background-image css prop @clock', async ({ page }) => {
|
||||
await assertBackgroundImageUrlFromBackgroundCss(page);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Example Imagery in Flexible layout with Controlled Clock @clock', () => {
|
||||
let flexibleLayout;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// We mock the clock so that we don't need to wait for time driven events
|
||||
// to verify functionality.
|
||||
await page.clock.install({ time: MISSION_TIME });
|
||||
await page.clock.resume();
|
||||
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
|
||||
flexibleLayout = await createDomainObjectWithDefaults(page, { type: 'Flexible Layout' });
|
||||
|
||||
// Create Example Imagery inside the Flexible Layout
|
||||
await createImageryViewWithShortDelay(page, {
|
||||
name: 'Unnamed Example Imagery',
|
||||
parent: flexibleLayout.uuid
|
||||
});
|
||||
|
||||
// set realtime mode
|
||||
await navigateToObjectWithRealTime(
|
||||
page,
|
||||
flexibleLayout.url,
|
||||
`${FIVE_MINUTES}`,
|
||||
`${THIRTY_SECONDS}`
|
||||
);
|
||||
|
||||
// Wait for image thumbnail auto-scroll to complete
|
||||
await expect(page.getByLabel('Image Thumbnail from').last()).toBeInViewport();
|
||||
});
|
||||
|
||||
test('Imagery Time Bounding @clock', async ({ page, browserName }) => {
|
||||
test.fixme(browserName === 'firefox', 'This test needs to be updated to work with firefox');
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/5326'
|
||||
});
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/7825'
|
||||
});
|
||||
|
||||
// verify that old images are discarded
|
||||
const lastImageInBounds = page.getByLabel('Image thumbnail from').first();
|
||||
const lastImageTimestamp = await lastImageInBounds.getAttribute('title');
|
||||
expect(lastImageTimestamp).not.toBeNull();
|
||||
|
||||
// go forward in time to ensure old images are discarded
|
||||
await page.clock.fastForward(IMAGE_LOAD_DELAY);
|
||||
await page.clock.resume();
|
||||
await expect(page.getByLabel(lastImageTimestamp)).toBeHidden();
|
||||
|
||||
// go way forward in time to ensure multiple images are discarded
|
||||
const IMAGES_TO_DISCARD_COUNT = 5;
|
||||
|
||||
const lastImageToDiscard = page
|
||||
.getByLabel('Image thumbnail from')
|
||||
.nth(IMAGES_TO_DISCARD_COUNT - 1);
|
||||
const lastImageToDiscardTimestamp = await lastImageToDiscard.getAttribute('title');
|
||||
expect(lastImageToDiscardTimestamp).not.toBeNull();
|
||||
|
||||
const imageAfterLastImageToDiscard = page
|
||||
.getByLabel('Image thumbnail from')
|
||||
.nth(IMAGES_TO_DISCARD_COUNT);
|
||||
const imageAfterLastImageToDiscardTimestamp =
|
||||
await imageAfterLastImageToDiscard.getAttribute('title');
|
||||
expect(imageAfterLastImageToDiscardTimestamp).not.toBeNull();
|
||||
|
||||
await page.clock.fastForward(IMAGE_LOAD_DELAY * IMAGES_TO_DISCARD_COUNT);
|
||||
await page.clock.resume();
|
||||
|
||||
await expect(page.getByLabel(lastImageToDiscardTimestamp)).toBeHidden();
|
||||
await expect(page.getByLabel(imageAfterLastImageToDiscardTimestamp)).toBeVisible();
|
||||
});
|
||||
|
||||
test('Get background-image url from background-image css prop @clock', async ({ page }) => {
|
||||
await assertBackgroundImageUrlFromBackgroundCss(page);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Example Imagery in Tabs View with Controlled Clock @clock', () => {
|
||||
let timeStripObject;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// We mock the clock so that we don't need to wait for time driven events
|
||||
// to verify functionality.
|
||||
await page.clock.install({ time: MISSION_TIME });
|
||||
await page.clock.resume();
|
||||
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
|
||||
timeStripObject = await createDomainObjectWithDefaults(page, { type: 'Tabs View' });
|
||||
await page.goto(timeStripObject.url);
|
||||
|
||||
/* Create Example Imagery with minimum Image Load Delay */
|
||||
// Click the Create button
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
|
||||
// Click text=Example Imagery
|
||||
await page.getByRole('menuitem', { name: 'Example Imagery' }).click();
|
||||
|
||||
// Clear and set Image load delay to minimum value
|
||||
await page.locator('input[type="number"]').clear();
|
||||
await page.locator('input[type="number"]').fill(`${IMAGE_LOAD_DELAY}`);
|
||||
|
||||
await page.getByLabel('Save').click();
|
||||
|
||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText(
|
||||
'Unnamed Example Imagery'
|
||||
);
|
||||
|
||||
// set realtime mode
|
||||
await navigateToObjectWithRealTime(
|
||||
page,
|
||||
timeStripObject.url,
|
||||
`${FIVE_MINUTES}`,
|
||||
`${THIRTY_SECONDS}`
|
||||
);
|
||||
|
||||
// Wait for image thumbnail auto-scroll to complete
|
||||
await expect(page.getByLabel('Image Thumbnail from').last()).toBeInViewport();
|
||||
});
|
||||
|
||||
test('Imagery Time Bounding @clock', async ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/5265'
|
||||
});
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/7825'
|
||||
});
|
||||
|
||||
// verify that old images are discarded
|
||||
const lastImageInBounds = page.getByLabel('Image thumbnail from').first();
|
||||
const lastImageTimestamp = await lastImageInBounds.getAttribute('title');
|
||||
expect(lastImageTimestamp).not.toBeNull();
|
||||
|
||||
// go forward in time to ensure old images are discarded
|
||||
await page.clock.fastForward(IMAGE_LOAD_DELAY);
|
||||
await page.clock.resume();
|
||||
await expect(page.getByLabel(lastImageTimestamp)).toBeHidden();
|
||||
|
||||
// go way forward in time to ensure multiple images are discarded
|
||||
const IMAGES_TO_DISCARD_COUNT = 5;
|
||||
|
||||
const lastImageToDiscard = page
|
||||
.getByLabel('Image thumbnail from')
|
||||
.nth(IMAGES_TO_DISCARD_COUNT - 1);
|
||||
const lastImageToDiscardTimestamp = await lastImageToDiscard.getAttribute('title');
|
||||
expect(lastImageToDiscardTimestamp).not.toBeNull();
|
||||
|
||||
const imageAfterLastImageToDiscard = page
|
||||
.getByLabel('Image thumbnail from')
|
||||
.nth(IMAGES_TO_DISCARD_COUNT);
|
||||
const imageAfterLastImageToDiscardTimestamp =
|
||||
await imageAfterLastImageToDiscard.getAttribute('title');
|
||||
expect(imageAfterLastImageToDiscardTimestamp).not.toBeNull();
|
||||
|
||||
await page.clock.fastForward(IMAGE_LOAD_DELAY * IMAGES_TO_DISCARD_COUNT);
|
||||
await page.clock.resume();
|
||||
|
||||
await expect(page.getByLabel(lastImageToDiscardTimestamp)).toBeHidden();
|
||||
await expect(page.getByLabel(imageAfterLastImageToDiscardTimestamp)).toBeVisible();
|
||||
});
|
||||
|
||||
test('Get background-image url from background-image css prop @clock', async ({ page }) => {
|
||||
await assertBackgroundImageUrlFromBackgroundCss(page);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Example Imagery in Time Strip with Controlled Clock @clock', () => {
|
||||
let timeStripObject;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// We mock the clock so that we don't need to wait for time driven events
|
||||
// to verify functionality.
|
||||
await page.clock.install({ time: MISSION_TIME });
|
||||
await page.clock.resume();
|
||||
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
|
||||
timeStripObject = await createDomainObjectWithDefaults(page, { type: 'Time Strip' });
|
||||
await page.goto(timeStripObject.url);
|
||||
|
||||
/* Create Example Imagery with minimum Image Load Delay */
|
||||
// Click the Create button
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
|
||||
// Click text=Example Imagery
|
||||
await page.getByRole('menuitem', { name: 'Example Imagery' }).click();
|
||||
|
||||
// Clear and set Image load delay to minimum value
|
||||
await page.locator('input[type="number"]').clear();
|
||||
await page.locator('input[type="number"]').fill(`${IMAGE_LOAD_DELAY}`);
|
||||
|
||||
await page.getByLabel('Save').click();
|
||||
|
||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText(
|
||||
'Unnamed Example Imagery'
|
||||
);
|
||||
|
||||
// set realtime mode
|
||||
await navigateToObjectWithRealTime(
|
||||
page,
|
||||
timeStripObject.url,
|
||||
`${FIVE_MINUTES}`,
|
||||
`${THIRTY_SECONDS}`
|
||||
);
|
||||
|
||||
// Wait for image thumbnail auto-scroll to complete
|
||||
await expect(page.getByLabel('wrapper-').last()).toBeInViewport();
|
||||
});
|
||||
|
||||
test('Imagery Time Bounding @clock', async ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/5265'
|
||||
});
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/7825'
|
||||
});
|
||||
|
||||
// verify that old images are discarded
|
||||
const lastImageInBounds = page.getByLabel('wrapper-').first();
|
||||
const lastImageTimestamp = await lastImageInBounds.getAttribute('aria-label');
|
||||
expect(lastImageTimestamp).not.toBeNull();
|
||||
|
||||
// go forward in time to ensure old images are discarded
|
||||
await page.clock.fastForward(IMAGE_LOAD_DELAY);
|
||||
await page.clock.resume();
|
||||
await expect(page.getByLabel(lastImageTimestamp)).toBeHidden();
|
||||
|
||||
// go way forward in time to ensure multiple images are discarded
|
||||
const IMAGES_TO_DISCARD_COUNT = 5;
|
||||
|
||||
const lastImageToDiscard = page.getByLabel('wrapper-').nth(IMAGES_TO_DISCARD_COUNT - 1);
|
||||
const lastImageToDiscardTimestamp = await lastImageToDiscard.getAttribute('aria-label');
|
||||
expect(lastImageToDiscardTimestamp).not.toBeNull();
|
||||
|
||||
const imageAfterLastImageToDiscard = page.getByLabel('wrapper-').nth(IMAGES_TO_DISCARD_COUNT);
|
||||
const imageAfterLastImageToDiscardTimestamp =
|
||||
await imageAfterLastImageToDiscard.getAttribute('aria-label');
|
||||
expect(imageAfterLastImageToDiscardTimestamp).not.toBeNull();
|
||||
|
||||
await page.clock.fastForward(IMAGE_LOAD_DELAY * IMAGES_TO_DISCARD_COUNT);
|
||||
await page.clock.resume();
|
||||
|
||||
await expect(page.getByLabel(lastImageToDiscardTimestamp)).toBeHidden();
|
||||
await expect(page.getByLabel(imageAfterLastImageToDiscardTimestamp)).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
async function assertBackgroundImageUrlFromBackgroundCss(page) {
|
||||
const backgroundImage = page.getByLabel('Focused Image Element');
|
||||
const backgroundImageUrl = await backgroundImage.evaluate((el) => {
|
||||
return window
|
||||
.getComputedStyle(el)
|
||||
.getPropertyValue('background-image')
|
||||
.match(/url\(([^)]+)\)/)[1];
|
||||
});
|
||||
|
||||
// go forward in time to ensure old images are discarded
|
||||
await page.clock.fastForward(IMAGE_LOAD_DELAY);
|
||||
await page.clock.resume();
|
||||
await expect(backgroundImage).not.toHaveJSProperty('background-image', backgroundImageUrl);
|
||||
}
|
@ -116,8 +116,7 @@ export default class TelemetryCollection extends EventEmitter {
|
||||
}
|
||||
|
||||
/**
|
||||
* This will start the requests for historical and realtime data,
|
||||
* as well as setting up initial values and watchers
|
||||
* @returns {Array} All bounded telemetry
|
||||
*/
|
||||
getAll() {
|
||||
return this.boundedTelemetry;
|
||||
|
@ -370,6 +370,7 @@ export default {
|
||||
createImageWrapper(index, image, showImagePlaceholders) {
|
||||
const id = `${ID_PREFIX}${image.time}`;
|
||||
let imageWrapper = document.createElement('div');
|
||||
imageWrapper.ariaLabel = id;
|
||||
imageWrapper.classList.add(IMAGE_WRAPPER_CLASS);
|
||||
imageWrapper.style.left = `${this.xScale(image.time)}px`;
|
||||
this.setNSAttributesForElement(imageWrapper, {
|
||||
|
@ -620,7 +620,7 @@ export default {
|
||||
if (matchIndex > -1) {
|
||||
this.setFocusedImage(matchIndex);
|
||||
} else {
|
||||
this.paused();
|
||||
this.paused(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1082,7 +1082,7 @@ export default {
|
||||
paused(state) {
|
||||
this.isPaused = Boolean(state);
|
||||
|
||||
if (!state) {
|
||||
if (!this.isPaused) {
|
||||
this.previousFocusedImage = null;
|
||||
this.setFocusedImage(this.nextImageIndex);
|
||||
this.autoScroll = true;
|
||||
|
@ -90,16 +90,17 @@ export default {
|
||||
dataCleared() {
|
||||
this.imageHistory = [];
|
||||
},
|
||||
dataRemoved(dataToRemove) {
|
||||
this.imageHistory = this.imageHistory.filter((existingDatum) => {
|
||||
const shouldKeep = dataToRemove.some((datumToRemove) => {
|
||||
const existingDatumTimestamp = this.parseTime(existingDatum);
|
||||
const datumToRemoveTimestamp = this.parseTime(datumToRemove);
|
||||
dataRemoved(removed) {
|
||||
const removedTimestamps = {};
|
||||
removed.forEach((_removed) => {
|
||||
const removedTimestamp = this.parseTime(_removed);
|
||||
removedTimestamps[removedTimestamp] = true;
|
||||
});
|
||||
|
||||
return existingDatumTimestamp !== datumToRemoveTimestamp;
|
||||
});
|
||||
this.imageHistory = this.imageHistory.filter((image) => {
|
||||
const imageTimestamp = this.parseTime(image);
|
||||
|
||||
return shouldKeep;
|
||||
return !removedTimestamps[imageTimestamp];
|
||||
});
|
||||
},
|
||||
setDataTimeContext() {
|
||||
|
@ -99,6 +99,7 @@ export default {
|
||||
this.composition.off('remove', this.removeItem);
|
||||
this.composition.off('reorder', this.reorder);
|
||||
this.stopFollowingTimeContext();
|
||||
this.handleContentResize.cancel();
|
||||
this.contentResizeObserver.disconnect();
|
||||
},
|
||||
mounted() {
|
||||
|
Loading…
Reference in New Issue
Block a user