[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",
"perfromance",
"MMOC",
"deploysentinel",
"codegen",
"Unfortuantely",
"viewports",

View File

@ -47,9 +47,8 @@ jobs:
bash src/plugins/persistence/couch/setup-couchdb.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:
DEPLOYSENTINEL_API_KEY: ${{ secrets.DEPLOYSENTINEL_API_KEY }}
COMMIT_INFO_SHA: ${{github.event.pull_request.head.sha }}
run: npm run test:e2e:couchdb

3
.gitignore vendored
View File

@ -15,6 +15,9 @@
*.idea
*.iml
# VSCode
.vscode/settings.json
# Build output
target
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.
### 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.
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.
(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
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
}
],
['junit', { outputFile: '../test-results/results.xml' }],
['@deploysentinel/playwright']
['junit', { outputFile: '../test-results/results.xml' }]
]
};

View File

@ -1,14 +1,9 @@
// playwright.config.js
// @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} */
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',
timeout: 60 * 1000,
webServer: {
@ -17,8 +12,7 @@ const config = {
timeout: 200 * 1000,
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: NUM_WORKERS, //Limit to 2 for CircleCI Agent
workers: '75%', //Limit to 75% of the CPU to support running with dev server
use: {
baseURL: 'http://localhost:8080/',
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
}
],
['junit', { outputFile: '../test-results/results.xml' }],
['@deploysentinel/playwright']
['junit', { outputFile: '../test-results/results.xml' }]
]
};

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": [
{
"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",
"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",

View File

@ -6,7 +6,7 @@
"localStorage": [
{
"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",

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');
//Click the Create button
await page.click('button:has-text("Create")');
await page.getByRole('button', { name: 'Create' }).click();
// Click the object specified by 'type'
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 }) => {
// Create Display Layout
const parent = await createDomainObjectWithDefaults(page, {
type: 'Display Layout',
name: 'Parent Display Layout'
});
const child1 = await createDomainObjectWithDefaults(page, {
await createDomainObjectWithDefaults(page, {
type: 'Display Layout',
name: 'Child Layout 1',
parent: parent.uuid
});
const child2 = await createDomainObjectWithDefaults(page, {
await createDomainObjectWithDefaults(page, {
type: 'Display Layout',
name: 'Child Layout 2',
parent: parent.uuid
});
await page.goto(parent.url);
await page.getByLabel('Edit').click();
await page.getByLabel(`${child2.name} Layout Grid`).hover();
await page.goto(parent.url, { waitUntil: 'domcontentloaded' });
await page.getByLabel('Edit Object').click();
await page.getByLabel('Child Layout 2 Layout', { exact: true }).hover();
await page.getByLabel('Move Sub-object Frame').nth(1).click();
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('Y:').fill('30');
@ -107,7 +106,7 @@ test.describe('Generate Visual Test Data @localStorage @generatedata', () => {
parent: parent.uuid
});
await page.goto(parent.url);
await page.goto(parent.url, { waitUntil: 'domcontentloaded' });
//Save localStorage for future test execution
await context.storageState({
@ -134,7 +133,7 @@ test.describe('Generate Visual Test Data @localStorage @generatedata', () => {
await page.locator('button[title="More actions"]').click();
// 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
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);
await page.goto(swgWith5sDelay.url);
await page.getByTitle('More actions').click();
await page.getByRole('menuitem', { name: ' Edit Properties...' }).click();
await page.getByLabel('More actions').click();
await page.getByLabel('Edit Properties...').click();
//Edit Example Telemetry Object to include 5s loading Delay
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
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
await context.storageState({
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';
test.describe('Branding tests', () => {
test('About Modal launches with basic branding properties', async ({ page }) => {
// Go to baseURL
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Click About button
await page.click('.l-shell__app-logo');
});
test('About Modal launches with basic branding properties', async ({ page }) => {
await page.getByLabel('About Modal').click();
// 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
const versionInformationLocator = page.locator('ul.t-info.l-info.s-info').first();
await expect(versionInformationLocator).toBeEnabled();
await expect.soft(versionInformationLocator).toContainText(/Version: \d/);
await expect.soft(page.getByLabel('Version Number')).toContainText(/Version: \d/);
await expect
.soft(versionInformationLocator)
.soft(page.getByLabel('Build Date'))
.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(versionInformationLocator).toContainText(/Branch: ./);
await expect.soft(page.getByLabel('Revision')).toContainText(/Revision: \b[0-9a-f]{5,40}\b/);
await expect.soft(page.getByLabel('Branch')).toContainText(/Branch: ./);
});
test('Verify Links in About Modal @2p', async ({ page }) => {
// Go to baseURL
await page.goto('./', { waitUntil: 'domcontentloaded' });
// 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
const [page2] = await Promise.all([
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();
});
});

View File

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

View File

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

View File

@ -41,8 +41,8 @@ test.describe('Form Validation Behavior', () => {
//Go to baseURL
await page.goto('./', { waitUntil: 'domcontentloaded' });
await page.click('button:has-text("Create")');
await page.getByRole('menuitem', { name: 'Folder' }).click();
await page.getByRole('button', { name: 'Create' }).click();
await page.getByRole('menuitem', { name: 'Folder' }).click();
// Fill in empty string into title and trigger validation with 'Tab'
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 }) => {
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.setInputFiles('#fileElem', jsonFilePath);
@ -94,7 +94,7 @@ test.describe('Form File Input Behavior', () => {
test('Can select an image file type', async ({ page }) => {
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.setInputFiles('#fileElem', imageFilePath);
@ -121,7 +121,7 @@ test.describe('Persistence operations @addInit', () => {
});
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');

View File

@ -107,7 +107,7 @@ test.describe('Notification Overlay', () => {
await page.getByRole('button', { name: 'Close' }).click();
// 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
await page.getByRole('button', { name: 'Save' }).click();

View File

@ -69,7 +69,7 @@ test.describe('Gantt Chart', () => {
.getByRole('dialog')
.filter({ hasText: 'This action will replace the current Plan. Do you want to continue?' });
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);
});

View File

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

View File

@ -34,7 +34,6 @@ import {
import { expect, test } from '../../../../pluginFixtures.js';
let conditionSetUrl;
let getConditionSetIdentifierFromUrl;
test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
test.beforeAll(async ({ browser }) => {
@ -42,7 +41,7 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
const context = await browser.newContext();
const page = await context.newPage();
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();
@ -58,8 +57,6 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
//Set object identifier from url
conditionSetUrl = page.url();
getConditionSetIdentifierFromUrl = conditionSetUrl.split('/').pop().split('?')[0];
console.debug(`getConditionSetIdentifierFromUrl: ${getConditionSetIdentifierFromUrl}`);
await page.close();
});
@ -234,7 +231,7 @@ test.describe('Basic Condition Set Use', () => {
await page.goto(conditionSet.url);
// Change the object to edit mode
await page.locator('[title="Edit"]').click();
await page.getByLabel('Edit Object').click();
// Click Add Condition button
await page.locator('#addCondition').click();
@ -262,7 +259,7 @@ test.describe('Basic Condition Set Use', () => {
await page.goto(conditionSet.url);
// 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
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.goto(conditionSet.url);
// Change the object to edit mode
await page.locator('[title="Edit"]').click();
await page.getByLabel('Edit Object').click();
// Create two conditions
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' })
.first()
.click();
await page.getByLabel('Edit').click();
await page.getByLabel('Edit Object').click();
});
test.use({
storageState: LOCALSTORAGE_PATH
@ -133,7 +133,7 @@ test.describe('Display Layout', () => {
name: 'Test 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
await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click();
@ -171,7 +171,7 @@ test.describe('Display Layout', () => {
name: 'Test 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
await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click();
@ -213,7 +213,7 @@ test.describe('Display Layout', () => {
name: 'Test 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
await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click();
@ -255,7 +255,7 @@ test.describe('Display Layout', () => {
type: '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
await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click();
@ -301,7 +301,7 @@ test.describe('Display Layout', () => {
type: '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
await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click();
@ -357,7 +357,7 @@ test.describe('Display Layout', () => {
name: 'Test 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
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
.click({ force: true });
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);
// Edit Flexible Layout
await page.locator('[title="Edit"]').click();
await page.getByLabel('Edit Object').click();
// Expand the 'My Items' folder in the left tree
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);
// Edit Flexible Layout
await page.locator('[title="Edit"]').click();
await page.getByLabel('Edit Object').click();
// Expand the 'My Items' folder in the left tree
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);
// Edit Flexible Layout
await page.locator('[title="Edit"]').click();
await page.getByLabel('Edit Object').click();
// Expand the 'My Items' folder in the left tree
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);
// Edit Flexible Layout
await page.locator('[title="Edit"]').click();
await page.getByLabel('Edit Object').click();
// Expand the 'My Items' folder in the left tree
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' })
.first()
.click();
await page.getByLabel('Edit').click();
await page.getByLabel('Edit Object').click();
});
test('Add/Remove Container', async ({ page }) => {
test.info().annotations.push({
@ -293,7 +293,7 @@ test.describe('Flexible Layout Toolbar Actions @localStorage', () => {
await expect(page.getByRole('dialog', { name: 'Overlay' })).toHaveText(
'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);
});
test('Remove Frame', async ({ page }) => {
@ -303,7 +303,7 @@ test.describe('Flexible Layout Toolbar Actions @localStorage', () => {
await expect(page.getByRole('dialog', { name: 'Overlay' })).toHaveText(
'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);
});
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 }) => {
// Create the gauge with defaults
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
const swg1 = await createDomainObjectWithDefaults(page, {
@ -54,9 +52,9 @@ test.describe('Gauge', () => {
// Navigate to the gauge and verify that
// the SWG appears in the elements pool
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 saveButtonLocator.click();
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
// 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
// appears in the elements pool and the old one is gone
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=${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
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'
});
//Click the Create button
await page.click('button:has-text("Create")');
await page.getByRole('button', { name: 'Create' }).click();
// Click the object specified by 'type'
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();
});
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
const backgroundImage = await page.locator(backgroundImageSelector);
await backgroundImage.click({
@ -397,7 +397,7 @@ test.describe('Example Imagery in Display Layout', () => {
});
// Edit mode
await page.click('button[title="Edit"]');
await page.getByLabel('Edit Object').click();
// Click on example imagery to expose toolbar
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 }) => {
const thumbsWrapperLocator = page.locator('.c-imagery__thumbs-wrapper');
// Edit mode
await page.click('button[title="Edit"]');
await page.getByLabel('Edit Object').click();
// Click on example imagery to expose toolbar
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 */
// Click the Create button
await page.click('button:has-text("Create")');
await page.getByRole('button', { name: 'Create' }).click();
// Click 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 */
// Click the Create button
await page.click('button:has-text("Create")');
await page.getByRole('button', { name: 'Create' }).click();
// Click 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) {
// Click the Create button
await page.click('button:has-text("Create")');
await page.getByRole('button', { name: 'Create' }).click();
// Click 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 }) => {
// Edit LAD table
await page.locator('[title="Edit"]').click();
await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'LAD Table Configuration' }).click();
// make sure headers are visible initially
@ -113,7 +113,7 @@ test.describe('Testing LAD table configuration', () => {
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible();
// Edit LAD table
await page.locator('[title="Edit"]').click();
await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'LAD Table Configuration' }).click();
// show timestamp column
@ -141,7 +141,7 @@ test.describe('Testing LAD table configuration', () => {
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible();
// Edit LAD table
await page.locator('[title="Edit"]').click();
await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'LAD Table Configuration' }).click();
// show units, type, and WATCH columns
@ -181,7 +181,7 @@ test.describe('Testing LAD table configuration', () => {
await page.goto(ladTable.url);
// Edit LAD table
await page.getByLabel('Edit').click();
await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'LAD Table Configuration' }).click();
// 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.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
// Remove Sin Wave Generator
// Remove Sine Wave Generator
openObjectTreeContextMenu(page, sineWaveObject.url);
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
// as Event Generator don't have them
@ -258,7 +258,7 @@ test.describe('Testing LAD table @unstable', () => {
name: 'Test 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
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'
});
// Edit LAD table
await page.locator('[title="Edit"]').click();
await page.getByLabel('Edit Object').click();
// Expand the 'My Items' folder in the left tree
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"
// });
await page.getByRole('button', { name: ' Snapshot ' }).click();
await page.getByRole('menuitem', { name: 'Save to Notebook Snapshots' }).click();
await page.getByLabel('Take a Notebook Snapshot').click();
await page.getByRole('menuitem', { name: 'Save to Notebook Snapshots' }).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 and Deleted with Delete All action',
@ -117,11 +122,7 @@ test.describe('Snapshot Container tests', () => {
//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(
'A snapshot can be Navigated To from Container with 3 dot action menu',
async ({ page }) => {}

View File

@ -128,7 +128,7 @@ test.describe('Tagging in Notebooks @addInit', () => {
});
// 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').fill('Sc');

View File

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

View File

@ -183,7 +183,7 @@ async function testLogTicks(page) {
*/
async function enableEditMode(page) {
// 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();
}

View File

@ -87,7 +87,7 @@ test.describe('Overlay Plot', () => {
expect(await page.locator('.c-plot-limit-line').count()).toBe(0);
// 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
await page.getByRole('tab', { name: 'Config' }).click();
@ -114,7 +114,7 @@ test.describe('Overlay Plot', () => {
await assertLimitLinesExistAndAreVisible(page);
// Enter edit mode
await page.click('button[title="Edit"]');
await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'Elements' }).click();
@ -165,7 +165,7 @@ test.describe('Overlay Plot', () => {
});
await page.goto(overlayPlot.url);
await page.click('button[title="Edit"]');
await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'Elements' }).click();
@ -239,7 +239,7 @@ test.describe('Overlay Plot', () => {
await page.goto(overlayPlot.url);
// Wait for plot series data to load and be drawn
await waitForPlotsToRender(page);
await page.click('button[title="Edit"]');
await page.getByLabel('Edit Object').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 }) => {
const editButton = page.locator('button[title="Edit"]');
const saveButton = page.locator('button[title="Save"]');
// Create a sine wave generator within the scatter plot
const swg1 = await createDomainObjectWithDefaults(page, {
type: 'Sine Wave Generator',
@ -54,10 +51,10 @@ test.describe('Scatter Plot', () => {
// Navigate to the scatter plot and verify that
// the SWG appears in the elements pool
await page.goto(scatterPlot.url);
await editButton.click();
await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'Elements' }).click();
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();
// 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
// appears in the elements pool and the old one is gone
await page.goto(scatterPlot.url);
await editButton.click();
await page.getByLabel('Edit Object').click();
// Click the "Elements" tab
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=${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
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.click('button[title="Edit"]');
await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'Elements' }).click();
@ -101,7 +101,7 @@ test.describe('Stacked Plot', () => {
await page.goto(stackedPlot.url);
await page.click('button[title="Edit"]');
await page.getByLabel('Edit Object').click();
await page.getByRole('tab', { name: 'Elements' }).click();
@ -187,7 +187,7 @@ test.describe('Stacked Plot', () => {
).toContainText(swgC.name);
// Go into edit mode
await page.click('button[title="Edit"]');
await page.getByLabel('Edit Object').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 }) => {
await page.goto(stackedPlot.url);
await waitForPlotsToRender(page);
// Go into edit mode
await page.click('button[title="Edit"]');
await page.getByLabel('Edit Object').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.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
await waitForPlotsToRender(page);
await assertAggregateLegendIsVisible(page);
await page.reload();
await waitForPlotsToRender(page);
await assertAggregateLegendIsVisible(page);
});
});

View File

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

View File

@ -74,7 +74,7 @@ test.describe('Stacked Plot styling', () => {
// Directly navigate to the stacked plot
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();
@ -165,7 +165,7 @@ test.describe('Stacked Plot styling', () => {
});
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();

View File

@ -49,7 +49,7 @@ test.describe('Style Inspector Options', () => {
await page.goto(flexibleLayout.url, { waitUntil: 'domcontentloaded' });
// 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.
await expect(page.getByRole('tab', { name: 'Styles' })).toBeVisible();

View File

@ -128,7 +128,10 @@ test.describe('Telemetry Table', () => {
// focus the Telemetry Table
page.goto(table.url);
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' }).fill('/[Rr]oger/');

View File

@ -104,7 +104,7 @@ test.describe('Recent Objects', () => {
button: 'right'
});
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
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();
// 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
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();
// 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
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);
// 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.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' });
//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
await expect(page.locator(':nth-match(:text("Folder"), 2)')).toBeEnabled();

View File

@ -96,7 +96,7 @@ test.describe('Verify tooltips', () => {
name: 'Test 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
await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.c-lad-table-wrapper');
@ -126,7 +126,7 @@ test.describe('Verify tooltips', () => {
name: 'Test Overlay Plots'
});
// 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
await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.gl-plot');
@ -197,7 +197,7 @@ test.describe('Verify tooltips', () => {
name: 'Test 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.locator('button[title="Save"]').click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
@ -208,7 +208,7 @@ test.describe('Verify tooltips', () => {
name: 'Test 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.locator('button[title="Save"]').click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
@ -219,7 +219,7 @@ test.describe('Verify tooltips', () => {
name: 'Test 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', {
targetPosition: { x: 0, y: 0 }
@ -428,7 +428,7 @@ test.describe('Verify tooltips', () => {
name: 'Test Time Strip'
});
// Edit Overlay Plot
await page.locator('[title="Edit"]').click();
await page.getByLabel('Edit Object').click();
await page.dragAndDrop(
`text=${sineWaveObject1.name}`,
'.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 { expect, scanForA11yViolations, test } from '../../../avpFixtures.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 }) => {
//Go to baseURL and Hide Tree
await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' });
@ -37,18 +37,22 @@ test.describe('Visual - Branding', () => {
test('Visual - About Modal', async ({ page, theme }) => {
// 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
const versionInformationLocator = page.locator('ul.t-info.l-info.s-info').first();
await expect(versionInformationLocator).toBeEnabled();
await versionInformationLocator.evaluate(
(node) =>
(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>')
);
await expect(page.locator('id=versionInformation')).toBeEnabled();
await page
.locator('id=versionInformation')
.evaluate(
(node) =>
(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
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 { scanForA11yViolations, test } from '../../../avpFixtures.js';
import { MISSION_TIME, VISUAL_URL } from '../../../constants.js';
import { test } from '../../../pluginFixtures.js';
//Declare the scope of the visual test
const inspectorPane = '.l-shell__pane-inspector';
test.describe('Visual - Controlled Clock', () => {
test.describe('Visual - Inspector @ally', () => {
test.beforeEach(async ({ page }) => {
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, 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})`, {
scope: treePane
});

View File

@ -44,14 +44,15 @@ test.describe('Visual - Controlled Clock', () => {
test('Overlay Plot Loading Indicator @localStorage', async ({ page, theme }) => {
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
await expect(page.locator('.l-browse-bar__object-name')).toContainText(
'Overlay Plot with 5s Delay'
);
await expect(page.getByRole('main')).toContainText('Overlay Plot with 5s Delay');
//Wait for canvas to be rendered and stop animating, but plot should not be loaded. Cannot use waitForPlotsToRender
await page.locator('canvas >> nth=1').hover({ trial: true });
//Wait for canvas to be rendered and stop animating, but plot should not be loaded.
//Cannot use waitForPlotsToRender due to clockOptions.
await page.locator('#webglContext').hover({ trial: true });
//Take snapshot of Sine Wave Generator within Overlay Plot
await percySnapshot(page, `SineWaveInOverlayPlot (theme: '${theme}')`);

View File

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

View File

@ -20,138 +20,77 @@
* 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 { createDomainObjectWithDefaults } from '../../appActions.js';
import { VISUAL_URL } from '../../constants.js';
import { test } from '../../pluginFixtures.js';
const snapshotScope = '.l-shell__pane-main .l-pane__contents';
test.describe('Visual - Display Layout', () => {
test('Resize Marquee surrounds selection', async ({ page, theme }) => {
const baseline = await setupBaseline(page);
const { child1LayoutLocator, child1LayoutObjectLocator } = baseline;
test.beforeEach(async ({ page, theme }) => {
await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' });
await percySnapshot(page, `Resize nested layout selected (theme: '${theme}')`, {
scope: snapshotScope
const parentLayout = await createDomainObjectWithDefaults(page, {
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 percySnapshot(page, `Resize new nested layout selected (theme: '${theme}')`, {
scope: snapshotScope
});
await page.goto(parentLayout.url, { waitUntil: 'domcontentloaded' });
await page.getByRole('button', { name: 'Edit Object' }).click();
await child1LayoutObjectLocator.click();
await percySnapshot(page, `Resize Object in nested layout selected (theme: '${theme}')`, {
scope: snapshotScope
});
//Move the Child Right Layout to the Right. It should be on top of the Left Layout at this point.
await page
.getByLabel('Child Right Layout Layout', { exact: true })
.getByLabel('Move Sub-object Frame')
.click();
await page.getByLabel('Move Sub-object Frame').nth(3).click(); //I'm not sure why this step is necessary
await page.getByLabel('X:').click();
await page.getByLabel('X:').fill('35');
});
test('Parent layout of selection displays grid', async ({ page, theme }) => {
const baseline = await setupBaseline(page);
const { parentLayoutLocator, child1LayoutObjectLocator } = baseline;
test('Resize Marquee surrounds selection', async ({ page, theme }) => {
//This is where the beforeEach leaves off.
await percySnapshot(page, `Last modified object selected (theme: '${theme}')`);
await percySnapshot(page, `Parent nested layout selected (theme: '${theme}')`, {
scope: snapshotScope
});
await page.getByLabel('Child Left Layout Layout', { exact: true }).click();
await percySnapshot(page, `Only Left Child Layout has Marque selection (theme: '${theme}')`);
await parentLayoutLocator.click();
await percySnapshot(page, `Parent outer layout selected (theme: '${theme}')`, {
scope: snapshotScope
});
await page.getByLabel('Child Right Layout Layout', { exact: true }).click();
await percySnapshot(page, `Only Right Child Layout has Marque selection (theme: '${theme}')`);
await child1LayoutObjectLocator.click();
await percySnapshot(page, `Parent Object in nested layout selected (theme: '${theme}')`, {
scope: snapshotScope
});
//Only the sub-object in the Right Layout should be highlighted with a marquee
await page
.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';
test.describe('Visual - LAD Table', () => {
/** @type {import('@playwright/test').Locator} */
let ladTable;
test.beforeEach(async ({ page }) => {
@ -46,9 +45,9 @@ test.describe('Visual - LAD Table', () => {
});
//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
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();
});
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(
page,
`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(
page,

View File

@ -26,43 +26,54 @@
import percySnapshot from '@percy/playwright';
import { createDomainObjectWithDefaults } from '../../appActions.js';
import { createNotification } from '../../appActions.js';
import { expect, scanForA11yViolations, test } from '../../avpFixtures.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 }) => {
await page.goto(VISUAL_URL, { waitUntil: 'domcontentloaded' });
});
test("Create a clock, click on 'Save successful' banner and dismiss it", async ({
page,
theme
}) => {
// Create a clock domain object
await createDomainObjectWithDefaults(page, {
type: 'Clock',
name: 'Default Clock'
test('Alert Levels and Notification List Modal', async ({ page, theme }) => {
await createNotification(page, {
message: 'Test info notification',
severity: 'info'
});
// Click on the div with role="alert" that has "Save successful" text
await page.getByRole('alert').filter({ hasText: 'Save successful' }).click();
// Verify there is a div with role="dialog"
await expect(page.getByRole('dialog', { name: 'Overlay' })).toBeVisible();
// Verify the div with role="dialog" contains text "Save successful"
expect(await page.getByRole('dialog', { name: 'Overlay' }).innerText()).toContain(
'Save successful'
);
await percySnapshot(page, `Notification banner shows Save successful (theme: '${theme}')`);
// Verify there is a button with text "Dismiss"
await expect(page.getByText('Dismiss', { exact: true })).toBeVisible();
await percySnapshot(page, `Notification banner shows Dismiss (theme: '${theme}')`);
// Click on button with text "Dismiss"
await page.getByText('Dismiss', { exact: true }).click();
// Verify there is no div with role="dialog"
await expect(page.getByRole('dialog', { name: 'Overlay' })).toBeHidden();
await percySnapshot(page, `Notification banner dismissed (theme: '${theme}')`);
});
test.afterEach(async ({ page }, testInfo) => {
await scanForA11yViolations(page, testInfo.title);
await expect(page.getByText('Test info notification')).toBeVisible();
await percySnapshot(page, `Info Notification banner shown (theme: '${theme}')`);
await page.getByLabel('Dismiss').click();
await page.getByRole('alert').waitFor({ state: 'detached' });
await createNotification(page, {
message: 'Test alert notification',
severity: 'alert'
});
await expect(page.getByText('Test alert notification')).toBeVisible();
await percySnapshot(page, `Alert Notification banner shown (theme: '${theme}')`);
await page.getByLabel('Dismiss').click();
await page.getByRole('alert').waitFor({ state: 'detached' });
await createNotification(page, {
message: 'Test error notification',
severity: 'error'
});
await expect(page.getByText('Test error notification')).toBeVisible();
await percySnapshot(page, `Error Notification banner shown (theme: '${theme}')`);
await page.getByLabel('Dismiss').click();
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}')`);
// 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
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' });
// Edit Flexible Layout
await page.getByLabel('Edit').click();
await page.getByLabel('Edit Object').click();
// Select styles tab
await page.getByRole('tab', { name: 'Styles' }).click();
@ -146,7 +146,7 @@ test.describe('Stacked Plot styling @a11y', () => {
await page.goto(stackedPlot.url, { waitUntil: 'domcontentloaded' });
// Edit Flexible Layout
await page.getByLabel('Edit').click();
await page.getByLabel('Edit Object').click();
// Select styles tab
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",
"@babel/eslint-parser": "7.23.3",
"@braintree/sanitize-url": "6.0.4",
"@deploysentinel/playwright": "0.3.4",
"@percy/cli": "1.27.4",
"@percy/playwright": "1.0.4",
"@playwright/test": "1.39.0",

View File

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

View File

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

View File

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

View File

@ -41,7 +41,11 @@
@mouseover.ctrl="showToolTip"
@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 class="c-telemetry-view__label-text">
{{ domainObject.name }}
@ -50,6 +54,7 @@
<div
v-if="showValue"
:aria-label="fieldName"
:title="fieldName"
class="c-telemetry-view__value"
:class="[telemetryClass]"

View File

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

View File

@ -42,14 +42,22 @@
<div class="c-grid-item__details">
<!-- Name and metadata -->
<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>
</div>
</div>
<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>
<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>
</a>

View File

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

View File

@ -34,7 +34,11 @@
class="c-object-label__type-icon c-list-item__name__type-icon"
: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 class="c-object-label__name c-list-item__name__name">{{ item.model.name }}</div>
</a>

View File

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

View File

@ -26,11 +26,20 @@
role="toolbar"
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" />
</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" />
</imagery-view-menu-switcher>
@ -47,6 +56,7 @@
<imagery-view-menu-switcher
class="--show-if-less-than-220"
:icon-class="'icon-magnify'"
:aria-label="'Zoom settings'"
:title="'Zoom settings'"
>
<zoom-settings

View File

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

View File

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

View File

@ -23,7 +23,12 @@
<template>
<div>
<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">
<span
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">
<button
class="c-icon-button c-button--menu icon-camera"
aria-label="Take a Notebook Snapshot"
title="Take a Notebook Snapshot"
@click.stop.prevent="showMenu"
>

View File

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

View File

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

View File

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

View File

@ -54,7 +54,11 @@
:class="[tab.status ? `is-status--${tab.status}` : '']"
>
<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>
<span class="c-button__label c-object-label__name">{{ tab.domainObject.name }}</span>
</div>

View File

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

View File

@ -21,11 +21,12 @@
-->
<template>
<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
v-if="allowExport"
v-show="!markedRows.length"
class="c-button icon-download labeled"
aria-label="Export this view's data"
title="Export this view's data"
@click="exportAllDataAsCSV()"
>
@ -35,6 +36,7 @@
v-if="allowExport"
v-show="markedRows.length"
class="c-button icon-download labeled"
aria-label="Export marked rows as CSV"
title="Export marked rows as CSV"
@click="exportMarkedDataAsCSV()"
>
@ -43,6 +45,7 @@
<button
v-show="markedRows.length"
class="c-button icon-x labeled"
aria-label="Unmark all rows"
title="Unmark all rows"
@click="unmarkAllRows()"
>
@ -51,6 +54,7 @@
<div v-if="marking.enable" class="c-separator"></div>
<button
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="paused ? 'icon-play is-paused' : 'icon-pause'"
:title="paused ? 'Continue real-time data flow' : 'Pause real-time data flow'"
@ -62,10 +66,11 @@
</button>
<template v-if="!isEditing">
<div class="c-separator"></div>
<div class="c-separator" role="separator"></div>
<button
v-if="isAutosizeEnabled"
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."
@click="recalculateColumnWidths"
>
@ -73,6 +78,7 @@
</button>
<button
v-else
aria-label="Automatically size columns to fit the table into the available space."
class="c-button icon-expand labeled"
title="Automatically size columns to fit the table into the available space."
@click="autosizeColumns"
@ -104,6 +110,7 @@
<button
:class="{ 'hide-nice': !markedRows.length }"
class="c-icon-button icon-x labeled"
aria-label="Deselect All"
title="Deselect All"
@click="unmarkAllRows()"
>
@ -191,6 +198,7 @@
<button
class="c-search__use-regex"
: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/"
@click="toggleRegex(key)"
>

View File

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

View File

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

View File

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

View File

@ -22,7 +22,7 @@
<template>
<div class="grid-row grid-row--pad-label-for-button">
<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="c-click-swatch c-click-swatch--menu" @click="toggleSwatch()">
<span class="c-color-swatch" :style="{ background: currentColor }"> </span>
@ -44,7 +44,7 @@
</div>
</template>
<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">
<span
class="c-color-swatch"

View File

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

View File

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

View File

@ -36,7 +36,11 @@
<div class="c-so-view__header">
<div class="c-object-label" :class="[statusClass]">
<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
ref="objectName"
@ -78,6 +82,7 @@
</div>
<button
class="c-icon-button icon-3-dots c-so-view__frame-controls__more"
aria-label="View menu items"
title="View menu items"
@click.prevent.stop="showMenuItems($event)"
></button>

View File

@ -29,7 +29,11 @@
@click="navigateOrPreview"
>
<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
ref="objectLabel"

View File

@ -35,7 +35,12 @@
:style="gridRowSpan"
>
<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 class="c-object-label__name">

View File

@ -24,7 +24,11 @@
<div class="c-inspector__header">
<div v-if="!multiSelect" class="c-inspector__selected c-object-label" :class="[statusClass]">
<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>
<span v-if="!singleSelectNonObject" class="c-inspector__selected c-object-label__name">{{
item.name

View File

@ -22,7 +22,7 @@
<template>
<!-- eslint-disable vue/no-v-html -->
<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
v-if="branding.aboutHtml"
@ -40,7 +40,10 @@
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
<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
>.
</p>
@ -57,11 +60,11 @@
</p>
</div>
<h2>Version Information</h2>
<ul class="t-info l-info s-info">
<li>Version: {{ buildInfo.version || 'Unknown' }}</li>
<li>Build Date: {{ buildInfo.buildDate || 'Unknown' }}</li>
<li>Revision: {{ buildInfo.revision || 'Unknown' }}</li>
<li>Branch: {{ buildInfo.branch || 'Unknown' }}</li>
<ul id="versionInformation" class="t-info l-info s-info">
<li aria-label="Version Number">Version: {{ buildInfo.version || 'Unknown' }}</li>
<li aria-label="Build Date">Build Date: {{ buildInfo.buildDate || 'Unknown' }}</li>
<li aria-label="Revision">Revision: {{ buildInfo.revision || 'Unknown' }}</li>
<li aria-label="Branch">Branch: {{ buildInfo.branch || 'Unknown' }}</li>
</ul>
</div>
</div>

View File

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

View File

@ -20,7 +20,13 @@
at runtime from the About dialog for additional information.
-->
<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>
<script>

View File

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