mirror of
https://github.com/nasa/openmct.git
synced 2025-06-16 22:28:13 +00:00
test(e2e): add e2e and visual tests for Mission Status (plus a11y) (#7462)
* feat: enable mission status in example user * test: add initial missionStatus suite * test(WIP): mission status e2e suite * test(e2e): add e2e and visual tests for mission status + a11y * test(a11y): scan for a11y violations * a11y: remove labels for non-interactive elements
This commit is contained in:
127
e2e/tests/functional/missionStatus.e2e.spec.js
Normal file
127
e2e/tests/functional/missionStatus.e2e.spec.js
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
This test suite is dedicated to tests which verify persistability checks
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
|
import { expect, test } from '../../baseFixtures.js';
|
||||||
|
|
||||||
|
test.describe('Mission Status @addInit', () => {
|
||||||
|
const NO_GO = '0';
|
||||||
|
const GO = '1';
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
// FIXME: determine if plugins will be added to index.html or need to be injected
|
||||||
|
await page.addInitScript({
|
||||||
|
path: fileURLToPath(new URL('../../helper/addInitExampleUser.js', import.meta.url))
|
||||||
|
});
|
||||||
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
|
await expect(page.getByText('Select Role')).toBeVisible();
|
||||||
|
// Description should be empty https://github.com/nasa/openmct/issues/6978
|
||||||
|
await expect(page.getByLabel('Dialog message')).toBeHidden();
|
||||||
|
// set role
|
||||||
|
await page.getByRole('button', { name: 'Select', exact: true }).click();
|
||||||
|
// dismiss role confirmation popup
|
||||||
|
await page.getByRole('button', { name: 'Dismiss' }).click();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Basic functionality', async ({ page }) => {
|
||||||
|
const imageryStatusSelect = page.getByRole('combobox', { name: 'Imagery' });
|
||||||
|
const commandingStatusSelect = page.getByRole('combobox', { name: 'Commanding' });
|
||||||
|
const drivingStatusSelect = page.getByRole('combobox', { name: 'Driving' });
|
||||||
|
const missionStatusPanel = page.getByRole('dialog', { name: 'User Control Panel' });
|
||||||
|
|
||||||
|
await test.step('Mission status panel shows/hides when toggled', async () => {
|
||||||
|
// Ensure that clicking the button toggles the dialog
|
||||||
|
await page.getByLabel('Toggle Mission Status Panel').click();
|
||||||
|
await expect(missionStatusPanel).toBeVisible();
|
||||||
|
await page.getByLabel('Toggle Mission Status Panel').click();
|
||||||
|
await expect(missionStatusPanel).toBeHidden();
|
||||||
|
await page.getByLabel('Toggle Mission Status Panel').click();
|
||||||
|
await expect(missionStatusPanel).toBeVisible();
|
||||||
|
|
||||||
|
// Ensure that clicking the close button closes the dialog
|
||||||
|
await page.getByLabel('Close Mission Status Panel').click();
|
||||||
|
await expect(missionStatusPanel).toBeHidden();
|
||||||
|
await page.getByLabel('Toggle Mission Status Panel').click();
|
||||||
|
await expect(missionStatusPanel).toBeVisible();
|
||||||
|
|
||||||
|
// Ensure clicking off the dialog also closes it
|
||||||
|
await page.getByLabel('My Items Grid View').click();
|
||||||
|
await expect(missionStatusPanel).toBeHidden();
|
||||||
|
await page.getByLabel('Toggle Mission Status Panel').click();
|
||||||
|
await expect(missionStatusPanel).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Mission action statuses have correct defaults and can be set', async () => {
|
||||||
|
await expect(imageryStatusSelect).toHaveValue(NO_GO);
|
||||||
|
await expect(commandingStatusSelect).toHaveValue(NO_GO);
|
||||||
|
await expect(drivingStatusSelect).toHaveValue(NO_GO);
|
||||||
|
|
||||||
|
await setMissionStatus(page, 'Imagery', GO);
|
||||||
|
await expect(imageryStatusSelect).toHaveValue(GO);
|
||||||
|
await expect(commandingStatusSelect).toHaveValue(NO_GO);
|
||||||
|
await expect(drivingStatusSelect).toHaveValue(NO_GO);
|
||||||
|
|
||||||
|
await setMissionStatus(page, 'Commanding', GO);
|
||||||
|
await expect(imageryStatusSelect).toHaveValue(GO);
|
||||||
|
await expect(commandingStatusSelect).toHaveValue(GO);
|
||||||
|
await expect(drivingStatusSelect).toHaveValue(NO_GO);
|
||||||
|
|
||||||
|
await setMissionStatus(page, 'Driving', GO);
|
||||||
|
await expect(imageryStatusSelect).toHaveValue(GO);
|
||||||
|
await expect(commandingStatusSelect).toHaveValue(GO);
|
||||||
|
await expect(drivingStatusSelect).toHaveValue(GO);
|
||||||
|
|
||||||
|
await setMissionStatus(page, 'Imagery', NO_GO);
|
||||||
|
await expect(imageryStatusSelect).toHaveValue(NO_GO);
|
||||||
|
await expect(commandingStatusSelect).toHaveValue(GO);
|
||||||
|
await expect(drivingStatusSelect).toHaveValue(GO);
|
||||||
|
|
||||||
|
await setMissionStatus(page, 'Commanding', NO_GO);
|
||||||
|
await expect(imageryStatusSelect).toHaveValue(NO_GO);
|
||||||
|
await expect(commandingStatusSelect).toHaveValue(NO_GO);
|
||||||
|
await expect(drivingStatusSelect).toHaveValue(GO);
|
||||||
|
|
||||||
|
await setMissionStatus(page, 'Driving', NO_GO);
|
||||||
|
await expect(imageryStatusSelect).toHaveValue(NO_GO);
|
||||||
|
await expect(commandingStatusSelect).toHaveValue(NO_GO);
|
||||||
|
await expect(drivingStatusSelect).toHaveValue(NO_GO);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('@playwright/test').Page} page
|
||||||
|
* @param {'Commanding'|'Imagery'|'Driving'} action
|
||||||
|
* @param {'0'|'1'} status
|
||||||
|
*/
|
||||||
|
async function setMissionStatus(page, action, status) {
|
||||||
|
await page.getByRole('combobox', { name: action }).selectOption(status);
|
||||||
|
await expect(
|
||||||
|
page.getByRole('alert').filter({ hasText: 'Successfully set mission status' })
|
||||||
|
).toBeVisible();
|
||||||
|
await page.getByLabel('Dismiss').click();
|
||||||
|
}
|
57
e2e/tests/visual-a11y/missionStatus.visual.spec.js
Normal file
57
e2e/tests/visual-a11y/missionStatus.visual.spec.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
import percySnapshot from '@percy/playwright';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
|
import { expect, scanForA11yViolations, test } from '../../avpFixtures.js';
|
||||||
|
|
||||||
|
test.describe('Mission Status Visual Tests @a11y', () => {
|
||||||
|
const GO = '1';
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await page.addInitScript({
|
||||||
|
path: fileURLToPath(new URL('../../helper/addInitExampleUser.js', import.meta.url))
|
||||||
|
});
|
||||||
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
|
await expect(page.getByText('Select Role')).toBeVisible();
|
||||||
|
// Description should be empty https://github.com/nasa/openmct/issues/6978
|
||||||
|
await expect(page.locator('c-message__action-text')).toBeHidden();
|
||||||
|
// set role
|
||||||
|
await page.getByRole('button', { name: 'Select', exact: true }).click();
|
||||||
|
// dismiss role confirmation popup
|
||||||
|
await page.getByRole('button', { name: 'Dismiss' }).click();
|
||||||
|
});
|
||||||
|
test('Mission status panel', async ({ page, theme }) => {
|
||||||
|
await page.getByLabel('Toggle Mission Status Panel').click();
|
||||||
|
await expect(page.getByRole('dialog', { name: 'User Control Panel' })).toBeVisible();
|
||||||
|
await percySnapshot(page, `Mission status panel w/ default statuses (theme: '${theme}')`);
|
||||||
|
await page.getByRole('combobox', { name: 'Commanding' }).selectOption(GO);
|
||||||
|
await expect(
|
||||||
|
page.getByRole('alert').filter({ hasText: 'Successfully set mission status' })
|
||||||
|
).toBeVisible();
|
||||||
|
await page.getByLabel('Dismiss').click();
|
||||||
|
await percySnapshot(page, `Mission status panel w/ non-default status (theme: '${theme}')`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await scanForA11yViolations(page, testInfo.title);
|
||||||
|
});
|
||||||
|
});
|
@ -60,10 +60,22 @@ const STATUSES = [
|
|||||||
statusFgColor: '#fff'
|
statusFgColor: '#fff'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const MISSION_STATUSES = [
|
||||||
|
{
|
||||||
|
key: 0,
|
||||||
|
label: 'NO GO'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 1,
|
||||||
|
label: 'GO'
|
||||||
|
}
|
||||||
|
];
|
||||||
/**
|
/**
|
||||||
* @implements {StatusUserProvider}
|
* @implements {StatusUserProvider}
|
||||||
*/
|
*/
|
||||||
export default class ExampleUserProvider extends EventEmitter {
|
export default class ExampleUserProvider extends EventEmitter {
|
||||||
|
#actionToStatusMap;
|
||||||
constructor(
|
constructor(
|
||||||
openmct,
|
openmct,
|
||||||
{ statusRoles } = {
|
{ statusRoles } = {
|
||||||
@ -73,6 +85,11 @@ export default class ExampleUserProvider extends EventEmitter {
|
|||||||
super();
|
super();
|
||||||
|
|
||||||
this.openmct = openmct;
|
this.openmct = openmct;
|
||||||
|
this.#actionToStatusMap = {
|
||||||
|
Imagery: MISSION_STATUSES[0],
|
||||||
|
Commanding: MISSION_STATUSES[0],
|
||||||
|
Driving: MISSION_STATUSES[0]
|
||||||
|
};
|
||||||
this.user = undefined;
|
this.user = undefined;
|
||||||
this.loggedIn = false;
|
this.loggedIn = false;
|
||||||
this.autoLoginUser = undefined;
|
this.autoLoginUser = undefined;
|
||||||
@ -112,7 +129,7 @@ export default class ExampleUserProvider extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
canSetMissionStatus() {
|
canSetMissionStatus() {
|
||||||
return Promise.resolve(false);
|
return Promise.resolve(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasRole(roleId) {
|
hasRole(roleId) {
|
||||||
@ -127,6 +144,28 @@ export default class ExampleUserProvider extends EventEmitter {
|
|||||||
return this.user.getRoles();
|
return this.user.getRoles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPossibleMissionActions() {
|
||||||
|
return Promise.resolve(Object.keys(this.#actionToStatusMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleMissionActionStatuses() {
|
||||||
|
return Promise.resolve(MISSION_STATUSES);
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatusForMissionAction(action) {
|
||||||
|
return Promise.resolve(this.#actionToStatusMap[action]);
|
||||||
|
}
|
||||||
|
|
||||||
|
setStatusForMissionAction(action, status) {
|
||||||
|
this.#actionToStatusMap[action] = status;
|
||||||
|
this.emit('missionStatusChange', {
|
||||||
|
action,
|
||||||
|
status
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
getAllStatusRoles() {
|
getAllStatusRoles() {
|
||||||
return Promise.resolve(this.statusRoles);
|
return Promise.resolve(this.statusRoles);
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<div class="l-grid-view" role="grid">
|
<div class="l-grid-view" role="grid" :aria-label="`${domainObject.name} Grid View`">
|
||||||
<grid-item
|
<grid-item
|
||||||
v-for="(item, index) in items"
|
v-for="(item, index) in items"
|
||||||
:key="index"
|
:key="index"
|
||||||
@ -38,6 +38,6 @@ import GridItem from './GridItem.vue';
|
|||||||
export default {
|
export default {
|
||||||
components: { GridItem },
|
components: { GridItem },
|
||||||
mixins: [compositionLoader],
|
mixins: [compositionLoader],
|
||||||
inject: ['openmct']
|
inject: ['openmct', 'domainObject']
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<div class="c-user-control-panel__header">
|
<div class="c-user-control-panel__header">
|
||||||
<div class="c-user-control-panel__title">Mission Status</div>
|
<div class="c-user-control-panel__title">Mission Status</div>
|
||||||
<button
|
<button
|
||||||
aria-label="Close"
|
aria-label="Close Mission Status Panel"
|
||||||
class="c-icon-button c-icon-button--sm t-close-btn icon-x"
|
class="c-icon-button c-icon-button--sm t-close-btn icon-x"
|
||||||
@click.stop="onDismiss"
|
@click.stop="onDismiss"
|
||||||
></button>
|
></button>
|
||||||
@ -18,7 +18,7 @@
|
|||||||
<select
|
<select
|
||||||
:id="action"
|
:id="action"
|
||||||
v-model="missionActionStatusMap[action]"
|
v-model="missionActionStatusMap[action]"
|
||||||
name="setMissionActionStatus"
|
:name="`${action} status`"
|
||||||
@change="onChangeStatus(action)"
|
@change="onChangeStatus(action)"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
|
@ -33,11 +33,24 @@
|
|||||||
<button v-if="availableRoles?.length > 1" @click.stop="promptForRoleSelection">
|
<button v-if="availableRoles?.length > 1" @click.stop="promptForRoleSelection">
|
||||||
Change Role
|
Change Role
|
||||||
</button>
|
</button>
|
||||||
<button v-if="canSetMissionStatus" @click.stop="togglePopup">Mission Status</button>
|
<button
|
||||||
|
v-if="canSetMissionStatus"
|
||||||
|
aria-label="Toggle Mission Status Panel"
|
||||||
|
@click.stop="togglePopup"
|
||||||
|
>
|
||||||
|
Mission Status
|
||||||
|
</button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<Teleport to="body">
|
<Teleport to="body">
|
||||||
<div v-show="isPopupVisible" ref="popupRef" class="c-user-control-panel" :style="popupStyle">
|
<div
|
||||||
|
v-show="isPopupVisible"
|
||||||
|
ref="popupRef"
|
||||||
|
class="c-user-control-panel"
|
||||||
|
role="dialog"
|
||||||
|
aria-label="User Control Panel"
|
||||||
|
:style="popupStyle"
|
||||||
|
>
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<template #default>
|
<template #default>
|
||||||
<MissionStatusPopup v-if="canSetMissionStatus" @dismiss="togglePopup" />
|
<MissionStatusPopup v-if="canSetMissionStatus" @dismiss="togglePopup" />
|
||||||
|
Reference in New Issue
Block a user