diff --git a/.eslintrc.cjs b/.eslintrc.cjs index e29c587925..6df907f782 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,10 +1,13 @@ const LEGACY_FILES = ['example/**']; -module.exports = { +/** @type {import('eslint').Linter.Config} */ +const config = { env: { browser: true, - es6: true, + es2024: true, jasmine: true, - amd: true + node: true, + worker: true, + serviceworker: true }, globals: { _: 'readonly' @@ -23,10 +26,11 @@ module.exports = { parser: '@babel/eslint-parser', requireConfigFile: false, allowImportExportEverywhere: true, - ecmaVersion: 2015, + ecmaVersion: 'latest', ecmaFeatures: { impliedStrict: true - } + }, + sourceType: 'module' }, rules: { 'simple-import-sort/imports': 'warn', @@ -152,7 +156,7 @@ module.exports = { cases: { pascalCase: true }, - ignore: ['^.*\\.js$'] + ignore: ['^.*\\.(js|cjs|mjs)$'] } ], 'vue/first-attribute-linebreak': 'error', @@ -179,3 +183,5 @@ module.exports = { } ] }; + +module.exports = config; diff --git a/.gitignore b/.gitignore index 65d8b17a02..d7627cd833 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,6 @@ index.html.bak .nyc_output coverage codecov + +# Don't commit MacOS screenshots +*-darwin.png diff --git a/e2e/README.md b/e2e/README.md index 65e4a9333b..ed9096b607 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -76,28 +76,30 @@ To read about how to write a good visual test, please see [How to write a great `npm run test:e2e:visual` commands will run all of the visual tests against a local instance of Open MCT. If no `PERCY_TOKEN` API key is found in the terminal or command line environment variables, no visual comparisons will be made. - - `npm run test:e2e:visual:ci` will run against every commit and PR. - - `npm run test:e2e:visual:full` will run every night with additional comparisons made for Larger Displays and with the `snow` theme. +- `npm run test:e2e:visual:ci` will run against every commit and PR. +- `npm run test:e2e:visual:full` will run every night with additional comparisons made for Larger Displays and with the `snow` theme. + #### Percy.io To make this possible, we're leveraging a 3rd party service, [Percy](https://percy.io/). This service maintains a copy of all changes, users, scm-metadata, and baselines to verify that the application looks and feels the same _unless approved by a Open MCT developer_. To request a Percy API token, please reach out to the Open MCT Dev team on GitHub. For more information, please see the official [Percy documentation](https://docs.percy.io/docs/visual-testing-basics). -At present, we are using percy with two configuration files: `./e2e/.percy.nightly.yml` and `./e2e/.percy.ci.yml`. This is mainly to reduce the number of snapshots. +At present, we are using percy with two configuration files: `./e2e/.percy.nightly.yml` and `./e2e/.percy.ci.yml`. This is mainly to reduce the number of snapshots. ### Advanced: Snapshot Testing (Not Recommended) While snapshot testing offers a precise way to detect changes in your application without relying on third-party services like Percy.io, we've found that it doesn't offer any advantages over visual testing in our use-cases. Therefore, snapshot testing is **not recommended** for further implementation. #### CI vs Manual Checks + Snapshot tests can be reliably executed in Continuous Integration (CI) environments but lack the manual oversight provided by visual testing platforms like Percy.io. This means they may miss issues that a human reviewer could catch during manual checks. #### Example + A single visual test assertion in Percy.io can be executed across 10 different browser and resolution combinations without additional setup, providing comprehensive testing with minimal configuration. In contrast, a snapshot test is restricted to a single OS and browser resolution, requiring more effort to achieve the same level of coverage. - #### Further Reading -For those interested in the mechanics of snapshot testing with Playwright, you can refer to the [Playwright Snapshots Documentation](https://playwright.dev/docs/test-snapshots). However, keep in mind that we do not recommend using this approach. +For those interested in the mechanics of snapshot testing with Playwright, you can refer to the [Playwright Snapshots Documentation](https://playwright.dev/docs/test-snapshots). However, keep in mind that we do not recommend using this approach. #### Open MCT's implementation @@ -118,14 +120,6 @@ When the `@snapshot` tests fail, they will need to be evaluated to determine if To compare a snapshot, run a test and open the html report with the 'Expected' vs 'Actual' screenshot. If the actual screenshot is preferred, then the source-controlled 'Expected' snapshots will need to be updated with the following scripts. -MacOS - -``` -npm run test:e2e:updatesnapshots -``` - -Linux/CI - ```sh // Replace {X.X.X} with the current Playwright version // from our package.json or circleCI configuration file @@ -335,9 +329,11 @@ We have a Mission-need to support iPad and mobile devices. To run our test suite In general, our test suite is not designed to run against mobile devices as the mobile experience is a focused version of the application. Core functionality is missing (chiefly the 'Create' button). To bypass the object creation, we leverage the `storageState` properties for starting the mobile tests with localstorage. For now, the mobile tests will exist in the /tests/mobile/ suites and be executed with the + ```sh npm run test:e2e:mobile ``` + command. #### **Skipping or executing tests based on browser, os, and/os browser version:** @@ -377,6 +373,7 @@ In general, strive to test only through the UI as a user would. As stated in the By adhering to this principle, we can create tests that are both robust and reflective of actual user experiences. #### How to make tests robust to function in other contexts (VISTA, COUCHDB, YAMCS, VIPER, etc.) + 1. Leverage the use of `appActions.js` methods such as `createDomainObjectWithDefaults()`. This ensures that your tests will create unique instances of objects for your test to interact with. 1. Do not assert on the order or structure of objects available unless you created them yourself. These tests may be used against a persistent datastore like couchdb with many objects in the tree. 1. Do not search for your created objects. Open MCT does not performance uniqueness checks so it's possible that your tests will break when run twice. @@ -384,6 +381,7 @@ By adhering to this principle, we can create tests that are both robust and refl 1. Leverage `await page.goto('./', { waitUntil: 'domcontentloaded' });` instead of `{ waitUntil: 'networkidle' }`. Tests run against deployments with websockets often have issues with the networkidle detection. #### How to make tests faster and more resilient + 1. Avoid app interaction when possible. The best way of doing this is to navigate directly by URL: ```js @@ -396,10 +394,11 @@ By adhering to this principle, we can create tests that are both robust and refl 1. Leverage `await page.goto('./', { waitUntil: 'domcontentloaded' });` - Initial navigation should _almost_ always use the `{ waitUntil: 'domcontentloaded' }` option. - 1. Avoid repeated setup to test a single assertion. Write longer tests with multiple soft assertions. + 1. Avoid repeated setup to test a single assertion. Write longer tests with multiple soft assertions. This ensures that your changes will be picked up with large refactors. ##### Utilizing LocalStorage + 1. In order to save test runtime in the case of tests that require a decent amount of initial setup (such as in the case of testing complex displays), you may use [Playwright's `storageState` feature](https://playwright.dev/docs/api/class-browsercontext#browser-context-storage-state) to generate and load localStorage states. 1. To generate a localStorage state to be used in a test: - Add an e2e test to our generateLocalStorageData suite which sets the initial state (creating/configuring objects, etc.), saving it in the `test-data` folder: @@ -420,7 +419,6 @@ By adhering to this principle, we can create tests that are both robust and refl }); ``` - ### How to write a great test - Avoid using css locators to find elements to the page. Use modern web accessible locators like `getByRole` @@ -436,7 +434,7 @@ By adhering to this principle, we can create tests that are both robust and refl await notesInput.fill(testNotes); ``` - #### How to Write a Great Visual Test +#### How to Write a Great Visual Test 1. **Look for the Unknown Unknowns**: Avoid asserting on specific differences in the visual diff. Visual tests are most effective for identifying unknown unknowns. @@ -445,23 +443,27 @@ By adhering to this principle, we can create tests that are both robust and refl 3. **Expect the Unexpected**: Use functional expect statements only to verify assumptions about the state between steps. A great visual test doesn't fail during the test itself, but rather when changes are reviewed in Percy.io. 4. **Control Variability**: Account for variations inherent in working with time-based telemetry and clocks. - - Utilize `percyCSS` to ignore time-based elements. For more details, consult our [percyCSS file](./.percy.ci.yml). - - Use Open MCT's fixed-time mode unless explicitly testing realtime clock - - Employ the `createExampleTelemetryObject` appAction to source telemetry and specify a `name` to avoid autogenerated names. - - Avoid creating objects with a time component like timers and clocks. + +- Utilize `percyCSS` to ignore time-based elements. For more details, consult our [percyCSS file](./.percy.ci.yml). +- Use Open MCT's fixed-time mode unless explicitly testing realtime clock +- Employ the `createExampleTelemetryObject` appAction to source telemetry and specify a `name` to avoid autogenerated names. +- Avoid creating objects with a time component like timers and clocks. 5. **Hide the Tree and Inspector**: Generally, your test will not require comparisons involving the tree and inspector. These aspects are covered in component-specific tests (explained below). To exclude them from the comparison by default, navigate to the root of the main view with the tree and inspector hidden: - `await page.goto('./#/browse/mine?hideTree=true&hideInspector=true')` 6. **Component-Specific Tests**: If you wish to focus on a particular component, use the `/visual-a11y/component/` folder and limit the scope of the comparison to that component. For instance: + ```js await percySnapshot(page, `Tree Pane w/ single level expanded (theme: ${theme})`, { scope: treePane }); ``` + - Note: The `scope` variable can be any valid CSS selector. 7. **Write many `percySnapshot` commands in a single test**: In line with our approach to longer functional tests, we recommend that many test percySnapshots are taken in a single test. For instance: + ```js // await percySnapshot(page, `Before object expanded (theme: ${theme})`); @@ -511,6 +513,7 @@ test.describe('foo test suite', () => { }); }); ``` + More info and options for `overrideClock` can be found in [baseFixtures.js](baseFixtures.js) - Working with multiple pages @@ -539,7 +542,6 @@ const key = getFirstKeyFromOpenMctJson(jsonData); expect(jsonData.openmct[key]).toHaveProperty('name', 'e2e folder'); ``` - ### Reporting Test Reporting is done through official Playwright reporters and the CI Systems which execute them. @@ -615,6 +617,7 @@ A single e2e test in Open MCT is extended to run: ### Writing Tests Playwright provides 3 supported methods of debugging and authoring tests: + - A 'watch mode' for running tests locally and debugging on the fly - A 'debug mode' for debugging tests and writing assertions against tests - A 'VSCode plugin' for debugging tests within the VSCode IDE. diff --git a/e2e/avpFixtures.js b/e2e/avpFixtures.js index 7328781eec..68c0f0df3d 100644 --- a/e2e/avpFixtures.js +++ b/e2e/avpFixtures.js @@ -36,27 +36,67 @@ import AxeBuilder from '@axe-core/playwright'; import fs from 'fs'; import path from 'path'; +import { fileURLToPath } from 'url'; import { expect, test } from './pluginFixtures.js'; - // Constants for repeated values -const TEST_RESULTS_DIR = './test-results'; +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const TEST_RESULTS_DIR = path.join(__dirname, './test-results'); + +const extendedTest = test.extend({ + /** + * Overrides the default screenshot function to apply default options that should apply to all + * screenshots taken in the AVP tests. + * + * @param {import('@playwright/test').PlaywrightTestArgs} args - The Playwright test arguments. + * @param {Function} use - The function to use the page object. + * Defaults: + * - Disables animations + * - Masks the clock indicator + * - Masks the time conductor last update time in realtime mode + * - Masks the time conductor start bounds in fixed mode + * - Masks the time conductor end bounds in fixed mode + */ + page: async ({ page }, use) => { + const playwrightScreenshot = page.screenshot; + + /** + * Override the screenshot function to always mask a given set of locators which will always + * show variance across screenshots. Defaults may be overridden by passing in options to the + * screenshot function. + * @param {import('@playwright/test').PageScreenshotOptions} options - The options for the screenshot. + * @returns {Promise} Returns the screenshot as a buffer. + */ + page.screenshot = async function (options = {}) { + const mask = [ + this.getByLabel('Clock Indicator'), // Mask the clock indicator + this.getByLabel('Last update'), // Mask the time conductor last update time in realtime mode + this.getByLabel('Start bounds'), // Mask the time conductor start bounds in fixed mode + this.getByLabel('End bounds') // Mask the time conductor end bounds in fixed mode + ]; + + const result = await playwrightScreenshot.call(this, { + animations: 'disabled', + mask, + ...options // Pass through or override any options + }); + return result; + }; + + await use(page); + } +}); /** * Scans for accessibility violations on a page and writes a report to disk if violations are found. * Automatically asserts that no violations should be present. * - * @typedef {Object} GenerateReportOptions - * @property {string} [reportName] - The name for the report file. - * * @param {import('playwright').Page} page - The page object from Playwright. * @param {string} testCaseName - The name of the test case. - * @param {GenerateReportOptions} [options={}] - The options for the report generation. - * - * @returns {Promise} Returns the accessibility scan results if violations are found, - * otherwise returns null. + * @param {{ reportName?: string }} [options={}] - The options for the report generation. + * @returns {Promise} Returns the accessibility scan results if violations are found, otherwise returns null. */ -/* eslint-disable no-undef */ + export async function scanForA11yViolations(page, testCaseName, options = {}) { const builder = new AxeBuilder({ page }); builder.withTags(['wcag2aa']); @@ -93,4 +133,4 @@ export async function scanForA11yViolations(page, testCaseName, options = {}) { } } -export { expect, test }; +export { expect, extendedTest as test }; diff --git a/e2e/baseFixtures.js b/e2e/baseFixtures.js index e912e90da5..681ab4319d 100644 --- a/e2e/baseFixtures.js +++ b/e2e/baseFixtures.js @@ -1,4 +1,3 @@ -/* eslint-disable no-undef */ /***************************************************************************** * Open MCT, Copyright (c) 2014-2024, United States Government * as represented by the Administrator of the National Aeronautics and Space @@ -111,6 +110,40 @@ const extendedTest = test.extend({ scope: 'test' } ], + /** + * Exposes a function to manually tick the clock. This is useful when overriding the clock to not + * tick (`shouldAdvanceTime: false`) for visual tests, as events such as re-renders and router params + * updates are clock-driven and must be manually ticked. + * + * Usage: + * ```js + * test.describe('Manual Clock Tick', () => { + * test.use({ + * clockOptions: { + * now: MISSION_TIME, // Set to the desired time + * shouldAdvanceTime: false // Clock overridden to no longer tick + * } + * }); + * test('Visual - Manual Clock Tick', async ({ page, tick }) => { + * // Tick the clock 2 seconds in the future + * await tick(2000); + * }); + * }); + * ``` + * + * @param {Object} param0 + * @param {import('@playwright/test').Page} param0.page + * @param {import('@playwright/test').Use} param0.use + */ + tick: async ({ page }, use) => { + // eslint-disable-next-line func-style + const tick = async (milliseconds) => { + await page.evaluate((_milliseconds) => { + window.__clock.tick(_milliseconds); + }, milliseconds); + }; + await use(tick); + }, /** * Extends the base context class to add codecoverage shim. * @see {@link https://github.com/mxschmitt/playwright-test-coverage Github Project} @@ -154,17 +187,13 @@ const extendedTest = test.extend({ // function in the generatorWorker context. This is necessary // to ensure that example telemetry data is generated for the new clock time. if (clockOptions?.now !== undefined) { - page.on( - 'worker', - (worker) => { - if (worker.url().includes('generatorWorker')) { - worker.evaluate((time) => { - self.Date.now = () => time; - }); - } - }, - clockOptions.now - ); + page.on('worker', (worker) => { + if (worker.url().includes('generatorWorker')) { + worker.evaluate((time) => { + self.Date.now = () => time; + }, clockOptions.now); + } + }); } // Capture any console errors during test execution diff --git a/e2e/constants.js b/e2e/constants.js index cd513b1217..d6fac901be 100644 --- a/e2e/constants.js +++ b/e2e/constants.js @@ -1,4 +1,3 @@ -/* eslint-disable prettier/prettier */ /** * Constants which may be used across all e2e tests. */ @@ -8,12 +7,30 @@ * - Used for overriding the browser clock in tests. */ export const MISSION_TIME = 1732413600000; // Saturday, November 23, 2024 6:00:00 PM GMT-08:00 (Thanksgiving Dinner Time) +// Subtracting 30 minutes from MISSION_TIME +export const MISSION_TIME_FIXED_START = 1732413600000 - 1800000; // 1732411800000 +// Adding 1 minute to MISSION_TIME +export const MISSION_TIME_FIXED_END = 1732413600000 + 60000; // 1732413660000 /** * URL Constants - * - This is the URL that the browser will be directed to when running visual tests. This URL - * - hides the tree and inspector to prevent visual noise - * - sets the time bounds to a fixed range + * These constants are used for initial navigation in visual tests, in either fixed or realtime mode. + * They navigate to the 'My Items' folder at MISSION_TIME. + * They set the following url parameters: + * - tc.mode - The time conductor mode ('fixed' or 'local') + * - tc.startBound - The time conductor start bound (when in fixed mode) + * - tc.endBound - The time conductor end bound (when in fixed mode) + * - tc.startDelta - The time conductor start delta (when in realtime mode) + * - tc.endDelta - The time conductor end delta (when in realtime mode) + * - tc.timeSystem - The time conductor time system ('utc') + * - view - The view to display ('grid') + * - hideInspector - Whether to hide the inspector (true) + * - hideTree - Whether to hide the tree (true) + * @typedef {string} VisualUrl */ -export const VISUAL_URL = - './#/browse/mine?tc.mode=fixed&tc.startBound=1693592063607&tc.endBound=1693593893607&tc.timeSystem=utc&view=grid&hideInspector=true&hideTree=true'; + +/** @type {VisualUrl} */ +export const VISUAL_FIXED_URL = `./#/browse/mine?tc.mode=fixed&tc.startBound=${MISSION_TIME_FIXED_START}&tc.endBound=${MISSION_TIME_FIXED_END}&tc.timeSystem=utc&view=grid&hideInspector=true&hideTree=true`; +/** @type {VisualUrl} */ +export const VISUAL_REALTIME_URL = + './#/browse/mine?tc.mode=local&tc.timeSystem=utc&view=grid&tc.startDelta=1800000&tc.endDelta=30000&hideTree=true&hideInspector=true'; diff --git a/e2e/helper/notebookUtils.js b/e2e/helper/notebookUtils.js index de7177af22..f4c885532f 100644 --- a/e2e/helper/notebookUtils.js +++ b/e2e/helper/notebookUtils.js @@ -68,7 +68,6 @@ async function commitEntry(page) { * @param {import('@playwright/test').Page} page */ async function startAndAddRestrictedNotebookObject(page) { - // eslint-disable-next-line no-undef await page.addInitScript({ path: fileURLToPath(new URL('./addInitRestrictedNotebook.js', import.meta.url)) }); diff --git a/e2e/playwright-visual-a11y.config.js b/e2e/playwright-visual-a11y.config.js index e0f6810f98..5a80cffedc 100644 --- a/e2e/playwright-visual-a11y.config.js +++ b/e2e/playwright-visual-a11y.config.js @@ -1,4 +1,3 @@ -/* eslint-disable no-undef */ // playwright.config.js // @ts-check diff --git a/e2e/pluginFixtures.js b/e2e/pluginFixtures.js index 83ceeb05fc..ea46a19b31 100644 --- a/e2e/pluginFixtures.js +++ b/e2e/pluginFixtures.js @@ -1,4 +1,3 @@ -/* eslint-disable no-undef */ /***************************************************************************** * Open MCT, Copyright (c) 2014-2024, United States Government * as represented by the Administrator of the National Aeronautics and Space @@ -123,7 +122,6 @@ const extendedTest = test.extend({ theme: [theme, { option: true }], // eslint-disable-next-line no-shadow page: async ({ page, theme }, use, testInfo) => { - // eslint-disable-next-line playwright/no-conditional-in-test if (theme === 'snow') { //inject snow theme await page.addInitScript({ diff --git a/e2e/tests/framework/baseFixtures.e2e.spec.js b/e2e/tests/framework/baseFixtures.e2e.spec.js index 9edf08f42b..133f5f5604 100644 --- a/e2e/tests/framework/baseFixtures.e2e.spec.js +++ b/e2e/tests/framework/baseFixtures.e2e.spec.js @@ -26,11 +26,12 @@ relates to how we've extended it (i.e. ./e2e/baseFixtures.js) and assumptions ma (`npm start` and ./e2e/webpack-dev-middleware.js) */ -import { test } from '../../baseFixtures.js'; +import { expect, test } from '../../baseFixtures.js'; +import { MISSION_TIME } from '../../constants.js'; test.describe('baseFixtures tests', () => { //Skip this test for now https://github.com/nasa/openmct/issues/6785 - test.fixme('Verify that tests fail if console.error is thrown', async ({ page }) => { + test('Verify that tests fail if console.error is thrown', async ({ page }) => { test.fail(); //Go to baseURL await page.goto('./', { waitUntil: 'domcontentloaded' }); @@ -52,3 +53,27 @@ test.describe('baseFixtures tests', () => { ]); }); }); + +test.describe('baseFixtures tests @clock', () => { + test.use({ + clockOptions: { + now: MISSION_TIME, + shouldAdvanceTime: false + } + }); + + test.beforeEach(async ({ page }) => { + await page.goto('./', { waitUntil: 'domcontentloaded' }); + }); + + test('Can use clockOptions and tick fixtures to control the clock', async ({ page, tick }) => { + let time = await page.evaluate(() => new Date().getTime()); + expect(time).toBe(MISSION_TIME); + await tick(1000); + time = await page.evaluate(() => new Date().getTime()); + expect(time).toBe(MISSION_TIME + 1000 * 1); + await tick(1000); + time = await page.evaluate(() => new Date().getTime()); + expect(time).toBe(MISSION_TIME + 1000 * 2); + }); +}); diff --git a/e2e/tests/functional/plugins/imagery/exampleImagery.e2e.spec.js b/e2e/tests/functional/plugins/imagery/exampleImagery.e2e.spec.js index 95e31acdd1..9a7df4136c 100644 --- a/e2e/tests/functional/plugins/imagery/exampleImagery.e2e.spec.js +++ b/e2e/tests/functional/plugins/imagery/exampleImagery.e2e.spec.js @@ -24,7 +24,7 @@ This test suite is dedicated to tests which verify the basic operations surrounding imagery, but only assume that example imagery is present. */ -/* globals process */ + import { createDomainObjectWithDefaults, setRealTimeMode } from '../../../../appActions.js'; import { waitForAnimations } from '../../../../baseFixtures.js'; import { expect, test } from '../../../../pluginFixtures.js'; diff --git a/e2e/tests/functional/plugins/inspectorDataVisualization/numericData.e2e.spec.js b/e2e/tests/functional/plugins/inspectorDataVisualization/numericData.e2e.spec.js index 7166112b05..d8ff8df76e 100644 --- a/e2e/tests/functional/plugins/inspectorDataVisualization/numericData.e2e.spec.js +++ b/e2e/tests/functional/plugins/inspectorDataVisualization/numericData.e2e.spec.js @@ -27,7 +27,6 @@ import { expect, test } from '../../../../pluginFixtures.js'; test.describe('Testing numeric data with inspector data visualization (i.e., data pivoting)', () => { test.beforeEach(async ({ page }) => { - // eslint-disable-next-line no-undef await page.addInitScript({ path: fileURLToPath( new URL('../../../../helper/addInitDataVisualization.js', import.meta.url) diff --git a/e2e/tests/functional/plugins/notebook/notebook.e2e.spec.js b/e2e/tests/functional/plugins/notebook/notebook.e2e.spec.js index e412392b6d..fb41968d33 100644 --- a/e2e/tests/functional/plugins/notebook/notebook.e2e.spec.js +++ b/e2e/tests/functional/plugins/notebook/notebook.e2e.spec.js @@ -277,7 +277,6 @@ test.describe('Notebook entry tests', () => { // Create Notebook with URL Whitelist let notebookObject; test.beforeEach(async ({ page }) => { - // eslint-disable-next-line no-undef await page.addInitScript({ path: fileURLToPath(new URL('../../../../helper/addInitNotebookWithUrls.js', import.meta.url)) }); diff --git a/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-darwin b/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-darwin deleted file mode 100644 index 072218f3f3..0000000000 Binary files a/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-darwin and /dev/null differ diff --git a/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-darwin.png b/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-darwin.png deleted file mode 100644 index abdb37b8dc..0000000000 Binary files a/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-panned-chrome-darwin.png and /dev/null differ diff --git a/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-darwin b/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-darwin deleted file mode 100644 index b6c7ba2fcf..0000000000 Binary files a/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-darwin and /dev/null differ diff --git a/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-darwin.png b/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-darwin.png deleted file mode 100644 index 148089b4bd..0000000000 Binary files a/e2e/tests/functional/plugins/plot/autoscale.e2e.spec.js-snapshots/autoscale-canvas-prepan-chrome-darwin.png and /dev/null differ diff --git a/e2e/tests/functional/plugins/timeConductor/timeConductor.e2e.spec.js b/e2e/tests/functional/plugins/timeConductor/timeConductor.e2e.spec.js index 37dd7d1986..8d3822852f 100644 --- a/e2e/tests/functional/plugins/timeConductor/timeConductor.e2e.spec.js +++ b/e2e/tests/functional/plugins/timeConductor/timeConductor.e2e.spec.js @@ -48,7 +48,7 @@ test.describe('Time conductor operations', () => { await setTimeConductorBounds(page, startDate); // Bring up the time conductor popup - const timeConductorMode = await page.locator('.c-compact-tc'); + const timeConductorMode = page.locator('.c-compact-tc'); await timeConductorMode.click(); const startDateLocator = page.locator('input[type="text"]').first(); const endDateLocator = page.locator('input[type="text"]').nth(2); diff --git a/e2e/tests/performance/memory/navigation.memory.perf.spec.js b/e2e/tests/performance/memory/navigation.memory.perf.spec.js index 17074fd8e0..cb74b6a11a 100644 --- a/e2e/tests/performance/memory/navigation.memory.perf.spec.js +++ b/e2e/tests/performance/memory/navigation.memory.perf.spec.js @@ -299,7 +299,6 @@ test.describe('Navigation memory leak is not detected in', () => { // for detecting memory leaks. await page.evaluate(() => { window.gcPromise = new Promise((resolve) => { - // eslint-disable-next-line no-undef window.fr = new FinalizationRegistry(resolve); window.fr.register( window.openmct.layout.$refs.browseObject.$refs.objectViewWrapper.firstChild, diff --git a/e2e/tests/visual-a11y/a11y.visual.spec.js b/e2e/tests/visual-a11y/a11y.visual.spec.js index 51969ff958..d981b3a8a8 100644 --- a/e2e/tests/visual-a11y/a11y.visual.spec.js +++ b/e2e/tests/visual-a11y/a11y.visual.spec.js @@ -21,14 +21,13 @@ *****************************************************************************/ import { test } from '../../avpFixtures.js'; -import { VISUAL_URL } from '../../constants.js'; +import { VISUAL_FIXED_URL } from '../../constants.js'; test.describe('a11y - Default', () => { test.beforeEach(async ({ page }) => { - await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' }); + await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' }); }); test('main view', async ({ page }, testInfo) => { - await page.goto('./'); //Skipping for https://github.com/nasa/openmct/issues/7421 //await scanForA11yViolations(page, testInfo.title); }); diff --git a/e2e/tests/visual-a11y/components/about.visual.spec.js b/e2e/tests/visual-a11y/components/about.visual.spec.js index 70ed07b86f..0701ee21b7 100644 --- a/e2e/tests/visual-a11y/components/about.visual.spec.js +++ b/e2e/tests/visual-a11y/components/about.visual.spec.js @@ -27,12 +27,12 @@ Tests the branding associated with the default deployment. At least the about mo import percySnapshot from '@percy/playwright'; import { expect, scanForA11yViolations, test } from '../../../avpFixtures.js'; -import { VISUAL_URL } from '../../../constants.js'; +import { VISUAL_FIXED_URL } from '../../../constants.js'; test.describe('Visual - Branding @a11y', () => { test.beforeEach(async ({ page }) => { //Go to baseURL and Hide Tree - await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' }); + await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' }); }); test('Visual - About Modal', async ({ page, theme }) => { diff --git a/e2e/tests/visual-a11y/components/header.visual.spec.js b/e2e/tests/visual-a11y/components/header.visual.spec.js index f9af410dcd..0c3d44c8a8 100644 --- a/e2e/tests/visual-a11y/components/header.visual.spec.js +++ b/e2e/tests/visual-a11y/components/header.visual.spec.js @@ -28,7 +28,7 @@ import percySnapshot from '@percy/playwright'; import { fileURLToPath } from 'url'; import { expect, test } from '../../../avpFixtures.js'; -import { VISUAL_URL } from '../../../constants.js'; +import { VISUAL_FIXED_URL } from '../../../constants.js'; //Declare the component scope of the visual test for Percy const header = '.l-shell__head'; @@ -36,7 +36,7 @@ const header = '.l-shell__head'; test.describe('Visual - Header @a11y', () => { test.beforeEach(async ({ page }) => { //Go to baseURL and Hide Tree - await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' }); + await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' }); // Wait for status bar to load await expect( page.getByRole('status', { diff --git a/e2e/tests/visual-a11y/components/inspector.visual.spec.js b/e2e/tests/visual-a11y/components/inspector.visual.spec.js index edd64aec74..efe9d65cab 100644 --- a/e2e/tests/visual-a11y/components/inspector.visual.spec.js +++ b/e2e/tests/visual-a11y/components/inspector.visual.spec.js @@ -23,14 +23,14 @@ import percySnapshot from '@percy/playwright'; import { test } from '../../../avpFixtures.js'; -import { MISSION_TIME, VISUAL_URL } from '../../../constants.js'; +import { MISSION_TIME, VISUAL_FIXED_URL } from '../../../constants.js'; //Declare the scope of the visual test const inspectorPane = '.l-shell__pane-inspector'; test.describe('Visual - Inspector @ally @clock', () => { test.beforeEach(async ({ page }) => { - await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' }); + await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' }); }); test.use({ storageState: './e2e/test-data/overlay_plot_with_delay_storage.json', diff --git a/e2e/tests/visual-a11y/components/timeConductor.visual.spec.js b/e2e/tests/visual-a11y/components/timeConductor.visual.spec.js new file mode 100644 index 0000000000..eef1114e40 --- /dev/null +++ b/e2e/tests/visual-a11y/components/timeConductor.visual.spec.js @@ -0,0 +1,116 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2024, 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. + *****************************************************************************/ + +/* + Tests the visual appearance of the Time Conductor component +*/ + +import { expect, test } from '../../../avpFixtures.js'; +import { + MISSION_TIME, + MISSION_TIME_FIXED_END, + MISSION_TIME_FIXED_START, + VISUAL_REALTIME_URL +} from '../../../constants.js'; + +test.describe('Visual - Time Conductor', () => { + test.use({ + clockOptions: { + now: MISSION_TIME, + shouldAdvanceTime: false + } + }); + test.beforeEach(async ({ page }) => { + await page.goto('./', { waitUntil: 'domcontentloaded' }); + }); + + // FIXME: checking for a11y violations times out. Might have something to do with the frozen clock. + // test.afterEach(async ({ page }, testInfo) => { + // await scanForA11yViolations(page, testInfo.title); + // }); + + test('Visual - Time Conductor (Fixed time) @clock @snapshot', async ({ page }) => { + // Navigate to a specific view that uses the Time Conductor in Fixed Time mode with inspect and browse panes collapsed + await page.goto( + `./#/browse/mine?tc.mode=fixed&tc.startBound=${MISSION_TIME_FIXED_START}&tc.endBound=${MISSION_TIME_FIXED_END}&tc.timeSystem=utc&view=grid&hideInspector=true&hideTree=true`, + { + waitUntil: 'domcontentloaded' + } + ); + + // Take a snapshot for comparison + const snapshot = await page.screenshot({ + mask: [] + }); + expect(snapshot).toMatchSnapshot('time-conductor-fixed-time.png'); + }); + + test('Visual - Time Conductor (Realtime) @clock @snapshot', async ({ page }) => { + // Navigate to a specific view that uses the Time Conductor in Fixed Time mode with inspect and browse panes collapsed + await page.goto(VISUAL_REALTIME_URL, { + waitUntil: 'domcontentloaded' + }); + + const mask = []; + + // Take a snapshot for comparison + const snapshot = await page.screenshot({ + mask + }); + expect(snapshot).toMatchSnapshot('time-conductor-realtime.png'); + }); + test( + 'Visual - Time Conductor Axis Resized @clock @snapshot', + { annotation: [{ type: 'issue', description: 'https://github.com/nasa/openmct/issues/7623' }] }, + async ({ page, tick }) => { + const VISUAL_REALTIME_WITH_PANES = VISUAL_REALTIME_URL.replace( + 'hideTree=true', + 'hideTree=false' + ).replace('hideInspector=true', 'hideInspector=false'); + // Navigate to a specific view that uses the Time Conductor in Fixed Time mode with inspect + await page.goto(VISUAL_REALTIME_WITH_PANES, { + waitUntil: 'domcontentloaded' + }); + + // Set the time conductor to fixed time mode + await page.getByLabel('Time Conductor Mode').click(); + await page.getByLabel('Time Conductor Mode Menu').click(); + await page.getByLabel('Fixed Timespan').click(); + await page.getByLabel('Submit time bounds').click(); + + // Collapse the inspect and browse panes to trigger a resize of the conductor axis + await page.getByLabel('Collapse Inspect Pane').click(); + await page.getByLabel('Collapse Browse Pane').click(); + + // manually tick the clock to trigger the resize / re-render + await tick(1000 * 2); + + const mask = []; + + // Take a snapshot for comparison + const snapshot = await page.screenshot({ + mask + }); + expect(snapshot).toMatchSnapshot('time-conductor-axis-resized.png'); + } + ); +}); diff --git a/e2e/tests/visual-a11y/components/timeConductor.visual.spec.js-snapshots/time-conductor-axis-resized-chrome-linux.png b/e2e/tests/visual-a11y/components/timeConductor.visual.spec.js-snapshots/time-conductor-axis-resized-chrome-linux.png new file mode 100644 index 0000000000..b86cc7eade Binary files /dev/null and b/e2e/tests/visual-a11y/components/timeConductor.visual.spec.js-snapshots/time-conductor-axis-resized-chrome-linux.png differ diff --git a/e2e/tests/visual-a11y/components/timeConductor.visual.spec.js-snapshots/time-conductor-fixed-time-chrome-linux.png b/e2e/tests/visual-a11y/components/timeConductor.visual.spec.js-snapshots/time-conductor-fixed-time-chrome-linux.png new file mode 100644 index 0000000000..7dd0a84073 Binary files /dev/null and b/e2e/tests/visual-a11y/components/timeConductor.visual.spec.js-snapshots/time-conductor-fixed-time-chrome-linux.png differ diff --git a/e2e/tests/visual-a11y/components/timeConductor.visual.spec.js-snapshots/time-conductor-realtime-chrome-linux.png b/e2e/tests/visual-a11y/components/timeConductor.visual.spec.js-snapshots/time-conductor-realtime-chrome-linux.png new file mode 100644 index 0000000000..b7b480324d Binary files /dev/null and b/e2e/tests/visual-a11y/components/timeConductor.visual.spec.js-snapshots/time-conductor-realtime-chrome-linux.png differ diff --git a/e2e/tests/visual-a11y/components/tree.visual.spec.js b/e2e/tests/visual-a11y/components/tree.visual.spec.js index e0671336ea..5eb2bba1ca 100644 --- a/e2e/tests/visual-a11y/components/tree.visual.spec.js +++ b/e2e/tests/visual-a11y/components/tree.visual.spec.js @@ -23,7 +23,7 @@ import percySnapshot from '@percy/playwright'; import { createDomainObjectWithDefaults, expandTreePaneItemByName } from '../../../appActions.js'; -import { VISUAL_URL } from '../../../constants.js'; +import { VISUAL_FIXED_URL } from '../../../constants.js'; import { test } from '../../../pluginFixtures.js'; //Declare the scope of the visual test @@ -32,7 +32,7 @@ const treePane = "[role=tree][aria-label='Main Tree']"; test.describe('Visual - Tree Pane', () => { test('Tree pane in various states', async ({ page, theme, openmctConfig }) => { const { myItemsFolderName } = openmctConfig; - await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' }); + await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' }); //Open Tree await page.getByRole('button', { name: 'Browse' }).click(); diff --git a/e2e/tests/visual-a11y/controlledClock.visual.spec.js b/e2e/tests/visual-a11y/controlledClock.visual.spec.js index 6e25b2e980..ad832b841e 100644 --- a/e2e/tests/visual-a11y/controlledClock.visual.spec.js +++ b/e2e/tests/visual-a11y/controlledClock.visual.spec.js @@ -27,12 +27,12 @@ clockOptions plugin fixture. import percySnapshot from '@percy/playwright'; -import { MISSION_TIME, VISUAL_URL } from '../../constants.js'; +import { MISSION_TIME, VISUAL_FIXED_URL } from '../../constants.js'; import { expect, test } from '../../pluginFixtures.js'; test.describe('Visual - Controlled Clock @clock', () => { test.beforeEach(async ({ page }) => { - await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' }); + await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' }); }); test.use({ storageState: './e2e/test-data/overlay_plot_with_delay_storage.json', @@ -43,7 +43,7 @@ test.describe('Visual - Controlled Clock @clock', () => { }); test('Overlay Plot Loading Indicator @localStorage', async ({ page, theme }) => { - await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' }); + await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' }); await page .getByRole('gridcell', { hasText: 'Overlay Plot with 5s Delay Overlay Plot' }) .click(); diff --git a/e2e/tests/visual-a11y/defaultPlugins.visual.spec.js b/e2e/tests/visual-a11y/defaultPlugins.visual.spec.js index cd54fcf0a8..ee90b6cc13 100644 --- a/e2e/tests/visual-a11y/defaultPlugins.visual.spec.js +++ b/e2e/tests/visual-a11y/defaultPlugins.visual.spec.js @@ -30,11 +30,11 @@ import percySnapshot from '@percy/playwright'; import { createDomainObjectWithDefaults } from '../../appActions.js'; import { expect, scanForA11yViolations, test } from '../../avpFixtures.js'; -import { VISUAL_URL } from '../../constants.js'; +import { VISUAL_FIXED_URL } from '../../constants.js'; test.describe('Visual - Default @a11y', () => { test.beforeEach(async ({ page }) => { - await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' }); + await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' }); }); test('Visual - Default Dashboard', async ({ page, theme }) => { diff --git a/e2e/tests/visual-a11y/displayLayout.visual.spec.js b/e2e/tests/visual-a11y/displayLayout.visual.spec.js index 91d875c655..f9cf123d4b 100644 --- a/e2e/tests/visual-a11y/displayLayout.visual.spec.js +++ b/e2e/tests/visual-a11y/displayLayout.visual.spec.js @@ -23,12 +23,18 @@ import percySnapshot from '@percy/playwright'; import { createDomainObjectWithDefaults } from '../../appActions.js'; -import { VISUAL_URL } from '../../constants.js'; +import { MISSION_TIME, VISUAL_FIXED_URL } from '../../constants.js'; import { test } from '../../pluginFixtures.js'; -test.describe('Visual - Display Layout', () => { - test.beforeEach(async ({ page, theme }) => { - await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' }); +test.describe('Visual - Display Layout @clock', () => { + test.use({ + clockOptions: { + now: MISSION_TIME, + shouldAdvanceTime: true + } + }); + test.beforeEach(async ({ page }) => { + await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' }); const parentLayout = await createDomainObjectWithDefaults(page, { type: 'Display Layout', @@ -59,12 +65,15 @@ test.describe('Visual - Display Layout', () => { await page.goto(parentLayout.url, { waitUntil: 'domcontentloaded' }); await page.getByRole('button', { name: 'Edit Object' }).click(); - //Move the Child Right Layout to the Right. It should be on top of the Left Layout at this point. + // Select the child right layout await page .getByLabel('Child Right Layout Layout', { exact: true }) .getByLabel('Move Sub-object Frame') .click(); - await page.getByLabel('Move Sub-object Frame').nth(3).click(); //I'm not sure why this step is necessary + // FIXME: Click to select the parent object (layout) + await page.getByLabel('Move Sub-object Frame').nth(3).click(); + + // Move the second layout element to the right await page.getByLabel('X:').click(); await page.getByLabel('X:').fill('35'); }); diff --git a/e2e/tests/visual-a11y/faultManagement.visual.spec.js b/e2e/tests/visual-a11y/faultManagement.visual.spec.js index f3f5be314c..9034c38de9 100644 --- a/e2e/tests/visual-a11y/faultManagement.visual.spec.js +++ b/e2e/tests/visual-a11y/faultManagement.visual.spec.js @@ -84,6 +84,12 @@ test.describe('Fault Management Visual Tests', () => { await shelveFault(page, 1); await changeViewTo(page, 'shelved'); + /* cspell:disable-next-line */ + // Since fault management is heavily dependent on events (bleh), we need to wait for the correct + // element counts + await expect(page.getByLabel('Select fault:')).toHaveCount(1); + await expect(page.getByLabel('Disposition Actions')).toHaveCount(1); + await percySnapshot(page, `Shelved faults appear in the shelved view (theme: '${theme}')`); await openFaultRowMenu(page, 1); diff --git a/e2e/tests/visual-a11y/imagery.visual.spec.js b/e2e/tests/visual-a11y/imagery.visual.spec.js index 251a2e3c71..d46df5435b 100644 --- a/e2e/tests/visual-a11y/imagery.visual.spec.js +++ b/e2e/tests/visual-a11y/imagery.visual.spec.js @@ -24,7 +24,7 @@ import percySnapshot from '@percy/playwright'; import { createDomainObjectWithDefaults, setRealTimeMode } from '../../appActions.js'; import { waitForAnimations } from '../../baseFixtures.js'; -import { VISUAL_URL } from '../../constants.js'; +import { VISUAL_FIXED_URL } from '../../constants.js'; import { expect, test } from '../../pluginFixtures.js'; test.describe('Visual - Example Imagery', () => { @@ -32,7 +32,7 @@ test.describe('Visual - Example Imagery', () => { let parentLayout; test.beforeEach(async ({ page }) => { - await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' }); + await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' }); parentLayout = await createDomainObjectWithDefaults(page, { type: 'Display Layout', diff --git a/e2e/tests/visual-a11y/ladTable.visual.spec.js b/e2e/tests/visual-a11y/ladTable.visual.spec.js index 9d3769500b..075d98f307 100644 --- a/e2e/tests/visual-a11y/ladTable.visual.spec.js +++ b/e2e/tests/visual-a11y/ladTable.visual.spec.js @@ -23,14 +23,14 @@ import percySnapshot from '@percy/playwright'; import { createDomainObjectWithDefaults } from '../../appActions.js'; -import { VISUAL_URL } from '../../constants.js'; +import { VISUAL_FIXED_URL } from '../../constants.js'; import { expect, test } from '../../pluginFixtures.js'; test.describe('Visual - LAD Table', () => { let ladTable; test.beforeEach(async ({ page }) => { - await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' }); + await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' }); // Create LAD Table ladTable = await createDomainObjectWithDefaults(page, { diff --git a/e2e/tests/visual-a11y/notebook.visual.spec.js b/e2e/tests/visual-a11y/notebook.visual.spec.js index d04f29ccaf..b42ea15258 100644 --- a/e2e/tests/visual-a11y/notebook.visual.spec.js +++ b/e2e/tests/visual-a11y/notebook.visual.spec.js @@ -24,7 +24,7 @@ import percySnapshot from '@percy/playwright'; import { createDomainObjectWithDefaults, expandTreePaneItemByName } from '../../appActions.js'; import { expect, test } from '../../avpFixtures.js'; -import { VISUAL_URL } from '../../constants.js'; +import { VISUAL_FIXED_URL } from '../../constants.js'; import { enterTextEntry, startAndAddRestrictedNotebookObject } from '../../helper/notebookUtils.js'; test.describe('Visual - Restricted Notebook @a11y', () => { @@ -80,7 +80,7 @@ test.describe('Visual - Notebook Snapshot @a11y', () => { test.describe('Visual - Notebook @a11y', () => { let notebook; test.beforeEach(async ({ page }) => { - await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' }); + await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' }); notebook = await createDomainObjectWithDefaults(page, { type: 'Notebook', name: 'Test Notebook' diff --git a/e2e/tests/visual-a11y/notification.visual.spec.js b/e2e/tests/visual-a11y/notification.visual.spec.js index cea1ec2709..cb3fe8282e 100644 --- a/e2e/tests/visual-a11y/notification.visual.spec.js +++ b/e2e/tests/visual-a11y/notification.visual.spec.js @@ -28,11 +28,11 @@ import percySnapshot from '@percy/playwright'; import { createNotification } from '../../appActions.js'; import { expect, scanForA11yViolations, test } from '../../avpFixtures.js'; -import { VISUAL_URL } from '../../constants.js'; +import { VISUAL_FIXED_URL } from '../../constants.js'; test.describe('Visual - Notifications @a11y', () => { test.beforeEach(async ({ page }) => { - await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' }); + await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' }); }); test('Alert Levels and Notification List Modal', async ({ page, theme }) => { diff --git a/e2e/tests/visual-a11y/planning.visual.spec.js b/e2e/tests/visual-a11y/planning.visual.spec.js index c7c62f50ed..fbd9151a6e 100644 --- a/e2e/tests/visual-a11y/planning.visual.spec.js +++ b/e2e/tests/visual-a11y/planning.visual.spec.js @@ -25,7 +25,7 @@ import fs from 'fs'; import { createDomainObjectWithDefaults, createPlanFromJSON } from '../../appActions.js'; import { test } from '../../avpFixtures.js'; -import { VISUAL_URL } from '../../constants.js'; +import { VISUAL_FIXED_URL } from '../../constants.js'; import { createTimelistWithPlanAndSetActivityInProgress, getFirstActivity, @@ -64,7 +64,7 @@ test.describe('Visual - Timelist progress bar @clock', () => { test.describe('Visual - Planning', () => { test.beforeEach(async ({ page }) => { - await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' }); + await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' }); }); test('Plan View', async ({ page, theme }) => { @@ -83,7 +83,7 @@ test.describe('Visual - Planning', () => { }); const newPage = await newContext.newPage(); - await newPage.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' }); + await newPage.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' }); const plan = await createPlanFromJSON(newPage, { name: 'Plan Visual Test', json: examplePlanSmall2 @@ -100,7 +100,7 @@ test.describe('Visual - Planning', () => { name: 'Plan Visual Test (Draft)', json: examplePlanSmall2 }); - await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' }); + await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' }); await setDraftStatusForPlan(page, plan); await setBoundsToSpanAllActivities(page, examplePlanSmall2, plan.url); @@ -110,7 +110,7 @@ test.describe('Visual - Planning', () => { test.describe('Visual - Gantt Chart', () => { test.beforeEach(async ({ page }) => { - await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' }); + await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' }); }); test('Gantt Chart View', async ({ page, theme }) => { const ganttChart = await createDomainObjectWithDefaults(page, { @@ -153,7 +153,7 @@ test.describe('Visual - Gantt Chart', () => { await setDraftStatusForPlan(page, plan); - await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' }); + await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' }); await setBoundsToSpanAllActivities(page, examplePlanSmall2, ganttChart.url); await percySnapshot(page, `Gantt Chart View w/ draft status (theme: ${theme})`); diff --git a/e2e/tests/visual-a11y/search.visual.spec.js b/e2e/tests/visual-a11y/search.visual.spec.js index c3c3e6e70b..b6a4966e45 100644 --- a/e2e/tests/visual-a11y/search.visual.spec.js +++ b/e2e/tests/visual-a11y/search.visual.spec.js @@ -28,13 +28,13 @@ import percySnapshot from '@percy/playwright'; import { createDomainObjectWithDefaults } from '../../appActions.js'; import { expect, scanForA11yViolations, test } from '../../avpFixtures.js'; -import { VISUAL_URL } from '../../constants.js'; +import { VISUAL_FIXED_URL } from '../../constants.js'; test.describe('Grand Search @a11y', () => { let conditionWidget; let displayLayout; test.beforeEach(async ({ page }) => { - await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' }); + await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' }); displayLayout = await createDomainObjectWithDefaults(page, { type: 'Display Layout', diff --git a/e2e/tests/visual-a11y/telemetryViews.visual.spec.js b/e2e/tests/visual-a11y/telemetryViews.visual.spec.js index acb6262804..05386e5f79 100644 --- a/e2e/tests/visual-a11y/telemetryViews.visual.spec.js +++ b/e2e/tests/visual-a11y/telemetryViews.visual.spec.js @@ -23,14 +23,14 @@ import percySnapshot from '@percy/playwright'; import { createDomainObjectWithDefaults } from '../../appActions.js'; -import { VISUAL_URL } from '../../constants.js'; +import { VISUAL_FIXED_URL } from '../../constants.js'; import { expect, test } from '../../pluginFixtures.js'; test.describe('Visual - Telemetry Views', () => { let telemetry; test.beforeEach(async ({ page }) => { - await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' }); + await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' }); // Create SWG inside of LAD Table telemetry = await createDomainObjectWithDefaults(page, { diff --git a/karma.conf.cjs b/karma.conf.cjs index 33e11e3eda..af5e1befaf 100644 --- a/karma.conf.cjs +++ b/karma.conf.cjs @@ -20,26 +20,32 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -/*global module,process*/ +// eslint-disable-next-line func-style +const loadWebpackConfig = async () => { + if (process.env.KARMA_DEBUG) { + return { + config: (await import('./.webpack/webpack.dev.js')).default, + browsers: ['ChromeDebugging'], + singleRun: false + }; + } else { + return { + config: (await import('./.webpack/webpack.coverage.js')).default, + browsers: ['ChromeHeadless'], + singleRun: true + }; + } +}; module.exports = async (config) => { - let webpackConfig; - let browsers; - let singleRun; - - if (process.env.KARMA_DEBUG) { - webpackConfig = (await import('./.webpack/webpack.dev.js')).default; - browsers = ['ChromeDebugging']; - singleRun = false; - } else { - webpackConfig = (await import('./.webpack/webpack.coverage.js')).default; - browsers = ['ChromeHeadless']; - singleRun = true; - } + const { config: webpackConfig, browsers, singleRun } = await loadWebpackConfig(); + // Adjust webpack config for Karma delete webpackConfig.output; - // karma doesn't support webpack entry - delete webpackConfig.entry; + delete webpackConfig.entry; // Karma doesn't support webpack entry + + // Ensure source maps are enabled for better debugging + webpackConfig.devtool = 'inline-source-map'; config.set({ basePath: '', @@ -106,7 +112,7 @@ module.exports = async (config) => { }, webpack: webpackConfig, webpackMiddleware: { - stats: 'errors-warnings' + stats: 'detailed' // Changed to 'detailed' for more debugging info }, concurrency: 1, singleRun, diff --git a/package-lock.json b/package-lock.json index 1674cff5b7..5f68e908d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "@types/eventemitter3": "1.2.0", "@types/jasmine": "5.1.2", "@types/lodash": "4.17.0", + "@types/sinonjs__fake-timers": "8.1.5", "@vue/compiler-sfc": "3.4.3", "babel-loader": "9.1.0", "babel-plugin-istanbul": "6.1.1", @@ -1904,6 +1905,12 @@ "@types/node": "*" } }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz", + "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==", + "dev": true + }, "node_modules/@types/sockjs": { "version": "0.3.36", "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", @@ -1932,207 +1939,6 @@ "@types/node": "*" } }, - "node_modules/@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -9255,6 +9061,207 @@ } } }, + "node_modules/prettier-eslint/node_modules/@typescript-eslint/parser": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/prettier-eslint/node_modules/@typescript-eslint/scope-manager": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/prettier-eslint/node_modules/@typescript-eslint/types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/prettier-eslint/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/prettier-eslint/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/prettier-eslint/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/prettier-eslint/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/prettier-eslint/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prettier-eslint/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prettier-eslint/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/prettier-eslint/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prettier-eslint/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/prettier-eslint/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/prettier-linter-helpers": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", diff --git a/package.json b/package.json index b2236a7524..d8eae3bb41 100644 --- a/package.json +++ b/package.json @@ -12,12 +12,13 @@ "@percy/playwright": "1.0.4", "@playwright/test": "1.42.1", "@types/d3-axis": "3.0.6", - "@types/d3-shape": "3.0.0", "@types/d3-scale": "4.0.8", "@types/d3-selection": "3.0.10", + "@types/d3-shape": "3.0.0", "@types/eventemitter3": "1.2.0", "@types/jasmine": "5.1.2", "@types/lodash": "4.17.0", + "@types/sinonjs__fake-timers": "8.1.5", "@vue/compiler-sfc": "3.4.3", "babel-loader": "9.1.0", "babel-plugin-istanbul": "6.1.1", @@ -27,9 +28,9 @@ "cspell": "7.3.8", "css-loader": "6.10.0", "d3-axis": "3.0.0", - "d3-shape": "3.0.0", "d3-scale": "4.0.2", "d3-selection": "3.0.0", + "d3-shape": "3.0.0", "eslint": "8.56.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-compat": "4.2.0", diff --git a/src/plugins/DeviceClassifier/src/DeviceClassifierSpec.js b/src/plugins/DeviceClassifier/src/DeviceClassifierSpec.js index 65341a0d50..16992f7fdc 100644 --- a/src/plugins/DeviceClassifier/src/DeviceClassifierSpec.js +++ b/src/plugins/DeviceClassifier/src/DeviceClassifierSpec.js @@ -61,7 +61,6 @@ describe('DeviceClassifier', function () { mockAgent[m].and.returnValue(true); }); - // eslint-disable-next-line no-new DeviceClassifier(mockAgent, mockDocument); }); diff --git a/src/plugins/URLIndicatorPlugin/URLIndicatorSpec.js b/src/plugins/URLIndicatorPlugin/URLIndicatorSpec.js index 7c435ee544..c924089d48 100644 --- a/src/plugins/URLIndicatorPlugin/URLIndicatorSpec.js +++ b/src/plugins/URLIndicatorPlugin/URLIndicatorSpec.js @@ -28,7 +28,7 @@ describe('The URLIndicator', function () { let openmct; let indicatorElement; let pluginOptions; - let urlIndicator; // eslint-disable-line + let urlIndicator; let fetchSpy; beforeEach(function () { diff --git a/src/plugins/charts/bar/pluginSpec.js b/src/plugins/charts/bar/pluginSpec.js index e25a3e3023..e8b809bcfd 100644 --- a/src/plugins/charts/bar/pluginSpec.js +++ b/src/plugins/charts/bar/pluginSpec.js @@ -125,7 +125,7 @@ describe('the plugin', function () { describe('The bar graph view', () => { let barGraphObject; - // eslint-disable-next-line no-unused-vars + let mockComposition; beforeEach(async () => { @@ -222,7 +222,7 @@ describe('the plugin', function () { describe('The spectral plot view for telemetry objects with array values', () => { let barGraphObject; - // eslint-disable-next-line no-unused-vars + let mockComposition; beforeEach(async () => { diff --git a/src/plugins/charts/scatter/pluginSpec.js b/src/plugins/charts/scatter/pluginSpec.js index ffa7e46cc4..18cdd93b81 100644 --- a/src/plugins/charts/scatter/pluginSpec.js +++ b/src/plugins/charts/scatter/pluginSpec.js @@ -117,7 +117,7 @@ describe('the plugin', function () { describe('The scatter plot view', () => { let testDomainObject; let scatterPlotObject; - // eslint-disable-next-line no-unused-vars + let mockComposition; beforeEach(async () => { diff --git a/src/plugins/importFromJSONAction/ImportFromJSONActionSpec.js b/src/plugins/importFromJSONAction/ImportFromJSONActionSpec.js index b1d582cb49..db6e77d4f0 100644 --- a/src/plugins/importFromJSONAction/ImportFromJSONActionSpec.js +++ b/src/plugins/importFromJSONAction/ImportFromJSONActionSpec.js @@ -134,7 +134,7 @@ describe('The import JSON action', function () { const pollutedResponse = { selectFile: { name: 'imported object', - // eslint-disable-next-line prettier/prettier + body: '{"openmct":{"c28d230d-e909-4a3e-9840-d9ef469dda70":{"identifier":{"key":"c28d230d-e909-4a3e-9840-d9ef469dda70","namespace":""},"name":"Unnamed Overlay Plot","type":"telemetry.plot.overlay","composition":[],"configuration":{"series":[]},"modified":1695837546833,"location":"mine","created":1695837546833,"persisted":1695837546833,"__proto__":{"toString":"foobar"}}},"rootId":"c28d230d-e909-4a3e-9840-d9ef469dda70"}' } }; diff --git a/src/plugins/localStorage/pluginSpec.js b/src/plugins/localStorage/pluginSpec.js index 9ad22924d4..cae2151236 100644 --- a/src/plugins/localStorage/pluginSpec.js +++ b/src/plugins/localStorage/pluginSpec.js @@ -1,4 +1,3 @@ -/* eslint-disable no-invalid-this */ /***************************************************************************** * Open MCT, Copyright (c) 2014-2024, United States Government * as represented by the Administrator of the National Aeronautics and Space diff --git a/src/plugins/summaryWidget/src/Rule.js b/src/plugins/summaryWidget/src/Rule.js index dc10d7f646..2e3a1c5e0a 100644 --- a/src/plugins/summaryWidget/src/Rule.js +++ b/src/plugins/summaryWidget/src/Rule.js @@ -161,7 +161,6 @@ export default function Rule( */ function onDragStart(event) { document.querySelectorAll('.t-drag-indicator').forEach((indicator) => { - // eslint-disable-next-line no-invalid-this const ruleHeader = self.domElement.querySelectorAll('.widget-rule-header')[0].cloneNode(true); indicator.textContent = ''; indicator.appendChild(ruleHeader); diff --git a/src/plugins/summaryWidget/src/input/ColorPalette.js b/src/plugins/summaryWidget/src/input/ColorPalette.js index 882956aefe..2463876769 100644 --- a/src/plugins/summaryWidget/src/input/ColorPalette.js +++ b/src/plugins/summaryWidget/src/input/ColorPalette.js @@ -106,7 +106,6 @@ export default function ColorPalette(cssClass, container, colors) { domElement.querySelector('.c-palette').classList.add('c-palette--color'); domElement.querySelectorAll('.c-palette__item').forEach((item) => { - // eslint-disable-next-line no-invalid-this item.style.backgroundColor = item.dataset.item; }); diff --git a/src/plugins/summaryWidget/src/input/IconPalette.js b/src/plugins/summaryWidget/src/input/IconPalette.js index 717a5fc74a..147b20fdf8 100644 --- a/src/plugins/summaryWidget/src/input/IconPalette.js +++ b/src/plugins/summaryWidget/src/input/IconPalette.js @@ -51,7 +51,6 @@ export default function IconPalette(cssClass, container, icons) { domElement.querySelector('.c-palette').classList.add('c-palette--icon'); domElement.querySelectorAll('.c-palette-item').forEach((item) => { - // eslint-disable-next-line no-invalid-this item.classList.add(item.dataset.item); }); diff --git a/src/plugins/timeConductor/ConductorAxis.vue b/src/plugins/timeConductor/ConductorAxis.vue index 49d6d61f70..bc824d08a1 100644 --- a/src/plugins/timeConductor/ConductorAxis.vue +++ b/src/plugins/timeConductor/ConductorAxis.vue @@ -20,7 +20,12 @@ at runtime from the About dialog for additional information. --> @@ -29,7 +34,9 @@ import { axisTop } from 'd3-axis'; import { scaleLinear, scaleUtc } from 'd3-scale'; import { select } from 'd3-selection'; +import { onMounted, ref } from 'vue'; +import { useResizeObserver } from '../../../src/ui/composables/resize.js'; import { TIME_CONTEXT_EVENTS } from '../../api/time/constants.js'; import utcMultiTimeFormat from './utcMultiTimeFormat.js'; @@ -55,20 +62,69 @@ export default { } }, emits: ['pan-axis', 'end-pan', 'zoom-axis', 'end-zoom'], + setup() { + const axisHolder = ref(null); + const { size: containerSize, startObserving } = useResizeObserver(); + + onMounted(() => { + startObserving(axisHolder.value); + }); + + return { + axisHolder, + containerSize + }; + }, data() { return { inPanMode: false, dragStartX: undefined, dragX: undefined, - zoomStyle: {} + zoomStyle: {}, + rect: null }; }, computed: { inZoomMode() { return !this.inPanMode; + }, + left() { + return this.rect.left; + }, + panBounds() { + const bounds = this.openmct.time.getBounds(); + const deltaTime = bounds.end - bounds.start; + const deltaX = this.dragX - this.dragStartX; + const percX = deltaX / this.width; + const panStart = bounds.start - percX * deltaTime; + + return { + start: parseInt(panStart, 10), + end: parseInt(panStart + deltaTime, 10) + }; + }, + zoomRange() { + const leftBound = this.left; + const rightBound = this.left + this.width; + const zoomStart = this.dragX < leftBound ? leftBound : Math.min(this.dragX, this.dragStartX); + const zoomEnd = this.dragX > rightBound ? rightBound : Math.max(this.dragX, this.dragStartX); + + return { + start: zoomStart, + end: zoomEnd + }; + }, + isChangingViewBounds() { + return this.dragStartX && this.dragX && this.dragStartX !== this.dragX; } }, watch: { + containerSize: { + handler() { + this.resize(); + }, + deep: true + }, viewBounds: { handler() { this.setScale(); @@ -77,7 +133,7 @@ export default { } }, mounted() { - let vis = select(this.$refs.axisHolder).append('svg:svg'); + const vis = select(this.axisHolder).append('svg:svg'); this.xAxis = axisTop(); this.dragging = false; @@ -93,15 +149,14 @@ export default { this.openmct.time.on(TIME_CONTEXT_EVENTS.timeSystemChanged, this.setViewFromTimeSystem); }, beforeUnmount() { - clearInterval(this.resizeTimer); + // Remove the listeners in case the component is unmounted while dragging + document.removeEventListener('mousemove', this.drag); + document.removeEventListener('mouseup', this.dragEnd); }, methods: { setAxisDimensions() { - const axisHolder = this.$refs.axisHolder; - const rect = axisHolder.getBoundingClientRect(); - - this.left = Math.round(rect.left); - this.width = axisHolder.clientWidth; + this.rect = this.axisHolder.getBoundingClientRect(); + this.width = this.axisHolder.clientWidth; }, setScale() { if (!this.width) { @@ -204,26 +259,14 @@ export default { this.dragX = undefined; }, pan() { - const panBounds = this.getPanBounds(); + const panBounds = this.panBounds; this.$emit('pan-axis', panBounds); }, endPan() { - const panBounds = this.isChangingViewBounds() ? this.getPanBounds() : undefined; + const panBounds = this.isChangingViewBounds ? this.panBounds : undefined; this.$emit('end-pan', panBounds); this.inPanMode = false; }, - getPanBounds() { - const bounds = this.openmct.time.getBounds(); - const deltaTime = bounds.end - bounds.start; - const deltaX = this.dragX - this.dragStartX; - const percX = deltaX / this.width; - const panStart = bounds.start - percX * deltaTime; - - return { - start: parseInt(panStart, 10), - end: parseInt(panStart + deltaTime, 10) - }; - }, startZoom() { const x = this.scaleToBounds(this.dragStartX); @@ -237,7 +280,7 @@ export default { }); }, zoom() { - const zoomRange = this.getZoomRange(); + const zoomRange = this.zoomRange; this.zoomStyle = { left: `${zoomRange.start - this.left}px`, @@ -251,8 +294,8 @@ export default { }, endZoom() { let zoomBounds; - if (this.isChangingViewBounds()) { - const zoomRange = this.getZoomRange(); + if (this.isChangingViewBounds) { + const zoomRange = this.zoomRange; zoomBounds = { start: this.scaleToBounds(zoomRange.start), end: this.scaleToBounds(zoomRange.end) @@ -262,19 +305,6 @@ export default { this.zoomStyle = {}; this.$emit('end-zoom', zoomBounds); }, - getZoomRange() { - const leftBound = this.left; - const rightBound = this.left + this.width; - - const zoomStart = this.dragX < leftBound ? leftBound : Math.min(this.dragX, this.dragStartX); - - const zoomEnd = this.dragX > rightBound ? rightBound : Math.max(this.dragX, this.dragStartX); - - return { - start: zoomStart, - end: zoomEnd - }; - }, scaleToBounds(value) { const bounds = this.openmct.time.getBounds(); const timeDelta = bounds.end - bounds.start; @@ -283,11 +313,8 @@ export default { return parseInt(bounds.start + offset, 10); }, - isChangingViewBounds() { - return this.dragStartX && this.dragX && this.dragStartX !== this.dragX; - }, resize() { - if (this.$refs.axisHolder.clientWidth !== this.width) { + if (this.axisHolder.clientWidth !== this.width) { this.setAxisDimensions(); this.setScale(); } diff --git a/src/ui/components/List/ListItem.vue b/src/ui/components/List/ListItem.vue index 55bb69d6ff..a1335b2c95 100644 --- a/src/ui/components/List/ListItem.vue +++ b/src/ui/components/List/ListItem.vue @@ -53,7 +53,6 @@ export default { formattedItemValues() { let values = []; this.itemProperties.forEach((property) => { - // eslint-disable-next-line you-dont-need-lodash-underscore/get let value = _.get(this.item, property.key); if (property.format) { value = property.format(value, this.item, property.key, this.openmct); diff --git a/src/utils/testing.js b/src/utils/testing.js index 037e239f54..5bc40949f0 100644 --- a/src/utils/testing.js +++ b/src/utils/testing.js @@ -83,29 +83,26 @@ export function clearBuiltinSpies() { } export function resetApplicationState(openmct) { - let promise; - clearBuiltinSpies(); - if (openmct !== undefined) { + if (openmct) { openmct.destroy(); } if (window.location.hash !== '#' && window.location.hash !== '') { - promise = new Promise((resolve, reject) => { - window.addEventListener('hashchange', cleanup); - window.location.hash = '#'; - - function cleanup() { - window.removeEventListener('hashchange', cleanup); + window.location.hash = '#'; + // Optionally wait for hashchange if necessary + return new Promise((resolve) => { + // eslint-disable-next-line func-style + const onHashChange = () => { + window.removeEventListener('hashchange', onHashChange); resolve(); - } + }; + window.addEventListener('hashchange', onHashChange); }); } else { - promise = Promise.resolve(); + return Promise.resolve(); } - - return promise; } // required: key