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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/app/graphql/mutations/oval_contents/delete.rb +9 -0
  3. data/app/graphql/mutations/oval_policies/delete.rb +9 -0
  4. data/app/graphql/mutations/oval_policies/update.rb +15 -0
  5. data/app/graphql/types/oval_check.rb +11 -0
  6. data/app/graphql/types/oval_content.rb +2 -0
  7. data/app/graphql/types/oval_policy.rb +3 -0
  8. data/app/models/concerns/foreman_openscap/host_extensions.rb +0 -6
  9. data/app/models/concerns/foreman_openscap/oval_facet_hostgroup_extensions.rb +15 -0
  10. data/app/models/foreman_openscap/oval_content.rb +2 -0
  11. data/app/services/foreman_openscap/oval/configure.rb +1 -1
  12. data/app/services/foreman_openscap/oval/setup.rb +5 -5
  13. data/app/services/foreman_openscap/oval/setup_check.rb +5 -2
  14. data/db/migrate/20210819143316_drop_unused_tables.rb +6 -0
  15. data/lib/foreman_openscap/engine.rb +6 -1
  16. data/lib/foreman_openscap/version.rb +1 -1
  17. data/package.json +3 -6
  18. data/test/graphql/mutations/oval_policies/delete_mutation_test.rb +63 -0
  19. data/test/graphql/queries/oval_content_query_test.rb +29 -0
  20. data/test/unit/services/hostgroup_overrider_test.rb +1 -1
  21. data/test/unit/services/oval/setup_check_test.rb +37 -0
  22. data/webpack/components/ConfirmModal.js +63 -0
  23. data/webpack/components/ConfirmModal.scss +3 -0
  24. data/webpack/components/EditableInput.js +157 -0
  25. data/webpack/components/EditableInput.scss +3 -0
  26. data/webpack/components/EmptyState.js +4 -1
  27. data/webpack/components/IndexLayout.js +11 -4
  28. data/webpack/components/IndexTable/index.js +17 -17
  29. data/webpack/components/LinkButton.js +26 -0
  30. data/webpack/components/withDeleteModal.js +51 -0
  31. data/webpack/components/withLoading.js +21 -3
  32. data/webpack/graphql/mutations/deleteOvalContent.gql +9 -0
  33. data/webpack/graphql/mutations/deleteOvalPolicy.gql +9 -0
  34. data/webpack/graphql/mutations/updateOvalPolicy.gql +14 -0
  35. data/webpack/graphql/queries/hostgroups.gql +14 -0
  36. data/webpack/graphql/queries/ovalContent.gql +8 -0
  37. data/webpack/graphql/queries/ovalContents.gql +3 -0
  38. data/webpack/graphql/queries/ovalPolicies.gql +3 -0
  39. data/webpack/helpers/formFieldsHelper.js +63 -0
  40. data/webpack/helpers/mutationHelper.js +68 -0
  41. data/webpack/helpers/pathsHelper.js +5 -0
  42. data/webpack/helpers/toastHelper.js +3 -0
  43. data/webpack/routes/OvalContents/OvalContentsIndex/OvalContentsIndex.js +25 -0
  44. data/webpack/routes/OvalContents/OvalContentsIndex/OvalContentsTable.js +41 -4
  45. data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsDestroy.fixtures.js +105 -0
  46. data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsDestroy.test.js +124 -0
  47. data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsIndex.fixtures.js +61 -59
  48. data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsIndex.test.js +29 -8
  49. data/webpack/routes/OvalContents/OvalContentsIndex/index.js +7 -1
  50. data/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNew.js +138 -0
  51. data/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNew.scss +3 -0
  52. data/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNewHelper.js +73 -0
  53. data/webpack/routes/OvalContents/OvalContentsNew/__tests__/OvalContentsNew.test.js +104 -0
  54. data/webpack/routes/OvalContents/OvalContentsNew/index.js +13 -0
  55. data/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShow.js +62 -0
  56. data/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShow.test.js +45 -0
  57. data/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShowHelper.js +0 -0
  58. data/webpack/routes/OvalContents/OvalContentsShow/index.js +35 -0
  59. data/webpack/routes/OvalPolicies/OvalPoliciesIndex/OvalPoliciesIndex.js +17 -2
  60. data/webpack/routes/OvalPolicies/OvalPoliciesIndex/OvalPoliciesTable.js +16 -3
  61. data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesDestroy.fixtures.js +101 -0
  62. data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesDestroy.test.js +117 -0
  63. data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesIndex.fixtures.js +57 -41
  64. data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesIndex.test.js +14 -2
  65. data/webpack/routes/OvalPolicies/OvalPoliciesIndex/index.js +7 -1
  66. data/webpack/routes/OvalPolicies/OvalPoliciesShow/DetailsTab.js +85 -0
  67. data/webpack/routes/OvalPolicies/OvalPoliciesShow/HostgroupsTab.js +49 -0
  68. data/webpack/routes/OvalPolicies/OvalPoliciesShow/HostgroupsTable.js +38 -0
  69. data/webpack/routes/OvalPolicies/OvalPoliciesShow/OvalPoliciesShow.js +15 -11
  70. data/webpack/routes/OvalPolicies/OvalPoliciesShow/OvalPoliciesShowHelper.js +77 -0
  71. data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesEdit.fixtures.js +48 -0
  72. data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesEdit.test.js +175 -0
  73. data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesShow.fixtures.js +28 -1
  74. data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesShow.test.js +47 -4
  75. data/webpack/routes/OvalPolicies/OvalPoliciesShow/index.js +3 -0
  76. data/webpack/routes/routes.js +14 -0
  77. data/webpack/testHelper.js +9 -1
  78. 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
+ });
@@ -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 actions = [];
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
- actions={actions}
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
+ });