Selenium Testing in Safari Browser – macOS Sierra

The Blog To Learn Selenium and Test Automation

Selenium Testing in Safari Browser – macOS Sierra

In this article, we are going to experiment running tests in Safari browser (12.1.1) and also going to check necessary pre-requisites to enable Safari browser for automated test execution. Throughout this article we used Python Selenium WebDriver which is Selenium’s cross-platform, cross-browser automation API for Python programming language.

Safari Browser – Test Automation

Safari browser now, starting with Safari 10 on OS X El Capitan and macOS Sierra, provides native support for the WebDriver API. This means

  • No need to download safari driver (You still need to download Safari 10 or latest version to get the Apple developed driver)
  • No need to pass safari driver executable path to create driver object – Selenium will automatically launch the driver without any further configuration.

New driver implementation is bundled in Safari and also it is maintained by the Web Developer Experience team at Apple. You can find Safari driver executable in /usr/bin/safaridriver folder path.

Note: Python Selenium library provides support for Safari’s native driver implementation starting from 3.0.0 release.

Apple-developed driver is unrelated to the legacy SafariDriver mentioned, which is no longer maintained and should not be used, in the Selenium project. You do not need to download anything besides Safari 10 or later to get the Apple-developed driver.

If you are trying to follow the old way to install any extensions, safari will throw error.

Safari no longer supports unsafe extensions
Error popup

How WebDriver Works?

As we all know WebDriver is specified in terms of a REST API and driver provides a local web server that accepts REST-style HTTP requests.

  • Python Selenium library translates each method call on driver into a REST API call and sends the corresponding HTTP request to local web server hosted by Safari driver
  • The driver receives the requests, validates the contents of it and forwards the corresponding command to the appropriate browser instance to execute
  • Upon completion of command execution, the driver receives execution response and send it as an HTTP response to the calling REST API call
  • The Python client library interprets the HTTP response and returns the result back to the test code

Pre-requisites

After downloading and installing Safari browser, we need to configure Safari to allow automation. Safari’s WebDriver support is turned off by default as this is a feature intended for developers and not for regular users.

  • First we need to make Develop menu available. To do this go to Safari -> preferences -> click on Advanced tab, and select ‘Show Develop menu in menu bar checkbox‘ if it is not checked.
  • Next step, we need to allow remote automation by enabling ‘Allow Remote Automation‘ toggle menu in the Develop menu. Select Develop > Allow Remote Automation in the menu bar.

Configuring above two steps should work and allow you to execute automated tests in Safari browser. If it is not happening, complete the following one time step as well to manually complete the authentication.

  1. From CLI, Go to /usr/bin and search for safaridriver (If it is not available, you are not using correct Safari browser)
  2. Run /usr/bin/safaridriver –enable
  3. Enter system password for the logged in user

Exceptions to look for

Following error will be thrown on opening browser using Selenium if ‘Allow Remote Automation’ is not enabled.

Traceback (most recent call last):
   File "test_safari_open_browser.py", line 3, in 
     driver = webdriver.Safari()
   File "/Library/Python/2.7/site-packages/selenium/webdriver/safari/webdriver.py", line 64, in init
     desired_capabilities=desired_capabilities)
   File "/Library/Python/2.7/site-packages/selenium/webdriver/remote/webdriver.py", line 157, in init
     self.start_session(capabilities, browser_profile)
   File "/Library/Python/2.7/site-packages/selenium/webdriver/remote/webdriver.py", line 252, in start_session
     response = self.execute(Command.NEW_SESSION, parameters)
   File "/Library/Python/2.7/site-packages/selenium/webdriver/remote/webdriver.py", line 321, in execute
     self.error_handler.check_response(response)
   File "/Library/Python/2.7/site-packages/selenium/webdriver/remote/errorhandler.py", line 242, in check_response
     raise exception_class(message, screen, stacktrace)
 selenium.common.exceptions.SessionNotCreatedException: Message: Could not create a session: You must enable the 'Allow Remote Automation' option in Safari's Develop menu to control Safari via WebDriver.

It is possible to invoke only one browser at a time using Selenium and this is as per the design of Safari Browser driver implementation. If you are trying to open more browser instances, you will get following error.

Traceback (most recent call last):
   File "test_safari_open_browser.py", line 4, in 
     driver = webdriver.Safari()
   File "/Library/Python/2.7/site-packages/selenium/webdriver/safari/webdriver.py", line 64, in init
     desired_capabilities=desired_capabilities)
   File "/Library/Python/2.7/site-packages/selenium/webdriver/remote/webdriver.py", line 157, in init
     self.start_session(capabilities, browser_profile)
   File "/Library/Python/2.7/site-packages/selenium/webdriver/remote/webdriver.py", line 252, in start_session
     response = self.execute(Command.NEW_SESSION, parameters)
   File "/Library/Python/2.7/site-packages/selenium/webdriver/remote/webdriver.py", line 321, in execute
     self.error_handler.check_response(response)
   File "/Library/Python/2.7/site-packages/selenium/webdriver/remote/errorhandler.py", line 242, in check_response
     raise exception_class(message, screen, stacktrace)
 selenium.common.exceptions.SessionNotCreatedException: Message: Could not create a session: The Safari instance is already paired with another WebDriver session.

Invoking Safari Browser

from selenium import webdriver

driver = webdriver.Safari()
driver.get("http://allselenium.info")

Looks same as other browsers invokation however we are not passing drivers executable path or have not configured driver executable path in environment PATH variable.

Special Features

When running a WebDriver test in Safari, test execution is confined to special Automation windows that are isolated from normal browsing windows, user settings, and preferences. Automation windows are easy to recognize by their orange Smart Search field. WebDriver tests that run in an Automation window always start from a clean slate and cannot access Safari’s normal browsing history, AutoFill data, or other sensitive information which is similar to browsing in a Private Browsing window. Aside from the obvious privacy benefits, this restriction also helps to ensure that tests are not affected by a previous test session’s persistent state such as local storage or cookies.

Safari installs a glass pane over the Automation window while the test is running to block any other manual interactions like mouse, keyboard, resizing, and so on from affecting the Automation window. It is possible to interrupt the running test by breaking the glass pane if a running test gets stuck, fails or we need to check something manually. When an automation session is interrupted, the test’s connection to the browser is permanently severed and the automation window remains open until closed manually. User is presented with three options when trying to break the automation session.

Safari browser controlled by selenium webdriver
Error Popup on trying to break WebDriver Session
  1. Turn off All Automation – Disables ‘Allow Remote Automation’ in the Develop menu. To run tests again in Safari browser, we have to do the second step in prerequisites again
  2. Stop Session – Stops current WebDriver session alone and enables user to interact with browser
  3. Continue Session – Continues session and user cannot interact with browser

Basic Functionalities

Other basic functionalities remains same.

Load URL

driver.get("url with protocol")

maximize browser

driver.maximize_window()

Minimize browser to tray

driver.minimize_window()

Close Window

driver.close() 

This method only closes the current window

Quit Window

driver.quit()

quit() method closes all windows opened by current webdriver session and terminates the driverserver executable process.

Basic operations on browser

  • maximize_window()
  • fullscreen_window()
  • set_window_size(width, height) # width and height are integer values
  • set_window_position(y-axis, x-axis) # y-axis and x-axis are integer values
  • set_window_rect(x-axis, y-axis, height, width) # All are integer values
  • get_window_position()
  • get_window_rect()
  • get_window_size()
  • minimize_window()

Note:

  • set_window_rect() is combination of set_window_size() and set_window_postition()
  • Also set_window_rect() accepts either x-axis and y-axis or height and width

Example -1

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
driver = webdriver.Safari()

driver.get("https://www.google.com")
print("Window size", driver.get_window_size())

driver.fullscreen_window()
print("Window size after full screen", driver.get_window_size())

driver.maximize_window()
print("Window size after maximize", driver.get_window_size())

driver.set_window_size(1240, 700)
print("Window size after custom change", driver.get_window_size())

driver.set_window_size(width=1340, height=800)
print("Window size after custom change", driver.get_window_size())
print("Window Rect", driver.get_window_rect())

driver.set_window_rect(50,50, 650, 1100)
print("Window Rect after custom change", driver.get_window_rect())
print("Window position", driver.get_window_position())

driver.set_window_position(40, 40)
print("Window position after custom change", driver.get_window_position())
driver.minimize_window()
driver.quit()

Output

('Window size', {'width': 800, 'height': 600})
('Window size after full screen', {'width': 800, 'height': 600})
('Window size after maximize', {'width': 1680, 'height': 972})
('Window size after custom change', {'width': 1240, 'height': 700})
('Window size after custom change', {'width': 1340, 'height': 800})
('Window Rect', {u'y': 23, u'x': 0, u'height': 800, u'width': 1340})
('Window Rect after custom change', {u'y': 50, u'x': 50, u'height': 1100, u'width': 650})
('Window position', {'y': 50, 'x': 50})
('Window position after custom change', {'y': 40, 'x': 40})

Example-2

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
driver = webdriver.Safari()

driver.get("https://www.google.com")
print("Window size", driver.get_window_size())

driver.set_window_size(width=1340, height=800)
print("Window size after custom change", driver.get_window_size())
print("Window Rect", driver.get_window_rect())

driver.set_window_rect(x=50, y=50, height=650, width=1100)
print("Window Rect after custom change", driver.get_window_rect())

driver.set_window_rect(height=850, width=1100)
print("Window Rect after custom height change", 
driver.get_window_rect())
print("Window position", driver.get_window_position())

driver.set_window_position(y=40, x=40)
print("Window position after custom change", driver.get_window_position())
driver.minimize_window()
driver.quit()

Output

('Window size', {'width': 800, 'height': 600})
('Window size after custom change', {'width': 1340, 'height': 800})
('Window Rect', {u'y': 395, u'x': 40, u'height': 800, u'width': 1340})
('Window Rect after custom change', {u'y': 50, u'x': 50, u'height': 650, u'width': 1100})
('Window Rect after custom height change', {u'y': 50, u'x': 50, u'height': 850, u'width': 1100})
('Window position', {'y': 50, 'x': 50})
('Window position after custom change', {'y': 40, 'x': 40})

Other supported operations

  • title
  • current_url
  • page_source
  • back()
  • forward()
  • refresh()

Hope this article is helpful. Please let us know your thoughts in comments and checkout other articles here.

 

Leave a Reply

Your email address will not be published. Required fields are marked *