Merge branch '3198-inode-cache' refs #3198
[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   test "Collection page renders name" do
24     Capybara.current_driver = :rack_test
25     uuid = api_fixture('collections')['foo_file']['uuid']
26     coll_name = api_fixture('collections')['foo_file']['name']
27     visit page_with_token('active', "/collections/#{uuid}")
28     assert(page.has_text?(coll_name), "Collection page did not include name")
29     # Now check that the page is otherwise normal, and the collection name
30     # isn't only showing up in an error message.
31     assert(page.has_link?('foo'), "Collection page did not include file link")
32   end
33
34   def check_sharing(want_state, link_regexp)
35     # We specifically want to click buttons.  See #4291.
36     if want_state == :off
37       click_button "Unshare"
38       text_assertion = :assert_no_text
39       link_assertion = :assert_empty
40     else
41       click_button "Create sharing link"
42       text_assertion = :assert_text
43       link_assertion = :refute_empty
44     end
45     using_wait_time(Capybara.default_wait_time * 3) do
46       send(text_assertion, "Shared at:")
47     end
48     send(link_assertion, all("a").select { |a| a[:href] =~ link_regexp })
49   end
50
51   test "creating and uncreating a sharing link" do
52     coll_uuid = api_fixture("collections", "collection_owned_by_active", "uuid")
53     download_link_re =
54       Regexp.new(Regexp.escape("/collections/download/#{coll_uuid}/"))
55     visit page_with_token("active_trustedclient", "/collections/#{coll_uuid}")
56     within "#sharing-button" do
57       check_sharing(:on, download_link_re)
58       check_sharing(:off, download_link_re)
59     end
60   end
61
62   test "can download an entire collection with a reader token" do
63     Capybara.current_driver = :rack_test
64     CollectionsController.any_instance.
65       stubs(:file_enumerator).returns(["foo\n", "file\n"])
66     uuid = api_fixture('collections')['foo_file']['uuid']
67     token = api_fixture('api_client_authorizations')['active_all_collections']['api_token']
68     url_head = "/collections/download/#{uuid}/#{token}/"
69     visit url_head
70     # It seems that Capybara can't inspect tags outside the body, so this is
71     # a very blunt approach.
72     assert_no_match(/<\s*meta[^>]+\bnofollow\b/i, page.html,
73                     "wget prohibited from recursing the collection page")
74     # Look at all the links that wget would recurse through using our
75     # recommended options, and check that it's exactly the file list.
76     hrefs = page.all('a').map do |anchor|
77       link = anchor[:href] || ''
78       if link.start_with? url_head
79         link[url_head.size .. -1]
80       elsif link.start_with? '/'
81         nil
82       else
83         link
84       end
85     end
86     assert_equal(['foo'], hrefs.compact.sort,
87                  "download page did provide strictly file links")
88     within "#collection_files" do
89       click_link "foo"
90       assert_equal("foo\nfile\n", page.html)
91     end
92   end
93
94   test "can view empty collection" do
95     Capybara.current_driver = :rack_test
96     uuid = 'd41d8cd98f00b204e9800998ecf8427e+0'
97     visit page_with_token('active', "/collections/#{uuid}")
98     assert page.has_text?(/This collection is empty|The following collections have this content/)
99   end
100
101   test "combine selected collections into new collection" do
102     foo_collection = api_fixture('collections')['foo_file']
103     bar_collection = api_fixture('collections')['bar_file']
104
105     visit page_with_token('active', "/collections")
106
107     assert(page.has_text?(foo_collection['uuid']), "Collection page did not include foo file")
108     assert(page.has_text?(bar_collection['uuid']), "Collection page did not include bar file")
109
110     within('tr', text: foo_collection['uuid']) do
111       find('input[type=checkbox]').click
112     end
113
114     within('tr', text: bar_collection['uuid']) do
115       find('input[type=checkbox]').click
116     end
117
118     click_button 'Selection...'
119     within('.selection-action-container') do
120       click_link 'Create new collection with selected collections'
121     end
122
123     # now in the newly created collection page
124     assert(page.has_text?('Copy to project'), "Copy to project text not found in new collection page")
125     assert(page.has_no_text?(foo_collection['name']), "Collection page did not include foo file")
126     assert(page.has_text?('foo'), "Collection page did not include foo file")
127     assert(page.has_no_text?(bar_collection['name']), "Collection page did not include foo file")
128     assert(page.has_text?('bar'), "Collection page did not include bar file")
129     assert(page.has_text?('Created new collection in your Home project'),
130                           'Not found flash message that new collection is created in Home project')
131   end
132
133   [
134     ['active', 'foo_file', false],
135     ['active', 'foo_collection_in_aproject', true],
136     ['project_viewer', 'foo_file', false],
137     ['project_viewer', 'foo_collection_in_aproject', false], #aproject not writable
138   ].each do |user, collection, expect_collection_in_aproject|
139     test "combine selected collection files into new collection #{user} #{collection} #{expect_collection_in_aproject}" do
140       my_collection = api_fixture('collections')[collection]
141
142       visit page_with_token(user, "/collections")
143
144       # choose file from foo collection
145       within('tr', text: my_collection['uuid']) do
146         click_link 'Show'
147       end
148
149       # now in collection page
150       find('input[type=checkbox]').click
151
152       click_button 'Selection...'
153       within('.selection-action-container') do
154         click_link 'Create new collection with selected files'
155       end
156
157       # now in the newly created collection page
158       assert(page.has_text?('Copy to project'), "Copy to project text not found in new collection page")
159       assert(page.has_no_text?(my_collection['name']), "Collection page did not include foo file")
160       assert(page.has_text?('foo'), "Collection page did not include foo file")
161       if expect_collection_in_aproject
162         aproject = api_fixture('groups')['aproject']
163         assert page.has_text?("Created new collection in the project #{aproject['name']}"),
164                               'Not found flash message that new collection is created in aproject'
165       else
166         assert page.has_text?("Created new collection in your Home project"),
167                               'Not found flash message that new collection is created in Home project'
168       end
169     end
170   end
171
172   test "combine selected collection files from collection subdirectory" do
173     visit page_with_token('user1_with_load', "/collections/zzzzz-4zz18-filesinsubdir00")
174
175     # now in collection page
176     input_files = page.all('input[type=checkbox]')
177     (0..input_files.count-1).each do |i|
178       input_files[i].click
179     end
180
181     click_button 'Selection...'
182     within('.selection-action-container') do
183       click_link 'Create new collection with selected files'
184     end
185
186     # now in the newly created collection page
187     assert(page.has_text?('file_in_subdir1'), 'file not found - file_in_subdir1')
188     assert(page.has_text?('file1_in_subdir3.txt'), 'file not found - file1_in_subdir3.txt')
189     assert(page.has_text?('file2_in_subdir3.txt'), 'file not found - file2_in_subdir3.txt')
190     assert(page.has_text?('file1_in_subdir4.txt'), 'file not found - file1_in_subdir4.txt')
191     assert(page.has_text?('file2_in_subdir4.txt'), 'file not found - file1_in_subdir4.txt')
192   end
193
194   test "Collection portable data hash redirect" do
195     di = api_fixture('collections')['docker_image']
196     visit page_with_token('active', "/collections/#{di['portable_data_hash']}")
197
198     # check redirection
199     assert current_path.end_with?("/collections/#{di['uuid']}")
200     assert page.has_text?("docker_image")
201     assert page.has_text?("Activity")
202     assert page.has_text?("Sharing and permissions")
203   end
204
205   test "Collection portable data hash with multiple matches" do
206     pdh = api_fixture('collections')['foo_file']['portable_data_hash']
207     visit page_with_token('admin', "/collections/#{pdh}")
208
209     matches = api_fixture('collections').select {|k,v| v["portable_data_hash"] == pdh}
210     assert matches.size > 1
211
212     matches.each do |k,v|
213       assert page.has_link?(v["name"]), "Page /collections/#{pdh} should contain link '#{v['name']}'"
214     end
215     assert_text 'The following collections have this content:'
216     assert_no_text 'more results are not shown'
217     assert_no_text 'Activity'
218     assert_no_text 'Sharing and permissions'
219   end
220
221   test "Collection portable data hash with multiple matches with more than one page of results" do
222     pdh = api_fixture('collections')['baz_file']['portable_data_hash']
223     visit page_with_token('admin', "/collections/#{pdh}")
224
225     assert_selector 'a', text: 'Collection_1'
226
227     assert_text 'The following collections have this content:'
228     assert_text 'more results are not shown'
229     assert_no_text 'Activity'
230     assert_no_text 'Sharing and permissions'
231   end
232
233   test "Filtering collection files by regexp" do
234     col = api_fixture('collections', 'multilevel_collection_1')
235     visit page_with_token('active', "/collections/#{col['uuid']}")
236
237     # Filter file list to some but not all files in the collection
238     page.find_field('file_regex').set('file[12]')
239     assert page.has_text?("file1")
240     assert page.has_text?("file2")
241     assert page.has_no_text?("file3")
242
243     # Filter file list with a regex matching all files
244     page.find_field('file_regex').set('.*')
245     assert page.has_text?("file1")
246     assert page.has_text?("file2")
247     assert page.has_text?("file3")
248
249     # Filter file list to a regex matching no files
250     page.find_field('file_regex').set('file9')
251     assert page.has_no_text?("file1")
252     assert page.has_no_text?("file2")
253     assert page.has_no_text?("file3")
254     # make sure that we actually are looking at the collections
255     # page and not e.g. a fiddlesticks
256     assert page.has_text?("multilevel_collection_1")
257     assert page.has_text?(col["name"] || col["uuid"])
258
259     # Set filename filter to a syntactically invalid regex
260     # Page loads, but stops filtering after the last valid regex parse
261     page.find_field('file_regex').set('file[2')
262     assert page.has_text?("multilevel_collection_1")
263     assert page.has_text?(col["name"] || col["uuid"])
264     assert page.has_text?("file1")
265     assert page.has_text?("file2")
266     assert page.has_text?("file3")
267
268     # Test the "Select all" button
269
270     # Note: calling .set('') on a Selenium element is not sufficient
271     # to reset the field for this test, as it does not send any key
272     # events to the browser. To clear the field, we must instead send
273     # a backspace character.
274     # See https://selenium.googlecode.com/svn/trunk/docs/api/rb/Selenium/WebDriver/Element.html#clear-instance_method
275     page.find_field('file_regex').set("\b") # backspace
276     find('button#select-all').click
277     assert_checkboxes_state('input[type=checkbox]', true, '"select all" should check all checkboxes')
278
279     # Test the "Unselect all" button
280     page.find_field('file_regex').set("\b") # backspace
281     find('button#unselect-all').click
282     assert_checkboxes_state('input[type=checkbox]', false, '"unselect all" should clear all checkboxes')
283
284     # Filter files, then "select all", then unfilter
285     page.find_field('file_regex').set("\b") # backspace
286     find('button#unselect-all').click
287     page.find_field('file_regex').set('file[12]')
288     find('button#select-all').click
289     page.find_field('file_regex').set("\b") # backspace
290
291     # all "file1" and "file2" checkboxes must be selected
292     # all "file3" checkboxes must be clear
293     assert_checkboxes_state('[value*="file1"]', true, 'checkboxes for file1 should be selected after filtering')
294     assert_checkboxes_state('[value*="file2"]', true, 'checkboxes for file2 should be selected after filtering')
295     assert_checkboxes_state('[value*="file3"]', false, 'checkboxes for file3 should be clear after filtering')
296
297     # Select all files, then filter, then "unselect all", then unfilter
298     page.find_field('file_regex').set("\b") # backspace
299     find('button#select-all').click
300     page.find_field('file_regex').set('file[12]')
301     find('button#unselect-all').click
302     page.find_field('file_regex').set("\b") # backspace
303
304     # all "file1" and "file2" checkboxes must be clear
305     # all "file3" checkboxes must be selected
306     assert_checkboxes_state('[value*="file1"]', false, 'checkboxes for file1 should be clear after filtering')
307     assert_checkboxes_state('[value*="file2"]', false, 'checkboxes for file2 should be clear after filtering')
308     assert_checkboxes_state('[value*="file3"]', true, 'checkboxes for file3 should be selected after filtering')
309   end
310
311   test "Creating collection from list of filtered files" do
312     col = api_fixture('collections', 'collection_with_files_in_subdir')
313     visit page_with_token('user1_with_load', "/collections/#{col['uuid']}")
314     assert page.has_text?('file_in_subdir1'), 'expected file_in_subdir1 not found'
315     assert page.has_text?('file1_in_subdir3'), 'expected file1_in_subdir3 not found'
316     assert page.has_text?('file2_in_subdir3'), 'expected file2_in_subdir3 not found'
317     assert page.has_text?('file1_in_subdir4'), 'expected file1_in_subdir4 not found'
318     assert page.has_text?('file2_in_subdir4'), 'expected file2_in_subdir4 not found'
319
320     # Select all files but then filter them to files in subdir1, subdir2 or subdir3
321     find('button#select-all').click
322     page.find_field('file_regex').set('_in_subdir[123]')
323     assert page.has_text?('file_in_subdir1'), 'expected file_in_subdir1 not in filtered files'
324     assert page.has_text?('file1_in_subdir3'), 'expected file1_in_subdir3 not in filtered files'
325     assert page.has_text?('file2_in_subdir3'), 'expected file2_in_subdir3 not in filtered files'
326     assert page.has_no_text?('file1_in_subdir4'), 'file1_in_subdir4 found in filtered files'
327     assert page.has_no_text?('file2_in_subdir4'), 'file2_in_subdir4 found in filtered files'
328
329     # Create a new collection
330     click_button 'Selection...'
331     within('.selection-action-container') do
332       click_link 'Create new collection with selected files'
333     end
334
335     # now in the newly created collection page
336     # must have files in subdir1 and subdir3 but not subdir4
337     assert page.has_text?('file_in_subdir1'), 'file_in_subdir1 missing from new collection'
338     assert page.has_text?('file1_in_subdir3'), 'file1_in_subdir3 missing from new collection'
339     assert page.has_text?('file2_in_subdir3'), 'file2_in_subdir3 missing from new collection'
340     assert page.has_no_text?('file1_in_subdir4'), 'file1_in_subdir4 found in new collection'
341     assert page.has_no_text?('file2_in_subdir4'), 'file2_in_subdir4 found in new collection'
342
343     # Make sure we're not still on the old collection page.
344     refute_match(%r{/collections/#{col['uuid']}}, page.current_url)
345   end
346 end