1 require 'integration_helper'
3 class PipelineInstancesTest < ActionDispatch::IntegrationTest
8 test 'Create and run a pipeline' do
9 visit page_with_token('active_trustedclient', '/pipeline_templates')
10 within('tr', text: 'Two Part Pipeline Template') do
11 find('a,button', text: 'Run').click
15 within('.modal-dialog') do
16 find('.selectable', text: 'A Project').click
17 find('button', text: 'Choose').click
20 # This pipeline needs input. So, Run should be disabled
21 page.assert_selector 'a.disabled,button.disabled', text: 'Run'
23 instance_page = current_path
25 # Add this collection to the project
27 find("#projects-menu").click
28 find('.dropdown-menu a,button', text: 'A Project').click
29 find('.btn', text: 'Add data').click
30 find('.dropdown-menu a,button', text: 'Copy data from another project').click
31 within('.modal-dialog') do
33 first('span', text: 'foo_tag').click
34 find('.btn', text: 'Copy').click
36 using_wait_time(Capybara.default_wait_time * 3) do
40 click_link 'Jobs and pipelines'
41 find('tr[data-kind="arvados#pipelineInstance"]', text: '(none)').
42 find('a', text: 'Show').
45 assert find('p', text: 'Provide a value')
47 find('div.form-group', text: 'Foo/bar pair').
48 find('.btn', text: 'Choose').
51 within('.modal-dialog') do
52 assert(has_text?("Foo/bar pair"),
53 "pipeline input picker missing name of input")
55 first('span', text: 'foo_tag').click
56 find('button', text: 'OK').click
60 # The input, after being specified, should still be displayed (#3382)
61 assert find('div.form-group', text: 'Foo/bar pair')
63 # The input, after being specified, should still be editable (#3382)
64 find('div.form-group', text: 'Foo/bar pair').
65 find('.btn', text: 'Choose').click
67 within('.modal-dialog') do
68 assert(has_text?("Foo/bar pair"),
69 "pipeline input picker missing name of input")
71 first('span', text: 'foo_tag').click
72 find('button', text: 'OK').click
75 # For good measure, check one last time that the input, after being specified twice, is still be displayed (#3382)
76 assert find('div.form-group', text: 'Foo/bar pair')
78 # Ensure that the collection's portable_data_hash, uuid and name
79 # are saved in the desired places. (#4015)
81 # foo_collection_in_aproject is the collection tagged with foo_tag.
82 collection = api_fixture('collections', 'foo_collection_in_aproject')
84 click_link 'API response'
85 api_response = JSON.parse(find('div#advanced_api_response pre').text)
86 input_params = api_response['components']['part-one']['script_parameters']['input']
87 assert_equal input_params['value'], collection['portable_data_hash']
88 assert_equal input_params['selection_name'], collection['name']
89 assert_equal input_params['selection_uuid'], collection['uuid']
91 # "Run" button is now enabled
92 page.assert_no_selector 'a.disabled,button.disabled', text: 'Run'
94 first('a,button', text: 'Run').click
96 # Pipeline is running. We have a "Pause" button instead now.
97 page.assert_selector 'a,button', text: 'Pause'
98 find('a,button', text: 'Pause').click
100 # Pipeline is stopped. It should now be in paused state and Runnable again.
101 assert page.has_text? 'Paused'
102 page.assert_no_selector 'a.disabled,button.disabled', text: 'Resume'
103 page.assert_selector 'a,button', text: 'Re-run with latest'
104 page.assert_selector 'a,button', text: 'Re-run options'
106 # Since it is test env, no jobs are created to run. So, graph not visible
107 assert_not page.has_text? 'Graph'
110 # Create a pipeline instance from within a project and run
111 test 'Create pipeline inside a project and run' do
112 visit page_with_token('active_trustedclient', '/projects')
114 # Add collection to the project using Add data button
115 find("#projects-menu").click
116 find('.dropdown-menu a,button', text: 'A Project').click
117 find('.btn', text: 'Add data').click
118 find('.dropdown-menu a,button', text: 'Copy data from another project').click
119 within('.modal-dialog') do
121 first('span', text: 'foo_tag').click
122 find('.btn', text: 'Copy').click
124 using_wait_time(Capybara.default_wait_time * 3) do
128 create_and_run_pipeline_in_aproject true, 'Two Part Pipeline Template', 'foo_collection_in_aproject', false
131 # Create a pipeline instance from outside of a project
132 test 'Run a pipeline from dashboard' do
133 visit page_with_token('active_trustedclient')
134 create_and_run_pipeline_in_aproject false, 'Two Part Pipeline Template', 'foo_collection_in_aproject', false
137 test 'view pipeline with job and see graph' do
138 visit page_with_token('active_trustedclient', '/pipeline_instances')
139 assert page.has_text? 'pipeline_with_job'
141 find('a', text: 'pipeline_with_job').click
143 # since the pipeline component has a job, expect to see the graph
144 assert page.has_text? 'Graph'
146 page.assert_selector "#provenance_graph"
149 test 'pipeline description' do
150 visit page_with_token('active_trustedclient', '/pipeline_instances')
151 assert page.has_text? 'pipeline_with_job'
153 find('a', text: 'pipeline_with_job').click
155 within('.arv-description-as-subtitle') do
156 find('.fa-pencil').click
157 find('.editable-input textarea').set('*Textile description for pipeline instance*')
158 find('.editable-submit').click
163 assert page.has_no_text? '*Textile description for pipeline instance*'
164 assert page.has_text? 'Textile description for pipeline instance'
167 test "JSON popup available for strange components" do
168 uuid = api_fixture("pipeline_instances")["components_is_jobspec"]["uuid"]
169 visit page_with_token("active", "/pipeline_instances/#{uuid}")
170 click_on "Components"
171 assert(page.has_no_text?("script_parameters"),
172 "components JSON visible without popup")
173 click_on "Show components JSON"
174 assert(page.has_text?("script_parameters"),
175 "components JSON not found")
178 def create_pipeline_from(template_name, project_name="Home")
179 # Visit the named pipeline template and create a pipeline instance from it.
180 # The instance will be created under the named project.
181 template_uuid = api_fixture("pipeline_templates", template_name, "uuid")
182 visit page_with_token("active", "/pipeline_templates/#{template_uuid}")
183 click_on "Run this pipeline"
184 within(".modal-dialog") do
185 # Set project for the new pipeline instance
186 find(".selectable", text: project_name).click
189 assert(has_text?("This pipeline was created from the template"),
190 "did not land on pipeline instance page")
193 PROJECT_WITH_SEARCH_COLLECTION = "A Subproject"
194 def check_parameter_search(proj_name)
195 create_pipeline_from("parameter_with_search", proj_name)
196 search_text = api_fixture("pipeline_templates", "parameter_with_search",
197 "components", "with-search",
198 "script_parameters", "input", "search_for")
199 first("a.btn,button", text: "Choose").click
200 within(".modal-body") do
201 if (proj_name != PROJECT_WITH_SEARCH_COLLECTION)
202 # Switch finder modal to Subproject to find the Collection.
204 click_on PROJECT_WITH_SEARCH_COLLECTION
206 assert_equal(search_text, first("input").value,
207 "parameter search not preseeded")
208 assert(has_text?(api_fixture("collections")["baz_collection_name_in_asubproject"]["name"]),
209 "baz Collection not in preseeded search results")
213 test "Workbench respects search_for parameter in templates" do
214 check_parameter_search(PROJECT_WITH_SEARCH_COLLECTION)
217 test "Workbench preserves search_for parameter after project switch" do
218 check_parameter_search("A Project")
221 test "enter a float for a number pipeline input" do
222 # Poltergeist either does not support the HTML 5 <input
223 # type="number">, or interferes with the associated X-Editable
224 # validation code. If the input field has type=number (forcing an
225 # integer), this test will yield a false positive under
226 # Poltergeist. --Brett, 2015-02-05
227 need_selenium "for strict X-Editable input validation"
228 create_pipeline_from("template_with_dataclass_number")
230 ".editable[data-name='[components][work][script_parameters][input][value]']"
231 find(INPUT_SELECTOR).click
232 find(".editable-input input").set("12.34")
233 find("#editable-submit").click
234 assert_no_selector(".editable-popup")
235 assert_selector(INPUT_SELECTOR, text: "12.34")
239 [true, 'Two Part Pipeline Template', 'foo_collection_in_aproject', false],
240 [false, 'Two Part Pipeline Template', 'foo_collection_in_aproject', false],
241 [true, 'Two Part Template with dataclass File', 'foo_collection_in_aproject', true],
242 [false, 'Two Part Template with dataclass File', 'foo_collection_in_aproject', true],
243 [true, 'Two Part Pipeline Template', 'collection_with_no_name_in_aproject', false],
244 ].each do |in_aproject, template_name, collection, choose_file|
245 test "Run pipeline instance in #{in_aproject} with #{template_name} with #{collection} file #{choose_file}" do
247 visit page_with_token 'active', \
248 '/projects/'+api_fixture('groups')['aproject']['uuid']
250 visit page_with_token 'active', '/'
253 # need bigger modal size when choosing a file from collection
254 if Capybara.current_driver == :selenium
255 Capybara.current_session.driver.browser.manage.window.resize_to(1200, 800)
258 create_and_run_pipeline_in_aproject in_aproject, template_name, collection, choose_file
259 instance_path = current_path
262 find('a,button', text: 'Pause').click
263 assert page.has_text? 'Paused'
264 page.assert_no_selector 'a.disabled,button.disabled', text: 'Resume'
265 page.assert_selector 'a,button', text: 'Re-run with latest'
266 page.assert_selector 'a,button', text: 'Re-run options'
268 # Verify that the newly created instance is created in the right project.
269 assert page.has_text? 'Home'
271 assert page.has_text? 'A Project'
273 assert page.has_no_text? 'A Project'
279 ['active', false, false, false],
280 ['active', false, false, true],
281 ['active', true, false, false],
282 ['active', true, true, false],
283 ['active', true, false, true],
284 ['active', true, true, true],
285 ['project_viewer', false, false, true],
286 ['project_viewer', true, true, true],
287 ].each do |user, with_options, choose_options, in_aproject|
288 test "Rerun pipeline instance as #{user} using options #{with_options} #{choose_options} in #{in_aproject}" do
290 path = '/pipeline_instances/'+api_fixture('pipeline_instances')['pipeline_owned_by_active_in_aproject']['uuid']
292 path = '/pipeline_instances/'+api_fixture('pipeline_instances')['pipeline_owned_by_active_in_home']['uuid']
295 visit page_with_token(user, path)
297 page.assert_selector 'a,button', text: 'Re-run with latest'
298 page.assert_selector 'a,button', text: 'Re-run options'
300 if user == 'project_viewer' && in_aproject
301 assert page.has_text? 'A Project'
304 # Now re-run the pipeline
306 assert_triggers_dom_event 'shown.bs.modal' do
307 find('a,button', text: 'Re-run options').click
309 within('.modal-dialog') do
310 page.assert_selector 'a,button', text: 'Copy and edit inputs'
311 page.assert_selector 'a,button', text: 'Run now'
313 find('button', text: 'Copy and edit inputs').click
315 find('button', text: 'Run now').click
319 find('a,button', text: 'Re-run with latest').click
322 # Verify that the newly created instance is created in the right
323 # project. In case of project_viewer user, since the user cannot
324 # write to the project, the pipeline should have been created in
325 # the user's Home project.
326 assert_not_equal path, current_path, 'Rerun instance path expected to be different'
328 if in_aproject && (user != 'project_viewer')
329 assert_text 'A Project'
331 assert_no_text 'A Project'
336 # Create and run a pipeline for 'Two Part Pipeline Template' in 'A Project'
337 def create_and_run_pipeline_in_aproject in_aproject, template_name, collection_fixture, choose_file=false
338 # collection in aproject to be used as input
339 collection = api_fixture('collections', collection_fixture)
341 # create a pipeline instance
342 find('.btn', text: 'Run a pipeline').click
343 within('.modal-dialog') do
344 find('.selectable', text: template_name).click
345 find('.btn', text: 'Next: choose inputs').click
348 assert find('p', text: 'Provide a value')
350 find('div.form-group', text: 'Foo/bar pair').
351 find('.btn', text: 'Choose').
354 within('.modal-dialog') do
356 assert_selector 'button.dropdown-toggle', text: 'A Project'
359 assert_selector 'button.dropdown-toggle', text: 'Home'
362 click_link "A Project"
366 if collection_fixture == 'foo_collection_in_aproject'
367 first('span', text: 'foo_tag').click
368 elsif collection['name']
369 first('span', text: "#{collection['name']}").click
371 collection_uuid = collection['uuid']
372 find("div[data-object-uuid=#{collection_uuid}]").click
377 find('.preview-selectable', text: 'foo').click
379 find('button', text: 'OK').click
382 # The input, after being specified, should still be displayed (#3382)
383 assert find('div.form-group', text: 'Foo/bar pair')
385 # Ensure that the collection's portable_data_hash, uuid and name
386 # are saved in the desired places. (#4015)
387 click_link 'Advanced'
388 click_link 'API response'
390 api_response = JSON.parse(find('div#advanced_api_response pre').text)
391 input_params = api_response['components']['part-one']['script_parameters']['input']
392 assert_equal(input_params['selection_uuid'], collection['uuid'], "Not found expected input param uuid")
394 assert_equal(input_params['value'], collection['portable_data_hash']+'/foo', "Not found expected input file param value")
395 assert_equal(input_params['selection_name'], collection['name']+'/foo', "Not found expected input file param name")
397 assert_equal(input_params['value'], collection['portable_data_hash'], "Not found expected input param value")
398 assert_equal(input_params['selection_name'], collection['name'], "Not found expected input selection name")
401 # "Run" button present and enabled
402 page.assert_no_selector 'a.disabled,button.disabled', text: 'Run'
403 first('a,button', text: 'Run').click
405 # Pipeline is running. We have a "Pause" button instead now.
406 page.assert_no_selector 'a,button', text: 'Run'
407 page.assert_no_selector 'a.disabled,button.disabled', text: 'Resume'
408 page.assert_selector 'a,button', text: 'Pause'
410 # Since it is test env, no jobs are created to run. So, graph not visible
411 assert_not page.has_text? 'Graph'
415 [1, 0], # run time 0 minutes
416 [10, 17*60*60 + 51*60], # run time 17 hours and 51 minutes
417 ].each do |index, run_time|
418 test "pipeline start and finish time display #{index}" do
419 visit page_with_token("user1_with_load", "/pipeline_instances/zzzzz-d1hrv-10pipelines0#{index.to_s.rjust(3, '0')}")
421 assert page.has_text? 'This pipeline started at'
422 page_text = page.text
424 match = /This pipeline started at (.*)\. It failed after (.*) seconds at (.*)\. Check the Log/.match page_text
425 assert_not_nil(match, 'Did not find text - This pipeline started at . . . ')
428 finished_at = match[3]
429 assert_not_nil(start_at, 'Did not find start_at time')
430 assert_not_nil(finished_at, 'Did not find finished_at time')
432 # start and finished time display is of the format '2:20 PM 10/20/2014'
433 start_time = DateTime.strptime(start_at, '%H:%M %p %m/%d/%Y').to_time
434 finished_time = DateTime.strptime(finished_at, '%H:%M %p %m/%d/%Y').to_time
435 assert_equal(run_time, finished_time-start_time,
436 "Time difference did not match for start_at #{start_at}, finished_at #{finished_at}, ran_for #{match[2]}")
441 ['fuse', nil, 2, 20], # has 2 as of 11-07-2014
442 ['user1_with_load', '000025pipelines', 25, 25], # owned_by the project zzzzz-j7d0g-000025pipelines, two pages
443 ['admin', 'pipeline_20', 1, 1],
444 ['active', 'no such match', 0, 0],
445 ].each do |user, search_filter, expected_min, expected_max|
446 test "scroll pipeline instances page for #{user} with search filter #{search_filter}
447 and expect #{expected_min} <= found_items <= #{expected_max}" do
448 visit page_with_token(user, "/pipeline_instances")
451 find('.recent-pipeline-instances-filterable-control').set(search_filter)
452 # Wait for 250ms debounce timer (see filterable.js)
457 page_scrolls = expected_max/20 + 2 # scroll num_pages+2 times to test scrolling is disabled when it should be
458 within('.arv-recent-pipeline-instances') do
459 (0..page_scrolls).each do |i|
460 page.driver.scroll_to 0, 999000
468 # Verify that expected number of pipeline instances are found
469 found_items = page.all('tr[data-kind="arvados#pipelineInstance"]')
470 found_count = found_items.count
471 if expected_min == expected_max
472 assert_equal(true, found_count == expected_min,
473 "Not found expected number of items. Expected #{expected_min} and found #{found_count}")
474 assert page.has_no_text? 'request failed'
476 assert_equal(true, found_count>=expected_min,
477 "Found too few items. Expected at least #{expected_min} and found #{found_count}")
478 assert_equal(true, found_count<=expected_max,
479 "Found too many items. Expected at most #{expected_max} and found #{found_count}")