Compare commits

..

8 Commits

Author SHA1 Message Date
unlikelyzero
2f79adcb88 Add notebook stubs and testcases 2022-03-21 15:52:05 -07:00
unlikelyzero
92112cf8a2 moved test suites 2022-03-17 09:03:19 -07:00
unlikelyzero
56159843a2 timestrip visual test 2022-03-16 23:52:15 -07:00
John Hill
93a81a1369 [CI] Update GHA to include a specific playwright dependency version (#4944)
* Update e2e-pr.yml

* Update e2e-pr.yml

* Update e2e-pr.yml

* visual

Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
2022-03-16 22:05:50 +00:00
Nikhil
482d8f392c Domain object properties validation not working always (#4893)
* Domain object properties validation not working always #4849

* stub of a test

* set local execution to chrome

* skip visual test

* use input event for changes and throttle update.

* moved file to correct location

* fixed e2e tests.

Co-authored-by: unlikelyzero <jchill2@gmail.com>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2022-03-16 21:53:58 +00:00
John Hill
67234c70a4 [Build] 🐰🐰🐰 Remove the last carrots 🐰🐰🐰 (#4941)
* [Build] 🐰🐰🐰 Remove the last carrots 🐰🐰🐰

* Update package.json

Co-authored-by: Khalid Adil <khalidadil29@gmail.com>
2022-03-16 21:44:41 +00:00
Nikhil
e9680e975f Form validation error messages are not being displayed properly (#4887)
* Form validation error messages are not being displayed properly #4697

* e2e Tests

* fixed element.checkValidity test

* remove comment

* adding timeconductor tests

* rename

Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
Co-authored-by: unlikelyzero <jchill2@gmail.com>
2022-03-16 13:54:46 -07:00
Jamie V
e691a89682 Correctly use creatable attribute and persistability when working with domainObjects (#4898)
* making move action location check persistability

* adding persistence check instead of creatability for styles

* added check for link action to make sure parent is persistable

* debug

* adding parent to link action and move action form location controls so they can be used in the form

* adding parent persistability check for duplicate

* updating multilple actions appliesTo methods to check for persistability

* updated the tree to not require an initial selection if being used in a form

* remove noneditable folder plugin

* added persistence check for the parent, in the create wizard

* minor name change

* removing noneditabl folder from default plugins as well

* checking the correct parent for persistability in create wizard

* importing file-saver correctly

* updated tests for import as json

* changes addressing PR review: using consts, removing comments, removing unneccessary code

Co-authored-by: Scott Bell <scott@traclabs.com>
2022-03-14 21:20:06 +01:00
22 changed files with 640 additions and 123 deletions

View File

@@ -30,7 +30,7 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: '16'
- run: npx playwright install-deps
- run: npx playwright@1.19.2 install
- run: npm install
- run: npm run test:e2e:full
- name: Archive test results

View File

@@ -17,7 +17,7 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: '16'
- run: npx playwright install-deps
- run: npx playwright@1.19.2 install
- run: npm install
- name: Run the e2e visual tests
run: npm run test:e2e:visual

View File

@@ -0,0 +1,161 @@
/*****************************************************************************
* 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 tests which verify the basic operations surrounding conditionSets.
*/
const { test, expect } = require('@playwright/test');
test.describe('Sine Wave Generator', () => {
test('Create new Sine Wave Generator Object and validate create Form Logic', async ({ page }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
//Click the Create button
await page.click('button:has-text("Create")');
// Click Sine Wave Generator
await page.click('text=Sine Wave Generator');
// Verify that the each required field has required indicator
// Title
await expect(page.locator('.c-form-row__state-indicator').first()).toHaveClass(['c-form-row__state-indicator req']);
// Verify that the Notes row does not have a required indicator
await expect(page.locator('.c-form__section div:nth-child(3) .form-row .c-form-row__state-indicator')).not.toContain('.req');
// Period
await expect(page.locator('.c-form__section div:nth-child(4) .form-row .c-form-row__state-indicator')).toHaveClass(['c-form-row__state-indicator req']);
// Amplitude
await expect(page.locator('.c-form__section div:nth-child(5) .form-row .c-form-row__state-indicator')).toHaveClass(['c-form-row__state-indicator req']);
// Offset
await expect(page.locator('.c-form__section div:nth-child(6) .form-row .c-form-row__state-indicator')).toHaveClass(['c-form-row__state-indicator req']);
// Data Rate
await expect(page.locator('.c-form__section div:nth-child(7) .form-row .c-form-row__state-indicator')).toHaveClass(['c-form-row__state-indicator req']);
// Phase
await expect(page.locator('.c-form__section div:nth-child(8) .form-row .c-form-row__state-indicator')).toHaveClass(['c-form-row__state-indicator req']);
// Randomness
await expect(page.locator('.c-form__section div:nth-child(9) .form-row .c-form-row__state-indicator')).toHaveClass(['c-form-row__state-indicator req']);
// Verify that by removing value from required text field shows invalid indicator
await page.locator('text=Properties Title Notes Period Amplitude Offset Data Rate (hz) Phase (radians) Ra >> input[type="text"]').fill('');
await expect(page.locator('.c-form-row__state-indicator').first()).toHaveClass(['c-form-row__state-indicator req invalid']);
// Verify that by adding value to empty required text field changes invalid to valid indicator
await page.locator('text=Properties Title Notes Period Amplitude Offset Data Rate (hz) Phase (radians) Ra >> input[type="text"]').fill('non empty');
await expect(page.locator('.c-form-row__state-indicator').first()).toHaveClass(['c-form-row__state-indicator req valid']);
// Verify that by removing value from required number field shows invalid indicator
await page.locator('.field.control.l-input-sm input').first().fill('');
await expect(page.locator('.c-form__section div:nth-child(4) .form-row .c-form-row__state-indicator')).toHaveClass(['c-form-row__state-indicator req invalid']);
// Verify that by adding value to empty required number field changes invalid to valid indicator
await page.locator('.field.control.l-input-sm input').first().fill('3');
await expect(page.locator('.c-form__section div:nth-child(4) .form-row .c-form-row__state-indicator')).toHaveClass(['c-form-row__state-indicator req valid']);
// Verify that can change value of number field by up/down arrows keys
// Click .field.control.l-input-sm input >> nth=0
await page.locator('.field.control.l-input-sm input').first().click();
// Press ArrowUp 3 times to change value from 3 to 6
await page.locator('.field.control.l-input-sm input').first().press('ArrowUp');
await page.locator('.field.control.l-input-sm input').first().press('ArrowUp');
await page.locator('.field.control.l-input-sm input').first().press('ArrowUp');
const value = await page.locator('.field.control.l-input-sm input').first().inputValue();
await expect(value).toBe('6');
// Click .c-form-row__state-indicator.grows
await page.locator('.c-form-row__state-indicator.grows').click();
// Click text=Properties Title Notes Period Amplitude Offset Data Rate (hz) Phase (radians) Ra >> input[type="text"]
await page.locator('text=Properties Title Notes Period Amplitude Offset Data Rate (hz) Phase (radians) Ra >> input[type="text"]').click();
// Click .c-form-row__state-indicator >> nth=0
await page.locator('.c-form-row__state-indicator').first().click();
// Fill text=Properties Title Notes Period Amplitude Offset Data Rate (hz) Phase (radians) Ra >> input[type="text"]
await page.locator('text=Properties Title Notes Period Amplitude Offset Data Rate (hz) Phase (radians) Ra >> input[type="text"]').fill('New Sine Wave Generator');
// Double click div:nth-child(4) .form-row .c-form-row__controls
await page.locator('div:nth-child(4) .form-row .c-form-row__controls').dblclick();
// Click .field.control.l-input-sm input >> nth=0
await page.locator('.field.control.l-input-sm input').first().click();
// Click div:nth-child(4) .form-row .c-form-row__state-indicator
await page.locator('div:nth-child(4) .form-row .c-form-row__state-indicator').click();
// Click .field.control.l-input-sm input >> nth=0
await page.locator('.field.control.l-input-sm input').first().click();
// Click .field.control.l-input-sm input >> nth=0
await page.locator('.field.control.l-input-sm input').first().click();
// Click div:nth-child(5) .form-row .c-form-row__controls .form-control .field input
await page.locator('div:nth-child(5) .form-row .c-form-row__controls .form-control .field input').click();
// Click div:nth-child(5) .form-row .c-form-row__controls .form-control .field input
await page.locator('div:nth-child(5) .form-row .c-form-row__controls .form-control .field input').click();
// Click div:nth-child(5) .form-row .c-form-row__controls .form-control .field input
await page.locator('div:nth-child(5) .form-row .c-form-row__controls .form-control .field input').click();
// Click div:nth-child(6) .form-row .c-form-row__controls .form-control .field input
await page.locator('div:nth-child(6) .form-row .c-form-row__controls .form-control .field input').click();
// Double click div:nth-child(7) .form-row .c-form-row__controls .form-control .field input
await page.locator('div:nth-child(7) .form-row .c-form-row__controls .form-control .field input').dblclick();
// Click div:nth-child(7) .form-row .c-form-row__state-indicator
await page.locator('div:nth-child(7) .form-row .c-form-row__state-indicator').click();
// Click div:nth-child(7) .form-row .c-form-row__controls .form-control .field input
await page.locator('div:nth-child(7) .form-row .c-form-row__controls .form-control .field input').click();
// Fill div:nth-child(7) .form-row .c-form-row__controls .form-control .field input
await page.locator('div:nth-child(7) .form-row .c-form-row__controls .form-control .field input').fill('3');
//Click text=OK
await Promise.all([
page.waitForNavigation(),
page.click('text=OK')
]);
// Verify that the Sine Wave Generator is displayed and correct
// Verify object properties
await expect(page.locator('.l-browse-bar__object-name')).toContainText('New Sine Wave Generator');
// Verify canvas rendered
await page.locator('canvas').nth(1).click({
position: {
x: 341,
y: 28
}
});
});
});

View File

@@ -0,0 +1,46 @@
/*****************************************************************************
* 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 tests which verify the basic operations surrounding Notebooks.
*/
const { test, expect } = require('@playwright/test');
test.describe('Notebook CRUD Operations', () => {
test.fixme('Can create a Notebook Object', async ({ page }) => {
//Create domain object
});
test.fixme('Can update a Notebook Object', async ({ page }) => {
//Create domain object
//Save Domain Object
//Verify that the newly created domain object can be exported as JSON from the 3 dot menu
});
test.fixme('Can view a perviously created Notebook Object', async ({ page }) => {
// Create 2 objects with hierarchy
// Export as JSON
// Verify Hiearchy
});
test.fixme('Can Delete a Notebook Object', async ({ page }) => {
// Other than non-persistible objects
});
});

View File

@@ -0,0 +1,114 @@
Notebook Testing:
## Useful commands:
1. - To check default notebook:
`JSON.parse(localStorage.getItem('notebook-storage'));`
1. - Clear default notebook:
`localStorage.setItem('notebook-storage', null);`
## I. Default Notebook:
1. Make sure there is no default notebook
`localStorage.setItem('notebook-storage', null);`
2. Check for default notebook is null
`JSON.parse(localStorage.getItem('notebook-storage'));`
3. Create 2 notebooks, add entry into first one to make it default now add entry to other one. Verify by icon change and using following
1. `JSON.parse(localStorage.getItem('notebook-storage'));`
2. There is default icon on notebook inside tree and main view after notebook name
3. Inside default notebook, default section and page has deafult icon.
4. Delete default notebook and check for default notebook is null and default icons are removed.
`JSON.parse(localStorage.getItem('notebook-storage'));`
## II. Sections and Pages:
1. - Newly created notebook should have one Section and one page, 'Unnamed Section'/'Unnamed Page'
### - Sections:
1. - Rename existing section '1 Section' and '1 Page'
- click 'add section' new section - should be added 'Unnamed Section' with new page 'Unnamed Page'
1. - Delete existing section
- new 'Unnamed Section' automatically gets created.
1. - Have 5 total sections without a default section/page
- select 3nd section then delete 4th section
- 3rd is still selected
1. - Have 5 total sections without a default section/page
- select 3rd section then delete 3rd section
- 1st is now selected
1. - Have 5 total sections with a 3rd section as default section
- select 2nd section then delete 2nd section
- 3rd (default) is now selected
1. - Have 5 total sections with a 3rd section as default section
- select 3rd section then delete 3rd section
- 1st is now selected and there is no default notebook
### - Pages:
1. - Delete existing page
- new 'Unnamed Page' automatically gets created
1. - Have 5 total pages without a default page
- select 3nd page then delete 4th page
- 3rd is still selected
1. - Have 5 total pages without a default page
- select 3rd page then delete 3rd page
- 1st is now selected
1. - Have 5 total pages with a 3rd page as default page
- select 2nd page then delete 2nd page
- 3rd (default) is now selected
1. - Have 5 total pages with a 3rd page as default page
- select 3rd page then delete 3rd page
- 1st is now selected and there is no default notebook
## III. Entries:
1. - Add new entry into page should create entry and focus on it
1. - Drag and drop any telmetry object on 'drop object'
- new entry gets created with telemtry object
1. - Add new entry into page
- drop any telmetry object on this entry
- telmetry object appears inside this entry
1. - Add new entry into page, enter text
- navigate away and return
- edit entry text
- navigate away and return back
- confirm text is correct
1. - delete previously created entry
## IV: Snapshot Menu:
1. - There should be no default notebook
- Clear default notebook if exists using `localStorage.setItem('notebook-storage', null);`
- refresh page
- Click on 'Notebook Snaphot Menu'
- Following popup option should be there
1. save to Notebook Snapshots
1. - Check for default notebook if there is one, else add some entry into one of notebook to make it default
- Click on 'Notebook Snaphot Menu'
- Following popup options should be there
1. save to Default Notebook
1. save to Notebook Snapshots
1. - Select any telemetry object eg: SWG
- From 'Notebook Snaphot Menu' click on 'save to Default Notebook'
- Navigate to default notebook - section - page and verify that SWG snaphot has been added with correct details
## V: Snapshot container:
1. - Select any telemetry object eg: SWG
- Click on 'Notebook Snaphot Menu'
- from popup options click on 'save to Notebook Snapshots'
- Snapshots indicator should blink, click on it to view snapshots
- drag and drop snapshot on droppable area for new enrty
- this should create a new entry with given snaphot has been added to it
1. - Select any telemetry object eg: SWG
- Click on 'Notebook Snaphot Menu'
- from popup options click on 'save to Notebook Snapshots'
- Snapshots indicator should blink, click on it to view snapshots
- goto any notebook with pre exisintg entry (if not create new entry)
- drag and drop snapshot on exisintg entry
- this should add a given snaphot inside that entry
## VI: Embeds:
1. - Add SWG using snapshot to notebook
- Go to that entry
- click on thumbnail image
- should open image with PNG, JPG and Annotate buttons
- verify they all work as intended
1. - Add SWG using snapshot to notebook
- note down start and end time on time conductor and Fixed Timestamp/Local Clock
- Go to that entry
- change start and end time on time conductor if Local Clock
- click on embed Name/Text
- should take to that object
- also verify that start and end time on time conductor (also should automatically switch to Fixed timestamp)*/

View File

@@ -0,0 +1,69 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
const { test, expect } = require('@playwright/test');
test.describe('Time counductor operations', () => {
test('validate start time does not exceeds end time', async ({ page }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
const year = new Date().getFullYear();
let startDate = 'xxxx-01-01 01:00:00.000Z';
startDate = year + startDate.substring(4);
let endDate = 'xxxx-01-01 02:00:00.000Z';
endDate = year + endDate.substring(4);
const startTimeLocator = page.locator('input[type="text"]').first();
const endTimeLocator = page.locator('input[type="text"]').nth(1);
// Click start time
await startTimeLocator.click();
// Click end time
await endTimeLocator.click();
await endTimeLocator.fill(endDate.toString());
await startTimeLocator.fill(startDate.toString());
// invalid start date
startDate = (year + 1) + startDate.substring(4);
await startTimeLocator.fill(startDate.toString());
await endTimeLocator.click();
const startDateValidityStatus = await startTimeLocator.evaluate((element) => element.checkValidity());
expect(startDateValidityStatus).not.toBeTruthy();
// fix to valid start date
startDate = (year - 1) + startDate.substring(4);
await startTimeLocator.fill(startDate.toString());
// invalid end date
endDate = (year - 2) + endDate.substring(4);
await endTimeLocator.fill(endDate.toString());
await startTimeLocator.click();
const endDateValidityStatus = await endTimeLocator.evaluate((element) => element.checkValidity());
expect(endDateValidityStatus).not.toBeTruthy();
});
});

View File

@@ -0,0 +1,126 @@
/*****************************************************************************
* 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.
*****************************************************************************/
/*
Collection of Visual Tests set to run in a default context. The tests within this suite
are only meant to run against openmct's app.js started by `npm run start` within the
`./e2e/playwright-visual.config.js` file.
These should only use functional expect statements to verify assumptions about the state
in a test and not for functional verification of correctness. Visual tests are not supposed
to "fail" on assertions. Instead, they should be used to detect changes between builds or branches.
Note: Larger testsuite sizes are OK due to the setup time associated with these tests.
*/
const { test, expect } = require('@playwright/test');
const percySnapshot = require('@percy/playwright');
const path = require('path');
const sinon = require('sinon');
const VISUAL_GRACE_PERIOD = 5 * 1000; //Lets the application "simmer" before the snapshot is taken
// // Snippet from https://github.com/microsoft/playwright/issues/6347#issuecomment-965887758
// //Will replace with cy.clock() equivalent
test.beforeEach(async ({ context }) => {
await context.addInitScript({
// eslint-disable-next-line no-undef
path: path.join(__dirname, '../../..', './node_modules/sinon/pkg/sinon.js')
});
await context.addInitScript(() => {
window.__clock = sinon.useFakeTimers({
now: 1483228800000,
shouldAdvanceTime: true
}); //Set browser clock to UNIX Epoch
});
});
test('Visual - Display layout items view', async ({ page }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
await page.evaluate(() => window.__clock.tick(1000));
//Click the Create button
await page.click('button:has-text("Create")');
// Click text=Display Layout
await page.click('text=Display Layout');
// Click text=OK
await page.click('text=OK');
// Take a snapshot of the newly created Display Layout object
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
await percySnapshot(page, 'Default Display Layout');
// Click text=Snapshot Save and Finish Editing Save and Continue Editing >> button >> nth=1
await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
// Click text=Save and Finish Editing
await page.locator('text=Save and Finish Editing').click();
//Click the Create button
await page.click('button:has-text("Create")');
// Click text=Sine Wave Generator
await page.click('text=Sine Wave Generator');
// Click text=Save In Open MCT No items >> input[type="search"]
await page.locator('text=Save In Open MCT No items >> input[type="search"]').click();
// Fill text=Save In Open MCT No items >> input[type="search"]
await page.locator('text=Save In Open MCT No items >> input[type="search"]').fill('Unnamed Display Layout');
// Click text=OK Cancel
await page.locator('text=OK Cancel').click();
// Click text=OK
await page.click('text=OK');
// Take a snapshot of the newly created Display Layout object
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
await percySnapshot(page, 'Default Sine Wave Generator');
});
test.skip('Visual - Timestrip', async ({ page }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
//Click the Create button
await page.click('button:has-text("Create")');
// Click li:has-text("Example Imagery")
await page.locator('li:has-text("Example Imagery")').click();
// Fill input[type="number"]
//await page.locator('input[type="number"]').fill('500');
// Click text=OK
await page.locator('text=OK').click();
await page.pause();
//
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
await percySnapshot(page, 'Default Timestrip created');
});

View File

@@ -40,7 +40,7 @@ const sinon = require('sinon');
const VISUAL_GRACE_PERIOD = 5 * 1000; //Lets the application "simmer" before the snapshot is taken
// Snippet from https://github.com/microsoft/playwright/issues/6347#issuecomment-965887758
// Will replace with cy.clock() equivalent
//Will replace with cy.clock() equivalent
test.beforeEach(async ({ context }) => {
await context.addInitScript({
// eslint-disable-next-line no-undef
@@ -112,28 +112,48 @@ test('Visual - Default Condition Widget', async ({ page }) => {
await percySnapshot(page, 'Default Condition Widget');
});
test.skip('Visual - Display layout items view', async ({ page }) => {
test('Visual - Time Conductor start time is less than end time', async ({ page }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
const year = new Date().getFullYear();
//Click the Create button
await page.click('button:has-text("Create")');
let startDate = 'xxxx-01-01 01:00:00.000Z';
startDate = year + startDate.substring(4);
// Click text=Display Layout
await page.click('text=Display Layout');
let endDate = 'xxxx-01-01 02:00:00.000Z';
endDate = year + endDate.substring(4);
// Click text=OK
await page.click('text=OK');
await page.locator('input[type="text"]').nth(1).fill(endDate.toString());
await page.locator('input[type="text"]').first().fill(startDate.toString());
// Take a snapshot of the newly created Display Layout object
// verify no error msg
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
await percySnapshot(page, 'Default Display Layout');
await percySnapshot(page, 'Default Time conductor');
// Click text=Snapshot Save and Finish Editing Save and Continue Editing >> button >> nth=1
await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
startDate = (year + 1) + startDate.substring(4);
await page.locator('input[type="text"]').first().fill(startDate.toString());
await page.locator('input[type="text"]').nth(1).click();
// Click text=Save and Finish Editing
await page.locator('text=Save and Finish Editing').click();
// verify error msg for start time (unable to capture snapshot of popup)
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
await percySnapshot(page, 'Start time error');
startDate = (year - 1) + startDate.substring(4);
await page.locator('input[type="text"]').first().fill(startDate.toString());
endDate = (year - 2) + endDate.substring(4);
await page.locator('input[type="text"]').nth(1).fill(endDate.toString());
await page.locator('input[type="text"]').first().click();
// verify error msg for end time (unable to capture snapshot of popup)
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
await percySnapshot(page, 'End time error');
});
test('Visual - Sine Wave Generator Form', async ({ page }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
//Click the Create button
await page.click('button:has-text("Create")');
@@ -141,20 +161,35 @@ test.skip('Visual - Display layout items view', async ({ page }) => {
// Click text=Sine Wave Generator
await page.click('text=Sine Wave Generator');
// Click text=Save In Open MCT No items >> input[type="search"]
await page.locator('text=Save In Open MCT No items >> input[type="search"]').click();
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
await percySnapshot(page, 'Default Sine Wave Generator Form');
// Fill text=Save In Open MCT No items >> input[type="search"]
await page.locator('text=Save In Open MCT No items >> input[type="search"]').fill('Unnamed Display Layout');
await page.locator('.field.control.l-input-sm input').first().click();
await page.locator('.field.control.l-input-sm input').first().fill('');
// Click text=OK Cancel
await page.locator('text=OK Cancel').click();
// Validate red x mark
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
await percySnapshot(page, 'removed amplitude property value');
});
test.only('Visual - Timestrip', async ({ page }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
//Click the Create button
await page.click('button:has-text("Create")');
// Click li:has-text("Example Imagery")
await page.locator('li:has-text("Example Imagery")').click();
// Fill input[type="number"]
//await page.locator('input[type="number"]').fill('500');
// Click text=OK
await page.click('text=OK');
await page.locator('text=OK').click();
// Take a snapshot of the newly created Display Layout object
await page.pause();
//
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
await percySnapshot(page, 'Default Sine Wave Generator');
await percySnapshot(page, 'Default Timestrip created');
});

View File

@@ -1,6 +1,6 @@
{
"name": "openmct",
"version": "2.0.1",
"version": "2.0.1-SNAPSHOT",
"description": "The Open MCT core platform",
"devDependencies": {
"@braintree/sanitize-url": "6.0.0",
@@ -30,7 +30,7 @@
"html-loader": "0.5.5",
"html2canvas": "1.4.1",
"imports-loader": "0.8.0",
"istanbul-instrumenter-loader": "^3.0.1",
"istanbul-instrumenter-loader": "3.0.1",
"jasmine-core": "4.0.0",
"jsdoc": "3.5.5",
"karma": "6.3.15",
@@ -43,36 +43,36 @@
"karma-junit-reporter": "2.0.1",
"karma-sourcemap-loader": "0.3.8",
"karma-spec-reporter": "0.0.33",
"karma-webpack": "^5.0.0",
"location-bar": "^3.0.1",
"lodash": "^4.17.12",
"karma-webpack": "5.0.0",
"location-bar": "3.0.1",
"lodash": "4.17.12",
"mini-css-extract-plugin": "2.4.5",
"moment": "2.29.1",
"moment-duration-format": "^2.2.2",
"moment-duration-format": "2.2.2",
"moment-timezone": "0.5.28",
"node-bourbon": "^4.2.3",
"painterro": "^1.2.56",
"plotly.js-basic-dist": "^2.5.0",
"plotly.js-gl2d-dist": "^2.5.0",
"printj": "^1.2.1",
"raw-loader": "^0.5.1",
"request": "^2.69.0",
"node-bourbon": "4.2.3",
"painterro": "1.2.56",
"plotly.js-basic-dist": "2.5.0",
"plotly.js-gl2d-dist": "2.5.0",
"printj": "1.2.1",
"raw-loader": "0.5.1",
"request": "2.69.0",
"resolve-url-loader": "4.0.0",
"sass": "1.49.0",
"sass-loader": "12.4.0",
"sinon": "13.0.1",
"style-loader": "^1.0.1",
"uuid": "^3.3.3",
"uuid": "3.3.3",
"vue": "2.6.14",
"vue-eslint-parser": "8.2.0",
"vue-loader": "15.9.8",
"vue-template-compiler": "2.6.14",
"webpack": "5.68.0",
"webpack-cli": "4.9.2",
"webpack-dev-middleware": "^3.1.3",
"webpack-hot-middleware": "^2.22.3",
"webpack-dev-middleware": "3.7.3",
"webpack-hot-middleware": "2.22.3",
"webpack-merge": "5.8.0",
"zepto": "^1.2.0"
"zepto": "1.2.0"
},
"scripts": {
"clean": "rm -rf ./dist ./node_modules; rm package-lock.json",
@@ -88,8 +88,8 @@
"test:debug": "cross-env NODE_ENV=debug karma start --no-single-run",
"test:coverage": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" COVERAGE=true karma start --single-run",
"test:coverage:firefox": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run --browsers=FirefoxHeadless",
"test:e2e:ci": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome smoke default condition.e2e",
"test:e2e:local": "npx playwright test --config=e2e/playwright-local.config.js",
"test:e2e:ci": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome smoke default condition timeConductor",
"test:e2e:local": "npx playwright test --config=e2e/playwright-local.config.js --project=chrome",
"test:e2e:visual": "percy exec --config ./e2e/.percy.yml -- npx playwright test --config=e2e/playwright-visual.config.js default",
"test:e2e:full": "npx playwright test --config=e2e/playwright-ci.config.js",
"test:watch": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --no-single-run",

View File

@@ -30,13 +30,15 @@
:min="model.min"
:max="model.max"
:step="model.step"
@blur="blur()"
@input="updateText()"
>
</span>
</span>
</template>
<script>
import { throttle } from 'lodash';
export default {
props: {
model: {
@@ -49,8 +51,12 @@ export default {
field: this.model.value
};
},
mounted() {
this.updateText = throttle(this.updateText.bind(this), 200);
},
methods: {
blur() {
updateText() {
console.log('updateText', this.field);
const data = {
model: this.model,
value: this.field

View File

@@ -28,7 +28,7 @@
<textarea v-model="field"
type="text"
:size="model.size"
@blur="blur()"
@input="updateText()"
>
</textarea>
</span>
@@ -36,6 +36,8 @@
</template>
<script>
import { throttle } from 'lodash';
export default {
props: {
model: {
@@ -48,8 +50,11 @@ export default {
field: this.model.value
};
},
mounted() {
this.updateText = throttle(this.updateText.bind(this), 500);
},
methods: {
blur() {
updateText() {
const data = {
model: this.model,
value: this.field

View File

@@ -28,13 +28,15 @@
<input v-model="field"
type="text"
:size="model.size"
@blur="blur()"
@input="updateText()"
>
</span>
</span>
</template>
<script>
import { throttle } from 'lodash';
export default {
props: {
model: {
@@ -47,8 +49,11 @@ export default {
field: this.model.value
};
},
mounted() {
this.updateText = throttle(this.updateText.bind(this), 500);
},
methods: {
blur() {
updateText() {
const data = {
model: this.model,
value: this.field

View File

@@ -15,8 +15,7 @@ export default function (folderName, couchPlugin, searchFilter) {
return Promise.resolve({
identifier,
type: 'folder',
name: folderName || "CouchDB Documents",
location: 'ROOT'
name: folderName || "CouchDB Documents"
});
}
}

View File

@@ -85,8 +85,7 @@ describe('the plugin', function () {
expect(object).toEqual({
identifier,
type: 'folder',
name: 'CouchDB Documents',
location: 'ROOT'
name: "CouchDB Documents"
});
});
});

View File

@@ -166,7 +166,7 @@ export default {
},
computed: {
gridSize() {
return this.domainObject.configuration.layoutGrid.map(Number);
return this.domainObject.configuration.layoutGrid;
},
layoutItems() {
return this.domainObject.configuration.items;

View File

@@ -43,7 +43,7 @@
<template v-for="(container, index) in containers">
<drop-hint
v-if="index === 0 && containers.length > 1"
:key="`hint-top-${container.id}`"
:key="index"
class="c-fl-frame__drop-hint"
:index="-1"
:allow-drop="allowContainerDrop"
@@ -51,7 +51,7 @@
/>
<container-component
:key="`component-${container.id}`"
:key="container.id"
class="c-fl__container"
:index="index"
:container="container"
@@ -66,7 +66,7 @@
<resize-handle
v-if="index !== (containers.length - 1)"
:key="`handle-${container.id}`"
:key="index"
:index="index"
:orientation="rowsLayout ? 'vertical' : 'horizontal'"
:is-editing="isEditing"
@@ -77,7 +77,7 @@
<drop-hint
v-if="containers.length > 1"
:key="`hint-bottom-${container.id}`"
:key="index"
class="c-fl-frame__drop-hint"
:index="index"
:allow-drop="allowContainerDrop"

View File

@@ -90,17 +90,6 @@ export default class LinkAction {
validate(currentParent) {
return (data) => {
// default current parent to ROOT, if it's undefined, then it's a root level item
if (currentParent === undefined) {
currentParent = {
identifier: {
key: 'ROOT',
namespace: ''
}
};
}
const parentCandidate = data.value[0];
const currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier);
const parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier);

View File

@@ -92,33 +92,23 @@ export default class RemoveAction {
this.openmct.editor.save();
}
if (!this.isAlias(child, parent)) {
const parentKeyString = this.openmct.objects.makeKeyString(parent.identifier);
const isAlias = parentKeyString !== child.location;
if (!isAlias) {
this.openmct.objects.mutate(child, 'location', null);
}
}
isAlias(child, parent) {
if (parent === undefined) {
// then it's a root item, not an alias
return false;
}
const parentKeyString = this.openmct.objects.makeKeyString(parent.identifier);
const childLocation = child.location;
return childLocation !== parentKeyString;
}
appliesTo(objectPath) {
const parent = objectPath[1];
const parentType = parent && this.openmct.types.get(parent.type);
const child = objectPath[0];
const locked = child.locked ? child.locked : parent && parent.locked;
const isEditing = this.openmct.editor.isEditing();
const isPersistable = this.openmct.objects.isPersistable(child.identifier);
const isAlias = this.isAlias(child, parent);
let parent = objectPath[1];
let parentType = parent && this.openmct.types.get(parent.type);
let child = objectPath[0];
let locked = child.locked ? child.locked : parent && parent.locked;
let isEditing = this.openmct.editor.isEditing();
let isPersistable = this.openmct.objects.isPersistable(child.identifier);
if (locked || (!isPersistable && !isAlias)) {
if (locked || !isPersistable) {
return false;
}

View File

@@ -31,11 +31,9 @@
>
<div class="c-conductor__time-bounds">
<conductor-inputs-fixed v-if="isFixed"
:input-bounds="viewBounds"
@updated="saveFixedOffsets"
/>
<conductor-inputs-realtime v-else
:input-bounds="viewBounds"
@updated="saveClockOffsets"
/>
<ConductorModeIcon class="c-conductor__mode-icon" />

View File

@@ -71,12 +71,6 @@ export default {
default() {
return undefined;
}
},
inputBounds: {
type: Object,
default() {
return undefined;
}
}
},
data() {
@@ -104,12 +98,6 @@ export default {
watch: {
keyString() {
this.setTimeContext();
},
inputBounds: {
handler(newBounds) {
this.handleNewBounds(newBounds);
},
deep: true
}
},
mounted() {
@@ -265,6 +253,8 @@ export default {
input.title = '';
}
this.$refs.fixedDeltaInput.reportValidity();
return validationResult.valid;
},
startDateSelected(date) {

View File

@@ -21,7 +21,7 @@
ref="startOffset"
class="c-button c-conductor__delta-button"
title="Set the time offset after now"
@click.prevent.stop="showTimePopupStart"
@click.prevent="showTimePopupStart"
>
{{ offsets.start }}
</button>
@@ -60,7 +60,7 @@
ref="endOffset"
class="c-button c-conductor__delta-button"
title="Set the time offset preceding now"
@click.prevent.stop="showTimePopupEnd"
@click.prevent="showTimePopupEnd"
>
{{ offsets.end }}
</button>
@@ -86,12 +86,6 @@ export default {
default() {
return undefined;
}
},
inputBounds: {
type: Object,
default() {
return undefined;
}
}
},
data() {
@@ -124,12 +118,6 @@ export default {
watch: {
keyString() {
this.setTimeContext();
},
inputBounds: {
handler(newBounds) {
this.handleNewBounds(newBounds);
},
deep: true
}
},
mounted() {

View File

@@ -122,9 +122,6 @@
flex: 1 1 auto;
height: 0; // Chrome 73 overflow bug fix
overflow: auto;
//To accommodate independent time conductor controls
display: flex;
flex-direction: column;
.u-fills-container {
// Expand component types that fill a container