Compare commits

...

6 Commits

7 changed files with 247 additions and 15 deletions

1
e2e/localstorage.json Normal file
View File

@ -0,0 +1 @@
{"tcHistory":"{\"utc\":[{\"start\":1640401741152,\"end\":1640403541152}]}","mct":"{\"mine\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"fbb74cd3-bc17-4913-a47b-6ec7b620c26d\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"persisted\":1640403542253,\"modified\":1640403542253},\"fbb74cd3-bc17-4913-a47b-6ec7b620c26d\":{\"identifier\":{\"key\":\"fbb74cd3-bc17-4913-a47b-6ec7b620c26d\",\"namespace\":\"\"},\"name\":\"All DomainObjects\",\"type\":\"folder\",\"composition\":[{\"key\":\"cbee96e6-ec97-4059-a5bb-5a81b834ff3d\",\"namespace\":\"\"},{\"key\":\"15631105-127c-44a0-8d54-e3800943a98b\",\"namespace\":\"\"},{\"key\":\"31c2c6e1-55bd-4339-86f9-8cb1693566d0\",\"namespace\":\"\"}],\"modified\":1640403603547,\"location\":\"mine\",\"persisted\":1640403603547},\"cbee96e6-ec97-4059-a5bb-5a81b834ff3d\":{\"identifier\":{\"key\":\"cbee96e6-ec97-4059-a5bb-5a81b834ff3d\",\"namespace\":\"\"},\"name\":\"Unnamed Timer\",\"type\":\"timer\",\"configuration\":{\"timerFormat\":\"long\",\"timezone\":\"UTC\",\"timerState\":\"stopped\"},\"modified\":1640403543115,\"location\":\"fbb74cd3-bc17-4913-a47b-6ec7b620c26d\",\"persisted\":1640403543115},\"15631105-127c-44a0-8d54-e3800943a98b\":{\"identifier\":{\"key\":\"15631105-127c-44a0-8d54-e3800943a98b\",\"namespace\":\"\"},\"name\":\"Notebook\",\"type\":\"notebook\",\"configuration\":{\"defaultSort\":\"oldest\",\"entries\":{},\"imageMigrationVer\":\"v1\",\"pageTitle\":\"Page\",\"sections\":[{\"id\":\"ef4092ba-b6d1-4275-a659-bfae80b6ec9a\",\"isDefault\":false,\"isSelected\":true,\"name\":\"Unnamed Section\",\"pages\":[{\"id\":\"b187e90c-4759-47d5-af16-4a347520c0a6\",\"isDefault\":false,\"isSelected\":true,\"name\":\"Unnamed Page\",\"pageTitle\":\"Page\"}],\"sectionTitle\":\"Section\"}],\"sectionTitle\":\"Section\",\"type\":\"General\"},\"modified\":1640403544367,\"location\":\"fbb74cd3-bc17-4913-a47b-6ec7b620c26d\",\"persisted\":1640403544368},\"31c2c6e1-55bd-4339-86f9-8cb1693566d0\":{\"name\":\"Mega Display Layout\",\"type\":\"layout\",\"identifier\":{\"key\":\"31c2c6e1-55bd-4339-86f9-8cb1693566d0\",\"namespace\":\"\"},\"composition\":[],\"configuration\":{\"items\":[],\"layoutGrid\":[10,10]},\"modified\":1640403603545,\"location\":\"fbb74cd3-bc17-4913-a47b-6ec7b620c26d\",\"persisted\":1640403603545}}","mct-tree-expanded":"[]"}

View File

@ -5,7 +5,7 @@
/** @type {import('@playwright/test').PlaywrightTestConfig} */
const config = {
retries: 0,
testDir: 'tests',
testDir: 'tests/visual',
timeout: 90 * 1000,
workers: 1,
webServer: {
@ -26,7 +26,6 @@ const config = {
reporter: [
['list'],
['junit', { outputFile: 'test-results/results.xml' }],
['allure-playwright']
]
};

View File

@ -0,0 +1,122 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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 used for generating localStorage artifacts for visual and performance
tests. This must be generated against app.js
*/
const { test, expect } = require('@playwright/test');
const fs = require('fs');
test('Generate domainObjects and store localstorage as localstorage.json', async ({ page }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
//Click the Create button
await page.click('button:has-text("Create")');
// Click :nth-match(:text("Folder"), 2)
await page.click(':nth-match(:text("Folder"), 2)');
// Fill text=Properties Title Notes >> input[type="text"]
await page.fill('text=Properties Title Notes >> input[type="text"]', 'All DomainObjects');
//await page.fill('input[type="text"]', 'All DomainObjects');
// Click text=OK
await Promise.all([
page.waitForNavigation(/*{ url: 'http://localhost:8080/#/browse/mine/07fa1bd8-1e7d-4b74-bc40-f561f8c00535?tc.mode=fixed&tc.startBound=1640392618958&tc.endBound=1640394418958&tc.timeSystem=utc&view=grid' }*/),
page.click('text=OK')
]);
// Click button:has-text("Create")
await page.click('button:has-text("Create")');
// Click text=Timer
await page.click('text=Timer');
// Click text=Save In My Items All DomainObjects >> input[type="search"]
await page.click('text=Save In My Items All DomainObjects >> input[type="search"]');
// Fill text=Save In My Items All DomainObjects >> input[type="search"]
await page.fill('text=Save In My Items All DomainObjects >> input[type="search"]', 'All DomainObjects');
// Click text=OK
await Promise.all([
page.waitForNavigation(/*{ url: 'http://localhost:8080/#/browse/mine/07fa1bd8-1e7d-4b74-bc40-f561f8c00535/810cc308-76d6-4e64-ab85-cb543c655698?tc.mode=fixed&tc.startBound=1640392618958&tc.endBound=1640394418958&tc.timeSystem=utc&view=timer.view' }*/),
page.click('text=OK')
]);
// Click button:has-text("Create")
await page.click('button:has-text("Create")');
// Click text=Notebook
await page.click('text=Notebook');
// Fill text=Properties Title Notes Entry Sorting Newest First Oldest First Note book Type Se >> input[type="text"]
await page.fill('text=Properties Title Notes Entry Sorting Newest First Oldest First Note book Type Se >> input[type="text"]', 'Notebook');
// Click text=Save In My Items All DomainObjects Unnamed Timer >> input[type="search"]
//await page.click('text=Save In My Items All DomainObjects Unnamed Timer >> input[type="search"]');
// Fill text=Save In My Items All DomainObjects Unnamed Timer >> input[type="search"]
await page.fill('text=Save In My Items All DomainObjects Unnamed Timer >> input[type="search"]', 'All DomainObjects');
// Click ul:nth-child(3) .c-tree__item-h .c-tree__item
await page.click('ul:nth-child(3) .c-tree__item-h .c-tree__item');
// Click button:has-text("OK")
await Promise.all([
page.waitForNavigation(/*{ url: 'http://localhost:8080/#/browse/ROOT/mine/07fa1bd8-1e7d-4b74-bc40-f561f8c00535/6b10425e-2d7f-4f65-83de-35317fbc2493?tc.mode=fixed&tc.startBound=1640392618958&tc.endBound=1640394418958&tc.timeSystem=utc&view=notebook-vue&pageId=9ad5b4ea-8dec-4cc0-b303-43a4f50ac7fa&sectionId=d298175b-d715-426e-8036-b87056e14525' }*/),
page.click('button:has-text("OK")')
]);
await page.pause();
const localStorage = await page.evaluate(() => JSON.stringify(window.localStorage));
fs.writeFileSync('e2e/localstorage.json', localStorage);
});
test('Load localstorage.json and verify contents', async ({ page }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
const localStorage = fs.readFileSync('e2e/localstorage.json', 'utf8')
const deserializedStorage = JSON.parse(localStorage)
await page.evaluate(deserializedStorage => {
for (const key in deserializedStorage) {
localStorage.setItem(key, deserializedStorage[key]);
}
}, deserializedStorage);
await page.reload();
// Expand Default Folder
await page.click('a:has-text("All DomainObjects Folder")');
await expect(page.locator('a:has-text("Unnamed Timer Timer")')).toBeEnabled();
await expect(page.locator('a:has-text("Notebook Notebook")')).toBeEnabled();
});

View File

@ -44,6 +44,5 @@ test('Verify that the create button appears and that the Folder Domain Object is
await page.click('button:has-text("Create")');
// Verify that Create Folder appears in the dropdown
const locator = page.locator(':nth-match(:text("Folder"), 2)');
await expect(locator).toBeEnabled();
await expect(page.locator(':nth-match(:text("Folder"), 2)')).toBeEnabled();
});

View File

@ -36,12 +36,13 @@ const { test, expect } = require('@playwright/test');
const percySnapshot = require('@percy/playwright');
const path = require('path');
const sinon = require('sinon');
const fs = require('fs');
const VISUAL_GRACE_PERIOD = 5 * 1000; //Lets the application "simmer" before the snapshot is taken
const VISUAL_GRACE_PERIOD = 1 * 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 }) => {
test.beforeEach(async ({ context,page }) => {
await context.addInitScript({
// eslint-disable-next-line no-undef
path: path.join(__dirname, '../../..', './node_modules/sinon/pkg/sinon.js')
@ -49,11 +50,10 @@ test.beforeEach(async ({ context }) => {
await context.addInitScript(() => {
window.__clock = sinon.useFakeTimers(); //Set browser clock to UNIX Epoch
});
await page.goto('/', { waitUntil: 'networkidle' });
});
test('Visual - Root and About', async ({ page }) => {
// Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
// Verify that Create button is actionable
const createButtonLocator = page.locator('button:has-text("Create")');
@ -77,8 +77,6 @@ test('Visual - Root and About', async ({ page }) => {
});
test('Visual - Default Condition Set', async ({ page }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
//Click the Create button
await page.click('button:has-text("Create")');
@ -95,8 +93,6 @@ test('Visual - Default Condition Set', async ({ page }) => {
});
test('Visual - Default Condition Widget', async ({ page }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
//Click the Create button
await page.click('button:has-text("Create")');
@ -111,3 +107,35 @@ test('Visual - Default Condition Widget', async ({ page }) => {
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
await percySnapshot(page, 'Default Condition Widget');
});
test('Visual - Default Timer', async ({ page }) => {
//Click the Create button
await page.click('button:has-text("Create")');
// Click text=Timer
await page.click('text=Timer');
// Click text=OK
await page.click('text=OK');
// Take a snapshot of the newly created Condition Widget object
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
await percySnapshot(page, 'Default Timer');
});
test('Visual - Default Display Layout', async ({ page }) => {
//Click the Create button
await page.click('button:has-text("Create")');
// Click text=Timer
await page.click('text=Display Layout');
// Click text=OK
await page.click('text=OK');
// Take a snapshot of the newly created Condition Widget object
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
await percySnapshot(page, 'Default Display Layout');
});

View File

@ -0,0 +1,82 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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 from a pre-generated . 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 fs = require('fs');
const VISUAL_GRACE_PERIOD = 1 * 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,page }) => {
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(); //Set browser clock to UNIX Epoch
});
await page.goto('/', { waitUntil: 'networkidle' });
const localStorage = fs.readFileSync('e2e/localstorage.json', 'utf8')
const deserializedStorage = JSON.parse(localStorage)
await page.evaluate(deserializedStorage => {
for (const key in deserializedStorage) {
localStorage.setItem(key, deserializedStorage[key]);
}
}, deserializedStorage);
await page.reload();
});
test('Visual - Combined Display Layout', async ({ page }) => {
//Click the Create button
await page.click('button:has-text("Create")');
// Click text=Timer
await page.click('text=Display Layout');
// Click text=OK
await page.click('text=OK');
// Take a snapshot of the newly created Condition Widget object
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
await percySnapshot(page, 'Default Display Layout');
});

View File

@ -6,7 +6,7 @@
"@braintree/sanitize-url": "^5.0.2",
"@percy/cli": "^1.0.0-beta.70",
"@percy/playwright": "^1.0.1",
"@playwright/test": "^1.16.3",
"@playwright/test": "^1.17.1",
"allure-playwright": "^2.0.0-beta.14",
"angular": ">=1.8.0",
"angular-route": "1.4.14",
@ -58,7 +58,7 @@
"moment-timezone": "0.5.28",
"node-bourbon": "^4.2.3",
"painterro": "^1.2.56",
"playwright": "^1.16.3",
"playwright": "^1.17.1",
"plotly.js-basic-dist": "^2.5.0",
"plotly.js-gl2d-dist": "^2.5.0",
"printj": "^1.2.1",
@ -97,8 +97,9 @@
"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 smoke",
"test:e2e:generate": "npx playwright test --config=e2e/playwright-local.config.js generateLocalStorage",
"test:e2e:local": "npx playwright test --config=e2e/playwright-local.config.js",
"test:e2e:visual": "percy exec -- npx playwright test --config=e2e/playwright-visual.config.js default",
"test:e2e:visual": "percy exec -- npx playwright test --config=e2e/playwright-visual.config.js",
"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",
"verify": "concurrently 'npm:test' 'npm:lint'",