Skip to content

Commit 33f3446

Browse files
committed
Fixes #39412 - replace messagebox with empty state and update to pf5 components inside PreupgradeReports
1 parent af94e75 commit 33f3446

4 files changed

Lines changed: 243 additions & 200 deletions

File tree

webpack/components/PreupgradeReports/PreupgradeReports.js

Lines changed: 63 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
import React, { useState } from 'react';
2-
import MessageBox from 'foremanReact/components/common/MessageBox';
3-
import { LoadingState, Row } from 'patternfly-react';
2+
import {
3+
Flex,
4+
FlexItem,
5+
Grid,
6+
GridItem,
7+
Icon,
8+
Spinner,
9+
} from '@patternfly/react-core';
10+
import { ExclamationCircleIcon } from '@patternfly/react-icons';
11+
import EmptyState from 'foremanReact/components/common/EmptyState';
412
import PropTypes from 'prop-types';
513
import { sprintf, translate as __ } from 'foremanReact/common/I18n';
614

@@ -70,31 +78,38 @@ const PreupgradeReports = ({
7078

7179
return (
7280
<React.Fragment>
73-
<Row>
74-
<div className="col-md-8">
81+
<Grid hasGutter>
82+
<GridItem span={8}>
7583
<EntriesFilter
7684
filterType={filterType}
7785
onFilterTypeChange={onFilterTypeChange}
7886
filterValue={filterValue}
7987
onFilterValueChange={onFilterValueChange}
8088
/>
81-
</div>
82-
<div className="col-md-4">
83-
<div className="btn-toolbar pull-right">
84-
<FixSelectedButton
85-
postUrl={newJobInvocationUrl}
86-
disabled={!anyEntriesFixable(preupgradeReports) || !anySelected}
87-
csrfToken={csrfToken}
88-
ids={idsForInvocationFromEntries(checked)}
89-
/>
90-
<UpgradeAllButton
91-
postUrl={newJobInvocationUrl}
92-
csrfToken={csrfToken}
93-
preupgradeReports={preupgradeReports}
94-
/>
95-
</div>
96-
</div>
97-
</Row>
89+
</GridItem>
90+
<GridItem span={4}>
91+
<Flex
92+
justifyContent={{ default: 'justifyContentFlexEnd' }}
93+
gap={{ default: 'gapSm' }}
94+
>
95+
<FlexItem>
96+
<FixSelectedButton
97+
postUrl={newJobInvocationUrl}
98+
disabled={!anyEntriesFixable(preupgradeReports) || !anySelected}
99+
csrfToken={csrfToken}
100+
ids={idsForInvocationFromEntries(checked)}
101+
/>
102+
</FlexItem>
103+
<FlexItem>
104+
<UpgradeAllButton
105+
postUrl={newJobInvocationUrl}
106+
csrfToken={csrfToken}
107+
preupgradeReports={preupgradeReports}
108+
/>
109+
</FlexItem>
110+
</Flex>
111+
</GridItem>
112+
</Grid>
98113
<PreupgradeReportsList
99114
allEntries={filterEntries(
100115
filterType,
@@ -116,26 +131,41 @@ const withLoadingState = Component => componentProps => {
116131

117132
if (!isEmpty(error)) {
118133
return (
119-
<MessageBox
134+
<EmptyState
120135
key="preupgrade-reports-error"
121-
icontype="error-circle-o"
122-
msg={sprintf(__('Could not retrieve data: %(status)s - %(msg)s'), {
136+
variant="xs"
137+
icon={
138+
<Icon iconSize="lg">
139+
<ExclamationCircleIcon />
140+
</Icon>
141+
}
142+
header={sprintf(__('Could not retrieve data: %(status)s - %(msg)s'), {
123143
status: error.statusText,
124144
msg: error.errorMsg,
125145
})}
126146
/>
127147
);
128148
}
129149

130-
return (
131-
<LoadingState loading={loading}>
132-
{preupgradeReports.length > 0 ? (
133-
<Component {...componentProps} />
134-
) : (
135-
<NoReports reportsExpected={reportsExpected} />
136-
)}
137-
</LoadingState>
138-
);
150+
if (loading) {
151+
return (
152+
<EmptyState
153+
variant="xs"
154+
icon={
155+
<Icon iconSize="sm">
156+
<Spinner aria-label={__('Loading')} />
157+
</Icon>
158+
}
159+
header={__('Loading')}
160+
/>
161+
);
162+
}
163+
164+
if (preupgradeReports.length > 0) {
165+
return <Component {...componentProps} />;
166+
}
167+
168+
return <NoReports reportsExpected={reportsExpected} />;
139169
};
140170

141171
PreupgradeReports.propTypes = {

webpack/components/PreupgradeReports/__tests__/PreupgradeReports.fixtures.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,49 @@ export const preupgradeReports = [
6060
},
6161
];
6262

63+
export const preupgradeReportsWithFixableEntries = [
64+
{
65+
hostId: 5,
66+
entries: [
67+
{
68+
title: 'Fixable entry',
69+
severity: 'high',
70+
id: 100,
71+
hostId: 5,
72+
hostname: 'host.example.com',
73+
flags: [],
74+
detail: {
75+
remediations: [{ type: 'command', context: ['echo', 'fix'] }],
76+
},
77+
},
78+
{
79+
title: 'Not fixable entry',
80+
severity: 'low',
81+
id: 101,
82+
hostId: 5,
83+
hostname: 'host.example.com',
84+
flags: [],
85+
},
86+
],
87+
},
88+
{
89+
hostId: 6,
90+
entries: [
91+
{
92+
title: 'Another fixable entry',
93+
severity: 'medium',
94+
id: 102,
95+
hostId: 6,
96+
hostname: 'foo.example.com',
97+
flags: [],
98+
detail: {
99+
remediations: [{ type: 'command', context: ['echo', 'fix2'] }],
100+
},
101+
},
102+
],
103+
},
104+
];
105+
63106
export const reportsWithRemediations = [
64107
{
65108
hostId: 5,
Lines changed: 137 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,145 @@
1-
import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
1+
import React from 'react';
2+
import { Provider } from 'react-redux';
3+
import configureMockStore from 'redux-mock-store';
4+
import { fireEvent, render, screen } from '@testing-library/react';
5+
import '@testing-library/jest-dom';
26

37
import PreupgradeReports from '../PreupgradeReports';
8+
import {
9+
preupgradeReports,
10+
preupgradeReportsWithFixableEntries,
11+
} from './PreupgradeReports.fixtures';
412

5-
import { preupgradeReports } from './PreupgradeReports.fixtures';
13+
jest.mock('foremanReact/components/Pagination', () => {
14+
const MockPagination = () => <div data-testid="pagination">Pagination</div>;
15+
return MockPagination;
16+
});
17+
18+
jest.mock('../../PreupgradeReportsList/components/images/i_severity-high.svg', () => 'severity-high.svg');
19+
jest.mock('../../PreupgradeReportsList/components/images/i_severity-med.svg', () => 'severity-med.svg');
20+
jest.mock('../../PreupgradeReportsList/components/images/i_severity-low.svg', () => 'severity-low.svg');
21+
22+
const mockStore = configureMockStore([]);
623

724
const csrfToken = 'xyz';
825
const newJobInvocationUrl = '/job_invocations/new';
9-
const getPreupgradeReports = () => {};
10-
11-
const fixtures = {
12-
'should render when loaded with reports': {
13-
loading: false,
14-
error: {},
15-
preupgradeReports,
16-
csrfToken,
17-
newJobInvocationUrl,
18-
getPreupgradeReports,
19-
reportsExpected: true,
20-
},
21-
'should render when loaded without reports': {
22-
loading: false,
23-
error: {},
24-
preupgradeReports: [],
25-
csrfToken,
26-
newJobInvocationUrl,
27-
getPreupgradeReports,
28-
reportsExpected: true,
29-
},
30-
'should render when loading': {
31-
loading: true,
32-
error: {},
33-
preupgradeReports: [],
34-
csrfToken,
35-
newJobInvocationUrl,
36-
getPreupgradeReports,
37-
reportsExpected: false,
38-
},
39-
'should render error': {
40-
loading: false,
41-
error: {
42-
statusText: 'Internal server error',
43-
errorMsg: 'Well, this is embarassing',
44-
},
45-
preupgradeReports: [],
46-
csrfToken,
47-
newJobInvocationUrl,
48-
getPreupgradeReports,
49-
reportsExpected: false,
50-
},
26+
27+
const defaultProps = {
28+
loading: false,
29+
error: {},
30+
preupgradeReports,
31+
csrfToken,
32+
newJobInvocationUrl,
33+
reportsExpected: true,
5134
};
5235

53-
describe('PreupgradeReports', () =>
54-
testComponentSnapshotsWithFixtures(PreupgradeReports, fixtures));
36+
const renderComponent = (props = {}) =>
37+
render(
38+
<Provider store={mockStore({})}>
39+
<PreupgradeReports {...defaultProps} {...props} />
40+
</Provider>
41+
);
42+
43+
const getFixSelectedForm = () =>
44+
screen.getByRole('button', { name: 'Fix Selected' }).closest('form');
45+
46+
describe('PreupgradeReports', () => {
47+
it('renders report entries when loaded with reports', () => {
48+
renderComponent();
49+
50+
expect(screen.getByText('Fix me!')).toBeInTheDocument();
51+
expect(screen.getByText('I am broken too')).toBeInTheDocument();
52+
expect(screen.getByText('Octocat is not happy')).toBeInTheDocument();
53+
});
54+
55+
it('renders empty state when loaded without reports', () => {
56+
renderComponent({ preupgradeReports: [] });
57+
58+
expect(
59+
screen.getByRole('heading', {
60+
name: 'No Preupgrade Report Available',
61+
level: 5,
62+
})
63+
).toBeInTheDocument();
64+
expect(
65+
screen.getByText(
66+
'The preupgrade report could not be generated, check the job details for the reason'
67+
)
68+
).toBeInTheDocument();
69+
});
70+
71+
it('renders loading state while data is being fetched', () => {
72+
renderComponent({
73+
loading: true,
74+
preupgradeReports: [],
75+
reportsExpected: false,
76+
});
77+
78+
expect(
79+
screen.getByRole('heading', { name: 'Loading', level: 5 })
80+
).toBeInTheDocument();
81+
expect(
82+
screen.queryByRole('heading', {
83+
name: 'No Preupgrade Report Available',
84+
})
85+
).not.toBeInTheDocument();
86+
});
87+
88+
it('renders error empty state when data retrieval fails', () => {
89+
renderComponent({
90+
error: {
91+
statusText: 'Internal server error',
92+
errorMsg: 'Unexpected error',
93+
},
94+
preupgradeReports: [],
95+
reportsExpected: false,
96+
});
97+
98+
expect(
99+
screen.getByRole('heading', {
100+
name: 'Could not retrieve data: Internal server error - Unexpected error',
101+
level: 5,
102+
})
103+
).toBeInTheDocument();
104+
});
105+
106+
it('disables Fix Selected when no fixable entries are selected', () => {
107+
renderComponent({ preupgradeReports: preupgradeReportsWithFixableEntries });
108+
109+
expect(screen.getByRole('button', { name: 'Fix Selected' })).toBeDisabled();
110+
});
111+
112+
it('enables Fix Selected and passes selected entry ids when a fixable entry is selected', () => {
113+
renderComponent({ preupgradeReports: preupgradeReportsWithFixableEntries });
114+
115+
const [, fixableEntryCheckbox] = screen.getAllByRole('checkbox');
116+
117+
fireEvent.click(fixableEntryCheckbox);
118+
119+
expect(screen.getByRole('button', { name: 'Fix Selected' })).toBeEnabled();
120+
expect(
121+
getFixSelectedForm().querySelector('input[name="inputs[remediation_ids]"]')
122+
).toHaveValue('100');
123+
});
124+
125+
it('selects all fixable entries when header checkbox is clicked', () => {
126+
renderComponent({ preupgradeReports: preupgradeReportsWithFixableEntries });
127+
128+
const [selectAllCheckbox] = screen.getAllByRole('checkbox');
129+
130+
fireEvent.click(selectAllCheckbox);
131+
132+
expect(screen.getByRole('button', { name: 'Fix Selected' })).toBeEnabled();
133+
134+
const form = getFixSelectedForm();
135+
136+
expect(
137+
form.querySelector('input[name="inputs[remediation_ids]"]')
138+
).toHaveValue('100,102');
139+
expect(
140+
[...form.querySelectorAll('input[name="host_ids[]"]')].map(
141+
input => input.value
142+
)
143+
).toEqual(['5', '6']);
144+
});
145+
});

0 commit comments

Comments
 (0)