5493: add test for the getting_started help menu item.
[arvados.git] / apps / workbench / test / integration / projects_test.rb
1 require 'integration_helper'
2 require 'helpers/share_object_helper'
3
4 class ProjectsTest < ActionDispatch::IntegrationTest
5   include ShareObjectHelper
6
7   setup do
8     need_javascript
9   end
10
11   test 'Check collection count for A Project in the tab pane titles' do
12     project_uuid = api_fixture('groups')['aproject']['uuid']
13     visit page_with_token 'active', '/projects/' + project_uuid
14     click_link 'Data collections'
15     wait_for_ajax
16     collection_count = page.all("[data-pk*='collection']").count
17     assert_selector '#Data_collections-tab span', text: "(#{collection_count})"
18   end
19
20   test 'Find a project and edit its description' do
21     visit page_with_token 'active', '/'
22     find("#projects-menu").click
23     find(".dropdown-menu a", text: "A Project").click
24     within('.container-fluid', text: api_fixture('groups')['aproject']['name']) do
25       find('span', text: api_fixture('groups')['aproject']['name']).click
26       within('.arv-description-as-subtitle') do
27         find('.fa-pencil').click
28         find('.editable-input textarea').set('I just edited this.')
29         find('.editable-submit').click
30       end
31       wait_for_ajax
32     end
33     visit current_path
34     assert(find?('.container-fluid', text: 'I just edited this.'),
35            "Description update did not survive page refresh")
36   end
37
38   test 'Find a project and edit description to textile description' do
39     visit page_with_token 'active', '/'
40     find("#projects-menu").click
41     find(".dropdown-menu a", text: "A Project").click
42     within('.container-fluid', text: api_fixture('groups')['aproject']['name']) do
43       find('span', text: api_fixture('groups')['aproject']['name']).click
44       within('.arv-description-as-subtitle') do
45         find('.fa-pencil').click
46         find('.editable-input textarea').set('<p>*Textile description for A project* - "take me home":/ </p><p>And a new paragraph in description.</p>')
47         find('.editable-submit').click
48       end
49       wait_for_ajax
50     end
51
52     # visit project page
53     visit current_path
54     assert_no_text '*Textile description for A project*'
55     assert(find?('.container-fluid', text: 'Textile description for A project'),
56            "Description update did not survive page refresh")
57     assert(find?('.container-fluid', text: 'And a new paragraph in description'),
58            "Description did not contain the expected new paragraph")
59     assert(page.has_link?("take me home"), "link not found in description")
60
61     click_link 'take me home'
62
63     # now in dashboard
64     assert(page.has_text?('Active pipelines'), 'Active pipelines - not found on dashboard')
65   end
66
67   test 'Find a project and edit description to html description' do
68     visit page_with_token 'active', '/'
69     find("#projects-menu").click
70     find(".dropdown-menu a", text: "A Project").click
71     within('.container-fluid', text: api_fixture('groups')['aproject']['name']) do
72       find('span', text: api_fixture('groups')['aproject']['name']).click
73       within('.arv-description-as-subtitle') do
74         find('.fa-pencil').click
75         find('.editable-input textarea').set('<br>Textile description for A project</br> - <a href="/">take me home</a>')
76         find('.editable-submit').click
77       end
78       wait_for_ajax
79     end
80     visit current_path
81     assert(find?('.container-fluid', text: 'Textile description for A project'),
82            "Description update did not survive page refresh")
83     assert(!find?('.container-fluid', text: '<br>Textile description for A project</br>'),
84            "Textile description is displayed with uninterpreted formatting characters")
85     assert(page.has_link?("take me home"),"link not found in description")
86     click_link 'take me home'
87     assert page.has_text?('Active pipelines')
88   end
89
90   test 'Find a project and edit description to textile description with link to object' do
91     visit page_with_token 'active', '/'
92     find("#projects-menu").click
93     find(".dropdown-menu a", text: "A Project").click
94     within('.container-fluid', text: api_fixture('groups')['aproject']['name']) do
95       find('span', text: api_fixture('groups')['aproject']['name']).click
96       within('.arv-description-as-subtitle') do
97         find('.fa-pencil').click
98         find('.editable-input textarea').set('*Textile description for A project* - "go to sub-project":' + api_fixture('groups')['asubproject']['uuid'] + "'")
99         find('.editable-submit').click
100       end
101       wait_for_ajax
102     end
103     visit current_path
104     assert(find?('.container-fluid', text: 'Textile description for A project'),
105            "Description update did not survive page refresh")
106     assert(!find?('.container-fluid', text: '*Textile description for A project*'),
107            "Textile description is displayed with uninterpreted formatting characters")
108     assert(page.has_link?("go to sub-project"), "link not found in description")
109     click_link 'go to sub-project'
110     assert(page.has_text?(api_fixture('groups')['asubproject']['name']), 'sub-project name not found after clicking link')
111   end
112
113   test 'Add a new name, then edit it, without creating a duplicate' do
114     project_uuid = api_fixture('groups')['aproject']['uuid']
115     specimen_uuid = api_fixture('traits')['owned_by_aproject_with_no_name']['uuid']
116     visit page_with_token 'active', '/projects/' + project_uuid
117     click_link 'Other objects'
118     within '.selection-action-container' do
119       # Wait for the tab to load:
120       assert_selector 'tr[data-kind="arvados#trait"]'
121       within first('tr', text: 'Trait') do
122         find(".fa-pencil").click
123         find('.editable-input input').set('Now I have a name.')
124         find('.glyphicon-ok').click
125         assert_selector '.editable', text: 'Now I have a name.'
126         find(".fa-pencil").click
127         find('.editable-input input').set('Now I have a new name.')
128         find('.glyphicon-ok').click
129       end
130       wait_for_ajax
131       assert_selector '.editable', text: 'Now I have a new name.'
132     end
133     visit current_path
134     click_link 'Other objects'
135     within '.selection-action-container' do
136       find '.editable', text: 'Now I have a new name.'
137       assert_no_selector '.editable', text: 'Now I have a name.'
138     end
139   end
140
141   test 'Create a project and move it into a different project' do
142     visit page_with_token 'active', '/projects'
143     find("#projects-menu").click
144     find(".dropdown-menu a", text: "Home").click
145     find('.btn', text: "Add a subproject").click
146
147     within('h2') do
148       find('.fa-pencil').click
149       find('.editable-input input').set('Project 1234')
150       find('.glyphicon-ok').click
151     end
152     wait_for_ajax
153
154     visit '/projects'
155     find("#projects-menu").click
156     find(".dropdown-menu a", text: "Home").click
157     find('.btn', text: "Add a subproject").click
158     within('h2') do
159       find('.fa-pencil').click
160       find('.editable-input input').set('Project 5678')
161       find('.glyphicon-ok').click
162     end
163     wait_for_ajax
164
165     click_link 'Move project...'
166     find('.selectable', text: 'Project 1234').click
167     find('.modal-footer a,button', text: 'Move').click
168     wait_for_ajax
169
170     # Wait for the page to refresh and show the new parent in Sharing panel
171     click_link 'Sharing'
172     assert(page.has_link?("Project 1234"),
173            "Project 5678 should now be inside project 1234")
174   end
175
176   def open_groups_sharing(project_name="aproject", token_name="active")
177     project = api_fixture("groups", project_name)
178     visit(page_with_token(token_name, "/projects/#{project['uuid']}"))
179     click_on "Sharing"
180     click_on "Share with groups"
181   end
182
183   def group_name(group_key)
184     api_fixture("groups", group_key, "name")
185   end
186
187   test "projects not publicly sharable when anonymous browsing disabled" do
188     Rails.configuration.anonymous_user_token = false
189     open_groups_sharing
190     # Check for a group we do expect first, to make sure the modal's loaded.
191     assert_selector(".modal-container .selectable",
192                     text: group_name("all_users"))
193     assert_no_selector(".modal-container .selectable",
194                        text: group_name("anonymous_group"))
195   end
196
197   test "projects publicly sharable when anonymous browsing enabled" do
198     Rails.configuration.anonymous_user_token = "testonlytoken"
199     open_groups_sharing
200     assert_selector(".modal-container .selectable",
201                     text: group_name("anonymous_group"))
202   end
203
204   test "project viewer can't see project sharing tab" do
205     show_object_using('project_viewer', 'groups', 'aproject', 'A Project')
206     assert(page.has_no_link?("Sharing"),
207            "read-only project user sees sharing tab")
208   end
209
210   test "project owner can manage sharing for another user" do
211     add_user = api_fixture('users')['future_project_user']
212     new_name = ["first_name", "last_name"].map { |k| add_user[k] }.join(" ")
213
214     show_object_using('active', 'groups', 'aproject', 'A Project')
215     click_on "Sharing"
216     add_share_and_check("users", new_name, add_user)
217     modify_share_and_check(new_name)
218   end
219
220   test "project owner can manage sharing for another group" do
221     new_name = api_fixture('groups')['future_project_viewing_group']['name']
222
223     show_object_using('active', 'groups', 'aproject', 'A Project')
224     click_on "Sharing"
225     add_share_and_check("groups", new_name)
226     modify_share_and_check(new_name)
227   end
228
229   test "'share with group' listing does not offer projects" do
230     show_object_using('active', 'groups', 'aproject', 'A Project')
231     click_on "Sharing"
232     click_on "Share with groups"
233     good_uuid = api_fixture("groups")["private"]["uuid"]
234     assert(page.has_selector?(".selectable[data-object-uuid=\"#{good_uuid}\"]"),
235            "'share with groups' listing missing owned user group")
236     bad_uuid = api_fixture("groups")["asubproject"]["uuid"]
237     assert(page.has_no_selector?(".selectable[data-object-uuid=\"#{bad_uuid}\"]"),
238            "'share with groups' listing includes project")
239   end
240
241   [
242     ['Move',api_fixture('collections')['collection_to_move_around_in_aproject'],
243       api_fixture('groups')['aproject'],api_fixture('groups')['asubproject']],
244     ['Remove',api_fixture('collections')['collection_to_move_around_in_aproject'],
245       api_fixture('groups')['aproject']],
246     ['Copy',api_fixture('collections')['collection_to_move_around_in_aproject'],
247       api_fixture('groups')['aproject'],api_fixture('groups')['asubproject']],
248     ['Remove',api_fixture('collections')['collection_in_aproject_with_same_name_as_in_home_project'],
249       api_fixture('groups')['aproject'],nil,true],
250   ].each do |action, my_collection, src, dest=nil, expect_name_change=nil|
251     test "selection #{action} -> #{expect_name_change.inspect} for project" do
252       perform_selection_action src, dest, my_collection, action
253
254       case action
255       when 'Copy'
256         assert page.has_text?(my_collection['name']), 'Collection not found in src project after copy'
257         visit page_with_token 'active', '/'
258         find("#projects-menu").click
259         find(".dropdown-menu a", text: dest['name']).click
260         click_link 'Data collections'
261         assert page.has_text?(my_collection['name']), 'Collection not found in dest project after copy'
262
263       when 'Move'
264         assert page.has_no_text?(my_collection['name']), 'Collection still found in src project after move'
265         visit page_with_token 'active', '/'
266         find("#projects-menu").click
267         find(".dropdown-menu a", text: dest['name']).click
268         click_link 'Data collections'
269         assert page.has_text?(my_collection['name']), 'Collection not found in dest project after move'
270
271       when 'Remove'
272         assert page.has_no_text?(my_collection['name']), 'Collection still found in src project after remove'
273       end
274     end
275   end
276
277   def perform_selection_action src, dest, item, action
278     visit page_with_token 'active', '/'
279     find("#projects-menu").click
280     find(".dropdown-menu a", text: src['name']).click
281     click_link 'Data collections'
282     assert page.has_text?(item['name']), 'Collection not found in src project'
283
284     within('tr', text: item['name']) do
285       find('input[type=checkbox]').click
286     end
287
288     click_button 'Selection'
289
290     within('.selection-action-container') do
291       assert page.has_text?("Compare selected"), "Compare selected link text not found"
292       assert page.has_link?("Copy selected"), "Copy selected link not found"
293       assert page.has_link?("Move selected"), "Move selected link not found"
294       assert page.has_link?("Remove selected"), "Remove selected link not found"
295
296       click_link "#{action} selected"
297     end
298
299     # select the destination project if a Copy or Move action is being performed
300     if action == 'Copy' || action == 'Move'
301       within(".modal-container") do
302         find('.selectable', text: dest['name']).click
303         find('.modal-footer a,button', text: action).click
304         wait_for_ajax
305       end
306     end
307   end
308
309   # Test copy action state. It should not be available when a subproject is selected.
310   test "copy action is disabled when a subproject is selected" do
311     my_project = api_fixture('groups')['aproject']
312     my_collection = api_fixture('collections')['collection_to_move_around_in_aproject']
313     my_subproject = api_fixture('groups')['asubproject']
314
315     # verify that selection options are disabled on the project until an item is selected
316     visit page_with_token 'active', '/'
317     find("#projects-menu").click
318     find(".dropdown-menu a", text: my_project['name']).click
319
320     click_link 'Data collections'
321     click_button 'Selection'
322     within('.selection-action-container') do
323       assert_selector 'li.disabled', text: 'Create new collection with selected collections'
324       assert_selector 'li.disabled', text: 'Compare selected'
325       assert_selector 'li.disabled', text: 'Copy selected'
326       assert_selector 'li.disabled', text: 'Move selected'
327       assert_selector 'li.disabled', text: 'Remove selected'
328     end
329
330     # select collection and verify links are enabled
331     visit page_with_token 'active', '/'
332     find("#projects-menu").click
333     find(".dropdown-menu a", text: my_project['name']).click
334     click_link 'Data collections'
335     assert page.has_text?(my_collection['name']), 'Collection not found in project'
336
337     within('tr', text: my_collection['name']) do
338       find('input[type=checkbox]').click
339     end
340
341     click_button 'Selection'
342     within('.selection-action-container') do
343       assert_no_selector 'li.disabled', text: 'Create new collection with selected collections'
344       assert_selector 'li', text: 'Create new collection with selected collections'
345       assert_selector 'li.disabled', text: 'Compare selected'
346       assert_no_selector 'li.disabled', text: 'Copy selected'
347       assert_selector 'li', text: 'Copy selected'
348       assert_no_selector 'li.disabled', text: 'Move selected'
349       assert_selector 'li', text: 'Move selected'
350       assert_no_selector 'li.disabled', text: 'Remove selected'
351       assert_selector 'li', text: 'Remove selected'
352     end
353
354     # select subproject and verify that copy action is disabled
355     visit page_with_token 'active', '/'
356     find("#projects-menu").click
357     find(".dropdown-menu a", text: my_project['name']).click
358
359     click_link 'Subprojects'
360     assert page.has_text?(my_subproject['name']), 'Subproject not found in project'
361
362     within('tr', text: my_subproject['name']) do
363       find('input[type=checkbox]').click
364     end
365
366     click_button 'Selection'
367     within('.selection-action-container') do
368       assert_selector 'li.disabled', text: 'Create new collection with selected collections'
369       assert_selector 'li.disabled', text: 'Compare selected'
370       assert_selector 'li.disabled', text: 'Copy selected'
371       assert_no_selector 'li.disabled', text: 'Move selected'
372       assert_selector 'li', text: 'Move selected'
373       assert_no_selector 'li.disabled', text: 'Remove selected'
374       assert_selector 'li', text: 'Remove selected'
375     end
376
377     # select subproject and a collection and verify that copy action is still disabled
378     visit page_with_token 'active', '/'
379     find("#projects-menu").click
380     find(".dropdown-menu a", text: my_project['name']).click
381
382     click_link 'Subprojects'
383     assert page.has_text?(my_subproject['name']), 'Subproject not found in project'
384
385     within('tr', text: my_subproject['name']) do
386       find('input[type=checkbox]').click
387     end
388
389     click_link 'Data collections'
390     assert page.has_text?(my_collection['name']), 'Collection not found in project'
391
392     within('tr', text: my_collection['name']) do
393       find('input[type=checkbox]').click
394     end
395
396     click_link 'Subprojects'
397     click_button 'Selection'
398     within('.selection-action-container') do
399       assert_selector 'li.disabled', text: 'Create new collection with selected collections'
400       assert_selector 'li.disabled', text: 'Compare selected'
401       assert_selector 'li.disabled', text: 'Copy selected'
402       assert_no_selector 'li.disabled', text: 'Move selected'
403       assert_selector 'li', text: 'Move selected'
404       assert_no_selector 'li.disabled', text: 'Remove selected'
405       assert_selector 'li', text: 'Remove selected'
406     end
407   end
408
409   # When project tabs are switched, only options applicable to the current tab's selections are enabled.
410   test "verify selection options when tabs are switched" do
411     my_project = api_fixture('groups')['aproject']
412     my_collection = api_fixture('collections')['collection_to_move_around_in_aproject']
413     my_subproject = api_fixture('groups')['asubproject']
414
415     # select subproject and a collection and verify that copy action is still disabled
416     visit page_with_token 'active', '/'
417     find("#projects-menu").click
418     find(".dropdown-menu a", text: my_project['name']).click
419
420     # Select a sub-project
421     click_link 'Subprojects'
422     assert page.has_text?(my_subproject['name']), 'Subproject not found in project'
423
424     within('tr', text: my_subproject['name']) do
425       find('input[type=checkbox]').click
426     end
427
428     # Select a collection
429     click_link 'Data collections'
430     assert page.has_text?(my_collection['name']), 'Collection not found in project'
431
432     within('tr', text: my_collection['name']) do
433       find('input[type=checkbox]').click
434     end
435
436     # Go back to Subprojects tab
437     click_link 'Subprojects'
438     click_button 'Selection'
439     within('.selection-action-container') do
440       assert_selector 'li.disabled', text: 'Create new collection with selected collections'
441       assert_selector 'li.disabled', text: 'Compare selected'
442       assert_selector 'li.disabled', text: 'Copy selected'
443       assert_no_selector 'li.disabled', text: 'Move selected'
444       assert_selector 'li', text: 'Move selected'
445       assert_no_selector 'li.disabled', text: 'Remove selected'
446       assert_selector 'li', text: 'Remove selected'
447     end
448
449     # Close the dropdown by clicking outside it.
450     find('.dropdown-toggle', text: 'Selection').find(:xpath, '..').click
451
452     # Go back to Data collections tab
453     find('.nav-tabs a', text: 'Data collections').click
454     click_button 'Selection'
455     within('.selection-action-container') do
456       assert_no_selector 'li.disabled', text: 'Create new collection with selected collections'
457       assert_selector 'li', text: 'Create new collection with selected collections'
458       assert_selector 'li.disabled', text: 'Compare selected'
459       assert_no_selector 'li.disabled', text: 'Copy selected'
460       assert_selector 'li', text: 'Copy selected'
461       assert_no_selector 'li.disabled', text: 'Move selected'
462       assert_selector 'li', text: 'Move selected'
463       assert_no_selector 'li.disabled', text: 'Remove selected'
464       assert_selector 'li', text: 'Remove selected'
465     end
466   end
467
468   # "Move selected" and "Remove selected" options should not be
469   # available when current user cannot write to the project
470   test "move selected and remove selected actions not available when current user cannot write to project" do
471     my_project = api_fixture('groups')['anonymously_accessible_project']
472     visit page_with_token 'active', "/projects/#{my_project['uuid']}"
473
474     click_link 'Data collections'
475     click_button 'Selection'
476     within('.selection-action-container') do
477       assert_selector 'li', text: 'Create new collection with selected collections'
478       assert_selector 'li', text: 'Compare selected'
479       assert_selector 'li', text: 'Copy selected'
480       assert_no_selector 'li', text: 'Move selected'
481       assert_no_selector 'li', text: 'Remove selected'
482     end
483   end
484
485   [
486     ['active', true],
487     ['project_viewer', false],
488   ].each do |user, expect_collection_in_aproject|
489     test "combine selected collections into new collection #{user} #{expect_collection_in_aproject}" do
490       my_project = api_fixture('groups')['aproject']
491       my_collection = api_fixture('collections')['collection_to_move_around_in_aproject']
492
493       visit page_with_token user, '/'
494       find("#projects-menu").click
495       find(".dropdown-menu a", text: my_project['name']).click
496       click_link 'Data collections'
497       assert page.has_text?(my_collection['name']), 'Collection not found in project'
498
499       within('tr', text: my_collection['name']) do
500         find('input[type=checkbox]').click
501       end
502
503       click_button 'Selection'
504       within('.selection-action-container') do
505         click_link 'Create new collection with selected collections'
506       end
507
508       # now in the new collection page
509       if expect_collection_in_aproject
510         assert page.has_text?("Created new collection in the project #{my_project['name']}"),
511                               'Not found flash message that new collection is created in aproject'
512       else
513         assert page.has_text?("Created new collection in your Home project"),
514                               'Not found flash message that new collection is created in Home project'
515       end
516     end
517   end
518
519   [
520     ["jobs", "/jobs"],
521     ["pipelines", "/pipeline_instances"],
522     ["collections", "/collections"]
523   ].each do |target,path|
524     test "Test dashboard button all #{target}" do
525       visit page_with_token 'active', '/'
526       click_link "All #{target}"
527       assert_equal path, current_path
528     end
529   end
530
531   def scroll_setup(project_name,
532                    total_nbr_items,
533                    item_list_parameter,
534                    sorted = false,
535                    sort_parameters = nil)
536     project_uuid = api_fixture('groups')[project_name]['uuid']
537     visit page_with_token 'user1_with_load', '/projects/' + project_uuid
538
539     assert(page.has_text?("#{item_list_parameter.humanize} (#{total_nbr_items})"), "Number of #{item_list_parameter.humanize} did not match the input amount")
540
541     click_link item_list_parameter.humanize
542     wait_for_ajax
543
544     if sorted
545       find("th[data-sort-order='#{sort_parameters.gsub(/\s/,'')}']").click
546       wait_for_ajax
547     end
548   end
549
550   def scroll_items_check(nbr_items,
551                          fixture_prefix,
552                          item_list_parameter,
553                          item_selector,
554                          sorted = false)
555     items = []
556     for i in 1..nbr_items
557       items << "#{fixture_prefix}#{i}"
558     end
559
560     verify_items = items.dup
561     unexpected_items = []
562     item_count = 0
563     within(".arv-project-#{item_list_parameter}") do
564       page.execute_script "window.scrollBy(0,999000)"
565       begin
566         wait_for_ajax
567       rescue
568       end
569
570       # Visit all rows. If not all expected items are found, retry
571       found_items = page.all(item_selector)
572       item_count = found_items.count
573
574       previous = nil
575       (0..item_count-1).each do |i|
576         # Found row text using the fixture string e.g. "Show Collection_#{n} "
577         item_name = found_items[i].text.split[1]
578         if !items.include? item_name
579           unexpected_items << item_name
580         else
581           verify_items.delete item_name
582         end
583         if sorted
584           # check sort order
585           assert_operator( previous.downcase, :<=, item_name.downcase) if previous
586           previous = item_name
587         end
588       end
589
590       assert_equal true, unexpected_items.empty?, "Found unexpected #{item_list_parameter.humanize} #{unexpected_items.inspect}"
591       assert_equal nbr_items, item_count, "Found different number of #{item_list_parameter.humanize}"
592       assert_equal true, verify_items.empty?, "Did not find all the #{item_list_parameter.humanize}"
593     end
594   end
595
596   [
597     ['project_with_10_collections', 10],
598     ['project_with_201_collections', 201], # two pages of data
599   ].each do |project_name, nbr_items|
600     test "scroll collections tab for #{project_name} with #{nbr_items} objects" do
601       item_list_parameter = "Data_collections"
602       scroll_setup project_name,
603                    nbr_items,
604                    item_list_parameter
605       scroll_items_check nbr_items,
606                          "Collection_",
607                          item_list_parameter,
608                          'tr[data-kind="arvados#collection"]'
609     end
610   end
611
612   [
613     ['project_with_10_collections', 10],
614     ['project_with_201_collections', 201], # two pages of data
615   ].each do |project_name, nbr_items|
616     test "scroll collections tab for #{project_name} with #{nbr_items} objects with ascending sort (case insensitive)" do
617       item_list_parameter = "Data_collections"
618       scroll_setup project_name,
619                    nbr_items,
620                    item_list_parameter,
621                    true,
622                    "collections.name"
623       scroll_items_check nbr_items,
624                          "Collection_",
625                          item_list_parameter,
626                          'tr[data-kind="arvados#collection"]',
627                          true
628     end
629   end
630
631   [
632     ['project_with_10_pipelines', 10, 0],
633     ['project_with_2_pipelines_and_60_jobs', 2, 60],
634     ['project_with_25_pipelines', 25, 0],
635   ].each do |project_name, num_pipelines, num_jobs|
636     test "scroll pipeline instances tab for #{project_name} with #{num_pipelines} pipelines and #{num_jobs} jobs" do
637       item_list_parameter = "Jobs_and_pipelines"
638       scroll_setup project_name,
639                    num_pipelines + num_jobs,
640                    item_list_parameter
641       # check the general scrolling and the pipelines
642       scroll_items_check num_pipelines,
643                          "pipeline_",
644                          item_list_parameter,
645                          'tr[data-kind="arvados#pipelineInstance"]'
646       # Check job count separately
647       jobs_found = page.all('tr[data-kind="arvados#job"]')
648       found_job_count = jobs_found.count
649       assert_equal num_jobs, found_job_count, 'Did not find expected number of jobs'
650     end
651   end
652
653   # Move button accessibility
654   [
655     ['admin', true],
656     ['active', true],  # project owner
657     ['project_viewer', false],
658     ].each do |user, can_move|
659     test "#{user} can move subproject under another user's Home #{can_move}" do
660       project = api_fixture('groups')['aproject']
661       collection = api_fixture('collections')['collection_to_move_around_in_aproject']
662
663       # verify the project move button
664       visit page_with_token user, "/projects/#{project['uuid']}"
665       if can_move
666         assert page.has_link? 'Move project...'
667       else
668         assert page.has_no_link? 'Move project...'
669       end
670     end
671   end
672
673   test "error while loading tab" do
674     original_arvados_v1_base = Rails.configuration.arvados_v1_base
675
676     visit page_with_token 'active', '/projects/' + api_fixture('groups')['aproject']['uuid']
677
678     # Point to a bad api server url to generate error
679     Rails.configuration.arvados_v1_base = "https://[100::f]:1/"
680     click_link 'Other objects'
681     within '#Other_objects' do
682       # Error
683       assert_selector('a', text: 'Reload tab')
684
685       # Now point back to the orig api server and reload tab
686       Rails.configuration.arvados_v1_base = original_arvados_v1_base
687       click_link 'Reload tab'
688       assert_no_selector('a', text: 'Reload tab')
689       assert_selector('button', text: 'Selection')
690       within '.selection-action-container' do
691         assert_selector 'tr[data-kind="arvados#trait"]'
692       end
693     end
694   end
695
696   test "add new project using projects dropdown" do
697     # verify that selection options are disabled on the project until an item is selected
698     visit page_with_token 'active', '/'
699
700     # Add a new project
701     find("#projects-menu").click
702     click_link 'Add a new project'
703     assert_text 'New project'
704     assert_text 'No description provided'
705
706     # Add one more new project
707     find("#projects-menu").click
708     click_link 'Add a new project'
709     match = /New project \(\d\)/.match page.text
710     assert match, 'Expected project name not found'
711     assert_text 'No description provided'
712   end
713
714   test "first tab loads data when visiting other tab directly" do
715     # As of 2014-12-19, the first tab of project#show uses infinite scrolling.
716     # Make sure that it loads data even if we visit another tab directly.
717     need_selenium 'to land on specified tab using {url}#Advanced'
718     user = api_fixture("users", "active")
719     visit(page_with_token("active_trustedclient",
720                           "/projects/#{user['uuid']}#Advanced"))
721     assert_text("API response")
722     find("#page-wrapper .nav-tabs :first-child a").click
723     assert_text("Collection modified at")
724   end
725 end