Merge branch 'master' into 4644-collection-file-filtering
[arvados.git] / apps / workbench / test / integration / collections_test.rb
1 require 'integration_helper'
2 require 'selenium-webdriver'
3 require 'headless'
4
5 class CollectionsTest < ActionDispatch::IntegrationTest
6   setup do
7     Capybara.current_driver = :rack_test
8   end
9
10   # check_checkboxes_state asserts that the page holds at least one
11   # checkbox matching 'selector', and that all matching checkboxes
12   # are in state 'checkbox_status' (i.e. checked if true, unchecked otherwise)
13   def assert_checkboxes_state(selector, checkbox_status, msg=nil)
14     assert page.has_selector?(selector)
15     page.all(selector).each do |checkbox|
16       assert(checkbox.checked? == checkbox_status, msg)
17     end
18   end
19
20   test "Can copy a collection to a project" do
21     Capybara.current_driver = Capybara.javascript_driver
22
23     collection_uuid = api_fixture('collections')['foo_file']['uuid']
24     collection_name = api_fixture('collections')['foo_file']['name']
25     project_uuid = api_fixture('groups')['aproject']['uuid']
26     project_name = api_fixture('groups')['aproject']['name']
27     visit page_with_token('active', "/collections/#{collection_uuid}")
28     click_link 'Copy to project...'
29     find('.selectable', text: project_name).click
30     find('.modal-footer a,button', text: 'Copy').click
31     wait_for_ajax
32     # It should navigate to the project after copying...
33     assert(page.has_text?(project_name))
34     assert(page.has_text?("Copy of #{collection_name}"))
35   end
36
37   test "Collection page renders name" do
38     uuid = api_fixture('collections')['foo_file']['uuid']
39     coll_name = api_fixture('collections')['foo_file']['name']
40     visit page_with_token('active', "/collections/#{uuid}")
41     assert(page.has_text?(coll_name), "Collection page did not include name")
42     # Now check that the page is otherwise normal, and the collection name
43     # isn't only showing up in an error message.
44     assert(page.has_link?('foo'), "Collection page did not include file link")
45   end
46
47   test "can download an entire collection with a reader token" do
48     CollectionsController.any_instance.
49       stubs(:file_enumerator).returns(["foo\n", "file\n"])
50     uuid = api_fixture('collections')['foo_file']['uuid']
51     token = api_fixture('api_client_authorizations')['active_all_collections']['api_token']
52     url_head = "/collections/download/#{uuid}/#{token}/"
53     visit url_head
54     # It seems that Capybara can't inspect tags outside the body, so this is
55     # a very blunt approach.
56     assert_no_match(/<\s*meta[^>]+\bnofollow\b/i, page.html,
57                     "wget prohibited from recursing the collection page")
58     # Look at all the links that wget would recurse through using our
59     # recommended options, and check that it's exactly the file list.
60     hrefs = page.all('a').map do |anchor|
61       link = anchor[:href] || ''
62       if link.start_with? url_head
63         link[url_head.size .. -1]
64       elsif link.start_with? '/'
65         nil
66       else
67         link
68       end
69     end
70     assert_equal(['foo'], hrefs.compact.sort,
71                  "download page did provide strictly file links")
72     within "#collection_files" do
73       click_link "foo"
74       assert_equal("foo\nfile\n", page.html)
75     end
76   end
77
78   test "can view empty collection" do
79     uuid = 'd41d8cd98f00b204e9800998ecf8427e+0'
80     visit page_with_token('active', "/collections/#{uuid}")
81     assert page.has_text?('This collection is empty')
82   end
83
84   test "combine selected collections into new collection" do
85     headless = Headless.new
86     headless.start
87     Capybara.current_driver = :selenium
88
89     foo_collection = api_fixture('collections')['foo_file']
90     bar_collection = api_fixture('collections')['bar_file']
91
92     visit page_with_token('active', "/collections")
93
94     assert(page.has_text?(foo_collection['uuid']), "Collection page did not include foo file")
95     assert(page.has_text?(bar_collection['uuid']), "Collection page did not include bar file")
96
97     within('tr', text: foo_collection['uuid']) do
98       find('input[type=checkbox]').click
99     end
100
101     within('tr', text: bar_collection['uuid']) do
102       find('input[type=checkbox]').click
103     end
104
105     click_button 'Selection...'
106     within('.selection-action-container') do
107       click_link 'Create new collection with selected collections'
108     end
109
110     # now in the newly created collection page
111     assert(page.has_text?('Copy to project'), "Copy to project text not found in new collection page")
112     assert(page.has_no_text?(foo_collection['name']), "Collection page did not include foo file")
113     assert(page.has_text?('foo'), "Collection page did not include foo file")
114     assert(page.has_no_text?(bar_collection['name']), "Collection page did not include foo file")
115     assert(page.has_text?('bar'), "Collection page did not include bar file")
116     assert(page.has_text?('Created new collection in your Home project'),
117                           'Not found flash message that new collection is created in Home project')
118     headless.stop
119   end
120
121   [
122     ['active', 'foo_file', false],
123     ['active', 'foo_collection_in_aproject', true],
124     ['project_viewer', 'foo_file', false],
125     ['project_viewer', 'foo_collection_in_aproject', false], #aproject not writable
126   ].each do |user, collection, expect_collection_in_aproject|
127     test "combine selected collection files into new collection #{user} #{collection} #{expect_collection_in_aproject}" do
128       headless = Headless.new
129       headless.start
130       Capybara.current_driver = :selenium
131
132       my_collection = api_fixture('collections')[collection]
133
134       visit page_with_token(user, "/collections")
135
136       # choose file from foo collection
137       within('tr', text: my_collection['uuid']) do
138         click_link 'Show'
139       end
140
141       # now in collection page
142       find('input[type=checkbox]').click
143
144       click_button 'Selection...'
145       within('.selection-action-container') do
146         click_link 'Create new collection with selected files'
147       end
148
149       # now in the newly created collection page
150       assert(page.has_text?('Copy to project'), "Copy to project text not found in new collection page")
151       assert(page.has_no_text?(my_collection['name']), "Collection page did not include foo file")
152       assert(page.has_text?('foo'), "Collection page did not include foo file")
153       if expect_collection_in_aproject
154         aproject = api_fixture('groups')['aproject']
155         assert page.has_text?("Created new collection in the project #{aproject['name']}"),
156                               'Not found flash message that new collection is created in aproject'
157       else
158         assert page.has_text?("Created new collection in your Home project"),
159                               'Not found flash message that new collection is created in Home project'
160       end
161
162       headless.stop
163     end
164   end
165
166   test "combine selected collection files from collection subdirectory" do
167     headless = Headless.new
168     headless.start
169     Capybara.current_driver = :selenium
170
171     visit page_with_token('user1_with_load', "/collections/zzzzz-4zz18-filesinsubdir00")
172
173     # now in collection page
174     input_files = page.all('input[type=checkbox]')
175     (0..input_files.count-1).each do |i|
176       input_files[i].click
177     end
178
179     click_button 'Selection...'
180     within('.selection-action-container') do
181       click_link 'Create new collection with selected files'
182     end
183
184     # now in the newly created collection page
185     assert(page.has_text?('file_in_subdir1'), 'file not found - file_in_subdir1')
186     assert(page.has_text?('file1_in_subdir3.txt'), 'file not found - file1_in_subdir3.txt')
187     assert(page.has_text?('file2_in_subdir3.txt'), 'file not found - file2_in_subdir3.txt')
188     assert(page.has_text?('file1_in_subdir4.txt'), 'file not found - file1_in_subdir4.txt')
189     assert(page.has_text?('file2_in_subdir4.txt'), 'file not found - file1_in_subdir4.txt')
190
191     headless.stop
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')['baz_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 page.has_no_text?("Activity")
216     assert page.has_no_text?("Sharing and permissions")
217   end
218
219   test "Filtering collection files by regexp" do
220     headless = Headless.new
221     headless.start
222     Capybara.current_driver = :selenium
223     col = api_fixture('collections', 'multilevel_collection_1')
224     visit page_with_token('active', "/collections/#{col['uuid']}")
225
226     # Filter file list to some but not all files in the collection
227     page.find_field('file_regex').set('file[12]')
228     assert page.has_text?("file1")
229     assert page.has_text?("file2")
230     assert page.has_no_text?("file3")
231
232     # Filter file list with a regex matching all files
233     page.find_field('file_regex').set('.*')
234     assert page.has_text?("file1")
235     assert page.has_text?("file2")
236     assert page.has_text?("file3")
237
238     # Filter file list to a regex matching no files
239     page.find_field('file_regex').set('file9')
240     assert page.has_no_text?("file1")
241     assert page.has_no_text?("file2")
242     assert page.has_no_text?("file3")
243     # make sure that we actually are looking at the collections
244     # page and not e.g. a fiddlesticks
245     assert page.has_text?("multilevel_collection_1")
246     assert page.has_text?(col['portable_data_hash'])
247
248     # Set filename filter to a syntactically invalid regex
249     # Page loads, but stops filtering after the last valid regex parse
250     page.find_field('file_regex').set('file[2')
251     assert page.has_text?("multilevel_collection_1")
252     assert page.has_text?(col['portable_data_hash'])
253     assert page.has_text?("file1")
254     assert page.has_text?("file2")
255     assert page.has_text?("file3")
256
257     # Test the "Select all" button
258
259     # Note: calling .set('') on a Selenium element is not sufficient
260     # to reset the field for this test, as it does not send any key
261     # events to the browser. To clear the field, we must instead send
262     # a backspace character.
263     # See https://selenium.googlecode.com/svn/trunk/docs/api/rb/Selenium/WebDriver/Element.html#clear-instance_method
264     page.find_field('file_regex').set("\b") # backspace
265     find('button#select-all').click
266     assert_checkboxes_state('input[type=checkbox]', true, '"select all" should check all checkboxes')
267
268     # Test the "Unselect all" button
269     page.find_field('file_regex').set("\b") # backspace
270     find('button#unselect-all').click
271     assert_checkboxes_state('input[type=checkbox]', false, '"unselect all" should clear all checkboxes')
272
273     # Filter files, then "select all", then unfilter
274     page.find_field('file_regex').set("\b") # backspace
275     find('button#unselect-all').click
276     page.find_field('file_regex').set('file[12]')
277     find('button#select-all').click
278     page.find_field('file_regex').set("\b") # backspace
279
280     # all "file1" and "file2" checkboxes must be selected
281     # all "file3" checkboxes must be clear
282     assert_checkboxes_state('[value*="file1"]', true, 'checkboxes for file1 should be selected after filtering')
283     assert_checkboxes_state('[value*="file2"]', true, 'checkboxes for file2 should be selected after filtering')
284     assert_checkboxes_state('[value*="file3"]', false, 'checkboxes for file3 should be clear after filtering')
285  
286     # Select all files, then filter, then "unselect all", then unfilter
287     page.find_field('file_regex').set("\b") # backspace
288     find('button#select-all').click
289     page.find_field('file_regex').set('file[12]')
290     find('button#unselect-all').click
291     page.find_field('file_regex').set("\b") # backspace
292
293     # all "file1" and "file2" checkboxes must be clear
294     # all "file3" checkboxes must be selected
295     assert_checkboxes_state('[value*="file1"]', false, 'checkboxes for file1 should be clear after filtering')
296     assert_checkboxes_state('[value*="file2"]', false, 'checkboxes for file2 should be clear after filtering')
297     assert_checkboxes_state('[value*="file3"]', true, 'checkboxes for file3 should be selected after filtering')
298   end
299
300   test "Creating collection from list of filtered files" do
301     headless = Headless.new
302     headless.start
303     Capybara.current_driver = :selenium
304
305     col = api_fixture('collections', 'collection_with_files_in_subdir')
306     visit page_with_token('user1_with_load', "/collections/#{col['uuid']}")
307     assert page.has_text?('file_in_subdir1'), 'expected file_in_subdir1 not found'
308     assert page.has_text?('file1_in_subdir3'), 'expected file1_in_subdir3 not found'
309     assert page.has_text?('file2_in_subdir3'), 'expected file2_in_subdir3 not found'
310     assert page.has_text?('file1_in_subdir4'), 'expected file1_in_subdir4 not found'
311     assert page.has_text?('file2_in_subdir4'), 'expected file2_in_subdir4 not found'
312
313     # Select all files but then filter them to files in subdir1, subdir2 or subdir3
314     find('button#select-all').click
315     page.find_field('file_regex').set('_in_subdir[123]')
316     assert page.has_text?('file_in_subdir1'), 'expected file_in_subdir1 not in filtered files'
317     assert page.has_text?('file1_in_subdir3'), 'expected file1_in_subdir3 not in filtered files'
318     assert page.has_text?('file2_in_subdir3'), 'expected file2_in_subdir3 not in filtered files'
319     assert page.has_no_text?('file1_in_subdir4'), 'file1_in_subdir4 found in filtered files'
320     assert page.has_no_text?('file2_in_subdir4'), 'file2_in_subdir4 found in filtered files'
321
322     # Create a new collection
323     click_button 'Selection...'
324     within('.selection-action-container') do
325       click_link 'Create new collection with selected files'
326     end
327
328     # now in the newly created collection page
329     assert page.has_text?('Content hash:'), 'not on new collection page'
330     assert page.has_no_text?(col['uuid']), 'new collection page has old collection uuid'
331     assert page.has_no_text?(col['portable_data_hash']), 'new collection page has old portable_data_hash'
332
333     # must have files in subdir1 and subdir3 but not subdir4
334     assert page.has_text?('file_in_subdir1'), 'file_in_subdir1 missing from new collection'
335     assert page.has_text?('file1_in_subdir3'), 'file1_in_subdir3 missing from new collection'
336     assert page.has_text?('file2_in_subdir3'), 'file2_in_subdir3 missing from new collection'
337     assert page.has_no_text?('file1_in_subdir4'), 'file1_in_subdir4 found in new collection'
338     assert page.has_no_text?('file2_in_subdir4'), 'file2_in_subdir4 found in new collection'
339   end
340 end