7824: Merge branch 'master' into 7824-arvls-arvput-collection-api-usage
[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     visit page_with_token('active', '/collections/zzzzz-4zz18-a21ux3541sxa8sf')
304     assert(page.has_text?('file1'), 'file not found - file1')
305
306     # remove first file
307     input_files = page.all('input[type=checkbox]')
308     input_files[0].click
309
310     click_button 'Selection...'
311     within('.selection-action-container') do
312       click_link 'Remove selected files'
313     end
314
315     assert(page.has_no_text?('file1'), 'file found - file')
316     assert(page.has_text?('file2'), 'file not found - file2')
317   end
318
319   test "remove a file in collection using trash icon" do
320     need_selenium 'to confirm remove'
321
322     visit page_with_token('active', '/collections/zzzzz-4zz18-a21ux3541sxa8sf')
323     assert(page.has_text?('file1'), 'file not found - file1')
324
325     first('.fa-trash-o').click
326     page.driver.browser.switch_to.alert.accept
327
328     assert(page.has_no_text?('file1'), 'file found - file')
329     assert(page.has_text?('file2'), 'file not found - file2')
330   end
331
332   test "rename a file in collection" do
333     visit page_with_token('active', '/collections/zzzzz-4zz18-a21ux3541sxa8sf')
334
335     within('.collection_files') do
336       first('.fa-pencil').click
337       find('.editable-input input').set('file1renamed')
338       find('.editable-submit').click
339     end
340
341     assert(page.has_text?('file1renamed'), 'file not found - file1renamed')
342   end
343
344   test "remove/rename file options not presented if user cannot update a collection" do
345     # visit a publicly accessible collection as 'spectator'
346     visit page_with_token('spectator', '/collections/zzzzz-4zz18-uukreo9rbgwsujr')
347
348     click_button 'Selection'
349     within('.selection-action-container') do
350       assert_selector 'li', text: 'Create new collection with selected files'
351       assert_no_selector 'li', text: 'Remove selected files'
352     end
353
354     within('.collection_files') do
355       assert(page.has_text?('GNU_General_Public_License'), 'file not found - GNU_General_Public_License')
356       assert_nil first('.fa-pencil')
357       assert_nil first('.fa-trash-o')
358     end
359   end
360 end