foreman-tasks 12.2.3 → 13.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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/foreman_tasks/tasks_controller.rb +0 -5
  3. data/config/routes.rb +3 -2
  4. data/lib/foreman_tasks/engine.rb +2 -2
  5. data/lib/foreman_tasks/version.rb +1 -1
  6. data/test/controllers/tasks_controller_test.rb +0 -9
  7. data/test/foreman_tasks_test_helper.rb +2 -2
  8. data/test/integration/tasks_test.rb +17 -0
  9. data/test/test_plugin_helper.rb +8 -0
  10. data/webpack/ForemanTasks/Components/TaskActions/TaskAction.test.js +212 -42
  11. data/webpack/ForemanTasks/Components/TaskDetails/Components/Errors.js +224 -55
  12. data/webpack/ForemanTasks/Components/TaskDetails/Components/Errors.scss +41 -0
  13. data/webpack/ForemanTasks/Components/TaskDetails/Components/TaskInfo.js +6 -51
  14. data/webpack/ForemanTasks/Components/TaskDetails/Components/TaskSkeleton.js +1 -6
  15. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Errors.test.js +159 -11
  16. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Task.test.js +0 -2
  17. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/TaskButtons.test.js +0 -2
  18. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetails.js +29 -15
  19. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetails.scss +1 -5
  20. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsConstants.js +2 -1
  21. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsSelectors.js +20 -5
  22. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/TaskDetails.fixtures.js +1 -1
  23. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/TaskDetails.test.js +97 -10
  24. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/TaskDetailsSelectors.test.js +81 -0
  25. data/webpack/ForemanTasks/Components/TaskDetails/index.js +6 -4
  26. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboard.test.js +94 -7
  27. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboardActions.test.js +97 -16
  28. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboardReducer.test.js +112 -46
  29. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboardSelectors.test.js +103 -15
  30. data/webpack/ForemanTasks/Components/TasksTable/TasksTableConstants.js +1 -1
  31. data/webpack/ForemanTasks/Components/common/taskResultIcon.js +53 -0
  32. data/webpack/ForemanTasks/Routes/ShowTaskDetails/TaskDetailsPage.js +74 -0
  33. data/webpack/ForemanTasks/Routes/ShowTaskDetails/__tests__/TaskDetailsPage.test.js +265 -0
  34. data/webpack/Routes/routes.js +6 -0
  35. data/webpack/Routes/routes.test.js +16 -5
  36. metadata +10 -7
  37. data/app/views/foreman_tasks/tasks/show.html.erb +0 -18
  38. data/webpack/ForemanTasks/Components/TaskActions/__snapshots__/TaskAction.test.js.snap +0 -233
  39. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/__snapshots__/TasksDashboard.test.js.snap +0 -51
  40. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/__snapshots__/TasksDashboardActions.test.js.snap +0 -151
  41. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/__snapshots__/TasksDashboardReducer.test.js.snap +0 -275
  42. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/__snapshots__/TasksDashboardSelectors.test.js.snap +0 -119
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 200260a81901f7159c565c8188aa71de7b80bc0583b7cf3bf6432a2541130567
4
- data.tar.gz: 3cb1afe2a7706fe328155ac0b32da0864155dfcb9e97a7b026283ba020f41489
3
+ metadata.gz: 9fa716da28a9b6bf38240aa02e47f9ab4a4ab6ba82f98c5d26f7d6066f3149e4
4
+ data.tar.gz: eb7ec125870236abf803a65c132b042fd4360f4de5d7d9a5b2bc718ba8672235
5
5
  SHA512:
6
- metadata.gz: 26157491f3be0ce0e7386008c492476db1e49d28d28934f1139daec25771626d3e7813c3e858b501020f719930ad4fee3fcf6a54850bb661d4104b0628676993
7
- data.tar.gz: d295b0470a35efb6a20801431614256612fdb16fb9f85f6c2c79ffdfaf0bd384656986d82f9d1bf0072b68366420884fbb552638152ccf9ca397f4c8a20a14c8
6
+ metadata.gz: 0547b82e39eabef4f7205100733272e5bdb7b619721ea1a9b94259403c1be370a4ee34564825ccbca83ed16767e72634dd2a070f288ceed28627c3c58671d726
7
+ data.tar.gz: c27122956334571f9b1cc287e8ec6cf322f5f2931be69780539cc58a7f37cbf7f7c4c81800807fc81a79ca8213d1510c127552492ee942c21a1067eb09258a9e
@@ -6,11 +6,6 @@ module ForemanTasks
6
6
 
7
7
  before_action :find_dynflow_task, only: [:unlock, :force_unlock, :cancel, :abort, :cancel_step, :resume]
8
8
 
9
- def show
10
- @task = resource_base.find(params[:id])
11
- render :layout => !request.xhr?
12
- end
13
-
14
9
  def index
15
10
  params[:order] ||= 'started_at DESC'
16
11
  respond_with_tasks resource_base
data/config/routes.rb CHANGED
@@ -12,7 +12,7 @@ Foreman::Application.routes.draw do
12
12
  end
13
13
  end
14
14
 
15
- resources :tasks, :only => [:show] do
15
+ resources :tasks, :only => [] do
16
16
  collection do
17
17
  get 'auto_complete_search'
18
18
  get '/summary/:recent_timeframe', action: 'summary'
@@ -27,7 +27,7 @@ Foreman::Application.routes.draw do
27
27
  post :cancel_step
28
28
  end
29
29
  end
30
- resources :tasks, :only => [:show], constraints: ->(req) { req.format == :csv } do
30
+ resources :tasks, :only => [], constraints: ->(req) { req.format == :csv } do
31
31
  member do
32
32
  get :sub_tasks
33
33
  end
@@ -35,6 +35,7 @@ Foreman::Application.routes.draw do
35
35
  resources :tasks, :only => [:index], constraints: ->(req) { req.format == :csv }
36
36
 
37
37
  match '/tasks', to: '/react#index', via: :get
38
+ match '/tasks/:id', to: '/react#index', via: :get, :as => :task
38
39
  match '/tasks/:id/sub_tasks', to: '/react#index', via: :get
39
40
 
40
41
  namespace :api do
@@ -20,7 +20,7 @@ module ForemanTasks
20
20
  require 'foreman/cron'
21
21
 
22
22
  Foreman::Plugin.register :"foreman-tasks" do
23
- requires_foreman '>= 3.19'
23
+ requires_foreman '>= 5.0'
24
24
  register_global_js_file 'global'
25
25
  divider :top_menu, :parent => :monitor_menu, :last => true, :caption => N_('Foreman Tasks')
26
26
  menu :top_menu, :tasks,
@@ -36,7 +36,7 @@ module ForemanTasks
36
36
  :last => true
37
37
 
38
38
  security_block :foreman_tasks do |_map|
39
- permission :view_foreman_tasks, { :'foreman_tasks/tasks' => [:auto_complete_search, :sub_tasks, :index, :summary, :summary_sub_tasks, :show],
39
+ permission :view_foreman_tasks, { :'foreman_tasks/tasks' => [:auto_complete_search, :sub_tasks, :index, :summary, :summary_sub_tasks],
40
40
  :'foreman_tasks/api/tasks' => [:bulk_search, :show, :index, :summary, :summary_sub_tasks, :details, :sub_tasks] }, :resource_type => 'ForemanTasks::Task'
41
41
  permission :edit_foreman_tasks, { :'foreman_tasks/tasks' => [:resume, :unlock, :force_unlock, :cancel_step, :cancel, :abort],
42
42
  :'foreman_tasks/api/tasks' => [:bulk_resume, :bulk_cancel, :bulk_stop] }, :resource_type => 'ForemanTasks::Task'
@@ -1,3 +1,3 @@
1
1
  module ForemanTasks
2
- VERSION = '12.2.3'.freeze
2
+ VERSION = '13.0.0'.freeze
3
3
  end
@@ -82,15 +82,6 @@ module ForemanTasks
82
82
  assert_include response.body.lines[1], 'Some action'
83
83
  end
84
84
 
85
- describe 'show' do
86
- it 'does not allow user without permissions to see task details' do
87
- setup_user('view', 'foreman_tasks', 'owner.id = current_user')
88
- get :show, params: { id: FactoryBot.create(:some_task).id },
89
- session: set_session_user(User.current)
90
- assert_response :not_found
91
- end
92
- end
93
-
94
85
  describe 'index' do
95
86
  it 'shows duration column' do
96
87
  task = ForemanTasks::Task.select_duration.find(FactoryBot.create(:some_task).id)
@@ -9,8 +9,8 @@ require_relative './support/history_tasks_builder'
9
9
  require 'dynflow/testing'
10
10
  require 'foreman_tasks/test_helpers'
11
11
 
12
- FactoryBot.definition_file_paths = ["#{ForemanTasks::Engine.root}/test/factories"]
13
- FactoryBot.find_definitions
12
+ FactoryBot.definition_file_paths << "#{ForemanTasks::Engine.root}/test/factories"
13
+ FactoryBot.reload
14
14
 
15
15
  ForemanTasks.dynflow.require!
16
16
  ForemanTasks.dynflow.config.disable_active_record_actions = true
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../test_plugin_helper'
4
+ require 'integration_test_helper'
5
+
6
+ class TasksIntegrationTest < IntegrationTestWithJavascript
7
+ test 'does not allow user without permissions to see task details' do
8
+ setup_user('view', 'foreman_tasks', 'owner.id = current_user')
9
+ task = FactoryBot.create(:some_task)
10
+ set_request_user(User.current)
11
+
12
+ visit "/foreman_tasks/tasks/#{task.id}"
13
+
14
+ assert_selector 'h5', text: /Unable to load task/i
15
+ assert_no_selector '#task-details-tabs'
16
+ end
17
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This calls the main test_helper in Foreman-core
4
+ require 'test_helper'
5
+
6
+ # Add plugin to FactoryBot's paths
7
+ FactoryBot.definition_file_paths << File.join(File.dirname(__FILE__), 'factories')
8
+ FactoryBot.reload
@@ -1,4 +1,3 @@
1
- import { testActionSnapshotWithFixtures } from '@theforeman/test';
2
1
  import { API } from 'foremanReact/redux/API';
3
2
  import {
4
3
  cancelTaskRequest,
@@ -6,6 +5,20 @@ import {
6
5
  forceCancelTaskRequest,
7
6
  unlockTaskRequest,
8
7
  } from './';
8
+ import {
9
+ TASKS_CANCEL_REQUEST,
10
+ TASKS_CANCEL_SUCCESS,
11
+ TASKS_CANCEL_FAILURE,
12
+ TASKS_RESUME_REQUEST,
13
+ TASKS_RESUME_SUCCESS,
14
+ TASKS_RESUME_FAILURE,
15
+ TASKS_FORCE_CANCEL_REQUEST,
16
+ TASKS_FORCE_CANCEL_SUCCESS,
17
+ TASKS_FORCE_CANCEL_FAILURE,
18
+ TASKS_UNLOCK_REQUEST,
19
+ TASKS_UNLOCK_SUCCESS,
20
+ TASKS_UNLOCK_FAILURE,
21
+ } from './TaskActionsConstants';
9
22
 
10
23
  jest.mock('foremanReact/components/common/table', () => ({
11
24
  getTableItemsAction: jest.fn(controller => controller),
@@ -21,48 +34,205 @@ jest.mock('foremanReact/components/ToastsList', () => ({
21
34
  }),
22
35
  }));
23
36
 
24
- const task = ['some-id', 'some-name'];
37
+ const taskId = 'some-id';
38
+ const taskName = 'some-name';
25
39
 
26
- const fixtures = {
27
- 'should cancelTaskRequest and succeed': () => cancelTaskRequest(...task),
28
- 'should cancelTaskRequest and fail': () => {
29
- API.post.mockImplementation(() =>
30
- Promise.reject(new Error('Network Error'))
31
- );
32
- return cancelTaskRequest(...task);
40
+ const toastAction = (message, type) => ({
41
+ type: 'TOASTS_ADD',
42
+ payload: {
43
+ message: {
44
+ message,
45
+ type,
46
+ },
33
47
  },
48
+ });
34
49
 
35
- 'should resumeTaskRequest and succeed': () => {
36
- API.post.mockImplementation(() => ({ data: 'some-data' }));
37
- return resumeTaskRequest(...task);
38
- },
39
- 'should resumeTaskRequest and fail': () => {
40
- API.post.mockImplementation(() =>
41
- Promise.reject(new Error('Network Error'))
42
- );
43
- return resumeTaskRequest(...task);
44
- },
45
- 'should forceCancelTaskRequest and succeed': () => {
46
- API.post.mockImplementation(() => ({ data: 'some-data' }));
47
- return forceCancelTaskRequest(...task);
48
- },
49
- 'should forceCancelTaskRequest and fail': () => {
50
- API.post.mockImplementation(() =>
51
- Promise.reject(new Error('Network Error'))
52
- );
53
- return forceCancelTaskRequest(...task);
54
- },
55
- 'should unlockTaskRequest and succeed': () => {
56
- API.post.mockImplementation(() => ({ data: 'some-data' }));
57
- return unlockTaskRequest(...task);
58
- },
59
- 'should unlockTaskRequest and fail': () => {
60
- API.post.mockImplementation(() =>
61
- Promise.reject(new Error('Network Error'))
62
- );
63
- return forceCancelTaskRequest(...task);
64
- },
65
- };
66
- describe('Tasks actions', () => {
67
- testActionSnapshotWithFixtures(fixtures);
50
+ describe('Task actions', () => {
51
+ beforeEach(() => {
52
+ API.post.mockReset();
53
+ API.post.mockResolvedValue({ data: 'some-data' });
54
+ });
55
+
56
+ describe('cancelTaskRequest', () => {
57
+ it('dispatches success actions and toasts when cancel succeeds', async () => {
58
+ const dispatch = jest.fn();
59
+
60
+ await cancelTaskRequest(taskId, taskName)(dispatch);
61
+
62
+ expect(API.post).toHaveBeenCalledWith(
63
+ `/foreman_tasks/tasks/${taskId}/cancel`
64
+ );
65
+ expect(dispatch).toHaveBeenCalledTimes(4);
66
+ expect(dispatch.mock.calls[0][0]).toEqual(
67
+ toastAction('Trying to cancel some-name task', 'info')
68
+ );
69
+ expect(dispatch.mock.calls[1][0]).toEqual({
70
+ type: TASKS_CANCEL_REQUEST,
71
+ });
72
+ expect(dispatch.mock.calls[2][0]).toEqual({
73
+ type: TASKS_CANCEL_SUCCESS,
74
+ });
75
+ expect(dispatch.mock.calls[3][0]).toEqual(
76
+ toastAction('some-name Task execution was cancelled', 'success')
77
+ );
78
+ });
79
+
80
+ it('dispatches failure actions and warning toast when cancel fails', async () => {
81
+ API.post.mockRejectedValue(new Error('Network Error'));
82
+ const dispatch = jest.fn();
83
+
84
+ await cancelTaskRequest(taskId, taskName)(dispatch);
85
+
86
+ expect(dispatch).toHaveBeenCalledTimes(4);
87
+ expect(dispatch.mock.calls[0][0]).toEqual(
88
+ toastAction('Trying to cancel some-name task', 'info')
89
+ );
90
+ expect(dispatch.mock.calls[1][0]).toEqual({
91
+ type: TASKS_CANCEL_REQUEST,
92
+ });
93
+ expect(dispatch.mock.calls[2][0]).toEqual({
94
+ type: TASKS_CANCEL_FAILURE,
95
+ payload: expect.any(Error),
96
+ });
97
+ expect(dispatch.mock.calls[3][0]).toEqual(
98
+ toastAction(
99
+ 'some-name Task execution task has to be cancellable',
100
+ 'warning'
101
+ )
102
+ );
103
+ });
104
+ });
105
+
106
+ describe('resumeTaskRequest', () => {
107
+ it('dispatches success actions and toast when resume succeeds', async () => {
108
+ const dispatch = jest.fn();
109
+
110
+ await resumeTaskRequest(taskId, taskName)(dispatch);
111
+
112
+ expect(API.post).toHaveBeenCalledWith(
113
+ `/foreman_tasks/tasks/${taskId}/resume`
114
+ );
115
+ expect(dispatch).toHaveBeenCalledTimes(3);
116
+ expect(dispatch.mock.calls[0][0]).toEqual({
117
+ type: TASKS_RESUME_REQUEST,
118
+ });
119
+ expect(dispatch.mock.calls[1][0]).toEqual({
120
+ type: TASKS_RESUME_SUCCESS,
121
+ });
122
+ expect(dispatch.mock.calls[2][0]).toEqual(
123
+ toastAction('some-name Task execution was resumed', 'success')
124
+ );
125
+ });
126
+
127
+ it('dispatches failure actions and error toast when resume fails', async () => {
128
+ API.post.mockRejectedValue(new Error('Network Error'));
129
+ const dispatch = jest.fn();
130
+
131
+ await resumeTaskRequest(taskId, taskName)(dispatch);
132
+
133
+ expect(dispatch).toHaveBeenCalledTimes(3);
134
+ expect(dispatch.mock.calls[0][0]).toEqual({
135
+ type: TASKS_RESUME_REQUEST,
136
+ });
137
+ expect(dispatch.mock.calls[1][0]).toEqual({
138
+ type: TASKS_RESUME_FAILURE,
139
+ payload: expect.any(Error),
140
+ });
141
+ expect(dispatch.mock.calls[2][0]).toEqual(
142
+ toastAction('some-name Task execution could not be resumed', 'error')
143
+ );
144
+ });
145
+ });
146
+
147
+ describe('forceCancelTaskRequest', () => {
148
+ it('dispatches success actions and toast when force cancel succeeds', async () => {
149
+ const dispatch = jest.fn();
150
+
151
+ await forceCancelTaskRequest(taskId, taskName)(dispatch);
152
+
153
+ expect(API.post).toHaveBeenCalledWith(
154
+ `/foreman_tasks/tasks/${taskId}/force_unlock`
155
+ );
156
+ expect(dispatch).toHaveBeenCalledTimes(3);
157
+ expect(dispatch.mock.calls[0][0]).toEqual({
158
+ type: TASKS_FORCE_CANCEL_REQUEST,
159
+ });
160
+ expect(dispatch.mock.calls[1][0]).toEqual({
161
+ type: TASKS_FORCE_CANCEL_SUCCESS,
162
+ });
163
+ expect(dispatch.mock.calls[2][0]).toEqual(
164
+ toastAction(
165
+ 'some-name Task execution resources were unlocked with force.',
166
+ 'success'
167
+ )
168
+ );
169
+ });
170
+
171
+ it('dispatches failure actions and warning toast when force cancel fails', async () => {
172
+ API.post.mockRejectedValue(new Error('Network Error'));
173
+ const dispatch = jest.fn();
174
+
175
+ await forceCancelTaskRequest(taskId, taskName)(dispatch);
176
+
177
+ expect(dispatch).toHaveBeenCalledTimes(3);
178
+ expect(dispatch.mock.calls[0][0]).toEqual({
179
+ type: TASKS_FORCE_CANCEL_REQUEST,
180
+ });
181
+ expect(dispatch.mock.calls[1][0]).toEqual({
182
+ type: TASKS_FORCE_CANCEL_FAILURE,
183
+ });
184
+ expect(dispatch.mock.calls[2][0]).toEqual(
185
+ toastAction(
186
+ 'some-name Task execution cannot be cancelled with force at the moment.',
187
+ 'warning'
188
+ )
189
+ );
190
+ });
191
+ });
192
+
193
+ describe('unlockTaskRequest', () => {
194
+ it('dispatches success actions and toast when unlock succeeds', async () => {
195
+ const dispatch = jest.fn();
196
+
197
+ await unlockTaskRequest(taskId, taskName)(dispatch);
198
+
199
+ expect(API.post).toHaveBeenCalledWith(
200
+ `/foreman_tasks/tasks/${taskId}/unlock`
201
+ );
202
+ expect(dispatch).toHaveBeenCalledTimes(3);
203
+ expect(dispatch.mock.calls[0][0]).toEqual({
204
+ type: TASKS_UNLOCK_REQUEST,
205
+ });
206
+ expect(dispatch.mock.calls[1][0]).toEqual({
207
+ type: TASKS_UNLOCK_SUCCESS,
208
+ });
209
+ expect(dispatch.mock.calls[2][0]).toEqual(
210
+ toastAction(
211
+ 'some-name Task execution resources were unlocked ',
212
+ 'success'
213
+ )
214
+ );
215
+ });
216
+
217
+ it('dispatches failure actions and warning toast when unlock fails', async () => {
218
+ API.post.mockRejectedValue(new Error('Network Error'));
219
+ const dispatch = jest.fn();
220
+
221
+ await unlockTaskRequest(taskId, taskName)(dispatch);
222
+
223
+ expect(dispatch).toHaveBeenCalledTimes(3);
224
+ expect(dispatch.mock.calls[0][0]).toEqual({
225
+ type: TASKS_UNLOCK_REQUEST,
226
+ });
227
+ expect(dispatch.mock.calls[1][0]).toEqual({
228
+ type: TASKS_UNLOCK_FAILURE,
229
+ });
230
+ expect(dispatch.mock.calls[2][0]).toEqual(
231
+ toastAction(
232
+ 'some-name Task execution resources cannot be unlocked at the moment.',
233
+ 'warning'
234
+ )
235
+ );
236
+ });
237
+ });
68
238
  });