foreman-tasks 11.0.7 → 12.0.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/app/views/foreman_tasks/api/tasks/dependency_summary.json.rabl +4 -0
- data/app/views/foreman_tasks/api/tasks/details.json.rabl +20 -0
- data/lib/foreman_tasks/engine.rb +6 -1
- data/lib/foreman_tasks/triggers.rb +4 -0
- data/lib/foreman_tasks/version.rb +1 -1
- data/lib/foreman_tasks.rb +24 -0
- data/test/unit/chaining_test.rb +62 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/Dependencies.js +93 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/Task.js +7 -12
- data/webpack/ForemanTasks/Components/TaskDetails/Components/TaskButtons.js +40 -20
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Dependencies.test.js +92 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/TaskButtons.test.js +2 -3
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Task.test.js.snap +90 -112
- data/webpack/ForemanTasks/Components/TaskDetails/TaskDetails.js +13 -1
- data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsSelectors.js +6 -0
- data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/TaskDetails.test.js.snap +19 -0
- data/webpack/ForemanTasks/Components/TaskDetails/index.js +4 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/OtherInfo.js +5 -3
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/OtherInfo.test.js +58 -9
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.scss +0 -30
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.test.js +190 -9
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCardTable.js +70 -35
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/TasksLabelsRow.js +9 -14
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/TasksLabelsRow.scss +3 -23
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/TasksLabelsRow.test.js +33 -26
- data/webpack/ForemanTasks/Components/TasksTable/Components/ActionSelectButton.js +31 -30
- data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/ActionSelectButton.test.js +78 -11
- data/webpack/ForemanTasks/Components/TasksTable/TasksTablePage.js +40 -21
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTablePage.test.js.snap +64 -62
- metadata +6 -7
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCardTable.test.js +0 -54
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/__snapshots__/OtherInfo.test.js.snap +0 -30
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/__snapshots__/StoppedTasksCard.test.js.snap +0 -107
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/__snapshots__/StoppedTasksCardTable.test.js.snap +0 -960
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/__snapshots__/TasksLabelsRow.test.js.snap +0 -47
- data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/__snapshots__/ActionSelectButton.test.js.snap +0 -43
|
@@ -15,59 +15,48 @@ exports[`Task rendering render with minimal Props 1`] = `
|
|
|
15
15
|
selectedRowsLen={1}
|
|
16
16
|
setModalClosed={[Function]}
|
|
17
17
|
/>
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
startBefore=""
|
|
61
|
-
startedAt=""
|
|
62
|
-
state=""
|
|
63
|
-
taskProgressToggle={[MockFunction]}
|
|
64
|
-
taskReload={false}
|
|
65
|
-
taskReloadStart={[MockFunction]}
|
|
66
|
-
unlockTaskRequest={[Function]}
|
|
67
|
-
username=""
|
|
68
|
-
usernamePath=""
|
|
69
|
-
/>
|
|
70
|
-
</Grid>
|
|
18
|
+
<TaskButtons
|
|
19
|
+
action=""
|
|
20
|
+
canEdit={false}
|
|
21
|
+
cancelTaskRequest={[Function]}
|
|
22
|
+
cancellable={false}
|
|
23
|
+
dynflowEnableConsole={false}
|
|
24
|
+
externalId=""
|
|
25
|
+
forceCancelTaskRequest={[Function]}
|
|
26
|
+
hasSubTasks={false}
|
|
27
|
+
id="test"
|
|
28
|
+
parentTask=""
|
|
29
|
+
resumable={false}
|
|
30
|
+
resumeTaskRequest={[Function]}
|
|
31
|
+
setForceUnlockModalOpen={[Function]}
|
|
32
|
+
setUnlockModalOpen={[Function]}
|
|
33
|
+
state=""
|
|
34
|
+
taskProgressToggle={[MockFunction]}
|
|
35
|
+
taskReload={false}
|
|
36
|
+
taskReloadStart={[MockFunction]}
|
|
37
|
+
unlockTaskRequest={[Function]}
|
|
38
|
+
/>
|
|
39
|
+
<TaskInfo
|
|
40
|
+
action=""
|
|
41
|
+
endedAt=""
|
|
42
|
+
errors={Array []}
|
|
43
|
+
forceCancelTaskRequest={[Function]}
|
|
44
|
+
help=""
|
|
45
|
+
id="test"
|
|
46
|
+
output=""
|
|
47
|
+
progress={0}
|
|
48
|
+
result="error"
|
|
49
|
+
startAt=""
|
|
50
|
+
startBefore=""
|
|
51
|
+
startedAt=""
|
|
52
|
+
state=""
|
|
53
|
+
taskProgressToggle={[MockFunction]}
|
|
54
|
+
taskReload={false}
|
|
55
|
+
taskReloadStart={[MockFunction]}
|
|
56
|
+
unlockTaskRequest={[Function]}
|
|
57
|
+
username=""
|
|
58
|
+
usernamePath=""
|
|
59
|
+
/>
|
|
71
60
|
</Fragment>
|
|
72
61
|
`;
|
|
73
62
|
|
|
@@ -86,64 +75,53 @@ exports[`Task rendering render with some Props 1`] = `
|
|
|
86
75
|
selectedRowsLen={1}
|
|
87
76
|
setModalClosed={[Function]}
|
|
88
77
|
/>
|
|
89
|
-
<
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
startedAt=""
|
|
138
|
-
state="paused"
|
|
139
|
-
status="RESOLVED"
|
|
140
|
-
taskProgressToggle={[MockFunction]}
|
|
141
|
-
taskReload={true}
|
|
142
|
-
taskReloadStart={[MockFunction]}
|
|
143
|
-
unlockTaskRequest={[Function]}
|
|
144
|
-
username=""
|
|
145
|
-
usernamePath=""
|
|
146
|
-
/>
|
|
147
|
-
</Grid>
|
|
78
|
+
<TaskButtons
|
|
79
|
+
action=""
|
|
80
|
+
canEdit={true}
|
|
81
|
+
cancelTaskRequest={[Function]}
|
|
82
|
+
cancellable={false}
|
|
83
|
+
dynflowEnableConsole={true}
|
|
84
|
+
externalId=""
|
|
85
|
+
forceCancelTaskRequest={[Function]}
|
|
86
|
+
hasSubTasks={true}
|
|
87
|
+
id="test"
|
|
88
|
+
parentTask="parent-id"
|
|
89
|
+
resumable={false}
|
|
90
|
+
resumeTaskRequest={[Function]}
|
|
91
|
+
setForceUnlockModalOpen={[Function]}
|
|
92
|
+
setUnlockModalOpen={[Function]}
|
|
93
|
+
state="paused"
|
|
94
|
+
status="RESOLVED"
|
|
95
|
+
taskProgressToggle={[MockFunction]}
|
|
96
|
+
taskReload={true}
|
|
97
|
+
taskReloadStart={[MockFunction]}
|
|
98
|
+
unlockTaskRequest={[Function]}
|
|
99
|
+
/>
|
|
100
|
+
<TaskInfo
|
|
101
|
+
action=""
|
|
102
|
+
canEdit={true}
|
|
103
|
+
dynflowEnableConsole={true}
|
|
104
|
+
endedAt=""
|
|
105
|
+
errors={Array []}
|
|
106
|
+
forceCancelTaskRequest={[Function]}
|
|
107
|
+
hasSubTasks={true}
|
|
108
|
+
help=""
|
|
109
|
+
id="test"
|
|
110
|
+
output=""
|
|
111
|
+
parentTask="parent-id"
|
|
112
|
+
progress={0}
|
|
113
|
+
result="error"
|
|
114
|
+
startAt=""
|
|
115
|
+
startBefore=""
|
|
116
|
+
startedAt=""
|
|
117
|
+
state="paused"
|
|
118
|
+
status="RESOLVED"
|
|
119
|
+
taskProgressToggle={[MockFunction]}
|
|
120
|
+
taskReload={true}
|
|
121
|
+
taskReloadStart={[MockFunction]}
|
|
122
|
+
unlockTaskRequest={[Function]}
|
|
123
|
+
username=""
|
|
124
|
+
usernamePath=""
|
|
125
|
+
/>
|
|
148
126
|
</Fragment>
|
|
149
127
|
`;
|
|
@@ -9,6 +9,7 @@ import RunningSteps from './Components/RunningSteps';
|
|
|
9
9
|
import Errors from './Components/Errors';
|
|
10
10
|
import Locks from './Components/Locks';
|
|
11
11
|
import Raw from './Components/Raw';
|
|
12
|
+
import Dependencies from './Components/Dependencies';
|
|
12
13
|
import { getTaskID } from './TasksDetailsHelper';
|
|
13
14
|
import { TaskSkeleton } from './Components/TaskSkeleton';
|
|
14
15
|
|
|
@@ -20,6 +21,8 @@ const TaskDetails = ({
|
|
|
20
21
|
runningSteps,
|
|
21
22
|
locks,
|
|
22
23
|
links,
|
|
24
|
+
dependsOn,
|
|
25
|
+
blocks,
|
|
23
26
|
cancelStep,
|
|
24
27
|
taskReloadStart,
|
|
25
28
|
taskReloadStop,
|
|
@@ -90,7 +93,10 @@ const TaskDetails = ({
|
|
|
90
93
|
<Tab eventKey={4} disabled={isLoading} title={__('Locks')}>
|
|
91
94
|
<Locks locks={locks.concat(links)} />
|
|
92
95
|
</Tab>
|
|
93
|
-
<Tab eventKey={5} disabled={isLoading} title={__('
|
|
96
|
+
<Tab eventKey={5} disabled={isLoading} title={__('Dependencies')}>
|
|
97
|
+
<Dependencies dependsOn={dependsOn} blocks={blocks} />
|
|
98
|
+
</Tab>
|
|
99
|
+
<Tab eventKey={6} disabled={isLoading} title={__('Raw')}>
|
|
94
100
|
<Raw
|
|
95
101
|
id={id}
|
|
96
102
|
label={props.label}
|
|
@@ -116,9 +122,12 @@ TaskDetails.propTypes = {
|
|
|
116
122
|
taskReloadStop: PropTypes.func.isRequired,
|
|
117
123
|
taskReloadStart: PropTypes.func.isRequired,
|
|
118
124
|
links: PropTypes.array,
|
|
125
|
+
dependsOn: PropTypes.array,
|
|
126
|
+
blocks: PropTypes.array,
|
|
119
127
|
...Task.propTypes,
|
|
120
128
|
...Errors.propTypes,
|
|
121
129
|
...Locks.propTypes,
|
|
130
|
+
...Dependencies.propTypes,
|
|
122
131
|
...Raw.propTypes,
|
|
123
132
|
};
|
|
124
133
|
TaskDetails.defaultProps = {
|
|
@@ -127,10 +136,13 @@ TaskDetails.defaultProps = {
|
|
|
127
136
|
APIerror: null,
|
|
128
137
|
status: STATUS.PENDING,
|
|
129
138
|
links: [],
|
|
139
|
+
dependsOn: [],
|
|
140
|
+
blocks: [],
|
|
130
141
|
...Task.defaultProps,
|
|
131
142
|
...RunningSteps.defaultProps,
|
|
132
143
|
...Errors.defaultProps,
|
|
133
144
|
...Locks.defaultProps,
|
|
145
|
+
...Dependencies.defaultProps,
|
|
134
146
|
...Raw.defaultProps,
|
|
135
147
|
};
|
|
136
148
|
|
|
@@ -109,3 +109,9 @@ export const selectAPIError = state =>
|
|
|
109
109
|
export const selectIsLoading = state =>
|
|
110
110
|
!!selectAPIByKey(state, FOREMAN_TASK_DETAILS).response &&
|
|
111
111
|
selectStatus(state) === STATUS.PENDING;
|
|
112
|
+
|
|
113
|
+
export const selectDependsOn = state =>
|
|
114
|
+
selectTaskDetailsResponse(state).depends_on || [];
|
|
115
|
+
|
|
116
|
+
export const selectBlocks = state =>
|
|
117
|
+
selectTaskDetailsResponse(state).blocks || [];
|
data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/TaskDetails.test.js.snap
CHANGED
|
@@ -58,6 +58,16 @@ exports[`TaskDetails rendering render with loading Props 1`] = `
|
|
|
58
58
|
<Tab
|
|
59
59
|
disabled={true}
|
|
60
60
|
eventKey={5}
|
|
61
|
+
title="Dependencies"
|
|
62
|
+
>
|
|
63
|
+
<Dependencies
|
|
64
|
+
blocks={Array []}
|
|
65
|
+
dependsOn={Array []}
|
|
66
|
+
/>
|
|
67
|
+
</Tab>
|
|
68
|
+
<Tab
|
|
69
|
+
disabled={true}
|
|
70
|
+
eventKey={6}
|
|
61
71
|
title="Raw"
|
|
62
72
|
>
|
|
63
73
|
<Raw
|
|
@@ -136,6 +146,15 @@ exports[`TaskDetails rendering render with min Props 1`] = `
|
|
|
136
146
|
</Tab>
|
|
137
147
|
<Tab
|
|
138
148
|
eventKey={5}
|
|
149
|
+
title="Dependencies"
|
|
150
|
+
>
|
|
151
|
+
<Dependencies
|
|
152
|
+
blocks={Array []}
|
|
153
|
+
dependsOn={Array []}
|
|
154
|
+
/>
|
|
155
|
+
</Tab>
|
|
156
|
+
<Tab
|
|
157
|
+
eventKey={6}
|
|
139
158
|
title="Raw"
|
|
140
159
|
>
|
|
141
160
|
<Raw
|
|
@@ -36,6 +36,8 @@ import {
|
|
|
36
36
|
selectStatus,
|
|
37
37
|
selectAPIError,
|
|
38
38
|
selectIsLoading,
|
|
39
|
+
selectDependsOn,
|
|
40
|
+
selectBlocks,
|
|
39
41
|
} from './TaskDetailsSelectors';
|
|
40
42
|
|
|
41
43
|
const mapStateToProps = state => ({
|
|
@@ -71,6 +73,8 @@ const mapStateToProps = state => ({
|
|
|
71
73
|
status: selectStatus(state),
|
|
72
74
|
APIerror: selectAPIError(state),
|
|
73
75
|
isLoading: selectIsLoading(state),
|
|
76
|
+
dependsOn: selectDependsOn(state),
|
|
77
|
+
blocks: selectBlocks(state),
|
|
74
78
|
});
|
|
75
79
|
|
|
76
80
|
const mapDispatchToProps = dispatch =>
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import classNames from 'classnames';
|
|
4
4
|
|
|
5
|
-
import { Button, Tooltip, TooltipPosition } from '@patternfly/react-core';
|
|
5
|
+
import { Button, Tooltip, TooltipPosition, Icon } from '@patternfly/react-core';
|
|
6
6
|
import { InfoCircleIcon } from '@patternfly/react-icons';
|
|
7
7
|
import { translate as __ } from 'foremanReact/common/I18n';
|
|
8
8
|
import {
|
|
@@ -25,8 +25,10 @@ export const OtherInfo = ({ updateQuery, otherCount, query }) => {
|
|
|
25
25
|
position={TooltipPosition.bottom}
|
|
26
26
|
>
|
|
27
27
|
<span>
|
|
28
|
-
<
|
|
29
|
-
|
|
28
|
+
<Icon isInline>
|
|
29
|
+
<InfoCircleIcon />
|
|
30
|
+
</Icon>{' '}
|
|
31
|
+
{__('Other:')}
|
|
30
32
|
</span>
|
|
31
33
|
</Tooltip>
|
|
32
34
|
<Button
|
|
@@ -1,14 +1,63 @@
|
|
|
1
|
-
import
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom';
|
|
2
4
|
|
|
3
5
|
import { OtherInfo } from './OtherInfo';
|
|
6
|
+
import {
|
|
7
|
+
TASKS_DASHBOARD_AVAILABLE_QUERY_STATES,
|
|
8
|
+
TASKS_DASHBOARD_AVAILABLE_QUERY_RESULTS,
|
|
9
|
+
} from '../../../../TasksDashboardConstants';
|
|
4
10
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
jest.mock('foremanReact/common/I18n', () => ({
|
|
12
|
+
translate: str => str,
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
const { STOPPED } = TASKS_DASHBOARD_AVAILABLE_QUERY_STATES;
|
|
16
|
+
const { OTHER } = TASKS_DASHBOARD_AVAILABLE_QUERY_RESULTS;
|
|
17
|
+
|
|
18
|
+
const defaultProps = {
|
|
19
|
+
updateQuery: jest.fn(),
|
|
20
|
+
otherCount: 7,
|
|
21
|
+
query: { state: STOPPED, result: OTHER },
|
|
11
22
|
};
|
|
12
23
|
|
|
13
|
-
describe('OtherInfo', () =>
|
|
14
|
-
|
|
24
|
+
describe('OtherInfo', () => {
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
defaultProps.updateQuery.mockClear();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('renders Other label, count', () => {
|
|
30
|
+
render(<OtherInfo {...defaultProps} />);
|
|
31
|
+
expect(screen.getByText('Other:')).toBeInTheDocument();
|
|
32
|
+
expect(screen.getByText('7')).toBeInTheDocument();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('calls updateQuery with STOPPED and OTHER when the count button is clicked', () => {
|
|
36
|
+
render(<OtherInfo {...defaultProps} />);
|
|
37
|
+
const button = screen.getByRole('button', { name: '7' });
|
|
38
|
+
fireEvent.click(button);
|
|
39
|
+
expect(defaultProps.updateQuery).toHaveBeenCalledWith({
|
|
40
|
+
state: STOPPED,
|
|
41
|
+
result: OTHER,
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('applies other-active class when query state is STOPPED and result is OTHER', () => {
|
|
46
|
+
const { container } = render(
|
|
47
|
+
<OtherInfo {...defaultProps} query={{ state: STOPPED, result: OTHER }} />
|
|
48
|
+
);
|
|
49
|
+
const wrapper = container.querySelector('.other-active');
|
|
50
|
+
expect(wrapper).toBeInTheDocument();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('does not apply other-active class when query result is not OTHER', () => {
|
|
54
|
+
const { container } = render(
|
|
55
|
+
<OtherInfo
|
|
56
|
+
{...defaultProps}
|
|
57
|
+
query={{ state: STOPPED, result: 'error' }}
|
|
58
|
+
/>
|
|
59
|
+
);
|
|
60
|
+
const wrapper = container.querySelector('.other-active');
|
|
61
|
+
expect(wrapper).not.toBeInTheDocument();
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -1,19 +1,5 @@
|
|
|
1
1
|
.stopped-tasks-card {
|
|
2
2
|
.stopped-table {
|
|
3
|
-
width: 100%;
|
|
4
|
-
border: none;
|
|
5
|
-
table-layout: fixed;
|
|
6
|
-
|
|
7
|
-
thead {
|
|
8
|
-
background-color: transparent;
|
|
9
|
-
background-image: none;
|
|
10
|
-
tr {
|
|
11
|
-
th {
|
|
12
|
-
font-weight: bold;
|
|
13
|
-
border: none;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
3
|
|
|
18
4
|
td,
|
|
19
5
|
th {
|
|
@@ -29,12 +15,6 @@
|
|
|
29
15
|
td {
|
|
30
16
|
&.active {
|
|
31
17
|
font-weight: bold;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
.data-col {
|
|
35
|
-
&:hover {
|
|
36
|
-
cursor: pointer;
|
|
37
|
-
border: 1px solid #d1d1d1;
|
|
38
18
|
background-color: #def3fe;
|
|
39
19
|
}
|
|
40
20
|
}
|
|
@@ -43,14 +23,4 @@
|
|
|
43
23
|
.other-active {
|
|
44
24
|
text-decoration: underline;
|
|
45
25
|
}
|
|
46
|
-
|
|
47
|
-
.pficon {
|
|
48
|
-
margin-right: 5px;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
.btn-link {
|
|
52
|
-
font-size: 14px;
|
|
53
|
-
padding-top: 0;
|
|
54
|
-
padding-bottom: 0;
|
|
55
|
-
}
|
|
56
26
|
}
|
|
@@ -1,15 +1,196 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
TASKS_DASHBOARD_AVAILABLE_QUERY_STATES,
|
|
7
|
+
TASKS_DASHBOARD_AVAILABLE_QUERY_MODES,
|
|
8
|
+
TASKS_DASHBOARD_AVAILABLE_TIMES,
|
|
9
|
+
} from '../../../../TasksDashboardConstants';
|
|
3
10
|
import StoppedTasksCard from './StoppedTasksCard';
|
|
4
11
|
|
|
5
|
-
const { STOPPED } = TASKS_DASHBOARD_AVAILABLE_QUERY_STATES;
|
|
12
|
+
const { STOPPED, RUNNING } = TASKS_DASHBOARD_AVAILABLE_QUERY_STATES;
|
|
13
|
+
const { LAST } = TASKS_DASHBOARD_AVAILABLE_QUERY_MODES;
|
|
14
|
+
const { WEEK } = TASKS_DASHBOARD_AVAILABLE_TIMES;
|
|
6
15
|
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
16
|
+
const defaultTableData = {
|
|
17
|
+
results: {
|
|
18
|
+
error: { total: 9, last: 1 },
|
|
19
|
+
warning: { total: 8, last: 2 },
|
|
20
|
+
success: { total: 7, last: 3 },
|
|
11
21
|
},
|
|
22
|
+
other: 0,
|
|
12
23
|
};
|
|
13
24
|
|
|
14
|
-
describe('StoppedTasksCard', () =>
|
|
15
|
-
|
|
25
|
+
describe('StoppedTasksCard', () => {
|
|
26
|
+
describe('rendering', () => {
|
|
27
|
+
it('renders card with base classes and id', () => {
|
|
28
|
+
const { container } = render(<StoppedTasksCard />);
|
|
29
|
+
const card = container.querySelector('#stopped-tasks-card');
|
|
30
|
+
expect(screen.getByText('Stopped')).toBeInTheDocument();
|
|
31
|
+
expect(card).toBeInTheDocument();
|
|
32
|
+
expect(card).toHaveClass('tasks-donut-card');
|
|
33
|
+
expect(card).toHaveClass('stopped-tasks-card');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('renders selected state when query.state is STOPPED', () => {
|
|
37
|
+
const { container } = render(
|
|
38
|
+
<StoppedTasksCard query={{ state: STOPPED }} />
|
|
39
|
+
);
|
|
40
|
+
expect(screen.getByText('Stopped')).toBeInTheDocument();
|
|
41
|
+
const card = container.querySelector('#stopped-tasks-card');
|
|
42
|
+
expect(card).toHaveClass('selected-tasks-card');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('renders not-focused state when query.state is not STOPPED', () => {
|
|
46
|
+
const { container } = render(
|
|
47
|
+
<StoppedTasksCard query={{ state: RUNNING }} />
|
|
48
|
+
);
|
|
49
|
+
const card = container.querySelector('#stopped-tasks-card');
|
|
50
|
+
expect(card).toHaveClass('not-focused');
|
|
51
|
+
expect(card).not.toHaveClass('selected-tasks-card');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('renders StoppedTable with Error, Warning, Success labels', () => {
|
|
55
|
+
render(<StoppedTasksCard />);
|
|
56
|
+
expect(screen.getByText('Error')).toBeInTheDocument();
|
|
57
|
+
expect(screen.getByText('Warning')).toBeInTheDocument();
|
|
58
|
+
expect(screen.getByText('Success')).toBeInTheDocument();
|
|
59
|
+
expect(screen.getByText('Other:')).toBeInTheDocument();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('renders data counts from props', () => {
|
|
63
|
+
const data = {
|
|
64
|
+
results: {
|
|
65
|
+
error: { total: 5, last: 2 },
|
|
66
|
+
warning: { total: 3, last: 1 },
|
|
67
|
+
success: { total: 10, last: 4 },
|
|
68
|
+
},
|
|
69
|
+
other: 7,
|
|
70
|
+
};
|
|
71
|
+
render(<StoppedTasksCard data={data} />);
|
|
72
|
+
expect(screen.getByText('5')).toBeInTheDocument();
|
|
73
|
+
expect(screen.getByText('2')).toBeInTheDocument();
|
|
74
|
+
expect(screen.getByText('3')).toBeInTheDocument();
|
|
75
|
+
expect(screen.getByText('1')).toBeInTheDocument();
|
|
76
|
+
expect(screen.getByText('10')).toBeInTheDocument();
|
|
77
|
+
expect(screen.getByText('4')).toBeInTheDocument();
|
|
78
|
+
expect(screen.getByText('7')).toBeInTheDocument();
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('user interactions', () => {
|
|
83
|
+
it('calls updateQuery with state STOPPED when title is clicked', () => {
|
|
84
|
+
const updateQuery = jest.fn();
|
|
85
|
+
render(<StoppedTasksCard updateQuery={updateQuery} />);
|
|
86
|
+
fireEvent.click(screen.getByText('Stopped'));
|
|
87
|
+
expect(updateQuery).toHaveBeenCalledWith({ state: STOPPED });
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('StoppedTable (via StoppedTasksCard)', () => {
|
|
92
|
+
it('renders table with data and time', () => {
|
|
93
|
+
render(
|
|
94
|
+
<StoppedTasksCard
|
|
95
|
+
data={defaultTableData}
|
|
96
|
+
time={WEEK}
|
|
97
|
+
query={{}}
|
|
98
|
+
updateQuery={jest.fn()}
|
|
99
|
+
/>
|
|
100
|
+
);
|
|
101
|
+
expect(screen.getByText('Total')).toBeInTheDocument();
|
|
102
|
+
expect(screen.getByText('week')).toBeInTheDocument();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('applies active class to Total cell when query has state STOPPED and result (total selected)', () => {
|
|
106
|
+
const { container } = render(
|
|
107
|
+
<StoppedTasksCard
|
|
108
|
+
data={defaultTableData}
|
|
109
|
+
time={WEEK}
|
|
110
|
+
query={{ state: STOPPED, result: 'error' }}
|
|
111
|
+
updateQuery={jest.fn()}
|
|
112
|
+
/>
|
|
113
|
+
);
|
|
114
|
+
const table = container.querySelector('.stopped-table');
|
|
115
|
+
expect(table).toBeInTheDocument();
|
|
116
|
+
const activeCells = table.querySelectorAll('.data-col.active');
|
|
117
|
+
expect(activeCells).toHaveLength(1);
|
|
118
|
+
expect(activeCells[0].textContent).toBe('9');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
['warning', 'success'].forEach(result => {
|
|
122
|
+
it(`applies active class to Total cell when result is ${result} (total selected)`, () => {
|
|
123
|
+
const { container } = render(
|
|
124
|
+
<StoppedTasksCard
|
|
125
|
+
data={defaultTableData}
|
|
126
|
+
time={WEEK}
|
|
127
|
+
query={{ state: STOPPED, result }}
|
|
128
|
+
updateQuery={jest.fn()}
|
|
129
|
+
/>
|
|
130
|
+
);
|
|
131
|
+
const table = container.querySelector('.stopped-table');
|
|
132
|
+
const activeCells = table.querySelectorAll('.data-col.active');
|
|
133
|
+
expect(activeCells.length).toBe(1);
|
|
134
|
+
const expectedTotal = result === 'warning' ? '8' : '7';
|
|
135
|
+
expect(activeCells[0].textContent).toBe(expectedTotal);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('applies active class to Last time cell when query has result, mode LAST and time (last selected)', () => {
|
|
140
|
+
const { container } = render(
|
|
141
|
+
<StoppedTasksCard
|
|
142
|
+
data={defaultTableData}
|
|
143
|
+
time={WEEK}
|
|
144
|
+
query={{
|
|
145
|
+
state: STOPPED,
|
|
146
|
+
result: 'error',
|
|
147
|
+
mode: LAST,
|
|
148
|
+
time: WEEK,
|
|
149
|
+
}}
|
|
150
|
+
updateQuery={jest.fn()}
|
|
151
|
+
/>
|
|
152
|
+
);
|
|
153
|
+
const table = container.querySelector('.stopped-table');
|
|
154
|
+
const activeCells = table.querySelectorAll('.data-col.active');
|
|
155
|
+
expect(activeCells).toHaveLength(1);
|
|
156
|
+
expect(activeCells[0].textContent).toBe('1');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('calls updateQuery with state and result when Total is clicked', () => {
|
|
160
|
+
const updateQuery = jest.fn();
|
|
161
|
+
render(
|
|
162
|
+
<StoppedTasksCard
|
|
163
|
+
data={defaultTableData}
|
|
164
|
+
time={WEEK}
|
|
165
|
+
query={{}}
|
|
166
|
+
updateQuery={updateQuery}
|
|
167
|
+
/>
|
|
168
|
+
);
|
|
169
|
+
fireEvent.click(screen.getByRole('button', { name: '9' }));
|
|
170
|
+
expect(updateQuery).toHaveBeenCalledWith({
|
|
171
|
+
state: STOPPED,
|
|
172
|
+
result: 'error',
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('calls updateQuery with state, result, mode LAST and time when Last time is clicked', () => {
|
|
177
|
+
const updateQuery = jest.fn();
|
|
178
|
+
render(
|
|
179
|
+
<StoppedTasksCard
|
|
180
|
+
data={defaultTableData}
|
|
181
|
+
time={WEEK}
|
|
182
|
+
query={{}}
|
|
183
|
+
updateQuery={updateQuery}
|
|
184
|
+
/>
|
|
185
|
+
);
|
|
186
|
+
const lastButtons = screen.getAllByRole('button', { name: '1' });
|
|
187
|
+
fireEvent.click(lastButtons[0]);
|
|
188
|
+
expect(updateQuery).toHaveBeenCalledWith({
|
|
189
|
+
state: STOPPED,
|
|
190
|
+
result: 'error',
|
|
191
|
+
mode: LAST,
|
|
192
|
+
time: WEEK,
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
});
|