foreman-tasks 12.0.0 → 12.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ruby_tests.yml +1 -0
- data/app/assets/javascripts/foreman-tasks/locale/de/foreman_tasks.js +58 -22
- data/app/assets/javascripts/foreman-tasks/locale/en/foreman_tasks.js +56 -20
- data/app/assets/javascripts/foreman-tasks/locale/es/foreman_tasks.js +59 -23
- data/app/assets/javascripts/foreman-tasks/locale/fr/foreman_tasks.js +61 -25
- data/app/assets/javascripts/foreman-tasks/locale/ja/foreman_tasks.js +61 -25
- data/app/assets/javascripts/foreman-tasks/locale/ka/foreman_tasks.js +60 -24
- data/app/assets/javascripts/foreman-tasks/locale/ko/foreman_tasks.js +61 -25
- data/app/assets/javascripts/foreman-tasks/locale/pt_BR/foreman_tasks.js +59 -23
- data/app/assets/javascripts/foreman-tasks/locale/ru/foreman_tasks.js +58 -22
- data/app/assets/javascripts/foreman-tasks/locale/zh_CN/foreman_tasks.js +61 -25
- data/app/assets/javascripts/foreman-tasks/locale/zh_TW/foreman_tasks.js +57 -21
- data/app/controllers/foreman_tasks/api/tasks_controller.rb +4 -19
- data/app/controllers/foreman_tasks/tasks_controller.rb +4 -5
- data/app/models/foreman_tasks/task.rb +1 -1
- data/app/views/foreman_tasks/api/tasks/dependency_summary.json.rabl +1 -3
- data/app/views/foreman_tasks/api/tasks/show.json.rabl +4 -1
- data/config/routes.rb +3 -3
- data/foreman-tasks.gemspec +3 -1
- data/lib/foreman_tasks/engine.rb +1 -1
- data/lib/foreman_tasks/tasks/export_tasks.rake +1 -1
- data/lib/foreman_tasks/version.rb +1 -1
- data/locale/de/LC_MESSAGES/foreman_tasks.mo +0 -0
- data/locale/de/foreman_tasks.po +59 -23
- data/locale/en/LC_MESSAGES/foreman_tasks.mo +0 -0
- data/locale/en/foreman_tasks.po +56 -20
- data/locale/es/LC_MESSAGES/foreman_tasks.mo +0 -0
- data/locale/es/foreman_tasks.po +59 -23
- data/locale/foreman_tasks.pot +173 -100
- data/locale/fr/LC_MESSAGES/foreman_tasks.mo +0 -0
- data/locale/fr/foreman_tasks.po +61 -25
- data/locale/ja/LC_MESSAGES/foreman_tasks.mo +0 -0
- data/locale/ja/foreman_tasks.po +61 -25
- data/locale/ka/LC_MESSAGES/foreman_tasks.mo +0 -0
- data/locale/ka/foreman_tasks.po +60 -24
- data/locale/ko/LC_MESSAGES/foreman_tasks.mo +0 -0
- data/locale/ko/foreman_tasks.po +61 -25
- data/locale/pt_BR/LC_MESSAGES/foreman_tasks.mo +0 -0
- data/locale/pt_BR/foreman_tasks.po +59 -23
- data/locale/ru/LC_MESSAGES/foreman_tasks.mo +0 -0
- data/locale/ru/foreman_tasks.po +58 -22
- data/locale/zh_CN/LC_MESSAGES/foreman_tasks.mo +0 -0
- data/locale/zh_CN/foreman_tasks.po +61 -25
- data/locale/zh_TW/LC_MESSAGES/foreman_tasks.mo +0 -0
- data/locale/zh_TW/foreman_tasks.po +57 -21
- data/test/controllers/api/tasks_controller_test.rb +29 -3
- data/test/controllers/tasks_controller_test.rb +46 -2
- data/webpack/ForemanTasks/Components/TaskActions/TaskAction.test.js +8 -0
- data/webpack/ForemanTasks/Components/TaskActions/TaskActionHelpers.js +8 -2
- data/webpack/ForemanTasks/Components/TaskActions/TaskActionHelpers.test.js +25 -33
- data/webpack/ForemanTasks/Components/TaskActions/index.js +24 -3
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/TaskInfo.test.js.snap +6 -6
- data/webpack/ForemanTasks/Components/TaskDetails/__tests__/TaskDetailsActions.test.js +9 -0
- data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/TaskDetailsActions.test.js.snap +16 -11
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/TasksTimeRow.js +2 -3
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/__snapshots__/TasksTimeRow.test.js.snap +4 -8
- data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboard.js +2 -4
- data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboard.scss +0 -3
- data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/__snapshots__/TasksDashboard.test.js.snap +2 -5
- data/webpack/ForemanTasks/Components/{common/ActionButtons/ActionButton.js → TasksTable/Components/CellActionButton.js} +36 -21
- data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/index.test.js +32 -29
- data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/createBulkTaskModal.js +14 -18
- data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/createTaskModal.js +13 -15
- data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/index.js +7 -7
- data/webpack/ForemanTasks/Components/TasksTable/TasksBulkActions.js +16 -23
- data/webpack/ForemanTasks/Components/TasksTable/TasksColumns.js +56 -0
- data/webpack/ForemanTasks/Components/TasksTable/TasksIndexPage.js +277 -3
- data/webpack/ForemanTasks/Components/TasksTable/TasksModals.js +96 -0
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableConstants.js +10 -18
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableHelpers.js +3 -3
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksBulkActions.test.js +130 -63
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksIndexPage.test.js +315 -8
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTable.fixtures.js +214 -41
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableHelpers.test.js +20 -12
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksBulkActions.test.js.snap +18 -35
- data/webpack/ForemanTasks/ForemanTasksReducers.js +0 -4
- data/webpack/Routes/routes.js +22 -0
- data/webpack/Routes/routes.test.js +95 -0
- data/webpack/global_index.js +10 -0
- data/webpack/index.js +0 -18
- data/webpack/test_setup.js +1 -0
- metadata +15 -89
- data/app/controllers/foreman_tasks/react_controller.rb +0 -17
- data/app/views/foreman_tasks/layouts/react.html.erb +0 -13
- data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/ConfirmModalSelectors.js +0 -30
- data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/ConfirmModalSelectors.test.js +0 -66
- data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/__snapshots__/ConfirmModalSelectors.test.js.snap +0 -33
- data/webpack/ForemanTasks/Components/TasksTable/Components/SelectAllAlert.js +0 -49
- data/webpack/ForemanTasks/Components/TasksTable/Components/TableSelectionCell.js +0 -32
- data/webpack/ForemanTasks/Components/TasksTable/Components/TableSelectionHeaderCell.js +0 -38
- data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/SelectAllAlert.test.js +0 -29
- data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/TableSelectionCell.test.js +0 -15
- data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/TableSelectionHeaderCell.test.js +0 -15
- data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/__snapshots__/SelectAllAlert.test.js.snap +0 -81
- data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/__snapshots__/TableSelectionCell.test.js.snap +0 -14
- data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/__snapshots__/TableSelectionHeaderCell.test.js.snap +0 -15
- data/webpack/ForemanTasks/Components/TasksTable/SubTasksPage.js +0 -40
- data/webpack/ForemanTasks/Components/TasksTable/TasksTable.js +0 -163
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableActions.js +0 -108
- data/webpack/ForemanTasks/Components/TasksTable/TasksTablePage.js +0 -234
- data/webpack/ForemanTasks/Components/TasksTable/TasksTablePage.scss +0 -20
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableReducer.js +0 -68
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableSchema.js +0 -85
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableSelectors.js +0 -63
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/SubTasksPage.test.js +0 -20
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTable.test.js +0 -9
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableActions.test.js +0 -65
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTablePage.test.js +0 -35
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableReducer.test.js +0 -87
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/SubTasksPage.test.js.snap +0 -48
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksIndexPage.test.js.snap +0 -39
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTable.test.js.snap +0 -52
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTableActions.test.js.snap +0 -40
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTablePage.test.js.snap +0 -368
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTableReducer.test.js.snap +0 -116
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/__snapshots__/actionCellFormatter.test.js.snap +0 -15
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/__snapshots__/actionNameCellFormatter.test.js.snap +0 -10
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/__snapshots__/dateCellFormmatter.test.js.snap +0 -9
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/__snapshots__/durationCellFormmatter.test.js.snap +0 -18
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/__snapshots__/selectionCellFormatter.test.js.snap +0 -12
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/__snapshots__/selectionHeaderCellFormatter.test.js.snap +0 -11
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/actionCellFormatter.test.js +0 -11
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/actionNameCellFormatter.test.js +0 -8
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/dateCellFormmatter.test.js +0 -7
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/durationCellFormmatter.test.js +0 -12
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/selectionCellFormatter.test.js +0 -12
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/selectionHeaderCellFormatter.test.js +0 -12
- data/webpack/ForemanTasks/Components/TasksTable/formatters/actionCellFormatter.js +0 -19
- data/webpack/ForemanTasks/Components/TasksTable/formatters/actionNameCellFormatter.js +0 -9
- data/webpack/ForemanTasks/Components/TasksTable/formatters/dateCellFormmatter.js +0 -7
- data/webpack/ForemanTasks/Components/TasksTable/formatters/durationCellFormmatter.js +0 -7
- data/webpack/ForemanTasks/Components/TasksTable/formatters/index.js +0 -7
- data/webpack/ForemanTasks/Components/TasksTable/formatters/selectionCellFormatter.js +0 -17
- data/webpack/ForemanTasks/Components/TasksTable/formatters/selectionHeaderCellFormatter.js +0 -11
- data/webpack/ForemanTasks/Components/TasksTable/index.js +0 -42
- data/webpack/ForemanTasks/Components/common/ActionButtons/ActionButton.test.js +0 -101
- data/webpack/ForemanTasks/Components/common/ActionButtons/__snapshots__/ActionButton.test.js.snap +0 -95
- data/webpack/ForemanTasks/ForemanTasks.js +0 -11
- data/webpack/ForemanTasks/ForemanTasks.test.js +0 -10
- data/webpack/ForemanTasks/Routes/ForemanTasksRouter.js +0 -14
- data/webpack/ForemanTasks/Routes/ForemanTasksRouter.test.js +0 -26
- data/webpack/ForemanTasks/Routes/ForemanTasksRoutes.js +0 -23
- data/webpack/ForemanTasks/Routes/ForemanTasksRoutes.test.js +0 -16
- data/webpack/ForemanTasks/Routes/__snapshots__/ForemanTasksRouter.test.js.snap +0 -16
- data/webpack/ForemanTasks/Routes/__snapshots__/ForemanTasksRoutes.test.js.snap +0 -37
- data/webpack/ForemanTasks/__snapshots__/ForemanTasks.test.js.snap +0 -7
- data/webpack/ForemanTasks/index.js +0 -1
- data/webpack/__mocks__/foremanReact/common/I18n.js +0 -7
- data/webpack/__mocks__/foremanReact/common/helpers.js +0 -6
- data/webpack/__mocks__/foremanReact/common/urlHelpers.js +0 -1
- data/webpack/__mocks__/foremanReact/components/Layout/LayoutActions.js +0 -2
- data/webpack/__mocks__/foremanReact/components/Pagination/index.js +0 -2
- data/webpack/__mocks__/foremanReact/components/ToastsList/index.js +0 -8
- data/webpack/__mocks__/foremanReact/components/common/ActionButtons/ActionButtons.js +0 -3
- data/webpack/__mocks__/foremanReact/components/common/MessageBox.js +0 -4
- data/webpack/__mocks__/foremanReact/components/common/dates/LongDateTime.js +0 -5
- data/webpack/__mocks__/foremanReact/components/common/dates/RelativeDateTime.js +0 -3
- data/webpack/__mocks__/foremanReact/components/common/table/actionsHelpers/actionTypeCreator.js +0 -7
- data/webpack/__mocks__/foremanReact/components/common/table.js +0 -5
- data/webpack/__mocks__/foremanReact/constants.js +0 -24
- data/webpack/__mocks__/foremanReact/redux/API/APISelectors.js +0 -10
- data/webpack/__mocks__/foremanReact/redux/API/index.js +0 -10
- data/webpack/__mocks__/foremanReact/redux/middlewares/IntervalMiddleware.js +0 -5
- data/webpack/__mocks__/foremanReact/routes/common/PageLayout/PageLayout.js +0 -10
- data/webpack/__mocks__/foremanReact/routes/common/PageLayout/components/ExportButton/ExportButton.js +0 -5
|
@@ -15,96 +15,87 @@ jest.mock('foremanReact/components/common/table', () => ({
|
|
|
15
15
|
|
|
16
16
|
jest.mock('foremanReact/redux/API');
|
|
17
17
|
|
|
18
|
+
jest.mock('foremanReact/components/ToastsList', () => ({
|
|
19
|
+
addToast: toast => ({
|
|
20
|
+
type: 'TOASTS_ADD',
|
|
21
|
+
payload: {
|
|
22
|
+
message: toast,
|
|
23
|
+
},
|
|
24
|
+
}),
|
|
25
|
+
}));
|
|
26
|
+
|
|
18
27
|
const task = {
|
|
19
28
|
id: 'some-id',
|
|
20
29
|
name: 'some-name',
|
|
21
|
-
|
|
30
|
+
action: 'some-action',
|
|
31
|
+
can_edit: true,
|
|
22
32
|
};
|
|
23
33
|
|
|
24
34
|
const fixtures = {
|
|
25
35
|
'handles bulkResumeById requests that fail': () => {
|
|
26
|
-
const selected = [{ ...task,
|
|
36
|
+
const selected = [{ ...task, available_actions: { resumable: true } }];
|
|
27
37
|
|
|
28
38
|
API.post.mockImplementation(() =>
|
|
29
39
|
Promise.reject(new Error('Network Error'))
|
|
30
40
|
);
|
|
31
|
-
return bulkResumeById({ selected,
|
|
32
|
-
},
|
|
33
|
-
'handles resumable bulkResumeById requests': () => {
|
|
34
|
-
const selected = [{ ...task, isResumable: true }];
|
|
35
|
-
|
|
36
|
-
API.post.mockImplementation(() => ({
|
|
37
|
-
data: {
|
|
38
|
-
resumed: [{ action: 'I am resumed' }],
|
|
39
|
-
failed: [{ action: 'I am failed' }],
|
|
40
|
-
},
|
|
41
|
-
}));
|
|
42
|
-
return bulkResumeById({ selected, url: 'some-url' });
|
|
43
|
-
},
|
|
44
|
-
'handles bulkCancelById requests': () => {
|
|
45
|
-
const selected = [{ ...task, isCancellable: true }];
|
|
46
|
-
|
|
47
|
-
API.post.mockImplementation(() => ({
|
|
48
|
-
data: {
|
|
49
|
-
cancelled: [{ action: 'I am cancelled' }],
|
|
50
|
-
},
|
|
51
|
-
}));
|
|
52
|
-
return bulkCancelById({ selected, url: 'some-url' });
|
|
41
|
+
return bulkResumeById({ selected, reloadPage: jest.fn() });
|
|
53
42
|
},
|
|
54
43
|
'handles skipped bulkResumeById requests': () => {
|
|
55
|
-
const selected = [{ ...task,
|
|
44
|
+
const selected = [{ ...task, available_actions: { resumable: true } }];
|
|
56
45
|
|
|
57
46
|
API.post.mockImplementation(() => ({
|
|
58
47
|
data: {
|
|
59
48
|
skipped: [{ action: 'I am skipped' }],
|
|
60
49
|
},
|
|
61
50
|
}));
|
|
62
|
-
return bulkResumeById({ selected,
|
|
51
|
+
return bulkResumeById({ selected, reloadPage: jest.fn() });
|
|
63
52
|
},
|
|
64
53
|
'handles skipped bulkCancelById requests': () => {
|
|
65
|
-
const selected = [{ ...task,
|
|
66
|
-
|
|
54
|
+
const selected = [{ ...task, available_actions: { cancellable: true } }];
|
|
67
55
|
API.post.mockImplementation(() => ({
|
|
68
56
|
data: {
|
|
69
57
|
skipped: [{ action: 'I am skipped' }],
|
|
70
58
|
},
|
|
71
59
|
}));
|
|
72
|
-
return bulkCancelById({ selected,
|
|
60
|
+
return bulkCancelById({ selected, reloadPage: jest.fn() });
|
|
73
61
|
},
|
|
74
|
-
|
|
75
|
-
'handles bulkForceCancelById requests': () => {
|
|
76
|
-
const selected = [{ ...task, isCancellable: true }];
|
|
77
|
-
|
|
78
|
-
API.post.mockImplementation(() => ({
|
|
79
|
-
data: {
|
|
80
|
-
stopped_length: 2,
|
|
81
|
-
skipped_length: 4,
|
|
82
|
-
},
|
|
83
|
-
}));
|
|
84
|
-
return bulkForceCancelById({ selected, url: 'some-url' });
|
|
85
|
-
},
|
|
86
|
-
|
|
87
62
|
'handles bulkForceCancelById requests that fail': () => {
|
|
88
|
-
const selected = [
|
|
63
|
+
const selected = [
|
|
64
|
+
{
|
|
65
|
+
...task,
|
|
66
|
+
state: 'running',
|
|
67
|
+
can_edit: true,
|
|
68
|
+
},
|
|
69
|
+
];
|
|
89
70
|
|
|
90
71
|
API.post.mockImplementation(() =>
|
|
91
72
|
Promise.reject(new Error('Network Error'))
|
|
92
73
|
);
|
|
93
|
-
return bulkForceCancelById({
|
|
74
|
+
return bulkForceCancelById({
|
|
75
|
+
selected,
|
|
76
|
+
reloadPage: jest.fn(),
|
|
77
|
+
});
|
|
94
78
|
},
|
|
95
79
|
|
|
96
80
|
'handles bulkCancelById requests that are not cancellable': () => {
|
|
97
|
-
const selected = [{ ...task,
|
|
98
|
-
return bulkCancelById({ selected,
|
|
81
|
+
const selected = [{ ...task, available_actions: { cancellable: false } }];
|
|
82
|
+
return bulkCancelById({ selected, reloadPage: jest.fn() });
|
|
99
83
|
},
|
|
100
84
|
'handles bulkResumeById requests that are not resumable': () => {
|
|
101
|
-
const selected = [
|
|
102
|
-
|
|
85
|
+
const selected = [
|
|
86
|
+
{ ...task, available_actions: { resumable: false, cancellable: false } },
|
|
87
|
+
];
|
|
88
|
+
return bulkResumeById({ selected, reloadPage: jest.fn() });
|
|
103
89
|
},
|
|
104
90
|
|
|
105
91
|
'handles bulkForceCancelById requests that are stopped': () => {
|
|
106
|
-
const selected = [
|
|
107
|
-
|
|
92
|
+
const selected = [
|
|
93
|
+
{ ...task, available_actions: { resumable: false }, state: 'stopped' },
|
|
94
|
+
];
|
|
95
|
+
return bulkForceCancelById({
|
|
96
|
+
selected,
|
|
97
|
+
reloadPage: jest.fn(),
|
|
98
|
+
});
|
|
108
99
|
},
|
|
109
100
|
|
|
110
101
|
'handles bulkCancelBySearch requests': () => {
|
|
@@ -115,8 +106,7 @@ const fixtures = {
|
|
|
115
106
|
},
|
|
116
107
|
}));
|
|
117
108
|
return bulkCancelBySearch({
|
|
118
|
-
query:
|
|
119
|
-
url: 'some-url',
|
|
109
|
+
query: '',
|
|
120
110
|
parentTaskID: 'parent',
|
|
121
111
|
});
|
|
122
112
|
},
|
|
@@ -130,31 +120,108 @@ const fixtures = {
|
|
|
130
120
|
},
|
|
131
121
|
}));
|
|
132
122
|
return bulkResumeBySearch({
|
|
133
|
-
query:
|
|
134
|
-
url: 'some-url',
|
|
123
|
+
query: '',
|
|
135
124
|
parentTaskID: 'parent',
|
|
136
125
|
});
|
|
137
126
|
},
|
|
138
127
|
'handles bulkForceCancelBySearch requests': () =>
|
|
139
128
|
bulkForceCancelBySearch({
|
|
140
|
-
query:
|
|
141
|
-
url: 'some-url',
|
|
129
|
+
query: '',
|
|
142
130
|
parentTaskID: 'parent',
|
|
143
131
|
}),
|
|
144
132
|
'handles bulkCancelById requests with canEdit false': () => {
|
|
145
|
-
const selected = [
|
|
146
|
-
|
|
133
|
+
const selected = [
|
|
134
|
+
{
|
|
135
|
+
...task,
|
|
136
|
+
available_actions: { cancellable: true },
|
|
137
|
+
can_edit: false,
|
|
138
|
+
},
|
|
139
|
+
];
|
|
140
|
+
return bulkCancelById({ selected, reloadPage: jest.fn() });
|
|
147
141
|
},
|
|
148
142
|
'handles bulkResumeById requests with canEdit false': () => {
|
|
149
|
-
const selected = [
|
|
150
|
-
|
|
143
|
+
const selected = [
|
|
144
|
+
{
|
|
145
|
+
...task,
|
|
146
|
+
available_actions: { resumable: true },
|
|
147
|
+
can_edit: false,
|
|
148
|
+
},
|
|
149
|
+
];
|
|
150
|
+
return bulkResumeById({ selected, reloadPage: jest.fn() });
|
|
151
151
|
},
|
|
152
152
|
'handles bulkForceCancelById requests with canEdit false': () => {
|
|
153
|
-
const selected = [
|
|
154
|
-
|
|
153
|
+
const selected = [
|
|
154
|
+
{
|
|
155
|
+
...task,
|
|
156
|
+
state: 'running',
|
|
157
|
+
available_actions: { resumable: false },
|
|
158
|
+
can_edit: false,
|
|
159
|
+
},
|
|
160
|
+
];
|
|
161
|
+
return bulkForceCancelById({ selected, reloadPage: jest.fn() });
|
|
155
162
|
},
|
|
156
163
|
};
|
|
157
164
|
|
|
158
|
-
describe('
|
|
165
|
+
describe('TasksBulkActions', () => {
|
|
166
|
+
describe('Test reloadPage callback', () => {
|
|
167
|
+
it('handles resumable bulkResumeById requests', async () => {
|
|
168
|
+
const selected = [{ ...task, available_actions: { resumable: true } }];
|
|
169
|
+
|
|
170
|
+
API.post.mockImplementation(() => ({
|
|
171
|
+
data: {
|
|
172
|
+
resumed: [{ action: 'I am resumed' }],
|
|
173
|
+
failed: [{ action: 'I am failed' }],
|
|
174
|
+
},
|
|
175
|
+
}));
|
|
176
|
+
const reloadPage = jest.fn();
|
|
177
|
+
const action = bulkResumeById({
|
|
178
|
+
selected,
|
|
179
|
+
reloadPage,
|
|
180
|
+
});
|
|
181
|
+
const dispatch = jest.fn();
|
|
182
|
+
await action(dispatch);
|
|
183
|
+
expect(reloadPage).toHaveBeenCalled();
|
|
184
|
+
expect(dispatch.mock.calls).toMatchSnapshot();
|
|
185
|
+
});
|
|
186
|
+
it('handles bulkCancelById requests', async () => {
|
|
187
|
+
const selected = [{ ...task, available_actions: { cancellable: true } }];
|
|
188
|
+
API.post.mockImplementation(() => ({
|
|
189
|
+
data: {
|
|
190
|
+
cancelled: [{ action: 'I am cancelled' }],
|
|
191
|
+
},
|
|
192
|
+
}));
|
|
193
|
+
const reloadPage = jest.fn();
|
|
194
|
+
const action = bulkCancelById({ selected, reloadPage });
|
|
195
|
+
const dispatch = jest.fn();
|
|
196
|
+
await action(dispatch);
|
|
197
|
+
expect(reloadPage).toHaveBeenCalled();
|
|
198
|
+
expect(dispatch.mock.calls).toMatchSnapshot();
|
|
199
|
+
});
|
|
200
|
+
it('handles bulkForceCancelById requests', async () => {
|
|
201
|
+
const selected = [
|
|
202
|
+
{
|
|
203
|
+
...task,
|
|
204
|
+
state: 'running',
|
|
205
|
+
can_edit: true,
|
|
206
|
+
},
|
|
207
|
+
];
|
|
208
|
+
|
|
209
|
+
API.post.mockImplementation(() => ({
|
|
210
|
+
data: {
|
|
211
|
+
stopped_length: 2,
|
|
212
|
+
skipped_length: 4,
|
|
213
|
+
},
|
|
214
|
+
}));
|
|
215
|
+
const reloadPage = jest.fn();
|
|
216
|
+
const action = bulkForceCancelById({
|
|
217
|
+
selected,
|
|
218
|
+
reloadPage,
|
|
219
|
+
});
|
|
220
|
+
const dispatch = jest.fn();
|
|
221
|
+
await action(dispatch);
|
|
222
|
+
expect(reloadPage).toHaveBeenCalled();
|
|
223
|
+
expect(dispatch.mock.calls).toMatchSnapshot();
|
|
224
|
+
});
|
|
225
|
+
});
|
|
159
226
|
testActionSnapshotWithFixtures(fixtures);
|
|
160
227
|
});
|
|
@@ -1,12 +1,319 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
screen,
|
|
4
|
+
within,
|
|
5
|
+
fireEvent,
|
|
6
|
+
waitFor,
|
|
7
|
+
render,
|
|
8
|
+
} from '@testing-library/react';
|
|
9
|
+
import '@testing-library/jest-dom';
|
|
10
|
+
import { IntlProvider } from 'react-intl';
|
|
4
11
|
|
|
5
|
-
|
|
6
|
-
|
|
12
|
+
import { rtlHelpers } from 'foremanReact/common/testHelpers';
|
|
13
|
+
|
|
14
|
+
import TasksTableIndexPage from '../TasksIndexPage';
|
|
15
|
+
import { CellActionButton } from '../Components/CellActionButton';
|
|
16
|
+
import { RESUME_MODAL, FORCE_UNLOCK_MODAL } from '../TasksTableConstants';
|
|
17
|
+
import { tasksSuccessResponse } from './TasksTable.fixtures';
|
|
18
|
+
|
|
19
|
+
const { renderWithStore } = rtlHelpers;
|
|
20
|
+
|
|
21
|
+
jest.mock(
|
|
22
|
+
// C3 is causing issues in the test, and this test is not testing the DonutChart
|
|
23
|
+
'../../TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChart.js',
|
|
24
|
+
() => ({
|
|
25
|
+
__esModule: true,
|
|
26
|
+
default: () => <div data-testid="donut-chart" />,
|
|
27
|
+
})
|
|
28
|
+
);
|
|
29
|
+
const mockApiResponse = {
|
|
30
|
+
...tasksSuccessResponse,
|
|
31
|
+
setAPIOptions: jest.fn(),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
jest.mock('foremanReact/common/hooks/API/APIHooks', () => ({
|
|
35
|
+
useAPI: jest.fn(() => mockApiResponse),
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
const mockApiPost = jest.fn(() => Promise.resolve());
|
|
39
|
+
jest.mock('foremanReact/redux/API/API', () => ({
|
|
40
|
+
__esModule: true,
|
|
41
|
+
default: {
|
|
42
|
+
get: () => Promise.resolve({}),
|
|
43
|
+
put: () => Promise.resolve({}),
|
|
44
|
+
post: (...args) => mockApiPost(...args),
|
|
45
|
+
delete: () => Promise.resolve({}),
|
|
46
|
+
patch: () => Promise.resolve({}),
|
|
47
|
+
},
|
|
48
|
+
}));
|
|
49
|
+
|
|
50
|
+
jest.mock('foremanReact/Root/Context/ForemanContext', () => ({
|
|
51
|
+
...jest.requireActual('foremanReact/Root/Context/ForemanContext'),
|
|
52
|
+
useForemanPermissions: () => new Set(['edit_foreman_tasks']),
|
|
53
|
+
}));
|
|
54
|
+
|
|
55
|
+
const defaultProps = {
|
|
56
|
+
match: { params: { id: null } },
|
|
57
|
+
history: {
|
|
58
|
+
location: { pathname: '/foreman_tasks/tasks', search: '' },
|
|
59
|
+
push: jest.fn(),
|
|
60
|
+
},
|
|
7
61
|
};
|
|
8
62
|
|
|
9
|
-
describe('
|
|
10
|
-
|
|
11
|
-
|
|
63
|
+
describe('TasksTableIndexPage', () => {
|
|
64
|
+
const renderPage = (props = defaultProps) =>
|
|
65
|
+
renderWithStore(
|
|
66
|
+
<IntlProvider locale="en">
|
|
67
|
+
<TasksTableIndexPage {...props} />
|
|
68
|
+
</IntlProvider>
|
|
69
|
+
);
|
|
70
|
+
it('should render the Tasks header when not viewing subtasks', () => {
|
|
71
|
+
renderPage();
|
|
72
|
+
|
|
73
|
+
expect(screen.getByRole('heading', { name: 'Tasks' })).toBeInTheDocument();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should render the Sub tasks header and breadcrumb when viewing subtasks', () => {
|
|
77
|
+
const propsWithParent = {
|
|
78
|
+
...defaultProps,
|
|
79
|
+
match: { params: { id: 'parent-123' } },
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
renderPage(propsWithParent);
|
|
83
|
+
|
|
84
|
+
expect(screen.getAllByText('Sub tasks')).toHaveLength(2);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('renders each row with the correct data', () => {
|
|
88
|
+
renderPage();
|
|
89
|
+
|
|
90
|
+
const { results } = tasksSuccessResponse.response;
|
|
91
|
+
|
|
92
|
+
results.forEach(task => {
|
|
93
|
+
const actionLink = screen.getByRole('link', { name: task.action });
|
|
94
|
+
const row = actionLink.closest('tr');
|
|
95
|
+
expect(row).toBeInTheDocument();
|
|
96
|
+
|
|
97
|
+
const rowScope = within(row);
|
|
98
|
+
|
|
99
|
+
expect(actionLink).toHaveAttribute(
|
|
100
|
+
'href',
|
|
101
|
+
`/foreman_tasks/tasks/${task.id}`
|
|
102
|
+
);
|
|
103
|
+
expect(
|
|
104
|
+
rowScope.getByRole('link', { name: task.action })
|
|
105
|
+
).toBeInTheDocument();
|
|
106
|
+
expect(rowScope.getByText(task.state)).toBeInTheDocument();
|
|
107
|
+
expect(rowScope.getByText(task.result)).toBeInTheDocument();
|
|
108
|
+
|
|
109
|
+
const hasNoStartDate = task.started_at == null;
|
|
110
|
+
if (hasNoStartDate) {
|
|
111
|
+
expect(rowScope.getAllByText('N/A')).toHaveLength(2);
|
|
112
|
+
const durationCell = rowScope.getByTitle('Task was canceled');
|
|
113
|
+
expect(durationCell).toBeInTheDocument();
|
|
114
|
+
expect(durationCell).toHaveClass('param-value');
|
|
115
|
+
expect(durationCell).toHaveTextContent('N/A');
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe('row modal open, close, and action', () => {
|
|
121
|
+
const { results } = tasksSuccessResponse.response;
|
|
122
|
+
const resumableTask = results.find(
|
|
123
|
+
r => r.available_actions && r.available_actions.resumable
|
|
124
|
+
);
|
|
125
|
+
const cancelOnlyTask = results.find(
|
|
126
|
+
r =>
|
|
127
|
+
r.available_actions?.cancellable &&
|
|
128
|
+
!r.available_actions?.resumable &&
|
|
129
|
+
r.state === 'stopped'
|
|
130
|
+
);
|
|
131
|
+
const forceCancelTask = results.find(r => r.state !== 'stopped');
|
|
132
|
+
|
|
133
|
+
const clickRowActionButton = (rowActionLabel, buttonName) => {
|
|
134
|
+
const row = screen
|
|
135
|
+
.getByRole('link', { name: rowActionLabel })
|
|
136
|
+
.closest('tr');
|
|
137
|
+
const rowScope = within(row);
|
|
138
|
+
fireEvent.click(rowScope.getByRole('button', { name: buttonName }));
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const openResumeModal = async () => {
|
|
142
|
+
clickRowActionButton(resumableTask.action, 'Resume');
|
|
143
|
+
await waitFor(() => {
|
|
144
|
+
expect(
|
|
145
|
+
screen.getByRole('heading', { name: 'Resume Task' })
|
|
146
|
+
).toBeInTheDocument();
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const openCancelModal = async () => {
|
|
151
|
+
clickRowActionButton(cancelOnlyTask.action, 'Cancel');
|
|
152
|
+
await waitFor(() => {
|
|
153
|
+
expect(
|
|
154
|
+
screen.getByRole('heading', { name: 'Cancel Task' })
|
|
155
|
+
).toBeInTheDocument();
|
|
156
|
+
});
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const openForceUnlockModal = async () => {
|
|
160
|
+
clickRowActionButton(forceCancelTask.action, 'Force Cancel');
|
|
161
|
+
await waitFor(() => {
|
|
162
|
+
expect(
|
|
163
|
+
screen.getByRole('heading', { name: 'Force Unlock Task' })
|
|
164
|
+
).toBeInTheDocument();
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
beforeEach(() => {
|
|
169
|
+
mockApiPost.mockClear();
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('opens Resume modal when Resume row action button is clicked', async () => {
|
|
173
|
+
renderPage();
|
|
174
|
+
expect(
|
|
175
|
+
screen.queryByRole('heading', { name: 'Resume Task' })
|
|
176
|
+
).not.toBeInTheDocument();
|
|
177
|
+
|
|
178
|
+
await openResumeModal();
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('closes modal when No is clicked', async () => {
|
|
182
|
+
renderPage();
|
|
183
|
+
await openResumeModal();
|
|
184
|
+
fireEvent.click(screen.getByRole('button', { name: 'No' }));
|
|
185
|
+
|
|
186
|
+
await waitFor(() => {
|
|
187
|
+
expect(
|
|
188
|
+
screen.queryByRole('heading', { name: 'Resume Task' })
|
|
189
|
+
).not.toBeInTheDocument();
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('calls API when Yes is clicked in Resume modal', async () => {
|
|
194
|
+
renderPage();
|
|
195
|
+
await openResumeModal();
|
|
196
|
+
|
|
197
|
+
fireEvent.click(screen.getByRole('button', { name: 'Yes' }));
|
|
198
|
+
|
|
199
|
+
await waitFor(() => {
|
|
200
|
+
expect(mockApiPost).toHaveBeenCalledTimes(1);
|
|
201
|
+
});
|
|
202
|
+
expect(mockApiPost).toHaveBeenCalledWith(
|
|
203
|
+
`/foreman_tasks/tasks/${resumableTask.id}/resume`
|
|
204
|
+
);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('calls API when Yes is clicked in Cancel modal', async () => {
|
|
208
|
+
renderPage();
|
|
209
|
+
await openCancelModal();
|
|
210
|
+
|
|
211
|
+
fireEvent.click(screen.getByRole('button', { name: 'Yes' }));
|
|
212
|
+
|
|
213
|
+
await waitFor(() => {
|
|
214
|
+
expect(mockApiPost).toHaveBeenCalledTimes(1);
|
|
215
|
+
});
|
|
216
|
+
expect(mockApiPost).toHaveBeenCalledWith(
|
|
217
|
+
`/foreman_tasks/tasks/${cancelOnlyTask.id}/cancel`
|
|
218
|
+
);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('calls API when Yes is clicked in Force Unlock modal', async () => {
|
|
222
|
+
renderPage();
|
|
223
|
+
await openForceUnlockModal();
|
|
224
|
+
|
|
225
|
+
fireEvent.click(screen.getByRole('button', { name: 'Yes' }));
|
|
226
|
+
|
|
227
|
+
await waitFor(() => {
|
|
228
|
+
expect(mockApiPost).toHaveBeenCalledTimes(1);
|
|
229
|
+
});
|
|
230
|
+
expect(mockApiPost).toHaveBeenCalledWith(
|
|
231
|
+
`/foreman_tasks/tasks/${forceCancelTask.id}/force_unlock`
|
|
232
|
+
);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
describe('CellActionButton', () => {
|
|
238
|
+
const setClickedTask = jest.fn();
|
|
239
|
+
const openModal = jest.fn();
|
|
240
|
+
|
|
241
|
+
const renderCellActionButton = (props = {}) =>
|
|
242
|
+
render(
|
|
243
|
+
<IntlProvider locale="en">
|
|
244
|
+
<CellActionButton
|
|
245
|
+
id="task-id-1"
|
|
246
|
+
action="Fixture action"
|
|
247
|
+
canEdit
|
|
248
|
+
setClickedTask={setClickedTask}
|
|
249
|
+
openModal={openModal}
|
|
250
|
+
{...props}
|
|
251
|
+
/>
|
|
252
|
+
</IntlProvider>
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
beforeEach(() => {
|
|
256
|
+
setClickedTask.mockClear();
|
|
257
|
+
openModal.mockClear();
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it('renders Resume for a resumable task when user can edit and opens resume modal on click', () => {
|
|
261
|
+
renderCellActionButton({
|
|
262
|
+
resumable: true,
|
|
263
|
+
cancellable: false,
|
|
264
|
+
stoppable: false,
|
|
265
|
+
});
|
|
266
|
+
const resumeBtn = screen.getByRole('button', { name: 'Resume' });
|
|
267
|
+
expect(resumeBtn).toBeInTheDocument();
|
|
268
|
+
fireEvent.click(resumeBtn);
|
|
269
|
+
expect(setClickedTask).toHaveBeenCalledWith({
|
|
270
|
+
id: 'task-id-1',
|
|
271
|
+
action: 'Fixture action',
|
|
272
|
+
});
|
|
273
|
+
expect(openModal).toHaveBeenCalledWith(RESUME_MODAL);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it('renders Cancel for cancellable non-resumable task when user can edit', () => {
|
|
277
|
+
renderCellActionButton({
|
|
278
|
+
cancellable: true,
|
|
279
|
+
resumable: false,
|
|
280
|
+
stoppable: false,
|
|
281
|
+
});
|
|
282
|
+
expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument();
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('renders no action buttons when user cannot edit', () => {
|
|
286
|
+
renderCellActionButton({
|
|
287
|
+
canEdit: false,
|
|
288
|
+
resumable: true,
|
|
289
|
+
cancellable: true,
|
|
290
|
+
stoppable: true,
|
|
291
|
+
});
|
|
292
|
+
expect(screen.queryByRole('button')).not.toBeInTheDocument();
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('renders disabled cancel button when no action is available', () => {
|
|
296
|
+
renderCellActionButton({
|
|
297
|
+
resumable: false,
|
|
298
|
+
cancellable: false,
|
|
299
|
+
stoppable: false,
|
|
300
|
+
});
|
|
301
|
+
expect(screen.getByRole('button', { name: 'Cancel' })).toBeDisabled();
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it('renders Force Cancel when task is stoppable and opens force unlock modal on click', () => {
|
|
305
|
+
renderCellActionButton({
|
|
306
|
+
resumable: false,
|
|
307
|
+
cancellable: false,
|
|
308
|
+
stoppable: true,
|
|
309
|
+
});
|
|
310
|
+
const forceBtn = screen.getByRole('button', { name: 'Force Cancel' });
|
|
311
|
+
expect(forceBtn).toBeInTheDocument();
|
|
312
|
+
fireEvent.click(forceBtn);
|
|
313
|
+
expect(setClickedTask).toHaveBeenCalledWith({
|
|
314
|
+
id: 'task-id-1',
|
|
315
|
+
action: 'Fixture action',
|
|
316
|
+
});
|
|
317
|
+
expect(openModal).toHaveBeenCalledWith(FORCE_UNLOCK_MODAL);
|
|
318
|
+
});
|
|
12
319
|
});
|