Calendar: add FT sprint
[tails/test.git] / features / step_definitions / browser.rb
blobe2128969afe4cce7928d7c1e7479b4966d0722d3
1 When /^I (?:try to )?start the Unsafe Browser(?: through the GNOME menu)?$/ do
2   step 'I start "Unsafe Browser" via GNOME Activities Overview'
3 end
5 When /^I successfully start the Unsafe Browser$/ do
6   step 'I start the Unsafe Browser'
7   step 'I see and accept the Unsafe Browser start verification'
8   step 'I see the "Starting the Unsafe Browser..." notification ' \
9        'after at most 60 seconds'
10   step 'the Unsafe Browser has started'
11 end
13 # This step works reliably only when there's no more than one tab:
14 # otherwise, browser.tabs.warnOnClose will block this with a
15 # "Quit and close tabs?" dialog.
16 When /^I close the (?:Tor|Unsafe) Browser$/ do
17   @screen.press('ctrl', 'q')
18 end
20 When(/^I kill the ((?:Tor|Unsafe) Browser)$/) do |browser|
21   info = xul_application_info(browser)
22   $vm.execute_successfully("pkill --full --exact '#{info[:cmd_regex]}'")
23   try_for(10) do
24     $vm.execute("pgrep --full --exact '#{info[:cmd_regex]}'").failure?
25   end
26 end
28 def tor_browser_application_info(defaults)
29   user = LIVE_USER
30   binary = $vm.execute_successfully(
31     'echo ${TBB_INSTALL}/firefox.real', libs: 'tor-browser'
32   ).stdout.chomp
33   cmd_regex = "#{binary} .* -profile " \
34               "/home/#{user}/\.tor-browser/profile\.default"
35   defaults.merge(
36     {
37       user:                        user,
38       cmd_regex:                   cmd_regex,
39       chroot:                      '',
40       new_tab_button_image:        'TorBrowserNewTabButton.png',
41       browser_reload_button_image: 'TorBrowserReloadButton.png',
42       browser_stop_button_image:   'TorBrowserStopButton.png',
43     }
44   )
45 end
47 def unsafe_browser_application_info(defaults)
48   user = 'clearnet'
49   binary = $vm.execute_successfully(
50     'echo ${TBB_INSTALL}/firefox.real', libs: 'tor-browser'
51   ).stdout.chomp
52   cmd_regex = "#{binary} .* " \
53               "-profile /home/#{user}/\.unsafe-browser/profile\.default"
54   defaults.merge(
55     {
56       user:                        user,
57       cmd_regex:                   cmd_regex,
58       chroot:                      '/var/lib/unsafe-browser/chroot',
59       new_tab_button_image:        'UnsafeBrowserNewTabButton.png',
60       browser_reload_button_image: 'UnsafeBrowserReloadButton.png',
61       browser_stop_button_image:   'UnsafeBrowserStopButton.png',
62     }
63   )
64 end
66 def tor_launcher_application_info(defaults)
67   user = 'tor-launcher'
68   # We do not enable AppArmor confinement for the Tor Launcher.
69   binary = $vm.execute_successfully(
70     'echo ${TBB_INSTALL}/firefox-unconfined', libs: 'tor-browser'
71   ).stdout.chomp
72   tor_launcher_install = $vm.execute_successfully(
73     'echo ${TOR_LAUNCHER_INSTALL}', libs: 'tor-browser'
74   ).stdout.chomp
75   cmd_regex = "#{binary}\s+-app #{tor_launcher_install}/application\.ini.*"
76   defaults.merge(
77     {
78       user:                        user,
79       cmd_regex:                   cmd_regex,
80       chroot:                      '',
81       new_tab_button_image:        nil,
82       browser_reload_button_image: nil,
83       browser_stop_button_image:   nil,
84       address_bar_images:          [],
85       # The standalone Tor Launcher uses fewer libs than the full
86       # browser.
87       unused_tbb_libs:             defaults[:unused_tbb_libs]
88         .concat(['libfreebl3.so', 'libfreeblpriv3.so',
89                  'libnssckbi.so', 'libsoftokn3.so',]),
90     }
91   )
92 end
94 def xul_application_info(application)
95   defaults = {
96     address_bar_images: ["BrowserAddressBar#{$language}.png",
97                          "BrowserAddressBar#{$language}Alt.png",],
98     unused_tbb_libs:    ['libnssdbm3.so', 'libmozavcodec.so', 'libmozavutil.so'],
99   }
100   case application
101   when 'Tor Browser'
102     tor_browser_application_info(defaults)
103   when 'Unsafe Browser'
104     unsafe_browser_application_info(defaults)
105   when 'Tor Launcher'
106     tor_launcher_application_info(defaults)
107   else
108     raise "Invalid browser or XUL application: #{application}"
109   end
112 When /^I open a new tab in the (.*)$/ do |browser|
113   info = xul_application_info(browser)
114   @screen.click(info[:new_tab_button_image])
115   @screen.wait_any(info[:address_bar_images], 10)
118 When /^I open the address "([^"]*)" in the (.*)$/ do |address, browser|
119   step "I open a new tab in the #{browser}"
120   info = xul_application_info(browser)
121   open_address = proc do
122     @screen.find_any(info[:address_bar_images])[:match].click
123     # This static here since we have no reliable visual indicators
124     # that we can watch to know when typing is "safe".
125     sleep 5
126     # The browser sometimes loses keypresses when suggestions are
127     # shown, which we work around by pasting the address from the
128     # clipboard, in one go.
129     $vm.set_clipboard(address)
130     @screen.press('ctrl', 'v')
131     @screen.press('Return')
132   end
133   recovery_on_failure = proc do
134     @screen.press('Escape')
135     @screen.wait_vanish(info[:browser_stop_button_image], 3)
136     open_address.call
137   end
138   retry_method = if browser == 'Tor Browser'
139                    method(:retry_tor)
140                  else
141                    proc { |p, &b| retry_action(10, recovery_proc: p, &b) }
142                  end
143   open_address.call
144   retry_method.call(recovery_on_failure) do
145     @screen.wait_vanish(info[:browser_stop_button_image], 120)
146     @screen.wait(info[:browser_reload_button_image], 120)
147   end
150 def page_has_loaded_in_the_tor_browser(page_titles)
151   page_titles = [page_titles] if page_titles.class == String
152   assert_equal(Array, page_titles.class)
153   if $language == 'German'
154     browser_name = 'Tor-Browser'
155     reload_action = 'Neu laden'
156   else
157     browser_name = 'Tor Browser'
158     reload_action = 'Reload'
159   end
160   try_for(180) do
161     # The 'Reload' button (graphically shown as a looping arrow)
162     # is only shown when a page has loaded, so once we see the
163     # expected title *and* this button has appeared, then we can be sure
164     # that the page has fully loaded.
165     @torbrowser.children(roleName: 'frame', showingOnly: true).any? do |frame|
166       page_titles
167         .map  { |page_title| "#{page_title} - #{browser_name}" }
168         .any? { |page_title| page_title == frame.name }
169     end &&
170       @torbrowser.child(reload_action, roleName:    'push button',
171                                        showingOnly: true)
172   end
175 # This step is limited to the Tor Browser due to #7502 since dogtail
176 # uses the same interface.
177 Then /^"([^"]+)" has loaded in the Tor Browser$/ do |title|
178   page_has_loaded_in_the_tor_browser(title)
181 Then /^the (.*) has no plugins installed$/ do |browser|
182   step "I open the address \"about:plugins\" in the #{browser}"
183   step 'I see "TorBrowserNoPlugins.png" after at most 30 seconds'
186 def xul_app_shared_lib_check(pid, chroot, expected_absent_tbb_libs = [])
187   absent_tbb_libs = []
188   unwanted_native_libs = []
189   tbb_libs = $vm.execute_successfully("ls -1 #{chroot}${TBB_INSTALL}/*.so",
190                                       libs: 'tor-browser').stdout.split
191   firefox_pmap_info = $vm.execute("pmap --show-path #{pid}").stdout
192   tbb_libs.each do |lib|
193     lib_name = File.basename lib
194     absent_tbb_libs << lib_name unless /\W#{lib}$/.match(firefox_pmap_info)
195     native_libs = $vm.execute_successfully(
196       "find /usr/lib /lib -name \"#{lib_name}\""
197     ).stdout.split
198     native_libs.each do |native_lib|
199       if /\W#{native_lib}$"/.match(firefox_pmap_info)
200         unwanted_native_libs << lib_name
201       end
202     end
203   end
204   absent_tbb_libs -= expected_absent_tbb_libs
205   assert(absent_tbb_libs.empty? && unwanted_native_libs.empty?,
206          'The loaded shared libraries for the firefox process are not the ' \
207          "way we expect them.\n" \
208          "Expected TBB libs that are absent: #{absent_tbb_libs}\n" \
209          "Native libs that we don't want: #{unwanted_native_libs}")
212 Then /^the (.*) uses all expected TBB shared libraries$/ do |application|
213   info = xul_application_info(application)
214   pid = $vm.execute_successfully(
215     "pgrep --uid #{info[:user]} --full --exact '#{info[:cmd_regex]}'"
216   ).stdout.chomp
217   assert_match(/\A\d+\z/, pid, "It seems like #{application} is not running")
218   xul_app_shared_lib_check(pid, info[:chroot], info[:unused_tbb_libs])
221 Then /^the (.*) chroot is torn down$/ do |browser|
222   info = xul_application_info(browser)
223   try_for(30, msg: "The #{browser} chroot '#{info[:chroot]}' was " \
224                       'not removed') do
225     !$vm.execute("test -d '#{info[:chroot]}'").success?
226   end
229 Then /^the (.*) runs as the expected user$/ do |browser|
230   info = xul_application_info(browser)
231   assert_vmcommand_success(
232     $vm.execute("pgrep --full --exact '#{info[:cmd_regex]}'"),
233     "The #{browser} is not running"
234   )
235   assert_vmcommand_success(
236     $vm.execute(
237       "pgrep --uid #{info[:user]} --full --exact '#{info[:cmd_regex]}'"
238     ),
239     "The #{browser} is not running as the #{info[:user]} user"
240   )
243 When /^I download some file in the Tor Browser$/ do
244   @some_file = 'tails-signing.key'
245   some_url = "https://tails.boum.org/#{@some_file}"
246   step "I open the address \"#{some_url}\" in the Tor Browser"
249 Then /^I get the browser download dialog$/ do
250   @screen.wait('BrowserDownloadDialog.png', 60)
251   @screen.wait('BrowserDownloadDialogSaveAsButton.png', 10)
254 When /^I save the file to the default Tor Browser download directory$/ do
255   @screen.click('BrowserDownloadDialogSaveAsButton.png')
256   @screen.wait('Gtk3SaveFileDialog.png', 10)
257   @screen.press('Return')
260 Then /^the file is saved to the default Tor Browser download directory$/ do
261   assert_not_nil(@some_file)
262   expected_path = "/home/#{LIVE_USER}/Tor Browser/#{@some_file}"
263   try_for(10) { $vm.file_exist?(expected_path) }
266 When /^I open the Tails homepage in the (.+)$/ do |browser|
267   step "I open the address \"https://tails.boum.org\" in the #{browser}"
270 Then /^the Tails homepage loads in the Unsafe Browser$/ do
271   @screen.wait('TailsHomepage.png', 60)
274 def headings_in_page(page_title)
275   @torbrowser.child(page_title, roleName: 'frame').children(roleName: 'heading')
278 def page_has_heading(page_title, heading)
279   headings_in_page(page_title).any? { |h| h.text == heading }
282 Then /^the Tor Browser shows the "([^"]+)" error$/ do |error|
283   try_for(60) do
284     page_has_heading('Problem loading page - Tor Browser', error)
285   end
288 Then /^Tor Browser displays a "([^"]+)" heading on the "([^"]+)" page$/ do |heading, page_title|
289   try_for(60) do
290     page_has_heading("#{page_title} - Tor Browser", heading)
291   end
294 Then /^Tor Browser displays a '([^']+)' heading on the "([^"]+)" page$/ do |heading, page_title|
295   try_for(60) do
296     page_has_heading("#{page_title} - Tor Browser", heading)
297   end
300 Then /^I can listen to an Ogg audio track in Tor Browser$/ do
301   test_url = 'https://archive.org/download/MussorgskyPicturesAtAnExhibitionorch.Ravel/09Mussorgsky_PicturesAtAnExhibition-LimogesTheMarketPlace.ogg'
302   info = xul_application_info('Tor Browser')
303   open_test_url = proc do
304     step "I open the address \"#{test_url}\" in the Tor Browser"
305   end
306   recovery_on_failure = proc do
307     @screen.press('Escape')
308     @screen.wait_vanish(info[:browser_stop_button_image], 3)
309     open_test_url.call
310   end
311   step 'no application is playing audio'
312   open_test_url.call
313   retry_tor(recovery_on_failure) do
314     step '1 application is playing audio after 30 seconds'
315   end
318 Then /^I can watch a WebM video in Tor Browser$/ do
319   test_url = WEBM_VIDEO_URL
320   info = xul_application_info('Tor Browser')
321   open_test_url = proc do
322     step "I open the address \"#{test_url}\" in the Tor Browser"
323   end
324   recovery_on_failure = proc do
325     @screen.press('Escape')
326     @screen.wait_vanish(info[:browser_stop_button_image], 3)
327     open_test_url.call
328   end
329   open_test_url.call
330   retry_tor(recovery_on_failure) do
331     @screen.wait('TorBrowserSampleRemoteWebMVideoFrame.png', 30)
332   end
335 Then /^DuckDuckGo is the default search engine$/ do
336   ddg_search_prompt = 'DuckDuckGoSearchPrompt.png'
337   case $language
338   when 'Arabic', 'Persian'
339     ddg_search_prompt = "DuckDuckGoSearchPromptRTL.png"
340   when 'Chinese', 'Hindi'
341     ddg_search_prompt = "DuckDuckGoSearchPrompt#{$language}.png"
342   end
343   step 'I start the Tor Browser'
344   step 'I open a new tab in the Tor Browser'
345   # Typing would require maintaining keymaps for every language in
346   # which we run this step ⇒ instead, paste the search string.
347   $vm.set_clipboard('a random search string')
348   @screen.press('ctrl', 'v')
349   @screen.wait(ddg_search_prompt, 20)
350   step 'I kill the Tor Browser'
353 Then(/^the screen keyboard works in Tor Browser$/) do
354   osk_key = 'ScreenKeyboardKeyX.png'
355   browser_bar_x = 'BrowserAddressBarX.png'
356   case $language
357   when 'Arabic'
358     browser_bar_x = 'BrowserAddressBarXRTL.png'
359   when 'Chinese', 'Hindi'
360     browser_bar_x = "BrowserAddressBarX#{$language}.png"
361   when 'Persian'
362     osk_key = 'ScreenKeyboardKeyPersian.png'
363     browser_bar_x = 'BrowserAddressBarXPersian.png'
364   end
365   step 'I start the Tor Browser'
366   step 'I open a new tab in the Tor Browser'
367   @screen.wait('ScreenKeyboard.png', 10)
368   @screen.wait(osk_key, 10).click
369   @screen.wait(browser_bar_x, 20)
370   step 'I kill the Tor Browser'