mirror of
https://github.com/ansible/awx.git
synced 2026-03-09 13:39:27 -02:30
fixes bug where one can launch erronously, adds tests for that bug
This commit is contained in:
@@ -1,13 +1,26 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
|
import { ExclamationCircleIcon as PFExclamationCircleIcon } from '@patternfly/react-icons';
|
||||||
|
import { Tooltip } from '@patternfly/react-core';
|
||||||
import { withFormik, useFormikContext } from 'formik';
|
import { withFormik, useFormikContext } from 'formik';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import styled from 'styled-components';
|
||||||
import Wizard from '../Wizard';
|
import Wizard from '../Wizard';
|
||||||
import AdHocCredentialStep from './AdHocCredentialStep';
|
import AdHocCredentialStep from './AdHocCredentialStep';
|
||||||
import AdHocDetailsStep from './AdHocDetailsStep';
|
import AdHocDetailsStep from './AdHocDetailsStep';
|
||||||
|
|
||||||
|
const AlertText = styled.div`
|
||||||
|
color: var(--pf-global--danger-color--200);
|
||||||
|
font-weight: var(--pf-global--FontWeight--bold);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ExclamationCircleIcon = styled(PFExclamationCircleIcon)`
|
||||||
|
margin-left: 10px;
|
||||||
|
color: var(--pf-global--danger-color--100);
|
||||||
|
`;
|
||||||
|
|
||||||
function AdHocCommandsWizard({
|
function AdHocCommandsWizard({
|
||||||
onLaunch,
|
onLaunch,
|
||||||
i18n,
|
i18n,
|
||||||
@@ -19,7 +32,7 @@ function AdHocCommandsWizard({
|
|||||||
const [currentStepId, setCurrentStepId] = useState(1);
|
const [currentStepId, setCurrentStepId] = useState(1);
|
||||||
const [enableLaunch, setEnableLaunch] = useState(false);
|
const [enableLaunch, setEnableLaunch] = useState(false);
|
||||||
|
|
||||||
const { values } = useFormikContext();
|
const { values, errors, touched } = useFormikContext();
|
||||||
|
|
||||||
const enabledNextOnDetailsStep = () => {
|
const enabledNextOnDetailsStep = () => {
|
||||||
if (!values.module_name) {
|
if (!values.module_name) {
|
||||||
@@ -36,11 +49,26 @@ function AdHocCommandsWizard({
|
|||||||
}
|
}
|
||||||
return undefined; // makes the linter happy;
|
return undefined; // makes the linter happy;
|
||||||
};
|
};
|
||||||
|
const hasDetailsStepError = errors.module_args && touched.module_args;
|
||||||
|
|
||||||
const steps = [
|
const steps = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
key: 1,
|
key: 1,
|
||||||
name: i18n._(t`Details`),
|
name: hasDetailsStepError ? (
|
||||||
|
<AlertText>
|
||||||
|
{i18n._(t`Details`)}
|
||||||
|
<Tooltip
|
||||||
|
position="right"
|
||||||
|
content={i18n._(t`This step contains errors`)}
|
||||||
|
trigger="click mouseenter focus"
|
||||||
|
>
|
||||||
|
<ExclamationCircleIcon />
|
||||||
|
</Tooltip>
|
||||||
|
</AlertText>
|
||||||
|
) : (
|
||||||
|
i18n._(t`Details`)
|
||||||
|
),
|
||||||
component: (
|
component: (
|
||||||
<AdHocDetailsStep
|
<AdHocDetailsStep
|
||||||
moduleOptions={moduleOptions}
|
moduleOptions={moduleOptions}
|
||||||
@@ -60,7 +88,7 @@ function AdHocCommandsWizard({
|
|||||||
onEnableLaunch={() => setEnableLaunch(true)}
|
onEnableLaunch={() => setEnableLaunch(true)}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
enableNext: enableLaunch,
|
enableNext: enableLaunch && Object.values(errors).length === 0,
|
||||||
nextButtonText: i18n._(t`Launch`),
|
nextButtonText: i18n._(t`Launch`),
|
||||||
canJumpTo: currentStepId >= 2,
|
canJumpTo: currentStepId >= 2,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -148,6 +148,20 @@ describe('<AdHocCommandsWizard/>', () => {
|
|||||||
|
|
||||||
expect(onLaunch).toHaveBeenCalled();
|
expect(onLaunch).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
test('should show error in navigation bar', async () => {
|
||||||
|
await waitForElement(wrapper, 'WizardNavItem', el => el.length > 0);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('AnsibleSelect[name="module_name"]').prop('onChange')(
|
||||||
|
{},
|
||||||
|
'command'
|
||||||
|
);
|
||||||
|
wrapper.find('input#module_args').simulate('change', {
|
||||||
|
target: { value: '', name: 'module_args' },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
waitForElement(wrapper, 'ExclamationCircleIcon', el => el.length > 0);
|
||||||
|
});
|
||||||
|
|
||||||
test('expect credential step to throw error', async () => {
|
test('expect credential step to throw error', async () => {
|
||||||
CredentialsAPI.read.mockRejectedValue(
|
CredentialsAPI.read.mockRejectedValue(
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ function AdHocCredentialStep({ i18n, credentialTypeId, onEnableLaunch }) {
|
|||||||
<FormGroup
|
<FormGroup
|
||||||
fieldId="credential"
|
fieldId="credential"
|
||||||
label={i18n._(t`Machine Credential`)}
|
label={i18n._(t`Machine Credential`)}
|
||||||
|
aria-label={i18n._(t`Machine Credential`)}
|
||||||
isRequired
|
isRequired
|
||||||
validated={
|
validated={
|
||||||
!credentialMeta.touched || !credentialMeta.error ? 'default' : 'error'
|
!credentialMeta.touched || !credentialMeta.error ? 'default' : 'error'
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ function AdHocDetailsStep({ i18n, verbosityOptions, moduleOptions }) {
|
|||||||
moduleNameField.value === 'command' || moduleNameField.value === 'shell';
|
moduleNameField.value === 'command' || moduleNameField.value === 'shell';
|
||||||
const [, argumentsMeta, argumentsHelpers] = useField({
|
const [, argumentsMeta, argumentsHelpers] = useField({
|
||||||
name: 'module_args',
|
name: 'module_args',
|
||||||
validate: argumentsRequired ? required(null, i18n) : null,
|
validate: argumentsRequired && required(null, i18n),
|
||||||
});
|
});
|
||||||
|
|
||||||
const isValid = !argumentsMeta.error || !argumentsMeta.touched;
|
const isValid = !argumentsMeta.error || !argumentsMeta.touched;
|
||||||
@@ -58,6 +58,7 @@ function AdHocDetailsStep({ i18n, verbosityOptions, moduleOptions }) {
|
|||||||
<FormFullWidthLayout>
|
<FormFullWidthLayout>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
fieldId="module_name"
|
fieldId="module_name"
|
||||||
|
aria-label={i18n._(t`Module`)}
|
||||||
label={i18n._(t`Module`)}
|
label={i18n._(t`Module`)}
|
||||||
isRequired
|
isRequired
|
||||||
helperTextInvalid={moduleNameMeta.error}
|
helperTextInvalid={moduleNameMeta.error}
|
||||||
@@ -103,9 +104,11 @@ function AdHocDetailsStep({ i18n, verbosityOptions, moduleOptions }) {
|
|||||||
<FormField
|
<FormField
|
||||||
id="module_args"
|
id="module_args"
|
||||||
name="module_args"
|
name="module_args"
|
||||||
|
aria-label={i18n._(t`Arguments`)}
|
||||||
type="text"
|
type="text"
|
||||||
label={i18n._(t`Arguments`)}
|
label={i18n._(t`Arguments`)}
|
||||||
validated={isValid ? 'default' : 'error'}
|
validated={isValid ? 'default' : 'error'}
|
||||||
|
onBlur={() => argumentsHelpers.setTouched(true)}
|
||||||
placeholder={i18n._(t`Enter arguments`)}
|
placeholder={i18n._(t`Enter arguments`)}
|
||||||
isRequired={
|
isRequired={
|
||||||
moduleNameField.value === 'command' ||
|
moduleNameField.value === 'command' ||
|
||||||
@@ -133,6 +136,7 @@ function AdHocDetailsStep({ i18n, verbosityOptions, moduleOptions }) {
|
|||||||
/>
|
/>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
fieldId="verbosity"
|
fieldId="verbosity"
|
||||||
|
aria-label={i18n._(t`Verbosity`)}
|
||||||
label={i18n._(t`Verbosity`)}
|
label={i18n._(t`Verbosity`)}
|
||||||
isRequired
|
isRequired
|
||||||
validated={
|
validated={
|
||||||
@@ -164,6 +168,7 @@ function AdHocDetailsStep({ i18n, verbosityOptions, moduleOptions }) {
|
|||||||
name="limit"
|
name="limit"
|
||||||
type="text"
|
type="text"
|
||||||
label={i18n._(t`Limit`)}
|
label={i18n._(t`Limit`)}
|
||||||
|
aria-label={i18n._(t`Limit`)}
|
||||||
tooltip={
|
tooltip={
|
||||||
<span>
|
<span>
|
||||||
{i18n._(
|
{i18n._(
|
||||||
@@ -185,6 +190,7 @@ function AdHocDetailsStep({ i18n, verbosityOptions, moduleOptions }) {
|
|||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
label={i18n._(t`Forks`)}
|
label={i18n._(t`Forks`)}
|
||||||
|
aria-label={i18n._(t`Forks`)}
|
||||||
tooltip={
|
tooltip={
|
||||||
<span>
|
<span>
|
||||||
{i18n._(
|
{i18n._(
|
||||||
@@ -203,6 +209,7 @@ function AdHocDetailsStep({ i18n, verbosityOptions, moduleOptions }) {
|
|||||||
<FormColumnLayout>
|
<FormColumnLayout>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={i18n._(t`Show changes`)}
|
label={i18n._(t`Show changes`)}
|
||||||
|
aria-label={i18n._(t`Show changes`)}
|
||||||
labelIcon={
|
labelIcon={
|
||||||
<FieldTooltip
|
<FieldTooltip
|
||||||
content={i18n._(
|
content={i18n._(
|
||||||
@@ -300,6 +307,7 @@ function AdHocDetailsStep({ i18n, verbosityOptions, moduleOptions }) {
|
|||||||
</TooltipWrapper>
|
</TooltipWrapper>
|
||||||
}
|
}
|
||||||
label={i18n._(t`Extra variables`)}
|
label={i18n._(t`Extra variables`)}
|
||||||
|
aria-label={i18n._(t`Extra variables`)}
|
||||||
/>
|
/>
|
||||||
</FormFullWidthLayout>
|
</FormFullWidthLayout>
|
||||||
</FormColumnLayout>
|
</FormColumnLayout>
|
||||||
|
|||||||
Reference in New Issue
Block a user