foreman-tasks 12.1.1 → 12.2.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.
Files changed (50) 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/Errors.js +25 -5
  8. data/webpack/ForemanTasks/Components/TaskDetails/Components/RunningSteps.js +12 -4
  9. data/webpack/ForemanTasks/Components/TaskDetails/Components/TaskInfo.js +217 -201
  10. data/webpack/ForemanTasks/Components/TaskDetails/Components/TaskSkeleton.js +30 -34
  11. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Dependencies.test.js +30 -30
  12. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Errors.test.js +50 -27
  13. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Raw.test.js +44 -22
  14. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/RunningSteps.test.js +56 -24
  15. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Task.test.js +42 -22
  16. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/TaskInfo.test.js +45 -54
  17. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetails.js +65 -25
  18. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/TaskDetails.test.js +37 -13
  19. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/TaskDetailsActions.test.js +53 -8
  20. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/PausedTasksCard/PausedTasksCard.test.js +53 -7
  21. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/RunningTasksCard/RunningTasksCard.test.js +53 -7
  22. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.js +5 -4
  23. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.test.js +44 -14
  24. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.js +6 -5
  25. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.js +5 -4
  26. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.scss +3 -8
  27. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.test.js +75 -34
  28. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChart.test.js +57 -34
  29. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChartHelper.test.js +23 -9
  30. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/TasksCardsGrid.fixtures.js +14 -11
  31. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/TasksCardsGrid.js +19 -21
  32. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/TasksCardsGrid.test.js +63 -14
  33. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/Components/TimeDropDown/TimeDropDown.js +9 -17
  34. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/Components/TimeDropDown/TimeDropDown.test.js +49 -13
  35. metadata +2 -45
  36. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Errors.test.js.snap +0 -77
  37. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Raw.test.js.snap +0 -174
  38. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/RunningSteps.test.js.snap +0 -62
  39. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Task.test.js.snap +0 -127
  40. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/TaskInfo.test.js.snap +0 -580
  41. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/TaskDetails.test.js.snap +0 -172
  42. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/TaskDetailsActions.test.js.snap +0 -52
  43. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/PausedTasksCard/__snapshots__/PausedTasksCard.test.js.snap +0 -38
  44. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/RunningTasksCard/__snapshots__/RunningTasksCard.test.js.snap +0 -38
  45. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/__snapshots__/ScheduledTasksCard.test.js.snap +0 -97
  46. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/__snapshots__/TasksDonutCard.test.js.snap +0 -183
  47. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/__snapshots__/TasksDonutChart.test.js.snap +0 -302
  48. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/__snapshots__/TasksDonutChartHelper.test.js.snap +0 -21
  49. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/__snapshots__/TasksCardsGrid.test.js.snap +0 -210
  50. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/Components/TimeDropDown/__snapshots__/TimeDropDown.test.js.snap +0 -85
@@ -1,17 +1,20 @@
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 info alert and None for both tables when there are no tasks', () => {
9
+ render(<Dependencies dependsOn={[]} blocks={[]} />);
10
+ expect(
11
+ screen.getByRole('heading', { name: /task dependencies/i })
12
+ ).toBeInTheDocument();
13
+ const noneLabels = screen.getAllByText(/^none$/i);
14
+ expect(noneLabels.length).toBeGreaterThanOrEqual(2);
12
15
  });
13
16
 
14
- it('should render with depends_on dependencies', () => {
17
+ it('renders depends_on tasks in the first table', () => {
15
18
  const dependsOn = [
16
19
  {
17
20
  id: '123',
@@ -28,16 +31,15 @@ describe('Dependencies', () => {
28
31
  result: 'pending',
29
32
  },
30
33
  ];
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');
34
+ render(<Dependencies dependsOn={dependsOn} blocks={[]} />);
35
+ const table = screen.getByRole('grid', { name: /depends on/i });
36
+ expect(within(table).getByText('Foo Bar Action')).toBeInTheDocument();
37
+ expect(within(table).getByText('Baz Qux Action')).toBeInTheDocument();
38
+ expect(within(table).getByText('stopped')).toBeInTheDocument();
39
+ expect(within(table).getByText('success')).toBeInTheDocument();
38
40
  });
39
41
 
40
- it('should render with blocks dependencies', () => {
42
+ it('renders blocks in the second table', () => {
41
43
  const blocks = [
42
44
  {
43
45
  id: '789',
@@ -47,15 +49,14 @@ describe('Dependencies', () => {
47
49
  result: 'warning',
48
50
  },
49
51
  ];
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');
52
+ render(<Dependencies dependsOn={[]} blocks={blocks} />);
53
+ const table = screen.getByRole('grid', { name: /^blocks$/i });
54
+ expect(within(table).getByText('Test Action')).toBeInTheDocument();
55
+ expect(within(table).getByText('paused')).toBeInTheDocument();
56
+ expect(within(table).getByText('warning')).toBeInTheDocument();
56
57
  });
57
58
 
58
- it('should render with both dependency types', () => {
59
+ it('renders both tables when dependsOn and blocks are present', () => {
59
60
  const dependsOn = [
60
61
  {
61
62
  id: '123',
@@ -81,12 +82,11 @@ describe('Dependencies', () => {
81
82
  result: 'error',
82
83
  },
83
84
  ];
84
- const wrapper = mount(
85
- <Dependencies dependsOn={dependsOn} blocks={blocks} />
86
- );
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');
85
+ render(<Dependencies dependsOn={dependsOn} blocks={blocks} />);
86
+ expect(screen.getByRole('grid', { name: /depends on/i })).toBeInTheDocument();
87
+ expect(screen.getByText('Foo Action')).toBeInTheDocument();
88
+ expect(screen.getByRole('grid', { name: /^blocks$/i })).toBeInTheDocument();
89
+ expect(screen.getByText('Bar Action')).toBeInTheDocument();
90
+ expect(screen.getByText('Baz Action')).toBeInTheDocument();
91
91
  });
92
92
  });
@@ -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,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
+ });
@@ -1,59 +1,50 @@
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
- import TaskInfo from '../TaskInfo';
5
+ jest.mock('foremanReact/components/common/dates/RelativeDateTime', () => {
6
+ const RelativeDateTime = ({ date, defaultValue }) => (
7
+ <span>{date || defaultValue}</span>
8
+ );
9
+ return RelativeDateTime;
10
+ });
4
11
 
5
- const fixtures = {
6
- 'render without Props': { id: 'test' },
7
- 'render with Props': {
8
- id: 'test',
9
- startAt: '2019-06-17 16:04:09 +0300',
10
- label: 'Actions::Katello::EventQueue::Monitor',
11
- pending: true,
12
- action: 'Monitor Event Queue',
13
- username: 'admin',
14
- startedAt: '2019-06-17 16:04:09 +0300',
15
- endedAt: null,
16
- state: 'paused',
17
- result: 'error',
18
- progress: 0.5,
19
- input: {
20
- locale: 'en',
21
- current_request_id: null,
22
- current_timezone: 'Asia/Jerusalem',
23
- current_user_id: 4,
24
- current_organization_id: null,
25
- current_location_id: null,
26
- },
27
- output: {},
28
- humanized: {
29
- action: 'Monitor Event Queue',
30
- input: '',
31
- output: '',
32
- errors: [
33
- 'Action Actions::Katello::EventQueue::Monitor is already active',
34
- ],
35
- },
36
- cli_example: null,
37
- executionPlan: {
38
- state: 'paused',
39
- cancellable: false,
40
- },
41
- help:
42
- "A paused task represents a process that has not finished properly. Any task in paused state can lead to potential inconsistency and needs to be resolved.\nThe recommended approach is to investigate the error messages below and in 'errors' tab, address the primary cause of the issue and resume the task.",
43
- hasSubTasks: false,
44
- locks: [
45
- {
46
- name: 'task_owner',
47
- exclusive: false,
48
- resource_type: 'User',
49
- resource_id: 4,
50
- },
51
- ],
52
- username_path: '<a href="/users/4-admin/edit">admin</a>',
53
- },
54
- };
12
+ import TaskInfo from '../TaskInfo';
55
13
 
56
14
  describe('TaskInfo', () => {
57
- describe('rendering', () =>
58
- testComponentSnapshotsWithFixtures(TaskInfo, fixtures));
15
+ it('renders default metadata labels when given minimal props', () => {
16
+ render(<TaskInfo id="test" />);
17
+ expect(screen.getByText(/name:/i)).toBeInTheDocument();
18
+ expect(screen.getByText(/start at:/i)).toBeInTheDocument();
19
+ expect(screen.getByText(/result:/i)).toBeInTheDocument();
20
+ });
21
+
22
+ it('renders task fields and troubleshooting when full props are provided', () => {
23
+ render(
24
+ <TaskInfo
25
+ id="test"
26
+ startAt="2019-06-17 16:04:09 +0300"
27
+ label="Actions::Katello::EventQueue::Monitor"
28
+ action="Monitor Event Queue"
29
+ username="admin"
30
+ startedAt="2019-06-17 16:04:09 +0300"
31
+ endedAt={null}
32
+ state="paused"
33
+ result="error"
34
+ progress={0.5}
35
+ help={
36
+ "A paused task represents a process that has not finished properly. Any task in paused state can lead to potential inconsistency and needs to be resolved.\nThe recommended approach is to investigate the error messages below and in 'errors' tab, address the primary cause of the issue and resume the task."
37
+ }
38
+ output={{}}
39
+ usernamePath=""
40
+ />
41
+ );
42
+ expect(screen.getByText('Monitor Event Queue')).toBeInTheDocument();
43
+ expect(screen.getByText(/troubleshooting/i)).toBeInTheDocument();
44
+ expect(
45
+ screen.getByText(/a paused task represents a process/i)
46
+ ).toBeInTheDocument();
47
+ expect(screen.getByText(/^paused$/)).toBeInTheDocument();
48
+ expect(screen.getByText(/^error$/)).toBeInTheDocument();
49
+ });
59
50
  });
@@ -1,6 +1,6 @@
1
- import React, { useEffect } from 'react';
1
+ import React, { useEffect, useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { Tab, Tabs } from 'patternfly-react';
3
+ import { Tabs, Tab, TabTitleText } from '@patternfly/react-core';
4
4
  import { translate as __, sprintf } from 'foremanReact/common/I18n';
5
5
  import { STATUS } from 'foremanReact/constants';
6
6
  import MessageBox from 'foremanReact/components/common/MessageBox';
@@ -31,6 +31,7 @@ const TaskDetails = ({
31
31
  }) => {
32
32
  const id = getTaskID();
33
33
  const { taskReload, status, isLoading } = props;
34
+ const [activeTabKey, setActiveTabKey] = useState(1);
34
35
 
35
36
  useEffect(() => {
36
37
  taskReloadStart(id);
@@ -58,27 +59,42 @@ const TaskDetails = ({
58
59
  }
59
60
  const resumable = executionPlan ? executionPlan.state === 'paused' : false;
60
61
  const cancellable = executionPlan ? executionPlan.cancellable : false;
62
+ const lockRecords = locks.concat(links);
63
+
64
+ const taskComponentProps = {
65
+ ...props,
66
+ cancellable,
67
+ resumable,
68
+ id,
69
+ status,
70
+ taskProgressToggle,
71
+ taskReloadStart,
72
+ };
73
+
61
74
  return (
62
75
  <div className="task-details-react well">
63
- <Tabs defaultActiveKey={1} animation={false} id="task-details-tabs">
64
- <Tab eventKey={1} title={__('Task')}>
65
- {isLoading ? (
66
- <TaskSkeleton />
67
- ) : (
68
- <Task
69
- {...{
70
- ...props,
71
- cancellable,
72
- resumable,
73
- id,
74
- status,
75
- taskProgressToggle,
76
- taskReloadStart,
77
- }}
78
- />
79
- )}
76
+ <Tabs
77
+ id="task-details-tabs"
78
+ ouiaId="task-details-tabs"
79
+ activeKey={activeTabKey}
80
+ onSelect={(_event, tabKey) => setActiveTabKey(tabKey)}
81
+ mountOnEnter
82
+ >
83
+ <Tab
84
+ eventKey={1}
85
+ title={<TabTitleText>{__('Task')}</TabTitleText>}
86
+ aria-label={__('Task')}
87
+ ouiaId="task-details-tab-task"
88
+ >
89
+ {isLoading ? <TaskSkeleton /> : <Task {...taskComponentProps} />}
80
90
  </Tab>
81
- <Tab eventKey={2} disabled={isLoading} title={__('Running Steps')}>
91
+ <Tab
92
+ eventKey={2}
93
+ title={<TabTitleText>{__('Running Steps')}</TabTitleText>}
94
+ isDisabled={isLoading}
95
+ aria-label={__('Running Steps')}
96
+ ouiaId="task-details-tab-running-steps"
97
+ >
82
98
  <RunningSteps
83
99
  runningSteps={runningSteps}
84
100
  id={id}
@@ -87,16 +103,40 @@ const TaskDetails = ({
87
103
  taskReloadStart={taskReloadStart}
88
104
  />
89
105
  </Tab>
90
- <Tab eventKey={3} disabled={isLoading} title={__('Errors')}>
106
+ <Tab
107
+ eventKey={3}
108
+ title={<TabTitleText>{__('Errors')}</TabTitleText>}
109
+ isDisabled={isLoading}
110
+ aria-label={__('Errors')}
111
+ ouiaId="task-details-tab-errors"
112
+ >
91
113
  <Errors executionPlan={executionPlan} failedSteps={failedSteps} />
92
114
  </Tab>
93
- <Tab eventKey={4} disabled={isLoading} title={__('Locks')}>
94
- <Locks locks={locks.concat(links)} />
115
+ <Tab
116
+ eventKey={4}
117
+ title={<TabTitleText>{__('Locks')}</TabTitleText>}
118
+ isDisabled={isLoading}
119
+ aria-label={__('Locks')}
120
+ ouiaId="task-details-tab-locks"
121
+ >
122
+ <Locks locks={lockRecords} />
95
123
  </Tab>
96
- <Tab eventKey={5} disabled={isLoading} title={__('Dependencies')}>
124
+ <Tab
125
+ eventKey={5}
126
+ title={<TabTitleText>{__('Dependencies')}</TabTitleText>}
127
+ isDisabled={isLoading}
128
+ aria-label={__('Dependencies')}
129
+ ouiaId="task-details-tab-dependencies"
130
+ >
97
131
  <Dependencies dependsOn={dependsOn} blocks={blocks} />
98
132
  </Tab>
99
- <Tab eventKey={6} disabled={isLoading} title={__('Raw')}>
133
+ <Tab
134
+ eventKey={6}
135
+ title={<TabTitleText>{__('Raw')}</TabTitleText>}
136
+ isDisabled={isLoading}
137
+ aria-label={__('Raw')}
138
+ ouiaId="task-details-tab-raw"
139
+ >
100
140
  <Raw
101
141
  id={id}
102
142
  label={props.label}