Merge branch '5261-project-description' closes #5261
[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')['baz_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 page.has_no_text?("Activity")
225     assert page.has_no_text?("Sharing and permissions")
226   end
227
228   test "Filtering collection files by regexp" do
229     col = api_fixture('collections', 'multilevel_collection_1')
230     visit page_with_token('active', "/collections/#{col['uuid']}")
231
232     # Filter file list to some but not all files in the collection
233     page.find_field('file_regex').set('file[12]')
234     assert page.has_text?("file1")
235     assert page.has_text?("file2")
236     assert page.has_no_text?("file3")
237
238     # Filter file list with a regex matching all files
239     page.find_field('file_regex').set('.*')
240     assert page.has_text?("file1")
241     assert page.has_text?("file2")
242     assert page.has_text?("file3")
243
244     # Filter file list to a regex matching no files
245     page.find_field('file_regex').set('file9')
246     assert page.has_no_text?("file1")
247     assert page.has_no_text?("file2")
248     assert page.has_no_text?("file3")
249     # make sure that we actually are looking at the collections
250     # page and not e.g. a fiddlesticks
251     assert page.has_text?("multilevel_collection_1")
252     assert page.has_text?(col['portable_data_hash'])
253
254     # Set filename filter to a syntactically invalid regex
255     # Page loads, but stops filtering after the last valid regex parse
256     page.find_field('file_regex').set('file[2')
257     assert page.has_text?("multilevel_collection_1")
258     assert page.has_text?(col['portable_data_hash'])
259     assert page.has_text?("file1")
260     assert page.has_text?("file2")
261     assert page.has_text?("file3")
262
263     # Test the "Select all" button
264
265     # Note: calling .set('') on a Selenium element is not sufficient
266     # to reset the field for this test, as it does not send any key
267     # events to the browser. To clear the field, we must instead send
268     # a backspace character.
269     # See https://selenium.googlecode.com/svn/trunk/docs/api/rb/Selenium/WebDriver/Element.html#clear-instance_method
270     page.find_field('file_regex').set("\b") # backspace
271     find('button#select-all').click
272     assert_checkboxes_state('input[type=checkbox]', true, '"select all" should check all checkboxes')
273
274     # Test the "Unselect all" button
275     page.find_field('file_regex').set("\b") # backspace
276     find('button#unselect-all').click
277     assert_checkboxes_state('input[type=checkbox]', false, '"unselect all" should clear all checkboxes')
278
279     # Filter files, then "select all", then unfilter
280     page.find_field('file_regex').set("\b") # backspace
281     find('button#unselect-all').click
282     page.find_field('file_regex').set('file[12]')
283     find('button#select-all').click
284     page.find_field('file_regex').set("\b") # backspace
285
286     # all "file1" and "file2" checkboxes must be selected
287     # all "file3" checkboxes must be clear
288     assert_checkboxes_state('[value*="file1"]', true, 'checkboxes for file1 should be selected after filtering')
289     assert_checkboxes_state('[value*="file2"]', true, 'checkboxes for file2 should be selected after filtering')
290     assert_checkboxes_state('[value*="file3"]', false, 'checkboxes for file3 should be clear after filtering')
291
292     # Select all files, then filter, then "unselect all", then unfilter
293     page.find_field('file_regex').set("\b") # backspace
294     find('button#select-all').click
295     page.find_field('file_regex').set('file[12]')
296     find('button#unselect-all').click
297     page.find_field('file_regex').set("\b") # backspace
298
299     # all "file1" and "file2" checkboxes must be clear
300     # all "file3" checkboxes must be selected
301     assert_checkboxes_state('[value*="file1"]', false, 'checkboxes for file1 should be clear after filtering')
302     assert_checkboxes_state('[value*="file2"]', false, 'checkboxes for file2 should be clear after filtering')
303     assert_checkboxes_state('[value*="file3"]', true, 'checkboxes for file3 should be selected after filtering')
304   end
305
306   test "Creating collection from list of filtered files" do
307     col = api_fixture('collections', 'collection_with_files_in_subdir')
308     visit page_with_token('user1_with_load', "/collections/#{col['uuid']}")
309     assert page.has_text?('file_in_subdir1'), 'expected file_in_subdir1 not found'
310     assert page.has_text?('file1_in_subdir3'), 'expected file1_in_subdir3 not found'
311     assert page.has_text?('file2_in_subdir3'), 'expected file2_in_subdir3 not found'
312     assert page.has_text?('file1_in_subdir4'), 'expected file1_in_subdir4 not found'
313     assert page.has_text?('file2_in_subdir4'), 'expected file2_in_subdir4 not found'
314
315     # Select all files but then filter them to files in subdir1, subdir2 or subdir3
316     find('button#select-all').click
317     page.find_field('file_regex').set('_in_subdir[123]')
318     assert page.has_text?('file_in_subdir1'), 'expected file_in_subdir1 not in filtered files'
319     assert page.has_text?('file1_in_subdir3'), 'expected file1_in_subdir3 not in filtered files'
320     assert page.has_text?('file2_in_subdir3'), 'expected file2_in_subdir3 not in filtered files'
321     assert page.has_no_text?('file1_in_subdir4'), 'file1_in_subdir4 found in filtered files'
322     assert page.has_no_text?('file2_in_subdir4'), 'file2_in_subdir4 found in filtered files'
323
324     # Create a new collection
325     click_button 'Selection...'
326     within('.selection-action-container') do
327       click_link 'Create new collection with selected files'
328     end
329
330     # now in the newly created collection page
331     assert page.has_text?('Content hash:'), 'not on new collection page'
332     assert page.has_no_text?(col['uuid']), 'new collection page has old collection uuid'
333     assert page.has_no_text?(col['portable_data_hash']), 'new collection page has old portable_data_hash'
334
335     # must have files in subdir1 and subdir3 but not subdir4
336     assert page.has_text?('file_in_subdir1'), 'file_in_subdir1 missing from new collection'
337     assert page.has_text?('file1_in_subdir3'), 'file1_in_subdir3 missing from new collection'
338     assert page.has_text?('file2_in_subdir3'), 'file2_in_subdir3 missing from new collection'
339     assert page.has_no_text?('file1_in_subdir4'), 'file1_in_subdir4 found in new collection'
340     assert page.has_no_text?('file2_in_subdir4'), 'file2_in_subdir4 found in new collection'
341   end
342 end