Compare commits

..

1 Commits

Author SHA1 Message Date
76a5fd9930 fix: enable devtools in prod 2024-05-08 19:45:37 -07:00
19 changed files with 49 additions and 224 deletions

View File

@ -5,7 +5,7 @@ orbs:
executors: executors:
pw-focal-development: pw-focal-development:
docker: docker:
- image: mcr.microsoft.com/playwright:v1.44.0-focal - image: mcr.microsoft.com/playwright:v1.42.1-focal
environment: environment:
NODE_ENV: development # Needed to ensure 'dist' folder created and devDependencies installed NODE_ENV: development # Needed to ensure 'dist' folder created and devDependencies installed
PERCY_POSTINSTALL_BROWSER: 'true' # Needed to store the percy browser in cache deps PERCY_POSTINSTALL_BROWSER: 'true' # Needed to store the percy browser in cache deps
@ -159,7 +159,7 @@ jobs:
steps: steps:
- build_and_install: - build_and_install:
node-version: lts/hydrogen node-version: lts/hydrogen
- run: npx playwright@1.44.0 install #Necessary for bare ubuntu machine - run: npx playwright@1.42.1 install #Necessary for bare ubuntu machine
- run: | - run: |
export $(cat src/plugins/persistence/couch/.env.ci | xargs) export $(cat src/plugins/persistence/couch/.env.ci | xargs)
docker-compose -f src/plugins/persistence/couch/couchdb-compose.yaml up --detach docker-compose -f src/plugins/persistence/couch/couchdb-compose.yaml up --detach

View File

@ -37,7 +37,7 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- run: npx playwright@1.44.0 install - run: npx playwright@1.42.1 install
- name: Start CouchDB Docker Container and Init with Setup Scripts - name: Start CouchDB Docker Container and Init with Setup Scripts
run: | run: |

View File

@ -30,7 +30,7 @@ jobs:
restore-keys: | restore-keys: |
${{ runner.os }}-node- ${{ runner.os }}-node-
- run: npx playwright@1.44.0 install - run: npx playwright@1.42.1 install
- run: npm ci --no-audit --progress=false - run: npm ci --no-audit --progress=false
- name: Run E2E Tests (Repeated 10 Times) - name: Run E2E Tests (Repeated 10 Times)

View File

@ -28,7 +28,7 @@ jobs:
restore-keys: | restore-keys: |
${{ runner.os }}-node- ${{ runner.os }}-node-
- run: npx playwright@1.44.0 install - run: npx playwright@1.42.1 install
- run: npm ci --no-audit --progress=false - run: npm ci --no-audit --progress=false
- run: npm run test:perf:localhost - run: npm run test:perf:localhost
- run: npm run test:perf:contract - run: npm run test:perf:contract

View File

@ -33,7 +33,7 @@ jobs:
restore-keys: | restore-keys: |
${{ runner.os }}-node- ${{ runner.os }}-node-
- run: npx playwright@1.44.0 install - run: npx playwright@1.42.1 install
- run: npx playwright install chrome-beta - run: npx playwright install chrome-beta
- run: npm ci --no-audit --progress=false - run: npm ci --no-audit --progress=false
- run: npm run test:e2e:full -- --max-failures=40 - run: npm run test:e2e:full -- --max-failures=40

View File

@ -90,7 +90,7 @@ const config = {
__OPENMCT_REVISION__: `'${gitRevision}'`, __OPENMCT_REVISION__: `'${gitRevision}'`,
__OPENMCT_BUILD_BRANCH__: `'${gitBranch}'`, __OPENMCT_BUILD_BRANCH__: `'${gitBranch}'`,
__VUE_OPTIONS_API__: true, // enable/disable Options API support, default: true __VUE_OPTIONS_API__: true, // enable/disable Options API support, default: true
__VUE_PROD_DEVTOOLS__: false // enable/disable devtools support in production, default: false __VUE_PROD_DEVTOOLS__: true // enable/disable devtools support in production, default: false
}), }),
new VueLoaderPlugin(), new VueLoaderPlugin(),
new CopyWebpackPlugin({ new CopyWebpackPlugin({

View File

@ -275,17 +275,6 @@ async function navigateToObjectWithFixedTimeBounds(page, url, start, end) {
); );
} }
/**
* Navigates directly to a given object url, in real-time mode.
* @param {import('@playwright/test').Page} page
* @param {string} url The url to the domainObject
*/
async function navigateToObjectWithRealTime(page, url, start = '1800000', end = '30000') {
await page.goto(
`${url}?tc.mode=local&tc.startDelta=${start}&tc.endDelta=${end}&tc.timeSystem=utc`
);
}
/** /**
* Open the given `domainObject`'s context menu from the object tree. * Open the given `domainObject`'s context menu from the object tree.
* Expands the path to the object and scrolls to it if necessary. * Expands the path to the object and scrolls to it if necessary.
@ -667,7 +656,6 @@ export {
getFocusedObjectUuid, getFocusedObjectUuid,
getHashUrlToDomainObject, getHashUrlToDomainObject,
navigateToObjectWithFixedTimeBounds, navigateToObjectWithFixedTimeBounds,
navigateToObjectWithRealTime,
openObjectTreeContextMenu, openObjectTreeContextMenu,
renameObjectFromContextMenu, renameObjectFromContextMenu,
setEndOffset, setEndOffset,

View File

@ -18,7 +18,7 @@
"@types/sinonjs__fake-timers": "8.1.5", "@types/sinonjs__fake-timers": "8.1.5",
"@percy/cli": "1.27.4", "@percy/cli": "1.27.4",
"@percy/playwright": "1.0.4", "@percy/playwright": "1.0.4",
"@playwright/test": "1.44.0", "@playwright/test": "1.42.1",
"@axe-core/playwright": "4.8.5", "@axe-core/playwright": "4.8.5",
"sinon": "17.0.0" "sinon": "17.0.0"
}, },

View File

@ -371,7 +371,7 @@ test.describe('Basic Condition Set Use', () => {
// Validate that the condition set is evaluating and outputting // Validate that the condition set is evaluating and outputting
// the correct value when the underlying telemetry subscription is active. // the correct value when the underlying telemetry subscription is active.
let outputValue = page.getByLabel('Current Output Value'); let outputValue = page.locator('[aria-label="Current Output Value"]');
await expect(outputValue).toHaveText('false'); await expect(outputValue).toHaveText('false');
await page.goto(exampleTelemetry.url); await page.goto(exampleTelemetry.url);
@ -462,7 +462,7 @@ test.describe('Basic Condition Set Use', () => {
// Validate that the condition set is evaluating and outputting // Validate that the condition set is evaluating and outputting
// the correct value when the underlying telemetry subscription is active. // the correct value when the underlying telemetry subscription is active.
let outputValue = page.getByLabel('Current Output Value'); let outputValue = page.locator('[aria-label="Current Output Value"]');
await expect(outputValue).toHaveText('false'); await expect(outputValue).toHaveText('false');
await page.goto(exampleTelemetry.url); await page.goto(exampleTelemetry.url);
@ -475,81 +475,3 @@ test.describe('Basic Condition Set Use', () => {
}); });
}); });
}); });
test.describe('Condition Set Composition', () => {
let conditionSet;
let exampleTelemetry;
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Create Condition Set
conditionSet = await createDomainObjectWithDefaults(page, {
type: 'Condition Set'
});
// Create Telemetry Object as child to Condition Set
exampleTelemetry = await createExampleTelemetryObject(page, conditionSet.uuid);
// Edit Condition Set
await page.goto(conditionSet.url);
await page.getByRole('button', { name: 'Edit Object' }).click();
// Add Condition to Condition Set
await page.getByRole('button', { name: 'Add Condition' }).click();
// Enter Condition Output
await page.getByLabel('Condition Name Input').first().fill('Negative');
await page.getByLabel('Condition Output Type').first().selectOption({ value: 'string' });
await page.getByLabel('Condition Output String').first().fill('Negative');
// Condition Trigger default is okay so no change needed to form
// Enter Condition Criterion
await page.getByLabel('Criterion Telemetry Selection').first().selectOption({ value: 'all' });
await page.getByLabel('Criterion Metadata Selection').first().selectOption({ value: 'sin' });
await page
.locator('select[aria-label="Criterion Comparison Selection"]')
.first()
.selectOption({ value: 'lessThan' });
await page.getByLabel('Criterion Input').first().fill('0');
// Save the Condition Set
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
});
test('You can remove telemetry from a condition set with existing conditions', async ({
page
}) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/7710'
});
await page.getByLabel('Expand My Items folder').click();
await page.getByLabel(`Expand ${conditionSet.name} conditionSet`).click();
await page
.getByLabel(`Navigate to ${exampleTelemetry.name}`, { exact: false })
.click({ button: 'right' });
await page
.getByLabel(`${exampleTelemetry.name} Context Menu`)
.getByRole('menuitem', { name: 'Remove' })
.click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
await page
.getByLabel(`Navigate to ${conditionSet.name} conditionSet Object`, { exact: true })
.click();
await page.getByRole('button', { name: 'Edit Object' }).click();
await page.getByRole('tab', { name: 'Elements' }).click();
expect(
await page
.getByRole('tabpanel', { name: 'Inspector Views' })
.getByRole('listitem', { name: exampleTelemetry.name })
.count()
).toEqual(0);
});
});

View File

@ -22,8 +22,8 @@
import { import {
createDomainObjectWithDefaults, createDomainObjectWithDefaults,
navigateToObjectWithRealTime, setTimeConductorBounds,
setTimeConductorBounds setTimeConductorMode
} from '../../../../appActions.js'; } from '../../../../appActions.js';
import { expect, test } from '../../../../pluginFixtures.js'; import { expect, test } from '../../../../pluginFixtures.js';
@ -39,52 +39,12 @@ test.describe('Telemetry Table', () => {
type: 'Sine Wave Generator', type: 'Sine Wave Generator',
parent: table.uuid parent: table.uuid
}); });
await navigateToObjectWithRealTime(page, table.url); await page.goto(table.url);
await setTimeConductorMode(page, false);
const rows = page.getByLabel('table content').getByLabel('Table Row'); const rows = page.getByLabel('table content').getByLabel('Table Row');
await expect(rows).toHaveCount(50); await expect(rows).toHaveCount(50);
}); });
test('on load, auto scrolls to top for descending, and to bottom for ascending', async ({
page
}) => {
const sineWaveGenerator = await createDomainObjectWithDefaults(page, {
type: 'Sine Wave Generator',
parent: table.uuid
});
// verify in telemetry table object view
await navigateToObjectWithRealTime(page, table.url);
expect(await getScrollPosition(page)).toBe(0);
// verify in telemetry table view
await page.goto(sineWaveGenerator.url);
await page.getByLabel('Open the View Switcher Menu').click();
await page.getByText('Telemetry Table', { exact: true }).click();
expect(await getScrollPosition(page)).toBe(0);
// navigate back to table
await page.goto(table.url);
// go into edit mode
await page.getByLabel('Edit Object').click();
// change sort direction
await page.locator('thead div').filter({ hasText: 'Time' }).click();
// save view
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
// navigate away and back
await page.goto(sineWaveGenerator.url);
await page.goto(table.url);
// verify scroll position
expect(await getScrollPosition(page, false)).toBeLessThan(1);
});
test('unpauses and filters data when paused by button and user changes bounds', async ({ test('unpauses and filters data when paused by button and user changes bounds', async ({
page page
}) => { }) => {
@ -223,42 +183,3 @@ test.describe('Telemetry Table', () => {
await page.click('button[title="Pause"]'); await page.click('button[title="Pause"]');
}); });
}); });
async function getScrollPosition(page, top = true) {
const tableBody = page.locator('.c-table__body-w');
// Wait for the scrollbar to appear
await tableBody.evaluate((node) => {
return new Promise((resolve) => {
function checkScroll() {
if (node.scrollHeight > node.clientHeight) {
resolve();
} else {
setTimeout(checkScroll, 100);
}
}
checkScroll();
});
});
// make sure there are rows
const rows = page.getByLabel('table content').getByLabel('Table Row');
await rows.first().waitFor();
// Using this to allow for rows to come and go, so we can truly test the scroll position
// eslint-disable-next-line playwright/no-wait-for-timeout
await page.waitForTimeout(1000);
const { scrollTop, clientHeight, scrollHeight } = await tableBody.evaluate((node) => ({
scrollTop: node.scrollTop,
clientHeight: node.clientHeight,
scrollHeight: node.scrollHeight
}));
// eslint-disable-next-line playwright/no-conditional-in-test
if (top) {
return scrollTop;
} else {
return Math.abs(scrollHeight - (scrollTop + clientHeight));
}
}

View File

@ -141,7 +141,7 @@ export default class ExampleUserProvider extends EventEmitter {
} }
getPossibleRoles() { getPossibleRoles() {
return this.loginPromise.then(() => this.user.getRoles()); return this.user.getRoles();
} }
getPossibleMissionActions() { getPossibleMissionActions() {

View File

@ -24,11 +24,12 @@ import ExampleUserProvider from './ExampleUserProvider.js';
const AUTO_LOGIN_USER = 'mct-user'; const AUTO_LOGIN_USER = 'mct-user';
const STATUS_ROLES = ['flight', 'driver']; const STATUS_ROLES = ['flight', 'driver'];
export default function ExampleUserPlugin(options = {}) { export default function ExampleUserPlugin(
const { { autoLoginUser, statusRoles } = {
autoLoginUser = AUTO_LOGIN_USER, autoLoginUser: AUTO_LOGIN_USER,
statusRoles = STATUS_ROLES statusRoles: STATUS_ROLES
} = options; }
) {
return function install(openmct) { return function install(openmct) {
const userProvider = new ExampleUserProvider(openmct, { const userProvider = new ExampleUserProvider(openmct, {
statusRoles statusRoles

24
package-lock.json generated
View File

@ -104,7 +104,7 @@
"@axe-core/playwright": "4.8.5", "@axe-core/playwright": "4.8.5",
"@percy/cli": "1.27.4", "@percy/cli": "1.27.4",
"@percy/playwright": "1.0.4", "@percy/playwright": "1.0.4",
"@playwright/test": "1.44.0", "@playwright/test": "1.42.1",
"@types/sinonjs__fake-timers": "8.1.5", "@types/sinonjs__fake-timers": "8.1.5",
"sinon": "17.0.0" "sinon": "17.0.0"
} }
@ -1559,12 +1559,12 @@
} }
}, },
"node_modules/@playwright/test": { "node_modules/@playwright/test": {
"version": "1.44.0", "version": "1.42.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.0.tgz", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.42.1.tgz",
"integrity": "sha512-rNX5lbNidamSUorBhB4XZ9SQTjAqfe5M+p37Z8ic0jPFBMo5iCtQz1kRWkEMg+rYOKSlVycpQmpqjSFq7LXOfg==", "integrity": "sha512-Gq9rmS54mjBL/7/MvBaNOBwbfnh7beHvS6oS4srqXFcQHpQCV1+c8JXWE8VLPyRDhgS3H8x8A7hztqI9VnwrAQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"playwright": "1.44.0" "playwright": "1.42.1"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"
@ -8833,12 +8833,12 @@
} }
}, },
"node_modules/playwright": { "node_modules/playwright": {
"version": "1.44.0", "version": "1.42.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.0.tgz", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.42.1.tgz",
"integrity": "sha512-F9b3GUCLQ3Nffrfb6dunPOkE5Mh68tR7zN32L4jCk4FjQamgesGay7/dAAe1WaMEGV04DkdJfcJzjoCKygUaRQ==", "integrity": "sha512-PgwB03s2DZBcNRoW+1w9E+VkLBxweib6KTXM0M3tkiT4jVxKSi6PmVJ591J+0u10LUrgxB7dLRbiJqO5s2QPMg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"playwright-core": "1.44.0" "playwright-core": "1.42.1"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"
@ -8851,9 +8851,9 @@
} }
}, },
"node_modules/playwright-core": { "node_modules/playwright-core": {
"version": "1.44.0", "version": "1.42.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.0.tgz", "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.42.1.tgz",
"integrity": "sha512-ZTbkNpFfYcGWohvTTl+xewITm7EOuqIqex0c7dNZ+aXsbrLj0qI8XlGKfPpipjm0Wny/4Lt4CJsWJk1stVS5qQ==", "integrity": "sha512-mxz6zclokgrke9p1vtdy/COWBH+eOZgYUVVU34C73M+4j4HLlQJHtfcqiqqxpP0o8HhMkflvfbquLX5dg6wlfA==",
"dev": true, "dev": true,
"bin": { "bin": {
"playwright-core": "cli.js" "playwright-core": "cli.js"

View File

@ -46,6 +46,14 @@ export default class ConditionManager extends EventEmitter {
applied: false applied: false
}; };
this.initialize(); this.initialize();
this.stopObservingForChanges = this.openmct.objects.observe(
this.conditionSetDomainObject,
'*',
(newDomainObject) => {
this.conditionSetDomainObject = newDomainObject;
}
);
} }
async requestLatestValue(endpoint) { async requestLatestValue(endpoint) {
@ -510,6 +518,10 @@ export default class ConditionManager extends EventEmitter {
Object.values(this.subscriptions).forEach((unsubscribe) => unsubscribe()); Object.values(this.subscriptions).forEach((unsubscribe) => unsubscribe());
delete this.subscriptions; delete this.subscriptions;
if (this.stopObservingForChanges) {
this.stopObservingForChanges();
}
this.conditions.forEach((condition) => { this.conditions.forEach((condition) => {
condition.destroy(); condition.destroy();
}); });

View File

@ -21,11 +21,7 @@
--> -->
<template> <template>
<section <section id="conditionCollection" :class="{ 'is-expanded': expanded }">
id="conditionCollection"
:class="{ 'is-expanded': expanded }"
aria-label="Condition Set Condition Collection"
>
<div class="c-cs__header c-section__header"> <div class="c-cs__header c-section__header">
<span <span
class="c-disclosure-triangle c-tree__item__view-control is-enabled" class="c-disclosure-triangle c-tree__item__view-control is-enabled"

View File

@ -24,7 +24,6 @@
<div <div
class="c-condition-h" class="c-condition-h"
:class="{ 'is-drag-target': draggingOver }" :class="{ 'is-drag-target': draggingOver }"
aria-label="Condition Set Condition"
@dragover.prevent @dragover.prevent
@drop.prevent="dropCondition($event, conditionIndex)" @drop.prevent="dropCondition($event, conditionIndex)"
@dragenter="dragEnter($event, conditionIndex)" @dragenter="dragEnter($event, conditionIndex)"
@ -84,7 +83,6 @@
<input <input
v-model="condition.configuration.name" v-model="condition.configuration.name"
class="t-condition-input__name" class="t-condition-input__name"
aria-label="Condition Name Input"
type="text" type="text"
@change="persist" @change="persist"
/> />
@ -93,11 +91,7 @@
<span class="c-cdef__label">Output</span> <span class="c-cdef__label">Output</span>
<span class="c-cdef__controls"> <span class="c-cdef__controls">
<span class="c-cdef__control"> <span class="c-cdef__control">
<select <select v-model="selectedOutputSelection" @change="setOutputValue">
v-model="selectedOutputSelection"
aria-label="Condition Output Type"
@change="setOutputValue"
>
<option v-for="option in outputOptions" :key="option" :value="option"> <option v-for="option in outputOptions" :key="option" :value="option">
{{ initCap(option) }} {{ initCap(option) }}
</option> </option>
@ -107,7 +101,6 @@
<input <input
v-if="selectedOutputSelection === outputOptions[2]" v-if="selectedOutputSelection === outputOptions[2]"
v-model="condition.configuration.output" v-model="condition.configuration.output"
aria-label="Condition Output String"
class="t-condition-name-input" class="t-condition-name-input"
type="text" type="text"
@change="persist" @change="persist"

View File

@ -21,7 +21,7 @@
--> -->
<template> <template>
<div class="c-cs" :class="{ 'is-stale': isStale }" aria-label="Condition Set"> <div class="c-cs" :class="{ 'is-stale': isStale }">
<section class="c-cs__current-output c-section"> <section class="c-cs__current-output c-section">
<div class="c-cs__content c-cs__current-output-value"> <div class="c-cs__content c-cs__current-output-value">
<span class="c-cs__current-output-value__label">Current Output</span> <span class="c-cs__current-output-value__label">Current Output</span>

View File

@ -21,12 +21,7 @@
--> -->
<template> <template>
<section <section v-show="isEditing" id="test-data" :class="{ 'is-expanded': expanded }">
v-show="isEditing"
id="test-data"
:class="{ 'is-expanded': expanded }"
aria-label="Condition Set Test Data"
>
<div class="c-cs__header c-section__header"> <div class="c-cs__header c-section__header">
<span <span
class="c-disclosure-triangle c-tree__item__view-control is-enabled" class="c-disclosure-triangle c-tree__item__view-control is-enabled"

View File

@ -561,9 +561,6 @@ export default {
this.table.initialize(); this.table.initialize();
this.rescaleToContainer(); this.rescaleToContainer();
// Scroll to the top of the table after loading
this.addToAfterLoadActions(this.scroll);
}, },
beforeUnmount() { beforeUnmount() {
this.table.off('object-added', this.addObject); this.table.off('object-added', this.addObject);