mirror of
https://github.com/nasa/openmct.git
synced 2025-02-20 01:16:46 +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:
parent
eeb8e9704b
commit
539b43325a
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'
|
||||
}
|
||||
];
|
||||
|
||||
const MISSION_STATUSES = [
|
||||
{
|
||||
key: 0,
|
||||
label: 'NO GO'
|
||||
},
|
||||
{
|
||||
key: 1,
|
||||
label: 'GO'
|
||||
}
|
||||
];
|
||||
/**
|
||||
* @implements {StatusUserProvider}
|
||||
*/
|
||||
export default class ExampleUserProvider extends EventEmitter {
|
||||
#actionToStatusMap;
|
||||
constructor(
|
||||
openmct,
|
||||
{ statusRoles } = {
|
||||
@ -73,6 +85,11 @@ export default class ExampleUserProvider extends EventEmitter {
|
||||
super();
|
||||
|
||||
this.openmct = openmct;
|
||||
this.#actionToStatusMap = {
|
||||
Imagery: MISSION_STATUSES[0],
|
||||
Commanding: MISSION_STATUSES[0],
|
||||
Driving: MISSION_STATUSES[0]
|
||||
};
|
||||
this.user = undefined;
|
||||
this.loggedIn = false;
|
||||
this.autoLoginUser = undefined;
|
||||
@ -112,7 +129,7 @@ export default class ExampleUserProvider extends EventEmitter {
|
||||
}
|
||||
|
||||
canSetMissionStatus() {
|
||||
return Promise.resolve(false);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
hasRole(roleId) {
|
||||
@ -127,6 +144,28 @@ export default class ExampleUserProvider extends EventEmitter {
|
||||
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() {
|
||||
return Promise.resolve(this.statusRoles);
|
||||
}
|
||||
|
@ -20,7 +20,7 @@
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<template>
|
||||
<div class="l-grid-view" role="grid">
|
||||
<div class="l-grid-view" role="grid" :aria-label="`${domainObject.name} Grid View`">
|
||||
<grid-item
|
||||
v-for="(item, index) in items"
|
||||
:key="index"
|
||||
@ -38,6 +38,6 @@ import GridItem from './GridItem.vue';
|
||||
export default {
|
||||
components: { GridItem },
|
||||
mixins: [compositionLoader],
|
||||
inject: ['openmct']
|
||||
inject: ['openmct', 'domainObject']
|
||||
};
|
||||
</script>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<div class="c-user-control-panel__header">
|
||||
<div class="c-user-control-panel__title">Mission Status</div>
|
||||
<button
|
||||
aria-label="Close"
|
||||
aria-label="Close Mission Status Panel"
|
||||
class="c-icon-button c-icon-button--sm t-close-btn icon-x"
|
||||
@click.stop="onDismiss"
|
||||
></button>
|
||||
@ -18,7 +18,7 @@
|
||||
<select
|
||||
:id="action"
|
||||
v-model="missionActionStatusMap[action]"
|
||||
name="setMissionActionStatus"
|
||||
:name="`${action} status`"
|
||||
@change="onChangeStatus(action)"
|
||||
>
|
||||
<option
|
||||
|
@ -33,11 +33,24 @@
|
||||
<button v-if="availableRoles?.length > 1" @click.stop="promptForRoleSelection">
|
||||
Change Role
|
||||
</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>
|
||||
</div>
|
||||
<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>
|
||||
<template #default>
|
||||
<MissionStatusPopup v-if="canSetMissionStatus" @dismiss="togglePopup" />
|
||||
|
Loading…
x
Reference in New Issue
Block a user