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 "creating and uncreating a sharing link" do
45 coll_uuid = api_fixture("collections", "collection_owned_by_active", "uuid")
47 Regexp.new(Regexp.escape("/collections/download/#{coll_uuid}/"))
48 visit page_with_token("active_trustedclient", "/collections/#{coll_uuid}")
49 within "#sharing-button" do
50 check_sharing(:on, download_link_re)
51 check_sharing(:off, download_link_re)
55 test "combine selected collections into new collection" do
56 foo_collection = api_fixture('collections')['foo_file']
57 bar_collection = api_fixture('collections')['bar_file']
59 visit page_with_token('active', "/collections")
61 assert(page.has_text?(foo_collection['uuid']), "Collection page did not include foo file")
62 assert(page.has_text?(bar_collection['uuid']), "Collection page did not include bar file")
64 within "tr[data-object-uuid=\"#{foo_collection['uuid']}\"]" do
65 find('input[type=checkbox]').click
68 within "tr[data-object-uuid=\"#{bar_collection['uuid']}\"]" do
69 find('input[type=checkbox]').click
72 click_button 'Selection...'
73 within('.selection-action-container') do
74 click_link 'Create new collection with selected collections'
77 # now in the newly created collection page
78 assert(page.has_text?('Copy to project'), "Copy to project text not found in new collection page")
79 assert(page.has_no_text?(foo_collection['name']), "Collection page did not include foo file")
80 assert(page.has_text?('foo'), "Collection page did not include foo file")
81 assert(page.has_no_text?(bar_collection['name']), "Collection page did not include foo file")
82 assert(page.has_text?('bar'), "Collection page did not include bar file")
83 assert(page.has_text?('Created new collection in your Home project'),
84 'Not found flash message that new collection is created in Home project')
88 ['active', 'foo_file', false],
89 ['active', 'foo_collection_in_aproject', true],
90 ['project_viewer', 'foo_file', false],
91 ['project_viewer', 'foo_collection_in_aproject', false], #aproject not writable
92 ].each do |user, collection, expect_collection_in_aproject|
93 test "combine selected collection files into new collection #{user} #{collection} #{expect_collection_in_aproject}" do
94 my_collection = api_fixture('collections')[collection]
96 visit page_with_token(user, "/collections")
98 # choose file from foo collection
99 within('tr', text: my_collection['uuid']) do
103 # now in collection page
104 find('input[type=checkbox]').click
106 click_button 'Selection...'
107 within('.selection-action-container') do
108 click_link 'Create new collection with selected files'
111 # now in the newly created collection page
112 assert(page.has_text?('Copy to project'), "Copy to project text not found in new collection page")
113 assert(page.has_no_text?(my_collection['name']), "Collection page did not include foo file")
114 assert(page.has_text?('foo'), "Collection page did not include foo file")
115 if expect_collection_in_aproject
116 aproject = api_fixture('groups')['aproject']
117 assert page.has_text?("Created new collection in the project #{aproject['name']}"),
118 'Not found flash message that new collection is created in aproject'
120 assert page.has_text?("Created new collection in your Home project"),
121 'Not found flash message that new collection is created in Home project'
126 test "combine selected collection files from collection subdirectory" do
127 visit page_with_token('user1_with_load', "/collections/zzzzz-4zz18-filesinsubdir00")
129 # now in collection page
130 input_files = page.all('input[type=checkbox]')
131 (0..input_files.count-1).each do |i|
135 click_button 'Selection...'
136 within('.selection-action-container') do
137 click_link 'Create new collection with selected files'
140 # now in the newly created collection page
141 assert(page.has_text?('file_in_subdir1'), 'file not found - file_in_subdir1')
142 assert(page.has_text?('file1_in_subdir3.txt'), 'file not found - file1_in_subdir3.txt')
143 assert(page.has_text?('file2_in_subdir3.txt'), 'file not found - file2_in_subdir3.txt')
144 assert(page.has_text?('file1_in_subdir4.txt'), 'file not found - file1_in_subdir4.txt')
145 assert(page.has_text?('file2_in_subdir4.txt'), 'file not found - file1_in_subdir4.txt')
148 test "Collection portable data hash with multiple matches with more than one page of results" do
149 pdh = api_fixture('collections')['baz_file']['portable_data_hash']
150 visit page_with_token('admin', "/collections/#{pdh}")
152 assert_selector 'a', text: 'Collection_1'
154 assert_text 'The following collections have this content:'
155 assert_text 'more results are not shown'
156 assert_no_text 'Activity'
157 assert_no_text 'Sharing and permissions'
160 test "Filtering collection files by regexp" do
161 col = api_fixture('collections', 'multilevel_collection_1')
162 visit page_with_token('active', "/collections/#{col['uuid']}")
164 # Filter file list to some but not all files in the collection
165 page.find_field('file_regex').set('file[12]')
166 assert page.has_text?("file1")
167 assert page.has_text?("file2")
168 assert page.has_no_text?("file3")
170 # Filter file list with a regex matching all files
171 page.find_field('file_regex').set('.*')
172 assert page.has_text?("file1")
173 assert page.has_text?("file2")
174 assert page.has_text?("file3")
176 # Filter file list to a regex matching no files
177 page.find_field('file_regex').set('file9')
178 assert page.has_no_text?("file1")
179 assert page.has_no_text?("file2")
180 assert page.has_no_text?("file3")
181 # make sure that we actually are looking at the collections
182 # page and not e.g. a fiddlesticks
183 assert page.has_text?("multilevel_collection_1")
184 assert page.has_text?(col["name"] || col["uuid"])
186 # Set filename filter to a syntactically invalid regex
187 # Page loads, but stops filtering after the last valid regex parse
188 page.find_field('file_regex').set('file[2')
189 assert page.has_text?("multilevel_collection_1")
190 assert page.has_text?(col["name"] || col["uuid"])
191 assert page.has_text?("file1")
192 assert page.has_text?("file2")
193 assert page.has_text?("file3")
195 # Test the "Select all" button
197 # Note: calling .set('') on a Selenium element is not sufficient
198 # to reset the field for this test, as it does not send any key
199 # events to the browser. To clear the field, we must instead send
200 # a backspace character.
201 # See https://selenium.googlecode.com/svn/trunk/docs/api/rb/Selenium/WebDriver/Element.html#clear-instance_method
202 page.find_field('file_regex').set("\b") # backspace
203 find('button#select-all').click
204 assert_checkboxes_state('input[type=checkbox]', true, '"select all" should check all checkboxes')
206 # Test the "Unselect all" button
207 page.find_field('file_regex').set("\b") # backspace
208 find('button#unselect-all').click
209 assert_checkboxes_state('input[type=checkbox]', false, '"unselect all" should clear all checkboxes')
211 # Filter files, then "select all", then unfilter
212 page.find_field('file_regex').set("\b") # backspace
213 find('button#unselect-all').click
214 page.find_field('file_regex').set('file[12]')
215 find('button#select-all').click
216 page.find_field('file_regex').set("\b") # backspace
218 # all "file1" and "file2" checkboxes must be selected
219 # all "file3" checkboxes must be clear
220 assert_checkboxes_state('[value*="file1"]', true, 'checkboxes for file1 should be selected after filtering')
221 assert_checkboxes_state('[value*="file2"]', true, 'checkboxes for file2 should be selected after filtering')
222 assert_checkboxes_state('[value*="file3"]', false, 'checkboxes for file3 should be clear after filtering')
224 # Select all files, then filter, then "unselect all", then unfilter
225 page.find_field('file_regex').set("\b") # backspace
226 find('button#select-all').click
227 page.find_field('file_regex').set('file[12]')
228 find('button#unselect-all').click
229 page.find_field('file_regex').set("\b") # backspace
231 # all "file1" and "file2" checkboxes must be clear
232 # all "file3" checkboxes must be selected
233 assert_checkboxes_state('[value*="file1"]', false, 'checkboxes for file1 should be clear after filtering')
234 assert_checkboxes_state('[value*="file2"]', false, 'checkboxes for file2 should be clear after filtering')
235 assert_checkboxes_state('[value*="file3"]', true, 'checkboxes for file3 should be selected after filtering')
238 test "Creating collection from list of filtered files" do
239 col = api_fixture('collections', 'collection_with_files_in_subdir')
240 visit page_with_token('user1_with_load', "/collections/#{col['uuid']}")
241 assert page.has_text?('file_in_subdir1'), 'expected file_in_subdir1 not found'
242 assert page.has_text?('file1_in_subdir3'), 'expected file1_in_subdir3 not found'
243 assert page.has_text?('file2_in_subdir3'), 'expected file2_in_subdir3 not found'
244 assert page.has_text?('file1_in_subdir4'), 'expected file1_in_subdir4 not found'
245 assert page.has_text?('file2_in_subdir4'), 'expected file2_in_subdir4 not found'
247 # Select all files but then filter them to files in subdir1, subdir2 or subdir3
248 find('button#select-all').click
249 page.find_field('file_regex').set('_in_subdir[123]')
250 assert page.has_text?('file_in_subdir1'), 'expected file_in_subdir1 not in filtered files'
251 assert page.has_text?('file1_in_subdir3'), 'expected file1_in_subdir3 not in filtered files'
252 assert page.has_text?('file2_in_subdir3'), 'expected file2_in_subdir3 not in filtered files'
253 assert page.has_no_text?('file1_in_subdir4'), 'file1_in_subdir4 found in filtered files'
254 assert page.has_no_text?('file2_in_subdir4'), 'file2_in_subdir4 found in filtered files'
256 # Create a new collection
257 click_button 'Selection...'
258 within('.selection-action-container') do
259 click_link 'Create new collection with selected files'
262 # now in the newly created collection page
263 # must have files in subdir1 and subdir3 but not subdir4
264 assert page.has_text?('file_in_subdir1'), 'file_in_subdir1 missing from new collection'
265 assert page.has_text?('file1_in_subdir3'), 'file1_in_subdir3 missing from new collection'
266 assert page.has_text?('file2_in_subdir3'), 'file2_in_subdir3 missing from new collection'
267 assert page.has_no_text?('file1_in_subdir4'), 'file1_in_subdir4 found in new collection'
268 assert page.has_no_text?('file2_in_subdir4'), 'file2_in_subdir4 found in new collection'
270 # Make sure we're not still on the old collection page.
271 refute_match(%r{/collections/#{col['uuid']}}, page.current_url)
274 test "remove a file from collection using checkbox and dropdown option" do
275 need_selenium 'to confirm unlock'
277 visit page_with_token('active', '/collections/zzzzz-4zz18-a21ux3541sxa8sf')
278 assert(page.has_text?('file1'), 'file not found - file1')
283 input_files = page.all('input[type=checkbox]')
286 click_button 'Selection...'
287 within('.selection-action-container') do
288 click_link 'Remove selected files'
291 assert(page.has_no_text?('file1'), 'file found - file')
292 assert(page.has_text?('file2'), 'file not found - file2')
295 test "remove a file in collection using trash icon" do
296 need_selenium 'to confirm unlock'
298 visit page_with_token('active', '/collections/zzzzz-4zz18-a21ux3541sxa8sf')
299 assert(page.has_text?('file1'), 'file not found - file1')
303 first('.fa-trash-o').click
306 assert(page.has_no_text?('file1'), 'file found - file')
307 assert(page.has_text?('file2'), 'file not found - file2')
310 test "rename a file in collection" do
311 need_selenium 'to confirm unlock'
313 visit page_with_token('active', '/collections/zzzzz-4zz18-a21ux3541sxa8sf')
317 within('.collection_files') do
318 first('.fa-pencil').click
319 find('.editable-input input').set('file1renamed')
320 find('.editable-submit').click
323 assert(page.has_text?('file1renamed'), 'file not found - file1renamed')
326 test "remove/rename file options not presented if user cannot update a collection" do
327 # visit a publicly accessible collection as 'spectator'
328 visit page_with_token('spectator', '/collections/zzzzz-4zz18-uukreo9rbgwsujr')
330 click_button 'Selection'
331 within('.selection-action-container') do
332 assert_selector 'li', text: 'Create new collection with selected files'
333 assert_no_selector 'li', text: 'Remove selected files'
336 within('.collection_files') do
337 assert(page.has_text?('GNU_General_Public_License'), 'file not found - GNU_General_Public_License')
338 assert_nil first('.fa-pencil')
339 assert_nil first('.fa-trash-o')
343 test "unlock collection to modify files" do
344 need_selenium 'to confirm remove'
346 collection = api_fixture('collections')['collection_owned_by_active']
348 # On load, collection is locked, and upload tab, rename and remove options are disabled
349 visit page_with_token('active', "/collections/#{collection['uuid']}")
351 assert_selector 'a[data-toggle="disabled"]', text: 'Upload'
353 within('.collection_files') do
354 file_ctrls = page.all('.btn-collection-file-control')
355 assert_equal 2, file_ctrls.size
356 assert_equal true, file_ctrls[0]['class'].include?('disabled')
357 assert_equal true, file_ctrls[1]['class'].include?('disabled')
358 find('input[type=checkbox]').click
361 click_button 'Selection'
362 within('.selection-action-container') do
363 assert_selector 'li.disabled', text: 'Remove selected files'
364 assert_selector 'li', text: 'Create new collection with selected files'
369 assert_no_selector 'a[data-toggle="disabled"]', text: 'Upload'
370 assert_selector 'a', text: 'Upload'
372 within('.collection_files') do
373 file_ctrls = page.all('.btn-collection-file-control')
374 assert_equal 2, file_ctrls.size
375 assert_equal false, file_ctrls[0]['class'].include?('disabled')
376 assert_equal false, file_ctrls[1]['class'].include?('disabled')
378 # previous checkbox selection won't result in firing a new event;
379 # undo and redo checkbox to fire the selection event again
380 find('input[type=checkbox]').click
381 find('input[type=checkbox]').click
384 click_button 'Selection'
385 within('.selection-action-container') do
386 assert_no_selector 'li.disabled', text: 'Remove selected files'
387 assert_selector 'li', text: 'Remove selected files'
391 def unlock_collection
392 first('.lock-collection-btn').click
396 test "collection tags tab" do
397 visit page_with_token('active', '/collections/zzzzz-4zz18-bv31uwvy3neko21')
402 # verify initial state
403 assert_selector 'a', text: 'Edit'
404 assert_no_selector 'a', text: 'Add new tag'
405 assert_no_selector 'a', text: 'Save'
406 assert_no_selector 'a', text: 'Cancel'
408 # Verify controls in edit mode
409 first('.edit-collection-tags').click
410 assert_selector 'a.disabled', text: 'Edit'
411 assert_selector 'a', text: 'Add new tag'
412 assert_selector 'a', text: 'Save'
413 assert_selector 'a', text: 'Cancel'
416 first('.glyphicon-plus').click
417 first('.collection-tag-field-key').click
418 first('.collection-tag-field-key').set('key 1')
419 first('.collection-tag-field-value').click
420 first('.collection-tag-field-value').set('value 1')
422 first('.glyphicon-plus').click
423 editable_key_fields = page.all('.collection-tag-field-key')
424 editable_key_fields[1].click
425 editable_key_fields[1].set('key 2')
426 editable_val_fields = page.all('.collection-tag-field-value')
427 editable_val_fields[1].click
428 editable_val_fields[1].set('value 2')
435 assert_text 'value 1'
437 assert_text 'value 2'
438 assert_selector 'a', text: 'Edit'
439 assert_no_selector 'a', text: 'Save'
442 first('.edit-collection-tags').click
443 assert_not_nil first('.glyphicon-remove')
444 first('.glyphicon-remove').click
449 assert_text 'value 2'
450 assert_no_text 'key 1'
451 assert_no_text 'value 1'
452 assert_selector 'a', text: 'Edit'
454 # Click on cancel and verify
455 first('.edit-collection-tags').click
456 first('.collection-tag-field-key').click
457 first('.collection-tag-field-key').set('this key wont stick')
458 first('.collection-tag-field-value').click
459 first('.collection-tag-field-value').set('this value wont stick')
465 assert_text 'value 2'
466 assert_no_text 'this key wont stick'
467 assert_no_text 'this value wont stick'
470 first('.edit-collection-tags').click
471 first('.glyphicon-remove').click
475 assert_selector 'a', text: 'Edit'
476 assert_no_text 'key 2'
477 assert_no_text 'value 2'