From 34ecc082385cdf9a787b77002ea169f41d086a1f Mon Sep 17 00:00:00 2001 From: John Hill Date: Tue, 5 Jul 2022 17:12:45 -0700 Subject: [PATCH] Backmerge e2e code coverage changes and fixes into release/2.0.5 (#5431) --- .circleci/config.yml | 44 +- .gitignore | 22 +- app.js | 2 +- codecov.yml | 17 +- e2e/fixtures.js | 32 +- e2e/playwright-ci.config.js | 29 +- e2e/playwright-local.config.js | 49 ++- e2e/playwright-performance.config.js | 4 +- e2e/playwright-visual.config.js | 2 +- e2e/tests/branding.e2e.spec.js | 1 + .../SinewaveLimitProvider.e2e.spec.js | 82 +--- .../plugins/condition/condition.e2e.spec.js | 17 +- .../imagery/exampleImagery.e2e.spec.js | 406 +++++++++++++++--- .../notebook/restrictedNotebook.e2e.spec.js | 212 +++++---- e2e/tests/plugins/plot/autoscale.e2e.spec.js | 43 +- .../autoscale-canvas-panned-chrome-darwin | Bin 0 -> 16116 bytes .../autoscale-canvas-panned-chrome-linux | Bin 0 -> 15770 bytes .../autoscale-canvas-panned-chrome.png | Bin 18379 -> 0 bytes .../autoscale-canvas-prepan-chrome-darwin | Bin 0 -> 18406 bytes .../autoscale-canvas-prepan-chrome-linux | Bin 0 -> 18071 bytes .../autoscale-canvas-prepan-chrome.png | Bin 19897 -> 0 bytes e2e/tests/plugins/plot/logPlot.e2e.spec.js | 30 +- e2e/tests/recycled_storage.json | 14 +- e2e/tests/smoke.e2e.spec.js | 13 +- e2e/tests/visual/default.visual.spec.js | 9 +- karma.conf.js | 9 +- package.json | 13 +- webpack.common.js | 9 +- webpack.coverage.js | 1 + 29 files changed, 700 insertions(+), 360 deletions(-) create mode 100644 e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-darwin create mode 100644 e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-linux delete mode 100644 e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome.png create mode 100644 e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-darwin create mode 100644 e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-linux delete mode 100644 e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome.png diff --git a/.circleci/config.yml b/.circleci/config.yml index 8863b9e057..6814113ac1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 executors: pw-focal-development: docker: - - image: mcr.microsoft.com/playwright:v1.21.1-focal + - image: mcr.microsoft.com/playwright:v1.23.0-focal environment: NODE_ENV: development # Needed to ensure 'dist' folder created and devDependencies installed parameters: @@ -12,7 +12,7 @@ parameters: type: boolean commands: build_and_install: - description: "All steps used to build and install. Will not work on node10" + description: "All steps used to build and install. Will use cache if found" parameters: node-version: type: string @@ -58,10 +58,14 @@ commands: ls -latR >> /tmp/artifacts/dir.txt - store_artifacts: path: /tmp/artifacts/ - upload_code_covio: - description: "Command to upload code coverage reports to codecov.io" - steps: - - run: curl -Os https://uploader.codecov.io/latest/linux/codecov;chmod +x codecov;./codecov + generate_e2e_code_cov_report: + description: "Generate e2e code coverage artifacts and publish to codecov.io. Needed to that we can ignore the exit code status of the npm run test" + parameters: + suite: + type: string + steps: + - run: npm run cov:e2e:report + - run: npm run cov:e2e:<>:publish orbs: node: circleci/node@4.9.0 browser-tools: circleci/browser-tools@1.3.0 @@ -114,12 +118,13 @@ jobs: - browser-tools/install-chrome: replace-existing: false - run: npm run test -- --browsers=<> + - run: npm run cov:unit:publish - save_cache_cmd: node-version: <> - store_test_results: path: dist/reports/tests/ - store_artifacts: - path: dist/reports/ + path: coverage - generate_and_store_version_and_filesystem_artifacts e2e-test: parameters: @@ -132,11 +137,22 @@ jobs: steps: - build_and_install: node-version: <> + - when: #Only install chrome-beta when running the full suite to save $$$ + condition: + equal: [ "full", <> ] + steps: + - run: npx playwright install chrome-beta - run: SHARD="$((${CIRCLE_NODE_INDEX}+1))"; npm run test:e2e:<> -- --shard=${SHARD}/${CIRCLE_NODE_TOTAL} + - generate_e2e_code_cov_report: + suite: <> - store_test_results: path: test-results/results.xml - store_artifacts: path: test-results + - store_artifacts: + path: coverage + - store_artifacts: + path: html-test-results - generate_and_store_version_and_filesystem_artifacts perf-test: parameters: @@ -151,19 +167,19 @@ jobs: path: test-results/results.xml - store_artifacts: path: test-results - - generate_and_store_version_and_filesystem_artifacts + - store_artifacts: + path: html-test-results + - generate_and_store_version_and_filesystem_artifacts workflows: overall-circleci-commit-status: #These jobs run on every commit jobs: - lint: - name: node16-lint - node-version: lts/gallium - - unit-test: - name: node14-chrome + name: node14-lint node-version: lts/fermium + - unit-test: + name: node16-chrome + node-version: lts/gallium browser: ChromeHeadless - post-steps: - - upload_code_covio - unit-test: name: node18-chrome node-version: "18" diff --git a/.gitignore b/.gitignore index 7c608343c6..11481c4c40 100644 --- a/.gitignore +++ b/.gitignore @@ -15,8 +15,6 @@ *.idea *.iml -# External dependencies - # Build output target dist @@ -24,30 +22,24 @@ dist # Mac OS X Finder .DS_Store -# Closed source libraries -closed-lib - # Node, Bower dependencies node_modules bower_components -# Protractor logs -protractor/logs - # npm-debug log npm-debug.log # karma reports report.*.json -# Lighthouse reports -.lighthouseci - # e2e test artifacts test-results -allure-results +html-test-results -package-lock.json - -#codecov artifacts +# codecov artifacts +.nyc_output +coverage codecov + +# :( +package-lock.json diff --git a/app.js b/app.js index b1ddaada9f..a1a30ef839 100644 --- a/app.js +++ b/app.js @@ -49,7 +49,7 @@ class WatchRunPlugin { } const webpack = require('webpack'); -const webpackConfig = require('./webpack.dev.js'); +const webpackConfig = process.env.CI ? require('./webpack.coverage.js') : require('./webpack.dev.js'); webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin()); webpackConfig.plugins.push(new WatchRunPlugin()); diff --git a/codecov.yml b/codecov.yml index 30b299937a..d69c35bbdb 100644 --- a/codecov.yml +++ b/codecov.yml @@ -13,17 +13,16 @@ coverage: round: down range: "66...100" -ignore: - -parsers: - gcov: - branch_detection: - conditional: true - loop: true - method: false - macro: false +flags: + unit: + carryforward: true + e2e-ci: + carryforward: true + e2e-full: + carryforward: true comment: layout: "reach,diff,flags,files,footer" behavior: default require_changes: false + show_carryforward_flags: true \ No newline at end of file diff --git a/e2e/fixtures.js b/e2e/fixtures.js index d602857d1f..ce46de5bbf 100644 --- a/e2e/fixtures.js +++ b/e2e/fixtures.js @@ -1,8 +1,13 @@ -/* eslint-disable no-undef */ +/* This file extends the base functionality of the playwright test framework to enable + * code coverage instrumentation, console log error detection and working with a 3rd + * party Chrome-as-a-service extension called Browserless. + */ -// This file extends the base functionality of the playwright test framework const base = require('@playwright/test'); const { expect } = require('@playwright/test'); +const fs = require('fs'); +const path = require('path'); +const { v4: uuid } = require('uuid'); /** * Takes a `ConsoleMessage` and returns a formatted string @@ -16,7 +21,30 @@ function consoleMessageToString(msg) { at (${url} ${lineNumber}:${columnNumber})`; } +//The following is based on https://github.com/mxschmitt/playwright-test-coverage +// eslint-disable-next-line no-undef +const istanbulCLIOutput = path.join(process.cwd(), '.nyc_output'); + +// eslint-disable-next-line no-undef exports.test = base.test.extend({ + //The following is based on https://github.com/mxschmitt/playwright-test-coverage + context: async ({ context }, use) => { + await context.addInitScript(() => + window.addEventListener('beforeunload', () => + (window).collectIstanbulCoverage(JSON.stringify((window).__coverage__)) + ) + ); + await fs.promises.mkdir(istanbulCLIOutput, { recursive: true }); + await context.exposeFunction('collectIstanbulCoverage', (coverageJSON) => { + if (coverageJSON) { + fs.writeFileSync(path.join(istanbulCLIOutput, `playwright_coverage_${uuid()}.json`), coverageJSON); + } + }); + await use(context); + for (const page of context.pages()) { + await page.evaluate(() => (window).collectIstanbulCoverage(JSON.stringify((window).__coverage__))); + } + }, page: async ({ baseURL, page }, use) => { const messages = []; page.on('console', (msg) => messages.push(msg)); diff --git a/e2e/playwright-ci.config.js b/e2e/playwright-ci.config.js index 0e8f74e8cd..4364d11bd4 100644 --- a/e2e/playwright-ci.config.js +++ b/e2e/playwright-ci.config.js @@ -7,13 +7,13 @@ const { devices } = require('@playwright/test'); /** @type {import('@playwright/test').PlaywrightTestConfig} */ const config = { - retries: 1, + retries: 3, //Retries 3 times for a total of 4. When running sharded and with maxFailures = 5, this should ensure that flake is managed without failing the full suite testDir: 'tests', testIgnore: '**/*.perf.spec.js', //Ignore performance tests and define in playwright-perfromance.config.js timeout: 60 * 1000, webServer: { command: 'npm run start', - port: 8080, + url: 'http://localhost:8080/#', timeout: 200 * 1000, reuseExistingServer: !process.env.CI }, @@ -36,6 +36,7 @@ const config = { }, { name: 'MMOC', + testMatch: '**/*.e2e.spec.js', // only run e2e tests grepInvert: /@snapshot/, use: { browserName: 'chromium', @@ -44,20 +45,30 @@ const config = { height: 1440 } } - } - /*{ - name: 'ipad', + }, + { + name: 'firefox', + testMatch: '**/*.e2e.spec.js', // only run e2e tests + grepInvert: /@snapshot/, use: { - browserName: 'webkit', - ...devices['iPad (gen 7) landscape'] // Complete List https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/server/deviceDescriptorsSource.json + browserName: 'firefox' } - }*/ + }, + { + name: 'chrome-beta', //Only Chrome Beta is available on ubuntu -- not chrome canary + testMatch: '**/*.e2e.spec.js', // only run e2e tests + grepInvert: /@snapshot/, + use: { + browserName: 'chromium', + channel: 'chrome-beta' + } + } ], reporter: [ ['list'], ['html', { open: 'never', - outputFolder: '../test-results/html/' + outputFolder: '../html-test-results' //Must be in different location due to https://github.com/microsoft/playwright/issues/12840 }], ['junit', { outputFile: 'test-results/results.xml' }], ['github'] diff --git a/e2e/playwright-local.config.js b/e2e/playwright-local.config.js index 124b842643..54f59b303e 100644 --- a/e2e/playwright-local.config.js +++ b/e2e/playwright-local.config.js @@ -13,7 +13,7 @@ const config = { timeout: 30 * 1000, webServer: { command: 'npm run start', - port: 8080, + url: 'http://localhost:8080/#', timeout: 120 * 1000, reuseExistingServer: !process.env.CI }, @@ -36,6 +36,7 @@ const config = { }, { name: 'MMOC', + testMatch: '**/*.e2e.spec.js', // only run e2e tests grepInvert: /@snapshot/, use: { browserName: 'chromium', @@ -44,20 +45,58 @@ const config = { height: 1440 } } - } - /*{ + }, + { + name: 'safari', + testMatch: '**/*.e2e.spec.js', // only run e2e tests + grep: /@ipad/, // only run ipad tests due to this bug https://github.com/microsoft/playwright/issues/8340 + grepInvert: /@snapshot/, + use: { + browserName: 'webkit' + } + }, + { + name: 'firefox', + testMatch: '**/*.e2e.spec.js', // only run e2e tests + grepInvert: /@snapshot/, + use: { + browserName: 'firefox' + } + }, + { + name: 'canary', + testMatch: '**/*.e2e.spec.js', // only run e2e tests + grepInvert: /@snapshot/, + use: { + browserName: 'chromium', + channel: 'chrome-canary' //Note this is not available in ubuntu/CircleCI + } + }, + { + name: 'chrome-beta', + testMatch: '**/*.e2e.spec.js', // only run e2e tests + grepInvert: /@snapshot/, + use: { + browserName: 'chromium', + channel: 'chrome-beta' + } + }, + { name: 'ipad', + testMatch: '**/*.e2e.spec.js', // only run e2e tests + grep: /@ipad/, + grepInvert: /@snapshot/, use: { browserName: 'webkit', ...devices['iPad (gen 7) landscape'] // Complete List https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/server/deviceDescriptorsSource.json } - }*/ + } ], reporter: [ ['list'], ['html', { open: 'on-failure', - outputFolder: '../test-results' + outputFolder: '../html-test-results' //Must be in different location due to https://github.com/microsoft/playwright/issues/12840 }] ] }; diff --git a/e2e/playwright-performance.config.js b/e2e/playwright-performance.config.js index 1d3b849ac4..e9b7e38449 100644 --- a/e2e/playwright-performance.config.js +++ b/e2e/playwright-performance.config.js @@ -4,13 +4,13 @@ /** @type {import('@playwright/test').PlaywrightTestConfig} */ const config = { - retries: 1, //Only for debugging purposes + retries: 1, //Only for debugging purposes because trace is enabled only on first retry testDir: 'tests/performance/', timeout: 60 * 1000, workers: 1, //Only run in serial with 1 worker webServer: { command: 'npm run start', - port: 8080, + url: 'http://localhost:8080/#', timeout: 200 * 1000, reuseExistingServer: !process.env.CI }, diff --git a/e2e/playwright-visual.config.js b/e2e/playwright-visual.config.js index fbf2c02011..07758121bd 100644 --- a/e2e/playwright-visual.config.js +++ b/e2e/playwright-visual.config.js @@ -10,7 +10,7 @@ const config = { workers: 1, // visual tests should never run in parallel due to test pollution webServer: { command: 'npm run start', - port: 8080, + url: 'http://localhost:8080/#', timeout: 200 * 1000, reuseExistingServer: !process.env.CI }, diff --git a/e2e/tests/branding.e2e.spec.js b/e2e/tests/branding.e2e.spec.js index dc6b94be28..a37101c8a0 100644 --- a/e2e/tests/branding.e2e.spec.js +++ b/e2e/tests/branding.e2e.spec.js @@ -58,6 +58,7 @@ test.describe('Branding tests', () => { page.waitForEvent('popup'), page.locator('text=click here for third party licensing information').click() ]); + await page2.waitForLoadState('networkidle'); //Avoids timing issues with juggler/firefox expect(page2.waitForURL('**/licenses**')).toBeTruthy(); }); }); diff --git a/e2e/tests/example/generator/SinewaveLimitProvider.e2e.spec.js b/e2e/tests/example/generator/SinewaveLimitProvider.e2e.spec.js index 24aad40a8b..972e410dfc 100644 --- a/e2e/tests/example/generator/SinewaveLimitProvider.e2e.spec.js +++ b/e2e/tests/example/generator/SinewaveLimitProvider.e2e.spec.js @@ -28,7 +28,9 @@ const { test } = require('../../../fixtures.js'); const { expect } = require('@playwright/test'); test.describe('Sine Wave Generator', () => { - test('Create new Sine Wave Generator Object and validate create Form Logic', async ({ page }) => { + test('Create new Sine Wave Generator Object and validate create Form Logic', async ({ page, browserName }) => { + test.fixme(browserName === 'firefox', 'This test needs to be updated to work with firefox'); + //Go to baseURL await page.goto('/', { waitUntil: 'networkidle' }); @@ -40,44 +42,45 @@ test.describe('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']); + await expect(page.locator('.c-form-row__state-indicator').first()).toHaveClass(/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'); + await page.locator('textarea[type="text"]').fill('Optional Note Text'); // 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']); + await expect(page.locator('div:nth-child(4) .c-form-row__state-indicator')).toHaveClass(/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']); + await expect(page.locator('div:nth-child(5) .c-form-row__state-indicator')).toHaveClass(/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']); + await expect(page.locator('div:nth-child(6) .c-form-row__state-indicator')).toHaveClass(/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']); + await expect(page.locator('div:nth-child(7) .c-form-row__state-indicator')).toHaveClass(/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']); + await expect(page.locator('div:nth-child(8) .c-form-row__state-indicator')).toHaveClass(/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']); + await expect(page.locator('div:nth-child(9) .c-form-row__state-indicator')).toHaveClass(/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']); + await expect(page.locator('.c-form-row__state-indicator').first()).toHaveClass(/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']); + await page.locator('text=Properties Title Notes Period Amplitude Offset Data Rate (hz) Phase (radians) Ra >> input[type="text"]').fill('New Sine Wave Generator'); + await expect(page.locator('.c-form-row__state-indicator').first()).toHaveClass(/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']); + await expect(page.locator('div:nth-child(4) .c-form-row__state-indicator')).toHaveClass(/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']); + await expect(page.locator('div:nth-child(4) .c-form-row__state-indicator')).toHaveClass(/valid/); // Verify that can change value of number field by up/down arrows keys // Click .field.control.l-input-sm input >> nth=0 @@ -90,57 +93,6 @@ test.describe('Sine Wave Generator', () => { 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(), @@ -151,7 +103,7 @@ test.describe('Sine Wave Generator', () => { // Verify object properties await expect(page.locator('.l-browse-bar__object-name')).toContainText('New Sine Wave Generator'); - // Verify canvas rendered + // Verify canvas rendered and can be interacted with await page.locator('canvas').nth(1).click({ position: { x: 341, diff --git a/e2e/tests/plugins/condition/condition.e2e.spec.js b/e2e/tests/plugins/condition/condition.e2e.spec.js index c7c2749068..43cd22f591 100644 --- a/e2e/tests/plugins/condition/condition.e2e.spec.js +++ b/e2e/tests/plugins/condition/condition.e2e.spec.js @@ -33,7 +33,7 @@ let conditionSetUrl; let getConditionSetIdentifierFromUrl; test.describe.serial('Condition Set CRUD Operations on @localStorage', () => { - test.beforeAll(async ({ browser }) => { + test.beforeAll(async ({ browser}) => { const context = await browser.newContext(); const page = await context.newPage(); //Go to baseURL @@ -60,6 +60,7 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => { getConditionSetIdentifierFromUrl = await conditionSetUrl.split('/').pop().split('?')[0]; console.debug('getConditionSetIdentifierFromUrl ' + getConditionSetIdentifierFromUrl); + await page.close(); }); test.afterAll(async ({ browser }) => { await browser.close(); @@ -67,11 +68,11 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => { //Load localStorage for subsequent tests test.use({ storageState: './e2e/test-data/recycled_local_storage.json' }); //Begin suite of tests again localStorage - test('Condition set object properties persist in main view and inspector', async ({ page }) => { + test('Condition set object properties persist in main view and inspector @localStorage', async ({ page }) => { //Navigate to baseURL with injected localStorage await page.goto(conditionSetUrl, { waitUntil: 'networkidle' }); - //Assertions on loaded Condition Set in main view + //Assertions on loaded Condition Set in main view. This is a stateful transition step after page.goto() await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set'); //Assertions on loaded Condition Set in Inspector @@ -92,7 +93,7 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => { test('condition set object can be modified on @localStorage', async ({ page }) => { await page.goto(conditionSetUrl, { waitUntil: 'networkidle' }); - //Assertions on loaded Condition Set in main view + //Assertions on loaded Condition Set in main view. This is a stateful transition step after page.goto() await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set'); //Update the Condition Set properties @@ -153,23 +154,25 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => { //Navigate to baseURL await page.goto('/', { waitUntil: 'networkidle' }); - const numberOfConditionSetsToStart = await page.locator('a:has-text("Unnamed Condition Set Condition Set")').count(); - //Expect Unnamed Condition Set to be visible in Main View + //Assertions on loaded Condition Set in main view. This is a stateful transition step after page.goto() await expect(page.locator('a:has-text("Unnamed Condition Set Condition Set") >> nth=0')).toBeVisible(); + const numberOfConditionSetsToStart = await page.locator('a:has-text("Unnamed Condition Set Condition Set")').count(); + // Search for Unnamed Condition Set await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Unnamed Condition Set'); // Click Search Result await page.locator('[aria-label="OpenMCT Search"] >> text=Unnamed Condition Set').first().click(); // Click hamburger button await page.locator('[title="More options"]').click(); + // Click text=Remove await page.locator('text=Remove').click(); - await page.locator('text=OK').click(); //Expect Unnamed Condition Set to be removed in Main View const numberOfConditionSetsAtEnd = await page.locator('a:has-text("Unnamed Condition Set Condition Set")').count(); + expect(numberOfConditionSetsAtEnd).toEqual(numberOfConditionSetsToStart - 1); //Feature? diff --git a/e2e/tests/plugins/imagery/exampleImagery.e2e.spec.js b/e2e/tests/plugins/imagery/exampleImagery.e2e.spec.js index 181477d457..22100fbce4 100644 --- a/e2e/tests/plugins/imagery/exampleImagery.e2e.spec.js +++ b/e2e/tests/plugins/imagery/exampleImagery.e2e.spec.js @@ -29,7 +29,10 @@ but only assume that example imagery is present. const { test } = require('../../../fixtures.js'); const { expect } = require('@playwright/test'); -test.describe('Example Imagery', () => { +const backgroundImageSelector = '.c-imagery__main-image__background-image'; + +//The following block of tests verifies the basic functionality of example imagery and serves as a template for Imagery objects embedded in other objects. +test.describe('Example Imagery Object', () => { test.beforeEach(async ({ page }) => { //Go to baseURL @@ -48,28 +51,30 @@ test.describe('Example Imagery', () => { //Wait for Save Banner to appear page.waitForSelector('.c-message-banner__message') ]); + // Close Banner + await page.locator('.c-message-banner__close-button').click(); + //Wait until Save Banner is gone await page.waitForSelector('.c-message-banner__message', { state: 'detached'}); await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery'); + await page.locator(backgroundImageSelector).hover({trial: true}); }); - const backgroundImageSelector = '.c-imagery__main-image__background-image'; test('Can use Mouse Wheel to zoom in and out of latest image', async ({ page }) => { - const bgImageLocator = page.locator(backgroundImageSelector); const deltaYStep = 100; //equivalent to 1x zoom - await bgImageLocator.hover({trial: true}); + await page.locator(backgroundImageSelector).hover({trial: true}); const originalImageDimensions = await page.locator(backgroundImageSelector).boundingBox(); // zoom in - await bgImageLocator.hover({trial: true}); + await page.locator(backgroundImageSelector).hover({trial: true}); await page.mouse.wheel(0, deltaYStep * 2); // wait for zoom animation to finish - await bgImageLocator.hover({trial: true}); + await page.locator(backgroundImageSelector).hover({trial: true}); const imageMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox(); // zoom out - await bgImageLocator.hover({trial: true}); + await page.locator(backgroundImageSelector).hover({trial: true}); await page.mouse.wheel(0, -deltaYStep); // wait for zoom animation to finish - await bgImageLocator.hover({trial: true}); + await page.locator(backgroundImageSelector).hover({trial: true}); const imageMouseZoomedOut = await page.locator(backgroundImageSelector).boundingBox(); expect(imageMouseZoomedIn.height).toBeGreaterThan(originalImageDimensions.height); @@ -83,13 +88,12 @@ test.describe('Example Imagery', () => { const deltaYStep = 100; //equivalent to 1x zoom const panHotkey = process.platform === 'linux' ? ['Control', 'Alt'] : ['Alt']; - const bgImageLocator = page.locator(backgroundImageSelector); - await bgImageLocator.hover({trial: true}); + await page.locator(backgroundImageSelector).hover({trial: true}); // zoom in await page.mouse.wheel(0, deltaYStep * 2); - await bgImageLocator.hover({trial: true}); - const zoomedBoundingBox = await bgImageLocator.boundingBox(); + await page.locator(backgroundImageSelector).hover({trial: true}); + const zoomedBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2; const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2; // move to the right @@ -112,7 +116,7 @@ test.describe('Example Imagery', () => { await page.mouse.move(imageCenterX - 200, imageCenterY, 10); await page.mouse.up(); await Promise.all(panHotkey.map(x => page.keyboard.up(x))); - const afterRightPanBoundingBox = await bgImageLocator.boundingBox(); + const afterRightPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); expect(zoomedBoundingBox.x).toBeGreaterThan(afterRightPanBoundingBox.x); // pan left @@ -121,7 +125,7 @@ test.describe('Example Imagery', () => { await page.mouse.move(imageCenterX, imageCenterY, 10); await page.mouse.up(); await Promise.all(panHotkey.map(x => page.keyboard.up(x))); - const afterLeftPanBoundingBox = await bgImageLocator.boundingBox(); + const afterLeftPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); expect(afterRightPanBoundingBox.x).toBeLessThan(afterLeftPanBoundingBox.x); // pan up @@ -131,7 +135,7 @@ test.describe('Example Imagery', () => { await page.mouse.move(imageCenterX, imageCenterY + 200, 10); await page.mouse.up(); await Promise.all(panHotkey.map(x => page.keyboard.up(x))); - const afterUpPanBoundingBox = await bgImageLocator.boundingBox(); + const afterUpPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); expect(afterUpPanBoundingBox.y).toBeGreaterThan(afterLeftPanBoundingBox.y); // pan down @@ -140,60 +144,58 @@ test.describe('Example Imagery', () => { await page.mouse.move(imageCenterX, imageCenterY - 200, 10); await page.mouse.up(); await Promise.all(panHotkey.map(x => page.keyboard.up(x))); - const afterDownPanBoundingBox = await bgImageLocator.boundingBox(); + const afterDownPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); expect(afterDownPanBoundingBox.y).toBeLessThan(afterUpPanBoundingBox.y); }); test('Can use + - buttons to zoom on the image', async ({ page }) => { - const bgImageLocator = page.locator(backgroundImageSelector); - await bgImageLocator.hover({trial: true}); + await page.locator(backgroundImageSelector).hover({trial: true}); const zoomInBtn = page.locator('.t-btn-zoom-in').nth(0); const zoomOutBtn = page.locator('.t-btn-zoom-out').nth(0); - const initialBoundingBox = await bgImageLocator.boundingBox(); + const initialBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); await zoomInBtn.click(); await zoomInBtn.click(); // wait for zoom animation to finish - await bgImageLocator.hover({trial: true}); - const zoomedInBoundingBox = await bgImageLocator.boundingBox(); + await page.locator(backgroundImageSelector).hover({trial: true}); + const zoomedInBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); expect(zoomedInBoundingBox.height).toBeGreaterThan(initialBoundingBox.height); expect(zoomedInBoundingBox.width).toBeGreaterThan(initialBoundingBox.width); await zoomOutBtn.click(); // wait for zoom animation to finish - await bgImageLocator.hover({trial: true}); - const zoomedOutBoundingBox = await bgImageLocator.boundingBox(); + await page.locator(backgroundImageSelector).hover({trial: true}); + const zoomedOutBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); expect(zoomedOutBoundingBox.height).toBeLessThan(zoomedInBoundingBox.height); expect(zoomedOutBoundingBox.width).toBeLessThan(zoomedInBoundingBox.width); }); test('Can use the reset button to reset the image', async ({ page }) => { - const bgImageLocator = page.locator(backgroundImageSelector); // wait for zoom animation to finish - await bgImageLocator.hover({trial: true}); + await page.locator(backgroundImageSelector).hover({trial: true}); const zoomInBtn = page.locator('.t-btn-zoom-in').nth(0); const zoomResetBtn = page.locator('.t-btn-zoom-reset').nth(0); - const initialBoundingBox = await bgImageLocator.boundingBox(); + const initialBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); await zoomInBtn.click(); // wait for zoom animation to finish - await bgImageLocator.hover({trial: true}); + await page.locator(backgroundImageSelector).hover({trial: true}); await zoomInBtn.click(); // wait for zoom animation to finish - await bgImageLocator.hover({trial: true}); + await page.locator(backgroundImageSelector).hover({trial: true}); - const zoomedInBoundingBox = await bgImageLocator.boundingBox(); + const zoomedInBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); expect.soft(zoomedInBoundingBox.height).toBeGreaterThan(initialBoundingBox.height); expect.soft(zoomedInBoundingBox.width).toBeGreaterThan(initialBoundingBox.width); await zoomResetBtn.click(); // wait for zoom animation to finish - await bgImageLocator.hover({trial: true}); + await page.locator(backgroundImageSelector).hover({trial: true}); - const resetBoundingBox = await bgImageLocator.boundingBox(); + const resetBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); expect.soft(resetBoundingBox.height).toBeLessThan(zoomedInBoundingBox.height); expect.soft(resetBoundingBox.width).toBeLessThan(zoomedInBoundingBox.width); @@ -202,10 +204,9 @@ test.describe('Example Imagery', () => { }); test('Using the zoom features does not pause telemetry', async ({ page }) => { - const bgImageLocator = page.locator(backgroundImageSelector); const pausePlayButton = page.locator('.c-button.pause-play'); // wait for zoom animation to finish - await bgImageLocator.hover({trial: true}); + await page.locator(backgroundImageSelector).hover({trial: true}); // open the time conductor drop down await page.locator('button:has-text("Fixed Timespan")').click(); @@ -216,7 +217,7 @@ test.describe('Example Imagery', () => { const zoomInBtn = page.locator('.t-btn-zoom-in').nth(0); await zoomInBtn.click(); // wait for zoom animation to finish - await bgImageLocator.hover({trial: true}); + await page.locator(backgroundImageSelector).hover({trial: true}); return expect(pausePlayButton).not.toHaveClass(/is-paused/); }); @@ -229,8 +230,8 @@ test.describe('Example Imagery', () => { // ('Clicking on the left arrow should pause the imagery and go to previous image'); // ('If the imagery view is in pause mode, it should not be updated when new images come in'); // ('If the imagery view is not in pause mode, it should be updated when new images come in'); -const backgroundImageSelector = '.c-imagery__main-image__background-image'; -test('Example Imagery in Display layout', async ({ page }) => { +test('Example Imagery in Display layout', async ({ page, browserName }) => { + test.fixme(browserName === 'firefox', 'This test needs to be updated to work with firefox'); test.info().annotations.push({ type: 'issue', description: 'https://github.com/nasa/openmct/issues/5265' @@ -262,8 +263,7 @@ test('Example Imagery in Display layout', async ({ page }) => { // Wait until Save Banner is gone await page.waitForSelector('.c-message-banner__message', { state: 'detached'}); await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery'); - const bgImageLocator = page.locator(backgroundImageSelector); - await bgImageLocator.hover({trial: true}); + await page.locator(backgroundImageSelector).hover({trial: true}); // Click previous image button const previousImageButton = page.locator('.c-nav--prev'); @@ -275,15 +275,15 @@ test('Example Imagery in Display layout', async ({ page }) => { // Zoom in const originalImageDimensions = await page.locator(backgroundImageSelector).boundingBox(); - await bgImageLocator.hover({trial: true}); + await page.locator(backgroundImageSelector).hover({trial: true}); const deltaYStep = 100; // equivalent to 1x zoom await page.mouse.wheel(0, deltaYStep * 2); - const zoomedBoundingBox = await bgImageLocator.boundingBox(); + const zoomedBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2; const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2; // Wait for zoom animation to finish - await bgImageLocator.hover({trial: true}); + await page.locator(backgroundImageSelector).hover({trial: true}); const imageMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox(); expect(imageMouseZoomedIn.height).toBeGreaterThan(originalImageDimensions.height); expect(imageMouseZoomedIn.width).toBeGreaterThan(originalImageDimensions.width); @@ -307,11 +307,11 @@ test('Example Imagery in Display layout', async ({ page }) => { await page.locator('[data-testid=conductor-modeOption-realtime]').click(); // Zoom in on next image - await bgImageLocator.hover({trial: true}); + await page.locator(backgroundImageSelector).hover({trial: true}); await page.mouse.wheel(0, deltaYStep * 2); // Wait for zoom animation to finish - await bgImageLocator.hover({trial: true}); + await page.locator(backgroundImageSelector).hover({trial: true}); const imageNextMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox(); expect(imageNextMouseZoomedIn.height).toBeGreaterThan(originalImageDimensions.height); expect(imageNextMouseZoomedIn.width).toBeGreaterThan(originalImageDimensions.width); @@ -337,7 +337,6 @@ test('Example Imagery in Display layout', async ({ page }) => { }); test.describe('Example imagery thumbnails resize in display layouts', () => { - test('Resizing the layout changes thumbnail visibility and size', async ({ page }) => { await page.goto('/', { waitUntil: 'networkidle' }); @@ -426,12 +425,137 @@ test.describe('Example imagery thumbnails resize in display layouts', () => { }); test.describe('Example Imagery in Flexible layout', () => { - test.fixme('Can use Mouse Wheel to zoom in and out of previous image'); - test.fixme('Can use alt+drag to move around image once zoomed in'); - test.fixme('Can zoom into the latest image and the real-time/fixed-time imagery will pause'); - test.fixme('Clicking on the left arrow should pause the imagery and go to previous image'); - test.fixme('If the imagery view is in pause mode, it should not be updated when new images come in'); - test.fixme('If the imagery view is not in pause mode, it should be updated when new images come in'); + test('Example Imagery in Flexible layout', async ({ page, browserName }) => { + test.fixme(browserName === 'firefox', 'This test needs to be updated to work with firefox'); + test.info().annotations.push({ + type: 'issue', + description: 'https://github.com/nasa/openmct/issues/5326' + }); + + // Go to baseURL + await page.goto('/', { waitUntil: 'networkidle' }); + + // Click the Create button + await page.click('button:has-text("Create")'); + + // Click text=Example Imagery + await page.click('text=Example Imagery'); + + // Clear and set Image load delay (milliseconds) + await page.click('input[type="number"]', {clickCount: 3}); + await page.type('input[type="number"]', "20"); + + // Click text=OK + await Promise.all([ + page.waitForNavigation({waitUntil: 'networkidle'}), + page.click('text=OK'), + //Wait for Save Banner to appear + page.waitForSelector('.c-message-banner__message') + ]); + // Wait until Save Banner is gone + await page.waitForSelector('.c-message-banner__message', { state: 'detached'}); + await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery'); + await page.locator(backgroundImageSelector).hover({trial: true}); + + // Click the Create button + await page.click('button:has-text("Create")'); + + // Click text=Flexible Layout + await page.click('text=Flexible Layout'); + + // Assert Flexable layout + await expect(page.locator('.js-form-title')).toHaveText('Create a New Flexible Layout'); + + await page.locator('form[name="mctForm"] >> text=My Items').click(); + + // Click My Items + await Promise.all([ + page.locator('text=OK').click(), + page.waitForNavigation({waitUntil: 'networkidle'}) + ]); + + // Click My Items + await page.locator('.c-disclosure-triangle').click(); + + // Right click example imagery + await page.click(('text=Unnamed Example Imagery'), { button: 'right' }); + + // Click move + await page.locator('.icon-move').click(); + + // Click triangle to open sub menu + await page.locator('.c-form__section .c-disclosure-triangle').click(); + + // Click Flexable Layout + await page.click('.c-overlay__outer >> text=Unnamed Flexible Layout'); + + // Click text=OK + await page.locator('text=OK').click(); + + // Save template + await saveTemplate(page); + + // Zoom in + await mouseZoomIn(page); + + // Center the mouse pointer + const zoomedBoundingBox = await await page.locator(backgroundImageSelector).boundingBox(); + const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2; + const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2; + await page.mouse.move(imageCenterX, imageCenterY); + + // Pan zoom + await panZoomAndAssertImageProperties(page); + + // Click previous image button + const previousImageButton = page.locator('.c-nav--prev'); + await previousImageButton.click(); + + // Verify previous image + const selectedImage = page.locator('.selected'); + await expect(selectedImage).toBeVisible(); + + // Click time conductor mode button + await page.locator('.c-mode-button').click(); + + // Select local clock mode + await page.locator('[data-testid=conductor-modeOption-realtime]').click(); + + // Zoom in on next image + await mouseZoomIn(page); + + // Click previous image button + await previousImageButton.click(); + + // Verify previous image + await expect(selectedImage).toBeVisible(); + + const imageCount = await page.locator('.c-imagery__thumb').count(); + await expect.poll(async () => { + const newImageCount = await page.locator('.c-imagery__thumb').count(); + + return newImageCount; + }, { + message: "verify that old images are discarded", + timeout: 6 * 1000 + }).toBe(imageCount); + + // Verify selected image is still displayed + await expect(selectedImage).toBeVisible(); + + // Unpause imagery + await page.locator('.pause-play').click(); + + //Get background-image url from background-image css prop + await assertBackgroundImageUrlFromBackgroundCss(page); + + // Open the image filter menu + await page.locator('[role=toolbar] button[title="Brightness and contrast"]').click(); + + // Drag the brightness and contrast sliders around and assert filter values + await dragBrightnessSliderAndAssertFilterValues(page); + await dragContrastSliderAndAssertFilterValues(page); + }); }); test.describe('Example Imagery in Tabs view', () => { @@ -443,3 +567,185 @@ test.describe('Example Imagery in Tabs view', () => { test.fixme('If the imagery view is in pause mode, it should not be updated when new images come in'); test.fixme('If the imagery view is not in pause mode, it should be updated when new images come in'); }); + +/** + * @param {import('@playwright/test').Page} page + */ +async function saveTemplate(page) { + await page.locator('.c-button--menu.c-button--major.icon-save').click(); + await page.locator('text=Save and Finish Editing').click(); +} + +/** + * Drag the brightness slider to max, min, and midpoint and assert the filter values + * @param {import('@playwright/test').Page} page + */ +async function dragBrightnessSliderAndAssertFilterValues(page) { + const brightnessSlider = 'div.c-image-controls__slider-wrapper.icon-brightness > input'; + const brightnessBoundingBox = await page.locator(brightnessSlider).boundingBox(); + const brightnessMidX = brightnessBoundingBox.x + brightnessBoundingBox.width / 2; + const brightnessMidY = brightnessBoundingBox.y + brightnessBoundingBox.height / 2; + + await page.locator(brightnessSlider).hover({trial: true}); + await page.mouse.down(); + await page.mouse.move(brightnessBoundingBox.x + brightnessBoundingBox.width, brightnessMidY); + await assertBackgroundImageBrightness(page, '500'); + await page.mouse.move(brightnessBoundingBox.x, brightnessMidY); + await assertBackgroundImageBrightness(page, '0'); + await page.mouse.move(brightnessMidX, brightnessMidY); + await assertBackgroundImageBrightness(page, '250'); + await page.mouse.up(); +} + +/** + * Drag the contrast slider to max, min, and midpoint and assert the filter values + * @param {import('@playwright/test').Page} page + */ +async function dragContrastSliderAndAssertFilterValues(page) { + const contrastSlider = 'div.c-image-controls__slider-wrapper.icon-contrast > input'; + const contrastBoundingBox = await page.locator(contrastSlider).boundingBox(); + const contrastMidX = contrastBoundingBox.x + contrastBoundingBox.width / 2; + const contrastMidY = contrastBoundingBox.y + contrastBoundingBox.height / 2; + + await page.locator(contrastSlider).hover({trial: true}); + await page.mouse.down(); + await page.mouse.move(contrastBoundingBox.x + contrastBoundingBox.width, contrastMidY); + await assertBackgroundImageContrast(page, '500'); + await page.mouse.move(contrastBoundingBox.x, contrastMidY); + await assertBackgroundImageContrast(page, '0'); + await page.mouse.move(contrastMidX, contrastMidY); + await assertBackgroundImageContrast(page, '250'); + await page.mouse.up(); +} + +/** + * Gets the filter:brightness value of the current background-image and + * asserts against an expected value + * @param {import('@playwright/test').Page} page + * @param {String} expected The expected brightness value + */ +async function assertBackgroundImageBrightness(page, expected) { + const backgroundImage = page.locator('.c-imagery__main-image__background-image'); + + // Get the brightness filter value (i.e: filter: brightness(500%) => "500") + const actual = await backgroundImage.evaluate((el) => { + return el.style.filter.match(/brightness\((\d{1,3})%\)/)[1]; + }); + expect(actual).toBe(expected); +} + +/** + * Gets the filter:contrast value of the current background-image and + * asserts against an expected value + * @param {import('@playwright/test').Page} page + * @param {String} expected The expected contrast value + */ +async function assertBackgroundImageContrast(page, expected) { + const backgroundImage = page.locator('.c-imagery__main-image__background-image'); + + // Get the contrast filter value (i.e: filter: contrast(500%) => "500") + const actual = await backgroundImage.evaluate((el) => { + return el.style.filter.match(/contrast\((\d{1,3})%\)/)[1]; + }); + expect(actual).toBe(expected); +} + +/** + * @param {import('@playwright/test').Page} page + */ +async function assertBackgroundImageUrlFromBackgroundCss(page) { + const backgroundImage = page.locator('.c-imagery__main-image__background-image'); + let backgroundImageUrl = await backgroundImage.evaluate((el) => { + return window.getComputedStyle(el).getPropertyValue('background-image').match(/url\(([^)]+)\)/)[1]; + }); + let backgroundImageUrl1 = backgroundImageUrl.slice(1, -1); //forgive me, padre + console.log('backgroundImageUrl1 ' + backgroundImageUrl1); + + let backgroundImageUrl2; + await expect.poll(async () => { + // Verify next image has updated + let backgroundImageUrlNext = await backgroundImage.evaluate((el) => { + return window.getComputedStyle(el).getPropertyValue('background-image').match(/url\(([^)]+)\)/)[1]; + }); + backgroundImageUrl2 = backgroundImageUrlNext.slice(1, -1); //forgive me, padre + + return backgroundImageUrl2; + }, { + message: "verify next image has updated", + timeout: 6 * 1000 + }).not.toBe(backgroundImageUrl1); + console.log('backgroundImageUrl2 ' + backgroundImageUrl2); +} + +/** + * @param {import('@playwright/test').Page} page + */ +async function panZoomAndAssertImageProperties(page) { + const panHotkey = process.platform === 'linux' ? ['Control', 'Alt'] : ['Alt']; + const expectedAltText = process.platform === 'linux' ? 'Ctrl+Alt drag to pan' : 'Alt drag to pan'; + const imageryHintsText = await page.locator('.c-imagery__hints').innerText(); + expect(expectedAltText).toEqual(imageryHintsText); + const zoomedBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); + const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2; + const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2; + + // Pan right + await Promise.all(panHotkey.map(x => page.keyboard.down(x))); + await page.mouse.down(); + await page.mouse.move(imageCenterX - 200, imageCenterY, 10); + await page.mouse.up(); + await Promise.all(panHotkey.map(x => page.keyboard.up(x))); + const afterRightPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); + expect(zoomedBoundingBox.x).toBeGreaterThan(afterRightPanBoundingBox.x); + + // Pan left + await Promise.all(panHotkey.map(x => page.keyboard.down(x))); + await page.mouse.down(); + await page.mouse.move(imageCenterX, imageCenterY, 10); + await page.mouse.up(); + await Promise.all(panHotkey.map(x => page.keyboard.up(x))); + const afterLeftPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); + expect(afterRightPanBoundingBox.x).toBeLessThan(afterLeftPanBoundingBox.x); + + // Pan up + await page.mouse.move(imageCenterX, imageCenterY); + await Promise.all(panHotkey.map(x => page.keyboard.down(x))); + await page.mouse.down(); + await page.mouse.move(imageCenterX, imageCenterY + 200, 10); + await page.mouse.up(); + await Promise.all(panHotkey.map(x => page.keyboard.up(x))); + const afterUpPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); + expect(afterUpPanBoundingBox.y).toBeGreaterThanOrEqual(afterLeftPanBoundingBox.y); + + // Pan down + await Promise.all(panHotkey.map(x => page.keyboard.down(x))); + await page.mouse.down(); + await page.mouse.move(imageCenterX, imageCenterY - 200, 10); + await page.mouse.up(); + await Promise.all(panHotkey.map(x => page.keyboard.up(x))); + const afterDownPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); + expect(afterDownPanBoundingBox.y).toBeLessThanOrEqual(afterUpPanBoundingBox.y); +} + +/** + * @param {import('@playwright/test').Page} page +*/ +async function mouseZoomIn(page) { + // Zoom in + const originalImageDimensions = await page.locator(backgroundImageSelector).boundingBox(); + await page.locator(backgroundImageSelector).hover({trial: true}); + const deltaYStep = 100; // equivalent to 1x zoom + await page.mouse.wheel(0, deltaYStep * 2); + const zoomedBoundingBox = await page.locator(backgroundImageSelector).boundingBox(); + const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2; + const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2; + + // center the mouse pointer + await page.mouse.move(imageCenterX, imageCenterY); + + // Wait for zoom animation to finish + await page.locator(backgroundImageSelector).hover({trial: true}); + const imageMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox(); + expect(imageMouseZoomedIn.height).toBeGreaterThan(originalImageDimensions.height); + expect(imageMouseZoomedIn.width).toBeGreaterThan(originalImageDimensions.width); +} diff --git a/e2e/tests/plugins/notebook/restrictedNotebook.e2e.spec.js b/e2e/tests/plugins/notebook/restrictedNotebook.e2e.spec.js index 30bc169f27..37012504a0 100644 --- a/e2e/tests/plugins/notebook/restrictedNotebook.e2e.spec.js +++ b/e2e/tests/plugins/notebook/restrictedNotebook.e2e.spec.js @@ -27,108 +27,11 @@ const path = require('path'); const TEST_TEXT = 'Testing text for entries.'; const TEST_TEXT_NAME = 'Test Page'; const CUSTOM_NAME = 'CUSTOM_NAME'; -const COMMIT_BUTTON_TEXT = 'button:has-text("Commit Entries")'; -const SINE_WAVE_GENERATOR = 'text=Unnamed Sine Wave Generator'; const NOTEBOOK_DROP_AREA = '.c-notebook__drag-area'; -/** - * @param {import('@playwright/test').Page} page - */ -async function startAndAddNotebookObject(page) { - // eslint-disable-next-line no-undef - await page.addInitScript({ path: path.join(__dirname, 'addInitRestrictedNotebook.js') }); - //Go to baseURL - await page.goto('/', { waitUntil: 'networkidle' }); - //Click the Create button - await page.click('button:has-text("Create")'); - // Click text=CUSTOME_NAME - await page.click(`text=${CUSTOM_NAME}`); // secondarily tests renamability also - // Click text=OK - await Promise.all([ - page.waitForNavigation({waitUntil: 'networkidle'}), - page.click('text=OK') - ]); - - return; -} - -/** - * @param {import('@playwright/test').Page} page - */ -async function enterTextEntry(page) { - // Click .c-notebook__drag-area - await page.locator(NOTEBOOK_DROP_AREA).click(); - - // enter text - await page.locator('div.c-ne__text').click(); - await page.locator('div.c-ne__text').fill(TEST_TEXT); - await page.locator('div.c-ne__text').press('Enter'); - - return; -} - -/** - * @param {import('@playwright/test').Page} page - */ -async function dragAndDropEmbed(page) { - // Click button:has-text("Create") - await page.locator('button:has-text("Create")').click(); - // Click li:has-text("Sine Wave Generator") - await page.locator('li:has-text("Sine Wave Generator")').click(); - // Click form[name="mctForm"] >> text=My Items - await page.locator('form[name="mctForm"] >> text=My Items').click(); - // Click text=OK - await page.locator('text=OK').click(); - // Click text=Open MCT My Items >> span >> nth=3 - await page.locator('text=Open MCT My Items >> span').nth(3).click(); - // Click text=Unnamed CUSTOM_NAME - await Promise.all([ - page.waitForNavigation(), - page.locator('text=Unnamed CUSTOM_NAME').click() - ]); - - await page.dragAndDrop(SINE_WAVE_GENERATOR, NOTEBOOK_DROP_AREA); - - return; -} - -/** - * @param {import('@playwright/test').Page} page - */ -async function lockPage(page) { - const commitButton = page.locator(COMMIT_BUTTON_TEXT); - await commitButton.click(); - - // confirmation dialog click - await page.locator('text=Lock Page').click(); - - // waiting for mutation of locked page - await new Promise((resolve, reject) => { - setTimeout(resolve, 1000); - }); - - return; -} - -/** - * @param {import('@playwright/test').Page} page - */ -async function openContextMenuRestrictedNotebook(page) { - // Click text=Open MCT My Items (This expands the My Items folder to show it's chilren in the tree) - await page.locator('text=Open MCT My Items >> span').nth(3).click(); - - // Click a:has-text("Unnamed CUSTOM_NAME") - await page.locator(`a:has-text("Unnamed ${CUSTOM_NAME}")`).click({ - button: 'right' - }); - - return; -} - -test.describe('Restricted Notebook @addInit', () => { - +test.describe('Restricted Notebook', () => { test.beforeEach(async ({ page }) => { - await startAndAddNotebookObject(page); + await startAndAddRestrictedNotebookObject(page); }); test('Can be renamed @addInit', async ({ page }) => { @@ -146,13 +49,16 @@ test.describe('Restricted Notebook @addInit', () => { // notbook tree object exists expect.soft(await restrictedNotebookTreeObject.count()).toEqual(1); - // Click text=Remove + // Click Remove Text await page.locator('text=Remove').click(); - // Click text=OK + + //Wait until Save Banner is gone await Promise.all([ - page.waitForNavigation(/*{ url: 'http://localhost:8080/#/browse/mine?tc.mode=fixed&tc.startBound=1653671067340&tc.endBound=1653672867340&tc.timeSystem=utc&view=grid' }*/), - page.locator('text=OK').click() + page.waitForNavigation(), + page.locator('text=OK').click(), + page.waitForSelector('.c-message-banner__message') ]); + await page.locator('.c-message-banner__close-button').click(); // has been deleted expect.soft(await restrictedNotebookTreeObject.count()).toEqual(0); @@ -162,7 +68,7 @@ test.describe('Restricted Notebook @addInit', () => { await enterTextEntry(page); - const commitButton = page.locator(COMMIT_BUTTON_TEXT); + const commitButton = page.locator('button:has-text("Commit Entries")'); expect.soft(await commitButton.count()).toEqual(1); }); @@ -171,7 +77,7 @@ test.describe('Restricted Notebook @addInit', () => { test.describe('Restricted Notebook with at least one entry and with the page locked @addInit', () => { test.beforeEach(async ({ page }) => { - await startAndAddNotebookObject(page); + await startAndAddRestrictedNotebookObject(page); await enterTextEntry(page); await lockPage(page); @@ -218,7 +124,7 @@ test.describe('Restricted Notebook with at least one entry and with the page loc await enterTextEntry(page); // expect new page to be lockable - const commitButton = page.locator(COMMIT_BUTTON_TEXT); + const commitButton = page.locator('BUTTON:HAS-TEXT("COMMIT ENTRIES")'); expect.soft(await commitButton.count()).toEqual(1); // Click text=Unnamed PageTest Page >> button @@ -240,7 +146,7 @@ test.describe('Restricted Notebook with at least one entry and with the page loc test.describe('Restricted Notebook with a page locked and with an embed @addInit', () => { test.beforeEach(async ({ page }) => { - await startAndAddNotebookObject(page); + await startAndAddRestrictedNotebookObject(page); await dragAndDropEmbed(page); }); @@ -262,3 +168,95 @@ test.describe('Restricted Notebook with a page locked and with an embed @addInit }); }); + +/** + * @param {import('@playwright/test').Page} page + */ +async function startAndAddRestrictedNotebookObject(page) { + // eslint-disable-next-line no-undef + await page.addInitScript({ path: path.join(__dirname, 'addInitRestrictedNotebook.js') }); + //Go to baseURL + await page.goto('/', { waitUntil: 'networkidle' }); + //Click the Create button + await page.click('button:has-text("Create")'); + // Click text=CUSTOME_NAME + await page.click(`text=${CUSTOM_NAME}`); // secondarily tests renamability also + // Click text=OK + await Promise.all([ + page.waitForNavigation({waitUntil: 'networkidle'}), + page.click('text=OK') + ]); +} + +/** + * @param {import('@playwright/test').Page} page + */ +async function enterTextEntry(page) { + // Click .c-notebook__drag-area + await page.locator(NOTEBOOK_DROP_AREA).click(); + + // enter text + await page.locator('div.c-ne__text').click(); + await page.locator('div.c-ne__text').fill(TEST_TEXT); + await page.locator('div.c-ne__text').press('Enter'); +} + +/** + * @param {import('@playwright/test').Page} page + */ +async function dragAndDropEmbed(page) { + // Click button:has-text("Create") + await page.locator('button:has-text("Create")').click(); + // Click li:has-text("Sine Wave Generator") + await page.locator('li:has-text("Sine Wave Generator")').click(); + // Click form[name="mctForm"] >> text=My Items + await page.locator('form[name="mctForm"] >> text=My Items').click(); + // Click text=OK + await page.locator('text=OK').click(); + // Click text=Open MCT My Items >> span >> nth=3 + await page.locator('text=Open MCT My Items >> span').nth(3).click(); + // Click text=Unnamed CUSTOM_NAME + await Promise.all([ + page.waitForNavigation(), + page.locator('text=Unnamed CUSTOM_NAME').click() + ]); + + await page.dragAndDrop('text=UNNAMED SINE WAVE GENERATOR', NOTEBOOK_DROP_AREA); +} + +/** + * @param {import('@playwright/test').Page} page + */ +async function lockPage(page) { + const commitButton = page.locator('button:has-text("Commit Entries")'); + await commitButton.click(); + + //Wait until Lock Banner is visible + await Promise.all([ + page.locator('text=Lock Page').click(), + page.waitForSelector('.c-message-banner__message') + ]); + // Close Lock Banner + await page.locator('.c-message-banner__close-button').click(); + + //artifically wait to avoid mutation delay TODO: https://github.com/nasa/openmct/issues/5409 + // eslint-disable-next-line playwright/no-wait-for-timeout + await page.waitForTimeout(1 * 1000); +} + +/** + * @param {import('@playwright/test').Page} page + */ +async function openContextMenuRestrictedNotebook(page) { + // Click text=Open MCT My Items (This expands the My Items folder to show it's chilren in the tree) + await page.locator('text=Open MCT My Items >> span').nth(3).click(); + + //artifically wait to avoid mutation delay TODO: https://github.com/nasa/openmct/issues/5409 + // eslint-disable-next-line playwright/no-wait-for-timeout + await page.waitForTimeout(1 * 1000); + + // Click a:has-text("Unnamed CUSTOM_NAME") + await page.locator(`a:has-text("Unnamed ${CUSTOM_NAME}")`).click({ + button: 'right' + }); +} diff --git a/e2e/tests/plugins/plot/autoscale.e2e.spec.js b/e2e/tests/plugins/plot/autoscale.e2e.spec.js index c6f1b6cf75..ee9782ec42 100644 --- a/e2e/tests/plugins/plot/autoscale.e2e.spec.js +++ b/e2e/tests/plugins/plot/autoscale.e2e.spec.js @@ -24,22 +24,9 @@ Testsuite for plot autoscale. */ -const { test: _test } = require('../../../fixtures.js'); +const { test } = require('../../../fixtures.js'); const { expect } = require('@playwright/test'); -// create a new `test` API that will not append platform details to snapshot -// file names, only for the tests in this file, so that the same snapshots will -// be used for all platforms. -const test = _test.extend({ - _autoSnapshotSuffix: [ - async ({}, use, testInfo) => { - testInfo.snapshotSuffix = ''; - await use(); - }, - { auto: true } - ] -}); - test.use({ viewport: { width: 1280, @@ -50,7 +37,7 @@ test.use({ test.describe('ExportAsJSON', () => { test('User can set autoscale with a valid range @snapshot', async ({ page }) => { //This is necessary due to the size of the test suite. - await test.setTimeout(120 * 1000); + test.slow(); await page.goto('/', { waitUntil: 'networkidle' }); @@ -62,16 +49,16 @@ test.describe('ExportAsJSON', () => { await turnOffAutoscale(page); + // Make sure that after turning off autoscale, the user selected range values start at the same values the plot had prior. + await testYTicks(page, ['-1.00', '-0.50', '0.00', '0.50', '1.00']); + const canvas = page.locator('canvas').nth(1); - // Make sure that after turning off autoscale, the user selected range values start at the same values the plot had prior. - await Promise.all([ - testYTicks(page, ['-1.00', '-0.50', '0.00', '0.50', '1.00']), - new Promise(r => setTimeout(r, 100)) - .then(() => canvas.screenshot()) - .then(shot => expect(shot).toMatchSnapshot('autoscale-canvas-prepan.png', { maxDiffPixels: 40 })) - ]); + await canvas.hover({trial: true}); + expect(await canvas.screenshot()).toMatchSnapshot('autoscale-canvas-prepan'); + + //Alt Drag Start await page.keyboard.down('Alt'); await canvas.dragTo(canvas, { @@ -85,15 +72,15 @@ test.describe('ExportAsJSON', () => { } }); + //Alt Drag End await page.keyboard.up('Alt'); // Ensure the drag worked. - await Promise.all([ - testYTicks(page, ['0.00', '0.50', '1.00', '1.50', '2.00']), - new Promise(r => setTimeout(r, 100)) - .then(() => canvas.screenshot()) - .then(shot => expect(shot).toMatchSnapshot('autoscale-canvas-panned.png', { maxDiffPixels: 40 })) - ]); + await testYTicks(page, ['0.00', '0.50', '1.00', '1.50', '2.00']); + + await canvas.hover({trial: true}); + + expect(await canvas.screenshot()).toMatchSnapshot('autoscale-canvas-panned'); }); }); diff --git a/e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-darwin b/e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-darwin new file mode 100644 index 0000000000000000000000000000000000000000..01850a3bc4bdfbe4c9eeb71284b62423d00cad43 GIT binary patch literal 16116 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%0A6wWGa%b(^0~|J`4p87f4xV`Cb6>yW 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 YrBKJ-IyCDd-TCNf=&P69di3Id0iF7f#{d8T literal 0 HcmV?d00001 diff --git a/e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome.png b/e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome.png deleted file mode 100644 index 4b546e18785da586ba2d5b5c476b0ba47b3ce330..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18379 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 SiO{j%Up22?EIfbn_WuEa-~sdi diff --git a/e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-darwin b/e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-darwin new file mode 100644 index 0000000000000000000000000000000000000000..75b1c4d9534e7ac3b621405f321304fec7ba7c05 GIT binary patch literal 18406 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 ORJzdHnx)rmpZ*VUQ|PMz literal 0 HcmV?d00001 diff --git a/e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome.png b/e2e/tests/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome.png deleted file mode 100644 index c0f293bd1694a4cefd5c240d1bffe20b2af3ac90..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19897 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 { test('Log Plot ticks are functionally correct in regular and log mode and after refresh', async ({ page }) => { - //This is necessary due to the size of the test suite. - await test.setTimeout(120 * 1000); + //Test.slow decorator is currently broken. Needs to be fixed in https://github.com/nasa/openmct/issues/5374 + test.slow(); await makeOverlayPlot(page); await testRegularTicks(page); @@ -44,20 +44,6 @@ test.describe('Log plot tests', () => { await testLogTicks(page); await saveOverlayPlot(page); await testLogTicks(page); - //await testLogPlotPixels(page); - - // FIXME: Get rid of the waitForTimeout() and lint warning exception. - // eslint-disable-next-line playwright/no-wait-for-timeout - await page.waitForTimeout(1 * 1000); - - // refresh page and wait for charts and ticks to load - await page.reload({ waitUntil: 'networkidle'}); - await page.waitForSelector('.gl-plot-chart-area'); - await page.waitForSelector('.gl-plot-y-tick-label'); - - // test log ticks hold up after refresh - await testLogTicks(page); - //await testLogPlotPixels(page); }); // Leaving test as 'TODO' for now. @@ -121,14 +107,14 @@ async function makeOverlayPlot(page) { // set amplitude to 6, offset 4, period 2 - await page.locator('div:nth-child(5) .form-row .c-form-row__controls .form-control .field input').click(); - await page.locator('div:nth-child(5) .form-row .c-form-row__controls .form-control .field input').fill('6'); + await page.locator('div:nth-child(5) .c-form-row__controls .form-control .field input').click(); + await page.locator('div:nth-child(5) .c-form-row__controls .form-control .field input').fill('6'); - await page.locator('div:nth-child(6) .form-row .c-form-row__controls .form-control .field input').click(); - await page.locator('div:nth-child(6) .form-row .c-form-row__controls .form-control .field input').fill('4'); + await page.locator('div:nth-child(6) .c-form-row__controls .form-control .field input').click(); + await page.locator('div:nth-child(6) .c-form-row__controls .form-control .field input').fill('4'); - await page.locator('div:nth-child(7) .form-row .c-form-row__controls .form-control .field input').click(); - await page.locator('div:nth-child(7) .form-row .c-form-row__controls .form-control .field input').fill('2'); + await page.locator('div:nth-child(7) .c-form-row__controls .form-control .field input').click(); + await page.locator('div:nth-child(7) .c-form-row__controls .form-control .field input').fill('2'); // Click OK to make generator diff --git a/e2e/tests/recycled_storage.json b/e2e/tests/recycled_storage.json index 53c7695977..8d59148b23 100644 --- a/e2e/tests/recycled_storage.json +++ b/e2e/tests/recycled_storage.json @@ -4,19 +4,19 @@ { "origin": "http://localhost:8080", "localStorage": [ + { + "name": "mct-tree-expanded", + "value": "[]" + }, { "name": "tcHistory", - "value": "{\"utc\":[{\"start\":1652301954635,\"end\":1652303754635}]}" + "value": "{\"utc\":[{\"start\":1656473493306,\"end\":1656475293306},{\"start\":1655769110258,\"end\":1655770910258},{\"start\":1652301954635,\"end\":1652303754635}]}" }, { "name": "mct", - "value": "{\"mine\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"f64bea3b-58a7-4586-8c05-8b651e5f0bfd\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"persisted\":1652303756008,\"modified\":1652303756007},\"f64bea3b-58a7-4586-8c05-8b651e5f0bfd\":{\"name\":\"Unnamed Condition Set\",\"type\":\"conditionSet\",\"identifier\":{\"key\":\"f64bea3b-58a7-4586-8c05-8b651e5f0bfd\",\"namespace\":\"\"},\"configuration\":{\"conditionTestData\":[],\"conditionCollection\":[{\"isDefault\":true,\"id\":\"73f2d9ae-d1f3-4561-b7fc-ecd5df557249\",\"configuration\":{\"name\":\"Default\",\"output\":\"Default\",\"trigger\":\"all\",\"criteria\":[]},\"summary\":\"Default condition\"}]},\"composition\":[],\"telemetry\":{},\"modified\":1652303755999,\"location\":\"mine\",\"persisted\":1652303756002}}" - }, - { - "name": "mct-tree-expanded", - "value": "[]" + "value": "{\"mine\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"f64bea3b-58a7-4586-8c05-8b651e5f0bfd\",\"namespace\":\"\"},{\"key\":\"18ba28bf-152e-4e0f-9b9c-638fb2ade0c3\",\"namespace\":\"\"},{\"key\":\"fa64bd6c-9351-4d94-a54e-e062a93be3b6\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"persisted\":1656475294042,\"modified\":1656475294042},\"f64bea3b-58a7-4586-8c05-8b651e5f0bfd\":{\"name\":\"Unnamed Condition Set\",\"type\":\"conditionSet\",\"identifier\":{\"key\":\"f64bea3b-58a7-4586-8c05-8b651e5f0bfd\",\"namespace\":\"\"},\"configuration\":{\"conditionTestData\":[],\"conditionCollection\":[{\"isDefault\":true,\"id\":\"73f2d9ae-d1f3-4561-b7fc-ecd5df557249\",\"configuration\":{\"name\":\"Default\",\"output\":\"Default\",\"trigger\":\"all\",\"criteria\":[]},\"summary\":\"Default condition\"}]},\"composition\":[],\"telemetry\":{},\"modified\":1652303755999,\"location\":\"mine\",\"persisted\":1652303756002},\"18ba28bf-152e-4e0f-9b9c-638fb2ade0c3\":{\"name\":\"Unnamed Condition Set\",\"type\":\"conditionSet\",\"identifier\":{\"key\":\"18ba28bf-152e-4e0f-9b9c-638fb2ade0c3\",\"namespace\":\"\"},\"configuration\":{\"conditionTestData\":[],\"conditionCollection\":[{\"isDefault\":true,\"id\":\"43cfb4b1-348c-43c0-a681-c4cf53b5335f\",\"configuration\":{\"name\":\"Default\",\"output\":\"Default\",\"trigger\":\"all\",\"criteria\":[]},\"summary\":\"Default condition\"}]},\"composition\":[],\"telemetry\":{},\"modified\":1655770911020,\"location\":\"mine\",\"persisted\":1655770911020},\"fa64bd6c-9351-4d94-a54e-e062a93be3b6\":{\"name\":\"Unnamed Condition Set\",\"type\":\"conditionSet\",\"identifier\":{\"key\":\"fa64bd6c-9351-4d94-a54e-e062a93be3b6\",\"namespace\":\"\"},\"configuration\":{\"conditionTestData\":[],\"conditionCollection\":[{\"isDefault\":true,\"id\":\"26739ce0-9a56-466c-91dd-f08bd9bfc9d7\",\"configuration\":{\"name\":\"Default\",\"output\":\"Default\",\"trigger\":\"all\",\"criteria\":[]},\"summary\":\"Default condition\"}]},\"composition\":[],\"telemetry\":{},\"modified\":1656475294040,\"location\":\"mine\",\"persisted\":1656475294040}}" } ] } ] -} +} \ No newline at end of file diff --git a/e2e/tests/smoke.e2e.spec.js b/e2e/tests/smoke.e2e.spec.js index 43e73972f5..f8f87794f7 100644 --- a/e2e/tests/smoke.e2e.spec.js +++ b/e2e/tests/smoke.e2e.spec.js @@ -45,6 +45,15 @@ 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(); +}); + +test('Verify that My Items Tree appears @ipad', async ({ page }) => { + //Test.slow annotation is currently broken. Needs to be fixed in https://github.com/nasa/openmct/issues/5374 + test.slow(); + //Go to baseURL + await page.goto('/'); + + //My Items to be visible + await expect(page.locator('a:has-text("My Items")')).toBeEnabled(); }); diff --git a/e2e/tests/visual/default.visual.spec.js b/e2e/tests/visual/default.visual.spec.js index b069f2e95f..e64de2925e 100644 --- a/e2e/tests/visual/default.visual.spec.js +++ b/e2e/tests/visual/default.visual.spec.js @@ -32,7 +32,8 @@ 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. */ -const { test, expect } = require('@playwright/test'); +const { test } = require('../../fixtures.js'); +const { expect } = require('@playwright/test'); const percySnapshot = require('@percy/playwright'); const path = require('path'); const sinon = require('sinon'); @@ -96,7 +97,11 @@ test('Visual - Default Condition Set', async ({ page }) => { await percySnapshot(page, 'Default Condition Set'); }); -test('Visual - Default Condition Widget', async ({ page }) => { +test.fixme('Visual - Default Condition Widget', async ({ page }) => { + test.info().annotations.push({ + type: 'issue', + description: 'https://github.com/nasa/openmct/issues/5349' + }); //Go to baseURL await page.goto('/', { waitUntil: 'networkidle' }); diff --git a/karma.conf.js b/karma.conf.js index e5fbe40ab0..e07aa94671 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -74,13 +74,8 @@ module.exports = (config) => { }, coverageIstanbulReporter: { fixWebpackSourcePaths: true, - dir: "dist/reports/coverage", - reports: ['lcovonly', 'text-summary'], - thresholds: { - global: { - lines: 52 - } - } + dir: "coverage/unit", + reports: ['lcovonly'] }, specReporter: { maxLogLines: 5, diff --git a/package.json b/package.json index 4439b37a68..5e11492516 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "@braintree/sanitize-url": "6.0.0", "@percy/cli": "1.2.1", "@percy/playwright": "1.0.4", - "@playwright/test": "1.21.1", + "@playwright/test": "1.23.0", "@types/eventemitter3": "^1.0.0", "@types/jasmine": "^4.0.1", "@types/karma": "^6.3.2", @@ -16,6 +16,7 @@ "babel-loader": "8.2.3", "babel-plugin-istanbul": "6.1.1", "comma-separated-values": "3.6.4", + "codecov":"3.8.3", "copy-webpack-plugin": "11.0.0", "cross-env": "7.0.3", "css-loader": "4.0.0", @@ -55,6 +56,7 @@ "moment-timezone": "0.5.34", "node-bourbon": "4.2.3", "painterro": "1.2.56", + "nyc":"15.1.0", "plotly.js-basic-dist": "2.12.0", "plotly.js-gl2d-dist": "2.12.0", "printj": "1.3.1", @@ -89,9 +91,10 @@ "test": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run", "test:firefox": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run --browsers=FirefoxHeadless", "test:debug": "cross-env NODE_ENV=debug karma start --no-single-run", - "test:e2e:ci": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome smoke branding default condition timeConductor clock exampleImagery persistence performance grandsearch tags", + "test:e2e": "npx playwright test", + "test:e2e:ci": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome smoke branding default condition timeConductor clock exampleImagery persistence performance grandsearch notebook/tags", "test:e2e:local": "npx playwright test --config=e2e/playwright-local.config.js --project=chrome", - "test:e2e:updatesnapshots": "npx playwright test --config=e2e/playwright-local.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:full": "npx playwright test --config=e2e/playwright-ci.config.js", "test:perf": "npx playwright test --config=e2e/playwright-performance.config.js", @@ -101,6 +104,10 @@ "update-copyright-date": "npm run update-about-dialog-copyright && grep -lr --null --include=*.{js,scss,vue,ts,sh,html,md,frag} 'Copyright (c) 20' . | xargs -r0 perl -pi -e 's/Copyright\\s\\(c\\)\\s20\\d\\d\\-20\\d\\d/Copyright \\(c\\)\\ 2014\\-2022/gm'", "otherdoc": "node docs/gendocs.js --in docs/src --out dist/docs --suppress-toc 'docs/src/index.md|docs/src/process/index.md'", "docs": "npm run jsdoc ; npm run otherdoc", + "cov:e2e:report":"nyc report --reporter=lcovonly --report-dir=./coverage/e2e", + "cov:e2e:full:publish":"codecov --disable=gcov -f ./coverage/e2e/lcov.info -F e2e-full", + "cov:e2e:ci:publish":"codecov --disable=gcov -f ./coverage/e2e/lcov.info -F e2e-ci", + "cov:unit:publish":"codecov --disable=gcov -f ./coverage/unit/lcov.info -F unit", "prepare": "npm run build:prod" }, "repository": { diff --git a/webpack.common.js b/webpack.common.js index 38e9e8644d..1c938af845 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -94,8 +94,13 @@ const config = { { loader: 'css-loader' }, - 'resolve-url-loader', - 'sass-loader' + { + loader: 'resolve-url-loader' + }, + { + loader: 'sass-loader', + options: {sourceMap: true } + } ] }, { diff --git a/webpack.coverage.js b/webpack.coverage.js index 94766eb6c8..478428bcd3 100644 --- a/webpack.coverage.js +++ b/webpack.coverage.js @@ -34,6 +34,7 @@ config.module.rules.push({ use: { loader: 'babel-loader', options: { + // eslint-disable-next-line no-undef configFile: path.resolve(process.cwd(), 'babel.coverage.js') } }