foreman_leapp 3.0.0 → 3.2.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/foreman_leapp/locale/de/foreman_leapp.js +4 -1
  3. data/app/assets/javascripts/foreman_leapp/locale/en/foreman_leapp.js +144 -2
  4. data/app/assets/javascripts/foreman_leapp/locale/fr/foreman_leapp.js +4 -1
  5. data/app/assets/javascripts/foreman_leapp/locale/ja/foreman_leapp.js +4 -1
  6. data/app/assets/javascripts/foreman_leapp/locale/ka/foreman_leapp.js +4 -1
  7. data/app/assets/javascripts/foreman_leapp/locale/ko/foreman_leapp.js +4 -1
  8. data/app/assets/javascripts/foreman_leapp/locale/zh_CN/foreman_leapp.js +3 -0
  9. data/app/views/api/v2/preupgrade_report_entries/base.json.rabl +1 -1
  10. data/app/views/foreman_leapp/job_templates/leapp_preupgrade.erb +9 -2
  11. data/app/views/foreman_leapp/job_templates/leapp_upgrade.erb +11 -3
  12. data/lib/foreman_leapp/engine.rb +2 -0
  13. data/lib/foreman_leapp/version.rb +1 -1
  14. data/locale/de/LC_MESSAGES/foreman_leapp.mo +0 -0
  15. data/locale/de/foreman_leapp.po +4 -2
  16. data/locale/en/LC_MESSAGES/foreman_leapp.mo +0 -0
  17. data/locale/en/foreman_leapp.po +150 -0
  18. data/locale/foreman_leapp.pot +17 -13
  19. data/locale/fr/LC_MESSAGES/foreman_leapp.mo +0 -0
  20. data/locale/fr/foreman_leapp.po +4 -2
  21. data/locale/ja/LC_MESSAGES/foreman_leapp.mo +0 -0
  22. data/locale/ja/foreman_leapp.po +4 -2
  23. data/locale/ka/LC_MESSAGES/foreman_leapp.mo +0 -0
  24. data/locale/ka/foreman_leapp.po +4 -2
  25. data/locale/ko/LC_MESSAGES/foreman_leapp.mo +0 -0
  26. data/locale/ko/foreman_leapp.po +4 -2
  27. data/locale/zh_CN/foreman_leapp.po +3 -1
  28. data/webpack/components/PreupgradeReports/PreupgradeReports.js +1 -1
  29. data/webpack/components/PreupgradeReports/PreupgradeReportsHelpers.js +2 -2
  30. data/webpack/components/PreupgradeReports/__tests__/__snapshots__/PreupgradeReports.test.js.snap +1 -1
  31. data/webpack/components/PreupgradeReports/__tests__/__snapshots__/PreupgradeReportsHelpers.test.js.snap +25 -1
  32. data/webpack/components/PreupgradeReports/components/__snapshots__/NoReports.test.js.snap +6 -0
  33. data/webpack/components/PreupgradeReportsList/__tests__/PreupgradeReportsList.test.js +119 -35
  34. data/webpack/components/PreupgradeReportsList/index.js +2 -3
  35. data/webpack/components/PreupgradeReportsTable/__tests__/PreupgradeReportsTable.test.js +117 -0
  36. data/webpack/components/PreupgradeReportsTable/index.js +180 -0
  37. data/webpack/global_index.js +10 -0
  38. metadata +6 -8
  39. data/webpack/__mocks__/foremanReact/common/I18n.js +0 -2
  40. data/webpack/__mocks__/foremanReact/components/Pagination.js +0 -2
  41. data/webpack/__mocks__/foremanReact/components/common/EmptyState.js +0 -1
  42. data/webpack/__mocks__/foremanReact/components/common/MessageBox.js +0 -2
  43. data/webpack/components/PreupgradeReportsList/__tests__/__snapshots__/PreupgradeReportsList.test.js.snap +0 -135
Binary file
@@ -8,10 +8,9 @@
8
8
  # Bryan Kearney <bryan.kearney@gmail.com>, 2022
9
9
  # Ewoud Kohl van Wijngaarden <ewoud+transifex@kohlvanwijngaarden.nl>, 2025
10
10
  #
11
- #, fuzzy
12
11
  msgid ""
13
12
  msgstr ""
14
- "Project-Id-Version: foreman_leapp 1.0.0\n"
13
+ "Project-Id-Version: foreman_leapp 3.0.0\n"
15
14
  "Report-Msgid-Bugs-To: \n"
16
15
  "PO-Revision-Date: 2022-10-12 12:00+0000\n"
17
16
  "Last-Translator: Ewoud Kohl van Wijngaarden <ewoud+transifex@kohlvanwijngaarde"
@@ -107,6 +106,9 @@ msgstr "사전 업그레이드 보고서가 없습니다"
107
106
  msgid "Preupgrade check with Leapp"
108
107
  msgstr "Leapp으로 업그레이드 전 확인"
109
108
 
109
+ msgid "Preupgrade job"
110
+ msgstr ""
111
+
110
112
  msgid "Remediation plan"
111
113
  msgstr "수정 계획"
112
114
 
@@ -8,7 +8,6 @@
8
8
  # Amit Upadhye <aupadhye@redhat.com>, 2023
9
9
  # Ewoud Kohl van Wijngaarden <ewoud+transifex@kohlvanwijngaarden.nl>, 2023
10
10
  #
11
- #, fuzzy
12
11
  msgid ""
13
12
  msgstr ""
14
13
  "Project-Id-Version: foreman_leapp 2.0.3\n"
@@ -108,6 +107,9 @@ msgstr "没有可用的预升级报告"
108
107
  msgid "Preupgrade check with Leapp"
109
108
  msgstr "使用 Leapp 的预升级检查"
110
109
 
110
+ msgid "Preupgrade job"
111
+ msgstr ""
112
+
111
113
  msgid "Remediation plan"
112
114
  msgstr "补救计划"
113
115
 
@@ -119,7 +119,7 @@ const withLoadingState = Component => componentProps => {
119
119
  <MessageBox
120
120
  key="preupgrade-reports-error"
121
121
  icontype="error-circle-o"
122
- msg={sprintf(__('Could not retrieve data: %(status) - %(msg)'), {
122
+ msg={sprintf(__('Could not retrieve data: %(status)s - %(msg)s'), {
123
123
  status: error.statusText,
124
124
  msg: error.errorMsg,
125
125
  })}
@@ -57,9 +57,9 @@ export const idsForInvocationFromReports = reports =>
57
57
  idsForInvocationFromEntries(flattenEntries(reports));
58
58
 
59
59
  export const entriesPage = (entries, pagination) => {
60
- const offset = (pagination.page - 1) * pagination.per_page;
60
+ const offset = (pagination.page - 1) * pagination.perPage;
61
61
 
62
- return entries.slice(offset, offset + pagination.per_page);
62
+ return entries.slice(offset, offset + pagination.perPage);
63
63
  };
64
64
 
65
65
  export const filterEntries = (attribute, value, entries) => {
@@ -4,7 +4,7 @@ exports[`PreupgradeReports should render error 1`] = `
4
4
  <MessageBox
5
5
  icontype="error-circle-o"
6
6
  key="preupgrade-reports-error"
7
- msg="Could not retrieve data: %(status) - %(msg)"
7
+ msg="Could not retrieve data: Internal server error - Well, this is embarassing"
8
8
  />
9
9
  `;
10
10
 
@@ -131,7 +131,31 @@ Array [
131
131
  ]
132
132
  `;
133
133
 
134
- exports[`PreupgradeReportsHelpers should return entries page 1`] = `Array []`;
134
+ exports[`PreupgradeReportsHelpers should return entries page 1`] = `
135
+ Array [
136
+ Object {
137
+ "flags": Array [],
138
+ "hostname": "foo.example.com",
139
+ "id": 45,
140
+ "severity": "low",
141
+ "title": "Not enough credits",
142
+ },
143
+ Object {
144
+ "flags": Array [],
145
+ "hostname": "foo.example.com",
146
+ "id": 46,
147
+ "severity": "medium",
148
+ "title": "SELinux is turned off",
149
+ },
150
+ Object {
151
+ "flags": Array [],
152
+ "hostname": "foo.example.com",
153
+ "id": 47,
154
+ "severity": "medium",
155
+ "title": "Root password is too short",
156
+ },
157
+ ]
158
+ `;
135
159
 
136
160
  exports[`PreupgradeReportsHelpers should return fixable entries 1`] = `
137
161
  Array [
@@ -2,18 +2,24 @@
2
2
 
3
3
  exports[`NoReports should render when reports expected 1`] = `
4
4
  <EmptyStatePattern
5
+ action={null}
5
6
  description="The preupgrade report could not be generated, check the job details for the reason"
7
+ documentation={null}
6
8
  header="No Preupgrade Report Available"
7
9
  icon="warning-triangle-o"
8
10
  iconType="pf"
11
+ secondaryActions={Array []}
9
12
  />
10
13
  `;
11
14
 
12
15
  exports[`NoReports should render when reports not expected 1`] = `
13
16
  <EmptyStatePattern
17
+ action={null}
14
18
  description="The preupgrade report will be available after the job finishes"
19
+ documentation={null}
15
20
  header="No Preupgrade Report Available"
16
21
  icon="in-progress"
17
22
  iconType="pf"
23
+ secondaryActions={Array []}
18
24
  />
19
25
  `;
@@ -1,40 +1,124 @@
1
- import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
1
+ import React from 'react';
2
+ import { render, screen, fireEvent } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
2
4
 
3
5
  import PreupgradeReportsList from '../index';
4
6
 
5
- const allEntries = [
6
- { title: 'Fix me!', severity: 'Too severe to talk about' },
7
- { title: 'I am broken too', severity: 'medium' },
8
- { title: 'Octocat is not happy', severity: 'high' },
9
- { title: 'Not enough credits', severity: 'low' },
10
- ];
11
-
12
- const isSelected = () => false;
13
- const toggleSelected = () => {};
14
- const sort = { attribute: '', order: 'asc' };
15
- const changeSort = () => {};
16
- const toggleSelectAll = () => {};
17
-
18
- const fixtures = {
19
- 'should render': {
20
- allEntries,
21
- fixAllWorking: false,
22
- isSelected,
23
- toggleSelected,
24
- sort,
25
- changeSort,
26
- toggleSelectAll,
27
- },
28
- 'should render when working': {
29
- allEntries,
30
- fixAllWorking: true,
31
- isSelected,
32
- toggleSelected,
33
- sort,
34
- changeSort,
35
- toggleSelectAll,
36
- },
7
+ jest.mock('foremanReact/components/Pagination', () => {
8
+ const MockPagination = () => <div data-testid="pagination">Pagination</div>;
9
+ return MockPagination;
10
+ });
11
+
12
+ jest.mock('../components/images/i_severity-high.svg', () => 'severity-high.svg');
13
+ jest.mock('../components/images/i_severity-med.svg', () => 'severity-med.svg');
14
+ jest.mock('../components/images/i_severity-low.svg', () => 'severity-low.svg');
15
+
16
+ const createMockEntries = count =>
17
+ Array.from({ length: count }, (_, i) => ({
18
+ id: i + 1,
19
+ preupgradeReportId: 100,
20
+ title: `Entry ${i + 1}`,
21
+ hostname: `host${i + 1}.example.com`,
22
+ severity: i % 3 === 0 ? 'high' : i % 3 === 1 ? 'medium' : 'low',
23
+ flags: i === 0 ? ['inhibitor'] : [],
24
+ detail: {
25
+ remediations:
26
+ i < 2
27
+ ? [{ type: 'command', context: ['echo', 'fix', 'command'] }]
28
+ : [],
29
+ },
30
+ }));
31
+
32
+ const defaultProps = {
33
+ allEntries: createMockEntries(4),
34
+ isSelected: () => false,
35
+ toggleSelected: jest.fn(),
36
+ sort: { attribute: '', order: 'asc' },
37
+ changeSort: jest.fn(),
38
+ toggleSelectAll: jest.fn(),
37
39
  };
38
40
 
39
- describe('PreupgradeReportsList', () =>
40
- testComponentSnapshotsWithFixtures(PreupgradeReportsList, fixtures));
41
+ const renderComponent = (props = {}) =>
42
+ render(<PreupgradeReportsList {...defaultProps} {...props} />);
43
+
44
+ describe('PreupgradeReportsList', () => {
45
+ beforeEach(() => {
46
+ jest.clearAllMocks();
47
+ });
48
+
49
+ it('renders the list header', () => {
50
+ renderComponent();
51
+
52
+ expect(screen.getByText('Title')).toBeInTheDocument();
53
+ expect(screen.getByText('Host')).toBeInTheDocument();
54
+ expect(screen.getByText('Risk Factor')).toBeInTheDocument();
55
+ expect(screen.getByText('Has Remediation?')).toBeInTheDocument();
56
+ expect(screen.getByText('Inhibitor?')).toBeInTheDocument();
57
+ });
58
+
59
+ it('renders entry titles', () => {
60
+ renderComponent();
61
+
62
+ expect(screen.getByText('Entry 1')).toBeInTheDocument();
63
+ expect(screen.getByText('Entry 2')).toBeInTheDocument();
64
+ expect(screen.getByText('Entry 3')).toBeInTheDocument();
65
+ expect(screen.getByText('Entry 4')).toBeInTheDocument();
66
+ });
67
+
68
+ it('renders entry hostnames', () => {
69
+ renderComponent();
70
+
71
+ expect(screen.getByText('host1.example.com')).toBeInTheDocument();
72
+ expect(screen.getByText('host2.example.com')).toBeInTheDocument();
73
+ });
74
+
75
+ it('renders checkboxes for entries', () => {
76
+ renderComponent();
77
+
78
+ const checkboxes = screen.getAllByRole('checkbox');
79
+ expect(checkboxes.length).toBeGreaterThan(0);
80
+ });
81
+
82
+ it('calls toggleSelectAll when header checkbox is clicked', () => {
83
+ const toggleSelectAll = jest.fn();
84
+ renderComponent({ toggleSelectAll });
85
+
86
+ const checkboxes = screen.getAllByRole('checkbox');
87
+ fireEvent.click(checkboxes[0]);
88
+
89
+ expect(toggleSelectAll).toHaveBeenCalled();
90
+ });
91
+
92
+ it('renders with empty entries array', () => {
93
+ renderComponent({ allEntries: [] });
94
+
95
+ expect(screen.getByText('Title')).toBeInTheDocument();
96
+ expect(screen.queryByText('Entry 1')).not.toBeInTheDocument();
97
+ });
98
+
99
+ it('renders entries with selected state', () => {
100
+ const isSelected = entry => entry.id === 1;
101
+ renderComponent({ isSelected });
102
+
103
+ const checkboxes = screen.getAllByRole('checkbox');
104
+ const entryCheckboxes = checkboxes.slice(1);
105
+ expect(entryCheckboxes[0]).toBeChecked();
106
+ expect(entryCheckboxes[1]).not.toBeChecked();
107
+ });
108
+
109
+ it('calls toggleSelected when entry checkbox is clicked', () => {
110
+ const toggleSelected = jest.fn();
111
+ renderComponent({ toggleSelected });
112
+
113
+ const checkboxes = screen.getAllByRole('checkbox');
114
+ fireEvent.click(checkboxes[1]);
115
+
116
+ expect(toggleSelected).toHaveBeenCalled();
117
+ });
118
+
119
+ it('renders pagination component', () => {
120
+ renderComponent();
121
+
122
+ expect(screen.getByTestId('pagination')).toBeInTheDocument();
123
+ });
124
+ });
@@ -19,11 +19,10 @@ const PreupgradeReportsList = ({
19
19
  changeSort,
20
20
  toggleSelectAll,
21
21
  }) => {
22
- const { perPage, perPageOptions } = useForemanSettings();
22
+ const { perPage } = useForemanSettings();
23
23
  const [pagination, setPagination] = useState({
24
24
  page: 1,
25
- per_page: perPage,
26
- perPageOptions,
25
+ perPage,
27
26
  });
28
27
 
29
28
  return (
@@ -0,0 +1,117 @@
1
+ import React from 'react';
2
+ import { render, screen, waitFor, fireEvent } from '@testing-library/react';
3
+ import '@testing-library/jest-dom/extend-expect';
4
+ import { Provider } from 'react-redux';
5
+ import configureMockStore from 'redux-mock-store';
6
+ import thunk from 'redux-thunk';
7
+ import { APIActions } from 'foremanReact/redux/API';
8
+ import PreupgradeReportsTable from '../index';
9
+
10
+ jest.mock('foremanReact/redux/API');
11
+
12
+ const mockStore = configureMockStore([thunk]);
13
+
14
+ const mockJobId = 42;
15
+ const mockReportId = 999;
16
+ const mockJobData = {
17
+ id: mockJobId,
18
+ template_name: 'Run preupgrade via Leapp',
19
+ };
20
+
21
+ const mockEntries = Array.from({ length: 12 }, (_, i) => ({
22
+ id: i + 1,
23
+ title: `Report Entry ${i + 1}`,
24
+ hostname: 'example.com',
25
+ severity: i === 0 ? 'high' : 'low',
26
+ flags: i === 0 ? ['inhibitor'] : [],
27
+ detail: { remediations: i === 0 ? [{ type: 'cmd' }] : [] },
28
+ }));
29
+
30
+ describe('PreupgradeReportsTable', () => {
31
+ let store;
32
+
33
+ beforeEach(() => {
34
+ store = mockStore({ API: {} });
35
+ jest.clearAllMocks();
36
+
37
+ APIActions.get.mockImplementation(({ key, handleSuccess }) => {
38
+ return dispatch => {
39
+ if (key.includes('GET_LEAPP_REPORT_LIST'))
40
+ handleSuccess({ results: [{ id: mockReportId }] });
41
+ if (key.includes('GET_LEAPP_REPORT_DETAIL'))
42
+ handleSuccess({
43
+ id: mockReportId,
44
+ preupgrade_report_entries: mockEntries,
45
+ });
46
+ return { type: 'MOCK_API_SUCCESS' };
47
+ };
48
+ });
49
+ });
50
+
51
+ const renderComponent = () =>
52
+ render(
53
+ <Provider store={store}>
54
+ <PreupgradeReportsTable data={mockJobData} />
55
+ </Provider>
56
+ );
57
+
58
+ const expandSection = () => {
59
+ fireEvent.click(screen.getByText('Leapp preupgrade report'));
60
+ };
61
+
62
+ it('renders data', async () => {
63
+ renderComponent();
64
+ expandSection();
65
+
66
+ await waitFor(() => screen.getByText('Report Entry 1'));
67
+
68
+ expect(screen.getByText('Report Entry 1')).toBeInTheDocument();
69
+ expect(screen.getByText('Report Entry 5')).toBeInTheDocument();
70
+ expect(screen.queryByText('Report Entry 6')).not.toBeInTheDocument();
71
+ });
72
+
73
+ it('paginates to the next page', async () => {
74
+ renderComponent();
75
+ expandSection();
76
+ await waitFor(() => screen.getByText('Report Entry 1'));
77
+
78
+ fireEvent.click(screen.getAllByLabelText('Go to next page')[0]);
79
+
80
+ await waitFor(() => screen.getByText('Report Entry 6'));
81
+ expect(screen.getByText('Report Entry 10')).toBeInTheDocument();
82
+ expect(screen.queryByText('Report Entry 1')).not.toBeInTheDocument();
83
+ });
84
+
85
+ it('changes perPage limit to 10', async () => {
86
+ renderComponent();
87
+ expandSection();
88
+ await waitFor(() => screen.getByText('Report Entry 1'));
89
+
90
+ fireEvent.click(screen.getAllByLabelText('Items per page')[0]);
91
+ fireEvent.click(screen.getAllByText('10 per page')[0]);
92
+
93
+ await waitFor(() => {
94
+ expect(screen.getByText('Report Entry 10')).toBeInTheDocument();
95
+ expect(screen.queryByText('Report Entry 11')).not.toBeInTheDocument();
96
+ });
97
+ });
98
+
99
+ it('renders empty state message when no issues found', async () => {
100
+ APIActions.get.mockImplementation(({ key, handleSuccess }) => {
101
+ return () => {
102
+ if (key.includes('GET_LEAPP_REPORT_LIST'))
103
+ handleSuccess({ results: [{ id: mockReportId }] });
104
+ if (key.includes('GET_LEAPP_REPORT_DETAIL'))
105
+ handleSuccess({ id: mockReportId, preupgrade_report_entries: [] });
106
+ return { type: 'EMPTY' };
107
+ };
108
+ });
109
+
110
+ renderComponent();
111
+ expandSection();
112
+
113
+ await waitFor(() => {
114
+ expect(screen.getByText('The preupgrade report shows no issues.')).toBeInTheDocument();
115
+ });
116
+ });
117
+ });
@@ -0,0 +1,180 @@
1
+ import PropTypes from 'prop-types';
2
+ import React, { useEffect, useState } from 'react';
3
+ import { useDispatch } from 'react-redux';
4
+ import { ExpandableSection, Label, Tooltip } from '@patternfly/react-core';
5
+ import { translate as __ } from 'foremanReact/common/I18n';
6
+ import { Table } from 'foremanReact/components/PF4/TableIndexPage/Table/Table';
7
+ import { APIActions } from 'foremanReact/redux/API';
8
+ import { STATUS } from 'foremanReact/constants';
9
+
10
+ import { entriesPage } from '../PreupgradeReports/PreupgradeReportsHelpers';
11
+
12
+ const renderSeverityLabel = severity => {
13
+ switch (severity) {
14
+ case 'high':
15
+ return <Label color="red">{__('High')}</Label>;
16
+ case 'medium':
17
+ return <Label color="orange">{__('Medium')}</Label>;
18
+ case 'low':
19
+ return <Label color="blue">{__('Low')}</Label>;
20
+ case 'info':
21
+ return <Label color="grey">{__('Info')}</Label>;
22
+ default:
23
+ return <Label color="grey">{severity || __('Info')}</Label>;
24
+ }
25
+ };
26
+
27
+ const PreupgradeReportsTable = ({ data = {} }) => {
28
+ const [error, setError] = useState(null);
29
+ const [isExpanded, setIsExpanded] = useState(false);
30
+ const [pagination, setPagination] = useState({ page: 1, perPage: 5 });
31
+ const [reportData, setReportData] = useState(null);
32
+ const [status, setStatus] = useState(STATUS.RESOLVED);
33
+ const dispatch = useDispatch();
34
+ // eslint-disable-next-line camelcase
35
+ const isLeappJob = data?.template_name?.includes('Run preupgrade via Leapp');
36
+
37
+ const columns = {
38
+ title: {
39
+ title: __('Title'),
40
+ },
41
+ host: {
42
+ title: __('Host'),
43
+ wrapper: entry =>
44
+ entry.hostname || (reportData && reportData.hostname) || '-',
45
+ },
46
+ risk_factor: {
47
+ title: __('Risk Factor'),
48
+ wrapper: ({ severity }) => renderSeverityLabel(severity),
49
+ },
50
+ has_remediation: {
51
+ title: __('Has Remediation?'),
52
+ wrapper: entry =>
53
+ entry.detail && entry.detail.remediations ? __('Yes') : __('No'),
54
+ },
55
+ inhibitor: {
56
+ title: __('Inhibitor?'),
57
+ wrapper: entry =>
58
+ entry.flags && entry.flags.some(flag => flag === 'inhibitor') ? (
59
+ <Tooltip content={__('This issue inhibits the upgrade.')}>
60
+ <span>{__('Yes')}</span>
61
+ </Tooltip>
62
+ ) : (
63
+ __('No')
64
+ ),
65
+ },
66
+ };
67
+
68
+ useEffect(() => {
69
+ let isMounted = true;
70
+ if (!isLeappJob || !isExpanded || reportData) {
71
+ return undefined;
72
+ }
73
+ setStatus(STATUS.PENDING);
74
+
75
+ dispatch(
76
+ APIActions.get({
77
+ key: `GET_LEAPP_REPORT_LIST_${data.id}`,
78
+ url: `/api/job_invocations/${data.id}/preupgrade_reports`,
79
+ handleSuccess: listResponse => {
80
+ if (!isMounted) return;
81
+ const listPayload = listResponse.data || listResponse;
82
+ const summary = listPayload.results?.[0];
83
+ if (summary?.id) {
84
+ dispatch(
85
+ APIActions.get({
86
+ key: `GET_LEAPP_REPORT_DETAIL_${summary.id}`,
87
+ url: `/api/preupgrade_reports/${summary.id}`,
88
+ handleSuccess: detailResponse => {
89
+ if (isMounted) {
90
+ const detailPayload = detailResponse.data || detailResponse;
91
+ setReportData(detailPayload);
92
+ setStatus(STATUS.RESOLVED);
93
+ }
94
+ },
95
+ handleError: err => {
96
+ if (isMounted) {
97
+ setError(err);
98
+ setStatus(STATUS.ERROR);
99
+ }
100
+ },
101
+ })
102
+ );
103
+ } else if (isMounted) {
104
+ setReportData({});
105
+ setStatus(STATUS.RESOLVED);
106
+ }
107
+ },
108
+ handleError: err => {
109
+ if (isMounted) {
110
+ setError(err);
111
+ setStatus(STATUS.ERROR);
112
+ }
113
+ },
114
+ })
115
+ );
116
+
117
+ return () => {
118
+ isMounted = false;
119
+ };
120
+ }, [isExpanded, data.id, isLeappJob, reportData, dispatch]);
121
+
122
+ // eslint-disable-next-line camelcase
123
+ const entries = reportData?.preupgrade_report_entries || [];
124
+ const pagedEntries = entriesPage(entries, pagination);
125
+
126
+ const handleParamsChange = newParams => {
127
+ setPagination(prev => ({
128
+ ...prev,
129
+ page: newParams.page || prev.page,
130
+ perPage: newParams.per_page || prev.perPage,
131
+ }));
132
+ };
133
+
134
+ if (!isLeappJob) return null;
135
+
136
+ return (
137
+ <ExpandableSection
138
+ className="leapp-report-section"
139
+ isExpanded={isExpanded}
140
+ onToggle={(_event, val) => setIsExpanded(val)}
141
+ toggleText={__('Leapp preupgrade report')}
142
+ >
143
+ <Table
144
+ ouiaId="leapp-report-table"
145
+ columns={columns}
146
+ isEmbedded
147
+ params={{
148
+ page: pagination.page,
149
+ perPage: pagination.perPage,
150
+ order: '',
151
+ }}
152
+ results={pagedEntries}
153
+ itemCount={entries.length}
154
+ url=""
155
+ isPending={status === STATUS.PENDING}
156
+ errorMessage={
157
+ status === STATUS.ERROR && error?.message ? error.message : null
158
+ }
159
+ showCheckboxes={false}
160
+ refreshData={() => {}}
161
+ isDeleteable={false}
162
+ emptyMessage={__('The preupgrade report shows no issues.')}
163
+ setParams={handleParamsChange}
164
+ />
165
+ </ExpandableSection>
166
+ );
167
+ };
168
+
169
+ PreupgradeReportsTable.propTypes = {
170
+ data: PropTypes.shape({
171
+ id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
172
+ template_name: PropTypes.string,
173
+ }),
174
+ };
175
+
176
+ PreupgradeReportsTable.defaultProps = {
177
+ data: {},
178
+ };
179
+
180
+ export default PreupgradeReportsTable;
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { addGlobalFill } from 'foremanReact/components/common/Fill/GlobalFill';
3
+ import PreupgradeReportsTable from './components/PreupgradeReportsTable';
4
+
5
+ addGlobalFill(
6
+ 'job-invocation-additional-info',
7
+ 'leapp-preupgrade-report-fill',
8
+ <PreupgradeReportsTable key="leapp-report-fill" />,
9
+ 100
10
+ );
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_leapp
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Foreman Leapp team
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-07-28 00:00:00.000000000 Z
10
+ date: 2026-01-28 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: foreman_remote_execution
@@ -130,10 +130,6 @@ files:
130
130
  - test/unit/actions/preupgrade_job_test.rb
131
131
  - test/unit/helpers/job_helper_test.rb
132
132
  - webpack/__mocks__/foremanReact/Root/Context/ForemanContext.js
133
- - webpack/__mocks__/foremanReact/common/I18n.js
134
- - webpack/__mocks__/foremanReact/components/Pagination.js
135
- - webpack/__mocks__/foremanReact/components/common/EmptyState.js
136
- - webpack/__mocks__/foremanReact/components/common/MessageBox.js
137
133
  - webpack/components/PreupgradeReports/PreupgradeReports.js
138
134
  - webpack/components/PreupgradeReports/PreupgradeReportsActions.js
139
135
  - webpack/components/PreupgradeReports/PreupgradeReportsHelpers.js
@@ -164,7 +160,6 @@ files:
164
160
  - webpack/components/PreupgradeReports/index.js
165
161
  - webpack/components/PreupgradeReportsList/PreupgradeReportList.scss
166
162
  - webpack/components/PreupgradeReportsList/__tests__/PreupgradeReportsList.test.js
167
- - webpack/components/PreupgradeReportsList/__tests__/__snapshots__/PreupgradeReportsList.test.js.snap
168
163
  - webpack/components/PreupgradeReportsList/components/EmptyInfoItem.js
169
164
  - webpack/components/PreupgradeReportsList/components/InfoItem.js
170
165
  - webpack/components/PreupgradeReportsList/components/InhibitorInfoItem.js
@@ -191,8 +186,11 @@ files:
191
186
  - webpack/components/PreupgradeReportsList/components/images/i_severity-low.svg
192
187
  - webpack/components/PreupgradeReportsList/components/images/i_severity-med.svg
193
188
  - webpack/components/PreupgradeReportsList/index.js
189
+ - webpack/components/PreupgradeReportsTable/__tests__/PreupgradeReportsTable.test.js
190
+ - webpack/components/PreupgradeReportsTable/index.js
194
191
  - webpack/consts.js
195
192
  - webpack/csrf.js
193
+ - webpack/global_index.js
196
194
  - webpack/index.js
197
195
  - webpack/reducer.js
198
196
  - webpack/testSetup.js
@@ -214,7 +212,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
214
212
  - !ruby/object:Gem::Version
215
213
  version: '0'
216
214
  requirements: []
217
- rubygems_version: 3.6.9
215
+ rubygems_version: 4.0.3
218
216
  specification_version: 4
219
217
  summary: A Foreman plugin for Leapp utility.
220
218
  test_files:
@@ -1,2 +0,0 @@
1
- export const translate = val => val;
2
- export const sprintf = val => val;
@@ -1,2 +0,0 @@
1
- const Pagination = () => jest.fn();
2
- export default Pagination;