mirror of
https://github.com/nasa/openmct.git
synced 2025-06-25 02:29:24 +00:00
Compare commits
28 Commits
master
...
eslint_upd
Author | SHA1 | Date | |
---|---|---|---|
6ab76820ba | |||
c05b3888ce | |||
e17e7c6c02 | |||
6a320328ba | |||
5d3be0ebaf | |||
c88679bf5a | |||
27c7b7fe80 | |||
61c59b2bbf | |||
95a2396dfc | |||
3bd6fd6802 | |||
c5eb5e21f5 | |||
e203e86cad | |||
875ddc54fb | |||
2597ae5f2e | |||
63aa55512e | |||
06910cda1a | |||
814ce3e940 | |||
fb3d309a2a | |||
685f0ea8d9 | |||
55dfd68404 | |||
bfe32027b9 | |||
a1eba9b728 | |||
2f77b18f0f | |||
dd665dd379 | |||
5054d502d7 | |||
aed50d18b3 | |||
151c7d41e1 | |||
631add975d |
@ -5,9 +5,8 @@ const config = {
|
||||
browser: true,
|
||||
es2024: true,
|
||||
jasmine: true,
|
||||
node: true,
|
||||
worker: true,
|
||||
serviceworker: true
|
||||
amd: true,
|
||||
node: true
|
||||
},
|
||||
globals: {
|
||||
_: 'readonly'
|
||||
@ -155,6 +154,7 @@ const config = {
|
||||
'error',
|
||||
{
|
||||
cases: {
|
||||
camelCase: true,
|
||||
pascalCase: true
|
||||
},
|
||||
ignore: ['^.*\\.(js|cjs|mjs)$']
|
||||
|
@ -1,14 +1,14 @@
|
||||
/* eslint-disable no-undef */
|
||||
module.exports = {
|
||||
extends: ['plugin:playwright/playwright-test'],
|
||||
extends: ['plugin:playwright/recommended'],
|
||||
rules: {
|
||||
'playwright/max-nested-describe': ['error', { max: 1 }]
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['tests/visual/*.spec.js'],
|
||||
files: ['**/*.spec.js'], // Added the 'files' property
|
||||
rules: {
|
||||
'playwright/no-wait-for-timeout': 'off'
|
||||
'playwright/expect-expect': 'off'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -380,8 +380,7 @@ By adhering to this principle, we can create tests that are both robust and refl
|
||||
1. Avoid creating locator aliases. This likely means that you're compensating for a bad locator. Improve the application instead.
|
||||
1. Leverage `await page.goto('./', { waitUntil: 'domcontentloaded' });` instead of `{ waitUntil: 'networkidle' }`. Tests run against deployments with websockets often have issues with the networkidle detection.
|
||||
|
||||
#### How to make tests faster and more resilient
|
||||
|
||||
#### How to make tests faster and more resilient to application changes
|
||||
1. Avoid app interaction when possible. The best way of doing this is to navigate directly by URL:
|
||||
|
||||
```js
|
||||
@ -396,6 +395,16 @@ By adhering to this principle, we can create tests that are both robust and refl
|
||||
- Initial navigation should _almost_ always use the `{ waitUntil: 'domcontentloaded' }` option.
|
||||
1. Avoid repeated setup to test a single assertion. Write longer tests with multiple soft assertions.
|
||||
This ensures that your changes will be picked up with large refactors.
|
||||
1. Use [user-facing locators](https://playwright.dev/docs/best-practices#use-locators) (Now a eslint rule!)
|
||||
|
||||
```js
|
||||
page.getByRole('button', { name: 'Create' } )
|
||||
```
|
||||
Instead of
|
||||
```js
|
||||
page.locator('.c-create-button')
|
||||
```
|
||||
Note: `page.locator()` can be used in performance tests as xk6-browser does not yet support the new `page.getBy` pattern and css lookups can be [1.5x faster](https://serpapi.com/blog/css-selectors-faster-than-getbyrole-playwright/)
|
||||
|
||||
##### Utilizing LocalStorage
|
||||
|
||||
|
@ -212,23 +212,6 @@ const extendedTest = test.extend({
|
||||
.not.toEqual('error')
|
||||
);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Extends the base browser class to enable CDP connection definition in playwright.config.js. Once
|
||||
* that RFE is implemented, this function can be removed.
|
||||
* @see {@link https://github.com/microsoft/playwright/issues/8379 Github RFE}
|
||||
*/
|
||||
browser: async ({ playwright, browser }, use, workerInfo) => {
|
||||
// Use browserless if configured
|
||||
if (workerInfo.project.name.match(/browserless/)) {
|
||||
const vBrowser = await playwright.chromium.connectOverCDP({
|
||||
endpointURL: 'ws://localhost:3003'
|
||||
});
|
||||
await use(vBrowser);
|
||||
} else {
|
||||
// Use Local Browser for testing.
|
||||
await use(browser);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -59,8 +59,8 @@ export async function navigateToFaultManagementWithoutExample(page) {
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
export async function navigateToFaultItemInTree(page) {
|
||||
await page.goto('./', { waitUntil: 'networkidle' });
|
||||
async function navigateToFaultItemInTree(page) {
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
|
||||
const faultManagementTreeItem = page
|
||||
.getByRole('tree', {
|
||||
|
File diff suppressed because one or more lines are too long
@ -41,7 +41,7 @@ test.describe('CouchDB Status Indicator with mocked responses @couchdb', () => {
|
||||
|
||||
//Go to baseURL
|
||||
await page.goto('./#/browse/mine?hideTree=true&hideInspector=true', {
|
||||
waitUntil: 'networkidle'
|
||||
waitUntil: 'domcontentloaded'
|
||||
});
|
||||
await expect(page.locator('div:has-text("CouchDB is connected")').nth(3)).toBeVisible();
|
||||
});
|
||||
@ -56,7 +56,7 @@ test.describe('CouchDB Status Indicator with mocked responses @couchdb', () => {
|
||||
|
||||
//Go to baseURL
|
||||
await page.goto('./#/browse/mine?hideTree=true&hideInspector=true', {
|
||||
waitUntil: 'networkidle'
|
||||
waitUntil: 'domcontentloaded'
|
||||
});
|
||||
await expect(page.locator('div:has-text("CouchDB is offline")').nth(3)).toBeVisible();
|
||||
});
|
||||
@ -71,7 +71,7 @@ test.describe('CouchDB Status Indicator with mocked responses @couchdb', () => {
|
||||
|
||||
//Go to baseURL
|
||||
await page.goto('./#/browse/mine?hideTree=true&hideInspector=true', {
|
||||
waitUntil: 'networkidle'
|
||||
waitUntil: 'domcontentloaded'
|
||||
});
|
||||
await expect(page.locator('div:has-text("CouchDB connectivity unknown")').nth(3)).toBeVisible();
|
||||
});
|
||||
|
@ -188,8 +188,8 @@ test.describe('Persistence operations @couchdb', () => {
|
||||
|
||||
// Both pages: Go to baseURL
|
||||
await Promise.all([
|
||||
page.goto('./', { waitUntil: 'networkidle' }),
|
||||
page2.goto('./', { waitUntil: 'networkidle' })
|
||||
page.goto('./', { waitUntil: 'domcontentloaded' }),
|
||||
page2.goto('./', { waitUntil: 'domcontentloaded' })
|
||||
]);
|
||||
|
||||
//Slow down the test a bit
|
||||
|
@ -68,39 +68,36 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage @2p', () =>
|
||||
});
|
||||
|
||||
//Begin suite of tests again localStorage
|
||||
test.fixme(
|
||||
'Condition set object properties persist in main view and inspector @localStorage',
|
||||
async ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/7421'
|
||||
});
|
||||
//Navigate to baseURL with injected localStorage
|
||||
await page.goto(conditionSetUrl, { waitUntil: 'networkidle' });
|
||||
test('Condition set object properties persist in main view and inspector after reload @localStorage', async ({
|
||||
page
|
||||
}) => {
|
||||
//Navigate to baseURL with injected localStorage
|
||||
await page.goto(conditionSetUrl, { waitUntil: 'domcontentloaded' });
|
||||
|
||||
//Assertions on loaded Condition Set in main view. This is a stateful transition step after page.goto()
|
||||
await expect
|
||||
.soft(page.locator('.l-browse-bar__object-name'))
|
||||
.toContainText('Unnamed Condition Set');
|
||||
//Assertions on loaded Condition Set in main view. This is a stateful transition step after page.goto()
|
||||
await expect.soft(page.getByRole('main')).toContainText('Unnamed Condition Set');
|
||||
|
||||
//Assertions on loaded Condition Set in Inspector
|
||||
expect.soft(page.locator('_vue=item.name=Unnamed Condition Set')).toBeTruthy();
|
||||
//Assertions on loaded Condition Set in Inspector
|
||||
await expect(
|
||||
page.getByLabel('Title inspector properties').getByLabel('inspector property value')
|
||||
).toContainText('Unnamed Condition Set');
|
||||
|
||||
//Reload Page
|
||||
await Promise.all([page.reload(), page.waitForLoadState('networkidle')]);
|
||||
//Reload Page
|
||||
await page.reload({ waitUntil: 'domcontentloaded' });
|
||||
|
||||
//Re-verify after reload
|
||||
await expect.soft(page.getByRole('main')).toContainText('Unnamed Condition Set');
|
||||
|
||||
//Assertions on loaded Condition Set in Inspector
|
||||
await expect(
|
||||
page.getByLabel('Title inspector properties').getByLabel('inspector property value')
|
||||
).toContainText('Unnamed Condition Set');
|
||||
});
|
||||
|
||||
//Re-verify after reload
|
||||
await expect
|
||||
.soft(page.locator('.l-browse-bar__object-name'))
|
||||
.toContainText('Unnamed Condition Set');
|
||||
//Assertions on loaded Condition Set in Inspector
|
||||
expect.soft(page.locator('_vue=item.name=Unnamed Condition Set')).toBeTruthy();
|
||||
}
|
||||
);
|
||||
test('condition set object can be modified on @localStorage', async ({ page, openmctConfig }) => {
|
||||
const { myItemsFolderName } = openmctConfig;
|
||||
|
||||
await page.goto(conditionSetUrl, { waitUntil: 'networkidle' });
|
||||
await page.goto(conditionSetUrl, { waitUntil: 'domcontentloaded' });
|
||||
|
||||
//Assertions on loaded Condition Set in main view. This is a stateful transition step after page.goto()
|
||||
await expect
|
||||
@ -151,7 +148,7 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage @2p', () =>
|
||||
expect(page.locator('a:has-text("Renamed Condition Set")')).toBeTruthy();
|
||||
|
||||
//Reload Page
|
||||
await Promise.all([page.reload(), page.waitForLoadState('networkidle')]);
|
||||
await Promise.all([page.reload(), page.waitForLoadState('domcontentloaded')]);
|
||||
|
||||
//Verify Main section reflects updated Name Property
|
||||
await expect
|
||||
@ -213,343 +210,7 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage @2p', () =>
|
||||
|
||||
//Feature?
|
||||
//Domain Object is still available by direct URL after delete
|
||||
await page.goto(conditionSetUrl, { waitUntil: 'networkidle' });
|
||||
await page.goto(conditionSetUrl, { waitUntil: 'domcontentloaded' });
|
||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Basic Condition Set Use', () => {
|
||||
let conditionSet;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Open a browser, navigate to the main page, and wait until all network events to resolve
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
// Create a new condition set
|
||||
conditionSet = await createDomainObjectWithDefaults(page, {
|
||||
type: 'Condition Set',
|
||||
name: 'Test Condition Set'
|
||||
});
|
||||
});
|
||||
test('Creating a condition defaults the condition name to "Unnamed Condition"', async ({
|
||||
page
|
||||
}) => {
|
||||
await page.goto(conditionSet.url);
|
||||
|
||||
// Change the object to edit mode
|
||||
await page.getByLabel('Edit Object').click();
|
||||
|
||||
// Click Add Condition button
|
||||
await page.locator('#addCondition').click();
|
||||
// Check that the new Unnamed Condition section appears
|
||||
const numOfUnnamedConditions = await page
|
||||
.locator('.c-condition__name', { hasText: 'Unnamed Condition' })
|
||||
.count();
|
||||
expect(numOfUnnamedConditions).toEqual(1);
|
||||
});
|
||||
test('ConditionSet should display appropriate view options', async ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/5924'
|
||||
});
|
||||
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
type: 'Sine Wave Generator',
|
||||
name: 'Alpha Sine Wave Generator'
|
||||
});
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
type: 'Sine Wave Generator',
|
||||
name: 'Beta Sine Wave Generator'
|
||||
});
|
||||
|
||||
await page.goto(conditionSet.url);
|
||||
|
||||
// Change the object to edit mode
|
||||
await page.getByLabel('Edit Object').click();
|
||||
|
||||
// Expand the 'My Items' folder in the left tree
|
||||
page.click('button[title="Show selected item in tree"]');
|
||||
// Add the Alpha & Beta Sine Wave Generator to the Condition Set and save changes
|
||||
const treePane = page.getByRole('tree', {
|
||||
name: 'Main Tree'
|
||||
});
|
||||
const alphaGeneratorTreeItem = treePane.getByRole('treeitem', {
|
||||
name: 'Alpha Sine Wave Generator'
|
||||
});
|
||||
const betaGeneratorTreeItem = treePane.getByRole('treeitem', {
|
||||
name: 'Beta Sine Wave Generator'
|
||||
});
|
||||
const conditionCollection = page.locator('#conditionCollection');
|
||||
|
||||
await alphaGeneratorTreeItem.dragTo(conditionCollection);
|
||||
await betaGeneratorTreeItem.dragTo(conditionCollection);
|
||||
|
||||
await page.locator('button[title="Save"]').click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
|
||||
await page.getByLabel('Open the View Switcher Menu').click();
|
||||
|
||||
await expect(page.getByRole('menuitem', { name: /Lad Table/ })).toBeHidden();
|
||||
await expect(page.getByRole('menuitem', { name: /Conditions View/ })).toBeVisible();
|
||||
await expect(page.getByRole('menuitem', { name: /Plot/ })).toBeVisible();
|
||||
await expect(page.getByRole('menuitem', { name: /Telemetry Table/ })).toBeVisible();
|
||||
await page.getByLabel('Plot').click();
|
||||
await expect(
|
||||
page.getByLabel('Plot Legend Collapsed').getByText('Test Condition Set')
|
||||
).toBeVisible();
|
||||
await page.getByLabel('Open the View Switcher Menu').click();
|
||||
await page.getByLabel('Telemetry Table').click();
|
||||
await expect(page.getByRole('searchbox', { name: 'output filter input' })).toBeVisible();
|
||||
await page.getByLabel('Open the View Switcher Menu').click();
|
||||
await page.getByLabel('Conditions View').click();
|
||||
await expect(page.getByText('Current Output')).toBeVisible();
|
||||
});
|
||||
test('ConditionSet has correct outputs when telemetry is and is not available', async ({
|
||||
page
|
||||
}) => {
|
||||
const exampleTelemetry = await createExampleTelemetryObject(page);
|
||||
|
||||
await page.getByLabel('Show selected item in tree').click();
|
||||
await page.goto(conditionSet.url);
|
||||
// Change the object to edit mode
|
||||
await page.getByLabel('Edit Object').click();
|
||||
|
||||
// Create two conditions
|
||||
await page.locator('#addCondition').click();
|
||||
await page.locator('#addCondition').click();
|
||||
await page.locator('#conditionCollection').getByRole('textbox').nth(0).fill('First Condition');
|
||||
await page.locator('#conditionCollection').getByRole('textbox').nth(1).fill('Second Condition');
|
||||
|
||||
// Add Telemetry to ConditionSet
|
||||
const sineWaveGeneratorTreeItem = page
|
||||
.getByRole('tree', {
|
||||
name: 'Main Tree'
|
||||
})
|
||||
.getByRole('treeitem', {
|
||||
name: exampleTelemetry.name
|
||||
});
|
||||
const conditionCollection = page.locator('#conditionCollection');
|
||||
await sineWaveGeneratorTreeItem.dragTo(conditionCollection);
|
||||
|
||||
// Modify First Criterion
|
||||
const firstCriterionTelemetry = page.locator(
|
||||
'[aria-label="Criterion Telemetry Selection"] >> nth=0'
|
||||
);
|
||||
firstCriterionTelemetry.selectOption({ label: exampleTelemetry.name });
|
||||
const firstCriterionMetadata = page.locator(
|
||||
'[aria-label="Criterion Metadata Selection"] >> nth=0'
|
||||
);
|
||||
firstCriterionMetadata.selectOption({ label: 'Sine' });
|
||||
const firstCriterionComparison = page.locator(
|
||||
'[aria-label="Criterion Comparison Selection"] >> nth=0'
|
||||
);
|
||||
firstCriterionComparison.selectOption({ label: 'is greater than or equal to' });
|
||||
const firstCriterionInput = page.locator('[aria-label="Criterion Input"] >> nth=0');
|
||||
await firstCriterionInput.fill('0');
|
||||
|
||||
// Modify First Criterion
|
||||
const secondCriterionTelemetry = page.locator(
|
||||
'[aria-label="Criterion Telemetry Selection"] >> nth=1'
|
||||
);
|
||||
secondCriterionTelemetry.selectOption({ label: exampleTelemetry.name });
|
||||
|
||||
const secondCriterionMetadata = page.locator(
|
||||
'[aria-label="Criterion Metadata Selection"] >> nth=1'
|
||||
);
|
||||
secondCriterionMetadata.selectOption({ label: 'Sine' });
|
||||
|
||||
const secondCriterionComparison = page.locator(
|
||||
'[aria-label="Criterion Comparison Selection"] >> nth=1'
|
||||
);
|
||||
secondCriterionComparison.selectOption({ label: 'is less than' });
|
||||
|
||||
const secondCriterionInput = page.locator('[aria-label="Criterion Input"] >> nth=1');
|
||||
await secondCriterionInput.fill('0');
|
||||
|
||||
// Save ConditionSet
|
||||
await page.locator('button[title="Save"]').click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
|
||||
// Validate that the condition set is evaluating and outputting
|
||||
// the correct value when the underlying telemetry subscription is active.
|
||||
let outputValue = page.getByLabel('Current Output Value');
|
||||
await expect(outputValue).toHaveText('false');
|
||||
|
||||
await page.goto(exampleTelemetry.url);
|
||||
|
||||
// Edit SWG to add 8 second loading delay to simulate the case
|
||||
// where telemetry is not available.
|
||||
await page.getByTitle('More actions').click();
|
||||
await page.getByRole('menuitem', { name: 'Edit Properties...' }).click();
|
||||
await page.getByRole('spinbutton', { name: 'Loading Delay (ms)' }).fill('8000');
|
||||
await page.getByLabel('Save').click();
|
||||
|
||||
// Expect that the output value is blank or '---' if the
|
||||
// underlying telemetry subscription is not active.
|
||||
await page.goto(conditionSet.url);
|
||||
await expect(outputValue).toHaveText('---');
|
||||
});
|
||||
|
||||
test('ConditionSet has correct outputs when test data is enabled', async ({ page }) => {
|
||||
const exampleTelemetry = await createExampleTelemetryObject(page);
|
||||
|
||||
await page.getByLabel('Show selected item in tree').click();
|
||||
await page.goto(conditionSet.url);
|
||||
// Change the object to edit mode
|
||||
await page.getByLabel('Edit Object').click();
|
||||
|
||||
// Create two conditions
|
||||
await page.locator('#addCondition').click();
|
||||
await page.locator('#addCondition').click();
|
||||
await page.locator('#conditionCollection').getByRole('textbox').nth(0).fill('First Condition');
|
||||
await page.locator('#conditionCollection').getByRole('textbox').nth(1).fill('Second Condition');
|
||||
|
||||
// Add Telemetry to ConditionSet
|
||||
const sineWaveGeneratorTreeItem = page
|
||||
.getByRole('tree', {
|
||||
name: 'Main Tree'
|
||||
})
|
||||
.getByRole('treeitem', {
|
||||
name: exampleTelemetry.name
|
||||
});
|
||||
const conditionCollection = page.locator('#conditionCollection');
|
||||
await sineWaveGeneratorTreeItem.dragTo(conditionCollection);
|
||||
|
||||
// Modify First Criterion
|
||||
const firstCriterionTelemetry = page.locator(
|
||||
'[aria-label="Criterion Telemetry Selection"] >> nth=0'
|
||||
);
|
||||
firstCriterionTelemetry.selectOption({ label: exampleTelemetry.name });
|
||||
const firstCriterionMetadata = page.locator(
|
||||
'[aria-label="Criterion Metadata Selection"] >> nth=0'
|
||||
);
|
||||
firstCriterionMetadata.selectOption({ label: 'Sine' });
|
||||
const firstCriterionComparison = page.locator(
|
||||
'[aria-label="Criterion Comparison Selection"] >> nth=0'
|
||||
);
|
||||
firstCriterionComparison.selectOption({ label: 'is greater than or equal to' });
|
||||
const firstCriterionInput = page.locator('[aria-label="Criterion Input"] >> nth=0');
|
||||
await firstCriterionInput.fill('0');
|
||||
|
||||
// Modify Second Criterion
|
||||
const secondCriterionTelemetry = page.locator(
|
||||
'[aria-label="Criterion Telemetry Selection"] >> nth=1'
|
||||
);
|
||||
await secondCriterionTelemetry.selectOption({ label: exampleTelemetry.name });
|
||||
|
||||
const secondCriterionMetadata = page.locator(
|
||||
'[aria-label="Criterion Metadata Selection"] >> nth=1'
|
||||
);
|
||||
await secondCriterionMetadata.selectOption({ label: 'Sine' });
|
||||
|
||||
const secondCriterionComparison = page.locator(
|
||||
'[aria-label="Criterion Comparison Selection"] >> nth=1'
|
||||
);
|
||||
await secondCriterionComparison.selectOption({ label: 'is less than' });
|
||||
|
||||
const secondCriterionInput = page.locator('[aria-label="Criterion Input"] >> nth=1');
|
||||
await secondCriterionInput.fill('0');
|
||||
|
||||
// Enable test data
|
||||
await page.getByLabel('Apply Test Data').nth(1).click();
|
||||
const testDataTelemetry = page.locator('[aria-label="Test Data Telemetry Selection"] >> nth=0');
|
||||
await testDataTelemetry.selectOption({ label: exampleTelemetry.name });
|
||||
|
||||
const testDataMetadata = page.locator('[aria-label="Test Data Metadata Selection"] >> nth=0');
|
||||
await testDataMetadata.selectOption({ label: 'Sine' });
|
||||
|
||||
const testInput = page.locator('[aria-label="Test Data Input"] >> nth=0');
|
||||
await testInput.fill('0');
|
||||
|
||||
// Validate that the condition set is evaluating and outputting
|
||||
// the correct value when the underlying telemetry subscription is active.
|
||||
let outputValue = page.getByLabel('Current Output Value');
|
||||
await expect(outputValue).toHaveText('false');
|
||||
|
||||
await page.goto(exampleTelemetry.url);
|
||||
});
|
||||
|
||||
test.fixme('Ensure condition sets work with telemetry like operator status', ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/7484'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Condition Set Composition', () => {
|
||||
let conditionSet;
|
||||
let exampleTelemetry;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
|
||||
// Create Condition Set
|
||||
conditionSet = await createDomainObjectWithDefaults(page, {
|
||||
type: 'Condition Set'
|
||||
});
|
||||
|
||||
// Create Telemetry Object as child to Condition Set
|
||||
exampleTelemetry = await createExampleTelemetryObject(page, conditionSet.uuid);
|
||||
|
||||
// Edit Condition Set
|
||||
await page.goto(conditionSet.url);
|
||||
await page.getByRole('button', { name: 'Edit Object' }).click();
|
||||
|
||||
// Add Condition to Condition Set
|
||||
await page.getByRole('button', { name: 'Add Condition' }).click();
|
||||
|
||||
// Enter Condition Output
|
||||
await page.getByLabel('Condition Name Input').first().fill('Negative');
|
||||
await page.getByLabel('Condition Output Type').first().selectOption({ value: 'string' });
|
||||
await page.getByLabel('Condition Output String').first().fill('Negative');
|
||||
|
||||
// Condition Trigger default is okay so no change needed to form
|
||||
|
||||
// Enter Condition Criterion
|
||||
await page.getByLabel('Criterion Telemetry Selection').first().selectOption({ value: 'all' });
|
||||
await page.getByLabel('Criterion Metadata Selection').first().selectOption({ value: 'sin' });
|
||||
await page
|
||||
.locator('select[aria-label="Criterion Comparison Selection"]')
|
||||
.first()
|
||||
.selectOption({ value: 'lessThan' });
|
||||
await page.getByLabel('Criterion Input').first().fill('0');
|
||||
|
||||
// Save the Condition Set
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
});
|
||||
|
||||
test('You can remove telemetry from a condition set with existing conditions', async ({
|
||||
page
|
||||
}) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/7710'
|
||||
});
|
||||
|
||||
await page.getByLabel('Expand My Items folder').click();
|
||||
await page.getByLabel(`Expand ${conditionSet.name} conditionSet`).click();
|
||||
|
||||
await page
|
||||
.getByLabel(`Navigate to ${exampleTelemetry.name}`, { exact: false })
|
||||
.click({ button: 'right' });
|
||||
|
||||
await page
|
||||
.getByLabel(`${exampleTelemetry.name} Context Menu`)
|
||||
.getByRole('menuitem', { name: 'Remove' })
|
||||
.click();
|
||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||
|
||||
await page
|
||||
.getByLabel(`Navigate to ${conditionSet.name} conditionSet Object`, { exact: true })
|
||||
.click();
|
||||
await page.getByRole('button', { name: 'Edit Object' }).click();
|
||||
await page.getByRole('tab', { name: 'Elements' }).click();
|
||||
expect(
|
||||
await page
|
||||
.getByRole('tabpanel', { name: 'Inspector Views' })
|
||||
.getByRole('listitem', { name: exampleTelemetry.name })
|
||||
.count()
|
||||
).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
@ -0,0 +1,368 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*
|
||||
This test suite is dedicated to tests which verify the basic operations surrounding conditionSets. Note: this
|
||||
suite is sharing state between tests which is considered an anti-pattern. Implementing in this way to
|
||||
demonstrate some playwright for test developers. This pattern should not be re-used in other CRUD suites.
|
||||
*/
|
||||
|
||||
import {
|
||||
createDomainObjectWithDefaults,
|
||||
createExampleTelemetryObject
|
||||
} from '../../../../appActions.js';
|
||||
import { expect, test } from '../../../../pluginFixtures.js';
|
||||
|
||||
test.describe('Basic Condition Set Use', () => {
|
||||
let conditionSet;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Open a browser, navigate to the main page, and wait until all network events to resolve
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
// Create a new condition set
|
||||
conditionSet = await createDomainObjectWithDefaults(page, {
|
||||
type: 'Condition Set',
|
||||
name: 'Test Condition Set'
|
||||
});
|
||||
});
|
||||
test('Creating a condition defaults the condition name to "Unnamed Condition"', async ({
|
||||
page
|
||||
}) => {
|
||||
await page.goto(conditionSet.url);
|
||||
|
||||
// Change the object to edit mode
|
||||
await page.getByLabel('Edit Object').click();
|
||||
|
||||
// Click Add Condition button
|
||||
await page.locator('#addCondition').click();
|
||||
// Check that the new Unnamed Condition section appears
|
||||
const numOfUnnamedConditions = await page
|
||||
.locator('.c-condition__name', { hasText: 'Unnamed Condition' })
|
||||
.count();
|
||||
expect(numOfUnnamedConditions).toEqual(1);
|
||||
});
|
||||
test('ConditionSet should display appropriate view options', async ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/5924'
|
||||
});
|
||||
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
type: 'Sine Wave Generator',
|
||||
name: 'Alpha Sine Wave Generator'
|
||||
});
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
type: 'Sine Wave Generator',
|
||||
name: 'Beta Sine Wave Generator'
|
||||
});
|
||||
|
||||
await page.goto(conditionSet.url);
|
||||
|
||||
// Change the object to edit mode
|
||||
await page.getByLabel('Edit Object').click();
|
||||
|
||||
// Expand the 'My Items' folder in the left tree
|
||||
page.click('button[title="Show selected item in tree"]');
|
||||
// Add the Alpha & Beta Sine Wave Generator to the Condition Set and save changes
|
||||
const treePane = page.getByRole('tree', {
|
||||
name: 'Main Tree'
|
||||
});
|
||||
const alphaGeneratorTreeItem = treePane.getByRole('treeitem', {
|
||||
name: 'Alpha Sine Wave Generator'
|
||||
});
|
||||
const betaGeneratorTreeItem = treePane.getByRole('treeitem', {
|
||||
name: 'Beta Sine Wave Generator'
|
||||
});
|
||||
const conditionCollection = page.locator('#conditionCollection');
|
||||
|
||||
await alphaGeneratorTreeItem.dragTo(conditionCollection);
|
||||
await betaGeneratorTreeItem.dragTo(conditionCollection);
|
||||
|
||||
await page.locator('button[title="Save"]').click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
|
||||
await page.getByLabel('Open the View Switcher Menu').click();
|
||||
|
||||
await expect(page.getByRole('menuitem', { name: /Lad Table/ })).toBeHidden();
|
||||
await expect(page.getByRole('menuitem', { name: /Conditions View/ })).toBeVisible();
|
||||
await expect(page.getByRole('menuitem', { name: /Plot/ })).toBeVisible();
|
||||
await expect(page.getByRole('menuitem', { name: /Telemetry Table/ })).toBeVisible();
|
||||
await page.getByLabel('Plot').click();
|
||||
await expect(
|
||||
page.getByLabel('Plot Legend Collapsed').getByText('Test Condition Set')
|
||||
).toBeVisible();
|
||||
await page.getByLabel('Open the View Switcher Menu').click();
|
||||
await page.getByLabel('Telemetry Table').click();
|
||||
await expect(page.getByRole('searchbox', { name: 'output filter input' })).toBeVisible();
|
||||
await page.getByLabel('Open the View Switcher Menu').click();
|
||||
await page.getByLabel('Conditions View').click();
|
||||
await expect(page.getByText('Current Output')).toBeVisible();
|
||||
});
|
||||
test('ConditionSet has correct outputs when telemetry is and is not available', async ({
|
||||
page
|
||||
}) => {
|
||||
const exampleTelemetry = await createExampleTelemetryObject(page);
|
||||
|
||||
await page.getByLabel('Show selected item in tree').click();
|
||||
await page.goto(conditionSet.url);
|
||||
// Change the object to edit mode
|
||||
await page.getByLabel('Edit Object').click();
|
||||
|
||||
// Create two conditions
|
||||
await page.locator('#addCondition').click();
|
||||
await page.locator('#addCondition').click();
|
||||
await page.locator('#conditionCollection').getByRole('textbox').nth(0).fill('First Condition');
|
||||
await page.locator('#conditionCollection').getByRole('textbox').nth(1).fill('Second Condition');
|
||||
|
||||
// Add Telemetry to ConditionSet
|
||||
const sineWaveGeneratorTreeItem = page
|
||||
.getByRole('tree', {
|
||||
name: 'Main Tree'
|
||||
})
|
||||
.getByRole('treeitem', {
|
||||
name: exampleTelemetry.name
|
||||
});
|
||||
const conditionCollection = page.locator('#conditionCollection');
|
||||
await sineWaveGeneratorTreeItem.dragTo(conditionCollection);
|
||||
|
||||
// Modify First Criterion
|
||||
const firstCriterionTelemetry = page.locator(
|
||||
'[aria-label="Criterion Telemetry Selection"] >> nth=0'
|
||||
);
|
||||
firstCriterionTelemetry.selectOption({ label: exampleTelemetry.name });
|
||||
const firstCriterionMetadata = page.locator(
|
||||
'[aria-label="Criterion Metadata Selection"] >> nth=0'
|
||||
);
|
||||
firstCriterionMetadata.selectOption({ label: 'Sine' });
|
||||
const firstCriterionComparison = page.locator(
|
||||
'[aria-label="Criterion Comparison Selection"] >> nth=0'
|
||||
);
|
||||
firstCriterionComparison.selectOption({ label: 'is greater than or equal to' });
|
||||
const firstCriterionInput = page.locator('[aria-label="Criterion Input"] >> nth=0');
|
||||
await firstCriterionInput.fill('0');
|
||||
|
||||
// Modify First Criterion
|
||||
const secondCriterionTelemetry = page.locator(
|
||||
'[aria-label="Criterion Telemetry Selection"] >> nth=1'
|
||||
);
|
||||
secondCriterionTelemetry.selectOption({ label: exampleTelemetry.name });
|
||||
|
||||
const secondCriterionMetadata = page.locator(
|
||||
'[aria-label="Criterion Metadata Selection"] >> nth=1'
|
||||
);
|
||||
secondCriterionMetadata.selectOption({ label: 'Sine' });
|
||||
|
||||
const secondCriterionComparison = page.locator(
|
||||
'[aria-label="Criterion Comparison Selection"] >> nth=1'
|
||||
);
|
||||
secondCriterionComparison.selectOption({ label: 'is less than' });
|
||||
|
||||
const secondCriterionInput = page.locator('[aria-label="Criterion Input"] >> nth=1');
|
||||
await secondCriterionInput.fill('0');
|
||||
|
||||
// Save ConditionSet
|
||||
await page.locator('button[title="Save"]').click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
|
||||
// Validate that the condition set is evaluating and outputting
|
||||
// the correct value when the underlying telemetry subscription is active.
|
||||
let outputValue = page.getByLabel('Current Output Value');
|
||||
await expect(outputValue).toHaveText('false');
|
||||
|
||||
await page.goto(exampleTelemetry.url);
|
||||
|
||||
// Edit SWG to add 8 second loading delay to simulate the case
|
||||
// where telemetry is not available.
|
||||
await page.getByTitle('More actions').click();
|
||||
await page.getByRole('menuitem', { name: 'Edit Properties...' }).click();
|
||||
await page.getByRole('spinbutton', { name: 'Loading Delay (ms)' }).fill('8000');
|
||||
await page.getByLabel('Save').click();
|
||||
|
||||
// Expect that the output value is blank or '---' if the
|
||||
// underlying telemetry subscription is not active.
|
||||
await page.goto(conditionSet.url);
|
||||
await expect(outputValue).toHaveText('---');
|
||||
});
|
||||
|
||||
test('ConditionSet has correct outputs when test data is enabled', async ({ page }) => {
|
||||
const exampleTelemetry = await createExampleTelemetryObject(page);
|
||||
|
||||
await page.getByLabel('Show selected item in tree').click();
|
||||
await page.goto(conditionSet.url);
|
||||
// Change the object to edit mode
|
||||
await page.getByLabel('Edit Object').click();
|
||||
|
||||
// Create two conditions
|
||||
await page.locator('#addCondition').click();
|
||||
await page.locator('#addCondition').click();
|
||||
await page.locator('#conditionCollection').getByRole('textbox').nth(0).fill('First Condition');
|
||||
await page.locator('#conditionCollection').getByRole('textbox').nth(1).fill('Second Condition');
|
||||
|
||||
// Add Telemetry to ConditionSet
|
||||
const sineWaveGeneratorTreeItem = page
|
||||
.getByRole('tree', {
|
||||
name: 'Main Tree'
|
||||
})
|
||||
.getByRole('treeitem', {
|
||||
name: exampleTelemetry.name
|
||||
});
|
||||
const conditionCollection = page.locator('#conditionCollection');
|
||||
await sineWaveGeneratorTreeItem.dragTo(conditionCollection);
|
||||
|
||||
// Modify First Criterion
|
||||
const firstCriterionTelemetry = page.locator(
|
||||
'[aria-label="Criterion Telemetry Selection"] >> nth=0'
|
||||
);
|
||||
firstCriterionTelemetry.selectOption({ label: exampleTelemetry.name });
|
||||
const firstCriterionMetadata = page.locator(
|
||||
'[aria-label="Criterion Metadata Selection"] >> nth=0'
|
||||
);
|
||||
firstCriterionMetadata.selectOption({ label: 'Sine' });
|
||||
const firstCriterionComparison = page.locator(
|
||||
'[aria-label="Criterion Comparison Selection"] >> nth=0'
|
||||
);
|
||||
firstCriterionComparison.selectOption({ label: 'is greater than or equal to' });
|
||||
const firstCriterionInput = page.locator('[aria-label="Criterion Input"] >> nth=0');
|
||||
await firstCriterionInput.fill('0');
|
||||
|
||||
// Modify Second Criterion
|
||||
const secondCriterionTelemetry = page.locator(
|
||||
'[aria-label="Criterion Telemetry Selection"] >> nth=1'
|
||||
);
|
||||
await secondCriterionTelemetry.selectOption({ label: exampleTelemetry.name });
|
||||
|
||||
const secondCriterionMetadata = page.locator(
|
||||
'[aria-label="Criterion Metadata Selection"] >> nth=1'
|
||||
);
|
||||
await secondCriterionMetadata.selectOption({ label: 'Sine' });
|
||||
|
||||
const secondCriterionComparison = page.locator(
|
||||
'[aria-label="Criterion Comparison Selection"] >> nth=1'
|
||||
);
|
||||
await secondCriterionComparison.selectOption({ label: 'is less than' });
|
||||
|
||||
const secondCriterionInput = page.locator('[aria-label="Criterion Input"] >> nth=1');
|
||||
await secondCriterionInput.fill('0');
|
||||
|
||||
// Enable test data
|
||||
await page.getByLabel('Apply Test Data').nth(1).click();
|
||||
const testDataTelemetry = page.locator('[aria-label="Test Data Telemetry Selection"] >> nth=0');
|
||||
await testDataTelemetry.selectOption({ label: exampleTelemetry.name });
|
||||
|
||||
const testDataMetadata = page.locator('[aria-label="Test Data Metadata Selection"] >> nth=0');
|
||||
await testDataMetadata.selectOption({ label: 'Sine' });
|
||||
|
||||
const testInput = page.locator('[aria-label="Test Data Input"] >> nth=0');
|
||||
await testInput.fill('0');
|
||||
|
||||
// Validate that the condition set is evaluating and outputting
|
||||
// the correct value when the underlying telemetry subscription is active.
|
||||
let outputValue = page.getByLabel('Current Output Value');
|
||||
await expect(outputValue).toHaveText('false');
|
||||
|
||||
await page.goto(exampleTelemetry.url);
|
||||
});
|
||||
|
||||
test.fixme('Ensure condition sets work with telemetry like operator status', ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/7484'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Condition Set Composition', () => {
|
||||
let conditionSet;
|
||||
let exampleTelemetry;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
|
||||
// Create Condition Set
|
||||
conditionSet = await createDomainObjectWithDefaults(page, {
|
||||
type: 'Condition Set'
|
||||
});
|
||||
|
||||
// Create Telemetry Object as child to Condition Set
|
||||
exampleTelemetry = await createExampleTelemetryObject(page, conditionSet.uuid);
|
||||
|
||||
// Edit Condition Set
|
||||
await page.goto(conditionSet.url);
|
||||
await page.getByRole('button', { name: 'Edit Object' }).click();
|
||||
|
||||
// Add Condition to Condition Set
|
||||
await page.getByRole('button', { name: 'Add Condition' }).click();
|
||||
|
||||
// Enter Condition Output
|
||||
await page.getByLabel('Condition Name Input').first().fill('Negative');
|
||||
await page.getByLabel('Condition Output Type').first().selectOption({ value: 'string' });
|
||||
await page.getByLabel('Condition Output String').first().fill('Negative');
|
||||
|
||||
// Condition Trigger default is okay so no change needed to form
|
||||
|
||||
// Enter Condition Criterion
|
||||
await page.getByLabel('Criterion Telemetry Selection').first().selectOption({ value: 'all' });
|
||||
await page.getByLabel('Criterion Metadata Selection').first().selectOption({ value: 'sin' });
|
||||
await page
|
||||
.locator('select[aria-label="Criterion Comparison Selection"]')
|
||||
.first()
|
||||
.selectOption({ value: 'lessThan' });
|
||||
await page.getByLabel('Criterion Input').first().fill('0');
|
||||
|
||||
// Save the Condition Set
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
});
|
||||
|
||||
test('You can remove telemetry from a condition set with existing conditions', async ({
|
||||
page
|
||||
}) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/7710'
|
||||
});
|
||||
|
||||
await page.getByLabel('Expand My Items folder').click();
|
||||
await page.getByLabel(`Expand ${conditionSet.name} conditionSet`).click();
|
||||
|
||||
await page
|
||||
.getByLabel(`Navigate to ${exampleTelemetry.name}`, { exact: false })
|
||||
.click({ button: 'right' });
|
||||
|
||||
await page
|
||||
.getByLabel(`${exampleTelemetry.name} Context Menu`)
|
||||
.getByRole('menuitem', { name: 'Remove' })
|
||||
.click();
|
||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||
|
||||
await page
|
||||
.getByLabel(`Navigate to ${conditionSet.name} conditionSet Object`, { exact: true })
|
||||
.click();
|
||||
await page.getByRole('button', { name: 'Edit Object' }).click();
|
||||
await page.getByRole('tab', { name: 'Elements' }).click();
|
||||
expect(
|
||||
await page
|
||||
.getByRole('tabpanel', { name: 'Inspector Views' })
|
||||
.getByRole('listitem', { name: exampleTelemetry.name })
|
||||
.count()
|
||||
).toEqual(0);
|
||||
});
|
||||
});
|
@ -519,7 +519,7 @@ test.describe('Display Layout', () => {
|
||||
await page.reload();
|
||||
|
||||
// wait for annotations requests to be batched and requested
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
// Network requests for the composite telemetry with multiple items should be:
|
||||
// 1. a single batched request for annotations
|
||||
expect(networkRequests.length).toBe(1);
|
||||
@ -531,7 +531,7 @@ test.describe('Display Layout', () => {
|
||||
await page.reload();
|
||||
|
||||
// wait for annotations to not load (if we have any, we've got a problem)
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
|
||||
// In real time mode, we don't fetch annotations at all
|
||||
expect(networkRequests.length).toBe(0);
|
||||
|
@ -136,14 +136,11 @@ test.describe('Gauge', () => {
|
||||
// TODO: Verify changes in the UI
|
||||
});
|
||||
|
||||
test.fixme('Gauge does not display NaN when data not available', async ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/7421'
|
||||
});
|
||||
test('Gauge does not display NaN when data not available', async ({ page }) => {
|
||||
// Create a Gauge
|
||||
const gauge = await createDomainObjectWithDefaults(page, {
|
||||
type: 'Gauge'
|
||||
type: 'Gauge',
|
||||
name: 'Gauge with no data'
|
||||
});
|
||||
|
||||
// Create a Sine Wave Generator in the Gauge with a loading delay
|
||||
@ -154,7 +151,7 @@ test.describe('Gauge', () => {
|
||||
await page.getByRole('menuitem', { name: /Edit Properties.../ }).click();
|
||||
|
||||
//Edit Example Telemetry Object to include 5s loading Delay
|
||||
await page.locator('[aria-label="Loading Delay \\(ms\\)"]').fill('5000');
|
||||
await page.getByLabel('Loading Delay (ms)', { exact: true }).fill('5000');
|
||||
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
@ -162,9 +159,13 @@ test.describe('Gauge', () => {
|
||||
await page.waitForURL(`**/${gauge.uuid}/*`);
|
||||
|
||||
// Nav to the Gauge
|
||||
await page.goto(gauge.url);
|
||||
const gaugeNoDataText = await page.locator('.js-dial-current-value tspan').textContent();
|
||||
expect(gaugeNoDataText).toBe('--');
|
||||
await page.goto(gauge.url, { waitUntil: 'domcontentloaded' });
|
||||
// Check that the value is not displayed
|
||||
//TODO https://github.com/nasa/openmct/issues/7790 update this locator
|
||||
await expect(page.getByTitle('Value is currently out of')).toHaveAttribute(
|
||||
'aria-valuenow',
|
||||
'--'
|
||||
);
|
||||
});
|
||||
|
||||
test('Gauge enforces composition policy', async ({ page }) => {
|
||||
|
@ -557,7 +557,38 @@ test.describe('Example Imagery in Flexible layout @clock', () => {
|
||||
await page.getByRole('button', { name: 'Close' }).click();
|
||||
});
|
||||
|
||||
test('Imagery View operations @clock', async ({ page, browserName }) => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
|
||||
flexibleLayout = await createDomainObjectWithDefaults(page, { type: 'Flexible Layout' });
|
||||
await page.goto(flexibleLayout.url);
|
||||
|
||||
/* Create Sine Wave Generator with minimum Image Load Delay */
|
||||
// Click the Create button
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
|
||||
// Click text=Example Imagery
|
||||
await page.click('li[role="menuitem"]:has-text("Example Imagery")');
|
||||
|
||||
// Clear and set Image load delay to minimum value
|
||||
await page.locator('input[type="number"]').fill('');
|
||||
await page.locator('input[type="number"]').fill('5000');
|
||||
|
||||
// Click text=OK
|
||||
await Promise.all([
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
|
||||
page.click('button:has-text("OK")'),
|
||||
//Wait for Save Banner to appear
|
||||
page.waitForSelector('.c-message-banner__message')
|
||||
]);
|
||||
|
||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText(
|
||||
'Unnamed Example Imagery'
|
||||
);
|
||||
|
||||
await page.goto(flexibleLayout.url);
|
||||
});
|
||||
test('Imagery View operations @unstable', async ({ page, browserName }) => {
|
||||
test.fixme(browserName === 'firefox', 'This test needs to be updated to work with firefox');
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
@ -594,7 +625,7 @@ test.describe('Example Imagery in Tabs View @clock', () => {
|
||||
|
||||
// Click text=OK
|
||||
await Promise.all([
|
||||
page.waitForNavigation({ waitUntil: 'networkidle' }),
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
|
||||
page.click('button:has-text("OK")'),
|
||||
//Wait for Save Banner to appear
|
||||
page.waitForSelector('.c-message-banner__message')
|
||||
@ -1049,19 +1080,14 @@ async function createImageryViewWithShortDelay(page, { name, parent }) {
|
||||
await page.getByLabel('More actions').click();
|
||||
await page.getByLabel('Edit Properties').click();
|
||||
// Clear and set Image load delay to minimum value
|
||||
await page.locator('input[type="number"]').fill(`${IMAGE_LOAD_DELAY}`);
|
||||
await page.getByLabel('Save').click();
|
||||
}
|
||||
await page.locator('input[type="number"]').fill('');
|
||||
await page.locator('input[type="number"]').fill('5000');
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
// eslint-disable-next-line require-await
|
||||
async function waitForZoomAndPanTransitions(page) {
|
||||
// Wait for image to stabilize
|
||||
await page.getByLabel('Focused Image Element').hover({ trial: true });
|
||||
// Wait for zoom to end
|
||||
await expect(page.getByLabel('Focused Image Element')).not.toHaveClass(/is-zooming|is-panning/);
|
||||
// Wait for image to stabilize
|
||||
await page.getByLabel('Focused Image Element').hover({ trial: true });
|
||||
// Click text=OK
|
||||
await Promise.all([
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
|
||||
page.click('button:has-text("OK")'),
|
||||
//Wait for Save Banner to appear
|
||||
page.waitForSelector('.c-message-banner__message')
|
||||
]);
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ test.describe('ExportAsJSON Disabled Actions', () => {
|
||||
test.describe('ExportAsJSON ProgressBar @couchdb', () => {
|
||||
let folder;
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('./', { waitUntil: 'networkidle' });
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
// Perform actions to create the domain object
|
||||
folder = await createDomainObjectWithDefaults(page, {
|
||||
type: 'Folder'
|
||||
|
@ -37,7 +37,7 @@ test.describe('Notebook Tests with CouchDB @couchdb', () => {
|
||||
|
||||
// Create Notebook
|
||||
testNotebook = await createDomainObjectWithDefaults(page, { type: 'Notebook' });
|
||||
await page.goto(testNotebook.url, { waitUntil: 'networkidle' });
|
||||
await page.goto(testNotebook.url, { waitUntil: 'domcontentloaded' });
|
||||
});
|
||||
|
||||
test('Inspect Notebook Entry Network Requests', async ({ page }) => {
|
||||
@ -58,7 +58,7 @@ test.describe('Notebook Tests with CouchDB @couchdb', () => {
|
||||
page.click('[aria-label="Add Page"]')
|
||||
]);
|
||||
// Ensures that there are no other network requests
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
|
||||
// Assert that only two requests are made
|
||||
// Network Requests are:
|
||||
@ -77,7 +77,7 @@ test.describe('Notebook Tests with CouchDB @couchdb', () => {
|
||||
// 2) The shared worker event from 👆 POST request
|
||||
notebookElementsRequests = [];
|
||||
await nbUtils.enterTextEntry(page, 'First Entry');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
expect(notebookElementsRequests.length).toBeLessThanOrEqual(2);
|
||||
|
||||
// Add some tags
|
||||
@ -141,7 +141,7 @@ test.describe('Notebook Tests with CouchDB @couchdb', () => {
|
||||
// 4) The shared worker event from 👆 POST request
|
||||
notebookElementsRequests = [];
|
||||
await nbUtils.enterTextEntry(page, 'Fourth Entry');
|
||||
page.waitForLoadState('networkidle');
|
||||
page.waitForLoadState('domcontentloaded');
|
||||
|
||||
expect(filterNonFetchRequests(notebookElementsRequests).length).toBeLessThanOrEqual(4);
|
||||
|
||||
@ -153,7 +153,7 @@ test.describe('Notebook Tests with CouchDB @couchdb', () => {
|
||||
// 4) The shared worker event from 👆 POST request
|
||||
notebookElementsRequests = [];
|
||||
await nbUtils.enterTextEntry(page, 'Fifth Entry');
|
||||
page.waitForLoadState('networkidle');
|
||||
page.waitForLoadState('domcontentloaded');
|
||||
|
||||
expect(filterNonFetchRequests(notebookElementsRequests).length).toBeLessThanOrEqual(4);
|
||||
|
||||
@ -164,7 +164,7 @@ test.describe('Notebook Tests with CouchDB @couchdb', () => {
|
||||
// 4) The shared worker event from 👆 POST request
|
||||
notebookElementsRequests = [];
|
||||
await nbUtils.enterTextEntry(page, 'Sixth Entry');
|
||||
page.waitForLoadState('networkidle');
|
||||
page.waitForLoadState('domcontentloaded');
|
||||
|
||||
expect(filterNonFetchRequests(notebookElementsRequests).length).toBeLessThanOrEqual(4);
|
||||
});
|
||||
@ -227,7 +227,7 @@ async function addTagAndAwaitNetwork(page, tagName) {
|
||||
page.locator(`[aria-label="Autocomplete Options"] >> text=${tagName}`).click(),
|
||||
expect(page.locator(`[aria-label="Tag"]:has-text("${tagName}")`)).toBeVisible()
|
||||
]);
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -246,5 +246,5 @@ async function removeTagAndAwaitNetwork(page, tagName) {
|
||||
)
|
||||
]);
|
||||
await expect(page.locator(`[aria-label="Tag"]:has-text("${tagName}")`)).toBeHidden();
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
}
|
||||
|
@ -61,12 +61,11 @@ test.describe('Autoscale', () => {
|
||||
await page.getByLabel('Edit Object').click();
|
||||
|
||||
await page.getByRole('tab', { name: 'Config' }).click();
|
||||
|
||||
// turn off autoscale
|
||||
await page.getByRole('checkbox', { name: 'Auto scale' }).uncheck();
|
||||
|
||||
await page.getByLabel('Y Axis 1 Minimum value').fill('-2');
|
||||
await page.getByLabel('Y Axis 1 Maximum value').fill('2');
|
||||
await page.getByRole('spinbutton').first().fill(-2);
|
||||
// set maximum value
|
||||
await page.getByRole('spinbutton').nth(1).fill(2);
|
||||
|
||||
// save
|
||||
await page.click('button[title="Save"]');
|
||||
|
@ -69,8 +69,7 @@ test.describe('Handle missing object for plots', () => {
|
||||
}, jsonData);
|
||||
|
||||
//Reloads page and clicks on stacked plot
|
||||
await page.reload({ waitUntil: 'domcontentloaded' });
|
||||
await page.goto(stackedPlot.url);
|
||||
await Promise.all([page.reload(), page.waitForLoadState('domcontentloaded')]);
|
||||
|
||||
//Verify Main section is there on load
|
||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText(stackedPlot.name);
|
||||
@ -81,3 +80,84 @@ test.describe('Handle missing object for plots', () => {
|
||||
expect(warningReceived).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* This is used the create a stacked plot object
|
||||
* @private
|
||||
*/
|
||||
async function makeStackedPlot(page, myItemsFolderName) {
|
||||
// fresh page with time range from 2022-03-29 22:00:00.000Z to 2022-03-29 22:00:30.000Z
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
|
||||
// create stacked plot
|
||||
await page.locator('button.c-create-button').click();
|
||||
await page.locator('li[role="menuitem"]:has-text("Stacked Plot")').click();
|
||||
|
||||
await Promise.all([
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
|
||||
page.locator('button:has-text("OK")').click(),
|
||||
//Wait for Save Banner to appear
|
||||
page.waitForSelector('.c-message-banner__message')
|
||||
]);
|
||||
|
||||
// save the stacked plot
|
||||
await saveStackedPlot(page);
|
||||
|
||||
// create a sinewave generator
|
||||
await createSineWaveGenerator(page);
|
||||
|
||||
// click on stacked plot
|
||||
await page.locator(`text=Open MCT ${myItemsFolderName} >> span`).nth(3).click();
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.locator('text=Unnamed Stacked Plot').first().click()
|
||||
]);
|
||||
|
||||
// create a second sinewave generator
|
||||
await createSineWaveGenerator(page);
|
||||
|
||||
// click on stacked plot
|
||||
await page.locator(`text=Open MCT ${myItemsFolderName} >> span`).nth(3).click();
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.locator('text=Unnamed Stacked Plot').first().click()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used to save a stacked plot object
|
||||
* @private
|
||||
*/
|
||||
async function saveStackedPlot(page) {
|
||||
// save stacked plot
|
||||
await page
|
||||
.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button')
|
||||
.nth(1)
|
||||
.click();
|
||||
|
||||
await Promise.all([
|
||||
page.locator('text=Save and Finish Editing').click(),
|
||||
//Wait for Save Banner to appear
|
||||
page.waitForSelector('.c-message-banner__message')
|
||||
]);
|
||||
//Wait until Save Banner is gone
|
||||
await page.locator('.c-message-banner__close-button').click();
|
||||
await page.waitForSelector('.c-message-banner__message', { state: 'detached' });
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used to create a sine wave generator object
|
||||
* @private
|
||||
*/
|
||||
async function createSineWaveGenerator(page) {
|
||||
//Create sine wave generator
|
||||
await page.locator('button.c-create-button').click();
|
||||
await page.locator('li[role="menuitem"]:has-text("Sine Wave Generator")').click();
|
||||
|
||||
await Promise.all([
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
|
||||
page.locator('button:has-text("OK")').click(),
|
||||
//Wait for Save Banner to appear
|
||||
page.waitForSelector('.c-message-banner__message')
|
||||
]);
|
||||
}
|
||||
|
@ -309,32 +309,26 @@ test.describe('Overlay Plot', () => {
|
||||
expect(yAxis3Group.getByRole('listitem').nth(0).getByText(swgB.name)).toBeTruthy();
|
||||
});
|
||||
|
||||
test.fixme(
|
||||
'Clicking on an item in the elements pool brings up the plot preview with data points',
|
||||
async ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/7421'
|
||||
});
|
||||
test('Clicking on an item in the elements pool brings up the plot preview with data points', async ({
|
||||
page
|
||||
}) => {
|
||||
const swgA = await createDomainObjectWithDefaults(page, {
|
||||
type: 'Sine Wave Generator',
|
||||
parent: overlayPlot.uuid
|
||||
});
|
||||
|
||||
const swgA = await createDomainObjectWithDefaults(page, {
|
||||
type: 'Sine Wave Generator',
|
||||
parent: overlayPlot.uuid
|
||||
});
|
||||
await page.goto(overlayPlot.url);
|
||||
// Wait for plot series data to load and be drawn
|
||||
await waitForPlotsToRender(page);
|
||||
await page.getByLabel('Edit Object').click();
|
||||
|
||||
await page.goto(overlayPlot.url);
|
||||
// Wait for plot series data to load and be drawn
|
||||
await waitForPlotsToRender(page);
|
||||
await page.getByLabel('Edit Object').click();
|
||||
await page.getByRole('tab', { name: 'Elements' }).click();
|
||||
|
||||
await page.getByRole('tab', { name: 'Elements' }).click();
|
||||
|
||||
await page.locator(`#inspector-elements-tree >> text=${swgA.name}`).click();
|
||||
const plotPixels = await getCanvasPixels(page, '.js-overlay canvas');
|
||||
const plotPixelSize = plotPixels.length;
|
||||
expect(plotPixelSize).toBeGreaterThan(0);
|
||||
}
|
||||
);
|
||||
await page.locator(`#inspector-elements-tree >> text=${swgA.name}`).click();
|
||||
const plotPixels = await getCanvasPixels(page, '.js-overlay canvas');
|
||||
const plotPixelSize = plotPixels.length;
|
||||
expect(plotPixelSize).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('Can remove an item via the elements pool action menu', async ({ page }) => {
|
||||
const swgA = await createDomainObjectWithDefaults(page, {
|
||||
|
@ -77,12 +77,12 @@ test.describe('Plot Controls', () => {
|
||||
await page.getByLabel('Edit Object').click();
|
||||
|
||||
await page.getByRole('tab', { name: 'Config' }).click();
|
||||
|
||||
// turn off autoscale
|
||||
await page.getByRole('checkbox', { name: 'Auto scale' }).uncheck();
|
||||
|
||||
await page.getByLabel('Y Axis 1 Minimum value').fill('-1');
|
||||
await page.getByLabel('Y Axis 1 Maximum value').fill('1');
|
||||
// set minimum value
|
||||
await page.getByRole('spinbutton').first().fill(-1);
|
||||
// set maximum value
|
||||
await page.getByRole('spinbutton').nth(1).fill(1);
|
||||
|
||||
// save
|
||||
await page.click('button[title="Save"]');
|
||||
|
@ -84,11 +84,7 @@ test.describe('Plot Rendering', () => {
|
||||
await expect(page.getByLabel('Time Conductor Mode')).toHaveText('Fixed Timespan');
|
||||
});
|
||||
|
||||
test.fixme('Plot is rendered when infinity values exist', async ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/7421'
|
||||
});
|
||||
test('Plot is rendered when infinity values exist', async ({ page }) => {
|
||||
// Edit Plot
|
||||
await editSineWaveToUseInfinityOption(page, sineWaveGeneratorObject);
|
||||
|
||||
|
@ -30,7 +30,7 @@ import { expect, test } from '../../baseFixtures.js';
|
||||
test.describe('Renaming objects', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Go to baseURL
|
||||
await page.goto('./', { waitUntil: 'networkidle' });
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
});
|
||||
|
||||
test('When renaming objects, the browse bar and various components all update', async ({
|
||||
|
@ -21,16 +21,7 @@
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
This test suite is dedicated to tests which can quickly verify that any openmct installation is
|
||||
operable and that any type of testing can proceed.
|
||||
|
||||
Ideally, smoke tests should make zero assumptions about how and where they are run. This makes them
|
||||
more resilient to change and therefor a better indicator of failure. Smoke tests will also run quickly
|
||||
as they cover a very "thin surface" of functionality.
|
||||
|
||||
When deciding between authoring new smoke tests or functional tests, ask yourself "would I feel
|
||||
comfortable running this test during a live mission?" Avoid creating or deleting Domain Objects.
|
||||
Make no assumptions about the order that elements appear in the DOM.
|
||||
This suite is dedicated to tests which verify that tooltips are displayed correctly.
|
||||
*/
|
||||
|
||||
import { createDomainObjectWithDefaults, expandEntireTree } from '../../appActions.js';
|
||||
@ -48,7 +39,7 @@ test.describe('Verify tooltips', () => {
|
||||
const swg2Path = 'My Items / Folder Foo / Folder Bar / SWG 2';
|
||||
const swg3Path = 'My Items / Folder Foo / Folder Bar / Folder Baz / SWG 3';
|
||||
|
||||
test.beforeEach(async ({ page, openmctConfig }) => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
|
||||
folder1 = await createDomainObjectWithDefaults(page, {
|
||||
@ -89,7 +80,7 @@ test.describe('Verify tooltips', () => {
|
||||
await expandEntireTree(page);
|
||||
});
|
||||
|
||||
test('display correct paths for LAD tables', async ({ page, openmctConfig }) => {
|
||||
test('display correct paths for LAD tables', async ({ page }) => {
|
||||
// Create LAD table
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
type: 'LAD Table',
|
||||
@ -98,25 +89,32 @@ test.describe('Verify tooltips', () => {
|
||||
// Edit LAD table
|
||||
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');
|
||||
await page.dragAndDrop(`text=${sineWaveObject2.name}`, '.c-lad-table-wrapper');
|
||||
await page.dragAndDrop(`text=${sineWaveObject3.name}`, '.c-lad-table-wrapper');
|
||||
await page.locator('button[title="Save"]').click();
|
||||
// Add the Sine Wave Generator to the LAD table and save changes.
|
||||
//TODO Follow up with https://github.com/nasa/openmct/issues/7773
|
||||
await page.dragAndDrop(`text=${sineWaveObject1.name}`, '#lad-table-drop-area');
|
||||
await page.dragAndDrop(`text=${sineWaveObject2.name}`, '#lad-table-drop-area');
|
||||
await page.dragAndDrop(`text=${sineWaveObject3.name}`, '#lad-table-drop-area');
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
|
||||
await page.keyboard.down('Control');
|
||||
//Hover on something else
|
||||
await page.getByRole('button', { name: 'Create' }).hover();
|
||||
//Hover over the first
|
||||
await page.getByLabel('lad name').getByText(sineWaveObject1.name).hover();
|
||||
await expect(page.getByRole('tooltip', { name: sineWaveObject1.path })).toBeVisible();
|
||||
|
||||
async function getToolTip(object) {
|
||||
await page.locator('.c-create-button').hover();
|
||||
await page.getByLabel('lad name').getByText(object.name).hover();
|
||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
return tooltipText.replace('\n', '').trim();
|
||||
}
|
||||
//Hover on something else
|
||||
await page.getByRole('button', { name: 'Create' }).hover();
|
||||
//Hover over second object
|
||||
await page.getByLabel('lad name').getByText(sineWaveObject2.name).hover();
|
||||
await expect(page.getByRole('tooltip', { name: sineWaveObject2.path })).toBeVisible();
|
||||
|
||||
expect(await getToolTip(sineWaveObject1)).toBe(sineWaveObject1.path);
|
||||
expect(await getToolTip(sineWaveObject2)).toBe(sineWaveObject2.path);
|
||||
expect(await getToolTip(sineWaveObject3)).toBe(sineWaveObject3.path);
|
||||
//Hover on something else
|
||||
await page.getByRole('button', { name: 'Create' }).hover();
|
||||
//Hover over third object
|
||||
await page.getByLabel('lad name').getByText(sineWaveObject3.name).hover();
|
||||
await expect(page.getByRole('tooltip', { name: sineWaveObject3.path })).toBeVisible();
|
||||
});
|
||||
|
||||
test('display correct paths for expanded and collapsed plot legend items', async ({ page }) => {
|
||||
@ -128,66 +126,74 @@ test.describe('Verify tooltips', () => {
|
||||
// Edit Overlay Plot
|
||||
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');
|
||||
await page.dragAndDrop(`text=${sineWaveObject2.name}`, '.gl-plot');
|
||||
await page.dragAndDrop(`text=${sineWaveObject3.name}`, '.gl-plot');
|
||||
await page.locator('button[title="Save"]').click();
|
||||
// Add the Sine Wave Generators to the and save changes
|
||||
await page
|
||||
.getByLabel('Preview SWG 1 generator Object')
|
||||
.dragTo(page.getByLabel('Plot Container Style Target'));
|
||||
await page
|
||||
.getByLabel('Preview SWG 2 generator Object')
|
||||
.dragTo(page.getByLabel('Plot Container Style Target'));
|
||||
await page
|
||||
.getByLabel('Preview SWG 3 generator Object')
|
||||
.dragTo(page.getByLabel('Plot Container Style Target'));
|
||||
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
|
||||
//Hover over Collapsed Plot Legend Components with the Control Key pressed
|
||||
await page.keyboard.down('Control');
|
||||
|
||||
async function getCollapsedLegendToolTip(object) {
|
||||
await page.locator('.c-create-button').hover();
|
||||
await page
|
||||
.locator('.plot-series-name', { has: page.locator(`text="${object.name} Hz"`) })
|
||||
.hover();
|
||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
return tooltipText.replace('\n', '').trim();
|
||||
}
|
||||
|
||||
async function getExpandedLegendToolTip(object) {
|
||||
await page.locator('.c-create-button').hover();
|
||||
await page
|
||||
.locator('.plot-series-name', { has: page.locator(`text="${object.name}"`) })
|
||||
.hover();
|
||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
return tooltipText.replace('\n', '').trim();
|
||||
}
|
||||
|
||||
expect(await getCollapsedLegendToolTip(sineWaveObject1)).toBe(sineWaveObject1.path);
|
||||
expect(await getCollapsedLegendToolTip(sineWaveObject2)).toBe(sineWaveObject2.path);
|
||||
expect(await getCollapsedLegendToolTip(sineWaveObject3)).toBe(sineWaveObject3.path);
|
||||
|
||||
//Hover over first object
|
||||
await page.getByText('SWG 1 Hz').hover();
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject1.path);
|
||||
//Hover over another object to clear
|
||||
await page.getByRole('button', { name: 'create' }).hover();
|
||||
//Hover over second object
|
||||
await page.getByText('SWG 2 Hz').hover();
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject2.path);
|
||||
//Hover over another object to clear
|
||||
await page.getByRole('button', { name: 'create' }).hover();
|
||||
//Hover over third object
|
||||
await page.getByText('SWG 3 Hz').hover();
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject3.path);
|
||||
//Release the Control Key
|
||||
await page.keyboard.up('Control');
|
||||
|
||||
//Expand the legend
|
||||
await page.locator('.gl-plot-legend__view-control.c-disclosure-triangle').click();
|
||||
|
||||
//Hover over Expanded Plot Legend Components with the Control Key pressed
|
||||
await page.keyboard.down('Control');
|
||||
|
||||
expect(await getExpandedLegendToolTip(sineWaveObject1)).toBe(sineWaveObject1.path);
|
||||
expect(await getExpandedLegendToolTip(sineWaveObject2)).toBe(sineWaveObject2.path);
|
||||
expect(await getExpandedLegendToolTip(sineWaveObject3)).toBe(sineWaveObject3.path);
|
||||
await page.getByLabel('Plot Legend Expanded').getByText('SWG 1').hover();
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject1.path);
|
||||
//Hover over another object to clear
|
||||
await page.getByRole('button', { name: 'create' }).hover();
|
||||
//Hover over second object
|
||||
await page.getByLabel('Plot Legend Expanded').getByText('SWG 2').hover();
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject2.path);
|
||||
//Hover over another object to clear
|
||||
await page.getByRole('button', { name: 'create' }).hover();
|
||||
//Hover over third object
|
||||
await page.getByLabel('Plot Legend Expanded').getByText('SWG 3').hover();
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject3.path);
|
||||
});
|
||||
|
||||
test('display correct paths when hovering over object labels', async ({ page }) => {
|
||||
async function getObjectLabelTooltip(object) {
|
||||
await page
|
||||
.locator('.c-tree__item__name.c-object-label__name', {
|
||||
has: page.locator(`text="${object.name}"`)
|
||||
})
|
||||
.click();
|
||||
await page.keyboard.down('Control');
|
||||
await page
|
||||
.locator('.l-browse-bar__object-name.c-object-label__name', {
|
||||
has: page.locator(`text="${object.name}"`)
|
||||
})
|
||||
.hover();
|
||||
const tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
await page.keyboard.up('Control');
|
||||
return tooltipText.replace('\n', '').trim();
|
||||
}
|
||||
//Navigate to SWG 1 in Tree
|
||||
await page.getByLabel('Navigate to SWG 1 generator').click();
|
||||
|
||||
expect(await getObjectLabelTooltip(sineWaveObject1)).toBe(sineWaveObject1.path);
|
||||
expect(await getObjectLabelTooltip(sineWaveObject3)).toBe(sineWaveObject3.path);
|
||||
//Expect tooltip to be the path of SWG 1
|
||||
await page.keyboard.down('Control');
|
||||
await page.getByRole('main').getByText('SWG 1', { exact: true }).hover();
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject1.path);
|
||||
await page.keyboard.up('Control');
|
||||
|
||||
//Navigate to SWG 3 in Tree
|
||||
await page.getByLabel('Navigate to SWG 3 generator').click();
|
||||
//Expect tooltip to be the path of SWG 3
|
||||
await page.keyboard.down('Control');
|
||||
await page.getByRole('main').getByText('SWG 3', { exact: true }).hover();
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject3.path);
|
||||
});
|
||||
|
||||
test('display correct paths when hovering over display layout pane headers', async ({ page }) => {
|
||||
@ -198,8 +204,11 @@ test.describe('Verify tooltips', () => {
|
||||
});
|
||||
// Edit Overlay Plot
|
||||
await page.getByLabel('Edit Object').click();
|
||||
await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.gl-plot');
|
||||
await page.locator('button[title="Save"]').click();
|
||||
|
||||
await page
|
||||
.getByLabel('Preview SWG 1 generator Object')
|
||||
.dragTo(page.getByLabel('Plot Container Style Target'));
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
|
||||
// Create Stacked Plot
|
||||
@ -209,8 +218,9 @@ test.describe('Verify tooltips', () => {
|
||||
});
|
||||
// Edit Stacked Plot
|
||||
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('button', { name: 'Save' }).click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
|
||||
// Create Display Layout
|
||||
@ -221,66 +231,77 @@ test.describe('Verify tooltips', () => {
|
||||
// Edit Display Layout
|
||||
await page.getByLabel('Edit Object').click();
|
||||
|
||||
await page.dragAndDrop("text='Test Overlay Plot'", '.l-layout__grid-holder', {
|
||||
targetPosition: { x: 0, y: 0 }
|
||||
});
|
||||
await page.dragAndDrop("text='Test Stacked Plot'", '.l-layout__grid-holder', {
|
||||
targetPosition: { x: 0, y: 250 }
|
||||
});
|
||||
await page.dragAndDrop(`text=${sineWaveObject3.name}`, '.l-layout__grid-holder', {
|
||||
targetPosition: { x: 500, y: 200 }
|
||||
});
|
||||
await page.locator('button[title="Save"]').click();
|
||||
await page
|
||||
.getByLabel('Preview Test Overlay Plot')
|
||||
.dragTo(page.locator('#display-layout-drop-area'), {
|
||||
targetPosition: { x: 0, y: 0 }
|
||||
});
|
||||
|
||||
//Add Display Layout below Overlay Plot
|
||||
await page
|
||||
.getByLabel('Preview Test Stacked Plot')
|
||||
.dragTo(page.locator('#display-layout-drop-area'), {
|
||||
targetPosition: { x: 0, y: 250 }
|
||||
});
|
||||
|
||||
//Drag the SWG3 Object to the Display off to the right
|
||||
await page
|
||||
.getByLabel('Preview SWG 3 generator Object')
|
||||
.dragTo(page.locator('#display-layout-drop-area'), {
|
||||
targetPosition: { x: 500, y: 200 }
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
|
||||
//Hover over Overlay Plot with the Control Key pressed
|
||||
await page.keyboard.down('Control');
|
||||
|
||||
await page.getByText('Test Overlay Plot').nth(2).hover();
|
||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe('My Items / Test Overlay Plot');
|
||||
|
||||
//Hover Overlay Plot
|
||||
await page.getByTitle('Test Overlay Plot').hover();
|
||||
await expect(page.getByRole('tooltip')).toHaveText('My Items / Test Overlay Plot');
|
||||
await page.keyboard.up('Control');
|
||||
await page.locator('.c-plot-legend__view-control >> nth=0').click();
|
||||
|
||||
//Expand the Overlay Plot Legend and hover over the first legend item
|
||||
await page.getByLabel('Expand Test Overlay Plot Legend').click();
|
||||
|
||||
await page.keyboard.down('Control');
|
||||
await page.locator('.plot-wrapper-expanded-legend .plot-series-name').first().hover();
|
||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject1.path);
|
||||
await page.getByLabel('Plot Legend Item for Test').getByText('SWG').hover();
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject1.path);
|
||||
|
||||
await page.getByText('Test Stacked Plot').nth(2).hover();
|
||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe('My Items / Test Stacked Plot');
|
||||
//Hover over Stacked Plot Title
|
||||
await page.getByTitle('Test Stacked Plot').hover();
|
||||
await expect(page.getByRole('tooltip')).toHaveText('My Items / Test Stacked Plot');
|
||||
|
||||
await page.getByText('SWG 3').nth(2).hover();
|
||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(sineWaveObject3.path).toBe(tooltipText);
|
||||
//Hover over SWG3 Object
|
||||
await page.getByLabel('Alpha-numeric telemetry name for SWG').hover();
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject3.path);
|
||||
});
|
||||
|
||||
test('display correct paths when hovering over flexible object labels', async ({ page }) => {
|
||||
//Create Flexible Layout
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
type: 'Flexible Layout',
|
||||
name: 'Test Flexible Layout'
|
||||
});
|
||||
|
||||
await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.c-fl-container >> nth=0');
|
||||
await page.dragAndDrop(`text=${sineWaveObject3.name}`, '.c-fl-container >> nth=1');
|
||||
//Add SWG1 and SWG3 to Flexible Layout
|
||||
await page.getByLabel('Navigate to SWG 1 generator').dragTo(page.getByRole('row').nth(0));
|
||||
await page
|
||||
.getByLabel('Preview SWG 3 generator Object')
|
||||
.dragTo(page.getByLabel('Container Handle 2'));
|
||||
|
||||
await page.locator('button[title="Save"]').click();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
|
||||
//Hover over SWG1 Object
|
||||
await page.keyboard.down('Control');
|
||||
await page.getByText('SWG 1').nth(2).hover();
|
||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject1.path);
|
||||
await page.getByTitle('SWG 1').hover();
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject1.path);
|
||||
|
||||
await page.getByText('SWG 3').nth(2).hover();
|
||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject3.path);
|
||||
//Hover over SWG3 Object
|
||||
await page.getByTitle('SWG 3').hover();
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject3.path);
|
||||
});
|
||||
|
||||
test('display correct paths when hovering over tab view labels', async ({ page }) => {
|
||||
@ -289,46 +310,40 @@ test.describe('Verify tooltips', () => {
|
||||
name: 'Test Tabs View'
|
||||
});
|
||||
|
||||
await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.c-tabs-view__tabs-holder');
|
||||
await page.dragAndDrop(`text=${sineWaveObject3.name}`, '.c-tabs-view__tabs-holder');
|
||||
//Add SWG1 and SWG3 to Flexible Layout
|
||||
await page
|
||||
.getByLabel('Navigate to SWG 1 generator')
|
||||
.dragTo(page.getByText('Drag objects here to add them'));
|
||||
await page.getByLabel('Preview SWG 3 generator Object').dragTo(page.getByLabel('SWG 1 tab'));
|
||||
|
||||
await page.locator('button[title="Save"]').click();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
|
||||
await page.keyboard.down('Control');
|
||||
await page.getByText('SWG 1').nth(2).hover();
|
||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject1.path);
|
||||
await page.getByLabel('SWG 1 tab').getByText('SWG').hover();
|
||||
|
||||
await page.getByText('SWG 3').nth(2).hover();
|
||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject3.path);
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject1.path);
|
||||
|
||||
await page.getByLabel('SWG 3 tab').getByText('SWG').hover();
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject3.path);
|
||||
});
|
||||
|
||||
test('display correct paths when hovering tree items', async ({ page }) => {
|
||||
await page.keyboard.down('Control');
|
||||
await page.getByText('SWG 1').nth(0).hover();
|
||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject1.path);
|
||||
await page.getByText('SWG 1').first().hover();
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject1.path);
|
||||
|
||||
await page.getByText('SWG 3').nth(0).hover();
|
||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject3.path);
|
||||
await page.getByText('SWG 3').first().hover();
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject3.path);
|
||||
});
|
||||
|
||||
test('display correct paths when hovering search items', async ({ page }) => {
|
||||
await page.getByRole('searchbox', { name: 'Search Input' }).click();
|
||||
await page.fill('.c-search__input', 'SWG 3');
|
||||
await page.getByRole('searchbox', { name: 'Search Input' }).fill('SWG 3');
|
||||
|
||||
await page.keyboard.down('Control');
|
||||
await page.locator('.c-gsearch-result__title').hover();
|
||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject3.path);
|
||||
await page.getByLabel('Object Results').getByText('SWG').hover();
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject3.path);
|
||||
});
|
||||
|
||||
test('display path for source telemetry when hovering over gauge', async ({ page }) => {
|
||||
@ -336,13 +351,11 @@ test.describe('Verify tooltips', () => {
|
||||
type: 'Gauge',
|
||||
name: 'Test Gauge'
|
||||
});
|
||||
await page.dragAndDrop(`text=${sineWaveObject3.name}`, '.c-gauge__wrapper');
|
||||
|
||||
await page.getByLabel('Navigate to SWG 3 generator').dragTo(page.getByRole('meter'));
|
||||
await page.keyboard.down('Control');
|
||||
// eslint-disable-next-line playwright/no-force-option
|
||||
await page.locator('.c-gauge.c-dial').hover({ position: { x: 0, y: 0 }, force: true });
|
||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject3.path);
|
||||
await page.getByRole('meter').hover({ position: { x: 0, y: 0 } });
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject3.path);
|
||||
});
|
||||
|
||||
test('display tooltip path for notebook embeds', async ({ page }) => {
|
||||
@ -351,27 +364,23 @@ test.describe('Verify tooltips', () => {
|
||||
name: 'Test Notebook'
|
||||
});
|
||||
|
||||
await page.dragAndDrop(`text=${sineWaveObject3.name}`, '.c-notebook__drag-area');
|
||||
await page
|
||||
.getByLabel('Navigate to SWG 3 generator')
|
||||
.dragTo(page.getByLabel('To start a new entry, click'));
|
||||
await page.keyboard.down('Control');
|
||||
await page.locator('.c-ne__embed').hover();
|
||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject3.path);
|
||||
await page.getByLabel('SWG 3 Notebook Embed').hover();
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject3.path);
|
||||
});
|
||||
|
||||
test.fixme('display tooltip path for telemetry table names', async ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/7421'
|
||||
});
|
||||
// set endBound to 10 seconds after start bound
|
||||
const url = await page.url();
|
||||
const parsedUrl = new URL(url.replace('#', '!'));
|
||||
const startBound = Number(parsedUrl.searchParams.get('tc.startBound'));
|
||||
const tenSecondsInMilliseconds = 10 * 1000;
|
||||
const endBound = startBound + tenSecondsInMilliseconds;
|
||||
parsedUrl.searchParams.set('tc.endBound', endBound);
|
||||
await page.goto(parsedUrl.href.replace('!', '#'));
|
||||
test('display tooltip path for telemetry table names', async ({ page }) => {
|
||||
// set endBound to 10 seconds after start bound to ensure that the telemetry doesn't change
|
||||
// const url = page.url();
|
||||
// const parsedUrl = new URL(url.replace('#', '!'));
|
||||
// const startBound = Number(parsedUrl.searchParams.get('tc.startBound'));
|
||||
// const tenSecondsInMilliseconds = 10 * 1000;
|
||||
// const endBound = startBound + tenSecondsInMilliseconds;
|
||||
// parsedUrl.searchParams.set('tc.endBound', endBound);
|
||||
// await page.goto(parsedUrl.href.replace('!', '#'));
|
||||
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
type: 'Telemetry Table',
|
||||
@ -381,48 +390,35 @@ test.describe('Verify tooltips', () => {
|
||||
await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.c-telemetry-table');
|
||||
await page.dragAndDrop(`text=${sineWaveObject3.name}`, '.c-telemetry-table');
|
||||
|
||||
await page.locator('button[title="Save"]').click();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
await page.keyboard.down('Control');
|
||||
|
||||
//Hover over SWG3 in Telemetry Table
|
||||
await page.locator('.noselect > [title="SWG 3"]').first().hover();
|
||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject3.path);
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject3.path);
|
||||
|
||||
//Hover over SWG1 in Telemetry Table
|
||||
await page.locator('.noselect > [title="SWG 1"]').first().hover();
|
||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject1.path);
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject1.path);
|
||||
});
|
||||
|
||||
test('display tooltip path for recently viewed items', async ({ page }) => {
|
||||
// drag up Recently Viewed pane
|
||||
await page
|
||||
.locator('.l-pane.l-pane--vertical-handle-before', {
|
||||
hasText: 'Recently Viewed'
|
||||
})
|
||||
.locator('.l-pane__handle')
|
||||
.hover();
|
||||
await page.getByLabel('Resize Recently Viewed Pane').hover();
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(0, 300);
|
||||
await page.mouse.up();
|
||||
|
||||
await page.keyboard.down('Control');
|
||||
await page.getByLabel('Recent Objects').getByText(sineWaveObject3.name).hover();
|
||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject3.path);
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject3.path);
|
||||
|
||||
await page.getByLabel('Recent Objects').getByText(sineWaveObject2.name).hover();
|
||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject2.path);
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject2.path);
|
||||
|
||||
await page.getByLabel('Recent Objects').getByText(sineWaveObject1.name).hover();
|
||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject1.path);
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject1.path);
|
||||
});
|
||||
|
||||
test('display tooltip path for time strips', async ({ page }) => {
|
||||
@ -445,23 +441,17 @@ test.describe('Verify tooltips', () => {
|
||||
`text=${sineWaveObject3.name}`,
|
||||
'.c-object-view.is-object-type-time-strip'
|
||||
);
|
||||
await page.locator('button[title="Save"]').click();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
|
||||
await page.keyboard.down('Control');
|
||||
await page.getByText(sineWaveObject1.name).nth(2).hover();
|
||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject1.path);
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject1.path);
|
||||
|
||||
await page.getByText(sineWaveObject2.name).nth(2).hover();
|
||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject2.path);
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject2.path);
|
||||
|
||||
await page.getByText(sineWaveObject3.name).nth(2).hover();
|
||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
||||
tooltipText = tooltipText.replace('\n', '').trim();
|
||||
expect(tooltipText).toBe(sineWaveObject3.path);
|
||||
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject3.path);
|
||||
});
|
||||
});
|
||||
|
@ -48,7 +48,9 @@ test.describe('Main Tree', () => {
|
||||
});
|
||||
|
||||
await expandTreePaneItemByName(page, folder.name);
|
||||
await assertTreeItemIsVisible(page, clock.name);
|
||||
await expect(
|
||||
page.getByRole('tree', { name: 'Main Tree' }).getByRole('treeitem', { name: clock.name })
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('Creating a child object on one tab and expanding its parent on the other shows the correct composition @2p', async ({
|
||||
@ -65,8 +67,8 @@ test.describe('Main Tree', () => {
|
||||
|
||||
// Both pages: Go to baseURL
|
||||
await Promise.all([
|
||||
page.goto('./', { waitUntil: 'networkidle' }),
|
||||
page2.goto('./', { waitUntil: 'networkidle' })
|
||||
page.goto('./', { waitUntil: 'domcontentloaded' }),
|
||||
page2.goto('./', { waitUntil: 'domcontentloaded' })
|
||||
]);
|
||||
|
||||
const page1Folder = await createDomainObjectWithDefaults(page, {
|
||||
@ -74,7 +76,11 @@ test.describe('Main Tree', () => {
|
||||
});
|
||||
|
||||
await expandTreePaneItemByName(page2, myItemsFolderName);
|
||||
await assertTreeItemIsVisible(page2, page1Folder.name);
|
||||
await expect(
|
||||
page2
|
||||
.getByRole('tree', { name: 'Main Tree' })
|
||||
.getByRole('treeitem', { name: page1Folder.name })
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('Creating a child object on one tab and expanding its parent on the other shows the correct composition @couchdb @2p', async ({
|
||||
@ -91,8 +97,8 @@ test.describe('Main Tree', () => {
|
||||
|
||||
// Both pages: Go to baseURL
|
||||
await Promise.all([
|
||||
page.goto('./', { waitUntil: 'networkidle' }),
|
||||
page2.goto('./', { waitUntil: 'networkidle' })
|
||||
page.goto('./', { waitUntil: 'domcontentloaded' }),
|
||||
page2.goto('./', { waitUntil: 'domcontentloaded' })
|
||||
]);
|
||||
|
||||
const page1Folder = await createDomainObjectWithDefaults(page, {
|
||||
@ -100,9 +106,13 @@ test.describe('Main Tree', () => {
|
||||
});
|
||||
|
||||
await expandTreePaneItemByName(page2, myItemsFolderName);
|
||||
await assertTreeItemIsVisible(page2, page1Folder.name);
|
||||
await expect(
|
||||
page2
|
||||
.getByRole('tree', { name: 'Main Tree' })
|
||||
.getByRole('treeitem', { name: page1Folder.name })
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
// eslint-disable-next-line playwright/expect-expect
|
||||
test('Renaming an object reorders the tree @unstable', async ({ page, openmctConfig }) => {
|
||||
const { myItemsFolderName } = openmctConfig;
|
||||
|
||||
@ -221,17 +231,6 @@ async function getAndAssertTreeItems(page, expected) {
|
||||
expect(allTexts).toEqual(expected);
|
||||
}
|
||||
|
||||
async function assertTreeItemIsVisible(page, name) {
|
||||
const mainTree = page.getByRole('tree', {
|
||||
name: 'Main Tree'
|
||||
});
|
||||
const treeItem = mainTree.getByRole('treeitem', {
|
||||
name
|
||||
});
|
||||
|
||||
await expect(treeItem).toBeVisible();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
* @param {string} name
|
||||
|
@ -82,14 +82,14 @@ test.describe('Smoke tests for @mobile', () => {
|
||||
await page.getByTitle('Collapse Browse Pane').click();
|
||||
await expect(page.getByRole('main').getByText('Parent Display Layout')).toBeVisible();
|
||||
//Verify both objects are in view
|
||||
await expect(await page.getByLabel('Child Layout 1 Layout')).toBeVisible();
|
||||
await expect(await page.getByLabel('Child Layout 2 Layout')).toBeVisible();
|
||||
await expect(page.getByLabel('Child Layout 1 Layout')).toBeVisible();
|
||||
await expect(page.getByLabel('Child Layout 2 Layout')).toBeVisible();
|
||||
//Remove First Object to bring up confirmation dialog
|
||||
await page.getByLabel('View menu items').nth(1).click();
|
||||
await page.getByLabel('Remove').click();
|
||||
await page.getByRole('button', { name: 'OK' }).click();
|
||||
//Verify that the object is removed
|
||||
await expect(await page.getByLabel('Child Layout 1 Layout')).toBeVisible();
|
||||
await expect(page.getByLabel('Child Layout 1 Layout')).toBeVisible();
|
||||
expect(await page.getByLabel('Child Layout 2 Layout').count()).toBe(0);
|
||||
});
|
||||
});
|
||||
|
@ -39,7 +39,7 @@ const filePath = 'test-data/PerformanceDisplayLayout.json';
|
||||
test.describe('Performance tests', () => {
|
||||
test.beforeEach(async ({ page, browser }, testInfo) => {
|
||||
// Go to baseURL
|
||||
await page.goto('./', { waitUntil: 'networkidle' });
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
|
||||
// Click a:has-text("My Items")
|
||||
await page.locator('a:has-text("My Items")').click({
|
||||
@ -129,12 +129,12 @@ test.describe('Performance tests', () => {
|
||||
]);
|
||||
|
||||
//Time to Example Imagery Frame loads within Display Layout
|
||||
await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible' });
|
||||
await page.locator('.c-imagery__main-image__bg').waitFor({ state: 'visible' });
|
||||
//Time to Example Imagery object loads
|
||||
await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible' });
|
||||
await page.locator('.c-imagery__main-image__background-image').waitFor({ state: 'visible' });
|
||||
|
||||
//Get background-image url from background-image css prop
|
||||
const backgroundImage = await page.locator('.c-imagery__main-image__background-image');
|
||||
const backgroundImage = page.locator('.c-imagery__main-image__background-image');
|
||||
let backgroundImageUrl = await backgroundImage.evaluate((el) => {
|
||||
return window
|
||||
.getComputedStyle(el)
|
||||
@ -156,15 +156,15 @@ test.describe('Performance tests', () => {
|
||||
await page.evaluate(() => window.performance.mark('viewLarge.start.test')); //This is a mark only to compare evaluate timing
|
||||
|
||||
//Time to Imagery Rendered in Large Frame
|
||||
await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible' });
|
||||
await page.locator('.c-imagery__main-image__bg').waitFor({ state: 'visible' });
|
||||
await page.evaluate(() => window.performance.mark('background-image-frame'));
|
||||
|
||||
//Time to Example Imagery object loads
|
||||
await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible' });
|
||||
await page.locator('.c-imagery__main-image__background-image').waitFor({ state: 'visible' });
|
||||
await page.evaluate(() => window.performance.mark('background-image-visible'));
|
||||
|
||||
// Get Current number of images in thumbstrip
|
||||
await page.waitForSelector('.c-imagery__thumb');
|
||||
await page.locator('.c-imagery__thumb').waitFor({ state: 'visible' });
|
||||
const thumbCount = await page.locator('.c-imagery__thumb').count();
|
||||
console.log('number of thumbs rendered ' + thumbCount);
|
||||
await page.locator('.c-imagery__thumb').last().click();
|
||||
|
@ -38,7 +38,7 @@ const notebookFilePath = 'test-data/PerformanceNotebook.json';
|
||||
test.describe('Performance tests', () => {
|
||||
test.beforeEach(async ({ page, browser }, testInfo) => {
|
||||
// Go to baseURL
|
||||
await page.goto('./', { waitUntil: 'networkidle' });
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
|
||||
// Click a:has-text("My Items")
|
||||
await page.locator('a:has-text("My Items")').click({
|
||||
@ -110,20 +110,19 @@ test.describe('Performance tests', () => {
|
||||
await page.evaluate(() => window.performance.mark('search-entered'));
|
||||
//Search Result Appears and is clicked
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.locator('a:has-text("Performance Notebook")').first().click(),
|
||||
page.evaluate(() => window.performance.mark('click-search-result'))
|
||||
]);
|
||||
|
||||
await page.waitForSelector('.c-tree__item c-tree-and-search__loading loading', {
|
||||
state: 'hidden'
|
||||
});
|
||||
await page
|
||||
.locator('.c-tree__item c-tree-and-search__loading loading')
|
||||
.waitFor({ state: 'hidden' });
|
||||
await page.evaluate(() => window.performance.mark('search-spinner-gone'));
|
||||
|
||||
await page.waitForSelector('.l-browse-bar__object-name', { state: 'visible' });
|
||||
await page.locator('.l-browse-bar__object-name').waitFor({ state: 'visible' });
|
||||
await page.evaluate(() => window.performance.mark('object-title-appears'));
|
||||
|
||||
await page.waitForSelector('.c-notebook__entry >> nth=0', { state: 'visible' });
|
||||
await page.locator('.c-notebook__entry >> nth=0').waitFor({ state: 'visible' });
|
||||
await page.evaluate(() => window.performance.mark('notebook-entry-appears'));
|
||||
|
||||
// Click Add new Notebook Entry
|
||||
@ -139,9 +138,9 @@ test.describe('Performance tests', () => {
|
||||
await page.evaluate(() => window.performance.mark('notebook-search-start'));
|
||||
await page.locator('.c-notebook__search >> input').fill('Existing Entry');
|
||||
await page.evaluate(() => window.performance.mark('notebook-search-filled'));
|
||||
await page.waitForSelector('text=Search Results (3)', { state: 'visible' });
|
||||
await page.locator('text=Search Results (3)').waitFor({ state: 'visible' });
|
||||
await page.evaluate(() => window.performance.mark('notebook-search-processed'));
|
||||
await page.waitForSelector('.c-notebook__entry >> nth=2', { state: 'visible' });
|
||||
await page.locator('.c-notebook__entry >> nth=2').waitFor({ state: 'visible' });
|
||||
await page.evaluate(() => window.performance.mark('notebook-search-processed'));
|
||||
|
||||
//Clear Search
|
||||
@ -154,7 +153,7 @@ test.describe('Performance tests', () => {
|
||||
await page.locator('div.c-ne__time-and-content').last().hover();
|
||||
await page.locator('button[title="Delete this entry"]').last().click();
|
||||
await page.locator('button:has-text("Ok")').click();
|
||||
await page.waitForSelector('.c-notebook__entry >> nth=3', { state: 'detached' });
|
||||
await page.locator('.c-notebook__entry >> nth=3').waitFor({ state: 'detached' });
|
||||
await page.evaluate(() => window.performance.mark('new-notebook-entry-deleted'));
|
||||
|
||||
//await client.send('HeapProfiler.enable');
|
||||
|
@ -228,9 +228,7 @@ test.describe('Navigation memory leak is not detected in', () => {
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
test('display layout with plots of swgs, alphanumerics, and condition sets, ', async ({
|
||||
page
|
||||
}) => {
|
||||
test('display layout with plots of swgs, alphanumerics, and condition sets', async ({ page }) => {
|
||||
const result = await navigateToObjectAndDetectMemoryLeak(
|
||||
page,
|
||||
'display-layout-simple-telemetry'
|
||||
|
@ -78,7 +78,7 @@ test.describe('Tabs View', () => {
|
||||
await page.getByLabel(`${sineWaveGenerator.name} tab`, { exact: true }).click();
|
||||
|
||||
// ensure sine wave generator visible
|
||||
expect(await page.locator('.c-plot').isVisible()).toBe(true);
|
||||
await expect(page.locator('.c-plot')).toBeVisible();
|
||||
|
||||
// now select notebook and clear animation calls
|
||||
await page.getByLabel(`${notebook.name} tab`, { exact: true }).click();
|
||||
|
@ -84,7 +84,7 @@ test.describe('Plot Tagging Performance', () => {
|
||||
await setRealTimeMode(page);
|
||||
|
||||
// Search for Science
|
||||
await page.getByRole('searchbox', { name: 'Search Input' });
|
||||
await page.getByRole('searchbox', { name: 'Search Input' }).click();
|
||||
await page.getByRole('searchbox', { name: 'Search Input' }).fill('sc');
|
||||
|
||||
// click on the search result
|
||||
|
@ -89,6 +89,7 @@ test.describe('Visual - Header @a11y', () => {
|
||||
await percySnapshot(page, `Notebook Snapshot Show button (theme: '${theme}')`, {
|
||||
scope: header
|
||||
});
|
||||
await expect(page.getByLabel('Show Snapshots')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
|
48
package-lock.json
generated
48
package-lock.json
generated
@ -37,7 +37,7 @@
|
||||
"eslint-config-prettier": "9.1.0",
|
||||
"eslint-plugin-compat": "4.2.0",
|
||||
"eslint-plugin-no-unsanitized": "4.0.2",
|
||||
"eslint-plugin-playwright": "0.12.0",
|
||||
"eslint-plugin-playwright": "1.5.2",
|
||||
"eslint-plugin-prettier": "5.1.3",
|
||||
"eslint-plugin-simple-import-sort": "10.0.0",
|
||||
"eslint-plugin-unicorn": "49.0.0",
|
||||
@ -4756,13 +4756,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-playwright": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-0.12.0.tgz",
|
||||
"integrity": "sha512-KXuzQjVzca5irMT/7rvzJKsVDGbQr43oQPc8i+SLEBqmfrTxlwMwRqfv9vtZqh4hpU0jmrnA/EOfwtls+5QC1w==",
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-1.5.2.tgz",
|
||||
"integrity": "sha512-TMzLrLGQMccngU8GogtzIc9u5RzXGnfsQEUjLfEfshINuVR2fS4SHfDtU7xYP90Vwm5vflHECf610KTdGvO53w==",
|
||||
"dev": true,
|
||||
"workspaces": [
|
||||
"examples"
|
||||
],
|
||||
"dependencies": {
|
||||
"globals": "^13.23.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">=7",
|
||||
"eslint-plugin-jest": ">=24"
|
||||
"eslint": ">=8.40.0",
|
||||
"eslint-plugin-jest": ">=25"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"eslint-plugin-jest": {
|
||||
@ -4770,6 +4779,33 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-playwright/node_modules/globals": {
|
||||
"version": "13.24.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
|
||||
"integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"type-fest": "^0.20.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-playwright/node_modules/type-fest": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-prettier": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz",
|
||||
|
@ -40,7 +40,7 @@
|
||||
"eslint-config-prettier": "9.1.0",
|
||||
"eslint-plugin-compat": "4.2.0",
|
||||
"eslint-plugin-no-unsanitized": "4.0.2",
|
||||
"eslint-plugin-playwright": "0.12.0",
|
||||
"eslint-plugin-playwright": "1.5.2",
|
||||
"eslint-plugin-prettier": "5.1.3",
|
||||
"eslint-plugin-simple-import-sort": "10.0.0",
|
||||
"eslint-plugin-unicorn": "49.0.0",
|
||||
|
@ -20,7 +20,13 @@ this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<template>
|
||||
<div ref="tooltip-wrapper" class="c-menu c-tooltip-wrapper" :style="toolTipLocationStyle">
|
||||
<div
|
||||
ref="tooltip-wrapper"
|
||||
class="c-menu c-tooltip-wrapper"
|
||||
:style="toolTipLocationStyle"
|
||||
role="tooltip"
|
||||
aria-live="polite"
|
||||
>
|
||||
<div class="c-tooltip">
|
||||
{{ toolTipText }}
|
||||
</div>
|
||||
|
@ -30,6 +30,7 @@
|
||||
>
|
||||
<td
|
||||
ref="tableCell"
|
||||
scope="row"
|
||||
aria-label="lad name"
|
||||
class="js-first-data"
|
||||
@mouseover.ctrl="showToolTip"
|
||||
|
@ -21,16 +21,20 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="c-lad-table-wrapper u-style-receiver js-style-receiver" :class="staleClass">
|
||||
<table aria-label="lad table" class="c-table c-lad-table" :class="applyLayoutClass">
|
||||
<div
|
||||
id="lad-table-drop-area"
|
||||
class="c-lad-table-wrapper u-style-receiver js-style-receiver"
|
||||
:class="staleClass"
|
||||
>
|
||||
<table class="c-table c-lad-table" :class="applyLayoutClass">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th v-if="showTimestamp">Timestamp</th>
|
||||
<th>Value</th>
|
||||
<th v-if="hasUnits">Units</th>
|
||||
<th v-if="showType">Type</th>
|
||||
<th v-for="limitColumn in limitColumnNames" :key="limitColumn.key">
|
||||
<th scope="col">Name</th>
|
||||
<th v-if="showTimestamp" scope="col">Timestamp</th>
|
||||
<th scope="col">Value</th>
|
||||
<th v-if="hasUnits" scope="col">Units</th>
|
||||
<th v-if="showType" scope="col">Type</th>
|
||||
<th v-for="limitColumn in limitColumnNames" :key="limitColumn.key" scope="col">
|
||||
{{ limitColumn.label }}
|
||||
</th>
|
||||
</tr>
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
<template>
|
||||
<div
|
||||
id="display-layout-drop-area"
|
||||
class="l-layout u-style-receiver js-style-receiver"
|
||||
:class="{
|
||||
'is-multi-selected': selectedLayoutItems.length > 1,
|
||||
|
@ -24,14 +24,19 @@
|
||||
ref="gaugeWrapper"
|
||||
class="c-gauge__wrapper js-gauge-wrapper"
|
||||
:class="gaugeClasses"
|
||||
:aria-label="gaugeTitle"
|
||||
:title="gaugeTitle"
|
||||
:aria-label="`${domainObject.name}`"
|
||||
:aria-valuemin="rangeLow"
|
||||
:aria-valuemax="rangeHigh"
|
||||
:aria-valuenow="curVal"
|
||||
:aria-valuetext="`Current value: ${curVal}`"
|
||||
>
|
||||
<template v-if="typeDial">
|
||||
<svg
|
||||
ref="gauge"
|
||||
class="c-gauge c-dial"
|
||||
viewBox="0 0 10 10"
|
||||
role="meter"
|
||||
@mouseover.ctrl="showToolTip"
|
||||
@mouseleave="hideToolTip"
|
||||
>
|
||||
@ -233,7 +238,7 @@
|
||||
</template>
|
||||
|
||||
<template v-if="typeMeter">
|
||||
<div class="c-meter" @mouseover.ctrl="showToolTip" @mouseleave="hideToolTip">
|
||||
<div class="c-meter" role="meter" @mouseover.ctrl="showToolTip" @mouseleave="hideToolTip">
|
||||
<div v-if="displayMinMax" class="c-gauge__range c-meter__range js-gauge-meter-range">
|
||||
<div class="c-meter__range__high">{{ rangeHigh }}</div>
|
||||
<div class="c-meter__range__low">{{ rangeLow }}</div>
|
||||
|
@ -98,12 +98,14 @@
|
||||
v-if="selectedPage && !selectedPage.isLocked"
|
||||
:aria-disabled="activeTransaction"
|
||||
class="c-notebook__drag-area icon-plus"
|
||||
aria-dropeffect="link"
|
||||
aria-labelledby="newEntryLabel"
|
||||
@click="newEntry(null, $event)"
|
||||
@dragover="dragOver"
|
||||
@drop.capture="dropCapture"
|
||||
@drop="dropOnEntry($event)"
|
||||
>
|
||||
<span class="c-notebook__drag-area__label">
|
||||
<span id="newEntryLabel" class="c-notebook__drag-area__label">
|
||||
To start a new entry, click here or drag and drop any object
|
||||
</span>
|
||||
</div>
|
||||
@ -150,9 +152,10 @@
|
||||
<button
|
||||
class="c-button commit-button icon-lock"
|
||||
title="Commit entries and lock this page from further changes"
|
||||
aria-labelledby="commitEntriesLabel"
|
||||
@click="lockPage()"
|
||||
>
|
||||
<span class="c-button__label">Commit Entries</span>
|
||||
<span id="commitEntriesLabel" class="c-button__label">Commit Entries</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -88,9 +88,15 @@
|
||||
<button
|
||||
class="c-button icon-minus"
|
||||
title="Zoom out"
|
||||
aria-label="Zoom out"
|
||||
@click="zoom('out', 0.2)"
|
||||
></button>
|
||||
<button class="c-button icon-plus" title="Zoom in" @click="zoom('in', 0.2)"></button>
|
||||
<button
|
||||
class="c-button icon-plus"
|
||||
title="Zoom in"
|
||||
aria-label="Zoom in"
|
||||
@click="zoom('in', 0.2)"
|
||||
></button>
|
||||
</div>
|
||||
<div
|
||||
v-if="plotHistory.length && !options.compact"
|
||||
@ -98,12 +104,14 @@
|
||||
>
|
||||
<button
|
||||
class="c-button icon-arrow-left"
|
||||
title="Restore previous pan/zoom"
|
||||
title="Restore previous pan and zoom"
|
||||
aria-label="Restore previous pan and zoom"
|
||||
@click="back()"
|
||||
></button>
|
||||
<button
|
||||
class="c-button icon-reset"
|
||||
title="Reset pan/zoom"
|
||||
title="Reset pan and zoom"
|
||||
aria-label="Reset pan and zoom"
|
||||
@click="resumeRealtimeData()"
|
||||
></button>
|
||||
</div>
|
||||
@ -115,12 +123,14 @@
|
||||
v-if="!isFrozen"
|
||||
class="c-button icon-pause"
|
||||
title="Pause incoming real-time data"
|
||||
aria-label="Pause incoming real-time data"
|
||||
@click="pause()"
|
||||
></button>
|
||||
<button
|
||||
v-if="isFrozen"
|
||||
class="c-button icon-arrow-right pause-play is-paused"
|
||||
title="Resume displaying real-time data"
|
||||
aria-label="Resume displaying real-time data"
|
||||
@click="resumeRealtimeData()"
|
||||
></button>
|
||||
</div>
|
||||
@ -128,6 +138,7 @@
|
||||
<button
|
||||
class="c-button icon-clock"
|
||||
title="Synchronize Time Conductor"
|
||||
aria-label="Synchronize Time Conductor"
|
||||
@click="showSynchronizeDialog()"
|
||||
></button>
|
||||
</div>
|
||||
@ -136,12 +147,14 @@
|
||||
class="c-button icon-crosshair"
|
||||
:class="{ 'is-active': cursorGuide }"
|
||||
title="Toggle cursor guides"
|
||||
aria-label="Toggle cursor guides"
|
||||
@click="toggleCursorGuide"
|
||||
></button>
|
||||
<button
|
||||
class="c-button"
|
||||
:class="{ 'icon-grid-on': gridLines, 'icon-grid-off': !gridLines }"
|
||||
title="Toggle grid lines"
|
||||
aria-label="Toggle grid lines"
|
||||
@click="toggleGridLines"
|
||||
></button>
|
||||
</div>
|
||||
|
@ -27,11 +27,13 @@
|
||||
'is-legend-hidden': isLegendHidden
|
||||
}"
|
||||
>
|
||||
<div
|
||||
<button
|
||||
class="c-plot-legend__view-control gl-plot-legend__view-control c-disclosure-triangle is-enabled"
|
||||
:class="{ 'c-disclosure-triangle--expanded': isLegendExpanded }"
|
||||
:aria-label="ariaLabelValue"
|
||||
tabindex="0"
|
||||
@click="toggleLegend"
|
||||
></div>
|
||||
></button>
|
||||
|
||||
<div class="c-plot-legend__wrapper" :class="{ 'is-cursor-locked': cursorLocked }">
|
||||
<!-- COLLAPSED PLOT LEGEND -->
|
||||
@ -127,6 +129,11 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
ariaLabelValue() {
|
||||
const name = this.domainObject.name ? ` ${this.domainObject.name}` : '';
|
||||
|
||||
return `${this.isLegendExpanded ? 'Collapse' : 'Expand'}${name} Legend`;
|
||||
},
|
||||
showUnitsWhenExpanded() {
|
||||
return this.loaded && this.legend.get('showUnitsWhenExpanded') === true;
|
||||
},
|
||||
|
@ -85,6 +85,7 @@
|
||||
v-for="(treeItem, index) in visibleItems"
|
||||
:key="`${treeItem.navigationPath}-${index}-${treeItem.object.name}`"
|
||||
:node="treeItem"
|
||||
:draggable="true"
|
||||
:is-selector-tree="isSelectorTree"
|
||||
:selected-item="selectedItem"
|
||||
:active-search="activeSearch"
|
||||
|
Reference in New Issue
Block a user