1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: AGPL-3.0
5 require 'integration_helper'
6 require_relative 'integration_test_utils'
8 class CollectionsTest < ActionDispatch::IntegrationTest
13 test "Can copy a collection to a project" do
14 collection_uuid = api_fixture('collections')['foo_file']['uuid']
15 collection_name = api_fixture('collections')['foo_file']['name']
16 project_uuid = api_fixture('groups')['aproject']['uuid']
17 project_name = api_fixture('groups')['aproject']['name']
18 visit page_with_token('active', "/collections/#{collection_uuid}")
19 click_link 'Copy to project...'
20 find('.selectable', text: project_name).click
21 find('.modal-footer a,button', text: 'Copy').click
22 # Should navigate to the Data collections tab of the project after copying
23 assert_text project_name
24 assert_text "Copy of #{collection_name}"
27 def check_sharing(want_state, link_regexp)
28 # We specifically want to click buttons. See #4291.
30 click_button "Unshare"
31 text_assertion = :assert_no_text
32 link_assertion = :assert_empty
34 click_button "Create sharing link"
35 text_assertion = :assert_text
36 link_assertion = :refute_empty
38 using_wait_time(Capybara.default_max_wait_time * 3) do
39 send(text_assertion, "Shared at:")
41 send(link_assertion, all("a").select { |a| a[:href] =~ link_regexp })
44 test "Hides sharing link button when configured to do so" do
45 Rails.configuration.Workbench.DisableSharingURLsUI = true
46 coll_uuid = api_fixture("collections", "collection_owned_by_active", "uuid")
47 visit page_with_token("active_trustedclient", "/collections/#{coll_uuid}")
48 assert_no_selector 'div#sharing-button'
51 test "creating and uncreating a sharing link" do
52 coll_uuid = api_fixture("collections", "collection_owned_by_active", "uuid")
54 Regexp.new(Regexp.escape("/c=#{coll_uuid}/"))
55 visit page_with_token("active_trustedclient", "/collections/#{coll_uuid}")
56 assert_selector 'div#sharing-button'
57 within "#sharing-button" do
58 check_sharing(:on, download_link_re)
59 check_sharing(:off, download_link_re)
63 test "can download an entire collection with a reader token" do
64 need_selenium "phantomjs does not follow redirects reliably, maybe https://github.com/ariya/phantomjs/issues/10389"
66 token = api_token('active')
68 datablock = `echo -n #{data.shellescape} | ARVADOS_API_TOKEN=#{token.shellescape} arv-put --no-progress --raw -`.strip
69 assert $?.success?, $?
73 mtxt = ". #{datablock} 0:#{data.length}:foo\n"
74 col = Collection.create(manifest_text: mtxt)
78 token = api_fixture('api_client_authorizations')['active_all_collections']['api_token']
79 url_head = "/collections/download/#{uuid}/#{token}/"
81 assert_text "You can download individual files listed below"
82 # It seems that Capybara can't inspect tags outside the body, so this is
83 # a very blunt approach.
84 assert_no_match(/<\s*meta[^>]+\bnofollow\b/i, page.html,
85 "wget prohibited from recursing the collection page")
86 # Look at all the links that wget would recurse through using our
87 # recommended options, and check that it's exactly the file list.
89 page.html.scan(/href="(.*?)"/) { |m| hrefs << m[0] }
90 assert_equal(['./foo'], hrefs, "download page did provide strictly file links")
92 assert_text "foo\nfile\n"
95 test "combine selected collections into new collection" do
96 foo_collection = api_fixture('collections')['foo_file']
97 bar_collection = api_fixture('collections')['bar_file']
99 visit page_with_token('active', "/collections")
101 assert(page.has_text?(foo_collection['uuid']), "Collection page did not include foo file")
102 assert(page.has_text?(bar_collection['uuid']), "Collection page did not include bar file")
104 within "tr[data-object-uuid=\"#{foo_collection['uuid']}\"]" do
105 find('input[type=checkbox]').click
108 within "tr[data-object-uuid=\"#{bar_collection['uuid']}\"]" do
109 find('input[type=checkbox]').click
112 click_button 'Selection...'
113 within('.selection-action-container') do
114 click_link 'Create new collection with selected collections'
117 # now in the newly created collection page
118 assert(page.has_text?('Copy to project'), "Copy to project text not found in new collection page")
119 assert(page.has_no_text?(foo_collection['name']), "Collection page did not include foo file")
120 assert(page.has_text?('foo'), "Collection page did not include foo file")
121 assert(page.has_no_text?(bar_collection['name']), "Collection page did not include foo file")
122 assert(page.has_text?('bar'), "Collection page did not include bar file")
123 assert(page.has_text?('Created new collection in your Home project'),
124 'Not found flash message that new collection is created in Home project')
128 ['active', 'foo_file', false],
129 ['active', 'foo_collection_in_aproject', true],
130 ['project_viewer', 'foo_file', false],
131 ['project_viewer', 'foo_collection_in_aproject', false], #aproject not writable
132 ].each do |user, collection, expect_collection_in_aproject|
133 test "combine selected collection files into new collection #{user} #{collection} #{expect_collection_in_aproject}" do
134 my_collection = api_fixture('collections')[collection]
136 visit page_with_token(user, "/collections")
138 # choose file from foo collection
139 within('tr', text: my_collection['uuid']) do
143 # now in collection page
144 find('input[type=checkbox]').click
146 click_button 'Selection...'
147 within('.selection-action-container') do
148 click_link 'Create new collection with selected files'
151 # now in the newly created collection page
152 assert(page.has_text?('Copy to project'), "Copy to project text not found in new collection page")
153 assert(page.has_no_text?(my_collection['name']), "Collection page did not include foo file")
154 assert(page.has_text?('foo'), "Collection page did not include foo file")
155 if expect_collection_in_aproject
156 aproject = api_fixture('groups')['aproject']
157 assert page.has_text?("Created new collection in the project #{aproject['name']}"),
158 'Not found flash message that new collection is created in aproject'
160 assert page.has_text?("Created new collection in your Home project"),
161 'Not found flash message that new collection is created in Home project'
166 test "combine selected collection files from collection subdirectory" do
167 visit page_with_token('user1_with_load', "/collections/zzzzz-4zz18-filesinsubdir00")
169 # now in collection page
170 input_files = page.all('input[type=checkbox]')
171 (0..input_files.count-1).each do |i|
175 click_button 'Selection...'
176 within('.selection-action-container') do
177 click_link 'Create new collection with selected files'
180 # now in the newly created collection page
181 assert(page.has_text?('file_in_subdir1'), 'file not found - file_in_subdir1')
182 assert(page.has_text?('file1_in_subdir3.txt'), 'file not found - file1_in_subdir3.txt')
183 assert(page.has_text?('file2_in_subdir3.txt'), 'file not found - file2_in_subdir3.txt')
184 assert(page.has_text?('file1_in_subdir4.txt'), 'file not found - file1_in_subdir4.txt')
185 assert(page.has_text?('file2_in_subdir4.txt'), 'file not found - file1_in_subdir4.txt')
188 test "Collection portable data hash with multiple matches with more than one page of results" do
189 pdh = api_fixture('collections')['baz_file']['portable_data_hash']
190 visit page_with_token('admin', "/collections/#{pdh}")
192 assert_selector 'a', text: 'Collection_1'
194 assert_text 'The following collections have this content:'
195 assert_text 'more results are not shown'
196 assert_no_text 'Activity'
197 assert_no_text 'Sharing and permissions'
200 test "Filtering collection files by regexp" do
201 col = api_fixture('collections', 'multilevel_collection_1')
202 visit page_with_token('active', "/collections/#{col['uuid']}")
204 # Filter file list to some but not all files in the collection
205 page.find_field('file_regex').set('file[12]')
206 assert page.has_text?("file1")
207 assert page.has_text?("file2")
208 assert page.has_no_text?("file3")
210 # Filter file list with a regex matching all files
211 page.find_field('file_regex').set('.*')
212 assert page.has_text?("file1")
213 assert page.has_text?("file2")
214 assert page.has_text?("file3")
216 # Filter file list to a regex matching no files
217 page.find_field('file_regex').set('file9')
218 assert page.has_no_text?("file1")
219 assert page.has_no_text?("file2")
220 assert page.has_no_text?("file3")
221 # make sure that we actually are looking at the collections
222 # page and not e.g. a fiddlesticks
223 assert page.has_text?("multilevel_collection_1")
224 assert page.has_text?(col["name"] || col["uuid"])
226 # Set filename filter to a syntactically invalid regex
227 # Page loads, but stops filtering after the last valid regex parse
228 page.find_field('file_regex').set('file[2')
229 assert page.has_text?("multilevel_collection_1")
230 assert page.has_text?(col["name"] || col["uuid"])
231 assert page.has_text?("file1")
232 assert page.has_text?("file2")
233 assert page.has_text?("file3")
235 # Test the "Select all" button
237 # Note: calling .set('') on a Selenium element is not sufficient
238 # to reset the field for this test, as it does not send any key
239 # events to the browser. To clear the field, we must instead send
240 # a backspace character.
241 # See https://selenium.googlecode.com/svn/trunk/docs/api/rb/Selenium/WebDriver/Element.html#clear-instance_method
242 page.find_field('file_regex').set("\b") # backspace
243 find('button#select-all').click
244 assert_checkboxes_state('input[type=checkbox]', true, '"select all" should check all checkboxes')
246 # Test the "Unselect all" button
247 page.find_field('file_regex').set("\b") # backspace
248 find('button#unselect-all').click
249 assert_checkboxes_state('input[type=checkbox]', false, '"unselect all" should clear all checkboxes')
251 # Filter files, then "select all", then unfilter
252 page.find_field('file_regex').set("\b") # backspace
253 find('button#unselect-all').click
254 page.find_field('file_regex').set('file[12]')
255 find('button#select-all').click
256 page.find_field('file_regex').set("\b") # backspace
258 # all "file1" and "file2" checkboxes must be selected
259 # all "file3" checkboxes must be clear
260 assert_checkboxes_state('[value*="file1"]', true, 'checkboxes for file1 should be selected after filtering')
261 assert_checkboxes_state('[value*="file2"]', true, 'checkboxes for file2 should be selected after filtering')
262 assert_checkboxes_state('[value*="file3"]', false, 'checkboxes for file3 should be clear after filtering')
264 # Select all files, then filter, then "unselect all", then unfilter
265 page.find_field('file_regex').set("\b") # backspace
266 find('button#select-all').click
267 page.find_field('file_regex').set('file[12]')
268 find('button#unselect-all').click
269 page.find_field('file_regex').set("\b") # backspace
271 # all "file1" and "file2" checkboxes must be clear
272 # all "file3" checkboxes must be selected
273 assert_checkboxes_state('[value*="file1"]', false, 'checkboxes for file1 should be clear after filtering')
274 assert_checkboxes_state('[value*="file2"]', false, 'checkboxes for file2 should be clear after filtering')
275 assert_checkboxes_state('[value*="file3"]', true, 'checkboxes for file3 should be selected after filtering')
278 test "Creating collection from list of filtered files" do
279 col = api_fixture('collections', 'collection_with_files_in_subdir')
280 visit page_with_token('user1_with_load', "/collections/#{col['uuid']}")
281 assert page.has_text?('file_in_subdir1'), 'expected file_in_subdir1 not found'
282 assert page.has_text?('file1_in_subdir3'), 'expected file1_in_subdir3 not found'
283 assert page.has_text?('file2_in_subdir3'), 'expected file2_in_subdir3 not found'
284 assert page.has_text?('file1_in_subdir4'), 'expected file1_in_subdir4 not found'
285 assert page.has_text?('file2_in_subdir4'), 'expected file2_in_subdir4 not found'
287 # Select all files but then filter them to files in subdir1, subdir2 or subdir3
288 find('button#select-all').click
289 page.find_field('file_regex').set('_in_subdir[123]')
290 assert page.has_text?('file_in_subdir1'), 'expected file_in_subdir1 not in filtered files'
291 assert page.has_text?('file1_in_subdir3'), 'expected file1_in_subdir3 not in filtered files'
292 assert page.has_text?('file2_in_subdir3'), 'expected file2_in_subdir3 not in filtered files'
293 assert page.has_no_text?('file1_in_subdir4'), 'file1_in_subdir4 found in filtered files'
294 assert page.has_no_text?('file2_in_subdir4'), 'file2_in_subdir4 found in filtered files'
296 # Create a new collection
297 click_button 'Selection...'
298 within('.selection-action-container') do
299 click_link 'Create new collection with selected files'
302 # now in the newly created collection page
303 # must have files in subdir1 and subdir3 but not subdir4
304 assert page.has_text?('file_in_subdir1'), 'file_in_subdir1 missing from new collection'
305 assert page.has_text?('file1_in_subdir3'), 'file1_in_subdir3 missing from new collection'
306 assert page.has_text?('file2_in_subdir3'), 'file2_in_subdir3 missing from new collection'
307 assert page.has_no_text?('file1_in_subdir4'), 'file1_in_subdir4 found in new collection'
308 assert page.has_no_text?('file2_in_subdir4'), 'file2_in_subdir4 found in new collection'
310 # Make sure we're not still on the old collection page.
311 refute_match(%r{/collections/#{col['uuid']}}, page.current_url)
314 test "remove a file from collection using checkbox and dropdown option" do
315 need_selenium 'to confirm unlock'
317 visit page_with_token('active', '/collections/zzzzz-4zz18-a21ux3541sxa8sf')
318 assert(page.has_text?('file1'), 'file not found - file1')
323 input_files = page.all('input[type=checkbox]')
326 click_button 'Selection...'
327 within('.selection-action-container') do
328 click_link 'Remove selected files'
331 assert(page.has_no_text?('file1'), 'file found - file')
332 assert(page.has_text?('file2'), 'file not found - file2')
335 test "remove a file in collection using trash icon" do
336 need_selenium 'to confirm unlock'
338 visit page_with_token('active', '/collections/zzzzz-4zz18-a21ux3541sxa8sf')
339 assert(page.has_text?('file1'), 'file not found - file1')
343 first('.fa-trash-o').click
346 assert(page.has_no_text?('file1'), 'file found - file')
347 assert(page.has_text?('file2'), 'file not found - file2')
350 test "rename a file in collection" do
351 need_selenium 'to confirm unlock'
353 visit page_with_token('active', '/collections/zzzzz-4zz18-a21ux3541sxa8sf')
357 within('.collection_files') do
358 first('.fa-pencil').click
359 find('.editable-input input').set('file1renamed')
360 find('.editable-submit').click
363 assert(page.has_text?('file1renamed'), 'file not found - file1renamed')
366 test "remove/rename file options not presented if user cannot update a collection" do
367 # visit a publicly accessible collection as 'spectator'
368 visit page_with_token('spectator', '/collections/zzzzz-4zz18-uukreo9rbgwsujr')
370 click_button 'Selection'
371 within('.selection-action-container') do
372 assert_selector 'li', text: 'Create new collection with selected files'
373 assert_no_selector 'li', text: 'Remove selected files'
376 within('.collection_files') do
377 assert(page.has_text?('GNU_General_Public_License'), 'file not found - GNU_General_Public_License')
378 assert_nil first('.fa-pencil')
379 assert_nil first('.fa-trash-o')
383 test "unlock collection to modify files" do
384 need_selenium 'to confirm remove'
386 collection = api_fixture('collections')['collection_owned_by_active']
388 # On load, collection is locked, and upload tab, rename and remove options are disabled
389 visit page_with_token('active', "/collections/#{collection['uuid']}")
391 assert_selector 'a[data-toggle="disabled"]', text: 'Upload'
393 within('.collection_files') do
394 file_ctrls = page.all('.btn-collection-file-control')
395 assert_equal 2, file_ctrls.size
396 assert_equal true, file_ctrls[0]['class'].include?('disabled')
397 assert_equal true, file_ctrls[1]['class'].include?('disabled')
398 find('input[type=checkbox]').click
401 click_button 'Selection'
402 within('.selection-action-container') do
403 assert_selector 'li.disabled', text: 'Remove selected files'
404 assert_selector 'li', text: 'Create new collection with selected files'
409 assert_no_selector 'a[data-toggle="disabled"]', text: 'Upload'
410 assert_selector 'a', text: 'Upload'
412 within('.collection_files') do
413 file_ctrls = page.all('.btn-collection-file-control')
414 assert_equal 2, file_ctrls.size
415 assert_equal false, file_ctrls[0]['class'].include?('disabled')
416 assert_equal false, file_ctrls[1]['class'].include?('disabled')
418 # previous checkbox selection won't result in firing a new event;
419 # undo and redo checkbox to fire the selection event again
420 find('input[type=checkbox]').click
421 find('input[type=checkbox]').click
424 click_button 'Selection'
425 within('.selection-action-container') do
426 assert_no_selector 'li.disabled', text: 'Remove selected files'
427 assert_selector 'li', text: 'Remove selected files'
431 def unlock_collection
432 first('.lock-collection-btn').click