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