mirror of
https://github.com/nasa/openmct.git
synced 2024-12-23 06:52:24 +00:00
feat(Fault Management): allow fault providers to define shelve durations (#7849)
* refactor: clean up FaultManagementView code * feat: providers can now provide "Shelve Duration" options * fix(exampleFaultSource): support `getShelveDurations` * a11y: aria label for fault management list item * a11y(FaultManagement): more labels * refactor: eliminate some faultUtils and refactor locator() out of tests * docs: add some more docs to fault management api * refactor: make for loop more readable * test: use static faults when testing * fix: set a timestamp for static faults and subtract so we get faults in order * refactor: autoformat * chore: add missing copyright header * fix: use as default parameter to get value as method is called * refactor: make magic number a const * fix(codecov): use codecov github action to upload * fix: generate the report * build: update circleci yml to use codecov orb * build: remove codecov scripts and package * build: don't use the orb because things can't be easy - nasa org disallows "third party" orbs * build: only use `sudo` if we ain't da root user --------- Co-authored-by: Andrew Henry <akhenry@gmail.com>
This commit is contained in:
parent
29f1956d1a
commit
ad30a0e2d0
.circleci
.github/workflows
e2e
example/faultManagement
package-lock.jsonpackage.jsonsrc
api/faultmanagement
plugins/faultManagement
@ -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:<<parameters.suite>>:publish
|
||||
- download_verify_codecov_cli
|
||||
- run:
|
||||
name: Upload coverage report to Codecov
|
||||
command: |
|
||||
./codecov --verbose upload-process --disable-search \
|
||||
-t $CODECOV_TOKEN \
|
||||
-n 'e2e-<<parameters.suite>>'-${CIRCLE_WORKFLOW_ID} \
|
||||
-F e2e-<<parameters.suite>> \
|
||||
-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:
|
||||
|
13
.github/workflows/e2e-couchdb.yml
vendored
13
.github/workflows/e2e-couchdb.yml
vendored
@ -52,10 +52,17 @@ jobs:
|
||||
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()
|
||||
|
@ -25,6 +25,7 @@ import { expect } from '../pluginFixtures.js';
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
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<void>}
|
||||
*/
|
||||
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<void>}
|
||||
*/
|
||||
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<void>}
|
||||
*/
|
||||
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<void>}
|
||||
*/
|
||||
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<void>}
|
||||
*/
|
||||
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<void>}
|
||||
*/
|
||||
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<void>}
|
||||
*/
|
||||
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<void>}
|
||||
*/
|
||||
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<void>}
|
||||
*/
|
||||
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<void>}
|
||||
*/
|
||||
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<string>}
|
||||
*/
|
||||
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<string>}
|
||||
*/
|
||||
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<string>}
|
||||
*/
|
||||
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<void>}
|
||||
*/
|
||||
export async function openFaultRowMenu(page, rowNumber) {
|
||||
// select
|
||||
await page
|
||||
.getByLabel('Disposition actions')
|
||||
.getByLabel('Fault triggered at')
|
||||
.nth(rowNumber - 1)
|
||||
.getByLabel('Disposition Actions')
|
||||
.click();
|
||||
}
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -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;
|
||||
|
152
package-lock.json
generated
152
package-lock.json
generated
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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.<FaultAPIResponse[]>}
|
||||
* 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.<FaultAPIResponse[]>} - 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.<T>} - 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.<T>}
|
||||
* 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.<T>} - 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
|
||||
|
@ -21,7 +21,12 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="c-fault-mgmt__list data-selectable" :class="classesFromState">
|
||||
<div
|
||||
role="listitem"
|
||||
:aria-label="listItemAriaLabel"
|
||||
class="c-fault-mgmt__list data-selectable"
|
||||
:class="classesFromState"
|
||||
>
|
||||
<div class="c-fault-mgmt-item c-fault-mgmt__list-checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
@ -33,29 +38,44 @@
|
||||
<div class="c-fault-mgmt-item">
|
||||
<div
|
||||
class="c-fault-mgmt__list-severity"
|
||||
:aria-label="fault.severity"
|
||||
:title="fault.severity"
|
||||
:aria-label="severityAriaLabel"
|
||||
:class="['is-severity-' + severity]"
|
||||
></div>
|
||||
</div>
|
||||
<div class="c-fault-mgmt-item c-fault-mgmt__list-content">
|
||||
<div class="c-fault-mgmt-item c-fault-mgmt__list-pathname">
|
||||
<div class="c-fault-mgmt__list-path">{{ fault.namespace }}</div>
|
||||
<div class="c-fault-mgmt__list-faultname">{{ fault.name }}</div>
|
||||
<div class="c-fault-mgmt__list-path" aria-label="Fault namespace">
|
||||
{{ fault.namespace }}
|
||||
</div>
|
||||
<div class="c-fault-mgmt__list-faultname" aria-label="Fault name">{{ fault.name }}</div>
|
||||
</div>
|
||||
<div class="c-fault-mgmt__list-content-right">
|
||||
<div class="c-fault-mgmt-item c-fault-mgmt__list-trigVal">
|
||||
<div class="c-fault-mgmt-item__value" :class="tripValueClassname" title="Trip Value">
|
||||
<div
|
||||
class="c-fault-mgmt-item__value"
|
||||
:class="tripValueClassname"
|
||||
title="Trip Value"
|
||||
aria-label="Trip Value"
|
||||
>
|
||||
{{ fault.triggerValueInfo.value }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="c-fault-mgmt-item c-fault-mgmt__list-curVal">
|
||||
<div class="c-fault-mgmt-item__value" :class="liveValueClassname" title="Live Value">
|
||||
<div
|
||||
class="c-fault-mgmt-item__value"
|
||||
:class="liveValueClassname"
|
||||
title="Live Value"
|
||||
aria-label="Live Value"
|
||||
>
|
||||
{{ fault.currentValueInfo.value }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="c-fault-mgmt-item c-fault-mgmt__list-trigTime">
|
||||
<div class="c-fault-mgmt-item__value" title="Last Trigger Time">
|
||||
<div
|
||||
class="c-fault-mgmt-item__value"
|
||||
title="Last Trigger Time"
|
||||
aria-label="Last Trigger Time"
|
||||
>
|
||||
{{ fault.triggerTime }}
|
||||
</div>
|
||||
</div>
|
||||
@ -98,7 +118,7 @@ export default {
|
||||
emits: ['acknowledge-selected', 'shelve-selected', 'toggle-selected', 'clear-all-selected'],
|
||||
computed: {
|
||||
checkBoxAriaLabel() {
|
||||
return `Select fault: ${this.fault.name}`;
|
||||
return `Select fault: ${this.fault.name || 'Unknown'} in ${this.fault.namespace || 'Unknown'}`;
|
||||
},
|
||||
classesFromState() {
|
||||
const exclusiveStates = [
|
||||
@ -165,6 +185,12 @@ export default {
|
||||
classname += SEVERITY_CLASS[triggerValueInfo.monitoringResult] || '';
|
||||
|
||||
return classname.trim();
|
||||
},
|
||||
listItemAriaLabel() {
|
||||
return `Fault triggered at ${this.fault.triggerTime || 'Unknown'} with severity ${this.fault.severity || 'Unknown'} in ${this.fault.namespace || 'Unknown'}`;
|
||||
},
|
||||
severityAriaLabel() {
|
||||
return `Severity: ${this.fault.severity || 'Unknown'}`;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -68,7 +68,6 @@
|
||||
import {
|
||||
FAULT_MANAGEMENT_ALARMS,
|
||||
FAULT_MANAGEMENT_GLOBAL_ALARMS,
|
||||
FAULT_MANAGEMENT_SHELVE_DURATIONS_IN_MS,
|
||||
FILTER_ITEMS,
|
||||
SORT_ITEMS
|
||||
} from './constants.js';
|
||||
@ -88,6 +87,13 @@ const SEARCH_KEYS = [
|
||||
'namespace'
|
||||
];
|
||||
|
||||
// Helper function for filtering faults
|
||||
function filterFaultsByTerm(faults, searchTerm) {
|
||||
return faults.filter((fault) =>
|
||||
SEARCH_KEYS.some((key) => fault[key]?.toString().toLowerCase().includes(searchTerm))
|
||||
);
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FaultManagementListHeader,
|
||||
@ -111,23 +117,18 @@ export default {
|
||||
},
|
||||
filteredFaultsList() {
|
||||
const filterName = FILTER_ITEMS[this.filterIndex];
|
||||
let list = this.faultsList;
|
||||
|
||||
// Exclude shelved alarms from all views except the Shelved view
|
||||
if (filterName !== 'Shelved') {
|
||||
list = list.filter((fault) => fault.shelved !== true);
|
||||
}
|
||||
let list = this.faultsList.filter((fault) =>
|
||||
filterName === 'Shelved' ? fault.shelved : !fault.shelved
|
||||
);
|
||||
|
||||
if (filterName === 'Acknowledged') {
|
||||
list = list.filter((fault) => fault.acknowledged);
|
||||
} else if (filterName === 'Unacknowledged') {
|
||||
list = list.filter((fault) => !fault.acknowledged);
|
||||
} else if (filterName === 'Shelved') {
|
||||
list = list.filter((fault) => fault.shelved);
|
||||
}
|
||||
|
||||
if (this.searchTerm.length > 0) {
|
||||
list = list.filter(this.filterUsingSearchTerm);
|
||||
list = filterFaultsByTerm(list, this.searchTerm);
|
||||
}
|
||||
|
||||
list.sort(SORT_ITEMS[this.sortBy].sortFunction);
|
||||
@ -138,6 +139,9 @@ export default {
|
||||
return this.openmct.faults.supportsActions();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.shelveDurations = this.openmct.faults.getShelveDurations();
|
||||
},
|
||||
mounted() {
|
||||
this.unsubscribe = this.openmct.faults.subscribe(this.domainObject, this.updateFault);
|
||||
},
|
||||
@ -158,14 +162,13 @@ export default {
|
||||
});
|
||||
}
|
||||
},
|
||||
updateFaultList() {
|
||||
this.openmct.faults.request(this.domainObject).then((faultsData) => {
|
||||
if (faultsData?.length > 0) {
|
||||
this.faultsList = faultsData.map((fd) => fd.fault);
|
||||
} else {
|
||||
this.faultsList = [];
|
||||
}
|
||||
});
|
||||
async updateFaultList() {
|
||||
const faultsData = await this.openmct.faults.request(this.domainObject);
|
||||
if (faultsData?.length > 0) {
|
||||
this.faultsList = faultsData.map((fd) => fd.fault);
|
||||
} else {
|
||||
this.faultsList = [];
|
||||
}
|
||||
},
|
||||
filterUsingSearchTerm(fault) {
|
||||
if (!fault) {
|
||||
@ -223,14 +226,29 @@ export default {
|
||||
);
|
||||
},
|
||||
async toggleAcknowledgeSelected(faults = this.selectedFaults) {
|
||||
let title = '';
|
||||
if (faults.length > 1) {
|
||||
title = `Acknowledge ${faults.length} selected faults`;
|
||||
} else if (faults.length === 1) {
|
||||
title = `Acknowledge fault: ${faults[0].name}`;
|
||||
}
|
||||
const title = this.getAcknowledgeTitle(faults);
|
||||
|
||||
const formStructure = {
|
||||
const formStructure = this.getAcknowledgeFormStructure(title);
|
||||
|
||||
try {
|
||||
const data = await this.openmct.forms.showForm(formStructure);
|
||||
this.acknowledgeFaults(faults, data);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
} finally {
|
||||
this.resetSelectedFaultMap();
|
||||
}
|
||||
},
|
||||
getAcknowledgeTitle(faults) {
|
||||
if (faults.length > 1) {
|
||||
return `Acknowledge ${faults.length} selected faults`;
|
||||
} else if (faults.length === 1) {
|
||||
return `Acknowledge fault: ${faults[0].name}`;
|
||||
}
|
||||
return '';
|
||||
},
|
||||
getAcknowledgeFormStructure(title) {
|
||||
return {
|
||||
title,
|
||||
sections: [
|
||||
{
|
||||
@ -253,17 +271,11 @@ export default {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const data = await this.openmct.forms.showForm(formStructure);
|
||||
faults.forEach((fault) => {
|
||||
this.openmct.faults.acknowledgeFault(fault, data);
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
} finally {
|
||||
this.resetSelectedFaultMap();
|
||||
}
|
||||
},
|
||||
acknowledgeFaults(faults, data) {
|
||||
faults.forEach((fault) => {
|
||||
this.openmct.faults.acknowledgeFault(fault, data);
|
||||
});
|
||||
},
|
||||
resetSelectedFaultMap() {
|
||||
Object.keys(this.selectedFaultMap).forEach((key) => {
|
||||
@ -273,7 +285,7 @@ export default {
|
||||
async toggleShelveSelected(faults = this.selectedFaults, shelveData = {}) {
|
||||
const { shelved = true } = shelveData;
|
||||
if (shelved) {
|
||||
let title =
|
||||
const title =
|
||||
faults.length > 1
|
||||
? `Shelve ${faults.length} selected faults`
|
||||
: `Shelve fault: ${faults[0].name}`;
|
||||
@ -295,10 +307,10 @@ export default {
|
||||
key: 'shelveDuration',
|
||||
control: 'select',
|
||||
name: 'Shelve duration',
|
||||
options: FAULT_MANAGEMENT_SHELVE_DURATIONS_IN_MS,
|
||||
options: this.shelveDurations,
|
||||
required: false,
|
||||
cssClass: 'l-input-lg',
|
||||
value: FAULT_MANAGEMENT_SHELVE_DURATIONS_IN_MS[0].value
|
||||
value: this.shelveDurations[0].value
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -318,19 +330,16 @@ export default {
|
||||
}
|
||||
|
||||
shelveData.comment = data.comment || '';
|
||||
shelveData.shelveDuration =
|
||||
data.shelveDuration !== undefined
|
||||
? data.shelveDuration
|
||||
: FAULT_MANAGEMENT_SHELVE_DURATIONS_IN_MS[0].value;
|
||||
shelveData.shelveDuration = data.shelveDuration ?? this.shelveDurations[0].value;
|
||||
} else {
|
||||
shelveData = {
|
||||
shelved: false
|
||||
};
|
||||
}
|
||||
|
||||
Object.values(faults).forEach((selectedFault) => {
|
||||
this.openmct.faults.shelveFault(selectedFault, shelveData);
|
||||
});
|
||||
await Promise.all(
|
||||
faults.map((selectedFault) => this.openmct.faults.shelveFault(selectedFault, shelveData))
|
||||
);
|
||||
|
||||
this.selectedFaultMap = {};
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user