mirror of
https://github.com/nasa/openmct.git
synced 2025-06-20 16:10:23 +00:00
fix: DisplayLayout and FlexibleLayout toolbar actions only apply to selected layout (#7184)
* refactor: convert to ES6 function * fix: include `keyString` in event name - This negates the need for complicated logic in determining which objectView the action was intended for * fix: handle the case of currentView being null * fix: add keyString to flexibleLayout toolbar events * fix: properly unregister listeners * fix: remove unused imports * fix: revert parameter reorder * refactor: replace usage of `arguments` with `...args` * fix: add a11y to display layout + toolbar * test: add first cut of layout toolbar suite * test: cleanup a bit and add Image test * test: add stubs * fix: remove unused variable * refactor(DisplayLayoutToolbar): convert to ES6 class * test: generate localStorage data for display layout tests * fix: clarify "Add" button label * test: cleanup and don't parameterize tests * test: fix path for recycled_local_storage.json * fix: path to local storage file * docs: add documentation for utilizing localStorage in e2e * fix: path to recycled_local_storage.json * docs: add note hyperlink
This commit is contained in:
@ -19,8 +19,9 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/* global __dirname */
|
||||
const { test, expect } = require('../../../../pluginFixtures');
|
||||
const path = require('path');
|
||||
const {
|
||||
createDomainObjectWithDefaults,
|
||||
setStartOffset,
|
||||
@ -29,6 +30,88 @@ const {
|
||||
setIndependentTimeConductorBounds
|
||||
} = require('../../../../appActions');
|
||||
|
||||
const LOCALSTORAGE_PATH = path.resolve(
|
||||
__dirname,
|
||||
'../../../../test-data/display_layout_with_child_layouts.json'
|
||||
);
|
||||
const TINY_IMAGE_BASE64 =
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII';
|
||||
|
||||
test.describe('Display Layout Toolbar Actions', () => {
|
||||
const PARENT_DISPLAY_LAYOUT_NAME = 'Parent Display Layout';
|
||||
const CHILD_DISPLAY_LAYOUT_NAME1 = 'Child Layout 1';
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
await setRealTimeMode(page);
|
||||
await page
|
||||
.locator('a')
|
||||
.filter({ hasText: 'Parent Display Layout Display Layout' })
|
||||
.first()
|
||||
.click();
|
||||
await page.getByLabel('Edit').click();
|
||||
});
|
||||
test.use({
|
||||
storageState: path.resolve(__dirname, LOCALSTORAGE_PATH)
|
||||
});
|
||||
|
||||
test('can add/remove Text element to a single layout', async ({ page }) => {
|
||||
const layoutObject = 'Text';
|
||||
await test.step(`Add and remove ${layoutObject} from the parent's layout`, async () => {
|
||||
await addAndRemoveDrawingObjectAndAssert(page, layoutObject, PARENT_DISPLAY_LAYOUT_NAME);
|
||||
});
|
||||
await test.step(`Add and remove ${layoutObject} from the child's layout`, async () => {
|
||||
await addAndRemoveDrawingObjectAndAssert(page, layoutObject, CHILD_DISPLAY_LAYOUT_NAME1);
|
||||
});
|
||||
});
|
||||
test('can add/remove Image to a single layout', async ({ page }) => {
|
||||
const layoutObject = 'Image';
|
||||
await test.step("Add and remove image element from the parent's layout", async () => {
|
||||
expect(await page.getByLabel(`Move ${layoutObject} Frame`).count()).toBe(0);
|
||||
await addLayoutObject(page, PARENT_DISPLAY_LAYOUT_NAME, layoutObject);
|
||||
expect(await page.getByLabel(`Move ${layoutObject} Frame`).count()).toBe(1);
|
||||
await removeLayoutObject(page, layoutObject);
|
||||
expect(await page.getByLabel(`Move ${layoutObject} Frame`).count()).toBe(0);
|
||||
});
|
||||
await test.step("Add and remove image from the child's layout", async () => {
|
||||
await addLayoutObject(page, CHILD_DISPLAY_LAYOUT_NAME1, layoutObject);
|
||||
expect(await page.getByLabel(`Move ${layoutObject} Frame`).count()).toBe(1);
|
||||
await removeLayoutObject(page, layoutObject);
|
||||
expect(await page.getByLabel(`Move ${layoutObject} Frame`).count()).toBe(0);
|
||||
});
|
||||
});
|
||||
test(`can add/remove Box to a single layout`, async ({ page }) => {
|
||||
const layoutObject = 'Box';
|
||||
await test.step(`Add and remove ${layoutObject} from the parent's layout`, async () => {
|
||||
await addAndRemoveDrawingObjectAndAssert(page, layoutObject, PARENT_DISPLAY_LAYOUT_NAME);
|
||||
});
|
||||
await test.step(`Add and remove ${layoutObject} from the child's layout`, async () => {
|
||||
await addAndRemoveDrawingObjectAndAssert(page, layoutObject, CHILD_DISPLAY_LAYOUT_NAME1);
|
||||
});
|
||||
});
|
||||
test(`can add/remove Line to a single layout`, async ({ page }) => {
|
||||
const layoutObject = 'Line';
|
||||
await test.step(`Add and remove ${layoutObject} from the parent's layout`, async () => {
|
||||
await addAndRemoveDrawingObjectAndAssert(page, layoutObject, PARENT_DISPLAY_LAYOUT_NAME);
|
||||
});
|
||||
await test.step(`Add and remove ${layoutObject} from the child's layout`, async () => {
|
||||
await addAndRemoveDrawingObjectAndAssert(page, layoutObject, CHILD_DISPLAY_LAYOUT_NAME1);
|
||||
});
|
||||
});
|
||||
test(`can add/remove Ellipse to a single layout`, async ({ page }) => {
|
||||
const layoutObject = 'Ellipse';
|
||||
await test.step(`Add and remove ${layoutObject} from the parent's layout`, async () => {
|
||||
await addAndRemoveDrawingObjectAndAssert(page, layoutObject, PARENT_DISPLAY_LAYOUT_NAME);
|
||||
});
|
||||
await test.step(`Add and remove ${layoutObject} from the child's layout`, async () => {
|
||||
await addAndRemoveDrawingObjectAndAssert(page, layoutObject, CHILD_DISPLAY_LAYOUT_NAME1);
|
||||
});
|
||||
});
|
||||
test.fixme('Can switch view types of a single SWG in a layout', async ({ page }) => {});
|
||||
test.fixme('Can merge multiple plots in a layout', async ({ page }) => {});
|
||||
test.fixme('Can adjust stack order of a single object in a layout', async ({ page }) => {});
|
||||
test.fixme('Can duplicate a single object in a layout', async ({ page }) => {});
|
||||
});
|
||||
|
||||
test.describe('Display Layout', () => {
|
||||
/** @type {import('../../../../appActions').CreatedObjectInfo} */
|
||||
let sineWaveObject;
|
||||
@ -41,6 +124,7 @@ test.describe('Display Layout', () => {
|
||||
type: 'Sine Wave Generator'
|
||||
});
|
||||
});
|
||||
|
||||
test('alpha-numeric widget telemetry value exactly matches latest telemetry value received in real time', async ({
|
||||
page
|
||||
}) => {
|
||||
@ -339,6 +423,59 @@ test.describe('Display Layout', () => {
|
||||
});
|
||||
});
|
||||
|
||||
async function addAndRemoveDrawingObjectAndAssert(page, layoutObject, DISPLAY_LAYOUT_NAME) {
|
||||
expect(await page.getByLabel(layoutObject, { exact: true }).count()).toBe(0);
|
||||
await addLayoutObject(page, DISPLAY_LAYOUT_NAME, layoutObject);
|
||||
expect(
|
||||
await page
|
||||
.getByLabel(layoutObject, {
|
||||
exact: true
|
||||
})
|
||||
.count()
|
||||
).toBe(1);
|
||||
await removeLayoutObject(page, layoutObject);
|
||||
expect(await page.getByLabel(layoutObject, { exact: true }).count()).toBe(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the first matching layout object from the layout
|
||||
* @param {import('@playwright/test').Page} page
|
||||
* @param {'Box' | 'Ellipse' | 'Line' | 'Text' | 'Image'} layoutObject
|
||||
*/
|
||||
async function removeLayoutObject(page, layoutObject) {
|
||||
await page
|
||||
.getByLabel(`Move ${layoutObject} Frame`, { exact: true })
|
||||
.or(page.getByLabel(layoutObject, { exact: true }))
|
||||
.first()
|
||||
// eslint-disable-next-line playwright/no-force-option
|
||||
.click({ force: true });
|
||||
await page.getByTitle('Delete the selected object').click();
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a layout object to the specified layout
|
||||
* @param {import('@playwright/test').Page} page
|
||||
* @param {string} layoutName
|
||||
* @param {'Box' | 'Ellipse' | 'Line' | 'Text' | 'Image'} layoutObject
|
||||
*/
|
||||
async function addLayoutObject(page, layoutName, layoutObject) {
|
||||
await page.getByLabel(`${layoutName} Layout Grid`).click();
|
||||
await page.getByText('Add Drawing Object').click();
|
||||
await page
|
||||
.getByRole('menuitem', {
|
||||
name: layoutObject
|
||||
})
|
||||
.click();
|
||||
if (layoutObject === 'Text') {
|
||||
await page.getByRole('textbox', { name: 'Text' }).fill('Hello, Universe!');
|
||||
await page.getByText('OK').click();
|
||||
} else if (layoutObject === 'Image') {
|
||||
await page.getByLabel('Image URL').fill(TINY_IMAGE_BASE64);
|
||||
await page.getByText('OK').click();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Util for subscribing to a telemetry object by object identifier
|
||||
* Limitations: Currently only works to return telemetry once to the node scope
|
||||
|
Reference in New Issue
Block a user