foreman_remote_execution 4.5.1 → 4.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/.github/workflows/ruby_ci.yml +7 -0
 - data/app/controllers/ui_job_wizard_controller.rb +7 -0
 - data/app/graphql/types/job_invocation.rb +16 -0
 - data/app/helpers/concerns/foreman_remote_execution/hosts_helper_extensions.rb +5 -1
 - data/app/helpers/remote_execution_helper.rb +9 -3
 - data/app/lib/actions/remote_execution/run_hosts_job.rb +1 -1
 - data/app/models/job_invocation_composer.rb +3 -3
 - data/app/models/job_template.rb +1 -1
 - data/app/models/remote_execution_feature.rb +5 -1
 - data/app/models/remote_execution_provider.rb +1 -1
 - data/app/views/templates/ssh/module_action.erb +1 -0
 - data/app/views/templates/ssh/power_action.erb +2 -0
 - data/app/views/templates/ssh/puppet_run_once.erb +1 -0
 - data/foreman_remote_execution.gemspec +2 -4
 - data/lib/foreman_remote_execution/engine.rb +3 -0
 - data/lib/foreman_remote_execution/version.rb +1 -1
 - data/test/graphql/queries/job_invocation_query_test.rb +31 -0
 - data/test/graphql/queries/job_invocations_query_test.rb +35 -0
 - data/test/unit/concerns/host_extensions_test.rb +4 -4
 - data/test/unit/input_template_renderer_test.rb +1 -89
 - data/test/unit/job_invocation_composer_test.rb +1 -12
 - data/webpack/JobWizard/JobWizard.js +28 -8
 - data/webpack/JobWizard/JobWizard.scss +39 -0
 - data/webpack/JobWizard/JobWizardConstants.js +10 -0
 - data/webpack/JobWizard/JobWizardSelectors.js +9 -0
 - data/webpack/JobWizard/__tests__/fixtures.js +104 -2
 - data/webpack/JobWizard/__tests__/integration.test.js +13 -85
 - data/webpack/JobWizard/steps/AdvancedFields/AdvancedFields.js +21 -4
 - data/webpack/JobWizard/steps/AdvancedFields/DescriptionField.js +67 -0
 - data/webpack/JobWizard/steps/AdvancedFields/Fields.js +73 -59
 - data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +135 -16
 - data/webpack/JobWizard/steps/AdvancedFields/__tests__/DescriptionField.test.js +23 -0
 - data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.test.js +122 -51
 - data/webpack/JobWizard/steps/Schedule/QueryType.js +48 -0
 - data/webpack/JobWizard/steps/Schedule/RepeatOn.js +61 -0
 - data/webpack/JobWizard/steps/Schedule/ScheduleType.js +25 -0
 - data/webpack/JobWizard/steps/Schedule/StartEndDates.js +51 -0
 - data/webpack/JobWizard/steps/Schedule/__tests__/StartEndDates.test.js +22 -0
 - data/webpack/JobWizard/steps/Schedule/index.js +41 -0
 - data/webpack/JobWizard/steps/form/FormHelpers.js +1 -0
 - data/webpack/JobWizard/steps/form/Formatter.js +149 -0
 - data/webpack/JobWizard/steps/form/NumberInput.js +33 -0
 - data/webpack/JobWizard/steps/form/SelectField.js +14 -2
 - data/webpack/JobWizard/steps/form/__tests__/Formatter.test.js.example +76 -0
 - data/webpack/__mocks__/foremanReact/components/SearchBar.js +18 -1
 - data/webpack/react_app/components/RecentJobsCard/JobStatusIcon.js +43 -0
 - data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +72 -66
 - data/webpack/react_app/components/RecentJobsCard/RecentJobsTable.js +98 -0
 - data/webpack/react_app/components/RecentJobsCard/constants.js +11 -0
 - data/webpack/react_app/components/RecentJobsCard/styles.scss +11 -0
 - data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +1 -0
 - data/webpack/react_app/extend/fillRecentJobsCard.js +1 -1
 - metadata +23 -27
 - data/webpack/JobWizard/__tests__/JobWizard.test.js +0 -13
 - data/webpack/JobWizard/__tests__/__snapshots__/JobWizard.test.js.snap +0 -32
 - data/webpack/JobWizard/steps/AdvancedFields/__tests__/__snapshots__/AdvancedFields.test.js.snap +0 -249
 - data/webpack/JobWizard/steps/CategoryAndTemplate/__snapshots__/CategoryAndTemplate.test.js.snap +0 -113
 - data/webpack/JobWizard/steps/form/__tests__/GroupedSelectField.test.js +0 -38
 - data/webpack/JobWizard/steps/form/__tests__/SelectField.test.js +0 -23
 - data/webpack/JobWizard/steps/form/__tests__/__snapshots__/GroupedSelectField.test.js.snap +0 -37
 - data/webpack/JobWizard/steps/form/__tests__/__snapshots__/SelectField.test.js.snap +0 -23
 - data/webpack/react_app/components/RecentJobsCard/styles.css +0 -15
 
| 
         @@ -0,0 +1,67 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import React, { useState } from 'react';
         
     | 
| 
      
 2 
     | 
    
         
            +
            import PropTypes from 'prop-types';
         
     | 
| 
      
 3 
     | 
    
         
            +
            import { FormGroup, TextInput, Button } from '@patternfly/react-core';
         
     | 
| 
      
 4 
     | 
    
         
            +
            import { translate as __ } from 'foremanReact/common/I18n';
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            export const DescriptionField = ({ inputs, value, setValue }) => {
         
     | 
| 
      
 7 
     | 
    
         
            +
              const generateDesc = () => {
         
     | 
| 
      
 8 
     | 
    
         
            +
                let newDesc = value;
         
     | 
| 
      
 9 
     | 
    
         
            +
                if (value) {
         
     | 
| 
      
 10 
     | 
    
         
            +
                  const re = new RegExp('%\\{([^\\}]+)\\}', 'gm');
         
     | 
| 
      
 11 
     | 
    
         
            +
                  const results = [...newDesc.matchAll(re)].map(result => ({
         
     | 
| 
      
 12 
     | 
    
         
            +
                    name: result[1],
         
     | 
| 
      
 13 
     | 
    
         
            +
                    text: result[0],
         
     | 
| 
      
 14 
     | 
    
         
            +
                  }));
         
     | 
| 
      
 15 
     | 
    
         
            +
                  results.forEach(result => {
         
     | 
| 
      
 16 
     | 
    
         
            +
                    newDesc = newDesc.replace(
         
     | 
| 
      
 17 
     | 
    
         
            +
                      result.text,
         
     | 
| 
      
 18 
     | 
    
         
            +
                      // TODO: Replace with the value of the input from Target Hosts step
         
     | 
| 
      
 19 
     | 
    
         
            +
                      inputs.find(input => input.name === result.name)?.name || result.text
         
     | 
| 
      
 20 
     | 
    
         
            +
                    );
         
     | 
| 
      
 21 
     | 
    
         
            +
                  });
         
     | 
| 
      
 22 
     | 
    
         
            +
                }
         
     | 
| 
      
 23 
     | 
    
         
            +
                return newDesc;
         
     | 
| 
      
 24 
     | 
    
         
            +
              };
         
     | 
| 
      
 25 
     | 
    
         
            +
              const [generatedDesc, setGeneratedDesc] = useState(generateDesc());
         
     | 
| 
      
 26 
     | 
    
         
            +
              const [isPreview, setIsPreview] = useState(true);
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
              const togglePreview = () => {
         
     | 
| 
      
 29 
     | 
    
         
            +
                setGeneratedDesc(generateDesc());
         
     | 
| 
      
 30 
     | 
    
         
            +
                setIsPreview(v => !v);
         
     | 
| 
      
 31 
     | 
    
         
            +
              };
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
              return (
         
     | 
| 
      
 34 
     | 
    
         
            +
                <FormGroup
         
     | 
| 
      
 35 
     | 
    
         
            +
                  label={__('Description')}
         
     | 
| 
      
 36 
     | 
    
         
            +
                  fieldId="description"
         
     | 
| 
      
 37 
     | 
    
         
            +
                  helperText={
         
     | 
| 
      
 38 
     | 
    
         
            +
                    <Button variant="link" isInline onClick={togglePreview}>
         
     | 
| 
      
 39 
     | 
    
         
            +
                      {isPreview
         
     | 
| 
      
 40 
     | 
    
         
            +
                        ? __('Edit job description template')
         
     | 
| 
      
 41 
     | 
    
         
            +
                        : __('Preview job description')}
         
     | 
| 
      
 42 
     | 
    
         
            +
                    </Button>
         
     | 
| 
      
 43 
     | 
    
         
            +
                  }
         
     | 
| 
      
 44 
     | 
    
         
            +
                >
         
     | 
| 
      
 45 
     | 
    
         
            +
                  {isPreview ? (
         
     | 
| 
      
 46 
     | 
    
         
            +
                    <TextInput id="description-preview" value={generatedDesc} isDisabled />
         
     | 
| 
      
 47 
     | 
    
         
            +
                  ) : (
         
     | 
| 
      
 48 
     | 
    
         
            +
                    <TextInput
         
     | 
| 
      
 49 
     | 
    
         
            +
                      type="text"
         
     | 
| 
      
 50 
     | 
    
         
            +
                      autoComplete="description"
         
     | 
| 
      
 51 
     | 
    
         
            +
                      id="description"
         
     | 
| 
      
 52 
     | 
    
         
            +
                      value={value}
         
     | 
| 
      
 53 
     | 
    
         
            +
                      onChange={newValue => setValue(newValue)}
         
     | 
| 
      
 54 
     | 
    
         
            +
                    />
         
     | 
| 
      
 55 
     | 
    
         
            +
                  )}
         
     | 
| 
      
 56 
     | 
    
         
            +
                </FormGroup>
         
     | 
| 
      
 57 
     | 
    
         
            +
              );
         
     | 
| 
      
 58 
     | 
    
         
            +
            };
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
            DescriptionField.propTypes = {
         
     | 
| 
      
 61 
     | 
    
         
            +
              inputs: PropTypes.array.isRequired,
         
     | 
| 
      
 62 
     | 
    
         
            +
              value: PropTypes.string,
         
     | 
| 
      
 63 
     | 
    
         
            +
              setValue: PropTypes.func.isRequired,
         
     | 
| 
      
 64 
     | 
    
         
            +
            };
         
     | 
| 
      
 65 
     | 
    
         
            +
            DescriptionField.defaultProps = {
         
     | 
| 
      
 66 
     | 
    
         
            +
              value: '',
         
     | 
| 
      
 67 
     | 
    
         
            +
            };
         
     | 
| 
         @@ -3,6 +3,8 @@ import PropTypes from 'prop-types'; 
     | 
|
| 
       3 
3 
     | 
    
         
             
            import { FormGroup, TextInput } from '@patternfly/react-core';
         
     | 
| 
       4 
4 
     | 
    
         
             
            import { translate as __ } from 'foremanReact/common/I18n';
         
     | 
| 
       5 
5 
     | 
    
         
             
            import { helpLabel } from '../form/FormHelpers';
         
     | 
| 
      
 6 
     | 
    
         
            +
            import { formatter } from '../form/Formatter';
         
     | 
| 
      
 7 
     | 
    
         
            +
            import { NumberInput } from '../form/NumberInput';
         
     | 
| 
       6 
8 
     | 
    
         | 
| 
       7 
9 
     | 
    
         
             
            export const EffectiveUserField = ({ value, setValue }) => (
         
     | 
| 
       8 
10 
     | 
    
         
             
              <FormGroup
         
     | 
| 
         @@ -26,25 +28,25 @@ export const EffectiveUserField = ({ value, setValue }) => ( 
     | 
|
| 
       26 
28 
     | 
    
         
             
            );
         
     | 
| 
       27 
29 
     | 
    
         | 
| 
       28 
30 
     | 
    
         
             
            export const TimeoutToKillField = ({ value, setValue }) => (
         
     | 
| 
       29 
     | 
    
         
            -
              < 
     | 
| 
       30 
     | 
    
         
            -
                 
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
                   
     | 
| 
       33 
     | 
    
         
            -
                     
     | 
| 
      
 31 
     | 
    
         
            +
              <NumberInput
         
     | 
| 
      
 32 
     | 
    
         
            +
                formProps={{
         
     | 
| 
      
 33 
     | 
    
         
            +
                  label: __('Timeout to kill'),
         
     | 
| 
      
 34 
     | 
    
         
            +
                  labelIcon: helpLabel(
         
     | 
| 
      
 35 
     | 
    
         
            +
                    __(
         
     | 
| 
      
 36 
     | 
    
         
            +
                      'Time in seconds from the start on the remote host after which the job should be killed.'
         
     | 
| 
      
 37 
     | 
    
         
            +
                    ),
         
     | 
| 
      
 38 
     | 
    
         
            +
                    'timeout-to-kill'
         
     | 
| 
       34 
39 
     | 
    
         
             
                  ),
         
     | 
| 
       35 
     | 
    
         
            -
                  'timeout-to-kill'
         
     | 
| 
       36 
     | 
    
         
            -
                 
     | 
| 
       37 
     | 
    
         
            -
                 
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
                   
     | 
| 
       41 
     | 
    
         
            -
                   
     | 
| 
       42 
     | 
    
         
            -
                   
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
                  onChange={newValue => setValue(newValue)}
         
     | 
| 
       46 
     | 
    
         
            -
                />
         
     | 
| 
       47 
     | 
    
         
            -
              </FormGroup>
         
     | 
| 
      
 40 
     | 
    
         
            +
                  fieldId: 'timeout-to-kill',
         
     | 
| 
      
 41 
     | 
    
         
            +
                }}
         
     | 
| 
      
 42 
     | 
    
         
            +
                inputProps={{
         
     | 
| 
      
 43 
     | 
    
         
            +
                  value,
         
     | 
| 
      
 44 
     | 
    
         
            +
                  placeholder: __('For example: 1, 2, 3, 4, 5...'),
         
     | 
| 
      
 45 
     | 
    
         
            +
                  autoComplete: 'timeout-to-kill',
         
     | 
| 
      
 46 
     | 
    
         
            +
                  id: 'timeout-to-kill',
         
     | 
| 
      
 47 
     | 
    
         
            +
                  onChange: newValue => setValue(newValue),
         
     | 
| 
      
 48 
     | 
    
         
            +
                }}
         
     | 
| 
      
 49 
     | 
    
         
            +
              />
         
     | 
| 
       48 
50 
     | 
    
         
             
            );
         
     | 
| 
       49 
51 
     | 
    
         | 
| 
       50 
52 
     | 
    
         
             
            export const PasswordField = ({ value, setValue }) => (
         
     | 
| 
         @@ -56,11 +58,11 @@ export const PasswordField = ({ value, setValue }) => ( 
     | 
|
| 
       56 
58 
     | 
    
         
             
                  ),
         
     | 
| 
       57 
59 
     | 
    
         
             
                  'password'
         
     | 
| 
       58 
60 
     | 
    
         
             
                )}
         
     | 
| 
       59 
     | 
    
         
            -
                fieldId="password"
         
     | 
| 
      
 61 
     | 
    
         
            +
                fieldId="job-password"
         
     | 
| 
       60 
62 
     | 
    
         
             
              >
         
     | 
| 
       61 
63 
     | 
    
         
             
                <TextInput
         
     | 
| 
       62 
     | 
    
         
            -
                  autoComplete="password"
         
     | 
| 
       63 
     | 
    
         
            -
                  id="password"
         
     | 
| 
      
 64 
     | 
    
         
            +
                  autoComplete="new-password" // to prevent firefox from autofilling the user password
         
     | 
| 
      
 65 
     | 
    
         
            +
                  id="job-password"
         
     | 
| 
       64 
66 
     | 
    
         
             
                  type="password"
         
     | 
| 
       65 
67 
     | 
    
         
             
                  placeholder="*****"
         
     | 
| 
       66 
68 
     | 
    
         
             
                  value={value}
         
     | 
| 
         @@ -114,51 +116,54 @@ export const EffectiveUserPasswordField = ({ value, setValue }) => ( 
     | 
|
| 
       114 
116 
     | 
    
         
             
            );
         
     | 
| 
       115 
117 
     | 
    
         | 
| 
       116 
118 
     | 
    
         
             
            export const ConcurrencyLevelField = ({ value, setValue }) => (
         
     | 
| 
       117 
     | 
    
         
            -
              < 
     | 
| 
       118 
     | 
    
         
            -
                 
     | 
| 
       119 
     | 
    
         
            -
             
     | 
| 
       120 
     | 
    
         
            -
                   
     | 
| 
       121 
     | 
    
         
            -
                     
     | 
| 
      
 119 
     | 
    
         
            +
              <NumberInput
         
     | 
| 
      
 120 
     | 
    
         
            +
                formProps={{
         
     | 
| 
      
 121 
     | 
    
         
            +
                  label: __('Concurrency level'),
         
     | 
| 
      
 122 
     | 
    
         
            +
                  labelIcon: helpLabel(
         
     | 
| 
      
 123 
     | 
    
         
            +
                    __(
         
     | 
| 
      
 124 
     | 
    
         
            +
                      'Run at most N tasks at a time. If this is set and proxy batch triggering is enabled, then tasks are triggered on the smart proxy in batches of size 1.'
         
     | 
| 
      
 125 
     | 
    
         
            +
                    ),
         
     | 
| 
      
 126 
     | 
    
         
            +
                    'concurrency-level'
         
     | 
| 
       122 
127 
     | 
    
         
             
                  ),
         
     | 
| 
       123 
     | 
    
         
            -
                  'concurrency-level'
         
     | 
| 
       124 
     | 
    
         
            -
                 
     | 
| 
       125 
     | 
    
         
            -
                 
     | 
| 
       126 
     | 
    
         
            -
             
     | 
| 
       127 
     | 
    
         
            -
             
     | 
| 
       128 
     | 
    
         
            -
                   
     | 
| 
       129 
     | 
    
         
            -
                   
     | 
| 
       130 
     | 
    
         
            -
                   
     | 
| 
       131 
     | 
    
         
            -
                   
     | 
| 
       132 
     | 
    
         
            -
             
     | 
| 
       133 
     | 
    
         
            -
             
     | 
| 
       134 
     | 
    
         
            -
                  onChange={newValue => setValue(newValue)}
         
     | 
| 
       135 
     | 
    
         
            -
                />
         
     | 
| 
       136 
     | 
    
         
            -
              </FormGroup>
         
     | 
| 
      
 128 
     | 
    
         
            +
                  fieldId: 'concurrency-level',
         
     | 
| 
      
 129 
     | 
    
         
            +
                }}
         
     | 
| 
      
 130 
     | 
    
         
            +
                inputProps={{
         
     | 
| 
      
 131 
     | 
    
         
            +
                  min: 1,
         
     | 
| 
      
 132 
     | 
    
         
            +
                  autoComplete: 'concurrency-level',
         
     | 
| 
      
 133 
     | 
    
         
            +
                  id: 'concurrency-level',
         
     | 
| 
      
 134 
     | 
    
         
            +
                  placeholder: __('For example: 1, 2, 3, 4, 5...'),
         
     | 
| 
      
 135 
     | 
    
         
            +
                  value,
         
     | 
| 
      
 136 
     | 
    
         
            +
                  onChange: newValue => setValue(newValue),
         
     | 
| 
      
 137 
     | 
    
         
            +
                }}
         
     | 
| 
      
 138 
     | 
    
         
            +
              />
         
     | 
| 
       137 
139 
     | 
    
         
             
            );
         
     | 
| 
       138 
140 
     | 
    
         | 
| 
       139 
141 
     | 
    
         
             
            export const TimeSpanLevelField = ({ value, setValue }) => (
         
     | 
| 
       140 
     | 
    
         
            -
              < 
     | 
| 
       141 
     | 
    
         
            -
                 
     | 
| 
       142 
     | 
    
         
            -
             
     | 
| 
       143 
     | 
    
         
            -
                   
     | 
| 
       144 
     | 
    
         
            -
                     
     | 
| 
      
 142 
     | 
    
         
            +
              <NumberInput
         
     | 
| 
      
 143 
     | 
    
         
            +
                formProps={{
         
     | 
| 
      
 144 
     | 
    
         
            +
                  label: __('Time span'),
         
     | 
| 
      
 145 
     | 
    
         
            +
                  labelIcon: helpLabel(
         
     | 
| 
      
 146 
     | 
    
         
            +
                    __(
         
     | 
| 
      
 147 
     | 
    
         
            +
                      'Distribute execution over N seconds. If this is set and proxy batch triggering is enabled, then tasks are triggered on the smart proxy in batches of size 1.'
         
     | 
| 
      
 148 
     | 
    
         
            +
                    ),
         
     | 
| 
      
 149 
     | 
    
         
            +
                    'time-span'
         
     | 
| 
       145 
150 
     | 
    
         
             
                  ),
         
     | 
| 
       146 
     | 
    
         
            -
                  'time-span'
         
     | 
| 
       147 
     | 
    
         
            -
                 
     | 
| 
       148 
     | 
    
         
            -
                 
     | 
| 
       149 
     | 
    
         
            -
             
     | 
| 
       150 
     | 
    
         
            -
             
     | 
| 
       151 
     | 
    
         
            -
                   
     | 
| 
       152 
     | 
    
         
            -
                   
     | 
| 
       153 
     | 
    
         
            -
                   
     | 
| 
       154 
     | 
    
         
            -
                   
     | 
| 
       155 
     | 
    
         
            -
             
     | 
| 
       156 
     | 
    
         
            -
             
     | 
| 
       157 
     | 
    
         
            -
                  onChange={newValue => setValue(newValue)}
         
     | 
| 
       158 
     | 
    
         
            -
                />
         
     | 
| 
       159 
     | 
    
         
            -
              </FormGroup>
         
     | 
| 
      
 151 
     | 
    
         
            +
                  fieldId: 'time-span',
         
     | 
| 
      
 152 
     | 
    
         
            +
                }}
         
     | 
| 
      
 153 
     | 
    
         
            +
                inputProps={{
         
     | 
| 
      
 154 
     | 
    
         
            +
                  min: 1,
         
     | 
| 
      
 155 
     | 
    
         
            +
                  autoComplete: 'time-span',
         
     | 
| 
      
 156 
     | 
    
         
            +
                  id: 'time-span',
         
     | 
| 
      
 157 
     | 
    
         
            +
                  placeholder: __('For example: 1, 2, 3, 4, 5...'),
         
     | 
| 
      
 158 
     | 
    
         
            +
                  value,
         
     | 
| 
      
 159 
     | 
    
         
            +
                  onChange: newValue => setValue(newValue),
         
     | 
| 
      
 160 
     | 
    
         
            +
                }}
         
     | 
| 
      
 161 
     | 
    
         
            +
              />
         
     | 
| 
       160 
162 
     | 
    
         
             
            );
         
     | 
| 
       161 
163 
     | 
    
         | 
| 
      
 164 
     | 
    
         
            +
            export const TemplateInputsFields = ({ inputs, value, setValue }) => (
         
     | 
| 
      
 165 
     | 
    
         
            +
              <>{inputs?.map(input => formatter(input, value, setValue))}</>
         
     | 
| 
      
 166 
     | 
    
         
            +
            );
         
     | 
| 
       162 
167 
     | 
    
         
             
            EffectiveUserField.propTypes = {
         
     | 
| 
       163 
168 
     | 
    
         
             
              value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
         
     | 
| 
       164 
169 
     | 
    
         
             
              setValue: PropTypes.func.isRequired,
         
     | 
| 
         @@ -179,3 +184,12 @@ ConcurrencyLevelField.propTypes = EffectiveUserField.propTypes; 
     | 
|
| 
       179 
184 
     | 
    
         
             
            ConcurrencyLevelField.defaultProps = EffectiveUserField.defaultProps;
         
     | 
| 
       180 
185 
     | 
    
         
             
            TimeSpanLevelField.propTypes = EffectiveUserField.propTypes;
         
     | 
| 
       181 
186 
     | 
    
         
             
            TimeSpanLevelField.defaultProps = EffectiveUserField.defaultProps;
         
     | 
| 
      
 187 
     | 
    
         
            +
            TemplateInputsFields.propTypes = {
         
     | 
| 
      
 188 
     | 
    
         
            +
              inputs: PropTypes.array.isRequired,
         
     | 
| 
      
 189 
     | 
    
         
            +
              value: PropTypes.object,
         
     | 
| 
      
 190 
     | 
    
         
            +
              setValue: PropTypes.func.isRequired,
         
     | 
| 
      
 191 
     | 
    
         
            +
            };
         
     | 
| 
      
 192 
     | 
    
         
            +
             
     | 
| 
      
 193 
     | 
    
         
            +
            TemplateInputsFields.defaultProps = {
         
     | 
| 
      
 194 
     | 
    
         
            +
              value: {},
         
     | 
| 
      
 195 
     | 
    
         
            +
            };
         
     | 
| 
         @@ -1,25 +1,144 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            import React from 'react';
         
     | 
| 
       2 
2 
     | 
    
         
             
            import { Provider } from 'react-redux';
         
     | 
| 
       3 
     | 
    
         
            -
            import configureMockStore from 'redux-mock-store';
         
     | 
| 
       4 
     | 
    
         
            -
            import * as patternfly from '@patternfly/react-core';
         
     | 
| 
       5 
3 
     | 
    
         
             
            import { mount } from '@theforeman/test';
         
     | 
| 
       6 
     | 
    
         
            -
            import {  
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
      
 4 
     | 
    
         
            +
            import { fireEvent, screen, render, act } from '@testing-library/react';
         
     | 
| 
      
 5 
     | 
    
         
            +
            import * as api from 'foremanReact/redux/API';
         
     | 
| 
      
 6 
     | 
    
         
            +
            import { JobWizard } from '../../../JobWizard';
         
     | 
| 
      
 7 
     | 
    
         
            +
            import * as selectors from '../../../JobWizardSelectors';
         
     | 
| 
      
 8 
     | 
    
         
            +
            import {
         
     | 
| 
      
 9 
     | 
    
         
            +
              jobTemplateResponse as jobTemplate,
         
     | 
| 
      
 10 
     | 
    
         
            +
              testSetup,
         
     | 
| 
      
 11 
     | 
    
         
            +
              mockApi,
         
     | 
| 
      
 12 
     | 
    
         
            +
            } from '../../../__tests__/fixtures';
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            const store = testSetup(selectors, api);
         
     | 
| 
      
 15 
     | 
    
         
            +
            mockApi(api);
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            jest.spyOn(selectors, 'selectEffectiveUser');
         
     | 
| 
      
 18 
     | 
    
         
            +
            jest.spyOn(selectors, 'selectTemplateInputs');
         
     | 
| 
      
 19 
     | 
    
         
            +
            jest.spyOn(selectors, 'selectAdvancedTemplateInputs');
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            selectors.selectEffectiveUser.mockImplementation(
         
     | 
| 
      
 22 
     | 
    
         
            +
              () => jobTemplate.effective_user
         
     | 
| 
      
 23 
     | 
    
         
            +
            );
         
     | 
| 
      
 24 
     | 
    
         
            +
            selectors.selectTemplateInputs.mockImplementation(
         
     | 
| 
      
 25 
     | 
    
         
            +
              () => jobTemplate.template_inputs
         
     | 
| 
      
 26 
     | 
    
         
            +
            );
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
            selectors.selectAdvancedTemplateInputs.mockImplementation(
         
     | 
| 
      
 29 
     | 
    
         
            +
              () => jobTemplate.advanced_template_inputs
         
     | 
| 
      
 30 
     | 
    
         
            +
            );
         
     | 
| 
       16 
31 
     | 
    
         
             
            describe('AdvancedFields', () => {
         
     | 
| 
       17 
     | 
    
         
            -
              it(' 
     | 
| 
       18 
     | 
    
         
            -
                const  
     | 
| 
      
 32 
     | 
    
         
            +
              it('should save data between steps for advanced fields', async () => {
         
     | 
| 
      
 33 
     | 
    
         
            +
                const wrapper = mount(
         
     | 
| 
       19 
34 
     | 
    
         
             
                  <Provider store={store}>
         
     | 
| 
       20 
     | 
    
         
            -
                    < 
     | 
| 
      
 35 
     | 
    
         
            +
                    <JobWizard advancedValues={{}} setAdvancedValues={jest.fn()} />
         
     | 
| 
       21 
36 
     | 
    
         
             
                  </Provider>
         
     | 
| 
       22 
37 
     | 
    
         
             
                );
         
     | 
| 
       23 
     | 
    
         
            -
                 
     | 
| 
      
 38 
     | 
    
         
            +
                // setup
         
     | 
| 
      
 39 
     | 
    
         
            +
                wrapper.find('.pf-c-button.pf-c-select__toggle-button').simulate('click');
         
     | 
| 
      
 40 
     | 
    
         
            +
                wrapper
         
     | 
| 
      
 41 
     | 
    
         
            +
                  .find('.pf-c-select__menu-item')
         
     | 
| 
      
 42 
     | 
    
         
            +
                  .first()
         
     | 
| 
      
 43 
     | 
    
         
            +
                  .simulate('click');
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                // test
         
     | 
| 
      
 46 
     | 
    
         
            +
                expect(wrapper.find('.pf-c-wizard__nav-link.pf-m-disabled')).toHaveLength(
         
     | 
| 
      
 47 
     | 
    
         
            +
                  0
         
     | 
| 
      
 48 
     | 
    
         
            +
                );
         
     | 
| 
      
 49 
     | 
    
         
            +
                wrapper
         
     | 
| 
      
 50 
     | 
    
         
            +
                  .find('.pf-c-wizard__nav-link')
         
     | 
| 
      
 51 
     | 
    
         
            +
                  .at(2)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  .simulate('click'); // Advanced step
         
     | 
| 
      
 53 
     | 
    
         
            +
                const effectiveUserInput = () => wrapper.find('input#effective-user');
         
     | 
| 
      
 54 
     | 
    
         
            +
                const advancedTemplateInput = () =>
         
     | 
| 
      
 55 
     | 
    
         
            +
                  wrapper.find('.pf-c-form__group-control textarea');
         
     | 
| 
      
 56 
     | 
    
         
            +
                const effectiveUesrValue = 'effective user new value';
         
     | 
| 
      
 57 
     | 
    
         
            +
                const advancedTemplateInputValue = 'advanced input new value';
         
     | 
| 
      
 58 
     | 
    
         
            +
                effectiveUserInput().getDOMNode().value = effectiveUesrValue;
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                effectiveUserInput().simulate('change');
         
     | 
| 
      
 61 
     | 
    
         
            +
                wrapper.update();
         
     | 
| 
      
 62 
     | 
    
         
            +
                advancedTemplateInput().getDOMNode().value = advancedTemplateInputValue;
         
     | 
| 
      
 63 
     | 
    
         
            +
                advancedTemplateInput().simulate('change');
         
     | 
| 
      
 64 
     | 
    
         
            +
                expect(effectiveUserInput().prop('value')).toEqual(effectiveUesrValue);
         
     | 
| 
      
 65 
     | 
    
         
            +
                expect(advancedTemplateInput().prop('value')).toEqual(
         
     | 
| 
      
 66 
     | 
    
         
            +
                  advancedTemplateInputValue
         
     | 
| 
      
 67 
     | 
    
         
            +
                );
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                wrapper
         
     | 
| 
      
 70 
     | 
    
         
            +
                  .find('.pf-c-wizard__nav-link')
         
     | 
| 
      
 71 
     | 
    
         
            +
                  .at(1)
         
     | 
| 
      
 72 
     | 
    
         
            +
                  .simulate('click');
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                expect(wrapper.find('.pf-c-wizard__nav-link.pf-m-current').text()).toEqual(
         
     | 
| 
      
 75 
     | 
    
         
            +
                  'Target Hosts'
         
     | 
| 
      
 76 
     | 
    
         
            +
                );
         
     | 
| 
      
 77 
     | 
    
         
            +
                wrapper
         
     | 
| 
      
 78 
     | 
    
         
            +
                  .find('.pf-c-wizard__nav-link')
         
     | 
| 
      
 79 
     | 
    
         
            +
                  .at(2)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  .simulate('click'); // Advanced step
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                expect(effectiveUserInput().prop('value')).toEqual(effectiveUesrValue);
         
     | 
| 
      
 83 
     | 
    
         
            +
                expect(advancedTemplateInput().prop('value')).toEqual(
         
     | 
| 
      
 84 
     | 
    
         
            +
                  advancedTemplateInputValue
         
     | 
| 
      
 85 
     | 
    
         
            +
                );
         
     | 
| 
      
 86 
     | 
    
         
            +
              });
         
     | 
| 
      
 87 
     | 
    
         
            +
              it('fill template fields', async () => {
         
     | 
| 
      
 88 
     | 
    
         
            +
                render(
         
     | 
| 
      
 89 
     | 
    
         
            +
                  <Provider store={store}>
         
     | 
| 
      
 90 
     | 
    
         
            +
                    <JobWizard />
         
     | 
| 
      
 91 
     | 
    
         
            +
                  </Provider>
         
     | 
| 
      
 92 
     | 
    
         
            +
                );
         
     | 
| 
      
 93 
     | 
    
         
            +
                await act(async () => {
         
     | 
| 
      
 94 
     | 
    
         
            +
                  fireEvent.click(screen.getByText('Advanced Fields'));
         
     | 
| 
      
 95 
     | 
    
         
            +
                });
         
     | 
| 
      
 96 
     | 
    
         
            +
                const searchValue = 'search test';
         
     | 
| 
      
 97 
     | 
    
         
            +
                const textValue = 'I am a text';
         
     | 
| 
      
 98 
     | 
    
         
            +
                const dateValue = '08/07/2021';
         
     | 
| 
      
 99 
     | 
    
         
            +
                const textField = screen.getByLabelText('adv plain hidden', {
         
     | 
| 
      
 100 
     | 
    
         
            +
                  selector: 'textarea',
         
     | 
| 
      
 101 
     | 
    
         
            +
                });
         
     | 
| 
      
 102 
     | 
    
         
            +
                const selectField = screen.getByText('option 1');
         
     | 
| 
      
 103 
     | 
    
         
            +
                const searchField = screen.getByPlaceholderText('Filter...');
         
     | 
| 
      
 104 
     | 
    
         
            +
                const dateField = screen.getByLabelText('adv date', {
         
     | 
| 
      
 105 
     | 
    
         
            +
                  selector: 'input',
         
     | 
| 
      
 106 
     | 
    
         
            +
                });
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                fireEvent.click(selectField);
         
     | 
| 
      
 109 
     | 
    
         
            +
                await act(async () => {
         
     | 
| 
      
 110 
     | 
    
         
            +
                  await fireEvent.click(screen.getByText('option 2'));
         
     | 
| 
      
 111 
     | 
    
         
            +
                  fireEvent.click(screen.getAllByText('Advanced Fields')[0]); // to remove focus
         
     | 
| 
      
 112 
     | 
    
         
            +
                  await fireEvent.change(textField, {
         
     | 
| 
      
 113 
     | 
    
         
            +
                    target: { value: textValue },
         
     | 
| 
      
 114 
     | 
    
         
            +
                  });
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                  await fireEvent.change(searchField, {
         
     | 
| 
      
 117 
     | 
    
         
            +
                    target: { value: searchValue },
         
     | 
| 
      
 118 
     | 
    
         
            +
                  });
         
     | 
| 
      
 119 
     | 
    
         
            +
                  await fireEvent.change(dateField, {
         
     | 
| 
      
 120 
     | 
    
         
            +
                    target: { value: dateValue },
         
     | 
| 
      
 121 
     | 
    
         
            +
                  });
         
     | 
| 
      
 122 
     | 
    
         
            +
                });
         
     | 
| 
      
 123 
     | 
    
         
            +
                expect(
         
     | 
| 
      
 124 
     | 
    
         
            +
                  screen.getByLabelText('adv plain hidden', {
         
     | 
| 
      
 125 
     | 
    
         
            +
                    selector: 'textarea',
         
     | 
| 
      
 126 
     | 
    
         
            +
                  }).value
         
     | 
| 
      
 127 
     | 
    
         
            +
                ).toBe(textValue);
         
     | 
| 
      
 128 
     | 
    
         
            +
                expect(searchField.value).toBe(searchValue);
         
     | 
| 
      
 129 
     | 
    
         
            +
                expect(dateField.value).toBe(dateValue);
         
     | 
| 
      
 130 
     | 
    
         
            +
                await act(async () => {
         
     | 
| 
      
 131 
     | 
    
         
            +
                  fireEvent.click(screen.getByText('Category and Template'));
         
     | 
| 
      
 132 
     | 
    
         
            +
                });
         
     | 
| 
      
 133 
     | 
    
         
            +
                expect(screen.getAllByText('Category and Template')).toHaveLength(3);
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
                await act(async () => {
         
     | 
| 
      
 136 
     | 
    
         
            +
                  fireEvent.click(screen.getByText('Advanced Fields'));
         
     | 
| 
      
 137 
     | 
    
         
            +
                });
         
     | 
| 
      
 138 
     | 
    
         
            +
                expect(textField.value).toBe(textValue);
         
     | 
| 
      
 139 
     | 
    
         
            +
                expect(searchField.value).toBe(searchValue);
         
     | 
| 
      
 140 
     | 
    
         
            +
                expect(dateField.value).toBe(dateValue);
         
     | 
| 
      
 141 
     | 
    
         
            +
                expect(screen.queryAllByText('option 1')).toHaveLength(0);
         
     | 
| 
      
 142 
     | 
    
         
            +
                expect(screen.queryAllByText('option 2')).toHaveLength(1);
         
     | 
| 
       24 
143 
     | 
    
         
             
              });
         
     | 
| 
       25 
144 
     | 
    
         
             
            });
         
     | 
| 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import React from 'react';
         
     | 
| 
      
 2 
     | 
    
         
            +
            import { mount } from '@theforeman/test';
         
     | 
| 
      
 3 
     | 
    
         
            +
            import { DescriptionField } from '../DescriptionField';
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            describe('DescriptionField', () => {
         
     | 
| 
      
 6 
     | 
    
         
            +
              it('rendring', () => {
         
     | 
| 
      
 7 
     | 
    
         
            +
                const component = mount(
         
     | 
| 
      
 8 
     | 
    
         
            +
                  <DescriptionField
         
     | 
| 
      
 9 
     | 
    
         
            +
                    inputs={[{ name: 'command' }]}
         
     | 
| 
      
 10 
     | 
    
         
            +
                    value="Run %{command}"
         
     | 
| 
      
 11 
     | 
    
         
            +
                    setValue={jest.fn()}
         
     | 
| 
      
 12 
     | 
    
         
            +
                  />
         
     | 
| 
      
 13 
     | 
    
         
            +
                );
         
     | 
| 
      
 14 
     | 
    
         
            +
                const preview = component.find('#description-preview').hostNodes();
         
     | 
| 
      
 15 
     | 
    
         
            +
                const findLink = () => component.find('.pf-m-link.pf-m-inline');
         
     | 
| 
      
 16 
     | 
    
         
            +
                expect(findLink().text()).toEqual('Edit job description template');
         
     | 
| 
      
 17 
     | 
    
         
            +
                expect(preview.props().value).toEqual('Run command');
         
     | 
| 
      
 18 
     | 
    
         
            +
                findLink().simulate('click');
         
     | 
| 
      
 19 
     | 
    
         
            +
                const description = component.find('#description').hostNodes();
         
     | 
| 
      
 20 
     | 
    
         
            +
                expect(description.props().value).toEqual('Run %{command}');
         
     | 
| 
      
 21 
     | 
    
         
            +
                expect(findLink().text()).toEqual('Preview job description');
         
     | 
| 
      
 22 
     | 
    
         
            +
              });
         
     | 
| 
      
 23 
     | 
    
         
            +
            });
         
     | 
| 
         @@ -1,52 +1,123 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            import  
     | 
| 
       2 
     | 
    
         
            -
            import {  
     | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
            const  
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
                 
     | 
| 
       18 
     | 
    
         
            -
                 
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
                     
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
                   
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
             
     | 
| 
       41 
     | 
    
         
            -
                 
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
                 
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
                 
     | 
| 
      
 1 
     | 
    
         
            +
            import React from 'react';
         
     | 
| 
      
 2 
     | 
    
         
            +
            import { Provider } from 'react-redux';
         
     | 
| 
      
 3 
     | 
    
         
            +
            import { fireEvent, screen, render, act } from '@testing-library/react';
         
     | 
| 
      
 4 
     | 
    
         
            +
            import * as api from 'foremanReact/redux/API';
         
     | 
| 
      
 5 
     | 
    
         
            +
            import { JobWizard } from '../../JobWizard';
         
     | 
| 
      
 6 
     | 
    
         
            +
            import * as selectors from '../../JobWizardSelectors';
         
     | 
| 
      
 7 
     | 
    
         
            +
            import { testSetup, mockApi } from '../../__tests__/fixtures';
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            const store = testSetup(selectors, api);
         
     | 
| 
      
 10 
     | 
    
         
            +
            mockApi(api);
         
     | 
| 
      
 11 
     | 
    
         
            +
            jest.spyOn(selectors, 'selectCategoryError');
         
     | 
| 
      
 12 
     | 
    
         
            +
            jest.spyOn(selectors, 'selectAllTemplatesError');
         
     | 
| 
      
 13 
     | 
    
         
            +
            jest.spyOn(selectors, 'selectTemplateError');
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            describe('Category And Template', () => {
         
     | 
| 
      
 16 
     | 
    
         
            +
              it('should select ', async () => {
         
     | 
| 
      
 17 
     | 
    
         
            +
                selectors.selectCategoryError.mockImplementation(() => null);
         
     | 
| 
      
 18 
     | 
    
         
            +
                selectors.selectAllTemplatesError.mockImplementation(() => null);
         
     | 
| 
      
 19 
     | 
    
         
            +
                selectors.selectTemplateError.mockImplementation(() => null);
         
     | 
| 
      
 20 
     | 
    
         
            +
                render(
         
     | 
| 
      
 21 
     | 
    
         
            +
                  <Provider store={store}>
         
     | 
| 
      
 22 
     | 
    
         
            +
                    <JobWizard />
         
     | 
| 
      
 23 
     | 
    
         
            +
                  </Provider>
         
     | 
| 
      
 24 
     | 
    
         
            +
                );
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                expect(screen.queryAllByLabelText('Error')).toHaveLength(0);
         
     | 
| 
      
 27 
     | 
    
         
            +
                expect(screen.queryAllByLabelText('failed')).toHaveLength(0);
         
     | 
| 
      
 28 
     | 
    
         
            +
                // Category
         
     | 
| 
      
 29 
     | 
    
         
            +
                fireEvent.click(
         
     | 
| 
      
 30 
     | 
    
         
            +
                  screen.getByLabelText('Ansible Commands', { selector: 'button' })
         
     | 
| 
      
 31 
     | 
    
         
            +
                );
         
     | 
| 
      
 32 
     | 
    
         
            +
                await act(async () => {
         
     | 
| 
      
 33 
     | 
    
         
            +
                  await fireEvent.click(screen.getByText('Puppet'));
         
     | 
| 
      
 34 
     | 
    
         
            +
                });
         
     | 
| 
      
 35 
     | 
    
         
            +
                fireEvent.click(screen.getAllByText('Category and Template')[0]); // to remove focus
         
     | 
| 
      
 36 
     | 
    
         
            +
                expect(
         
     | 
| 
      
 37 
     | 
    
         
            +
                  screen.queryAllByLabelText('Ansible Commands', { selector: 'button' })
         
     | 
| 
      
 38 
     | 
    
         
            +
                ).toHaveLength(0);
         
     | 
| 
      
 39 
     | 
    
         
            +
                expect(
         
     | 
| 
      
 40 
     | 
    
         
            +
                  screen.queryAllByLabelText('Puppet', { selector: 'button' })
         
     | 
| 
      
 41 
     | 
    
         
            +
                ).toHaveLength(1);
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                // Template
         
     | 
| 
      
 44 
     | 
    
         
            +
                fireEvent.click(
         
     | 
| 
      
 45 
     | 
    
         
            +
                  screen.getByDisplayValue('template1', { selector: 'button' })
         
     | 
| 
      
 46 
     | 
    
         
            +
                );
         
     | 
| 
      
 47 
     | 
    
         
            +
                await act(async () => {
         
     | 
| 
      
 48 
     | 
    
         
            +
                  await fireEvent.click(screen.getByText('template2'));
         
     | 
| 
      
 49 
     | 
    
         
            +
                });
         
     | 
| 
      
 50 
     | 
    
         
            +
                fireEvent.click(screen.getAllByText('Category and Template')[0]); // to remove focus
         
     | 
| 
      
 51 
     | 
    
         
            +
                expect(
         
     | 
| 
      
 52 
     | 
    
         
            +
                  screen.queryAllByDisplayValue('template1', { selector: 'button' })
         
     | 
| 
      
 53 
     | 
    
         
            +
                ).toHaveLength(0);
         
     | 
| 
      
 54 
     | 
    
         
            +
                expect(
         
     | 
| 
      
 55 
     | 
    
         
            +
                  screen.queryAllByDisplayValue('template2', { selector: 'button' })
         
     | 
| 
      
 56 
     | 
    
         
            +
                ).toHaveLength(1);
         
     | 
| 
      
 57 
     | 
    
         
            +
              });
         
     | 
| 
      
 58 
     | 
    
         
            +
              it('category error ', async () => {
         
     | 
| 
      
 59 
     | 
    
         
            +
                selectors.selectCategoryError.mockImplementation(() => 'category error');
         
     | 
| 
      
 60 
     | 
    
         
            +
                selectors.selectAllTemplatesError.mockImplementation(() => null);
         
     | 
| 
      
 61 
     | 
    
         
            +
                selectors.selectTemplateError.mockImplementation(() => null);
         
     | 
| 
      
 62 
     | 
    
         
            +
                render(
         
     | 
| 
      
 63 
     | 
    
         
            +
                  <Provider store={store}>
         
     | 
| 
      
 64 
     | 
    
         
            +
                    <JobWizard />
         
     | 
| 
      
 65 
     | 
    
         
            +
                  </Provider>
         
     | 
| 
      
 66 
     | 
    
         
            +
                );
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                expect(
         
     | 
| 
      
 69 
     | 
    
         
            +
                  screen.queryAllByText('Categories list failed with:', { exact: false })
         
     | 
| 
      
 70 
     | 
    
         
            +
                ).toHaveLength(1);
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                expect(
         
     | 
| 
      
 73 
     | 
    
         
            +
                  screen.queryAllByText('Templates list failed with:', { exact: false })
         
     | 
| 
      
 74 
     | 
    
         
            +
                ).toHaveLength(0);
         
     | 
| 
      
 75 
     | 
    
         
            +
                expect(
         
     | 
| 
      
 76 
     | 
    
         
            +
                  screen.queryAllByText('Template failed with:', { exact: false })
         
     | 
| 
      
 77 
     | 
    
         
            +
                ).toHaveLength(0);
         
     | 
| 
      
 78 
     | 
    
         
            +
              });
         
     | 
| 
      
 79 
     | 
    
         
            +
              it('templates error ', async () => {
         
     | 
| 
      
 80 
     | 
    
         
            +
                selectors.selectCategoryError.mockImplementation(() => null);
         
     | 
| 
      
 81 
     | 
    
         
            +
                selectors.selectAllTemplatesError.mockImplementation(
         
     | 
| 
      
 82 
     | 
    
         
            +
                  () => 'templates error'
         
     | 
| 
      
 83 
     | 
    
         
            +
                );
         
     | 
| 
      
 84 
     | 
    
         
            +
                selectors.selectTemplateError.mockImplementation(() => null);
         
     | 
| 
      
 85 
     | 
    
         
            +
                render(
         
     | 
| 
      
 86 
     | 
    
         
            +
                  <Provider store={store}>
         
     | 
| 
      
 87 
     | 
    
         
            +
                    <JobWizard />
         
     | 
| 
      
 88 
     | 
    
         
            +
                  </Provider>
         
     | 
| 
      
 89 
     | 
    
         
            +
                );
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                expect(
         
     | 
| 
      
 92 
     | 
    
         
            +
                  screen.queryAllByText('Categories list failed with:', { exact: false })
         
     | 
| 
      
 93 
     | 
    
         
            +
                ).toHaveLength(0);
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                expect(
         
     | 
| 
      
 96 
     | 
    
         
            +
                  screen.queryAllByText('Templates list failed with:', { exact: false })
         
     | 
| 
      
 97 
     | 
    
         
            +
                ).toHaveLength(1);
         
     | 
| 
      
 98 
     | 
    
         
            +
                expect(
         
     | 
| 
      
 99 
     | 
    
         
            +
                  screen.queryAllByText('Template failed with:', { exact: false })
         
     | 
| 
      
 100 
     | 
    
         
            +
                ).toHaveLength(0);
         
     | 
| 
      
 101 
     | 
    
         
            +
              });
         
     | 
| 
      
 102 
     | 
    
         
            +
              it('template error ', async () => {
         
     | 
| 
      
 103 
     | 
    
         
            +
                selectors.selectCategoryError.mockImplementation(() => null);
         
     | 
| 
      
 104 
     | 
    
         
            +
                selectors.selectAllTemplatesError.mockImplementation(() => null);
         
     | 
| 
      
 105 
     | 
    
         
            +
                selectors.selectTemplateError.mockImplementation(() => 'template error');
         
     | 
| 
      
 106 
     | 
    
         
            +
                render(
         
     | 
| 
      
 107 
     | 
    
         
            +
                  <Provider store={store}>
         
     | 
| 
      
 108 
     | 
    
         
            +
                    <JobWizard />
         
     | 
| 
      
 109 
     | 
    
         
            +
                  </Provider>
         
     | 
| 
      
 110 
     | 
    
         
            +
                );
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                expect(
         
     | 
| 
      
 113 
     | 
    
         
            +
                  screen.queryAllByText('Categories list failed with:', { exact: false })
         
     | 
| 
      
 114 
     | 
    
         
            +
                ).toHaveLength(0);
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                expect(
         
     | 
| 
      
 117 
     | 
    
         
            +
                  screen.queryAllByText('Templates list failed with:', { exact: false })
         
     | 
| 
      
 118 
     | 
    
         
            +
                ).toHaveLength(0);
         
     | 
| 
      
 119 
     | 
    
         
            +
                expect(
         
     | 
| 
      
 120 
     | 
    
         
            +
                  screen.queryAllByText('Template failed with:', { exact: false })
         
     | 
| 
      
 121 
     | 
    
         
            +
                ).toHaveLength(1);
         
     | 
| 
      
 122 
     | 
    
         
            +
              });
         
     | 
| 
       52 
123 
     | 
    
         
             
            });
         
     |