1 require 'integration_helper'
2 require 'selenium-webdriver'
5 class ProjectsTest < ActionDispatch::IntegrationTest
7 headless = Headless.new
9 Capybara.current_driver = :selenium
11 # project tests need bigger page size to be able to see all the buttons
12 Capybara.current_session.driver.browser.manage.window.resize_to(1152, 768)
15 test 'Check collection count for A Project in the tab pane titles' do
16 project_uuid = api_fixture('groups')['aproject']['uuid']
17 visit page_with_token 'active', '/projects/' + project_uuid
19 collection_count = page.all("[data-pk*='collection']").count
20 assert_selector '#Data_collections-tab span', text: "(#{collection_count})"
23 test 'Find a project and edit its description' do
24 visit page_with_token 'active', '/'
25 find("#projects-menu").click
26 find(".dropdown-menu a", text: "A Project").click
27 within('.container-fluid', text: api_fixture('groups')['aproject']['name']) do
28 find('span', text: api_fixture('groups')['aproject']['name']).click
29 within('.arv-description-as-subtitle') do
30 find('.fa-pencil').click
31 find('.editable-input textarea').set('I just edited this.')
32 find('.editable-submit').click
37 assert(find?('.container-fluid', text: 'I just edited this.'),
38 "Description update did not survive page refresh")
41 test 'Find a project and edit description to textile description' do
42 visit page_with_token 'active', '/'
43 find("#projects-menu").click
44 find(".dropdown-menu a", text: "A Project").click
45 within('.container-fluid', text: api_fixture('groups')['aproject']['name']) do
46 find('span', text: api_fixture('groups')['aproject']['name']).click
47 within('.arv-description-as-subtitle') do
48 find('.fa-pencil').click
49 find('.editable-input textarea').set('<p>*Textile description for A project* - "take me home":/ </p><p>And a new paragraph in description.</p>')
50 find('.editable-submit').click
57 assert_no_text '*Textile description for A project*'
58 assert(find?('.container-fluid', text: 'Textile description for A project'),
59 "Description update did not survive page refresh")
60 assert(find?('.container-fluid', text: 'And a new paragraph in description'),
61 "Description did not contain the expected new paragraph")
62 assert(page.has_link?("take me home"), "link not found in description")
64 click_link 'take me home'
67 assert(page.has_text?('Active pipelines'), 'Active pipelines - not found on dashboard')
70 test 'Find a project and edit description to html description' do
71 visit page_with_token 'active', '/'
72 find("#projects-menu").click
73 find(".dropdown-menu a", text: "A Project").click
74 within('.container-fluid', text: api_fixture('groups')['aproject']['name']) do
75 find('span', text: api_fixture('groups')['aproject']['name']).click
76 within('.arv-description-as-subtitle') do
77 find('.fa-pencil').click
78 find('.editable-input textarea').set('<br>Textile description for A project</br> - <a href="/">take me home</a>')
79 find('.editable-submit').click
84 assert(find?('.container-fluid', text: 'Textile description for A project'),
85 "Description update did not survive page refresh")
86 assert(!find?('.container-fluid', text: '<br>Textile description for A project</br>'),
87 "Textile description is displayed with uninterpreted formatting characters")
88 assert(page.has_link?("take me home"),"link not found in description")
89 click_link 'take me home'
90 assert page.has_text?('Active pipelines')
93 test 'Find a project and edit description to textile description with link to object' do
94 visit page_with_token 'active', '/'
95 find("#projects-menu").click
96 find(".dropdown-menu a", text: "A Project").click
97 within('.container-fluid', text: api_fixture('groups')['aproject']['name']) do
98 find('span', text: api_fixture('groups')['aproject']['name']).click
99 within('.arv-description-as-subtitle') do
100 find('.fa-pencil').click
101 find('.editable-input textarea').set('*Textile description for A project* - "go to sub-project":' + api_fixture('groups')['asubproject']['uuid'] + "'")
102 find('.editable-submit').click
107 assert(find?('.container-fluid', text: 'Textile description for A project'),
108 "Description update did not survive page refresh")
109 assert(!find?('.container-fluid', text: '*Textile description for A project*'),
110 "Textile description is displayed with uninterpreted formatting characters")
111 assert(page.has_link?("go to sub-project"), "link not found in description")
112 click_link 'go to sub-project'
113 assert(page.has_text?(api_fixture('groups')['asubproject']['name']), 'sub-project name not found after clicking link')
116 test 'Add a new name, then edit it, without creating a duplicate' do
117 project_uuid = api_fixture('groups')['aproject']['uuid']
118 specimen_uuid = api_fixture('traits')['owned_by_aproject_with_no_name']['uuid']
119 visit page_with_token 'active', '/projects/' + project_uuid
120 click_link 'Other objects'
121 within '.selection-action-container' do
122 # Wait for the tab to load:
123 assert_selector 'tr[data-kind="arvados#trait"]'
124 within first('tr', text: 'Trait') do
125 find(".fa-pencil").click
126 find('.editable-input input').set('Now I have a name.')
127 find('.glyphicon-ok').click
128 assert_selector '.editable', text: 'Now I have a name.'
129 find(".fa-pencil").click
130 find('.editable-input input').set('Now I have a new name.')
131 find('.glyphicon-ok').click
134 assert_selector '.editable', text: 'Now I have a new name.'
137 click_link 'Other objects'
138 within '.selection-action-container' do
139 find '.editable', text: 'Now I have a new name.'
140 page.assert_no_selector '.editable', text: 'Now I have a name.'
144 test 'Create a project and move it into a different project' do
145 visit page_with_token 'active', '/projects'
146 find("#projects-menu").click
147 find(".dropdown-menu a", text: "Home").click
148 find('.btn', text: "Add a subproject").click
150 # within('.editable', text: 'New project') do
152 find('.fa-pencil').click
153 find('.editable-input input').set('Project 1234')
154 find('.glyphicon-ok').click
159 find("#projects-menu").click
160 find(".dropdown-menu a", text: "Home").click
161 find('.btn', text: "Add a subproject").click
163 find('.fa-pencil').click
164 find('.editable-input input').set('Project 5678')
165 find('.glyphicon-ok').click
169 click_link 'Move project...'
170 find('.selectable', text: 'Project 1234').click
171 find('.modal-footer a,button', text: 'Move').click
174 # Wait for the page to refresh and show the new parent in Sharing panel
176 assert(page.has_link?("Project 1234"),
177 "Project 5678 should now be inside project 1234")
180 def show_project_using(auth_key, proj_key='aproject')
181 project_uuid = api_fixture('groups')[proj_key]['uuid']
182 visit(page_with_token(auth_key, "/projects/#{project_uuid}"))
183 assert(page.has_text?("A Project"), "not on expected project page")
187 find('#project_sharing').all('tr')
190 def add_share_and_check(share_type, name, obj=nil)
191 assert(page.has_no_text?(name), "project is already shared with #{name}")
192 start_share_count = share_rows.size
193 click_on("Share with #{share_type}")
194 within(".modal-container") do
195 # Order is important here: we should find something that appears in the
196 # modal before we make any assertions about what's not in the modal.
197 # Otherwise, the not-included assertions might falsely pass because
198 # the modal hasn't loaded yet.
199 find(".selectable", text: name).click
200 assert(has_no_selector?(".modal-dialog-preview-pane"),
201 "preview pane available in sharing dialog")
202 if share_type == 'users' and obj and obj['email']
203 assert(page.has_text?(obj['email']), "Did not find user's email")
205 assert_raises(Capybara::ElementNotFound,
206 "Projects pulldown available from sharing dialog") do
207 click_on "All projects"
211 using_wait_time(Capybara.default_wait_time * 3) do
212 assert(page.has_link?(name),
213 "new share was not added to sharing table")
214 assert_equal(start_share_count + 1, share_rows.size,
215 "new share did not add row to sharing table")
219 def modify_share_and_check(name)
220 start_rows = share_rows
221 link_row = start_rows.select { |row| row.has_text?(name) }
222 assert_equal(1, link_row.size, "row with new permission not found")
223 within(link_row.first) do
225 select("Write", from: "share_change_level")
226 click_on("editable-submit")
227 assert(has_link?("Write"),
228 "failed to change access level on new share")
230 page.driver.browser.switch_to.alert.accept
233 using_wait_time(Capybara.default_wait_time * 3) do
234 assert(page.has_no_text?(name),
235 "new share row still exists after being revoked")
236 assert_equal(start_rows.size - 1, share_rows.size,
237 "revoking share did not remove row from sharing table")
241 test "project viewer can't see project sharing tab" do
242 show_project_using("project_viewer")
243 assert(page.has_no_link?("Sharing"),
244 "read-only project user sees sharing tab")
247 test "project owner can manage sharing for another user" do
248 add_user = api_fixture('users')['future_project_user']
249 new_name = ["first_name", "last_name"].map { |k| add_user[k] }.join(" ")
251 show_project_using("active")
253 add_share_and_check("users", new_name, add_user)
254 modify_share_and_check(new_name)
257 test "project owner can manage sharing for another group" do
258 new_name = api_fixture('groups')['future_project_viewing_group']['name']
260 show_project_using("active")
262 add_share_and_check("groups", new_name)
263 modify_share_and_check(new_name)
266 test "'share with group' listing does not offer projects" do
267 show_project_using("active")
269 click_on "Share with groups"
270 good_uuid = api_fixture("groups")["private"]["uuid"]
271 assert(page.has_selector?(".selectable[data-object-uuid=\"#{good_uuid}\"]"),
272 "'share with groups' listing missing owned user group")
273 bad_uuid = api_fixture("groups")["asubproject"]["uuid"]
274 assert(page.has_no_selector?(".selectable[data-object-uuid=\"#{bad_uuid}\"]"),
275 "'share with groups' listing includes project")
279 ['Move',api_fixture('collections')['collection_to_move_around_in_aproject'],
280 api_fixture('groups')['aproject'],api_fixture('groups')['asubproject']],
281 ['Remove',api_fixture('collections')['collection_to_move_around_in_aproject'],
282 api_fixture('groups')['aproject']],
283 ['Copy',api_fixture('collections')['collection_to_move_around_in_aproject'],
284 api_fixture('groups')['aproject'],api_fixture('groups')['asubproject']],
285 ['Remove',api_fixture('collections')['collection_in_aproject_with_same_name_as_in_home_project'],
286 api_fixture('groups')['aproject'],nil,true],
287 ].each do |action, my_collection, src, dest=nil, expect_name_change=nil|
288 test "selection #{action} -> #{expect_name_change.inspect} for project" do
289 perform_selection_action src, dest, my_collection, action
293 assert page.has_text?(my_collection['name']), 'Collection not found in src project after copy'
294 visit page_with_token 'active', '/'
295 find("#projects-menu").click
296 find(".dropdown-menu a", text: dest['name']).click
297 assert page.has_text?(my_collection['name']), 'Collection not found in dest project after copy'
300 assert page.has_no_text?(my_collection['name']), 'Collection still found in src project after move'
301 visit page_with_token 'active', '/'
302 find("#projects-menu").click
303 find(".dropdown-menu a", text: dest['name']).click
304 assert page.has_text?(my_collection['name']), 'Collection not found in dest project after move'
307 assert page.has_no_text?(my_collection['name']), 'Collection still found in src project after remove'
308 visit page_with_token 'active', '/'
309 find("#projects-menu").click
310 find(".dropdown-menu a", text: "Home").click
311 assert page.has_text?(my_collection['name']), 'Collection not found in home project after remove'
312 if expect_name_change
313 assert page.has_text?(my_collection['name']+' removed from ' + src['name']),
314 'Collection with update name is not found in home project after remove'
320 def perform_selection_action src, dest, item, action
321 visit page_with_token 'active', '/'
322 find("#projects-menu").click
323 find(".dropdown-menu a", text: src['name']).click
324 assert page.has_text?(item['name']), 'Collection not found in src project'
326 within('tr', text: item['name']) do
327 find('input[type=checkbox]').click
330 click_button 'Selection'
332 within('.selection-action-container') do
333 assert page.has_text?("Compare selected"), "Compare selected link text not found"
334 assert page.has_link?("Copy selected"), "Copy selected link not found"
335 assert page.has_link?("Move selected"), "Move selected link not found"
336 assert page.has_link?("Remove selected"), "Remove selected link not found"
338 click_link "#{action} selected"
341 # select the destination project if a Copy or Move action is being performed
342 if action == 'Copy' || action == 'Move'
343 within(".modal-container") do
344 find('.selectable', text: dest['name']).click
345 find('.modal-footer a,button', text: action).click
351 # Test copy action state. It should not be available when a subproject is selected.
352 test "copy action is disabled when a subproject is selected" do
353 my_project = api_fixture('groups')['aproject']
354 my_collection = api_fixture('collections')['collection_to_move_around_in_aproject']
355 my_subproject = api_fixture('groups')['asubproject']
357 # verify that selection options are disabled on the project until an item is selected
358 visit page_with_token 'active', '/'
359 find("#projects-menu").click
360 find(".dropdown-menu a", text: my_project['name']).click
362 click_button 'Selection'
363 within('.selection-action-container') do
364 page.assert_selector 'li.disabled', text: 'Create new collection with selected collections'
365 page.assert_selector 'li.disabled', text: 'Compare selected'
366 page.assert_selector 'li.disabled', text: 'Copy selected'
367 page.assert_selector 'li.disabled', text: 'Move selected'
368 page.assert_selector 'li.disabled', text: 'Remove selected'
371 # select collection and verify links are enabled
372 visit page_with_token 'active', '/'
373 find("#projects-menu").click
374 find(".dropdown-menu a", text: my_project['name']).click
375 assert page.has_text?(my_collection['name']), 'Collection not found in project'
377 within('tr', text: my_collection['name']) do
378 find('input[type=checkbox]').click
381 click_button 'Selection'
382 within('.selection-action-container') do
383 page.assert_no_selector 'li.disabled', text: 'Create new collection with selected collections'
384 page.assert_selector 'li', text: 'Create new collection with selected collections'
385 page.assert_selector 'li.disabled', text: 'Compare selected'
386 page.assert_no_selector 'li.disabled', text: 'Copy selected'
387 page.assert_selector 'li', text: 'Copy selected'
388 page.assert_no_selector 'li.disabled', text: 'Move selected'
389 page.assert_selector 'li', text: 'Move selected'
390 page.assert_no_selector 'li.disabled', text: 'Remove selected'
391 page.assert_selector 'li', text: 'Remove selected'
394 # select subproject and verify that copy action is disabled
395 visit page_with_token 'active', '/'
396 find("#projects-menu").click
397 find(".dropdown-menu a", text: my_project['name']).click
399 click_link 'Subprojects'
400 assert page.has_text?(my_subproject['name']), 'Subproject not found in project'
402 within('tr', text: my_subproject['name']) do
403 find('input[type=checkbox]').click
406 click_button 'Selection'
407 within('.selection-action-container') do
408 page.assert_selector 'li.disabled', text: 'Create new collection with selected collections'
409 page.assert_selector 'li.disabled', text: 'Compare selected'
410 page.assert_selector 'li.disabled', text: 'Copy selected'
411 page.assert_no_selector 'li.disabled', text: 'Move selected'
412 page.assert_selector 'li', text: 'Move selected'
413 page.assert_no_selector 'li.disabled', text: 'Remove selected'
414 page.assert_selector 'li', text: 'Remove selected'
417 # select subproject and a collection and verify that copy action is still disabled
418 visit page_with_token 'active', '/'
419 find("#projects-menu").click
420 find(".dropdown-menu a", text: my_project['name']).click
422 click_link 'Subprojects'
423 assert page.has_text?(my_subproject['name']), 'Subproject not found in project'
425 within('tr', text: my_subproject['name']) do
426 find('input[type=checkbox]').click
429 click_link 'Data collections'
430 assert page.has_text?(my_collection['name']), 'Collection not found in project'
432 within('tr', text: my_collection['name']) do
433 find('input[type=checkbox]').click
436 click_button 'Selection'
437 within('.selection-action-container') do
438 page.assert_selector 'li.disabled', text: 'Create new collection with selected collections'
439 page.assert_selector 'li.disabled', text: 'Compare selected'
440 page.assert_selector 'li.disabled', text: 'Copy selected'
441 page.assert_no_selector 'li.disabled', text: 'Move selected'
442 page.assert_selector 'li', text: 'Move selected'
443 page.assert_no_selector 'li.disabled', text: 'Remove selected'
444 page.assert_selector 'li', text: 'Remove selected'
448 # "Remove selected" selection option should not be available when current user cannot write to the project
449 test "remove selected action is not available when current user cannot write to project" do
450 my_project = api_fixture('groups')['anonymously_accessible_project']
451 visit page_with_token 'active', "/projects/#{my_project['uuid']}"
453 click_button 'Selection'
454 within('.selection-action-container') do
455 assert_selector 'li', text: 'Create new collection with selected collections'
456 assert_selector 'li', text: 'Compare selected'
457 assert_selector 'li', text: 'Copy selected'
458 assert_selector 'li', text: 'Move selected'
459 assert_no_selector 'li', text: 'Remove selected'
465 ['project_viewer', false],
466 ].each do |user, expect_collection_in_aproject|
467 test "combine selected collections into new collection #{user} #{expect_collection_in_aproject}" do
468 my_project = api_fixture('groups')['aproject']
469 my_collection = api_fixture('collections')['collection_to_move_around_in_aproject']
471 visit page_with_token user, '/'
472 find("#projects-menu").click
473 find(".dropdown-menu a", text: my_project['name']).click
474 assert page.has_text?(my_collection['name']), 'Collection not found in project'
476 within('tr', text: my_collection['name']) do
477 find('input[type=checkbox]').click
480 click_button 'Selection'
481 within('.selection-action-container') do
482 click_link 'Create new collection with selected collections'
485 # now in the new collection page
486 if expect_collection_in_aproject
487 assert page.has_text?("Created new collection in the project #{my_project['name']}"),
488 'Not found flash message that new collection is created in aproject'
490 assert page.has_text?("Created new collection in your Home project"),
491 'Not found flash message that new collection is created in Home project'
493 assert page.has_text?('Content hash'), 'Not found content hash in collection page'
499 ["pipelines", "/pipeline_instances"],
500 ["collections", "/collections"]
501 ].each do |target,path|
502 test "Test dashboard button all #{target}" do
503 visit page_with_token 'active', '/'
504 click_link "All #{target}"
505 assert_equal path, current_path
509 def scroll_setup(project_name,
513 sort_parameters = nil)
514 project_uuid = api_fixture('groups')[project_name]['uuid']
515 visit page_with_token 'user1_with_load', '/projects/' + project_uuid
517 assert(page.has_text?("#{item_list_parameter.humanize} (#{total_nbr_items})"), "Number of #{item_list_parameter.humanize} did not match the input amount")
519 click_link item_list_parameter.humanize
523 find("th[data-sort-order='#{sort_parameters.gsub(/\s/,'')}']").click
528 def scroll_items_check(nbr_items,
534 for i in 1..nbr_items
535 items << "#{fixture_prefix}#{i}"
538 verify_items = items.dup
539 unexpected_items = []
541 within(".arv-project-#{item_list_parameter}") do
542 page.execute_script "window.scrollBy(0,999000)"
548 # Visit all rows. If not all expected items are found, retry
549 found_items = page.all(item_selector)
550 item_count = found_items.count
553 (0..item_count-1).each do |i|
554 # Found row text using the fixture string e.g. "Show Collection_#{n} "
555 item_name = found_items[i].text.split[1]
556 if !items.include? item_name
557 unexpected_items << item_name
559 verify_items.delete item_name
563 assert_operator( previous.downcase, :<=, item_name.downcase) if previous
568 assert_equal true, unexpected_items.empty?, "Found unexpected #{item_list_parameter.humanize} #{unexpected_items.inspect}"
569 assert_equal nbr_items, item_count, "Found different number of #{item_list_parameter.humanize}"
570 assert_equal true, verify_items.empty?, "Did not find all the #{item_list_parameter.humanize}"
575 ['project_with_10_collections', 10],
576 ['project_with_201_collections', 201], # two pages of data
577 ].each do |project_name, nbr_items|
578 test "scroll collections tab for #{project_name} with #{nbr_items} objects" do
579 item_list_parameter = "Data_collections"
580 scroll_setup project_name,
583 scroll_items_check nbr_items,
586 'tr[data-kind="arvados#collection"]'
591 ['project_with_10_collections', 10],
592 ['project_with_201_collections', 201], # two pages of data
593 ].each do |project_name, nbr_items|
594 test "scroll collections tab for #{project_name} with #{nbr_items} objects with ascending sort (case insensitive)" do
595 item_list_parameter = "Data_collections"
596 scroll_setup project_name,
601 scroll_items_check nbr_items,
604 'tr[data-kind="arvados#collection"]',
610 ['project_with_10_pipelines', 10, 0],
611 ['project_with_2_pipelines_and_60_jobs', 2, 60],
612 ['project_with_25_pipelines', 25, 0],
613 ].each do |project_name, num_pipelines, num_jobs|
614 test "scroll pipeline instances tab for #{project_name} with #{num_pipelines} pipelines and #{num_jobs} jobs" do
615 item_list_parameter = "Jobs_and_pipelines"
616 scroll_setup project_name,
617 num_pipelines + num_jobs,
619 # check the general scrolling and the pipelines
620 scroll_items_check num_pipelines,
623 'tr[data-kind="arvados#pipelineInstance"]'
624 # Check job count separately
625 jobs_found = page.all('tr[data-kind="arvados#job"]')
626 found_job_count = jobs_found.count
627 assert_equal num_jobs, found_job_count, 'Did not find expected number of jobs'
631 # Move button accessibility
634 ['active', true], # project owner
635 ['project_viewer', false],
636 ].each do |user, can_move|
637 test "#{user} can move subproject under another user's Home #{can_move}" do
638 project = api_fixture('groups')['aproject']
639 collection = api_fixture('collections')['collection_to_move_around_in_aproject']
641 # verify the project move button
642 visit page_with_token user, "/projects/#{project['uuid']}"
644 assert page.has_link? 'Move project...'
646 assert page.has_no_link? 'Move project...'
651 test "error while loading tab" do
652 original_arvados_v1_base = Rails.configuration.arvados_v1_base
654 visit page_with_token 'active', '/projects/' + api_fixture('groups')['aproject']['uuid']
656 # Point to a bad api server url to generate error
657 Rails.configuration.arvados_v1_base = "https://[100::f]:1/"
658 click_link 'Other objects'
659 within '#Other_objects' do
661 assert_selector('a', text: 'Reload tab')
663 # Now point back to the orig api server and reload tab
664 Rails.configuration.arvados_v1_base = original_arvados_v1_base
665 click_link 'Reload tab'
666 assert_no_selector('a', text: 'Reload tab')
667 assert_selector('button', text: 'Selection')
668 within '.selection-action-container' do
669 assert_selector 'tr[data-kind="arvados#trait"]'