mirror of
https://github.com/nasa/openmct.git
synced 2024-12-18 12:56:25 +00:00
test(e2e): Major refactor and stabilization of e2e tests (#7581)
* fix: update broken locator * update eslint package * first pass of lint fixes * update package * change ruleset * update component tests to match linting rules * driveby * start to factor out bad locators * update gauge component * update notebook snapshot drop area * Update plot aria * add draggable true to tree items * update package * driveby to remove dead code * unneeded * unneeded * tells a screenreader that this is a row and a cell * adds an id for dragondrops * this should be a button * first pass at fixing tooltip selectors * review comments * Updating more tests * update to remove expect expect given our use of check functions * add expand component * move role around * update more locators * force * new local storage * remove choochoo steps * test: do `lint:fix` and also add back accidentally removed code * test: add back more removed code * test: remove `unstable` annotation from tests which are not unstable * test: remove invalid test-- the "new" time conductor doesn't allow for millisecond changes in fixed time * test: fix unstable gauge test * test: remove useless asserts-- this was secretly non-functional. now that we've fixed it, it makes no sense and just fails * test: add back accidentally removed changes * test: revert changes that break test * test: more fixes * Remove all notion of the unstable/stable e2e tests * test: eviscerate the flake with FACTS and LOGIC * test: fix anotha one * lint fixes * test: no need to wait for save dialog * test: fix more tests * lint: fix more warnings * test: fix anotha one * test: use `toHaveLength` instead of `.length).toBe()` * test: stabilize tabs view example imagery test * fix: more tests be fixed * test: more `toHaveCount()`s please * test: revert more accidentally removed fixes * test: fix selector * test: fix anotha one * update lint rules to clean up bad locators in shared fixtures * update and remove bad appActions * test: fix some restricted notebook tests * test: mass find/replace to enforce `toHaveCount()` instead of `.count()).toBe()` * Remove some bad appActions and update text * test: fix da tree tests * test: await not await await * test: fix upload plan appAction and add a11y * Updating externalFixtures with best practice locators and add missing appAction framework tests * test: fix test * test: fix appAction test for plans * test: yum yum fix'em up and get rid of some dragon drops * fix: alas, a `.only()` got my hopes up that i was done fixing tests * test: add `setTimeConductorMode` test "suite" which covers most TC related appActions * test: fix arg * test(couchdb): fix some network tests via expect polling * Stabalize visual test * getCanasPixels * test: stabilize tooltip telemetry table test, better a11y for tooltips * chore: update to use `docker compose` instead of `docker-compose` * New rules, new tests, new me * fix sort order * test: add `waitForPlotsToRender` framework test, passthru timeout override * test: remove `clockOptions` test as we have `page.clock` now * test: refactor out `overrideClock` * test: use `clock.install` instead * test: use `clock.install` instead * time clock fix * test: fix timer tests * remove ever reference to old base fixture * test: stabilize restricted notebook test * lint fixes * test: use clock.install * update timelist * test: update visual tests to use `page.clock()`, update snapshots * test: stabilize tree renaming/reordering test * a11y: add aria-label and role=region to object view * refactor: use `dragTo` * refactor: use `dragTo`, other small fixes * test: use `page.clock()` to stabilize tooltip telemetry table test * test: use web-first assertion to stabilize staleness test * test: knock out a few more `page.click`s * test: destroy all `page.click()`s * refactor: consistently use `'Ok'` instead of `'OK'` and `'Ok'` mixed * test: remove gauge aria label * test: more test fixes * test: more fixes and refactors * docs: add comment * test: refactor all instances of `dragAndDrop` * test: remove redundant test (covered in previous test steps) * test: stabilize imagery operations tests for display layout * chore: remove bad unicorn rule * chore(lint): remove unused disable directives --------- Co-authored-by: Jesse Mazzella <jesse.d.mazzella@nasa.gov>
This commit is contained in:
parent
4ee68cccd6
commit
0413e77d8a
@ -93,7 +93,7 @@ jobs:
|
|||||||
- generate_and_store_version_and_filesystem_artifacts
|
- generate_and_store_version_and_filesystem_artifacts
|
||||||
e2e-test:
|
e2e-test:
|
||||||
parameters:
|
parameters:
|
||||||
suite: #stable or full
|
suite: #ci or full
|
||||||
type: string
|
type: string
|
||||||
executor: pw-focal-development
|
executor: pw-focal-development
|
||||||
parallelism: 7
|
parallelism: 7
|
||||||
@ -162,7 +162,7 @@ jobs:
|
|||||||
- run: npx playwright@1.45.2 install #Necessary for bare ubuntu machine
|
- run: npx playwright@1.45.2 install #Necessary for bare ubuntu machine
|
||||||
- run: |
|
- run: |
|
||||||
export $(cat src/plugins/persistence/couch/.env.ci | xargs)
|
export $(cat src/plugins/persistence/couch/.env.ci | xargs)
|
||||||
docker-compose -f src/plugins/persistence/couch/couchdb-compose.yaml up --detach
|
docker compose -f src/plugins/persistence/couch/couchdb-compose.yaml up --detach
|
||||||
sleep 3
|
sleep 3
|
||||||
bash src/plugins/persistence/couch/setup-couchdb.sh
|
bash src/plugins/persistence/couch/setup-couchdb.sh
|
||||||
- run: sh src/plugins/persistence/couch/replace-localstorage-with-couchdb-indexhtml.sh #Replace LocalStorage Plugin with CouchDB
|
- run: sh src/plugins/persistence/couch/replace-localstorage-with-couchdb-indexhtml.sh #Replace LocalStorage Plugin with CouchDB
|
||||||
@ -253,8 +253,8 @@ workflows:
|
|||||||
name: node18-chrome
|
name: node18-chrome
|
||||||
node-version: lts/hydrogen
|
node-version: lts/hydrogen
|
||||||
- e2e-test:
|
- e2e-test:
|
||||||
name: e2e-stable
|
name: e2e-ci
|
||||||
suite: stable
|
suite: ci
|
||||||
- e2e-mobile
|
- e2e-mobile
|
||||||
- visual-a11y:
|
- visual-a11y:
|
||||||
name: visual-a11y-ci
|
name: visual-a11y-ci
|
||||||
|
17
.cspell.json
17
.cspell.json
@ -482,19 +482,10 @@
|
|||||||
"composables",
|
"composables",
|
||||||
"countup",
|
"countup",
|
||||||
"darkmatter",
|
"darkmatter",
|
||||||
"Undeletes"
|
"Undeletes",
|
||||||
],
|
"SSSZ"
|
||||||
"dictionaries": [
|
|
||||||
"npm",
|
|
||||||
"softwareTerms",
|
|
||||||
"node",
|
|
||||||
"html",
|
|
||||||
"css",
|
|
||||||
"bash",
|
|
||||||
"en_US",
|
|
||||||
"en-gb",
|
|
||||||
"misc"
|
|
||||||
],
|
],
|
||||||
|
"dictionaries": ["npm", "softwareTerms", "node", "html", "css", "bash", "en_US", "en-gb", "misc"],
|
||||||
"ignorePaths": [
|
"ignorePaths": [
|
||||||
"package.json",
|
"package.json",
|
||||||
"dist/**",
|
"dist/**",
|
||||||
@ -505,4 +496,4 @@
|
|||||||
"html-test-results",
|
"html-test-results",
|
||||||
"test-results"
|
"test-results"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,8 @@ const config = {
|
|||||||
browser: true,
|
browser: true,
|
||||||
es2024: true,
|
es2024: true,
|
||||||
jasmine: true,
|
jasmine: true,
|
||||||
node: true,
|
amd: true,
|
||||||
worker: true,
|
node: true
|
||||||
serviceworker: true
|
|
||||||
},
|
},
|
||||||
globals: {
|
globals: {
|
||||||
_: 'readonly',
|
_: 'readonly',
|
||||||
|
2
.github/workflows/e2e-couchdb.yml
vendored
2
.github/workflows/e2e-couchdb.yml
vendored
@ -42,7 +42,7 @@ jobs:
|
|||||||
- name: Start CouchDB Docker Container and Init with Setup Scripts
|
- name: Start CouchDB Docker Container and Init with Setup Scripts
|
||||||
run: |
|
run: |
|
||||||
export $(cat src/plugins/persistence/couch/.env.ci | xargs)
|
export $(cat src/plugins/persistence/couch/.env.ci | xargs)
|
||||||
docker-compose -f src/plugins/persistence/couch/couchdb-compose.yaml up --detach
|
docker compose -f src/plugins/persistence/couch/couchdb-compose.yaml up --detach
|
||||||
sleep 3
|
sleep 3
|
||||||
bash src/plugins/persistence/couch/setup-couchdb.sh
|
bash src/plugins/persistence/couch/setup-couchdb.sh
|
||||||
bash src/plugins/persistence/couch/replace-localstorage-with-couchdb-indexhtml.sh
|
bash src/plugins/persistence/couch/replace-localstorage-with-couchdb-indexhtml.sh
|
||||||
|
2
.github/workflows/e2e-flakefinder.yml
vendored
2
.github/workflows/e2e-flakefinder.yml
vendored
@ -34,7 +34,7 @@ jobs:
|
|||||||
- run: npm ci --no-audit --progress=false
|
- run: npm ci --no-audit --progress=false
|
||||||
|
|
||||||
- name: Run E2E Tests (Repeated 10 Times)
|
- name: Run E2E Tests (Repeated 10 Times)
|
||||||
run: npm run test:e2e:stable -- --retries=0 --repeat-each=10 --max-failures=50
|
run: npm run test:e2e:ci -- --retries=0 --repeat-each=10 --max-failures=50
|
||||||
|
|
||||||
- name: Archive test results
|
- name: Archive test results
|
||||||
if: success() || failure()
|
if: success() || failure()
|
||||||
|
@ -109,7 +109,7 @@ Our e2e (end-to-end), Visual, and Performance tests leverage the Playwright fram
|
|||||||
- **e2e Tests**: These tests are run on every commit. To run the tests locally, use:
|
- **e2e Tests**: These tests are run on every commit. To run the tests locally, use:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npm run test:e2e:stable
|
npm run test:e2e:ci
|
||||||
```
|
```
|
||||||
|
|
||||||
- **Visual Tests**: For running the visual test suite, use:
|
- **Visual Tests**: For running the visual test suite, use:
|
||||||
|
@ -66,8 +66,8 @@ The e2e line coverage is a bit more complex than the karma implementation. This
|
|||||||
1. Each e2e suite will start webpack with the ```npm run start:coverage``` command with config `webpack.coverage.mjs` and the `babel-plugin-istanbul` plugin to generate code coverage during e2e test execution using our custom [baseFixture](./baseFixtures.js).
|
1. Each e2e suite will start webpack with the ```npm run start:coverage``` command with config `webpack.coverage.mjs` and the `babel-plugin-istanbul` plugin to generate code coverage during e2e test execution using our custom [baseFixture](./baseFixtures.js).
|
||||||
1. During testcase execution, each e2e shard will generate its piece of the larger coverage suite. **This coverage file is not merged**. The raw coverage file is stored in a `.nyc_report` directory.
|
1. During testcase execution, each e2e shard will generate its piece of the larger coverage suite. **This coverage file is not merged**. The raw coverage file is stored in a `.nyc_report` directory.
|
||||||
1. [nyc](https://github.com/istanbuljs/nyc) converts this directory into a `lcov` file with the following command `npm run cov:e2e:report`
|
1. [nyc](https://github.com/istanbuljs/nyc) converts this directory into a `lcov` file with the following command `npm run cov:e2e:report`
|
||||||
1. Most of the tests are run in the '@stable' configuration and focus on chrome/ubuntu at a single resolution. This coverage is published to codecov with `npm run cov:e2e:stable:publish`.
|
1. Most of the tests focus on chrome/ubuntu at a single resolution. This coverage is published to codecov with `npm run cov:e2e:ci:publish`.
|
||||||
1. The rest of our coverage only appears when run against `@unstable` tests, persistent datastore (couchdb), non-ubuntu machines, and non-chrome browsers with the `npm run cov:e2e:full:publish` flag. Since this happens about once a day, we have leveraged codecov.io's carryforward flag to report on lines covered outside of each commit on an individual PR.
|
1. The rest of our coverage only appears when run against persistent datastore (couchdb), non-ubuntu machines, and non-chrome browsers with the `npm run cov:e2e:full:publish` flag. Since this happens about once a day, we have leveraged codecov.io's carryforward flag to report on lines covered outside of each commit on an individual PR.
|
||||||
|
|
||||||
|
|
||||||
### Limitations in our code coverage reporting
|
### Limitations in our code coverage reporting
|
||||||
|
@ -11,18 +11,18 @@ coverage:
|
|||||||
informational: true
|
informational: true
|
||||||
precision: 2
|
precision: 2
|
||||||
round: down
|
round: down
|
||||||
range: '66...100'
|
range: "66...100"
|
||||||
|
|
||||||
flags:
|
flags:
|
||||||
unit:
|
unit:
|
||||||
carryforward: false
|
carryforward: false
|
||||||
e2e-stable:
|
e2e-ci:
|
||||||
carryforward: false
|
carryforward: false
|
||||||
e2e-full:
|
e2e-full:
|
||||||
carryforward: true
|
carryforward: true
|
||||||
|
|
||||||
comment:
|
comment:
|
||||||
layout: 'diff,flags,files,footer'
|
layout: "diff,flags,files,footer"
|
||||||
behavior: default
|
behavior: default
|
||||||
require_changes: false
|
require_changes: false
|
||||||
show_carryforward_flags: true
|
show_carryforward_flags: true
|
||||||
|
@ -1,14 +1,24 @@
|
|||||||
/* eslint-disable no-undef */
|
/* eslint-disable no-undef */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
extends: ['plugin:playwright/playwright-test'],
|
extends: ['plugin:playwright/recommended'],
|
||||||
rules: {
|
rules: {
|
||||||
'playwright/max-nested-describe': ['error', { max: 1 }]
|
'playwright/max-nested-describe': ['error', { max: 1 }],
|
||||||
|
'playwright/expect-expect': 'off'
|
||||||
},
|
},
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
files: ['tests/visual/*.spec.js'],
|
//Apply Best Practices to externalFixtures and exampleTemplate.e2e.spec.js
|
||||||
|
files: [
|
||||||
|
'appActions.js',
|
||||||
|
'baseFixtures.js',
|
||||||
|
'pluginFixtures.js',
|
||||||
|
'**/exampleTemplate.e2e.spec.js'
|
||||||
|
],
|
||||||
rules: {
|
rules: {
|
||||||
'playwright/no-wait-for-timeout': 'off'
|
'playwright/no-raw-locators': 'error',
|
||||||
|
'playwright/no-nth-methods': 'error',
|
||||||
|
'playwright/no-get-by-title': 'error',
|
||||||
|
'playwright/prefer-comparison-matcher': 'error'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -225,14 +225,13 @@ Current list of test tags:
|
|||||||
|:-:|-|
|
|:-:|-|
|
||||||
|`@mobile` | Test case or test suite is compatible with Playwright's iPad support and Open MCT's read-only mobile view (i.e. no create button).|
|
|`@mobile` | Test case or test suite is compatible with Playwright's iPad support and Open MCT's read-only mobile view (i.e. no create button).|
|
||||||
|`@a11y` | Test case or test suite to execute playwright-axe accessibility checks and generate a11y reports.|
|
|`@a11y` | Test case or test suite to execute playwright-axe accessibility checks and generate a11y reports.|
|
||||||
|`@gds` | Denotes a GDS Test Case used in the VIPER Mission.|
|
|
||||||
|`@addInit` | Initializes the browser with an injected and artificial state. Useful for loading non-default plugins. Likely will not work outside of `npm start`.|
|
|`@addInit` | Initializes the browser with an injected and artificial state. Useful for loading non-default plugins. Likely will not work outside of `npm start`.|
|
||||||
|`@localStorage` | Captures or generates session storage to manipulate browser state. Useful for excluding in tests which require a persistent backend (i.e. CouchDB). See [note](#utilizing-localstorage)|
|
|`@localStorage` | Captures or generates session storage to manipulate browser state. Useful for excluding in tests which require a persistent backend (i.e. CouchDB). See [note](#utilizing-localstorage)|
|
||||||
|`@snapshot` | Uses Playwright's snapshot functionality to record a copy of the DOM for direct comparison. Must be run inside of the playwright container.|
|
|`@snapshot` | Uses Playwright's snapshot functionality to record a copy of the DOM for direct comparison. Must be run inside of the playwright container.|
|
||||||
|`@unstable` | A new test or test which is known to be flaky.|
|
|
||||||
|`@2p` | Indicates that multiple users are involved, or multiple tabs/pages are used. Useful for testing multi-user interactivity.|
|
|`@2p` | Indicates that multiple users are involved, or multiple tabs/pages are used. Useful for testing multi-user interactivity.|
|
||||||
|`@generatedata` | Indicates that a test is used to generate testdata or test the generated test data. Usually to be associated with localstorage, but this may grow over time.|
|
|`@generatedata` | Indicates that a test is used to generate testdata or test the generated test data. Usually to be associated with localstorage, but this may grow over time.|
|
||||||
|`@clock` | A test which modifies the clock. These have expanded out of the visual tests and into the functional tests.
|
|`@clock` | A test which modifies the clock. These have expanded out of the visual tests and into the functional tests.
|
||||||
|
|`@framework` | A test for open mct e2e capabilities. This is primarily to ensure we don't break projects which depend on sourcing this project's fixtures like appActions.js.
|
||||||
|
|
||||||
### Continuous Integration
|
### Continuous Integration
|
||||||
|
|
||||||
@ -248,7 +247,7 @@ Our CI environment consists of 3 main modes of operation:
|
|||||||
|
|
||||||
CircleCI
|
CircleCI
|
||||||
|
|
||||||
- Stable e2e tests against ubuntu and chrome
|
- e2e tests against ubuntu and chrome
|
||||||
- Performance tests against ubuntu and chrome
|
- Performance tests against ubuntu and chrome
|
||||||
- e2e tests are linted
|
- e2e tests are linted
|
||||||
- Visual and a11y tests are run in a single resolution on the default `espresso` theme
|
- Visual and a11y tests are run in a single resolution on the default `espresso` theme
|
||||||
@ -287,18 +286,6 @@ So for every commit, Playwright is effectively running 4 x 2 concurrent browserc
|
|||||||
|
|
||||||
At the same time, we don't want to waste CI resources on parallel runs, so we've configured each shard to fail after 5 test failures. Test failure logs are recorded and stored to allow fast triage.
|
At the same time, we don't want to waste CI resources on parallel runs, so we've configured each shard to fail after 5 test failures. Test failure logs are recorded and stored to allow fast triage.
|
||||||
|
|
||||||
#### Test Promotion
|
|
||||||
|
|
||||||
In order to maintain fast and reliable feedback, tests go through a promotion process. All new test cases or test suites must be labeled with the `@unstable` annotation. The Open MCT dev team runs these unstable tests in our private repos to ensure they work downstream and are reliable.
|
|
||||||
|
|
||||||
- To run the stable tests, use the `npm run test:e2e:stable` command.
|
|
||||||
- To run the new and flaky tests, use the `npm run test:e2e:unstable` command.
|
|
||||||
|
|
||||||
A testcase and testsuite are to be unmarked as @unstable when:
|
|
||||||
|
|
||||||
1. They run as part of "full" run 5 times without failure.
|
|
||||||
2. They've been by a Open MCT Developer 5 times in the closed source repo without failure.
|
|
||||||
|
|
||||||
### Cross-browser and Cross-operating system
|
### Cross-browser and Cross-operating system
|
||||||
|
|
||||||
#### **What's supported:**
|
#### **What's supported:**
|
||||||
@ -380,8 +367,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. 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.
|
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:
|
1. Avoid app interaction when possible. The best way of doing this is to navigate directly by URL:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
@ -396,6 +382,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.
|
- 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.
|
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.
|
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
|
##### Utilizing LocalStorage
|
||||||
|
|
||||||
@ -448,6 +444,7 @@ By adhering to this principle, we can create tests that are both robust and refl
|
|||||||
- Use Open MCT's fixed-time mode unless explicitly testing realtime clock
|
- Use Open MCT's fixed-time mode unless explicitly testing realtime clock
|
||||||
- Employ the `createExampleTelemetryObject` appAction to source telemetry and specify a `name` to avoid autogenerated names.
|
- Employ the `createExampleTelemetryObject` appAction to source telemetry and specify a `name` to avoid autogenerated names.
|
||||||
- Avoid creating objects with a time component like timers and clocks.
|
- Avoid creating objects with a time component like timers and clocks.
|
||||||
|
- Utilize the playwright clock() API. See @clock Annotations for examples.
|
||||||
|
|
||||||
5. **Hide the Tree and Inspector**: Generally, your test will not require comparisons involving the tree and inspector. These aspects are covered in component-specific tests (explained below). To exclude them from the comparison by default, navigate to the root of the main view with the tree and inspector hidden:
|
5. **Hide the Tree and Inspector**: Generally, your test will not require comparisons involving the tree and inspector. These aspects are covered in component-specific tests (explained below). To exclude them from the comparison by default, navigate to the root of the main view with the tree and inspector hidden:
|
||||||
- `await page.goto('./#/browse/mine?hideTree=true&hideInspector=true')`
|
- `await page.goto('./#/browse/mine?hideTree=true&hideInspector=true')`
|
||||||
@ -493,29 +490,25 @@ For best practices with regards to mocking network responses, see our [couchdb.e
|
|||||||
The following contains a list of tips and tricks which don't exactly fit into a FAQ or Best Practices doc.
|
The following contains a list of tips and tricks which don't exactly fit into a FAQ or Best Practices doc.
|
||||||
|
|
||||||
- (Advanced) Overriding the Browser's Clock
|
- (Advanced) Overriding the Browser's Clock
|
||||||
It is possible to override the browser's clock in order to control time-based elements. Since this can cause unwanted behavior (i.e. Tree not rendering), only use this sparingly. To do this, use the `overrideClock` fixture as such:
|
It is possible to override the browser's clock in order to control time-based elements. Since this can cause unwanted behavior -- i.e. Tree not rendering -- only use this sparingly. Use the `page.clock()` API as such:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import { test, expect } from '../../pluginFixtures.js';
|
import { test, expect } from '../../pluginFixtures.js';
|
||||||
|
|
||||||
test.describe('foo test suite', () => {
|
test.describe('foo test suite @clock', () => {
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
// All subsequent tests in this suite will override the clock
|
//Set clock time
|
||||||
test.use({
|
await page.clock.install({ time: MISSION_TIME });
|
||||||
clockOptions: {
|
await page.clock.resume();
|
||||||
now: 1732413600000, // A timestamp given as milliseconds since the epoch
|
//Navigate to page with new clock
|
||||||
shouldAdvanceTime: true // Should the clock tick?
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('bar test', async ({ page }) => {
|
test('bar here', async ({ page }) => {
|
||||||
// ...
|
/// ...
|
||||||
});
|
});
|
||||||
});
|
|
||||||
```
|
```
|
||||||
|
|
||||||
More info and options for `overrideClock` can be found in [baseFixtures.js](baseFixtures.js)
|
|
||||||
|
|
||||||
- Working with multiple pages
|
- Working with multiple pages
|
||||||
There are instances where multiple browser pages will needed to verify multi-page or multi-tab application behavior. Make sure to use the `@2p` annotation as well as name each page appropriately: i.e. `page1` and `page2` or `tab1` and `tab2` depending on the intended use case. Generally pages should be used unless testing `sharedWorker` code, specifically.
|
There are instances where multiple browser pages will needed to verify multi-page or multi-tab application behavior. Make sure to use the `@2p` annotation as well as name each page appropriately: i.e. `page1` and `page2` or `tab1` and `tab2` depending on the intended use case. Generally pages should be used unless testing `sharedWorker` code, specifically.
|
||||||
|
|
||||||
|
@ -35,7 +35,6 @@
|
|||||||
* @property {string} type the type of domain object to create (e.g.: "Sine Wave Generator").
|
* @property {string} type the type of domain object to create (e.g.: "Sine Wave Generator").
|
||||||
* @property {string} [name] the desired name of the created domain object.
|
* @property {string} [name] the desired name of the created domain object.
|
||||||
* @property {string | import('../src/api/objects/ObjectAPI').Identifier} [parent] the Identifier or uuid of the parent object.
|
* @property {string | import('../src/api/objects/ObjectAPI').Identifier} [parent] the Identifier or uuid of the parent object.
|
||||||
* @property {Record<string, string>} [customParameters] any additional parameters to be passed to the domain object's form. E.g. '[aria-label="Data Rate (hz)"]': {'0.1'}
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -62,14 +61,14 @@ import { v4 as genUuid } from 'uuid';
|
|||||||
* This common function creates a domain object with the default options. It is the preferred way of creating objects
|
* This common function creates a domain object with the default options. It is the preferred way of creating objects
|
||||||
* in the e2e suite when uninterested in properties of the objects themselves.
|
* in the e2e suite when uninterested in properties of the objects themselves.
|
||||||
*
|
*
|
||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page - The Playwright page object.
|
||||||
* @param {CreateObjectOptions} options
|
* @param {Object} options - Options for creating the domain object.
|
||||||
|
* @param {string} options.type - The type of domain object to create (e.g., "Sine Wave Generator").
|
||||||
|
* @param {string} [options.name] - The desired name of the created domain object.
|
||||||
|
* @param {string | import('../src/api/objects/ObjectAPI').Identifier} [options.parent='mine'] - The Identifier or uuid of the parent object. Defaults to 'mine' folder
|
||||||
* @returns {Promise<CreatedObjectInfo>} An object containing information about the newly created domain object.
|
* @returns {Promise<CreatedObjectInfo>} An object containing information about the newly created domain object.
|
||||||
*/
|
*/
|
||||||
async function createDomainObjectWithDefaults(
|
async function createDomainObjectWithDefaults(page, { type, name, parent = 'mine' }) {
|
||||||
page,
|
|
||||||
{ type, name, parent = 'mine', customParameters = {} }
|
|
||||||
) {
|
|
||||||
if (!name) {
|
if (!name) {
|
||||||
name = `${type}:${genUuid()}`;
|
name = `${type}:${genUuid()}`;
|
||||||
}
|
}
|
||||||
@ -86,32 +85,18 @@ async function createDomainObjectWithDefaults(
|
|||||||
// Click the object specified by 'type'-- case insensitive
|
// Click the object specified by 'type'-- case insensitive
|
||||||
await page.getByRole('menuitem', { name: new RegExp(`^${type}$`, 'i') }).click();
|
await page.getByRole('menuitem', { name: new RegExp(`^${type}$`, 'i') }).click();
|
||||||
|
|
||||||
// Modify the name input field of the domain object to accept 'name'
|
// Fill in the name of the object
|
||||||
const nameInput = page.locator('form[name="mctForm"] .first input[type="text"]');
|
await page.getByLabel('Title', { exact: true }).fill('');
|
||||||
await nameInput.fill('');
|
await page.getByLabel('Title', { exact: true }).fill(name);
|
||||||
await nameInput.fill(name);
|
|
||||||
|
|
||||||
if (page.testNotes) {
|
if (page.testNotes) {
|
||||||
// Fill the "Notes" section with information about the
|
// Fill the "Notes" section with information about the
|
||||||
// currently running test and its project.
|
// currently running test and its project.
|
||||||
const notesInput = page.locator('form[name="mctForm"] #notes-textarea');
|
// eslint-disable-next-line playwright/no-raw-locators
|
||||||
await notesInput.fill(page.testNotes);
|
await page.locator('#notes-textarea').fill(page.testNotes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there are any further parameters, fill them in
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
for (const [key, value] of Object.entries(customParameters)) {
|
|
||||||
const input = page.locator(`form[name="mctForm"] ${key}`);
|
|
||||||
await input.fill('');
|
|
||||||
await input.fill(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Click OK button and wait for Navigate event
|
|
||||||
await Promise.all([
|
|
||||||
page.waitForLoadState(),
|
|
||||||
await page.getByRole('button', { name: 'Save' }).click(),
|
|
||||||
// Wait for Save Banner to appear
|
|
||||||
page.waitForSelector('.c-message-banner__message')
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Wait until the URL is updated
|
// Wait until the URL is updated
|
||||||
await page.waitForURL(`**/${parent}/*`);
|
await page.waitForURL(`**/${parent}/*`);
|
||||||
@ -151,61 +136,41 @@ async function createNotification(page, createNotificationOptions) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expand an item in the tree by a given object name.
|
* Create a Plan object from JSON with the provided options. Must be used with a json based plan.
|
||||||
|
* Please check appActions.e2e.spec.js for an example of how to use this function.
|
||||||
|
*
|
||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
*/
|
* @param {Object} json
|
||||||
async function expandTreePaneItemByName(page, name) {
|
* @param {string | import('../src/api/objects/ObjectAPI').Identifier} [parent] the uuid or identifier of the parent object. Defaults to 'mine'
|
||||||
const treePane = page.getByRole('tree', {
|
|
||||||
name: 'Main Tree'
|
|
||||||
});
|
|
||||||
const treeItem = treePane.locator(`role=treeitem[expanded=false][name=/${name}/]`);
|
|
||||||
const expandTriangle = treeItem.locator('.c-disclosure-triangle');
|
|
||||||
await expandTriangle.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a Plan object from JSON with the provided options.
|
|
||||||
* @param {import('@playwright/test').Page} page
|
|
||||||
* @param {*} options
|
|
||||||
* @returns {Promise<CreatedObjectInfo>} An object containing information about the newly created domain object.
|
* @returns {Promise<CreatedObjectInfo>} An object containing information about the newly created domain object.
|
||||||
*/
|
*/
|
||||||
async function createPlanFromJSON(page, { name, json, parent = 'mine' }) {
|
async function createPlanFromJSON(page, { name, json, parent = 'mine' }) {
|
||||||
if (!name) {
|
|
||||||
name = `Plan:${genUuid()}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const parentUrl = await getHashUrlToDomainObject(page, parent);
|
const parentUrl = await getHashUrlToDomainObject(page, parent);
|
||||||
|
|
||||||
// Navigate to the parent object. This is necessary to create the object
|
// Navigate to the parent object. This is necessary to create the object
|
||||||
// in the correct location, such as a folder, layout, or plot.
|
// in the correct location, such as a folder, layout, or plot.
|
||||||
await page.goto(`${parentUrl}`);
|
await page.goto(`${parentUrl}`);
|
||||||
|
|
||||||
// Click the Create button
|
|
||||||
await page.getByRole('button', { name: 'Create' }).click();
|
await page.getByRole('button', { name: 'Create' }).click();
|
||||||
|
|
||||||
// Click 'Plan' menu option
|
await page.getByRole('menuitem', { name: 'Plan' }).click();
|
||||||
await page.click(`li:text("Plan")`);
|
|
||||||
|
|
||||||
// Modify the name input field of the domain object to accept 'name'
|
// Fill in the name of the object or generate a random one
|
||||||
const nameInput = page.getByLabel('Title', { exact: true });
|
if (!name) {
|
||||||
await nameInput.fill('');
|
name = `Plan:${genUuid()}`;
|
||||||
await nameInput.fill(name);
|
}
|
||||||
|
await page.getByLabel('Title', { exact: true }).fill('');
|
||||||
|
await page.getByLabel('Title', { exact: true }).fill(name);
|
||||||
|
|
||||||
// Upload buffer from memory
|
// Upload buffer from memory
|
||||||
await page.locator('input#fileElem').setInputFiles({
|
await page.getByLabel('Select File...').setInputFiles({
|
||||||
name: 'plan.txt',
|
name: 'plan.txt',
|
||||||
mimeType: 'text/plain',
|
mimeType: 'text/plain',
|
||||||
buffer: Buffer.from(JSON.stringify(json))
|
buffer: Buffer.from(JSON.stringify(json))
|
||||||
});
|
});
|
||||||
|
|
||||||
// Click OK button and wait for Navigate event
|
await page.getByLabel('Save').click();
|
||||||
await Promise.all([
|
|
||||||
page.waitForLoadState(),
|
|
||||||
page.click('[aria-label="Save"]'),
|
|
||||||
// Wait for Save Banner to appear
|
|
||||||
page.waitForSelector('.c-message-banner__message')
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Wait until the URL is updated
|
// Wait until the URL is updated
|
||||||
await page.waitForURL(`**/${parent}/*`);
|
await page.waitForURL(`**/${parent}/*`);
|
||||||
@ -233,10 +198,10 @@ async function createExampleTelemetryObject(page, parent = 'mine') {
|
|||||||
|
|
||||||
await page.getByRole('button', { name: 'Create' }).click();
|
await page.getByRole('button', { name: 'Create' }).click();
|
||||||
|
|
||||||
await page.locator('li:has-text("Sine Wave Generator")').click();
|
await page.getByRole('menuitem', { name: 'Sine Wave Generator' }).click();
|
||||||
|
|
||||||
const name = 'VIPER Rover Heading';
|
const name = 'VIPER Rover Heading';
|
||||||
await page.getByRole('dialog').locator('input[type="text"]').fill(name);
|
await page.getByLabel('Title', { exact: true }).fill(name);
|
||||||
|
|
||||||
// Fill out the fields with default values
|
// Fill out the fields with default values
|
||||||
await page.getByRole('spinbutton', { name: 'Period' }).fill('10');
|
await page.getByRole('spinbutton', { name: 'Period' }).fill('10');
|
||||||
@ -263,7 +228,9 @@ async function createExampleTelemetryObject(page, parent = 'mine') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Navigates directly to a given object url, in fixed time mode, with the given start and end bounds.
|
* Navigates directly to a given object url, in fixed time mode, with the given start and end bounds. Note: does not set
|
||||||
|
* default view type.
|
||||||
|
*
|
||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
* @param {string} url The url to the domainObject
|
* @param {string} url The url to the domainObject
|
||||||
* @param {string | number} start The starting time bound in milliseconds since epoch
|
* @param {string | number} start The starting time bound in milliseconds since epoch
|
||||||
@ -276,9 +243,13 @@ async function navigateToObjectWithFixedTimeBounds(page, url, start, end) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Navigates directly to a given object url, in real-time mode.
|
* Navigates directly to a given object url, in real-time mode. Note: does not set
|
||||||
|
* default view type.
|
||||||
|
*
|
||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
* @param {string} url The url to the domainObject
|
* @param {string} url The url to the domainObject
|
||||||
|
* @param {string | number} start The start offset in milliseconds
|
||||||
|
* @param {string | number} end The end offset in milliseconds
|
||||||
*/
|
*/
|
||||||
async function navigateToObjectWithRealTime(page, url, start = '1800000', end = '30000') {
|
async function navigateToObjectWithRealTime(page, url, start = '1800000', end = '30000') {
|
||||||
await page.goto(
|
await page.goto(
|
||||||
@ -287,23 +258,11 @@ async function navigateToObjectWithRealTime(page, url, start = '1800000', end =
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open the given `domainObject`'s context menu from the object tree.
|
* Expands the entire object tree (every expandable tree item). Can be used to
|
||||||
* Expands the path to the object and scrolls to it if necessary.
|
* ensure that the tree is fully expanded before performing actions on objects.
|
||||||
|
* Can be applied to either the main tree or the create modal tree.
|
||||||
*
|
*
|
||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
* @param {string} url the url to the object
|
|
||||||
*/
|
|
||||||
async function openObjectTreeContextMenu(page, url) {
|
|
||||||
await page.goto(url);
|
|
||||||
await page.getByLabel('Show selected item in tree').click();
|
|
||||||
await page.locator('.is-navigated-object').click({
|
|
||||||
button: 'right'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Expands the entire object tree (every expandable tree item).
|
|
||||||
* @param {import('@playwright/test').Page} page
|
|
||||||
* @param {"Main Tree" | "Create Modal Tree"} [treeName="Main Tree"]
|
* @param {"Main Tree" | "Create Modal Tree"} [treeName="Main Tree"]
|
||||||
*/
|
*/
|
||||||
async function expandEntireTree(page, treeName = 'Main Tree') {
|
async function expandEntireTree(page, treeName = 'Main Tree') {
|
||||||
@ -314,9 +273,10 @@ async function expandEntireTree(page, treeName = 'Main Tree') {
|
|||||||
.getByRole('treeitem', {
|
.getByRole('treeitem', {
|
||||||
expanded: false
|
expanded: false
|
||||||
})
|
})
|
||||||
.locator('span.c-disclosure-triangle.is-enabled');
|
.getByLabel(/Expand/);
|
||||||
|
|
||||||
while ((await collapsedTreeItems.count()) > 0) {
|
while ((await collapsedTreeItems.count()) > 0) {
|
||||||
|
//eslint-disable-next-line playwright/no-nth-methods
|
||||||
await collapsedTreeItems.nth(0).click();
|
await collapsedTreeItems.nth(0).click();
|
||||||
|
|
||||||
// FIXME: Replace hard wait with something event-driven.
|
// FIXME: Replace hard wait with something event-driven.
|
||||||
@ -388,10 +348,11 @@ async function _isInEditMode(page, identifier) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the time conductor mode to either fixed timespan or realtime mode.
|
* Set the time conductor mode to either fixed timespan or realtime mode.
|
||||||
|
* @private
|
||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
* @param {boolean} [isFixedTimespan=true] true for fixed timespan mode, false for realtime mode; default is true
|
* @param {boolean} [isFixedTimespan=true] true for fixed timespan mode, false for realtime mode; default is true
|
||||||
*/
|
*/
|
||||||
async function setTimeConductorMode(page, isFixedTimespan = true) {
|
async function _setTimeConductorMode(page, isFixedTimespan = true) {
|
||||||
// Click 'mode' button
|
// Click 'mode' button
|
||||||
await page.getByRole('button', { name: 'Time Conductor Mode', exact: true }).click();
|
await page.getByRole('button', { name: 'Time Conductor Mode', exact: true }).click();
|
||||||
await page.getByRole('button', { name: 'Time Conductor Mode Menu' }).click();
|
await page.getByRole('button', { name: 'Time Conductor Mode Menu' }).click();
|
||||||
@ -412,7 +373,7 @@ async function setTimeConductorMode(page, isFixedTimespan = true) {
|
|||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
*/
|
*/
|
||||||
async function setFixedTimeMode(page) {
|
async function setFixedTimeMode(page) {
|
||||||
await setTimeConductorMode(page, true);
|
await _setTimeConductorMode(page, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -420,7 +381,7 @@ async function setFixedTimeMode(page) {
|
|||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
*/
|
*/
|
||||||
async function setRealTimeMode(page) {
|
async function setRealTimeMode(page) {
|
||||||
await setTimeConductorMode(page, false);
|
await _setTimeConductorMode(page, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -542,19 +503,20 @@ async function setTimeConductorBounds(page, { submitChanges = true, ...bounds })
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the independent time conductor bounds in fixed time mode
|
* Set the bounds of the visible conductor in fixed time mode.
|
||||||
|
* Requires that page already has an independent time conductor in view.
|
||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
* @param {string} startDate
|
* @param {string} start - The start date in 'YYYY-MM-DD HH:mm:ss.SSSZ' format
|
||||||
* @param {string} endDate
|
* @param {string} end - The end date in 'YYYY-MM-DD HH:mm:ss.SSSZ' format
|
||||||
*/
|
*/
|
||||||
async function setIndependentTimeConductorBounds(page, { start, end }) {
|
async function setFixedIndependentTimeConductorBounds(page, { start, end }) {
|
||||||
// Activate Independent Time Conductor
|
// Activate Independent Time Conductor
|
||||||
await page.getByLabel('Enable Independent Time Conductor').click();
|
await page.getByLabel('Enable Independent Time Conductor').click();
|
||||||
|
|
||||||
// Bring up the time conductor popup
|
// Bring up the time conductor popup
|
||||||
await page.getByLabel('Independent Time Conductor Settings').click();
|
await page.getByLabel('Independent Time Conductor Settings').click();
|
||||||
await expect(page.locator('.itc-popout')).toBeInViewport();
|
await expect(page.getByLabel('Time Conductor Options')).toBeInViewport();
|
||||||
await setTimeBounds(page, start, end);
|
await _setTimeBounds(page, start, end);
|
||||||
|
|
||||||
await page.keyboard.press('Enter');
|
await page.keyboard.press('Enter');
|
||||||
}
|
}
|
||||||
@ -563,10 +525,10 @@ async function setIndependentTimeConductorBounds(page, { start, end }) {
|
|||||||
* Set the bounds of the visible conductor in fixed time mode
|
* Set the bounds of the visible conductor in fixed time mode
|
||||||
* @private
|
* @private
|
||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
* @param {string} startDate
|
* @param {string} start - The start date in 'YYYY-MM-DD HH:mm:ss.SSSZ' format
|
||||||
* @param {string} endDate
|
* @param {string} end - The end date in 'YYYY-MM-DD HH:mm:ss.SSSZ' format
|
||||||
*/
|
*/
|
||||||
async function setTimeBounds(page, startDate, endDate) {
|
async function _setTimeBounds(page, startDate, endDate) {
|
||||||
if (startDate) {
|
if (startDate) {
|
||||||
// Fill start time
|
// Fill start time
|
||||||
await page
|
await page
|
||||||
@ -596,11 +558,13 @@ async function setTimeBounds(page, startDate, endDate) {
|
|||||||
* all plots on the page and waits up to the default timeout for the class to be
|
* all plots on the page and waits up to the default timeout for the class to be
|
||||||
* attached to each plot.
|
* attached to each plot.
|
||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
|
* @param {number} [timeout] Provide a custom timeout in milliseconds to override the default timeout
|
||||||
*/
|
*/
|
||||||
async function waitForPlotsToRender(page) {
|
async function waitForPlotsToRender(page, { timeout } = {}) {
|
||||||
|
//eslint-disable-next-line playwright/no-raw-locators
|
||||||
const plotLocator = page.locator('.gl-plot');
|
const plotLocator = page.locator('.gl-plot');
|
||||||
for (const plot of await plotLocator.all()) {
|
for (const plot of await plotLocator.all()) {
|
||||||
await expect(plot).toHaveClass(/js-series-data-loaded/);
|
await expect(plot).toHaveClass(/js-series-data-loaded/, { timeout });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -665,41 +629,20 @@ async function getCanvasPixels(page, canvasSelector) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {import('@playwright/test').Page} page
|
|
||||||
* @param {string} myItemsFolderName
|
|
||||||
* @param {string} url
|
|
||||||
* @param {string} newName
|
|
||||||
*/
|
|
||||||
async function renameObjectFromContextMenu(page, url, newName) {
|
|
||||||
await openObjectTreeContextMenu(page, url);
|
|
||||||
await page.click('li:text("Edit Properties")');
|
|
||||||
const nameInput = page.getByLabel('Title', { exact: true });
|
|
||||||
await nameInput.fill('');
|
|
||||||
await nameInput.fill(newName);
|
|
||||||
await page.click('[aria-label="Save"]');
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
createDomainObjectWithDefaults,
|
createDomainObjectWithDefaults,
|
||||||
createExampleTelemetryObject,
|
createExampleTelemetryObject,
|
||||||
createNotification,
|
createNotification,
|
||||||
createPlanFromJSON,
|
createPlanFromJSON,
|
||||||
expandEntireTree,
|
expandEntireTree,
|
||||||
expandTreePaneItemByName,
|
|
||||||
getCanvasPixels,
|
getCanvasPixels,
|
||||||
getFocusedObjectUuid,
|
|
||||||
getHashUrlToDomainObject,
|
|
||||||
navigateToObjectWithFixedTimeBounds,
|
navigateToObjectWithFixedTimeBounds,
|
||||||
navigateToObjectWithRealTime,
|
navigateToObjectWithRealTime,
|
||||||
openObjectTreeContextMenu,
|
|
||||||
renameObjectFromContextMenu,
|
|
||||||
setEndOffset,
|
setEndOffset,
|
||||||
|
setFixedIndependentTimeConductorBounds,
|
||||||
setFixedTimeMode,
|
setFixedTimeMode,
|
||||||
setIndependentTimeConductorBounds,
|
|
||||||
setRealTimeMode,
|
setRealTimeMode,
|
||||||
setStartOffset,
|
setStartOffset,
|
||||||
setTimeConductorBounds,
|
setTimeConductorBounds,
|
||||||
setTimeConductorMode,
|
|
||||||
waitForPlotsToRender
|
waitForPlotsToRender
|
||||||
};
|
};
|
||||||
|
@ -30,7 +30,6 @@
|
|||||||
import { expect, request, test } from '@playwright/test';
|
import { expect, request, test } from '@playwright/test';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import sinon from 'sinon';
|
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
@ -70,82 +69,6 @@ const extendedTest = test.extend({
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
coveragePath: [istanbulCLIOutput, { option: true }],
|
coveragePath: [istanbulCLIOutput, { option: true }],
|
||||||
/**
|
|
||||||
* This allows the test to manipulate the browser clock. This is useful for Visual and Snapshot tests which need
|
|
||||||
* the Time Indicator Clock to be in a specific state.
|
|
||||||
*
|
|
||||||
* Warning: Has many limitations and secondary side effects in Open MCT.
|
|
||||||
* 1. The tree component does not render.
|
|
||||||
* 2. page.WaitForNavigation does not trigger.
|
|
||||||
*
|
|
||||||
* Usage:
|
|
||||||
* ```js
|
|
||||||
* test.use({
|
|
||||||
* clockOptions: {
|
|
||||||
* now: MISSION_TIME,
|
|
||||||
* shouldAdvanceTime: true
|
|
||||||
* ```
|
|
||||||
* If clockOptions are provided, will override the default clock with fake timers provided by SinonJS.
|
|
||||||
*
|
|
||||||
* Default: `undefined`
|
|
||||||
*
|
|
||||||
* @see {@link https://github.com/microsoft/playwright/issues/6347 Github RFE}
|
|
||||||
* @see {@link https://github.com/sinonjs/fake-timers/#var-clock--faketimersinstallconfig SinonJS FakeTimers Config}
|
|
||||||
* @type {import('@types/sinonjs__fake-timers').FakeTimerInstallOpts}
|
|
||||||
*/
|
|
||||||
clockOptions: [undefined, { option: true }],
|
|
||||||
overrideClock: [
|
|
||||||
async ({ context, clockOptions }, use) => {
|
|
||||||
if (clockOptions !== undefined) {
|
|
||||||
await context.addInitScript({
|
|
||||||
path: fileURLToPath(new URL('../node_modules/sinon/pkg/sinon.js', import.meta.url))
|
|
||||||
});
|
|
||||||
await context.addInitScript((options) => {
|
|
||||||
window.__clock = sinon.useFakeTimers(options);
|
|
||||||
}, clockOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
await use(context);
|
|
||||||
},
|
|
||||||
{
|
|
||||||
auto: true,
|
|
||||||
scope: 'test'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
/**
|
|
||||||
* Exposes a function to manually tick the clock. This is useful when overriding the clock to not
|
|
||||||
* tick (`shouldAdvanceTime: false`) for visual tests, as events such as re-renders and router params
|
|
||||||
* updates are clock-driven and must be manually ticked.
|
|
||||||
*
|
|
||||||
* Usage:
|
|
||||||
* ```js
|
|
||||||
* test.describe('Manual Clock Tick', () => {
|
|
||||||
* test.use({
|
|
||||||
* clockOptions: {
|
|
||||||
* now: MISSION_TIME, // Set to the desired time
|
|
||||||
* shouldAdvanceTime: false // Clock overridden to no longer tick
|
|
||||||
* }
|
|
||||||
* });
|
|
||||||
* test('Visual - Manual Clock Tick', async ({ page, tick }) => {
|
|
||||||
* // Tick the clock 2 seconds in the future
|
|
||||||
* await tick(2000);
|
|
||||||
* });
|
|
||||||
* });
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @param {Object} param0
|
|
||||||
* @param {import('@playwright/test').Page} param0.page
|
|
||||||
* @param {import('@playwright/test').Use} param0.use
|
|
||||||
*/
|
|
||||||
tick: async ({ page }, use) => {
|
|
||||||
// eslint-disable-next-line func-style
|
|
||||||
const tick = async (milliseconds) => {
|
|
||||||
await page.evaluate((_milliseconds) => {
|
|
||||||
window.__clock.tick(_milliseconds);
|
|
||||||
}, milliseconds);
|
|
||||||
};
|
|
||||||
await use(tick);
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
* Extends the base context class to add codecoverage shim.
|
* Extends the base context class to add codecoverage shim.
|
||||||
* @see {@link https://github.com/mxschmitt/playwright-test-coverage Github Project}
|
* @see {@link https://github.com/mxschmitt/playwright-test-coverage Github Project}
|
||||||
@ -184,20 +107,7 @@ const extendedTest = test.extend({
|
|||||||
* Extends the base page class to enable console log error detection.
|
* Extends the base page class to enable console log error detection.
|
||||||
* @see {@link https://github.com/microsoft/playwright/discussions/11690 Github Discussion}
|
* @see {@link https://github.com/microsoft/playwright/discussions/11690 Github Discussion}
|
||||||
*/
|
*/
|
||||||
page: async ({ page, failOnConsoleError, clockOptions }, use) => {
|
page: async ({ page, failOnConsoleError }, use) => {
|
||||||
// If overriding the clock, we must also override the Date.now()
|
|
||||||
// function in the generatorWorker context. This is necessary
|
|
||||||
// to ensure that example telemetry data is generated for the new clock time.
|
|
||||||
if (clockOptions?.now !== undefined) {
|
|
||||||
page.on('worker', (worker) => {
|
|
||||||
if (worker.url().includes('generatorWorker')) {
|
|
||||||
worker.evaluate((time) => {
|
|
||||||
self.Date.now = () => time;
|
|
||||||
}, clockOptions.now);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Capture any console errors during test execution
|
// Capture any console errors during test execution
|
||||||
const messages = [];
|
const messages = [];
|
||||||
page.on('console', (msg) => messages.push(msg));
|
page.on('console', (msg) => messages.push(msg));
|
||||||
@ -207,28 +117,12 @@ const extendedTest = test.extend({
|
|||||||
// Assert against console errors during teardown
|
// Assert against console errors during teardown
|
||||||
if (failOnConsoleError) {
|
if (failOnConsoleError) {
|
||||||
messages.forEach((msg) =>
|
messages.forEach((msg) =>
|
||||||
|
// eslint-disable-next-line playwright/no-standalone-expect
|
||||||
expect
|
expect
|
||||||
.soft(msg.type(), `Console error detected: ${_consoleMessageToString(msg)}`)
|
.soft(msg.type(), `Console error detected: ${_consoleMessageToString(msg)}`)
|
||||||
.not.toEqual('error')
|
.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,9 @@ export async function navigateToFaultManagementWithoutExample(page) {
|
|||||||
/**
|
/**
|
||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
*/
|
*/
|
||||||
export async function navigateToFaultItemInTree(page) {
|
async function navigateToFaultItemInTree(page) {
|
||||||
await page.goto('./', { waitUntil: 'networkidle' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
|
await page.waitForURL('**/#/browse/mine?**');
|
||||||
|
|
||||||
const faultManagementTreeItem = page
|
const faultManagementTreeItem = page
|
||||||
.getByRole('tree', {
|
.getByRole('tree', {
|
||||||
|
@ -63,7 +63,7 @@ async function dragAndDropEmbed(page, notebookObject) {
|
|||||||
// Expand the tree to reveal the notebook
|
// Expand the tree to reveal the notebook
|
||||||
await page.getByLabel('Show selected item in tree').click();
|
await page.getByLabel('Show selected item in tree').click();
|
||||||
// Drag and drop the SWG into the notebook
|
// Drag and drop the SWG into the notebook
|
||||||
await page.dragAndDrop(`text=${swg.name}`, NOTEBOOK_DROP_AREA);
|
await page.getByLabel(`Navigate to ${swg.name}`).dragTo(page.locator(NOTEBOOK_DROP_AREA));
|
||||||
await commitEntry(page);
|
await commitEntry(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,6 +84,7 @@ async function startAndAddRestrictedNotebookObject(page) {
|
|||||||
path: fileURLToPath(new URL('./addInitRestrictedNotebook.js', import.meta.url))
|
path: fileURLToPath(new URL('./addInitRestrictedNotebook.js', import.meta.url))
|
||||||
});
|
});
|
||||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
|
await page.waitForURL('**/browse/mine?**');
|
||||||
|
|
||||||
return createDomainObjectWithDefaults(page, {
|
return createDomainObjectWithDefaults(page, {
|
||||||
type: CUSTOM_NAME,
|
type: CUSTOM_NAME,
|
||||||
@ -95,10 +96,9 @@ async function startAndAddRestrictedNotebookObject(page) {
|
|||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
*/
|
*/
|
||||||
async function lockPage(page) {
|
async function lockPage(page) {
|
||||||
const commitButton = page.locator('button:has-text("Commit Entries")');
|
// Click the Commit Entries button
|
||||||
await commitButton.click();
|
await page.getByLabel('Commit Entries').click();
|
||||||
|
// Wait until Lock Banner is visible
|
||||||
//Wait until Lock Banner is visible
|
|
||||||
await page.locator('text=Lock Page').click();
|
await page.locator('text=Lock Page').click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,9 +30,9 @@ import { expect } from '../pluginFixtures.js';
|
|||||||
* start time as the start bound and the current activity's end time as the end bound.
|
* start time as the start bound and the current activity's end time as the end bound.
|
||||||
* @param {import('@playwright/test').Page} page the page
|
* @param {import('@playwright/test').Page} page the page
|
||||||
* @param {Object} plan The raw plan json to assert against
|
* @param {Object} plan The raw plan json to assert against
|
||||||
* @param {string} objectUrl The URL of the object to assert against (plan or gantt chart)
|
* @param {string} planObjectUrl The URL of the object to assert against (plan or gantt chart)
|
||||||
*/
|
*/
|
||||||
export async function assertPlanActivities(page, plan, objectUrl) {
|
export async function assertPlanActivities(page, plan, planObjectUrl) {
|
||||||
const groups = Object.keys(plan);
|
const groups = Object.keys(plan);
|
||||||
for (const group of groups) {
|
for (const group of groups) {
|
||||||
for (let i = 0; i < plan[group].length; i++) {
|
for (let i = 0; i < plan[group].length; i++) {
|
||||||
@ -48,13 +48,12 @@ export async function assertPlanActivities(page, plan, objectUrl) {
|
|||||||
|
|
||||||
// Switch to fixed time mode with all plan events within the bounds
|
// Switch to fixed time mode with all plan events within the bounds
|
||||||
await page.goto(
|
await page.goto(
|
||||||
`${objectUrl}?tc.mode=fixed&tc.startBound=${startBound}&tc.endBound=${endBound}&tc.timeSystem=utc&view=plan.view`
|
`${planObjectUrl}?tc.mode=fixed&tc.startBound=${startBound}&tc.endBound=${endBound}&tc.timeSystem=utc&view=plan.view`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Assert that the number of activities in the plan view matches the number of
|
// Assert that the number of activities in the plan view matches the number of
|
||||||
// activities in the plan data within the specified time bounds
|
// activities in the plan data within the specified time bounds
|
||||||
const eventCount = await page.locator('.activity-bounds').count();
|
await expect(page.locator('.activity-bounds')).toHaveCount(
|
||||||
expect(eventCount).toEqual(
|
|
||||||
Object.values(plan)
|
Object.values(plan)
|
||||||
.flat()
|
.flat()
|
||||||
.filter((event) =>
|
.filter((event) =>
|
||||||
@ -101,8 +100,8 @@ export async function assertPlanOrderedSwimLanes(page, plan, objectUrl) {
|
|||||||
for (let i = 0; i < groups.length; i++) {
|
for (let i = 0; i < groups.length; i++) {
|
||||||
// Assert that the order of groups in the plan view matches the order of
|
// Assert that the order of groups in the plan view matches the order of
|
||||||
// groups in the plan data
|
// groups in the plan data
|
||||||
const groupName = await planGroups[i].innerText();
|
const groupName = planGroups[i];
|
||||||
expect(groupName).toEqual(groups[i].name);
|
await expect(groupName).toHaveText(groups[i].name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,16 +14,14 @@
|
|||||||
"test:visual": "percy exec"
|
"test:visual": "percy exec"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/sinonjs__fake-timers": "8.1.5",
|
|
||||||
"@percy/cli": "1.27.4",
|
"@percy/cli": "1.27.4",
|
||||||
"@percy/playwright": "1.0.4",
|
"@percy/playwright": "1.0.4",
|
||||||
"@playwright/test": "1.45.2",
|
"@playwright/test": "1.45.2",
|
||||||
"@axe-core/playwright": "4.8.5",
|
"@axe-core/playwright": "4.8.5"
|
||||||
"sinon": "17.0.0"
|
|
||||||
},
|
},
|
||||||
"author": {
|
"author": {
|
||||||
"name": "National Aeronautics and Space Administration",
|
"name": "National Aeronautics and Space Administration",
|
||||||
"url": "https://www.nasa.gov"
|
"url": "https://www.nasa.gov"
|
||||||
},
|
},
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -6,15 +6,15 @@
|
|||||||
"localStorage": [
|
"localStorage": [
|
||||||
{
|
{
|
||||||
"name": "mct",
|
"name": "mct",
|
||||||
"value": "{\"mine\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"e78ca721-fb5e-409b-bf6d-597c87cb716f\",\"namespace\":\"\"},{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"modified\":1732413602460,\"created\":1732413600960,\"persisted\":1732413602460},\"e78ca721-fb5e-409b-bf6d-597c87cb716f\":{\"identifier\":{\"key\":\"e78ca721-fb5e-409b-bf6d-597c87cb716f\",\"namespace\":\"\"},\"name\":\"Overlay Plot with Telemetry Object\",\"type\":\"telemetry.plot.overlay\",\"composition\":[{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"}],\"configuration\":{\"series\":[{\"identifier\":{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"}}]},\"notes\":\"framework/generateLocalStorageData.e2e.spec.js\\nGenerate Visual Test Data @localStorage @generatedata\\nGenerate Overlay Plot with Telemetry Object\\nchrome\",\"modified\":1732413603880,\"location\":\"mine\",\"created\":1732413601740,\"persisted\":1732413603880},\"c6100044-56be-44b3-acca-6b9fddfb3849\":{\"name\":\"VIPER Rover Heading\",\"type\":\"generator\",\"identifier\":{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"},\"telemetry\":{\"period\":10,\"amplitude\":1,\"offset\":0,\"dataRateInHz\":1,\"phase\":0,\"randomness\":0,\"loadDelay\":0,\"infinityValues\":false,\"exceedFloat32\":false,\"staleness\":false},\"modified\":1732413602460,\"location\":\"mine\",\"created\":1732413602460,\"persisted\":1732413602460}}"
|
"value": "{\"mine\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"d3014736-1182-4b70-8122-6d0c6ef540e1\",\"namespace\":\"\"},{\"key\":\"8c53d61f-b514-4535-be87-0fb20eb56576\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"modified\":1732413605677,\"created\":1732413603298,\"persisted\":1732413605677},\"d3014736-1182-4b70-8122-6d0c6ef540e1\":{\"identifier\":{\"key\":\"d3014736-1182-4b70-8122-6d0c6ef540e1\",\"namespace\":\"\"},\"name\":\"Overlay Plot with Telemetry Object\",\"type\":\"telemetry.plot.overlay\",\"composition\":[{\"key\":\"8c53d61f-b514-4535-be87-0fb20eb56576\",\"namespace\":\"\"}],\"configuration\":{\"series\":[]},\"notes\":\"framework/generateLocalStorageData.e2e.spec.js\\nGenerate Visual Test Data @localStorage @generatedata @clock\\nGenerate Overlay Plot with Telemetry Object\\nchrome\",\"modified\":1732413607031,\"location\":\"mine\",\"created\":1732413605018,\"persisted\":1732413607031},\"8c53d61f-b514-4535-be87-0fb20eb56576\":{\"name\":\"VIPER Rover Heading\",\"type\":\"generator\",\"identifier\":{\"key\":\"8c53d61f-b514-4535-be87-0fb20eb56576\",\"namespace\":\"\"},\"telemetry\":{\"period\":10,\"amplitude\":1,\"offset\":0,\"dataRateInHz\":1,\"phase\":0,\"randomness\":0,\"loadDelay\":0,\"infinityValues\":false,\"exceedFloat32\":false,\"staleness\":false},\"modified\":1732413605677,\"location\":\"mine\",\"created\":1732413605677,\"persisted\":1732413605677}}"
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mct-recent-objects",
|
|
||||||
"value": "[{\"objectPath\":[{\"identifier\":{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"},\"name\":\"VIPER Rover Heading\",\"type\":\"generator\",\"telemetry\":{\"period\":10,\"amplitude\":1,\"offset\":0,\"dataRateInHz\":1,\"phase\":0,\"randomness\":0,\"loadDelay\":0,\"infinityValues\":false,\"exceedFloat32\":false,\"staleness\":false},\"modified\":1732413602460,\"location\":\"mine\",\"created\":1732413602460,\"persisted\":1732413602460},{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"e78ca721-fb5e-409b-bf6d-597c87cb716f\",\"namespace\":\"\"},{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"modified\":1732413602460,\"created\":1732413600960,\"persisted\":1732413602460},{\"identifier\":{\"key\":\"ROOT\",\"namespace\":\"\"},\"name\":\"Open MCT\",\"type\":\"root\",\"composition\":[{\"key\":\"mine\",\"namespace\":\"\"}]}],\"navigationPath\":\"/browse/mine/c6100044-56be-44b3-acca-6b9fddfb3849\",\"domainObject\":{\"identifier\":{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"},\"name\":\"VIPER Rover Heading\",\"type\":\"generator\",\"telemetry\":{\"period\":10,\"amplitude\":1,\"offset\":0,\"dataRateInHz\":1,\"phase\":0,\"randomness\":0,\"loadDelay\":0,\"infinityValues\":false,\"exceedFloat32\":false,\"staleness\":false},\"modified\":1732413602460,\"location\":\"mine\",\"created\":1732413602460,\"persisted\":1732413602460}},{\"objectPath\":[{\"identifier\":{\"key\":\"e78ca721-fb5e-409b-bf6d-597c87cb716f\",\"namespace\":\"\"},\"name\":\"Overlay Plot with Telemetry Object\",\"type\":\"telemetry.plot.overlay\",\"composition\":[{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"}],\"configuration\":{\"series\":[{\"identifier\":{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"}}]},\"notes\":\"framework/generateLocalStorageData.e2e.spec.js\\nGenerate Visual Test Data @localStorage @generatedata\\nGenerate Overlay Plot with Telemetry Object\\nchrome\",\"modified\":1732413603880,\"location\":\"mine\",\"created\":1732413601740,\"persisted\":1732413603880},{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"e78ca721-fb5e-409b-bf6d-597c87cb716f\",\"namespace\":\"\"},{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"modified\":1732413602460,\"created\":1732413600960,\"persisted\":1732413602460},{\"identifier\":{\"key\":\"ROOT\",\"namespace\":\"\"},\"name\":\"Open MCT\",\"type\":\"root\",\"composition\":[{\"key\":\"mine\",\"namespace\":\"\"}]}],\"navigationPath\":\"/browse/mine/e78ca721-fb5e-409b-bf6d-597c87cb716f\",\"domainObject\":{\"identifier\":{\"key\":\"e78ca721-fb5e-409b-bf6d-597c87cb716f\",\"namespace\":\"\"},\"name\":\"Overlay Plot with Telemetry Object\",\"type\":\"telemetry.plot.overlay\",\"composition\":[{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"}],\"configuration\":{\"series\":[{\"identifier\":{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"}}]},\"notes\":\"framework/generateLocalStorageData.e2e.spec.js\\nGenerate Visual Test Data @localStorage @generatedata\\nGenerate Overlay Plot with Telemetry Object\\nchrome\",\"modified\":1732413603880,\"location\":\"mine\",\"created\":1732413601740,\"persisted\":1732413603880}},{\"objectPath\":[{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"e78ca721-fb5e-409b-bf6d-597c87cb716f\",\"namespace\":\"\"},{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"modified\":1732413602460,\"created\":1732413600960,\"persisted\":1732413602460},{\"identifier\":{\"key\":\"ROOT\",\"namespace\":\"\"},\"name\":\"Open MCT\",\"type\":\"root\",\"composition\":[{\"key\":\"mine\",\"namespace\":\"\"}]}],\"navigationPath\":\"/browse/mine\",\"domainObject\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"e78ca721-fb5e-409b-bf6d-597c87cb716f\",\"namespace\":\"\"},{\"key\":\"c6100044-56be-44b3-acca-6b9fddfb3849\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"modified\":1732413602460,\"created\":1732413600960,\"persisted\":1732413602460}}]"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mct-tree-expanded",
|
"name": "mct-tree-expanded",
|
||||||
"value": "[]"
|
"value": "[]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "mct-recent-objects",
|
||||||
|
"value": "[{\"objectPath\":[{\"identifier\":{\"key\":\"8c53d61f-b514-4535-be87-0fb20eb56576\",\"namespace\":\"\"},\"name\":\"VIPER Rover Heading\",\"type\":\"generator\",\"telemetry\":{\"period\":10,\"amplitude\":1,\"offset\":0,\"dataRateInHz\":1,\"phase\":0,\"randomness\":0,\"loadDelay\":0,\"infinityValues\":false,\"exceedFloat32\":false,\"staleness\":false},\"modified\":1732413605677,\"location\":\"mine\",\"created\":1732413605677,\"persisted\":1732413605677},{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"d3014736-1182-4b70-8122-6d0c6ef540e1\",\"namespace\":\"\"},{\"key\":\"8c53d61f-b514-4535-be87-0fb20eb56576\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"modified\":1732413605677,\"created\":1732413603298,\"persisted\":1732413605677},{\"identifier\":{\"key\":\"ROOT\",\"namespace\":\"\"},\"name\":\"Open MCT\",\"type\":\"root\",\"composition\":[{\"key\":\"mine\",\"namespace\":\"\"}]}],\"navigationPath\":\"/browse/mine/8c53d61f-b514-4535-be87-0fb20eb56576\",\"domainObject\":{\"identifier\":{\"key\":\"8c53d61f-b514-4535-be87-0fb20eb56576\",\"namespace\":\"\"},\"name\":\"VIPER Rover Heading\",\"type\":\"generator\",\"telemetry\":{\"period\":10,\"amplitude\":1,\"offset\":0,\"dataRateInHz\":1,\"phase\":0,\"randomness\":0,\"loadDelay\":0,\"infinityValues\":false,\"exceedFloat32\":false,\"staleness\":false},\"modified\":1732413605677,\"location\":\"mine\",\"created\":1732413605677,\"persisted\":1732413605677}},{\"objectPath\":[{\"identifier\":{\"key\":\"d3014736-1182-4b70-8122-6d0c6ef540e1\",\"namespace\":\"\"},\"name\":\"Overlay Plot with Telemetry Object\",\"type\":\"telemetry.plot.overlay\",\"composition\":[{\"key\":\"8c53d61f-b514-4535-be87-0fb20eb56576\",\"namespace\":\"\"}],\"configuration\":{\"series\":[{\"identifier\":{\"key\":\"8c53d61f-b514-4535-be87-0fb20eb56576\",\"namespace\":\"\"}}]},\"notes\":\"framework/generateLocalStorageData.e2e.spec.js\\nGenerate Visual Test Data @localStorage @generatedata @clock\\nGenerate Overlay Plot with Telemetry Object\\nchrome\",\"modified\":1732413607031,\"location\":\"mine\",\"created\":1732413605018,\"persisted\":1732413607031},{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"d3014736-1182-4b70-8122-6d0c6ef540e1\",\"namespace\":\"\"},{\"key\":\"8c53d61f-b514-4535-be87-0fb20eb56576\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"modified\":1732413605677,\"created\":1732413603298,\"persisted\":1732413605677},{\"identifier\":{\"key\":\"ROOT\",\"namespace\":\"\"},\"name\":\"Open MCT\",\"type\":\"root\",\"composition\":[{\"key\":\"mine\",\"namespace\":\"\"}]}],\"navigationPath\":\"/browse/mine/d3014736-1182-4b70-8122-6d0c6ef540e1\",\"domainObject\":{\"identifier\":{\"key\":\"d3014736-1182-4b70-8122-6d0c6ef540e1\",\"namespace\":\"\"},\"name\":\"Overlay Plot with Telemetry Object\",\"type\":\"telemetry.plot.overlay\",\"composition\":[{\"key\":\"8c53d61f-b514-4535-be87-0fb20eb56576\",\"namespace\":\"\"}],\"configuration\":{\"series\":[{\"identifier\":{\"key\":\"8c53d61f-b514-4535-be87-0fb20eb56576\",\"namespace\":\"\"}}]},\"notes\":\"framework/generateLocalStorageData.e2e.spec.js\\nGenerate Visual Test Data @localStorage @generatedata @clock\\nGenerate Overlay Plot with Telemetry Object\\nchrome\",\"modified\":1732413607031,\"location\":\"mine\",\"created\":1732413605018,\"persisted\":1732413607031}},{\"objectPath\":[{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"d3014736-1182-4b70-8122-6d0c6ef540e1\",\"namespace\":\"\"},{\"key\":\"8c53d61f-b514-4535-be87-0fb20eb56576\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"modified\":1732413605677,\"created\":1732413603298,\"persisted\":1732413605677},{\"identifier\":{\"key\":\"ROOT\",\"namespace\":\"\"},\"name\":\"Open MCT\",\"type\":\"root\",\"composition\":[{\"key\":\"mine\",\"namespace\":\"\"}]}],\"navigationPath\":\"/browse/mine\",\"domainObject\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"d3014736-1182-4b70-8122-6d0c6ef540e1\",\"namespace\":\"\"},{\"key\":\"8c53d61f-b514-4535-be87-0fb20eb56576\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"modified\":1732413605677,\"created\":1732413603298,\"persisted\":1732413605677}}]"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
"localStorage": [
|
"localStorage": [
|
||||||
{
|
{
|
||||||
"name": "mct",
|
"name": "mct",
|
||||||
"value": "{\"mine\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"67ca2e0a-b37e-4eda-86a4-ccdbb228bbc0\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"modified\":1732413601720,\"created\":1732413600920,\"persisted\":1732413601720},\"67ca2e0a-b37e-4eda-86a4-ccdbb228bbc0\":{\"identifier\":{\"key\":\"67ca2e0a-b37e-4eda-86a4-ccdbb228bbc0\",\"namespace\":\"\"},\"name\":\"Overlay Plot with 5s Delay\",\"type\":\"telemetry.plot.overlay\",\"composition\":[{\"key\":\"8f524b49-ad06-47f9-98e0-087b31a2f3e0\",\"namespace\":\"\"}],\"configuration\":{\"series\":[{\"identifier\":{\"key\":\"8f524b49-ad06-47f9-98e0-087b31a2f3e0\",\"namespace\":\"\"}}]},\"notes\":\"framework/generateLocalStorageData.e2e.spec.js\\nGenerate Visual Test Data @localStorage @generatedata\\nGenerate Overlay Plot with 5s Delay\\nchrome\",\"modified\":1732413603020,\"location\":\"mine\",\"created\":1732413601720,\"persisted\":1732413603020},\"8f524b49-ad06-47f9-98e0-087b31a2f3e0\":{\"identifier\":{\"key\":\"8f524b49-ad06-47f9-98e0-087b31a2f3e0\",\"namespace\":\"\"},\"name\":\"VIPER Rover Heading\",\"type\":\"generator\",\"telemetry\":{\"period\":10,\"amplitude\":1,\"offset\":0,\"dataRateInHz\":1,\"phase\":0,\"randomness\":0,\"loadDelay\":5000,\"infinityValues\":false,\"exceedFloat32\":false,\"staleness\":false},\"modified\":1732413602920,\"location\":\"67ca2e0a-b37e-4eda-86a4-ccdbb228bbc0\",\"created\":1732413602420,\"persisted\":1732413602920}}"
|
"value": "{\"mine\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"f34f457e-d7f4-4fc4-ba71-52e19e925646\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"modified\":1732413605044,\"created\":1732413603140,\"persisted\":1732413605044},\"f34f457e-d7f4-4fc4-ba71-52e19e925646\":{\"identifier\":{\"key\":\"f34f457e-d7f4-4fc4-ba71-52e19e925646\",\"namespace\":\"\"},\"name\":\"Overlay Plot with 5s Delay\",\"type\":\"telemetry.plot.overlay\",\"composition\":[{\"key\":\"c568fa66-62e0-4eee-97eb-cdbc7421e556\",\"namespace\":\"\"}],\"configuration\":{\"series\":[{\"identifier\":{\"key\":\"c568fa66-62e0-4eee-97eb-cdbc7421e556\",\"namespace\":\"\"}}]},\"notes\":\"framework/generateLocalStorageData.e2e.spec.js\\nGenerate Visual Test Data @localStorage @generatedata @clock\\nGenerate Overlay Plot with 5s Delay\\nchrome\",\"modified\":1732413606208.9,\"location\":\"mine\",\"created\":1732413605044,\"persisted\":1732413606208.9},\"c568fa66-62e0-4eee-97eb-cdbc7421e556\":{\"identifier\":{\"key\":\"c568fa66-62e0-4eee-97eb-cdbc7421e556\",\"namespace\":\"\"},\"name\":\"VIPER Rover Heading\",\"type\":\"generator\",\"telemetry\":{\"period\":10,\"amplitude\":1,\"offset\":0,\"dataRateInHz\":1,\"phase\":0,\"randomness\":0,\"loadDelay\":5000,\"infinityValues\":false,\"exceedFloat32\":false,\"staleness\":false},\"modified\":1732413606049,\"location\":\"f34f457e-d7f4-4fc4-ba71-52e19e925646\",\"created\":1732413605554,\"persisted\":1732413606049}}"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mct-tree-expanded",
|
"name": "mct-tree-expanded",
|
||||||
|
File diff suppressed because one or more lines are too long
@ -19,19 +19,29 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createDomainObjectWithDefaults,
|
createDomainObjectWithDefaults,
|
||||||
|
createExampleTelemetryObject,
|
||||||
createNotification,
|
createNotification,
|
||||||
|
createPlanFromJSON,
|
||||||
expandEntireTree,
|
expandEntireTree,
|
||||||
openObjectTreeContextMenu,
|
getCanvasPixels,
|
||||||
|
navigateToObjectWithFixedTimeBounds,
|
||||||
|
navigateToObjectWithRealTime,
|
||||||
|
setEndOffset,
|
||||||
|
setFixedIndependentTimeConductorBounds,
|
||||||
setFixedTimeMode,
|
setFixedTimeMode,
|
||||||
setRealTimeMode,
|
setRealTimeMode,
|
||||||
setTimeConductorBounds
|
setStartOffset,
|
||||||
|
setTimeConductorBounds,
|
||||||
|
waitForPlotsToRender
|
||||||
} from '../../appActions.js';
|
} from '../../appActions.js';
|
||||||
|
import { assertPlanActivities, setBoundsToSpanAllActivities } from '../../helper/planningUtils.js';
|
||||||
import { expect, test } from '../../pluginFixtures.js';
|
import { expect, test } from '../../pluginFixtures.js';
|
||||||
|
|
||||||
test.describe('AppActions', () => {
|
test.describe('AppActions @framework', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
});
|
});
|
||||||
@ -94,6 +104,38 @@ test.describe('AppActions', () => {
|
|||||||
expect(folder3.url).toBe(`${e2eFolder.url}/${folder1.uuid}/${folder2.uuid}/${folder3.uuid}`);
|
expect(folder3.url).toBe(`${e2eFolder.url}/${folder1.uuid}/${folder2.uuid}/${folder3.uuid}`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
test('createExampleTelemetryObject', async ({ page }) => {
|
||||||
|
const gauge = await createDomainObjectWithDefaults(page, {
|
||||||
|
type: 'Gauge',
|
||||||
|
name: 'Gauge with no data'
|
||||||
|
});
|
||||||
|
|
||||||
|
const swgWithParent = await createExampleTelemetryObject(page, gauge.uuid);
|
||||||
|
|
||||||
|
await page.goto(swgWithParent.url);
|
||||||
|
await expect(page.locator('.l-browse-bar__object-name')).toHaveText(swgWithParent.name);
|
||||||
|
await page.getByLabel('More actions').click();
|
||||||
|
await page.getByLabel('Edit Properties...').click();
|
||||||
|
|
||||||
|
// Check Default values of created object
|
||||||
|
await expect(page.getByLabel('Title', { exact: true })).toHaveValue('VIPER Rover Heading');
|
||||||
|
await expect(page.getByRole('spinbutton', { name: 'Period' })).toHaveValue('10');
|
||||||
|
await expect(page.getByRole('spinbutton', { name: 'Amplitude' })).toHaveValue('1');
|
||||||
|
await expect(page.getByRole('spinbutton', { name: 'Offset' })).toHaveValue('0');
|
||||||
|
await expect(page.getByRole('spinbutton', { name: 'Data Rate (hz)' })).toHaveValue('1');
|
||||||
|
await expect(page.getByRole('spinbutton', { name: 'Phase (radians)' })).toHaveValue('0');
|
||||||
|
await expect(page.getByRole('spinbutton', { name: 'Randomness' })).toHaveValue('0');
|
||||||
|
await expect(page.getByRole('spinbutton', { name: 'Loading Delay (ms)' })).toHaveValue('0');
|
||||||
|
|
||||||
|
await page.getByLabel('Cancel').click();
|
||||||
|
|
||||||
|
const swgWithoutParent = await createExampleTelemetryObject(page);
|
||||||
|
|
||||||
|
await page.getByLabel('Show selected item in tree').click();
|
||||||
|
|
||||||
|
expect(swgWithParent.url).toBe(`${gauge.url}/${swgWithParent.uuid}`);
|
||||||
|
expect(swgWithoutParent.url).toBe(`./#/browse/mine/${swgWithoutParent.uuid}`);
|
||||||
|
});
|
||||||
test('createNotification', async ({ page }) => {
|
test('createNotification', async ({ page }) => {
|
||||||
await createNotification(page, {
|
await createNotification(page, {
|
||||||
message: 'Test info notification',
|
message: 'Test info notification',
|
||||||
@ -117,6 +159,19 @@ test.describe('AppActions', () => {
|
|||||||
await expect(page.locator('.c-message-banner')).toHaveClass(/error/);
|
await expect(page.locator('.c-message-banner')).toHaveClass(/error/);
|
||||||
await page.locator('[aria-label="Dismiss"]').click();
|
await page.locator('[aria-label="Dismiss"]').click();
|
||||||
});
|
});
|
||||||
|
test('createPlanFromJSON', async ({ page }) => {
|
||||||
|
const examplePlanSmall1 = JSON.parse(
|
||||||
|
fs.readFileSync(
|
||||||
|
new URL('../../test-data/examplePlans/ExamplePlan_Small1.json', import.meta.url)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const plan = await createPlanFromJSON(page, {
|
||||||
|
name: 'Test Plan',
|
||||||
|
json: examplePlanSmall1
|
||||||
|
});
|
||||||
|
await setBoundsToSpanAllActivities(page, examplePlanSmall1, plan.url);
|
||||||
|
await assertPlanActivities(page, examplePlanSmall1, plan.url);
|
||||||
|
});
|
||||||
test('expandEntireTree', async ({ page }) => {
|
test('expandEntireTree', async ({ page }) => {
|
||||||
const rootFolder = await createDomainObjectWithDefaults(page, {
|
const rootFolder = await createDomainObjectWithDefaults(page, {
|
||||||
type: 'Folder'
|
type: 'Folder'
|
||||||
@ -153,46 +208,135 @@ test.describe('AppActions', () => {
|
|||||||
name: 'Main Tree'
|
name: 'Main Tree'
|
||||||
});
|
});
|
||||||
const treePaneCollapsedItems = treePane.getByRole('treeitem', { expanded: false });
|
const treePaneCollapsedItems = treePane.getByRole('treeitem', { expanded: false });
|
||||||
expect(await treePaneCollapsedItems.count()).toBe(0);
|
await expect(treePaneCollapsedItems).toHaveCount(0);
|
||||||
|
|
||||||
await page.goto('./#/browse/mine');
|
await page.goto('./#/browse/mine');
|
||||||
//Click the Create button
|
//Click the Create button
|
||||||
await page.getByRole('button', { name: 'Create' }).click();
|
await page.getByRole('button', { name: 'Create' }).click();
|
||||||
|
|
||||||
// Click the object specified by 'type'
|
// Click the object specified by 'type'
|
||||||
await page.click(`li[role='menuitem']:text("Clock")`);
|
await page.getByRole('menuitem', { name: 'Clock' }).click();
|
||||||
await expandEntireTree(page, 'Create Modal Tree');
|
await expandEntireTree(page, 'Create Modal Tree');
|
||||||
const locatorTree = page.getByRole('tree', {
|
const locatorTree = page.getByRole('tree', {
|
||||||
name: 'Create Modal Tree'
|
name: 'Create Modal Tree'
|
||||||
});
|
});
|
||||||
const locatorTreeCollapsedItems = locatorTree.locator('role=treeitem[expanded=false]');
|
const locatorTreeCollapsedItems = locatorTree.locator('role=treeitem[expanded=false]');
|
||||||
expect(await locatorTreeCollapsedItems.count()).toBe(0);
|
await expect(locatorTreeCollapsedItems).toHaveCount(0);
|
||||||
});
|
});
|
||||||
test('openObjectTreeContextMenu', async ({ page }) => {
|
test('getCanvasPixels', async ({ page }) => {
|
||||||
const folder = await createDomainObjectWithDefaults(page, {
|
let overlayPlot = await createDomainObjectWithDefaults(page, {
|
||||||
type: 'Folder'
|
type: 'Overlay Plot'
|
||||||
});
|
});
|
||||||
await openObjectTreeContextMenu(page, folder.url);
|
|
||||||
await expect(page.getByLabel(`${folder.name} Context Menu`)).toBeVisible();
|
await createExampleTelemetryObject(page, overlayPlot.uuid);
|
||||||
|
|
||||||
|
await page.goto(overlayPlot.url);
|
||||||
|
//Get pixel data from Canvas
|
||||||
|
const plotPixels = await getCanvasPixels(page, 'canvas');
|
||||||
|
const plotPixelSize = plotPixels.length;
|
||||||
|
expect(plotPixelSize).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
test('setTimeConductorMode', async ({ page }) => {
|
test('navigateToObjectWithFixedTimeBounds', async ({ page }) => {
|
||||||
await setFixedTimeMode(page);
|
const exampleTelemetry = await createExampleTelemetryObject(page);
|
||||||
|
//Navigate without explicit bounds
|
||||||
|
await navigateToObjectWithFixedTimeBounds(page, exampleTelemetry.url);
|
||||||
await expect(page.getByLabel('Start bounds:')).toBeVisible();
|
await expect(page.getByLabel('Start bounds:')).toBeVisible();
|
||||||
await expect(page.getByLabel('End bounds:')).toBeVisible();
|
await expect(page.getByLabel('End bounds:')).toBeVisible();
|
||||||
await setRealTimeMode(page);
|
//Navigate with explicit bounds
|
||||||
await expect(page.getByLabel('Start offset')).toBeVisible();
|
await navigateToObjectWithFixedTimeBounds(
|
||||||
await expect(page.getByLabel('End offset')).toBeVisible();
|
page,
|
||||||
|
exampleTelemetry.url,
|
||||||
|
1693592063607,
|
||||||
|
1693593893607
|
||||||
|
);
|
||||||
|
await expect(page.getByLabel('Start bounds: 2023-09-01 18:')).toBeVisible();
|
||||||
|
await expect(page.getByLabel('End bounds: 2023-09-01 18:44:')).toBeVisible();
|
||||||
});
|
});
|
||||||
test('setTimeConductorBounds', async ({ page }) => {
|
test('navigateToObjectWithRealTime', async ({ page }) => {
|
||||||
// Assume in real-time mode by default
|
const exampleTelemetry = await createExampleTelemetryObject(page);
|
||||||
await setFixedTimeMode(page);
|
//Navigate without explicit bounds
|
||||||
await setTimeConductorBounds(page, {
|
await navigateToObjectWithRealTime(page, exampleTelemetry.url);
|
||||||
startDate: '2024-01-01',
|
await expect(page.getByLabel('Start offset:')).toBeVisible();
|
||||||
endDate: '2024-01-02',
|
await expect(page.getByLabel('End offset: 00:00:')).toBeVisible();
|
||||||
startTime: '00:00:00',
|
//Navigate with explicit bounds
|
||||||
endTime: '23:59:59'
|
await navigateToObjectWithRealTime(page, exampleTelemetry.url, 1693592063607, 1693593893607);
|
||||||
|
await expect(page.getByLabel('Start offset: 18:14:')).toBeVisible();
|
||||||
|
await expect(page.getByLabel('End offset: 18:44:')).toBeVisible();
|
||||||
|
});
|
||||||
|
test('setTimeConductorMode', async ({ page }) => {
|
||||||
|
await test.step('setFixedTimeMode', async () => {
|
||||||
|
await setFixedTimeMode(page);
|
||||||
|
await expect(page.getByLabel('Start bounds:')).toBeVisible();
|
||||||
|
await expect(page.getByLabel('End bounds:')).toBeVisible();
|
||||||
});
|
});
|
||||||
await expect(page.getByLabel('Start bounds: 2024-01-01 00:00:00')).toBeVisible();
|
await test.step('setTimeConductorBounds', async () => {
|
||||||
await expect(page.getByLabel('End bounds: 2024-01-02 23:59:59')).toBeVisible();
|
await setTimeConductorBounds(page, {
|
||||||
|
startDate: '2024-01-01',
|
||||||
|
endDate: '2024-01-02',
|
||||||
|
startTime: '00:00:00',
|
||||||
|
endTime: '23:59:59'
|
||||||
|
});
|
||||||
|
await expect(page.getByLabel('Start bounds: 2024-01-01 00:00:00')).toBeVisible();
|
||||||
|
await expect(page.getByLabel('End bounds: 2024-01-02 23:59:59')).toBeVisible();
|
||||||
|
});
|
||||||
|
await test.step('setRealTimeMode', async () => {
|
||||||
|
await setRealTimeMode(page);
|
||||||
|
await expect(page.getByLabel('Start offset')).toBeVisible();
|
||||||
|
await expect(page.getByLabel('End offset')).toBeVisible();
|
||||||
|
});
|
||||||
|
await test.step('setStartOffset', async () => {
|
||||||
|
await setStartOffset(page, {
|
||||||
|
startHours: '04',
|
||||||
|
startMins: '20',
|
||||||
|
startSecs: '22'
|
||||||
|
});
|
||||||
|
await expect(page.getByLabel('Start offset: 04:20:22')).toBeVisible();
|
||||||
|
});
|
||||||
|
await test.step('setEndOffset', async () => {
|
||||||
|
await setEndOffset(page, {
|
||||||
|
endHours: '04',
|
||||||
|
endMins: '20',
|
||||||
|
endSecs: '22'
|
||||||
|
});
|
||||||
|
await expect(page.getByLabel('End offset: 04:20:22')).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test('setFixedIndependentTimeConductorBounds', async ({ page }) => {
|
||||||
|
// Create a Display Layout
|
||||||
|
const displayLayout = await createDomainObjectWithDefaults(page, {
|
||||||
|
type: 'Display Layout'
|
||||||
|
});
|
||||||
|
await createDomainObjectWithDefaults(page, {
|
||||||
|
type: 'Example Imagery',
|
||||||
|
parent: displayLayout.uuid
|
||||||
|
});
|
||||||
|
|
||||||
|
const startDate = '2021-12-30 01:01:00.000Z';
|
||||||
|
const endDate = '2021-12-30 01:11:00.000Z';
|
||||||
|
await setFixedIndependentTimeConductorBounds(page, { start: startDate, end: endDate });
|
||||||
|
|
||||||
|
// check image date
|
||||||
|
await expect(page.getByText('2021-12-30 01:11:00.000Z').first()).toBeVisible();
|
||||||
|
|
||||||
|
// flip it off
|
||||||
|
await page.getByRole('switch').click();
|
||||||
|
// timestamp shouldn't be in the past anymore
|
||||||
|
await expect(page.getByText('2021-12-30 01:11:00.000Z')).toBeHidden();
|
||||||
|
});
|
||||||
|
test.fail('waitForPlotsToRender', async ({ page }) => {
|
||||||
|
// Create a SWG
|
||||||
|
await createDomainObjectWithDefaults(page, {
|
||||||
|
type: 'Sine Wave Generator'
|
||||||
|
});
|
||||||
|
// Edit the SWG
|
||||||
|
await page.getByLabel('More actions').click();
|
||||||
|
await page.getByLabel('Edit Properties...').click();
|
||||||
|
// Set loading delay to 10 seconds
|
||||||
|
await page.getByLabel('Loading Delay (ms)', { exact: true }).fill('10000');
|
||||||
|
await page.getByLabel('Save').click();
|
||||||
|
// Reload the page
|
||||||
|
await page.reload();
|
||||||
|
// Expect this step to fail
|
||||||
|
await waitForPlotsToRender(page, { timeout: 1000 });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -26,7 +26,7 @@ relates to how we've extended it (i.e. ./e2e/baseFixtures.js) and assumptions ma
|
|||||||
(`npm start` and ./e2e/webpack-dev-middleware.js)
|
(`npm start` and ./e2e/webpack-dev-middleware.js)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { expect, test } from '../../baseFixtures.js';
|
import { test } from '../../baseFixtures.js';
|
||||||
import { MISSION_TIME } from '../../constants.js';
|
import { MISSION_TIME } from '../../constants.js';
|
||||||
|
|
||||||
test.describe('baseFixtures tests', () => {
|
test.describe('baseFixtures tests', () => {
|
||||||
@ -42,6 +42,21 @@ test.describe('baseFixtures tests', () => {
|
|||||||
page.waitForEvent('console') // always wait for the event to happen while triggering it!
|
page.waitForEvent('console') // always wait for the event to happen while triggering it!
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
test('Verify that tests fail if console.error is thrown with clock override @clock', async ({
|
||||||
|
page
|
||||||
|
}) => {
|
||||||
|
test.fail();
|
||||||
|
//Set clock time
|
||||||
|
await page.clock.install({ time: MISSION_TIME });
|
||||||
|
await page.clock.resume();
|
||||||
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
|
|
||||||
|
//Verify that ../fixtures.js detects console log errors
|
||||||
|
await Promise.all([
|
||||||
|
page.evaluate(() => console.error('This should result in a failure')),
|
||||||
|
page.waitForEvent('console') // always wait for the event to happen while triggering it!
|
||||||
|
]);
|
||||||
|
});
|
||||||
test('Verify that tests pass if console.warn is thrown', async ({ page }) => {
|
test('Verify that tests pass if console.warn is thrown', async ({ page }) => {
|
||||||
//Go to baseURL
|
//Go to baseURL
|
||||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
@ -53,27 +68,3 @@ test.describe('baseFixtures tests', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe('baseFixtures tests @clock', () => {
|
|
||||||
test.use({
|
|
||||||
clockOptions: {
|
|
||||||
now: MISSION_TIME,
|
|
||||||
shouldAdvanceTime: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test.beforeEach(async ({ page }) => {
|
|
||||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Can use clockOptions and tick fixtures to control the clock', async ({ page, tick }) => {
|
|
||||||
let time = await page.evaluate(() => new Date().getTime());
|
|
||||||
expect(time).toBe(MISSION_TIME);
|
|
||||||
await tick(1000);
|
|
||||||
time = await page.evaluate(() => new Date().getTime());
|
|
||||||
expect(time).toBe(MISSION_TIME + 1000 * 1);
|
|
||||||
await tick(1000);
|
|
||||||
time = await page.evaluate(() => new Date().getTime());
|
|
||||||
expect(time).toBe(MISSION_TIME + 1000 * 2);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
@ -30,7 +30,6 @@
|
|||||||
* Demonstrated:
|
* Demonstrated:
|
||||||
* - Using appActions to leverage existing functions
|
* - Using appActions to leverage existing functions
|
||||||
* - Structure
|
* - Structure
|
||||||
* - @unstable annotation
|
|
||||||
* - await, expect, test, describe syntax
|
* - await, expect, test, describe syntax
|
||||||
* - Writing a custom function for a test suite
|
* - Writing a custom function for a test suite
|
||||||
* - Test stub for unfinished test coverage (test.fixme)
|
* - Test stub for unfinished test coverage (test.fixme)
|
||||||
@ -45,7 +44,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Structure: Some standard Imports. Please update the required pathing.
|
// Structure: Some standard Imports. Please update the required pathing.
|
||||||
import { createDomainObjectWithDefaults } from '../../appActions.js';
|
import { createDomainObjectWithDefaults, createExampleTelemetryObject } from '../../appActions.js';
|
||||||
import { expect, test } from '../../pluginFixtures.js';
|
import { expect, test } from '../../pluginFixtures.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,11 +52,8 @@ import { expect, test } from '../../pluginFixtures.js';
|
|||||||
* Try to keep a single describe block per logical groups of tests.
|
* Try to keep a single describe block per logical groups of tests.
|
||||||
* If your test runtime exceeds 5 minutes or 500 lines, it's likely that it will need to be split.
|
* If your test runtime exceeds 5 minutes or 500 lines, it's likely that it will need to be split.
|
||||||
*
|
*
|
||||||
* Annotations:
|
|
||||||
* Please use the @unstable tag at the end of the test title so that our automation can pick it up
|
|
||||||
* as a part of our test promotion pipeline.
|
|
||||||
*/
|
*/
|
||||||
test.describe('Renaming Timer Object', () => {
|
test.describe('Example - Renaming Timer Object', () => {
|
||||||
// Top-level declaration of the Timer object created in beforeEach().
|
// Top-level declaration of the Timer object created in beforeEach().
|
||||||
// We can then use this throughout the entire test suite.
|
// We can then use this throughout the entire test suite.
|
||||||
let timer;
|
let timer;
|
||||||
@ -70,7 +66,7 @@ test.describe('Renaming Timer Object', () => {
|
|||||||
timer = await createDomainObjectWithDefaults(page, { type: 'Timer' });
|
timer = await createDomainObjectWithDefaults(page, { type: 'Timer' });
|
||||||
|
|
||||||
// Assert the object to be created and check its name in the title
|
// Assert the object to be created and check its name in the title
|
||||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText(timer.name);
|
await expect(page.getByRole('main')).toContainText(timer.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,7 +81,7 @@ test.describe('Renaming Timer Object', () => {
|
|||||||
await renameTimerFrom3DotMenu(page, timer.url, newObjectName);
|
await renameTimerFrom3DotMenu(page, timer.url, newObjectName);
|
||||||
|
|
||||||
// Assert that the name has changed in the browser bar to the value we assigned above
|
// Assert that the name has changed in the browser bar to the value we assigned above
|
||||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText(newObjectName);
|
await expect(page.getByRole('main')).toContainText(newObjectName);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('An existing Timer object can be renamed twice', async ({ page }) => {
|
test('An existing Timer object can be renamed twice', async ({ page }) => {
|
||||||
@ -95,13 +91,13 @@ test.describe('Renaming Timer Object', () => {
|
|||||||
await renameTimerFrom3DotMenu(page, timer.url, newObjectName);
|
await renameTimerFrom3DotMenu(page, timer.url, newObjectName);
|
||||||
|
|
||||||
// Assert that the name has changed in the browser bar to the value we assigned above
|
// Assert that the name has changed in the browser bar to the value we assigned above
|
||||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText(newObjectName);
|
await expect(page.getByRole('main')).toContainText(newObjectName);
|
||||||
|
|
||||||
// Rename the Timer object again
|
// Rename the Timer object again
|
||||||
await renameTimerFrom3DotMenu(page, timer.url, newObjectName2);
|
await renameTimerFrom3DotMenu(page, timer.url, newObjectName2);
|
||||||
|
|
||||||
// Assert that the name has changed in the browser bar to the second value
|
// Assert that the name has changed in the browser bar to the second value
|
||||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText(newObjectName2);
|
await expect(page.getByRole('main')).toContainText(newObjectName2);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -121,7 +117,7 @@ test.describe('Renaming Timer Object', () => {
|
|||||||
* The next most important concept in our testing is working with telemetry objects. Telemetry is at the core of Open MCT
|
* The next most important concept in our testing is working with telemetry objects. Telemetry is at the core of Open MCT
|
||||||
* and we have developed a great pattern for working with it.
|
* and we have developed a great pattern for working with it.
|
||||||
*/
|
*/
|
||||||
test.describe('Advanced: Working with telemetry objects', () => {
|
test.describe('Advanced Example - Working with telemetry objects', () => {
|
||||||
let displayLayout;
|
let displayLayout;
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
@ -132,17 +128,14 @@ test.describe('Advanced: Working with telemetry objects', () => {
|
|||||||
name: 'Display Layout with Embedded SWG'
|
name: 'Display Layout with Embedded SWG'
|
||||||
});
|
});
|
||||||
// Create Telemetry object within the parent object created above
|
// Create Telemetry object within the parent object created above
|
||||||
await createDomainObjectWithDefaults(page, {
|
//reference the display layout in the creation process
|
||||||
type: 'Sine Wave Generator',
|
await createExampleTelemetryObject(page, displayLayout.uuid);
|
||||||
name: 'Telemetry',
|
|
||||||
parent: displayLayout.uuid //reference the display layout in the creation process
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
test('Can directly navigate to a Display Layout with embedded telemetry', async ({ page }) => {
|
test('Can directly navigate to a Display Layout with embedded telemetry', async ({ page }) => {
|
||||||
//Now you can directly navigate to the displayLayout created in the beforeEach with the embedded telemetry
|
//Now you can directly navigate to the displayLayout created in the beforeEach with the embedded telemetry
|
||||||
await page.goto(displayLayout.url);
|
await page.goto(displayLayout.url);
|
||||||
//Expect the created Telemetry Object to be visible when directly navigating to the displayLayout
|
//Expect the created Telemetry Object to be visible when directly navigating to the displayLayout
|
||||||
await expect(page.getByTitle('Sine')).toBeVisible();
|
await expect(page.getByLabel('Alpha-numeric telemetry name')).toBeVisible();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -160,18 +153,14 @@ test.describe('Advanced: Working with telemetry objects', () => {
|
|||||||
* @param {string} newNameForTimer New name for object
|
* @param {string} newNameForTimer New name for object
|
||||||
*/
|
*/
|
||||||
async function renameTimerFrom3DotMenu(page, timerUrl, newNameForTimer) {
|
async function renameTimerFrom3DotMenu(page, timerUrl, newNameForTimer) {
|
||||||
// Navigate to the timer object
|
// Navigate to the timer object directly
|
||||||
await page.goto(timerUrl);
|
await page.goto(timerUrl);
|
||||||
|
|
||||||
// Click on 3 Dot Menu
|
await page.getByLabel('More actions').click();
|
||||||
await page.locator('button[title="More actions"]').click();
|
await page.getByLabel('Edit Properties...').click();
|
||||||
|
|
||||||
// Click text=Edit Properties...
|
|
||||||
await page.locator('text=Edit Properties...').click();
|
|
||||||
|
|
||||||
// Rename the timer object
|
// Rename the timer object
|
||||||
await page.locator('text=Properties Title Notes >> input[type="text"]').fill(newNameForTimer);
|
await page.getByLabel('Title', { exact: true }).fill(newNameForTimer);
|
||||||
|
|
||||||
// Click Ok button to Save
|
await page.getByLabel('Save').click();
|
||||||
await page.locator('button:has-text("OK")').click();
|
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ import { fileURLToPath } from 'url';
|
|||||||
import {
|
import {
|
||||||
createDomainObjectWithDefaults,
|
createDomainObjectWithDefaults,
|
||||||
createExampleTelemetryObject,
|
createExampleTelemetryObject,
|
||||||
setIndependentTimeConductorBounds,
|
setFixedIndependentTimeConductorBounds,
|
||||||
setTimeConductorBounds
|
setTimeConductorBounds
|
||||||
} from '../../appActions.js';
|
} from '../../appActions.js';
|
||||||
import { MISSION_TIME } from '../../constants.js';
|
import { MISSION_TIME } from '../../constants.js';
|
||||||
@ -45,14 +45,10 @@ import { expect, test } from '../../pluginFixtures.js';
|
|||||||
const overlayPlotName = 'Overlay Plot with Telemetry Object';
|
const overlayPlotName = 'Overlay Plot with Telemetry Object';
|
||||||
|
|
||||||
test.describe('Generate Visual Test Data @localStorage @generatedata @clock', () => {
|
test.describe('Generate Visual Test Data @localStorage @generatedata @clock', () => {
|
||||||
test.use({
|
|
||||||
clockOptions: {
|
|
||||||
now: MISSION_TIME,
|
|
||||||
shouldAdvanceTime: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
|
// Override the clock
|
||||||
|
await page.clock.install({ time: MISSION_TIME });
|
||||||
|
await page.clock.resume();
|
||||||
// Go to baseURL
|
// Go to baseURL
|
||||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
});
|
});
|
||||||
@ -112,7 +108,7 @@ test.describe('Generate Visual Test Data @localStorage @generatedata @clock', ()
|
|||||||
|
|
||||||
await page.goto(parent.url, { waitUntil: 'domcontentloaded' });
|
await page.goto(parent.url, { waitUntil: 'domcontentloaded' });
|
||||||
|
|
||||||
await setIndependentTimeConductorBounds(page, {
|
await setFixedIndependentTimeConductorBounds(page, {
|
||||||
start: '2024-11-12 19:11:11.000Z',
|
start: '2024-11-12 19:11:11.000Z',
|
||||||
end: '2024-11-12 20:11:11.000Z'
|
end: '2024-11-12 20:11:11.000Z'
|
||||||
});
|
});
|
||||||
@ -211,11 +207,7 @@ test.describe('Generate Visual Test Data @localStorage @generatedata @clock', ()
|
|||||||
// TODO: Flesh Out Assertions against created Objects
|
// TODO: Flesh Out Assertions against created Objects
|
||||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText(overlayPlotName);
|
await expect(page.locator('.l-browse-bar__object-name')).toContainText(overlayPlotName);
|
||||||
await page.getByRole('tab', { name: 'Config' }).click();
|
await page.getByRole('tab', { name: 'Config' }).click();
|
||||||
await page
|
await page.getByLabel('Plot Series Items').getByLabel('Expand').click();
|
||||||
.getByRole('list', { name: 'Plot Series Properties' })
|
|
||||||
.locator('span')
|
|
||||||
.first()
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// TODO: Modify the Overlay Plot to use fixed Scaling
|
// TODO: Modify the Overlay Plot to use fixed Scaling
|
||||||
// TODO: Verify Autoscaling.
|
// TODO: Verify Autoscaling.
|
||||||
@ -274,7 +266,7 @@ test.describe('Generate Visual Test Data @localStorage @generatedata @clock', ()
|
|||||||
page.waitForNavigation(),
|
page.waitForNavigation(),
|
||||||
page.locator('text=OK').click(),
|
page.locator('text=OK').click(),
|
||||||
//Wait for Save Banner to appear
|
//Wait for Save Banner to appear
|
||||||
page.waitForSelector('.c-message-banner__message')
|
page.locator('.c-message-banner__message').hover({ trial: true })
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// focus the overlay plot
|
// focus the overlay plot
|
||||||
@ -284,7 +276,7 @@ test.describe('Generate Visual Test Data @localStorage @generatedata @clock', ()
|
|||||||
|
|
||||||
// Clear Recently Viewed
|
// Clear Recently Viewed
|
||||||
await page.getByRole('button', { name: 'Clear Recently Viewed' }).click();
|
await page.getByRole('button', { name: 'Clear Recently Viewed' }).click();
|
||||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
await page.getByRole('button', { name: 'Ok', exact: true }).click();
|
||||||
//Save localStorage for future test execution
|
//Save localStorage for future test execution
|
||||||
await context.storageState({
|
await context.storageState({
|
||||||
path: fileURLToPath(
|
path: fileURLToPath(
|
||||||
@ -307,11 +299,7 @@ test.describe('Validate Overlay Plot with Telemetry Object @localStorage @genera
|
|||||||
// TODO: Flesh Out Assertions against created Objects
|
// TODO: Flesh Out Assertions against created Objects
|
||||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText(overlayPlotName);
|
await expect(page.locator('.l-browse-bar__object-name')).toContainText(overlayPlotName);
|
||||||
await page.getByRole('tab', { name: 'Config' }).click();
|
await page.getByRole('tab', { name: 'Config' }).click();
|
||||||
await page
|
await page.getByLabel('Plot Series Items').getByLabel('Expand').click();
|
||||||
.getByRole('list', { name: 'Plot Series Properties' })
|
|
||||||
.locator('span')
|
|
||||||
.first()
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// TODO: Modify the Overlay Plot to use fixed Scaling
|
// TODO: Modify the Overlay Plot to use fixed Scaling
|
||||||
// TODO: Verify Autoscaling.
|
// TODO: Verify Autoscaling.
|
||||||
@ -352,11 +340,7 @@ test.describe('Validate Overlay Plot with 5s Delay Telemetry Object @localStorag
|
|||||||
// TODO: Flesh Out Assertions against created Objects
|
// TODO: Flesh Out Assertions against created Objects
|
||||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText(plotName);
|
await expect(page.locator('.l-browse-bar__object-name')).toContainText(plotName);
|
||||||
await page.getByRole('tab', { name: 'Config' }).click();
|
await page.getByRole('tab', { name: 'Config' }).click();
|
||||||
await page
|
await page.getByLabel('Plot Series Items').getByLabel('Expand').click();
|
||||||
.getByRole('list', { name: 'Plot Series Properties' })
|
|
||||||
.locator('span')
|
|
||||||
.first()
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// TODO: Modify the Overlay Plot to use fixed Scaling
|
// TODO: Modify the Overlay Plot to use fixed Scaling
|
||||||
// TODO: Verify Autoscaling.
|
// TODO: Verify Autoscaling.
|
||||||
|
@ -31,7 +31,7 @@ import { test } from '../../pluginFixtures.js';
|
|||||||
test.describe.skip('pluginFixtures tests', () => {
|
test.describe.skip('pluginFixtures tests', () => {
|
||||||
// test.use({ domainObjectName: 'Timer' });
|
// test.use({ domainObjectName: 'Timer' });
|
||||||
// let timerUUID;
|
// let timerUUID;
|
||||||
// test('Creates a timer object @framework @unstable', ({ domainObject }) => {
|
// test('Creates a timer object @framework', ({ domainObject }) => {
|
||||||
// const { uuid } = domainObject;
|
// const { uuid } = domainObject;
|
||||||
// const uuidRegexp = /[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/;
|
// const uuidRegexp = /[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/;
|
||||||
// expect(uuid).toMatch(uuidRegexp);
|
// expect(uuid).toMatch(uuidRegexp);
|
||||||
|
@ -59,6 +59,6 @@ test.describe('Clear Data Action', () => {
|
|||||||
|
|
||||||
// Verify that the background image is no longer visible
|
// Verify that the background image is no longer visible
|
||||||
await expect(page.locator(backgroundImageSelector)).toBeHidden();
|
await expect(page.locator(backgroundImageSelector)).toBeHidden();
|
||||||
expect(await page.locator('.c-thumb__image').count()).toBe(0);
|
await expect(page.locator('.c-thumb__image')).toHaveCount(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -41,7 +41,7 @@ test.describe('CouchDB Status Indicator with mocked responses @couchdb', () => {
|
|||||||
|
|
||||||
//Go to baseURL
|
//Go to baseURL
|
||||||
await page.goto('./#/browse/mine?hideTree=true&hideInspector=true', {
|
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();
|
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
|
//Go to baseURL
|
||||||
await page.goto('./#/browse/mine?hideTree=true&hideInspector=true', {
|
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();
|
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
|
//Go to baseURL
|
||||||
await page.goto('./#/browse/mine?hideTree=true&hideInspector=true', {
|
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();
|
await expect(page.locator('div:has-text("CouchDB connectivity unknown")').nth(3)).toBeVisible();
|
||||||
});
|
});
|
||||||
|
@ -41,7 +41,7 @@ test.describe('Sine Wave Generator', () => {
|
|||||||
await page.getByRole('button', { name: 'Create' }).click();
|
await page.getByRole('button', { name: 'Create' }).click();
|
||||||
|
|
||||||
// Click Sine Wave Generator
|
// Click Sine Wave Generator
|
||||||
await page.click('text=Sine Wave Generator');
|
await page.getByRole('menuitem', { name: 'Sine Wave Generator' }).click();
|
||||||
|
|
||||||
// Verify that the each required field has required indicator
|
// Verify that the each required field has required indicator
|
||||||
// Title
|
// Title
|
||||||
@ -107,11 +107,11 @@ test.describe('Sine Wave Generator', () => {
|
|||||||
await page.locator('.field.control.l-input-sm input').first().press('ArrowUp');
|
await page.locator('.field.control.l-input-sm input').first().press('ArrowUp');
|
||||||
await page.locator('.field.control.l-input-sm input').first().press('ArrowUp');
|
await page.locator('.field.control.l-input-sm input').first().press('ArrowUp');
|
||||||
|
|
||||||
const value = await page.locator('.field.control.l-input-sm input').first().inputValue();
|
const value = page.locator('.field.control.l-input-sm input').first();
|
||||||
await expect(value).toBe('6');
|
await expect(value).toHaveValue('6');
|
||||||
|
|
||||||
//Click text=OK
|
//Click save button
|
||||||
await Promise.all([page.waitForNavigation(), page.click('button:has-text("OK")')]);
|
await page.getByLabel('Save').click();
|
||||||
|
|
||||||
// Verify that the Sine Wave Generator is displayed and correct
|
// Verify that the Sine Wave Generator is displayed and correct
|
||||||
// Verify object properties
|
// Verify object properties
|
||||||
|
@ -45,25 +45,23 @@ test.describe('Form Validation Behavior', () => {
|
|||||||
await page.getByRole('menuitem', { name: 'Folder' }).click();
|
await page.getByRole('menuitem', { name: 'Folder' }).click();
|
||||||
|
|
||||||
// Fill in empty string into title and trigger validation with 'Tab'
|
// Fill in empty string into title and trigger validation with 'Tab'
|
||||||
await page.click('text=Properties Title Notes >> input[type="text"]');
|
await page.getByLabel('Title', { exact: true }).fill('');
|
||||||
await page.fill('text=Properties Title Notes >> input[type="text"]', '');
|
await page.getByLabel('Title', { exact: true }).press('Tab');
|
||||||
await page.press('text=Properties Title Notes >> input[type="text"]', 'Tab');
|
|
||||||
|
|
||||||
//Required Field Form Validation
|
//Required Field Form Validation
|
||||||
await expect(page.locator('button:has-text("OK")')).toBeDisabled();
|
await expect(page.getByLabel('Save')).toBeDisabled();
|
||||||
await expect(page.locator('.c-form-row__state-indicator').first()).toHaveClass(/invalid/);
|
await expect(page.locator('.c-form-row__state-indicator').first()).toHaveClass(/invalid/);
|
||||||
|
|
||||||
//Correct Form Validation for missing title and trigger validation with 'Tab'
|
//Correct Form Validation for missing title and trigger validation with 'Tab'
|
||||||
await page.click('text=Properties Title Notes >> input[type="text"]');
|
await page.getByLabel('Title', { exact: true }).fill(TEST_FOLDER);
|
||||||
await page.fill('text=Properties Title Notes >> input[type="text"]', TEST_FOLDER);
|
await page.getByLabel('Title', { exact: true }).press('Tab');
|
||||||
await page.press('text=Properties Title Notes >> input[type="text"]', 'Tab');
|
|
||||||
|
|
||||||
//Required Field Form Validation is corrected
|
//Required Field Form Validation is corrected
|
||||||
await expect(page.locator('button:has-text("OK")')).toBeEnabled();
|
await expect(page.getByLabel('Save')).toBeEnabled();
|
||||||
await expect(page.locator('.c-form-row__state-indicator').first()).not.toHaveClass(/invalid/);
|
await expect(page.locator('.c-form-row__state-indicator').first()).not.toHaveClass(/invalid/);
|
||||||
|
|
||||||
//Finish Creating Domain Object
|
//Finish Creating Domain Object
|
||||||
await Promise.all([page.waitForNavigation(), page.click('button:has-text("OK")')]);
|
await page.getByLabel('Save').click();
|
||||||
|
|
||||||
//Verify that the Domain Object has been created with the corrected title property
|
//Verify that the Domain Object has been created with the corrected title property
|
||||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText(TEST_FOLDER);
|
await expect(page.locator('.l-browse-bar__object-name')).toContainText(TEST_FOLDER);
|
||||||
@ -87,8 +85,8 @@ test.describe('Form File Input Behavior', () => {
|
|||||||
|
|
||||||
await page.getByRole('button', { name: 'Save' }).click();
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
|
|
||||||
const type = await page.locator('#file-input-type').textContent();
|
const type = page.locator('#file-input-type');
|
||||||
await expect(type).toBe(`"string"`);
|
await expect(type).toHaveText(`"string"`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Can select an image file type', async ({ page }) => {
|
test('Can select an image file type', async ({ page }) => {
|
||||||
@ -101,8 +99,8 @@ test.describe('Form File Input Behavior', () => {
|
|||||||
|
|
||||||
await page.getByRole('button', { name: 'Save' }).click();
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
|
|
||||||
const type = await page.locator('#file-input-type').textContent();
|
const type = page.locator('#file-input-type');
|
||||||
await expect(type).toBe(`"object"`);
|
await expect(type).toHaveText(`"object"`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -123,11 +121,11 @@ test.describe('Persistence operations @addInit', () => {
|
|||||||
|
|
||||||
await page.getByRole('button', { name: 'Create' }).click();
|
await page.getByRole('button', { name: 'Create' }).click();
|
||||||
|
|
||||||
await page.click('text=Condition Set');
|
await page.getByRole('menuitem', { name: 'Condition Set' }).click();
|
||||||
|
|
||||||
await page.locator('form[name="mctForm"] >> text=Persistence Testing').click();
|
await page.locator('form[name="mctForm"] >> text=Persistence Testing').click();
|
||||||
|
|
||||||
const okButton = page.locator('button:has-text("OK")');
|
const okButton = page.getByLabel('Save');
|
||||||
await expect(okButton).toBeDisabled();
|
await expect(okButton).toBeDisabled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -158,14 +156,12 @@ test.describe('Persistence operations @couchdb', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Open the edit form for the clock object
|
// Open the edit form for the clock object
|
||||||
await page.click('button[title="More actions"]');
|
await page.getByLabel('More actions').click();
|
||||||
await page.click('li[title="Edit properties of this object."]');
|
await page.getByLabel('Edit Properties...').click();
|
||||||
|
|
||||||
// Modify the display format from default 12hr -> 24hr and click 'Save'
|
// Modify the display format from default 12hr -> 24hr and click 'Save'
|
||||||
await page
|
await page.getByLabel('12 or 24 hour clock').selectOption({ value: 'clock24' });
|
||||||
.locator('select[aria-label="12 or 24 hour clock"]')
|
await page.getByLabel('Save').click();
|
||||||
.selectOption({ value: 'clock24' });
|
|
||||||
await page.click('button[aria-label="Save"]');
|
|
||||||
|
|
||||||
await expect
|
await expect
|
||||||
.poll(() => putRequestCount, {
|
.poll(() => putRequestCount, {
|
||||||
@ -188,8 +184,8 @@ test.describe('Persistence operations @couchdb', () => {
|
|||||||
|
|
||||||
// Both pages: Go to baseURL
|
// Both pages: Go to baseURL
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
page.goto('./', { waitUntil: 'networkidle' }),
|
page.goto('./', { waitUntil: 'domcontentloaded' }),
|
||||||
page2.goto('./', { waitUntil: 'networkidle' })
|
page2.goto('./', { waitUntil: 'domcontentloaded' })
|
||||||
]);
|
]);
|
||||||
|
|
||||||
//Slow down the test a bit
|
//Slow down the test a bit
|
||||||
@ -202,14 +198,14 @@ test.describe('Persistence operations @couchdb', () => {
|
|||||||
|
|
||||||
// Both pages: Click the Create button
|
// Both pages: Click the Create button
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
page.click('button:has-text("Create")'),
|
page.getByRole('button', { name: 'Create' }).click(),
|
||||||
page2.click('button:has-text("Create")')
|
page2.getByRole('button', { name: 'Create' }).click()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Both pages: Click "Clock" in the Create menu
|
// Both pages: Click "Clock" in the Create menu
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
page.click(`li[role='menuitem']:text("Clock")`),
|
page.getByRole('menuitem', { name: 'Clock' }).click(),
|
||||||
page2.click(`li[role='menuitem']:text("Clock")`)
|
page2.getByRole('menuitem', { name: 'Clock' }).click()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Generate unique names for both objects
|
// Generate unique names for both objects
|
||||||
@ -236,9 +232,9 @@ test.describe('Persistence operations @couchdb', () => {
|
|||||||
// conditions for a conflict error from the first page.
|
// conditions for a conflict error from the first page.
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
page2.waitForLoadState(),
|
page2.waitForLoadState(),
|
||||||
page2.click('[aria-label="Save"]'),
|
page2.getByLabel('Save').click(),
|
||||||
// Wait for Save Banner to appear
|
// Wait for Save Banner to appear
|
||||||
page2.waitForSelector('.c-message-banner__message')
|
page2.locator('.c-message-banner__message').hover({ trial: true })
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Close Page 2, we're done with it.
|
// Close Page 2, we're done with it.
|
||||||
@ -249,9 +245,9 @@ test.describe('Persistence operations @couchdb', () => {
|
|||||||
// the composition of the parent folder.
|
// the composition of the parent folder.
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
page.waitForLoadState(),
|
page.waitForLoadState(),
|
||||||
page.click('[aria-label="Save"]'),
|
page.getByLabel('Save').click(),
|
||||||
// Wait for Save Banner to appear
|
// Wait for Save Banner to appear
|
||||||
page.waitForSelector('.c-message-banner__message')
|
page.locator('.c-message-banner__message').hover({ trial: true })
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Page 1: Verify that the conflict has occurred and an error notification is displayed.
|
// Page 1: Verify that the conflict has occurred and an error notification is displayed.
|
||||||
|
@ -50,13 +50,13 @@ test.describe('Notifications List', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Verify that there is a button with aria-label "Review 2 Notifications"
|
// Verify that there is a button with aria-label "Review 2 Notifications"
|
||||||
expect(await page.locator('button[aria-label="Review 2 Notifications"]').count()).toBe(1);
|
await expect(page.locator('button[aria-label="Review 2 Notifications"]')).toHaveCount(1);
|
||||||
|
|
||||||
// Click on button with aria-label "Review 2 Notifications"
|
// Click on button with aria-label "Review 2 Notifications"
|
||||||
await page.click('button[aria-label="Review 2 Notifications"]');
|
await page.getByLabel('Review 2 Notifications').click();
|
||||||
|
|
||||||
// Click on button with aria-label="Dismiss notification of Error message"
|
// Click on button with aria-label="Dismiss notification of Error message"
|
||||||
await page.click('button[aria-label="Dismiss notification of Error message"]');
|
await page.getByLabel('Dismiss notification of Error message').click();
|
||||||
|
|
||||||
// Verify there is no a notification (listitem) with the text "Error message" since it was dismissed
|
// Verify there is no a notification (listitem) with the text "Error message" since it was dismissed
|
||||||
expect(await page.locator('div[role="dialog"] div[role="listitem"]').innerText()).not.toContain(
|
expect(await page.locator('div[role="dialog"] div[role="listitem"]').innerText()).not.toContain(
|
||||||
@ -69,10 +69,10 @@ test.describe('Notifications List', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Click on button with aria-label="Dismiss notification of Alert message"
|
// Click on button with aria-label="Dismiss notification of Alert message"
|
||||||
await page.click('button[aria-label="Dismiss notification of Alert message"]');
|
await page.getByLabel('Dismiss notification of Alert message').click();
|
||||||
|
|
||||||
// Verify that there is no dialog since the notification overlay was closed automatically after all notifications were dismissed
|
// Verify that there is no dialog since the notification overlay was closed automatically after all notifications were dismissed
|
||||||
expect(await page.locator('div[role="dialog"]').count()).toBe(0);
|
await expect(page.locator('div[role="dialog"]')).toHaveCount(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
|
||||||
import { createPlanFromJSON } from '../../../appActions.js';
|
import { createPlanFromJSON, navigateToObjectWithFixedTimeBounds } from '../../../appActions.js';
|
||||||
import {
|
import {
|
||||||
addPlanGetInterceptor,
|
addPlanGetInterceptor,
|
||||||
assertPlanActivities,
|
assertPlanActivities,
|
||||||
@ -81,9 +81,7 @@ test.describe('Plan', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Switch to fixed time mode with all plan events within the bounds
|
// Switch to fixed time mode with all plan events within the bounds
|
||||||
await page.goto(
|
await navigateToObjectWithFixedTimeBounds(page, plan.url, startBound, endBound);
|
||||||
`${plan.url}?tc.mode=fixed&tc.startBound=${startBound}&tc.endBound=${endBound}&tc.timeSystem=utc&view=plan.view`
|
|
||||||
);
|
|
||||||
|
|
||||||
// select the first activity in the list
|
// select the first activity in the list
|
||||||
await page.getByText('Past event 1').click();
|
await page.getByText('Past event 1').click();
|
||||||
|
@ -21,7 +21,11 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
|
||||||
import { createDomainObjectWithDefaults, createPlanFromJSON } from '../../../appActions.js';
|
import {
|
||||||
|
createDomainObjectWithDefaults,
|
||||||
|
createPlanFromJSON,
|
||||||
|
navigateToObjectWithFixedTimeBounds
|
||||||
|
} from '../../../appActions.js';
|
||||||
import { expect, test } from '../../../pluginFixtures.js';
|
import { expect, test } from '../../../pluginFixtures.js';
|
||||||
|
|
||||||
const examplePlanSmall1 = JSON.parse(
|
const examplePlanSmall1 = JSON.parse(
|
||||||
@ -59,14 +63,12 @@ test.describe('Time List', () => {
|
|||||||
const endBound = lastActivity.end;
|
const endBound = lastActivity.end;
|
||||||
|
|
||||||
// Switch to fixed time mode with all plan events within the bounds
|
// Switch to fixed time mode with all plan events within the bounds
|
||||||
await page.goto(
|
await navigateToObjectWithFixedTimeBounds(page, timelist.url, startBound, endBound);
|
||||||
`${timelist.url}?tc.mode=fixed&tc.startBound=${startBound}&tc.endBound=${endBound}&tc.timeSystem=utc&view=timelist.view`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Verify all events are displayed
|
// Verify all events are displayed
|
||||||
const eventCount = await page.getByRole('row').count();
|
const eventCount = await page.getByRole('row').count();
|
||||||
// subtracting one for the header
|
// subtracting one for the header
|
||||||
await expect(eventCount - 1).toEqual(firstGroupItems.length);
|
expect(eventCount - 1).toEqual(firstGroupItems.length);
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('Does not show milliseconds in times', async () => {
|
await test.step('Does not show milliseconds in times', async () => {
|
||||||
@ -124,9 +126,7 @@ test("View a timelist in expanded view, verify all the activities are displayed
|
|||||||
const endBound = lastActivity.end;
|
const endBound = lastActivity.end;
|
||||||
|
|
||||||
// Switch to fixed time mode with all plan events within the bounds
|
// Switch to fixed time mode with all plan events within the bounds
|
||||||
await page.goto(
|
await navigateToObjectWithFixedTimeBounds(page, timelist.url, startBound, endBound);
|
||||||
`${timelist.url}?tc.mode=fixed&tc.startBound=${startBound}&tc.endBound=${endBound}&tc.timeSystem=utc&view=timelist.view`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Change the object to edit mode
|
// Change the object to edit mode
|
||||||
await page.getByRole('button', { name: 'Edit Object' }).click();
|
await page.getByRole('button', { name: 'Edit Object' }).click();
|
||||||
@ -142,7 +142,7 @@ test("View a timelist in expanded view, verify all the activities are displayed
|
|||||||
|
|
||||||
// Verify all events are displayed
|
// Verify all events are displayed
|
||||||
const eventCount = await page.getByRole('row').count();
|
const eventCount = await page.getByRole('row').count();
|
||||||
await expect(eventCount).toEqual(firstGroupItems.length);
|
expect(eventCount).toEqual(firstGroupItems.length);
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('Shows activity properties when a row is selected in the expanded view', async () => {
|
await test.step('Shows activity properties when a row is selected in the expanded view', async () => {
|
||||||
@ -158,7 +158,7 @@ test("View a timelist in expanded view, verify all the activities are displayed
|
|||||||
|
|
||||||
await test.step("Verify absence of progress indication for an activity that's not in progress", async () => {
|
await test.step("Verify absence of progress indication for an activity that's not in progress", async () => {
|
||||||
// When an activity is not in progress, the progress pie is not visible
|
// When an activity is not in progress, the progress pie is not visible
|
||||||
const hidden = await page.getByRole('row').locator('path').nth(1).isHidden();
|
const hidden = page.getByRole('row').locator('path').nth(1);
|
||||||
await expect(hidden).toBe(true);
|
await expect(hidden).toBeHidden();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -22,12 +22,16 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
Collection of Time List tests set to run with browser clock manipulate made possible with the
|
Collection of Time List tests set to run with browser clock manipulate made possible with the
|
||||||
clockOptions plugin fixture.
|
page.clock() API.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
|
||||||
import { createDomainObjectWithDefaults, createPlanFromJSON } from '../../../appActions.js';
|
import {
|
||||||
|
createDomainObjectWithDefaults,
|
||||||
|
createPlanFromJSON,
|
||||||
|
navigateToObjectWithRealTime
|
||||||
|
} from '../../../appActions.js';
|
||||||
import {
|
import {
|
||||||
createTimelistWithPlanAndSetActivityInProgress,
|
createTimelistWithPlanAndSetActivityInProgress,
|
||||||
getEarliestStartTime,
|
getEarliestStartTime,
|
||||||
@ -93,14 +97,12 @@ const COUNTDOWN = Object.freeze({
|
|||||||
SECONDS: 5
|
SECONDS: 5
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const FIRST_ACTIVITY_SMALL_1 = getFirstActivity(examplePlanSmall1);
|
||||||
|
|
||||||
test.describe('Time List with controlled clock @clock', () => {
|
test.describe('Time List with controlled clock @clock', () => {
|
||||||
test.use({
|
|
||||||
clockOptions: {
|
|
||||||
now: getEarliestStartTime(examplePlanSmall3),
|
|
||||||
shouldAdvanceTime: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await page.clock.install({ time: getEarliestStartTime(examplePlanSmall3) });
|
||||||
|
await page.clock.resume();
|
||||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
// Create Time List
|
// Create Time List
|
||||||
const timelist = await createDomainObjectWithDefaults(page, {
|
const timelist = await createDomainObjectWithDefaults(page, {
|
||||||
@ -115,9 +117,7 @@ test.describe('Time List with controlled clock @clock', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Navigate to the Time List in real-time mode
|
// Navigate to the Time List in real-time mode
|
||||||
await page.goto(
|
await navigateToObjectWithRealTime(page, timelist.url, 900000, 1800000);
|
||||||
`${timelist.url}?tc.mode=local&tc.startDelta=900000&tc.endDelta=1800000&tc.timeSystem=utc&view=grid`
|
|
||||||
);
|
|
||||||
|
|
||||||
//Expand the viewport to show the entire time list
|
//Expand the viewport to show the entire time list
|
||||||
await page.getByLabel('Collapse Inspect Pane').click();
|
await page.getByLabel('Collapse Inspect Pane').click();
|
||||||
@ -172,16 +172,9 @@ test.describe('Time List with controlled clock @clock', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test.describe('Activity progress when activity is in the future @clock', () => {
|
test.describe('Activity progress when activity is in the future @clock', () => {
|
||||||
const firstActivity = getFirstActivity(examplePlanSmall1);
|
|
||||||
|
|
||||||
test.use({
|
|
||||||
clockOptions: {
|
|
||||||
now: firstActivity.start - 1,
|
|
||||||
shouldAdvanceTime: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await page.clock.install({ time: FIRST_ACTIVITY_SMALL_1.start - 1 });
|
||||||
|
await page.clock.resume();
|
||||||
await createTimelistWithPlanAndSetActivityInProgress(page, examplePlanSmall1);
|
await createTimelistWithPlanAndSetActivityInProgress(page, examplePlanSmall1);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -195,18 +188,13 @@ test.describe('Activity progress when activity is in the future @clock', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test.describe('Activity progress when now is between start and end of the activity @clock', () => {
|
test.describe('Activity progress when now is between start and end of the activity @clock', () => {
|
||||||
const firstActivity = getFirstActivity(examplePlanSmall1);
|
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await page.clock.install({ time: FIRST_ACTIVITY_SMALL_1.start + 50000 });
|
||||||
|
await page.clock.resume();
|
||||||
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
await createTimelistWithPlanAndSetActivityInProgress(page, examplePlanSmall1);
|
await createTimelistWithPlanAndSetActivityInProgress(page, examplePlanSmall1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test.use({
|
|
||||||
clockOptions: {
|
|
||||||
now: firstActivity.start + 50000,
|
|
||||||
shouldAdvanceTime: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test('progress pie is partially filled', async ({ page }) => {
|
test('progress pie is partially filled', async ({ page }) => {
|
||||||
const anActivity = page.getByRole('row').nth(0);
|
const anActivity = page.getByRole('row').nth(0);
|
||||||
const pathElement = anActivity.getByLabel('Activity in progress').locator('path');
|
const pathElement = anActivity.getByLabel('Activity in progress').locator('path');
|
||||||
@ -216,16 +204,10 @@ test.describe('Activity progress when now is between start and end of the activi
|
|||||||
});
|
});
|
||||||
|
|
||||||
test.describe('Activity progress when now is after end of the activity @clock', () => {
|
test.describe('Activity progress when now is after end of the activity @clock', () => {
|
||||||
const firstActivity = getFirstActivity(examplePlanSmall1);
|
|
||||||
|
|
||||||
test.use({
|
|
||||||
clockOptions: {
|
|
||||||
now: firstActivity.end + 10000,
|
|
||||||
shouldAdvanceTime: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await page.clock.install({ time: FIRST_ACTIVITY_SMALL_1.end + 10000 });
|
||||||
|
await page.clock.resume();
|
||||||
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
await createTimelistWithPlanAndSetActivityInProgress(page, examplePlanSmall1);
|
await createTimelistWithPlanAndSetActivityInProgress(page, examplePlanSmall1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -23,7 +23,8 @@
|
|||||||
import {
|
import {
|
||||||
createDomainObjectWithDefaults,
|
createDomainObjectWithDefaults,
|
||||||
createPlanFromJSON,
|
createPlanFromJSON,
|
||||||
setIndependentTimeConductorBounds
|
navigateToObjectWithFixedTimeBounds,
|
||||||
|
setFixedIndependentTimeConductorBounds
|
||||||
} from '../../../appActions.js';
|
} from '../../../appActions.js';
|
||||||
import { expect, test } from '../../../pluginFixtures.js';
|
import { expect, test } from '../../../pluginFixtures.js';
|
||||||
|
|
||||||
@ -73,7 +74,7 @@ const testPlan = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
test.describe('Time Strip', () => {
|
test.describe('Time Strip', () => {
|
||||||
test('Create two Time Strips, add a single Plan to both, and verify they can have separate Independent Time Contexts @unstable', async ({
|
test('Create two Time Strips, add a single Plan to both, and verify they can have separate Independent Time Contexts', async ({
|
||||||
page
|
page
|
||||||
}) => {
|
}) => {
|
||||||
test.info().annotations.push({
|
test.info().annotations.push({
|
||||||
@ -103,17 +104,17 @@ test.describe('Time Strip', () => {
|
|||||||
|
|
||||||
await page.goto(timestrip.url);
|
await page.goto(timestrip.url);
|
||||||
// Expand the tree to show the plan
|
// Expand the tree to show the plan
|
||||||
await page.click("button[title='Show selected item in tree']");
|
await page.getByLabel('Show selected item in tree').click();
|
||||||
await page.dragAndDrop(`role=treeitem[name=/${createdPlan.name}/]`, '.c-object-view');
|
await page
|
||||||
await page.click("button[title='Save']");
|
.getByLabel(`Navigate to ${createdPlan.name}`)
|
||||||
await page.click("li[title='Save and Finish Editing']");
|
.dragTo(page.getByLabel('Object View'));
|
||||||
|
await page.getByLabel('Save').click();
|
||||||
|
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
const startBound = testPlan.TEST_GROUP[0].start;
|
const startBound = testPlan.TEST_GROUP[0].start;
|
||||||
const endBound = testPlan.TEST_GROUP[testPlan.TEST_GROUP.length - 1].end;
|
const endBound = testPlan.TEST_GROUP[testPlan.TEST_GROUP.length - 1].end;
|
||||||
|
|
||||||
// Switch to fixed time mode with all plan events within the bounds
|
// Switch to fixed time mode with all plan events within the bounds
|
||||||
await page.goto(
|
await navigateToObjectWithFixedTimeBounds(page, timestrip.url, startBound, endBound);
|
||||||
`${timestrip.url}?tc.mode=fixed&tc.startBound=${startBound}&tc.endBound=${endBound}&tc.timeSystem=utc&view=time-strip.view`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Verify all events are displayed
|
// Verify all events are displayed
|
||||||
const eventCount = await page.locator('.activity-bounds').count();
|
const eventCount = await page.locator('.activity-bounds').count();
|
||||||
@ -131,7 +132,7 @@ test.describe('Time Strip', () => {
|
|||||||
const startBoundString = new Date(startBound).toISOString().replace('T', ' ');
|
const startBoundString = new Date(startBound).toISOString().replace('T', ' ');
|
||||||
const endBoundString = new Date(endBound).toISOString().replace('T', ' ');
|
const endBoundString = new Date(endBound).toISOString().replace('T', ' ');
|
||||||
|
|
||||||
await setIndependentTimeConductorBounds(page, {
|
await setFixedIndependentTimeConductorBounds(page, {
|
||||||
start: startBoundString,
|
start: startBoundString,
|
||||||
end: endBoundString
|
end: endBoundString
|
||||||
});
|
});
|
||||||
@ -149,9 +150,9 @@ test.describe('Time Strip', () => {
|
|||||||
expect(objectName).toBe(createdTimeStrip.name);
|
expect(objectName).toBe(createdTimeStrip.name);
|
||||||
|
|
||||||
// Drag the existing Plan onto the newly created Time Strip, and save.
|
// Drag the existing Plan onto the newly created Time Strip, and save.
|
||||||
await page.dragAndDrop(`role=treeitem[name=/${plan.name}/]`, '.c-object-view');
|
await page.getByLabel(`Navigate to ${plan.name}`).dragTo(page.getByLabel('Object View'));
|
||||||
await page.click("button[title='Save']");
|
await page.getByLabel('Save').click();
|
||||||
await page.click("li[title='Save and Finish Editing']");
|
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
|
|
||||||
// All events should be displayed at this point because the
|
// All events should be displayed at this point because the
|
||||||
// initial independent context bounds will match the global bounds
|
// initial independent context bounds will match the global bounds
|
||||||
@ -163,7 +164,7 @@ test.describe('Time Strip', () => {
|
|||||||
const startBoundString = new Date(startBound).toISOString().replace('T', ' ');
|
const startBoundString = new Date(startBound).toISOString().replace('T', ' ');
|
||||||
const endBoundString = new Date(endBound).toISOString().replace('T', ' ');
|
const endBoundString = new Date(endBound).toISOString().replace('T', ' ');
|
||||||
|
|
||||||
await setIndependentTimeConductorBounds(page, {
|
await setFixedIndependentTimeConductorBounds(page, {
|
||||||
start: startBoundString,
|
start: startBoundString,
|
||||||
end: endBoundString
|
end: endBoundString
|
||||||
});
|
});
|
||||||
|
@ -41,11 +41,10 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage @2p', () =>
|
|||||||
const context = await browser.newContext();
|
const context = await browser.newContext();
|
||||||
const page = await context.newPage();
|
const page = await context.newPage();
|
||||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
await page.getByRole('button', { name: 'Create' }).click();
|
const conditionSet = await createDomainObjectWithDefaults(page, {
|
||||||
|
type: 'Condition Set',
|
||||||
await page.locator('li[role="menuitem"]:has-text("Condition Set")').click();
|
name: 'Unnamed Condition Set'
|
||||||
|
});
|
||||||
await Promise.all([page.waitForNavigation(), page.click('button:has-text("OK")')]);
|
|
||||||
|
|
||||||
//Save localStorage for future test execution
|
//Save localStorage for future test execution
|
||||||
await context.storageState({
|
await context.storageState({
|
||||||
@ -55,7 +54,7 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage @2p', () =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
//Set object identifier from url
|
//Set object identifier from url
|
||||||
conditionSetUrl = page.url();
|
conditionSetUrl = conditionSet.url;
|
||||||
|
|
||||||
await page.close();
|
await page.close();
|
||||||
});
|
});
|
||||||
@ -68,44 +67,39 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage @2p', () =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
//Begin suite of tests again localStorage
|
//Begin suite of tests again localStorage
|
||||||
test.fixme(
|
test('Condition set object properties persist in main view and inspector after reload @localStorage', async ({
|
||||||
'Condition set object properties persist in main view and inspector @localStorage',
|
page
|
||||||
async ({ page }) => {
|
}) => {
|
||||||
test.info().annotations.push({
|
//Navigate to baseURL with injected localStorage
|
||||||
type: 'issue',
|
await page.goto(conditionSetUrl, { waitUntil: 'domcontentloaded' });
|
||||||
description: 'https://github.com/nasa/openmct/issues/7421'
|
|
||||||
});
|
|
||||||
//Navigate to baseURL with injected localStorage
|
|
||||||
await page.goto(conditionSetUrl, { waitUntil: 'networkidle' });
|
|
||||||
|
|
||||||
//Assertions on loaded Condition Set in main view. This is a stateful transition step after page.goto()
|
//Assertions on loaded Condition Set in main view. This is a stateful transition step after page.goto()
|
||||||
await expect
|
await expect.soft(page.getByRole('main')).toContainText('Unnamed Condition Set');
|
||||||
.soft(page.locator('.l-browse-bar__object-name'))
|
|
||||||
.toContainText('Unnamed Condition Set');
|
|
||||||
|
|
||||||
//Assertions on loaded Condition Set in Inspector
|
//Assertions on loaded Condition Set in Inspector
|
||||||
expect.soft(page.locator('_vue=item.name=Unnamed Condition Set')).toBeTruthy();
|
await expect(
|
||||||
|
page.getByLabel('Title inspector properties').getByLabel('inspector property value')
|
||||||
|
).toContainText('Unnamed Condition Set');
|
||||||
|
|
||||||
//Reload Page
|
//Reload Page
|
||||||
await Promise.all([page.reload(), page.waitForLoadState('networkidle')]);
|
await page.reload({ waitUntil: 'domcontentloaded' });
|
||||||
|
|
||||||
|
//Re-verify after reload
|
||||||
|
await expect(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 }) => {
|
test('condition set object can be modified on @localStorage', async ({ page, openmctConfig }) => {
|
||||||
const { myItemsFolderName } = 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()
|
//Assertions on loaded Condition Set in main view. This is a stateful transition step after page.goto()
|
||||||
await expect
|
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set');
|
||||||
.soft(page.locator('.l-browse-bar__object-name'))
|
|
||||||
.toContainText('Unnamed Condition Set');
|
|
||||||
|
|
||||||
//Update the Condition Set properties
|
//Update the Condition Set properties
|
||||||
// Click Edit Button
|
// Click Edit Button
|
||||||
@ -151,7 +145,7 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage @2p', () =>
|
|||||||
expect(page.locator('a:has-text("Renamed Condition Set")')).toBeTruthy();
|
expect(page.locator('a:has-text("Renamed Condition Set")')).toBeTruthy();
|
||||||
|
|
||||||
//Reload Page
|
//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
|
//Verify Main section reflects updated Name Property
|
||||||
await expect
|
await expect
|
||||||
@ -213,7 +207,7 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage @2p', () =>
|
|||||||
|
|
||||||
//Feature?
|
//Feature?
|
||||||
//Domain Object is still available by direct URL after delete
|
//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');
|
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -267,7 +261,7 @@ test.describe('Basic Condition Set Use', () => {
|
|||||||
await page.getByLabel('Edit Object').click();
|
await page.getByLabel('Edit Object').click();
|
||||||
|
|
||||||
// Expand the 'My Items' folder in the left tree
|
// Expand the 'My Items' folder in the left tree
|
||||||
page.click('button[title="Show selected item in tree"]');
|
await page.getByLabel('Show selected item in tree').click();
|
||||||
// Add the Alpha & Beta Sine Wave Generator to the Condition Set and save changes
|
// Add the Alpha & Beta Sine Wave Generator to the Condition Set and save changes
|
||||||
const treePane = page.getByRole('tree', {
|
const treePane = page.getByRole('tree', {
|
||||||
name: 'Main Tree'
|
name: 'Main Tree'
|
||||||
@ -538,7 +532,7 @@ test.describe('Condition Set Composition', () => {
|
|||||||
.getByLabel(`${exampleTelemetry.name} Context Menu`)
|
.getByLabel(`${exampleTelemetry.name} Context Menu`)
|
||||||
.getByRole('menuitem', { name: 'Remove' })
|
.getByRole('menuitem', { name: 'Remove' })
|
||||||
.click();
|
.click();
|
||||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
await page.getByRole('button', { name: 'Ok', exact: true }).click();
|
||||||
|
|
||||||
await page
|
await page
|
||||||
.getByLabel(`Navigate to ${conditionSet.name} conditionSet Object`, { exact: true })
|
.getByLabel(`Navigate to ${conditionSet.name} conditionSet Object`, { exact: true })
|
||||||
|
@ -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
|
||||||
|
await page.getByLabel('Show selected item in tree').click();
|
||||||
|
// 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);
|
||||||
|
});
|
||||||
|
});
|
@ -24,8 +24,8 @@ import { fileURLToPath } from 'url';
|
|||||||
import {
|
import {
|
||||||
createDomainObjectWithDefaults,
|
createDomainObjectWithDefaults,
|
||||||
navigateToObjectWithFixedTimeBounds,
|
navigateToObjectWithFixedTimeBounds,
|
||||||
|
setFixedIndependentTimeConductorBounds,
|
||||||
setFixedTimeMode,
|
setFixedTimeMode,
|
||||||
setIndependentTimeConductorBounds,
|
|
||||||
setRealTimeMode,
|
setRealTimeMode,
|
||||||
setStartOffset
|
setStartOffset
|
||||||
} from '../../../../appActions.js';
|
} from '../../../../appActions.js';
|
||||||
@ -75,18 +75,12 @@ test.describe('Display Layout Sub-object Actions @localStorage', () => {
|
|||||||
const TEST_FIXED_END_TIME = TEST_FIXED_START_TIME + 3600000; // 2024-11-11 20:11:11.000Z
|
const TEST_FIXED_END_TIME = TEST_FIXED_START_TIME + 3600000; // 2024-11-11 20:11:11.000Z
|
||||||
|
|
||||||
// Verify the ITC has the expected initial bounds
|
// Verify the ITC has the expected initial bounds
|
||||||
expect(
|
await expect(
|
||||||
await page
|
page.getByLabel('Child Overlay Plot 1 Frame Controls').getByLabel('Start bounds')
|
||||||
.getByLabel('Child Overlay Plot 1 Frame Controls')
|
).toHaveText(INIT_ITC_START_BOUNDS);
|
||||||
.getByLabel('Start bounds')
|
await expect(
|
||||||
.textContent()
|
page.getByLabel('Child Overlay Plot 1 Frame Controls').getByLabel('End bounds')
|
||||||
).toEqual(INIT_ITC_START_BOUNDS);
|
).toHaveText(INIT_ITC_END_BOUNDS);
|
||||||
expect(
|
|
||||||
await page
|
|
||||||
.getByLabel('Child Overlay Plot 1 Frame Controls')
|
|
||||||
.getByLabel('End bounds')
|
|
||||||
.textContent()
|
|
||||||
).toEqual(INIT_ITC_END_BOUNDS);
|
|
||||||
|
|
||||||
// Update the global fixed bounds to 2024-11-11 19:11:11.000Z / 2024-11-11 20:11:11.000Z
|
// Update the global fixed bounds to 2024-11-11 19:11:11.000Z / 2024-11-11 20:11:11.000Z
|
||||||
const url = page.url().split('?')[0];
|
const url = page.url().split('?')[0];
|
||||||
@ -98,18 +92,12 @@ test.describe('Display Layout Sub-object Actions @localStorage', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// ITC bounds should still match the initial ITC bounds
|
// ITC bounds should still match the initial ITC bounds
|
||||||
expect(
|
await expect(
|
||||||
await page
|
page.getByLabel('Child Overlay Plot 1 Frame Controls').getByLabel('Start bounds')
|
||||||
.getByLabel('Child Overlay Plot 1 Frame Controls')
|
).toHaveText(INIT_ITC_START_BOUNDS);
|
||||||
.getByLabel('Start bounds')
|
await expect(
|
||||||
.textContent()
|
page.getByLabel('Child Overlay Plot 1 Frame Controls').getByLabel('End bounds')
|
||||||
).toEqual(INIT_ITC_START_BOUNDS);
|
).toHaveText(INIT_ITC_END_BOUNDS);
|
||||||
expect(
|
|
||||||
await page
|
|
||||||
.getByLabel('Child Overlay Plot 1 Frame Controls')
|
|
||||||
.getByLabel('End bounds')
|
|
||||||
.textContent()
|
|
||||||
).toEqual(INIT_ITC_END_BOUNDS);
|
|
||||||
|
|
||||||
// Open the Child Overlay Plot 1 in a new tab
|
// Open the Child Overlay Plot 1 in a new tab
|
||||||
await page.getByLabel('View menu items').click();
|
await page.getByLabel('View menu items').click();
|
||||||
@ -120,28 +108,22 @@ test.describe('Display Layout Sub-object Actions @localStorage', () => {
|
|||||||
await newPage.waitForLoadState('domcontentloaded');
|
await newPage.waitForLoadState('domcontentloaded');
|
||||||
|
|
||||||
// Verify that the global time conductor bounds in the new page match the updated global bounds
|
// Verify that the global time conductor bounds in the new page match the updated global bounds
|
||||||
expect(
|
await expect(newPage.getByLabel('Global Time Conductor').getByLabel('Start bounds')).toHaveText(
|
||||||
await newPage.getByLabel('Global Time Conductor').getByLabel('Start bounds').textContent()
|
NEW_GLOBAL_START_BOUNDS
|
||||||
).toEqual(NEW_GLOBAL_START_BOUNDS);
|
);
|
||||||
expect(
|
await expect(newPage.getByLabel('Global Time Conductor').getByLabel('End bounds')).toHaveText(
|
||||||
await newPage.getByLabel('Global Time Conductor').getByLabel('End bounds').textContent()
|
NEW_GLOBAL_END_BOUNDS
|
||||||
).toEqual(NEW_GLOBAL_END_BOUNDS);
|
);
|
||||||
|
|
||||||
// Verify that the ITC is enabled in the new page
|
// Verify that the ITC is enabled in the new page
|
||||||
await expect(newPage.getByLabel('Disable Independent Time Conductor')).toBeVisible();
|
await expect(newPage.getByLabel('Disable Independent Time Conductor')).toBeVisible();
|
||||||
// Verify that the ITC bounds in the new page match the original ITC bounds
|
// Verify that the ITC bounds in the new page match the original ITC bounds
|
||||||
expect(
|
await expect(
|
||||||
await newPage
|
newPage.getByLabel('Independent Time Conductor Panel').getByLabel('Start bounds')
|
||||||
.getByLabel('Independent Time Conductor Panel')
|
).toHaveText(INIT_ITC_START_BOUNDS);
|
||||||
.getByLabel('Start bounds')
|
await expect(
|
||||||
.textContent()
|
newPage.getByLabel('Independent Time Conductor Panel').getByLabel('End bounds')
|
||||||
).toEqual(INIT_ITC_START_BOUNDS);
|
).toHaveText(INIT_ITC_END_BOUNDS);
|
||||||
expect(
|
|
||||||
await newPage
|
|
||||||
.getByLabel('Independent Time Conductor Panel')
|
|
||||||
.getByLabel('End bounds')
|
|
||||||
.textContent()
|
|
||||||
).toEqual(INIT_ITC_END_BOUNDS);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -174,17 +156,17 @@ test.describe('Display Layout Toolbar Actions @localStorage', () => {
|
|||||||
test('can add/remove Image to a single layout', async ({ page }) => {
|
test('can add/remove Image to a single layout', async ({ page }) => {
|
||||||
const layoutObject = 'Image';
|
const layoutObject = 'Image';
|
||||||
await test.step("Add and remove image element from the parent's layout", async () => {
|
await test.step("Add and remove image element from the parent's layout", async () => {
|
||||||
expect(await page.getByLabel(`Move ${layoutObject} Frame`).count()).toBe(0);
|
await expect(page.getByLabel(`Move ${layoutObject} Frame`)).toHaveCount(0);
|
||||||
await addLayoutObject(page, PARENT_DISPLAY_LAYOUT_NAME, layoutObject);
|
await addLayoutObject(page, PARENT_DISPLAY_LAYOUT_NAME, layoutObject);
|
||||||
expect(await page.getByLabel(`Move ${layoutObject} Frame`).count()).toBe(1);
|
await expect(page.getByLabel(`Move ${layoutObject} Frame`)).toHaveCount(1);
|
||||||
await removeLayoutObject(page, layoutObject);
|
await removeLayoutObject(page, layoutObject);
|
||||||
expect(await page.getByLabel(`Move ${layoutObject} Frame`).count()).toBe(0);
|
await expect(page.getByLabel(`Move ${layoutObject} Frame`)).toHaveCount(0);
|
||||||
});
|
});
|
||||||
await test.step("Add and remove image from the child's layout", async () => {
|
await test.step("Add and remove image from the child's layout", async () => {
|
||||||
await addLayoutObject(page, CHILD_DISPLAY_LAYOUT_NAME1, layoutObject);
|
await addLayoutObject(page, CHILD_DISPLAY_LAYOUT_NAME1, layoutObject);
|
||||||
expect(await page.getByLabel(`Move ${layoutObject} Frame`).count()).toBe(1);
|
await expect(page.getByLabel(`Move ${layoutObject} Frame`)).toHaveCount(1);
|
||||||
await removeLayoutObject(page, layoutObject);
|
await removeLayoutObject(page, layoutObject);
|
||||||
expect(await page.getByLabel(`Move ${layoutObject} Frame`).count()).toBe(0);
|
await expect(page.getByLabel(`Move ${layoutObject} Frame`)).toHaveCount(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
test(`can add/remove Box to a single layout`, async ({ page }) => {
|
test(`can add/remove Box to a single layout`, async ({ page }) => {
|
||||||
@ -253,20 +235,17 @@ test.describe('Display Layout', () => {
|
|||||||
const sineWaveGeneratorTreeItem = treePane.getByRole('treeitem', {
|
const sineWaveGeneratorTreeItem = treePane.getByRole('treeitem', {
|
||||||
name: new RegExp(sineWaveObject.name)
|
name: new RegExp(sineWaveObject.name)
|
||||||
});
|
});
|
||||||
const layoutGridHolder = page.locator('.l-layout__grid-holder');
|
await sineWaveGeneratorTreeItem.dragTo(page.getByLabel('Layout Grid'));
|
||||||
await sineWaveGeneratorTreeItem.dragTo(layoutGridHolder);
|
|
||||||
await page.locator('button[title="Save"]').click();
|
await page.locator('button[title="Save"]').click();
|
||||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
|
|
||||||
// Subscribe to the Sine Wave Generator data
|
// Subscribe to the Sine Wave Generator data
|
||||||
// On getting data, check if the value found in the Display Layout is the most recent value
|
// On getting data, check if the value found in the Display Layout is the most recent value
|
||||||
// from the Sine Wave Generator
|
// from the Sine Wave Generator
|
||||||
const getTelemValuePromise = await subscribeToTelemetry(page, sineWaveObject.uuid);
|
const getTelemValuePromise = subscribeToTelemetry(page, sineWaveObject.uuid);
|
||||||
const formattedTelemetryValue = getTelemValuePromise;
|
const formattedTelemetryValue = await getTelemValuePromise;
|
||||||
const displayLayoutValuePromise = await page.waitForSelector(
|
await expect(page.getByText(formattedTelemetryValue)).toBeVisible();
|
||||||
`text="${formattedTelemetryValue}"`
|
const displayLayoutValue = await page.getByText(formattedTelemetryValue).textContent();
|
||||||
);
|
|
||||||
const displayLayoutValue = await displayLayoutValuePromise.textContent();
|
|
||||||
const trimmedDisplayValue = displayLayoutValue.trim();
|
const trimmedDisplayValue = displayLayoutValue.trim();
|
||||||
|
|
||||||
expect(trimmedDisplayValue).toBe(formattedTelemetryValue);
|
expect(trimmedDisplayValue).toBe(formattedTelemetryValue);
|
||||||
@ -298,24 +277,21 @@ test.describe('Display Layout', () => {
|
|||||||
const sineWaveGeneratorTreeItem = treePane.getByRole('treeitem', {
|
const sineWaveGeneratorTreeItem = treePane.getByRole('treeitem', {
|
||||||
name: new RegExp(sineWaveObject.name)
|
name: new RegExp(sineWaveObject.name)
|
||||||
});
|
});
|
||||||
const layoutGridHolder = page.locator('.l-layout__grid-holder');
|
await sineWaveGeneratorTreeItem.dragTo(page.getByLabel('Layout Grid'));
|
||||||
await sineWaveGeneratorTreeItem.dragTo(layoutGridHolder);
|
|
||||||
await page.locator('button[title="Save"]').click();
|
await page.locator('button[title="Save"]').click();
|
||||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
|
|
||||||
// Subscribe to the Sine Wave Generator data
|
// Subscribe to the Sine Wave Generator data
|
||||||
const getTelemValuePromise = await subscribeToTelemetry(page, sineWaveObject.uuid);
|
const getTelemValuePromise = subscribeToTelemetry(page, sineWaveObject.uuid);
|
||||||
// Set an offset of 1 minute and then change the time mode to fixed to set a 1 minute historical window
|
// Set an offset of 1 minute and then change the time mode to fixed to set a 1 minute historical window
|
||||||
await setStartOffset(page, { mins: '1' });
|
await setStartOffset(page, { startMins: '1' });
|
||||||
await setFixedTimeMode(page);
|
await setFixedTimeMode(page);
|
||||||
|
|
||||||
// On getting data, check if the value found in the Display Layout is the most recent value
|
// On getting data, check if the value found in the Display Layout is the most recent value
|
||||||
// from the Sine Wave Generator
|
// from the Sine Wave Generator
|
||||||
const formattedTelemetryValue = getTelemValuePromise;
|
const formattedTelemetryValue = await getTelemValuePromise;
|
||||||
const displayLayoutValuePromise = await page.waitForSelector(
|
await expect(page.getByText(formattedTelemetryValue)).toBeVisible();
|
||||||
`text="${formattedTelemetryValue}"`
|
const displayLayoutValue = await page.getByText(formattedTelemetryValue).textContent();
|
||||||
);
|
|
||||||
const displayLayoutValue = await displayLayoutValuePromise.textContent();
|
|
||||||
const trimmedDisplayValue = displayLayoutValue.trim();
|
const trimmedDisplayValue = displayLayoutValue.trim();
|
||||||
|
|
||||||
expect(trimmedDisplayValue).toBe(formattedTelemetryValue);
|
expect(trimmedDisplayValue).toBe(formattedTelemetryValue);
|
||||||
@ -340,8 +316,7 @@ test.describe('Display Layout', () => {
|
|||||||
const sineWaveGeneratorTreeItem = treePane.getByRole('treeitem', {
|
const sineWaveGeneratorTreeItem = treePane.getByRole('treeitem', {
|
||||||
name: new RegExp(sineWaveObject.name)
|
name: new RegExp(sineWaveObject.name)
|
||||||
});
|
});
|
||||||
const layoutGridHolder = page.locator('.l-layout__grid-holder');
|
await sineWaveGeneratorTreeItem.dragTo(page.getByLabel('Layout Grid'));
|
||||||
await sineWaveGeneratorTreeItem.dragTo(layoutGridHolder);
|
|
||||||
await page.locator('button[title="Save"]').click();
|
await page.locator('button[title="Save"]').click();
|
||||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
|
|
||||||
@ -382,8 +357,7 @@ test.describe('Display Layout', () => {
|
|||||||
const sineWaveGeneratorTreeItem = treePane.getByRole('treeitem', {
|
const sineWaveGeneratorTreeItem = treePane.getByRole('treeitem', {
|
||||||
name: new RegExp(sineWaveObject.name)
|
name: new RegExp(sineWaveObject.name)
|
||||||
});
|
});
|
||||||
const layoutGridHolder = page.locator('.l-layout__grid-holder');
|
await sineWaveGeneratorTreeItem.dragTo(page.getByLabel('Layout Grid'));
|
||||||
await sineWaveGeneratorTreeItem.dragTo(layoutGridHolder);
|
|
||||||
await page.locator('button[title="Save"]').click();
|
await page.locator('button[title="Save"]').click();
|
||||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
|
|
||||||
@ -428,8 +402,7 @@ test.describe('Display Layout', () => {
|
|||||||
const exampleImageryTreeItem = treePane.getByRole('treeitem', {
|
const exampleImageryTreeItem = treePane.getByRole('treeitem', {
|
||||||
name: new RegExp(exampleImageryObject.name)
|
name: new RegExp(exampleImageryObject.name)
|
||||||
});
|
});
|
||||||
let layoutGridHolder = page.locator('.l-layout__grid-holder');
|
await exampleImageryTreeItem.dragTo(page.getByLabel('Layout Grid'));
|
||||||
await exampleImageryTreeItem.dragTo(layoutGridHolder);
|
|
||||||
|
|
||||||
//adjust so that we can see the independent time conductor toggle
|
//adjust so that we can see the independent time conductor toggle
|
||||||
// Adjust object height
|
// Adjust object height
|
||||||
@ -445,7 +418,7 @@ test.describe('Display Layout', () => {
|
|||||||
|
|
||||||
const startDate = '2021-12-30 01:01:00.000Z';
|
const startDate = '2021-12-30 01:01:00.000Z';
|
||||||
const endDate = '2021-12-30 01:11:00.000Z';
|
const endDate = '2021-12-30 01:11:00.000Z';
|
||||||
await setIndependentTimeConductorBounds(page, { start: startDate, end: endDate });
|
await setFixedIndependentTimeConductorBounds(page, { start: startDate, end: endDate });
|
||||||
|
|
||||||
// check image date
|
// check image date
|
||||||
await expect(page.getByText('2021-12-30 01:11:00.000Z').first()).toBeVisible();
|
await expect(page.getByText('2021-12-30 01:11:00.000Z').first()).toBeVisible();
|
||||||
@ -485,9 +458,8 @@ test.describe('Display Layout', () => {
|
|||||||
name: new RegExp(sineWaveObject.name)
|
name: new RegExp(sineWaveObject.name)
|
||||||
});
|
});
|
||||||
|
|
||||||
let layoutGridHolder = page.locator('.l-layout__grid-holder');
|
|
||||||
// eslint-disable-next-line playwright/no-force-option
|
// eslint-disable-next-line playwright/no-force-option
|
||||||
await sineWaveGeneratorTreeItem.dragTo(layoutGridHolder, { force: true });
|
await sineWaveGeneratorTreeItem.dragTo(page.getByLabel('Layout Grid'), { force: true });
|
||||||
|
|
||||||
await page.getByText('View type').click();
|
await page.getByText('View type').click();
|
||||||
await page.getByText('Overlay Plot').click();
|
await page.getByText('Overlay Plot').click();
|
||||||
@ -495,14 +467,13 @@ test.describe('Display Layout', () => {
|
|||||||
const anotherSineWaveGeneratorTreeItem = treePane.getByRole('treeitem', {
|
const anotherSineWaveGeneratorTreeItem = treePane.getByRole('treeitem', {
|
||||||
name: new RegExp(anotherSineWaveObject.name)
|
name: new RegExp(anotherSineWaveObject.name)
|
||||||
});
|
});
|
||||||
layoutGridHolder = page.locator('.l-layout__grid-holder');
|
|
||||||
// eslint-disable-next-line playwright/no-force-option
|
// eslint-disable-next-line playwright/no-force-option
|
||||||
await anotherSineWaveGeneratorTreeItem.dragTo(layoutGridHolder, { force: true });
|
await anotherSineWaveGeneratorTreeItem.dragTo(page.getByLabel('Layout Grid'), { force: true });
|
||||||
|
|
||||||
await page.getByText('View type').click();
|
await page.getByText('View type').click();
|
||||||
await page.getByText('Overlay Plot').click();
|
await page.getByText('Overlay Plot').click();
|
||||||
|
|
||||||
await page.locator('button[title="Save"]').click();
|
await page.getByLabel('Save').click();
|
||||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
|
|
||||||
// Time to inspect some network traffic
|
// Time to inspect some network traffic
|
||||||
@ -519,10 +490,10 @@ test.describe('Display Layout', () => {
|
|||||||
await page.reload();
|
await page.reload();
|
||||||
|
|
||||||
// wait for annotations requests to be batched and requested
|
// 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:
|
// Network requests for the composite telemetry with multiple items should be:
|
||||||
// 1. a single batched request for annotations
|
// 1. a single batched request for annotations
|
||||||
expect(networkRequests.length).toBe(1);
|
await expect.poll(() => networkRequests, { timeout: 10000 }).toHaveLength(1);
|
||||||
|
|
||||||
await setRealTimeMode(page);
|
await setRealTimeMode(page);
|
||||||
|
|
||||||
@ -531,15 +502,15 @@ test.describe('Display Layout', () => {
|
|||||||
await page.reload();
|
await page.reload();
|
||||||
|
|
||||||
// wait for annotations to not load (if we have any, we've got a problem)
|
// 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
|
// In real time mode, we don't fetch annotations at all
|
||||||
expect(networkRequests.length).toBe(0);
|
await expect.poll(() => networkRequests, { timeout: 10000 }).toHaveLength(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
async function addAndRemoveDrawingObjectAndAssert(page, layoutObject, DISPLAY_LAYOUT_NAME) {
|
async function addAndRemoveDrawingObjectAndAssert(page, layoutObject, DISPLAY_LAYOUT_NAME) {
|
||||||
expect(await page.getByLabel(layoutObject, { exact: true }).count()).toBe(0);
|
await expect(page.getByLabel(layoutObject, { exact: true })).toHaveCount(0);
|
||||||
await addLayoutObject(page, DISPLAY_LAYOUT_NAME, layoutObject);
|
await addLayoutObject(page, DISPLAY_LAYOUT_NAME, layoutObject);
|
||||||
expect(
|
expect(
|
||||||
await page
|
await page
|
||||||
@ -549,7 +520,7 @@ async function addAndRemoveDrawingObjectAndAssert(page, layoutObject, DISPLAY_LA
|
|||||||
.count()
|
.count()
|
||||||
).toBe(1);
|
).toBe(1);
|
||||||
await removeLayoutObject(page, layoutObject);
|
await removeLayoutObject(page, layoutObject);
|
||||||
expect(await page.getByLabel(layoutObject, { exact: true }).count()).toBe(0);
|
await expect(page.getByLabel(layoutObject, { exact: true })).toHaveCount(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -565,7 +536,7 @@ async function removeLayoutObject(page, layoutObject) {
|
|||||||
// eslint-disable-next-line playwright/no-force-option
|
// eslint-disable-next-line playwright/no-force-option
|
||||||
.click({ force: true });
|
.click({ force: true });
|
||||||
await page.getByTitle('Delete the selected object').click();
|
await page.getByTitle('Delete the selected object').click();
|
||||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
await page.getByRole('button', { name: 'Ok', exact: true }).click();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -584,10 +555,10 @@ async function addLayoutObject(page, layoutName, layoutObject) {
|
|||||||
.click();
|
.click();
|
||||||
if (layoutObject === 'Text') {
|
if (layoutObject === 'Text') {
|
||||||
await page.getByRole('textbox', { name: 'Text' }).fill('Hello, Universe!');
|
await page.getByRole('textbox', { name: 'Text' }).fill('Hello, Universe!');
|
||||||
await page.getByText('OK').click();
|
await page.getByText('Ok').click();
|
||||||
} else if (layoutObject === 'Image') {
|
} else if (layoutObject === 'Image') {
|
||||||
await page.getByLabel('Image URL').fill(TINY_IMAGE_BASE64);
|
await page.getByLabel('Image URL').fill(TINY_IMAGE_BASE64);
|
||||||
await page.getByText('OK').click();
|
await page.getByText('Ok').click();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,17 +62,15 @@ test.describe('The Fault Management Plugin using example faults', () => {
|
|||||||
await selectFaultItem(page, 1);
|
await selectFaultItem(page, 1);
|
||||||
|
|
||||||
await page.getByRole('tab', { name: 'Config' }).click();
|
await page.getByRole('tab', { name: 'Config' }).click();
|
||||||
const selectedFaultName = await page
|
|
||||||
.locator('.c-fault-mgmt__list.is-selected .c-fault-mgmt__list-faultname')
|
const inspectorFaultName = page
|
||||||
.textContent();
|
.getByLabel('Source inspector properties')
|
||||||
const inspectorFaultNameCount = await page
|
.getByLabel('inspector property value');
|
||||||
.locator(`.c-inspector__properties >> :text("${selectedFaultName}")`)
|
|
||||||
.count();
|
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
page.locator('.c-faults-list-view-item-body > .c-fault-mgmt__list').first()
|
page.locator('.c-faults-list-view-item-body > .c-fault-mgmt__list').first()
|
||||||
).toHaveClass(/is-selected/);
|
).toHaveClass(/is-selected/);
|
||||||
expect(inspectorFaultNameCount).toEqual(1);
|
await expect(inspectorFaultName).toHaveCount(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('When selecting multiple faults, no specific fault information is shown in the inspector', async ({
|
test('When selecting multiple faults, no specific fault information is shown in the inspector', async ({
|
||||||
@ -110,13 +108,13 @@ test.describe('The Fault Management Plugin using example faults', () => {
|
|||||||
|
|
||||||
// check it is removed from standard view
|
// check it is removed from standard view
|
||||||
const afterShelvedFault = getFaultByName(page, shelvedFaultName);
|
const afterShelvedFault = getFaultByName(page, shelvedFaultName);
|
||||||
expect(await afterShelvedFault.count()).toBe(0);
|
await expect(afterShelvedFault).toHaveCount(0);
|
||||||
|
|
||||||
await changeViewTo(page, 'shelved');
|
await changeViewTo(page, 'shelved');
|
||||||
|
|
||||||
const shelvedViewFault = getFaultByName(page, shelvedFaultName);
|
const shelvedViewFault = getFaultByName(page, shelvedFaultName);
|
||||||
|
|
||||||
expect(await shelvedViewFault.count()).toBe(1);
|
await expect(shelvedViewFault).toHaveCount(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Allows you to acknowledge a fault', async ({ page }) => {
|
test('Allows you to acknowledge a fault', async ({ page }) => {
|
||||||
|
@ -24,7 +24,7 @@ import { fileURLToPath } from 'url';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
createDomainObjectWithDefaults,
|
createDomainObjectWithDefaults,
|
||||||
setIndependentTimeConductorBounds
|
setFixedIndependentTimeConductorBounds
|
||||||
} from '../../../../appActions.js';
|
} from '../../../../appActions.js';
|
||||||
import { expect, test } from '../../../../pluginFixtures.js';
|
import { expect, test } from '../../../../pluginFixtures.js';
|
||||||
|
|
||||||
@ -248,7 +248,7 @@ test.describe('Flexible Layout', () => {
|
|||||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
|
|
||||||
// flip on independent time conductor
|
// flip on independent time conductor
|
||||||
await setIndependentTimeConductorBounds(page, {
|
await setFixedIndependentTimeConductorBounds(page, {
|
||||||
start: '2021-12-30 01:01:00.000Z',
|
start: '2021-12-30 01:01:00.000Z',
|
||||||
end: '2021-12-30 01:11:00.000Z'
|
end: '2021-12-30 01:11:00.000Z'
|
||||||
});
|
});
|
||||||
@ -292,7 +292,7 @@ test.describe('Flexible Layout Toolbar Actions @localStorage', () => {
|
|||||||
await expect(page.getByRole('dialog', { name: 'Overlay' })).toContainText(
|
await expect(page.getByRole('dialog', { name: 'Overlay' })).toContainText(
|
||||||
'This action will permanently delete this container from this Flexible Layout. Do you want to continue?'
|
'This action will permanently delete this container from this Flexible Layout. Do you want to continue?'
|
||||||
);
|
);
|
||||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
await page.getByRole('button', { name: 'Ok', exact: true }).click();
|
||||||
expect(await containerHandles.count()).toEqual(2);
|
expect(await containerHandles.count()).toEqual(2);
|
||||||
});
|
});
|
||||||
test('Remove Frame', async ({ page }) => {
|
test('Remove Frame', async ({ page }) => {
|
||||||
@ -302,7 +302,7 @@ test.describe('Flexible Layout Toolbar Actions @localStorage', () => {
|
|||||||
await expect(page.getByRole('dialog', { name: 'Overlay' })).toContainText(
|
await expect(page.getByRole('dialog', { name: 'Overlay' })).toContainText(
|
||||||
'This action will remove this frame from this Flexible Layout. Do you want to continue?'
|
'This action will remove this frame from this Flexible Layout. Do you want to continue?'
|
||||||
);
|
);
|
||||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
await page.getByRole('button', { name: 'Ok', exact: true }).click();
|
||||||
expect(await page.getByRole('group', { name: 'Frame' }).count()).toEqual(1);
|
expect(await page.getByRole('group', { name: 'Frame' }).count()).toEqual(1);
|
||||||
});
|
});
|
||||||
test('Columns/Rows Layout Toggle', async ({ page }) => {
|
test('Columns/Rows Layout Toggle', async ({ page }) => {
|
||||||
|
@ -38,7 +38,7 @@ test.describe('Gauge', () => {
|
|||||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Can add and remove telemetry sources @unstable', async ({ page }) => {
|
test('Can add and remove telemetry sources', async ({ page }) => {
|
||||||
// Create the gauge with defaults
|
// Create the gauge with defaults
|
||||||
const gauge = await createDomainObjectWithDefaults(page, { type: 'Gauge' });
|
const gauge = await createDomainObjectWithDefaults(page, { type: 'Gauge' });
|
||||||
|
|
||||||
@ -53,6 +53,7 @@ test.describe('Gauge', () => {
|
|||||||
// the SWG appears in the elements pool
|
// the SWG appears in the elements pool
|
||||||
await page.goto(gauge.url);
|
await page.goto(gauge.url);
|
||||||
await page.getByLabel('Edit Object').click();
|
await page.getByLabel('Edit Object').click();
|
||||||
|
await page.getByRole('tab', { name: 'Elements' }).click();
|
||||||
await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg1.name}`)).toBeVisible();
|
await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg1.name}`)).toBeVisible();
|
||||||
await page.getByRole('button', { name: 'Save' }).click();
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
@ -65,38 +66,35 @@ test.describe('Gauge', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Verify that the 'Replace telemetry source' modal appears and accept it
|
// Verify that the 'Replace telemetry source' modal appears and accept it
|
||||||
await expect
|
await expect(
|
||||||
.soft(
|
page.getByText(
|
||||||
page.locator(
|
'This action will replace the current telemetry source. Do you want to continue?'
|
||||||
'text=This action will replace the current telemetry source. Do you want to continue?'
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.toBeVisible();
|
).toBeVisible();
|
||||||
await page.click('text=Ok');
|
await page.getByRole('button', { name: 'Ok', exact: true }).click();
|
||||||
|
|
||||||
// Navigate to the gauge and verify that the new SWG
|
// Navigate to the gauge and verify that the new SWG
|
||||||
// appears in the elements pool and the old one is gone
|
// appears in the elements pool and the old one is gone
|
||||||
await page.goto(gauge.url);
|
await page.goto(gauge.url);
|
||||||
await page.getByLabel('Edit Object').click();
|
await page.getByLabel('Edit Object').click();
|
||||||
await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg1.name}`)).toBeHidden();
|
await page.getByRole('tab', { name: 'Elements' }).click();
|
||||||
await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg2.name}`)).toBeVisible();
|
await expect(page.getByLabel(`Preview ${swg1.name}`)).toBeHidden();
|
||||||
|
await expect(page.getByLabel(`Preview ${swg2.name}`)).toBeVisible();
|
||||||
await page.getByRole('button', { name: 'Save' }).click();
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
|
|
||||||
// Right click on the new SWG in the elements pool and delete it
|
// Right click on the new SWG in the elements pool and delete it
|
||||||
await page.locator(`#inspector-elements-tree >> text=${swg2.name}`).click({
|
await page.getByLabel(`Preview ${swg2.name}`).click({
|
||||||
button: 'right'
|
button: 'right'
|
||||||
});
|
});
|
||||||
await page.locator('li[title="Remove this object from its containing object."]').click();
|
await page.getByLabel('Remove').click();
|
||||||
|
|
||||||
// Verify that the 'Remove object' confirmation modal appears and accept it
|
// Verify that the 'Remove object' confirmation modal appears and accept it
|
||||||
await expect
|
await expect(
|
||||||
.soft(
|
page.getByText(
|
||||||
page.locator(
|
'Warning! This action will remove this object. Are you sure you want to continue?'
|
||||||
'text=Warning! This action will remove this object. Are you sure you want to continue?'
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.toBeVisible();
|
).toBeVisible();
|
||||||
await page.click('text=Ok');
|
await page.getByRole('button', { name: 'Ok', exact: true }).click();
|
||||||
|
|
||||||
// Verify that the elements pool shows no elements
|
// Verify that the elements pool shows no elements
|
||||||
await expect(page.locator('text="No contained elements"')).toBeVisible();
|
await expect(page.locator('text="No contained elements"')).toBeVisible();
|
||||||
@ -110,11 +108,11 @@ test.describe('Gauge', () => {
|
|||||||
await page.getByRole('button', { name: 'Create' }).click();
|
await page.getByRole('button', { name: 'Create' }).click();
|
||||||
|
|
||||||
// Click the object specified by 'type'
|
// Click the object specified by 'type'
|
||||||
await page.click(`li[role='menuitem']:text("Gauge")`);
|
await page.getByRole('menuitem', { name: 'Gauge' }).click();
|
||||||
// FIXME: We need better selectors for these custom form controls
|
// FIXME: We need better selectors for these custom form controls
|
||||||
const displayCurrentValueSwitch = page.locator('.c-toggle-switch__slider >> nth=0');
|
const displayCurrentValueSwitch = page.locator('.c-toggle-switch__slider >> nth=0');
|
||||||
await displayCurrentValueSwitch.setChecked(false);
|
await displayCurrentValueSwitch.uncheck();
|
||||||
await page.click('button[aria-label="Save"]');
|
await page.getByLabel('Save').click();
|
||||||
|
|
||||||
// TODO: Verify changes in the UI
|
// TODO: Verify changes in the UI
|
||||||
});
|
});
|
||||||
@ -126,24 +124,21 @@ test.describe('Gauge', () => {
|
|||||||
|
|
||||||
// Create the gauge with defaults
|
// Create the gauge with defaults
|
||||||
await createDomainObjectWithDefaults(page, { type: 'Gauge' });
|
await createDomainObjectWithDefaults(page, { type: 'Gauge' });
|
||||||
await page.click('button[title="More actions"]');
|
await page.getByLabel('More actions').click();
|
||||||
await page.click('li[role="menuitem"]:has-text("Edit Properties")');
|
await page.getByRole('menuitem', { name: 'Edit Properties...' }).click();
|
||||||
// FIXME: We need better selectors for these custom form controls
|
// FIXME: We need better selectors for these custom form controls
|
||||||
const displayCurrentValueSwitch = page.locator('.c-toggle-switch__slider >> nth=0');
|
const displayCurrentValueSwitch = page.locator('.c-toggle-switch__slider >> nth=0');
|
||||||
await displayCurrentValueSwitch.setChecked(false);
|
await displayCurrentValueSwitch.uncheck();
|
||||||
await page.click('button[aria-label="Save"]');
|
await page.getByLabel('Save').click();
|
||||||
|
|
||||||
// TODO: Verify changes in the UI
|
// TODO: Verify changes in the UI
|
||||||
});
|
});
|
||||||
|
|
||||||
test.fixme('Gauge does not display NaN when data not available', async ({ page }) => {
|
test('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'
|
|
||||||
});
|
|
||||||
// Create a Gauge
|
// Create a Gauge
|
||||||
const gauge = await createDomainObjectWithDefaults(page, {
|
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
|
// Create a Sine Wave Generator in the Gauge with a loading delay
|
||||||
@ -154,7 +149,7 @@ test.describe('Gauge', () => {
|
|||||||
await page.getByRole('menuitem', { name: /Edit Properties.../ }).click();
|
await page.getByRole('menuitem', { name: /Edit Properties.../ }).click();
|
||||||
|
|
||||||
//Edit Example Telemetry Object to include 5s loading Delay
|
//Edit Example Telemetry Object to include 5s loading Delay
|
||||||
await page.locator('[aria-label="Loading Delay \\(ms\\)"]').fill('5000');
|
await page.getByLabel('Loading Delay (ms)', { exact: true }).fill('5000');
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Save' }).click();
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
|
|
||||||
@ -162,9 +157,13 @@ test.describe('Gauge', () => {
|
|||||||
await page.waitForURL(`**/${gauge.uuid}/*`);
|
await page.waitForURL(`**/${gauge.uuid}/*`);
|
||||||
|
|
||||||
// Nav to the Gauge
|
// Nav to the Gauge
|
||||||
await page.goto(gauge.url);
|
await page.goto(gauge.url, { waitUntil: 'domcontentloaded' });
|
||||||
const gaugeNoDataText = await page.locator('.js-dial-current-value tspan').textContent();
|
// Check that the value is not displayed
|
||||||
expect(gaugeNoDataText).toBe('--');
|
//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 }) => {
|
test('Gauge enforces composition policy', async ({ page }) => {
|
||||||
|
@ -75,6 +75,7 @@ test.describe('Example Imagery Object', () => {
|
|||||||
const backgroundImage = page.getByLabel('Focused Image Element');
|
const backgroundImage = page.getByLabel('Focused Image Element');
|
||||||
await backgroundImage.click({
|
await backgroundImage.click({
|
||||||
button: 'right',
|
button: 'right',
|
||||||
|
// Need force option here due to annotation overlay which blocks playwright's click
|
||||||
// eslint-disable-next-line playwright/no-force-option
|
// eslint-disable-next-line playwright/no-force-option
|
||||||
force: true
|
force: true
|
||||||
});
|
});
|
||||||
@ -351,8 +352,8 @@ test.describe('Example Imagery Object', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Uses low fetch priority', async ({ page }) => {
|
test('Uses low fetch priority', async ({ page }) => {
|
||||||
const priority = await page.locator('.js-imageryView-image').getAttribute('fetchpriority');
|
const priority = page.locator('.js-imageryView-image');
|
||||||
expect(priority).toBe('low');
|
await expect(priority).toHaveAttribute('fetchpriority', 'low');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -362,7 +363,7 @@ test.describe('Example Imagery in Display Layout @clock', () => {
|
|||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
// We mock the clock so that we don't need to wait for time driven events
|
// We mock the clock so that we don't need to wait for time driven events
|
||||||
// to verify functionality.
|
// to verify functionality.
|
||||||
await page.clock.setSystemTime(MISSION_TIME);
|
await page.clock.install({ time: MISSION_TIME });
|
||||||
await page.clock.resume();
|
await page.clock.resume();
|
||||||
|
|
||||||
// Go to baseURL
|
// Go to baseURL
|
||||||
@ -447,6 +448,8 @@ test.describe('Example Imagery in Display Layout @clock', () => {
|
|||||||
await page.locator('div[title="Resize object width"] > input').click();
|
await page.locator('div[title="Resize object width"] > input').click();
|
||||||
await page.locator('div[title="Resize object width"] > input').fill('50');
|
await page.locator('div[title="Resize object width"] > input').fill('50');
|
||||||
|
|
||||||
|
await expect(page.getByLabel('Image Thumbnail from').last()).toBeInViewport();
|
||||||
|
|
||||||
await performImageryViewOperationsAndAssert(page, displayLayout);
|
await performImageryViewOperationsAndAssert(page, displayLayout);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -528,7 +531,7 @@ test.describe('Example Imagery in Flexible layout @clock', () => {
|
|||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
// We mock the clock so that we don't need to wait for time driven events
|
// We mock the clock so that we don't need to wait for time driven events
|
||||||
// to verify functionality.
|
// to verify functionality.
|
||||||
await page.clock.setSystemTime(MISSION_TIME);
|
await page.clock.install({ time: MISSION_TIME });
|
||||||
await page.clock.resume();
|
await page.clock.resume();
|
||||||
|
|
||||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
@ -543,6 +546,8 @@ test.describe('Example Imagery in Flexible layout @clock', () => {
|
|||||||
|
|
||||||
// Navigate back to Flexible Layout
|
// Navigate back to Flexible Layout
|
||||||
await page.goto(flexibleLayout.url);
|
await page.goto(flexibleLayout.url);
|
||||||
|
// Wait for image thumbnail auto-scroll to complete
|
||||||
|
await expect(page.getByLabel('Image Thumbnail from').last()).toBeInViewport();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Can double-click on the image to view large image', async ({ page }) => {
|
test('Can double-click on the image to view large image', async ({ page }) => {
|
||||||
@ -573,7 +578,7 @@ test.describe('Example Imagery in Tabs View @clock', () => {
|
|||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
// We mock the clock so that we don't need to wait for time driven events
|
// We mock the clock so that we don't need to wait for time driven events
|
||||||
// to verify functionality.
|
// to verify functionality.
|
||||||
await page.clock.setSystemTime(MISSION_TIME);
|
await page.clock.install({ time: MISSION_TIME });
|
||||||
await page.clock.resume();
|
await page.clock.resume();
|
||||||
|
|
||||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
@ -586,25 +591,21 @@ test.describe('Example Imagery in Tabs View @clock', () => {
|
|||||||
await page.getByRole('button', { name: 'Create' }).click();
|
await page.getByRole('button', { name: 'Create' }).click();
|
||||||
|
|
||||||
// Click text=Example Imagery
|
// Click text=Example Imagery
|
||||||
await page.click('li[role="menuitem"]:has-text("Example Imagery")');
|
await page.getByRole('menuitem', { name: 'Example Imagery' }).click();
|
||||||
|
|
||||||
// Clear and set Image load delay to minimum value
|
// Clear and set Image load delay to minimum value
|
||||||
await page.locator('input[type="number"]').clear();
|
await page.locator('input[type="number"]').clear();
|
||||||
await page.locator('input[type="number"]').fill(`${IMAGE_LOAD_DELAY}`);
|
await page.locator('input[type="number"]').fill(`${IMAGE_LOAD_DELAY}`);
|
||||||
|
|
||||||
// Click text=OK
|
await page.getByLabel('Save').click();
|
||||||
await Promise.all([
|
|
||||||
page.waitForNavigation({ waitUntil: 'networkidle' }),
|
|
||||||
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(
|
await expect(page.locator('.l-browse-bar__object-name')).toContainText(
|
||||||
'Unnamed Example Imagery'
|
'Unnamed Example Imagery'
|
||||||
);
|
);
|
||||||
|
|
||||||
await page.goto(tabsView.url);
|
await page.goto(tabsView.url);
|
||||||
|
// Wait for image thumbnail auto-scroll to complete
|
||||||
|
await expect(page.getByLabel('Image Thumbnail from').last()).toBeInViewport();
|
||||||
});
|
});
|
||||||
test('Imagery View operations @clock', async ({ page }) => {
|
test('Imagery View operations @clock', async ({ page }) => {
|
||||||
await performImageryViewOperationsAndAssert(page, tabsView);
|
await performImageryViewOperationsAndAssert(page, tabsView);
|
||||||
@ -836,8 +837,7 @@ async function assertBackgroundImageUrlFromBackgroundCss(page) {
|
|||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
*/
|
*/
|
||||||
async function panZoomAndAssertImageProperties(page) {
|
async function panZoomAndAssertImageProperties(page) {
|
||||||
const imageryHintsText = await page.locator('.c-imagery__hints').innerText();
|
await expect(page.locator('.c-imagery__hints')).toContainText(expectedAltText);
|
||||||
expect(expectedAltText).toEqual(imageryHintsText);
|
|
||||||
const zoomedBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
const zoomedBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||||
const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2;
|
const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2;
|
||||||
const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
|
const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
|
||||||
@ -1056,7 +1056,6 @@ async function createImageryViewWithShortDelay(page, { name, parent }) {
|
|||||||
/**
|
/**
|
||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line require-await
|
|
||||||
async function waitForZoomAndPanTransitions(page) {
|
async function waitForZoomAndPanTransitions(page) {
|
||||||
// Wait for image to stabilize
|
// Wait for image to stabilize
|
||||||
await page.getByLabel('Focused Image Element').hover({ trial: true });
|
await page.getByLabel('Focused Image Element').hover({ trial: true });
|
||||||
|
@ -26,10 +26,7 @@ This test suite is dedicated to tests which verify the basic operations surround
|
|||||||
|
|
||||||
import fs from 'fs/promises';
|
import fs from 'fs/promises';
|
||||||
|
|
||||||
import {
|
import { createDomainObjectWithDefaults } from '../../../../appActions.js';
|
||||||
createDomainObjectWithDefaults,
|
|
||||||
openObjectTreeContextMenu
|
|
||||||
} from '../../../../appActions.js';
|
|
||||||
import { expect, test } from '../../../../baseFixtures.js';
|
import { expect, test } from '../../../../baseFixtures.js';
|
||||||
import { navigateToFaultManagementWithExample } from '../../../../helper/faultUtils.js';
|
import { navigateToFaultManagementWithExample } from '../../../../helper/faultUtils.js';
|
||||||
|
|
||||||
@ -51,7 +48,10 @@ test.describe('ExportAsJSON', () => {
|
|||||||
await page.goto(folder.url);
|
await page.goto(folder.url);
|
||||||
|
|
||||||
// Open context menu and initiate download
|
// Open context menu and initiate download
|
||||||
await openObjectTreeContextMenu(page, folder.url);
|
await page.getByLabel('Show selected item in tree').click();
|
||||||
|
await page.getByRole('treeitem', { name: 'Expand e2e folder folder' }).click({
|
||||||
|
button: 'right'
|
||||||
|
});
|
||||||
const [download] = await Promise.all([
|
const [download] = await Promise.all([
|
||||||
page.waitForEvent('download'), // Waits for the download event
|
page.waitForEvent('download'), // Waits for the download event
|
||||||
page.getByLabel('Export as JSON').click() // Triggers the download
|
page.getByLabel('Export as JSON').click() // Triggers the download
|
||||||
@ -105,7 +105,12 @@ test.describe('ExportAsJSON', () => {
|
|||||||
await page.goto(timer.url);
|
await page.goto(timer.url);
|
||||||
|
|
||||||
//do this against parent folder.url, NOT timer.url child
|
//do this against parent folder.url, NOT timer.url child
|
||||||
await openObjectTreeContextMenu(page, folder.url);
|
// Open context menu and initiate download
|
||||||
|
await page.getByLabel('Show selected item in tree').click();
|
||||||
|
await page.getByRole('treeitem', { name: 'Collapse e2e folder folder' }).click({
|
||||||
|
button: 'right'
|
||||||
|
});
|
||||||
|
|
||||||
// Open context menu and initiate download
|
// Open context menu and initiate download
|
||||||
const [download] = await Promise.all([
|
const [download] = await Promise.all([
|
||||||
page.waitForEvent('download'), // Waits for the download event
|
page.waitForEvent('download'), // Waits for the download event
|
||||||
@ -141,18 +146,18 @@ test.describe('ExportAsJSON Disabled Actions', () => {
|
|||||||
});
|
});
|
||||||
test('Verify that the ExportAsJSON dropdown does not appear for the item X', async ({ page }) => {
|
test('Verify that the ExportAsJSON dropdown does not appear for the item X', async ({ page }) => {
|
||||||
await page.getByLabel('More actions').click();
|
await page.getByLabel('More actions').click();
|
||||||
await expect(await page.getByLabel('Export as JSON')).toHaveCount(0);
|
await expect(page.getByLabel('Export as JSON')).toHaveCount(0);
|
||||||
|
|
||||||
await page.getByRole('treeitem', { name: 'Fault Management' }).click({
|
await page.getByRole('treeitem', { name: 'Fault Management' }).click({
|
||||||
button: 'right'
|
button: 'right'
|
||||||
});
|
});
|
||||||
await expect(await page.getByLabel('Export as JSON')).toHaveCount(0);
|
await expect(page.getByLabel('Export as JSON')).toHaveCount(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
test.describe('ExportAsJSON ProgressBar @couchdb', () => {
|
test.describe('ExportAsJSON ProgressBar @couchdb', () => {
|
||||||
let folder;
|
let folder;
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await page.goto('./', { waitUntil: 'networkidle' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
// Perform actions to create the domain object
|
// Perform actions to create the domain object
|
||||||
folder = await createDomainObjectWithDefaults(page, {
|
folder = await createDomainObjectWithDefaults(page, {
|
||||||
type: 'Folder'
|
type: 'Folder'
|
||||||
|
@ -36,7 +36,7 @@ test.describe('Testing numeric data with inspector data visualization (i.e., dat
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Can click on telemetry and see data in inspector @2p', async ({ page, context }) => {
|
test('Can click on telemetry and see data in inspector @2p', async ({ page, context }) => {
|
||||||
const initStartBounds = await page.getByLabel('Start bounds').textContent();
|
const initStartBounds = page.getByLabel('Start bounds');
|
||||||
const initEndBounds = await page.getByLabel('End bounds').textContent();
|
const initEndBounds = await page.getByLabel('End bounds').textContent();
|
||||||
const exampleDataVisualizationSource = await createDomainObjectWithDefaults(page, {
|
const exampleDataVisualizationSource = await createDomainObjectWithDefaults(page, {
|
||||||
type: 'Example Data Visualization Source'
|
type: 'Example Data Visualization Source'
|
||||||
@ -81,7 +81,9 @@ test.describe('Testing numeric data with inspector data visualization (i.e., dat
|
|||||||
await expect(newPage).toHaveTitle('Second Sine Wave Generator');
|
await expect(newPage).toHaveTitle('Second Sine Wave Generator');
|
||||||
|
|
||||||
// Verify that "Open in New Tab" preserves the time bounds
|
// Verify that "Open in New Tab" preserves the time bounds
|
||||||
expect(initStartBounds).toEqual(await newPage.getByLabel('Start bounds').textContent());
|
await expect(initStartBounds).toHaveText(
|
||||||
|
await newPage.getByLabel('Start bounds').textContent()
|
||||||
|
);
|
||||||
expect(initEndBounds).toEqual(await newPage.getByLabel('End bounds').textContent());
|
expect(initEndBounds).toEqual(await newPage.getByLabel('End bounds').textContent());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
createDomainObjectWithDefaults,
|
createDomainObjectWithDefaults,
|
||||||
openObjectTreeContextMenu,
|
navigateToObjectWithRealTime,
|
||||||
setFixedTimeMode,
|
setFixedTimeMode,
|
||||||
setRealTimeMode,
|
setRealTimeMode,
|
||||||
setStartOffset
|
setStartOffset
|
||||||
@ -56,61 +56,61 @@ test.describe('Testing LAD table configuration', () => {
|
|||||||
await page.getByRole('tab', { name: 'LAD Table Configuration' }).click();
|
await page.getByRole('tab', { name: 'LAD Table Configuration' }).click();
|
||||||
|
|
||||||
// make sure headers are visible initially
|
// make sure headers are visible initially
|
||||||
await expect(page.getByRole('cell', { name: 'Timestamp', exact: true })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Timestamp', exact: true })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'Units' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Units' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'Type', exact: true })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Type', exact: true })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'WATCH' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit WATCH' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit WARNING' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit DISTRESS' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit CRITICAL' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit SEVERE' })).toBeVisible();
|
||||||
|
|
||||||
// hide timestamp column
|
// hide timestamp column
|
||||||
await page.getByLabel('Timestamp', { exact: true }).uncheck();
|
await page.getByLabel('Timestamp', { exact: true }).uncheck();
|
||||||
await expect(page.getByRole('cell', { name: 'Timestamp', exact: true })).toBeHidden();
|
await expect(page.getByRole('columnheader', { name: 'Timestamp', exact: true })).toBeHidden();
|
||||||
await expect(page.getByRole('cell', { name: 'Units' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Units' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'Type', exact: true })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Type', exact: true })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'WATCH' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit WATCH' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit WARNING' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit DISTRESS' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit CRITICAL' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit SEVERE' })).toBeVisible();
|
||||||
|
|
||||||
// hide units & type column
|
// hide units & type column
|
||||||
await page.getByLabel('Units').uncheck();
|
await page.getByLabel('Units').uncheck();
|
||||||
await page.getByLabel('Type', { exact: true }).uncheck();
|
await page.getByLabel('Type', { exact: true }).uncheck();
|
||||||
await expect(page.getByRole('cell', { name: 'Timestamp', exact: true })).toBeHidden();
|
await expect(page.getByRole('columnheader', { name: 'Timestamp', exact: true })).toBeHidden();
|
||||||
await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden();
|
await expect(page.getByRole('columnheader', { name: 'Units' })).toBeHidden();
|
||||||
await expect(page.getByRole('cell', { name: 'Type', exact: true })).toBeHidden();
|
await expect(page.getByRole('columnheader', { name: 'Type', exact: true })).toBeHidden();
|
||||||
await expect(page.getByRole('cell', { name: 'WATCH' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit WATCH' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit WARNING' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit DISTRESS' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit CRITICAL' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit SEVERE' })).toBeVisible();
|
||||||
|
|
||||||
// hide WATCH column
|
// hide WATCH column
|
||||||
await page.getByLabel('WATCH').uncheck();
|
await page.getByLabel('WATCH').uncheck();
|
||||||
await expect(page.getByRole('cell', { name: 'Timestamp', exact: true })).toBeHidden();
|
await expect(page.getByRole('columnheader', { name: 'Timestamp', exact: true })).toBeHidden();
|
||||||
await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden();
|
await expect(page.getByRole('columnheader', { name: 'Units' })).toBeHidden();
|
||||||
await expect(page.getByRole('cell', { name: 'Type', exact: true })).toBeHidden();
|
await expect(page.getByRole('columnheader', { name: 'Type', exact: true })).toBeHidden();
|
||||||
await expect(page.getByRole('cell', { name: 'WATCH' })).toBeHidden();
|
await expect(page.getByRole('columnheader', { name: 'Limit WATCH' })).toBeHidden();
|
||||||
await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit WARNING' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit DISTRESS' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit CRITICAL' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit SEVERE' })).toBeVisible();
|
||||||
|
|
||||||
// save and reload and verify they columns are still hidden
|
// save and reload and verify they columns are still hidden
|
||||||
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.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
await page.reload();
|
await page.reload();
|
||||||
await expect(page.getByRole('cell', { name: 'Timestamp', exact: true })).toBeHidden();
|
await expect(page.getByRole('columnheader', { name: 'Timestamp', exact: true })).toBeHidden();
|
||||||
await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden();
|
await expect(page.getByRole('columnheader', { name: 'Units' })).toBeHidden();
|
||||||
await expect(page.getByRole('cell', { name: 'Type', exact: true })).toBeHidden();
|
await expect(page.getByRole('columnheader', { name: 'Type', exact: true })).toBeHidden();
|
||||||
await expect(page.getByRole('cell', { name: 'WATCH' })).toBeHidden();
|
await expect(page.getByRole('columnheader', { name: 'Limit WATCH' })).toBeHidden();
|
||||||
await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit WARNING' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit DISTRESS' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit CRITICAL' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit SEVERE' })).toBeVisible();
|
||||||
|
|
||||||
// Edit LAD table
|
// Edit LAD table
|
||||||
await page.getByLabel('Edit Object').click();
|
await page.getByLabel('Edit Object').click();
|
||||||
@ -118,27 +118,27 @@ test.describe('Testing LAD table configuration', () => {
|
|||||||
|
|
||||||
// show timestamp column
|
// show timestamp column
|
||||||
await page.getByLabel('Timestamp', { exact: true }).check();
|
await page.getByLabel('Timestamp', { exact: true }).check();
|
||||||
await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden();
|
await expect(page.getByRole('columnheader', { name: 'Units' })).toBeHidden();
|
||||||
await expect(page.getByRole('cell', { name: 'Type', exact: true })).toBeHidden();
|
await expect(page.getByRole('columnheader', { name: 'Type', exact: true })).toBeHidden();
|
||||||
await expect(page.getByRole('cell', { name: 'Timestamp', exact: true })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Timestamp', exact: true })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'WATCH' })).toBeHidden();
|
await expect(page.getByRole('columnheader', { name: 'Limit WATCH' })).toBeHidden();
|
||||||
await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit WARNING' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit DISTRESS' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit CRITICAL' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit SEVERE' })).toBeVisible();
|
||||||
|
|
||||||
// save and reload and make sure timestamp is still visible
|
// save and reload and make sure timestamp is still visible
|
||||||
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.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
await page.reload();
|
await page.reload();
|
||||||
await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden();
|
await expect(page.getByRole('columnheader', { name: 'Units' })).toBeHidden();
|
||||||
await expect(page.getByRole('cell', { name: 'Type', exact: true })).toBeHidden();
|
await expect(page.getByRole('columnheader', { name: 'Type', exact: true })).toBeHidden();
|
||||||
await expect(page.getByRole('cell', { name: 'Timestamp', exact: true })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Timestamp', exact: true })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'WATCH' })).toBeHidden();
|
await expect(page.getByRole('columnheader', { name: 'Limit WATCH' })).toBeHidden();
|
||||||
await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit WARNING' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit DISTRESS' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit CRITICAL' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit SEVERE' })).toBeVisible();
|
||||||
|
|
||||||
// Edit LAD table
|
// Edit LAD table
|
||||||
await page.getByLabel('Edit Object').click();
|
await page.getByLabel('Edit Object').click();
|
||||||
@ -148,27 +148,27 @@ test.describe('Testing LAD table configuration', () => {
|
|||||||
await page.getByLabel('Units').check();
|
await page.getByLabel('Units').check();
|
||||||
await page.getByLabel('Type', { exact: true }).check();
|
await page.getByLabel('Type', { exact: true }).check();
|
||||||
await page.getByLabel('WATCH').check();
|
await page.getByLabel('WATCH').check();
|
||||||
await expect(page.getByRole('cell', { name: 'Timestamp', exact: true })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Timestamp', exact: true })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'Units' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Units' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'Type', exact: true })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Type', exact: true })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'WATCH' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit WATCH' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit WARNING' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit DISTRESS' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit CRITICAL' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit SEVERE' })).toBeVisible();
|
||||||
|
|
||||||
// save and reload and make sure all columns are still visible
|
// save and reload and make sure all columns are still visible
|
||||||
await page.locator('button[title="Save"]').click();
|
await page.locator('button[title="Save"]').click();
|
||||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
await page.reload();
|
await page.reload();
|
||||||
await expect(page.getByRole('cell', { name: 'Timestamp', exact: true })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Timestamp', exact: true })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'Units' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Units' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'Type', exact: true })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Type', exact: true })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'WATCH' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit WATCH' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit WARNING' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit DISTRESS' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit CRITICAL' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit SEVERE' })).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('When adding something without Units, do not show Units column', async ({ page }) => {
|
test('When adding something without Units, do not show Units column', async ({ page }) => {
|
||||||
@ -185,14 +185,14 @@ test.describe('Testing LAD table configuration', () => {
|
|||||||
await page.getByRole('tab', { name: 'LAD Table Configuration' }).click();
|
await page.getByRole('tab', { name: 'LAD Table Configuration' }).click();
|
||||||
|
|
||||||
// make sure Sine Wave headers are visible initially too
|
// make sure Sine Wave headers are visible initially too
|
||||||
await expect(page.getByRole('cell', { name: 'Timestamp', exact: true })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Timestamp', exact: true })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'Units' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Units' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'Type', exact: true })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Type', exact: true })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'WATCH' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit WATCH' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'WARNING' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit WARNING' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit DISTRESS' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit CRITICAL' })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Limit SEVERE' })).toBeVisible();
|
||||||
|
|
||||||
// save and reload and verify they columns are still hidden
|
// save and reload and verify they columns are still hidden
|
||||||
await page.getByLabel('Save').click();
|
await page.getByLabel('Save').click();
|
||||||
@ -201,25 +201,25 @@ test.describe('Testing LAD table configuration', () => {
|
|||||||
// Remove Sine Wave Generator
|
// Remove Sine Wave Generator
|
||||||
openObjectTreeContextMenu(page, sineWaveObject.url);
|
openObjectTreeContextMenu(page, sineWaveObject.url);
|
||||||
await page.getByRole('menuitem', { name: /Remove/ }).click();
|
await page.getByRole('menuitem', { name: /Remove/ }).click();
|
||||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
await page.getByRole('button', { name: 'Ok', exact: true }).click();
|
||||||
|
|
||||||
// Ensure Units & Limit columns are gone
|
// Ensure Units & Limit columns are gone
|
||||||
// as Event Generator don't have them
|
// as Event Generator don't have them
|
||||||
await page.goto(ladTable.url);
|
await page.goto(ladTable.url);
|
||||||
await expect(page.getByRole('cell', { name: 'Timestamp', exact: true })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Timestamp', exact: true })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'Type', exact: true })).toBeVisible();
|
await expect(page.getByRole('columnheader', { name: 'Type', exact: true })).toBeVisible();
|
||||||
await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden();
|
await expect(page.getByRole('columnheader', { name: 'Units' })).toBeHidden();
|
||||||
await expect(page.getByRole('cell', { name: 'WATCH' })).toBeHidden();
|
await expect(page.getByRole('columnheader', { name: 'Limit WATCH' })).toBeHidden();
|
||||||
await expect(page.getByRole('cell', { name: 'WARNING' })).toBeHidden();
|
await expect(page.getByRole('columnheader', { name: 'Limit WARNING' })).toBeHidden();
|
||||||
await expect(page.getByRole('cell', { name: 'DISTRESS' })).toBeHidden();
|
await expect(page.getByRole('columnheader', { name: 'Limit DISTRESS' })).toBeHidden();
|
||||||
await expect(page.getByRole('cell', { name: 'CRITICAL' })).toBeHidden();
|
await expect(page.getByRole('columnheader', { name: 'Limit CRITICAL' })).toBeHidden();
|
||||||
await expect(page.getByRole('cell', { name: 'SEVERE' })).toBeHidden();
|
await expect(page.getByRole('columnheader', { name: 'Limit SEVERE' })).toBeHidden();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("LAD Tables don't allow selection of rows but does show context click menus", async ({
|
test("LAD Tables don't allow selection of rows but does show context click menus", async ({
|
||||||
page
|
page
|
||||||
}) => {
|
}) => {
|
||||||
const cell = await page.locator('.js-first-data');
|
const cell = page.locator('.js-first-data');
|
||||||
const userSelectable = await cell.evaluate((el) => {
|
const userSelectable = await cell.evaluate((el) => {
|
||||||
return window.getComputedStyle(el).getPropertyValue('user-select');
|
return window.getComputedStyle(el).getPropertyValue('user-select');
|
||||||
});
|
});
|
||||||
@ -237,19 +237,21 @@ test.describe('Testing LAD table configuration', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe('Testing LAD table @unstable', () => {
|
test.describe('Testing LAD table', () => {
|
||||||
let sineWaveObject;
|
let sineWaveObject;
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
await setRealTimeMode(page);
|
|
||||||
|
|
||||||
// Create Sine Wave Generator
|
// Create Sine Wave Generator
|
||||||
sineWaveObject = await createDomainObjectWithDefaults(page, {
|
sineWaveObject = await createDomainObjectWithDefaults(page, {
|
||||||
type: 'Sine Wave Generator',
|
type: 'Sine Wave Generator',
|
||||||
name: 'Test Sine Wave Generator'
|
name: 'Test Sine Wave Generator'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Switch to real time mode by navigating directly to the URL
|
||||||
|
await navigateToObjectWithRealTime(page, sineWaveObject.url);
|
||||||
});
|
});
|
||||||
test('telemetry value exactly matches latest telemetry value received in real time', async ({
|
test('telemetry value exactly matches latest telemetry value received in realtime mode', async ({
|
||||||
page
|
page
|
||||||
}) => {
|
}) => {
|
||||||
// Create LAD table
|
// Create LAD table
|
||||||
@ -261,23 +263,23 @@ test.describe('Testing LAD table @unstable', () => {
|
|||||||
await page.getByLabel('Edit Object').click();
|
await page.getByLabel('Edit Object').click();
|
||||||
|
|
||||||
// Expand the 'My Items' folder in the left tree
|
// Expand the 'My Items' folder in the left tree
|
||||||
await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click();
|
await page.getByLabel('Expand My Items').click();
|
||||||
// Add the Sine Wave Generator to the LAD table and save changes
|
// Add the Sine Wave Generator to the LAD table and save changes
|
||||||
await page.dragAndDrop('text=Test Sine Wave Generator', '.c-lad-table-wrapper');
|
await page.getByLabel('Preview Test Sine Wave').dragTo(page.locator('#lad-table-drop-area'));
|
||||||
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.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
|
|
||||||
// Subscribe to the Sine Wave Generator data
|
// Subscribe to the Sine Wave Generator data
|
||||||
// On getting data, check if the value found in the LAD table is the most recent value
|
// On getting data, check if the value found in the LAD table is the most recent value
|
||||||
// from the Sine Wave Generator
|
// from the Sine Wave Generator
|
||||||
const getTelemValuePromise = await subscribeToTelemetry(page, sineWaveObject.uuid);
|
const getTelemValuePromise = subscribeToTelemetry(page, sineWaveObject.uuid);
|
||||||
const subscribeTelemValue = await getTelemValuePromise;
|
const subscribeTelemValue = await getTelemValuePromise;
|
||||||
const ladTableValuePromise = await page.waitForSelector(`text="${subscribeTelemValue}"`);
|
await expect(page.getByLabel('lad value')).toHaveText(subscribeTelemValue);
|
||||||
const ladTableValue = await ladTableValuePromise.textContent();
|
const ladTableValue = await page.getByText(subscribeTelemValue).textContent();
|
||||||
|
|
||||||
expect(ladTableValue).toBe(subscribeTelemValue);
|
expect(ladTableValue).toEqual(subscribeTelemValue);
|
||||||
});
|
});
|
||||||
test('telemetry value exactly matches latest telemetry value received in fixed time', async ({
|
test('telemetry value exactly matches latest telemetry value received in fixed time mode', async ({
|
||||||
page
|
page
|
||||||
}) => {
|
}) => {
|
||||||
// Create LAD table
|
// Create LAD table
|
||||||
@ -289,25 +291,23 @@ test.describe('Testing LAD table @unstable', () => {
|
|||||||
await page.getByLabel('Edit Object').click();
|
await page.getByLabel('Edit Object').click();
|
||||||
|
|
||||||
// Expand the 'My Items' folder in the left tree
|
// Expand the 'My Items' folder in the left tree
|
||||||
await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click();
|
await page.getByLabel('Expand My Items').click();
|
||||||
// Add the Sine Wave Generator to the LAD table and save changes
|
// Add the Sine Wave Generator to the LAD table and save changes
|
||||||
await page.dragAndDrop('text=Test Sine Wave Generator', '.c-lad-table-wrapper');
|
await page.getByLabel('Preview Test Sine Wave').dragTo(page.locator('#lad-table-drop-area'));
|
||||||
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.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
|
|
||||||
// Subscribe to the Sine Wave Generator data
|
// Subscribe to the Sine Wave Generator data
|
||||||
const getTelemValuePromise = await subscribeToTelemetry(page, sineWaveObject.uuid);
|
const getTelemValuePromise = subscribeToTelemetry(page, sineWaveObject.uuid);
|
||||||
// Set an offset of 1 minute and then change the time mode to fixed to set a 1 minute historical window
|
// Set an offset of 1 minute and then change the time mode to fixed to set a 1 minute historical window
|
||||||
await setStartOffset(page, { mins: '1' });
|
await setRealTimeMode(page);
|
||||||
|
await setStartOffset(page, { startMins: '01' });
|
||||||
await setFixedTimeMode(page);
|
await setFixedTimeMode(page);
|
||||||
|
|
||||||
// On getting data, check if the value found in the LAD table is the most recent value
|
// On getting data, check if the value found in the LAD table is the most recent value
|
||||||
// from the Sine Wave Generator
|
// from the Sine Wave Generator
|
||||||
const subscribeTelemValue = await getTelemValuePromise;
|
const subscribeTelemValue = await getTelemValuePromise;
|
||||||
const ladTableValuePromise = await page.waitForSelector(`text="${subscribeTelemValue}"`);
|
await expect(page.getByLabel('lad value')).toHaveText(subscribeTelemValue);
|
||||||
const ladTableValue = await ladTableValuePromise.textContent();
|
|
||||||
|
|
||||||
expect(ladTableValue).toBe(subscribeTelemValue);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -338,3 +338,18 @@ async function subscribeToTelemetry(page, objectIdentifier) {
|
|||||||
|
|
||||||
return getTelemValuePromise;
|
return getTelemValuePromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the given `domainObject`'s context menu from the object tree.
|
||||||
|
* Expands the path to the object and scrolls to it if necessary.
|
||||||
|
*
|
||||||
|
* @param {import('@playwright/test').Page} page
|
||||||
|
* @param {string} url the url to the object
|
||||||
|
*/
|
||||||
|
async function openObjectTreeContextMenu(page, url) {
|
||||||
|
await page.goto(url);
|
||||||
|
await page.getByLabel('Show selected item in tree').click();
|
||||||
|
await page.locator('.is-navigated-object').click({
|
||||||
|
button: 'right'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -76,7 +76,7 @@ test.describe('LAD Table', () => {
|
|||||||
// Right-click the SWG treeitem context menu and click 'Remove' and confirm
|
// Right-click the SWG treeitem context menu and click 'Remove' and confirm
|
||||||
await page.getByRole('treeitem', { name: swg.name }).click({ button: 'right' });
|
await page.getByRole('treeitem', { name: swg.name }).click({ button: 'right' });
|
||||||
await page.getByRole('menuitem', { name: 'Remove' }).click();
|
await page.getByRole('menuitem', { name: 'Remove' }).click();
|
||||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
await page.getByRole('button', { name: 'Ok', exact: true }).click();
|
||||||
|
|
||||||
// Assert that the SWG is no longer in the tree and the table is empty
|
// Assert that the SWG is no longer in the tree and the table is empty
|
||||||
await expect(page.getByRole('treeitem', { name: swg.name })).toBeHidden();
|
await expect(page.getByRole('treeitem', { name: swg.name })).toBeHidden();
|
||||||
|
@ -259,7 +259,7 @@ test.describe('Notebook export tests', () => {
|
|||||||
const exportedText = await streamToString(readStream);
|
const exportedText = await streamToString(readStream);
|
||||||
expect(exportedText).toContain('Foo bar entry');
|
expect(exportedText).toContain('Foo bar entry');
|
||||||
});
|
});
|
||||||
test.fixme('can export multiple notebook entries as text ', async ({ page }) => {});
|
test.fixme('can export multiple notebook entries as text', async ({ page }) => {});
|
||||||
test.fixme('can export all notebook entry metdata', async ({ page }) => {});
|
test.fixme('can export all notebook entry metdata', async ({ page }) => {});
|
||||||
test.fixme('can export all notebook tags', async ({ page }) => {});
|
test.fixme('can export all notebook tags', async ({ page }) => {});
|
||||||
test.fixme('can export all notebook snapshots', async ({ page }) => {});
|
test.fixme('can export all notebook snapshots', async ({ page }) => {});
|
||||||
@ -296,7 +296,7 @@ test.describe('Notebook entry tests', () => {
|
|||||||
await expect(page.getByLabel('Notebook Entry Input')).toBeVisible();
|
await expect(page.getByLabel('Notebook Entry Input')).toBeVisible();
|
||||||
await expect(page.getByLabel('Notebook Entry', { exact: true })).toHaveClass(/is-selected/);
|
await expect(page.getByLabel('Notebook Entry', { exact: true })).toHaveClass(/is-selected/);
|
||||||
});
|
});
|
||||||
test('When an object is dropped into a notebook, a new entry is created and it should be focused @unstable', async ({
|
test('When an object is dropped into a notebook, a new entry is created and it should be focused', async ({
|
||||||
page
|
page
|
||||||
}) => {
|
}) => {
|
||||||
// Create Overlay Plot
|
// Create Overlay Plot
|
||||||
@ -320,7 +320,7 @@ test.describe('Notebook entry tests', () => {
|
|||||||
await expect(embed).toHaveClass(/icon-plot-overlay/);
|
await expect(embed).toHaveClass(/icon-plot-overlay/);
|
||||||
expect(embedName).toBe(overlayPlot.name);
|
expect(embedName).toBe(overlayPlot.name);
|
||||||
});
|
});
|
||||||
test('When an object is dropped into a notebooks existing entry, it should be focused @unstable', async ({
|
test('When an object is dropped into a notebooks existing entry, it should be focused', async ({
|
||||||
page
|
page
|
||||||
}) => {
|
}) => {
|
||||||
// Create Overlay Plot
|
// Create Overlay Plot
|
||||||
@ -354,19 +354,19 @@ test.describe('Notebook entry tests', () => {
|
|||||||
await page.goto(notebookObject.url);
|
await page.goto(notebookObject.url);
|
||||||
|
|
||||||
await nbUtils.enterTextEntry(page, 'First Entry');
|
await nbUtils.enterTextEntry(page, 'First Entry');
|
||||||
await page.hover('text="First Entry"');
|
await page.getByLabel('Notebook Entry', { exact: true }).hover();
|
||||||
await page.click('button[title="Delete this entry"]');
|
await page.getByLabel('Delete this entry').click();
|
||||||
await page.getByRole('button', { name: 'Ok' }).filter({ hasText: 'Ok' }).click();
|
await page.getByRole('button', { name: 'Ok', exact: true }).click();
|
||||||
await expect(page.locator('text="First Entry"')).toBeHidden();
|
await expect(page.getByText('First Entry')).toBeHidden();
|
||||||
await nbUtils.enterTextEntry(page, 'Another First Entry');
|
await nbUtils.enterTextEntry(page, 'Another First Entry');
|
||||||
await nbUtils.enterTextEntry(page, 'Second Entry');
|
await nbUtils.enterTextEntry(page, 'Second Entry');
|
||||||
await nbUtils.enterTextEntry(page, 'Third Entry');
|
await nbUtils.enterTextEntry(page, 'Third Entry');
|
||||||
await page.hover('[aria-label="Notebook Entry"] >> nth=2');
|
await page.getByLabel('Notebook Entry', { exact: true }).nth(2).hover();
|
||||||
await page.click('button[title="Delete this entry"] >> nth=2');
|
await page.getByLabel('Delete this entry').nth(2).click();
|
||||||
await page.getByRole('button', { name: 'Ok' }).filter({ hasText: 'Ok' }).click();
|
await page.getByRole('button', { name: 'Ok', exact: true }).click();
|
||||||
await expect(page.locator('text="Third Entry"')).toBeHidden();
|
await expect(page.getByText('Third Entry')).toBeHidden();
|
||||||
await expect(page.locator('text="Another First Entry"')).toBeVisible();
|
await expect(page.getByText('Another First Entry')).toBeVisible();
|
||||||
await expect(page.locator('text="Second Entry"')).toBeVisible();
|
await expect(page.getByText('Second Entry')).toBeVisible();
|
||||||
});
|
});
|
||||||
test('when a valid link is entered into a notebook entry, it becomes clickable when viewing', async ({
|
test('when a valid link is entered into a notebook entry, it becomes clickable when viewing', async ({
|
||||||
page
|
page
|
||||||
@ -383,7 +383,7 @@ test.describe('Notebook entry tests', () => {
|
|||||||
|
|
||||||
const validLink = page.locator(`a[href="${TEST_LINK}"]`);
|
const validLink = page.locator(`a[href="${TEST_LINK}"]`);
|
||||||
|
|
||||||
expect(await validLink.count()).toBe(1);
|
await expect(validLink).toHaveCount(1);
|
||||||
|
|
||||||
// Start waiting for popup before clicking. Note no await.
|
// Start waiting for popup before clicking. Note no await.
|
||||||
const popupPromise = page.waitForEvent('popup');
|
const popupPromise = page.waitForEvent('popup');
|
||||||
@ -410,7 +410,7 @@ test.describe('Notebook entry tests', () => {
|
|||||||
|
|
||||||
const invalidLink = page.locator(`a[href="${TEST_LINK}"]`);
|
const invalidLink = page.locator(`a[href="${TEST_LINK}"]`);
|
||||||
|
|
||||||
expect(await invalidLink.count()).toBe(0);
|
await expect(invalidLink).toHaveCount(0);
|
||||||
});
|
});
|
||||||
test('when a link is entered, but it is not in the whitelisted urls, it does not become clickable when viewing', async ({
|
test('when a link is entered, but it is not in the whitelisted urls, it does not become clickable when viewing', async ({
|
||||||
page
|
page
|
||||||
@ -427,7 +427,7 @@ test.describe('Notebook entry tests', () => {
|
|||||||
|
|
||||||
const invalidLink = page.locator(`a[href="${TEST_LINK}"]`);
|
const invalidLink = page.locator(`a[href="${TEST_LINK}"]`);
|
||||||
|
|
||||||
expect(await invalidLink.count()).toBe(0);
|
await expect(invalidLink).toHaveCount(0);
|
||||||
});
|
});
|
||||||
test('when a valid link with a subdomain and a valid domain in the whitelisted urls is entered into a notebook entry, it becomes clickable when viewing', async ({
|
test('when a valid link with a subdomain and a valid domain in the whitelisted urls is entered into a notebook entry, it becomes clickable when viewing', async ({
|
||||||
page
|
page
|
||||||
@ -444,7 +444,7 @@ test.describe('Notebook entry tests', () => {
|
|||||||
|
|
||||||
const validLink = page.locator(`a[href="${INVALID_TEST_LINK}"]`);
|
const validLink = page.locator(`a[href="${INVALID_TEST_LINK}"]`);
|
||||||
|
|
||||||
expect(await validLink.count()).toBe(1);
|
await expect(validLink).toHaveCount(1);
|
||||||
});
|
});
|
||||||
test('when a valid secure link is entered into a notebook entry, it becomes clickable when viewing', async ({
|
test('when a valid secure link is entered into a notebook entry, it becomes clickable when viewing', async ({
|
||||||
page
|
page
|
||||||
@ -461,7 +461,7 @@ test.describe('Notebook entry tests', () => {
|
|||||||
|
|
||||||
const validLink = page.locator(`a[href="${TEST_LINK}"]`);
|
const validLink = page.locator(`a[href="${TEST_LINK}"]`);
|
||||||
|
|
||||||
expect(await validLink.count()).toBe(1);
|
await expect(validLink).toHaveCount(1);
|
||||||
|
|
||||||
// Start waiting for popup before clicking. Note no await.
|
// Start waiting for popup before clicking. Note no await.
|
||||||
const popupPromise = page.waitForEvent('popup');
|
const popupPromise = page.waitForEvent('popup');
|
||||||
@ -494,7 +494,7 @@ test.describe('Notebook entry tests', () => {
|
|||||||
const unsanitizedLink = page.locator(`a[href="${TEST_LINK_BAD}"]`);
|
const unsanitizedLink = page.locator(`a[href="${TEST_LINK_BAD}"]`);
|
||||||
|
|
||||||
expect.soft(await sanitizedLink.count()).toBe(1);
|
expect.soft(await sanitizedLink.count()).toBe(1);
|
||||||
expect(await unsanitizedLink.count()).toBe(0);
|
await expect(unsanitizedLink).toHaveCount(0);
|
||||||
});
|
});
|
||||||
test('Can add markdown to a notebook entry', async ({ page }) => {
|
test('Can add markdown to a notebook entry', async ({ page }) => {
|
||||||
await page.goto(notebookObject.url);
|
await page.goto(notebookObject.url);
|
||||||
|
@ -76,7 +76,7 @@ test.describe('Snapshot image tests', () => {
|
|||||||
const secondThumbnail = page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).nth(1);
|
const secondThumbnail = page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).nth(1);
|
||||||
await secondThumbnail.waitFor({ state: 'attached' });
|
await secondThumbnail.waitFor({ state: 'attached' });
|
||||||
// expect two embedded images now
|
// expect two embedded images now
|
||||||
expect(await page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).count()).toBe(2);
|
await expect(page.getByRole('img', { name: 'favicon-96x96.png thumbnail' })).toHaveCount(2);
|
||||||
|
|
||||||
await page.locator('.c-snapshot.c-ne__embed').first().getByTitle('More actions').click();
|
await page.locator('.c-snapshot.c-ne__embed').first().getByTitle('More actions').click();
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ test.describe('Snapshot image tests', () => {
|
|||||||
await secondThumbnail.waitFor({ state: 'detached' });
|
await secondThumbnail.waitFor({ state: 'detached' });
|
||||||
|
|
||||||
// expect one embedded image now as we deleted the other
|
// expect one embedded image now as we deleted the other
|
||||||
expect(await page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).count()).toBe(1);
|
await expect(page.getByRole('img', { name: 'favicon-96x96.png thumbnail' })).toHaveCount(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ test.describe('Notebook Tests with CouchDB @couchdb', () => {
|
|||||||
|
|
||||||
// Create Notebook
|
// Create Notebook
|
||||||
testNotebook = await createDomainObjectWithDefaults(page, { type: '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 }) => {
|
test('Inspect Notebook Entry Network Requests', async ({ page }) => {
|
||||||
@ -55,15 +55,15 @@ test.describe('Notebook Tests with CouchDB @couchdb', () => {
|
|||||||
// Waits for the next request with the specified url
|
// Waits for the next request with the specified url
|
||||||
page.waitForRequest(`**/openmct/${testNotebook.uuid}`),
|
page.waitForRequest(`**/openmct/${testNotebook.uuid}`),
|
||||||
// Triggers the request
|
// Triggers the request
|
||||||
page.click('[aria-label="Add Page"]')
|
page.getByLabel('Add Page').click()
|
||||||
]);
|
]);
|
||||||
// Ensures that there are no other network requests
|
// Ensures that there are no other network requests
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('domcontentloaded');
|
||||||
|
|
||||||
// Assert that only two requests are made
|
// Assert that only two requests are made
|
||||||
// Network Requests are:
|
// Network Requests are:
|
||||||
// 1) The actual POST to create the page
|
// 1) The actual POST to create the page
|
||||||
expect(notebookElementsRequests.length).toBe(1);
|
expect(notebookElementsRequests).toHaveLength(1);
|
||||||
|
|
||||||
// Assert on request object
|
// Assert on request object
|
||||||
expect(notebookUrlRequest.postDataJSON().metadata.name).toBe(testNotebook.name);
|
expect(notebookUrlRequest.postDataJSON().metadata.name).toBe(testNotebook.name);
|
||||||
@ -77,7 +77,7 @@ test.describe('Notebook Tests with CouchDB @couchdb', () => {
|
|||||||
// 2) The shared worker event from 👆 POST request
|
// 2) The shared worker event from 👆 POST request
|
||||||
notebookElementsRequests = [];
|
notebookElementsRequests = [];
|
||||||
await nbUtils.enterTextEntry(page, 'First Entry');
|
await nbUtils.enterTextEntry(page, 'First Entry');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('domcontentloaded');
|
||||||
expect(notebookElementsRequests.length).toBeLessThanOrEqual(2);
|
expect(notebookElementsRequests.length).toBeLessThanOrEqual(2);
|
||||||
|
|
||||||
// Add some tags
|
// Add some tags
|
||||||
@ -120,8 +120,8 @@ test.describe('Notebook Tests with CouchDB @couchdb', () => {
|
|||||||
expect(filterNonFetchRequests(notebookElementsRequests).length).toBeLessThanOrEqual(12);
|
expect(filterNonFetchRequests(notebookElementsRequests).length).toBeLessThanOrEqual(12);
|
||||||
|
|
||||||
// Add two more pages
|
// Add two more pages
|
||||||
await page.click('[aria-label="Add Page"]');
|
await page.getByLabel('Add Page').click();
|
||||||
await page.click('[aria-label="Add Page"]');
|
await page.getByLabel('Add Page').click();
|
||||||
|
|
||||||
// Add three entries
|
// Add three entries
|
||||||
await nbUtils.enterTextEntry(page, 'First Entry');
|
await nbUtils.enterTextEntry(page, 'First Entry');
|
||||||
@ -141,7 +141,7 @@ test.describe('Notebook Tests with CouchDB @couchdb', () => {
|
|||||||
// 4) The shared worker event from 👆 POST request
|
// 4) The shared worker event from 👆 POST request
|
||||||
notebookElementsRequests = [];
|
notebookElementsRequests = [];
|
||||||
await nbUtils.enterTextEntry(page, 'Fourth Entry');
|
await nbUtils.enterTextEntry(page, 'Fourth Entry');
|
||||||
page.waitForLoadState('networkidle');
|
page.waitForLoadState('domcontentloaded');
|
||||||
|
|
||||||
expect(filterNonFetchRequests(notebookElementsRequests).length).toBeLessThanOrEqual(4);
|
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
|
// 4) The shared worker event from 👆 POST request
|
||||||
notebookElementsRequests = [];
|
notebookElementsRequests = [];
|
||||||
await nbUtils.enterTextEntry(page, 'Fifth Entry');
|
await nbUtils.enterTextEntry(page, 'Fifth Entry');
|
||||||
page.waitForLoadState('networkidle');
|
page.waitForLoadState('domcontentloaded');
|
||||||
|
|
||||||
expect(filterNonFetchRequests(notebookElementsRequests).length).toBeLessThanOrEqual(4);
|
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
|
// 4) The shared worker event from 👆 POST request
|
||||||
notebookElementsRequests = [];
|
notebookElementsRequests = [];
|
||||||
await nbUtils.enterTextEntry(page, 'Sixth Entry');
|
await nbUtils.enterTextEntry(page, 'Sixth Entry');
|
||||||
page.waitForLoadState('networkidle');
|
page.waitForLoadState('domcontentloaded');
|
||||||
|
|
||||||
expect(filterNonFetchRequests(notebookElementsRequests).length).toBeLessThanOrEqual(4);
|
expect(filterNonFetchRequests(notebookElementsRequests).length).toBeLessThanOrEqual(4);
|
||||||
});
|
});
|
||||||
@ -227,7 +227,7 @@ async function addTagAndAwaitNetwork(page, tagName) {
|
|||||||
page.locator(`[aria-label="Autocomplete Options"] >> text=${tagName}`).click(),
|
page.locator(`[aria-label="Autocomplete Options"] >> text=${tagName}`).click(),
|
||||||
expect(page.locator(`[aria-label="Tag"]:has-text("${tagName}")`)).toBeVisible()
|
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 expect(page.locator(`[aria-label="Tag"]:has-text("${tagName}")`)).toBeHidden();
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('domcontentloaded');
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import { openObjectTreeContextMenu } from '../../../../appActions.js';
|
|
||||||
import {
|
import {
|
||||||
dragAndDropEmbed,
|
dragAndDropEmbed,
|
||||||
enterTextEntry,
|
enterTextEntry,
|
||||||
@ -51,27 +50,22 @@ test.describe('Restricted Notebook', () => {
|
|||||||
const restrictedNotebookTreeObject = page.locator(`a:has-text("${notebook.name}")`);
|
const restrictedNotebookTreeObject = page.locator(`a:has-text("${notebook.name}")`);
|
||||||
|
|
||||||
// notebook tree object exists
|
// notebook tree object exists
|
||||||
expect.soft(await restrictedNotebookTreeObject.count()).toEqual(1);
|
await expect(restrictedNotebookTreeObject).toHaveCount(1);
|
||||||
|
|
||||||
// Click Remove Text
|
// Click Remove Text
|
||||||
await page.locator('li[role="menuitem"]:has-text("Remove")').click();
|
await page.locator('li[role="menuitem"]:has-text("Remove")').click();
|
||||||
|
|
||||||
// Click 'OK' on confirmation window and wait for save banner to appear
|
// Click 'Ok' on confirmation window
|
||||||
await Promise.all([
|
await page.locator('button:has-text("OK")').click();
|
||||||
page.waitForNavigation(),
|
|
||||||
page.locator('button:has-text("OK")').click(),
|
|
||||||
page.waitForSelector('.c-message-banner__message')
|
|
||||||
]);
|
|
||||||
|
|
||||||
// has been deleted
|
// has been deleted
|
||||||
expect(await restrictedNotebookTreeObject.count()).toEqual(0);
|
await expect(restrictedNotebookTreeObject).toHaveCount(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Can be locked if at least one page has one entry @addInit', async ({ page }) => {
|
test('Can be locked if at least one page has one entry @addInit', async ({ page }) => {
|
||||||
await enterTextEntry(page, TEST_TEXT);
|
await enterTextEntry(page, TEST_TEXT);
|
||||||
|
|
||||||
const commitButton = page.locator('button:has-text("Commit Entries")');
|
await expect(page.getByLabel('Commit Entries')).toHaveCount(1);
|
||||||
expect(await commitButton.count()).toEqual(1);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -86,20 +80,18 @@ test.describe('Restricted Notebook with at least one entry and with the page loc
|
|||||||
await page.locator('button.c-notebook__toggle-nav-button').click();
|
await page.locator('button.c-notebook__toggle-nav-button').click();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Locked page should now be in a locked state @addInit @unstable', async ({
|
test('Locked page should now be in a locked state @addInit', async ({ page }, testInfo) => {
|
||||||
page
|
|
||||||
}, testInfo) => {
|
|
||||||
// eslint-disable-next-line playwright/no-skipped-test
|
// eslint-disable-next-line playwright/no-skipped-test
|
||||||
test.skip(testInfo.project === 'chrome-beta', 'Test is unreliable on chrome-beta');
|
test.skip(testInfo.project === 'chrome-beta', 'Test is unreliable on chrome-beta');
|
||||||
// main lock message on page
|
// main lock message on page
|
||||||
const lockMessage = page.locator(
|
const lockMessage = page.locator(
|
||||||
'text=This page has been committed and cannot be modified or removed'
|
'text=This page has been committed and cannot be modified or removed'
|
||||||
);
|
);
|
||||||
expect.soft(await lockMessage.count()).toEqual(1);
|
await expect(lockMessage).toHaveCount(1);
|
||||||
|
|
||||||
// lock icon on page in sidebar
|
// lock icon on page in sidebar
|
||||||
const pageLockIcon = page.locator('ul.c-notebook__pages li div.icon-lock');
|
const pageLockIcon = page.locator('ul.c-notebook__pages li div.icon-lock');
|
||||||
expect.soft(await pageLockIcon.count()).toEqual(1);
|
await expect(pageLockIcon).toHaveCount(1);
|
||||||
|
|
||||||
// no way to remove a restricted notebook with a locked page
|
// no way to remove a restricted notebook with a locked page
|
||||||
await openObjectTreeContextMenu(page, notebook.url);
|
await openObjectTreeContextMenu(page, notebook.url);
|
||||||
@ -119,17 +111,14 @@ test.describe('Restricted Notebook with at least one entry and with the page loc
|
|||||||
await page.getByText('Unnamed Page').nth(1).fill(TEST_TEXT_NAME);
|
await page.getByText('Unnamed Page').nth(1).fill(TEST_TEXT_NAME);
|
||||||
|
|
||||||
// expect to be able to rename unlocked pages
|
// expect to be able to rename unlocked pages
|
||||||
const newPageElement = page.getByText(TEST_TEXT_NAME);
|
await page.getByText(TEST_TEXT_NAME).press('Enter'); // exit contenteditable state
|
||||||
const newPageCount = await newPageElement.count();
|
await expect(page.locator('div').filter({ hasText: /^Test Page$/ })).toHaveCount(1);
|
||||||
await newPageElement.press('Enter'); // exit contenteditable state
|
|
||||||
expect.soft(newPageCount).toEqual(1);
|
|
||||||
|
|
||||||
// enter test text
|
// enter test text
|
||||||
await enterTextEntry(page, TEST_TEXT);
|
await enterTextEntry(page, TEST_TEXT);
|
||||||
|
|
||||||
// expect new page to be lockable
|
// expect new page to be lockable
|
||||||
const commitButton = page.getByRole('button', { name: ' Commit Entries' });
|
await expect(page.getByLabel('Commit Entries')).toHaveCount(1);
|
||||||
expect.soft(await commitButton.count()).toEqual(1);
|
|
||||||
|
|
||||||
// Click the context menu button for the new page
|
// Click the context menu button for the new page
|
||||||
await page.getByTitle('Open context menu').click();
|
await page.getByTitle('Open context menu').click();
|
||||||
@ -140,7 +129,7 @@ test.describe('Restricted Notebook with at least one entry and with the page loc
|
|||||||
|
|
||||||
// deleted page, should no longer exist
|
// deleted page, should no longer exist
|
||||||
const deletedPageElement = page.getByText(TEST_TEXT_NAME);
|
const deletedPageElement = page.getByText(TEST_TEXT_NAME);
|
||||||
expect(await deletedPageElement.count()).toEqual(0);
|
await expect(deletedPageElement).toHaveCount(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -173,7 +162,7 @@ test.describe('can export restricted notebook as text', () => {
|
|||||||
await startAndAddRestrictedNotebookObject(page);
|
await startAndAddRestrictedNotebookObject(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('basic functionality ', async ({ page }) => {
|
test('basic functionality', async ({ page }) => {
|
||||||
await enterTextEntry(page, `Foo bar entry`);
|
await enterTextEntry(page, `Foo bar entry`);
|
||||||
// Click on 3 Dot Menu
|
// Click on 3 Dot Menu
|
||||||
await page.locator('button[title="More actions"]').click();
|
await page.locator('button[title="More actions"]').click();
|
||||||
@ -190,8 +179,23 @@ test.describe('can export restricted notebook as text', () => {
|
|||||||
expect(exportedText).toContain('Foo bar entry');
|
expect(exportedText).toContain('Foo bar entry');
|
||||||
});
|
});
|
||||||
|
|
||||||
test.fixme('can export multiple notebook entries as text ', async ({ page }) => {});
|
test.fixme('can export multiple notebook entries as text', async ({ page }) => {});
|
||||||
test.fixme('can export all notebook entry metdata', async ({ page }) => {});
|
test.fixme('can export all notebook entry metdata', async ({ page }) => {});
|
||||||
test.fixme('can export all notebook tags', async ({ page }) => {});
|
test.fixme('can export all notebook tags', async ({ page }) => {});
|
||||||
test.fixme('can export all notebook snapshots', async ({ page }) => {});
|
test.fixme('can export all notebook snapshots', async ({ page }) => {});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the given `domainObject`'s context menu from the object tree.
|
||||||
|
* Expands the path to the object and scrolls to it if necessary.
|
||||||
|
*
|
||||||
|
* @param {import('@playwright/test').Page} page
|
||||||
|
* @param {string} url the url to the object
|
||||||
|
*/
|
||||||
|
async function openObjectTreeContextMenu(page, url) {
|
||||||
|
await page.goto(url);
|
||||||
|
await page.getByLabel('Show selected item in tree').click();
|
||||||
|
await page.locator('.is-navigated-object').click({
|
||||||
|
button: 'right'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -24,7 +24,10 @@
|
|||||||
Testsuite for plot autoscale.
|
Testsuite for plot autoscale.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createDomainObjectWithDefaults } from '../../../../appActions.js';
|
import {
|
||||||
|
createDomainObjectWithDefaults,
|
||||||
|
navigateToObjectWithFixedTimeBounds
|
||||||
|
} from '../../../../appActions.js';
|
||||||
import { expect, test } from '../../../../pluginFixtures.js';
|
import { expect, test } from '../../../../pluginFixtures.js';
|
||||||
test.use({
|
test.use({
|
||||||
viewport: {
|
viewport: {
|
||||||
@ -51,9 +54,7 @@ test.describe('Autoscale', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Switch to fixed time, start: 2022-03-28 22:00:00.000 UTC, end: 2022-03-28 22:00:30.000 UTC
|
// Switch to fixed time, start: 2022-03-28 22:00:00.000 UTC, end: 2022-03-28 22:00:30.000 UTC
|
||||||
await page.goto(
|
await navigateToObjectWithFixedTimeBounds(page, overlayPlot.url, 1648591200000, 1648591230000);
|
||||||
`${overlayPlot.url}?tc.mode=fixed&tc.startBound=1648591200000&tc.endBound=1648591230000&tc.timeSystem=utc&view=plot-overlay`
|
|
||||||
);
|
|
||||||
|
|
||||||
await testYTicks(page, ['-1.00', '-0.50', '0.00', '0.50', '1.00']);
|
await testYTicks(page, ['-1.00', '-0.50', '0.00', '0.50', '1.00']);
|
||||||
|
|
||||||
@ -69,15 +70,15 @@ test.describe('Autoscale', () => {
|
|||||||
await page.getByLabel('Y Axis 1 Maximum value').fill('2');
|
await page.getByLabel('Y Axis 1 Maximum value').fill('2');
|
||||||
|
|
||||||
// save
|
// save
|
||||||
await page.click('button[title="Save"]');
|
await page.getByLabel('Save').click();
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(),
|
page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(),
|
||||||
//Wait for Save Banner to appear
|
//Wait for Save Banner to appear
|
||||||
page.waitForSelector('.c-message-banner__message')
|
page.locator('.c-message-banner__message').hover({ trial: true })
|
||||||
]);
|
]);
|
||||||
//Wait until Save Banner is gone
|
//Wait until Save Banner is gone
|
||||||
await page.locator('.c-message-banner__close-button').click();
|
await page.locator('.c-message-banner__close-button').click();
|
||||||
await page.waitForSelector('.c-message-banner__message', { state: 'detached' });
|
await page.locator('.c-message-banner__message').waitFor({ state: 'detached' });
|
||||||
|
|
||||||
// Make sure that after turning off autoscale, the user entered range values are reflected in the ticks.
|
// Make sure that after turning off autoscale, the user entered range values are reflected in the ticks.
|
||||||
await testYTicks(page, [
|
await testYTicks(page, [
|
||||||
|
@ -29,15 +29,46 @@ import { createDomainObjectWithDefaults, setTimeConductorBounds } from '../../..
|
|||||||
import { expect, test } from '../../../../pluginFixtures.js';
|
import { expect, test } from '../../../../pluginFixtures.js';
|
||||||
|
|
||||||
test.describe('Log plot tests', () => {
|
test.describe('Log plot tests', () => {
|
||||||
test('Log Plot ticks are functionally correct in regular and log mode and after refresh', async ({
|
test.beforeEach(async ({ page }) => {
|
||||||
page,
|
// fresh page with time range from 2022-03-29 22:00:00.000Z to 2022-03-29 22:00:30.000Z
|
||||||
openmctConfig
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
}) => {
|
|
||||||
const { myItemsFolderName } = openmctConfig;
|
|
||||||
//Test is slow and should be split in the future
|
|
||||||
test.slow();
|
|
||||||
|
|
||||||
await makeOverlayPlot(page, myItemsFolderName);
|
// Set a specific time range for consistency, otherwise it will change
|
||||||
|
// on every test to a range based on the current time.
|
||||||
|
const startDate = '2022-03-29';
|
||||||
|
const startTime = '22:00:00';
|
||||||
|
const endDate = '2022-03-29';
|
||||||
|
const endTime = '22:00:30';
|
||||||
|
|
||||||
|
await setTimeConductorBounds(page, { startDate, startTime, endDate, endTime });
|
||||||
|
|
||||||
|
const overlayPlot = await createDomainObjectWithDefaults(page, {
|
||||||
|
type: 'Overlay Plot',
|
||||||
|
name: 'Unnamed Overlay Plot'
|
||||||
|
});
|
||||||
|
|
||||||
|
// create a sinewave generator
|
||||||
|
await createDomainObjectWithDefaults(page, {
|
||||||
|
type: 'Sine Wave Generator',
|
||||||
|
name: 'Unnamed Sine Wave Generator',
|
||||||
|
parent: overlayPlot.uuid
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.getByLabel('More actions').click();
|
||||||
|
await page.getByLabel('Edit Properties...').click();
|
||||||
|
|
||||||
|
// set amplitude to 6, offset 4, data rate 2 hz
|
||||||
|
await page.getByLabel('Amplitude', { exact: true }).fill('6');
|
||||||
|
await page.getByLabel('Offset', { exact: true }).fill('4');
|
||||||
|
await page.getByLabel('Data Rate (hz)', { exact: true }).fill('2');
|
||||||
|
|
||||||
|
await page.getByLabel('Save').click();
|
||||||
|
|
||||||
|
await page.goto(overlayPlot.url);
|
||||||
|
});
|
||||||
|
test('Log Plot ticks are functionally correct in regular and log mode and after refresh', async ({
|
||||||
|
page
|
||||||
|
}) => {
|
||||||
await testRegularTicks(page);
|
await testRegularTicks(page);
|
||||||
await enableEditMode(page);
|
await enableEditMode(page);
|
||||||
await page.getByRole('tab', { name: 'Config' }).click();
|
await page.getByRole('tab', { name: 'Config' }).click();
|
||||||
@ -47,83 +78,35 @@ test.describe('Log plot tests', () => {
|
|||||||
await testRegularTicks(page);
|
await testRegularTicks(page);
|
||||||
await enableLogMode(page);
|
await enableLogMode(page);
|
||||||
await testLogTicks(page);
|
await testLogTicks(page);
|
||||||
await saveOverlayPlot(page);
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
|
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
await testLogTicks(page);
|
await testLogTicks(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Leaving test as 'TODO' for now.
|
// Leaving test as 'TODO' for now.
|
||||||
// NOTE: Not eligible for community contributions.
|
// NOTE: Not eligible for community contributions.
|
||||||
test.fixme(
|
test.fixme('Verify that log mode option is reflected in import/export JSON', async ({ page }) => {
|
||||||
'Verify that log mode option is reflected in import/export JSON',
|
await enableEditMode(page);
|
||||||
async ({ page, openmctConfig }) => {
|
await enableLogMode(page);
|
||||||
const { myItemsFolderName } = openmctConfig;
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
|
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
|
|
||||||
await makeOverlayPlot(page, myItemsFolderName);
|
// TODO ...export, delete the overlay, then import it...
|
||||||
await enableEditMode(page);
|
|
||||||
await enableLogMode(page);
|
|
||||||
await saveOverlayPlot(page);
|
|
||||||
|
|
||||||
// TODO ...export, delete the overlay, then import it...
|
//await testLogTicks(page);
|
||||||
|
|
||||||
//await testLogTicks(page);
|
// TODO, the plot is slightly at different position that in the other test, so this fails.
|
||||||
|
// ...We can fix it by copying all steps from the first test...
|
||||||
// TODO, the plot is slightly at different position that in the other test, so this fails.
|
// await testLogPlotPixels(page);
|
||||||
// ...We can fix it by copying all steps from the first test...
|
});
|
||||||
// await testLogPlotPixels(page);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Makes an overlay plot with a sine wave generator and clicks on the overlay plot in the sidebar so it is the active thing displayed.
|
|
||||||
* @param {import('@playwright/test').Page} page
|
|
||||||
* @param {string} myItemsFolderName
|
|
||||||
*/
|
|
||||||
async function makeOverlayPlot(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' });
|
|
||||||
|
|
||||||
// Set a specific time range for consistency, otherwise it will change
|
|
||||||
// on every test to a range based on the current time.
|
|
||||||
|
|
||||||
const startDate = '2022-03-29';
|
|
||||||
const startTime = '22:00:00';
|
|
||||||
const endDate = '2022-03-29';
|
|
||||||
const endTime = '22:00:30';
|
|
||||||
|
|
||||||
await setTimeConductorBounds(page, { startDate, startTime, endDate, endTime });
|
|
||||||
|
|
||||||
const overlayPlot = await createDomainObjectWithDefaults(page, {
|
|
||||||
type: 'Overlay Plot',
|
|
||||||
name: 'Unnamed Overlay Plot'
|
|
||||||
});
|
|
||||||
|
|
||||||
// create a sinewave generator
|
|
||||||
await createDomainObjectWithDefaults(page, {
|
|
||||||
type: 'Sine Wave Generator',
|
|
||||||
name: 'Unnamed Sine Wave Generator',
|
|
||||||
parent: overlayPlot.uuid
|
|
||||||
});
|
|
||||||
|
|
||||||
await page.getByLabel('More actions').click();
|
|
||||||
await page.getByLabel('Edit Properties...').click();
|
|
||||||
|
|
||||||
// set amplitude to 6, offset 4, data rate 2 hz
|
|
||||||
await page.getByLabel('Amplitude', { exact: true }).fill('6');
|
|
||||||
await page.getByLabel('Offset', { exact: true }).fill('4');
|
|
||||||
await page.getByLabel('Data Rate (hz)', { exact: true }).fill('2');
|
|
||||||
|
|
||||||
await page.getByLabel('Save').click();
|
|
||||||
|
|
||||||
await page.goto(overlayPlot.url);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
*/
|
*/
|
||||||
async function testRegularTicks(page) {
|
async function testRegularTicks(page) {
|
||||||
const yTicks = page.locator('.gl-plot-y-tick-label');
|
const yTicks = page.locator('.gl-plot-y-tick-label');
|
||||||
expect(await yTicks.count()).toBe(7);
|
await expect(yTicks).toHaveCount(7);
|
||||||
await expect(yTicks.nth(0)).toHaveText('-2');
|
await expect(yTicks.nth(0)).toHaveText('-2');
|
||||||
await expect(yTicks.nth(1)).toHaveText('0');
|
await expect(yTicks.nth(1)).toHaveText('0');
|
||||||
await expect(yTicks.nth(2)).toHaveText('2');
|
await expect(yTicks.nth(2)).toHaveText('2');
|
||||||
@ -138,7 +121,7 @@ async function testRegularTicks(page) {
|
|||||||
*/
|
*/
|
||||||
async function testLogTicks(page) {
|
async function testLogTicks(page) {
|
||||||
const yTicks = page.locator('.gl-plot-y-tick-label');
|
const yTicks = page.locator('.gl-plot-y-tick-label');
|
||||||
expect(await yTicks.count()).toBe(9);
|
await expect(yTicks).toHaveCount(9);
|
||||||
await expect(yTicks.nth(0)).toHaveText('-2.98');
|
await expect(yTicks.nth(0)).toHaveText('-2.98');
|
||||||
await expect(yTicks.nth(1)).toHaveText('-1.51');
|
await expect(yTicks.nth(1)).toHaveText('-1.51');
|
||||||
await expect(yTicks.nth(2)).toHaveText('-0.58');
|
await expect(yTicks.nth(2)).toHaveText('-0.58');
|
||||||
@ -175,26 +158,6 @@ async function disableLogMode(page) {
|
|||||||
await page.getByRole('checkbox', { name: 'Log mode' }).uncheck();
|
await page.getByRole('checkbox', { name: 'Log mode' }).uncheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {import('@playwright/test').Page} page
|
|
||||||
*/
|
|
||||||
async function saveOverlayPlot(page) {
|
|
||||||
// save overlay plot
|
|
||||||
await page
|
|
||||||
.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button')
|
|
||||||
.nth(1)
|
|
||||||
.click();
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
page.getByRole('listitem', { name: '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' });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
*/
|
*/
|
||||||
|
@ -52,8 +52,8 @@ test.describe('Overlay Plot', () => {
|
|||||||
await page.getByRole('tab', { name: 'Config' }).click();
|
await page.getByRole('tab', { name: 'Config' }).click();
|
||||||
|
|
||||||
// navigate to plot series color palette
|
// navigate to plot series color palette
|
||||||
await page.click('.l-browse-bar__actions__edit');
|
await page.getByLabel('Edit Object').click();
|
||||||
await page.locator('li.c-tree__item.menus-to-left .c-disclosure-triangle').click();
|
await page.getByLabel('Expand Sine Wave Generator:').click();
|
||||||
await page.locator('.c-click-swatch--menu').click();
|
await page.locator('.c-click-swatch--menu').click();
|
||||||
await page.locator('.c-palette__item[style="background: rgb(255, 166, 61);"]').click();
|
await page.locator('.c-palette__item[style="background: rgb(255, 166, 61);"]').click();
|
||||||
// gets color for swatch located in legend
|
// gets color for swatch located in legend
|
||||||
@ -93,7 +93,7 @@ test.describe('Overlay Plot', () => {
|
|||||||
await expect(page.getByLabel('Plot Legend Expanded')).toBeHidden();
|
await expect(page.getByLabel('Plot Legend Expanded')).toBeHidden();
|
||||||
await expect(page.getByLabel('Expand by Default')).toHaveText(/No/);
|
await expect(page.getByLabel('Expand by Default')).toHaveText(/No/);
|
||||||
|
|
||||||
expect(await page.getByLabel('Plot Legend Item').count()).toBe(3);
|
await expect(page.getByLabel('Plot Legend Item')).toHaveCount(3);
|
||||||
|
|
||||||
// Change the legend to expand by default
|
// Change the legend to expand by default
|
||||||
await page.getByLabel('Edit Object').click();
|
await page.getByLabel('Edit Object').click();
|
||||||
@ -136,8 +136,8 @@ test.describe('Overlay Plot', () => {
|
|||||||
await page.goto(overlayPlot.url);
|
await page.goto(overlayPlot.url);
|
||||||
|
|
||||||
// Assert that no limit lines are shown by default
|
// Assert that no limit lines are shown by default
|
||||||
await page.waitForSelector('.js-limit-area', { state: 'attached' });
|
await page.locator('.js-limit-area').waitFor({ state: 'attached' });
|
||||||
expect(await page.locator('.c-plot-limit-line').count()).toBe(0);
|
await expect(page.locator('.c-plot-limit-line')).toHaveCount(0);
|
||||||
|
|
||||||
// Enter edit mode
|
// Enter edit mode
|
||||||
await page.getByLabel('Edit Object').click();
|
await page.getByLabel('Edit Object').click();
|
||||||
@ -200,8 +200,8 @@ test.describe('Overlay Plot', () => {
|
|||||||
await page.goto(overlayPlot.url);
|
await page.goto(overlayPlot.url);
|
||||||
|
|
||||||
// Assert that no limit lines are shown by default
|
// Assert that no limit lines are shown by default
|
||||||
await page.waitForSelector('.js-limit-area', { state: 'attached' });
|
await expect(page.locator('.js-limit-area')).toBeAttached();
|
||||||
expect(await page.locator('.c-plot-limit-line').count()).toBe(0);
|
await expect(page.locator('.c-plot-limit-line')).toHaveCount(0);
|
||||||
|
|
||||||
// Enter edit mode
|
// Enter edit mode
|
||||||
await page.getByLabel('Edit Object').click();
|
await page.getByLabel('Edit Object').click();
|
||||||
@ -309,32 +309,26 @@ test.describe('Overlay Plot', () => {
|
|||||||
expect(yAxis3Group.getByRole('listitem').nth(0).getByText(swgB.name)).toBeTruthy();
|
expect(yAxis3Group.getByRole('listitem').nth(0).getByText(swgB.name)).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
test.fixme(
|
test('Clicking on an item in the elements pool brings up the plot preview with data points', async ({
|
||||||
'Clicking on an item in the elements pool brings up the plot preview with data points',
|
page
|
||||||
async ({ page }) => {
|
}) => {
|
||||||
test.info().annotations.push({
|
const swgA = await createDomainObjectWithDefaults(page, {
|
||||||
type: 'issue',
|
type: 'Sine Wave Generator',
|
||||||
description: 'https://github.com/nasa/openmct/issues/7421'
|
parent: overlayPlot.uuid
|
||||||
});
|
});
|
||||||
|
|
||||||
const swgA = await createDomainObjectWithDefaults(page, {
|
await page.goto(overlayPlot.url);
|
||||||
type: 'Sine Wave Generator',
|
// Wait for plot series data to load and be drawn
|
||||||
parent: overlayPlot.uuid
|
await waitForPlotsToRender(page);
|
||||||
});
|
await page.getByLabel('Edit Object').click();
|
||||||
|
|
||||||
await page.goto(overlayPlot.url);
|
await page.getByRole('tab', { name: 'Elements' }).click();
|
||||||
// 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.locator(`#inspector-elements-tree >> text=${swgA.name}`).click();
|
||||||
|
const plotPixels = await getCanvasPixels(page, '.js-overlay canvas');
|
||||||
await page.locator(`#inspector-elements-tree >> text=${swgA.name}`).click();
|
const plotPixelSize = plotPixels.length;
|
||||||
const plotPixels = await getCanvasPixels(page, '.js-overlay canvas');
|
expect(plotPixelSize).toBeGreaterThan(0);
|
||||||
const plotPixelSize = plotPixels.length;
|
});
|
||||||
expect(plotPixelSize).toBeGreaterThan(0);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
test('Can remove an item via the elements pool action menu', async ({ page }) => {
|
test('Can remove an item via the elements pool action menu', async ({ page }) => {
|
||||||
const swgA = await createDomainObjectWithDefaults(page, {
|
const swgA = await createDomainObjectWithDefaults(page, {
|
||||||
@ -357,7 +351,7 @@ test.describe('Overlay Plot', () => {
|
|||||||
await expect(swgAElementsPoolItem).toBeVisible();
|
await expect(swgAElementsPoolItem).toBeVisible();
|
||||||
await swgAElementsPoolItem.click({ button: 'right' });
|
await swgAElementsPoolItem.click({ button: 'right' });
|
||||||
await page.getByRole('menuitem', { name: 'Remove' }).click();
|
await page.getByRole('menuitem', { name: 'Remove' }).click();
|
||||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
await page.getByRole('button', { name: 'Ok', exact: true }).click();
|
||||||
await expect(swgAElementsPoolItem).toBeHidden();
|
await expect(swgAElementsPoolItem).toBeHidden();
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Save' }).click();
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
@ -387,7 +381,7 @@ async function assertLimitLinesExistAndAreVisible(page) {
|
|||||||
// Wait for plot series data to load
|
// Wait for plot series data to load
|
||||||
await waitForPlotsToRender(page);
|
await waitForPlotsToRender(page);
|
||||||
// Wait for limit lines to be created
|
// Wait for limit lines to be created
|
||||||
await page.waitForSelector('.js-limit-area', { state: 'attached' });
|
await page.locator('.js-limit-area').waitFor({ state: 'attached' });
|
||||||
// There should be 10 limit lines created by default
|
// There should be 10 limit lines created by default
|
||||||
await expect(page.locator('.c-plot-limit-line')).toHaveCount(10);
|
await expect(page.locator('.c-plot-limit-line')).toHaveCount(10);
|
||||||
const limitLineCount = await page.locator('.c-plot-limit-line').count();
|
const limitLineCount = await page.locator('.c-plot-limit-line').count();
|
||||||
|
@ -85,15 +85,8 @@ test.describe('Plot Controls', () => {
|
|||||||
await page.getByLabel('Y Axis 1 Maximum value').fill('1');
|
await page.getByLabel('Y Axis 1 Maximum value').fill('1');
|
||||||
|
|
||||||
// save
|
// save
|
||||||
await page.click('button[title="Save"]');
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
await Promise.all([
|
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
page.getByRole('listitem', { name: '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' });
|
|
||||||
// hover over plot for plot controls
|
// hover over plot for plot controls
|
||||||
await page.getByLabel('Plot Canvas').hover();
|
await page.getByLabel('Plot Canvas').hover();
|
||||||
// click on pause control
|
// click on pause control
|
||||||
|
@ -78,17 +78,13 @@ test.describe('Plot Rendering', () => {
|
|||||||
// click on synchronize with time conductor
|
// click on synchronize with time conductor
|
||||||
await page.getByTitle('Synchronize Time Conductor').click();
|
await page.getByTitle('Synchronize Time Conductor').click();
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
await page.getByRole('button', { name: 'Ok', exact: true }).click();
|
||||||
|
|
||||||
//confirm that you're now in fixed mode with the correct range
|
//confirm that you're now in fixed mode with the correct range
|
||||||
await expect(page.getByLabel('Time Conductor Mode')).toHaveText('Fixed Timespan');
|
await expect(page.getByLabel('Time Conductor Mode')).toHaveText('Fixed Timespan');
|
||||||
});
|
});
|
||||||
|
|
||||||
test.fixme('Plot is rendered when infinity values exist', async ({ page }) => {
|
test('Plot is rendered when infinity values exist', async ({ page }) => {
|
||||||
test.info().annotations.push({
|
|
||||||
type: 'issue',
|
|
||||||
description: 'https://github.com/nasa/openmct/issues/7421'
|
|
||||||
});
|
|
||||||
// Edit Plot
|
// Edit Plot
|
||||||
await editSineWaveToUseInfinityOption(page, sineWaveGeneratorObject);
|
await editSineWaveToUseInfinityOption(page, sineWaveGeneratorObject);
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ test.describe('Scatter Plot', () => {
|
|||||||
await page.goto(scatterPlot.url);
|
await page.goto(scatterPlot.url);
|
||||||
await page.getByLabel('Edit Object').click();
|
await page.getByLabel('Edit Object').click();
|
||||||
await page.getByRole('tab', { name: 'Elements' }).click();
|
await page.getByRole('tab', { name: 'Elements' }).click();
|
||||||
await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg1.name}`)).toBeVisible();
|
await expect(page.getByLabel(`Preview ${swg1.name}`)).toBeVisible();
|
||||||
await page.getByRole('button', { name: 'Save' }).click();
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
|
|
||||||
@ -65,14 +65,12 @@ test.describe('Scatter Plot', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Verify that the 'Replace telemetry source' modal appears and accept it
|
// Verify that the 'Replace telemetry source' modal appears and accept it
|
||||||
await expect
|
await expect(
|
||||||
.soft(
|
page.getByText(
|
||||||
page.locator(
|
'This action will replace the current telemetry source. Do you want to continue?'
|
||||||
'text=This action will replace the current telemetry source. Do you want to continue?'
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.toBeVisible();
|
).toBeVisible();
|
||||||
await page.click('text=Ok');
|
await page.getByRole('button', { name: 'Ok', exact: true }).click();
|
||||||
|
|
||||||
// Navigate to the scatter plot and verify that the new SWG
|
// Navigate to the scatter plot and verify that the new SWG
|
||||||
// appears in the elements pool and the old one is gone
|
// appears in the elements pool and the old one is gone
|
||||||
@ -81,27 +79,25 @@ test.describe('Scatter Plot', () => {
|
|||||||
|
|
||||||
// Click the "Elements" tab
|
// Click the "Elements" tab
|
||||||
await page.getByRole('tab', { name: 'Elements' }).click();
|
await page.getByRole('tab', { name: 'Elements' }).click();
|
||||||
await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg1.name}`)).toBeHidden();
|
await expect(page.getByLabel(`Preview ${swg1.name}`)).toBeHidden();
|
||||||
await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg2.name}`)).toBeVisible();
|
await expect(page.getByLabel(`Preview ${swg2.name}`)).toBeVisible();
|
||||||
await page.getByRole('button', { name: 'Save' }).click();
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
|
|
||||||
// Right click on the new SWG in the elements pool and delete it
|
// Right click on the new SWG in the elements pool and delete it
|
||||||
await page.locator(`#inspector-elements-tree >> text=${swg2.name}`).click({
|
await page.getByLabel(`Preview ${swg2.name}`).click({
|
||||||
button: 'right'
|
button: 'right'
|
||||||
});
|
});
|
||||||
await page.locator('li[title="Remove this object from its containing object."]').click();
|
await page.getByLabel('Remove').click();
|
||||||
|
|
||||||
// Verify that the 'Remove object' confirmation modal appears and accept it
|
// Verify that the 'Remove object' confirmation modal appears and accept it
|
||||||
await expect
|
await expect(
|
||||||
.soft(
|
page.getByText(
|
||||||
page.locator(
|
'Warning! This action will remove this object. Are you sure you want to continue?'
|
||||||
'text=Warning! This action will remove this object. Are you sure you want to continue?'
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.toBeVisible();
|
).toBeVisible();
|
||||||
await page.click('text=Ok');
|
await page.getByRole('button', { name: 'Ok', exact: true }).click();
|
||||||
|
|
||||||
// Verify that the elements pool shows no elements
|
// Verify that the elements pool shows no elements
|
||||||
await expect(page.locator('text="No contained elements"')).toBeVisible();
|
await expect(page.getByText('No contained elements')).toBeVisible();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -82,7 +82,7 @@ test.describe('Stacked Plot', () => {
|
|||||||
.getByRole('menuitem')
|
.getByRole('menuitem')
|
||||||
.filter({ hasText: /Remove/ })
|
.filter({ hasText: /Remove/ })
|
||||||
.click();
|
.click();
|
||||||
await page.getByRole('button').filter({ hasText: 'OK' }).click();
|
await page.getByRole('button').filter({ hasText: 'Ok' }).click();
|
||||||
|
|
||||||
await expect(page.locator('#inspector-elements-tree .js-elements-pool__item')).toHaveCount(2);
|
await expect(page.locator('#inspector-elements-tree .js-elements-pool__item')).toHaveCount(2);
|
||||||
|
|
||||||
@ -239,7 +239,7 @@ test.describe('Stacked Plot', () => {
|
|||||||
await page.getByLabel(`Stacked Plot Item ${swgA.name}`).click();
|
await page.getByLabel(`Stacked Plot Item ${swgA.name}`).click();
|
||||||
|
|
||||||
// Expand config for the series
|
// Expand config for the series
|
||||||
await page.getByLabel('Expand Sine Wave Generator').click();
|
await page.getByLabel('Expand Sine Wave Generator A Plot Series Options').click();
|
||||||
|
|
||||||
// turn off alarm markers
|
// turn off alarm markers
|
||||||
await page.getByLabel('Alarm Markers').uncheck();
|
await page.getByLabel('Alarm Markers').uncheck();
|
||||||
@ -256,8 +256,7 @@ test.describe('Stacked Plot', () => {
|
|||||||
await page.getByLabel(`Stacked Plot Item ${swgA.name}`).click();
|
await page.getByLabel(`Stacked Plot Item ${swgA.name}`).click();
|
||||||
|
|
||||||
// Expand config for the series
|
// Expand config for the series
|
||||||
//TODO Fix this locator
|
await page.getByLabel('Expand Sine Wave Generator A Plot Series Options').click();
|
||||||
await page.getByLabel('Expand Sine Wave Generator A generator').click();
|
|
||||||
|
|
||||||
// Assert that alarm markers are still turned off
|
// Assert that alarm markers are still turned off
|
||||||
await expect(
|
await expect(
|
||||||
@ -277,7 +276,7 @@ test.describe('Stacked Plot', () => {
|
|||||||
|
|
||||||
await page.getByRole('tab', { name: 'Config' }).click();
|
await page.getByRole('tab', { name: 'Config' }).click();
|
||||||
|
|
||||||
let legendProperties = await page.locator('[aria-label="Legend Properties"]');
|
const legendProperties = page.getByLabel('Legend Properties');
|
||||||
await legendProperties.locator('[title="Display legends per sub plot."]~div input').uncheck();
|
await legendProperties.locator('[title="Display legends per sub plot."]~div input').uncheck();
|
||||||
|
|
||||||
await assertAggregateLegendIsVisible(page);
|
await assertAggregateLegendIsVisible(page);
|
||||||
@ -356,11 +355,9 @@ async function assertAggregateLegendIsVisible(page) {
|
|||||||
// Wait for plot series data to load
|
// Wait for plot series data to load
|
||||||
await waitForPlotsToRender(page);
|
await waitForPlotsToRender(page);
|
||||||
// Wait for plot legend to be shown
|
// Wait for plot legend to be shown
|
||||||
await page.waitForSelector('.js-stacked-plot-legend', { state: 'attached' });
|
await expect(page.locator('.js-stacked-plot-legend')).toBeVisible();
|
||||||
// There should be 3 legend items
|
// There should be 3 legend items
|
||||||
expect(
|
await expect(
|
||||||
await page
|
page.locator('.js-stacked-plot-legend .c-plot-legend__wrapper div.plot-legend-item')
|
||||||
.locator('.js-stacked-plot-legend .c-plot-legend__wrapper div.plot-legend-item')
|
).toHaveCount(3);
|
||||||
.count()
|
|
||||||
).toBe(3);
|
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ test.describe('Plot Tagging', () => {
|
|||||||
await setRealTimeMode(page);
|
await setRealTimeMode(page);
|
||||||
|
|
||||||
// Search for Science Tag
|
// Search for Science Tag
|
||||||
await page.getByRole('searchbox', { name: 'Search Input' });
|
await page.getByRole('searchbox', { name: 'Search Input' }).click();
|
||||||
await page.getByRole('searchbox', { name: 'Search Input' }).fill('sc');
|
await page.getByRole('searchbox', { name: 'Search Input' }).fill('sc');
|
||||||
|
|
||||||
// Click on the search object result
|
// Click on the search object result
|
||||||
|
@ -42,19 +42,21 @@ test.describe('Reload action', () => {
|
|||||||
|
|
||||||
await createDomainObjectWithDefaults(page, {
|
await createDomainObjectWithDefaults(page, {
|
||||||
type: 'Sine Wave Generator',
|
type: 'Sine Wave Generator',
|
||||||
parent: alphaTable.uuid,
|
parent: alphaTable.uuid
|
||||||
customParameters: {
|
|
||||||
'[aria-label="Data Rate (hz)"]': '0.001'
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
await page.getByLabel('More actions').click();
|
||||||
|
await page.getByRole('menuitem', { name: /Edit Properties/ }).click();
|
||||||
|
await page.getByLabel('Data Rate (hz)', { exact: true }).fill('0.001');
|
||||||
|
await page.getByLabel('Save').click();
|
||||||
|
|
||||||
await createDomainObjectWithDefaults(page, {
|
await createDomainObjectWithDefaults(page, {
|
||||||
type: 'Sine Wave Generator',
|
type: 'Sine Wave Generator',
|
||||||
parent: betaTable.uuid,
|
parent: betaTable.uuid
|
||||||
customParameters: {
|
|
||||||
'[aria-label="Data Rate (hz)"]': '0.001'
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
await page.getByLabel('More actions').click();
|
||||||
|
await page.getByRole('menuitem', { name: /Edit Properties/ }).click();
|
||||||
|
await page.getByLabel('Data Rate (hz)', { exact: true }).fill('0.001');
|
||||||
|
await page.getByLabel('Save').click();
|
||||||
|
|
||||||
await page.goto(displayLayout.url);
|
await page.goto(displayLayout.url);
|
||||||
|
|
||||||
@ -63,20 +65,26 @@ test.describe('Reload action', () => {
|
|||||||
|
|
||||||
await page.getByLabel('Edit Object', { exact: true }).click();
|
await page.getByLabel('Edit Object', { exact: true }).click();
|
||||||
|
|
||||||
await page.dragAndDrop(`text='Alpha Table'`, '.l-layout__grid-holder', {
|
await page
|
||||||
targetPosition: { x: 0, y: 0 }
|
.getByLabel('Main Tree')
|
||||||
});
|
.getByLabel(`Preview ${alphaTable.name}`)
|
||||||
|
.dragTo(page.getByLabel('Layout Grid'), {
|
||||||
|
targetPosition: { x: 0, y: 0 }
|
||||||
|
});
|
||||||
|
|
||||||
await page.dragAndDrop(`text='Beta Table'`, '.l-layout__grid-holder', {
|
await page
|
||||||
targetPosition: { x: 0, y: 250 }
|
.getByLabel('Main Tree')
|
||||||
});
|
.getByLabel(`Preview ${betaTable.name}`)
|
||||||
|
.dragTo(page.getByLabel('Layout Grid'), {
|
||||||
|
targetPosition: { x: 0, y: 250 }
|
||||||
|
});
|
||||||
|
|
||||||
await page.locator('button[title="Save"]').click();
|
await page.getByLabel('Save').click();
|
||||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can reload display layout and its children', async ({ page }) => {
|
test('can reload display layout and its children', async ({ page }) => {
|
||||||
const beforeReloadAlphaTelemetryValue = await page
|
const beforeReloadAlphaTelemetryValue = page
|
||||||
.getByLabel('Alpha Table table content')
|
.getByLabel('Alpha Table table content')
|
||||||
.getByLabel('wavelengths table cell')
|
.getByLabel('wavelengths table cell')
|
||||||
.first()
|
.first()
|
||||||
|
@ -466,7 +466,7 @@ test.describe('Flexible Layout styling', () => {
|
|||||||
page.getByLabel('Flexible Layout Column')
|
page.getByLabel('Flexible Layout Column')
|
||||||
);
|
);
|
||||||
await page.getByLabel('Cancel Editing').click();
|
await page.getByLabel('Cancel Editing').click();
|
||||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
await page.getByRole('button', { name: 'Ok', exact: true }).click();
|
||||||
await checkStyles(
|
await checkStyles(
|
||||||
hexToRGB(defaultBorderTargetColor),
|
hexToRGB(defaultBorderTargetColor),
|
||||||
NO_STYLE_RGBA,
|
NO_STYLE_RGBA,
|
||||||
|
@ -114,8 +114,8 @@ test.describe('Tabs View CRUD', () => {
|
|||||||
await page.getByLabel('Edit Object').click();
|
await page.getByLabel('Edit Object').click();
|
||||||
await page.getByLabel('More actions').click();
|
await page.getByLabel('More actions').click();
|
||||||
await page.getByLabel('Edit Properties...').click();
|
await page.getByLabel('Edit Properties...').click();
|
||||||
await expect(await page.getByLabel('Eager Load Tabs')).not.toBeChecked();
|
await expect(page.getByLabel('Eager Load Tabs')).not.toBeChecked();
|
||||||
await page.getByLabel('Eager Load Tabs').setChecked(true);
|
await page.getByLabel('Eager Load Tabs').setChecked(true);
|
||||||
await expect(await page.getByLabel('Eager Load Tabs')).toBeChecked();
|
await expect(page.getByLabel('Eager Load Tabs')).toBeChecked();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -155,8 +155,7 @@ test.describe('Telemetry Table', () => {
|
|||||||
expect(cells.length).toBeGreaterThan(1);
|
expect(cells.length).toBeGreaterThan(1);
|
||||||
// ensure the text content of each cell contains the search term
|
// ensure the text content of each cell contains the search term
|
||||||
for (const cell of cells) {
|
for (const cell of cells) {
|
||||||
const text = await cell.textContent();
|
await expect(cell).toHaveText(/Roger/);
|
||||||
expect(text).toContain('Roger');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await page.getByRole('searchbox', { name: 'message filter input' }).click();
|
await page.getByRole('searchbox', { name: 'message filter input' }).click();
|
||||||
@ -167,15 +166,14 @@ test.describe('Telemetry Table', () => {
|
|||||||
.getByText(/Dodger/)
|
.getByText(/Dodger/)
|
||||||
.all();
|
.all();
|
||||||
// ensure we've got more than one cell
|
// ensure we've got more than one cell
|
||||||
expect(cells.length).toBe(0);
|
expect(cells).toHaveLength(0);
|
||||||
// ensure the text content of each cell contains the search term
|
// ensure the text content of each cell contains the search term
|
||||||
for (const cell of cells) {
|
for (const cell of cells) {
|
||||||
const text = await cell.textContent();
|
await expect(cell).not.toHaveText(/Dodger/);
|
||||||
expect(text).not.toContain('Dodger');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Click pause button
|
// Click pause button
|
||||||
await page.click('button[title="Pause"]');
|
await page.getByLabel('Pause').click();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Supports filtering using Regex', async ({ page }) => {
|
test('Supports filtering using Regex', async ({ page }) => {
|
||||||
@ -201,8 +199,7 @@ test.describe('Telemetry Table', () => {
|
|||||||
expect(cells.length).toBeGreaterThan(1);
|
expect(cells.length).toBeGreaterThan(1);
|
||||||
// ensure the text content of each cell contains the search term
|
// ensure the text content of each cell contains the search term
|
||||||
for (const cell of cells) {
|
for (const cell of cells) {
|
||||||
const text = await cell.textContent();
|
await expect(cell).toHaveText(/Roger/);
|
||||||
expect(text).toContain('Roger');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await page.getByRole('searchbox', { name: 'message filter input' }).click();
|
await page.getByRole('searchbox', { name: 'message filter input' }).click();
|
||||||
@ -213,15 +210,14 @@ test.describe('Telemetry Table', () => {
|
|||||||
.getByText(/Dodger/)
|
.getByText(/Dodger/)
|
||||||
.all();
|
.all();
|
||||||
// ensure we've got more than one cell
|
// ensure we've got more than one cell
|
||||||
expect(cells.length).toBe(0);
|
expect(cells).toHaveLength(0);
|
||||||
// ensure the text content of each cell contains the search term
|
// ensure the text content of each cell contains the search term
|
||||||
for (const cell of cells) {
|
for (const cell of cells) {
|
||||||
const text = await cell.textContent();
|
await expect(cell).not.toHaveText(/Dodger/);
|
||||||
expect(text).not.toContain('Dodger');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Click pause button
|
// Click pause button
|
||||||
await page.click('button[title="Pause"]');
|
await page.getByLabel('Pause').click();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -256,7 +252,6 @@ async function getScrollPosition(page, top = true) {
|
|||||||
scrollHeight: node.scrollHeight
|
scrollHeight: node.scrollHeight
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// eslint-disable-next-line playwright/no-conditional-in-test
|
|
||||||
if (top) {
|
if (top) {
|
||||||
return scrollTop;
|
return scrollTop;
|
||||||
} else {
|
} else {
|
||||||
|
@ -22,16 +22,18 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
createDomainObjectWithDefaults,
|
createDomainObjectWithDefaults,
|
||||||
setIndependentTimeConductorBounds
|
navigateToObjectWithFixedTimeBounds,
|
||||||
|
setFixedIndependentTimeConductorBounds
|
||||||
} from '../../../../appActions.js';
|
} from '../../../../appActions.js';
|
||||||
import { expect, test } from '../../../../pluginFixtures.js';
|
import { expect, test } from '../../../../pluginFixtures.js';
|
||||||
|
|
||||||
const FIXED_TIME =
|
const FIXED_TIME_URL = './#/browse/mine';
|
||||||
'./#/browse/mine?tc.mode=fixed&tc.startBound=1693592063607&tc.endBound=1693593893607&tc.timeSystem=utc&view=grid&hideInspector=true&hideTree=true';
|
|
||||||
test.describe('Datepicker operations', () => {
|
test.describe('Datepicker operations', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await page.goto(FIXED_TIME);
|
await navigateToObjectWithFixedTimeBounds(page, FIXED_TIME_URL, 1693592063607, 1693593893607);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Verify that user can use the datepicker in the TC', async ({ page }) => {
|
test('Verify that user can use the datepicker in the TC', async ({ page }) => {
|
||||||
await page.getByLabel('Time Conductor Mode').click();
|
await page.getByLabel('Time Conductor Mode').click();
|
||||||
// Click on the date picker that is left-most on the screen
|
// Click on the date picker that is left-most on the screen
|
||||||
@ -42,12 +44,13 @@ test.describe('Datepicker operations', () => {
|
|||||||
// Expect datepicker to close and time conductor date setting to be changed
|
// Expect datepicker to close and time conductor date setting to be changed
|
||||||
await expect(page.getByRole('dialog')).toHaveCount(0);
|
await expect(page.getByRole('dialog')).toHaveCount(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Verify that user can use the datepicker in the ITC', async ({ page }) => {
|
test('Verify that user can use the datepicker in the ITC', async ({ page }) => {
|
||||||
const createdTimeList = await createDomainObjectWithDefaults(page, { type: 'Time List' });
|
const createdTimeList = await createDomainObjectWithDefaults(page, { type: 'Time List' });
|
||||||
|
|
||||||
await page.goto(createdTimeList.url, { waitUntil: 'domcontentloaded' });
|
await page.goto(createdTimeList.url, { waitUntil: 'domcontentloaded' });
|
||||||
|
|
||||||
await setIndependentTimeConductorBounds(page, {
|
await setFixedIndependentTimeConductorBounds(page, {
|
||||||
start: '2024-11-12 19:11:11.000Z',
|
start: '2024-11-12 19:11:11.000Z',
|
||||||
end: '2024-11-12 20:11:11.000Z'
|
end: '2024-11-12 20:11:11.000Z'
|
||||||
});
|
});
|
||||||
|
@ -281,29 +281,3 @@ test.describe('Global Time Conductor', () => {
|
|||||||
// select an option and verify the offsets are updated correctly
|
// select an option and verify the offsets are updated correctly
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe('Time Conductor History', () => {
|
|
||||||
test('shows milliseconds on hover @unstable', async ({ page }) => {
|
|
||||||
test.info().annotations.push({
|
|
||||||
type: 'issue',
|
|
||||||
description: 'https://github.com/nasa/openmct/issues/4386'
|
|
||||||
});
|
|
||||||
// Navigate to Open MCT in Fixed Time Mode, UTC Time System
|
|
||||||
// with startBound at 2022-01-01 00:00:00.000Z
|
|
||||||
// and endBound at 2022-01-01 00:00:00.200Z
|
|
||||||
await page.goto(
|
|
||||||
'./#/browse/mine?view=grid&tc.mode=fixed&tc.startBound=1640995200000&tc.endBound=1640995200200&tc.timeSystem=utc&hideInspector=true'
|
|
||||||
);
|
|
||||||
await page.getByRole('button', { name: 'Time Conductor Settings' }).click();
|
|
||||||
await page.getByRole('button', { name: 'Time Conductor History' }).hover({ trial: true });
|
|
||||||
await page.getByRole('button', { name: 'Time Conductor History' }).click();
|
|
||||||
|
|
||||||
// Validate history item format
|
|
||||||
const historyItem = page.locator('text="2022-01-01 00:00:00 + 200ms"');
|
|
||||||
await expect(historyItem).toBeEnabled();
|
|
||||||
await expect(historyItem).toHaveAttribute(
|
|
||||||
'title',
|
|
||||||
'2022-01-01 00:00:00.000 - 2022-01-01 00:00:00.200'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
@ -20,10 +20,7 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import {
|
import { createDomainObjectWithDefaults } from '../../../../appActions.js';
|
||||||
createDomainObjectWithDefaults,
|
|
||||||
openObjectTreeContextMenu
|
|
||||||
} from '../../../../appActions.js';
|
|
||||||
import { MISSION_TIME } from '../../../../constants.js';
|
import { MISSION_TIME } from '../../../../constants.js';
|
||||||
import { expect, test } from '../../../../pluginFixtures.js';
|
import { expect, test } from '../../../../pluginFixtures.js';
|
||||||
|
|
||||||
@ -33,7 +30,6 @@ test.describe('Timer', () => {
|
|||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
timer = await createDomainObjectWithDefaults(page, { type: 'timer' });
|
timer = await createDomainObjectWithDefaults(page, { type: 'timer' });
|
||||||
await assertTimerElements(page, timer);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Can perform actions on the Timer', async ({ page }) => {
|
test('Can perform actions on the Timer', async ({ page }) => {
|
||||||
@ -42,13 +38,11 @@ test.describe('Timer', () => {
|
|||||||
description: 'https://github.com/nasa/openmct/issues/4313'
|
description: 'https://github.com/nasa/openmct/issues/4313'
|
||||||
});
|
});
|
||||||
|
|
||||||
const timerUrl = timer.url;
|
|
||||||
|
|
||||||
await test.step('From the tree context menu', async () => {
|
await test.step('From the tree context menu', async () => {
|
||||||
await triggerTimerContextMenuAction(page, timerUrl, 'Start');
|
await triggerTimerContextMenuAction(page, timer.url, 'Start');
|
||||||
await triggerTimerContextMenuAction(page, timerUrl, 'Pause');
|
await triggerTimerContextMenuAction(page, timer.url, 'Pause');
|
||||||
await triggerTimerContextMenuAction(page, timerUrl, 'Restart at 0');
|
await triggerTimerContextMenuAction(page, timer.url, 'Restart at 0');
|
||||||
await triggerTimerContextMenuAction(page, timerUrl, 'Stop');
|
await triggerTimerContextMenuAction(page, timer.url, 'Stop');
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('From the 3dot menu', async () => {
|
await test.step('From the 3dot menu', async () => {
|
||||||
@ -67,26 +61,18 @@ test.describe('Timer', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test.describe('Timer with target date @clock', () => {
|
test.describe('Timer with target date @clock', () => {
|
||||||
let timer;
|
|
||||||
|
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await page.clock.install({ time: MISSION_TIME });
|
||||||
|
await page.clock.resume();
|
||||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
timer = await createDomainObjectWithDefaults(page, { type: 'timer' });
|
await createDomainObjectWithDefaults(page, { type: 'timer' });
|
||||||
await assertTimerElements(page, timer);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Override clock
|
|
||||||
test.use({
|
|
||||||
clockOptions: {
|
|
||||||
now: MISSION_TIME,
|
|
||||||
shouldAdvanceTime: true
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Can count down to a target date', async ({ page }) => {
|
test('Can count down to a target date', async ({ page }) => {
|
||||||
// Set the target date to 2024-11-24 03:30:00
|
// Set the target date to 2024-11-24 03:30:00
|
||||||
await page.getByTitle('More actions').click();
|
await page.getByTitle('More actions').click();
|
||||||
await page.getByRole('menuitem', { name: /Edit Properties.../ }).click();
|
await page.getByRole('menuitem', { name: /Edit Properties.../ }).click();
|
||||||
|
|
||||||
await page.getByPlaceholder('YYYY-MM-DD').fill('2024-11-24');
|
await page.getByPlaceholder('YYYY-MM-DD').fill('2024-11-24');
|
||||||
await page.locator('input[name="hour"]').fill('3');
|
await page.locator('input[name="hour"]').fill('3');
|
||||||
await page.locator('input[name="min"]').fill('30');
|
await page.locator('input[name="min"]').fill('30');
|
||||||
@ -159,14 +145,13 @@ async function triggerTimerContextMenuAction(page, timerUrl, action) {
|
|||||||
*/
|
*/
|
||||||
async function triggerTimer3dotMenuAction(page, action) {
|
async function triggerTimer3dotMenuAction(page, action) {
|
||||||
const menuAction = `.c-menu ul li >> text="${action}"`;
|
const menuAction = `.c-menu ul li >> text="${action}"`;
|
||||||
const threeDotMenuButton = 'button[title="More actions"]';
|
|
||||||
let isActionAvailable = false;
|
let isActionAvailable = false;
|
||||||
let iterations = 0;
|
let iterations = 0;
|
||||||
// Dismiss/open the 3dot menu until the action is available
|
// Dismiss/open the 3dot menu until the action is available
|
||||||
// or a maximum number of iterations is reached
|
// or a maximum number of iterations is reached
|
||||||
while (!isActionAvailable && iterations <= 20) {
|
while (!isActionAvailable && iterations <= 20) {
|
||||||
await page.click('.c-object-view');
|
await page.getByLabel('Object View').click();
|
||||||
await page.click(threeDotMenuButton);
|
await page.getByLabel('More actions').click();
|
||||||
isActionAvailable = await page.locator(menuAction).isVisible();
|
isActionAvailable = await page.locator(menuAction).isVisible();
|
||||||
iterations++;
|
iterations++;
|
||||||
}
|
}
|
||||||
@ -183,7 +168,7 @@ async function triggerTimer3dotMenuAction(page, action) {
|
|||||||
async function triggerTimerViewAction(page, action) {
|
async function triggerTimerViewAction(page, action) {
|
||||||
await page.locator('.c-timer').hover({ trial: true });
|
await page.locator('.c-timer').hover({ trial: true });
|
||||||
const buttonTitle = buttonTitleFromAction(action);
|
const buttonTitle = buttonTitleFromAction(action);
|
||||||
await page.click(`button[title="${buttonTitle}"]`);
|
await page.getByLabel(buttonTitle, { exact: true }).click();
|
||||||
assertTimerStateAfterAction(page, action);
|
assertTimerStateAfterAction(page, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,11 +199,11 @@ async function assertTimerStateAfterAction(page, action) {
|
|||||||
case 'Start':
|
case 'Start':
|
||||||
case 'Restart at 0':
|
case 'Restart at 0':
|
||||||
timerStateClass = 'is-started';
|
timerStateClass = 'is-started';
|
||||||
expect(await timerValue.innerText()).toBe('0D 00:00:00');
|
await expect(timerValue).toHaveText('0D 00:00:00');
|
||||||
break;
|
break;
|
||||||
case 'Stop':
|
case 'Stop':
|
||||||
timerStateClass = 'is-stopped';
|
timerStateClass = 'is-stopped';
|
||||||
expect(await timerValue.innerText()).toBe('--:--:--');
|
await expect(timerValue).toHaveText('--:--:--');
|
||||||
break;
|
break;
|
||||||
case 'Pause':
|
case 'Pause':
|
||||||
timerStateClass = 'is-paused';
|
timerStateClass = 'is-paused';
|
||||||
@ -229,23 +214,16 @@ async function assertTimerStateAfterAction(page, action) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assert that all the major components of a timer are present in the DOM.
|
* Open the given `domainObject`'s context menu from the object tree.
|
||||||
|
* Expands the path to the object and scrolls to it if necessary.
|
||||||
|
*
|
||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
* @param {import('../../../../appActions').CreatedObjectInfo} timer
|
* @param {string} url the url to the object
|
||||||
*/
|
*/
|
||||||
async function assertTimerElements(page, timer) {
|
async function openObjectTreeContextMenu(page, url) {
|
||||||
const timerElement = page.locator('.c-timer');
|
await page.goto(url);
|
||||||
const resetButton = page.getByRole('button', { name: 'Reset' });
|
await page.getByLabel('Show selected item in tree').click();
|
||||||
const pausePlayButton = page
|
await page.locator('.is-navigated-object').click({
|
||||||
.getByRole('button', { name: 'Pause' })
|
button: 'right'
|
||||||
.or(page.getByRole('button', { name: 'Start' }));
|
});
|
||||||
const timerDirectionIcon = page.locator('.c-timer__direction');
|
|
||||||
const timerValue = page.locator('.c-timer__value');
|
|
||||||
|
|
||||||
expect(await page.locator('.l-browse-bar__object-name').innerText()).toBe(timer.name);
|
|
||||||
expect(timerElement).toBeAttached();
|
|
||||||
expect(resetButton).toBeAttached();
|
|
||||||
expect(pausePlayButton).toBeAttached();
|
|
||||||
expect(timerDirectionIcon).toBeAttached();
|
|
||||||
expect(timerValue).toBeAttached();
|
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ test.describe('Recent Objects', () => {
|
|||||||
).toBeGreaterThan(0);
|
).toBeGreaterThan(0);
|
||||||
expect(recentObjectsList.getByRole('listitem', { name: folderA.name })).toBeTruthy();
|
expect(recentObjectsList.getByRole('listitem', { name: folderA.name })).toBeTruthy();
|
||||||
|
|
||||||
await page.click('button[title="Show selected item in tree"]');
|
await page.getByLabel('Show selected item in tree').click();
|
||||||
// Delete the folder via the left tree pane treeitem context menu
|
// Delete the folder via the left tree pane treeitem context menu
|
||||||
await page
|
await page
|
||||||
.getByRole('treeitem', { name: new RegExp(folderA.name) })
|
.getByRole('treeitem', { name: new RegExp(folderA.name) })
|
||||||
@ -104,7 +104,7 @@ test.describe('Recent Objects', () => {
|
|||||||
button: 'right'
|
button: 'right'
|
||||||
});
|
});
|
||||||
await page.getByRole('menuitem', { name: /Remove/ }).click();
|
await page.getByRole('menuitem', { name: /Remove/ }).click();
|
||||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
await page.getByRole('button', { name: 'Ok', exact: true }).click();
|
||||||
|
|
||||||
// Verify that the folder and clock are no longer in the recent objects list
|
// Verify that the folder and clock are no longer in the recent objects list
|
||||||
await expect(recentObjectsList.getByRole('listitem', { name: folderA.name })).toBeHidden();
|
await expect(recentObjectsList.getByRole('listitem', { name: folderA.name })).toBeHidden();
|
||||||
@ -227,24 +227,22 @@ test.describe('Recent Objects', () => {
|
|||||||
.click();
|
.click();
|
||||||
|
|
||||||
// Assert that two recent objects are displayed and one of them is an alias
|
// Assert that two recent objects are displayed and one of them is an alias
|
||||||
expect(await recentObjectsList.getByRole('listitem', { name: clock.name }).count()).toBe(2);
|
await expect(recentObjectsList.getByRole('listitem', { name: clock.name })).toHaveCount(2);
|
||||||
expect(await recentObjectsList.locator('.is-alias').count()).toBe(1);
|
await expect(recentObjectsList.locator('.is-alias')).toHaveCount(1);
|
||||||
|
|
||||||
// Assert that the alias and the original's breadcrumbs are different
|
// Assert that the alias and the original's breadcrumbs are different
|
||||||
const clockBreadcrumbs = recentObjectsList
|
const clockBreadcrumbs = recentObjectsList
|
||||||
.getByRole('listitem', { name: clock.name })
|
.getByRole('listitem', { name: clock.name })
|
||||||
.getByRole('navigation');
|
.getByRole('navigation');
|
||||||
expect(await clockBreadcrumbs.count()).toBe(2);
|
await expect(clockBreadcrumbs).toHaveCount(2);
|
||||||
expect(await clockBreadcrumbs.nth(0).innerText()).not.toEqual(
|
await expect(clockBreadcrumbs.nth(0)).not.toHaveText(await clockBreadcrumbs.nth(1).innerText());
|
||||||
await clockBreadcrumbs.nth(1).innerText()
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
test('Enforces a limit of 20 recent objects and clears the recent objects', async ({ page }) => {
|
test('Enforces a limit of 20 recent objects and clears the recent objects', async ({ page }) => {
|
||||||
// Creating 21 objects takes a while, so increase the timeout
|
// Creating 21 objects takes a while, so increase the timeout
|
||||||
test.slow();
|
test.slow();
|
||||||
|
|
||||||
// Assert that the list initially contains 3 objects (clock, folder, my items)
|
// Assert that the list initially contains 3 objects (clock, folder, my items)
|
||||||
expect(await recentObjectsList.locator('.c-recentobjects-listitem').count()).toBe(3);
|
await expect(recentObjectsList.locator('.c-recentobjects-listitem')).toHaveCount(3);
|
||||||
|
|
||||||
let lastFolder;
|
let lastFolder;
|
||||||
let lastClock;
|
let lastClock;
|
||||||
@ -261,7 +259,7 @@ test.describe('Recent Objects', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Assert that the list contains 20 objects
|
// Assert that the list contains 20 objects
|
||||||
expect(await recentObjectsList.locator('.c-recentobjects-listitem').count()).toBe(20);
|
await expect(recentObjectsList.locator('.c-recentobjects-listitem')).toHaveCount(20);
|
||||||
|
|
||||||
// Collapse the tree
|
// Collapse the tree
|
||||||
await page.getByTitle('Collapse all tree items').click();
|
await page.getByTitle('Collapse all tree items').click();
|
||||||
@ -293,44 +291,38 @@ test.describe('Recent Objects', () => {
|
|||||||
await page.getByRole('button', { name: 'Clear Recently Viewed' }).click();
|
await page.getByRole('button', { name: 'Clear Recently Viewed' }).click();
|
||||||
|
|
||||||
// Click on the "OK" button in the confirmation dialog
|
// Click on the "OK" button in the confirmation dialog
|
||||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
await page.getByRole('button', { name: 'Ok', exact: true }).click();
|
||||||
|
|
||||||
// Assert that the list is empty
|
// Assert that the list is empty
|
||||||
expect(await recentObjectsList.locator('.c-recentobjects-listitem').count()).toBe(0);
|
await expect(recentObjectsList.locator('.c-recentobjects-listitem')).toHaveCount(0);
|
||||||
});
|
});
|
||||||
test('Verify functionality of "clear" and "collapse pane" buttons', async ({ page }) => {
|
test('Verify functionality of "clear" and "collapse pane" buttons', async ({ page }) => {
|
||||||
// Assert that the list initially contains 3 objects (clock, folder, my items)
|
// Assert that the list initially contains 3 objects (clock, folder, my items)
|
||||||
expect(await recentObjectsList.locator('.c-recentobjects-listitem').count()).toBe(3);
|
await expect(recentObjectsList.locator('.c-recentobjects-listitem')).toHaveCount(3);
|
||||||
|
|
||||||
// Assert that the button is enabled
|
// Assert that the button is enabled
|
||||||
expect(await page.getByRole('button', { name: 'Clear Recently Viewed' }).isEnabled()).toBe(
|
await expect(page.getByRole('button', { name: 'Clear Recently Viewed' })).toBeEnabled();
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
// Click the aria-label="Clear Recently Viewed" button
|
// Click the aria-label="Clear Recently Viewed" button
|
||||||
await page.getByRole('button', { name: 'Clear Recently Viewed' }).click();
|
await page.getByRole('button', { name: 'Clear Recently Viewed' }).click();
|
||||||
|
|
||||||
// Click on the "OK" button in the confirmation dialog
|
// Click on the "OK" button in the confirmation dialog
|
||||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
await page.getByRole('button', { name: 'Ok', exact: true }).click();
|
||||||
|
|
||||||
// Assert that the list is empty
|
// Assert that the list is empty
|
||||||
expect(await recentObjectsList.locator('.c-recentobjects-listitem').count()).toBe(0);
|
await expect(recentObjectsList.locator('.c-recentobjects-listitem')).toHaveCount(0);
|
||||||
|
|
||||||
// Assert that the button is disabled
|
// Assert that the button is disabled
|
||||||
expect(await page.getByRole('button', { name: 'Clear Recently Viewed' }).isEnabled()).toBe(
|
await expect(page.getByRole('button', { name: 'Clear Recently Viewed' })).toBeDisabled();
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
// Navigate to folder object
|
// Navigate to folder object
|
||||||
await page.goto(folderA.url);
|
await page.goto(folderA.url);
|
||||||
|
|
||||||
// Assert that the list contains 1 object
|
// Assert that the list contains 1 object
|
||||||
expect(await recentObjectsList.locator('.c-recentobjects-listitem').count()).toBe(1);
|
await expect(recentObjectsList.locator('.c-recentobjects-listitem')).toHaveCount(1);
|
||||||
|
|
||||||
// Assert that the button is enabled
|
// Assert that the button is enabled
|
||||||
expect(await page.getByRole('button', { name: 'Clear Recently Viewed' }).isEnabled()).toBe(
|
await expect(page.getByRole('button', { name: 'Clear Recently Viewed' })).toBeEnabled();
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
// Assert initial state of pane and collapse the Recent Objects panel
|
// Assert initial state of pane and collapse the Recent Objects panel
|
||||||
await expect(page.getByLabel('Expand Recently Viewed Pane')).toBeHidden();
|
await expect(page.getByLabel('Expand Recently Viewed Pane')).toBeHidden();
|
||||||
|
@ -24,13 +24,13 @@
|
|||||||
This test suite is dedicated to tests for renaming objects, and their global application effects.
|
This test suite is dedicated to tests for renaming objects, and their global application effects.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createDomainObjectWithDefaults, renameObjectFromContextMenu } from '../../appActions.js';
|
import { createDomainObjectWithDefaults } from '../../appActions.js';
|
||||||
import { expect, test } from '../../baseFixtures.js';
|
import { expect, test } from '../../baseFixtures.js';
|
||||||
|
|
||||||
test.describe('Renaming objects', () => {
|
test.describe('Renaming objects', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
// Go to baseURL
|
// 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 ({
|
test('When renaming objects, the browse bar and various components all update', async ({
|
||||||
@ -73,3 +73,33 @@ test.describe('Renaming objects', () => {
|
|||||||
expect(title).toBe(clock.name);
|
expect(title).toBe(clock.name);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('@playwright/test').Page} page
|
||||||
|
* @param {string} myItemsFolderName
|
||||||
|
* @param {string} url
|
||||||
|
* @param {string} newName
|
||||||
|
*/
|
||||||
|
async function renameObjectFromContextMenu(page, url, newName) {
|
||||||
|
await openObjectTreeContextMenu(page, url);
|
||||||
|
await page.locator('li:text("Edit Properties")').click();
|
||||||
|
const nameInput = page.getByLabel('Title', { exact: true });
|
||||||
|
await nameInput.fill('');
|
||||||
|
await nameInput.fill(newName);
|
||||||
|
await page.locator('[aria-label="Save"]').click();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the given `domainObject`'s context menu from the object tree.
|
||||||
|
* Expands the path to the object and scrolls to it if necessary.
|
||||||
|
*
|
||||||
|
* @param {import('@playwright/test').Page} page
|
||||||
|
* @param {string} url the url to the object
|
||||||
|
*/
|
||||||
|
async function openObjectTreeContextMenu(page, url) {
|
||||||
|
await page.goto(url);
|
||||||
|
await page.getByLabel('Show selected item in tree').click();
|
||||||
|
await page.locator('.is-navigated-object').click({
|
||||||
|
button: 'right'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -162,7 +162,7 @@ test.describe('Grand Search', () => {
|
|||||||
const searchResults = page.getByRole('listitem', { name: 'Object Search Result' });
|
const searchResults = page.getByRole('listitem', { name: 'Object Search Result' });
|
||||||
|
|
||||||
// Verify that no results are found
|
// Verify that no results are found
|
||||||
expect(await searchResults.count()).toBe(0);
|
await expect(searchResults).toHaveCount(0);
|
||||||
|
|
||||||
// Verify proper message appears
|
// Verify proper message appears
|
||||||
await expect(page.getByText('No results found')).toBeVisible();
|
await expect(page.getByText('No results found')).toBeVisible();
|
||||||
@ -187,7 +187,7 @@ test.describe('Grand Search', () => {
|
|||||||
|
|
||||||
// Verify that one result is found
|
// Verify that one result is found
|
||||||
await expect(searchResults).toBeVisible();
|
await expect(searchResults).toBeVisible();
|
||||||
expect(await searchResults.count()).toBe(1);
|
await expect(searchResults).toHaveCount(1);
|
||||||
await expect(searchResults).toContainText(folderName);
|
await expect(searchResults).toContainText(folderName);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -216,7 +216,7 @@ test.describe('Grand Search', () => {
|
|||||||
|
|
||||||
// Network requests for the composite telemetry with multiple items should be:
|
// Network requests for the composite telemetry with multiple items should be:
|
||||||
// 1. batched request for latest telemetry using the bulk API
|
// 1. batched request for latest telemetry using the bulk API
|
||||||
expect(networkRequests.length).toBe(1);
|
await expect.poll(() => networkRequests, { timeout: 10000 }).toHaveLength(1);
|
||||||
|
|
||||||
await expect(page.getByRole('list', { name: 'Object Results' })).toContainText('Clock A');
|
await expect(page.getByRole('list', { name: 'Object Results' })).toContainText('Clock A');
|
||||||
});
|
});
|
||||||
@ -282,7 +282,7 @@ test.describe('Grand Search', () => {
|
|||||||
// Get the search results
|
// Get the search results
|
||||||
const objectSearchResults = page.getByLabel('Object Search Result');
|
const objectSearchResults = page.getByLabel('Object Search Result');
|
||||||
// Verify that two results are found
|
// Verify that two results are found
|
||||||
expect(await objectSearchResults.count()).toBe(2);
|
await expect(objectSearchResults).toHaveCount(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -29,8 +29,6 @@ test.describe('Staleness', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Does not show staleness after navigating from a stale object', async ({ page }) => {
|
test('Does not show staleness after navigating from a stale object', async ({ page }) => {
|
||||||
const objectViewSelector = '.c-object-view';
|
|
||||||
const isStaleClass = 'is-stale';
|
|
||||||
const staleSWG = await createDomainObjectWithDefaults(page, {
|
const staleSWG = await createDomainObjectWithDefaults(page, {
|
||||||
type: 'Sine Wave Generator',
|
type: 'Sine Wave Generator',
|
||||||
name: 'SWG'
|
name: 'SWG'
|
||||||
@ -51,7 +49,7 @@ test.describe('Staleness', () => {
|
|||||||
await navigateToObjectWithRealTime(page, staleSWG.url);
|
await navigateToObjectWithRealTime(page, staleSWG.url);
|
||||||
|
|
||||||
// Assert that staleness is shown
|
// Assert that staleness is shown
|
||||||
await expect(page.locator(`${objectViewSelector} .${isStaleClass}`)).toBeAttached({
|
await expect(page.getByLabel('Object View')).toHaveClass(/is-stale/, {
|
||||||
timeout: 30 * 1000 // Give 30 seconds for the staleness to be updated
|
timeout: 30 * 1000 // Give 30 seconds for the staleness to be updated
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -59,6 +57,6 @@ test.describe('Staleness', () => {
|
|||||||
await page.goto(folder.url);
|
await page.goto(folder.url);
|
||||||
|
|
||||||
// Verify that staleness is not shown
|
// Verify that staleness is not shown
|
||||||
await expect(page.locator(`${objectViewSelector} .${isStaleClass}`)).not.toBeAttached();
|
await expect(page.getByLabel('Object View')).not.toHaveClass(/is-stale/);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -21,19 +21,11 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This test suite is dedicated to tests which can quickly verify that any openmct installation is
|
This suite is dedicated to tests which verify that tooltips are displayed correctly.
|
||||||
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.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createDomainObjectWithDefaults, expandEntireTree } from '../../appActions.js';
|
import { createDomainObjectWithDefaults, expandEntireTree } from '../../appActions.js';
|
||||||
|
import { MISSION_TIME } from '../../constants.js';
|
||||||
import { expect, test } from '../../pluginFixtures.js';
|
import { expect, test } from '../../pluginFixtures.js';
|
||||||
|
|
||||||
test.describe('Verify tooltips', () => {
|
test.describe('Verify tooltips', () => {
|
||||||
@ -48,7 +40,7 @@ test.describe('Verify tooltips', () => {
|
|||||||
const swg2Path = 'My Items / Folder Foo / Folder Bar / SWG 2';
|
const swg2Path = 'My Items / Folder Foo / Folder Bar / SWG 2';
|
||||||
const swg3Path = 'My Items / Folder Foo / Folder Bar / Folder Baz / SWG 3';
|
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' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
|
|
||||||
folder1 = await createDomainObjectWithDefaults(page, {
|
folder1 = await createDomainObjectWithDefaults(page, {
|
||||||
@ -89,7 +81,7 @@ test.describe('Verify tooltips', () => {
|
|||||||
await expandEntireTree(page);
|
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
|
// Create LAD table
|
||||||
await createDomainObjectWithDefaults(page, {
|
await createDomainObjectWithDefaults(page, {
|
||||||
type: 'LAD Table',
|
type: 'LAD Table',
|
||||||
@ -98,25 +90,32 @@ test.describe('Verify tooltips', () => {
|
|||||||
// Edit LAD table
|
// Edit LAD table
|
||||||
await page.getByLabel('Edit Object').click();
|
await page.getByLabel('Edit Object').click();
|
||||||
|
|
||||||
// Add the Sine Wave Generator to the LAD table and save changes
|
// Add the Sine Wave Generator to the LAD table and save changes.
|
||||||
await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.c-lad-table-wrapper');
|
//TODO Follow up with https://github.com/nasa/openmct/issues/7773
|
||||||
await page.dragAndDrop(`text=${sineWaveObject2.name}`, '.c-lad-table-wrapper');
|
await page.getByLabel(`Preview ${sineWaveObject1.name}`).dragTo(page.getByLabel('Object View'));
|
||||||
await page.dragAndDrop(`text=${sineWaveObject3.name}`, '.c-lad-table-wrapper');
|
await page.getByLabel(`Preview ${sineWaveObject2.name}`).dragTo(page.getByLabel('Object View'));
|
||||||
await page.locator('button[title="Save"]').click();
|
await page.getByLabel(`Preview ${sineWaveObject3.name}`).dragTo(page.getByLabel('Object View'));
|
||||||
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
|
|
||||||
await page.keyboard.down('Control');
|
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) {
|
//Hover on something else
|
||||||
await page.locator('.c-create-button').hover();
|
await page.getByRole('button', { name: 'Create' }).hover();
|
||||||
await page.getByLabel('lad name').getByText(object.name).hover();
|
//Hover over second object
|
||||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
await page.getByLabel('lad name').getByText(sineWaveObject2.name).hover();
|
||||||
return tooltipText.replace('\n', '').trim();
|
await expect(page.getByRole('tooltip', { name: sineWaveObject2.path })).toBeVisible();
|
||||||
}
|
|
||||||
|
|
||||||
expect(await getToolTip(sineWaveObject1)).toBe(sineWaveObject1.path);
|
//Hover on something else
|
||||||
expect(await getToolTip(sineWaveObject2)).toBe(sineWaveObject2.path);
|
await page.getByRole('button', { name: 'Create' }).hover();
|
||||||
expect(await getToolTip(sineWaveObject3)).toBe(sineWaveObject3.path);
|
//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 }) => {
|
test('display correct paths for expanded and collapsed plot legend items', async ({ page }) => {
|
||||||
@ -128,66 +127,74 @@ test.describe('Verify tooltips', () => {
|
|||||||
// Edit Overlay Plot
|
// Edit Overlay Plot
|
||||||
await page.getByLabel('Edit Object').click();
|
await page.getByLabel('Edit Object').click();
|
||||||
|
|
||||||
// Add the Sine Wave Generator to the LAD table and save changes
|
// Add the Sine Wave Generators to the and save changes
|
||||||
await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.gl-plot');
|
await page
|
||||||
await page.dragAndDrop(`text=${sineWaveObject2.name}`, '.gl-plot');
|
.getByLabel('Preview SWG 1 generator Object')
|
||||||
await page.dragAndDrop(`text=${sineWaveObject3.name}`, '.gl-plot');
|
.dragTo(page.getByLabel('Plot Container Style Target'));
|
||||||
await page.locator('button[title="Save"]').click();
|
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();
|
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');
|
await page.keyboard.down('Control');
|
||||||
|
//Hover over first object
|
||||||
async function getCollapsedLegendToolTip(object) {
|
await page.getByText('SWG 1 Hz').hover();
|
||||||
await page.locator('.c-create-button').hover();
|
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject1.path);
|
||||||
await page
|
//Hover over another object to clear
|
||||||
.locator('.plot-series-name', { has: page.locator(`text="${object.name} Hz"`) })
|
await page.getByRole('button', { name: 'create' }).hover();
|
||||||
.hover();
|
//Hover over second object
|
||||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
await page.getByText('SWG 2 Hz').hover();
|
||||||
return tooltipText.replace('\n', '').trim();
|
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject2.path);
|
||||||
}
|
//Hover over another object to clear
|
||||||
|
await page.getByRole('button', { name: 'create' }).hover();
|
||||||
async function getExpandedLegendToolTip(object) {
|
//Hover over third object
|
||||||
await page.locator('.c-create-button').hover();
|
await page.getByText('SWG 3 Hz').hover();
|
||||||
await page
|
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject3.path);
|
||||||
.locator('.plot-series-name', { has: page.locator(`text="${object.name}"`) })
|
//Release the Control Key
|
||||||
.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);
|
|
||||||
|
|
||||||
await page.keyboard.up('Control');
|
await page.keyboard.up('Control');
|
||||||
|
|
||||||
|
//Expand the legend
|
||||||
await page.locator('.gl-plot-legend__view-control.c-disclosure-triangle').click();
|
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');
|
await page.keyboard.down('Control');
|
||||||
|
|
||||||
expect(await getExpandedLegendToolTip(sineWaveObject1)).toBe(sineWaveObject1.path);
|
await page.getByLabel('Plot Legend Expanded').getByText('SWG 1').hover();
|
||||||
expect(await getExpandedLegendToolTip(sineWaveObject2)).toBe(sineWaveObject2.path);
|
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject1.path);
|
||||||
expect(await getExpandedLegendToolTip(sineWaveObject3)).toBe(sineWaveObject3.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 }) => {
|
test('display correct paths when hovering over object labels', async ({ page }) => {
|
||||||
async function getObjectLabelTooltip(object) {
|
//Navigate to SWG 1 in Tree
|
||||||
await page
|
await page.getByLabel('Navigate to SWG 1 generator').click();
|
||||||
.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();
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await getObjectLabelTooltip(sineWaveObject1)).toBe(sineWaveObject1.path);
|
//Expect tooltip to be the path of SWG 1
|
||||||
expect(await getObjectLabelTooltip(sineWaveObject3)).toBe(sineWaveObject3.path);
|
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 }) => {
|
test('display correct paths when hovering over display layout pane headers', async ({ page }) => {
|
||||||
@ -198,8 +205,11 @@ test.describe('Verify tooltips', () => {
|
|||||||
});
|
});
|
||||||
// Edit Overlay Plot
|
// Edit Overlay Plot
|
||||||
await page.getByLabel('Edit Object').click();
|
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();
|
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
|
|
||||||
// Create Stacked Plot
|
// Create Stacked Plot
|
||||||
@ -209,8 +219,9 @@ test.describe('Verify tooltips', () => {
|
|||||||
});
|
});
|
||||||
// Edit Stacked Plot
|
// Edit Stacked Plot
|
||||||
await page.getByLabel('Edit Object').click();
|
await page.getByLabel('Edit Object').click();
|
||||||
await page.dragAndDrop(`text=${sineWaveObject2.name}`, '.c-plot--stacked.holder');
|
|
||||||
await page.locator('button[title="Save"]').click();
|
await page.getByLabel(`Preview ${sineWaveObject2.name}`).dragTo(page.getByLabel('Object View'));
|
||||||
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
|
|
||||||
// Create Display Layout
|
// Create Display Layout
|
||||||
@ -221,66 +232,77 @@ test.describe('Verify tooltips', () => {
|
|||||||
// Edit Display Layout
|
// Edit Display Layout
|
||||||
await page.getByLabel('Edit Object').click();
|
await page.getByLabel('Edit Object').click();
|
||||||
|
|
||||||
await page.dragAndDrop("text='Test Overlay Plot'", '.l-layout__grid-holder', {
|
await page
|
||||||
targetPosition: { x: 0, y: 0 }
|
.getByLabel('Preview Test Overlay Plot')
|
||||||
});
|
.dragTo(page.locator('#display-layout-drop-area'), {
|
||||||
await page.dragAndDrop("text='Test Stacked Plot'", '.l-layout__grid-holder', {
|
targetPosition: { x: 0, y: 0 }
|
||||||
targetPosition: { x: 0, y: 250 }
|
});
|
||||||
});
|
|
||||||
await page.dragAndDrop(`text=${sineWaveObject3.name}`, '.l-layout__grid-holder', {
|
//Add Display Layout below Overlay Plot
|
||||||
targetPosition: { x: 500, y: 200 }
|
await page
|
||||||
});
|
.getByLabel('Preview Test Stacked Plot')
|
||||||
await page.locator('button[title="Save"]').click();
|
.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();
|
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.keyboard.down('Control');
|
||||||
|
|
||||||
await page.getByText('Test Overlay Plot').nth(2).hover();
|
//Hover Overlay Plot
|
||||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
await page.getByTitle('Test Overlay Plot').hover();
|
||||||
tooltipText = tooltipText.replace('\n', '').trim();
|
await expect(page.getByRole('tooltip')).toHaveText('My Items / Test Overlay Plot');
|
||||||
expect(tooltipText).toBe('My Items / Test Overlay Plot');
|
|
||||||
|
|
||||||
await page.keyboard.up('Control');
|
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.keyboard.down('Control');
|
||||||
await page.locator('.plot-wrapper-expanded-legend .plot-series-name').first().hover();
|
await page.getByLabel('Plot Legend Item for Test').getByText('SWG').hover();
|
||||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject1.path);
|
||||||
tooltipText = tooltipText.replace('\n', '').trim();
|
|
||||||
expect(tooltipText).toBe(sineWaveObject1.path);
|
|
||||||
|
|
||||||
await page.getByText('Test Stacked Plot').nth(2).hover();
|
//Hover over Stacked Plot Title
|
||||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
await page.getByTitle('Test Stacked Plot').hover();
|
||||||
tooltipText = tooltipText.replace('\n', '').trim();
|
await expect(page.getByRole('tooltip')).toHaveText('My Items / Test Stacked Plot');
|
||||||
expect(tooltipText).toBe('My Items / Test Stacked Plot');
|
|
||||||
|
|
||||||
await page.getByText('SWG 3').nth(2).hover();
|
//Hover over SWG3 Object
|
||||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
await page.getByLabel('Alpha-numeric telemetry name for SWG').hover();
|
||||||
tooltipText = tooltipText.replace('\n', '').trim();
|
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject3.path);
|
||||||
expect(sineWaveObject3.path).toBe(tooltipText);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('display correct paths when hovering over flexible object labels', async ({ page }) => {
|
test('display correct paths when hovering over flexible object labels', async ({ page }) => {
|
||||||
|
//Create Flexible Layout
|
||||||
await createDomainObjectWithDefaults(page, {
|
await createDomainObjectWithDefaults(page, {
|
||||||
type: 'Flexible Layout',
|
type: 'Flexible Layout',
|
||||||
name: 'Test Flexible Layout'
|
name: 'Test Flexible Layout'
|
||||||
});
|
});
|
||||||
|
|
||||||
await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.c-fl-container >> nth=0');
|
//Add SWG1 and SWG3 to Flexible Layout
|
||||||
await page.dragAndDrop(`text=${sineWaveObject3.name}`, '.c-fl-container >> nth=1');
|
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();
|
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
|
|
||||||
|
//Hover over SWG1 Object
|
||||||
await page.keyboard.down('Control');
|
await page.keyboard.down('Control');
|
||||||
await page.getByText('SWG 1').nth(2).hover();
|
await page.getByTitle('SWG 1').hover();
|
||||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject1.path);
|
||||||
tooltipText = tooltipText.replace('\n', '').trim();
|
|
||||||
expect(tooltipText).toBe(sineWaveObject1.path);
|
|
||||||
|
|
||||||
await page.getByText('SWG 3').nth(2).hover();
|
//Hover over SWG3 Object
|
||||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
await page.getByTitle('SWG 3').hover();
|
||||||
tooltipText = tooltipText.replace('\n', '').trim();
|
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject3.path);
|
||||||
expect(tooltipText).toBe(sineWaveObject3.path);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('display correct paths when hovering over tab view labels', async ({ page }) => {
|
test('display correct paths when hovering over tab view labels', async ({ page }) => {
|
||||||
@ -289,46 +311,40 @@ test.describe('Verify tooltips', () => {
|
|||||||
name: 'Test Tabs View'
|
name: 'Test Tabs View'
|
||||||
});
|
});
|
||||||
|
|
||||||
await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.c-tabs-view__tabs-holder');
|
//Add SWG1 and SWG3 to Flexible Layout
|
||||||
await page.dragAndDrop(`text=${sineWaveObject3.name}`, '.c-tabs-view__tabs-holder');
|
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.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
|
|
||||||
await page.keyboard.down('Control');
|
await page.keyboard.down('Control');
|
||||||
await page.getByText('SWG 1').nth(2).hover();
|
await page.getByLabel('SWG 1 tab').getByText('SWG').hover();
|
||||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
|
||||||
tooltipText = tooltipText.replace('\n', '').trim();
|
|
||||||
expect(tooltipText).toBe(sineWaveObject1.path);
|
|
||||||
|
|
||||||
await page.getByText('SWG 3').nth(2).hover();
|
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject1.path);
|
||||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
|
||||||
tooltipText = tooltipText.replace('\n', '').trim();
|
await page.getByLabel('SWG 3 tab').getByText('SWG').hover();
|
||||||
expect(tooltipText).toBe(sineWaveObject3.path);
|
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject3.path);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('display correct paths when hovering tree items', async ({ page }) => {
|
test('display correct paths when hovering tree items', async ({ page }) => {
|
||||||
await page.keyboard.down('Control');
|
await page.keyboard.down('Control');
|
||||||
await page.getByText('SWG 1').nth(0).hover();
|
await page.getByText('SWG 1').first().hover();
|
||||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject1.path);
|
||||||
tooltipText = tooltipText.replace('\n', '').trim();
|
|
||||||
expect(tooltipText).toBe(sineWaveObject1.path);
|
|
||||||
|
|
||||||
await page.getByText('SWG 3').nth(0).hover();
|
await page.getByText('SWG 3').first().hover();
|
||||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject3.path);
|
||||||
tooltipText = tooltipText.replace('\n', '').trim();
|
|
||||||
expect(tooltipText).toBe(sineWaveObject3.path);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('display correct paths when hovering search items', async ({ page }) => {
|
test('display correct paths when hovering search items', async ({ page }) => {
|
||||||
await page.getByRole('searchbox', { name: 'Search Input' }).click();
|
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.keyboard.down('Control');
|
||||||
await page.locator('.c-gsearch-result__title').hover();
|
await page.getByLabel('Object Results').getByText('SWG').hover();
|
||||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject3.path);
|
||||||
tooltipText = tooltipText.replace('\n', '').trim();
|
|
||||||
expect(tooltipText).toBe(sineWaveObject3.path);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('display path for source telemetry when hovering over gauge', async ({ page }) => {
|
test('display path for source telemetry when hovering over gauge', async ({ page }) => {
|
||||||
@ -336,13 +352,14 @@ test.describe('Verify tooltips', () => {
|
|||||||
type: 'Gauge',
|
type: 'Gauge',
|
||||||
name: 'Test 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');
|
await page.keyboard.down('Control');
|
||||||
|
// FIXME: We shouldn't need a `force: true` here, but the parent
|
||||||
|
// element blocks
|
||||||
// eslint-disable-next-line playwright/no-force-option
|
// eslint-disable-next-line playwright/no-force-option
|
||||||
await page.locator('.c-gauge.c-dial').hover({ position: { x: 0, y: 0 }, force: true });
|
await page.getByRole('meter').hover({ position: { x: 0, y: 0 }, force: true });
|
||||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject3.path);
|
||||||
tooltipText = tooltipText.replace('\n', '').trim();
|
|
||||||
expect(tooltipText).toBe(sineWaveObject3.path);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('display tooltip path for notebook embeds', async ({ page }) => {
|
test('display tooltip path for notebook embeds', async ({ page }) => {
|
||||||
@ -351,78 +368,72 @@ test.describe('Verify tooltips', () => {
|
|||||||
name: 'Test Notebook'
|
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.keyboard.down('Control');
|
||||||
await page.locator('.c-ne__embed').hover();
|
await page.getByLabel('SWG 3 Notebook Embed').hover();
|
||||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject3.path);
|
||||||
tooltipText = tooltipText.replace('\n', '').trim();
|
|
||||||
expect(tooltipText).toBe(sineWaveObject3.path);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test.fixme('display tooltip path for telemetry table names', async ({ page }) => {
|
test('display tooltip path for telemetry table names @clock', async ({ page }) => {
|
||||||
test.info().annotations.push({
|
await page.clock.install({ time: MISSION_TIME });
|
||||||
type: 'issue',
|
await page.clock.resume();
|
||||||
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('!', '#'));
|
|
||||||
|
|
||||||
await createDomainObjectWithDefaults(page, {
|
await createDomainObjectWithDefaults(page, {
|
||||||
type: 'Telemetry Table',
|
type: 'Telemetry Table',
|
||||||
name: 'Test Telemetry Table'
|
name: 'Test Telemetry Table'
|
||||||
});
|
});
|
||||||
|
|
||||||
await page.dragAndDrop(`text=${sineWaveObject1.name}`, '.c-telemetry-table');
|
await page
|
||||||
await page.dragAndDrop(`text=${sineWaveObject3.name}`, '.c-telemetry-table');
|
.getByLabel(`Navigate to ${sineWaveObject1.name}`)
|
||||||
|
.dragTo(page.getByLabel('Object View'));
|
||||||
|
await page.getByLabel(`Preview ${sineWaveObject3.name}`).dragTo(page.getByLabel('Object View'));
|
||||||
|
|
||||||
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.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
|
|
||||||
|
// Confirm that telemetry rows exist for SWG 1 and 3 and are in view
|
||||||
|
await expect(page.getByLabel('name table cell SWG 1').first()).toBeInViewport();
|
||||||
|
await expect(page.getByLabel('name table cell SWG 3').first()).toBeInViewport();
|
||||||
|
|
||||||
|
// Pause to prevent more telemetry from streaming in
|
||||||
|
await page.clock.pauseAt(MISSION_TIME + 30 * 1000);
|
||||||
|
// Run for 30 seconds to allow SOME telemetry to stream in
|
||||||
|
await page.clock.runFor(30 * 1000);
|
||||||
|
|
||||||
await page.keyboard.down('Control');
|
await page.keyboard.down('Control');
|
||||||
|
// Hover over SWG3 in Telemetry Table
|
||||||
|
await page.getByLabel('name table cell SWG 3').first().hover();
|
||||||
|
await expect(page.getByRole('tooltip', { name: sineWaveObject3.path })).toBeVisible();
|
||||||
|
|
||||||
await page.locator('.noselect > [title="SWG 3"]').first().hover();
|
// Release Control Key
|
||||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
await page.keyboard.up('Control');
|
||||||
tooltipText = tooltipText.replace('\n', '').trim();
|
// Hover somewhere else so the tooltip goes away
|
||||||
expect(tooltipText).toBe(sineWaveObject3.path);
|
await page.getByLabel('Navigate to Test Telemetry Table').hover();
|
||||||
|
await expect(page.getByRole('tooltip')).toBeHidden();
|
||||||
|
|
||||||
await page.locator('.noselect > [title="SWG 1"]').first().hover();
|
await page.keyboard.down('Control');
|
||||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
// Hover over SWG1 in Telemetry Table
|
||||||
tooltipText = tooltipText.replace('\n', '').trim();
|
await page.getByLabel('name table cell SWG 1').first().hover();
|
||||||
expect(tooltipText).toBe(sineWaveObject1.path);
|
await expect(page.getByRole('tooltip', { name: sineWaveObject1.path })).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('display tooltip path for recently viewed items', async ({ page }) => {
|
test('display tooltip path for recently viewed items', async ({ page }) => {
|
||||||
// drag up Recently Viewed pane
|
// drag up Recently Viewed pane
|
||||||
await page
|
await page.getByLabel('Resize Recently Viewed Pane').hover();
|
||||||
.locator('.l-pane.l-pane--vertical-handle-before', {
|
|
||||||
hasText: 'Recently Viewed'
|
|
||||||
})
|
|
||||||
.locator('.l-pane__handle')
|
|
||||||
.hover();
|
|
||||||
await page.mouse.down();
|
await page.mouse.down();
|
||||||
await page.mouse.move(0, 300);
|
await page.mouse.move(0, 300);
|
||||||
await page.mouse.up();
|
await page.mouse.up();
|
||||||
|
|
||||||
await page.keyboard.down('Control');
|
await page.keyboard.down('Control');
|
||||||
await page.getByLabel('Recent Objects').getByText(sineWaveObject3.name).hover();
|
await page.getByLabel('Recent Objects').getByText(sineWaveObject3.name).hover();
|
||||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject3.path);
|
||||||
tooltipText = tooltipText.replace('\n', '').trim();
|
|
||||||
expect(tooltipText).toBe(sineWaveObject3.path);
|
|
||||||
|
|
||||||
await page.getByLabel('Recent Objects').getByText(sineWaveObject2.name).hover();
|
await page.getByLabel('Recent Objects').getByText(sineWaveObject2.name).hover();
|
||||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject2.path);
|
||||||
tooltipText = tooltipText.replace('\n', '').trim();
|
|
||||||
expect(tooltipText).toBe(sineWaveObject2.path);
|
|
||||||
|
|
||||||
await page.getByLabel('Recent Objects').getByText(sineWaveObject1.name).hover();
|
await page.getByLabel('Recent Objects').getByText(sineWaveObject1.name).hover();
|
||||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject1.path);
|
||||||
tooltipText = tooltipText.replace('\n', '').trim();
|
|
||||||
expect(tooltipText).toBe(sineWaveObject1.path);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('display tooltip path for time strips', async ({ page }) => {
|
test('display tooltip path for time strips', async ({ page }) => {
|
||||||
@ -433,35 +444,27 @@ test.describe('Verify tooltips', () => {
|
|||||||
});
|
});
|
||||||
// Edit Overlay Plot
|
// Edit Overlay Plot
|
||||||
await page.getByLabel('Edit Object').click();
|
await page.getByLabel('Edit Object').click();
|
||||||
await page.dragAndDrop(
|
await page
|
||||||
`text=${sineWaveObject1.name}`,
|
.getByLabel(`Preview ${sineWaveObject1.name}`)
|
||||||
'.c-object-view.is-object-type-time-strip'
|
.dragTo(page.getByLabel('Test Time Strip Object View'));
|
||||||
);
|
await page
|
||||||
await page.dragAndDrop(
|
.getByLabel(`Preview ${sineWaveObject2.name}`)
|
||||||
`text=${sineWaveObject2.name}`,
|
.dragTo(page.getByLabel('Test Time Strip Object View'));
|
||||||
'.c-object-view.is-object-type-time-strip'
|
await page
|
||||||
);
|
.getByLabel(`Preview ${sineWaveObject3.name}`)
|
||||||
await page.dragAndDrop(
|
.dragTo(page.getByLabel('Test Time Strip Object View'));
|
||||||
`text=${sineWaveObject3.name}`,
|
|
||||||
'.c-object-view.is-object-type-time-strip'
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
);
|
|
||||||
await page.locator('button[title="Save"]').click();
|
|
||||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||||
|
|
||||||
await page.keyboard.down('Control');
|
await page.keyboard.down('Control');
|
||||||
await page.getByText(sineWaveObject1.name).nth(2).hover();
|
await page.getByText(sineWaveObject1.name).nth(2).hover();
|
||||||
let tooltipText = await page.locator('.c-tooltip').textContent();
|
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject1.path);
|
||||||
tooltipText = tooltipText.replace('\n', '').trim();
|
|
||||||
expect(tooltipText).toBe(sineWaveObject1.path);
|
|
||||||
|
|
||||||
await page.getByText(sineWaveObject2.name).nth(2).hover();
|
await page.getByText(sineWaveObject2.name).nth(2).hover();
|
||||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject2.path);
|
||||||
tooltipText = tooltipText.replace('\n', '').trim();
|
|
||||||
expect(tooltipText).toBe(sineWaveObject2.path);
|
|
||||||
|
|
||||||
await page.getByText(sineWaveObject3.name).nth(2).hover();
|
await page.getByText(sineWaveObject3.name).nth(2).hover();
|
||||||
tooltipText = await page.locator('.c-tooltip').textContent();
|
await expect(page.getByRole('tooltip')).toHaveText(sineWaveObject3.path);
|
||||||
tooltipText = tooltipText.replace('\n', '').trim();
|
|
||||||
expect(tooltipText).toBe(sineWaveObject3.path);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import { createDomainObjectWithDefaults, renameObjectFromContextMenu } from '../../appActions.js';
|
import { createDomainObjectWithDefaults } from '../../appActions.js';
|
||||||
import { expect, test } from '../../pluginFixtures.js';
|
import { expect, test } from '../../pluginFixtures.js';
|
||||||
|
|
||||||
test.describe('Main Tree', () => {
|
test.describe('Main Tree', () => {
|
||||||
@ -47,123 +47,139 @@ test.describe('Main Tree', () => {
|
|||||||
parent: folder.uuid
|
parent: folder.uuid
|
||||||
});
|
});
|
||||||
|
|
||||||
await expandTreePaneItemByName(page, folder.name);
|
await page.getByLabel(`Expand ${folder.name} folder`).click();
|
||||||
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 ({
|
test('Creating a child object on one tab and expanding its parent on the other shows the correct composition @2p', async ({
|
||||||
page,
|
page
|
||||||
openmctConfig
|
|
||||||
}) => {
|
}) => {
|
||||||
test.info().annotations.push({
|
test.info().annotations.push({
|
||||||
type: 'issue',
|
type: 'issue',
|
||||||
description: 'https://github.com/nasa/openmct/issues/6391'
|
description: 'https://github.com/nasa/openmct/issues/6391'
|
||||||
});
|
});
|
||||||
|
|
||||||
const { myItemsFolderName } = openmctConfig;
|
|
||||||
const page2 = await page.context().newPage();
|
const page2 = await page.context().newPage();
|
||||||
|
|
||||||
// Both pages: Go to baseURL
|
// Both pages: Go to baseURL
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
page.goto('./', { waitUntil: 'networkidle' }),
|
page.goto('./', { waitUntil: 'domcontentloaded' }),
|
||||||
page2.goto('./', { waitUntil: 'networkidle' })
|
page2.goto('./', { waitUntil: 'domcontentloaded' })
|
||||||
|
]);
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
page.waitForURL('**/browse/mine?**'),
|
||||||
|
page2.waitForURL('**/browse/mine?**')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const page1Folder = await createDomainObjectWithDefaults(page, {
|
const page1Folder = await createDomainObjectWithDefaults(page, {
|
||||||
type: 'Folder'
|
type: 'Folder'
|
||||||
});
|
});
|
||||||
|
|
||||||
await expandTreePaneItemByName(page2, myItemsFolderName);
|
await page2.getByLabel('Expand My Items folder').click();
|
||||||
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 ({
|
test('Creating a child object on one tab and expanding its parent on the other shows the correct composition @couchdb @2p', async ({
|
||||||
page,
|
page
|
||||||
openmctConfig
|
|
||||||
}) => {
|
}) => {
|
||||||
test.info().annotations.push({
|
test.info().annotations.push({
|
||||||
type: 'issue',
|
type: 'issue',
|
||||||
description: 'https://github.com/nasa/openmct/issues/6391'
|
description: 'https://github.com/nasa/openmct/issues/6391'
|
||||||
});
|
});
|
||||||
|
|
||||||
const { myItemsFolderName } = openmctConfig;
|
|
||||||
const page2 = await page.context().newPage();
|
const page2 = await page.context().newPage();
|
||||||
|
|
||||||
// Both pages: Go to baseURL
|
// Both pages: Go to baseURL
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
page.goto('./', { waitUntil: 'networkidle' }),
|
page.goto('./', { waitUntil: 'domcontentloaded' }),
|
||||||
page2.goto('./', { waitUntil: 'networkidle' })
|
page2.goto('./', { waitUntil: 'domcontentloaded' })
|
||||||
|
]);
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
page.waitForURL('**/browse/mine?**'),
|
||||||
|
page2.waitForURL('**/browse/mine?**')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const page1Folder = await createDomainObjectWithDefaults(page, {
|
const page1Folder = await createDomainObjectWithDefaults(page, {
|
||||||
type: 'Folder'
|
type: 'Folder'
|
||||||
});
|
});
|
||||||
|
|
||||||
await expandTreePaneItemByName(page2, myItemsFolderName);
|
await page2.getByLabel('Expand My Items folder').click();
|
||||||
await assertTreeItemIsVisible(page2, page1Folder.name);
|
await expect(
|
||||||
|
page2
|
||||||
|
.getByRole('tree', { name: 'Main Tree' })
|
||||||
|
.getByRole('treeitem', { name: page1Folder.name })
|
||||||
|
).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Renaming an object reorders the tree @unstable', async ({ page, openmctConfig }) => {
|
test('Renaming an object reorders the tree', async ({ page }) => {
|
||||||
const { myItemsFolderName } = openmctConfig;
|
const foo = await createDomainObjectWithDefaults(page, {
|
||||||
|
|
||||||
await createDomainObjectWithDefaults(page, {
|
|
||||||
type: 'Folder',
|
type: 'Folder',
|
||||||
name: 'Foo'
|
name: 'Foo'
|
||||||
});
|
});
|
||||||
|
|
||||||
await createDomainObjectWithDefaults(page, {
|
const bar = await createDomainObjectWithDefaults(page, {
|
||||||
type: 'Folder',
|
type: 'Folder',
|
||||||
name: 'Bar'
|
name: 'Bar'
|
||||||
});
|
});
|
||||||
|
|
||||||
await createDomainObjectWithDefaults(page, {
|
const baz = await createDomainObjectWithDefaults(page, {
|
||||||
type: 'Folder',
|
type: 'Folder',
|
||||||
name: 'Baz'
|
name: 'Baz'
|
||||||
});
|
});
|
||||||
|
|
||||||
const clock1 = await createDomainObjectWithDefaults(page, {
|
let clock1 = await createDomainObjectWithDefaults(page, {
|
||||||
type: 'Clock',
|
type: 'Clock',
|
||||||
name: 'aaa'
|
name: 'aaa'
|
||||||
});
|
});
|
||||||
|
|
||||||
await createDomainObjectWithDefaults(page, {
|
const www = await createDomainObjectWithDefaults(page, {
|
||||||
type: 'Clock',
|
type: 'Clock',
|
||||||
name: 'www'
|
name: 'www'
|
||||||
});
|
});
|
||||||
|
|
||||||
// Expand the root folder
|
// Expand the root folder
|
||||||
await expandTreePaneItemByName(page, myItemsFolderName);
|
await page.getByLabel('Expand My Items folder').click();
|
||||||
|
|
||||||
await test.step('Reorders objects with the same tree depth', async () => {
|
await test.step('Reorders objects with the same tree depth', async () => {
|
||||||
await getAndAssertTreeItems(page, ['aaa', 'Bar', 'Baz', 'Foo', 'www']);
|
await getAndAssertTreeItems(page, ['My Items', 'aaa', 'Bar', 'Baz', 'Foo', 'www']);
|
||||||
await renameObjectFromContextMenu(page, clock1.url, 'zzz');
|
clock1.name = 'zzz';
|
||||||
await getAndAssertTreeItems(page, ['Bar', 'Baz', 'Foo', 'www', 'zzz']);
|
await renameObjectFromContextMenu(page, clock1.url, clock1.name);
|
||||||
|
await getAndAssertTreeItems(page, ['My Items', 'Bar', 'Baz', 'Foo', 'www', 'zzz']);
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('Reorders links to objects as well as original objects', async () => {
|
await test.step('Reorders links to objects as well as original objects', async () => {
|
||||||
await page.click('role=treeitem[name=/Bar/]');
|
await page.getByLabel(`Navigate to ${bar.name}`).dragTo(page.getByLabel('Object View'));
|
||||||
await page.dragAndDrop('role=treeitem[name=/www/]', '.c-object-view');
|
await page.getByLabel(`Navigate to ${www.name}`).dragTo(page.getByLabel('Object View'));
|
||||||
await page.dragAndDrop('role=treeitem[name=/zzz/]', '.c-object-view');
|
await page.getByLabel(`Navigate to ${clock1.name}`).dragTo(page.getByLabel('Object View'));
|
||||||
await page.click('role=treeitem[name=/Baz/]');
|
await page.getByLabel(`Navigate to ${baz.name}`).dragTo(page.getByLabel('Object View'));
|
||||||
await page.dragAndDrop('role=treeitem[name=/www/]', '.c-object-view');
|
await page.getByLabel(`Navigate to ${www.name}`).dragTo(page.getByLabel('Object View'));
|
||||||
await page.dragAndDrop('role=treeitem[name=/zzz/]', '.c-object-view');
|
await page.getByLabel(`Navigate to ${clock1.name}`).dragTo(page.getByLabel('Object View'));
|
||||||
await page.click('role=treeitem[name=/Foo/]');
|
await page.goto(foo.url);
|
||||||
await page.dragAndDrop('role=treeitem[name=/www/]', '.c-object-view');
|
await page.getByLabel(`Navigate to ${www.name}`).dragTo(page.getByLabel('Object View'));
|
||||||
await page.dragAndDrop('role=treeitem[name=/zzz/]', '.c-object-view');
|
await page.getByLabel(`Navigate to ${clock1.name}`).dragTo(page.getByLabel('Object View'));
|
||||||
// Expand the unopened folders
|
// Expand the unopened folders
|
||||||
await expandTreePaneItemByName(page, 'Bar');
|
await page.getByLabel(`Expand Bar folder`).click();
|
||||||
await expandTreePaneItemByName(page, 'Baz');
|
await page.getByLabel(`Expand Baz folder`).click();
|
||||||
await expandTreePaneItemByName(page, 'Foo');
|
await page.getByLabel(`Expand Foo folder`).click();
|
||||||
|
|
||||||
await renameObjectFromContextMenu(page, clock1.url, '___');
|
clock1.name = '___';
|
||||||
|
await renameObjectFromContextMenu(page, clock1.url, clock1.name);
|
||||||
|
await expect(page.getByLabel('Navigate to ' + clock1.name)).toHaveCount(2);
|
||||||
await getAndAssertTreeItems(page, [
|
await getAndAssertTreeItems(page, [
|
||||||
|
'My Items',
|
||||||
'___',
|
'___',
|
||||||
'Bar',
|
'Bar',
|
||||||
'___',
|
|
||||||
'www',
|
|
||||||
'Baz',
|
'Baz',
|
||||||
'___',
|
|
||||||
'www',
|
|
||||||
'Foo',
|
'Foo',
|
||||||
'___',
|
'___',
|
||||||
'www',
|
'www',
|
||||||
@ -172,10 +188,8 @@ test.describe('Main Tree', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
test('Opening and closing an item before the request has been fulfilled will abort the request @couchdb', async ({
|
test('Opening and closing an item before the request has been fulfilled will abort the request @couchdb', async ({
|
||||||
page,
|
page
|
||||||
openmctConfig
|
|
||||||
}) => {
|
}) => {
|
||||||
const { myItemsFolderName } = openmctConfig;
|
|
||||||
let requestWasAborted = false;
|
let requestWasAborted = false;
|
||||||
|
|
||||||
page.on('requestfailed', (request) => {
|
page.on('requestfailed', (request) => {
|
||||||
@ -201,7 +215,7 @@ test.describe('Main Tree', () => {
|
|||||||
// Quickly Expand/close the root folder
|
// Quickly Expand/close the root folder
|
||||||
await page
|
await page
|
||||||
.getByRole('button', {
|
.getByRole('button', {
|
||||||
name: `Expand ${myItemsFolderName} folder`
|
name: `Expand My Items folder`
|
||||||
})
|
})
|
||||||
.dblclick({ delay: 400 });
|
.dblclick({ delay: 400 });
|
||||||
|
|
||||||
@ -214,35 +228,36 @@ test.describe('Main Tree', () => {
|
|||||||
* @param {Array<string>} expected
|
* @param {Array<string>} expected
|
||||||
*/
|
*/
|
||||||
async function getAndAssertTreeItems(page, expected) {
|
async function getAndAssertTreeItems(page, expected) {
|
||||||
const treeItems = page.locator('[role="treeitem"]');
|
const treeItems = page.getByRole('treeitem');
|
||||||
const allTexts = await treeItems.allInnerTexts();
|
await expect(treeItems).toHaveCount(expected.length);
|
||||||
// Get rid of root folder ('My Items') as its position will not change
|
await expect(treeItems).toHaveText(expected, { useInnerText: true });
|
||||||
allTexts.shift();
|
|
||||||
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 {import('@playwright/test').Page} page
|
||||||
* @param {string} name
|
* @param {string} myItemsFolderName
|
||||||
|
* @param {string} url
|
||||||
|
* @param {string} newName
|
||||||
*/
|
*/
|
||||||
async function expandTreePaneItemByName(page, name) {
|
async function renameObjectFromContextMenu(page, url, newName) {
|
||||||
const mainTree = page.getByRole('tree', {
|
await openObjectTreeContextMenu(page, url);
|
||||||
name: 'Main Tree'
|
await page.getByLabel('Edit Properties...').click();
|
||||||
});
|
const nameInput = page.getByLabel('Title', { exact: true });
|
||||||
const treeItem = mainTree.getByRole('treeitem', {
|
await nameInput.fill(newName);
|
||||||
name,
|
await page.getByLabel('Save').click();
|
||||||
expanded: false
|
}
|
||||||
});
|
|
||||||
await treeItem.locator('.c-disclosure-triangle').click();
|
/**
|
||||||
|
* Open the given `domainObject`'s context menu from the object tree.
|
||||||
|
* Expands the path to the object and scrolls to it if necessary.
|
||||||
|
*
|
||||||
|
* @param {import('@playwright/test').Page} page
|
||||||
|
* @param {string} url the url to the object
|
||||||
|
*/
|
||||||
|
async function openObjectTreeContextMenu(page, url) {
|
||||||
|
await page.goto(url);
|
||||||
|
await page.getByLabel('Show selected item in tree').click();
|
||||||
|
await page.locator('.is-navigated-object').click({
|
||||||
|
button: 'right'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ test.describe('Smoke tests for @mobile', () => {
|
|||||||
test('Verify that user can change time conductor @mobile', async ({ page }) => {
|
test('Verify that user can change time conductor @mobile', async ({ page }) => {
|
||||||
//Collapse Browse Pane to get more Time Conductor space
|
//Collapse Browse Pane to get more Time Conductor space
|
||||||
await page.getByLabel('Collapse Browse Pane').click();
|
await page.getByLabel('Collapse Browse Pane').click();
|
||||||
//Open Time Conductor and change to Real Time Mode and set offset hour by 1 hour
|
// Open Time Conductor and change to Real Time Mode and set offset hour by 1 hour
|
||||||
// Disabling line because we're intentionally obscuring the text
|
// Disabling line because we're intentionally obscuring the text
|
||||||
// eslint-disable-next-line playwright/no-force-option
|
// eslint-disable-next-line playwright/no-force-option
|
||||||
await page.getByLabel('Time Conductor Mode').click({ force: true });
|
await page.getByLabel('Time Conductor Mode').click({ force: true });
|
||||||
@ -82,14 +82,14 @@ test.describe('Smoke tests for @mobile', () => {
|
|||||||
await page.getByTitle('Collapse Browse Pane').click();
|
await page.getByTitle('Collapse Browse Pane').click();
|
||||||
await expect(page.getByRole('main').getByText('Parent Display Layout')).toBeVisible();
|
await expect(page.getByRole('main').getByText('Parent Display Layout')).toBeVisible();
|
||||||
//Verify both objects are in view
|
//Verify both objects are in view
|
||||||
await expect(await page.getByLabel('Child Layout 1 Layout')).toBeVisible();
|
await expect(page.getByLabel('Child Layout 1 Layout')).toBeVisible();
|
||||||
await expect(await page.getByLabel('Child Layout 2 Layout')).toBeVisible();
|
await expect(page.getByLabel('Child Layout 2 Layout')).toBeVisible();
|
||||||
//Remove First Object to bring up confirmation dialog
|
//Remove First Object to bring up confirmation dialog
|
||||||
await page.getByLabel('View menu items').nth(1).click();
|
await page.getByLabel('View menu items').nth(1).click();
|
||||||
await page.getByLabel('Remove').click();
|
await page.getByLabel('Remove').click();
|
||||||
await page.getByRole('button', { name: 'OK' }).click();
|
await page.getByRole('button', { name: 'Ok' }).click();
|
||||||
//Verify that the object is removed
|
//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);
|
await expect(page.getByLabel('Child Layout 2 Layout')).toHaveCount(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -39,7 +39,7 @@ const filePath = 'test-data/PerformanceDisplayLayout.json';
|
|||||||
test.describe('Performance tests', () => {
|
test.describe('Performance tests', () => {
|
||||||
test.beforeEach(async ({ page, browser }, testInfo) => {
|
test.beforeEach(async ({ page, browser }, testInfo) => {
|
||||||
// Go to baseURL
|
// Go to baseURL
|
||||||
await page.goto('./', { waitUntil: 'networkidle' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
|
|
||||||
// Click a:has-text("My Items")
|
// Click a:has-text("My Items")
|
||||||
await page.locator('a:has-text("My Items")').click({
|
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
|
//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
|
//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
|
//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) => {
|
let backgroundImageUrl = await backgroundImage.evaluate((el) => {
|
||||||
return window
|
return window
|
||||||
.getComputedStyle(el)
|
.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
|
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
|
//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'));
|
await page.evaluate(() => window.performance.mark('background-image-frame'));
|
||||||
|
|
||||||
//Time to Example Imagery object loads
|
//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'));
|
await page.evaluate(() => window.performance.mark('background-image-visible'));
|
||||||
|
|
||||||
// Get Current number of images in thumbstrip
|
// 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();
|
const thumbCount = await page.locator('.c-imagery__thumb').count();
|
||||||
console.log('number of thumbs rendered ' + thumbCount);
|
console.log('number of thumbs rendered ' + thumbCount);
|
||||||
await page.locator('.c-imagery__thumb').last().click();
|
await page.locator('.c-imagery__thumb').last().click();
|
||||||
|
@ -38,7 +38,7 @@ const notebookFilePath = 'test-data/PerformanceNotebook.json';
|
|||||||
test.describe('Performance tests', () => {
|
test.describe('Performance tests', () => {
|
||||||
test.beforeEach(async ({ page, browser }, testInfo) => {
|
test.beforeEach(async ({ page, browser }, testInfo) => {
|
||||||
// Go to baseURL
|
// Go to baseURL
|
||||||
await page.goto('./', { waitUntil: 'networkidle' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
|
|
||||||
// Click a:has-text("My Items")
|
// Click a:has-text("My Items")
|
||||||
await page.locator('a:has-text("My Items")').click({
|
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'));
|
await page.evaluate(() => window.performance.mark('search-entered'));
|
||||||
//Search Result Appears and is clicked
|
//Search Result Appears and is clicked
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
page.waitForNavigation(),
|
|
||||||
page.locator('a:has-text("Performance Notebook")').first().click(),
|
page.locator('a:has-text("Performance Notebook")').first().click(),
|
||||||
page.evaluate(() => window.performance.mark('click-search-result'))
|
page.evaluate(() => window.performance.mark('click-search-result'))
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await page.waitForSelector('.c-tree__item c-tree-and-search__loading loading', {
|
await page
|
||||||
state: 'hidden'
|
.locator('.c-tree__item c-tree-and-search__loading loading')
|
||||||
});
|
.waitFor({ state: 'hidden' });
|
||||||
await page.evaluate(() => window.performance.mark('search-spinner-gone'));
|
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.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'));
|
await page.evaluate(() => window.performance.mark('notebook-entry-appears'));
|
||||||
|
|
||||||
// Click Add new Notebook Entry
|
// Click Add new Notebook Entry
|
||||||
@ -139,9 +138,9 @@ test.describe('Performance tests', () => {
|
|||||||
await page.evaluate(() => window.performance.mark('notebook-search-start'));
|
await page.evaluate(() => window.performance.mark('notebook-search-start'));
|
||||||
await page.locator('.c-notebook__search >> input').fill('Existing Entry');
|
await page.locator('.c-notebook__search >> input').fill('Existing Entry');
|
||||||
await page.evaluate(() => window.performance.mark('notebook-search-filled'));
|
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.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'));
|
await page.evaluate(() => window.performance.mark('notebook-search-processed'));
|
||||||
|
|
||||||
//Clear Search
|
//Clear Search
|
||||||
@ -154,7 +153,7 @@ test.describe('Performance tests', () => {
|
|||||||
await page.locator('div.c-ne__time-and-content').last().hover();
|
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[title="Delete this entry"]').last().click();
|
||||||
await page.locator('button:has-text("Ok")').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 page.evaluate(() => window.performance.mark('new-notebook-entry-deleted'));
|
||||||
|
|
||||||
//await client.send('HeapProfiler.enable');
|
//await client.send('HeapProfiler.enable');
|
||||||
|
@ -228,9 +228,7 @@ test.describe('Navigation memory leak is not detected in', () => {
|
|||||||
expect(result).toBe(true);
|
expect(result).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('display layout with plots of swgs, alphanumerics, and condition sets, ', async ({
|
test('display layout with plots of swgs, alphanumerics, and condition sets', async ({ page }) => {
|
||||||
page
|
|
||||||
}) => {
|
|
||||||
const result = await navigateToObjectAndDetectMemoryLeak(
|
const result = await navigateToObjectAndDetectMemoryLeak(
|
||||||
page,
|
page,
|
||||||
'display-layout-simple-telemetry'
|
'display-layout-simple-telemetry'
|
||||||
|
@ -78,7 +78,7 @@ test.describe('Tabs View', () => {
|
|||||||
await page.getByLabel(`${sineWaveGenerator.name} tab`, { exact: true }).click();
|
await page.getByLabel(`${sineWaveGenerator.name} tab`, { exact: true }).click();
|
||||||
|
|
||||||
// ensure sine wave generator visible
|
// 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
|
// now select notebook and clear animation calls
|
||||||
await page.getByLabel(`${notebook.name} tab`, { exact: true }).click();
|
await page.getByLabel(`${notebook.name} tab`, { exact: true }).click();
|
||||||
|
@ -84,7 +84,7 @@ test.describe('Plot Tagging Performance', () => {
|
|||||||
await setRealTimeMode(page);
|
await setRealTimeMode(page);
|
||||||
|
|
||||||
// Search for Science
|
// 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');
|
await page.getByRole('searchbox', { name: 'Search Input' }).fill('sc');
|
||||||
|
|
||||||
// click on the search result
|
// click on the search result
|
||||||
|
@ -89,6 +89,7 @@ test.describe('Visual - Header @a11y', () => {
|
|||||||
await percySnapshot(page, `Notebook Snapshot Show button (theme: '${theme}')`, {
|
await percySnapshot(page, `Notebook Snapshot Show button (theme: '${theme}')`, {
|
||||||
scope: header
|
scope: header
|
||||||
});
|
});
|
||||||
|
await expect(page.getByLabel('Show Snapshots')).toBeVisible();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -29,15 +29,14 @@ import { MISSION_TIME, VISUAL_FIXED_URL } from '../../../constants.js';
|
|||||||
const inspectorPane = '.l-shell__pane-inspector';
|
const inspectorPane = '.l-shell__pane-inspector';
|
||||||
|
|
||||||
test.describe('Visual - Inspector @ally @clock', () => {
|
test.describe('Visual - Inspector @ally @clock', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
|
||||||
await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' });
|
|
||||||
});
|
|
||||||
test.use({
|
test.use({
|
||||||
storageState: 'test-data/overlay_plot_with_delay_storage.json',
|
storageState: 'test-data/overlay_plot_with_delay_storage.json'
|
||||||
clockOptions: {
|
});
|
||||||
now: MISSION_TIME,
|
|
||||||
shouldAdvanceTime: true
|
test.beforeEach(async ({ page }) => {
|
||||||
}
|
await page.clock.install({ time: MISSION_TIME });
|
||||||
|
await page.clock.resume();
|
||||||
|
await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' });
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Inspector from overlay_plot_with_delay_storage @localStorage', async ({ page, theme }) => {
|
test('Inspector from overlay_plot_with_delay_storage @localStorage', async ({ page, theme }) => {
|
||||||
|
@ -33,13 +33,9 @@ import {
|
|||||||
} from '../../../constants.js';
|
} from '../../../constants.js';
|
||||||
|
|
||||||
test.describe('Visual - Time Conductor', () => {
|
test.describe('Visual - Time Conductor', () => {
|
||||||
test.use({
|
|
||||||
clockOptions: {
|
|
||||||
now: MISSION_TIME,
|
|
||||||
shouldAdvanceTime: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await page.clock.install({ time: MISSION_TIME });
|
||||||
|
await page.clock.pauseAt(MISSION_TIME);
|
||||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -87,7 +83,7 @@ test.describe('Visual - Time Conductor', () => {
|
|||||||
test(
|
test(
|
||||||
'Visual - Time Conductor Axis Resized @clock @snapshot',
|
'Visual - Time Conductor Axis Resized @clock @snapshot',
|
||||||
{ annotation: [{ type: 'issue', description: 'https://github.com/nasa/openmct/issues/7623' }] },
|
{ annotation: [{ type: 'issue', description: 'https://github.com/nasa/openmct/issues/7623' }] },
|
||||||
async ({ page, tick }) => {
|
async ({ page }) => {
|
||||||
const VISUAL_REALTIME_WITH_PANES = VISUAL_REALTIME_URL.replace(
|
const VISUAL_REALTIME_WITH_PANES = VISUAL_REALTIME_URL.replace(
|
||||||
'hideTree=true',
|
'hideTree=true',
|
||||||
'hideTree=false'
|
'hideTree=false'
|
||||||
@ -108,7 +104,7 @@ test.describe('Visual - Time Conductor', () => {
|
|||||||
await page.getByLabel('Collapse Browse Pane').click();
|
await page.getByLabel('Collapse Browse Pane').click();
|
||||||
|
|
||||||
// manually tick the clock to trigger the resize / re-render
|
// manually tick the clock to trigger the resize / re-render
|
||||||
await tick(1000 * 2);
|
await page.clock.runFor(1000 * 2);
|
||||||
|
|
||||||
const mask = [];
|
const mask = [];
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 30 KiB |
Binary file not shown.
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 27 KiB |
@ -22,16 +22,15 @@
|
|||||||
|
|
||||||
import percySnapshot from '@percy/playwright';
|
import percySnapshot from '@percy/playwright';
|
||||||
|
|
||||||
import { createDomainObjectWithDefaults, expandTreePaneItemByName } from '../../../appActions.js';
|
import { createDomainObjectWithDefaults } from '../../../appActions.js';
|
||||||
|
import { test } from '../../../avpFixtures.js';
|
||||||
import { VISUAL_FIXED_URL } from '../../../constants.js';
|
import { VISUAL_FIXED_URL } from '../../../constants.js';
|
||||||
import { test } from '../../../pluginFixtures.js';
|
|
||||||
|
|
||||||
//Declare the scope of the visual test
|
//Declare the scope of the visual test
|
||||||
const treePane = "[role=tree][aria-label='Main Tree']";
|
const treePane = "[role=tree][aria-label='Main Tree']";
|
||||||
|
|
||||||
test.describe('Visual - Tree Pane', () => {
|
test.describe('Visual - Tree Pane', () => {
|
||||||
test('Tree pane in various states', async ({ page, theme, openmctConfig }) => {
|
test('Tree pane in various states', async ({ page, theme }) => {
|
||||||
const { myItemsFolderName } = openmctConfig;
|
|
||||||
await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' });
|
await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' });
|
||||||
|
|
||||||
//Open Tree
|
//Open Tree
|
||||||
@ -69,28 +68,28 @@ test.describe('Visual - Tree Pane', () => {
|
|||||||
scope: treePane
|
scope: treePane
|
||||||
});
|
});
|
||||||
|
|
||||||
await expandTreePaneItemByName(page, myItemsFolderName);
|
await page.getByLabel('Expand My Items folder').click();
|
||||||
|
|
||||||
await page.goto(foo.url);
|
await page.goto(foo.url);
|
||||||
await page.dragAndDrop('role=treeitem[name=/A Clock/]', '.c-object-view');
|
await page.getByLabel('Navigate to A Clock').dragTo(page.getByLabel('Object View'));
|
||||||
await page.dragAndDrop('role=treeitem[name=/Z Clock/]', '.c-object-view');
|
await page.getByLabel('Navigate to Z Clock').dragTo(page.getByLabel('Object View'));
|
||||||
await page.goto(bar.url);
|
await page.goto(bar.url);
|
||||||
await page.dragAndDrop('role=treeitem[name=/A Clock/]', '.c-object-view');
|
await page.getByLabel('Navigate to A Clock').dragTo(page.getByLabel('Object View'));
|
||||||
await page.dragAndDrop('role=treeitem[name=/Z Clock/]', '.c-object-view');
|
await page.getByLabel('Navigate to Z Clock').dragTo(page.getByLabel('Object View'));
|
||||||
await page.goto(baz.url);
|
await page.goto(baz.url);
|
||||||
await page.dragAndDrop('role=treeitem[name=/A Clock/]', '.c-object-view');
|
await page.getByLabel('Navigate to A Clock').dragTo(page.getByLabel('Object View'));
|
||||||
await page.dragAndDrop('role=treeitem[name=/Z Clock/]', '.c-object-view');
|
await page.getByLabel('Navigate to Z Clock').dragTo(page.getByLabel('Object View'));
|
||||||
|
|
||||||
await percySnapshot(page, `Tree Pane w/ single level expanded (theme: ${theme})`, {
|
await percySnapshot(page, `Tree Pane w/ single level expanded (theme: ${theme})`, {
|
||||||
scope: treePane
|
scope: treePane
|
||||||
});
|
});
|
||||||
|
|
||||||
await expandTreePaneItemByName(page, foo.name);
|
await page.getByLabel(`Expand ${foo.name} folder`).click();
|
||||||
await expandTreePaneItemByName(page, bar.name);
|
await page.getByLabel(`Expand ${bar.name} folder`).click();
|
||||||
await expandTreePaneItemByName(page, baz.name);
|
await page.getByLabel(`Expand ${baz.name} folder`).click();
|
||||||
|
|
||||||
// eslint-disable-next-line playwright/no-wait-for-timeout
|
// eslint-disable-next-line playwright/no-wait-for-timeout
|
||||||
await page.waitForTimeout(1 * 1000); //https://github.com/nasa/openmct/issues/7059
|
await page.waitForTimeout(3 * 1000); //https://github.com/nasa/openmct/issues/7059
|
||||||
|
|
||||||
await percySnapshot(page, `Tree Pane w/ multiple levels expanded (theme: ${theme})`, {
|
await percySnapshot(page, `Tree Pane w/ multiple levels expanded (theme: ${theme})`, {
|
||||||
scope: treePane
|
scope: treePane
|
||||||
|
@ -32,18 +32,14 @@ import { expect, test } from '../../pluginFixtures.js';
|
|||||||
|
|
||||||
test.describe('Visual - Controlled Clock @clock', () => {
|
test.describe('Visual - Controlled Clock @clock', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await page.clock.install({ time: MISSION_TIME });
|
||||||
await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' });
|
await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' });
|
||||||
});
|
});
|
||||||
test.use({
|
test.use({
|
||||||
storageState: 'test-data/overlay_plot_with_delay_storage.json',
|
storageState: 'test-data/overlay_plot_with_delay_storage.json'
|
||||||
clockOptions: {
|
|
||||||
now: MISSION_TIME,
|
|
||||||
shouldAdvanceTime: false //Don't advance the clock
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Overlay Plot Loading Indicator @localStorage', async ({ page, theme }) => {
|
test('Overlay Plot Loading Indicator @localStorage', async ({ page, theme }) => {
|
||||||
await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' });
|
|
||||||
await page
|
await page
|
||||||
.getByRole('gridcell', { hasText: 'Overlay Plot with 5s Delay Overlay Plot' })
|
.getByRole('gridcell', { hasText: 'Overlay Plot with 5s Delay Overlay Plot' })
|
||||||
.click();
|
.click();
|
||||||
|
@ -27,13 +27,9 @@ import { MISSION_TIME, VISUAL_FIXED_URL } from '../../constants.js';
|
|||||||
import { test } from '../../pluginFixtures.js';
|
import { test } from '../../pluginFixtures.js';
|
||||||
|
|
||||||
test.describe('Visual - Display Layout @clock', () => {
|
test.describe('Visual - Display Layout @clock', () => {
|
||||||
test.use({
|
|
||||||
clockOptions: {
|
|
||||||
now: MISSION_TIME,
|
|
||||||
shouldAdvanceTime: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await page.clock.install({ time: MISSION_TIME });
|
||||||
|
await page.clock.resume();
|
||||||
await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' });
|
await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' });
|
||||||
|
|
||||||
const parentLayout = await createDomainObjectWithDefaults(page, {
|
const parentLayout = await createDomainObjectWithDefaults(page, {
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
import percySnapshot from '@percy/playwright';
|
import percySnapshot from '@percy/playwright';
|
||||||
|
|
||||||
import { createDomainObjectWithDefaults, expandTreePaneItemByName } from '../../appActions.js';
|
import { createDomainObjectWithDefaults } from '../../appActions.js';
|
||||||
import { expect, scanForA11yViolations, test } from '../../avpFixtures.js';
|
import { expect, scanForA11yViolations, test } from '../../avpFixtures.js';
|
||||||
import { VISUAL_FIXED_URL } from '../../constants.js';
|
import { VISUAL_FIXED_URL } from '../../constants.js';
|
||||||
import { enterTextEntry, startAndAddRestrictedNotebookObject } from '../../helper/notebookUtils.js';
|
import { enterTextEntry, startAndAddRestrictedNotebookObject } from '../../helper/notebookUtils.js';
|
||||||
@ -86,9 +86,7 @@ test.describe('Visual - Notebook @a11y', () => {
|
|||||||
name: 'Test Notebook'
|
name: 'Test Notebook'
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
test('Accepts dropped objects as embeds', async ({ page, theme, openmctConfig }) => {
|
test('Accepts dropped objects as embeds', async ({ page, theme }) => {
|
||||||
const { myItemsFolderName } = openmctConfig;
|
|
||||||
|
|
||||||
// Create Overlay Plot
|
// Create Overlay Plot
|
||||||
await createDomainObjectWithDefaults(page, {
|
await createDomainObjectWithDefaults(page, {
|
||||||
type: 'Overlay Plot',
|
type: 'Overlay Plot',
|
||||||
@ -98,11 +96,13 @@ test.describe('Visual - Notebook @a11y', () => {
|
|||||||
//Open Tree to perform drag
|
//Open Tree to perform drag
|
||||||
await page.getByRole('button', { name: 'Browse' }).click();
|
await page.getByRole('button', { name: 'Browse' }).click();
|
||||||
|
|
||||||
await expandTreePaneItemByName(page, myItemsFolderName);
|
await page.getByLabel('Expand My Items folder').click();
|
||||||
|
|
||||||
await page.goto(notebook.url);
|
await page.goto(notebook.url);
|
||||||
|
|
||||||
await page.dragAndDrop('role=treeitem[name=/Dropped Overlay Plot/]', '.c-notebook__drag-area');
|
await page
|
||||||
|
.getByLabel('Navigate to Dropped Overlay Plot')
|
||||||
|
.dragTo(page.getByLabel('To start a new entry, click here or drag and drop any object'));
|
||||||
|
|
||||||
await percySnapshot(page, `Notebook w/ dropped embed (theme: ${theme})`);
|
await percySnapshot(page, `Notebook w/ dropped embed (theme: ${theme})`);
|
||||||
});
|
});
|
||||||
|
@ -33,17 +33,12 @@ const examplePlanSmall1 = JSON.parse(
|
|||||||
fs.readFileSync(new URL('../../test-data/examplePlans/ExamplePlan_Small1.json', import.meta.url))
|
fs.readFileSync(new URL('../../test-data/examplePlans/ExamplePlan_Small1.json', import.meta.url))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const FIRST_ACTIVITY_SMALL_1 = getFirstActivity(examplePlanSmall1);
|
||||||
|
|
||||||
test.describe('Visual - Timelist progress bar @clock @a11y', () => {
|
test.describe('Visual - Timelist progress bar @clock @a11y', () => {
|
||||||
const firstActivity = getFirstActivity(examplePlanSmall1);
|
|
||||||
|
|
||||||
test.use({
|
|
||||||
clockOptions: {
|
|
||||||
now: firstActivity.end + 10000,
|
|
||||||
shouldAdvanceTime: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await page.clock.install({ time: FIRST_ACTIVITY_SMALL_1.end + 10000 });
|
||||||
|
await page.clock.resume();
|
||||||
await createTimelistWithPlanAndSetActivityInProgress(page, examplePlanSmall1);
|
await createTimelistWithPlanAndSetActivityInProgress(page, examplePlanSmall1);
|
||||||
await page.getByLabel('Click to collapse items').click();
|
await page.getByLabel('Click to collapse items').click();
|
||||||
});
|
});
|
||||||
|
@ -44,11 +44,13 @@ test.describe('Visual - Time Strip @a11y', () => {
|
|||||||
});
|
});
|
||||||
await createPlanFromJSON(page, {
|
await createPlanFromJSON(page, {
|
||||||
json: examplePlanSmall2,
|
json: examplePlanSmall2,
|
||||||
parent: timeStrip.uuid
|
parent: timeStrip.uuid,
|
||||||
|
name: 'examplePlanSmall2'
|
||||||
});
|
});
|
||||||
await createDomainObjectWithDefaults(page, {
|
await createDomainObjectWithDefaults(page, {
|
||||||
type: 'Sine Wave Generator',
|
type: 'Sine Wave Generator',
|
||||||
parent: timeStrip.uuid
|
parent: timeStrip.uuid,
|
||||||
|
name: 'Sine Wave Generator'
|
||||||
});
|
});
|
||||||
await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' });
|
await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' });
|
||||||
|
|
||||||
|
@ -41,17 +41,12 @@ const examplePlanSmall2 = JSON.parse(
|
|||||||
fs.readFileSync(new URL('../../test-data/examplePlans/ExamplePlan_Small2.json', import.meta.url))
|
fs.readFileSync(new URL('../../test-data/examplePlans/ExamplePlan_Small2.json', import.meta.url))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const FIRST_ACTIVITY_SMALL_1 = getFirstActivity(examplePlanSmall1);
|
||||||
|
|
||||||
test.describe('Visual - Timelist progress bar @clock @a11y', () => {
|
test.describe('Visual - Timelist progress bar @clock @a11y', () => {
|
||||||
const firstActivity = getFirstActivity(examplePlanSmall1);
|
|
||||||
|
|
||||||
test.use({
|
|
||||||
clockOptions: {
|
|
||||||
now: firstActivity.end + 10000,
|
|
||||||
shouldAdvanceTime: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await page.clock.install({ time: FIRST_ACTIVITY_SMALL_1.end + 10000 });
|
||||||
|
await page.clock.resume();
|
||||||
await createTimelistWithPlanAndSetActivityInProgress(page, examplePlanSmall1);
|
await createTimelistWithPlanAndSetActivityInProgress(page, examplePlanSmall1);
|
||||||
await page.getByLabel('Click to collapse items').click();
|
await page.getByLabel('Click to collapse items').click();
|
||||||
});
|
});
|
||||||
|
184
package-lock.json
generated
184
package-lock.json
generated
@ -37,7 +37,7 @@
|
|||||||
"eslint-config-prettier": "9.1.0",
|
"eslint-config-prettier": "9.1.0",
|
||||||
"eslint-plugin-compat": "4.2.0",
|
"eslint-plugin-compat": "4.2.0",
|
||||||
"eslint-plugin-no-unsanitized": "4.0.2",
|
"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-prettier": "5.1.3",
|
||||||
"eslint-plugin-simple-import-sort": "10.0.0",
|
"eslint-plugin-simple-import-sort": "10.0.0",
|
||||||
"eslint-plugin-unicorn": "49.0.0",
|
"eslint-plugin-unicorn": "49.0.0",
|
||||||
@ -104,9 +104,7 @@
|
|||||||
"@axe-core/playwright": "4.8.5",
|
"@axe-core/playwright": "4.8.5",
|
||||||
"@percy/cli": "1.27.4",
|
"@percy/cli": "1.27.4",
|
||||||
"@percy/playwright": "1.0.4",
|
"@percy/playwright": "1.0.4",
|
||||||
"@playwright/test": "1.45.2",
|
"@playwright/test": "1.45.2"
|
||||||
"@types/sinonjs__fake-timers": "8.1.5",
|
|
||||||
"sinon": "17.0.0"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"e2e/node_modules/@percy/cli": {
|
"e2e/node_modules/@percy/cli": {
|
||||||
@ -1582,50 +1580,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sinonjs/commons": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
|
|
||||||
"integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"type-detect": "4.0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@sinonjs/fake-timers": {
|
|
||||||
"version": "11.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz",
|
|
||||||
"integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@sinonjs/commons": "^3.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@sinonjs/samsam": {
|
|
||||||
"version": "8.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz",
|
|
||||||
"integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@sinonjs/commons": "^2.0.0",
|
|
||||||
"lodash.get": "^4.4.2",
|
|
||||||
"type-detect": "^4.0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"type-detect": "4.0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@sinonjs/text-encoding": {
|
|
||||||
"version": "0.7.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz",
|
|
||||||
"integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/@socket.io/component-emitter": {
|
"node_modules/@socket.io/component-emitter": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
|
||||||
@ -1906,12 +1860,6 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/sinonjs__fake-timers": {
|
|
||||||
"version": "8.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz",
|
|
||||||
"integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/@types/sockjs": {
|
"node_modules/@types/sockjs": {
|
||||||
"version": "0.3.36",
|
"version": "0.3.36",
|
||||||
"resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz",
|
"resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz",
|
||||||
@ -4235,15 +4183,6 @@
|
|||||||
"integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==",
|
"integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/diff": {
|
|
||||||
"version": "5.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
|
|
||||||
"integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.3.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/dir-glob": {
|
"node_modules/dir-glob": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
||||||
@ -4756,13 +4695,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-playwright": {
|
"node_modules/eslint-plugin-playwright": {
|
||||||
"version": "0.12.0",
|
"version": "1.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-0.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-1.5.2.tgz",
|
||||||
"integrity": "sha512-KXuzQjVzca5irMT/7rvzJKsVDGbQr43oQPc8i+SLEBqmfrTxlwMwRqfv9vtZqh4hpU0jmrnA/EOfwtls+5QC1w==",
|
"integrity": "sha512-TMzLrLGQMccngU8GogtzIc9u5RzXGnfsQEUjLfEfshINuVR2fS4SHfDtU7xYP90Vwm5vflHECf610KTdGvO53w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"workspaces": [
|
||||||
|
"examples"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"globals": "^13.23.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.6.0"
|
||||||
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"eslint": ">=7",
|
"eslint": ">=8.40.0",
|
||||||
"eslint-plugin-jest": ">=24"
|
"eslint-plugin-jest": ">=25"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"eslint-plugin-jest": {
|
"eslint-plugin-jest": {
|
||||||
@ -4770,6 +4718,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": {
|
"node_modules/eslint-plugin-prettier": {
|
||||||
"version": "5.1.3",
|
"version": "5.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz",
|
||||||
@ -7250,12 +7225,6 @@
|
|||||||
"graceful-fs": "^4.1.6"
|
"graceful-fs": "^4.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/just-extend": {
|
|
||||||
"version": "6.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz",
|
|
||||||
"integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/karma": {
|
"node_modules/karma": {
|
||||||
"version": "6.4.2",
|
"version": "6.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/karma/-/karma-6.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/karma/-/karma-6.4.2.tgz",
|
||||||
@ -7636,12 +7605,6 @@
|
|||||||
"integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==",
|
"integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/lodash.get": {
|
|
||||||
"version": "4.4.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
|
|
||||||
"integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/lodash.memoize": {
|
"node_modules/lodash.memoize": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
||||||
@ -8073,19 +8036,6 @@
|
|||||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/nise": {
|
|
||||||
"version": "5.1.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz",
|
|
||||||
"integrity": "sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@sinonjs/commons": "^3.0.0",
|
|
||||||
"@sinonjs/fake-timers": "^11.2.2",
|
|
||||||
"@sinonjs/text-encoding": "^0.7.2",
|
|
||||||
"just-extend": "^6.2.0",
|
|
||||||
"path-to-regexp": "^6.2.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/node-fetch": {
|
"node_modules/node-fetch": {
|
||||||
"version": "2.7.0",
|
"version": "2.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||||
@ -10200,45 +10150,6 @@
|
|||||||
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/sinon": {
|
|
||||||
"version": "17.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.0.tgz",
|
|
||||||
"integrity": "sha512-p4lJiYKBoOEVUxxVIC9H1MM2znG1/c8gud++I2BauJA5hsz7hHsst35eurNWXTusBsIq66FzOQbZ/uMdpvbPIQ==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@sinonjs/commons": "^3.0.0",
|
|
||||||
"@sinonjs/fake-timers": "^11.2.2",
|
|
||||||
"@sinonjs/samsam": "^8.0.0",
|
|
||||||
"diff": "^5.1.0",
|
|
||||||
"nise": "^5.1.5",
|
|
||||||
"supports-color": "^7.2.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/sinon"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/sinon/node_modules/has-flag": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/sinon/node_modules/supports-color": {
|
|
||||||
"version": "7.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
|
||||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"has-flag": "^4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/slash": {
|
"node_modules/slash": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz",
|
||||||
@ -10944,15 +10855,6 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/type-detect": {
|
|
||||||
"version": "4.0.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
|
|
||||||
"integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/type-fest": {
|
"node_modules/type-fest": {
|
||||||
"version": "0.8.1",
|
"version": "0.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
|
||||||
|
11
package.json
11
package.json
@ -40,7 +40,7 @@
|
|||||||
"eslint-config-prettier": "9.1.0",
|
"eslint-config-prettier": "9.1.0",
|
||||||
"eslint-plugin-compat": "4.2.0",
|
"eslint-plugin-compat": "4.2.0",
|
||||||
"eslint-plugin-no-unsanitized": "4.0.2",
|
"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-prettier": "5.1.3",
|
||||||
"eslint-plugin-simple-import-sort": "10.0.0",
|
"eslint-plugin-simple-import-sort": "10.0.0",
|
||||||
"eslint-plugin-unicorn": "49.0.0",
|
"eslint-plugin-unicorn": "49.0.0",
|
||||||
@ -116,14 +116,13 @@
|
|||||||
"test:e2e:a11y": "npm test --workspace e2e -- --config=playwright-visual-a11y.config.js --project=chrome --grep @a11y",
|
"test:e2e:a11y": "npm test --workspace e2e -- --config=playwright-visual-a11y.config.js --project=chrome --grep @a11y",
|
||||||
"test:e2e:mobile": "npm test --workspace e2e -- --config=playwright-mobile.config.js",
|
"test:e2e:mobile": "npm test --workspace e2e -- --config=playwright-mobile.config.js",
|
||||||
"test:e2e:couchdb": "npm test --workspace e2e -- --config=playwright-ci.config.js --project=chrome --grep @couchdb --workers=1",
|
"test:e2e:couchdb": "npm test --workspace e2e -- --config=playwright-ci.config.js --project=chrome --grep @couchdb --workers=1",
|
||||||
"test:e2e:stable": "npm test --workspace e2e -- --config=playwright-ci.config.js --project=chrome --grep-invert \"@unstable|@couchdb|@generatedata\"",
|
"test:e2e:ci": "npm test --workspace e2e -- --config=playwright-ci.config.js --project=chrome --grep-invert \"@couchdb|@generatedata\"",
|
||||||
"test:e2e:unstable": "npm test --workspace e2e -- --config=playwright-ci.config.js --project=chrome --grep @unstable",
|
|
||||||
"test:e2e:local": "npm test --workspace e2e -- --config=playwright-local.config.js --project=chrome",
|
"test:e2e:local": "npm test --workspace e2e -- --config=playwright-local.config.js --project=chrome",
|
||||||
"test:e2e:generatedata": "npm test --workspace e2e -- --config=playwright-ci.config.js --project=chrome --grep @generatedata",
|
"test:e2e:generatedata": "npm test --workspace e2e -- --config=playwright-ci.config.js --project=chrome --grep @generatedata",
|
||||||
"test:e2e:checksnapshots": "npm test --workspace e2e -- --config=playwright-ci.config.js --project=chrome --grep @snapshot --retries=0",
|
"test:e2e:checksnapshots": "npm test --workspace e2e -- --config=playwright-ci.config.js --project=chrome --grep @snapshot --retries=0",
|
||||||
"test:e2e:updatesnapshots": "npm test --workspace e2e -- --config=playwright-ci.config.js --project=chrome --grep @snapshot --update-snapshots",
|
"test:e2e:updatesnapshots": "npm test --workspace e2e -- --config=playwright-ci.config.js --project=chrome --grep @snapshot --update-snapshots",
|
||||||
"test:e2e:visual:ci": "npm run test:visual --workspace e2e -- --config .percy.ci.yml --partial -- npx playwright test --config=playwright-visual-a11y.config.js --project=chrome --grep-invert @unstable",
|
"test:e2e:visual:ci": "npm run test:visual --workspace e2e -- --config .percy.ci.yml --partial -- npx playwright test --config=playwright-visual-a11y.config.js --project=chrome",
|
||||||
"test:e2e:visual:full": "npm run test:visual --workspace e2e -- --config .percy.nightly.yml -- npx playwright test --config=playwright-visual-a11y.config.js --grep-invert @unstable",
|
"test:e2e:visual:full": "npm run test:visual --workspace e2e -- --config .percy.nightly.yml -- npx playwright test --config=playwright-visual-a11y.config.js",
|
||||||
"test:e2e:full": "npm test --workspace e2e -- --config=playwright-ci.config.js --grep-invert @couchdb",
|
"test:e2e:full": "npm test --workspace e2e -- --config=playwright-ci.config.js --grep-invert @couchdb",
|
||||||
"test:e2e:watch": "npm test --workspace e2e -- --ui --config=playwright-watch.config.js",
|
"test:e2e:watch": "npm test --workspace e2e -- --ui --config=playwright-watch.config.js",
|
||||||
"test:perf:contract": "npm test --workspace e2e -- --config=playwright-performance-dev.config.js",
|
"test:perf:contract": "npm test --workspace e2e -- --config=playwright-performance-dev.config.js",
|
||||||
@ -133,7 +132,7 @@
|
|||||||
"update-copyright-date": "npm run update-about-dialog-copyright && grep -lr --null --include=*.{js,scss,vue,ts,sh,html,md,frag} 'Copyright (c) 20' . | xargs -r0 perl -pi -e 's/Copyright\\s\\(c\\)\\s20\\d\\d\\-20\\d\\d/Copyright \\(c\\)\\ 2014\\-2024/gm'",
|
"update-copyright-date": "npm run update-about-dialog-copyright && grep -lr --null --include=*.{js,scss,vue,ts,sh,html,md,frag} 'Copyright (c) 20' . | xargs -r0 perl -pi -e 's/Copyright\\s\\(c\\)\\s20\\d\\d\\-20\\d\\d/Copyright \\(c\\)\\ 2014\\-2024/gm'",
|
||||||
"cov:e2e:report": "nyc report --reporter=lcovonly --report-dir=./coverage/e2e",
|
"cov:e2e:report": "nyc report --reporter=lcovonly --report-dir=./coverage/e2e",
|
||||||
"cov:e2e:full:publish": "codecov --disable=gcov -f ./coverage/e2e/lcov.info -F e2e-full",
|
"cov:e2e:full:publish": "codecov --disable=gcov -f ./coverage/e2e/lcov.info -F e2e-full",
|
||||||
"cov:e2e:stable:publish": "codecov --disable=gcov -f ./coverage/e2e/lcov.info -F e2e-stable",
|
"cov:e2e:ci:publish": "codecov --disable=gcov -f ./coverage/e2e/lcov.info -F e2e-ci",
|
||||||
"cov:unit:publish": "codecov --disable=gcov -f ./coverage/unit/lcov.info -F unit",
|
"cov:unit:publish": "codecov --disable=gcov -f ./coverage/unit/lcov.info -F unit",
|
||||||
"prepare": "npm run build:prod && npx tsc"
|
"prepare": "npm run build:prod && npx tsc"
|
||||||
},
|
},
|
||||||
|
@ -115,7 +115,7 @@ export default {
|
|||||||
return this.model.buttons.submit.label;
|
return this.model.buttons.submit.label;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'OK';
|
return 'Ok';
|
||||||
},
|
},
|
||||||
cancelLabel() {
|
cancelLabel() {
|
||||||
if (this.model.buttons && this.model.buttons.cancel && this.model.buttons.cancel.label) {
|
if (this.model.buttons && this.model.buttons.cancel && this.model.buttons.cancel.label) {
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
type="file"
|
type="file"
|
||||||
:accept="acceptableFileTypes"
|
:accept="acceptableFileTypes"
|
||||||
style="display: none"
|
style="display: none"
|
||||||
|
aria-labelledby="fileSelect"
|
||||||
/>
|
/>
|
||||||
<button id="fileSelect" class="c-button" @click="selectFile">
|
<button id="fileSelect" class="c-button" @click="selectFile">
|
||||||
{{ name }}
|
{{ name }}
|
||||||
|
@ -20,10 +20,17 @@ this source code distribution or the Licensing information page available
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<div ref="tooltip-wrapper" class="c-menu c-tooltip-wrapper" :style="toolTipLocationStyle">
|
<div
|
||||||
<div class="c-tooltip">
|
ref="tooltip-wrapper"
|
||||||
|
class="c-menu c-tooltip-wrapper"
|
||||||
|
:style="toolTipLocationStyle"
|
||||||
|
role="tooltip"
|
||||||
|
aria-labelledby="tooltip-text"
|
||||||
|
aria-live="polite"
|
||||||
|
>
|
||||||
|
<span id="tooltip-text" class="c-tooltip">
|
||||||
{{ toolTipText }}
|
{{ toolTipText }}
|
||||||
</div>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ class ImageExporter {
|
|||||||
message: 'Image was not captured successfully!',
|
message: 'Image was not captured successfully!',
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
label: 'OK',
|
label: 'Ok',
|
||||||
emphasis: true,
|
emphasis: true,
|
||||||
callback: function () {
|
callback: function () {
|
||||||
errorDialog.dismiss();
|
errorDialog.dismiss();
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
>
|
>
|
||||||
<td
|
<td
|
||||||
ref="tableCell"
|
ref="tableCell"
|
||||||
|
scope="row"
|
||||||
aria-label="lad name"
|
aria-label="lad name"
|
||||||
class="js-first-data"
|
class="js-first-data"
|
||||||
@mouseover.ctrl="showToolTip"
|
@mouseover.ctrl="showToolTip"
|
||||||
|
@ -21,16 +21,20 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="c-lad-table-wrapper u-style-receiver js-style-receiver" :class="staleClass">
|
<div
|
||||||
<table aria-label="lad table" class="c-table c-lad-table" :class="applyLayoutClass">
|
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>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th scope="col">Name</th>
|
||||||
<th v-if="showTimestamp">Timestamp</th>
|
<th v-if="showTimestamp" scope="col">Timestamp</th>
|
||||||
<th>Value</th>
|
<th scope="col">Value</th>
|
||||||
<th v-if="hasUnits">Units</th>
|
<th v-if="hasUnits" scope="col">Units</th>
|
||||||
<th v-if="showType">Type</th>
|
<th v-if="showType" scope="col">Type</th>
|
||||||
<th v-for="limitColumn in limitColumnNames" :key="limitColumn.key">
|
<th v-for="limitColumn in limitColumnNames" :key="limitColumn.key" scope="col">
|
||||||
{{ limitColumn.label }}
|
{{ limitColumn.label }}
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -268,7 +268,7 @@ export default class DisplayLayoutToolbar {
|
|||||||
message: `Warning! This action will remove this item from the Display Layout. Do you want to continue?`,
|
message: `Warning! This action will remove this item from the Display Layout. Do you want to continue?`,
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
label: 'OK',
|
label: 'Ok',
|
||||||
emphasis: 'true',
|
emphasis: 'true',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
this.#openmct.objectViews.emit(
|
this.#openmct.objectViews.emit(
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
|
id="display-layout-drop-area"
|
||||||
class="l-layout u-style-receiver js-style-receiver"
|
class="l-layout u-style-receiver js-style-receiver"
|
||||||
:class="{
|
:class="{
|
||||||
'is-multi-selected': selectedLayoutItems.length > 1,
|
'is-multi-selected': selectedLayoutItems.length > 1,
|
||||||
@ -453,7 +454,7 @@ export default {
|
|||||||
message: 'This item is already in layout and will not be added again.',
|
message: 'This item is already in layout and will not be added again.',
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
label: 'OK',
|
label: 'Ok',
|
||||||
callback: function () {
|
callback: function () {
|
||||||
prompt.dismiss();
|
prompt.dismiss();
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user