mirror of
https://github.com/nasa/openmct.git
synced 2025-01-30 08:04:03 +00:00
Use Composition API to add/remove from composition (#5941)
* Use composition API in RemoveAction * refactor: ScatterPlotView to use composition API * fix: initialize transaction to null and reset * fix: remove seriesKey and correct found condition * refactor: Gauge to use composition API * refactor: DisplayLayout to use composition API * test: RemoveAction starts and ends transactions * test: add ScatterPlot add/remove telemetry test * test: fix e2e test and add annotation * test: remove unnecessary awaits * test: make some displayLayout tests stable * test{displayLayout}: navigate to objects via url * test(gauge): add test for add/remove telemetry * fix(#3117): init layoutItems within transaction * refactor: add clearSelection() method * test: remove unstable tag * fix(#3117): init frames and use transactions - fixes #3117 for flexible layouts by syncing frames and composition - also uses transactions now to avoid race condition * test(flexibleLayout): removing items via context menu - add test for removing items via context menu while focusing the layout - add test for removing items via context menu while not focusing the layout * fix(e2e): use pluginFixtures * refactor(e2e): improve selectors * refactor: use async/await for saving transactions * docs(e2e): fix comments * test: use soft expects Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
This commit is contained in:
parent
8af3b4309f
commit
7bb4a136d7
@ -23,7 +23,7 @@
|
|||||||
const { test, expect } = require('../../../../pluginFixtures');
|
const { test, expect } = require('../../../../pluginFixtures');
|
||||||
const { createDomainObjectWithDefaults, setStartOffset, setFixedTimeMode, setRealTimeMode } = require('../../../../appActions');
|
const { createDomainObjectWithDefaults, setStartOffset, setFixedTimeMode, setRealTimeMode } = require('../../../../appActions');
|
||||||
|
|
||||||
test.describe('Testing Display Layout @unstable', () => {
|
test.describe('Display Layout', () => {
|
||||||
let sineWaveObject;
|
let sineWaveObject;
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await page.goto('./', { waitUntil: 'networkidle' });
|
await page.goto('./', { waitUntil: 'networkidle' });
|
||||||
@ -55,12 +55,12 @@ test.describe('Testing Display Layout @unstable', () => {
|
|||||||
// On getting data, check if the value found in the Display Layout is the most recent value
|
// On getting data, check if the value found in the Display Layout is the most recent value
|
||||||
// from the Sine Wave Generator
|
// from the Sine Wave Generator
|
||||||
const getTelemValuePromise = await subscribeToTelemetry(page, sineWaveObject.uuid);
|
const getTelemValuePromise = await subscribeToTelemetry(page, sineWaveObject.uuid);
|
||||||
const formattedTelemetryValue = await getTelemValuePromise;
|
const formattedTelemetryValue = getTelemValuePromise;
|
||||||
const displayLayoutValuePromise = await page.waitForSelector(`text="${formattedTelemetryValue}"`);
|
const displayLayoutValuePromise = await page.waitForSelector(`text="${formattedTelemetryValue}"`);
|
||||||
const displayLayoutValue = await displayLayoutValuePromise.textContent();
|
const displayLayoutValue = await displayLayoutValuePromise.textContent();
|
||||||
const trimmedDisplayValue = displayLayoutValue.trim();
|
const trimmedDisplayValue = displayLayoutValue.trim();
|
||||||
|
|
||||||
await expect(trimmedDisplayValue).toBe(formattedTelemetryValue);
|
expect(trimmedDisplayValue).toBe(formattedTelemetryValue);
|
||||||
});
|
});
|
||||||
test('alpha-numeric widget telemetry value exactly matches latest telemetry value received in fixed time', async ({ page }) => {
|
test('alpha-numeric widget telemetry value exactly matches latest telemetry value received in fixed time', async ({ page }) => {
|
||||||
// Create a Display Layout
|
// Create a Display Layout
|
||||||
@ -86,12 +86,12 @@ test.describe('Testing Display Layout @unstable', () => {
|
|||||||
|
|
||||||
// On getting data, check if the value found in the Display Layout is the most recent value
|
// On getting data, check if the value found in the Display Layout is the most recent value
|
||||||
// from the Sine Wave Generator
|
// from the Sine Wave Generator
|
||||||
const formattedTelemetryValue = await getTelemValuePromise;
|
const formattedTelemetryValue = getTelemValuePromise;
|
||||||
const displayLayoutValuePromise = await page.waitForSelector(`text="${formattedTelemetryValue}"`);
|
const displayLayoutValuePromise = await page.waitForSelector(`text="${formattedTelemetryValue}"`);
|
||||||
const displayLayoutValue = await displayLayoutValuePromise.textContent();
|
const displayLayoutValue = await displayLayoutValuePromise.textContent();
|
||||||
const trimmedDisplayValue = displayLayoutValue.trim();
|
const trimmedDisplayValue = displayLayoutValue.trim();
|
||||||
|
|
||||||
await expect(trimmedDisplayValue).toBe(formattedTelemetryValue);
|
expect(trimmedDisplayValue).toBe(formattedTelemetryValue);
|
||||||
});
|
});
|
||||||
test('items in a display layout can be removed with object tree context menu when viewing the display layout', async ({ page }) => {
|
test('items in a display layout can be removed with object tree context menu when viewing the display layout', async ({ page }) => {
|
||||||
// Create a Display Layout
|
// Create a Display Layout
|
||||||
@ -121,11 +121,15 @@ test.describe('Testing Display Layout @unstable', () => {
|
|||||||
|
|
||||||
// delete
|
// delete
|
||||||
|
|
||||||
expect.soft(await page.locator('.l-layout .l-layout__frame').count()).toEqual(0);
|
expect(await page.locator('.l-layout .l-layout__frame').count()).toEqual(0);
|
||||||
});
|
});
|
||||||
test('items in a display layout can be removed with object tree context menu when viewing another item', async ({ page }) => {
|
test('items in a display layout can be removed with object tree context menu when viewing another item', async ({ page }) => {
|
||||||
|
test.info().annotations.push({
|
||||||
|
type: 'issue',
|
||||||
|
description: 'https://github.com/nasa/openmct/issues/3117'
|
||||||
|
});
|
||||||
// Create a Display Layout
|
// Create a Display Layout
|
||||||
await createDomainObjectWithDefaults(page, {
|
const displayLayout = await createDomainObjectWithDefaults(page, {
|
||||||
type: 'Display Layout',
|
type: 'Display Layout',
|
||||||
name: "Test Display Layout"
|
name: "Test Display Layout"
|
||||||
});
|
});
|
||||||
@ -144,18 +148,18 @@ test.describe('Testing Display Layout @unstable', () => {
|
|||||||
// Expand the Display Layout so we can remove the sine wave generator
|
// Expand the Display Layout so we can remove the sine wave generator
|
||||||
await page.locator('.c-tree__item.is-navigated-object .c-disclosure-triangle').click();
|
await page.locator('.c-tree__item.is-navigated-object .c-disclosure-triangle').click();
|
||||||
|
|
||||||
// Click the original Sine Wave Generator to navigate away from the Display Layout
|
// Go to the original Sine Wave Generator to navigate away from the Display Layout
|
||||||
await page.locator('.c-tree__item .c-tree__item__name:text("Test Sine Wave Generator")').click();
|
await page.goto(sineWaveObject.url);
|
||||||
|
|
||||||
// Bring up context menu and remove
|
// Bring up context menu and remove
|
||||||
await page.locator('.c-tree__item.is-alias .c-tree__item__name:text("Test Sine Wave Generator")').click({ button: 'right' });
|
await page.locator('.c-tree__item.is-alias .c-tree__item__name:text("Test Sine Wave Generator")').click({ button: 'right' });
|
||||||
await page.locator('text=Remove').click();
|
await page.locator('li[role="menuitem"]:has-text("Remove")').click();
|
||||||
await page.locator('button:has-text("OK")').click();
|
await page.locator('button:has-text("OK")').click();
|
||||||
|
|
||||||
// navigate back to the display layout to confirm it has been removed
|
// navigate back to the display layout to confirm it has been removed
|
||||||
await page.locator('.c-tree__item .c-tree__item__name:text("Test Display Layout")').click();
|
await page.goto(displayLayout.url);
|
||||||
|
|
||||||
expect.soft(await page.locator('.l-layout .l-layout__frame').count()).toEqual(0);
|
expect(await page.locator('.l-layout .l-layout__frame').count()).toEqual(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -23,12 +23,13 @@
|
|||||||
const { test, expect } = require('../../../../pluginFixtures');
|
const { test, expect } = require('../../../../pluginFixtures');
|
||||||
const { createDomainObjectWithDefaults } = require('../../../../appActions');
|
const { createDomainObjectWithDefaults } = require('../../../../appActions');
|
||||||
|
|
||||||
test.describe('Testing Flexible Layout @unstable', () => {
|
test.describe('Flexible Layout', () => {
|
||||||
|
let sineWaveObject;
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await page.goto('./', { waitUntil: 'networkidle' });
|
await page.goto('./', { waitUntil: 'networkidle' });
|
||||||
|
|
||||||
// Create Sine Wave Generator
|
// Create Sine Wave Generator
|
||||||
await createDomainObjectWithDefaults(page, {
|
sineWaveObject = await createDomainObjectWithDefaults(page, {
|
||||||
type: 'Sine Wave Generator',
|
type: 'Sine Wave Generator',
|
||||||
name: "Test Sine Wave Generator"
|
name: "Test Sine Wave Generator"
|
||||||
});
|
});
|
||||||
@ -54,13 +55,81 @@ test.describe('Testing Flexible Layout @unstable', () => {
|
|||||||
await page.dragAndDrop('text=Test Sine Wave Generator', '.c-fl__container.is-empty');
|
await page.dragAndDrop('text=Test Sine Wave Generator', '.c-fl__container.is-empty');
|
||||||
await page.dragAndDrop('text=Test Clock', '.c-fl__container.is-empty');
|
await page.dragAndDrop('text=Test Clock', '.c-fl__container.is-empty');
|
||||||
// Check that panes can be dragged while Flexible Layout is in Edit mode
|
// Check that panes can be dragged while Flexible Layout is in Edit mode
|
||||||
let dragWrapper = await page.locator('.c-fl-container__frames-holder .c-fl-frame__drag-wrapper').first();
|
let dragWrapper = page.locator('.c-fl-container__frames-holder .c-fl-frame__drag-wrapper').first();
|
||||||
await expect(dragWrapper).toHaveAttribute('draggable', 'true');
|
await expect(dragWrapper).toHaveAttribute('draggable', 'true');
|
||||||
// Save Flexible Layout
|
// Save Flexible Layout
|
||||||
await page.locator('button[title="Save"]').click();
|
await page.locator('button[title="Save"]').click();
|
||||||
await page.locator('text=Save and Finish Editing').click();
|
await page.locator('text=Save and Finish Editing').click();
|
||||||
// Check that panes are not draggable while Flexible Layout is in Browse mode
|
// Check that panes are not draggable while Flexible Layout is in Browse mode
|
||||||
dragWrapper = await page.locator('.c-fl-container__frames-holder .c-fl-frame__drag-wrapper').first();
|
dragWrapper = page.locator('.c-fl-container__frames-holder .c-fl-frame__drag-wrapper').first();
|
||||||
await expect(dragWrapper).toHaveAttribute('draggable', 'false');
|
await expect(dragWrapper).toHaveAttribute('draggable', 'false');
|
||||||
});
|
});
|
||||||
|
test('items in a flexible layout can be removed with object tree context menu when viewing the flexible layout', async ({ page }) => {
|
||||||
|
// Create a Display Layout
|
||||||
|
await createDomainObjectWithDefaults(page, {
|
||||||
|
type: 'Flexible Layout',
|
||||||
|
name: "Test Flexible Layout"
|
||||||
|
});
|
||||||
|
// Edit Flexible Layout
|
||||||
|
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').first().click();
|
||||||
|
// Add the Sine Wave Generator to the Flexible Layout and save changes
|
||||||
|
await page.dragAndDrop('text=Test Sine Wave Generator', '.c-fl__container.is-empty');
|
||||||
|
await page.locator('button[title="Save"]').click();
|
||||||
|
await page.locator('text=Save and Finish Editing').click();
|
||||||
|
|
||||||
|
expect.soft(await page.locator('.c-fl-container__frame').count()).toEqual(1);
|
||||||
|
|
||||||
|
// Expand the Flexible Layout so we can remove the sine wave generator
|
||||||
|
await page.locator('.c-tree__item.is-navigated-object .c-disclosure-triangle').click();
|
||||||
|
|
||||||
|
// Bring up context menu and remove
|
||||||
|
await page.locator('.c-tree__item.is-alias .c-tree__item__name:text("Test Sine Wave Generator")').first().click({ button: 'right' });
|
||||||
|
await page.locator('li[role="menuitem"]:has-text("Remove")').click();
|
||||||
|
await page.locator('button:has-text("OK")').click();
|
||||||
|
|
||||||
|
// Verify that the item has been removed from the layout
|
||||||
|
expect(await page.locator('.c-fl-container__frame').count()).toEqual(0);
|
||||||
|
});
|
||||||
|
test('items in a flexible layout can be removed with object tree context menu when viewing another item', async ({ page }) => {
|
||||||
|
test.info().annotations.push({
|
||||||
|
type: 'issue',
|
||||||
|
description: 'https://github.com/nasa/openmct/issues/3117'
|
||||||
|
});
|
||||||
|
// Create a Flexible Layout
|
||||||
|
const flexibleLayout = await createDomainObjectWithDefaults(page, {
|
||||||
|
type: 'Flexible Layout',
|
||||||
|
name: "Test Flexible Layout"
|
||||||
|
});
|
||||||
|
// Edit Flexible Layout
|
||||||
|
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 Flexible Layout and save changes
|
||||||
|
await page.dragAndDrop('text=Test Sine Wave Generator', '.c-fl__container.is-empty');
|
||||||
|
await page.locator('button[title="Save"]').click();
|
||||||
|
await page.locator('text=Save and Finish Editing').click();
|
||||||
|
|
||||||
|
expect.soft(await page.locator('.c-fl-container__frame').count()).toEqual(1);
|
||||||
|
|
||||||
|
// Expand the Flexible Layout so we can remove the sine wave generator
|
||||||
|
await page.locator('.c-tree__item.is-navigated-object .c-disclosure-triangle').click();
|
||||||
|
|
||||||
|
// Go to the original Sine Wave Generator to navigate away from the Flexible Layout
|
||||||
|
await page.goto(sineWaveObject.url);
|
||||||
|
|
||||||
|
// Bring up context menu and remove
|
||||||
|
await page.locator('.c-tree__item.is-alias .c-tree__item__name:text("Test Sine Wave Generator")').click({ button: 'right' });
|
||||||
|
await page.locator('li[role="menuitem"]:has-text("Remove")').click();
|
||||||
|
await page.locator('button:has-text("OK")').click();
|
||||||
|
|
||||||
|
// navigate back to the display layout to confirm it has been removed
|
||||||
|
await page.goto(flexibleLayout.url);
|
||||||
|
|
||||||
|
// Verify that the item has been removed from the layout
|
||||||
|
expect(await page.locator('.c-fl-container__frame').count()).toEqual(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
93
e2e/tests/functional/plugins/gauge/gauge.e2e.spec.js
Normal file
93
e2e/tests/functional/plugins/gauge/gauge.e2e.spec.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This test suite is dedicated to testing the Gauge component.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { test, expect } = require('../../../../baseFixtures');
|
||||||
|
const { createDomainObjectWithDefaults } = require('../../../../appActions');
|
||||||
|
const uuid = require('uuid').v4;
|
||||||
|
|
||||||
|
test.describe('Gauge', () => {
|
||||||
|
let gauge;
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
// Open a browser, navigate to the main page, and wait until all networkevents to resolve
|
||||||
|
await page.goto('./', { waitUntil: 'networkidle' });
|
||||||
|
|
||||||
|
// Create the gauge
|
||||||
|
gauge = await createDomainObjectWithDefaults(page, { type: 'Gauge' });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Can add and remove telemetry sources @unstable', async ({ page }) => {
|
||||||
|
const editButtonLocator = page.locator('button[title="Edit"]');
|
||||||
|
const saveButtonLocator = page.locator('button[title="Save"]');
|
||||||
|
|
||||||
|
// Create a sine wave generator within the gauge
|
||||||
|
const swg1 = await createDomainObjectWithDefaults(page, {
|
||||||
|
type: 'Sine Wave Generator',
|
||||||
|
name: `swg-${uuid()}`,
|
||||||
|
parent: gauge.uuid
|
||||||
|
});
|
||||||
|
|
||||||
|
// Navigate to the gauge and verify that
|
||||||
|
// the SWG appears in the elements pool
|
||||||
|
await page.goto(gauge.url);
|
||||||
|
await editButtonLocator.click();
|
||||||
|
await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg1.name}`)).toBeVisible();
|
||||||
|
await saveButtonLocator.click();
|
||||||
|
await page.locator('li[title="Save and Finish Editing"]').click();
|
||||||
|
|
||||||
|
// Create another sine wave generator within the gauge
|
||||||
|
const swg2 = await createDomainObjectWithDefaults(page, {
|
||||||
|
type: 'Sine Wave Generator',
|
||||||
|
name: `swg-${uuid()}`,
|
||||||
|
parent: gauge.uuid
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify that the 'Replace telemetry source' modal appears and accept it
|
||||||
|
await expect.soft(page.locator('text=This action will replace the current telemetry source. Do you want to continue?')).toBeVisible();
|
||||||
|
await page.click('text=Ok');
|
||||||
|
|
||||||
|
// Navigate to the gauge and verify that the new SWG
|
||||||
|
// appears in the elements pool and the old one is gone
|
||||||
|
await page.goto(gauge.url);
|
||||||
|
await editButtonLocator.click();
|
||||||
|
await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg1.name}`)).toBeHidden();
|
||||||
|
await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg2.name}`)).toBeVisible();
|
||||||
|
await saveButtonLocator.click();
|
||||||
|
|
||||||
|
// Right click on the new SWG in the elements pool and delete it
|
||||||
|
await page.locator(`#inspector-elements-tree >> text=${swg2.name}`).click({
|
||||||
|
button: 'right'
|
||||||
|
});
|
||||||
|
await page.locator('li[title="Remove this object from its containing object."]').click();
|
||||||
|
|
||||||
|
// Verify that the 'Remove object' confirmation modal appears and accept it
|
||||||
|
await expect.soft(page.locator('text=Warning! This action will remove this object. Are you sure you want to continue?')).toBeVisible();
|
||||||
|
await page.click('text=Ok');
|
||||||
|
|
||||||
|
// Verify that the elements pool shows no elements
|
||||||
|
await expect(page.locator('text="No contained elements"')).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
93
e2e/tests/functional/plugins/plot/scatterPlot.e2e.spec.js
Normal file
93
e2e/tests/functional/plugins/plot/scatterPlot.e2e.spec.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This test suite is dedicated to testing the Scatter Plot component.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { test, expect } = require('../../../../pluginFixtures');
|
||||||
|
const { createDomainObjectWithDefaults } = require('../../../../appActions');
|
||||||
|
const uuid = require('uuid').v4;
|
||||||
|
|
||||||
|
test.describe('Scatter Plot', () => {
|
||||||
|
let scatterPlot;
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
// Open a browser, navigate to the main page, and wait until all networkevents to resolve
|
||||||
|
await page.goto('./', { waitUntil: 'networkidle' });
|
||||||
|
|
||||||
|
// Create the Scatter Plot
|
||||||
|
scatterPlot = await createDomainObjectWithDefaults(page, { type: 'Scatter Plot' });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Can add and remove telemetry sources', async ({ page }) => {
|
||||||
|
const editButtonLocator = page.locator('button[title="Edit"]');
|
||||||
|
const saveButtonLocator = page.locator('button[title="Save"]');
|
||||||
|
|
||||||
|
// Create a sine wave generator within the scatter plot
|
||||||
|
const swg1 = await createDomainObjectWithDefaults(page, {
|
||||||
|
type: 'Sine Wave Generator',
|
||||||
|
name: `swg-${uuid()}`,
|
||||||
|
parent: scatterPlot.uuid
|
||||||
|
});
|
||||||
|
|
||||||
|
// Navigate to the scatter plot and verify that
|
||||||
|
// the SWG appears in the elements pool
|
||||||
|
await page.goto(scatterPlot.url);
|
||||||
|
await editButtonLocator.click();
|
||||||
|
await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg1.name}`)).toBeVisible();
|
||||||
|
await saveButtonLocator.click();
|
||||||
|
await page.locator('li[title="Save and Finish Editing"]').click();
|
||||||
|
|
||||||
|
// Create another sine wave generator within the scatter plot
|
||||||
|
const swg2 = await createDomainObjectWithDefaults(page, {
|
||||||
|
type: 'Sine Wave Generator',
|
||||||
|
name: `swg-${uuid()}`,
|
||||||
|
parent: scatterPlot.uuid
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify that the 'Replace telemetry source' modal appears and accept it
|
||||||
|
await expect.soft(page.locator('text=This action will replace the current telemetry source. Do you want to continue?')).toBeVisible();
|
||||||
|
await page.click('text=Ok');
|
||||||
|
|
||||||
|
// Navigate to the scatter plot and verify that the new SWG
|
||||||
|
// appears in the elements pool and the old one is gone
|
||||||
|
await page.goto(scatterPlot.url);
|
||||||
|
await editButtonLocator.click();
|
||||||
|
await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg1.name}`)).toBeHidden();
|
||||||
|
await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg2.name}`)).toBeVisible();
|
||||||
|
await saveButtonLocator.click();
|
||||||
|
|
||||||
|
// Right click on the new SWG in the elements pool and delete it
|
||||||
|
await page.locator(`#inspector-elements-tree >> text=${swg2.name}`).click({
|
||||||
|
button: 'right'
|
||||||
|
});
|
||||||
|
await page.locator('li[title="Remove this object from its containing object."]').click();
|
||||||
|
|
||||||
|
// Verify that the 'Remove object' confirmation modal appears and accept it
|
||||||
|
await expect.soft(page.locator('text=Warning! This action will remove this object. Are you sure you want to continue?')).toBeVisible();
|
||||||
|
await page.click('text=Ok');
|
||||||
|
|
||||||
|
// Verify that the elements pool shows no elements
|
||||||
|
await expect(page.locator('text="No contained elements"')).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
@ -56,17 +56,12 @@ export default class Editor extends EventEmitter {
|
|||||||
* Save any unsaved changes from this editing session. This will
|
* Save any unsaved changes from this editing session. This will
|
||||||
* end the current transaction.
|
* end the current transaction.
|
||||||
*/
|
*/
|
||||||
save() {
|
async save() {
|
||||||
const transaction = this.openmct.objects.getActiveTransaction();
|
const transaction = this.openmct.objects.getActiveTransaction();
|
||||||
|
await transaction.commit();
|
||||||
return transaction.commit()
|
this.editing = false;
|
||||||
.then(() => {
|
this.emit('isEditing', false);
|
||||||
this.editing = false;
|
this.openmct.objects.endTransaction();
|
||||||
this.emit('isEditing', false);
|
|
||||||
this.openmct.objects.endTransaction();
|
|
||||||
}).catch(error => {
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -112,11 +112,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
removeFromComposition(telemetryObject) {
|
removeFromComposition(telemetryObject) {
|
||||||
let composition = this.domainObject.composition.filter(id =>
|
this.composition.remove(telemetryObject);
|
||||||
!this.openmct.objects.areIdsEqual(id, telemetryObject.identifier)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.openmct.objects.mutate(this.domainObject, 'composition', composition);
|
|
||||||
},
|
},
|
||||||
addTelemetryObject(telemetryObject) {
|
addTelemetryObject(telemetryObject) {
|
||||||
// grab information we need from the added telmetry object
|
// grab information we need from the added telmetry object
|
||||||
|
@ -104,10 +104,14 @@ export default {
|
|||||||
this.$set(this.plotSeries, this.plotSeries.length, series);
|
this.$set(this.plotSeries, this.plotSeries.length, series);
|
||||||
this.setAxesLabels();
|
this.setAxesLabels();
|
||||||
},
|
},
|
||||||
removeSeries(series) {
|
removeSeries(seriesKey) {
|
||||||
const index = this.plotSeries.findIndex(plotSeries => this.openmct.objects.areIdsEqual(series.identifier, plotSeries.identifier));
|
const seriesIndex = this.plotSeries.findIndex(
|
||||||
if (index !== undefined) {
|
plotSeries => this.openmct.objects.areIdsEqual(seriesKey, plotSeries.identifier)
|
||||||
this.$delete(this.plotSeries, index);
|
);
|
||||||
|
|
||||||
|
const foundSeries = seriesIndex > -1;
|
||||||
|
if (foundSeries) {
|
||||||
|
this.$delete(this.plotSeries, seriesIndex);
|
||||||
this.setAxesLabels();
|
this.setAxesLabels();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -245,6 +245,9 @@ export default {
|
|||||||
});
|
});
|
||||||
this.gridDimensions = [wMax * this.gridSize[0], hMax * this.gridSize[1]];
|
this.gridDimensions = [wMax * this.gridSize[0], hMax * this.gridSize[1]];
|
||||||
},
|
},
|
||||||
|
clearSelection() {
|
||||||
|
this.$el.click();
|
||||||
|
},
|
||||||
watchDisplayResize() {
|
watchDisplayResize() {
|
||||||
const resizeObserver = new ResizeObserver(() => this.updateGrid());
|
const resizeObserver = new ResizeObserver(() => this.updateGrid());
|
||||||
|
|
||||||
@ -478,7 +481,7 @@ export default {
|
|||||||
});
|
});
|
||||||
_.pullAt(this.layoutItems, indices);
|
_.pullAt(this.layoutItems, indices);
|
||||||
this.mutate("configuration.items", this.layoutItems);
|
this.mutate("configuration.items", this.layoutItems);
|
||||||
this.$el.click();
|
this.clearSelection();
|
||||||
},
|
},
|
||||||
untrackItem(item) {
|
untrackItem(item) {
|
||||||
if (!item.identifier) {
|
if (!item.identifier) {
|
||||||
@ -504,15 +507,11 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!telemetryViewCount && !objectViewCount) {
|
if (!telemetryViewCount && !objectViewCount) {
|
||||||
this.removeFromComposition(keyString);
|
this.removeFromComposition(item);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
removeFromComposition(keyString) {
|
removeFromComposition(item) {
|
||||||
let composition = this.domainObject.composition ? this.domainObject.composition : [];
|
this.composition.remove(item);
|
||||||
composition = composition.filter(identifier => {
|
|
||||||
return this.openmct.objects.makeKeyString(identifier) !== keyString;
|
|
||||||
});
|
|
||||||
this.mutate("composition", composition);
|
|
||||||
},
|
},
|
||||||
initializeItems() {
|
initializeItems() {
|
||||||
this.telemetryViewMap = {};
|
this.telemetryViewMap = {};
|
||||||
@ -529,7 +528,10 @@ export default {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.startTransaction();
|
||||||
removedItems.forEach(this.removeFromConfiguration);
|
removedItems.forEach(this.removeFromConfiguration);
|
||||||
|
|
||||||
|
return this.endTransaction();
|
||||||
},
|
},
|
||||||
isItemAlreadyTracked(child) {
|
isItemAlreadyTracked(child) {
|
||||||
let found = false;
|
let found = false;
|
||||||
@ -590,7 +592,7 @@ export default {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.mutate("configuration.items", layoutItems);
|
this.mutate("configuration.items", layoutItems);
|
||||||
this.$el.click();
|
this.clearSelection();
|
||||||
},
|
},
|
||||||
orderItem(position, selectedItems) {
|
orderItem(position, selectedItems) {
|
||||||
let delta = ORDERS[position];
|
let delta = ORDERS[position];
|
||||||
@ -773,7 +775,7 @@ export default {
|
|||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.openmct.objects.mutate(this.domainObject, "configuration.items", this.layoutItems);
|
this.openmct.objects.mutate(this.domainObject, "configuration.items", this.layoutItems);
|
||||||
this.openmct.objects.mutate(this.domainObject, "configuration.objectStyles", objectStyles);
|
this.openmct.objects.mutate(this.domainObject, "configuration.objectStyles", objectStyles);
|
||||||
this.$el.click(); //clear selection;
|
this.clearSelection();
|
||||||
|
|
||||||
newDomainObjectsArray.forEach(domainObject => {
|
newDomainObjectsArray.forEach(domainObject => {
|
||||||
this.composition.add(domainObject);
|
this.composition.add(domainObject);
|
||||||
@ -867,6 +869,20 @@ export default {
|
|||||||
this.removeItem(selection);
|
this.removeItem(selection);
|
||||||
this.initSelectIndex = this.layoutItems.length - 1; //restore selection
|
this.initSelectIndex = this.layoutItems.length - 1; //restore selection
|
||||||
},
|
},
|
||||||
|
startTransaction() {
|
||||||
|
if (!this.openmct.objects.isTransactionActive()) {
|
||||||
|
this.transaction = this.openmct.objects.startTransaction();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async endTransaction() {
|
||||||
|
if (!this.transaction) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.transaction.commit();
|
||||||
|
this.openmct.objects.endTransaction();
|
||||||
|
this.transaction = null;
|
||||||
|
},
|
||||||
toggleGrid() {
|
toggleGrid() {
|
||||||
this.showGrid = !this.showGrid;
|
this.showGrid = !this.showGrid;
|
||||||
},
|
},
|
||||||
|
@ -185,10 +185,24 @@ export default {
|
|||||||
this.composition.off('add', this.addFrame);
|
this.composition.off('add', this.addFrame);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
containsObject(identifier) {
|
||||||
|
if ('composition' in this.domainObject) {
|
||||||
|
return this.domainObject.composition
|
||||||
|
.some(childId => this.openmct.objects.areIdsEqual(childId, identifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
buildIdentifierMap() {
|
buildIdentifierMap() {
|
||||||
this.containers.forEach(container => {
|
this.containers.forEach(container => {
|
||||||
container.frames.forEach(frame => {
|
container.frames.forEach(frame => {
|
||||||
let keystring = this.openmct.objects.makeKeyString(frame.domainObjectIdentifier);
|
if (!this.containsObject(frame.domainObjectIdentifier)) {
|
||||||
|
this.removeChildObject(frame.domainObjectIdentifier);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const keystring = this.openmct.objects.makeKeyString(frame.domainObjectIdentifier);
|
||||||
this.identifierMap[keystring] = true;
|
this.identifierMap[keystring] = true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -296,11 +310,14 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
persist(index) {
|
persist(index) {
|
||||||
|
this.startTransaction();
|
||||||
if (index) {
|
if (index) {
|
||||||
this.openmct.objects.mutate(this.domainObject, `configuration.containers[${index}]`, this.containers[index]);
|
this.openmct.objects.mutate(this.domainObject, `configuration.containers[${index}]`, this.containers[index]);
|
||||||
} else {
|
} else {
|
||||||
this.openmct.objects.mutate(this.domainObject, 'configuration.containers', this.containers);
|
this.openmct.objects.mutate(this.domainObject, 'configuration.containers', this.containers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.endTransaction();
|
||||||
},
|
},
|
||||||
startContainerResizing(index) {
|
startContainerResizing(index) {
|
||||||
let beforeContainer = this.containers[index];
|
let beforeContainer = this.containers[index];
|
||||||
@ -366,6 +383,20 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.persist();
|
this.persist();
|
||||||
|
},
|
||||||
|
startTransaction() {
|
||||||
|
if (!this.openmct.objects.isTransactionActive()) {
|
||||||
|
this.transaction = this.openmct.objects.startTransaction();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async endTransaction() {
|
||||||
|
if (!this.transaction) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.transaction.commit();
|
||||||
|
this.openmct.objects.endTransaction();
|
||||||
|
this.transaction = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -52,7 +52,7 @@ export default class EditPropertiesAction extends PropertiesAction {
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_onSave(changes) {
|
async _onSave(changes) {
|
||||||
if (!this.openmct.objects.isTransactionActive()) {
|
if (!this.openmct.objects.isTransactionActive()) {
|
||||||
this.openmct.objects.startTransaction();
|
this.openmct.objects.startTransaction();
|
||||||
}
|
}
|
||||||
@ -70,14 +70,8 @@ export default class EditPropertiesAction extends PropertiesAction {
|
|||||||
this.openmct.objects.mutate(this.domainObject, key, value);
|
this.openmct.objects.mutate(this.domainObject, key, value);
|
||||||
});
|
});
|
||||||
const transaction = this.openmct.objects.getActiveTransaction();
|
const transaction = this.openmct.objects.getActiveTransaction();
|
||||||
|
await transaction.commit();
|
||||||
return transaction.commit()
|
this.openmct.objects.endTransaction();
|
||||||
.catch(error => {
|
|
||||||
throw error;
|
|
||||||
}).finally(() => {
|
|
||||||
this.openmct.objects.endTransaction();
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.openmct.notifications.error('Error saving objects');
|
this.openmct.notifications.error('Error saving objects');
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
@ -598,11 +598,7 @@ export default {
|
|||||||
return this.round(((vPercent / 100) * 270) + DIAL_VALUE_DEG_OFFSET, 2);
|
return this.round(((vPercent / 100) * 270) + DIAL_VALUE_DEG_OFFSET, 2);
|
||||||
},
|
},
|
||||||
removeFromComposition(telemetryObject = this.telemetryObject) {
|
removeFromComposition(telemetryObject = this.telemetryObject) {
|
||||||
let composition = this.domainObject.composition.filter(id =>
|
this.composition.remove(telemetryObject);
|
||||||
!this.openmct.objects.areIdsEqual(id, telemetryObject.identifier)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.openmct.objects.mutate(this.domainObject, 'composition', composition);
|
|
||||||
},
|
},
|
||||||
refreshData(bounds, isTick) {
|
refreshData(bounds, isTick) {
|
||||||
if (!isTick) {
|
if (!isTick) {
|
||||||
|
@ -894,24 +894,16 @@ export default {
|
|||||||
this.transaction = this.openmct.objects.startTransaction();
|
this.transaction = this.openmct.objects.startTransaction();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
saveTransaction() {
|
async saveTransaction() {
|
||||||
if (this.transaction !== undefined) {
|
if (this.transaction !== undefined) {
|
||||||
this.transaction.commit()
|
await this.transaction.commit();
|
||||||
.catch(error => {
|
this.openmct.objects.endTransaction();
|
||||||
throw error;
|
|
||||||
}).finally(() => {
|
|
||||||
this.openmct.objects.endTransaction();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cancelTransaction() {
|
async cancelTransaction() {
|
||||||
if (this.transaction !== undefined) {
|
if (this.transaction !== undefined) {
|
||||||
this.transaction.cancel()
|
await this.transaction.cancel();
|
||||||
.catch(error => {
|
this.openmct.objects.endTransaction();
|
||||||
throw error;
|
|
||||||
}).finally(() => {
|
|
||||||
this.openmct.objects.endTransaction();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ export default class RemoveAction {
|
|||||||
this.openmct = openmct;
|
this.openmct = openmct;
|
||||||
|
|
||||||
this.removeFromComposition = this.removeFromComposition.bind(this); // for access to private transaction variable
|
this.removeFromComposition = this.removeFromComposition.bind(this); // for access to private transaction variable
|
||||||
|
this.#transaction = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async invoke(objectPath) {
|
async invoke(objectPath) {
|
||||||
@ -152,16 +153,13 @@ export default class RemoveAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
saveTransaction() {
|
async saveTransaction() {
|
||||||
if (!this.#transaction) {
|
if (!this.#transaction) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.#transaction.commit()
|
await this.#transaction.commit();
|
||||||
.catch(error => {
|
this.openmct.objects.endTransaction();
|
||||||
throw error;
|
this.#transaction = null;
|
||||||
}).finally(() => {
|
|
||||||
this.openmct.objects.endTransaction();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,6 +78,8 @@ describe("The Remove Action plugin", () => {
|
|||||||
spyOn(removeAction, 'removeFromComposition').and.callThrough();
|
spyOn(removeAction, 'removeFromComposition').and.callThrough();
|
||||||
spyOn(removeAction, 'inNavigationPath').and.returnValue(false);
|
spyOn(removeAction, 'inNavigationPath').and.returnValue(false);
|
||||||
spyOn(openmct.objects, 'mutate').and.callThrough();
|
spyOn(openmct.objects, 'mutate').and.callThrough();
|
||||||
|
spyOn(openmct.objects, 'startTransaction').and.callThrough();
|
||||||
|
spyOn(openmct.objects, 'endTransaction').and.callThrough();
|
||||||
removeAction.removeFromComposition(parentObject, childObject);
|
removeAction.removeFromComposition(parentObject, childObject);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -90,6 +92,17 @@ describe("The Remove Action plugin", () => {
|
|||||||
expect(openmct.objects.mutate).toHaveBeenCalled();
|
expect(openmct.objects.mutate).toHaveBeenCalled();
|
||||||
expect(openmct.objects.mutate.calls.argsFor(0)[0]).toEqual(parentObject);
|
expect(openmct.objects.mutate.calls.argsFor(0)[0]).toEqual(parentObject);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("it should start a transaction", () => {
|
||||||
|
expect(openmct.objects.startTransaction).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("it should end the transaction", (done) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(openmct.objects.endTransaction).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when determining the object is applicable", () => {
|
describe("when determining the object is applicable", () => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user