import React from 'react';
import configureMockStore from 'redux-mock-store';
import { fireEvent, render, screen, act } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import { foremanUrl } from 'foremanReact/common/helpers';
import * as api from 'foremanReact/redux/API';
import JobInvocationDetailPage from '../index';
import {
jobInvocationData,
jobInvocationDataScheduled,
jobInvocationDataRecurring,
mockPermissionsData,
mockReportTemplatesResponse,
mockReportTemplateInputsResponse,
} from './fixtures';
import {
cancelJob,
enableRecurringLogic,
cancelRecurringLogic,
} from '../JobInvocationActions';
import {
CANCEL_JOB,
CANCEL_RECURRING_LOGIC,
CHANGE_ENABLED_RECURRING_LOGIC,
GET_REPORT_TEMPLATES,
GET_REPORT_TEMPLATE_INPUTS,
JOB_INVOCATION_KEY,
} from '../JobInvocationConstants';
jest.spyOn(api, 'get');
jest.mock('foremanReact/common/hooks/API/APIHooks', () => ({
useAPI: jest.fn(() => ({
response: mockPermissionsData,
})),
}));
jest.mock('foremanReact/routes/common/PageLayout/PageLayout', () =>
jest.fn(props => (
{props.header &&
{props.header}
}
{props.toolbarButtons &&
{props.toolbarButtons}
}
{props.children}
))
);
const initialState = {
JOB_INVOCATION_KEY: {
response: jobInvocationData,
},
GET_REPORT_TEMPLATES: mockReportTemplatesResponse,
};
const initialStateScheduled = {
JOB_INVOCATION_KEY: {
response: jobInvocationDataScheduled,
},
};
api.get.mockImplementation(({ handleSuccess, ...action }) => {
if (action.key === 'GET_REPORT_TEMPLATES') {
handleSuccess &&
handleSuccess({
data: mockReportTemplatesResponse,
});
} else if (action.key === 'GET_REPORT_TEMPLATE_INPUTS') {
handleSuccess &&
handleSuccess({
data: mockReportTemplateInputsResponse,
});
}
return { type: 'get', ...action };
});
jest.mock('../JobInvocationHostTable.js', () => () => (
Mock Table
));
const reportTemplateJobId = mockReportTemplatesResponse.results[0].id;
const mockStore = configureMockStore([thunk]);
describe('JobInvocationDetailPage', () => {
it('renders main information', async () => {
const jobId = jobInvocationData.id;
const store = mockStore(initialState);
const { container } = render(
);
expect(screen.getByText('Description')).toBeInTheDocument();
expect(
container.querySelector('.chart-donut .pf-c-chart')
).toBeInTheDocument();
expect(screen.getByText('2/6')).toBeInTheDocument();
expect(screen.getByText('Systems')).toBeInTheDocument();
expect(screen.getByText('System status')).toBeInTheDocument();
expect(screen.getByText('Succeeded: 2')).toBeInTheDocument();
expect(screen.getByText('Failed: 4')).toBeInTheDocument();
expect(screen.getByText('In Progress: 0')).toBeInTheDocument();
expect(screen.getByText('Canceled: 0')).toBeInTheDocument();
const informationToCheck = {
'Effective user:': jobInvocationData.effective_user,
'Started at:': 'Jan 1, 2024, 11:34 UTC',
'SSH user:': 'Not available',
'Template:': jobInvocationData.template_name,
};
Object.entries(informationToCheck).forEach(([term, expectedText]) => {
const termContainers = container.querySelectorAll(
'.pf-c-description-list__term .pf-c-description-list__text'
);
termContainers.forEach(termContainer => {
if (termContainer.textContent.includes(term)) {
let descriptionContainer;
if (term === 'SSH user:') {
descriptionContainer = termContainer
.closest('.pf-c-description-list__group')
.querySelector(
'.pf-c-description-list__description .pf-c-description-list__text .disabled-text'
);
} else {
descriptionContainer = termContainer
.closest('.pf-c-description-list__group')
.querySelector(
'.pf-c-description-list__description .pf-c-description-list__text'
);
}
expect(descriptionContainer.textContent).toContain(expectedText);
}
});
});
// checks the global actions and if they link to the correct url
expect(screen.getByText('Create report').getAttribute('href')).toEqual(
foremanUrl(
`/templates/report_templates/${mockReportTemplatesResponse.results[0].id}/generate?report_template_report%5Binput_values%5D%5B${mockReportTemplateInputsResponse.results[0].id}%5D%5Bvalue%5D=${jobId}`
)
);
expect(screen.getByText('Rerun all').getAttribute('href')).toEqual(
foremanUrl(`/job_invocations/${jobId}/rerun`)
);
act(() => {
fireEvent.click(screen.getByRole('button', { name: 'Select' }));
});
expect(
screen
.getByText('Rerun successful')
.closest('a')
.getAttribute('href')
).toEqual(foremanUrl(`/job_invocations/${jobId}/rerun?succeeded_only=1`));
expect(
screen
.getByText('Rerun failed')
.closest('a')
.getAttribute('href')
).toEqual(foremanUrl(`/job_invocations/${jobId}/rerun?failed_only=1`));
expect(
screen
.getByText('View task')
.closest('a')
.getAttribute('href')
).toEqual(foremanUrl(`/foreman_tasks/tasks/${jobInvocationData.task.id}`));
expect(screen.getByText('Cancel')).toBeInTheDocument();
expect(screen.getByText('Abort')).toBeInTheDocument();
expect(screen.queryByText('Enable recurring')).not.toBeInTheDocument();
expect(screen.queryByText('Cancel recurring')).not.toBeInTheDocument();
expect(
screen
.getByText('Legacy UI')
.closest('a')
.getAttribute('href')
).toEqual(`/job_invocations/${jobId}`);
});
it('shows scheduled date', async () => {
const store = mockStore(initialStateScheduled);
render(
);
expect(screen.getByText('Scheduled at:')).toBeInTheDocument();
expect(screen.getByText('Jan 1, 3000, 11:34 UTC')).toBeInTheDocument();
expect(screen.getByText('Not yet')).toBeInTheDocument();
});
it('should dispatch global actions', async () => {
// recurring in the future
const jobId = jobInvocationDataRecurring.id;
const recurrenceId = jobInvocationDataRecurring.recurrence.id;
const store = mockStore(jobInvocationDataRecurring);
render(
);
const expectedActions = [
{ key: GET_REPORT_TEMPLATES, url: '/api/report_templates' },
{
key: JOB_INVOCATION_KEY,
url: `/api/job_invocations/${jobId}?host_status=true`,
},
{
key: GET_REPORT_TEMPLATE_INPUTS,
url: `/api/templates/${reportTemplateJobId}/template_inputs`,
},
{
key: CANCEL_JOB,
url: `/job_invocations/${jobId}/cancel`,
},
{
key: CANCEL_JOB,
url: `/job_invocations/${jobId}/cancel?force=true`,
},
{
key: CHANGE_ENABLED_RECURRING_LOGIC,
url: `/foreman_tasks/api/recurring_logics/${recurrenceId}`,
},
{
key: CHANGE_ENABLED_RECURRING_LOGIC,
url: `/foreman_tasks/api/recurring_logics/${recurrenceId}`,
},
{
key: CANCEL_RECURRING_LOGIC,
url: `/foreman_tasks/recurring_logics/${recurrenceId}/cancel`,
},
];
store.dispatch(cancelJob(jobId, false));
store.dispatch(cancelJob(jobId, true));
store.dispatch(enableRecurringLogic(recurrenceId, true, jobId));
store.dispatch(enableRecurringLogic(recurrenceId, false, jobId));
store.dispatch(cancelRecurringLogic(recurrenceId, jobId));
const actualActions = store.getActions();
expect(actualActions).toHaveLength(expectedActions.length);
expectedActions.forEach((expectedAction, index) => {
if (actualActions[index].type === 'WITH_INTERVAL') {
expect(actualActions[index].key.key).toEqual(expectedAction.key);
expect(actualActions[index].key.url).toEqual(expectedAction.url);
} else {
expect(actualActions[index].key).toEqual(expectedAction.key);
if (expectedAction.url) {
expect(actualActions[index].url).toEqual(expectedAction.url);
}
}
});
});
});