Compare commits

...

173 Commits

Author SHA1 Message Date
Shefali
a07eb07090 Set the mode of the independent time conductor timecontext so that it's clock can tick. 2023-06-21 21:16:18 -07:00
Jamie V
9f55e4c98e WIP 2023-06-20 12:54:55 -07:00
Jamie V
17a1a74eed stil WIP, but progress made on independent and regular conductor popup finctionality, next steps polishing api 2023-06-15 11:58:10 -07:00
Jamie V
ec95faa57f WIP 2023-06-01 11:35:49 -07:00
Jamie V
488902f436 various changes, but mainly changes to independent time context 2023-05-24 16:34:43 -07:00
Jamie V
0db9a5e2a7 cleanup 2023-05-24 11:30:57 -07:00
Jamie V
1950839f80 itc work, moving mutating the domain object into itc so its in one place 2023-05-24 11:29:01 -07:00
Jamie V
0ddb49c11d forgot one 2023-05-24 09:10:45 -07:00
Jamie V
5f6cf7b0df using constants for events in the conductor components 2023-05-24 09:09:52 -07:00
Jamie V
295acdb36b cleaning up some items 2023-05-23 21:30:55 -07:00
Jamie V
d68a58f7e0 updated xAxis to new api, it was thrwoing many warnings for calling .clock, may want to look into that 2023-05-23 13:41:41 -07:00
Jamie V
4a2d903050 realtime working on load 2023-05-23 13:23:48 -07:00
Jamie V
7aa3762684 WIP 2023-05-22 16:05:13 -07:00
Jamie V
74cfab0ec0 working on independent time conductor part 2023-05-22 09:46:33 -07:00
Jamie V
1ebf566bb7 WIP: lots of updates to methods and moving component creation from programatic to v-ifs, mostly focused on conductor at the moment, independent next 2023-05-18 17:07:34 -07:00
Jamie V
784113012e moved conductor popup into conductor instead of creating the component on the fly, this way we are prepping for vue 3 and we can use v-if to destroy it 2023-05-10 12:50:09 -07:00
Jamie V
0260e06222 various changes for setting modes and clocks, also updates to conductor history for new api methods 2023-05-10 11:22:11 -07:00
Jamie V
266470a755 WIP 2023-05-09 17:34:48 -07:00
Jamie V
d851b01363 WIP 2023-05-02 12:05:40 -07:00
Jamie V
f41df68458 Merge branch 'time-api-enhancements' into mode-dropdown
Merging in shefali's time api updatesy
2023-04-28 13:41:19 -07:00
Jamie V
ac5c579874 WIP 2023-04-28 12:34:25 -07:00
Jamie V
204fb8c0e0 manually cherry picking over my changes to this existing branch 2023-04-26 14:32:42 -07:00
Shefali
c605cd7a17 Stubs for the new Time API methods 2023-04-25 21:43:15 -07:00
Shefali
a5db0f3b71 Ensure that clock changes are reflected downstream 2023-04-25 09:06:15 -07:00
Shefali
62483583eb Ensure independent time conductor mode works as expected 2023-04-24 21:46:28 -07:00
Shefali
6c63773641 object path for independent time conductor 2023-04-24 20:19:23 -07:00
Shefali
e27e315784 Independent time conductor popup draft 2023-04-24 13:18:31 -07:00
Shefali
5dc82742bf Add independent time conductor popup logic 2023-04-21 15:48:35 -07:00
Shefali
bd4d30f481 Ensure conductor history works when mode is switched. Submit and cancel work as expected 2023-04-19 21:03:26 -07:00
Shefali
b8322a8311 Merge branch 'master' of https://github.com/nasa/openmct into time-conductor-4975 2023-04-19 14:35:07 -07:00
Jesse Mazzella
c200999659 test(e2e): fix overlay plot element preview test flake (#6609)
test: wait for plot to be drawn before assertions
2023-04-19 13:40:44 -07:00
Jesse Mazzella
ddeeff4822 Fix test flake and playwright version in couchdb circleci test (#6608)
* test: wait for `domcontentloaded` on reload

* chore: playwright `1.32.3` in circleci couchdb test

* test: simplify(why was this creating a clock??)
2023-04-19 19:11:09 +00:00
dependabot[bot]
5610846147 chore(deps-dev): bump @deploysentinel/playwright from 0.3.3 to 0.3.4 (#6606)
Bumps @deploysentinel/playwright from 0.3.3 to 0.3.4.

---
updated-dependencies:
- dependency-name: "@deploysentinel/playwright"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-19 17:17:57 +00:00
John Hill
88fde47932 Add @types/lodash to dependabot semver ignore list (#6604)
Add lodash types to ignore list
2023-04-19 09:23:16 -07:00
Shefali
2d6c6a6b38 Save fixed time bounds 2023-04-18 23:08:25 -07:00
Shefali
f4e747a85e Ensure toggling between fixed timespan and clock modes works. Save clock offsets. 2023-04-18 21:51:19 -07:00
dependabot[bot]
2a0faba35f chore(deps-dev): bump eslint-plugin-vue from 9.10.0 to 9.11.0 (#6597)
Bumps [eslint-plugin-vue](https://github.com/vuejs/eslint-plugin-vue) from 9.10.0 to 9.11.0.
- [Release notes](https://github.com/vuejs/eslint-plugin-vue/releases)
- [Commits](https://github.com/vuejs/eslint-plugin-vue/compare/v9.10.0...v9.11.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-vue
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-18 16:41:58 -07:00
dependabot[bot]
a47abf5f96 chore(deps-dev): bump webpack-dev-server from 4.11.1 to 4.13.3 (#6596)
* chore(deps-dev): bump webpack-dev-server from 4.11.1 to 4.13.3

Bumps [webpack-dev-server](https://github.com/webpack/webpack-dev-server) from 4.11.1 to 4.13.3.
- [Release notes](https://github.com/webpack/webpack-dev-server/releases)
- [Changelog](https://github.com/webpack/webpack-dev-server/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-dev-server/compare/v4.11.1...v4.13.3)

---
updated-dependencies:
- dependency-name: webpack-dev-server
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix: disable webpack overlay for runtime errors

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jesse Mazzella <jesse.d.mazzella@nasa.gov>
2023-04-18 16:33:43 -07:00
Jesse Mazzella
968eee6698 chore: bump Playwright to v1.32.3 (#6511)
* chore: bump Playwright to v1.32.1

* test: fix locators, remove unnecessary awaits

* chore: bump Playwright in ci workflows

* test: better selectors for yAxis configs

- fix tests

* chore: bump Playwright to 1.32.3

* refactor: ensure openmct starts after plugins install

* fix: wait for domcontentloaded on initial nav

* test: fix autoscale snapshot test

* test: fix `--max-failures` argname typo

* test: update old locators

* test(fix): add missing await

* test: fix typo 😅
2023-04-18 22:32:29 +00:00
John Hill
43d56a68bb Bump for 2.2.3 (#6600)
Update package.json

Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2023-04-18 21:13:05 +00:00
Jesse Mazzella
f055a8a0c7 fix(e2e): remove unnecessary wait for networkidle and fix selectors (#6370) 2023-04-18 20:25:43 +00:00
Shefali
9ccdbcede8 Merge branch 'master' of https://github.com/nasa/openmct into time-conductor-4975 2023-04-18 11:12:38 -07:00
John Hill
2820237d60 Fixes the way we start and stop couchdb on bare metal CI agents (#6589) 2023-04-17 14:26:13 -07:00
Shefali
2e04c686f4 Initial work on getting the conductor popup working 2023-04-17 13:36:05 -07:00
Jesse Mazzella
dbdc9bb4e2 fix(#6549): [StaticRootPlugin] Remap non-empty root namespaces and non-root namespaces correctly (#6583)
* fix: preserve truthy namespaces and unmapped values

- Fixes the issue of the Static Root provider overriding existing namespaces, such as those from a telemetry dictionary
- Fixes the issue of keys of child objects NOT present in the idMapping (such as those from a telemetry dictionary) being overwritten as undefined
- TODO: This will not work for objects exported from an environment that has the "MyItems" namespace defined to anything other than an empty string. Need to figure out how to handle this.

* fix: handle the case of rootId having a namespace !== ""

* refactor: use `parseKeyString`

* fix: StaticRootPlugin object mapping for non-empty namespaces

* fix: use index, fix location identifiers

* tets: add non-empty namespace tests (wip)

* refactor: rename and move test files

* test: update StaticModelProvider tests

* fix: remap to identifiers for config, not keystring

---------

Co-authored-by: Khalid Adil <khalidadil29@gmail.com>
2023-04-14 18:31:04 -05:00
Jesse Mazzella
a9a98380f2 test(visual): add theme to notification banner test name (#6450)
* test(visual): add theme to notification banner test name

* test: rename file

* test: switch to small example plan

* test: snapshot object-view only

* test: fix selectors

* refactor: chain locators together
2023-04-13 10:02:56 -07:00
dependabot[bot]
e3ab085dd5 chore(deps-dev): bump webpack from 5.78.0 to 5.79.0 (#6588)
Bumps [webpack](https://github.com/webpack/webpack) from 5.78.0 to 5.79.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.78.0...v5.79.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-12 15:38:59 -07:00
dependabot[bot]
519135527b chore(deps-dev): bump sass from 1.61.0 to 1.62.0 (#6587)
Bumps [sass](https://github.com/sass/dart-sass) from 1.61.0 to 1.62.0.
- [Release notes](https://github.com/sass/dart-sass/releases)
- [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/dart-sass/compare/1.61.0...1.62.0)

---
updated-dependencies:
- dependency-name: sass
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-12 13:44:16 -07:00
John Hill
fc37f6e05b Move Dependabot to weekly to reduce frequency of changes and reduce CI Bill (#6585)
Update dependabot.yml
2023-04-12 11:11:41 -07:00
dependabot[bot]
ab1df89396 chore(deps-dev): bump webpack from 5.77.0 to 5.78.0 (#6559)
Bumps [webpack](https://github.com/webpack/webpack) from 5.77.0 to 5.78.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.77.0...v5.78.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-11 12:25:59 -07:00
dependabot[bot]
9ee5ab96f3 chore(deps-dev): bump sass from 1.59.3 to 1.61.0 (#6569)
Bumps [sass](https://github.com/sass/dart-sass) from 1.59.3 to 1.61.0.
- [Release notes](https://github.com/sass/dart-sass/releases)
- [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/dart-sass/compare/1.59.3...1.61.0)

---
updated-dependencies:
- dependency-name: sass
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-11 12:09:45 -07:00
dependabot[bot]
8b2c6e3fb3 chore(deps-dev): bump @percy/cli from 1.22.0 to 1.23.0 (#6578)
Bumps [@percy/cli](https://github.com/percy/cli/tree/HEAD/packages/cli) from 1.22.0 to 1.23.0.
- [Release notes](https://github.com/percy/cli/releases)
- [Commits](https://github.com/percy/cli/commits/v1.23.0/packages/cli)

---
updated-dependencies:
- dependency-name: "@percy/cli"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-11 08:15:48 -07:00
John Hill
b8b0a08eeb Add Code Coverage Doc and some other drive-bys (#5724) 2023-04-10 12:25:17 -07:00
dependabot[bot]
633b6be2fd chore(deps-dev): bump typescript from 4.9.5 to 5.0.4 (#6574)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.9.5 to 5.0.4.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.9.5...v5.0.4)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-10 11:21:16 -07:00
Shefali Joshi
4963aff8a0 Only decrement and save if there is composition but no child object reference (#6568)
Co-authored-by: David Tsay <3614296+davetsay@users.noreply.github.com>
2023-04-07 06:17:52 +00:00
John Hill
6786be54fa Add safari as well as iOS safari browserlist check (#6567)
* Add safari as well as iOS safari browserlist check

* Update package.json

* math
2023-04-06 16:51:28 -07:00
Charles Hacskaylo
508c2ebd87 Closes #4975
- CSS fix for to-be-deprecated division operation.
2023-04-06 15:56:09 -07:00
dependabot[bot]
b081389e68 chore(deps-dev): bump @percy/cli from 1.21.0 to 1.22.0 (#6565)
Bumps [@percy/cli](https://github.com/percy/cli/tree/HEAD/packages/cli) from 1.21.0 to 1.22.0.
- [Release notes](https://github.com/percy/cli/releases)
- [Commits](https://github.com/percy/cli/commits/v1.22.0/packages/cli)

---
updated-dependencies:
- dependency-name: "@percy/cli"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-06 14:34:59 -07:00
Shefali
892963aa0e Merge branch 'master' of https://github.com/nasa/openmct into time-conductor-4975 2023-04-06 09:38:13 -07:00
Jamie V
7a3ec3a241 Fix ExportAsJSONAction to not lose layout configurations on linked objects (#6562) 2023-04-05 23:03:23 -07:00
John Hill
c0c383bf18 First attempt at DeploySentinel and fix couchdb notebook tests (#6398)
* First attempt

* Remove commented pattern

* Add deploysentinel to github action

* drive by

* Stablization

* remove only

* entries now selected on creation

* select previous entry on deletion

* add deletion test

* wip

* fix adding focus selection

* remove previous entry selection logic

* null check for event

* address review comments

* address review comments

* refactor tests a bit

* typo

* Add some determinism to avoid console errors

* refactor and use methods

* stabilize

* remove debug

* remove only

* combine clean commands

* comments

* change to expects

* test: await toBeHidden() assertion

* test: use `myItemsFolderName` instead of 'My Items'

---------

Co-authored-by: Scott Bell <scott@traclabs.com>
Co-authored-by: Jesse Mazzella <jesse.d.mazzella@nasa.gov>
2023-04-05 06:25:28 -07:00
John Hill
fe1c99de12 First attempt at DeploySentinel and fix couchdb notebook tests (#6398)
* First attempt

* Remove commented pattern

* Add deploysentinel to github action

* drive by

* Stablization

* remove only

* entries now selected on creation

* select previous entry on deletion

* add deletion test

* wip

* fix adding focus selection

* remove previous entry selection logic

* null check for event

* address review comments

* address review comments

* refactor tests a bit

* typo

* Add some determinism to avoid console errors

* refactor and use methods

* stabilize

* remove debug

* remove only

* combine clean commands

* comments

* change to expects

* test: await toBeHidden() assertion

* test: use `myItemsFolderName` instead of 'My Items'

---------

Co-authored-by: Scott Bell <scott@traclabs.com>
Co-authored-by: Jesse Mazzella <jesse.d.mazzella@nasa.gov>
2023-04-05 06:24:57 -07:00
Scott Bell
2e60da0401 Ensure annotations on empty entries in notebook are not lost (#6525)
* entries now selected on creation

* select previous entry on deletion

* add deletion test

* wip

* fix adding focus selection

* remove previous entry selection logic

* null check for event

* address review comments

* address review comments

* refactor tests a bit

* typo

* remove clicking on entries
2023-04-04 14:37:38 -07:00
Vitor Henckel
bc3a5408b4 fix(#6503): Recently Viewed Items - Disable button if no items (#6533)
* add e2e test

* fix: remove slow function

* test: After deactivating the button, new objects must be inserted and the button becomes active again

* test: ensure clear recent objects button is active or inactive

* add an event to notify when an object is inserted in the recents list

* add an event to notify when an object is inserted in the recents list

* fix: adjusting function name and add validation for triggering the event

* fix: add event to disable button only when user click ok to clear list of recents

* test: fix failing e2e + better assertions

---------

Co-authored-by: Jesse Mazzella <jessemazzella@gmail.com>
2023-04-03 23:56:03 +00:00
John Hill
344bf8eed3 [CI] Stop taking patch releases for moment-timezone (#6553)
Co-authored-by: Jesse Mazzella <ozyx@users.noreply.github.com>
2023-04-03 17:20:09 +00:00
John Hill
cbb3368937 chore: bump version to 2.2.2-SNAPSHOT (#6552)
Update package.json
2023-04-03 09:06:24 -07:00
Scott Bell
b7a671d392 Allow Restricted Notebooks to export text (#6542)
* Refactor string to stream

* add to restricted notebooks and allow for blank users

* forgot to add notebook

* use better types and fix test

* move streamToString

* add export group

* catch blank pages
2023-04-01 07:08:22 +02:00
John Hill
4f10a93ef5 Use node-version: 'lts/gallium' (#6540)
* Update e2e-couchdb.yml

* this really doesn't like it

---------

Co-authored-by: Jesse Mazzella <ozyx@users.noreply.github.com>
2023-03-31 11:55:20 -07:00
dependabot[bot]
f8186e4b4e chore(deps-dev): bump karma-sourcemap-loader from 0.3.8 to 0.4.0 (#6290)
Bumps [karma-sourcemap-loader](https://github.com/demerzel3/karma-sourcemap-loader) from 0.3.8 to 0.4.0.
- [Release notes](https://github.com/demerzel3/karma-sourcemap-loader/releases)
- [Changelog](https://github.com/demerzel3/karma-sourcemap-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/demerzel3/karma-sourcemap-loader/commits/0.4.0)

---
updated-dependencies:
- dependency-name: karma-sourcemap-loader
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-31 02:53:09 -07:00
dependabot[bot]
4e0c364d89 chore(deps-dev): bump sass-loader from 13.2.1 to 13.2.2 (#6513)
Bumps [sass-loader](https://github.com/webpack-contrib/sass-loader) from 13.2.1 to 13.2.2.
- [Release notes](https://github.com/webpack-contrib/sass-loader/releases)
- [Changelog](https://github.com/webpack-contrib/sass-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/sass-loader/compare/v13.2.1...v13.2.2)

---
updated-dependencies:
- dependency-name: sass-loader
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-31 04:04:35 +00:00
dependabot[bot]
f3bed9c651 chore(deps-dev): bump webpack from 5.76.3 to 5.77.0 (#6520)
Bumps [webpack](https://github.com/webpack/webpack) from 5.76.3 to 5.77.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.76.3...v5.77.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-30 20:54:11 -07:00
dependabot[bot]
4d93907d58 chore(deps-dev): bump eslint-plugin-compat from 4.1.1 to 4.1.2 (#6352)
Bumps [eslint-plugin-compat](https://github.com/amilajack/eslint-plugin-compat) from 4.1.1 to 4.1.2.
- [Release notes](https://github.com/amilajack/eslint-plugin-compat/releases)
- [Changelog](https://github.com/amilajack/eslint-plugin-compat/blob/main/CHANGELOG.md)
- [Commits](https://github.com/amilajack/eslint-plugin-compat/compare/v4.1.1...v4.1.2)

---
updated-dependencies:
- dependency-name: eslint-plugin-compat
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jesse Mazzella <ozyx@users.noreply.github.com>
2023-03-30 15:37:59 -07:00
John Hill
6f656a6783 [Build]Remove Node14LTS from supported versions and update our pipelines (#6527)
* bump minimumum and pin drivebys

* move to 16 and remove 14

* remove 14

* update to latest
2023-03-30 20:53:44 +00:00
Jesse Mazzella
767fb6c5fd fix: remove redundant request on FaultManagement mount (#6502)
* fix: remove redundant update request

* fix: handle case where request returns no faults

* test: fix fault management tests

* docs: clean up FaultManagement API types

---------

Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
2023-03-30 18:43:55 +00:00
dependabot[bot]
b0a0b4bb58 chore(deps-dev): bump eslint from 8.36.0 to 8.37.0 (#6521)
Bumps [eslint](https://github.com/eslint/eslint) from 8.36.0 to 8.37.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.36.0...v8.37.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jesse Mazzella <ozyx@users.noreply.github.com>
2023-03-30 11:15:36 -07:00
dependabot[bot]
340f4a9e79 chore(deps-dev): bump @percy/cli from 1.17.0 to 1.21.0 (#6439)
Bumps [@percy/cli](https://github.com/percy/cli/tree/HEAD/packages/cli) from 1.17.0 to 1.21.0.
- [Release notes](https://github.com/percy/cli/releases)
- [Commits](https://github.com/percy/cli/commits/v1.21.0/packages/cli)

---
updated-dependencies:
- dependency-name: "@percy/cli"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jesse Mazzella <ozyx@users.noreply.github.com>
2023-03-30 11:05:32 -07:00
Scott Bell
3007b28b0f Simple text export of Notebook (#6510)
* add simple prototype

* tags and metadata now exported

* add form for options

* revert notebook

* add simple e2e test

* add test stubs

* death to debug
2023-03-30 19:44:12 +02:00
David Tsay
20789601b4 Add contextual domain object back for contextual row actions (#6524)
* re-enable historical row action
2023-03-30 10:08:09 -07:00
Shefali Joshi
a56cfed732 Remove ticker and rely solely on the clock ticks to update the timelist durations (#6495)
* Remove ticker for timelist and rename a function for readability

* Use formatting for remote clock if available.

* throttle updates to the timestamp and listing activities

---------

Co-authored-by: Andrew Henry <akhenry@gmail.com>
2023-03-29 15:16:52 -07:00
Charles Hacskaylo
7ec2c4475b LAD Tables now disallow user-select (#6322)
* Closes #6321
- Set `user-select: none` on LAD Table td elements.
- Added hover effect to LAD Table rows as affordance of the Action
menu's availability.

* Add test to ensure rows cannot be selected but do show context menus

---------

Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2023-03-28 14:38:52 -07:00
dependabot[bot]
8f59b16465 chore(deps-dev): bump @types/lodash from 4.14.191 to 4.14.192 (#6512)
Bumps [@types/lodash](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/lodash) from 4.14.191 to 4.14.192.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/lodash)

---
updated-dependencies:
- dependency-name: "@types/lodash"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-28 16:09:39 +00:00
Jamie V
36cfb1d515 [Condition Widgets] Keep styles for widgets with a URL (#6515)
* call update style after view is update with current style rule manager styles for components (namely condition widget) that have DOM changes after the element is grabbed to style

* target blank yo

---------

Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2023-03-28 15:58:15 +00:00
David Tsay
2ff7132e90 use relative path (#6518) 2023-03-27 23:52:54 +00:00
Khalid Adil
d0ca398e01 [Plots, Multiple-Y Axis] Fix for rectangle drawn on each axis (#6490)
* Change logic so that the rectangle is only drawn on one axis

* Filter firstDrawableAxis before the rest of the logic

* Update to filter yAxisIds by the canDraw function
2023-03-24 13:46:10 -07:00
Jesse Mazzella
59278e8a06 chore: bump version to 2.2.1-SNAPSHOT (#6501) 2023-03-23 16:27:31 -07:00
dependabot[bot]
c8377f392b chore(deps-dev): bump eslint-plugin-vue from 9.9.0 to 9.10.0 (#6500)
Bumps [eslint-plugin-vue](https://github.com/vuejs/eslint-plugin-vue) from 9.9.0 to 9.10.0.
- [Release notes](https://github.com/vuejs/eslint-plugin-vue/releases)
- [Commits](https://github.com/vuejs/eslint-plugin-vue/compare/v9.9.0...v9.10.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-vue
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jesse Mazzella <ozyx@users.noreply.github.com>
2023-03-23 15:54:10 -07:00
Vitor Henckel
29df748f2b fix(#6457): Tags disappear after resizing then dismissing the Add Tag select (#6499) 2023-03-23 14:48:56 -07:00
Vitor Henckel
665ba6dae1 fix(#6347): Searching a Notebook with whitelisted links for '.' exposes some html (#6396) 2023-03-23 21:23:49 +00:00
David Tsay
f39f8df4e2 Suppress annotations tab if no annotations defined (#6498)
* fix bad copy paste

* suppress annotations inspector tab if no tags
2023-03-23 14:00:46 -07:00
Marcelo Arias
4aa572d489 Button to clear the recent objects list (#6327) 2023-03-23 19:53:01 +00:00
Jesse Mazzella
0b24c4f2c5 fix(#6488): better determination of child tree items when collapsing a parent (#6489)
Splits the parent and child navigationPaths into arrays of keystrings and then checks to ensure that every keystring in the parent path is included in the child path in order
2023-03-23 19:02:44 +00:00
dependabot[bot]
e4657f79cd chore(deps-dev): bump plotly.js-basic-dist from 2.17.0 to 2.20.0 (#6438)
Bumps [plotly.js-basic-dist](https://github.com/plotly/plotly.js) from 2.17.0 to 2.20.0.
- [Release notes](https://github.com/plotly/plotly.js/releases)
- [Changelog](https://github.com/plotly/plotly.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/plotly/plotly.js/compare/v2.17.0...v2.20.0)

---
updated-dependencies:
- dependency-name: plotly.js-basic-dist
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2023-03-22 22:09:15 +00:00
dependabot[bot]
f2059406e0 chore(deps-dev): bump webpack from 5.76.2 to 5.76.3 (#6494)
Bumps [webpack](https://github.com/webpack/webpack) from 5.76.2 to 5.76.3.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.76.2...v5.76.3)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-22 13:35:24 -07:00
dependabot[bot]
3e3dc7dd83 chore(deps-dev): bump webpack from 5.74.0 to 5.76.2 (#6440)
Bumps [webpack](https://github.com/webpack/webpack) from 5.74.0 to 5.76.2.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.74.0...v5.76.2)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-22 19:22:53 +00:00
Charles Hacskaylo
50742c4f82 Hide Flexible Layout header buttons when frame is hidden (#6486)
- Hide nested frame header buttons for Flexible Layouts as well as Display Layouts.
- Adjust z-index of frame control header buttons.
2023-03-21 17:24:41 -07:00
Vitor Henckel
2f04add2a3 fix(#6408): Zooming in the corner of an image makes it fly away (#6410)
* fix(#6480): Zooming in the corner of an image makes it fly away

* fix: keeping layers in the right place even when zoomed in
2023-03-22 00:15:02 +00:00
Charles Hacskaylo
0ce5060246 Fix Imagery local controls z-index (#6482)
- Corrected z-index for imagery local controls.
2023-03-22 00:02:34 +00:00
Scott Bell
00353cdccf Cancel annotation selections if you click outside the plot component (#6476)
* resolve conflicts

* resolve conflicts

* more selectively add listeners

* add and fix tests

* address PR review comments

* test(e2e): stabilize flaky overlayPlot test

---------

Co-authored-by: Jesse Mazzella <ozyx@users.noreply.github.com>
Co-authored-by: Jesse Mazzella <jesse.d.mazzella@nasa.gov>
2023-03-21 16:53:24 -07:00
dependabot[bot]
a1ac209d74 chore(deps-dev): bump plotly.js-gl2d-dist from 2.17.1 to 2.20.0 (#6441)
Bumps [plotly.js-gl2d-dist](https://github.com/plotly/plotly.js) from 2.17.1 to 2.20.0.
- [Release notes](https://github.com/plotly/plotly.js/releases)
- [Changelog](https://github.com/plotly/plotly.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/plotly/plotly.js/compare/v2.17.1...v2.20.0)

---
updated-dependencies:
- dependency-name: plotly.js-gl2d-dist
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-21 13:57:59 -07:00
dependabot[bot]
bdd8477b54 chore(deps-dev): bump sass from 1.57.1 to 1.59.3 (#6442)
Bumps [sass](https://github.com/sass/dart-sass) from 1.57.1 to 1.59.3.
- [Release notes](https://github.com/sass/dart-sass/releases)
- [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/dart-sass/compare/1.57.1...1.59.3)

---
updated-dependencies:
- dependency-name: sass
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-21 19:27:17 +00:00
dependabot[bot]
f690f36bfb chore(deps-dev): bump sass-loader from 13.2.0 to 13.2.1 (#6480)
Bumps [sass-loader](https://github.com/webpack-contrib/sass-loader) from 13.2.0 to 13.2.1.
- [Release notes](https://github.com/webpack-contrib/sass-loader/releases)
- [Changelog](https://github.com/webpack-contrib/sass-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/sass-loader/compare/v13.2.0...v13.2.1)

---
updated-dependencies:
- dependency-name: sass-loader
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2023-03-21 12:11:42 -07:00
Shefali Joshi
e174f075df Don't initialize the selected condition style when a view is loaded (#6478)
* Don't initialize the selected condition style when a view is loaded

* Ensure selection of styles in Edit mode works as expected. When out of edit mode, only a computed style should be chosen to display.
2023-03-20 17:06:10 -07:00
Michael Rogers
8cf12db104 Add a view action button to toggle on class to control fixed layout (#6465)
* Add a view action buttons to toggle on class to control fixed layout
* Add configuration watcher and initial view action
* Added next tick in mount and updated action key
* Updated the view action key
2023-03-17 15:30:35 -07:00
David Tsay
453b1f3009 fixes to entries (#6464)
* fixes to entries
fix delete first notebook entry
fix select unfocused on create
* do not blur if nothing is focused
2023-03-17 22:14:15 +00:00
Jesse Mazzella
201c669328 fix(#6455): fix Create modal tree infinite loop (#6462)
* fix(#6455): fix infinite loop

- When the Create modal is opened, it defaults the object selected in the tree to the parent of the currently selected object. However, if this object is static, it can sometimes have a weird navigationPath and an edge case where we try to infinitely walk up the path to find the parent.

- This adds a fail-safe to verify that the navigationPath by this point contains `/browse/mine` (thus is within a creatable path). If not, it sets the default selected tree item to the "My Items" folder

---------

Co-authored-by: Andrew Henry <akhenry@gmail.com>
2023-03-17 22:03:23 +00:00
Shefali Joshi
1b7fb9b952 Ensure that datum that is of type array is formatted as such (#6467)
* Ensure that datum that is of type array is formatted as such
* Ensure the requestEnded event is emitted even when historicalData is empty.
2023-03-17 14:22:51 -07:00
Charles Hacskaylo
a3c5450205 Improvements to reduce repainting (#5876) 2023-03-17 20:07:57 +00:00
Scott Bell
8831b75c5d Clicking a plot annotation should pause the plot (#6446)
* free plot on search selection and remove rectangles when resuming

* add test

* remove rectanges

* update test

* more reliable way to load annotations

* use event to wait for axes and update tests

* restore test

* cancel selection in plots if clicking outside plot

* Revert "cancel selection in plots if clicking outside plot"

This reverts commit 82ea50152b.

* Listen to the navigation triggered selection of the target object before selecting the annotations for the object

* remove commented out code

* check if we've already navigated to the object

---------

Co-authored-by: Khalid Adil <khalidadil29@gmail.com>
Co-authored-by: Shefali <simplyrender@gmail.com>
2023-03-17 20:12:52 +01:00
dependabot[bot]
8fe0472af2 chore(deps-dev): bump mini-css-extract-plugin from 2.7.2 to 2.7.5 (#6463)
Bumps [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) from 2.7.2 to 2.7.5.
- [Release notes](https://github.com/webpack-contrib/mini-css-extract-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/mini-css-extract-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/mini-css-extract-plugin/compare/v2.7.2...v2.7.5)

---
updated-dependencies:
- dependency-name: mini-css-extract-plugin
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2023-03-17 09:44:28 -07:00
Scott Bell
6cb5c47f3a Conditional set output is wrong (#6244)
* Only use default if we've evaluated as default
* Add e2e test for conditional sets
---------

Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2023-03-17 15:58:09 +00:00
Khalid Adil
eff0cc96b9 Add check for minimized notification (#6431) 2023-03-17 15:46:09 +00:00
Michael Rogers
6ac7f24c63 Event limit severity css classes (#6445)
* Duplicated event limit css classes

* Closes akhenry/openmct-yamcs#287
- New theme constant values for event styling.
- CSS def for `is-event*` classes moved to correct location.

---------

Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com>
2023-03-17 15:24:35 +00:00
Jamie V
39463c515f [Greedy LAD] Add new functionality for Latest Available Data views (#6432)
* adding greedyLAD logic to telemetry collections
---------

Co-authored-by: Andrew Henry <akhenry@gmail.com>
2023-03-16 19:12:11 -07:00
Jamie V
25c0dab346 [Remove Action][Move Action] Update logic when working with locked, aliased domain objects (#6384)
* Allow move action for locked shift logs.
* Allow remove action for links to locked shift logs.
---------

Co-authored-by: Andrew Henry <akhenry@gmail.com>
2023-03-16 13:53:25 -07:00
Michael Rogers
3714958627 Set table to fixed layout and ellipted overflowing cells (#6453) 2023-03-16 15:16:46 -05:00
dependabot[bot]
270a3d4f49 chore(deps-dev): bump eslint from 8.35.0 to 8.36.0 (#6420)
Bumps [eslint](https://github.com/eslint/eslint) from 8.35.0 to 8.36.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.35.0...v8.36.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-16 18:43:25 +00:00
David Tsay
1dc137f95e [Time] use clock current value instead of end bounds for current time (#6405)
* use clock current value instead of end bounds for current time

* add unit test
2023-03-16 17:55:01 +00:00
Jesse Mazzella
ff3a20e446 feat: configurable Plan Views for reducing vertical scroll distance (#6415)
* refactor: convert Type API to ES6 module

- Another AMD module bites the dust 🧹

* feat: add initial configurable plan type

- Name change TBD

* feat: add `clipActivityNames` property

- refactor: initialize data to `null`

* refactor: general code cleanup

* feat(WIP): name clipping via clipPath elements

* feat: compose a Gantt Chart using a Plan

- Allows Plans to be dragged into Gantt Charts (name tentative) to create a configurable Activity View

- Clip/Unclip activity names by editing domainObject property

* feat: replace Plan if another is dragged in

- SImilar to Gauges or Scatter Plots, launch a confirmation dialog to replace the existing Plan with another, if another Plan is dragged into the chart.

* test: fix tests, add basic tests for gantt

* tes(e2e): fix plan test

* docs: add TODO

* refactor: clean up more string literals

* style: remove `rx`, increase min width

- round widths to nearest integer

* refactor: extract timeline creation logic

- extracts the logic for creating the timeline into its own component, `ActivityTimeline.vue`. This will save us a lot of re-renders, as we were manually creating elements / clearing them on each tick

* style: fix text y-pos and don't round

* fix: make activities clickable again

* docs: add copyright docs

* feat: swimlane visibility

- configure plan view from inspector

fix: update plans when file changes

- fix gantt chart display in time strips

- code cleanup

* fix: gantt chart embed in time strip

* remove viewBox for now

* fix: make `clipPath` ids more unique

* refactor: more code cleanup

* refactor: more code cleanup

* test: fix existing Plan unit tests

* refactor: rename variables

* fix: respond to code review comments

- Move config manipulation to PlanViewConfiguration.js/.vue

- Variable renames, code refactoring

* fix: unique, reproducible clipPathIds

* fix: only mutate swimlaneVisibility once on init

* fix: really make clipPathId unique this time

* refactor: use default config

* Closes #6113
- Refined CSS class naming and application.
- Set cursor to pointer for Activity elements.
- Added <title> node to Activity elements.
- Styling for selected Activities.
- Better Inspector tab name.

* fix: make Plan creatability configurable and false by default

* test: fix existing tests and add a couple new ones

* Closes #6113
- Now uses SVG <symbol> instead of rect within Activity element.
- Passes in `rowHeight` as a prop from Plan.vue.
- SWIMLANE_PADDING const added and used to create margin at top and bottom
edges of swimlanes.
- Refined styling for selected activities.
- New `$colorGanttSelectedBorder` theme constant.
- Smoke tested in Espresso and Snow themes.

* fix: default swimlaneWidth to clientWidth

* test: fix test

* feat: display selected activity name as header

* fix: remove redundant listener

* refactor: move `examplePlans.js` into `test-data/`

* docs: remove copyright header

* refactor: move `helper.js` into `helper/`

* refactor: `helper.js` -> `planningUtils.js`

* fix: update pathing

* test: add tests for gantt/plan

- add visual tests for gantt / plan

- add test for clicking a single activity and verifying its contents in the inspector

---------

Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com>
2023-03-16 10:34:31 -07:00
David Tsay
0b3e0e7efd do not show loaded tabs before showing current tab (#6424)
* do not show loaded tabs before showing current tab

* clean up logic to show added tabs

* fix unit tests

* remove unnecessary mocks

* handle objects current tab when last tab removed

---------

Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2023-03-15 22:29:27 +00:00
Jesse Mazzella
22cc28d733 fix(#6413): Inspector View tab priority (#6414)
* fix: inspector view tab priority

- fixes issue where inspector view priorities were not being passed to the view registry

* chore: run lint:fix

- eslint sez no danglin' commas! EVER!

* fix: update more viewProviders

---------

Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2023-03-15 14:47:40 -07:00
Jesse Mazzella
006fa0bcc7 fix(#6391): refresh object composition before expanding a tree item (#6437)
* fix(#6391): refresh composition on treeItem open

- On treeItem open, gets the latest composition from persistence

- Composition was being refreshed, but only within the same instance (mutables)

* test: regression tests for localStorage and couch

---------

Co-authored-by: Scott Bell <scott@traclabs.com>
2023-03-15 17:17:48 +00:00
Scott Bell
817d8da3e4 Allow tag cancelation (#6436)
* add e2e test

* remove visual test as we can cover this with functional tests
2023-03-15 18:08:54 +01:00
Michael Rogers
8df81f0ea9 Changed tooltip text to be Shift+Alt drag to pan (#6417)
* Changed tooltip text to be Shift+Alt drag to pan

* Updated e2e test to match alt text change

* Updated panHotKey to match changed alt text

---------

Co-authored-by: John Hill <john.c.hill@nasa.gov>
2023-03-14 20:42:31 -07:00
Scott Bell
1f30706d27 Differentiate aggregate telenmetry in LAD tables (#6406)
* allow lad tables rows to be selected

* rows now clickable and previewable

* trying to add type column

* aggregate and telemetry

* if aggregate, use blank for value and timestamps

* remove extraneous path lookup

* cleanup css

* add tests

* allow hiding of type column

* adjust tests to include type column
2023-03-15 01:31:15 +00:00
Khalid Adil
600890c4a6 [Missing Object] Notifications are shown as minimized (#6416)
* Add minimize to the notification model and minimize missing object notifications

* Add test

* Short circuit telemetry api functions if passed in domainObject is missing/type unknown

* Clear notifications properly after test
2023-03-14 13:36:45 -05:00
Jamie V
b5002e166a [LAD Table/Table Sets] Configurable hidden columns (#6386)
* adding lad table configuration, specifically column visibility

* making sure units column checkbox is updated when lad tables are removed from lad table sets

* fixes based on PR feedback, copyright, consts, code, oh my!

* added a test for column hiding

* remove .only

* add a notification for inspector view that must be editing, move selection logic to vue component as it was not being triggered after navigating away in the inspector tabs
2023-03-11 13:01:33 +01:00
Charles Hacskaylo
39cff51db0 Fix Notebook enter key 6354 (#6365)
* Closes #6354 Notebook Enter key adds new lines
- Removed enter key handlers from Vue component.
- Added "Save" button.
* entry must be selected before editing
* focus on newly created entry
* Closes #6354 Notebook Enter key adds new lines
- Removed enter key handlers from Vue component.
- Added "Save" button.
* do not allow edit unless entry is selected
* remove css for disabled cass

---------

Co-authored-by: David Tsay <david.e.tsay@nasa.gov>
2023-03-10 11:00:01 -08:00
Jesse Mazzella
73734d99ea fix(#6338): LimitLines persist when series is moved to another Y Axis (#6367)
* fix: remove unnecessary emit

* refactor: fix parameter names

* fix: update limit lines on axis reset

* Revert "fix: remove unnecessary emit"

This reverts commit b9a92e5e96.

* refactor: adjust parameter names

* test: add test for limit lines visibility

* refactor: convert to one-liner

---------

Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2023-03-08 00:19:16 +00:00
David Tsay
1d4cf1ff06 feat: Inspector tabs (#6137)
* framework for all inspector views being provided

* move elements view to plugin

* move location view into plugin

* move styles view into plugin

* move properties view into plugin

* install inspector views in index.html

* rename filters inspector view provider for tab

* finish elements view as plugin

* finish location view as plugin

* finish properties view as plugin

* finish styles view as plugin

* point main styles to new plugins

* finish inspector tab and views components

* fix paths for styles views

* fix path issues

* rename fault management inspector view

fix unit test

* fix paths for unit tests

* rename bar graph inspector view

fix unit test

* rename plots inspector view

fix unit test

* inspector views installed in mct.js

* sort inspector views by priority

* make name required for inspector tabs

* priority changes

* only show filters tab if filters exist

* object renamed to domainObject

* remove dead code

* select first tab if selected tab becomes hidden

* bandaid fix to get e2e working

* also apply bandaid to this test

* [a11y] Basic ARIA tab role for Inspector panels

* test(e2e): better selectors for scatterPlot test

* test(e2e): fix search test selector

* pass key and glyph to views

* use key for tabs identification

* high + 1 priority for object specific views

* Closes #6118
- Significant layout and behavior refinements to Inspector tabs.
- New theme constants for tabs.
- Tabs in Tab Views updated to use theme constants.

* Closes #6118
- Refinement to look of Inspector tabs.
- Shortened names in many *InspectorViewProvider.js files.
- WIP adding glyph capability, display not yet wired up.

* Closes #6118
- Tightened H2 spacing in Inspector.

* move annotations into plugin

* register annotations view provider

* move tags inside annotations

* fix paths

* move element item group into plugin

* move PlotElementsPool view into plugin

* plots has a different element view

* fix paths for plot elements pool

* fix: `role=` instead of `aria-role=` 🤦‍♂️

* test(e2e): fix tab locators

* move location views into properties tab view

* include location.scss

* move location into properties tab

* fix html for location within properties view

* retain selected tab on new selection

* refresh view of same tab with new selection

* add browse mode inspector view for alphanumerics

* fix prop passing

* removed vestigial code

* fix inspector tab selection

* remove timeouts and unnessecary awaits

* test: assert checkbox status before checking

* add selectInspectorTab to general app actions

* use selectInspectorTabs from appActions

* need to pass page to playwright function

* select the correct tab

* fix plan unit test

* fix plots tests by clicking on correct tab

---------

Co-authored-by: Jesse Mazzella <jesse.d.mazzella@nasa.gov>
Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
Co-authored-by: Scott Bell <scott@traclabs.com>
2023-03-06 14:11:25 -08:00
Jesse Mazzella
f388d9a548 fix(#5975): Transaction-ify the CreateAction (#6306)
* fix: Transaction-ify the CreateAction
* test: add regression test and update tree locators
* test: make object search test less flaky
* test: revert locator
2023-03-06 22:01:25 +00:00
Scott Bell
8040b275fc Update copyright date (#6395)
update copyright date
2023-03-06 21:58:18 +04:00
Scott Bell
0dd12bce85 Freeze plot automatically when adding tags (#6377)
* freeze if annotation is selected

* add test

* add stacked plot test

* actually test ticket issue

* wait for canvas to stabalize

* wait for canvas to stabalize

* address PR comments
2023-03-02 18:42:25 +01:00
dependabot[bot]
9c9e0442f1 chore(deps-dev): bump sanitize-html from 2.8.1 to 2.10.0 (#6360)
Bumps [sanitize-html](https://github.com/apostrophecms/sanitize-html) from 2.8.1 to 2.10.0.
- [Release notes](https://github.com/apostrophecms/sanitize-html/releases)
- [Changelog](https://github.com/apostrophecms/sanitize-html/blob/main/CHANGELOG.md)
- [Commits](https://github.com/apostrophecms/sanitize-html/compare/2.8.1...2.10.0)

---
updated-dependencies:
- dependency-name: sanitize-html
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-28 09:32:14 +01:00
Rukmini Bose (Ruki)
d49f057698 [Tags] Fix jumping of tags on hover (#6358) 2023-02-27 18:02:58 -08:00
dependabot[bot]
c74ad1279c chore(deps-dev): bump moment-timezone from 0.5.40 to 0.5.41 (#6375)
Bumps [moment-timezone](https://github.com/moment/moment-timezone) from 0.5.40 to 0.5.41.
- [Release notes](https://github.com/moment/moment-timezone/releases)
- [Changelog](https://github.com/moment/moment-timezone/blob/develop/changelog.md)
- [Commits](https://github.com/moment/moment-timezone/compare/0.5.40...0.5.41)

---
updated-dependencies:
- dependency-name: moment-timezone
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-27 23:44:34 +00:00
dependabot[bot]
470a451956 chore(deps-dev): bump eslint from 8.34.0 to 8.35.0 (#6374)
Bumps [eslint](https://github.com/eslint/eslint) from 8.34.0 to 8.35.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.34.0...v8.35.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jesse Mazzella <ozyx@users.noreply.github.com>
2023-02-27 15:20:37 -08:00
Jesse Mazzella
fa6cbb6f4d fix(#6158): StackedPlotItem reorders are reflected on the plot in real time (#6346)
* fix: ensure Vue can react to stacked plot item reorders

- Use Vue.$set instead of assignment so Vue can pick up StackedPlot composition reorders immediately

* test(e2e): add test for stacked plot item reorder

* docs: specify plot item order in comments

* fix: correct page.goto() url
2023-02-27 14:35:02 -08:00
Michael Rogers
52c00cfaef Ignore focus mouseover event when composed in layout (#6373)
Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2023-02-27 21:57:15 +00:00
Michael Rogers
96d723a424 Changed opacity from 0.5 to 0.7 (#6194) 2023-02-27 13:45:23 -08:00
Scott Bell
fb4b80862e Supress LAD Table View from ConditionSet (#6372)
* add test and fix

* remove premature test

* PR comments
2023-02-27 21:37:42 +00:00
Vitor Henckel
bb2c8cfa63 fix(#6330): Clicking Add Tag and then removing an existing tag causes the Add Tag button to disappear (#6371) 2023-02-27 13:14:43 -08:00
Simon Waldherr
ceffee9f22 docs: Remove LGTM badge from README (#6072)
Update README.md

remove LGTM-Badge - Sadly, LGTM.com is no longer available
2023-02-22 23:09:31 +00:00
Jesse Mazzella
a08ccd80dc chore: bump version to 2.2.0-SNAPSHOT (#6341)
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2023-02-15 21:17:21 +00:00
Scott Bell
3509eacdec Debounce search results (#6259)
* throttle search results to one a second

* changed to use custom debounce due to async

* attempt to fix flakey test

* attempt to fix flakey test

* revert test changes

* reduce debounce time and add e2e test

* allow canceling of timeout

* removed debug

* make search result e2e tests more stable

* make drop down selector a constant

* address pr comments
2023-02-15 21:50:03 +01:00
Jamie V
d4496cba41 [Notebook] Links, Restricted Notebook Links, Search links (#6344)
* big simplification and enhancement to highlight component and added formatting for locked notebooks as well

* fix typo, fix logic
2023-02-15 11:05:28 -08:00
Shefali Joshi
64f300d466 Emit indices of out of order telemetry collection items (#6342)
* Emit the indices of items added by the telemetry collections
* Handle added item indices for imagery
2023-02-14 15:25:23 -08:00
Scott Bell
8de24a109a Have clicking on annotation search result use the preview action if in edit mode (#6331)
* fix preview issue

* reenabled test suite after testing 30x in parallel

* add e2e test and disable unit tests for search

* change to const
2023-02-14 11:29:18 -08:00
Shefali Joshi
6d62e0e73c Decouple removal of independent time context for a view from refreshing context for other views (#6334)
* Decouple removing the context for a view from refreshing the context of views to get their upstream contexts

* Add test for clicking on an item in the elements pool for a preview

* Add test to ensure independent time context for items with no parents works as expected
2023-02-13 21:19:26 +00:00
David Tsay
5da1c9c0d7 Compass rose fix (#6318)
* won't mount if cameraAngleOfView undefined

* fix non-gimbling camera azimuth

correct pan to azimuth

* reorganize shared props passing

* enable hud for non-gimbling cameras

* fix unit tests

* rotate function needs to work with numbers

* avoid -0

* fix: don't delete imagery size, update related telemetry on focusedImage change

* refactor: remove unused prop

* fix: ensure thumbnail key is unique

* fix: watch `focusedImage`, not `focusedImageIndex`

- Corrects a false assumption that if the `focusedImageIndex` changes, the `focusedImage` has changed. This was causing us to mistakenly reset a lot of display props that control whether or not the Compass shows.

- For example, if an image falls out of bounds, the `focusedImageIndex` will change as the old image is removed from the array.

---------

Co-authored-by: Jesse Mazzella <jesse.d.mazzella@nasa.gov>
Co-authored-by: Jesse Mazzella <ozyx@users.noreply.github.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2023-02-13 19:28:00 +00:00
dependabot[bot]
4fa9a9697b chore(deps-dev): bump eslint from 8.32.0 to 8.34.0 (#6325)
Bumps [eslint](https://github.com/eslint/eslint) from 8.32.0 to 8.34.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.32.0...v8.34.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-13 18:52:09 +00:00
dependabot[bot]
bf48a6e306 chore(deps-dev): bump eslint-plugin-compat from 4.0.2 to 4.1.1 (#6311)
Bumps [eslint-plugin-compat](https://github.com/amilajack/eslint-plugin-compat) from 4.0.2 to 4.1.1.
- [Release notes](https://github.com/amilajack/eslint-plugin-compat/releases)
- [Changelog](https://github.com/amilajack/eslint-plugin-compat/blob/main/CHANGELOG.md)
- [Commits](https://github.com/amilajack/eslint-plugin-compat/compare/v4.0.2...v4.1.1)

---
updated-dependencies:
- dependency-name: eslint-plugin-compat
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-13 10:34:36 -08:00
dependabot[bot]
00ad452930 chore(deps-dev): bump typescript from 4.9.4 to 4.9.5 (#6233)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.9.4 to 4.9.5.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.9.4...v4.9.5)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-13 10:16:33 -08:00
Jesse Mazzella
8df1f6406b docs: fix docker command and formatting (#6329)
- Fixes an inconsistency in the docs with regards to the docker command for spinning up a Playwright container

- Closes an unclosed markdown code block
2023-02-13 18:09:55 +01:00
Shefali Joshi
a50960d66c Remove console warn (#6320) 2023-02-10 13:47:46 -08:00
Jamie V
e3a69c8856 [Notebook] Fix link formatting on load, remove duplicate snapshot indicator (#6317)
* adding urlWhitelist to data so when/if it is updated it triggers link formatting

* removing dupe notebook and restricted notebook checks and just moving it to base notebook functionality install checks

* nullthing to see here
2023-02-10 12:08:58 -08:00
Charles Hacskaylo
672cb7e621 Prevent tabbing into Notebook entries (#6315)
Closes #6312
- Set entry inputs to `tabindex="-1"` to prevent tabbing into entry inputs.

Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
2023-02-09 23:51:38 +00:00
Khalid Adil
7dcccee1ae [TimeAPI] Fix independent time context check (#6308)
* Fix independent time context to check first object in path (self) for upstream content instead of last object in path

* Revert changes that don't allow unregistering independent time context

---------

Co-authored-by: Shefali <simplyrender@gmail.com>
2023-02-09 15:34:17 -08:00
Jesse Mazzella
302dbe7359 fix(#6268): show correct yAxis options upon series drag to another yAxis (#6301)
* fix: show yAxis properties when series is moved

- Shows the correct yAxis properties immediately when a series is moved to another yAxis

* test: check for correct yAxis properties

- refactor test to use unique autogenerated object names

- update Elements pool pane handle selector

* test: fix goto and move to beforeEach

* test: ☠️ disable GrandSearch and ApplicationRouterSpec ☠️

---------

Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2023-02-09 11:58:03 -08:00
Scott Bell
b4df01965e Fix router test flakes (#6309)
* resolve conflicts
* disable flaky tests
2023-02-09 11:34:34 -08:00
Shefali
34864771b3 Merge branch 'time-conductor-4975' of https://github.com/nasa/openmct into time-conductor-4975 2022-11-03 13:40:57 -07:00
Charles Hacskaylo
2f1eb7f1bc Fixes #4975 - Compact Time Conductor styling
- Styling for Time Conductor in layout frames.
2022-05-17 23:08:03 -07:00
Charles Hacskaylo
2506dfb25d Fixes #4975 - Compact Time Conductor styling
- Convert Time Conductor symbol to use SVG instead of font glyph
2022-05-17 22:33:29 -07:00
Charles Hacskaylo
6ec07490ff Fixes #4975 - Compact Time Conductor styling
- Layout, display behavior for Time Conductor in layout frames.
- Code cleanups.
2022-05-17 22:07:31 -07:00
Charles Hacskaylo
04c76a0d0d Fixes #4975 - Compact Time Conductor styling
- Fix SCSS error.
2022-05-17 13:24:13 -07:00
Charles Hacskaylo
e5d701dea2 Fixes #4975 - Compact Time Conductor styling
- Layout, display behavior.
- Hide functional buttons to be moved.
- Remove unneeded markup.
- Input widths for fixed popup date and time.
- Add new `c-not-button` class.
- Add new `u-flex-spreader` class.
- Code cleanups.
2022-05-17 13:21:03 -07:00
Charles Hacskaylo
f22f826546 Fixes #4975 - Compact Time Conductor styling
- Stubbed buttons into popups.
- More `$colorTime*` theme constants defined and applied.
- Still quite WIP!
2022-05-16 23:03:30 -07:00
Charles Hacskaylo
fc83d88670 Fixes #4975 - Compact Time Conductor styling
- Fixed inputs popup layout with style and layout.
- More `$colorTime*` theme constants defined and applied.
- Better CSS organization.
2022-05-16 22:31:04 -07:00
Charles Hacskaylo
47e4c3af67 Fixes #4975 - Compact Time Conductor styling
- Styling for new mini toggle slider switch.
2022-05-16 18:43:29 -07:00
Charles Hacskaylo
d0cc125867 Fixes #4975 - Compact Time Conductor styling
- Markup and script moved into new timePopup* components.
- Significant work on styling.
- Theme constants augmented, better naming.
2022-05-16 18:42:58 -07:00
Joshi
65be53ba18 Merge branch 'master' of https://github.com/nasa/openmct into time-conductor-4975 2022-05-16 10:42:09 -07:00
Charles Hacskaylo
1e302e9f5e Fixes #4975 - Compact Time Conductor styling
- Added CSS class `is-expanded` to main view TC component.
2022-05-13 17:28:37 -07:00
Charles Hacskaylo
9e174c40ed Fixes #4975 - Compact Time Conductor styling
- Significant CSS and markup work.
- Refinements to `c-icon-button` classes, including new `--compact` definition.
- Styling for independent and main conductor components.
- Code cleanups.
- STILL WIP!
2022-05-13 17:23:17 -07:00
Charles Hacskaylo
453311272e Fixed #4975 - Compact Time Conductor styling
- Moved IndependentTimeConductor.vue into BrowseBar.vue.
- Styling for read-only conductor views.
- VERY WIP!
2022-05-13 12:07:19 -07:00
775 changed files with 12062 additions and 5172 deletions

View File

@@ -2,11 +2,15 @@ version: 2.1
executors:
pw-focal-development:
docker:
- image: mcr.microsoft.com/playwright:v1.29.0-focal
- image: mcr.microsoft.com/playwright:v1.32.3-focal
environment:
NODE_ENV: development # Needed to ensure 'dist' folder created and devDependencies installed
PERCY_POSTINSTALL_BROWSER: 'true' # Needed to store the percy browser in cache deps
PERCY_LOGLEVEL: 'debug' # Enable DEBUG level logging for Percy (Issue: https://github.com/nasa/openmct/issues/5742)
ubuntu:
machine:
image: ubuntu-2204:current
docker_layer_caching: true
parameters:
BUST_CACHE:
description: "Set this with the CircleCI UI Trigger Workflow button (boolean = true) to bust the cache!"
@@ -23,9 +27,8 @@ commands:
- restore_cache_cmd:
node-version: << parameters.node-version >>
- node/install:
install-npm: true
node-version: << parameters.node-version >>
- run: npm install --prefer-offline --no-audit --progress=false
- run: npm install --no-audit --progress=false
restore_cache_cmd:
description: "Custom command for restoring cache with the ability to bust cache. When BUST_CACHE is set to true, jobs will not restore cache"
parameters:
@@ -37,7 +40,7 @@ commands:
equal: [false, << pipeline.parameters.BUST_CACHE >> ]
steps:
- restore_cache:
key: deps-{{ .Branch }}--<< parameters.node-version >>--{{ checksum "package.json" }}-{{ checksum ".circleci/config.yml" }}
key: deps--{{ arch }}--{{ .Branch }}--<< parameters.node-version >>--{{ checksum "package.json" }}-{{ checksum ".circleci/config.yml" }}
save_cache_cmd:
description: "Custom command for saving cache."
parameters:
@@ -45,7 +48,7 @@ commands:
type: string
steps:
- save_cache:
key: deps-{{ .Branch }}--<< parameters.node-version >>--{{ checksum "package.json" }}-{{ checksum ".circleci/config.yml" }}
key: deps--{{ arch }}--{{ .Branch }}--<< parameters.node-version >>--{{ checksum "package.json" }}-{{ checksum ".circleci/config.yml" }}
paths:
- ~/.npm
- node_modules
@@ -53,8 +56,8 @@ commands:
description: "Track important packages and files"
steps:
- run: |
mkdir /tmp/artifacts
printenv NODE_ENV >> /tmp/artifacts/NODE_ENV.txt
[[ $EUID -ne 0 ]] && (sudo mkdir -p /tmp/artifacts && sudo chmod 777 /tmp/artifacts) || (mkdir -p /tmp/artifacts && chmod 777 /tmp/artifacts)
printenv NODE_ENV >> /tmp/artifacts/NODE_ENV.txt || true
npm -v >> /tmp/artifacts/npm-version.txt
node -v >> /tmp/artifacts/node-version.txt
ls -latR >> /tmp/artifacts/dir.txt
@@ -69,7 +72,7 @@ commands:
- run: npm run cov:e2e:report || true
- run: npm run cov:e2e:<<parameters.suite>>:publish
orbs:
node: circleci/node@4.9.0
node: circleci/node@5.1.0
browser-tools: circleci/browser-tools@1.3.0
jobs:
npm-audit:
@@ -110,7 +113,11 @@ jobs:
path: dist/reports/tests/
- store_artifacts:
path: coverage
- generate_and_store_version_and_filesystem_artifacts
- when:
condition:
equal: [ 42, 42 ] # Always generate version artifacts regardless of test failure https://discuss.circleci.com/t/make-custom-command-run-always-with-when-always/38957/2
steps:
- generate_and_store_version_and_filesystem_artifacts
e2e-test:
parameters:
node-version:
@@ -128,8 +135,12 @@ jobs:
steps:
- run: npx playwright install chrome-beta
- run: SHARD="$((${CIRCLE_NODE_INDEX}+1))"; npm run test:e2e:<<parameters.suite>> -- --shard=${SHARD}/${CIRCLE_NODE_TOTAL}
- generate_e2e_code_cov_report:
suite: <<parameters.suite>>
- when:
condition:
equal: [ 42, 42 ] # Always run codecov reports regardless of test failure https://discuss.circleci.com/t/make-custom-command-run-always-with-when-always/38957/2
steps:
- generate_e2e_code_cov_report:
suite: <<parameters.suite>>
- store_test_results:
path: test-results/results.xml
- store_artifacts:
@@ -138,7 +149,46 @@ jobs:
path: coverage
- store_artifacts:
path: html-test-results
- generate_and_store_version_and_filesystem_artifacts
- when:
condition:
equal: [ 42, 42 ] # Always generate version artifacts regardless of test failure https://discuss.circleci.com/t/make-custom-command-run-always-with-when-always/38957/2
steps:
- generate_and_store_version_and_filesystem_artifacts
e2e-couchdb:
parameters:
node-version:
type: string
executor: ubuntu
steps:
- build_and_install:
node-version: <<parameters.node-version>>
- run: npx playwright@1.32.3 install #Necessary for bare ubuntu machine
- run: |
export $(cat src/plugins/persistence/couch/.env.ci | xargs)
docker-compose -f src/plugins/persistence/couch/couchdb-compose.yaml up --detach
sleep 3
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: npm run test:e2e:couchdb
- when:
condition:
equal: [ 42, 42 ] # Always run codecov reports regardless of test failure https://discuss.circleci.com/t/make-custom-command-run-always-with-when-always/38957/2
steps:
- generate_e2e_code_cov_report:
suite: full #add to full suite
- store_test_results:
path: test-results/results.xml
- store_artifacts:
path: test-results
- store_artifacts:
path: coverage
- store_artifacts:
path: html-test-results
- when:
condition:
equal: [ 42, 42 ] # Always generate version artifacts regardless of test failure https://discuss.circleci.com/t/make-custom-command-run-always-with-when-always/38957/2
steps:
- generate_and_store_version_and_filesystem_artifacts
perf-test:
parameters:
node-version:
@@ -154,7 +204,11 @@ jobs:
path: test-results
- store_artifacts:
path: html-test-results
- generate_and_store_version_and_filesystem_artifacts
- when:
condition:
equal: [ 42, 42 ] # Always run codecov reports regardless of test failure https://discuss.circleci.com/t/make-custom-command-run-always-with-when-always/38957/2
steps:
- generate_and_store_version_and_filesystem_artifacts
visual-test:
parameters:
node-version:
@@ -170,46 +224,49 @@ jobs:
path: test-results
- store_artifacts:
path: html-test-results
- generate_and_store_version_and_filesystem_artifacts
- when:
condition:
equal: [ 42, 42 ] # Always generate version artifacts regardless of test failure https://discuss.circleci.com/t/make-custom-command-run-always-with-when-always/38957/2
steps:
- generate_and_store_version_and_filesystem_artifacts
workflows:
overall-circleci-commit-status: #These jobs run on every commit
jobs:
- lint:
name: node14-lint
node-version: lts/fermium
name: node16-lint
node-version: lts/gallium
- unit-test:
name: node18-chrome
node-version: "18"
node-version: lts/hydrogen
- e2e-test:
name: e2e-stable
node-version: lts/gallium
node-version: lts/hydrogen
suite: stable
- perf-test:
node-version: lts/gallium
node-version: lts/hydrogen
- visual-test:
node-version: lts/gallium
node-version: lts/hydrogen
the-nightly: #These jobs do not run on PRs, but against master at night
jobs:
- unit-test:
name: node14-chrome-nightly
node-version: lts/fermium
- unit-test:
name: node16-chrome-nightly
node-version: lts/gallium
- unit-test:
name: node18-chrome
node-version: "18"
node-version: lts/hydrogen
- npm-audit:
node-version: lts/gallium
node-version: lts/hydrogen
- e2e-test:
name: e2e-full-nightly
node-version: lts/gallium
node-version: lts/hydrogen
suite: full
- perf-test:
node-version: lts/gallium
node-version: lts/hydrogen
- visual-test:
node-version: lts/gallium
node-version: lts/hydrogen
- e2e-couchdb:
node-version: lts/hydrogen
triggers:
- schedule:
cron: "0 0 * * *"

View File

@@ -4,13 +4,13 @@ updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
interval: "weekly"
open-pull-requests-limit: 10
labels:
- "pr:daveit"
- "pr:e2e"
- "type:maintenance"
- "dependencies"
- "pr:daveit"
- "pr:platform"
ignore:
#We have to source the playwright container which is not detected by Dependabot
@@ -25,11 +25,15 @@ updates:
update-types: ["version-update:semver-patch"]
- dependency-name: "sinon"
update-types: ["version-update:semver-patch"]
- dependency-name: "moment-timezone"
update-types: ["version-update:semver-patch"]
- dependency-name: "@types/lodash"
update-types: ["version-update:semver-patch"]
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
labels:
- "pr:daveit"
- "type:maintenance"
- "dependencies"
- "pr:daveit"

View File

@@ -5,34 +5,39 @@ on:
types:
- labeled
- opened
env:
OPENMCT_DATABASE_NAME: openmct
COUCH_ADMIN_USER: admin
COUCH_ADMIN_PASSWORD: password
COUCH_BASE_LOCAL: http://localhost:5984
COUCH_NODE_NAME: nonode@nohost
jobs:
e2e-couchdb:
if: ${{ github.event.label.name == 'pr:e2e:couchdb' }}
if: ${{ github.event.label.name == 'pr:e2e:couchdb' }} || ${{ github.event.action == 'opened' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run : docker-compose -f src/plugins/persistence/couch/couchdb-compose.yaml up --detach
- run : sleep 3 # wait until CouchDB has started (TODO: there must be a better way)
- run : bash src/plugins/persistence/couch/setup-couchdb.sh
- uses: actions/setup-node@v3
with:
node-version: '16'
- run: npx playwright@1.29.0 install
node-version: 'lts/gallium'
- run: npx playwright@1.32.3 install
- run: npm install
- run: sh src/plugins/persistence/couch/replace-localstorage-with-couchdb-indexhtml.sh
- run: npm run test:e2e:couchdb
- run: ls -latr
- name: Start CouchDB Docker Container and Init with Setup Scripts
run : |
export $(cat src/plugins/persistence/couch/.env.ci | xargs)
docker-compose -f src/plugins/persistence/couch/couchdb-compose.yaml up --detach
sleep 3
bash src/plugins/persistence/couch/setup-couchdb.sh
bash src/plugins/persistence/couch/replace-localstorage-with-couchdb-indexhtml.sh
- name: Run CouchDB Tests and publish to deploysentinel
env:
DEPLOYSENTINEL_API_KEY: ${{ secrets.DEPLOYSENTINEL_API_KEY }}
run: npm run test:e2e:couchdb
- name: Publish Results to Codecov.io
env:
SUPER_SECRET: ${{ secrets.CODECOV_TOKEN }}
run: npm run cov:e2e:full:publish
- name: Archive test results
if: success() || failure()
uses: actions/upload-artifact@v3
with:
path: test-results
- name: Archive html test results
if: success() || failure()
uses: actions/upload-artifact@v3
with:
path: html-test-results

View File

@@ -5,7 +5,6 @@ on:
types:
- labeled
- opened
jobs:
e2e-full:
if: ${{ github.event.label.name == 'pr:e2e' }}
@@ -30,11 +29,18 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: '16'
- run: npx playwright@1.29.0 install
- run: npx playwright@1.32.3 install
- run: npx playwright install chrome-beta
- run: npm install
- run: npm run test:e2e:full
- run: npm run test:e2e:full -- --max-failures=40
- run: npm run cov:e2e:report || true
- shell: bash
env:
SUPER_SECRET: ${{ secrets.CODECOV_TOKEN }}
run: |
npm run cov:e2e:full:publish
- name: Archive test results
if: success() || failure()
uses: actions/upload-artifact@v3
with:
path: test-results

View File

@@ -1,21 +0,0 @@
name: "e2e"
on:
workflow_dispatch:
inputs:
version:
description: 'Which branch do you want to test?' # Limited to branch for now
required: false
default: 'master'
jobs:
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.inputs.version }}
- uses: actions/setup-node@v3
with:
node-version: '16'
- run: npm install
- name: Run the e2e tests
run: npm run test:e2e:ci

View File

@@ -16,7 +16,6 @@ jobs:
- macos-latest
- windows-latest
node_version:
- 14
- 16
- 18
architecture:

View File

@@ -53,7 +53,11 @@ module.exports = merge(common, {
},
client: {
progress: true,
overlay: true
overlay: {
// Disable overlay for runtime errors.
// See: https://github.com/webpack/webpack-dev-server/issues/4771
runtimeErrors: false
}
}
}
});

View File

@@ -191,7 +191,7 @@ The following guidelines are provided for anyone contributing source code to the
if (responseCode === 401)
```
1. Use the ternary operator only for simple cases such as variable assignment. Nested ternaries should be avoided in all cases.
1. Test specs should reside alongside the source code they test, not in a separate directory.
1. Unit Test specs should reside alongside the source code they test, not in a separate directory.
1. Organize code by feature, not by type.
eg.
```
@@ -222,44 +222,6 @@ The following guidelines are provided for anyone contributing source code to the
Deviations from Open MCT code style guidelines require two-party agreement,
typically from the author of the change and its reviewer.
### Test Standards
Automated testing shall occur whenever changes are merged into the main
development branch and must be confirmed alongside any pull request.
Automated tests are tests which exercise plugins, API, and utility classes.
Tests are subject to code review along with the actual implementation, to
ensure that tests are applicable and useful.
Examples of useful tests:
* Tests which replicate bugs (or their root causes) to verify their
resolution.
* Tests which reflect details from software specifications.
* Tests which exercise edge or corner cases among inputs.
* Tests which verify expected interactions with other components in the
system.
#### Guidelines
* 100% statement coverage is achievable and desirable.
* Do blackbox testing. Test external behaviors, not internal details. Write tests that describe what your plugin is supposed to do. How it does this doesn't matter, so don't test it.
* Unit test specs for plugins should be defined at the plugin level. Start with one test spec per plugin named pluginSpec.js, and as this test spec grows too big, break it up into multiple test specs that logically group related tests.
* Unit tests for API or for utility functions and classes may be defined at a per-source file level.
* Wherever possible only use and mock public API, builtin functions, and UI in your test specs. Do not directly invoke any private functions. ie. only call or mock functions and objects exposed by openmct.* (eg. openmct.telemetry, openmct.objectView, etc.), and builtin browser functions (fetch, requestAnimationFrame, setTimeout, etc.).
* Where builtin functions have been mocked, be sure to clear them between tests.
* Test at an appropriate level of isolation. Eg.
* If youre testing a view, you do not need to test the whole application UI, you can just fetch the view provider using the public API and render the view into an element that you have created.
* You do not need to test that the view switcher works, there should be separate tests for that.
* You do not need to test that telemetry providers work, you can mock openmct.telemetry.request() to feed test data to the view.
* Use your best judgement when deciding on appropriate scope.
* Automated tests for plugins should start by actually installing the plugin being tested, and then test that installing the plugin adds the desired features and behavior to Open MCT, observing the above rules.
* All variables used in a test spec, including any instances of the Open MCT API should be declared inside of an appropriate block scope (not at the root level of the source file), and should be initialized in the relevant beforeEach block. `beforeEach` is preferable to `beforeAll` to avoid leaking of state between tests.
* A `afterEach` or `afterAll` should be used to do any clean up necessary to prevent leakage of state between test specs. This can happen when functions on `window` are wrapped, or when the URL is changed. [A convenience function](https://github.com/nasa/openmct/blob/master/src/utils/testing.js#L59) is provided for resetting the URL and clearing builtin spies between tests.
* If writing unit tests for legacy Angular code be sure to follow [best practices in order to avoid memory leaks](https://www.thecodecampus.de/blog/avoid-memory-leaks-angularjs-unit-tests/).
#### Examples
* [Example of an automated test spec for an object view plugin](https://github.com/nasa/openmct/blob/master/src/plugins/telemetryTable/pluginSpec.js)
* [Example of an automated test spec for API](https://github.com/nasa/openmct/blob/master/src/api/time/TimeAPISpec.js)
### Commit Message Standards
Commit messages should:
@@ -301,7 +263,7 @@ Issue severity is categorized as follows (in ascending order):
* _Trivial_: Minimal impact on the usefulness and functionality of the software; a "nice-to-have." Visual impact without functional impact,
* _Medium_: Some impairment of use, but simple workarounds exist
* _Critical_: Significant loss of functionality or impairment of use. Display of telemetry data is not affected though.
* _Critical_: Significant loss of functionality or impairment of use. Display of telemetry data is not affected though. Complex workarounds exist.
* _Blocker_: Major functionality is impaired or lost, threatening mission success. Display of telemetry data is impaired or blocked by the bug, which could lead to loss of situational awareness.
## Check Lists
@@ -310,22 +272,4 @@ The following check lists should be completed and attached to pull requests
when they are filed (author checklist) and when they are merged (reviewer
checklist).
### Author Checklist
[Within PR Template](.github/PULL_REQUEST_TEMPLATE.md)
### Reviewer Checklist
* [ ] Changes appear to address issue?
* [ ] Changes appear not to be breaking changes?
* [ ] Appropriate unit tests included?
* [ ] Code style and in-line documentation are appropriate?
* [ ] Commit messages meet standards?
* [ ] Has associated issue been labelled `unverified`? (only applicable if this PR closes the issue)
* [ ] Has associated issue been labelled `bug`? (only applicable if this PR is for a bug fix)
* [ ] List of Acceptance Tests Performed.
Write out a small list of tests performed with just enough detail for another developer on the team
to execute.
i.e. ```When Clicking on Add button, new `object` appears in dropdown.```

View File

@@ -1,6 +1,6 @@
# Open MCT License
Open MCT, Copyright (c) 2014-2022, United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All rights reserved.
Open MCT, Copyright (c) 2014-2023, 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.

View File

@@ -1,4 +1,4 @@
# Open MCT [![license](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0) [![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/nasa/openmct.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/nasa/openmct/context:javascript) [![codecov](https://codecov.io/gh/nasa/openmct/branch/master/graph/badge.svg?token=7DQIipp3ej)](https://codecov.io/gh/nasa/openmct) [![This project is using Percy.io for visual regression testing.](https://percy.io/static/images/percy-badge.svg)](https://percy.io/b2e34b17/openmct) [![npm version](https://img.shields.io/npm/v/openmct.svg)](https://www.npmjs.com/package/openmct)
# Open MCT [![license](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0) [![codecov](https://codecov.io/gh/nasa/openmct/branch/master/graph/badge.svg?token=7DQIipp3ej)](https://codecov.io/gh/nasa/openmct) [![This project is using Percy.io for visual regression testing.](https://percy.io/static/images/percy-badge.svg)](https://percy.io/b2e34b17/openmct) [![npm version](https://img.shields.io/npm/v/openmct.svg)](https://www.npmjs.com/package/openmct)
Open MCT (Open Mission Control Technologies) is a next-generation mission control framework for visualization of data on desktop and mobile devices. It is developed at NASA's Ames Research Center, and is being used by NASA for data analysis of spacecraft missions, as well as planning and operation of experimental rover systems. As a generalizable and open source framework, Open MCT could be used as the basis for building applications for planning, operation, and analysis of any systems producing telemetry data.
@@ -98,7 +98,7 @@ To run the performance tests:
The test suite is configured to all tests localed in `e2e/tests/` ending in `*.e2e.spec.js`. For more about the e2e test suite, please see the [README](./e2e/README.md)
### Security Tests
Each commit is analyzed for known security vulnerabilities using [CodeQL](https://codeql.github.com/docs/codeql-language-guides/codeql-library-for-javascript/) and our overall security report is available on [LGTM](https://lgtm.com/projects/g/nasa/openmct/). The list of CWE coverage items is avaiable in the [CodeQL docs](https://codeql.github.com/codeql-query-help/javascript-cwe/). The CodeQL workflow is specified in the [CodeQL analysis file](./.github/workflows/codeql-analysis.yml) and the custom [CodeQL config](./.github/codeql/codeql-config.yml).
Each commit is analyzed for known security vulnerabilities using [CodeQL](https://codeql.github.com/docs/codeql-language-guides/codeql-library-for-javascript/). The list of CWE coverage items is avaiable in the [CodeQL docs](https://codeql.github.com/codeql-query-help/javascript-cwe/). The CodeQL workflow is specified in the [CodeQL analysis file](./.github/workflows/codeql-analysis.yml) and the custom [CodeQL config](./.github/codeql/codeql-config.yml).
### Test Reporting and Code Coverage

50
TESTING.md Normal file
View File

@@ -0,0 +1,50 @@
# Testing
Open MCT Testing is iterating and improving at a rapid pace. This document serves to capture and index existing testing documentation and house documentation which no other obvious location as our testing evolves.
## General Testing Process
Documentation located [here](./docs/src/process/testing/plan.md)
## Unit Testing
Unit testing is essential part of our test strategy and complements our e2e testing strategy.
#### Unit Test Guidelines
* Unit Test specs should reside alongside the source code they test, not in a separate directory.
* Unit test specs for plugins should be defined at the plugin level. Start with one test spec per plugin named pluginSpec.js, and as this test spec grows too big, break it up into multiple test specs that logically group related tests.
* Unit tests for API or for utility functions and classes may be defined at a per-source file level.
* Wherever possible only use and mock public API, builtin functions, and UI in your test specs. Do not directly invoke any private functions. ie. only call or mock functions and objects exposed by openmct.* (eg. openmct.telemetry, openmct.objectView, etc.), and builtin browser functions (fetch, requestAnimationFrame, setTimeout, etc.).
* Where builtin functions have been mocked, be sure to clear them between tests.
* Test at an appropriate level of isolation. Eg.
* If youre testing a view, you do not need to test the whole application UI, you can just fetch the view provider using the public API and render the view into an element that you have created.
* You do not need to test that the view switcher works, there should be separate tests for that.
* You do not need to test that telemetry providers work, you can mock openmct.telemetry.request() to feed test data to the view.
* Use your best judgement when deciding on appropriate scope.
* Automated tests for plugins should start by actually installing the plugin being tested, and then test that installing the plugin adds the desired features and behavior to Open MCT, observing the above rules.
* All variables used in a test spec, including any instances of the Open MCT API should be declared inside of an appropriate block scope (not at the root level of the source file), and should be initialized in the relevant beforeEach block. `beforeEach` is preferable to `beforeAll` to avoid leaking of state between tests.
* A `afterEach` or `afterAll` should be used to do any clean up necessary to prevent leakage of state between test specs. This can happen when functions on `window` are wrapped, or when the URL is changed. [A convenience function](https://github.com/nasa/openmct/blob/master/src/utils/testing.js#L59) is provided for resetting the URL and clearing builtin spies between tests.
#### Unit Test Examples
* [Example of an automated test spec for an object view plugin](https://github.com/nasa/openmct/blob/master/src/plugins/telemetryTable/pluginSpec.js)
* [Example of an automated test spec for API](https://github.com/nasa/openmct/blob/master/src/api/time/TimeAPISpec.js)
#### Unit Testing Execution
The unit tests can be executed in one of two ways:
`npm run test` which runs the entire suite against headless chrome
`npm run test:debug` for debugging the tests in realtime in an active chrome session.
## e2e, performance, and visual testing
Documentation located [here](./e2e/README.md)
## Code Coverage
* 100% statement coverage is achievable and desirable.
Codecov.io will combine each of the above commands with [Codecov.io Flags](https://docs.codecov.com/docs/flags). Effectively, this allows us to combine multiple reports which are run at various stages of our CI Pipeline or run as part of a parallel process.
This e2e coverage is combined with our unit test report to give a comprehensive (if flawed) view of line coverage.
### Limitations in our code coverage reporting
Our code coverage implementation has two known limitations:
- [Variability and accuracy](https://github.com/nasa/openmct/issues/5811)
- [Vue instrumentation](https://github.com/nasa/openmct/issues/4973)

View File

@@ -1,7 +1,7 @@
#!/bin/bash
#*****************************************************************************
#* Open MCT, Copyright (c) 2014-2022, United States Government
#* Open MCT, Copyright (c) 2014-2023, United States Government
#* as represented by the Administrator of the National Aeronautics and Space
#* Administration. All rights reserved.
#*

View File

@@ -15,14 +15,14 @@ coverage:
flags:
unit:
carryforward: true
e2e-ci:
carryforward: true
carryforward: false
e2e-stable:
carryforward: false
e2e-full:
carryforward: true
comment:
layout: "reach,diff,flags,files,footer"
layout: "diff,flags,files,footer"
behavior: default
require_changes: false
show_carryforward_flags: true
show_carryforward_flags: true

View File

@@ -1,5 +1,5 @@
<!--
Open MCT, Copyright (c) 2014-2022, United States Government
Open MCT, Copyright (c) 2014-2023, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*

View File

@@ -92,7 +92,9 @@ Read more about [Playwright Snapshots](https://playwright.dev/docs/test-snapshot
- Snapshots need to be executed within the official Playwright container to ensure we're using the exact rendering platform in CI and locally. To do a valid comparison locally:
```sh
docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:[GET THIS VERSION FROM OUR CIRCLECI CONFIG FILE]-focal /bin/bash
// Replace {X.X.X} with the current Playwright version
// from our package.json or circleCI configuration file
docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v{X.X.X}-focal /bin/bash
npm install
npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep @snapshot
```
@@ -104,17 +106,20 @@ When the `@snapshot` tests fail, they will need to be evaluated to determine if
To compare a snapshot, run a test and open the html report with the 'Expected' vs 'Actual' screenshot. If the actual screenshot is preferred, then the source-controlled 'Expected' snapshots will need to be updated with the following scripts.
MacOS
```
npm run test:e2e:updatesnapshots
```
Linux/CI
```sh
// Replace {X.X.X} with the current Playwright version
// from our package.json or circleCI configuration file
docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v{X.X.X}-focal /bin/bash
npm install
npm run test:e2e:updatesnapshots
```
## Performance Testing
@@ -195,6 +200,7 @@ CircleCI
Github Actions / Workflow
- Full suite against all browsers/projects. Triggered with Github Label Event 'pr:e2e'
- CouchDB Tests. Triggered on PR Create and again with Github Label Event 'pr:e2e:couchdb'
- Visual Tests. Triggered with Github Label Event 'pr:visual'
#### 3. Scheduled / Batch Testing
@@ -346,6 +352,10 @@ When looking at the reports run in CI, you'll leverage this same HTML Report whi
### e2e Code Coverage
Our e2e code coverage is captured and combined with our unit test coverage. For more information, please see our [code coverage documentation](../TESTING.md)
#### Generating e2e code coverage
Code coverage is collected during test execution using our custom [baseFixture](./baseFixtures.js). The raw coverage files are stored in a `.nyc_report` directory to be converted into a lcov file with the following [nyc](https://github.com/istanbuljs/nyc) command:
```npm run cov:e2e:report```
@@ -356,10 +366,6 @@ At this point, the nyc linecov report can be published to [codecov.io](https://a
or
```npm run cov:e2e:full:publish``` for the full suite running against all available platforms.
Codecov.io will combine each of the above commands with [Codecov.io Flags](https://docs.codecov.com/docs/flags). Effectively, this allows us to combine multiple reports which are run at various stages of our CI Pipeline or run as part of a parallel process.
This e2e coverage is combined with our unit test report to give a comprehensive (if flawed) view of line coverage.
## Other
### About e2e testing

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -74,7 +74,6 @@ async function createDomainObjectWithDefaults(page, { type, name, parent = 'mine
// Navigate to the parent object. This is necessary to create the object
// in the correct location, such as a folder, layout, or plot.
await page.goto(`${parentUrl}?hideTree=true`);
await page.waitForLoadState('networkidle');
//Click the Create button
await page.click('button:has-text("Create")');
@@ -140,6 +139,7 @@ async function createNotification(page, createNotificationOptions) {
}
/**
* Expand an item in the tree by a given object name.
* @param {import('@playwright/test').Page} page
* @param {string} name
*/
@@ -159,24 +159,26 @@ async function expandTreePaneItemByName(page, name) {
* @returns {Promise<CreatedObjectInfo>} An object containing information about the newly created domain object.
*/
async function createPlanFromJSON(page, { name, json, parent = 'mine' }) {
if (!name) {
name = `Plan:${genUuid()}`;
}
const parentUrl = await getHashUrlToDomainObject(page, parent);
// Navigate to the parent object. This is necessary to create the object
// in the correct location, such as a folder, layout, or plot.
await page.goto(`${parentUrl}?hideTree=true`);
//Click the Create button
// Click the Create button
await page.click('button:has-text("Create")');
// Click 'Plan' menu option
await page.click(`li:text("Plan")`);
// Modify the name input field of the domain object to accept 'name'
if (name) {
const nameInput = page.locator('form[name="mctForm"] .first input[type="text"]');
await nameInput.fill("");
await nameInput.fill(name);
}
const nameInput = page.locator('form[name="mctForm"] .first input[type="text"]');
await nameInput.fill("");
await nameInput.fill(name);
// Upload buffer from memory
await page.locator('input#fileElem').setInputFiles({
@@ -194,7 +196,7 @@ async function createPlanFromJSON(page, { name, json, parent = 'mine' }) {
]);
// Wait until the URL is updated
await page.waitForURL(`**/mine/*`);
await page.waitForURL(`**/${parent}/*`);
const uuid = await getFocusedObjectUuid(page);
const objectUrl = await getHashUrlToDomainObject(page, uuid);
@@ -270,6 +272,7 @@ async function getFocusedObjectUuid(page) {
* @returns {Promise<string>} the url of the object
*/
async function getHashUrlToDomainObject(page, uuid) {
await page.waitForLoadState('load'); //Add some determinism
const hashUrl = await page.evaluate(async (objectUuid) => {
const path = await window.openmct.objects.getOriginalPath(objectUuid);
let url = './#/browse/' + [...path].reverse()
@@ -383,6 +386,25 @@ async function setEndOffset(page, offset) {
await setTimeConductorOffset(page, offset, endOffsetButton);
}
/**
* Selects an inspector tab based on the provided tab name
*
* @param {import('@playwright/test').Page} page
* @param {String} name the name of the tab
*/
async function selectInspectorTab(page, name) {
const inspectorTabs = page.getByRole('tablist');
const inspectorTab = inspectorTabs.getByTitle(name);
const inspectorTabClass = await inspectorTab.getAttribute('class');
const isSelectedInspectorTab = inspectorTabClass.includes('is-current');
// do not click a tab that is already selected or it will timeout your test
// do to a { pointer-events: none; } on selected tabs
if (!isSelectedInspectorTab) {
await inspectorTab.click();
}
}
// eslint-disable-next-line no-undef
module.exports = {
createDomainObjectWithDefaults,
@@ -396,5 +418,6 @@ module.exports = {
setFixedTimeMode,
setRealTimeMode,
setStartOffset,
setEndOffset
setEndOffset,
selectInspectorTab
};

View File

@@ -1,6 +1,6 @@
/* eslint-disable no-undef */
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -170,5 +170,6 @@ exports.test = base.test.extend({
}
}
});
exports.expect = expect;
exports.waitForAnimations = waitForAnimations;

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -58,8 +58,14 @@ async function navigateToFaultManagementWithoutExample(page) {
async function navigateToFaultItemInTree(page) {
await page.goto('./', { waitUntil: 'networkidle' });
// Click text=Fault Management
await page.click('text=Fault Management'); // this verifies the plugin has been added
const faultManagementTreeItem = page.getByRole('tree', {
name: "Main Tree"
}).getByRole('treeitem', {
name: "Fault Management"
});
// Navigate to "Fault Management" from the tree
await faultManagementTreeItem.click();
}
/**
@@ -141,8 +147,7 @@ async function clearSearch(page) {
* @param {import('@playwright/test').Page} page
*/
async function selectFaultItem(page, rowNumber) {
// eslint-disable-next-line playwright/no-force-option
await page.check(`.c-fault-mgmt-item > input >> nth=${rowNumber - 1}`, { force: true }); // this will not work without force true, saw this may be a pw bug
await page.locator(`.c-fault-mgmt-item > input >> nth=${rowNumber - 1}`).check();
}
/**

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -28,13 +28,12 @@ const NOTEBOOK_DROP_AREA = '.c-notebook__drag-area';
* @param {import('@playwright/test').Page} page
*/
async function enterTextEntry(page, text) {
// Click .c-notebook__drag-area
// Click the 'Add Notebook Entry' area
await page.locator(NOTEBOOK_DROP_AREA).click();
// enter text
await page.locator('div.c-ne__text').click();
await page.locator('div.c-ne__text').fill(text);
await page.locator('div.c-ne__text').press('Enter');
await page.locator('[aria-label="Notebook Entry"].is-selected div.c-ne__text').fill(text);
await commitEntry(page);
}
/**
@@ -51,6 +50,16 @@ async function dragAndDropEmbed(page, notebookObject) {
await page.click('button[title="Show selected item in tree"]');
// Drag and drop the SWG into the notebook
await page.dragAndDrop(`text=${swg.name}`, NOTEBOOK_DROP_AREA);
await commitEntry(page);
}
/**
* @private
* @param {import('@playwright/test').Page} page
*/
async function commitEntry(page) {
//Click the Commit Entry button
await page.locator('.c-ne__save-button > button').click();
}
// eslint-disable-next-line no-undef

View File

@@ -0,0 +1,92 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import { expect } from '../pluginFixtures';
/**
* Asserts that the number of activities in the plan view matches the number of
* activities in the plan data within the specified time bounds. Performs an assertion
* for each activity in the plan data per group, using the earliest activity's
* 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 {object} plan The raw plan json to assert against
* @param {string} objectUrl The URL of the object to assert against (plan or gantt chart)
*/
export async function assertPlanActivities(page, plan, objectUrl) {
const groups = Object.keys(plan);
for (const group of groups) {
for (let i = 0; i < plan[group].length; i++) {
// Set the startBound to the start time of the first activity in the group
const startBound = plan[group][0].start;
// Set the endBound to the end time of the current activity
let endBound = plan[group][i].end;
if (endBound === startBound) {
// Prevent oddities with setting start and end bound equal
// via URL params
endBound += 1;
}
// Switch to fixed time mode with all plan events within the bounds
await page.goto(`${objectUrl}?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
// activities in the plan data within the specified time bounds
const eventCount = await page.locator('.activity-bounds').count();
expect(eventCount).toEqual(Object.values(plan)
.flat()
.filter(event =>
activitiesWithinTimeBounds(event.start, event.end, startBound, endBound)).length);
}
}
}
/**
* Returns true if the activities time bounds overlap, false otherwise.
* @param {number} start1 the start time of the first activity
* @param {number} end1 the end time of the first activity
* @param {number} start2 the start time of the second activity
* @param {number} end2 the end time of the second activity
* @returns {boolean} true if the activities overlap, false otherwise
*/
function activitiesWithinTimeBounds(start1, end1, start2, end2) {
return (start1 >= start2 && start1 <= end2)
|| (end1 >= start2 && end1 <= end2)
|| (start2 >= start1 && start2 <= end1)
|| (end2 >= start1 && end2 <= end1);
}
/**
* Navigate to the plan view, switch to fixed time mode,
* and set the bounds to span all activities.
* @param {import('@playwright/test').Page} page
* @param {object} planJson
* @param {string} planObjectUrl
*/
export async function setBoundsToSpanAllActivities(page, planJson, planObjectUrl) {
const activities = Object.values(planJson).flat();
// Get the earliest start value
const start = Math.min(...activities.map(activity => activity.start));
// Get the latest end value
const end = Math.max(...activities.map(activity => activity.end));
// Set the start and end bounds to the earliest start and latest end
await page.goto(`${planObjectUrl}?tc.mode=fixed&tc.startBound=${start}&tc.endBound=${end}&tc.timeSystem=utc&view=plan.view`);
}

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*

View File

@@ -9,7 +9,7 @@ const NUM_WORKERS = 2;
/** @type {import('@playwright/test').PlaywrightTestConfig} */
const config = {
retries: 2, //Retries 2 times for a total of 3 runs. When running sharded and with maxFailures = 5, this should ensure that flake is managed without failing the full suite
retries: 2, //Retries 2 times for a total of 3 runs. When running sharded and with max-failures=5, this should ensure that flake is managed without failing the full suite
testDir: 'tests',
testIgnore: '**/*.perf.spec.js', //Ignore performance tests and define in playwright-perfromance.config.js
timeout: 60 * 1000,
@@ -74,7 +74,8 @@ const config = {
outputFolder: '../html-test-results' //Must be in different location due to https://github.com/microsoft/playwright/issues/12840
}],
['junit', { outputFile: '../test-results/results.xml' }],
['github']
['github'],
['@deploysentinel/playwright']
]
};

View File

@@ -1,6 +1,6 @@
/* eslint-disable no-undef */
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -150,3 +150,17 @@ exports.test = test.extend({
}
});
exports.expect = expect;
/**
* Takes a readable stream and returns a string.
* @param {ReadableStream} readable - the readable stream
* @return {Promise<String>} the stringified stream
*/
exports.streamToString = async function (readable) {
let result = '';
for await (const chunk of readable) {
result += chunk;
}
return result;
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,44 @@
{
"Group 1": [
{
"name": "Past event 1",
"start": 1660320408000,
"end": 1660343797000,
"type": "Group 1",
"color": "orange",
"textColor": "white"
},
{
"name": "Past event 2",
"start": 1660406808000,
"end": 1660429160000,
"type": "Group 1",
"color": "orange",
"textColor": "white"
},
{
"name": "Past event 3",
"start": 1660493208000,
"end": 1660503981000,
"type": "Group 1",
"color": "orange",
"textColor": "white"
},
{
"name": "Past event 4",
"start": 1660579608000,
"end": 1660624108000,
"type": "Group 1",
"color": "orange",
"textColor": "white"
},
{
"name": "Past event 5",
"start": 1660666008000,
"end": 1660681529000,
"type": "Group 1",
"color": "orange",
"textColor": "white"
}
]
}

View File

@@ -0,0 +1,38 @@
{
"Group 1": [
{
"name": "Group 1 event 1",
"start": 1650320408000,
"end": 1660343797000,
"type": "Group 1",
"color": "orange",
"textColor": "white"
},
{
"name": "Group 1 event 2",
"start": 1660005808000,
"end": 1660429160000,
"type": "Group 1",
"color": "yellow",
"textColor": "white"
}
],
"Group 2": [
{
"name": "Group 2 event 1",
"start": 1660320408000,
"end": 1660420408000,
"type": "Group 2",
"color": "green",
"textColor": "white"
},
{
"name": "Group 2 event 2",
"start": 1660406808000,
"end": 1690429160000,
"type": "Group 2",
"color": "blue",
"textColor": "white"
}
]
}

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -25,7 +25,7 @@ const { createDomainObjectWithDefaults, createNotification, expandEntireTree } =
test.describe('AppActions', () => {
test('createDomainObjectsWithDefaults', async ({ page }) => {
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
const e2eFolder = await createDomainObjectWithDefaults(page, {
type: 'Folder',
@@ -86,7 +86,7 @@ test.describe('AppActions', () => {
});
});
test("createNotification", async ({ page }) => {
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
await createNotification(page, {
message: 'Test info notification',
severity: 'info'
@@ -110,7 +110,7 @@ test.describe('AppActions', () => {
await page.locator('[aria-label="Dismiss"]').click();
});
test('expandEntireTree', async ({ page }) => {
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
const rootFolder = await createDomainObjectWithDefaults(page, {
type: 'Folder'

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -32,7 +32,7 @@ test.describe('baseFixtures tests', () => {
test('Verify that tests fail if console.error is thrown', async ({ page }) => {
test.fail();
//Go to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
//Verify that ../fixtures.js detects console log errors
await Promise.all([
@@ -43,7 +43,7 @@ test.describe('baseFixtures tests', () => {
});
test('Verify that tests pass if console.warn is thrown', async ({ page }) => {
//Go to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
//Verify that ../fixtures.js detects console log errors
await Promise.all([

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -63,7 +63,7 @@ test.describe('Renaming Timer Object', () => {
let timer;
test.beforeEach(async ({ page }) => {
// Open a browser, navigate to the main page, and wait until all network events to resolve
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
// We provide some helper functions in appActions like `createDomainObjectWithDefaults()`.
// This example will create a Timer object with default properties, under the root folder:

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -36,7 +36,7 @@ const { test, expect } = require('../../pluginFixtures.js');
test('Generate Visual Test Data @localStorage', async ({ page, context }) => {
//Go to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
const overlayPlot = await createDomainObjectWithDefaults(page, { type: 'Overlay Plot' });
// click create button

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -30,7 +30,7 @@ test.describe('recycled_local_storage @localStorage', () => {
//We may want to do some additional level of verification of this file. For now, we just verify that it exists and can be used in a test suite.
test.use({ storageState: './e2e/test-data/recycled_local_storage.json' });
test('Can use recycled_local_storage file', async ({ page }) => {
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
});
});

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -29,7 +29,7 @@ const { test, expect } = require('../../baseFixtures.js');
test.describe('Branding tests', () => {
test('About Modal launches with basic branding properties', async ({ page }) => {
// Go to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Click About button
await page.click('.l-shell__app-logo');
@@ -47,7 +47,7 @@ test.describe('Branding tests', () => {
});
test('Verify Links in About Modal @2p', async ({ page }) => {
// Go to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Click About button
await page.click('.l-shell__app-logo');

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -100,7 +100,7 @@ test.describe("CouchDB initialization with mocked responses @couchdb", () => {
&& req.method() === 'GET');
// Go to baseURL.
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Wait for both requests to resolve.
await Promise.all([

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -30,7 +30,7 @@ const { createDomainObjectWithDefaults } = require('../../../appActions');
test.describe('Example Event Generator CRUD Operations', () => {
test('Can create a Test Event Generator and it results in the table View', async ({ page }) => {
//Go to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
//Create a name for the object
const newObjectName = 'Test Event Generator';

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -32,7 +32,7 @@ test.describe('Sine Wave Generator', () => {
test.skip(browserName === 'firefox', 'This test needs to be updated to work with firefox');
//Go to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
//Click the Create button
await page.click('button:has-text("Create")');

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -36,7 +36,7 @@ const imageFilePath = 'e2e/test-data/rick.jpg';
test.describe('Form Validation Behavior', () => {
test('Required Field indicators appear if title is empty and can be corrected', async ({ page }) => {
//Go to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
await page.click('button:has-text("Create")');
await page.click(':nth-match(:text("Folder"), 2)');
@@ -77,7 +77,7 @@ test.describe('Form File Input Behavior', () => {
});
test('Can select a JSON file type', async ({ page }) => {
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
await page.getByRole('button', { name: ' Create ' }).click();
await page.getByRole('menuitem', { name: 'JSON File Input Object' }).click();
@@ -91,7 +91,7 @@ test.describe('Form File Input Behavior', () => {
});
test('Can select an image file type', async ({ page }) => {
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
await page.getByRole('button', { name: ' Create ' }).click();
await page.getByRole('menuitem', { name: 'Image File Input Object' }).click();
@@ -117,7 +117,7 @@ test.describe('Persistence operations @addInit', () => {
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/4323'
});
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
await page.click('button:has-text("Create")');
@@ -138,7 +138,7 @@ test.describe('Persistence operations @couchdb', () => {
description: 'https://github.com/nasa/openmct/issues/5616'
});
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Create a new 'Clock' object with default settings
const clock = await createDomainObjectWithDefaults(page, {
@@ -166,12 +166,13 @@ test.describe('Persistence operations @couchdb', () => {
timeout: 1000
}).toEqual(1);
});
test('Can create an object after a conflict error @couchdb @2p', async ({ page }) => {
test('Can create an object after a conflict error @couchdb @2p', async ({ page, openmctConfig }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/5982'
});
const { myItemsFolderName } = openmctConfig;
// Instantiate a second page/tab
const page2 = await page.context().newPage();
// Both pages: Go to baseURL
@@ -180,6 +181,10 @@ test.describe('Persistence operations @couchdb', () => {
page2.goto('./', { waitUntil: 'networkidle' })
]);
//Slow down the test a bit
await expect(page.getByRole('treeitem', { name: `  ${myItemsFolderName}` })).toBeVisible();
await expect(page2.getByRole('treeitem', { name: `  ${myItemsFolderName}` })).toBeVisible();
// Both pages: Click the Create button
await Promise.all([
page.click('button:has-text("Create")'),

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -36,7 +36,7 @@ test.describe('Persistence operations @addInit', () => {
});
test('Non-persistable objects should not show persistence related actions', async ({ page }) => {
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
await page.locator('text=Persistence Testing').first().click({
button: 'right'

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*

View File

@@ -35,7 +35,7 @@ test.describe('Notifications List', () => {
});
// Go to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Create an error notification with the message "Error message"
await createNotification(page, {
@@ -80,7 +80,7 @@ test.describe('Notification Overlay', () => {
});
// Go to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Create a new Display Layout object
await createDomainObjectWithDefaults(page, { type: 'Display Layout' });

View File

@@ -0,0 +1,85 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2023, 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.
*****************************************************************************/
const { test, expect } = require('../../../pluginFixtures');
const { createPlanFromJSON, createDomainObjectWithDefaults, selectInspectorTab } = require('../../../appActions');
const testPlan1 = require('../../../test-data/examplePlans/ExamplePlan_Small1.json');
const testPlan2 = require('../../../test-data/examplePlans/ExamplePlan_Small2.json');
const { assertPlanActivities, setBoundsToSpanAllActivities } = require('../../../helper/planningUtils');
const { getPreciseDuration } = require('../../../../src/utils/duration');
test.describe("Gantt Chart", () => {
let ganttChart;
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'domcontentloaded' });
ganttChart = await createDomainObjectWithDefaults(page, {
type: 'Gantt Chart'
});
await createPlanFromJSON(page, {
json: testPlan1,
parent: ganttChart.uuid
});
});
test("Displays all plan events", async ({ page }) => {
await page.goto(ganttChart.url);
await assertPlanActivities(page, testPlan1, ganttChart.url);
});
test("Replaces a plan with a new plan", async ({ page }) => {
await assertPlanActivities(page, testPlan1, ganttChart.url);
await createPlanFromJSON(page, {
json: testPlan2,
parent: ganttChart.uuid
});
const replaceModal = page.getByRole('dialog').filter({ hasText: "This action will replace the current Plan. Do you want to continue?" });
await expect(replaceModal).toBeVisible();
await page.getByRole('button', { name: 'OK' }).click();
await assertPlanActivities(page, testPlan2, ganttChart.url);
});
test("Can select a single activity and display its details in the inspector", async ({ page }) => {
test.slow();
await page.goto(ganttChart.url);
await setBoundsToSpanAllActivities(page, testPlan1, ganttChart.url);
const activities = Object.values(testPlan1).flat();
const activity = activities[0];
await page.locator('g').filter({ hasText: new RegExp(activity.name) }).click();
await selectInspectorTab(page, 'Activity');
const startDateTime = await page.locator('.c-inspect-properties__label:has-text("Start DateTime")+.c-inspect-properties__value').innerText();
const endDateTime = await page.locator('.c-inspect-properties__label:has-text("End DateTime")+.c-inspect-properties__value').innerText();
const duration = await page.locator('.c-inspect-properties__label:has-text("duration")+.c-inspect-properties__value').innerText();
const expectedStartDate = new Date(activity.start).toISOString();
const actualStartDate = new Date(startDateTime).toISOString();
const expectedEndDate = new Date(activity.end).toISOString();
const actualEndDate = new Date(endDateTime).toISOString();
const expectedDuration = getPreciseDuration(activity.end - activity.start);
const actualDuration = duration;
expect(expectedStartDate).toEqual(actualStartDate);
expect(expectedEndDate).toEqual(actualEndDate);
expect(expectedDuration).toEqual(actualDuration);
});
});

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -19,69 +19,21 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
const { test, expect } = require('../../../pluginFixtures');
const { test } = require('../../../pluginFixtures');
const { createPlanFromJSON } = require('../../../appActions');
const testPlan = {
"TEST_GROUP": [
{
"name": "Past event 1",
"start": 1660320408000,
"end": 1660343797000,
"type": "TEST-GROUP",
"color": "orange",
"textColor": "white"
},
{
"name": "Past event 2",
"start": 1660406808000,
"end": 1660429160000,
"type": "TEST-GROUP",
"color": "orange",
"textColor": "white"
},
{
"name": "Past event 3",
"start": 1660493208000,
"end": 1660503981000,
"type": "TEST-GROUP",
"color": "orange",
"textColor": "white"
},
{
"name": "Past event 4",
"start": 1660579608000,
"end": 1660624108000,
"type": "TEST-GROUP",
"color": "orange",
"textColor": "white"
},
{
"name": "Past event 5",
"start": 1660666008000,
"end": 1660681529000,
"type": "TEST-GROUP",
"color": "orange",
"textColor": "white"
}
]
};
const testPlan1 = require('../../../test-data/examplePlans/ExamplePlan_Small1.json');
const { assertPlanActivities } = require('../../../helper/planningUtils');
test.describe("Plan", () => {
test("Create a Plan and display all plan events @unstable", async ({ page }) => {
await page.goto('./', { waitUntil: 'networkidle' });
const plan = await createPlanFromJSON(page, {
name: 'Test Plan',
json: testPlan
let plan;
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'domcontentloaded' });
plan = await createPlanFromJSON(page, {
json: testPlan1
});
const startBound = testPlan.TEST_GROUP[0].start;
const endBound = testPlan.TEST_GROUP[testPlan.TEST_GROUP.length - 1].end;
});
// Switch to fixed time mode with all plan events within the bounds
await page.goto(`${plan.url}?tc.mode=fixed&tc.startBound=${startBound}&tc.endBound=${endBound}&tc.timeSystem=utc&view=plan.view`);
const eventCount = await page.locator('.activity-bounds').count();
expect(eventCount).toEqual(testPlan.TEST_GROUP.length);
test("Displays all plan events", async ({ page }) => {
await assertPlanActivities(page, testPlan1, plan.url);
});
});

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -80,7 +80,7 @@ test.describe("Time Strip", () => {
const activityBounds = page.locator('.activity-bounds');
// Goto baseURL
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
const timestrip = await test.step("Create a Time Strip", async () => {
const createdTimeStrip = await createDomainObjectWithDefaults(page, { type: 'Time Strip' });

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -34,7 +34,7 @@ test.describe('Clock Generator CRUD Operations', () => {
description: 'https://github.com/nasa/openmct/issues/4878'
});
//Go to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
//Click the Create button
await page.click('button:has-text("Create")');

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -37,7 +37,7 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
//TODO: This needs to be refactored
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
await page.click('button:has-text("Create")');
await page.locator('li[role="menuitem"]:has-text("Condition Set")').click();
@@ -52,10 +52,9 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
//Set object identifier from url
conditionSetUrl = page.url();
console.log('conditionSetUrl ' + conditionSetUrl);
getConditionSetIdentifierFromUrl = conditionSetUrl.split('/').pop().split('?')[0];
console.debug('getConditionSetIdentifierFromUrl ' + getConditionSetIdentifierFromUrl);
console.debug(`getConditionSetIdentifierFromUrl: ${getConditionSetIdentifierFromUrl}`);
await page.close();
});
@@ -149,7 +148,7 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
});
test('condition set object can be deleted by Search Tree Actions menu on @localStorage', async ({ page }) => {
//Navigate to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
//Assertions on loaded Condition Set in main view. This is a stateful transition step after page.goto()
await expect(page.locator('a:has-text("Unnamed Condition Set Condition Set") >> nth=0')).toBeVisible();
@@ -181,10 +180,11 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
});
test.describe('Basic Condition Set Use', () => {
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' });
});
test('Can add a condition', async ({ page }) => {
//Navigate to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
// Create a new condition set
await createDomainObjectWithDefaults(page, {
type: 'Condition Set',
@@ -199,4 +199,127 @@ test.describe('Basic Condition Set Use', () => {
const numOfUnnamedConditions = await page.locator('text=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"
});
const conditionSet1 = await createDomainObjectWithDefaults(page, {
type: 'Condition Set',
name: "Test Condition Set"
});
// Change the object to edit mode
await page.locator('[title="Edit"]').click();
// Expand the 'My Items' folder in the left tree
await page.goto(conditionSet1.url);
page.click('button[title="Show selected item in tree"]');
// Add the Alpha & Beta Sine Wave Generator to the Condition Set and save changes
const treePane = page.getByRole('tree', {
name: 'Main Tree'
});
const alphaGeneratorTreeItem = treePane.getByRole('treeitem', { name: "Alpha Sine Wave Generator"});
const betaGeneratorTreeItem = treePane.getByRole('treeitem', { name: "Beta Sine Wave Generator"});
const conditionCollection = page.locator('#conditionCollection');
await alphaGeneratorTreeItem.dragTo(conditionCollection);
await betaGeneratorTreeItem.dragTo(conditionCollection);
const saveButtonLocator = page.locator('button[title="Save"]');
await saveButtonLocator.click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
await page.click('button[title="Change the current view"]');
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();
});
test('ConditionSet should output blank instead of the default value', async ({ page }) => {
//Navigate to baseURL
await page.goto('./', { waitUntil: 'domcontentloaded' });
//Click the Create button
await page.click('button:has-text("Create")');
// Click the object specified by 'type'
await page.click(`li[role='menuitem']:text("Sine Wave Generator")`);
await page.getByRole('spinbutton', { name: 'Loading Delay (ms)' }).fill('8000');
const nameInput = page.locator('form[name="mctForm"] .first input[type="text"]');
await nameInput.fill("Delayed Sine Wave Generator");
// Click OK button and wait for Navigate event
await Promise.all([
page.waitForLoadState(),
page.click('[aria-label="Save"]'),
// Wait for Save Banner to appear
page.waitForSelector('.c-message-banner__message')
]);
// Create a new condition set
await createDomainObjectWithDefaults(page, {
type: 'Condition Set',
name: "Test Blank Output of Condition Set"
});
// Change the object to edit mode
await page.locator('[title="Edit"]').click();
// Click Add Condition button twice
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');
// Expand the 'My Items' folder in the left tree
await page.locator('.c-tree__item__view-control.c-disclosure-triangle').first().click();
// Add the Sine Wave Generator to the Condition Set and save changes
const treePane = page.getByRole('tree', {
name: 'Main Tree'
});
const sineWaveGeneratorTreeItem = treePane.getByRole('treeitem', { name: "Delayed Sine Wave Generator"});
const conditionCollection = await page.locator('#conditionCollection');
await sineWaveGeneratorTreeItem.dragTo(conditionCollection);
const firstCriterionTelemetry = await page.locator('[aria-label="Criterion Telemetry Selection"] >> nth=0');
firstCriterionTelemetry.selectOption({ label: 'Delayed Sine Wave Generator' });
const secondCriterionTelemetry = await page.locator('[aria-label="Criterion Telemetry Selection"] >> nth=1');
secondCriterionTelemetry.selectOption({ label: 'Delayed Sine Wave Generator' });
const firstCriterionMetadata = await page.locator('[aria-label="Criterion Metadata Selection"] >> nth=0');
firstCriterionMetadata.selectOption({ label: 'Sine' });
const secondCriterionMetadata = await page.locator('[aria-label="Criterion Metadata Selection"] >> nth=1');
secondCriterionMetadata.selectOption({ label: 'Sine' });
const firstCriterionComparison = await page.locator('[aria-label="Criterion Comparison Selection"] >> nth=0');
firstCriterionComparison.selectOption({ label: 'is greater than or equal to' });
const secondCriterionComparison = await page.locator('[aria-label="Criterion Comparison Selection"] >> nth=1');
secondCriterionComparison.selectOption({ label: 'is less than' });
const firstCriterionInput = await page.locator('[aria-label="Criterion Input"] >> nth=0');
await firstCriterionInput.fill("0");
const secondCriterionInput = await page.locator('[aria-label="Criterion Input"] >> nth=1');
await secondCriterionInput.fill("0");
const saveButtonLocator = page.locator('button[title="Save"]');
await saveButtonLocator.click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
const outputValue = await page.locator('[aria-label="Current Output Value"]');
await expect(outputValue).toHaveText('---');
});
});

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -27,7 +27,7 @@ test.describe('Display Layout', () => {
/** @type {import('../../../../appActions').CreatedObjectInfo} */
let sineWaveObject;
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
await setRealTimeMode(page);
// Create Sine Wave Generator

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -22,6 +22,7 @@
const { test, expect } = require('../../../../pluginFixtures');
const utils = require('../../../../helper/faultUtils');
const { selectInspectorTab } = require('../../../../appActions');
test.describe('The Fault Management Plugin using example faults', () => {
test.beforeEach(async ({ page }) => {
@@ -38,6 +39,7 @@ test.describe('The Fault Management Plugin using example faults', () => {
test('When selecting a fault, it has an "is-selected" class and it\'s information shows in the inspector @unstable', async ({ page }) => {
await utils.selectFaultItem(page, 1);
await selectInspectorTab(page, 'Fault Management Configuration');
const selectedFaultName = await page.locator('.c-fault-mgmt__list.is-selected .c-fault-mgmt__list-faultname').textContent();
const inspectorFaultNameCount = await page.locator(`.c-inspector__properties >> :text("${selectedFaultName}")`).count();
@@ -52,6 +54,7 @@ test.describe('The Fault Management Plugin using example faults', () => {
const selectedRows = page.locator('.c-fault-mgmt__list.is-selected .c-fault-mgmt__list-faultname');
expect.soft(await selectedRows.count()).toEqual(2);
await selectInspectorTab(page, 'Fault Management Configuration');
const firstSelectedFaultName = await selectedRows.nth(0).textContent();
const secondSelectedFaultName = await selectedRows.nth(1).textContent();
const firstNameInInspectorCount = await page.locator(`.c-inspector__properties >> :text("${firstSelectedFaultName}")`).count();

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -27,7 +27,7 @@ test.describe('Flexible Layout', () => {
let sineWaveObject;
let clockObject;
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Create Sine Wave Generator
sineWaveObject = await createDomainObjectWithDefaults(page, {

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -31,7 +31,7 @@ const uuid = require('uuid').v4;
test.describe('Gauge', () => {
test.beforeEach(async ({ page }) => {
// Open a browser, navigate to the main page, and wait until all networkevents to resolve
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
});
test('Can add and remove telemetry sources @unstable', async ({ page }) => {

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -29,15 +29,15 @@ const { waitForAnimations } = require('../../../../baseFixtures');
const { test, expect } = require('../../../../pluginFixtures');
const { createDomainObjectWithDefaults } = require('../../../../appActions');
const backgroundImageSelector = '.c-imagery__main-image__background-image';
const panHotkey = process.platform === 'linux' ? ['Control', 'Alt'] : ['Alt'];
const expectedAltText = process.platform === 'linux' ? 'Ctrl+Alt drag to pan' : 'Alt drag to pan';
const panHotkey = process.platform === 'linux' ? ['Shift', 'Alt'] : ['Alt'];
const expectedAltText = process.platform === 'linux' ? 'Shift+Alt drag to pan' : 'Alt drag to pan';
const thumbnailUrlParamsRegexp = /\?w=100&h=100/;
//The following block of tests verifies the basic functionality of example imagery and serves as a template for Imagery objects embedded in other objects.
test.describe('Example Imagery Object', () => {
test.beforeEach(async ({ page }) => {
//Go to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Create a default 'Example Imagery' object
const exampleImagery = await createDomainObjectWithDefaults(page, { type: 'Example Imagery' });
@@ -178,7 +178,7 @@ test.describe('Example Imagery in Display Layout', () => {
let displayLayout;
test.beforeEach(async ({ page }) => {
// Go to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
displayLayout = await createDomainObjectWithDefaults(page, { type: 'Display Layout' });
await page.goto(displayLayout.url);
@@ -317,7 +317,7 @@ test.describe('Example Imagery in Display Layout', () => {
test.describe('Example Imagery in Flexible layout', () => {
let flexibleLayout;
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
flexibleLayout = await createDomainObjectWithDefaults(page, { type: 'Flexible Layout' });
await page.goto(flexibleLayout.url);
@@ -359,7 +359,7 @@ test.describe('Example Imagery in Flexible layout', () => {
test.describe('Example Imagery in Tabs View', () => {
let tabsView;
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
tabsView = await createDomainObjectWithDefaults(page, { type: 'Tabs View' });
await page.goto(tabsView.url);
@@ -395,7 +395,7 @@ test.describe('Example Imagery in Tabs View', () => {
test.describe('Example Imagery in Time Strip', () => {
let timeStripObject;
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
timeStripObject = await createDomainObjectWithDefaults(page, {
type: 'Time Strip'
});

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,12 +21,125 @@
*****************************************************************************/
const { test, expect } = require('../../../../pluginFixtures');
const { createDomainObjectWithDefaults, setStartOffset, setFixedTimeMode, setRealTimeMode } = require('../../../../appActions');
const { createDomainObjectWithDefaults, setStartOffset, setFixedTimeMode, setRealTimeMode, selectInspectorTab } = require('../../../../appActions');
test.describe('Testing LAD table configuration', () => {
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Create LAD table
const ladTable = await createDomainObjectWithDefaults(page, {
type: 'LAD Table',
name: "Test LAD Table"
});
// Create Sine Wave Generator
await createDomainObjectWithDefaults(page, {
type: 'Sine Wave Generator',
name: "Test Sine Wave Generator",
parent: ladTable.uuid
});
await page.goto(ladTable.url);
});
test('in edit mode, LAD Tables provide ability to hide columns', async ({ page }) => {
// Edit LAD table
await page.locator('[title="Edit"]').click();
// // Expand the 'My Items' folder in the left tree
// await page.locator('.c-tree__item__view-control.c-disclosure-triangle').click();
// // Add the Sine Wave Generator to the LAD table and save changes
// await page.dragAndDrop('role=treeitem[name=/Test Sine Wave Generator/]', '.c-lad-table-wrapper');
// select configuration tab in inspector
await selectInspectorTab(page, 'LAD Table Configuration');
// make sure headers are visible initially
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible();
await expect(page.getByRole('cell', { name: 'Units' })).toBeVisible();
await expect(page.getByRole('cell', { name: 'Type' })).toBeVisible();
// hide timestamp column
await page.getByLabel('Timestamp').uncheck();
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeHidden();
await expect(page.getByRole('cell', { name: 'Units' })).toBeVisible();
await expect(page.getByRole('cell', { name: 'Type' })).toBeVisible();
// hide units & type column
await page.getByLabel('Units').uncheck();
await page.getByLabel('Type').uncheck();
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeHidden();
await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden();
await expect(page.getByRole('cell', { name: 'Type' })).toBeHidden();
// save and reload and verify they columns are still hidden
await page.locator('button[title="Save"]').click();
await page.locator('text=Save and Finish Editing').click();
await page.reload();
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeHidden();
await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden();
await expect(page.getByRole('cell', { name: 'Type' })).toBeHidden();
// Edit LAD table
await page.locator('[title="Edit"]').click();
await selectInspectorTab(page, 'LAD Table Configuration');
// show timestamp column
await page.getByLabel('Timestamp').check();
await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden();
await expect(page.getByRole('cell', { name: 'Type' })).toBeHidden();
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible();
// save and reload and make sure only timestamp is still visible
await page.locator('button[title="Save"]').click();
await page.locator('text=Save and Finish Editing').click();
await page.reload();
await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden();
await expect(page.getByRole('cell', { name: 'Type' })).toBeHidden();
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible();
// Edit LAD table
await page.locator('[title="Edit"]').click();
await selectInspectorTab(page, 'LAD Table Configuration');
// show units and type columns
await page.getByLabel('Units').check();
await page.getByLabel('Type').check();
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible();
await expect(page.getByRole('cell', { name: 'Units' })).toBeVisible();
await expect(page.getByRole('cell', { name: 'Type' })).toBeVisible();
// save and reload and make sure all columns are still visible
await page.locator('button[title="Save"]').click();
await page.locator('text=Save and Finish Editing').click();
await page.reload();
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible();
await expect(page.getByRole('cell', { name: 'Units' })).toBeVisible();
await expect(page.getByRole('cell', { name: 'Type' })).toBeVisible();
});
test('LAD Tables don\'t allow selection of rows but does show context click menus', async ({ page }) => {
const cell = await page.locator('.js-first-data');
const userSelectable = await cell.evaluate((el) => {
return window.getComputedStyle(el).getPropertyValue('user-select');
});
expect(userSelectable).toBe('none');
// Right-click on the LAD table row
await cell.click({
button: 'right'
});
const menuOptions = page.locator('.c-menu ul');
await expect.soft(menuOptions).toContainText('View Full Datum');
await expect.soft(menuOptions).toContainText('View Historical Data');
await expect.soft(menuOptions).toContainText('Remove');
// await page.locator('li[title="Remove this object from its containing object."]').click();
});
});
test.describe('Testing LAD table @unstable', () => {
let sineWaveObject;
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
await setRealTimeMode(page);
// Create Sine Wave Generator

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -24,7 +24,7 @@
This test suite is dedicated to tests which verify the basic operations surrounding Notebooks.
*/
const { test, expect } = require('../../../../pluginFixtures');
const { test, expect, streamToString } = require('../../../../pluginFixtures');
const { createDomainObjectWithDefaults } = require('../../../../appActions');
const nbUtils = require('../../../../helper/notebookUtils');
const path = require('path');
@@ -72,7 +72,7 @@ test.describe('Notebook section tests', () => {
//The following test cases are associated with Notebook Sections
test.beforeEach(async ({ page }) => {
//Navigate to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Create Notebook
await createDomainObjectWithDefaults(page, {
@@ -133,7 +133,7 @@ test.describe('Notebook page tests', () => {
//The following test cases are associated with Notebook Pages
test.beforeEach(async ({ page }) => {
//Navigate to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Create Notebook
await createDomainObjectWithDefaults(page, {
@@ -198,6 +198,36 @@ test.describe('Notebook page tests', () => {
});
});
test.describe('Notebook export tests', () => {
test.beforeEach(async ({ page }) => {
//Navigate to baseURL
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Create Notebook
await createDomainObjectWithDefaults(page, {
type: NOTEBOOK_NAME
});
});
test('can export notebook as text', async ({ page }) => {
await nbUtils.enterTextEntry(page, `Foo bar entry`);
// Click on 3 Dot Menu
await page.locator('button[title="More options"]').click();
const downloadPromise = page.waitForEvent('download');
await page.getByRole('menuitem', { name: /Export Notebook as Text/ }).click();
await page.getByRole('button', { name: 'Save' }).click();
const download = await downloadPromise;
const readStream = await download.createReadStream();
const exportedText = await streamToString(readStream);
expect(exportedText).toContain('Foo bar entry');
});
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 tags', async ({ page }) => {});
test.fixme('can export all notebook snapshots', async ({ page }) => {});
});
test.describe('Notebook search tests', () => {
test.fixme('Can search for a single result', async ({ page }) => {});
test.fixme('Can search for many results', async ({ page }) => {});
@@ -213,16 +243,24 @@ test.describe('Notebook entry tests', () => {
test.beforeEach(async ({ page }) => {
// eslint-disable-next-line no-undef
await page.addInitScript({ path: path.join(__dirname, '../../../../helper/', 'addInitNotebookWithUrls.js') });
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
notebookObject = await createDomainObjectWithDefaults(page, {
type: NOTEBOOK_NAME
});
});
test.fixme('When a new entry is created, it should be focused', async ({ page }) => {});
test('When a new entry is created, it should be focused and selected', async ({ page }) => {
// Navigate to the notebook object
await page.goto(notebookObject.url);
// Click .c-notebook__drag-area
await page.locator('.c-notebook__drag-area').click();
await expect(page.locator('[aria-label="Notebook Entry Input"]')).toBeVisible();
await expect(page.locator('[aria-label="Notebook Entry"]')).toHaveClass(/is-selected/);
});
test('When an object is dropped into a notebook, a new entry is created and it should be focused @unstable', async ({ page }) => {
// Create Overlay Plot
await createDomainObjectWithDefaults(page, {
const overlayPlot = await createDomainObjectWithDefaults(page, {
type: 'Overlay Plot'
});
@@ -232,17 +270,17 @@ test.describe('Notebook entry tests', () => {
// Reveal the notebook in the tree
await page.getByTitle('Show selected item in tree').click();
await page.dragAndDrop('role=treeitem[name=/Dropped Overlay Plot/]', '.c-notebook__drag-area');
await page.dragAndDrop(`role=treeitem[name=/${overlayPlot.name}/]`, '.c-notebook__drag-area');
const embed = page.locator('.c-ne__embed__link');
const embedName = await embed.textContent();
await expect(embed).toHaveClass(/icon-plot-overlay/);
expect(embedName).toBe('Dropped Overlay Plot');
expect(embedName).toBe(overlayPlot.name);
});
test('When an object is dropped into a notebooks existing entry, it should be focused @unstable', async ({ page }) => {
// Create Overlay Plot
await createDomainObjectWithDefaults(page, {
const overlayPlot = await createDomainObjectWithDefaults(page, {
type: 'Overlay Plot'
});
@@ -253,17 +291,35 @@ test.describe('Notebook entry tests', () => {
await page.getByTitle('Show selected item in tree').click();
await nbUtils.enterTextEntry(page, 'Entry to drop into');
await page.dragAndDrop('role=treeitem[name=/Dropped Overlay Plot/]', 'text=Entry to drop into');
await page.dragAndDrop(`role=treeitem[name=/${overlayPlot.name}/]`, 'text=Entry to drop into');
const existingEntry = page.locator('.c-ne__content', { has: page.locator('text="Entry to drop into"') });
const embed = existingEntry.locator('.c-ne__embed__link');
const embedName = await embed.textContent();
await expect(embed).toHaveClass(/icon-plot-overlay/);
expect(embedName).toBe('Dropped Overlay Plot');
expect(embedName).toBe(overlayPlot.name);
});
test.fixme('new entries persist through navigation events without save', async ({ page }) => {});
test.fixme('previous and new entries can be deleted', async ({ page }) => {});
test('previous and new entries can be deleted', async ({ page }) => {
// Navigate to the notebook object
await page.goto(notebookObject.url);
await nbUtils.enterTextEntry(page, 'First Entry');
await page.hover('text="First Entry"');
await page.click('button[title="Delete this entry"]');
await page.getByRole('button', { name: 'Ok' }).filter({ hasText: 'Ok' }).click();
await expect(page.locator('text="First Entry"')).toBeHidden();
await nbUtils.enterTextEntry(page, 'Another First Entry');
await nbUtils.enterTextEntry(page, 'Second Entry');
await nbUtils.enterTextEntry(page, 'Third Entry');
await page.hover('[aria-label="Notebook Entry"] >> nth=2');
await page.click('button[title="Delete this entry"] >> nth=2');
await page.getByRole('button', { name: 'Ok' }).filter({ hasText: 'Ok' }).click();
await expect(page.locator('text="Third Entry"')).toBeHidden();
await expect(page.locator('text="Another First Entry"')).toBeVisible();
await expect(page.locator('text="Second Entry"')).toBeVisible();
});
test('when a valid link is entered into a notebook entry, it becomes clickable when viewing', async ({ page }) => {
const TEST_LINK = 'http://www.google.com';

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -66,7 +66,7 @@ test.describe('Snapshot Menu tests', () => {
test.describe('Snapshot Container tests', () => {
test.beforeEach(async ({ page }) => {
//Navigate to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Create Notebook
// const notebook = await createDomainObjectWithDefaults(page, {

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -26,46 +26,49 @@ This test suite is dedicated to tests which verify the basic operations surround
const { test, expect } = require('../../../../pluginFixtures');
const { createDomainObjectWithDefaults } = require('../../../../appActions');
const nbUtils = require('../../../../helper/notebookUtils');
test.describe('Notebook Tests with CouchDB @couchdb', () => {
let testNotebook;
test.beforeEach(async ({ page }) => {
//Navigate to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Create Notebook
testNotebook = await createDomainObjectWithDefaults(page, {
type: 'Notebook',
name: "TestNotebook"
});
testNotebook = await createDomainObjectWithDefaults(page, {type: 'Notebook' });
await page.goto(testNotebook.url, { waitUntil: 'networkidle'});
});
test('Inspect Notebook Entry Network Requests', async ({ page }) => {
//Ensure we're on the annotations Tab in the inspector
await page.getByText('Annotations').click();
// Expand sidebar
await page.locator('.c-notebook__toggle-nav-button').click();
// Collect all request events to count and assert after notebook action
let addingNotebookElementsRequests = [];
page.on('request', (request) => addingNotebookElementsRequests.push(request));
let notebookElementsRequests = [];
page.on('request', (request) => notebookElementsRequests.push(request));
//Clicking Add Page generates
let [notebookUrlRequest, allDocsRequest] = await Promise.all([
// Waits for the next request with the specified url
page.waitForRequest(`**/openmct/${testNotebook.uuid}`),
page.waitForRequest('**/openmct/_all_docs?include_docs=true'),
// Triggers the request
page.click('[aria-label="Add Page"]'),
// Ensures that there are no other network requests
page.waitForLoadState('networkidle')
page.click('[aria-label="Add Page"]')
]);
// Ensures that there are no other network requests
await page.waitForLoadState('networkidle');
// Assert that only two requests are made
// Network Requests are:
// 1) The actual POST to create the page
// 2) The shared worker event from 👆 request
expect(addingNotebookElementsRequests.length).toBe(2);
expect(notebookElementsRequests.length).toBe(2);
// Assert on request object
expect(notebookUrlRequest.postDataJSON().metadata.name).toBe('TestNotebook');
expect(notebookUrlRequest.postDataJSON().metadata.name).toBe(testNotebook.name);
expect(notebookUrlRequest.postDataJSON().model.persisted).toBeGreaterThanOrEqual(notebookUrlRequest.postDataJSON().model.modified);
expect(allDocsRequest.postDataJSON().keys).toContain(testNotebook.uuid);
@@ -73,13 +76,10 @@ test.describe('Notebook Tests with CouchDB @couchdb', () => {
// Network Requests are:
// 1) The actual POST to create the entry
// 2) The shared worker event from 👆 POST request
addingNotebookElementsRequests = [];
await page.locator('text=To start a new entry, click here or drag and drop any object').click();
await page.locator('[aria-label="Notebook Entry Input"]').click();
await page.locator('[aria-label="Notebook Entry Input"]').fill(`First Entry`);
await page.locator('[aria-label="Notebook Entry Input"]').press('Enter');
notebookElementsRequests = [];
await nbUtils.enterTextEntry(page, 'First Entry');
await page.waitForLoadState('networkidle');
expect(addingNotebookElementsRequests.length).toBeLessThanOrEqual(2);
expect(notebookElementsRequests.length).toBeLessThanOrEqual(2);
// Add some tags
// Network Requests are for each tag creation are:
@@ -95,32 +95,17 @@ test.describe('Notebook Tests with CouchDB @couchdb', () => {
// 10) Entry is timestamped
// 11) The shared worker event from 👆 POST request
addingNotebookElementsRequests = [];
await page.hover(`button:has-text("Add Tag")`);
await page.locator(`button:has-text("Add Tag")`).click();
await page.locator('[placeholder="Type to select tag"]').click();
await page.locator('[aria-label="Autocomplete Options"] >> text=Driving').click();
await page.waitForSelector('[aria-label="Tag"]:has-text("Driving")');
page.waitForLoadState('networkidle');
expect(filterNonFetchRequests(addingNotebookElementsRequests).length).toBeLessThanOrEqual(11);
notebookElementsRequests = [];
await addTagAndAwaitNetwork(page, 'Driving');
expect(filterNonFetchRequests(notebookElementsRequests).length).toBeLessThanOrEqual(11);
addingNotebookElementsRequests = [];
await page.hover(`button:has-text("Add Tag")`);
await page.locator(`button:has-text("Add Tag")`).click();
await page.locator('[placeholder="Type to select tag"]').click();
await page.locator('[aria-label="Autocomplete Options"] >> text=Drilling').click();
await page.waitForSelector('[aria-label="Tag"]:has-text("Drilling")');
page.waitForLoadState('networkidle');
expect(filterNonFetchRequests(addingNotebookElementsRequests).length).toBeLessThanOrEqual(11);
notebookElementsRequests = [];
await addTagAndAwaitNetwork(page, 'Drilling');
expect(filterNonFetchRequests(notebookElementsRequests).length).toBeLessThanOrEqual(11);
addingNotebookElementsRequests = [];
await page.hover(`button:has-text("Add Tag")`);
await page.locator(`button:has-text("Add Tag")`).click();
await page.locator('[placeholder="Type to select tag"]').click();
await page.locator('[aria-label="Autocomplete Options"] >> text=Science').click();
await page.waitForSelector('[aria-label="Tag"]:has-text("Science")');
page.waitForLoadState('networkidle');
expect(filterNonFetchRequests(addingNotebookElementsRequests).length).toBeLessThanOrEqual(11);
notebookElementsRequests = [];
await addTagAndAwaitNetwork(page, 'Science');
expect(filterNonFetchRequests(notebookElementsRequests).length).toBeLessThanOrEqual(11);
// Delete all the tags
// Network requests are:
@@ -129,58 +114,25 @@ test.describe('Notebook Tests with CouchDB @couchdb', () => {
// 3) Timestamp update on entry
// 4) The shared worker event from 👆 POST request
// This happens for 3 tags so 12 requests
addingNotebookElementsRequests = [];
await page.hover('[aria-label="Tag"]:has-text("Driving")');
await page.locator('[aria-label="Tag"]:has-text("Driving") ~ .c-completed-tag-deletion').click();
await page.waitForSelector('[aria-label="Tag"]:has-text("Driving")', {state: 'hidden'});
await page.hover('[aria-label="Tag"]:has-text("Drilling")');
await page.locator('[aria-label="Tag"]:has-text("Drilling") ~ .c-completed-tag-deletion').click();
await page.waitForSelector('[aria-label="Tag"]:has-text("Drilling")', {state: 'hidden'});
page.hover('[aria-label="Tag"]:has-text("Science")');
await page.locator('[aria-label="Tag"]:has-text("Science") ~ .c-completed-tag-deletion').click();
await page.waitForSelector('[aria-label="Tag"]:has-text("Science")', {state: 'hidden'});
page.waitForLoadState('networkidle');
expect(filterNonFetchRequests(addingNotebookElementsRequests).length).toBeLessThanOrEqual(12);
notebookElementsRequests = [];
await removeTagAndAwaitNetwork(page, 'Driving');
await removeTagAndAwaitNetwork(page, 'Drilling');
await removeTagAndAwaitNetwork(page, 'Science');
expect(filterNonFetchRequests(notebookElementsRequests).length).toBeLessThanOrEqual(12);
// Add two more pages
await page.click('[aria-label="Add Page"]');
await page.click('[aria-label="Add Page"]');
// Add three entries
await page.locator('text=To start a new entry, click here or drag and drop any object').click();
await page.locator('[aria-label="Notebook Entry Input"]').click();
await page.locator('[aria-label="Notebook Entry Input"]').fill(`First Entry`);
await page.locator('[aria-label="Notebook Entry Input"]').press('Enter');
await page.locator('text=To start a new entry, click here or drag and drop any object').click();
await page.locator('[aria-label="Notebook Entry Input"] >> nth=1').click();
await page.locator('[aria-label="Notebook Entry Input"] >> nth=1').fill(`Second Entry`);
await page.locator('[aria-label="Notebook Entry Input"] >> nth=1').press('Enter');
await page.locator('text=To start a new entry, click here or drag and drop any object').click();
await page.locator('[aria-label="Notebook Entry Input"] >> nth=2').click();
await page.locator('[aria-label="Notebook Entry Input"] >> nth=2').fill(`Third Entry`);
await page.locator('[aria-label="Notebook Entry Input"] >> nth=2').press('Enter');
await nbUtils.enterTextEntry(page, 'First Entry');
await nbUtils.enterTextEntry(page, 'Second Entry');
await nbUtils.enterTextEntry(page, 'Third Entry');
// Add three tags
await page.hover(`button:has-text("Add Tag")`);
await page.locator(`button:has-text("Add Tag")`).click();
await page.locator('[placeholder="Type to select tag"]').click();
await page.locator('[aria-label="Autocomplete Options"] >> text=Science').click();
await page.waitForSelector('[aria-label="Tag"]:has-text("Science")');
await page.hover(`button:has-text("Add Tag")`);
await page.locator(`button:has-text("Add Tag")`).click();
await page.locator('[placeholder="Type to select tag"]').click();
await page.locator('[aria-label="Autocomplete Options"] >> text=Drilling').click();
await page.waitForSelector('[aria-label="Tag"]:has-text("Drilling")');
await page.hover(`button:has-text("Add Tag")`);
await page.locator(`button:has-text("Add Tag")`).click();
await page.locator('[placeholder="Type to select tag"]').click();
await page.locator('[aria-label="Autocomplete Options"] >> text=Driving').click();
await page.waitForSelector('[aria-label="Tag"]:has-text("Driving")');
page.waitForLoadState('networkidle');
await addTagAndAwaitNetwork(page, 'Science');
await addTagAndAwaitNetwork(page, 'Drilling');
await addTagAndAwaitNetwork(page, 'Driving');
// Add a fourth entry
// Network requests are:
@@ -188,14 +140,11 @@ test.describe('Notebook Tests with CouchDB @couchdb', () => {
// 2) The shared worker event from 👆 POST request
// 3) Timestamp update on entry
// 4) The shared worker event from 👆 POST request
addingNotebookElementsRequests = [];
await page.locator('text=To start a new entry, click here or drag and drop any object').click();
await page.locator('[aria-label="Notebook Entry Input"] >> nth=3').click();
await page.locator('[aria-label="Notebook Entry Input"] >> nth=3').fill(`Fourth Entry`);
await page.locator('[aria-label="Notebook Entry Input"] >> nth=3').press('Enter');
notebookElementsRequests = [];
await nbUtils.enterTextEntry(page, 'Fourth Entry');
page.waitForLoadState('networkidle');
expect(filterNonFetchRequests(addingNotebookElementsRequests).length).toBeLessThanOrEqual(4);
expect(filterNonFetchRequests(notebookElementsRequests).length).toBeLessThanOrEqual(4);
// Add a fifth entry
// Network requests are:
@@ -203,28 +152,22 @@ test.describe('Notebook Tests with CouchDB @couchdb', () => {
// 2) The shared worker event from 👆 POST request
// 3) Timestamp update on entry
// 4) The shared worker event from 👆 POST request
addingNotebookElementsRequests = [];
await page.locator('text=To start a new entry, click here or drag and drop any object').click();
await page.locator('[aria-label="Notebook Entry Input"] >> nth=4').click();
await page.locator('[aria-label="Notebook Entry Input"] >> nth=4').fill(`Fifth Entry`);
await page.locator('[aria-label="Notebook Entry Input"] >> nth=4').press('Enter');
notebookElementsRequests = [];
await nbUtils.enterTextEntry(page, 'Fifth Entry');
page.waitForLoadState('networkidle');
expect(filterNonFetchRequests(addingNotebookElementsRequests).length).toBeLessThanOrEqual(4);
expect(filterNonFetchRequests(notebookElementsRequests).length).toBeLessThanOrEqual(4);
// Add a sixth entry
// 1) Send POST to add new entry
// 2) The shared worker event from 👆 POST request
// 3) Timestamp update on entry
// 4) The shared worker event from 👆 POST request
addingNotebookElementsRequests = [];
await page.locator('text=To start a new entry, click here or drag and drop any object').click();
await page.locator('[aria-label="Notebook Entry Input"] >> nth=5').click();
await page.locator('[aria-label="Notebook Entry Input"] >> nth=5').fill(`Sixth Entry`);
await page.locator('[aria-label="Notebook Entry Input"] >> nth=5').press('Enter');
notebookElementsRequests = [];
await nbUtils.enterTextEntry(page, 'Sixth Entry');
page.waitForLoadState('networkidle');
expect(filterNonFetchRequests(addingNotebookElementsRequests).length).toBeLessThanOrEqual(4);
expect(filterNonFetchRequests(notebookElementsRequests).length).toBeLessThanOrEqual(4);
});
test('Search tests', async ({ page }) => {
@@ -233,35 +176,21 @@ test.describe('Notebook Tests with CouchDB @couchdb', () => {
description: 'https://github.com/akhenry/openmct-yamcs/issues/69'
});
await page.getByText('Annotations').click();
await page.locator('text=To start a new entry, click here or drag and drop any object').click();
await page.locator('[aria-label="Notebook Entry Input"]').click();
await page.locator('[aria-label="Notebook Entry Input"]').fill(`First Entry`);
await page.locator('[aria-label="Notebook Entry Input"]').press('Enter');
await nbUtils.enterTextEntry(page, 'First Entry');
// Add three tags
await page.hover(`button:has-text("Add Tag")`);
await page.locator(`button:has-text("Add Tag")`).click();
await page.locator('[placeholder="Type to select tag"]').click();
await page.locator('[aria-label="Autocomplete Options"] >> text=Science').click();
await page.waitForSelector('[aria-label="Tag"]:has-text("Science")');
await page.hover(`button:has-text("Add Tag")`);
await page.locator(`button:has-text("Add Tag")`).click();
await page.locator('[placeholder="Type to select tag"]').click();
await page.locator('[aria-label="Autocomplete Options"] >> text=Drilling').click();
await page.waitForSelector('[aria-label="Tag"]:has-text("Drilling")');
await page.hover(`button:has-text("Add Tag")`);
await page.locator(`button:has-text("Add Tag")`).click();
await page.locator('[placeholder="Type to select tag"]').click();
await page.locator('[aria-label="Autocomplete Options"] >> text=Driving').click();
await page.waitForSelector('[aria-label="Tag"]:has-text("Driving")');
await addTagAndAwaitNetwork(page, 'Science');
await addTagAndAwaitNetwork(page, 'Drilling');
await addTagAndAwaitNetwork(page, 'Driving');
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
//Partial match for "Science" should only return Science
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Sc');
await expect(page.locator('[aria-label="Search Result"]').first()).toContainText("Science");
await expect(page.locator('[aria-label="Search Result"]').first()).not.toContainText("Driving");
await expect(page.locator('[aria-label="Search Result"]').first()).not.toContainText("Drilling");
//Searching for a tag which does not exist should return an empty result
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Xq');
await expect(page.locator('text=No results found')).toBeVisible();
@@ -275,3 +204,40 @@ function filterNonFetchRequests(requests) {
return (request.resourceType() === 'fetch');
});
}
/**
* Add a tag to a notebook entry by providing a tagName.
* Reduces indeterminism by waiting until all necessary requests are completed.
* @param {import('@playwright/test').Page} page
* @param {string} tagName
*/
async function addTagAndAwaitNetwork(page, tagName) {
await page.hover(`button:has-text("Add Tag")`);
await page.locator(`button:has-text("Add Tag")`).click();
await page.locator('[placeholder="Type to select tag"]').click();
await Promise.all([
// Waits for the next request with the specified url
page.waitForRequest('**/openmct/_all_docs?include_docs=true'),
// Triggers the request
page.locator(`[aria-label="Autocomplete Options"] >> text=${tagName}`).click(),
expect(page.locator(`[aria-label="Tag"]:has-text("${tagName}")`)).toBeVisible()
]);
await page.waitForLoadState('networkidle');
}
/**
* Remove a tag to a notebook entry by providing a tagName.
* Reduces indeterminism by waiting until all necessary requests are completed.
* @param {import('@playwright/test').Page} page
* @param {string} tagName
*/
async function removeTagAndAwaitNetwork(page, tagName) {
await page.hover(`[aria-label="Tag"]:has-text("${tagName}")`);
await Promise.all([
page.locator(`[aria-label="Remove tag ${tagName}"]`).click(),
//With this pattern, we're awaiting the response but asserting on the request payload.
page.waitForResponse(resp => resp.request().postData().includes(`"_deleted":true`) && resp.status() === 201)
]);
await expect(page.locator(`[aria-label="Tag"]:has-text("${tagName}")`)).toBeHidden();
await page.waitForLoadState('networkidle');
}

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,7 +20,7 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
const { test, expect } = require('../../../../pluginFixtures');
const { test, expect, streamToString } = require('../../../../pluginFixtures');
const { openObjectTreeContextMenu, createDomainObjectWithDefaults } = require('../../../../appActions');
const path = require('path');
const nbUtils = require('../../../../helper/notebookUtils');
@@ -169,13 +169,40 @@ test.describe('Restricted Notebook with a page locked and with an embed @addInit
});
test.describe('can export restricted notebook as text', () => {
test.beforeEach(async ({ page }) => {
await startAndAddRestrictedNotebookObject(page);
});
test('basic functionality ', async ({ page }) => {
await nbUtils.enterTextEntry(page, `Foo bar entry`);
// Click on 3 Dot Menu
await page.locator('button[title="More options"]').click();
const downloadPromise = page.waitForEvent('download');
await page.getByRole('menuitem', { name: /Export Notebook as Text/ }).click();
await page.getByRole('button', { name: 'Save' }).click();
const download = await downloadPromise;
const readStream = await download.createReadStream();
const exportedText = await streamToString(readStream);
expect(exportedText).toContain('Foo bar entry');
});
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 tags', async ({ page }) => {});
test.fixme('can export all notebook snapshots', async ({ page }) => {});
});
/**
* @param {import('@playwright/test').Page} page
*/
async function startAndAddRestrictedNotebookObject(page) {
// eslint-disable-next-line no-undef
await page.addInitScript({ path: path.join(__dirname, '../../../../helper/', 'addInitRestrictedNotebook.js') });
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
return createDomainObjectWithDefaults(page, { type: CUSTOM_NAME });
}

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,11 +21,12 @@
*****************************************************************************/
/*
This test suite is dedicated to tests which verify form functionality.
This test suite is dedicated to tests which verify notebook tag functionality.
*/
const { test, expect } = require('../../../../pluginFixtures');
const { createDomainObjectWithDefaults } = require('../../../../appActions');
const { createDomainObjectWithDefaults, selectInspectorTab } = require('../../../../appActions');
const nbUtils = require('../../../../helper/notebookUtils');
/**
* Creates a notebook object and adds an entry.
@@ -33,18 +34,10 @@ const { createDomainObjectWithDefaults } = require('../../../../appActions');
* @param {number} [iterations = 1] - the number of entries to create
*/
async function createNotebookAndEntry(page, iterations = 1) {
//Go to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
const notebook = createDomainObjectWithDefaults(page, { type: 'Notebook' });
for (let iteration = 0; iteration < iterations; iteration++) {
// Create an entry
await page.locator('text=To start a new entry, click here or drag and drop any object').click();
const entryLocator = `[aria-label="Notebook Entry Input"] >> nth = ${iteration}`;
await page.locator(entryLocator).click();
await page.locator(entryLocator).fill(`Entry ${iteration}`);
await page.locator(entryLocator).press('Enter');
await nbUtils.enterTextEntry(page, `Entry ${iteration}`);
}
return notebook;
@@ -57,7 +50,7 @@ async function createNotebookAndEntry(page, iterations = 1) {
*/
async function createNotebookEntryAndTags(page, iterations = 1) {
const notebook = await createNotebookAndEntry(page, iterations);
await page.locator('text=Annotations').click();
await selectInspectorTab(page, 'Annotations');
for (let iteration = 0; iteration < iterations; iteration++) {
// Hover and click "Add Tag" button
@@ -85,10 +78,14 @@ async function createNotebookEntryAndTags(page, iterations = 1) {
}
test.describe('Tagging in Notebooks @addInit', () => {
test.beforeEach(async ({ page }) => {
//Go to baseURL
await page.goto('./', { waitUntil: 'domcontentloaded' });
});
test('Can load tags', async ({ page }) => {
await createNotebookAndEntry(page);
await page.locator('text=Annotations').click();
await selectInspectorTab(page, 'Annotations');
await page.locator('button:has-text("Add Tag")').click();
@@ -111,7 +108,43 @@ test.describe('Tagging in Notebooks @addInit', () => {
await expect(page.locator('[aria-label="Autocomplete Options"]')).not.toContainText("Driving");
await expect(page.locator('[aria-label="Autocomplete Options"]')).toContainText("Drilling");
});
test('Can search for tags', async ({ page }) => {
test('Can add tags with blank entry', async ({ page }) => {
await createDomainObjectWithDefaults(page, { type: 'Notebook' });
await selectInspectorTab(page, 'Annotations');
await nbUtils.enterTextEntry(page, '');
await page.hover(`button:has-text("Add Tag")`);
await page.locator(`button:has-text("Add Tag")`).click();
// Click inside the tag search input
await page.locator('[placeholder="Type to select tag"]').click();
// Select the "Driving" tag
await page.locator('[aria-label="Autocomplete Options"] >> text=Driving').click();
await expect(page.locator('[aria-label="Notebook Entry"]')).toContainText("Driving");
});
test('Can cancel adding tags', async ({ page }) => {
await createNotebookAndEntry(page);
await selectInspectorTab(page, 'Annotations');
// Test canceling adding a tag after we click "Type to select tag"
await page.locator('button:has-text("Add Tag")').click();
await page.locator('[placeholder="Type to select tag"]').click();
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
await expect(page.locator('button:has-text("Add Tag")')).toBeVisible();
// Test canceling adding a tag after we just click "Add Tag"
await page.locator('button:has-text("Add Tag")').click();
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
await expect(page.locator('button:has-text("Add Tag")')).toBeVisible();
});
test('Can search for tags and preview works properly', async ({ page }) => {
await createNotebookEntryAndTags(page);
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('sc');
@@ -126,13 +159,26 @@ test.describe('Tagging in Notebooks @addInit', () => {
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Xq');
await expect(page.locator('text=No results found')).toBeVisible();
await createDomainObjectWithDefaults(page, {
type: 'Display Layout'
});
// Go back into edit mode for the display layout
await page.locator('button[title="Edit"]').click();
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Sc');
await expect(page.locator('[aria-label="Search Result"]')).toContainText("Science");
await page.getByText('Entry 0').click();
await expect(page.locator('.js-preview-window')).toBeVisible();
});
test('Can delete tags', async ({ page }) => {
await createNotebookEntryAndTags(page);
// Delete Driving
await page.hover('[aria-label="Tag"]:has-text("Driving")');
await page.locator('[aria-label="Tag"]:has-text("Driving") ~ .c-completed-tag-deletion').click();
await page.locator('[aria-label="Remove tag Driving"]').click();
await expect(page.locator('[aria-label="Tags Inspector"]')).toContainText("Science");
await expect(page.locator('[aria-label="Tags Inspector"]')).not.toContainText("Driving");
@@ -168,7 +214,7 @@ test.describe('Tagging in Notebooks @addInit', () => {
await page.locator('button[title="More options"]').click();
await page.locator('li[title="Remove this object from its containing object."]').click();
await page.locator('button:has-text("OK")').click();
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Unnamed');
await expect(page.locator('text=No results found')).toBeVisible();
@@ -179,37 +225,13 @@ test.describe('Tagging in Notebooks @addInit', () => {
});
test('Tags persist across reload', async ({ page }) => {
//Go to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
const clock = await createDomainObjectWithDefaults(page, { type: 'Clock' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
const ITERATIONS = 4;
const notebook = await createNotebookEntryAndTags(page, ITERATIONS);
await page.goto(notebook.url);
for (let iteration = 0; iteration < ITERATIONS; iteration++) {
const entryLocator = `[aria-label="Notebook Entry"] >> nth = ${iteration}`;
await expect(page.locator(entryLocator)).toContainText("Science");
await expect(page.locator(entryLocator)).toContainText("Driving");
}
await Promise.all([
page.waitForNavigation(),
page.goto('./#/browse/mine?hideTree=false'),
page.click('.c-disclosure-triangle')
]);
const treePane = page.getByRole('tree', {
name: 'Main Tree'
});
// Click Clock
await treePane.getByRole('treeitem', {
name: clock.name
}).click();
// Click Notebook
await page.getByRole('treeitem', {
name: notebook.name
}).click();
// Verify tags are present
for (let iteration = 0; iteration < ITERATIONS; iteration++) {
const entryLocator = `[aria-label="Notebook Entry"] >> nth = ${iteration}`;
await expect(page.locator(entryLocator)).toContainText("Science");
@@ -217,14 +239,9 @@ test.describe('Tagging in Notebooks @addInit', () => {
}
//Reload Page
await Promise.all([
page.reload(),
page.waitForLoadState('networkidle')
]);
// Click Notebook
await page.click(`text="${notebook.name}"`);
await page.reload({ waitUntil: 'domcontentloaded' });
// Verify tags persist across reload
for (let iteration = 0; iteration < ITERATIONS; iteration++) {
const entryLocator = `[aria-label="Notebook Entry"] >> nth = ${iteration}`;
await expect(page.locator(entryLocator)).toContainText("Science");
@@ -234,8 +251,7 @@ test.describe('Tagging in Notebooks @addInit', () => {
test('Can cancel adding a tag', async ({ page }) => {
await createNotebookAndEntry(page);
// Click on Annotations tab
await page.locator('.c-inspector__tab', { hasText: "Annotations" }).click();
await selectInspectorTab(page, 'Annotations');
// Click on the "Add Tag" button
await page.locator('button:has-text("Add Tag")').click();

View File

@@ -44,7 +44,7 @@ test.describe('Operator Status', () => {
await page.addInitScript({ path: path.join(__dirname, '../../../../helper/', 'addInitExampleUser.js')});
// eslint-disable-next-line no-undef
await page.addInitScript({ path: path.join(__dirname, '../../../../helper/', 'addInitOperatorStatus.js')});
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
});
// verify that operator status is visible

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -24,6 +24,7 @@
Testsuite for plot autoscale.
*/
const { selectInspectorTab } = require('../../../../appActions');
const { test, expect } = require('../../../../pluginFixtures');
test.use({
viewport: {
@@ -39,7 +40,7 @@ test.describe('Autoscale', () => {
//This is necessary due to the size of the test suite.
test.slow();
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
await setTimeRange(page);
@@ -50,6 +51,7 @@ test.describe('Autoscale', () => {
// enter edit mode
await page.click('button[title="Edit"]');
await selectInspectorTab(page, 'Config');
await turnOffAutoscale(page);
await setUserDefinedMinAndMax(page, '-2', '2');
@@ -71,6 +73,7 @@ test.describe('Autoscale', () => {
const canvas = page.locator('canvas').nth(1);
await canvas.hover({trial: true});
await expect(page.locator('.js-series-data-loaded')).toBeVisible();
expect.soft(await canvas.screenshot()).toMatchSnapshot('autoscale-canvas-prepan.png', { animations: 'disabled' });
@@ -170,7 +173,7 @@ async function createSinewaveOverlayPlot(page, myItemsFolderName) {
*/
async function turnOffAutoscale(page) {
// uncheck autoscale
await page.getByRole('listitem').filter({ hasText: 'Auto scale' }).getByRole('checkbox').uncheck();
await page.getByRole('checkbox', { name: 'Auto scale' }).uncheck();
}
/**
@@ -180,14 +183,9 @@ async function turnOffAutoscale(page) {
*/
async function setUserDefinedMinAndMax(page, min, max) {
// set minimum value
const minRangeInput = page.getByRole('listitem').filter({ hasText: 'Minimum Value' }).locator('input[type="number"]');
await minRangeInput.click();
await minRangeInput.fill(min);
await page.getByRole('spinbutton').first().fill(min);
// set maximum value
const maxRangeInput = page.getByRole('listitem').filter({ hasText: 'Maximum Value' }).locator('input[type="number"]');
await maxRangeInput.click();
await maxRangeInput.fill(max);
await page.getByRole('spinbutton').nth(1).fill(max);
}
/**

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -26,6 +26,8 @@ necessarily be used for reference when writing new tests in this area.
*/
const { test, expect } = require('../../../../pluginFixtures');
const { selectInspectorTab } = require('../../../../appActions');
test.describe('Log plot tests', () => {
test('Log Plot ticks are functionally correct in regular and log mode and after refresh', async ({ page, openmctConfig }) => {
const { myItemsFolderName } = openmctConfig;
@@ -36,6 +38,7 @@ test.describe('Log plot tests', () => {
await makeOverlayPlot(page, myItemsFolderName);
await testRegularTicks(page);
await enableEditMode(page);
await selectInspectorTab(page, 'Config');
await enableLogMode(page);
await testLogTicks(page);
await disableLogMode(page);
@@ -73,7 +76,7 @@ test.describe('Log plot tests', () => {
*/
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: 'networkidle' });
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.
@@ -144,7 +147,7 @@ async function makeOverlayPlot(page, myItemsFolderName) {
* @param {import('@playwright/test').Page} page
*/
async function testRegularTicks(page) {
const yTicks = await 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.nth(0)).toHaveText('-2');
await expect(yTicks.nth(1)).toHaveText('0');
@@ -159,7 +162,7 @@ async function testRegularTicks(page) {
* @param {import('@playwright/test').Page} page
*/
async function testLogTicks(page) {
const yTicks = await 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.nth(0)).toHaveText('-2.98');
await expect(yTicks.nth(1)).toHaveText('-1.51');
@@ -177,25 +180,24 @@ async function testLogTicks(page) {
*/
async function enableEditMode(page) {
// turn on edit mode
await page.locator('text=Unnamed Overlay Plot Snapshot >> button').nth(3).click();
await expect(await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1)).toBeVisible();
await page.getByRole('button', { name: 'Edit' }).click();
await expect(page.getByRole('button', { name: 'Save' })).toBeVisible();
}
/**
* @param {import('@playwright/test').Page} page
*/
async function enableLogMode(page) {
// turn on log mode
await page.getByRole('listitem').filter({ hasText: 'Log mode' }).getByRole('checkbox').check();
// await page.locator('text=Y Axis Label Log mode Auto scale Padding >> input[type="checkbox"]').first().check();
await expect(page.getByRole('checkbox', { name: 'Log mode' })).not.toBeChecked();
await page.getByRole('checkbox', { name: 'Log mode' }).check();
}
/**
* @param {import('@playwright/test').Page} page
*/
async function disableLogMode(page) {
// turn off log mode
await page.getByRole('listitem').filter({ hasText: 'Log mode' }).getByRole('checkbox').uncheck();
await expect(page.getByRole('checkbox', { name: 'Log mode' })).toBeChecked();
await page.getByRole('checkbox', { name: 'Log mode' }).uncheck();
}
/**

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -84,7 +84,7 @@ test.describe('Handle missing object for plots', () => {
*/
async function makeStackedPlot(page, myItemsFolderName) {
// fresh page with time range from 2022-03-29 22:00:00.000Z to 2022-03-29 22:00:30.000Z
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
// create stacked plot
await page.locator('button.c-create-button').click();

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -26,11 +26,14 @@ necessarily be used for reference when writing new tests in this area.
*/
const { test, expect } = require('../../../../pluginFixtures');
const { createDomainObjectWithDefaults } = require('../../../../appActions');
const { createDomainObjectWithDefaults, selectInspectorTab } = require('../../../../appActions');
test.describe('Overlay Plot', () => {
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'domcontentloaded' });
});
test('Plot legend color is in sync with plot series color', async ({ page }) => {
await page.goto('/', { waitUntil: 'networkidle' });
const overlayPlot = await createDomainObjectWithDefaults(page, {
type: "Overlay Plot"
});
@@ -42,6 +45,8 @@ test.describe('Overlay Plot', () => {
await page.goto(overlayPlot.url);
await selectInspectorTab(page, 'Config');
// navigate to plot series color palette
await page.click('.l-browse-bar__actions__edit');
await page.locator('li.c-tree__item.menus-to-left .c-disclosure-triangle').click();
@@ -56,69 +61,226 @@ test.describe('Overlay Plot', () => {
expect(color).toBe('rgb(255, 166, 61)');
});
test('The elements pool supports dragging series into multiple y-axis buckets', async ({ page }) => {
await page.goto('/', { waitUntil: 'networkidle' });
test('Limit lines persist when series is moved to another Y Axis and on refresh', async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/6338'
});
// Create an Overlay Plot with a default SWG
const overlayPlot = await createDomainObjectWithDefaults(page, {
type: "Overlay Plot"
});
await createDomainObjectWithDefaults(page, {
const swgA = await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
name: 'swg a',
parent: overlayPlot.uuid
});
await createDomainObjectWithDefaults(page, {
await page.goto(overlayPlot.url);
// Assert that no limit lines are shown by default
await page.waitForSelector('.js-limit-area', { state: 'attached' });
expect(await page.locator('.c-plot-limit-line').count()).toBe(0);
// Enter edit mode
await page.click('button[title="Edit"]');
// Expand the "Sine Wave Generator" plot series options and enable limit lines
await selectInspectorTab(page, 'Config');
await page.getByRole('list', { name: 'Plot Series Properties' }).locator('span').first().click();
await page.getByRole('list', { name: 'Plot Series Properties' }).locator('[title="Display limit lines"]~div input').check();
await assertLimitLinesExistAndAreVisible(page);
// Save (exit edit mode)
await page.locator('button[title="Save"]').click();
await page.locator('li[title="Save and Finish Editing"]').click();
await assertLimitLinesExistAndAreVisible(page);
await page.reload();
await assertLimitLinesExistAndAreVisible(page);
// Enter edit mode
await page.click('button[title="Edit"]');
await selectInspectorTab(page, 'Elements');
// Drag Sine Wave Generator series from Y Axis 1 into Y Axis 2
await page.locator(`#inspector-elements-tree >> text=${swgA.name}`).dragTo(page.locator('[aria-label="Element Item Group Y Axis 2"]'));
await assertLimitLinesExistAndAreVisible(page);
// Save (exit edit mode)
await page.locator('button[title="Save"]').click();
await page.locator('li[title="Save and Finish Editing"]').click();
await assertLimitLinesExistAndAreVisible(page);
await page.reload();
await assertLimitLinesExistAndAreVisible(page);
});
test('The elements pool supports dragging series into multiple y-axis buckets', async ({ page }) => {
const overlayPlot = await createDomainObjectWithDefaults(page, {
type: "Overlay Plot"
});
const swgA = await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
name: 'swg b',
parent: overlayPlot.uuid
});
await createDomainObjectWithDefaults(page, {
const swgB = await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
name: 'swg c',
parent: overlayPlot.uuid
});
await createDomainObjectWithDefaults(page, {
const swgC = await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
name: 'swg d',
parent: overlayPlot.uuid
});
await createDomainObjectWithDefaults(page, {
const swgD = await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
parent: overlayPlot.uuid
});
const swgE = await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
name: 'swg e',
parent: overlayPlot.uuid
});
await page.goto(overlayPlot.url);
await page.click('button[title="Edit"]');
// Expand the elements pool vertically
await page.locator('.l-pane__handle').nth(2).hover({ trial: true });
await page.mouse.down();
await page.mouse.move(0, 100);
await page.mouse.up();
await selectInspectorTab(page, 'Elements');
// Drag swg a, c, e into Y Axis 2
await page.locator('#inspector-elements-tree >> text=swg a').dragTo(page.locator('[aria-label="Element Item Group Y Axis 2"]'));
await page.locator('#inspector-elements-tree >> text=swg c').dragTo(page.locator('[aria-label="Element Item Group Y Axis 2"]'));
await page.locator('#inspector-elements-tree >> text=swg e').dragTo(page.locator('[aria-label="Element Item Group Y Axis 2"]'));
await page.locator(`#inspector-elements-tree >> text=${swgA.name}`).dragTo(page.locator('[aria-label="Element Item Group Y Axis 2"]'));
await page.locator(`#inspector-elements-tree >> text=${swgC.name}`).dragTo(page.locator('[aria-label="Element Item Group Y Axis 2"]'));
await page.locator(`#inspector-elements-tree >> text=${swgE.name}`).dragTo(page.locator('[aria-label="Element Item Group Y Axis 2"]'));
// Drag swg b into Y Axis 3
await page.locator('#inspector-elements-tree >> text=swg b').dragTo(page.locator('[aria-label="Element Item Group Y Axis 3"]'));
// Assert that Y Axis 1 and Y Axis 2 property groups are visible only
await selectInspectorTab(page, 'Config');
const yAxis1PropertyGroup = page.locator('[aria-label="Y Axis Properties"]');
const yAxis2PropertyGroup = page.locator('[aria-label="Y Axis 2 Properties"]');
const yAxis3PropertyGroup = page.locator('[aria-label="Y Axis 3 Properties"]');
await expect(yAxis1PropertyGroup).toBeVisible();
await expect(yAxis2PropertyGroup).toBeVisible();
await expect(yAxis3PropertyGroup).toBeHidden();
const yAxis1Group = page.getByLabel("Y Axis 1");
const yAxis2Group = page.getByLabel("Y Axis 2");
const yAxis3Group = page.getByLabel("Y Axis 3");
await selectInspectorTab(page, 'Elements');
// Drag swg b into Y Axis 3
await page.locator(`#inspector-elements-tree >> text=${swgB.name}`).dragTo(page.locator('[aria-label="Element Item Group Y Axis 3"]'));
// Assert that all Y Axis property groups are visible
await selectInspectorTab(page, 'Config');
await expect(yAxis1PropertyGroup).toBeVisible();
await expect(yAxis2PropertyGroup).toBeVisible();
await expect(yAxis3PropertyGroup).toBeVisible();
// Verify that the elements are in the correct buckets and in the correct order
expect(yAxis1Group.getByRole('listitem', { name: 'swg d' })).toBeTruthy();
expect(yAxis1Group.getByRole('listitem').nth(0).getByText('swg d')).toBeTruthy();
expect(yAxis2Group.getByRole('listitem', { name: 'swg e' })).toBeTruthy();
expect(yAxis2Group.getByRole('listitem').nth(0).getByText('swg e')).toBeTruthy();
expect(yAxis2Group.getByRole('listitem', { name: 'swg c' })).toBeTruthy();
expect(yAxis2Group.getByRole('listitem').nth(1).getByText('swg c')).toBeTruthy();
expect(yAxis2Group.getByRole('listitem', { name: 'swg a' })).toBeTruthy();
expect(yAxis2Group.getByRole('listitem').nth(2).getByText('swg a')).toBeTruthy();
expect(yAxis3Group.getByRole('listitem', { name: 'swg b' })).toBeTruthy();
expect(yAxis3Group.getByRole('listitem').nth(0).getByText('swg b')).toBeTruthy();
await selectInspectorTab(page, 'Elements');
expect(yAxis1Group.getByRole('listitem', { name: swgD.name })).toBeTruthy();
expect(yAxis1Group.getByRole('listitem').nth(0).getByText(swgD.name)).toBeTruthy();
expect(yAxis2Group.getByRole('listitem', { name: swgE.name })).toBeTruthy();
expect(yAxis2Group.getByRole('listitem').nth(0).getByText(swgE.name)).toBeTruthy();
expect(yAxis2Group.getByRole('listitem', { name: swgC.name })).toBeTruthy();
expect(yAxis2Group.getByRole('listitem').nth(1).getByText(swgC.name)).toBeTruthy();
expect(yAxis2Group.getByRole('listitem', { name: swgA.name })).toBeTruthy();
expect(yAxis2Group.getByRole('listitem').nth(2).getByText(swgA.name)).toBeTruthy();
expect(yAxis3Group.getByRole('listitem', { name: swgB.name })).toBeTruthy();
expect(yAxis3Group.getByRole('listitem').nth(0).getByText(swgB.name)).toBeTruthy();
});
test('Clicking on an item in the elements pool brings up the plot preview with data points', async ({ page }) => {
const overlayPlot = await createDomainObjectWithDefaults(page, {
type: "Overlay Plot"
});
const swgA = await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
parent: overlayPlot.uuid
});
await page.goto(overlayPlot.url);
// Wait for plot series data to load and be drawn
await expect(page.locator('.js-series-data-loaded')).toBeVisible();
await page.click('button[title="Edit"]');
await selectInspectorTab(page, 'Elements');
await page.locator(`#inspector-elements-tree >> text=${swgA.name}`).click();
// Wait for "View Large" plot series data to load and be drawn
await expect(page.locator('.c-overlay .js-series-data-loaded')).toBeVisible();
const plotPixelSize = await getCanvasPixelsWithData(page);
expect(plotPixelSize).toBeGreaterThan(0);
});
});
/**
* @param {import('@playwright/test').Page} page
*/
async function getCanvasPixelsWithData(page) {
const getTelemValuePromise = new Promise(resolve => page.exposeFunction('getCanvasValue', resolve));
await page.evaluate(() => {
// The document canvas is where the plot points and lines are drawn.
// The only way to access the canvas is using document (using page.evaluate)
let data;
let canvas;
let ctx;
canvas = document.querySelector('.js-overlay canvas');
ctx = canvas.getContext('2d');
data = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
const imageDataValues = Object.values(data);
let plotPixels = [];
// Each pixel consists of four values within the ImageData.data array. The for loop iterates by multiples of four.
// The values associated with each pixel are R (red), G (green), B (blue), and A (alpha), in that order.
for (let i = 0; i < imageDataValues.length;) {
if (imageDataValues[i] > 0) {
plotPixels.push({
startIndex: i,
endIndex: i + 3,
value: `rgb(${imageDataValues[i]}, ${imageDataValues[i + 1]}, ${imageDataValues[i + 2]}, ${imageDataValues[i + 3]})`
});
}
i = i + 4;
}
window.getCanvasValue(plotPixels.length);
});
return getTelemValuePromise;
}
/**
*
* @param {import('@playwright/test').Page} page
*/
async function assertLimitLinesExistAndAreVisible(page) {
// Wait for plot series data to load
await expect(page.locator('.js-series-data-loaded')).toBeVisible();
// Wait for limit lines to be created
await page.waitForSelector('.js-limit-area', { state: 'attached' });
const limitLineCount = await page.locator('.c-plot-limit-line').count();
// There should be 10 limit lines created by default
expect(await page.locator('.c-plot-limit-line').count()).toBe(10);
for (let i = 0; i < limitLineCount; i++) {
await expect(page.locator('.c-plot-limit-line').nth(i)).toBeVisible();
}
}

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -25,6 +25,7 @@ Tests to verify log plot functionality. Note this test suite if very much under
necessarily be used for reference when writing new tests in this area.
*/
const { selectInspectorTab } = require('../../../../appActions');
const { test, expect } = require('../../../../pluginFixtures');
test.describe('Legend color in sync with plot color', () => {
@@ -33,6 +34,8 @@ test.describe('Legend color in sync with plot color', () => {
// navigate to plot series color palette
await page.click('.l-browse-bar__actions__edit');
await selectInspectorTab(page, 'Config');
await page.locator('li.c-tree__item.menus-to-left .c-disclosure-triangle').click();
await page.locator('.c-click-swatch--menu').click();
await page.locator('.c-palette__item[style="background: rgb(255, 166, 61);"]').click();

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -33,7 +33,7 @@ test.describe('Plot Integrity Testing @unstable', () => {
test.beforeEach(async ({ page }) => {
//Open a browser, navigate to the main page, and wait until all networkevents to resolve
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
sineWaveGeneratorObject = await createDomainObjectWithDefaults(page, { type: 'Sine Wave Generator' });
});

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -25,7 +25,7 @@
*/
const { test, expect } = require('../../../../pluginFixtures');
const { createDomainObjectWithDefaults } = require('../../../../appActions');
const { createDomainObjectWithDefaults, selectInspectorTab } = require('../../../../appActions');
const uuid = require('uuid').v4;
test.describe('Scatter Plot', () => {
@@ -33,15 +33,15 @@ test.describe('Scatter Plot', () => {
test.beforeEach(async ({ page }) => {
// Open a browser, navigate to the main page, and wait until all networkevents to resolve
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Create the Scatter Plot
scatterPlot = await createDomainObjectWithDefaults(page, { type: 'Scatter Plot' });
});
test('Can add and remove telemetry sources', async ({ page }) => {
const editButtonLocator = page.locator('button[title="Edit"]');
const saveButtonLocator = page.locator('button[title="Save"]');
const editButton = page.locator('button[title="Edit"]');
const saveButton = page.locator('button[title="Save"]');
// Create a sine wave generator within the scatter plot
const swg1 = await createDomainObjectWithDefaults(page, {
@@ -53,9 +53,10 @@ test.describe('Scatter Plot', () => {
// Navigate to the scatter plot and verify that
// the SWG appears in the elements pool
await page.goto(scatterPlot.url);
await editButtonLocator.click();
await editButton.click();
await selectInspectorTab(page, 'Elements');
await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg1.name}`)).toBeVisible();
await saveButtonLocator.click();
await saveButton.click();
await page.locator('li[title="Save and Finish Editing"]').click();
// Create another sine wave generator within the scatter plot
@@ -72,10 +73,13 @@ test.describe('Scatter Plot', () => {
// Navigate to the scatter plot and verify that the new SWG
// appears in the elements pool and the old one is gone
await page.goto(scatterPlot.url);
await editButtonLocator.click();
await editButton.click();
// Click the "Elements" tab
await selectInspectorTab(page, 'Elements');
await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg1.name}`)).toBeHidden();
await expect.soft(page.locator(`#inspector-elements-tree >> text=${swg2.name}`)).toBeVisible();
await saveButtonLocator.click();
await saveButton.click();
// Right click on the new SWG in the elements pool and delete it
await page.locator(`#inspector-elements-tree >> text=${swg2.name}`).click({

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -26,7 +26,7 @@ necessarily be used for reference when writing new tests in this area.
*/
const { test, expect } = require('../../../../pluginFixtures');
const { createDomainObjectWithDefaults } = require('../../../../appActions');
const { createDomainObjectWithDefaults, selectInspectorTab } = require('../../../../appActions');
test.describe('Stacked Plot', () => {
let stackedPlot;
@@ -35,8 +35,9 @@ test.describe('Stacked Plot', () => {
let swgC;
test.beforeEach(async ({ page }) => {
//Open a browser, navigate to the main page, and wait until all networkevents to resolve
await page.goto('/', { waitUntil: 'networkidle' });
// Open a browser, navigate to the main page, and wait until all networkevents to resolve
await page.goto('./', { waitUntil: 'domcontentloaded' });
stackedPlot = await createDomainObjectWithDefaults(page, {
type: "Stacked Plot"
});
@@ -64,11 +65,7 @@ test.describe('Stacked Plot', () => {
await page.click('button[title="Edit"]');
// Expand the elements pool vertically
await page.locator('.l-pane__handle').nth(2).hover({ trial: true });
await page.mouse.down();
await page.mouse.move(0, 100);
await page.mouse.up();
await selectInspectorTab(page, 'Elements');
await swgBElementsPoolItem.click({ button: 'right' });
await page.getByRole('menuitem').filter({ hasText: /Remove/ }).click();
@@ -82,15 +79,66 @@ test.describe('Stacked Plot', () => {
await expect(swgCElementsPoolItem).toHaveCount(1);
});
test('Can reorder Stacked Plot items', async ({ page }) => {
const swgAElementsPoolItem = page.locator('#inspector-elements-tree').locator('.c-object-label', { hasText: swgA.name });
const swgBElementsPoolItem = page.locator('#inspector-elements-tree').locator('.c-object-label', { hasText: swgB.name });
const swgCElementsPoolItem = page.locator('#inspector-elements-tree').locator('.c-object-label', { hasText: swgC.name });
await page.goto(stackedPlot.url);
await page.click('button[title="Edit"]');
await selectInspectorTab(page, 'Elements');
const stackedPlotItem1 = page.locator('.c-plot--stacked-container').nth(0);
const stackedPlotItem2 = page.locator('.c-plot--stacked-container').nth(1);
const stackedPlotItem3 = page.locator('.c-plot--stacked-container').nth(2);
// assert initial plot order - [swgA, swgB, swgC]
await expect(stackedPlotItem1).toHaveAttribute('aria-label', `Stacked Plot Item ${swgA.name}`);
await expect(stackedPlotItem2).toHaveAttribute('aria-label', `Stacked Plot Item ${swgB.name}`);
await expect(stackedPlotItem3).toHaveAttribute('aria-label', `Stacked Plot Item ${swgC.name}`);
// Drag and drop to reorder - [swgB, swgA, swgC]
await swgBElementsPoolItem.dragTo(swgAElementsPoolItem);
// assert plot order after reorder - [swgB, swgA, swgC]
await expect(stackedPlotItem1).toHaveAttribute('aria-label', `Stacked Plot Item ${swgB.name}`);
await expect(stackedPlotItem2).toHaveAttribute('aria-label', `Stacked Plot Item ${swgA.name}`);
await expect(stackedPlotItem3).toHaveAttribute('aria-label', `Stacked Plot Item ${swgC.name}`);
// Drag and drop to reorder - [swgB, swgC, swgA]
await swgCElementsPoolItem.dragTo(swgAElementsPoolItem);
// assert plot order after second reorder - [swgB, swgC, swgA]
await expect(stackedPlotItem1).toHaveAttribute('aria-label', `Stacked Plot Item ${swgB.name}`);
await expect(stackedPlotItem2).toHaveAttribute('aria-label', `Stacked Plot Item ${swgC.name}`);
await expect(stackedPlotItem3).toHaveAttribute('aria-label', `Stacked Plot Item ${swgA.name}`);
// collapse inspector
await page.locator('.l-shell__pane-inspector .l-pane__collapse-button').click();
// Save (exit edit mode)
await page.locator('button[title="Save"]').click();
await page.locator('li[title="Save and Finish Editing"]').click();
// assert plot order persists after save - [swgB, swgC, swgA]
await expect(stackedPlotItem1).toHaveAttribute('aria-label', `Stacked Plot Item ${swgB.name}`);
await expect(stackedPlotItem2).toHaveAttribute('aria-label', `Stacked Plot Item ${swgC.name}`);
await expect(stackedPlotItem3).toHaveAttribute('aria-label', `Stacked Plot Item ${swgA.name}`);
});
test('Selecting a child plot while in browse and edit modes shows its properties in the inspector', async ({ page }) => {
await page.goto(stackedPlot.url);
await selectInspectorTab(page, 'Config');
// Click on the 1st plot
await page.locator(`[aria-label="Stacked Plot Item ${swgA.name}"] canvas`).nth(1).click();
// Assert that the inspector shows the Y Axis properties for swgA
await expect(page.locator('[aria-label="Plot Series Properties"] >> h2')).toContainText("Plot Series");
await expect(page.getByRole('list', { name: "Y Axis Properties" }).locator("h2")).toContainText("Y Axis");
await expect(page.getByRole('heading', { name: "Y Axis" })).toBeVisible();
await expect(page.locator('[aria-label="Plot Series Properties"] .c-object-label')).toContainText(swgA.name);
// Click on the 2nd plot
@@ -98,7 +146,7 @@ test.describe('Stacked Plot', () => {
// Assert that the inspector shows the Y Axis properties for swgB
await expect(page.locator('[aria-label="Plot Series Properties"] >> h2')).toContainText("Plot Series");
await expect(page.getByRole('list', { name: "Y Axis Properties" }).locator("h2")).toContainText("Y Axis");
await expect(page.getByRole('heading', { name: 'Y Axis' })).toBeVisible();
await expect(page.locator('[aria-label="Plot Series Properties"] .c-object-label')).toContainText(swgB.name);
// Click on the 3rd plot
@@ -106,18 +154,20 @@ test.describe('Stacked Plot', () => {
// Assert that the inspector shows the Y Axis properties for swgC
await expect(page.locator('[aria-label="Plot Series Properties"] >> h2')).toContainText("Plot Series");
await expect(page.getByRole('list', { name: "Y Axis Properties" }).locator("h2")).toContainText("Y Axis");
await expect(page.getByRole('heading', { name: 'Y Axis' })).toBeVisible();
await expect(page.locator('[aria-label="Plot Series Properties"] .c-object-label')).toContainText(swgC.name);
// Go into edit mode
await page.click('button[title="Edit"]');
await selectInspectorTab(page, 'Config');
// Click on canvas for the 1st plot
await page.locator(`[aria-label="Stacked Plot Item ${swgA.name}"]`).click();
// Assert that the inspector shows the Y Axis properties for swgA
await expect(page.locator('[aria-label="Plot Series Properties"] >> h2')).toContainText("Plot Series");
await expect(page.getByRole('list', { name: "Y Axis Properties" }).locator("h2")).toContainText("Y Axis");
await expect(page.getByRole('heading', { name: 'Y Axis' })).toBeVisible();
await expect(page.locator('[aria-label="Plot Series Properties"] .c-object-label')).toContainText(swgA.name);
//Click on canvas for the 2nd plot
@@ -125,7 +175,7 @@ test.describe('Stacked Plot', () => {
// Assert that the inspector shows the Y Axis properties for swgB
await expect(page.locator('[aria-label="Plot Series Properties"] >> h2')).toContainText("Plot Series");
await expect(page.getByRole('list', { name: "Y Axis Properties" }).locator("h2")).toContainText("Y Axis");
await expect(page.getByRole('heading', { name: 'Y Axis' })).toBeVisible();
await expect(page.locator('[aria-label="Plot Series Properties"] .c-object-label')).toContainText(swgB.name);
//Click on canvas for the 3rd plot
@@ -133,7 +183,7 @@ test.describe('Stacked Plot', () => {
// Assert that the inspector shows the Y Axis properties for swgC
await expect(page.locator('[aria-label="Plot Series Properties"] >> h2')).toContainText("Plot Series");
await expect(page.getByRole('list', { name: "Y Axis Properties" }).locator("h2")).toContainText("Y Axis");
await expect(page.getByRole('heading', { name: 'Y Axis' })).toBeVisible();
await expect(page.locator('[aria-label="Plot Series Properties"] .c-object-label')).toContainText(swgC.name);
});
});

View File

@@ -0,0 +1,267 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*
Tests to verify plot tagging functionality.
*/
const { test, expect } = require('../../../../pluginFixtures');
const { createDomainObjectWithDefaults, setRealTimeMode, setFixedTimeMode } = require('../../../../appActions');
test.describe('Plot Tagging', () => {
/**
* Given a canvas and a set of points, tags the points on the canvas.
* @param {import('@playwright/test').Page} page
* @param {HTMLCanvasElement} canvas a telemetry item with a plot
* @param {Number} xEnd a telemetry item with a plot
* @param {Number} yEnd a telemetry item with a plot
* @returns {Promise}
*/
async function createTags({page, canvas, xEnd, yEnd}) {
await canvas.hover({trial: true});
//Alt+Shift Drag Start to select some points to tag
await page.keyboard.down('Alt');
await page.keyboard.down('Shift');
await canvas.dragTo(canvas, {
sourcePosition: {
x: 1,
y: 1
},
targetPosition: {
x: xEnd,
y: yEnd
}
});
//Alt Drag End
await page.keyboard.up('Alt');
await page.keyboard.up('Shift');
//Wait for canvas to stablize.
await canvas.hover({trial: true});
// add some tags
await page.getByText('Annotations').click();
await page.getByRole('button', { name: /Add Tag/ }).click();
await page.getByPlaceholder('Type to select tag').click();
await page.getByText('Driving').click();
await page.getByRole('button', { name: /Add Tag/ }).click();
await page.getByPlaceholder('Type to select tag').click();
await page.getByText('Science').click();
}
/**
* Given a telemetry item (e.g., a Sine Wave Generator) with a plot, tests that the plot can be tagged.
* @param {import('@playwright/test').Page} page
* @param {import('../../../../appActions').CreatedObjectInfo} telemetryItem a telemetry item with a plot
* @returns {Promise}
*/
async function testTelemetryItem(page, telemetryItem) {
// Check that telemetry item also received the tag
await page.goto(telemetryItem.url);
await expect(page.getByText('No tags to display for this item')).toBeVisible();
const canvas = page.locator('canvas').nth(1);
//Wait for canvas to stablize.
await canvas.hover({trial: true});
// click on the tagged plot point
await canvas.click({
position: {
x: 325,
y: 377
}
});
await expect(page.getByText('Science')).toBeVisible();
await expect(page.getByText('Driving')).toBeHidden();
}
/**
* Given a page, tests that tags are searchable, deletable, and persist across reloads.
* @param {import('@playwright/test').Page} page
* @returns {Promise}
*/
async function basicTagsTests(page) {
// Search for Driving
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
// Clicking elsewhere should cause annotation selection to be cleared
await expect(page.getByText('No tags to display for this item')).toBeVisible();
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('driv');
// click on the search result
await page.getByRole('searchbox', { name: 'OpenMCT Search' }).getByText(/Sine Wave/).first().click();
// Delete Driving
await page.hover('[aria-label="Tag"]:has-text("Driving")');
await page.locator('[aria-label="Remove tag Driving"]').click();
// Search for Science
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('sc');
await expect(page.locator('[aria-label="Search Result"]').nth(0)).toContainText("Science");
await expect(page.locator('[aria-label="Search Result"]').nth(0)).not.toContainText("Drilling");
// Search for Driving
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('driv');
await expect(page.getByText('No results found')).toBeVisible();
//Reload Page
await Promise.all([
page.reload(),
page.waitForLoadState('networkidle')
]);
// wait for plots to load
await expect(page.locator('.js-series-data-loaded')).toBeVisible();
await page.getByText('Annotations').click();
await expect(page.getByText('No tags to display for this item')).toBeVisible();
const canvas = page.locator('canvas').nth(1);
// click on the tagged plot point
await canvas.click({
position: {
x: 100,
y: 100
}
});
await expect(page.getByText('Science')).toBeVisible();
await expect(page.getByText('Driving')).toBeHidden();
}
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'domcontentloaded' });
});
test('Tags work with Overlay Plots', async ({ page }) => {
//Test.slow decorator is currently broken. Needs to be fixed in https://github.com/nasa/openmct/issues/5374
test.slow();
const overlayPlot = await createDomainObjectWithDefaults(page, {
type: "Overlay Plot"
});
const alphaSineWave = await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
name: "Alpha Sine Wave",
parent: overlayPlot.uuid
});
await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
name: "Beta Sine Wave",
parent: overlayPlot.uuid
});
await page.goto(overlayPlot.url);
let canvas = page.locator('canvas').nth(1);
// Switch to real-time mode
// Adding tags should pause the plot
await setRealTimeMode(page);
await createTags({
page,
canvas,
xEnd: 700,
yEnd: 480
});
await setFixedTimeMode(page);
// changing to fixed time mode rebuilds canvas?
canvas = page.locator('canvas').nth(1);
await basicTagsTests(page);
await testTelemetryItem(page, alphaSineWave);
// set to real time mode
await setRealTimeMode(page);
// Search for Science
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('sc');
// click on the search result
await page.getByRole('searchbox', { name: 'OpenMCT Search' }).getByText('Alpha Sine Wave').first().click();
// wait for plots to load
await expect(page.locator('.js-series-data-loaded')).toBeVisible();
// expect plot to be paused
await expect(page.locator('[title="Resume displaying real-time data"]')).toBeVisible();
await setFixedTimeMode(page);
});
test('Tags work with Plot View of telemetry items', async ({ page }) => {
await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator"
});
const canvas = page.locator('canvas').nth(1);
await createTags({
page,
canvas,
xEnd: 700,
yEnd: 480
});
await basicTagsTests(page);
});
test('Tags work with Stacked Plots', async ({ page }) => {
const stackedPlot = await createDomainObjectWithDefaults(page, {
type: "Stacked Plot"
});
const alphaSineWave = await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
name: "Alpha Sine Wave",
parent: stackedPlot.uuid
});
await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
name: "Beta Sine Wave",
parent: stackedPlot.uuid
});
await page.goto(stackedPlot.url);
const canvas = page.locator('canvas').nth(1);
await createTags({
page,
canvas,
xEnd: 700,
yEnd: 215
});
await basicTagsTests(page);
await testTelemetryItem(page, alphaSineWave);
});
});

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -30,7 +30,7 @@ test.describe('Telemetry Table', () => {
description: 'https://github.com/nasa/openmct/issues/5113'
});
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
const table = await createDomainObjectWithDefaults(page, { type: 'Telemetry Table' });
await createDomainObjectWithDefaults(page, {

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -26,7 +26,7 @@ const { setFixedTimeMode, setRealTimeMode, setStartOffset, setEndOffset } = requ
test.describe('Time conductor operations', () => {
test('validate start time does not exceeds end time', async ({ page }) => {
// Go to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
const year = new Date().getFullYear();
let startDate = 'xxxx-01-01 01:00:00.000Z';
@@ -82,7 +82,7 @@ test.describe('Time conductor input fields real-time mode', () => {
};
// Go to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Switch to real-time mode
await setRealTimeMode(page);
@@ -119,7 +119,7 @@ test.describe('Time conductor input fields real-time mode', () => {
const endDelta = (1 * 1000);
// Go to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Switch to real-time mode
await setRealTimeMode(page);

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -26,7 +26,7 @@ const { openObjectTreeContextMenu, createDomainObjectWithDefaults } = require('.
test.describe('Timer', () => {
let timer;
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
timer = await createDomainObjectWithDefaults(page, { type: 'timer' });
});

View File

@@ -32,7 +32,7 @@ test.describe('Recent Objects', () => {
/** @type {import('@playwright/test').Locator} */
let folderA;
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Set Recent Objects List locator for subsequent tests
recentObjectsList = page.getByRole('list', {
@@ -58,7 +58,7 @@ test.describe('Recent Objects', () => {
});
test('Navigated objects show up in recents, object renames and deletions are reflected', async ({ page }) => {
// Verify that both created objects appear in the list and are in the correct order
assertInitialRecentObjectsListState();
await assertInitialRecentObjectsListState();
// Navigate to the folder by clicking on the main object name in the recent objects list item
await page.getByRole('listitem', { name: folderA.name }).getByText(folderA.name).click();
@@ -149,9 +149,9 @@ test.describe('Recent Objects', () => {
await expect(clockTreeItem.locator('.c-tree__item')).not.toHaveClass(/is-targeted-item/);
});
test("Persists on refresh", async ({ page }) => {
assertInitialRecentObjectsListState();
await assertInitialRecentObjectsListState();
await page.reload();
assertInitialRecentObjectsListState();
await assertInitialRecentObjectsListState();
});
test("Displays objects and aliases uniquely", async ({ page }) => {
const mainTree = page.getByRole('tree', { name: 'Main Tree'});
@@ -191,7 +191,7 @@ test.describe('Recent Objects', () => {
expect(await clockBreadcrumbs.count()).toBe(2);
expect(await clockBreadcrumbs.nth(0).innerText()).not.toEqual(await clockBreadcrumbs.nth(1).innerText());
});
test("Enforces a limit of 20 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
test.slow();
@@ -242,14 +242,67 @@ test.describe('Recent Objects', () => {
// Assert that the Clock treeitem is no longer highlighted
await expect(lastClockTreeItem.locator('.c-tree__item')).not.toHaveClass(/is-targeted-item/);
// Click the aria-label="Clear Recently Viewed" button
await page.getByRole('button', { name: 'Clear Recently Viewed' }).click();
// Click on the "OK" button in the confirmation dialog
await page.getByRole('button', { name: 'OK' }).click();
// Assert that the list is empty
expect(await recentObjectsList.locator('.c-recentobjects-listitem').count()).toBe(0);
});
test("Ensure clear recent objects button is active or inactive", async ({ page }) => {
// Assert that the list initially contains 3 objects (clock, folder, my items)
expect(await recentObjectsList.locator('.c-recentobjects-listitem').count()).toBe(3);
// Assert that the button is enabled
expect(
await page
.getByRole("button", { name: "Clear Recently Viewed" })
.isEnabled()
).toBe(true);
// Click the aria-label="Clear Recently Viewed" button
await page.getByRole("button", { name: "Clear Recently Viewed" }).click();
// Click on the "OK" button in the confirmation dialog
await page.getByRole("button", { name: "OK" }).click();
// Assert that the list is empty
expect(
await recentObjectsList.locator(".c-recentobjects-listitem").count()
).toBe(0);
// Assert that the button is disabled
expect(
await page
.getByRole("button", { name: "Clear Recently Viewed" })
.isEnabled()
).toBe(false);
// Navigate to folder object
await page.goto(folderA.url);
// Assert that the list contains 1 object
expect(await recentObjectsList.locator('.c-recentobjects-listitem').count()).toBe(1);
// Assert that the button is enabled
expect(
await page
.getByRole("button", { name: "Clear Recently Viewed" })
.isEnabled()
).toBe(true);
});
function assertInitialRecentObjectsListState() {
expect(recentObjectsList.getByRole('listitem', { name: clock.name })).toBeTruthy();
expect(recentObjectsList.getByRole('listitem', { name: folderA.name })).toBeTruthy();
expect(recentObjectsList.getByRole('listitem', { name: clock.name }).locator('a').getByText(folderA.name)).toBeTruthy();
expect(recentObjectsList.getByRole('listitem').nth(0).getByText(clock.name)).toBeTruthy();
expect(recentObjectsList.getByRole('listitem', { name: clock.name }).locator('a').getByText(folderA.name)).toBeTruthy();
expect(recentObjectsList.getByRole('listitem').nth(1).getByText(folderA.name)).toBeTruthy();
return Promise.all([
expect(recentObjectsList.getByRole('listitem', { name: clock.name })).toBeVisible(),
expect(recentObjectsList.getByRole('listitem', { name: folderA.name })).toBeVisible(),
expect(recentObjectsList.getByRole('listitem', { name: clock.name }).locator('a').getByText(folderA.name)).toBeVisible(),
expect(recentObjectsList.getByRole('listitem').nth(0).getByText(clock.name)).toBeVisible(),
expect(recentObjectsList.getByRole('listitem', { name: clock.name }).locator('a').getByText(folderA.name)).toBeVisible(),
expect(recentObjectsList.getByRole('listitem').nth(3).getByText(folderA.name)).toBeVisible()
]);
}
});

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -24,10 +24,18 @@
*/
const { test, expect } = require('../../pluginFixtures');
const { createDomainObjectWithDefaults } = require('../../appActions');
const { createDomainObjectWithDefaults, selectInspectorTab } = require('../../appActions');
const { v4: uuid } = require('uuid');
test.describe('Grand Search', () => {
const searchResultSelector = '.c-gsearch-result__title';
const searchResultDropDownSelector = '.c-gsearch__results';
test.beforeEach(async ({ page }) => {
// Go to baseURL
await page.goto("./", { waitUntil: "networkidle" });
});
test('Can search for objects, and subsequent search dropdown behaves properly', async ({ page, openmctConfig }) => {
const { myItemsFolderName } = openmctConfig;
@@ -42,7 +50,7 @@ test.describe('Grand Search', () => {
await expect(page.locator('[aria-label="Search Result"] >> nth=2')).toContainText(`Clock C ${myItemsFolderName} Red Folder Blue Folder`);
await expect(page.locator('[aria-label="Search Result"] >> nth=3')).toContainText(`Clock D ${myItemsFolderName} Red Folder Blue Folder`);
// Click the Elements pool to dismiss the search menu
await page.locator('.l-pane__label:has-text("Elements")').click();
await selectInspectorTab(page, 'Elements');
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toBeHidden();
await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').click();
@@ -55,7 +63,7 @@ test.describe('Grand Search', () => {
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toContainText(`Clock A ${myItemsFolderName} Red Folder Blue Folder`);
// Click [aria-label="OpenMCT Search"] a >> nth=0
await page.locator('[aria-label="OpenMCT Search"] a').first().click();
await page.locator('[aria-label="Search Result"] >> nth=0').click();
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toBeHidden();
// Fill [aria-label="OpenMCT Search"] input[type="search"]
@@ -89,15 +97,8 @@ test.describe('Grand Search', () => {
await expect(page.locator('[aria-label="Search Result"] >> nth=2')).toContainText(`Clock C ${myItemsFolderName} Red Folder Blue Folder`);
await expect(page.locator('[aria-label="Search Result"] >> nth=3')).toContainText(`Clock D ${myItemsFolderName} Red Folder Blue Folder`);
});
});
test.describe("Search Tests @unstable", () => {
const searchResultSelector = '.c-gsearch-result__title';
test('Validate empty search result', async ({ page }) => {
// Go to baseURL
await page.goto("./", { waitUntil: "networkidle" });
// Invalid search for objects
await page.type("input[type=search]", 'not found');
@@ -105,7 +106,7 @@ test.describe("Search Tests @unstable", () => {
await waitForSearchCompletion(page);
// Get the search results
const searchResults = await page.locator(searchResultSelector);
const searchResults = page.locator(searchResultSelector);
// Verify that no results are found
expect(await searchResults.count()).toBe(0);
@@ -115,9 +116,6 @@ test.describe("Search Tests @unstable", () => {
});
test('Validate single object in search result @couchdb', async ({ page }) => {
//Go to baseURL
await page.goto("./", { waitUntil: "networkidle" });
// Create a folder object
const folderName = uuid();
await createDomainObjectWithDefaults(page, {
@@ -135,25 +133,61 @@ test.describe("Search Tests @unstable", () => {
const searchResults = page.locator(searchResultSelector);
// Verify that one result is found
await expect(searchResults).toBeVisible();
expect(await searchResults.count()).toBe(1);
await expect(searchResults).toHaveText(folderName);
});
test('Search results are debounced @couchdb', async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/6179'
});
await createObjectsForSearch(page);
let networkRequests = [];
page.on('request', (request) => {
const searchRequest = request.url().endsWith('_find');
const fetchRequest = request.resourceType() === 'fetch';
if (searchRequest && fetchRequest) {
networkRequests.push(request);
}
});
// Full search for object
await page.type("input[type=search]", 'Clock', { delay: 100 });
// Wait for search to finish
await waitForSearchCompletion(page);
// Network requests for the composite telemetry with multiple items should be:
// 1. batched request for latest telemetry using the bulk API
expect(networkRequests.length).toBe(1);
const searchResultDropDown = await page.locator(searchResultDropDownSelector);
await expect(searchResultDropDown).toContainText('Clock A');
});
test("Validate multiple objects in search results return partial matches", async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/4667'
});
// Go to baseURL
await page.goto("/", { waitUntil: "networkidle" });
// Create folder objects
const folderName = "e928a26e-e924-4ea0";
const folderName1 = "e928a26e-e924-4ea0";
const folderName2 = "e928a26e-e924-4001";
await createFolderObject(page, folderName);
await createFolderObject(page, folderName2);
await createDomainObjectWithDefaults(page, {
type: 'Folder',
name: folderName1
});
await createDomainObjectWithDefaults(page, {
type: 'Folder',
name: folderName2
});
// Partial search for objects
await page.type("input[type=search]", 'e928a26e');
@@ -161,36 +195,22 @@ test.describe("Search Tests @unstable", () => {
// Wait for search to finish
await waitForSearchCompletion(page);
// Get the search results
const searchResults = await page.locator(searchResultSelector);
const searchResultDropDown = page.locator(searchResultDropDownSelector);
// Verify that the search result/s correctly match the search query
await expect(searchResultDropDown).toContainText(folderName1);
await expect(searchResultDropDown).toContainText(folderName2);
// Get the search results
const searchResults = page.locator(searchResultSelector);
// Verify that two results are found
expect(await searchResults.count()).toBe(2);
await expect(await searchResults.first()).toHaveText(folderName);
await expect(await searchResults.last()).toHaveText(folderName2);
});
});
async function createFolderObject(page, folderName) {
// Open Create menu
await page.locator('button:has-text("Create")').click();
// Select Folder object
await page.locator('text=Folder').nth(1).click();
// Click folder title to enter edit mode
await page.locator('text=Properties Title Notes >> input[type="text"]').click();
// Enter folder name
await page.locator('text=Properties Title Notes >> input[type="text"]').fill(folderName);
// Create folder object
await page.locator('button:has-text("OK")').click();
}
async function waitForSearchCompletion(page) {
// Wait loading spinner to disappear
await page.waitForSelector('.c-tree-and-search__loading', { state: 'detached' });
await page.waitForSelector('.search-finished');
}
/**
@@ -198,9 +218,6 @@ async function waitForSearchCompletion(page) {
* @param {import('@playwright/test').Page} page
*/
async function createObjectsForSearch(page) {
//Go to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
const redFolder = await createDomainObjectWithDefaults(page, {
type: 'Folder',
name: 'Red Folder'

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -38,7 +38,7 @@ const { test, expect } = require('../../pluginFixtures');
test('Verify that the create button appears and that the Folder Domain Object is available for selection', async ({ page }) => {
//Go to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
await page.goto('./', { waitUntil: 'domcontentloaded' });
//Click the Create button
await page.click('button:has-text("Create")');

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -26,10 +26,80 @@ const {
openObjectTreeContextMenu
} = require('../../appActions.js');
test.describe('Tree operations', () => {
test.describe('Main Tree', () => {
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'domcontentloaded' });
});
test('Creating a child object within a folder and immediately opening it shows the created object in the tree @couchdb', async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/5975'
});
const folder = await createDomainObjectWithDefaults(page, {
type: 'Folder'
});
await page.getByTitle('Show selected item in tree').click();
const clock = await createDomainObjectWithDefaults(page, {
type: 'Clock',
parent: folder.uuid
});
await expandTreePaneItemByName(page, folder.name);
await assertTreeItemIsVisible(page, clock.name);
});
test('Creating a child object on one tab and expanding its parent on the other shows the correct composition @2p', async ({ page, openmctConfig }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/6391'
});
const { myItemsFolderName } = openmctConfig;
const page2 = await page.context().newPage();
// Both pages: Go to baseURL
await Promise.all([
page.goto('./', { waitUntil: 'networkidle' }),
page2.goto('./', { waitUntil: 'networkidle' })
]);
const page1Folder = await createDomainObjectWithDefaults(page, {
type: 'Folder'
});
await expandTreePaneItemByName(page2, myItemsFolderName);
await assertTreeItemIsVisible(page2, page1Folder.name);
});
test('Creating a child object on one tab and expanding its parent on the other shows the correct composition @couchdb @2p', async ({ page, openmctConfig }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/6391'
});
const { myItemsFolderName } = openmctConfig;
const page2 = await page.context().newPage();
// Both pages: Go to baseURL
await Promise.all([
page.goto('./', { waitUntil: 'networkidle' }),
page2.goto('./', { waitUntil: 'networkidle' })
]);
const page1Folder = await createDomainObjectWithDefaults(page, {
type: 'Folder'
});
await expandTreePaneItemByName(page2, myItemsFolderName);
await assertTreeItemIsVisible(page2, page1Folder.name);
});
test('Renaming an object reorders the tree @unstable', async ({ page, openmctConfig }) => {
const { myItemsFolderName } = openmctConfig;
await page.goto('./', { waitUntil: 'networkidle' });
await createDomainObjectWithDefaults(page, {
type: 'Folder',
@@ -111,17 +181,30 @@ async function getAndAssertTreeItems(page, expected) {
expect(allTexts).toEqual(expected);
}
async function assertTreeItemIsVisible(page, name) {
const mainTree = page.getByRole('tree', {
name: 'Main Tree'
});
const treeItem = mainTree.getByRole('treeitem', {
name
});
await expect(treeItem).toBeVisible();
}
/**
* @param {import('@playwright/test').Page} page
* @param {string} name
*/
async function expandTreePaneItemByName(page, name) {
const treePane = page.getByRole('tree', {
const mainTree = 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();
const treeItem = mainTree.getByRole('treeitem', {
name,
expanded: false
});
await treeItem.locator('.c-disclosure-triangle').click();
}
/**

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -138,6 +138,7 @@ test.describe('Performance tests', () => {
await page.evaluate(() => window.performance.mark("notebook-search-processed"));
//Clear Search
await page.locator('.c-search.c-notebook__search .c-search__input').hover();
await page.locator('.c-search.c-notebook__search .c-search__clear-input').click();
await page.evaluate(() => window.performance.mark("notebook-search-processed"));

View File

@@ -1,6 +1,6 @@
/* eslint-disable no-undef */
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*

View File

@@ -1,68 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2023, 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 is dedicated to test the blur behavior of the add tag button.
*/
const { test } = require("../../pluginFixtures");
const percySnapshot = require('@percy/playwright');
const { createDomainObjectWithDefaults } = require('../../appActions');
test.describe("Visual - Check blur of Add Tag button", () => {
test.beforeEach(async ({ page }) => {
// Open a browser, navigate to the main page, and wait until all network events to resolve
await page.goto('./', { waitUntil: 'networkidle' });
});
test("Blur 'Add tag'", async ({ page, theme }) => {
createDomainObjectWithDefaults(page, { type: 'Notebook' });
await page.locator('text=To start a new entry, click here or drag and drop any object').click();
const entryLocator = `[aria-label="Notebook Entry Input"] >> nth = 0`;
await page.locator(entryLocator).click();
await page.locator(entryLocator).fill(`Entry 0`);
await page.locator(entryLocator).press('Enter');
// Click on Annotations tab
await page.locator('.c-inspector__tab', { hasText: "Annotations" }).click();
// Take snapshot of the notebook with the Annotations tab opened
await percySnapshot(page, `Notebook Annotation (theme: '${theme}')`);
// Click on the "Add Tag" button
await page.locator('button:has-text("Add Tag")').click();
// Take snapshot of the notebook with the AutoComplete field visible
await percySnapshot(page, `Notebook Add Tag (theme: '${theme}')`);
// Click inside the AutoComplete field
await page.locator('[placeholder="Type to select tag"]').click();
// Click on the "Tags" header (simulating a click outside the autocomplete field)
await page.locator('div.c-inspect-properties__header:has-text("Tags")').click();
// Take snapshot of the notebook with the AutoComplete field hidden and with the "Add Tag" button visible
await percySnapshot(page, `Notebook Annotation de-select blur (theme: '${theme}')`);
});
});

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*

View File

@@ -34,7 +34,7 @@ test.describe('Visual - Check Notification Info Banner of \'Save successful\'',
await page.goto('./', { waitUntil: 'networkidle' });
});
test('Create a clock, click on \'Save successful\' banner and dismiss it', async ({ page }) => {
test('Create a clock, click on \'Save successful\' banner and dismiss it', async ({ page, theme }) => {
// Create a clock domain object
await createDomainObjectWithDefaults(page, { type: 'Clock' });
// Verify there is a button with aria-label="Review 1 Notification"
@@ -47,7 +47,7 @@ test.describe('Visual - Check Notification Info Banner of \'Save successful\'',
expect(await page.locator('div[role="dialog"]').isVisible()).toBe(true);
// Verify the div with role="dialog" contains text "Save successful"
expect(await page.locator('div[role="dialog"]').innerText()).toContain('Save successful');
await percySnapshot(page, 'Notification banner');
await percySnapshot(page, `Notification banner - ${theme}`);
// Verify there is a button with text "Dismiss"
expect(await page.locator('button:has-text("Dismiss")').isVisible()).toBe(true);
// Click on button with text "Dismiss"

View File

@@ -0,0 +1,58 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2023, 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.
*****************************************************************************/
const { test } = require('../../pluginFixtures');
const { setBoundsToSpanAllActivities } = require('../../helper/planningUtils');
const { createDomainObjectWithDefaults, createPlanFromJSON } = require('../../appActions');
const percySnapshot = require('@percy/playwright');
const examplePlanSmall = require('../../test-data/examplePlans/ExamplePlan_Small2.json');
const snapshotScope = '.c-object-view';
test.describe('Visual - Planning', () => {
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'networkidle' });
});
test('Plan View', async ({ page, theme }) => {
const plan = await createPlanFromJSON(page, {
json: examplePlanSmall
});
await setBoundsToSpanAllActivities(page, examplePlanSmall, plan.url);
await percySnapshot(page, `Plan View (theme: ${theme})`, {
scope: snapshotScope
});
});
test('Gantt Chart View', async ({ page, theme }) => {
const ganttChart = await createDomainObjectWithDefaults(page, {
type: 'Gantt Chart'
});
await createPlanFromJSON(page, {
json: examplePlanSmall,
parent: ganttChart.uuid
});
await setBoundsToSpanAllActivities(page, examplePlanSmall, ganttChart.url);
await percySnapshot(page, `Gantt Chart View (theme: ${theme})`, {
scope: snapshotScope
});
});
});

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, United States Government
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*

Some files were not shown because too many files have changed in this diff Show More