foreman_openscap 5.0.0 → 5.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/graphql/mutations/oval_contents/delete.rb +9 -0
- data/app/graphql/mutations/oval_policies/delete.rb +9 -0
- data/app/graphql/mutations/oval_policies/update.rb +15 -0
- data/app/graphql/types/oval_check.rb +11 -0
- data/app/graphql/types/oval_content.rb +2 -0
- data/app/graphql/types/oval_policy.rb +3 -0
- data/app/models/concerns/foreman_openscap/host_extensions.rb +0 -6
- data/app/models/concerns/foreman_openscap/oval_facet_hostgroup_extensions.rb +15 -0
- data/app/models/foreman_openscap/oval_content.rb +2 -0
- data/app/services/foreman_openscap/oval/configure.rb +1 -1
- data/app/services/foreman_openscap/oval/setup.rb +5 -5
- data/app/services/foreman_openscap/oval/setup_check.rb +5 -2
- data/db/migrate/20210819143316_drop_unused_tables.rb +6 -0
- data/lib/foreman_openscap/engine.rb +6 -1
- data/lib/foreman_openscap/version.rb +1 -1
- data/package.json +3 -6
- data/test/graphql/mutations/oval_policies/delete_mutation_test.rb +63 -0
- data/test/graphql/queries/oval_content_query_test.rb +29 -0
- data/test/unit/services/hostgroup_overrider_test.rb +1 -1
- data/test/unit/services/oval/setup_check_test.rb +37 -0
- data/webpack/components/ConfirmModal.js +63 -0
- data/webpack/components/ConfirmModal.scss +3 -0
- data/webpack/components/EditableInput.js +157 -0
- data/webpack/components/EditableInput.scss +3 -0
- data/webpack/components/EmptyState.js +4 -1
- data/webpack/components/IndexLayout.js +11 -4
- data/webpack/components/IndexTable/index.js +17 -17
- data/webpack/components/LinkButton.js +26 -0
- data/webpack/components/withDeleteModal.js +51 -0
- data/webpack/components/withLoading.js +21 -3
- data/webpack/graphql/mutations/deleteOvalContent.gql +9 -0
- data/webpack/graphql/mutations/deleteOvalPolicy.gql +9 -0
- data/webpack/graphql/mutations/updateOvalPolicy.gql +14 -0
- data/webpack/graphql/queries/hostgroups.gql +14 -0
- data/webpack/graphql/queries/ovalContent.gql +8 -0
- data/webpack/graphql/queries/ovalContents.gql +3 -0
- data/webpack/graphql/queries/ovalPolicies.gql +3 -0
- data/webpack/helpers/formFieldsHelper.js +63 -0
- data/webpack/helpers/mutationHelper.js +68 -0
- data/webpack/helpers/pathsHelper.js +5 -0
- data/webpack/helpers/toastHelper.js +3 -0
- data/webpack/routes/OvalContents/OvalContentsIndex/OvalContentsIndex.js +25 -0
- data/webpack/routes/OvalContents/OvalContentsIndex/OvalContentsTable.js +41 -4
- data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsDestroy.fixtures.js +105 -0
- data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsDestroy.test.js +124 -0
- data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsIndex.fixtures.js +61 -59
- data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsIndex.test.js +29 -8
- data/webpack/routes/OvalContents/OvalContentsIndex/index.js +7 -1
- data/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNew.js +138 -0
- data/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNew.scss +3 -0
- data/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNewHelper.js +73 -0
- data/webpack/routes/OvalContents/OvalContentsNew/__tests__/OvalContentsNew.test.js +104 -0
- data/webpack/routes/OvalContents/OvalContentsNew/index.js +13 -0
- data/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShow.js +62 -0
- data/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShow.test.js +45 -0
- data/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShowHelper.js +0 -0
- data/webpack/routes/OvalContents/OvalContentsShow/index.js +35 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/OvalPoliciesIndex.js +17 -2
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/OvalPoliciesTable.js +16 -3
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesDestroy.fixtures.js +101 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesDestroy.test.js +117 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesIndex.fixtures.js +57 -41
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesIndex.test.js +14 -2
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/index.js +7 -1
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/DetailsTab.js +85 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/HostgroupsTab.js +49 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/HostgroupsTable.js +38 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/OvalPoliciesShow.js +15 -11
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/OvalPoliciesShowHelper.js +77 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesEdit.fixtures.js +48 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesEdit.test.js +175 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesShow.fixtures.js +28 -1
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesShow.test.js +47 -4
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/index.js +3 -0
- data/webpack/routes/routes.js +14 -0
- data/webpack/testHelper.js +9 -1
- metadata +46 -3
@@ -0,0 +1,104 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
|
3
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
4
|
+
import userEvent from '@testing-library/user-event';
|
5
|
+
import '@testing-library/jest-dom';
|
6
|
+
import api from 'foremanReact/redux/API/API';
|
7
|
+
|
8
|
+
import OvalContentsNew from '../OvalContentsNew';
|
9
|
+
import { withRouter, withRedux, tick } from '../../../../testHelper';
|
10
|
+
import { ovalContentsPath } from '../../../../helpers/pathsHelper';
|
11
|
+
|
12
|
+
jest.mock('foremanReact/redux/API/API', () => ({ post: jest.fn() }));
|
13
|
+
|
14
|
+
const TestComponent = withRouter(withRedux(OvalContentsNew));
|
15
|
+
|
16
|
+
describe('OvalContentsNew', () => {
|
17
|
+
it('should create with content from URL', async () => {
|
18
|
+
const pushMock = jest.fn();
|
19
|
+
const toastMock = jest.fn();
|
20
|
+
|
21
|
+
api.post.mockImplementation(() => Promise.resolve());
|
22
|
+
|
23
|
+
render(
|
24
|
+
<TestComponent history={{ push: pushMock }} showToast={toastMock} />
|
25
|
+
);
|
26
|
+
expect(screen.getByText('Name')).toBeInTheDocument();
|
27
|
+
expect(screen.getByText('OVAL Content Source')).toBeInTheDocument();
|
28
|
+
expect(screen.getByText('URL')).toBeInTheDocument();
|
29
|
+
expect(screen.queryByText('File')).not.toBeInTheDocument();
|
30
|
+
expect(screen.getByText('Submit')).toBeDisabled();
|
31
|
+
userEvent.type(screen.getByLabelText('name'), 'test content');
|
32
|
+
await waitFor(tick);
|
33
|
+
expect(screen.getByText('Submit')).toBeDisabled();
|
34
|
+
userEvent.type(
|
35
|
+
screen.getByLabelText(/url/),
|
36
|
+
'http://oval-content-source.org/security/data/oval/v2/CentOS7/ansible-2.9.oval.xml.bz2'
|
37
|
+
);
|
38
|
+
await waitFor(tick);
|
39
|
+
expect(screen.getByText('Submit')).not.toBeDisabled();
|
40
|
+
userEvent.click(screen.getByText('Submit'));
|
41
|
+
await waitFor(tick);
|
42
|
+
expect(pushMock).toHaveBeenCalledWith(ovalContentsPath, {
|
43
|
+
refreshOvalContents: true,
|
44
|
+
});
|
45
|
+
expect(toastMock).toHaveBeenCalledWith({
|
46
|
+
type: 'success',
|
47
|
+
message: 'OVAL Content test content successfully created',
|
48
|
+
});
|
49
|
+
});
|
50
|
+
it('should show resource errors', async () => {
|
51
|
+
const pushMock = jest.fn();
|
52
|
+
const toastMock = jest.fn();
|
53
|
+
api.post.mockImplementation(() => {
|
54
|
+
// eslint-disable-next-line no-throw-literal
|
55
|
+
throw {
|
56
|
+
response: {
|
57
|
+
status: 422,
|
58
|
+
data: { error: { errors: { name: ['has already been taken'] } } },
|
59
|
+
},
|
60
|
+
};
|
61
|
+
});
|
62
|
+
|
63
|
+
render(
|
64
|
+
<TestComponent history={{ push: pushMock }} showToast={toastMock} />
|
65
|
+
);
|
66
|
+
userEvent.type(screen.getByLabelText('name'), 'test content');
|
67
|
+
userEvent.type(
|
68
|
+
screen.getByLabelText(/url/),
|
69
|
+
'http://oval-content-source.org/security/data/oval/v2/CentOS7/ansible-2.9.oval.xml.bz2'
|
70
|
+
);
|
71
|
+
await waitFor(tick);
|
72
|
+
userEvent.click(screen.getByText('Submit'));
|
73
|
+
await waitFor(tick);
|
74
|
+
expect(pushMock).not.toHaveBeenCalled();
|
75
|
+
expect(screen.getByText('has already been taken')).toBeInTheDocument();
|
76
|
+
});
|
77
|
+
it('should show error toast on unexpected error', async () => {
|
78
|
+
const pushMock = jest.fn();
|
79
|
+
const toastMock = jest.fn();
|
80
|
+
|
81
|
+
api.post.mockImplementation(() => {
|
82
|
+
// eslint-disable-next-line no-throw-literal
|
83
|
+
throw { response: { status: 500 } };
|
84
|
+
});
|
85
|
+
|
86
|
+
render(
|
87
|
+
<TestComponent history={{ push: pushMock }} showToast={toastMock} />
|
88
|
+
);
|
89
|
+
userEvent.type(screen.getByLabelText('name'), 'test content');
|
90
|
+
userEvent.type(
|
91
|
+
screen.getByLabelText(/url/),
|
92
|
+
'http://oval-content-source.org/security/data/oval/v2/CentOS7/ansible-2.9.oval.xml.bz2'
|
93
|
+
);
|
94
|
+
await waitFor(tick);
|
95
|
+
userEvent.click(screen.getByText('Submit'));
|
96
|
+
await waitFor(tick);
|
97
|
+
expect(pushMock).not.toHaveBeenCalled();
|
98
|
+
expect(screen.getByText('Submit')).not.toBeDisabled();
|
99
|
+
expect(toastMock).toHaveBeenCalledWith({
|
100
|
+
type: 'error',
|
101
|
+
message: 'Unknown error when submitting data, please try again later.',
|
102
|
+
});
|
103
|
+
});
|
104
|
+
});
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { useDispatch } from 'react-redux';
|
3
|
+
import { showToast } from '../../../helpers/toastHelper';
|
4
|
+
|
5
|
+
import OvalContentsNew from './OvalContentsNew';
|
6
|
+
|
7
|
+
const WrappedOvalContentsNew = props => {
|
8
|
+
const dispatch = useDispatch();
|
9
|
+
|
10
|
+
return <OvalContentsNew {...props} showToast={showToast(dispatch)} />;
|
11
|
+
};
|
12
|
+
|
13
|
+
export default WrappedOvalContentsNew;
|
@@ -0,0 +1,62 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import { Helmet } from 'react-helmet';
|
4
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
5
|
+
|
6
|
+
import {
|
7
|
+
Grid,
|
8
|
+
GridItem,
|
9
|
+
TextContent,
|
10
|
+
Text,
|
11
|
+
TextVariants,
|
12
|
+
} from '@patternfly/react-core';
|
13
|
+
|
14
|
+
import withLoading from '../../../components/withLoading';
|
15
|
+
|
16
|
+
const OvalContentsShow = ({ ovalContent }) => {
|
17
|
+
let contentSource;
|
18
|
+
if (ovalContent.url) {
|
19
|
+
contentSource = (
|
20
|
+
<React.Fragment>
|
21
|
+
<Text component={TextVariants.h3}>{__('URL')}</Text>
|
22
|
+
<Text component={TextVariants.p}>{ovalContent.url || ''}</Text>
|
23
|
+
</React.Fragment>
|
24
|
+
);
|
25
|
+
} else {
|
26
|
+
contentSource = (
|
27
|
+
<React.Fragment>
|
28
|
+
<Text component={TextVariants.h3}>{__('File')}</Text>
|
29
|
+
<Text component={TextVariants.p}>
|
30
|
+
{ovalContent.originalFilename || ''}
|
31
|
+
</Text>
|
32
|
+
</React.Fragment>
|
33
|
+
);
|
34
|
+
}
|
35
|
+
|
36
|
+
return (
|
37
|
+
<React.Fragment>
|
38
|
+
<Helmet>
|
39
|
+
<title>{`${ovalContent.name} | ${__('OVAL Content')}`}</title>
|
40
|
+
</Helmet>
|
41
|
+
<Grid className="scap-page-grid">
|
42
|
+
<GridItem span={10}>
|
43
|
+
<Text component={TextVariants.h1}>{ovalContent.name}</Text>
|
44
|
+
</GridItem>
|
45
|
+
<GridItem span={2} />
|
46
|
+
<GridItem span={12}>
|
47
|
+
<TextContent className="pf-u-pt-md">
|
48
|
+
<Text component={TextVariants.h3}>{__('Name')}</Text>
|
49
|
+
<Text component={TextVariants.p}>{ovalContent.name}</Text>
|
50
|
+
{contentSource}
|
51
|
+
</TextContent>
|
52
|
+
</GridItem>
|
53
|
+
</Grid>
|
54
|
+
</React.Fragment>
|
55
|
+
);
|
56
|
+
};
|
57
|
+
|
58
|
+
OvalContentsShow.propTypes = {
|
59
|
+
ovalContent: PropTypes.object.isRequired,
|
60
|
+
};
|
61
|
+
|
62
|
+
export default withLoading(OvalContentsShow);
|
@@ -0,0 +1,45 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import '@testing-library/jest-dom';
|
3
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
4
|
+
|
5
|
+
import { withMockedProvider, tick } from '../../../testHelper';
|
6
|
+
import ovalContentQuery from '../../../graphql/queries/ovalContent.gql';
|
7
|
+
import OvalContentsShow from './';
|
8
|
+
|
9
|
+
const TestComponent = withMockedProvider(OvalContentsShow);
|
10
|
+
|
11
|
+
const matchMock = { params: { id: 4 } };
|
12
|
+
const name = 'dotnet OVAL content';
|
13
|
+
const url =
|
14
|
+
'http://oval-content-source/security/data/oval/dotnet-2.2.oval.xml.bz2';
|
15
|
+
const id = 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsQ29udGVudC00';
|
16
|
+
|
17
|
+
const mocks = [
|
18
|
+
{
|
19
|
+
request: {
|
20
|
+
query: ovalContentQuery,
|
21
|
+
variables: { id },
|
22
|
+
},
|
23
|
+
result: {
|
24
|
+
data: {
|
25
|
+
ovalContent: {
|
26
|
+
id,
|
27
|
+
name,
|
28
|
+
url,
|
29
|
+
originalFilename: '',
|
30
|
+
},
|
31
|
+
},
|
32
|
+
},
|
33
|
+
},
|
34
|
+
];
|
35
|
+
|
36
|
+
describe('OVAL Contents show page', () => {
|
37
|
+
it('should show OVAL Content', async () => {
|
38
|
+
render(<TestComponent match={matchMock} mocks={mocks} />);
|
39
|
+
expect(screen.getByText('Loading')).toBeInTheDocument();
|
40
|
+
await waitFor(tick);
|
41
|
+
expect(screen.queryByText('Loading')).not.toBeInTheDocument();
|
42
|
+
expect(screen.getAllByText(name).length === 2).toBeTruthy();
|
43
|
+
expect(screen.getByText(url)).toBeInTheDocument();
|
44
|
+
});
|
45
|
+
});
|
File without changes
|
@@ -0,0 +1,35 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import { useQuery } from '@apollo/client';
|
4
|
+
|
5
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
6
|
+
|
7
|
+
import OvalContentsShow from './OvalContentsShow';
|
8
|
+
import { encodeId } from '../../../helpers/globalIdHelper';
|
9
|
+
|
10
|
+
import ovalContent from '../../../graphql/queries/ovalContent.gql';
|
11
|
+
|
12
|
+
const WrappedOvalContentsShow = props => {
|
13
|
+
const id = encodeId('ForemanOpenscap::OvalContent', props.match.params.id);
|
14
|
+
|
15
|
+
const useFetchFn = componentProps =>
|
16
|
+
useQuery(ovalContent, { variables: { id } });
|
17
|
+
|
18
|
+
const renameData = data => ({ ovalContent: data.ovalContent });
|
19
|
+
|
20
|
+
return (
|
21
|
+
<OvalContentsShow
|
22
|
+
{...props}
|
23
|
+
fetchFn={useFetchFn}
|
24
|
+
renameData={renameData}
|
25
|
+
resultPath="ovalContent"
|
26
|
+
emptyStateTitle={__('No OVAL Content found')}
|
27
|
+
/>
|
28
|
+
);
|
29
|
+
};
|
30
|
+
|
31
|
+
WrappedOvalContentsShow.propTypes = {
|
32
|
+
match: PropTypes.object.isRequired,
|
33
|
+
};
|
34
|
+
|
35
|
+
export default WrappedOvalContentsShow;
|
@@ -4,6 +4,8 @@ import { useQuery } from '@apollo/client';
|
|
4
4
|
import { translate as __ } from 'foremanReact/common/I18n';
|
5
5
|
|
6
6
|
import OvalPoliciesTable from './OvalPoliciesTable';
|
7
|
+
import { submitDelete, prepareMutation } from '../../../helpers/mutationHelper';
|
8
|
+
|
7
9
|
import IndexLayout from '../../../components/IndexLayout';
|
8
10
|
|
9
11
|
import {
|
@@ -11,10 +13,9 @@ import {
|
|
11
13
|
useCurrentPagination,
|
12
14
|
} from '../../../helpers/pageParamsHelper';
|
13
15
|
import policiesQuery from '../../../graphql/queries/ovalPolicies.gql';
|
16
|
+
import deleteOvalPolicyMutation from '../../../graphql/mutations/deleteOvalPolicy.gql';
|
14
17
|
|
15
18
|
const OvalPoliciesIndex = props => {
|
16
|
-
const pagination = useCurrentPagination(props.history);
|
17
|
-
|
18
19
|
const useFetchFn = componentProps =>
|
19
20
|
useQuery(policiesQuery, {
|
20
21
|
variables: useParamsToVars(componentProps.history),
|
@@ -25,6 +26,8 @@ const OvalPoliciesIndex = props => {
|
|
25
26
|
totalCount: data.ovalPolicies.totalCount,
|
26
27
|
});
|
27
28
|
|
29
|
+
const pagination = useCurrentPagination(props.history);
|
30
|
+
|
28
31
|
return (
|
29
32
|
<IndexLayout pageTitle={__('OVAL Policies')}>
|
30
33
|
<OvalPoliciesTable
|
@@ -35,6 +38,17 @@ const OvalPoliciesIndex = props => {
|
|
35
38
|
pagination={pagination}
|
36
39
|
emptyStateTitle={__('No OVAL Policies found')}
|
37
40
|
permissions={['view_oval_policies']}
|
41
|
+
confirmDeleteTitle={__('Delete OVAL Policy')}
|
42
|
+
submitDelete={submitDelete}
|
43
|
+
prepareMutation={prepareMutation(
|
44
|
+
props.history,
|
45
|
+
props.showToast,
|
46
|
+
policiesQuery,
|
47
|
+
'deleteOvalPolicy',
|
48
|
+
__('OVAL policy was successfully deleted.'),
|
49
|
+
deleteOvalPolicyMutation,
|
50
|
+
__('OVAL policy')
|
51
|
+
)}
|
38
52
|
/>
|
39
53
|
</IndexLayout>
|
40
54
|
);
|
@@ -42,6 +56,7 @@ const OvalPoliciesIndex = props => {
|
|
42
56
|
|
43
57
|
OvalPoliciesIndex.propTypes = {
|
44
58
|
history: PropTypes.object.isRequired,
|
59
|
+
showToast: PropTypes.func.isRequired,
|
45
60
|
};
|
46
61
|
|
47
62
|
export default OvalPoliciesIndex;
|
@@ -4,6 +4,7 @@ import { translate as __ } from 'foremanReact/common/I18n';
|
|
4
4
|
|
5
5
|
import IndexTable from '../../../components/IndexTable';
|
6
6
|
import withLoading from '../../../components/withLoading';
|
7
|
+
import withDeleteModal from '../../../components/withDeleteModal';
|
7
8
|
|
8
9
|
import { linkCell } from '../../../helpers/tableHelper';
|
9
10
|
import { ovalPoliciesPath, modelPath } from '../../../helpers/pathsHelper';
|
@@ -19,13 +20,24 @@ const OvalPoliciesTable = props => {
|
|
19
20
|
policy,
|
20
21
|
}));
|
21
22
|
|
22
|
-
const
|
23
|
+
const actionResolver = (rowData, rest) => {
|
24
|
+
const actions = [];
|
25
|
+
if (rowData.policy.meta.canDestroy) {
|
26
|
+
actions.push({
|
27
|
+
title: __('Delete OVAL Policy'),
|
28
|
+
onClick: (event, rowId, rData, extra) => {
|
29
|
+
props.toggleModal(rData.policy);
|
30
|
+
},
|
31
|
+
});
|
32
|
+
}
|
33
|
+
return actions;
|
34
|
+
};
|
23
35
|
|
24
36
|
return (
|
25
37
|
<IndexTable
|
26
38
|
columns={columns}
|
27
39
|
rows={rows}
|
28
|
-
|
40
|
+
actionResolver={actionResolver}
|
29
41
|
pagination={props.pagination}
|
30
42
|
totalCount={props.totalCount}
|
31
43
|
history={props.history}
|
@@ -39,6 +51,7 @@ OvalPoliciesTable.propTypes = {
|
|
39
51
|
pagination: PropTypes.object.isRequired,
|
40
52
|
totalCount: PropTypes.number.isRequired,
|
41
53
|
history: PropTypes.object.isRequired,
|
54
|
+
toggleModal: PropTypes.func.isRequired,
|
42
55
|
};
|
43
56
|
|
44
|
-
export default withLoading(OvalPoliciesTable);
|
57
|
+
export default withLoading(withDeleteModal(OvalPoliciesTable));
|
@@ -0,0 +1,101 @@
|
|
1
|
+
import policiesQuery from '../../../../graphql/queries/ovalPolicies.gql';
|
2
|
+
import deleteOvalPolicy from '../../../../graphql/mutations/deleteOvalPolicy.gql';
|
3
|
+
|
4
|
+
import { admin } from '../../../../testHelper';
|
5
|
+
|
6
|
+
export const firstCall = {
|
7
|
+
data: {
|
8
|
+
ovalPolicies: {
|
9
|
+
totalCount: 5,
|
10
|
+
nodes: [
|
11
|
+
{
|
12
|
+
__typename: 'ForemanOpenscap::OvalPolicy',
|
13
|
+
id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsUG9saWN5LTQz',
|
14
|
+
name: 'first policy',
|
15
|
+
meta: { canDestroy: true },
|
16
|
+
ovalContent: { name: 'foo' },
|
17
|
+
},
|
18
|
+
{
|
19
|
+
__typename: 'ForemanOpenscap::OvalPolicy',
|
20
|
+
id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsUG9saWN5LTQ0',
|
21
|
+
name: 'second policy',
|
22
|
+
meta: { canDestroy: true },
|
23
|
+
ovalContent: { name: 'foo' },
|
24
|
+
},
|
25
|
+
],
|
26
|
+
},
|
27
|
+
currentUser: admin,
|
28
|
+
},
|
29
|
+
};
|
30
|
+
|
31
|
+
export const secondCall = {
|
32
|
+
data: {
|
33
|
+
ovalPolicies: {
|
34
|
+
totalCount: 4,
|
35
|
+
nodes: [
|
36
|
+
{
|
37
|
+
id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsUG9saWN5LTQ0',
|
38
|
+
name: 'second policy',
|
39
|
+
meta: { canDestroy: true },
|
40
|
+
ovalContent: { name: 'foo' },
|
41
|
+
},
|
42
|
+
{
|
43
|
+
id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsUG9saWN5LTQ1',
|
44
|
+
name: 'third policy',
|
45
|
+
meta: { canDestroy: true },
|
46
|
+
ovalContent: { name: 'foo' },
|
47
|
+
},
|
48
|
+
],
|
49
|
+
},
|
50
|
+
currentUser: admin,
|
51
|
+
},
|
52
|
+
};
|
53
|
+
|
54
|
+
export const deleteMockFactory = (first, second, errors = null) => {
|
55
|
+
let called = false;
|
56
|
+
|
57
|
+
const deleteMocks = [
|
58
|
+
{
|
59
|
+
request: {
|
60
|
+
query: deleteOvalPolicy,
|
61
|
+
variables: {
|
62
|
+
id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsUG9saWN5LTQz',
|
63
|
+
},
|
64
|
+
},
|
65
|
+
result: {
|
66
|
+
data: {
|
67
|
+
deleteOvalPolicy: {
|
68
|
+
__typename: 'ForemanOpenscap::OvalPolicy',
|
69
|
+
id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsUG9saWN5LTQz',
|
70
|
+
errors,
|
71
|
+
},
|
72
|
+
},
|
73
|
+
},
|
74
|
+
},
|
75
|
+
{
|
76
|
+
request: {
|
77
|
+
query: policiesQuery,
|
78
|
+
variables: {
|
79
|
+
first: 2,
|
80
|
+
last: 2,
|
81
|
+
},
|
82
|
+
},
|
83
|
+
newData: () => {
|
84
|
+
if (called && !errors) {
|
85
|
+
return second;
|
86
|
+
} else if (called && errors) {
|
87
|
+
return first;
|
88
|
+
}
|
89
|
+
called = true;
|
90
|
+
return first;
|
91
|
+
},
|
92
|
+
},
|
93
|
+
];
|
94
|
+
return deleteMocks;
|
95
|
+
};
|
96
|
+
|
97
|
+
export const pageParamsHistoryMock = {
|
98
|
+
location: {
|
99
|
+
search: '?page=1&perPage=2',
|
100
|
+
},
|
101
|
+
};
|
@@ -0,0 +1,117 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
3
|
+
import '@testing-library/jest-dom';
|
4
|
+
import userEvent from '@testing-library/user-event';
|
5
|
+
|
6
|
+
import OvalPoliciesIndex from '../OvalPoliciesIndex';
|
7
|
+
import {
|
8
|
+
withRouter,
|
9
|
+
withRedux,
|
10
|
+
withMockedProvider,
|
11
|
+
tick,
|
12
|
+
historyMock,
|
13
|
+
} from '../../../../testHelper';
|
14
|
+
import { mocks, noDeleteMocks } from './OvalPoliciesIndex.fixtures';
|
15
|
+
import {
|
16
|
+
firstCall,
|
17
|
+
secondCall,
|
18
|
+
deleteMockFactory,
|
19
|
+
pageParamsHistoryMock,
|
20
|
+
} from './OvalPoliciesDestroy.fixtures';
|
21
|
+
|
22
|
+
const TestComponent = withRouter(
|
23
|
+
withRedux(withMockedProvider(OvalPoliciesIndex))
|
24
|
+
);
|
25
|
+
|
26
|
+
describe('OvalPoliciesIndex', () => {
|
27
|
+
it('should open and close delete modal', async () => {
|
28
|
+
render(
|
29
|
+
<TestComponent
|
30
|
+
history={historyMock}
|
31
|
+
mocks={mocks}
|
32
|
+
showToast={jest.fn()}
|
33
|
+
/>
|
34
|
+
);
|
35
|
+
await waitFor(tick);
|
36
|
+
expect(screen.getByText('first policy')).toBeInTheDocument();
|
37
|
+
userEvent.click(screen.getAllByRole('button', { name: 'Actions' })[0]);
|
38
|
+
userEvent.click(screen.getByText('Delete OVAL Policy'));
|
39
|
+
await waitFor(tick);
|
40
|
+
expect(
|
41
|
+
screen.getByText('Are you sure you want to delete first policy?')
|
42
|
+
).toBeInTheDocument();
|
43
|
+
userEvent.click(screen.getByText('Cancel'));
|
44
|
+
await waitFor(tick);
|
45
|
+
expect(
|
46
|
+
screen.queryByText('Are you sure you want to delete first policy?')
|
47
|
+
).not.toBeInTheDocument();
|
48
|
+
expect(screen.getByText('first policy')).toBeInTheDocument();
|
49
|
+
});
|
50
|
+
it('should delete OVAL policy', async () => {
|
51
|
+
const showToast = jest.fn();
|
52
|
+
render(
|
53
|
+
<TestComponent
|
54
|
+
history={pageParamsHistoryMock}
|
55
|
+
mocks={deleteMockFactory(firstCall, secondCall)}
|
56
|
+
showToast={showToast}
|
57
|
+
/>
|
58
|
+
);
|
59
|
+
await waitFor(tick);
|
60
|
+
expect(screen.getByText('first policy')).toBeInTheDocument();
|
61
|
+
expect(screen.queryByText('third policy')).not.toBeInTheDocument();
|
62
|
+
userEvent.click(screen.getAllByRole('button', { name: 'Actions' })[0]);
|
63
|
+
userEvent.click(screen.getByText('Delete OVAL Policy'));
|
64
|
+
await waitFor(tick);
|
65
|
+
userEvent.click(screen.getByText('Confirm'));
|
66
|
+
await waitFor(tick);
|
67
|
+
expect(showToast).toHaveBeenCalledWith({
|
68
|
+
type: 'success',
|
69
|
+
message: 'OVAL policy was successfully deleted.',
|
70
|
+
});
|
71
|
+
await waitFor(tick);
|
72
|
+
expect(screen.queryByText('first policy')).not.toBeInTheDocument();
|
73
|
+
expect(screen.getByText('third policy')).toBeInTheDocument();
|
74
|
+
});
|
75
|
+
it('should show error when deleting OVAL policy fails', async () => {
|
76
|
+
const showToast = jest.fn();
|
77
|
+
render(
|
78
|
+
<TestComponent
|
79
|
+
history={pageParamsHistoryMock}
|
80
|
+
mocks={deleteMockFactory(firstCall, secondCall, [
|
81
|
+
{ message: 'cannot do it', path: 'base' },
|
82
|
+
{ message: 'will not do it', path: 'base' },
|
83
|
+
])}
|
84
|
+
showToast={showToast}
|
85
|
+
/>
|
86
|
+
);
|
87
|
+
await waitFor(tick);
|
88
|
+
expect(screen.getByText('first policy')).toBeInTheDocument();
|
89
|
+
expect(screen.queryByText('third policy')).not.toBeInTheDocument();
|
90
|
+
userEvent.click(screen.getAllByRole('button', { name: 'Actions' })[0]);
|
91
|
+
userEvent.click(screen.getByText('Delete OVAL Policy'));
|
92
|
+
await waitFor(tick);
|
93
|
+
userEvent.click(screen.getByText('Confirm'));
|
94
|
+
await waitFor(tick);
|
95
|
+
expect(showToast).toHaveBeenCalledWith({
|
96
|
+
type: 'error',
|
97
|
+
message:
|
98
|
+
'There was a following error when deleting OVAL policy: cannot do it, will not do it',
|
99
|
+
});
|
100
|
+
expect(screen.getByText('first policy')).toBeInTheDocument();
|
101
|
+
expect(screen.queryByText('third policy')).not.toBeInTheDocument();
|
102
|
+
});
|
103
|
+
it('should not show delete button when user does not have delete permissions', async () => {
|
104
|
+
render(
|
105
|
+
<TestComponent
|
106
|
+
history={historyMock}
|
107
|
+
mocks={noDeleteMocks}
|
108
|
+
showToast={jest.fn()}
|
109
|
+
/>
|
110
|
+
);
|
111
|
+
await waitFor(tick);
|
112
|
+
expect(screen.getByText('first policy')).toBeInTheDocument();
|
113
|
+
expect(
|
114
|
+
screen.queryByRole('button', { name: 'Actions' })
|
115
|
+
).not.toBeInTheDocument();
|
116
|
+
});
|
117
|
+
});
|