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