* fix: resize conductor properly * refactor: more computed properties, unregister listener * fix: beforeUnmounted hook * test(visual): add time conductor visual test for fixed mode * fix: initialize to `null` * feat: extend the base `screenshot` function to mask elements which will always create variance in an Open MCT screenshot * docs: add types for fixtures * fix: remove unneeded await * chore: add sinon timers types package back * docs: remove unused docs * doc: remove unused docs * test: add visual realtime url, update imports * feat: provide wrapped page.screenshot fixture that applies defaults * test: add basic timeConductor snapshot tests * chore: update eslint config * lint: remove unused disable directives * test: remove redundant navigation * fix: remove listeners * fix: maybe stabilize unit tests * docs: remove * fix: provide sourcemaps in unit tests * test: add regression snapshot test for time conductor axis * lint: remove unused imports * feat(e2e): add fixture to manually tick the clock and use it * test: reactivate test now that we don't use deploysentinel :( * test: update snapshots * test: add test for clockOptions and tick fixtures * test: add afterEach stub and fixme * test: try and stabilize fault management flake * lint: defy the word gods * chore: ignore `*-darwin.png` screenshots * chore: remove darwin screenshot binaries * docs: markdownlint * docs: remove MacOS specific instructions from snapshot testing * fix: remove a11y
@ -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;
|
||||
|
3
.gitignore
vendored
@ -47,3 +47,6 @@ index.html.bak
|
||||
.nyc_output
|
||||
coverage
|
||||
codecov
|
||||
|
||||
# Don't commit MacOS screenshots
|
||||
*-darwin.png
|
||||
|
@ -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
|
||||
//<Some interesting state>
|
||||
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.
|
||||
|
@ -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<Buffer>} 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<object|null>} Returns the accessibility scan results if violations are found,
|
||||
* otherwise returns null.
|
||||
* @param {{ reportName?: string }} [options={}] - The options for the report generation.
|
||||
* @returns {Promise<Object|null>} 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 };
|
||||
|
@ -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
|
||||
|
@ -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';
|
||||
|
@ -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))
|
||||
});
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-undef */
|
||||
// playwright.config.js
|
||||
// @ts-check
|
||||
|
||||
|
@ -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({
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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';
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
});
|
||||
|
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 19 KiB |
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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 }) => {
|
||||
|
@ -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', {
|
||||
|
@ -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',
|
||||
|
116
e2e/tests/visual-a11y/components/timeConductor.visual.spec.js
Normal file
@ -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');
|
||||
}
|
||||
);
|
||||
});
|
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 29 KiB |
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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 }) => {
|
||||
|
@ -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');
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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',
|
||||
|
@ -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, {
|
||||
|
@ -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'
|
||||
|
@ -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 }) => {
|
||||
|
@ -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})`);
|
||||
|
@ -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',
|
||||
|
@ -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, {
|
||||
|
@ -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,
|
||||
|
409
package-lock.json
generated
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -61,7 +61,6 @@ describe('DeviceClassifier', function () {
|
||||
mockAgent[m].and.returnValue(true);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
DeviceClassifier(mockAgent, mockDocument);
|
||||
});
|
||||
|
||||
|
@ -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 () {
|
||||
|
@ -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 () => {
|
||||
|
@ -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 () => {
|
||||
|
@ -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"}'
|
||||
}
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
});
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
|
||||
|
@ -20,7 +20,12 @@
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<template>
|
||||
<div ref="axisHolder" class="c-conductor-axis" @mousedown="dragStart($event)">
|
||||
<div
|
||||
ref="axisHolder"
|
||||
aria-label="Time Conductor Axis"
|
||||
class="c-conductor-axis"
|
||||
@mousedown="dragStart($event)"
|
||||
>
|
||||
<div class="c-conductor-axis__zoom-indicator" :style="zoomStyle"></div>
|
||||
</div>
|
||||
</template>
|
||||
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|