foreman-tasks 11.1.0 → 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/__tests__/Dependencies.test.js +92 -0
- 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/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 -2
- data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/__snapshots__/ActionSelectButton.test.js.snap +0 -43
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7ca02aa356eb223ac95653b1a27efcdf2b8b38a7dca41f947e9c29c7655632cb
|
|
4
|
+
data.tar.gz: c67e4b53d065a81e9744919e90b63743b8919d56116313e18ad468db0fac1be9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a9380c9445247ccbca6896e389762c5c5f7356bfc876adad55b29adc2d75339ecc3a572274650c34ef67e363e396860d75edebb8d41884940e4de51564dbfc7f
|
|
7
|
+
data.tar.gz: 40edbd36d1f8dd923ec796497052e934361294835444eea0eff9a479303778cb21a3d2c73ac7e336418b7eb4994eb372133de564011916a654f417e54612f0d0
|
|
@@ -18,3 +18,23 @@ node(:links) do
|
|
|
18
18
|
end
|
|
19
19
|
node(:username_path) { username_link_task(@task.owner, @task.username) }
|
|
20
20
|
node(:dynflow_enable_console) { Setting['dynflow_enable_console'] }
|
|
21
|
+
node(:depends_on) do
|
|
22
|
+
if @task.execution_plan
|
|
23
|
+
dynflow_uuids = ForemanTasks.dynflow.world.persistence.find_execution_plan_dependencies(@task.execution_plan.id)
|
|
24
|
+
ForemanTasks::Task.where(external_id: dynflow_uuids).map do |task|
|
|
25
|
+
partial('foreman_tasks/api/tasks/dependency_summary', :object => task)
|
|
26
|
+
end
|
|
27
|
+
else
|
|
28
|
+
[]
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
node(:blocks) do
|
|
32
|
+
if @task.execution_plan
|
|
33
|
+
dynflow_uuids = ForemanTasks.dynflow.world.persistence.find_blocked_execution_plans(@task.execution_plan.id)
|
|
34
|
+
ForemanTasks::Task.where(external_id: dynflow_uuids).map do |task|
|
|
35
|
+
partial('foreman_tasks/api/tasks/dependency_summary', :object => task)
|
|
36
|
+
end
|
|
37
|
+
else
|
|
38
|
+
[]
|
|
39
|
+
end
|
|
40
|
+
end
|
data/lib/foreman_tasks/engine.rb
CHANGED
|
@@ -17,8 +17,10 @@ module ForemanTasks
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
initializer 'foreman_tasks.register_plugin', :before => :finisher_hook do
|
|
20
|
+
require 'foreman/cron'
|
|
21
|
+
|
|
20
22
|
Foreman::Plugin.register :"foreman-tasks" do
|
|
21
|
-
requires_foreman '>= 3.
|
|
23
|
+
requires_foreman '>= 3.19'
|
|
22
24
|
divider :top_menu, :parent => :monitor_menu, :last => true, :caption => N_('Foreman Tasks')
|
|
23
25
|
menu :top_menu, :tasks,
|
|
24
26
|
:url_hash => { :controller => 'foreman_tasks/tasks', :action => :index },
|
|
@@ -124,6 +126,9 @@ module ForemanTasks
|
|
|
124
126
|
widget 'foreman_tasks/tasks/dashboard/latest_tasks_in_error_warning', :sizex => 6, :sizey => 1, :name => N_('Latest Warning/Error Tasks')
|
|
125
127
|
|
|
126
128
|
register_gettext domain: "foreman_tasks"
|
|
129
|
+
|
|
130
|
+
# Register recurring task with Foreman::Cron framework
|
|
131
|
+
Foreman::Cron.register(:daily, 'foreman_tasks:cleanup')
|
|
127
132
|
end
|
|
128
133
|
end
|
|
129
134
|
|
data/lib/foreman_tasks.rb
CHANGED
|
@@ -62,6 +62,30 @@ module ForemanTasks
|
|
|
62
62
|
ForemanTasks::Task::DynflowTask.where(:external_id => result.id).first!
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
+
# Chain a task to wait for dependency task(s) to finish before executing.
|
|
66
|
+
# The chained task remains 'scheduled' until all dependencies reach 'stopped' state.
|
|
67
|
+
#
|
|
68
|
+
# @param dependencies [ForemanTasks::Task, Array<ForemanTasks::Task>, ActiveRecord::Relation]
|
|
69
|
+
# Dependency ForemanTasks task object(s) or an ActiveRecord relation of tasks.
|
|
70
|
+
# @param action [Class] Action class to execute
|
|
71
|
+
# @param args Arguments to pass to the action
|
|
72
|
+
# @return [ForemanTasks::Task::DynflowTask] The chained task
|
|
73
|
+
def self.chain(dependencies, action, *args)
|
|
74
|
+
plan_uuids =
|
|
75
|
+
if dependencies.is_a?(ActiveRecord::Relation)
|
|
76
|
+
dependencies.pluck(:external_id)
|
|
77
|
+
else
|
|
78
|
+
Array(dependencies).map(&:external_id)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
if plan_uuids.any?(&:blank?)
|
|
82
|
+
raise ArgumentError, 'All dependency tasks must have external_id set'
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
result = dynflow.world.chain(plan_uuids, action, *args)
|
|
86
|
+
ForemanTasks::Task::DynflowTask.where(:external_id => result.id).first!
|
|
87
|
+
end
|
|
88
|
+
|
|
65
89
|
def self.register_scheduled_task(task_class, cronline)
|
|
66
90
|
ForemanTasks::RecurringLogic.transaction(isolation: :serializable) do
|
|
67
91
|
return if ForemanTasks::RecurringLogic.joins(:tasks)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require 'foreman_tasks_test_helper'
|
|
2
|
+
|
|
3
|
+
module ForemanTasks
|
|
4
|
+
class ChainingTest < ActiveSupport::TestCase
|
|
5
|
+
include ForemanTasks::TestHelpers::WithInThreadExecutor
|
|
6
|
+
|
|
7
|
+
before do
|
|
8
|
+
User.current = User.where(:login => 'apiadmin').first
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'creates a scheduled task chained to a dependency task' do
|
|
12
|
+
triggered = ForemanTasks.trigger(Support::DummyDynflowAction)
|
|
13
|
+
triggered.finished.wait(30)
|
|
14
|
+
dependency_task = ForemanTasks::Task::DynflowTask.find_by!(:external_id => triggered.id)
|
|
15
|
+
|
|
16
|
+
task = ForemanTasks.chain(dependency_task, Support::DummyDynflowAction)
|
|
17
|
+
|
|
18
|
+
assert_kind_of ForemanTasks::Task::DynflowTask, task
|
|
19
|
+
assert_predicate task, :scheduled?
|
|
20
|
+
|
|
21
|
+
dependencies = ForemanTasks.dynflow.world.persistence.find_execution_plan_dependencies(task.execution_plan.id)
|
|
22
|
+
assert_includes dependencies, dependency_task.external_id
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'accepts multiple dependency tasks' do
|
|
26
|
+
triggered_1 = ForemanTasks.trigger(Support::DummyDynflowAction)
|
|
27
|
+
triggered_2 = ForemanTasks.trigger(Support::DummyDynflowAction)
|
|
28
|
+
triggered_1.finished.wait(30)
|
|
29
|
+
triggered_2.finished.wait(30)
|
|
30
|
+
dependency_task_1 = ForemanTasks::Task::DynflowTask.find_by!(:external_id => triggered_1.id)
|
|
31
|
+
dependency_task_2 = ForemanTasks::Task::DynflowTask.find_by!(:external_id => triggered_2.id)
|
|
32
|
+
|
|
33
|
+
task = ForemanTasks.chain([dependency_task_1, dependency_task_2], Support::DummyDynflowAction)
|
|
34
|
+
|
|
35
|
+
dependencies = ForemanTasks.dynflow.world.persistence.find_execution_plan_dependencies(task.execution_plan.id)
|
|
36
|
+
assert_includes dependencies, dependency_task_1.external_id
|
|
37
|
+
assert_includes dependencies, dependency_task_2.external_id
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'accepts dependency task objects' do
|
|
41
|
+
triggered = ForemanTasks.trigger(Support::DummyDynflowAction)
|
|
42
|
+
triggered.finished.wait(30)
|
|
43
|
+
dependency_task = ForemanTasks::Task::DynflowTask.find_by!(:external_id => triggered.id)
|
|
44
|
+
|
|
45
|
+
task = ForemanTasks.chain(dependency_task, Support::DummyDynflowAction)
|
|
46
|
+
|
|
47
|
+
dependencies = ForemanTasks.dynflow.world.persistence.find_execution_plan_dependencies(task.execution_plan.id)
|
|
48
|
+
assert_includes dependencies, dependency_task.external_id
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'accepts dependency tasks as a relation' do
|
|
52
|
+
triggered = ForemanTasks.trigger(Support::DummyDynflowAction)
|
|
53
|
+
triggered.finished.wait(30)
|
|
54
|
+
dependency_task = ForemanTasks::Task::DynflowTask.find_by!(:external_id => triggered.id)
|
|
55
|
+
|
|
56
|
+
task = ForemanTasks.chain(ForemanTasks::Task::DynflowTask.where(:id => dependency_task.id), Support::DummyDynflowAction)
|
|
57
|
+
|
|
58
|
+
dependencies = ForemanTasks.dynflow.world.persistence.find_execution_plan_dependencies(task.execution_plan.id)
|
|
59
|
+
assert_includes dependencies, dependency_task.external_id
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import {
|
|
4
|
+
Alert,
|
|
5
|
+
AlertVariant,
|
|
6
|
+
Grid,
|
|
7
|
+
GridItem,
|
|
8
|
+
Title,
|
|
9
|
+
} from '@patternfly/react-core';
|
|
10
|
+
import { Table, Thead, Tbody, Tr, Th, Td } from '@patternfly/react-table';
|
|
11
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
|
12
|
+
|
|
13
|
+
const DependencyTable = ({ title, tasks }) => {
|
|
14
|
+
const tableId = title.toLowerCase().replace(/\s+/g, '-');
|
|
15
|
+
return (
|
|
16
|
+
<div>
|
|
17
|
+
<Title headingLevel="h4" size="md" ouiaId={`${tableId}-title`}>
|
|
18
|
+
{title}
|
|
19
|
+
</Title>
|
|
20
|
+
{tasks.length === 0 ? (
|
|
21
|
+
<p className="text-muted">{__('None')}</p>
|
|
22
|
+
) : (
|
|
23
|
+
<Table aria-label={title} variant="compact" ouiaId={`${tableId}-table`}>
|
|
24
|
+
<Thead>
|
|
25
|
+
<Tr ouiaId={`${tableId}-table-header`}>
|
|
26
|
+
<Th width={50}>{__('Action')}</Th>
|
|
27
|
+
<Th width={25}>{__('State')}</Th>
|
|
28
|
+
<Th width={25}>{__('Result')}</Th>
|
|
29
|
+
</Tr>
|
|
30
|
+
</Thead>
|
|
31
|
+
<Tbody>
|
|
32
|
+
{tasks.map(task => (
|
|
33
|
+
<Tr key={task.id} ouiaId={`${tableId}-table-row-${task.id}`}>
|
|
34
|
+
<Td>
|
|
35
|
+
<a href={`/foreman_tasks/tasks/${task.id}`}>
|
|
36
|
+
{task.humanized || task.action}
|
|
37
|
+
</a>
|
|
38
|
+
</Td>
|
|
39
|
+
<Td>{task.state}</Td>
|
|
40
|
+
<Td>{task.result}</Td>
|
|
41
|
+
</Tr>
|
|
42
|
+
))}
|
|
43
|
+
</Tbody>
|
|
44
|
+
</Table>
|
|
45
|
+
)}
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
DependencyTable.propTypes = {
|
|
51
|
+
title: PropTypes.string.isRequired,
|
|
52
|
+
tasks: PropTypes.array,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
DependencyTable.defaultProps = {
|
|
56
|
+
tasks: [],
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const Dependencies = ({ dependsOn, blocks }) => (
|
|
60
|
+
<div>
|
|
61
|
+
<Alert
|
|
62
|
+
variant={AlertVariant.info}
|
|
63
|
+
isInline
|
|
64
|
+
title={__('Task dependencies')}
|
|
65
|
+
ouiaId="task-dependencies-info-alert"
|
|
66
|
+
>
|
|
67
|
+
{__(
|
|
68
|
+
'This task may have dependencies on other tasks or may be blocking other tasks from executing. Dependencies are established through task chaining relationships.'
|
|
69
|
+
)}
|
|
70
|
+
</Alert>
|
|
71
|
+
<br />
|
|
72
|
+
<Grid hasGutter>
|
|
73
|
+
<GridItem span={6}>
|
|
74
|
+
<DependencyTable title={__('Depends on')} tasks={dependsOn} />
|
|
75
|
+
</GridItem>
|
|
76
|
+
<GridItem span={6}>
|
|
77
|
+
<DependencyTable title={__('Blocks')} tasks={blocks} />
|
|
78
|
+
</GridItem>
|
|
79
|
+
</Grid>
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
Dependencies.propTypes = {
|
|
84
|
+
dependsOn: PropTypes.array,
|
|
85
|
+
blocks: PropTypes.array,
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
Dependencies.defaultProps = {
|
|
89
|
+
dependsOn: [],
|
|
90
|
+
blocks: [],
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export default Dependencies;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { mount } from 'enzyme';
|
|
3
|
+
import Dependencies from '../Dependencies';
|
|
4
|
+
|
|
5
|
+
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');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should render with depends_on dependencies', () => {
|
|
15
|
+
const dependsOn = [
|
|
16
|
+
{
|
|
17
|
+
id: '123',
|
|
18
|
+
action: 'Actions::FooBar',
|
|
19
|
+
humanized: 'Foo Bar Action',
|
|
20
|
+
state: 'stopped',
|
|
21
|
+
result: 'success',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: '456',
|
|
25
|
+
action: 'Actions::BazQux',
|
|
26
|
+
humanized: 'Baz Qux Action',
|
|
27
|
+
state: 'running',
|
|
28
|
+
result: 'pending',
|
|
29
|
+
},
|
|
30
|
+
];
|
|
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');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should render with blocks dependencies', () => {
|
|
41
|
+
const blocks = [
|
|
42
|
+
{
|
|
43
|
+
id: '789',
|
|
44
|
+
action: 'Actions::Test',
|
|
45
|
+
humanized: 'Test Action',
|
|
46
|
+
state: 'paused',
|
|
47
|
+
result: 'warning',
|
|
48
|
+
},
|
|
49
|
+
];
|
|
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');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should render with both dependency types', () => {
|
|
59
|
+
const dependsOn = [
|
|
60
|
+
{
|
|
61
|
+
id: '123',
|
|
62
|
+
action: 'Actions::Foo',
|
|
63
|
+
humanized: 'Foo Action',
|
|
64
|
+
state: 'stopped',
|
|
65
|
+
result: 'success',
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
const blocks = [
|
|
69
|
+
{
|
|
70
|
+
id: '456',
|
|
71
|
+
action: 'Actions::Bar',
|
|
72
|
+
humanized: 'Bar Action',
|
|
73
|
+
state: 'running',
|
|
74
|
+
result: 'pending',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: '789',
|
|
78
|
+
action: 'Actions::Baz',
|
|
79
|
+
humanized: 'Baz Action',
|
|
80
|
+
state: 'stopped',
|
|
81
|
+
result: 'error',
|
|
82
|
+
},
|
|
83
|
+
];
|
|
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');
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -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 =>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { DropdownButton, MenuItem } from 'patternfly-react';
|
|
3
2
|
import PropTypes from 'prop-types';
|
|
3
|
+
import { SimpleDropdown } from '@patternfly/react-templates';
|
|
4
|
+
import { EllipsisVIcon } from '@patternfly/react-icons';
|
|
4
5
|
import { translate as __ } from 'foremanReact/common/I18n';
|
|
5
6
|
|
|
6
7
|
export const ActionSelectButton = ({
|
|
@@ -8,35 +9,35 @@ export const ActionSelectButton = ({
|
|
|
8
9
|
onResume,
|
|
9
10
|
onForceCancel,
|
|
10
11
|
disabled,
|
|
11
|
-
}) =>
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
{
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
12
|
+
}) => {
|
|
13
|
+
const buttons = [
|
|
14
|
+
{
|
|
15
|
+
content: __('Cancel Selected'),
|
|
16
|
+
onClick: onCancel,
|
|
17
|
+
value: 1,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
content: __('Resume Selected'),
|
|
21
|
+
onClick: onResume,
|
|
22
|
+
value: 2,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
content: __('Force Cancel Selected'),
|
|
26
|
+
onClick: onForceCancel,
|
|
27
|
+
value: 3,
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
return (
|
|
31
|
+
<SimpleDropdown
|
|
32
|
+
isDisabled={disabled}
|
|
33
|
+
ouiaId="tasks-table-action-select-dropdown"
|
|
34
|
+
toggleVariant="plain"
|
|
35
|
+
popperProps={{ position: 'right' }}
|
|
36
|
+
initialItems={buttons}
|
|
37
|
+
toggleContent={<EllipsisVIcon aria-hidden="true" />}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
40
41
|
|
|
41
42
|
ActionSelectButton.propTypes = {
|
|
42
43
|
disabled: PropTypes.bool,
|
data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/ActionSelectButton.test.js
CHANGED
|
@@ -1,14 +1,81 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom';
|
|
3
4
|
import { ActionSelectButton } from '../ActionSelectButton';
|
|
4
5
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
|
|
6
|
+
const mockOnCancel = jest.fn();
|
|
7
|
+
const mockOnResume = jest.fn();
|
|
8
|
+
const mockOnForceCancel = jest.fn();
|
|
9
|
+
|
|
10
|
+
describe('ActionSelectButton', () => {
|
|
11
|
+
const renderComponent = (props = {}) => {
|
|
12
|
+
const defaultProps = {
|
|
13
|
+
onCancel: mockOnCancel,
|
|
14
|
+
onResume: mockOnResume,
|
|
15
|
+
onForceCancel: mockOnForceCancel,
|
|
16
|
+
disabled: false,
|
|
17
|
+
};
|
|
18
|
+
return render(<ActionSelectButton {...defaultProps} {...props} />);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
jest.clearAllMocks();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('renders with minimal props', () => {
|
|
26
|
+
renderComponent();
|
|
27
|
+
|
|
28
|
+
const toggle = screen.getByRole('button');
|
|
29
|
+
expect(toggle).toBeInTheDocument();
|
|
30
|
+
expect(toggle).not.toBeDisabled();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('renders disabled when disabled prop is true', () => {
|
|
34
|
+
renderComponent({ disabled: true });
|
|
35
|
+
|
|
36
|
+
const toggle = screen.getByRole('button');
|
|
37
|
+
expect(toggle).toBeDisabled();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('opens dropdown and shows action options when toggle is clicked', async () => {
|
|
41
|
+
renderComponent();
|
|
42
|
+
const toggle = screen.getByRole('button');
|
|
43
|
+
fireEvent.click(toggle);
|
|
44
|
+
|
|
45
|
+
await waitFor(() => {
|
|
46
|
+
expect(screen.getByText('Cancel Selected')).toBeInTheDocument();
|
|
47
|
+
expect(screen.getByText('Resume Selected')).toBeInTheDocument();
|
|
48
|
+
expect(screen.getByText('Force Cancel Selected')).toBeInTheDocument();
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('calls onCancel when Cancel Selected is clicked', async () => {
|
|
53
|
+
renderComponent();
|
|
54
|
+
fireEvent.click(screen.getByRole('button'));
|
|
55
|
+
await waitFor(() => {
|
|
56
|
+
expect(screen.getByText('Cancel Selected')).toBeInTheDocument();
|
|
57
|
+
});
|
|
58
|
+
fireEvent.click(screen.getByText('Cancel Selected'));
|
|
59
|
+
expect(mockOnCancel).toHaveBeenCalledTimes(1);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('calls onResume when Resume Selected is clicked', async () => {
|
|
63
|
+
renderComponent();
|
|
64
|
+
fireEvent.click(screen.getByRole('button'));
|
|
65
|
+
await waitFor(() => {
|
|
66
|
+
expect(screen.getByText('Resume Selected')).toBeInTheDocument();
|
|
67
|
+
});
|
|
68
|
+
fireEvent.click(screen.getByText('Resume Selected'));
|
|
69
|
+
expect(mockOnResume).toHaveBeenCalledTimes(1);
|
|
70
|
+
});
|
|
12
71
|
|
|
13
|
-
|
|
14
|
-
|
|
72
|
+
it('calls onForceCancel when Force Cancel Selected is clicked', async () => {
|
|
73
|
+
renderComponent();
|
|
74
|
+
fireEvent.click(screen.getByRole('button'));
|
|
75
|
+
await waitFor(() => {
|
|
76
|
+
expect(screen.getByText('Force Cancel Selected')).toBeInTheDocument();
|
|
77
|
+
});
|
|
78
|
+
fireEvent.click(screen.getByText('Force Cancel Selected'));
|
|
79
|
+
expect(mockOnForceCancel).toHaveBeenCalledTimes(1);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -2,11 +2,11 @@ import React, { useState } from 'react';
|
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import URI from 'urijs';
|
|
4
4
|
import { getURIsearch } from 'foremanReact/common/urlHelpers';
|
|
5
|
-
import {
|
|
5
|
+
import { Button, ToolbarItem, Spinner } from '@patternfly/react-core';
|
|
6
|
+
import { RedoIcon } from '@patternfly/react-icons';
|
|
6
7
|
import PageLayout from 'foremanReact/routes/common/PageLayout/PageLayout';
|
|
7
8
|
import { translate as __ } from 'foremanReact/common/I18n';
|
|
8
9
|
import { getURIQuery } from 'foremanReact/common/helpers';
|
|
9
|
-
import ExportButton from 'foremanReact/routes/common/PageLayout/components/ExportButton/ExportButton';
|
|
10
10
|
import { STATUS } from 'foremanReact/constants';
|
|
11
11
|
import TasksDashboard from '../TasksDashboard';
|
|
12
12
|
import TasksTable from './TasksTable';
|
|
@@ -127,25 +127,44 @@ const TasksTablePage = ({
|
|
|
127
127
|
header={createHeader(props.actionName)}
|
|
128
128
|
breadcrumbOptions={getBreadcrumbs(props.actionName)}
|
|
129
129
|
toolbarButtons={
|
|
130
|
-
|
|
131
|
-
<
|
|
132
|
-
<
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
130
|
+
<>
|
|
131
|
+
<ToolbarItem>
|
|
132
|
+
<Button
|
|
133
|
+
ouiaId="tasks-table-refresh-data"
|
|
134
|
+
variant="primary"
|
|
135
|
+
onClick={() => props.reloadPage(url, props.parentTaskID)}
|
|
136
|
+
icon={<RedoIcon />}
|
|
137
|
+
>
|
|
138
|
+
{__('Refresh Data')}
|
|
139
|
+
</Button>
|
|
140
|
+
</ToolbarItem>
|
|
141
|
+
{props.status === STATUS.PENDING && (
|
|
142
|
+
<ToolbarItem>
|
|
143
|
+
<Spinner size="lg" />
|
|
144
|
+
</ToolbarItem>
|
|
145
|
+
)}
|
|
146
|
+
<ToolbarItem>
|
|
147
|
+
<Button
|
|
148
|
+
ouiaId="tasks-table-export-all"
|
|
149
|
+
variant="secondary"
|
|
150
|
+
component="a"
|
|
151
|
+
href={getCSVurl(history.location.pathname, uriQuery)}
|
|
152
|
+
>
|
|
153
|
+
{__('Export All')}
|
|
154
|
+
</Button>
|
|
155
|
+
</ToolbarItem>
|
|
156
|
+
<ToolbarItem>
|
|
157
|
+
<ActionSelectButton
|
|
158
|
+
disabled={
|
|
159
|
+
!props.permissions.edit ||
|
|
160
|
+
!(props.selectedRows.length || props.allRowsSelected)
|
|
161
|
+
}
|
|
162
|
+
onCancel={() => openModal(CANCEL_SELECTED_MODAL)}
|
|
163
|
+
onResume={() => openModal(RESUME_SELECTED_MODAL)}
|
|
164
|
+
onForceCancel={() => openModal(FORCE_UNLOCK_SELECTED_MODAL)}
|
|
165
|
+
/>
|
|
166
|
+
</ToolbarItem>
|
|
167
|
+
</>
|
|
149
168
|
}
|
|
150
169
|
searchQuery={getURIsearch()}
|
|
151
170
|
beforeToolbarComponent={
|
data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTablePage.test.js.snap
CHANGED
|
@@ -108,38 +108,39 @@ exports[`TasksTablePage rendering render with Breadcrubs and edit permissions 1`
|
|
|
108
108
|
searchable={true}
|
|
109
109
|
toolbarButtons={
|
|
110
110
|
<React.Fragment>
|
|
111
|
-
<
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
111
|
+
<ToolbarItem>
|
|
112
|
+
<Button
|
|
113
|
+
icon={<RedoIcon />}
|
|
114
|
+
onClick={[Function]}
|
|
115
|
+
ouiaId="tasks-table-refresh-data"
|
|
116
|
+
variant="primary"
|
|
117
|
+
>
|
|
118
|
+
Refresh Data
|
|
119
|
+
</Button>
|
|
120
|
+
</ToolbarItem>
|
|
121
|
+
<ToolbarItem>
|
|
122
|
+
<Spinner
|
|
123
|
+
size="lg"
|
|
122
124
|
/>
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
/>
|
|
125
|
+
</ToolbarItem>
|
|
126
|
+
<ToolbarItem>
|
|
127
|
+
<Button
|
|
128
|
+
component="a"
|
|
129
|
+
href="/foreman_tasks/tasks.csv?search=%28state%3Dstopped%29"
|
|
130
|
+
ouiaId="tasks-table-export-all"
|
|
131
|
+
variant="secondary"
|
|
132
|
+
>
|
|
133
|
+
Export All
|
|
134
|
+
</Button>
|
|
135
|
+
</ToolbarItem>
|
|
136
|
+
<ToolbarItem>
|
|
137
|
+
<ActionSelectButton
|
|
138
|
+
disabled={true}
|
|
139
|
+
onCancel={[Function]}
|
|
140
|
+
onForceCancel={[Function]}
|
|
141
|
+
onResume={[Function]}
|
|
142
|
+
/>
|
|
143
|
+
</ToolbarItem>
|
|
143
144
|
</React.Fragment>
|
|
144
145
|
}
|
|
145
146
|
>
|
|
@@ -283,38 +284,39 @@ exports[`TasksTablePage rendering render with minimal props 1`] = `
|
|
|
283
284
|
searchable={true}
|
|
284
285
|
toolbarButtons={
|
|
285
286
|
<React.Fragment>
|
|
286
|
-
<
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
287
|
+
<ToolbarItem>
|
|
288
|
+
<Button
|
|
289
|
+
icon={<RedoIcon />}
|
|
290
|
+
onClick={[Function]}
|
|
291
|
+
ouiaId="tasks-table-refresh-data"
|
|
292
|
+
variant="primary"
|
|
293
|
+
>
|
|
294
|
+
Refresh Data
|
|
295
|
+
</Button>
|
|
296
|
+
</ToolbarItem>
|
|
297
|
+
<ToolbarItem>
|
|
298
|
+
<Spinner
|
|
299
|
+
size="lg"
|
|
300
|
+
/>
|
|
301
|
+
</ToolbarItem>
|
|
302
|
+
<ToolbarItem>
|
|
303
|
+
<Button
|
|
304
|
+
component="a"
|
|
305
|
+
href="/foreman_tasks/tasks.csv?search=%28state%3Dstopped%29"
|
|
306
|
+
ouiaId="tasks-table-export-all"
|
|
307
|
+
variant="secondary"
|
|
308
|
+
>
|
|
309
|
+
Export All
|
|
310
|
+
</Button>
|
|
311
|
+
</ToolbarItem>
|
|
312
|
+
<ToolbarItem>
|
|
313
|
+
<ActionSelectButton
|
|
314
|
+
disabled={true}
|
|
315
|
+
onCancel={[Function]}
|
|
316
|
+
onForceCancel={[Function]}
|
|
317
|
+
onResume={[Function]}
|
|
297
318
|
/>
|
|
298
|
-
|
|
299
|
-
Refresh Data
|
|
300
|
-
</Button>
|
|
301
|
-
<Spinner
|
|
302
|
-
className=""
|
|
303
|
-
inline={false}
|
|
304
|
-
inverse={false}
|
|
305
|
-
loading={true}
|
|
306
|
-
size="md"
|
|
307
|
-
/>
|
|
308
|
-
<ExportButton
|
|
309
|
-
title="Export All"
|
|
310
|
-
url="/foreman_tasks/tasks.csv?search=%28state%3Dstopped%29"
|
|
311
|
-
/>
|
|
312
|
-
<ActionSelectButton
|
|
313
|
-
disabled={true}
|
|
314
|
-
onCancel={[Function]}
|
|
315
|
-
onForceCancel={[Function]}
|
|
316
|
-
onResume={[Function]}
|
|
317
|
-
/>
|
|
319
|
+
</ToolbarItem>
|
|
318
320
|
</React.Fragment>
|
|
319
321
|
}
|
|
320
322
|
>
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: foreman-tasks
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 12.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ivan Nečas
|
|
@@ -234,6 +234,7 @@ files:
|
|
|
234
234
|
- app/views/foreman_tasks/api/recurring_logics/main.json.rabl
|
|
235
235
|
- app/views/foreman_tasks/api/recurring_logics/show.json.rabl
|
|
236
236
|
- app/views/foreman_tasks/api/recurring_logics/update.json.rabl
|
|
237
|
+
- app/views/foreman_tasks/api/tasks/dependency_summary.json.rabl
|
|
237
238
|
- app/views/foreman_tasks/api/tasks/details.json.rabl
|
|
238
239
|
- app/views/foreman_tasks/api/tasks/index.json.rabl
|
|
239
240
|
- app/views/foreman_tasks/api/tasks/show.json.rabl
|
|
@@ -377,6 +378,7 @@ files:
|
|
|
377
378
|
- test/unit/actions/proxy_action_test.rb
|
|
378
379
|
- test/unit/actions/recurring_action_test.rb
|
|
379
380
|
- test/unit/actions/trigger_proxy_batch_test.rb
|
|
381
|
+
- test/unit/chaining_test.rb
|
|
380
382
|
- test/unit/cleaner_test.rb
|
|
381
383
|
- test/unit/config/environment.rb
|
|
382
384
|
- test/unit/dynflow_console_authorizer_test.rb
|
|
@@ -398,6 +400,7 @@ files:
|
|
|
398
400
|
- webpack/ForemanTasks/Components/TaskActions/TaskActionsConstants.js
|
|
399
401
|
- webpack/ForemanTasks/Components/TaskActions/__snapshots__/TaskAction.test.js.snap
|
|
400
402
|
- webpack/ForemanTasks/Components/TaskActions/index.js
|
|
403
|
+
- webpack/ForemanTasks/Components/TaskDetails/Components/Dependencies.js
|
|
401
404
|
- webpack/ForemanTasks/Components/TaskDetails/Components/Errors.js
|
|
402
405
|
- webpack/ForemanTasks/Components/TaskDetails/Components/Locks.js
|
|
403
406
|
- webpack/ForemanTasks/Components/TaskDetails/Components/Raw.js
|
|
@@ -407,6 +410,7 @@ files:
|
|
|
407
410
|
- webpack/ForemanTasks/Components/TaskDetails/Components/TaskHelper.js
|
|
408
411
|
- webpack/ForemanTasks/Components/TaskDetails/Components/TaskInfo.js
|
|
409
412
|
- webpack/ForemanTasks/Components/TaskDetails/Components/TaskSkeleton.js
|
|
413
|
+
- webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Dependencies.test.js
|
|
410
414
|
- webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Errors.test.js
|
|
411
415
|
- webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Locks.test.js
|
|
412
416
|
- webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Raw.test.js
|
|
@@ -510,7 +514,6 @@ files:
|
|
|
510
514
|
- webpack/ForemanTasks/Components/TasksTable/Components/__test__/SelectAllAlert.test.js
|
|
511
515
|
- webpack/ForemanTasks/Components/TasksTable/Components/__test__/TableSelectionCell.test.js
|
|
512
516
|
- webpack/ForemanTasks/Components/TasksTable/Components/__test__/TableSelectionHeaderCell.test.js
|
|
513
|
-
- webpack/ForemanTasks/Components/TasksTable/Components/__test__/__snapshots__/ActionSelectButton.test.js.snap
|
|
514
517
|
- webpack/ForemanTasks/Components/TasksTable/Components/__test__/__snapshots__/SelectAllAlert.test.js.snap
|
|
515
518
|
- webpack/ForemanTasks/Components/TasksTable/Components/__test__/__snapshots__/TableSelectionCell.test.js.snap
|
|
516
519
|
- webpack/ForemanTasks/Components/TasksTable/Components/__test__/__snapshots__/TableSelectionHeaderCell.test.js.snap
|
|
@@ -660,6 +663,7 @@ test_files:
|
|
|
660
663
|
- test/unit/actions/proxy_action_test.rb
|
|
661
664
|
- test/unit/actions/recurring_action_test.rb
|
|
662
665
|
- test/unit/actions/trigger_proxy_batch_test.rb
|
|
666
|
+
- test/unit/chaining_test.rb
|
|
663
667
|
- test/unit/cleaner_test.rb
|
|
664
668
|
- test/unit/config/environment.rb
|
|
665
669
|
- test/unit/dynflow_console_authorizer_test.rb
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
-
|
|
3
|
-
exports[`ActionSelectButton renders with minimal props 1`] = `
|
|
4
|
-
<DropdownButton
|
|
5
|
-
disabled={false}
|
|
6
|
-
id="selcted-action-type"
|
|
7
|
-
title="Select Action"
|
|
8
|
-
>
|
|
9
|
-
<MenuItem
|
|
10
|
-
bsClass="dropdown"
|
|
11
|
-
disabled={false}
|
|
12
|
-
divider={false}
|
|
13
|
-
eventKey="1"
|
|
14
|
-
header={false}
|
|
15
|
-
onClick={[MockFunction]}
|
|
16
|
-
title="Cancel selected tasks"
|
|
17
|
-
>
|
|
18
|
-
Cancel Selected
|
|
19
|
-
</MenuItem>
|
|
20
|
-
<MenuItem
|
|
21
|
-
bsClass="dropdown"
|
|
22
|
-
disabled={false}
|
|
23
|
-
divider={false}
|
|
24
|
-
eventKey="2"
|
|
25
|
-
header={false}
|
|
26
|
-
onClick={[MockFunction]}
|
|
27
|
-
title="Resume selected tasks"
|
|
28
|
-
>
|
|
29
|
-
Resume Selected
|
|
30
|
-
</MenuItem>
|
|
31
|
-
<MenuItem
|
|
32
|
-
bsClass="dropdown"
|
|
33
|
-
disabled={false}
|
|
34
|
-
divider={false}
|
|
35
|
-
eventKey="3"
|
|
36
|
-
header={false}
|
|
37
|
-
onClick={[MockFunction]}
|
|
38
|
-
title="Force Cancel selected tasks"
|
|
39
|
-
>
|
|
40
|
-
Force Cancel Selected
|
|
41
|
-
</MenuItem>
|
|
42
|
-
</DropdownButton>
|
|
43
|
-
`;
|