foreman-tasks 1.1.0 → 1.1.1
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/app/controllers/foreman_tasks/api/tasks_controller.rb +12 -5
- data/app/lib/foreman_tasks/concerns/polling_action_extensions.rb +12 -0
- data/app/models/setting/foreman_tasks.rb +6 -1
- data/app/services/ui_notifications/tasks/task_bulk_cancel.rb +36 -0
- data/app/services/ui_notifications/tasks/task_bulk_resume.rb +38 -0
- data/db/seeds.d/30-notification_blueprints.rb +14 -0
- data/foreman-tasks.gemspec +1 -0
- data/gemfile.d/foreman-tasks.rb +1 -0
- data/lib/foreman_tasks/engine.rb +1 -0
- data/lib/foreman_tasks/version.rb +1 -1
- data/script/travis_run_js_tests.sh +2 -2
- data/test/lib/concerns/polling_action_extensions_test.rb +34 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/TaskInfo.test.js.snap +1 -3
- data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardConstants.js +5 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardHelper.js +3 -2
- data/webpack/ForemanTasks/Components/TasksTable/Components/SelectAllAlert.js +43 -0
- data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/SelectAllAlert.test.js +29 -0
- data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/__snapshots__/SelectAllAlert.test.js.snap +75 -0
- data/webpack/ForemanTasks/Components/TasksTable/SubTasksPage.js +2 -1
- data/webpack/ForemanTasks/Components/TasksTable/TasksBulkActions.js +164 -0
- data/webpack/ForemanTasks/Components/TasksTable/TasksTable.js +24 -10
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableActionHelpers.js +52 -0
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableActions.js +66 -128
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableConstants.js +11 -1
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableHelpers.js +4 -3
- data/webpack/ForemanTasks/Components/TasksTable/TasksTablePage.js +64 -12
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableReducer.js +21 -2
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableSelectors.js +6 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksBulkActions.test.js +112 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTable.fixtures.js +5 -3
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableActionHelpers.test.js +46 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableActions.test.js +19 -41
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableHelpers.test.js +17 -1
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTablePage.test.js +9 -1
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableReducer.test.js +22 -1
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/SubTasksPage.test.js.snap +5 -3
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksBulkActions.test.js.snap +229 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksIndexPage.test.js.snap +5 -3
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTableActions.test.js.snap +39 -124
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTablePage.test.js.snap +40 -16
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTableReducer.test.js.snap +34 -0
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/selectionHeaderCellFormatter.test.js +1 -1
- data/webpack/ForemanTasks/Components/TasksTable/formatters/selectionHeaderCellFormatter.js +2 -2
- data/webpack/ForemanTasks/Components/TasksTable/index.js +8 -2
- data/webpack/ForemanTasks/Components/common/ToastTypesConstants.js +11 -0
- metadata +31 -2
@@ -1,12 +1,22 @@
|
|
1
1
|
import { getControllerSearchProps } from 'foremanReact/constants';
|
2
2
|
|
3
3
|
export const TASKS_TABLE_ID = 'TASKS_TABLE';
|
4
|
+
|
4
5
|
export const SELECT_ROWS = 'SELECT_ROWS';
|
5
6
|
export const UNSELECT_ROWS = 'UNSELECT_ROWS';
|
6
7
|
export const UNSELECT_ALL_ROWS = 'UNSELECT_ALL_ROWS';
|
8
|
+
export const SELECT_ALL_ROWS = 'SELECT_ALL_ROWS';
|
9
|
+
export const OPEN_SELECT_ALL = 'OPEN_SELECT_ALL';
|
10
|
+
|
7
11
|
export const TASKS_RESUME_REQUEST = 'TASKS_RESUME_REQUEST';
|
8
12
|
export const TASKS_RESUME_SUCCESS = 'TASKS_RESUME_SUCCESS';
|
9
13
|
export const TASKS_RESUME_FAILURE = 'TASKS_RESUME_FAILURE';
|
14
|
+
export const TASKS_CANCEL_REQUEST = 'TASKS_CANCEL_REQUEST';
|
15
|
+
export const TASKS_CANCEL_SUCCESS = 'TASKS_CANCEL_SUCCESS';
|
16
|
+
export const TASKS_CANCEL_FAILURE = 'TASKS_CANCEL_FAILURE';
|
17
|
+
|
18
|
+
export const BULK_CANCEL_PATH = 'bulk_cancel';
|
19
|
+
export const BULK_RESUME_PATH = 'bulk_resume';
|
10
20
|
|
11
21
|
export const CANCEL_CONFIRM_MODAL_ID = 'cancelConfirmModal';
|
12
22
|
export const RESUME_CONFIRM_MODAL_ID = 'resumeConfirmModal';
|
@@ -16,5 +26,5 @@ export const UPDATE_CLICKED = 'UPDATE_CLICKED';
|
|
16
26
|
|
17
27
|
export const TASKS_SEARCH_PROPS = {
|
18
28
|
...getControllerSearchProps('tasks'),
|
19
|
-
controller: 'foreman_tasks/tasks'
|
29
|
+
controller: 'foreman_tasks/tasks',
|
20
30
|
};
|
@@ -22,9 +22,10 @@ export const resolveSearchQuery = (search, history) => {
|
|
22
22
|
updateURlQuery(uriQuery, history);
|
23
23
|
};
|
24
24
|
|
25
|
-
export const
|
26
|
-
|
27
|
-
url.
|
25
|
+
export const getCSVurl = (path, query) => {
|
26
|
+
let url = new URI(path);
|
27
|
+
url = url.pathname(`${url.pathname()}.csv`);
|
28
|
+
url.addSearch(query);
|
28
29
|
return url.toString();
|
29
30
|
};
|
30
31
|
|
@@ -10,7 +10,7 @@ import { STATUS } from 'foremanReact/constants';
|
|
10
10
|
import { useForemanModal } from 'foremanReact/components/ForemanModal/ForemanModalHooks';
|
11
11
|
import TasksDashboard from '../TasksDashboard';
|
12
12
|
import TasksTable from './TasksTable';
|
13
|
-
import { resolveSearchQuery,
|
13
|
+
import { resolveSearchQuery, getCSVurl } from './TasksTableHelpers';
|
14
14
|
import { ConfirmationModals } from './Components/ConfirmationModals';
|
15
15
|
import {
|
16
16
|
TASKS_SEARCH_PROPS,
|
@@ -21,8 +21,17 @@ import {
|
|
21
21
|
} from './TasksTableConstants';
|
22
22
|
import { ActionSelectButton } from './Components/ActionSelectButton';
|
23
23
|
import './TasksTablePage.scss';
|
24
|
+
import { SelectAllAlert } from './Components/SelectAllAlert';
|
24
25
|
|
25
|
-
const TasksTablePage = ({
|
26
|
+
const TasksTablePage = ({
|
27
|
+
getBreadcrumbs,
|
28
|
+
history,
|
29
|
+
clicked,
|
30
|
+
createHeader,
|
31
|
+
selectAllRows,
|
32
|
+
showSelectAll,
|
33
|
+
...props
|
34
|
+
}) => {
|
26
35
|
const url = history.location.pathname + history.location.search;
|
27
36
|
const uriQuery = getURIQuery(url);
|
28
37
|
const onSearch = searchQuery => {
|
@@ -53,15 +62,23 @@ const TasksTablePage = ({ getBreadcrumbs, history, clicked, createHeader, ...pro
|
|
53
62
|
};
|
54
63
|
|
55
64
|
const {
|
56
|
-
|
57
|
-
|
65
|
+
bulkCancelById,
|
66
|
+
bulkCancelBySearch,
|
67
|
+
bulkResumeById,
|
68
|
+
bulkResumeBySearch,
|
58
69
|
cancelTask,
|
59
70
|
resumeTask,
|
60
71
|
parentTaskID,
|
61
72
|
} = props;
|
62
73
|
const tasksActions = {
|
63
74
|
cancelSelectedTasks: () => {
|
64
|
-
|
75
|
+
props.allRowsSelected
|
76
|
+
? bulkCancelBySearch({ query: uriQuery, parentTaskID })
|
77
|
+
: bulkCancelById({
|
78
|
+
selected: getSelectedTasks(),
|
79
|
+
url,
|
80
|
+
parentTaskID,
|
81
|
+
});
|
65
82
|
},
|
66
83
|
cancelTask: () => {
|
67
84
|
cancelTask({
|
@@ -72,7 +89,13 @@ const TasksTablePage = ({ getBreadcrumbs, history, clicked, createHeader, ...pro
|
|
72
89
|
});
|
73
90
|
},
|
74
91
|
resumeSelectedTasks: () => {
|
75
|
-
|
92
|
+
props.allRowsSelected
|
93
|
+
? bulkResumeBySearch({ query: uriQuery, parentTaskID })
|
94
|
+
: bulkResumeById({
|
95
|
+
selected: getSelectedTasks(),
|
96
|
+
url,
|
97
|
+
parentTaskID,
|
98
|
+
});
|
76
99
|
},
|
77
100
|
resumeTask: () => {
|
78
101
|
resumeTask({
|
@@ -88,7 +111,9 @@ const TasksTablePage = ({ getBreadcrumbs, history, clicked, createHeader, ...pro
|
|
88
111
|
<div className="tasks-table-wrapper">
|
89
112
|
<ConfirmationModals
|
90
113
|
tasksActions={tasksActions}
|
91
|
-
selectedRowsLen={
|
114
|
+
selectedRowsLen={
|
115
|
+
props.allRowsSelected ? props.itemCount : props.selectedRows.length
|
116
|
+
}
|
92
117
|
modalProps={modalProps}
|
93
118
|
/>
|
94
119
|
<PageLayout
|
@@ -102,11 +127,11 @@ const TasksTablePage = ({ getBreadcrumbs, history, clicked, createHeader, ...pro
|
|
102
127
|
<React.Fragment>
|
103
128
|
{props.status === STATUS.PENDING && <Spinner size="lg" loading />}
|
104
129
|
<ExportButton
|
105
|
-
url={
|
130
|
+
url={getCSVurl(url, uriQuery)}
|
106
131
|
title={__('Export All')}
|
107
132
|
/>
|
108
133
|
<ActionSelectButton
|
109
|
-
disabled={props.selectedRows.length
|
134
|
+
disabled={!(props.selectedRows.length || props.allRowsSelected)}
|
110
135
|
onCancel={modalProps.cancelSelectedModal.setModalOpen}
|
111
136
|
onResume={modalProps.resumeSelectedModal.setModalOpen}
|
112
137
|
/>
|
@@ -117,13 +142,30 @@ const TasksTablePage = ({ getBreadcrumbs, history, clicked, createHeader, ...pro
|
|
117
142
|
<TasksDashboard history={history} parentTaskID={props.parentTaskID} />
|
118
143
|
}
|
119
144
|
>
|
120
|
-
<
|
145
|
+
<React.Fragment>
|
146
|
+
{showSelectAll && props.itemCount >= props.pagination.perPage && (
|
147
|
+
<SelectAllAlert
|
148
|
+
itemCount={props.itemCount}
|
149
|
+
perPage={props.pagination.perPage}
|
150
|
+
selectAllRows={selectAllRows}
|
151
|
+
unselectAllRows={props.unselectAllRows}
|
152
|
+
allRowsSelected={props.allRowsSelected}
|
153
|
+
/>
|
154
|
+
)}
|
155
|
+
<TasksTable history={history} {...props} modalProps={modalProps} />
|
156
|
+
</React.Fragment>
|
121
157
|
</PageLayout>
|
122
158
|
</div>
|
123
159
|
);
|
124
160
|
};
|
125
161
|
|
126
162
|
TasksTablePage.propTypes = {
|
163
|
+
allRowsSelected: PropTypes.bool,
|
164
|
+
itemCount: PropTypes.number.isRequired,
|
165
|
+
pagination: PropTypes.shape({
|
166
|
+
perPage: PropTypes.number,
|
167
|
+
}),
|
168
|
+
selectAllRows: PropTypes.func.isRequired,
|
127
169
|
results: PropTypes.array.isRequired,
|
128
170
|
getTableItems: PropTypes.func.isRequired,
|
129
171
|
getBreadcrumbs: PropTypes.func.isRequired,
|
@@ -132,8 +174,10 @@ TasksTablePage.propTypes = {
|
|
132
174
|
history: PropTypes.object.isRequired,
|
133
175
|
cancelTask: PropTypes.func.isRequired,
|
134
176
|
resumeTask: PropTypes.func.isRequired,
|
135
|
-
|
136
|
-
|
177
|
+
bulkCancelById: PropTypes.func.isRequired,
|
178
|
+
bulkCancelBySearch: PropTypes.func.isRequired,
|
179
|
+
bulkResumeById: PropTypes.func.isRequired,
|
180
|
+
bulkResumeBySearch: PropTypes.func.isRequired,
|
137
181
|
selectedRows: PropTypes.arrayOf(PropTypes.string),
|
138
182
|
parentTaskID: PropTypes.string,
|
139
183
|
createHeader: PropTypes.func,
|
@@ -142,15 +186,23 @@ TasksTablePage.propTypes = {
|
|
142
186
|
taskName: PropTypes.string,
|
143
187
|
parentTaskID: PropTypes.string,
|
144
188
|
}),
|
189
|
+
showSelectAll: PropTypes.bool,
|
190
|
+
unselectAllRows: PropTypes.func.isRequired,
|
145
191
|
};
|
146
192
|
|
147
193
|
TasksTablePage.defaultProps = {
|
194
|
+
pagination: {
|
195
|
+
page: 1,
|
196
|
+
perPage: 20,
|
197
|
+
},
|
198
|
+
allRowsSelected: false,
|
148
199
|
actionName: '',
|
149
200
|
status: STATUS.PENDING,
|
150
201
|
selectedRows: [],
|
151
202
|
parentTaskID: null,
|
152
203
|
clicked: {},
|
153
204
|
createHeader: () => __('Tasks'),
|
205
|
+
showSelectAll: false,
|
154
206
|
};
|
155
207
|
|
156
208
|
export default TasksTablePage;
|
@@ -9,6 +9,8 @@ import {
|
|
9
9
|
UNSELECT_ROWS,
|
10
10
|
UNSELECT_ALL_ROWS,
|
11
11
|
UPDATE_CLICKED,
|
12
|
+
SELECT_ALL_ROWS,
|
13
|
+
OPEN_SELECT_ALL,
|
12
14
|
} from './TasksTableConstants';
|
13
15
|
|
14
16
|
const initialState = Immutable({
|
@@ -21,6 +23,8 @@ export const TasksTableQueryReducer = (state = initialState, action) => {
|
|
21
23
|
response || {};
|
22
24
|
const ACTION_TYPES = createTableActionTypes(TASKS_TABLE_ID);
|
23
25
|
switch (type) {
|
26
|
+
case SELECT_ALL_ROWS:
|
27
|
+
return state.set('allRowsSelected', true);
|
24
28
|
case ACTION_TYPES.SUCCESS:
|
25
29
|
return Immutable.merge(state, {
|
26
30
|
itemCount: subtotal,
|
@@ -33,13 +37,28 @@ export const TasksTableQueryReducer = (state = initialState, action) => {
|
|
33
37
|
});
|
34
38
|
case SELECT_ROWS:
|
35
39
|
return state.set('selectedRows', union(payload, state.selectedRows));
|
40
|
+
case OPEN_SELECT_ALL:
|
41
|
+
return state.set('showSelectAll', true);
|
36
42
|
case UNSELECT_ROWS:
|
43
|
+
if (state.allRowsSelected) {
|
44
|
+
// User can unselect rows if only the page rows are selected
|
45
|
+
return state
|
46
|
+
.set(
|
47
|
+
'selectedRows',
|
48
|
+
payload.results.map(row => row.id).filter(row => row !== payload.id)
|
49
|
+
)
|
50
|
+
.set('allRowsSelected', false)
|
51
|
+
.set('showSelectAll', false);
|
52
|
+
}
|
37
53
|
return state.set(
|
38
54
|
'selectedRows',
|
39
|
-
state.selectedRows.filter(row => row !== payload)
|
55
|
+
state.selectedRows.filter(row => row !== payload.id)
|
40
56
|
);
|
41
57
|
case UNSELECT_ALL_ROWS:
|
42
|
-
return state
|
58
|
+
return state
|
59
|
+
.set('selectedRows', [])
|
60
|
+
.set('allRowsSelected', false)
|
61
|
+
.set('showSelectAll', false);
|
43
62
|
case UPDATE_CLICKED:
|
44
63
|
return state.set('clicked', payload.clicked);
|
45
64
|
default:
|
@@ -48,3 +48,9 @@ export const selectError = state => selectTasksTableContent(state).error;
|
|
48
48
|
|
49
49
|
export const selectSort = state =>
|
50
50
|
selectTasksTableQuery(state).sort || { by: 'started_at', order: 'DESC' };
|
51
|
+
|
52
|
+
export const selectAllRowsSelected = state =>
|
53
|
+
selectTasksTableQuery(state).allRowsSelected;
|
54
|
+
|
55
|
+
export const selectShowSelectAll = state =>
|
56
|
+
selectTasksTableQuery(state).showSelectAll;
|
@@ -0,0 +1,112 @@
|
|
1
|
+
import { testActionSnapshotWithFixtures } from '@theforeman/test';
|
2
|
+
import API from 'foremanReact/API';
|
3
|
+
import {
|
4
|
+
bulkCancelById,
|
5
|
+
bulkCancelBySearch,
|
6
|
+
bulkResumeById,
|
7
|
+
bulkResumeBySearch,
|
8
|
+
} from '../TasksBulkActions';
|
9
|
+
|
10
|
+
jest.mock('foremanReact/components/common/table', () => ({
|
11
|
+
getTableItemsAction: jest.fn(controller => controller),
|
12
|
+
}));
|
13
|
+
|
14
|
+
jest.mock('foremanReact/API');
|
15
|
+
|
16
|
+
const task = {
|
17
|
+
id: 'some-id',
|
18
|
+
name: 'some-name',
|
19
|
+
};
|
20
|
+
|
21
|
+
const fixtures = {
|
22
|
+
'handles bulkResumeById requests that fail': () => {
|
23
|
+
const selected = [{ ...task, isResumable: true }];
|
24
|
+
|
25
|
+
API.post.mockImplementation(() =>
|
26
|
+
Promise.reject(new Error('Network Error'))
|
27
|
+
);
|
28
|
+
return bulkResumeById({ selected, url: 'some-url' });
|
29
|
+
},
|
30
|
+
'handles resumable bulkResumeById requests': () => {
|
31
|
+
const selected = [{ ...task, isResumable: true }];
|
32
|
+
|
33
|
+
API.post.mockImplementation(() => ({
|
34
|
+
data: {
|
35
|
+
resumed: [{ action: 'I am resumed' }],
|
36
|
+
failed: [{ action: 'I am failed' }],
|
37
|
+
},
|
38
|
+
}));
|
39
|
+
return bulkResumeById({ selected, url: 'some-url' });
|
40
|
+
},
|
41
|
+
'handles bulkCancelById requests': () => {
|
42
|
+
const selected = [{ ...task, isCancellable: true }];
|
43
|
+
|
44
|
+
API.post.mockImplementation(() => ({
|
45
|
+
data: {
|
46
|
+
cancelled: [{ action: 'I am cancelled' }],
|
47
|
+
},
|
48
|
+
}));
|
49
|
+
return bulkCancelById({ selected, url: 'some-url' });
|
50
|
+
},
|
51
|
+
'handles skipped bulkResumeById requests': () => {
|
52
|
+
const selected = [{ ...task, isResumable: true }];
|
53
|
+
|
54
|
+
API.post.mockImplementation(() => ({
|
55
|
+
data: {
|
56
|
+
skipped: [{ action: 'I am skipped' }],
|
57
|
+
},
|
58
|
+
}));
|
59
|
+
return bulkResumeById({ selected, url: 'some-url' });
|
60
|
+
},
|
61
|
+
'handles skipped bulkCancelById requests': () => {
|
62
|
+
const selected = [{ ...task, isCancellable: true }];
|
63
|
+
|
64
|
+
API.post.mockImplementation(() => ({
|
65
|
+
data: {
|
66
|
+
skipped: [{ action: 'I am skipped' }],
|
67
|
+
},
|
68
|
+
}));
|
69
|
+
return bulkCancelById({ selected, url: 'some-url' });
|
70
|
+
},
|
71
|
+
'handles bulkCancelById requests that are not cancellable': () => {
|
72
|
+
const selected = [{ ...task, isCancellable: false }];
|
73
|
+
return bulkCancelById({ selected, url: 'some-url' });
|
74
|
+
},
|
75
|
+
'handles bulkResumeById requests that are not resumable': () => {
|
76
|
+
const selected = [{ ...task, isResumable: false, isCancellable: false }];
|
77
|
+
return bulkResumeById({ selected, url: 'some-url' });
|
78
|
+
},
|
79
|
+
|
80
|
+
'handles bulkCancelBySearch requests': () => {
|
81
|
+
API.post.mockImplementation(() => ({
|
82
|
+
data: {
|
83
|
+
cancelled: [{ action: 'I am cancelled' }],
|
84
|
+
skipped: [{ action: 'I am skipped' }],
|
85
|
+
},
|
86
|
+
}));
|
87
|
+
return bulkCancelBySearch({
|
88
|
+
query: { search: {} },
|
89
|
+
url: 'some-url',
|
90
|
+
parentTaskID: 'parent',
|
91
|
+
});
|
92
|
+
},
|
93
|
+
|
94
|
+
'handles bulkResumeBySearch requests': () => {
|
95
|
+
API.post.mockImplementation(() => ({
|
96
|
+
data: {
|
97
|
+
cancelled: [{ action: 'I am cancelled' }],
|
98
|
+
skipped: [{ action: 'I am skipped' }],
|
99
|
+
failed: [{ action: 'I am failed' }],
|
100
|
+
},
|
101
|
+
}));
|
102
|
+
return bulkResumeBySearch({
|
103
|
+
query: { search: {} },
|
104
|
+
url: 'some-url',
|
105
|
+
parentTaskID: 'parent',
|
106
|
+
});
|
107
|
+
},
|
108
|
+
};
|
109
|
+
|
110
|
+
describe('TasksTable bulk actions', () => {
|
111
|
+
testActionSnapshotWithFixtures(fixtures);
|
112
|
+
});
|
@@ -6,8 +6,11 @@ export const minProps = {
|
|
6
6
|
itemCount: 2,
|
7
7
|
cancelTask: jest.fn(),
|
8
8
|
resumeTask: jest.fn(),
|
9
|
-
|
10
|
-
|
9
|
+
bulkResumeById: jest.fn(),
|
10
|
+
bulkCancelById: jest.fn(),
|
11
|
+
bulkResumeBySearch: jest.fn(),
|
12
|
+
bulkCancelBySearch: jest.fn(),
|
13
|
+
selectPage: jest.fn(),
|
11
14
|
selectAllRows: jest.fn(),
|
12
15
|
unselectAllRows: jest.fn(),
|
13
16
|
selectRow: jest.fn(),
|
@@ -18,7 +21,6 @@ export const minProps = {
|
|
18
21
|
perPage: 10,
|
19
22
|
},
|
20
23
|
history: { location: { search: '' } },
|
21
|
-
status: STATUS.RESOLVED,
|
22
24
|
results: ['a', 'b'],
|
23
25
|
sort: {
|
24
26
|
by: 'q',
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import { convertDashboardQuery } from '../TasksTableActionHelpers';
|
2
|
+
import {
|
3
|
+
TASKS_DASHBOARD_JS_QUERY_MODES,
|
4
|
+
TASKS_DASHBOARD_AVAILABLE_TIMES,
|
5
|
+
} from '../../TasksDashboard/TasksDashboardConstants';
|
6
|
+
|
7
|
+
let realDate;
|
8
|
+
|
9
|
+
describe('convertDashboardQuery', () => {
|
10
|
+
it('convertDashboardQuery should work with full query', () => {
|
11
|
+
// Setup
|
12
|
+
const currentDate = new Date('2020-05-08T11:01:58.135Z');
|
13
|
+
realDate = Date;
|
14
|
+
global.Date = class extends Date {
|
15
|
+
constructor(date) {
|
16
|
+
if (date) {
|
17
|
+
// eslint-disable-next-line constructor-super
|
18
|
+
return super(date);
|
19
|
+
}
|
20
|
+
return currentDate;
|
21
|
+
}
|
22
|
+
};
|
23
|
+
const query = {
|
24
|
+
time_mode: TASKS_DASHBOARD_JS_QUERY_MODES.RECENT,
|
25
|
+
time_horizon: TASKS_DASHBOARD_AVAILABLE_TIMES.WEEK,
|
26
|
+
state: 'stopped',
|
27
|
+
result: 'error',
|
28
|
+
search: 'action~job',
|
29
|
+
};
|
30
|
+
expect(convertDashboardQuery(query)).toEqual(
|
31
|
+
'state=stopped and result=error and action~job and (state_updated_at>2020-05-01T11:01:58.135Z or state_updated_at = NULL)'
|
32
|
+
);
|
33
|
+
// Cleanup
|
34
|
+
global.Date = realDate;
|
35
|
+
});
|
36
|
+
it('convertDashboardQuery should work with only search query', () => {
|
37
|
+
const query = {
|
38
|
+
search: 'action~job',
|
39
|
+
};
|
40
|
+
expect(convertDashboardQuery(query)).toEqual('action~job');
|
41
|
+
});
|
42
|
+
it('convertDashboardQuery should work with no query', () => {
|
43
|
+
const query = {};
|
44
|
+
expect(convertDashboardQuery(query)).toEqual('');
|
45
|
+
});
|
46
|
+
});
|
@@ -1,4 +1,7 @@
|
|
1
|
-
import {
|
1
|
+
import {
|
2
|
+
testActionSnapshotWithFixtures,
|
3
|
+
IntegrationTestHelper,
|
4
|
+
} from '@theforeman/test';
|
2
5
|
import API from 'foremanReact/API';
|
3
6
|
import { TASKS_TABLE_ID } from '../TasksTableConstants';
|
4
7
|
import {
|
@@ -7,8 +10,7 @@ import {
|
|
7
10
|
cancelTaskRequest,
|
8
11
|
resumeTask,
|
9
12
|
resumeTaskRequest,
|
10
|
-
|
11
|
-
bulkResume,
|
13
|
+
selectPage,
|
12
14
|
} from '../TasksTableActions';
|
13
15
|
|
14
16
|
jest.mock('foremanReact/components/common/table', () => ({
|
@@ -24,13 +26,7 @@ const taskInfo = {
|
|
24
26
|
taskName: 'some-name',
|
25
27
|
};
|
26
28
|
|
27
|
-
const task = {
|
28
|
-
id: 'some-id',
|
29
|
-
name: 'some-name',
|
30
|
-
};
|
31
|
-
|
32
29
|
const fixtures = {
|
33
|
-
'should cancelTask': () => cancelTask({ ...taskInfo, url: 'some-url' }),
|
34
30
|
'should cancelTaskRequest and succeed': () =>
|
35
31
|
cancelTaskRequest('some-id', 'some-name'),
|
36
32
|
'should cancelTaskRequest and fail': () => {
|
@@ -40,7 +36,6 @@ const fixtures = {
|
|
40
36
|
return cancelTaskRequest('some-id', 'some-name');
|
41
37
|
},
|
42
38
|
|
43
|
-
'should resumeTask': () => resumeTask({ ...taskInfo, url: 'some-url' }),
|
44
39
|
'should resumeTaskRequest and succeed': () => {
|
45
40
|
API.post.mockImplementation(() => ({ data: 'some-data' }));
|
46
41
|
return resumeTaskRequest('some-id', 'some-name');
|
@@ -51,41 +46,24 @@ const fixtures = {
|
|
51
46
|
);
|
52
47
|
return resumeTaskRequest('some-id', 'some-name');
|
53
48
|
},
|
54
|
-
'
|
55
|
-
const selected = [{ ...task, isResumable: true, isCancellable: false }];
|
56
|
-
|
57
|
-
API.post.mockImplementation(() =>
|
58
|
-
Promise.reject(new Error('Network Error'))
|
59
|
-
);
|
60
|
-
return bulkResume({ selected, url: 'some-url' });
|
61
|
-
},
|
62
|
-
'handles resumable bulkResume requests': () => {
|
63
|
-
const selected = [{ ...task, isResumable: true, isCancellable: false }];
|
64
|
-
|
65
|
-
API.post.mockImplementation(() => ({
|
66
|
-
data: {
|
67
|
-
resumed: [{ action: 'I am resumed' }],
|
68
|
-
failed: [{ action: 'I am failed' }],
|
69
|
-
},
|
70
|
-
}));
|
71
|
-
return bulkResume({ selected, url: 'some-url' });
|
72
|
-
},
|
73
|
-
'handles bulkCancel requests': () => {
|
74
|
-
const selected = [{ ...task, isResumable: false, isCancellable: true }];
|
75
|
-
return bulkCancel({ selected, url: 'some-url' });
|
76
|
-
},
|
77
|
-
'handles bulkCancel requests that are not cancellable': () => {
|
78
|
-
const selected = [{ ...task, isResumable: false, isCancellable: false }];
|
79
|
-
return bulkCancel({ selected, url: 'some-url' });
|
80
|
-
},
|
81
|
-
'handles bulkResume requests that are not resumable': () => {
|
82
|
-
const selected = [{ ...task, isResumable: false, isCancellable: false }];
|
83
|
-
return bulkResume({ selected, url: 'some-url' });
|
84
|
-
},
|
49
|
+
'should selectPage and succeed': () => selectPage([{ id: 'some-id' }]),
|
85
50
|
};
|
86
51
|
describe('TasksTable actions', () => {
|
87
52
|
it('getTableItems should reuse common/table/getTableItemsAction', () => {
|
88
53
|
expect(getTableItems('')).toEqual(TASKS_TABLE_ID);
|
89
54
|
});
|
55
|
+
|
56
|
+
it('should resumeTask', async () => {
|
57
|
+
const dispatch = jest.fn();
|
58
|
+
resumeTask({ ...taskInfo, url: 'some-url' })(dispatch);
|
59
|
+
await IntegrationTestHelper.flushAllPromises();
|
60
|
+
expect(dispatch.mock.calls).toHaveLength(3);
|
61
|
+
});
|
62
|
+
it('should cancelTask', async () => {
|
63
|
+
const dispatch = jest.fn();
|
64
|
+
cancelTask({ ...taskInfo, url: 'some-url' })(dispatch);
|
65
|
+
await IntegrationTestHelper.flushAllPromises();
|
66
|
+
expect(dispatch.mock.calls).toHaveLength(3);
|
67
|
+
});
|
90
68
|
testActionSnapshotWithFixtures(fixtures);
|
91
69
|
});
|