foreman_ansible 6.3.3 → 7.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/api/v2/ansible_inventories_controller.rb +1 -1
- data/app/graphql/mutations/ansible_variable_overrides/create.rb +26 -0
- data/app/graphql/mutations/ansible_variable_overrides/delete.rb +38 -0
- data/app/graphql/mutations/ansible_variable_overrides/update.rb +26 -0
- data/app/graphql/mutations/hosts/assign_ansible_roles.rb +37 -0
- data/app/graphql/presenters/ansible_role_presenter.rb +12 -0
- data/app/graphql/presenters/overriden_ansible_variable_presenter.rb +19 -0
- data/app/graphql/types/ansible_role.rb +9 -0
- data/app/graphql/types/ansible_variable.rb +23 -0
- data/app/graphql/types/ansible_variable_override.rb +9 -0
- data/app/graphql/types/inherited_ansible_role.rb +13 -0
- data/app/graphql/types/overriden_ansible_variable.rb +27 -0
- data/app/helpers/foreman_ansible/ansible_reports_helper.rb +35 -54
- data/app/models/concerns/foreman_ansible/host_managed_extensions.rb +23 -4
- data/app/models/concerns/foreman_ansible/hostgroup_extensions.rb +1 -0
- data/app/models/foreman_ansible/ansible_provider.rb +56 -6
- data/app/services/foreman_ansible/ansible_report_importer.rb +2 -2
- data/app/services/foreman_ansible/inventory_creator.rb +1 -1
- data/app/services/foreman_ansible/override_resolver.rb +22 -0
- data/app/views/api/v2/ansible_override_values/index.json.rabl +3 -0
- data/app/views/api/v2/ansible_variables/show.json.rabl +1 -1
- data/app/views/foreman_ansible/ansible_roles/_hostgroup_ansible_roles_button.erb +3 -0
- data/app/views/foreman_ansible/config_reports/_ansible.html.erb +14 -5
- data/app/views/foreman_ansible/job_templates/ansible_roles_-_ansible_default.erb +4 -0
- data/app/views/foreman_ansible/job_templates/convert_to_rhel.erb +6 -2
- data/app/views/foreman_ansible/job_templates/run_openscap_scans_-_ansible_default.erb +20 -0
- data/config/routes.rb +3 -0
- data/db/migrate/20210818083407_fix_ansible_setting_category_to_dsl.rb +5 -0
- data/lib/foreman_ansible/engine.rb +0 -18
- data/lib/foreman_ansible/register.rb +114 -2
- data/lib/foreman_ansible/version.rb +1 -1
- data/package.json +10 -6
- data/test/functional/api/v2/ansible_inventories_controller_test.rb +1 -2
- data/test/graphql/mutations/hosts/assign_ansible_roles_mutation_test.rb +96 -0
- data/test/graphql/queries/ansible_roles_query_test.rb +35 -0
- data/test/unit/ansible_provider_test.rb +3 -6
- data/test/unit/concerns/host_managed_extensions_test.rb +8 -0
- data/test/unit/concerns/hostgroup_extensions_test.rb +6 -0
- data/test/unit/helpers/ansible_reports_helper_test.rb +4 -30
- data/test/unit/services/override_resolver_test.rb +34 -0
- data/webpack/components/AnsibleHostDetail/AnsibleHostDetail.js +59 -0
- data/webpack/components/AnsibleHostDetail/AnsibleHostDetail.scss +6 -0
- data/webpack/components/AnsibleHostDetail/AnsibleHostDetail.test.js +20 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/AnsibleHostInventory.js +22 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/AnsibleHostInventory.scss +4 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/AnsibleHostInventory.test.js +104 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/index.js +38 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverrides.scss +3 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverridesTable.js +238 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverridesTableHelper.js +111 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableAction.js +161 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableAction.scss +7 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableActionHelper.js +49 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableValue.js +70 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableValueHelper.js +35 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverrides.fixtures.js +429 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverrides.test.js +71 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverridesDelete.test.js +74 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverridesUpdate.test.js +188 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/index.js +58 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/JobsTabHelper.js +79 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/NewRecurringJobHelper.js +106 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/NewRecurringJobModal.js +129 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/NewRecurringJobModal.scss +7 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/PreviousJobsTable.js +103 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/RecurringJobsTable.js +96 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/__test__/JobsTab.fixtures.js +184 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/__test__/JobsTab.test.js +195 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/index.js +88 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/AllRolesModal/AllRolesTable.js +89 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/AllRolesModal/index.js +80 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/EditRolesForm.js +90 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/EditRolesModal.scss +3 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/EditRolesModalHelper.js +40 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/index.js +82 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/RolesTable.js +129 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/EditRoles.test.js +85 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/RolesTab.fixtures.js +180 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/RolesTab.test.js +75 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/index.js +51 -0
- data/webpack/components/AnsibleHostDetail/components/SecondaryTabRoutes.js +60 -0
- data/webpack/components/AnsibleHostDetail/components/TabLayout.js +12 -0
- data/webpack/components/AnsibleHostDetail/constants.js +9 -0
- data/webpack/components/AnsibleHostDetail/helpers.js +4 -0
- data/webpack/components/AnsibleHostDetail/index.js +6 -0
- data/webpack/components/AnsibleRolesAndVariables/__test__/AnsibleRolesAndVariablesImport.test.js +15 -10
- data/webpack/components/AnsibleRolesSwitcher/components/AnsibleRole.js +29 -0
- data/webpack/components/AnsibleRolesSwitcher/components/AnsibleRole.test.js +3 -0
- data/webpack/components/AnsibleRolesSwitcher/components/AvailableRolesList.js +2 -1
- data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AnsiblePermissionDenied.test.js.snap +2 -0
- data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AnsibleRole.test.js.snap +3 -3
- data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AssignedRolesList.test.js.snap +4 -4
- data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AvailableRolesList.test.js.snap +9 -0
- data/webpack/components/DualList/DualList.scss +3 -0
- data/webpack/components/DualList/ListControls.js +65 -0
- data/webpack/components/DualList/ListHeader.js +16 -0
- data/webpack/components/DualList/ListItem.js +69 -0
- data/webpack/components/DualList/ListPane.js +95 -0
- data/webpack/components/DualList/SelectedStatus.js +21 -0
- data/webpack/components/DualList/index.js +103 -0
- data/webpack/components/ErrorState.js +16 -0
- data/webpack/components/withLoading.js +135 -0
- data/webpack/components/withPagination.js +0 -0
- data/webpack/formHelper.js +131 -0
- data/webpack/globalIdHelper.js +13 -0
- data/webpack/global_index.js +18 -0
- data/webpack/graphql/mutations/assignAnsibleRoles.gql +17 -0
- data/webpack/graphql/mutations/cancelRecurringLogic.gql +12 -0
- data/webpack/graphql/mutations/createAnsibleVariableOverride.gql +28 -0
- data/webpack/graphql/mutations/createJobInvocation.gql +11 -0
- data/webpack/graphql/mutations/deleteAnsibleVariableOverride.gql +17 -0
- data/webpack/graphql/mutations/updateAnsibleVariableOverride.gql +29 -0
- data/webpack/graphql/queries/allAnsibleRoles.gql +13 -0
- data/webpack/graphql/queries/ansibleRoles.gql +13 -0
- data/webpack/graphql/queries/currentUserAttributes.gql +11 -0
- data/webpack/graphql/queries/hostAnsibleRoles.gql +17 -0
- data/webpack/graphql/queries/hostAvailableAnsibleRoles.gql +11 -0
- data/webpack/graphql/queries/hostVariableOverrides.gql +39 -0
- data/webpack/graphql/queries/recurringJobs.gql +28 -0
- data/webpack/helpers/pageParamsHelper.js +40 -0
- data/webpack/helpers/paginationHelper.js +9 -0
- data/webpack/permissionsHelper.js +58 -0
- data/webpack/routes/HostgroupJobs/__test__/HostgroupJobs.fixtures.js +63 -0
- data/webpack/routes/HostgroupJobs/__test__/HostgroupJobs.test.js +112 -0
- data/webpack/routes/HostgroupJobs/index.js +26 -0
- data/webpack/routes/routes.js +10 -0
- data/webpack/testHelper.js +165 -0
- data/webpack/toastHelper.js +4 -0
- metadata +130 -78
- data/app/assets/images/foreman_ansible/Ansible.png +0 -0
- data/app/models/foreman_ansible/fact_name.rb +0 -16
- data/app/models/setting/ansible.rb +0 -106
- data/app/services/foreman_ansible/fact_importer.rb +0 -99
- data/app/services/foreman_ansible/fact_parser.rb +0 -126
- data/app/services/foreman_ansible/fact_sparser.rb +0 -37
- data/app/services/foreman_ansible/operating_system_parser.rb +0 -102
- data/app/services/foreman_ansible/structured_fact_importer.rb +0 -25
- data/test/unit/lib/foreman_ansible_core/ansible_runner_test.rb +0 -51
- data/test/unit/lib/foreman_ansible_core/command_creator_test.rb +0 -64
- data/test/unit/lib/foreman_ansible_core/playbook_runner_test.rb +0 -110
- data/test/unit/services/fact_importer_test.rb +0 -52
- data/test/unit/services/fact_parser_test.rb +0 -281
- data/test/unit/services/fact_sparser_test.rb +0 -24
- data/test/unit/services/structured_fact_importer_test.rb +0 -30
- data/webpack/__mocks__/foremanReact/common/I18n.js +0 -1
- data/webpack/__mocks__/foremanReact/common/helpers.js +0 -13
- data/webpack/__mocks__/foremanReact/components/Pagination/PaginationWrapper.js +0 -2
- data/webpack/__mocks__/foremanReact/components/common/EmptyState.js +0 -5
- data/webpack/__mocks__/foremanReact/components/common/forms/OrderableSelect/helpers.js +0 -5
- data/webpack/__mocks__/foremanReact/redux/API.js +0 -7
- data/webpack/components/AnsibleRolesAndVariables/__test__/__snapshots__/AnsibleRolesAndVariablesImport.test.js.snap +0 -177
@@ -0,0 +1,17 @@
|
|
1
|
+
#import "./currentUserAttributes.gql"
|
2
|
+
|
3
|
+
query($id: String!, $first: Int, $last: Int) {
|
4
|
+
host(id: $id) {
|
5
|
+
id
|
6
|
+
ownAnsibleRoles(first: $first, last: $last) {
|
7
|
+
totalCount
|
8
|
+
nodes {
|
9
|
+
id
|
10
|
+
name
|
11
|
+
}
|
12
|
+
}
|
13
|
+
}
|
14
|
+
currentUser {
|
15
|
+
...CurrentUserAttributes
|
16
|
+
}
|
17
|
+
}
|
@@ -0,0 +1,39 @@
|
|
1
|
+
#import "./currentUserAttributes.gql"
|
2
|
+
|
3
|
+
query($id: String!, $match: String, $first: Int, $last: Int) {
|
4
|
+
host(id: $id) {
|
5
|
+
id
|
6
|
+
ansibleVariablesWithOverrides(first: $first, last: $last) {
|
7
|
+
totalCount
|
8
|
+
nodes {
|
9
|
+
id
|
10
|
+
key
|
11
|
+
meta {
|
12
|
+
canEdit
|
13
|
+
}
|
14
|
+
defaultValue
|
15
|
+
parameterType
|
16
|
+
ansibleRoleName
|
17
|
+
validatorType
|
18
|
+
validatorRule
|
19
|
+
required
|
20
|
+
lookupValues(match: $match) {
|
21
|
+
nodes {
|
22
|
+
id
|
23
|
+
match
|
24
|
+
value
|
25
|
+
omit
|
26
|
+
}
|
27
|
+
}
|
28
|
+
currentValue {
|
29
|
+
value
|
30
|
+
element
|
31
|
+
elementName
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
currentUser {
|
37
|
+
...CurrentUserAttributes
|
38
|
+
}
|
39
|
+
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#import "./currentUserAttributes.gql"
|
2
|
+
|
3
|
+
query($search: String, $first: Int, $last: Int) {
|
4
|
+
jobInvocations(search: $search, first: $first, last: $last) {
|
5
|
+
totalCount
|
6
|
+
nodes {
|
7
|
+
id
|
8
|
+
description
|
9
|
+
startAt
|
10
|
+
statusLabel
|
11
|
+
recurringLogic {
|
12
|
+
id
|
13
|
+
cronLine
|
14
|
+
meta {
|
15
|
+
canEdit
|
16
|
+
}
|
17
|
+
}
|
18
|
+
task {
|
19
|
+
id
|
20
|
+
state
|
21
|
+
result
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}
|
25
|
+
currentUser {
|
26
|
+
...CurrentUserAttributes
|
27
|
+
}
|
28
|
+
}
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import URI from 'urijs';
|
2
|
+
import { useForemanSettings } from 'foremanReact/Root/Context/ForemanContext';
|
3
|
+
|
4
|
+
const parsePageParams = history => URI.parseQuery(history.location.search);
|
5
|
+
|
6
|
+
export const addSearch = (basePath, params) => {
|
7
|
+
let stringyfied = '';
|
8
|
+
if (Object.keys(params).length > 0) {
|
9
|
+
stringyfied = `?${URI.buildQuery(params)}`;
|
10
|
+
}
|
11
|
+
|
12
|
+
return `${basePath}${stringyfied}`;
|
13
|
+
};
|
14
|
+
|
15
|
+
export const useCurrentPagination = (
|
16
|
+
history,
|
17
|
+
keys = { page: 'page', perPage: 'perPage' }
|
18
|
+
) => {
|
19
|
+
const pageParams = parsePageParams(history);
|
20
|
+
const uiSettings = useForemanSettings();
|
21
|
+
|
22
|
+
return {
|
23
|
+
[keys.page]: parseInt(pageParams[keys.page], 10) || 1,
|
24
|
+
[keys.perPage]:
|
25
|
+
parseInt(pageParams[keys.perPage], 10) || uiSettings.perPage,
|
26
|
+
};
|
27
|
+
};
|
28
|
+
|
29
|
+
export const pageToVars = (
|
30
|
+
pagination,
|
31
|
+
keys = { page: 'page', perPage: 'perPage' }
|
32
|
+
) => ({
|
33
|
+
first: pagination[keys.page] * pagination[keys.perPage],
|
34
|
+
last: pagination[keys.perPage],
|
35
|
+
});
|
36
|
+
|
37
|
+
export const useParamsToVars = (
|
38
|
+
history,
|
39
|
+
keys = { page: 'page', perPage: 'perPage' }
|
40
|
+
) => pageToVars(useCurrentPagination(history, keys), keys);
|
@@ -0,0 +1,9 @@
|
|
1
|
+
import { addSearch } from './pageParamsHelper';
|
2
|
+
|
3
|
+
export const preparePerPageOptions = opts =>
|
4
|
+
opts.map(item => ({ title: item.toString(), value: item }));
|
5
|
+
|
6
|
+
export const refreshPage = (history, params = {}) => {
|
7
|
+
const url = addSearch(history.location.pathname, params);
|
8
|
+
history.push(url);
|
9
|
+
};
|
@@ -0,0 +1,58 @@
|
|
1
|
+
import { translate as __, sprintf } from 'foremanReact/common/I18n';
|
2
|
+
|
3
|
+
export const permissionCheck = (user, permissionsRequired) => {
|
4
|
+
if (permissionsRequired.length === 0) {
|
5
|
+
return { allowed: true };
|
6
|
+
}
|
7
|
+
|
8
|
+
if (!user) {
|
9
|
+
throw new Error(
|
10
|
+
'No user data when loading the page - cannot determine if current user is allowed to view the page.'
|
11
|
+
);
|
12
|
+
}
|
13
|
+
|
14
|
+
if (user.admin) {
|
15
|
+
return { allowed: true };
|
16
|
+
}
|
17
|
+
|
18
|
+
const permList = permissionsRequired.reduce((memo, item) => {
|
19
|
+
const found = user.permissions.nodes.find(
|
20
|
+
permission => permission.name === item
|
21
|
+
);
|
22
|
+
memo.push({ name: item, present: !!found });
|
23
|
+
return memo;
|
24
|
+
}, []);
|
25
|
+
|
26
|
+
if (permList.reduce((memo, item) => memo && item.present, true)) {
|
27
|
+
return { allowed: true, permissions: permList };
|
28
|
+
}
|
29
|
+
|
30
|
+
return { allowed: false, permissions: permList };
|
31
|
+
};
|
32
|
+
|
33
|
+
export const permissionDeniedMsg = permissions => {
|
34
|
+
let msg = __('You are not authorized to view the page. ');
|
35
|
+
if (permissions?.length > 0) {
|
36
|
+
msg += sprintf(
|
37
|
+
__('Request the following permissions from administrator: %s.'),
|
38
|
+
permissions.join(', ')
|
39
|
+
);
|
40
|
+
}
|
41
|
+
return msg;
|
42
|
+
};
|
43
|
+
|
44
|
+
export const allowPrimaryAction = (
|
45
|
+
emptyStateProps,
|
46
|
+
currentUser,
|
47
|
+
permissionsRequired
|
48
|
+
) => {
|
49
|
+
if (!permissionCheck(currentUser, permissionsRequired).allowed) {
|
50
|
+
return Object.keys(emptyStateProps)
|
51
|
+
.filter(key => key !== 'action')
|
52
|
+
.reduce((memo, key) => {
|
53
|
+
memo[key] = emptyStateProps[key];
|
54
|
+
return memo;
|
55
|
+
}, {});
|
56
|
+
}
|
57
|
+
return emptyStateProps;
|
58
|
+
};
|
@@ -0,0 +1,63 @@
|
|
1
|
+
import { toVars } from '../../../components/AnsibleHostDetail/components/JobsTab/NewRecurringJobHelper';
|
2
|
+
|
3
|
+
import {
|
4
|
+
scheduledJobsSearch,
|
5
|
+
previousJobsSearch,
|
6
|
+
} from '../../../components/AnsibleHostDetail/components/JobsTab/JobsTabHelper';
|
7
|
+
import {
|
8
|
+
jobInvocationsMockFactory,
|
9
|
+
jobCreateMockFactory,
|
10
|
+
firstJob,
|
11
|
+
secondJob,
|
12
|
+
futureDate,
|
13
|
+
} from '../../../components/AnsibleHostDetail/components/JobsTab/__test__/JobsTab.fixtures';
|
14
|
+
|
15
|
+
import { admin } from '../../../testHelper';
|
16
|
+
|
17
|
+
export const hgId = 22;
|
18
|
+
export const matchMock = {
|
19
|
+
params: {
|
20
|
+
id: '22',
|
21
|
+
},
|
22
|
+
};
|
23
|
+
export { futureDate };
|
24
|
+
|
25
|
+
const emptyScheduledJobsMock = jobInvocationsMockFactory(
|
26
|
+
{ search: scheduledJobsSearch('hostgroup', hgId) },
|
27
|
+
{ nodes: [], totalCount: 0 },
|
28
|
+
{ currentUser: admin }
|
29
|
+
);
|
30
|
+
const emptyScheduledJobsRefetchMock = jobInvocationsMockFactory(
|
31
|
+
{ search: scheduledJobsSearch('hostgroup', hgId) },
|
32
|
+
{ nodes: [], totalCount: 0 },
|
33
|
+
{ refetchData: { nodes: [firstJob], totalCount: 1 }, currentUser: admin }
|
34
|
+
);
|
35
|
+
const emptyPreviousJobsMock = jobInvocationsMockFactory(
|
36
|
+
{ search: previousJobsSearch('hostgroup', hgId), first: 20, last: 20 },
|
37
|
+
{ nodes: [], totalCount: 0 },
|
38
|
+
{ currentUser: admin }
|
39
|
+
);
|
40
|
+
const scheduledJobsMocks = jobInvocationsMockFactory(
|
41
|
+
{ search: scheduledJobsSearch('hostgroup', hgId) },
|
42
|
+
{ nodes: [firstJob], totalCount: 1 },
|
43
|
+
{ currentUser: admin }
|
44
|
+
);
|
45
|
+
const previousJobsMocks = jobInvocationsMockFactory(
|
46
|
+
{ search: previousJobsSearch('hostgroup', hgId), first: 20, last: 20 },
|
47
|
+
{ nodes: [secondJob], totalCount: 1 },
|
48
|
+
{ currentUser: admin }
|
49
|
+
);
|
50
|
+
|
51
|
+
export const emptyMocks = emptyScheduledJobsMock.concat(emptyPreviousJobsMock);
|
52
|
+
export const scheduledAndPreviousMocks = scheduledJobsMocks.concat(
|
53
|
+
previousJobsMocks
|
54
|
+
);
|
55
|
+
|
56
|
+
const createJobMock = jobCreateMockFactory(
|
57
|
+
toVars('hostgroup', hgId, futureDate, 'weekly').variables,
|
58
|
+
{ jobInvocation: { id: 'MDE6Sm9iSW52b2NhdGlvbi00MTU=' }, errors: [] }
|
59
|
+
);
|
60
|
+
|
61
|
+
export const createMocks = emptyScheduledJobsRefetchMock
|
62
|
+
.concat(emptyPreviousJobsMock)
|
63
|
+
.concat(createJobMock);
|
@@ -0,0 +1,112 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
3
|
+
import userEvent from '@testing-library/user-event';
|
4
|
+
import '@testing-library/jest-dom';
|
5
|
+
|
6
|
+
import { i18nProviderWrapperFactory } from 'foremanReact/common/i18nProviderWrapperFactory';
|
7
|
+
|
8
|
+
import {
|
9
|
+
emptyMocks,
|
10
|
+
scheduledAndPreviousMocks,
|
11
|
+
createMocks,
|
12
|
+
matchMock,
|
13
|
+
futureDate,
|
14
|
+
} from './HostgroupJobs.fixtures';
|
15
|
+
import HostgroupJobs from '../';
|
16
|
+
import * as toasts from '../../../toastHelper';
|
17
|
+
|
18
|
+
import {
|
19
|
+
tick,
|
20
|
+
withRouter,
|
21
|
+
withMockedProvider,
|
22
|
+
withRedux,
|
23
|
+
historyMock,
|
24
|
+
} from '../../../testHelper';
|
25
|
+
|
26
|
+
import { toCron } from '../../../components/AnsibleHostDetail/components/JobsTab/NewRecurringJobHelper';
|
27
|
+
|
28
|
+
const TestComponent = withRedux(withRouter(withMockedProvider(HostgroupJobs)));
|
29
|
+
|
30
|
+
const now = new Date('2021-08-28 00:00:00 -1100');
|
31
|
+
const ComponentWithIntl = i18nProviderWrapperFactory(now, 'UTC')(TestComponent);
|
32
|
+
|
33
|
+
describe('HostgroupJobs', () => {
|
34
|
+
it('should load the page', async () => {
|
35
|
+
render(
|
36
|
+
<ComponentWithIntl
|
37
|
+
match={matchMock}
|
38
|
+
mocks={scheduledAndPreviousMocks}
|
39
|
+
history={historyMock}
|
40
|
+
/>
|
41
|
+
);
|
42
|
+
await waitFor(tick);
|
43
|
+
await waitFor(tick);
|
44
|
+
screen
|
45
|
+
.getAllByText('Run Ansible roles')
|
46
|
+
.map(element => expect(element).toBeInTheDocument());
|
47
|
+
expect(screen.getByText('Scheduled recurring jobs')).toBeInTheDocument();
|
48
|
+
expect(screen.getByText('Previously executed jobs')).toBeInTheDocument();
|
49
|
+
expect(screen.getByText('54 10 15 * *')).toBeInTheDocument();
|
50
|
+
});
|
51
|
+
it('should show empty state', async () => {
|
52
|
+
render(
|
53
|
+
<ComponentWithIntl
|
54
|
+
match={matchMock}
|
55
|
+
mocks={emptyMocks}
|
56
|
+
history={historyMock}
|
57
|
+
/>
|
58
|
+
);
|
59
|
+
await waitFor(tick);
|
60
|
+
await waitFor(tick);
|
61
|
+
expect(
|
62
|
+
screen.getByText('No config job for Ansible roles scheduled')
|
63
|
+
).toBeInTheDocument();
|
64
|
+
});
|
65
|
+
it('should create new recurring job', async () => {
|
66
|
+
const showToast = jest.fn();
|
67
|
+
jest.spyOn(toasts, 'showToast').mockImplementation(showToast);
|
68
|
+
|
69
|
+
render(
|
70
|
+
<ComponentWithIntl
|
71
|
+
match={matchMock}
|
72
|
+
mocks={createMocks}
|
73
|
+
history={historyMock}
|
74
|
+
/>
|
75
|
+
);
|
76
|
+
await waitFor(tick);
|
77
|
+
userEvent.click(
|
78
|
+
screen.getByRole('button', { name: 'schedule recurring job' })
|
79
|
+
);
|
80
|
+
await waitFor(tick);
|
81
|
+
userEvent.selectOptions(screen.getByLabelText(/repeat/), 'weekly');
|
82
|
+
userEvent.type(
|
83
|
+
screen.getByLabelText(/startTime/),
|
84
|
+
futureDate
|
85
|
+
.toISOString()
|
86
|
+
.split('T')[1]
|
87
|
+
.slice(0, 5)
|
88
|
+
);
|
89
|
+
userEvent.type(
|
90
|
+
screen.getByLabelText(/startDate/),
|
91
|
+
futureDate.toISOString().split('T')[0]
|
92
|
+
);
|
93
|
+
expect(
|
94
|
+
screen.getByRole('button', { name: 'submit creating job' })
|
95
|
+
).not.toBeDisabled();
|
96
|
+
userEvent.click(
|
97
|
+
screen.getByRole('button', { name: 'submit creating job' })
|
98
|
+
);
|
99
|
+
await waitFor(tick);
|
100
|
+
await waitFor(tick);
|
101
|
+
await waitFor(tick);
|
102
|
+
expect(showToast).toHaveBeenCalledWith({
|
103
|
+
type: 'success',
|
104
|
+
message: 'Ansible job was successfully created.',
|
105
|
+
});
|
106
|
+
expect(screen.getByText(toCron(futureDate, 'weekly'))).toBeInTheDocument();
|
107
|
+
expect(screen.getByText('in 3 days')).toBeInTheDocument();
|
108
|
+
expect(
|
109
|
+
screen.queryByText('No config job for Ansible roles scheduled')
|
110
|
+
).not.toBeInTheDocument();
|
111
|
+
});
|
112
|
+
});
|
@@ -0,0 +1,26 @@
|
|
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 JobsPage from '../../components/AnsibleHostDetail/components/JobsTab';
|
7
|
+
|
8
|
+
const HostgroupJobs = props => (
|
9
|
+
<React.Fragment>
|
10
|
+
<Helmet>
|
11
|
+
<title>{__('Configure Recurring Job')}</title>
|
12
|
+
</Helmet>
|
13
|
+
<JobsPage
|
14
|
+
resourceName="hostgroup"
|
15
|
+
resourceId={parseInt(props.match.params.id, 10)}
|
16
|
+
history={props.history}
|
17
|
+
/>
|
18
|
+
</React.Fragment>
|
19
|
+
);
|
20
|
+
|
21
|
+
HostgroupJobs.propTypes = {
|
22
|
+
match: PropTypes.object.isRequired,
|
23
|
+
history: PropTypes.object.isRequired,
|
24
|
+
};
|
25
|
+
|
26
|
+
export default HostgroupJobs;
|
@@ -0,0 +1,165 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { Provider } from 'react-redux';
|
3
|
+
import { MockedProvider } from '@apollo/react-testing';
|
4
|
+
import { Router, MemoryRouter } from 'react-router-dom';
|
5
|
+
import { createMemoryHistory } from 'history';
|
6
|
+
|
7
|
+
import store from 'foremanReact/redux';
|
8
|
+
import ConfirmModal from 'foremanReact/components/ConfirmModal';
|
9
|
+
import { getForemanContext } from 'foremanReact/Root/Context/ForemanContext';
|
10
|
+
|
11
|
+
export const withRedux = Component => props => (
|
12
|
+
<Provider store={store}>
|
13
|
+
<Component {...props} />
|
14
|
+
<ConfirmModal />
|
15
|
+
</Provider>
|
16
|
+
);
|
17
|
+
|
18
|
+
export const withRouter = Component => props => (
|
19
|
+
<MemoryRouter>
|
20
|
+
<Component {...props} />
|
21
|
+
</MemoryRouter>
|
22
|
+
);
|
23
|
+
|
24
|
+
export const withReactRouter = Component => props => {
|
25
|
+
// eslint-disable-next-line react/prop-types
|
26
|
+
const history = props.history || createMemoryHistory();
|
27
|
+
|
28
|
+
return (
|
29
|
+
<Router history={history}>
|
30
|
+
<Component {...props} history={history} />
|
31
|
+
</Router>
|
32
|
+
);
|
33
|
+
};
|
34
|
+
|
35
|
+
export const withMockedProvider = Component => props => {
|
36
|
+
const ForemanContext = getForemanContext(ctx);
|
37
|
+
// eslint-disable-next-line react/prop-types
|
38
|
+
const { mocks, ...rest } = props;
|
39
|
+
|
40
|
+
const ctx = {
|
41
|
+
metadata: {
|
42
|
+
UISettings: {
|
43
|
+
perPage: 20,
|
44
|
+
},
|
45
|
+
},
|
46
|
+
};
|
47
|
+
|
48
|
+
return (
|
49
|
+
<ForemanContext.Provider value={ctx}>
|
50
|
+
<MockedProvider mocks={mocks}>
|
51
|
+
<Component {...rest} />
|
52
|
+
</MockedProvider>
|
53
|
+
</ForemanContext.Provider>
|
54
|
+
);
|
55
|
+
};
|
56
|
+
|
57
|
+
export const userFactory = (login, permissions = []) => ({
|
58
|
+
__typename: 'User',
|
59
|
+
id: 'MDE6VXNlci01',
|
60
|
+
login,
|
61
|
+
admin: false,
|
62
|
+
permissions: {
|
63
|
+
nodes: permissions,
|
64
|
+
},
|
65
|
+
});
|
66
|
+
|
67
|
+
export const admin = {
|
68
|
+
__typename: 'User',
|
69
|
+
id: 'MDE6VXNlci00',
|
70
|
+
login: 'admin',
|
71
|
+
admin: true,
|
72
|
+
permissions: {
|
73
|
+
nodes: [],
|
74
|
+
},
|
75
|
+
};
|
76
|
+
|
77
|
+
export const intruder = userFactory('intruder', [
|
78
|
+
{
|
79
|
+
__typename: 'Permission',
|
80
|
+
id: 'MDE6UGVybWlzc2lvbi0x',
|
81
|
+
name: 'view_architectures',
|
82
|
+
},
|
83
|
+
]);
|
84
|
+
|
85
|
+
// use to resolve async mock requests for apollo MockedProvider
|
86
|
+
export const tick = () => new Promise(resolve => setTimeout(resolve, 0));
|
87
|
+
|
88
|
+
export const historyMock = {
|
89
|
+
location: {
|
90
|
+
search: '',
|
91
|
+
},
|
92
|
+
};
|
93
|
+
|
94
|
+
export const mockFactory = (resultName, query) => (
|
95
|
+
variables,
|
96
|
+
modelResults,
|
97
|
+
{ errors = [], currentUser = null, refetchData = null } = {}
|
98
|
+
) => {
|
99
|
+
let called = false;
|
100
|
+
|
101
|
+
const returnData = results => {
|
102
|
+
const result = {
|
103
|
+
data: {
|
104
|
+
[resultName]: results,
|
105
|
+
},
|
106
|
+
};
|
107
|
+
|
108
|
+
if (errors.length !== 0) {
|
109
|
+
result.errors = errors;
|
110
|
+
}
|
111
|
+
|
112
|
+
if (currentUser) {
|
113
|
+
result.data.currentUser = currentUser;
|
114
|
+
}
|
115
|
+
|
116
|
+
return result;
|
117
|
+
};
|
118
|
+
|
119
|
+
const mock = {
|
120
|
+
request: {
|
121
|
+
query,
|
122
|
+
variables,
|
123
|
+
},
|
124
|
+
newData: () => {
|
125
|
+
if (called && refetchData) {
|
126
|
+
return returnData(refetchData);
|
127
|
+
}
|
128
|
+
called = true;
|
129
|
+
return returnData(modelResults);
|
130
|
+
},
|
131
|
+
};
|
132
|
+
|
133
|
+
return [mock];
|
134
|
+
};
|
135
|
+
|
136
|
+
export const advancedMockFactory = query => (
|
137
|
+
variables,
|
138
|
+
data,
|
139
|
+
{ errors = [], currentUser = null, refetchData = null } = {}
|
140
|
+
) => {
|
141
|
+
let called = false;
|
142
|
+
|
143
|
+
const mock = {
|
144
|
+
request: {
|
145
|
+
query,
|
146
|
+
variables,
|
147
|
+
},
|
148
|
+
newData: () => {
|
149
|
+
if (called && refetchData) {
|
150
|
+
return { data: refetchData };
|
151
|
+
}
|
152
|
+
called = true;
|
153
|
+
return { data };
|
154
|
+
},
|
155
|
+
};
|
156
|
+
|
157
|
+
if (errors.length !== 0) {
|
158
|
+
mock.result.errors = errors;
|
159
|
+
}
|
160
|
+
|
161
|
+
if (currentUser) {
|
162
|
+
mock.result.data.currentUser = currentUser;
|
163
|
+
}
|
164
|
+
return [mock];
|
165
|
+
};
|