foreman-tasks 12.1.1 → 12.2.2

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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/app/views/foreman_tasks/api/tasks/show.json.rabl +1 -1
  4. data/foreman-tasks.gemspec +1 -3
  5. data/lib/foreman_tasks/version.rb +1 -1
  6. data/test/controllers/api/tasks_controller_test.rb +2 -2
  7. data/webpack/ForemanTasks/Components/TaskDetails/Components/Dependencies.js +69 -58
  8. data/webpack/ForemanTasks/Components/TaskDetails/Components/Errors.js +25 -5
  9. data/webpack/ForemanTasks/Components/TaskDetails/Components/Locks.js +170 -43
  10. data/webpack/ForemanTasks/Components/TaskDetails/Components/RunningSteps.js +12 -4
  11. data/webpack/ForemanTasks/Components/TaskDetails/Components/TaskInfo.js +217 -201
  12. data/webpack/ForemanTasks/Components/TaskDetails/Components/TaskSkeleton.js +30 -34
  13. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Dependencies.test.js +90 -29
  14. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Errors.test.js +50 -27
  15. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Locks.test.js +256 -23
  16. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Raw.test.js +44 -22
  17. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/RunningSteps.test.js +56 -24
  18. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Task.test.js +42 -22
  19. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/TaskInfo.test.js +45 -54
  20. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetails.js +65 -25
  21. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/TaskDetails.test.js +37 -13
  22. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/TaskDetailsActions.test.js +53 -8
  23. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/PausedTasksCard/PausedTasksCard.test.js +53 -7
  24. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/RunningTasksCard/RunningTasksCard.test.js +53 -7
  25. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.js +5 -4
  26. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.test.js +44 -14
  27. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.js +6 -5
  28. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.js +5 -4
  29. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.scss +3 -8
  30. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.test.js +75 -34
  31. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChart.test.js +57 -34
  32. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChartHelper.test.js +23 -9
  33. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/TasksCardsGrid.fixtures.js +14 -11
  34. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/TasksCardsGrid.js +19 -21
  35. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/TasksCardsGrid.test.js +63 -14
  36. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/Components/TimeDropDown/TimeDropDown.js +9 -17
  37. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/Components/TimeDropDown/TimeDropDown.test.js +49 -13
  38. metadata +2 -46
  39. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Errors.test.js.snap +0 -77
  40. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Locks.test.js.snap +0 -116
  41. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Raw.test.js.snap +0 -174
  42. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/RunningSteps.test.js.snap +0 -62
  43. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Task.test.js.snap +0 -127
  44. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/TaskInfo.test.js.snap +0 -580
  45. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/TaskDetails.test.js.snap +0 -172
  46. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/TaskDetailsActions.test.js.snap +0 -52
  47. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/PausedTasksCard/__snapshots__/PausedTasksCard.test.js.snap +0 -38
  48. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/RunningTasksCard/__snapshots__/RunningTasksCard.test.js.snap +0 -38
  49. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/__snapshots__/ScheduledTasksCard.test.js.snap +0 -97
  50. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/__snapshots__/TasksDonutCard.test.js.snap +0 -183
  51. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/__snapshots__/TasksDonutChart.test.js.snap +0 -302
  52. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/__snapshots__/TasksDonutChartHelper.test.js.snap +0 -21
  53. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/__snapshots__/TasksCardsGrid.test.js.snap +0 -210
  54. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/Components/TimeDropDown/__snapshots__/TimeDropDown.test.js.snap +0 -85
@@ -1,208 +1,227 @@
1
- import React, { Component } from 'react';
1
+ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { Grid, Row, Col, ProgressBar } from 'patternfly-react';
3
+ import {
4
+ Grid,
5
+ GridItem,
6
+ Progress,
7
+ ProgressVariant,
8
+ Icon,
9
+ } from '@patternfly/react-core';
10
+ import {
11
+ CheckCircleIcon,
12
+ ExclamationCircleIcon,
13
+ ExclamationTriangleIcon,
14
+ QuestionCircleIcon,
15
+ } from '@patternfly/react-icons';
4
16
  import { translate as __ } from 'foremanReact/common/I18n';
5
17
  import RelativeDateTime from 'foremanReact/components/common/dates/RelativeDateTime';
6
18
 
7
- class TaskInfo extends Component {
8
- isDelayed = () => {
9
- const startAt = new Date(this.props.startAt);
10
- const startedAt = new Date(this.props.startedAt);
11
- startAt.setMilliseconds(0);
12
- startedAt.setMilliseconds(0);
13
- return startAt.getTime() !== startedAt.getTime();
14
- };
15
- resultIcon = () => {
16
- if (this.props.state !== 'stopped') return 'task-status pficon-help';
17
- const { result } = this.props;
18
- let icon;
19
- switch (result) {
20
- case 'success':
21
- icon = 'pficon-ok';
22
- break;
23
- case 'error':
24
- icon = 'pficon-error-circle-o';
25
- break;
26
- case 'warning':
27
- icon = 'pficon-ok status-warn';
28
- break;
29
- default:
30
- icon = 'pficon-help';
31
- break;
32
- }
33
- return `task-status ${icon}`;
34
- };
35
-
36
- progressBarType = () => {
37
- const { result } = this.props;
38
- switch (result) {
39
- case 'error':
40
- return 'danger';
41
- case 'success':
42
- return 'success';
43
- case 'warning':
44
- return 'warning';
45
- default:
46
- return null;
47
- }
48
- };
49
-
50
- render() {
51
- const {
52
- action,
53
- endedAt,
54
- result,
55
- startAt,
56
- startBefore,
57
- startedAt,
58
- state,
59
- help,
60
- output,
61
- errors,
62
- progress,
63
- username,
64
- usernamePath,
65
- } = this.props;
19
+ const isDelayed = ({ startAt, startedAt }) => {
20
+ if (
21
+ startAt == null ||
22
+ startedAt == null ||
23
+ startAt === '' ||
24
+ startedAt === ''
25
+ ) {
26
+ return false;
27
+ }
28
+ const a = new Date(startAt);
29
+ const b = new Date(startedAt);
30
+ if (Number.isNaN(a.getTime()) || Number.isNaN(b.getTime())) {
31
+ return false;
32
+ }
33
+ a.setMilliseconds(0);
34
+ b.setMilliseconds(0);
35
+ return a.getTime() !== b.getTime();
36
+ };
66
37
 
67
- const details = [
68
- [
69
- {
70
- title: 'Name',
71
- value: action || __('N/A'),
72
- className: 'details-name',
73
- },
74
- {
75
- title: 'Start at',
76
- value: <RelativeDateTime defaultValue={__('N/A')} date={startAt} />,
77
- },
78
- ],
79
- [
80
- {
81
- title: 'Result',
82
- value: (
83
- <React.Fragment>
84
- <i className={this.resultIcon()} />
85
- <span> {result}</span>
86
- </React.Fragment>
87
- ),
88
- },
89
- {
90
- title: 'Started at',
91
- value: <RelativeDateTime defaultValue={__('N/A')} date={startedAt} />,
92
- },
93
- ],
94
- [
95
- {
96
- title: 'Triggered by',
97
- value: (usernamePath || '').includes('href') ? (
98
- <a href={usernamePath.split('"')[1]}>{username}</a>
99
- ) : (
100
- username || ''
101
- ),
102
- },
103
- {
104
- title: 'Ended at',
105
- value: <RelativeDateTime defaultValue={__('N/A')} date={endedAt} />,
106
- },
107
- ],
108
- [
109
- {
110
- title: 'Execution type',
111
- value: __(this.isDelayed() ? 'Delayed' : 'Immediate'),
112
- },
113
- {
114
- title: 'Start before',
115
- value: startBefore ? (
116
- <RelativeDateTime defaultValue={__('N/A')} date={startBefore} />
117
- ) : (
118
- '-'
119
- ),
120
- },
121
- ],
122
- ];
38
+ const resultIconEl = (state, result) => {
39
+ if (state !== 'stopped')
123
40
  return (
124
- <Grid>
125
- <br />
126
- {details.map((items, key) => (
127
- <Row key={key}>
128
- <Col md={2} sm={6}>
129
- <span className="list-group-item-heading">
130
- {__(items[0].title)}:
131
- </span>
132
- </Col>
133
- <Col md={5} sm={6} className={items[0].className}>
134
- <span>{items[0].value}</span>
135
- </Col>
136
- <Col md={2} sm={6}>
137
- <span className="list-group-item-heading">
138
- {__(items[1].title)}:
139
- </span>
140
- </Col>
141
- <Col md={3} sm={6} className={items[1].className}>
142
- {items[1].value}
143
- </Col>
144
- </Row>
145
- ))}
146
- <br />
147
- <Row>
148
- <Col xs={6}>
149
- <div className="progress-description">
150
- <span className="list-group-item-heading">{__('State')}: </span>
151
- {state}
152
- </div>
153
- </Col>
154
- <Col xs={3} xsOffset={3} className="progress-label-top-right">
155
- <span>{`${progress}% ${__('Complete')}`}</span>
156
- </Col>
157
- <Col xs={12}>
158
- <ProgressBar
159
- now={progress}
160
- bsStyle={this.progressBarType()}
161
- striped
162
- />
163
- </Col>
164
- </Row>
165
- <br />
166
- {help && (
167
- <Row>
168
- <Col xs={12}>
169
- <p>
170
- <span>
171
- <b>{__('Troubleshooting')}</b>
172
- </span>
173
- </p>
174
- <p dangerouslySetInnerHTML={{ __html: help }} />
175
- </Col>
176
- </Row>
177
- )}
178
- {output && output.length > 0 && (
179
- <Row>
180
- <Col xs={12}>
181
- <p>
182
- <span>
183
- <b>{__('Output:')}</b>
184
- </span>
185
- </p>
186
- <pre>{output}</pre>
187
- </Col>
188
- </Row>
189
- )}
190
- {errors && errors.length > 0 && (
191
- <Row>
192
- <Col xs={12}>
193
- <div>
194
- <span>
195
- <b>{__('Errors:')}</b>
196
- </span>
197
- </div>
198
- <pre>{errors}</pre>
199
- </Col>
200
- </Row>
201
- )}
202
- </Grid>
41
+ <Icon>
42
+ <QuestionCircleIcon />
43
+ </Icon>
203
44
  );
45
+ switch (result) {
46
+ case 'success':
47
+ return (
48
+ <Icon status="success">
49
+ <CheckCircleIcon />
50
+ </Icon>
51
+ );
52
+ case 'error':
53
+ return (
54
+ <Icon status="danger">
55
+ <ExclamationCircleIcon />
56
+ </Icon>
57
+ );
58
+ case 'warning':
59
+ return (
60
+ <Icon status="warning">
61
+ <ExclamationTriangleIcon />
62
+ </Icon>
63
+ );
64
+ default:
65
+ return (
66
+ <Icon>
67
+ <QuestionCircleIcon />
68
+ </Icon>
69
+ );
70
+ }
71
+ };
72
+
73
+ const progressVariantForResult = result => {
74
+ switch (result) {
75
+ case 'error':
76
+ return ProgressVariant.danger;
77
+ case 'success':
78
+ return ProgressVariant.success;
79
+ case 'warning':
80
+ return ProgressVariant.warning;
81
+ default:
82
+ return undefined;
204
83
  }
205
- }
84
+ };
85
+
86
+ const TaskInfo = props => {
87
+ const {
88
+ action,
89
+ endedAt,
90
+ result,
91
+ startAt,
92
+ startBefore,
93
+ startedAt,
94
+ state,
95
+ help,
96
+ output,
97
+ errors,
98
+ progress,
99
+ username,
100
+ usernamePath,
101
+ } = props;
102
+
103
+ const details = [
104
+ [
105
+ {
106
+ title: 'Name',
107
+ value: action || __('N/A'),
108
+ className: 'details-name',
109
+ },
110
+ {
111
+ title: 'Start at',
112
+ value: <RelativeDateTime defaultValue={__('N/A')} date={startAt} />,
113
+ },
114
+ ],
115
+ [
116
+ {
117
+ title: 'Result',
118
+ value: (
119
+ <span>
120
+ {resultIconEl(state, result)} {result}
121
+ </span>
122
+ ),
123
+ },
124
+ {
125
+ title: 'Started at',
126
+ value: <RelativeDateTime defaultValue={__('N/A')} date={startedAt} />,
127
+ },
128
+ ],
129
+ [
130
+ {
131
+ title: 'Triggered by',
132
+ value: (usernamePath || '').includes('href') ? (
133
+ <a href={usernamePath.split('"')[1]}>{username}</a>
134
+ ) : (
135
+ username || ''
136
+ ),
137
+ },
138
+ {
139
+ title: 'Ended at',
140
+ value: <RelativeDateTime defaultValue={__('N/A')} date={endedAt} />,
141
+ },
142
+ ],
143
+ [
144
+ {
145
+ title: 'Execution type',
146
+ value: __(isDelayed(props) ? 'Delayed' : 'Immediate'),
147
+ },
148
+ {
149
+ title: 'Start before',
150
+ value: startBefore ? (
151
+ <RelativeDateTime defaultValue={__('N/A')} date={startBefore} />
152
+ ) : (
153
+ '-'
154
+ ),
155
+ },
156
+ ],
157
+ ];
158
+
159
+ const progVariant = progressVariantForResult(result);
160
+
161
+ return (
162
+ <Grid>
163
+ <GridItem span={12} className="pf-v5-u-pb-lg" />
164
+ {details.map((items, key) => (
165
+ <React.Fragment key={key}>
166
+ <GridItem md={2} sm={6}>
167
+ <span className="list-group-item-heading">
168
+ {__(items[0].title)}:
169
+ </span>
170
+ </GridItem>
171
+ <GridItem md={5} sm={6} className={items[0].className}>
172
+ <span>{items[0].value}</span>
173
+ </GridItem>
174
+ <GridItem md={2} sm={6}>
175
+ <span className="list-group-item-heading">
176
+ {__(items[1].title)}:
177
+ </span>
178
+ </GridItem>
179
+ <GridItem md={3} sm={6} className={items[1].className}>
180
+ {items[1].value}
181
+ </GridItem>
182
+ </React.Fragment>
183
+ ))}
184
+ <GridItem span={12} className="pf-v5-u-pb-lg" />
185
+ <GridItem span={6}>
186
+ <div className="progress-description">
187
+ <span className="list-group-item-heading">{__('State')}: </span>
188
+ {state}
189
+ </div>
190
+ </GridItem>
191
+ <GridItem span={6} className="progress-label-top-right">
192
+ <span>{`${progress}% ${__('Complete')}`}</span>
193
+ </GridItem>
194
+ <GridItem span={12}>
195
+ <Progress
196
+ value={progress}
197
+ max={100}
198
+ aria-label={`${progress}% ${__('Complete')}`}
199
+ measureLocation="outside"
200
+ {...(progVariant ? { variant: progVariant } : {})}
201
+ />
202
+ </GridItem>
203
+ <GridItem span={12} className="pf-v5-u-pb-lg" />
204
+ {help && (
205
+ <GridItem span={12}>
206
+ <b>{__('Troubleshooting')}</b>
207
+ <p dangerouslySetInnerHTML={{ __html: help }} />
208
+ </GridItem>
209
+ )}
210
+ {output && output.length > 0 && (
211
+ <GridItem span={12}>
212
+ <b>{__('Output:')}</b>
213
+ <pre>{output}</pre>
214
+ </GridItem>
215
+ )}
216
+ {errors && errors.length > 0 && (
217
+ <GridItem span={12}>
218
+ <b>{__('Errors:')}</b>
219
+ <pre>{errors}</pre>
220
+ </GridItem>
221
+ )}
222
+ </Grid>
223
+ );
224
+ };
206
225
 
207
226
  TaskInfo.propTypes = {
208
227
  action: PropTypes.string,
@@ -217,10 +236,7 @@ TaskInfo.propTypes = {
217
236
  progress: PropTypes.number,
218
237
  username: PropTypes.string,
219
238
  usernamePath: PropTypes.string,
220
- output: PropTypes.PropTypes.oneOfType([
221
- PropTypes.string,
222
- PropTypes.shape({}),
223
- ]),
239
+ output: PropTypes.oneOfType([PropTypes.string, PropTypes.shape({})]),
224
240
  };
225
241
 
226
242
  TaskInfo.defaultProps = {
@@ -1,48 +1,44 @@
1
1
  import React from 'react';
2
2
  import Skeleton from 'react-loading-skeleton';
3
- import { Grid, Row, Col } from 'patternfly-react';
3
+ import { Grid, GridItem } from '@patternfly/react-core';
4
4
 
5
5
  export const TaskSkeleton = () => {
6
- const details = [1, 2, 3, 4, 5, 6];
6
+ const details = [1, 2, 3, 4];
7
7
  return (
8
- <Grid>
9
- <br />
10
- <Row>
11
- <Col>
12
- <Skeleton />
13
- </Col>
14
- </Row>
15
- {details.map((items, key) => (
16
- <Row key={key}>
17
- <Col md={2} sm={6}>
8
+ <Grid hasGutter>
9
+ <GridItem span={12} className="pf-v5-u-pb-lg" />
10
+ <GridItem span={12}>
11
+ <Skeleton />
12
+ </GridItem>
13
+ {details.map(key => (
14
+ <React.Fragment key={key}>
15
+ <GridItem md={2} sm={6}>
18
16
  <Skeleton />
19
- </Col>
20
- <Col md={5} sm={6}>
17
+ </GridItem>
18
+ <GridItem md={5} sm={6}>
21
19
  <Skeleton />
22
- </Col>
23
- <Col md={2} sm={6}>
20
+ </GridItem>
21
+ <GridItem md={2} sm={6}>
24
22
  <Skeleton />
25
- </Col>
26
- <Col md={3} sm={6}>
23
+ </GridItem>
24
+ <GridItem md={3} sm={6}>
27
25
  <Skeleton />
28
- </Col>
29
- </Row>
26
+ </GridItem>
27
+ </React.Fragment>
30
28
  ))}
31
- <br />
32
- <Row>
33
- <Col xs={6}>
34
- <div className="progress-description">
35
- <Skeleton />
36
- </div>
37
- </Col>
38
- <Col xs={3} xsOffset={3} className="progress-label-top-right">
39
- <Skeleton />
40
- </Col>
41
- <Col xs={12}>
29
+ <GridItem span={12} className="pf-v5-u-pb-lg" />
30
+ <GridItem span={6}>
31
+ <div className="progress-description">
42
32
  <Skeleton />
43
- </Col>
44
- </Row>
45
- <br />
33
+ </div>
34
+ </GridItem>
35
+ <GridItem span={6} className="progress-label-top-right">
36
+ <Skeleton />
37
+ </GridItem>
38
+ <GridItem span={12}>
39
+ <Skeleton />
40
+ </GridItem>
41
+ <GridItem span={12} className="pf-v5-u-pb-lg" />
46
42
  </Grid>
47
43
  );
48
44
  };
@@ -1,17 +1,25 @@
1
1
  import React from 'react';
2
- import { mount } from 'enzyme';
2
+ import { render, screen, within } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
4
+
3
5
  import Dependencies from '../Dependencies';
4
6
 
5
7
  describe('Dependencies', () => {
6
- it('should render with no dependencies', () => {
7
- const wrapper = mount(<Dependencies dependsOn={[]} blocks={[]} />);
8
- expect(wrapper.find('Alert')).toHaveLength(1);
9
- expect(wrapper.find('DependencyTable')).toHaveLength(2);
10
- expect(wrapper.find('Table')).toHaveLength(0);
11
- expect(wrapper.text()).toContain('None');
8
+ it('renders both sections with empty placeholders when there are no tasks', () => {
9
+ render(<Dependencies dependsOn={[]} blocks={[]} />);
10
+
11
+ expect(
12
+ screen.getByRole('heading', { name: /task depends on/i })
13
+ ).toBeInTheDocument();
14
+ expect(
15
+ screen.getByRole('heading', { name: /task blocks/i })
16
+ ).toBeInTheDocument();
17
+
18
+ expect(screen.getAllByText(/^none$/i)).toHaveLength(2);
19
+ expect(screen.queryByRole('grid')).not.toBeInTheDocument();
12
20
  });
13
21
 
14
- it('should render with depends_on dependencies', () => {
22
+ it('renders depends-on tasks in the first grid', () => {
15
23
  const dependsOn = [
16
24
  {
17
25
  id: '123',
@@ -28,16 +36,39 @@ describe('Dependencies', () => {
28
36
  result: 'pending',
29
37
  },
30
38
  ];
31
- const wrapper = mount(<Dependencies dependsOn={dependsOn} blocks={[]} />);
32
- expect(wrapper.find('Table')).toHaveLength(1);
33
- expect(wrapper.find('Tbody').find('Tr')).toHaveLength(2);
34
- expect(wrapper.text()).toContain('Foo Bar Action');
35
- expect(wrapper.text()).toContain('Baz Qux Action');
36
- expect(wrapper.text()).toContain('stopped');
37
- expect(wrapper.text()).toContain('success');
39
+
40
+ render(<Dependencies dependsOn={dependsOn} blocks={[]} />);
41
+
42
+ const dependsGrid = screen.getByRole('grid', {
43
+ name: /task depends on/i,
44
+ });
45
+ expect(screen.queryAllByRole('grid')).toHaveLength(1);
46
+
47
+ expect(
48
+ within(dependsGrid).getByRole('columnheader', { name: /^name$/i })
49
+ ).toBeInTheDocument();
50
+
51
+ const bodyRows = within(dependsGrid)
52
+ .getAllByRole('row')
53
+ .slice(1);
54
+ expect(bodyRows).toHaveLength(2);
55
+
56
+ expect(
57
+ screen.getByRole('link', { name: 'Foo Bar Action' })
58
+ ).toHaveAttribute('href', '/foreman_tasks/tasks/123');
59
+ expect(
60
+ screen.getByRole('link', { name: 'Baz Qux Action' })
61
+ ).toHaveAttribute('href', '/foreman_tasks/tasks/456');
62
+
63
+ expect(within(dependsGrid).getByText('Stopped')).toBeInTheDocument();
64
+ expect(within(dependsGrid).getByText('Success')).toBeInTheDocument();
65
+ expect(within(dependsGrid).getByText('Running')).toBeInTheDocument();
66
+ expect(within(dependsGrid).getByText('Pending')).toBeInTheDocument();
67
+
68
+ expect(screen.getAllByText(/^none$/i)).toHaveLength(1);
38
69
  });
39
70
 
40
- it('should render with blocks dependencies', () => {
71
+ it('renders blocks in the second grid', () => {
41
72
  const blocks = [
42
73
  {
43
74
  id: '789',
@@ -47,15 +78,28 @@ describe('Dependencies', () => {
47
78
  result: 'warning',
48
79
  },
49
80
  ];
50
- const wrapper = mount(<Dependencies dependsOn={[]} blocks={blocks} />);
51
- expect(wrapper.find('Table')).toHaveLength(1);
52
- expect(wrapper.find('Tbody').find('Tr')).toHaveLength(1);
53
- expect(wrapper.text()).toContain('Test Action');
54
- expect(wrapper.text()).toContain('paused');
55
- expect(wrapper.text()).toContain('warning');
81
+
82
+ render(<Dependencies dependsOn={[]} blocks={blocks} />);
83
+
84
+ const blocksGrid = screen.getByRole('grid', { name: /task blocks/i });
85
+ expect(screen.queryAllByRole('grid')).toHaveLength(1);
86
+
87
+ const bodyRows = within(blocksGrid)
88
+ .getAllByRole('row')
89
+ .slice(1);
90
+ expect(bodyRows).toHaveLength(1);
91
+
92
+ expect(screen.getByRole('link', { name: 'Test Action' })).toHaveAttribute(
93
+ 'href',
94
+ '/foreman_tasks/tasks/789'
95
+ );
96
+ expect(within(blocksGrid).getByText('Paused')).toBeInTheDocument();
97
+ expect(within(blocksGrid).getByText('Warning')).toBeInTheDocument();
98
+
99
+ expect(screen.getAllByText(/^none$/i)).toHaveLength(1);
56
100
  });
57
101
 
58
- it('should render with both dependency types', () => {
102
+ it('renders both grids when dependsOn and blocks are present', () => {
59
103
  const dependsOn = [
60
104
  {
61
105
  id: '123',
@@ -81,12 +125,29 @@ describe('Dependencies', () => {
81
125
  result: 'error',
82
126
  },
83
127
  ];
84
- const wrapper = mount(
85
- <Dependencies dependsOn={dependsOn} blocks={blocks} />
128
+
129
+ render(<Dependencies dependsOn={dependsOn} blocks={blocks} />);
130
+
131
+ expect(
132
+ screen.getByRole('grid', { name: /task depends on/i })
133
+ ).toBeInTheDocument();
134
+ expect(
135
+ screen.getByRole('grid', { name: /task blocks/i })
136
+ ).toBeInTheDocument();
137
+
138
+ expect(screen.getByRole('link', { name: 'Foo Action' })).toHaveAttribute(
139
+ 'href',
140
+ '/foreman_tasks/tasks/123'
141
+ );
142
+ expect(screen.getByRole('link', { name: 'Bar Action' })).toHaveAttribute(
143
+ 'href',
144
+ '/foreman_tasks/tasks/456'
86
145
  );
87
- expect(wrapper.find('Table')).toHaveLength(2);
88
- expect(wrapper.text()).toContain('Foo Action');
89
- expect(wrapper.text()).toContain('Bar Action');
90
- expect(wrapper.text()).toContain('Baz Action');
146
+ expect(screen.getByRole('link', { name: 'Baz Action' })).toHaveAttribute(
147
+ 'href',
148
+ '/foreman_tasks/tasks/789'
149
+ );
150
+
151
+ expect(screen.queryByText(/^none$/i)).not.toBeInTheDocument();
91
152
  });
92
153
  });