From 18e976ad12e23a242af110f977111e6ada90b194 Mon Sep 17 00:00:00 2001 From: David Tsay <3614296+davetsay@users.noreply.github.com> Date: Wed, 13 Mar 2024 12:04:02 -0700 Subject: [PATCH 01/28] allow inspector pane content to scroll vertically (#7567) * allow content to scroll vertically * add framework for inspector content scrollable e2e test * fix paths and spelling error * add aria-label to properties list * add scroll check to test * use click, which scrolls if needed * use scrollbar to scroll * Closes #7566 - Fixed scrolling to only apply to the area below the Inspector tabs. - Removed unneeded padding in pane.scss. - Alignment fixes to related scroll elements in tree. * cspell: ignore this file because it doesn't understand latin * fix selectors and test * lint fix * driveby: wait for thumbnail bar to finish scrolling before taking snapshot --------- Co-authored-by: Charles Hacskaylo Co-authored-by: Jesse Mazzella --- e2e/tests/functional/ui/inspector.e2e.spec.js | 75 +++++++++++++++++++ e2e/tests/visual-a11y/imagery.visual.spec.js | 2 + .../properties/PropertiesComponent.vue | 5 +- src/ui/inspector/inspector.scss | 8 +- src/ui/layout/mct-tree.scss | 4 - src/ui/layout/pane.scss | 10 +-- src/ui/layout/search/search.scss | 3 +- 7 files changed, 89 insertions(+), 18 deletions(-) create mode 100644 e2e/tests/functional/ui/inspector.e2e.spec.js diff --git a/e2e/tests/functional/ui/inspector.e2e.spec.js b/e2e/tests/functional/ui/inspector.e2e.spec.js new file mode 100644 index 0000000000..54ddc99502 --- /dev/null +++ b/e2e/tests/functional/ui/inspector.e2e.spec.js @@ -0,0 +1,75 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ +import { createDomainObjectWithDefaults } from '../../../appActions.js'; +import { expect, test } from '../../../baseFixtures.js'; + +// We don't need cspell to check this. It doesn't know latin. +/* cSpell:disable */ +const loremIpsum = `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Molestie at elementum eu facilisis sed. Feugiat pretium nibh ipsum consequat. Amet consectetur adipiscing elit duis tristique sollicitudin nibh sit amet. Eget nullam non nisi est sit amet. A pellentesque sit amet porttitor eget dolor morbi non arcu. Ullamcorper sit amet risus nullam eget felis eget nunc. In tellus integer feugiat scelerisque varius morbi enim nunc. Ac feugiat sed lectus vestibulum mattis ullamcorper. Nulla facilisi morbi tempus iaculis urna id volutpat. Massa vitae tortor condimentum lacinia quis vel eros donec. Ornare quam viverra orci sagittis eu. Vestibulum sed arcu non odio. In egestas erat imperdiet sed euismod nisi porta lorem. Vitae auctor eu augue ut lectus arcu bibendum at. Donec adipiscing tristique risus nec feugiat in fermentum posuere urna. Velit euismod in pellentesque massa placerat duis ultricies. Nulla facilisi nullam vehicula ipsum a arcu cursus vitae. Aliquam malesuada bibendum arcu vitae elementum curabitur. +Vel eros donec ac odio tempor orci. Et netus et malesuada fames ac turpis egestas sed tempus. Turpis egestas pretium aenean pharetra magna ac placerat. Euismod elementum nisi quis eleifend. Vitae auctor eu augue ut lectus arcu. At imperdiet dui accumsan sit amet nulla facilisi. Est velit egestas dui id ornare arcu odio ut sem. Ornare arcu dui vivamus arcu felis. Luctus venenatis lectus magna fringilla. At elementum eu facilisis sed. Tristique et egestas quis ipsum suspendisse ultrices gravida dictum. Enim eu turpis egestas pretium aenean pharetra magna ac placerat. Lobortis scelerisque fermentum dui faucibus in. Tempor orci eu lobortis elementum nibh tellus molestie nunc non. Dignissim convallis aenean et tortor at risus. Enim tortor at auctor urna nunc id cursus. Libero volutpat sed cras ornare arcu dui vivamus. Scelerisque fermentum dui faucibus in ornare quam viverra. +Odio ut sem nulla pharetra. Neque vitae tempus quam pellentesque nec. A arcu cursus vitae congue mauris. Turpis nunc eget lorem dolor sed viverra ipsum nunc aliquet. Nibh tellus molestie nunc non blandit massa enim nec. Risus feugiat in ante metus dictum at tempor commodo ullamcorper. Nec tincidunt praesent semper feugiat nibh sed pulvinar proin gravida. Pulvinar elementum integer enim neque. Bibendum ut tristique et egestas. Nibh praesent tristique magna sit. Lectus magna fringilla urna porttitor. Eu non diam phasellus vestibulum lorem sed risus. Rhoncus mattis rhoncus urna neque. Rutrum tellus pellentesque eu tincidunt tortor aliquam. Pharetra convallis posuere morbi leo urna molestie at elementum. Quis commodo odio aenean sed adipiscing. Enim sit amet venenatis urna cursus eget nunc. +Enim nec dui nunc mattis. Cursus turpis massa tincidunt dui ut. Donec adipiscing tristique risus nec feugiat in. Eleifend mi in nulla posuere sollicitudin. Donec enim diam vulputate ut pharetra sit. Ultricies mi eget mauris pharetra et ultrices neque. Eros in cursus turpis massa tincidunt dui. Cursus risus at ultrices mi tempus imperdiet nulla malesuada. Morbi enim nunc faucibus a pellentesque sit. Porttitor rhoncus dolor purus non. Ac tortor vitae purus faucibus. +Proin libero nunc consequat interdum varius sit amet mattis vulputate. Metus dictum at tempor commodo ullamcorper a lacus vestibulum sed. Quisque non tellus orci ac auctor augue mauris. Id ornare arcu odio ut. Rhoncus est pellentesque elit ullamcorper dignissim. Senectus et netus et malesuada fames ac turpis egestas. Volutpat ac tincidunt vitae semper quis lectus nulla. Adipiscing elit duis tristique sollicitudin. Ipsum faucibus vitae aliquet nec ullamcorper sit. Gravida neque convallis a cras semper auctor neque vitae tempus. Porttitor leo a diam sollicitudin tempor id. Dictum non consectetur a erat nam at lectus. At volutpat diam ut venenatis tellus in. Morbi enim nunc faucibus a pellentesque sit amet. Cursus in hac habitasse platea. Sed augue lacus viverra vitae. +`; + +test.describe('Inspector tests', () => { + test.beforeEach(async ({ page }) => { + await page.goto('./', { waitUntil: 'domcontentloaded' }); + }); + + test('Content in inspector can be scrolled to vertically', async ({ page }) => { + const folderWithOverflowingTitle = await createDomainObjectWithDefaults(page, { + type: 'Folder', + name: loremIpsum + }); + + await page.goto(folderWithOverflowingTitle.url); + + const inspectorPropertiesLocator = page + .getByRole('tabpanel', { name: 'Inspector Views' }) + .getByLabel('Inspector Properties Details'); + const inspectorPropertiesList = inspectorPropertiesLocator.getByRole('list'); + const firstInspectorPropertyValue = inspectorPropertiesList + .getByRole('listitem') + .first() + .getByLabel('value', { exact: false }); + const lastInspectorPropertyValue = inspectorPropertiesList + .getByRole('listitem') + .last() + .getByLabel('value', { exact: false }); + + // inspector content partially in viewport, but not all the way in viewport + await expect(inspectorPropertiesLocator).toBeInViewport(); + await expect(inspectorPropertiesLocator).not.toBeInViewport({ ratio: 0.9 }); + + await expect(firstInspectorPropertyValue).toBeInViewport(); + await expect(lastInspectorPropertyValue).not.toBeInViewport(); + + // using page.mouse.wheel to scroll the inspector content by the height of the content + // because click and scrollIntoView will scroll even if scrollbar not available + await inspectorPropertiesLocator.hover(); + const offset = await inspectorPropertiesLocator.evaluate((el) => el.offsetHeight); + await page.mouse.wheel(0, offset); + + await expect(lastInspectorPropertyValue).toBeInViewport(); + }); +}); diff --git a/e2e/tests/visual-a11y/imagery.visual.spec.js b/e2e/tests/visual-a11y/imagery.visual.spec.js index bb0c343c2a..251a2e3c71 100644 --- a/e2e/tests/visual-a11y/imagery.visual.spec.js +++ b/e2e/tests/visual-a11y/imagery.visual.spec.js @@ -23,6 +23,7 @@ import percySnapshot from '@percy/playwright'; import { createDomainObjectWithDefaults, setRealTimeMode } from '../../appActions.js'; +import { waitForAnimations } from '../../baseFixtures.js'; import { VISUAL_URL } from '../../constants.js'; import { expect, test } from '../../pluginFixtures.js'; @@ -78,6 +79,7 @@ test.describe('Visual - Example Imagery', () => { await expect(page.getByLabel('Image Wrapper')).toBeVisible(); + await waitForAnimations(page.locator('.animate-scroll')); await percySnapshot(page, `Example Imagery in Real Time (theme: ${theme})`); }); diff --git a/src/plugins/inspectorViews/properties/PropertiesComponent.vue b/src/plugins/inspectorViews/properties/PropertiesComponent.vue index 0e4c82b51f..77e3c837e7 100644 --- a/src/plugins/inspectorViews/properties/PropertiesComponent.vue +++ b/src/plugins/inspectorViews/properties/PropertiesComponent.vue @@ -22,7 +22,10 @@ @@ -526,20 +523,16 @@ export default { return activities.map(this.styleActivity); }, setSort() { - const sortOrder = SORT_ORDER_OPTIONS[this.domainObject.configuration.sortOrderIndex]; - const property = sortOrder.property; - const direction = sortOrder.direction.toLowerCase() === 'asc'; + const { property, direction } = + SORT_ORDER_OPTIONS[this.domainObject.configuration.sortOrderIndex]; this.defaultSort = { property, - defaultDirection: direction + defaultDirection: direction.toLowerCase() === 'asc' }; }, sortItems(activities) { - let sortedItems = _.sortBy(activities, this.defaultSort.property); - if (!this.defaultSort.defaultDirection) { - sortedItems = sortedItems.reverse(); - } - return sortedItems; + const sortedItems = _.sortBy(activities, this.defaultSort.property); + return this.defaultSort.defaultDirection ? sortedItems : sortedItems.reverse(); }, setStatus(status) { this.status = status; @@ -548,10 +541,7 @@ export default { this.isEditing = isEditing; this.setViewFromConfig(this.domainObject.configuration); }, - sort(data) { - const property = data.property; - const direction = data.direction; - + sort({ property, direction }) { if (this.defaultSort.property === property) { this.defaultSort.defaultDirection = !this.defaultSort.defaultDirection; } else { @@ -565,10 +555,10 @@ export default { this.openmct.selection.select( [ { - element: element, + element, context: { type: 'activity', - activity: activity + activity } }, { @@ -581,6 +571,11 @@ export default { ], multiSelect ); + }, + getSortDirection(headerItem) { + return this.defaultSort.property === headerItem.property + ? this.defaultSort.defaultDirection + : headerItem.defaultDirection; } } }; From 10eb749d329ff4e6b19e3ec7a78601e86d2649ff Mon Sep 17 00:00:00 2001 From: Jamie V Date: Thu, 14 Mar 2024 09:05:23 -0700 Subject: [PATCH 03/28] Prevent Metadata Time System Error for Missing Objects (#7565) https://github.com/nasa/openmct/pull/7565 Modified Stacked Plots to not show Missing Objects. Added a check in Telemetry Collections for missing objects before displaying telemetry metadata time system error. --- .../plugins/plot/missingPlotObj.e2e.spec.js | 142 ++++-------------- src/api/objects/ObjectAPI.js | 2 +- src/api/telemetry/TelemetryCollection.js | 8 +- .../plot/configuration/SeriesCollection.js | 5 + src/plugins/plot/stackedPlot/StackedPlot.vue | 5 + .../plot/stackedPlot/StackedPlotItem.vue | 8 +- src/ui/components/ObjectFrame.vue | 5 +- src/ui/layout/BrowseBar.vue | 58 ++++--- 8 files changed, 87 insertions(+), 146 deletions(-) diff --git a/e2e/tests/functional/plugins/plot/missingPlotObj.e2e.spec.js b/e2e/tests/functional/plugins/plot/missingPlotObj.e2e.spec.js index bee8c83a2d..8e51d6c9eb 100644 --- a/e2e/tests/functional/plugins/plot/missingPlotObj.e2e.spec.js +++ b/e2e/tests/functional/plugins/plot/missingPlotObj.e2e.spec.js @@ -24,138 +24,60 @@ Tests to verify log plot functionality when objects are missing */ +import { createDomainObjectWithDefaults } from '../../../../appActions.js'; import { expect, test } from '../../../../pluginFixtures.js'; test.describe('Handle missing object for plots', () => { - test('Displays empty div for missing stacked plot item @unstable', async ({ - page, - browserName, - openmctConfig - }) => { + test.beforeEach(async ({ page }) => { + await page.goto('./', { waitUntil: 'domcontentloaded' }); + }); + test('Displays empty div for missing stacked plot item', async ({ page, browserName }) => { // eslint-disable-next-line playwright/no-skipped-test test.skip(browserName === 'firefox', 'Firefox failing due to console events being missed'); - const { myItemsFolderName } = openmctConfig; - const errorLogs = []; + let warningReceived = false; page.on('console', (message) => { if (message.type() === 'warning' && message.text().includes('Missing domain object')) { - errorLogs.push(message.text()); + warningReceived = true; } }); - //Make stacked plot - await makeStackedPlot(page, myItemsFolderName); + const stackedPlot = await createDomainObjectWithDefaults(page, { + type: 'Stacked Plot' + }); + await createDomainObjectWithDefaults(page, { + type: 'Sine Wave Generator', + parent: stackedPlot.uuid + }); + await createDomainObjectWithDefaults(page, { + type: 'Sine Wave Generator', + parent: stackedPlot.uuid + }); //Gets local storage and deletes the last sine wave generator in the stacked plot - const localStorage = await page.evaluate(() => window.localStorage); - const parsedData = JSON.parse(localStorage.mct); - const keys = Object.keys(parsedData); - const lastKey = keys[keys.length - 1]; + const mct = await page.evaluate(() => window.localStorage.getItem('mct')); + const parsedData = JSON.parse(mct); + const key = Object.entries(parsedData).find(([, value]) => value.type === 'generator')?.[0]; - delete parsedData[lastKey]; + delete parsedData[key]; //Sets local storage with missing object - await page.evaluate(`window.localStorage.setItem('mct', '${JSON.stringify(parsedData)}')`); + const jsonData = JSON.stringify(parsedData); + await page.evaluate((data) => { + window.localStorage.setItem('mct', data); + }, jsonData); //Reloads page and clicks on stacked plot - await Promise.all([page.reload(), page.waitForLoadState('networkidle')]); + await page.reload({ waitUntil: 'domcontentloaded' }); + await page.goto(stackedPlot.url); //Verify Main section is there on load - await expect - .soft(page.locator('.l-browse-bar__object-name')) - .toContainText('Unnamed Stacked Plot'); - - await page.locator(`text=Open MCT ${myItemsFolderName} >> span`).nth(3).click(); - await Promise.all([ - page.waitForNavigation(), - page.locator('text=Unnamed Stacked Plot').first().click() - ]); + await expect(page.locator('.l-browse-bar__object-name')).toContainText(stackedPlot.name); //Check that there is only one stacked item plot with a plot, the missing one will be empty - await expect(page.locator('.c-plot--stacked-container:has(.gl-plot)')).toHaveCount(1); - //Verify that console.warn is thrown - expect(errorLogs).toHaveLength(1); + await expect(page.getByLabel('Stacked Plot Item')).toHaveCount(1); + //Verify that console.warn was thrown + expect(warningReceived).toBe(true); }); }); - -/** - * This is used the create a stacked plot object - * @private - */ -async function makeStackedPlot(page, myItemsFolderName) { - // fresh page with time range from 2022-03-29 22:00:00.000Z to 2022-03-29 22:00:30.000Z - await page.goto('./', { waitUntil: 'domcontentloaded' }); - - // create stacked plot - await page.locator('button.c-create-button').click(); - await page.locator('li[role="menuitem"]:has-text("Stacked Plot")').click(); - - await Promise.all([ - page.waitForNavigation({ waitUntil: 'networkidle' }), - page.locator('button:has-text("OK")').click(), - //Wait for Save Banner to appear - page.waitForSelector('.c-message-banner__message') - ]); - - // save the stacked plot - await saveStackedPlot(page); - - // create a sinewave generator - await createSineWaveGenerator(page); - - // click on stacked plot - await page.locator(`text=Open MCT ${myItemsFolderName} >> span`).nth(3).click(); - await Promise.all([ - page.waitForNavigation(), - page.locator('text=Unnamed Stacked Plot').first().click() - ]); - - // create a second sinewave generator - await createSineWaveGenerator(page); - - // click on stacked plot - await page.locator(`text=Open MCT ${myItemsFolderName} >> span`).nth(3).click(); - await Promise.all([ - page.waitForNavigation(), - page.locator('text=Unnamed Stacked Plot').first().click() - ]); -} - -/** - * This is used to save a stacked plot object - * @private - */ -async function saveStackedPlot(page) { - // save stacked plot - await page - .locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button') - .nth(1) - .click(); - - await Promise.all([ - page.locator('text=Save and Finish Editing').click(), - //Wait for Save Banner to appear - page.waitForSelector('.c-message-banner__message') - ]); - //Wait until Save Banner is gone - await page.locator('.c-message-banner__close-button').click(); - await page.waitForSelector('.c-message-banner__message', { state: 'detached' }); -} - -/** - * This is used to create a sine wave generator object - * @private - */ -async function createSineWaveGenerator(page) { - //Create sine wave generator - await page.locator('button.c-create-button').click(); - await page.locator('li[role="menuitem"]:has-text("Sine Wave Generator")').click(); - - await Promise.all([ - page.waitForNavigation({ waitUntil: 'networkidle' }), - page.locator('button:has-text("OK")').click(), - //Wait for Save Banner to appear - page.waitForSelector('.c-message-banner__message') - ]); -} diff --git a/src/api/objects/ObjectAPI.js b/src/api/objects/ObjectAPI.js index 3615f3c94c..55b46d2850 100644 --- a/src/api/objects/ObjectAPI.js +++ b/src/api/objects/ObjectAPI.js @@ -249,7 +249,7 @@ export default class ObjectAPI { .get(identifier, abortSignal) .then((domainObject) => { delete this.cache[keystring]; - if (!domainObject && abortSignal.aborted) { + if (!domainObject && abortSignal?.aborted) { // we've aborted the request return; } diff --git a/src/api/telemetry/TelemetryCollection.js b/src/api/telemetry/TelemetryCollection.js index 4791a04ae0..eb2462aa36 100644 --- a/src/api/telemetry/TelemetryCollection.js +++ b/src/api/telemetry/TelemetryCollection.js @@ -442,8 +442,12 @@ export default class TelemetryCollection extends EventEmitter { } else { this.timeKey = undefined; - this._warn(TIMESYSTEM_KEY_WARNING); - this.openmct.notifications.alert(TIMESYSTEM_KEY_NOTIFICATION); + // missing objects will never have a domain, if one happens to get through + // to this point this warning/notification does not apply + if (!this.openmct.objects.isMissing(this.domainObject)) { + this._warn(TIMESYSTEM_KEY_WARNING); + this.openmct.notifications.alert(TIMESYSTEM_KEY_NOTIFICATION); + } } let valueFormatter = this.openmct.telemetry.getValueFormatter(metadataValue); diff --git a/src/plugins/plot/configuration/SeriesCollection.js b/src/plugins/plot/configuration/SeriesCollection.js index 001d9358df..cd3401b416 100644 --- a/src/plugins/plot/configuration/SeriesCollection.js +++ b/src/plugins/plot/configuration/SeriesCollection.js @@ -46,6 +46,11 @@ export default class SeriesCollection extends Collection { this.listenTo(this.plot, 'change:domainObject', this.trackPersistedConfig, this); const domainObject = this.plot.get('domainObject'); + + if (this.openmct.objects.isMissing(domainObject)) { + return; + } + if (domainObject.telemetry) { this.addTelemetryObject(domainObject); } else { diff --git a/src/plugins/plot/stackedPlot/StackedPlot.vue b/src/plugins/plot/stackedPlot/StackedPlot.vue index 89e76a6058..897dca433d 100644 --- a/src/plugins/plot/stackedPlot/StackedPlot.vue +++ b/src/plugins/plot/stackedPlot/StackedPlot.vue @@ -219,6 +219,11 @@ export default { }, addChild(child) { + if (this.openmct.objects.isMissing(child)) { + console.warn('Missing domain object for stacked plot: ', child); + return; + } + const id = this.openmct.objects.makeKeyString(child.identifier); this.tickWidthMap[id] = { diff --git a/src/plugins/plot/stackedPlot/StackedPlotItem.vue b/src/plugins/plot/stackedPlot/StackedPlotItem.vue index 15388e48a6..a53b13ee71 100644 --- a/src/plugins/plot/stackedPlot/StackedPlotItem.vue +++ b/src/plugins/plot/stackedPlot/StackedPlotItem.vue @@ -170,6 +170,10 @@ export default { //If this object is not persistable, then package it with it's parent const plotObject = this.getPlotObject(); + if (plotObject === null) { + return; + } + if (this.openmct.telemetry.isTelemetryObject(plotObject)) { this.subscribeToStaleness(plotObject); } else { @@ -215,10 +219,6 @@ export default { }, getPlotObject() { this.checkPlotConfiguration(); - // If object is missing, warn - if (this.openmct.objects.isMissing(this.childObject)) { - console.warn('Missing domain object for stacked plot', this.childObject); - } return this.childObject; }, checkPlotConfiguration() { diff --git a/src/ui/components/ObjectFrame.vue b/src/ui/components/ObjectFrame.vue index 1b3f7ec98c..177f9d5d1f 100644 --- a/src/ui/components/ObjectFrame.vue +++ b/src/ui/components/ObjectFrame.vue @@ -190,7 +190,7 @@ export default { this.soViewResizeObserver.observe(this.$refs.soView); } - const viewKey = this.getViewKey(); + const viewKey = this.$refs.objectView?.viewKey; this.supportsIndependentTime = this.domainObject && SupportedViewTypes.includes(viewKey); }, beforeUnmount() { @@ -257,9 +257,6 @@ export default { this.widthClass = wClass.trimStart(); }, - getViewKey() { - return this.$refs.objectView?.viewKey; - }, async showToolTip() { const { BELOW } = this.openmct.tooltips.TOOLTIP_LOCATIONS; this.buildToolTip(await this.getObjectPath(), BELOW, 'objectName'); diff --git a/src/ui/layout/BrowseBar.vue b/src/ui/layout/BrowseBar.vue index b71e601a43..315122b93d 100644 --- a/src/ui/layout/BrowseBar.vue +++ b/src/ui/layout/BrowseBar.vue @@ -29,7 +29,7 @@ @click="goToParent" >
-
+
- {{ domainObject.name }} + {{ domainObjectName }}
@@ -150,8 +150,6 @@ import tooltipHelpers from '../../api/tooltips/tooltipMixins.js'; import { SupportedViewTypes } from '../../utils/constants.js'; import ViewSwitcher from './ViewSwitcher.vue'; -const PLACEHOLDER_OBJECT = {}; - export default { components: { IndependentTimeConductor, @@ -168,12 +166,12 @@ export default { } } }, - data: function () { + data() { return { notebookTypes: [], showViewMenu: false, showSaveMenu: false, - domainObject: PLACEHOLDER_OBJECT, + domainObject: undefined, viewKey: undefined, isEditing: this.openmct.editor.isEditing(), notebookEnabled: this.openmct.types.get('notebook'), @@ -185,11 +183,22 @@ export default { statusClass() { return this.status ? `is-status--${this.status}` : ''; }, + supportsIndependentTime() { + return ( + this.domainObject?.identifier && + !this.openmct.objects.isMissing(this.domainObject) && + SupportedViewTypes.includes(this.viewKey) + ); + }, currentView() { return this.views.filter((v) => v.key === this.viewKey)[0] || {}; }, views() { - if (this.domainObject && this.openmct.router.started !== true) { + if (this.domainObject && this.openmct.router.started === false) { + return []; + } + + if (!this.domainObject) { return []; } @@ -203,25 +212,29 @@ export default { }); }, hasParent() { - return toRaw(this.domainObject) !== PLACEHOLDER_OBJECT && this.parentUrl !== '/browse'; + return toRaw(this.domainObject) && this.parentUrl !== '/browse'; }, parentUrl() { - const objectKeyString = this.openmct.objects.makeKeyString(this.domainObject.identifier); + const objectKeyString = this.openmct.objects.makeKeyString(this.domainObject?.identifier); const hash = this.openmct.router.getCurrentLocation().path; return hash.slice(0, hash.lastIndexOf('/' + objectKeyString)); }, - type() { - const objectType = this.openmct.types.get(this.domainObject.type); - if (!objectType) { - return {}; + cssClass() { + if (!this.domainObject) { + return ''; } - return objectType.definition; + const objectType = this.openmct.types.get(this.domainObject.type); + if (!objectType) { + return ''; + } + + return objectType?.definition?.cssClass ?? ''; }, isPersistable() { - let persistable = - this.domainObject.identifier && + const persistable = + this.domainObject?.identifier && this.openmct.objects.isPersistable(this.domainObject.identifier); return persistable; @@ -246,10 +259,8 @@ export default { return 'Unlocked for editing - click to lock.'; } }, - supportsIndependentTime() { - const viewKey = this.getViewKey(); - - return this.domainObject && SupportedViewTypes.includes(viewKey); + domainObjectName() { + return this.domainObject?.name ?? ''; } }, watch: { @@ -273,7 +284,7 @@ export default { this.updateActionItems(this.actionCollection.getActionsObject()); } }, - mounted: function () { + mounted() { document.addEventListener('click', this.closeViewAndSaveMenu); this.promptUserbeforeNavigatingAway = this.promptUserbeforeNavigatingAway.bind(this); window.addEventListener('beforeunload', this.promptUserbeforeNavigatingAway); @@ -282,7 +293,7 @@ export default { this.isEditing = isEditing; }); }, - beforeUnmount: function () { + beforeUnmount() { if (this.mutationObserver) { this.mutationObserver(); } @@ -323,9 +334,6 @@ export default { edit() { this.openmct.editor.edit(); }, - getViewKey() { - return this.viewKey; - }, promptUserandCancelEditing() { let dialog = this.openmct.overlays.dialog({ iconClass: 'alert', From ceeb761d944313c8e11d7e4c36003b7ebaf5d95d Mon Sep 17 00:00:00 2001 From: John Hill Date: Thu, 14 Mar 2024 09:27:31 -0700 Subject: [PATCH 04/28] [build] Re-enable package lock (#7584) * include package lock * migrate to npm ci * remove cache busting doc and replace with npm run clean --------- Co-authored-by: Jesse Mazzella Co-authored-by: Andrew Henry --- .circleci/config.yml | 42 +- .github/PULL_REQUEST_TEMPLATE.md | 1 - .github/workflows/e2e-couchdb.yml | 2 +- .github/workflows/e2e-flakefinder.yml | 2 +- .github/workflows/e2e-perf.yml | 2 +- .github/workflows/e2e-pr.yml | 2 +- .github/workflows/npm-prerelease.yml | 4 +- .github/workflows/pr-platform.yml | 2 +- .gitignore | 3 - .npmrc | 3 - TESTING.md | 14 +- package-lock.json | 12284 ++++++++++++++++++++++++ package.json | 2 +- src/selection/Selection.js | 19 + 14 files changed, 12324 insertions(+), 58 deletions(-) create mode 100644 package-lock.json diff --git a/.circleci/config.yml b/.circleci/config.yml index 0dcca1d322..9656e12e99 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,4 +1,7 @@ version: 2.1 +orbs: + node: circleci/node@5.2.0 + browser-tools: circleci/browser-tools@1.3.0 executors: pw-focal-development: docker: @@ -11,47 +14,17 @@ executors: machine: image: ubuntu-2204:current docker_layer_caching: true -parameters: - BUST_CACHE: - description: "Set this with the CircleCI UI Trigger Workflow button (boolean = true) to bust the cache!" - default: false - type: boolean commands: build_and_install: - description: "All steps used to build and install. Will use cache if found" + description: "All steps used to build and install." parameters: node-version: type: string steps: - checkout - - restore_cache_cmd: - node-version: << parameters.node-version >> - node/install: node-version: << parameters.node-version >> - - run: npm install --no-audit --progress=false - restore_cache_cmd: - description: "Custom command for restoring cache with the ability to bust cache. When BUST_CACHE is set to true, jobs will not restore cache" - parameters: - node-version: - type: string - steps: - - when: - condition: - equal: [false, << pipeline.parameters.BUST_CACHE >>] - steps: - - restore_cache: - key: deps--{{ arch }}--{{ .Branch }}--<< parameters.node-version >>--{{ checksum "package.json" }}-{{ checksum ".circleci/config.yml" }} - save_cache_cmd: - description: "Custom command for saving cache." - parameters: - node-version: - type: string - steps: - - save_cache: - key: deps--{{ arch }}--{{ .Branch }}--<< parameters.node-version >>--{{ checksum "package.json" }}-{{ checksum ".circleci/config.yml" }} - paths: - - ~/.npm - - node_modules + - node/install-packages generate_and_store_version_and_filesystem_artifacts: description: "Track important packages and files" steps: @@ -71,9 +44,6 @@ commands: steps: - run: npm run cov:e2e:report || true - run: npm run cov:e2e:<>:publish -orbs: - node: circleci/node@5.1.0 - browser-tools: circleci/browser-tools@1.3.0 jobs: npm-audit: parameters: @@ -111,8 +81,6 @@ jobs: TESTFILES=$(circleci tests glob "src/**/*Spec.js") echo "$TESTFILES" | circleci tests run --command="xargs npm run test" --verbose - run: npm run cov:unit:publish - - save_cache_cmd: - node-version: <> - store_test_results: path: dist/reports/tests/ - store_artifacts: diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 34ab10e6a9..444d552e91 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -17,7 +17,6 @@ Closes @@ -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 From 46823ec225aeaa05bc4271efb88f35a591203deb Mon Sep 17 00:00:00 2001 From: Jesse Mazzella Date: Wed, 27 Mar 2024 09:09:57 -0700 Subject: [PATCH 24/28] chore(gha): run prcop workflow on milestone (#7647) chore: run prcop workflow on milestone --- .github/workflows/prcop.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/prcop.yml b/.github/workflows/prcop.yml index e933f7319e..86aa3b48c4 100644 --- a/.github/workflows/prcop.yml +++ b/.github/workflows/prcop.yml @@ -5,6 +5,8 @@ on: types: - labeled - unlabeled + - milestoned + - demilestoned - opened - reopened - synchronize From a5c6b141a6bbf510a5821ed738b3ef873cc736cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Mar 2024 13:12:48 -0700 Subject: [PATCH 25/28] chore(deps-dev): bump express from 4.18.3 to 4.19.2 (#7646) Bumps [express](https://github.com/expressjs/express) from 4.18.3 to 4.19.2. - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/master/History.md) - [Commits](https://github.com/expressjs/express/compare/4.18.3...4.19.2) --- updated-dependencies: - dependency-name: express dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5f68e908d4..910d7097ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5441,9 +5441,9 @@ } }, "node_modules/express": { - "version": "4.18.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.3.tgz", - "integrity": "sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw==", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "dev": true, "dependencies": { "accepts": "~1.3.8", @@ -5451,7 +5451,7 @@ "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -5483,9 +5483,9 @@ } }, "node_modules/express/node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "dev": true, "engines": { "node": ">= 0.6" From a3fb84ad4386b7349af1410b667eb58605b7ae3d Mon Sep 17 00:00:00 2001 From: Jesse Mazzella Date: Thu, 28 Mar 2024 14:49:00 -0700 Subject: [PATCH 26/28] chore: remove `type: module`, create `openmct-e2e` subpackage (#7590) * fix: remove mystery webpack code * fix: remove type:module and specify exports - we aren't a module... yet * fix: rename webpack*.js to webpack*.mjs so we can use import/export. fix imports * fix: exports format * fix: woops, need to add `start` script back * chore: split e2e into its own module * fix: use normal Painterro import * fix: update e2e pathing * fix: copy over helper functions * chore: specify `cwd` for playwright configs so that openmct npm commands work as intended in any environment * chore: add pretest script to e2e package.json * chore: don't package e2e * refactor: tidy up webpack common config * chore: compile types to a single file * chore: fix visual test npm scripts * chore: fix import pathing * chore: define package exports, move test specific dependencies to the subpackage * chore: export test framework from openmct-e2e * chore: export baseFixtures also * chore: let `openmct` and `openmct-e2e` share `node_modules/` * chore: use `--workspace`, remove pretest script * Revert "fix: remove mystery webpack code" This reverts commit eb14d52569ffa27ab1a090b883694f4707b59cd0. * chore: update package-lock * chore: add `.npmignore` * fix: *js -> mjs --- .npmignore | 6 - .../{webpack.common.js => webpack.common.mjs} | 15 +- ...bpack.coverage.js => webpack.coverage.mjs} | 4 +- .webpack/{webpack.dev.js => webpack.dev.mjs} | 7 +- .../{webpack.prod.js => webpack.prod.mjs} | 2 +- TESTING.md | 2 +- e2e/README.md | 6 +- e2e/index.js | 8 + e2e/package-lock.json | 1449 +++++++++++++++++ e2e/package.json | 27 + e2e/playwright-ci.config.js | 1 + e2e/playwright-local.config.js | 1 + e2e/playwright-mobile.config.js | 1 + e2e/playwright-performance-dev.config.js | 1 + e2e/playwright-performance-prod.config.js | 1 + e2e/playwright-visual-a11y.config.js | 1 + e2e/playwright-watch.config.js | 1 + e2e/tests/functional/forms.e2e.spec.js | 4 +- .../planning/ganttChart.e2e.spec.js | 56 +- .../contract/imagery.contract.perf.spec.js | 2 +- .../contract/notebook.contract.perf.spec.js | 2 +- .../components/inspector.visual.spec.js | 2 +- .../controlledClock.visual.spec.js | 2 +- .../visual-a11y/defaultPlugins.visual.spec.js | 2 +- karma.conf.cjs | 4 +- package-lock.json | 42 +- package.json | 68 +- packages/e2e/.npmignore | 7 + .../notebook/utils/painterroInstance.js | 2 +- tsconfig.json | 18 +- 30 files changed, 1655 insertions(+), 89 deletions(-) rename .webpack/{webpack.common.js => webpack.common.mjs} (93%) rename .webpack/{webpack.coverage.js => webpack.coverage.mjs} (82%) rename .webpack/{webpack.dev.js => webpack.dev.mjs} (96%) rename .webpack/{webpack.prod.js => webpack.prod.mjs} (89%) create mode 100644 e2e/index.js create mode 100644 e2e/package-lock.json create mode 100644 e2e/package.json create mode 100644 packages/e2e/.npmignore diff --git a/.npmignore b/.npmignore index 1bdde6a5f5..c26dbd4b3c 100644 --- a/.npmignore +++ b/.npmignore @@ -22,9 +22,3 @@ !index.html !openmct.js !SECURITY.md - -# Add e2e tests to npm package -!/e2e/**/* - -# ... except our test-data folder files. -/e2e/test-data/*.json diff --git a/.webpack/webpack.common.js b/.webpack/webpack.common.mjs similarity index 93% rename from .webpack/webpack.common.js rename to .webpack/webpack.common.mjs index 1dc3af7ba6..7a7a3a2023 100644 --- a/.webpack/webpack.common.js +++ b/.webpack/webpack.common.mjs @@ -1,8 +1,8 @@ /* This is the OpenMCT common webpack file. It is imported by the other three webpack configurations: - - webpack.prod.js - the production configuration for OpenMCT (default) - - webpack.dev.js - the development configuration for OpenMCT - - webpack.coverage.js - imports webpack.dev.js and adds code coverage + - webpack.prod.mjs - the production configuration for OpenMCT (default) + - webpack.dev.mjs - the development configuration for OpenMCT + - webpack.coverage.mjs - imports webpack.dev.js and adds code coverage There are separate npm scripts to use these configurations, though simply running `npm install` will use the default production configuration. */ @@ -15,6 +15,7 @@ import CopyWebpackPlugin from 'copy-webpack-plugin'; import MiniCssExtractPlugin from 'mini-css-extract-plugin'; import { VueLoaderPlugin } from 'vue-loader'; import webpack from 'webpack'; +import { merge } from 'webpack-merge'; let gitRevision = 'error-retrieving-revision'; let gitBranch = 'error-retrieving-branch'; @@ -54,9 +55,11 @@ const config = { globalObject: 'this', filename: '[name].js', path: path.resolve(projectRootDir, 'dist'), - library: 'openmct', - libraryExport: 'default', - libraryTarget: 'umd', + library: { + name: 'openmct', + type: 'umd', + export: 'default' + }, publicPath: '', hashFunction: 'xxhash64', clean: true diff --git a/.webpack/webpack.coverage.js b/.webpack/webpack.coverage.mjs similarity index 82% rename from .webpack/webpack.coverage.js rename to .webpack/webpack.coverage.mjs index 0493efaff8..47204a30f8 100644 --- a/.webpack/webpack.coverage.js +++ b/.webpack/webpack.coverage.mjs @@ -1,10 +1,10 @@ /* -This file extends the webpack.dev.js config to add babel istanbul coverage. +This file extends the webpack.dev.mjs config to add babel istanbul coverage. OpenMCT Continuous Integration servers use this configuration to add code coverage information to pull requests. */ -import config from './webpack.dev.js'; +import config from './webpack.dev.mjs'; config.devtool = 'source-map'; config.devServer.hot = false; diff --git a/.webpack/webpack.dev.js b/.webpack/webpack.dev.mjs similarity index 96% rename from .webpack/webpack.dev.js rename to .webpack/webpack.dev.mjs index 3d18812e97..46170f08f7 100644 --- a/.webpack/webpack.dev.js +++ b/.webpack/webpack.dev.mjs @@ -1,14 +1,15 @@ /* This configuration should be used for development purposes. It contains full source map, a devServer (which be invoked using by `npm start`), and a non-minified Vue.js distribution. -If OpenMCT is to be used for a production server, use webpack.prod.js instead. +If OpenMCT is to be used for a production server, use webpack.prod.mjs instead. */ +import { fileURLToPath } from 'node:url'; + import path from 'path'; import webpack from 'webpack'; import { merge } from 'webpack-merge'; -import { fileURLToPath } from 'node:url'; -import common from './webpack.common.js'; +import common from './webpack.common.mjs'; export default merge(common, { mode: 'development', diff --git a/.webpack/webpack.prod.js b/.webpack/webpack.prod.mjs similarity index 89% rename from .webpack/webpack.prod.js rename to .webpack/webpack.prod.mjs index 9224625a12..f0656b67e0 100644 --- a/.webpack/webpack.prod.js +++ b/.webpack/webpack.prod.mjs @@ -6,7 +6,7 @@ It is the default webpack configuration. import webpack from 'webpack'; import { merge } from 'webpack-merge'; -import common from './webpack.common.js'; +import common from './webpack.common.mjs'; export default merge(common, { mode: 'production', diff --git a/TESTING.md b/TESTING.md index 9bf73022a0..ba8f9cc8a3 100644 --- a/TESTING.md +++ b/TESTING.md @@ -63,7 +63,7 @@ Once the file is generated, it can be published to codecov with ### e2e The e2e line coverage is a bit more complex than the karma implementation. This is the general sequence of events: -1. Each e2e suite will start webpack with the ```npm run start:coverage``` command with config `webpack.coverage.js` and the `babel-plugin-istanbul` plugin to generate code coverage during e2e test execution using our custom [baseFixture](./baseFixtures.js). +1. Each e2e suite will start webpack with the ```npm run start:coverage``` command with config `webpack.coverage.mjs` and the `babel-plugin-istanbul` plugin to generate code coverage during e2e test execution using our custom [baseFixture](./baseFixtures.js). 1. During testcase execution, each e2e shard will generate its piece of the larger coverage suite. **This coverage file is not merged**. The raw coverage file is stored in a `.nyc_report` directory. 1. [nyc](https://github.com/istanbuljs/nyc) converts this directory into a `lcov` file with the following command `npm run cov:e2e:report` 1. Most of the tests are run in the '@stable' configuration and focus on chrome/ubuntu at a single resolution. This coverage is published to codecov with `npm run cov:e2e:stable:publish`. diff --git a/e2e/README.md b/e2e/README.md index ed9096b607..e9f351a64e 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -167,9 +167,9 @@ When an a11y test fails, the result must be interpreted in the html test report The open source performance tests function in three ways which match their naming and folder structure: -`./e2e/tests/performance` - The tests at the root of this folder path detect functional changes which are mostly apparent with large performance regressions like [this](https://github.com/nasa/openmct/issues/6879). These tests run against openmct webpack in `production-mode` with the `npm run test:perf:localhost` script. -`./e2e/tests/performance/contract/` - These tests serve as [contracts](https://martinfowler.com/bliki/ContractTest.html) for the locator logic, functionality, and assumptions will work in our downstream, closed source test suites. These tests run against openmct webpack in `dev-mode` with the `npm run test:perf:contract` script. -`./e2e/tests/performance/memory/` - These tests execute memory leak detection checks in various ways. This is expected to evolve as we move to the `memlab` project. These tests run against openmct webpack in `production-mode` with the `npm run test:perf:memory` script. +`tests/performance` - The tests at the root of this folder path detect functional changes which are mostly apparent with large performance regressions like [this](https://github.com/nasa/openmct/issues/6879). These tests run against openmct webpack in `production-mode` with the `npm run test:perf:localhost` script. +`tests/performance/contract/` - These tests serve as [contracts](https://martinfowler.com/bliki/ContractTest.html) for the locator logic, functionality, and assumptions will work in our downstream, closed source test suites. These tests run against openmct webpack in `dev-mode` with the `npm run test:perf:contract` script. +`tests/performance/memory/` - These tests execute memory leak detection checks in various ways. This is expected to evolve as we move to the `memlab` project. These tests run against openmct webpack in `production-mode` with the `npm run test:perf:memory` script. These tests are expected to become blocking and gating with assertions as we extend the capabilities of Playwright. diff --git a/e2e/index.js b/e2e/index.js new file mode 100644 index 0000000000..f6897363de --- /dev/null +++ b/e2e/index.js @@ -0,0 +1,8 @@ +// Import everything from the specific fixture files +import * as appActions from './appActions.js'; +import * as avpFixtures from './avpFixtures.js'; +import * as baseFixtures from './baseFixtures.js'; +import * as pluginFixtures from './pluginFixtures.js'; + +// Export these as named exports +export { appActions, avpFixtures, baseFixtures, pluginFixtures }; diff --git a/e2e/package-lock.json b/e2e/package-lock.json new file mode 100644 index 0000000000..145ab2b706 --- /dev/null +++ b/e2e/package-lock.json @@ -0,0 +1,1449 @@ +{ + "name": "openmct-e2e", + "version": "4.0.0-next", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "openmct-e2e", + "version": "4.0.0-next", + "license": "Apache-2.0", + "devDependencies": { + "@axe-core/playwright": "4.8.5", + "@percy/cli": "1.27.4", + "@percy/playwright": "1.0.4", + "@playwright/test": "1.42.1", + "@types/sinonjs__fake-timers": "8.1.5", + "sinon": "17.0.0" + } + }, + "node_modules/@axe-core/playwright": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.8.5.tgz", + "integrity": "sha512-GFdXXAEn9uk0Vyzgl2eEP+VwvgGzas0YSnacoJ/0U237G83zWZ1PhbP/RDymm0cqevoA+xRjMo+ONzh9Q711nw==", + "dev": true, + "dependencies": { + "axe-core": "~4.8.4" + }, + "peerDependencies": { + "playwright-core": ">= 1.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@percy/cli": { + "version": "1.27.4", + "resolved": "https://registry.npmjs.org/@percy/cli/-/cli-1.27.4.tgz", + "integrity": "sha512-eIM44ejCMFc/S2W7X0htV+lvvmf63x5CaBpsSoQ9LRc/W02zHVAwQYdFFUowZEK6G1EwJEPIUnDxuuEx9PLG5A==", + "dev": true, + "dependencies": { + "@percy/cli-app": "1.27.4", + "@percy/cli-build": "1.27.4", + "@percy/cli-command": "1.27.4", + "@percy/cli-config": "1.27.4", + "@percy/cli-exec": "1.27.4", + "@percy/cli-snapshot": "1.27.4", + "@percy/cli-upload": "1.27.4", + "@percy/client": "1.27.4", + "@percy/logger": "1.27.4" + }, + "bin": { + "percy": "bin/run.cjs" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@percy/cli-app": { + "version": "1.27.4", + "resolved": "https://registry.npmjs.org/@percy/cli-app/-/cli-app-1.27.4.tgz", + "integrity": "sha512-av/s6K2QmQgq4SCQQ+3lmteNHeQtIpMeBjMfSgxs9zeBoPVOMx5hXrdsi6l7ChvOLXyYfzl/TbEuwrSDXiA8mw==", + "dev": true, + "dependencies": { + "@percy/cli-command": "1.27.4", + "@percy/cli-exec": "1.27.4" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@percy/cli-build": { + "version": "1.27.4", + "resolved": "https://registry.npmjs.org/@percy/cli-build/-/cli-build-1.27.4.tgz", + "integrity": "sha512-tzCAcV0sAw608Gr/Q6NtPvVkA8dnIehMzvEXNIN3WP9DkprOgu7MYuexN0fZXf4vSroDWYXT87pHYP8YrrnDag==", + "dev": true, + "dependencies": { + "@percy/cli-command": "1.27.4" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@percy/cli-command": { + "version": "1.27.4", + "resolved": "https://registry.npmjs.org/@percy/cli-command/-/cli-command-1.27.4.tgz", + "integrity": "sha512-YDKeeOr1MvksDOnc2ZKQ/XuERGrWwzuT/vWZ9it8L+0SyPj28UbklDu0e9zBgPsSDfxJlIvsWXRuHNGHsweKXg==", + "dev": true, + "dependencies": { + "@percy/config": "1.27.4", + "@percy/core": "1.27.4", + "@percy/logger": "1.27.4" + }, + "bin": { + "percy-cli-readme": "bin/readme.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@percy/cli-config": { + "version": "1.27.4", + "resolved": "https://registry.npmjs.org/@percy/cli-config/-/cli-config-1.27.4.tgz", + "integrity": "sha512-wFtQwPw4LEqpcZ6ac6WtejyGrvrrzzLdyvXNvsCPQLE47qXnXVXJ+E99k9KGcjavtUuPxrbWtX996Fz9Fb5hoQ==", + "dev": true, + "dependencies": { + "@percy/cli-command": "1.27.4" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@percy/cli-exec": { + "version": "1.27.4", + "resolved": "https://registry.npmjs.org/@percy/cli-exec/-/cli-exec-1.27.4.tgz", + "integrity": "sha512-aSDLvzXXdwJso+p5iI4iTOa7AYzgFdRoqY9ij/R5aAL9juNkvG5QatB1bkUNbJabKFe16t7iigt4eJnlS0R13A==", + "dev": true, + "dependencies": { + "@percy/cli-command": "1.27.4", + "cross-spawn": "^7.0.3", + "which": "^2.0.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@percy/cli-snapshot": { + "version": "1.27.4", + "resolved": "https://registry.npmjs.org/@percy/cli-snapshot/-/cli-snapshot-1.27.4.tgz", + "integrity": "sha512-dDT2UpeP6X5NcMdj3AKLhHGmnobwzlXsHa52C+ne3kg3HSZgaXH9OsNY866Xe7onvcsZxvnRKDYHmWW6kC3cKQ==", + "dev": true, + "dependencies": { + "@percy/cli-command": "1.27.4", + "yaml": "^2.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@percy/cli-upload": { + "version": "1.27.4", + "resolved": "https://registry.npmjs.org/@percy/cli-upload/-/cli-upload-1.27.4.tgz", + "integrity": "sha512-+4mcEOUydFubyMWVzQjPV79sL1Jar95SR7Yr7Vp4FBoE0iq0CbaHoJtyOWDfwvHYYp4rRjVMxpY0ha3jnmF0mA==", + "dev": true, + "dependencies": { + "@percy/cli-command": "1.27.4", + "fast-glob": "^3.2.11", + "image-size": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@percy/client": { + "version": "1.27.4", + "resolved": "https://registry.npmjs.org/@percy/client/-/client-1.27.4.tgz", + "integrity": "sha512-1F8ulTJhfk4/Lgj1Cn0blaRd8vTRJDxahAGseTbfrnZ2PHsftPZ65/5nCHPtpdD/2CE8N5COBQscGTMQQO+hBA==", + "dev": true, + "dependencies": { + "@percy/env": "1.27.4", + "@percy/logger": "1.27.4" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@percy/config": { + "version": "1.27.4", + "resolved": "https://registry.npmjs.org/@percy/config/-/config-1.27.4.tgz", + "integrity": "sha512-mlgiOdzdSfUSx9FskVIjmbT/iHbTif0Ow5evZQJTT1W0xgHOBWDCZyhINdsqulSBw+K1PNhHsu1J0h2ijxF4uA==", + "dev": true, + "dependencies": { + "@percy/logger": "1.27.4", + "ajv": "^8.6.2", + "cosmiconfig": "^8.0.0", + "yaml": "^2.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@percy/core": { + "version": "1.27.4", + "resolved": "https://registry.npmjs.org/@percy/core/-/core-1.27.4.tgz", + "integrity": "sha512-WdsA4zlPgXl9xj+a5WW2wA20iU6VTDmRq5sgsYNSuPzZfQB2I5Cecgvb55p86dhlUTbPJrC76daQKzDTGe0hfA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@percy/client": "1.27.4", + "@percy/config": "1.27.4", + "@percy/dom": "1.27.4", + "@percy/logger": "1.27.4", + "@percy/webdriver-utils": "1.27.4", + "content-disposition": "^0.5.4", + "cross-spawn": "^7.0.3", + "extract-zip": "^2.0.1", + "fast-glob": "^3.2.11", + "micromatch": "^4.0.4", + "mime-types": "^2.1.34", + "path-to-regexp": "^6.2.0", + "rimraf": "^3.0.2", + "ws": "^8.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@percy/dom": { + "version": "1.27.4", + "resolved": "https://registry.npmjs.org/@percy/dom/-/dom-1.27.4.tgz", + "integrity": "sha512-pwPDx3e9y7uRobVlEya8xu3BB3GeXbC74kQ6pPM/wFYDwi/Dg8DJywCsj5Nko/7QuhXP02rYgatkbREOIRxDnA==", + "dev": true + }, + "node_modules/@percy/env": { + "version": "1.27.4", + "resolved": "https://registry.npmjs.org/@percy/env/-/env-1.27.4.tgz", + "integrity": "sha512-Xl2VUpljOrlCvAp/+KfmN9NUcTGpRdXPa1U9zSIyBnV/oAksp3/CK5EPpKZX/f8xUUkTp78UPaG99sEMA8VvXQ==", + "dev": true, + "dependencies": { + "@percy/logger": "1.27.4" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@percy/logger": { + "version": "1.27.4", + "resolved": "https://registry.npmjs.org/@percy/logger/-/logger-1.27.4.tgz", + "integrity": "sha512-AwXqYaDkHaq1TPkP+ByB8rjvH9ddvkAH9tFd2kmq8AeFFXZ0amAPSbm6u090OUtdHWjRmKQK9JjSouBxEh0aRw==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@percy/playwright": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@percy/playwright/-/playwright-1.0.4.tgz", + "integrity": "sha512-prXvaEyvM6M7vGx0YujdMmFf+rZqkQpDEK32ZOwXbMzKYhGXjyiYESa4Axhc+Pz/c0THhXZWH4/70IW0RcyCQA==", + "dev": true, + "dependencies": { + "@percy/sdk-utils": "^1.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "playwright-core": ">=1" + } + }, + "node_modules/@percy/sdk-utils": { + "version": "1.28.2", + "resolved": "https://registry.npmjs.org/@percy/sdk-utils/-/sdk-utils-1.28.2.tgz", + "integrity": "sha512-cMFz8AjZ2KunN0dVwzA+Wosk4B+6G9dUkh2YPhYvqs0KLcCyYs3s91IzOQmtBOYwAUVja/W/u6XmBHw0jaxg0A==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@percy/webdriver-utils": { + "version": "1.27.4", + "resolved": "https://registry.npmjs.org/@percy/webdriver-utils/-/webdriver-utils-1.27.4.tgz", + "integrity": "sha512-pZOOYns8Fikh2qlbxO16DxFEnCrnFIoLpE7iz4M9jXxOfk16VZF1PWknMChSr5NqG2I9k2OMjizUE2j8zvtl2Q==", + "dev": true, + "dependencies": { + "@percy/config": "1.27.4", + "@percy/sdk-utils": "1.27.4" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@percy/webdriver-utils/node_modules/@percy/sdk-utils": { + "version": "1.27.4", + "resolved": "https://registry.npmjs.org/@percy/sdk-utils/-/sdk-utils-1.27.4.tgz", + "integrity": "sha512-vhPcdtmJlvTYJ5VOqiVzo02ujdtBFNw1/Bj+2ybiZgn7PkCDPFcITfXoWWPea319EIibGC4ZHjWHctRBgtW/tQ==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@playwright/test": { + "version": "1.42.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.42.1.tgz", + "integrity": "sha512-Gq9rmS54mjBL/7/MvBaNOBwbfnh7beHvS6oS4srqXFcQHpQCV1+c8JXWE8VLPyRDhgS3H8x8A7hztqI9VnwrAQ==", + "dev": true, + "dependencies": { + "playwright": "1.42.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", + "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", + "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.11.28", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.28.tgz", + "integrity": "sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==", + "dev": true, + "optional": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "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/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/axe-core": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.8.4.tgz", + "integrity": "sha512-CZLSKisu/bhJ2awW4kJndluz2HLZYIHh5Uy1+ZwDRkJi69811xgIXXfdU9HSLX0Th+ILrHj8qfL/5wzamsFtQg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/image-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.1.1.tgz", + "integrity": "sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==", + "dev": true, + "dependencies": { + "queue": "6.0.2" + }, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=16.x" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/just-extend": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", + "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", + "dev": true + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/nise": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz", + "integrity": "sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/text-encoding": "^0.7.2", + "just-extend": "^6.2.0", + "path-to-regexp": "^6.2.1" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", + "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/playwright": { + "version": "1.42.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.42.1.tgz", + "integrity": "sha512-PgwB03s2DZBcNRoW+1w9E+VkLBxweib6KTXM0M3tkiT4jVxKSi6PmVJ591J+0u10LUrgxB7dLRbiJqO5s2QPMg==", + "dev": true, + "dependencies": { + "playwright-core": "1.42.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.42.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.42.1.tgz", + "integrity": "sha512-mxz6zclokgrke9p1vtdy/COWBH+eOZgYUVVU34C73M+4j4HLlQJHtfcqiqqxpP0o8HhMkflvfbquLX5dg6wlfA==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "dev": true, + "dependencies": { + "inherits": "~2.0.3" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/sinon": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.0.tgz", + "integrity": "sha512-p4lJiYKBoOEVUxxVIC9H1MM2znG1/c8gud++I2BauJA5hsz7hHsst35eurNWXTusBsIq66FzOQbZ/uMdpvbPIQ==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.5", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/sinon/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/sinon/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "optional": true + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yaml": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz", + "integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } +} diff --git a/e2e/package.json b/e2e/package.json new file mode 100644 index 0000000000..ede4211362 --- /dev/null +++ b/e2e/package.json @@ -0,0 +1,27 @@ +{ + "name": "openmct-e2e", + "version": "4.0.0-next", + "description": "The Open MCT e2e framework", + "type": "module", + "module": "index.js", + "exports": { + ".": { + "import": "./index.js" + } + }, + "scripts": { + "pretest:visual": "npm install", + "test": "npx playwright test", + "test:visual": "percy exec" + }, + "devDependencies": { + "@types/sinonjs__fake-timers": "8.1.5", + "@percy/cli": "1.27.4", + "@percy/playwright": "1.0.4", + "@playwright/test": "1.42.1", + "@axe-core/playwright": "4.8.5", + "sinon": "17.0.0" + }, + "author": "NASA Ames Research Center", + "license": "Apache-2.0" +} \ No newline at end of file diff --git a/e2e/playwright-ci.config.js b/e2e/playwright-ci.config.js index d86fd2507e..ee44b40641 100644 --- a/e2e/playwright-ci.config.js +++ b/e2e/playwright-ci.config.js @@ -15,6 +15,7 @@ const config = { timeout: 60 * 1000, webServer: { command: 'npm run start:coverage', + cwd: '../', // Provide cwd for the root of the project url: 'http://localhost:8080/#', timeout: 200 * 1000, reuseExistingServer: true //This was originally disabled to prevent differences in local debugging vs. CI. However, it significantly speeds up local debugging. diff --git a/e2e/playwright-local.config.js b/e2e/playwright-local.config.js index 37afca2152..454dc97927 100644 --- a/e2e/playwright-local.config.js +++ b/e2e/playwright-local.config.js @@ -10,6 +10,7 @@ const config = { timeout: 30 * 1000, webServer: { command: 'npm run start:coverage', + cwd: '../', // Provide cwd for the root of the project url: 'http://localhost:8080/#', timeout: 120 * 1000, reuseExistingServer: true diff --git a/e2e/playwright-mobile.config.js b/e2e/playwright-mobile.config.js index a7cbf2b685..52b11aa324 100644 --- a/e2e/playwright-mobile.config.js +++ b/e2e/playwright-mobile.config.js @@ -14,6 +14,7 @@ const config = { timeout: 30 * 1000, webServer: { command: 'npm run start:coverage', + cwd: '../', // Provide cwd for the root of the project url: 'http://localhost:8080/#', timeout: 200 * 1000, reuseExistingServer: true //This was originally disabled to prevent differences in local debugging vs. CI. However, it significantly speeds up local debugging. diff --git a/e2e/playwright-performance-dev.config.js b/e2e/playwright-performance-dev.config.js index a10c350e80..0ad3a8835f 100644 --- a/e2e/playwright-performance-dev.config.js +++ b/e2e/playwright-performance-dev.config.js @@ -10,6 +10,7 @@ const config = { workers: 1, //Only run in serial with 1 worker webServer: { command: 'npm run start', //need development mode for performance.marks and others + cwd: '../', // Provide cwd for the root of the project url: 'http://localhost:8080/#', timeout: 200 * 1000, reuseExistingServer: false diff --git a/e2e/playwright-performance-prod.config.js b/e2e/playwright-performance-prod.config.js index 92976fb48e..791c8f1d0d 100644 --- a/e2e/playwright-performance-prod.config.js +++ b/e2e/playwright-performance-prod.config.js @@ -10,6 +10,7 @@ const config = { workers: 1, //Only run in serial with 1 worker webServer: { command: 'npm run start:prod', //Production mode + cwd: '../', // Provide cwd for the root of the project url: 'http://localhost:8080/#', timeout: 200 * 1000, reuseExistingServer: false //Must be run with this option to prevent dev mode diff --git a/e2e/playwright-visual-a11y.config.js b/e2e/playwright-visual-a11y.config.js index 5a80cffedc..e1de0cb879 100644 --- a/e2e/playwright-visual-a11y.config.js +++ b/e2e/playwright-visual-a11y.config.js @@ -10,6 +10,7 @@ const config = { workers: 1, //Lower stress on Circle CI Agent for Visual tests https://github.com/percy/cli/discussions/1067 webServer: { command: 'npm run start:coverage', + cwd: '../', // Provide cwd for the root of the project url: 'http://localhost:8080/#', timeout: 200 * 1000, reuseExistingServer: !process.env.CI diff --git a/e2e/playwright-watch.config.js b/e2e/playwright-watch.config.js index bd40be4638..3d4996ce00 100644 --- a/e2e/playwright-watch.config.js +++ b/e2e/playwright-watch.config.js @@ -11,6 +11,7 @@ const config = { timeout: 60 * 1000, webServer: { command: 'npm run start', //Start in dev mode for hot reloading + cwd: '../', // Provide cwd for the root of the project url: 'http://localhost:8080/#', timeout: 200 * 1000, reuseExistingServer: true //This was originally disabled to prevent differences in local debugging vs. CI. However, it significantly speeds up local debugging. diff --git a/e2e/tests/functional/forms.e2e.spec.js b/e2e/tests/functional/forms.e2e.spec.js index 7ec750c037..7e3ba59221 100644 --- a/e2e/tests/functional/forms.e2e.spec.js +++ b/e2e/tests/functional/forms.e2e.spec.js @@ -31,8 +31,8 @@ import { createDomainObjectWithDefaults } from '../../appActions.js'; import { expect, test } from '../../pluginFixtures.js'; const TEST_FOLDER = 'test folder'; -const jsonFilePath = 'e2e/test-data/ExampleLayouts.json'; -const imageFilePath = 'e2e/test-data/rick.jpg'; +const jsonFilePath = 'test-data/ExampleLayouts.json'; +const imageFilePath = 'test-data/rick.jpg'; test.describe('Form Validation Behavior', () => { test('Required Field indicators appear if title is empty and can be corrected', async ({ diff --git a/e2e/tests/functional/planning/ganttChart.e2e.spec.js b/e2e/tests/functional/planning/ganttChart.e2e.spec.js index ff72955e26..554f81d1c6 100644 --- a/e2e/tests/functional/planning/ganttChart.e2e.spec.js +++ b/e2e/tests/functional/planning/ganttChart.e2e.spec.js @@ -21,7 +21,6 @@ *****************************************************************************/ import fs from 'fs'; -import { getPreciseDuration } from '../../../../src/utils/duration.js'; import { createDomainObjectWithDefaults, createPlanFromJSON } from '../../../appActions.js'; import { assertPlanActivities, @@ -132,3 +131,58 @@ test.describe('Gantt Chart', () => { ); }); }); + +const ONE_SECOND = 1000; +const ONE_MINUTE = 60 * ONE_SECOND; +const ONE_HOUR = ONE_MINUTE * 60; +const ONE_DAY = ONE_HOUR * 24; + +function normalizeAge(num) { + const hundredtized = num * 100; + const isWhole = hundredtized % 100 === 0; + + return isWhole ? hundredtized / 100 : num; +} + +function padLeadingZeros(num, numOfLeadingZeros) { + return num.toString().padStart(numOfLeadingZeros, '0'); +} + +function toDoubleDigits(num) { + return padLeadingZeros(num, 2); +} + +function toTripleDigits(num) { + return padLeadingZeros(num, 3); +} + +function getPreciseDuration(value, { excludeMilliSeconds, useDayFormat } = {}) { + let preciseDuration; + const ms = value || 0; + + const duration = [ + Math.floor(normalizeAge(ms / ONE_DAY)), + toDoubleDigits(Math.floor(normalizeAge((ms % ONE_DAY) / ONE_HOUR))), + toDoubleDigits(Math.floor(normalizeAge((ms % ONE_HOUR) / ONE_MINUTE))), + toDoubleDigits(Math.floor(normalizeAge((ms % ONE_MINUTE) / ONE_SECOND))) + ]; + if (!excludeMilliSeconds) { + duration.push(toTripleDigits(Math.floor(normalizeAge(ms % ONE_SECOND)))); + } + + if (useDayFormat) { + // Format days as XD + const days = duration.shift(); + if (days > 0) { + preciseDuration = `${days}D ${duration.join(':')}`; + } else { + preciseDuration = duration.join(':'); + } + } else { + const days = toDoubleDigits(duration.shift()); + duration.unshift(days); + preciseDuration = duration.join(':'); + } + + return preciseDuration; +} diff --git a/e2e/tests/performance/contract/imagery.contract.perf.spec.js b/e2e/tests/performance/contract/imagery.contract.perf.spec.js index 1608b7956c..7b3b00b6cf 100644 --- a/e2e/tests/performance/contract/imagery.contract.perf.spec.js +++ b/e2e/tests/performance/contract/imagery.contract.perf.spec.js @@ -34,7 +34,7 @@ TODO: import { expect, test } from '@playwright/test'; -const filePath = 'e2e/test-data/PerformanceDisplayLayout.json'; +const filePath = 'test-data/PerformanceDisplayLayout.json'; test.describe('Performance tests', () => { test.beforeEach(async ({ page, browser }, testInfo) => { diff --git a/e2e/tests/performance/contract/notebook.contract.perf.spec.js b/e2e/tests/performance/contract/notebook.contract.perf.spec.js index a1a96e3a28..5a63d73ed3 100644 --- a/e2e/tests/performance/contract/notebook.contract.perf.spec.js +++ b/e2e/tests/performance/contract/notebook.contract.perf.spec.js @@ -33,7 +33,7 @@ TODO: import { expect, test } from '@playwright/test'; -const notebookFilePath = 'e2e/test-data/PerformanceNotebook.json'; +const notebookFilePath = 'test-data/PerformanceNotebook.json'; test.describe('Performance tests', () => { test.beforeEach(async ({ page, browser }, testInfo) => { diff --git a/e2e/tests/visual-a11y/components/inspector.visual.spec.js b/e2e/tests/visual-a11y/components/inspector.visual.spec.js index efe9d65cab..90e641fed4 100644 --- a/e2e/tests/visual-a11y/components/inspector.visual.spec.js +++ b/e2e/tests/visual-a11y/components/inspector.visual.spec.js @@ -33,7 +33,7 @@ test.describe('Visual - Inspector @ally @clock', () => { await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' }); }); test.use({ - storageState: './e2e/test-data/overlay_plot_with_delay_storage.json', + storageState: 'test-data/overlay_plot_with_delay_storage.json', clockOptions: { now: MISSION_TIME, shouldAdvanceTime: true diff --git a/e2e/tests/visual-a11y/controlledClock.visual.spec.js b/e2e/tests/visual-a11y/controlledClock.visual.spec.js index ad832b841e..8778713107 100644 --- a/e2e/tests/visual-a11y/controlledClock.visual.spec.js +++ b/e2e/tests/visual-a11y/controlledClock.visual.spec.js @@ -35,7 +35,7 @@ test.describe('Visual - Controlled Clock @clock', () => { await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' }); }); test.use({ - storageState: './e2e/test-data/overlay_plot_with_delay_storage.json', + storageState: 'test-data/overlay_plot_with_delay_storage.json', clockOptions: { now: MISSION_TIME, shouldAdvanceTime: false //Don't advance the clock diff --git a/e2e/tests/visual-a11y/defaultPlugins.visual.spec.js b/e2e/tests/visual-a11y/defaultPlugins.visual.spec.js index ee90b6cc13..d7d022d39d 100644 --- a/e2e/tests/visual-a11y/defaultPlugins.visual.spec.js +++ b/e2e/tests/visual-a11y/defaultPlugins.visual.spec.js @@ -23,7 +23,7 @@ /* Collection of Visual Tests set to run in a default context with default Plugins. The tests within this suite are only meant to run against openmct's app.js started by `npm run start` within the -`./e2e/playwright-visual.config.js` file. +`playwright-visual.config.js` file. */ import percySnapshot from '@percy/playwright'; diff --git a/karma.conf.cjs b/karma.conf.cjs index af5e1befaf..1fc2256bfe 100644 --- a/karma.conf.cjs +++ b/karma.conf.cjs @@ -24,13 +24,13 @@ const loadWebpackConfig = async () => { if (process.env.KARMA_DEBUG) { return { - config: (await import('./.webpack/webpack.dev.js')).default, + config: (await import('./.webpack/webpack.dev.mjs')).default, browsers: ['ChromeDebugging'], singleRun: false }; } else { return { - config: (await import('./.webpack/webpack.coverage.js')).default, + config: (await import('./.webpack/webpack.coverage.mjs')).default, browsers: ['ChromeHeadless'], singleRun: true }; diff --git a/package-lock.json b/package-lock.json index 910d7097ec..9794ac219d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,13 +8,12 @@ "name": "openmct", "version": "4.0.0-next", "license": "Apache-2.0", + "workspaces": [ + "e2e" + ], "devDependencies": { - "@axe-core/playwright": "4.8.5", "@babel/eslint-parser": "7.23.3", "@braintree/sanitize-url": "6.0.4", - "@percy/cli": "1.27.4", - "@percy/playwright": "1.0.4", - "@playwright/test": "1.42.1", "@types/d3-axis": "3.0.6", "@types/d3-scale": "4.0.8", "@types/d3-selection": "3.0.10", @@ -22,7 +21,6 @@ "@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", @@ -81,7 +79,6 @@ "sanitize-html": "2.12.1", "sass": "1.71.1", "sass-loader": "14.1.1", - "sinon": "17.0.0", "style-loader": "3.3.3", "terser-webpack-plugin": "5.3.9", "tiny-emitter": "2.1.0", @@ -99,6 +96,19 @@ "node": ">=18.14.2 <22" } }, + "e2e": { + "name": "openmct-e2e", + "version": "4.0.0-next", + "license": "Apache-2.0", + "devDependencies": { + "@axe-core/playwright": "4.8.5", + "@percy/cli": "1.27.4", + "@percy/playwright": "1.0.4", + "@playwright/test": "1.42.1", + "@types/sinonjs__fake-timers": "8.1.5", + "sinon": "17.0.0" + } + }, "node_modules/@aashutoshrathi/word-wrap": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", @@ -1496,9 +1506,9 @@ } }, "node_modules/@percy/sdk-utils": { - "version": "1.28.1", - "resolved": "https://registry.npmjs.org/@percy/sdk-utils/-/sdk-utils-1.28.1.tgz", - "integrity": "sha512-joS3i5wjFYXRSVL/NbUvip+bB7ErgwNjoDcID31l61y/QaSYUVCOxl/Fy4nvePJtHVyE1hpV0O7XO3tkoG908g==", + "version": "1.28.2", + "resolved": "https://registry.npmjs.org/@percy/sdk-utils/-/sdk-utils-1.28.2.tgz", + "integrity": "sha512-cMFz8AjZ2KunN0dVwzA+Wosk4B+6G9dUkh2YPhYvqs0KLcCyYs3s91IzOQmtBOYwAUVja/W/u6XmBHw0jaxg0A==", "dev": true, "engines": { "node": ">=14" @@ -1929,16 +1939,6 @@ "@types/node": "*" } }, - "node_modules/@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "dev": true, - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -8549,6 +8549,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openmct-e2e": { + "resolved": "e2e", + "link": true + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", diff --git a/package.json b/package.json index d8eae3bb41..60852a235a 100644 --- a/package.json +++ b/package.json @@ -2,15 +2,21 @@ "name": "openmct", "version": "4.0.0-next", "description": "The Open MCT core platform", - "type": "module", + "module": "dist/openmct.js", "main": "dist/openmct.js", + "types": "dist/types/index.d.ts", + "exports": { + ".": { + "import": "./dist/openmct.js", + "require": "./dist/openmct.js" + } + }, + "workspaces": [ + "e2e" + ], "devDependencies": { - "@axe-core/playwright": "4.8.5", "@babel/eslint-parser": "7.23.3", "@braintree/sanitize-url": "6.0.4", - "@percy/cli": "1.27.4", - "@percy/playwright": "1.0.4", - "@playwright/test": "1.42.1", "@types/d3-axis": "3.0.6", "@types/d3-scale": "4.0.8", "@types/d3-selection": "3.0.10", @@ -18,7 +24,6 @@ "@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", @@ -77,7 +82,6 @@ "sanitize-html": "2.12.1", "sass": "1.71.1", "sass-loader": "14.1.1", - "sinon": "17.0.0", "style-loader": "3.3.3", "terser-webpack-plugin": "5.3.9", "tiny-emitter": "2.1.0", @@ -92,39 +96,39 @@ "webpack-merge": "5.10.0" }, "scripts": { - "clean": "rm -rf ./dist ./node_modules ./coverage ./html-test-results ./test-results ./.nyc_output ", - "start": "npx webpack serve --config ./.webpack/webpack.dev.js", - "start:prod": "npx webpack serve --config ./.webpack/webpack.prod.js", - "start:coverage": "npx webpack serve --config ./.webpack/webpack.coverage.js", + "clean": "rm -rf ./dist ./node_modules ./coverage ./html-test-results ./test-results ./.nyc_output", + "start": "npx webpack serve --config ./.webpack/webpack.dev.mjs", + "start:prod": "npx webpack serve --config ./.webpack/webpack.prod.mjs", + "start:coverage": "npx webpack serve --config ./.webpack/webpack.coverage.mjs", "lint:js": "eslint \"example/**/*.js\" \"src/**/*.js\" \"e2e/**/*.js\" \"openmct.js\" --max-warnings=0", "lint:vue": "eslint \"src/**/*.vue\"", "lint:spelling": "cspell \"**/*.{js,md,vue}\" --show-context --gitignore --quiet", "lint": "run-p \"lint:js -- {1}\" \"lint:vue -- {1}\" \"lint:spelling -- {1}\" --", "lint:fix": "eslint example src e2e --ext .js,.vue openmct.js --fix", - "build:prod": "webpack --config ./.webpack/webpack.prod.js", - "build:dev": "webpack --config ./.webpack/webpack.dev.js", - "build:coverage": "webpack --config ./.webpack/webpack.coverage.js", - "build:watch": "webpack --config ./.webpack/webpack.dev.js --watch", + "build:prod": "webpack --config ./.webpack/webpack.prod.mjs", + "build:dev": "webpack --config ./.webpack/webpack.dev.mjs", + "build:coverage": "webpack --config ./.webpack/webpack.coverage.mjs", + "build:watch": "webpack --config ./.webpack/webpack.dev.mjs --watch", "info": "npx envinfo --system --browsers --npmPackages --binaries --languages --markdown", "test": "karma start karma.conf.cjs", "test:debug": "KARMA_DEBUG=true karma start karma.conf.cjs", - "test:e2e": "npx playwright test", - "test:e2e:a11y": "npx playwright test --config=e2e/playwright-visual-a11y.config.js --project=chrome --grep @a11y", - "test:e2e:mobile": "npx playwright test --config=e2e/playwright-mobile.config.js", - "test:e2e:couchdb": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep @couchdb --workers=1", - "test:e2e:stable": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep-invert \"@unstable|@couchdb|@generatedata\"", - "test:e2e:unstable": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep @unstable", - "test:e2e:local": "npx playwright test --config=e2e/playwright-local.config.js --project=chrome", - "test:e2e:generatedata": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep @generatedata", - "test:e2e:checksnapshots": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep @snapshot --retries=0", - "test:e2e:updatesnapshots": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep @snapshot --update-snapshots", - "test:e2e:visual:ci": "percy exec --config ./e2e/.percy.ci.yml --partial -- npx playwright test --config=e2e/playwright-visual-a11y.config.js --project=chrome --grep-invert @unstable", - "test:e2e:visual:full": "percy exec --config ./e2e/.percy.nightly.yml -- npx playwright test --config=e2e/playwright-visual-a11y.config.js --grep-invert @unstable", - "test:e2e:full": "npx playwright test --config=e2e/playwright-ci.config.js --grep-invert @couchdb", - "test:e2e:watch": "npx playwright test --ui --config=e2e/playwright-watch.config.js", - "test:perf:contract": "npx playwright test --config=e2e/playwright-performance-dev.config.js", - "test:perf:localhost": "npx playwright test --config=e2e/playwright-performance-prod.config.js --project=chrome", - "test:perf:memory": "npx playwright test --config=e2e/playwright-performance-prod.config.js --project=chrome-memory", + "test:e2e": "npm test --workspace e2e", + "test:e2e:a11y": "npm test --workspace e2e -- --config=playwright-visual-a11y.config.js --project=chrome --grep @a11y", + "test:e2e:mobile": "npm test --workspace e2e -- --config=playwright-mobile.config.js", + "test:e2e:couchdb": "npm test --workspace e2e -- --config=playwright-ci.config.js --project=chrome --grep @couchdb --workers=1", + "test:e2e:stable": "npm test --workspace e2e -- --config=playwright-ci.config.js --project=chrome --grep-invert \"@unstable|@couchdb|@generatedata\"", + "test:e2e:unstable": "npm test --workspace e2e -- --config=playwright-ci.config.js --project=chrome --grep @unstable", + "test:e2e:local": "npm test --workspace e2e -- --config=playwright-local.config.js --project=chrome", + "test:e2e:generatedata": "npm test --workspace e2e -- --config=playwright-ci.config.js --project=chrome --grep @generatedata", + "test:e2e:checksnapshots": "npm test --workspace e2e -- --config=playwright-ci.config.js --project=chrome --grep @snapshot --retries=0", + "test:e2e:updatesnapshots": "npm test --workspace e2e -- --config=playwright-ci.config.js --project=chrome --grep @snapshot --update-snapshots", + "test:e2e:visual:ci": "npm run test:visual --workspace e2e -- --config .percy.ci.yml --partial -- npx playwright test --config=playwright-visual-a11y.config.js --project=chrome --grep-invert @unstable", + "test:e2e:visual:full": "npm run test:visual --workspace e2e -- --config .percy.nightly.yml -- npx playwright test --config=playwright-visual-a11y.config.js --grep-invert @unstable", + "test:e2e:full": "npm test --workspace e2e -- --config=playwright-ci.config.js --grep-invert @couchdb", + "test:e2e:watch": "npm test --workspace e2e -- --ui --config=playwright-watch.config.js", + "test:perf:contract": "npm test --workspace e2e -- --config=playwright-performance-dev.config.js", + "test:perf:localhost": "npm test --workspace e2e -- --config=playwright-performance-prod.config.js --project=chrome", + "test:perf:memory": "npm test --workspace e2e -- --config=playwright-performance-prod.config.js --project=chrome-memory", "update-about-dialog-copyright": "perl -pi -e 's/20\\d\\d\\-202\\d/2014\\-2023/gm' ./src/ui/layout/AboutDialog.vue", "update-copyright-date": "npm run update-about-dialog-copyright && grep -lr --null --include=*.{js,scss,vue,ts,sh,html,md,frag} 'Copyright (c) 20' . | xargs -r0 perl -pi -e 's/Copyright\\s\\(c\\)\\s20\\d\\d\\-20\\d\\d/Copyright \\(c\\)\\ 2014\\-2024/gm'", "cov:e2e:report": "nyc report --reporter=lcovonly --report-dir=./coverage/e2e", diff --git a/packages/e2e/.npmignore b/packages/e2e/.npmignore new file mode 100644 index 0000000000..9b0060759a --- /dev/null +++ b/packages/e2e/.npmignore @@ -0,0 +1,7 @@ +* +!appActions.js +!baseFixtures.js +!pluginFixtures.js +!avpFixtures.js +!index.js +!*.md diff --git a/src/plugins/notebook/utils/painterroInstance.js b/src/plugins/notebook/utils/painterroInstance.js index 1f83637b42..f8d421fa11 100644 --- a/src/plugins/notebook/utils/painterroInstance.js +++ b/src/plugins/notebook/utils/painterroInstance.js @@ -46,7 +46,7 @@ export default class PainterroInstance { this.config.id = this.elementId; this.config.saveHandler = this.saveHandler.bind(this); - this.painterro = Painterro.default(this.config); + this.painterro = Painterro(this.config); } save(callback) { diff --git a/tsconfig.json b/tsconfig.json index 08102d357e..cb6a862477 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,14 +16,22 @@ "noImplicitAny": false, "module": "esnext", "moduleResolution": "node", - "outDir": "dist", + "outFile": "dist/types/index.d.ts", "skipLibCheck": true, "target": "ES2015", "paths": { // matches the alias in webpack config, so that types for those imports are visible. - "@/*": ["src/*"] + "@/*": [ + "src/*" + ] } }, - "include": ["src/api/**/*.js"], - "exclude": ["node_modules", "dist", "**/*Spec.js"] -} + "include": [ + "src/api/**/*.js" + ], + "exclude": [ + "node_modules", + "dist", + "**/*Spec.js" + ] +} \ No newline at end of file From 1671a585fb1618fe3a1457e06359a20f488f2e18 Mon Sep 17 00:00:00 2001 From: Jamie V Date: Thu, 28 Mar 2024 17:32:03 -0700 Subject: [PATCH 27/28] Mct7636 (#7645) * setting order for sort to descending if in performance mode and no sort set --------- Co-authored-by: Andrew Henry --- src/plugins/telemetryTable/TelemetryTable.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/plugins/telemetryTable/TelemetryTable.js b/src/plugins/telemetryTable/TelemetryTable.js index 00ae1159ee..3d4d91ae92 100644 --- a/src/plugins/telemetryTable/TelemetryTable.js +++ b/src/plugins/telemetryTable/TelemetryTable.js @@ -171,6 +171,10 @@ export default class TelemetryTable extends EventEmitter { this.removeTelemetryCollection(keyString); + let sortOptions = this.configuration.getConfiguration().sortOptions; + requestOptions.order = + sortOptions?.direction ?? (this.telemetryMode === 'performance' ? 'desc' : 'asc'); + if (this.telemetryMode === 'performance') { requestOptions.size = this.rowLimit; requestOptions.enforceSize = true; From f98eb319561955629a36d7436e18ff4fcd2a8821 Mon Sep 17 00:00:00 2001 From: Jesse Mazzella Date: Thu, 28 Mar 2024 17:32:46 -0700 Subject: [PATCH 28/28] fix: move file to correct folder (#7652) --- {packages/e2e => e2e}/.npmignore | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {packages/e2e => e2e}/.npmignore (100%) diff --git a/packages/e2e/.npmignore b/e2e/.npmignore similarity index 100% rename from packages/e2e/.npmignore rename to e2e/.npmignore