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