foreman_remote_execution 16.2.0 → 16.2.2
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/assets/javascripts/foreman_remote_execution/locale/de/foreman_remote_execution.js +3 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/en_GB/foreman_remote_execution.js +3 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/es/foreman_remote_execution.js +3 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/fr/foreman_remote_execution.js +3 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/ja/foreman_remote_execution.js +3 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/ka/foreman_remote_execution.js +3 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/ko/foreman_remote_execution.js +4 -1
- data/app/assets/javascripts/foreman_remote_execution/locale/pt_BR/foreman_remote_execution.js +3 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/ru/foreman_remote_execution.js +4 -1
- data/app/assets/javascripts/foreman_remote_execution/locale/zh_CN/foreman_remote_execution.js +4 -1
- data/app/assets/javascripts/foreman_remote_execution/locale/zh_TW/foreman_remote_execution.js +3 -0
- data/config/routes.rb +2 -1
- data/extra/cockpit/foreman-cockpit-session +3 -2
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/locale/de/foreman_remote_execution.po +3 -0
- data/locale/en_GB/foreman_remote_execution.po +3 -0
- data/locale/es/foreman_remote_execution.po +3 -0
- data/locale/foreman_remote_execution.pot +17 -13
- data/locale/fr/foreman_remote_execution.po +3 -0
- data/locale/ja/foreman_remote_execution.po +3 -0
- data/locale/ka/foreman_remote_execution.po +3 -0
- data/locale/ko/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ko/foreman_remote_execution.po +4 -1
- data/locale/pt_BR/foreman_remote_execution.po +3 -0
- data/locale/ru/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ru/foreman_remote_execution.po +4 -1
- data/locale/zh_CN/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/zh_CN/foreman_remote_execution.po +4 -1
- data/locale/zh_TW/foreman_remote_execution.po +3 -0
- data/webpack/JobInvocationDetail/CheckboxesActions.js +39 -30
- data/webpack/JobInvocationDetail/JobInvocationConstants.js +0 -1
- data/webpack/JobInvocationDetail/JobInvocationHostTable.js +156 -170
- data/webpack/JobInvocationDetail/OpenAllInvocationsModal.js +27 -32
- data/webpack/JobInvocationDetail/__tests__/MainInformation.test.js +10 -2
- data/webpack/JobInvocationDetail/__tests__/TableToolbarActions.test.js +46 -30
- data/webpack/JobInvocationDetail/index.js +13 -2
- data/webpack/JobWizard/Footer.js +7 -1
- data/webpack/JobWizard/steps/HostsAndInputs/buildHostQuery.js +1 -1
- data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +5 -6
- data/webpack/react_app/components/RegistrationExtension/RexInterface.js +5 -19
- data/webpack/react_app/components/RegistrationExtension/__tests__/RexInterface.test.js +19 -3
- metadata +2 -3
- data/webpack/react_app/components/RegistrationExtension/__tests__/__snapshots__/RexInterface.test.js.snap +0 -36
@@ -32,6 +32,29 @@ selectors.selectTaskCancelable.mockImplementation(() => true);
|
|
32
32
|
const mockStore = configureStore([]);
|
33
33
|
const store = mockStore({});
|
34
34
|
|
35
|
+
const allJobs = [
|
36
|
+
{
|
37
|
+
id: 1,
|
38
|
+
job_status: 'error',
|
39
|
+
},
|
40
|
+
{
|
41
|
+
id: 2,
|
42
|
+
job_status: 'error',
|
43
|
+
},
|
44
|
+
{
|
45
|
+
id: 3,
|
46
|
+
job_status: 'error',
|
47
|
+
},
|
48
|
+
{
|
49
|
+
id: 4,
|
50
|
+
job_status: 'error',
|
51
|
+
},
|
52
|
+
{
|
53
|
+
id: 5,
|
54
|
+
job_status: 'error',
|
55
|
+
},
|
56
|
+
];
|
57
|
+
|
35
58
|
describe('TableToolbarActions', () => {
|
36
59
|
const jobID = '42';
|
37
60
|
let openSpy;
|
@@ -58,8 +81,9 @@ describe('TableToolbarActions', () => {
|
|
58
81
|
<Provider store={store}>
|
59
82
|
<CheckboxesActions
|
60
83
|
selectedIds={selectedIds}
|
61
|
-
failedCount={0}
|
62
84
|
jobID={jobID}
|
85
|
+
allJobs={allJobs}
|
86
|
+
setShowAlert={jest.fn()}
|
63
87
|
/>
|
64
88
|
</Provider>
|
65
89
|
);
|
@@ -76,8 +100,9 @@ describe('TableToolbarActions', () => {
|
|
76
100
|
<Provider store={store}>
|
77
101
|
<CheckboxesActions
|
78
102
|
selectedIds={selectedIds}
|
79
|
-
failedCount={0}
|
80
103
|
jobID={jobID}
|
104
|
+
allJobs={allJobs}
|
105
|
+
setShowAlert={jest.fn()}
|
81
106
|
/>
|
82
107
|
</Provider>
|
83
108
|
);
|
@@ -94,47 +119,52 @@ describe('TableToolbarActions', () => {
|
|
94
119
|
test('shows alert when popups are blocked', async () => {
|
95
120
|
openSpy.mockReturnValue(null);
|
96
121
|
const selectedIds = [1, 2];
|
122
|
+
const setShowMock = jest.fn();
|
97
123
|
render(
|
98
124
|
<Provider store={store}>
|
99
125
|
<CheckboxesActions
|
100
126
|
selectedIds={selectedIds}
|
101
|
-
failedCount={0}
|
102
127
|
jobID={jobID}
|
128
|
+
allJobs={allJobs}
|
129
|
+
setShowAlert={setShowMock}
|
103
130
|
/>
|
104
131
|
</Provider>
|
105
132
|
);
|
106
133
|
fireEvent.click(
|
107
134
|
screen.getByLabelText(/open all template invocations in new tab/i)
|
108
135
|
);
|
109
|
-
expect(
|
110
|
-
await screen.findByText(/Popups are blocked by your browser/)
|
111
|
-
).toBeInTheDocument();
|
136
|
+
expect(setShowMock).toHaveBeenCalledWith(true);
|
112
137
|
});
|
113
138
|
});
|
114
139
|
|
115
140
|
describe('Opening failed in new tabs', () => {
|
116
141
|
test('opens links when results length is less than or equal to 3', async () => {
|
117
|
-
const failedHosts = [{ id: 301 }, { id: 302 }];
|
118
|
-
useAPI.mockReturnValue({
|
119
|
-
response: { results: failedHosts },
|
120
|
-
status: 'success',
|
121
|
-
});
|
122
142
|
render(
|
123
143
|
<Provider store={store}>
|
124
|
-
<CheckboxesActions
|
144
|
+
<CheckboxesActions
|
145
|
+
selectedIds={[]}
|
146
|
+
jobID={jobID}
|
147
|
+
allJobs={allJobs.slice(0, 2)}
|
148
|
+
setShowAlert={jest.fn()}
|
149
|
+
/>
|
125
150
|
</Provider>
|
126
151
|
);
|
127
152
|
fireEvent.click(screen.getByLabelText(/actions dropdown toggle/i));
|
128
153
|
fireEvent.click(await screen.findByText(/open all failed runs/i));
|
129
154
|
await waitFor(() => {
|
130
|
-
expect(openSpy).toHaveBeenCalledTimes(
|
155
|
+
expect(openSpy).toHaveBeenCalledTimes(2);
|
131
156
|
});
|
132
157
|
});
|
133
158
|
|
134
159
|
test('shows modal when results length is greater than 3', async () => {
|
135
160
|
render(
|
136
161
|
<Provider store={store}>
|
137
|
-
<CheckboxesActions
|
162
|
+
<CheckboxesActions
|
163
|
+
selectedIds={[]}
|
164
|
+
jobID={jobID}
|
165
|
+
allJobs={allJobs}
|
166
|
+
setShowAlert={jest.fn()}
|
167
|
+
/>
|
138
168
|
</Provider>
|
139
169
|
);
|
140
170
|
fireEvent.click(screen.getByLabelText(/actions dropdown toggle/i));
|
@@ -145,21 +175,6 @@ describe('TableToolbarActions', () => {
|
|
145
175
|
})
|
146
176
|
).toBeInTheDocument();
|
147
177
|
});
|
148
|
-
|
149
|
-
test('calls useApi with skip: true when failedCount is 0', () => {
|
150
|
-
render(
|
151
|
-
<Provider store={store}>
|
152
|
-
<CheckboxesActions selectedIds={[]} failedCount={0} jobID={jobID} />
|
153
|
-
</Provider>
|
154
|
-
);
|
155
|
-
expect(useAPI).toHaveBeenCalledWith(
|
156
|
-
'get',
|
157
|
-
foremanUrl(`/api/job_invocations/${jobID}/hosts`),
|
158
|
-
expect.objectContaining({
|
159
|
-
skip: true,
|
160
|
-
})
|
161
|
-
);
|
162
|
-
});
|
163
178
|
});
|
164
179
|
|
165
180
|
describe('PopupAlert', () => {
|
@@ -188,8 +203,9 @@ describe('TableToolbarActions', () => {
|
|
188
203
|
<CheckboxesActions
|
189
204
|
bulkParams="(id ^ (101, 102, 103))"
|
190
205
|
selectedIds={selectedIds}
|
191
|
-
failedCount={1}
|
192
206
|
jobID={jobID}
|
207
|
+
allJobs={allJobs}
|
208
|
+
setShowAlert={jest.fn()}
|
193
209
|
/>
|
194
210
|
</Provider>
|
195
211
|
);
|
@@ -35,12 +35,12 @@ const JobInvocationDetailPage = ({
|
|
35
35
|
match: {
|
36
36
|
params: { id },
|
37
37
|
},
|
38
|
+
history,
|
38
39
|
}) => {
|
39
40
|
const dispatch = useDispatch();
|
40
41
|
const items = useSelector(selectItems);
|
41
42
|
const {
|
42
43
|
description,
|
43
|
-
failed = 0,
|
44
44
|
status_label: statusLabel,
|
45
45
|
task,
|
46
46
|
start_at: startAt,
|
@@ -95,6 +95,16 @@ const JobInvocationDetailPage = ({
|
|
95
95
|
: STATUS_UPPERCASE.RESOLVED;
|
96
96
|
|
97
97
|
const breadcrumbOptions = {
|
98
|
+
isSwitchable: true,
|
99
|
+
onSwitcherItemClick: (e, href) => {
|
100
|
+
e.preventDefault();
|
101
|
+
history.push(href);
|
102
|
+
},
|
103
|
+
resource: {
|
104
|
+
nameField: 'description',
|
105
|
+
resourceUrl: '/api/v2/job_invocations',
|
106
|
+
switcherItemUrl: '/job_invocations/:id',
|
107
|
+
},
|
98
108
|
breadcrumbItems: [
|
99
109
|
{ caption: __('Jobs'), url: `/job_invocations` },
|
100
110
|
{
|
@@ -182,7 +192,7 @@ const JobInvocationDetailPage = ({
|
|
182
192
|
<JobInvocationHostTable
|
183
193
|
id={id}
|
184
194
|
targeting={targeting}
|
185
|
-
|
195
|
+
finished={finished}
|
186
196
|
autoRefresh={autoRefresh}
|
187
197
|
initialFilter={selectedFilter}
|
188
198
|
statusLabel={statusLabel}
|
@@ -200,6 +210,7 @@ JobInvocationDetailPage.propTypes = {
|
|
200
210
|
id: PropTypes.string.isRequired,
|
201
211
|
}),
|
202
212
|
}).isRequired,
|
213
|
+
history: PropTypes.object.isRequired,
|
203
214
|
};
|
204
215
|
|
205
216
|
export default JobInvocationDetailPage;
|
data/webpack/JobWizard/Footer.js
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import React from 'react';
|
1
|
+
import React, { useRef } from 'react';
|
2
2
|
import { useSelector } from 'react-redux';
|
3
3
|
import PropTypes from 'prop-types';
|
4
4
|
import { Button, Tooltip, Spinner } from '@patternfly/react-core';
|
@@ -12,6 +12,8 @@ import { selectIsSubmitting } from './JobWizardSelectors';
|
|
12
12
|
|
13
13
|
export const Footer = ({ canSubmit, onSave }) => {
|
14
14
|
const isSubmitting = useSelector(selectIsSubmitting);
|
15
|
+
const tooltipRunOn = useRef(null);
|
16
|
+
const tooltipSkipTo = useRef(null);
|
15
17
|
return (
|
16
18
|
<WizardFooter>
|
17
19
|
<WizardContextConsumer>
|
@@ -52,6 +54,7 @@ export const Footer = ({ canSubmit, onSave }) => {
|
|
52
54
|
: __('Fill all required fields in all the steps')}
|
53
55
|
</div>
|
54
56
|
}
|
57
|
+
triggerRef={tooltipRunOn}
|
55
58
|
>
|
56
59
|
<Button
|
57
60
|
ouiaId="run-on-selected-hosts-footer"
|
@@ -59,6 +62,7 @@ export const Footer = ({ canSubmit, onSave }) => {
|
|
59
62
|
onClick={onSave}
|
60
63
|
isAriaDisabled={!canSubmit}
|
61
64
|
isDisabled={isSubmitting}
|
65
|
+
ref={tooltipRunOn}
|
62
66
|
>
|
63
67
|
{__('Run on selected hosts')}
|
64
68
|
</Button>
|
@@ -73,6 +77,7 @@ export const Footer = ({ canSubmit, onSave }) => {
|
|
73
77
|
)}
|
74
78
|
</div>
|
75
79
|
}
|
80
|
+
triggerRef={tooltipSkipTo}
|
76
81
|
>
|
77
82
|
<Button
|
78
83
|
ouiaId="skip-to-review-footer"
|
@@ -80,6 +85,7 @@ export const Footer = ({ canSubmit, onSave }) => {
|
|
80
85
|
onClick={() => goToStepByName(WIZARD_TITLES.review)}
|
81
86
|
isAriaDisabled={!canSubmit}
|
82
87
|
isDisabled={isSubmitting}
|
88
|
+
ref={tooltipSkipTo}
|
83
89
|
>
|
84
90
|
{__('Skip to review')}
|
85
91
|
</Button>
|
@@ -18,7 +18,7 @@ export const buildHostQuery = (selected, search) => {
|
|
18
18
|
.join(',')})`;
|
19
19
|
|
20
20
|
let hostGroupsSearch;
|
21
|
-
if (
|
21
|
+
if (hostGroups.length < MAX_NAME_ITEMS)
|
22
22
|
hostGroupsSearch = `hostgroup_fullname ^ (${hostGroups
|
23
23
|
.map(({ name }) => nameEscape(name))
|
24
24
|
.join(',')})`;
|
@@ -1,8 +1,7 @@
|
|
1
1
|
import PropTypes from 'prop-types';
|
2
2
|
import React, { useState } from 'react';
|
3
3
|
|
4
|
-
import { Tabs, Tab, TabTitleText } from '@patternfly/react-core';
|
5
|
-
import { DropdownItem } from '@patternfly/react-core/deprecated';
|
4
|
+
import { DropdownItem, Tabs, Tab, TabTitleText } from '@patternfly/react-core';
|
6
5
|
import CardTemplate from 'foremanReact/components/HostDetails/Templates/CardItem/CardTemplate';
|
7
6
|
import { translate as __ } from 'foremanReact/common/I18n';
|
8
7
|
import { foremanUrl } from 'foremanReact/common/helpers';
|
@@ -26,14 +25,14 @@ const RecentJobsCard = ({ hostDetails: { name, id } }) => {
|
|
26
25
|
header={__('Recent jobs')}
|
27
26
|
dropdownItems={[
|
28
27
|
<DropdownItem
|
29
|
-
|
28
|
+
to={foremanUrl(`${JOB_BASE_URL}${id}`)}
|
30
29
|
key="link-to-all"
|
31
30
|
ouiaId="link-to-all-dropdown-item"
|
32
31
|
>
|
33
32
|
{__('View all jobs')}
|
34
33
|
</DropdownItem>,
|
35
34
|
<DropdownItem
|
36
|
-
|
35
|
+
to={foremanUrl(
|
37
36
|
`${JOB_BASE_URL}${id}+and+status+%3D+failed+or+status%3D+succeeded`
|
38
37
|
)}
|
39
38
|
key="link-to-finished"
|
@@ -42,14 +41,14 @@ const RecentJobsCard = ({ hostDetails: { name, id } }) => {
|
|
42
41
|
{__('View finished jobs')}
|
43
42
|
</DropdownItem>,
|
44
43
|
<DropdownItem
|
45
|
-
|
44
|
+
to={foremanUrl(`${JOB_BASE_URL}${id}+and+status+%3D+running`)}
|
46
45
|
key="link-to-running"
|
47
46
|
ouiaId="link-to-running-dropdown-item"
|
48
47
|
>
|
49
48
|
{__('View running jobs')}
|
50
49
|
</DropdownItem>,
|
51
50
|
<DropdownItem
|
52
|
-
|
51
|
+
to={foremanUrl(`${JOB_BASE_URL}${id}+and+status+%3D+queued`)}
|
53
52
|
key="link-to-scheduled"
|
54
53
|
ouiaId="link-to-scheduled-dropdown-item"
|
55
54
|
>
|
@@ -2,32 +2,18 @@ import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
3
3
|
|
4
4
|
import { translate as __ } from 'foremanReact/common/I18n';
|
5
|
+
import LabelIcon from 'foremanReact/components/common/LabelIcon';
|
5
6
|
|
6
|
-
import { FormGroup, TextInput
|
7
|
-
|
8
|
-
import { HelpIcon } from '@patternfly/react-icons';
|
7
|
+
import { FormGroup, TextInput } from '@patternfly/react-core';
|
9
8
|
|
10
9
|
const RexInterface = ({ isLoading, onChange }) => (
|
11
10
|
<FormGroup
|
12
11
|
label={__('Remote Execution Interface')}
|
13
12
|
fieldId="reg_rex_interface"
|
14
13
|
labelIcon={
|
15
|
-
<
|
16
|
-
|
17
|
-
|
18
|
-
{__('Identifier of the Host interface for Remote execution')}
|
19
|
-
</div>
|
20
|
-
}
|
21
|
-
>
|
22
|
-
<button
|
23
|
-
className="pf-v5-cform__group-label-help"
|
24
|
-
onClick={e => e.preventDefault()}
|
25
|
-
>
|
26
|
-
<Icon isInline>
|
27
|
-
<HelpIcon />
|
28
|
-
</Icon>
|
29
|
-
</button>
|
30
|
-
</Popover>
|
14
|
+
<LabelIcon
|
15
|
+
text={__('Identifier of the Host interface for Remote execution')}
|
16
|
+
/>
|
31
17
|
}
|
32
18
|
>
|
33
19
|
<TextInput
|
@@ -1,9 +1,25 @@
|
|
1
|
-
import
|
1
|
+
import React from 'react';
|
2
|
+
import { screen, fireEvent, render, act } from '@testing-library/react';
|
3
|
+
import '@testing-library/jest-dom/extend-expect';
|
2
4
|
import RexInterface from '../RexInterface';
|
3
5
|
|
4
6
|
const fixtures = {
|
5
7
|
renders: { isLoading: false, onChange: () => {} },
|
6
8
|
};
|
7
9
|
|
8
|
-
describe('RexInterface', () =>
|
9
|
-
|
10
|
+
describe('RexInterface', () => {
|
11
|
+
it('should render label with help icon and popover instructions', async () => {
|
12
|
+
jest.useFakeTimers();
|
13
|
+
render(<RexInterface {...fixtures.renders} />);
|
14
|
+
await act(async () => {
|
15
|
+
await fireEvent.click(screen.getByRole('button'));
|
16
|
+
|
17
|
+
jest.advanceTimersByTime(500);
|
18
|
+
});
|
19
|
+
|
20
|
+
expect(screen.getByText('Remote Execution Interface')).toBeInTheDocument();
|
21
|
+
expect(
|
22
|
+
screen.getByText('Identifier of the Host interface for Remote execution')
|
23
|
+
).toBeInTheDocument();
|
24
|
+
});
|
25
|
+
});
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foreman_remote_execution
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 16.2.
|
4
|
+
version: 16.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Foreman Remote Execution team
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-10-07 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: deface
|
@@ -551,7 +551,6 @@ files:
|
|
551
551
|
- webpack/react_app/components/RegistrationExtension/RexInterface.js
|
552
552
|
- webpack/react_app/components/RegistrationExtension/RexPull.js
|
553
553
|
- webpack/react_app/components/RegistrationExtension/__tests__/RexInterface.test.js
|
554
|
-
- webpack/react_app/components/RegistrationExtension/__tests__/__snapshots__/RexInterface.test.js.snap
|
555
554
|
- webpack/react_app/components/TargetingHosts/TargetingHosts.js
|
556
555
|
- webpack/react_app/components/TargetingHosts/TargetingHostsConsts.js
|
557
556
|
- webpack/react_app/components/TargetingHosts/TargetingHostsHelpers.js
|
@@ -1,36 +0,0 @@
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
2
|
-
|
3
|
-
exports[`RexInterface renders 1`] = `
|
4
|
-
<FormGroup
|
5
|
-
fieldId="reg_rex_interface"
|
6
|
-
label="Remote Execution Interface"
|
7
|
-
labelIcon={
|
8
|
-
<Popover
|
9
|
-
bodyContent={
|
10
|
-
<div>
|
11
|
-
Identifier of the Host interface for Remote execution
|
12
|
-
</div>
|
13
|
-
}
|
14
|
-
>
|
15
|
-
<button
|
16
|
-
className="pf-v5-cform__group-label-help"
|
17
|
-
onClick={[Function]}
|
18
|
-
>
|
19
|
-
<Icon
|
20
|
-
isInline={true}
|
21
|
-
>
|
22
|
-
<HelpIcon />
|
23
|
-
</Icon>
|
24
|
-
</button>
|
25
|
-
</Popover>
|
26
|
-
}
|
27
|
-
>
|
28
|
-
<TextInput
|
29
|
-
id="reg_rex_interface_input"
|
30
|
-
isDisabled={false}
|
31
|
-
onBlur={[Function]}
|
32
|
-
ouiaId="reg_rex_interface_input"
|
33
|
-
type="text"
|
34
|
-
/>
|
35
|
-
</FormGroup>
|
36
|
-
`;
|