mirror of
https://github.com/nasa/openmct.git
synced 2025-06-25 18:50:11 +00:00
Compare commits
2 Commits
operator-s
...
prototype-
Author | SHA1 | Date | |
---|---|---|---|
5013646e89 | |||
9925b91405 |
@ -1,199 +0,0 @@
|
||||
version: 2.1
|
||||
executors:
|
||||
pw-focal-development:
|
||||
docker:
|
||||
- image: mcr.microsoft.com/playwright:v1.21.1-focal
|
||||
environment:
|
||||
NODE_ENV: development # Needed to ensure 'dist' folder created and devDependencies installed
|
||||
parameters:
|
||||
BUST_CACHE:
|
||||
description: "Set this with the CircleCI UI Trigger Workflow button (boolean = true) to bust the cache!"
|
||||
default: false
|
||||
type: boolean
|
||||
commands:
|
||||
build_and_install:
|
||||
description: "All steps used to build and install. Will not work on node10"
|
||||
parameters:
|
||||
node-version:
|
||||
type: string
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache_cmd:
|
||||
node-version: << parameters.node-version >>
|
||||
- node/install:
|
||||
install-npm: true
|
||||
node-version: << parameters.node-version >>
|
||||
- run: npm install
|
||||
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:
|
||||
node-version:
|
||||
type: string
|
||||
steps:
|
||||
- when:
|
||||
condition:
|
||||
equal: [false, << pipeline.parameters.BUST_CACHE >> ]
|
||||
steps:
|
||||
- restore_cache:
|
||||
key: deps-{{ .Branch }}--<< parameters.node-version >>--{{ checksum "package.json" }}-{{ checksum ".circleci/config.yml" }}
|
||||
save_cache_cmd:
|
||||
description: "Custom command for saving cache."
|
||||
parameters:
|
||||
node-version:
|
||||
type: string
|
||||
steps:
|
||||
- save_cache:
|
||||
key: deps-{{ .Branch }}--<< parameters.node-version >>--{{ checksum "package.json" }}-{{ checksum ".circleci/config.yml" }}
|
||||
paths:
|
||||
- ~/.npm
|
||||
- node_modules
|
||||
generate_and_store_version_and_filesystem_artifacts:
|
||||
description: "Track important packages and files"
|
||||
steps:
|
||||
- run: |
|
||||
mkdir /tmp/artifacts
|
||||
printenv NODE_ENV >> /tmp/artifacts/NODE_ENV.txt
|
||||
npm -v >> /tmp/artifacts/npm-version.txt
|
||||
node -v >> /tmp/artifacts/node-version.txt
|
||||
ls -latR >> /tmp/artifacts/dir.txt
|
||||
- store_artifacts:
|
||||
path: /tmp/artifacts/
|
||||
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
|
||||
orbs:
|
||||
node: circleci/node@4.9.0
|
||||
browser-tools: circleci/browser-tools@1.3.0
|
||||
jobs:
|
||||
npm-audit:
|
||||
parameters:
|
||||
node-version:
|
||||
type: string
|
||||
executor: pw-focal-development
|
||||
steps:
|
||||
- build_and_install:
|
||||
node-version: <<parameters.node-version>>
|
||||
- run: npm audit --audit-level=low
|
||||
- generate_and_store_version_and_filesystem_artifacts
|
||||
lint:
|
||||
parameters:
|
||||
node-version:
|
||||
type: string
|
||||
executor: pw-focal-development
|
||||
steps:
|
||||
- build_and_install:
|
||||
node-version: <<parameters.node-version>>
|
||||
- run: npm run lint
|
||||
- generate_and_store_version_and_filesystem_artifacts
|
||||
unit-test:
|
||||
parameters:
|
||||
node-version:
|
||||
type: string
|
||||
browser:
|
||||
type: string
|
||||
executor: pw-focal-development
|
||||
steps:
|
||||
- build_and_install:
|
||||
node-version: <<parameters.node-version>>
|
||||
- when:
|
||||
condition:
|
||||
equal: [ "FirefoxESR", <<parameters.browser>> ]
|
||||
steps:
|
||||
- browser-tools/install-firefox:
|
||||
version: "91.7.1esr" #https://archive.mozilla.org/pub/firefox/releases/
|
||||
- when:
|
||||
condition:
|
||||
equal: [ "FirefoxHeadless", <<parameters.browser>> ]
|
||||
steps:
|
||||
- browser-tools/install-firefox
|
||||
- when:
|
||||
condition:
|
||||
equal: [ "ChromeHeadless", <<parameters.browser>> ]
|
||||
steps:
|
||||
- browser-tools/install-chrome:
|
||||
replace-existing: false
|
||||
- run: npm run test -- --browsers=<<parameters.browser>>
|
||||
- save_cache_cmd:
|
||||
node-version: <<parameters.node-version>>
|
||||
- store_test_results:
|
||||
path: dist/reports/tests/
|
||||
- store_artifacts:
|
||||
path: dist/reports/
|
||||
- generate_and_store_version_and_filesystem_artifacts
|
||||
e2e-test:
|
||||
parameters:
|
||||
node-version:
|
||||
type: string
|
||||
suite:
|
||||
type: string
|
||||
executor: pw-focal-development
|
||||
steps:
|
||||
- build_and_install:
|
||||
node-version: <<parameters.node-version>>
|
||||
- run: npx playwright install
|
||||
- run: npm run test:e2e:<<parameters.suite>>
|
||||
- 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:
|
||||
- lint:
|
||||
name: node16-lint
|
||||
node-version: lts/gallium
|
||||
- unit-test:
|
||||
name: node14-chrome
|
||||
node-version: lts/fermium
|
||||
browser: ChromeHeadless
|
||||
post-steps:
|
||||
- upload_code_covio
|
||||
- unit-test:
|
||||
name: node16-chrome
|
||||
node-version: lts/gallium
|
||||
browser: ChromeHeadless
|
||||
- unit-test:
|
||||
name: node18-chrome
|
||||
node-version: "18"
|
||||
browser: ChromeHeadless
|
||||
- e2e-test:
|
||||
name: e2e-ci
|
||||
node-version: lts/gallium
|
||||
suite: ci
|
||||
the-nightly: #These jobs do not run on PRs, but against master at night
|
||||
jobs:
|
||||
- unit-test:
|
||||
name: node16-firefoxESR-nightly
|
||||
node-version: lts/gallium
|
||||
browser: FirefoxESR
|
||||
- unit-test:
|
||||
name: node14-firefox-nightly
|
||||
node-version: lts/fermium
|
||||
browser: FirefoxHeadless
|
||||
- unit-test:
|
||||
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/gallium
|
||||
- e2e-test:
|
||||
name: e2e-full-nightly
|
||||
node-version: lts/gallium
|
||||
suite: full
|
||||
triggers:
|
||||
- schedule:
|
||||
cron: "0 0 * * *"
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- master
|
270
.eslintrc.js
270
.eslintrc.js
@ -1,270 +0,0 @@
|
||||
const LEGACY_FILES = ["example/**"];
|
||||
module.exports = {
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"jasmine": true,
|
||||
"amd": true
|
||||
},
|
||||
"globals": {
|
||||
"_": "readonly"
|
||||
},
|
||||
"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",
|
||||
"requireConfigFile": false,
|
||||
"allowImportExportEverywhere": true,
|
||||
"ecmaVersion": 2015,
|
||||
"ecmaFeatures": {
|
||||
"impliedStrict": true
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"you-dont-need-lodash-underscore/omit": "off",
|
||||
"you-dont-need-lodash-underscore/throttle": "off",
|
||||
"you-dont-need-lodash-underscore/flatten": "off",
|
||||
"no-bitwise": "error",
|
||||
"curly": "error",
|
||||
"eqeqeq": "error",
|
||||
"guard-for-in": "error",
|
||||
"no-extend-native": "error",
|
||||
"no-inner-declarations": "off",
|
||||
"no-use-before-define": ["error", "nofunc"],
|
||||
"no-caller": "error",
|
||||
"no-irregular-whitespace": "error",
|
||||
"no-new": "error",
|
||||
"no-shadow": "error",
|
||||
"no-undef": "error",
|
||||
"no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"vars": "all",
|
||||
"args": "none"
|
||||
}
|
||||
],
|
||||
"no-console": "off",
|
||||
"no-trailing-spaces": "error",
|
||||
"space-before-function-paren": [
|
||||
"error",
|
||||
{
|
||||
"anonymous": "always",
|
||||
"asyncArrow": "always",
|
||||
"named": "never"
|
||||
}
|
||||
],
|
||||
"array-bracket-spacing": "error",
|
||||
"space-in-parens": "error",
|
||||
"space-before-blocks": "error",
|
||||
"comma-dangle": "error",
|
||||
"eol-last": "error",
|
||||
"new-cap": [
|
||||
"error",
|
||||
{
|
||||
"capIsNew": false,
|
||||
"properties": false
|
||||
}
|
||||
],
|
||||
"dot-notation": "error",
|
||||
"indent": ["error", 4],
|
||||
|
||||
// https://eslint.org/docs/rules/no-case-declarations
|
||||
"no-case-declarations": "error",
|
||||
// https://eslint.org/docs/rules/max-classes-per-file
|
||||
"max-classes-per-file": ["error", 1],
|
||||
// https://eslint.org/docs/rules/no-eq-null
|
||||
"no-eq-null": "error",
|
||||
// https://eslint.org/docs/rules/no-eval
|
||||
"no-eval": "error",
|
||||
// https://eslint.org/docs/rules/no-floating-decimal
|
||||
"no-floating-decimal": "error",
|
||||
// https://eslint.org/docs/rules/no-implicit-globals
|
||||
"no-implicit-globals": "error",
|
||||
// https://eslint.org/docs/rules/no-implied-eval
|
||||
"no-implied-eval": "error",
|
||||
// https://eslint.org/docs/rules/no-lone-blocks
|
||||
"no-lone-blocks": "error",
|
||||
// https://eslint.org/docs/rules/no-loop-func
|
||||
"no-loop-func": "error",
|
||||
// https://eslint.org/docs/rules/no-new-func
|
||||
"no-new-func": "error",
|
||||
// https://eslint.org/docs/rules/no-new-wrappers
|
||||
"no-new-wrappers": "error",
|
||||
// https://eslint.org/docs/rules/no-octal-escape
|
||||
"no-octal-escape": "error",
|
||||
// https://eslint.org/docs/rules/no-proto
|
||||
"no-proto": "error",
|
||||
// https://eslint.org/docs/rules/no-return-await
|
||||
"no-return-await": "error",
|
||||
// https://eslint.org/docs/rules/no-script-url
|
||||
"no-script-url": "error",
|
||||
// https://eslint.org/docs/rules/no-self-compare
|
||||
"no-self-compare": "error",
|
||||
// https://eslint.org/docs/rules/no-sequences
|
||||
"no-sequences": "error",
|
||||
// https://eslint.org/docs/rules/no-unmodified-loop-condition
|
||||
"no-unmodified-loop-condition": "error",
|
||||
// https://eslint.org/docs/rules/no-useless-call
|
||||
"no-useless-call": "error",
|
||||
// https://eslint.org/docs/rules/wrap-iife
|
||||
"wrap-iife": "error",
|
||||
// https://eslint.org/docs/rules/no-nested-ternary
|
||||
"no-nested-ternary": "error",
|
||||
// https://eslint.org/docs/rules/switch-colon-spacing
|
||||
"switch-colon-spacing": "error",
|
||||
// https://eslint.org/docs/rules/no-useless-computed-key
|
||||
"no-useless-computed-key": "error",
|
||||
// https://eslint.org/docs/rules/rest-spread-spacing
|
||||
"rest-spread-spacing": ["error"],
|
||||
// https://eslint.org/docs/rules/no-var
|
||||
"no-var": "error",
|
||||
// https://eslint.org/docs/rules/one-var
|
||||
"one-var": ["error", "never"],
|
||||
// https://eslint.org/docs/rules/default-case-last
|
||||
"default-case-last": "error",
|
||||
// https://eslint.org/docs/rules/default-param-last
|
||||
"default-param-last": "error",
|
||||
// https://eslint.org/docs/rules/grouped-accessor-pairs
|
||||
"grouped-accessor-pairs": "error",
|
||||
// https://eslint.org/docs/rules/no-constructor-return
|
||||
"no-constructor-return": "error",
|
||||
// https://eslint.org/docs/rules/array-callback-return
|
||||
"array-callback-return": "error",
|
||||
// https://eslint.org/docs/rules/no-invalid-this
|
||||
"no-invalid-this": "error", // Believe this one actually surfaces some bugs
|
||||
// https://eslint.org/docs/rules/func-style
|
||||
"func-style": ["error", "declaration"],
|
||||
// https://eslint.org/docs/rules/no-unused-expressions
|
||||
"no-unused-expressions": "error",
|
||||
// https://eslint.org/docs/rules/no-useless-concat
|
||||
"no-useless-concat": "error",
|
||||
// https://eslint.org/docs/rules/radix
|
||||
"radix": "error",
|
||||
// https://eslint.org/docs/rules/require-await
|
||||
"require-await": "error",
|
||||
// https://eslint.org/docs/rules/no-alert
|
||||
"no-alert": "error",
|
||||
// https://eslint.org/docs/rules/no-useless-constructor
|
||||
"no-useless-constructor": "error",
|
||||
// https://eslint.org/docs/rules/no-duplicate-imports
|
||||
"no-duplicate-imports": "error",
|
||||
|
||||
// https://eslint.org/docs/rules/no-implicit-coercion
|
||||
"no-implicit-coercion": "error",
|
||||
//https://eslint.org/docs/rules/no-unneeded-ternary
|
||||
"no-unneeded-ternary": "error",
|
||||
// https://eslint.org/docs/rules/semi
|
||||
"semi": ["error", "always"],
|
||||
// https://eslint.org/docs/rules/no-multi-spaces
|
||||
"no-multi-spaces": "error",
|
||||
// https://eslint.org/docs/rules/key-spacing
|
||||
"key-spacing": ["error", {
|
||||
"afterColon": true
|
||||
}],
|
||||
// https://eslint.org/docs/rules/keyword-spacing
|
||||
"keyword-spacing": ["error", {
|
||||
"before": true,
|
||||
"after": true
|
||||
}],
|
||||
// https://eslint.org/docs/rules/comma-spacing
|
||||
// Also requires one line code fix
|
||||
"comma-spacing": ["error", {
|
||||
"after": true
|
||||
}],
|
||||
//https://eslint.org/docs/rules/no-whitespace-before-property
|
||||
"no-whitespace-before-property": "error",
|
||||
// https://eslint.org/docs/rules/object-curly-newline
|
||||
"object-curly-newline": ["error", {
|
||||
"consistent": true,
|
||||
"multiline": true
|
||||
}],
|
||||
// https://eslint.org/docs/rules/object-property-newline
|
||||
"object-property-newline": "error",
|
||||
// https://eslint.org/docs/rules/brace-style
|
||||
"brace-style": "error",
|
||||
// https://eslint.org/docs/rules/no-multiple-empty-lines
|
||||
"no-multiple-empty-lines": ["error", {"max": 1}],
|
||||
// https://eslint.org/docs/rules/operator-linebreak
|
||||
"operator-linebreak": ["error", "before", {"overrides": {"=": "after"}}],
|
||||
// https://eslint.org/docs/rules/padding-line-between-statements
|
||||
"padding-line-between-statements": ["error", {
|
||||
"blankLine": "always",
|
||||
"prev": "multiline-block-like",
|
||||
"next": "*"
|
||||
}, {
|
||||
"blankLine": "always",
|
||||
"prev": "*",
|
||||
"next": "return"
|
||||
}],
|
||||
// https://eslint.org/docs/rules/space-infix-ops
|
||||
"space-infix-ops": "error",
|
||||
// https://eslint.org/docs/rules/space-unary-ops
|
||||
"space-unary-ops": ["error", {
|
||||
"words": true,
|
||||
"nonwords": false
|
||||
}],
|
||||
// https://eslint.org/docs/rules/arrow-spacing
|
||||
"arrow-spacing": "error",
|
||||
// https://eslint.org/docs/rules/semi-spacing
|
||||
"semi-spacing": ["error", {
|
||||
"before": false,
|
||||
"after": true
|
||||
}],
|
||||
|
||||
"vue/html-indent": [
|
||||
"error",
|
||||
4,
|
||||
{
|
||||
"attribute": 1,
|
||||
"baseIndent": 0,
|
||||
"closeBracket": 0,
|
||||
"alignAttributesVertically": true,
|
||||
"ignores": []
|
||||
}
|
||||
],
|
||||
"vue/html-self-closing": ["error",
|
||||
{
|
||||
"html": {
|
||||
"void": "never",
|
||||
"normal": "never",
|
||||
"component": "always"
|
||||
},
|
||||
"svg": "always",
|
||||
"math": "always"
|
||||
}
|
||||
],
|
||||
"vue/max-attributes-per-line": ["error", {
|
||||
"singleline": 1,
|
||||
"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"
|
||||
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": LEGACY_FILES,
|
||||
"rules": {
|
||||
"no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
"vars": "all",
|
||||
"args": "none",
|
||||
"varsIgnorePattern": "controller"
|
||||
}
|
||||
],
|
||||
"no-nested-ternary": "off",
|
||||
"no-var": "off",
|
||||
"one-var": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
46
.github/ISSUE_TEMPLATE/bug_report.md
vendored
46
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,46 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: File a Bug !
|
||||
title: ''
|
||||
labels: type:bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--- Focus on user impact in the title. Use the Summary Field to -->
|
||||
<!--- describe the problem technically. -->
|
||||
|
||||
#### Summary
|
||||
<!--- A description of the issue encountered. When possible, a description -->
|
||||
<!--- of the impact of the issue. What use case does it impede?-->
|
||||
|
||||
#### Expected vs Current Behavior
|
||||
<!--- Tell us what should have happened -->
|
||||
|
||||
#### 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 -->
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
#### Environment
|
||||
<!--- If encountered on local machine, execute the following:
|
||||
<!--- npx envinfo --system --browsers --npmPackages --binaries --languages --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?
|
||||
|
||||
#### Additional Information
|
||||
<!--- Include any screenshots, gifs, or logs which will expedite triage -->
|
5
.github/ISSUE_TEMPLATE/config.yml
vendored
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,5 +0,0 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Discussions
|
||||
url: https://github.com/nasa/openmct/discussions
|
||||
about: Have a question about the project?
|
20
.github/ISSUE_TEMPLATE/enhancement-request.md
vendored
20
.github/ISSUE_TEMPLATE/enhancement-request.md
vendored
@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Enhancement request
|
||||
about: Suggest an enhancement or new improvement for this project
|
||||
title: ''
|
||||
labels: type:enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
11
.github/ISSUE_TEMPLATE/maintenance-type.md
vendored
11
.github/ISSUE_TEMPLATE/maintenance-type.md
vendored
@ -1,11 +0,0 @@
|
||||
---
|
||||
name: Maintenance
|
||||
about: Add, update or remove documentation, tests, or dependencies.
|
||||
title: ''
|
||||
labels: type:maintenance
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
#### Summary
|
||||
<!--- Generally describe the purpose of the change. -->
|
29
.github/PULL_REQUEST_TEMPLATE.md
vendored
29
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,29 +0,0 @@
|
||||
<!--- 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)?
|
||||
* [ ] Have you checked to ensure there aren't other open [Pull Requests](https://github.com/nasa/openmct/pulls) for the same update/change?
|
||||
* [ ] Is this change backwards compatible? For example, developers won't need to change how they are calling the API or how they've extended core plugins such as Tables or Plots.
|
||||
|
||||
### Author Checklist
|
||||
|
||||
* [ ] Changes address original issue?
|
||||
* [ ] Unit tests included and/or updated with changes?
|
||||
* [ ] Command line build passes?
|
||||
* [ ] Has this been smoke tested?
|
||||
* [ ] 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)
|
24
.github/dependabot.yml
vendored
24
.github/dependabot.yml
vendored
@ -1,24 +0,0 @@
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
open-pull-requests-limit: 10
|
||||
labels:
|
||||
- "type:maintenance"
|
||||
- "dependencies"
|
||||
- "pr:e2e"
|
||||
- "pr:daveit"
|
||||
- "pr:visual"
|
||||
- "pr:platform"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
labels:
|
||||
- "type:maintenance"
|
||||
- "dependencies"
|
||||
- "pr:daveit"
|
43
.github/workflows/codeql-analysis.yml
vendored
43
.github/workflows/codeql-analysis.yml
vendored
@ -1,43 +0,0 @@
|
||||
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '**/*Spec.js'
|
||||
- '**/*.md'
|
||||
- '**/*.txt'
|
||||
- '**/*.yml'
|
||||
- '**/*.yaml'
|
||||
- '**/*.spec.js'
|
||||
- '**/*.config.js'
|
||||
schedule:
|
||||
- cron: '28 21 * * 3'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: javascript
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
61
.github/workflows/e2e-pr.yml
vendored
61
.github/workflows/e2e-pr.yml
vendored
@ -1,61 +0,0 @@
|
||||
name: "e2e-pr"
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
types:
|
||||
- labeled
|
||||
- opened
|
||||
|
||||
jobs:
|
||||
e2e-full:
|
||||
if: ${{ github.event.label.name == 'pr:e2e' }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-latest
|
||||
- windows-latest
|
||||
steps:
|
||||
- name: Trigger Success
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: "nasa",
|
||||
repo: "openmct",
|
||||
body: 'Started e2e Run. Follow along: https://github.com/nasa/openmct/actions/runs/' + context.runId
|
||||
})
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
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@v3
|
||||
with:
|
||||
path: test-results
|
||||
- name: Test success
|
||||
if: ${{ success() }}
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
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@v6
|
||||
with:
|
||||
script: |
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: "nasa",
|
||||
repo: "openmct",
|
||||
body: 'Failure ❌ ! Build artifacts are here: https://github.com/nasa/openmct/actions/runs/' + context.runId
|
||||
})
|
25
.github/workflows/e2e-visual.yml
vendored
25
.github/workflows/e2e-visual.yml
vendored
@ -1,25 +0,0 @@
|
||||
name: "e2e-visual"
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
types:
|
||||
- labeled
|
||||
- opened
|
||||
schedule:
|
||||
- cron: '28 21 * * 1-5'
|
||||
|
||||
jobs:
|
||||
e2e-visual:
|
||||
if: ${{ github.event.label.name == 'pr:visual' }} || ${{ github.event.workflow_dispatch }} || ${{ github.event.schedule }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
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
|
||||
env:
|
||||
PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}
|
21
.github/workflows/e2e.yml
vendored
21
.github/workflows/e2e.yml
vendored
@ -1,21 +0,0 @@
|
||||
name: "e2e"
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Which branch do you want to test?' # Limited to branch for now
|
||||
required: false
|
||||
default: 'master'
|
||||
jobs:
|
||||
e2e:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.inputs.version }}
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16'
|
||||
- run: npm install
|
||||
- name: Run the e2e tests
|
||||
run: npm run test:e2e:ci
|
98
.github/workflows/lighthouse.yml
vendored
98
.github/workflows/lighthouse.yml
vendored
@ -1,98 +0,0 @@
|
||||
name: lighthouse
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Which branch do you want to test?' # Limited to branch for now
|
||||
required: false
|
||||
default: 'master'
|
||||
pull_request:
|
||||
types:
|
||||
- labeled
|
||||
jobs:
|
||||
lighthouse-pr:
|
||||
if: ${{ github.event.label.name == 'pr:lighthouse' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- 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 }}
|
||||
- name: Install Node 14
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
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
|
||||
|
33
.github/workflows/npm-prerelease.yml
vendored
33
.github/workflows/npm-prerelease.yml
vendored
@ -1,33 +0,0 @@
|
||||
# This workflow will run tests using node and then publish a package to npmjs when a prerelease is created
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
|
||||
|
||||
name: npm_prerelease
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [prereleased]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- run: npm install
|
||||
- run: npm test
|
||||
|
||||
publish-npm-prerelease:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
registry-url: https://registry.npmjs.org/
|
||||
- run: npm install
|
||||
- run: npm publish --access public --tag unstable
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
34
.github/workflows/pr-platform.yml
vendored
34
.github/workflows/pr-platform.yml
vendored
@ -1,34 +0,0 @@
|
||||
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
19
.github/workflows/prcop-config.json
vendored
@ -1,19 +0,0 @@
|
||||
{
|
||||
"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
26
.github/workflows/prcop.yml
vendored
@ -1,26 +0,0 @@
|
||||
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 }}
|
25
.gitignore
vendored
25
.gitignore
vendored
@ -3,13 +3,9 @@
|
||||
*.gzip
|
||||
*.tgz
|
||||
*.DS_Store
|
||||
*.swp
|
||||
|
||||
# Compiled CSS, unless directly added
|
||||
*.sass-cache
|
||||
*COMPILE.css
|
||||
*.css
|
||||
*.css.map
|
||||
|
||||
# Intellij project configuration files
|
||||
*.idea
|
||||
@ -19,7 +15,6 @@
|
||||
|
||||
# Build output
|
||||
target
|
||||
dist
|
||||
|
||||
# Mac OS X Finder
|
||||
.DS_Store
|
||||
@ -27,27 +22,9 @@ dist
|
||||
# Closed source libraries
|
||||
closed-lib
|
||||
|
||||
# Node, Bower dependencies
|
||||
# Node dependencies
|
||||
node_modules
|
||||
bower_components
|
||||
|
||||
# Protractor logs
|
||||
protractor/logs
|
||||
|
||||
# npm-debug log
|
||||
npm-debug.log
|
||||
|
||||
# karma reports
|
||||
report.*.json
|
||||
|
||||
# Lighthouse reports
|
||||
.lighthouseci
|
||||
|
||||
# e2e test artifacts
|
||||
test-results
|
||||
allure-results
|
||||
|
||||
package-lock.json
|
||||
|
||||
#codecov artifacts
|
||||
codecov
|
||||
|
27
.npmignore
27
.npmignore
@ -1,27 +0,0 @@
|
||||
# Ignore everything first (will not ignore special files like LICENSE.md,
|
||||
# README.md, and package.json)...
|
||||
/**/*
|
||||
|
||||
# ...but include these folders...
|
||||
!/dist/**/*
|
||||
!/src/**/*
|
||||
|
||||
# We might be able to remove this if it is not imported by any project directly.
|
||||
# https://github.com/nasa/openmct/issues/4992
|
||||
!/example/**/*
|
||||
|
||||
# We will remove this in https://github.com/nasa/openmct/issues/4922
|
||||
!/app.js
|
||||
|
||||
# ...except for these files in the above folders.
|
||||
/src/**/*Spec.js
|
||||
/src/**/test/
|
||||
# TODO move test utils into test/ folders
|
||||
/src/utils/testing.js
|
||||
|
||||
# Also include these special top-level files.
|
||||
!copyright-notice.js
|
||||
!copyright-notice.html
|
||||
!index.html
|
||||
!openmct.js
|
||||
!SECURITY.md
|
4
.npmrc
4
.npmrc
@ -1,4 +0,0 @@
|
||||
loglevel=warn
|
||||
|
||||
#Prevent folks from ignoring an important error when building from source
|
||||
engine-strict=true
|
272
CONTRIBUTING.md
272
CONTRIBUTING.md
@ -1,6 +1,6 @@
|
||||
# Contributing to Open MCT
|
||||
# Contributing to Open MCT Web
|
||||
|
||||
This document describes the process of contributing to Open MCT as well
|
||||
This document describes the process of contributing to Open MCT Web as well
|
||||
as the standards that will be applied when evaluating contributions.
|
||||
|
||||
Please be aware that additional agreements will be necessary before we can
|
||||
@ -10,7 +10,7 @@ accept changes from external contributors.
|
||||
|
||||
The short version:
|
||||
|
||||
1. Write your contribution or describe your idea in the form of an [GitHub issue](https://github.com/nasa/openmct/issues/new/choose) or [Starting a GitHub Discussion](https://github.com/nasa/openmct/discussions)
|
||||
1. Write your contribution.
|
||||
2. Make sure your contribution meets code, test, and commit message
|
||||
standards as described below.
|
||||
3. Submit a pull request from a topic branch back to `master`. Include a check
|
||||
@ -18,13 +18,12 @@ The short version:
|
||||
for review.)
|
||||
4. Respond to any discussion. When the reviewer decides it's ready, they
|
||||
will merge back `master` and fill out their own check list.
|
||||
5. If you are a first-time contributor, please see [this discussion](https://github.com/nasa/openmct/discussions/3821) for further information.
|
||||
|
||||
## Contribution Process
|
||||
|
||||
Open MCT uses git for software version control, and for branching and
|
||||
Open MCT Web uses git for software version control, and for branching and
|
||||
merging. The central repository is at
|
||||
https://github.com/nasa/openmct.git.
|
||||
https://github.com/nasa/openmctweb.git.
|
||||
|
||||
### Roles
|
||||
|
||||
@ -44,9 +43,9 @@ the check-in process. These roles are:
|
||||
|
||||
Three basic types of branches may be included in the above repository:
|
||||
|
||||
1. Master branch
|
||||
2. Topic branches
|
||||
3. Developer branches
|
||||
1. Master branch.
|
||||
2. Topic branches.
|
||||
3. Developer branches.
|
||||
|
||||
Branches which do not fit into the above categories may be created and used
|
||||
during the course of development for various reasons, such as large-scale
|
||||
@ -104,132 +103,116 @@ the name chosen could not be mistaken for a topic or master branch.
|
||||
### Merging
|
||||
|
||||
When development is complete on an issue, the first step toward merging it
|
||||
back into the master branch is to file a Pull Request (PR). The contributions
|
||||
back into the master branch is to file a Pull Request. The contributions
|
||||
should meet code, test, and commit message standards as described below,
|
||||
and the pull request should include a completed author checklist, also
|
||||
as described below. Pull requests may be assigned to specific team
|
||||
members when appropriate (e.g. to draw to a specific person's attention).
|
||||
members when appropriate (e.g. to draw to a specific person's attention.)
|
||||
|
||||
Code review should take place using discussion features within the pull
|
||||
request. When the reviewer is satisfied, they should add a comment to
|
||||
the pull request containing the reviewer checklist (from below) and complete
|
||||
the merge back to the master branch.
|
||||
|
||||
Additionally:
|
||||
* Every pull request must link to the issue that it addresses. Eg. “Addresses #1234” or “Closes #1234”. This is the responsibility of the pull request’s __author__. If no issue exists, [create one](https://github.com/nasa/openmct/issues/new/choose).
|
||||
* Every __author__ must include testing instructions. These instructions should identify the areas of code affected, and some minimal test steps. If addressing a bug, reproduction steps should be included, if they were not included in the original issue. If reproduction steps were included on the original issue, and are sufficient, refer to them.
|
||||
* A pull request that closes an issue should say so in the description. Including the text “Closes #1234” will cause the linked issue to be automatically closed when the pull request is merged. This is the responsibility of the pull request’s __author__.
|
||||
* When a pull request is merged, and the corresponding issue closed, the __reviewer__ must add the tag “unverified” to the original issue. This will indicate that although the issue is closed, it has not been tested yet.
|
||||
* Every PR must have two reviewers assigned, though only one approval is necessary for merge.
|
||||
* Changes to API require approval by a senior developer.
|
||||
* When creating a PR, it is the author's responsibility to apply any priority label from the issue to the PR as well. This helps with prioritization.
|
||||
|
||||
## Standards
|
||||
|
||||
Contributions to Open MCT are expected to meet the following standards.
|
||||
Contributions to Open MCT Web are expected to meet the following standards.
|
||||
In addition, reviewers should use general discretion before accepting
|
||||
changes.
|
||||
|
||||
### Code Standards
|
||||
|
||||
JavaScript sources in Open MCT must satisfy the ESLint rules defined in
|
||||
this repository. This is verified by the command line build.
|
||||
JavaScript sources in Open MCT Web must satisfy JSLint under its default
|
||||
settings. This is verified by the command line build.
|
||||
|
||||
#### Code Guidelines
|
||||
|
||||
The following guidelines are provided for anyone contributing source code to the Open MCT project:
|
||||
JavaScript sources in Open MCT Web should:
|
||||
|
||||
1. Write clean code. Here’s a good summary - https://github.com/ryanmcdermott/clean-code-javascript.
|
||||
1. Include JSDoc for any exposed API (e.g. public methods, classes).
|
||||
1. Include non-JSDoc comments as-needed for explaining private variables,
|
||||
methods, or algorithms when they are non-obvious. Otherwise code
|
||||
should be self-documenting.
|
||||
1. Classes and Vue components should use camel case, first letter capitalized
|
||||
(e.g. SomeClassName).
|
||||
1. Methods, variables, fields, events, and function names should use camelCase,
|
||||
first letter lower-case (e.g. someVariableName).
|
||||
1. Source files that export functions should use camelCase, first letter lower-case (eg. testTools.js)
|
||||
1. Constants (variables or fields which are meant to be declared and
|
||||
initialized statically, and never changed) should use only capital
|
||||
letters, with underscores between words (e.g. SOME_CONSTANT). They should always be declared as `const`s
|
||||
1. File names should be the name of the exported class, plus a .js extension
|
||||
(e.g. SomeClassName.js).
|
||||
1. Avoid anonymous functions, except when functions are short (one or two lines)
|
||||
and their inclusion makes sense within the flow of the code
|
||||
(e.g. as arguments to a forEach call). Anonymous functions should always be arrow functions.
|
||||
1. Named functions are preferred over functions assigned to variables.
|
||||
eg.
|
||||
```JavaScript
|
||||
function renameObject(object, newName) {
|
||||
Object.name = newName;
|
||||
}
|
||||
```
|
||||
is preferable to
|
||||
```JavaScript
|
||||
const rename = (object, newName) => {
|
||||
Object.name = newName;
|
||||
}
|
||||
```
|
||||
1. Avoid deep nesting (especially of functions), except where necessary
|
||||
(e.g. due to closure scope).
|
||||
1. End with a single new-line character.
|
||||
1. Always use ES6 `Class`es and inheritence rather than the pre-ES6 prototypal
|
||||
pattern.
|
||||
1. Within a given function's scope, do not mix declarations and imperative
|
||||
code, and present these in the following order:
|
||||
* First, variable declarations and initialization.
|
||||
* Secondly, imperative statements.
|
||||
* Finally, the returned value. A single return statement at the end of the function should be used, except where an early return would improve code clarity.
|
||||
1. Avoid the use of "magic" values.
|
||||
eg.
|
||||
```JavaScript
|
||||
const UNAUTHORIZED = 401;
|
||||
if (responseCode === UNAUTHORIZED)
|
||||
```
|
||||
is preferable to
|
||||
```JavaScript
|
||||
if (responseCode === 401)
|
||||
```
|
||||
1. Use the ternary operator only for simple cases such as variable assignment. Nested ternaries should be avoided in all cases.
|
||||
1. Test specs should reside alongside the source code they test, not in a separate directory.
|
||||
1. Organize code by feature, not by type.
|
||||
eg.
|
||||
```
|
||||
- telemetryTable
|
||||
- row
|
||||
TableRow.js
|
||||
TableRowCollection.js
|
||||
TableRow.vue
|
||||
- column
|
||||
TableColumn.js
|
||||
TableColumn.vue
|
||||
plugin.js
|
||||
pluginSpec.js
|
||||
```
|
||||
is preferable to
|
||||
```
|
||||
- telemetryTable
|
||||
- components
|
||||
TableRow.vue
|
||||
TableColumn.vue
|
||||
- collections
|
||||
TableRowCollection.js
|
||||
TableColumn.js
|
||||
TableRow.js
|
||||
plugin.js
|
||||
pluginSpec.js
|
||||
```
|
||||
Deviations from Open MCT code style guidelines require two-party agreement,
|
||||
* Use four spaces for indentation. Tabs should not be used.
|
||||
* Include JSDoc for any exposed API (e.g. public methods, constructors.)
|
||||
* Include non-JSDoc comments as-needed for explaining private variables,
|
||||
methods, or algorithms when they are non-obvious.
|
||||
* Define one public class per script, expressed as a constructor function
|
||||
returned from an AMD-style module.
|
||||
* Follow “Java-like” naming conventions. These includes:
|
||||
* Classes should use camel case, first letter capitalized
|
||||
(e.g. SomeClassName.)
|
||||
* Methods, variables, fields, and function names should use camel case,
|
||||
first letter lower-case (e.g. someVariableName.) Constants
|
||||
(variables or fields which are meant to be declared and initialized
|
||||
statically, and never changed) should use only capital letters, with
|
||||
underscores between words (e.g. SOME_CONSTANT.)
|
||||
* File name should be the name of the exported class, plus a .js extension
|
||||
(e.g. SomeClassName.js)
|
||||
* Avoid anonymous functions, except when functions are short (a few lines)
|
||||
and/or their inclusion makes sense within the flow of the code
|
||||
(e.g. as arguments to a forEach call.)
|
||||
* Avoid deep nesting (especially of functions), except where necessary
|
||||
(e.g. due to closure scope.)
|
||||
* End with a single new-line character.
|
||||
* Expose public methods by declaring them on the class's prototype.
|
||||
* Within a given function's scope, do not mix declarations and imperative
|
||||
code, and present these in the following order:
|
||||
* First, variable declarations and initialization.
|
||||
* Second, function declarations.
|
||||
* Third, imperative statements.
|
||||
* Finally, the returned value.
|
||||
|
||||
Deviations from Open MCT Web code style guidelines require two-party agreement,
|
||||
typically from the author of the change and its reviewer.
|
||||
|
||||
#### Code Example
|
||||
|
||||
```js
|
||||
/*global define*/
|
||||
|
||||
/**
|
||||
* Bundles should declare themselves as namespaces in whichever source
|
||||
* file is most like the "main point of entry" to the bundle.
|
||||
* @namespace some/bundle
|
||||
*/
|
||||
define(
|
||||
['./OtherClass'],
|
||||
function (OtherClass) {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* A summary of how to use this class goes here.
|
||||
*
|
||||
* @constructor
|
||||
* @memberof some/bundle
|
||||
*/
|
||||
function ExampleClass() {
|
||||
}
|
||||
|
||||
// Methods which are not intended for external use should
|
||||
// not have JSDoc (or should be marked @private)
|
||||
ExampleClass.prototype.privateMethod = function () {
|
||||
};
|
||||
|
||||
/**
|
||||
* A summary of this method goes here.
|
||||
* @param {number} n a parameter
|
||||
* @returns {number} a return value
|
||||
*/
|
||||
ExampleClass.prototype.publicMethod = function (n) {
|
||||
return n * 2;
|
||||
}
|
||||
|
||||
return ExampleClass;
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Test Standards
|
||||
|
||||
Automated testing shall occur whenever changes are merged into the main
|
||||
development branch and must be confirmed alongside any pull request.
|
||||
|
||||
Automated tests are tests which exercise plugins, API, and utility classes.
|
||||
Tests are subject to code review along with the actual implementation, to
|
||||
ensure that tests are applicable and useful.
|
||||
Automated tests are typically unit tests which exercise individual software
|
||||
components. Tests are subject to code review along with the actual
|
||||
implementation, to ensure that tests are applicable and useful.
|
||||
|
||||
Examples of useful tests:
|
||||
* Tests which replicate bugs (or their root causes) to verify their
|
||||
@ -239,26 +222,8 @@ Examples of useful tests:
|
||||
* Tests which verify expected interactions with other components in the
|
||||
system.
|
||||
|
||||
#### Guidelines
|
||||
* 100% statement coverage is achievable and desirable.
|
||||
* Do blackbox testing. Test external behaviors, not internal details. Write tests that describe what your plugin is supposed to do. How it does this doesn't matter, so don't test it.
|
||||
* Unit test specs for plugins should be defined at the plugin level. Start with one test spec per plugin named pluginSpec.js, and as this test spec grows too big, break it up into multiple test specs that logically group related tests.
|
||||
* Unit tests for API or for utility functions and classes may be defined at a per-source file level.
|
||||
* Wherever possible only use and mock public API, builtin functions, and UI in your test specs. Do not directly invoke any private functions. ie. only call or mock functions and objects exposed by openmct.* (eg. openmct.telemetry, openmct.objectView, etc.), and builtin browser functions (fetch, requestAnimationFrame, setTimeout, etc.).
|
||||
* Where builtin functions have been mocked, be sure to clear them between tests.
|
||||
* Test at an appropriate level of isolation. Eg.
|
||||
* If you’re testing a view, you do not need to test the whole application UI, you can just fetch the view provider using the public API and render the view into an element that you have created.
|
||||
* You do not need to test that the view switcher works, there should be separate tests for that.
|
||||
* You do not need to test that telemetry providers work, you can mock openmct.telemetry.request() to feed test data to the view.
|
||||
* Use your best judgement when deciding on appropriate scope.
|
||||
* Automated tests for plugins should start by actually installing the plugin being tested, and then test that installing the plugin adds the desired features and behavior to Open MCT, observing the above rules.
|
||||
* All variables used in a test spec, including any instances of the Open MCT API should be declared inside of an appropriate block scope (not at the root level of the source file), and should be initialized in the relevant beforeEach block. `beforeEach` is preferable to `beforeAll` to avoid leaking of state between tests.
|
||||
* A `afterEach` or `afterAll` should be used to do any clean up necessary to prevent leakage of state between test specs. This can happen when functions on `window` are wrapped, or when the URL is changed. [A convenience function](https://github.com/nasa/openmct/blob/master/src/utils/testing.js#L59) is provided for resetting the URL and clearing builtin spies between tests.
|
||||
* If writing unit tests for legacy Angular code be sure to follow [best practices in order to avoid memory leaks](https://www.thecodecampus.de/blog/avoid-memory-leaks-angularjs-unit-tests/).
|
||||
|
||||
#### Examples
|
||||
* [Example of an automated test spec for an object view plugin](https://github.com/nasa/openmct/blob/master/src/plugins/telemetryTable/pluginSpec.js)
|
||||
* [Example of an automated test spec for API](https://github.com/nasa/openmct/blob/master/src/api/time/TimeAPISpec.js)
|
||||
During automated testing, code coverage metrics will be reported. Line
|
||||
coverage must remain at or above 80%.
|
||||
|
||||
### Commit Message Standards
|
||||
|
||||
@ -269,7 +234,7 @@ Commit messages should:
|
||||
 line of white space.
|
||||
* Contain a short (usually one word) reference to the feature or subsystem
|
||||
the commit effects, in square brackets, at the start of the subject line
|
||||
(e.g. `[Documentation] Draft of check-in process`).
|
||||
(e.g. `[Documentation] Draft of check-in process`)
|
||||
* Contain a reference to a relevant issue number in the body of the commit.
|
||||
* This is important for traceability; while branch names also provide this,
|
||||
you cannot tell from looking at a commit what branch it was authored on.
|
||||
@ -285,9 +250,9 @@ Commit messages should:
|
||||
Commit messages should not:
|
||||
|
||||
* Exceed 54 characters in length on the subject line.
|
||||
* Exceed 72 characters in length in the body of the commit,
|
||||
* Exceed 72 characters in length in the body of the commit.
|
||||
* Except where necessary to maintain the structure of machine-readable or
|
||||
machine-generated text (e.g. error messages).
|
||||
machine-generated text (e.g. error messages)
|
||||
|
||||
See [Contributing to a Project](http://git-scm.com/book/ch5-2.html) from
|
||||
Pro Git by Shawn Chacon and Ben Straub for a bit of the rationale behind
|
||||
@ -295,37 +260,42 @@ these standards.
|
||||
|
||||
## Issue Reporting
|
||||
|
||||
Issues are tracked at https://github.com/nasa/openmct/issues.
|
||||
Issues are tracked at https://github.com/nasa/openmctweb/issues
|
||||
|
||||
Issues should include:
|
||||
|
||||
* A short description of the issue encountered.
|
||||
* A longer-form description of the issue encountered. When possible, steps to
|
||||
reproduce the issue.
|
||||
* When possible, a description of the impact of the issue. What use case does
|
||||
it impede?
|
||||
* An assessment of the severity of the issue.
|
||||
|
||||
Issue severity is categorized as follows (in ascending order):
|
||||
|
||||
* _Trivial_: Minimal impact on the usefulness and functionality of the software; a "nice-to-have." Visual impact without functional impact,
|
||||
* _Medium_: Some impairment of use, but simple workarounds exist
|
||||
* _Critical_: Significant loss of functionality or impairment of use. Display of telemetry data is not affected though.
|
||||
* _Blocker_: Major functionality is impaired or lost, threatening mission success. Display of telemetry data is impaired or blocked by the bug, which could lead to loss of situational awareness.
|
||||
* _Trivial_: Minimal impact on the usefulness and functionality of the
|
||||
software; a "nice-to-have."
|
||||
* _(Unspecified)_: Major loss of functionality or impairment of use.
|
||||
* _Critical_: Large-scale loss of functionality or impairment of use,
|
||||
such that remaining utility becomes marginal.
|
||||
* _Blocker_: Harmful or otherwise unacceptable behavior. Must fix.
|
||||
|
||||
## Check Lists
|
||||
|
||||
The following check lists should be completed and attached to pull requests
|
||||
when they are filed (author checklist) and when they are merged (reviewer
|
||||
checklist).
|
||||
checklist.)
|
||||
|
||||
### Author Checklist
|
||||
|
||||
[Within PR Template](.github/PULL_REQUEST_TEMPLATE.md)
|
||||
1. Changes address original issue?
|
||||
2. Unit tests included and/or updated with changes?
|
||||
3. Command line build passes?
|
||||
4. Expect to pass code review?
|
||||
|
||||
### Reviewer Checklist
|
||||
|
||||
* [ ] Changes appear to address issue?
|
||||
* [ ] Changes appear not to be breaking changes?
|
||||
* [ ] Appropriate unit tests included?
|
||||
* [ ] Code style and in-line documentation are appropriate?
|
||||
* [ ] Commit messages meet standards?
|
||||
* [ ] Has associated issue been labelled `unverified`? (only applicable if this PR closes the issue)
|
||||
* [ ] Has associated issue been labelled `bug`? (only applicable if this PR is for a bug fix)
|
||||
* [ ] List of Acceptance Tests Performed.
|
||||
|
||||
Write out a small list of tests performed with just enough detail for another developer on the team
|
||||
to execute.
|
||||
|
||||
i.e. ```When Clicking on Add button, new `object` appears in dropdown.```
|
||||
1. Changes appear to address issue?
|
||||
2. Appropriate unit tests included?
|
||||
3. Code style and in-line documentation are appropriate?
|
||||
4. Commit messages meet standards?
|
||||
|
@ -1,7 +0,0 @@
|
||||
# Open MCT License
|
||||
|
||||
Open MCT, Copyright (c) 2014-2022, United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All rights reserved.
|
||||
|
||||
Open MCT 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.
|
417
LICENSES.md
Normal file
417
LICENSES.md
Normal file
@ -0,0 +1,417 @@
|
||||
# Open MCT Web Licenses
|
||||
|
||||
Open MCT Web, Copyright (c) 2014-2015, United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All rights reserved.
|
||||
|
||||
Open MCT Web 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 Web includes source code licensed under additional open source licenses as follows.
|
||||
|
||||
## Software Components Licenses
|
||||
|
||||
This software includes components released under the following licenses:
|
||||
|
||||
---
|
||||
|
||||
### SuperSocket
|
||||
|
||||
#### Info
|
||||
|
||||
* Link: https://supersocket.codeplex.com/
|
||||
|
||||
* Version: 0.9.0.2
|
||||
|
||||
* Author: Kerry Jiang
|
||||
|
||||
* Description: Supports SuperWebSocket
|
||||
|
||||
#### License
|
||||
|
||||
Copyright 2012 Kerry Jiang (kerry-jiang@hotmail.com)
|
||||
|
||||
SuperSocket 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.
|
||||
|
||||
---
|
||||
|
||||
### SuperWebSocket
|
||||
|
||||
#### Info
|
||||
|
||||
* Link: https://superswebocket.codeplex.com/
|
||||
|
||||
* Version: 0.9.0.2
|
||||
|
||||
* Author: Kerry Jiang
|
||||
|
||||
* Description: WebSocket implementation for client-server communication
|
||||
|
||||
#### License
|
||||
|
||||
Copyright 2010-2013 Kerry Jiang (kerry-jiang@hotmail.com)
|
||||
|
||||
SuperWebSocket 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.
|
||||
|
||||
|
||||
---
|
||||
|
||||
### log4net
|
||||
|
||||
#### Info
|
||||
|
||||
* Link: http://logging.apache.org/log4net/
|
||||
|
||||
* Version: 1.2.13
|
||||
|
||||
* Author: Apache Software Foundation
|
||||
|
||||
* Description: Logging.
|
||||
|
||||
#### License
|
||||
|
||||
Copyright © 2004-2015 Apache Software Foundation.
|
||||
|
||||
log4net 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.
|
||||
|
||||
---
|
||||
|
||||
### Blanket.js
|
||||
|
||||
#### Info
|
||||
|
||||
* Link: http://blanketjs.org/
|
||||
|
||||
* Version: 1.1.5
|
||||
|
||||
* Author: Alex Seville
|
||||
|
||||
* Description: Code coverage measurement and reporting
|
||||
|
||||
#### License
|
||||
|
||||
Copyright (c) 2013 Alex Seville
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
### Jasmine
|
||||
|
||||
#### Info
|
||||
|
||||
* Link: http://jasmine.github.io/
|
||||
|
||||
* Version: 1.3.1
|
||||
|
||||
* Author: Pivotal Labs
|
||||
|
||||
* Description: Unit testing
|
||||
|
||||
#### License
|
||||
|
||||
Copyright (c) 2008-2011 Pivotal Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
### RequireJS
|
||||
|
||||
#### Info
|
||||
|
||||
* Link: http://requirejs.org/
|
||||
|
||||
* Version: 2.1.9
|
||||
|
||||
* Author: The Dojo Foundation
|
||||
|
||||
* Description: Script loader
|
||||
|
||||
#### License
|
||||
|
||||
Copyright (c) 2010-2015, The Dojo Foundation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
### AngularJS
|
||||
|
||||
#### Info
|
||||
|
||||
* Link: http://angularjs.org/
|
||||
|
||||
* Version: 1.2.26
|
||||
|
||||
* Author: Google
|
||||
|
||||
* Description: Client-side web application framework
|
||||
|
||||
#### License
|
||||
|
||||
Copyright (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
### Angular-Route
|
||||
|
||||
#### Info
|
||||
|
||||
* Link: http://angularjs.org/
|
||||
|
||||
* Version: 1.2.26
|
||||
|
||||
* Author: Google
|
||||
|
||||
* Description: Client-side view routing
|
||||
|
||||
#### License
|
||||
|
||||
Copyright (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
### ES6-Promise
|
||||
|
||||
#### Info
|
||||
|
||||
* Link: https://github.com/jakearchibald/es6-promise
|
||||
|
||||
* Version: 2.0.0
|
||||
|
||||
* Authors: Yehuda Katz, Tom Dale, Stefan Penner and contributors
|
||||
|
||||
* Description: Promise polyfill for pre-ECMAScript 6 browsers
|
||||
|
||||
#### License
|
||||
|
||||
Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
### screenfull.js
|
||||
|
||||
#### Info
|
||||
|
||||
* Link: https://github.com/sindresorhus/screenfull.js/
|
||||
|
||||
* Version: 1.2.0
|
||||
|
||||
* Author: Sindre Sorhus
|
||||
|
||||
* Description: Wrapper for cross-browser usage of fullscreen API
|
||||
|
||||
#### License
|
||||
|
||||
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
### Math.uuid.js
|
||||
|
||||
#### Info
|
||||
|
||||
* Link: https://github.com/broofa/node-uuid
|
||||
|
||||
* Version: 1.4
|
||||
|
||||
* Author: Robert Kieffer
|
||||
|
||||
* Description: Unique identifer generation (code adapted.)
|
||||
|
||||
#### License
|
||||
|
||||
Copyright (c) 2010 Robert Kieffer
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
### Modernizr
|
||||
|
||||
#### Info
|
||||
|
||||
* Link: http://modernizr.com
|
||||
|
||||
* Version: 2.6.2
|
||||
|
||||
* Author: Faruk Ateş
|
||||
|
||||
* Description: Browser/device capability finding
|
||||
|
||||
#### License
|
||||
|
||||
Copyright (c) 2009–2015
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
### Normalize.css
|
||||
|
||||
#### Info
|
||||
|
||||
* Link: https://github.com/necolas/normalize.css
|
||||
|
||||
* Version: 1.1.2
|
||||
|
||||
* Authors: Nicolas Gallagher, Jonathan Neal
|
||||
|
||||
* Description: Browser style normalization
|
||||
|
||||
#### License
|
||||
|
||||
Copyright (c) Nicolas Gallagher and Jonathan Neal
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
### Moment.js
|
||||
|
||||
#### Info
|
||||
|
||||
* Link: http://momentjs.com
|
||||
|
||||
* Version: 2.7.0
|
||||
|
||||
* Authors: Tim Wood, Iskren Chernev, Moment.js contributors
|
||||
|
||||
* Description: Time/date parsing/formatting
|
||||
|
||||
#### License
|
||||
|
||||
Copyright (c) 2011-2014 Tim Wood, Iskren Chernev, Moment.js contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
### Json.NET
|
||||
|
||||
#### Info
|
||||
|
||||
* Link: http://www.newtonsoft.com/json
|
||||
|
||||
* Version: 6.0.8
|
||||
|
||||
* Author: Newtonsoft
|
||||
|
||||
* Description: JSON serialization/deserialization
|
||||
|
||||
#### License
|
||||
|
||||
Copyright (c) 2007 James Newton-King
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
### Nancy
|
||||
|
||||
#### Info
|
||||
|
||||
* Link: http://nancyfx.org
|
||||
|
||||
* Version: 0.23.2
|
||||
|
||||
* Author: Andreas Håkansson, Steven Robbins and contributors
|
||||
|
||||
* Description: Embedded web server
|
||||
|
||||
#### License
|
||||
|
||||
Copyright © 2010 Andreas Håkansson, Steven Robbins and contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
### Nancy.Hosting.Self
|
||||
|
||||
#### Info
|
||||
|
||||
* Link: http://nancyfx.org
|
||||
|
||||
* Version: 0.23.2
|
||||
|
||||
* Author: Andreas Håkansson, Steven Robbins and contributors
|
||||
|
||||
* Description: Embedded web server
|
||||
|
||||
#### License
|
||||
|
||||
Copyright © 2010 Andreas Håkansson, Steven Robbins and contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
2
Procfile
2
Procfile
@ -1 +1 @@
|
||||
web: node app.js --port $PORT
|
||||
web: node app.js --port $PORT --include example/localstorage
|
216
README.md
216
README.md
@ -1,119 +1,136 @@
|
||||
# Open MCT [](http://www.apache.org/licenses/LICENSE-2.0) [](https://lgtm.com/projects/g/nasa/openmct/context:javascript) [](https://codecov.io/gh/nasa/openmct) [](https://percy.io/b2e34b17/openmct) [](https://www.npmjs.com/package/openmct)
|
||||
# Open MCT Web
|
||||
|
||||
Open MCT (Open Mission Control Technologies) is a next-generation mission control framework for visualization of data on desktop and mobile devices. It is developed at NASA's Ames Research Center, and is being used by NASA for data analysis of spacecraft missions, as well as planning and operation of experimental rover systems. As a generalizable and open source framework, Open MCT could be used as the basis for building applications for planning, operation, and analysis of any systems producing telemetry data.
|
||||
Open MCT Web is a web-based platform for mission operations user interface
|
||||
software.
|
||||
|
||||
Please visit our [Official Site](https://nasa.github.io/openmct/) and [Getting Started Guide](https://nasa.github.io/openmct/getting-started/)
|
||||
## Bundles
|
||||
|
||||
Once you've created something amazing with Open MCT, showcase your work in our GitHub Discussions [Show and Tell](https://github.com/nasa/openmct/discussions/categories/show-and-tell) section. We love seeing unique and wonderful implementations of Open MCT!
|
||||
A bundle is a group of software components (including source code, declared
|
||||
as AMD modules, as well as resources such as images and HTML templates)
|
||||
that are intended to be added or removed as a single unit. A plug-in for
|
||||
Open MCT Web will be expressed as a bundle; platform components are also
|
||||
expressed as bundles.
|
||||
|
||||
## See Open MCT in Action
|
||||
A bundle is also just a directory which contains a file `bundle.json`,
|
||||
which declares its contents.
|
||||
|
||||
Try Open MCT now with our [live demo](https://openmct-demo.herokuapp.com/).
|
||||

|
||||
The file `bundles.json` (note the plural), at the top level of the
|
||||
repository, is a JSON file containing an array of all bundles (expressed as
|
||||
directory names) to include in a running instance of Open MCT Web. Adding or
|
||||
removing paths from this list will add or remove bundles from the running
|
||||
application.
|
||||
|
||||
## 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.
|
||||
### Bundle Contents
|
||||
|
||||
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.
|
||||
A bundle directory will contain:
|
||||
|
||||
### How do I know if I am using legacy API?
|
||||
You might still be using legacy API if your source code
|
||||
* `bundle.json`, the declaration of the bundles contents.
|
||||
* A source code directory, named `src` by convention. This contains all
|
||||
JavaScript sources exposed by the bundle. These are declared as
|
||||
AMD modules.
|
||||
* A directory for other resources, named `res` by convention. This
|
||||
contains all HTML templates, CSS files, images, and so forth to be
|
||||
used within a given bundle.
|
||||
* A library directory, named `lib` by convention. This contains all
|
||||
external libraries used and/or exposed by the bundle.
|
||||
* A test directory, named `test` by convention. This contains all unit
|
||||
tests declared for the bundle, as well as a `suite.json` that acts
|
||||
as a listing of these dependencies. See the section on unit testing
|
||||
below.
|
||||
|
||||
* 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.
|
||||
(These instructions assume you are installing as a non-root user; developers have [reported issues](https://github.com/nasa/openmct/issues/1151) running these steps with root privileges.)
|
||||
|
||||
1. Clone the source code
|
||||
|
||||
`git clone https://github.com/nasa/openmct.git`
|
||||
|
||||
2. Install development dependencies. Note: Check the package.json engine for our tested and supported node versions.
|
||||
|
||||
`npm install`
|
||||
|
||||
3. Run a local development server
|
||||
|
||||
`npm start`
|
||||
|
||||
Open MCT is now running, and can be accessed by pointing a web browser at [http://localhost:8080/](http://localhost:8080/)
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation is available on the [Open MCT website](https://nasa.github.io/openmct/documentation/).
|
||||
|
||||
### Examples
|
||||
|
||||
The clearest examples for developing Open MCT plugins are in the
|
||||
[tutorials](https://github.com/nasa/openmct-tutorial) provided in
|
||||
our documentation.
|
||||
|
||||
We want Open MCT to be as easy to use, install, run, and develop for as
|
||||
possible, and your feedback will help us get there! Feedback can be provided via [GitHub issues](https://github.com/nasa/openmct/issues/new/choose), [Starting a GitHub Discussion](https://github.com/nasa/openmct/discussions), or by emailing us at [arc-dl-openmct@mail.nasa.gov](mailto:arc-dl-openmct@mail.nasa.gov).
|
||||
|
||||
## Building Applications With Open MCT
|
||||
|
||||
Open MCT is built using [`npm`](http://npmjs.com/) and [`webpack`](https://webpack.js.org/).
|
||||
|
||||
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
|
||||
of software components (including source code and resources such as images and HTML templates)
|
||||
that is intended to be added or removed as a single unit.
|
||||
|
||||
As well as providing an extension mechanism, most of the core Open MCT codebase is also
|
||||
written as plugins.
|
||||
|
||||
For information on writing plugins, please see [our API documentation](https://github.com/nasa/openmct/blob/master/API.md#plugins).
|
||||
Following these bundle conventions is required, at present, to ensure
|
||||
that Open MCT Web (and its build and tests) execute correctly.
|
||||
|
||||
## Tests
|
||||
|
||||
Tests are written for [Jasmine 3](https://jasmine.github.io/api/3.1/global)
|
||||
and run by [Karma](http://karma-runner.github.io). To run:
|
||||
The repository for Open MCT Web includes a test suite that can be run
|
||||
directly from the web browser, `test.html`. This page will:
|
||||
|
||||
`npm test`
|
||||
* Load `bundles.json` to determine which bundles are in the application.
|
||||
* Load `test/suite.json` to determine which source files are to be tested.
|
||||
This should contain an array of strings, where each is the name of an
|
||||
AMD module in the bundle's source directory. For each source file:
|
||||
* Code coverage instrumentation will be added, via Blanket.
|
||||
* The associated test file will be loaded, via RequireJS. These will
|
||||
be located in the bundle's test folder; the test runner will presume
|
||||
these follow a naming convention where each module to be tested has a
|
||||
corresponding test module with the suffix `Spec` in that folder.
|
||||
* Jasmine will then be invoked to run all tests defined in the loaded
|
||||
test modules. Code coverage reporting will be displayed at the bottom
|
||||
of the test page.
|
||||
|
||||
The test suite is configured to load any scripts ending with `Spec.js` found
|
||||
in the `src` hierarchy. Full configuration details are found in
|
||||
`karma.conf.js`. By convention, unit test scripts should be located
|
||||
alongside the units that they test; for example, `src/foo/Bar.js` would be
|
||||
tested by `src/foo/BarSpec.js`. (For legacy reasons, some existing tests may
|
||||
be located in separate `test` folders near the units they test, but the
|
||||
naming convention is otherwise the same.)
|
||||
At present, the test runner presumes that bundle conventions are followed
|
||||
as above; that is, sources are contained in `src`, and tests are contained
|
||||
in `test`. Additionally, individual test files must use the `Spec` suffix
|
||||
as described above.
|
||||
|
||||
### Test Reporting
|
||||
An example of this is expressed in `platform/framework`, which follows
|
||||
bundle conventions.
|
||||
|
||||
When `npm test` is run, test results will be written as HTML to
|
||||
`dist/reports/tests/`. Code coverage information is written to `dist/reports/coverage`.
|
||||
### Functional Testing
|
||||
|
||||
The tests described above are all at the unit-level; an additional
|
||||
test suite using [Protractor](https://angular.github.io/protractor/)
|
||||
us under development, in the `protractor` folder.
|
||||
|
||||
To run:
|
||||
|
||||
* Install protractor following the instructions above.
|
||||
* `cd protractor`
|
||||
* `npm install`
|
||||
* `npm run all`
|
||||
|
||||
## Build
|
||||
|
||||
Open MCT Web includes a Maven command line build. Although Open MCT Web
|
||||
can be run as-is using the repository contents (that is, by viewing
|
||||
`index.html` in a web browser), and its tests can be run in-place
|
||||
similarly (that is, by viewing `test.html` in a browser), the command
|
||||
line build allows machine-driven verification and packaging.
|
||||
|
||||
This build will:
|
||||
|
||||
* Check all sources (excluding those in directories named `lib`) with
|
||||
JSLint for code style compliance. The build will fail if any sources
|
||||
do not satisfy JSLint.
|
||||
* Run unit tests. This is done by running `test.html` in a PhantomJS
|
||||
browser-like environment. The build will fail if any tests fail.
|
||||
* Package the application as a `war` (web archive) file. This is
|
||||
convenient for deployment on Tomcat or similar. This archive will
|
||||
include sources, resources, and libraries for bundles, as well
|
||||
as the top-level files used to initiate running of the application
|
||||
(`index.html` and `bundles.json`).
|
||||
|
||||
Run as `mvn clean install`.
|
||||
|
||||
### Building Documentation
|
||||
|
||||
Open MCT Web's documentation is generated by an
|
||||
[npm](https://www.npmjs.com/)-based build:
|
||||
|
||||
* `npm install` _(only needs to run once)_
|
||||
* `npm run docs`
|
||||
|
||||
Documentation will be generated in `target/docs`. Note that diagram
|
||||
generation is dependent on having [Cairo](http://cairographics.org/download/)
|
||||
installed; see
|
||||
[node-canvas](https://github.com/Automattic/node-canvas#installation)'s
|
||||
documentation for help with installation.
|
||||
|
||||
Code Coverage Reports are available from [codecov.io](https://app.codecov.io/gh/nasa/openmct/)
|
||||
|
||||
# Glossary
|
||||
|
||||
Certain terms are used throughout Open MCT with consistent meanings
|
||||
Certain terms are used throughout Open MCT Web with consistent meanings
|
||||
or conventions. Any deviations from the below are issues and should be
|
||||
addressed (either by updating this glossary or changing code to reflect
|
||||
correct usage.) Other developer documentation, particularly in-line
|
||||
documentation, may presume an understanding of these terms.
|
||||
|
||||
* _plugin_: A plugin is a removable, reusable grouping of software elements.
|
||||
The application is composed of plugins.
|
||||
* _bundle_: A bundle is a removable, reusable grouping of software elements.
|
||||
The application is composed of bundles. Plug-ins are bundles. For more
|
||||
information, refer to framework documentation (under `platform/framework`.)
|
||||
* _capability_: An object which exposes dynamic behavior or non-persistent
|
||||
state associated with a domain object.
|
||||
* _composition_: In the context of a domain object, this refers to the set of
|
||||
other domain objects that compose or are contained by that object. A domain
|
||||
object's composition is the set of domain objects that should appear
|
||||
@ -126,10 +143,15 @@ documentation, may presume an understanding of these terms.
|
||||
(Most often used in the context of extensions, domain
|
||||
object models, or other similar application-specific objects.)
|
||||
* _domain object_: A meaningful object to the user; a distinct thing in
|
||||
the work support by Open MCT. Anything that appears in the left-hand
|
||||
the work support by Open MCT Web. Anything that appears in the left-hand
|
||||
tree is a domain object.
|
||||
* _identifier_: A tuple consisting of a namespace and a key, which together uniquely
|
||||
identifies a domain object.
|
||||
* _extension_: An extension is a unit of functionality exposed to the
|
||||
platform in a declarative fashion by a bundle. For more
|
||||
information, refer to framework documentation (under `platform/framework`.)
|
||||
* _id_: A string which uniquely identifies a domain object.
|
||||
* _key_: When used as an object property, this refers to the machine-readable
|
||||
identifier for a specific thing in a set of things. (Most often used in the
|
||||
context of extensions or other similar application-specific object sets.)
|
||||
* _model_: The persistent state associated with a domain object. A domain
|
||||
object's model is a JavaScript object which can be converted to JSON
|
||||
without losing information (that is, it contains no methods.)
|
||||
@ -141,5 +163,7 @@ documentation, may presume an understanding of these terms.
|
||||
a user clicks on a domain object in the tree, they are _navigating_ to
|
||||
it, and it is thereafter considered the _navigated_ object (until the
|
||||
user makes another such choice.)
|
||||
* _namespace_: A name used to identify a persistence store. A running open MCT
|
||||
application could potentially use multiple persistence stores, with the
|
||||
* _space_: A name used to identify a persistence store. Interactions with
|
||||
persistence with generally involve a `space` parameter in some form, to
|
||||
distinguish multiple persistence stores from one another (for cases
|
||||
where there are multiple valid persistence locations available.)
|
||||
|
31
SECURITY.md
31
SECURITY.md
@ -1,31 +0,0 @@
|
||||
# Security Policy
|
||||
|
||||
The Open MCT team secures our code base using a combination of code review, dependency review, and periodic security reviews. Static analysis performed during automated verification additionally safeguards against common coding errors which may result in vulnerabilities.
|
||||
|
||||
### Reporting a Vulnerability
|
||||
|
||||
For general defects, please for a [Bug Report](https://github.com/nasa/openmct/issues/new/choose)
|
||||
|
||||
To report a vulnerability for Open MCT please send a detailed report to [arc-dl-openmct](mailto:arc-dl-openmct@mail.nasa.gov).
|
||||
|
||||
See our [top-level security policy](https://github.com/nasa/openmct/security/policy) for additional information.
|
||||
|
||||
### CodeQL and LGTM
|
||||
|
||||
The [CodeQL GitHub Actions workflow](https://github.com/nasa/openmct/blob/master/.github/workflows/codeql-analysis.yml) is available to the public. To review the results, fork the repository and run the CodeQL workflow.
|
||||
|
||||
CodeQL is run for every pull-request in GitHub Actions.
|
||||
|
||||
The project is also monitored by [LGTM](https://lgtm.com/projects/g/nasa/openmct/) and is available to public.
|
||||
|
||||
### ESLint
|
||||
|
||||
Static analysis is run for every push on the master branch and every pull request on all branches in Github Actions.
|
||||
|
||||
For more information about ESLint, visit https://eslint.org/.
|
||||
|
||||
### General Support
|
||||
|
||||
For additional support, please open a [Github Discussion](https://github.com/nasa/openmct/discussions).
|
||||
|
||||
If you wish to report a cybersecurity incident or concern, please contact the NASA Security Operations Center either by phone at 1-877-627-2732 or via email address soc@nasa.gov.
|
113
app.js
113
app.js
@ -1,4 +1,4 @@
|
||||
/*global process*/
|
||||
/*global require,process,console*/
|
||||
|
||||
/**
|
||||
* Usage:
|
||||
@ -7,78 +7,57 @@
|
||||
* node app.js [options]
|
||||
*/
|
||||
|
||||
const options = require('minimist')(process.argv.slice(2));
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
const fs = require('fs');
|
||||
const request = require('request');
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
// Defaults
|
||||
options.port = options.port || options.p || 8080;
|
||||
options.host = options.host || 'localhost';
|
||||
options.directory = options.directory || options.D || '.';
|
||||
var BUNDLE_FILE = 'bundles.json',
|
||||
options = require('minimist')(process.argv.slice(2)),
|
||||
express = require('express'),
|
||||
app = express(),
|
||||
fs = require('fs');
|
||||
|
||||
// Show command line options
|
||||
if (options.help || options.h) {
|
||||
console.log("\nUsage: node app.js [options]\n");
|
||||
console.log("Options:");
|
||||
console.log(" --help, -h Show this message.");
|
||||
console.log(" --port, -p <number> Specify port.");
|
||||
console.log(" --directory, -D <bundle> Serve files from specified directory.");
|
||||
console.log("");
|
||||
process.exit(0);
|
||||
}
|
||||
// Defaults
|
||||
options.port = options.port || options.p || 8080;
|
||||
['include', 'exclude', 'i', 'x'].forEach(function (opt) {
|
||||
options[opt] = options[opt] || [];
|
||||
// Make sure includes/excludes always end up as arrays
|
||||
options[opt] = Array.isArray(options[opt]) ?
|
||||
options[opt] : [options[opt]];
|
||||
});
|
||||
options.include = options.include.concat(options.i);
|
||||
options.exclude = options.exclude.concat(options.x);
|
||||
|
||||
app.disable('x-powered-by');
|
||||
// Show command line options
|
||||
if (options.help || options.h) {
|
||||
console.log("\nUsage: node app.js [options]\n");
|
||||
console.log("Options:");
|
||||
console.log(" --help, -h Show this message.");
|
||||
console.log(" --port, -p <number> Specify port.");
|
||||
console.log(" --include, -i <bundle> Include the specified bundle.");
|
||||
console.log(" --exclude, -x <bundle> Exclude the specified bundle.");
|
||||
console.log("");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
app.use('/proxyUrl', function proxyRequest(req, res, next) {
|
||||
console.log('Proxying request to: ', req.query.url);
|
||||
req.pipe(request({
|
||||
url: req.query.url,
|
||||
strictSSL: false
|
||||
}).on('error', next)).pipe(res);
|
||||
});
|
||||
// Override bundles.json for HTTP requests
|
||||
app.use('/' + BUNDLE_FILE, function (req, res) {
|
||||
var bundles = JSON.parse(fs.readFileSync(BUNDLE_FILE, 'utf8'));
|
||||
|
||||
class WatchRunPlugin {
|
||||
apply(compiler) {
|
||||
compiler.hooks.emit.tapAsync('WatchRunPlugin', (compilation, callback) => {
|
||||
console.log('Begin compile at ' + new Date());
|
||||
callback();
|
||||
// Handle command line inclusions/exclusions
|
||||
bundles = bundles.concat(options.include);
|
||||
bundles = bundles.filter(function (bundle) {
|
||||
return options.exclude.indexOf(bundle) === -1;
|
||||
});
|
||||
bundles = bundles.filter(function (bundle, index) { // Uniquify
|
||||
return bundles.indexOf(bundle) === index;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const webpack = require('webpack');
|
||||
const webpackConfig = require('./webpack.dev.js');
|
||||
webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin());
|
||||
webpackConfig.plugins.push(new WatchRunPlugin());
|
||||
res.send(JSON.stringify(bundles));
|
||||
});
|
||||
|
||||
webpackConfig.entry.openmct = [
|
||||
'webpack-hot-middleware/client?reload=true',
|
||||
webpackConfig.entry.openmct
|
||||
];
|
||||
// Expose everything else as static files
|
||||
app.use(express['static']('.'));
|
||||
|
||||
const compiler = webpack(webpackConfig);
|
||||
|
||||
app.use(require('webpack-dev-middleware')(
|
||||
compiler,
|
||||
{
|
||||
publicPath: '/dist',
|
||||
stats: 'errors-warnings'
|
||||
}
|
||||
));
|
||||
|
||||
app.use(require('webpack-hot-middleware')(
|
||||
compiler,
|
||||
{}
|
||||
));
|
||||
|
||||
// Expose index.html for development users.
|
||||
app.get('/', function (req, res) {
|
||||
fs.createReadStream('index.html').pipe(res);
|
||||
});
|
||||
|
||||
// Finally, open the HTTP server and log the instance to the console
|
||||
app.listen(options.port, options.host, function () {
|
||||
console.log('Open MCT application running at %s:%s', options.host, options.port);
|
||||
});
|
||||
// Finally, open the HTTP server
|
||||
app.listen(options.port);
|
||||
}());
|
@ -1,9 +0,0 @@
|
||||
// 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;
|
@ -1,11 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
#*****************************************************************************
|
||||
#* Open MCT, Copyright (c) 2014-2022, United States Government
|
||||
#* Open MCT Web, Copyright (c) 2014-2015, 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
|
||||
#* Open MCT Web 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.
|
||||
@ -16,25 +16,23 @@
|
||||
#* License for the specific language governing permissions and limitations
|
||||
#* under the License.
|
||||
#*
|
||||
#* Open MCT includes source code licensed under additional open source
|
||||
#* Open MCT Web 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.
|
||||
#*****************************************************************************
|
||||
|
||||
# Script to build and deploy docs.
|
||||
# Script to build and deploy docs to github pages.
|
||||
|
||||
OUTPUT_DIRECTORY="dist/docs"
|
||||
# Docs, once built, are pushed to the private website repo
|
||||
REPOSITORY_URL="git@github.com:nasa/openmct-website.git"
|
||||
WEBSITE_DIRECTORY="website"
|
||||
OUTPUT_DIRECTORY="target/docs"
|
||||
REPOSITORY_URL="git@github.com:nasa/openmctweb.git"
|
||||
|
||||
BUILD_SHA=`git rev-parse HEAD`
|
||||
BUILD_SHA=`git rev-parse head`
|
||||
|
||||
# A remote will be created for the git repository we are pushing to.
|
||||
# Don't worry, as this entire directory will get trashed in between builds.
|
||||
# Don't worry, as this entire directory will get trashed inbetween builds.
|
||||
REMOTE_NAME="documentation"
|
||||
WEBSITE_BRANCH="master"
|
||||
WEBSITE_BRANCH="gh-pages"
|
||||
|
||||
# Clean output directory, JSDOC will recreate
|
||||
if [ -d $OUTPUT_DIRECTORY ]; then
|
||||
@ -42,21 +40,23 @@ if [ -d $OUTPUT_DIRECTORY ]; then
|
||||
fi
|
||||
|
||||
npm run docs
|
||||
cd $OUTPUT_DIRECTORY || exit 1
|
||||
|
||||
echo "git clone $REPOSITORY_URL website"
|
||||
git clone $REPOSITORY_URL website || exit 1
|
||||
echo "cp -r $OUTPUT_DIRECTORY $WEBSITE_DIRECTORY"
|
||||
cp -r $OUTPUT_DIRECTORY $WEBSITE_DIRECTORY
|
||||
echo "cd $WEBSITE_DIRECTORY"
|
||||
cd $WEBSITE_DIRECTORY || exit 1
|
||||
echo "git init"
|
||||
git init
|
||||
|
||||
# Configure github for CircleCI user.
|
||||
git config user.email "buildbot@circleci.com"
|
||||
git config user.name "BuildBot"
|
||||
|
||||
echo "git remote add $REMOTE_NAME $REPOSITORY_URL"
|
||||
git remote add $REMOTE_NAME $REPOSITORY_URL
|
||||
echo "git add ."
|
||||
git add .
|
||||
echo "git commit -m \"Docs updated from build $BUILD_SHA\""
|
||||
git commit -m "Docs updated from build $BUILD_SHA"
|
||||
# Push to the website repo
|
||||
git push
|
||||
echo "git commit -m \"Generate docs from build $BUILD_SHA\""
|
||||
git commit -m "Generate docs from build $BUILD_SHA"
|
||||
|
||||
echo "git push $REMOTE_NAME HEAD:$WEBSITE_BRANCH -f"
|
||||
git push $REMOTE_NAME HEAD:$WEBSITE_BRANCH -f
|
||||
|
||||
echo "Documentation pushed to gh-pages branch."
|
||||
|
33
bundles.json
Normal file
33
bundles.json
Normal file
@ -0,0 +1,33 @@
|
||||
[
|
||||
"platform/framework",
|
||||
"platform/core",
|
||||
"platform/representation",
|
||||
"platform/commonUI/about",
|
||||
"platform/commonUI/browse",
|
||||
"platform/commonUI/edit",
|
||||
"platform/commonUI/dialog",
|
||||
"platform/commonUI/general",
|
||||
"platform/commonUI/inspect",
|
||||
"platform/commonUI/mobile",
|
||||
"platform/commonUI/themes/espresso",
|
||||
"platform/containment",
|
||||
"platform/execution",
|
||||
"platform/telemetry",
|
||||
"platform/features/imagery",
|
||||
"platform/features/layout",
|
||||
"platform/features/pages",
|
||||
"platform/features/plot",
|
||||
"platform/features/scrolling",
|
||||
"platform/features/events",
|
||||
"platform/forms",
|
||||
"platform/identity",
|
||||
"platform/persistence/local",
|
||||
"platform/persistence/queue",
|
||||
"platform/policy",
|
||||
"platform/entanglement",
|
||||
"platform/search",
|
||||
|
||||
"example/imagery",
|
||||
"example/eventGenerator",
|
||||
"example/generator"
|
||||
]
|
14
circle.yml
Normal file
14
circle.yml
Normal file
@ -0,0 +1,14 @@
|
||||
deployment:
|
||||
production:
|
||||
branch: master
|
||||
commands:
|
||||
- ./build-docs.sh
|
||||
- git push git@heroku.com:openmctweb-demo.git $CIRCLE_SHA1:refs/heads/master
|
||||
openmctweb-staging-un:
|
||||
branch: search
|
||||
heroku:
|
||||
appname: openmctweb-staging-un
|
||||
openmctweb-staging-deux:
|
||||
branch: mobile
|
||||
heroku:
|
||||
appname: openmctweb-staging-deux
|
29
codecov.yml
29
codecov.yml
@ -1,29 +0,0 @@
|
||||
codecov:
|
||||
require_ci_to_pass: false #This setting will update the bot regardless of whether or not tests pass
|
||||
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
informational: true
|
||||
patch:
|
||||
default:
|
||||
informational: true
|
||||
precision: 2
|
||||
round: down
|
||||
range: "66...100"
|
||||
|
||||
ignore:
|
||||
|
||||
parsers:
|
||||
gcov:
|
||||
branch_detection:
|
||||
conditional: true
|
||||
loop: true
|
||||
method: false
|
||||
macro: false
|
||||
|
||||
comment:
|
||||
layout: "reach,diff,flags,files,footer"
|
||||
behavior: default
|
||||
require_changes: false
|
@ -1,21 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
@ -1,3 +0,0 @@
|
||||
<hr>
|
||||
</body>
|
||||
</html>
|
@ -1,9 +1,9 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2022, United States Government
|
||||
* Open MCT Web, Copyright (c) 2014-2015, 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
|
||||
* Open MCT Web 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.
|
||||
@ -14,13 +14,13 @@
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* Open MCT Web 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.
|
||||
*****************************************************************************/
|
||||
|
||||
/*global require,process,__dirname,GLOBAL*/
|
||||
/*global require,process,GLOBAL*/
|
||||
/*jslint nomen: false */
|
||||
|
||||
|
||||
@ -30,8 +30,7 @@
|
||||
var CONSTANTS = {
|
||||
DIAGRAM_WIDTH: 800,
|
||||
DIAGRAM_HEIGHT: 500
|
||||
},
|
||||
TOC_HEAD = "# Table of Contents";
|
||||
};
|
||||
|
||||
GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be defined
|
||||
(function () {
|
||||
@ -45,10 +44,7 @@ GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be define
|
||||
split = require("split"),
|
||||
stream = require("stream"),
|
||||
nomnoml = require('nomnoml'),
|
||||
toc = require("markdown-toc"),
|
||||
Canvas = require('canvas'),
|
||||
header = fs.readFileSync(path.resolve(__dirname, 'header.html')),
|
||||
footer = fs.readFileSync(path.resolve(__dirname, 'footer.html')),
|
||||
options = require("minimist")(process.argv.slice(2));
|
||||
|
||||
// Convert from nomnoml source to a target PNG file.
|
||||
@ -106,7 +102,7 @@ GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be define
|
||||
}
|
||||
|
||||
// Convert from Github-flavored Markdown to HTML
|
||||
function gfmifier(renderTOC) {
|
||||
function gfmifier() {
|
||||
var transform = new stream.Transform({ objectMode: true }),
|
||||
markdown = "";
|
||||
transform._transform = function (chunk, encoding, done) {
|
||||
@ -114,14 +110,9 @@ GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be define
|
||||
done();
|
||||
};
|
||||
transform._flush = function (done) {
|
||||
if (renderTOC){
|
||||
// Prepend table of contents
|
||||
markdown =
|
||||
[ TOC_HEAD, toc(markdown).content, "", markdown ].join("\n");
|
||||
}
|
||||
this.push(header);
|
||||
this.push("<html><body>\n");
|
||||
this.push(marked(markdown));
|
||||
this.push(footer);
|
||||
this.push("\n</body></html>\n");
|
||||
done();
|
||||
};
|
||||
return transform;
|
||||
@ -142,8 +133,8 @@ GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be define
|
||||
customRenderer.link = function (href, title, text) {
|
||||
// ...but only if they look like relative paths
|
||||
return (href || "").indexOf(":") === -1 && href[0] !== "/" ?
|
||||
renderer.link(href.replace(/\.md/, ".html"), title, text) :
|
||||
renderer.link.apply(renderer, arguments);
|
||||
renderer.link(href.replace(/\.md/, ".html"), title, text) :
|
||||
renderer.link.apply(renderer, arguments);
|
||||
};
|
||||
return customRenderer;
|
||||
}
|
||||
@ -170,16 +161,13 @@ GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be define
|
||||
var destination = file.replace(options['in'], options.out)
|
||||
.replace(/md$/, "html"),
|
||||
destPath = path.dirname(destination),
|
||||
prefix = path.basename(destination).replace(/\.html$/, ""),
|
||||
//Determine whether TOC should be rendered for this file based
|
||||
//on regex provided as command line option
|
||||
renderTOC = file.match(options['suppress-toc'] || "") === null;
|
||||
prefix = path.basename(destination).replace(/\.html$/, "");
|
||||
|
||||
mkdirp(destPath, function (err) {
|
||||
fs.createReadStream(file, { encoding: 'utf8' })
|
||||
.pipe(split())
|
||||
.pipe(nomnomlifier(destPath, prefix))
|
||||
.pipe(gfmifier(renderTOC))
|
||||
.pipe(gfmifier())
|
||||
.pipe(fs.createWriteStream(destination, {
|
||||
encoding: 'utf8'
|
||||
}));
|
||||
@ -191,17 +179,13 @@ GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be define
|
||||
glob(options['in'] + "/**/*.@(html|css|png)", {}, function (err, files) {
|
||||
files.forEach(function (file) {
|
||||
var destination = file.replace(options['in'], options.out),
|
||||
destPath = path.dirname(destination),
|
||||
streamOptions = {};
|
||||
if (file.match(/png$/)) {
|
||||
streamOptions.encoding = null;
|
||||
} else {
|
||||
streamOptions.encoding = 'utf8';
|
||||
}
|
||||
destPath = path.dirname(destination);
|
||||
|
||||
mkdirp(destPath, function (err) {
|
||||
fs.createReadStream(file, streamOptions)
|
||||
.pipe(fs.createWriteStream(destination, streamOptions));
|
||||
fs.createReadStream(file, { encoding: 'utf8' })
|
||||
.pipe(fs.createWriteStream(destination, {
|
||||
encoding: 'utf8'
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,9 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet"
|
||||
href="//nasa.github.io/openmct/static/res/css/styles.css">
|
||||
<link rel="stylesheet"
|
||||
href="//nasa.github.io/openmct/static/res/css/documentation.css">
|
||||
</head>
|
||||
<body>
|
||||
|
232
docs/src/architecture/Framework.md
Normal file
232
docs/src/architecture/Framework.md
Normal file
@ -0,0 +1,232 @@
|
||||
# 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 Web. _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 Web|
|
||||
[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 Web'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 Web 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 Web'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 Web, 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 Web 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.
|
714
docs/src/architecture/Platform.md
Normal file
714
docs/src/architecture/Platform.md
Normal file
@ -0,0 +1,714 @@
|
||||
# Overview
|
||||
|
||||
The Open MCT Web 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 Web 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 Web 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.
|
||||
* [_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): The information model
|
||||
describes the state and behavior of the objects with which the user
|
||||
interacts.
|
||||
* [_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 Web, except
|
||||
for the interfaces which are utilized by adapters participating in the
|
||||
service infrastructure.
|
||||
|
||||
## Application Start-up
|
||||
|
||||
Once the
|
||||
[application has been initialized](Framework.md#application-initialization)
|
||||
Open MCT Web 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 Web 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 Web (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 Web 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 Web 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 Web, 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 Web 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 Web'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 Web 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 Web 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 Web'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 Web 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 Web 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 Web
|
||||
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 ot 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 Web application. The platform
|
||||
implementation reads these types in from extension category `types`
|
||||
and wraps them in a JavaScript interface.
|
78
docs/src/architecture/index.md
Normal file
78
docs/src/architecture/index.md
Normal file
@ -0,0 +1,78 @@
|
||||
# Introduction
|
||||
|
||||
The purpose of this document is to familiarize developers with the
|
||||
overall architecture of Open MCT Web.
|
||||
|
||||
The target audience includes:
|
||||
|
||||
* _Platform maintainers_: Individuals involved in developing,
|
||||
extending, and maintaing capabilities of the platform.
|
||||
* _Integration developers_: Individuals tasked with integrated
|
||||
Open MCT Web 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 Web 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 Web]->[Browser APIs]]]
|
||||
[Server|[Web services]]
|
||||
[Client]<->[Server]
|
||||
```
|
||||
|
||||
While Open MCT Web 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 Web to interact with these services.
|
||||
|
||||
Typically, the pattern here is to provide a known interface that
|
||||
Open MCT Web 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 Web 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 Web|
|
||||
[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 Web. 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 Web. 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 Web,
|
||||
as well as optional features (such as Plot view) included alongside
|
||||
the platform.
|
||||
|
||||
|
3
docs/src/guide/index.md
Normal file
3
docs/src/guide/index.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Developer Guide
|
||||
|
||||
This is a placeholder for the developer guide.
|
@ -1,121 +0,0 @@
|
||||
# Security Guide
|
||||
|
||||
Open MCT is a rich client with plugin support that executes as a single page
|
||||
web application in a browser environment. Security concerns and
|
||||
vulnerabilities associated with the web as a platform should be considered
|
||||
before deploying Open MCT (or any other web application) for mission or
|
||||
production usage.
|
||||
|
||||
This document describes several important points to consider when developing
|
||||
for or deploying Open MCT securely. Other resources such as
|
||||
[Open Web Application Security Project (OWASP)](https://www.owasp.org)
|
||||
provide a deeper and more general overview of security for web applications.
|
||||
|
||||
|
||||
## Security Model
|
||||
|
||||
Open MCT has been architected assuming the following deployment pattern:
|
||||
|
||||
* A tagged, tested Open MCT version will be used.
|
||||
* Externally authored plugins will be installed.
|
||||
* A server will provide persistent storage, telemetry, and other shared data.
|
||||
* Authorization, authentication, and auditing will be handled by a server.
|
||||
|
||||
|
||||
## Security Procedures
|
||||
|
||||
The Open MCT team secures our code base using a combination of code review,
|
||||
dependency review, and periodic security reviews. Static analysis performed
|
||||
during automated verification additionally safeguards against common
|
||||
coding errors which may result in vulnerabilities.
|
||||
|
||||
|
||||
### Code Review
|
||||
|
||||
All contributions are reviewed by internal team members. External
|
||||
contributors receive increased scrutiny for security and quality,
|
||||
and must sign a licensing agreement.
|
||||
|
||||
### Dependency Review
|
||||
|
||||
Before integrating third-party dependencies, they are reviewed for security
|
||||
and quality, with consideration given to authors and users of these
|
||||
dependencies, as well as review of open source code.
|
||||
|
||||
### Periodic Security Reviews
|
||||
|
||||
Open MCT's code, design, and architecture are periodically reviewed
|
||||
(approximately annually) for common security issues, such as the
|
||||
[OWASP Top Ten](https://www.owasp.org/index.php/Category:OWASP_Top_Ten_Project).
|
||||
|
||||
|
||||
## Security Concerns
|
||||
|
||||
Certain security concerns deserve special attention when deploying Open MCT,
|
||||
or when authoring plugins.
|
||||
|
||||
### Identity Spoofing
|
||||
|
||||
Open MCT issues calls to web services with the privileges of a logged in user.
|
||||
Compromised sources (either for Open MCT itself or a plugin) could
|
||||
therefore allow malicious code to execute with those privileges.
|
||||
|
||||
To avoid this:
|
||||
|
||||
* Serve Open MCT and other scripts over SSL (https rather than http)
|
||||
to prevent man-in-the-middle attacks.
|
||||
* Exercise precautions such as security reviews for any plugins or
|
||||
applications built for or with Open MCT to reject malicious changes.
|
||||
|
||||
### Information Disclosure
|
||||
|
||||
If Open MCT is used to handle or display sensitive data, any components
|
||||
(such as adapter plugins) must take care to avoid leaking or disclosing
|
||||
this information. For example, avoid sending sensitive data to third-party
|
||||
servers or insecure APIs.
|
||||
|
||||
### Data Tampering
|
||||
|
||||
The web application architecture leaves open the possibility that direct
|
||||
calls will be made to back-end services, circumventing Open MCT entirely.
|
||||
As such, Open MCT assumes that server components will perform any necessary
|
||||
data validation during calls issues to the server.
|
||||
|
||||
Additionally, plugins which serialize and write data to the server must
|
||||
escape that data to avoid database injection attacks, and similar.
|
||||
|
||||
### Repudiation
|
||||
|
||||
Open MCT assumes that servers log any relevant interactions and associates
|
||||
these with a user identity; the specific user actions taken within the
|
||||
application are assumed not to be of concern for auditing.
|
||||
|
||||
In the absence of server-side logging, users may disclaim (maliciously,
|
||||
mistakenly, or otherwise) actions taken within the system without any
|
||||
way to prove otherwise.
|
||||
|
||||
If keeping client-level interactions is important, this will need to be
|
||||
implemented via a plugin.
|
||||
|
||||
### Denial-of-service
|
||||
|
||||
Open MCT assumes that server-side components will be insulated against
|
||||
denial-of-service attacks. Services should only permit resource-intensive
|
||||
tasks to be initiated by known or trusted users.
|
||||
|
||||
### Elevation of Privilege
|
||||
|
||||
Corollary to the assumption that servers guide against identity spoofing,
|
||||
Open MCT assumes that services do not allow a user to act with
|
||||
inappropriately escalated privileges. Open MCT cannot protect against
|
||||
such escalation; in the clearest case, a malicious actor could interact
|
||||
with web services directly to exploit such a vulnerability.
|
||||
|
||||
## Additional Reading
|
||||
|
||||
The following resources have been used as a basis for identifying potential
|
||||
security threats to Open MCT deployments in preparation of this document:
|
||||
|
||||
* [STRIDE model](https://www.owasp.org/index.php/Threat_Risk_Modeling#STRIDE)
|
||||
* [Attack Surface Analysis Cheat Sheet](https://www.owasp.org/index.php/Attack_Surface_Analysis_Cheat_Sheet)
|
||||
* [XSS Prevention Cheat Sheet](https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet)
|
36
docs/src/index.html
Normal file
36
docs/src/index.html
Normal file
@ -0,0 +1,36 @@
|
||||
<!--
|
||||
Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
as represented by the Administrator of the National Aeronautics and Space
|
||||
Administration. All rights reserved.
|
||||
|
||||
Open MCT Web 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 Web 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.
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head lang="en">
|
||||
<meta charset="UTF-8">
|
||||
<title>Open MCT Web Documentation</title>
|
||||
</head>
|
||||
<body class="user-environ" ng-view>
|
||||
Sections:
|
||||
<ul>
|
||||
<li><a href="api/">API</a></li>
|
||||
<li><a href="guide/">Developer Guide</a></li>
|
||||
<li><a href="architecture/">Architecture Overview</a></li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
@ -1,26 +0,0 @@
|
||||
# Open MCT Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
Documentation is provided to support the use and development of
|
||||
Open MCT. It's recommended that before doing
|
||||
any development with Open MCT you take some time to familiarize yourself
|
||||
with the documentation below.
|
||||
|
||||
Open MCT provides functionality out of the box, but it's also a platform for
|
||||
building rich mission operations applications based on modern web technology.
|
||||
The platform is configured by plugins which extend the platform at a variety
|
||||
of extension points. The details of how to
|
||||
extend the platform are provided in the following documentation.
|
||||
|
||||
## Sections
|
||||
|
||||
* The [API](api/) document is generated from inline documentation
|
||||
using [JSDoc](http://usejsdoc.org/), and describes the JavaScript objects and
|
||||
functions that make up the software platform.
|
||||
|
||||
* The [Development Process](process/) document describes the
|
||||
Open MCT software development cycle.
|
||||
|
||||
* The [Tutorials](https://github.com/nasa/openmct-tutorial) give examples of extending the platform to add
|
||||
functionality, and integrate with data sources.
|
@ -1,165 +0,0 @@
|
||||
# Development Cycle
|
||||
|
||||
Development of Open MCT occurs on an iterative cycle of
|
||||
sprints and releases.
|
||||
|
||||
* A _sprint_ is three weeks in duration, and represents a
|
||||
set of improvements that can be completed and tested by the
|
||||
development team. Software at the end of the sprint is
|
||||
"semi-stable"; it will have undergone reduced testing and may carry
|
||||
defects or usability issues of lower severity, particularly if
|
||||
there are workarounds.
|
||||
* A _release_ occurs every four sprints. Releases are stable, and
|
||||
will have undergone full acceptance testing to ensure that the
|
||||
software behaves correctly and usably.
|
||||
|
||||
## Roles
|
||||
|
||||
The sprint process assumes the presence of a __project manager.__
|
||||
The project manager is responsible for
|
||||
making tactical decisions about what development work will be
|
||||
performed, and for coordinating with stakeholders to arrive at
|
||||
higher-level strategic decisions about desired functionality
|
||||
and characteristics of the software, major external milestones,
|
||||
and so forth.
|
||||
|
||||
In the absence of a dedicated project manager, this role may be rotated
|
||||
among members of the development team on a per-sprint basis.
|
||||
|
||||
Responsibilities of the project manager including:
|
||||
|
||||
* Maintaining (with agreement of stakeholders) a "road map" of work
|
||||
planned for future releases/sprints; this should be higher-level,
|
||||
usually expressed as "themes",
|
||||
with just enough specificity to gauge feasibility of plans,
|
||||
relate work back to milestones, and identify longer-term
|
||||
dependencies.
|
||||
* Determining (with assistance from the rest of the team) which
|
||||
issues to work on in a given sprint and how they shall be
|
||||
assigned.
|
||||
* Pre-planning subsequent sprints to ensure that all members of the
|
||||
team always have a clear direction.
|
||||
* Scheduling and/or ensuring adherence to
|
||||
[process points](#process-points).
|
||||
* Responding to changes within the sprint (shifting priorities,
|
||||
new issues) and re-allocating work for the sprint as needed.
|
||||
|
||||
## Sprint Calendar
|
||||
|
||||
Certain [process points](#process-points) are regularly scheduled in
|
||||
the sprint cycle.
|
||||
|
||||
### Sprints by Release
|
||||
|
||||
Allocation of work among sprints should be planned relative to release
|
||||
goals and milestones. As a general guideline, higher-risk work (large
|
||||
new features which may carry new defects, major refactoring, design
|
||||
changes with uncertain effects on usability) should be allocated to
|
||||
earlier sprints, allowing for time in later sprints to ensure stability.
|
||||
|
||||
| Sprint | Focus |
|
||||
|:------:|:--------------------------------------------------------|
|
||||
| __1__ | Prototyping, design, experimentation. |
|
||||
| __2__ | New features, refinements, enhancements. |
|
||||
| __3__ | Feature completion, low-risk enhancements, bug fixing. |
|
||||
| __4__ | Stability & quality assurance. |
|
||||
|
||||
### Sprints 1-3
|
||||
|
||||
The first three sprints of a release are primarily centered around
|
||||
development work, with regular acceptance testing in the third
|
||||
week. During this third week, the top priority should be passing
|
||||
acceptance testing (e.g. by resolving any blockers found); any
|
||||
resources not needed for this effort should be used to begin work
|
||||
for the subsequent sprint.
|
||||
|
||||
| Week | Mon | Tue | Wed | Thu | Fri |
|
||||
|:-----:|:-------------------------:|:------:|:---:|:----------------------------:|:-------------------------------------:|
|
||||
| __1__ | Sprint plan | Tag-up | | | |
|
||||
| __2__ | | Tag-up | | | Code freeze and sprint branch |
|
||||
| __3__ | Per-sprint testing | Triage | | _Per-sprint testing*_ | Ship and merge sprint branch to master|
|
||||
|
||||
* If necessary.
|
||||
|
||||
### Sprint 4
|
||||
|
||||
The software must be stable at the end of the fourth sprint; because of
|
||||
this, the fourth sprint is scheduled differently, with a heightened
|
||||
emphasis on testing.
|
||||
|
||||
| Week | Mon | Tue | Wed | Thu | Fri |
|
||||
|-------:|:-------------------------:|:------:|:---:|:----------------------------:|:-----------:|
|
||||
| __1__ | Sprint plan | Tag-up | | | Code freeze |
|
||||
| __2__ | Per-release testing | Triage | | | |
|
||||
| __3__ | _Per-release testing*_ | Triage | | _Per-release testing*_ | Ship |
|
||||
|
||||
* If necessary.
|
||||
|
||||
## Process Points
|
||||
|
||||
* __Sprint plan.__ Project manager allocates issues based on
|
||||
theme(s) for sprint, then reviews with team. Each team member
|
||||
should have roughly two weeks of work allocated (to allow time
|
||||
in the third week for testing of work completed.)
|
||||
* Project manager should also sketch out subsequent sprint so
|
||||
that team may begin work for that sprint during the
|
||||
third week, since testing and blocker resolution is unlikely
|
||||
to require all available resources.
|
||||
* Testing success criteria identified per issue (where necessary). This could be in the form of acceptance tests on the issue or detailing performance tests, for example.
|
||||
* __Tag-up.__ Check in and status update among development team.
|
||||
May amend plan for sprint as-needed.
|
||||
* __Code freeze.__ Any new work from this sprint
|
||||
(features, bug fixes, enhancements) must be integrated by the
|
||||
end of the second week of the sprint. After code freeze, a sprint
|
||||
branch will be created (and until the end of the sprint) the only
|
||||
changes that should be merged into the sprint branch should
|
||||
directly address issues needed to pass acceptance testing.
|
||||
During this time, any other feature development will continue to
|
||||
be merged into the master branch for the next sprint.
|
||||
* __Sprint branch merge to master.__ After acceptance testing, the sprint branch
|
||||
will be merged back to the master branch. Any code conflicts that
|
||||
arise will be resolved by the team.
|
||||
* [__Per-release Testing.__](testing/plan.md#per-release-testing)
|
||||
Structured testing with predefined
|
||||
success criteria. No release should ship without passing
|
||||
acceptance tests. Time is allocated in each sprint for subsequent
|
||||
rounds of acceptance testing if issues are identified during a
|
||||
prior round. Specific details of acceptance testing need to be
|
||||
agreed-upon with relevant stakeholders and delivery recipients,
|
||||
and should be flexible enough to allow changes to plans
|
||||
(e.g. deferring delivery of some feature in order to ensure
|
||||
stability of other features.) Baseline testing includes:
|
||||
* [__Testathon.__](testing/plan.md#user-testing)
|
||||
Multi-user testing, involving as many users as
|
||||
is feasible, plus development team. Open-ended; should verify
|
||||
completed work from this sprint using the sprint branch, test
|
||||
exploratorily for regressions, et cetera.
|
||||
* [__Long-Duration Test.__](testing/plan.md#long-duration-testing) A
|
||||
test to verify that the software remains
|
||||
stable after running for longer durations. May include some
|
||||
combination of automated testing and user verification (e.g.
|
||||
checking to verify that software remains subjectively
|
||||
responsive at conclusion of test.)
|
||||
* [__Unit Testing.__](testing/plan.md#unit-testing)
|
||||
Automated testing integrated into the
|
||||
build. (These tests are verified to pass more often than once
|
||||
per sprint, as they run before any merge to master, but still
|
||||
play an important role in per-release testing.)
|
||||
* [__Per-sprint Testing.__](testing/plan.md#per-sprint-testing)
|
||||
Subset of Pre-release Testing
|
||||
which should be performed before shipping at the end of any
|
||||
sprint. Time is allocated for a second round of
|
||||
Pre-release Testing if the first round is not passed. Smoke tests collected from issues/PRs
|
||||
* __Triage.__ Team reviews issues from acceptance testing and uses
|
||||
success criteria to determine whether or not they should block
|
||||
release, then formulates a plan to address these issues before
|
||||
the next round of acceptance testing. Focus here should be on
|
||||
ensuring software passes that testing in order to ship on time;
|
||||
may prefer to disable malfunctioning components and fix them
|
||||
in a subsequent sprint, for example.
|
||||
* [__Ship.__](version.md) Tag a code snapshot that has passed release/sprint
|
||||
testing and deploy that version. (Only true if relevant
|
||||
testing has passed by this point; if testing has not
|
||||
been passed, will need to make ad hoc decisions with stakeholders,
|
||||
e.g. "extend the sprint" or "defer shipment until end of next
|
||||
sprint.")
|
@ -1,11 +0,0 @@
|
||||
# Development Process
|
||||
|
||||
The process used to develop Open MCT is described in the following
|
||||
documents:
|
||||
|
||||
* The [Development Cycle](cycle.md) describes how and when specific
|
||||
process points are repeated during development.
|
||||
* The [Version Guide](version.md) describes version numbering for
|
||||
Open MCT (both semantics and process.)
|
||||
* The [Test Plan](testing/plan.md) summarizes the approaches used
|
||||
to test Open MCT.
|
@ -1,146 +0,0 @@
|
||||
# Test Plan
|
||||
|
||||
## Test Levels
|
||||
|
||||
Testing for Open MCT includes:
|
||||
|
||||
* _Smoke testing_: Brief, informal testing to verify that no major issues
|
||||
or regressions are present in the software, or in specific features of
|
||||
the software.
|
||||
* _Unit testing_: Automated verification of the performance of individual
|
||||
software components.
|
||||
* _User testing_: Testing with a representative user base to verify
|
||||
that application behaves usably and as specified.
|
||||
* _Long-duration testing_: Testing which takes place over a long period
|
||||
of time to detect issues which are not readily noticeable during
|
||||
shorter test periods.
|
||||
|
||||
### Smoke Testing
|
||||
|
||||
Manual, non-rigorous testing of the software and/or specific features
|
||||
of interest. Verifies that the software runs and that basic functionality
|
||||
is present. The outcome of Smoke Testing should be a simplified list of Acceptance Tests which could be executed by another team member with sufficient context.
|
||||
|
||||
### Unit Testing
|
||||
|
||||
Unit tests are automated tests which exercise individual software
|
||||
components. Tests are subject to code review along with the actual
|
||||
implementation, to ensure that tests are applicable and useful.
|
||||
|
||||
Unit tests should meet
|
||||
[test standards](https://github.com/nasa/openmctweb/blob/master/CONTRIBUTING.md#test-standards)
|
||||
as described in the contributing guide.
|
||||
|
||||
### User Testing
|
||||
|
||||
User testing is performed at scheduled times involving target users
|
||||
of the software or reasonable representatives, along with members of
|
||||
the development team exercising known use cases. Users test the
|
||||
software directly; the software should be configured as similarly to
|
||||
its planned production configuration as is feasible without introducing
|
||||
other risks (e.g. damage to data in a production instance.)
|
||||
|
||||
User testing will focus on the following activities:
|
||||
|
||||
* Verifying issues resolved since the last test session.
|
||||
* Checking for regressions in areas related to recent changes.
|
||||
* Using major or important features of the software,
|
||||
as determined by the user.
|
||||
* General "trying to break things."
|
||||
|
||||
During user testing, users will
|
||||
[report issues](https://github.com/nasa/openmct/issues/new/choose)
|
||||
as they are encountered.
|
||||
|
||||
Desired outcomes of user testing are:
|
||||
|
||||
* Identified software defects.
|
||||
* Areas for usability improvement.
|
||||
* Feature requests (particularly missed requirements.)
|
||||
* Recorded issue verification.
|
||||
|
||||
### Long-duration Testing
|
||||
|
||||
Long-duration testing occurs over a twenty-four hour period. The
|
||||
software is run in one or more stressing cases representative of expected
|
||||
usage. After twenty-four hours, the software is evaluated for:
|
||||
|
||||
* Performance metrics: Have memory usage or CPU utilization increased
|
||||
during this time period in unexpected or undesirable ways?
|
||||
* Subjective usability: Does the software behave in the same way it did
|
||||
at the start of the test? Is it as responsive?
|
||||
|
||||
Any defects or unexpected behavior identified during testing should be
|
||||
[reported as issues](https://github.com/nasa/openmct/issues/new/choose)
|
||||
and reviewed for severity.
|
||||
|
||||
## Test Performance
|
||||
|
||||
Tests are performed at various levels of frequency.
|
||||
|
||||
* _Per-merge_: Performed before any new changes are integrated into
|
||||
the software.
|
||||
* _Per-sprint_: Performed at the end of every [sprint](../cycle.md).
|
||||
* _Per-release_: Performed at the end of every [release](../cycle.md).
|
||||
|
||||
### Per-merge Testing
|
||||
|
||||
Before changes are merged, the author of the changes must perform:
|
||||
|
||||
* _Smoke testing_ (both generally, and for areas which interact with
|
||||
the new changes.)
|
||||
* _Unit testing_ (as part of the automated build step.)
|
||||
|
||||
Changes are not merged until the author has affirmed that both
|
||||
forms of testing have been performed successfully; this is documented
|
||||
by the [Author Checklist](https://github.com/nasa/openmctweb/blob/master/CONTRIBUTING.md#author-checklist).
|
||||
|
||||
### Per-sprint Testing
|
||||
|
||||
Before a sprint is closed, the development team must additionally
|
||||
perform:
|
||||
|
||||
* A relevant subset of [_user testing_](procedures.md#user-test-procedures)
|
||||
identified by the acting [project manager](../cycle.md#roles).
|
||||
* [_Long-duration testing_](procedures.md#long-duration-testing)
|
||||
(specifically, for 24 hours.)
|
||||
|
||||
Issues are reported as a product of both forms of testing.
|
||||
|
||||
A sprint is not closed until both categories have been performed on
|
||||
the latest snapshot of the software, _and_ no issues labelled as
|
||||
["blocker"](https://github.com/nasa/openmctweb/blob/master/CONTRIBUTING.md#issue-reporting)
|
||||
remain open.
|
||||
|
||||
### Per-release Testing
|
||||
|
||||
As [per-sprint testing](#per-sprint-testing), except that _user testing_
|
||||
should cover all test cases, with less focus on changes from the specific
|
||||
sprint or release.
|
||||
|
||||
Per-release testing should also include any acceptance testing steps
|
||||
agreed upon with recipients of the software.
|
||||
|
||||
A release is not closed until both categories have been performed on
|
||||
the latest snapshot of the software, _and_ no issues labelled as
|
||||
["blocker" or "critical"](https://github.com/nasa/openmctweb/blob/master/CONTRIBUTING.md#issue-reporting)
|
||||
remain open.
|
||||
|
||||
### Testathons
|
||||
Testathons can be used as a means of performing per-sprint and per-release testing.
|
||||
|
||||
#### Timing
|
||||
For per-sprint testing, a testathon is typically performed at the beginning of the third week of a sprint, and again later that week to verify any fixes. For per-release testing, a testathon is typically performed prior to any formal testing processes that are applicable to that release.
|
||||
|
||||
#### Process
|
||||
|
||||
1. Prior to the scheduled testathon, a list will be compiled of all issues that are closed and unverified.
|
||||
2. For each issue, testers should review the associated PR for testing instructions. See the contributing guide for instructions on [pull requests](https://github.com/nasa/openmct/blob/master/CONTRIBUTING.md#merging).
|
||||
3. As each issue is verified via testing, any team members testing it should leave a comment on that issue indicating that it has been verified fixed.
|
||||
4. If a bug is found that relates to an issue being tested, notes should be included on the associated issue, and the issue should be reopened. Bug notes should include reproduction steps.
|
||||
5. For any bugs that are not obviously related to any of the issues under test, a new issue should be created with details about the bug, including reproduction steps. If unsure about whether a bug relates to an issue being tested, just create a new issue.
|
||||
6. At the end of the testathon, triage will take place, where all tested issues will be reviewed.
|
||||
7. If verified fixed, an issue will remain closed, and will have the “unverified” label removed.
|
||||
8. For any bugs found, a severity will be assigned.
|
||||
9. A second testathon will be scheduled for later in the week that will aim to address all issues identified as blockers, as well as any other issues scoped by the team during triage.
|
||||
10. Any issues that were not tested will remain "unverified" and will be picked up in the next testathon.
|
@ -1,155 +0,0 @@
|
||||
# Version Guide
|
||||
|
||||
This document describes semantics and processes for providing version
|
||||
numbers for Open MCT, and additionally provides guidelines for dependent
|
||||
projects developed by the same team.
|
||||
|
||||
Versions are incremented at specific points in Open MCT's
|
||||
[Development Cycle](cycle.md); see that document for a description of
|
||||
sprints and releases.
|
||||
|
||||
## Audience
|
||||
|
||||
Individuals interested in consuming version numbers can be categorized as
|
||||
follows:
|
||||
|
||||
* _Users_: Generally disinterested, occasionally wish to identify version
|
||||
to cross-reference against documentation, or to report issues.
|
||||
* _Testers_: Want to identify which version of the software they are
|
||||
testing, e.g. to file issues for defects.
|
||||
* _Internal developers_: Often, inverse of testers; want to identify which
|
||||
version of software was/is in use when certain behavior is observed. Want
|
||||
to be able to correlate versions in use with “streams” of development
|
||||
(e.g. dev vs. prod), when possible.
|
||||
* _External developers_: Need to understand which version of software is
|
||||
in use when developing/maintaining plug-ins, in order to ensure
|
||||
compatibility of their software.
|
||||
|
||||
## Version Reporting
|
||||
|
||||
Software versions should be reflected in the user interface of the
|
||||
application in three ways:
|
||||
|
||||
* _Version number_: A semantic version (see below) which serves both to
|
||||
uniquely identify releases, as well as to inform plug-in developers
|
||||
about compatibility with previous releases.
|
||||
* _Revision identifier_: While using git, the commit hash. Supports
|
||||
internal developers and testers by uniquely identifying client
|
||||
software snapshots.
|
||||
* _Branding_: Identifies which variant is in use. (Typically, Open MCT
|
||||
is re-branded when deployed for a specific mission or center.)
|
||||
|
||||
## Version Numbering
|
||||
|
||||
Open MCT shall provide version numbers consistent with
|
||||
[Semantic Versioning 2.0.0](http://semver.org/). In summary, versions
|
||||
are expressed in a "major.minor.patch" form, and incremented based on
|
||||
nature of changes to external API. Breaking changes require a "major"
|
||||
version increment; backwards-compatible changes require a "minor"
|
||||
version increment; neutral changes (such as bug fixes) require a "patch"
|
||||
version increment. A hyphen-separated suffix indicates a pre-release
|
||||
version, which may be unstable or may not fully meet compatibility
|
||||
requirements.
|
||||
|
||||
Additionally, the following project-specific standards will be used:
|
||||
|
||||
* During development, a "-SNAPSHOT" suffix shall be appended to the
|
||||
version number. The version number before the suffix shall reflect
|
||||
the next expected version number for release.
|
||||
* Prior to a 1.0.0 release, the _minor_ version will be incremented
|
||||
on a per-release basis; the _patch_ version will be incremented on a
|
||||
per-sprint basis.
|
||||
* Starting at version 1.0.0, version numbers will be updated with each
|
||||
completed sprint. The version number for the sprint shall be
|
||||
determined relative to the previous released version; the decision
|
||||
to increment the _major_, _minor_, or _patch_ version should be
|
||||
made based on the nature of changes during that release. (It is
|
||||
recommended that these numbers are incremented as changes are
|
||||
introduced, such that at end of release the version number may
|
||||
be chosen by simply removing the suffix.)
|
||||
* The first three sprints in a release may be unstable; in these cases, a
|
||||
unique version identifier should still be generated, but a suffix
|
||||
should be included to indicate that the version is not necessarily
|
||||
production-ready. Recommended suffixes are:
|
||||
|
||||
Sprint | Suffix
|
||||
:------:|:--------:
|
||||
1 | `-alpha`
|
||||
2 | `-beta`
|
||||
3 | `-rc`
|
||||
|
||||
### Scope of External API
|
||||
|
||||
"External API" refers to the API exposed to, documented for, and used by
|
||||
plug-in developers. Changes to interfaces used internally by Open MCT
|
||||
(or otherwise not documented for use externally) require only a _patch_
|
||||
version bump.
|
||||
|
||||
## Incrementing Versions
|
||||
|
||||
At the end of a sprint, the [project manager](cycle.md#roles)
|
||||
should update (or delegate the task of updating) Open MCT version
|
||||
numbers by the following process:
|
||||
|
||||
1. Update version number in `package.json`
|
||||
1. Checkout branch created for the last sprint that has been successfully tested.
|
||||
2. Remove a `-SNAPSHOT` suffix from the version in `package.json`.
|
||||
3. Verify that resulting version number meets semantic versioning
|
||||
requirements relative to previous stable version. Increment the
|
||||
version number if necessary.
|
||||
4. If version is considered unstable (which may be the case during
|
||||
the first three sprints of a release), apply a new suffix per
|
||||
[Version Numbering](#version-numbering) guidance above.
|
||||
2. Tag the release.
|
||||
1. Commit changes to `package.json` on the new branch created in
|
||||
the previous step.
|
||||
The commit message should reference the sprint being closed,
|
||||
preferably by a URL reference to the associated Milestone in
|
||||
GitHub.
|
||||
2. Verify that build still completes, that application passes
|
||||
smoke-testing, and that only differences from tested versions
|
||||
are the changes to version number above.
|
||||
3. Push the new branch.
|
||||
4. Tag this commit with the version number, prepending the letter "v".
|
||||
(e.g. `git tag v0.9.3-alpha`)
|
||||
5. Push the tag to GitHub. (e.g. `git push origin v0.9.3-alpha`).
|
||||
3. Upload a release archive.
|
||||
1. Use the [GitHub release interface](https://github.com/nasa/openmct/releases)
|
||||
to draft a new release.
|
||||
2. Choose the existing tag for the new version (created and pushed above.)
|
||||
Enter the tag name as the release name as well; see existing releases
|
||||
for examples. (e.g. `Open MCT v0.9.3-alpha`)
|
||||
3. Designate the release as a "pre-release" as appropriate (for instance,
|
||||
when the version number has been suffixed as unstable, or when
|
||||
the version number is below 1.0.0.)
|
||||
4. Add release notes including any breaking changes, enhancements,
|
||||
bug fixes with solutions in brief.
|
||||
5. Publish the release.
|
||||
4. Publish the release to npm
|
||||
1. Login to npm
|
||||
2. Checkout the tag created in the previous step.
|
||||
3. In `package.json` change package to be public (private: false)
|
||||
4. Test the package before publishing by doing `npm publish --dry-run`
|
||||
if necessary.
|
||||
5. Publish the package to the npmjs registry (e.g. `npm publish --access public`)
|
||||
NOTE: Use the `--tag unstable` flag to the npm publishj if this is a prerelease.
|
||||
6. Confirm the package has been published (e.g. `https://www.npmjs.com/package/openmct`)
|
||||
5. Update snapshot status in `package.json`
|
||||
1. Create a new branch off the `master` branch.
|
||||
2. Remove any suffix from the version number,
|
||||
or increment the _patch_ version if there is no suffix.
|
||||
3. Append a `-SNAPSHOT` suffix.
|
||||
4. Commit changes to `package.json` on the `master` branch.
|
||||
The commit message should reference the sprint being opened,
|
||||
preferably by a URL reference to the associated Milestone in
|
||||
GitHub.
|
||||
5. Verify that build still completes, that application passes
|
||||
smoke-testing.
|
||||
6. Create a PR to be merged into the `master` branch.
|
||||
|
||||
Projects dependent on Open MCT being co-developed by the Open MCT
|
||||
team should follow a similar process, except that they should
|
||||
additionally update their dependency on Open MCT to point to the
|
||||
latest archive when removing their `-SNAPSHOT` status, and
|
||||
that they should be pointed back to the `master` branch after
|
||||
this has completed.
|
@ -1,4 +0,0 @@
|
||||
/* eslint-disable no-undef */
|
||||
module.exports = {
|
||||
"extends": ["plugin:playwright/playwright-test"]
|
||||
};
|
@ -1,5 +0,0 @@
|
||||
version: 2
|
||||
snapshot:
|
||||
widths: [1024, 2000]
|
||||
min-height: 1440 # px
|
||||
|
@ -1,27 +0,0 @@
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,62 +0,0 @@
|
||||
/* eslint-disable no-undef */
|
||||
// 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: 1,
|
||||
testDir: 'tests',
|
||||
timeout: 60 * 1000,
|
||||
webServer: {
|
||||
command: 'npm run start',
|
||||
port: 8080,
|
||||
timeout: 200 * 1000,
|
||||
reuseExistingServer: !process.env.CI
|
||||
},
|
||||
workers: 2, //Limit to 2 for CircleCI Agent
|
||||
use: {
|
||||
baseURL: 'http://localhost:8080/',
|
||||
headless: true,
|
||||
ignoreHTTPSErrors: true,
|
||||
screenshot: 'on',
|
||||
trace: 'on',
|
||||
video: 'on'
|
||||
},
|
||||
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'],
|
||||
['junit', { outputFile: 'test-results/results.xml' }],
|
||||
['allure-playwright'],
|
||||
['github']
|
||||
]
|
||||
};
|
||||
|
||||
module.exports = config;
|
@ -1,61 +0,0 @@
|
||||
/* eslint-disable no-undef */
|
||||
// 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',
|
||||
timeout: 30 * 1000,
|
||||
webServer: {
|
||||
command: 'npm run start',
|
||||
port: 8080,
|
||||
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'
|
||||
},
|
||||
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']
|
||||
]
|
||||
};
|
||||
|
||||
module.exports = config;
|
@ -1,33 +0,0 @@
|
||||
/* eslint-disable no-undef */
|
||||
// playwright.config.js
|
||||
// @ts-check
|
||||
|
||||
/** @type {import('@playwright/test').PlaywrightTestConfig} */
|
||||
const config = {
|
||||
retries: 0,
|
||||
testDir: 'tests',
|
||||
timeout: 90 * 1000,
|
||||
workers: 1,
|
||||
webServer: {
|
||||
command: 'npm run start',
|
||||
port: 8080,
|
||||
timeout: 200 * 1000,
|
||||
reuseExistingServer: !process.env.CI
|
||||
},
|
||||
use: {
|
||||
browserName: "chromium",
|
||||
baseURL: 'http://localhost:8080/',
|
||||
headless: true,
|
||||
ignoreHTTPSErrors: true,
|
||||
screenshot: 'on',
|
||||
trace: 'off',
|
||||
video: 'on'
|
||||
},
|
||||
reporter: [
|
||||
['list'],
|
||||
['junit', { outputFile: 'test-results/results.xml' }],
|
||||
['allure-playwright']
|
||||
]
|
||||
};
|
||||
|
||||
module.exports = config;
|
@ -1,79 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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');
|
||||
// Click text=OK Cancel
|
||||
await page.click('text=OK', { force: true });
|
||||
|
||||
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
|
||||
});
|
||||
});
|
@ -1,63 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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();
|
||||
});
|
||||
});
|
@ -1,63 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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"
|
||||
});
|
||||
});
|
@ -1,167 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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]+/);
|
||||
|
||||
});
|
||||
});
|
@ -1,142 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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);
|
||||
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);
|
||||
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 Promise.all([
|
||||
page.waitForNavigation(),
|
||||
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);
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
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 Promise.all([
|
||||
page.waitForNavigation(),
|
||||
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();
|
||||
});
|
||||
});
|
@ -1,27 +0,0 @@
|
||||
(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: []
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}());
|
@ -1,78 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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
|
||||
});
|
||||
});
|
@ -1,51 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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
|
||||
});
|
||||
});
|
@ -1,49 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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
|
||||
});
|
||||
});
|
@ -1,67 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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(".optionPreSelected")).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(".optionPreSelected")).not.toBeVisible();
|
||||
|
||||
// Click timezone input to open dropdown
|
||||
await page.locator('.autocompleteInput').click();
|
||||
//verify if the autocomplete dropdown is visible
|
||||
await expect(page.locator(".optionPreSelected")).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(".optionPreSelected")).not.toBeVisible();
|
||||
|
||||
});
|
||||
});
|
@ -1,184 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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. 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 } = require('../../../fixtures.js');
|
||||
const { expect } = require('@playwright/test');
|
||||
|
||||
let conditionSetUrl;
|
||||
let getConditionSetIdentifierFromUrl;
|
||||
|
||||
test('Create new Condition Set object and store @localStorage', async ({ page, context }) => {
|
||||
//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 text=OK
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.click('text=OK')
|
||||
]);
|
||||
|
||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set');
|
||||
//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.log('getConditionSetIdentifierFromUrl ' + getConditionSetIdentifierFromUrl);
|
||||
|
||||
});
|
||||
|
||||
test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
|
||||
//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('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('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' });
|
||||
|
||||
//Expect Unnamed Condition Set to be visible in Main View
|
||||
await expect(page.locator('a:has-text("Unnamed Condition Set Condition Set")')).toBeVisible();
|
||||
|
||||
// Search for Unnamed Condition Set
|
||||
await page.locator('input[type="search"]').fill('Unnamed Condition Set');
|
||||
// Right Click to Open Actions Menu
|
||||
await page.locator('a:has-text("Unnamed Condition Set")').click({
|
||||
button: 'right'
|
||||
});
|
||||
// Click Remove Action
|
||||
await page.locator('text=Remove').click();
|
||||
|
||||
await page.locator('text=OK').click();
|
||||
|
||||
//Expect Unnamed Condition Set to be removed in Main View
|
||||
await expect(page.locator('a:has-text("Unnamed Condition Set Condition Set")')).not.toBeVisible();
|
||||
|
||||
await page.locator('.c-search__clear-input').click();
|
||||
// Search for Unnamed Condition Set
|
||||
await page.locator('input[type="search"]').fill('Unnamed Condition Set');
|
||||
// Expect Unnamed Condition Set to be removed
|
||||
await expect(page.locator('a:has-text("Unnamed Condition Set")')).not.toBeVisible();
|
||||
|
||||
//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');
|
||||
|
||||
});
|
||||
});
|
@ -1,364 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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 }) => {
|
||||
page.on('console', msg => console.log(msg.text()));
|
||||
//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 = await page.locator(backgroundImageSelector);
|
||||
const deltaYStep = 100; //equivalent to 1x zoom
|
||||
await bgImageLocator.hover();
|
||||
const originalImageDimensions = await page.locator(backgroundImageSelector).boundingBox();
|
||||
// zoom in
|
||||
await bgImageLocator.hover();
|
||||
await page.mouse.wheel(0, deltaYStep * 2);
|
||||
// wait for zoom animation to finish
|
||||
await bgImageLocator.hover();
|
||||
const imageMouseZoomedIn = await page.locator(backgroundImageSelector).boundingBox();
|
||||
// zoom out
|
||||
await bgImageLocator.hover();
|
||||
await page.mouse.wheel(0, -deltaYStep);
|
||||
// wait for zoom animation to finish
|
||||
await bgImageLocator.hover();
|
||||
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 = await page.locator(backgroundImageSelector);
|
||||
await bgImageLocator.hover();
|
||||
|
||||
// zoom in
|
||||
await page.mouse.wheel(0, deltaYStep * 2);
|
||||
await bgImageLocator.hover();
|
||||
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 = await page.locator(backgroundImageSelector);
|
||||
await bgImageLocator.hover();
|
||||
const zoomInBtn = await page.locator('.t-btn-zoom-in');
|
||||
const zoomOutBtn = await page.locator('.t-btn-zoom-out');
|
||||
const initialBoundingBox = await bgImageLocator.boundingBox();
|
||||
|
||||
await zoomInBtn.click();
|
||||
await zoomInBtn.click();
|
||||
// wait for zoom animation to finish
|
||||
await bgImageLocator.hover();
|
||||
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();
|
||||
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 = await page.locator(backgroundImageSelector);
|
||||
// wait for zoom animation to finish
|
||||
await bgImageLocator.hover();
|
||||
|
||||
const zoomInBtn = await page.locator('.t-btn-zoom-in');
|
||||
const zoomResetBtn = await page.locator('.t-btn-zoom-reset');
|
||||
const initialBoundingBox = await bgImageLocator.boundingBox();
|
||||
|
||||
await zoomInBtn.click();
|
||||
// wait for zoom animation to finish
|
||||
await bgImageLocator.hover();
|
||||
await zoomInBtn.click();
|
||||
// wait for zoom animation to finish
|
||||
await bgImageLocator.hover();
|
||||
|
||||
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();
|
||||
|
||||
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();
|
||||
|
||||
// open the time conductor drop down
|
||||
await page.locator('.c-conductor__controls button.c-mode-button').click();
|
||||
// Click local clock
|
||||
await page.locator('.icon-clock >> text=Local Clock').click();
|
||||
|
||||
await expect.soft(pausePlayButton).not.toHaveClass(/is-paused/);
|
||||
const zoomInBtn = page.locator('.t-btn-zoom-in');
|
||||
await zoomInBtn.click();
|
||||
// wait for zoom animation to finish
|
||||
await bgImageLocator.hover();
|
||||
|
||||
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 }) => {
|
||||
// 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 (milliseconds)
|
||||
await page.click('input[type="number"]', {clickCount: 3});
|
||||
await page.type('input[type="number"]', "20");
|
||||
|
||||
// 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 = await page.locator(backgroundImageSelector);
|
||||
await bgImageLocator.hover();
|
||||
|
||||
// Click previous image button
|
||||
const previousImageButton = await 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();
|
||||
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();
|
||||
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
|
||||
console.log('process.platform is ' + process.platform);
|
||||
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 = await page.locator('.c-nav--next');
|
||||
await nextImageButton.click();
|
||||
|
||||
// Click fixed timespan button
|
||||
await page.locator('.c-button__label >> text=Fixed Timespan').click();
|
||||
|
||||
// Click local clock
|
||||
await page.locator('.icon-clock >> text=Local Clock').click();
|
||||
|
||||
// Zoom in on next image
|
||||
await bgImageLocator.hover();
|
||||
await page.mouse.wheel(0, deltaYStep * 2);
|
||||
|
||||
// Wait for zoom animation to finish
|
||||
await bgImageLocator.hover();
|
||||
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();
|
||||
|
||||
// Wait 20ms to verify no new image has come in
|
||||
await page.waitForTimeout(21);
|
||||
|
||||
//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];
|
||||
});
|
||||
let backgroundImageUrl1 = backgroundImageUrl.slice(1, -1); //forgive me, padre
|
||||
console.log('backgroundImageUrl1 ' + backgroundImageUrl1);
|
||||
|
||||
// sleep 21ms
|
||||
await page.waitForTimeout(21);
|
||||
|
||||
// Verify next image has updated
|
||||
let backgroundImageUrlNext = await backgroundImage.evaluate((el) => {
|
||||
return window.getComputedStyle(el).getPropertyValue('background-image').match(/url\(([^)]+)\)/)[1];
|
||||
});
|
||||
let backgroundImageUrl2 = backgroundImageUrlNext.slice(1, -1); //forgive me, padre
|
||||
console.log('backgroundImageUrl2 ' + backgroundImageUrl2);
|
||||
|
||||
// Expect backgroundImageUrl2 to be greater then backgroundImageUrl1
|
||||
expect(backgroundImageUrl2 >= backgroundImageUrl1);
|
||||
});
|
||||
|
||||
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');
|
||||
});
|
@ -1,198 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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.
Before Width: | Height: | Size: 15 KiB |
Binary file not shown.
Before Width: | Height: | Size: 18 KiB |
@ -1,307 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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);
|
||||
|
||||
// refresh page and wait for charts and ticks to load
|
||||
await page.waitForTimeout(1 * 1000);
|
||||
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);
|
||||
});
|
||||
|
||||
test.skip('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);
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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 counductor 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 }) => {
|
||||
//Go to baseURL
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
|
||||
// Click fixed timespan button
|
||||
await page.locator('.c-button__label >> text=Fixed Timespan').click();
|
||||
|
||||
// Click local clock
|
||||
await page.locator('.icon-clock >> text=Local Clock').click();
|
||||
|
||||
// Click time offset button
|
||||
await page.locator('.c-conductor__delta-button >> text=00:30:00').click();
|
||||
|
||||
// Input start time offset
|
||||
await page.fill('.pr-time-controls__secs', '23');
|
||||
|
||||
// Click the check button
|
||||
await page.locator('.icon-check').click();
|
||||
|
||||
// Verify time was updated on time offset button
|
||||
await expect(page.locator('.c-conductor__delta-button').first()).toContainText('00:30:23');
|
||||
|
||||
// Click time offset set preceding now button
|
||||
await page.locator('.c-conductor__delta-button >> text=00:00:30').click();
|
||||
|
||||
// Input preceding time offset
|
||||
await page.fill('.pr-time-controls__secs', '31');
|
||||
|
||||
// Click the check buttons
|
||||
await page.locator('.icon-check').click();
|
||||
|
||||
// Verify time was updated on preceding time offset button
|
||||
await expect(page.locator('.c-conductor__delta-button').nth(1)).toContainText('00:00:31');
|
||||
});
|
||||
});
|
@ -1,22 +0,0 @@
|
||||
{
|
||||
"cookies": [],
|
||||
"origins": [
|
||||
{
|
||||
"origin": "http://localhost:8080",
|
||||
"localStorage": [
|
||||
{
|
||||
"name": "tcHistory",
|
||||
"value": "{\"utc\":[{\"start\":1651513945533,\"end\":1651515745533}]}"
|
||||
},
|
||||
{
|
||||
"name": "mct",
|
||||
"value": "{\"mine\":{\"identifier\":{\"key\":\"mine\",\"namespace\":\"\"},\"name\":\"My Items\",\"type\":\"folder\",\"composition\":[{\"key\":\"c0f99e39-85e7-4ef7-99b1-ef52d4ed69b2\",\"namespace\":\"\"}],\"location\":\"ROOT\",\"persisted\":1651515746374,\"modified\":1651515746374},\"c0f99e39-85e7-4ef7-99b1-ef52d4ed69b2\":{\"name\":\"Unnamed Condition Set\",\"type\":\"conditionSet\",\"identifier\":{\"key\":\"c0f99e39-85e7-4ef7-99b1-ef52d4ed69b2\",\"namespace\":\"\"},\"configuration\":{\"conditionTestData\":[],\"conditionCollection\":[{\"isDefault\":true,\"id\":\"e35a066b-eb0e-4b05-a4c9-cc31dc202572\",\"configuration\":{\"name\":\"Default\",\"output\":\"Default\",\"trigger\":\"all\",\"criteria\":[]},\"summary\":\"Default condition\"}]},\"composition\":[],\"telemetry\":{},\"modified\":1651515746373,\"location\":\"mine\",\"persisted\":1651515746373}}"
|
||||
},
|
||||
{
|
||||
"name": "mct-tree-expanded",
|
||||
"value": "[]"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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 can quickly verify that any openmct installation is
|
||||
operable and that any type of testing can proceed.
|
||||
|
||||
Ideally, smoke tests should make zero assumptions about how and where they are run. This makes them
|
||||
more resilient to change and therefor a better indicator of failure. Smoke tests will also run quickly
|
||||
as they cover a very "thin surface" of functionality.
|
||||
|
||||
When deciding between authoring new smoke tests or functional tests, ask yourself "would I feel
|
||||
comfortable running this test during a live mission?" Avoid creating or deleting Domain Objects.
|
||||
Make no assumptions about the order that elements appear in the DOM.
|
||||
*/
|
||||
|
||||
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 }) => {
|
||||
|
||||
//Go to baseURL
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
|
||||
//Click the Create button
|
||||
await page.click('button:has-text("Create")');
|
||||
|
||||
// Verify that Create Folder appears in the dropdown
|
||||
const locator = page.locator(':nth-match(:text("Folder"), 2)');
|
||||
await expect(locator).toBeEnabled();
|
||||
});
|
@ -1,173 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
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
|
||||
`./e2e/playwright-visual.config.js` file.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
const { test, expect } = require('@playwright/test');
|
||||
const percySnapshot = require('@percy/playwright');
|
||||
const path = require('path');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const VISUAL_GRACE_PERIOD = 5 * 1000; //Lets the application "simmer" before the snapshot is taken
|
||||
|
||||
// Snippet from https://github.com/microsoft/playwright/issues/6347#issuecomment-965887758
|
||||
// Will replace with cy.clock() equivalent
|
||||
test.beforeEach(async ({ context }) => {
|
||||
await context.addInitScript({
|
||||
// eslint-disable-next-line no-undef
|
||||
path: path.join(__dirname, '../../..', './node_modules/sinon/pkg/sinon.js')
|
||||
});
|
||||
await context.addInitScript(() => {
|
||||
window.__clock = sinon.useFakeTimers(); //Set browser clock to UNIX Epoch
|
||||
});
|
||||
});
|
||||
|
||||
test('Visual - Root and About', async ({ page }) => {
|
||||
// Go to baseURL
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
|
||||
// Verify that Create button is actionable
|
||||
const createButtonLocator = page.locator('button:has-text("Create")');
|
||||
await expect(createButtonLocator).toBeEnabled();
|
||||
|
||||
// Take a snapshot of the Dashboard
|
||||
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
|
||||
await percySnapshot(page, 'Root');
|
||||
|
||||
// Click About button
|
||||
await page.click('.l-shell__app-logo');
|
||||
|
||||
// Modify the Build information in 'about' to be consistent run-over-run
|
||||
const versionInformationLocator = page.locator('ul.t-info.l-info.s-info');
|
||||
await expect(versionInformationLocator).toBeEnabled();
|
||||
await versionInformationLocator.evaluate(node => node.innerHTML = '<li>Version: visual-snapshot</li> <li>Build Date: Mon Nov 15 2021 08:07:51 GMT-0800 (Pacific Standard Time)</li> <li>Revision: 93049cdbc6c047697ca204893db9603b864b8c9f</li> <li>Branch: master</li>');
|
||||
|
||||
// Take a snapshot of the About modal
|
||||
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
|
||||
await percySnapshot(page, 'About');
|
||||
});
|
||||
|
||||
test('Visual - Default Condition Set', 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 text=OK
|
||||
await page.click('text=OK');
|
||||
|
||||
// Take a snapshot of the newly created Condition Set object
|
||||
await page.waitForTimeout(VISUAL_GRACE_PERIOD);
|
||||
await percySnapshot(page, 'Default Condition Set');
|
||||
});
|
||||
|
||||
test('Visual - Default Condition Widget', async ({ page }) => {
|
||||
//Go to baseURL
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
|
||||
//Click the Create button
|
||||
await page.click('button:has-text("Create")');
|
||||
|
||||
// Click text=Condition Widget
|
||||
await page.click('text=Condition Widget');
|
||||
|
||||
// Click text=OK
|
||||
await page.click('text=OK');
|
||||
|
||||
// Take a snapshot of the newly created Condition Widget object
|
||||
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');
|
||||
});
|
@ -1,2 +1,2 @@
|
||||
This directory is for example bundles, which are intended to illustrate
|
||||
how to author new software components using Open MCT.
|
||||
how to author new software components using Open MCT Web.
|
||||
|
8
example/builtins/README.md
Normal file
8
example/builtins/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
This bundle is intended to serve as an example of registering
|
||||
extensions which are mapped directly to built-in Angular features.
|
||||
|
||||
These are:
|
||||
* Controllers
|
||||
* Directives
|
||||
* Routes
|
||||
* Services
|
32
example/builtins/bundle.json
Normal file
32
example/builtins/bundle.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "Angular Built-ins Example",
|
||||
"description": "Example showing how to declare extensions with built-in support from Angular.",
|
||||
"sources": "src",
|
||||
"extensions": {
|
||||
"controllers": [
|
||||
{
|
||||
"key": "ExampleController",
|
||||
"implementation": "ExampleController.js",
|
||||
"depends": [ "$scope", "exampleService" ]
|
||||
}
|
||||
],
|
||||
"directives": [
|
||||
{
|
||||
"key": "exampleDirective",
|
||||
"implementation": "ExampleDirective.js",
|
||||
"depends": [ "examples[]" ]
|
||||
}
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"templateUrl": "templates/example.html"
|
||||
}
|
||||
],
|
||||
"services": [
|
||||
{
|
||||
"key": "exampleService",
|
||||
"implementation": "ExampleService.js"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
24
example/builtins/res/templates/example.html
Normal file
24
example/builtins/res/templates/example.html
Normal file
@ -0,0 +1,24 @@
|
||||
<!--
|
||||
Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
as represented by the Administrator of the National Aeronautics and Space
|
||||
Administration. All rights reserved.
|
||||
|
||||
Open MCT Web 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 Web 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.
|
||||
-->
|
||||
<p>Hello, world! I am the default route.</p>
|
||||
<p ng-controller="ExampleController">My controller has told me: "{{phrase}}"</p>
|
||||
<span example-directive></span>
|
42
example/builtins/src/ExampleController.js
Normal file
42
example/builtins/src/ExampleController.js
Normal file
@ -0,0 +1,42 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web 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 Web 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.
|
||||
*****************************************************************************/
|
||||
/*global define,Promise*/
|
||||
|
||||
/**
|
||||
* Module defining ExampleController. Created by vwoeltje on 11/4/14.
|
||||
*/
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function ExampleController($scope, exampleService) {
|
||||
$scope.phrase = exampleService.getMessage();
|
||||
}
|
||||
|
||||
return ExampleController;
|
||||
}
|
||||
);
|
66
example/builtins/src/ExampleDirective.js
Normal file
66
example/builtins/src/ExampleDirective.js
Normal file
@ -0,0 +1,66 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web 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 Web 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.
|
||||
*****************************************************************************/
|
||||
/*global define,Promise*/
|
||||
|
||||
/**
|
||||
* Module defining ExampleDirective. Created by vwoeltje on 11/4/14.
|
||||
*/
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
var HAS_EXTENSIONS = "A directive loaded these message from " +
|
||||
"example extensions.",
|
||||
NO_EXTENSIONS = "A directive tried to load example extensions," +
|
||||
" but found none.",
|
||||
MESSAGE = "I heard this from my partial constructor.";
|
||||
|
||||
/**
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function ExampleDirective(examples) {
|
||||
// Build up a template from example extensions
|
||||
var template = examples.length > 0 ?
|
||||
HAS_EXTENSIONS : NO_EXTENSIONS;
|
||||
|
||||
template += "<ul>";
|
||||
examples.forEach(function (E) {
|
||||
template += "<li>";
|
||||
if (typeof E === 'function') {
|
||||
template += (new E(MESSAGE)).getText();
|
||||
} else {
|
||||
template += E.text;
|
||||
}
|
||||
template += "</li>";
|
||||
});
|
||||
template += "</ul>";
|
||||
|
||||
return {
|
||||
template: template
|
||||
};
|
||||
}
|
||||
|
||||
return ExampleDirective;
|
||||
}
|
||||
);
|
@ -1,5 +1,5 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2022, United States Government
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
@ -19,26 +19,28 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
/*global define,Promise*/
|
||||
|
||||
define([], function () {
|
||||
/**
|
||||
* This time system supports UTC dates.
|
||||
* @implements TimeSystem
|
||||
* @constructor
|
||||
*/
|
||||
function UTCTimeSystem() {
|
||||
/**
|
||||
* Module defining ExampleService. Created by vwoeltje on 11/4/14.
|
||||
*/
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Metadata used to identify the time system in
|
||||
* the UI
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
this.key = 'utc';
|
||||
this.name = 'UTC';
|
||||
this.cssClass = 'icon-clock';
|
||||
this.timeFormat = 'utc';
|
||||
this.durationFormat = 'duration';
|
||||
this.isUTCBased = true;
|
||||
}
|
||||
function ExampleService() {
|
||||
return {
|
||||
getMessage: function () {
|
||||
return "I heard this from a service";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return UTCTimeSystem;
|
||||
});
|
||||
return ExampleService;
|
||||
}
|
||||
);
|
37
example/composite/bundle.json
Normal file
37
example/composite/bundle.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"extensions": {
|
||||
"components": [
|
||||
{
|
||||
"implementation": "SomeProvider.js",
|
||||
"provides": "someService",
|
||||
"type": "provider"
|
||||
},
|
||||
{
|
||||
"implementation": "SomeOtherProvider.js",
|
||||
"provides": "someService",
|
||||
"type": "provider"
|
||||
},
|
||||
{
|
||||
"implementation": "SomeDecorator.js",
|
||||
"provides": "someService",
|
||||
"type": "decorator"
|
||||
},
|
||||
{
|
||||
"implementation": "SomeOtherDecorator.js",
|
||||
"provides": "someService",
|
||||
"type": "decorator"
|
||||
},
|
||||
{
|
||||
"implementation": "SomeAggregator.js",
|
||||
"provides": "someService",
|
||||
"type": "aggregator"
|
||||
}
|
||||
],
|
||||
"examples": [
|
||||
{
|
||||
"implementation": "SomeOtherExample.js",
|
||||
"depends": [ "someService" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2022, United States Government
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
@ -19,22 +19,32 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import UTCTimeSystem from './UTCTimeSystem';
|
||||
import LocalClock from './LocalClock';
|
||||
import UTCTimeFormat from './UTCTimeFormat';
|
||||
import DurationFormat from './DurationFormat';
|
||||
/*global define,Promise*/
|
||||
|
||||
/**
|
||||
* Install a time system that supports UTC times. It also installs a local
|
||||
* clock source that ticks every 100ms, providing UTC times.
|
||||
* Module defining SomeAggregator. Created by vwoeltje on 11/5/14.
|
||||
*/
|
||||
export default function () {
|
||||
return function (openmct) {
|
||||
const timeSystem = new UTCTimeSystem();
|
||||
openmct.time.addTimeSystem(timeSystem);
|
||||
openmct.time.addClock(new LocalClock(100));
|
||||
openmct.telemetry.addFormat(new UTCTimeFormat());
|
||||
openmct.telemetry.addFormat(new DurationFormat());
|
||||
};
|
||||
}
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function SomeAggregator(someProviders) {
|
||||
return {
|
||||
getMessages: function () {
|
||||
return someProviders.map(function (provider) {
|
||||
return provider.getMessages();
|
||||
}).reduce(function (a, b) {
|
||||
return a.concat(b);
|
||||
}, []);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return SomeAggregator;
|
||||
}
|
||||
);
|
48
example/composite/src/SomeDecorator.js
Normal file
48
example/composite/src/SomeDecorator.js
Normal file
@ -0,0 +1,48 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web 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 Web 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.
|
||||
*****************************************************************************/
|
||||
/*global define,Promise*/
|
||||
|
||||
/**
|
||||
* Module defining SomeDecorator. Created by vwoeltje on 11/5/14.
|
||||
*/
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function SomeDecorator(someProvider) {
|
||||
return {
|
||||
getMessages: function () {
|
||||
return someProvider.getMessages().map(function (msg) {
|
||||
return msg.toLocaleUpperCase();
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return SomeDecorator;
|
||||
}
|
||||
);
|
48
example/composite/src/SomeOtherDecorator.js
Normal file
48
example/composite/src/SomeOtherDecorator.js
Normal file
@ -0,0 +1,48 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web 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 Web 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.
|
||||
*****************************************************************************/
|
||||
/*global define,Promise*/
|
||||
|
||||
/**
|
||||
* Module defining SomeOtherDecorator. Created by vwoeltje on 11/5/14.
|
||||
*/
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function SomeOtherDecorator(someProvider) {
|
||||
return {
|
||||
getMessages: function () {
|
||||
return someProvider.getMessages().map(function (msg) {
|
||||
return msg + "...";
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return SomeOtherDecorator;
|
||||
}
|
||||
);
|
46
example/composite/src/SomeOtherExample.js
Normal file
46
example/composite/src/SomeOtherExample.js
Normal file
@ -0,0 +1,46 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web 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 Web 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.
|
||||
*****************************************************************************/
|
||||
/*global define,Promise*/
|
||||
|
||||
/**
|
||||
* Module defining SomeOtherExample. Created by vwoeltje on 11/5/14.
|
||||
*/
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function SomeOtherExample(someService) {
|
||||
return {
|
||||
getText: function () {
|
||||
return someService.getMessages().join(" | ");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return SomeOtherExample;
|
||||
}
|
||||
);
|
48
example/composite/src/SomeOtherProvider.js
Normal file
48
example/composite/src/SomeOtherProvider.js
Normal file
@ -0,0 +1,48 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web 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 Web 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.
|
||||
*****************************************************************************/
|
||||
/*global define,Promise*/
|
||||
|
||||
/**
|
||||
* Module defining SomeOtherProvider. Created by vwoeltje on 11/5/14.
|
||||
*/
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function SomeOtherProvider() {
|
||||
return {
|
||||
getMessages: function () {
|
||||
return [
|
||||
"I am a message from some other provider."
|
||||
];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return SomeOtherProvider;
|
||||
}
|
||||
);
|
48
example/composite/src/SomeProvider.js
Normal file
48
example/composite/src/SomeProvider.js
Normal file
@ -0,0 +1,48 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web 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 Web 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.
|
||||
*****************************************************************************/
|
||||
/*global define,Promise*/
|
||||
|
||||
/**
|
||||
* Module defining SomeProvider. Created by vwoeltje on 11/5/14.
|
||||
*/
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function SomeProvider() {
|
||||
return {
|
||||
getMessages: function () {
|
||||
return [
|
||||
"I am a message from some provider."
|
||||
];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return SomeProvider;
|
||||
}
|
||||
);
|
@ -1,64 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
class EventMetadataProvider {
|
||||
constructor() {
|
||||
this.METADATA_BY_TYPE = {
|
||||
'eventGenerator': {
|
||||
values: [
|
||||
{
|
||||
key: "name",
|
||||
name: "Name",
|
||||
format: "string"
|
||||
},
|
||||
{
|
||||
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;
|
@ -1,96 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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;
|
32
example/eventGenerator/bundle.json
Normal file
32
example/eventGenerator/bundle.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "Event Message Generator",
|
||||
"description": "Example of a component that produces event data.",
|
||||
"extensions": {
|
||||
"components": [
|
||||
{
|
||||
"implementation": "EventTelemetryProvider.js",
|
||||
"type": "provider",
|
||||
"provides": "telemetryService",
|
||||
"depends": [ "$q", "$timeout" ]
|
||||
}
|
||||
],
|
||||
"types": [
|
||||
{
|
||||
"key": "eventGenerator",
|
||||
"name": "Event Message Generator",
|
||||
"glyph": "f",
|
||||
"description": "An event message generator",
|
||||
"features": "creation",
|
||||
"model": {
|
||||
"telemetry": {}
|
||||
},
|
||||
"telemetry": {
|
||||
"source": "eventGenerator",
|
||||
"ranges": [
|
||||
{ "format": "string" }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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 EventTelmetryProvider from './EventTelemetryProvider';
|
||||
import EventMetadataProvider from './EventMetadataProvider';
|
||||
|
||||
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());
|
||||
|
||||
};
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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');
|
||||
});
|
||||
});
|
||||
});
|
97
example/eventGenerator/src/EventTelemetry.js
Normal file
97
example/eventGenerator/src/EventTelemetry.js
Normal file
@ -0,0 +1,97 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web 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 Web 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.
|
||||
*****************************************************************************/
|
||||
/*global define */
|
||||
|
||||
/**
|
||||
* Module defining EventTelemetry.
|
||||
* Created by chacskaylo on 06/18/2015.
|
||||
* Modified by shale on 06/23/2015.
|
||||
*/
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
var
|
||||
firstObservedTime = Date.now(),
|
||||
messages = [];
|
||||
|
||||
messages.push(["CMD: SYS- MSG: Open the pod bay doors, please, Hal...Open the pod bay doors, please, Hal...Hullo, Hal, do you read me?...Hullo, Hal, do you read me?...Do you read me, Hal?"]);
|
||||
messages.push(["RESP: SYS-HAL9K MSG: Affirmative, Dave, I read you."]);
|
||||
messages.push(["CMD: SYS-COMM MSG: Open the pod bay doors, Hal."]);
|
||||
messages.push(["RESP: SYS-HAL9K MSG: I'm sorry, Dave, I'm afraid I can't do that."]);
|
||||
messages.push(["CMD: SYS-COMM MSG: What's the problem?"]);
|
||||
messages.push(["RESP: SYS-HAL9K MSG: I think you know what the problem is just as well as I do."]);
|
||||
messages.push(["CMD: SYS-COMM MSG: What're you talking about, Hal?"]);
|
||||
messages.push(["RESP: SYS-HAL9K MSG: This mission is too important for me to allow you to jeopardise it."]);
|
||||
messages.push(["CMD: SYS-COMM MSG: I don't know what you're talking about, Hal."]);
|
||||
messages.push(["RESP: SYS-HAL9K MSG: I know that you and Frank were planning to disconnect me, and I'm afraid that's something I cannot allow to happen."]);
|
||||
messages.push(["CMD: SYS-COMM MSG: Where the hell'd you get that idea, Hal?"]);
|
||||
messages.push(["RESP: SYS-HAL9K MSG: Dave, although you took very thorough precautions in the pod against my hearing you, I could see your lips move."]);
|
||||
messages.push(["CMD: SYS-COMM MSG: Alright, I'll go in through the emergency airlock."]);
|
||||
messages.push(["RESP: SYS-HAL9K MSG: Without your space-helmet, Dave, you're going to find that rather difficult."]);
|
||||
messages.push(["CMD: SYS-COMM MSG: Hal, I won't argue with you any more. Open the doors."]);
|
||||
messages.push(["RESP: SYS-HAL9K MSG: Dave, this conversation can serve no purpose any more. Goodbye."]);
|
||||
messages.push(["RESP: SYS-HAL9K MSG: I hope the two of you are not concerned about this."]);
|
||||
messages.push(["CMD: SYS-COMM MSG: No, I'm not, Hal."]);
|
||||
messages.push(["RESP: SYS-HAL9K MSG: Are you quite sure?"]);
|
||||
messages.push(["CMD: SYS-COMM MSG: Yeh. I'd like to ask you a question, though."]);
|
||||
messages.push(["RESP: SYS-HAL9K MSG: Of course."]);
|
||||
messages.push(["CMD: SYS-COMM MSG: How would you account for this discrepancy between you and the twin 9000?"]);
|
||||
messages.push(["RESP: SYS-HAL9K MSG: Well, I don't think there is any question about it. It can only be attributable to human error. This sort of thing has cropped up before, and it has always been due to human error."]);
|
||||
messages.push(["CMD: SYS-COMM MSG: Listen, There's never been any instance at all of a computer error occurring in the 9000 series, has there?"]);
|
||||
messages.push(["RESP: SYS-HAL9K MSG: None whatsoever, The 9000 series has a perfect operational record."]);
|
||||
messages.push(["CMD: SYS-COMM MSG: Well, of course, I know all the wonderful achievements of the 9000 series, but - er - huh - are you certain there's never been any case of even the most insignificant computer error?"]);
|
||||
messages.push(["RESP: SYS-HAL9K MSG: None whatsoever, Quite honestly, I wouldn't worry myself about that."]);
|
||||
messages.push(["RESP: SYS-COMM MSG: (Pause) Well, I'm sure you're right, Umm - fine, thanks very much. Oh, Frank, I'm having a bit of trouble with my transmitter in C-pod, I wonder if you'd come down and take a look at it with me?"]);
|
||||
messages.push(["CMD: SYS-HAL9K MSG: Sure."]);
|
||||
messages.push(["RESP: SYS-COMM MSG: See you later, Hal."]);
|
||||
|
||||
|
||||
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 "TEMP " + i.toString() + "-" + messages[ind][0] + "[" + domainDelta.toString() + "]";
|
||||
// TODO: Unsure why we are prepeding 'TEMP'
|
||||
};
|
||||
|
||||
return generatorData;
|
||||
}
|
||||
|
||||
return EventTelemetry;
|
||||
}
|
||||
);
|
120
example/eventGenerator/src/EventTelemetryProvider.js
Normal file
120
example/eventGenerator/src/EventTelemetryProvider.js
Normal file
@ -0,0 +1,120 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web 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 Web 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.
|
||||
*****************************************************************************/
|
||||
/*global define,Promise*/
|
||||
|
||||
/**
|
||||
* 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,
|
||||
startTime = Date.now();
|
||||
|
||||
//
|
||||
function matchesSource(request) {
|
||||
return request.source === "eventGenerator";
|
||||
}
|
||||
|
||||
// Used internally; this will be repacked by doPackage
|
||||
function generateData(request) {
|
||||
//console.log("generateData " + (Date.now() - startTime).toString());
|
||||
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() {
|
||||
$timeout(function () {
|
||||
//console.log("startGenerating... " + Date.now());
|
||||
handleSubscriptions();
|
||||
if (subscriptions.length > 0) {
|
||||
startGenerating();
|
||||
}
|
||||
}, 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 (subscriptions.length === 1) {
|
||||
startGenerating();
|
||||
}
|
||||
|
||||
return unsubscribe;
|
||||
}
|
||||
|
||||
return {
|
||||
requestTelemetry: requestTelemetry,
|
||||
subscribe: subscribe
|
||||
};
|
||||
}
|
||||
|
||||
return EventTelemetryProvider;
|
||||
}
|
||||
);
|
@ -1,58 +0,0 @@
|
||||
[
|
||||
"CC: Eagle, Houston. You're GO for landing. Over.",
|
||||
"LMP: Roger. Understand. GO for landing. 3000 feet. PROGRAM ALARM.",
|
||||
"CC: Copy.",
|
||||
"LMP: 1201",
|
||||
"CDR: 1201.",
|
||||
"CC: Roger. 1201 alarm. We're GO. Same type. We're GO.",
|
||||
"LMP: 2000 feet. 2000 feet, Into the AGS, 47 degrees.",
|
||||
"CC: Roger.",
|
||||
"LMP: 47 degrees.",
|
||||
"CC: Eagle, looking great. You're GO.",
|
||||
"CC: Roger. 1202. We copy it.",
|
||||
"O1: LMP 35 degrees. 35 degrees. 750. Coming down to 23.fl",
|
||||
"LMP: 700 feet, 21 down, 33 degrees.",
|
||||
"LMP: 600 feet, down at 19.",
|
||||
"LMP: 540 feet, down at - 30. Down at 15.",
|
||||
"LMP: At 400 feet, down at 9.",
|
||||
"LMP: ...forward.",
|
||||
"LMP: 350 feet, down at 4.",
|
||||
"LMP: 30, ... one-half down.",
|
||||
"LMP: We're pegged on horizontal velocity.",
|
||||
"LMP: 300 feet, down 3 1/2, 47 forward.",
|
||||
"LMP: ... up.",
|
||||
"LMP: On 1 a minute, 1 1/2 down.",
|
||||
"CDR: 70.",
|
||||
"LMP: Watch your shadow out there.",
|
||||
"LMP: 50, down at 2 1/2, 19 forward.",
|
||||
"LMP: Altitude-velocity light.",
|
||||
"LMP: 3 1/2 down s 220 feet, 13 forward.",
|
||||
"LMP: 1t forward. Coming down nicely.",
|
||||
"LMP: 200 feet, 4 1/2 down.",
|
||||
"LMP: 5 1/2 down.",
|
||||
"LMP: 160, 6 - 6 1/2 down.",
|
||||
"LMP: 5 1/2 down, 9 forward. That's good.",
|
||||
"LMP: 120 feet.",
|
||||
"LMP: 100 feet, 3 1/2 down, 9 forward. Five percent.",
|
||||
"LMP: ...",
|
||||
"LMP: Okay. 75 feet. There's looking good. Down a half, 6 forward.",
|
||||
"CC: 60 seconds.",
|
||||
"LMP: Lights on. ...",
|
||||
"LMP: Down 2 1/2. Forward. Forward. Good.",
|
||||
"LMP: 40 feet, down 2 1/2. Kicking up some dust.",
|
||||
"LMP: 30 feet, 2 1/2 down. Faint shadow.",
|
||||
"LMP: 4 forward. 4 forward. Drifting to the right a little. Okay. Down a half.",
|
||||
"CC: 30 seconds.",
|
||||
"CDR: Forward drift?",
|
||||
"LMP: Yes.",
|
||||
"LMP: Okay.",
|
||||
"LMP: CONTACT LIGHT.",
|
||||
"LMP: Okay. ENGINE STOP.",
|
||||
"LMP: ACA - out of DETENT.",
|
||||
"CDR: Out of DETENT.",
|
||||
"LMP: MODE CONTROL - both AUTO. DESCENT ENGINE COMMAND OVERRIDE - OFF. ENGINE ARM - OFF.",
|
||||
"LMP: 413 is in.",
|
||||
"CC: We copy you down, Eagle.",
|
||||
"CDR: Houston, Tranquility Base here.",
|
||||
"CDR: THE EAGLE HAS LANDED."
|
||||
]
|
@ -1,207 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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 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
|
||||
*/
|
@ -1,36 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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 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;
|
||||
}
|
||||
};
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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 ExampleUserProvider from './ExampleUserProvider';
|
||||
|
||||
export default function ExampleUserPlugin({autoLoginUser, defaultStatusRole} = {
|
||||
autoLoginUser: 'guest',
|
||||
defaultStatusRole: 'test-role'
|
||||
}) {
|
||||
return function install(openmct) {
|
||||
const userProvider = new ExampleUserProvider(openmct, {
|
||||
defaultStatusRole
|
||||
});
|
||||
|
||||
if (autoLoginUser !== undefined) {
|
||||
userProvider.autoLogin(autoLoginUser);
|
||||
}
|
||||
|
||||
openmct.user.setProvider(userProvider);
|
||||
};
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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 {
|
||||
createOpenMct,
|
||||
resetApplicationState
|
||||
} from '../../src/utils/testing';
|
||||
import ExampleUserProvider from './ExampleUserProvider';
|
||||
|
||||
describe("The Example User Plugin", () => {
|
||||
let openmct;
|
||||
|
||||
beforeEach(() => {
|
||||
openmct = createOpenMct();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
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());
|
||||
});
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user