diff --git a/.circleci/config.yml b/.circleci/config.yml index 994555277d..5cd72f893a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -37,6 +37,29 @@ commands: ls -latR >> /tmp/artifacts/dir.txt - store_artifacts: path: /tmp/artifacts/ + download_verify_codecov_cli: + description: 'Download and verify Codecov CLI' + steps: + - run: + name: Download and verify Codecov CLI + command: | + # Download Codecov CLI + curl -Os https://cli.codecov.io/latest/linux/codecov + + # Import Codecov's GPG key + curl https://keybase.io/codecovsecurity/pgp_keys.asc | gpg --no-default-keyring --keyring trustedkeys.gpg --import + + # Download and verify the SHA256SUM and its signature + curl -Os https://cli.codecov.io/latest/linux/codecov.SHA256SUM + curl -Os https://cli.codecov.io/latest/linux/codecov.SHA256SUM.sig + gpgv codecov.SHA256SUM.sig codecov.SHA256SUM + + # Verify the checksum + shasum -a 256 -c codecov.SHA256SUM + + # Make the codecov executable + [[ $EUID -ne 0 ]] && sudo chmod +x codecov || chmod +x codecov + ./codecov --help 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" parameters: @@ -44,7 +67,15 @@ commands: type: string steps: - run: npm run cov:e2e:report || true - - run: npm run cov:e2e:<>:publish + - download_verify_codecov_cli + - run: + name: Upload coverage report to Codecov + command: | + ./codecov --verbose upload-process --disable-search \ + -t $CODECOV_TOKEN \ + -n 'e2e-<>'-${CIRCLE_WORKFLOW_ID} \ + -F e2e-<> \ + -f ./coverage/e2e/lcov.info jobs: npm-audit: parameters: @@ -81,7 +112,15 @@ jobs: mkdir -p dist/reports/tests/ TESTFILES=$(circleci tests glob "src/**/*Spec.js") echo "$TESTFILES" | circleci tests run --command="xargs npm run test" --verbose - - run: npm run cov:unit:publish + - download_verify_codecov_cli + - run: + name: Upload coverage report to Codecov + command: | + ./codecov --verbose upload-process --disable-search \ + -t $CODECOV_TOKEN \ + -n 'unit-test'-${CIRCLE_WORKFLOW_ID} \ + -F unit \ + -f ./coverage/unit/lcov.info - store_test_results: path: dist/reports/tests/ - store_artifacts: diff --git a/.github/workflows/e2e-couchdb.yml b/.github/workflows/e2e-couchdb.yml index 48ba6f352a..1a8800324c 100644 --- a/.github/workflows/e2e-couchdb.yml +++ b/.github/workflows/e2e-couchdb.yml @@ -51,11 +51,18 @@ jobs: env: COMMIT_INFO_SHA: ${{github.event.pull_request.head.sha }} run: npm run test:e2e:couchdb + + - name: Generate Code Coverage Report + run: npm run cov:e2e:report - name: Publish Results to Codecov.io - env: - SUPER_SECRET: ${{ secrets.CODECOV_TOKEN }} - run: npm run cov:e2e:full:publish + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./coverage/e2e/lcov.info + flags: e2e-full + fail_ci_if_error: true + verbose: true - name: Archive test results if: success() || failure() diff --git a/e2e/helper/faultUtils.js b/e2e/helper/faultUtils.js index 05c7df98db..5e9e0ffa31 100644 --- a/e2e/helper/faultUtils.js +++ b/e2e/helper/faultUtils.js @@ -25,6 +25,7 @@ import { expect } from '../pluginFixtures.js'; /** * @param {import('@playwright/test').Page} page + * @returns {Promise} */ export async function navigateToFaultManagementWithExample(page) { await page.addInitScript({ @@ -36,6 +37,7 @@ export async function navigateToFaultManagementWithExample(page) { /** * @param {import('@playwright/test').Page} page + * @returns {Promise} */ export async function navigateToFaultManagementWithStaticExample(page) { await page.addInitScript({ @@ -47,6 +49,7 @@ export async function navigateToFaultManagementWithStaticExample(page) { /** * @param {import('@playwright/test').Page} page + * @returns {Promise} */ export async function navigateToFaultManagementWithoutExample(page) { await page.addInitScript({ @@ -58,6 +61,7 @@ export async function navigateToFaultManagementWithoutExample(page) { /** * @param {import('@playwright/test').Page} page + * @returns {Promise} */ async function navigateToFaultItemInTree(page) { await page.goto('./', { waitUntil: 'domcontentloaded' }); @@ -77,6 +81,8 @@ async function navigateToFaultItemInTree(page) { /** * @param {import('@playwright/test').Page} page + * @param {number} rowNumber + * @returns {Promise} */ export async function acknowledgeFault(page, rowNumber) { await openFaultRowMenu(page, rowNumber); @@ -86,6 +92,8 @@ export async function acknowledgeFault(page, rowNumber) { /** * @param {import('@playwright/test').Page} page + * @param {...number} nums + * @returns {Promise} */ export async function shelveMultipleFaults(page, ...nums) { const selectRows = nums.map((num) => { @@ -99,6 +107,8 @@ export async function shelveMultipleFaults(page, ...nums) { /** * @param {import('@playwright/test').Page} page + * @param {...number} nums + * @returns {Promise} */ export async function acknowledgeMultipleFaults(page, ...nums) { const selectRows = nums.map((num) => { @@ -106,50 +116,43 @@ export async function acknowledgeMultipleFaults(page, ...nums) { }); await Promise.all(selectRows); - await page.locator('button:has-text("Acknowledge")').click(); + await page.getByLabel('Acknowledge selected faults').click(); await page.getByLabel('Save').click(); } /** * @param {import('@playwright/test').Page} page + * @param {number} rowNumber + * @returns {Promise} */ export async function shelveFault(page, rowNumber) { await openFaultRowMenu(page, rowNumber); - await page.locator('.c-menu >> text="Shelve"').click(); - // Click [aria-label="Save"] + await page.getByLabel('Shelve', { exact: true }).click(); await page.getByLabel('Save').click(); } /** * @param {import('@playwright/test').Page} page - */ -export async function changeViewTo(page, view) { - await page.locator('.c-fault-mgmt__search-row select').first().selectOption(view); -} - -/** - * @param {import('@playwright/test').Page} page + * @param {'severity' | 'newest-first' | 'oldest-first'} sort + * @returns {Promise} */ export async function sortFaultsBy(page, sort) { - await page.locator('.c-fault-mgmt__list-header-sortButton select').selectOption(sort); + await page.getByTitle('Sort By').getByRole('combobox').selectOption(sort); } /** * @param {import('@playwright/test').Page} page + * @param {'acknowledged' | 'shelved' | 'standard view'} view + * @returns {Promise} */ -export async function enterSearchTerm(page, term) { - await page.locator('.c-fault-mgmt-search [aria-label="Search Input"]').fill(term); -} - -/** - * @param {import('@playwright/test').Page} page - */ -export async function clearSearch(page) { - await enterSearchTerm(page, ''); +export async function changeViewTo(page, view) { + await page.getByTitle('View Filter').getByRole('combobox').selectOption(view); } /** * @param {import('@playwright/test').Page} page + * @param {number} rowNumber + * @returns {Promise} */ export async function selectFaultItem(page, rowNumber) { await page @@ -165,71 +168,37 @@ export async function selectFaultItem(page, rowNumber) { /** * @param {import('@playwright/test').Page} page - */ -export async function getHighestSeverity(page) { - const criticalCount = await page.locator('[title=CRITICAL]').count(); - const warningCount = await page.locator('[title=WARNING]').count(); - - if (criticalCount > 0) { - return 'CRITICAL'; - } else if (warningCount > 0) { - return 'WARNING'; - } - - return 'WATCH'; -} - -/** - * @param {import('@playwright/test').Page} page - */ -export async function getLowestSeverity(page) { - const warningCount = await page.locator('[title=WARNING]').count(); - const watchCount = await page.locator('[title=WATCH]').count(); - - if (watchCount > 0) { - return 'WATCH'; - } else if (warningCount > 0) { - return 'WARNING'; - } - - return 'CRITICAL'; -} - -/** - * @param {import('@playwright/test').Page} page - */ -export async function getFaultResultCount(page) { - const count = await page.locator('.c-faults-list-view-item-body > .c-fault-mgmt__list').count(); - - return count; -} - -/** - * @param {import('@playwright/test').Page} page + * @param {number} rowNumber + * @returns {import('@playwright/test').Locator} */ export function getFault(page, rowNumber) { - const fault = page.locator( - `.c-faults-list-view-item-body > .c-fault-mgmt__list >> nth=${rowNumber - 1}` - ); + const fault = page.getByLabel('Fault triggered at').nth(rowNumber - 1); return fault; } /** * @param {import('@playwright/test').Page} page + * @param {string} name + * @returns {import('@playwright/test').Locator} */ export function getFaultByName(page, name) { - const fault = page.locator(`.c-fault-mgmt__list-faultname:has-text("${name}")`); + const fault = page.getByLabel('Fault triggered at').filter({ + hasText: name + }); return fault; } /** * @param {import('@playwright/test').Page} page + * @param {number} rowNumber + * @returns {Promise} */ export async function getFaultName(page, rowNumber) { const faultName = await page - .locator(`.c-fault-mgmt__list-faultname >> nth=${rowNumber - 1}`) + .getByLabel('Fault name', { exact: true }) + .nth(rowNumber - 1) .textContent(); return faultName; @@ -237,21 +206,13 @@ export async function getFaultName(page, rowNumber) { /** * @param {import('@playwright/test').Page} page - */ -export 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'); - - return faultSeverity; -} - -/** - * @param {import('@playwright/test').Page} page + * @param {number} rowNumber + * @returns {Promise} */ export async function getFaultNamespace(page, rowNumber) { const faultNamespace = await page - .locator(`.c-fault-mgmt__list-path >> nth=${rowNumber - 1}`) + .getByLabel('Fault namespace') + .nth(rowNumber - 1) .textContent(); return faultNamespace; @@ -259,10 +220,13 @@ export async function getFaultNamespace(page, rowNumber) { /** * @param {import('@playwright/test').Page} page + * @param {number} rowNumber + * @returns {Promise} */ export async function getFaultTriggerTime(page, rowNumber) { const faultTriggerTime = await page - .locator(`.c-fault-mgmt__list-trigTime >> nth=${rowNumber - 1} >> .c-fault-mgmt-item__value`) + .getByLabel('Last Trigger Time') + .nth(rowNumber - 1) .textContent(); return faultTriggerTime.toString().trim(); @@ -270,11 +234,14 @@ export async function getFaultTriggerTime(page, rowNumber) { /** * @param {import('@playwright/test').Page} page + * @param {number} rowNumber + * @returns {Promise} */ export async function openFaultRowMenu(page, rowNumber) { // select await page - .getByLabel('Disposition actions') + .getByLabel('Fault triggered at') .nth(rowNumber - 1) + .getByLabel('Disposition Actions') .click(); } diff --git a/e2e/tests/functional/plugins/faultManagement/faultManagement.e2e.spec.js b/e2e/tests/functional/plugins/faultManagement/faultManagement.e2e.spec.js index 792c2abacc..df94b16651 100644 --- a/e2e/tests/functional/plugins/faultManagement/faultManagement.e2e.spec.js +++ b/e2e/tests/functional/plugins/faultManagement/faultManagement.e2e.spec.js @@ -24,19 +24,13 @@ import { acknowledgeFault, acknowledgeMultipleFaults, changeViewTo, - clearSearch, - enterSearchTerm, getFault, getFaultByName, getFaultName, getFaultNamespace, - getFaultResultCount, - getFaultSeverity, getFaultTriggerTime, - getHighestSeverity, - getLowestSeverity, - navigateToFaultManagementWithExample, navigateToFaultManagementWithoutExample, + navigateToFaultManagementWithStaticExample, selectFaultItem, shelveFault, shelveMultipleFaults, @@ -46,7 +40,7 @@ import { expect, test } from '../../../../pluginFixtures.js'; test.describe('The Fault Management Plugin using example faults', () => { test.beforeEach(async ({ page }) => { - await navigateToFaultManagementWithExample(page); + await navigateToFaultManagementWithStaticExample(page); }); test('Shows a criticality icon for every fault', async ({ page }) => { @@ -56,7 +50,7 @@ test.describe('The Fault Management Plugin using example faults', () => { expect(faultCount).toEqual(criticalityIconCount); }); - test('When selecting a fault, it has an "is-selected" class and it\'s information shows in the inspector', async ({ + test('When selecting a fault, it has an "is-selected" class and its information shows in the inspector', async ({ page }) => { await selectFaultItem(page, 1); @@ -67,9 +61,7 @@ test.describe('The Fault Management Plugin using example faults', () => { .getByLabel('Source inspector properties') .getByLabel('inspector property value'); - await expect( - page.locator('.c-faults-list-view-item-body > .c-fault-mgmt__list').first() - ).toHaveClass(/is-selected/); + await expect(page.getByLabel('Fault triggered at').first()).toHaveClass(/is-selected/); await expect(inspectorFaultName).toHaveCount(1); }); @@ -79,23 +71,18 @@ test.describe('The Fault Management Plugin using example faults', () => { await selectFaultItem(page, 1); await selectFaultItem(page, 2); - const selectedRows = page.locator( - '.c-fault-mgmt__list.is-selected .c-fault-mgmt__list-faultname' - ); - expect(await selectedRows.count()).toEqual(2); + const selectedRows = page.getByRole('checkbox', { checked: true }); + await expect(selectedRows).toHaveCount(2); await page.getByRole('tab', { name: 'Config' }).click(); 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(); - - expect(firstNameInInspectorCount).toEqual(0); - expect(secondNameInInspectorCount).toEqual(0); + await expect( + page.locator(`.c-inspector__properties >> :text("${firstSelectedFaultName}")`) + ).toHaveCount(0); + await expect( + page.locator(`.c-inspector__properties >> :text("${secondSelectedFaultName}")`) + ).toHaveCount(0); }); test('Allows you to shelve a fault', async ({ page }) => { @@ -186,44 +173,60 @@ test.describe('The Fault Management Plugin using example faults', () => { const faultFiveTriggerTime = await getFaultTriggerTime(page, 5); // should be all faults (5) - let faultResultCount = await getFaultResultCount(page); - expect(faultResultCount).toEqual(5); + await expect(page.getByLabel('Fault triggered at')).toHaveCount(5); // search namespace - await enterSearchTerm(page, faultThreeNamespace); + await page + .getByLabel('Fault Management Object View') + .getByLabel('Search Input') + .fill(faultThreeNamespace); - faultResultCount = await getFaultResultCount(page); - expect(faultResultCount).toEqual(1); + await expect(page.getByLabel('Fault triggered at')).toHaveCount(1); expect(await getFaultNamespace(page, 1)).toEqual(faultThreeNamespace); // all faults - await clearSearch(page); - faultResultCount = await getFaultResultCount(page); - expect(faultResultCount).toEqual(5); + await page.getByLabel('Fault Management Object View').getByLabel('Search Input').fill(''); + await expect(page.getByLabel('Fault triggered at')).toHaveCount(5); // search name - await enterSearchTerm(page, faultTwoName); + await page + .getByLabel('Fault Management Object View') + .getByLabel('Search Input') + .fill(faultTwoName); - faultResultCount = await getFaultResultCount(page); - expect(faultResultCount).toEqual(1); + await expect(page.getByLabel('Fault triggered at')).toHaveCount(1); expect(await getFaultName(page, 1)).toEqual(faultTwoName); // all faults - await clearSearch(page); - faultResultCount = await getFaultResultCount(page); - expect(faultResultCount).toEqual(5); + await page.getByLabel('Fault Management Object View').getByLabel('Search Input').fill(''); + await expect(page.getByLabel('Fault triggered at')).toHaveCount(5); // search triggerTime - await enterSearchTerm(page, faultFiveTriggerTime); + await page + .getByLabel('Fault Management Object View') + .getByLabel('Search Input') + .fill(faultFiveTriggerTime); - faultResultCount = await getFaultResultCount(page); - expect(faultResultCount).toEqual(1); + await expect(page.getByLabel('Fault triggered at')).toHaveCount(1); expect(await getFaultTriggerTime(page, 1)).toEqual(faultFiveTriggerTime); }); test('Allows you to sort faults', async ({ page }) => { - const highestSeverity = await getHighestSeverity(page); - const lowestSeverity = await getLowestSeverity(page); + /** + * Compares two severity levels and returns a number indicating their relative order. + * + * @param {'CRITICAL' | 'WARNING' | 'WATCH'} severity1 - The first severity level to compare. + * @param {'CRITICAL' | 'WARNING' | 'WATCH'} severity2 - The second severity level to compare. + * @returns {number} - A negative number if severity1 is less severe than severity2, + * a positive number if severity1 is more severe than severity2, + * or 0 if they are equally severe. + */ + // eslint-disable-next-line func-style + const compareSeverity = (severity1, severity2) => { + const severityOrder = ['WATCH', 'WARNING', 'CRITICAL']; + return severityOrder.indexOf(severity1) - severityOrder.indexOf(severity2); + }; + const faultOneName = 'Example Fault 1'; const faultFiveName = 'Example Fault 5'; let firstFaultName = await getFaultName(page, 1); @@ -237,10 +240,19 @@ test.describe('The Fault Management Plugin using example faults', () => { await sortFaultsBy(page, 'severity'); - const sortedHighestSeverity = await getFaultSeverity(page, 1); - const sortedLowestSeverity = await getFaultSeverity(page, 5); - expect(sortedHighestSeverity).toEqual(highestSeverity); - expect(sortedLowestSeverity).toEqual(lowestSeverity); + const firstFaultSeverityLabel = await page + .getByLabel('Severity:') + .first() + .getAttribute('aria-label'); + const firstFaultSeverity = firstFaultSeverityLabel.split(' ').slice(1).join(' '); + + const lastFaultSeverityLabel = await page + .getByLabel('Severity:') + .last() + .getAttribute('aria-label'); + const lastFaultSeverity = lastFaultSeverityLabel.split(' ').slice(1).join(' '); + + expect(compareSeverity(firstFaultSeverity, lastFaultSeverity)).toBeGreaterThan(0); }); }); @@ -250,24 +262,18 @@ test.describe('The Fault Management Plugin without using example faults', () => }); test('Shows no faults when no faults are provided', async ({ page }) => { - const faultCount = await page.locator('c-fault-mgmt__list').count(); - - expect(faultCount).toEqual(0); + await expect(page.getByLabel('Fault triggered at')).toHaveCount(0); await changeViewTo(page, 'acknowledged'); - const acknowledgedCount = await page.locator('c-fault-mgmt__list').count(); - expect(acknowledgedCount).toEqual(0); + await expect(page.getByLabel('Fault triggered at')).toHaveCount(0); await changeViewTo(page, 'shelved'); - const shelvedCount = await page.locator('c-fault-mgmt__list').count(); - expect(shelvedCount).toEqual(0); + await expect(page.getByLabel('Fault triggered at')).toHaveCount(0); }); test('Will return no faults when searching', async ({ page }) => { - await enterSearchTerm(page, 'fault'); + await page.getByLabel('Fault Management Object View').getByLabel('Search Input').fill('fault'); - const faultCount = await page.locator('c-fault-mgmt__list').count(); - - expect(faultCount).toEqual(0); + await expect(page.getByLabel('Fault triggered at')).toHaveCount(0); }); }); diff --git a/example/faultManagement/exampleFaultSource.js b/example/faultManagement/exampleFaultSource.js index 17592ba4b9..bf36ae6c47 100644 --- a/example/faultManagement/exampleFaultSource.js +++ b/example/faultManagement/exampleFaultSource.js @@ -20,6 +20,7 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ +import { DEFAULT_SHELVE_DURATIONS } from '../../src/api/faultmanagement/FaultManagementAPI.js'; import { acknowledgeFault, randomFaults, shelveFault } from './utils.js'; export default function (staticFaults = false) { @@ -56,6 +57,9 @@ export default function (staticFaults = false) { return Promise.resolve({ success: true }); + }, + getShelveDurations() { + return DEFAULT_SHELVE_DURATIONS; } }); }; diff --git a/example/faultManagement/utils.js b/example/faultManagement/utils.js index ed5de3f0e0..16614d8ad5 100644 --- a/example/faultManagement/utils.js +++ b/example/faultManagement/utils.js @@ -1,4 +1,27 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2024, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ + const SEVERITIES = ['WATCH', 'WARNING', 'CRITICAL']; +const MOONWALK_TIMESTAMP = 14159040000; const NAMESPACE = '/Example/fault-'; const getRandom = { severity: () => SEVERITIES[Math.floor(Math.random() * 3)], @@ -13,7 +36,8 @@ const getRandom = { val = num; severity = SEVERITIES[severityIndex - 1]; - time = num; + // Subtract `num` from the timestamp so that the faults are in order + time = MOONWALK_TIMESTAMP - num; // Mon, 21 Jul 1969 02:56:00 GMT 🌔👨‍🚀👨‍🚀👨‍🚀 } return { @@ -43,14 +67,7 @@ const getRandom = { } }; -export function shelveFault( - fault, - opts = { - shelved: true, - comment: '', - shelveDuration: 90000 - } -) { +export function shelveFault(fault, opts = { shelved: true, comment: '', shelveDuration: 90000 }) { fault.shelved = true; setTimeout(() => { @@ -65,8 +82,8 @@ export function acknowledgeFault(fault) { export function randomFaults(staticFaults, count = 5) { let faults = []; - for (let x = 1, y = count + 1; x < y; x++) { - faults.push(getRandom.fault(x, staticFaults)); + for (let i = 1; i <= count; i++) { + faults.push(getRandom.fault(i, staticFaults)); } return faults; diff --git a/package-lock.json b/package-lock.json index 9a29ebe433..4eff9deb1a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,6 @@ "@vue/compiler-sfc": "3.4.3", "babel-loader": "9.1.0", "babel-plugin-istanbul": "6.1.1", - "codecov": "3.8.3", "comma-separated-values": "3.6.4", "copy-webpack-plugin": "12.0.2", "cspell": "7.3.8", @@ -1586,15 +1585,6 @@ "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", "dev": true }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -2308,18 +2298,6 @@ "node": ">=8.9" } }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -2454,16 +2432,6 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/argv": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/argv/-/argv-0.0.2.tgz", - "integrity": "sha512-dEamhpPEwRUBpLNHeuCm/v+g0anFByHahxodVO/BbAarHVBBg2MccCwf9K+o1Pof+2btdnkJelYVUWjW/VrATw==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dev": true, - "engines": { - "node": ">=0.6.10" - } - }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -3013,26 +2981,6 @@ "node": ">=0.10.0" } }, - "node_modules/codecov": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/codecov/-/codecov-3.8.3.tgz", - "integrity": "sha512-Y8Hw+V3HgR7V71xWH2vQ9lyS358CbGCldWlJFR0JirqoGtOoas3R3/OclRTvgUYFK29mmJICDPauVKmpqbwhOA==", - "deprecated": "https://about.codecov.io/blog/codecov-uploader-deprecation-plan/", - "dev": true, - "dependencies": { - "argv": "0.0.2", - "ignore-walk": "3.0.4", - "js-yaml": "3.14.1", - "teeny-request": "7.1.1", - "urlgrey": "1.0.0" - }, - "bin": { - "codecov": "bin/codecov" - }, - "engines": { - "node": ">=4.0" - } - }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -5582,21 +5530,6 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, - "node_modules/fast-url-parser": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", - "integrity": "sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==", - "dev": true, - "dependencies": { - "punycode": "^1.3.2" - } - }, - "node_modules/fast-url-parser/node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", - "dev": true - }, "node_modules/fastest-levenshtein": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", @@ -6387,20 +6320,6 @@ "node": ">=8.0.0" } }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/http-proxy-middleware": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", @@ -6431,19 +6350,6 @@ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", "dev": true }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -6486,15 +6392,6 @@ "node": ">= 4" } }, - "node_modules/ignore-walk": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz", - "integrity": "sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==", - "dev": true, - "dependencies": { - "minimatch": "^3.0.4" - } - }, "node_modules/image-size": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.1.1.tgz", @@ -10346,15 +10243,6 @@ "node": ">= 0.6" } }, - "node_modules/stream-events": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", - "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", - "dev": true, - "dependencies": { - "stubs": "^3.0.0" - } - }, "node_modules/streamroller": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", @@ -10537,12 +10425,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/stubs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", - "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", - "dev": true - }, "node_modules/style-loader": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.3.tgz", @@ -10608,31 +10490,6 @@ "node": ">=6" } }, - "node_modules/teeny-request": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-7.1.1.tgz", - "integrity": "sha512-iwY6rkW5DDGq8hE2YgNQlKbptYpY5Nn2xecjQiNjOXWbKzPGUfmeUBCSQbbr306d7Z7U2N0TPl+/SwYRfua1Dg==", - "dev": true, - "dependencies": { - "http-proxy-agent": "^4.0.0", - "https-proxy-agent": "^5.0.0", - "node-fetch": "^2.6.1", - "stream-events": "^1.0.5", - "uuid": "^8.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/teeny-request/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/terser": { "version": "5.29.1", "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.1.tgz", @@ -11013,15 +10870,6 @@ "punycode": "^2.1.0" } }, - "node_modules/urlgrey": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/urlgrey/-/urlgrey-1.0.0.tgz", - "integrity": "sha512-hJfIzMPJmI9IlLkby8QrsCykQ+SXDeO2W5Q9QTW3QpqZVTx4a/K7p8/5q+/isD8vsbVaFgql/gvAoQCRQ2Cb5w==", - "dev": true, - "dependencies": { - "fast-url-parser": "^1.1.3" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index 5677abe42b..7bd2b12e89 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,6 @@ "@vue/compiler-sfc": "3.4.3", "babel-loader": "9.1.0", "babel-plugin-istanbul": "6.1.1", - "codecov": "3.8.3", "comma-separated-values": "3.6.4", "copy-webpack-plugin": "12.0.2", "cspell": "7.3.8", @@ -131,9 +130,6 @@ "update-about-dialog-copyright": "perl -pi -e 's/20\\d\\d\\-202\\d/2014\\-2023/gm' ./src/ui/layout/AboutDialog.vue", "update-copyright-date": "npm run update-about-dialog-copyright && grep -lr --null --include=*.{js,scss,vue,ts,sh,html,md,frag} 'Copyright (c) 20' . | xargs -r0 perl -pi -e 's/Copyright\\s\\(c\\)\\s20\\d\\d\\-20\\d\\d/Copyright \\(c\\)\\ 2014\\-2024/gm'", "cov:e2e:report": "nyc report --reporter=lcovonly --report-dir=./coverage/e2e", - "cov:e2e:full:publish": "codecov --disable=gcov -f ./coverage/e2e/lcov.info -F e2e-full", - "cov:e2e:ci:publish": "codecov --disable=gcov -f ./coverage/e2e/lcov.info -F e2e-ci", - "cov:unit:publish": "codecov --disable=gcov -f ./coverage/unit/lcov.info -F unit", "prepare": "npm run build:prod && npx tsc" }, "homepage": "https://nasa.github.io/openmct", @@ -160,4 +156,4 @@ "keywords": [ "nasa" ] -} +} \ No newline at end of file diff --git a/src/api/faultmanagement/FaultManagementAPI.js b/src/api/faultmanagement/FaultManagementAPI.js index 07eef4f7cb..2eb3673a61 100644 --- a/src/api/faultmanagement/FaultManagementAPI.js +++ b/src/api/faultmanagement/FaultManagementAPI.js @@ -20,6 +20,32 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ +/** @type {ShelveDuration[]} */ +export const DEFAULT_SHELVE_DURATIONS = [ + { + name: '5 Minutes', + value: 300000 + }, + { + name: '10 Minutes', + value: 600000 + }, + { + name: '15 Minutes', + value: 900000 + }, + { + name: 'Indefinite', + value: null + } +]; + +/** + * Provides an API for managing faults within Open MCT. + * It allows for the addition of a fault provider, checking for provider support, and + * performing various operations such as requesting, subscribing to, acknowledging, + * and shelving faults. + */ export default class FaultManagementAPI { /** * @param {import("openmct").OpenMCT} openmct @@ -29,14 +55,20 @@ export default class FaultManagementAPI { } /** - * @param {*} provider + * Sets the provider for the Fault Management API. + * The provider should implement methods for acknowledging and shelving faults. + * + * @param {*} provider - The provider to be set. */ addProvider(provider) { this.provider = provider; } /** - * @returns {boolean} + * Checks if the current provider supports fault management actions. + * Specifically, it checks if the provider has methods for acknowledging and shelving faults. + * + * @returns {boolean} - Returns true if the provider supports fault management actions, otherwise false. */ supportsActions() { return ( @@ -45,48 +77,78 @@ export default class FaultManagementAPI { } /** - * @param {import('openmct').DomainObject} domainObject - * @returns {Promise.} + * Requests fault data for a given domain object. + * This method checks if the current provider supports the request operation for the given domain object. + * If supported, it delegates the request to the provider's request method. + * If not supported, it returns a rejected promise. + * + * @param {import('openmct').DomainObject} domainObject - The domain object for which fault data is requested. + * @returns {Promise.} - A promise that resolves to an array of fault API responses. */ request(domainObject) { if (!this.provider?.supportsRequest(domainObject)) { - return Promise.reject(); + return Promise.reject('Provider does not support request operation'); } return this.provider.request(domainObject); } /** - * @param {import('openmct').DomainObject} domainObject - * @param {Function} callback - * @returns {Function} unsubscribe + * Subscribes to fault data updates for a given domain object. + * This method checks if the current provider supports the subscribe operation for the given domain object. + * If supported, it delegates the subscription to the provider's subscribe method. + * If not supported, it returns a rejected promise. + * + * @param {import('openmct').DomainObject} domainObject - The domain object for which to subscribe to fault data updates. + * @param {Function} callback - The callback function to be called with fault data updates. + * @returns {Function} unsubscribe - A function to unsubscribe from the fault data updates. */ subscribe(domainObject, callback) { if (!this.provider?.supportsSubscribe(domainObject)) { - return Promise.reject(); + return Promise.reject('Provider does not support subscribe operation'); } return this.provider.subscribe(domainObject, callback); } /** - * @param {Fault} fault - * @param {*} ackData + * Acknowledges a fault using the provider's acknowledgeFault method. + * + * @param {Fault} fault - The fault object to be acknowledged. + * @param {*} ackData - Additional data required for acknowledging the fault. + * @returns {Promise.} - A promise that resolves when the fault is acknowledged. */ acknowledgeFault(fault, ackData) { return this.provider.acknowledgeFault(fault, ackData); } /** - * @param {Fault} fault - * @param {*} shelveData - * @returns {Promise.} + * Shelves a fault using the provider's shelveFault method. + * + * @param {Fault} fault - The fault object to be shelved. + * @param {*} shelveData - Additional data required for shelving the fault. + * @returns {Promise.} - A promise that resolves when the fault is shelved. */ shelveFault(fault, shelveData) { return this.provider.shelveFault(fault, shelveData); } + + /** + * Retrieves the available shelve durations from the provider, or the default durations if the + * provider does not provide any. + * @returns {ShelveDuration[]} + */ + getShelveDurations() { + return this.provider?.getShelveDurations() ?? DEFAULT_SHELVE_DURATIONS; + } } +/** + * @typedef {Object} ShelveDuration + * @property {string} name - The name of the shelve duration + * @property {number|null} value - The value of the shelve duration in milliseconds, or null for indefinite + */ + /** * @typedef {Object} TriggerValueInfo * @property {number} value diff --git a/src/plugins/faultManagement/FaultManagementListItem.vue b/src/plugins/faultManagement/FaultManagementListItem.vue index b472e8523f..d5a15c5297 100644 --- a/src/plugins/faultManagement/FaultManagementListItem.vue +++ b/src/plugins/faultManagement/FaultManagementListItem.vue @@ -21,7 +21,12 @@ -->