[e2e] Update remaining tests and add missing comparison coverage (#7363)

This commit is contained in:
John Hill 2024-01-11 14:47:00 -08:00 committed by GitHub
parent 6fd7b6f7a3
commit 6ce340cebd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
88 changed files with 561 additions and 382 deletions

View File

@ -43,7 +43,6 @@
"sharded", "sharded",
"perfromance", "perfromance",
"MMOC", "MMOC",
"deploysentinel",
"codegen", "codegen",
"Unfortuantely", "Unfortuantely",
"viewports", "viewports",

View File

@ -47,9 +47,8 @@ jobs:
bash src/plugins/persistence/couch/setup-couchdb.sh bash src/plugins/persistence/couch/setup-couchdb.sh
bash src/plugins/persistence/couch/replace-localstorage-with-couchdb-indexhtml.sh bash src/plugins/persistence/couch/replace-localstorage-with-couchdb-indexhtml.sh
- name: Run CouchDB Tests and publish to deploysentinel - name: Run CouchDB Tests
env: env:
DEPLOYSENTINEL_API_KEY: ${{ secrets.DEPLOYSENTINEL_API_KEY }}
COMMIT_INFO_SHA: ${{github.event.pull_request.head.sha }} COMMIT_INFO_SHA: ${{github.event.pull_request.head.sha }}
run: npm run test:e2e:couchdb run: npm run test:e2e:couchdb

3
.gitignore vendored
View File

@ -15,6 +15,9 @@
*.idea *.idea
*.iml *.iml
# VSCode
.vscode/settings.json
# Build output # Build output
target target
dist dist

View File

@ -85,9 +85,8 @@ There are a few reasons that your GitHub PR could be failing beyond simple faile
* Not all required checks are run per commit. You may need to manually trigger addition GitHub checks with a `pr:<label>` label added to your PR. * Not all required checks are run per commit. You may need to manually trigger addition GitHub checks with a `pr:<label>` label added to your PR.
### Flaky tests ### Flaky tests
There are two ways to know if a test on your branch is historically flaky:
1. `deploysentinel`'s PR comment bot to give an accurate and historical view of e2e flakiness. Check your PR for a view of the test failures and flakes (with link to the failing test). Note: only a 7 day window of flake is available. (CircleCI's test insights feature)[https://circleci.com/blog/introducing-test-insights-with-flaky-test-detection/] collects historical data about the individual test results for both unit and e2e tests. Note: only a 14 day window of flake is available.
2. (CircleCI's test insights feature)[https://circleci.com/blog/introducing-test-insights-with-flaky-test-detection/] collects historical data about the individual test results for both unit and e2e tests. Note: only a 14 day window of flake is available.
### Local=Pass and CI=Fail ### Local=Pass and CI=Fail
Although rare, it is possible that your test can pass locally but fail in CI. Although rare, it is possible that your test can pass locally but fail in CI.

View File

@ -75,8 +75,7 @@ const config = {
outputFolder: '../html-test-results' //Must be in different location due to https://github.com/microsoft/playwright/issues/12840 outputFolder: '../html-test-results' //Must be in different location due to https://github.com/microsoft/playwright/issues/12840
} }
], ],
['junit', { outputFile: '../test-results/results.xml' }], ['junit', { outputFile: '../test-results/results.xml' }]
['@deploysentinel/playwright']
] ]
}; };

View File

@ -1,14 +1,9 @@
// playwright.config.js // playwright.config.js
// @ts-check // @ts-check
// eslint-disable-next-line no-unused-vars
import { devices } from '@playwright/test';
const MAX_FAILURES = 5;
const NUM_WORKERS = 2;
/** @type {import('@playwright/test').PlaywrightTestConfig} */ /** @type {import('@playwright/test').PlaywrightTestConfig} */
const config = { const config = {
retries: 0, //Retries 2 times for a total of 3 runs. When running sharded and with max-failures=5, this should ensure that flake is managed without failing the full suite retries: 0, //Retries are not needed with watch mode
testDir: 'tests', testDir: 'tests',
timeout: 60 * 1000, timeout: 60 * 1000,
webServer: { webServer: {
@ -17,8 +12,7 @@ const config = {
timeout: 200 * 1000, timeout: 200 * 1000,
reuseExistingServer: true //This was originally disabled to prevent differences in local debugging vs. CI. However, it significantly speeds up local debugging. reuseExistingServer: true //This was originally disabled to prevent differences in local debugging vs. CI. However, it significantly speeds up local debugging.
}, },
maxFailures: MAX_FAILURES, //Limits failures to 5 to reduce CI Waste workers: '75%', //Limit to 75% of the CPU to support running with dev server
workers: NUM_WORKERS, //Limit to 2 for CircleCI Agent
use: { use: {
baseURL: 'http://localhost:8080/', baseURL: 'http://localhost:8080/',
headless: true, headless: true,
@ -45,8 +39,7 @@ const config = {
outputFolder: '../html-test-results' //Must be in different location due to https://github.com/microsoft/playwright/issues/12840 outputFolder: '../html-test-results' //Must be in different location due to https://github.com/microsoft/playwright/issues/12840
} }
], ],
['junit', { outputFile: '../test-results/results.xml' }], ['junit', { outputFile: '../test-results/results.xml' }]
['@deploysentinel/playwright']
] ]
}; };

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -6,11 +6,11 @@
"localStorage": [ "localStorage": [
{ {
"name": "mct", "name": "mct",
"value": "{\"mine\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"20e7d5fe-9cf8-4099-8957-9453a8954c67\",\"namespace\":\"\"},{\"key\":\"2db521a9-996d-4d04-a171-93f4c5c220af\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"modified\":1732413602540,\"created\":1732413600760,\"persisted\":1732413602540},\"20e7d5fe-9cf8-4099-8957-9453a8954c67\":{\"identifier\":{\"key\":\"20e7d5fe-9cf8-4099-8957-9453a8954c67\",\"namespace\":\"\"},\"name\":\"Overlay Plot with Telemetry Object\",\"type\":\"telemetry.plot.overlay\",\"composition\":[{\"key\":\"2db521a9-996d-4d04-a171-93f4c5c220af\",\"namespace\":\"\"}],\"configuration\":{\"series\":[]},\"notes\":\"framework/generateLocalStorageData.e2e.spec.js\\nGenerate Visual Test Data @localStorage @generatedata\\nGenerate Overlay Plot with Telemetry Object\\nchrome\",\"modified\":1732413603960,\"location\":\"mine\",\"created\":1732413601820,\"persisted\":1732413603960},\"2db521a9-996d-4d04-a171-93f4c5c220af\":{\"name\":\"VIPER Rover Heading\",\"type\":\"generator\",\"identifier\":{\"key\":\"2db521a9-996d-4d04-a171-93f4c5c220af\",\"namespace\":\"\"},\"telemetry\":{\"period\":10,\"amplitude\":1,\"offset\":0,\"dataRateInHz\":1,\"phase\":0,\"randomness\":0,\"loadDelay\":0,\"infinityValues\":false,\"staleness\":false},\"modified\":1732413602540,\"location\":\"mine\",\"created\":1732413602540,\"persisted\":1732413602540}}" "value": "{\"mine\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"e78ca721-fb5e-409b-bf6d-597c87cb716f\",\"namespace\":\"\"},{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"modified\":1732413602460,\"created\":1732413600960,\"persisted\":1732413602460},\"e78ca721-fb5e-409b-bf6d-597c87cb716f\":{\"identifier\":{\"key\":\"e78ca721-fb5e-409b-bf6d-597c87cb716f\",\"namespace\":\"\"},\"name\":\"Overlay Plot with Telemetry Object\",\"type\":\"telemetry.plot.overlay\",\"composition\":[{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"}],\"configuration\":{\"series\":[{\"identifier\":{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"}}]},\"notes\":\"framework/generateLocalStorageData.e2e.spec.js\\nGenerate Visual Test Data @localStorage @generatedata\\nGenerate Overlay Plot with Telemetry Object\\nchrome\",\"modified\":1732413603880,\"location\":\"mine\",\"created\":1732413601740,\"persisted\":1732413603880},\"c6100044-56be-44b3-acca-6b9fddfb3849\":{\"name\":\"VIPER Rover Heading\",\"type\":\"generator\",\"identifier\":{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"},\"telemetry\":{\"period\":10,\"amplitude\":1,\"offset\":0,\"dataRateInHz\":1,\"phase\":0,\"randomness\":0,\"loadDelay\":0,\"infinityValues\":false,\"exceedFloat32\":false,\"staleness\":false},\"modified\":1732413602460,\"location\":\"mine\",\"created\":1732413602460,\"persisted\":1732413602460}}"
}, },
{ {
"name": "mct-recent-objects", "name": "mct-recent-objects",
"value": "[{\"objectPath\":[{\"identifier\":{\"key\":\"2db521a9-996d-4d04-a171-93f4c5c220af\",\"namespace\":\"\"},\"name\":\"VIPER Rover Heading\",\"type\":\"generator\",\"telemetry\":{\"period\":10,\"amplitude\":1,\"offset\":0,\"dataRateInHz\":1,\"phase\":0,\"randomness\":0,\"loadDelay\":0,\"infinityValues\":false,\"staleness\":false},\"modified\":1732413602540,\"location\":\"mine\",\"created\":1732413602540,\"persisted\":1732413602540},{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"20e7d5fe-9cf8-4099-8957-9453a8954c67\",\"namespace\":\"\"},{\"key\":\"2db521a9-996d-4d04-a171-93f4c5c220af\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"modified\":1732413602540,\"created\":1732413600760,\"persisted\":1732413602540},{\"identifier\":{\"key\":\"ROOT\",\"namespace\":\"\"},\"name\":\"Open MCT\",\"type\":\"root\",\"composition\":[{\"key\":\"mine\",\"namespace\":\"\"}]}],\"navigationPath\":\"/browse/mine/2db521a9-996d-4d04-a171-93f4c5c220af\",\"domainObject\":{\"identifier\":{\"key\":\"2db521a9-996d-4d04-a171-93f4c5c220af\",\"namespace\":\"\"},\"name\":\"VIPER Rover Heading\",\"type\":\"generator\",\"telemetry\":{\"period\":10,\"amplitude\":1,\"offset\":0,\"dataRateInHz\":1,\"phase\":0,\"randomness\":0,\"loadDelay\":0,\"infinityValues\":false,\"staleness\":false},\"modified\":1732413602540,\"location\":\"mine\",\"created\":1732413602540,\"persisted\":1732413602540}},{\"objectPath\":[{\"identifier\":{\"key\":\"20e7d5fe-9cf8-4099-8957-9453a8954c67\",\"namespace\":\"\"},\"name\":\"Overlay Plot with Telemetry Object\",\"type\":\"telemetry.plot.overlay\",\"composition\":[{\"key\":\"2db521a9-996d-4d04-a171-93f4c5c220af\",\"namespace\":\"\"}],\"configuration\":{\"series\":[{\"identifier\":{\"key\":\"2db521a9-996d-4d04-a171-93f4c5c220af\",\"namespace\":\"\"}}]},\"notes\":\"framework/generateLocalStorageData.e2e.spec.js\\nGenerate Visual Test Data @localStorage @generatedata\\nGenerate Overlay Plot with Telemetry Object\\nchrome\",\"modified\":1732413603960,\"location\":\"mine\",\"created\":1732413601820,\"persisted\":1732413603960},{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"20e7d5fe-9cf8-4099-8957-9453a8954c67\",\"namespace\":\"\"},{\"key\":\"2db521a9-996d-4d04-a171-93f4c5c220af\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"modified\":1732413602540,\"created\":1732413600760,\"persisted\":1732413602540},{\"identifier\":{\"key\":\"ROOT\",\"namespace\":\"\"},\"name\":\"Open MCT\",\"type\":\"root\",\"composition\":[{\"key\":\"mine\",\"namespace\":\"\"}]}],\"navigationPath\":\"/browse/mine/20e7d5fe-9cf8-4099-8957-9453a8954c67\",\"domainObject\":{\"identifier\":{\"key\":\"20e7d5fe-9cf8-4099-8957-9453a8954c67\",\"namespace\":\"\"},\"name\":\"Overlay Plot with Telemetry Object\",\"type\":\"telemetry.plot.overlay\",\"composition\":[{\"key\":\"2db521a9-996d-4d04-a171-93f4c5c220af\",\"namespace\":\"\"}],\"configuration\":{\"series\":[{\"identifier\":{\"key\":\"2db521a9-996d-4d04-a171-93f4c5c220af\",\"namespace\":\"\"}}]},\"notes\":\"framework/generateLocalStorageData.e2e.spec.js\\nGenerate Visual Test Data @localStorage @generatedata\\nGenerate Overlay Plot with Telemetry Object\\nchrome\",\"modified\":1732413603960,\"location\":\"mine\",\"created\":1732413601820,\"persisted\":1732413603960}},{\"objectPath\":[{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"20e7d5fe-9cf8-4099-8957-9453a8954c67\",\"namespace\":\"\"},{\"key\":\"2db521a9-996d-4d04-a171-93f4c5c220af\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"modified\":1732413602540,\"created\":1732413600760,\"persisted\":1732413602540},{\"identifier\":{\"key\":\"ROOT\",\"namespace\":\"\"},\"name\":\"Open MCT\",\"type\":\"root\",\"composition\":[{\"key\":\"mine\",\"namespace\":\"\"}]}],\"navigationPath\":\"/browse/mine\",\"domainObject\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"20e7d5fe-9cf8-4099-8957-9453a8954c67\",\"namespace\":\"\"},{\"key\":\"2db521a9-996d-4d04-a171-93f4c5c220af\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"modified\":1732413602540,\"created\":1732413600760,\"persisted\":1732413602540}}]" "value": "[{\"objectPath\":[{\"identifier\":{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"},\"name\":\"VIPER Rover Heading\",\"type\":\"generator\",\"telemetry\":{\"period\":10,\"amplitude\":1,\"offset\":0,\"dataRateInHz\":1,\"phase\":0,\"randomness\":0,\"loadDelay\":0,\"infinityValues\":false,\"exceedFloat32\":false,\"staleness\":false},\"modified\":1732413602460,\"location\":\"mine\",\"created\":1732413602460,\"persisted\":1732413602460},{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"e78ca721-fb5e-409b-bf6d-597c87cb716f\",\"namespace\":\"\"},{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"modified\":1732413602460,\"created\":1732413600960,\"persisted\":1732413602460},{\"identifier\":{\"key\":\"ROOT\",\"namespace\":\"\"},\"name\":\"Open MCT\",\"type\":\"root\",\"composition\":[{\"key\":\"mine\",\"namespace\":\"\"}]}],\"navigationPath\":\"/browse/mine/c6100044-56be-44b3-acca-6b9fddfb3849\",\"domainObject\":{\"identifier\":{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"},\"name\":\"VIPER Rover Heading\",\"type\":\"generator\",\"telemetry\":{\"period\":10,\"amplitude\":1,\"offset\":0,\"dataRateInHz\":1,\"phase\":0,\"randomness\":0,\"loadDelay\":0,\"infinityValues\":false,\"exceedFloat32\":false,\"staleness\":false},\"modified\":1732413602460,\"location\":\"mine\",\"created\":1732413602460,\"persisted\":1732413602460}},{\"objectPath\":[{\"identifier\":{\"key\":\"e78ca721-fb5e-409b-bf6d-597c87cb716f\",\"namespace\":\"\"},\"name\":\"Overlay Plot with Telemetry Object\",\"type\":\"telemetry.plot.overlay\",\"composition\":[{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"}],\"configuration\":{\"series\":[{\"identifier\":{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"}}]},\"notes\":\"framework/generateLocalStorageData.e2e.spec.js\\nGenerate Visual Test Data @localStorage @generatedata\\nGenerate Overlay Plot with Telemetry Object\\nchrome\",\"modified\":1732413603880,\"location\":\"mine\",\"created\":1732413601740,\"persisted\":1732413603880},{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"e78ca721-fb5e-409b-bf6d-597c87cb716f\",\"namespace\":\"\"},{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"modified\":1732413602460,\"created\":1732413600960,\"persisted\":1732413602460},{\"identifier\":{\"key\":\"ROOT\",\"namespace\":\"\"},\"name\":\"Open MCT\",\"type\":\"root\",\"composition\":[{\"key\":\"mine\",\"namespace\":\"\"}]}],\"navigationPath\":\"/browse/mine/e78ca721-fb5e-409b-bf6d-597c87cb716f\",\"domainObject\":{\"identifier\":{\"key\":\"e78ca721-fb5e-409b-bf6d-597c87cb716f\",\"namespace\":\"\"},\"name\":\"Overlay Plot with Telemetry Object\",\"type\":\"telemetry.plot.overlay\",\"composition\":[{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"}],\"configuration\":{\"series\":[{\"identifier\":{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"}}]},\"notes\":\"framework/generateLocalStorageData.e2e.spec.js\\nGenerate Visual Test Data @localStorage @generatedata\\nGenerate Overlay Plot with Telemetry Object\\nchrome\",\"modified\":1732413603880,\"location\":\"mine\",\"created\":1732413601740,\"persisted\":1732413603880}},{\"objectPath\":[{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"e78ca721-fb5e-409b-bf6d-597c87cb716f\",\"namespace\":\"\"},{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"modified\":1732413602460,\"created\":1732413600960,\"persisted\":1732413602460},{\"identifier\":{\"key\":\"ROOT\",\"namespace\":\"\"},\"name\":\"Open MCT\",\"type\":\"root\",\"composition\":[{\"key\":\"mine\",\"namespace\":\"\"}]}],\"navigationPath\":\"/browse/mine\",\"domainObject\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"e78ca721-fb5e-409b-bf6d-597c87cb716f\",\"namespace\":\"\"},{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"modified\":1732413602460,\"created\":1732413600960,\"persisted\":1732413602460}}]"
}, },
{ {
"name": "mct-tree-expanded", "name": "mct-tree-expanded",

View File

@ -6,7 +6,7 @@
"localStorage": [ "localStorage": [
{ {
"name": "mct", "name": "mct",
"value": "{\"mine\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"98161570-a735-4a50-9c75-11b346ad3789\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"modified\":1732413601340,\"created\":1732413600580,\"persisted\":1732413601340},\"98161570-a735-4a50-9c75-11b346ad3789\":{\"identifier\":{\"key\":\"98161570-a735-4a50-9c75-11b346ad3789\",\"namespace\":\"\"},\"name\":\"Overlay Plot with 5s Delay\",\"type\":\"telemetry.plot.overlay\",\"composition\":[{\"key\":\"477e60bb-4cba-4603-b4c9-2281ccf7e054\",\"namespace\":\"\"}],\"configuration\":{\"series\":[{\"identifier\":{\"key\":\"477e60bb-4cba-4603-b4c9-2281ccf7e054\",\"namespace\":\"\"}}]},\"notes\":\"framework/generateLocalStorageData.e2e.spec.js\\nGenerate Visual Test Data @localStorage @generatedata\\nGenerate Overlay Plot with 5s Delay\\nchrome\",\"modified\":1732413602660,\"location\":\"mine\",\"created\":1732413601340,\"persisted\":1732413602660},\"477e60bb-4cba-4603-b4c9-2281ccf7e054\":{\"identifier\":{\"key\":\"477e60bb-4cba-4603-b4c9-2281ccf7e054\",\"namespace\":\"\"},\"name\":\"VIPER Rover Heading\",\"type\":\"generator\",\"telemetry\":{\"period\":10,\"amplitude\":1,\"offset\":0,\"dataRateInHz\":1,\"phase\":0,\"randomness\":0,\"loadDelay\":5000,\"infinityValues\":false,\"staleness\":false},\"modified\":1732413602520,\"location\":\"98161570-a735-4a50-9c75-11b346ad3789\",\"created\":1732413602040,\"persisted\":1732413602520}}" "value": "{\"mine\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"67ca2e0a-b37e-4eda-86a4-ccdbb228bbc0\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"modified\":1732413601720,\"created\":1732413600920,\"persisted\":1732413601720},\"67ca2e0a-b37e-4eda-86a4-ccdbb228bbc0\":{\"identifier\":{\"key\":\"67ca2e0a-b37e-4eda-86a4-ccdbb228bbc0\",\"namespace\":\"\"},\"name\":\"Overlay Plot with 5s Delay\",\"type\":\"telemetry.plot.overlay\",\"composition\":[{\"key\":\"8f524b49-ad06-47f9-98e0-087b31a2f3e0\",\"namespace\":\"\"}],\"configuration\":{\"series\":[{\"identifier\":{\"key\":\"8f524b49-ad06-47f9-98e0-087b31a2f3e0\",\"namespace\":\"\"}}]},\"notes\":\"framework/generateLocalStorageData.e2e.spec.js\\nGenerate Visual Test Data @localStorage @generatedata\\nGenerate Overlay Plot with 5s Delay\\nchrome\",\"modified\":1732413603020,\"location\":\"mine\",\"created\":1732413601720,\"persisted\":1732413603020},\"8f524b49-ad06-47f9-98e0-087b31a2f3e0\":{\"identifier\":{\"key\":\"8f524b49-ad06-47f9-98e0-087b31a2f3e0\",\"namespace\":\"\"},\"name\":\"VIPER Rover Heading\",\"type\":\"generator\",\"telemetry\":{\"period\":10,\"amplitude\":1,\"offset\":0,\"dataRateInHz\":1,\"phase\":0,\"randomness\":0,\"loadDelay\":5000,\"infinityValues\":false,\"exceedFloat32\":false,\"staleness\":false},\"modified\":1732413602920,\"location\":\"67ca2e0a-b37e-4eda-86a4-ccdbb228bbc0\",\"created\":1732413602420,\"persisted\":1732413602920}}"
}, },
{ {
"name": "mct-tree-expanded", "name": "mct-tree-expanded",

File diff suppressed because one or more lines are too long

View File

@ -155,7 +155,7 @@ test.describe('AppActions', () => {
await page.goto('./#/browse/mine'); await page.goto('./#/browse/mine');
//Click the Create button //Click the Create button
await page.click('button:has-text("Create")'); await page.getByRole('button', { name: 'Create' }).click();
// Click the object specified by 'type' // Click the object specified by 'type'
await page.click(`li[role='menuitem']:text("Clock")`); await page.click(`li[role='menuitem']:text("Clock")`);

View File

@ -53,29 +53,28 @@ test.describe('Generate Visual Test Data @localStorage @generatedata', () => {
}); });
test('Generate display layout with 2 child display layouts', async ({ page, context }) => { test('Generate display layout with 2 child display layouts', async ({ page, context }) => {
// Create Display Layout
const parent = await createDomainObjectWithDefaults(page, { const parent = await createDomainObjectWithDefaults(page, {
type: 'Display Layout', type: 'Display Layout',
name: 'Parent Display Layout' name: 'Parent Display Layout'
}); });
const child1 = await createDomainObjectWithDefaults(page, { await createDomainObjectWithDefaults(page, {
type: 'Display Layout', type: 'Display Layout',
name: 'Child Layout 1', name: 'Child Layout 1',
parent: parent.uuid parent: parent.uuid
}); });
const child2 = await createDomainObjectWithDefaults(page, { await createDomainObjectWithDefaults(page, {
type: 'Display Layout', type: 'Display Layout',
name: 'Child Layout 2', name: 'Child Layout 2',
parent: parent.uuid parent: parent.uuid
}); });
await page.goto(parent.url); await page.goto(parent.url, { waitUntil: 'domcontentloaded' });
await page.getByLabel('Edit').click(); await page.getByLabel('Edit Object').click();
await page.getByLabel(`${child2.name} Layout Grid`).hover(); await page.getByLabel('Child Layout 2 Layout', { exact: true }).hover();
await page.getByLabel('Move Sub-object Frame').nth(1).click(); await page.getByLabel('Move Sub-object Frame').nth(1).click();
await page.getByLabel('X:').fill('30'); await page.getByLabel('X:').fill('30');
await page.getByLabel(`${child1.name} Layout Grid`).hover(); await page.getByLabel('Child Layout 1 Layout', { exact: true }).hover();
await page.getByLabel('Move Sub-object Frame').first().click(); await page.getByLabel('Move Sub-object Frame').first().click();
await page.getByLabel('Y:').fill('30'); await page.getByLabel('Y:').fill('30');
@ -107,7 +106,7 @@ test.describe('Generate Visual Test Data @localStorage @generatedata', () => {
parent: parent.uuid parent: parent.uuid
}); });
await page.goto(parent.url); await page.goto(parent.url, { waitUntil: 'domcontentloaded' });
//Save localStorage for future test execution //Save localStorage for future test execution
await context.storageState({ await context.storageState({
@ -134,7 +133,7 @@ test.describe('Generate Visual Test Data @localStorage @generatedata', () => {
await page.locator('button[title="More actions"]').click(); await page.locator('button[title="More actions"]').click();
// Select 'Create Link' from dropdown // Select 'Create Link' from dropdown
await page.getByRole('menuitem', { name: 'Create Link' }).click(); await page.getByRole('menuitem', { name: 'Create Link' }).click();
// Search and Select for overlay Plot within Create Modal // Search and Select for overlay Plot within Create Modal
await page.getByRole('dialog').getByRole('searchbox', { name: 'Search Input' }).click(); await page.getByRole('dialog').getByRole('searchbox', { name: 'Search Input' }).click();
@ -206,8 +205,8 @@ test.describe('Generate Visual Test Data @localStorage @generatedata', () => {
const swgWith5sDelay = await createExampleTelemetryObject(page, overlayPlot.uuid); const swgWith5sDelay = await createExampleTelemetryObject(page, overlayPlot.uuid);
await page.goto(swgWith5sDelay.url); await page.goto(swgWith5sDelay.url);
await page.getByTitle('More actions').click(); await page.getByLabel('More actions').click();
await page.getByRole('menuitem', { name: ' Edit Properties...' }).click(); await page.getByLabel('Edit Properties...').click();
//Edit Example Telemetry Object to include 5s loading Delay //Edit Example Telemetry Object to include 5s loading Delay
await page.locator('[aria-label="Loading Delay \\(ms\\)"]').fill('5000'); await page.locator('[aria-label="Loading Delay \\(ms\\)"]').fill('5000');
@ -226,7 +225,7 @@ test.describe('Generate Visual Test Data @localStorage @generatedata', () => {
// Clear Recently Viewed // Clear Recently Viewed
await page.getByRole('button', { name: 'Clear Recently Viewed' }).click(); await page.getByRole('button', { name: 'Clear Recently Viewed' }).click();
await page.getByRole('button', { name: 'OK' }).click(); await page.getByRole('button', { name: 'OK', exact: true }).click();
//Save localStorage for future test execution //Save localStorage for future test execution
await context.storageState({ await context.storageState({
path: fileURLToPath( path: fileURLToPath(

View File

@ -27,39 +27,33 @@ This test suite is dedicated to tests which verify branding related components.
import { expect, test } from '../../baseFixtures.js'; import { expect, test } from '../../baseFixtures.js';
test.describe('Branding tests', () => { test.describe('Branding tests', () => {
test('About Modal launches with basic branding properties', async ({ page }) => { test.beforeEach(async ({ page }) => {
// Go to baseURL
await page.goto('./', { waitUntil: 'domcontentloaded' }); await page.goto('./', { waitUntil: 'domcontentloaded' });
});
// Click About button test('About Modal launches with basic branding properties', async ({ page }) => {
await page.click('.l-shell__app-logo'); await page.getByLabel('About Modal').click();
// Verify that the NASA Logo Appears // Verify that the NASA Logo Appears
await expect(page.locator('.c-about__image')).toBeVisible(); await expect(page.getByAltText('Open MCT Splash Logo')).toBeVisible();
// Modify the Build information in 'about' Modal // Modify the Build information in 'about' Modal
const versionInformationLocator = page.locator('ul.t-info.l-info.s-info').first(); await expect.soft(page.getByLabel('Version Number')).toContainText(/Version: \d/);
await expect(versionInformationLocator).toBeEnabled();
await expect.soft(versionInformationLocator).toContainText(/Version: \d/);
await expect await expect
.soft(versionInformationLocator) .soft(page.getByLabel('Build Date'))
.toContainText(/Build Date: ((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun))/); .toContainText(/Build Date: ((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun))/);
await expect.soft(versionInformationLocator).toContainText(/Revision: \b[0-9a-f]{5,40}\b/); await expect.soft(page.getByLabel('Revision')).toContainText(/Revision: \b[0-9a-f]{5,40}\b/);
await expect.soft(versionInformationLocator).toContainText(/Branch: ./); await expect.soft(page.getByLabel('Branch')).toContainText(/Branch: ./);
}); });
test('Verify Links in About Modal @2p', async ({ page }) => { test('Verify Links in About Modal @2p', async ({ page }) => {
// Go to baseURL
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Click About button // Click About button
await page.click('.l-shell__app-logo'); await page.getByLabel('About Modal').click();
// Verify that clicking on the third party licenses information opens up another tab on licenses url // Verify that clicking on the third party licenses information opens up another tab on licenses url
const [page2] = await Promise.all([ const [page2] = await Promise.all([
page.waitForEvent('popup'), page.waitForEvent('popup'),
page.locator('text=click here for third party licensing information').click() page.getByText('click here for third party licensing information').click()
]); ]);
await page2.waitForLoadState('networkidle'); //Avoids timing issues with juggler/firefox await page2.waitForLoadState('domcontentloaded'); //Avoids timing issues with juggler/firefox
expect(page2.waitForURL('**/licenses**')).toBeTruthy(); expect(page2.waitForURL('**/licenses**')).toBeTruthy();
}); });
}); });

View File

@ -46,11 +46,16 @@ test.describe('Clear Data Action', () => {
await expect(await page.locator('.c-thumb__image').count()).toBeGreaterThan(0); await expect(await page.locator('.c-thumb__image').count()).toBeGreaterThan(0);
// Click the "Clear Data" menu action // Click the "Clear Data" menu action
await page.getByTitle('More actions').click(); await page.getByTitle('More actions').click();
const clearDataMenuItem = page.getByRole('menuitem', { await expect(
name: 'Clear Data' page.getByRole('menuitem', {
}); name: 'Clear Data for Object'
await expect(clearDataMenuItem).toBeEnabled(); })
await clearDataMenuItem.click(); ).toBeEnabled();
await page
.getByRole('menuitem', {
name: 'Clear Data for Object'
})
.click();
// Verify that the background image is no longer visible // Verify that the background image is no longer visible
await expect(page.locator(backgroundImageSelector)).toBeHidden(); await expect(page.locator(backgroundImageSelector)).toBeHidden();

View File

@ -38,7 +38,7 @@ test.describe('Sine Wave Generator', () => {
await page.goto('./', { waitUntil: 'domcontentloaded' }); await page.goto('./', { waitUntil: 'domcontentloaded' });
//Click the Create button //Click the Create button
await page.click('button:has-text("Create")'); await page.getByRole('button', { name: 'Create' }).click();
// Click Sine Wave Generator // Click Sine Wave Generator
await page.click('text=Sine Wave Generator'); await page.click('text=Sine Wave Generator');

View File

@ -41,8 +41,8 @@ test.describe('Form Validation Behavior', () => {
//Go to baseURL //Go to baseURL
await page.goto('./', { waitUntil: 'domcontentloaded' }); await page.goto('./', { waitUntil: 'domcontentloaded' });
await page.click('button:has-text("Create")'); await page.getByRole('button', { name: 'Create' }).click();
await page.getByRole('menuitem', { name: 'Folder' }).click(); await page.getByRole('menuitem', { name: 'Folder' }).click();
// Fill in empty string into title and trigger validation with 'Tab' // Fill in empty string into title and trigger validation with 'Tab'
await page.click('text=Properties Title Notes >> input[type="text"]'); await page.click('text=Properties Title Notes >> input[type="text"]');
@ -80,7 +80,7 @@ test.describe('Form File Input Behavior', () => {
test('Can select a JSON file type', async ({ page }) => { test('Can select a JSON file type', async ({ page }) => {
await page.goto('./', { waitUntil: 'domcontentloaded' }); await page.goto('./', { waitUntil: 'domcontentloaded' });
await page.getByRole('button', { name: 'Create' }).click(); await page.getByRole('button', { name: 'Create' }).click();
await page.getByRole('menuitem', { name: 'JSON File Input Object' }).click(); await page.getByRole('menuitem', { name: 'JSON File Input Object' }).click();
await page.setInputFiles('#fileElem', jsonFilePath); await page.setInputFiles('#fileElem', jsonFilePath);
@ -94,7 +94,7 @@ test.describe('Form File Input Behavior', () => {
test('Can select an image file type', async ({ page }) => { test('Can select an image file type', async ({ page }) => {
await page.goto('./', { waitUntil: 'domcontentloaded' }); await page.goto('./', { waitUntil: 'domcontentloaded' });
await page.getByRole('button', { name: 'Create' }).click(); await page.getByRole('button', { name: 'Create' }).click();
await page.getByRole('menuitem', { name: 'Image File Input Object' }).click(); await page.getByRole('menuitem', { name: 'Image File Input Object' }).click();
await page.setInputFiles('#fileElem', imageFilePath); await page.setInputFiles('#fileElem', imageFilePath);
@ -121,7 +121,7 @@ test.describe('Persistence operations @addInit', () => {
}); });
await page.goto('./', { waitUntil: 'domcontentloaded' }); await page.goto('./', { waitUntil: 'domcontentloaded' });
await page.click('button:has-text("Create")'); await page.getByRole('button', { name: 'Create' }).click();
await page.click('text=Condition Set'); await page.click('text=Condition Set');

View File

@ -107,7 +107,7 @@ test.describe('Notification Overlay', () => {
await page.getByRole('button', { name: 'Close' }).click(); await page.getByRole('button', { name: 'Close' }).click();
// On the Display Layout object, click on the "Edit" button // On the Display Layout object, click on the "Edit" button
await page.getByRole('button', { name: 'Edit' }).click(); await page.getByRole('button', { name: 'Edit Object' }).click();
// Click on the "Save" button // Click on the "Save" button
await page.getByRole('button', { name: 'Save' }).click(); await page.getByRole('button', { name: 'Save' }).click();

View File

@ -69,7 +69,7 @@ test.describe('Gantt Chart', () => {
.getByRole('dialog') .getByRole('dialog')
.filter({ hasText: 'This action will replace the current Plan. Do you want to continue?' }); .filter({ hasText: 'This action will replace the current Plan. Do you want to continue?' });
await expect(replaceModal).toBeVisible(); await expect(replaceModal).toBeVisible();
await page.getByRole('button', { name: 'OK' }).click(); await page.getByRole('button', { name: 'Ok', exact: true }).click();
await assertPlanActivities(page, testPlan2, ganttChart.url); await assertPlanActivities(page, testPlan2, ganttChart.url);
}); });

View File

@ -38,7 +38,7 @@ test.describe('Clock Generator CRUD Operations', () => {
await page.goto('./', { waitUntil: 'domcontentloaded' }); await page.goto('./', { waitUntil: 'domcontentloaded' });
//Click the Create button //Click the Create button
await page.click('button:has-text("Create")'); await page.getByRole('button', { name: 'Create' }).click();
// Click Clock // Click Clock
await page.getByRole('menuitem').first().click(); await page.getByRole('menuitem').first().click();

View File

@ -34,7 +34,6 @@ import {
import { expect, test } from '../../../../pluginFixtures.js'; import { expect, test } from '../../../../pluginFixtures.js';
let conditionSetUrl; let conditionSetUrl;
let getConditionSetIdentifierFromUrl;
test.describe.serial('Condition Set CRUD Operations on @localStorage', () => { test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
test.beforeAll(async ({ browser }) => { test.beforeAll(async ({ browser }) => {
@ -42,7 +41,7 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
const context = await browser.newContext(); const context = await browser.newContext();
const page = await context.newPage(); const page = await context.newPage();
await page.goto('./', { waitUntil: 'domcontentloaded' }); await page.goto('./', { waitUntil: 'domcontentloaded' });
await page.click('button:has-text("Create")'); await page.getByRole('button', { name: 'Create' }).click();
await page.locator('li[role="menuitem"]:has-text("Condition Set")').click(); await page.locator('li[role="menuitem"]:has-text("Condition Set")').click();
@ -58,8 +57,6 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
//Set object identifier from url //Set object identifier from url
conditionSetUrl = page.url(); conditionSetUrl = page.url();
getConditionSetIdentifierFromUrl = conditionSetUrl.split('/').pop().split('?')[0];
console.debug(`getConditionSetIdentifierFromUrl: ${getConditionSetIdentifierFromUrl}`);
await page.close(); await page.close();
}); });
@ -234,7 +231,7 @@ test.describe('Basic Condition Set Use', () => {
await page.goto(conditionSet.url); await page.goto(conditionSet.url);
// Change the object to edit mode // Change the object to edit mode
await page.locator('[title="Edit"]').click(); await page.getByLabel('Edit Object').click();
// Click Add Condition button // Click Add Condition button
await page.locator('#addCondition').click(); await page.locator('#addCondition').click();
@ -262,7 +259,7 @@ test.describe('Basic Condition Set Use', () => {
await page.goto(conditionSet.url); await page.goto(conditionSet.url);
// Change the object to edit mode // Change the object to edit mode
await page.locator('[title="Edit"]').click(); await page.getByLabel('Edit Object').click();
// Expand the 'My Items' folder in the left tree // Expand the 'My Items' folder in the left tree
page.click('button[title="Show selected item in tree"]'); page.click('button[title="Show selected item in tree"]');
@ -299,7 +296,7 @@ test.describe('Basic Condition Set Use', () => {
await page.getByTitle('Show selected item in tree').click(); await page.getByTitle('Show selected item in tree').click();
await page.goto(conditionSet.url); await page.goto(conditionSet.url);
// Change the object to edit mode // Change the object to edit mode
await page.locator('[title="Edit"]').click(); await page.getByLabel('Edit Object').click();
// Create two conditions // Create two conditions
await page.locator('#addCondition').click(); await page.locator('#addCondition').click();

View File

@ -47,7 +47,7 @@ test.describe('Display Layout Toolbar Actions @localStorage', () => {
.filter({ hasText: 'Parent Display Layout Display Layout' }) .filter({ hasText: 'Parent Display Layout Display Layout' })
.first() .first()
.click(); .click();
await page.getByLabel('Edit').click(); await page.getByLabel('Edit Object').click();
}); });
test.use({ test.use({
storageState: LOCALSTORAGE_PATH storageState: LOCALSTORAGE_PATH
@ -133,7 +133,7 @@ test.describe('Display Layout', () => {
name: 'Test Display Layout' name: 'Test Display Layout'
}); });
// Edit Display Layout // Edit Display Layout
await page.locator('[title="Edit"]').click(); await page.getByLabel('Edit Object').click();
// Expand the 'My Items' folder in the left tree // Expand the 'My Items' folder in the left tree
await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click(); await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click();
@ -171,7 +171,7 @@ test.describe('Display Layout', () => {
name: 'Test Display Layout' name: 'Test Display Layout'
}); });
// Edit Display Layout // Edit Display Layout
await page.locator('[title="Edit"]').click(); await page.getByLabel('Edit Object').click();
// Expand the 'My Items' folder in the left tree // Expand the 'My Items' folder in the left tree
await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click(); await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click();
@ -213,7 +213,7 @@ test.describe('Display Layout', () => {
name: 'Test Display Layout' name: 'Test Display Layout'
}); });
// Edit Display Layout // Edit Display Layout
await page.locator('[title="Edit"]').click(); await page.getByLabel('Edit Object').click();
// Expand the 'My Items' folder in the left tree // Expand the 'My Items' folder in the left tree
await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click(); await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click();
@ -255,7 +255,7 @@ test.describe('Display Layout', () => {
type: 'Display Layout' type: 'Display Layout'
}); });
// Edit Display Layout // Edit Display Layout
await page.locator('[title="Edit"]').click(); await page.getByLabel('Edit Object').click();
// Expand the 'My Items' folder in the left tree // Expand the 'My Items' folder in the left tree
await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click(); await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click();
@ -301,7 +301,7 @@ test.describe('Display Layout', () => {
type: 'Display Layout' type: 'Display Layout'
}); });
// Edit Display Layout // Edit Display Layout
await page.locator('[title="Edit"]').click(); await page.getByLabel('Edit Object').click();
// Expand the 'My Items' folder in the left tree // Expand the 'My Items' folder in the left tree
await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click(); await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click();
@ -357,7 +357,7 @@ test.describe('Display Layout', () => {
name: 'Test Display Layout' name: 'Test Display Layout'
}); });
// Edit Display Layout // Edit Display Layout
await page.locator('[title="Edit"]').click(); await page.getByLabel('Edit Object').click();
// Expand the 'My Items' folder in the left tree // Expand the 'My Items' folder in the left tree
await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click(); await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click();
@ -449,7 +449,7 @@ async function removeLayoutObject(page, layoutObject) {
// eslint-disable-next-line playwright/no-force-option // eslint-disable-next-line playwright/no-force-option
.click({ force: true }); .click({ force: true });
await page.getByTitle('Delete the selected object').click(); await page.getByTitle('Delete the selected object').click();
await page.getByRole('button', { name: 'OK' }).click(); await page.getByRole('button', { name: 'OK', exact: true }).click();
} }
/** /**

View File

@ -73,7 +73,7 @@ test.describe('Flexible Layout', () => {
}) => { }) => {
await page.goto(flexibleLayout.url); await page.goto(flexibleLayout.url);
// Edit Flexible Layout // Edit Flexible Layout
await page.locator('[title="Edit"]').click(); await page.getByLabel('Edit Object').click();
// Expand the 'My Items' folder in the left tree // Expand the 'My Items' folder in the left tree
await page.locator('.c-tree__item__view-control.c-disclosure-triangle').first().click(); await page.locator('.c-tree__item__view-control.c-disclosure-triangle').first().click();
@ -166,7 +166,7 @@ test.describe('Flexible Layout', () => {
}) => { }) => {
await page.goto(flexibleLayout.url); await page.goto(flexibleLayout.url);
// Edit Flexible Layout // Edit Flexible Layout
await page.locator('[title="Edit"]').click(); await page.getByLabel('Edit Object').click();
// Expand the 'My Items' folder in the left tree // Expand the 'My Items' folder in the left tree
await page.locator('.c-tree__item__view-control.c-disclosure-triangle').first().click(); await page.locator('.c-tree__item__view-control.c-disclosure-triangle').first().click();
@ -197,7 +197,7 @@ test.describe('Flexible Layout', () => {
}); });
await page.goto(flexibleLayout.url); await page.goto(flexibleLayout.url);
// Edit Flexible Layout // Edit Flexible Layout
await page.locator('[title="Edit"]').click(); await page.getByLabel('Edit Object').click();
// Expand the 'My Items' folder in the left tree // Expand the 'My Items' folder in the left tree
await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click(); await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click();
@ -234,7 +234,7 @@ test.describe('Flexible Layout', () => {
await page.goto(flexibleLayout.url); await page.goto(flexibleLayout.url);
// Edit Flexible Layout // Edit Flexible Layout
await page.locator('[title="Edit"]').click(); await page.getByLabel('Edit Object').click();
// Expand the 'My Items' folder in the left tree // Expand the 'My Items' folder in the left tree
await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click(); await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click();
@ -276,7 +276,7 @@ test.describe('Flexible Layout Toolbar Actions @localStorage', () => {
.filter({ hasText: 'Parent Flexible Layout Flexible Layout' }) .filter({ hasText: 'Parent Flexible Layout Flexible Layout' })
.first() .first()
.click(); .click();
await page.getByLabel('Edit').click(); await page.getByLabel('Edit Object').click();
}); });
test('Add/Remove Container', async ({ page }) => { test('Add/Remove Container', async ({ page }) => {
test.info().annotations.push({ test.info().annotations.push({
@ -293,7 +293,7 @@ test.describe('Flexible Layout Toolbar Actions @localStorage', () => {
await expect(page.getByRole('dialog', { name: 'Overlay' })).toHaveText( await expect(page.getByRole('dialog', { name: 'Overlay' })).toHaveText(
'This action will permanently delete this container from this Flexible Layout. Do you want to continue?' 'This action will permanently delete this container from this Flexible Layout. Do you want to continue?'
); );
await page.getByRole('button', { name: 'OK' }).click(); await page.getByRole('button', { name: 'OK', exact: true }).click();
expect(await containerHandles.count()).toEqual(2); expect(await containerHandles.count()).toEqual(2);
}); });
test('Remove Frame', async ({ page }) => { test('Remove Frame', async ({ page }) => {
@ -303,7 +303,7 @@ test.describe('Flexible Layout Toolbar Actions @localStorage', () => {
await expect(page.getByRole('dialog', { name: 'Overlay' })).toHaveText( await expect(page.getByRole('dialog', { name: 'Overlay' })).toHaveText(
'This action will remove this frame from this Flexible Layout. Do you want to continue?' 'This action will remove this frame from this Flexible Layout. Do you want to continue?'
); );
await page.getByRole('button', { name: 'OK' }).click(); await page.getByRole('button', { name: 'OK', exact: true }).click();
expect(await page.getByRole('group', { name: 'Frame' }).count()).toEqual(1); expect(await page.getByRole('group', { name: 'Frame' }).count()).toEqual(1);
}); });
test('Columns/Rows Layout Toggle', async ({ page }) => { test('Columns/Rows Layout Toggle', async ({ page }) => {

View File

@ -41,8 +41,6 @@ test.describe('Gauge', () => {
test('Can add and remove telemetry sources @unstable', async ({ page }) => { test('Can add and remove telemetry sources @unstable', async ({ page }) => {
// Create the gauge with defaults // Create the gauge with defaults
const gauge = await createDomainObjectWithDefaults(page, { type: 'Gauge' }); const gauge = await createDomainObjectWithDefaults(page, { type: 'Gauge' });
const editButtonLocator = page.locator('button[title="Edit"]');
const saveButtonLocator = page.locator('button[title="Save"]');
// Create a sine wave generator within the gauge // Create a sine wave generator within the gauge
const swg1 = await createDomainObjectWithDefaults(page, { const swg1 = await createDomainObjectWithDefaults(page, {
@ -54,9 +52,9 @@ test.describe('Gauge', () => {
// Navigate to the gauge and verify that // Navigate to the gauge and verify that
// the SWG appears in the elements pool // the SWG appears in the elements pool
await page.goto(gauge.url); await page.goto(gauge.url);
await editButtonLocator.click(); await page.getByLabel('Edit Object').click();
await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg1.name}`)).toBeVisible(); await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg1.name}`)).toBeVisible();
await saveButtonLocator.click(); await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
// Create another sine wave generator within the gauge // Create another sine wave generator within the gauge
@ -79,10 +77,10 @@ test.describe('Gauge', () => {
// Navigate to the gauge and verify that the new SWG // Navigate to the gauge and verify that the new SWG
// appears in the elements pool and the old one is gone // appears in the elements pool and the old one is gone
await page.goto(gauge.url); await page.goto(gauge.url);
await editButtonLocator.click(); await page.getByLabel('Edit Object').click();
await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg1.name}`)).toBeHidden(); await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg1.name}`)).toBeHidden();
await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg2.name}`)).toBeVisible(); await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg2.name}`)).toBeVisible();
await saveButtonLocator.click(); await page.getByRole('button', { name: 'Save' }).click();
// Right click on the new SWG in the elements pool and delete it // Right click on the new SWG in the elements pool and delete it
await page.locator(`#inspector-elements-tree >> text=${swg2.name}`).click({ await page.locator(`#inspector-elements-tree >> text=${swg2.name}`).click({
@ -109,7 +107,7 @@ test.describe('Gauge', () => {
description: 'https://github.com/nasa/openmct/issues/5356' description: 'https://github.com/nasa/openmct/issues/5356'
}); });
//Click the Create button //Click the Create button
await page.click('button:has-text("Create")'); await page.getByRole('button', { name: 'Create' }).click();
// Click the object specified by 'type' // Click the object specified by 'type'
await page.click(`li[role='menuitem']:text("Gauge")`); await page.click(`li[role='menuitem']:text("Gauge")`);

View File

@ -61,7 +61,7 @@ test.describe('Example Imagery Object', () => {
await expect(page.locator('.c-hud')).toBeHidden(); await expect(page.locator('.c-hud')).toBeHidden();
}); });
test('Can right click on image and open it in a new tab', async ({ page, context }) => { test('Can right click on image and open it in a new tab @2p', async ({ page, context }) => {
// try to right click on image // try to right click on image
const backgroundImage = await page.locator(backgroundImageSelector); const backgroundImage = await page.locator(backgroundImageSelector);
await backgroundImage.click({ await backgroundImage.click({
@ -397,7 +397,7 @@ test.describe('Example Imagery in Display Layout', () => {
}); });
// Edit mode // Edit mode
await page.click('button[title="Edit"]'); await page.getByLabel('Edit Object').click();
// Click on example imagery to expose toolbar // Click on example imagery to expose toolbar
await page.locator('.c-so-view__header').click(); await page.locator('.c-so-view__header').click();
@ -416,7 +416,7 @@ test.describe('Example Imagery in Display Layout', () => {
test('Resizing the layout changes thumbnail visibility and size', async ({ page }) => { test('Resizing the layout changes thumbnail visibility and size', async ({ page }) => {
const thumbsWrapperLocator = page.locator('.c-imagery__thumbs-wrapper'); const thumbsWrapperLocator = page.locator('.c-imagery__thumbs-wrapper');
// Edit mode // Edit mode
await page.click('button[title="Edit"]'); await page.getByLabel('Edit Object').click();
// Click on example imagery to expose toolbar // Click on example imagery to expose toolbar
await page.locator('.c-so-view__header').click(); await page.locator('.c-so-view__header').click();
@ -493,7 +493,7 @@ test.describe('Example Imagery in Flexible layout', () => {
/* Create Sine Wave Generator with minimum Image Load Delay */ /* Create Sine Wave Generator with minimum Image Load Delay */
// Click the Create button // Click the Create button
await page.click('button:has-text("Create")'); await page.getByRole('button', { name: 'Create' }).click();
// Click text=Example Imagery // Click text=Example Imagery
await page.click('li[role="menuitem"]:has-text("Example Imagery")'); await page.click('li[role="menuitem"]:has-text("Example Imagery")');
@ -537,7 +537,7 @@ test.describe('Example Imagery in Tabs View', () => {
/* Create Sine Wave Generator with minimum Image Load Delay */ /* Create Sine Wave Generator with minimum Image Load Delay */
// Click the Create button // Click the Create button
await page.click('button:has-text("Create")'); await page.getByRole('button', { name: 'Create' }).click();
// Click text=Example Imagery // Click text=Example Imagery
await page.click('li[role="menuitem"]:has-text("Example Imagery")'); await page.click('li[role="menuitem"]:has-text("Example Imagery")');
@ -984,7 +984,7 @@ async function resetImageryPanAndZoom(page) {
*/ */
async function createImageryView(page) { async function createImageryView(page) {
// Click the Create button // Click the Create button
await page.click('button:has-text("Create")'); await page.getByRole('button', { name: 'Create' }).click();
// Click text=Example Imagery // Click text=Example Imagery
await page.click('li[role="menuitem"]:has-text("Example Imagery")'); await page.click('li[role="menuitem"]:has-text("Example Imagery")');

View File

@ -52,7 +52,7 @@ test.describe('Testing LAD table configuration', () => {
}); });
test('in edit mode, LAD Tables provide ability to hide columns', async ({ page }) => { test('in edit mode, LAD Tables provide ability to hide columns', async ({ page }) => {
// Edit LAD table // Edit LAD table
await page.locator('[title="Edit"]').click(); await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'LAD Table Configuration' }).click(); await page.getByRole('tab', { name: 'LAD Table Configuration' }).click();
// make sure headers are visible initially // make sure headers are visible initially
@ -113,7 +113,7 @@ test.describe('Testing LAD table configuration', () => {
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible(); await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible();
// Edit LAD table // Edit LAD table
await page.locator('[title="Edit"]').click(); await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'LAD Table Configuration' }).click(); await page.getByRole('tab', { name: 'LAD Table Configuration' }).click();
// show timestamp column // show timestamp column
@ -141,7 +141,7 @@ test.describe('Testing LAD table configuration', () => {
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible(); await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible();
// Edit LAD table // Edit LAD table
await page.locator('[title="Edit"]').click(); await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'LAD Table Configuration' }).click(); await page.getByRole('tab', { name: 'LAD Table Configuration' }).click();
// show units, type, and WATCH columns // show units, type, and WATCH columns
@ -181,7 +181,7 @@ test.describe('Testing LAD table configuration', () => {
await page.goto(ladTable.url); await page.goto(ladTable.url);
// Edit LAD table // Edit LAD table
await page.getByLabel('Edit').click(); await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'LAD Table Configuration' }).click(); await page.getByRole('tab', { name: 'LAD Table Configuration' }).click();
// make sure Sine Wave headers are visible initially too // make sure Sine Wave headers are visible initially too
@ -198,10 +198,10 @@ test.describe('Testing LAD table configuration', () => {
await page.getByLabel('Save').click(); await page.getByLabel('Save').click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
// Remove Sin Wave Generator // Remove Sine Wave Generator
openObjectTreeContextMenu(page, sineWaveObject.url); openObjectTreeContextMenu(page, sineWaveObject.url);
await page.getByRole('menuitem', { name: /Remove/ }).click(); await page.getByRole('menuitem', { name: /Remove/ }).click();
await page.getByRole('button', { name: 'OK' }).click(); await page.getByRole('button', { name: 'OK', exact: true }).click();
// Ensure Units & Limit columns are gone // Ensure Units & Limit columns are gone
// as Event Generator don't have them // as Event Generator don't have them
@ -258,7 +258,7 @@ test.describe('Testing LAD table @unstable', () => {
name: 'Test LAD Table' name: 'Test LAD Table'
}); });
// Edit LAD table // Edit LAD table
await page.locator('[title="Edit"]').click(); await page.getByLabel('Edit Object').click();
// Expand the 'My Items' folder in the left tree // Expand the 'My Items' folder in the left tree
await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click(); await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click();
@ -286,7 +286,7 @@ test.describe('Testing LAD table @unstable', () => {
name: 'Test LAD Table' name: 'Test LAD Table'
}); });
// Edit LAD table // Edit LAD table
await page.locator('[title="Edit"]').click(); await page.getByLabel('Edit Object').click();
// Expand the 'My Items' folder in the left tree // Expand the 'My Items' folder in the left tree
await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click(); await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click();

View File

@ -89,10 +89,15 @@ test.describe('Snapshot Container tests', () => {
// name: "Dropped Overlay Plot" // name: "Dropped Overlay Plot"
// }); // });
await page.getByRole('button', { name: ' Snapshot ' }).click(); await page.getByLabel('Take a Notebook Snapshot').click();
await page.getByRole('menuitem', { name: 'Save to Notebook Snapshots' }).click(); await page.getByRole('menuitem', { name: 'Save to Notebook Snapshots' }).click();
await page.getByRole('button', { name: 'Show' }).click(); await page.getByRole('button', { name: 'Show' }).click();
}); });
test('A snapshot can be Quick Viewed from Container with 3 dot action menu', async ({ page }) => {
await page.locator('.c-snapshot.c-ne__embed').first().getByTitle('More actions').click();
await page.getByRole('menuitem', { name: 'Quick View' }).click();
await expect(page.locator('.c-overlay__outer')).toBeVisible();
});
test.fixme('5 Snapshots can be added to a container', async ({ page }) => {}); test.fixme('5 Snapshots can be added to a container', async ({ page }) => {});
test.fixme( test.fixme(
'5 Snapshots can be added to a container and Deleted with Delete All action', '5 Snapshots can be added to a container and Deleted with Delete All action',
@ -117,11 +122,7 @@ test.describe('Snapshot Container tests', () => {
//await expect(await page.locator) //await expect(await page.locator)
} }
); );
test('A snapshot can be Quick Viewed from Container with 3 dot action menu', async ({ page }) => {
await page.locator('.c-snapshot.c-ne__embed').first().getByTitle('More actions').click();
await page.getByRole('menuitem', { name: 'Quick View' }).click();
await expect(page.locator('.c-overlay__outer')).toBeVisible();
});
test.fixme( test.fixme(
'A snapshot can be Navigated To from Container with 3 dot action menu', 'A snapshot can be Navigated To from Container with 3 dot action menu',
async ({ page }) => {} async ({ page }) => {}

View File

@ -128,7 +128,7 @@ test.describe('Tagging in Notebooks @addInit', () => {
}); });
// Go back into edit mode for the display layout // Go back into edit mode for the display layout
await page.getByRole('button', { name: 'Edit' }).click(); await page.getByRole('button', { name: 'Edit Object' }).click();
await page.getByRole('search').getByLabel('Search Input').click(); await page.getByRole('search').getByLabel('Search Input').click();
await page.getByRole('search').getByLabel('Search Input').fill('Sc'); await page.getByRole('search').getByLabel('Search Input').fill('Sc');

View File

@ -58,7 +58,7 @@ test.describe('Autoscale', () => {
await testYTicks(page, ['-1.00', '-0.50', '0.00', '0.50', '1.00']); await testYTicks(page, ['-1.00', '-0.50', '0.00', '0.50', '1.00']);
// enter edit mode // enter edit mode
await page.click('button[title="Edit"]'); await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'Config' }).click(); await page.getByRole('tab', { name: 'Config' }).click();
await turnOffAutoscale(page); await turnOffAutoscale(page);

View File

@ -183,7 +183,7 @@ async function testLogTicks(page) {
*/ */
async function enableEditMode(page) { async function enableEditMode(page) {
// turn on edit mode // turn on edit mode
await page.getByRole('button', { name: 'Edit' }).click(); await page.getByRole('button', { name: 'Edit Object' }).click();
await expect(page.getByRole('button', { name: 'Save' })).toBeVisible(); await expect(page.getByRole('button', { name: 'Save' })).toBeVisible();
} }

View File

@ -87,7 +87,7 @@ test.describe('Overlay Plot', () => {
expect(await page.locator('.c-plot-limit-line').count()).toBe(0); expect(await page.locator('.c-plot-limit-line').count()).toBe(0);
// Enter edit mode // Enter edit mode
await page.click('button[title="Edit"]'); await page.getByLabel('Edit Object').click();
// Expand the "Sine Wave Generator" plot series options and enable limit lines // Expand the "Sine Wave Generator" plot series options and enable limit lines
await page.getByRole('tab', { name: 'Config' }).click(); await page.getByRole('tab', { name: 'Config' }).click();
@ -114,7 +114,7 @@ test.describe('Overlay Plot', () => {
await assertLimitLinesExistAndAreVisible(page); await assertLimitLinesExistAndAreVisible(page);
// Enter edit mode // Enter edit mode
await page.click('button[title="Edit"]'); await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'Elements' }).click(); await page.getByRole('tab', { name: 'Elements' }).click();
@ -165,7 +165,7 @@ test.describe('Overlay Plot', () => {
}); });
await page.goto(overlayPlot.url); await page.goto(overlayPlot.url);
await page.click('button[title="Edit"]'); await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'Elements' }).click(); await page.getByRole('tab', { name: 'Elements' }).click();
@ -239,7 +239,7 @@ test.describe('Overlay Plot', () => {
await page.goto(overlayPlot.url); await page.goto(overlayPlot.url);
// Wait for plot series data to load and be drawn // Wait for plot series data to load and be drawn
await waitForPlotsToRender(page); await waitForPlotsToRender(page);
await page.click('button[title="Edit"]'); await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'Elements' }).click(); await page.getByRole('tab', { name: 'Elements' }).click();

View File

@ -41,9 +41,6 @@ test.describe('Scatter Plot', () => {
}); });
test('Can add and remove telemetry sources', async ({ page }) => { test('Can add and remove telemetry sources', async ({ page }) => {
const editButton = page.locator('button[title="Edit"]');
const saveButton = page.locator('button[title="Save"]');
// Create a sine wave generator within the scatter plot // Create a sine wave generator within the scatter plot
const swg1 = await createDomainObjectWithDefaults(page, { const swg1 = await createDomainObjectWithDefaults(page, {
type: 'Sine Wave Generator', type: 'Sine Wave Generator',
@ -54,10 +51,10 @@ test.describe('Scatter Plot', () => {
// Navigate to the scatter plot and verify that // Navigate to the scatter plot and verify that
// the SWG appears in the elements pool // the SWG appears in the elements pool
await page.goto(scatterPlot.url); await page.goto(scatterPlot.url);
await editButton.click(); await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'Elements' }).click(); await page.getByRole('tab', { name: 'Elements' }).click();
await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg1.name}`)).toBeVisible(); await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg1.name}`)).toBeVisible();
await saveButton.click(); await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
// Create another sine wave generator within the scatter plot // Create another sine wave generator within the scatter plot
@ -80,13 +77,13 @@ test.describe('Scatter Plot', () => {
// Navigate to the scatter plot and verify that the new SWG // Navigate to the scatter plot and verify that the new SWG
// appears in the elements pool and the old one is gone // appears in the elements pool and the old one is gone
await page.goto(scatterPlot.url); await page.goto(scatterPlot.url);
await editButton.click(); await page.getByLabel('Edit Object').click();
// Click the "Elements" tab // Click the "Elements" tab
await page.getByRole('tab', { name: 'Elements' }).click(); await page.getByRole('tab', { name: 'Elements' }).click();
await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg1.name}`)).toBeHidden(); await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg1.name}`)).toBeHidden();
await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg2.name}`)).toBeVisible(); await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg2.name}`)).toBeVisible();
await saveButton.click(); await page.getByRole('button', { name: 'Save' }).click();
// Right click on the new SWG in the elements pool and delete it // Right click on the new SWG in the elements pool and delete it
await page.locator(`#inspector-elements-tree >> text=${swg2.name}`).click({ await page.locator(`#inspector-elements-tree >> text=${swg2.name}`).click({

View File

@ -69,7 +69,7 @@ test.describe('Stacked Plot', () => {
await page.goto(stackedPlot.url); await page.goto(stackedPlot.url);
await page.click('button[title="Edit"]'); await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'Elements' }).click(); await page.getByRole('tab', { name: 'Elements' }).click();
@ -101,7 +101,7 @@ test.describe('Stacked Plot', () => {
await page.goto(stackedPlot.url); await page.goto(stackedPlot.url);
await page.click('button[title="Edit"]'); await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'Elements' }).click(); await page.getByRole('tab', { name: 'Elements' }).click();
@ -187,7 +187,7 @@ test.describe('Stacked Plot', () => {
).toContainText(swgC.name); ).toContainText(swgC.name);
// Go into edit mode // Go into edit mode
await page.click('button[title="Edit"]'); await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'Config' }).click(); await page.getByRole('tab', { name: 'Config' }).click();
@ -231,8 +231,10 @@ test.describe('Stacked Plot', () => {
test('the legend toggles between aggregate and per child', async ({ page }) => { test('the legend toggles between aggregate and per child', async ({ page }) => {
await page.goto(stackedPlot.url); await page.goto(stackedPlot.url);
await waitForPlotsToRender(page);
// Go into edit mode // Go into edit mode
await page.click('button[title="Edit"]'); await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'Config' }).click(); await page.getByRole('tab', { name: 'Config' }).click();
@ -245,10 +247,14 @@ test.describe('Stacked Plot', () => {
await page.locator('button[title="Save"]').click(); await page.locator('button[title="Save"]').click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
await waitForPlotsToRender(page);
await assertAggregateLegendIsVisible(page); await assertAggregateLegendIsVisible(page);
await page.reload(); await page.reload();
await waitForPlotsToRender(page);
await assertAggregateLegendIsVisible(page); await assertAggregateLegendIsVisible(page);
}); });
}); });

View File

@ -72,7 +72,7 @@ test.describe('Flexible Layout styling', () => {
await page.goto(flexibleLayout.url, { waitUntil: 'domcontentloaded' }); await page.goto(flexibleLayout.url, { waitUntil: 'domcontentloaded' });
// Edit Flexible Layout // Edit Flexible Layout
await page.getByLabel('Edit').click(); await page.getByLabel('Edit Object').click();
// Select styles tab // Select styles tab
await page.getByRole('tab', { name: 'Styles' }).click(); await page.getByRole('tab', { name: 'Styles' }).click();
@ -133,7 +133,7 @@ test.describe('Flexible Layout styling', () => {
await page.goto(flexibleLayout.url, { waitUntil: 'domcontentloaded' }); await page.goto(flexibleLayout.url, { waitUntil: 'domcontentloaded' });
// Edit Flexible Layout // Edit Flexible Layout
await page.getByLabel('Edit').click(); await page.getByLabel('Edit Object').click();
// Select styles tab // Select styles tab
await page.getByRole('tab', { name: 'Styles' }).click(); await page.getByRole('tab', { name: 'Styles' }).click();
@ -210,7 +210,7 @@ test.describe('Flexible Layout styling', () => {
await page.goto(stackedPlot.url, { waitUntil: 'domcontentloaded' }); await page.goto(stackedPlot.url, { waitUntil: 'domcontentloaded' });
// Edit stackedPlot // Edit stackedPlot
await page.getByLabel('Edit').click(); await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'Styles' }).click(); await page.getByRole('tab', { name: 'Styles' }).click();
@ -231,7 +231,7 @@ test.describe('Flexible Layout styling', () => {
await page.goto(flexibleLayout.url, { waitUntil: 'domcontentloaded' }); await page.goto(flexibleLayout.url, { waitUntil: 'domcontentloaded' });
// Edit Flexible Layout // Edit Flexible Layout
await page.getByLabel('Edit').click(); await page.getByLabel('Edit Object').click();
// Select styles tab // Select styles tab
await page.getByRole('tab', { name: 'Styles' }).click(); await page.getByRole('tab', { name: 'Styles' }).click();
@ -296,7 +296,7 @@ test.describe('Flexible Layout styling', () => {
await page.goto(flexibleLayout.url, { waitUntil: 'domcontentloaded' }); await page.goto(flexibleLayout.url, { waitUntil: 'domcontentloaded' });
// Edit Flexible Layout // Edit Flexible Layout
await page.getByLabel('Edit').click(); await page.getByLabel('Edit Object').click();
// Select styles tab // Select styles tab
await page.getByRole('tab', { name: 'Styles' }).click(); await page.getByRole('tab', { name: 'Styles' }).click();
@ -345,7 +345,7 @@ test.describe('Flexible Layout styling', () => {
await page.goto(flexibleLayout.url, { waitUntil: 'domcontentloaded' }); await page.goto(flexibleLayout.url, { waitUntil: 'domcontentloaded' });
// Edit Flexible Layout // Edit Flexible Layout
await page.getByLabel('Edit').click(); await page.getByLabel('Edit Object').click();
// Select styles tab // Select styles tab
await page.getByRole('tab', { name: 'Styles' }).click(); await page.getByRole('tab', { name: 'Styles' }).click();
@ -375,7 +375,7 @@ test.describe('Flexible Layout styling', () => {
await page.reload({ waitUntil: 'domcontentloaded' }); await page.reload({ waitUntil: 'domcontentloaded' });
// Edit Flexible Layout // Edit Flexible Layout
await page.getByLabel('Edit').click(); await page.getByLabel('Edit Object').click();
// Select styles tab // Select styles tab
await page.getByRole('tab', { name: 'Styles' }).click(); await page.getByRole('tab', { name: 'Styles' }).click();

View File

@ -74,7 +74,7 @@ test.describe('Stacked Plot styling', () => {
// Directly navigate to the stacked plot // Directly navigate to the stacked plot
await page.goto(stackedPlot.url, { waitUntil: 'domcontentloaded' }); await page.goto(stackedPlot.url, { waitUntil: 'domcontentloaded' });
await page.getByLabel('Edit').click(); await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'Styles' }).click(); await page.getByRole('tab', { name: 'Styles' }).click();
@ -165,7 +165,7 @@ test.describe('Stacked Plot styling', () => {
}); });
await page.goto(stackedPlot.url, { waitUntil: 'domcontentloaded' }); await page.goto(stackedPlot.url, { waitUntil: 'domcontentloaded' });
await page.getByLabel('Edit').click(); await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'Styles' }).click(); await page.getByRole('tab', { name: 'Styles' }).click();

View File

@ -49,7 +49,7 @@ test.describe('Style Inspector Options', () => {
await page.goto(flexibleLayout.url, { waitUntil: 'domcontentloaded' }); await page.goto(flexibleLayout.url, { waitUntil: 'domcontentloaded' });
// Edit Flexible Layout // Edit Flexible Layout
await page.getByLabel('Edit').click(); await page.getByLabel('Edit Object').click();
// The overall Flex Layout or Stacked Plot itself MUST be style-able. // The overall Flex Layout or Stacked Plot itself MUST be style-able.
await expect(page.getByRole('tab', { name: 'Styles' })).toBeVisible(); await expect(page.getByRole('tab', { name: 'Styles' })).toBeVisible();

View File

@ -128,7 +128,10 @@ test.describe('Telemetry Table', () => {
// focus the Telemetry Table // focus the Telemetry Table
page.goto(table.url); page.goto(table.url);
await page.getByRole('searchbox', { name: 'message filter input' }).hover(); await page.getByRole('searchbox', { name: 'message filter input' }).hover();
await page.getByLabel('Message filter header').getByRole('button', { name: '/R/' }).click(); await page
.getByLabel('Message filter header')
.getByLabel('Click to enable regex: enter a string with slashes, like this: /regex_exp/')
.click();
await page.getByRole('searchbox', { name: 'message filter input' }).click(); await page.getByRole('searchbox', { name: 'message filter input' }).click();
await page.getByRole('searchbox', { name: 'message filter input' }).fill('/[Rr]oger/'); await page.getByRole('searchbox', { name: 'message filter input' }).fill('/[Rr]oger/');

View File

@ -104,7 +104,7 @@ test.describe('Recent Objects', () => {
button: 'right' button: 'right'
}); });
await page.getByRole('menuitem', { name: /Remove/ }).click(); await page.getByRole('menuitem', { name: /Remove/ }).click();
await page.getByRole('button', { name: 'OK' }).click(); await page.getByRole('button', { name: 'OK', exact: true }).click();
// Verify that the folder and clock are no longer in the recent objects list // Verify that the folder and clock are no longer in the recent objects list
await expect(recentObjectsList.getByRole('listitem', { name: folderA.name })).toBeHidden(); await expect(recentObjectsList.getByRole('listitem', { name: folderA.name })).toBeHidden();
@ -293,7 +293,7 @@ test.describe('Recent Objects', () => {
await page.getByRole('button', { name: 'Clear Recently Viewed' }).click(); await page.getByRole('button', { name: 'Clear Recently Viewed' }).click();
// Click on the "OK" button in the confirmation dialog // Click on the "OK" button in the confirmation dialog
await page.getByRole('button', { name: 'OK' }).click(); await page.getByRole('button', { name: 'OK', exact: true }).click();
// Assert that the list is empty // Assert that the list is empty
expect(await recentObjectsList.locator('.c-recentobjects-listitem').count()).toBe(0); expect(await recentObjectsList.locator('.c-recentobjects-listitem').count()).toBe(0);
@ -311,7 +311,7 @@ test.describe('Recent Objects', () => {
await page.getByRole('button', { name: 'Clear Recently Viewed' }).click(); await page.getByRole('button', { name: 'Clear Recently Viewed' }).click();
// Click on the "OK" button in the confirmation dialog // Click on the "OK" button in the confirmation dialog
await page.getByRole('button', { name: 'OK' }).click(); await page.getByRole('button', { name: 'OK', exact: true }).click();
// Assert that the list is empty // Assert that the list is empty
expect(await recentObjectsList.locator('.c-recentobjects-listitem').count()).toBe(0); expect(await recentObjectsList.locator('.c-recentobjects-listitem').count()).toBe(0);

View File

@ -49,7 +49,7 @@ test.describe('Grand Search', () => {
const createdObjects = await createObjectsForSearch(page); const createdObjects = await createObjectsForSearch(page);
// Go back into edit mode for the display layout // Go back into edit mode for the display layout
await page.getByRole('button', { name: 'Edit' }).click(); await page.getByRole('button', { name: 'Edit Object' }).click();
await grandSearchInput.click(); await grandSearchInput.click();
await grandSearchInput.fill('Cl'); await grandSearchInput.fill('Cl');

View File

@ -42,7 +42,7 @@ test('Verify that the create button appears and that the Folder Domain Object is
await page.goto('./', { waitUntil: 'domcontentloaded' }); await page.goto('./', { waitUntil: 'domcontentloaded' });
//Click the Create button //Click the Create button
await page.click('button:has-text("Create")'); await page.getByRole('button', { name: 'Create' }).click();
// Verify that Create Folder appears in the dropdown // Verify that Create Folder appears in the dropdown
await expect(page.locator(':nth-match(:text("Folder"), 2)')).toBeEnabled(); await expect(page.locator(':nth-match(:text("Folder"), 2)')).toBeEnabled();

View File

@ -96,7 +96,7 @@ test.describe('Verify tooltips', () => {
name: 'Test LAD Table' name: 'Test LAD Table'
}); });
// Edit LAD table // Edit LAD table
await page.locator('[title="Edit"]').click(); await page.getByLabel('Edit Object').click();
// Add the Sine Wave Generator to the LAD table and save changes // Add the Sine Wave Generator to the LAD table and save changes
await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.c-lad-table-wrapper'); await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.c-lad-table-wrapper');
@ -126,7 +126,7 @@ test.describe('Verify tooltips', () => {
name: 'Test Overlay Plots' name: 'Test Overlay Plots'
}); });
// Edit Overlay Plot // Edit Overlay Plot
await page.locator('[title="Edit"]').click(); await page.getByLabel('Edit Object').click();
// Add the Sine Wave Generator to the LAD table and save changes // Add the Sine Wave Generator to the LAD table and save changes
await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.gl-plot'); await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.gl-plot');
@ -197,7 +197,7 @@ test.describe('Verify tooltips', () => {
name: 'Test Overlay Plot' name: 'Test Overlay Plot'
}); });
// Edit Overlay Plot // Edit Overlay Plot
await page.locator('[title="Edit"]').click(); await page.getByLabel('Edit Object').click();
await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.gl-plot'); await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.gl-plot');
await page.locator('button[title="Save"]').click(); await page.locator('button[title="Save"]').click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
@ -208,7 +208,7 @@ test.describe('Verify tooltips', () => {
name: 'Test Stacked Plot' name: 'Test Stacked Plot'
}); });
// Edit Stacked Plot // Edit Stacked Plot
await page.locator('[title="Edit"]').click(); await page.getByLabel('Edit Object').click();
await page.dragAndDrop(`text=${sineWaveObject2.name}`, '.c-plot--stacked.holder'); await page.dragAndDrop(`text=${sineWaveObject2.name}`, '.c-plot--stacked.holder');
await page.locator('button[title="Save"]').click(); await page.locator('button[title="Save"]').click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(); await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
@ -219,7 +219,7 @@ test.describe('Verify tooltips', () => {
name: 'Test Display Layout' name: 'Test Display Layout'
}); });
// Edit Display Layout // Edit Display Layout
await page.locator('[title="Edit"]').click(); await page.getByLabel('Edit Object').click();
await page.dragAndDrop("text='Test Overlay Plot'", '.l-layout__grid-holder', { await page.dragAndDrop("text='Test Overlay Plot'", '.l-layout__grid-holder', {
targetPosition: { x: 0, y: 0 } targetPosition: { x: 0, y: 0 }
@ -428,7 +428,7 @@ test.describe('Verify tooltips', () => {
name: 'Test Time Strip' name: 'Test Time Strip'
}); });
// Edit Overlay Plot // Edit Overlay Plot
await page.locator('[title="Edit"]').click(); await page.getByLabel('Edit Object').click();
await page.dragAndDrop( await page.dragAndDrop(
`text=${sineWaveObject1.name}`, `text=${sineWaveObject1.name}`,
'.c-object-view.is-object-type-time-strip' '.c-object-view.is-object-type-time-strip'

View File

@ -26,10 +26,10 @@ Tests the branding associated with the default deployment. At least the about mo
import percySnapshot from '@percy/playwright'; import percySnapshot from '@percy/playwright';
import { expect, scanForA11yViolations, test } from '../../../avpFixtures.js';
import { VISUAL_URL } from '../../../constants.js'; import { VISUAL_URL } from '../../../constants.js';
import { expect, test } from '../../../pluginFixtures.js';
test.describe('Visual - Branding', () => { test.describe('Visual - Branding @a11y', () => {
test.beforeEach(async ({ page }) => { test.beforeEach(async ({ page }) => {
//Go to baseURL and Hide Tree //Go to baseURL and Hide Tree
await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' }); await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' });
@ -37,18 +37,22 @@ test.describe('Visual - Branding', () => {
test('Visual - About Modal', async ({ page, theme }) => { test('Visual - About Modal', async ({ page, theme }) => {
// Click About button // Click About button
await page.click('.l-shell__app-logo'); await page.getByLabel('About Modal').click();
// Modify the Build information in 'about' to be consistent run-over-run // Modify the Build information in 'about' to be consistent run-over-run
const versionInformationLocator = page.locator('ul.t-info.l-info.s-info').first(); await expect(page.locator('id=versionInformation')).toBeEnabled();
await expect(versionInformationLocator).toBeEnabled(); await page
await versionInformationLocator.evaluate( .locator('id=versionInformation')
(node) => .evaluate(
(node.innerHTML = (node) =>
'<li>Version: visual-snapshot</li> <li>Build Date: Mon Nov 15 2021 08:07:51 GMT-0800 (Pacific Standard Time)</li> <li>Revision: 93049cdbc6c047697ca204893db9603b864b8c9f</li> <li>Branch: master</li>') (node.innerHTML =
); '<li>Version: visual-snapshot</li> <li>Build Date: Mon Nov 15 2021 08:07:51 GMT-0800 (Pacific Standard Time)</li> <li>Revision: 93049cdbc6c047697ca204893db9603b864b8c9f</li> <li>Branch: master</li>')
);
// Take a snapshot of the About modal // Take a snapshot of the About modal
await percySnapshot(page, `About (theme: '${theme}')`); await percySnapshot(page, `About (theme: '${theme}')`);
}); });
}); });
test.afterEach(async ({ page }, testInfo) => {
await scanForA11yViolations(page, testInfo.title);
});

View File

@ -0,0 +1,56 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*
Tests the branding associated with the default deployment. At least the about modal for now
*/
import percySnapshot from '@percy/playwright';
import { scanForA11yViolations, test } from '../../../avpFixtures.js';
import { VISUAL_URL } from '../../../constants.js';
//Declare the scope of the visual test
const header = '.l-shell__head';
test.describe('Visual - Header @a11y', () => {
test.beforeEach(async ({ page }) => {
//Go to baseURL and Hide Tree
await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' });
});
test('header sizing', async ({ page, theme }) => {
// Click About button
await percySnapshot(page, `Header default (theme: '${theme}')`, {
scope: header
});
await page.getByLabel('Click to collapse items').click();
await percySnapshot(page, `Header Collapsed (theme: '${theme}')`, {
scope: header
});
});
});
test.afterEach(async ({ page }, testInfo) => {
await scanForA11yViolations(page, testInfo.title);
});

View File

@ -22,13 +22,13 @@
import percySnapshot from '@percy/playwright'; import percySnapshot from '@percy/playwright';
import { scanForA11yViolations, test } from '../../../avpFixtures.js';
import { MISSION_TIME, VISUAL_URL } from '../../../constants.js'; import { MISSION_TIME, VISUAL_URL } from '../../../constants.js';
import { test } from '../../../pluginFixtures.js';
//Declare the scope of the visual test //Declare the scope of the visual test
const inspectorPane = '.l-shell__pane-inspector'; const inspectorPane = '.l-shell__pane-inspector';
test.describe('Visual - Controlled Clock', () => { test.describe('Visual - Inspector @ally', () => {
test.beforeEach(async ({ page }) => { test.beforeEach(async ({ page }) => {
await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' }); await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' });
}); });
@ -55,3 +55,6 @@ test.describe('Visual - Controlled Clock', () => {
}); });
}); });
}); });
test.afterEach(async ({ page }, testInfo) => {
await scanForA11yViolations(page, testInfo.title);
});

View File

@ -89,6 +89,9 @@ test.describe('Visual - Tree Pane', () => {
await expandTreePaneItemByName(page, bar.name); await expandTreePaneItemByName(page, bar.name);
await expandTreePaneItemByName(page, baz.name); await expandTreePaneItemByName(page, baz.name);
// eslint-disable-next-line playwright/no-wait-for-timeout
await page.waitForTimeout(1 * 1000); //https://github.com/nasa/openmct/issues/7059
await percySnapshot(page, `Tree Pane w/ multiple levels expanded (theme: ${theme})`, { await percySnapshot(page, `Tree Pane w/ multiple levels expanded (theme: ${theme})`, {
scope: treePane scope: treePane
}); });

View File

@ -44,14 +44,15 @@ test.describe('Visual - Controlled Clock', () => {
test('Overlay Plot Loading Indicator @localStorage', async ({ page, theme }) => { test('Overlay Plot Loading Indicator @localStorage', async ({ page, theme }) => {
await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' }); await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' });
await page.locator('a').filter({ hasText: 'Overlay Plot with 5s Delay' }).click(); await page
.getByRole('gridcell', { hasText: 'Overlay Plot with 5s Delay Overlay Plot' })
.click();
//Ensure that we're on the Unnamed Overlay Plot object //Ensure that we're on the Unnamed Overlay Plot object
await expect(page.locator('.l-browse-bar__object-name')).toContainText( await expect(page.getByRole('main')).toContainText('Overlay Plot with 5s Delay');
'Overlay Plot with 5s Delay'
);
//Wait for canvas to be rendered and stop animating, but plot should not be loaded. Cannot use waitForPlotsToRender //Wait for canvas to be rendered and stop animating, but plot should not be loaded.
await page.locator('canvas >> nth=1').hover({ trial: true }); //Cannot use waitForPlotsToRender due to clockOptions.
await page.locator('#webglContext').hover({ trial: true });
//Take snapshot of Sine Wave Generator within Overlay Plot //Take snapshot of Sine Wave Generator within Overlay Plot
await percySnapshot(page, `SineWaveInOverlayPlot (theme: '${theme}')`); await percySnapshot(page, `SineWaveInOverlayPlot (theme: '${theme}')`);

View File

@ -39,7 +39,7 @@ test.describe('Visual - Default @a11y', () => {
test('Visual - Default Dashboard', async ({ page, theme }) => { test('Visual - Default Dashboard', async ({ page, theme }) => {
// Verify that Create button is actionable // Verify that Create button is actionable
await expect(page.locator('button:has-text("Create")')).toBeEnabled(); await expect(page.getByRole('button', { name: 'Create' })).toBeEnabled();
// Take a snapshot of the Dashboard // Take a snapshot of the Dashboard
await percySnapshot(page, `Default Dashboard (theme: '${theme}')`); await percySnapshot(page, `Default Dashboard (theme: '${theme}')`);
@ -66,27 +66,22 @@ test.describe('Visual - Default @a11y', () => {
}); });
test('Visual - Sine Wave Generator Form', async ({ page, theme }) => { test('Visual - Sine Wave Generator Form', async ({ page, theme }) => {
//Click the Create button await page.getByRole('button', { name: 'Create' }).click();
await page.click('button:has-text("Create")');
// Click text=Sine Wave Generator await page.getByRole('menuItem', { name: 'Sine Wave Generator' }).click();
await page.click('text=Sine Wave Generator');
await percySnapshot(page, `Default Sine Wave Generator Form (theme: '${theme}')`); await percySnapshot(page, `Default Sine Wave Generator Form (theme: '${theme}')`);
await page.locator('.field.control.l-input-sm input').first().click(); await page.getByLabel('Period').click();
await page.locator('.field.control.l-input-sm input').first().fill(''); await page.getByLabel('Period').fill('');
// Validate red x mark
await percySnapshot(page, `removed amplitude property value (theme: '${theme}')`); await percySnapshot(page, `removed amplitude property value (theme: '${theme}')`);
}); });
test('Visual - Display Layout Icon is correct in Create Menu', async ({ page, theme }) => { test('Visual - Display Layout Icon is correct in Create Menu', async ({ page, theme }) => {
// Click the Create button await page.getByRole('button', { name: 'Create' }).click();
await page.click('button:has-text("Create")');
// Hover on Display Layout option. await page.getByRole('menuItem', { name: 'Display Layout' }).hover({ trial: true });
await page.locator('text=Display Layout').hover();
await percySnapshot(page, `Display Layout Create Menu (theme: '${theme}')`); await percySnapshot(page, `Display Layout Create Menu (theme: '${theme}')`);
}); });

View File

@ -20,138 +20,77 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
/**
* Defines playwright locators that can be used in tests.
* @typedef {Object} LayoutLocators
* @property {Object<string, import('@playwright/test').Locator>} LayoutLocator
*/
import percySnapshot from '@percy/playwright'; import percySnapshot from '@percy/playwright';
import { createDomainObjectWithDefaults } from '../../appActions.js'; import { createDomainObjectWithDefaults } from '../../appActions.js';
import { VISUAL_URL } from '../../constants.js'; import { VISUAL_URL } from '../../constants.js';
import { test } from '../../pluginFixtures.js'; import { test } from '../../pluginFixtures.js';
const snapshotScope = '.l-shell__pane-main .l-pane__contents';
test.describe('Visual - Display Layout', () => { test.describe('Visual - Display Layout', () => {
test('Resize Marquee surrounds selection', async ({ page, theme }) => { test.beforeEach(async ({ page, theme }) => {
const baseline = await setupBaseline(page); await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' });
const { child1LayoutLocator, child1LayoutObjectLocator } = baseline;
await percySnapshot(page, `Resize nested layout selected (theme: '${theme}')`, { const parentLayout = await createDomainObjectWithDefaults(page, {
scope: snapshotScope type: 'Display Layout',
name: 'Parent Layout'
});
const child2Layout = await createDomainObjectWithDefaults(page, {
type: 'Display Layout',
name: 'Child Left Layout',
parent: parentLayout.uuid
});
//Create this layout second so that it is on top for the position change
const child1Layout = await createDomainObjectWithDefaults(page, {
type: 'Display Layout',
name: 'Child Right Layout',
parent: parentLayout.uuid
});
await createDomainObjectWithDefaults(page, {
type: 'Sine Wave Generator',
name: 'SWG 1',
parent: child1Layout.uuid
});
await createDomainObjectWithDefaults(page, {
type: 'Sine Wave Generator',
name: 'SWG 2',
parent: child2Layout.uuid
}); });
await child1LayoutLocator.click(); await page.goto(parentLayout.url, { waitUntil: 'domcontentloaded' });
await percySnapshot(page, `Resize new nested layout selected (theme: '${theme}')`, { await page.getByRole('button', { name: 'Edit Object' }).click();
scope: snapshotScope
});
await child1LayoutObjectLocator.click(); //Move the Child Right Layout to the Right. It should be on top of the Left Layout at this point.
await percySnapshot(page, `Resize Object in nested layout selected (theme: '${theme}')`, { await page
scope: snapshotScope .getByLabel('Child Right Layout Layout', { exact: true })
}); .getByLabel('Move Sub-object Frame')
.click();
await page.getByLabel('Move Sub-object Frame').nth(3).click(); //I'm not sure why this step is necessary
await page.getByLabel('X:').click();
await page.getByLabel('X:').fill('35');
}); });
test('Parent layout of selection displays grid', async ({ page, theme }) => { test('Resize Marquee surrounds selection', async ({ page, theme }) => {
const baseline = await setupBaseline(page); //This is where the beforeEach leaves off.
const { parentLayoutLocator, child1LayoutObjectLocator } = baseline; await percySnapshot(page, `Last modified object selected (theme: '${theme}')`);
await percySnapshot(page, `Parent nested layout selected (theme: '${theme}')`, { await page.getByLabel('Child Left Layout Layout', { exact: true }).click();
scope: snapshotScope await percySnapshot(page, `Only Left Child Layout has Marque selection (theme: '${theme}')`);
});
await parentLayoutLocator.click(); await page.getByLabel('Child Right Layout Layout', { exact: true }).click();
await percySnapshot(page, `Parent outer layout selected (theme: '${theme}')`, { await percySnapshot(page, `Only Right Child Layout has Marque selection (theme: '${theme}')`);
scope: snapshotScope
});
await child1LayoutObjectLocator.click(); //Only the sub-object in the Right Layout should be highlighted with a marquee
await percySnapshot(page, `Parent Object in nested layout selected (theme: '${theme}')`, { await page
scope: snapshotScope .getByLabel('Child Right Layout Layout', { exact: true })
}); .getByLabel('Move Sub-object Frame')
.click();
await percySnapshot(
page,
`Selecting a sub-object from Right Layout selected (theme: '${theme}')`
);
await page.getByLabel('Parent Layout Layout', { exact: true }).click();
await percySnapshot(page, `Parent outer layout selected (theme: '${theme}')`);
}); });
}); });
/**
* Sets up a complex layout with nested layouts and provides the playwright locators
* @param {import('@playwright/test').Page} page
* @returns {LayoutLocators} locators of baseline complex display to be used in tests
*/
async function setupBaseline(page) {
// Load Open MCT visual test baseline
await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' });
// Open Tree
await page.getByRole('button', { name: 'Browse' }).click();
const treePane = page.getByRole('tree', {
name: 'Main Tree'
});
const objectViewLocator = page.locator('.c-object-view');
const parentLayoutLocator = objectViewLocator.first();
const child1LayoutLocator = parentLayoutLocator.locator(objectViewLocator).first();
const child1LayoutObjectLocator = child1LayoutLocator.locator('.l-layout__frame');
const parentLayout = await createDomainObjectWithDefaults(page, {
type: 'Display Layout',
name: 'Parent Layout'
});
const child1Layout = await createDomainObjectWithDefaults(page, {
type: 'Display Layout',
name: 'Child 1 Layout'
});
const child2Layout = await createDomainObjectWithDefaults(page, {
type: 'Display Layout',
name: 'Child 2 Layout'
});
const swg1 = await createDomainObjectWithDefaults(page, {
type: 'Sine Wave Generator',
name: 'SWG 1'
});
const swg2 = await createDomainObjectWithDefaults(page, {
type: 'Sine Wave Generator',
name: 'SWG 2'
});
const child1LayoutTreeItem = treePane.getByRole('treeitem', {
name: new RegExp(child1Layout.name)
});
const child2LayoutTreeItem = treePane.getByRole('treeitem', {
name: new RegExp(child2Layout.name)
});
const swg1TreeItem = treePane.getByRole('treeitem', {
name: new RegExp(swg1.name)
});
const swg2TreeItem = treePane.getByRole('treeitem', {
name: new RegExp(swg2.name)
});
// Expand folder containing created objects
await page.goto(parentLayout.url);
await page.getByTitle('Show selected item in tree').click();
// Add swg1 to child1Layout
await page.goto(child1Layout.url);
await page.getByRole('button', { name: 'Edit' }).click();
await swg1TreeItem.dragTo(parentLayoutLocator, { targetPosition: { x: 0, y: 0 } });
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
// Add swg1 to child1Layout
await page.goto(child2Layout.url);
await page.getByRole('button', { name: 'Edit' }).click();
await swg2TreeItem.dragTo(parentLayoutLocator, { targetPosition: { x: 0, y: 0 } });
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
// Add child1Layout and child2Layout to parentLayout
await page.goto(parentLayout.url);
await page.getByRole('button', { name: 'Edit' }).click();
await child1LayoutTreeItem.dragTo(parentLayoutLocator, { targetPosition: { x: 350, y: 0 } });
await child2LayoutTreeItem.dragTo(parentLayoutLocator, { targetPosition: { x: 0, y: 0 } });
return {
parentLayoutLocator,
child1LayoutLocator,
child1LayoutObjectLocator
};
}

View File

@ -27,7 +27,6 @@ import { VISUAL_URL } from '../../constants.js';
import { expect, test } from '../../pluginFixtures.js'; import { expect, test } from '../../pluginFixtures.js';
test.describe('Visual - LAD Table', () => { test.describe('Visual - LAD Table', () => {
/** @type {import('@playwright/test').Locator} */
let ladTable; let ladTable;
test.beforeEach(async ({ page }) => { test.beforeEach(async ({ page }) => {
@ -46,9 +45,9 @@ test.describe('Visual - LAD Table', () => {
}); });
//Modify SWG to create a really stable SWG //Modify SWG to create a really stable SWG
await page.locator('button[title="More actions"]').click(); await page.getByRole('button', { name: 'More actions' }).click();
await page.getByRole('menuitem', { name: 'Edit Properties...' }).click(); await page.getByRole('menuitem', { name: 'Edit Properties...' }).click();
//Forgive me, padre //Forgive me, padre
await page.getByRole('spinbutton', { name: 'Data Rate (hz)' }).fill('0'); await page.getByRole('spinbutton', { name: 'Data Rate (hz)' }).fill('0');
@ -57,18 +56,18 @@ test.describe('Visual - LAD Table', () => {
await page.getByRole('button', { name: 'Save' }).click(); await page.getByRole('button', { name: 'Save' }).click();
}); });
test('Toggled column widths behave accordingly', async ({ page, theme }) => { test('Toggled column widths behave accordingly', async ({ page, theme }) => {
await page.goto(ladTable.url); await page.goto(ladTable.url, { waitUntil: 'domcontentloaded' });
await expect(page.locator('button[title="Expand Columns"]')).toBeVisible(); await expect(page.getByLabel('Expand Columns')).toBeVisible();
await percySnapshot( await percySnapshot(
page, page,
`LAD Table w/ Sine Wave Generator columns autosized (theme: ${theme})` `LAD Table w/ Sine Wave Generator columns autosized (theme: ${theme})`
); );
await page.locator('button[title="Expand Columns"]').click(); await page.getByLabel('Expand Columns').click();
await expect(page.locator('button[title="Autosize Columns"]')).toBeVisible(); await expect(page.getByRole('button', { name: 'Autosize Columns' })).toBeVisible();
await percySnapshot( await percySnapshot(
page, page,

View File

@ -26,43 +26,54 @@
import percySnapshot from '@percy/playwright'; import percySnapshot from '@percy/playwright';
import { createDomainObjectWithDefaults } from '../../appActions.js'; import { createNotification } from '../../appActions.js';
import { expect, scanForA11yViolations, test } from '../../avpFixtures.js'; import { expect, scanForA11yViolations, test } from '../../avpFixtures.js';
import { VISUAL_URL } from '../../constants.js'; import { VISUAL_URL } from '../../constants.js';
test.describe("Visual - Check Notification Info Banner of 'Save successful' @a11y", () => { test.describe('Visual - Notifications @a11y', () => {
test.beforeEach(async ({ page }) => { test.beforeEach(async ({ page }) => {
await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' }); await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' });
}); });
test("Create a clock, click on 'Save successful' banner and dismiss it", async ({ test('Alert Levels and Notification List Modal', async ({ page, theme }) => {
page, await createNotification(page, {
theme message: 'Test info notification',
}) => { severity: 'info'
// Create a clock domain object
await createDomainObjectWithDefaults(page, {
type: 'Clock',
name: 'Default Clock'
}); });
// Click on the div with role="alert" that has "Save successful" text await expect(page.getByText('Test info notification')).toBeVisible();
await page.getByRole('alert').filter({ hasText: 'Save successful' }).click(); await percySnapshot(page, `Info Notification banner shown (theme: '${theme}')`);
// Verify there is a div with role="dialog" await page.getByLabel('Dismiss').click();
await expect(page.getByRole('dialog', { name: 'Overlay' })).toBeVisible(); await page.getByRole('alert').waitFor({ state: 'detached' });
// Verify the div with role="dialog" contains text "Save successful" await createNotification(page, {
expect(await page.getByRole('dialog', { name: 'Overlay' }).innerText()).toContain( message: 'Test alert notification',
'Save successful' severity: 'alert'
); });
await percySnapshot(page, `Notification banner shows Save successful (theme: '${theme}')`); await expect(page.getByText('Test alert notification')).toBeVisible();
// Verify there is a button with text "Dismiss" await percySnapshot(page, `Alert Notification banner shown (theme: '${theme}')`);
await expect(page.getByText('Dismiss', { exact: true })).toBeVisible(); await page.getByLabel('Dismiss').click();
await percySnapshot(page, `Notification banner shows Dismiss (theme: '${theme}')`); await page.getByRole('alert').waitFor({ state: 'detached' });
// Click on button with text "Dismiss" await createNotification(page, {
await page.getByText('Dismiss', { exact: true }).click(); message: 'Test error notification',
// Verify there is no div with role="dialog" severity: 'error'
await expect(page.getByRole('dialog', { name: 'Overlay' })).toBeHidden(); });
await percySnapshot(page, `Notification banner dismissed (theme: '${theme}')`); await expect(page.getByText('Test error notification')).toBeVisible();
}); await percySnapshot(page, `Error Notification banner shown (theme: '${theme}')`);
test.afterEach(async ({ page }, testInfo) => { await page.getByLabel('Dismiss').click();
await scanForA11yViolations(page, testInfo.title); await page.getByRole('alert').waitFor({ state: 'detached' });
await page.getByLabel('Review 2 Notifications').click();
await page.getByText('Test alert notification').waitFor();
await percySnapshot(page, `Notification List Modal with alert and error (theme: '${theme}')`);
// Skipping due to https://github.com/nasa/openmct/issues/6820
// await page.getByLabel('Dismiss notification of Test alert notification').click();
// await page.getByText('Test alert notification').waitFor({ state: 'detached' });
// await percySnapshot(page, `Notification Modal with error only (theme: '${theme}')`);
await page.getByRole('button', { name: 'Clear All Notifications', exact: true }).click();
await percySnapshot(page, `Notification Modal after Clear All (theme: '${theme}')`);
}); });
}); });
test.afterEach(async ({ page }, testInfo) => {
await scanForA11yViolations(page, testInfo.title);
});

View File

@ -64,7 +64,7 @@ test.describe('Grand Search @a11y', () => {
await percySnapshot(page, `Searching for Object (theme: '${theme}')`); await percySnapshot(page, `Searching for Object (theme: '${theme}')`);
// Enter Edit mode on the Display Layout // Enter Edit mode on the Display Layout
await page.getByRole('button', { name: 'Edit' }).click(); await page.getByRole('button', { name: 'Edit Object' }).click();
// Navigate to the object while in edit mode on the display layout // Navigate to the object while in edit mode on the display layout
await page.getByRole('searchbox', { name: 'Search Input' }).click(); await page.getByRole('searchbox', { name: 'Search Input' }).click();

View File

@ -68,7 +68,7 @@ test.describe('Flexible Layout styling @a11y', () => {
await page.goto(flexibleLayout.url, { waitUntil: 'domcontentloaded' }); await page.goto(flexibleLayout.url, { waitUntil: 'domcontentloaded' });
// Edit Flexible Layout // Edit Flexible Layout
await page.getByLabel('Edit').click(); await page.getByLabel('Edit Object').click();
// Select styles tab // Select styles tab
await page.getByRole('tab', { name: 'Styles' }).click(); await page.getByRole('tab', { name: 'Styles' }).click();
@ -146,7 +146,7 @@ test.describe('Stacked Plot styling @a11y', () => {
await page.goto(stackedPlot.url, { waitUntil: 'domcontentloaded' }); await page.goto(stackedPlot.url, { waitUntil: 'domcontentloaded' });
// Edit Flexible Layout // Edit Flexible Layout
await page.getByLabel('Edit').click(); await page.getByLabel('Edit Object').click();
// Select styles tab // Select styles tab
await page.getByRole('tab', { name: 'Styles' }).click(); await page.getByRole('tab', { name: 'Styles' }).click();

View File

@ -0,0 +1,78 @@
/*****************************************************************************
* 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 percySnapshot from '@percy/playwright';
import { createDomainObjectWithDefaults } from '../../appActions.js';
import { VISUAL_URL } from '../../constants.js';
import { expect, test } from '../../pluginFixtures.js';
test.describe('Visual - Telemetry Views', () => {
let telemetry;
test.beforeEach(async ({ page }) => {
await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' });
// Create SWG inside of LAD Table
telemetry = await createDomainObjectWithDefaults(page, {
type: 'Sine Wave Generator',
name: 'SWG4'
});
//Modify SWG to create a really stable SWG
await page.getByRole('button', { name: 'More actions' }).click();
await page.getByRole('menuitem', { name: 'Edit Properties...' }).click();
//Forgive me, padre
await page.getByRole('spinbutton', { name: 'Data Rate (hz)' }).fill('0');
await page.getByRole('spinbutton', { name: 'Period' }).fill('0');
await page.getByRole('button', { name: 'Save' }).click();
});
test('Telemetry Table toggled column widths behave accordingly', async ({ page, theme }) => {
await page.goto(telemetry.url, { waitUntil: 'domcontentloaded' });
//Click this button to see telemetry display options
await page.getByRole('button', { name: 'Plot' }).click();
await page.getByLabel('Telemetry Table').click();
//Get Table View in place
expect(await page.getByLabel('Expand Columns')).toBeInViewport();
await percySnapshot(page, `Default Telemetry Table View (theme: ${theme})`);
await page.getByLabel('Expand Columns').click();
await expect(page.getByRole('button', { name: 'Autosize Columns' })).toBeVisible();
await percySnapshot(page, `Default Telemetry Table columns expanded (theme: ${theme})`);
await page.getByLabel('More actions').click();
await percySnapshot(page, `Telemetry View Actions Menu expanded (theme: ${theme})`);
await page.getByRole('menuitem', { name: 'Pause' }).click();
await percySnapshot(page, `Telemetry View Paused (theme: ${theme})`);
});
});

View File

@ -8,7 +8,6 @@
"@axe-core/playwright": "4.8.2", "@axe-core/playwright": "4.8.2",
"@babel/eslint-parser": "7.23.3", "@babel/eslint-parser": "7.23.3",
"@braintree/sanitize-url": "6.0.4", "@braintree/sanitize-url": "6.0.4",
"@deploysentinel/playwright": "0.3.4",
"@percy/cli": "1.27.4", "@percy/cli": "1.27.4",
"@percy/playwright": "1.0.4", "@percy/playwright": "1.0.4",
"@playwright/test": "1.39.0", "@playwright/test": "1.39.0",

View File

@ -29,6 +29,7 @@
:key="action.name" :key="action.name"
role="menuitem" role="menuitem"
:class="[action.cssClass, action.isDisabled ? 'disabled' : '']" :class="[action.cssClass, action.isDisabled ? 'disabled' : '']"
:aria-label="action.name"
:title="action.description" :title="action.description"
@click="action.onItemClicked" @click="action.onItemClicked"
> >
@ -51,6 +52,7 @@
:key="action.name" :key="action.name"
role="menuitem" role="menuitem"
:class="[action.cssClass, action.isDisabled ? 'disabled' : '']" :class="[action.cssClass, action.isDisabled ? 'disabled' : '']"
:aria-label="action.name"
:title="action.description" :title="action.description"
@click="action.onItemClicked" @click="action.onItemClicked"
> >

View File

@ -62,6 +62,7 @@
:key="action.name" :key="action.name"
role="menuitem" role="menuitem"
:class="action.cssClass" :class="action.cssClass"
:aria-label="action.name"
:title="action.description" :title="action.description"
@click="action.onItemClicked" @click="action.onItemClicked"
@mouseover="toggleItemDescription(action)" @mouseover="toggleItemDescription(action)"

View File

@ -37,12 +37,13 @@
<ul v-for="column in columns" class="l-autoflow-col" :style="{ width: width + 'px' }"> <ul v-for="column in columns" class="l-autoflow-col" :style="{ width: width + 'px' }">
<li v-for="row in column" class="l-autoflow-row"> <li v-for="row in column" class="l-autoflow-row">
<span <span
:aria-label="row.value"
:title="row.value" :title="row.value"
:data-value="row.value" :data-value="row.value"
:class="'l-autoflow-item r l-obj-val-format ' + row.classes" :class="'l-autoflow-item r l-obj-val-format ' + row.classes"
>{{row.value}}</span >{{row.value}}</span
> >
<span :title="row.name" class="l-autoflow-item l">{{row.name}}</span> <span :aria-label="row.name" :title="row.name" class="l-autoflow-item l">{{row.name}}</span>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -41,7 +41,11 @@
@mouseover.ctrl="showToolTip" @mouseover.ctrl="showToolTip"
@mouseleave="hideToolTip" @mouseleave="hideToolTip"
> >
<div class="is-status__indicator" :title="`This item is ${status}`"></div> <div
class="is-status__indicator"
:aria-label="`This item is ${status}`"
:title="`This item is ${status}`"
></div>
<div v-if="showLabel" class="c-telemetry-view__label"> <div v-if="showLabel" class="c-telemetry-view__label">
<div class="c-telemetry-view__label-text"> <div class="c-telemetry-view__label-text">
{{ domainObject.name }} {{ domainObject.name }}
@ -50,6 +54,7 @@
<div <div
v-if="showValue" v-if="showValue"
:aria-label="fieldName"
:title="fieldName" :title="fieldName"
class="c-telemetry-view__value" class="c-telemetry-view__value"
:class="[telemetryClass]" :class="[telemetryClass]"

View File

@ -28,6 +28,7 @@
<div class="c-fault-mgmt-item"> <div class="c-fault-mgmt-item">
<div <div
class="c-fault-mgmt__list-severity" class="c-fault-mgmt__list-severity"
:aria-label="fault.severity"
:title="fault.severity" :title="fault.severity"
:class="['is-severity-' + severity]" :class="['is-severity-' + severity]"
></div> ></div>

View File

@ -42,14 +42,22 @@
<div class="c-grid-item__details"> <div class="c-grid-item__details">
<!-- Name and metadata --> <!-- Name and metadata -->
<div class="c-grid-item__name" :title="item.model.name">{{ item.model.name }}</div> <div class="c-grid-item__name" :title="item.model.name">{{ item.model.name }}</div>
<div class="c-grid-item__metadata" :title="item.type.name"> <div class="c-grid-item__metadata" :aria-label="item.type.name" :title="item.type.name">
<span class="c-grid-item__metadata__type">{{ item.type.name }}</span> <span class="c-grid-item__metadata__type">{{ item.type.name }}</span>
</div> </div>
</div> </div>
<div class="c-grid-item__controls"> <div class="c-grid-item__controls">
<div class="is-status__indicator" :title="`This item is ${status}`"></div> <div
class="is-status__indicator"
:aria-label="`This item is ${status}`"
:title="`This item is ${status}`"
></div>
<div class="icon-people" title="Shared"></div> <div class="icon-people" title="Shared"></div>
<button class="c-icon-button icon-info c-info-button" title="More Info"></button> <button
class="c-icon-button icon-info c-info-button"
aria-label="More Info"
title="More Info"
></button>
<div class="icon-pointer-right c-pointer-icon"></div> <div class="icon-pointer-right c-pointer-icon"></div>
</div> </div>
</a> </a>

View File

@ -20,12 +20,13 @@
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<template> <template>
<div class="l-grid-view"> <div class="l-grid-view" role="grid">
<grid-item <grid-item
v-for="(item, index) in items" v-for="(item, index) in items"
:key="index" :key="index"
:item="item" :item="item"
:object-path="item.objectPath" :object-path="item.objectPath"
role="gridcell"
/> />
</div> </div>
</template> </template>

View File

@ -34,7 +34,11 @@
class="c-object-label__type-icon c-list-item__name__type-icon" class="c-object-label__type-icon c-list-item__name__type-icon"
:class="item.type.cssClass" :class="item.type.cssClass"
> >
<span class="is-status__indicator" :title="`This item is ${status}`"></span> <span
class="is-status__indicator"
:aria-label="`This item is ${status}`"
:title="`This item is ${status}`"
></span>
</div> </div>
<div class="c-object-label__name c-list-item__name__name">{{ item.model.name }}</div> <div class="c-object-label__name c-list-item__name__name">{{ item.model.name }}</div>
</a> </a>

View File

@ -24,6 +24,7 @@
ref="gaugeWrapper" ref="gaugeWrapper"
class="c-gauge__wrapper js-gauge-wrapper" class="c-gauge__wrapper js-gauge-wrapper"
:class="gaugeClasses" :class="gaugeClasses"
:aria-label="gaugeTitle"
:title="gaugeTitle" :title="gaugeTitle"
> >
<template v-if="typeDial"> <template v-if="typeDial">

View File

@ -26,11 +26,20 @@
role="toolbar" role="toolbar"
aria-label="Image controls" aria-label="Image controls"
> >
<imagery-view-menu-switcher :icon-class="'icon-brightness'" :title="'Brightness and contrast'"> <imagery-view-menu-switcher
:icon-class="'icon-brightness'"
:aria-label="'Brightness and contrast'"
:title="'Brightness and contrast'"
>
<filter-settings @filter-changed="updateFilterValues" /> <filter-settings @filter-changed="updateFilterValues" />
</imagery-view-menu-switcher> </imagery-view-menu-switcher>
<imagery-view-menu-switcher v-if="layers.length" :icon-class="'icon-layers'" :title="'Layers'"> <imagery-view-menu-switcher
v-if="layers.length"
icon-class="icon-layers"
aria-label="Layers"
title="Layers"
>
<layer-settings :layers="layers" @toggle-layer-visibility="toggleLayerVisibility" /> <layer-settings :layers="layers" @toggle-layer-visibility="toggleLayerVisibility" />
</imagery-view-menu-switcher> </imagery-view-menu-switcher>
@ -47,6 +56,7 @@
<imagery-view-menu-switcher <imagery-view-menu-switcher
class="--show-if-less-than-220" class="--show-if-less-than-220"
:icon-class="'icon-magnify'" :icon-class="'icon-magnify'"
:aria-label="'Zoom settings'"
:title="'Zoom settings'" :title="'Zoom settings'"
> >
<zoom-settings <zoom-settings

View File

@ -28,6 +28,7 @@
selected: selected, selected: selected,
'real-time': realTime 'real-time': realTime
}" }"
:aria-label="image.formattedTime"
:title="image.formattedTime" :title="image.formattedTime"
@click="handleClick" @click="handleClick"
> >

View File

@ -25,6 +25,7 @@
:id="id" :id="id"
class="c-button c-button--menu c-switcher-menu__button" class="c-button c-button--menu c-switcher-menu__button"
:class="iconClass" :class="iconClass"
:aria-label="title"
:title="title" :title="title"
@click="toggleMenu" @click="toggleMenu"
/> />

View File

@ -23,7 +23,12 @@
<template> <template>
<div> <div>
<div class="c-style c-style--saved has-local-controls c-toolbar"> <div class="c-style c-style--saved has-local-controls c-toolbar">
<div class="c-style__controls" :title="description" @click="selectStyle()"> <div
class="c-style__controls"
:aria-label="description"
:title="description"
@click="selectStyle()"
>
<div class="c-style-thumb" :style="thumbStyle"> <div class="c-style-thumb" :style="thumbStyle">
<span <span
class="c-style-thumb__text u-style-receiver js-style-receiver" class="c-style-thumb__text u-style-receiver js-style-receiver"

View File

@ -23,6 +23,7 @@
<div class="c-menu-button c-ctrl-wrapper c-ctrl-wrapper--menus-left"> <div class="c-menu-button c-ctrl-wrapper c-ctrl-wrapper--menus-left">
<button <button
class="c-icon-button c-button--menu icon-camera" class="c-icon-button c-button--menu icon-camera"
aria-label="Take a Notebook Snapshot"
title="Take a Notebook Snapshot" title="Take a Notebook Snapshot"
@click.stop.prevent="showMenu" @click.stop.prevent="showMenu"
> >

View File

@ -37,6 +37,7 @@
<div <div
v-for="entry in statusCountViewModel" v-for="entry in statusCountViewModel"
:key="entry.status.key" :key="entry.status.key"
:aria-label="entry.status.label"
:title="entry.status.label" :title="entry.status.label"
class="c-status-poll-report__count" class="c-status-poll-report__count"
:style="[ :style="[

View File

@ -30,6 +30,7 @@
:style="{ :style="{
left: (100 * (tick.value - min)) / interval + '%' left: (100 * (tick.value - min)) / interval + '%'
}" }"
:aria-label="tick.fullText || tick.text"
:title="tick.fullText || tick.text" :title="tick.fullText || tick.text"
> >
{{ tick.text }} {{ tick.text }}
@ -41,6 +42,7 @@
:key="'tick-top' + i" :key="'tick-top' + i"
class="gl-plot-tick gl-plot-y-tick-label" class="gl-plot-tick gl-plot-y-tick-label"
:style="{ top: (100 * (max - tick.value)) / interval + '%' }" :style="{ top: (100 * (max - tick.value)) / interval + '%' }"
:aria-label="tick.fullText || tick.text"
:title="tick.fullText || tick.text" :title="tick.fullText || tick.text"
style="margin-top: -0.5em; direction: ltr" style="margin-top: -0.5em; direction: ltr"
> >

View File

@ -22,9 +22,9 @@
<template> <template>
<div ref="chart" class="gl-plot-chart-area"> <div ref="chart" class="gl-plot-chart-area">
<canvas :style="canvasStyle" class="js-overlay-canvas"></canvas> <canvas id="2dContext" :style="canvasStyle" class="js-overlay-canvas" role="img"></canvas>
<canvas :style="canvasStyle" class="js-main-canvas"></canvas> <canvas id="webglContext" :style="canvasStyle" class="js-main-canvas" role="img"></canvas>
<div ref="limitArea" class="js-limit-area"> <div ref="limitArea" class="js-limit-area" aria-hidden="true">
<limit-label <limit-label
v-for="(limitLabel, index) in visibleLimitLabels" v-for="(limitLabel, index) in visibleLimitLabels"
:key="index" :key="index"

View File

@ -54,7 +54,11 @@
:class="[tab.status ? `is-status--${tab.status}` : '']" :class="[tab.status ? `is-status--${tab.status}` : '']"
> >
<div class="c-object-label__type-icon" :class="tab.type.definition.cssClass"> <div class="c-object-label__type-icon" :class="tab.type.definition.cssClass">
<span class="is-status__indicator" :title="`This item is ${tab.status}`"></span> <span
class="is-status__indicator"
:aria-label="`This item is ${tab.status}`"
:title="`This item is ${tab.status}`"
></span>
</div> </div>
<span class="c-button__label c-object-label__name">{{ tab.domainObject.name }}</span> <span class="c-button__label c-object-label__name">{{ tab.domainObject.name }}</span>
</div> </div>

View File

@ -22,6 +22,7 @@
<template> <template>
<td <td
ref="tableCell" ref="tableCell"
:aria-label="formattedValue"
:title="formattedValue" :title="formattedValue"
@click="selectCell($event.currentTarget, columnKey)" @click="selectCell($event.currentTarget, columnKey)"
@mouseover.ctrl="showToolTip" @mouseover.ctrl="showToolTip"

View File

@ -21,11 +21,12 @@
--> -->
<template> <template>
<div ref="root" class="c-table-wrapper" :class="tableClasses"> <div ref="root" class="c-table-wrapper" :class="tableClasses">
<div v-if="enableLegacyToolbar" class="c-table-control-bar c-control-bar"> <div v-if="enableLegacyToolbar" class="c-table-control-bar c-control-bar" role="menubar">
<button <button
v-if="allowExport" v-if="allowExport"
v-show="!markedRows.length" v-show="!markedRows.length"
class="c-button icon-download labeled" class="c-button icon-download labeled"
aria-label="Export this view's data"
title="Export this view's data" title="Export this view's data"
@click="exportAllDataAsCSV()" @click="exportAllDataAsCSV()"
> >
@ -35,6 +36,7 @@
v-if="allowExport" v-if="allowExport"
v-show="markedRows.length" v-show="markedRows.length"
class="c-button icon-download labeled" class="c-button icon-download labeled"
aria-label="Export marked rows as CSV"
title="Export marked rows as CSV" title="Export marked rows as CSV"
@click="exportMarkedDataAsCSV()" @click="exportMarkedDataAsCSV()"
> >
@ -43,6 +45,7 @@
<button <button
v-show="markedRows.length" v-show="markedRows.length"
class="c-button icon-x labeled" class="c-button icon-x labeled"
aria-label="Unmark all rows"
title="Unmark all rows" title="Unmark all rows"
@click="unmarkAllRows()" @click="unmarkAllRows()"
> >
@ -51,6 +54,7 @@
<div v-if="marking.enable" class="c-separator"></div> <div v-if="marking.enable" class="c-separator"></div>
<button <button
v-if="marking.enable" v-if="marking.enable"
:aria-label="paused ? 'Continue real-time data flow' : 'Pause real-time data flow'"
class="c-button icon-pause pause-play labeled" class="c-button icon-pause pause-play labeled"
:class="paused ? 'icon-play is-paused' : 'icon-pause'" :class="paused ? 'icon-play is-paused' : 'icon-pause'"
:title="paused ? 'Continue real-time data flow' : 'Pause real-time data flow'" :title="paused ? 'Continue real-time data flow' : 'Pause real-time data flow'"
@ -62,10 +66,11 @@
</button> </button>
<template v-if="!isEditing"> <template v-if="!isEditing">
<div class="c-separator"></div> <div class="c-separator" role="separator"></div>
<button <button
v-if="isAutosizeEnabled" v-if="isAutosizeEnabled"
class="c-button icon-arrows-right-left labeled" class="c-button icon-arrows-right-left labeled"
aria-label="Increase column widths to fit currently available data."
title="Increase column widths to fit currently available data." title="Increase column widths to fit currently available data."
@click="recalculateColumnWidths" @click="recalculateColumnWidths"
> >
@ -73,6 +78,7 @@
</button> </button>
<button <button
v-else v-else
aria-label="Automatically size columns to fit the table into the available space."
class="c-button icon-expand labeled" class="c-button icon-expand labeled"
title="Automatically size columns to fit the table into the available space." title="Automatically size columns to fit the table into the available space."
@click="autosizeColumns" @click="autosizeColumns"
@ -104,6 +110,7 @@
<button <button
:class="{ 'hide-nice': !markedRows.length }" :class="{ 'hide-nice': !markedRows.length }"
class="c-icon-button icon-x labeled" class="c-icon-button icon-x labeled"
aria-label="Deselect All"
title="Deselect All" title="Deselect All"
@click="unmarkAllRows()" @click="unmarkAllRows()"
> >
@ -191,6 +198,7 @@
<button <button
class="c-search__use-regex" class="c-search__use-regex"
:class="{ 'is-active': enableRegexSearch[key] }" :class="{ 'is-active': enableRegexSearch[key] }"
aria-label="Click to enable regex: enter a string with slashes, like this: /regex_exp/"
title="Click to enable regex: enter a string with slashes, like this: /regex_exp/" title="Click to enable regex: enter a string with slashes, like this: /regex_exp/"
@click="toggleRegex(key)" @click="toggleRegex(key)"
> >

View File

@ -25,6 +25,7 @@
v-if="filterNames.length > 0" v-if="filterNames.length > 0"
class="c-table-indicator__filter c-table-indicator__elem c-filter-indication" class="c-table-indicator__filter c-table-indicator__elem c-filter-indication"
:class="{ 'c-filter-indication--mixed': hasMixedFilters }" :class="{ 'c-filter-indication--mixed': hasMixedFilters }"
:aria-label="title"
:title="title" :title="title"
> >
<span class="c-filter-indication__mixed">{{ label }}</span> <span class="c-filter-indication__mixed">{{ label }}</span>
@ -35,6 +36,7 @@
<div class="c-table-indicator__counts"> <div class="c-table-indicator__counts">
<span <span
:aria-label="totalRows + ' rows visible after any filtering'"
:title="totalRows + ' rows visible after any filtering'" :title="totalRows + ' rows visible after any filtering'"
class="c-table-indicator__elem c-table-indicator__row-count" class="c-table-indicator__elem c-table-indicator__row-count"
> >
@ -44,6 +46,7 @@
<span <span
v-if="markedRows" v-if="markedRows"
class="c-table-indicator__elem c-table-indicator__marked-count" class="c-table-indicator__elem c-table-indicator__marked-count"
:aria-label="markedRows + ' rows selected'"
:title="markedRows + ' rows selected'" :title="markedRows + ' rows selected'"
> >
{{ markedRows }} Marked {{ markedRows }} Marked

View File

@ -31,6 +31,7 @@
<div <div
v-if="!compact" v-if="!compact"
class="c-compact-tc__setting-value icon-minus u-fade-truncate--lg --no-sep" class="c-compact-tc__setting-value icon-minus u-fade-truncate--lg --no-sep"
:aria-label="`Start offset: ${offsets.start}`"
:title="`Start offset: ${offsets.start}`" :title="`Start offset: ${offsets.start}`"
> >
{{ offsets.start }} {{ offsets.start }}
@ -40,12 +41,14 @@
v-if="!compact" v-if="!compact"
class="c-compact-tc__setting-value icon-plus u-fade-truncate--lg" class="c-compact-tc__setting-value icon-plus u-fade-truncate--lg"
:class="{ '--no-sep': compact }" :class="{ '--no-sep': compact }"
:aria-label="`End offset: ${offsets.end}`"
:title="`End offset: ${offsets.end}`" :title="`End offset: ${offsets.end}`"
> >
{{ offsets.end }} {{ offsets.end }}
</div> </div>
<div <div
class="c-compact-tc__setting-value icon-clock c-compact-tc__current-update u-fade-truncate--lg --no-sep" class="c-compact-tc__setting-value icon-clock c-compact-tc__current-update u-fade-truncate--lg --no-sep"
aria-label="Last update"
title="Last update" title="Last update"
> >
{{ formattedCurrentValue }} {{ formattedCurrentValue }}

View File

@ -37,6 +37,7 @@
<div <div
v-else v-else
class="c-compact-tc__setting-value__elem" class="c-compact-tc__setting-value__elem"
:aria-label="`Time system: ${selectedTimeSystem.name}`"
:title="`Time system: ${selectedTimeSystem.name}`" :title="`Time system: ${selectedTimeSystem.name}`"
> >
{{ selectedTimeSystem.name }} {{ selectedTimeSystem.name }}

View File

@ -22,7 +22,7 @@
<template> <template>
<div class="grid-row grid-row--pad-label-for-button"> <div class="grid-row grid-row--pad-label-for-button">
<template v-if="canEdit"> <template v-if="canEdit">
<div class="grid-cell label" :title="editTitle">{{ shortLabel }}</div> <div class="grid-cell label" :aria-label="editTitle" :title="editTitle">{{ shortLabel }}</div>
<div class="grid-cell value"> <div class="grid-cell value">
<div class="c-click-swatch c-click-swatch--menu" @click="toggleSwatch()"> <div class="c-click-swatch c-click-swatch--menu" @click="toggleSwatch()">
<span class="c-color-swatch" :style="{ background: currentColor }"> </span> <span class="c-color-swatch" :style="{ background: currentColor }"> </span>
@ -44,7 +44,7 @@
</div> </div>
</template> </template>
<template v-else> <template v-else>
<div class="grid-cell label" :title="viewTitle">{{ shortLabel }}</div> <div class="grid-cell label" :aria-label="viewTitle" :title="viewTitle">{{ shortLabel }}</div>
<div class="grid-cell value"> <div class="grid-cell value">
<span <span
class="c-color-swatch" class="c-color-swatch"

View File

@ -26,6 +26,7 @@
:key="itemValue.key" :key="itemValue.key"
class="c-list-item__value js-list-item__value" class="c-list-item__value js-list-item__value"
:class="['--' + itemValue.key]" :class="['--' + itemValue.key]"
:aria-label="itemValue.text"
:title="itemValue.text" :title="itemValue.text"
> >
{{ itemValue.text }} {{ itemValue.text }}

View File

@ -29,6 +29,7 @@
:key="headerItem.property" :key="headerItem.property"
:direction="sortBy === headerItem.property ? ascending : headerItem.defaultDirection" :direction="sortBy === headerItem.property ? ascending : headerItem.defaultDirection"
:is-sortable="headerItem.isSortable" :is-sortable="headerItem.isSortable"
:aria-label="headerItem.name"
:title="headerItem.name" :title="headerItem.name"
:property="headerItem.property" :property="headerItem.property"
:current-sort="sortBy" :current-sort="sortBy"

View File

@ -36,7 +36,11 @@
<div class="c-so-view__header"> <div class="c-so-view__header">
<div class="c-object-label" :class="[statusClass]"> <div class="c-object-label" :class="[statusClass]">
<div class="c-object-label__type-icon" :class="cssClass"> <div class="c-object-label__type-icon" :class="cssClass">
<span class="is-status__indicator" :title="`This item is ${status}`"></span> <span
class="is-status__indicator"
:aria-label="`This item is ${status}`"
:title="`This item is ${status}`"
></span>
</div> </div>
<div <div
ref="objectName" ref="objectName"
@ -78,6 +82,7 @@
</div> </div>
<button <button
class="c-icon-button icon-3-dots c-so-view__frame-controls__more" class="c-icon-button icon-3-dots c-so-view__frame-controls__more"
aria-label="View menu items"
title="View menu items" title="View menu items"
@click.prevent.stop="showMenuItems($event)" @click.prevent.stop="showMenuItems($event)"
></button> ></button>

View File

@ -29,7 +29,11 @@
@click="navigateOrPreview" @click="navigateOrPreview"
> >
<div class="c-tree__item__type-icon c-object-label__type-icon" :class="typeClass"> <div class="c-tree__item__type-icon c-object-label__type-icon" :class="typeClass">
<span class="is-status__indicator" :title="`This item is ${status}`"></span> <span
class="is-status__indicator"
:aria-label="`This item is ${status}`"
:title="`This item is ${status}`"
></span>
</div> </div>
<div <div
ref="objectLabel" ref="objectLabel"

View File

@ -35,7 +35,12 @@
:style="gridRowSpan" :style="gridRowSpan"
> >
<div v-if="iconClass" class="c-object-label__type-icon" :class="iconClass"> <div v-if="iconClass" class="c-object-label__type-icon" :class="iconClass">
<span v-if="status" class="is-status__indicator" :title="`This item is ${status}`"></span> <span
v-if="status"
class="is-status__indicator"
:aria-label="`This item is ${status}`"
:title="`This item is ${status}`"
></span>
</div> </div>
<div class="c-object-label__name"> <div class="c-object-label__name">

View File

@ -24,7 +24,11 @@
<div class="c-inspector__header"> <div class="c-inspector__header">
<div v-if="!multiSelect" class="c-inspector__selected c-object-label" :class="[statusClass]"> <div v-if="!multiSelect" class="c-inspector__selected c-object-label" :class="[statusClass]">
<div class="c-object-label__type-icon" :class="typeCssClass"> <div class="c-object-label__type-icon" :class="typeCssClass">
<span class="is-status__indicator" :title="`This item is ${status}`"></span> <span
class="is-status__indicator"
:aria-label="`This item is ${status}`"
:title="`This item is ${status}`"
></span>
</div> </div>
<span v-if="!singleSelectNonObject" class="c-inspector__selected c-object-label__name">{{ <span v-if="!singleSelectNonObject" class="c-inspector__selected c-object-label__name">{{
item.name item.name

View File

@ -22,7 +22,7 @@
<template> <template>
<!-- eslint-disable vue/no-v-html --> <!-- eslint-disable vue/no-v-html -->
<div class="c-about c-about--splash"> <div class="c-about c-about--splash">
<div class="c-about__image c-splash-image"></div> <div class="c-about__image c-splash-image" role="img" alt="Open MCT Splash Logo"></div>
<div class="c-about__text s-text"> <div class="c-about__text s-text">
<div <div
v-if="branding.aboutHtml" v-if="branding.aboutHtml"
@ -40,7 +40,10 @@
Open MCT is licensed under the Apache License, Version 2.0 (the "License"); you may not 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 use this file except in compliance with the License. You may obtain a copy of the
License at License at
<a target="_blank" href="http://www.apache.org/licenses/LICENSE-2.0" <a
target="_blank"
href="http://www.apache.org/licenses/LICENSE-2.0"
rel="noopener noreferrer"
>http://www.apache.org/licenses/LICENSE-2.0</a >http://www.apache.org/licenses/LICENSE-2.0</a
>. >.
</p> </p>
@ -57,11 +60,11 @@
</p> </p>
</div> </div>
<h2>Version Information</h2> <h2>Version Information</h2>
<ul class="t-info l-info s-info"> <ul id="versionInformation" class="t-info l-info s-info">
<li>Version: {{ buildInfo.version || 'Unknown' }}</li> <li aria-label="Version Number">Version: {{ buildInfo.version || 'Unknown' }}</li>
<li>Build Date: {{ buildInfo.buildDate || 'Unknown' }}</li> <li aria-label="Build Date">Build Date: {{ buildInfo.buildDate || 'Unknown' }}</li>
<li>Revision: {{ buildInfo.revision || 'Unknown' }}</li> <li aria-label="Revision">Revision: {{ buildInfo.revision || 'Unknown' }}</li>
<li>Branch: {{ buildInfo.branch || 'Unknown' }}</li> <li aria-label="Branch">Branch: {{ buildInfo.branch || 'Unknown' }}</li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -45,6 +45,7 @@
? 'l-shell__head__collapse-button--collapse' ? 'l-shell__head__collapse-button--collapse'
: 'l-shell__head__collapse-button--expand' : 'l-shell__head__collapse-button--expand'
" "
:aria-label="`Click to ${headExpanded ? 'collapse' : 'expand'} items`"
:title="`Click to ${headExpanded ? 'collapse' : 'expand'} items`" :title="`Click to ${headExpanded ? 'collapse' : 'expand'} items`"
@click="toggleShellHead" @click="toggleShellHead"
></button> ></button>
@ -61,6 +62,7 @@
'c-icon-button c-icon-button--major', 'c-icon-button c-icon-button--major',
fullScreen ? 'icon-fullscreen-collapse' : 'icon-fullscreen-expand' fullScreen ? 'icon-fullscreen-collapse' : 'icon-fullscreen-expand'
]" ]"
:aria-label="`${fullScreen ? 'Exit' : 'Enable'} full screen mode`"
:title="`${fullScreen ? 'Exit' : 'Enable'} full screen mode`" :title="`${fullScreen ? 'Exit' : 'Enable'} full screen mode`"
@click="fullScreenToggle" @click="fullScreenToggle"
></button> ></button>

View File

@ -20,7 +20,13 @@
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<template> <template>
<div ref="aboutLogo" class="l-shell__app-logo" @click="launchAbout"></div> <div
ref="aboutLogo"
class="l-shell__app-logo"
role="button"
aria-label="About Modal"
@click="launchAbout"
></div>
</template> </template>
<script> <script>

View File

@ -71,6 +71,7 @@
v-for="(item, index) in statusBarItems" v-for="(item, index) in statusBarItems"
:key="index" :key="index"
class="c-button" class="c-button"
:aria-label="item.name"
:title="item.name" :title="item.name"
:class="item.cssClass" :class="item.cssClass"
@click="item.onItemClicked" @click="item.onItemClicked"
@ -78,6 +79,7 @@
<button <button
v-if="isViewEditable & !isEditing" v-if="isViewEditable & !isEditing"
:aria-label="lockedOrUnlockedTitle"
:title="lockedOrUnlockedTitle" :title="lockedOrUnlockedTitle"
:class="{ :class="{
'c-button icon-lock': domainObject.locked, 'c-button icon-lock': domainObject.locked,
@ -89,8 +91,8 @@
<button <button
v-if="isViewEditable && !isEditing && !domainObject.locked" v-if="isViewEditable && !isEditing && !domainObject.locked"
class="l-browse-bar__actions__edit c-button c-button--major icon-pencil" class="l-browse-bar__actions__edit c-button c-button--major icon-pencil"
title="Edit" title="Edit Object"
aria-label="Edit" aria-label="Edit Object"
@click="edit()" @click="edit()"
></button> ></button>
@ -123,6 +125,7 @@
<button <button
v-if="isEditing" v-if="isEditing"
class="l-browse-bar__actions c-button icon-x" class="l-browse-bar__actions c-button icon-x"
aria-label="Cancel Editing"
title="Cancel Editing" title="Cancel Editing"
@click="promptUserandCancelEditing()" @click="promptUserandCancelEditing()"
></button> ></button>