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(has_no_text?('.container-fluid', text: '*Textile description for A project*'),
58 "Description is not rendered properly")
59 assert(find?('.container-fluid', text: 'Textile description for A project'),
60 "Description update did not survive page refresh")
61 assert(find?('.container-fluid', text: 'And a new paragraph in description'),
62 "Description did not contain the expected new paragraph")
63 assert(page.has_link?("take me home"), "link not found in description")
65 click_link 'take me home'
68 assert(page.has_text?('Active pipelines'), 'Active pipelines - not found on dashboard')
71 test 'Find a project and edit description to html description' do
72 visit page_with_token 'active', '/'
73 find("#projects-menu").click
74 find(".dropdown-menu a", text: "A Project").click
75 within('.container-fluid', text: api_fixture('groups')['aproject']['name']) do
76 find('span', text: api_fixture('groups')['aproject']['name']).click
77 within('.arv-description-as-subtitle') do
78 find('.fa-pencil').click
79 find('.editable-input textarea').set('<br>Textile description for A project</br> - <a href="/">take me home</a>')
80 find('.editable-submit').click
85 assert(find?('.container-fluid', text: 'Textile description for A project'),
86 "Description update did not survive page refresh")
87 assert(!find?('.container-fluid', text: '<br>Textile description for A project</br>'),
88 "Textile description is displayed with uninterpreted formatting characters")
89 assert(page.has_link?("take me home"),"link not found in description")
90 click_link 'take me home'
91 assert page.has_text?('Active pipelines')
94 test 'Find a project and edit description to textile description with link to object' do
95 visit page_with_token 'active', '/'
96 find("#projects-menu").click
97 find(".dropdown-menu a", text: "A Project").click
98 within('.container-fluid', text: api_fixture('groups')['aproject']['name']) do
99 find('span', text: api_fixture('groups')['aproject']['name']).click
100 within('.arv-description-as-subtitle') do
101 find('.fa-pencil').click
102 find('.editable-input textarea').set('*Textile description for A project* - "go to sub-project":' + api_fixture('groups')['asubproject']['uuid'] + "'")
103 find('.editable-submit').click
108 assert(find?('.container-fluid', text: 'Textile description for A project'),
109 "Description update did not survive page refresh")
110 assert(!find?('.container-fluid', text: '*Textile description for A project*'),
111 "Textile description is displayed with uninterpreted formatting characters")
112 assert(page.has_link?("go to sub-project"), "link not found in description")
113 click_link 'go to sub-project'
114 assert(page.has_text?(api_fixture('groups')['asubproject']['name']), 'sub-project name not found after clicking link')
117 test 'Add a new name, then edit it, without creating a duplicate' do
118 project_uuid = api_fixture('groups')['aproject']['uuid']
119 specimen_uuid = api_fixture('traits')['owned_by_aproject_with_no_name']['uuid']
120 visit page_with_token 'active', '/projects/' + project_uuid
121 click_link 'Other objects'
122 within '.selection-action-container' do
123 # Wait for the tab to load:
124 assert_selector 'tr[data-kind="arvados#trait"]'
125 within first('tr', text: 'Trait') do
126 find(".fa-pencil").click
127 find('.editable-input input').set('Now I have a name.')
128 find('.glyphicon-ok').click
129 assert_selector '.editable', text: 'Now I have a name.'
130 find(".fa-pencil").click
131 find('.editable-input input').set('Now I have a new name.')
132 find('.glyphicon-ok').click
135 assert_selector '.editable', text: 'Now I have a new name.'
138 click_link 'Other objects'
139 within '.selection-action-container' do
140 find '.editable', text: 'Now I have a new name.'
141 page.assert_no_selector '.editable', text: 'Now I have a name.'
145 test 'Create a project and move it into a different project' do
146 visit page_with_token 'active', '/projects'
147 find("#projects-menu").click
148 find(".dropdown-menu a", text: "Home").click
149 find('.btn', text: "Add a subproject").click
151 # within('.editable', text: 'New project') do
153 find('.fa-pencil').click
154 find('.editable-input input').set('Project 1234')
155 find('.glyphicon-ok').click
160 find("#projects-menu").click
161 find(".dropdown-menu a", text: "Home").click
162 find('.btn', text: "Add a subproject").click
164 find('.fa-pencil').click
165 find('.editable-input input').set('Project 5678')
166 find('.glyphicon-ok').click
170 click_link 'Move project...'
171 find('.selectable', text: 'Project 1234').click
172 find('.modal-footer a,button', text: 'Move').click
175 # Wait for the page to refresh and show the new parent in Sharing panel
177 assert(page.has_link?("Project 1234"),
178 "Project 5678 should now be inside project 1234")
181 def show_project_using(auth_key, proj_key='aproject')
182 project_uuid = api_fixture('groups')[proj_key]['uuid']
183 visit(page_with_token(auth_key, "/projects/#{project_uuid}"))
184 assert(page.has_text?("A Project"), "not on expected project page")
188 find('#project_sharing').all('tr')
191 def add_share_and_check(share_type, name, obj=nil)
192 assert(page.has_no_text?(name), "project is already shared with #{name}")
193 start_share_count = share_rows.size
194 click_on("Share with #{share_type}")
195 within(".modal-container") do
196 # Order is important here: we should find something that appears in the
197 # modal before we make any assertions about what's not in the modal.
198 # Otherwise, the not-included assertions might falsely pass because
199 # the modal hasn't loaded yet.
200 find(".selectable", text: name).click
201 assert(has_no_selector?(".modal-dialog-preview-pane"),
202 "preview pane available in sharing dialog")
203 if share_type == 'users' and obj and obj['email']
204 assert(page.has_text?(obj['email']), "Did not find user's email")
206 assert_raises(Capybara::ElementNotFound,
207 "Projects pulldown available from sharing dialog") do
208 click_on "All projects"
212 using_wait_time(Capybara.default_wait_time * 3) do
213 assert(page.has_link?(name),
214 "new share was not added to sharing table")
215 assert_equal(start_share_count + 1, share_rows.size,
216 "new share did not add row to sharing table")
220 def modify_share_and_check(name)
221 start_rows = share_rows
222 link_row = start_rows.select { |row| row.has_text?(name) }
223 assert_equal(1, link_row.size, "row with new permission not found")
224 within(link_row.first) do
226 select("Write", from: "share_change_level")
227 click_on("editable-submit")
228 assert(has_link?("Write"),
229 "failed to change access level on new share")
231 page.driver.browser.switch_to.alert.accept
234 using_wait_time(Capybara.default_wait_time * 3) do
235 assert(page.has_no_text?(name),
236 "new share row still exists after being revoked")
237 assert_equal(start_rows.size - 1, share_rows.size,
238 "revoking share did not remove row from sharing table")
242 test "project viewer can't see project sharing tab" do
243 show_project_using("project_viewer")
244 assert(page.has_no_link?("Sharing"),
245 "read-only project user sees sharing tab")
248 test "project owner can manage sharing for another user" do
249 add_user = api_fixture('users')['future_project_user']
250 new_name = ["first_name", "last_name"].map { |k| add_user[k] }.join(" ")
252 show_project_using("active")
254 add_share_and_check("users", new_name, add_user)
255 modify_share_and_check(new_name)
258 test "project owner can manage sharing for another group" do
259 new_name = api_fixture('groups')['future_project_viewing_group']['name']
261 show_project_using("active")
263 add_share_and_check("groups", new_name)
264 modify_share_and_check(new_name)
267 test "'share with group' listing does not offer projects" do
268 show_project_using("active")
270 click_on "Share with groups"
271 good_uuid = api_fixture("groups")["private"]["uuid"]
272 assert(page.has_selector?(".selectable[data-object-uuid=\"#{good_uuid}\"]"),
273 "'share with groups' listing missing owned user group")
274 bad_uuid = api_fixture("groups")["asubproject"]["uuid"]
275 assert(page.has_no_selector?(".selectable[data-object-uuid=\"#{bad_uuid}\"]"),
276 "'share with groups' listing includes project")
280 ['Move',api_fixture('collections')['collection_to_move_around_in_aproject'],
281 api_fixture('groups')['aproject'],api_fixture('groups')['asubproject']],
282 ['Remove',api_fixture('collections')['collection_to_move_around_in_aproject'],
283 api_fixture('groups')['aproject']],
284 ['Copy',api_fixture('collections')['collection_to_move_around_in_aproject'],
285 api_fixture('groups')['aproject'],api_fixture('groups')['asubproject']],
286 ['Remove',api_fixture('collections')['collection_in_aproject_with_same_name_as_in_home_project'],
287 api_fixture('groups')['aproject'],nil,true],
288 ].each do |action, my_collection, src, dest=nil, expect_name_change=nil|
289 test "selection #{action} #{expect_name_change} for project" do
290 perform_selection_action src, dest, my_collection, action
294 assert page.has_text?(my_collection['name']), 'Collection not found in src project after copy'
295 visit page_with_token 'active', '/'
296 find("#projects-menu").click
297 find(".dropdown-menu a", text: dest['name']).click
298 assert page.has_text?(my_collection['name']), 'Collection not found in dest project after copy'
300 # now remove it from destination project to restore to original state
301 perform_selection_action dest, nil, my_collection, 'Remove'
303 assert page.has_no_text?(my_collection['name']), 'Collection still found in src project after move'
304 visit page_with_token 'active', '/'
305 find("#projects-menu").click
306 find(".dropdown-menu a", text: dest['name']).click
307 assert page.has_text?(my_collection['name']), 'Collection not found in dest project after move'
309 # move it back to src project to restore to original state
310 perform_selection_action dest, src, my_collection, action
312 assert page.has_no_text?(my_collection['name']), 'Collection still found in src project after remove'
313 visit page_with_token 'active', '/'
314 find("#projects-menu").click
315 find(".dropdown-menu a", text: "Home").click
316 assert page.has_text?(my_collection['name']), 'Collection not found in home project after remove'
317 if expect_name_change
318 assert page.has_text?(my_collection['name']+' removed from ' + src['name']),
319 'Collection with update name is not found in home project after remove'
325 def perform_selection_action src, dest, item, action
326 visit page_with_token 'active', '/'
327 find("#projects-menu").click
328 find(".dropdown-menu a", text: src['name']).click
329 assert page.has_text?(item['name']), 'Collection not found in src project'
331 within('tr', text: item['name']) do
332 find('input[type=checkbox]').click
335 click_button 'Selection...'
337 within('.selection-action-container') do
338 assert page.has_text?("Compare selected"), "Compare selected link text not found"
339 assert page.has_link?("Copy selected"), "Copy selected link not found"
340 assert page.has_link?("Move selected"), "Move selected link not found"
341 assert page.has_link?("Remove selected"), "Remove selected link not found"
343 click_link "#{action} selected"
346 # select the destination project if a Copy or Move action is being performed
347 if action == 'Copy' || action == 'Move'
348 within(".modal-container") do
349 find('.selectable', text: dest['name']).click
350 find('.modal-footer a,button', text: action).click
356 # Test copy action state. It should not be available when a subproject is selected.
357 test "copy action is disabled when a subproject is selected" do
358 my_project = api_fixture('groups')['aproject']
359 my_collection = api_fixture('collections')['collection_to_move_around_in_aproject']
360 my_subproject = api_fixture('groups')['asubproject']
362 # verify that selection options are disabled on the project until an item is selected
363 visit page_with_token 'active', '/'
364 find("#projects-menu").click
365 find(".dropdown-menu a", text: my_project['name']).click
367 click_button 'Selection...'
368 within('.selection-action-container') do
369 page.assert_selector 'li.disabled', text: 'Create new collection with selected collections'
370 page.assert_selector 'li.disabled', text: 'Compare selected'
371 page.assert_selector 'li.disabled', text: 'Copy selected'
372 page.assert_selector 'li.disabled', text: 'Move selected'
373 page.assert_selector 'li.disabled', text: 'Remove selected'
376 # select collection and verify links are enabled
377 visit page_with_token 'active', '/'
378 find("#projects-menu").click
379 find(".dropdown-menu a", text: my_project['name']).click
380 assert page.has_text?(my_collection['name']), 'Collection not found in project'
382 within('tr', text: my_collection['name']) do
383 find('input[type=checkbox]').click
386 click_button 'Selection...'
387 within('.selection-action-container') do
388 page.assert_no_selector 'li.disabled', text: 'Create new collection with selected collections'
389 page.assert_selector 'li', text: 'Create new collection with selected collections'
390 page.assert_selector 'li.disabled', text: 'Compare selected'
391 page.assert_no_selector 'li.disabled', text: 'Copy selected'
392 page.assert_selector 'li', text: 'Copy selected'
393 page.assert_no_selector 'li.disabled', text: 'Move selected'
394 page.assert_selector 'li', text: 'Move selected'
395 page.assert_no_selector 'li.disabled', text: 'Remove selected'
396 page.assert_selector 'li', text: 'Remove selected'
399 # select subproject and verify that copy action is disabled
400 visit page_with_token 'active', '/'
401 find("#projects-menu").click
402 find(".dropdown-menu a", text: my_project['name']).click
404 click_link 'Subprojects'
405 assert page.has_text?(my_subproject['name']), 'Subproject not found in project'
407 within('tr', text: my_subproject['name']) do
408 find('input[type=checkbox]').click
411 click_button 'Selection...'
412 within('.selection-action-container') do
413 page.assert_selector 'li.disabled', text: 'Create new collection with selected collections'
414 page.assert_selector 'li.disabled', text: 'Compare selected'
415 page.assert_selector 'li.disabled', text: 'Copy selected'
416 page.assert_no_selector 'li.disabled', text: 'Move selected'
417 page.assert_selector 'li', text: 'Move selected'
418 page.assert_no_selector 'li.disabled', text: 'Remove selected'
419 page.assert_selector 'li', text: 'Remove selected'
422 # select subproject and a collection and verify that copy action is still disabled
423 visit page_with_token 'active', '/'
424 find("#projects-menu").click
425 find(".dropdown-menu a", text: my_project['name']).click
427 click_link 'Subprojects'
428 assert page.has_text?(my_subproject['name']), 'Subproject not found in project'
430 within('tr', text: my_subproject['name']) do
431 find('input[type=checkbox]').click
434 click_link 'Data collections'
435 assert page.has_text?(my_collection['name']), 'Collection not found in project'
437 within('tr', text: my_collection['name']) do
438 find('input[type=checkbox]').click
441 click_button 'Selection...'
442 within('.selection-action-container') do
443 page.assert_selector 'li.disabled', text: 'Create new collection with selected collections'
444 page.assert_selector 'li.disabled', text: 'Compare selected'
445 page.assert_selector 'li.disabled', text: 'Copy selected'
446 page.assert_no_selector 'li.disabled', text: 'Move selected'
447 page.assert_selector 'li', text: 'Move selected'
448 page.assert_no_selector 'li.disabled', text: 'Remove selected'
449 page.assert_selector 'li', text: 'Remove selected'
455 ['project_viewer', false],
456 ].each do |user, expect_collection_in_aproject|
457 test "combine selected collections into new collection #{user} #{expect_collection_in_aproject}" do
458 my_project = api_fixture('groups')['aproject']
459 my_collection = api_fixture('collections')['collection_to_move_around_in_aproject']
461 visit page_with_token user, '/'
462 find("#projects-menu").click
463 find(".dropdown-menu a", text: my_project['name']).click
464 assert page.has_text?(my_collection['name']), 'Collection not found in project'
466 within('tr', text: my_collection['name']) do
467 find('input[type=checkbox]').click
470 click_button 'Selection...'
471 within('.selection-action-container') do
472 click_link 'Create new collection with selected collections'
475 # now in the new collection page
476 if expect_collection_in_aproject
477 assert page.has_text?("Created new collection in the project #{my_project['name']}"),
478 'Not found flash message that new collection is created in aproject'
480 assert page.has_text?("Created new collection in your Home project"),
481 'Not found flash message that new collection is created in Home project'
483 assert page.has_text?('Content hash'), 'Not found content hash in collection page'
489 ["pipelines", "/pipeline_instances"],
490 ["collections", "/collections"]
491 ].each do |target,path|
492 test "Test dashboard button all #{target}" do
493 visit page_with_token 'active', '/'
494 click_link "All #{target}"
495 assert_equal path, current_path
499 def scroll_setup(project_name,
503 sort_parameters = nil)
504 project_uuid = api_fixture('groups')[project_name]['uuid']
505 visit page_with_token 'user1_with_load', '/projects/' + project_uuid
507 assert(page.has_text?("#{item_list_parameter.humanize} (#{total_nbr_items})"), "Number of #{item_list_parameter.humanize} did not match the input amount")
509 click_link item_list_parameter.humanize
513 find("th[data-sort-order='#{sort_parameters.gsub(/\s/,'')}']").click
518 def scroll_items_check(nbr_items,
524 for i in 1..nbr_items
525 items << "#{fixture_prefix}#{i}"
528 verify_items = items.dup
529 unexpected_items = []
531 within(".arv-project-#{item_list_parameter}") do
532 page.execute_script "window.scrollBy(0,999000)"
538 # Visit all rows. If not all expected items are found, retry
539 found_items = page.all(item_selector)
540 item_count = found_items.count
543 (0..item_count-1).each do |i|
544 # Found row text using the fixture string e.g. "Show Collection_#{n} "
545 item_name = found_items[i].text.split[1]
546 if !items.include? item_name
547 unexpected_items << item_name
549 verify_items.delete item_name
553 assert_operator( previous.downcase, :<=, item_name.downcase) if previous
558 assert_equal true, unexpected_items.empty?, "Found unexpected #{item_list_parameter.humanize} #{unexpected_items.inspect}"
559 assert_equal nbr_items, item_count, "Found different number of #{item_list_parameter.humanize}"
560 assert_equal true, verify_items.empty?, "Did not find all the #{item_list_parameter.humanize}"
565 ['project_with_10_collections', 10],
566 ['project_with_201_collections', 201], # two pages of data
567 ].each do |project_name, nbr_items|
568 test "scroll collections tab for #{project_name} with #{nbr_items} objects" do
569 item_list_parameter = "Data_collections"
570 scroll_setup project_name,
573 scroll_items_check nbr_items,
576 'tr[data-kind="arvados#collection"]'
581 ['project_with_10_collections', 10],
582 ['project_with_201_collections', 201], # two pages of data
583 ].each do |project_name, nbr_items|
584 test "scroll collections tab for #{project_name} with #{nbr_items} objects with ascending sort (case insensitive)" do
585 item_list_parameter = "Data_collections"
586 scroll_setup project_name,
591 scroll_items_check nbr_items,
594 'tr[data-kind="arvados#collection"]',
600 ['project_with_10_pipelines', 10, 0],
601 ['project_with_2_pipelines_and_60_jobs', 2, 60],
602 ['project_with_25_pipelines', 25, 0],
603 ].each do |project_name, num_pipelines, num_jobs|
604 test "scroll pipeline instances tab for #{project_name} with #{num_pipelines} pipelines and #{num_jobs} jobs" do
605 item_list_parameter = "Jobs_and_pipelines"
606 scroll_setup project_name,
607 num_pipelines + num_jobs,
609 # check the general scrolling and the pipelines
610 scroll_items_check num_pipelines,
613 'tr[data-kind="arvados#pipelineInstance"]'
614 # Check job count separately
615 jobs_found = page.all('tr[data-kind="arvados#job"]')
616 found_job_count = jobs_found.count
617 assert_equal num_jobs, found_job_count, 'Did not find expected number of jobs'
621 # Move button accessibility
624 ['active', true], # project owner
625 ['project_viewer', false],
626 ].each do |user, can_move|
627 test "#{user} can move subproject under another user's Home #{can_move}" do
628 project = api_fixture('groups')['aproject']
629 collection = api_fixture('collections')['collection_to_move_around_in_aproject']
631 # verify the project move button
632 visit page_with_token user, "/projects/#{project['uuid']}"
634 assert page.has_link? 'Move project...'
636 assert page.has_no_link? 'Move project...'
641 test "error while loading tab" do
642 original_arvados_v1_base = Rails.configuration.arvados_v1_base
644 visit page_with_token 'active', '/projects/' + api_fixture('groups')['aproject']['uuid']
646 # Point to a bad api server url to generate error
647 Rails.configuration.arvados_v1_base = "https://[100::f]:1/"
648 click_link 'Other objects'
649 within '#Other_objects' do
651 assert_selector('a', text: 'Reload tab')
653 # Now point back to the orig api server and reload tab
654 Rails.configuration.arvados_v1_base = original_arvados_v1_base
655 click_link 'Reload tab'
656 assert_no_selector('a', text: 'Reload tab')
657 assert_selector('button', text: 'Selection...')
658 within '.selection-action-container' do
659 assert_selector 'tr[data-kind="arvados#trait"]'