closes #11450
[arvados.git] / apps / workbench / test / integration / collections_test.rb
1 require 'integration_helper'
2 require_relative 'integration_test_utils'
3
4 class CollectionsTest < ActionDispatch::IntegrationTest
5   setup do
6     need_javascript
7   end
8
9   test "Can copy a collection to a project" do
10     collection_uuid = api_fixture('collections')['foo_file']['uuid']
11     collection_name = api_fixture('collections')['foo_file']['name']
12     project_uuid = api_fixture('groups')['aproject']['uuid']
13     project_name = api_fixture('groups')['aproject']['name']
14     visit page_with_token('active', "/collections/#{collection_uuid}")
15     click_link 'Copy to project...'
16     find('.selectable', text: project_name).click
17     find('.modal-footer a,button', text: 'Copy').click
18     # Should navigate to the Data collections tab of the project after copying
19     assert_text project_name
20     assert_text "Copy of #{collection_name}"
21   end
22
23   def check_sharing(want_state, link_regexp)
24     # We specifically want to click buttons.  See #4291.
25     if want_state == :off
26       click_button "Unshare"
27       text_assertion = :assert_no_text
28       link_assertion = :assert_empty
29     else
30       click_button "Create sharing link"
31       text_assertion = :assert_text
32       link_assertion = :refute_empty
33     end
34     using_wait_time(Capybara.default_max_wait_time * 3) do
35       send(text_assertion, "Shared at:")
36     end
37     send(link_assertion, all("a").select { |a| a[:href] =~ link_regexp })
38   end
39
40   test "creating and uncreating a sharing link" do
41     coll_uuid = api_fixture("collections", "collection_owned_by_active", "uuid")
42     download_link_re =
43       Regexp.new(Regexp.escape("/collections/download/#{coll_uuid}/"))
44     visit page_with_token("active_trustedclient", "/collections/#{coll_uuid}")
45     within "#sharing-button" do
46       check_sharing(:on, download_link_re)
47       check_sharing(:off, download_link_re)
48     end
49   end
50
51   test "can download an entire collection with a reader token" do
52     Capybara.current_driver = :rack_test
53     CollectionsController.any_instance.
54       stubs(:file_enumerator).returns(["foo\n", "file\n"])
55     uuid = api_fixture('collections')['foo_file']['uuid']
56     token = api_fixture('api_client_authorizations')['active_all_collections']['api_token']
57     url_head = "/collections/download/#{uuid}/#{token}/"
58     visit url_head
59     # It seems that Capybara can't inspect tags outside the body, so this is
60     # a very blunt approach.
61     assert_no_match(/<\s*meta[^>]+\bnofollow\b/i, page.html,
62                     "wget prohibited from recursing the collection page")
63     # Look at all the links that wget would recurse through using our
64     # recommended options, and check that it's exactly the file list.
65     hrefs = page.all('a').map do |anchor|
66       link = anchor[:href] || ''
67       if link.start_with? url_head
68         link[url_head.size .. -1]
69       elsif link.start_with? '/'
70         nil
71       else
72         link
73       end
74     end
75     assert_equal(['foo'], hrefs.compact.sort,
76                  "download page did provide strictly file links")
77     within "#collection_files" do
78       click_link "foo"
79       assert_equal("foo\nfile\n", page.html)
80     end
81   end
82
83   test "combine selected collections into new collection" do
84     foo_collection = api_fixture('collections')['foo_file']
85     bar_collection = api_fixture('collections')['bar_file']
86
87     visit page_with_token('active', "/collections")
88
89     assert(page.has_text?(foo_collection['uuid']), "Collection page did not include foo file")
90     assert(page.has_text?(bar_collection['uuid']), "Collection page did not include bar file")
91
92     within('tr', text: foo_collection['uuid']) do
93       find('input[type=checkbox]').click
94     end
95
96     within('tr', text: bar_collection['uuid']) do
97       find('input[type=checkbox]').click
98     end
99
100     click_button 'Selection...'
101     within('.selection-action-container') do
102       click_link 'Create new collection with selected collections'
103     end
104
105     # now in the newly created collection page
106     assert(page.has_text?('Copy to project'), "Copy to project text not found in new collection page")
107     assert(page.has_no_text?(foo_collection['name']), "Collection page did not include foo file")
108     assert(page.has_text?('foo'), "Collection page did not include foo file")
109     assert(page.has_no_text?(bar_collection['name']), "Collection page did not include foo file")
110     assert(page.has_text?('bar'), "Collection page did not include bar file")
111     assert(page.has_text?('Created new collection in your Home project'),
112                           'Not found flash message that new collection is created in Home project')
113   end
114
115   [
116     ['active', 'foo_file', false],
117     ['active', 'foo_collection_in_aproject', true],
118     ['project_viewer', 'foo_file', false],
119     ['project_viewer', 'foo_collection_in_aproject', false], #aproject not writable
120   ].each do |user, collection, expect_collection_in_aproject|
121     test "combine selected collection files into new collection #{user} #{collection} #{expect_collection_in_aproject}" do
122       my_collection = api_fixture('collections')[collection]
123
124       visit page_with_token(user, "/collections")
125
126       # choose file from foo collection
127       within('tr', text: my_collection['uuid']) do
128         click_link 'Show'
129       end
130
131       # now in collection page
132       find('input[type=checkbox]').click
133
134       click_button 'Selection...'
135       within('.selection-action-container') do
136         click_link 'Create new collection with selected files'
137       end
138
139       # now in the newly created collection page
140       assert(page.has_text?('Copy to project'), "Copy to project text not found in new collection page")
141       assert(page.has_no_text?(my_collection['name']), "Collection page did not include foo file")
142       assert(page.has_text?('foo'), "Collection page did not include foo file")
143       if expect_collection_in_aproject
144         aproject = api_fixture('groups')['aproject']
145         assert page.has_text?("Created new collection in the project #{aproject['name']}"),
146                               'Not found flash message that new collection is created in aproject'
147       else
148         assert page.has_text?("Created new collection in your Home project"),
149                               'Not found flash message that new collection is created in Home project'
150       end
151     end
152   end
153
154   test "combine selected collection files from collection subdirectory" do
155     visit page_with_token('user1_with_load', "/collections/zzzzz-4zz18-filesinsubdir00")
156
157     # now in collection page
158     input_files = page.all('input[type=checkbox]')
159     (0..input_files.count-1).each do |i|
160       input_files[i].click
161     end
162
163     click_button 'Selection...'
164     within('.selection-action-container') do
165       click_link 'Create new collection with selected files'
166     end
167
168     # now in the newly created collection page
169     assert(page.has_text?('file_in_subdir1'), 'file not found - file_in_subdir1')
170     assert(page.has_text?('file1_in_subdir3.txt'), 'file not found - file1_in_subdir3.txt')
171     assert(page.has_text?('file2_in_subdir3.txt'), 'file not found - file2_in_subdir3.txt')
172     assert(page.has_text?('file1_in_subdir4.txt'), 'file not found - file1_in_subdir4.txt')
173     assert(page.has_text?('file2_in_subdir4.txt'), 'file not found - file1_in_subdir4.txt')
174   end
175
176   test "Collection portable data hash with multiple matches with more than one page of results" do
177     pdh = api_fixture('collections')['baz_file']['portable_data_hash']
178     visit page_with_token('admin', "/collections/#{pdh}")
179
180     assert_selector 'a', text: 'Collection_1'
181
182     assert_text 'The following collections have this content:'
183     assert_text 'more results are not shown'
184     assert_no_text 'Activity'
185     assert_no_text 'Sharing and permissions'
186   end
187
188   test "Filtering collection files by regexp" do
189     col = api_fixture('collections', 'multilevel_collection_1')
190     visit page_with_token('active', "/collections/#{col['uuid']}")
191
192     # Filter file list to some but not all files in the collection
193     page.find_field('file_regex').set('file[12]')
194     assert page.has_text?("file1")
195     assert page.has_text?("file2")
196     assert page.has_no_text?("file3")
197
198     # Filter file list with a regex matching all files
199     page.find_field('file_regex').set('.*')
200     assert page.has_text?("file1")
201     assert page.has_text?("file2")
202     assert page.has_text?("file3")
203
204     # Filter file list to a regex matching no files
205     page.find_field('file_regex').set('file9')
206     assert page.has_no_text?("file1")
207     assert page.has_no_text?("file2")
208     assert page.has_no_text?("file3")
209     # make sure that we actually are looking at the collections
210     # page and not e.g. a fiddlesticks
211     assert page.has_text?("multilevel_collection_1")
212     assert page.has_text?(col["name"] || col["uuid"])
213
214     # Set filename filter to a syntactically invalid regex
215     # Page loads, but stops filtering after the last valid regex parse
216     page.find_field('file_regex').set('file[2')
217     assert page.has_text?("multilevel_collection_1")
218     assert page.has_text?(col["name"] || col["uuid"])
219     assert page.has_text?("file1")
220     assert page.has_text?("file2")
221     assert page.has_text?("file3")
222
223     # Test the "Select all" button
224
225     # Note: calling .set('') on a Selenium element is not sufficient
226     # to reset the field for this test, as it does not send any key
227     # events to the browser. To clear the field, we must instead send
228     # a backspace character.
229     # See https://selenium.googlecode.com/svn/trunk/docs/api/rb/Selenium/WebDriver/Element.html#clear-instance_method
230     page.find_field('file_regex').set("\b") # backspace
231     find('button#select-all').click
232     assert_checkboxes_state('input[type=checkbox]', true, '"select all" should check all checkboxes')
233
234     # Test the "Unselect all" button
235     page.find_field('file_regex').set("\b") # backspace
236     find('button#unselect-all').click
237     assert_checkboxes_state('input[type=checkbox]', false, '"unselect all" should clear all checkboxes')
238
239     # Filter files, then "select all", then unfilter
240     page.find_field('file_regex').set("\b") # backspace
241     find('button#unselect-all').click
242     page.find_field('file_regex').set('file[12]')
243     find('button#select-all').click
244     page.find_field('file_regex').set("\b") # backspace
245
246     # all "file1" and "file2" checkboxes must be selected
247     # all "file3" checkboxes must be clear
248     assert_checkboxes_state('[value*="file1"]', true, 'checkboxes for file1 should be selected after filtering')
249     assert_checkboxes_state('[value*="file2"]', true, 'checkboxes for file2 should be selected after filtering')
250     assert_checkboxes_state('[value*="file3"]', false, 'checkboxes for file3 should be clear after filtering')
251
252     # Select all files, then filter, then "unselect all", then unfilter
253     page.find_field('file_regex').set("\b") # backspace
254     find('button#select-all').click
255     page.find_field('file_regex').set('file[12]')
256     find('button#unselect-all').click
257     page.find_field('file_regex').set("\b") # backspace
258
259     # all "file1" and "file2" checkboxes must be clear
260     # all "file3" checkboxes must be selected
261     assert_checkboxes_state('[value*="file1"]', false, 'checkboxes for file1 should be clear after filtering')
262     assert_checkboxes_state('[value*="file2"]', false, 'checkboxes for file2 should be clear after filtering')
263     assert_checkboxes_state('[value*="file3"]', true, 'checkboxes for file3 should be selected after filtering')
264   end
265
266   test "Creating collection from list of filtered files" do
267     col = api_fixture('collections', 'collection_with_files_in_subdir')
268     visit page_with_token('user1_with_load', "/collections/#{col['uuid']}")
269     assert page.has_text?('file_in_subdir1'), 'expected file_in_subdir1 not found'
270     assert page.has_text?('file1_in_subdir3'), 'expected file1_in_subdir3 not found'
271     assert page.has_text?('file2_in_subdir3'), 'expected file2_in_subdir3 not found'
272     assert page.has_text?('file1_in_subdir4'), 'expected file1_in_subdir4 not found'
273     assert page.has_text?('file2_in_subdir4'), 'expected file2_in_subdir4 not found'
274
275     # Select all files but then filter them to files in subdir1, subdir2 or subdir3
276     find('button#select-all').click
277     page.find_field('file_regex').set('_in_subdir[123]')
278     assert page.has_text?('file_in_subdir1'), 'expected file_in_subdir1 not in filtered files'
279     assert page.has_text?('file1_in_subdir3'), 'expected file1_in_subdir3 not in filtered files'
280     assert page.has_text?('file2_in_subdir3'), 'expected file2_in_subdir3 not in filtered files'
281     assert page.has_no_text?('file1_in_subdir4'), 'file1_in_subdir4 found in filtered files'
282     assert page.has_no_text?('file2_in_subdir4'), 'file2_in_subdir4 found in filtered files'
283
284     # Create a new collection
285     click_button 'Selection...'
286     within('.selection-action-container') do
287       click_link 'Create new collection with selected files'
288     end
289
290     # now in the newly created collection page
291     # must have files in subdir1 and subdir3 but not subdir4
292     assert page.has_text?('file_in_subdir1'), 'file_in_subdir1 missing from new collection'
293     assert page.has_text?('file1_in_subdir3'), 'file1_in_subdir3 missing from new collection'
294     assert page.has_text?('file2_in_subdir3'), 'file2_in_subdir3 missing from new collection'
295     assert page.has_no_text?('file1_in_subdir4'), 'file1_in_subdir4 found in new collection'
296     assert page.has_no_text?('file2_in_subdir4'), 'file2_in_subdir4 found in new collection'
297
298     # Make sure we're not still on the old collection page.
299     refute_match(%r{/collections/#{col['uuid']}}, page.current_url)
300   end
301
302   test "remove a file from collection using checkbox and dropdown option" do
303     need_selenium 'to confirm unlock'
304
305     visit page_with_token('active', '/collections/zzzzz-4zz18-a21ux3541sxa8sf')
306     assert(page.has_text?('file1'), 'file not found - file1')
307
308     unlock_collection
309
310     # remove first file
311     input_files = page.all('input[type=checkbox]')
312     input_files[0].click
313
314     click_button 'Selection...'
315     within('.selection-action-container') do
316       click_link 'Remove selected files'
317     end
318
319     assert(page.has_no_text?('file1'), 'file found - file')
320     assert(page.has_text?('file2'), 'file not found - file2')
321   end
322
323   test "remove a file in collection using trash icon" do
324     need_selenium 'to confirm unlock'
325
326     visit page_with_token('active', '/collections/zzzzz-4zz18-a21ux3541sxa8sf')
327     assert(page.has_text?('file1'), 'file not found - file1')
328
329     unlock_collection
330
331     first('.fa-trash-o').click
332     accept_alert
333
334     assert(page.has_no_text?('file1'), 'file found - file')
335     assert(page.has_text?('file2'), 'file not found - file2')
336   end
337
338   test "rename a file in collection" do
339     need_selenium 'to confirm unlock'
340
341     visit page_with_token('active', '/collections/zzzzz-4zz18-a21ux3541sxa8sf')
342
343     unlock_collection
344
345     within('.collection_files') do
346       first('.fa-pencil').click
347       find('.editable-input input').set('file1renamed')
348       find('.editable-submit').click
349     end
350
351     assert(page.has_text?('file1renamed'), 'file not found - file1renamed')
352   end
353
354   test "remove/rename file options not presented if user cannot update a collection" do
355     # visit a publicly accessible collection as 'spectator'
356     visit page_with_token('spectator', '/collections/zzzzz-4zz18-uukreo9rbgwsujr')
357
358     click_button 'Selection'
359     within('.selection-action-container') do
360       assert_selector 'li', text: 'Create new collection with selected files'
361       assert_no_selector 'li', text: 'Remove selected files'
362     end
363
364     within('.collection_files') do
365       assert(page.has_text?('GNU_General_Public_License'), 'file not found - GNU_General_Public_License')
366       assert_nil first('.fa-pencil')
367       assert_nil first('.fa-trash-o')
368     end
369   end
370
371   test "unlock collection to modify files" do
372     need_selenium 'to confirm remove'
373
374     collection = api_fixture('collections')['collection_owned_by_active']
375
376     # On load, collection is locked, and upload tab, rename and remove options are disabled
377     visit page_with_token('active', "/collections/#{collection['uuid']}")
378
379     assert_selector 'a[data-toggle="disabled"]', text: 'Upload'
380
381     within('.collection_files') do
382       file_ctrls = page.all('.btn-collection-file-control')
383       assert_equal 2, file_ctrls.size
384       assert_equal true, file_ctrls[0]['class'].include?('disabled')
385       assert_equal true, file_ctrls[1]['class'].include?('disabled')
386       find('input[type=checkbox]').click
387     end
388
389     click_button 'Selection'
390     within('.selection-action-container') do
391       assert_selector 'li.disabled', text: 'Remove selected files'
392       assert_selector 'li', text: 'Create new collection with selected files'
393     end
394
395     unlock_collection
396
397     assert_no_selector 'a[data-toggle="disabled"]', text: 'Upload'
398     assert_selector 'a', text: 'Upload'
399
400     within('.collection_files') do
401       file_ctrls = page.all('.btn-collection-file-control')
402       assert_equal 2, file_ctrls.size
403       assert_equal false, file_ctrls[0]['class'].include?('disabled')
404       assert_equal false, file_ctrls[1]['class'].include?('disabled')
405
406       # previous checkbox selection won't result in firing a new event;
407       # undo and redo checkbox to fire the selection event again
408       find('input[type=checkbox]').click
409       find('input[type=checkbox]').click
410     end
411
412     click_button 'Selection'
413     within('.selection-action-container') do
414       assert_no_selector 'li.disabled', text: 'Remove selected files'
415       assert_selector 'li', text: 'Remove selected files'
416     end
417   end
418
419   def unlock_collection
420     first('.lock-collection-btn').click
421     accept_alert
422   end
423 end