Add Visual Test for Snow Theme and add visual tests to PR execution (#5570)
This commit is contained in:
@@ -5,6 +5,7 @@ executors:
|
|||||||
- image: mcr.microsoft.com/playwright:v1.23.0-focal
|
- image: mcr.microsoft.com/playwright:v1.23.0-focal
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: development # Needed to ensure 'dist' folder created and devDependencies installed
|
NODE_ENV: development # Needed to ensure 'dist' folder created and devDependencies installed
|
||||||
|
PERCY_POSTINSTALL_BROWSER: 'true' # Needed to store the percy browser in cache deps
|
||||||
parameters:
|
parameters:
|
||||||
BUST_CACHE:
|
BUST_CACHE:
|
||||||
description: "Set this with the CircleCI UI Trigger Workflow button (boolean = true) to bust the cache!"
|
description: "Set this with the CircleCI UI Trigger Workflow button (boolean = true) to bust the cache!"
|
||||||
@@ -64,7 +65,7 @@ commands:
|
|||||||
suite:
|
suite:
|
||||||
type: string
|
type: string
|
||||||
steps:
|
steps:
|
||||||
- run: npm run cov:e2e:report
|
- run: npm run cov:e2e:report || true
|
||||||
- run: npm run cov:e2e:<<parameters.suite>>:publish
|
- run: npm run cov:e2e:<<parameters.suite>>:publish
|
||||||
orbs:
|
orbs:
|
||||||
node: circleci/node@4.9.0
|
node: circleci/node@4.9.0
|
||||||
@@ -153,6 +154,22 @@ jobs:
|
|||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: html-test-results
|
path: html-test-results
|
||||||
- generate_and_store_version_and_filesystem_artifacts
|
- generate_and_store_version_and_filesystem_artifacts
|
||||||
|
visual-test:
|
||||||
|
parameters:
|
||||||
|
node-version:
|
||||||
|
type: string
|
||||||
|
executor: pw-focal-development
|
||||||
|
steps:
|
||||||
|
- build_and_install:
|
||||||
|
node-version: <<parameters.node-version>>
|
||||||
|
- run: npm run test:e2e:visual
|
||||||
|
- store_test_results:
|
||||||
|
path: test-results/results.xml
|
||||||
|
- store_artifacts:
|
||||||
|
path: test-results
|
||||||
|
- store_artifacts:
|
||||||
|
path: html-test-results
|
||||||
|
- generate_and_store_version_and_filesystem_artifacts
|
||||||
workflows:
|
workflows:
|
||||||
overall-circleci-commit-status: #These jobs run on every commit
|
overall-circleci-commit-status: #These jobs run on every commit
|
||||||
jobs:
|
jobs:
|
||||||
@@ -168,6 +185,9 @@ workflows:
|
|||||||
suite: stable
|
suite: stable
|
||||||
- perf-test:
|
- perf-test:
|
||||||
node-version: lts/gallium
|
node-version: lts/gallium
|
||||||
|
- visual-test:
|
||||||
|
node-version: lts/gallium
|
||||||
|
|
||||||
the-nightly: #These jobs do not run on PRs, but against master at night
|
the-nightly: #These jobs do not run on PRs, but against master at night
|
||||||
jobs:
|
jobs:
|
||||||
- unit-test:
|
- unit-test:
|
||||||
@@ -185,6 +205,10 @@ workflows:
|
|||||||
name: e2e-full-nightly
|
name: e2e-full-nightly
|
||||||
node-version: lts/gallium
|
node-version: lts/gallium
|
||||||
suite: full
|
suite: full
|
||||||
|
- perf-test:
|
||||||
|
node-version: lts/gallium
|
||||||
|
- visual-test:
|
||||||
|
node-version: lts/gallium
|
||||||
triggers:
|
triggers:
|
||||||
- schedule:
|
- schedule:
|
||||||
cron: "0 0 * * *"
|
cron: "0 0 * * *"
|
||||||
|
|||||||
25
.github/workflows/e2e-visual.yml
vendored
25
.github/workflows/e2e-visual.yml
vendored
@@ -1,25 +0,0 @@
|
|||||||
name: "e2e-visual"
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
pull_request:
|
|
||||||
types:
|
|
||||||
- labeled
|
|
||||||
- opened
|
|
||||||
schedule:
|
|
||||||
- cron: '28 21 * * 1-5'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
e2e-visual:
|
|
||||||
if: ${{ github.event.label.name == 'pr:visual' }} || ${{ github.event.workflow_dispatch }} || ${{ github.event.schedule }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: '16'
|
|
||||||
- run: npx playwright@1.23.0 install
|
|
||||||
- run: npm install
|
|
||||||
- name: Run the e2e visual tests
|
|
||||||
run: npm run test:e2e:visual
|
|
||||||
env:
|
|
||||||
PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}
|
|
||||||
@@ -57,7 +57,7 @@ async function createDomainObjectWithDefaults(page, type, name) {
|
|||||||
|
|
||||||
// Click OK button and wait for Navigate event
|
// Click OK button and wait for Navigate event
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
page.waitForNavigation(),
|
page.waitForLoadState(),
|
||||||
page.click('[aria-label="Save"]'),
|
page.click('[aria-label="Save"]'),
|
||||||
// Wait for Save Banner to appear
|
// Wait for Save Banner to appear
|
||||||
page.waitForSelector('.c-message-banner__message')
|
page.waitForSelector('.c-message-banner__message')
|
||||||
|
|||||||
30
e2e/helper/useSnowTheme.js
Normal file
30
e2e/helper/useSnowTheme.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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 should be used to install the Snow theme for Open MCT. Espresso is the default
|
||||||
|
// e.g.
|
||||||
|
// await page.addInitScript({ path: path.join(__dirname, 'useSnowTheme.js') });
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const openmct = window.openmct;
|
||||||
|
openmct.install(openmct.plugins.Snow());
|
||||||
|
});
|
||||||
@@ -32,6 +32,7 @@ const config = {
|
|||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
name: 'chrome',
|
name: 'chrome',
|
||||||
|
testMatch: '**/*.e2e.spec.js', // only run e2e tests
|
||||||
use: {
|
use: {
|
||||||
browserName: 'chromium'
|
browserName: 'chromium'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,13 @@
|
|||||||
// playwright.config.js
|
// playwright.config.js
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
/** @type {import('@playwright/test').PlaywrightTestConfig} */
|
/** @type {import('@playwright/test').PlaywrightTestConfig<{ theme: string }>} */
|
||||||
const config = {
|
const config = {
|
||||||
retries: 0, // visual tests should never retry due to snapshot comparison errors
|
retries: 0, // visual tests should never retry due to snapshot comparison errors
|
||||||
testDir: 'tests/visual',
|
testDir: 'tests/visual',
|
||||||
timeout: 90 * 1000,
|
testMatch: '**/*.visual.spec.js', // only run visual tests
|
||||||
workers: 1, // visual tests should never run in parallel due to test pollution
|
timeout: 60 * 1000,
|
||||||
|
workers: 2, //Limit to 2 for CircleCI Agent
|
||||||
webServer: {
|
webServer: {
|
||||||
command: 'cross-env NODE_ENV=test npm run start',
|
command: 'cross-env NODE_ENV=test npm run start',
|
||||||
url: 'http://localhost:8080/#',
|
url: 'http://localhost:8080/#',
|
||||||
@@ -15,17 +16,35 @@ const config = {
|
|||||||
reuseExistingServer: !process.env.CI
|
reuseExistingServer: !process.env.CI
|
||||||
},
|
},
|
||||||
use: {
|
use: {
|
||||||
browserName: "chromium",
|
|
||||||
baseURL: 'http://localhost:8080/',
|
baseURL: 'http://localhost:8080/',
|
||||||
headless: true, // this needs to remain headless to avoid visual changes due to GPU
|
headless: true, // this needs to remain headless to avoid visual changes due to GPU rendering in headed browsers
|
||||||
ignoreHTTPSErrors: true,
|
ignoreHTTPSErrors: true,
|
||||||
screenshot: 'on',
|
screenshot: 'on',
|
||||||
trace: 'off',
|
trace: 'on',
|
||||||
video: 'off'
|
video: 'off'
|
||||||
},
|
},
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: 'chrome',
|
||||||
|
use: {
|
||||||
|
browserName: 'chromium'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'chrome-snow-theme',
|
||||||
|
use: {
|
||||||
|
browserName: 'chromium',
|
||||||
|
theme: 'snow'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
reporter: [
|
reporter: [
|
||||||
['list'],
|
['list'],
|
||||||
['junit', { outputFile: 'test-results/results.xml' }]
|
['junit', { outputFile: 'test-results/results.xml' }],
|
||||||
|
['html', {
|
||||||
|
open: 'on-failure',
|
||||||
|
outputFolder: '../html-test-results' //Must be in different location due to https://github.com/microsoft/playwright/issues/12840
|
||||||
|
}]
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
const { test, expect } = require('./baseFixtures');
|
const { test, expect } = require('./baseFixtures');
|
||||||
// const { createDomainObjectWithDefaults } = require('./appActions');
|
// const { createDomainObjectWithDefaults } = require('./appActions');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} ObjectCreateOptions
|
* @typedef {Object} ObjectCreateOptions
|
||||||
@@ -95,6 +96,23 @@ const { test, expect } = require('./baseFixtures');
|
|||||||
*/
|
*/
|
||||||
// const objectCreateOptions = null;
|
// const objectCreateOptions = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default theme for VIPER and Open MCT is the 'espresso' theme. Overriding this value with 'snow' in our playwright config.js
|
||||||
|
* will override the default theme by injecting the 'snow' theme on launch.
|
||||||
|
*
|
||||||
|
* ### Example:
|
||||||
|
* ```js
|
||||||
|
* projects: [
|
||||||
|
* {
|
||||||
|
* name: 'chrome-snow-theme',
|
||||||
|
* use: {
|
||||||
|
* browserName: 'chromium',
|
||||||
|
* theme: 'snow'
|
||||||
|
* ```
|
||||||
|
* @type {'snow' | 'espresso'}
|
||||||
|
*/
|
||||||
|
const theme = 'espresso';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of the "My Items" folder in the domain object tree.
|
* The name of the "My Items" folder in the domain object tree.
|
||||||
*
|
*
|
||||||
@@ -105,6 +123,18 @@ const { test, expect } = require('./baseFixtures');
|
|||||||
const myItemsFolderName = "My Items";
|
const myItemsFolderName = "My Items";
|
||||||
|
|
||||||
exports.test = test.extend({
|
exports.test = test.extend({
|
||||||
|
// This should follow in the Project's configuration. Can be set to 'snow' in playwright config.js
|
||||||
|
theme: [theme, { option: true }],
|
||||||
|
// eslint-disable-next-line no-shadow
|
||||||
|
page: async ({ page, theme }, use) => {
|
||||||
|
// eslint-disable-next-line playwright/no-conditional-in-test
|
||||||
|
if (theme === 'snow') {
|
||||||
|
//inject snow theme
|
||||||
|
await page.addInitScript({ path: path.join(__dirname, './helper', './useSnowTheme.js') });
|
||||||
|
}
|
||||||
|
|
||||||
|
await use(page);
|
||||||
|
},
|
||||||
myItemsFolderName: [myItemsFolderName, { option: true }],
|
myItemsFolderName: [myItemsFolderName, { option: true }],
|
||||||
// eslint-disable-next-line no-shadow
|
// eslint-disable-next-line no-shadow
|
||||||
openmctConfig: async ({ myItemsFolderName }, use) => {
|
openmctConfig: async ({ myItemsFolderName }, use) => {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ relates to how we've extended it (i.e. ./e2e/baseFixtures.js) and assumptions ma
|
|||||||
const { test } = require('../../baseFixtures.js');
|
const { test } = require('../../baseFixtures.js');
|
||||||
|
|
||||||
test.describe('baseFixtures tests', () => {
|
test.describe('baseFixtures tests', () => {
|
||||||
test('Verify that tests fail if console.error is thrown @framework', async ({ page }) => {
|
test('Verify that tests fail if console.error is thrown', async ({ page }) => {
|
||||||
test.fail();
|
test.fail();
|
||||||
//Go to baseURL
|
//Go to baseURL
|
||||||
await page.goto('./', { waitUntil: 'networkidle' });
|
await page.goto('./', { waitUntil: 'networkidle' });
|
||||||
@@ -41,7 +41,7 @@ test.describe('baseFixtures tests', () => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
});
|
});
|
||||||
test('Verify that tests pass if console.warn is thrown @framework', async ({ page }) => {
|
test('Verify that tests pass if console.warn is thrown', async ({ page }) => {
|
||||||
//Go to baseURL
|
//Go to baseURL
|
||||||
await page.goto('./', { waitUntil: 'networkidle' });
|
await page.goto('./', { waitUntil: 'networkidle' });
|
||||||
|
|
||||||
|
|||||||
@@ -32,39 +32,31 @@ Note: Larger testsuite sizes are OK due to the setup time associated with these
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
const { test, expect } = require('../../baseFixtures.js');
|
const { test, expect } = require('../../pluginFixtures');
|
||||||
|
const { createDomainObjectWithDefaults } = require('../../appActions');
|
||||||
const percySnapshot = require('@percy/playwright');
|
const percySnapshot = require('@percy/playwright');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const VISUAL_GRACE_PERIOD = 5 * 1000; //Lets the application "simmer" before the snapshot is taken
|
|
||||||
|
|
||||||
const CUSTOM_NAME = 'CUSTOM_NAME';
|
const CUSTOM_NAME = 'CUSTOM_NAME';
|
||||||
|
|
||||||
test.describe('Visual - addInit', () => {
|
test.describe('Visual - addInit', () => {
|
||||||
test.use({
|
test.use({
|
||||||
clockOptions: {
|
clockOptions: {
|
||||||
shouldAdvanceTime: true
|
now: 0, //Set browser clock to UNIX Epoch
|
||||||
|
shouldAdvanceTime: false //Don't advance the clock
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Restricted Notebook is visually correct @addInit', async ({ page }) => {
|
test('Restricted Notebook is visually correct @addInit @unstable', async ({ page, theme }) => {
|
||||||
// eslint-disable-next-line no-undef
|
// eslint-disable-next-line no-undef
|
||||||
await page.addInitScript({ path: path.join(__dirname, '../../helper', './addInitRestrictedNotebook.js') });
|
await page.addInitScript({ path: path.join(__dirname, '../../helper', './addInitRestrictedNotebook.js') });
|
||||||
//Go to baseURL
|
//Go to baseURL
|
||||||
await page.goto('./#/browse/mine?hideTree=true', { waitUntil: 'networkidle' });
|
await page.goto('./#/browse/mine?hideTree=true', { waitUntil: 'networkidle' });
|
||||||
//Click the Create button
|
|
||||||
await page.click('button:has-text("Create")');
|
await createDomainObjectWithDefaults(page, CUSTOM_NAME);
|
||||||
// Click text=CUSTOM_NAME
|
|
||||||
await page.click(`text=${CUSTOM_NAME}`);
|
|
||||||
// Click text=OK
|
|
||||||
await Promise.all([
|
|
||||||
page.waitForNavigation({waitUntil: 'networkidle'}),
|
|
||||||
page.click('text=OK')
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Take a snapshot of the newly created CUSTOM_NAME notebook
|
// Take a snapshot of the newly created CUSTOM_NAME notebook
|
||||||
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
|
await percySnapshot(page, `Restricted Notebook with CUSTOM_NAME (theme: '${theme}')`);
|
||||||
await percySnapshot(page, 'Restricted Notebook with CUSTOM_NAME');
|
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,27 +25,21 @@ Collection of Visual Tests set to run in a default context. The tests within thi
|
|||||||
are only meant to run against openmct's app.js started by `npm run start` within the
|
are only meant to run against openmct's app.js started by `npm run start` within the
|
||||||
`./e2e/playwright-visual.config.js` file.
|
`./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('../../baseFixtures.js');
|
const { test, expect } = require('../../pluginFixtures');
|
||||||
const percySnapshot = require('@percy/playwright');
|
const percySnapshot = require('@percy/playwright');
|
||||||
|
|
||||||
test.describe('Visual - Controlled Clock', () => {
|
test.describe('Visual - Controlled Clock @localStorage', () => {
|
||||||
test.use({
|
test.use({
|
||||||
storageState: './e2e/test-data/VisualTestData_storage.json',
|
storageState: './e2e/test-data/VisualTestData_storage.json',
|
||||||
clockOptions: {
|
clockOptions: {
|
||||||
now: 0, //Set browser clock to UNIX Epoch
|
now: 0, //Set browser clock to UNIX Epoch
|
||||||
shouldAdvanceTime: false, //Don't advance the clock
|
shouldAdvanceTime: false //Don't advance the clock
|
||||||
toFake: ["setTimeout", "nextTick"]
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Overlay Plot Loading Indicator @localstorage', async ({ page }) => {
|
test('Overlay Plot Loading Indicator @localStorage', async ({ page, theme }) => {
|
||||||
// Go to baseURL
|
// Go to baseURL
|
||||||
await page.goto('./#/browse/mine?hideTree=true', { waitUntil: 'networkidle' });
|
await page.goto('./#/browse/mine?hideTree=true', { waitUntil: 'networkidle' });
|
||||||
|
|
||||||
@@ -57,6 +51,6 @@ test.describe('Visual - Controlled Clock', () => {
|
|||||||
await page.locator('canvas >> nth=1').hover({trial: true});
|
await page.locator('canvas >> nth=1').hover({trial: true});
|
||||||
|
|
||||||
//Take snapshot of Sine Wave Generator within Overlay Plot
|
//Take snapshot of Sine Wave Generator within Overlay Plot
|
||||||
await percySnapshot(page, 'SineWaveInOverlayPlot');
|
await percySnapshot(page, `SineWaveInOverlayPlot (theme: '${theme}')`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -32,85 +32,62 @@ to "fail" on assertions. Instead, they should be used to detect changes between
|
|||||||
Note: Larger testsuite sizes are OK due to the setup time associated with these tests.
|
Note: Larger testsuite sizes are OK due to the setup time associated with these tests.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { test, expect } = require('../../baseFixtures.js');
|
const { test, expect } = require('../../pluginFixtures');
|
||||||
const percySnapshot = require('@percy/playwright');
|
const percySnapshot = require('@percy/playwright');
|
||||||
const VISUAL_GRACE_PERIOD = 5 * 1000; //Lets the application "simmer" before the snapshot is taken
|
const { createDomainObjectWithDefaults } = require('../../appActions');
|
||||||
|
|
||||||
test.describe('Visual - Default', () => {
|
test.describe('Visual - Default', () => {
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
//Go to baseURL and Hide Tree
|
||||||
|
await page.goto('./#/browse/mine?hideTree=true', { waitUntil: 'networkidle' });
|
||||||
|
});
|
||||||
test.use({
|
test.use({
|
||||||
clockOptions: {
|
clockOptions: {
|
||||||
now: 0,
|
now: 0, //Set browser clock to UNIX Epoch
|
||||||
shouldAdvanceTime: true
|
shouldAdvanceTime: false //Don't advance the clock
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Visual - Root and About', async ({ page }) => {
|
test('Visual - Root and About', async ({ page, theme }) => {
|
||||||
// Go to baseURL
|
|
||||||
await page.goto('./#/browse/mine?hideTree=true', { waitUntil: 'networkidle' });
|
|
||||||
|
|
||||||
// Verify that Create button is actionable
|
// Verify that Create button is actionable
|
||||||
await expect(page.locator('button:has-text("Create")')).toBeEnabled();
|
await expect(page.locator('button:has-text("Create")')).toBeEnabled();
|
||||||
|
|
||||||
// Take a snapshot of the Dashboard
|
// Take a snapshot of the Dashboard
|
||||||
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
|
await percySnapshot(page, `Root (theme: '${theme}')`);
|
||||||
await percySnapshot(page, 'Root');
|
|
||||||
|
|
||||||
// Click About button
|
// Click About button
|
||||||
await page.click('.l-shell__app-logo');
|
await page.click('.l-shell__app-logo');
|
||||||
|
|
||||||
// Modify the Build information in 'about' to be consistent run-over-run
|
// Modify the Build information in 'about' to be consistent run-over-run
|
||||||
const versionInformationLocator = page.locator('ul.t-info.l-info.s-info');
|
const versionInformationLocator = page.locator('ul.t-info.l-info.s-info').first();
|
||||||
await expect(versionInformationLocator).toBeEnabled();
|
await expect(versionInformationLocator).toBeEnabled();
|
||||||
await versionInformationLocator.evaluate(node => node.innerHTML = '<li>Version: visual-snapshot</li> <li>Build Date: Mon Nov 15 2021 08:07:51 GMT-0800 (Pacific Standard Time)</li> <li>Revision: 93049cdbc6c047697ca204893db9603b864b8c9f</li> <li>Branch: master</li>');
|
await versionInformationLocator.evaluate(node => node.innerHTML = '<li>Version: visual-snapshot</li> <li>Build Date: Mon Nov 15 2021 08:07:51 GMT-0800 (Pacific Standard Time)</li> <li>Revision: 93049cdbc6c047697ca204893db9603b864b8c9f</li> <li>Branch: master</li>');
|
||||||
|
|
||||||
// Take a snapshot of the About modal
|
// Take a snapshot of the About modal
|
||||||
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
|
await percySnapshot(page, `About (theme: '${theme}')`);
|
||||||
await percySnapshot(page, 'About');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Visual - Default Condition Set', async ({ page }) => {
|
test('Visual - Default Condition Set', async ({ page, theme }) => {
|
||||||
//Go to baseURL
|
|
||||||
await page.goto('/', { waitUntil: 'networkidle' });
|
|
||||||
|
|
||||||
//Click the Create button
|
await createDomainObjectWithDefaults(page, 'Condition Set');
|
||||||
await page.click('button:has-text("Create")');
|
|
||||||
|
|
||||||
// Click text=Condition Set
|
|
||||||
await page.click('text=Condition Set');
|
|
||||||
|
|
||||||
// Click text=OK
|
|
||||||
await page.click('text=OK');
|
|
||||||
|
|
||||||
// Take a snapshot of the newly created Condition Set object
|
// Take a snapshot of the newly created Condition Set object
|
||||||
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
|
await percySnapshot(page, `Default Condition Set (theme: '${theme}')`);
|
||||||
await percySnapshot(page, 'Default Condition Set');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test.fixme('Visual - Default Condition Widget', async ({ page }) => {
|
test.fixme('Visual - Default Condition Widget', async ({ page, theme }) => {
|
||||||
test.info().annotations.push({
|
test.info().annotations.push({
|
||||||
type: 'issue',
|
type: 'issue',
|
||||||
description: 'https://github.com/nasa/openmct/issues/5349'
|
description: 'https://github.com/nasa/openmct/issues/5349'
|
||||||
});
|
});
|
||||||
//Go to baseURL
|
|
||||||
await page.goto('/', { waitUntil: 'networkidle' });
|
|
||||||
|
|
||||||
//Click the Create button
|
await createDomainObjectWithDefaults(page, 'Condition Widget');
|
||||||
await page.click('button:has-text("Create")');
|
|
||||||
|
|
||||||
// Click text=Condition Widget
|
|
||||||
await page.click('text=Condition Widget');
|
|
||||||
|
|
||||||
// Click text=OK
|
|
||||||
await page.click('text=OK');
|
|
||||||
|
|
||||||
// Take a snapshot of the newly created Condition Widget object
|
// Take a snapshot of the newly created Condition Widget object
|
||||||
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
|
await percySnapshot(page, `Default Condition Widget (theme: '${theme}')`);
|
||||||
await percySnapshot(page, 'Default Condition Widget');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Visual - Time Conductor start time is less than end time', async ({ page }) => {
|
test('Visual - Time Conductor start time is less than end time', async ({ page, theme }) => {
|
||||||
//Go to baseURL
|
|
||||||
await page.goto('/', { waitUntil: 'networkidle' });
|
|
||||||
const year = new Date().getFullYear();
|
const year = new Date().getFullYear();
|
||||||
|
|
||||||
let startDate = 'xxxx-01-01 01:00:00.000Z';
|
let startDate = 'xxxx-01-01 01:00:00.000Z';
|
||||||
@@ -123,16 +100,14 @@ test.describe('Visual - Default', () => {
|
|||||||
await page.locator('input[type="text"]').first().fill(startDate.toString());
|
await page.locator('input[type="text"]').first().fill(startDate.toString());
|
||||||
|
|
||||||
// verify no error msg
|
// verify no error msg
|
||||||
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
|
await percySnapshot(page, `Default Time conductor (theme: '${theme}')`);
|
||||||
await percySnapshot(page, 'Default Time conductor');
|
|
||||||
|
|
||||||
startDate = (year + 1) + startDate.substring(4);
|
startDate = (year + 1) + startDate.substring(4);
|
||||||
await page.locator('input[type="text"]').first().fill(startDate.toString());
|
await page.locator('input[type="text"]').first().fill(startDate.toString());
|
||||||
await page.locator('input[type="text"]').nth(1).click();
|
await page.locator('input[type="text"]').nth(1).click();
|
||||||
|
|
||||||
// verify error msg for start time (unable to capture snapshot of popup)
|
// verify error msg for start time (unable to capture snapshot of popup)
|
||||||
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
|
await percySnapshot(page, `Start time error (theme: '${theme}')`);
|
||||||
await percySnapshot(page, 'Start time error');
|
|
||||||
|
|
||||||
startDate = (year - 1) + startDate.substring(4);
|
startDate = (year - 1) + startDate.substring(4);
|
||||||
await page.locator('input[type="text"]').first().fill(startDate.toString());
|
await page.locator('input[type="text"]').first().fill(startDate.toString());
|
||||||
@@ -143,79 +118,51 @@ test.describe('Visual - Default', () => {
|
|||||||
await page.locator('input[type="text"]').first().click();
|
await page.locator('input[type="text"]').first().click();
|
||||||
|
|
||||||
// verify error msg for end time (unable to capture snapshot of popup)
|
// verify error msg for end time (unable to capture snapshot of popup)
|
||||||
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
|
await percySnapshot(page, `End time error (theme: '${theme}')`);
|
||||||
await percySnapshot(page, 'End time error');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Visual - Sine Wave Generator Form', async ({ page }) => {
|
test('Visual - Sine Wave Generator Form', async ({ page, theme }) => {
|
||||||
//Go to baseURL
|
|
||||||
await page.goto('/', { waitUntil: 'networkidle' });
|
|
||||||
|
|
||||||
//Click the Create button
|
//Click the Create button
|
||||||
await page.click('button:has-text("Create")');
|
await page.click('button:has-text("Create")');
|
||||||
|
|
||||||
// Click text=Sine Wave Generator
|
// Click text=Sine Wave Generator
|
||||||
await page.click('text=Sine Wave Generator');
|
await page.click('text=Sine Wave Generator');
|
||||||
|
|
||||||
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
|
await percySnapshot(page, `Default Sine Wave Generator Form (theme: '${theme}')`);
|
||||||
await percySnapshot(page, 'Default Sine Wave Generator Form');
|
|
||||||
|
|
||||||
await page.locator('.field.control.l-input-sm input').first().click();
|
await page.locator('.field.control.l-input-sm input').first().click();
|
||||||
await page.locator('.field.control.l-input-sm input').first().fill('');
|
await page.locator('.field.control.l-input-sm input').first().fill('');
|
||||||
|
|
||||||
// Validate red x mark
|
// Validate red x mark
|
||||||
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
|
await percySnapshot(page, `removed amplitude property value (theme: '${theme}')`);
|
||||||
await percySnapshot(page, 'removed amplitude property value');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Visual - Save Successful Banner', async ({ page }) => {
|
test('Visual - Save Successful Banner', async ({ page, theme }) => {
|
||||||
//Go to baseURL
|
await createDomainObjectWithDefaults(page, 'Timer');
|
||||||
await page.goto('/', { waitUntil: 'networkidle' });
|
|
||||||
|
|
||||||
//Click the Create button
|
|
||||||
await page.click('button:has-text("Create")');
|
|
||||||
|
|
||||||
//NOTE Something other than example imagery
|
|
||||||
await page.click('text=Timer');
|
|
||||||
|
|
||||||
// Click text=OK
|
|
||||||
await page.click('text=OK');
|
|
||||||
await page.locator('.c-message-banner__message').hover({ trial: true });
|
await page.locator('.c-message-banner__message').hover({ trial: true });
|
||||||
await percySnapshot(page, 'Banner message shown');
|
await percySnapshot(page, `Banner message shown (theme: '${theme}')`);
|
||||||
|
|
||||||
//Wait until Save Banner is gone
|
//Wait until Save Banner is gone
|
||||||
|
await page.locator('.c-message-banner__close-button').click();
|
||||||
await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
|
await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
|
||||||
await percySnapshot(page, 'Banner message gone');
|
await percySnapshot(page, `Banner message gone (theme: '${theme}')`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Visual - Display Layout Icon is correct', async ({ page }) => {
|
test('Visual - Display Layout Icon is correct', async ({ page, theme }) => {
|
||||||
//Go to baseURL
|
|
||||||
await page.goto('/', { waitUntil: 'networkidle' });
|
|
||||||
|
|
||||||
//Click the Create button
|
//Click the Create button
|
||||||
await page.click('button:has-text("Create")');
|
await page.click('button:has-text("Create")');
|
||||||
|
|
||||||
//Hover on Display Layout option.
|
//Hover on Display Layout option.
|
||||||
await page.locator('text=Display Layout').hover();
|
await page.locator('text=Display Layout').hover();
|
||||||
await percySnapshot(page, 'Display Layout Create Menu');
|
await percySnapshot(page, `Display Layout Create Menu (theme: '${theme}')`);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Visual - Default Gauge is correct', async ({ page }) => {
|
test('Visual - Default Gauge is correct', async ({ page, theme }) => {
|
||||||
|
await createDomainObjectWithDefaults(page, 'Gauge');
|
||||||
//Go to baseURL
|
|
||||||
await page.goto('/', { waitUntil: 'networkidle' });
|
|
||||||
|
|
||||||
//Click the Create button
|
|
||||||
await page.click('button:has-text("Create")');
|
|
||||||
|
|
||||||
await page.click('text=Gauge');
|
|
||||||
|
|
||||||
await page.click('text=OK');
|
|
||||||
|
|
||||||
// Take a snapshot of the newly created Gauge object
|
// Take a snapshot of the newly created Gauge object
|
||||||
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
|
await percySnapshot(page, `Default Gauge (theme: '${theme}')`);
|
||||||
await percySnapshot(page, 'Default Gauge');
|
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,81 +24,58 @@
|
|||||||
This test suite is dedicated to tests which verify search functionality.
|
This test suite is dedicated to tests which verify search functionality.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { test, expect } = require('../../baseFixtures.js');
|
const { test, expect } = require('../../pluginFixtures');
|
||||||
|
const { createDomainObjectWithDefaults } = require('../../appActions');
|
||||||
|
|
||||||
const percySnapshot = require('@percy/playwright');
|
const percySnapshot = require('@percy/playwright');
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a notebook object and adds an entry.
|
|
||||||
* @param {import('@playwright/test').Page} page
|
|
||||||
*/
|
|
||||||
async function createClockAndDisplayLayout(page) {
|
|
||||||
//Go to baseURL
|
|
||||||
await page.goto('./', { waitUntil: 'networkidle' });
|
|
||||||
|
|
||||||
// Click button:has-text("Create")
|
|
||||||
await page.locator('button:has-text("Create")').click();
|
|
||||||
// Click li:has-text("Notebook")
|
|
||||||
await page.locator('li:has-text("Clock")').click();
|
|
||||||
// Click button:has-text("OK")
|
|
||||||
await Promise.all([
|
|
||||||
page.waitForNavigation(),
|
|
||||||
page.locator('button:has-text("OK")').click()
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Click a:has-text("My Items")
|
|
||||||
await Promise.all([
|
|
||||||
page.waitForNavigation(),
|
|
||||||
page.locator('a:has-text("My Items") >> nth=0').click()
|
|
||||||
]);
|
|
||||||
// Click button:has-text("Create")
|
|
||||||
await page.locator('button:has-text("Create")').click();
|
|
||||||
// Click li:has-text("Notebook")
|
|
||||||
await page.locator('li:has-text("Display Layout")').click();
|
|
||||||
// Click button:has-text("OK")
|
|
||||||
await Promise.all([
|
|
||||||
page.waitForNavigation(),
|
|
||||||
page.locator('button:has-text("OK")').click()
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
test.describe('Grand Search', () => {
|
test.describe('Grand Search', () => {
|
||||||
test('Can search for objects, and subsequent search dropdown behaves properly', async ({ page }) => {
|
test.beforeEach(async ({ page, theme }) => {
|
||||||
await createClockAndDisplayLayout(page);
|
//Go to baseURL and Hide Tree
|
||||||
|
await page.goto('./#/browse/mine?hideTree=true', { waitUntil: 'networkidle' });
|
||||||
|
});
|
||||||
|
test.use({
|
||||||
|
clockOptions: {
|
||||||
|
now: 0, //Set browser clock to UNIX Epoch
|
||||||
|
shouldAdvanceTime: false //Don't advance the clock
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//This needs to be rewritten to use a non clock or non display layout object
|
||||||
|
test('Can search for objects, and subsequent search dropdown behaves properly @unstable', async ({ page, theme }) => {
|
||||||
|
// await createDomainObjectWithDefaults(page, 'Display Layout');
|
||||||
|
// await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
|
||||||
|
// await page.locator('text=Save and Finish Editing').click();
|
||||||
|
const folder1 = 'Folder1';
|
||||||
|
await createDomainObjectWithDefaults(page, 'Folder', folder1);
|
||||||
|
|
||||||
// Click [aria-label="OpenMCT Search"] input[type="search"]
|
// Click [aria-label="OpenMCT Search"] input[type="search"]
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
|
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
|
||||||
// Fill [aria-label="OpenMCT Search"] input[type="search"]
|
// Fill [aria-label="OpenMCT Search"] input[type="search"]
|
||||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Cl');
|
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill(folder1);
|
||||||
await expect(page.locator('[aria-label="Search Result"]')).toContainText('Clock');
|
await expect(page.locator('[aria-label="Search Result"]')).toContainText(folder1);
|
||||||
await percySnapshot(page, 'Searching for Clocks');
|
await percySnapshot(page, 'Searching for Folder Object');
|
||||||
// Click text=Elements >> nth=0
|
|
||||||
await page.locator('text=Elements').first().click();
|
|
||||||
await expect(page.locator('[aria-label="Search Result"]')).not.toBeVisible();
|
|
||||||
|
|
||||||
// Click [aria-label="OpenMCT Search"] [aria-label="Search Input"]
|
|
||||||
await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').click();
|
await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').click();
|
||||||
// Click [aria-label="Unnamed Clock clock result"] >> text=Unnamed Clock
|
|
||||||
await page.locator('[aria-label="Unnamed Clock clock result"] >> text=Unnamed Clock').click();
|
await page.locator('[aria-label="Unnamed Clock clock result"] >> text=Unnamed Clock').click();
|
||||||
await percySnapshot(page, 'Preview for clock should display when editing enabled and search item clicked');
|
await percySnapshot(page, 'Preview for clock should display when editing enabled and search item clicked');
|
||||||
|
|
||||||
// Click [aria-label="Close"]
|
|
||||||
await page.locator('[aria-label="Close"]').click();
|
await page.locator('[aria-label="Close"]').click();
|
||||||
await percySnapshot(page, 'Search should still be showing after preview closed');
|
await percySnapshot(page, 'Search should still be showing after preview closed');
|
||||||
|
|
||||||
// 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();
|
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();
|
await page.locator('text=Save and Finish Editing').click();
|
||||||
// Click [aria-label="OpenMCT Search"] [aria-label="Search Input"]
|
|
||||||
await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').click();
|
await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').click();
|
||||||
// Fill [aria-label="OpenMCT Search"] [aria-label="Search Input"]
|
|
||||||
await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').fill('Cl');
|
await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').fill('Cl');
|
||||||
// Click text=Unnamed Clock
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
page.waitForNavigation(),
|
page.waitForNavigation(),
|
||||||
page.locator('text=Unnamed Clock').click()
|
page.locator('text=Unnamed Clock').click()
|
||||||
]);
|
]);
|
||||||
await percySnapshot(page, 'Clicking on search results should navigate to them if not editing');
|
await percySnapshot(page, `Clicking on search results should navigate to them if not editing (theme: '${theme}')`);
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/eslint-parser": "7.18.2",
|
"@babel/eslint-parser": "7.18.2",
|
||||||
"@braintree/sanitize-url": "6.0.0",
|
"@braintree/sanitize-url": "6.0.0",
|
||||||
"@percy/cli": "1.2.1",
|
"@percy/cli": "1.6.4",
|
||||||
"@percy/playwright": "1.0.4",
|
"@percy/playwright": "1.0.4",
|
||||||
"@playwright/test": "1.23.0",
|
"@playwright/test": "1.23.0",
|
||||||
"@types/eventemitter3": "^1.0.0",
|
"@types/eventemitter3": "^1.0.0",
|
||||||
@@ -94,7 +94,7 @@
|
|||||||
"test:e2e:unstable": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep @unstable",
|
"test:e2e:unstable": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep @unstable",
|
||||||
"test:e2e:local": "npx playwright test --config=e2e/playwright-local.config.js --project=chrome",
|
"test:e2e:local": "npx playwright test --config=e2e/playwright-local.config.js --project=chrome",
|
||||||
"test:e2e:updatesnapshots": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep @snapshot --update-snapshots",
|
"test:e2e:updatesnapshots": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep @snapshot --update-snapshots",
|
||||||
"test:e2e:visual": "percy exec --config ./e2e/.percy.yml -- npx playwright test --config=e2e/playwright-visual.config.js",
|
"test:e2e:visual": "percy exec --config ./e2e/.percy.yml -- npx playwright test --config=e2e/playwright-visual.config.js --grep-invert @unstable",
|
||||||
"test:e2e:full": "npx playwright test --config=e2e/playwright-ci.config.js",
|
"test:e2e:full": "npx playwright test --config=e2e/playwright-ci.config.js",
|
||||||
"test:perf": "npx playwright test --config=e2e/playwright-performance.config.js",
|
"test:perf": "npx playwright test --config=e2e/playwright-performance.config.js",
|
||||||
"test:watch": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --no-single-run",
|
"test:watch": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --no-single-run",
|
||||||
|
|||||||
Reference in New Issue
Block a user