Compare commits

...

213 Commits

Author SHA1 Message Date
05e3303828 Fault management (#5212)
* Implements Fault Management

Co-authored-by: Rukmini Bose <rukmini.bose15@gmail.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
2022-06-06 13:43:20 -07:00
aa0fc70e54 Add CouchDB Status Indicator (#5276)
* Add CouchStatusIndicator

* Remove stray console log

* convert `request()` to async

* refactor

* Fix typo

* Instantiate indicator outside of object provider

- Add 'Maintenance' CouchDB status

- Add text and description for all CouchDB statuses

- Some code cleanup

* Update comments

* Add default cases to switches, make method private

* Small status text change

* Make jsdoc @private methods actually private

* Handle commonly encountered CouchDB errors

- Handle 400, 401, 404, 412, 500 status codes

- Remove `MAINTENANCE` status from this logic since that can only be assumed if receiving a 404 status from GET `{db}/_up`

* Fix tests: avoid directly calling private method

* Add some tests for indicator status

* Update docs for `CouchStatusIndicator`

* Update docs for new `CouchObjectProvider` method

* Make method private

* fix the oopsie

* Add test for 'pending' state

Co-authored-by: Scott Bell <scott@traclabs.com>
2022-06-06 18:49:47 +02:00
9fbb695379 [Restricted Notebook] Creating new Restricted Notebook type (#5173)
* added/removed status for locked, will not work with current one status per domain object setup
* setting restricted right away based on nb type
* added confirmation dialog for locking a page

* Styling for restricted Notebook
- Markup, CSS and content changes for lock button and locked message.
- Removed "Note book Type" property from NotebookType.js.
* have a version of entry template that has no listeners for locked items
* cleaning up page and section components
* making sure basic notebook stuff is installed at least once
* updating data transfer values for locked page entries, fixing page and section selection from edits
* adding locked flag to search result entries
* fixing uneditable section/page names
* cleaning up updateName function for page/section names
* removing install of restricted notebook
* updating confirmation dialog
* updating tests for new export structur
- New symbols glyph and SVG for the Shift Log. IMPORTANT: OVERRIDE ANY MERGE CONFLICTS WITH THIS COMMIT!

* made create button items dynamic each time the button is clicked, this will pick up any new types added after the create menu is created

* removing dynamic create menu list

* found a way to add the plugin before openmct.start is called
* making create items dynamic to include types added after openmct is started
* more e2e tests for restricted notebook

* updates from PR reviews, also fixed error in mct-tree thrown by not checking for an element

* plain notebook tests

* More testcase definition

* actually removing notebook object to test

* removing dupes

* checking if agent exists before relying on it... it was breaking tests with errors

* updating for new browser agent code

* fixing linting errors

Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com>
Co-authored-by: unlikelyzero <jchill2@gmail.com>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2022-06-04 09:06:07 -07:00
584d11a2ef Expose Stacked Plot view (#5290) 2022-06-04 00:39:23 -05:00
162cc6bc77 Support for spectral plots via existing bar graphs (#5162)
Spectral plots support

Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2022-06-03 19:32:32 -07:00
111b0d0d68 Imagery layers (#4968)
* Moved imagery controls to a separate component
* Zoom pan controls moved to component
* Implement adjustments to encapsulate state into ImageryControls
* Track modifier key pressed for layouts
* image control popup open/close fix
* Styling for imagery local controls

Co-authored-by: Michael Rogers <contact@mhrogers.com>
Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
Co-authored-by: Scott Bell <scott@traclabs.com>
Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com>
Co-authored-by: unlikelyzero <jchill2@gmail.com>
Co-authored-by: Jamie Vigliotta <jamie.j.vigliotta@nasa.gov>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2022-06-04 01:24:43 +00:00
59c0da1b57 Add units to Gauges (#5196)
* Fixes #3197
- Code and styling to allow units display.

Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2022-06-03 21:34:03 +00:00
3c70cf1767 Search & Notebook Tagging - Mct4820 (#5203)
* implement new search and tagging for notebooks
* add example tags, remove inspector reference
* include annotations in mct
* fix performance tests


Co-authored-by: John Hill <john.c.hill@nasa.gov>
Co-authored-by: unlikelyzero <jchill2@gmail.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2022-06-03 13:12:42 -07:00
2aec1ee854 Bump eslint-plugin-vue from 8.5.0 to 9.1.0 (#5287)
Bumps [eslint-plugin-vue](https://github.com/vuejs/eslint-plugin-vue) from 8.5.0 to 9.1.0.
- [Release notes](https://github.com/vuejs/eslint-plugin-vue/releases)
- [Commits](https://github.com/vuejs/eslint-plugin-vue/compare/v8.5.0...v9.1.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-vue
  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>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2022-06-03 19:59:10 +00:00
ab60e3c3bd Bump vue-eslint-parser from 8.3.0 to 9.0.2 (#5262)
Bumps [vue-eslint-parser](https://github.com/vuejs/vue-eslint-parser) from 8.3.0 to 9.0.2.
- [Release notes](https://github.com/vuejs/vue-eslint-parser/releases)
- [Commits](https://github.com/vuejs/vue-eslint-parser/compare/v8.3.0...v9.0.2)

---
updated-dependencies:
- dependency-name: vue-eslint-parser
  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>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2022-06-03 19:52:04 +00:00
4445d7116a Expose components (#5289)
* export components

* add components to openmct

* add unit tests

Co-authored-by: Andrew Henry <akhenry@gmail.com>
2022-06-03 12:29:08 -07:00
93abc18001 Bump @babel/eslint-parser from 7.16.3 to 7.18.2 (#5286)
Bumps [@babel/eslint-parser](https://github.com/babel/babel/tree/HEAD/eslint/babel-eslint-parser) from 7.16.3 to 7.18.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.18.2/eslint/babel-eslint-parser)

---
updated-dependencies:
- dependency-name: "@babel/eslint-parser"
  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: John Hill <john.c.hill@nasa.gov>
2022-06-03 18:46:57 +00:00
7fb37de721 Bump sass from 1.49.9 to 1.52.2 (#5285)
Bumps [sass](https://github.com/sass/dart-sass) from 1.49.9 to 1.52.2.
- [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.49.9...1.52.2)

---
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>
2022-06-03 10:56:28 -07:00
1c525f50c8 Display Layout toolbar refinements for units (#5197)
* Fixes #3197
- Moved position of hide/show units toggle button.
- Added labels to many toolbar buttons, including hide/show units, hide/show frame, edit text, more.
- Added label to toolbar-toggle-button.vue.
- Added separator between stackOrder button and position inputs.

* Fixes #3197
- Removed unwanted margin in alphanumerics when label is hidden.

Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2022-06-03 16:53:21 +00:00
40a7451064 Fix stackplots static style (#5045)
* [4864] Fixes cancelling edit properties console error
* Get the style receiver when the styleRuleManager is initialized. This prevents any ambiguity about which element should receive the style

* Don't subscribe if the styleRuleManager has been destroyed

Co-authored-by: John Hill <john.c.hill@nasa.gov>
Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
Co-authored-by: Andrew Henry <andrew.k.henry@nasa.gov>
2022-06-03 16:46:27 +00:00
04ee6f49d6 Remove all non legacy usage of zepto (#5159)
* Removed Zepto
* Added utility functions for compiling HTML templates and toggling classes on and off

Co-authored-by: John Hill <john.c.hill@nasa.gov>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2022-06-02 15:47:14 -07:00
f5796c984e Operator status (#5179)
* Added click event to simple indicator

* Moved operator status plugin to Open

* Implementing user role status API

* Support adding indicators asynchronously

* Adding user status API

* Updated example user provider

* Update icon with status

* Adding admin indicator

* Apply config options

* Set status class on indicator. Clear all statuses

* Show poll question in op stat indicator

* Implementing status summary

* Get statuses from providers. Reset statuses when poll question set

* Styling for operator status
- New icon glyph - IMPORTANT: OVERRIDE ANY MERGE CONFLICTS USING THIS COMMIT!
- Fixed erroneous font glyph mapping;
- Added default color for indicator icon;
- Changed user indicator to display response when set to other than "NO_STATUS".
- Standardized icon display.

* Cherrypick symbols font updates from restricted-notebook branch. This is the most full and complete version of the symbols font - OVERRIDE ANY MERGE CONFLICTS WITH THIS COMMIT!

* Fix positioning of popups

* Also fix positioning of status indicator

* Get roles by status instead of users

* Refactor how status summary is determined to simplify API

* Re-fetch status summary on status change

* Implemented status reset

* Move status into separate API

* Refactor user status to its own sub-API

* Create RAF utility class

* Error handling

* Add copyright notices

* Fix test issues

* Added jsdocs

* Additional tests for raf utility function

* Move status style configuration into Open

* Move styling from the API into the view

* Added some docs

* Added some unit tests and fixed a bug found in the process. Tests work\!

Co-authored-by: Andrew Henry <akhenry@gmail.com>
2022-06-02 13:46:13 -07:00
50b642fabe Updated the dependency injection syntax to use v4 instead of default (#5279) 2022-06-02 18:42:11 +00:00
dfb726b924 Unpause telemetry table on user bounds change (#5186)
* Unpause telemetry table on user bounds change (#5113)

* Add tests for table pause and unpause (#5113)

* Add test (#5113)

- Add test for scenario where table is paused by button but unpaused by user bounds change

* Add test (#5113)

- Add test for table does not unpause on a bounds change caused by a tick

* Add e2e test (#5113)

- Add test for scenario where table is paused by button but unpaused by user bounds change

* Add test (#5113)

- Correctly simulate clock tick

- Exclude datum with new bounds and ensure the correct tableRow count

* Remove 'wait for save banner' logic from e2e test

* Use augmented `test` object in e2e test

- Imports `test` object from `fixtures.js`

* e2e: Add workarounds for chromium issue

* Refactor per code review comments

- Simplify `userBoundsChanged()` logic, get rid of duplicate code

* Just get rid of the unnecessary method

* Respond to code review comments

- `destroyed()` --> `beforeDestroy()`

- Rename `unpausedByButton` parameter to include user bounds change condition

- Remove unused parameter
2022-06-02 10:27:49 -07:00
8d761f729b Add visual test for create menu and display layout icon (#5278)
Co-authored-by: unlikelyzero <jchill2@gmail.com>
2022-06-02 07:43:40 -07:00
d88ead502c Sprint 2.0.5 (#5272)
* Bump d3-selection from 1.3.2 to 3.0.0

Bumps [d3-selection](https://github.com/d3/d3-selection) from 1.3.2 to 3.0.0.
- [Release notes](https://github.com/d3/d3-selection/releases)
- [Commits](https://github.com/d3/d3-selection/compare/v1.3.2...v3.0.0)

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

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

* Prep for release 2.0.5

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-31 11:15:02 -07:00
c46849b166 MCT 3930 (#4372)
* MCT 3930

* temp push

* fix merge conflicts

* update font size

Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
Co-authored-by: unlikelyzero <jchill2@gmail.com>
2022-05-27 08:24:55 -07:00
6c71fa01f5 Fix existing eslint warnings, configure eslint to fail on warning (#5258)
* [e2e] Remove unnecessary step with force click

* `test.skip()` -> `test.fixme()`

* Bypass `no-wait-for-timeout` rule for visual tests

* Fail lint step if warnings > 0

* Set default value for `imageUrl`

- Resolves `vue/require-default-prop` warning

* Disable eslint rule `you-dont-need-lodash-underscore/get`

- Disable the rule for now as the implementation they suggest doesn't match lodash `_.get()` functionality, and fails a bunch of our tests. See https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore/issues/311 and https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore/issues/294

* Disable `no-wait-for-timeout` warning for `visual` folder

* Add rule exception and comments in logPlot test

- Add exception and FIXME for timeout

- Add comment on fixme test to discourage community contribution

* clean up tests

- remove unnecessary awaits

- update locators to use data-testid where possible

- remove unnecessary wait

* Wait for image count condition instead of timeout

* code review comments: use expect.poll()

* readability

* .fixme() + comment instead of .skip()

* disable `.skip()` warning for memleak test suite
2022-05-26 09:45:16 -07:00
c56d458ecb Performance tests, notebook tests, and sharding (#5236)
* Renamed test files
* temp push of performance branch
* comma
* stash
* final
* rename to imagery
* remove old
* stash
* fix name
* Add plain notebook e2e and perf test
* Shard and add perf to ci
* Import fixtures
* 3
* also off by one?
* forgive me, father
* update perf test name
* SHARD
* one mo shot
* add suites
* failfast
* remove allure
* add more testsuites
* full
* skip
* ignore glob
* headless?
* skip audit to save ci time
* temp push
* remove allure
* remove doubled test and update snapshots
* update snapthos 2
* back to ci
* update comments
* remove notebook
* updates
* remove npx playwright install
* Prevent deleting browsers
* circleci comment
* re-enable audit
* Add html reporter
* speed up execution
* speed up CI
* Add performance line to bug report
* PR Comments
* change dir of report
* remove test
* Update Tracepath. Update playwright-percy
* Remove shim
* Improve stability and move html report
* fix space
* Add more slowdown for moveObjects
* Get rid of navigation event
* fix missing events
* review comments
* last change!

Co-authored-by: unlikelyzero <jchill2@gmail.com>
2022-05-25 15:45:11 -07:00
f74a35f45a Bump uuid from 3.3.3 to 8.3.2 (#5170) 2022-05-25 16:09:08 -05:00
d9ac0182c3 Remove languages from bug report as we don't need it (#5213)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2022-05-24 20:10:14 +00:00
7bb108c36b Bump webpack-dev-middleware from 5.3.1 to 5.3.3 (#5242)
Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 5.3.1 to 5.3.3.
- [Release notes](https://github.com/webpack/webpack-dev-middleware/releases)
- [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v5.3.1...v5.3.3)

---
updated-dependencies:
- dependency-name: webpack-dev-middleware
  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>
2022-05-24 19:28:04 +00:00
77804cff75 Bump @percy/cli from 1.0.4 to 1.2.1 (#5244)
Bumps [@percy/cli](https://github.com/percy/cli/tree/HEAD/packages/cli) from 1.0.4 to 1.2.1.
- [Release notes](https://github.com/percy/cli/releases)
- [Commits](https://github.com/percy/cli/commits/v1.2.1/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: John Hill <john.c.hill@nasa.gov>
2022-05-24 19:06:32 +00:00
2d73296b36 Do not install Chart plugin by default (#5163)
* Do not install Chart plugin by default (#5088)

* Install Chart plugin in development mode (#5088)

Co-authored-by: Andrew Henry <akhenry@gmail.com>
2022-05-24 19:00:39 +00:00
405418b9d5 Preserve local clock offsets on mode switch, fall back to defaults (#5217) 2022-05-23 14:10:59 -07:00
f999b9e12b Bump sinon from 13.0.1 to 14.0.0 (#5243)
Bumps [sinon](https://github.com/sinonjs/sinon) from 13.0.1 to 14.0.0.
- [Release notes](https://github.com/sinonjs/sinon/releases)
- [Changelog](https://github.com/sinonjs/sinon/blob/main/docs/changelog.md)
- [Commits](https://github.com/sinonjs/sinon/compare/v13.0.1...v14.0.0)

---
updated-dependencies:
- dependency-name: sinon
  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>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2022-05-23 16:37:01 +00:00
664ba399ea Bump karma from 6.3.18 to 6.3.20 (#5241)
Bumps [karma](https://github.com/karma-runner/karma) from 6.3.18 to 6.3.20.
- [Release notes](https://github.com/karma-runner/karma/releases)
- [Changelog](https://github.com/karma-runner/karma/blob/master/CHANGELOG.md)
- [Commits](https://github.com/karma-runner/karma/compare/v6.3.18...v6.3.20)

---
updated-dependencies:
- dependency-name: karma
  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>
2022-05-23 16:12:59 +00:00
c6078a234a Bump lighthouse from 9.5.0 to 9.6.1 (#5230)
Bumps [lighthouse](https://github.com/GoogleChrome/lighthouse) from 9.5.0 to 9.6.1.
- [Release notes](https://github.com/GoogleChrome/lighthouse/releases)
- [Changelog](https://github.com/GoogleChrome/lighthouse/blob/v9.6.1/changelog.md)
- [Commits](https://github.com/GoogleChrome/lighthouse/compare/v9.5.0...v9.6.1)

---
updated-dependencies:
- dependency-name: lighthouse
  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: John Hill <john.c.hill@nasa.gov>
2022-05-21 11:07:23 -07:00
17c16eba50 Added visual test for capturing the Save Successful Banner (#5237)
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2022-05-20 22:11:32 +00:00
9f9c69ee68 Gauge limits (#5156)
* Fix tick values for plots ticks in log mode and null check (#5119)
* [2297] When there is no display range or range, skip setting the range value when auto scale is turned off.
* If the formatted value is a number and a float, set precision to 2 decimal points.
* Fix value assignment
* Use whole numbers in log mode
* Revert whole numbers fix - need floats for values between 0 and 1.
* Reverts forced precision for log plots axis labels (#5147)
* [Gauge Plugin] Limits and Composition Issues #5155
* default current value as '--'
* updated as per review comments.
Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2022-05-20 20:28:56 +00:00
037886aa01 Better detection of device orientation based on browser feature availability (#5172)
* Update Agent.isPortrait() utility method (#4875)
* Properly feature detect for orientation APIs (#4875)
* Use Agent to detect device orientation (#4875)
* Tests for display orientation detection (#4875)

Co-authored-by: Andrew Henry <akhenry@gmail.com>
2022-05-20 19:31:59 +00:00
48916564e4 Refactor plot actions to save space (#5201)
* Move image export actions to 3-dot menu
* Move cursor guide and toggle grid lines to local controls for plots (on hover)
* toggle cursor and gridlines affect all plots in a stacked plot
* Fix tests
* Better message when exporting plots, fixed typo

Co-authored-by: Joe Pea <trusktr@gmail.com>
Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com>
2022-05-20 18:41:01 +00:00
1ca5271c3e e2e test for image thumbnail visibility and size - 5106 (#5232)
* e2e test for timage thumbnail visibility and size

* Lint fix

* Remove URL comment

* Added comment and assigned selectors to var
2022-05-20 17:26:54 +00:00
6521b888d6 Enable lint enforcement on e2e tests and fix the existing errors (#5229)
* Add `e2e` folder to lint scripts

* Fix or add exceptions to all new linting errors

* fix an oopsie 👀
2022-05-19 16:09:22 -07:00
85fce3c456 Bump jasmine-core from 4.0.1 to 4.1.1 (#5225)
Bumps [jasmine-core](https://github.com/jasmine/jasmine) from 4.0.1 to 4.1.1.
- [Release notes](https://github.com/jasmine/jasmine/releases)
- [Changelog](https://github.com/jasmine/jasmine/blob/main/RELEASE.md)
- [Commits](https://github.com/jasmine/jasmine/compare/v4.0.1...v4.1.1)

---
updated-dependencies:
- dependency-name: jasmine-core
  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: John Hill <john.c.hill@nasa.gov>
2022-05-19 15:03:41 -07:00
8d577a8958 Removed requestHistory call from mount and clearData method (#5223)
Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
2022-05-19 20:07:20 +00:00
9c8ee09960 Rename a file tree object trigger an alphabetical reordering (#5187)
* Add observers to domain object and update tree func

* Remove unused argument

Co-authored-by: Michael Rogers <contact@mhrogers.com>
2022-05-19 11:21:33 -04:00
9568da9d5f Fix Example imagery 5158 (#5183)
* Created new project file

* click previous image button

* Zooms left, right, up, down

* Rebased and added my tests back

* Removed expected pause from zoom

* printing var

Co-authored-by: John Hill <john.c.hill@nasa.gov>
2022-05-19 15:11:12 +00:00
2aa3b810ba Scatter plots (#4881)
* New view for plot underlays
* Update to show markers from data
* Add scatter plot x and y axes configuration
* Add color properties for scatter plots
* Add x and y axis min and max to work with underlays
* Use request API for telemetry (telemetry collections bug)
* Allow zero values
* Add pan and zoom functionality

* Glyphs and text changes for Scatter Plots
IMPORTANT: ANY MERGE CONFLICTS WITH FONT FILES SHOULD OVERRIDE USING THIS COMMIT - DO NOT MERGE CHANGES!
- Changed name to 'Scatter Plot', refined description;
- New icon glyph and SVG bg for `icon-plot-scatter`, font-files updated;
- More clarity added to underlay min/max form labels for clarity;

* Glyphs and text changes for Scatter Plots
- Add updated Icomoon file;

* Inspector refinements for Scatter Plots
- Consolidated Inspector section layout;
- Improved ColorSwatch.vue code using <template> tags to allow less brittle CSS styling;
- Improved Inspector CSS to remove overly specific selectors for `grid-row` elements;

* Enable indeendent time conductor
* Add button to remove scatter plot underlay
* Adds tests for scatter plot
* Modded look and icon of file remove button

Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2022-05-19 14:01:33 +00:00
1cdbb34e21 Notebook entry tweaks for #4954 (#5036)
* Notebook entry tweaks for #4954
- Standardized entry layout;
- Colors, styles and padding refinements for entry elements;

Co-authored-by: Andrew Henry <andrew.k.henry@nasa.gov>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2022-05-18 12:08:15 -07:00
95299336d0 New forms code needs tests #4539 (#4758)
* New forms code needs tests #4539

Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
Co-authored-by: unlikelyzero <jchill2@gmail.com>
Co-authored-by: Joshi <simplyrender@gmail.com>
2022-05-18 09:25:11 -07:00
b8ff5c7f33 Pan/Zoom persistence and pause handling improvements - 5068 (#5188)
* Remove pause on pan/zoom wheel or button input

* Test to ensure that pause mode is not activated during zoom

Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2022-05-18 01:44:16 +00:00
9ede023cfa Bump copy-webpack-plugin from 10.2.0 to 11.0.0 (#5208)
Bumps [copy-webpack-plugin](https://github.com/webpack-contrib/copy-webpack-plugin) from 10.2.0 to 11.0.0.
- [Release notes](https://github.com/webpack-contrib/copy-webpack-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/copy-webpack-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v10.2.0...v11.0.0)

---
updated-dependencies:
- dependency-name: copy-webpack-plugin
  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>
2022-05-17 13:45:38 -07:00
308e621b5d 4863 - Object.hasOwn unsupported (#4920)
Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
2022-05-17 11:23:51 -07:00
e6b5870234 Update PR Template to include cases which ignore testing instructions (#5204)
Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2022-05-17 18:07:59 +00:00
03e7d912be Warn user if telemetry not all telemetry metadata matches time system (#4996)
* warn user if telemetry not all telemetry metadata matches time system
* create spec file for telemetry collections
* Add tests (#4999)
- Test for warn if metadata does not match active TimeSystem
- Test for no warn if metadata matches active TimeSystem
* unset timeKey if no matching domain found
* Extract errors to constants file and update tests

Co-authored-by: Jesse Mazzella <jesse.d.mazzella@nasa.gov>
2022-05-16 11:58:36 -07:00
09da373d1c Add console error checking to our e2e suite (#5177)
Co-authored-by: unlikelyzero <jchill2@gmail.com>
2022-05-13 10:55:34 -07:00
b8d9e41c01 Bump karma-coverage from 2.1.1 to 2.2.0 (#5181)
Bumps [karma-coverage](https://github.com/karma-runner/karma-coverage) from 2.1.1 to 2.2.0.
- [Release notes](https://github.com/karma-runner/karma-coverage/releases)
- [Changelog](https://github.com/karma-runner/karma-coverage/blob/master/CHANGELOG.md)
- [Commits](https://github.com/karma-runner/karma-coverage/compare/v2.1.1...v2.2.0)

---
updated-dependencies:
- dependency-name: karma-coverage
  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>
2022-05-10 03:37:05 +00:00
815e7d169c Bump @percy/playwright from 1.0.2 to 1.0.3 (#5174)
Bumps [@percy/playwright](https://github.com/percy/percy-playwright) from 1.0.2 to 1.0.3.
- [Release notes](https://github.com/percy/percy-playwright/releases)
- [Commits](https://github.com/percy/percy-playwright/compare/v1.0.2...v1.0.3)

---
updated-dependencies:
- dependency-name: "@percy/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>
2022-05-09 20:26:35 -07:00
58387e0902 Prepare for release 2.0.4 (#5176)
* Bump d3-selection from 1.3.2 to 3.0.0

Bumps [d3-selection](https://github.com/d3/d3-selection) from 1.3.2 to 3.0.0.
- [Release notes](https://github.com/d3/d3-selection/releases)
- [Commits](https://github.com/d3/d3-selection/compare/v1.3.2...v3.0.0)

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

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

* Prepare for sprint 2.0.4

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2022-05-09 21:38:49 +00:00
0a0826f87e Bump plotly.js-basic-dist from 2.5.0 to 2.12.0 (#5153)
* Bump plotly.js-basic-dist, plotly-gl2d from 2.5.0 to 2.12.0

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2022-05-09 21:28:43 +00:00
e063442d8c Bump d3-selection from 1.3.2 to 3.0.0 (#5009)
* Bump d3-selection, d3-scale and d3-axis from 1.3.2 to 3.x.x

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Joshi <simplyrender@gmail.com>
2022-05-09 20:58:12 +00:00
6a5823ab5c Fix all e2e tests (#5168)
Fix all e2e tests
2022-05-09 20:25:21 +00:00
0493e5ae3c Bump moment from 2.29.1 to 2.29.3 (#5109)
Bumps [moment](https://github.com/moment/moment) from 2.29.1 to 2.29.3.
- [Release notes](https://github.com/moment/moment/releases)
- [Changelog](https://github.com/moment/moment/blob/2.29.3/CHANGELOG.md)
- [Commits](https://github.com/moment/moment/compare/2.29.1...2.29.3)

---
updated-dependencies:
- dependency-name: moment
  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>
2022-05-07 09:40:10 -07:00
24f13b6249 Time conductor real time 4914 (#5169)
* Created Time counductor input fields real-time mode

* Added click timespan button and click local clock button

* Click time offset button, input time offset in seconds

* Click the check button

* Verify time was updated on start time offset, click preceding now button

* Verify time was updated on preceding time offset button

* Added testing instructions as comment as testcase guide

* Typo in test name

* Updated Verify time was updated on time offset button to awaits

* Updated  Verify time was updated on preceding time offset button to awaits
2022-05-07 16:05:22 +00:00
221fb4d6bf [e2e] Update playwright eslint rules (#5141)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2022-05-06 15:42:49 +00:00
257742b45b Update the path of local.ini (#5165)
Modified the instructions to reference the homebrew location of `local.ini`

Co-authored-by: Scott Bell <scott@traclabs.com>
2022-05-05 13:09:22 -05:00
44edec4f04 [e2e]: added test for creating and moving objects (#5128)
* added test for creating and moving objects

* Refactored and cleaned up test code

* Removed extra await in expect

* Clean up playwright default text in waits and nav

* Finished test file with second test

Co-authored-by: John Hill <john.c.hill@nasa.gov>
2022-05-05 10:39:33 -07:00
ab4d0dd37f [e2e] Fix some of the plot tests (#5158)
* small general fixes

* Rename testsuite and use snapshot alias

* remove only

* Add some more determinism by waiting for Save Banner

* rename

* reduce time to fail

* add determinism

* log the process

Co-authored-by: unlikelyzero <jchill2@gmail.com>
2022-05-04 09:15:39 -07:00
c089a4760d 2.0.3 merge to master (#5157)
* Release 2.0.3

* Fix tick values for plots ticks in log mode and null check (#5119)

* [2297] When there is no display range or range, skip setting the range value when auto scale is turned off.

* If the formatted value is a number and a float, set precision to 2 decimal points.

* Fix value assignment

* Use whole numbers in log mode

* Revert whole numbers fix - need floats for values between 0 and 1.

* Handle scrolling to focused image on resize/new data (#5121)

* Scroll to focused image when view resizes - this will force scrolling to focused image when going to/from view large mode

* Scroll to the right if there is no paused focused image

* [LAD Tables] Use Telemetry Collections (#5127)

* Use telemetry collections to handle bounds checks

* added telemetry collection to alphanumeric telemetry view (#5131)

* Added animation styling for POS and CAM; adjusted cutoff for isNewImage (#5116)

* Added animation styling for POS and CAM; adjusted cutoff for isNewImage

* Remove animation from POS and CAM

* Fix transactions overwriting latest objects with stale objects on save (#5132)

* use object (map) instead of set to track dirty objects
* fix tests due to internals change

Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>

* Gauge edit enabled 2.0.3 (#5133)

* Gauge plugin #4896, add edit mode

* Dynamic dial-type Gauge sizing by height and width (#5129)

* Improve sizing strategy for gauges.
* Do not install gauge by default for now

Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
Co-authored-by: Jamie Vigliotta <jamie.j.vigliotta@nasa.gov>
Co-authored-by: Andrew Henry <akhenry@gmail.com>

* [Telemetry Collections] Include data with start and end bounds (#5145)

* Reverts forced precision for log plots axis labels (#5147)

* Condition Widgets trigger hundreds of persistence calls (#5146)

Co-authored-by: unlikelyzero <jchill2@gmail.com>

Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
Co-authored-by: Michael Rogers <contact@mhrogers.com>
Co-authored-by: David Tsay <3614296+davetsay@users.noreply.github.com>
Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
Co-authored-by: unlikelyzero <jchill2@gmail.com>
2022-05-03 11:09:12 -07:00
b77a4066f2 Use navigator platform to display separate for Linux OS - 4848 (#5115)
* Regex match the linux platform and display separate message

* Added test for different alt test based on OS in userAgent

* Simplify to use full navigator string instead of navigator.platform or userAgentData.platform

* Use userAgent string

* Test.skip depending on OS

* Remove .only after confirming test

* Adjust the skip logic

* Fix Flake

* halfbaked implementation

* Updated test to use os specific hotkeys and check for correct text

* Remove test.only

* Delete old tests

Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
Co-authored-by: unlikelyzero <jchill2@gmail.com>
2022-05-03 17:18:06 +00:00
20d7e80502 Bump github/codeql-action from 1 to 2 (#5110)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 1 to 2.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v1...v2)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  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>
2022-05-03 15:31:51 +00:00
d63fec51a7 [Build] Update CircleCI Dependency to fix flakey downloads (#5123) 2022-05-02 17:34:07 -07:00
d30c4fcb53 Add Gauge plugin #4896, add edit mode (#5118)
* Add Gauge plugin #4896, add edit mode
2022-04-26 14:32:23 -07:00
fff3ce0acf [Telemetry Collection] Telemetry table excluding start and end bound values #5095 (#5096) 2022-04-23 01:49:38 +00:00
db5cb2517f Telemetry Table performance marks (#5107)
Co-authored-by: unlikelyzero <jchill2@gmail.com>
Co-authored-by: David Tsay <3614296+davetsay@users.noreply.github.com>
2022-04-22 17:49:50 -07:00
5236f1c796 Sort and merge incoming telemetry (#5042)
* use sort and merge sorted strategy for incoming data
* add shortcut for merging to beginning or end of existing rows

Co-authored-by: Andrew Henry <akhenry@gmail.com>
2022-04-22 23:51:47 +00:00
1ed253cb07 Show image thumbnails in layout views - 4884 (#5099)
* Only show thumbnails if image view is > 400px high

Co-authored-by: Andrew Henry <akhenry@gmail.com>
Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com>
2022-04-22 16:14:59 -07:00
a6553ba010 Delete gauge.e2eSpec.js (#5105)
Deletes the erroneously committed e2e spec for gauges.

Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2022-04-22 22:20:15 +00:00
cf6bc5be2d Search fix identifier (#4947)
* use identifier not key for object get calls
* re-index on composition or name changes only
* search should account for namespaces

Co-authored-by: Scott Bell <scott@traclabs.com>
Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
2022-04-22 15:15:37 -07:00
a53a3a0297 Add gauge 4896 (#4919)
* Add new Gauge component

Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com>
Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2022-04-22 14:58:08 -07:00
402cd15726 Log plots 2 / custom ticks (#4931)
* add some types to XAxisModel

* Add UI to toggle log mode.

* handle autoscale toggle for logMode

* add log plot tests

* test log ticks work after refresh

* add an initial manually-created visual snapshot test of log plot chart

* update plot unit tests for log mode

* remove scale variable for now

* make v-for keys unique per template to avoid a small performance hazard of v-for markup in the same subtree of a template having clashing keys (Vue quirk)

Co-authored-by: unlikelyzero <jchill2@gmail.com>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2022-04-20 14:28:46 -07:00
a5580912e3 fix a couple flaky tests (#5061)
* fix: forgot to increase maxDiffPixels for one snapshot test, making it more chance to flake. let's see if this work

* hopefully fix PerformanceIndicator test flakes

* hopefully actually fix PerfIndicator test this time

* ok, *finally* fix PerfIndicator test... hopefully...

* simplify PerfIndicator test to check only for positive fps value
2022-04-20 10:41:09 -07:00
54d1b8991c [Build] Add support for node18 (#5091) 2022-04-19 15:05:08 -07:00
7b6acee793 Bump karma-spec-reporter from 0.0.33 to 0.0.34 (#5086)
Bumps [karma-spec-reporter](https://github.com/tmcgee123/karma-spec-reporter) from 0.0.33 to 0.0.34.
- [Release notes](https://github.com/tmcgee123/karma-spec-reporter/releases)
- [Commits](https://github.com/tmcgee123/karma-spec-reporter/compare/v0.0.33...v0.0.34)

---
updated-dependencies:
- dependency-name: karma-spec-reporter
  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>
2022-04-19 21:21:23 +00:00
04e1c60e5c Prepare for 2.0.3 release (#5087)
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2022-04-19 18:53:38 +00:00
91bcd78d40 fix preview by checking before accessing key, fix delay of resize, by using leading:true option of debounce (#5054) 2022-04-19 18:34:26 +00:00
a3c0e073c8 Plots y axis and legend fixes (#5062)
* [5058] Change the unit if the yKey changes after initialization

* [5057] Show y axis label when more than one series is present with the same range value

* Fix typo for model length check

Co-authored-by: David Tsay <3614296+davetsay@users.noreply.github.com>
2022-04-19 17:37:01 +00:00
21ae9f45c1 Bump resolve-url-loader from 4.0.0 to 5.0.0 (#4870)
Bumps [resolve-url-loader](https://github.com/bholloway/resolve-url-loader/tree/HEAD/packages/resolve-url-loader) from 4.0.0 to 5.0.0.
- [Release notes](https://github.com/bholloway/resolve-url-loader/releases)
- [Changelog](https://github.com/bholloway/resolve-url-loader/blob/v5/packages/resolve-url-loader/CHANGELOG.md)
- [Commits](https://github.com/bholloway/resolve-url-loader/commits/5.0.0/packages/resolve-url-loader)

---
updated-dependencies:
- dependency-name: resolve-url-loader
  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>
Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
Co-authored-by: Nikhil Mandlik <nikhil.k.mandlik@nasa.gov>
2022-04-18 15:48:03 -07:00
0a40c8dd0b Bump moment-duration-format from 2.2.2 to 2.3.2 (#5010)
Bumps [moment-duration-format](https://github.com/jsmreese/moment-duration-format) from 2.2.2 to 2.3.2.
- [Release notes](https://github.com/jsmreese/moment-duration-format/releases)
- [Commits](https://github.com/jsmreese/moment-duration-format/compare/2.2.2...2.3.2)

---
updated-dependencies:
- dependency-name: moment-duration-format
  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: John Hill <john.c.hill@nasa.gov>
2022-04-17 20:18:13 +00:00
ef1ea8e712 Bump actions/upload-artifact from 2 to 3 (#5049)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  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>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2022-04-17 19:53:45 +00:00
5c4fad77ff Bump karma from 6.3.17 to 6.3.18 (#5071)
Bumps [karma](https://github.com/karma-runner/karma) from 6.3.17 to 6.3.18.
- [Release notes](https://github.com/karma-runner/karma/releases)
- [Changelog](https://github.com/karma-runner/karma/blob/master/CHANGELOG.md)
- [Commits](https://github.com/karma-runner/karma/compare/v6.3.17...v6.3.18)

---
updated-dependencies:
- dependency-name: karma
  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>
2022-04-16 10:26:40 -07:00
dbac9e6cd2 Bump vue-eslint-parser from 8.2.0 to 8.3.0 (#5065)
Bumps [vue-eslint-parser](https://github.com/vuejs/vue-eslint-parser) from 8.2.0 to 8.3.0.
- [Release notes](https://github.com/vuejs/vue-eslint-parser/releases)
- [Commits](https://github.com/vuejs/vue-eslint-parser/compare/v8.2.0...v8.3.0)

---
updated-dependencies:
- dependency-name: vue-eslint-parser
  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>
2022-04-14 17:22:30 -07:00
4b7bcf9c89 Bump eslint from 8.11.0 to 8.13.0 (#5056)
Bumps [eslint](https://github.com/eslint/eslint) from 8.11.0 to 8.13.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.11.0...v8.13.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: John Hill <john.c.hill@nasa.gov>
2022-04-14 16:39:07 +00:00
2b42abd495 Update package.json (#5050)
Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
2022-04-14 09:10:02 -07:00
1f2102b845 Fix duration to milliseconds converter (#5064) 2022-04-14 08:27:38 -07:00
2ccb90aa41 De-reactify tables (#5046) 2022-04-11 14:34:52 -07:00
525496fbca fix: autoscale turned off could cause errors (#5040)
* fix: autoscale turned off could cause errors

* remove commented code

* add tests for plot ticks

* make sure autoscale tests use a certain window size so they work consistently

* add commented code to use once playwright snapshot testing is fixed

* default the user selected range to the current range prior to when they turn off autoscale

* add snapshot tests for plots autoscale turned off test
2022-04-11 11:22:44 -07:00
47099786cb release 2.0.2 merge to master (#5044)
* Fix version number

* temp remove e2e-ci until percy fix (#5032)

* [Imagery] Improve View Large Action Performance (#5024)

* added the ability to pass the element you would like to enlarge to the view large action
* Example of performance marks (#5027)
Co-authored-by: John Hill <john.c.hill@nasa.gov>
Co-authored-by: unlikelyzero <jchill2@gmail.com>
Co-authored-by: Andrew Henry <andrew.k.henry@nasa.gov>

* [Notebooks] Transactions for entry creation/editing (#4917)

* adding transactions to notebook entry editing

Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
Co-authored-by: Andrew Henry <andrew.k.henry@nasa.gov>

* Revert "temp remove e2e-ci until percy fix (#5032)" (#5047)

This reverts commit 5b4ba7772a.

Co-authored-by: John Hill <john.c.hill@nasa.gov>
Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
Co-authored-by: Andrew Henry <andrew.k.henry@nasa.gov>
2022-04-08 11:35:34 -07:00
3a11291a3b Set flex direction to row reverse to right-align imagery thumbnails (#4934)
* Set flex direction to row reverse so thumbnails are right-aligned

* Flex direction to justify content

Co-authored-by: John Hill <john.c.hill@nasa.gov>
Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2022-04-06 11:45:01 -05:00
476f1b2579 Freshness Indicators (#5002)
* Added animation-delay and animation-duration properties to inline styles

* Accept config options from plugin

* Lint fix

* Lint remove trailing space

* Lint: blank line

* Make default values consistent

* Removal of default css and cleanup

* Updated the default values for image freshness

Co-authored-by: John Hill <john.c.hill@nasa.gov>
2022-04-06 16:26:00 +00:00
6153ad8e1e Add new timelist view and plugin (#4766)
* Add new timelist view and plugin
* Add inspector properties
* calculate list bounds to show/hide events
* Add timer to track 'Now' for timelist
* Styling for Timelist view

Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com>
Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
2022-04-05 14:48:32 -07:00
77c0b16050 [Build] Update broken transitive percy package and override core-js (#5030)
* Update package.json

* Update package.json

* Update package.json

* override percy/cli install and move core-js

* Update package.json

* published fix

* Attempt without specific dependency

* Attempt without specific dependency

* revert

Co-authored-by: unlikelyzero <jchill2@gmail.com>
2022-04-05 13:41:33 -07:00
d19088cec6 Conditional styles for stacked plots (#4965) 2022-03-31 14:47:58 -07:00
43afb39e56 Added soft assertion for exampleImagery e2e (#5021) 2022-03-30 14:16:59 -07:00
cd8c332fb5 Bump @types/jasmine from 3.10.4 to 4.0.1 (#5018)
Bumps [@types/jasmine](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jasmine) from 3.10.4 to 4.0.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jasmine)

---
updated-dependencies:
- dependency-name: "@types/jasmine"
  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>
2022-03-30 21:11:51 +00:00
b899475939 Bump mini-css-extract-plugin from 2.4.5 to 2.6.0 (#4926)
Bumps [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) from 2.4.5 to 2.6.0.
- [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.4.5...v2.6.0)

---
updated-dependencies:
- dependency-name: mini-css-extract-plugin
  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: Nikhil <nikhil.k.mandlik@nasa.gov>
2022-03-30 13:03:12 -07:00
cc1f7659f9 Bump sass-loader from 12.4.0 to 12.6.0 (#4871)
* Bump sass-loader from 12.4.0 to 12.6.0

Bumps [sass-loader](https://github.com/webpack-contrib/sass-loader) from 12.4.0 to 12.6.0.
- [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/v12.4.0...v12.6.0)

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

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

* update sass": "1.49.9"

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
2022-03-30 18:04:40 +00:00
0d5539be96 Bump moment-timezone from 0.5.28 to 0.5.34 (#5005)
Bumps [moment-timezone](https://github.com/moment/moment-timezone) from 0.5.28 to 0.5.34.
- [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.28...0.5.34)

---
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>
2022-03-30 11:00:07 -07:00
0a511e6155 remove file loader as dependency (#5012) 2022-03-30 18:58:26 +02:00
47b6d19de8 Bump webpack-dev-middleware from 3.7.3 to 5.3.1 (#4829)
* Bump webpack-dev-middleware from 3.7.3 to 5.3.1

Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 3.7.3 to 5.3.1.
- [Release notes](https://github.com/webpack/webpack-dev-middleware/releases)
- [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v3.7.3...v5.3.1)

---
updated-dependencies:
- dependency-name: webpack-dev-middleware
  dependency-type: direct:development
  update-type: version-update:semver-major
...

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

* Fixed config to support updated webpack-dev-middleware

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2022-03-30 00:58:49 +00:00
3fd93f47bc Remove html-loader (#4991)
* Bump html-loader from 0.5.5 to 3.1.0

Bumps [html-loader](https://github.com/webpack-contrib/html-loader) from 0.5.5 to 3.1.0.
- [Release notes](https://github.com/webpack-contrib/html-loader/releases)
- [Changelog](https://github.com/webpack-contrib/html-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/html-loader/compare/v0.5.5...v3.1.0)

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

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

* fix asset loading and make clean idempotent

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Scott Bell <scott@traclabs.com>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2022-03-29 23:11:15 +00:00
651e61954c Type annotations (#4789)
* add some types to XAxisModel

* some more type defs and small code tweaks while getting familiar with plots

* more type annotations and a few small tweaks

* more type annotations and small tweaks to make types show

* add mocha types

* Add karma and jasmine, too

* further simplify plot canvas creation

* further simplify plot canvas creation

* update types, avoid runtime behavior in type definition that breaks SeriesCollection

* undo the changes to MctChart, improve it later

* lint fix

Co-authored-by: unlikelyzero <jchill2@gmail.com>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
2022-03-29 14:39:49 -07:00
d30ec4c757 Bump sass from 1.49.0 to 1.49.9 (#4985)
Bumps [sass](https://github.com/sass/dart-sass) from 1.49.0 to 1.49.9.
- [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.49.0...1.49.9)

---
updated-dependencies:
- dependency-name: sass
  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: Andrew Henry <andrew.k.henry@nasa.gov>
2022-03-28 21:01:11 -07:00
3c24733476 Update package.json (#5004) 2022-03-28 17:32:05 -07:00
04d00fac3d relax text assertion to be any number (#5001) 2022-03-28 11:20:55 -07:00
150909d4b9 [Build] Remove testing and support for node12 (#4963)
* Remove all node12 testing other than platform

* add lighthouse to our deps now that 12 is deprecated

* removing 12 from platform pr

* Updated config.yml

Co-authored-by: unlikelyzero <jchill2@gmail.com>
Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2022-03-25 18:11:41 +00:00
2b599a7ff4 Fix Cursor Grab while panning (#4957)
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2022-03-25 11:07:05 -07:00
824a597825 remove raw-loader as a dependency (#4998)
* remove raw-loader as a dependency

* move clock to correct dir and response to canvas clicking
2022-03-25 09:13:10 -07:00
cf5edf2db0 [clock] Timezone dropdown will collapse when clicked outside or on dropdown icon again (#4956)
* Fix timezone dropdown collapse issue

* Dropdown should collapse when click outside

* Fix Lint error

* add e2e test for autocomplete

* updates based of code review

* Typo fixed

* Modification based on review

Co-authored-by: Scott Bell <scott@traclabs.com>
2022-03-24 13:10:32 +01:00
0705d321da invert npmignore to include what we want (#4605)
* invert npmignore to include what we want
* remove line for no-longer-existent src/**/*.spec.js files from npmignore
* cleanup npmignore comments
* remove platform dirs from npmignore, they were removed from the repo
* remove one more platform reference from npmignore
* publish example/ and app.js, at least for now

Co-authored-by: John Hill <john.c.hill@nasa.gov>
Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
2022-03-23 22:37:36 +00:00
dad0768d57 Prepare for sprint 2.0.2 (#4990)
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2022-03-23 22:32:37 +00:00
9643dbe918 Bump @percy/cli from 1.0.0-beta.75 to 1.0.0-beta.76 (#4989)
Bumps [@percy/cli](https://github.com/percy/cli/tree/HEAD/packages/cli) from 1.0.0-beta.75 to 1.0.0-beta.76.
- [Release notes](https://github.com/percy/cli/releases)
- [Commits](https://github.com/percy/cli/commits/v1.0.0-beta.76/packages/cli)

---
updated-dependencies:
- dependency-name: "@percy/cli"
  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>
2022-03-23 22:00:32 +00:00
594f9d3e9a 2.0.1 release merged into master (#4971)
* Correctly use creatable attribute and persistability when working with domainObjects (#4898) (#4936)

* making move action location check persistability

* adding persistence check instead of creatability for styles

* added check for link action to make sure parent is persistable

* debug

* adding parent to link action and move action form location controls so they can be used in the form

* adding parent persistability check for duplicate

* updating multilple actions appliesTo methods to check for persistability

* updated the tree to not require an initial selection if being used in a form

* remove noneditable folder plugin

* added persistence check for the parent, in the create wizard

* minor name change

* removing noneditabl folder from default plugins as well

* checking the correct parent for persistability in create wizard

* importing file-saver correctly

* updated tests for import as json

* changes addressing PR review: using consts, removing comments, removing unneccessary code

Co-authored-by: Scott Bell <scott@traclabs.com>

Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
Co-authored-by: Scott Bell <scott@traclabs.com>

* Fix display layout items getting cut off on the bottom (like plots) (#4903)

* Fix display layout items getting cut off on the bottom (like plots)
Also fix Vue warnings

* Add partial e2e test for this bug fix. WIP.

* Address review comments

Co-authored-by: John Hill <john.c.hill@nasa.gov>

* Link action fix (#4945)

* handling edge case for linking a root item

* added location to viper plans (couch search folder) set to ROOT, added a check to remove action for alias (so you can remove linked nonpersistable items)

* added check for no parent in remove action (which means it is a root item)

* updating test

* Update time conductor inputs realtime (#4877)

* Update time conductor inputs realtime

* Update moveObjects.e2e.spec.js

* Update importAsJson.e2e.spec.js

* Update default.spec.js

Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
Co-authored-by: Scott Bell <scott@traclabs.com>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2022-03-23 14:53:39 -07:00
0f9e727675 34 - Image Pan and Zoom (#4736) 2022-03-23 21:10:50 +00:00
0cf30940c8 Bump karma-chrome-launcher from 3.1.0 to 3.1.1 (#4987)
Bumps [karma-chrome-launcher](https://github.com/karma-runner/karma-chrome-launcher) from 3.1.0 to 3.1.1.
- [Release notes](https://github.com/karma-runner/karma-chrome-launcher/releases)
- [Changelog](https://github.com/karma-runner/karma-chrome-launcher/blob/master/CHANGELOG.md)
- [Commits](https://github.com/karma-runner/karma-chrome-launcher/compare/v3.1.0...v3.1.1)

---
updated-dependencies:
- dependency-name: karma-chrome-launcher
  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>
2022-03-23 19:37:49 +00:00
3a2381b90b Bump webpack-hot-middleware from 2.22.3 to 2.25.1 (#4969)
Bumps [webpack-hot-middleware](https://github.com/webpack-contrib/webpack-hot-middleware) from 2.22.3 to 2.25.1.
- [Release notes](https://github.com/webpack-contrib/webpack-hot-middleware/releases)
- [Commits](https://github.com/webpack-contrib/webpack-hot-middleware/compare/v2.22.3...v2.25.1)

---
updated-dependencies:
- dependency-name: webpack-hot-middleware
  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>
2022-03-23 18:30:55 +00:00
94def56bcb Bump request from 2.69.0 to 2.88.2 (#4982)
Bumps [request](https://github.com/request/request) from 2.69.0 to 2.88.2.
- [Release notes](https://github.com/request/request/releases)
- [Changelog](https://github.com/request/request/blob/master/CHANGELOG.md)
- [Commits](https://github.com/request/request/commits)

---
updated-dependencies:
- dependency-name: request
  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: John Hill <john.c.hill@nasa.gov>
2022-03-23 16:21:04 +00:00
93a796353d Bump printj from 1.2.1 to 1.3.1 (#4981)
Bumps [printj](https://github.com/SheetJS/printj) from 1.2.1 to 1.3.1.
- [Release notes](https://github.com/SheetJS/printj/releases)
- [Commits](https://github.com/SheetJS/printj/commits)

---
updated-dependencies:
- dependency-name: printj
  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>
2022-03-23 08:03:37 -07:00
18ff16052a Bump jasmine-core from 4.0.0 to 4.0.1 (#4980)
Bumps [jasmine-core](https://github.com/jasmine/jasmine) from 4.0.0 to 4.0.1.
- [Release notes](https://github.com/jasmine/jasmine/releases)
- [Changelog](https://github.com/jasmine/jasmine/blob/main/RELEASE.md)
- [Commits](https://github.com/jasmine/jasmine/compare/v4.0.0...v4.0.1)

---
updated-dependencies:
- dependency-name: jasmine-core
  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>
2022-03-22 21:00:28 -07:00
f1c7fa337d Bump raw-loader from 0.5.1 to 4.0.2 (#4978)
Bumps [raw-loader](https://github.com/webpack-contrib/raw-loader) from 0.5.1 to 4.0.2.
- [Release notes](https://github.com/webpack-contrib/raw-loader/releases)
- [Changelog](https://github.com/webpack-contrib/raw-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/raw-loader/commits/v4.0.2)

---
updated-dependencies:
- dependency-name: raw-loader
  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>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2022-03-23 03:29:04 +00:00
557f29f9bb bump git-rev-sync and add tests (#4959)
Co-authored-by: unlikelyzero <jchill2@gmail.com>
2022-03-22 20:20:39 -07:00
255ebc9a37 Bump core-js from 3.20.3 to 3.21.1 (#4970)
Bumps [core-js](https://github.com/zloirock/core-js) from 3.20.3 to 3.21.1.
- [Release notes](https://github.com/zloirock/core-js/releases)
- [Changelog](https://github.com/zloirock/core-js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/zloirock/core-js/compare/v3.20.3...v3.21.1)

---
updated-dependencies:
- dependency-name: core-js
  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>
2022-03-22 18:03:09 -07:00
ca7fbe58e3 code coverage with babel istanbul (#4649)
* WIP add instanbul code coverage to vue files

* move webpack coverage config to a separate file for test:coverage, pin dependencies, remove istanbul-instrumenter-loader

* dont include node_modules in babel config, it breaks istanbul for some reason

* ignore spec files from coverage

* document coverage files, remove unused karma config

* use test instead of test:coverage in config.yml, disable code and link to issue to enable coverage in Vue <template>s
2022-03-22 16:04:23 -07:00
b347f35892 Bump file-loader from 6.1.0 to 6.2.0 (#4834)
Bumps [file-loader](https://github.com/webpack-contrib/file-loader) from 6.1.0 to 6.2.0.
- [Release notes](https://github.com/webpack-contrib/file-loader/releases)
- [Changelog](https://github.com/webpack-contrib/file-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/file-loader/compare/v6.1.0...v6.2.0)

---
updated-dependencies:
- dependency-name: file-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>
Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2022-03-22 13:33:02 -07:00
28d5d72834 Event Generator now shows data in fixed time mode (#4883)
* fixed issue and added tests
* exclude leaky test
* ignore case when looking for testing instructions
2022-03-21 15:37:49 -07:00
0df33730f7 on route change, calling showTab (#4955) 2022-03-21 16:29:47 -05:00
7b2ff8fa15 [Docs] add browserlist and linting capability (#4811)
* add browserlist to package.json

* add browserlist linting

* add eslint compat package

* Add blurb about browser support

* add ios safari

* remove node

* add comment and simple disable

Co-authored-by: unlikelyzero <jchill2@gmail.com>
Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
Co-authored-by: Scott Bell <scott@traclabs.com>
2022-03-21 20:27:18 +01:00
d80b692354 Update eslint (#4554)
Bumps [eslint-plugin-vue](https://github.com/vuejs/eslint-plugin-vue) from 7.20.0 to 8.0.3.
- [Release notes](https://github.com/vuejs/eslint-plugin-vue/releases)
- [Commits](https://github.com/vuejs/eslint-plugin-vue/compare/v7.20.0...v8.0.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
* bump eslint to 8.11.0
* bump eslint-plugin-vue to 8.5.0
* disable eslint rule for multi-word component names. TODO enable it and follow conventions

Co-authored-by: Nikhil Mandlik <nikhil.k.mandlik@nasa.gov>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
Co-authored-by: unlikelyzero <jchill2@gmail.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2022-03-21 11:40:35 -07:00
4205abdc80 Add Example Generator end to end test (#4949)
* add test

* added name to test and changed comment
2022-03-18 06:57:50 -07:00
492ff2fa64 [e2e] Persistence tests (#4943)
* initial setup for createButton e2e test

* insignificant change

* export as json

* import tests

* added first of two tests

* add stub

* fixme

* move objects

* added test for menu options for non persistable items

* removing "only" for debuggin test

* removing debug code

* soft assertion for multiple expects

Co-authored-by: unlikelyzero <jchill2@gmail.com>
2022-03-17 14:30:45 -07:00
93a81a1369 [CI] Update GHA to include a specific playwright dependency version (#4944)
* Update e2e-pr.yml

* Update e2e-pr.yml

* Update e2e-pr.yml

* visual

Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
2022-03-16 22:05:50 +00:00
482d8f392c Domain object properties validation not working always (#4893)
* Domain object properties validation not working always #4849

* stub of a test

* set local execution to chrome

* skip visual test

* use input event for changes and throttle update.

* moved file to correct location

* fixed e2e tests.

Co-authored-by: unlikelyzero <jchill2@gmail.com>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2022-03-16 21:53:58 +00:00
67234c70a4 [Build] 🐰🐰🐰 Remove the last carrots 🐰🐰🐰 (#4941)
* [Build] 🐰🐰🐰 Remove the last carrots 🐰🐰🐰

* Update package.json

Co-authored-by: Khalid Adil <khalidadil29@gmail.com>
2022-03-16 21:44:41 +00:00
e9680e975f Form validation error messages are not being displayed properly (#4887)
* Form validation error messages are not being displayed properly #4697

* e2e Tests

* fixed element.checkValidity test

* remove comment

* adding timeconductor tests

* rename

Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
Co-authored-by: unlikelyzero <jchill2@gmail.com>
2022-03-16 13:54:46 -07:00
e691a89682 Correctly use creatable attribute and persistability when working with domainObjects (#4898)
* making move action location check persistability

* adding persistence check instead of creatability for styles

* added check for link action to make sure parent is persistable

* debug

* adding parent to link action and move action form location controls so they can be used in the form

* adding parent persistability check for duplicate

* updating multilple actions appliesTo methods to check for persistability

* updated the tree to not require an initial selection if being used in a form

* remove noneditable folder plugin

* added persistence check for the parent, in the create wizard

* minor name change

* removing noneditabl folder from default plugins as well

* checking the correct parent for persistability in create wizard

* importing file-saver correctly

* updated tests for import as json

* changes addressing PR review: using consts, removing comments, removing unneccessary code

Co-authored-by: Scott Bell <scott@traclabs.com>
2022-03-14 21:20:06 +01:00
bcd668594d Add source maps to map plan properties to expected properties (#4933) 2022-03-11 10:40:05 -08:00
5471e13d9e Bump @braintree/sanitize-url from 5.0.2 to 6.0.0 (#4928)
Bumps [@braintree/sanitize-url](https://github.com/braintree/sanitize-url) from 5.0.2 to 6.0.0.
- [Release notes](https://github.com/braintree/sanitize-url/releases)
- [Changelog](https://github.com/braintree/sanitize-url/blob/main/CHANGELOG.md)
- [Commits](https://github.com/braintree/sanitize-url/compare/v5.0.2...v6.0.0)

---
updated-dependencies:
- dependency-name: "@braintree/sanitize-url"
  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>
2022-03-09 19:53:39 -08:00
dae446808e Update with CRUD Operation (#4930)
Co-authored-by: unlikelyzero <jchill2@gmail.com>
2022-03-09 06:45:10 -08:00
ee9e47f487 Bump @playwright/test from 1.19.1 to 1.19.2 (#4905)
Bumps [@playwright/test](https://github.com/Microsoft/playwright) from 1.19.1 to 1.19.2.
- [Release notes](https://github.com/Microsoft/playwright/releases)
- [Commits](https://github.com/Microsoft/playwright/compare/v1.19.1...v1.19.2)

---
updated-dependencies:
- dependency-name: "@playwright/test"
  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>
2022-03-08 18:49:48 -08:00
003b1ffede Bump actions/setup-node from 2 to 3 (#4900)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 2 to 3.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-type: direct:production
  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>
2022-03-07 18:33:18 -08:00
16cb5f3911 [CI] Remove warning output for eslint on platform workflow (#4906) 2022-03-03 14:07:54 -08:00
74b83903c9 Bump actions/checkout from 2 to 3 (#4904)
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  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>
2022-03-02 09:59:13 -08:00
6a470fba1a Static root plugin not loading after namespace/key changes in OpenMCT #4684
Static root plugin not loading after namespace/key changes in OpenMCT
2022-02-28 11:51:34 -08:00
4e7debabb1 [Notebook] Add active user to entries (#4764)
* if user provider, user added to notebook enntries and snapshot entries, updated code to work with asynnc nature of user api

Co-authored-by: Andrew Henry <akhenry@gmail.com>
Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
2022-02-22 12:37:47 -08:00
384e36920c 3175 - Enable listening to clearData action for Imagery (#4733)
* Add clearData listener for imageryData module

* Remove commented out code

* Updated imagery clear data test

* Adjusted telemetry stub to return empty array if data cleared

* Remove forced test

* Restub telemetry before

* Cleanup and reset clear data boolean after

* Remove double blank line

Co-authored-by: Scott Bell <scott@traclabs.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2022-02-18 08:48:48 -06:00
d4429f9686 Bump cross-env from 6.0.3 to 7.0.3 (#4837)
Bumps [cross-env](https://github.com/kentcdodds/cross-env) from 6.0.3 to 7.0.3.
- [Release notes](https://github.com/kentcdodds/cross-env/releases)
- [Changelog](https://github.com/kentcdodds/cross-env/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kentcdodds/cross-env/compare/v6.0.3...v7.0.3)

---
updated-dependencies:
- dependency-name: cross-env
  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>
2022-02-17 10:34:14 -08:00
0b2d08078b [CI] Add windows OS and Mac OS to CI testing (#4840)
* Update e2e tests to run on windows and linux

* Add pr-platform to run against all supported archictures

* Update dependabot to run pr:platform

* Update to run in fail-fast false

* remove x86

* Update pr-platform.yml

Co-authored-by: unlikelyzero <jchill2@gmail.com>
2022-02-17 14:52:46 +00:00
3bbc9e1582 [CI] Enable Per PR Lighthouse CI Execution, Run nightly (#4817)
* [CI] Add lighthouse to our deps to track with dependabot

lighthouse ci is still actively maintained and will be following a traditional release model. We should continue to use this package until it's functionality is replaced in playwright

* Add lighthouse to dependencies to track with dependabot

* Allow lighthouse to be triggered from PRs

* Update lighthouse.yml

* add lhci scrript

* bump to 16

* remove from deps until node 18

* document steps and add caching. Revert to 14

* ignore exit codes

* add secret for app

* remove env for baseline

Co-authored-by: unlikelyzero <jchill2@gmail.com>
Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
2022-02-16 17:12:00 -08:00
af0420361b [CI] Update playwright to 1.19.1 (#4873)
* [CI] Update playwright to 1.19.1

* Update config.yml
2022-02-16 15:12:09 -08:00
896f0ca3f4 Bump moment from 2.25.3 to 2.29.1 (#4869)
Bumps [moment](https://github.com/moment/moment) from 2.25.3 to 2.29.1.
- [Release notes](https://github.com/moment/moment/releases)
- [Changelog](https://github.com/moment/moment/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/moment/moment/compare/2.25.3...2.29.1)

---
updated-dependencies:
- dependency-name: moment
  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>
2022-02-16 11:22:51 -08:00
28c5405a01 Bump actions/github-script from 5 to 6 (#4853)
Bumps [actions/github-script](https://github.com/actions/github-script) from 5 to 6.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-type: direct:production
  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>
2022-02-15 15:31:06 -08:00
d114353556 [CI] Update PR Cop to run on more events (#4855)
* [CI] Update PR Cop to run on more events

* Update prcop.yml

* Update prcop.yml

* Update e2e PR

* Update e2e-visual.yml
2022-02-15 14:13:52 -08:00
f40398807e [CI] Update CI to use node16 by default (#4868)
* Update node versions to 16

* New Line

Co-authored-by: unlikelyzero <jchill2@gmail.com>
2022-02-15 14:08:53 -08:00
81f440e1e6 [Docs] Update Github Bug Report Template with envinfo (#4858)
* [Docs] Update Github Bug Report Template with envinfo

* Moving Impact Checklist to bottom so that folks can file and click without markdown x

* Add to npm run scripts
2022-02-15 14:00:37 -08:00
c6d6400131 [CI] Bump playwright to 1.19.0 (#4856)
* bump playwright

* remove playwright dependency and leave playwrite/test

Co-authored-by: unlikelyzero <jchill2@gmail.com>
2022-02-15 10:57:24 -06:00
55828af1ec Bump file-saver from 1.3.8 to 2.0.5 (#4830)
* Bump file-saver from 1.3.8 to 2.0.5

Bumps [file-saver](https://github.com/eligrey/FileSaver.js) from 1.3.8 to 2.0.5.
- [Release notes](https://github.com/eligrey/FileSaver.js/releases)
- [Changelog](https://github.com/eligrey/FileSaver.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eligrey/FileSaver.js/commits)

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

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

* fix paths

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Scott Bell <scott@traclabs.com>
2022-02-15 14:07:58 +01:00
34b951f4c6 Bumped dependency versions to fix build issues in Node 16 and later (#4866)
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2022-02-14 17:15:21 -08:00
a7d4006fee Sprint 2.0.1 (#4861)
* Prep for release 1.8.6

* Update version to 2.0.1
2022-02-14 13:00:32 -08:00
a71485f820 Prep for release 1.8.6 (#4859) 2022-02-14 10:13:08 -08:00
058259278c Remove legacy codebase (#4844)
* Remove legacy codebase

* Remove legacy docs

* Fixed memory leak in timer spec

* Remove Angular dependency

* Removed adapter layer

* Removed legacy support plugin from main Open MCT repo

* Restored TelemetryAPI.js which had been inexplicably deleted?

* Fix linting error

* Drop line coverage threshold due to removed code

* Added a section on Open MCT 2.0.0 to the readme
2022-02-11 11:09:58 -08:00
345285b966 [Build] Enforce engine at build time (#4846)
* lock engine

* pin jsdoc to latest to support larger node range

Co-authored-by: unlikelyzero <jchill2@gmail.com>
2022-02-10 11:04:30 -08:00
ce98c10145 Separate out object name checking from the sorting function (#4677) 2022-02-09 17:59:56 +00:00
22604220de Bump sinon from 12.0.1 to 13.0.1 (#4835)
Bumps [sinon](https://github.com/sinonjs/sinon) from 12.0.1 to 13.0.1.
- [Release notes](https://github.com/sinonjs/sinon/releases)
- [Changelog](https://github.com/sinonjs/sinon/blob/master/docs/changelog.md)
- [Commits](https://github.com/sinonjs/sinon/compare/v12.0.1...v13.0.1)

---
updated-dependencies:
- dependency-name: sinon
  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>
2022-02-08 10:59:21 -08:00
548afda6d7 Bump karma from 6.3.13 to 6.3.15 (#4824)
Bumps [karma](https://github.com/karma-runner/karma) from 6.3.13 to 6.3.15.
- [Release notes](https://github.com/karma-runner/karma/releases)
- [Changelog](https://github.com/karma-runner/karma/blob/master/CHANGELOG.md)
- [Commits](https://github.com/karma-runner/karma/compare/v6.3.13...v6.3.15)

---
updated-dependencies:
- dependency-name: karma
  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>
2022-02-08 10:55:26 -08:00
88c96675e4 Bump karma-coverage from 2.1.0 to 2.1.1 (#4823)
Bumps [karma-coverage](https://github.com/karma-runner/karma-coverage) from 2.1.0 to 2.1.1.
- [Release notes](https://github.com/karma-runner/karma-coverage/releases)
- [Changelog](https://github.com/karma-runner/karma-coverage/blob/master/CHANGELOG.md)
- [Commits](https://github.com/karma-runner/karma-coverage/compare/v2.1.0...v2.1.1)

---
updated-dependencies:
- dependency-name: karma-coverage
  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>
2022-02-08 10:47:16 -08:00
10b97d83ab Bump eslint-plugin-you-dont-need-lodash-underscore from 6.10.0 to 6.12.0 (#4833)
Bumps [eslint-plugin-you-dont-need-lodash-underscore](https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore) from 6.10.0 to 6.12.0.
- [Release notes](https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore/releases)
- [Commits](https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore/compare/v6.10.0...v6.12.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-you-dont-need-lodash-underscore
  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: John Hill <john.c.hill@nasa.gov>
2022-02-08 18:43:22 +00:00
7c48ff9088 Bump @playwright/test from 1.18.0 to 1.18.1 (#4825)
Bumps [@playwright/test](https://github.com/Microsoft/playwright) from 1.18.0 to 1.18.1.
- [Release notes](https://github.com/Microsoft/playwright/releases)
- [Commits](https://github.com/Microsoft/playwright/compare/v1.18.0...v1.18.1)

---
updated-dependencies:
- dependency-name: "@playwright/test"
  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>
2022-02-08 18:02:30 +00:00
839e210ac1 Fixes #4810 (#4815)
- Add objectTypeClass property to allow new CSS selector to target Plan object types in Timestrip views.;

Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2022-02-07 21:57:46 +00:00
7209104d05 Fix code scanning alert - Client-side URL redirect #4757 (#4790)
Co-authored-by: John Hill <john.c.hill@nasa.gov>
Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2022-02-07 13:45:14 -08:00
869c2ab16c [CI] Dependabot to run on all packages. Pin Packages. Remove unused packages (#4795)
* [CI] Dependabot to run on all packages
* Pin dependencies
Co-authored-by: unlikelyzero <jchill2@gmail.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2022-02-07 12:53:08 -08:00
e434b294b9 Bump karma from 6.3.11 to 6.3.13 (#4816)
Bumps [karma](https://github.com/karma-runner/karma) from 6.3.11 to 6.3.13.
- [Release notes](https://github.com/karma-runner/karma/releases)
- [Changelog](https://github.com/karma-runner/karma/blob/master/CHANGELOG.md)
- [Commits](https://github.com/karma-runner/karma/compare/v6.3.11...v6.3.13)

---
updated-dependencies:
- dependency-name: karma
  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>
2022-02-01 12:15:09 -08:00
48b66e0af2 [CI] Update playwright to 1.18.1 (#4805)
* Update package.json
* Update config.yml
2022-01-31 16:19:41 -08:00
54ef9a830e Show all options when clicking arrow in autocomplete form - Mct4578 (#4708)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
2022-01-28 13:24:04 -08:00
cd38545267 [Build]npm run clean uses absolute node_modules directory instead of relative (#4801) 2022-01-27 11:00:01 -08:00
8aed21d670 [CI] Update github actions automation to work across forks (#4799)
* Update e2e-pr.yml

* Update e2e-pr.yml
2022-01-27 08:41:33 -08:00
b27f24cc30 Merge release 1.8.4 into master (#4797)
* Update version number (#4759)

* update copyright (#4775)

* Observe changes to sub-objects in flexible layouts. (#4780)

* Get child of flex layout as mutable if possible

* Fix bug when no default notebook defined

* Follow domain object changes for Independent time conductor (#4783)

* Track if domain object changes when independent time conductor is in use.

* fix typo when using fallback template (#4784)

Co-authored-by: Scott Bell <scott@traclabs.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2022-01-27 06:32:47 -08:00
a7df8bf168 Bump eslint-plugin-playwright from 0.7.1 to 0.8.0 (#4792)
Bumps [eslint-plugin-playwright](https://github.com/playwright-community/eslint-plugin-playwright) from 0.7.1 to 0.8.0.
- [Release notes](https://github.com/playwright-community/eslint-plugin-playwright/releases)
- [Commits](https://github.com/playwright-community/eslint-plugin-playwright/compare/v0.7.1...v0.8.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-playwright
  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>
2022-01-26 12:34:39 -08:00
ff269ac390 [Docs] Update PR Template warning (#4791)
* [Docs] Update PR Template warning
2022-01-26 19:55:55 +00:00
cdee5e8102 [CI] Update PR Template to remove the colon from Closes (#4785) 2022-01-26 11:34:07 -08:00
159457a52d [Build] Update minimum node12 version (#4779) 2022-01-25 14:06:15 -08:00
d637420da1 Mct4467 (#4468)
* dynamic grid display
* add one more guard
2022-01-25 12:56:51 -08:00
eb4da293c6 [CI]Update dependabot rules again (#4762)
* Update dependabot rules again

* update safeword

Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2022-01-25 14:10:00 +00:00
738fac64b8 Bump vue-eslint-parser from 8.1.0 to 8.2.0 (#4770)
Bumps [vue-eslint-parser](https://github.com/vuejs/vue-eslint-parser) from 8.1.0 to 8.2.0.
- [Release notes](https://github.com/vuejs/vue-eslint-parser/releases)
- [Commits](https://github.com/vuejs/vue-eslint-parser/compare/v8.1.0...v8.2.0)

---
updated-dependencies:
- dependency-name: vue-eslint-parser
  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>
2022-01-24 15:57:30 -08:00
7703ec0a61 Bump vue-eslint-parser from 8.0.1 to 8.1.0 (#4761)
Bumps [vue-eslint-parser](https://github.com/vuejs/vue-eslint-parser) from 8.0.1 to 8.1.0.
- [Release notes](https://github.com/vuejs/vue-eslint-parser/releases)
- [Commits](https://github.com/vuejs/vue-eslint-parser/compare/v8.0.1...v8.1.0)

---
updated-dependencies:
- dependency-name: vue-eslint-parser
  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>
2022-01-21 13:36:26 -08:00
52466999b8 Prepare for sprint 1.8.5 (#4760) 2022-01-20 18:57:29 -08:00
45373c56f7 [MCT Tree] Enhance to be used as selection tree as well (#4734)
* removed selector tree, using mct-tree for selctor now, updated style view to use new forms api, update mct-tree to be a selector if need be

* added some extra calculations for height when the tree is being used as a selector in forms

Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2022-01-21 02:46:40 +00:00
91e909bb4a Fix notebook sync (#4738)
* Do not keep fetching default notebook

* Fixes issue where notebooks would stop listening for remote changes.

* Fix issue with notebook merge conflicts
2022-01-20 18:43:17 -08:00
556f762d20 Fix CouchDB search duplicates - Mct4667 (#4688)
* added separate search provider
* add unit test
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2022-01-20 16:00:06 -08:00
e14b7cd0e2 [UserAPI] [UserIndicator Plugin] [ExampleUserProvider Plugin] New user api and related functionality (#4538)
* Implements User API and example user plugin

Co-authored-by: John Hill <john.c.hill@nasa.gov>
Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2022-01-20 13:56:17 -08:00
b1cffd0df3 [CI] Dependabot and PR Cop have conflicts (#4756)
This updates the PR to include a "safe word" for pr cop to ignore PRs opened by the dependabot user
2022-01-20 13:25:57 -08:00
09f25bc525 Bump makaroni4/prcop from 1.0.31 to 1.0.35 (#4755)
Bumps [makaroni4/prcop](https://github.com/makaroni4/prcop) from 1.0.31 to 1.0.35.
- [Release notes](https://github.com/makaroni4/prcop/releases)
- [Commits](https://github.com/makaroni4/prcop/compare/v1.0.31...v1.0.35)

---
updated-dependencies:
- dependency-name: makaroni4/prcop
  dependency-type: direct:production
  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>
2022-01-20 20:56:33 +00:00
bd406d1a73 update playwright dependencies (#4749)
Co-authored-by: unlikelyzero <jchill2@gmail.com>
2022-01-20 20:33:01 +00:00
03380e1846 Snapshot tray is sometimes obscured by long notebooks #4746
also fixes one of the issue mentioned in #3529

Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
2022-01-20 12:05:29 -08:00
dcaad40064 release 1.8.3 - master (#4752)
* Fix object creation (#4675)

* Save the object before adding it to the parent so that transaction committing works properly
* Fix object creation - composition policy changes

* use relative path (#4683)

* Notebook Snapshotting to the default Notebook isn't working (#4475)

* Notebook Snapshotting to the default Notebook isn't working #4469

* Check for ref when trying to get the style receiver. (#4687)

Also ensure that the property being displayed in Properties is actually a part of the domainObject.

* Mct 4555 rebased v3 (#4689)

* Preserve the previousFocusedImage for subscription updates, bound change for local and fixed time

* Only preserve previous focused image if paused

* Forcibly reset imageContainer size to prevent aspect ratio distortion

* Remove unneccesary mixin invocation

* Use image history instead of imagehistory size for watcher. Revert other changes

* Added check if last image index is selected

* isPaused instead of paused

Co-authored-by: Michael Rogers <contact@mhrogers.com>

* Snapshot notice link not navigating as expected #4194 (#4686)

* Fix Link Action to accept the right input for validation (#4696)

* Remove snapshot from release version (#4721)

Co-authored-by: David Tsay <3614296+davetsay@users.noreply.github.com>
Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
Co-authored-by: Michael Rogers <contact@mhrogers.com>
2022-01-20 11:09:46 -08:00
cf47f7c1b2 Task automation and test improvement (#4673)
* Run full regression suite on PR
* Limit concurrency to 2 workers
* Update PULL_REQUEST_TEMPLATE.md
* Added prcop config

Co-authored-by: unlikelyzero <jchill2@gmail.com>
Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2022-01-20 10:31:06 -08:00
ba2a6030c2 Re-implement the event generator as a new-style plugin - Mct4552 (#4620)
Updates Event Generator to ES6 classes and current API.

Co-authored-by: Andrew Henry <akhenry@gmail.com>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2022-01-20 10:23:40 -08:00
384af89d4c Address marked Security Vulnerability (#4725)
More information here https://github.com/advisories/GHSA-5v2h-r2cx-5xgj

Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
2022-01-20 06:17:40 -08:00
324318793d Include note about node version support in the intro install guide (#4732) 2022-01-19 11:45:57 -08:00
f211d3fab0 Reset independent time when domain object changes (#4723) 2022-01-18 12:25:50 -08:00
6aaf4a2c06 Add projects to local config and comment out ipad (#4727)
Co-authored-by: unlikelyzero <jchill2@gmail.com>
Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
2022-01-18 11:53:05 -08:00
4a9744e916 touch every file on earth to update copyright (#4729)
* touch every file on earth to update copyright

* update one more

* pick up 2017 2018 2019

* revert fuchs copyright

* revert other bundle copyrights

* somehow missed one
2022-01-18 11:27:28 -08:00
05b73bb654 Script to update all copyright dates - Mct4532 (#4715)
* wrote little script

* wrote little script

* added script to update copyright

* include html

* include md

* update script

* captures all copyright now

Co-authored-by: John Hill <john.c.hill@nasa.gov>
2022-01-18 09:52:24 -08:00
19b3be7ec0 [e2e] Update Playwright config to include MMOC and Ipad (#4717)
Co-authored-by: unlikelyzero <jchill2@gmail.com>
2022-01-14 14:47:35 -08:00
c39593b065 Bump karma from 6.3.10 to 6.3.11 (#4718)
Bumps [karma](https://github.com/karma-runner/karma) from 6.3.10 to 6.3.11.
- [Release notes](https://github.com/karma-runner/karma/releases)
- [Changelog](https://github.com/karma-runner/karma/blob/master/CHANGELOG.md)
- [Commits](https://github.com/karma-runner/karma/compare/v6.3.10...v6.3.11)

---
updated-dependencies:
- dependency-name: karma
  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>
2022-01-14 10:42:25 -08:00
f62d834384 Update version for sprint (#4724) 2022-01-14 07:43:03 -08:00
7a7202f75b Exclude Spec files from CodeCov - mct4563 (#4714) 2022-01-13 17:07:57 -08:00
6bcc9bfd84 turn off errors from type checking in IDEs by default. Turn it on locally to type check (#4705)
Co-authored-by: Joe Pea <joe.pea@nasa.gov>
2022-01-11 11:40:51 -08:00
1269 changed files with 28227 additions and 62864 deletions

View File

@ -2,7 +2,7 @@ version: 2.1
executors:
pw-focal-development:
docker:
- image: mcr.microsoft.com/playwright:v1.17.2-focal
- image: mcr.microsoft.com/playwright:v1.21.1-focal
environment:
NODE_ENV: development # Needed to ensure 'dist' folder created and devDependencies installed
parameters:
@ -23,7 +23,7 @@ commands:
- node/install:
install-npm: true
node-version: << parameters.node-version >>
- run: npm install
- run: npm install --prefer-offline --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:
@ -31,7 +31,7 @@ commands:
type: string
steps:
- when:
condition:
condition:
equal: [false, << pipeline.parameters.BUST_CACHE >> ]
steps:
- restore_cache:
@ -41,7 +41,7 @@ commands:
parameters:
node-version:
type: string
steps:
steps:
- save_cache:
key: deps-{{ .Branch }}--<< parameters.node-version >>--{{ checksum "package.json" }}-{{ checksum ".circleci/config.yml" }}
paths:
@ -61,10 +61,10 @@ commands:
upload_code_covio:
description: "Command to upload code coverage reports to codecov.io"
steps:
- run: curl -Os https://uploader.codecov.io/latest/linux/codecov;chmod +x codecov;./codecov
- run: curl -Os https://uploader.codecov.io/latest/linux/codecov;chmod +x codecov;./codecov
orbs:
node: circleci/node@4.9.0
browser-tools: circleci/browser-tools@1.2.3
browser-tools: circleci/browser-tools@1.3.0
jobs:
npm-audit:
parameters:
@ -76,7 +76,7 @@ jobs:
node-version: <<parameters.node-version>>
- run: npm audit --audit-level=low
- generate_and_store_version_and_filesystem_artifacts
node14-lint:
lint:
parameters:
node-version:
type: string
@ -101,7 +101,7 @@ jobs:
equal: [ "FirefoxESR", <<parameters.browser>> ]
steps:
- browser-tools/install-firefox:
version: "91.4.0esr" #https://archive.mozilla.org/pub/firefox/releases/
version: "91.7.1esr" #https://archive.mozilla.org/pub/firefox/releases/
- when:
condition:
equal: [ "FirefoxHeadless", <<parameters.browser>> ]
@ -113,7 +113,7 @@ jobs:
steps:
- browser-tools/install-chrome:
replace-existing: false
- run: npm run test:coverage -- --browsers=<<parameters.browser>>
- run: npm run test -- --browsers=<<parameters.browser>>
- save_cache_cmd:
node-version: <<parameters.node-version>>
- store_test_results:
@ -128,45 +128,58 @@ jobs:
suite:
type: string
executor: pw-focal-development
parallelism: 4
steps:
- build_and_install:
node-version: <<parameters.node-version>>
- run: npx playwright install
- run: npm run test:e2e:<<parameters.suite>>
- run: SHARD="$((${CIRCLE_NODE_INDEX}+1))"; npm run test:e2e:<<parameters.suite>> -- --shard=${SHARD}/${CIRCLE_NODE_TOTAL}
- store_test_results:
path: test-results/results.xml
- store_artifacts:
path: test-results
- generate_and_store_version_and_filesystem_artifacts
perf-test:
parameters:
node-version:
type: string
executor: pw-focal-development
steps:
- build_and_install:
node-version: <<parameters.node-version>>
- run: npm run test:perf
- store_test_results:
path: test-results/results.xml
- store_artifacts:
path: test-results
- generate_and_store_version_and_filesystem_artifacts
workflows:
overall-circleci-commit-status: #These jobs run on every commit
jobs:
- node14-lint:
node-version: lts/fermium
- lint:
name: node16-lint
node-version: lts/gallium
- unit-test:
name: node12-chrome
node-version: lts/erbium
browser: ChromeHeadless
- unit-test:
name: node14-chrome
node-version: lts/fermium
browser: ChromeHeadless
post-steps:
- upload_code_covio
- upload_code_covio
- unit-test:
name: node18-chrome
node-version: "18"
browser: ChromeHeadless
- e2e-test:
name: e2e-ci
node-version: lts/fermium
node-version: lts/gallium
suite: ci
- perf-test:
node-version: lts/gallium
the-nightly: #These jobs do not run on PRs, but against master at night
jobs:
- unit-test:
name: node12-firefoxESR-nightly
node-version: lts/erbium
name: node16-firefoxESR-nightly
node-version: lts/gallium
browser: FirefoxESR
- unit-test:
name: node12-chrome-nightly
node-version: lts/erbium
browser: ChromeHeadless
- unit-test:
name: node14-firefox-nightly
node-version: lts/fermium
@ -175,11 +188,19 @@ workflows:
name: node14-chrome-nightly
node-version: lts/fermium
browser: ChromeHeadless
- unit-test:
name: node16-chrome-nightly
node-version: lts/gallium
browser: ChromeHeadless
- unit-test:
name: node18-chrome
node-version: "18"
browser: ChromeHeadless
- npm-audit:
node-version: lts/fermium
node-version: lts/gallium
- e2e-test:
name: e2e-full-nightly
node-version: lts/fermium
node-version: lts/gallium
suite: full
triggers:
- schedule:

View File

@ -1,4 +1,4 @@
const LEGACY_FILES = ["platform/**", "example/**"];
const LEGACY_FILES = ["example/**"];
module.exports = {
"env": {
"browser": true,
@ -11,12 +11,14 @@ module.exports = {
},
"extends": [
"eslint:recommended",
"plugin:compat/recommended",
"plugin:vue/recommended",
"plugin:you-dont-need-lodash-underscore/compatible"
],
"parser": "vue-eslint-parser",
"parserOptions": {
"parser": "babel-eslint",
"parser": "@babel/eslint-parser",
"requireConfigFile": false,
"allowImportExportEverywhere": true,
"ecmaVersion": 2015,
"ecmaFeatures": {
@ -27,6 +29,7 @@ module.exports = {
"you-dont-need-lodash-underscore/omit": "off",
"you-dont-need-lodash-underscore/throttle": "off",
"you-dont-need-lodash-underscore/flatten": "off",
"you-dont-need-lodash-underscore/get": "off",
"no-bitwise": "error",
"curly": "error",
"eqeqeq": "error",
@ -35,7 +38,6 @@ module.exports = {
"no-inner-declarations": "off",
"no-use-before-define": ["error", "nofunc"],
"no-caller": "error",
"no-sequences": "error",
"no-irregular-whitespace": "error",
"no-new": "error",
"no-shadow": "error",
@ -239,13 +241,12 @@ module.exports = {
],
"vue/max-attributes-per-line": ["error", {
"singleline": 1,
"multiline": {
"max": 1,
"allowFirstLine": true
}
"multiline": 1,
}],
"vue/first-attribute-linebreak": "error",
"vue/multiline-html-element-content-newline": "off",
"vue/singleline-html-element-content-newline": "off",
"vue/multi-word-component-names": "off", // TODO enable, align with conventions
"vue/no-mutating-props": "off"
},

View File

@ -17,15 +17,6 @@ assignees: ''
#### Expected vs Current Behavior
<!--- Tell us what should have happened -->
#### Impact Check List
<!--- Please select from the following options -->
- [ ] Data loss or misrepresented data?
- [ ] Regression? Did this used to work or has it always been broken?
- [ ] Is there a workaround available?
- [ ] Does this impact a critical component?
- [ ] Is this just a visual bug with no functional impact?
#### Steps to Reproduce
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
<!--- reproduce this bug. Include code to reproduce, if relevant -->
@ -35,10 +26,22 @@ assignees: ''
4.
#### Environment
<!--- If encountered on local machine, execute the following:
<!--- npx envinfo --system --browsers --npmPackages --binaries --markdown -->
* Open MCT Version: <!--- date of build, version, or SHA -->
* Deployment Type: <!--- npm dev? VIPER Dev? openmct-yamcs? -->
* OS:
* Browser:
#### Impact Check List
<!--- Please select from the following options -->
- [ ] Data loss or misrepresented data?
- [ ] Regression? Did this used to work or has it always been broken?
- [ ] Is there a workaround available?
- [ ] Does this impact a critical component?
- [ ] Is this just a visual bug with no functional impact?
- [ ] Does this block the execution of e2e tests?
- [ ] Does this have an impact on Performance?
#### Additional Information
<!--- Include any screenshots, gifs, or logs which will expedite triage -->

View File

@ -1,3 +1,9 @@
<!--- Note: Please open the PR in draft form until you are ready for active review. -->
Closes <!--- Insert Issue Number(s) this PR addresses. Start by typing # will open a dropdown of recent issues. Note: this does not work on PRs which target release branches -->
### Describe your changes:
<!--- Describe your changes and add any comments about your approach either here or inline if code comments aren't added -->
### All Submissions:
* [ ] Have you followed the guidelines in our [Contributing document](https://github.com/nasa/openmct/blob/master/CONTRIBUTING.md)?
@ -10,4 +16,14 @@
* [ ] Unit tests included and/or updated with changes?
* [ ] Command line build passes?
* [ ] Has this been smoke tested?
* [ ] Testing instructions included in associated issue?
* [ ] Testing instructions included in associated issue OR is this a dependency/testcase change?
### 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)

View File

@ -5,18 +5,14 @@ updates:
directory: "/"
schedule:
interval: "daily"
open-pull-requests-limit: 4
open-pull-requests-limit: 10
labels:
- "type:maintenance"
- "dependencies"
- "pr:e2e"
allow:
- dependency-name: "*eslint*"
- dependency-name: "*karma*"
- dependency-name: "*jasmine*"
- dependency-name: "*playwright*"
- dependency-name: "*percy*"
- dependency-name: "*vue-loader*"
- "pr:daveit"
- "pr:visual"
- "pr:platform"
- package-ecosystem: "github-actions"
directory: "/"
@ -25,3 +21,4 @@ updates:
labels:
- "type:maintenance"
- "dependencies"
- "pr:daveit"

View File

@ -28,16 +28,16 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
uses: github/codeql-action/init@v2
with:
languages: javascript
- name: Autobuild
uses: github/codeql-action/autobuild@v1
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
uses: github/codeql-action/analyze@v2

View File

@ -2,52 +2,60 @@ name: "e2e-pr"
on:
workflow_dispatch:
pull_request:
types: [ labeled ]
types:
- labeled
- opened
jobs:
e2e-full:
if: ${{ github.event.label.name == 'pr:e2e' }}
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- ubuntu-latest
- windows-latest
steps:
- name: Trigger Success
uses: actions/github-script@v5
uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
owner: "nasa",
repo: "openmct",
body: 'Started e2e Run. Follow along: https://github.com/nasa/openmct/actions/runs/' + context.runId
})
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '14'
node-version: '16'
- run: npx playwright@1.21.1 install
- run: npm install
- run: npm run test:e2e:full
- name: Archive test results
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
path: test-results
- name: Test success
if: ${{ success() }}
uses: actions/github-script@v5
uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
owner: "nasa",
repo: "openmct",
body: 'Success ✅ ! Build artifacts are here: https://github.com/nasa/openmct/actions/runs/' + context.runId
})
- name: Test failure
if: ${{ failure() }}
uses: actions/github-script@v5
uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
owner: "nasa",
repo: "openmct",
body: 'Failure ❌ ! Build artifacts are here: https://github.com/nasa/openmct/actions/runs/' + context.runId
})

View File

@ -4,6 +4,7 @@ on:
pull_request:
types:
- labeled
- opened
schedule:
- cron: '28 21 * * 1-5'
@ -12,10 +13,11 @@ jobs:
if: ${{ github.event.label.name == 'pr:visual' }} || ${{ github.event.workflow_dispatch }} || ${{ github.event.schedule }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '14'
node-version: '16'
- run: npx playwright@1.21.1 install
- run: npm install
- name: Run the e2e visual tests
run: npm run test:e2e:visual

View File

@ -10,12 +10,12 @@ jobs:
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
ref: ${{ github.event.inputs.version }}
- uses: actions/setup-node@v2
- uses: actions/setup-node@v3
with:
node-version: '14'
node-version: '16'
- run: npm install
- name: Run the e2e tests
run: npm run test:e2e:ci

View File

@ -5,16 +5,94 @@ on:
version:
description: 'Which branch do you want to test?' # Limited to branch for now
required: false
default: 'master'
default: 'master'
pull_request:
types:
- labeled
jobs:
lighthouse:
lighthouse-pr:
if: ${{ github.event.label.name == 'pr:lighthouse' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Checkout Master for Baseline
uses: actions/checkout@v3
with:
ref: master #explicitly checkout master for baseline
- name: Install Node 16
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
- name: npm install with lighthouse cli
run: npm install && npm install -g @lhci/cli
- name: Run lhci against master to generate baseline and ignore exit codes
run: lhci autorun || true
- name: Perform clean checkout of PR
uses: actions/checkout@v3
with:
clean: true
- name: Install Node version which is compatible with PR
uses: actions/setup-node@v3
- name: npm install with lighthouse cli
run: npm install && npm install -g @lhci/cli
- name: Run lhci with PR
run: lhci autorun
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
lighthouse-nightly:
if: ${{ github.event.schedule }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Node 16
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
- name: npm install with lighthouse cli
run: npm install && npm install -g @lhci/cli
- name: Run lhci against master to generate baseline
run: lhci autorun
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
lighthouse-dispatch:
if: ${{ github.event.workflow_dispatch }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.inputs.version }}
- uses: actions/setup-node@v2
- name: Install Node 14
uses: actions/setup-node@v3
with:
node-version: '14'
- run: npm install && npm install -g @lhci/cli #Don't want to include this in our deps
- run: lhci autorun
node-version: '16'
- name: Cache node modules
uses: actions/cache@v3
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
- name: npm install with lighthouse cli
run: npm install && npm install -g @lhci/cli
- name: Run lhci against master to generate baseline
run: lhci autorun

View File

@ -11,10 +11,10 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 14
node-version: 16
- run: npm install
- run: npm test
@ -22,10 +22,10 @@ jobs:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 14
node-version: 16
registry-url: https://registry.npmjs.org/
- run: npm install
- run: npm publish --access public --tag unstable

34
.github/workflows/pr-platform.yml vendored Normal file
View File

@ -0,0 +1,34 @@
name: "pr-platform"
on:
workflow_dispatch:
pull_request:
types: [ labeled ]
jobs:
e2e-full:
if: ${{ github.event.label.name == 'pr:platform' }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- macos-latest
- windows-latest
node_version:
- 14
- 16
- 18
architecture:
- x64
name: Node ${{ matrix.node_version }} - ${{ matrix.architecture }} on ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Setup node
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node_version }}
architecture: ${{ matrix.architecture }}
- run: npm install
- run: npm test
- run: npm run lint -- --quiet

19
.github/workflows/prcop-config.json vendored Normal file
View File

@ -0,0 +1,19 @@
{
"linters": [
{
"name": "descriptionRegexp",
"config": {
"regexp": "x] Testing instructions",
"errorMessage": ":police_officer: PR Description does not confirm that associated issue(s) contain Testing instructions"
}
},
{
"name": "descriptionMinWords",
"config": {
"minWordsCount": 160,
"errorMessage": ":police_officer: Please, be sure to use existing PR template."
}
}
],
"disableWord": "pr:daveit"
}

26
.github/workflows/prcop.yml vendored Normal file
View File

@ -0,0 +1,26 @@
name: PRCop
on:
pull_request:
types:
- opened
- reopened
- edited
- synchronize
- ready_for_review
- review_requested
- review_request_removed
pull_request_review_comment:
types:
- created
jobs:
prcop:
runs-on: ubuntu-latest
name: Template Check
steps:
- name: Linting Pull Request
uses: makaroni4/prcop@v1.0.35
with:
config-file: ".github/workflows/prcop-config.json"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,44 +1,27 @@
*.scssc
*.zip
*.gzip
*.tgz
*.DS_Store
# Ignore everything first (will not ignore special files like LICENSE.md,
# README.md, and package.json)...
/**/*
*.sass-cache
*COMPILE.css
# ...but include these folders...
!/dist/**/*
!/src/**/*
# Intellij project configuration files
*.idea
*.iml
# We might be able to remove this if it is not imported by any project directly.
# https://github.com/nasa/openmct/issues/4992
!/example/**/*
# External dependencies
# We will remove this in https://github.com/nasa/openmct/issues/4922
!/app.js
# Build output
target
# ...except for these files in the above folders.
/src/**/*Spec.js
/src/**/test/
# TODO move test utils into test/ folders
/src/utils/testing.js
# Mac OS X Finder
.DS_Store
# Closed source libraries
closed-lib
# Node, Bower dependencies
node_modules
bower_components
Procfile
# Protractor logs
protractor/logs
# npm-debug log
npm-debug.log
# Infra and tests
.circleci
.github
e2e
codecov.yml
lighthouserc.yml
*.Spec.js
karma.conf.js
# Also include these special top-level files.
!copyright-notice.js
!copyright-notice.html
!index.html
!openmct.js
!SECURITY.md

6
.npmrc
View File

@ -1,6 +1,4 @@
loglevel=warn
# Temporary: istanbul-instrumenter-loader is working with webpack 5, but states
# webpack 4 being the latest version it supports, so this legacy-peer-deps
# allows us to install it anyway.
legacy-peer-deps=true
#Prevent folks from ignoring an important error when building from source
engine-strict=true

View File

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

@ -11,6 +11,22 @@ Once you've created something amazing with Open MCT, showcase your work in our G
Try Open MCT now with our [live demo](https://openmct-demo.herokuapp.com/).
![Demo](https://nasa.github.io/openmct/static/res/images/Open-MCT.Browse.Layout.Mars-Weather-1.jpg)
## Open MCT v2.0.0
Support for our legacy bundle-based API, and the libraries that it was built on (like Angular 1.x), have now been removed entirely from this repository.
For now if you have an Open MCT application that makes use of the legacy API, [a plugin](https://github.com/nasa/openmct-legacy-plugin) is provided that bootstraps the legacy bundling mechanism and API. This plugin will not be maintained over the long term however, and the legacy support plugin will not be tested for compatibility with future versions of Open MCT. It is provided for convenience only.
### How do I know if I am using legacy API?
You might still be using legacy API if your source code
* Contains files named bundle.js, or bundle.json,
* Makes calls to `openmct.$injector()`, or `openmct.$angular`,
* Makes calls to `openmct.legacyRegistry`, `openmct.legacyExtension`, or `openmct.legacyBundle`.
### What should I do if I am using legacy API?
Please refer to [the modern Open MCT API](https://nasa.github.io/openmct/documentation/). Post any questions to the [Discussions section](https://github.com/nasa/openmct/discussions) of the Open MCT GitHub repository.
## Building and Running Open MCT Locally
Building and running Open MCT in your local dev environment is very easy. Be sure you have [Git](https://git-scm.com/downloads) and [Node.js](https://nodejs.org/) installed, then follow the directions below. Need additional information? Check out the [Getting Started](https://nasa.github.io/openmct/getting-started/) page on our website.
@ -20,7 +36,7 @@ Building and running Open MCT in your local dev environment is very easy. Be sur
`git clone https://github.com/nasa/openmct.git`
2. Install development dependencies
2. Install development dependencies. Note: Check the package.json engine for our tested and supported node versions.
`npm install`
@ -30,11 +46,6 @@ Building and running Open MCT in your local dev environment is very easy. Be sur
Open MCT is now running, and can be accessed by pointing a web browser at [http://localhost:8080/](http://localhost:8080/)
## Open MCT v1.0.0
This represents a major overhaul of Open MCT with significant changes under the hood. We aim to maintain backward compatibility but if you do find compatibility issues, please let us know by filing an issue in this repository. If you are having major issues with v1.0.0 please check-out the v0.14.0 tag until we can resolve them for you.
If you are migrating an application built with Open MCT as a dependency to v1.0.0 from an earlier version, please refer to [our migration guide](https://nasa.github.io/openmct/documentation/migration-guide).
## Documentation
Documentation is available on the [Open MCT website](https://nasa.github.io/openmct/documentation/).
@ -54,6 +65,12 @@ Open MCT is built using [`npm`](http://npmjs.com/) and [`webpack`](https://webpa
See our documentation for a guide on [building Applications with Open MCT](https://github.com/nasa/openmct/blob/master/API.md#starting-an-open-mct-application).
## Compatibility
This is a fast moving project and we do our best to test and support the widest possible range of browsers, operating systems, and nodejs APIs. We have a published list of support available in our package.json's `browserslist` key.
If you encounter an issue with a particular browser, OS, or nodejs API, please file a [GitHub issue](https://github.com/nasa/openmct/issues/new/choose)
## Plugins
Open MCT can be extended via plugins that make calls to the Open MCT API. A plugin is a group

6
app.js
View File

@ -1,4 +1,4 @@
/*global require,process,console*/
/*global process*/
/**
* Usage:
@ -64,7 +64,7 @@ app.use(require('webpack-dev-middleware')(
compiler,
{
publicPath: '/dist',
logLevel: 'warn'
stats: 'errors-warnings'
}
));
@ -79,6 +79,6 @@ app.get('/', function (req, res) {
});
// Finally, open the HTTP server and log the instance to the console
app.listen(options.port, options.host, function() {
app.listen(options.port, options.host, function () {
console.log('Open MCT application running at %s:%s', options.host, options.port);
});

9
babel.coverage.js Normal file
View File

@ -0,0 +1,9 @@
// This is a Babel config that webpack.coverage.js uses in order to instrument
// code with coverage instrumentation.
const babelConfig = {
plugins: [['babel-plugin-istanbul', {
extension: ['.js', '.vue']
}]]
};
module.exports = babelConfig;

View File

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

View File

@ -14,8 +14,6 @@ coverage:
range: "66...100"
ignore:
- "**/*Spec.js"
- "e2e"
parsers:
gcov:

View File

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

View File

@ -1,232 +0,0 @@
# Overview
The framework layer's most basic responsibility is allowing individual
software components to communicate. The software components it recognizes
are:
* _Extensions_: Individual units of functionality that can be added to
or removed from Open MCT. _Extension categories_ distinguish what
type of functionality is being added/removed.
* _Bundles_: A grouping of related extensions
(named after an analogous concept from [OSGi](http://www.osgi.org/))
that may be added or removed as a group.
The framework layer operates by taking a set of active bundles, and
exposing extensions to one another as-needed, using
[dependency injection](https://en.wikipedia.org/wiki/Dependency_injection).
Extensions are responsible for declaring their dependencies in a
manner which the framework layer can understand.
```nomnoml
#direction: down
[Open MCT|
[Dependency injection framework]-->[Platform bundle #1]
[Dependency injection framework]-->[Platform bundle #2]
[Dependency injection framework]-->[Plugin bundle #1]
[Dependency injection framework]-->[Plugin bundle #2]
[Platform bundle #1|[Extensions]]
[Platform bundle #2|[Extensions]]
[Plugin bundle #1|[Extensions]]
[Plugin bundle #2|[Extensions]]
[Platform bundle #1]<->[Platform bundle #2]
[Plugin bundle #1]<->[Platform bundle #2]
[Plugin bundle #1]<->[Plugin bundle #2]
]
```
The "dependency injection framework" in this case is
[AngularJS](https://angularjs.org/). Open MCT's framework layer
is really just a thin wrapper over Angular that recognizes the
concepts of bundles and extensions (as declared in JSON files) and
registering extensions with Angular. It additionally acts as a
mediator between Angular and [RequireJS](http://requirejs.org/),
which is used to load JavaScript sources which implement
extensions.
```nomnoml
[Framework layer|
[AngularJS]<-[Framework Component]
[RequireJS]<-[Framework Component]
[Framework Component]1o-*[Bundles]
]
```
It is worth noting that _no other components_ are "aware" of the
framework component directly; Angular and Require are _used by_ the
framework components, and extensions in various bundles will have
their dependencies satisfied by Angular as a consequence of registration
activities which were performed by the framework component.
## Application Initialization
The framework component initializes an Open MCT application following
a simple sequence of steps.
```nomnoml
[<start> Start]->[<state> Load bundles.json]
[Load bundles.json]->[<state> Load bundle.json files]
[Load bundle.json files]->[<state> Resolve implementations]
[Resolve implementations]->[<state> Register with Angular]
[Register with Angular]->[<state> Bootstrap application]
[Bootstrap application]->[<end> End]
```
1. __Loading bundles.json.__ A file named `bundles.json` is loaded to determine
which bundles to load. Bundles are given in this file as relative paths
which point to bundle directories.
2. __Load bundle.json files.__ Individual bundle definitions are loaded; a
`bundle.json` file is expected in each bundle directory.
2. __Resolving implementations.__ Any scripts which provide implementations for
extensions exposed by bundles are loaded, using RequireJS.
3. __Register with Angular.__ Resolved extensions are registered with Angular,
such that they can be used by the application at run-time. This stage
includes both registration of Angular built-ins (directives, controllers,
routes, constants, and services) as well as registration of non-Angular
extensions.
4. __Bootstrap application.__ Once all extensions have been registered,
the Angular application
[is bootstrapped](https://docs.angularjs.org/guide/bootstrap).
## Architectural Paradigm
```nomnoml
[Extension]
[Extension]o->[Dependency #1]
[Extension]o->[Dependency #2]
[Extension]o->[Dependency #3]
```
Open MCT's architecture relies on a simple premise: Individual units
(extensions) only have access to the dependencies they declare that they
need, and they acquire references to these dependencies via dependency
injection. This has several desirable traits:
* Programming to an interface is enforced. Any given dependency can be
swapped out for something which exposes an equivalent interface. This
improves flexibility against refactoring, simplifies testing, and
provides a common mechanism for extension and reconfiguration.
* The dependencies of a unit must be explicitly defined. This means that
it can be easily determined what a given unit's role is within the
larger system, in terms of what other components it will interact with.
It also helps to enforce good separation of concerns: When a set of
declared dependencies becomes long it is obvious, and this is usually
a sign that a given unit is involved in too many concerns and should
be refactored into smaller pieces.
* Individual units do not need to be aware of the framework; they need
only be aware of the interfaces to the components they specifically
use. This avoids introducing a ubiquitous dependency upon the framework
layer itself; it is plausible to modify or replace the framework
without making changes to individual software components which run upon
the framework.
A drawback to this approach is that it makes it difficult to define
"the architecture" of Open MCT, in terms of describing the specific
units that interact at run-time. The run-time architecture is determined
by the framework as the consequence of wiring together dependencies.
As such, the specific architecture of any given application built on
Open MCT can look very different.
Keeping that in mind, there are a few useful patterns supported by the
framework that are useful to keep in mind.
The specific service infrastructure provided by the platform is described
in the [Platform Architecture](platform.md).
## Extension Categories
One of the capabilities that the framework component layers on top of
AngularJS is support for many-to-one dependencies. That is, a specific
extension may declare a dependency to _all extensions of a specific
category_, instead of being limited to declaring specific dependencies.
```nomnoml
#direction: right
[Specific Extension] 1 o-> * [Extension of Some Category]
```
This is useful for introducing specific extension points to an application.
Some unit of software will depend upon all extensions of a given category
and integrate their behavior into the system in some fashion; plugin authors
can then add new extensions of that category to augment existing behaviors.
Some developers may be familiar with the use of registries to achieve
similar characteristics. This approach is similar, except that the registry
is effectively implicit whenever a new extension category is used or
depended-upon. This has some advantages over a more straightforward
registry-based approach:
* These many-to-one relationships are expressed as dependencies; an
extension category is registered as having dependencies on all individual
extensions of this category. This avoids ordering issues that may occur
with more conventional registries, which may be observed before all
dependencies are resolved.
* The need for service registries of specific types is removed, reducing
the number of interfaces to manage within the system. Groups of
extensions are provided as arrays.
## Composite Services
Composite services (registered via extension category `components`) are
a pattern supported by the framework. These allow service instances to
be built from multiple components at run-time; support for this pattern
allows additional bundles to introduce or modify behavior associated
with these services without modifying or replacing original service
instances.
```nomnoml
#direction: down
[<abstract> FooService]
[FooDecorator #1]--:>[FooService]
[FooDecorator #n]--:>[FooService]
[FooAggregator]--:>[FooService]
[FooProvider #1]--:>[FooService]
[FooProvider #n]--:>[FooService]
[FooDecorator #1]o->[<state> ...decorators...]
[...decorators...]o->[FooDecorator #n]
[FooDecorator #n]o->[FooAggregator]
[FooAggregator]o->[FooProvider #1]
[FooAggregator]o->[<state> ...providers...]
[FooAggregator]o->[FooProvider #n]
[FooDecorator #1]--[<note> Exposed as fooService]
```
In this pattern, components all implement an interface which is
standardized for that service. Components additionally declare
that they belong to one of three types:
* __Providers.__ A provider actually implements the behavior
(satisfies the contract) for that kind of service. For instance,
if a service is responsible for looking up documents by an identifier,
one provider may do so by querying a database, while another may
do so by reading a static JSON document. From the outside, either
provider would look the same (they expose the same interface) and
they could be swapped out easily.
* __Aggregator.__ An aggregator takes many providers and makes them
behave as one. Again, this implements the same interface as an
individual provider, so users of the service do not need to be
concerned about the difference between consulting many providers
and consulting one. Continuing with the example of a service that
looks up documents by identifiers, an aggregator here might consult
all providers, and return any document is found (perhaps picking one
over the other or merging documents if there are multiple matches.)
* __Decorators.__ A decorator exposes the same interface as other
components, but instead of fully implementing the behavior associated
with that kind of service, it only acts as an intermediary, delegating
the actual behavior to a different component. Decorators may transform
inputs or outputs, or initiate some side effects associated with a
service. This is useful if certain common behavior associated with a
service (caching, for instance) may be useful across many different
implementations of that same service.
The framework will register extensions in this category such that an
aggregator will depend on all of its providers, and decorators will
depend upon on one another in a chain. The result of this compositing step
(the last decorator, if any; otherwise the aggregator, if any;
otherwise a single provider) will be exposed as a single service that
other extensions can acquire through dependency injection. Because all
components of the same type of service expose the same interface, users
of that service do not need to be aware that they are talking to an
aggregator or a provider, for instance.

View File

@ -1,76 +0,0 @@
# Introduction
The purpose of this document is to familiarize developers with the
overall architecture of Open MCT.
The target audience includes:
* _Platform maintainers_: Individuals involved in developing,
extending, and maintaining capabilities of the platform.
* _Integration developers_: Individuals tasked with integrated
Open MCT into a larger system, who need to understand
its inner workings sufficiently to complete this integration.
As the focus of this document is on architecture, whenever possible
implementation details (such as relevant API or JSON syntax) have been
omitted. These details may be found in the developer guide.
# Overview
Open MCT is client software: It runs in a web browser and
provides a user interface, while communicating with various
server-side resources through browser APIs.
```nomnoml
#direction: right
[Client|[Browser|[Open MCT]->[Browser APIs]]]
[Server|[Web services]]
[Client]<->[Server]
```
While Open MCT can be configured to run as a standalone client,
this is rarely very useful. Instead, it is intended to be used as a
display and interaction layer for information obtained from a
variety of back-end services. Doing so requires authoring or utilizing
adapter plugins which allow Open MCT to interact with these services.
Typically, the pattern here is to provide a known interface that
Open MCT can utilize, and implement it such that it interacts with
whatever back-end provides the relevant information.
Examples of back-ends that can be utilized in this fashion include
databases for the persistence of user-created objects, or sources of
telemetry data.
## Software Architecture
The simplest overview of Open MCT is to look at it as a "layered"
architecture, where each layer more clearly specifies the behavior
of the software.
```nomnoml
#direction: down
[Open MCT|
[Platform]<->[Application]
[Framework]->[Application]
[Framework]->[Platform]
]
```
These layers are:
* [_Framework_](framework.md): The framework layer is responsible for
managing the interactions between application components. It has no
application-specific knowledge; at this layer, we have only
established an abstraction by which different software components
may communicate and/or interact.
* [_Platform_](platform.md): The platform layer defines the general look,
feel, and behavior of Open MCT. This includes user-facing components like
Browse mode and Edit mode, as well as underlying elements of the
information model and the general service infrastructure.
* _Application_: The application layer defines specific features of
an application built on Open MCT. This includes adapters to
specific back-ends, new types of things for users to create, and
new ways of visualizing objects within the system. This layer
typically consists of a mix of custom plug-ins to Open MCT,
as well as optional features (such as Plot view) included alongside
the platform.

View File

@ -1,726 +0,0 @@
# Overview
The Open MCT platform utilizes the [framework layer](framework.md)
to provide an extensible baseline for applications which includes:
* A common user interface (and user interface paradigm) for dealing with
domain objects of various sorts.
* A variety of extension points for introducing new functionality
of various kinds within the context of the common user interface.
* A service infrastructure to support building additional components.
## Platform Architecture
While the framework provides a more general architectural paradigm for
building application, the platform adds more specificity by defining
additional extension types and allowing for integration with back end
components.
The run-time architecture of an Open MCT application can be categorized
into certain high-level tiers:
```nomnoml
[DOM]->[<state> AngularJS]
[AngularJS]->[Presentation Layer]
[Presentation Layer]->[Information Model]
[Presentation Layer]->[Service Infrastructure]
[Information Model]->[Service Infrastructure]
[Service Infrastructure]->[<state> Browser APIs]
[Browser APIs]->[Back-end]
```
Applications built using Open MCT may add or configure functionality
in __any of these tiers__.
* _DOM_: The rendered HTML document, composed from HTML templates which
have been processed by AngularJS and will be updated by AngularJS
to reflect changes from the presentation layer. User interactions
are initiated from here and invoke behavior in the presentation layer. HTML 
templates are written in Angulars template syntax; see the [Angular documentation on templates](https://docs.angularjs.org/guide/templates). 
These describe the page as actually seen by the user. Conceptually, 
stylesheets (controlling the look-and-feel of the rendered templates) belong 
in this grouping as well. 
* [_Presentation layer_](#presentation-layer): The presentation layer
is responsible for updating (and providing information to update)
the displayed state of the application. The presentation layer consists
primarily of _controllers_ and _directives_. The presentation layer is
concerned with inspecting the information model and preparing it for
display.
* [_Information model_](#information-model): Provides a common (within Open MCT 
Web) set of interfaces for dealing with “things” ­ domain objects ­ within the 
system. User-facing concerns in a Open MCT Web application are expressed as 
domain objects; examples include folders (used to organize other domain 
objects), layouts (used to build displays), or telemetry points (used as 
handles for streams of remote measurements.) These domain objects expose a 
common set of interfaces to allow reusable user interfaces to be built in the 
presentation and template tiers; the specifics of these behaviors are then 
mapped to interactions with underlying services. 
* [_Service infrastructure_](#service-infrastructure): The service
infrastructure is responsible for providing the underlying general
functionality needed to support the information model. This includes
exposing underlying sets of extensions and mediating with the
back-end.
* _Back-end_: The back-end is out of the scope of Open MCT, except
for the interfaces which are utilized by adapters participating in the
service infrastructure. Includes the underlying persistence stores, telemetry 
streams, and so forth which the Open MCT Web client is being used to interact 
with.
## Application Start-up
Once the
[application has been initialized](Framework.md#application-initialization)
Open MCT primarily operates in an event-driven paradigm; various
events (mouse clicks, timers firing, receiving responses to XHRs) trigger
the invocation of functions, typically in the presentation layer for
user actions or in the service infrastructure for server responses.
The "main point of entry" into an initialized Open MCT application
is effectively the
[route](https://docs.angularjs.org/api/ngRoute/service/$route#example)
which is associated with the URL used to access Open MCT (or a
default route.) This route will be associated with a template which
will be displayed; this template will include references to directives
and controllers which will be interpreted by Angular and used to
initialize the state of the display in a manner which is backed by
both the information model and the service infrastructure.
```nomnoml
[<start> Start]->[<state> page load]
[page load]->[<state> route selection]
[route selection]->[<state> compile, display template]
[compile, display template]->[Template]
[Template]->[<state> use Controllers]
[Template]->[<state> use Directives]
[use Controllers]->[Controllers]
[use Directives]->[Directives]
[Controllers]->[<state> consult information model]
[consult information model]->[<state> expose data]
[expose data]->[Angular]
[Angular]->[<state> update display]
[Directives]->[<state> add event listeners]
[Directives]->[<state> update display]
[add event listeners]->[<end> End]
[update display]->[<end> End]
```
# Presentation Layer
The presentation layer of Open MCT is responsible for providing
information to display within templates, and for handling interactions
which are initiated from templated DOM elements. AngularJS acts as
an intermediary between the web page as the user sees it, and the
presentation layer implemented as Open MCT extensions.
```nomnoml
[Presentation Layer|
[Angular built-ins|
[routes]
[controllers]
[directives]
[templates]
]
[Domain object representation|
[views]
[representations]
[representers]
[gestures]
]
]
```
## Angular built-ins
Several extension categories in the presentation layer map directly
to primitives from AngularJS:
* [_Controllers_](https://docs.angularjs.org/guide/controller) provide
data to templates, and expose functionality that can be called from
templates.
* [_Directives_](https://docs.angularjs.org/guide/directive) effectively
extend HTML to provide custom behavior associated with specific
attributes and tags.
* [_Routes_](https://docs.angularjs.org/api/ngRoute/service/$route#example)
are used to associate specific URLs (including the fragment identifier)
with specific application states. (In Open MCT, these are used to
describe the mode of usage - e.g. browse or edit - as well as to
identify the object being used.)
* [_Templates_](https://docs.angularjs.org/guide/templates) are partial
HTML documents that will be rendered and kept up-to-date by AngularJS.
Open MCT introduces a custom `mct-include` directive which acts
as a wrapper around `ng-include` to allow templates to be referred
to by symbolic names.
## Domain object representation
The remaining extension categories in the presentation layer are specific
to displaying domain objects.
* _Representations_ are templates that will be used to display
domain objects in specific ways (e.g. "as a tree node.")
* _Views_ are representations which are exposed to the user as options
for displaying domain objects.
* _Representers_ are extensions which modify or augment the process
of representing domain objects generally (e.g. by attaching
gestures to them.)
* _Gestures_ provide associations between specific user actions
(expressed as DOM events) and resulting behavior upon domain objects
(typically expressed as members of the `actions` extension category)
that can be reused across domain objects. For instance, `drag` and
`drop` are both gestures associated with using drag-and-drop to
modify the composition of domain objects by interacting with their
representations.
# Information Model
```nomnoml
#direction: right
[Information Model|
[DomainObject|
getId() : string
getModel() : object
getCapability(key : string) : Capability
hasCapability(key : string) : boolean
useCapability(key : string, args...) : *
]
[DomainObject] 1 +- 1 [Model]
[DomainObject] 1 o- * [Capability]
]
```
Domain objects are the most fundamental component of Open MCT's
information model. A domain object is some distinct thing relevant to a
user's work flow, such as a telemetry channel, display, or similar.
Open MCT is a tool for viewing, browsing, manipulating, and otherwise
interacting with a graph of domain objects.
A domain object should be conceived of as the union of the following:
* _Identifier_: A machine-readable string that uniquely identifies the
domain object within this application instance.
* _Model_: The persistent state of the domain object. A domain object's
model is a JavaScript object that can be losslessly converted to JSON.
* _Capabilities_: Dynamic behavior associated with the domain object.
Capabilities are JavaScript objects which provide additional methods
for interacting with the domain objects which expose those capabilities.
Not all domain objects expose all capabilities. The interface exposed
by any given capability will depend on its type (as identified
by the `key` argument.) For instance, a `persistence` capability
has a different interface from a `telemetry` capability. Using
capabilities requires some prior knowledge of their interface.
## Capabilities and Services
```nomnoml
#direction: right
[DomainObject]o-[FooCapability]
[FooCapability]o-[FooService]
[FooService]o-[foos]
```
At run-time, the user is primarily concerned with interacting with
domain objects. These interactions are ultimately supported via back-end
services, but to allow customization per-object, these are often mediated
by capabilities.
A common pattern that emerges in the Open MCT Platform is as follows:
* A `DomainObject` has some particular behavior that will be supported
by a service.
* A `Capability` of that domain object will define that behavior,
_for that domain object_, supported by a service.
* A `Service` utilized by that capability will perform the actual behavior.
* An extension category will be utilized by that capability to determine
the set of possible behaviors.
Concrete examples of capabilities which follow this pattern
(or a subset of this pattern) include:
```nomnoml
#direction: right
[DomainObject]1 o- *[Capability]
[Capability]<:--[TypeCapability]
[Capability]<:--[ActionCapability]
[Capability]<:--[PersistenceCapability]
[Capability]<:--[TelemetryCapability]
[TypeCapability]o-[TypeService]
[TypeService]o-[types]
[ActionCapability]o-[ActionService]
[ActionService]o-[actions]
[PersistenceCapability]o-[PersistenceService]
[TelemetryCapability]o-[TelemetryService]
```
# Service Infrastructure
Most services exposed by the Open MCT platform follow the
[composite services](Framework.md#composite-services) to permit
a higher degree of flexibility in how a service can be modified
or customized for specific applications.
To simplify usage for plugin developers, the platform also usually
includes a provider implementation for these service type that consumes
some extension category. For instance, an `ActionService` provider is
included which depends upon extension category `actions`, and exposes
all actions declared as such to the system. As such, plugin developers
can simply implement the new actions they wish to be made available without
worrying about the details of composite services or implementing a new
`ActionService` provider; however, the ability to implement a new provider
remains useful when the expressive power of individual extensions is
insufficient.
```nomnoml
[ Service Infrastructure |
[ObjectService]->[ModelService]
[ModelService]->[PersistenceService]
[ObjectService]->[CapabilityService]
[CapabilityService]->[capabilities]
[capabilities]->[TelemetryService]
[capabilities]->[PersistenceService]
[capabilities]->[TypeService]
[capabilities]->[ActionService]
[capabilities]->[ViewService]
[PersistenceService]->[<database> Document store]
[TelemetryService]->[<database> Telemetry source]
[ActionService]->[actions]
[ActionService]->[PolicyService]
[ViewService]->[PolicyService]
[ViewService]->[views]
[PolicyService]->[policies]
[TypeService]->[types]
]
```
A short summary of the roles of these services:
* _[ObjectService](#object-service)_: Allows retrieval of domain objects by
their identifiers; in practice, often the main point of entry into the
[information model](#information-model).
* _[ModelService](#model-service)_: Provides domain object models, retrieved
by their identifier.
* _[CapabilityService](#capability-service)_: Provides capabilities, as they
apply to specific domain objects (as judged from their model.)
* _[TelemetryService](#telemetry-service)_: Provides access to historical
and real-time telemetry data.
* _[PersistenceService](#persistence-service)_: Provides the ability to
store and retrieve documents (such as domain object models.)
* _[ActionService](#action-service)_: Provides distinct user actions that
can take place within the system (typically, upon or using domain objects.)
* _[ViewService](#view-service)_: Provides views for domain objects. A view
is a user-selectable representation of a domain object (in practice, an
HTML template.)
* _[PolicyService](#policy-service)_: Handles decisions about which
behavior are allowed within certain specific contexts.
* _[TypeService](#type-service)_: Provides information to distinguish
different types of domain objects from one another within the system.
## Object Service
```nomnoml
#direction: right
[<abstract> ObjectService|
getObjects(ids : Array.<string>) : Promise.<object.<string, DomainObject>>
]
[DomainObjectProvider]--:>[ObjectService]
[DomainObjectProvider]o-[ModelService]
[DomainObjectProvider]o-[CapabilityService]
```
As domain objects are central to Open MCT's information model,
acquiring domain objects is equally important.
```nomnoml
#direction: right
[<start> Start]->[<state> Look up models]
[<state> Look up models]->[<state> Look up capabilities]
[<state> Look up capabilities]->[<state> Instantiate DomainObject]
[<state> Instantiate DomainObject]->[<end> End]
```
Open MCT includes an implementation of an `ObjectService` which
satisfies this capability by:
* Consulting the [Model Service](#model-service) to acquire domain object
models by identifier.
* Passing these models to a [Capability Service](#capability-service) to
determine which capabilities are applicable.
* Combining these results together as [DomainObject](#information-model)
instances.
## Model Service
```nomnoml
#direction: down
[<abstract> ModelService|
getModels(ids : Array.<string>) : Promise.<object.<string, object>>
]
[StaticModelProvider]--:>[ModelService]
[RootModelProvider]--:>[ModelService]
[PersistedModelProvider]--:>[ModelService]
[ModelAggregator]--:>[ModelService]
[CachingModelDecorator]--:>[ModelService]
[MissingModelDecorator]--:>[ModelService]
[MissingModelDecorator]o-[CachingModelDecorator]
[CachingModelDecorator]o-[ModelAggregator]
[ModelAggregator]o-[StaticModelProvider]
[ModelAggregator]o-[RootModelProvider]
[ModelAggregator]o-[PersistedModelProvider]
[PersistedModelProvider]o-[PersistenceService]
[RootModelProvider]o-[roots]
[StaticModelProvider]o-[models]
```
The platform's model service is responsible for providing domain object
models (effectively, JSON documents describing the persistent state
associated with domain objects.) These are retrieved by identifier.
The platform includes multiple components of this variety:
* `PersistedModelProvider` looks up domain object models from
a persistence store (the [`PersistenceService`](#persistence-service));
this is how user-created and user-modified
domain object models are retrieved.
* `RootModelProvider` provides domain object models that have been
declared via the `roots` extension category. These will appear at the
top level of the tree hierarchy in the user interface.
* `StaticModelProvider` provides domain object models that have been
declared via the `models` extension category. This is useful for
allowing plugins to expose new domain objects declaratively.
* `ModelAggregator` merges together the results from multiple providers.
If multiple providers return models for the same domain object,
the most recently modified version (as determined by the `modified`
property of the model) is chosen.
* `CachingModelDecorator` caches model instances in memory. This
ensures that only a single instance of a domain object model is
present at any given time within the application, and prevent
redundant retrievals.
* `MissingModelDecorator` adds in placeholders when no providers
have returned domain object models for a specific identifier. This
allows the user to easily see that something was expected to be
present, but wasn't.
## Capability Service
```nomnoml
#direction: down
[<abstract> CapabilityService|
getCapabilities(model : object) : object.<string, Function>
]
[CoreCapabilityProvider]--:>[CapabilityService]
[QueuingPersistenceCapabilityDecorator]--:>[CapabilityService]
[CoreCapabilityProvider]o-[capabilities]
[QueuingPersistenceCapabilityDecorator]o-[CoreCapabilityProvider]
```
The capability service is responsible for determining which capabilities
are applicable for a given domain object, based on its model. Primarily,
this is handled by the `CoreCapabilityProvider`, which examines
capabilities exposed via the `capabilities` extension category.
Additionally, `platform/persistence/queue` decorates the persistence
capability specifically to batch persistence attempts among multiple
objects (this allows failures to be recognized and handled in groups.)
## Telemetry Service
```nomnoml
[<abstract> TelemetryService|
requestData(requests : Array.<TelemetryRequest>) : Promise.<object>
subscribe(requests : Array.<TelemetryRequest>) : Function
]<--:[TelemetryAggregator]
```
The telemetry service is responsible for acquiring telemetry data.
Notably, the platform does not include any providers for
`TelemetryService`; applications built on Open MCT will need to
implement a provider for this service if they wish to expose telemetry
data. This is usually the most important step for integrating Open MCT
into an existing telemetry system.
Requests for telemetry data are usually initiated in the
[presentation layer](#presentation-layer) by some `Controller` referenced
from a view. The `telemetryHandler` service is most commonly used (although
one could also use an object's `telemetry` capability directly) as this
handles capability delegation, by which a domain object such as a Telemetry
Panel can declare that its `telemetry` capability should be handled by the
objects it contains. Ultimately, the request for historical data and the
new subscriptions will reach the `TelemetryService`, and, by way of the
provider(s) which are present for that `TelemetryService`, will pass the
same requests to the back-end.
```nomnoml
[<start> Start]->[Controller]
[Controller]->[<state> declares object of interest]
[declares object of interest]->[TelemetryHandler]
[TelemetryHandler]->[<state> requests telemetry from capabilities]
[TelemetryHandler]->[<state> subscribes to telemetry using capabilities]
[requests telemetry from capabilities]->[TelemetryCapability]
[subscribes to telemetry using capabilities]->[TelemetryCapability]
[TelemetryCapability]->[<state> requests telemetry]
[TelemetryCapability]->[<state> subscribes to telemetry]
[requests telemetry]->[TelemetryService]
[subscribes to telemetry]->[TelemetryService]
[TelemetryService]->[<state> issues request]
[TelemetryService]->[<state> updates subscriptions]
[TelemetryService]->[<state> listens for real-time data]
[issues request]->[<database> Telemetry Back-end]
[updates subscriptions]->[Telemetry Back-end]
[listens for real-time data]->[Telemetry Back-end]
[Telemetry Back-end]->[<end> End]
```
The back-end, in turn, is expected to provide whatever historical
telemetry is available to satisfy the request that has been issue.
```nomnoml
[<start> Start]->[<database> Telemetry Back-end]
[Telemetry Back-end]->[<state> transmits historical telemetry]
[transmits historical telemetry]->[TelemetryService]
[TelemetryService]->[<state> packages telemetry, fulfills requests]
[packages telemetry, fulfills requests]->[TelemetryCapability]
[TelemetryCapability]->[<state> unpacks telemetry per-object, fulfills request]
[unpacks telemetry per-object, fulfills request]->[TelemetryHandler]
[TelemetryHandler]->[<state> exposes data]
[TelemetryHandler]->[<state> notifies controller]
[exposes data]->[Controller]
[notifies controller]->[Controller]
[Controller]->[<state> prepares data for template]
[prepares data for template]->[Template]
[Template]->[<state> displays data]
[displays data]->[<end> End]
```
One peculiarity of this approach is that we package many responses
together at once in the `TelemetryService`, then unpack these in the
`TelemetryCapability`, then repackage these in the `TelemetryHandler`.
The rationale for this is as follows:
* In the `TelemetryService`, we want to have the ability to combine
multiple requests into one call to the back-end, as many back-ends
will support this. It follows that we give the response as a single
object, packages in a manner that allows responses to individual
requests to be easily identified.
* In the `TelemetryCapability`, we want to provide telemetry for a
_single object_, so the telemetry data gets unpacked. This allows
for the unpacking of data to be handled in a single place, and
also permits a flexible substitution method; domain objects may have
implementations of the `telemetry` capability that do not use the
`TelemetryService` at all, while still maintaining compatibility
with any presentation layer code written to utilize this capability.
(This is true of capabilities generally.)
* In the `TelemetryHandler`, we want to group multiple responses back
together again to make it easy for the presentation layer to consume.
In this case, the grouping is different from what may have occurred
in the `TelemetryService`; this grouping is based on what is expected
to be useful _in a specific view_. The `TelemetryService`
may be receiving requests from multiple views.
```nomnoml
[<start> Start]->[<database> Telemetry Back-end]
[Telemetry Back-end]->[<state> notifies client of new data]
[notifies client of new data]->[TelemetryService]
[TelemetryService]->[<choice> relevant subscribers?]
[relevant subscribers?] yes ->[<state> notify subscribers]
[relevant subscribers?] no ->[<state> ignore]
[ignore]->[<end> Ignored]
[notify subscribers]->[TelemetryCapability]
[TelemetryCapability]->[<state> notify listener]
[notify listener]->[TelemetryHandler]
[TelemetryHandler]->[<state> exposes data]
[TelemetryHandler]->[<state> notifies controller]
[exposes data]->[Controller]
[notifies controller]->[Controller]
[Controller]->[<state> prepares data for template]
[prepares data for template]->[Template]
[Template]->[<state> displays data]
[displays data]->[<end> End]
```
The flow of real-time data is similar, and is handled by a sequence
of callbacks between the presentation layer component which is
interested in data and the telemetry service. Providers in the
telemetry service listen to the back-end for new data (via whatever
mechanism their specific back-end supports), package this data in
the same manner as historical data, and pass that to the callbacks
which are associated with relevant requests.
## Persistence Service
```nomnoml
#direction: right
[<abstract> PersistenceService|
listSpaces() : Promise.<Array.<string>>
listObjects() : Promise.<Array.<string>>
createObject(space : string, key : string, document : object) : Promise.<boolean>
readObject(space : string, key : string, document : object) : Promise.<object>
updateObject(space : string, key : string, document : object) : Promise.<boolean>
deleteObject(space : string, key : string, document : object) : Promise.<boolean>
]
[ElasticPersistenceProvider]--:>[PersistenceService]
[ElasticPersistenceProvider]->[<database> ElasticSearch]
[CouchPersistenceProvider]--:>[PersistenceService]
[CouchPersistenceProvider]->[<database> CouchDB]
```
Closely related to the notion of domain objects models is their
persistence. The `PersistenceService` allows these to be saved
and loaded. (Currently, this capability is only used for domain
object models, but the interface has been designed without this idea
in mind; other kinds of documents could be saved and loaded in the
same manner.)
There is no single definitive implementation of a `PersistenceService` in
the platform. Optional adapters are provided to store and load documents
from CouchDB and ElasticSearch, respectively; plugin authors may also
write additional adapters to utilize different back end technologies.
## Action Service
```nomnoml
[ActionService|
getActions(context : ActionContext) : Array.<Action>
]
[ActionProvider]--:>[ActionService]
[CreateActionProvider]--:>[ActionService]
[ActionAggregator]--:>[ActionService]
[LoggingActionDecorator]--:>[ActionService]
[PolicyActionDecorator]--:>[ActionService]
[LoggingActionDecorator]o-[PolicyActionDecorator]
[PolicyActionDecorator]o-[ActionAggregator]
[ActionAggregator]o-[ActionProvider]
[ActionAggregator]o-[CreateActionProvider]
[ActionProvider]o-[actions]
[CreateActionProvider]o-[TypeService]
[PolicyActionDecorator]o-[PolicyService]
```
Actions are discrete tasks or behaviors that can be initiated by a user
upon or using a domain object. Actions may appear as menu items or
buttons in the user interface, or may be triggered by certain gestures.
Responsibilities of platform components of the action service are as
follows:
* `ActionProvider` exposes actions registered via extension category
`actions`, supporting simple addition of new actions. Actions are
filtered down to match action contexts based on criteria defined as
part of an action's extension definition.
* `CreateActionProvider` provides the various Create actions which
populate the Create menu. These are driven by the available types,
so do not map easily to extension category `actions`; instead, these
are generated after looking up which actions are available from the
[`TypeService`](#type-service).
* `ActionAggregator` merges together actions from multiple providers.
* `PolicyActionDecorator` enforces the `action` policy category by
filtering out actions which violate this policy, as determined by
consulting the [`PolicyService`](#policy-service).
* `LoggingActionDecorator` wraps exposed actions and writes to the
console when they are performed.
## View Service
```nomnoml
[ViewService|
getViews(domainObject : DomainObject) : Array.<View>
]
[ViewProvider]--:>[ViewService]
[PolicyViewDecorator]--:>[ViewService]
[ViewProvider]o-[views]
[PolicyViewDecorator]o-[ViewProvider]
```
The view service provides views that are relevant to a specified domain
object. A "view" is a user-selectable visualization of a domain object.
The responsibilities of components of the view service are as follows:
* `ViewProvider` exposes views registered via extension category
`views`, supporting simple addition of new views. Views are
filtered down to match domain objects based on criteria defined as
part of a view's extension definition.
* `PolicyViewDecorator` enforces the `view` policy category by
filtering out views which violate this policy, as determined by
consulting the [`PolicyService`](#policy-service).
## Policy Service
```nomnoml
[PolicyService|
allow(category : string, candidate : object, context : object, callback? : Function) : boolean
]
[PolicyProvider]--:>[PolicyService]
[PolicyProvider]o-[policies]
```
The policy service provides a general-purpose extensible decision-making
mechanism; plugins can add new extensions of category `policies` to
modify decisions of a known category.
Often, the policy service is referenced from a decorator for another
service, to filter down the results of using that service based on some
appropriate policy category.
The policy provider works by looking up all registered policy extensions
which are relevant to a particular _category_, then consulting each in
order to see if they allow a particular _candidate_ in a particular
_context_; the types for the `candidate` and `context` arguments will
vary depending on the `category`. Any one policy may disallow the
decision as a whole.
```nomnoml
[<start> Start]->[<state> is something allowed?]
[is something allowed?]->[PolicyService]
[PolicyService]->[<state> look up relevant policies by category]
[look up relevant policies by category]->[<state> consult policy #1]
[consult policy #1]->[Policy #1]
[Policy #1]->[<choice> policy #1 allows?]
[policy #1 allows?] no ->[<state> decision disallowed]
[policy #1 allows?] yes ->[<state> consult policy #2]
[consult policy #2]->[Policy #2]
[Policy #2]->[<choice> policy #2 allows?]
[policy #2 allows?] no ->[<state> decision disallowed]
[policy #2 allows?] yes ->[<state> consult policy #3]
[consult policy #3]->[<state> ...]
[...]->[<state> consult policy #n]
[consult policy #n]->[Policy #n]
[Policy #n]->[<choice> policy #n allows?]
[policy #n allows?] no ->[<state> decision disallowed]
[policy #n allows?] yes ->[<state> decision allowed]
[decision disallowed]->[<end> Disallowed]
[decision allowed]->[<end> Allowed]
```
The policy decision is effectively an "and" operation over the individual
policy decisions: That is, all policies must agree to allow a particular
policy decision, and the first policy to disallow a decision will cause
the entire decision to be disallowed. As a consequence of this, policies
should generally be written with a default behavior of "allow", and
should only disallow the specific circumstances they are intended to
disallow.
## Type Service
```nomnoml
[TypeService|
listTypes() : Array.<Type>
getType(key : string) : Type
]
[TypeProvider]--:>[TypeService]
[TypeProvider]o-[types]
```
The type service provides metadata about the different types of domain
objects that exist within an Open MCT application. The platform
implementation reads these types in from extension category `types`
and wraps them in a JavaScript interface.

View File

@ -1,3 +0,0 @@
Design proposals:
* [API Redesign](proposals/APIRedesign.md)

View File

@ -1,338 +0,0 @@
# API Refactoring
This document summarizes a path toward implementing API changes
from the [API Redesign](../proposals/APIRedesign.md) for Open MCT
v1.0.0.
# Goals
These plans are intended to minimize:
* Waste; avoid allocating effort to temporary changes.
* Downtime; avoid making changes in large increments that blocks
delivery of new features for substantial periods of time.
* Risk; ensure that changes can be validated quickly, avoid putting
large effort into changes that have not been validated.
# Plan
```nomnoml
#comment: This diagram is in nomnoml syntax and should be rendered.
#comment: See https://github.com/nasa/openmctweb/issues/264#issuecomment-167166471
[<start> Start]->[<state> Imperative bundle registration]
[<state> Imperative bundle registration]->[<state> Build and packaging]
[<state> Imperative bundle registration]->[<state> Refactor API]
[<state> Build and packaging |
[<start> Start]->[<state> Incorporate a build step]
[<state> Incorporate a build step |
[<start> Start]->[<state> Choose package manager]
[<start> Start]->[<state> Choose build system]
[<state> Choose build system]<->[<state> Choose package manager]
[<state> Choose package manager]->[<state> Implement]
[<state> Choose build system]->[<state> Implement]
[<state> Implement]->[<end> End]
]->[<state> Separate repositories]
[<state> Separate repositories]->[<end> End]
]->[<state> Release candidacy]
[<start> Start]->[<state> Design registration API]
[<state> Design registration API |
[<start> Start]->[<state> Decide on role of Angular]
[<state> Decide on role of Angular]->[<state> Design API]
[<state> Design API]->[<choice> Passes review?]
[<choice> Passes review?] no ->[<state> Design API]
[<choice> Passes review?]-> yes [<end> End]
]->[<state> Refactor API]
[<state> Refactor API |
[<start> Start]->[<state> Imperative extension registration]
[<state> Imperative extension registration]->[<state> Refactor individual extensions]
[<state> Refactor individual extensions |
[<start> Start]->[<state> Prioritize]
[<state> Prioritize]->[<choice> Sufficient value added?]
[<choice> Sufficient value added?] no ->[<end> End]
[<choice> Sufficient value added?] yes ->[<state> Design]
[<state> Design]->[<choice> Passes review?]
[<choice> Passes review?] no ->[<state> Design]
[<choice> Passes review?]-> yes [<state> Implement]
[<state> Implement]->[<end> End]
]->[<state> Remove legacy bundle support]
[<state> Remove legacy bundle support]->[<end> End]
]->[<state> Release candidacy]
[<state> Release candidacy |
[<start> Start]->[<state> Verify |
[<start> Start]->[<choice> API well-documented?]
[<start> Start]->[<choice> API well-tested?]
[<choice> API well-documented?]-> no [<state> Write documentation]
[<choice> API well-documented?] yes ->[<end> End]
[<state> Write documentation]->[<choice> API well-documented?]
[<choice> API well-tested?]-> no [<state> Write test cases]
[<choice> API well-tested?]-> yes [<end> End]
[<state> Write test cases]->[<choice> API well-tested?]
]
[<start> Start]->[<state> Validate |
[<start> Start]->[<choice> Passes review?]
[<start> Start]->[<state> Use internally]
[<state> Use internally]->[<choice> Proves useful?]
[<choice> Passes review?]-> no [<state> Address feedback]
[<state> Address feedback]->[<choice> Passes review?]
[<choice> Passes review?] yes -> [<end> End]
[<choice> Proves useful?] yes -> [<end> End]
[<choice> Proves useful?] no -> [<state> Fix problems]
[<state> Fix problems]->[<state> Use internally]
]
[<state> Validate]->[<end> End]
[<state> Verify]->[<end> End]
]->[<state> Release]
[<state> Release]->[<end> End]
```
## Step 1. Imperative bundle registration
Register whole bundles imperatively, using their current format.
For example, in each bundle add a `bundle.js` file:
```js
define([
'mctRegistry',
'json!bundle.json'
], function (mctRegistry, bundle) {
mctRegistry.install(bundle, "path/to/bundle");
});
```
Where `mctRegistry.install` is placeholder API that wires into the
existing bundle registration mechanisms. The main point of entry
would need to be adapted to clearly depend on these bundles
(in the require sense of a dependency), and the framework layer
would need to implement and integrate with this transitional
API.
Benefits:
* Achieves an API Redesign goal with minimal immediate effort.
* Conversion to an imperative syntax may be trivially automated.
* Minimal change; reuse existing bundle definitions, primarily.
* Allows early validation of switch to imperative; unforeseen
consequences of the change may be detected at this point.
* Allows implementation effort to progress in parallel with decisions
about API changes, including fundamental ones such as the role of
Angular. May act in some sense as a prototype to inform those
decisions.
* Creates a location (framework layer) where subsequent changes to
the manner in which extensions are registered may be centralized.
When there is a one-to-one correspondence between the existing
form of an extension and its post-refactor form, adapters can be
written here to defer the task of making changes ubiquitously
throughout bundles, allowing for earlier validation and
verification of those changes, and avoiding ubiquitous changes
which might require us to go dark. (Mitigates
["greenfield paradox"](http://stepaheadsoftware.blogspot.com/2012/09/greenfield-or-refactor-legacy-code-base.html);
want to add value with new API but don't want to discard value
of tested/proven legacy codebase.)
Detriments:
* Requires transitional API to be implemented/supported; this is
waste. May mitigate this by time-bounding the effort put into
this step to ensure that waste is minimal.
Note that API changes at this point do not meaningfully reflect
the desired 1.0.0 API, so no API reviews are necessary.
## Step 2. Incorporate a build step
After the previous step is completed, there should be a
straightforward dependency graph among AMD modules, and an
imperative (albeit transitional) API allowing for other plugins
to register themselves. This should allow for a build step to
be included in a straightforward fashion.
Some goals for this build step:
* Compile (and, preferably, optimize/minify) Open MCT
sources into a single `.js` file.
* It is desirable to do the same for HTML sources, but
may wish to defer this until a subsequent refactoring
step if appropriate.
* Provide non-code assets in a format that can be reused by
derivative projects in a straightforward fashion.
Should also consider which dependency/packaging manager should
be used by dependent projects to obtain Open MCT. Approaches
include:
1. Plain `npm`. Dependents then declare their dependency with
`npm` and utilize built sources and assets in a documented
fashion. (Note that there are
[documented challenges](http://blog.npmjs.org/post/101775448305/npm-and-front-end-packaging)
in using `npm` in this fashion.)
2. Build with `npm`, but recommend dependents install using
`bower`, as this is intended for front-end development. This may
require checking in built products, however, which
we wish to avoid (this could be solved by maintaining
a separate repository for built products.)
In all cases, there is a related question of which build system
to use for asset generation/management and compilation/minification/etc.
1. [`webpack`](https://webpack.github.io/)
is well-suited in principle, as it is specifically
designed for modules with non-JS dependencies. However,
there may be limitations and/or undesired behavior here
(for instance, CSS dependencies get in-lined as style tags,
removing our ability to control ordering) so it may
2. `gulp` or `grunt`. Commonplace, but both still require
non-trivial coding and/or configuration in order to produce
appropriate build artifacts.
3. [Just `npm`](http://blog.keithcirkel.co.uk/how-to-use-npm-as-a-build-tool/).
Reduces the amount of tooling being used, but may introduce
some complexity (e.g. custom scripts) to the build process,
and may reduce portability.
## Step 3. Separate repositories
Refactor existing applications built on Open MCT such that they
are no longer forks, but instead separate projects with a dependency
on the built artifacts from Step 2.
Note that this is achievable already using `bower` (see `warp-bower`
branch at http://developer.nasa.gov/mct/warp for an example.)
However, changes involved in switching to an imperative API and
introducing a build process may change (and should simplify) the
approach used to utilize Open MCT as a dependency, so these
changes should be introduced first.
## Step 4. Design registration API
Design the registration API that will replace declarative extension
categories and extensions (including Angular built-ins and composite
services.)
This may occur in parallel with implementation steps.
It will be necessary
to have a decision about the role of Angular at this point; are extensions
registered via provider configuration (Angular), or directly in some
exposed registry?
Success criteria here should be based on peer review. Scope of peer
review should be based on perceived risk/uncertainty surrounding
proposed changes, to avoid waste; may wish to limit this review to
the internal team. (The extent to which external
feedback is available is limited, but there is an inherent timeliness
to external review; need to balance this.)
Benefits:
* Solves the "general case" early, allowing for early validation.
Note that in specific cases, it may be desirable to refactor some
current "extension category" in a manner that will not appear as
registries, _or_ to locate these in different
namespaces, _or_ to remove/replace certain categories entirely.
This work is deferred intentionally to allow for a solution of the
general case.
## Step 5. Imperative extension registration
Register individual extensions imperatively, implementing API changes
from the previous step. At this stage, _usage_ of the API may be confined
to a transitional adapter in the framework layer; bundles may continue
to utilize the transitional API for registering extensions in the
legacy format.
An important, ongoing sub-task here will be to discover and define dependencies
among bundles. Composite services and extension categories are presently
"implicit"; after the API redesign, these will become "explicit", insofar
as some specific component will be responsible for creating any registries.
As such, "bundles" which _use_ specific registries will need to have an
enforceable dependency (e.g. require) upon those "bundles" which
_declare_ those registries.
## Step 6. Refactor individual extensions
Refactor individual extension categories and/or services that have
been identified as needing changes. This includes, but is not
necessarily limited to:
* Views/Representations/Templates (refactored into "components.")
* Capabilities (refactored into "roles", potentially.)
* Telemetry (from `TelemetrySeries` to `TelemetryService`.)
Changes should be made one category at a time (either serially
or separately in parallel) and should involve a tight cycle of:
1. Prioritization/reprioritization; highest-value API improvements
should be done first.
2. Design.
3. Review. Refactoring individual extensions will require significant
effort (likely the most significant effort in the process) so changes
should be validated early to minimize risk/waste.
4. Implementation. These changes will not have a one-to-one relationship
with existing extensions, so changes cannot be centralized; usages
will need to be updated across all "bundles" instead of centralized
in a legacy adapter. If changes are of sufficient complexity, some
planning should be done to spread out the changes incrementally.
By necessity, these changes may break functionality in applications
built using Open MCT. On a case-by-case basis, should consider
providing temporary "legacy support" to allow downstream updates
to occur as a separate task; the relevant trade here is between
waste/effort required to maintain legacy support, versus the
downtime which may be introduced by making these changes simultaneously
across several repositories.
## Step 7. Remove legacy bundle support
Update bundles to remove any usages of legacy support for bundles
(including that used by dependent projects.) Then, remove legacy
support from Open MCT.
## Step 8. Release candidacy
Once API changes are complete, Open MCT should enter a release
candidacy cycle. Important things to look at here:
* Are changes really complete?
* Are they sufficiently documented?
* Are they sufficiently tested?
* Are changes really sufficient?
* Do reviewers think they are usable?
* Does the development team find them useful in practice? This
will require calendar time to ascertain; should allocate time
for this, particularly in alignment with the sprint/release
cycle.
* Has learning curve been measurably decreased? Comparing a to-do
list tutorial to [other examples(http://todomvc.com/) could
provide an empirical basis to this. How much code is required?
How much explanation is required? How many dependencies must
be installed before initial setup?
* Does the API offer sufficient power to implement the extensions we
anticipate?
* Any open API-related issues which should block a 1.0.0 release?
Any problems identified during release candidacy will require
subsequent design changes and planning.
## Step 9. Release
Once API changes have been verified and validated, proceed
with release, including:
* Tagging as version 1.0.0 (at an appropriate time in the
sprint/release cycle.)
* Close any open issues which have been resolved (or made obsolete)
by API changes.

File diff suppressed because it is too large Load Diff

View File

@ -1,251 +0,0 @@
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [Reducing interface depth (the bundle.json version)](#reducing-interface-depth-the-bundlejson-version)
- [Imperative component registries](#imperative-component-registries)
- [Get rid of "extension category" concept.](#get-rid-of-extension-category-concept)
- [Reduce number and depth of extension points](#reduce-number-and-depth-of-extension-points)
- [Composite services should not be the default](#composite-services-should-not-be-the-default)
- [Get rid of views, representations, and templates.](#get-rid-of-views-representations-and-templates)
- [Reducing interface depth (The angular discussion)](#reducing-interface-depth-the-angular-discussion)
- [More angular: for all services](#more-angular-for-all-services)
- [Less angular: only for views](#less-angular-only-for-views)
- [Standard packaging and build system](#standard-packaging-and-build-system)
- [Use systemjs for module loading](#use-systemjs-for-module-loading)
- [Use gulp or grunt for standard tooling](#use-gulp-or-grunt-for-standard-tooling)
- [Package openmctweb as single versioned file.](#package-openmctweb-as-single-versioned-file)
- [Misc Improvements](#misc-improvements)
- [Refresh on navigation](#refresh-on-navigation)
- [Move persistence adapter to promise rejection.](#move-persistence-adapter-to-promise-rejection)
- [Remove bulk requests from providers](#remove-bulk-requests-from-providers)
- [Notes on current API proposals:](#notes-on-current-api-proposals)
- [[1] Footnote: The angular debacle](#1-footnote-the-angular-debacle)
- ["Do or do not, there is no try"](#do-or-do-not-there-is-no-try)
- [A lack of commitment](#a-lack-of-commitment)
- [Commitment is good!](#commitment-is-good)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
# Reducing interface depth (the bundle.json version)
## Imperative component registries
Transition component registries to javascript, get rid of bundle.json and bundles.json. Prescribe a method for application configuration, but allow flexibility in how application configuration is defined.
Register components in an imperative fashion, see angularApp.factory, angularApp.controller, etc. Alternatively, implement our own application object with new registries and it's own form of registering objects.
## Get rid of "extension category" concept.
The concept of an "extension category" is itself an extraneous concept-- an extra layer of interface depth, an extra thing to learn before you can say "hello world". Extension points should be clearly supported and documented with whatever interfaces make sense. Developers who wish to add something that is conceptually equivalent to an extension category can do so directly, in the manner that suites their needs, without us forcing a common method on them.
## Reduce number and depth of extension points
Clearly specify supported extension points (e.g. persistence, model providers, telemetry providers, routes, time systems), but don't claim that the system has a clear and perfect repeatable solution for unknown extension types. New extension categories can be implemented in whatever way makes sense, without prescribing "the one and only system for managing extensions".
The underlying problem here is we are predicting needs for extension points where none exist-- if we try and design the extension system before we know how it is used, we design the wrong thing and have to rewrite it later.
## Composite services should not be the default
Understanding composite services, and describing services as composite services can confuse developers. Aggregators are implemented once and forgotten, while decorators tend to be hacky, brittle solutions that are generally needed to avoid circular imports. While composite services are a useful construct, it reduces interface depth to implement them as registries + typed providers.
You can write a provider (provides "thing x" for "inputs y") with a simple interface. A provider has two or more methods:
* a method which takes "inputs y" and returns True if it knows how to provide "thing x", false otherwise.
* one or more methods which provide "thing x" for objects of "inputs y".
Actually checking whether a provider can respond to a request before asking it to do work allows for faster failure and clearer errors when no providers match the request.
## Get rid of views, representations, and templates.
Templates are an implementation detail that should be handled by module loaders. Views and representations become "components," and a new concept, "routes", is used to exposing specific views to end users.
`components` - building blocks for views, have clear inputs and outputs, and can be coupled to other components when it makes sense. (e.g. parent-child components such as menu and menu item), but should have ZERO knowledge of our data models or telemetry apis. They should define data models that enable them to do their job well while still being easy to test.
`routes` - a view type for a given domain object, e.g. a plot, table, display layout, etc. Can be described as "whatever shows in the main screen when you are viewing an object." Handle loading of data from a domain object and passing that data to the view components. Routes should support editing as it makes sense in their own context.
To facilitate testing:
* routes should be testable without having to test the actual view.
* components should be independently testable with zero knowledge of our data models or telemetry APIs.
Component code should be organized side by side, such as:
```
app
|- components
|- productDetail
| |- productDetail.js
| |- productDetail.css
| |- productDetail.html
| |- productDetailSpec.js
|- productList
|- checkout
|- wishlist
```
Components are not always reusable, and we shouldn't be overly concerned with making them so. If components are heavily reused, they should either be moved to a platform feature (e.g. notifications, indicators), or broken off as an external dependency (e.g. publish mct-plot as mct-plot.js).
# Reducing interface depth (The angular discussion)
Two options here: use more angular, use less angular. Wrapping angular methods does not reduce interface depth and must be avoided.
The primary issue with angular is duplications of concerns-- both angular and the openmctweb platform implement the same tools side by side and it can be hard to comprehend-- it increases interface depth. For other concerns, see footnotes[1].
Wrapping angular methods for non-view related code is confusing to developers because of the random constraints angular places on these items-- developers ultimately have to understand both angular DI and our framework. For example, it's not possible to name the topic service "topicService" because angular expects Services to be implemented by Providers, which is different than our expectation.
To reduce interface depth, we can replace our own provider and registry patterns with angular patterns, or we can only utilize angular view logic, and only use our own DI patterns.
## More angular: for all services
Increasing our commitment to angular would mean using more of the angular factories, services, etc, and less of our home grown tools. We'd implement our services and extension points as angular providers, and make them configurable via app.config.
As an example, registering a specific type of model provider in angular would look like:
```javascript
mct.provider('model', modelProvider() { /* implementation */});
mct.config(['modelProvider', function (modelProvider) {
modelProvider.providers.push(RootModelProvider);
}]);
```
## Less angular: only for views
If we wish to use less angular, I would recommend discontinuing use of all angular components that are not view related-- services, factories, $http, etc, and implementing them in our own paradigm. Otherwise, we end up with layered interfaces-- one of the goals we would like to avoid.
# Standard packaging and build system
Standardize the packaging and build system, and completely separate the core platform from deployments. Prescribe a starting point for deployments, but allow flexibility.
## Use systemjs for module loading
Allow developers to use whatever module loading system they'd like to use, while still supporting all standard cases. We should also use this system for loading assets (css, scss, html templates), which makes it easier to implement a single file deployment using standard build tooling.
## Use gulp or grunt for standard tooling
Using gulp or grunt as a task runner would bring us in line with standard web developer workflows and help standardize rendering, deployment, and packaging. Additional tools can be added to the workflow at low cost, simplifying the setup of developer environments.
Gulp and grunt provide useful developer tooling such as live reload, automatic scss/less/etc compilation, and ease of extensibility for standard production build processes. They're key in decoupling code.
## Package openmctweb as single versioned file.
Deployments should depend on a specific version of openmctweb, but otherwise be allowed to have their own deployment and development toolsets.
Customizations and deployments of openmctweb should not use the same build tooling as the core platform; instead they should be free to use their own build tools as they wish. (We would provide a template for an application, based on our experience with warp-for-rp and vista)
Installation and utilization of openmctweb should be as simple as downloading the js file, including it in your own html page, and then initializing an app and running it. If a developer would prefer, they could use bower or npm to handle installation.
Then, if we're using imperative methods for extending the application we can use the following for basic customization:
```html
<script src="//localhost/openmctweb.js"></script>
<script>
// can configure from object
var myApp = new OpenMCTWeb({
persistence: {
providers: [
{
type: 'elastic',
uri: 'http://someElasticHost/'
} // ...
]
}
});
// alternative configurations
myApp.persistence.addProvider(MyPersistenceAdapter);
myApp.model.addProvider(someProviderObject);
// Removing via method
myApp.persistence.removeProvider('some method for removing functionality');
// directly mutating providers
myApp.persistence.providers = [ThisProviderStandsAlone];
//
myApp.run();
</script>
```
This packaging reduces the complexity of managing multiple deployed versions, and also allows us to provide users with incredibly simple tutorials-- they can use whatever tooling they like. For instance, a hello world tutorial may take the option of "exposing a new object in the tree".
```javascript
var myApp = new OpenMCTWeb();
myApp.roots.addRoot({
id: 'myRoot',
name: 'Hello World!',
});
myApp.routes.setDefault('myRoot');
myApp.run();
```
# Misc Improvements
## Refresh on navigation
In cases where navigation events change the entire screen, we should be using routes and location changes to navigate between objects. We should be using href for all navigation events.
At the same time, navigating should refresh state of every visible object. A properly configured persistence store will handle caching with standard cache headers and 304 not modified responses, which will provide good performance of object reloads, while helping us ensure that objects are always in sync between clients.
View state (say, the expanded tree nodes) should not be tied to caching of data-- it should be something we intentionally persist and restore with each navigation. Data (such as object definitions) should be reloaded from server as necessary to restore state.
## Move persistence adapter to promise rejection.
Simple: reject on fail, resolve on success.
## Remove bulk requests from providers
Aggregators can request multiple things at once, but individual providers should only have to implement handling at the level of a single request. Each provider can implement it's own internal batching, but it should support making requests at a finer level of detail.
Excessive wrapping of code with $q.all causes additional digest cycles and decreased performance.
For example, instead of every telemetry provider responding to a given telemetry request, aggregators should route each request to the first provider that can fulfill that request.
# Notes on current API proposals:
* [RequireJS for Dependency Injection](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#requirejs-as-dependency-injector): requires other topics to be discussed first.
* [Arbitrary HTML Views](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#arbitrary-html-views): think there is a place for it, requires other topics to be discussed first.
* [Wrap Angular Services](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#wrap-angular-services): No, this is bad.
* [Bundle definitions in Javascript](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#bundle-declarations-in-javascript): Points to a solution, but ultimately requires more discussion.
* [pass around a dependency injector](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#pass-around-a-dependency-injector): No.
* [remove partial constructors](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#remove-partial-constructors): Yes, this should be superseded by another proposal though. The entire concept was a messy solution to dependency injection issues caused by declarative syntax.
* [Rename views to applications](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#rename-views-to-applications): Points to a problem that needs to be solved but I think the name is bad.
* [Provide classes for extensions](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#provide-classes-for-extensions): Yes, in specific places
* [Normalize naming conventions](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#normalize-naming-conventions): Yes.
* [Expose no third-party APIs](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#expose-no-third-party-apis): Completely disagree, points to a real problem with poor angular integration.
* [Register Extensions as Instances instead of Constructors](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#register-extensions-as-instances-instead-of-constructors): Superseded by the fact that we should not hope to implement a generic construct.
* [Remove capability delegation](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#remove-capability-delegation): Yes.
* [Nomenclature Change](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#nomenclature-change): Yes, hope to discuss the implications of this more clearly in other proposals.
* [Capabilities as mixins](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#capabilities-as-mixins): Yes.
* [Remove appliesTo methods](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#remove-applies-to-methods): No-- I think some level of this is necessary. I think a more holistic approach to policy is needed. it's a rather complicated system.
* [Revise telemetry API](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#revise-telemetry-api): If we can rough out and agree to the specifics, then Yes. Needs discussion.
* [Allow composite services to fail gracefully](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#allow-composite-services-to-fail-gracefully): No. As mentioned above, I think composite services themselves should be eliminated for a more purpose bound tool.
* [Plugins as angular modules](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#plugins-as-angular-modules): Should we decide to embrace Angular completely, I would support this. Otherwise, no.
* [Contextual Injection](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#contextual-injection): No, don't see a need.
* [Add New Abstractions for Actions](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#add-new-abstractions-for-actions): Worth a discussion.
* [Add gesture handlers](https://github.com/nasa/openmctweb/blob/api-redesign/docs/src/design/proposals/APIRedesign.md#add-gesture-handlers): Yes if we can agree on details. We need a platform implementation that is easy to use, but we should not reinvent the wheel.
# [1] Footnote: The angular debacle
## "Do or do not, there is no try"
A commonly voiced concern of embracing angular is the possibility of becoming dependent on a third party framework. This concern is itself detrimental-- if we're afraid of becoming dependent on a third party framework, then we will do a bad job of using the framework, and inevitably will want to stop using it.
If we're using a framework, we need to use it fully, or not use it at all.
## A lack of commitment
A number of the concerns we heard from developers and interns can be attributed to the tenuous relationship between the OpenMCTWeb platform and angular. We claimed to be angular, but we weren't really angular. Instead, we are caught between our incomplete framework paradigm and the angular paradigm. In many cases we reinvented the wheel or worked around functionality that angular provides, and ended up in a more confusing state.
## Commitment is good!
We could just be an application that is built with angular.
An application that is modular and extensible not because it reinvents tools for providing modularity and extensibility, but because it reuses existing tools for modularity and extensibility.
There are benefits to buying into the angular paradigm: shift documentation burden to external project, engage a larger talent pool available both as voluntary open source contributors and as experienced developers for hire, and gain access to an ecosystem of tools that we can use to increase the speed of development.
There are negatives too: Angular is a monolith, it has performance concerns, and an unclear future. If we can't live with it, we should look at alternatives.

View File

@ -1,164 +0,0 @@
# Imperative Plugins
This is a design proposal for handling
[bundle declarations in JavaScript](
APIRedesign.md#bundle-declarations-in-javascript).
## Developer Use Cases
Developers will want to use bundles/plugins to (in rough order
of occurrence):
1. Add new extension instances.
2. Use existing services
3. Add new service implementations.
4. Decorate service implementations.
5. Decorate extension instances.
6. Add new types of services.
7. Add new extension categories.
Notably, bullets 4 and 5 above are currently handled implicitly,
which has been cited as a source of confusion.
## Interfaces
Two base classes may be used to satisfy these use cases:
* The `CompositeServiceFactory` provides composite service instances.
Decorators may be added; the approach used for compositing may be
modified; and individual services may be registered to support compositing.
* The `ExtensionRegistry` allows for the simpler case where what is desired
is an array of all instances of some kind of thing within the system.
Note that additional developer use cases may be supported by using the
more general-purpose `Registry`
```nomnoml
[Factory.<T, V>
|
- factoryFn : function (V) : T
|
+ decorate(decoratorFn : function (T, V) : T, options? : RegistrationOptions)
]-:>[function (V) : T]
[RegistrationOptions |
+ priority : number or string
]
[Registry.<T, V>
|
- compositorFn : function (Array.<T>) : V
|
+ register(item : T, options? : RegistrationOptions)
+ composite(compositorFn : function (Array.<T>) : V, options? : RegistrationOptions)
]-:>[Factory.<V, Void>]
[Factory.<V, Void>]-:>[Factory.<T, V>]
[ExtensionRegistry.<T>]-:>[Registry.<T, Array.<T>>]
[Registry.<T, Array.<T>>]-:>[Registry.<T, V>]
[CompositeServiceFactory.<T>]-:>[Registry.<T, T>]
[Registry.<T, T>]-:>[Registry.<T, V>]
```
## Examples
### 1. Add new extension instances.
```js
// Instance-style registration
mct.types.register(new mct.Type({
key: "timeline",
name: "Timeline",
description: "A container for activities ordered in time."
});
// Factory-style registration
mct.actions.register(function (domainObject) {
return new RemoveAction(domainObject);
}, { priority: 200 });
```
### 2. Use existing services
```js
mct.actions.register(function (domainObject) {
var dialogService = mct.ui.dialogServiceFactory();
return new PropertiesAction(dialogService, domainObject);
});
```
### 3. Add new service implementations
```js
// Instance-style registration
mct.persistenceServiceFactory.register(new LocalPersistenceService());
// Factory-style registration
mct.persistenceServiceFactory.register(function () {
var $http = angular.injector(['ng']).get('$http');
return new LocalPersistenceService($http);
});
```
### 4. Decorate service implementations
```js
mct.modelServiceFactory.decorate(function (modelService) {
return new CachingModelDecorator(modelService);
}, { priority: 100 });
```
### 5. Decorate extension instances
```js
mct.capabilities.decorate(function (capabilities) {
return capabilities.map(decorateIfApplicable);
});
```
This use case is not well-supported by these API changes. The most
common case for decoration is capabilities, which are under reconsideration;
should consider handling decoration of capabilities in a different way.
### 6. Add new types of services
```js
myModule.myServiceFactory = new mct.CompositeServiceFactory();
// In cases where a custom composition strategy is desired
myModule.myServiceFactory.composite(function (services) {
return new MyServiceCompositor(services);
});
```
### 7. Add new extension categories.
```js
myModule.hamburgers = new mct.ExtensionRegistry();
```
## Evaluation
### Benefits
* Encourages separation of registration from declaration (individual
components are decoupled from the manner in which they are added
to the architecture.)
* Minimizes "magic." Dependencies are acquired, managed, and exposed
using plain-old-JavaScript without any dependency injector present
to obfuscate what is happening.
* Offers comparable expressive power to existing APIs; can still
extend the behavior of platform components in a variety of ways.
* Does not force or limit formalisms to use;
### Detriments
* Does not encourage separation of dependency acquisition from
declaration; that is, it would be quite natural using this API
to acquire references to services during the constructor call
to an extension or service. But, passing these in as constructor
arguments is preferred (to separate implementation from architecture.)
* Adds (negligible?) boilerplate relative to declarative syntax.
* Relies on factories, increasing number of interfaces to be concerned
with.

View File

@ -1,138 +0,0 @@
# Roles
Roles are presented as an alternative formulation to capabilities
(dynamic behavior associated with individual domain objects.)
Specific goals here:
* Dependencies of individual scripts should be clear.
* Domain objects should be able to selectively exhibit a wide
variety of behaviors.
## Developer Use Cases
1. Checking for the existence of behavior.
2. Using behavior.
3. Augmenting existing behaviors.
4. Overriding existing behaviors.
5. Adding new behaviors.
## Overview of Proposed Solution
Remove `getCapability` from domain objects; add roles as external
services which can be applied to domain objects.
## Interfaces
```nomnoml
[Factory.<T, V>
|
- factoryFn : function (V) : T
|
+ decorate(decoratorFn : function (T, V) : T, options? : RegistrationOptions)
]-:>[function (V) : T]
[RegistrationOptions |
+ priority : number or string
]<:-[RoleOptions |
+ validate : function (DomainObject) : boolean
]
[Role.<T> |
+ validate(domainObject : DomainObject) : boolean
+ decorate(decoratorFn : function (T, V) : T, options? : RoleOptions)
]-:>[Factory.<T, DomainObject>]
[Factory.<T, DomainObject>]-:>[Factory.<T, V>]
```
## Examples
### 1. Checking for the existence of behavior
```js
function PlotViewPolicy(telemetryRole) {
this.telemetryRole = telemetryRole;
}
PlotViewPolicy.prototype.allow = function (view, domainObject) {
return this.telemetryRole.validate(domainObject);
};
```
### 2. Using behavior
```js
PropertiesAction.prototype.perform = function () {
var mutation = this.mutationRole(this.domainObject);
return this.showDialog.then(function (newModel) {
return mutation.mutate(function () {
return newModel;
});
});
};
```
### 3. Augmenting existing behaviors
```js
// Non-Angular style
mct.roles.persistenceRole.decorate(function (persistence) {
return new DecoratedPersistence(persistence);
});
// Angular style
myModule.decorate('persistenceRole', ['$delegate', function ($delegate) {
return new DecoratedPersistence(persistence);
}]);
```
### 4. Overriding existing behaviors
```js
// Non-Angular style
mct.roles.persistenceRole.decorate(function (persistence, domainObject) {
return domainObject.getModel().type === 'someType' ?
new DifferentPersistence(domainObject) :
persistence;
}, {
validate: function (domainObject, next) {
return domainObject.getModel().type === 'someType' || next();
}
});
```
### 5. Adding new behaviors
```js
function FooRole() {
mct.Role.apply(this, [function (domainObject) {
return new Foo(domainObject);
}]);
}
FooRole.prototype = Object.create(mct.Role.prototype);
FooRole.prototype.validate = function (domainObject) {
return domainObject.getModel().type === 'some-type';
};
//
myModule.roles.fooRole = new FooRole();
```
## Evaluation
### Benefits
* Simplifies/standardizes augmentation or replacement of behavior associated
with specific domain objects.
* Minimizes number of abstractions; roles are just factories.
* Clarifies dependencies; roles used must be declared/acquired in the
same manner as services.
### Detriments
* Externalizes functionality which is conceptually associated with a
domain object.
* Relies on factories, increasing number of interfaces to be concerned
with.

File diff suppressed because it is too large Load Diff

View File

@ -22,16 +22,5 @@
* The [Development Process](process/) document describes the
Open MCT software development cycle.
## Legacy Documentation
As we transition to a new API, the following documentation for the old API
(which is supported during the transition) may be useful as well:
* The [Architecture Overview](architecture/) describes the concepts used
throughout Open MCT, and gives a high level overview of the platform's design.
* The [Developer's Guide](guide/) goes into more detail about how to use the
platform and the functionality that it provides.
* The [Tutorials](https://github.com/nasa/openmct-tutorial) give examples of extending the platform to add
functionality, and integrate with data sources.

View File

@ -7,9 +7,5 @@ documents:
process points are repeated during development.
* The [Version Guide](version.md) describes version numbering for
Open MCT (both semantics and process.)
* Testing is described in two documents:
* The [Test Plan](testing/plan.md) summarizes the approaches used
to test Open MCT.
* The [Test Procedures](testing/procedures.md) document what
specific tests are performed to verify correctness, and how
they should be carried out.
* The [Test Plan](testing/plan.md) summarizes the approaches used
to test Open MCT.

View File

@ -1,169 +0,0 @@
# Test Procedures
## Introduction
This document is intended to be used:
* By testers, to verify that Open MCT behaves as specified.
* By the development team, to document new test cases and to provide
guidance on how to author these.
## Writing Procedures
### Template
Procedures for individual tests should use the following template,
adapted from [https://swehb.nasa.gov/display/7150/SWE-114]().
Property | Value
---------------|---------------------------------------------------------------
Test ID |
Relevant reqs. |
Prerequisites |
Test input |
Instructions |
Expectation |
Eval. criteria |
For multi-line descriptions, use an asterisk or similar indicator to refer
to a longer-form description below.
#### Example Procedure - Edit a Layout
Property | Value
---------------|---------------------------------------------------------------
Test ID | MCT-TEST-000X - Edit a layout
Relevant reqs. | MCT-EDIT-000Y
Prerequisites | Create a layout, as in MCT-TEST-000Z
Test input | Domain object database XYZ
Instructions | See below &ast;
Expectation | Change to editing context &dagger;
Eval. criteria | Visual inspection
&ast; Follow the following steps:
1. Verify that the created layout is currently navigated-to,
as in MCT-TEST-00ZZ.
2. Click the Edit button, identified by a pencil icon and the text "Edit"
displayed on hover.
&dagger; Right-hand viewing area should be surrounded by a dashed
blue border when a domain object is being edited.
### Guidelines
Test procedures should be written assuming minimal prior knowledge of the
application: Non-standard terms should only be used when they are documented
in [the glossary](#glossary), and shorthands used for user actions should
be accompanied by useful references to test procedures describing those
actions (when available) or descriptions in user documentation.
Test cases should be narrow in scope; if a list of steps is excessively
long (or must be written vaguely to be kept short) it should be broken
down into multiple tests which reference one another.
All requirements satisfied by Open MCT should be verifiable using
one or more test procedures.
## Glossary
This section will contain terms used in test procedures. This may link to
a common glossary, to avoid replication of content.
## Procedures
This section will contain specific test procedures. Presently, procedures
are placeholders describing general patterns for setting up and conducting
testing.
### User Testing Setup
These procedures describes a general pattern for setting up for user
testing. Specific deployments should customize this pattern with
relevant data and any additional steps necessary.
Property | Value
---------------|---------------------------------------------------------------
Test ID | MCT-TEST-SETUP0 - User Testing Setup
Relevant reqs. | TBD
Prerequisites | Build of relevant components
Test input | Exemplary database; exemplary telemetry data set
Instructions | See below
Expectation | Able to load application in a web browser (Google Chrome)
Eval. criteria | Visual inspection
Instructions:
1. Start telemetry server.
2. Start ElasticSearch.
3. Restore database snapshot to ElasticSearch.
4. Start telemetry playback.
5. Start HTTP server for client sources.
### User Test Procedures
Specific user test cases have not yet been authored. In their absence,
user testing is conducted by:
* Reviewing the text of issues from the issue tracker to understand the
desired behavior, and exercising this behavior in the running application.
(For instance, by following steps to reproduce from the original issue.)
* Issues which appear to be resolved should be marked as such with comments
on the original issue (e.g. "verified during user testing MM/DD/YYYY".)
* Issues which appear not to have been resolved should be reopened with an
explanation of what unexpected behavior has been observed.
* In cases where an issue appears resolved as-worded but other related
undesirable behavior is observed during testing, a new issue should be
opened, and linked to from a comment in the original issues.
* General usage of new features and/or existing features which have undergone
recent changes. Defects or problems with usability should be documented
by filing issues in the issue tracker.
* Open-ended testing to discover defects, identify usability issues, and
generate feature requests.
### Long-Duration Testing
The purpose of long-duration testing is to identify performance issues
and/or other defects which are sensitive to the amount of time the
application is kept running. (Memory leaks, for instance.)
Property | Value
---------------|---------------------------------------------------------------
Test ID | MCT-TEST-LDT0 - Long-duration Testing
Relevant reqs. | TBD
Prerequisites | MCT-TEST-SETUP0
Test input | (As for test setup.)
Instructions | See "Instructions" below &ast;
Expectation | See "Expectations" below &dagger;
Eval. criteria | Visual inspection
&ast; Instructions:
1. Start `top` or a similar tool to measure CPU usage and memory utilization.
2. Open several user-created displays (as many as would be realistically
opened during actual usage in a stressing case) in some combination of
separate tabs and windows (approximately as many tabs-per-window as
total windows.)
3. Ensure that playback data is set to run continuously for at least 24 hours
(e.g. on a loop.)
4. Record CPU usage and memory utilization.
5. In at least one tab, try some general user interface gestures and make
notes about the subjective experience of using the application. (Particularly,
the degree of responsiveness.)
6. Leave client displays open for 24 hours.
7. Record CPU usage and memory utilization again.
8. Make additional notes about the subjective experience of using the
application (again, particularly responsiveness.)
9. Check logs for any unexpected warnings or errors.
&dagger; Expectations:
* At the end of the test, CPU usage and memory usage should both be similar
to their levels at the start of the test.
* At the end of the test, subjective usage of the application should not
be observably different from the way it was at the start of the test.
(In particular, responsiveness should not decrease.)
* Logs should not contain any unexpected warnings or errors ("expected"
warnings or errors are those that have been documented and prioritized
as known issues, or those that are explained by transient conditions
external to the software, such as network outages.)

View File

@ -1,4 +1,12 @@
/* eslint-disable no-undef */
module.exports = {
"extends": ["plugin:playwright/playwright-test"]
"extends": ["plugin:playwright/playwright-test"],
"overrides": [
{
"files": ["tests/visual/*.spec.js"],
"rules": {
"playwright/no-wait-for-timeout": "off"
}
}
]
};

5
e2e/.percy.yml Normal file
View File

@ -0,0 +1,5 @@
version: 2
snapshot:
widths: [1024, 2000]
min-height: 1440 # px

27
e2e/fixtures.js Normal file
View File

@ -0,0 +1,27 @@
/* eslint-disable no-undef */
// This file extends the base functionality of the playwright test framework
const base = require('@playwright/test');
const { expect } = require('@playwright/test');
exports.test = base.test.extend({
page: async ({ baseURL, page }, use) => {
const messages = [];
page.on('console', msg => messages.push(`[${msg.type()}] ${msg.text()}`));
await use(page);
await expect.soft(messages.toString()).not.toContain('[error]');
},
browser: async ({ playwright, browser }, use, workerInfo) => {
// Use browserless if configured
if (workerInfo.project.name.match(/browserless/)) {
const vBrowser = await playwright.chromium.connectOverCDP({
endpointURL: 'ws://localhost:3003'
});
await use(vBrowser);
} else {
// Use Local Browser for testing.
await use(browser);
}
}
});

View File

@ -2,31 +2,65 @@
// playwright.config.js
// @ts-check
// eslint-disable-next-line no-unused-vars
const { devices } = require('@playwright/test');
/** @type {import('@playwright/test').PlaywrightTestConfig} */
const config = {
retries: 2,
retries: 1,
testDir: 'tests',
timeout: 90 * 1000,
testIgnore: '**/*.perf.spec.js', //Ignore performance tests and define in playwright-perfromance.config.js
timeout: 60 * 1000,
webServer: {
command: 'npm run start',
port: 8080,
timeout: 200 * 1000,
reuseExistingServer: !process.env.CI
},
maxFailures: process.env.CI ? 5 : undefined, //Limits failures to 5 to reduce CI Waste
workers: 2, //Limit to 2 for CircleCI Agent
use: {
browserName: "chromium",
baseURL: 'http://localhost:8080/',
headless: true,
ignoreHTTPSErrors: true,
screenshot: 'on',
trace: 'on',
video: 'on'
screenshot: 'only-on-failure',
trace: 'on-first-retry',
video: 'on-first-retry'
},
projects: [
{
name: 'chrome',
use: {
browserName: 'chromium'
}
},
{
name: 'MMOC',
grepInvert: /@snapshot/,
use: {
browserName: 'chromium',
viewport: {
width: 2560,
height: 1440
}
}
}
/*{
name: 'ipad',
use: {
browserName: 'webkit',
...devices['iPad (gen 7) landscape'] // Complete List https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/server/deviceDescriptorsSource.json
}
}*/
],
reporter: [
['list'],
['html', {
open: 'never',
outputFolder: '../test-results/html/'
}],
['junit', { outputFile: 'test-results/results.xml' }],
['allure-playwright']
['github']
]
};

View File

@ -2,10 +2,14 @@
// playwright.config.js
// @ts-check
// eslint-disable-next-line no-unused-vars
const { devices } = require('@playwright/test');
/** @type {import('@playwright/test').PlaywrightTestConfig} */
const config = {
retries: 0,
testDir: 'tests',
testIgnore: '**/*.perf.spec.js',
timeout: 30 * 1000,
webServer: {
command: 'npm run start',
@ -13,18 +17,48 @@ const config = {
timeout: 120 * 1000,
reuseExistingServer: !process.env.CI
},
workers: 1,
use: {
browserName: "chromium",
baseURL: 'http://localhost:8080/',
headless: false,
ignoreHTTPSErrors: true,
screenshot: 'on',
trace: 'on',
video: 'on'
screenshot: 'only-on-failure',
trace: 'retain-on-failure',
video: 'retain-on-failure'
},
projects: [
{
name: 'chrome',
use: {
browserName: 'chromium'
}
},
{
name: 'MMOC',
grepInvert: /@snapshot/,
use: {
browserName: 'chromium',
viewport: {
width: 2560,
height: 1440
}
}
}
/*{
name: 'ipad',
use: {
browserName: 'webkit',
...devices['iPad (gen 7) landscape'] // Complete List https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/server/deviceDescriptorsSource.json
}
}*/
],
reporter: [
['list'],
['allure-playwright']
['html', {
open: 'on-failure',
outputFolder: '../test-results'
}]
]
};

View File

@ -0,0 +1,41 @@
/* eslint-disable no-undef */
// playwright.config.js
// @ts-check
/** @type {import('@playwright/test').PlaywrightTestConfig} */
const config = {
retries: 1, //Only for debugging purposes
testDir: 'tests/performance/',
timeout: 60 * 1000,
workers: 1, //Only run in serial with 1 worker
webServer: {
command: 'npm run start',
port: 8080,
timeout: 200 * 1000,
reuseExistingServer: !process.env.CI
},
use: {
browserName: "chromium",
baseURL: 'http://localhost:8080/',
headless: Boolean(process.env.CI), //Only if running locally
ignoreHTTPSErrors: true,
screenshot: 'off',
trace: 'on-first-retry',
video: 'off'
},
projects: [
{
name: 'chrome',
use: {
browserName: 'chromium'
}
}
],
reporter: [
['list'],
['junit', { outputFile: 'test-results/results.xml' }],
['json', { outputFile: 'test-results/results.json' }]
]
};
module.exports = config;

View File

@ -4,10 +4,10 @@
/** @type {import('@playwright/test').PlaywrightTestConfig} */
const config = {
retries: 0,
testDir: 'tests',
retries: 0, // visual tests should never retry due to snapshot comparison errors
testDir: 'tests/visual',
timeout: 90 * 1000,
workers: 1,
workers: 1, // visual tests should never run in parallel due to test pollution
webServer: {
command: 'npm run start',
port: 8080,
@ -17,7 +17,7 @@ const config = {
use: {
browserName: "chromium",
baseURL: 'http://localhost:8080/',
headless: true,
headless: true, // this needs to remain headless to avoid visual changes due to GPU
ignoreHTTPSErrors: true,
screenshot: 'on',
trace: 'off',
@ -25,8 +25,7 @@ const config = {
},
reporter: [
['list'],
['junit', { outputFile: 'test-results/results.xml' }],
['allure-playwright']
['junit', { outputFile: 'test-results/results.xml' }]
]
};

View File

@ -0,0 +1 @@
{"openmct":{"b3cee102-86dd-4c0a-8eec-4d5d276f8691":{"identifier":{"key":"b3cee102-86dd-4c0a-8eec-4d5d276f8691","namespace":""},"name":"Performance Display Layout","type":"layout","composition":[{"key":"9666e7b4-be0c-47a5-94b8-99accad7155e","namespace":""}],"configuration":{"items":[{"width":32,"height":18,"x":12,"y":9,"identifier":{"key":"9666e7b4-be0c-47a5-94b8-99accad7155e","namespace":""},"hasFrame":true,"fontSize":"default","font":"default","type":"subobject-view","id":"23ca351d-a67d-46aa-a762-290eb742d2f1"}],"layoutGrid":[10,10]},"modified":1654299875432,"location":"mine","persisted":1654299878751},"9666e7b4-be0c-47a5-94b8-99accad7155e":{"identifier":{"key":"9666e7b4-be0c-47a5-94b8-99accad7155e","namespace":""},"name":"Performance Example Imagery","type":"example.imagery","configuration":{"imageLocation":"","imageLoadDelayInMilliSeconds":20000,"imageSamples":[],"layers":[{"source":"dist/imagery/example-imagery-layer-16x9.png","name":"16:9","visible":false},{"source":"dist/imagery/example-imagery-layer-safe.png","name":"Safe","visible":false},{"source":"dist/imagery/example-imagery-layer-scale.png","name":"Scale","visible":false}]},"telemetry":{"values":[{"name":"Name","key":"name"},{"name":"Time","key":"utc","format":"utc","hints":{"domain":2}},{"name":"Local Time","key":"local","format":"local-format","hints":{"domain":1}},{"name":"Image","key":"url","format":"image","hints":{"image":1},"layers":[{"source":"dist/imagery/example-imagery-layer-16x9.png","name":"16:9"},{"source":"dist/imagery/example-imagery-layer-safe.png","name":"Safe"},{"source":"dist/imagery/example-imagery-layer-scale.png","name":"Scale"}]},{"name":"Image Download Name","key":"imageDownloadName","format":"imageDownloadName","hints":{"imageDownloadName":1}}]},"modified":1654299840077,"location":"b3cee102-86dd-4c0a-8eec-4d5d276f8691","persisted":1654299840078}},"rootId":"b3cee102-86dd-4c0a-8eec-4d5d276f8691"}

View File

@ -0,0 +1 @@
{"openmct":{"6d2fa9fd-f2aa-461a-a1e1-164ac44bec9d":{"identifier":{"key":"6d2fa9fd-f2aa-461a-a1e1-164ac44bec9d","namespace":""},"name":"Performance Notebook","type":"notebook","configuration":{"defaultSort":"oldest","entries":{"3e31c412-33ba-4757-8ade-e9821f6ba321":{"8c8f6035-631c-45af-8c24-786c60295335":[{"id":"entry-1652815305457","createdOn":1652815305457,"createdBy":"","text":"Existing Entry 1","embeds":[]},{"id":"entry-1652815313465","createdOn":1652815313465,"createdBy":"","text":"Existing Entry 2","embeds":[]},{"id":"entry-1652815399955","createdOn":1652815399955,"createdBy":"","text":"Existing Entry 3","embeds":[]}]}},"imageMigrationVer":"v1","pageTitle":"Page","sections":[{"id":"3e31c412-33ba-4757-8ade-e9821f6ba321","isDefault":false,"isSelected":false,"name":"Section1","pages":[{"id":"8c8f6035-631c-45af-8c24-786c60295335","isDefault":false,"isSelected":false,"name":"Page1","pageTitle":"Page"},{"id":"36555942-c9aa-439c-bbdb-0aaf50db50f5","isDefault":false,"isSelected":false,"name":"Page2","pageTitle":"Page"}],"sectionTitle":"Section"},{"id":"dab0bd1d-2c5a-405c-987f-107123d6189a","isDefault":false,"isSelected":true,"name":"Section2","pages":[{"id":"f625a86a-cb99-4898-8082-80543c8de534","isDefault":false,"isSelected":false,"name":"Page1","pageTitle":"Page"},{"id":"e77ef810-f785-42a7-942e-07e999b79c59","isDefault":false,"isSelected":true,"name":"Page2","pageTitle":"Page"}],"sectionTitle":"Section"}],"sectionTitle":"Section","type":"General","showTime":"0"},"modified":1652815915219,"location":"mine","persisted":1652815915222}},"rootId":"6d2fa9fd-f2aa-461a-a1e1-164ac44bec9d"}

View File

@ -0,0 +1,77 @@
/*****************************************************************************
* 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 is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*
This test suite is dedicated to tests which verify form functionality.
*/
const { test, expect } = require('@playwright/test');
const TEST_FOLDER = 'test folder';
test.describe('forms set', () => {
test('New folder form has title as required field', async ({ page }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
// Click button:has-text("Create")
await page.click('button:has-text("Create")');
// Click :nth-match(:text("Folder"), 2)
await page.click(':nth-match(:text("Folder"), 2)');
// Click text=Properties Title Notes >> input[type="text"]
await page.click('text=Properties Title Notes >> input[type="text"]');
// Fill text=Properties Title Notes >> input[type="text"]
await page.fill('text=Properties Title Notes >> input[type="text"]', '');
// Press Tab
await page.press('text=Properties Title Notes >> input[type="text"]', 'Tab');
const okButton = page.locator('text=OK');
await expect(okButton).toBeDisabled();
await expect(page.locator('.c-form-row__state-indicator').first()).toHaveClass(/invalid/);
// Click text=Properties Title Notes >> input[type="text"]
await page.click('text=Properties Title Notes >> input[type="text"]');
// Fill text=Properties Title Notes >> input[type="text"]
await page.fill('text=Properties Title Notes >> input[type="text"]', TEST_FOLDER);
// Press Tab
await page.press('text=Properties Title Notes >> input[type="text"]', 'Tab');
await expect(page.locator('.c-form-row__state-indicator').first()).not.toHaveClass(/invalid/);
// Click text=OK
await Promise.all([
page.waitForNavigation(),
page.click('text=OK')
]);
await expect(page.locator('.l-browse-bar__object-name')).toContainText(TEST_FOLDER);
});
test.fixme('Create all object types and verify correctness', async ({ page }) => {
//Create the following Domain Objects with their unique Object Types
// Sine Wave Generator (number object)
// Timer Object
// Plan View Object
// Clock Object
// Hyperlink
});
});

View File

@ -0,0 +1,63 @@
/*****************************************************************************
* 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 is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*
This test suite is dedicated to tests which verify branding related components.
*/
const { test } = require('../fixtures.js');
const { expect } = require('@playwright/test');
test.describe('Branding tests', () => {
test('About Modal launches with basic branding properties', async ({ page }) => {
// Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
// Click About button
await page.click('.l-shell__app-logo');
// Verify that the NASA Logo Appears
await expect(await page.locator('.c-about__image')).toBeVisible();
// Modify the Build information in 'about' Modal
const versionInformationLocator = page.locator('ul.t-info.l-info.s-info');
await expect(versionInformationLocator).toBeEnabled();
await expect.soft(versionInformationLocator).toContainText(/Version: \d/);
await expect.soft(versionInformationLocator).toContainText(/Build Date: ((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun))/);
await expect.soft(versionInformationLocator).toContainText(/Revision: \b[0-9a-f]{5,40}\b/);
await expect.soft(versionInformationLocator).toContainText(/Branch: ./);
});
test('Verify Links in About Modal', async ({ page }) => {
// Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
// Click About button
await page.click('.l-shell__app-logo');
// Verify that clicking on the third party licenses information opens up another tab on licenses url
const [page2] = await Promise.all([
page.waitForEvent('popup'),
page.locator('text=click here for third party licensing information').click()
]);
expect(page2.waitForURL('**/licenses**')).toBeTruthy();
});
});

View File

@ -0,0 +1,63 @@
/*****************************************************************************
* 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 is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*
This test suite is dedicated to tests which verify the basic operations surrounding the example event generator.
*/
const { test } = require('../../fixtures.js');
const { expect } = require('@playwright/test');
test.describe('Example Event Generator Operations', () => {
test('Can create example event generator with a name', async ({ page }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
// let's make an event generator
await page.locator('button:has-text("Create")').click();
// Click li:has-text("Event Message Generator")
await page.locator('li:has-text("Event Message Generator")').click();
// Click text=Properties Title Notes >> input[type="text"]
await page.locator('text=Properties Title Notes >> input[type="text"]').click();
// Fill text=Properties Title Notes >> input[type="text"]
await page.locator('text=Properties Title Notes >> input[type="text"]').fill('Test Event Generator');
// Press Enter
await page.locator('text=Properties Title Notes >> input[type="text"]').press('Enter');
// Click text=OK
await Promise.all([
page.waitForNavigation({ url: /.*&view=table/ }),
page.locator('text=OK').click()
]);
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Test Event Generator');
// Click button:has-text("Fixed Timespan")
await page.locator('button:has-text("Fixed Timespan")').click();
});
test.fixme('telemetry is coming in for test event', async ({ page }) => {
// Go to object created in step one
// Verify the telemetry table is filled with > 1 row
});
test.fixme('telemetry is sorted by time ascending', async ({ page }) => {
// Go to object created in step one
// Verify the telemetry table has a class with "is-sorting asc"
});
});

View File

@ -0,0 +1,167 @@
/*****************************************************************************
* 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 is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*
This test suite is dedicated to tests which verify the basic operations surrounding conditionSets.
*/
const { test } = require('../../../fixtures.js');
const { expect } = require('@playwright/test');
test.describe('Sine Wave Generator', () => {
test('Create new Sine Wave Generator Object and validate create Form Logic', async ({ page }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
//Click the Create button
await page.click('button:has-text("Create")');
// Click Sine Wave Generator
await page.click('text=Sine Wave Generator');
// Verify that the each required field has required indicator
// Title
await expect(page.locator('.c-form-row__state-indicator').first()).toHaveClass(['c-form-row__state-indicator req']);
// Verify that the Notes row does not have a required indicator
await expect(page.locator('.c-form__section div:nth-child(3) .form-row .c-form-row__state-indicator')).not.toContain('.req');
// Period
await expect(page.locator('.c-form__section div:nth-child(4) .form-row .c-form-row__state-indicator')).toHaveClass(['c-form-row__state-indicator req']);
// Amplitude
await expect(page.locator('.c-form__section div:nth-child(5) .form-row .c-form-row__state-indicator')).toHaveClass(['c-form-row__state-indicator req']);
// Offset
await expect(page.locator('.c-form__section div:nth-child(6) .form-row .c-form-row__state-indicator')).toHaveClass(['c-form-row__state-indicator req']);
// Data Rate
await expect(page.locator('.c-form__section div:nth-child(7) .form-row .c-form-row__state-indicator')).toHaveClass(['c-form-row__state-indicator req']);
// Phase
await expect(page.locator('.c-form__section div:nth-child(8) .form-row .c-form-row__state-indicator')).toHaveClass(['c-form-row__state-indicator req']);
// Randomness
await expect(page.locator('.c-form__section div:nth-child(9) .form-row .c-form-row__state-indicator')).toHaveClass(['c-form-row__state-indicator req']);
// Verify that by removing value from required text field shows invalid indicator
await page.locator('text=Properties Title Notes Period Amplitude Offset Data Rate (hz) Phase (radians) Ra >> input[type="text"]').fill('');
await expect(page.locator('.c-form-row__state-indicator').first()).toHaveClass(['c-form-row__state-indicator req invalid']);
// Verify that by adding value to empty required text field changes invalid to valid indicator
await page.locator('text=Properties Title Notes Period Amplitude Offset Data Rate (hz) Phase (radians) Ra >> input[type="text"]').fill('non empty');
await expect(page.locator('.c-form-row__state-indicator').first()).toHaveClass(['c-form-row__state-indicator req valid']);
// Verify that by removing value from required number field shows invalid indicator
await page.locator('.field.control.l-input-sm input').first().fill('');
await expect(page.locator('.c-form__section div:nth-child(4) .form-row .c-form-row__state-indicator')).toHaveClass(['c-form-row__state-indicator req invalid']);
// Verify that by adding value to empty required number field changes invalid to valid indicator
await page.locator('.field.control.l-input-sm input').first().fill('3');
await expect(page.locator('.c-form__section div:nth-child(4) .form-row .c-form-row__state-indicator')).toHaveClass(['c-form-row__state-indicator req valid']);
// Verify that can change value of number field by up/down arrows keys
// Click .field.control.l-input-sm input >> nth=0
await page.locator('.field.control.l-input-sm input').first().click();
// Press ArrowUp 3 times to change value from 3 to 6
await page.locator('.field.control.l-input-sm input').first().press('ArrowUp');
await page.locator('.field.control.l-input-sm input').first().press('ArrowUp');
await page.locator('.field.control.l-input-sm input').first().press('ArrowUp');
const value = await page.locator('.field.control.l-input-sm input').first().inputValue();
await expect(value).toBe('6');
// Click .c-form-row__state-indicator.grows
await page.locator('.c-form-row__state-indicator.grows').click();
// Click text=Properties Title Notes Period Amplitude Offset Data Rate (hz) Phase (radians) Ra >> input[type="text"]
await page.locator('text=Properties Title Notes Period Amplitude Offset Data Rate (hz) Phase (radians) Ra >> input[type="text"]').click();
// Click .c-form-row__state-indicator >> nth=0
await page.locator('.c-form-row__state-indicator').first().click();
// Fill text=Properties Title Notes Period Amplitude Offset Data Rate (hz) Phase (radians) Ra >> input[type="text"]
await page.locator('text=Properties Title Notes Period Amplitude Offset Data Rate (hz) Phase (radians) Ra >> input[type="text"]').fill('New Sine Wave Generator');
// Double click div:nth-child(4) .form-row .c-form-row__controls
await page.locator('div:nth-child(4) .form-row .c-form-row__controls').dblclick();
// Click .field.control.l-input-sm input >> nth=0
await page.locator('.field.control.l-input-sm input').first().click();
// Click div:nth-child(4) .form-row .c-form-row__state-indicator
await page.locator('div:nth-child(4) .form-row .c-form-row__state-indicator').click();
// Click .field.control.l-input-sm input >> nth=0
await page.locator('.field.control.l-input-sm input').first().click();
// Click .field.control.l-input-sm input >> nth=0
await page.locator('.field.control.l-input-sm input').first().click();
// Click div:nth-child(5) .form-row .c-form-row__controls .form-control .field input
await page.locator('div:nth-child(5) .form-row .c-form-row__controls .form-control .field input').click();
// Click div:nth-child(5) .form-row .c-form-row__controls .form-control .field input
await page.locator('div:nth-child(5) .form-row .c-form-row__controls .form-control .field input').click();
// Click div:nth-child(5) .form-row .c-form-row__controls .form-control .field input
await page.locator('div:nth-child(5) .form-row .c-form-row__controls .form-control .field input').click();
// Click div:nth-child(6) .form-row .c-form-row__controls .form-control .field input
await page.locator('div:nth-child(6) .form-row .c-form-row__controls .form-control .field input').click();
// Double click div:nth-child(7) .form-row .c-form-row__controls .form-control .field input
await page.locator('div:nth-child(7) .form-row .c-form-row__controls .form-control .field input').dblclick();
// Click div:nth-child(7) .form-row .c-form-row__state-indicator
await page.locator('div:nth-child(7) .form-row .c-form-row__state-indicator').click();
// Click div:nth-child(7) .form-row .c-form-row__controls .form-control .field input
await page.locator('div:nth-child(7) .form-row .c-form-row__controls .form-control .field input').click();
// Fill div:nth-child(7) .form-row .c-form-row__controls .form-control .field input
await page.locator('div:nth-child(7) .form-row .c-form-row__controls .form-control .field input').fill('3');
//Click text=OK
await Promise.all([
page.waitForNavigation(),
page.click('text=OK')
]);
// Verify that the Sine Wave Generator is displayed and correct
// Verify object properties
await expect(page.locator('.l-browse-bar__object-name')).toContainText('New Sine Wave Generator');
// Verify canvas rendered
await page.locator('canvas').nth(1).click({
position: {
x: 341,
y: 28
}
});
// Verify that where we click on canvas shows the number we clicked on
// Note that any number will do, we just care that a number exists
await expect(page.locator('.value-to-display-nearestValue')).toContainText(/[+-]?([0-9]*[.])?[0-9]+/);
});
});

View File

@ -0,0 +1,146 @@
/*****************************************************************************
* 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 is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*
This test suite is dedicated to tests which verify the basic operations surrounding moving objects.
*/
const { test } = require('../fixtures.js');
const { expect } = require('@playwright/test');
test.describe('Move item tests', () => {
test('Create a basic object and verify that it can be moved to another folder', async ({ page }) => {
// Go to Open MCT
await page.goto('/');
// Create a new folder in the root my items folder
let folder1 = "Folder1";
await page.locator('button:has-text("Create")').click();
await page.locator('li.icon-folder').click();
await page.locator('text=Properties Title Notes >> input[type="text"]').click();
await page.locator('text=Properties Title Notes >> input[type="text"]').fill(folder1);
// Click on My Items in Tree. Workaround for https://github.com/nasa/openmct/issues/5184
await page.click('form[name="mctForm"] a:has-text("My Items")');
await Promise.all([
page.waitForNavigation(),
page.locator('text=OK').click(),
page.waitForSelector('.c-message-banner__message')
]);
//Wait until Save Banner is gone
await page.locator('.c-message-banner__close-button').click();
await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
// Create another folder with a new name at default location, which is currently inside Folder 1
let folder2 = "Folder2";
await page.locator('button:has-text("Create")').click();
await page.locator('li.icon-folder').click();
await page.locator('text=Properties Title Notes >> input[type="text"]').click();
await page.locator('text=Properties Title Notes >> input[type="text"]').fill(folder2);
// Click on My Items in Tree. Workaround for https://github.com/nasa/openmct/issues/5184
await page.click('form[name="mctForm"] a:has-text("My Items")');
await Promise.all([
page.waitForNavigation(),
page.locator('text=OK').click(),
page.waitForSelector('.c-message-banner__message')
]);
//Wait until Save Banner is gone
await page.locator('.c-message-banner__close-button').click();
await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
// Move Folder 2 from Folder 1 to My Items
await page.locator('text=Open MCT My Items >> span').nth(3).click();
await page.locator('.c-tree__scrollable div div:nth-child(2) .c-tree__item .c-tree__item__view-control').click();
await page.locator(`a:has-text("${folder2}")`).click({
button: 'right'
});
await page.locator('li.icon-move').click();
await page.locator('form[name="mctForm"] >> text=My Items').click();
await page.locator('text=OK').click();
// Expect that Folder 2 is in My Items, the root folder
expect(page.locator(`text=My Items >> nth=0:has(text=${folder2})`)).toBeTruthy();
});
test('Create a basic object and verify that it cannot be moved to telemetry object without Composition Provider', async ({ page }) => {
// Go to Open MCT
await page.goto('/');
// Create Telemetry Table
let telemetryTable = 'Test Telemetry Table';
await page.locator('button:has-text("Create")').click();
await page.locator('li:has-text("Telemetry Table")').click();
await page.locator('text=Properties Title Notes >> input[type="text"]').click();
await page.locator('text=Properties Title Notes >> input[type="text"]').fill(telemetryTable);
// Click on My Items in Tree. Workaround for https://github.com/nasa/openmct/issues/5184
await page.click('form[name="mctForm"] a:has-text("My Items")');
await page.locator('text=OK').click();
// Finish editing and save Telemetry Table
await page.locator('.c-button--menu.c-button--major.icon-save').click();
await page.locator('text=Save and Finish Editing').click();
// Create New Folder Basic Domain Object
let folder = 'Test Folder';
await page.locator('button:has-text("Create")').click();
await page.locator('li:has-text("Folder")').click();
await page.locator('text=Properties Title Notes >> input[type="text"]').click();
await page.locator('text=Properties Title Notes >> input[type="text"]').fill(folder);
// See if it's possible to put the folder in the Telemetry object during creation (Soft Assert)
await page.locator(`form[name="mctForm"] >> text=${telemetryTable}`).click();
let okButton = await page.locator('button.c-button.c-button--major:has-text("OK")');
let okButtonStateDisabled = await okButton.isDisabled();
expect.soft(okButtonStateDisabled).toBeTruthy();
// Continue test regardless of assertion and create it in My Items
await page.locator('form[name="mctForm"] >> text=My Items').click();
await page.locator('text=OK').click();
// Open My Items
await page.locator('text=Open MCT My Items >> span').nth(3).click();
// Select Folder Object and select Move from context menu
await Promise.all([
page.waitForNavigation(),
page.locator(`a:has-text("${folder}")`).click()
]);
await page.locator('.c-tree__item.is-navigated-object .c-tree__item__label .c-tree__item__type-icon').click({
button: 'right'
});
await page.locator('li.icon-move').click();
// See if it's possible to put the folder in the Telemetry object after creation
await page.locator('text=Location Open MCT My Items >> span').nth(3).click();
await page.locator(`form[name="mctForm"] >> text=${telemetryTable}`).click();
let okButton2 = await page.locator('button.c-button.c-button--major:has-text("OK")');
let okButtonStateDisabled2 = await okButton2.isDisabled();
expect(okButtonStateDisabled2).toBeTruthy();
});
});

View File

@ -0,0 +1,177 @@
/*****************************************************************************
* 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 is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*
This test suite is dedicated to performance tests to ensure that testability of performance
is not broken upstream on Open MCT. Any assumptions made downstream will be tested here
TODO:
- Update resolution of performance config
- Add Performance Observer on init to push all performance marks
- Move client CDP connection to before or to a fixture
-
*/
const { test, expect } = require('@playwright/test');
const filePath = 'e2e/test-data/PerformanceDisplayLayout.json';
test.describe('Performance tests', () => {
test.beforeEach(async ({ page, browser }, testInfo) => {
// Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
// Click a:has-text("My Items")
await page.locator('a:has-text("My Items")').click({
button: 'right'
});
// Click text=Import from JSON
await page.locator('text=Import from JSON').click();
// Upload Performance Display Layout.json
await page.setInputFiles('#fileElem', filePath);
// Click text=OK
await page.locator('text=OK').click();
await expect(page.locator('a:has-text("Performance Display Layout Display Layout")')).toBeVisible();
//Create a Chrome Performance Timeline trace to store as a test artifact
console.log("\n==== Devtools: startTracing ====\n");
await browser.startTracing(page, {
path: `${testInfo.outputPath()}-trace.json`,
screenshots: true
});
});
test.afterEach(async ({ page, browser}) => {
console.log("\n==== Devtools: stopTracing ====\n");
await browser.stopTracing();
/* Measurement Section
/ The following section includes a block of performance measurements.
*/
//Get time difference between viewlarge actionability and evaluate time
await page.evaluate(() => (window.performance.measure("machine-time-difference", "viewlarge.start", "viewLarge.start.test")));
//Get StartTime
const startTime = await page.evaluate(() => window.performance.timing.navigationStart);
console.log('window.performance.timing.navigationStart', startTime);
//Get All Performance Marks
const getAllMarksJson = await page.evaluate(() => JSON.stringify(window.performance.getEntriesByType("mark")));
const getAllMarks = JSON.parse(getAllMarksJson);
console.log('window.performance.getEntriesByType("mark")', getAllMarks);
//Get All Performance Measures
const getAllMeasuresJson = await page.evaluate(() => JSON.stringify(window.performance.getEntriesByType("measure")));
const getAllMeasures = JSON.parse(getAllMeasuresJson);
console.log('window.performance.getEntriesByType("measure")', getAllMeasures);
});
/* The following test will navigate to a previously created Performance Display Layout and measure the
/ following metrics:
/ - ElementResourceTiming
/ - Interaction Timing
*/
test('Embedded View Large for Imagery is performant in Fixed Time', async ({ page, browser }) => {
const client = await page.context().newCDPSession(page);
// Tell the DevTools session to record performance metrics
// https://chromedevtools.github.io/devtools-protocol/tot/Performance/#method-getMetrics
await client.send('Performance.enable');
// Go to baseURL
await page.goto('/');
// Search Available after Launch
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
await page.evaluate(() => window.performance.mark("search-available"));
// Fill Search input
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Performance Display Layout');
await page.evaluate(() => window.performance.mark("search-entered"));
//Search Result Appears and is clicked
await Promise.all([
page.waitForNavigation(),
page.locator('a:has-text("Performance Display Layout")').first().click(),
page.evaluate(() => window.performance.mark("click-search-result"))
]);
//Time to Example Imagery Frame loads within Display Layout
await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible'});
//Time to Example Imagery object loads
await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible'});
//Get background-image url from background-image css prop
const backgroundImage = await page.locator('.c-imagery__main-image__background-image');
let backgroundImageUrl = await backgroundImage.evaluate((el) => {
return window.getComputedStyle(el).getPropertyValue('background-image').match(/url\(([^)]+)\)/)[1];
});
backgroundImageUrl = backgroundImageUrl.slice(1, -1); //forgive me, padre
console.log('backgroundImageurl ' + backgroundImageUrl);
//Get ResourceTiming of background-image jpg
const resourceTimingJson = await page.evaluate((bgImageUrl) =>
JSON.stringify(window.performance.getEntriesByName(bgImageUrl).pop()),
backgroundImageUrl
);
console.log('resourceTimingJson ' + resourceTimingJson);
//Open Large view
await page.locator('button:has-text("Large View")').click(); //This action includes the performance.mark named 'viewLarge.start'
await page.evaluate(() => window.performance.mark("viewLarge.start.test")); //This is a mark only to compare evaluate timing
//Time to Imagery Rendered in Large Frame
await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible'});
await page.evaluate(() => window.performance.mark("background-image-frame"));
//Time to Example Imagery object loads
await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible'});
await page.evaluate(() => window.performance.mark("background-image-visible"));
// Get Current number of images in thumbstrip
await page.waitForSelector('.c-imagery__thumb');
const thumbCount = await page.locator('.c-imagery__thumb').count();
console.log('number of thumbs rendered ' + thumbCount);
await page.locator('.c-imagery__thumb').last().click();
//Get ResourceTiming of all jpg resources
const resourceTimingJson2 = await page.evaluate(() =>
JSON.stringify(window.performance.getEntriesByType('resource'))
);
const resourceTiming = JSON.parse(resourceTimingJson2);
const jpgResourceTiming = resourceTiming.find((element) =>
element.name.includes('.jpg')
);
console.log('jpgResourceTiming ' + JSON.stringify(jpgResourceTiming));
// Click Close Icon
await page.locator('[aria-label="Close"]').click();
await page.evaluate(() => window.performance.mark("view-large-close-button"));
//await client.send('HeapProfiler.enable');
await client.send('HeapProfiler.collectGarbage');
let performanceMetrics = await client.send('Performance.getMetrics');
console.log(performanceMetrics.metrics);
});
});

View File

@ -0,0 +1,119 @@
/*****************************************************************************
* 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 is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*
This test suite is an initial example for memory leak testing using performance. This configuration and execution must
be kept separate from the traditional performance measurements to avoid any "observer" effects associated with tracing
or profiling playwright and/or the browser.
Based on a pattern identified in https://github.com/trentmwillis/devtools-protocol-demos/blob/master/testing-demos/memory-leak-by-heap.js
and https://github.com/paulirish/automated-chrome-profiling/issues/3
Best path forward: https://github.com/cowchimp/headless-devtools/blob/master/src/Memory/example.js
*/
const { test, expect } = require('@playwright/test');
const filePath = 'e2e/test-data/PerformanceDisplayLayout.json';
// eslint-disable-next-line playwright/no-skipped-test
test.describe.skip('Memory Performance tests', () => {
test.beforeEach(async ({ page, browser }, testInfo) => {
// Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
// Click a:has-text("My Items")
await page.locator('a:has-text("My Items")').click({
button: 'right'
});
// Click text=Import from JSON
await page.locator('text=Import from JSON').click();
// Upload Performance Display Layout.json
await page.setInputFiles('#fileElem', filePath);
// Click text=OK
await page.locator('text=OK').click();
await expect(page.locator('a:has-text("Performance Display Layout Display Layout")')).toBeVisible();
});
test('Embedded View Large for Imagery is performant in Fixed Time', async ({ page, browser }) => {
await page.goto('/', {waitUntil: 'networkidle'});
// To to Search Available after Launch
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
// Fill Search input
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Performance Display Layout');
//Search Result Appears and is clicked
await Promise.all([
page.waitForNavigation(),
page.locator('a:has-text("Performance Display Layout")').first().click()
]);
//Time to Example Imagery Frame loads within Display Layout
await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible'});
//Time to Example Imagery object loads
await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible'});
const client = await page.context().newCDPSession(page);
await client.send('HeapProfiler.enable');
await client.send('HeapProfiler.startSampling');
// await client.send('HeapProfiler.collectGarbage');
await client.send('Performance.enable');
let performanceMetricsBefore = await client.send('Performance.getMetrics');
console.log(performanceMetricsBefore.metrics);
//await client.send('Performance.disable');
//Open Large view
await page.locator('button:has-text("Large View")').click();
await client.send('HeapProfiler.takeHeapSnapshot');
//Time to Imagery Rendered in Large Frame
await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible'});
//Time to Example Imagery object loads
await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible'});
// Click Close Icon
await page.locator('.c-click-icon').click();
//Time to Example Imagery Frame loads within Display Layout
await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible'});
//Time to Example Imagery object loads
await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible'});
await client.send('HeapProfiler.collectGarbage');
//await client.send('Performance.enable');
let performanceMetricsAfter = await client.send('Performance.getMetrics');
console.log(performanceMetricsAfter.metrics);
//await client.send('Performance.disable');
});
});

View File

@ -0,0 +1,158 @@
/*****************************************************************************
* 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 is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*
This test suite is dedicated to performance tests to ensure that testability of performance
is not broken upstream on Open MCT. Any assumptions made downstream will be tested here.
TODO:
- Update resolution of performance config
- Add Performance Observer on init to push all performance marks
- Move client CDP connection to before or to a fixture
*/
const { test, expect } = require('@playwright/test');
const notebookFilePath = 'e2e/test-data/PerformanceNotebook.json';
test.describe('Performance tests', () => {
test.beforeEach(async ({ page, browser }, testInfo) => {
// Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
// Click a:has-text("My Items")
await page.locator('a:has-text("My Items")').click({
button: 'right'
});
// Click text=Import from JSON
await page.locator('text=Import from JSON').click();
// Upload Performance Display Layout.json
await page.setInputFiles('#fileElem', notebookFilePath);
// TODO Fix this
await page.locator('text=OK >> nth=1').click();
await expect(page.locator('a:has-text("Performance Notebook")')).toBeVisible();
//Create a Chrome Performance Timeline trace to store as a test artifact
console.log("\n==== Devtools: startTracing ====\n");
await browser.startTracing(page, {
path: `${testInfo.outputPath()}-trace.json`,
screenshots: true
});
});
test.afterEach(async ({ page, browser}) => {
console.log("\n==== Devtools: stopTracing ====\n");
await browser.stopTracing();
/* Measurement Section
/ The following section includes a block of performance measurements.
*/
const startTime = await page.evaluate(() => window.performance.timing.navigationStart);
console.log('window.performance.timing.navigationStart', startTime);
//Get All Performance Marks
const getAllMarksJson = await page.evaluate(() => JSON.stringify(window.performance.getEntriesByType("mark")));
const getAllMarks = JSON.parse(getAllMarksJson);
console.log('window.performance.getEntriesByType("mark")', getAllMarks);
//Get All Performance Measures
const getAllMeasuresJson = await page.evaluate(() => JSON.stringify(window.performance.getEntriesByType("measure")));
const getAllMeasures = JSON.parse(getAllMeasuresJson);
console.log('window.performance.getEntriesByType("measure")', getAllMeasures);
});
/* The following test will navigate to a previously created Performance Display Layout and measure the
/ following metrics:
/ - ElementResourceTiming
/ - Interaction Timing
*/
test('Notebook Search, Add Entry, Update Entry are performant', async ({ page, browser }) => {
const client = await page.context().newCDPSession(page);
// Tell the DevTools session to record performance metrics
// https://chromedevtools.github.io/devtools-protocol/tot/Performance/#method-getMetrics
await client.send('Performance.enable');
// Go to baseURL
await page.goto('/');
// To to Search Available after Launch
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
await page.evaluate(() => window.performance.mark("search-available"));
// Fill Search input
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Performance Notebook');
await page.evaluate(() => window.performance.mark("search-entered"));
//Search Result Appears and is clicked
await Promise.all([
page.waitForNavigation(),
page.locator('a:has-text("Performance Notebook")').first().click(),
page.evaluate(() => window.performance.mark("click-search-result"))
]);
await page.waitForSelector('.c-tree__item c-tree-and-search__loading loading', {state: 'hidden'});
await page.evaluate(() => window.performance.mark("search-spinner-gone"));
await page.waitForSelector('.l-browse-bar__object-name', { state: 'visible'});
await page.evaluate(() => window.performance.mark("object-title-appears"));
await page.waitForSelector('.c-notebook__entry >> nth=0', { state: 'visible'});
await page.evaluate(() => window.performance.mark("notebook-entry-appears"));
// Click Add new Notebook Entry
await page.locator('.c-notebook__drag-area').click();
await page.evaluate(() => window.performance.mark("new-notebook-entry-created"));
// Enter Notebook Entry text
await page.locator('div.c-ne__text').last().fill('New Entry');
await page.keyboard.press('Enter');
await page.evaluate(() => window.performance.mark("new-notebook-entry-filled"));
//Individual Notebook Entry Search
await page.evaluate(() => window.performance.mark("notebook-search-start"));
await page.locator('.c-notebook__search >> input').fill('Existing Entry');
await page.evaluate(() => window.performance.mark("notebook-search-filled"));
await page.waitForSelector('text=Search Results (3)', { state: 'visible'});
await page.evaluate(() => window.performance.mark("notebook-search-processed"));
await page.waitForSelector('.c-notebook__entry >> nth=2', { state: 'visible'});
await page.evaluate(() => window.performance.mark("notebook-search-processed"));
//Clear Search
await page.locator('.c-search.c-notebook__search .c-search__clear-input').click();
await page.evaluate(() => window.performance.mark("notebook-search-processed"));
// Hover on Last
await page.evaluate(() => window.performance.mark("new-notebook-entry-delete"));
await page.locator('div.c-ne__time-and-content').last().hover();
await page.locator('button[title="Delete this entry"]').last().click();
await page.locator('button:has-text("Ok")').click();
await page.waitForSelector('.c-notebook__entry >> nth=3', { state: 'detached'});
await page.evaluate(() => window.performance.mark("new-notebook-entry-deleted"));
//await client.send('HeapProfiler.enable');
await client.send('HeapProfiler.collectGarbage');
let performanceMetrics = await client.send('Performance.getMetrics');
console.log(performanceMetrics.metrics);
});
});

View File

@ -0,0 +1,27 @@
(function () {
document.addEventListener('DOMContentLoaded', () => {
const PERSISTENCE_KEY = 'persistence-tests';
const openmct = window.openmct;
openmct.objects.addRoot({
namespace: PERSISTENCE_KEY,
key: PERSISTENCE_KEY
});
openmct.objects.addProvider(PERSISTENCE_KEY, {
get(identifier) {
if (identifier.key !== PERSISTENCE_KEY) {
return undefined;
} else {
return Promise.resolve({
identifier,
type: 'folder',
name: 'Persistence Testing',
location: 'ROOT',
composition: []
});
}
}
});
});
}());

View File

@ -0,0 +1,78 @@
/*****************************************************************************
* 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 is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*
This test suite is dedicated to tests which verify the basic operations surrounding conditionSets.
*/
const { test } = require('../../fixtures.js');
const { expect } = require('@playwright/test');
const path = require('path');
// https://github.com/nasa/openmct/issues/4323#issuecomment-1067282651
test.describe('Persistence operations', () => {
// add non persistable root item
test.beforeEach(async ({ page }) => {
// eslint-disable-next-line no-undef
await page.addInitScript({ path: path.join(__dirname, 'addNoneditableObject.js') });
});
test('Persistability should be respected in the create form location field', async ({ page }) => {
// Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
// Click the Create button
await page.click('button:has-text("Create")');
// Click text=Condition Set
await page.click('text=Condition Set');
// Click form[name="mctForm"] >> text=Persistence Testing
await page.locator('form[name="mctForm"] >> text=Persistence Testing').click();
// Check that "OK" button is disabled
const okButton = page.locator('button:has-text("OK")');
await expect(okButton).toBeDisabled();
});
test('Non-persistable objects should not show persistence related actions', async ({ page }) => {
// Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
// Click text=Persistence Testing >> nth=0
await page.locator('text=Persistence Testing').first().click({
button: 'right'
});
const menuOptions = page.locator('.c-menu ul');
await expect.soft(menuOptions).toContainText(['Open In New Tab', 'View', 'Create Link']);
await expect(menuOptions).not.toContainText(['Move', 'Duplicate', 'Remove', 'Add New Folder', 'Edit Properties...', 'Export as JSON', 'Import from JSON']);
});
test.fixme('Cannot move a previously created domain object to non-peristable boject in Move Modal', async ({ page }) => {
//Create a domain object
//Save Domain object
//Move Object and verify that cannot select non-persistable object
//Move Object to My Items
//Verify successful move
});
});

View File

@ -0,0 +1,51 @@
/*****************************************************************************
* 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 is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*
This test suite is dedicated to tests which verify the basic operations surrounding exportAsJSON.
*/
const { test } = require('../../../fixtures.js');
// FIXME: Remove this eslint exception once tests are implemented
// eslint-disable-next-line no-unused-vars
const { expect } = require('@playwright/test');
test.describe('ExportAsJSON', () => {
test.fixme('Create a basic object and verify that it can be exported as JSON from Tree', async ({ page }) => {
//Create domain object
//Save Domain Object
//Verify that the newly created domain object can be exported as JSON from the Tree
});
test.fixme('Create a basic object and verify that it can be exported as JSON from 3 dot menu', async ({ page }) => {
//Create domain object
//Save Domain Object
//Verify that the newly created domain object can be exported as JSON from the 3 dot menu
});
test.fixme('Verify that a nested Object can be exported as JSON', async ({ page }) => {
// Create 2 objects with hierarchy
// Export as JSON
// Verify Hiearchy
});
test.fixme('Verify that the ExportAsJSON dropdown does not appear for the item X', async ({ page }) => {
// Other than non-persistible objects
});
});

View File

@ -0,0 +1,49 @@
/*****************************************************************************
* 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 is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*
This test suite is dedicated to tests which verify the basic operations surrounding importAsJSON.
*/
const { test } = require('../../../fixtures.js');
// FIXME: Remove this eslint exception once tests are implemented
// eslint-disable-next-line no-unused-vars
const { expect } = require('@playwright/test');
test.describe('ExportAsJSON', () => {
test.fixme('Verify that domain object can be importAsJSON from Tree', async ({ page }) => {
//Verify that an testdata JSON file can be imported from Tree
//Verify correctness of imported domain object
});
test.fixme('Verify that domain object can be importAsJSON from 3 dot menu on folder', async ({ page }) => {
//Verify that an testdata JSON file can be imported from 3 dot menu on folder domain object
//Verify correctness of imported domain object
});
test.fixme('Verify that a nested Objects can be importAsJSON', async ({ page }) => {
// Testdata with hierarchy
// ImportAsJSON on Tree
// Verify Hierarchy
});
test.fixme('Verify that the ImportAsJSON dropdown does not appear for the item X', async ({ page }) => {
// Other than non-persistible objects
});
});

View File

@ -0,0 +1,67 @@
/*****************************************************************************
* 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 is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*
This test suite is dedicated to tests which verify the basic operations surrounding Clock.
*/
const { test } = require('../../../fixtures.js');
const { expect } = require('@playwright/test');
test.describe('Clock Generator', () => {
test('Timezone dropdown will collapse when clicked outside or on dropdown icon again', async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/4878'
});
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
//Click the Create button
await page.click('button:has-text("Create")');
// Click Clock
await page.click('text=Clock');
// Click .icon-arrow-down
await page.locator('.icon-arrow-down').click();
//verify if the autocomplete dropdown is visible
await expect(page.locator(".c-input--autocomplete__options")).toBeVisible();
// Click .icon-arrow-down
await page.locator('.icon-arrow-down').click();
// Verify clicking on the autocomplete arrow collapses the dropdown
await expect(page.locator(".c-input--autocomplete__options")).not.toBeVisible();
// Click timezone input to open dropdown
await page.locator('.c-input--autocomplete__input').click();
//verify if the autocomplete dropdown is visible
await expect(page.locator(".c-input--autocomplete__options")).toBeVisible();
// Verify clicking outside the autocomplete dropdown collapses it
await page.locator('text=Timezone').click();
// Verify clicking on the autocomplete arrow collapses the dropdown
await expect(page.locator(".c-input--autocomplete__options")).not.toBeVisible();
});
});

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, United States Government
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@ -21,13 +21,21 @@
*****************************************************************************/
/*
This test suite is dedicated to tests which verify the basic operations surrounding conditionSets.
This test suite is dedicated to tests which verify the basic operations surrounding conditionSets. Note: this
suite is sharing state between tests which is considered an anti-pattern. Implimenting in this way to
demonstrate some playwright for test developers. This pattern should not be re-used in other CRUD suites.
*/
const { test, expect } = require('@playwright/test');
const { test } = require('../../../fixtures.js');
const { expect } = require('@playwright/test');
test.describe('condition set', () => {
test('create new button `condition set` creates new condition object', async ({ page }) => {
let conditionSetUrl;
let getConditionSetIdentifierFromUrl;
test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
test.beforeAll(async ({ browser }) => {
const context = await browser.newContext();
const page = await context.newPage();
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
@ -35,14 +43,139 @@ test.describe('condition set', () => {
await page.click('button:has-text("Create")');
// Click text=Condition Set
await page.click('text=Condition Set');
await page.locator('li:has-text("Condition Set")').click();
// Click text=OK
await Promise.all([
page.waitForNavigation(/*{ url: 'http://localhost:8080/#/browse/mine/dab945d4-5a84-480e-8180-222b4aa730fa?tc.mode=fixed&tc.startBound=1639696164435&tc.endBound=1639697964435&tc.timeSystem=utc&view=conditionSet.view' }*/),
page.waitForNavigation(),
page.click('text=OK')
]);
//Save localStorage for future test execution
await context.storageState({ path: './e2e/tests/recycled_storage.json' });
//Set object identifier from url
conditionSetUrl = await page.url();
console.log('conditionSetUrl ' + conditionSetUrl);
getConditionSetIdentifierFromUrl = await conditionSetUrl.split('/').pop().split('?')[0];
console.debug('getConditionSetIdentifierFromUrl ' + getConditionSetIdentifierFromUrl);
});
test.afterAll(async ({ browser }) => {
await browser.close();
});
//Load localStorage for subsequent tests
test.use({ storageState: './e2e/tests/recycled_storage.json' });
//Begin suite of tests again localStorage
test('Condition set object properties persist in main view and inspector', async ({ page }) => {
//Navigate to baseURL with injected localStorage
await page.goto(conditionSetUrl, { waitUntil: 'networkidle' });
//Assertions on loaded Condition Set in main view
await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set');
//Assertions on loaded Condition Set in Inspector
await expect.soft(page.locator('_vue=item.name=Unnamed Condition Set')).toBeTruthy;
//Reload Page
await Promise.all([
page.reload(),
page.waitForLoadState('networkidle')
]);
//Re-verify after reload
await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set');
//Assertions on loaded Condition Set in Inspector
await expect.soft(page.locator('_vue=item.name=Unnamed Condition Set')).toBeTruthy;
});
test('condition set object can be modified on @localStorage', async ({ page }) => {
await page.goto(conditionSetUrl, { waitUntil: 'networkidle' });
//Assertions on loaded Condition Set in main view
await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set');
//Update the Condition Set properties
// Click Edit Button
await page.locator('text=Conditions View Snapshot >> button').nth(3).click();
//Edit Condition Set Name from main view
await page.locator('text=Unnamed Condition Set').first().fill('Renamed Condition Set');
await page.locator('text=Renamed Condition Set').first().press('Enter');
// Click Save Button
await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
// Click Save and Finish Editing Option
await page.locator('text=Save and Finish Editing').click();
//Verify Main section reflects updated Name Property
await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Renamed Condition Set');
// Verify Inspector properties
// Verify Inspector has updated Name property
await expect.soft(page.locator('text=Renamed Condition Set').nth(1)).toBeTruthy();
// Verify Inspector Details has updated Name property
await expect.soft(page.locator('text=Renamed Condition Set').nth(2)).toBeTruthy();
// Verify Tree reflects updated Name proprety
// Expand Tree
await page.locator('text=Open MCT My Items >> span >> nth=3').click();
// Verify Condition Set Object is renamed in Tree
await expect(page.locator('a:has-text("Renamed Condition Set")')).toBeTruthy();
// Verify Search Tree reflects renamed Name property
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Renamed');
await expect(page.locator('a:has-text("Renamed Condition Set")')).toBeTruthy();
//Reload Page
await Promise.all([
page.reload(),
page.waitForLoadState('networkidle')
]);
//Verify Main section reflects updated Name Property
await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Renamed Condition Set');
// Verify Inspector properties
// Verify Inspector has updated Name property
await expect.soft(page.locator('text=Renamed Condition Set').nth(1)).toBeTruthy();
// Verify Inspector Details has updated Name property
await expect.soft(page.locator('text=Renamed Condition Set').nth(2)).toBeTruthy();
// Verify Tree reflects updated Name proprety
// Expand Tree
await page.locator('text=Open MCT My Items >> span >> nth=3').click();
// Verify Condition Set Object is renamed in Tree
await expect(page.locator('a:has-text("Renamed Condition Set")')).toBeTruthy();
// Verify Search Tree reflects renamed Name property
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Renamed');
await expect(page.locator('a:has-text("Renamed Condition Set")')).toBeTruthy();
});
test('condition set object can be deleted by Search Tree Actions menu on @localStorage', async ({ page }) => {
//Navigate to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
const numberOfConditionSetsToStart = await page.locator('a:has-text("Unnamed Condition Set Condition Set")').count();
//Expect Unnamed Condition Set to be visible in Main View
await expect(page.locator('a:has-text("Unnamed Condition Set Condition Set") >> nth=0')).toBeVisible();
// Search for Unnamed Condition Set
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Unnamed Condition Set');
// Click Search Result
await page.locator('[aria-label="OpenMCT Search"] >> text=Unnamed Condition Set').first().click();
// Click hamburger button
await page.locator('[title="More options"]').click();
// Click text=Remove
await page.locator('text=Remove').click();
await page.locator('text=OK').click();
//Expect Unnamed Condition Set to be removed in Main View
const numberOfConditionSetsAtEnd = await page.locator('a:has-text("Unnamed Condition Set Condition Set")').count();
expect(numberOfConditionSetsAtEnd).toEqual(numberOfConditionSetsToStart - 1);
//Feature?
//Domain Object is still available by direct URL after delete
await page.goto(conditionSetUrl, { waitUntil: 'networkidle' });
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set');
});
});

View File

@ -0,0 +1,474 @@
/*****************************************************************************
* 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 is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*
This test suite is dedicated to tests which verify the basic operations surrounding imagery,
but only assume that example imagery is present.
*/
/* globals process */
const { test } = require('../../../fixtures.js');
const { expect } = require('@playwright/test');
test.describe('Example Imagery', () => {
test.beforeEach(async ({ page }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
//Click the Create button
await page.click('button:has-text("Create")');
// Click text=Example Imagery
await page.click('text=Example Imagery');
// Click on My Items in Tree. Workaround for https://github.com/nasa/openmct/issues/5184
await page.click('form[name="mctForm"] a:has-text("My Items")');
// Click text=OK
await Promise.all([
page.waitForNavigation({waitUntil: 'networkidle'}),
page.click('text=OK'),
//Wait for Save Banner to appear
page.waitForSelector('.c-message-banner__message')
]);
//Wait until Save Banner is gone
await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery');
});
const backgroundImageSelector = '.c-imagery__main-image__background-image';
test('Can use Mouse Wheel to zoom in and out of latest image', async ({ page }) => {
const bgImageLocator = page.locator(backgroundImageSelector);
const deltaYStep = 100; //equivalent to 1x zoom
await bgImageLocator.hover({trial: true});
const originalImageDimensions = await page.locator(backgroundImageSelector).boundingBox();
// zoom in
await bgImageLocator.hover({trial: true});
await page.mouse.wheel(0, deltaYStep * 2);
// wait for zoom animation to finish
await bgImageLocator.hover({trial: true});
const imageMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox();
// zoom out
await bgImageLocator.hover({trial: true});
await page.mouse.wheel(0, -deltaYStep);
// wait for zoom animation to finish
await bgImageLocator.hover({trial: true});
const imageMouseZoomedOut = await page.locator(backgroundImageSelector).boundingBox();
expect(imageMouseZoomedIn.height).toBeGreaterThan(originalImageDimensions.height);
expect(imageMouseZoomedIn.width).toBeGreaterThan(originalImageDimensions.width);
expect(imageMouseZoomedOut.height).toBeLessThan(imageMouseZoomedIn.height);
expect(imageMouseZoomedOut.width).toBeLessThan(imageMouseZoomedIn.width);
});
test('Can use alt+drag to move around image once zoomed in', async ({ page }) => {
const deltaYStep = 100; //equivalent to 1x zoom
const panHotkey = process.platform === 'linux' ? ['Control', 'Alt'] : ['Alt'];
const bgImageLocator = page.locator(backgroundImageSelector);
await bgImageLocator.hover({trial: true});
// zoom in
await page.mouse.wheel(0, deltaYStep * 2);
await bgImageLocator.hover({trial: true});
const zoomedBoundingBox = await bgImageLocator.boundingBox();
const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2;
const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
// move to the right
// center the mouse pointer
await page.mouse.move(imageCenterX, imageCenterY);
//Get Diagnostic info about process environment
console.log('process.platform is ' + process.platform);
const getUA = await page.evaluate(() => navigator.userAgent);
console.log('navigator.userAgent ' + getUA);
// Pan Imagery Hints
const expectedAltText = process.platform === 'linux' ? 'Ctrl+Alt drag to pan' : 'Alt drag to pan';
const imageryHintsText = await page.locator('.c-imagery__hints').innerText();
expect(expectedAltText).toEqual(imageryHintsText);
// pan right
await Promise.all(panHotkey.map(x => page.keyboard.down(x)));
await page.mouse.down();
await page.mouse.move(imageCenterX - 200, imageCenterY, 10);
await page.mouse.up();
await Promise.all(panHotkey.map(x => page.keyboard.up(x)));
const afterRightPanBoundingBox = await bgImageLocator.boundingBox();
expect(zoomedBoundingBox.x).toBeGreaterThan(afterRightPanBoundingBox.x);
// pan left
await Promise.all(panHotkey.map(x => page.keyboard.down(x)));
await page.mouse.down();
await page.mouse.move(imageCenterX, imageCenterY, 10);
await page.mouse.up();
await Promise.all(panHotkey.map(x => page.keyboard.up(x)));
const afterLeftPanBoundingBox = await bgImageLocator.boundingBox();
expect(afterRightPanBoundingBox.x).toBeLessThan(afterLeftPanBoundingBox.x);
// pan up
await page.mouse.move(imageCenterX, imageCenterY);
await Promise.all(panHotkey.map(x => page.keyboard.down(x)));
await page.mouse.down();
await page.mouse.move(imageCenterX, imageCenterY + 200, 10);
await page.mouse.up();
await Promise.all(panHotkey.map(x => page.keyboard.up(x)));
const afterUpPanBoundingBox = await bgImageLocator.boundingBox();
expect(afterUpPanBoundingBox.y).toBeGreaterThan(afterLeftPanBoundingBox.y);
// pan down
await Promise.all(panHotkey.map(x => page.keyboard.down(x)));
await page.mouse.down();
await page.mouse.move(imageCenterX, imageCenterY - 200, 10);
await page.mouse.up();
await Promise.all(panHotkey.map(x => page.keyboard.up(x)));
const afterDownPanBoundingBox = await bgImageLocator.boundingBox();
expect(afterDownPanBoundingBox.y).toBeLessThan(afterUpPanBoundingBox.y);
});
test('Can use + - buttons to zoom on the image', async ({ page }) => {
const bgImageLocator = page.locator(backgroundImageSelector);
await bgImageLocator.hover({trial: true});
const zoomInBtn = page.locator('.t-btn-zoom-in').nth(0);
const zoomOutBtn = page.locator('.t-btn-zoom-out').nth(0);
const initialBoundingBox = await bgImageLocator.boundingBox();
await zoomInBtn.click();
await zoomInBtn.click();
// wait for zoom animation to finish
await bgImageLocator.hover({trial: true});
const zoomedInBoundingBox = await bgImageLocator.boundingBox();
expect(zoomedInBoundingBox.height).toBeGreaterThan(initialBoundingBox.height);
expect(zoomedInBoundingBox.width).toBeGreaterThan(initialBoundingBox.width);
await zoomOutBtn.click();
// wait for zoom animation to finish
await bgImageLocator.hover({trial: true});
const zoomedOutBoundingBox = await bgImageLocator.boundingBox();
expect(zoomedOutBoundingBox.height).toBeLessThan(zoomedInBoundingBox.height);
expect(zoomedOutBoundingBox.width).toBeLessThan(zoomedInBoundingBox.width);
});
test('Can use the reset button to reset the image', async ({ page }) => {
const bgImageLocator = page.locator(backgroundImageSelector);
// wait for zoom animation to finish
await bgImageLocator.hover({trial: true});
const zoomInBtn = page.locator('.t-btn-zoom-in').nth(0);
const zoomResetBtn = page.locator('.t-btn-zoom-reset').nth(0);
const initialBoundingBox = await bgImageLocator.boundingBox();
await zoomInBtn.click();
// wait for zoom animation to finish
await bgImageLocator.hover({trial: true});
await zoomInBtn.click();
// wait for zoom animation to finish
await bgImageLocator.hover({trial: true});
const zoomedInBoundingBox = await bgImageLocator.boundingBox();
expect.soft(zoomedInBoundingBox.height).toBeGreaterThan(initialBoundingBox.height);
expect.soft(zoomedInBoundingBox.width).toBeGreaterThan(initialBoundingBox.width);
await zoomResetBtn.click();
// wait for zoom animation to finish
await bgImageLocator.hover({trial: true});
const resetBoundingBox = await bgImageLocator.boundingBox();
expect.soft(resetBoundingBox.height).toBeLessThan(zoomedInBoundingBox.height);
expect.soft(resetBoundingBox.width).toBeLessThan(zoomedInBoundingBox.width);
expect.soft(resetBoundingBox.height).toEqual(initialBoundingBox.height);
expect(resetBoundingBox.width).toEqual(initialBoundingBox.width);
});
test('Using the zoom features does not pause telemetry', async ({ page }) => {
const bgImageLocator = page.locator(backgroundImageSelector);
const pausePlayButton = page.locator('.c-button.pause-play');
// wait for zoom animation to finish
await bgImageLocator.hover({trial: true});
// open the time conductor drop down
await page.locator('button:has-text("Fixed Timespan")').click();
// Click local clock
await page.locator('[data-testid="conductor-modeOption-realtime"]').click();
await expect.soft(pausePlayButton).not.toHaveClass(/is-paused/);
const zoomInBtn = page.locator('.t-btn-zoom-in').nth(0);
await zoomInBtn.click();
// wait for zoom animation to finish
await bgImageLocator.hover({trial: true});
return expect(pausePlayButton).not.toHaveClass(/is-paused/);
});
});
// The following test case will cover these scenarios
// ('Can use Mouse Wheel to zoom in and out of previous image');
// ('Can use alt+drag to move around image once zoomed in');
// ('Clicking on the left arrow should pause the imagery and go to previous image');
// ('If the imagery view is in pause mode, it should not be updated when new images come in');
// ('If the imagery view is not in pause mode, it should be updated when new images come in');
const backgroundImageSelector = '.c-imagery__main-image__background-image';
test('Example Imagery in Display layout', async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/5265'
});
// Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
// Click the Create button
await page.click('button:has-text("Create")');
// Click text=Example Imagery
await page.click('text=Example Imagery');
// Clear and set Image load delay to minimum value
// FIXME: Update the value to 5000 ms when this bug is fixed.
// See: https://github.com/nasa/openmct/issues/5265
await page.locator('input[type="number"]').fill('');
await page.locator('input[type="number"]').fill('0');
// Click text=OK
await Promise.all([
page.waitForNavigation({waitUntil: 'networkidle'}),
page.click('text=OK'),
//Wait for Save Banner to appear
page.waitForSelector('.c-message-banner__message')
]);
// Wait until Save Banner is gone
await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery');
const bgImageLocator = page.locator(backgroundImageSelector);
await bgImageLocator.hover({trial: true});
// Click previous image button
const previousImageButton = page.locator('.c-nav--prev');
await previousImageButton.click();
// Verify previous image
const selectedImage = page.locator('.selected');
await expect(selectedImage).toBeVisible();
// Zoom in
const originalImageDimensions = await page.locator(backgroundImageSelector).boundingBox();
await bgImageLocator.hover({trial: true});
const deltaYStep = 100; // equivalent to 1x zoom
await page.mouse.wheel(0, deltaYStep * 2);
const zoomedBoundingBox = await bgImageLocator.boundingBox();
const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2;
const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
// Wait for zoom animation to finish
await bgImageLocator.hover({trial: true});
const imageMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox();
expect(imageMouseZoomedIn.height).toBeGreaterThan(originalImageDimensions.height);
expect(imageMouseZoomedIn.width).toBeGreaterThan(originalImageDimensions.width);
// Center the mouse pointer
await page.mouse.move(imageCenterX, imageCenterY);
// Pan Imagery Hints
const expectedAltText = process.platform === 'linux' ? 'Ctrl+Alt drag to pan' : 'Alt drag to pan';
const imageryHintsText = await page.locator('.c-imagery__hints').innerText();
expect(expectedAltText).toEqual(imageryHintsText);
// Click next image button
const nextImageButton = page.locator('.c-nav--next');
await nextImageButton.click();
// Click time conductor mode button
await page.locator('.c-mode-button').click();
// Select local clock mode
await page.locator('[data-testid=conductor-modeOption-realtime]').click();
// Zoom in on next image
await bgImageLocator.hover({trial: true});
await page.mouse.wheel(0, deltaYStep * 2);
// Wait for zoom animation to finish
await bgImageLocator.hover({trial: true});
const imageNextMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox();
expect(imageNextMouseZoomedIn.height).toBeGreaterThan(originalImageDimensions.height);
expect(imageNextMouseZoomedIn.width).toBeGreaterThan(originalImageDimensions.width);
// Click previous image button
await previousImageButton.click();
// Verify previous image
await expect(selectedImage).toBeVisible();
const imageCount = await page.locator('.c-imagery__thumb').count();
await expect.poll(async () => {
const newImageCount = await page.locator('.c-imagery__thumb').count();
return newImageCount;
}, {
message: "verify that new images still stream in",
timeout: 6 * 1000
}).toBeGreaterThan(imageCount);
// Verify selected image is still displayed
await expect(selectedImage).toBeVisible();
// Unpause imagery
await page.locator('.pause-play').click();
//Get background-image url from background-image css prop
const backgroundImage = page.locator('.c-imagery__main-image__background-image');
let backgroundImageUrl = await backgroundImage.evaluate((el) => {
return window.getComputedStyle(el).getPropertyValue('background-image').match(/url\(([^)]+)\)/)[1];
});
let backgroundImageUrl1 = backgroundImageUrl.slice(1, -1); //forgive me, padre
console.log('backgroundImageUrl1 ' + backgroundImageUrl1);
let backgroundImageUrl2;
await expect.poll(async () => {
// Verify next image has updated
let backgroundImageUrlNext = await backgroundImage.evaluate((el) => {
return window.getComputedStyle(el).getPropertyValue('background-image').match(/url\(([^)]+)\)/)[1];
});
backgroundImageUrl2 = backgroundImageUrlNext.slice(1, -1); //forgive me, padre
return backgroundImageUrl2;
}, {
message: "verify next image has updated",
timeout: 6 * 1000
}).not.toBe(backgroundImageUrl1);
console.log('backgroundImageUrl2 ' + backgroundImageUrl2);
});
test.describe('Example imagery thumbnails resize in display layouts', () => {
test('Resizing the layout changes thumbnail visibility and size', async ({ page }) => {
await page.goto('/', { waitUntil: 'networkidle' });
const thumbsWrapperLocator = page.locator('.c-imagery__thumbs-wrapper');
// Click button:has-text("Create")
await page.locator('button:has-text("Create")').click();
// Click li:has-text("Display Layout")
await page.locator('li:has-text("Display Layout")').click();
const displayLayoutTitleField = page.locator('text=Properties Title Notes Horizontal grid (px) Vertical grid (px) Horizontal size ( >> input[type="text"]');
await displayLayoutTitleField.click();
await displayLayoutTitleField.fill('Thumbnail Display Layout');
// Click text=OK
await Promise.all([
page.waitForNavigation(),
page.locator('text=OK').click()
]);
// Click text=Snapshot Save and Finish Editing Save and Continue Editing >> button >> nth=1
await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
// Click text=Save and Finish Editing
await page.locator('text=Save and Finish Editing').click();
// Click button:has-text("Create")
await page.locator('button:has-text("Create")').click();
// Click li:has-text("Example Imagery")
await page.locator('li:has-text("Example Imagery")').click();
const imageryTitleField = page.locator('text=Properties Title Notes Images url list (comma separated) Image load delay (milli >> input[type="text"]');
// Click text=Properties Title Notes Images url list (comma separated) Image load delay (milli >> input[type="text"]
await imageryTitleField.click();
// Fill text=Properties Title Notes Images url list (comma separated) Image load delay (milli >> input[type="text"]
await imageryTitleField.fill('Thumbnail Example Imagery');
// Click text=OK
await Promise.all([
page.waitForNavigation(),
page.locator('text=OK').click()
]);
// Click text=Thumbnail Example Imagery Imagery Layout Snapshot >> button >> nth=0
await Promise.all([
page.waitForNavigation(),
page.locator('text=Thumbnail Example Imagery Imagery Layout Snapshot >> button').first().click()
]);
// Edit mode
await page.locator('text=Thumbnail Display Layout Snapshot >> button').nth(3).click();
// Click on example imagery to expose toolbar
await page.locator('text=Thumbnail Example Imagery Snapshot Large View').click();
// expect thumbnails not be visible when first added
expect.soft(thumbsWrapperLocator.isHidden()).toBeTruthy();
// Resize the example imagery vertically to change the thumbnail visibility
/*
The following arbitrary values are added to observe the separate visual
conditions of the thumbnails (hidden, small thumbnails, regular thumbnails).
Specifically, height is set to 50px for small thumbs and 100px for regular
*/
// Click #mct-input-id-103
await page.locator('#mct-input-id-103').click();
// Fill #mct-input-id-103
await page.locator('#mct-input-id-103').fill('50');
expect(thumbsWrapperLocator.isVisible()).toBeTruthy();
await expect(thumbsWrapperLocator).toHaveClass(/is-small-thumbs/);
// Resize the example imagery vertically to change the thumbnail visibility
// Click #mct-input-id-103
await page.locator('#mct-input-id-103').click();
// Fill #mct-input-id-103
await page.locator('#mct-input-id-103').fill('100');
expect(thumbsWrapperLocator.isVisible()).toBeTruthy();
await expect(thumbsWrapperLocator).not.toHaveClass(/is-small-thumbs/);
});
});
test.describe('Example Imagery in Flexible layout', () => {
test.fixme('Can use Mouse Wheel to zoom in and out of previous image');
test.fixme('Can use alt+drag to move around image once zoomed in');
test.fixme('Can zoom into the latest image and the real-time/fixed-time imagery will pause');
test.fixme('Clicking on the left arrow should pause the imagery and go to previous image');
test.fixme('If the imagery view is in pause mode, it should not be updated when new images come in');
test.fixme('If the imagery view is not in pause mode, it should be updated when new images come in');
});
test.describe('Example Imagery in Tabs view', () => {
test.fixme('Can use Mouse Wheel to zoom in and out of previous image');
test.fixme('Can use alt+drag to move around image once zoomed in');
test.fixme('Can zoom into the latest image and the real-time/fixed-time imagery will pause');
test.fixme('Can zoom into a previous image from thumbstrip in real-time or fixed-time');
test.fixme('Clicking on the left arrow should pause the imagery and go to previous image');
test.fixme('If the imagery view is in pause mode, it should not be updated when new images come in');
test.fixme('If the imagery view is not in pause mode, it should be updated when new images come in');
});

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, United States Government
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@ -20,17 +20,11 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(['objectUtils'], function (objectUtils) {
function AdapterCapability(domainObject) {
this.domainObject = domainObject;
}
// this will be called from the test suite with
// await page.addInitScript({ path: path.join(__dirname, 'addInitRestrictedNotebook.js') });
// it will install the RestrictedNotebook since it is not installed by default
AdapterCapability.prototype.invoke = function () {
return objectUtils.toNewFormat(
this.domainObject.getModel(),
this.domainObject.getId()
);
};
return AdapterCapability;
document.addEventListener('DOMContentLoaded', () => {
const openmct = window.openmct;
openmct.install(openmct.plugins.RestrictedNotebook('CUSTOM_NAME'));
});

View File

@ -0,0 +1,198 @@
/*****************************************************************************
* 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 is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*
This test suite is dedicated to tests which verify the basic operations surrounding Notebooks.
*/
const { test } = require('../../../fixtures');
test.describe('Notebook CRUD Operations', () => {
test.fixme('Can create a Notebook Object', async ({ page }) => {
//Create domain object
//Newly created notebook should have one Section and one page, 'Unnamed Section'/'Unnamed Page'
});
test.fixme('Can update a Notebook Object', async ({ page }) => {});
test.fixme('Can view a perviously created Notebook Object', async ({ page }) => {});
test.fixme('Can Delete a Notebook Object', async ({ page }) => {
// Other than non-persistible objects
});
});
test.describe('Default Notebook', () => {
// General Default Notebook statements
// ## Useful commands:
// 1. - To check default notebook:
// `JSON.parse(localStorage.getItem('notebook-storage'));`
// 1. - Clear default notebook:
// `localStorage.setItem('notebook-storage', null);`
test.fixme('A newly created Notebook is automatically set as the default notebook if no other notebooks exist', async ({ page }) => {
//Create new notebook
//Verify Default Notebook Characteristics
});
test.fixme('A newly created Notebook is automatically set as the default notebook if at least one other notebook exists', async ({ page }) => {
//Create new notebook A
//Create second notebook B
//Verify Non-Default Notebook A Characteristics
//Verify Default Notebook B Characteristics
});
test.fixme('If a default notebook is deleted, the second most recent notebook becomes the default', async ({ page }) => {
//Create new notebook A
//Create second notebook B
//Delete Notebook B
//Verify Default Notebook A Characteristics
});
});
test.describe('Notebook section tests', () => {
//The following test cases are associated with Notebook Sections
test.fixme('New sections are automatically named Unnamed Section with Unnamed Page', async ({ page }) => {
//Create new notebook A
//Add section
//Verify new section and new page details
});
test.fixme('Section selection operations and associated behavior', async ({ page }) => {
//Create new notebook A
//Add Sections until 6 total with no default section/page
//Select 3rd section
//Delete 4th section
//3rd section is still selected
//Delete 3rd section
//1st section is selected
//Set 3rd section as default
//Delete 2nd section
//3rd section is still default
//Delete 3rd section
//1st is selected and there is no default notebook
});
});
test.describe('Notebook page tests', () => {
//The following test cases are associated with Notebook Pages
test.fixme('Page selection operations and associated behavior', async ({ page }) => {
//Create new notebook A
//Delete existing Page
//New 'Unnamed Page' automatically created
//Create 6 total Pages without a default page
//Select 3rd
//Delete 3rd
//First is now selected
//Set 3rd as default
//Select 2nd page
//Delete 2nd page
//3rd (default) is now selected
//Set 3rd as default page
//Select 3rd (default) page
//Delete 3rd page
//First is now selected and there is no default notebook
});
});
test.describe('Notebook search tests', () => {
test.fixme('Can search for a single result', async ({ page }) => {});
test.fixme('Can search for many results', async ({ page }) => {});
test.fixme('Can search for new and recently modified entries', async ({ page }) => {});
test.fixme('Can search for section text', async ({ page }) => {});
test.fixme('Can search for page text', async ({ page }) => {});
test.fixme('Can search for entry text', async ({ page }) => {});
});
test.describe('Notebook entry tests', () => {
test.fixme('When a new entry is created, it should be focused', async ({ page }) => {});
test.fixme('When a telemetry object is dropped into a notebook, a new entry is created and it should be focused', async ({ page }) => {
// Drag and drop any telmetry object on 'drop object'
// new entry gets created with telemtry object
});
test.fixme('When a telemetry object is dropped into a notebooks existing entry, it should be focused', async ({ page }) => {
// Drag and drop any telemetry object onto existing entry
// Entry updated with object and snapshot
});
test.fixme('new entries persist through navigation events without save', async ({ page }) => {});
test.fixme('previous and new entries can be deleted', async ({ page }) => {});
});
test.describe('Snapshot Menu tests', () => {
test.fixme('When no default notebook is selected, Snapshot Menu dropdown should only have a single option', async ({ page }) => {
// There should be no default notebook
// Clear default notebook if exists using `localStorage.setItem('notebook-storage', null);`
// refresh page
// Click on 'Notebook Snaphot Menu'
// 'save to Notebook Snapshots' should be only option there
});
test.fixme('When default notebook is updated selected, Snapshot Menu dropdown should list it as the newest option', async ({ page }) => {
// Create 2a notebooks
// Set Notebook A as Default
// Open Snapshot Menu and note that Notebook A is listed
// Close Snapshot Menu
// Set Default Notebook to Notebook B
// Open Snapshot Notebook and note that Notebook B is listed
// Select Default Notebook Option and verify that Snapshot is added to Notebook B
});
test.fixme('Can add Snapshots via Snapshot Menu and details are correct', async ({ page }) => {
//Note this should be a visual test, too
// Create Telemetry object
// Create A notebook with many pages and sections.
// Set page and section defaults to be between first and last of many. i.e. 3 of 5
// Navigate to Telemetry object
// Select Default Notebook Option and verify that Snapshot is added to Notebook A
// Verify Snapshot Details appear correctly
});
test.fixme('Snapshots adjust time conductor', async ({ page }) => {
// Create Telemetry object
// Set Telemetry object's timeconductor to Fixed time with Start and Endtimes are recorded
// Embed Telemetry object into notebook
// Set Time Conductor to Local clock
// Click into embedded telemetry object and verify object appears with same fixed time from record
});
});
test.describe('Snapshot Container tests', () => {
test.fixme('5 Snapshots can be added to a container', async ({ page }) => {});
test.fixme('5 Snapshots can be added to a container and Deleted with Delete All action', async ({ page }) => {});
test.fixme('A snapshot can be Deleted from Container', async ({ page }) => {});
test.fixme('A snapshot can be Previewed from Container', async ({ page }) => {});
test.fixme('A snapshot Container can be open and closed', async ({ page }) => {});
test.fixme('Can add object to Snapshot container and pull into notebook and create a new entry', async ({ page }) => {
//Create Notebook
//Create Telemetry Object
//From Telemetry Object, use 'save to Notebook Snapshots'
//Snapshots indicator should blink, click on it to view snapshots
//Navigate to Notebook
//Drag and Drop onto droppable area for new entry
//New Entry created with given snapshot added
//Snapshot removed from container?
});
test.fixme('Can add object to Snapshot container and pull into notebook and existing entry', async ({ page }) => {
//Create Notebook
//Create Telemetry Object
//From Telemetry Object, use 'save to Notebook Snapshots'
//Snapshots indicator should blink, click on it to view snapshots
//Navigate to Notebook
//Drag and Drop into exiting entry
//Existing Entry updated with given snapshot
//Snapshot removed from container?
});
test.fixme('Verify Embedded options for PNG, JPG, and Annotate work correctly', async ({ page }) => {
//Add snapshot to container
//Verify PNG, JPG, and Annotate buttons work correctly
});
});

View File

@ -0,0 +1,264 @@
/*****************************************************************************
* 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 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('../../../fixtures');
const { expect } = require('@playwright/test');
const path = require('path');
const TEST_TEXT = 'Testing text for entries.';
const TEST_TEXT_NAME = 'Test Page';
const CUSTOM_NAME = 'CUSTOM_NAME';
const COMMIT_BUTTON_TEXT = 'button:has-text("Commit Entries")';
const SINE_WAVE_GENERATOR = 'text=Unnamed Sine Wave Generator';
const NOTEBOOK_DROP_AREA = '.c-notebook__drag-area';
/**
* @param {import('@playwright/test').Page} page
*/
async function startAndAddNotebookObject(page) {
// eslint-disable-next-line no-undef
await page.addInitScript({ path: path.join(__dirname, 'addInitRestrictedNotebook.js') });
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
//Click the Create button
await page.click('button:has-text("Create")');
// Click text=CUSTOME_NAME
await page.click(`text=${CUSTOM_NAME}`); // secondarily tests renamability also
// Click text=OK
await Promise.all([
page.waitForNavigation({waitUntil: 'networkidle'}),
page.click('text=OK')
]);
return;
}
/**
* @param {import('@playwright/test').Page} page
*/
async function enterTextEntry(page) {
// Click .c-notebook__drag-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(TEST_TEXT);
await page.locator('div.c-ne__text').press('Enter');
return;
}
/**
* @param {import('@playwright/test').Page} page
*/
async function dragAndDropEmbed(page) {
// Click button:has-text("Create")
await page.locator('button:has-text("Create")').click();
// Click li:has-text("Sine Wave Generator")
await page.locator('li:has-text("Sine Wave Generator")').click();
// Click form[name="mctForm"] >> text=My Items
await page.locator('form[name="mctForm"] >> text=My Items').click();
// Click text=OK
await page.locator('text=OK').click();
// Click text=Open MCT My Items >> span >> nth=3
await page.locator('text=Open MCT My Items >> span').nth(3).click();
// Click text=Unnamed CUSTOM_NAME
await Promise.all([
page.waitForNavigation(),
page.locator('text=Unnamed CUSTOM_NAME').click()
]);
await page.dragAndDrop(SINE_WAVE_GENERATOR, NOTEBOOK_DROP_AREA);
return;
}
/**
* @param {import('@playwright/test').Page} page
*/
async function lockPage(page) {
const commitButton = page.locator(COMMIT_BUTTON_TEXT);
await commitButton.click();
// confirmation dialog click
await page.locator('text=Lock Page').click();
// waiting for mutation of locked page
await new Promise((resolve, reject) => {
setTimeout(resolve, 1000);
});
return;
}
/**
* @param {import('@playwright/test').Page} page
*/
async function openContextMenuRestrictedNotebook(page) {
// Click text=Open MCT My Items (This expands the My Items folder to show it's chilren in the tree)
await page.locator('text=Open MCT My Items >> span').nth(3).click();
// Click a:has-text("Unnamed CUSTOM_NAME")
await page.locator(`a:has-text("Unnamed ${CUSTOM_NAME}")`).click({
button: 'right'
});
return;
}
test.describe('Restricted Notebook', () => {
test.beforeEach(async ({ page }) => {
await startAndAddNotebookObject(page);
});
test('Can be renamed', async ({ page }) => {
await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText(`Unnamed ${CUSTOM_NAME}`);
});
test('Can be deleted if there are no locked pages', async ({ page }) => {
await openContextMenuRestrictedNotebook(page);
const menuOptions = page.locator('.c-menu ul');
await expect.soft(menuOptions).toContainText('Remove');
const restrictedNotebookTreeObject = page.locator(`a:has-text("Unnamed ${CUSTOM_NAME}")`);
// notbook tree object exists
expect.soft(await restrictedNotebookTreeObject.count()).toEqual(1);
// Click text=Remove
await page.locator('text=Remove').click();
// Click text=OK
await Promise.all([
page.waitForNavigation(/*{ url: 'http://localhost:8080/#/browse/mine?tc.mode=fixed&tc.startBound=1653671067340&tc.endBound=1653672867340&tc.timeSystem=utc&view=grid' }*/),
page.locator('text=OK').click()
]);
// has been deleted
expect.soft(await restrictedNotebookTreeObject.count()).toEqual(0);
});
test('Can be locked if at least one page has one entry', async ({ page }) => {
await enterTextEntry(page);
const commitButton = page.locator(COMMIT_BUTTON_TEXT);
expect.soft(await commitButton.count()).toEqual(1);
});
});
test.describe('Restricted Notebook with at least one entry and with the page locked', () => {
test.beforeEach(async ({ page }) => {
await startAndAddNotebookObject(page);
await enterTextEntry(page);
await lockPage(page);
// open sidebar
await page.locator('button.c-notebook__toggle-nav-button').click();
});
test('Locked page should now be in a locked state', async ({ page }) => {
// main lock message on page
const lockMessage = page.locator('text=This page has been committed and cannot be modified or removed');
expect.soft(await lockMessage.count()).toEqual(1);
// lock icon on page in sidebar
const pageLockIcon = page.locator('ul.c-notebook__pages li div.icon-lock');
expect.soft(await pageLockIcon.count()).toEqual(1);
// no way to remove a restricted notebook with a locked page
await openContextMenuRestrictedNotebook(page);
const menuOptions = page.locator('.c-menu ul');
await expect.soft(menuOptions).not.toContainText('Remove');
});
test('Can still: add page, rename, add entry, delete unlocked pages', async ({ page }) => {
// Click text=Page Add >> button
await Promise.all([
page.waitForNavigation(),
page.locator('text=Page Add >> button').click()
]);
// Click text=Unnamed Page >> nth=1
await page.locator('text=Unnamed Page').nth(1).click();
// Press a with modifiers
await page.locator('text=Unnamed Page').nth(1).fill(TEST_TEXT_NAME);
// expect to be able to rename unlocked pages
const newPageElement = page.locator(`text=${TEST_TEXT_NAME}`);
const newPageCount = await newPageElement.count();
await newPageElement.press('Enter'); // exit contenteditable state
expect.soft(newPageCount).toEqual(1);
// enter test text
await enterTextEntry(page);
// expect new page to be lockable
const commitButton = page.locator(COMMIT_BUTTON_TEXT);
expect.soft(await commitButton.count()).toEqual(1);
// Click text=Unnamed PageTest Page >> button
await page.locator('text=Unnamed PageTest Page >> button').click();
// Click text=Delete Page
await page.locator('text=Delete Page').click();
// Click text=Ok
await Promise.all([
page.waitForNavigation(),
page.locator('text=Ok').click()
]);
// deleted page, should no longer exist
const deletedPageElement = page.locator(`text=${TEST_TEXT_NAME}`);
expect.soft(await deletedPageElement.count()).toEqual(0);
});
});
test.describe('Restricted Notebook with a page locked and with an embed', () => {
test.beforeEach(async ({ page }) => {
await startAndAddNotebookObject(page);
await dragAndDropEmbed(page);
});
test('Allows embeds to be deleted if page unlocked', async ({ page }) => {
// Click .c-ne__embed__name .c-popup-menu-button
await page.locator('.c-ne__embed__name .c-popup-menu-button').click(); // embed popup menu
const embedMenu = page.locator('body >> .c-menu');
await expect.soft(embedMenu).toContainText('Remove This Embed');
});
test('Disallows embeds to be deleted if page locked', async ({ page }) => {
await lockPage(page);
// Click .c-ne__embed__name .c-popup-menu-button
await page.locator('.c-ne__embed__name .c-popup-menu-button').click(); // embed popup menu
const embedMenu = page.locator('body >> .c-menu');
await expect.soft(embedMenu).not.toContainText('Remove This Embed');
});
});

View File

@ -0,0 +1,198 @@
/*****************************************************************************
* 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 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.
*****************************************************************************/
/*
Testsuite for plot autoscale.
*/
const { test: _test } = require('../../../fixtures.js');
const { expect } = require('@playwright/test');
// create a new `test` API that will not append platform details to snapshot
// file names, only for the tests in this file, so that the same snapshots will
// be used for all platforms.
const test = _test.extend({
_autoSnapshotSuffix: [
async ({}, use, testInfo) => {
testInfo.snapshotSuffix = '';
await use();
},
{ auto: true }
]
});
test.use({
viewport: {
width: 1280,
height: 720
}
});
test.describe('ExportAsJSON', () => {
test('User can set autoscale with a valid range @snapshot', async ({ page }) => {
//This is necessary due to the size of the test suite.
await test.setTimeout(120 * 1000);
await page.goto('/', { waitUntil: 'networkidle' });
await setTimeRange(page);
await createSinewaveOverlayPlot(page);
await testYTicks(page, ['-1.00', '-0.50', '0.00', '0.50', '1.00']);
await turnOffAutoscale(page);
const canvas = page.locator('canvas').nth(1);
// Make sure that after turning off autoscale, the user selected range values start at the same values the plot had prior.
await Promise.all([
testYTicks(page, ['-1.00', '-0.50', '0.00', '0.50', '1.00']),
new Promise(r => setTimeout(r, 100))
.then(() => canvas.screenshot())
.then(shot => expect(shot).toMatchSnapshot('autoscale-canvas-prepan.png', { maxDiffPixels: 40 }))
]);
await page.keyboard.down('Alt');
await canvas.dragTo(canvas, {
sourcePosition: {
x: 200,
y: 200
},
targetPosition: {
x: 400,
y: 400
}
});
await page.keyboard.up('Alt');
// Ensure the drag worked.
await Promise.all([
testYTicks(page, ['0.00', '0.50', '1.00', '1.50', '2.00']),
new Promise(r => setTimeout(r, 100))
.then(() => canvas.screenshot())
.then(shot => expect(shot).toMatchSnapshot('autoscale-canvas-panned.png', { maxDiffPixels: 40 }))
]);
});
});
/**
* @param {import('@playwright/test').Page} page
* @param {string} start
* @param {string} end
*/
async function setTimeRange(page, start = '2022-03-29 22:00:00.000Z', end = '2022-03-29 22:00:30.000Z') {
// Set a specific time range for consistency, otherwise it will change
// on every test to a range based on the current time.
const timeInputs = page.locator('input.c-input--datetime');
await timeInputs.first().click();
await timeInputs.first().fill(start);
await timeInputs.nth(1).click();
await timeInputs.nth(1).fill(end);
}
/**
* @param {import('@playwright/test').Page} page
*/
async function createSinewaveOverlayPlot(page) {
// click create button
await page.locator('button:has-text("Create")').click();
// add overlay plot with defaults
await page.locator('li:has-text("Overlay Plot")').click();
await Promise.all([
page.waitForNavigation(),
page.locator('text=OK').click(),
//Wait for Save Banner to appear1
page.waitForSelector('.c-message-banner__message')
]);
//Wait until Save Banner is gone
await page.locator('.c-message-banner__close-button').click();
await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
// save (exit edit mode)
await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
await page.locator('text=Save and Finish Editing').click();
// click create button
await page.locator('button:has-text("Create")').click();
// add sine wave generator with defaults
await page.locator('li:has-text("Sine Wave Generator")').click();
await Promise.all([
page.waitForNavigation(),
page.locator('text=OK').click(),
//Wait for Save Banner to appear1
page.waitForSelector('.c-message-banner__message')
]);
//Wait until Save Banner is gone
await page.locator('.c-message-banner__close-button').click();
await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
// focus the overlay plot
await page.locator('text=Open MCT My Items >> span').nth(3).click();
await Promise.all([
page.waitForNavigation(),
page.locator('text=Unnamed Overlay Plot').first().click()
]);
}
/**
* @param {import('@playwright/test').Page} page
*/
async function turnOffAutoscale(page) {
// enter edit mode
await page.locator('text=Unnamed Overlay Plot Snapshot >> button').nth(3).click();
// uncheck autoscale
await page.locator('text=Y Axis Label Log mode Auto scale Padding >> input[type="checkbox"] >> nth=1').uncheck();
// save
await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
await Promise.all([
page.locator('text=Save and Finish Editing').click(),
//Wait for Save Banner to appear
page.waitForSelector('.c-message-banner__message')
]);
//Wait until Save Banner is gone
await page.locator('.c-message-banner__close-button').click();
await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
}
/**
* @param {import('@playwright/test').Page} page
*/
async function testYTicks(page, values) {
const yTicks = page.locator('.gl-plot-y-tick-label');
await page.locator('canvas >> nth=1').hover();
let promises = [yTicks.count().then(c => expect(c).toBe(values.length))];
for (let i = 0, l = values.length; i < l; i += 1) {
promises.push(expect(yTicks.nth(i)).toHaveText(values[i])); // eslint-disable-line
}
await Promise.all(promises);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,312 @@
/*****************************************************************************
* 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 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 log plot functionality. Note this test suite if very much under active development and should not
necessarily be used for reference when writing new tests in this area.
*/
const { test } = require('../../../fixtures.js');
const { expect } = require('@playwright/test');
test.describe('Log plot tests', () => {
test('Log Plot ticks are functionally correct in regular and log mode and after refresh', async ({ page }) => {
//This is necessary due to the size of the test suite.
await test.setTimeout(120 * 1000);
await makeOverlayPlot(page);
await testRegularTicks(page);
await enableEditMode(page);
await enableLogMode(page);
await testLogTicks(page);
await disableLogMode(page);
await testRegularTicks(page);
await enableLogMode(page);
await testLogTicks(page);
await saveOverlayPlot(page);
await testLogTicks(page);
//await testLogPlotPixels(page);
// FIXME: Get rid of the waitForTimeout() and lint warning exception.
// eslint-disable-next-line playwright/no-wait-for-timeout
await page.waitForTimeout(1 * 1000);
// refresh page and wait for charts and ticks to load
await page.reload({ waitUntil: 'networkidle'});
await page.waitForSelector('.gl-plot-chart-area');
await page.waitForSelector('.gl-plot-y-tick-label');
// test log ticks hold up after refresh
await testLogTicks(page);
//await testLogPlotPixels(page);
});
// Leaving test as 'TODO' for now.
// NOTE: Not eligible for community contributions.
test.fixme('Verify that log mode option is reflected in import/export JSON', async ({ page }) => {
await makeOverlayPlot(page);
await enableEditMode(page);
await enableLogMode(page);
await saveOverlayPlot(page);
// TODO ...export, delete the overlay, then import it...
//await testLogTicks(page);
// TODO, the plot is slightly at different position that in the other test, so this fails.
// ...We can fix it by copying all steps from the first test...
// await testLogPlotPixels(page);
});
});
/**
* Makes an overlay plot with a sine wave generator and clicks on the overlay plot in the sidebar so it is the active thing displayed.
* @param {import('@playwright/test').Page} page
*/
async function makeOverlayPlot(page) {
// 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' });
// Set a specific time range for consistency, otherwise it will change
// on every test to a range based on the current time.
const timeInputs = page.locator('input.c-input--datetime');
await timeInputs.first().click();
await timeInputs.first().fill('2022-03-29 22:00:00.000Z');
await timeInputs.nth(1).click();
await timeInputs.nth(1).fill('2022-03-29 22:00:30.000Z');
// create overlay plot
await page.locator('button.c-create-button').click();
await page.locator('li:has-text("Overlay Plot")').click();
await Promise.all([
page.waitForNavigation({ waitUntil: 'networkidle'}),
page.locator('text=OK').click(),
//Wait for Save Banner to appear
page.waitForSelector('.c-message-banner__message')
]);
//Wait until Save Banner is gone
await page.locator('.c-message-banner__close-button').click();
await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
// save the overlay plot
await saveOverlayPlot(page);
// create a sinewave generator
await page.locator('button.c-create-button').click();
await page.locator('li:has-text("Sine Wave Generator")').click();
// set amplitude to 6, offset 4, period 2
await page.locator('div:nth-child(5) .form-row .c-form-row__controls .form-control .field input').click();
await page.locator('div:nth-child(5) .form-row .c-form-row__controls .form-control .field input').fill('6');
await page.locator('div:nth-child(6) .form-row .c-form-row__controls .form-control .field input').click();
await page.locator('div:nth-child(6) .form-row .c-form-row__controls .form-control .field input').fill('4');
await page.locator('div:nth-child(7) .form-row .c-form-row__controls .form-control .field input').click();
await page.locator('div:nth-child(7) .form-row .c-form-row__controls .form-control .field input').fill('2');
// Click OK to make generator
await Promise.all([
page.waitForNavigation({ waitUntil: 'networkidle'}),
page.locator('text=OK').click(),
//Wait for Save Banner to appear
page.waitForSelector('.c-message-banner__message')
]);
//Wait until Save Banner is gone
await page.locator('.c-message-banner__close-button').click();
await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
// click on overlay plot
await page.locator('text=Open MCT My Items >> span').nth(3).click();
await Promise.all([
page.waitForNavigation(),
page.locator('text=Unnamed Overlay Plot').first().click()
]);
}
/**
* @param {import('@playwright/test').Page} page
*/
async function testRegularTicks(page) {
const yTicks = await 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');
await expect(yTicks.nth(2)).toHaveText('2');
await expect(yTicks.nth(3)).toHaveText('4');
await expect(yTicks.nth(4)).toHaveText('6');
await expect(yTicks.nth(5)).toHaveText('8');
await expect(yTicks.nth(6)).toHaveText('10');
}
/**
* @param {import('@playwright/test').Page} page
*/
async function testLogTicks(page) {
const yTicks = await page.locator('.gl-plot-y-tick-label');
expect(await yTicks.count()).toBe(28);
await expect(yTicks.nth(0)).toHaveText('-2.98');
await expect(yTicks.nth(1)).toHaveText('-2.50');
await expect(yTicks.nth(2)).toHaveText('-2.00');
await expect(yTicks.nth(3)).toHaveText('-1.51');
await expect(yTicks.nth(4)).toHaveText('-1.20');
await expect(yTicks.nth(5)).toHaveText('-1.00');
await expect(yTicks.nth(6)).toHaveText('-0.80');
await expect(yTicks.nth(7)).toHaveText('-0.58');
await expect(yTicks.nth(8)).toHaveText('-0.40');
await expect(yTicks.nth(9)).toHaveText('-0.20');
await expect(yTicks.nth(10)).toHaveText('-0.00');
await expect(yTicks.nth(11)).toHaveText('0.20');
await expect(yTicks.nth(12)).toHaveText('0.40');
await expect(yTicks.nth(13)).toHaveText('0.58');
await expect(yTicks.nth(14)).toHaveText('0.80');
await expect(yTicks.nth(15)).toHaveText('1.00');
await expect(yTicks.nth(16)).toHaveText('1.20');
await expect(yTicks.nth(17)).toHaveText('1.51');
await expect(yTicks.nth(18)).toHaveText('2.00');
await expect(yTicks.nth(19)).toHaveText('2.50');
await expect(yTicks.nth(20)).toHaveText('2.98');
await expect(yTicks.nth(21)).toHaveText('3.50');
await expect(yTicks.nth(22)).toHaveText('4.00');
await expect(yTicks.nth(23)).toHaveText('4.50');
await expect(yTicks.nth(24)).toHaveText('5.31');
await expect(yTicks.nth(25)).toHaveText('7.00');
await expect(yTicks.nth(26)).toHaveText('8.00');
await expect(yTicks.nth(27)).toHaveText('9.00');
}
/**
* @param {import('@playwright/test').Page} 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();
}
/**
* @param {import('@playwright/test').Page} page
*/
async function enableLogMode(page) {
// turn on log mode
await page.locator('text=Y Axis Label Log mode Auto scale Padding >> input[type="checkbox"]').first().check();
}
/**
* @param {import('@playwright/test').Page} page
*/
async function disableLogMode(page) {
// turn off log mode
await page.locator('text=Y Axis Label Log mode Auto scale Padding >> input[type="checkbox"]').first().uncheck();
}
/**
* @param {import('@playwright/test').Page} page
*/
async function saveOverlayPlot(page) {
// save overlay plot
await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
await Promise.all([
page.locator('text=Save and Finish Editing').click(),
//Wait for Save Banner to appear
page.waitForSelector('.c-message-banner__message')
]);
//Wait until Save Banner is gone
await page.locator('.c-message-banner__close-button').click();
await page.waitForSelector('.c-message-banner__message', { state: 'detached' });
}
/**
* @param {import('@playwright/test').Page} page
*/
// FIXME: Remove this eslint exception once implemented
// eslint-disable-next-line no-unused-vars
async function testLogPlotPixels(page) {
const pixelsMatch = await page.evaluate(async () => {
// TODO get canvas pixels at a few locations to make sure they're the correct color, to test that the plot comes out as expected.
await new Promise((r) => setTimeout(r, 5 * 1000));
// These are some pixels that should be blue points in the log plot.
// If the plot changes shape to an unexpected shape, this will
// likely fail, which is what we want.
//
// I found these pixels by pausing playwright in debug mode at this
// point, and using similar code as below to output the pixel data, then
// I logged those pixels here.
const expectedBluePixels = [
// TODO these pixel sets only work with the first test, but not the second test.
// [60, 35],
// [121, 125],
// [156, 377],
// [264, 73],
// [372, 186],
// [576, 73],
// [659, 439],
// [675, 423]
[60, 35],
[120, 125],
[156, 375],
[264, 73],
[372, 185],
[575, 72],
[659, 437],
[675, 421]
];
// The first canvas in the DOM is the one that has the plot point
// icons (canvas 2d), which is the one we are testing. The second
// one in the DOM is the WebGL canvas with the line. (Why aren't
// they both WebGL?)
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
for (const pixel of expectedBluePixels) {
// XXX Possible optimization: call getImageData only once with
// area including all pixels to be tested.
const data = ctx.getImageData(pixel[0], pixel[1], 1, 1).data;
// #43b0ffff <-- openmct cyanish-blue with 100% opacity
// if (data[0] !== 0x43 || data[1] !== 0xb0 || data[2] !== 0xff || data[3] !== 0xff) {
if (data[0] === 0 && data[1] === 0 && data[2] === 0 && data[3] === 0) {
// If any pixel is empty, it means we didn't hit a plot point.
return false;
}
}
return true;
});
expect(pixelsMatch).toBe(true);
}

View File

@ -0,0 +1,101 @@
/*****************************************************************************
* 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 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('../../../fixtures');
const { expect } = require('@playwright/test');
test.describe('Telemetry Table', () => {
test('unpauses when paused by button and user changes bounds', async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/5113'
});
const bannerMessage = '.c-message-banner__message';
const createButton = 'button:has-text("Create")';
await page.goto('/', { waitUntil: 'networkidle' });
// Click create button
await page.locator(createButton).click();
await page.locator('li:has-text("Telemetry Table")').click();
// Click on My Items in Tree. Workaround for https://github.com/nasa/openmct/issues/5184
await page.click('form[name="mctForm"] a:has-text("My Items")');
await Promise.all([
page.waitForNavigation(),
page.locator('text=OK').click(),
// Wait for Save Banner to appear
page.waitForSelector(bannerMessage)
]);
// Save (exit edit mode)
await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(3).click();
await page.locator('text=Save and Finish Editing').click();
// Click create button
await page.locator(createButton).click();
// add Sine Wave Generator with defaults
await page.locator('li:has-text("Sine Wave Generator")').click();
// Click on My Items in Tree. Workaround for https://github.com/nasa/openmct/issues/5184
await page.click('form[name="mctForm"] a:has-text("My Items")');
await Promise.all([
page.waitForNavigation(),
page.locator('text=OK').click(),
// Wait for Save Banner to appear
page.waitForSelector(bannerMessage)
]);
// focus the Telemetry Table
await page.locator('text=Open MCT My Items >> span').nth(3).click();
await Promise.all([
page.waitForNavigation(),
page.locator('text=Unnamed Telemetry Table').first().click()
]);
// Click pause button
const pauseButton = await page.locator('button.c-button.icon-pause');
await pauseButton.click();
const tableWrapper = await page.locator('div.c-table-wrapper');
await expect(tableWrapper).toHaveClass(/is-paused/);
// Arbitrarily change end date to some time in the future
const endTimeInput = page.locator('input[type="text"].c-input--datetime').nth(1);
await endTimeInput.click();
let endDate = await endTimeInput.inputValue();
endDate = new Date(endDate);
endDate.setUTCDate(endDate.getUTCDate() + 1);
endDate = endDate.toISOString().replace(/T.*/, '');
await endTimeInput.fill('');
await endTimeInput.fill(endDate);
await page.keyboard.press('Enter');
await expect(tableWrapper).not.toHaveClass(/is-paused/);
});
});

View File

@ -0,0 +1,235 @@
/*****************************************************************************
* 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 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('../../../fixtures.js');
const { expect } = require('@playwright/test');
test.describe('Time conductor operations', () => {
test('validate start time does not exceeds end time', async ({ page }) => {
// Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
const year = new Date().getFullYear();
let startDate = 'xxxx-01-01 01:00:00.000Z';
startDate = year + startDate.substring(4);
let endDate = 'xxxx-01-01 02:00:00.000Z';
endDate = year + endDate.substring(4);
const startTimeLocator = page.locator('input[type="text"]').first();
const endTimeLocator = page.locator('input[type="text"]').nth(1);
// Click start time
await startTimeLocator.click();
// Click end time
await endTimeLocator.click();
await endTimeLocator.fill(endDate.toString());
await startTimeLocator.fill(startDate.toString());
// invalid start date
startDate = (year + 1) + startDate.substring(4);
await startTimeLocator.fill(startDate.toString());
await endTimeLocator.click();
const startDateValidityStatus = await startTimeLocator.evaluate((element) => element.checkValidity());
expect(startDateValidityStatus).not.toBeTruthy();
// fix to valid start date
startDate = (year - 1) + startDate.substring(4);
await startTimeLocator.fill(startDate.toString());
// invalid end date
endDate = (year - 2) + endDate.substring(4);
await endTimeLocator.fill(endDate.toString());
await startTimeLocator.click();
const endDateValidityStatus = await endTimeLocator.evaluate((element) => element.checkValidity());
expect(endDateValidityStatus).not.toBeTruthy();
});
});
// Testing instructions:
// Try to change the realtime offsets when in realtime (local clock) mode.
test.describe('Time conductor input fields real-time mode', () => {
test('validate input fields in real-time mode', async ({ page }) => {
const startOffset = {
secs: '23'
};
const endOffset = {
secs: '31'
};
// Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
// Switch to real-time mode
await setRealTimeMode(page);
// Set start time offset
await setStartOffset(page, startOffset);
// Verify time was updated on time offset button
await expect(page.locator('data-testid=conductor-start-offset-button')).toContainText('00:30:23');
// Set end time offset
await setEndOffset(page, endOffset);
// Verify time was updated on preceding time offset button
await expect(page.locator('data-testid=conductor-end-offset-button')).toContainText('00:00:31');
});
/**
* Verify that offsets and url params are preserved when switching
* between fixed timespan and real-time mode.
*/
test('preserve offsets and url params when switching between fixed and real-time mode', async ({ page }) => {
const startOffset = {
mins: '30',
secs: '23'
};
const endOffset = {
secs: '01'
};
// Convert offsets to milliseconds
const startDelta = (30 * 60 * 1000) + (23 * 1000);
const endDelta = (1 * 1000);
// Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
// Switch to real-time mode
await setRealTimeMode(page);
// Set start time offset
await setStartOffset(page, startOffset);
// Set end time offset
await setEndOffset(page, endOffset);
// Switch to fixed timespan mode
await setFixedTimeMode(page);
// Switch back to real-time mode
await setRealTimeMode(page);
// Verify updated start time offset persists after mode switch
await expect(page.locator('data-testid=conductor-start-offset-button')).toContainText('00:30:23');
// Verify updated end time offset persists after mode switch
await expect(page.locator('data-testid=conductor-end-offset-button')).toContainText('00:00:01');
// Verify url parameters persist after mode switch
await page.waitForNavigation();
expect(page.url()).toContain(`startDelta=${startDelta}`);
expect(page.url()).toContain(`endDelta=${endDelta}`);
});
});
/**
* @typedef {Object} OffsetValues
* @property {string | undefined} hours
* @property {string | undefined} mins
* @property {string | undefined} secs
*/
/**
* Set the values (hours, mins, secs) for the start time offset when in realtime mode
* @param {import('@playwright/test').Page} page
* @param {OffsetValues} offset
*/
async function setStartOffset(page, offset) {
const startOffsetButton = page.locator('data-testid=conductor-start-offset-button');
await setTimeConductorOffset(page, offset, startOffsetButton);
}
/**
* Set the values (hours, mins, secs) for the end time offset when in realtime mode
* @param {import('@playwright/test').Page} page
* @param {OffsetValues} offset
*/
async function setEndOffset(page, offset) {
const endOffsetButton = page.locator('data-testid=conductor-end-offset-button');
await setTimeConductorOffset(page, offset, endOffsetButton);
}
/**
* Set the time conductor to fixed timespan mode
* @param {import('@playwright/test').Page} page
*/
async function setFixedTimeMode(page) {
await setTimeConductorMode(page, true);
}
/**
* Set the time conductor to realtime mode
* @param {import('@playwright/test').Page} page
*/
async function setRealTimeMode(page) {
await setTimeConductorMode(page, false);
}
/**
* Set the values (hours, mins, secs) for the TimeConductor offsets when in realtime mode
* @param {import('@playwright/test').Page} page
* @param {OffsetValues} offset
* @param {import('@playwright/test').Locator} offsetButton
*/
async function setTimeConductorOffset(page, {hours, mins, secs}, offsetButton) {
await offsetButton.click();
if (hours) {
await page.fill('.pr-time-controls__hrs', hours);
}
if (mins) {
await page.fill('.pr-time-controls__mins', mins);
}
if (secs) {
await page.fill('.pr-time-controls__secs', secs);
}
// Click the check button
await page.locator('.icon-check').click();
}
/**
* Set the time conductor mode to either fixed timespan or realtime mode.
* @param {import('@playwright/test').Page} page
* @param {boolean} [isFixedTimespan=true] true for fixed timespan mode, false for realtime mode; default is true
*/
async function setTimeConductorMode(page, isFixedTimespan = true) {
// Click 'mode' button
await page.locator('.c-mode-button').click();
// Switch time conductor mode
if (isFixedTimespan) {
await page.locator('data-testid=conductor-modeOption-fixed').click();
} else {
await page.locator('data-testid=conductor-modeOption-realtime').click();
}
}

View File

@ -0,0 +1,22 @@
{
"cookies": [],
"origins": [
{
"origin": "http://localhost:8080",
"localStorage": [
{
"name": "tcHistory",
"value": "{\"utc\":[{\"start\":1652301954635,\"end\":1652303754635}]}"
},
{
"name": "mct",
"value": "{\"mine\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"f64bea3b-58a7-4586-8c05-8b651e5f0bfd\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"persisted\":1652303756008,\"modified\":1652303756007},\"f64bea3b-58a7-4586-8c05-8b651e5f0bfd\":{\"name\":\"Unnamed Condition Set\",\"type\":\"conditionSet\",\"identifier\":{\"key\":\"f64bea3b-58a7-4586-8c05-8b651e5f0bfd\",\"namespace\":\"\"},\"configuration\":{\"conditionTestData\":[],\"conditionCollection\":[{\"isDefault\":true,\"id\":\"73f2d9ae-d1f3-4561-b7fc-ecd5df557249\",\"configuration\":{\"name\":\"Default\",\"output\":\"Default\",\"trigger\":\"all\",\"criteria\":[]},\"summary\":\"Default condition\"}]},\"composition\":[],\"telemetry\":{},\"modified\":1652303755999,\"location\":\"mine\",\"persisted\":1652303756002}}"
},
{
"name": "mct-tree-expanded",
"value": "[]"
}
]
}
]
}

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, United States Government
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@ -33,7 +33,8 @@ comfortable running this test during a live mission?" Avoid creating or deleting
Make no assumptions about the order that elements appear in the DOM.
*/
const { test, expect } = require('@playwright/test');
const { test } = require('../fixtures.js');
const { expect } = require('@playwright/test');
test('Verify that the create button appears and that the Folder Domain Object is available for selection', async ({ page }) => {

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, United States Government
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@ -22,14 +22,14 @@
/*
Collection of Visual Tests set to run in a default context. The tests within this suite
are only meant to run against openmct's app.js started by `npm run start` within the
are only meant to run against openmct's app.js started by `npm run start` within the
`./e2e/playwright-visual.config.js` file.
These should only use functional expect statements to verify assumptions about the state
These should only use functional expect statements to verify assumptions about the state
in a test and not for functional verification of correctness. Visual tests are not supposed
to "fail" on assertions. Instead, they should be used to detect changes between builds or branches.
Note: Larger testsuite sizes are OK due to the setup time associated with these tests.
Note: Larger testsuite sizes are OK due to the setup time associated with these tests.
*/
const { test, expect } = require('@playwright/test');
@ -47,7 +47,10 @@ test.beforeEach(async ({ context }) => {
path: path.join(__dirname, '../../..', './node_modules/sinon/pkg/sinon.js')
});
await context.addInitScript(() => {
window.__clock = sinon.useFakeTimers(); //Set browser clock to UNIX Epoch
window.__clock = sinon.useFakeTimers({
now: 0,
shouldAdvanceTime: true
}); //Set browser clock to UNIX Epoch
});
});
@ -56,8 +59,7 @@ test('Visual - Root and About', async ({ page }) => {
await page.goto('/', { waitUntil: 'networkidle' });
// Verify that Create button is actionable
const createButtonLocator = page.locator('button:has-text("Create")');
await expect(createButtonLocator).toBeEnabled();
await expect(page.locator('button:has-text("Create")')).toBeEnabled();
// Take a snapshot of the Dashboard
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
@ -111,3 +113,96 @@ test('Visual - Default Condition Widget', async ({ page }) => {
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
await percySnapshot(page, 'Default Condition Widget');
});
test('Visual - Time Conductor start time is less than end time', async ({ page }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
const year = new Date().getFullYear();
let startDate = 'xxxx-01-01 01:00:00.000Z';
startDate = year + startDate.substring(4);
let endDate = 'xxxx-01-01 02:00:00.000Z';
endDate = year + endDate.substring(4);
await page.locator('input[type="text"]').nth(1).fill(endDate.toString());
await page.locator('input[type="text"]').first().fill(startDate.toString());
// verify no error msg
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
await percySnapshot(page, 'Default Time conductor');
startDate = (year + 1) + startDate.substring(4);
await page.locator('input[type="text"]').first().fill(startDate.toString());
await page.locator('input[type="text"]').nth(1).click();
// verify error msg for start time (unable to capture snapshot of popup)
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
await percySnapshot(page, 'Start time error');
startDate = (year - 1) + startDate.substring(4);
await page.locator('input[type="text"]').first().fill(startDate.toString());
endDate = (year - 2) + endDate.substring(4);
await page.locator('input[type="text"]').nth(1).fill(endDate.toString());
await page.locator('input[type="text"]').first().click();
// verify error msg for end time (unable to capture snapshot of popup)
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
await percySnapshot(page, 'End time error');
});
test('Visual - Sine Wave Generator Form', async ({ page }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
//Click the Create button
await page.click('button:has-text("Create")');
// Click text=Sine Wave Generator
await page.click('text=Sine Wave Generator');
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
await percySnapshot(page, 'Default Sine Wave Generator Form');
await page.locator('.field.control.l-input-sm input').first().click();
await page.locator('.field.control.l-input-sm input').first().fill('');
// Validate red x mark
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
await percySnapshot(page, 'removed amplitude property value');
});
test('Visual - Save Successful Banner', async ({ page }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
//Click the Create button
await page.click('button:has-text("Create")');
//NOTE Something other than example imagery
await page.click('text=Timer');
// Click text=OK
await page.click('text=OK');
await page.locator('.c-message-banner__message').hover({ trial: true });
await percySnapshot(page, 'Banner message shown');
//Wait until Save Banner is gone
await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
await percySnapshot(page, 'Banner message gone');
});
test('Visual - Display Layout Icon is correct', async ({ page }) => {
//Go to baseURL
await page.goto('/', { waitUntil: 'networkidle' });
//Click the Create button
await page.click('button:has-text("Create")');
//Hover on Display Layout option.
await page.locator('text=Display Layout').hover();
await percySnapshot(page, 'Display Layout Create Menu');
});

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, United States Government
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@ -20,36 +20,45 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
"./src/WatchIndicator",
"./src/DigestIndicator"
], function (
WatchIndicator,
DigestIndicator
) {
"use strict";
return {
name: "example/profiling",
definition: {
"extensions": {
"indicators": [
class EventMetadataProvider {
constructor() {
this.METADATA_BY_TYPE = {
'eventGenerator': {
values: [
{
"implementation": WatchIndicator,
"depends": [
"$interval",
"$rootScope"
]
key: "name",
name: "Name",
format: "string"
},
{
"implementation": DigestIndicator,
"depends": [
"$interval",
"$rootScope"
]
key: "utc",
name: "Time",
format: "utc",
hints: {
domain: 1
}
},
{
key: "message",
name: "Message",
format: "string"
}
]
}
}
};
});
};
}
supportsMetadata(domainObject) {
return Object.prototype.hasOwnProperty.call(this.METADATA_BY_TYPE, domainObject.type);
}
getMetadata(domainObject) {
return Object.assign(
{},
domainObject.telemetry,
this.METADATA_BY_TYPE[domainObject.type]
);
}
}
export default EventMetadataProvider;

View File

@ -0,0 +1,96 @@
/*****************************************************************************
* 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 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.
*****************************************************************************/
/**
* Module defining EventTelemetryProvider. Created by chacskaylo on 06/18/2015.
*/
import messages from './transcript.json';
class EventTelemetryProvider {
constructor() {
this.defaultSize = 25;
}
generateData(firstObservedTime, count, startTime, duration, name) {
const millisecondsSinceStart = startTime - firstObservedTime;
const utc = startTime + (count * duration);
const ind = count % messages.length;
const message = messages[ind] + " - [" + millisecondsSinceStart + "]";
return {
name,
utc,
message
};
}
supportsRequest(domainObject) {
return domainObject.type === 'eventGenerator';
}
supportsSubscribe(domainObject) {
return domainObject.type === 'eventGenerator';
}
subscribe(domainObject, callback) {
const duration = domainObject.telemetry.duration * 1000;
const firstObservedTime = Date.now();
let count = 0;
const interval = setInterval(() => {
const startTime = Date.now();
const datum = this.generateData(firstObservedTime, count, startTime, duration, domainObject.name);
count += 1;
callback(datum);
}, duration);
return function () {
clearInterval(interval);
};
}
request(domainObject, options) {
let start = options.start;
const end = Math.min(Date.now(), options.end); // no future values
const duration = domainObject.telemetry.duration * 1000;
const size = options.size ? options.size : this.defaultSize;
const data = [];
const firstObservedTime = options.start;
let count = 0;
if (options.strategy === 'latest' || options.size === 1) {
start = end;
}
while (start <= end && data.length < size) {
const startTime = options.start + count;
data.push(this.generateData(firstObservedTime, count, startTime, duration, domainObject.name));
start += duration;
count += 1;
}
return Promise.resolve(data);
}
}
export default EventTelemetryProvider;

View File

@ -1,80 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
"./src/EventTelemetryProvider"
], function (
EventTelemetryProvider
) {
"use strict";
return {
name: "example/eventGenerator",
definition: {
"name": "Event Message Generator",
"description": "For development use. Creates sample event message data that mimics a live data stream.",
"extensions": {
"components": [
{
"implementation": EventTelemetryProvider,
"type": "provider",
"provides": "telemetryService",
"depends": [
"$q",
"$timeout"
]
}
],
"types": [
{
"key": "eventGenerator",
"name": "Event Message Generator",
"cssClass": "icon-generator-events",
"description": "For development use. Creates sample event message data that mimics a live data stream.",
"priority": 10,
"features": "creation",
"model": {
"telemetry": {}
},
"telemetry": {
"source": "eventGenerator",
"domains": [
{
"key": "utc",
"name": "Timestamp",
"format": "utc"
}
],
"ranges": [
{
"key": "message",
"name": "Message",
"format": "string"
}
]
}
}
]
}
}
};
});

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, United States Government
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@ -19,28 +19,24 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import EventTelmetryProvider from './EventTelemetryProvider';
import EventMetadataProvider from './EventMetadataProvider';
define([
'./ExportTelemetryAsCSVAction'
], function (ExportTelemetryAsCSVAction) {
"use strict";
return {
name: "example/export",
definition: {
"name": "Example of using CSV Export",
"extensions": {
"actions": [
{
"key": "example.export",
"name": "Export Telemetry as CSV",
"implementation": ExportTelemetryAsCSVAction,
"category": "contextual",
"cssClass": "icon-download",
"depends": ["exportService"]
}
]
export default function EventGeneratorPlugin(options) {
return function install(openmct) {
openmct.types.addType("eventGenerator", {
name: "Event Message Generator",
description: "For development use. Creates sample event message data that mimics a live data stream.",
cssClass: "icon-generator-events",
creatable: true,
initialize: function (object) {
object.telemetry = {
duration: 5
};
}
}
});
openmct.telemetry.addProvider(new EventTelmetryProvider());
openmct.telemetry.addProvider(new EventMetadataProvider());
};
});
}

View File

@ -0,0 +1,76 @@
/*****************************************************************************
* 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 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 EventMessageGeneratorPlugin from './plugin.js';
import {
createOpenMct,
resetApplicationState
} from '../../src/utils/testing';
describe('the plugin', () => {
let openmct;
const mockDomainObject = {
identifier: {
namespace: '',
key: 'some-value'
},
telemetry: {
duration: 0
},
options: {},
type: 'eventGenerator'
};
beforeEach((done) => {
const options = {};
openmct = createOpenMct();
openmct.install(new EventMessageGeneratorPlugin(options));
openmct.on('start', done);
openmct.startHeadless();
});
afterEach(async () => {
await resetApplicationState(openmct);
});
describe('the plugin', () => {
it("supports subscription", (done) => {
const unsubscribe = openmct.telemetry.subscribe(mockDomainObject, (telemetry) => {
expect(telemetry).not.toEqual(null);
expect(telemetry.message).toContain('CC: Eagle, Houston');
expect(unsubscribe).not.toEqual(null);
unsubscribe();
done();
});
});
it("supports requests without start/end defined", async () => {
const telemetry = await openmct.telemetry.request(mockDomainObject);
expect(telemetry[0].message).toContain('CC: Eagle, Houston');
});
it("supports requests with arbitrary start time in the past", async () => {
mockDomainObject.options.start = 100000000000; // Mar 03 1973
const telemetry = await openmct.telemetry.request(mockDomainObject);
expect(telemetry[0].message).toContain('CC: Eagle, Houston');
});
});
});

View File

@ -1,62 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
/**
* Module defining EventTelemetry.
* Created by chacskaylo on 06/18/2015.
* Modified by shale on 06/23/2015.
*/
define(
['../data/transcript.json'],
function (messages) {
"use strict";
var firstObservedTime = Date.now();
function EventTelemetry(request, interval) {
var latestObservedTime = Date.now(),
count = Math.floor((latestObservedTime - firstObservedTime) / interval),
generatorData = {};
generatorData.getPointCount = function () {
return count;
};
generatorData.getDomainValue = function (i, domain) {
return i * interval
+ (domain !== 'delta' ? firstObservedTime : 0);
};
generatorData.getRangeValue = function (i, range) {
var domainDelta = this.getDomainValue(i) - firstObservedTime,
ind = i % messages.length;
return messages[ind] + " - [" + domainDelta.toString() + "]";
};
return generatorData;
}
return EventTelemetry;
}
);

View File

@ -1,118 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
/**
* Module defining EventTelemetryProvider. Created by chacskaylo on 06/18/2015.
*/
define(
["./EventTelemetry"],
function (EventTelemetry) {
"use strict";
/**
*
* @constructor
*/
function EventTelemetryProvider($q, $timeout) {
var subscriptions = [],
genInterval = 1000,
generating = false;
//
function matchesSource(request) {
return request.source === "eventGenerator";
}
// Used internally; this will be repacked by doPackage
function generateData(request) {
return {
key: request.key,
telemetry: new EventTelemetry(request, genInterval)
};
}
//
function doPackage(results) {
var packaged = {};
results.forEach(function (result) {
packaged[result.key] = result.telemetry;
});
// Format as expected (sources -> keys -> telemetry)
return { eventGenerator: packaged };
}
function requestTelemetry(requests) {
return $timeout(function () {
return doPackage(requests.filter(matchesSource).map(generateData));
}, 0);
}
function handleSubscriptions(timeout) {
subscriptions.forEach(function (subscription) {
var requests = subscription.requests;
subscription.callback(doPackage(
requests.filter(matchesSource).map(generateData)
));
});
}
function startGenerating() {
generating = true;
$timeout(function () {
handleSubscriptions();
if (generating && subscriptions.length > 0) {
startGenerating();
} else {
generating = false;
}
}, genInterval);
}
function subscribe(callback, requests) {
var subscription = {
callback: callback,
requests: requests
};
function unsubscribe() {
subscriptions = subscriptions.filter(function (s) {
return s !== subscription;
});
}
subscriptions.push(subscription);
if (!generating) {
startGenerating();
}
return unsubscribe;
}
return {
requestTelemetry: requestTelemetry,
subscribe: subscribe
};
}
return EventTelemetryProvider;
}
);

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, United States Government
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@ -19,16 +19,15 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
["../src/PersistenceFailureConstants"],
function (PersistenceFailureConstants) {
describe("Persistence failure constants", function () {
it("defines an overwrite key", function () {
expect(PersistenceFailureConstants.OVERWRITE_KEY)
.toEqual(jasmine.any(String));
});
import availableTags from './tags.json';
/**
* @returns {function} The plugin install function
*/
export default function exampleTagsPlugin() {
return function install(openmct) {
Object.keys(availableTags.tags).forEach(tagKey => {
const tagDefinition = availableTags.tags[tagKey];
openmct.annotation.defineTag(tagKey, tagDefinition);
});
}
);
};
}

View File

@ -0,0 +1,19 @@
{
"tags": {
"46a62ad1-bb86-4f88-9a17-2a029e12669d": {
"label": "Science",
"backgroundColor": "#cc0000",
"foregroundColor": "#ffffff"
},
"65f150ef-73b7-409a-b2e8-258cbd8b7323": {
"label": "Driving",
"backgroundColor": "#ffad32",
"foregroundColor": "#333333"
},
"f156b038-c605-46db-88a6-67cf2489a371": {
"label": "Drilling",
"backgroundColor": "#b0ac4e",
"foregroundColor": "#FFFFFF"
}
}
}

View File

@ -0,0 +1,207 @@
/*****************************************************************************
* 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 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 EventEmitter from 'EventEmitter';
import { v4 as uuid } from 'uuid';
import createExampleUser from './exampleUserCreator';
const STATUSES = [{
key: "NO_STATUS",
label: "Not set",
iconClass: "icon-question-mark",
iconClassPoll: "icon-status-poll-question-mark"
}, {
key: "GO",
label: "GO",
iconClass: "icon-check",
iconClassPoll: "icon-status-poll-question-mark",
statusClass: "s-status-ok",
statusBgColor: "#33cc33",
statusFgColor: "#000"
}, {
key: "MAYBE",
label: "MAYBE",
iconClass: "icon-alert-triangle",
iconClassPoll: "icon-status-poll-question-mark",
statusClass: "s-status-warning",
statusBgColor: "#ffb66c",
statusFgColor: "#000"
}, {
key: "NO_GO",
label: "NO GO",
iconClass: "icon-circle-slash",
iconClassPoll: "icon-status-poll-question-mark",
statusClass: "s-status-error",
statusBgColor: "#9900cc",
statusFgColor: "#fff"
}];
/**
* @implements {StatusUserProvider}
*/
export default class ExampleUserProvider extends EventEmitter {
constructor(openmct, {defaultStatusRole} = {defaultStatusRole: undefined}) {
super();
this.openmct = openmct;
this.user = undefined;
this.loggedIn = false;
this.autoLoginUser = undefined;
this.status = STATUSES[1];
this.pollQuestion = undefined;
this.defaultStatusRole = defaultStatusRole;
this.ExampleUser = createExampleUser(this.openmct.user.User);
this.loginPromise = undefined;
}
isLoggedIn() {
return this.loggedIn;
}
autoLogin(username) {
this.autoLoginUser = username;
}
getCurrentUser() {
if (!this.loginPromise) {
this.loginPromise = this._login().then(() => this.user);
}
return this.loginPromise;
}
canProvideStatusForRole() {
return Promise.resolve(true);
}
canSetPollQuestion() {
return Promise.resolve(true);
}
hasRole(roleId) {
if (!this.loggedIn) {
Promise.resolve(undefined);
}
return Promise.resolve(this.user.getRoles().includes(roleId));
}
getStatusRoleForCurrentUser() {
return Promise.resolve(this.defaultStatusRole);
}
getAllStatusRoles() {
return Promise.resolve([this.defaultStatusRole]);
}
getStatusForRole(role) {
return Promise.resolve(this.status);
}
async getDefaultStatusForRole(role) {
const allRoles = await this.getPossibleStatuses();
return allRoles?.[0];
}
setStatusForRole(role, status) {
this.status = status;
this.emit('statusChange', {
role,
status
});
return true;
}
getPollQuestion() {
return Promise.resolve({
question: 'Set "GO" if your position is ready for a boarding action on the Klingon cruiser',
timestamp: Date.now()
});
}
setPollQuestion(pollQuestion) {
this.pollQuestion = {
question: pollQuestion,
timestamp: Date.now()
};
this.emit("pollQuestionChange", this.pollQuestion);
return true;
}
getPossibleStatuses() {
return Promise.resolve(STATUSES);
}
_login() {
const id = uuid();
// for testing purposes, this will skip the form, this wouldn't be used in
// a normal authentication process
if (this.autoLoginUser) {
this.user = new this.ExampleUser(id, this.autoLoginUser, ['example-role']);
this.loggedIn = true;
return Promise.resolve();
}
const formStructure = {
title: "Login",
sections: [
{
rows: [
{
key: "username",
control: "textfield",
name: "Username",
pattern: "\\S+",
required: true,
cssClass: "l-input-lg",
value: ''
}
]
}
],
buttons: {
submit: {
label: 'Login'
}
}
};
return this.openmct.forms.showForm(formStructure).then(
(info) => {
this.user = new this.ExampleUser(id, info.username, ['example-role']);
this.loggedIn = true;
},
() => { // user canceled, setting a default username
this.user = new this.ExampleUser(id, 'Pat', ['example-role']);
this.loggedIn = true;
}
);
}
}
/**
* @typedef {import('@/api/user/StatusUserProvider').default} StatusUserProvider
*/

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, United States Government
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@ -19,13 +19,18 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
@import "../../../../platform/commonUI/general/res/sass/constants";
@import "../../../../platform/commonUI/general/res/sass/mobile/constants";
@import "../../../../platform/commonUI/general/res/sass/mobile/mixins";
@include phoneandtablet {
// Show the Create button
.create-button-holder {
display: block !important;
}
export default function createExampleUser(UserClass) {
return class ExampleUser extends UserClass {
constructor(id, name, roles) {
super(id, name);
this.roles = roles;
this.getRoles = this.getRoles.bind(this);
}
getRoles() {
return this.roles;
}
};
}

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, United States Government
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@ -20,25 +20,21 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
import ExampleUserProvider from './ExampleUserProvider';
], function (
export default function ExampleUserPlugin({autoLoginUser, defaultStatusRole} = {
autoLoginUser: 'guest',
defaultStatusRole: 'test-role'
}) {
return function install(openmct) {
const userProvider = new ExampleUserProvider(openmct, {
defaultStatusRole
});
) {
function SplashScreenManager($document) {
var splash;
$document = $document[0];
splash = $document.querySelectorAll('.l-splash-holder')[0];
if (!splash) {
return;
if (autoLoginUser !== undefined) {
userProvider.autoLogin(autoLoginUser);
}
splash.className += ' fadeout';
splash.addEventListener('transitionend', function () {
splash.parentNode.removeChild(splash);
});
}
return SplashScreenManager;
});
openmct.user.setProvider(userProvider);
};
}

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, United States Government
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@ -20,30 +20,31 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[
'../src/IdentityProvider'
],
function (IdentityProvider) {
import {
createOpenMct,
resetApplicationState
} from '../../src/utils/testing';
import ExampleUserProvider from './ExampleUserProvider';
describe("IdentityProvider", function () {
var mockQ, provider;
describe("The Example User Plugin", () => {
let openmct;
beforeEach(function () {
mockQ = jasmine.createSpyObj('$q', ['when']);
mockQ.when.and.callFake(function (v) {
return Promise.resolve(v);
});
beforeEach(() => {
openmct = createOpenMct();
});
provider = new IdentityProvider(mockQ);
});
afterEach(() => {
return resetApplicationState(openmct);
});
it("provides an undefined user", function () {
return provider.getUser().then(function (user) {
expect(user).toBe(undefined);
});
});
it('is not installed by default', () => {
expect(openmct.user.hasProvider()).toBeFalse();
});
it('can be installed', () => {
openmct.user.on('providerAdded', (provider) => {
expect(provider).toBeInstanceOf(ExampleUserProvider);
});
}
);
openmct.install(openmct.plugins.example.ExampleUser());
});
});

View File

@ -1,89 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([], function () {
'use strict';
/**
* An example of using the `exportService`; queries for telemetry
* and provides the results as a CSV file.
* @param {platform/exporters.ExportService} exportService the
* service which will handle the CSV export
* @param {ActionContext} context the action's context
* @constructor
* @memberof example/export
* @implements {Action}
*/
function ExportTelemetryAsCSVAction(exportService, context) {
this.exportService = exportService;
this.context = context;
}
ExportTelemetryAsCSVAction.prototype.perform = function () {
var context = this.context,
domainObject = context.domainObject,
telemetry = domainObject.getCapability("telemetry"),
metadata = telemetry.getMetadata(),
domains = metadata.domains,
ranges = metadata.ranges,
exportService = this.exportService;
function getName(domainOrRange) {
return domainOrRange.name;
}
telemetry.requestData({}).then(function (series) {
var headers = domains.map(getName).concat(ranges.map(getName)),
rows = [],
row,
i;
function copyDomainsToRow(telemetryRow, index) {
domains.forEach(function (domain) {
telemetryRow[domain.name] = series.getDomainValue(index, domain.key);
});
}
function copyRangesToRow(telemetryRow, index) {
ranges.forEach(function (range) {
telemetryRow[range.name] = series.getRangeValue(index, range.key);
});
}
for (i = 0; i < series.getPointCount(); i += 1) {
row = {};
copyDomainsToRow(row, i);
copyRangesToRow(row, i);
rows.push(row);
}
exportService.exportCSV(rows, { headers: headers });
});
};
ExportTelemetryAsCSVAction.appliesTo = function (context) {
return context.domainObject
&& context.domainObject.hasCapability("telemetry");
};
return ExportTelemetryAsCSVAction;
});

View File

@ -0,0 +1,83 @@
/*****************************************************************************
* 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 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.
*****************************************************************************/
export default function () {
return function install(openmct) {
openmct.install(openmct.plugins.FaultManagement());
openmct.faults.addProvider({
request(domainObject, options) {
const faults = JSON.parse(localStorage.getItem('faults'));
return Promise.resolve(faults.alarms);
},
subscribe(domainObject, callback) {
const faultsData = JSON.parse(localStorage.getItem('faults')).alarms;
function getRandomIndex(start, end) {
return Math.floor(start + (Math.random() * (end - start + 1)));
}
let id = setInterval(() => {
const index = getRandomIndex(0, faultsData.length - 1);
const randomFaultData = faultsData[index];
const randomFault = randomFaultData.fault;
randomFault.currentValueInfo.value = Math.random();
callback({
fault: randomFault,
type: 'alarms'
});
}, 300);
return () => {
clearInterval(id);
};
},
supportsRequest(domainObject) {
const faults = localStorage.getItem('faults');
return faults && domainObject.type === 'faultManagement';
},
supportsSubscribe(domainObject) {
const faults = localStorage.getItem('faults');
return faults && domainObject.type === 'faultManagement';
},
acknowledgeFault(fault, { comment = '' }) {
console.log('acknowledgeFault', fault);
console.log('comment', comment);
return Promise.resolve({
success: true
});
},
shelveFault(fault, shelveData) {
console.log('shelveFault', fault);
console.log('shelveData', shelveData);
return Promise.resolve({
success: true
});
}
});
};
}

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, United States Government
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@ -20,14 +20,28 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
'./legacyRegistry',
'./BundleRegistry'
], function (legacyRegistry, BundleRegistry) {
import {
createOpenMct,
resetApplicationState
} from '../../src/utils/testing';
describe("legacyRegistry", function () {
it("is a BundleRegistry", function () {
expect(legacyRegistry instanceof BundleRegistry).toBe(true);
});
describe("The Example Fault Source Plugin", () => {
let openmct;
beforeEach(() => {
openmct = createOpenMct();
});
afterEach(() => {
return resetApplicationState(openmct);
});
it('is not installed by default', () => {
expect(openmct.faults.provider).toBeUndefined();
});
it('can be installed', () => {
openmct.install(openmct.plugins.example.ExampleFaultSource());
expect(openmct.faults.provider).not.toBeUndefined();
});
});

View File

@ -1,53 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
"./src/ExampleFormController"
], function (
ExampleFormController
) {
"use strict";
return {
name: "example/forms",
definition: {
"name": "Declarative Forms example",
"sources": "src",
"extensions": {
"controllers": [
{
"key": "ExampleFormController",
"implementation": ExampleFormController,
"depends": [
"$scope"
]
}
],
"routes": [
{
"templateUrl": "templates/exampleForm.html"
}
]
}
}
};
});

View File

@ -1,42 +0,0 @@
<!--
Open MCT, Copyright (c) 2014-2021, 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.
-->
<div ng-controller="ExampleFormController">
<mct-toolbar structure="toolbar"
ng-model="state"
name="aToolbar"></mct-toolbar>
<mct-form structure="form"
ng-model="state"
class="validates"
name="aForm"></mct-form>
<ul>
<li>Dirty: {{aForm.$dirty}}</li>
<li>Valid: {{aForm.$valid}}</li>
</ul>
<pre>
<textarea>
{{state | json}}
</textarea>
</pre>
</div>

View File

@ -1,205 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define(
[],
function () {
"use strict";
function ExampleFormController($scope) {
$scope.state = {
};
$scope.toolbar = {
name: "An example toolbar.",
sections: [
{
description: "First section",
items: [
{
name: "X",
description: "X coordinate",
control: "textfield",
pattern: "^\\d+$",
disabled: true,
size: 2,
key: "x"
},
{
name: "Y",
description: "Y coordinate",
control: "textfield",
pattern: "^\\d+$",
size: 2,
key: "y"
},
{
name: "W",
description: "Cell width",
control: "textfield",
pattern: "^\\d+$",
size: 2,
key: "w"
},
{
name: "H",
description: "Cell height",
control: "textfield",
pattern: "^\\d+$",
size: 2,
key: "h"
}
]
},
{
description: "Second section",
items: [
{
control: "button",
csslass: "icon-save",
click: function () {
console.log("Save");
}
},
{
control: "button",
csslass: "icon-x",
description: "Button B",
click: function () {
console.log("Cancel");
}
},
{
control: "button",
csslass: "icon-trash",
description: "Button C",
disabled: true,
click: function () {
console.log("Delete");
}
}
]
},
{
items: [
{
control: "color",
key: "color"
}
]
}
]
};
$scope.form = {
name: "An example form.",
sections: [
{
name: "First section",
rows: [
{
name: "Check me",
control: "checkbox",
key: "checkMe"
},
{
name: "Enter your name",
required: true,
control: "textfield",
key: "yourName"
},
{
name: "Enter a number",
control: "textfield",
pattern: "^\\d+$",
key: "aNumber"
}
]
},
{
name: "Second section",
rows: [
{
name: "Pick a date",
required: true,
description: "Enter date in form YYYY-DDD",
control: "datetime",
key: "aDate"
},
{
name: "Choose something",
control: "select",
options: [
{
name: "Hats",
value: "hats"
},
{
name: "Bats",
value: "bats"
},
{
name: "Cats",
value: "cats"
},
{
name: "Mats",
value: "mats"
}
],
key: "aChoice"
},
{
name: "Choose something",
control: "select",
required: true,
options: [
{
name: "Hats",
value: "hats"
},
{
name: "Bats",
value: "bats"
},
{
name: "Cats",
value: "cats"
},
{
name: "Mats",
value: "mats"
}
],
key: "aRequiredChoice"
}
]
}
]
};
}
return ExampleFormController;
}
);

View File

@ -29,12 +29,12 @@ define([
}
},
{
key: "cos",
name: "Cosine",
unit: "deg",
formatString: '%0.2f',
key: "wavelengths",
name: "Wavelength",
unit: "nm",
format: 'string[]',
hints: {
domain: 3
range: 4
}
},
// Need to enable "LocalTimeSystem" plugin to make use of this
@ -64,6 +64,14 @@ define([
hints: {
range: 2
}
},
{
key: "intensities",
name: "Intensities",
format: 'number[]',
hints: {
range: 3
}
}
]
},

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, United States Government
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@ -35,8 +35,8 @@ define([
phase: 0
};
function GeneratorProvider() {
this.workerInterface = new WorkerInterface();
function GeneratorProvider(openmct) {
this.workerInterface = new WorkerInterface(openmct);
}
GeneratorProvider.prototype.canProvideTelemetry = function (domainObject) {

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, United States Government
* Open MCT, Copyright (c) 2014-2022, 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-2021, United States Government
* Open MCT, Copyright (c) 2014-2022, 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-2021, United States Government
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@ -21,20 +21,13 @@
*****************************************************************************/
define([
'raw-loader!./generatorWorker.js',
'uuid'
], function (
workerText,
uuid
{ v4: uuid }
) {
var workerBlob = new Blob(
[workerText],
{type: 'application/javascript'}
);
var workerUrl = URL.createObjectURL(workerBlob);
function WorkerInterface() {
function WorkerInterface(openmct) {
// eslint-disable-next-line no-undef
const workerUrl = `${openmct.getAssetPath()}${__OPENMCT_ROOT_RELATIVE__}generatorWorker.js`;
this.worker = new Worker(workerUrl);
this.worker.onmessage = this.onMessage.bind(this);
this.callbacks = {};

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, United States Government
* Open MCT, Copyright (c) 2014-2022, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@ -77,7 +77,8 @@
utc: nextStep,
yesterday: nextStep - 60 * 60 * 24 * 1000,
sin: sin(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness),
wavelength: wavelength(start, nextStep),
wavelengths: wavelengths(),
intensities: intensities(),
cos: cos(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness)
}
});
@ -126,7 +127,8 @@
utc: nextStep,
yesterday: nextStep - 60 * 60 * 24 * 1000,
sin: sin(nextStep, period, amplitude, offset, phase, randomness),
wavelength: wavelength(start, nextStep),
wavelengths: wavelengths(),
intensities: intensities(),
cos: cos(nextStep, period, amplitude, offset, phase, randomness)
});
}
@ -154,8 +156,28 @@
* Math.sin(phase + (timestamp / period / 1000 * Math.PI * 2)) + (amplitude * Math.random() * randomness) + offset;
}
function wavelength(start, nextStep) {
return (nextStep - start) / 10;
function wavelengths() {
let values = [];
while (values.length < 5) {
const randomValue = Math.random() * 100;
if (!values.includes(randomValue)) {
values.push(String(randomValue));
}
}
return values;
}
function intensities() {
let values = [];
while (values.length < 5) {
const randomValue = Math.random() * 10;
if (!values.includes(randomValue)) {
values.push(String(randomValue));
}
}
return values;
}
function sendError(error, message) {

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