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