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