Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/views/api/v2/preupgrade_report_entries/base.json.rabl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
object @preupgrade_report_entry

attributes :id, :preupgrade_report_id, :host_id, :hostname, :title, :actor, :audience,
attributes :id, :detail, :preupgrade_report_id, :host_id, :hostname, :title, :actor, :audience,
:severity, :leapp_run_id, :summary, :tags, :flags, :created_at, :updated_at
2 changes: 2 additions & 0 deletions lib/foreman_leapp/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ class Engine < ::Rails::Engine
:resource_type => 'JobInvocation'
end

register_global_js_file 'global'

describe_host do
multiple_actions_provider :leapp_hosts_multiple_actions
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';

export const Table = ({ children, bottomPagination, customEmptyState }) => (

Check failure on line 3 in webpack/__mocks__/foremanReact/components/PF4/TableIndexPage/Table/Table.js

View workflow job for this annotation

GitHub Actions / JavaScript / Foreman develop Ruby 3.0 and Node 22

'customEmptyState' is missing in props validation

Check failure on line 3 in webpack/__mocks__/foremanReact/components/PF4/TableIndexPage/Table/Table.js

View workflow job for this annotation

GitHub Actions / JavaScript / Foreman develop Ruby 3.0 and Node 22

'bottomPagination' is missing in props validation

Check failure on line 3 in webpack/__mocks__/foremanReact/components/PF4/TableIndexPage/Table/Table.js

View workflow job for this annotation

GitHub Actions / JavaScript / Foreman develop Ruby 3.0 and Node 22

'children' is missing in props validation

Check failure on line 3 in webpack/__mocks__/foremanReact/components/PF4/TableIndexPage/Table/Table.js

View workflow job for this annotation

GitHub Actions / JavaScript / Foreman develop Ruby 3.0 and Node 18

'customEmptyState' is missing in props validation

Check failure on line 3 in webpack/__mocks__/foremanReact/components/PF4/TableIndexPage/Table/Table.js

View workflow job for this annotation

GitHub Actions / JavaScript / Foreman develop Ruby 3.0 and Node 18

'bottomPagination' is missing in props validation

Check failure on line 3 in webpack/__mocks__/foremanReact/components/PF4/TableIndexPage/Table/Table.js

View workflow job for this annotation

GitHub Actions / JavaScript / Foreman develop Ruby 3.0 and Node 18

'children' is missing in props validation
<div data-testid="mock-table">
<table>
{children}
{customEmptyState && <tbody>{customEmptyState}</tbody>}
</table>
{bottomPagination}
</div>
);
15 changes: 14 additions & 1 deletion webpack/__mocks__/foremanReact/components/Pagination.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,15 @@
const Pagination = () => jest.fn();
import React from 'react';

const Pagination = ({ page, perPage, onChange }) => (

Check failure on line 3 in webpack/__mocks__/foremanReact/components/Pagination.js

View workflow job for this annotation

GitHub Actions / JavaScript / Foreman develop Ruby 3.0 and Node 22

'onChange' is missing in props validation

Check failure on line 3 in webpack/__mocks__/foremanReact/components/Pagination.js

View workflow job for this annotation

GitHub Actions / JavaScript / Foreman develop Ruby 3.0 and Node 22

'perPage' is missing in props validation

Check failure on line 3 in webpack/__mocks__/foremanReact/components/Pagination.js

View workflow job for this annotation

GitHub Actions / JavaScript / Foreman develop Ruby 3.0 and Node 22

'page' is missing in props validation

Check failure on line 3 in webpack/__mocks__/foremanReact/components/Pagination.js

View workflow job for this annotation

GitHub Actions / JavaScript / Foreman develop Ruby 3.0 and Node 18

'onChange' is missing in props validation

Check failure on line 3 in webpack/__mocks__/foremanReact/components/Pagination.js

View workflow job for this annotation

GitHub Actions / JavaScript / Foreman develop Ruby 3.0 and Node 18

'perPage' is missing in props validation

Check failure on line 3 in webpack/__mocks__/foremanReact/components/Pagination.js

View workflow job for this annotation

GitHub Actions / JavaScript / Foreman develop Ruby 3.0 and Node 18

'page' is missing in props validation
<div data-testid="pagination">
<span>Page {page}</span>
<span>Per Page {perPage}</span>
<button onClick={() => onChange({ page: page + 1, perPage })}>
Next Page
</button>
<button onClick={() => onChange({ page: 1, perPage: 10 })}>Set 10</button>
<button onClick={() => onChange({ page: 1, perPage: 5 })}>Set 5</button>
</div>
);

export default Pagination;
3 changes: 3 additions & 0 deletions webpack/__mocks__/foremanReact/redux/API.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const APIActions = {
get: jest.fn(),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.leapp-report-section .table-title-section,
.leapp-report-section .table-toolbar-section {
padding-top: 0 !important;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React from 'react';
import { translate as __ } from 'foremanReact/common/I18n';
import { Label } from '@patternfly/react-core';

export const STATUS = {
PENDING: 'PENDING',
RESOLVED: 'RESOLVED',
ERROR: 'ERROR',
};

export const PER_PAGE_OPTIONS = [
{ title: '5', value: 5 },
{ title: '10', value: 10 },
{ title: '20', value: 20 },
{ title: '50', value: 50 },
];

export const columns = {
title: {
title: __('Title'),
wrapper: () => null,
},
host: {
title: __('Host'),
wrapper: () => null,
},
risk_factor: {
title: __('Risk Factor'),
wrapper: () => null,
},
has_remediation: {
title: __('Has Remediation?'),
wrapper: () => null,
},
inhibitor: {
title: __('Inhibitor?'),
wrapper: () => null,
},
};

export const renderSeverityLabel = severity => {
switch (severity) {
case 'high':
return <Label color="red">{__('High')}</Label>;
case 'medium':
return <Label color="orange">{__('Medium')}</Label>;
case 'low':
return <Label color="blue">{__('Low')}</Label>;
case 'info':
return <Label color="grey">{__('Info')}</Label>;
default:
return <Label color="grey">{severity || __('Info')}</Label>;
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import React from 'react';
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import { thunk } from 'redux-thunk';
import { APIActions } from 'foremanReact/redux/API';
import PreupgradeReportsTable from '../index';

jest.mock('foremanReact/redux/API');
jest.mock('foremanReact/common/I18n');
jest.mock('foremanReact/components/Pagination');
jest.mock('foremanReact/components/PF4/TableIndexPage/Table/Table');

jest.mock(
'foremanReact/components/PF4/TableIndexPage/TableIndexPage',
() => ({
__esModule: true,
default: ({ children, customToolbarItems }) => (
<div data-testid="table-index-page">
<div className="toolbar">{customToolbarItems}</div>
{children}
</div>
),
}),
{ virtual: true }
);

jest.mock(
'foremanReact/Root/Context/ForemanContext',
() => ({
useForemanSettings: () => ({
perPage: 20,
perPageOptions: [5, 10, 20, 50],
}),
}),
{ virtual: true }
);

const mockStore = configureMockStore([thunk]);

const mockJobId = 42;
const mockReportId = 999;
const mockJobData = {
id: mockJobId,
template_name: 'Run preupgrade via Leapp',
};

const mockEntries = Array.from({ length: 12 }, (_, i) => ({
id: i + 1,
title: `Report Entry ${i + 1}`,
hostname: 'example.com',
severity: i === 0 ? 'high' : 'low',
flags: i === 0 ? ['inhibitor'] : [],
detail: { remediations: i === 0 ? [{ type: 'cmd' }] : [] },
}));

describe('PreupgradeReportsTable', () => {
let store;

beforeEach(() => {
store = mockStore({});
jest.clearAllMocks();

APIActions.get.mockImplementation(({ key, handleSuccess }) => {
return dispatch => {
if (key.includes('GET_LEAPP_REPORT_LIST'))
handleSuccess({ results: [{ id: mockReportId }] });
if (key.includes('GET_LEAPP_REPORT_DETAIL'))
handleSuccess({
id: mockReportId,
preupgrade_report_entries: mockEntries,
});
return { type: 'MOCK_API_SUCCESS' };
};
});
});

const renderComponent = () =>
render(
<Provider store={store}>
<PreupgradeReportsTable data={mockJobData} />
</Provider>
);

const expandSection = () => {
fireEvent.click(screen.getByText('Leapp preupgrade report'));
};

it('renders data', async () => {
renderComponent();
expandSection();

await waitFor(() => screen.getByText('Report Entry 1'));

expect(screen.getByText('Report Entry 1')).toBeInTheDocument();
expect(screen.getByText('Report Entry 5')).toBeInTheDocument();
expect(screen.queryByText('Report Entry 6')).not.toBeInTheDocument();
});

it('paginates to the next page', async () => {
renderComponent();
expandSection();
await waitFor(() => screen.getByText('Report Entry 1'));

fireEvent.click(screen.getAllByText('Next Page')[0]);

await waitFor(() => screen.getByText('Report Entry 6'));
expect(screen.getByText('Report Entry 10')).toBeInTheDocument();
expect(screen.queryByText('Report Entry 1')).not.toBeInTheDocument();
});

it('changes per_page limit to 10', async () => {
renderComponent();
expandSection();
await waitFor(() => screen.getByText('Report Entry 1'));

fireEvent.click(screen.getAllByText('Set 10')[0]);

await waitFor(() => {
expect(screen.getByText('Report Entry 10')).toBeInTheDocument();
expect(screen.queryByText('Report Entry 11')).not.toBeInTheDocument();
});
});

it('renders empty state message when no issues found', async () => {
APIActions.get.mockImplementation(({ key, handleSuccess }) => {
return () => {
if (key.includes('GET_LEAPP_REPORT_LIST'))
handleSuccess({ results: [{ id: mockReportId }] });
if (key.includes('GET_LEAPP_REPORT_DETAIL'))
handleSuccess({ id: mockReportId, preupgrade_report_entries: [] });
return { type: 'EMPTY' };
};
});

renderComponent();
expandSection();

await waitFor(() => {
expect(screen.getByText('No issues found')).toBeInTheDocument();
});
});
});
Loading
Loading