Compare commits
	
		
			32 Commits
		
	
	
		
			v2.0.0
			...
			timestrip-
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 2f79adcb88 | ||
|   | 92112cf8a2 | ||
|   | 56159843a2 | ||
|   | 93a81a1369 | ||
|   | 482d8f392c | ||
|   | 67234c70a4 | ||
|   | e9680e975f | ||
|   | e691a89682 | ||
|   | bcd668594d | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 5471e13d9e | ||
|   | dae446808e | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | ee9e47f487 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 003b1ffede | ||
|   | 16cb5f3911 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 74b83903c9 | ||
|   | 6a470fba1a | ||
|   | 4e7debabb1 | ||
|   | 384e36920c | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | d4429f9686 | ||
|   | 0b2d08078b | ||
|   | 3bbc9e1582 | ||
|   | af0420361b | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 896f0ca3f4 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 28c5405a01 | ||
|   | d114353556 | ||
|   | f40398807e | ||
|   | 81f440e1e6 | ||
|   | c6d6400131 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 55828af1ec | ||
|   | 34b951f4c6 | ||
|   | a7d4006fee | ||
|   | a71485f820 | 
| @@ -2,7 +2,7 @@ version: 2.1 | ||||
| executors: | ||||
|   pw-focal-development: | ||||
|     docker: | ||||
|       - image: mcr.microsoft.com/playwright:v1.18.1-focal | ||||
|       - image: mcr.microsoft.com/playwright:v1.19.1-focal | ||||
|     environment: | ||||
|       NODE_ENV: development # Needed to ensure 'dist' folder created and devDependencies installed | ||||
| parameters: | ||||
| @@ -76,7 +76,7 @@ jobs: | ||||
|           node-version: <<parameters.node-version>> | ||||
|       - run: npm audit --audit-level=low | ||||
|       - generate_and_store_version_and_filesystem_artifacts | ||||
|   node14-lint: | ||||
|   lint: | ||||
|     parameters: | ||||
|       node-version: | ||||
|         type: string | ||||
| @@ -141,8 +141,8 @@ jobs: | ||||
| workflows: | ||||
|   overall-circleci-commit-status: #These jobs run on every commit | ||||
|     jobs: | ||||
|       - node14-lint: | ||||
|           node-version: lts/fermium | ||||
|       - lint: | ||||
|           node-version: lts/gallium | ||||
|       - unit-test: | ||||
|           name: node12-chrome | ||||
|           node-version: lts/erbium | ||||
| @@ -152,10 +152,14 @@ workflows: | ||||
|           node-version: lts/fermium | ||||
|           browser: ChromeHeadless | ||||
|           post-steps: | ||||
|             - upload_code_covio                | ||||
|             - upload_code_covio   | ||||
|       - unit-test: | ||||
|           name: node16-chrome | ||||
|           node-version: lts/gallium | ||||
|           browser: ChromeHeadless              | ||||
|       - e2e-test: | ||||
|           name: e2e-ci | ||||
|           node-version: lts/fermium | ||||
|           node-version: lts/gallium | ||||
|           suite: ci | ||||
|   the-nightly: #These jobs do not run on PRs, but against master at night | ||||
|     jobs: | ||||
| @@ -175,11 +179,15 @@ workflows: | ||||
|           name: node14-chrome-nightly | ||||
|           node-version: lts/fermium | ||||
|           browser: ChromeHeadless | ||||
|       - unit-test: | ||||
|           name: node16-chrome-nightly | ||||
|           node-version: lts/gallium | ||||
|           browser: ChromeHeadless | ||||
|       - npm-audit: | ||||
|           node-version: lts/fermium | ||||
|           node-version: lts/gallium | ||||
|       - e2e-test: | ||||
|           name: e2e-full-nightly | ||||
|           node-version: lts/fermium | ||||
|           node-version: lts/gallium | ||||
|           suite: full | ||||
|     triggers: | ||||
|       - schedule: | ||||
|   | ||||
							
								
								
									
										19
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
								
							| @@ -17,15 +17,6 @@ assignees: '' | ||||
| #### Expected vs Current Behavior | ||||
| <!--- Tell us what should have happened --> | ||||
|  | ||||
| #### Impact Check List | ||||
| <!--- Please select from the following options --> | ||||
|  | ||||
| - [ ] Data loss or misrepresented data? | ||||
| - [ ] Regression? Did this used to work or has it always been broken? | ||||
| - [ ] Is there a workaround available? | ||||
| - [ ] Does this impact a critical component? | ||||
| - [ ] Is this just a visual bug with no functional impact? | ||||
|  | ||||
| #### Steps to Reproduce | ||||
| <!--- Provide a link to a live example, or an unambiguous set of steps to --> | ||||
| <!--- reproduce this bug. Include code to reproduce, if relevant --> | ||||
| @@ -35,10 +26,20 @@ assignees: '' | ||||
| 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? | ||||
|  | ||||
| #### Additional Information | ||||
| <!--- Include any screenshots, gifs, or logs which will expedite triage --> | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							| @@ -11,6 +11,8 @@ updates: | ||||
|       - "dependencies" | ||||
|       - "pr:e2e" | ||||
|       - "pr:daveit" | ||||
|       - "pr:visual" | ||||
|       - "pr:platform" | ||||
|  | ||||
|   - package-ecosystem: "github-actions" | ||||
|     directory: "/" | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							| @@ -28,7 +28,7 @@ jobs: | ||||
|  | ||||
|     steps: | ||||
|     - name: Checkout repository | ||||
|       uses: actions/checkout@v2 | ||||
|       uses: actions/checkout@v3 | ||||
|  | ||||
|     # Initializes the CodeQL tools for scanning. | ||||
|     - name: Initialize CodeQL | ||||
|   | ||||
							
								
								
									
										25
									
								
								.github/workflows/e2e-pr.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										25
									
								
								.github/workflows/e2e-pr.yml
									
									
									
									
										vendored
									
									
								
							| @@ -2,15 +2,22 @@ name: "e2e-pr" | ||||
| on: | ||||
|   workflow_dispatch: | ||||
|   pull_request: | ||||
|     types: [ labeled ] | ||||
|     types: | ||||
|       - labeled | ||||
|       - opened | ||||
|  | ||||
| jobs: | ||||
|   e2e-full: | ||||
|     if: ${{ github.event.label.name == 'pr:e2e' }} | ||||
|     runs-on: ubuntu-latest | ||||
|     runs-on: ${{ matrix.os }} | ||||
|     strategy: | ||||
|       matrix: | ||||
|         os: | ||||
|           - ubuntu-latest | ||||
|           - windows-latest | ||||
|     steps: | ||||
|       - name: Trigger Success | ||||
|         uses: actions/github-script@v5 | ||||
|         uses: actions/github-script@v6 | ||||
|         with: | ||||
|           script: | | ||||
|             github.rest.issues.createComment({ | ||||
| @@ -19,11 +26,11 @@ jobs: | ||||
|               repo: "openmct", | ||||
|               body: 'Started e2e Run. Follow along: https://github.com/nasa/openmct/actions/runs/' + context.runId | ||||
|             }) | ||||
|       - uses: actions/checkout@v2 | ||||
|       - uses: actions/setup-node@v2 | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/setup-node@v3 | ||||
|         with: | ||||
|           node-version: '14' | ||||
|       - run: npx playwright install-deps | ||||
|           node-version: '16' | ||||
|       - run: npx playwright@1.19.2 install | ||||
|       - run: npm install | ||||
|       - run: npm run test:e2e:full | ||||
|       - name: Archive test results | ||||
| @@ -32,7 +39,7 @@ jobs: | ||||
|           path: test-results | ||||
|       - name: Test success | ||||
|         if: ${{ success() }} | ||||
|         uses: actions/github-script@v5 | ||||
|         uses: actions/github-script@v6 | ||||
|         with: | ||||
|           script: | | ||||
|             github.rest.issues.createComment({ | ||||
| @@ -43,7 +50,7 @@ jobs: | ||||
|             }) | ||||
|       - name: Test failure | ||||
|         if: ${{ failure() }} | ||||
|         uses: actions/github-script@v5 | ||||
|         uses: actions/github-script@v6 | ||||
|         with: | ||||
|           script: | | ||||
|             github.rest.issues.createComment({ | ||||
|   | ||||
							
								
								
									
										9
									
								
								.github/workflows/e2e-visual.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.github/workflows/e2e-visual.yml
									
									
									
									
										vendored
									
									
								
							| @@ -4,6 +4,7 @@ on: | ||||
|   pull_request: | ||||
|     types:  | ||||
|       - labeled | ||||
|       - opened | ||||
|   schedule: | ||||
|     - cron: '28 21 * * 1-5' | ||||
|  | ||||
| @@ -12,11 +13,11 @@ jobs: | ||||
|     if: ${{ github.event.label.name == 'pr:visual' }} || ${{ github.event.workflow_dispatch }} || ${{ github.event.schedule }} | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       - uses: actions/setup-node@v2 | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/setup-node@v3 | ||||
|         with: | ||||
|           node-version: '14' | ||||
|       - run: npx playwright install-deps | ||||
|           node-version: '16' | ||||
|       - run: npx playwright@1.19.2 install | ||||
|       - run: npm install | ||||
|       - name: Run the e2e visual tests | ||||
|         run: npm run test:e2e:visual | ||||
|   | ||||
							
								
								
									
										6
									
								
								.github/workflows/e2e.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/e2e.yml
									
									
									
									
										vendored
									
									
								
							| @@ -10,12 +10,12 @@ jobs: | ||||
|   e2e: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       - uses: actions/checkout@v3 | ||||
|         with: | ||||
|           ref: ${{ github.event.inputs.version }} | ||||
|       - uses: actions/setup-node@v2 | ||||
|       - uses: actions/setup-node@v3 | ||||
|         with: | ||||
|           node-version: '14' | ||||
|           node-version: '16' | ||||
|       - run: npm install | ||||
|       - name: Run the e2e tests | ||||
|         run: npm run test:e2e:ci | ||||
|   | ||||
							
								
								
									
										94
									
								
								.github/workflows/lighthouse.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										94
									
								
								.github/workflows/lighthouse.yml
									
									
									
									
										vendored
									
									
								
							| @@ -5,16 +5,96 @@ on: | ||||
|       version: | ||||
|         description: 'Which branch do you want to test?' # Limited to branch for now | ||||
|         required: false | ||||
|         default: 'master'  | ||||
|         default: 'master' | ||||
|   pull_request: | ||||
|     types:  | ||||
|       - labeled | ||||
|   schedule: | ||||
|     - cron: '28 21 * * 1-5' | ||||
| jobs: | ||||
|   lighthouse: | ||||
|   lighthouse-pr: | ||||
|     if: ${{ github.event.label.name == 'pr:lighthouse' }} | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       - name: Checkout Master for Baseline | ||||
|         uses: actions/checkout@v3 | ||||
|         with: | ||||
|           ref: ${{ github.event.inputs.version }} | ||||
|       - uses: actions/setup-node@v2 | ||||
|           ref: master #explicitly checkout master for baseline | ||||
|       - name: Install Node 14 | ||||
|         uses: actions/setup-node@v3 | ||||
|         with: | ||||
|           node-version: '14' | ||||
|       - run: npm install && npm install -g @lhci/cli #Don't want to include this in our deps | ||||
|       - run: lhci autorun | ||||
|       - 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 14 | ||||
|         uses: actions/setup-node@v3 | ||||
|         with: | ||||
|           node-version: '14' | ||||
|       - 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: '14' | ||||
|       - 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 | ||||
|          | ||||
							
								
								
									
										12
									
								
								.github/workflows/npm-prerelease.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.github/workflows/npm-prerelease.yml
									
									
									
									
										vendored
									
									
								
							| @@ -11,10 +11,10 @@ jobs: | ||||
|   build: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       - uses: actions/setup-node@v2 | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/setup-node@v3 | ||||
|         with: | ||||
|           node-version: 14 | ||||
|           node-version: 16 | ||||
|       - run: npm install | ||||
|       - run: npm test | ||||
|  | ||||
| @@ -22,10 +22,10 @@ jobs: | ||||
|     needs: build | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       - uses: actions/setup-node@v2 | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/setup-node@v3 | ||||
|         with: | ||||
|           node-version: 14 | ||||
|           node-version: 16 | ||||
|           registry-url: https://registry.npmjs.org/ | ||||
|       - run: npm install | ||||
|       - run: npm publish --access public --tag unstable | ||||
|   | ||||
							
								
								
									
										34
									
								
								.github/workflows/pr-platform.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								.github/workflows/pr-platform.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| name: "pr-platform" | ||||
| on: | ||||
|   workflow_dispatch: | ||||
|   pull_request: | ||||
|     types: [ labeled ] | ||||
|  | ||||
| jobs: | ||||
|   e2e-full: | ||||
|     if: ${{ github.event.label.name == 'pr:platform' }} | ||||
|     runs-on: ${{ matrix.os }} | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         os: | ||||
|           - ubuntu-latest | ||||
|           - macos-latest | ||||
|           - windows-latest | ||||
|         node_version: | ||||
|           - 12 | ||||
|           - 14 | ||||
|           - 16 | ||||
|         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 | ||||
							
								
								
									
										7
									
								
								.github/workflows/prcop.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.github/workflows/prcop.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,12 +1,15 @@ | ||||
| name: PRcop | ||||
| name: PRCop | ||||
|  | ||||
| on: | ||||
|   pull_request: | ||||
|     types: | ||||
|       - opened | ||||
|       - reopened | ||||
|       - edited | ||||
|       - synchronize | ||||
|       - ready_for_review | ||||
|       - review_requested | ||||
|       - review_request_removed | ||||
|   pull_request_review_comment: | ||||
|     types: | ||||
|       - created | ||||
| @@ -14,7 +17,7 @@ on: | ||||
| jobs: | ||||
|   prcop: | ||||
|     runs-on: ubuntu-latest | ||||
|     name: PRcop | ||||
|     name: Template Check | ||||
|     steps: | ||||
|       - name: Linting Pull Request | ||||
|         uses: makaroni4/prcop@v1.0.35 | ||||
|   | ||||
							
								
								
									
										161
									
								
								e2e/tests/example/generator/SinewaveLimitProvider.e2e.spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								e2e/tests/example/generator/SinewaveLimitProvider.e2e.spec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,161 @@ | ||||
| /***************************************************************************** | ||||
|  * 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, 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 | ||||
|             } | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
| @@ -26,8 +26,8 @@ This test suite is dedicated to tests which verify the basic operations surround | ||||
|  | ||||
| const { test, expect } = require('@playwright/test'); | ||||
|  | ||||
| test.describe('condition set', () => { | ||||
|     test('create new button `condition set` creates new condition object', async ({ page }) => { | ||||
| test.describe('Condition Set Operations', () => { | ||||
|     test('Create new button `condition set` creates new condition object', async ({ page }) => { | ||||
|         //Go to baseURL | ||||
|         await page.goto('/', { waitUntil: 'networkidle' }); | ||||
|  | ||||
| @@ -45,4 +45,21 @@ test.describe('condition set', () => { | ||||
|  | ||||
|         await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set'); | ||||
|     }); | ||||
|     test.fixme('condition set object properties exist', async ({ page }) => { | ||||
|         //Go to object created in step one | ||||
|         //Verify the Condition Set properties persist on Save | ||||
|         //Verify the Condition Set properties persist on page.reload() | ||||
|     }); | ||||
|     test.fixme('condition set object can be modified', async ({ page }) => { | ||||
|         //Go to object created in step one | ||||
|         //Update the Condition Set properties | ||||
|         //Verify the Condition Set properties persist on Save | ||||
|         //Verify the Condition Set properties persist on page.reload() | ||||
|     }); | ||||
|     test.fixme('condition set object can be deleted', async ({ page }) => { | ||||
|         //Go to object created in step one | ||||
|         //Verify that Condition Set object can be deleted | ||||
|         //Verify the Condition Set object does not exist in Tree | ||||
|         //Verify the Condition Set object does not exist with direct navigation to object's URL | ||||
|     }); | ||||
| }); | ||||
|   | ||||
| @@ -19,32 +19,28 @@ | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| import { | ||||
|     createOpenMct, | ||||
|     resetApplicationState | ||||
| } from 'utils/testing'; | ||||
| 
 | ||||
| describe("the plugin", () => { | ||||
|     const NON_EDITABLE_FOLDER_KEY = 'noneditable.folder'; | ||||
|     let openmct; | ||||
| /* | ||||
| This test suite is dedicated to tests which verify the basic operations surrounding Notebooks. | ||||
| */ | ||||
| 
 | ||||
|     beforeEach((done) => { | ||||
|         openmct = createOpenMct(); | ||||
|         openmct.install(openmct.plugins.NonEditableFolder()); | ||||
| const { test, expect } = require('@playwright/test'); | ||||
| 
 | ||||
|         openmct.on('start', done); | ||||
|         openmct.startHeadless(); | ||||
| test.describe('Notebook CRUD Operations', () => { | ||||
|     test.fixme('Can create a Notebook Object', async ({ page }) => { | ||||
|         //Create domain object
 | ||||
|     }); | ||||
| 
 | ||||
|     afterEach(() => { | ||||
|         return resetApplicationState(openmct); | ||||
|     test.fixme('Can update a Notebook Object', 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
 | ||||
|     }); | ||||
| 
 | ||||
|     it('adds the new non-editable folder type', () => { | ||||
|         const type = openmct.types.get(NON_EDITABLE_FOLDER_KEY); | ||||
| 
 | ||||
|         expect(type).toBeDefined(); | ||||
|         expect(type.definition.creatable).toBeFalse(); | ||||
|     test.fixme('Can view a perviously created Notebook Object', async ({ page }) => { | ||||
|         // Create 2 objects with hierarchy
 | ||||
|         // Export as JSON
 | ||||
|         // Verify Hiearchy
 | ||||
|     }); | ||||
|     test.fixme('Can Delete a Notebook Object', async ({ page }) => { | ||||
|         // Other than non-persistible objects
 | ||||
|     }); | ||||
| 
 | ||||
| }); | ||||
							
								
								
									
										114
									
								
								e2e/tests/plugins/notebook/notebookTesting.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								e2e/tests/plugins/notebook/notebookTesting.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,114 @@ | ||||
|     Notebook Testing: | ||||
| ## Useful commands: | ||||
| 1.  - To check default notebook: | ||||
|     `JSON.parse(localStorage.getItem('notebook-storage'));` | ||||
| 1.  - Clear default notebook: | ||||
|     `localStorage.setItem('notebook-storage', null);` | ||||
|  | ||||
| ## I. Default Notebook: | ||||
| 1. Make sure there is no default notebook | ||||
|     `localStorage.setItem('notebook-storage', null);` | ||||
| 2. Check for default notebook is null | ||||
|     `JSON.parse(localStorage.getItem('notebook-storage'));` | ||||
| 3. Create 2 notebooks, add entry into first one to make it default now add entry to other one. Verify by icon change and using following | ||||
|     1. `JSON.parse(localStorage.getItem('notebook-storage'));` | ||||
|     2. There is default icon on notebook inside tree and main view after notebook name | ||||
|     3. Inside default notebook, default section and page has deafult icon. | ||||
| 4. Delete default notebook and check for default notebook is null and default icons are removed. | ||||
|     `JSON.parse(localStorage.getItem('notebook-storage'));` | ||||
|  | ||||
| ## II. Sections and Pages: | ||||
| 1.  - Newly created notebook should have one Section and one page, 'Unnamed Section'/'Unnamed Page' | ||||
|  | ||||
| ### - Sections: | ||||
| 1.  - Rename existing section '1 Section' and '1 Page' | ||||
|     - click 'add section' new section - should be added 'Unnamed Section' with new page 'Unnamed Page' | ||||
| 1.  - Delete existing section | ||||
|     - new 'Unnamed Section' automatically gets created. | ||||
| 1.  - Have 5 total sections without a default section/page | ||||
|     - select 3nd section then delete 4th section | ||||
|     - 3rd is still selected | ||||
| 1.  -  Have 5 total sections without a default section/page | ||||
|     - select 3rd section then delete 3rd section | ||||
|     - 1st is now selected | ||||
| 1.  - Have 5 total sections with a 3rd section as default section | ||||
|     - select 2nd section then delete 2nd section | ||||
|     - 3rd (default) is now selected | ||||
| 1.  - Have 5 total sections with a 3rd section as default section | ||||
|     - select 3rd section then delete 3rd section | ||||
|     - 1st is now selected and there is no default notebook | ||||
|  | ||||
| ### - Pages: | ||||
| 1.  - Delete existing page | ||||
|     - new 'Unnamed Page' automatically gets created | ||||
| 1.  - Have 5 total pages without a default page | ||||
|     - select 3nd page then delete 4th page | ||||
|     - 3rd is still selected | ||||
| 1.  - Have 5 total pages without a default page | ||||
|     - select 3rd page then delete 3rd page | ||||
|     - 1st is now selected | ||||
| 1.  - Have 5 total pages with a 3rd page as default page | ||||
|     - select 2nd page then delete 2nd page | ||||
|     - 3rd (default) is now selected | ||||
| 1.  - Have 5 total pages with a 3rd page as default page | ||||
|     - select 3rd page then delete 3rd page | ||||
|     - 1st is now selected and there is no default notebook | ||||
|  | ||||
| ## III. Entries: | ||||
| 1.  - Add new entry into page should create entry and focus on it | ||||
| 1.  - Drag and drop any telmetry object on 'drop object' | ||||
|     - new entry gets created with telemtry object | ||||
| 1.  - Add new entry into page | ||||
|     - drop any telmetry object on this entry | ||||
|     - telmetry object appears inside this entry | ||||
| 1.  - Add new entry into page, enter text | ||||
|     - navigate away and return | ||||
|     - edit entry text | ||||
|     - navigate away and return back | ||||
|     - confirm text is correct | ||||
| 1.  - delete previously created entry | ||||
|  | ||||
| ## IV: Snapshot Menu: | ||||
| 1.  - There should be no default notebook | ||||
|     - Clear default notebook if exists using `localStorage.setItem('notebook-storage', null);` | ||||
|     - refresh page | ||||
|     - Click on 'Notebook Snaphot Menu' | ||||
|     - Following popup option should be there | ||||
|         1. save to Notebook Snapshots | ||||
| 1.  - Check for default notebook if there is one, else add some entry into one of notebook to make it default | ||||
|     - Click on 'Notebook Snaphot Menu' | ||||
|     - Following popup options should be there | ||||
|         1. save to Default Notebook | ||||
|         1. save to Notebook Snapshots | ||||
| 1.  - Select any telemetry object eg: SWG | ||||
|     - From 'Notebook Snaphot Menu' click on 'save to Default Notebook' | ||||
|     - Navigate to default notebook - section - page and verify that SWG snaphot has been added with correct details | ||||
|  | ||||
| ## V: Snapshot container: | ||||
| 1.  - Select any telemetry object eg: SWG | ||||
|     - Click on 'Notebook Snaphot Menu' | ||||
|     - from popup options click on 'save to Notebook Snapshots' | ||||
|     - Snapshots indicator should blink, click on it to view snapshots | ||||
|     - drag and drop snapshot on droppable area for new enrty | ||||
|     - this should create a new entry with given snaphot has been added to it | ||||
| 1.  - Select any telemetry object eg: SWG | ||||
|     - Click on 'Notebook Snaphot Menu' | ||||
|     - from popup options click on 'save to Notebook Snapshots' | ||||
|     - Snapshots indicator should blink, click on it to view snapshots | ||||
|     - goto any notebook with pre exisintg entry (if not create new entry) | ||||
|     - drag and drop snapshot on exisintg entry | ||||
|     - this should add a given snaphot inside that entry | ||||
|  | ||||
| ## VI: Embeds: | ||||
| 1.  - Add SWG using snapshot to notebook | ||||
|     - Go to that entry | ||||
|     - click on thumbnail image | ||||
|     - should open image with PNG, JPG and Annotate buttons | ||||
|     - verify they all work as intended | ||||
| 1.  - Add SWG using snapshot to notebook | ||||
|     - note down start and end time on time conductor and Fixed Timestamp/Local Clock | ||||
|     - Go to that entry | ||||
|     - change start and end time on time conductor if Local Clock | ||||
|     - click on embed Name/Text | ||||
|     - should take to that object | ||||
|     - also verify that start and end time on time conductor (also should automatically switch to Fixed timestamp)*/ | ||||
							
								
								
									
										69
									
								
								e2e/tests/plugins/timeConductor/timeConductor.e2e.spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								e2e/tests/plugins/timeConductor/timeConductor.e2e.spec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| /***************************************************************************** | ||||
|  * 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, 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(); | ||||
|     }); | ||||
| }); | ||||
							
								
								
									
										126
									
								
								e2e/tests/visual/default.visual.spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								e2e/tests/visual/default.visual.spec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,126 @@ | ||||
| /***************************************************************************** | ||||
|  * 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({ | ||||
|             now: 1483228800000, | ||||
|             shouldAdvanceTime: true | ||||
|         }); //Set browser clock to UNIX Epoch | ||||
|     }); | ||||
| }); | ||||
|  | ||||
| test('Visual - Display layout items view', async ({ page }) => { | ||||
|     //Go to baseURL | ||||
|     await page.goto('/', { waitUntil: 'networkidle' }); | ||||
|  | ||||
|     await page.evaluate(() => window.__clock.tick(1000)); | ||||
|  | ||||
|     //Click the Create button | ||||
|     await page.click('button:has-text("Create")'); | ||||
|  | ||||
|     // Click text=Display Layout | ||||
|     await page.click('text=Display Layout'); | ||||
|  | ||||
|     // Click text=OK | ||||
|     await page.click('text=OK'); | ||||
|  | ||||
|     // Take a snapshot of the newly created Display Layout object | ||||
|     await page.waitForTimeout(VISUAL_GRACE_PERIOD); | ||||
|     await percySnapshot(page, 'Default Display Layout'); | ||||
|  | ||||
|     // Click text=Snapshot Save and Finish Editing Save and Continue Editing >> button >> nth=1 | ||||
|     await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click(); | ||||
|  | ||||
|     // Click text=Save and Finish Editing | ||||
|     await page.locator('text=Save and Finish Editing').click(); | ||||
|  | ||||
|     //Click the Create button | ||||
|     await page.click('button:has-text("Create")'); | ||||
|  | ||||
|     // Click text=Sine Wave Generator | ||||
|     await page.click('text=Sine Wave Generator'); | ||||
|  | ||||
|     // Click text=Save In Open MCT No items >> input[type="search"] | ||||
|     await page.locator('text=Save In Open MCT No items >> input[type="search"]').click(); | ||||
|  | ||||
|     // Fill text=Save In Open MCT No items >> input[type="search"] | ||||
|     await page.locator('text=Save In Open MCT No items >> input[type="search"]').fill('Unnamed Display Layout'); | ||||
|  | ||||
|     // Click text=OK Cancel | ||||
|     await page.locator('text=OK Cancel').click(); | ||||
|  | ||||
|     // Click text=OK | ||||
|     await page.click('text=OK'); | ||||
|  | ||||
|     // Take a snapshot of the newly created Display Layout object | ||||
|     await page.waitForTimeout(VISUAL_GRACE_PERIOD); | ||||
|     await percySnapshot(page, 'Default Sine Wave Generator'); | ||||
|  | ||||
| }); | ||||
|  | ||||
| test.skip('Visual - Timestrip', async ({ page }) => { | ||||
|     //Go to baseURL | ||||
|     await page.goto('/', { waitUntil: 'networkidle' }); | ||||
|  | ||||
|     //Click the Create button | ||||
|     await page.click('button:has-text("Create")'); | ||||
|  | ||||
|     // Click li:has-text("Example Imagery") | ||||
|     await page.locator('li:has-text("Example Imagery")').click(); | ||||
|  | ||||
|     // Fill input[type="number"] | ||||
|     //await page.locator('input[type="number"]').fill('500'); | ||||
|  | ||||
|     // Click text=OK | ||||
|     await page.locator('text=OK').click(); | ||||
|  | ||||
|     await page.pause(); | ||||
|     //  | ||||
|     await page.waitForTimeout(VISUAL_GRACE_PERIOD); | ||||
|     await percySnapshot(page, 'Default Timestrip created'); | ||||
| }); | ||||
| @@ -22,14 +22,14 @@ | ||||
| 
 | ||||
| /* | ||||
| Collection of Visual Tests set to run in a default context. The tests within this suite | ||||
| are only meant to run against openmct's app.js started by `npm run start` within the  | ||||
| are only meant to run against openmct's app.js started by `npm run start` within the | ||||
| `./e2e/playwright-visual.config.js` file. | ||||
| 
 | ||||
| These should only use functional expect statements to verify assumptions about the state  | ||||
| These should only use functional expect statements to verify assumptions about the state | ||||
| in a test and not for functional verification of correctness. Visual tests are not supposed | ||||
| to "fail" on assertions. Instead, they should be used to detect changes between builds or branches. | ||||
| 
 | ||||
| Note: Larger testsuite sizes are OK due to the setup time associated with these tests.  | ||||
| Note: Larger testsuite sizes are OK due to the setup time associated with these tests. | ||||
| */ | ||||
| 
 | ||||
| const { test, expect } = require('@playwright/test'); | ||||
| @@ -40,7 +40,7 @@ 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
 | ||||
| //Will replace with cy.clock() equivalent
 | ||||
| test.beforeEach(async ({ context }) => { | ||||
|     await context.addInitScript({ | ||||
|         // eslint-disable-next-line no-undef
 | ||||
| @@ -111,3 +111,85 @@ test('Visual - Default Condition Widget', async ({ page }) => { | ||||
|     await page.waitForTimeout(VISUAL_GRACE_PERIOD); | ||||
|     await percySnapshot(page, 'Default Condition Widget'); | ||||
| }); | ||||
| 
 | ||||
| test('Visual - Time Conductor start time is less than end time', async ({ page }) => { | ||||
|     //Go to baseURL
 | ||||
|     await page.goto('/', { waitUntil: 'networkidle' }); | ||||
|     const year = new Date().getFullYear(); | ||||
| 
 | ||||
|     let startDate = 'xxxx-01-01 01:00:00.000Z'; | ||||
|     startDate = year + startDate.substring(4); | ||||
| 
 | ||||
|     let endDate = 'xxxx-01-01 02:00:00.000Z'; | ||||
|     endDate = year + endDate.substring(4); | ||||
| 
 | ||||
|     await page.locator('input[type="text"]').nth(1).fill(endDate.toString()); | ||||
|     await page.locator('input[type="text"]').first().fill(startDate.toString()); | ||||
| 
 | ||||
|     //  verify no error msg
 | ||||
|     await page.waitForTimeout(VISUAL_GRACE_PERIOD); | ||||
|     await percySnapshot(page, 'Default Time conductor'); | ||||
| 
 | ||||
|     startDate = (year + 1) + startDate.substring(4); | ||||
|     await page.locator('input[type="text"]').first().fill(startDate.toString()); | ||||
|     await page.locator('input[type="text"]').nth(1).click(); | ||||
| 
 | ||||
|     //  verify error msg for start time (unable to capture snapshot of popup)
 | ||||
|     await page.waitForTimeout(VISUAL_GRACE_PERIOD); | ||||
|     await percySnapshot(page, 'Start time error'); | ||||
| 
 | ||||
|     startDate = (year - 1) + startDate.substring(4); | ||||
|     await page.locator('input[type="text"]').first().fill(startDate.toString()); | ||||
| 
 | ||||
|     endDate = (year - 2) + endDate.substring(4); | ||||
|     await page.locator('input[type="text"]').nth(1).fill(endDate.toString()); | ||||
| 
 | ||||
|     await page.locator('input[type="text"]').first().click(); | ||||
| 
 | ||||
|     //  verify error msg for end time (unable to capture snapshot of popup)
 | ||||
|     await page.waitForTimeout(VISUAL_GRACE_PERIOD); | ||||
|     await percySnapshot(page, 'End time error'); | ||||
| }); | ||||
| 
 | ||||
| test('Visual - Sine Wave Generator Form', async ({ page }) => { | ||||
|     //Go to baseURL
 | ||||
|     await page.goto('/', { waitUntil: 'networkidle' }); | ||||
| 
 | ||||
|     //Click the Create button
 | ||||
|     await page.click('button:has-text("Create")'); | ||||
| 
 | ||||
|     // Click text=Sine Wave Generator
 | ||||
|     await page.click('text=Sine Wave Generator'); | ||||
| 
 | ||||
|     await page.waitForTimeout(VISUAL_GRACE_PERIOD); | ||||
|     await percySnapshot(page, 'Default Sine Wave Generator Form'); | ||||
| 
 | ||||
|     await page.locator('.field.control.l-input-sm input').first().click(); | ||||
|     await page.locator('.field.control.l-input-sm input').first().fill(''); | ||||
| 
 | ||||
|     // Validate red x mark
 | ||||
|     await page.waitForTimeout(VISUAL_GRACE_PERIOD); | ||||
|     await percySnapshot(page, 'removed amplitude property value'); | ||||
| }); | ||||
| 
 | ||||
| test.only('Visual - Timestrip', async ({ page }) => { | ||||
|     //Go to baseURL
 | ||||
|     await page.goto('/', { waitUntil: 'networkidle' }); | ||||
| 
 | ||||
|     //Click the Create button
 | ||||
|     await page.click('button:has-text("Create")'); | ||||
| 
 | ||||
|     // Click li:has-text("Example Imagery")
 | ||||
|     await page.locator('li:has-text("Example Imagery")').click(); | ||||
| 
 | ||||
|     // Fill input[type="number"]
 | ||||
|     //await page.locator('input[type="number"]').fill('500');
 | ||||
| 
 | ||||
|     // Click text=OK
 | ||||
|     await page.locator('text=OK').click(); | ||||
| 
 | ||||
|     await page.pause(); | ||||
|     // 
 | ||||
|     await page.waitForTimeout(VISUAL_GRACE_PERIOD); | ||||
|     await percySnapshot(page, 'Default Timestrip created'); | ||||
| }); | ||||
| @@ -190,7 +190,7 @@ | ||||
|         openmct.install(openmct.plugins.Filters(['table', 'telemetry.plot.overlay'])); | ||||
|         openmct.install(openmct.plugins.ObjectMigration()); | ||||
|         openmct.install(openmct.plugins.ClearData( | ||||
|             ['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked'], | ||||
|             ['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked', 'example.imagery'], | ||||
|             {indicator: true} | ||||
|         )); | ||||
|         openmct.install(openmct.plugins.Clock({ enableClockIndicator: true })); | ||||
|   | ||||
							
								
								
									
										62
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,18 +1,18 @@ | ||||
| { | ||||
|   "name": "openmct", | ||||
|   "version": "1.8.5-SNAPSHOT", | ||||
|   "version": "2.0.1-SNAPSHOT", | ||||
|   "description": "The Open MCT core platform", | ||||
|   "devDependencies": { | ||||
|     "@braintree/sanitize-url": "5.0.2", | ||||
|     "@percy/cli": "1.0.0-beta.73", | ||||
|     "@braintree/sanitize-url": "6.0.0", | ||||
|     "@percy/cli": "1.0.0-beta.75", | ||||
|     "@percy/playwright": "1.0.1", | ||||
|     "@playwright/test": "1.18.1", | ||||
|     "allure-playwright": "2.0.0-beta.14", | ||||
|     "@playwright/test": "1.19.2", | ||||
|     "allure-playwright": "2.0.0-beta.15", | ||||
|     "babel-eslint": "10.1.0", | ||||
|     "comma-separated-values": "3.6.4", | ||||
|     "copy-webpack-plugin": "10.2.0", | ||||
|     "core-js": "3.20.3", | ||||
|     "cross-env": "6.0.3", | ||||
|     "cross-env": "7.0.3", | ||||
|     "css-loader": "4.0.0", | ||||
|     "d3-axis": "1.0.x", | ||||
|     "d3-scale": "1.0.x", | ||||
| @@ -25,12 +25,12 @@ | ||||
|     "exports-loader": "0.7.0", | ||||
|     "express": "4.13.1", | ||||
|     "file-loader": "6.1.0", | ||||
|     "file-saver": "1.3.8", | ||||
|     "file-saver": "2.0.5", | ||||
|     "git-rev-sync": "1.4.0", | ||||
|     "html-loader": "0.5.5", | ||||
|     "html2canvas": "1.4.1", | ||||
|     "imports-loader": "0.8.0", | ||||
|     "istanbul-instrumenter-loader": "^3.0.1", | ||||
|     "istanbul-instrumenter-loader": "3.0.1", | ||||
|     "jasmine-core": "4.0.0", | ||||
|     "jsdoc": "3.5.5", | ||||
|     "karma": "6.3.15", | ||||
| @@ -43,37 +43,36 @@ | ||||
|     "karma-junit-reporter": "2.0.1", | ||||
|     "karma-sourcemap-loader": "0.3.8", | ||||
|     "karma-spec-reporter": "0.0.33", | ||||
|     "karma-webpack": "^5.0.0", | ||||
|     "location-bar": "^3.0.1", | ||||
|     "lodash": "^4.17.12", | ||||
|     "karma-webpack": "5.0.0", | ||||
|     "location-bar": "3.0.1", | ||||
|     "lodash": "4.17.12", | ||||
|     "mini-css-extract-plugin": "2.4.5", | ||||
|     "moment": "2.25.3", | ||||
|     "moment-duration-format": "^2.2.2", | ||||
|     "moment": "2.29.1", | ||||
|     "moment-duration-format": "2.2.2", | ||||
|     "moment-timezone": "0.5.28", | ||||
|     "node-bourbon": "^4.2.3", | ||||
|     "painterro": "^1.2.56", | ||||
|     "playwright": "^1.18.1", | ||||
|     "plotly.js-basic-dist": "^2.5.0", | ||||
|     "plotly.js-gl2d-dist": "^2.5.0", | ||||
|     "printj": "^1.2.1", | ||||
|     "raw-loader": "^0.5.1", | ||||
|     "request": "^2.69.0", | ||||
|     "node-bourbon": "4.2.3", | ||||
|     "painterro": "1.2.56", | ||||
|     "plotly.js-basic-dist": "2.5.0", | ||||
|     "plotly.js-gl2d-dist": "2.5.0", | ||||
|     "printj": "1.2.1", | ||||
|     "raw-loader": "0.5.1", | ||||
|     "request": "2.69.0", | ||||
|     "resolve-url-loader": "4.0.0", | ||||
|     "sass": "1.49.0", | ||||
|     "sass-loader": "12.4.0", | ||||
|     "sinon": "13.0.1", | ||||
|     "style-loader": "^1.0.1", | ||||
|     "uuid": "^3.3.3", | ||||
|     "vue": "2.5.6", | ||||
|     "uuid": "3.3.3", | ||||
|     "vue": "2.6.14", | ||||
|     "vue-eslint-parser": "8.2.0", | ||||
|     "vue-loader": "15.9.8", | ||||
|     "vue-template-compiler": "2.5.6", | ||||
|     "webpack": "5.67.0", | ||||
|     "vue-template-compiler": "2.6.14", | ||||
|     "webpack": "5.68.0", | ||||
|     "webpack-cli": "4.9.2", | ||||
|     "webpack-dev-middleware": "^3.1.3", | ||||
|     "webpack-hot-middleware": "^2.22.3", | ||||
|     "webpack-dev-middleware": "3.7.3", | ||||
|     "webpack-hot-middleware": "2.22.3", | ||||
|     "webpack-merge": "5.8.0", | ||||
|     "zepto": "^1.2.0" | ||||
|     "zepto": "1.2.0" | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "clean": "rm -rf ./dist ./node_modules; rm package-lock.json", | ||||
| @@ -84,12 +83,13 @@ | ||||
|     "build:prod": "cross-env webpack --config webpack.prod.js", | ||||
|     "build:dev": "webpack --config webpack.dev.js", | ||||
|     "build:watch": "webpack --config webpack.dev.js --watch", | ||||
|     "info": "npx envinfo --system --browsers --npmPackages --binaries --languages --markdown", | ||||
|     "test": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run", | ||||
|     "test:debug": "cross-env NODE_ENV=debug karma start --no-single-run", | ||||
|     "test:coverage": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" COVERAGE=true karma start --single-run", | ||||
|     "test:coverage:firefox": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run --browsers=FirefoxHeadless", | ||||
|     "test:e2e:ci": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome smoke default condition.e2e", | ||||
|     "test:e2e:local": "npx playwright test --config=e2e/playwright-local.config.js", | ||||
|     "test:e2e:ci": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome smoke default condition timeConductor", | ||||
|     "test:e2e:local": "npx playwright test --config=e2e/playwright-local.config.js --project=chrome", | ||||
|     "test:e2e:visual": "percy exec --config ./e2e/.percy.yml -- npx playwright test --config=e2e/playwright-visual.config.js default", | ||||
|     "test:e2e:full": "npx playwright test --config=e2e/playwright-ci.config.js", | ||||
|     "test:watch": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --no-single-run", | ||||
| @@ -105,7 +105,7 @@ | ||||
|     "url": "https://github.com/nasa/openmct.git" | ||||
|   }, | ||||
|   "engines": { | ||||
|     "node": ">=12.20.1 <15.0.0" | ||||
|     "node": ">=12.22.0" | ||||
|   }, | ||||
|   "author": "", | ||||
|   "license": "Apache-2.0", | ||||
|   | ||||
| @@ -269,7 +269,6 @@ define([ | ||||
|         this.install(this.plugins.ViewDatumAction()); | ||||
|         this.install(this.plugins.ViewLargeAction()); | ||||
|         this.install(this.plugins.ObjectInterceptors()); | ||||
|         this.install(this.plugins.NonEditableFolder()); | ||||
|         this.install(this.plugins.DeviceClassifier()); | ||||
|         this.install(this.plugins.UserIndicator()); | ||||
|     } | ||||
|   | ||||
| @@ -30,13 +30,15 @@ | ||||
|                :min="model.min" | ||||
|                :max="model.max" | ||||
|                :step="model.step" | ||||
|                @blur="blur()" | ||||
|                @input="updateText()" | ||||
|         > | ||||
|     </span> | ||||
| </span> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { throttle } from 'lodash'; | ||||
|  | ||||
| export default { | ||||
|     props: { | ||||
|         model: { | ||||
| @@ -49,8 +51,12 @@ export default { | ||||
|             field: this.model.value | ||||
|         }; | ||||
|     }, | ||||
|     mounted() { | ||||
|         this.updateText = throttle(this.updateText.bind(this), 200); | ||||
|     }, | ||||
|     methods: { | ||||
|         blur() { | ||||
|         updateText() { | ||||
|             console.log('updateText', this.field); | ||||
|             const data = { | ||||
|                 model: this.model, | ||||
|                 value: this.field | ||||
|   | ||||
| @@ -28,7 +28,7 @@ | ||||
|         <textarea v-model="field" | ||||
|                   type="text" | ||||
|                   :size="model.size" | ||||
|                   @blur="blur()" | ||||
|                   @input="updateText()" | ||||
|         > | ||||
|         </textarea> | ||||
|     </span> | ||||
| @@ -36,6 +36,8 @@ | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { throttle } from 'lodash'; | ||||
|  | ||||
| export default { | ||||
|     props: { | ||||
|         model: { | ||||
| @@ -48,8 +50,11 @@ export default { | ||||
|             field: this.model.value | ||||
|         }; | ||||
|     }, | ||||
|     mounted() { | ||||
|         this.updateText = throttle(this.updateText.bind(this), 500); | ||||
|     }, | ||||
|     methods: { | ||||
|         blur() { | ||||
|         updateText() { | ||||
|             const data = { | ||||
|                 model: this.model, | ||||
|                 value: this.field | ||||
|   | ||||
| @@ -28,13 +28,15 @@ | ||||
|         <input v-model="field" | ||||
|                type="text" | ||||
|                :size="model.size" | ||||
|                @blur="blur()" | ||||
|                @input="updateText()" | ||||
|         > | ||||
|     </span> | ||||
| </span> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { throttle } from 'lodash'; | ||||
|  | ||||
| export default { | ||||
|     props: { | ||||
|         model: { | ||||
| @@ -47,8 +49,11 @@ export default { | ||||
|             field: this.model.value | ||||
|         }; | ||||
|     }, | ||||
|     mounted() { | ||||
|         this.updateText = throttle(this.updateText.bind(this), 500); | ||||
|     }, | ||||
|     methods: { | ||||
|         blur() { | ||||
|         updateText() { | ||||
|             const data = { | ||||
|                 model: this.model, | ||||
|                 value: this.field | ||||
|   | ||||
| @@ -21,7 +21,7 @@ | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| import CSV from 'comma-separated-values'; | ||||
| import {saveAs} from 'file-saver/FileSaver'; | ||||
| import {saveAs} from 'saveAs'; | ||||
|  | ||||
| class CSVExporter { | ||||
|     export(rows, options) { | ||||
|   | ||||
| @@ -31,7 +31,7 @@ function replaceDotsWithUnderscores(filename) { | ||||
|     return filename.replace(regex, '_'); | ||||
| } | ||||
|  | ||||
| import {saveAs} from 'file-saver/FileSaver'; | ||||
| import {saveAs} from 'saveAs'; | ||||
| import html2canvas from 'html2canvas'; | ||||
| import uuid from 'uuid'; | ||||
|  | ||||
|   | ||||
| @@ -20,7 +20,7 @@ | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| import {saveAs} from 'file-saver/FileSaver'; | ||||
| import {saveAs} from 'saveAs'; | ||||
|  | ||||
| class JSONExporter { | ||||
|     export(obj, options) { | ||||
|   | ||||
| @@ -185,10 +185,14 @@ describe('The Clear Data Plugin:', () => { | ||||
|         beforeEach((done) => { | ||||
|             openmct = createOpenMct(); | ||||
|  | ||||
|             clearDataPlugin = new ClearDataPlugin( | ||||
|                 ['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked'], | ||||
|                 {indicator: true} | ||||
|             ); | ||||
|             clearDataPlugin = new ClearDataPlugin([ | ||||
|                 'table', | ||||
|                 'telemetry.plot.overlay', | ||||
|                 'telemetry.plot.stacked', | ||||
|                 'example.imagery' | ||||
|             ], { | ||||
|                 indicator: true | ||||
|             }); | ||||
|             openmct.install(clearDataPlugin); | ||||
|             appHolder = document.createElement('div'); | ||||
|             document.body.appendChild(appHolder); | ||||
|   | ||||
| @@ -325,16 +325,7 @@ export default { | ||||
|             return item && (item.type === type); | ||||
|         }, | ||||
|         canPersistObject(item) { | ||||
|             // for now the only way to tell if an object can be persisted is if it is creatable. | ||||
|             let creatable = false; | ||||
|             if (item) { | ||||
|                 const type = this.openmct.types.get(item.type); | ||||
|                 if (type && type.definition) { | ||||
|                     creatable = (type.definition.creatable !== undefined && (type.definition.creatable === 'true' || type.definition.creatable === true)); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return creatable; | ||||
|             return this.openmct.objects.isPersistable(item.identifier); | ||||
|         }, | ||||
|         hasConditionalStyle(domainObject, layoutItem) { | ||||
|             const id = layoutItem ? layoutItem.id : undefined; | ||||
|   | ||||
| @@ -97,13 +97,16 @@ export default class DuplicateAction { | ||||
|  | ||||
|     validate(currentParent) { | ||||
|         return (data) => { | ||||
|             const parentCandidatePath = data.value; | ||||
|             const parentCandidate = parentCandidatePath[0]; | ||||
|             const parentCandidate = data.value[0]; | ||||
|  | ||||
|             let currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier); | ||||
|             let parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier); | ||||
|             let objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier); | ||||
|  | ||||
|             if (!this.openmct.objects.isPersistable(parentCandidate.identifier)) { | ||||
|                 return false; | ||||
|             } | ||||
|  | ||||
|             if (!parentCandidateKeystring || !currentParentKeystring) { | ||||
|                 return false; | ||||
|             } | ||||
| @@ -122,13 +125,14 @@ export default class DuplicateAction { | ||||
|     } | ||||
|  | ||||
|     appliesTo(objectPath) { | ||||
|         let parent = objectPath[1]; | ||||
|         let parentType = parent && this.openmct.types.get(parent.type); | ||||
|         let child = objectPath[0]; | ||||
|         let childType = child && this.openmct.types.get(child.type); | ||||
|         let locked = child.locked ? child.locked : parent && parent.locked; | ||||
|         const parent = objectPath[1]; | ||||
|         const parentType = parent && this.openmct.types.get(parent.type); | ||||
|         const child = objectPath[0]; | ||||
|         const childType = child && this.openmct.types.get(child.type); | ||||
|         const locked = child.locked ? child.locked : parent && parent.locked; | ||||
|         const isPersistable = this.openmct.objects.isPersistable(child.identifier); | ||||
|  | ||||
|         if (locked) { | ||||
|         if (locked || !isPersistable) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -52,7 +52,7 @@ export default class ExportAsJSONAction { | ||||
|     appliesTo(objectPath) { | ||||
|         let domainObject = objectPath[0]; | ||||
|  | ||||
|         return this._isCreatable(domainObject); | ||||
|         return this._isCreatableAndPersistable(domainObject); | ||||
|     } | ||||
|     /** | ||||
|      * | ||||
| @@ -80,10 +80,11 @@ export default class ExportAsJSONAction { | ||||
|      * @param {object} domainObject | ||||
|      * @returns {boolean} | ||||
|      */ | ||||
|     _isCreatable(domainObject) { | ||||
|     _isCreatableAndPersistable(domainObject) { | ||||
|         const type = this.openmct.types.get(domainObject.type); | ||||
|         const isPersistable = this.openmct.objects.isPersistable(domainObject.identifier); | ||||
|  | ||||
|         return type && type.definition.creatable; | ||||
|         return type && type.definition.creatable && isPersistable; | ||||
|     } | ||||
|     /** | ||||
|      * @private | ||||
| @@ -170,7 +171,7 @@ export default class ExportAsJSONAction { | ||||
|                 .then((children) => { | ||||
|                     children.forEach((child, index) => { | ||||
|                         // Only export if object is creatable | ||||
|                         if (this._isCreatable(child)) { | ||||
|                         if (this._isCreatableAndPersistable(child)) { | ||||
|                             // Prevents infinite export of self-contained objs | ||||
|                             if (!Object.prototype.hasOwnProperty.call(this.tree, this._getId(child))) { | ||||
|                                 // If object is a link to something absent from | ||||
|   | ||||
| @@ -27,6 +27,10 @@ describe('Export as JSON plugin', () => { | ||||
|  | ||||
|     it('ExportAsJSONAction applies to folder', () => { | ||||
|         domainObject = { | ||||
|             identifier: { | ||||
|                 key: 'export-testing', | ||||
|                 namespace: '' | ||||
|             }, | ||||
|             composition: [], | ||||
|             location: 'mine', | ||||
|             modified: 1640115501237, | ||||
| @@ -40,6 +44,10 @@ describe('Export as JSON plugin', () => { | ||||
|  | ||||
|     it('ExportAsJSONAction applies to telemetry.plot.overlay', () => { | ||||
|         domainObject = { | ||||
|             identifier: { | ||||
|                 key: 'export-testing', | ||||
|                 namespace: '' | ||||
|             }, | ||||
|             composition: [], | ||||
|             location: 'mine', | ||||
|             modified: 1640115501237, | ||||
| @@ -53,6 +61,10 @@ describe('Export as JSON plugin', () => { | ||||
|  | ||||
|     it('ExportAsJSONAction applies to telemetry.plot.stacked', () => { | ||||
|         domainObject = { | ||||
|             identifier: { | ||||
|                 key: 'export-testing', | ||||
|                 namespace: '' | ||||
|             }, | ||||
|             composition: [], | ||||
|             location: 'mine', | ||||
|             modified: 1640115501237, | ||||
| @@ -64,16 +76,24 @@ describe('Export as JSON plugin', () => { | ||||
|         expect(exportAsJSONAction.appliesTo([domainObject])).toEqual(true); | ||||
|     }); | ||||
|  | ||||
|     it('ExportAsJSONAction applies does not applies to non-creatable objects', () => { | ||||
|     it('ExportAsJSONAction does not applie to non-persistable objects', () => { | ||||
|         domainObject = { | ||||
|             identifier: { | ||||
|                 key: 'export-testing', | ||||
|                 namespace: '' | ||||
|             }, | ||||
|             composition: [], | ||||
|             location: 'mine', | ||||
|             modified: 1640115501237, | ||||
|             name: 'Non Editable Folder', | ||||
|             persisted: 1640115501237, | ||||
|             type: 'noneditable.folder' | ||||
|             type: 'folder' | ||||
|         }; | ||||
|  | ||||
|         spyOn(openmct.objects, 'getProvider').and.callFake(() => { | ||||
|             return { get: () => domainObject }; | ||||
|         }); | ||||
|  | ||||
|         expect(exportAsJSONAction.appliesTo([domainObject])).toEqual(false); | ||||
|     }); | ||||
|  | ||||
|   | ||||
| @@ -101,7 +101,10 @@ export default class CreateWizard { | ||||
|         // Ensure there is always a 'save in' section | ||||
|         if (includeLocation) { | ||||
|             function validateLocation(data) { | ||||
|                 return self.openmct.composition.checkPolicy(data.value[0], domainObject); | ||||
|                 const policyCheck = self.openmct.composition.checkPolicy(data.value[0], domainObject); | ||||
|                 const parentIsPersistable = self.openmct.objects.isPersistable(data.value[0].identifier); | ||||
|  | ||||
|                 return policyCheck && parentIsPersistable; | ||||
|             } | ||||
|  | ||||
|             sections.push({ | ||||
|   | ||||
| @@ -30,6 +30,7 @@ export default { | ||||
|         this.timeSystemChange = this.timeSystemChange.bind(this); | ||||
|         this.setDataTimeContext = this.setDataTimeContext.bind(this); | ||||
|         this.setDataTimeContext(); | ||||
|         this.openmct.objectViews.on('clearData', this.clearData); | ||||
|  | ||||
|         // set | ||||
|         this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier); | ||||
| @@ -54,6 +55,7 @@ export default { | ||||
|         } | ||||
|  | ||||
|         this.stopFollowingDataTimeContext(); | ||||
|         this.openmct.objectViews.off('clearData', this.clearData); | ||||
|     }, | ||||
|     methods: { | ||||
|         setDataTimeContext() { | ||||
| @@ -151,6 +153,25 @@ export default { | ||||
|                 this.imageHistory = imagery; | ||||
|             } | ||||
|         }, | ||||
|         clearData(domainObjectToClear) { | ||||
|             // global clearData button is accepted therefore no truthy check on inputted param | ||||
|             const clearDataForObjectSelected = Boolean(domainObjectToClear); | ||||
|             if (clearDataForObjectSelected) { | ||||
|                 const idsEqual = this.openmct.objects.areIdsEqual( | ||||
|                     domainObjectToClear.identifier, | ||||
|                     this.domainObject.identifier | ||||
|                 ); | ||||
|                 if (!idsEqual) { | ||||
|                     return; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // splice array to encourage garbage collection | ||||
|             this.imageHistory.splice(0, this.imageHistory.length); | ||||
|  | ||||
|             // requesting history effectively clears imageHistory array | ||||
|             return this.requestHistory(); | ||||
|         }, | ||||
|         timeSystemChange() { | ||||
|             this.timeSystem = this.timeContext.timeSystem(); | ||||
|             this.timeKey = this.timeSystem.key; | ||||
|   | ||||
| @@ -27,6 +27,7 @@ import { | ||||
|     resetApplicationState, | ||||
|     simulateKeyEvent | ||||
| } from 'utils/testing'; | ||||
| import ClearDataPlugin from '../clearData/plugin'; | ||||
|  | ||||
| const ONE_MINUTE = 1000 * 60; | ||||
| const TEN_MINUTES = ONE_MINUTE * 10; | ||||
| @@ -83,6 +84,7 @@ describe("The Imagery View Layouts", () => { | ||||
|     let telemetryPromise; | ||||
|     let telemetryPromiseResolve; | ||||
|     let cleanupFirst; | ||||
|     let isClearDataTriggered; | ||||
|  | ||||
|     let openmct; | ||||
|     let parent; | ||||
| @@ -201,6 +203,10 @@ describe("The Imagery View Layouts", () => { | ||||
|         }); | ||||
|  | ||||
|         spyOn(openmct.telemetry, 'request').and.callFake(() => { | ||||
|             if (isClearDataTriggered) { | ||||
|                 return []; | ||||
|             } | ||||
|  | ||||
|             telemetryPromiseResolve(imageTelemetry); | ||||
|  | ||||
|             return telemetryPromise; | ||||
| @@ -323,6 +329,8 @@ describe("The Imagery View Layouts", () => { | ||||
|         let applicableViews; | ||||
|         let imageryViewProvider; | ||||
|         let imageryView; | ||||
|         let clearDataPlugin; | ||||
|         let clearDataAction; | ||||
|  | ||||
|         beforeEach(() => { | ||||
|  | ||||
| @@ -330,16 +338,21 @@ describe("The Imagery View Layouts", () => { | ||||
|             imageryViewProvider = applicableViews.find(viewProvider => viewProvider.key === imageryKey); | ||||
|             imageryView = imageryViewProvider.view(imageryObject, [imageryObject]); | ||||
|             imageryView.show(child); | ||||
|             clearDataPlugin = new ClearDataPlugin( | ||||
|                 ['example.imagery'], | ||||
|                 {indicator: true} | ||||
|             ); | ||||
|             openmct.install(clearDataPlugin); | ||||
|             clearDataAction = openmct.actions.getAction('clear-data-action'); | ||||
|  | ||||
|             return Vue.nextTick(); | ||||
|         }); | ||||
|  | ||||
|         // afterEach(() => { | ||||
|         //     openmct.time.stopClock(); | ||||
|         //     openmct.router.removeListener('change:hash', resolveFunction); | ||||
|         // | ||||
|         //     imageryView.destroy(); | ||||
|         // }); | ||||
|         afterEach(() => { | ||||
|             isClearDataTriggered = false; | ||||
|             // openmct.time.stopClock(); | ||||
|             // openmct.router.removeListener('change:hash', resolveFunction); | ||||
|             // imageryView.destroy(); | ||||
|         }); | ||||
|  | ||||
|         it("on mount should show the the most recent image", (done) => { | ||||
|             //Looks like we need Vue.nextTick here so that computed properties settle down | ||||
| @@ -470,6 +483,21 @@ describe("The Imagery View Layouts", () => { | ||||
|                 }); | ||||
|             }); | ||||
|         }); | ||||
|         it('clear data action is installed', () => { | ||||
|             expect(clearDataAction).toBeDefined(); | ||||
|         }); | ||||
|  | ||||
|         it('on clearData action should clear data for object is selected', (done) => { | ||||
|             expect(parent.querySelectorAll('.c-imagery__thumb').length).not.toBe(0); | ||||
|             openmct.objectViews.on('clearData', async (_domainObject) => { | ||||
|                 await Vue.nextTick(); | ||||
|                 expect(parent.querySelectorAll('.c-imagery__thumb').length).toBe(0); | ||||
|                 done(); | ||||
|             }); | ||||
|             // stubbed telemetry data will return empty array when true | ||||
|             isClearDataTriggered = true; | ||||
|             clearDataAction.invoke(imageryObject); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     describe("imagery time strip view", () => { | ||||
|   | ||||
| @@ -33,10 +33,7 @@ export default class LinkAction { | ||||
|     } | ||||
|  | ||||
|     appliesTo(objectPath) { | ||||
|         let domainObject = objectPath[0]; | ||||
|         let type = domainObject && this.openmct.types.get(domainObject.type); | ||||
|  | ||||
|         return type && type.definition.creatable; | ||||
|         return true; // link away! | ||||
|     } | ||||
|  | ||||
|     invoke(objectPath) { | ||||
| @@ -77,6 +74,7 @@ export default class LinkAction { | ||||
|                         { | ||||
|                             name: "location", | ||||
|                             control: "locator", | ||||
|                             parent: parentDomainObject, | ||||
|                             required: true, | ||||
|                             validate: this.validate(parentDomainObject), | ||||
|                             key: 'location' | ||||
| @@ -97,6 +95,10 @@ export default class LinkAction { | ||||
|             const parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier); | ||||
|             const objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier); | ||||
|  | ||||
|             if (!this.openmct.objects.isPersistable(parentCandidate.identifier)) { | ||||
|                 return false; | ||||
|             } | ||||
|  | ||||
|             if (!parentCandidateKeystring || !currentParentKeystring) { | ||||
|                 return false; | ||||
|             } | ||||
|   | ||||
| @@ -126,6 +126,7 @@ export default class MoveAction { | ||||
|                         { | ||||
|                             name: "Location", | ||||
|                             control: "locator", | ||||
|                             parent: parentDomainObject, | ||||
|                             required: true, | ||||
|                             validate: this.validate(parentDomainObject), | ||||
|                             key: 'location' | ||||
| @@ -144,6 +145,10 @@ export default class MoveAction { | ||||
|             const parentCandidatePath = data.value; | ||||
|             const parentCandidate = parentCandidatePath[0]; | ||||
|  | ||||
|             if (!this.openmct.objects.isPersistable(parentCandidate.identifier)) { | ||||
|                 return false; | ||||
|             } | ||||
|  | ||||
|             let currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier); | ||||
|             let parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier); | ||||
|             let objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier); | ||||
| @@ -174,8 +179,9 @@ export default class MoveAction { | ||||
|         let parentType = parent && this.openmct.types.get(parent.type); | ||||
|         let child = objectPath[0]; | ||||
|         let childType = child && this.openmct.types.get(child.type); | ||||
|         let isPersistable = this.openmct.objects.isPersistable(child.identifier); | ||||
|  | ||||
|         if (child.locked || (parent && parent.locked)) { | ||||
|         if (child.locked || (parent && parent.locked) || !isPersistable) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -1,33 +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 () { | ||||
|     return function (openmct) { | ||||
|         openmct.types.addType("noneditable.folder", { | ||||
|             name: "Non-Editable Folder", | ||||
|             key: "noneditable.folder", | ||||
|             description: "Create folders to organize other objects or links to objects without the ability to edit it's properties.", | ||||
|             cssClass: "icon-folder", | ||||
|             creatable: false | ||||
|         }); | ||||
|     }; | ||||
| } | ||||
| @@ -596,8 +596,9 @@ export default { | ||||
|             this.resetSearch(); | ||||
|             const notebookStorage = this.createNotebookStorageObject(); | ||||
|             this.updateDefaultNotebook(notebookStorage); | ||||
|             const id = addNotebookEntry(this.openmct, this.domainObject, notebookStorage, embed); | ||||
|             this.focusEntryId = id; | ||||
|             addNotebookEntry(this.openmct, this.domainObject, notebookStorage, embed).then(id => { | ||||
|                 this.focusEntryId = id; | ||||
|             }); | ||||
|         }, | ||||
|         orientationChange() { | ||||
|             this.formatSidebar(); | ||||
|   | ||||
| @@ -28,6 +28,9 @@ | ||||
| > | ||||
|     <div class="c-ne__time-and-content"> | ||||
|         <div class="c-ne__time"> | ||||
|             <template v-if="entry.createdBy"> | ||||
|                 <span class="c-icon icon-person">{{ entry.createdBy }}</span> | ||||
|             </template> | ||||
|             <span>{{ createdOnDate }}</span> | ||||
|             <span>{{ createdOnTime }}</span> | ||||
|         </div> | ||||
| @@ -182,7 +185,7 @@ export default { | ||||
|         this.dropOnEntry = this.dropOnEntry.bind(this); | ||||
|     }, | ||||
|     methods: { | ||||
|         addNewEmbed(objectPath) { | ||||
|         async addNewEmbed(objectPath) { | ||||
|             const bounds = this.openmct.time.bounds(); | ||||
|             const snapshotMeta = { | ||||
|                 bounds, | ||||
| @@ -190,7 +193,7 @@ export default { | ||||
|                 objectPath, | ||||
|                 openmct: this.openmct | ||||
|             }; | ||||
|             const newEmbed = createNewEmbed(snapshotMeta); | ||||
|             const newEmbed = await createNewEmbed(snapshotMeta); | ||||
|             this.entry.embeds.push(newEmbed); | ||||
|         }, | ||||
|         cancelEditMode(event) { | ||||
| @@ -206,7 +209,7 @@ export default { | ||||
|         deleteEntry() { | ||||
|             this.$emit('deleteEntry', this.entry.id); | ||||
|         }, | ||||
|         dropOnEntry($event) { | ||||
|         async dropOnEntry($event) { | ||||
|             event.stopImmediatePropagation(); | ||||
|  | ||||
|             const snapshotId = $event.dataTransfer.getData('openmct/snapshot/id'); | ||||
| @@ -221,7 +224,7 @@ export default { | ||||
|             } else { | ||||
|                 const data = $event.dataTransfer.getData('openmct/domain-object-path'); | ||||
|                 const objectPath = JSON.parse(data); | ||||
|                 this.addNewEmbed(objectPath); | ||||
|                 await this.addNewEmbed(objectPath); | ||||
|             } | ||||
|  | ||||
|             this.$emit('updateEntry', this.entry); | ||||
|   | ||||
| @@ -41,16 +41,17 @@ export default class Snapshot { | ||||
|             fullSizeImageObjectIdentifier: object.identifier, | ||||
|             thumbnailImage | ||||
|         }; | ||||
|         const embed = createNewEmbed(snapshotMeta, snapshot); | ||||
|         if (notebookType === NOTEBOOK_DEFAULT) { | ||||
|             const notebookStorage = getDefaultNotebook(); | ||||
|         createNewEmbed(snapshotMeta, snapshot).then(embed => { | ||||
|             if (notebookType === NOTEBOOK_DEFAULT) { | ||||
|                 const notebookStorage = getDefaultNotebook(); | ||||
|  | ||||
|             this._saveToDefaultNoteBook(notebookStorage, embed); | ||||
|             const notebookImageDomainObject = updateNamespaceOfDomainObject(object, notebookStorage.identifier.namespace); | ||||
|             saveNotebookImageDomainObject(this.openmct, notebookImageDomainObject); | ||||
|         } else { | ||||
|             this._saveToNotebookSnapshots(object, embed); | ||||
|         } | ||||
|                 this._saveToDefaultNoteBook(notebookStorage, embed); | ||||
|                 const notebookImageDomainObject = updateNamespaceOfDomainObject(object, notebookStorage.identifier.namespace); | ||||
|                 saveNotebookImageDomainObject(this.openmct, notebookImageDomainObject); | ||||
|             } else { | ||||
|                 this._saveToNotebookSnapshots(object, embed); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -58,26 +59,26 @@ export default class Snapshot { | ||||
|      */ | ||||
|     _saveToDefaultNoteBook(notebookStorage, embed) { | ||||
|         this.openmct.objects.get(notebookStorage.identifier) | ||||
|             .then(async (domainObject) => { | ||||
|                 addNotebookEntry(this.openmct, domainObject, notebookStorage, embed); | ||||
|             .then((domainObject) => { | ||||
|                 return addNotebookEntry(this.openmct, domainObject, notebookStorage, embed).then(async () => { | ||||
|                     let link = notebookStorage.link; | ||||
|  | ||||
|                 let link = notebookStorage.link; | ||||
|                     // Backwards compatibility fix (old notebook model without link) | ||||
|                     if (!link) { | ||||
|                         link = await getDefaultNotebookLink(this.openmct, domainObject); | ||||
|                         notebookStorage.link = link; | ||||
|                         setDefaultNotebook(this.openmct, notebookStorage); | ||||
|                     } | ||||
|  | ||||
|                 // Backwards compatibility fix (old notebook model without link) | ||||
|                 if (!link) { | ||||
|                     link = await getDefaultNotebookLink(this.openmct, domainObject); | ||||
|                     notebookStorage.link = link; | ||||
|                     setDefaultNotebook(this.openmct, notebookStorage); | ||||
|                 } | ||||
|                     const { section, page } = getNotebookSectionAndPage(domainObject, notebookStorage.defaultSectionId, notebookStorage.defaultPageId); | ||||
|                     if (!section || !page) { | ||||
|                         return; | ||||
|                     } | ||||
|  | ||||
|                 const { section, page } = getNotebookSectionAndPage(domainObject, notebookStorage.defaultSectionId, notebookStorage.defaultPageId); | ||||
|                 if (!section || !page) { | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 const defaultPath = `${domainObject.name} - ${section.name} - ${page.name}`; | ||||
|                 const msg = `Saved to Notebook ${defaultPath}`; | ||||
|                 this._showNotification(msg, link); | ||||
|                     const defaultPath = `${domainObject.name} - ${section.name} - ${page.name}`; | ||||
|                     const msg = `Saved to Notebook ${defaultPath}`; | ||||
|                     this._showNotification(msg, link); | ||||
|                 }); | ||||
|             }); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,17 @@ | ||||
| import objectLink from '../../../ui/mixins/object-link'; | ||||
|  | ||||
| async function getUsername(openmct) { | ||||
|     let username = ''; | ||||
|  | ||||
|     if (openmct.user.hasProvider()) { | ||||
|         const user = await openmct.user.getCurrentUser(); | ||||
|         username = user.getName(); | ||||
|     } | ||||
|  | ||||
|     return username; | ||||
|  | ||||
| } | ||||
|  | ||||
| export const DEFAULT_CLASS = 'notebook-default'; | ||||
| const TIME_BOUNDS = { | ||||
|     START_BOUND: 'tc.startBound', | ||||
| @@ -61,7 +73,7 @@ export function getHistoricLinkInFixedMode(openmct, bounds, historicLink) { | ||||
|     return params.join('&'); | ||||
| } | ||||
|  | ||||
| export function createNewEmbed(snapshotMeta, snapshot = '') { | ||||
| export async function createNewEmbed(snapshotMeta, snapshot = '') { | ||||
|     const { | ||||
|         bounds, | ||||
|         link, | ||||
| @@ -83,10 +95,12 @@ export function createNewEmbed(snapshotMeta, snapshot = '') { | ||||
|         }); | ||||
|     const name = domainObject.name; | ||||
|     const type = domainObject.identifier.key; | ||||
|     const createdBy = await getUsername(openmct); | ||||
|  | ||||
|     return { | ||||
|         bounds, | ||||
|         createdOn: date, | ||||
|         createdBy, | ||||
|         cssClass, | ||||
|         domainObject, | ||||
|         historicLink, | ||||
| @@ -97,7 +111,7 @@ export function createNewEmbed(snapshotMeta, snapshot = '') { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| export function addNotebookEntry(openmct, domainObject, notebookStorage, embed = null, entryText = '') { | ||||
| export async function addNotebookEntry(openmct, domainObject, notebookStorage, embed = null, entryText = '') { | ||||
|     if (!openmct || !domainObject || !notebookStorage) { | ||||
|         return; | ||||
|     } | ||||
| @@ -109,10 +123,12 @@ export function addNotebookEntry(openmct, domainObject, notebookStorage, embed = | ||||
|         ? [embed] | ||||
|         : []; | ||||
|  | ||||
|     const createdBy = await getUsername(openmct); | ||||
|     const id = `entry-${date}`; | ||||
|     const entry = { | ||||
|         id, | ||||
|         createdOn: date, | ||||
|         createdBy, | ||||
|         text: entryText, | ||||
|         embeds | ||||
|     }; | ||||
|   | ||||
| @@ -127,7 +127,7 @@ describe('Notebook Entries:', () => { | ||||
|         expect(entries.length).toEqual(0); | ||||
|     }); | ||||
|  | ||||
|     it('addNotebookEntry adds entry', () => { | ||||
|     it('addNotebookEntry adds entry', async () => { | ||||
|         const unlisten = openmct.objects.observe(notebookDomainObject, '*', (object) => { | ||||
|             const entries = NotebookEntries.getNotebookEntries(notebookDomainObject, selectedSection, selectedPage); | ||||
|  | ||||
| @@ -135,17 +135,38 @@ describe('Notebook Entries:', () => { | ||||
|             unlisten(); | ||||
|         }); | ||||
|  | ||||
|         NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage); | ||||
|         await NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage); | ||||
|     }); | ||||
|  | ||||
|     it('getEntryPosById returns valid position', () => { | ||||
|         const entryId1 = NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage); | ||||
|     it('addNotebookEntry adds active user to entry', async () => { | ||||
|         const USER = 'Timmy'; | ||||
|         openmct.user.hasProvider = () => true; | ||||
|         openmct.user.getCurrentUser = () => { | ||||
|             return Promise.resolve({ | ||||
|                 getName: () => { | ||||
|                     return USER; | ||||
|                 } | ||||
|             }); | ||||
|         }; | ||||
|  | ||||
|         const unlisten = openmct.objects.observe(notebookDomainObject, '*', (object) => { | ||||
|             const entries = NotebookEntries.getNotebookEntries(notebookDomainObject, selectedSection, selectedPage); | ||||
|  | ||||
|             expect(entries[0].createdBy).toEqual(USER); | ||||
|             unlisten(); | ||||
|         }); | ||||
|  | ||||
|         await NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage); | ||||
|     }); | ||||
|  | ||||
|     it('getEntryPosById returns valid position', async () => { | ||||
|         const entryId1 = await NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage); | ||||
|         const position1 = NotebookEntries.getEntryPosById(entryId1, notebookDomainObject, selectedSection, selectedPage); | ||||
|  | ||||
|         const entryId2 = NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage); | ||||
|         const entryId2 = await NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage); | ||||
|         const position2 = NotebookEntries.getEntryPosById(entryId2, notebookDomainObject, selectedSection, selectedPage); | ||||
|  | ||||
|         const entryId3 = NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage); | ||||
|         const entryId3 = await NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage); | ||||
|         const position3 = NotebookEntries.getEntryPosById(entryId3, notebookDomainObject, selectedSection, selectedPage); | ||||
|  | ||||
|         const success = position1 === 0 | ||||
| @@ -155,9 +176,9 @@ describe('Notebook Entries:', () => { | ||||
|         expect(success).toBe(true); | ||||
|     }); | ||||
|  | ||||
|     it('deleteNotebookEntries deletes correct page entries', () => { | ||||
|         NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage); | ||||
|         NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage); | ||||
|     it('deleteNotebookEntries deletes correct page entries', async () => { | ||||
|         await NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage); | ||||
|         await NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage); | ||||
|  | ||||
|         NotebookEntries.deleteNotebookEntries(openmct, notebookDomainObject, selectedSection, selectedPage); | ||||
|         const afterEntries = NotebookEntries.getNotebookEntries(notebookDomainObject, selectedSection, selectedPage); | ||||
|   | ||||
| @@ -21,6 +21,7 @@ | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| export function getValidatedPlan(domainObject) { | ||||
|     let sourceMap = domainObject.sourceMap; | ||||
|     let body = domainObject.selectFile.body; | ||||
|     let json = {}; | ||||
|     if (typeof body === 'string') { | ||||
| @@ -33,5 +34,33 @@ export function getValidatedPlan(domainObject) { | ||||
|         json = body; | ||||
|     } | ||||
|  | ||||
|     return json; | ||||
|     if (sourceMap !== undefined && sourceMap.activities !== undefined && sourceMap.groupId !== undefined) { | ||||
|         let mappedJson = {}; | ||||
|         json[sourceMap.activities].forEach((activity) => { | ||||
|             if (activity[sourceMap.groupId]) { | ||||
|                 const groupIdKey = activity[sourceMap.groupId]; | ||||
|                 let groupActivity = { | ||||
|                     ...activity | ||||
|                 }; | ||||
|  | ||||
|                 if (sourceMap.start) { | ||||
|                     groupActivity.start = activity[sourceMap.start]; | ||||
|                 } | ||||
|  | ||||
|                 if (sourceMap.end) { | ||||
|                     groupActivity.end = activity[sourceMap.end]; | ||||
|                 } | ||||
|  | ||||
|                 if (!mappedJson[groupIdKey]) { | ||||
|                     mappedJson[groupIdKey] = []; | ||||
|                 } | ||||
|  | ||||
|                 mappedJson[groupIdKey].push(groupActivity); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         return mappedJson; | ||||
|     } else { | ||||
|         return json; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -61,7 +61,6 @@ define([ | ||||
|     './URLTimeSettingsSynchronizer/plugin', | ||||
|     './notificationIndicator/plugin', | ||||
|     './newFolderAction/plugin', | ||||
|     './nonEditableFolder/plugin', | ||||
|     './persistence/couch/plugin', | ||||
|     './defaultRootName/plugin', | ||||
|     './plan/plugin', | ||||
| @@ -119,7 +118,6 @@ define([ | ||||
|     URLTimeSettingsSynchronizer, | ||||
|     NotificationIndicator, | ||||
|     NewFolderAction, | ||||
|     NonEditableFolder, | ||||
|     CouchDBPlugin, | ||||
|     DefaultRootName, | ||||
|     PlanLayout, | ||||
| @@ -151,7 +149,7 @@ define([ | ||||
|  | ||||
|     plugins.MyItems = MyItems.default; | ||||
|  | ||||
|     plugins.StaticRootPlugin = StaticRootPlugin; | ||||
|     plugins.StaticRootPlugin = StaticRootPlugin.default; | ||||
|  | ||||
|     /** | ||||
|      * A tabular view showing the latest values of multiple telemetry points at | ||||
| @@ -197,7 +195,6 @@ define([ | ||||
|     plugins.URLTimeSettingsSynchronizer = URLTimeSettingsSynchronizer.default; | ||||
|     plugins.NotificationIndicator = NotificationIndicator.default; | ||||
|     plugins.NewFolderAction = NewFolderAction.default; | ||||
|     plugins.NonEditableFolder = NonEditableFolder.default; | ||||
|     plugins.ISOTimeFormat = ISOTimeFormat.default; | ||||
|     plugins.DefaultRootName = DefaultRootName.default; | ||||
|     plugins.PlanLayout = PlanLayout.default; | ||||
|   | ||||
| @@ -106,6 +106,11 @@ export default class RemoveAction { | ||||
|         let child = objectPath[0]; | ||||
|         let locked = child.locked ? child.locked : parent && parent.locked; | ||||
|         let isEditing = this.openmct.editor.isEditing(); | ||||
|         let isPersistable = this.openmct.objects.isPersistable(child.identifier); | ||||
|  | ||||
|         if (locked || !isPersistable) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if (isEditing) { | ||||
|             let currentItemInView = this.openmct.router.path[0]; | ||||
| @@ -116,10 +121,6 @@ export default class RemoveAction { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (locked) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return parentType | ||||
|             && parentType.definition.creatable | ||||
|             && Array.isArray(parent.composition); | ||||
|   | ||||
| @@ -1,45 +1,139 @@ | ||||
| define([ | ||||
|     'objectUtils' | ||||
| ], function ( | ||||
|     objectUtils | ||||
| ) { | ||||
| /***************************************************************************** | ||||
|  * 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. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| /** | ||||
|  * Transforms an import json blob into a object map that can be used to | ||||
|  * provide objects.  Rewrites root identifier in import data with provided | ||||
|  * rootIdentifier, and rewrites all child object identifiers so that they | ||||
|  * exist in the same namespace as the rootIdentifier. | ||||
|  */ | ||||
| import objectUtils from 'objectUtils'; | ||||
|  | ||||
| class StaticModelProvider { | ||||
|     constructor(importData, rootIdentifier) { | ||||
|         this.objectMap = {}; | ||||
|         this.rewriteModel(importData, rootIdentifier); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Transforms an import json blob into a object map that can be used to | ||||
|      * provide objects.  Rewrites root identifier in import data with provided | ||||
|      * rootIdentifier, and rewrites all child object identifiers so that they | ||||
|      * exist in the same namespace as the rootIdentifier. | ||||
|      * Standard "Get". | ||||
|      */ | ||||
|     function rewriteObjectIdentifiers(importData, rootIdentifier) { | ||||
|         const rootId = importData.rootId; | ||||
|         let objectString = JSON.stringify(importData.openmct); | ||||
|     get(identifier) { | ||||
|         const keyString = objectUtils.makeKeyString(identifier); | ||||
|         if (this.objectMap[keyString]) { | ||||
|             return this.objectMap[keyString]; | ||||
|         } | ||||
|  | ||||
|         Object.keys(importData.openmct).forEach(function (originalId, i) { | ||||
|             let newId; | ||||
|             if (originalId === rootId) { | ||||
|                 newId = objectUtils.makeKeyString(rootIdentifier); | ||||
|             } else { | ||||
|                 newId = objectUtils.makeKeyString({ | ||||
|                     namespace: rootIdentifier.namespace, | ||||
|                     key: i | ||||
|         throw new Error(keyString + ' not found in import models.'); | ||||
|     } | ||||
|  | ||||
|     parseObjectLeaf(objectLeaf, idMap, namespace) { | ||||
|         Object.keys(objectLeaf).forEach((nodeKey) => { | ||||
|             if (idMap.get(nodeKey)) { | ||||
|                 const newIdentifier = objectUtils.makeKeyString({ | ||||
|                     namespace, | ||||
|                     key: idMap.get(nodeKey) | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             while (objectString.indexOf(originalId) !== -1) { | ||||
|                 objectString = objectString.replace( | ||||
|                     '"' + originalId + '"', | ||||
|                     '"' + newId + '"' | ||||
|                 ); | ||||
|                 objectLeaf[newIdentifier] = { ...objectLeaf[nodeKey] }; | ||||
|                 delete objectLeaf[nodeKey]; | ||||
|                 objectLeaf[newIdentifier] = this.parseTreeLeaf(newIdentifier, objectLeaf[newIdentifier], idMap, namespace); | ||||
|             } else { | ||||
|                 objectLeaf[nodeKey] = this.parseTreeLeaf(nodeKey, objectLeaf[nodeKey], idMap, namespace); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         return JSON.parse(objectString); | ||||
|         return objectLeaf; | ||||
|     } | ||||
|  | ||||
|     parseArrayLeaf(arrayLeaf, idMap, namespace) { | ||||
|         return arrayLeaf.map((leafValue, index) => this.parseTreeLeaf( | ||||
|             null, leafValue, idMap, namespace)); | ||||
|     } | ||||
|  | ||||
|     parseBranchedLeaf(branchedLeafValue, idMap, namespace) { | ||||
|         if (Array.isArray(branchedLeafValue)) { | ||||
|             return this.parseArrayLeaf(branchedLeafValue, idMap, namespace); | ||||
|         } else { | ||||
|             return this.parseObjectLeaf(branchedLeafValue, idMap, namespace); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     parseTreeLeaf(leafKey, leafValue, idMap, namespace) { | ||||
|         const hasChild = typeof leafValue === 'object'; | ||||
|         if (hasChild) { | ||||
|             return this.parseBranchedLeaf(leafValue, idMap, namespace); | ||||
|         } | ||||
|  | ||||
|         if (leafKey === 'key') { | ||||
|             return idMap.get(leafValue); | ||||
|         } else if (leafKey === 'namespace') { | ||||
|             return namespace; | ||||
|         } else if (leafKey === 'location') { | ||||
|             if (idMap.get(leafValue)) { | ||||
|                 const newLocationIdentifier = objectUtils.makeKeyString({ | ||||
|                     namespace, | ||||
|                     key: idMap.get(leafValue) | ||||
|                 }); | ||||
|  | ||||
|                 return newLocationIdentifier; | ||||
|             } | ||||
|  | ||||
|             return null; | ||||
|         } else if (idMap.get(leafValue)) { | ||||
|             const newIdentifier = objectUtils.makeKeyString({ | ||||
|                 namespace, | ||||
|                 key: idMap.get(leafValue) | ||||
|             }); | ||||
|  | ||||
|             return newIdentifier; | ||||
|         } else { | ||||
|             return leafValue; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     rewriteObjectIdentifiers(importData, rootIdentifier) { | ||||
|         const namespace = rootIdentifier.namespace; | ||||
|         const idMap = new Map(); | ||||
|         const objectTree = importData.openmct; | ||||
|  | ||||
|         Object.keys(objectTree).forEach((originalId, index) => { | ||||
|             let newId = index.toString(); | ||||
|             if (originalId === importData.rootId) { | ||||
|                 newId = rootIdentifier.key; | ||||
|             } | ||||
|  | ||||
|             idMap.set(originalId, newId); | ||||
|         }); | ||||
|  | ||||
|         const newTree = this.parseTreeLeaf(null, objectTree, idMap, namespace); | ||||
|  | ||||
|         return newTree; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Converts all objects in an object make from old format objects to new | ||||
|      * format objects. | ||||
|      */ | ||||
|     function convertToNewObjects(oldObjectMap) { | ||||
|     convertToNewObjects(oldObjectMap) { | ||||
|         return Object.keys(oldObjectMap) | ||||
|             .reduce(function (newObjectMap, key) { | ||||
|                 newObjectMap[key] = objectUtils.toNewFormat(oldObjectMap[key], key); | ||||
| @@ -49,7 +143,7 @@ define([ | ||||
|     } | ||||
|  | ||||
|     /* Set the root location correctly for a top-level object */ | ||||
|     function setRootLocation(objectMap, rootIdentifier) { | ||||
|     setRootLocation(objectMap, rootIdentifier) { | ||||
|         objectMap[objectUtils.makeKeyString(rootIdentifier)].location = 'ROOT'; | ||||
|  | ||||
|         return objectMap; | ||||
| @@ -59,24 +153,11 @@ define([ | ||||
|      * Takes importData (as provided by the ImportExport plugin) and exposes | ||||
|      * an object provider to fetch those objects. | ||||
|      */ | ||||
|     function StaticModelProvider(importData, rootIdentifier) { | ||||
|         const oldFormatObjectMap = rewriteObjectIdentifiers(importData, rootIdentifier); | ||||
|         const newFormatObjectMap = convertToNewObjects(oldFormatObjectMap); | ||||
|         this.objectMap = setRootLocation(newFormatObjectMap, rootIdentifier); | ||||
|     rewriteModel(importData, rootIdentifier) { | ||||
|         const oldFormatObjectMap = this.rewriteObjectIdentifiers(importData, rootIdentifier); | ||||
|         const newFormatObjectMap = this.convertToNewObjects(oldFormatObjectMap); | ||||
|         this.objectMap = this.setRootLocation(newFormatObjectMap, rootIdentifier); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     /** | ||||
|      * Standard "Get". | ||||
|      */ | ||||
|     StaticModelProvider.prototype.get = function (identifier) { | ||||
|         const keyString = objectUtils.makeKeyString(identifier); | ||||
|         if (this.objectMap[keyString]) { | ||||
|             return this.objectMap[keyString]; | ||||
|         } | ||||
|  | ||||
|         throw new Error(keyString + ' not found in import models.'); | ||||
|     }; | ||||
|  | ||||
|     return StaticModelProvider; | ||||
|  | ||||
| }); | ||||
| export default StaticModelProvider; | ||||
|   | ||||
| @@ -1,133 +1,149 @@ | ||||
| define([ | ||||
|     './StaticModelProvider', | ||||
|     './static-provider-test.json' | ||||
| ], function ( | ||||
|     StaticModelProvider, | ||||
|     testStaticData | ||||
| ) { | ||||
| /***************************************************************************** | ||||
|  * 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. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
|     describe('StaticModelProvider', function () { | ||||
| import testStaticData from './static-provider-test.json'; | ||||
| import StaticModelProvider from './StaticModelProvider'; | ||||
|  | ||||
|         let staticProvider; | ||||
| describe('StaticModelProvider', function () { | ||||
|  | ||||
|     let staticProvider; | ||||
|  | ||||
|     beforeEach(function () { | ||||
|         const staticData = JSON.parse(JSON.stringify(testStaticData)); | ||||
|         staticProvider = new StaticModelProvider(staticData, { | ||||
|             namespace: 'my-import', | ||||
|             key: 'root' | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     describe('rootObject', function () { | ||||
|         let rootModel; | ||||
|  | ||||
|         beforeEach(function () { | ||||
|             const staticData = JSON.parse(JSON.stringify(testStaticData)); | ||||
|             staticProvider = new StaticModelProvider(staticData, { | ||||
|             rootModel = staticProvider.get({ | ||||
|                 namespace: 'my-import', | ||||
|                 key: 'root' | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         describe('rootObject', function () { | ||||
|             let rootModel; | ||||
|         it('is located at top level', function () { | ||||
|             expect(rootModel.location).toBe('ROOT'); | ||||
|         }); | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 rootModel = staticProvider.get({ | ||||
|                     namespace: 'my-import', | ||||
|                     key: 'root' | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             it('is located at top level', function () { | ||||
|                 expect(rootModel.location).toBe('ROOT'); | ||||
|             }); | ||||
|  | ||||
|             it('has new-format identifier', function () { | ||||
|                 expect(rootModel.identifier).toEqual({ | ||||
|                     namespace: 'my-import', | ||||
|                     key: 'root' | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             it('has new-format composition', function () { | ||||
|                 expect(rootModel.composition).toContain({ | ||||
|                     namespace: 'my-import', | ||||
|                     key: '1' | ||||
|                 }); | ||||
|                 expect(rootModel.composition).toContain({ | ||||
|                     namespace: 'my-import', | ||||
|                     key: '2' | ||||
|                 }); | ||||
|         it('has new-format identifier', function () { | ||||
|             expect(rootModel.identifier).toEqual({ | ||||
|                 namespace: 'my-import', | ||||
|                 key: 'root' | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         describe('childObjects', function () { | ||||
|             let swg; | ||||
|             let layout; | ||||
|             let fixed; | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 swg = staticProvider.get({ | ||||
|                     namespace: 'my-import', | ||||
|                     key: '1' | ||||
|                 }); | ||||
|                 layout = staticProvider.get({ | ||||
|                     namespace: 'my-import', | ||||
|                     key: '2' | ||||
|                 }); | ||||
|                 fixed = staticProvider.get({ | ||||
|                     namespace: 'my-import', | ||||
|                     key: '3' | ||||
|                 }); | ||||
|         it('has new-format composition', function () { | ||||
|             expect(rootModel.composition).toContain({ | ||||
|                 namespace: 'my-import', | ||||
|                 key: '1' | ||||
|             }); | ||||
|  | ||||
|             it('match expected ordering', function () { | ||||
|                 // this is a sanity check to make sure the identifiers map in | ||||
|                 // the correct order. | ||||
|                 expect(swg.type).toBe('generator'); | ||||
|                 expect(layout.type).toBe('layout'); | ||||
|                 expect(fixed.type).toBe('telemetry.fixed'); | ||||
|             expect(rootModel.composition).toContain({ | ||||
|                 namespace: 'my-import', | ||||
|                 key: '2' | ||||
|             }); | ||||
|  | ||||
|             it('have new-style identifiers', function () { | ||||
|                 expect(swg.identifier).toEqual({ | ||||
|                     namespace: 'my-import', | ||||
|                     key: '1' | ||||
|                 }); | ||||
|                 expect(layout.identifier).toEqual({ | ||||
|                     namespace: 'my-import', | ||||
|                     key: '2' | ||||
|                 }); | ||||
|                 expect(fixed.identifier).toEqual({ | ||||
|                     namespace: 'my-import', | ||||
|                     key: '3' | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             it('have new-style composition', function () { | ||||
|                 expect(layout.composition).toContain({ | ||||
|                     namespace: 'my-import', | ||||
|                     key: '1' | ||||
|                 }); | ||||
|                 expect(layout.composition).toContain({ | ||||
|                     namespace: 'my-import', | ||||
|                     key: '3' | ||||
|                 }); | ||||
|                 expect(fixed.composition).toContain({ | ||||
|                     namespace: 'my-import', | ||||
|                     key: '1' | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             it('rewrites locations', function () { | ||||
|                 expect(swg.location).toBe('my-import:root'); | ||||
|                 expect(layout.location).toBe('my-import:root'); | ||||
|                 expect(fixed.location).toBe('my-import:2'); | ||||
|             }); | ||||
|  | ||||
|             it('rewrites matched identifiers in objects', function () { | ||||
|                 expect(layout.configuration.layout.panels['my-import:1']) | ||||
|                     .toBeDefined(); | ||||
|                 expect(layout.configuration.layout.panels['my-import:3']) | ||||
|                     .toBeDefined(); | ||||
|                 expect(layout.configuration.layout.panels['483c00d4-bb1d-4b42-b29a-c58e06b322a0']) | ||||
|                     .not.toBeDefined(); | ||||
|                 expect(layout.configuration.layout.panels['20273193-f069-49e9-b4f7-b97a87ed755d']) | ||||
|                     .not.toBeDefined(); | ||||
|                 expect(fixed.configuration['fixed-display'].elements[0].id) | ||||
|                     .toBe('my-import:1'); | ||||
|             }); | ||||
|  | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     describe('childObjects', function () { | ||||
|         let swg; | ||||
|         let layout; | ||||
|         let fixed; | ||||
|  | ||||
|         beforeEach(function () { | ||||
|             swg = staticProvider.get({ | ||||
|                 namespace: 'my-import', | ||||
|                 key: '1' | ||||
|             }); | ||||
|             layout = staticProvider.get({ | ||||
|                 namespace: 'my-import', | ||||
|                 key: '2' | ||||
|             }); | ||||
|             fixed = staticProvider.get({ | ||||
|                 namespace: 'my-import', | ||||
|                 key: '3' | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         it('match expected ordering', function () { | ||||
|             // this is a sanity check to make sure the identifiers map in | ||||
|             // the correct order. | ||||
|             expect(swg.type).toBe('generator'); | ||||
|             expect(layout.type).toBe('layout'); | ||||
|             expect(fixed.type).toBe('telemetry.fixed'); | ||||
|         }); | ||||
|  | ||||
|         it('have new-style identifiers', function () { | ||||
|             expect(swg.identifier).toEqual({ | ||||
|                 namespace: 'my-import', | ||||
|                 key: '1' | ||||
|             }); | ||||
|             expect(layout.identifier).toEqual({ | ||||
|                 namespace: 'my-import', | ||||
|                 key: '2' | ||||
|             }); | ||||
|             expect(fixed.identifier).toEqual({ | ||||
|                 namespace: 'my-import', | ||||
|                 key: '3' | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         it('have new-style composition', function () { | ||||
|             expect(layout.composition).toContain({ | ||||
|                 namespace: 'my-import', | ||||
|                 key: '1' | ||||
|             }); | ||||
|             expect(layout.composition).toContain({ | ||||
|                 namespace: 'my-import', | ||||
|                 key: '3' | ||||
|             }); | ||||
|             expect(fixed.composition).toContain({ | ||||
|                 namespace: 'my-import', | ||||
|                 key: '1' | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         it('rewrites locations', function () { | ||||
|             expect(swg.location).toBe('my-import:root'); | ||||
|             expect(layout.location).toBe('my-import:root'); | ||||
|             expect(fixed.location).toBe('my-import:2'); | ||||
|         }); | ||||
|  | ||||
|         it('rewrites matched identifiers in objects', function () { | ||||
|             expect(layout.configuration.layout.panels['my-import:1']) | ||||
|                 .toBeDefined(); | ||||
|             expect(layout.configuration.layout.panels['my-import:3']) | ||||
|                 .toBeDefined(); | ||||
|             expect(layout.configuration.layout.panels['483c00d4-bb1d-4b42-b29a-c58e06b322a0']) | ||||
|                 .not.toBeDefined(); | ||||
|             expect(layout.configuration.layout.panels['20273193-f069-49e9-b4f7-b97a87ed755d']) | ||||
|                 .not.toBeDefined(); | ||||
|             expect(fixed.configuration['fixed-display'].elements[0].id) | ||||
|                 .toBe('my-import:1'); | ||||
|         }); | ||||
|  | ||||
|     }); | ||||
| }); | ||||
|   | ||||
| @@ -1,52 +1,63 @@ | ||||
| define([ | ||||
|     './StaticModelProvider' | ||||
| ], function ( | ||||
|     StaticModelProvider | ||||
| ) { | ||||
|     /** | ||||
|      * Static Root Plugin: takes an export file and exposes it as a new root | ||||
|      * object. | ||||
|      */ | ||||
|     function StaticRootPlugin(namespace, exportUrl) { | ||||
| /***************************************************************************** | ||||
|  * 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 rootIdentifier = { | ||||
|             namespace: namespace, | ||||
|             key: 'root' | ||||
|         }; | ||||
| import StaticModelProvider from './StaticModelProvider'; | ||||
|  | ||||
|         let cachedProvider; | ||||
| export default function StaticRootPlugin(options) { | ||||
|     const rootIdentifier = { | ||||
|         namespace: options.namespace, | ||||
|         key: 'root' | ||||
|     }; | ||||
|  | ||||
|         function loadProvider() { | ||||
|             return fetch(exportUrl) | ||||
|                 .then(function (response) { | ||||
|                     return response.json(); | ||||
|                 }) | ||||
|                 .then(function (importData) { | ||||
|                     cachedProvider = new StaticModelProvider(importData, rootIdentifier); | ||||
|     let cachedProvider; | ||||
|  | ||||
|                     return cachedProvider; | ||||
|                 }); | ||||
|         } | ||||
|     function loadProvider() { | ||||
|         return fetch(options.exportUrl) | ||||
|             .then(function (response) { | ||||
|                 return response.json(); | ||||
|             }) | ||||
|             .then(function (importData) { | ||||
|                 cachedProvider = new StaticModelProvider(importData, rootIdentifier); | ||||
|  | ||||
|         function getProvider() { | ||||
|             if (!cachedProvider) { | ||||
|                 cachedProvider = loadProvider(); | ||||
|             } | ||||
|  | ||||
|             return Promise.resolve(cachedProvider); | ||||
|         } | ||||
|  | ||||
|         return function install(openmct) { | ||||
|             openmct.objects.addRoot(rootIdentifier); | ||||
|             openmct.objects.addProvider(namespace, { | ||||
|                 get: function (identifier) { | ||||
|                     return getProvider().then(function (provider) { | ||||
|                         return provider.get(identifier); | ||||
|                     }); | ||||
|                 } | ||||
|                 return cachedProvider; | ||||
|             }); | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     return StaticRootPlugin; | ||||
| }); | ||||
|     function getProvider() { | ||||
|         if (!cachedProvider) { | ||||
|             cachedProvider = loadProvider(); | ||||
|         } | ||||
|  | ||||
|         return Promise.resolve(cachedProvider); | ||||
|     } | ||||
|  | ||||
|     return function install(openmct) { | ||||
|         openmct.objects.addRoot(rootIdentifier); | ||||
|         openmct.objects.addProvider(options.namespace, { | ||||
|             get: function (identifier) { | ||||
|                 return getProvider().then(function (provider) { | ||||
|                     return provider.get(identifier); | ||||
|                 }); | ||||
|             } | ||||
|         }); | ||||
|     }; | ||||
| } | ||||
|   | ||||
| @@ -253,6 +253,8 @@ export default { | ||||
|                 input.title = ''; | ||||
|             } | ||||
|  | ||||
|             this.$refs.fixedDeltaInput.reportValidity(); | ||||
|  | ||||
|             return validationResult.valid; | ||||
|         }, | ||||
|         startDateSelected(date) { | ||||
|   | ||||
| @@ -258,10 +258,12 @@ export default { | ||||
|         if (!this.isSelectorTree) { | ||||
|             await this.syncTreeOpenItems(); | ||||
|         } else { | ||||
|             const objectPath = await this.openmct.objects.getOriginalPath(this.initialSelection.identifier); | ||||
|             const navigationPath = this.buildNavigationPath(objectPath); | ||||
|             if (this.initialSelection.identifier) { | ||||
|                 const objectPath = await this.openmct.objects.getOriginalPath(this.initialSelection.identifier); | ||||
|                 const navigationPath = this.buildNavigationPath(objectPath); | ||||
|  | ||||
|             this.openAndScrollTo(navigationPath); | ||||
|                 this.openAndScrollTo(navigationPath); | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|     created() { | ||||
|   | ||||
| @@ -6,7 +6,7 @@ const CopyWebpackPlugin = require('copy-webpack-plugin'); | ||||
| const webpack = require('webpack'); | ||||
| const MiniCssExtractPlugin = require("mini-css-extract-plugin"); | ||||
|  | ||||
| const VueLoaderPlugin = require('vue-loader/lib/plugin'); | ||||
| const {VueLoaderPlugin} = require('vue-loader'); | ||||
| const gitRevision = require('child_process') | ||||
|     .execSync('git rev-parse HEAD') | ||||
|     .toString().trim(); | ||||
| @@ -37,7 +37,7 @@ const config = { | ||||
|         alias: { | ||||
|             "@": path.join(__dirname, "src"), | ||||
|             "legacyRegistry": path.join(__dirname, "src/legacyRegistry"), | ||||
|             "saveAs": "file-saver", | ||||
|             "saveAs": "file-saver/src/FileSaver.js", | ||||
|             "csv": "comma-separated-values", | ||||
|             "EventEmitter": "eventemitter3", | ||||
|             "bourbon": "bourbon.scss", | ||||
|   | ||||
		Reference in New Issue
	
	Block a user