Merge branch '10847-nodemanager-shutdown' refs #10847
[arvados.git] / apps / workbench / test / integration / work_units_test.rb
1 require 'helpers/fake_websocket_helper'
2 require 'integration_helper'
3
4 class WorkUnitsTest < ActionDispatch::IntegrationTest
5   include FakeWebsocketHelper
6
7   setup do
8     need_javascript
9   end
10
11   test "scroll all_processes page" do
12       expected_min, expected_max, expected, not_expected = [
13         25, 100,
14         ['/pipeline_instances/zzzzz-d1hrv-1yfj61234abcdk3',
15          '/pipeline_instances/zzzzz-d1hrv-jobspeccomponts',
16          '/jobs/zzzzz-8i9sb-grx15v5mjnsyxk7',
17          '/jobs/zzzzz-8i9sb-n7omg50bvt0m1nf',
18          '/container_requests/zzzzz-xvhdp-cr4completedcr2',
19          '/container_requests/zzzzz-xvhdp-cr4requestercn2'],
20         ['/pipeline_instances/zzzzz-d1hrv-scarxiyajtshq3l',
21          '/container_requests/zzzzz-xvhdp-oneof60crs00001']
22       ]
23
24       visit page_with_token('active', "/all_processes")
25
26       page_scrolls = expected_max/20 + 2
27       within('.arv-recent-all-processes') do
28         (0..page_scrolls).each do |i|
29           page.driver.scroll_to 0, 999000
30           begin
31             wait_for_ajax
32           rescue
33           end
34         end
35       end
36
37       # Verify that expected number of processes are found
38       found_items = page.all('tr[data-object-uuid]')
39       found_count = found_items.count
40       if expected_min == expected_max
41         assert_equal(true, found_count == expected_min,
42           "Not found expected number of items. Expected #{expected_min} and found #{found_count}")
43         assert page.has_no_text? 'request failed'
44       else
45         assert_equal(true, found_count>=expected_min,
46           "Found too few items. Expected at least #{expected_min} and found #{found_count}")
47         assert_equal(true, found_count<=expected_max,
48           "Found too many items. Expected at most #{expected_max} and found #{found_count}")
49       end
50
51       # verify that all expected uuid links are found
52       expected.each do |link|
53         assert_selector "a[href=\"#{link}\"]"
54       end
55
56       # verify that none of the not_expected uuid links are found
57       not_expected.each do |link|
58         assert_no_selector "a[href=\"#{link}\"]"
59       end
60   end
61
62   [
63     ['jobs', 'running_job_with_components', true, true],
64     ['pipeline_instances', 'components_is_jobspec', true, true],
65     ['containers', 'running', false],
66     ['container_requests', 'running', true],
67   ].each do |type, fixture, cancelable, confirm_cancellation|
68     test "cancel button for #{type}/#{fixture}" do
69       if cancelable
70         need_selenium 'to cancel'
71       end
72
73       obj = api_fixture(type)[fixture]
74       visit page_with_token "active", "/#{type}/#{obj['uuid']}"
75
76       assert_text 'created_at'
77       if cancelable
78         assert_text 'priority: 1' if type.include?('container')
79         if type.include?('pipeline')
80           assert_selector 'a', text: 'Pause'
81           first('a,link', text: 'Pause').click
82         else
83           assert_selector 'button', text: 'Cancel'
84           first('a,button', text: 'Cancel').click
85         end
86         if confirm_cancellation
87           alert = page.driver.browser.switch_to.alert
88           alert.accept
89         end
90         wait_for_ajax
91       end
92
93       if type.include?('pipeline')
94         assert_selector 'a', text: 'Resume'
95         assert_no_selector 'a', text: 'Pause'
96       elsif type.include?('job')
97         assert_text 'Cancelled'
98         assert_text 'Paused'  # this job has a pipeline child which was also cancelled
99         assert_no_selector 'button', text: 'Cancel'
100       elsif cancelable
101         assert_text 'priority: 0'
102       end
103     end
104   end
105
106   [
107     ['jobs', 'running_job_with_components'],
108     ['pipeline_instances', 'has_component_with_completed_jobs'],
109     ['container_requests', 'running'],
110     ['container_requests', 'completed'],
111   ].each do |type, fixture|
112     test "edit description for #{type}/#{fixture}" do
113       obj = api_fixture(type)[fixture]
114       visit page_with_token "active", "/#{type}/#{obj['uuid']}"
115
116       within('.arv-description-as-subtitle') do
117         find('.fa-pencil').click
118         find('.editable-input textarea').set('*Textile description for object*')
119         find('.editable-submit').click
120       end
121       wait_for_ajax
122
123       # verify description
124       assert page.has_no_text? '*Textile description for object*'
125       assert page.has_text? 'Textile description for object'
126     end
127   end
128
129   [
130     ['Pipeline with default input specifications', 'part-one', 'Provide values for the following'],
131     ['Workflow with default input specifications', 'this workflow has inputs specified', 'Provide a value for the following'],
132   ].each do |template_name, preview_txt, process_txt|
133     test "run a process using template #{template_name} from dashboard" do
134       visit page_with_token('admin')
135       assert_text 'Recent pipelines and processes' # seeing dashboard now
136
137       within('.recent-processes-actions') do
138         assert page.has_link?('All processes')
139         find('a', text: 'Run a process').click
140       end
141
142       # in the chooser, verify preview and click Next button
143       within('.modal-dialog') do
144         find('.selectable', text: template_name).click
145         assert_text preview_txt
146         find('.btn', text: 'Next: choose inputs').click
147       end
148
149       # in the process page now
150       assert_text process_txt
151       assert_selector 'a', text: template_name
152
153       assert_equal "Set value for ex_string_def", find('div.form-group > div > p.form-control-static > a', text: "hello-testing-123")[:"data-title"]
154
155       page.assert_selector 'a.disabled,button.disabled', text: 'Run'
156     end
157   end
158
159   test 'display container state changes in Container Request live log' do
160     use_fake_websocket_driver
161     c = api_fixture('containers')['queued']
162     cr = api_fixture('container_requests')['queued']
163     visit page_with_token('active', '/container_requests/'+cr['uuid'])
164     click_link('Log')
165
166     # The attrs of the "terminal window" text div in the log tab
167     # indicates which objects' events are worth displaying. Events
168     # that arrive too early (before that div exists) are not
169     # shown. For the user's sake, these early logs should also be
170     # retrieved and shown one way or another -- but in this particular
171     # test, we are only interested in logs that arrive by
172     # websocket. Therefore, to avoid races, we wait for the log tab to
173     # display before sending any events.
174     assert_text 'Recent logs'
175
176     [[{
177         event_type: 'dispatch',
178         properties: {
179           text: "dispatch logged a fake message\n",
180         },
181       }, "dispatch logged"],
182      [{
183         event_type: 'update',
184         properties: {
185           old_attributes: {state: 'Locked'},
186           new_attributes: {state: 'Queued'},
187         },
188       }, "Container #{c['uuid']} was returned to the queue"],
189      [{
190         event_type: 'update',
191         properties: {
192           old_attributes: {state: 'Queued'},
193           new_attributes: {state: 'Locked'},
194         },
195       }, "Container #{c['uuid']} was taken from the queue by a dispatch process"],
196      [{
197         event_type: 'crunch-run',
198         properties: {
199           text: "according to fake crunch-run,\nsome setup stuff happened on the compute node\n",
200         },
201       }, "setup stuff happened"],
202      [{
203         event_type: 'update',
204         properties: {
205           old_attributes: {state: 'Locked'},
206           new_attributes: {state: 'Running'},
207         },
208       }, "Container #{c['uuid']} started"],
209      [{
210         event_type: 'update',
211         properties: {
212           old_attributes: {state: 'Running'},
213           new_attributes: {state: 'Complete', exit_code: 1},
214         },
215       }, "Container #{c['uuid']} finished with exit code 1 (failure)"],
216      # It's unrealistic for state to change again once it's Complete,
217      # but the logging code doesn't care, so we do it to keep the test
218      # simple.
219      [{
220         event_type: 'update',
221         properties: {
222           old_attributes: {state: 'Running'},
223           new_attributes: {state: 'Cancelled'},
224         },
225       }, "Container #{c['uuid']} was cancelled"],
226     ].each do |send_event, expect_log_text|
227       assert_no_text(expect_log_text)
228       fake_websocket_event(send_event.merge(object_uuid: c['uuid']))
229       assert_text(expect_log_text)
230     end
231   end
232
233   [
234     ['jobs', 'active', 'running_job_with_components', 'component1', '/jobs/zzzzz-8i9sb-jyq01m7in1jlofj#Log'],
235     ['pipeline_instances', 'active', 'pipeline_in_running_state', 'foo', '/jobs/zzzzz-8i9sb-pshmckwoma9plh7#Log'],
236     ['pipeline_instances', nil, 'pipeline_in_publicly_accessible_project_but_other_objects_elsewhere', 'foo', 'Log unavailable'],
237   ].each do |type, token, fixture, child, log_link|
238     test "link_to_log for #{fixture} for #{token}" do
239       obj = api_fixture(type)[fixture]
240       if token
241         visit page_with_token token, "/#{type}/#{obj['uuid']}"
242       else
243         Rails.configuration.anonymous_user_token =
244           api_fixture("api_client_authorizations", "anonymous", "api_token")
245         visit "/#{type}/#{obj['uuid']}"
246       end
247
248       click_link(child)
249
250       if token
251         assert_selector "a[href=\"#{log_link}\"]"
252       else
253         assert_text log_link
254       end
255     end
256   end
257
258   test 'Run from workflows index page' do
259     visit page_with_token('active', '/workflows')
260
261     wf_count = page.all('a[data-original-title="show workflow"]').count
262     assert_equal true, wf_count>0
263
264     # Run one of the workflows
265     wf_name = 'Workflow with input specifications'
266     within('tr', text: wf_name) do
267       find('a,button', text: 'Run').click
268     end
269
270     # Choose project for the container_request being created
271     within('.modal-dialog') do
272       find('.selectable', text: 'A Project').click
273       find('button', text: 'Choose').click
274     end
275
276     # In newly created container_request page now
277     assert_text 'A Project' # CR created in "A Project"
278     assert_text "This container request was created from the workflow #{wf_name}"
279     assert_match /Provide a value for .* then click the \"Run\" button to start the workflow/, page.text
280   end
281
282   test 'Run workflow from show page' do
283     visit page_with_token('active', '/workflows/zzzzz-7fd4e-validwithinputs')
284
285     find('a,button', text: 'Run this workflow').click
286
287     # Choose project for the container_request being created
288     within('.modal-dialog') do
289       find('.selectable', text: 'A Project').click
290       find('button', text: 'Choose').click
291     end
292
293     # In newly created container_request page now
294     assert_text 'A Project' # CR created in "A Project"
295     assert_text "This container request was created from the workflow"
296     assert_match /Provide a value for .* then click the \"Run\" button to start the workflow/, page.text
297   end
298 end