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,36 +1,59 @@
1
- import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
2
4
 
3
5
  import Errors from '../Errors';
4
6
 
5
- const fixtures = {
6
- 'render without Props': {},
7
- 'render with Props': {
8
- executionPlan: {
9
- state: 'paused',
10
- cancellable: false,
11
- },
12
- failedSteps: [
13
- {
14
- error: {
15
- exception_class: 'RuntimeError',
16
- message:
17
- 'Action Actions::Katello::EventQueue::Monitor is already active',
18
- backtrace: [
19
- "/home/vagrant/.gem/ruby/gems/dynflow-1.2.3/lib/dynflow/action/singleton.rb:15:in `rescue in singleton_lock!'",
20
- "/home/vagrant/.gem/ruby/gems/dynflow-1.2.3/lib/dynflow/action/singleton.rb:12:in `singleton_lock!'",
21
- ],
22
- },
23
- action_class: 'Actions::Katello::EventQueue::Monitor',
24
- state: 'error',
25
- input:
26
- '{"locale"=>"en",\n "current_request_id"=>nil,\n "current_user_id"=>4,\n "current_organization_id"=>nil,\n "current_location_id"=>nil}\n',
27
- output: '{}\n',
28
- },
7
+ const failedStepFixture = {
8
+ error: {
9
+ exception_class: 'RuntimeError',
10
+ message: 'Action Actions::Katello::EventQueue::Monitor is already active',
11
+ backtrace: [
12
+ "/home/vagrant/.gem/ruby/gems/dynflow-1.2.3/lib/dynflow/action/singleton.rb:15:in `rescue in singleton_lock!'",
13
+ "/home/vagrant/.gem/ruby/gems/dynflow-1.2.3/lib/dynflow/action/singleton.rb:12:in `singleton_lock!'",
29
14
  ],
30
15
  },
16
+ action_class: 'Actions::Katello::EventQueue::Monitor',
17
+ state: 'error',
18
+ input:
19
+ '{"locale"=>"en",\n "current_request_id"=>nil,\n "current_user_id"=>4,\n "current_organization_id"=>nil,\n "current_location_id"=>nil}\n',
20
+ output: '{}\n',
31
21
  };
32
22
 
33
23
  describe('Errors', () => {
34
- describe('rendering', () =>
35
- testComponentSnapshotsWithFixtures(Errors, fixtures));
24
+ it('renders warning when execution plan is missing', () => {
25
+ render(<Errors failedSteps={[]} executionPlan={null} />);
26
+ expect(
27
+ screen.getByText(/execution plan data not available/i)
28
+ ).toBeInTheDocument();
29
+ });
30
+
31
+ it('renders success state when there are no failed steps', () => {
32
+ render(<Errors failedSteps={[]} executionPlan={{ state: 'paused' }} />);
33
+ const noErrors = screen.getAllByText(/^no errors$/i);
34
+ expect(noErrors.length).toBeGreaterThanOrEqual(1);
35
+ });
36
+
37
+ it('renders failed step details when failedSteps is non-empty', () => {
38
+ const { container } = render(
39
+ <Errors
40
+ executionPlan={{ state: 'paused', cancellable: false }}
41
+ failedSteps={[failedStepFixture]}
42
+ />
43
+ );
44
+ const stepAlert = container.querySelector(
45
+ '[data-ouia-component-id="task-error-0"]'
46
+ );
47
+ expect(stepAlert).toBeInTheDocument();
48
+ expect(stepAlert).toHaveClass('pf-m-inline');
49
+ expect(
50
+ screen.getByText('Actions::Katello::EventQueue::Monitor')
51
+ ).toBeInTheDocument();
52
+ expect(screen.getByText(/runtimeerror/i)).toBeInTheDocument();
53
+ expect(
54
+ screen.getByText(
55
+ /action actions::katello::eventqueue::monitor is already active/i
56
+ )
57
+ ).toBeInTheDocument();
58
+ });
36
59
  });
@@ -1,28 +1,261 @@
1
- import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
2
4
 
3
5
  import Locks from '../Locks';
4
6
 
5
- const fixtures = {
6
- 'render without Props': {},
7
- 'render with Props': {
8
- locks: [
9
- {
10
- name: 'task_owner',
11
- exclusive: false,
12
- resource_type: 'User',
13
- resource_id: 4,
14
- },
15
- {
16
- name: 'task_owner2',
17
- exclusive: false,
18
- resource_type: 'User',
19
- resource_id: 2,
20
- },
21
- ],
22
- },
23
- };
24
-
25
7
  describe('Locks', () => {
26
- describe('rendering', () =>
27
- testComponentSnapshotsWithFixtures(Locks, fixtures));
8
+ it('renders empty state when there are no locks', () => {
9
+ const { container } = render(<Locks locks={[]} />);
10
+ expect(
11
+ screen.getByRole('heading', { name: /no resources/i })
12
+ ).toBeInTheDocument();
13
+ expect(
14
+ screen.getByText(
15
+ /no resources currently associated with this task/i
16
+ )
17
+ ).toBeInTheDocument();
18
+ });
19
+
20
+ it('renders non-exclusive section with rows when there are only non-exclusive locks', () => {
21
+ render(
22
+ <Locks
23
+ locks={[
24
+ {
25
+ name: 'task_owner',
26
+ exclusive: false,
27
+ resource_type: 'User',
28
+ resource_id: 4,
29
+ link: null,
30
+ },
31
+ {
32
+ name: 'task_owner2',
33
+ exclusive: false,
34
+ resource_type: 'User',
35
+ resource_id: 2,
36
+ link: null,
37
+ },
38
+ ]}
39
+ />
40
+ );
41
+ expect(
42
+ screen.getByRole('heading', { name: 'Non-exclusive resources' })
43
+ ).toBeInTheDocument();
44
+ expect(
45
+ screen.getByText(
46
+ /other tasks can access the resource simultaneously/i
47
+ )
48
+ ).toBeInTheDocument();
49
+ expect(screen.getAllByText('User')).toHaveLength(2);
50
+ expect(screen.getByText('id: 4')).toBeInTheDocument();
51
+ expect(screen.getByText('id: 2')).toBeInTheDocument();
52
+ expect(
53
+ screen.queryByRole('heading', { name: 'Exclusive resources' })
54
+ ).not.toBeInTheDocument();
55
+ });
56
+
57
+ it('renders exclusive section when there are only exclusive locks', () => {
58
+ render(
59
+ <Locks
60
+ locks={[
61
+ {
62
+ name: 'host_lock',
63
+ exclusive: true,
64
+ resource_type: 'Host',
65
+ resource_id: 1,
66
+ link: '/hosts/1',
67
+ },
68
+ ]}
69
+ />
70
+ );
71
+ expect(
72
+ screen.getByRole('heading', { name: 'Exclusive resources' })
73
+ ).toBeInTheDocument();
74
+ expect(
75
+ screen.getByText(
76
+ /only this task can access the resource/i
77
+ )
78
+ ).toBeInTheDocument();
79
+ expect(screen.getByRole('link', { name: 'Host' })).toHaveAttribute(
80
+ 'href',
81
+ '/hosts/1'
82
+ );
83
+ expect(screen.getByText('id: 1')).toBeInTheDocument();
84
+ expect(
85
+ screen.queryByRole('heading', { name: 'Non-exclusive resources' })
86
+ ).not.toBeInTheDocument();
87
+ });
88
+
89
+ it('renders both sections when locks are mixed', () => {
90
+ render(
91
+ <Locks
92
+ locks={[
93
+ {
94
+ name: 'a',
95
+ exclusive: false,
96
+ resource_type: 'Smart proxy',
97
+ resource_id: 7,
98
+ link: null,
99
+ },
100
+ {
101
+ name: 'b',
102
+ exclusive: true,
103
+ resource_type: 'Host managed',
104
+ resource_id: 1,
105
+ link: null,
106
+ },
107
+ ]}
108
+ />
109
+ );
110
+ expect(
111
+ screen.getByRole('heading', { name: 'Non-exclusive resources' })
112
+ ).toBeInTheDocument();
113
+ expect(
114
+ screen.getByRole('heading', { name: 'Exclusive resources' })
115
+ ).toBeInTheDocument();
116
+ expect(
117
+ screen.getByText(
118
+ /other tasks can access the resource simultaneously/i
119
+ )
120
+ ).toBeInTheDocument();
121
+ expect(
122
+ screen.getByText(/only this task can access the resource/i)
123
+ ).toBeInTheDocument();
124
+ expect(screen.getByText('Smart proxy')).toBeInTheDocument();
125
+ expect(screen.getByText('Host managed')).toBeInTheDocument();
126
+ expect(screen.getByText('id: 7')).toBeInTheDocument();
127
+ expect(screen.getByText('id: 1')).toBeInTheDocument();
128
+ });
129
+
130
+ it('renders a link for non-exclusive locks when lock.link is set', () => {
131
+ render(
132
+ <Locks
133
+ locks={[
134
+ {
135
+ name: 'proxy',
136
+ exclusive: false,
137
+ resource_type: 'Smart proxy',
138
+ resource_id: 7,
139
+ link: '/smart_proxies/7',
140
+ },
141
+ ]}
142
+ />
143
+ );
144
+ const link = screen.getByRole('link', { name: 'Smart proxy' });
145
+ expect(link).toHaveAttribute('href', '/smart_proxies/7');
146
+ });
147
+
148
+ it('formats string resource_id values in the row label', () => {
149
+ render(
150
+ <Locks
151
+ locks={[
152
+ {
153
+ name: 'x',
154
+ exclusive: false,
155
+ resource_type: 'Custom',
156
+ resource_id: 'uuid-abc',
157
+ link: null,
158
+ },
159
+ ]}
160
+ />
161
+ );
162
+ expect(screen.getByText('id: uuid-abc')).toBeInTheDocument();
163
+ });
164
+
165
+ it('sets ouia ids on populated container, tables, rows, and resource links', () => {
166
+ const { container } = render(
167
+ <Locks
168
+ locks={[
169
+ {
170
+ name: 'ne',
171
+ exclusive: false,
172
+ resource_type: 'A',
173
+ resource_id: 1,
174
+ link: '/non-exclusive/1',
175
+ },
176
+ {
177
+ name: 'ex',
178
+ exclusive: true,
179
+ resource_type: 'B',
180
+ resource_id: 2,
181
+ link: '/exclusive/2',
182
+ },
183
+ ]}
184
+ />
185
+ );
186
+ expect(
187
+ container.querySelector('[data-ouia-component-id="task-locks-populated"]')
188
+ ).toBeInTheDocument();
189
+ expect(
190
+ container.querySelector(
191
+ '[data-ouia-component-id="task-locks-non-exclusive-table"]'
192
+ )
193
+ ).toBeInTheDocument();
194
+ expect(
195
+ container.querySelector(
196
+ '[data-ouia-component-id="task-locks-exclusive-table"]'
197
+ )
198
+ ).toBeInTheDocument();
199
+ expect(
200
+ container.querySelector(
201
+ '[data-ouia-component-id="task-locks-non-exclusive-row-0"]'
202
+ )
203
+ ).toBeInTheDocument();
204
+ expect(
205
+ container.querySelector(
206
+ '[data-ouia-component-id="task-locks-exclusive-row-0"]'
207
+ )
208
+ ).toBeInTheDocument();
209
+ expect(
210
+ container.querySelector(
211
+ '[data-ouia-component-id="task-locks-non-exclusive-resource-type-link-0"]'
212
+ )
213
+ ).toBeInTheDocument();
214
+ expect(
215
+ container.querySelector(
216
+ '[data-ouia-component-id="task-locks-exclusive-resource-type-link-0"]'
217
+ )
218
+ ).toBeInTheDocument();
219
+ });
220
+
221
+ it('does not render resource_type as a link when lock.link is absent', () => {
222
+ render(
223
+ <Locks
224
+ locks={[
225
+ {
226
+ name: 'ex',
227
+ exclusive: true,
228
+ resource_type: 'Host managed',
229
+ resource_id: 1,
230
+ link: null,
231
+ },
232
+ ]}
233
+ />
234
+ );
235
+ expect(
236
+ screen.queryByRole('link', { name: 'Host managed' })
237
+ ).not.toBeInTheDocument();
238
+ expect(screen.getByText('Host managed')).toBeInTheDocument();
239
+ });
240
+
241
+ it('does not set resource-type link ouia id when lock.link is absent', () => {
242
+ const { container } = render(
243
+ <Locks
244
+ locks={[
245
+ {
246
+ name: 'ne',
247
+ exclusive: false,
248
+ resource_type: 'Thing',
249
+ resource_id: 1,
250
+ link: null,
251
+ },
252
+ ]}
253
+ />
254
+ );
255
+ expect(
256
+ container.querySelector(
257
+ '[data-ouia-component-id="task-locks-non-exclusive-resource-type-link-0"]'
258
+ )
259
+ ).not.toBeInTheDocument();
260
+ });
28
261
  });
@@ -1,27 +1,49 @@
1
- import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
2
4
 
3
5
  import Raw from '../Raw';
4
6
 
5
- const fixtures = {
6
- 'render without Props': {},
7
- 'render with Props': {
8
- startedAt: '2019-06-17 16:04:09 +0300',
9
- endedAt: '2019-06-17 16:05:09 +0300',
10
- id: '6b0d6db2-e9ab-40da-94e5-b6842ac50bd0',
11
- label: 'Actions::Katello::EventQueue::Monitor',
12
- input: {
13
- locale: 'en',
14
- current_request_id: 1,
15
- current_user_id: 4,
16
- current_organization_id: 2,
17
- current_location_id: 3,
18
- },
19
- output: {},
20
- externalId: 'test',
21
- },
22
- };
23
-
24
7
  describe('Raw', () => {
25
- describe('rendering', () =>
26
- testComponentSnapshotsWithFixtures(Raw, fixtures));
8
+ it('renders id and label when minimal props are given', () => {
9
+ render(
10
+ <Raw
11
+ id="x"
12
+ label=""
13
+ startedAt=""
14
+ endedAt=""
15
+ input={[]}
16
+ output={{}}
17
+ externalId=""
18
+ />
19
+ );
20
+ expect(screen.getByText('x')).toBeInTheDocument();
21
+ });
22
+
23
+ it('renders raw task fields from props', () => {
24
+ render(
25
+ <Raw
26
+ startedAt="2019-06-17 16:04:09 +0300"
27
+ endedAt="2019-06-17 16:05:09 +0300"
28
+ id="6b0d6db2-e9ab-40da-94e5-b6842ac50bd0"
29
+ label="Actions::Katello::EventQueue::Monitor"
30
+ input={{
31
+ locale: 'en',
32
+ current_request_id: 1,
33
+ current_user_id: 4,
34
+ current_organization_id: 2,
35
+ current_location_id: 3,
36
+ }}
37
+ output={{}}
38
+ externalId="test"
39
+ />
40
+ );
41
+ expect(
42
+ screen.getByText('6b0d6db2-e9ab-40da-94e5-b6842ac50bd0')
43
+ ).toBeInTheDocument();
44
+ expect(
45
+ screen.getByText('Actions::Katello::EventQueue::Monitor')
46
+ ).toBeInTheDocument();
47
+ expect(screen.getByText(/"locale":/)).toBeInTheDocument();
48
+ });
27
49
  });
@@ -1,36 +1,68 @@
1
- import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
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 RunningSteps from '../RunningSteps';
4
6
 
5
- const minProps = {
7
+ const baseProps = {
6
8
  id: 'task-id1',
7
9
  taskReload: true,
8
10
  cancelStep: jest.fn(),
9
11
  taskReloadStart: jest.fn(),
10
12
  };
11
- const fixtures = {
12
- 'render with min Props': minProps,
13
- 'render with Props': {
14
- ...minProps,
15
- executionPlan: {
16
- state: 'paused',
17
- cancellable: false,
18
- },
19
- runningSteps: [
20
- {
21
- cancellable: false,
22
- id: 1,
23
- action_class: 'test',
24
- state: 'paused',
25
- input:
26
- '{"locale"=>"en",\n "current_request_id"=>nil,\n "current_user_id"=>4,\n "current_organization_id"=>nil,\n "current_location_id"=>nil}\n',
27
- output: '{}\n',
28
- },
29
- ],
30
- },
13
+
14
+ const sampleStep = {
15
+ cancellable: false,
16
+ id: 1,
17
+ action_class: 'test',
18
+ state: 'paused',
19
+ input:
20
+ '{"locale"=>"en",\n "current_request_id"=>nil,\n "current_user_id"=>4,\n "current_organization_id"=>nil,\n "current_location_id"=>nil}\n',
21
+ output: '{}\n',
31
22
  };
32
23
 
33
24
  describe('RunningSteps', () => {
34
- describe('rendering', () =>
35
- testComponentSnapshotsWithFixtures(RunningSteps, fixtures));
25
+ it('renders empty message when there are no running steps', () => {
26
+ render(<RunningSteps {...baseProps} runningSteps={[]} />);
27
+ expect(screen.getByText(/no running steps/i)).toBeInTheDocument();
28
+ });
29
+
30
+ it('renders running step fields and Cancel when step is cancellable', () => {
31
+ const cancelStep = jest.fn();
32
+ render(
33
+ <RunningSteps
34
+ {...baseProps}
35
+ cancelStep={cancelStep}
36
+ runningSteps={[
37
+ {
38
+ ...sampleStep,
39
+ cancellable: true,
40
+ id: 99,
41
+ },
42
+ ]}
43
+ />
44
+ );
45
+ expect(
46
+ screen.getByRole('heading', { name: 'Warning alert: Running step 1' })
47
+ ).toBeInTheDocument();
48
+ expect(screen.getByText('test')).toBeInTheDocument();
49
+ expect(screen.getByText('paused')).toBeInTheDocument();
50
+ fireEvent.click(screen.getByRole('button', { name: /cancel/i }));
51
+ expect(cancelStep).toHaveBeenCalledWith('task-id1', 99);
52
+ });
53
+
54
+ it('uses sprintf so each running step title shows a 1-based step index', () => {
55
+ render(
56
+ <RunningSteps
57
+ {...baseProps}
58
+ runningSteps={[sampleStep, { ...sampleStep, id: 2, state: 'running' }]}
59
+ />
60
+ );
61
+ expect(
62
+ screen.getByRole('heading', { name: 'Warning alert: Running step 1' })
63
+ ).toBeInTheDocument();
64
+ expect(
65
+ screen.getByRole('heading', { name: 'Warning alert: Running step 2' })
66
+ ).toBeInTheDocument();
67
+ });
36
68
  });
@@ -1,26 +1,46 @@
1
- import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
2
4
  import { STATUS } from 'foremanReact/constants';
5
+
3
6
  import Task from '../Task';
4
7
 
5
- const fixtures = {
6
- 'render with minimal Props': {
7
- id: 'test',
8
- taskReloadStart: jest.fn(),
9
- taskProgressToggle: jest.fn(),
10
- },
11
- 'render with some Props': {
12
- id: 'test',
13
- state: 'paused',
14
- hasSubTasks: true,
15
- dynflowEnableConsole: true,
16
- parentTask: 'parent-id',
17
- taskReload: true,
18
- canEdit: true,
19
- status: STATUS.RESOLVED,
20
- taskProgressToggle: jest.fn(),
21
- taskReloadStart: jest.fn(),
22
- },
23
- };
8
+ describe('Task', () => {
9
+ it('renders task controls from TaskButtons with minimal props', () => {
10
+ render(
11
+ <Task
12
+ id="test"
13
+ taskReloadStart={jest.fn()}
14
+ taskProgressToggle={jest.fn()}
15
+ />
16
+ );
17
+ expect(
18
+ screen.getByRole('button', { name: /start auto-reloading/i })
19
+ ).toBeInTheDocument();
20
+ });
24
21
 
25
- describe('Task rendering', () =>
26
- testComponentSnapshotsWithFixtures(Task, fixtures));
22
+ it('renders parent task and sub tasks links when provided', () => {
23
+ render(
24
+ <Task
25
+ id="test"
26
+ state="paused"
27
+ hasSubTasks
28
+ dynflowEnableConsole
29
+ parentTask="parent-id"
30
+ taskReload
31
+ canEdit
32
+ status={STATUS.RESOLVED}
33
+ taskProgressToggle={jest.fn()}
34
+ taskReloadStart={jest.fn()}
35
+ />
36
+ );
37
+ expect(screen.getByRole('link', { name: /parent task/i })).toHaveAttribute(
38
+ 'href',
39
+ '/foreman_tasks/tasks/parent-id'
40
+ );
41
+ expect(screen.getByRole('link', { name: /sub tasks/i })).toHaveAttribute(
42
+ 'href',
43
+ '/foreman_tasks/tasks/test/sub_tasks'
44
+ );
45
+ });
46
+ });