From c56d458ecb448a060448bf5daa759ea22849a163 Mon Sep 17 00:00:00 2001 From: John Hill Date: Wed, 25 May 2022 15:45:11 -0700 Subject: [PATCH] Performance tests, notebook tests, and sharding (#5236) * Renamed test files * temp push of performance branch * comma * stash * final * rename to imagery * remove old * stash * fix name * Add plain notebook e2e and perf test * Shard and add perf to ci * Import fixtures * 3 * also off by one? * forgive me, father * update perf test name * SHARD * one mo shot * add suites * failfast * remove allure * add more testsuites * full * skip * ignore glob * headless? * skip audit to save ci time * temp push * remove allure * remove doubled test and update snapshots * update snapthos 2 * back to ci * update comments * remove notebook * updates * remove npx playwright install * Prevent deleting browsers * circleci comment * re-enable audit * Add html reporter * speed up execution * speed up CI * Add performance line to bug report * PR Comments * change dir of report * remove test * Update Tracepath. Update playwright-percy * Remove shim * Improve stability and move html report * fix space * Add more slowdown for moveObjects * Get rid of navigation event * fix missing events * review comments * last change! Co-authored-by: unlikelyzero --- .circleci/config.yml | 26 ++- .github/ISSUE_TEMPLATE/bug_report.md | 1 + e2e/playwright-ci.config.js | 13 +- e2e/playwright-local.config.js | 12 +- e2e/playwright-performance.config.js | 41 ++++ e2e/test-data/PerformanceDisplayLayout.json | 1 + e2e/test-data/PerformanceNotebook.json | 1 + e2e/tests/moveObjects.e2e.spec.js | 28 +-- e2e/tests/performance/imagery.perf.spec.js | 177 ++++++++++++++++++ .../performance/memleak-imagery.perf.spec.js | 118 ++++++++++++ e2e/tests/performance/notebook.perf.spec.js | 158 ++++++++++++++++ .../plugins/condition/condition.e2e.spec.js | 3 + .../autoscale-canvas-panned-chrome.png | Bin 15248 -> 18379 bytes .../autoscale-canvas-prepan-chrome.png | Bin 17964 -> 19897 bytes package.json | 6 +- 15 files changed, 555 insertions(+), 30 deletions(-) create mode 100644 e2e/playwright-performance.config.js create mode 100644 e2e/test-data/PerformanceDisplayLayout.json create mode 100644 e2e/test-data/PerformanceNotebook.json create mode 100644 e2e/tests/performance/imagery.perf.spec.js create mode 100644 e2e/tests/performance/memleak-imagery.perf.spec.js create mode 100644 e2e/tests/performance/notebook.perf.spec.js diff --git a/.circleci/config.yml b/.circleci/config.yml index 5422dd9f63..3268c660a6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -23,7 +23,7 @@ commands: - node/install: install-npm: true node-version: << parameters.node-version >> - - run: npm install + - run: npm install --prefer-offline --no-audit --progress=false restore_cache_cmd: description: "Custom command for restoring cache with the ability to bust cache. When BUST_CACHE is set to true, jobs will not restore cache" parameters: @@ -128,16 +128,30 @@ jobs: suite: type: string executor: pw-focal-development + parallelism: 4 steps: - build_and_install: node-version: <> - - run: npx playwright install - - run: npm run test:e2e:<> + - run: SHARD="$((${CIRCLE_NODE_INDEX}+1))"; npm run test:e2e:<> -- --shard=${SHARD}/${CIRCLE_NODE_TOTAL} - store_test_results: path: test-results/results.xml - store_artifacts: path: test-results - generate_and_store_version_and_filesystem_artifacts + perf-test: + parameters: + node-version: + type: string + executor: pw-focal-development + steps: + - build_and_install: + node-version: <> + - run: npm run test:perf + - store_test_results: + path: test-results/results.xml + - store_artifacts: + path: test-results + - generate_and_store_version_and_filesystem_artifacts workflows: overall-circleci-commit-status: #These jobs run on every commit jobs: @@ -150,10 +164,6 @@ workflows: browser: ChromeHeadless post-steps: - upload_code_covio - - unit-test: - name: node16-chrome - node-version: lts/gallium - browser: ChromeHeadless - unit-test: name: node18-chrome node-version: "18" @@ -162,6 +172,8 @@ workflows: name: e2e-ci node-version: lts/gallium suite: ci + - perf-test: + node-version: lts/gallium the-nightly: #These jobs do not run on PRs, but against master at night jobs: - unit-test: diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 747ec79bb6..2fd6b80917 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -41,6 +41,7 @@ assignees: '' - [ ] Does this impact a critical component? - [ ] Is this just a visual bug with no functional impact? - [ ] Does this block the execution of e2e tests? +- [ ] Does this have an impact on Performance? #### Additional Information diff --git a/e2e/playwright-ci.config.js b/e2e/playwright-ci.config.js index 92b55f17d8..0e8f74e8cd 100644 --- a/e2e/playwright-ci.config.js +++ b/e2e/playwright-ci.config.js @@ -9,6 +9,7 @@ const { devices } = require('@playwright/test'); const config = { retries: 1, testDir: 'tests', + testIgnore: '**/*.perf.spec.js', //Ignore performance tests and define in playwright-perfromance.config.js timeout: 60 * 1000, webServer: { command: 'npm run start', @@ -16,14 +17,15 @@ const config = { timeout: 200 * 1000, reuseExistingServer: !process.env.CI }, + maxFailures: process.env.CI ? 5 : undefined, //Limits failures to 5 to reduce CI Waste workers: 2, //Limit to 2 for CircleCI Agent use: { baseURL: 'http://localhost:8080/', headless: true, ignoreHTTPSErrors: true, - screenshot: 'on', - trace: 'on', - video: 'on' + screenshot: 'only-on-failure', + trace: 'on-first-retry', + video: 'on-first-retry' }, projects: [ { @@ -53,8 +55,11 @@ const config = { ], reporter: [ ['list'], + ['html', { + open: 'never', + outputFolder: '../test-results/html/' + }], ['junit', { outputFile: 'test-results/results.xml' }], - ['allure-playwright'], ['github'] ] }; diff --git a/e2e/playwright-local.config.js b/e2e/playwright-local.config.js index fe3145f310..124b842643 100644 --- a/e2e/playwright-local.config.js +++ b/e2e/playwright-local.config.js @@ -9,6 +9,7 @@ const { devices } = require('@playwright/test'); const config = { retries: 0, testDir: 'tests', + testIgnore: '**/*.perf.spec.js', timeout: 30 * 1000, webServer: { command: 'npm run start', @@ -22,9 +23,9 @@ const config = { baseURL: 'http://localhost:8080/', headless: false, ignoreHTTPSErrors: true, - screenshot: 'on', - trace: 'on', - video: 'on' + screenshot: 'only-on-failure', + trace: 'retain-on-failure', + video: 'retain-on-failure' }, projects: [ { @@ -54,7 +55,10 @@ const config = { ], reporter: [ ['list'], - ['allure-playwright'] + ['html', { + open: 'on-failure', + outputFolder: '../test-results' + }] ] }; diff --git a/e2e/playwright-performance.config.js b/e2e/playwright-performance.config.js new file mode 100644 index 0000000000..a718f1df03 --- /dev/null +++ b/e2e/playwright-performance.config.js @@ -0,0 +1,41 @@ +/* eslint-disable no-undef */ +// playwright.config.js +// @ts-check + +/** @type {import('@playwright/test').PlaywrightTestConfig} */ +const config = { + retries: 0, + testDir: 'tests/performance/', + timeout: 30 * 1000, + workers: 1, //Only run in serial with 1 worker + webServer: { + command: 'npm run start', + port: 8080, + timeout: 200 * 1000, + reuseExistingServer: !process.env.CI + }, + use: { + browserName: "chromium", + baseURL: 'http://localhost:8080/', + headless: Boolean(process.env.CI), //Only if running locally + ignoreHTTPSErrors: true, + screenshot: 'off', + trace: 'off', + video: 'off' + }, + projects: [ + { + name: 'chrome', + use: { + browserName: 'chromium' + } + } + ], + reporter: [ + ['list'], + ['junit', { outputFile: 'test-results/results.xml' }], + ['json', { outputFile: 'test-results/results.json' }] + ] +}; + +module.exports = config; diff --git a/e2e/test-data/PerformanceDisplayLayout.json b/e2e/test-data/PerformanceDisplayLayout.json new file mode 100644 index 0000000000..eebc7635ad --- /dev/null +++ b/e2e/test-data/PerformanceDisplayLayout.json @@ -0,0 +1 @@ +{"openmct":{"21338566-d472-4377-aed1-21b79272c8de":{"identifier":{"key":"21338566-d472-4377-aed1-21b79272c8de","namespace":""},"name":"Performance Display Layout","type":"layout","composition":[{"key":"644c2e47-2903-475f-8a4a-6be1588ee02f","namespace":""}],"configuration":{"items":[{"width":32,"height":18,"x":1,"y":1,"identifier":{"key":"644c2e47-2903-475f-8a4a-6be1588ee02f","namespace":""},"hasFrame":true,"fontSize":"default","font":"default","type":"subobject-view","id":"5aeb5a71-3149-41ed-9d8a-d34b0a18b053"}],"layoutGrid":[10,10]},"modified":1652228997384,"location":"mine","persisted":1652228997384},"644c2e47-2903-475f-8a4a-6be1588ee02f":{"identifier":{"key":"644c2e47-2903-475f-8a4a-6be1588ee02f","namespace":""},"name":"Performance Example Imagery","type":"example.imagery","configuration":{"imageLocation":"","imageLoadDelayInMilliSeconds":20000,"imageSamples":[]},"telemetry":{"values":[{"name":"Name","key":"name"},{"name":"Time","key":"utc","format":"utc","hints":{"domain":2}},{"name":"Local Time","key":"local","format":"local-format","hints":{"domain":1}},{"name":"Image","key":"url","format":"image","hints":{"image":1}},{"name":"Image Download Name","key":"imageDownloadName","format":"imageDownloadName","hints":{"imageDownloadName":1}}]},"modified":1652228997375,"location":"21338566-d472-4377-aed1-21b79272c8de","persisted":1652228997375}},"rootId":"21338566-d472-4377-aed1-21b79272c8de"} \ No newline at end of file diff --git a/e2e/test-data/PerformanceNotebook.json b/e2e/test-data/PerformanceNotebook.json new file mode 100644 index 0000000000..ae08431874 --- /dev/null +++ b/e2e/test-data/PerformanceNotebook.json @@ -0,0 +1 @@ +{"openmct":{"6d2fa9fd-f2aa-461a-a1e1-164ac44bec9d":{"identifier":{"key":"6d2fa9fd-f2aa-461a-a1e1-164ac44bec9d","namespace":""},"name":"Performance Notebook","type":"notebook","configuration":{"defaultSort":"oldest","entries":{"3e31c412-33ba-4757-8ade-e9821f6ba321":{"8c8f6035-631c-45af-8c24-786c60295335":[{"id":"entry-1652815305457","createdOn":1652815305457,"createdBy":"","text":"Existing Entry 1","embeds":[]},{"id":"entry-1652815313465","createdOn":1652815313465,"createdBy":"","text":"Existing Entry 2","embeds":[]},{"id":"entry-1652815399955","createdOn":1652815399955,"createdBy":"","text":"Existing Entry 3","embeds":[]}]}},"imageMigrationVer":"v1","pageTitle":"Page","sections":[{"id":"3e31c412-33ba-4757-8ade-e9821f6ba321","isDefault":false,"isSelected":false,"name":"Section1","pages":[{"id":"8c8f6035-631c-45af-8c24-786c60295335","isDefault":false,"isSelected":false,"name":"Page1","pageTitle":"Page"},{"id":"36555942-c9aa-439c-bbdb-0aaf50db50f5","isDefault":false,"isSelected":false,"name":"Page2","pageTitle":"Page"}],"sectionTitle":"Section"},{"id":"dab0bd1d-2c5a-405c-987f-107123d6189a","isDefault":false,"isSelected":true,"name":"Section2","pages":[{"id":"f625a86a-cb99-4898-8082-80543c8de534","isDefault":false,"isSelected":false,"name":"Page1","pageTitle":"Page"},{"id":"e77ef810-f785-42a7-942e-07e999b79c59","isDefault":false,"isSelected":true,"name":"Page2","pageTitle":"Page"}],"sectionTitle":"Section"}],"sectionTitle":"Section","type":"General","showTime":"0"},"modified":1652815915219,"location":"mine","persisted":1652815915222}},"rootId":"6d2fa9fd-f2aa-461a-a1e1-164ac44bec9d"} \ No newline at end of file diff --git a/e2e/tests/moveObjects.e2e.spec.js b/e2e/tests/moveObjects.e2e.spec.js index 81a7bc9b7a..d788192cb7 100644 --- a/e2e/tests/moveObjects.e2e.spec.js +++ b/e2e/tests/moveObjects.e2e.spec.js @@ -39,6 +39,10 @@ test.describe('Move item tests', () => { await page.locator('text=Properties Title Notes >> input[type="text"]').click(); await page.locator('text=Properties Title Notes >> input[type="text"]').fill(folder1); + + // Click on My Items in Tree. Workaround for https://github.com/nasa/openmct/issues/5184 + await page.click('form[name="mctForm"] a:has-text("My Items")'); + await Promise.all([ page.waitForNavigation(), page.locator('text=OK').click(), @@ -54,6 +58,10 @@ test.describe('Move item tests', () => { await page.locator('li.icon-folder').click(); await page.locator('text=Properties Title Notes >> input[type="text"]').click(); await page.locator('text=Properties Title Notes >> input[type="text"]').fill(folder2); + + // Click on My Items in Tree. Workaround for https://github.com/nasa/openmct/issues/5184 + await page.click('form[name="mctForm"] a:has-text("My Items")'); + await Promise.all([ page.waitForNavigation(), page.locator('text=OK').click(), @@ -72,10 +80,8 @@ test.describe('Move item tests', () => { }); await page.locator('li.icon-move').click(); await page.locator('form[name="mctForm"] >> text=My Items').click(); - await Promise.all([ - page.waitForNavigation(), - page.locator('text=OK').click() - ]); + + await page.locator('text=OK').click(); // Expect that Folder 2 is in My Items, the root folder expect(page.locator(`text=My Items >> nth=0:has(text=${folder2})`)).toBeTruthy(); @@ -90,10 +96,11 @@ test.describe('Move item tests', () => { await page.locator('li:has-text("Telemetry Table")').click(); await page.locator('text=Properties Title Notes >> input[type="text"]').click(); await page.locator('text=Properties Title Notes >> input[type="text"]').fill(telemetryTable); - await Promise.all([ - page.waitForNavigation(), - page.locator('text=OK').click() - ]); + + // Click on My Items in Tree. Workaround for https://github.com/nasa/openmct/issues/5184 + await page.click('form[name="mctForm"] a:has-text("My Items")'); + + await page.locator('text=OK').click(); // Finish editing and save Telemetry Table await page.locator('.c-button--menu.c-button--major.icon-save').click(); @@ -114,10 +121,7 @@ test.describe('Move item tests', () => { // Continue test regardless of assertion and create it in My Items await page.locator('form[name="mctForm"] >> text=My Items').click(); - await Promise.all([ - page.waitForNavigation(), - page.locator('text=OK').click() - ]); + await page.locator('text=OK').click(); // Open My Items await page.locator('text=Open MCT My Items >> span').nth(3).click(); diff --git a/e2e/tests/performance/imagery.perf.spec.js b/e2e/tests/performance/imagery.perf.spec.js new file mode 100644 index 0000000000..0e75e52af1 --- /dev/null +++ b/e2e/tests/performance/imagery.perf.spec.js @@ -0,0 +1,177 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2022, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ + +/* +This test suite is dedicated to performance tests to ensure that testability of performance +is not broken upstream on Open MCT. Any assumptions made downstream will be tested here + +TODO: + - Update resolution of performance config + - Add Performance Observer on init to push all performance marks + - Move client CDP connection to before or to a fixture + - + +*/ + +const { test, expect } = require('@playwright/test'); + +const filePath = 'e2e/test-data/PerformanceDisplayLayout.json'; + +test.describe('Performance tests', () => { + test.beforeEach(async ({ page, browser }, testInfo) => { + // Go to baseURL + await page.goto('/', { waitUntil: 'networkidle' }); + + // Click a:has-text("My Items") + await page.locator('a:has-text("My Items")').click({ + button: 'right' + }); + + // Click text=Import from JSON + await page.locator('text=Import from JSON').click(); + + // Upload Performance Display Layout.json + await page.setInputFiles('#fileElem', filePath); + + // Click text=OK + await page.locator('text=OK').click(); + + await expect(page.locator('a:has-text("Performance Display Layout Display Layout")')).toBeVisible(); + + //Create a Chrome Performance Timeline trace to store as a test artifact + console.log("\n==== Devtools: startTracing ====\n"); + await browser.startTracing(page, { + path: `${testInfo.outputPath()}-trace.json`, + screenshots: true + }); + }); + test.afterEach(async ({ page, browser}) => { + console.log("\n==== Devtools: stopTracing ====\n"); + await browser.stopTracing(); + + /* Measurement Section + / The following section includes a block of performance measurements. + */ + //Get time difference between viewlarge actionability and evaluate time + await page.evaluate(() => (window.performance.measure("machine-time-difference", "viewlarge.start", "viewLarge.start.test"))); + + //Get StartTime + const startTime = await page.evaluate(() => window.performance.timing.navigationStart); + console.log('window.performance.timing.navigationStart', startTime); + + //Get All Performance Marks + const getAllMarksJson = await page.evaluate(() => JSON.stringify(window.performance.getEntriesByType("mark"))); + const getAllMarks = JSON.parse(getAllMarksJson); + console.log('window.performance.getEntriesByType("mark")', getAllMarks); + + //Get All Performance Measures + const getAllMeasuresJson = await page.evaluate(() => JSON.stringify(window.performance.getEntriesByType("measure"))); + const getAllMeasures = JSON.parse(getAllMeasuresJson); + console.log('window.performance.getEntriesByType("measure")', getAllMeasures); + + }); + /* The following test will navigate to a previously created Performance Display Layout and measure the + / following metrics: + / - ElementResourceTiming + / - Interaction Timing + */ + test('Embedded View Large for Imagery is performant in Fixed Time', async ({ page, browser }) => { + const client = await page.context().newCDPSession(page); + // Tell the DevTools session to record performance metrics + // https://chromedevtools.github.io/devtools-protocol/tot/Performance/#method-getMetrics + await client.send('Performance.enable'); + // Go to baseURL + await page.goto('/'); + + // Search Available after Launch + await page.locator('input[type="search"]').click(); + await page.evaluate(() => window.performance.mark("search-available")); + // Fill Search input + await page.locator('input[type="search"]').fill('Performance Display Layout'); + await page.evaluate(() => window.performance.mark("search-entered")); + //Search Result Appears and is clicked + await Promise.all([ + page.waitForNavigation(), + page.locator('a:has-text("Performance Display Layout")').first().click(), + page.evaluate(() => window.performance.mark("click-search-result")) + ]); + + //Time to Example Imagery Frame loads within Display Layout + await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible'}); + //Time to Example Imagery object loads + await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible'}); + + //Get background-image url from background-image css prop + const backgroundImage = await page.locator('.c-imagery__main-image__background-image'); + let backgroundImageUrl = await backgroundImage.evaluate((el) => { + return window.getComputedStyle(el).getPropertyValue('background-image').match(/url\(([^)]+)\)/)[1]; + }); + backgroundImageUrl = backgroundImageUrl.slice(1, -1); //forgive me, padre + console.log('backgroundImageurl ' + backgroundImageUrl); + + //Get ResourceTiming of background-image jpg + const resourceTimingJson = await page.evaluate((bgImageUrl) => + JSON.stringify(window.performance.getEntriesByName(bgImageUrl).pop()), + backgroundImageUrl + ); + console.log('resourceTimingJson ' + resourceTimingJson); + + //Open Large view + await page.locator('button:has-text("Large View")').click(); //This action includes the performance.mark named 'viewLarge.start' + await page.evaluate(() => window.performance.mark("viewLarge.start.test")); //This is a mark only to compare evaluate timing + + //Time to Imagery Rendered in Large Frame + await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible'}); + await page.evaluate(() => window.performance.mark("background-image-frame")); + + //Time to Example Imagery object loads + await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible'}); + await page.evaluate(() => window.performance.mark("background-image-visible")); + + // Get Current number of images in thumbstrip + await page.waitForSelector('.c-imagery__thumb'); + const thumbCount = await page.locator('.c-imagery__thumb').count(); + console.log('number of thumbs rendered ' + thumbCount); + await page.locator('.c-imagery__thumb').last().click(); + + //Get ResourceTiming of all jpg resources + const resourceTimingJson2 = await page.evaluate(() => + JSON.stringify(window.performance.getEntriesByType('resource')) + ); + const resourceTiming = JSON.parse(resourceTimingJson2); + const jpgResourceTiming = resourceTiming.find((element) => + element.name.includes('.jpg') + ); + console.log('jpgResourceTiming ' + JSON.stringify(jpgResourceTiming)); + + // Click Close Icon + await page.locator('.c-click-icon').click(); + await page.evaluate(() => window.performance.mark("view-large-close-button")); + + //await client.send('HeapProfiler.enable'); + await client.send('HeapProfiler.collectGarbage'); + + let performanceMetrics = await client.send('Performance.getMetrics'); + console.log(performanceMetrics.metrics); + + }); +}); diff --git a/e2e/tests/performance/memleak-imagery.perf.spec.js b/e2e/tests/performance/memleak-imagery.perf.spec.js new file mode 100644 index 0000000000..86f6d68a73 --- /dev/null +++ b/e2e/tests/performance/memleak-imagery.perf.spec.js @@ -0,0 +1,118 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2022, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ + +/* +This test suite is an initial example for memory leak testing using performance. This configuration and execution must +be kept separate from the traditional performance measurements to avoid any "observer" effects associated with tracing +or profiling playwright and/or the browser. + +Based on a pattern identified in https://github.com/trentmwillis/devtools-protocol-demos/blob/master/testing-demos/memory-leak-by-heap.js +and https://github.com/paulirish/automated-chrome-profiling/issues/3 + +Best path forward: https://github.com/cowchimp/headless-devtools/blob/master/src/Memory/example.js + +*/ + +const { test, expect } = require('@playwright/test'); + +const filePath = 'e2e/test-data/PerformanceDisplayLayout.json'; + +test.describe.skip('Memory Performance tests', () => { + test.beforeEach(async ({ page, browser }, testInfo) => { + // Go to baseURL + await page.goto('/', { waitUntil: 'networkidle' }); + + // Click a:has-text("My Items") + await page.locator('a:has-text("My Items")').click({ + button: 'right' + }); + + // Click text=Import from JSON + await page.locator('text=Import from JSON').click(); + + // Upload Performance Display Layout.json + await page.setInputFiles('#fileElem', filePath); + + // Click text=OK + await page.locator('text=OK').click(); + + await expect(page.locator('a:has-text("Performance Display Layout Display Layout")')).toBeVisible(); + }); + + test.skip('Embedded View Large for Imagery is performant in Fixed Time', async ({ page, browser }) => { + + await page.goto('/', {waitUntil: 'networkidle'}); + + // To to Search Available after Launch + await page.locator('input[type="search"]').click(); + // Fill Search input + await page.locator('input[type="search"]').fill('Performance Display Layout'); + //Search Result Appears and is clicked + await Promise.all([ + page.waitForNavigation(), + page.locator('a:has-text("Performance Display Layout")').first().click() + ]); + + //Time to Example Imagery Frame loads within Display Layout + await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible'}); + //Time to Example Imagery object loads + await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible'}); + + const client = await page.context().newCDPSession(page); + await client.send('HeapProfiler.enable'); + await client.send('HeapProfiler.startSampling'); + // await client.send('HeapProfiler.collectGarbage'); + await client.send('Performance.enable'); + + let performanceMetricsBefore = await client.send('Performance.getMetrics'); + console.log(performanceMetricsBefore.metrics); + + //await client.send('Performance.disable'); + + //Open Large view + await page.locator('button:has-text("Large View")').click(); + await client.send('HeapProfiler.takeHeapSnapshot'); + + //Time to Imagery Rendered in Large Frame + await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible'}); + + //Time to Example Imagery object loads + await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible'}); + + // Click Close Icon + await page.locator('.c-click-icon').click(); + + //Time to Example Imagery Frame loads within Display Layout + await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible'}); + //Time to Example Imagery object loads + await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible'}); + + await client.send('HeapProfiler.collectGarbage'); + //await client.send('Performance.enable'); + + let performanceMetricsAfter = await client.send('Performance.getMetrics'); + console.log(performanceMetricsAfter.metrics); + + //await client.send('Performance.disable'); + + }); +}); diff --git a/e2e/tests/performance/notebook.perf.spec.js b/e2e/tests/performance/notebook.perf.spec.js new file mode 100644 index 0000000000..14c74bd32d --- /dev/null +++ b/e2e/tests/performance/notebook.perf.spec.js @@ -0,0 +1,158 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2022, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ + +/* +This test suite is dedicated to performance tests to ensure that testability of performance +is not broken upstream on Open MCT. Any assumptions made downstream will be tested here. + +TODO: + - Update resolution of performance config + - Add Performance Observer on init to push all performance marks + - Move client CDP connection to before or to a fixture + +*/ + +const { test, expect } = require('@playwright/test'); + +const notebookFilePath = 'e2e/test-data/PerformanceNotebook.json'; + +test.describe('Performance tests', () => { + test.beforeEach(async ({ page, browser }, testInfo) => { + // Go to baseURL + await page.goto('/', { waitUntil: 'networkidle' }); + + // Click a:has-text("My Items") + await page.locator('a:has-text("My Items")').click({ + button: 'right' + }); + + // Click text=Import from JSON + await page.locator('text=Import from JSON').click(); + + // Upload Performance Display Layout.json + await page.setInputFiles('#fileElem', notebookFilePath); + + // TODO Fix this + await page.locator('text=OK >> nth=1').click(); + + await expect(page.locator('a:has-text("Performance Notebook")')).toBeVisible(); + + //Create a Chrome Performance Timeline trace to store as a test artifact + console.log("\n==== Devtools: startTracing ====\n"); + await browser.startTracing(page, { + path: `${testInfo.outputPath()}-trace.json`, + screenshots: true + }); + }); + test.afterEach(async ({ page, browser}) => { + console.log("\n==== Devtools: stopTracing ====\n"); + await browser.stopTracing(); + + /* Measurement Section + / The following section includes a block of performance measurements. + */ + const startTime = await page.evaluate(() => window.performance.timing.navigationStart); + console.log('window.performance.timing.navigationStart', startTime); + + //Get All Performance Marks + const getAllMarksJson = await page.evaluate(() => JSON.stringify(window.performance.getEntriesByType("mark"))); + const getAllMarks = JSON.parse(getAllMarksJson); + console.log('window.performance.getEntriesByType("mark")', getAllMarks); + + //Get All Performance Measures + const getAllMeasuresJson = await page.evaluate(() => JSON.stringify(window.performance.getEntriesByType("measure"))); + const getAllMeasures = JSON.parse(getAllMeasuresJson); + console.log('window.performance.getEntriesByType("measure")', getAllMeasures); + + }); + /* The following test will navigate to a previously created Performance Display Layout and measure the + / following metrics: + / - ElementResourceTiming + / - Interaction Timing + */ + test('Notebook Search, Add Entry, Update Entry are performant', async ({ page, browser }) => { + const client = await page.context().newCDPSession(page); + // Tell the DevTools session to record performance metrics + // https://chromedevtools.github.io/devtools-protocol/tot/Performance/#method-getMetrics + await client.send('Performance.enable'); + // Go to baseURL + await page.goto('/'); + + // To to Search Available after Launch + await page.locator('input[type="search"]').click(); + await page.evaluate(() => window.performance.mark("search-available")); + // Fill Search input + await page.locator('input[type="search"]').fill('Performance Notebook'); + await page.evaluate(() => window.performance.mark("search-entered")); + //Search Result Appears and is clicked + await Promise.all([ + page.waitForNavigation(), + page.locator('a:has-text("Performance Notebook")').first().click(), + page.evaluate(() => window.performance.mark("click-search-result")) + ]); + + await page.waitForSelector('.c-tree__item c-tree-and-search__loading loading', {state: 'hidden'}); + await page.evaluate(() => window.performance.mark("search-spinner-gone")); + + await page.waitForSelector('.l-browse-bar__object-name', { state: 'visible'}); + await page.evaluate(() => window.performance.mark("object-title-appears")); + + await page.waitForSelector('.c-notebook__entry >> nth=0', { state: 'visible'}); + await page.evaluate(() => window.performance.mark("notebook-entry-appears")); + + // Click Add new Notebook Entry + await page.locator('.c-notebook__drag-area').click(); + await page.evaluate(() => window.performance.mark("new-notebook-entry-created")); + + // Enter Notebook Entry text + await page.locator('div.c-ne__text').last().fill('New Entry'); + await page.keyboard.press('Enter'); + await page.evaluate(() => window.performance.mark("new-notebook-entry-filled")); + + //Individual Notebook Entry Search + await page.evaluate(() => window.performance.mark("notebook-search-start")); + await page.locator('.c-notebook__search >> input').fill('Existing Entry'); + await page.evaluate(() => window.performance.mark("notebook-search-filled")); + await page.waitForSelector('text=Search Results (3)', { state: 'visible'}); + await page.evaluate(() => window.performance.mark("notebook-search-processed")); + await page.waitForSelector('.c-notebook__entry >> nth=2', { state: 'visible'}); + await page.evaluate(() => window.performance.mark("notebook-search-processed")); + + //Clear Search + await page.locator('.c-search.c-notebook__search .c-search__clear-input').click(); + await page.evaluate(() => window.performance.mark("notebook-search-processed")); + + // Hover on Last + await page.evaluate(() => window.performance.mark("new-notebook-entry-delete")); + await page.locator('div.c-ne__time-and-content').last().hover(); + await page.locator('button[title="Delete this entry"]').last().click(); + await page.locator('button:has-text("Ok")').click(); + await page.waitForSelector('.c-notebook__entry >> nth=3', { state: 'detached'}); + await page.evaluate(() => window.performance.mark("new-notebook-entry-deleted")); + + //await client.send('HeapProfiler.enable'); + await client.send('HeapProfiler.collectGarbage'); + + let performanceMetrics = await client.send('Performance.getMetrics'); + console.log(performanceMetrics.metrics); + }); +}); diff --git a/e2e/tests/plugins/condition/condition.e2e.spec.js b/e2e/tests/plugins/condition/condition.e2e.spec.js index 5b4cc14850..68f31eed01 100644 --- a/e2e/tests/plugins/condition/condition.e2e.spec.js +++ b/e2e/tests/plugins/condition/condition.e2e.spec.js @@ -42,6 +42,9 @@ test('Create new Condition Set object and store @localStorage', async ({ page, c // Click text=Condition Set await page.click('text=Condition Set'); + // Click on My Items in Tree. Workaround for https://github.com/nasa/openmct/issues/5184 + await page.click('form[name="mctForm"] a:has-text("My Items")'); + // Click text=OK await Promise.all([ page.waitForNavigation(), 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 index 3170e08516921a1c6cf2731b183455ca169fa2b4..4b546e18785da586ba2d5b5c476b0ba47b3ce330 100644 GIT binary patch literal 18379 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 SiO{j%Up22?EIfbn_WuEa-~sdi literal 15248 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 pAAp`hSSIx8@9O_cz