foreman_remote_execution 4.4.0 → 4.5.3
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/app/controllers/api/v2/job_invocations_controller.rb +13 -24
- data/app/controllers/job_invocations_controller.rb +1 -1
- data/app/controllers/job_templates_controller.rb +4 -4
- data/app/controllers/ui_job_wizard_controller.rb +19 -0
- data/app/helpers/job_invocations_helper.rb +2 -2
- data/app/helpers/remote_execution_helper.rb +13 -9
- data/app/lib/actions/remote_execution/run_host_job.rb +36 -6
- data/app/models/concerns/foreman_remote_execution/host_extensions.rb +7 -5
- data/app/models/concerns/foreman_remote_execution/smart_proxy_extensions.rb +6 -0
- data/app/models/host_proxy_invocation.rb +4 -0
- data/app/models/host_status/execution_status.rb +5 -5
- data/app/models/job_invocation.rb +31 -12
- data/app/models/job_invocation_composer.rb +61 -19
- data/app/models/remote_execution_provider.rb +1 -1
- data/app/models/setting/remote_execution.rb +2 -2
- data/app/models/ssh_execution_provider.rb +4 -4
- data/app/models/targeting.rb +5 -1
- data/app/overrides/execution_interface.rb +8 -8
- data/app/overrides/subnet_proxies.rb +6 -6
- data/app/views/job_invocations/index.html.erb +1 -1
- data/app/views/templates/ssh/module_action.erb +1 -0
- data/app/views/templates/ssh/puppet_run_once.erb +1 -0
- data/config/routes.rb +1 -0
- data/db/migrate/20180110104432_rename_template_invocation_permission.rb +1 -1
- data/db/migrate/20190111153330_remove_remote_execution_without_proxy_setting.rb +4 -4
- data/db/migrate/2021051713291621250977_add_host_proxy_invocations.rb +12 -0
- data/extra/cockpit/foreman-cockpit-session +6 -6
- data/lib/foreman_remote_execution/engine.rb +11 -8
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/package.json +2 -1
- data/test/functional/api/v2/job_invocations_controller_test.rb +14 -1
- data/test/unit/job_invocation_composer_test.rb +59 -2
- data/test/unit/job_invocation_test.rb +1 -1
- data/webpack/JobWizard/JobWizard.js +80 -19
- data/webpack/JobWizard/JobWizard.scss +42 -1
- data/webpack/JobWizard/JobWizardConstants.js +11 -0
- data/webpack/JobWizard/JobWizardSelectors.js +27 -1
- data/webpack/JobWizard/__tests__/__snapshots__/integration.test.js.snap +43 -0
- data/webpack/JobWizard/__tests__/fixtures.js +128 -0
- data/webpack/JobWizard/__tests__/integration.test.js +84 -0
- data/webpack/JobWizard/steps/AdvancedFields/AdvancedFields.js +110 -0
- data/webpack/JobWizard/steps/AdvancedFields/DescriptionField.js +67 -0
- data/webpack/JobWizard/steps/AdvancedFields/Fields.js +195 -0
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +144 -0
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/DescriptionField.test.js +23 -0
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.js +34 -2
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.test.js +122 -44
- data/webpack/JobWizard/steps/CategoryAndTemplate/index.js +9 -1
- 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 +20 -0
- data/webpack/JobWizard/steps/form/Formatter.js +149 -0
- data/webpack/JobWizard/steps/form/GroupedSelectField.js +3 -0
- data/webpack/JobWizard/steps/form/NumberInput.js +33 -0
- data/webpack/JobWizard/steps/form/SelectField.js +24 -3
- data/webpack/JobWizard/steps/form/__tests__/Formatter.test.js.example +76 -0
- data/webpack/__mocks__/foremanReact/components/SearchBar.js +18 -1
- data/webpack/__mocks__/foremanReact/redux/API/APISelectors.js +21 -2
- data/webpack/global_index.js +5 -3
- data/webpack/index.js +3 -0
- data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +1 -5
- data/webpack/react_app/components/TargetingHosts/__tests__/TargetingHostsSelectors.test.js +8 -3
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +1 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsSelectors.test.js.snap +7 -2
- data/webpack/react_app/extend/{fills.js → fillRecentJobsCard.js} +7 -6
- data/webpack/react_app/extend/fillregistrationAdvanced.js +11 -0
- data/webpack/react_app/extend/reducers.js +2 -1
- metadata +24 -14
- data/app/models/concerns/foreman_remote_execution/orchestration/ssh.rb +0 -70
- data/test/models/orchestration/ssh_test.rb +0 -56
- data/webpack/JobWizard/__tests__/JobWizard.test.js +0 -20
- data/webpack/JobWizard/__tests__/__snapshots__/JobWizard.test.js.snap +0 -83
- data/webpack/JobWizard/steps/CategoryAndTemplate/__snapshots__/CategoryAndTemplate.test.js.snap +0 -64
- 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 -36
- data/webpack/JobWizard/steps/form/__tests__/__snapshots__/SelectField.test.js.snap +0 -22
- data/webpack/fills_index.js +0 -11
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Provider } from 'react-redux';
|
|
3
|
+
import { mount } from '@theforeman/test';
|
|
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
|
+
);
|
|
31
|
+
describe('AdvancedFields', () => {
|
|
32
|
+
it('should save data between steps for advanced fields', async () => {
|
|
33
|
+
const wrapper = mount(
|
|
34
|
+
<Provider store={store}>
|
|
35
|
+
<JobWizard advancedValues={{}} setAdvancedValues={jest.fn()} />
|
|
36
|
+
</Provider>
|
|
37
|
+
);
|
|
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);
|
|
143
|
+
});
|
|
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,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import { Title, Text, TextVariants, Form } from '@patternfly/react-core';
|
|
3
|
+
import { Title, Text, TextVariants, Form, Alert } from '@patternfly/react-core';
|
|
4
4
|
import { translate as __ } from 'foremanReact/common/I18n';
|
|
5
5
|
import { SelectField } from '../form/SelectField';
|
|
6
6
|
import { GroupedSelectField } from '../form/GroupedSelectField';
|
|
@@ -12,6 +12,7 @@ export const CategoryAndTemplate = ({
|
|
|
12
12
|
selectedTemplateID,
|
|
13
13
|
selectedCategory,
|
|
14
14
|
setCategory,
|
|
15
|
+
errors,
|
|
15
16
|
}) => {
|
|
16
17
|
const templatesGroups = {};
|
|
17
18
|
jobTemplates.forEach(template => {
|
|
@@ -35,9 +36,11 @@ export const CategoryAndTemplate = ({
|
|
|
35
36
|
setCategory(newCategory);
|
|
36
37
|
setJobTemplate(null);
|
|
37
38
|
};
|
|
39
|
+
const { categoryError, allTemplatesError, templateError } = errors;
|
|
40
|
+
const isError = !!(categoryError || allTemplatesError || templateError);
|
|
38
41
|
return (
|
|
39
42
|
<>
|
|
40
|
-
<Title headingLevel="h2">{__('Category
|
|
43
|
+
<Title headingLevel="h2">{__('Category and Template')}</Title>
|
|
41
44
|
<Text component={TextVariants.p}>{__('All fields are required.')}</Text>
|
|
42
45
|
<Form>
|
|
43
46
|
<SelectField
|
|
@@ -46,6 +49,8 @@ export const CategoryAndTemplate = ({
|
|
|
46
49
|
options={jobCategories}
|
|
47
50
|
setValue={onSelectCategory}
|
|
48
51
|
value={selectedCategory}
|
|
52
|
+
placeholderText={categoryError ? __('Error') : ''}
|
|
53
|
+
isDisabled={!!categoryError}
|
|
49
54
|
/>
|
|
50
55
|
<GroupedSelectField
|
|
51
56
|
label={__('Job template')}
|
|
@@ -53,7 +58,28 @@ export const CategoryAndTemplate = ({
|
|
|
53
58
|
groups={Object.values(templatesGroups)}
|
|
54
59
|
setSelected={setJobTemplate}
|
|
55
60
|
selected={selectedTemplate}
|
|
61
|
+
isDisabled={!!(categoryError || allTemplatesError)}
|
|
62
|
+
placeholderText={allTemplatesError ? __('Error') : ''}
|
|
56
63
|
/>
|
|
64
|
+
{isError && (
|
|
65
|
+
<Alert variant="danger" title={__('Errors:')}>
|
|
66
|
+
{categoryError && (
|
|
67
|
+
<span>
|
|
68
|
+
{__('Categories list failed with:')} {categoryError}
|
|
69
|
+
</span>
|
|
70
|
+
)}
|
|
71
|
+
{allTemplatesError && (
|
|
72
|
+
<span>
|
|
73
|
+
{__('Templates list failed with:')} {allTemplatesError}
|
|
74
|
+
</span>
|
|
75
|
+
)}
|
|
76
|
+
{templateError && (
|
|
77
|
+
<span>
|
|
78
|
+
{__('Template failed with:')} {templateError}
|
|
79
|
+
</span>
|
|
80
|
+
)}
|
|
81
|
+
</Alert>
|
|
82
|
+
)}
|
|
57
83
|
</Form>
|
|
58
84
|
</>
|
|
59
85
|
);
|
|
@@ -66,12 +92,18 @@ CategoryAndTemplate.propTypes = {
|
|
|
66
92
|
selectedTemplateID: PropTypes.number,
|
|
67
93
|
setCategory: PropTypes.func.isRequired,
|
|
68
94
|
selectedCategory: PropTypes.string,
|
|
95
|
+
errors: PropTypes.shape({
|
|
96
|
+
categoryError: PropTypes.string,
|
|
97
|
+
allTemplatesError: PropTypes.string,
|
|
98
|
+
templateError: PropTypes.string,
|
|
99
|
+
}),
|
|
69
100
|
};
|
|
70
101
|
CategoryAndTemplate.defaultProps = {
|
|
71
102
|
jobCategories: [],
|
|
72
103
|
jobTemplates: [],
|
|
73
104
|
selectedTemplateID: null,
|
|
74
105
|
selectedCategory: null,
|
|
106
|
+
errors: {},
|
|
75
107
|
};
|
|
76
108
|
|
|
77
109
|
export default CategoryAndTemplate;
|
|
@@ -1,45 +1,123 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
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
|
+
});
|
|
45
123
|
});
|
|
@@ -8,6 +8,9 @@ import {
|
|
|
8
8
|
selectJobTemplates,
|
|
9
9
|
selectJobCategoriesStatus,
|
|
10
10
|
filterJobTemplates,
|
|
11
|
+
selectCategoryError,
|
|
12
|
+
selectAllTemplatesError,
|
|
13
|
+
selectTemplateError,
|
|
11
14
|
} from '../../JobWizardSelectors';
|
|
12
15
|
import { CategoryAndTemplate } from './CategoryAndTemplate';
|
|
13
16
|
|
|
@@ -41,7 +44,6 @@ const ConnectedCategoryAndTemplate = ({
|
|
|
41
44
|
}, [jobCategoriesStatus, dispatch, setCategory]);
|
|
42
45
|
|
|
43
46
|
const jobCategories = useSelector(selectJobCategories);
|
|
44
|
-
|
|
45
47
|
useEffect(() => {
|
|
46
48
|
if (category) {
|
|
47
49
|
const templatesUrlObject = new URI(templatesUrl);
|
|
@@ -63,6 +65,11 @@ const ConnectedCategoryAndTemplate = ({
|
|
|
63
65
|
|
|
64
66
|
const jobTemplates = useSelector(selectJobTemplates);
|
|
65
67
|
|
|
68
|
+
const errors = {
|
|
69
|
+
categoryError: useSelector(selectCategoryError),
|
|
70
|
+
allTemplatesError: useSelector(selectAllTemplatesError),
|
|
71
|
+
templateError: useSelector(selectTemplateError),
|
|
72
|
+
};
|
|
66
73
|
return (
|
|
67
74
|
<CategoryAndTemplate
|
|
68
75
|
jobTemplates={jobTemplates}
|
|
@@ -71,6 +78,7 @@ const ConnectedCategoryAndTemplate = ({
|
|
|
71
78
|
selectedTemplateID={jobTemplate}
|
|
72
79
|
setCategory={setCategory}
|
|
73
80
|
selectedCategory={category}
|
|
81
|
+
errors={errors}
|
|
74
82
|
/>
|
|
75
83
|
);
|
|
76
84
|
};
|