Skip to content

Commit ad31f39

Browse files
authored
Merge pull request #410 from seleniumbase/safari-improvements
Improve Test Automation on Safari
2 parents 9d30bca + 5e2175a commit ad31f39

File tree

11 files changed

+118
-43
lines changed

11 files changed

+118
-43
lines changed

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
[<img src="https://img.shields.io/github/release/seleniumbase/SeleniumBase.svg" alt=" " />](https://github.com/seleniumbase/SeleniumBase/releases) [<img src="https://dev.azure.com/seleniumbase/seleniumbase/_apis/build/status/seleniumbase.SeleniumBase?branchName=master" alt=" " />](https://dev.azure.com/seleniumbase/seleniumbase/_build/latest?definitionId=1&branchName=master) [<img src="https://travis-ci.org/seleniumbase/SeleniumBase.svg?branch=master" alt=" " />](https://travis-ci.org/seleniumbase/SeleniumBase) [<img src="https://badges.gitter.im/seleniumbase/SeleniumBase.svg" alt=" " />](https://gitter.im/seleniumbase/SeleniumBase) [<img src="https://img.shields.io/badge/license-MIT-22BBCC.svg" alt=" " />](https://github.com/seleniumbase/SeleniumBase/blob/master/LICENSE) [<img src="https://img.shields.io/github/stars/seleniumbase/seleniumbase.svg" alt=" " />](https://github.com/seleniumbase/SeleniumBase/stargazers)
44

5-
A complete framework for web automation, end-to-end testing, and [user-onboarding](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/tour_examples/ReadMe.md). SeleniumBase uses [pytest](https://github.com/pytest-dev/pytest) for running Python scripts, while using [Selenium WebDriver](https://www.seleniumhq.org/) for controlling web browsers.
5+
All-in-one framework for web automation, end-to-end testing, and website tours. SeleniumBase uses pytest for running Python scripts, while using Selenium WebDriver for controlling web browsers.
66

7-
* No more flaky tests! (Smart-waiting keeps tests reliable.)
7+
* Contains reliable, smart-waiting code to prevent flaky tests.
8+
* Simplifies the process of creating UI tests for any website.
89
* Includes [Plugins](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/plugins/pytest_plugin.py) for logging [test results and screenshots](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/example_logs/ReadMe.md).
10+
* Multiplies the abilities of [pytest](https://pytest.org) and [Selenium WebDriver](https://www.seleniumhq.org/).
911
* Uses versatile [Python methods](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/method_summary.md) and [command-line options](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/customizing_test_runs.md).
10-
* Has tools for [website tours](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/tour_examples/ReadMe.md), [assisted-QA](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/master_qa/ReadMe.md), and [visual testing](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/visual_testing/ReadMe.md).
12+
* Includes tools for [assisted-QA](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/master_qa/ReadMe.md), [visual testing](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/visual_testing/ReadMe.md), and [web tours](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/tour_examples/ReadMe.md).
1113
* Integrates with [Selenium Grid](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/utilities/selenium_grid/ReadMe.md), [Katalon Recorder](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/utilities/selenium_ide/ReadMe.md), and [MySQL](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/core/testcase_manager.py).
1214
* Supports [Azure Pipelines](https://github.com/seleniumbase/SeleniumBase/blob/master/integrations/azure/azure_pipelines/ReadMe.md), [GCP](https://github.com/seleniumbase/SeleniumBase/blob/master/integrations/google_cloud/ReadMe.md), [TravisCI](https://github.com/seleniumbase/SeleniumBase/blob/master/.travis.yml), [Docker](https://github.com/seleniumbase/SeleniumBase/blob/master/integrations/docker/ReadMe.md), and more.
1315
* To see the full list of SeleniumBase features, [click here](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/features_list.md).

help_docs/hidden_files_info.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
### Info about hidden files on macOS
1+
## Info about hidden files on macOS
22

33
Depending on your macOS settings, some files may be hidden from view in your Finder window, such as ``.gitignore``. To view all files, run the following command and then reopen the Finder window:
44
```bash
55
defaults write com.apple.finder AppleShowAllFiles -bool true
66
```
77

88
More info on that can be found here:
9-
https://www.defaults-write.com/show-hidden-files-in-os-x-finder/
10-
https://www.macworld.co.uk/how-to/mac-software/hidden-files-mac-3520878/
11-
https://setapp.com/how-to/show-hidden-files-on-mac
9+
* https://www.defaults-write.com/show-hidden-files-in-os-x-finder/
10+
* https://www.macworld.co.uk/how-to/mac-software/hidden-files-mac-3520878/
11+
* https://setapp.com/how-to/show-hidden-files-on-mac

help_docs/method_summary.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ self.click_chain(selectors_list, by=By.CSS_SELECTOR, timeout=None, spacing=0)
2424

2525
self.type(selector, text, by=By.CSS_SELECTOR, timeout=None, retry=False)
2626

27+
self.input(selector, text, by=By.CSS_SELECTOR, timeout=None, retry=False)
28+
2729
self.update_text(selector, new_value, by=By.CSS_SELECTOR, timeout=None, retry=False)
2830

2931
self.add_text(selector, text, by=By.CSS_SELECTOR, timeout=None)

help_docs/using_safari_driver.md

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,9 @@
1-
### Info about using Safari Driver for running automated tests on macOS
1+
## Using Safari's WebDriver for running browser tests on macOS
22

3-
(NOTE: SafariDriver requires Safari 10 running on OS X 10.11 El Capitan or greater)
3+
*(NOTE: Safari's WebDriver requires macOS 10.13 "High Sierra" or later.)*
44

5-
You can find info on Safari Driver if you scroll down to that section on the [SeleniumHQ downloads page](https://www.seleniumhq.org/download/).
5+
You can find the official Apple documentation regarding "Testing with WebDriver in Safari" on the following page: https://developer.apple.com/documentation/webkit/testing_with_webdriver_in_safari
66

7-
That page will tell you to [download the required Safari Driver browser extension (SafariDriver.safariextz) here at this link](http://selenium-release.storage.googleapis.com/index.html?path=2.48/).
7+
Run ``safaridriver --enable`` once in a terminal to enable Safari's WebDriver. (If you’re upgrading from a previous macOS release, you may need to prefix the command with ``sudo``.)
88

9-
For that to work, you'll need to [download the Standalone Selenium Server from here](http://docs.seleniumhq.org/download/) and put that JAR file in ``/usr/local/bin/``. To make the next step easier, rename the downloaded JAR file to ``selenium-server-standalone.jar`` (if it's not already called that).
10-
11-
Next, configure the Selenium Server JAR file into your PATH like this:
12-
13-
```bash
14-
export SELENIUM_SERVER_JAR=/usr/local/bin/selenium-server-standalone.jar
15-
export PATH=$PATH:/usr/local/bin/selenium-server-standalone.jar
16-
```
17-
18-
Now you're ready to run automated tests on Safari if you use ``--browser=safari`` on the command line when running your tests/scripts with SeleniumBase.
9+
Now you can use ``--browser=safari`` to run your **SeleniumBase** tests on Safari.

help_docs/verify_webdriver.md

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,39 @@
1-
### Verify that web drivers were successfully installed
1+
## Verifying that web drivers are installed
22

3-
*You can do this by checking inside a Python command prompt. (NOTE: xkcd is a webcomic)*
3+
*You can do this by checking inside a Python command prompt.*
44

55
#### Verifying ChromeDriver
66
```bash
77
python
8+
```
9+
```python
810
>>> from selenium import webdriver
9-
>>> browser = webdriver.Chrome()
10-
>>> browser.get("http://xkcd.com/1337/")
11-
>>> browser.close()
11+
>>> driver = webdriver.Chrome()
12+
>>> driver.get("https://www.google.com/chrome")
13+
>>> driver.quit()
1214
>>> exit()
1315
```
1416

15-
#### Verifying FirefoxDriver (Geckodriver)
17+
#### Verifying Geckodriver (Firefox WebDriver)
1618
```bash
1719
python
20+
```
21+
```python
22+
>>> from selenium import webdriver
23+
>>> driver = webdriver.Firefox()
24+
>>> driver.get("https://www.mozilla.org/firefox")
25+
>>> driver.quit()
26+
>>> exit()
27+
```
28+
29+
#### Verifying WebDriver for Safari
30+
```bash
31+
python
32+
```
33+
```python
1834
>>> from selenium import webdriver
19-
>>> browser = webdriver.Firefox()
20-
>>> browser.get("http://xkcd.com/1337/")
21-
>>> browser.close()
35+
>>> driver = webdriver.Safari()
36+
>>> driver.get("https://www.apple.com/safari")
37+
>>> driver.quit()
2238
>>> exit()
2339
```

integrations/selenium_ide/ReadMe.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,19 @@
1-
### The ReadMe for the Katalon / Selenium IDE conversion tool has been moved to: [seleniumbase/utilities/selenium_ide/ReadMe.md](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/utilities/selenium_ide/ReadMe.md) and all related code has been moved to [seleniumbase/utilities/selenium_ide](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/utilities/selenium_ide)
1+
## Converting Katalon recordings into SeleniumBase test scripts
2+
3+
Katalon Recorder (Selenium IDE) is a tool that allows you to record and playback actions performed inside a web browser. It's available as a [downloadable Chrome extension](https://chrome.google.com/webstore/detail/katalon-recorder-selenium/ljdobmomdgdljniojadhoplhkpialdid) and a [downloadable Firefox extension](https://addons.mozilla.org/en-US/firefox/addon/katalon-automation-record/). The Katalon Recorder comes with an option to export recordings as various WebDriver test scripts, one of which is ``Python 2 (WebDriver + unittest)``. Unfortunately, these natively-exported scripts can be very messy and don't always run reliably. The purpose of this converter is to clean up and improve the scripts so that they can be used in production-level environments.
4+
5+
#### Step 1: Make a recording with the Katalon Recorder
6+
7+
![](https://cdn2.hubspot.net/hubfs/100006/images/katalon_recorder_2.png "Katalon Recorder example")
8+
9+
#### Step 2: Export your recording as a Python 2 Webdriver script
10+
11+
* ``{} Export`` => ``Python 2 (WebDriver + unittest)`` => ``Save As File``
12+
13+
#### Step 3: Run ``seleniumbase convert`` on your exported Python file
14+
15+
```
16+
seleniumbase convert [MY_TEST.py]
17+
```
18+
19+
* You should see a [MY_TEST_SB.py] file appear in the folder. (``_SB`` is added to the file name so that the original file stays intact in case you still need it.) This new clean & reliable SeleniumBase test script is ready to be added into your test suite for running.

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ colorama>=0.4.1
3333
pymysql>=0.9.3
3434
pyotp>=2.3.0
3535
boto>=2.49.0
36-
cffi>=1.13.1
36+
cffi>=1.13.2
3737
tqdm>=4.37.0
3838
flake8>=3.7.9
3939
certifi>=2019.9.11

seleniumbase/core/browser_launcher.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,8 @@ def get_local_driver(
545545
else:
546546
return webdriver.Edge()
547547
elif browser_name == constants.Browser.SAFARI:
548+
if "".join(sys.argv) == "-c": # Skip if multithreaded
549+
raise Exception("Can't run Safari tests in multi-threaded mode!")
548550
return webdriver.Safari()
549551
elif browser_name == constants.Browser.OPERA:
550552
if LOCAL_OPERADRIVER and os.path.exists(LOCAL_OPERADRIVER):

seleniumbase/fixtures/base_case.py

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,13 @@ def click(self, selector, by=By.CSS_SELECTOR, timeout=None, delay=0):
141141
time.sleep(0.05)
142142
element = page_actions.wait_for_element_visible(
143143
self.driver, selector, by, timeout=timeout)
144-
element.click()
144+
if self.browser == "safari":
145+
if by == By.LINK_TEXT:
146+
self.__jquery_click(selector, by=by)
147+
else:
148+
self.__js_click(selector, by=by)
149+
else:
150+
element.click()
145151
except (WebDriverException, MoveTargetOutOfBoundsException):
146152
self.wait_for_ready_state_complete()
147153
try:
@@ -242,12 +248,26 @@ def type(self, selector, text, by=By.CSS_SELECTOR,
242248
by = By.XPATH
243249
self.update_text(selector, text, by=by, timeout=timeout, retry=retry)
244250

251+
def input(self, selector, text, by=By.CSS_SELECTOR,
252+
timeout=None, retry=False):
253+
""" Same as update_text(). """
254+
if not timeout:
255+
timeout = settings.LARGE_TIMEOUT
256+
if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
257+
timeout = self.__get_new_timeout(timeout)
258+
if page_utils.is_xpath_selector(selector):
259+
by = By.XPATH
260+
self.update_text(selector, text, by=by, timeout=timeout, retry=retry)
261+
245262
def update_text(self, selector, new_value, by=By.CSS_SELECTOR,
246263
timeout=None, retry=False):
247264
""" This method updates an element's text field with new text.
248-
Has two parts:
249-
1. Clears the text field.
250-
2. Types in new text into the text field.
265+
Has multiple parts:
266+
* Waits for the element to be visible.
267+
* Waits for the element to be interactive.
268+
* Clears the text field.
269+
* Types in the new text.
270+
* Hits Enter/Submit (if the text ends in "\n").
251271
@Params
252272
selector - the selector of the text field
253273
new_value - the new value to type into the text field
@@ -273,7 +293,10 @@ def update_text(self, selector, new_value, by=By.CSS_SELECTOR,
273293
time.sleep(0.06)
274294
element = self.wait_for_element_visible(
275295
selector, by=by, timeout=timeout)
276-
element.clear()
296+
try:
297+
element.clear()
298+
except Exception:
299+
pass # Clearing the text field first isn't critical
277300
except Exception:
278301
pass # Clearing the text field first isn't critical
279302
self.__demo_mode_pause_if_active(tiny=True)
@@ -411,11 +434,15 @@ def get_title(self):
411434
def go_back(self):
412435
self.__last_page_load_url = None
413436
self.driver.back()
437+
if self.browser == "safari":
438+
self.driver.refresh()
414439
self.wait_for_ready_state_complete()
415440

416441
def go_forward(self):
417442
self.__last_page_load_url = None
418443
self.driver.forward()
444+
if self.browser == "safari":
445+
self.driver.refresh()
419446
self.wait_for_ready_state_complete()
420447

421448
def is_element_present(self, selector, by=By.CSS_SELECTOR):
@@ -1373,15 +1400,26 @@ def get_new_driver(self, browser=None, headless=None,
13731400
# WebDrivers can get closed during tearDown().
13741401
pass
13751402
else:
1376-
if self.browser == 'chrome' or self.browser == 'opera':
1403+
if self.browser == 'chrome':
1404+
width = 1250
1405+
height = 840
13771406
try:
13781407
if self.maximize_option:
13791408
self.driver.maximize_window()
13801409
else:
1381-
self.driver.set_window_size(1250, 840)
1410+
self.driver.set_window_size(width, height)
13821411
self.wait_for_ready_state_complete()
13831412
except Exception:
13841413
pass # Keep existing browser resolution
1414+
elif self.browser == 'firefox':
1415+
pass # No changes
1416+
elif self.browser == 'safari':
1417+
if self.maximize_option:
1418+
try:
1419+
self.driver.maximize_window()
1420+
self.wait_for_ready_state_complete()
1421+
except Exception:
1422+
pass # Keep existing browser resolution
13851423
elif self.browser == 'edge':
13861424
try:
13871425
self.driver.maximize_window()
@@ -3582,6 +3620,12 @@ def __recalculate_selector(self, selector, by):
35823620
name = page_utils.get_name_from_selector(selector)
35833621
selector = '[name="%s"]' % name
35843622
by = By.CSS_SELECTOR
3623+
if by == By.LINK_TEXT or by == By.PARTIAL_LINK_TEXT:
3624+
if self.browser == "safari" and selector.lower() != selector:
3625+
selector = ("""//a[contains(translate(.,"ABCDEFGHIJKLMNOPQR"""
3626+
"""STUVWXYZ","abcdefghijklmnopqrstuvw"""
3627+
"""xyz"),"%s")]""" % selector.lower())
3628+
by = By.XPATH
35853629
return (selector, by)
35863630

35873631
def __make_css_match_first_element_only(self, selector):

seleniumbase/utilities/selenium_ide/ReadMe.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
## Converting Katalon/Selenium IDE recordings into SeleniumBase test scripts
1+
## Converting Katalon recordings into SeleniumBase test scripts
22

3-
[Katalon Recorder / Selenium IDE](https://www.katalon.com/resources-center/blog/katalon-automation-recorder/) is a tool that allows you to record and playback actions performed inside a web browser. It's available as a [downloadable Chrome extension](https://chrome.google.com/webstore/detail/katalon-recorder-selenium/ljdobmomdgdljniojadhoplhkpialdid) and a [downloadable Firefox extension](https://addons.mozilla.org/en-US/firefox/addon/katalon-automation-record/). The Katalon Recorder comes with an option to export recordings as various WebDriver test scripts, one of which is ``Python 2 (WebDriver + unittest)``. Unfortunately, these natively-exported scripts can be very messy and don't always run reliably. The purpose of this converter is to clean up and improve the scripts so that they can be used in production-level environments.
3+
Katalon Recorder (Selenium IDE) is a tool that allows you to record and playback actions performed inside a web browser. It's available as a [downloadable Chrome extension](https://chrome.google.com/webstore/detail/katalon-recorder-selenium/ljdobmomdgdljniojadhoplhkpialdid) and a [downloadable Firefox extension](https://addons.mozilla.org/en-US/firefox/addon/katalon-automation-record/). The Katalon Recorder comes with an option to export recordings as various WebDriver test scripts, one of which is ``Python 2 (WebDriver + unittest)``. Unfortunately, these natively-exported scripts can be very messy and don't always run reliably. The purpose of this converter is to clean up and improve the scripts so that they can be used in production-level environments.
44

55
#### Step 1: Make a recording with the Katalon Recorder
66

0 commit comments

Comments
 (0)