diff --git a/awx/ui_next/src/components/ContentError/ContentError.jsx b/awx/ui_next/src/components/ContentError/ContentError.jsx index f6fb2d825c..7f766f7e91 100644 --- a/awx/ui_next/src/components/ContentError/ContentError.jsx +++ b/awx/ui_next/src/components/ContentError/ContentError.jsx @@ -25,7 +25,6 @@ function ContentError({ error, children, isNotFound, i18n }) { return null; } } - console.error(error); const is404 = isNotFound || (error && error.response && error.response.status === 404); const is401 = error && error.response && error.response.status === 401; diff --git a/awx/ui_next/src/components/LaunchPrompt/CredentialsStep.test.jsx b/awx/ui_next/src/components/LaunchPrompt/CredentialsStep.test.jsx new file mode 100644 index 0000000000..5038482125 --- /dev/null +++ b/awx/ui_next/src/components/LaunchPrompt/CredentialsStep.test.jsx @@ -0,0 +1,79 @@ +import React from 'react'; +import { act } from 'react-dom/test-utils'; +import { Formik } from 'formik'; +import { mountWithContexts } from '@testUtils/enzymeHelpers'; +import CredentialsStep from './CredentialsStep'; +import { CredentialsAPI, CredentialTypesAPI } from '@api'; + +jest.mock('@api/models/CredentialTypes'); +jest.mock('@api/models/Credentials'); + +const types = [ + { id: 1, kind: 'ssh', name: 'SSH' }, + { id: 2, kind: 'cloud', name: 'Ansible Tower' }, + { id: 3, kind: 'vault', name: 'Vault' }, +]; + +const credentials = [ + { id: 1, kind: 'cloud', name: 'Cred 1', url: 'www.google.com' }, + { id: 2, kind: 'ssh', name: 'Cred 2', url: 'www.google.com' }, + { id: 3, kind: 'Ansible', name: 'Cred 3', url: 'www.google.com' }, + { id: 4, kind: 'Machine', name: 'Cred 4', url: 'www.google.com' }, + { id: 5, kind: 'Machine', name: 'Cred 5', url: 'www.google.com' }, +]; + +describe('CredentialsStep', () => { + beforeEach(() => { + CredentialTypesAPI.loadAllTypes.mockResolvedValue(types); + CredentialsAPI.read.mockResolvedValue({ + data: { + results: credentials, + count: 5, + }, + }); + }); + + test('should load credentials', async () => { + let wrapper; + await act(async () => { + wrapper = mountWithContexts( + + + + ); + }); + wrapper.update(); + + expect(CredentialsAPI.read).toHaveBeenCalled(); + expect(wrapper.find('OptionsList').prop('options')).toEqual(credentials); + }); + + test('should load credentials for selected type', async () => { + let wrapper; + await act(async () => { + wrapper = mountWithContexts( + + + + ); + }); + wrapper.update(); + + expect(CredentialsAPI.read).toHaveBeenCalledWith({ + credential_type: 1, + order_by: 'name', + page: 1, + page_size: 5, + }); + + await act(async () => { + wrapper.find('AnsibleSelect').invoke('onChange')({}, 2); + }); + expect(CredentialsAPI.read).toHaveBeenCalledWith({ + credential_type: 2, + order_by: 'name', + page: 1, + page_size: 5, + }); + }); +}); diff --git a/awx/ui_next/src/components/LaunchPrompt/LaunchPrompt.jsx b/awx/ui_next/src/components/LaunchPrompt/LaunchPrompt.jsx index 4a5a256ad7..ecf6c760c5 100644 --- a/awx/ui_next/src/components/LaunchPrompt/LaunchPrompt.jsx +++ b/awx/ui_next/src/components/LaunchPrompt/LaunchPrompt.jsx @@ -22,7 +22,7 @@ function LaunchPrompt({ config, resource, onLaunch, onCancel, i18n }) { if (config.ask_credential_on_launch) { initialValues.credentials = resource?.summary_fields?.credentials || []; steps.push({ - name: i18n._(t`Credential`), + name: i18n._(t`Credentials`), component: , }); } diff --git a/awx/ui_next/src/components/LaunchPrompt/LaunchPrompt.test.jsx b/awx/ui_next/src/components/LaunchPrompt/LaunchPrompt.test.jsx index edbb0cd7dc..3a490db6bc 100644 --- a/awx/ui_next/src/components/LaunchPrompt/LaunchPrompt.test.jsx +++ b/awx/ui_next/src/components/LaunchPrompt/LaunchPrompt.test.jsx @@ -3,6 +3,8 @@ import { act, isElementOfType } from 'react-dom/test-utils'; import { mountWithContexts } from '@testUtils/enzymeHelpers'; import LaunchPrompt from './LaunchPrompt'; import InventoryStep from './InventoryStep'; +import CredentialsStep from './CredentialsStep'; +import OtherPromptsStep from './OtherPromptsStep'; import PreviewStep from './PreviewStep'; import { InventoriesAPI } from '@api'; @@ -69,7 +71,7 @@ describe('LaunchPrompt', () => { expect(steps).toHaveLength(5); expect(steps[0].name).toEqual('Inventory'); - expect(steps[1].name).toEqual('Credential'); + expect(steps[1].name).toEqual('Credentials'); expect(steps[2].name).toEqual('Other Prompts'); expect(steps[3].name).toEqual('Survey'); expect(steps[4].name).toEqual('Preview'); @@ -97,4 +99,50 @@ describe('LaunchPrompt', () => { expect(isElementOfType(steps[0].component, InventoryStep)).toEqual(true); expect(isElementOfType(steps[1].component, PreviewStep)).toEqual(true); }); + + test('should add credentials step', async () => { + let wrapper; + await act(async () => { + wrapper = mountWithContexts( + + ); + }); + const steps = wrapper.find('Wizard').prop('steps'); + + expect(steps).toHaveLength(2); + expect(steps[0].name).toEqual('Credentials'); + expect(isElementOfType(steps[0].component, CredentialsStep)).toEqual(true); + expect(isElementOfType(steps[1].component, PreviewStep)).toEqual(true); + }); + + test('should add other prompts step', async () => { + let wrapper; + await act(async () => { + wrapper = mountWithContexts( + + ); + }); + const steps = wrapper.find('Wizard').prop('steps'); + + expect(steps).toHaveLength(2); + expect(steps[0].name).toEqual('Other Prompts'); + expect(isElementOfType(steps[0].component, OtherPromptsStep)).toEqual(true); + expect(isElementOfType(steps[1].component, PreviewStep)).toEqual(true); + }); }); diff --git a/awx/ui_next/src/components/LaunchPrompt/OtherPromptsStep.jsx b/awx/ui_next/src/components/LaunchPrompt/OtherPromptsStep.jsx index 09445e63e9..0989368652 100644 --- a/awx/ui_next/src/components/LaunchPrompt/OtherPromptsStep.jsx +++ b/awx/ui_next/src/components/LaunchPrompt/OtherPromptsStep.jsx @@ -7,6 +7,17 @@ import FormField, { FieldTooltip } from '@components/FormField'; import { TagMultiSelect } from '@components/MultiSelect'; import AnsibleSelect from '@components/AnsibleSelect'; import { VariablesField } from '@components/CodeMirrorInput'; +import styled from 'styled-components'; + +const FieldHeader = styled.div` + display: flex; + justify-content: space-between; + padding-bottom: var(--pf-c-form__label--PaddingBottom); + + label { + --pf-c-form__label--PaddingBottom: 0px; + } +`; function OtherPromptsStep({ config, i18n }) { return ( @@ -131,12 +142,28 @@ function VerbosityField({ i18n }) { function ShowChangesToggle({ i18n }) { const [field, , helpers] = useField('diff_mode'); return ( - + + + {' '} + + + + ); } @@ -150,23 +177,4 @@ function TagField({ id, name, label, tooltip }) { ); } -/* - tooltips: - verbosity: Control the level of output ansible will produce as the playbook executes. - job tags: Tags are useful when you have a large playbook, and you want to run a specific part of a play or task. Use commas to separate multiple tags. Refer to Ansible Tower documentation for details on the usage of tags. - skip tags: Skip tags are useful when you have a large playbook, and you want to skip specific parts of a play or task. Use commas to separate multiple tags. Refer to Ansible Tower documentation for details on the usage of tags. - show changes: If enabled, show the changes made by Ansible tasks, where supported. This is equivalent to Ansible’s --diff mode. - extra variables: Pass extra command line variables to the playbook. This is the -e or --extra-vars command line parameter for ansible-playbook. Provide key/value pairs using either YAML or JSON. - - JSON: - { - "somevar": "somevalue", - "password": "magic" - } - YAML: - --- - somevar: somevalue - password: magic -*/ - export default withI18n()(OtherPromptsStep); diff --git a/awx/ui_next/src/components/LaunchPrompt/OtherPromptsStep.test.jsx b/awx/ui_next/src/components/LaunchPrompt/OtherPromptsStep.test.jsx new file mode 100644 index 0000000000..58f0856131 --- /dev/null +++ b/awx/ui_next/src/components/LaunchPrompt/OtherPromptsStep.test.jsx @@ -0,0 +1,90 @@ +import React from 'react'; +import { act } from 'react-dom/test-utils'; +import { Formik } from 'formik'; +import { mountWithContexts } from '@testUtils/enzymeHelpers'; +import OtherPromptsStep from './OtherPromptsStep'; + +describe('OtherPromptsStep', () => { + test('should render job type field', async () => { + let wrapper; + await act(async () => { + wrapper = mountWithContexts( + + + + ); + }); + + expect(wrapper.find('JobTypeField')).toHaveLength(1); + expect( + wrapper.find('JobTypeField AnsibleSelect').prop('data') + ).toHaveLength(3); + expect(wrapper.find('JobTypeField AnsibleSelect').prop('value')).toEqual( + 'run' + ); + }); + + test('should render limit field', async () => { + let wrapper; + await act(async () => { + wrapper = mountWithContexts( + + + + ); + }); + + expect(wrapper.find('FormField#prompt-limit')).toHaveLength(1); + expect(wrapper.find('FormField#prompt-limit input').prop('name')).toEqual( + 'limit' + ); + }); + + test('should render verbosity field', async () => { + let wrapper; + await act(async () => { + wrapper = mountWithContexts( + + + + ); + }); + + expect(wrapper.find('VerbosityField')).toHaveLength(1); + expect( + wrapper.find('VerbosityField AnsibleSelect').prop('data') + ).toHaveLength(5); + }); + + test('should render show changes toggle', async () => { + let wrapper; + await act(async () => { + wrapper = mountWithContexts( + + + + ); + }); + + expect(wrapper.find('ShowChangesToggle')).toHaveLength(1); + expect(wrapper.find('ShowChangesToggle Switch').prop('isChecked')).toEqual( + true + ); + }); +});