chore: add prettier (2/3): apply formatting, re-enable lint ci step (#6682)

* style: apply prettier formatting

* fix: re-enable lint ci check
This commit is contained in:
Jesse Mazzella 2023-05-18 14:54:46 -07:00 committed by GitHub
parent 172e0b23fd
commit caa7bc6fae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
976 changed files with 115922 additions and 114693 deletions

View File

@ -13,12 +13,12 @@ executors:
docker_layer_caching: true
parameters:
BUST_CACHE:
description: "Set this with the CircleCI UI Trigger Workflow button (boolean = true) to bust the cache!"
description: 'Set this with the CircleCI UI Trigger Workflow button (boolean = true) to bust the cache!'
default: false
type: boolean
commands:
build_and_install:
description: "All steps used to build and install. Will use cache if found"
description: 'All steps used to build and install. Will use cache if found'
parameters:
node-version:
type: string
@ -30,19 +30,19 @@ commands:
node-version: << parameters.node-version >>
- run: npm install --no-audit --progress=false
restore_cache_cmd:
description: "Custom command for restoring cache with the ability to bust cache. When BUST_CACHE is set to true, jobs will not restore cache"
description: 'Custom command for restoring cache with the ability to bust cache. When BUST_CACHE is set to true, jobs will not restore cache'
parameters:
node-version:
type: string
steps:
- when:
condition:
equal: [false, << pipeline.parameters.BUST_CACHE >> ]
equal: [false, << pipeline.parameters.BUST_CACHE >>]
steps:
- restore_cache:
key: deps--{{ arch }}--{{ .Branch }}--<< parameters.node-version >>--{{ checksum "package.json" }}-{{ checksum ".circleci/config.yml" }}
save_cache_cmd:
description: "Custom command for saving cache."
description: 'Custom command for saving cache.'
parameters:
node-version:
type: string
@ -53,7 +53,7 @@ commands:
- ~/.npm
- node_modules
generate_and_store_version_and_filesystem_artifacts:
description: "Track important packages and files"
description: 'Track important packages and files'
steps:
- run: |
[[ $EUID -ne 0 ]] && (sudo mkdir -p /tmp/artifacts && sudo chmod 777 /tmp/artifacts) || (mkdir -p /tmp/artifacts && chmod 777 /tmp/artifacts)
@ -64,7 +64,7 @@ commands:
- store_artifacts:
path: /tmp/artifacts/
generate_e2e_code_cov_report:
description: "Generate e2e code coverage artifacts and publish to codecov.io. Needed to that we can ignore the exit code status of the npm run test"
description: 'Generate e2e code coverage artifacts and publish to codecov.io. Needed to that we can ignore the exit code status of the npm run test'
parameters:
suite:
type: string
@ -115,7 +115,7 @@ jobs:
path: coverage
- when:
condition:
equal: [ 42, 42 ] # Always generate version artifacts regardless of test failure https://discuss.circleci.com/t/make-custom-command-run-always-with-when-always/38957/2
equal: [42, 42] # Always generate version artifacts regardless of test failure https://discuss.circleci.com/t/make-custom-command-run-always-with-when-always/38957/2
steps:
- generate_and_store_version_and_filesystem_artifacts
e2e-test:
@ -131,13 +131,13 @@ jobs:
node-version: <<parameters.node-version>>
- when: #Only install chrome-beta when running the 'full' suite to save $$$
condition:
equal: [ "full", <<parameters.suite>> ]
equal: ['full', <<parameters.suite>>]
steps:
- run: npx playwright install chrome-beta
- run: SHARD="$((${CIRCLE_NODE_INDEX}+1))"; npm run test:e2e:<<parameters.suite>> -- --shard=${SHARD}/${CIRCLE_NODE_TOTAL}
- when:
condition:
equal: [ 42, 42 ] # Always run codecov reports regardless of test failure https://discuss.circleci.com/t/make-custom-command-run-always-with-when-always/38957/2
equal: [42, 42] # Always run codecov reports regardless of test failure https://discuss.circleci.com/t/make-custom-command-run-always-with-when-always/38957/2
steps:
- generate_e2e_code_cov_report:
suite: <<parameters.suite>>
@ -151,7 +151,7 @@ jobs:
path: html-test-results
- when:
condition:
equal: [ 42, 42 ] # Always generate version artifacts regardless of test failure https://discuss.circleci.com/t/make-custom-command-run-always-with-when-always/38957/2
equal: [42, 42] # Always generate version artifacts regardless of test failure https://discuss.circleci.com/t/make-custom-command-run-always-with-when-always/38957/2
steps:
- generate_and_store_version_and_filesystem_artifacts
e2e-couchdb:
@ -172,7 +172,7 @@ jobs:
- run: npm run test:e2e:couchdb
- when:
condition:
equal: [ 42, 42 ] # Always run codecov reports regardless of test failure https://discuss.circleci.com/t/make-custom-command-run-always-with-when-always/38957/2
equal: [42, 42] # Always run codecov reports regardless of test failure https://discuss.circleci.com/t/make-custom-command-run-always-with-when-always/38957/2
steps:
- generate_e2e_code_cov_report:
suite: full #add to full suite
@ -186,7 +186,7 @@ jobs:
path: html-test-results
- when:
condition:
equal: [ 42, 42 ] # Always generate version artifacts regardless of test failure https://discuss.circleci.com/t/make-custom-command-run-always-with-when-always/38957/2
equal: [42, 42] # Always generate version artifacts regardless of test failure https://discuss.circleci.com/t/make-custom-command-run-always-with-when-always/38957/2
steps:
- generate_and_store_version_and_filesystem_artifacts
perf-test:
@ -206,7 +206,7 @@ jobs:
path: html-test-results
- when:
condition:
equal: [ 42, 42 ] # Always run codecov reports regardless of test failure https://discuss.circleci.com/t/make-custom-command-run-always-with-when-always/38957/2
equal: [42, 42] # Always run codecov reports regardless of test failure https://discuss.circleci.com/t/make-custom-command-run-always-with-when-always/38957/2
steps:
- generate_and_store_version_and_filesystem_artifacts
visual-test:
@ -226,15 +226,15 @@ jobs:
path: html-test-results
- when:
condition:
equal: [ 42, 42 ] # Always generate version artifacts regardless of test failure https://discuss.circleci.com/t/make-custom-command-run-always-with-when-always/38957/2
equal: [42, 42] # Always generate version artifacts regardless of test failure https://discuss.circleci.com/t/make-custom-command-run-always-with-when-always/38957/2
steps:
- generate_and_store_version_and_filesystem_artifacts
workflows:
overall-circleci-commit-status: #These jobs run on every commit
jobs:
# - lint:
# name: node16-lint
# node-version: lts/gallium
- lint:
name: node16-lint
node-version: lts/gallium
- unit-test:
name: node18-chrome
node-version: lts/hydrogen
@ -269,7 +269,7 @@ workflows:
node-version: lts/hydrogen
triggers:
- schedule:
cron: "0 0 * * *"
cron: '0 0 * * *'
filters:
branches:
only:

View File

@ -1,165 +1,165 @@
const LEGACY_FILES = ["example/**"];
const LEGACY_FILES = ['example/**'];
module.exports = {
"env": {
"browser": true,
"es6": true,
"jasmine": true,
"amd": true
env: {
browser: true,
es6: true,
jasmine: true,
amd: true
},
"globals": {
"_": "readonly"
globals: {
_: 'readonly'
},
"plugins": ["prettier"],
"extends": [
"eslint:recommended",
"plugin:compat/recommended",
"plugin:vue/recommended",
"plugin:you-dont-need-lodash-underscore/compatible",
"plugin:prettier/recommended"
plugins: ['prettier'],
extends: [
'eslint:recommended',
'plugin:compat/recommended',
'plugin:vue/recommended',
'plugin:you-dont-need-lodash-underscore/compatible',
'plugin:prettier/recommended'
],
"parser": "vue-eslint-parser",
"parserOptions": {
"parser": "@babel/eslint-parser",
"requireConfigFile": false,
"allowImportExportEverywhere": true,
"ecmaVersion": 2015,
"ecmaFeatures": {
"impliedStrict": true
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@babel/eslint-parser',
requireConfigFile: false,
allowImportExportEverywhere: true,
ecmaVersion: 2015,
ecmaFeatures: {
impliedStrict: true
}
},
"rules": {
"prettier/prettier": "error",
"you-dont-need-lodash-underscore/omit": "off",
"you-dont-need-lodash-underscore/throttle": "off",
"you-dont-need-lodash-underscore/flatten": "off",
"you-dont-need-lodash-underscore/get": "off",
"no-bitwise": "error",
"curly": "error",
"eqeqeq": "error",
"guard-for-in": "error",
"no-extend-native": "error",
"no-inner-declarations": "off",
"no-use-before-define": ["error", "nofunc"],
"no-caller": "error",
"no-irregular-whitespace": "error",
"no-new": "error",
"no-shadow": "error",
"no-undef": "error",
"no-unused-vars": [
"error",
rules: {
'prettier/prettier': 'error',
'you-dont-need-lodash-underscore/omit': 'off',
'you-dont-need-lodash-underscore/throttle': 'off',
'you-dont-need-lodash-underscore/flatten': 'off',
'you-dont-need-lodash-underscore/get': 'off',
'no-bitwise': 'error',
curly: 'error',
eqeqeq: 'error',
'guard-for-in': 'error',
'no-extend-native': 'error',
'no-inner-declarations': 'off',
'no-use-before-define': ['error', 'nofunc'],
'no-caller': 'error',
'no-irregular-whitespace': 'error',
'no-new': 'error',
'no-shadow': 'error',
'no-undef': 'error',
'no-unused-vars': [
'error',
{
"vars": "all",
"args": "none"
vars: 'all',
args: 'none'
}
],
"no-console": "off",
"new-cap": [
"error",
'no-console': 'off',
'new-cap': [
'error',
{
"capIsNew": false,
"properties": false
capIsNew: false,
properties: false
}
],
"dot-notation": "error",
'dot-notation': 'error',
// https://eslint.org/docs/rules/no-case-declarations
"no-case-declarations": "error",
'no-case-declarations': 'error',
// https://eslint.org/docs/rules/max-classes-per-file
"max-classes-per-file": ["error", 1],
'max-classes-per-file': ['error', 1],
// https://eslint.org/docs/rules/no-eq-null
"no-eq-null": "error",
'no-eq-null': 'error',
// https://eslint.org/docs/rules/no-eval
"no-eval": "error",
'no-eval': 'error',
// https://eslint.org/docs/rules/no-implicit-globals
"no-implicit-globals": "error",
'no-implicit-globals': 'error',
// https://eslint.org/docs/rules/no-implied-eval
"no-implied-eval": "error",
'no-implied-eval': 'error',
// https://eslint.org/docs/rules/no-lone-blocks
"no-lone-blocks": "error",
'no-lone-blocks': 'error',
// https://eslint.org/docs/rules/no-loop-func
"no-loop-func": "error",
'no-loop-func': 'error',
// https://eslint.org/docs/rules/no-new-func
"no-new-func": "error",
'no-new-func': 'error',
// https://eslint.org/docs/rules/no-new-wrappers
"no-new-wrappers": "error",
'no-new-wrappers': 'error',
// https://eslint.org/docs/rules/no-octal-escape
"no-octal-escape": "error",
'no-octal-escape': 'error',
// https://eslint.org/docs/rules/no-proto
"no-proto": "error",
'no-proto': 'error',
// https://eslint.org/docs/rules/no-return-await
"no-return-await": "error",
'no-return-await': 'error',
// https://eslint.org/docs/rules/no-script-url
"no-script-url": "error",
'no-script-url': 'error',
// https://eslint.org/docs/rules/no-self-compare
"no-self-compare": "error",
'no-self-compare': 'error',
// https://eslint.org/docs/rules/no-sequences
"no-sequences": "error",
'no-sequences': 'error',
// https://eslint.org/docs/rules/no-unmodified-loop-condition
"no-unmodified-loop-condition": "error",
'no-unmodified-loop-condition': 'error',
// https://eslint.org/docs/rules/no-useless-call
"no-useless-call": "error",
'no-useless-call': 'error',
// https://eslint.org/docs/rules/no-nested-ternary
"no-nested-ternary": "error",
'no-nested-ternary': 'error',
// https://eslint.org/docs/rules/no-useless-computed-key
"no-useless-computed-key": "error",
'no-useless-computed-key': 'error',
// https://eslint.org/docs/rules/no-var
"no-var": "error",
'no-var': 'error',
// https://eslint.org/docs/rules/one-var
"one-var": ["error", "never"],
'one-var': ['error', 'never'],
// https://eslint.org/docs/rules/default-case-last
"default-case-last": "error",
'default-case-last': 'error',
// https://eslint.org/docs/rules/default-param-last
"default-param-last": "error",
'default-param-last': 'error',
// https://eslint.org/docs/rules/grouped-accessor-pairs
"grouped-accessor-pairs": "error",
'grouped-accessor-pairs': 'error',
// https://eslint.org/docs/rules/no-constructor-return
"no-constructor-return": "error",
'no-constructor-return': 'error',
// https://eslint.org/docs/rules/array-callback-return
"array-callback-return": "error",
'array-callback-return': 'error',
// https://eslint.org/docs/rules/no-invalid-this
"no-invalid-this": "error", // Believe this one actually surfaces some bugs
'no-invalid-this': 'error', // Believe this one actually surfaces some bugs
// https://eslint.org/docs/rules/func-style
"func-style": ["error", "declaration"],
'func-style': ['error', 'declaration'],
// https://eslint.org/docs/rules/no-unused-expressions
"no-unused-expressions": "error",
'no-unused-expressions': 'error',
// https://eslint.org/docs/rules/no-useless-concat
"no-useless-concat": "error",
'no-useless-concat': 'error',
// https://eslint.org/docs/rules/radix
"radix": "error",
radix: 'error',
// https://eslint.org/docs/rules/require-await
"require-await": "error",
'require-await': 'error',
// https://eslint.org/docs/rules/no-alert
"no-alert": "error",
'no-alert': 'error',
// https://eslint.org/docs/rules/no-useless-constructor
"no-useless-constructor": "error",
'no-useless-constructor': 'error',
// https://eslint.org/docs/rules/no-duplicate-imports
"no-duplicate-imports": "error",
'no-duplicate-imports': 'error',
// https://eslint.org/docs/rules/no-implicit-coercion
"no-implicit-coercion": "error",
'no-implicit-coercion': 'error',
//https://eslint.org/docs/rules/no-unneeded-ternary
"no-unneeded-ternary": "error",
"vue/first-attribute-linebreak": "error",
"vue/multiline-html-element-content-newline": "off",
"vue/singleline-html-element-content-newline": "off",
"vue/multi-word-component-names": "off", // TODO enable, align with conventions
"vue/no-mutating-props": "off"
'no-unneeded-ternary': 'error',
'vue/first-attribute-linebreak': 'error',
'vue/multiline-html-element-content-newline': 'off',
'vue/singleline-html-element-content-newline': 'off',
'vue/multi-word-component-names': 'off', // TODO enable, align with conventions
'vue/no-mutating-props': 'off'
},
"overrides": [
overrides: [
{
"files": LEGACY_FILES,
"rules": {
"no-unused-vars": [
"warn",
files: LEGACY_FILES,
rules: {
'no-unused-vars': [
'warn',
{
"vars": "all",
"args": "none",
"varsIgnorePattern": "controller"
vars: 'all',
args: 'none',
varsIgnorePattern: 'controller'
}
],
"no-nested-ternary": "off",
"no-var": "off",
"one-var": "off"
'no-nested-ternary': 'off',
'no-var': 'off',
'one-var': 'off'
}
}
]

View File

@ -1,39 +1,38 @@
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
- package-ecosystem: 'npm'
directory: '/'
schedule:
interval: "weekly"
interval: 'weekly'
open-pull-requests-limit: 10
labels:
- "pr:daveit"
- "pr:e2e"
- "type:maintenance"
- "dependencies"
- "pr:platform"
- 'pr:daveit'
- 'pr:e2e'
- 'type:maintenance'
- 'dependencies'
- 'pr:platform'
ignore:
#We have to source the playwright container which is not detected by Dependabot
- dependency-name: "@playwright/test"
- dependency-name: "playwright-core"
- dependency-name: '@playwright/test'
- dependency-name: 'playwright-core'
#Lots of noise in these type patch releases.
- dependency-name: "@babel/eslint-parser"
update-types: ["version-update:semver-patch"]
- dependency-name: "eslint-plugin-vue"
update-types: ["version-update:semver-patch"]
- dependency-name: "babel-loader"
update-types: ["version-update:semver-patch"]
- dependency-name: "sinon"
update-types: ["version-update:semver-patch"]
- dependency-name: "moment-timezone"
update-types: ["version-update:semver-patch"]
- dependency-name: "@types/lodash"
update-types: ["version-update:semver-patch"]
- package-ecosystem: "github-actions"
directory: "/"
- dependency-name: '@babel/eslint-parser'
update-types: ['version-update:semver-patch']
- dependency-name: 'eslint-plugin-vue'
update-types: ['version-update:semver-patch']
- dependency-name: 'babel-loader'
update-types: ['version-update:semver-patch']
- dependency-name: 'sinon'
update-types: ['version-update:semver-patch']
- dependency-name: 'moment-timezone'
update-types: ['version-update:semver-patch']
- dependency-name: '@types/lodash'
update-types: ['version-update:semver-patch']
- package-ecosystem: 'github-actions'
directory: '/'
schedule:
interval: "daily"
interval: 'daily'
labels:
- "pr:daveit"
- "type:maintenance"
- "dependencies"
- 'pr:daveit'
- 'type:maintenance'
- 'dependencies'

View File

@ -1,4 +1,4 @@
name: "e2e-couchdb"
name: 'e2e-couchdb'
on:
workflow_dispatch:
pull_request:
@ -17,7 +17,7 @@ jobs:
- run: npx playwright@1.32.3 install
- run: npm install
- name: Start CouchDB Docker Container and Init with Setup Scripts
run : |
run: |
export $(cat src/plugins/persistence/couch/.env.ci | xargs)
docker-compose -f src/plugins/persistence/couch/couchdb-compose.yaml up --detach
sleep 3

View File

@ -1,4 +1,4 @@
name: "e2e-pr"
name: 'e2e-pr'
on:
workflow_dispatch:
pull_request:

View File

@ -1,8 +1,8 @@
name: "pr-platform"
name: 'pr-platform'
on:
workflow_dispatch:
pull_request:
types: [ labeled ]
types: [labeled]
jobs:
e2e-full:

View File

@ -22,5 +22,5 @@ jobs:
- name: Linting Pull Request
uses: makaroni4/prcop@v1.0.35
with:
config-file: ".github/workflows/prcop-config.json"
config-file: '.github/workflows/prcop-config.json'
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -8,80 +8,67 @@ This is the OpenMCT common webpack file. It is imported by the other three webpa
There are separate npm scripts to use these configurations, though simply running `npm install`
will use the default production configuration.
*/
const path = require("path");
const packageDefinition = require("../package.json");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const webpack = require("webpack");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const path = require('path');
const packageDefinition = require('../package.json');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { VueLoaderPlugin } = require("vue-loader");
let gitRevision = "error-retrieving-revision";
let gitBranch = "error-retrieving-branch";
const { VueLoaderPlugin } = require('vue-loader');
let gitRevision = 'error-retrieving-revision';
let gitBranch = 'error-retrieving-branch';
try {
gitRevision = require("child_process")
.execSync("git rev-parse HEAD")
.toString()
.trim();
gitBranch = require("child_process")
.execSync("git rev-parse --abbrev-ref HEAD")
gitRevision = require('child_process').execSync('git rev-parse HEAD').toString().trim();
gitBranch = require('child_process')
.execSync('git rev-parse --abbrev-ref HEAD')
.toString()
.trim();
} catch (err) {
console.warn(err);
}
const projectRootDir = path.resolve(__dirname, "..");
const projectRootDir = path.resolve(__dirname, '..');
/** @type {import('webpack').Configuration} */
const config = {
context: projectRootDir,
entry: {
openmct: "./openmct.js",
generatorWorker: "./example/generator/generatorWorker.js",
couchDBChangesFeed:
"./src/plugins/persistence/couch/CouchChangesFeed.js",
inMemorySearchWorker: "./src/api/objects/InMemorySearchWorker.js",
espressoTheme: "./src/plugins/themes/espresso-theme.scss",
snowTheme: "./src/plugins/themes/snow-theme.scss"
openmct: './openmct.js',
generatorWorker: './example/generator/generatorWorker.js',
couchDBChangesFeed: './src/plugins/persistence/couch/CouchChangesFeed.js',
inMemorySearchWorker: './src/api/objects/InMemorySearchWorker.js',
espressoTheme: './src/plugins/themes/espresso-theme.scss',
snowTheme: './src/plugins/themes/snow-theme.scss'
},
output: {
globalObject: "this",
filename: "[name].js",
path: path.resolve(projectRootDir, "dist"),
library: "openmct",
libraryTarget: "umd",
publicPath: "",
hashFunction: "xxhash64",
globalObject: 'this',
filename: '[name].js',
path: path.resolve(projectRootDir, 'dist'),
library: 'openmct',
libraryTarget: 'umd',
publicPath: '',
hashFunction: 'xxhash64',
clean: true
},
resolve: {
alias: {
"@": path.join(projectRootDir, "src"),
legacyRegistry: path.join(projectRootDir, "src/legacyRegistry"),
saveAs: "file-saver/src/FileSaver.js",
csv: "comma-separated-values",
EventEmitter: "eventemitter3",
bourbon: "bourbon.scss",
"plotly-basic": "plotly.js-basic-dist",
"plotly-gl2d": "plotly.js-gl2d-dist",
"d3-scale": path.join(
projectRootDir,
"node_modules/d3-scale/dist/d3-scale.min.js"
),
printj: path.join(
projectRootDir,
"node_modules/printj/dist/printj.min.js"
),
styles: path.join(projectRootDir, "src/styles"),
MCT: path.join(projectRootDir, "src/MCT"),
testUtils: path.join(projectRootDir, "src/utils/testUtils.js"),
objectUtils: path.join(
projectRootDir,
"src/api/objects/object-utils.js"
),
"kdbush": path.join(projectRootDir, "node_modules/kdbush/kdbush.min.js"),
utils: path.join(projectRootDir, "src/utils")
'@': path.join(projectRootDir, 'src'),
legacyRegistry: path.join(projectRootDir, 'src/legacyRegistry'),
saveAs: 'file-saver/src/FileSaver.js',
csv: 'comma-separated-values',
EventEmitter: 'eventemitter3',
bourbon: 'bourbon.scss',
'plotly-basic': 'plotly.js-basic-dist',
'plotly-gl2d': 'plotly.js-gl2d-dist',
'd3-scale': path.join(projectRootDir, 'node_modules/d3-scale/dist/d3-scale.min.js'),
printj: path.join(projectRootDir, 'node_modules/printj/dist/printj.min.js'),
styles: path.join(projectRootDir, 'src/styles'),
MCT: path.join(projectRootDir, 'src/MCT'),
testUtils: path.join(projectRootDir, 'src/utils/testUtils.js'),
objectUtils: path.join(projectRootDir, 'src/api/objects/object-utils.js'),
kdbush: path.join(projectRootDir, 'node_modules/kdbush/kdbush.min.js'),
utils: path.join(projectRootDir, 'src/utils')
}
},
plugins: [
@ -95,24 +82,24 @@ const config = {
new CopyWebpackPlugin({
patterns: [
{
from: "src/images/favicons",
to: "favicons"
from: 'src/images/favicons',
to: 'favicons'
},
{
from: "./index.html",
from: './index.html',
transform: function (content) {
return content.toString().replace(/dist\//g, "");
return content.toString().replace(/dist\//g, '');
}
},
{
from: "src/plugins/imagery/layers",
to: "imagery"
from: 'src/plugins/imagery/layers',
to: 'imagery'
}
]
}),
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[name].css"
filename: '[name].css',
chunkFilename: '[name].css'
})
],
module: {
@ -122,49 +109,49 @@ const config = {
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader"
loader: 'css-loader'
},
{
loader: "resolve-url-loader"
loader: 'resolve-url-loader'
},
{
loader: "sass-loader",
loader: 'sass-loader',
options: { sourceMap: true }
}
]
},
{
test: /\.vue$/,
use: "vue-loader"
use: 'vue-loader'
},
{
test: /\.html$/,
type: "asset/source"
type: 'asset/source'
},
{
test: /\.(jpg|jpeg|png|svg)$/,
type: "asset/resource",
type: 'asset/resource',
generator: {
filename: "images/[name][ext]"
filename: 'images/[name][ext]'
}
},
{
test: /\.ico$/,
type: "asset/resource",
type: 'asset/resource',
generator: {
filename: "icons/[name][ext]"
filename: 'icons/[name][ext]'
}
},
{
test: /\.(woff|woff2?|eot|ttf)$/,
type: "asset/resource",
type: 'asset/resource',
generator: {
filename: "fonts/[name][ext]"
filename: 'fonts/[name][ext]'
}
}
]
},
stats: "errors-warnings",
stats: 'errors-warnings',
performance: {
// We should eventually consider chunking to decrease
// these values

View File

@ -6,9 +6,9 @@ OpenMCT Continuous Integration servers use this configuration to add code covera
information to pull requests.
*/
const config = require("./webpack.dev");
const config = require('./webpack.dev');
// eslint-disable-next-line no-undef
const CI = process.env.CI === "true";
const CI = process.env.CI === 'true';
config.devtool = CI ? false : undefined;
@ -18,15 +18,15 @@ config.module.rules.push({
test: /\.js$/,
exclude: /(Spec\.js$)|(node_modules)/,
use: {
loader: "babel-loader",
loader: 'babel-loader',
options: {
retainLines: true,
// eslint-disable-next-line no-undef
plugins: [
[
"babel-plugin-istanbul",
'babel-plugin-istanbul',
{
extension: [".js", ".vue"]
extension: ['.js', '.vue']
}
]
]

View File

@ -5,29 +5,29 @@ This configuration should be used for development purposes. It contains full sou
devServer (which be invoked using by `npm start`), and a non-minified Vue.js distribution.
If OpenMCT is to be used for a production server, use webpack.prod.js instead.
*/
const path = require("path");
const webpack = require("webpack");
const { merge } = require("webpack-merge");
const path = require('path');
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const common = require("./webpack.common");
const projectRootDir = path.resolve(__dirname, "..");
const common = require('./webpack.common');
const projectRootDir = path.resolve(__dirname, '..');
module.exports = merge(common, {
mode: "development",
mode: 'development',
watchOptions: {
// Since we use require.context, webpack is watching the entire directory.
// We need to exclude any files we don't want webpack to watch.
// See: https://webpack.js.org/configuration/watch/#watchoptions-exclude
ignored: [
"**/{node_modules,dist,docs,e2e}", // All files in node_modules, dist, docs, e2e,
"**/{*.yml,Procfile,webpack*.js,babel*.js,package*.json,tsconfig.json}", // Config files
"**/*.{sh,md,png,ttf,woff,svg}", // Non source files
"**/.*" // dotfiles and dotfolders
'**/{node_modules,dist,docs,e2e}', // All files in node_modules, dist, docs, e2e,
'**/{*.yml,Procfile,webpack*.js,babel*.js,package*.json,tsconfig.json}', // Config files
'**/*.{sh,md,png,ttf,woff,svg}', // Non source files
'**/.*' // dotfiles and dotfolders
]
},
resolve: {
alias: {
vue: path.join(projectRootDir, "node_modules/vue/dist/vue.js")
vue: path.join(projectRootDir, 'node_modules/vue/dist/vue.js')
}
},
plugins: [
@ -35,20 +35,20 @@ module.exports = merge(common, {
__OPENMCT_ROOT_RELATIVE__: '"dist/"'
})
],
devtool: "eval-source-map",
devtool: 'eval-source-map',
devServer: {
devMiddleware: {
writeToDisk: (filePathString) => {
const filePath = path.parse(filePathString);
const shouldWrite = !filePath.base.includes("hot-update");
const shouldWrite = !filePath.base.includes('hot-update');
return shouldWrite;
}
},
watchFiles: ["**/*.css"],
watchFiles: ['**/*.css'],
static: {
directory: path.join(__dirname, "..", "/dist"),
publicPath: "/dist",
directory: path.join(__dirname, '..', '/dist'),
publicPath: '/dist',
watch: false
},
client: {

View File

@ -4,18 +4,18 @@
This configuration should be used for production installs.
It is the default webpack configuration.
*/
const path = require("path");
const webpack = require("webpack");
const { merge } = require("webpack-merge");
const path = require('path');
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const common = require("./webpack.common");
const projectRootDir = path.resolve(__dirname, "..");
const common = require('./webpack.common');
const projectRootDir = path.resolve(__dirname, '..');
module.exports = merge(common, {
mode: "production",
mode: 'production',
resolve: {
alias: {
vue: path.join(projectRootDir, "node_modules/vue/dist/vue.min.js")
vue: path.join(projectRootDir, 'node_modules/vue/dist/vue.min.js')
}
},
plugins: [
@ -23,5 +23,5 @@ module.exports = merge(common, {
__OPENMCT_ROOT_RELATIVE__: '""'
})
],
devtool: "source-map"
devtool: 'source-map'
});

View File

@ -11,7 +11,7 @@ coverage:
informational: true
precision: 2
round: down
range: "66...100"
range: '66...100'
flags:
unit:
@ -22,7 +22,7 @@ flags:
carryforward: true
comment:
layout: "diff,flags,files,footer"
layout: 'diff,flags,files,footer'
behavior: default
require_changes: false
show_carryforward_flags: true

View File

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

View File

@ -84,7 +84,7 @@ async function createDomainObjectWithDefaults(page, { type, name, parent = 'mine
// Modify the name input field of the domain object to accept 'name'
const nameInput = page.locator('form[name="mctForm"] .first input[type="text"]');
await nameInput.fill("");
await nameInput.fill('');
await nameInput.fill(name);
if (page.testNotes) {
@ -178,7 +178,7 @@ async function createPlanFromJSON(page, { name, json, parent = 'mine' }) {
// Modify the name input field of the domain object to accept 'name'
const nameInput = page.locator('form[name="mctForm"] .first input[type="text"]');
await nameInput.fill("");
await nameInput.fill('');
await nameInput.fill(name);
// Upload buffer from memory
@ -209,12 +209,12 @@ async function createPlanFromJSON(page, { name, json, parent = 'mine' }) {
}
/**
* Open the given `domainObject`'s context menu from the object tree.
* Expands the path to the object and scrolls to it if necessary.
*
* @param {import('@playwright/test').Page} page
* @param {string} url the url to the object
*/
* Open the given `domainObject`'s context menu from the object tree.
* Expands the path to the object and scrolls to it if necessary.
*
* @param {import('@playwright/test').Page} page
* @param {string} url the url to the object
*/
async function openObjectTreeContextMenu(page, url) {
await page.goto(url);
await page.click('button[title="Show selected item in tree"]');
@ -228,15 +228,17 @@ async function openObjectTreeContextMenu(page, url) {
* @param {import('@playwright/test').Page} page
* @param {"Main Tree" | "Create Modal Tree"} [treeName="Main Tree"]
*/
async function expandEntireTree(page, treeName = "Main Tree") {
async function expandEntireTree(page, treeName = 'Main Tree') {
const treeLocator = page.getByRole('tree', {
name: treeName
});
const collapsedTreeItems = treeLocator.getByRole('treeitem', {
const collapsedTreeItems = treeLocator
.getByRole('treeitem', {
expanded: false
}).locator('span.c-disclosure-triangle.is-enabled');
})
.locator('span.c-disclosure-triangle.is-enabled');
while (await collapsedTreeItems.count() > 0) {
while ((await collapsedTreeItems.count()) > 0) {
await collapsedTreeItems.nth(0).click();
// FIXME: Replace hard wait with something event-driven.
@ -276,7 +278,10 @@ async function getHashUrlToDomainObject(page, uuid) {
await page.waitForLoadState('load'); //Add some determinism
const hashUrl = await page.evaluate(async (objectUuid) => {
const path = await window.openmct.objects.getOriginalPath(objectUuid);
let url = './#/browse/' + [...path].reverse()
let url =
'./#/browse/' +
[...path]
.reverse()
.map((object) => window.openmct.objects.makeKeyString(object.identifier))
.join('/');
@ -348,7 +353,7 @@ async function setRealTimeMode(page) {
* @param {OffsetValues} offset
* @param {import('@playwright/test').Locator} offsetButton
*/
async function setTimeConductorOffset(page, {hours, mins, secs}, offsetButton) {
async function setTimeConductorOffset(page, { hours, mins, secs }, offsetButton) {
await offsetButton.click();
if (hours) {
@ -407,16 +412,16 @@ async function selectInspectorTab(page, name) {
}
/**
* Waits and asserts that all plot series data on the page
* is loaded and drawn.
*
* In lieu of a better way to detect when a plot is done rendering,
* we [attach a class to the '.gl-plot' element](https://github.com/nasa/openmct/blob/5924d7ea95a0c2d4141c602a3c7d0665cb91095f/src/plugins/plot/MctPlot.vue#L27)
* once all pending series data has been loaded. The following appAction retrieves
* all plots on the page and waits up to the default timeout for the class to be
* attached to each plot.
* @param {import('@playwright/test').Page} page
*/
* Waits and asserts that all plot series data on the page
* is loaded and drawn.
*
* In lieu of a better way to detect when a plot is done rendering,
* we [attach a class to the '.gl-plot' element](https://github.com/nasa/openmct/blob/5924d7ea95a0c2d4141c602a3c7d0665cb91095f/src/plugins/plot/MctPlot.vue#L27)
* once all pending series data has been loaded. The following appAction retrieves
* all plots on the page and waits up to the default timeout for the class to be
* attached to each plot.
* @param {import('@playwright/test').Page} page
*/
async function waitForPlotsToRender(page) {
const plotLocator = page.locator('.gl-plot');
for (const plot of await plotLocator.all()) {
@ -441,12 +446,21 @@ async function waitForPlotsToRender(page) {
* @return {Promise<PlotPixel[]>}
*/
async function getCanvasPixels(page, canvasSelector) {
const getTelemValuePromise = new Promise(resolve => page.exposeFunction('getCanvasValue', resolve));
const canvasHandle = await page.evaluateHandle((canvas) => document.querySelector(canvas), canvasSelector);
const canvasContextHandle = await page.evaluateHandle(canvas => canvas.getContext('2d'), canvasHandle);
const getTelemValuePromise = new Promise((resolve) =>
page.exposeFunction('getCanvasValue', resolve)
);
const canvasHandle = await page.evaluateHandle(
(canvas) => document.querySelector(canvas),
canvasSelector
);
const canvasContextHandle = await page.evaluateHandle(
(canvas) => canvas.getContext('2d'),
canvasHandle
);
await waitForPlotsToRender(page);
await page.evaluate(([canvas, ctx]) => {
await page.evaluate(
([canvas, ctx]) => {
// The document canvas is where the plot points and lines are drawn.
// The only way to access the canvas is using document (using page.evaluate)
/** @type {ImageData} */
@ -457,14 +471,16 @@ async function getCanvasPixels(page, canvasSelector) {
const plotPixels = [];
// Each pixel consists of four values within the ImageData.data array. The for loop iterates by multiples of four.
// The values associated with each pixel are R (red), G (green), B (blue), and A (alpha), in that order.
for (let i = 0; i < imageDataValues.length;) {
for (let i = 0; i < imageDataValues.length; ) {
if (imageDataValues[i] > 0) {
plotPixels.push({
r: imageDataValues[i],
g: imageDataValues[i + 1],
b: imageDataValues[i + 2],
a: imageDataValues[i + 3],
strValue: `rgb(${imageDataValues[i]}, ${imageDataValues[i + 1]}, ${imageDataValues[i + 2]}, ${imageDataValues[i + 3]})`
strValue: `rgb(${imageDataValues[i]}, ${imageDataValues[i + 1]}, ${
imageDataValues[i + 2]
}, ${imageDataValues[i + 3]})`
});
}
@ -472,7 +488,9 @@ async function getCanvasPixels(page, canvasSelector) {
}
window.getCanvasValue(plotPixels);
}, [canvasHandle, canvasContextHandle]);
},
[canvasHandle, canvasContextHandle]
);
return getTelemValuePromise;
}

View File

@ -56,12 +56,9 @@ function _consoleMessageToString(msg) {
* @return {Promise<Animation[]>}
*/
function waitForAnimations(locator) {
return locator
.evaluate((element) =>
Promise.all(
element
.getAnimations({ subtree: true })
.map((animation) => animation.finished)));
return locator.evaluate((element) =>
Promise.all(element.getAnimations({ subtree: true }).map((animation) => animation.finished))
);
}
/**
@ -90,7 +87,8 @@ exports.test = base.test.extend({
* @see {@link https://github.com/sinonjs/fake-timers/#var-clock--faketimersinstallconfig SinonJS FakeTimers Config}
*/
clockOptions: [undefined, { option: true }],
overrideClock: [async ({ context, clockOptions }, use) => {
overrideClock: [
async ({ context, clockOptions }, use) => {
if (clockOptions !== undefined) {
await context.addInitScript({
path: path.join(__dirname, '../', './node_modules/sinon/pkg/sinon.js')
@ -101,10 +99,12 @@ exports.test = base.test.extend({
}
await use(context);
}, {
},
{
auto: true,
scope: 'test'
}],
}
],
/**
* Extends the base context class to add codecoverage shim.
* @see {@link https://github.com/mxschmitt/playwright-test-coverage Github Project}
@ -112,19 +112,24 @@ exports.test = base.test.extend({
context: async ({ context }, use) => {
await context.addInitScript(() =>
window.addEventListener('beforeunload', () =>
(window).collectIstanbulCoverage(JSON.stringify((window).__coverage__))
window.collectIstanbulCoverage(JSON.stringify(window.__coverage__))
)
);
await fs.promises.mkdir(istanbulCLIOutput, { recursive: true });
await context.exposeFunction('collectIstanbulCoverage', (coverageJSON) => {
if (coverageJSON) {
fs.writeFileSync(path.join(istanbulCLIOutput, `playwright_coverage_${uuid()}.json`), coverageJSON);
fs.writeFileSync(
path.join(istanbulCLIOutput, `playwright_coverage_${uuid()}.json`),
coverageJSON
);
}
});
await use(context);
for (const page of context.pages()) {
await page.evaluate(() => (window).collectIstanbulCoverage(JSON.stringify((window).__coverage__)));
await page.evaluate(() =>
window.collectIstanbulCoverage(JSON.stringify(window.__coverage__))
);
}
},
/**
@ -147,8 +152,10 @@ exports.test = base.test.extend({
// Assert against console errors during teardown
if (failOnConsoleError) {
messages.forEach(
msg => expect.soft(msg.type(), `Console error detected: ${_consoleMessageToString(msg)}`).not.toEqual('error')
messages.forEach((msg) =>
expect
.soft(msg.type(), `Console error detected: ${_consoleMessageToString(msg)}`)
.not.toEqual('error')
);
}
},

View File

@ -6,8 +6,7 @@ class DomainObjectViewProvider {
}
canView(domainObject) {
return domainObject.type === 'imageFileInput'
|| domainObject.type === 'jsonFileInput';
return domainObject.type === 'imageFileInput' || domainObject.type === 'jsonFileInput';
}
view(domainObject, objectPath) {
@ -36,7 +35,7 @@ document.addEventListener('DOMContentLoaded', () => {
openmct.types.addType('jsonFileInput', {
key: 'jsonFileInput',
name: "JSON File Input Object",
name: 'JSON File Input Object',
creatable: true,
form: [
{
@ -46,16 +45,14 @@ document.addEventListener('DOMContentLoaded', () => {
required: true,
text: 'Select File...',
type: 'application/json',
property: [
"selectFile"
]
property: ['selectFile']
}
]
});
openmct.types.addType('imageFileInput', {
key: 'imageFileInput',
name: "Image File Input Object",
name: 'Image File Input Object',
creatable: true,
form: [
{
@ -65,9 +62,7 @@ document.addEventListener('DOMContentLoaded', () => {
required: true,
text: 'Select File...',
type: 'image/*',
property: [
"selectFile"
]
property: ['selectFile']
}
]
});

View File

@ -24,4 +24,4 @@
}
});
});
}());
})();

View File

@ -35,7 +35,9 @@ async function navigateToFaultManagementWithExample(page) {
* @param {import('@playwright/test').Page} page
*/
async function navigateToFaultManagementWithStaticExample(page) {
await page.addInitScript({ path: path.join(__dirname, './', 'addInitExampleFaultProviderStatic.js') });
await page.addInitScript({
path: path.join(__dirname, './', 'addInitExampleFaultProviderStatic.js')
});
await navigateToFaultItemInTree(page);
}
@ -55,10 +57,12 @@ async function navigateToFaultManagementWithoutExample(page) {
async function navigateToFaultItemInTree(page) {
await page.goto('./', { waitUntil: 'networkidle' });
const faultManagementTreeItem = page.getByRole('tree', {
name: "Main Tree"
}).getByRole('treeitem', {
name: "Fault Management"
const faultManagementTreeItem = page
.getByRole('tree', {
name: 'Main Tree'
})
.getByRole('treeitem', {
name: 'Fault Management'
});
// Navigate to "Fault Management" from the tree
@ -73,7 +77,6 @@ async function acknowledgeFault(page, rowNumber) {
await page.locator('.c-menu >> text="Acknowledge"').click();
// Click [aria-label="Save"]
await page.locator('[aria-label="Save"]').click();
}
/**
@ -192,7 +195,9 @@ async function getFaultResultCount(page) {
* @param {import('@playwright/test').Page} page
*/
function getFault(page, rowNumber) {
const fault = page.locator(`.c-faults-list-view-item-body > .c-fault-mgmt__list >> nth=${rowNumber - 1}`);
const fault = page.locator(
`.c-faults-list-view-item-body > .c-fault-mgmt__list >> nth=${rowNumber - 1}`
);
return fault;
}
@ -210,7 +215,9 @@ function getFaultByName(page, name) {
* @param {import('@playwright/test').Page} page
*/
async function getFaultName(page, rowNumber) {
const faultName = await page.locator(`.c-fault-mgmt__list-faultname >> nth=${rowNumber - 1}`).textContent();
const faultName = await page
.locator(`.c-fault-mgmt__list-faultname >> nth=${rowNumber - 1}`)
.textContent();
return faultName;
}
@ -219,7 +226,9 @@ async function getFaultName(page, rowNumber) {
* @param {import('@playwright/test').Page} page
*/
async function getFaultSeverity(page, rowNumber) {
const faultSeverity = await page.locator(`.c-faults-list-view-item-body .c-fault-mgmt__list-severity >> nth=${rowNumber - 1}`).getAttribute('title');
const faultSeverity = await page
.locator(`.c-faults-list-view-item-body .c-fault-mgmt__list-severity >> nth=${rowNumber - 1}`)
.getAttribute('title');
return faultSeverity;
}
@ -228,7 +237,9 @@ async function getFaultSeverity(page, rowNumber) {
* @param {import('@playwright/test').Page} page
*/
async function getFaultNamespace(page, rowNumber) {
const faultNamespace = await page.locator(`.c-fault-mgmt__list-path >> nth=${rowNumber - 1}`).textContent();
const faultNamespace = await page
.locator(`.c-fault-mgmt__list-path >> nth=${rowNumber - 1}`)
.textContent();
return faultNamespace;
}
@ -237,7 +248,9 @@ async function getFaultNamespace(page, rowNumber) {
* @param {import('@playwright/test').Page} page
*/
async function getFaultTriggerTime(page, rowNumber) {
const faultTriggerTime = await page.locator(`.c-fault-mgmt__list-trigTime >> nth=${rowNumber - 1} >> .c-fault-mgmt-item__value`).textContent();
const faultTriggerTime = await page
.locator(`.c-fault-mgmt__list-trigTime >> nth=${rowNumber - 1} >> .c-fault-mgmt-item__value`)
.textContent();
return faultTriggerTime.toString().trim();
}
@ -247,8 +260,9 @@ async function getFaultTriggerTime(page, rowNumber) {
*/
async function openFaultRowMenu(page, rowNumber) {
// select
await page.locator(`.c-fault-mgmt-item > .c-fault-mgmt__list-action-button >> nth=${rowNumber - 1}`).click();
await page
.locator(`.c-fault-mgmt-item > .c-fault-mgmt__list-action-button >> nth=${rowNumber - 1}`)
.click();
}
// eslint-disable-next-line no-undef

View File

@ -42,7 +42,7 @@ async function enterTextEntry(page, text) {
async function dragAndDropEmbed(page, notebookObject) {
// Create example telemetry object
const swg = await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator"
type: 'Sine Wave Generator'
});
// Navigate to notebook
await page.goto(notebookObject.url);

View File

@ -46,32 +46,39 @@ export async function assertPlanActivities(page, plan, objectUrl) {
}
// Switch to fixed time mode with all plan events within the bounds
await page.goto(`${objectUrl}?tc.mode=fixed&tc.startBound=${startBound}&tc.endBound=${endBound}&tc.timeSystem=utc&view=plan.view`);
await page.goto(
`${objectUrl}?tc.mode=fixed&tc.startBound=${startBound}&tc.endBound=${endBound}&tc.timeSystem=utc&view=plan.view`
);
// Assert that the number of activities in the plan view matches the number of
// activities in the plan data within the specified time bounds
const eventCount = await page.locator('.activity-bounds').count();
expect(eventCount).toEqual(Object.values(plan)
expect(eventCount).toEqual(
Object.values(plan)
.flat()
.filter(event =>
activitiesWithinTimeBounds(event.start, event.end, startBound, endBound)).length);
.filter((event) =>
activitiesWithinTimeBounds(event.start, event.end, startBound, endBound)
).length
);
}
}
}
/**
* Returns true if the activities time bounds overlap, false otherwise.
* @param {number} start1 the start time of the first activity
* @param {number} end1 the end time of the first activity
* @param {number} start2 the start time of the second activity
* @param {number} end2 the end time of the second activity
* @returns {boolean} true if the activities overlap, false otherwise
*/
* @param {number} start1 the start time of the first activity
* @param {number} end1 the end time of the first activity
* @param {number} start2 the start time of the second activity
* @param {number} end2 the end time of the second activity
* @returns {boolean} true if the activities overlap, false otherwise
*/
function activitiesWithinTimeBounds(start1, end1, start2, end2) {
return (start1 >= start2 && start1 <= end2)
|| (end1 >= start2 && end1 <= end2)
|| (start2 >= start1 && start2 <= end1)
|| (end2 >= start1 && end2 <= end1);
return (
(start1 >= start2 && start1 <= end2) ||
(end1 >= start2 && end1 <= end2) ||
(start2 >= start1 && start2 <= end1) ||
(end2 >= start1 && end2 <= end1)
);
}
/**
@ -84,9 +91,11 @@ function activitiesWithinTimeBounds(start1, end1, start2, end2) {
export async function setBoundsToSpanAllActivities(page, planJson, planObjectUrl) {
const activities = Object.values(planJson).flat();
// Get the earliest start value
const start = Math.min(...activities.map(activity => activity.start));
const start = Math.min(...activities.map((activity) => activity.start));
// Get the latest end value
const end = Math.max(...activities.map(activity => activity.end));
const end = Math.max(...activities.map((activity) => activity.end));
// Set the start and end bounds to the earliest start and latest end
await page.goto(`${planObjectUrl}?tc.mode=fixed&tc.startBound=${start}&tc.endBound=${end}&tc.timeSystem=utc&view=plan.view`);
await page.goto(
`${planObjectUrl}?tc.mode=fixed&tc.startBound=${start}&tc.endBound=${end}&tc.timeSystem=utc&view=plan.view`
);
}

View File

@ -69,10 +69,13 @@ const config = {
],
reporter: [
['list'],
['html', {
[
'html',
{
open: 'never',
outputFolder: '../html-test-results' //Must be in different location due to https://github.com/microsoft/playwright/issues/12840
}],
}
],
['junit', { outputFile: '../test-results/results.xml' }],
['github'],
['@deploysentinel/playwright']

View File

@ -19,7 +19,7 @@ const config = {
},
workers: 1,
use: {
browserName: "chromium",
browserName: 'chromium',
baseURL: 'http://localhost:8080/',
headless: false,
ignoreHTTPSErrors: true,
@ -94,10 +94,13 @@ const config = {
],
reporter: [
['list'],
['html', {
[
'html',
{
open: 'on-failure',
outputFolder: '../html-test-results' //Must be in different location due to https://github.com/microsoft/playwright/issues/12840
}]
}
]
]
};

View File

@ -17,7 +17,7 @@ const config = {
reuseExistingServer: !CI
},
use: {
browserName: "chromium",
browserName: 'chromium',
baseURL: 'http://localhost:8080/',
headless: CI, //Only if running locally
ignoreHTTPSErrors: true,

View File

@ -41,10 +41,13 @@ const config = {
reporter: [
['list'],
['junit', { outputFile: '../test-results/results.xml' }],
['html', {
[
'html',
{
open: 'on-failure',
outputFolder: '../html-test-results' //Must be in different location due to https://github.com/microsoft/playwright/issues/12840
}]
}
]
]
};

View File

@ -120,7 +120,7 @@ const theme = 'espresso';
*
* @type {string}
*/
const myItemsFolderName = "My Items";
const myItemsFolderName = 'My Items';
exports.test = test.extend({
// This should follow in the Project's configuration. Can be set to 'snow' in playwright config.js
@ -136,10 +136,7 @@ exports.test = test.extend({
// Attach info about the currently running test and its project.
// This will be used by appActions to fill in the created
// domain object's notes.
page.testNotes = [
`${testInfo.titlePath.join('\n')}`,
`${testInfo.project.name}`
].join('\n');
page.testNotes = [`${testInfo.titlePath.join('\n')}`, `${testInfo.project.name}`].join('\n');
await use(page);
},

View File

@ -274,10 +274,7 @@
"id": "ac0d7eb1-b485-458f-bd2a-a63aa87a3a8a"
}
],
"layoutGrid": [
10,
10
],
"layoutGrid": [10, 10],
"objectStyles": {
"ed63cc29-80e2-4e2b-a472-3d6d4adbf310": {
"staticStyle": {
@ -1455,9 +1452,7 @@
"id": "64e49fe7-5b36-43db-8347-4550b910de4c",
"telemetry": "any",
"operation": "greaterThan",
"input": [
"120"
],
"input": ["120"],
"metadata": "sin"
}
]
@ -1475,10 +1470,7 @@
"id": "59f1c4bf-5d36-450c-9668-6546955fc066",
"telemetry": "any",
"operation": "between",
"input": [
"120",
"-20"
],
"input": ["120", "-20"],
"metadata": "sin"
}
]
@ -1496,9 +1488,7 @@
"id": "6707be12-6a6e-4535-bb97-ab5c86f99934",
"telemetry": "any",
"operation": "lessThan",
"input": [
"-20"
],
"input": ["-20"],
"metadata": "sin"
}
]
@ -1550,9 +1540,7 @@
"id": "64e49fe7-5b36-43db-8347-4550b910de4c",
"telemetry": "any",
"operation": "greaterThan",
"input": [
"120"
],
"input": ["120"],
"metadata": "sin"
}
]
@ -1570,10 +1558,7 @@
"id": "59f1c4bf-5d36-450c-9668-6546955fc066",
"telemetry": "any",
"operation": "between",
"input": [
"120",
"-20"
],
"input": ["120", "-20"],
"metadata": "sin"
}
]
@ -1591,9 +1576,7 @@
"id": "6707be12-6a6e-4535-bb97-ab5c86f99934",
"telemetry": "any",
"operation": "lessThan",
"input": [
"-20"
],
"input": ["-20"],
"metadata": "sin"
}
]
@ -1645,9 +1628,7 @@
"id": "64e49fe7-5b36-43db-8347-4550b910de4c",
"telemetry": "any",
"operation": "greaterThan",
"input": [
"150"
],
"input": ["150"],
"metadata": "sin"
}
]
@ -1665,10 +1646,7 @@
"id": "59f1c4bf-5d36-450c-9668-6546955fc066",
"telemetry": "any",
"operation": "between",
"input": [
"50",
"-50"
],
"input": ["50", "-50"],
"metadata": "sin"
}
]
@ -1720,9 +1698,7 @@
"id": "64e49fe7-5b36-43db-8347-4550b910de4c",
"telemetry": "any",
"operation": "greaterThan",
"input": [
"150"
],
"input": ["150"],
"metadata": "sin"
}
]
@ -1740,10 +1716,7 @@
"id": "59f1c4bf-5d36-450c-9668-6546955fc066",
"telemetry": "any",
"operation": "between",
"input": [
"50",
"-50"
],
"input": ["50", "-50"],
"metadata": "sin"
}
]

View File

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

View File

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

View File

@ -21,7 +21,11 @@
*****************************************************************************/
const { test, expect } = require('../../pluginFixtures.js');
const { createDomainObjectWithDefaults, createNotification, expandEntireTree } = require('../../appActions.js');
const {
createDomainObjectWithDefaults,
createNotification,
expandEntireTree
} = require('../../appActions.js');
test.describe('AppActions', () => {
test('createDomainObjectsWithDefaults', async ({ page }) => {
@ -85,7 +89,7 @@ test.describe('AppActions', () => {
expect(folder3.url).toBe(`${e2eFolder.url}/${folder1.uuid}/${folder2.uuid}/${folder3.uuid}`);
});
});
test("createNotification", async ({ page }) => {
test('createNotification', async ({ page }) => {
await page.goto('./', { waitUntil: 'domcontentloaded' });
await createNotification(page, {
message: 'Test info notification',
@ -144,7 +148,7 @@ test.describe('AppActions', () => {
await page.goto('./#/browse/mine');
await expandEntireTree(page);
const treePane = page.getByRole('tree', {
name: "Main Tree"
name: 'Main Tree'
});
const treePaneCollapsedItems = treePane.getByRole('treeitem', { expanded: false });
expect(await treePaneCollapsedItems.count()).toBe(0);
@ -155,9 +159,9 @@ test.describe('AppActions', () => {
// Click the object specified by 'type'
await page.click(`li[role='menuitem']:text("Clock")`);
await expandEntireTree(page, "Create Modal Tree");
const locatorTree = page.getByRole("tree", {
name: "Create Modal Tree"
await expandEntireTree(page, 'Create Modal Tree');
const locatorTree = page.getByRole('tree', {
name: 'Create Modal Tree'
});
const locatorTreeCollapsedItems = locatorTree.locator('role=treeitem[expanded=false]');
expect(await locatorTreeCollapsedItems.count()).toBe(0);

View File

@ -39,7 +39,6 @@ test.describe('baseFixtures tests', () => {
page.evaluate(() => console.error('This should result in a failure')),
page.waitForEvent('console') // always wait for the event to happen while triggering it!
]);
});
test('Verify that tests pass if console.warn is thrown', async ({ page }) => {
//Go to baseURL
@ -50,6 +49,5 @@ test.describe('baseFixtures tests', () => {
page.evaluate(() => console.warn('This should result in a pass')),
page.waitForEvent('console') // always wait for the event to happen while triggering it!
]);
});
});

View File

@ -21,28 +21,28 @@
*****************************************************************************/
/*
* This test suite template is to be used when creating new test suites. It will be kept up to date with the latest improvements
* made by the Open MCT team. It will also follow our best pratices as those evolve. Please use this structure as a _reference_ and clear
* or update any references when creating a new test suite!
*
* To illustrate current best practices, we've included a mocked up test suite for Renaming a Timer domain object.
*
* Demonstrated:
* - Using appActions to leverage existing functions
* - Structure
* - @unstable annotation
* - await, expect, test, describe syntax
* - Writing a custom function for a test suite
* - Test stub for unfinished test coverage (test.fixme)
*
* The structure should follow
* 1. imports
* 2. test.describe()
* 3. -> test1
* -> test2
* -> test3(stub)
* 4. Any custom functions
*/
* This test suite template is to be used when creating new test suites. It will be kept up to date with the latest improvements
* made by the Open MCT team. It will also follow our best pratices as those evolve. Please use this structure as a _reference_ and clear
* or update any references when creating a new test suite!
*
* To illustrate current best practices, we've included a mocked up test suite for Renaming a Timer domain object.
*
* Demonstrated:
* - Using appActions to leverage existing functions
* - Structure
* - @unstable annotation
* - await, expect, test, describe syntax
* - Writing a custom function for a test suite
* - Test stub for unfinished test coverage (test.fixme)
*
* The structure should follow
* 1. imports
* 2. test.describe()
* 3. -> test1
* -> test2
* -> test3(stub)
* 4. Any custom functions
*/
// Structure: Some standard Imports. Please update the required pathing.
const { test, expect } = require('../../pluginFixtures');
@ -79,7 +79,7 @@ test.describe('Renaming Timer Object', () => {
* some hint as to what went wrong if the test fails.
*/
test('An existing Timer object can be renamed via the 3dot actions menu', async ({ page }) => {
const newObjectName = "Renamed Timer";
const newObjectName = 'Renamed Timer';
// We've created an example of a shared function which pases the page and newObjectName values
await renameTimerFrom3DotMenu(page, timer.url, newObjectName);
@ -89,8 +89,8 @@ test.describe('Renaming Timer Object', () => {
});
test('An existing Timer object can be renamed twice', async ({ page }) => {
const newObjectName = "Renamed Timer";
const newObjectName2 = "Re-Renamed Timer";
const newObjectName = 'Renamed Timer';
const newObjectName2 = 'Re-Renamed Timer';
await renameTimerFrom3DotMenu(page, timer.url, newObjectName);

View File

@ -31,14 +31,12 @@ const { test } = require('../../pluginFixtures.js');
test.describe.skip('pluginFixtures tests', () => {
// test.use({ domainObjectName: 'Timer' });
// let timerUUID;
// test('Creates a timer object @framework @unstable', ({ domainObject }) => {
// const { uuid } = domainObject;
// const uuidRegexp = /[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/;
// expect(uuid).toMatch(uuidRegexp);
// timerUUID = uuid;
// });
// test('Provides same uuid for subsequent uses of the same object @framework', ({ domainObject }) => {
// const { uuid } = domainObject;
// expect(uuid).toEqual(timerUUID);

View File

@ -21,8 +21,8 @@
*****************************************************************************/
/*
* This test suite template is to be used when verifying Test Data files found in /e2e/test-data/
*/
* This test suite template is to be used when verifying Test Data files found in /e2e/test-data/
*/
const { test } = require('../../baseFixtures');
@ -33,4 +33,3 @@ test.describe('recycled_local_storage @localStorage', () => {
await page.goto('./', { waitUntil: 'domcontentloaded' });
});
});

View File

@ -41,7 +41,9 @@ test.describe('Branding tests', () => {
const versionInformationLocator = page.locator('ul.t-info.l-info.s-info').first();
await expect(versionInformationLocator).toBeEnabled();
await expect.soft(versionInformationLocator).toContainText(/Version: \d/);
await expect.soft(versionInformationLocator).toContainText(/Build Date: ((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun))/);
await expect
.soft(versionInformationLocator)
.toContainText(/Build Date: ((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun))/);
await expect.soft(versionInformationLocator).toContainText(/Revision: \b[0-9a-f]{5,40}\b/);
await expect.soft(versionInformationLocator).toContainText(/Branch: ./);
});

View File

@ -21,17 +21,17 @@
*****************************************************************************/
/*
* This test suite is meant to be executed against a couchdb container. More doc to come
*
*/
* This test suite is meant to be executed against a couchdb container. More doc to come
*
*/
const { test, expect } = require('../../pluginFixtures');
test.describe("CouchDB Status Indicator with mocked responses @couchdb", () => {
test.describe('CouchDB Status Indicator with mocked responses @couchdb', () => {
test.use({ failOnConsoleError: false });
//TODO BeforeAll Verify CouchDB Connectivity with APIContext
test('Shows green if connected', async ({ page }) => {
await page.route('**/openmct/mine', route => {
await page.route('**/openmct/mine', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
@ -40,11 +40,13 @@ test.describe("CouchDB Status Indicator with mocked responses @couchdb", () => {
});
//Go to baseURL
await page.goto('./#/browse/mine?hideTree=true&hideInspector=true', { waitUntil: 'networkidle' });
await page.goto('./#/browse/mine?hideTree=true&hideInspector=true', {
waitUntil: 'networkidle'
});
await expect(page.locator('div:has-text("CouchDB is connected")').nth(3)).toBeVisible();
});
test('Shows red if not connected', async ({ page }) => {
await page.route('**/openmct/**', route => {
await page.route('**/openmct/**', (route) => {
route.fulfill({
status: 503,
contentType: 'application/json',
@ -53,11 +55,13 @@ test.describe("CouchDB Status Indicator with mocked responses @couchdb", () => {
});
//Go to baseURL
await page.goto('./#/browse/mine?hideTree=true&hideInspector=true', { waitUntil: 'networkidle' });
await page.goto('./#/browse/mine?hideTree=true&hideInspector=true', {
waitUntil: 'networkidle'
});
await expect(page.locator('div:has-text("CouchDB is offline")').nth(3)).toBeVisible();
});
test('Shows unknown if it receives an unexpected response code', async ({ page }) => {
await page.route('**/openmct/mine', route => {
await page.route('**/openmct/mine', (route) => {
route.fulfill({
status: 418,
contentType: 'application/json',
@ -66,12 +70,14 @@ test.describe("CouchDB Status Indicator with mocked responses @couchdb", () => {
});
//Go to baseURL
await page.goto('./#/browse/mine?hideTree=true&hideInspector=true', { waitUntil: 'networkidle' });
await page.goto('./#/browse/mine?hideTree=true&hideInspector=true', {
waitUntil: 'networkidle'
});
await expect(page.locator('div:has-text("CouchDB connectivity unknown")').nth(3)).toBeVisible();
});
});
test.describe("CouchDB initialization with mocked responses @couchdb", () => {
test.describe('CouchDB initialization with mocked responses @couchdb', () => {
test.use({ failOnConsoleError: false });
test("'My Items' folder is created if it doesn't exist", async ({ page }) => {
const mockedMissingObjectResponsefromCouchDB = {
@ -83,29 +89,30 @@ test.describe("CouchDB initialization with mocked responses @couchdb", () => {
// Override the first request to GET openmct/mine to return a 404.
// This simulates the case of starting Open MCT with a fresh database
// and no "My Items" folder created yet.
await page.route('**/mine', route => {
await page.route(
'**/mine',
(route) => {
route.fulfill(mockedMissingObjectResponsefromCouchDB);
}, { times: 1 });
},
{ times: 1 }
);
// Set up promise to verify that a PUT request to create "My Items"
// folder was made.
const putMineFolderRequest = page.waitForRequest(req =>
req.url().endsWith('/mine')
&& req.method() === 'PUT');
const putMineFolderRequest = page.waitForRequest(
(req) => req.url().endsWith('/mine') && req.method() === 'PUT'
);
// Set up promise to verify that a GET request to retrieve "My Items"
// folder was made.
const getMineFolderRequest = page.waitForRequest(req =>
req.url().endsWith('/mine')
&& req.method() === 'GET');
const getMineFolderRequest = page.waitForRequest(
(req) => req.url().endsWith('/mine') && req.method() === 'GET'
);
// Go to baseURL.
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Wait for both requests to resolve.
await Promise.all([
putMineFolderRequest,
getMineFolderRequest
]);
await Promise.all([putMineFolderRequest, getMineFolderRequest]);
});
});

View File

@ -47,7 +47,6 @@ test.describe('Example Event Generator CRUD Operations', () => {
});
test.describe('Example Event Generator Telemetry Event Verficiation', () => {
test.fixme('telemetry is coming in for test event', async ({ page }) => {
// Go to object created in step one
// Verify the telemetry table is filled with > 1 row

View File

@ -27,7 +27,10 @@ This test suite is dedicated to tests which verify the basic operations surround
const { test, expect } = require('../../../../baseFixtures');
test.describe('Sine Wave Generator', () => {
test('Create new Sine Wave Generator Object and validate create Form Logic', async ({ page, browserName }) => {
test('Create new Sine Wave Generator Object and validate create Form Logic', async ({
page,
browserName
}) => {
// eslint-disable-next-line playwright/no-skipped-test
test.skip(browserName === 'firefox', 'This test needs to be updated to work with firefox');
@ -45,7 +48,9 @@ test.describe('Sine Wave Generator', () => {
await expect(page.locator('.c-form-row__state-indicator').first()).toHaveClass(/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');
await expect(
page.locator('.c-form__section div:nth-child(3) .form-row .c-form-row__state-indicator')
).not.toContain('.req');
await page.locator('textarea[type="text"]').fill('Optional Note Text');
// Period
@ -67,20 +72,32 @@ test.describe('Sine Wave Generator', () => {
await expect(page.locator('div:nth-child(9) .c-form-row__state-indicator')).toHaveClass(/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 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(/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('New Sine Wave Generator');
await page
.locator(
'text=Properties Title Notes Period Amplitude Offset Data Rate (hz) Phase (radians) Ra >> input[type="text"]'
)
.fill('New Sine Wave Generator');
await expect(page.locator('.c-form-row__state-indicator').first()).toHaveClass(/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('div:nth-child(4) .c-form-row__state-indicator')).toHaveClass(/invalid/);
await expect(page.locator('div:nth-child(4) .c-form-row__state-indicator')).toHaveClass(
/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('div:nth-child(4) .c-form-row__state-indicator')).toHaveClass(/valid/);
await expect(page.locator('div:nth-child(4) .c-form-row__state-indicator')).toHaveClass(
/valid/
);
// Verify that can change value of number field by up/down arrows keys
// Click .field.control.l-input-sm input >> nth=0
@ -94,17 +111,19 @@ test.describe('Sine Wave Generator', () => {
await expect(value).toBe('6');
//Click text=OK
await Promise.all([
page.waitForNavigation(),
page.click('button:has-text("OK")')
]);
await Promise.all([page.waitForNavigation(), page.click('button:has-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');
await expect(page.locator('.l-browse-bar__object-name')).toContainText(
'New Sine Wave Generator'
);
// Verify canvas rendered and can be interacted with
await page.locator('canvas').nth(1).click({
await page
.locator('canvas')
.nth(1)
.click({
position: {
x: 341,
y: 28
@ -113,7 +132,8 @@ test.describe('Sine Wave Generator', () => {
// Verify that where we click on canvas shows the number we clicked on
// Note that any number will do, we just care that a number exists
await expect(page.locator('.value-to-display-nearestValue')).toContainText(/[+-]?([0-9]*[.])?[0-9]+/);
await expect(page.locator('.value-to-display-nearestValue')).toContainText(
/[+-]?([0-9]*[.])?[0-9]+/
);
});
});

View File

@ -34,7 +34,9 @@ const jsonFilePath = 'e2e/test-data/ExampleLayouts.json';
const imageFilePath = 'e2e/test-data/rick.jpg';
test.describe('Form Validation Behavior', () => {
test('Required Field indicators appear if title is empty and can be corrected', async ({ page }) => {
test('Required Field indicators appear if title is empty and can be corrected', async ({
page
}) => {
//Go to baseURL
await page.goto('./', { waitUntil: 'domcontentloaded' });
@ -60,10 +62,7 @@ test.describe('Form Validation Behavior', () => {
await expect(page.locator('.c-form-row__state-indicator').first()).not.toHaveClass(/invalid/);
//Finish Creating Domain Object
await Promise.all([
page.waitForNavigation(),
page.click('button:has-text("OK")')
]);
await Promise.all([page.waitForNavigation(), page.click('button:has-text("OK")')]);
//Verify that the Domain Object has been created with the corrected title property
await expect(page.locator('.l-browse-bar__object-name')).toContainText(TEST_FOLDER);
@ -72,7 +71,9 @@ test.describe('Form Validation Behavior', () => {
test.describe('Form File Input Behavior', () => {
test.beforeEach(async ({ page }) => {
await page.addInitScript({ path: path.join(__dirname, '../../helper', 'addInitFileInputObject.js') });
await page.addInitScript({
path: path.join(__dirname, '../../helper', 'addInitFileInputObject.js')
});
});
test('Can select a JSON file type', async ({ page }) => {
@ -107,7 +108,9 @@ test.describe('Form File Input Behavior', () => {
test.describe('Persistence operations @addInit', () => {
// add non persistable root item
test.beforeEach(async ({ page }) => {
await page.addInitScript({ path: path.join(__dirname, '../../helper', 'addNoneditableObject.js') });
await page.addInitScript({
path: path.join(__dirname, '../../helper', 'addNoneditableObject.js')
});
});
test('Persistability should be respected in the create form location field', async ({ page }) => {
@ -130,7 +133,9 @@ test.describe('Persistence operations @addInit', () => {
test.describe('Persistence operations @couchdb', () => {
test.use({ failOnConsoleError: false });
test('Editing object properties should generate a single persistence operation', async ({ page }) => {
test('Editing object properties should generate a single persistence operation', async ({
page
}) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/5616'
@ -145,7 +150,7 @@ test.describe('Persistence operations @couchdb', () => {
// Count all persistence operations (PUT requests) for this specific object
let putRequestCount = 0;
page.on('request', req => {
page.on('request', (req) => {
if (req.method() === 'PUT' && req.url().endsWith(clock.uuid)) {
putRequestCount += 1;
}
@ -156,15 +161,22 @@ test.describe('Persistence operations @couchdb', () => {
await page.click('li[title="Edit properties of this object."]');
// Modify the display format from default 12hr -> 24hr and click 'Save'
await page.locator('select[aria-label="12 or 24 hour clock"]').selectOption({ value: 'clock24' });
await page
.locator('select[aria-label="12 or 24 hour clock"]')
.selectOption({ value: 'clock24' });
await page.click('button[aria-label="Save"]');
await expect.poll(() => putRequestCount, {
await expect
.poll(() => putRequestCount, {
message: 'Verify a single PUT request was made to persist the object',
timeout: 1000
}).toEqual(1);
})
.toEqual(1);
});
test('Can create an object after a conflict error @couchdb @2p', async ({ page, openmctConfig }) => {
test('Can create an object after a conflict error @couchdb @2p', async ({
page,
openmctConfig
}) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/5982'
@ -201,9 +213,9 @@ test.describe('Persistence operations @couchdb', () => {
// Both pages: Fill in the 'Name' form field.
await Promise.all([
nameInput.fill(""),
nameInput.fill(''),
nameInput.fill(`Clock:${genUuid()}`),
nameInput2.fill(""),
nameInput2.fill(''),
nameInput2.fill(`Clock:${genUuid()}`)
]);
@ -212,10 +224,7 @@ test.describe('Persistence operations @couchdb', () => {
const testNotes = page.testNotes;
const notesInput = page.locator('form[name="mctForm"] #notes-textarea');
const notesInput2 = page2.locator('form[name="mctForm"] #notes-textarea');
await Promise.all([
notesInput.fill(testNotes),
notesInput2.fill(testNotes)
]);
await Promise.all([notesInput.fill(testNotes), notesInput2.fill(testNotes)]);
// Page 2: Click "OK" to create the domain object and wait for navigation.
// This will update the composition of the parent folder, setting the
@ -241,9 +250,11 @@ test.describe('Persistence operations @couchdb', () => {
]);
// Page 1: Verify that the conflict has occurred and an error notification is displayed.
await expect(page.locator('.c-message-banner__message', {
hasText: "Conflict detected while saving mine"
})).toBeVisible();
await expect(
page.locator('.c-message-banner__message', {
hasText: 'Conflict detected while saving mine'
})
).toBeVisible();
// Page 1: Start logging console errors from this point on
let errors = [];
@ -259,14 +270,19 @@ test.describe('Persistence operations @couchdb', () => {
});
// Page 1: Wait for save progress dialog to appear/disappear
await page.locator('.c-message-banner__message', {
hasText: 'Do not navigate away from this page or close this browser tab while this message is displayed.',
await page
.locator('.c-message-banner__message', {
hasText:
'Do not navigate away from this page or close this browser tab while this message is displayed.',
state: 'visible'
}).waitFor({ state: 'hidden' });
})
.waitFor({ state: 'hidden' });
// Page 1: Navigate to 'My Items' and verify that the second clock was created
await page.goto('./#/browse/mine');
await expect(page.locator(`.c-grid-item__name[title="${clockAfterConflict.name}"]`)).toBeVisible();
await expect(
page.locator(`.c-grid-item__name[title="${clockAfterConflict.name}"]`)
).toBeVisible();
// Verify no console errors occurred
expect(errors).toHaveLength(0);
@ -274,9 +290,9 @@ test.describe('Persistence operations @couchdb', () => {
});
test.describe('Form Correctness by Object Type', () => {
test.fixme('Verify correct behavior of number object (SWG)', async ({page}) => {});
test.fixme('Verify correct behavior of number object Timer', async ({page}) => {});
test.fixme('Verify correct behavior of number object Plan View', async ({page}) => {});
test.fixme('Verify correct behavior of number object Clock', async ({page}) => {});
test.fixme('Verify correct behavior of number object Hyperlink', async ({page}) => {});
test.fixme('Verify correct behavior of number object (SWG)', async ({ page }) => {});
test.fixme('Verify correct behavior of number object Timer', async ({ page }) => {});
test.fixme('Verify correct behavior of number object Plan View', async ({ page }) => {});
test.fixme('Verify correct behavior of number object Clock', async ({ page }) => {});
test.fixme('Verify correct behavior of number object Hyperlink', async ({ page }) => {});
});

View File

@ -31,7 +31,9 @@ const path = require('path');
test.describe('Persistence operations @addInit', () => {
// add non persistable root item
test.beforeEach(async ({ page }) => {
await page.addInitScript({ path: path.join(__dirname, '../../helper', 'addNoneditableObject.js') });
await page.addInitScript({
path: path.join(__dirname, '../../helper', 'addNoneditableObject.js')
});
});
test('Non-persistable objects should not show persistence related actions', async ({ page }) => {
@ -44,6 +46,14 @@ test.describe('Persistence operations @addInit', () => {
const menuOptions = page.locator('.c-menu li');
await expect.soft(menuOptions).toContainText(['Open In New Tab', 'View', 'Create Link']);
await expect(menuOptions).not.toContainText(['Move', 'Duplicate', 'Remove', 'Add New Folder', 'Edit Properties...', 'Export as JSON', 'Import from JSON']);
await expect(menuOptions).not.toContainText([
'Move',
'Duplicate',
'Remove',
'Add New Folder',
'Edit Properties...',
'Export as JSON',
'Import from JSON'
]);
});
});

View File

@ -28,7 +28,10 @@ const { test, expect } = require('../../pluginFixtures');
const { createDomainObjectWithDefaults } = require('../../appActions');
test.describe('Move & link item tests', () => {
test('Create a basic object and verify that it can be moved to another folder', async ({ page, openmctConfig }) => {
test('Create a basic object and verify that it can be moved to another folder', async ({
page,
openmctConfig
}) => {
const { myItemsFolderName } = openmctConfig;
// Go to Open MCT
@ -55,18 +58,22 @@ test.describe('Move & link item tests', () => {
const treePane = page.getByRole('tree', {
name: 'Main Tree'
});
await treePane.getByRole('treeitem', {
await treePane
.getByRole('treeitem', {
name: 'Parent Folder'
}).click({
})
.click({
button: 'right'
});
await page.getByRole('menuitem', {
await page
.getByRole('menuitem', {
name: /Move/
}).click();
})
.click();
const createModalTree = page.getByRole('tree', {
name: "Create Modal Tree"
name: 'Create Modal Tree'
});
const myItemsLocatorTreeItem = createModalTree.getByRole('treeitem', {
name: myItemsFolderName
@ -100,14 +107,18 @@ test.describe('Move & link item tests', () => {
await page.locator('[aria-label="Cancel"]').click();
// Move Child Folder from Parent Folder to My Items
await treePane.getByRole('treeitem', {
await treePane
.getByRole('treeitem', {
name: new RegExp(childFolder.name)
}).click({
})
.click({
button: 'right'
});
await page.getByRole('menuitem', {
await page
.getByRole('menuitem', {
name: /Move/
}).click();
})
.click();
await myItemsLocatorTreeItem.click();
await page.locator('[aria-label="Save"]').click();
@ -118,7 +129,10 @@ test.describe('Move & link item tests', () => {
// Expect that Child Folder is in My Items, the root folder
expect(myItemsPaneTreeItem.locator('nth=0:has(text=Child Folder)')).toBeTruthy();
});
test('Create a basic object and verify that it cannot be moved to telemetry object without Composition Provider', async ({ page, openmctConfig }) => {
test('Create a basic object and verify that it cannot be moved to telemetry object without Composition Provider', async ({
page,
openmctConfig
}) => {
const { myItemsFolderName } = openmctConfig;
// Go to Open MCT
@ -158,11 +172,10 @@ test.describe('Move & link item tests', () => {
await page.locator(`text=Open MCT ${myItemsFolderName} >> span`).nth(3).click();
// Select Folder Object and select Move from context menu
await Promise.all([
page.waitForNavigation(),
page.locator(`a:has-text("${folder}")`).click()
]);
await page.locator('.c-tree__item.is-navigated-object .c-tree__item__label .c-tree__item__type-icon').click({
await Promise.all([page.waitForNavigation(), page.locator(`a:has-text("${folder}")`).click()]);
await page
.locator('.c-tree__item.is-navigated-object .c-tree__item__label .c-tree__item__type-icon')
.click({
button: 'right'
});
await page.locator('li.icon-move').click();
@ -175,7 +188,10 @@ test.describe('Move & link item tests', () => {
expect(okButtonStateDisabled2).toBeTruthy();
});
test('Create a basic object and verify that it can be linked to another folder', async ({ page, openmctConfig }) => {
test('Create a basic object and verify that it can be linked to another folder', async ({
page,
openmctConfig
}) => {
const { myItemsFolderName } = openmctConfig;
// Go to Open MCT
@ -202,18 +218,22 @@ test.describe('Move & link item tests', () => {
const treePane = page.getByRole('tree', {
name: 'Main Tree'
});
await treePane.getByRole('treeitem', {
await treePane
.getByRole('treeitem', {
name: 'Parent Folder'
}).click({
})
.click({
button: 'right'
});
await page.getByRole('menuitem', {
await page
.getByRole('menuitem', {
name: /Move/
}).click();
})
.click();
const createModalTree = page.getByRole('tree', {
name: "Create Modal Tree"
name: 'Create Modal Tree'
});
const myItemsLocatorTreeItem = createModalTree.getByRole('treeitem', {
name: myItemsFolderName
@ -247,14 +267,18 @@ test.describe('Move & link item tests', () => {
await page.locator('[aria-label="Cancel"]').click();
// Move Child Folder from Parent Folder to My Items
await treePane.getByRole('treeitem', {
await treePane
.getByRole('treeitem', {
name: new RegExp(childFolder.name)
}).click({
})
.click({
button: 'right'
});
await page.getByRole('menuitem', {
await page
.getByRole('menuitem', {
name: /Link/
}).click();
})
.click();
await myItemsLocatorTreeItem.click();
await page.locator('[aria-label="Save"]').click();
@ -267,10 +291,13 @@ test.describe('Move & link item tests', () => {
});
});
test.fixme('Cannot move a previously created domain object to non-peristable object in Move Modal', async ({ page }) => {
test.fixme(
'Cannot move a previously created domain object to non-peristable object in Move Modal',
async ({ page }) => {
//Create a domain object
//Save Domain object
//Move Object and verify that cannot select non-persistable object
//Move Object to My Items
//Verify successful move
});
}
);

View File

@ -59,10 +59,14 @@ test.describe('Notifications List', () => {
await page.click('button[aria-label="Dismiss notification of Error message"]');
// Verify there is no a notification (listitem) with the text "Error message" since it was dismissed
expect(await page.locator('div[role="dialog"] div[role="listitem"]').innerText()).not.toContain('Error message');
expect(await page.locator('div[role="dialog"] div[role="listitem"]').innerText()).not.toContain(
'Error message'
);
// Verify there is still a notification (listitem) with the text "Alert message"
expect(await page.locator('div[role="dialog"] div[role="listitem"]').innerText()).toContain('Alert message');
expect(await page.locator('div[role="dialog"] div[role="listitem"]').innerText()).toContain(
'Alert message'
);
// Click on button with aria-label="Dismiss notification of Alert message"
await page.click('button[aria-label="Dismiss notification of Alert message"]');
@ -73,7 +77,9 @@ test.describe('Notifications List', () => {
});
test.describe('Notification Overlay', () => {
test('Closing notification list after notification banner disappeared does not cause it to open automatically', async ({ page }) => {
test('Closing notification list after notification banner disappeared does not cause it to open automatically', async ({
page
}) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/6130'
@ -92,7 +98,7 @@ test.describe('Notification Overlay', () => {
expect(await page.locator('div[role="dialog"]').isVisible()).toBe(true);
// Wait until there is no Notification Banner
await page.waitForSelector('div[role="alert"]', { state: 'detached'});
await page.waitForSelector('div[role="alert"]', { state: 'detached' });
// Click on the "Close" button of the Notification List
await page.click('button[aria-label="Close"]');

View File

@ -20,13 +20,20 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
const { test, expect } = require('../../../pluginFixtures');
const { createPlanFromJSON, createDomainObjectWithDefaults, selectInspectorTab } = require('../../../appActions');
const {
createPlanFromJSON,
createDomainObjectWithDefaults,
selectInspectorTab
} = require('../../../appActions');
const testPlan1 = require('../../../test-data/examplePlans/ExamplePlan_Small1.json');
const testPlan2 = require('../../../test-data/examplePlans/ExamplePlan_Small2.json');
const { assertPlanActivities, setBoundsToSpanAllActivities } = require('../../../helper/planningUtils');
const {
assertPlanActivities,
setBoundsToSpanAllActivities
} = require('../../../helper/planningUtils');
const { getPreciseDuration } = require('../../../../src/utils/duration');
test.describe("Gantt Chart", () => {
test.describe('Gantt Chart', () => {
let ganttChart;
let plan;
test.beforeEach(async ({ page }) => {
@ -40,24 +47,28 @@ test.describe("Gantt Chart", () => {
});
});
test("Displays all plan events", async ({ page }) => {
test('Displays all plan events', async ({ page }) => {
await page.goto(ganttChart.url);
await assertPlanActivities(page, testPlan1, ganttChart.url);
});
test("Replaces a plan with a new plan", async ({ page }) => {
test('Replaces a plan with a new plan', async ({ page }) => {
await assertPlanActivities(page, testPlan1, ganttChart.url);
await createPlanFromJSON(page, {
json: testPlan2,
parent: ganttChart.uuid
});
const replaceModal = page.getByRole('dialog').filter({ hasText: "This action will replace the current Plan. Do you want to continue?" });
const replaceModal = page
.getByRole('dialog')
.filter({ hasText: 'This action will replace the current Plan. Do you want to continue?' });
await expect(replaceModal).toBeVisible();
await page.getByRole('button', { name: 'OK' }).click();
await assertPlanActivities(page, testPlan2, ganttChart.url);
});
test("Can select a single activity and display its details in the inspector", async ({ page }) => {
test('Can select a single activity and display its details in the inspector', async ({
page
}) => {
test.slow();
await page.goto(ganttChart.url);
@ -65,12 +76,23 @@ test.describe("Gantt Chart", () => {
const activities = Object.values(testPlan1).flat();
const activity = activities[0];
await page.locator('g').filter({ hasText: new RegExp(activity.name) }).click();
await page
.locator('g')
.filter({ hasText: new RegExp(activity.name) })
.click();
await selectInspectorTab(page, 'Activity');
const startDateTime = await page.locator('.c-inspect-properties__label:has-text("Start DateTime")+.c-inspect-properties__value').innerText();
const endDateTime = await page.locator('.c-inspect-properties__label:has-text("End DateTime")+.c-inspect-properties__value').innerText();
const duration = await page.locator('.c-inspect-properties__label:has-text("duration")+.c-inspect-properties__value').innerText();
const startDateTime = await page
.locator(
'.c-inspect-properties__label:has-text("Start DateTime")+.c-inspect-properties__value'
)
.innerText();
const endDateTime = await page
.locator('.c-inspect-properties__label:has-text("End DateTime")+.c-inspect-properties__value')
.innerText();
const duration = await page
.locator('.c-inspect-properties__label:has-text("duration")+.c-inspect-properties__value')
.innerText();
const expectedStartDate = new Date(activity.start).toISOString();
const actualStartDate = new Date(startDateTime).toISOString();
@ -98,6 +120,8 @@ test.describe("Gantt Chart", () => {
await page.goto(ganttChart.url);
// Assert that the Plan's status is displayed as draft
expect(await page.locator('.u-contents.c-swimlane.is-status--draft').count()).toBe(Object.keys(testPlan1).length);
expect(await page.locator('.u-contents.c-swimlane.is-status--draft').count()).toBe(
Object.keys(testPlan1).length
);
});
});

View File

@ -24,7 +24,7 @@ const { createPlanFromJSON } = require('../../../appActions');
const testPlan1 = require('../../../test-data/examplePlans/ExamplePlan_Small1.json');
const { assertPlanActivities } = require('../../../helper/planningUtils');
test.describe("Plan", () => {
test.describe('Plan', () => {
let plan;
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'domcontentloaded' });
@ -33,7 +33,7 @@ test.describe("Plan", () => {
});
});
test("Displays all plan events", async ({ page }) => {
test('Displays all plan events', async ({ page }) => {
await assertPlanActivities(page, testPlan1, plan.url);
});
});

View File

@ -24,65 +24,69 @@ const { test, expect } = require('../../../pluginFixtures');
const { createDomainObjectWithDefaults, createPlanFromJSON } = require('../../../appActions');
const testPlan = {
"TEST_GROUP": [
TEST_GROUP: [
{
"name": "Past event 1",
"start": 1660320408000,
"end": 1660343797000,
"type": "TEST-GROUP",
"color": "orange",
"textColor": "white"
name: 'Past event 1',
start: 1660320408000,
end: 1660343797000,
type: 'TEST-GROUP',
color: 'orange',
textColor: 'white'
},
{
"name": "Past event 2",
"start": 1660406808000,
"end": 1660429160000,
"type": "TEST-GROUP",
"color": "orange",
"textColor": "white"
name: 'Past event 2',
start: 1660406808000,
end: 1660429160000,
type: 'TEST-GROUP',
color: 'orange',
textColor: 'white'
},
{
"name": "Past event 3",
"start": 1660493208000,
"end": 1660503981000,
"type": "TEST-GROUP",
"color": "orange",
"textColor": "white"
name: 'Past event 3',
start: 1660493208000,
end: 1660503981000,
type: 'TEST-GROUP',
color: 'orange',
textColor: 'white'
},
{
"name": "Past event 4",
"start": 1660579608000,
"end": 1660624108000,
"type": "TEST-GROUP",
"color": "orange",
"textColor": "white"
name: 'Past event 4',
start: 1660579608000,
end: 1660624108000,
type: 'TEST-GROUP',
color: 'orange',
textColor: 'white'
},
{
"name": "Past event 5",
"start": 1660666008000,
"end": 1660681529000,
"type": "TEST-GROUP",
"color": "orange",
"textColor": "white"
name: 'Past event 5',
start: 1660666008000,
end: 1660681529000,
type: 'TEST-GROUP',
color: 'orange',
textColor: 'white'
}
]
};
test.describe("Time Strip", () => {
test("Create two Time Strips, add a single Plan to both, and verify they can have separate Indepdenent Time Contexts @unstable", async ({ page }) => {
test.describe('Time Strip', () => {
test('Create two Time Strips, add a single Plan to both, and verify they can have separate Indepdenent Time Contexts @unstable', async ({
page
}) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/5627'
});
// Constant locators
const independentTimeConductorInputs = page.locator('.l-shell__main-independent-time-conductor .c-input--datetime');
const independentTimeConductorInputs = page.locator(
'.l-shell__main-independent-time-conductor .c-input--datetime'
);
const activityBounds = page.locator('.activity-bounds');
// Goto baseURL
await page.goto('./', { waitUntil: 'domcontentloaded' });
const timestrip = await test.step("Create a Time Strip", async () => {
const timestrip = await test.step('Create a Time Strip', async () => {
const createdTimeStrip = await createDomainObjectWithDefaults(page, { type: 'Time Strip' });
const objectName = await page.locator('.l-browse-bar__object-name').innerText();
expect(objectName).toBe(createdTimeStrip.name);
@ -90,7 +94,7 @@ test.describe("Time Strip", () => {
return createdTimeStrip;
});
const plan = await test.step("Create a Plan and add it to the timestrip", async () => {
const plan = await test.step('Create a Plan and add it to the timestrip', async () => {
const createdPlan = await createPlanFromJSON(page, {
name: 'Test Plan',
json: testPlan
@ -106,7 +110,9 @@ test.describe("Time Strip", () => {
const endBound = testPlan.TEST_GROUP[testPlan.TEST_GROUP.length - 1].end;
// Switch to fixed time mode with all plan events within the bounds
await page.goto(`${timestrip.url}?tc.mode=fixed&tc.startBound=${startBound}&tc.endBound=${endBound}&tc.timeSystem=utc&view=time-strip.view`);
await page.goto(
`${timestrip.url}?tc.mode=fixed&tc.startBound=${startBound}&tc.endBound=${endBound}&tc.timeSystem=utc&view=time-strip.view`
);
// Verify all events are displayed
const eventCount = await page.locator('.activity-bounds').count();
@ -115,7 +121,7 @@ test.describe("Time Strip", () => {
return createdPlan;
});
await test.step("TimeStrip can use the Independent Time Conductor", async () => {
await test.step('TimeStrip can use the Independent Time Conductor', async () => {
// Activate Independent Time Conductor in Fixed Time Mode
await page.click('.c-toggle-switch__slider');
expect(await activityBounds.count()).toEqual(0);
@ -135,11 +141,11 @@ test.describe("Time Strip", () => {
expect(await activityBounds.count()).toEqual(1);
});
await test.step("Can have multiple TimeStrips with the same plan linked and different Independent Time Contexts", async () => {
await test.step('Can have multiple TimeStrips with the same plan linked and different Independent Time Contexts', async () => {
// Create another Time Strip and verify that it has been created
const createdTimeStrip = await createDomainObjectWithDefaults(page, {
type: 'Time Strip',
name: "Another Time Strip"
name: 'Another Time Strip'
});
const objectName = await page.locator('.l-browse-bar__object-name').innerText();

View File

@ -27,8 +27,9 @@ This test suite is dedicated to tests which verify the basic operations surround
const { test, expect } = require('../../../../baseFixtures');
test.describe('Clock Generator CRUD Operations', () => {
test('Timezone dropdown will collapse when clicked outside or on dropdown icon again', async ({ page }) => {
test('Timezone dropdown will collapse when clicked outside or on dropdown icon again', async ({
page
}) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/4878'
@ -45,22 +46,21 @@ test.describe('Clock Generator CRUD Operations', () => {
// Click .icon-arrow-down
await page.locator('.icon-arrow-down').click();
//verify if the autocomplete dropdown is visible
await expect(page.locator(".c-input--autocomplete__options")).toBeVisible();
await expect(page.locator('.c-input--autocomplete__options')).toBeVisible();
// Click .icon-arrow-down
await page.locator('.icon-arrow-down').click();
// Verify clicking on the autocomplete arrow collapses the dropdown
await expect(page.locator(".c-input--autocomplete__options")).toBeHidden();
await expect(page.locator('.c-input--autocomplete__options')).toBeHidden();
// Click timezone input to open dropdown
await page.locator('.c-input--autocomplete__input').click();
//verify if the autocomplete dropdown is visible
await expect(page.locator(".c-input--autocomplete__options")).toBeVisible();
await expect(page.locator('.c-input--autocomplete__options')).toBeVisible();
// Verify clicking outside the autocomplete dropdown collapses it
await page.locator('text=Timezone').click();
// Verify clicking on the autocomplete arrow collapses the dropdown
await expect(page.locator(".c-input--autocomplete__options")).toBeHidden();
await expect(page.locator('.c-input--autocomplete__options')).toBeHidden();
});
});

View File

@ -33,7 +33,7 @@ let conditionSetUrl;
let getConditionSetIdentifierFromUrl;
test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
test.beforeAll(async ({ browser}) => {
test.beforeAll(async ({ browser }) => {
//TODO: This needs to be refactored
const context = await browser.newContext();
const page = await context.newPage();
@ -42,10 +42,7 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
await page.locator('li[role="menuitem"]:has-text("Condition Set")').click();
await Promise.all([
page.waitForNavigation(),
page.click('button:has-text("OK")')
]);
await Promise.all([page.waitForNavigation(), page.click('button:has-text("OK")')]);
//Save localStorage for future test execution
await context.storageState({ path: './e2e/test-data/recycled_local_storage.json' });
@ -62,27 +59,29 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
test.use({ storageState: './e2e/test-data/recycled_local_storage.json' });
//Begin suite of tests again localStorage
test('Condition set object properties persist in main view and inspector @localStorage', async ({ page }) => {
test('Condition set object properties persist in main view and inspector @localStorage', async ({
page
}) => {
//Navigate to baseURL with injected localStorage
await page.goto(conditionSetUrl, { waitUntil: 'networkidle' });
//Assertions on loaded Condition Set in main view. This is a stateful transition step after page.goto()
await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set');
await expect
.soft(page.locator('.l-browse-bar__object-name'))
.toContainText('Unnamed Condition Set');
//Assertions on loaded Condition Set in Inspector
expect.soft(page.locator('_vue=item.name=Unnamed Condition Set')).toBeTruthy();
//Reload Page
await Promise.all([
page.reload(),
page.waitForLoadState('networkidle')
]);
await Promise.all([page.reload(), page.waitForLoadState('networkidle')]);
//Re-verify after reload
await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set');
await expect
.soft(page.locator('.l-browse-bar__object-name'))
.toContainText('Unnamed Condition Set');
//Assertions on loaded Condition Set in Inspector
expect.soft(page.locator('_vue=item.name=Unnamed Condition Set')).toBeTruthy();
});
test('condition set object can be modified on @localStorage', async ({ page, openmctConfig }) => {
const { myItemsFolderName } = openmctConfig;
@ -90,22 +89,37 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
await page.goto(conditionSetUrl, { waitUntil: 'networkidle' });
//Assertions on loaded Condition Set in main view. This is a stateful transition step after page.goto()
await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set');
await expect
.soft(page.locator('.l-browse-bar__object-name'))
.toContainText('Unnamed Condition Set');
//Update the Condition Set properties
// Click Edit Button
await page.locator('text=Conditions View Snapshot >> button').nth(3).click();
//Edit Condition Set Name from main view
await page.locator('.l-browse-bar__object-name').filter({ hasText: 'Unnamed Condition Set' }).first().fill('Renamed Condition Set');
await page.locator('.l-browse-bar__object-name').filter({ hasText: 'Renamed Condition Set' }).first().press('Enter');
await page
.locator('.l-browse-bar__object-name')
.filter({ hasText: 'Unnamed Condition Set' })
.first()
.fill('Renamed Condition Set');
await page
.locator('.l-browse-bar__object-name')
.filter({ hasText: 'Renamed Condition Set' })
.first()
.press('Enter');
// Click Save Button
await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
await page
.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button')
.nth(1)
.click();
// Click Save and Finish Editing Option
await page.locator('text=Save and Finish Editing').click();
//Verify Main section reflects updated Name Property
await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Renamed Condition Set');
await expect
.soft(page.locator('.l-browse-bar__object-name'))
.toContainText('Renamed Condition Set');
// Verify Inspector properties
// Verify Inspector has updated Name property
@ -123,13 +137,12 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
expect(page.locator('a:has-text("Renamed Condition Set")')).toBeTruthy();
//Reload Page
await Promise.all([
page.reload(),
page.waitForLoadState('networkidle')
]);
await Promise.all([page.reload(), page.waitForLoadState('networkidle')]);
//Verify Main section reflects updated Name Property
await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Renamed Condition Set');
await expect
.soft(page.locator('.l-browse-bar__object-name'))
.toContainText('Renamed Condition Set');
// Verify Inspector properties
// Verify Inspector has updated Name property
@ -146,19 +159,30 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Renamed');
expect(page.locator('a:has-text("Renamed Condition Set")')).toBeTruthy();
});
test('condition set object can be deleted by Search Tree Actions menu on @localStorage', async ({ page }) => {
test('condition set object can be deleted by Search Tree Actions menu on @localStorage', async ({
page
}) => {
//Navigate to baseURL
await page.goto('./', { waitUntil: 'domcontentloaded' });
//Assertions on loaded Condition Set in main view. This is a stateful transition step after page.goto()
await expect(page.locator('a:has-text("Unnamed Condition Set Condition Set") >> nth=0')).toBeVisible();
await expect(
page.locator('a:has-text("Unnamed Condition Set Condition Set") >> nth=0')
).toBeVisible();
const numberOfConditionSetsToStart = await page.locator('a:has-text("Unnamed Condition Set Condition Set")').count();
const numberOfConditionSetsToStart = await page
.locator('a:has-text("Unnamed Condition Set Condition Set")')
.count();
// Search for Unnamed Condition Set
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Unnamed Condition Set');
await page
.locator('[aria-label="OpenMCT Search"] input[type="search"]')
.fill('Unnamed Condition Set');
// Click Search Result
await page.locator('[aria-label="OpenMCT Search"] >> text=Unnamed Condition Set').first().click();
await page
.locator('[aria-label="OpenMCT Search"] >> text=Unnamed Condition Set')
.first()
.click();
// Click hamburger button
await page.locator('[title="More options"]').click();
@ -167,7 +191,9 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
await page.locator('button:has-text("OK")').click();
//Expect Unnamed Condition Set to be removed in Main View
const numberOfConditionSetsAtEnd = await page.locator('a:has-text("Unnamed Condition Set Condition Set")').count();
const numberOfConditionSetsAtEnd = await page
.locator('a:has-text("Unnamed Condition Set Condition Set")')
.count();
expect(numberOfConditionSetsAtEnd).toEqual(numberOfConditionSetsToStart - 1);
@ -175,7 +201,6 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
//Domain Object is still available by direct URL after delete
await page.goto(conditionSetUrl, { waitUntil: 'networkidle' });
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Condition Set');
});
});
@ -188,7 +213,7 @@ test.describe('Basic Condition Set Use', () => {
// Create a new condition set
await createDomainObjectWithDefaults(page, {
type: 'Condition Set',
name: "Test Condition Set"
name: 'Test Condition Set'
});
// Change the object to edit mode
await page.locator('[title="Edit"]').click();
@ -207,15 +232,15 @@ test.describe('Basic Condition Set Use', () => {
await createDomainObjectWithDefaults(page, {
type: 'Sine Wave Generator',
name: "Alpha Sine Wave Generator"
name: 'Alpha Sine Wave Generator'
});
await createDomainObjectWithDefaults(page, {
type: 'Sine Wave Generator',
name: "Beta Sine Wave Generator"
name: 'Beta Sine Wave Generator'
});
const conditionSet1 = await createDomainObjectWithDefaults(page, {
type: 'Condition Set',
name: "Test Condition Set"
name: 'Test Condition Set'
});
// Change the object to edit mode
@ -228,8 +253,12 @@ test.describe('Basic Condition Set Use', () => {
const treePane = page.getByRole('tree', {
name: 'Main Tree'
});
const alphaGeneratorTreeItem = treePane.getByRole('treeitem', { name: "Alpha Sine Wave Generator"});
const betaGeneratorTreeItem = treePane.getByRole('treeitem', { name: "Beta Sine Wave Generator"});
const alphaGeneratorTreeItem = treePane.getByRole('treeitem', {
name: 'Alpha Sine Wave Generator'
});
const betaGeneratorTreeItem = treePane.getByRole('treeitem', {
name: 'Beta Sine Wave Generator'
});
const conditionCollection = page.locator('#conditionCollection');
await alphaGeneratorTreeItem.dragTo(conditionCollection);
@ -256,7 +285,7 @@ test.describe('Basic Condition Set Use', () => {
await page.click(`li[role='menuitem']:text("Sine Wave Generator")`);
await page.getByRole('spinbutton', { name: 'Loading Delay (ms)' }).fill('8000');
const nameInput = page.locator('form[name="mctForm"] .first input[type="text"]');
await nameInput.fill("Delayed Sine Wave Generator");
await nameInput.fill('Delayed Sine Wave Generator');
// Click OK button and wait for Navigate event
await Promise.all([
@ -269,7 +298,7 @@ test.describe('Basic Condition Set Use', () => {
// Create a new condition set
await createDomainObjectWithDefaults(page, {
type: 'Condition Set',
name: "Test Blank Output of Condition Set"
name: 'Test Blank Output of Condition Set'
});
// Change the object to edit mode
await page.locator('[title="Edit"]').click();
@ -286,34 +315,48 @@ test.describe('Basic Condition Set Use', () => {
const treePane = page.getByRole('tree', {
name: 'Main Tree'
});
const sineWaveGeneratorTreeItem = treePane.getByRole('treeitem', { name: "Delayed Sine Wave Generator"});
const sineWaveGeneratorTreeItem = treePane.getByRole('treeitem', {
name: 'Delayed Sine Wave Generator'
});
const conditionCollection = await page.locator('#conditionCollection');
await sineWaveGeneratorTreeItem.dragTo(conditionCollection);
const firstCriterionTelemetry = await page.locator('[aria-label="Criterion Telemetry Selection"] >> nth=0');
const firstCriterionTelemetry = await page.locator(
'[aria-label="Criterion Telemetry Selection"] >> nth=0'
);
firstCriterionTelemetry.selectOption({ label: 'Delayed Sine Wave Generator' });
const secondCriterionTelemetry = await page.locator('[aria-label="Criterion Telemetry Selection"] >> nth=1');
const secondCriterionTelemetry = await page.locator(
'[aria-label="Criterion Telemetry Selection"] >> nth=1'
);
secondCriterionTelemetry.selectOption({ label: 'Delayed Sine Wave Generator' });
const firstCriterionMetadata = await page.locator('[aria-label="Criterion Metadata Selection"] >> nth=0');
const firstCriterionMetadata = await page.locator(
'[aria-label="Criterion Metadata Selection"] >> nth=0'
);
firstCriterionMetadata.selectOption({ label: 'Sine' });
const secondCriterionMetadata = await page.locator('[aria-label="Criterion Metadata Selection"] >> nth=1');
const secondCriterionMetadata = await page.locator(
'[aria-label="Criterion Metadata Selection"] >> nth=1'
);
secondCriterionMetadata.selectOption({ label: 'Sine' });
const firstCriterionComparison = await page.locator('[aria-label="Criterion Comparison Selection"] >> nth=0');
const firstCriterionComparison = await page.locator(
'[aria-label="Criterion Comparison Selection"] >> nth=0'
);
firstCriterionComparison.selectOption({ label: 'is greater than or equal to' });
const secondCriterionComparison = await page.locator('[aria-label="Criterion Comparison Selection"] >> nth=1');
const secondCriterionComparison = await page.locator(
'[aria-label="Criterion Comparison Selection"] >> nth=1'
);
secondCriterionComparison.selectOption({ label: 'is less than' });
const firstCriterionInput = await page.locator('[aria-label="Criterion Input"] >> nth=0');
await firstCriterionInput.fill("0");
await firstCriterionInput.fill('0');
const secondCriterionInput = await page.locator('[aria-label="Criterion Input"] >> nth=1');
await secondCriterionInput.fill("0");
await secondCriterionInput.fill('0');
const saveButtonLocator = page.locator('button[title="Save"]');
await saveButtonLocator.click();

View File

@ -21,7 +21,12 @@
*****************************************************************************/
const { test, expect } = require('../../../../pluginFixtures');
const { createDomainObjectWithDefaults, setStartOffset, setFixedTimeMode, setRealTimeMode } = require('../../../../appActions');
const {
createDomainObjectWithDefaults,
setStartOffset,
setFixedTimeMode,
setRealTimeMode
} = require('../../../../appActions');
test.describe('Display Layout', () => {
/** @type {import('../../../../appActions').CreatedObjectInfo} */
@ -35,11 +40,13 @@ test.describe('Display Layout', () => {
type: 'Sine Wave Generator'
});
});
test('alpha-numeric widget telemetry value exactly matches latest telemetry value received in real time', async ({ page }) => {
test('alpha-numeric widget telemetry value exactly matches latest telemetry value received in real time', async ({
page
}) => {
// Create a Display Layout
await createDomainObjectWithDefaults(page, {
type: 'Display Layout',
name: "Test Display Layout"
name: 'Test Display Layout'
});
// Edit Display Layout
await page.locator('[title="Edit"]').click();
@ -63,17 +70,21 @@ test.describe('Display Layout', () => {
// from the Sine Wave Generator
const getTelemValuePromise = await subscribeToTelemetry(page, sineWaveObject.uuid);
const formattedTelemetryValue = getTelemValuePromise;
const displayLayoutValuePromise = await page.waitForSelector(`text="${formattedTelemetryValue}"`);
const displayLayoutValuePromise = await page.waitForSelector(
`text="${formattedTelemetryValue}"`
);
const displayLayoutValue = await displayLayoutValuePromise.textContent();
const trimmedDisplayValue = displayLayoutValue.trim();
expect(trimmedDisplayValue).toBe(formattedTelemetryValue);
});
test('alpha-numeric widget telemetry value exactly matches latest telemetry value received in fixed time', async ({ page }) => {
test('alpha-numeric widget telemetry value exactly matches latest telemetry value received in fixed time', async ({
page
}) => {
// Create a Display Layout
await createDomainObjectWithDefaults(page, {
type: 'Display Layout',
name: "Test Display Layout"
name: 'Test Display Layout'
});
// Edit Display Layout
await page.locator('[title="Edit"]').click();
@ -101,17 +112,21 @@ test.describe('Display Layout', () => {
// On getting data, check if the value found in the Display Layout is the most recent value
// from the Sine Wave Generator
const formattedTelemetryValue = getTelemValuePromise;
const displayLayoutValuePromise = await page.waitForSelector(`text="${formattedTelemetryValue}"`);
const displayLayoutValuePromise = await page.waitForSelector(
`text="${formattedTelemetryValue}"`
);
const displayLayoutValue = await displayLayoutValuePromise.textContent();
const trimmedDisplayValue = displayLayoutValue.trim();
expect(trimmedDisplayValue).toBe(formattedTelemetryValue);
});
test('items in a display layout can be removed with object tree context menu when viewing the display layout', async ({ page }) => {
test('items in a display layout can be removed with object tree context menu when viewing the display layout', async ({
page
}) => {
// Create a Display Layout
await createDomainObjectWithDefaults(page, {
type: 'Display Layout',
name: "Test Display Layout"
name: 'Test Display Layout'
});
// Edit Display Layout
await page.locator('[title="Edit"]').click();
@ -144,7 +159,9 @@ test.describe('Display Layout', () => {
expect(await page.locator('.l-layout .l-layout__frame').count()).toEqual(0);
});
test('items in a display layout can be removed with object tree context menu when viewing another item', async ({ page }) => {
test('items in a display layout can be removed with object tree context menu when viewing another item', async ({
page
}) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/3117'
@ -200,7 +217,9 @@ test.describe('Display Layout', () => {
* @returns {Promise<string>} the formatted sin telemetry value
*/
async function subscribeToTelemetry(page, objectIdentifier) {
const getTelemValuePromise = new Promise(resolve => page.exposeFunction('getTelemValue', resolve));
const getTelemValuePromise = new Promise((resolve) =>
page.exposeFunction('getTelemValue', resolve)
);
await page.evaluate(async (telemetryIdentifier) => {
const telemetryObject = await window.openmct.objects.get(telemetryIdentifier);

View File

@ -36,29 +36,45 @@ test.describe('The Fault Management Plugin using example faults', () => {
expect.soft(faultCount).toEqual(criticalityIconCount);
});
test('When selecting a fault, it has an "is-selected" class and it\'s information shows in the inspector @unstable', async ({ page }) => {
test('When selecting a fault, it has an "is-selected" class and it\'s information shows in the inspector @unstable', async ({
page
}) => {
await utils.selectFaultItem(page, 1);
await selectInspectorTab(page, 'Fault Management Configuration');
const selectedFaultName = await page.locator('.c-fault-mgmt__list.is-selected .c-fault-mgmt__list-faultname').textContent();
const inspectorFaultNameCount = await page.locator(`.c-inspector__properties >> :text("${selectedFaultName}")`).count();
const selectedFaultName = await page
.locator('.c-fault-mgmt__list.is-selected .c-fault-mgmt__list-faultname')
.textContent();
const inspectorFaultNameCount = await page
.locator(`.c-inspector__properties >> :text("${selectedFaultName}")`)
.count();
await expect.soft(page.locator('.c-faults-list-view-item-body > .c-fault-mgmt__list').first()).toHaveClass(/is-selected/);
await expect
.soft(page.locator('.c-faults-list-view-item-body > .c-fault-mgmt__list').first())
.toHaveClass(/is-selected/);
expect.soft(inspectorFaultNameCount).toEqual(1);
});
test('When selecting multiple faults, no specific fault information is shown in the inspector @unstable', async ({ page }) => {
test('When selecting multiple faults, no specific fault information is shown in the inspector @unstable', async ({
page
}) => {
await utils.selectFaultItem(page, 1);
await utils.selectFaultItem(page, 2);
const selectedRows = page.locator('.c-fault-mgmt__list.is-selected .c-fault-mgmt__list-faultname');
const selectedRows = page.locator(
'.c-fault-mgmt__list.is-selected .c-fault-mgmt__list-faultname'
);
expect.soft(await selectedRows.count()).toEqual(2);
await selectInspectorTab(page, 'Fault Management Configuration');
const firstSelectedFaultName = await selectedRows.nth(0).textContent();
const secondSelectedFaultName = await selectedRows.nth(1).textContent();
const firstNameInInspectorCount = await page.locator(`.c-inspector__properties >> :text("${firstSelectedFaultName}")`).count();
const secondNameInInspectorCount = await page.locator(`.c-inspector__properties >> :text("${secondSelectedFaultName}")`).count();
const firstNameInInspectorCount = await page
.locator(`.c-inspector__properties >> :text("${firstSelectedFaultName}")`)
.count();
const secondNameInInspectorCount = await page
.locator(`.c-inspector__properties >> :text("${secondSelectedFaultName}")`)
.count();
expect.soft(firstNameInInspectorCount).toEqual(0);
expect.soft(secondNameInInspectorCount).toEqual(0);
@ -208,7 +224,6 @@ test.describe('The Fault Management Plugin using example faults', () => {
expect.soft(sortedHighestSeverity).toEqual(highestSeverity);
expect.soft(sortedLowestSeverity).toEqual(lowestSeverity);
});
});
test.describe('The Fault Management Plugin without using example faults', () => {

View File

@ -39,7 +39,9 @@ test.describe('Flexible Layout', () => {
type: 'Clock'
});
});
test('panes have the appropriate draggable attribute while in Edit and Browse modes', async ({ page }) => {
test('panes have the appropriate draggable attribute while in Edit and Browse modes', async ({
page
}) => {
const treePane = page.getByRole('tree', {
name: 'Main Tree'
});
@ -62,7 +64,9 @@ test.describe('Flexible Layout', () => {
await sineWaveGeneratorTreeItem.dragTo(page.locator('.c-fl__container.is-empty').first());
await clockTreeItem.dragTo(page.locator('.c-fl__container.is-empty'));
// Check that panes can be dragged while Flexible Layout is in Edit mode
let dragWrapper = page.locator('.c-fl-container__frames-holder .c-fl-frame__drag-wrapper').first();
let dragWrapper = page
.locator('.c-fl-container__frames-holder .c-fl-frame__drag-wrapper')
.first();
await expect(dragWrapper).toHaveAttribute('draggable', 'true');
// Save Flexible Layout
await page.locator('button[title="Save"]').click();
@ -71,7 +75,9 @@ test.describe('Flexible Layout', () => {
dragWrapper = page.locator('.c-fl-container__frames-holder .c-fl-frame__drag-wrapper').first();
await expect(dragWrapper).toHaveAttribute('draggable', 'false');
});
test('items in a flexible layout can be removed with object tree context menu when viewing the flexible layout', async ({ page }) => {
test('items in a flexible layout can be removed with object tree context menu when viewing the flexible layout', async ({
page
}) => {
const treePane = page.getByRole('tree', {
name: 'Main Tree'
});
@ -105,7 +111,9 @@ test.describe('Flexible Layout', () => {
// Verify that the item has been removed from the layout
expect(await page.locator('.c-fl-container__frame').count()).toEqual(0);
});
test('items in a flexible layout can be removed with object tree context menu when viewing another item', async ({ page }) => {
test('items in a flexible layout can be removed with object tree context menu when viewing another item', async ({
page
}) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/3117'

View File

@ -21,8 +21,8 @@
*****************************************************************************/
/*
* This test suite is dedicated to testing the Gauge component.
*/
* This test suite is dedicated to testing the Gauge component.
*/
const { test, expect } = require('../../../../pluginFixtures');
const { createDomainObjectWithDefaults } = require('../../../../appActions');
@ -63,7 +63,13 @@ test.describe('Gauge', () => {
});
// Verify that the 'Replace telemetry source' modal appears and accept it
await expect.soft(page.locator('text=This action will replace the current telemetry source. Do you want to continue?')).toBeVisible();
await expect
.soft(
page.locator(
'text=This action will replace the current telemetry source. Do you want to continue?'
)
)
.toBeVisible();
await page.click('text=Ok');
// Navigate to the gauge and verify that the new SWG
@ -81,7 +87,13 @@ test.describe('Gauge', () => {
await page.locator('li[title="Remove this object from its containing object."]').click();
// Verify that the 'Remove object' confirmation modal appears and accept it
await expect.soft(page.locator('text=Warning! This action will remove this object. Are you sure you want to continue?')).toBeVisible();
await expect
.soft(
page.locator(
'text=Warning! This action will remove this object. Are you sure you want to continue?'
)
)
.toBeVisible();
await page.click('text=Ok');
// Verify that the elements pool shows no elements

View File

@ -44,7 +44,7 @@ test.describe('Example Imagery Object', () => {
// Verify that the created object is focused
await expect(page.locator('.l-browse-bar__object-name')).toContainText(exampleImagery.name);
await page.locator(backgroundImageSelector).hover({trial: true});
await page.locator(backgroundImageSelector).hover({ trial: true });
});
test('Can use Mouse Wheel to zoom in and out of latest image', async ({ page }) => {
@ -55,7 +55,10 @@ test.describe('Example Imagery Object', () => {
await mouseZoomOnImageAndAssert(page, -2);
});
test('Can adjust image brightness/contrast by dragging the sliders', async ({ page, browserName }) => {
test('Can adjust image brightness/contrast by dragging the sliders', async ({
page,
browserName
}) => {
// eslint-disable-next-line playwright/no-skipped-test
test.skip(browserName === 'firefox', 'This test needs to be updated to work with firefox');
// Open the image filter menu
@ -69,11 +72,11 @@ test.describe('Example Imagery Object', () => {
test('Can use alt+drag to move around image once zoomed in', async ({ page }) => {
const deltaYStep = 100; //equivalent to 1x zoom
await page.locator(backgroundImageSelector).hover({trial: true});
await page.locator(backgroundImageSelector).hover({ trial: true });
// zoom in
await page.mouse.wheel(0, deltaYStep * 2);
await page.locator(backgroundImageSelector).hover({trial: true});
await page.locator(backgroundImageSelector).hover({ trial: true });
const zoomedBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2;
const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
@ -91,42 +94,41 @@ test.describe('Example Imagery Object', () => {
expect(expectedAltText).toEqual(imageryHintsText);
// pan right
await Promise.all(panHotkey.map(x => page.keyboard.down(x)));
await Promise.all(panHotkey.map((x) => page.keyboard.down(x)));
await page.mouse.down();
await page.mouse.move(imageCenterX - 200, imageCenterY, 10);
await page.mouse.up();
await Promise.all(panHotkey.map(x => page.keyboard.up(x)));
await Promise.all(panHotkey.map((x) => page.keyboard.up(x)));
const afterRightPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
expect(zoomedBoundingBox.x).toBeGreaterThan(afterRightPanBoundingBox.x);
// pan left
await Promise.all(panHotkey.map(x => page.keyboard.down(x)));
await Promise.all(panHotkey.map((x) => page.keyboard.down(x)));
await page.mouse.down();
await page.mouse.move(imageCenterX, imageCenterY, 10);
await page.mouse.up();
await Promise.all(panHotkey.map(x => page.keyboard.up(x)));
await Promise.all(panHotkey.map((x) => page.keyboard.up(x)));
const afterLeftPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
expect(afterRightPanBoundingBox.x).toBeLessThan(afterLeftPanBoundingBox.x);
// pan up
await page.mouse.move(imageCenterX, imageCenterY);
await Promise.all(panHotkey.map(x => page.keyboard.down(x)));
await Promise.all(panHotkey.map((x) => page.keyboard.down(x)));
await page.mouse.down();
await page.mouse.move(imageCenterX, imageCenterY + 200, 10);
await page.mouse.up();
await Promise.all(panHotkey.map(x => page.keyboard.up(x)));
await Promise.all(panHotkey.map((x) => page.keyboard.up(x)));
const afterUpPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
expect(afterUpPanBoundingBox.y).toBeGreaterThan(afterLeftPanBoundingBox.y);
// pan down
await Promise.all(panHotkey.map(x => page.keyboard.down(x)));
await Promise.all(panHotkey.map((x) => page.keyboard.down(x)));
await page.mouse.down();
await page.mouse.move(imageCenterX, imageCenterY - 200, 10);
await page.mouse.up();
await Promise.all(panHotkey.map(x => page.keyboard.up(x)));
await Promise.all(panHotkey.map((x) => page.keyboard.up(x)));
const afterDownPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
expect(afterDownPanBoundingBox.y).toBeLessThan(afterUpPanBoundingBox.y);
});
test('Can use + - buttons to zoom on the image @unstable', async ({ page }) => {
@ -134,7 +136,7 @@ test.describe('Example Imagery Object', () => {
});
test('Can use the reset button to reset the image @unstable', async ({ page }, testInfo) => {
test.slow(testInfo.project === 'chrome-beta', "This test is slow in chrome-beta");
test.slow(testInfo.project === 'chrome-beta', 'This test is slow in chrome-beta');
// Get initial image dimensions
const initialBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
@ -196,18 +198,22 @@ test.describe('Example Imagery in Display Layout', () => {
// Click text=OK
await Promise.all([
page.waitForNavigation({waitUntil: 'networkidle'}),
page.waitForNavigation({ waitUntil: 'networkidle' }),
page.click('button:has-text("OK")'),
//Wait for Save Banner to appear
page.waitForSelector('.c-message-banner__message')
]);
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery');
await expect(page.locator('.l-browse-bar__object-name')).toContainText(
'Unnamed Example Imagery'
);
await page.goto(displayLayout.url);
});
test('View Large action pauses imagery when in realtime and returns to realtime', async ({ page }) => {
test('View Large action pauses imagery when in realtime and returns to realtime', async ({
page
}) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/3647'
@ -335,13 +341,15 @@ test.describe('Example Imagery in Flexible layout', () => {
// Click text=OK
await Promise.all([
page.waitForNavigation({waitUntil: 'networkidle'}),
page.waitForNavigation({ waitUntil: 'networkidle' }),
page.click('button:has-text("OK")'),
//Wait for Save Banner to appear
page.waitForSelector('.c-message-banner__message')
]);
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery');
await expect(page.locator('.l-browse-bar__object-name')).toContainText(
'Unnamed Example Imagery'
);
await page.goto(flexibleLayout.url);
});
@ -377,13 +385,15 @@ test.describe('Example Imagery in Tabs View', () => {
// Click text=OK
await Promise.all([
page.waitForNavigation({waitUntil: 'networkidle'}),
page.waitForNavigation({ waitUntil: 'networkidle' }),
page.click('button:has-text("OK")'),
//Wait for Save Banner to appear
page.waitForSelector('.c-message-banner__message')
]);
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery');
await expect(page.locator('.l-browse-bar__object-name')).toContainText(
'Unnamed Example Imagery'
);
await page.goto(tabsView.url);
});
@ -417,7 +427,9 @@ test.describe('Example Imagery in Time Strip', () => {
await page.locator('.c-imagery-tsv-container').hover();
// Get the img src of the hovered image thumbnail
const hoveredThumbnailImg = page.locator('.c-imagery-tsv div.c-imagery-tsv__image-wrapper:hover img');
const hoveredThumbnailImg = page.locator(
'.c-imagery-tsv div.c-imagery-tsv__image-wrapper:hover img'
);
const hoveredThumbnailImgSrc = await hoveredThumbnailImg.getAttribute('src');
// Verify that imagery timestrip view uses the thumbnailUrl as img src for thumbnails
@ -496,14 +508,19 @@ async function performImageryViewOperationsAndAssert(page) {
// The imagery view should be updated when new images come in
const imageCount = await page.locator('.c-imagery__thumb').count();
await expect.poll(async () => {
await expect
.poll(
async () => {
const newImageCount = await page.locator('.c-imagery__thumb').count();
return newImageCount;
}, {
message: "verify that old images are discarded",
},
{
message: 'verify that old images are discarded',
timeout: 7 * 1000
}).toBe(imageCount);
}
)
.toBe(imageCount);
// Verify selected image is still displayed
await expect(selectedImage).toBeVisible();
@ -532,7 +549,7 @@ async function dragBrightnessSliderAndAssertFilterValues(page) {
const brightnessMidX = brightnessBoundingBox.x + brightnessBoundingBox.width / 2;
const brightnessMidY = brightnessBoundingBox.y + brightnessBoundingBox.height / 2;
await page.locator(brightnessSlider).hover({trial: true});
await page.locator(brightnessSlider).hover({ trial: true });
await page.mouse.down();
await page.mouse.move(brightnessBoundingBox.x + brightnessBoundingBox.width, brightnessMidY);
await assertBackgroundImageBrightness(page, '500');
@ -553,7 +570,7 @@ async function dragContrastSliderAndAssertFilterValues(page) {
const contrastMidX = contrastBoundingBox.x + contrastBoundingBox.width / 2;
const contrastMidY = contrastBoundingBox.y + contrastBoundingBox.height / 2;
await page.locator(contrastSlider).hover({trial: true});
await page.locator(contrastSlider).hover({ trial: true });
await page.mouse.down();
await page.mouse.move(contrastBoundingBox.x + contrastBoundingBox.width, contrastMidY);
await assertBackgroundImageContrast(page, '500');
@ -586,24 +603,35 @@ async function assertBackgroundImageBrightness(page, expected) {
async function assertBackgroundImageUrlFromBackgroundCss(page) {
const backgroundImage = page.locator('.c-imagery__main-image__background-image');
let backgroundImageUrl = await backgroundImage.evaluate((el) => {
return window.getComputedStyle(el).getPropertyValue('background-image').match(/url\(([^)]+)\)/)[1];
return window
.getComputedStyle(el)
.getPropertyValue('background-image')
.match(/url\(([^)]+)\)/)[1];
});
let backgroundImageUrl1 = backgroundImageUrl.slice(1, -1); //forgive me, padre
console.log('backgroundImageUrl1 ' + backgroundImageUrl1);
let backgroundImageUrl2;
await expect.poll(async () => {
await expect
.poll(
async () => {
// Verify next image has updated
let backgroundImageUrlNext = await backgroundImage.evaluate((el) => {
return window.getComputedStyle(el).getPropertyValue('background-image').match(/url\(([^)]+)\)/)[1];
return window
.getComputedStyle(el)
.getPropertyValue('background-image')
.match(/url\(([^)]+)\)/)[1];
});
backgroundImageUrl2 = backgroundImageUrlNext.slice(1, -1); //forgive me, padre
return backgroundImageUrl2;
}, {
message: "verify next image has updated",
},
{
message: 'verify next image has updated',
timeout: 7 * 1000
}).not.toBe(backgroundImageUrl1);
}
)
.not.toBe(backgroundImageUrl1);
console.log('backgroundImageUrl2 ' + backgroundImageUrl2);
}
@ -618,39 +646,39 @@ async function panZoomAndAssertImageProperties(page) {
const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
// Pan right
await Promise.all(panHotkey.map(x => page.keyboard.down(x)));
await Promise.all(panHotkey.map((x) => page.keyboard.down(x)));
await page.mouse.down();
await page.mouse.move(imageCenterX - 200, imageCenterY, 10);
await page.mouse.up();
await Promise.all(panHotkey.map(x => page.keyboard.up(x)));
await Promise.all(panHotkey.map((x) => page.keyboard.up(x)));
const afterRightPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
expect(zoomedBoundingBox.x).toBeGreaterThan(afterRightPanBoundingBox.x);
// Pan left
await Promise.all(panHotkey.map(x => page.keyboard.down(x)));
await Promise.all(panHotkey.map((x) => page.keyboard.down(x)));
await page.mouse.down();
await page.mouse.move(imageCenterX, imageCenterY, 10);
await page.mouse.up();
await Promise.all(panHotkey.map(x => page.keyboard.up(x)));
await Promise.all(panHotkey.map((x) => page.keyboard.up(x)));
const afterLeftPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
expect(afterRightPanBoundingBox.x).toBeLessThan(afterLeftPanBoundingBox.x);
// Pan up
await page.mouse.move(imageCenterX, imageCenterY);
await Promise.all(panHotkey.map(x => page.keyboard.down(x)));
await Promise.all(panHotkey.map((x) => page.keyboard.down(x)));
await page.mouse.down();
await page.mouse.move(imageCenterX, imageCenterY + 200, 10);
await page.mouse.up();
await Promise.all(panHotkey.map(x => page.keyboard.up(x)));
await Promise.all(panHotkey.map((x) => page.keyboard.up(x)));
const afterUpPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
expect(afterUpPanBoundingBox.y).toBeGreaterThanOrEqual(afterLeftPanBoundingBox.y);
// Pan down
await Promise.all(panHotkey.map(x => page.keyboard.down(x)));
await Promise.all(panHotkey.map((x) => page.keyboard.down(x)));
await page.mouse.down();
await page.mouse.move(imageCenterX, imageCenterY - 200, 10);
await page.mouse.up();
await Promise.all(panHotkey.map(x => page.keyboard.up(x)));
await Promise.all(panHotkey.map((x) => page.keyboard.up(x)));
const afterDownPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
expect(afterDownPanBoundingBox.y).toBeLessThanOrEqual(afterUpPanBoundingBox.y);
}
@ -660,11 +688,11 @@ async function panZoomAndAssertImageProperties(page) {
* has successfully zoomed in or out.
* @param {import('@playwright/test').Page} page
* @param {number} [factor = 2] The zoom factor. Positive for zoom in, negative for zoom out.
*/
*/
async function mouseZoomOnImageAndAssert(page, factor = 2) {
// Zoom in
const originalImageDimensions = await page.locator(backgroundImageSelector).boundingBox();
await page.locator(backgroundImageSelector).hover({trial: true});
await page.locator(backgroundImageSelector).hover({ trial: true });
const deltaYStep = 100; // equivalent to 1x zoom
await page.mouse.wheel(0, deltaYStep * factor);
const zoomedBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
@ -675,7 +703,7 @@ async function mouseZoomOnImageAndAssert(page, factor = 2) {
await page.mouse.move(imageCenterX, imageCenterY);
// Wait for zoom animation to finish
await page.locator(backgroundImageSelector).hover({trial: true});
await page.locator(backgroundImageSelector).hover({ trial: true });
const imageMouseZoomed = await page.locator(backgroundImageSelector).boundingBox();
if (factor > 0) {
@ -742,10 +770,12 @@ async function assertBackgroundImageContrast(page, expected) {
*/
async function zoomIntoImageryByButton(page) {
// FIXME: There should only be one set of imagery buttons, but there are two?
const zoomInBtn = page.locator("[role='toolbar'][aria-label='Image controls'] .t-btn-zoom-in").nth(0);
const zoomInBtn = page
.locator("[role='toolbar'][aria-label='Image controls'] .t-btn-zoom-in")
.nth(0);
const backgroundImage = page.locator(backgroundImageSelector);
if (!(await zoomInBtn.isVisible())) {
await backgroundImage.hover({trial: true});
await backgroundImage.hover({ trial: true });
}
await zoomInBtn.click();
@ -759,10 +789,12 @@ async function zoomIntoImageryByButton(page) {
*/
async function zoomOutOfImageryByButton(page) {
// FIXME: There should only be one set of imagery buttons, but there are two?
const zoomOutBtn = page.locator("[role='toolbar'][aria-label='Image controls'] .t-btn-zoom-out").nth(0);
const zoomOutBtn = page
.locator("[role='toolbar'][aria-label='Image controls'] .t-btn-zoom-out")
.nth(0);
const backgroundImage = page.locator(backgroundImageSelector);
if (!(await zoomOutBtn.isVisible())) {
await backgroundImage.hover({trial: true});
await backgroundImage.hover({ trial: true });
}
await zoomOutBtn.click();
@ -776,10 +808,12 @@ async function zoomOutOfImageryByButton(page) {
*/
async function resetImageryPanAndZoom(page) {
// FIXME: There should only be one set of imagery buttons, but there are two?
const panZoomResetBtn = page.locator("[role='toolbar'][aria-label='Image controls'] .t-btn-zoom-reset").nth(0);
const panZoomResetBtn = page
.locator("[role='toolbar'][aria-label='Image controls'] .t-btn-zoom-reset")
.nth(0);
const backgroundImage = page.locator(backgroundImageSelector);
if (!(await panZoomResetBtn.isVisible())) {
await backgroundImage.hover({trial: true});
await backgroundImage.hover({ trial: true });
}
await panZoomResetBtn.click();

View File

@ -29,22 +29,31 @@ This test suite is dedicated to tests which verify the basic operations surround
const { test, expect } = require('../../../../baseFixtures');
test.describe('ExportAsJSON', () => {
test.fixme('Create a basic object and verify that it can be exported as JSON from Tree', async ({ page }) => {
test.fixme(
'Create a basic object and verify that it can be exported as JSON from Tree',
async ({ page }) => {
//Create domain object
//Save Domain Object
//Verify that the newly created domain object can be exported as JSON from the Tree
});
test.fixme('Create a basic object and verify that it can be exported as JSON from 3 dot menu', async ({ page }) => {
}
);
test.fixme(
'Create a basic object and verify that it can be exported as JSON from 3 dot menu',
async ({ page }) => {
//Create domain object
//Save Domain Object
//Verify that the newly created domain object can be exported as JSON from the 3 dot menu
});
}
);
test.fixme('Verify that a nested Object can be exported as JSON', async ({ page }) => {
// Create 2 objects with hierarchy
// Export as JSON
// Verify Hiearchy
});
test.fixme('Verify that the ExportAsJSON dropdown does not appear for the item X', async ({ page }) => {
test.fixme(
'Verify that the ExportAsJSON dropdown does not appear for the item X',
async ({ page }) => {
// Other than non-persistible objects
});
}
);
});

View File

@ -33,16 +33,22 @@ test.describe('ExportAsJSON', () => {
//Verify that an testdata JSON file can be imported from Tree
//Verify correctness of imported domain object
});
test.fixme('Verify that domain object can be importAsJSON from 3 dot menu on folder', async ({ page }) => {
test.fixme(
'Verify that domain object can be importAsJSON from 3 dot menu on folder',
async ({ page }) => {
//Verify that an testdata JSON file can be imported from 3 dot menu on folder domain object
//Verify correctness of imported domain object
});
}
);
test.fixme('Verify that a nested Objects can be importAsJSON', async ({ page }) => {
// Testdata with hierarchy
// ImportAsJSON on Tree
// Verify Hierarchy
});
test.fixme('Verify that the ImportAsJSON dropdown does not appear for the item X', async ({ page }) => {
test.fixme(
'Verify that the ImportAsJSON dropdown does not appear for the item X',
async ({ page }) => {
// Other than non-persistible objects
});
}
);
});

View File

@ -21,7 +21,13 @@
*****************************************************************************/
const { test, expect } = require('../../../../pluginFixtures');
const { createDomainObjectWithDefaults, setStartOffset, setFixedTimeMode, setRealTimeMode, selectInspectorTab } = require('../../../../appActions');
const {
createDomainObjectWithDefaults,
setStartOffset,
setFixedTimeMode,
setRealTimeMode,
selectInspectorTab
} = require('../../../../appActions');
test.describe('Testing LAD table configuration', () => {
test.beforeEach(async ({ page }) => {
@ -30,13 +36,13 @@ test.describe('Testing LAD table configuration', () => {
// Create LAD table
const ladTable = await createDomainObjectWithDefaults(page, {
type: 'LAD Table',
name: "Test LAD Table"
name: 'Test LAD Table'
});
// Create Sine Wave Generator
await createDomainObjectWithDefaults(page, {
type: 'Sine Wave Generator',
name: "Test Sine Wave Generator",
name: 'Test Sine Wave Generator',
parent: ladTable.uuid
});
@ -117,7 +123,9 @@ test.describe('Testing LAD table configuration', () => {
await expect(page.getByRole('cell', { name: 'Type' })).toBeVisible();
});
test('LAD Tables don\'t allow selection of rows but does show context click menus', async ({ page }) => {
test("LAD Tables don't allow selection of rows but does show context click menus", async ({
page
}) => {
const cell = await page.locator('.js-first-data');
const userSelectable = await cell.evaluate((el) => {
return window.getComputedStyle(el).getPropertyValue('user-select');
@ -145,14 +153,16 @@ test.describe('Testing LAD table @unstable', () => {
// Create Sine Wave Generator
sineWaveObject = await createDomainObjectWithDefaults(page, {
type: 'Sine Wave Generator',
name: "Test Sine Wave Generator"
name: 'Test Sine Wave Generator'
});
});
test('telemetry value exactly matches latest telemetry value received in real time', async ({ page }) => {
test('telemetry value exactly matches latest telemetry value received in real time', async ({
page
}) => {
// Create LAD table
await createDomainObjectWithDefaults(page, {
type: 'LAD Table',
name: "Test LAD Table"
name: 'Test LAD Table'
});
// Edit LAD table
await page.locator('[title="Edit"]').click();
@ -174,11 +184,13 @@ test.describe('Testing LAD table @unstable', () => {
expect(ladTableValue).toBe(subscribeTelemValue);
});
test('telemetry value exactly matches latest telemetry value received in fixed time', async ({ page }) => {
test('telemetry value exactly matches latest telemetry value received in fixed time', async ({
page
}) => {
// Create LAD table
await createDomainObjectWithDefaults(page, {
type: 'LAD Table',
name: "Test LAD Table"
name: 'Test LAD Table'
});
// Edit LAD table
await page.locator('[title="Edit"]').click();
@ -216,7 +228,9 @@ test.describe('Testing LAD table @unstable', () => {
* @returns {Promise<string>} the formatted sin telemetry value
*/
async function subscribeToTelemetry(page, objectIdentifier) {
const getTelemValuePromise = new Promise(resolve => page.exposeFunction('getTelemValue', resolve));
const getTelemValuePromise = new Promise((resolve) =>
page.exposeFunction('getTelemValue', resolve)
);
await page.evaluate(async (telemetryIdentifier) => {
const telemetryObject = await window.openmct.objects.get(telemetryIdentifier);

View File

@ -50,22 +50,31 @@ test.describe('Default Notebook', () => {
// `JSON.parse(localStorage.getItem('notebook-storage'));`
// 1. - Clear default notebook:
// `localStorage.setItem('notebook-storage', null);`
test.fixme('A newly created Notebook is automatically set as the default notebook if no other notebooks exist', async ({ page }) => {
test.fixme(
'A newly created Notebook is automatically set as the default notebook if no other notebooks exist',
async ({ page }) => {
//Create new notebook
//Verify Default Notebook Characteristics
});
test.fixme('A newly created Notebook is automatically set as the default notebook if at least one other notebook exists', async ({ page }) => {
}
);
test.fixme(
'A newly created Notebook is automatically set as the default notebook if at least one other notebook exists',
async ({ page }) => {
//Create new notebook A
//Create second notebook B
//Verify Non-Default Notebook A Characteristics
//Verify Default Notebook B Characteristics
});
test.fixme('If a default notebook is deleted, the second most recent notebook becomes the default', async ({ page }) => {
}
);
test.fixme(
'If a default notebook is deleted, the second most recent notebook becomes the default',
async ({ page }) => {
//Create new notebook A
//Create second notebook B
//Delete Notebook B
//Verify Default Notebook A Characteristics
});
}
);
});
test.describe('Notebook section tests', () => {
@ -79,7 +88,9 @@ test.describe('Notebook section tests', () => {
type: NOTEBOOK_NAME
});
});
test('Default and new sections are automatically named Unnamed Section with Unnamed Page', async ({ page }) => {
test('Default and new sections are automatically named Unnamed Section with Unnamed Page', async ({
page
}) => {
const notebookSectionNames = page.locator('.c-notebook__sections .c-list__item__name');
const notebookPageNames = page.locator('.c-notebook__pages .c-list__item__name');
await expect(notebookSectionNames).toBeHidden();
@ -104,7 +115,6 @@ test.describe('Notebook section tests', () => {
const newPageName = await notebookPageNames.innerText();
await expect(notebookPageNames).toBeVisible();
expect(newPageName).toBe('Unnamed Page');
});
test.fixme('Section selection operations and associated behavior', async ({ page }) => {
//Create new notebook A
@ -152,7 +162,9 @@ test.describe('Notebook page tests', () => {
});
//Test will need to be implemented after a refactor in #5713
// eslint-disable-next-line playwright/no-skipped-test
test.skip('Delete page popup is removed properly on clicking dropdown again', async ({ page }) => {
test.skip('Delete page popup is removed properly on clicking dropdown again', async ({
page
}) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/5713'
@ -252,7 +264,9 @@ test.describe('Notebook entry tests', () => {
let notebookObject;
test.beforeEach(async ({ page }) => {
// eslint-disable-next-line no-undef
await page.addInitScript({ path: path.join(__dirname, '../../../../helper/', 'addInitNotebookWithUrls.js') });
await page.addInitScript({
path: path.join(__dirname, '../../../../helper/', 'addInitNotebookWithUrls.js')
});
await page.goto('./', { waitUntil: 'domcontentloaded' });
notebookObject = await createDomainObjectWithDefaults(page, {
@ -268,7 +282,9 @@ test.describe('Notebook entry tests', () => {
await expect(page.locator('[aria-label="Notebook Entry Input"]')).toBeVisible();
await expect(page.locator('[aria-label="Notebook Entry"]')).toHaveClass(/is-selected/);
});
test('When an object is dropped into a notebook, a new entry is created and it should be focused @unstable', async ({ page }) => {
test('When an object is dropped into a notebook, a new entry is created and it should be focused @unstable', async ({
page
}) => {
// Create Overlay Plot
const overlayPlot = await createDomainObjectWithDefaults(page, {
type: 'Overlay Plot'
@ -280,7 +296,9 @@ test.describe('Notebook entry tests', () => {
// Reveal the notebook in the tree
await page.getByTitle('Show selected item in tree').click();
await page.getByRole('treeitem', { name: overlayPlot.name }).dragTo(page.locator('.c-notebook__drag-area'));
await page
.getByRole('treeitem', { name: overlayPlot.name })
.dragTo(page.locator('.c-notebook__drag-area'));
const embed = page.locator('.c-ne__embed__link');
const embedName = await embed.innerText();
@ -288,7 +306,9 @@ test.describe('Notebook entry tests', () => {
await expect(embed).toHaveClass(/icon-plot-overlay/);
expect(embedName).toBe(overlayPlot.name);
});
test('When an object is dropped into a notebooks existing entry, it should be focused @unstable', async ({ page }) => {
test('When an object is dropped into a notebooks existing entry, it should be focused @unstable', async ({
page
}) => {
// Create Overlay Plot
const overlayPlot = await createDomainObjectWithDefaults(page, {
type: 'Overlay Plot'
@ -301,7 +321,9 @@ test.describe('Notebook entry tests', () => {
await page.getByTitle('Show selected item in tree').click();
await nbUtils.enterTextEntry(page, 'Entry to drop into');
await page.getByRole('treeitem', { name: overlayPlot.name }).dragTo(page.locator('text=Entry to drop into'));
await page
.getByRole('treeitem', { name: overlayPlot.name })
.dragTo(page.locator('text=Entry to drop into'));
const existingEntry = page.locator('.c-ne__content', {
has: page.locator('text="Entry to drop into"')
@ -332,7 +354,9 @@ test.describe('Notebook entry tests', () => {
await expect(page.locator('text="Another First Entry"')).toBeVisible();
await expect(page.locator('text="Second Entry"')).toBeVisible();
});
test('when a valid link is entered into a notebook entry, it becomes clickable when viewing', async ({ page }) => {
test('when a valid link is entered into a notebook entry, it becomes clickable when viewing', async ({
page
}) => {
const TEST_LINK = 'http://www.google.com';
// Navigate to the notebook object
@ -357,7 +381,9 @@ test.describe('Notebook entry tests', () => {
expect(await validLink.count()).toBe(1);
});
test('when an invalid link is entered into a notebook entry, it does not become clickable when viewing', async ({ page }) => {
test('when an invalid link is entered into a notebook entry, it does not become clickable when viewing', async ({
page
}) => {
const TEST_LINK = 'www.google.com';
// Navigate to the notebook object
@ -372,7 +398,9 @@ test.describe('Notebook entry tests', () => {
expect(await invalidLink.count()).toBe(0);
});
test('when a link is entered, but it is not in the whitelisted urls, it does not become clickable when viewing', async ({ page }) => {
test('when a link is entered, but it is not in the whitelisted urls, it does not become clickable when viewing', async ({
page
}) => {
const TEST_LINK = 'http://www.bing.com';
// Navigate to the notebook object
@ -387,7 +415,9 @@ test.describe('Notebook entry tests', () => {
expect(await invalidLink.count()).toBe(0);
});
test('when a valid link with a subdomain and a valid domain in the whitelisted urls is entered into a notebook entry, it becomes clickable when viewing', async ({ page }) => {
test('when a valid link with a subdomain and a valid domain in the whitelisted urls is entered into a notebook entry, it becomes clickable when viewing', async ({
page
}) => {
const INVALID_TEST_LINK = 'http://bing.google.com';
// Navigate to the notebook object
@ -402,7 +432,9 @@ test.describe('Notebook entry tests', () => {
expect(await validLink.count()).toBe(1);
});
test('when a valid secure link is entered into a notebook entry, it becomes clickable when viewing', async ({ page }) => {
test('when a valid secure link is entered into a notebook entry, it becomes clickable when viewing', async ({
page
}) => {
const TEST_LINK = 'https://www.google.com';
// Navigate to the notebook object
@ -427,7 +459,9 @@ test.describe('Notebook entry tests', () => {
expect(await validLink.count()).toBe(1);
});
test('when a nefarious link is entered into a notebook entry, it is sanitized when viewing', async ({ page }) => {
test('when a nefarious link is entered into a notebook entry, it is sanitized when viewing', async ({
page
}) => {
const TEST_LINK = 'http://www.google.com?bad=';
const TEST_LINK_BAD = `http://www.google.com?bad=<script>alert('gimme your cookies')</script>`;
@ -437,7 +471,10 @@ test.describe('Notebook entry tests', () => {
// Reveal the notebook in the tree
await page.getByTitle('Show selected item in tree').click();
await nbUtils.enterTextEntry(page, `This should be a link, BUT not a bad link: ${TEST_LINK_BAD} is it?`);
await nbUtils.enterTextEntry(
page,
`This should be a link, BUT not a bad link: ${TEST_LINK_BAD} is it?`
);
const sanitizedLink = page.locator(`a[href="${TEST_LINK}"]`);
const unsanitizedLink = page.locator(`a[href="${TEST_LINK_BAD}"]`);

View File

@ -29,14 +29,19 @@ const { test, expect } = require('../../../../pluginFixtures');
// const nbUtils = require('../../../../helper/notebookUtils');
test.describe('Snapshot Menu tests', () => {
test.fixme('When no default notebook is selected, Snapshot Menu dropdown should only have a single option', async ({ page }) => {
test.fixme(
'When no default notebook is selected, Snapshot Menu dropdown should only have a single option',
async ({ page }) => {
// There should be no default notebook
// Clear default notebook if exists using `localStorage.setItem('notebook-storage', null);`
// refresh page
// Click on 'Notebook Snaphot Menu'
// 'save to Notebook Snapshots' should be only option there
});
test.fixme('When default notebook is updated selected, Snapshot Menu dropdown should list it as the newest option', async ({ page }) => {
}
);
test.fixme(
'When default notebook is updated selected, Snapshot Menu dropdown should list it as the newest option',
async ({ page }) => {
// Create 2a notebooks
// Set Notebook A as Default
// Open Snapshot Menu and note that Notebook A is listed
@ -44,7 +49,8 @@ test.describe('Snapshot Menu tests', () => {
// Set Default Notebook to Notebook B
// Open Snapshot Notebook and note that Notebook B is listed
// Select Default Notebook Option and verify that Snapshot is added to Notebook B
});
}
);
test.fixme('Can add Snapshots via Snapshot Menu and details are correct', async ({ page }) => {
//Note this should be a visual test, too
// Create Telemetry object
@ -82,12 +88,19 @@ test.describe('Snapshot Container tests', () => {
await page.getByRole('button', { name: ' Snapshot ' }).click();
await page.getByRole('menuitem', { name: ' Save to Notebook Snapshots' }).click();
await page.getByRole('button', { name: 'Show' }).click();
});
test.fixme('5 Snapshots can be added to a container', async ({ page }) => {});
test.fixme('5 Snapshots can be added to a container and Deleted with Delete All action', async ({ page }) => {});
test.fixme('A snapshot can be Deleted from Container with 3 dot action menu', async ({ page }) => {});
test.fixme('A snapshot can be Viewed, Annotated, display deleted, and saved from Container with 3 dot action menu', async ({ page }) => {
test.fixme(
'5 Snapshots can be added to a container and Deleted with Delete All action',
async ({ page }) => {}
);
test.fixme(
'A snapshot can be Deleted from Container with 3 dot action menu',
async ({ page }) => {}
);
test.fixme(
'A snapshot can be Viewed, Annotated, display deleted, and saved from Container with 3 dot action menu',
async ({ page }) => {
await page.locator('.c-snapshot.c-ne__embed').first().getByTitle('More options').click();
await page.getByRole('menuitem', { name: ' View Snapshot' }).click();
await expect(page.locator('.c-overlay__outer')).toBeVisible();
@ -98,16 +111,25 @@ test.describe('Snapshot Container tests', () => {
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('button', { name: 'Done' }).click();
//await expect(await page.locator)
});
}
);
test('A snapshot can be Quick Viewed from Container with 3 dot action menu', async ({ page }) => {
await page.locator('.c-snapshot.c-ne__embed').first().getByTitle('More options').click();
await page.getByRole('menuitem', { name: 'Quick View' }).click();
await expect(page.locator('.c-overlay__outer')).toBeVisible();
});
test.fixme('A snapshot can be Navigated To from Container with 3 dot action menu', async ({ page }) => {});
test.fixme('A snapshot can be Navigated To Item in Time from Container with 3 dot action menu', async ({ page }) => {});
test.fixme(
'A snapshot can be Navigated To from Container with 3 dot action menu',
async ({ page }) => {}
);
test.fixme(
'A snapshot can be Navigated To Item in Time from Container with 3 dot action menu',
async ({ page }) => {}
);
test.fixme('A snapshot Container can be open and closed', async ({ page }) => {});
test.fixme('Can add object to Snapshot container and pull into notebook and create a new entry', async ({ page }) => {
test.fixme(
'Can add object to Snapshot container and pull into notebook and create a new entry',
async ({ page }) => {
//Create Notebook
//Create Telemetry Object
//From Telemetry Object, use 'save to Notebook Snapshots'
@ -116,8 +138,11 @@ test.describe('Snapshot Container tests', () => {
//Drag and Drop onto droppable area for new entry
//New Entry created with given snapshot added
//Snapshot removed from container?
});
test.fixme('Can add object to Snapshot container and pull into notebook and existing entry', async ({ page }) => {
}
);
test.fixme(
'Can add object to Snapshot container and pull into notebook and existing entry',
async ({ page }) => {
//Create Notebook
//Create Telemetry Object
//From Telemetry Object, use 'save to Notebook Snapshots'
@ -126,9 +151,13 @@ test.describe('Snapshot Container tests', () => {
//Drag and Drop into exiting entry
//Existing Entry updated with given snapshot
//Snapshot removed from container?
});
test.fixme('Verify Embedded options for PNG, JPG, and Annotate work correctly', async ({ page }) => {
}
);
test.fixme(
'Verify Embedded options for PNG, JPG, and Annotate work correctly',
async ({ page }) => {
//Add snapshot to container
//Verify PNG, JPG, and Annotate buttons work correctly
});
}
);
});

View File

@ -36,8 +36,8 @@ test.describe('Notebook Tests with CouchDB @couchdb', () => {
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Create Notebook
testNotebook = await createDomainObjectWithDefaults(page, {type: 'Notebook' });
await page.goto(testNotebook.url, { waitUntil: 'networkidle'});
testNotebook = await createDomainObjectWithDefaults(page, { type: 'Notebook' });
await page.goto(testNotebook.url, { waitUntil: 'networkidle' });
});
test('Inspect Notebook Entry Network Requests', async ({ page }) => {
@ -69,7 +69,9 @@ test.describe('Notebook Tests with CouchDB @couchdb', () => {
// Assert on request object
expect(notebookUrlRequest.postDataJSON().metadata.name).toBe(testNotebook.name);
expect(notebookUrlRequest.postDataJSON().model.persisted).toBeGreaterThanOrEqual(notebookUrlRequest.postDataJSON().model.modified);
expect(notebookUrlRequest.postDataJSON().model.persisted).toBeGreaterThanOrEqual(
notebookUrlRequest.postDataJSON().model.modified
);
expect(allDocsRequest.postDataJSON().keys).toContain(testNotebook.uuid);
// Add an entry
@ -186,9 +188,11 @@ test.describe('Notebook Tests with CouchDB @couchdb', () => {
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
//Partial match for "Science" should only return Science
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Sc');
await expect(page.locator('[aria-label="Search Result"]').first()).toContainText("Science");
await expect(page.locator('[aria-label="Search Result"]').first()).not.toContainText("Driving");
await expect(page.locator('[aria-label="Search Result"]').first()).not.toContainText("Drilling");
await expect(page.locator('[aria-label="Search Result"]').first()).toContainText('Science');
await expect(page.locator('[aria-label="Search Result"]').first()).not.toContainText('Driving');
await expect(page.locator('[aria-label="Search Result"]').first()).not.toContainText(
'Drilling'
);
//Searching for a tag which does not exist should return an empty result
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
@ -200,8 +204,8 @@ test.describe('Notebook Tests with CouchDB @couchdb', () => {
// Try to reduce indeterminism of browser requests by only returning fetch requests.
// Filter out preflight CORS, fetching stylesheets, page icons, etc. that can occur during tests
function filterNonFetchRequests(requests) {
return requests.filter(request => {
return (request.resourceType() === 'fetch');
return requests.filter((request) => {
return request.resourceType() === 'fetch';
});
}
@ -236,7 +240,9 @@ async function removeTagAndAwaitNetwork(page, tagName) {
await Promise.all([
page.locator(`[aria-label="Remove tag ${tagName}"]`).click(),
//With this pattern, we're awaiting the response but asserting on the request payload.
page.waitForResponse(resp => resp.request().postData().includes(`"_deleted":true`) && resp.status() === 201)
page.waitForResponse(
(resp) => resp.request().postData().includes(`"_deleted":true`) && resp.status() === 201
)
]);
await expect(page.locator(`[aria-label="Tag"]:has-text("${tagName}")`)).toBeHidden();
await page.waitForLoadState('networkidle');

View File

@ -21,7 +21,10 @@
*****************************************************************************/
/* global __dirname */
const { test, expect, streamToString } = require('../../../../pluginFixtures');
const { openObjectTreeContextMenu, createDomainObjectWithDefaults } = require('../../../../appActions');
const {
openObjectTreeContextMenu,
createDomainObjectWithDefaults
} = require('../../../../appActions');
const path = require('path');
const nbUtils = require('../../../../helper/notebookUtils');
@ -65,13 +68,11 @@ test.describe('Restricted Notebook', () => {
});
test('Can be locked if at least one page has one entry @addInit', async ({ page }) => {
await nbUtils.enterTextEntry(page, TEST_TEXT);
const commitButton = page.locator('button:has-text("Commit Entries")');
expect(await commitButton.count()).toEqual(1);
});
});
test.describe('Restricted Notebook with at least one entry and with the page locked @addInit', () => {
@ -85,11 +86,15 @@ test.describe('Restricted Notebook with at least one entry and with the page loc
await page.locator('button.c-notebook__toggle-nav-button').click();
});
test('Locked page should now be in a locked state @addInit @unstable', async ({ page }, testInfo) => {
test('Locked page should now be in a locked state @addInit @unstable', async ({
page
}, testInfo) => {
// eslint-disable-next-line playwright/no-skipped-test
test.skip(testInfo.project === 'chrome-beta', "Test is unreliable on chrome-beta");
test.skip(testInfo.project === 'chrome-beta', 'Test is unreliable on chrome-beta');
// main lock message on page
const lockMessage = page.locator('text=This page has been committed and cannot be modified or removed');
const lockMessage = page.locator(
'text=This page has been committed and cannot be modified or removed'
);
expect.soft(await lockMessage.count()).toEqual(1);
// lock icon on page in sidebar
@ -103,7 +108,9 @@ test.describe('Restricted Notebook with at least one entry and with the page loc
await expect(menuOptions).not.toContainText('Remove');
});
test('Can still: add page, rename, add entry, delete unlocked pages @addInit', async ({ page }) => {
test('Can still: add page, rename, add entry, delete unlocked pages @addInit', async ({
page
}) => {
// Add a new page to the section
await page.getByRole('button', { name: 'Add Page' }).click();
// Focus the new page by clicking it
@ -138,7 +145,6 @@ test.describe('Restricted Notebook with at least one entry and with the page loc
});
test.describe('Restricted Notebook with a page locked and with an embed @addInit', () => {
test.beforeEach(async ({ page }) => {
const notebook = await startAndAddRestrictedNotebookObject(page);
await nbUtils.dragAndDropEmbed(page, notebook);
@ -160,7 +166,6 @@ test.describe('Restricted Notebook with a page locked and with an embed @addInit
const embedMenu = page.locator('body >> .c-menu');
await expect(embedMenu).not.toContainText('Remove This Embed');
});
});
test.describe('can export restricted notebook as text', () => {
@ -181,7 +186,6 @@ test.describe('can export restricted notebook as text', () => {
const readStream = await download.createReadStream();
const exportedText = await streamToString(readStream);
expect(exportedText).toContain('Foo bar entry');
});
test.fixme('can export multiple notebook entries as text ', async ({ page }) => {});
@ -194,7 +198,9 @@ test.describe('can export restricted notebook as text', () => {
* @param {import('@playwright/test').Page} page
*/
async function startAndAddRestrictedNotebookObject(page) {
await page.addInitScript({ path: path.join(__dirname, '../../../../helper/', 'addInitRestrictedNotebook.js') });
await page.addInitScript({
path: path.join(__dirname, '../../../../helper/', 'addInitRestrictedNotebook.js')
});
await page.goto('./', { waitUntil: 'domcontentloaded' });
return createDomainObjectWithDefaults(page, { type: CUSTOM_NAME });

View File

@ -91,22 +91,22 @@ test.describe('Tagging in Notebooks @addInit', () => {
await page.locator('[placeholder="Type to select tag"]').click();
await expect(page.locator('[aria-label="Autocomplete Options"]')).toContainText("Science");
await expect(page.locator('[aria-label="Autocomplete Options"]')).toContainText("Drilling");
await expect(page.locator('[aria-label="Autocomplete Options"]')).toContainText("Driving");
await expect(page.locator('[aria-label="Autocomplete Options"]')).toContainText('Science');
await expect(page.locator('[aria-label="Autocomplete Options"]')).toContainText('Drilling');
await expect(page.locator('[aria-label="Autocomplete Options"]')).toContainText('Driving');
});
test('Can add tags', async ({ page }) => {
await createNotebookEntryAndTags(page);
await expect(page.locator('[aria-label="Notebook Entry"]')).toContainText("Science");
await expect(page.locator('[aria-label="Notebook Entry"]')).toContainText("Driving");
await expect(page.locator('[aria-label="Notebook Entry"]')).toContainText('Science');
await expect(page.locator('[aria-label="Notebook Entry"]')).toContainText('Driving');
await page.locator('button:has-text("Add Tag")').click();
await page.locator('[placeholder="Type to select tag"]').click();
await expect(page.locator('[aria-label="Autocomplete Options"]')).not.toContainText("Science");
await expect(page.locator('[aria-label="Autocomplete Options"]')).not.toContainText("Driving");
await expect(page.locator('[aria-label="Autocomplete Options"]')).toContainText("Drilling");
await expect(page.locator('[aria-label="Autocomplete Options"]')).not.toContainText('Science');
await expect(page.locator('[aria-label="Autocomplete Options"]')).not.toContainText('Driving');
await expect(page.locator('[aria-label="Autocomplete Options"]')).toContainText('Drilling');
});
test('Can add tags with blank entry', async ({ page }) => {
await createDomainObjectWithDefaults(page, { type: 'Notebook' });
@ -121,7 +121,7 @@ test.describe('Tagging in Notebooks @addInit', () => {
// Select the "Driving" tag
await page.locator('[aria-label="Autocomplete Options"] >> text=Driving').click();
await expect(page.locator('[aria-label="Notebook Entry"]')).toContainText("Driving");
await expect(page.locator('[aria-label="Notebook Entry"]')).toContainText('Driving');
});
test('Can cancel adding tags', async ({ page }) => {
await createNotebookAndEntry(page);
@ -148,13 +148,13 @@ test.describe('Tagging in Notebooks @addInit', () => {
await createNotebookEntryAndTags(page);
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('sc');
await expect(page.locator('[aria-label="Search Result"]')).toContainText("Science");
await expect(page.locator('[aria-label="Search Result"]')).not.toContainText("Driving");
await expect(page.locator('[aria-label="Search Result"]')).toContainText('Science');
await expect(page.locator('[aria-label="Search Result"]')).not.toContainText('Driving');
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Sc');
await expect(page.locator('[aria-label="Search Result"]')).toContainText("Science");
await expect(page.locator('[aria-label="Search Result"]')).not.toContainText("Driving");
await expect(page.locator('[aria-label="Search Result"]')).toContainText('Science');
await expect(page.locator('[aria-label="Search Result"]')).not.toContainText('Driving');
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Xq');
@ -169,7 +169,7 @@ test.describe('Tagging in Notebooks @addInit', () => {
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Sc');
await expect(page.locator('[aria-label="Search Result"]')).toContainText("Science");
await expect(page.locator('[aria-label="Search Result"]')).toContainText('Science');
await page.getByText('Entry 0').click();
await expect(page.locator('.js-preview-window')).toBeVisible();
});
@ -180,11 +180,11 @@ test.describe('Tagging in Notebooks @addInit', () => {
await page.hover('[aria-label="Tag"]:has-text("Driving")');
await page.locator('[aria-label="Remove tag Driving"]').click();
await expect(page.locator('[aria-label="Tags Inspector"]')).toContainText("Science");
await expect(page.locator('[aria-label="Tags Inspector"]')).not.toContainText("Driving");
await expect(page.locator('[aria-label="Tags Inspector"]')).toContainText('Science');
await expect(page.locator('[aria-label="Tags Inspector"]')).not.toContainText('Driving');
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('sc');
await expect(page.locator('[aria-label="Search Result"]')).not.toContainText("Driving");
await expect(page.locator('[aria-label="Search Result"]')).not.toContainText('Driving');
});
test('Can delete entries without tags', async ({ page }) => {
@ -203,9 +203,13 @@ test.describe('Tagging in Notebooks @addInit', () => {
await page.hover('[aria-label="Notebook Entry Input"] >> nth=1');
await page.locator('button[title="Delete this entry"]').last().click();
await expect(page.locator('text=This action will permanently delete this entry. Do you wish to continue?')).toBeVisible();
await expect(
page.locator('text=This action will permanently delete this entry. Do you wish to continue?')
).toBeVisible();
await page.locator('button:has-text("Ok")').click();
await expect(page.locator('text=This action will permanently delete this entry. Do you wish to continue?')).toBeHidden();
await expect(
page.locator('text=This action will permanently delete this entry. Do you wish to continue?')
).toBeHidden();
});
test('Can delete objects with tags and neither return in search', async ({ page }) => {
@ -234,8 +238,8 @@ test.describe('Tagging in Notebooks @addInit', () => {
// Verify tags are present
for (let iteration = 0; iteration < ITERATIONS; iteration++) {
const entryLocator = `[aria-label="Notebook Entry"] >> nth = ${iteration}`;
await expect(page.locator(entryLocator)).toContainText("Science");
await expect(page.locator(entryLocator)).toContainText("Driving");
await expect(page.locator(entryLocator)).toContainText('Science');
await expect(page.locator(entryLocator)).toContainText('Driving');
}
//Reload Page
@ -244,8 +248,8 @@ test.describe('Tagging in Notebooks @addInit', () => {
// Verify tags persist across reload
for (let iteration = 0; iteration < ITERATIONS; iteration++) {
const entryLocator = `[aria-label="Notebook Entry"] >> nth = ${iteration}`;
await expect(page.locator(entryLocator)).toContainText("Science");
await expect(page.locator(entryLocator)).toContainText("Driving");
await expect(page.locator(entryLocator)).toContainText('Science');
await expect(page.locator(entryLocator)).toContainText('Driving');
}
});
test('Can cancel adding a tag', async ({ page }) => {

View File

@ -21,8 +21,8 @@
*****************************************************************************/
/* global __dirname */
/*
* This test suite is dedicated to testing the operator status plugin.
*/
* This test suite is dedicated to testing the operator status plugin.
*/
const path = require('path');
const { test, expect } = require('../../../../pluginFixtures');
@ -40,8 +40,12 @@ STUB (test.fixme) Rolling through each
test.describe('Operator Status', () => {
test.beforeEach(async ({ page }) => {
// FIXME: determine if plugins will be added to index.html or need to be injected
await page.addInitScript({ path: path.join(__dirname, '../../../../helper/', 'addInitExampleUser.js')});
await page.addInitScript({ path: path.join(__dirname, '../../../../helper/', 'addInitOperatorStatus.js')});
await page.addInitScript({
path: path.join(__dirname, '../../../../helper/', 'addInitExampleUser.js')
});
await page.addInitScript({
path: path.join(__dirname, '../../../../helper/', 'addInitOperatorStatus.js')
});
await page.goto('./', { waitUntil: 'domcontentloaded' });
});
@ -76,7 +80,7 @@ test.describe('Operator Status', () => {
// get selected status value
const selectStatus = page.locator('select[name="setStatus"]');
await selectStatus.selectOption({ index: 1});
await selectStatus.selectOption({ index: 1 });
const initialStatusValue = await selectStatus.inputValue();
// open manage status poll
@ -88,13 +92,14 @@ test.describe('Operator Status', () => {
const rowValuesArr = rowValues.split('\t');
const COLUMN_STATUS_INDEX = 1;
// check initial set value matches status table
expect(rowValuesArr[COLUMN_STATUS_INDEX].toLowerCase())
.toEqual(initialStatusValue.toLowerCase());
expect(rowValuesArr[COLUMN_STATUS_INDEX].toLowerCase()).toEqual(
initialStatusValue.toLowerCase()
);
// change user status
await statusPollIndicator.click();
// FIXME: might want to grab a dynamic option instead of arbitrary
await page.locator('select[name="setStatus"]').selectOption({ index: 2});
await page.locator('select[name="setStatus"]').selectOption({ index: 2 });
const updatedStatusValue = await selectStatus.inputValue();
// verify user status is reflected in table
await manageStatusPollIndicator.click();
@ -103,9 +108,9 @@ test.describe('Operator Status', () => {
const updatedRowValues = await updatedRow.innerText();
const updatedRowValuesArr = updatedRowValues.split('\t');
expect(updatedRowValuesArr[COLUMN_STATUS_INDEX].toLowerCase())
.toEqual(updatedStatusValue.toLowerCase());
expect(updatedRowValuesArr[COLUMN_STATUS_INDEX].toLowerCase()).toEqual(
updatedStatusValue.toLowerCase()
);
});
test('clear poll button removes poll responses', async ({ page }) => {
@ -120,7 +125,7 @@ test.describe('Operator Status', () => {
// get selected status value
const selectStatus = page.locator('select[name="setStatus"]');
// FIXME: might want to grab a dynamic option instead of arbitrary
await selectStatus.selectOption({ index: 1});
await selectStatus.selectOption({ index: 1 });
const initialStatusValue = await selectStatus.inputValue();
// open manage status poll
@ -132,8 +137,9 @@ test.describe('Operator Status', () => {
const rowValuesArr = rowValues.split('\t');
const COLUMN_STATUS_INDEX = 1;
// check initial set value matches status table
expect(rowValuesArr[COLUMN_STATUS_INDEX].toLowerCase())
.toEqual(initialStatusValue.toLowerCase());
expect(rowValuesArr[COLUMN_STATUS_INDEX].toLowerCase()).toEqual(
initialStatusValue.toLowerCase()
);
// clear the poll
await page.locator('button[title="Clear the previous poll question"]').click();
@ -142,13 +148,10 @@ test.describe('Operator Status', () => {
const updatedRowValues = await updatedRow.innerText();
const updatedRowValuesArr = updatedRowValues.split('\t');
const UNSET_VALUE_LABEL = 'Not set';
expect(updatedRowValuesArr[COLUMN_STATUS_INDEX])
.toEqual(UNSET_VALUE_LABEL);
expect(updatedRowValuesArr[COLUMN_STATUS_INDEX]).toEqual(UNSET_VALUE_LABEL);
});
test.fixme('iterate through all possible response values', async ({ page }) => {
// test all possible respone values for the poll
});
});

View File

@ -65,17 +65,29 @@ test.describe('Autoscale', () => {
]);
//Wait until Save Banner is gone
await page.locator('.c-message-banner__close-button').click();
await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
await page.waitForSelector('.c-message-banner__message', { state: 'detached' });
// Make sure that after turning off autoscale, the user entered range values are reflexted in the ticks.
await testYTicks(page, ['-2.00', '-1.50', '-1.00', '-0.50', '0.00', '0.50', '1.00', '1.50', '2.00']);
await testYTicks(page, [
'-2.00',
'-1.50',
'-1.00',
'-0.50',
'0.00',
'0.50',
'1.00',
'1.50',
'2.00'
]);
const canvas = page.locator('canvas').nth(1);
await canvas.hover({trial: true});
await canvas.hover({ trial: true });
await expect(page.locator('.js-series-data-loaded')).toBeVisible();
expect.soft(await canvas.screenshot()).toMatchSnapshot('autoscale-canvas-prepan.png', { animations: 'disabled' });
expect
.soft(await canvas.screenshot())
.toMatchSnapshot('autoscale-canvas-prepan.png', { animations: 'disabled' });
//Alt Drag Start
await page.keyboard.down('Alt');
@ -98,9 +110,11 @@ test.describe('Autoscale', () => {
await testYTicks(page, ['0.00', '0.50', '1.00', '1.50', '2.00', '2.50', '3.00', '3.50']);
//Wait for canvas to stablize.
await canvas.hover({trial: true});
await canvas.hover({ trial: true });
expect.soft(await canvas.screenshot()).toMatchSnapshot('autoscale-canvas-panned.png', { animations: 'disabled' });
expect
.soft(await canvas.screenshot())
.toMatchSnapshot('autoscale-canvas-panned.png', { animations: 'disabled' });
});
});
@ -109,7 +123,11 @@ test.describe('Autoscale', () => {
* @param {string} start
* @param {string} end
*/
async function setTimeRange(page, start = '2022-03-29 22:00:00.000Z', end = '2022-03-29 22:00:30.000Z') {
async function setTimeRange(
page,
start = '2022-03-29 22:00:00.000Z',
end = '2022-03-29 22:00:30.000Z'
) {
// Set a specific time range for consistency, otherwise it will change
// on every test to a range based on the current time.
@ -139,10 +157,13 @@ async function createSinewaveOverlayPlot(page, myItemsFolderName) {
]);
//Wait until Save Banner is gone
await page.locator('.c-message-banner__close-button').click();
await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
await page.waitForSelector('.c-message-banner__message', { state: 'detached' });
// save (exit edit mode)
await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
await page
.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button')
.nth(1)
.click();
await page.locator('text=Save and Finish Editing').click();
// click create button
@ -158,7 +179,7 @@ async function createSinewaveOverlayPlot(page, myItemsFolderName) {
]);
//Wait until Save Banner is gone
await page.locator('.c-message-banner__close-button').click();
await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
await page.waitForSelector('.c-message-banner__message', { state: 'detached' });
// focus the overlay plot
await page.locator(`text=Open MCT ${myItemsFolderName} >> span`).nth(3).click();
@ -194,7 +215,7 @@ async function setUserDefinedMinAndMax(page, min, max) {
async function testYTicks(page, values) {
const yTicks = page.locator('.gl-plot-y-tick-label');
await page.locator('canvas >> nth=1').hover();
let promises = [yTicks.count().then(c => expect(c).toBe(values.length))];
let promises = [yTicks.count().then((c) => expect(c).toBe(values.length))];
for (let i = 0, l = values.length; i < l; i += 1) {
promises.push(expect.soft(yTicks.nth(i)).toHaveText(values[i])); // eslint-disable-line

View File

@ -29,7 +29,10 @@ const { test, expect } = require('../../../../pluginFixtures');
const { selectInspectorTab } = require('../../../../appActions');
test.describe('Log plot tests', () => {
test('Log Plot ticks are functionally correct in regular and log mode and after refresh', async ({ page, openmctConfig }) => {
test('Log Plot ticks are functionally correct in regular and log mode and after refresh', async ({
page,
openmctConfig
}) => {
const { myItemsFolderName } = openmctConfig;
//Test.slow decorator is currently broken. Needs to be fixed in https://github.com/nasa/openmct/issues/5374
@ -51,7 +54,9 @@ test.describe('Log plot tests', () => {
// Leaving test as 'TODO' for now.
// NOTE: Not eligible for community contributions.
test.fixme('Verify that log mode option is reflected in import/export JSON', async ({ page, openmctConfig }) => {
test.fixme(
'Verify that log mode option is reflected in import/export JSON',
async ({ page, openmctConfig }) => {
const { myItemsFolderName } = openmctConfig;
await makeOverlayPlot(page, myItemsFolderName);
@ -66,7 +71,8 @@ test.describe('Log plot tests', () => {
// TODO, the plot is slightly at different position that in the other test, so this fails.
// ...We can fix it by copying all steps from the first test...
// await testLogPlotPixels(page);
});
}
);
});
/**
@ -93,14 +99,14 @@ async function makeOverlayPlot(page, myItemsFolderName) {
await page.locator('button.c-create-button').click();
await page.locator('li[role="menuitem"]:has-text("Overlay Plot")').click();
await Promise.all([
page.waitForNavigation({ waitUntil: 'networkidle'}),
page.waitForNavigation({ waitUntil: 'networkidle' }),
page.locator('button:has-text("OK")').click(),
//Wait for Save Banner to appear
page.waitForSelector('.c-message-banner__message')
]);
//Wait until Save Banner is gone
await page.locator('.c-message-banner__close-button').click();
await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
await page.waitForSelector('.c-message-banner__message', { state: 'detached' });
// save the overlay plot
@ -125,14 +131,14 @@ async function makeOverlayPlot(page, myItemsFolderName) {
// Click OK to make generator
await Promise.all([
page.waitForNavigation({ waitUntil: 'networkidle'}),
page.waitForNavigation({ waitUntil: 'networkidle' }),
page.locator('button:has-text("OK")').click(),
//Wait for Save Banner to appear
page.waitForSelector('.c-message-banner__message')
]);
//Wait until Save Banner is gone
await page.locator('.c-message-banner__close-button').click();
await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
await page.waitForSelector('.c-message-banner__message', { state: 'detached' });
// click on overlay plot
@ -205,7 +211,10 @@ async function disableLogMode(page) {
*/
async function saveOverlayPlot(page) {
// save overlay plot
await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
await page
.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button')
.nth(1)
.click();
await Promise.all([
page.locator('text=Save and Finish Editing').click(),

View File

@ -27,14 +27,18 @@ Tests to verify log plot functionality when objects are missing
const { test, expect } = require('../../../../pluginFixtures');
test.describe('Handle missing object for plots', () => {
test('Displays empty div for missing stacked plot item @unstable', async ({ page, browserName, openmctConfig }) => {
test('Displays empty div for missing stacked plot item @unstable', async ({
page,
browserName,
openmctConfig
}) => {
// eslint-disable-next-line playwright/no-skipped-test
test.skip(browserName === 'firefox', 'Firefox failing due to console events being missed');
const { myItemsFolderName } = openmctConfig;
const errorLogs = [];
page.on("console", (message) => {
page.on('console', (message) => {
if (message.type() === 'warning' && message.text().includes('Missing domain object')) {
errorLogs.push(message.text());
}
@ -52,18 +56,15 @@ test.describe('Handle missing object for plots', () => {
delete parsedData[lastKey];
//Sets local storage with missing object
await page.evaluate(
`window.localStorage.setItem('mct', '${JSON.stringify(parsedData)}')`
);
await page.evaluate(`window.localStorage.setItem('mct', '${JSON.stringify(parsedData)}')`);
//Reloads page and clicks on stacked plot
await Promise.all([
page.reload(),
page.waitForLoadState('networkidle')
]);
await Promise.all([page.reload(), page.waitForLoadState('networkidle')]);
//Verify Main section is there on load
await expect.soft(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Stacked Plot');
await expect
.soft(page.locator('.l-browse-bar__object-name'))
.toContainText('Unnamed Stacked Plot');
await page.locator(`text=Open MCT ${myItemsFolderName} >> span`).nth(3).click();
await Promise.all([
@ -72,7 +73,7 @@ test.describe('Handle missing object for plots', () => {
]);
//Check that there is only one stacked item plot with a plot, the missing one will be empty
await expect(page.locator(".c-plot--stacked-container:has(.gl-plot)")).toHaveCount(1);
await expect(page.locator('.c-plot--stacked-container:has(.gl-plot)')).toHaveCount(1);
//Verify that console.warn is thrown
expect(errorLogs).toHaveLength(1);
});
@ -91,7 +92,7 @@ async function makeStackedPlot(page, myItemsFolderName) {
await page.locator('li[role="menuitem"]:has-text("Stacked Plot")').click();
await Promise.all([
page.waitForNavigation({ waitUntil: 'networkidle'}),
page.waitForNavigation({ waitUntil: 'networkidle' }),
page.locator('button:has-text("OK")').click(),
//Wait for Save Banner to appear
page.waitForSelector('.c-message-banner__message')
@ -127,7 +128,10 @@ async function makeStackedPlot(page, myItemsFolderName) {
*/
async function saveStackedPlot(page) {
// save stacked plot
await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
await page
.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button')
.nth(1)
.click();
await Promise.all([
page.locator('text=Save and Finish Editing').click(),
@ -149,7 +153,7 @@ async function createSineWaveGenerator(page) {
await page.locator('li[role="menuitem"]:has-text("Sine Wave Generator")').click();
await Promise.all([
page.waitForNavigation({ waitUntil: 'networkidle'}),
page.waitForNavigation({ waitUntil: 'networkidle' }),
page.locator('button:has-text("OK")').click(),
//Wait for Save Banner to appear
page.waitForSelector('.c-message-banner__message')

View File

@ -26,7 +26,12 @@ necessarily be used for reference when writing new tests in this area.
*/
const { test, expect } = require('../../../../pluginFixtures');
const { createDomainObjectWithDefaults, getCanvasPixels, selectInspectorTab, waitForPlotsToRender } = require('../../../../appActions');
const {
createDomainObjectWithDefaults,
getCanvasPixels,
selectInspectorTab,
waitForPlotsToRender
} = require('../../../../appActions');
test.describe('Overlay Plot', () => {
test.beforeEach(async ({ page }) => {
@ -35,11 +40,11 @@ test.describe('Overlay Plot', () => {
test('Plot legend color is in sync with plot series color', async ({ page }) => {
const overlayPlot = await createDomainObjectWithDefaults(page, {
type: "Overlay Plot"
type: 'Overlay Plot'
});
await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
type: 'Sine Wave Generator',
parent: overlayPlot.uuid
});
@ -53,22 +58,26 @@ test.describe('Overlay Plot', () => {
await page.locator('.c-click-swatch--menu').click();
await page.locator('.c-palette__item[style="background: rgb(255, 166, 61);"]').click();
// gets color for swatch located in legend
const seriesColorSwatch = page.locator('.gl-plot-y-label-swatch-container > .plot-series-color-swatch');
const seriesColorSwatch = page.locator(
'.gl-plot-y-label-swatch-container > .plot-series-color-swatch'
);
await expect(seriesColorSwatch).toHaveCSS('background-color', 'rgb(255, 166, 61)');
});
test('Limit lines persist when series is moved to another Y Axis and on refresh', async ({ page }) => {
test('Limit lines persist when series is moved to another Y Axis and on refresh', async ({
page
}) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/6338'
});
// Create an Overlay Plot with a default SWG
const overlayPlot = await createDomainObjectWithDefaults(page, {
type: "Overlay Plot"
type: 'Overlay Plot'
});
const swgA = await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
type: 'Sine Wave Generator',
parent: overlayPlot.uuid
});
@ -83,8 +92,15 @@ test.describe('Overlay Plot', () => {
// Expand the "Sine Wave Generator" plot series options and enable limit lines
await selectInspectorTab(page, 'Config');
await page.getByRole('list', { name: 'Plot Series Properties' }).locator('span').first().click();
await page.getByRole('list', { name: 'Plot Series Properties' }).locator('[title="Display limit lines"]~div input').check();
await page
.getByRole('list', { name: 'Plot Series Properties' })
.locator('span')
.first()
.click();
await page
.getByRole('list', { name: 'Plot Series Properties' })
.locator('[title="Display limit lines"]~div input')
.check();
await assertLimitLinesExistAndAreVisible(page);
@ -104,7 +120,9 @@ test.describe('Overlay Plot', () => {
await selectInspectorTab(page, 'Elements');
// Drag Sine Wave Generator series from Y Axis 1 into Y Axis 2
await page.locator(`#inspector-elements-tree >> text=${swgA.name}`).dragTo(page.locator('[aria-label="Element Item Group Y Axis 2"]'));
await page
.locator(`#inspector-elements-tree >> text=${swgA.name}`)
.dragTo(page.locator('[aria-label="Element Item Group Y Axis 2"]'));
await assertLimitLinesExistAndAreVisible(page);
@ -117,32 +135,33 @@ test.describe('Overlay Plot', () => {
await page.reload();
await assertLimitLinesExistAndAreVisible(page);
});
test('The elements pool supports dragging series into multiple y-axis buckets', async ({ page }) => {
test('The elements pool supports dragging series into multiple y-axis buckets', async ({
page
}) => {
const overlayPlot = await createDomainObjectWithDefaults(page, {
type: "Overlay Plot"
type: 'Overlay Plot'
});
const swgA = await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
type: 'Sine Wave Generator',
parent: overlayPlot.uuid
});
const swgB = await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
type: 'Sine Wave Generator',
parent: overlayPlot.uuid
});
const swgC = await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
type: 'Sine Wave Generator',
parent: overlayPlot.uuid
});
const swgD = await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
type: 'Sine Wave Generator',
parent: overlayPlot.uuid
});
const swgE = await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
type: 'Sine Wave Generator',
parent: overlayPlot.uuid
});
@ -152,9 +171,15 @@ test.describe('Overlay Plot', () => {
await selectInspectorTab(page, 'Elements');
// Drag swg a, c, e into Y Axis 2
await page.locator(`#inspector-elements-tree >> text=${swgA.name}`).dragTo(page.locator('[aria-label="Element Item Group Y Axis 2"]'));
await page.locator(`#inspector-elements-tree >> text=${swgC.name}`).dragTo(page.locator('[aria-label="Element Item Group Y Axis 2"]'));
await page.locator(`#inspector-elements-tree >> text=${swgE.name}`).dragTo(page.locator('[aria-label="Element Item Group Y Axis 2"]'));
await page
.locator(`#inspector-elements-tree >> text=${swgA.name}`)
.dragTo(page.locator('[aria-label="Element Item Group Y Axis 2"]'));
await page
.locator(`#inspector-elements-tree >> text=${swgC.name}`)
.dragTo(page.locator('[aria-label="Element Item Group Y Axis 2"]'));
await page
.locator(`#inspector-elements-tree >> text=${swgE.name}`)
.dragTo(page.locator('[aria-label="Element Item Group Y Axis 2"]'));
// Assert that Y Axis 1 and Y Axis 2 property groups are visible only
await selectInspectorTab(page, 'Config');
@ -167,14 +192,16 @@ test.describe('Overlay Plot', () => {
await expect(yAxis2PropertyGroup).toBeVisible();
await expect(yAxis3PropertyGroup).toBeHidden();
const yAxis1Group = page.getByLabel("Y Axis 1");
const yAxis2Group = page.getByLabel("Y Axis 2");
const yAxis3Group = page.getByLabel("Y Axis 3");
const yAxis1Group = page.getByLabel('Y Axis 1');
const yAxis2Group = page.getByLabel('Y Axis 2');
const yAxis3Group = page.getByLabel('Y Axis 3');
await selectInspectorTab(page, 'Elements');
// Drag swg b into Y Axis 3
await page.locator(`#inspector-elements-tree >> text=${swgB.name}`).dragTo(page.locator('[aria-label="Element Item Group Y Axis 3"]'));
await page
.locator(`#inspector-elements-tree >> text=${swgB.name}`)
.dragTo(page.locator('[aria-label="Element Item Group Y Axis 3"]'));
// Assert that all Y Axis property groups are visible
await selectInspectorTab(page, 'Config');
@ -198,13 +225,15 @@ test.describe('Overlay Plot', () => {
expect(yAxis3Group.getByRole('listitem').nth(0).getByText(swgB.name)).toBeTruthy();
});
test('Clicking on an item in the elements pool brings up the plot preview with data points', async ({ page }) => {
test('Clicking on an item in the elements pool brings up the plot preview with data points', async ({
page
}) => {
const overlayPlot = await createDomainObjectWithDefaults(page, {
type: "Overlay Plot"
type: 'Overlay Plot'
});
const swgA = await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
type: 'Sine Wave Generator',
parent: overlayPlot.uuid
});

View File

@ -21,9 +21,9 @@
*****************************************************************************/
/*
* This test suite is dedicated to testing the rendering and interaction of plots.
*
*/
* This test suite is dedicated to testing the rendering and interaction of plots.
*
*/
const { test, expect } = require('../../../../pluginFixtures');
const { createDomainObjectWithDefaults, getCanvasPixels } = require('../../../../appActions');
@ -34,7 +34,9 @@ test.describe('Plot Rendering', () => {
test.beforeEach(async ({ page }) => {
// Open a browser, navigate to the main page, and wait until all networkevents to resolve
await page.goto('./', { waitUntil: 'domcontentloaded' });
sineWaveGeneratorObject = await createDomainObjectWithDefaults(page, { type: 'Sine Wave Generator' });
sineWaveGeneratorObject = await createDomainObjectWithDefaults(page, {
type: 'Sine Wave Generator'
});
});
test('Plots do not re-request data when a plot is clicked', async ({ page }) => {
@ -44,7 +46,7 @@ test.describe('Plot Rendering', () => {
await page.locator('canvas').nth(1).click();
// No request was made to get historical data
const createMineFolderRequests = [];
page.on('request', req => {
page.on('request', (req) => {
createMineFolderRequests.push(req);
});
expect(createMineFolderRequests.length).toEqual(0);
@ -73,13 +75,17 @@ async function editSineWaveToUseInfinityOption(page, sineWaveGeneratorObject) {
// Edit SWG properties to include infinity values
await page.locator('[title="More options"]').click();
await page.locator('[title="Edit properties of this object."]').click();
await page.getByRole('switch', {
name: "Include Infinity Values"
}).check();
await page
.getByRole('switch', {
name: 'Include Infinity Values'
})
.check();
await page.getByRole('button', {
await page
.getByRole('button', {
name: 'Save'
}).click();
})
.click();
// FIXME: Changes to SWG properties should be reflected on save, but they're not?
// Thus, navigate away and back to the object.

View File

@ -21,8 +21,8 @@
*****************************************************************************/
/*
* This test suite is dedicated to testing the Scatter Plot component.
*/
* This test suite is dedicated to testing the Scatter Plot component.
*/
const { test, expect } = require('../../../../pluginFixtures');
const { createDomainObjectWithDefaults, selectInspectorTab } = require('../../../../appActions');
@ -67,7 +67,13 @@ test.describe('Scatter Plot', () => {
});
// Verify that the 'Replace telemetry source' modal appears and accept it
await expect.soft(page.locator('text=This action will replace the current telemetry source. Do you want to continue?')).toBeVisible();
await expect
.soft(
page.locator(
'text=This action will replace the current telemetry source. Do you want to continue?'
)
)
.toBeVisible();
await page.click('text=Ok');
// Navigate to the scatter plot and verify that the new SWG
@ -88,7 +94,13 @@ test.describe('Scatter Plot', () => {
await page.locator('li[title="Remove this object from its containing object."]').click();
// Verify that the 'Remove object' confirmation modal appears and accept it
await expect.soft(page.locator('text=Warning! This action will remove this object. Are you sure you want to continue?')).toBeVisible();
await expect
.soft(
page.locator(
'text=Warning! This action will remove this object. Are you sure you want to continue?'
)
)
.toBeVisible();
await page.click('text=Ok');
// Verify that the elements pool shows no elements

View File

@ -39,27 +39,33 @@ test.describe('Stacked Plot', () => {
await page.goto('./', { waitUntil: 'domcontentloaded' });
stackedPlot = await createDomainObjectWithDefaults(page, {
type: "Stacked Plot"
type: 'Stacked Plot'
});
swgA = await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
type: 'Sine Wave Generator',
parent: stackedPlot.uuid
});
swgB = await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
type: 'Sine Wave Generator',
parent: stackedPlot.uuid
});
swgC = await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
type: 'Sine Wave Generator',
parent: stackedPlot.uuid
});
});
test('Using the remove action removes the correct plot', async ({ page }) => {
const swgAElementsPoolItem = page.locator('#inspector-elements-tree').locator('.c-object-label', { hasText: swgA.name });
const swgBElementsPoolItem = page.locator('#inspector-elements-tree').locator('.c-object-label', { hasText: swgB.name });
const swgCElementsPoolItem = page.locator('#inspector-elements-tree').locator('.c-object-label', { hasText: swgC.name });
const swgAElementsPoolItem = page
.locator('#inspector-elements-tree')
.locator('.c-object-label', { hasText: swgA.name });
const swgBElementsPoolItem = page
.locator('#inspector-elements-tree')
.locator('.c-object-label', { hasText: swgB.name });
const swgCElementsPoolItem = page
.locator('#inspector-elements-tree')
.locator('.c-object-label', { hasText: swgC.name });
await page.goto(stackedPlot.url);
@ -68,8 +74,11 @@ test.describe('Stacked Plot', () => {
await selectInspectorTab(page, 'Elements');
await swgBElementsPoolItem.click({ button: 'right' });
await page.getByRole('menuitem').filter({ hasText: /Remove/ }).click();
await page.getByRole('button').filter({ hasText: "OK" }).click();
await page
.getByRole('menuitem')
.filter({ hasText: /Remove/ })
.click();
await page.getByRole('button').filter({ hasText: 'OK' }).click();
await expect(page.locator('#inspector-elements-tree .js-elements-pool__item')).toHaveCount(2);
@ -80,9 +89,15 @@ test.describe('Stacked Plot', () => {
});
test('Can reorder Stacked Plot items', async ({ page }) => {
const swgAElementsPoolItem = page.locator('#inspector-elements-tree').locator('.c-object-label', { hasText: swgA.name });
const swgBElementsPoolItem = page.locator('#inspector-elements-tree').locator('.c-object-label', { hasText: swgB.name });
const swgCElementsPoolItem = page.locator('#inspector-elements-tree').locator('.c-object-label', { hasText: swgC.name });
const swgAElementsPoolItem = page
.locator('#inspector-elements-tree')
.locator('.c-object-label', { hasText: swgA.name });
const swgBElementsPoolItem = page
.locator('#inspector-elements-tree')
.locator('.c-object-label', { hasText: swgB.name });
const swgCElementsPoolItem = page
.locator('#inspector-elements-tree')
.locator('.c-object-label', { hasText: swgC.name });
await page.goto(stackedPlot.url);
@ -128,7 +143,9 @@ test.describe('Stacked Plot', () => {
await expect(stackedPlotItem3).toHaveAttribute('aria-label', `Stacked Plot Item ${swgA.name}`);
});
test('Selecting a child plot while in browse and edit modes shows its properties in the inspector', async ({ page }) => {
test('Selecting a child plot while in browse and edit modes shows its properties in the inspector', async ({
page
}) => {
await page.goto(stackedPlot.url);
await selectInspectorTab(page, 'Config');
@ -137,25 +154,37 @@ test.describe('Stacked Plot', () => {
await page.locator(`[aria-label="Stacked Plot Item ${swgA.name}"] canvas`).nth(1).click();
// Assert that the inspector shows the Y Axis properties for swgA
await expect(page.locator('[aria-label="Plot Series Properties"] >> h2')).toContainText("Plot Series");
await expect(page.getByRole('heading', { name: "Y Axis" })).toBeVisible();
await expect(page.locator('[aria-label="Plot Series Properties"] .c-object-label')).toContainText(swgA.name);
await expect(page.locator('[aria-label="Plot Series Properties"] >> h2')).toContainText(
'Plot Series'
);
await expect(page.getByRole('heading', { name: 'Y Axis' })).toBeVisible();
await expect(
page.locator('[aria-label="Plot Series Properties"] .c-object-label')
).toContainText(swgA.name);
// Click on the 2nd plot
await page.locator(`[aria-label="Stacked Plot Item ${swgB.name}"] canvas`).nth(1).click();
// Assert that the inspector shows the Y Axis properties for swgB
await expect(page.locator('[aria-label="Plot Series Properties"] >> h2')).toContainText("Plot Series");
await expect(page.locator('[aria-label="Plot Series Properties"] >> h2')).toContainText(
'Plot Series'
);
await expect(page.getByRole('heading', { name: 'Y Axis' })).toBeVisible();
await expect(page.locator('[aria-label="Plot Series Properties"] .c-object-label')).toContainText(swgB.name);
await expect(
page.locator('[aria-label="Plot Series Properties"] .c-object-label')
).toContainText(swgB.name);
// Click on the 3rd plot
await page.locator(`[aria-label="Stacked Plot Item ${swgC.name}"] canvas`).nth(1).click();
// Assert that the inspector shows the Y Axis properties for swgC
await expect(page.locator('[aria-label="Plot Series Properties"] >> h2')).toContainText("Plot Series");
await expect(page.locator('[aria-label="Plot Series Properties"] >> h2')).toContainText(
'Plot Series'
);
await expect(page.getByRole('heading', { name: 'Y Axis' })).toBeVisible();
await expect(page.locator('[aria-label="Plot Series Properties"] .c-object-label')).toContainText(swgC.name);
await expect(
page.locator('[aria-label="Plot Series Properties"] .c-object-label')
).toContainText(swgC.name);
// Go into edit mode
await page.click('button[title="Edit"]');
@ -166,24 +195,36 @@ test.describe('Stacked Plot', () => {
await page.locator(`[aria-label="Stacked Plot Item ${swgA.name}"]`).click();
// Assert that the inspector shows the Y Axis properties for swgA
await expect(page.locator('[aria-label="Plot Series Properties"] >> h2')).toContainText("Plot Series");
await expect(page.locator('[aria-label="Plot Series Properties"] >> h2')).toContainText(
'Plot Series'
);
await expect(page.getByRole('heading', { name: 'Y Axis' })).toBeVisible();
await expect(page.locator('[aria-label="Plot Series Properties"] .c-object-label')).toContainText(swgA.name);
await expect(
page.locator('[aria-label="Plot Series Properties"] .c-object-label')
).toContainText(swgA.name);
//Click on canvas for the 2nd plot
await page.locator(`[aria-label="Stacked Plot Item ${swgB.name}"]`).click();
// Assert that the inspector shows the Y Axis properties for swgB
await expect(page.locator('[aria-label="Plot Series Properties"] >> h2')).toContainText("Plot Series");
await expect(page.locator('[aria-label="Plot Series Properties"] >> h2')).toContainText(
'Plot Series'
);
await expect(page.getByRole('heading', { name: 'Y Axis' })).toBeVisible();
await expect(page.locator('[aria-label="Plot Series Properties"] .c-object-label')).toContainText(swgB.name);
await expect(
page.locator('[aria-label="Plot Series Properties"] .c-object-label')
).toContainText(swgB.name);
//Click on canvas for the 3rd plot
await page.locator(`[aria-label="Stacked Plot Item ${swgC.name}"]`).click();
// Assert that the inspector shows the Y Axis properties for swgC
await expect(page.locator('[aria-label="Plot Series Properties"] >> h2')).toContainText("Plot Series");
await expect(page.locator('[aria-label="Plot Series Properties"] >> h2')).toContainText(
'Plot Series'
);
await expect(page.getByRole('heading', { name: 'Y Axis' })).toBeVisible();
await expect(page.locator('[aria-label="Plot Series Properties"] .c-object-label')).toContainText(swgC.name);
await expect(
page.locator('[aria-label="Plot Series Properties"] .c-object-label')
).toContainText(swgC.name);
});
});

View File

@ -25,7 +25,12 @@ Tests to verify plot tagging functionality.
*/
const { test, expect } = require('../../../../pluginFixtures');
const { createDomainObjectWithDefaults, setRealTimeMode, setFixedTimeMode, waitForPlotsToRender } = require('../../../../appActions');
const {
createDomainObjectWithDefaults,
setRealTimeMode,
setFixedTimeMode,
waitForPlotsToRender
} = require('../../../../appActions');
test.describe('Plot Tagging', () => {
/**
@ -36,8 +41,8 @@ test.describe('Plot Tagging', () => {
* @param {Number} yEnd a telemetry item with a plot
* @returns {Promise}
*/
async function createTags({page, canvas, xEnd, yEnd}) {
await canvas.hover({trial: true});
async function createTags({ page, canvas, xEnd, yEnd }) {
await canvas.hover({ trial: true });
//Alt+Shift Drag Start to select some points to tag
await page.keyboard.down('Alt');
@ -59,7 +64,7 @@ test.describe('Plot Tagging', () => {
await page.keyboard.up('Shift');
//Wait for canvas to stablize.
await canvas.hover({trial: true});
await canvas.hover({ trial: true });
// add some tags
await page.getByText('Annotations').click();
@ -87,7 +92,7 @@ test.describe('Plot Tagging', () => {
const canvas = page.locator('canvas').nth(1);
//Wait for canvas to stablize.
await canvas.hover({trial: true});
await canvas.hover({ trial: true });
// click on the tagged plot point
await canvas.click({
@ -115,7 +120,11 @@ test.describe('Plot Tagging', () => {
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('driv');
// click on the search result
await page.getByRole('searchbox', { name: 'OpenMCT Search' }).getByText(/Sine Wave/).first().click();
await page
.getByRole('searchbox', { name: 'OpenMCT Search' })
.getByText(/Sine Wave/)
.first()
.click();
// Delete Driving
await page.hover('[aria-label="Tag"]:has-text("Driving")');
@ -124,8 +133,8 @@ test.describe('Plot Tagging', () => {
// Search for Science
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('sc');
await expect(page.locator('[aria-label="Search Result"]').nth(0)).toContainText("Science");
await expect(page.locator('[aria-label="Search Result"]').nth(0)).not.toContainText("Drilling");
await expect(page.locator('[aria-label="Search Result"]').nth(0)).toContainText('Science');
await expect(page.locator('[aria-label="Search Result"]').nth(0)).not.toContainText('Drilling');
// Search for Driving
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
@ -162,18 +171,18 @@ test.describe('Plot Tagging', () => {
test.slow();
const overlayPlot = await createDomainObjectWithDefaults(page, {
type: "Overlay Plot"
type: 'Overlay Plot'
});
const alphaSineWave = await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
name: "Alpha Sine Wave",
type: 'Sine Wave Generator',
name: 'Alpha Sine Wave',
parent: overlayPlot.uuid
});
await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
name: "Beta Sine Wave",
type: 'Sine Wave Generator',
name: 'Beta Sine Wave',
parent: overlayPlot.uuid
});
@ -204,7 +213,11 @@ test.describe('Plot Tagging', () => {
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('sc');
// click on the search result
await page.getByRole('searchbox', { name: 'OpenMCT Search' }).getByText('Alpha Sine Wave').first().click();
await page
.getByRole('searchbox', { name: 'OpenMCT Search' })
.getByText('Alpha Sine Wave')
.first()
.click();
// wait for plots to load
await expect(page.locator('.js-series-data-loaded')).toBeVisible();
// expect plot to be paused
@ -215,7 +228,7 @@ test.describe('Plot Tagging', () => {
test('Tags work with Plot View of telemetry items', async ({ page }) => {
await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator"
type: 'Sine Wave Generator'
});
const canvas = page.locator('canvas').nth(1);
@ -230,18 +243,18 @@ test.describe('Plot Tagging', () => {
test('Tags work with Stacked Plots', async ({ page }) => {
const stackedPlot = await createDomainObjectWithDefaults(page, {
type: "Stacked Plot"
type: 'Stacked Plot'
});
const alphaSineWave = await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
name: "Alpha Sine Wave",
type: 'Sine Wave Generator',
name: 'Alpha Sine Wave',
parent: stackedPlot.uuid
});
await createDomainObjectWithDefaults(page, {
type: "Sine Wave Generator",
name: "Beta Sine Wave",
type: 'Sine Wave Generator',
name: 'Beta Sine Wave',
parent: stackedPlot.uuid
});

View File

@ -24,7 +24,9 @@ const { createDomainObjectWithDefaults } = require('../../../../appActions');
const { test, expect } = require('../../../../pluginFixtures');
test.describe('Telemetry Table', () => {
test('unpauses and filters data when paused by button and user changes bounds', async ({ page }) => {
test('unpauses and filters data when paused by button and user changes bounds', async ({
page
}) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/5113'
@ -65,7 +67,12 @@ test.describe('Telemetry Table', () => {
await expect(tableWrapper).not.toHaveClass(/is-paused/);
// Get the most recent telemetry date
const latestTelemetryDate = await page.locator('table.c-telemetry-table__body > tbody > tr').last().locator('td').nth(1).getAttribute('title');
const latestTelemetryDate = await page
.locator('table.c-telemetry-table__body > tbody > tr')
.last()
.locator('td')
.nth(1)
.getAttribute('title');
// Verify that it is <= our new end bound
const latestMilliseconds = Date.parse(latestTelemetryDate);

View File

@ -21,7 +21,12 @@
*****************************************************************************/
const { test, expect } = require('../../../../pluginFixtures');
const { setFixedTimeMode, setRealTimeMode, setStartOffset, setEndOffset } = require('../../../../appActions');
const {
setFixedTimeMode,
setRealTimeMode,
setStartOffset,
setEndOffset
} = require('../../../../appActions');
test.describe('Time conductor operations', () => {
test('validate start time does not exceeds end time', async ({ page }) => {
@ -48,23 +53,27 @@ test.describe('Time conductor operations', () => {
await startTimeLocator.fill(startDate.toString());
// invalid start date
startDate = (year + 1) + startDate.substring(4);
startDate = year + 1 + startDate.substring(4);
await startTimeLocator.fill(startDate.toString());
await endTimeLocator.click();
const startDateValidityStatus = await startTimeLocator.evaluate((element) => element.checkValidity());
const startDateValidityStatus = await startTimeLocator.evaluate((element) =>
element.checkValidity()
);
expect(startDateValidityStatus).not.toBeTruthy();
// fix to valid start date
startDate = (year - 1) + startDate.substring(4);
startDate = year - 1 + startDate.substring(4);
await startTimeLocator.fill(startDate.toString());
// invalid end date
endDate = (year - 2) + endDate.substring(4);
endDate = year - 2 + endDate.substring(4);
await endTimeLocator.fill(endDate.toString());
await startTimeLocator.click();
const endDateValidityStatus = await endTimeLocator.evaluate((element) => element.checkValidity());
const endDateValidityStatus = await endTimeLocator.evaluate((element) =>
element.checkValidity()
);
expect(endDateValidityStatus).not.toBeTruthy();
});
});
@ -91,7 +100,9 @@ test.describe('Time conductor input fields real-time mode', () => {
await setStartOffset(page, startOffset);
// Verify time was updated on time offset button
await expect(page.locator('data-testid=conductor-start-offset-button')).toContainText('00:30:23');
await expect(page.locator('data-testid=conductor-start-offset-button')).toContainText(
'00:30:23'
);
// Set end time offset
await setEndOffset(page, endOffset);
@ -104,7 +115,9 @@ test.describe('Time conductor input fields real-time mode', () => {
* Verify that offsets and url params are preserved when switching
* between fixed timespan and real-time mode.
*/
test('preserve offsets and url params when switching between fixed and real-time mode', async ({ page }) => {
test('preserve offsets and url params when switching between fixed and real-time mode', async ({
page
}) => {
const startOffset = {
mins: '30',
secs: '23'
@ -115,8 +128,8 @@ test.describe('Time conductor input fields real-time mode', () => {
};
// Convert offsets to milliseconds
const startDelta = (30 * 60 * 1000) + (23 * 1000);
const endDelta = (1 * 1000);
const startDelta = 30 * 60 * 1000 + 23 * 1000;
const endDelta = 1 * 1000;
// Go to baseURL
await page.goto('./', { waitUntil: 'domcontentloaded' });
@ -137,7 +150,9 @@ test.describe('Time conductor input fields real-time mode', () => {
await setRealTimeMode(page);
// Verify updated start time offset persists after mode switch
await expect(page.locator('data-testid=conductor-start-offset-button')).toContainText('00:30:23');
await expect(page.locator('data-testid=conductor-start-offset-button')).toContainText(
'00:30:23'
);
// Verify updated end time offset persists after mode switch
await expect(page.locator('data-testid=conductor-end-offset-button')).toContainText('00:00:01');
@ -148,20 +163,29 @@ test.describe('Time conductor input fields real-time mode', () => {
expect(page.url()).toContain(`endDelta=${endDelta}`);
});
test.fixme('time conductor history in fixed time mode will track changing start and end times', async ({ page }) => {
test.fixme(
'time conductor history in fixed time mode will track changing start and end times',
async ({ page }) => {
// change start time, verify it's tracked in history
// change end time, verify it's tracked in history
});
}
);
test.fixme('time conductor history in realtime mode will track changing start and end times', async ({ page }) => {
test.fixme(
'time conductor history in realtime mode will track changing start and end times',
async ({ page }) => {
// change start offset, verify it's tracked in history
// change end offset, verify it's tracked in history
});
}
);
test.fixme('time conductor history allows you to set a historical timeframe', async ({ page }) => {
test.fixme(
'time conductor history allows you to set a historical timeframe',
async ({ page }) => {
// make sure there are historical history options
// select an option and make sure the time conductor start and end bounds are updated correctly
});
}
);
test.fixme('time conductor history allows you to set a realtime offsets', async ({ page }) => {
// make sure there are realtime history options
@ -170,7 +194,7 @@ test.describe('Time conductor input fields real-time mode', () => {
});
test.describe('Time Conductor History', () => {
test("shows milliseconds on hover @unstable", async ({ page }) => {
test('shows milliseconds on hover @unstable', async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/4386'
@ -178,13 +202,19 @@ test.describe('Time Conductor History', () => {
// Navigate to Open MCT in Fixed Time Mode, UTC Time System
// with startBound at 2022-01-01 00:00:00.000Z
// and endBound at 2022-01-01 00:00:00.200Z
await page.goto('./#/browse/mine?view=grid&tc.mode=fixed&tc.startBound=1640995200000&tc.endBound=1640995200200&tc.timeSystem=utc&hideInspector=true', { waitUntil: 'networkidle' });
await page.locator("[aria-label='Time Conductor History']").hover({ trial: true});
await page.goto(
'./#/browse/mine?view=grid&tc.mode=fixed&tc.startBound=1640995200000&tc.endBound=1640995200200&tc.timeSystem=utc&hideInspector=true',
{ waitUntil: 'networkidle' }
);
await page.locator("[aria-label='Time Conductor History']").hover({ trial: true });
await page.locator("[aria-label='Time Conductor History']").click();
// Validate history item format
const historyItem = page.locator('text="2022-01-01 00:00:00 + 200ms"');
await expect(historyItem).toBeEnabled();
await expect(historyItem).toHaveAttribute('title', '2022-01-01 00:00:00.000 - 2022-01-01 00:00:00.200');
await expect(historyItem).toHaveAttribute(
'title',
'2022-01-01 00:00:00.000 - 2022-01-01 00:00:00.200'
);
});
});

View File

@ -21,7 +21,10 @@
*****************************************************************************/
const { test, expect } = require('../../../../pluginFixtures');
const { openObjectTreeContextMenu, createDomainObjectWithDefaults } = require('../../../../appActions');
const {
openObjectTreeContextMenu,
createDomainObjectWithDefaults
} = require('../../../../appActions');
test.describe('Timer', () => {
let timer;
@ -38,21 +41,21 @@ test.describe('Timer', () => {
const timerUrl = timer.url;
await test.step("From the tree context menu", async () => {
await test.step('From the tree context menu', async () => {
await triggerTimerContextMenuAction(page, timerUrl, 'Start');
await triggerTimerContextMenuAction(page, timerUrl, 'Pause');
await triggerTimerContextMenuAction(page, timerUrl, 'Restart at 0');
await triggerTimerContextMenuAction(page, timerUrl, 'Stop');
});
await test.step("From the 3dot menu", async () => {
await test.step('From the 3dot menu', async () => {
await triggerTimer3dotMenuAction(page, 'Start');
await triggerTimer3dotMenuAction(page, 'Pause');
await triggerTimer3dotMenuAction(page, 'Restart at 0');
await triggerTimer3dotMenuAction(page, 'Stop');
});
await test.step("From the object view", async () => {
await test.step('From the object view', async () => {
await triggerTimerViewAction(page, 'Start');
await triggerTimerViewAction(page, 'Pause');
await triggerTimerViewAction(page, 'Restart at 0');
@ -111,7 +114,7 @@ async function triggerTimer3dotMenuAction(page, action) {
* @param {TimerViewAction} action
*/
async function triggerTimerViewAction(page, action) {
await page.locator('.c-timer').hover({trial: true});
await page.locator('.c-timer').hover({ trial: true });
const buttonTitle = buttonTitleFromAction(action);
await page.click(`button[title="${buttonTitle}"]`);
assertTimerStateAfterAction(page, action);
@ -142,7 +145,7 @@ async function assertTimerStateAfterAction(page, action) {
switch (action) {
case 'Start':
case 'Restart at 0':
timerStateClass = "is-started";
timerStateClass = 'is-started';
break;
case 'Stop':
timerStateClass = 'is-stopped';

View File

@ -49,14 +49,19 @@ test.describe('Recent Objects', () => {
});
// Drag the Recent Objects panel up a bit
await page.locator('.l-pane.l-pane--vertical-handle-before', {
await page
.locator('.l-pane.l-pane--vertical-handle-before', {
hasText: 'Recently Viewed'
}).locator('.l-pane__handle').hover();
})
.locator('.l-pane__handle')
.hover();
await page.mouse.down();
await page.mouse.move(0, 100);
await page.mouse.up();
});
test('Navigated objects show up in recents, object renames and deletions are reflected', async ({ page }) => {
test('Navigated objects show up in recents, object renames and deletions are reflected', async ({
page
}) => {
// Verify that both created objects appear in the list and are in the correct order
await assertInitialRecentObjectsListState();
@ -67,22 +72,31 @@ test.describe('Recent Objects', () => {
// Rename
folderA.name = `${folderA.name}-NEW!`;
await page.locator('.l-browse-bar__object-name').fill("");
await page.locator('.l-browse-bar__object-name').fill('');
await page.locator('.l-browse-bar__object-name').fill(folderA.name);
await page.keyboard.press('Enter');
// Verify rename has been applied in recent objects list item and objects paths
expect(await page.getByRole('navigation', {
expect(
await page
.getByRole('navigation', {
name: clock.name
}).locator('a').filter({
})
.locator('a')
.filter({
hasText: folderA.name
}).count()).toBeGreaterThan(0);
})
.count()
).toBeGreaterThan(0);
expect(recentObjectsList.getByRole('listitem', { name: folderA.name })).toBeTruthy();
// Delete
await page.click('button[title="Show selected item in tree"]');
// Delete the folder via the left tree pane treeitem context menu
await page.getByRole('treeitem', { name: new RegExp(folderA.name) }).locator('a').click({
await page
.getByRole('treeitem', { name: new RegExp(folderA.name) })
.locator('a')
.click({
button: 'right'
});
await page.getByRole('menuitem', { name: /Remove/ }).click();
@ -92,7 +106,10 @@ test.describe('Recent Objects', () => {
await expect(recentObjectsList.getByRole('listitem', { name: folderA.name })).toBeHidden();
await expect(recentObjectsList.getByRole('listitem', { name: clock.name })).toBeHidden();
});
test("Clicking on an object in the path of a recent object navigates to the object", async ({ page, openmctConfig }) => {
test('Clicking on an object in the path of a recent object navigates to the object', async ({
page,
openmctConfig
}) => {
const { myItemsFolderName } = openmctConfig;
test.info().annotations.push({
type: 'issue',
@ -102,11 +119,15 @@ test.describe('Recent Objects', () => {
// Navigate to the folder by clicking on its entry in the Clock's breadcrumb
const waitForFolderNavigation = page.waitForURL(`**/${folderA.uuid}?*`);
await page.getByRole('navigation', {
await page
.getByRole('navigation', {
name: clock.name
}).locator('a').filter({
})
.locator('a')
.filter({
hasText: folderA.name
}).click();
})
.click();
// Verify that the hash URL updates correctly
await waitForFolderNavigation;
@ -114,26 +135,33 @@ test.describe('Recent Objects', () => {
// Navigate to My Items by clicking on its entry in the Clock's breadcrumb
const waitForMyItemsNavigation = page.waitForURL(`**/mine?*`);
await page.getByRole('navigation', {
await page
.getByRole('navigation', {
name: clock.name
}).locator('a').filter({
})
.locator('a')
.filter({
hasText: myItemsFolderName
}).click();
})
.click();
// Verify that the hash URL updates correctly
await waitForMyItemsNavigation;
expect(page.url()).toMatch(new RegExp(`.*mine?.*`));
});
test("Clicking on the 'target button' scrolls the object into view in the tree and highlights it", async ({ page }) => {
const clockTreeItem = page.getByRole('tree', { name: 'Main Tree'}).getByRole('treeitem', { name: clock.name });
const folderTreeItem = page.getByRole('tree', { name: 'Main Tree'})
.getByRole('treeitem', {
test("Clicking on the 'target button' scrolls the object into view in the tree and highlights it", async ({
page
}) => {
const clockTreeItem = page
.getByRole('tree', { name: 'Main Tree' })
.getByRole('treeitem', { name: clock.name });
const folderTreeItem = page.getByRole('tree', { name: 'Main Tree' }).getByRole('treeitem', {
name: folderA.name,
expanded: true
});
// Click the "Target" button for the Clock which is nested in a folder
await page.getByRole('button', { name: `Open and scroll to ${clock.name}`}).click();
await page.getByRole('button', { name: `Open and scroll to ${clock.name}` }).click();
// Assert that the Clock parent folder has expanded and the Clock is visible)
await expect(folderTreeItem.locator('.c-disclosure-triangle')).toHaveClass(/--expanded/);
@ -148,50 +176,65 @@ test.describe('Recent Objects', () => {
// Assert that the Clock treeitem is no longer highlighted
await expect(clockTreeItem.locator('.c-tree__item')).not.toHaveClass(/is-targeted-item/);
});
test("Persists on refresh", async ({ page }) => {
test('Persists on refresh', async ({ page }) => {
await assertInitialRecentObjectsListState();
await page.reload();
await assertInitialRecentObjectsListState();
});
test("Displays objects and aliases uniquely", async ({ page }) => {
const mainTree = page.getByRole('tree', { name: 'Main Tree'});
test('Displays objects and aliases uniquely', async ({ page }) => {
const mainTree = page.getByRole('tree', { name: 'Main Tree' });
// Navigate to the clock and reveal it in the tree
await page.goto(clock.url);
await page.getByTitle('Show selected item in tree').click();
// Right click the clock and create an alias using the "link" context menu action
const clockTreeItem = page.getByRole('tree', {
const clockTreeItem = page
.getByRole('tree', {
name: 'Main Tree'
}).getByRole('treeitem', {
})
.getByRole('treeitem', {
name: clock.name
});
await clockTreeItem.click({
button: 'right'
});
await page.getByRole('menuitem', {
await page
.getByRole('menuitem', {
name: /Create Link/
}).click();
await page.getByRole('tree', { name: 'Create Modal Tree'}).getByRole('treeitem').first().click();
})
.click();
await page
.getByRole('tree', { name: 'Create Modal Tree' })
.getByRole('treeitem')
.first()
.click();
await page.getByRole('button', { name: 'Save' }).click();
// Click the newly created object alias in the tree
await mainTree.getByRole('treeitem', {
await mainTree
.getByRole('treeitem', {
name: new RegExp(clock.name)
}).filter({
})
.filter({
has: page.locator('.is-alias')
}).click();
})
.click();
// Assert that two recent objects are displayed and one of them is an alias
expect(await recentObjectsList.getByRole('listitem', { name: clock.name }).count()).toBe(2);
expect(await recentObjectsList.locator('.is-alias').count()).toBe(1);
// Assert that the alias and the original's breadcrumbs are different
const clockBreadcrumbs = recentObjectsList.getByRole('listitem', {name: clock.name}).getByRole('navigation');
const clockBreadcrumbs = recentObjectsList
.getByRole('listitem', { name: clock.name })
.getByRole('navigation');
expect(await clockBreadcrumbs.count()).toBe(2);
expect(await clockBreadcrumbs.nth(0).innerText()).not.toEqual(await clockBreadcrumbs.nth(1).innerText());
expect(await clockBreadcrumbs.nth(0).innerText()).not.toEqual(
await clockBreadcrumbs.nth(1).innerText()
);
});
test("Enforces a limit of 20 recent objects and clears the recent objects", async ({ page }) => {
test('Enforces a limit of 20 recent objects and clears the recent objects', async ({ page }) => {
// Creating 21 objects takes a while, so increase the timeout
test.slow();
@ -203,11 +246,11 @@ test.describe('Recent Objects', () => {
// Create 19 more objects (3 in beforeEach() + 18 new = 21 total)
for (let i = 0; i < 9; i++) {
lastFolder = await createDomainObjectWithDefaults(page, {
type: "Folder",
type: 'Folder',
parent: lastFolder?.uuid
});
lastClock = await createDomainObjectWithDefaults(page, {
type: "Clock",
type: 'Clock',
parent: lastFolder?.uuid
});
}
@ -216,19 +259,17 @@ test.describe('Recent Objects', () => {
expect(await recentObjectsList.locator('.c-recentobjects-listitem').count()).toBe(20);
// Collapse the tree
await page.getByTitle("Collapse all tree items").click();
const lastFolderTreeItem = page.getByRole('tree', { name: 'Main Tree'})
.getByRole('treeitem', {
await page.getByTitle('Collapse all tree items').click();
const lastFolderTreeItem = page.getByRole('tree', { name: 'Main Tree' }).getByRole('treeitem', {
name: lastFolder.name,
expanded: true
});
const lastClockTreeItem = page.getByRole('tree', { name: 'Main Tree'})
.getByRole('treeitem', {
const lastClockTreeItem = page.getByRole('tree', { name: 'Main Tree' }).getByRole('treeitem', {
name: lastClock.name
});
// Test "Open and Scroll To" in a deeply nested tree, while we're here
await page.getByRole('button', { name: `Open and scroll to ${lastClock.name}`}).click();
await page.getByRole('button', { name: `Open and scroll to ${lastClock.name}` }).click();
// Assert that the Clock parent folder has expanded and the Clock is visible)
await expect(lastFolderTreeItem.locator('.c-disclosure-triangle')).toHaveClass(/--expanded/);
@ -252,34 +293,28 @@ test.describe('Recent Objects', () => {
// Assert that the list is empty
expect(await recentObjectsList.locator('.c-recentobjects-listitem').count()).toBe(0);
});
test("Ensure clear recent objects button is active or inactive", async ({ page }) => {
test('Ensure clear recent objects button is active or inactive', async ({ page }) => {
// Assert that the list initially contains 3 objects (clock, folder, my items)
expect(await recentObjectsList.locator('.c-recentobjects-listitem').count()).toBe(3);
// Assert that the button is enabled
expect(
await page
.getByRole("button", { name: "Clear Recently Viewed" })
.isEnabled()
).toBe(true);
expect(await page.getByRole('button', { name: 'Clear Recently Viewed' }).isEnabled()).toBe(
true
);
// Click the aria-label="Clear Recently Viewed" button
await page.getByRole("button", { name: "Clear Recently Viewed" }).click();
await page.getByRole('button', { name: 'Clear Recently Viewed' }).click();
// Click on the "OK" button in the confirmation dialog
await page.getByRole("button", { name: "OK" }).click();
await page.getByRole('button', { name: 'OK' }).click();
// Assert that the list is empty
expect(
await recentObjectsList.locator(".c-recentobjects-listitem").count()
).toBe(0);
expect(await recentObjectsList.locator('.c-recentobjects-listitem').count()).toBe(0);
// Assert that the button is disabled
expect(
await page
.getByRole("button", { name: "Clear Recently Viewed" })
.isEnabled()
).toBe(false);
expect(await page.getByRole('button', { name: 'Clear Recently Viewed' }).isEnabled()).toBe(
false
);
// Navigate to folder object
await page.goto(folderA.url);
@ -288,20 +323,28 @@ test.describe('Recent Objects', () => {
expect(await recentObjectsList.locator('.c-recentobjects-listitem').count()).toBe(1);
// Assert that the button is enabled
expect(
await page
.getByRole("button", { name: "Clear Recently Viewed" })
.isEnabled()
).toBe(true);
expect(await page.getByRole('button', { name: 'Clear Recently Viewed' }).isEnabled()).toBe(
true
);
});
function assertInitialRecentObjectsListState() {
return Promise.all([
expect(recentObjectsList.getByRole('listitem', { name: clock.name })).toBeVisible(),
expect(recentObjectsList.getByRole('listitem', { name: folderA.name })).toBeVisible(),
expect(recentObjectsList.getByRole('listitem', { name: clock.name }).locator('a').getByText(folderA.name)).toBeVisible(),
expect(
recentObjectsList
.getByRole('listitem', { name: clock.name })
.locator('a')
.getByText(folderA.name)
).toBeVisible(),
expect(recentObjectsList.getByRole('listitem').nth(0).getByText(clock.name)).toBeVisible(),
expect(recentObjectsList.getByRole('listitem', { name: clock.name }).locator('a').getByText(folderA.name)).toBeVisible(),
expect(
recentObjectsList
.getByRole('listitem', { name: clock.name })
.locator('a')
.getByText(folderA.name)
).toBeVisible(),
expect(recentObjectsList.getByRole('listitem').nth(3).getByText(folderA.name)).toBeVisible()
]);
}

View File

@ -33,10 +33,13 @@ test.describe('Grand Search', () => {
test.beforeEach(async ({ page }) => {
// Go to baseURL
await page.goto("./", { waitUntil: "networkidle" });
await page.goto('./', { waitUntil: 'networkidle' });
});
test('Can search for objects, and subsequent search dropdown behaves properly', async ({ page, openmctConfig }) => {
test('Can search for objects, and subsequent search dropdown behaves properly', async ({
page,
openmctConfig
}) => {
const { myItemsFolderName } = openmctConfig;
const createdObjects = await createObjectsForSearch(page);
@ -45,10 +48,18 @@ test.describe('Grand Search', () => {
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
// Fill [aria-label="OpenMCT Search"] input[type="search"]
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Cl');
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toContainText(`Clock A ${myItemsFolderName} Red Folder Blue Folder`);
await expect(page.locator('[aria-label="Search Result"] >> nth=1')).toContainText(`Clock B ${myItemsFolderName} Red Folder Blue Folder`);
await expect(page.locator('[aria-label="Search Result"] >> nth=2')).toContainText(`Clock C ${myItemsFolderName} Red Folder Blue Folder`);
await expect(page.locator('[aria-label="Search Result"] >> nth=3')).toContainText(`Clock D ${myItemsFolderName} Red Folder Blue Folder`);
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toContainText(
`Clock A ${myItemsFolderName} Red Folder Blue Folder`
);
await expect(page.locator('[aria-label="Search Result"] >> nth=1')).toContainText(
`Clock B ${myItemsFolderName} Red Folder Blue Folder`
);
await expect(page.locator('[aria-label="Search Result"] >> nth=2')).toContainText(
`Clock C ${myItemsFolderName} Red Folder Blue Folder`
);
await expect(page.locator('[aria-label="Search Result"] >> nth=3')).toContainText(
`Clock D ${myItemsFolderName} Red Folder Blue Folder`
);
// Click the Elements pool to dismiss the search menu
await selectInspectorTab(page, 'Elements');
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toBeHidden();
@ -60,7 +71,9 @@ test.describe('Grand Search', () => {
// Click [aria-label="Close"]
await page.locator('[aria-label="Close"]').click();
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toBeVisible();
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toContainText(`Clock A ${myItemsFolderName} Red Folder Blue Folder`);
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toContainText(
`Clock A ${myItemsFolderName} Red Folder Blue Folder`
);
// Click [aria-label="OpenMCT Search"] a >> nth=0
await page.locator('[aria-label="Search Result"] >> nth=0').click();
@ -71,7 +84,10 @@ test.describe('Grand Search', () => {
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toBeHidden();
// 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();
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 [aria-label="OpenMCT Search"] [aria-label="Search Input"]
@ -85,22 +101,34 @@ test.describe('Grand Search', () => {
await expect(page.locator('.is-object-type-clock')).toBeVisible();
await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').fill('Disp');
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toContainText(createdObjects.displayLayout.name);
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toContainText(
createdObjects.displayLayout.name
);
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).not.toContainText('Folder');
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Clock C');
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toContainText(`Clock C ${myItemsFolderName} Red Folder Blue Folder`);
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toContainText(
`Clock C ${myItemsFolderName} Red Folder Blue Folder`
);
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Cloc');
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toContainText(`Clock A ${myItemsFolderName} Red Folder Blue Folder`);
await expect(page.locator('[aria-label="Search Result"] >> nth=1')).toContainText(`Clock B ${myItemsFolderName} Red Folder Blue Folder`);
await expect(page.locator('[aria-label="Search Result"] >> nth=2')).toContainText(`Clock C ${myItemsFolderName} Red Folder Blue Folder`);
await expect(page.locator('[aria-label="Search Result"] >> nth=3')).toContainText(`Clock D ${myItemsFolderName} Red Folder Blue Folder`);
await expect(page.locator('[aria-label="Search Result"] >> nth=0')).toContainText(
`Clock A ${myItemsFolderName} Red Folder Blue Folder`
);
await expect(page.locator('[aria-label="Search Result"] >> nth=1')).toContainText(
`Clock B ${myItemsFolderName} Red Folder Blue Folder`
);
await expect(page.locator('[aria-label="Search Result"] >> nth=2')).toContainText(
`Clock C ${myItemsFolderName} Red Folder Blue Folder`
);
await expect(page.locator('[aria-label="Search Result"] >> nth=3')).toContainText(
`Clock D ${myItemsFolderName} Red Folder Blue Folder`
);
});
test('Validate empty search result', async ({ page }) => {
// Invalid search for objects
await page.type("input[type=search]", 'not found');
await page.type('input[type=search]', 'not found');
// Wait for search to complete
await waitForSearchCompletion(page);
@ -124,7 +152,7 @@ test.describe('Grand Search', () => {
});
// Full search for object
await page.type("input[type=search]", folderName);
await page.type('input[type=search]', folderName);
// Wait for search to complete
await waitForSearchCompletion(page);
@ -155,7 +183,7 @@ test.describe('Grand Search', () => {
});
// Full search for object
await page.type("input[type=search]", 'Clock', { delay: 100 });
await page.type('input[type=search]', 'Clock', { delay: 100 });
// Wait for search to finish
await waitForSearchCompletion(page);
@ -169,15 +197,15 @@ test.describe('Grand Search', () => {
await expect(searchResultDropDown).toContainText('Clock A');
});
test("Validate multiple objects in search results return partial matches", async ({ page }) => {
test('Validate multiple objects in search results return partial matches', async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/4667'
});
// Create folder objects
const folderName1 = "e928a26e-e924-4ea0";
const folderName2 = "e928a26e-e924-4001";
const folderName1 = 'e928a26e-e924-4ea0';
const folderName2 = 'e928a26e-e924-4001';
await createDomainObjectWithDefaults(page, {
type: 'Folder',
@ -190,7 +218,7 @@ test.describe('Grand Search', () => {
});
// Partial search for objects
await page.type("input[type=search]", 'e928a26e');
await page.type('input[type=search]', 'e928a26e');
// Wait for search to finish
await waitForSearchCompletion(page);

View File

@ -35,8 +35,9 @@ Make no assumptions about the order that elements appear in the DOM.
const { test, expect } = require('../../pluginFixtures');
test('Verify that the create button appears and that the Folder Domain Object is available for selection', async ({ page }) => {
test('Verify that the create button appears and that the Folder Domain Object is available for selection', async ({
page
}) => {
//Go to baseURL
await page.goto('./', { waitUntil: 'domcontentloaded' });

View File

@ -31,7 +31,9 @@ test.describe('Main Tree', () => {
await page.goto('./', { waitUntil: 'domcontentloaded' });
});
test('Creating a child object within a folder and immediately opening it shows the created object in the tree @couchdb', async ({ page }) => {
test('Creating a child object within a folder and immediately opening it shows the created object in the tree @couchdb', async ({
page
}) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/5975'
@ -52,7 +54,10 @@ test.describe('Main Tree', () => {
await assertTreeItemIsVisible(page, clock.name);
});
test('Creating a child object on one tab and expanding its parent on the other shows the correct composition @2p', async ({ page, openmctConfig }) => {
test('Creating a child object on one tab and expanding its parent on the other shows the correct composition @2p', async ({
page,
openmctConfig
}) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/6391'
@ -75,7 +80,10 @@ test.describe('Main Tree', () => {
await assertTreeItemIsVisible(page2, page1Folder.name);
});
test('Creating a child object on one tab and expanding its parent on the other shows the correct composition @couchdb @2p', async ({ page, openmctConfig }) => {
test('Creating a child object on one tab and expanding its parent on the other shows the correct composition @couchdb @2p', async ({
page,
openmctConfig
}) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/6391'
@ -129,13 +137,13 @@ test.describe('Main Tree', () => {
// Expand the root folder
await expandTreePaneItemByName(page, myItemsFolderName);
await test.step("Reorders objects with the same tree depth", async () => {
await test.step('Reorders objects with the same tree depth', async () => {
await getAndAssertTreeItems(page, ['aaa', 'Bar', 'Baz', 'Foo', 'www']);
await renameObjectFromContextMenu(page, clock1.url, 'zzz');
await getAndAssertTreeItems(page, ['Bar', 'Baz', 'Foo', 'www', 'zzz']);
});
await test.step("Reorders links to objects as well as original objects", async () => {
await test.step('Reorders links to objects as well as original objects', async () => {
await page.click('role=treeitem[name=/Bar/]');
await page.dragAndDrop('role=treeitem[name=/www/]', '.c-object-view');
await page.dragAndDrop('role=treeitem[name=/zzz/]', '.c-object-view');
@ -151,19 +159,18 @@ test.describe('Main Tree', () => {
await expandTreePaneItemByName(page, 'Foo');
await renameObjectFromContextMenu(page, clock1.url, '___');
await getAndAssertTreeItems(page,
[
"___",
"Bar",
"___",
"www",
"Baz",
"___",
"www",
"Foo",
"___",
"www",
"www"
await getAndAssertTreeItems(page, [
'___',
'Bar',
'___',
'www',
'Baz',
'___',
'www',
'Foo',
'___',
'www',
'www'
]);
});
});
@ -217,7 +224,7 @@ async function renameObjectFromContextMenu(page, url, newName) {
await openObjectTreeContextMenu(page, url);
await page.click('li:text("Edit Properties")');
const nameInput = page.locator('form[name="mctForm"] .first input[type="text"]');
await nameInput.fill("");
await nameInput.fill('');
await nameInput.fill(newName);
await page.click('[aria-label="Save"]');
}

View File

@ -55,39 +55,50 @@ test.describe('Performance tests', () => {
// Click text=OK
await page.locator('button:has-text("OK")').click();
await expect(page.locator('a:has-text("Performance Display Layout Display Layout")')).toBeVisible();
await expect(
page.locator('a:has-text("Performance Display Layout Display Layout")')
).toBeVisible();
//Create a Chrome Performance Timeline trace to store as a test artifact
console.log("\n==== Devtools: startTracing ====\n");
console.log('\n==== Devtools: startTracing ====\n');
await browser.startTracing(page, {
path: `${testInfo.outputPath()}-trace.json`,
screenshots: true
});
});
test.afterEach(async ({ page, browser}) => {
console.log("\n==== Devtools: stopTracing ====\n");
test.afterEach(async ({ page, browser }) => {
console.log('\n==== Devtools: stopTracing ====\n');
await browser.stopTracing();
/* Measurement Section
/ The following section includes a block of performance measurements.
*/
//Get time difference between viewlarge actionability and evaluate time
await page.evaluate(() => (window.performance.measure("machine-time-difference", "viewlarge.start", "viewLarge.start.test")));
await page.evaluate(() =>
window.performance.measure(
'machine-time-difference',
'viewlarge.start',
'viewLarge.start.test'
)
);
//Get StartTime
const startTime = await page.evaluate(() => window.performance.timing.navigationStart);
console.log('window.performance.timing.navigationStart', startTime);
//Get All Performance Marks
const getAllMarksJson = await page.evaluate(() => JSON.stringify(window.performance.getEntriesByType("mark")));
const getAllMarksJson = await page.evaluate(() =>
JSON.stringify(window.performance.getEntriesByType('mark'))
);
const getAllMarks = JSON.parse(getAllMarksJson);
console.log('window.performance.getEntriesByType("mark")', getAllMarks);
//Get All Performance Measures
const getAllMeasuresJson = await page.evaluate(() => JSON.stringify(window.performance.getEntriesByType("measure")));
const getAllMeasuresJson = await page.evaluate(() =>
JSON.stringify(window.performance.getEntriesByType('measure'))
);
const getAllMeasures = JSON.parse(getAllMeasuresJson);
console.log('window.performance.getEntriesByType("measure")', getAllMeasures);
});
/* The following test will navigate to a previously created Performance Display Layout and measure the
/ following metrics:
@ -104,48 +115,53 @@ test.describe('Performance tests', () => {
// Search Available after Launch
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
await page.evaluate(() => window.performance.mark("search-available"));
await page.evaluate(() => window.performance.mark('search-available'));
// Fill Search input
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Performance Display Layout');
await page.evaluate(() => window.performance.mark("search-entered"));
await page
.locator('[aria-label="OpenMCT Search"] input[type="search"]')
.fill('Performance Display Layout');
await page.evaluate(() => window.performance.mark('search-entered'));
//Search Result Appears and is clicked
await Promise.all([
page.waitForNavigation(),
page.locator('a:has-text("Performance Display Layout")').first().click(),
page.evaluate(() => window.performance.mark("click-search-result"))
page.evaluate(() => window.performance.mark('click-search-result'))
]);
//Time to Example Imagery Frame loads within Display Layout
await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible'});
await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible' });
//Time to Example Imagery object loads
await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible'});
await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible' });
//Get background-image url from background-image css prop
const backgroundImage = await page.locator('.c-imagery__main-image__background-image');
let backgroundImageUrl = await backgroundImage.evaluate((el) => {
return window.getComputedStyle(el).getPropertyValue('background-image').match(/url\(([^)]+)\)/)[1];
return window
.getComputedStyle(el)
.getPropertyValue('background-image')
.match(/url\(([^)]+)\)/)[1];
});
backgroundImageUrl = backgroundImageUrl.slice(1, -1); //forgive me, padre
console.log('backgroundImageurl ' + backgroundImageUrl);
//Get ResourceTiming of background-image jpg
const resourceTimingJson = await page.evaluate((bgImageUrl) =>
JSON.stringify(window.performance.getEntriesByName(bgImageUrl).pop()),
const resourceTimingJson = await page.evaluate(
(bgImageUrl) => JSON.stringify(window.performance.getEntriesByName(bgImageUrl).pop()),
backgroundImageUrl
);
console.log('resourceTimingJson ' + resourceTimingJson);
//Open Large view
await page.locator('button:has-text("Large View")').click(); //This action includes the performance.mark named 'viewLarge.start'
await page.evaluate(() => window.performance.mark("viewLarge.start.test")); //This is a mark only to compare evaluate timing
await page.evaluate(() => window.performance.mark('viewLarge.start.test')); //This is a mark only to compare evaluate timing
//Time to Imagery Rendered in Large Frame
await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible'});
await page.evaluate(() => window.performance.mark("background-image-frame"));
await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible' });
await page.evaluate(() => window.performance.mark('background-image-frame'));
//Time to Example Imagery object loads
await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible'});
await page.evaluate(() => window.performance.mark("background-image-visible"));
await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible' });
await page.evaluate(() => window.performance.mark('background-image-visible'));
// Get Current number of images in thumbstrip
await page.waitForSelector('.c-imagery__thumb');
@ -158,20 +174,17 @@ test.describe('Performance tests', () => {
JSON.stringify(window.performance.getEntriesByType('resource'))
);
const resourceTiming = JSON.parse(resourceTimingJson2);
const jpgResourceTiming = resourceTiming.find((element) =>
element.name.includes('.jpg')
);
const jpgResourceTiming = resourceTiming.find((element) => element.name.includes('.jpg'));
console.log('jpgResourceTiming ' + JSON.stringify(jpgResourceTiming));
// Click Close Icon
await page.locator('[aria-label="Close"]').click();
await page.evaluate(() => window.performance.mark("view-large-close-button"));
await page.evaluate(() => window.performance.mark('view-large-close-button'));
//await client.send('HeapProfiler.enable');
await client.send('HeapProfiler.collectGarbage');
let performanceMetrics = await client.send('Performance.getMetrics');
console.log(performanceMetrics.metrics);
});
});

View File

@ -56,17 +56,20 @@ test.describe.skip('Memory Performance tests', () => {
// Click text=OK
await page.locator('text=OK').click();
await expect(page.locator('a:has-text("Performance Display Layout Display Layout")')).toBeVisible();
await expect(
page.locator('a:has-text("Performance Display Layout Display Layout")')
).toBeVisible();
});
test('Embedded View Large for Imagery is performant in Fixed Time', async ({ page, browser }) => {
await page.goto('./', {waitUntil: 'networkidle'});
await page.goto('./', { waitUntil: 'networkidle' });
// To to Search Available after Launch
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
// Fill Search input
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Performance Display Layout');
await page
.locator('[aria-label="OpenMCT Search"] input[type="search"]')
.fill('Performance Display Layout');
//Search Result Appears and is clicked
await Promise.all([
page.waitForNavigation(),
@ -74,9 +77,9 @@ test.describe.skip('Memory Performance tests', () => {
]);
//Time to Example Imagery Frame loads within Display Layout
await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible'});
await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible' });
//Time to Example Imagery object loads
await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible'});
await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible' });
const client = await page.context().newCDPSession(page);
await client.send('HeapProfiler.enable');
@ -94,18 +97,18 @@ test.describe.skip('Memory Performance tests', () => {
await client.send('HeapProfiler.takeHeapSnapshot');
//Time to Imagery Rendered in Large Frame
await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible'});
await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible' });
//Time to Example Imagery object loads
await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible'});
await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible' });
// Click Close Icon
await page.locator('.c-click-icon').click();
//Time to Example Imagery Frame loads within Display Layout
await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible'});
await page.waitForSelector('.c-imagery__main-image__bg', { state: 'visible' });
//Time to Example Imagery object loads
await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible'});
await page.waitForSelector('.c-imagery__main-image__background-image', { state: 'visible' });
await client.send('HeapProfiler.collectGarbage');
//await client.send('Performance.enable');
@ -114,6 +117,5 @@ test.describe.skip('Memory Performance tests', () => {
console.log(performanceMetricsAfter.metrics);
//await client.send('Performance.disable');
});
});

View File

@ -57,14 +57,14 @@ test.describe('Performance tests', () => {
await expect(page.locator('a:has-text("Performance Notebook")')).toBeVisible();
//Create a Chrome Performance Timeline trace to store as a test artifact
console.log("\n==== Devtools: startTracing ====\n");
console.log('\n==== Devtools: startTracing ====\n');
await browser.startTracing(page, {
path: `${testInfo.outputPath()}-trace.json`,
screenshots: true
});
});
test.afterEach(async ({ page, browser}) => {
console.log("\n==== Devtools: stopTracing ====\n");
test.afterEach(async ({ page, browser }) => {
console.log('\n==== Devtools: stopTracing ====\n');
await browser.stopTracing();
/* Measurement Section
@ -74,15 +74,18 @@ test.describe('Performance tests', () => {
console.log('window.performance.timing.navigationStart', startTime);
//Get All Performance Marks
const getAllMarksJson = await page.evaluate(() => JSON.stringify(window.performance.getEntriesByType("mark")));
const getAllMarksJson = await page.evaluate(() =>
JSON.stringify(window.performance.getEntriesByType('mark'))
);
const getAllMarks = JSON.parse(getAllMarksJson);
console.log('window.performance.getEntriesByType("mark")', getAllMarks);
//Get All Performance Measures
const getAllMeasuresJson = await page.evaluate(() => JSON.stringify(window.performance.getEntriesByType("measure")));
const getAllMeasuresJson = await page.evaluate(() =>
JSON.stringify(window.performance.getEntriesByType('measure'))
);
const getAllMeasures = JSON.parse(getAllMeasuresJson);
console.log('window.performance.getEntriesByType("measure")', getAllMeasures);
});
/* The following test will navigate to a previously created Performance Display Layout and measure the
/ following metrics:
@ -99,56 +102,60 @@ test.describe('Performance tests', () => {
// To to Search Available after Launch
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
await page.evaluate(() => window.performance.mark("search-available"));
await page.evaluate(() => window.performance.mark('search-available'));
// Fill Search input
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Performance Notebook');
await page.evaluate(() => window.performance.mark("search-entered"));
await page
.locator('[aria-label="OpenMCT Search"] input[type="search"]')
.fill('Performance Notebook');
await page.evaluate(() => window.performance.mark('search-entered'));
//Search Result Appears and is clicked
await Promise.all([
page.waitForNavigation(),
page.locator('a:has-text("Performance Notebook")').first().click(),
page.evaluate(() => window.performance.mark("click-search-result"))
page.evaluate(() => window.performance.mark('click-search-result'))
]);
await page.waitForSelector('.c-tree__item c-tree-and-search__loading loading', {state: 'hidden'});
await page.evaluate(() => window.performance.mark("search-spinner-gone"));
await page.waitForSelector('.c-tree__item c-tree-and-search__loading loading', {
state: 'hidden'
});
await page.evaluate(() => window.performance.mark('search-spinner-gone'));
await page.waitForSelector('.l-browse-bar__object-name', { state: 'visible'});
await page.evaluate(() => window.performance.mark("object-title-appears"));
await page.waitForSelector('.l-browse-bar__object-name', { state: 'visible' });
await page.evaluate(() => window.performance.mark('object-title-appears'));
await page.waitForSelector('.c-notebook__entry >> nth=0', { state: 'visible'});
await page.evaluate(() => window.performance.mark("notebook-entry-appears"));
await page.waitForSelector('.c-notebook__entry >> nth=0', { state: 'visible' });
await page.evaluate(() => window.performance.mark('notebook-entry-appears'));
// Click Add new Notebook Entry
await page.locator('.c-notebook__drag-area').click();
await page.evaluate(() => window.performance.mark("new-notebook-entry-created"));
await page.evaluate(() => window.performance.mark('new-notebook-entry-created'));
// Enter Notebook Entry text
await page.locator('div.c-ne__text').last().fill('New Entry');
await page.keyboard.press('Enter');
await page.evaluate(() => window.performance.mark("new-notebook-entry-filled"));
await page.evaluate(() => window.performance.mark('new-notebook-entry-filled'));
//Individual Notebook Entry Search
await page.evaluate(() => window.performance.mark("notebook-search-start"));
await page.evaluate(() => window.performance.mark('notebook-search-start'));
await page.locator('.c-notebook__search >> input').fill('Existing Entry');
await page.evaluate(() => window.performance.mark("notebook-search-filled"));
await page.waitForSelector('text=Search Results (3)', { state: 'visible'});
await page.evaluate(() => window.performance.mark("notebook-search-processed"));
await page.waitForSelector('.c-notebook__entry >> nth=2', { state: 'visible'});
await page.evaluate(() => window.performance.mark("notebook-search-processed"));
await page.evaluate(() => window.performance.mark('notebook-search-filled'));
await page.waitForSelector('text=Search Results (3)', { state: 'visible' });
await page.evaluate(() => window.performance.mark('notebook-search-processed'));
await page.waitForSelector('.c-notebook__entry >> nth=2', { state: 'visible' });
await page.evaluate(() => window.performance.mark('notebook-search-processed'));
//Clear Search
await page.locator('.c-search.c-notebook__search .c-search__input').hover();
await page.locator('.c-search.c-notebook__search .c-search__clear-input').click();
await page.evaluate(() => window.performance.mark("notebook-search-processed"));
await page.evaluate(() => window.performance.mark('notebook-search-processed'));
// Hover on Last
await page.evaluate(() => window.performance.mark("new-notebook-entry-delete"));
await page.evaluate(() => window.performance.mark('new-notebook-entry-delete'));
await page.locator('div.c-ne__time-and-content').last().hover();
await page.locator('button[title="Delete this entry"]').last().click();
await page.locator('button:has-text("Ok")').click();
await page.waitForSelector('.c-notebook__entry >> nth=3', { state: 'detached'});
await page.evaluate(() => window.performance.mark("new-notebook-entry-deleted"));
await page.waitForSelector('.c-notebook__entry >> nth=3', { state: 'detached' });
await page.evaluate(() => window.performance.mark('new-notebook-entry-deleted'));
//await client.send('HeapProfiler.enable');
await client.send('HeapProfiler.collectGarbage');

View File

@ -48,7 +48,9 @@ test.describe('Visual - addInit', () => {
});
test('Restricted Notebook is visually correct @addInit @unstable', async ({ page, theme }) => {
await page.addInitScript({ path: path.join(__dirname, '../../helper', './addInitRestrictedNotebook.js') });
await page.addInitScript({
path: path.join(__dirname, '../../helper', './addInitRestrictedNotebook.js')
});
//Go to baseURL
await page.goto('./#/browse/mine?hideTree=true', { waitUntil: 'networkidle' });
@ -56,6 +58,5 @@ test.describe('Visual - addInit', () => {
// Take a snapshot of the newly created CUSTOM_NAME notebook
await percySnapshot(page, `Restricted Notebook with CUSTOM_NAME (theme: '${theme}')`);
});
});

View File

@ -32,18 +32,18 @@ test.describe('Visual - Tree Pane', () => {
const foo = await createDomainObjectWithDefaults(page, {
type: 'Folder',
name: "Foo Folder"
name: 'Foo Folder'
});
const bar = await createDomainObjectWithDefaults(page, {
type: 'Folder',
name: "Bar Folder",
name: 'Bar Folder',
parent: foo.uuid
});
const baz = await createDomainObjectWithDefaults(page, {
type: 'Folder',
name: "Baz Folder",
name: 'Baz Folder',
parent: bar.uuid
});

View File

@ -48,7 +48,7 @@ test.describe('Visual - Controlled Clock @localStorage', () => {
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Overlay Plot');
//Wait for canvas to be rendered and stop animating
await page.locator('canvas >> nth=1').hover({trial: true});
await page.locator('canvas >> nth=1').hover({ trial: true });
//Take snapshot of Sine Wave Generator within Overlay Plot
await percySnapshot(page, `SineWaveInOverlayPlot (theme: '${theme}')`);

View File

@ -61,14 +61,17 @@ test.describe('Visual - Default', () => {
// Modify the Build information in 'about' to be consistent run-over-run
const versionInformationLocator = page.locator('ul.t-info.l-info.s-info').first();
await expect(versionInformationLocator).toBeEnabled();
await versionInformationLocator.evaluate(node => node.innerHTML = '<li>Version: visual-snapshot</li> <li>Build Date: Mon Nov 15 2021 08:07:51 GMT-0800 (Pacific Standard Time)</li> <li>Revision: 93049cdbc6c047697ca204893db9603b864b8c9f</li> <li>Branch: master</li>');
await versionInformationLocator.evaluate(
(node) =>
(node.innerHTML =
'<li>Version: visual-snapshot</li> <li>Build Date: Mon Nov 15 2021 08:07:51 GMT-0800 (Pacific Standard Time)</li> <li>Revision: 93049cdbc6c047697ca204893db9603b864b8c9f</li> <li>Branch: master</li>')
);
// Take a snapshot of the About modal
await percySnapshot(page, `About (theme: '${theme}')`);
});
test('Visual - Default Condition Set @unstable', async ({ page, theme }) => {
await createDomainObjectWithDefaults(page, { type: 'Condition Set' });
// Take a snapshot of the newly created Condition Set object
@ -102,17 +105,17 @@ test.describe('Visual - Default', () => {
// verify no error msg
await percySnapshot(page, `Default Time conductor (theme: '${theme}')`);
startDate = (year + 1) + startDate.substring(4);
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 percySnapshot(page, `Start time error (theme: '${theme}')`);
startDate = (year - 1) + startDate.substring(4);
startDate = year - 1 + startDate.substring(4);
await page.locator('input[type="text"]').first().fill(startDate.toString());
endDate = (year - 2) + endDate.substring(4);
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();
@ -145,7 +148,7 @@ test.describe('Visual - Default', () => {
//Wait until Save Banner is gone
await page.locator('.c-message-banner__close-button').click();
await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
await page.waitForSelector('.c-message-banner__message', { state: 'detached' });
await percySnapshot(page, `Banner message gone (theme: '${theme}')`);
});
@ -156,7 +159,6 @@ test.describe('Visual - Default', () => {
//Hover on Display Layout option.
await page.locator('text=Display Layout').hover();
await percySnapshot(page, `Display Layout Create Menu (theme: '${theme}')`);
});
test('Visual - Default Gauge is correct @unstable', async ({ page, theme }) => {

View File

@ -27,9 +27,10 @@ const percySnapshot = require('@percy/playwright');
const utils = require('../../helper/faultUtils');
test.describe('The Fault Management Plugin Visual Test', () => {
test('icon test', async ({ page, theme }) => {
await page.addInitScript({ path: path.join(__dirname, '../../helper/', 'addInitFaultManagementPlugin.js') });
await page.addInitScript({
path: path.join(__dirname, '../../helper/', 'addInitFaultManagementPlugin.js')
});
await page.goto('./', { waitUntil: 'networkidle' });
await percySnapshot(page, `Fault Management icon appears in tree (theme: '${theme}')`);
@ -43,7 +44,10 @@ test.describe('The Fault Management Plugin Visual Test', () => {
await utils.acknowledgeFault(page, 1);
await utils.changeViewTo(page, 'acknowledged');
await percySnapshot(page, `Acknowledged faults, have a checkmark on the fault icon and appear in the acknowldeged view (theme: '${theme}')`);
await percySnapshot(
page,
`Acknowledged faults, have a checkmark on the fault icon and appear in the acknowldeged view (theme: '${theme}')`
);
});
test('shelved faults', async ({ page, theme }) => {
@ -56,7 +60,10 @@ test.describe('The Fault Management Plugin Visual Test', () => {
await utils.openFaultRowMenu(page, 1);
await percySnapshot(page, `Shelved faults have a 3-dot menu with Unshelve option enabled (theme: '${theme}')`);
await percySnapshot(
page,
`Shelved faults have a 3-dot menu with Unshelve option enabled (theme: '${theme}')`
);
});
test('3-dot menu for fault', async ({ page, theme }) => {
@ -64,7 +71,10 @@ test.describe('The Fault Management Plugin Visual Test', () => {
await utils.openFaultRowMenu(page, 1);
await percySnapshot(page, `Faults have a 3-dot menu with Acknowledge, Shelve and Unshelve (Unshelve is disabled) options (theme: '${theme}')`);
await percySnapshot(
page,
`Faults have a 3-dot menu with Acknowledge, Shelve and Unshelve (Unshelve is disabled) options (theme: '${theme}')`
);
});
test('ability to acknowledge or shelve', async ({ page, theme }) => {
@ -72,6 +82,9 @@ test.describe('The Fault Management Plugin Visual Test', () => {
await utils.selectFaultItem(page, 1);
await percySnapshot(page, `Selected faults highlight the ability to Acknowledge or Shelve above the fault list (theme: '${theme}')`);
await percySnapshot(
page,
`Selected faults highlight the ability to Acknowledge or Shelve above the fault list (theme: '${theme}')`
);
});
});

View File

@ -54,7 +54,6 @@ test.describe('Visual - LAD Table', () => {
await page.getByRole('button', { name: 'Save' }).click();
});
test('Toggled column widths behave accordingly', async ({ page, theme }) => {
await page.goto(ladTable.url);
//Close panes for visual consistency
await page.getByTitle('Collapse Inspect Pane').click();
@ -62,13 +61,18 @@ test.describe('Visual - LAD Table', () => {
await expect(page.locator('button[title="Expand Columns"]')).toBeVisible();
await percySnapshot(page, `LAD Table w/ Sine Wave Generator columns autosized (theme: ${theme})`);
await percySnapshot(
page,
`LAD Table w/ Sine Wave Generator columns autosized (theme: ${theme})`
);
await page.locator('button[title="Expand Columns"]').click();
await expect(page.locator('button[title="Autosize Columns"]')).toBeVisible();
await percySnapshot(page, `LAD Table w/ Sine Wave Generator columns expanded (theme: ${theme})`);
await percySnapshot(
page,
`LAD Table w/ Sine Wave Generator columns expanded (theme: ${theme})`
);
});
});

View File

@ -32,12 +32,12 @@ test.describe('Visual - Notebook', () => {
// Create Notebook
const notebook = await createDomainObjectWithDefaults(page, {
type: 'Notebook',
name: "Embed Test Notebook"
name: 'Embed Test Notebook'
});
// Create Overlay Plot
await createDomainObjectWithDefaults(page, {
type: 'Overlay Plot',
name: "Dropped Overlay Plot"
name: 'Dropped Overlay Plot'
});
await expandTreePaneItemByName(page, myItemsFolderName);
@ -46,6 +46,5 @@ test.describe('Visual - Notebook', () => {
await page.dragAndDrop('role=treeitem[name=/Dropped Overlay Plot/]', '.c-notebook__drag-area');
await percySnapshot(page, `Notebook w/ dropped embed (theme: ${theme})`);
});
});

View File

@ -28,19 +28,24 @@ const { test, expect } = require('../../pluginFixtures');
const percySnapshot = require('@percy/playwright');
const { createDomainObjectWithDefaults } = require('../../appActions');
test.describe('Visual - Check Notification Info Banner of \'Save successful\'', () => {
test.describe("Visual - Check Notification Info Banner of 'Save successful'", () => {
test.beforeEach(async ({ page }) => {
// Go to baseURL and Hide Tree
await page.goto('./', { waitUntil: 'networkidle' });
});
test('Create a clock, click on \'Save successful\' banner and dismiss it', async ({ page, theme }) => {
test("Create a clock, click on 'Save successful' banner and dismiss it", async ({
page,
theme
}) => {
// Create a clock domain object
await createDomainObjectWithDefaults(page, { type: 'Clock' });
// Verify there is a button with aria-label="Review 1 Notification"
expect(await page.locator('button[aria-label="Review 1 Notification"]').isVisible()).toBe(true);
// Verify there is a button with aria-label="Clear all notifications"
expect(await page.locator('button[aria-label="Clear all notifications"]').isVisible()).toBe(true);
expect(await page.locator('button[aria-label="Clear all notifications"]').isVisible()).toBe(
true
);
// Click on the div with role="alert" that has "Save successful" text
await page.locator('div[role="alert"]:has-text("Save successful")').click();
// Verify there is a div with role="dialog"

View File

@ -41,7 +41,10 @@ test.describe('Grand Search', () => {
}
});
//This needs to be rewritten to use a non clock or non display layout object
test('Can search for objects, and subsequent search dropdown behaves properly @unstable', async ({ page, theme }) => {
test('Can search for objects, and subsequent search dropdown behaves properly @unstable', async ({
page,
theme
}) => {
// await createDomainObjectWithDefaults(page, 'Display Layout');
// await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
// await page.locator('text=Save and Finish Editing').click();
@ -60,12 +63,18 @@ test.describe('Grand Search', () => {
await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').click();
await page.locator('[aria-label="Unnamed Clock clock result"] >> text=Unnamed Clock').click();
await percySnapshot(page, 'Preview for clock should display when editing enabled and search item clicked');
await percySnapshot(
page,
'Preview for clock should display when editing enabled and search item clicked'
);
await page.locator('[aria-label="Close"]').click();
await percySnapshot(page, 'Search should still be showing after preview closed');
await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
await page
.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button')
.nth(1)
.click();
await page.locator('text=Save and Finish Editing').click();
@ -73,12 +82,10 @@ test.describe('Grand Search', () => {
await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').fill('Cl');
await Promise.all([
page.waitForNavigation(),
page.locator('text=Unnamed Clock').click()
]);
await percySnapshot(page, `Clicking on search results should navigate to them if not editing (theme: '${theme}')`);
await Promise.all([page.waitForNavigation(), page.locator('text=Unnamed Clock').click()]);
await percySnapshot(
page,
`Clicking on search results should navigate to them if not editing (theme: '${theme}')`
);
});
});

View File

@ -23,25 +23,25 @@
class EventMetadataProvider {
constructor() {
this.METADATA_BY_TYPE = {
'eventGenerator': {
eventGenerator: {
values: [
{
key: "name",
name: "Name",
format: "string"
key: 'name',
name: 'Name',
format: 'string'
},
{
key: "utc",
name: "Time",
format: "utc",
key: 'utc',
name: 'Time',
format: 'utc',
hints: {
domain: 1
}
},
{
key: "message",
name: "Message",
format: "string"
key: 'message',
name: 'Message',
format: 'string'
}
]
}
@ -53,11 +53,7 @@ class EventMetadataProvider {
}
getMetadata(domainObject) {
return Object.assign(
{},
domainObject.telemetry,
this.METADATA_BY_TYPE[domainObject.type]
);
return Object.assign({}, domainObject.telemetry, this.METADATA_BY_TYPE[domainObject.type]);
}
}

View File

@ -33,9 +33,9 @@ class EventTelemetryProvider {
generateData(firstObservedTime, count, startTime, duration, name) {
const millisecondsSinceStart = startTime - firstObservedTime;
const utc = startTime + (count * duration);
const utc = startTime + count * duration;
const ind = count % messages.length;
const message = messages[ind] + " - [" + millisecondsSinceStart + "]";
const message = messages[ind] + ' - [' + millisecondsSinceStart + ']';
return {
name,
@ -59,7 +59,13 @@ class EventTelemetryProvider {
const interval = setInterval(() => {
const startTime = Date.now();
const datum = this.generateData(firstObservedTime, count, startTime, duration, domainObject.name);
const datum = this.generateData(
firstObservedTime,
count,
startTime,
duration,
domainObject.name
);
count += 1;
callback(datum);
}, duration);
@ -84,7 +90,9 @@ class EventTelemetryProvider {
while (start <= end && data.length < size) {
const startTime = options.start + count;
data.push(this.generateData(firstObservedTime, count, startTime, duration, domainObject.name));
data.push(
this.generateData(firstObservedTime, count, startTime, duration, domainObject.name)
);
start += duration;
count += 1;
}

View File

@ -24,10 +24,11 @@ import EventMetadataProvider from './EventMetadataProvider';
export default function EventGeneratorPlugin(options) {
return function install(openmct) {
openmct.types.addType("eventGenerator", {
name: "Event Message Generator",
description: "For development use. Creates sample event message data that mimics a live data stream.",
cssClass: "icon-generator-events",
openmct.types.addType('eventGenerator', {
name: 'Event Message Generator',
description:
'For development use. Creates sample event message data that mimics a live data stream.',
cssClass: 'icon-generator-events',
creatable: true,
initialize: function (object) {
object.telemetry = {
@ -37,6 +38,5 @@ export default function EventGeneratorPlugin(options) {
});
openmct.telemetry.addProvider(new EventTelmetryProvider());
openmct.telemetry.addProvider(new EventMetadataProvider());
};
}

View File

@ -20,10 +20,7 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
import EventMessageGeneratorPlugin from './plugin.js';
import {
createOpenMct,
resetApplicationState
} from '../../src/utils/testing';
import { createOpenMct, resetApplicationState } from '../../src/utils/testing';
describe('the plugin', () => {
let openmct;
@ -52,7 +49,7 @@ describe('the plugin', () => {
});
describe('the plugin', () => {
it("supports subscription", (done) => {
it('supports subscription', (done) => {
const unsubscribe = openmct.telemetry.subscribe(mockDomainObject, (telemetry) => {
expect(telemetry).not.toEqual(null);
expect(telemetry.message).toContain('CC: Eagle, Houston');
@ -62,12 +59,12 @@ describe('the plugin', () => {
});
});
it("supports requests without start/end defined", async () => {
it('supports requests without start/end defined', async () => {
const telemetry = await openmct.telemetry.request(mockDomainObject);
expect(telemetry[0].message).toContain('CC: Eagle, Houston');
});
it("supports requests with arbitrary start time in the past", async () => {
it('supports requests with arbitrary start time in the past', async () => {
mockDomainObject.options.start = 100000000000; // Mar 03 1973
const telemetry = await openmct.telemetry.request(mockDomainObject);
expect(telemetry[0].message).toContain('CC: Eagle, Houston');

View File

@ -37,7 +37,7 @@ export default function exampleTagsPlugin(options) {
openmct.annotation.setNamespaceToSaveAnnotations(options?.namespaceToSaveAnnotations);
}
Object.keys(availableTags.tags).forEach(tagKey => {
Object.keys(availableTags.tags).forEach((tagKey) => {
const tagDefinition = availableTags.tags[tagKey];
openmct.annotation.defineTag(tagKey, tagDefinition);
});

View File

@ -24,41 +24,46 @@ import EventEmitter from 'EventEmitter';
import { v4 as uuid } from 'uuid';
import createExampleUser from './exampleUserCreator';
const STATUSES = [{
key: "NO_STATUS",
label: "Not set",
iconClass: "icon-question-mark",
iconClassPoll: "icon-status-poll-question-mark"
}, {
key: "GO",
label: "Go",
iconClass: "icon-check",
iconClassPoll: "icon-status-poll-question-mark",
statusClass: "s-status-ok",
statusBgColor: "#33cc33",
statusFgColor: "#000"
}, {
key: "MAYBE",
label: "Maybe",
iconClass: "icon-alert-triangle",
iconClassPoll: "icon-status-poll-question-mark",
statusClass: "s-status-warning",
statusBgColor: "#ffb66c",
statusFgColor: "#000"
}, {
key: "NO_GO",
label: "No go",
iconClass: "icon-circle-slash",
iconClassPoll: "icon-status-poll-question-mark",
statusClass: "s-status-error",
statusBgColor: "#9900cc",
statusFgColor: "#fff"
}];
const STATUSES = [
{
key: 'NO_STATUS',
label: 'Not set',
iconClass: 'icon-question-mark',
iconClassPoll: 'icon-status-poll-question-mark'
},
{
key: 'GO',
label: 'Go',
iconClass: 'icon-check',
iconClassPoll: 'icon-status-poll-question-mark',
statusClass: 's-status-ok',
statusBgColor: '#33cc33',
statusFgColor: '#000'
},
{
key: 'MAYBE',
label: 'Maybe',
iconClass: 'icon-alert-triangle',
iconClassPoll: 'icon-status-poll-question-mark',
statusClass: 's-status-warning',
statusBgColor: '#ffb66c',
statusFgColor: '#000'
},
{
key: 'NO_GO',
label: 'No go',
iconClass: 'icon-circle-slash',
iconClassPoll: 'icon-status-poll-question-mark',
statusClass: 's-status-error',
statusBgColor: '#9900cc',
statusFgColor: '#fff'
}
];
/**
* @implements {StatusUserProvider}
*/
export default class ExampleUserProvider extends EventEmitter {
constructor(openmct, {defaultStatusRole} = {defaultStatusRole: undefined}) {
constructor(openmct, { defaultStatusRole } = { defaultStatusRole: undefined }) {
super();
this.openmct = openmct;
@ -155,7 +160,7 @@ export default class ExampleUserProvider extends EventEmitter {
question: pollQuestion,
timestamp: Date.now()
};
this.emit("pollQuestionChange", this.pollQuestion);
this.emit('pollQuestionChange', this.pollQuestion);
return true;
}
@ -177,17 +182,17 @@ export default class ExampleUserProvider extends EventEmitter {
}
const formStructure = {
title: "Login",
title: 'Login',
sections: [
{
rows: [
{
key: "username",
control: "textfield",
name: "Username",
pattern: "\\S+",
key: 'username',
control: 'textfield',
name: 'Username',
pattern: '\\S+',
required: true,
cssClass: "l-input-lg",
cssClass: 'l-input-lg',
value: ''
}
]
@ -205,7 +210,8 @@ export default class ExampleUserProvider extends EventEmitter {
this.user = new this.ExampleUser(id, info.username, ['example-role']);
this.loggedIn = true;
},
() => { // user canceled, setting a default username
() => {
// user canceled, setting a default username
this.user = new this.ExampleUser(id, 'Pat', ['example-role']);
this.loggedIn = true;
}

View File

@ -22,10 +22,12 @@
import ExampleUserProvider from './ExampleUserProvider';
export default function ExampleUserPlugin({autoLoginUser, defaultStatusRole} = {
export default function ExampleUserPlugin(
{ autoLoginUser, defaultStatusRole } = {
autoLoginUser: 'guest',
defaultStatusRole: 'test-role'
}) {
}
) {
return function install(openmct) {
const userProvider = new ExampleUserProvider(openmct, {
defaultStatusRole

View File

@ -20,13 +20,10 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
import {
createOpenMct,
resetApplicationState
} from '../../src/utils/testing';
import { createOpenMct, resetApplicationState } from '../../src/utils/testing';
import ExampleUserProvider from './ExampleUserProvider';
describe("The Example User Plugin", () => {
describe('The Example User Plugin', () => {
let openmct;
beforeEach(() => {

View File

@ -20,12 +20,9 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
import {
createOpenMct,
resetApplicationState
} from '../../src/utils/testing';
import { createOpenMct, resetApplicationState } from '../../src/utils/testing';
describe("The Example Fault Source Plugin", () => {
describe('The Example Fault Source Plugin', () => {
let openmct;
beforeEach(() => {

View File

@ -43,11 +43,14 @@ const getRandom = {
}
};
function shelveFault(fault, opts = {
function shelveFault(
fault,
opts = {
shelved: true,
comment: '',
shelveDuration: 90000
}) {
}
) {
fault.shelved = true;
setTimeout(() => {

View File

@ -1,37 +1,32 @@
define([
'lodash'
], function (
_
) {
define(['lodash'], function (_) {
var METADATA_BY_TYPE = {
'generator': {
generator: {
values: [
{
key: "name",
name: "Name",
format: "string"
key: 'name',
name: 'Name',
format: 'string'
},
{
key: "utc",
name: "Time",
format: "utc",
key: 'utc',
name: 'Time',
format: 'utc',
hints: {
domain: 1
}
},
{
key: "yesterday",
name: "Yesterday",
format: "utc",
key: 'yesterday',
name: 'Yesterday',
format: 'utc',
hints: {
domain: 2
}
},
{
key: "wavelengths",
name: "Wavelength",
unit: "nm",
key: 'wavelengths',
name: 'Wavelength',
unit: 'nm',
format: 'string[]',
hints: {
range: 4
@ -48,26 +43,26 @@ define([
// }
// },
{
key: "sin",
name: "Sine",
unit: "Hz",
key: 'sin',
name: 'Sine',
unit: 'Hz',
formatString: '%0.2f',
hints: {
range: 1
}
},
{
key: "cos",
name: "Cosine",
unit: "deg",
key: 'cos',
name: 'Cosine',
unit: 'deg',
formatString: '%0.2f',
hints: {
range: 2
}
},
{
key: "intensities",
name: "Intensities",
key: 'intensities',
name: 'Intensities',
format: 'number[]',
hints: {
range: 3
@ -78,40 +73,40 @@ define([
'example.state-generator': {
values: [
{
key: "name",
name: "Name",
format: "string"
key: 'name',
name: 'Name',
format: 'string'
},
{
key: "utc",
name: "Time",
format: "utc",
key: 'utc',
name: 'Time',
format: 'utc',
hints: {
domain: 1
}
},
{
key: "local",
name: "Time",
format: "utc",
source: "utc",
key: 'local',
name: 'Time',
format: 'utc',
source: 'utc',
hints: {
domain: 2
}
},
{
key: "state",
source: "value",
name: "State",
format: "enum",
key: 'state',
source: 'value',
name: 'State',
format: 'enum',
enumerations: [
{
value: 0,
string: "OFF"
string: 'OFF'
},
{
value: 1,
string: "ON"
string: 'ON'
}
],
hints: {
@ -119,8 +114,8 @@ define([
}
},
{
key: "value",
name: "Value",
key: 'value',
name: 'Value',
hints: {
range: 2
}
@ -129,22 +124,15 @@ define([
}
};
function GeneratorMetadataProvider() {
}
function GeneratorMetadataProvider() {}
GeneratorMetadataProvider.prototype.supportsMetadata = function (domainObject) {
return Object.prototype.hasOwnProperty.call(METADATA_BY_TYPE, domainObject.type);
};
GeneratorMetadataProvider.prototype.getMetadata = function (domainObject) {
return Object.assign(
{},
domainObject.telemetry,
METADATA_BY_TYPE[domainObject.type]
);
return Object.assign({}, domainObject.telemetry, METADATA_BY_TYPE[domainObject.type]);
};
return GeneratorMetadataProvider;
});

View File

@ -20,12 +20,7 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
'./WorkerInterface'
], function (
WorkerInterface
) {
define(['./WorkerInterface'], function (WorkerInterface) {
var REQUEST_DEFAULTS = {
amplitude: 1,
period: 10,
@ -46,8 +41,7 @@ define([
return domainObject.type === 'generator';
};
GeneratorProvider.prototype.supportsRequest =
GeneratorProvider.prototype.supportsSubscribe =
GeneratorProvider.prototype.supportsRequest = GeneratorProvider.prototype.supportsSubscribe =
GeneratorProvider.prototype.canProvideTelemetry;
GeneratorProvider.prototype.makeWorkerRequest = function (domainObject, request) {
@ -67,7 +61,10 @@ define([
var workerRequest = {};
props.forEach(function (prop) {
if (domainObject.telemetry && Object.prototype.hasOwnProperty.call(domainObject.telemetry, prop)) {
if (
domainObject.telemetry &&
Object.prototype.hasOwnProperty.call(domainObject.telemetry, prop)
) {
workerRequest[prop] = domainObject.telemetry[prop];
}

View File

@ -20,12 +20,7 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
], function (
) {
define([], function () {
var PURPLE = {
sin: 2.2,
cos: 2.2
@ -48,34 +43,32 @@ define([
},
LIMITS = {
rh: {
cssClass: "is-limit--upr is-limit--red",
cssClass: 'is-limit--upr is-limit--red',
low: RED,
high: Number.POSITIVE_INFINITY,
name: "Red High"
name: 'Red High'
},
rl: {
cssClass: "is-limit--lwr is-limit--red",
cssClass: 'is-limit--lwr is-limit--red',
high: -RED,
low: Number.NEGATIVE_INFINITY,
name: "Red Low"
name: 'Red Low'
},
yh: {
cssClass: "is-limit--upr is-limit--yellow",
cssClass: 'is-limit--upr is-limit--yellow',
low: YELLOW,
high: RED,
name: "Yellow High"
name: 'Yellow High'
},
yl: {
cssClass: "is-limit--lwr is-limit--yellow",
cssClass: 'is-limit--lwr is-limit--yellow',
low: -RED,
high: -YELLOW,
name: "Yellow Low"
name: 'Yellow Low'
}
};
function SinewaveLimitProvider() {
}
function SinewaveLimitProvider() {}
SinewaveLimitProvider.prototype.supportsLimits = function (domainObject) {
return domainObject.type === 'generator';
@ -106,62 +99,61 @@ define([
};
SinewaveLimitProvider.prototype.getLimits = function (domainObject) {
return {
limits: function () {
return Promise.resolve({
WATCH: {
low: {
color: "cyan",
color: 'cyan',
sin: -CYAN.sin,
cos: -CYAN.cos
},
high: {
color: "cyan",
color: 'cyan',
...CYAN
}
},
WARNING: {
low: {
color: "yellow",
color: 'yellow',
sin: -YELLOW.sin,
cos: -YELLOW.cos
},
high: {
color: "yellow",
color: 'yellow',
...YELLOW
}
},
DISTRESS: {
low: {
color: "orange",
color: 'orange',
sin: -ORANGE.sin,
cos: -ORANGE.cos
},
high: {
color: "orange",
color: 'orange',
...ORANGE
}
},
CRITICAL: {
low: {
color: "red",
color: 'red',
sin: -RED.sin,
cos: -RED.cos
},
high: {
color: "red",
color: 'red',
...RED
}
},
SEVERE: {
low: {
color: "purple",
color: 'purple',
sin: -PURPLE.sin,
cos: -PURPLE.cos
},
high: {
color: "purple",
color: 'purple',
...PURPLE
}
}

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