foreman_rh_cloud 13.2.5 → 13.2.7
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/lib/foreman_rh_cloud/version.rb +1 -1
- data/package.json +1 -1
- data/webpack/CVEsHostDetailsTab/CVEsHostDetailsTab.js +6 -1
- data/webpack/CVEsHostDetailsTab/__tests__/CVEsHostDetailsTab.test.js +45 -6
- data/webpack/InsightsHostDetailsTab/NewHostDetailsTab.js +44 -14
- data/webpack/InsightsHostDetailsTab/__tests__/NewHostDetailsTab.test.js +154 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 59eab13298515d3f87c52f499b2d79522b5455985ed8c2652400df1790fe7133
|
|
4
|
+
data.tar.gz: 14f62161dbd0e8b635b9d7728f41d121c6be2b76cc042b4c7168d20ca2376641
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2da9750fa293b99a458766c2b08ed045f1d39c07f5275918d8900f146764226654579a92ced0fe033666faf94e4067b8522e2991c19522be5bd067e6f705ef68
|
|
7
|
+
data.tar.gz: e3a262a6166e9c4c5901f4c57d052b24c405756ed0a71acfb3dd71d62a66e99f09d6ff5f0df904d3a932c68d58204466689a587d1c1f723a27e769b9c9789234
|
data/package.json
CHANGED
|
@@ -10,7 +10,12 @@ const CVEsHostDetailsTab = ({ systemId }) => {
|
|
|
10
10
|
const module = './SystemDetailTable';
|
|
11
11
|
return (
|
|
12
12
|
<div className="rh-cloud-insights-vulnerability-host-details-component vulnerability">
|
|
13
|
-
<ScalprumComponent
|
|
13
|
+
<ScalprumComponent
|
|
14
|
+
key={systemId}
|
|
15
|
+
scope={scope}
|
|
16
|
+
module={module}
|
|
17
|
+
systemId={systemId}
|
|
18
|
+
/>
|
|
14
19
|
</div>
|
|
15
20
|
);
|
|
16
21
|
};
|
|
@@ -11,14 +11,25 @@ jest.mock('foremanReact/Root/Context/ForemanContext', () => ({
|
|
|
11
11
|
useForemanPermissions: () => new Set(['view_vulnerability']),
|
|
12
12
|
}));
|
|
13
13
|
|
|
14
|
-
jest.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
const mockUnmountTracker = jest.fn();
|
|
15
|
+
jest.mock('@scalprum/react-core', () => {
|
|
16
|
+
const ReactMock = require('react');
|
|
17
|
+
return {
|
|
18
|
+
ScalprumComponent: jest.fn(props => {
|
|
19
|
+
ReactMock.useEffect(() => mockUnmountTracker, []);
|
|
20
|
+
return (
|
|
21
|
+
<div data-testid="mock-scalprum-component">{JSON.stringify(props)}</div>
|
|
22
|
+
);
|
|
23
|
+
}),
|
|
24
|
+
ScalprumProvider: jest.fn(({ children }) => <div>{children}</div>),
|
|
25
|
+
};
|
|
26
|
+
});
|
|
20
27
|
|
|
21
28
|
describe('CVEsHostDetailsTabWrapper', () => {
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
jest.clearAllMocks();
|
|
31
|
+
});
|
|
32
|
+
|
|
22
33
|
it('renders without crashing', () => {
|
|
23
34
|
const { container } = render(
|
|
24
35
|
<CVEsHostDetailsTabWrapper
|
|
@@ -31,4 +42,32 @@ describe('CVEsHostDetailsTabWrapper', () => {
|
|
|
31
42
|
)
|
|
32
43
|
).toBeTruthy();
|
|
33
44
|
});
|
|
45
|
+
|
|
46
|
+
it('remounts ScalprumComponent when systemId changes', () => {
|
|
47
|
+
const { ScalprumComponent } = require('@scalprum/react-core');
|
|
48
|
+
|
|
49
|
+
const { rerender } = render(
|
|
50
|
+
<CVEsHostDetailsTabWrapper
|
|
51
|
+
response={{ subscription_facet_attributes: { uuid: 'uuid-host-A' } }}
|
|
52
|
+
/>
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
expect(mockUnmountTracker).not.toHaveBeenCalled();
|
|
56
|
+
expect(ScalprumComponent).toHaveBeenLastCalledWith(
|
|
57
|
+
expect.objectContaining({ systemId: 'uuid-host-A' }),
|
|
58
|
+
expect.anything()
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
rerender(
|
|
62
|
+
<CVEsHostDetailsTabWrapper
|
|
63
|
+
response={{ subscription_facet_attributes: { uuid: 'uuid-host-B' } }}
|
|
64
|
+
/>
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
expect(mockUnmountTracker).toHaveBeenCalledTimes(1);
|
|
68
|
+
expect(ScalprumComponent).toHaveBeenLastCalledWith(
|
|
69
|
+
expect.objectContaining({ systemId: 'uuid-host-B' }),
|
|
70
|
+
expect.anything()
|
|
71
|
+
);
|
|
72
|
+
});
|
|
34
73
|
});
|
|
@@ -33,13 +33,28 @@ const NewHostDetailsTab = ({ hostName, router }) => {
|
|
|
33
33
|
const hits = useSelector(selectHits);
|
|
34
34
|
const isIop = useIopConfig();
|
|
35
35
|
|
|
36
|
-
useEffect(
|
|
36
|
+
useEffect(
|
|
37
|
+
() => () => {
|
|
38
|
+
// Preserve hash when clearing search params to prevent tab navigation bugs
|
|
39
|
+
if (router && typeof router.replace === 'function') {
|
|
40
|
+
const replaceOptions = { search: null };
|
|
41
|
+
if (router.location && router.location.hash) {
|
|
42
|
+
replaceOptions.hash = router.location.hash;
|
|
43
|
+
}
|
|
44
|
+
router.replace(replaceOptions);
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
[router]
|
|
48
|
+
);
|
|
37
49
|
|
|
38
50
|
const onSearch = q => dispatch(fetchInsights({ query: q, page: 1 }));
|
|
39
51
|
|
|
40
52
|
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
|
41
|
-
const onSatInsightsClick = () =>
|
|
42
|
-
router.push
|
|
53
|
+
const onSatInsightsClick = () => {
|
|
54
|
+
if (router && typeof router.push === 'function') {
|
|
55
|
+
router.push({ pathname: '/foreman_rh_cloud/insights_cloud' });
|
|
56
|
+
}
|
|
57
|
+
};
|
|
43
58
|
|
|
44
59
|
const dropdownItems = [
|
|
45
60
|
<DropdownItem key="insights-link" ouiaId="insights-link">
|
|
@@ -113,17 +128,32 @@ NewHostDetailsTab.defaultProps = {
|
|
|
113
128
|
const scope = 'advisor';
|
|
114
129
|
const module = './SystemDetailWrapped';
|
|
115
130
|
|
|
116
|
-
const IopInsightsTab = props =>
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
131
|
+
const IopInsightsTab = props => {
|
|
132
|
+
// eslint-disable-next-line camelcase
|
|
133
|
+
const systemId = props.response?.subscription_facet_attributes?.uuid;
|
|
134
|
+
return (
|
|
135
|
+
<div className="advisor">
|
|
136
|
+
<ScalprumComponent
|
|
137
|
+
key={systemId || props.hostName}
|
|
138
|
+
scope={scope}
|
|
139
|
+
module={module}
|
|
140
|
+
IopRemediationModal={RemediationModal}
|
|
141
|
+
generateRuleUrl={generateRuleUrl}
|
|
142
|
+
{...props}
|
|
143
|
+
/>
|
|
144
|
+
</div>
|
|
145
|
+
);
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
IopInsightsTab.propTypes = {
|
|
149
|
+
hostName: PropTypes.string,
|
|
150
|
+
response: PropTypes.object,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
IopInsightsTab.defaultProps = {
|
|
154
|
+
hostName: '',
|
|
155
|
+
response: {},
|
|
156
|
+
};
|
|
127
157
|
|
|
128
158
|
const IopInsightsTabWrapped = props => {
|
|
129
159
|
const permissions = useInsightsPermissions();
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom';
|
|
4
|
+
import { Provider } from 'react-redux';
|
|
5
|
+
import configureMockStore from 'redux-mock-store';
|
|
6
|
+
import thunk from 'redux-thunk';
|
|
7
|
+
import NewHostDetailsTab from '../NewHostDetailsTab';
|
|
8
|
+
|
|
9
|
+
jest.mock('../../common/Hooks/ConfigHooks', () => ({
|
|
10
|
+
useIopConfig: jest.fn(() => false),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
jest.mock('foremanReact/common/I18n', () => ({
|
|
14
|
+
translate: jest.fn(str => str),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
const mockStore = configureMockStore([thunk]);
|
|
18
|
+
|
|
19
|
+
describe('NewHostDetailsTab', () => {
|
|
20
|
+
let store;
|
|
21
|
+
let mockRouter;
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
mockRouter = {
|
|
25
|
+
push: jest.fn(),
|
|
26
|
+
replace: jest.fn(),
|
|
27
|
+
location: {
|
|
28
|
+
pathname: '/new/hosts/test-host.example.com',
|
|
29
|
+
search: '?page=1&per_page=20',
|
|
30
|
+
hash: '#/Insights',
|
|
31
|
+
query: { page: '1', per_page: '20' },
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
store = mockStore({
|
|
36
|
+
API: {},
|
|
37
|
+
ForemanRhCloud: {
|
|
38
|
+
InsightsCloudSync: {
|
|
39
|
+
table: {
|
|
40
|
+
selectedIds: {},
|
|
41
|
+
isAllSelected: false,
|
|
42
|
+
showSelectAllAlert: false,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
insightsHostDetailsTab: {
|
|
47
|
+
query: '',
|
|
48
|
+
hits: [],
|
|
49
|
+
selectedIds: {},
|
|
50
|
+
error: null,
|
|
51
|
+
},
|
|
52
|
+
router: {
|
|
53
|
+
location: {
|
|
54
|
+
pathname: '/new/hosts/test-host.example.com',
|
|
55
|
+
search: '?page=1&per_page=20',
|
|
56
|
+
hash: '#/Insights',
|
|
57
|
+
query: { page: '1', per_page: '20' },
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
afterEach(() => {
|
|
64
|
+
jest.clearAllMocks();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe('cleanup effect', () => {
|
|
68
|
+
it('should preserve hash when clearing search params on unmount', () => {
|
|
69
|
+
const { unmount } = render(
|
|
70
|
+
<Provider store={store}>
|
|
71
|
+
<NewHostDetailsTab
|
|
72
|
+
hostName="test-host.example.com"
|
|
73
|
+
router={mockRouter}
|
|
74
|
+
/>
|
|
75
|
+
</Provider>
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// Unmount the component to trigger cleanup
|
|
79
|
+
unmount();
|
|
80
|
+
|
|
81
|
+
// Verify router.replace was called with both search: null AND the existing hash
|
|
82
|
+
expect(mockRouter.replace).toHaveBeenCalledWith({
|
|
83
|
+
search: null,
|
|
84
|
+
hash: '#/Insights',
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should only clear search params when no hash exists', () => {
|
|
89
|
+
mockRouter.location.hash = '';
|
|
90
|
+
|
|
91
|
+
const { unmount } = render(
|
|
92
|
+
<Provider store={store}>
|
|
93
|
+
<NewHostDetailsTab
|
|
94
|
+
hostName="test-host.example.com"
|
|
95
|
+
router={mockRouter}
|
|
96
|
+
/>
|
|
97
|
+
</Provider>
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
unmount();
|
|
101
|
+
|
|
102
|
+
// When there's no hash, should only pass search: null
|
|
103
|
+
expect(mockRouter.replace).toHaveBeenCalledWith({
|
|
104
|
+
search: null,
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should handle router.location being undefined gracefully', () => {
|
|
109
|
+
const routerWithoutLocation = {
|
|
110
|
+
push: jest.fn(),
|
|
111
|
+
replace: jest.fn(),
|
|
112
|
+
location: undefined,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const { unmount } = render(
|
|
116
|
+
<Provider store={store}>
|
|
117
|
+
<NewHostDetailsTab
|
|
118
|
+
hostName="test-host.example.com"
|
|
119
|
+
router={routerWithoutLocation}
|
|
120
|
+
/>
|
|
121
|
+
</Provider>
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
unmount();
|
|
125
|
+
|
|
126
|
+
// Should still call replace with search: null even if location is undefined
|
|
127
|
+
expect(routerWithoutLocation.replace).toHaveBeenCalledWith({
|
|
128
|
+
search: null,
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should use the latest hash value at unmount time, not a stale captured value', () => {
|
|
133
|
+
const { unmount } = render(
|
|
134
|
+
<Provider store={store}>
|
|
135
|
+
<NewHostDetailsTab
|
|
136
|
+
hostName="test-host.example.com"
|
|
137
|
+
router={mockRouter}
|
|
138
|
+
/>
|
|
139
|
+
</Provider>
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
// Change the hash after mount, before unmount
|
|
143
|
+
mockRouter.location.hash = '#/Overview';
|
|
144
|
+
|
|
145
|
+
unmount();
|
|
146
|
+
|
|
147
|
+
// Verify router.replace was called with the UPDATED hash, not the initial '#/Insights'
|
|
148
|
+
expect(mockRouter.replace).toHaveBeenCalledWith({
|
|
149
|
+
search: null,
|
|
150
|
+
hash: '#/Overview',
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
});
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: foreman_rh_cloud
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 13.2.
|
|
4
|
+
version: 13.2.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Foreman Red Hat Cloud team
|
|
@@ -586,6 +586,7 @@ files:
|
|
|
586
586
|
- webpack/InsightsHostDetailsTab/__tests__/InsightsTabReducer.test.js
|
|
587
587
|
- webpack/InsightsHostDetailsTab/__tests__/InsightsTabSelectors.test.js
|
|
588
588
|
- webpack/InsightsHostDetailsTab/__tests__/InsightsTotalRiskChart.test.js
|
|
589
|
+
- webpack/InsightsHostDetailsTab/__tests__/NewHostDetailsTab.test.js
|
|
589
590
|
- webpack/InsightsHostDetailsTab/__tests__/__snapshots__/InsightsTab.test.js.snap
|
|
590
591
|
- webpack/InsightsHostDetailsTab/__tests__/__snapshots__/InsightsTabActions.test.js.snap
|
|
591
592
|
- webpack/InsightsHostDetailsTab/__tests__/__snapshots__/InsightsTabReducer.test.js.snap
|
|
@@ -668,7 +669,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
668
669
|
- !ruby/object:Gem::Version
|
|
669
670
|
version: '0'
|
|
670
671
|
requirements: []
|
|
671
|
-
rubygems_version: 4.0.
|
|
672
|
+
rubygems_version: 4.0.6
|
|
672
673
|
specification_version: 4
|
|
673
674
|
summary: Summary of ForemanRhCloud.
|
|
674
675
|
test_files:
|