Skip to content

Commit de72fbc

Browse files
authored
Merge pull request #440 from seleniumbase/mobile-device-testing
Add mobile device testing to SeleniumBase
2 parents 39783ed + e3bc5e9 commit de72fbc

File tree

8 files changed

+198
-58
lines changed

8 files changed

+198
-58
lines changed

README.md

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1-
[<img src="https://cdn2.hubspot.net/hubfs/100006/images/super_logo_sb8.png" title="SeleniumBase" height="48">](https://github.com/seleniumbase/SeleniumBase/blob/master/README.md)
1+
[<img src="https://cdn2.hubspot.net/hubfs/100006/images/super_logo_sb4.png" title="SeleniumBase" height="48">](https://github.com/seleniumbase/SeleniumBase/blob/master/README.md)
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-
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.
5+
All-in-one framework for web automation, end-to-end testing, and website tours. SeleniumBase uses [pytest](https://pytest.org) for running Python scripts, while using [Selenium WebDriver](https://selenium.dev/) for controlling web browsers.
66

7-
* Contains reliable, smart-waiting code to prevent flaky tests.
8-
* Simplifies the process of creating UI tests for any website.
9-
* 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://selenium.dev/).
11-
* 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).
7+
* Helps you build reliable, non-flaky UI tests for any website.
8+
* Includes flexible [command-line options](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/customizing_test_runs.md) for running tests.
9+
* Comes with easy-to-use [Python methods](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/method_summary.md) for writing tests.
1210
* 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).
13-
* 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).
14-
* 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.
11+
* 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 more.
1512
* To see the full list of SeleniumBase features, [click here](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/features_list.md).
1613

1714
<img src="https://cdn2.hubspot.net/hubfs/100006/images/my_first_test_gif.gif" title="SeleniumBase"><br />
@@ -212,6 +209,8 @@ SeleniumBase provides additional Pytest command-line options for tests:
212209
--port=PORT # (The port that's used by the test server.)
213210
--proxy=SERVER:PORT # (This is the proxy server:port combo used by tests.)
214211
--agent=STRING # (This designates the web browser's User Agent to use.)
212+
--mobile # (The option to use the mobile emulator while running tests.)
213+
--metrics=STRING # ("CSSWidth,Height,PixelRatio" for mobile emulator tests.)
215214
--extension-zip=ZIP # (Load a Chrome Extension .zip file, comma-separated.)
216215
--extension-dir=DIR # (Load a Chrome Extension directory, comma-separated.)
217216
--headless # (The option to run tests headlessly. The default on Linux OS.)

examples/raw_parameter_script.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
sb.data = None
3636
sb.environment = "test"
3737
sb.user_agent = None
38+
sb.mobile_emulator = False
39+
sb.device_metrics = None
3840
sb.extension_zip = None
3941
sb.extension_dir = None
4042
sb.database_env = "test"

help_docs/customizing_test_runs.md

Lines changed: 57 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,69 +2,74 @@
22

33
## <img src="https://cdn2.hubspot.net/hubfs/100006/images/super_square_logo_3a.png" title="SeleniumBase" height="32"> Customizing test runs
44

5-
In addition to [settings.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/config/settings.py) (which lets you customize SeleniumBase global properties) you can customize test runs from the command line with pytest (or nosetests):
6-
7-
* Choose the browser for tests to use (Default: Chrome)
8-
* Choose betweeen pytest & nose unittest runners
9-
* Choose whether to enter Debug Mode on failures
10-
* Choose additional variables to pass into tests
11-
* Choose the User-Agent for the browser to use
12-
* Choose the automation speed (with Demo Mode)
13-
* Choose whether to run tests multi-threaded
14-
* Choose whether to rerun failing tests
15-
* Choose whether to reuse the browser session
16-
* Choose a Chrome Extension to load
17-
* Choose a Chrome User Data Directory to use
18-
* Choose a BrowserStack server to run on
19-
* Choose a Sauce Labs server to run on
20-
* Choose a TestingBot server to run on
21-
* Choose a CrossBrowserTesting server
22-
* Choose a Selenium Grid to connect to
23-
* Choose a database to save results to
24-
* Choose a proxy server to connect to
25-
26-
...and more!
27-
28-
#### **Examples:**
29-
30-
These are run from the **[examples](https://github.com/seleniumbase/SeleniumBase/tree/master/examples)** folder.
31-
(Chrome is the default browser if not specified.)
5+
In addition to [settings.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/config/settings.py) for customizing global properties, you can customize test runs [from the command-line](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/plugins/pytest_plugin.py).
6+
7+
The following tests can be run from the [examples/](https://github.com/seleniumbase/SeleniumBase/tree/master/examples) folder:
328

339
```bash
10+
# Run my_first_test.py in Chrome (default browser)
3411
pytest my_first_test.py
3512

13+
# Run my_first_test.py in Firefox
14+
pytest my_first_test.py --browser=firefox
15+
16+
# Run tests in Demo Mode to see assertions
3617
pytest my_first_test.py --demo
3718

38-
pytest my_first_test.py --browser=firefox
19+
# Run tests in Headless Mode (invisible browser)
20+
pytest test_suite.py --headless
21+
22+
# Run tests multi-threaded using [n] threads
23+
pytest test_suite.py -n=4
3924

25+
# Create a pytest html report after tests are done
4026
pytest test_suite.py --html=report.html
4127

42-
nosetests test_suite.py --report --show-report
28+
# Enter Debug Mode on failures
29+
pytest test_fail.py --pdb -s
30+
31+
# Rerun failing tests more times
32+
pytest test_suite.py --reruns=1
4333

44-
pytest test_suite.py --headless -n=4
34+
# Pass extra data into tests (retrieve: self.data)
35+
pytest my_first_test.py --data="ABC,DEF"
4536

46-
pytest test_suite.py --reruns=1 --reruns-delay=1
37+
# Run tests on a local Selenium Grid
38+
pytest test_suite.py --server=127.0.0.1
4739

40+
# Run tests on a remote Selenium Grid
4841
pytest test_suite.py --server=IP_ADDRESS --port=4444
4942

50-
pytest test_suite.py --reuse-session
43+
# Run tests on a remote Selenium Grid with authentication
44+
pytest test_suite.py --server=USERNAME:KEY@IP_ADDRESS --port=80
5145

52-
pytest test_fail.py --pdb -s
46+
# Reuse the same browser session for all tests being run
47+
pytest test_suite.py --reuse-session
5348

49+
# Run tests through a proxy server
5450
pytest proxy_test.py --proxy=IP_ADDRESS:PORT
5551

52+
# Run tests through a proxy server with authentication
5653
pytest proxy_test.py --proxy=USERNAME:PASSWORD@IP_ADDRESS:PORT
5754

55+
# Run tests while setting the web browser's User Agent
5856
pytest user_agent_test.py --agent="USER-AGENT-STRING"
5957

58+
# Run tests using Chrome's mobile device emulator (default settings)
59+
pytest test_swag_labs.py --mobile
60+
61+
# Run mobile tests specifying CSS Width, CSS Height, and Pixel-Ratio
62+
pytest test_swag_labs.py --mobile --metrics="411,731,3"
63+
64+
# Run tests while changing SeleniumBase default settings
6065
pytest my_first_test.py --settings-file=custom_settings.py
6166
```
6267

63-
You can interchange **pytest** with **nosetests**, but using pytest is strongly recommended because developers stopped supporting nosetests. Chrome is the default browser if not specified.
68+
You can interchange **pytest** with **nosetests** for most things, but using pytest is strongly recommended because developers stopped supporting nosetests. Chrome is the default browser if not specified.
6469

6570
(NOTE: If you're using **pytest** for running tests outside of the SeleniumBase repo, **you'll want a copy of [pytest.ini](https://github.com/seleniumbase/SeleniumBase/blob/master/pytest.ini) at the base of the new folder structure**. If using **nosetests**, the same applies for [setup.cfg](https://github.com/seleniumbase/SeleniumBase/blob/master/setup.cfg).)
6671

67-
An easy way to override seleniumbase/config/settings.py is by using a custom settings file.
72+
An easy way to override [seleniumbase/config/settings.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/config/settings.py) is by using a custom settings file.
6873
Here's the command-line option to add to tests: (See [examples/custom_settings.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/custom_settings.py))
6974
``--settings-file=custom_settings.py``
7075
(Settings include default timeout values, a two-factor auth key, DB credentials, S3 credentials, and other important settings used by tests.)
@@ -194,6 +199,8 @@ SeleniumBase provides additional Pytest command-line options for tests:
194199
--port=PORT # (The port that's used by the test server.)
195200
--proxy=SERVER:PORT # (This is the proxy server:port combo used by tests.)
196201
--agent=STRING # (This designates the web browser's User Agent to use.)
202+
--mobile # (The option to use the mobile emulator while running tests.)
203+
--metrics=STRING # ("CSSWidth,Height,PixelRatio" for mobile emulator tests.)
197204
--extension-zip=ZIP # (Load a Chrome Extension .zip file, comma-separated.)
198205
--extension-dir=DIR # (Load a Chrome Extension directory, comma-separated.)
199206
--headless # (The option to run tests headlessly. The default on Linux OS.)
@@ -246,3 +253,18 @@ If you wish to change the User-Agent for your browser tests (Chrome and Firefox
246253
```bash
247254
pytest user_agent_test.py --agent="Mozilla/5.0 (Nintendo 3DS; U; ; en) Version/1.7412.EU"
248255
```
256+
257+
#### **Mobile Device Testing:**
258+
259+
Use ``--mobile`` to quickly run your tests using Chrome's mobile device emulator with default values for device metrics (CSS Width, CSS Height, Pixel-Ratio) and a default value set for the user agent. To configure the mobile device metrics, use ``--metrics="CSS_Width,CSS_Height,Pixel_Ratio"`` to set those values. You'll also be able to set the user agent with ``--agent="USER-AGENT-STRING"`` (a default user agent will be used if not specified). To find real values for device metrics, [see this GitHub Gist](https://gist.github.com/sidferreira/3f5fad525e99b395d8bd882ee0fd9d00). For a list of available user agent strings, [check out this page](https://developers.whatismybrowser.com/useragents/explore/).
260+
261+
```bash
262+
# Run tests using Chrome's mobile device emulator (default settings)
263+
pytest test_swag_labs.py --mobile
264+
265+
# Run mobile tests specifying CSS Width, CSS Height, and Pixel-Ratio
266+
pytest test_swag_labs.py --mobile --metrics="411,731,3"
267+
268+
# Run mobile tests specifying the user agent
269+
pytest test_swag_labs.py --mobile --agent="Mozilla/5.0 (Linux; Android 9; Pixel 3 XL)"
270+
```

seleniumbase/core/browser_launcher.py

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ def _add_chrome_disable_csp_extension(chrome_options):
125125
def _set_chrome_options(
126126
downloads_path, headless, proxy_string, proxy_auth,
127127
proxy_user, proxy_pass, user_agent, disable_csp, enable_sync,
128-
user_data_dir, extension_zip, extension_dir):
128+
user_data_dir, extension_zip, extension_dir, mobile_emulator,
129+
device_width, device_height, device_pixel_ratio):
129130
chrome_options = webdriver.ChromeOptions()
130131
prefs = {
131132
"download.default_directory": downloads_path,
@@ -140,6 +141,23 @@ def _set_chrome_options(
140141
chrome_options.add_experimental_option(
141142
"excludeSwitches", ["enable-automation"])
142143
chrome_options.add_experimental_option("useAutomationExtension", False)
144+
if mobile_emulator:
145+
emulator_settings = {}
146+
device_metrics = {}
147+
if type(device_width) is int and type(device_height) is int and (
148+
type(device_pixel_ratio) is int):
149+
device_metrics["width"] = device_width
150+
device_metrics["height"] = device_height
151+
device_metrics["pixelRatio"] = device_pixel_ratio
152+
else:
153+
device_metrics["width"] = 411
154+
device_metrics["height"] = 731
155+
device_metrics["pixelRatio"] = 3
156+
emulator_settings["deviceMetrics"] = device_metrics
157+
if user_agent:
158+
emulator_settings["userAgent"] = user_agent
159+
chrome_options.add_experimental_option(
160+
"mobileEmulation", emulator_settings)
143161
if enable_sync:
144162
chrome_options.add_experimental_option(
145163
"excludeSwitches", ["disable-sync"])
@@ -302,7 +320,8 @@ def get_driver(browser_name, headless=False, use_grid=False,
302320
servername='localhost', port=4444, proxy_string=None,
303321
user_agent=None, cap_file=None, disable_csp=None,
304322
enable_sync=None, user_data_dir=None,
305-
extension_zip=None, extension_dir=None):
323+
extension_zip=None, extension_dir=None, mobile_emulator=False,
324+
device_width=None, device_height=None, device_pixel_ratio=None):
306325
proxy_auth = False
307326
proxy_user = None
308327
proxy_pass = None
@@ -336,19 +355,22 @@ def get_driver(browser_name, headless=False, use_grid=False,
336355
browser_name, headless, servername, port,
337356
proxy_string, proxy_auth, proxy_user, proxy_pass, user_agent,
338357
cap_file, disable_csp, enable_sync, user_data_dir,
339-
extension_zip, extension_dir)
358+
extension_zip, extension_dir, mobile_emulator,
359+
device_width, device_height, device_pixel_ratio)
340360
else:
341361
return get_local_driver(
342362
browser_name, headless,
343363
proxy_string, proxy_auth, proxy_user, proxy_pass, user_agent,
344364
disable_csp, enable_sync, user_data_dir,
345-
extension_zip, extension_dir)
365+
extension_zip, extension_dir, mobile_emulator,
366+
device_width, device_height, device_pixel_ratio)
346367

347368

348369
def get_remote_driver(
349370
browser_name, headless, servername, port, proxy_string, proxy_auth,
350371
proxy_user, proxy_pass, user_agent, cap_file, disable_csp,
351-
enable_sync, user_data_dir, extension_zip, extension_dir):
372+
enable_sync, user_data_dir, extension_zip, extension_dir,
373+
mobile_emulator, device_width, device_height, device_pixel_ratio):
352374
downloads_path = download_helper.get_downloads_folder()
353375
download_helper.reset_downloads_folder()
354376
address = "http://%s:%s/wd/hub" % (servername, port)
@@ -359,7 +381,8 @@ def get_remote_driver(
359381
chrome_options = _set_chrome_options(
360382
downloads_path, headless, proxy_string, proxy_auth,
361383
proxy_user, proxy_pass, user_agent, disable_csp, enable_sync,
362-
user_data_dir, extension_zip, extension_dir)
384+
user_data_dir, extension_zip, extension_dir, mobile_emulator,
385+
device_width, device_height, device_pixel_ratio)
363386
capabilities = chrome_options.to_capabilities()
364387
for key in desired_caps.keys():
365388
capabilities[key] = desired_caps[key]
@@ -469,7 +492,8 @@ def get_local_driver(
469492
browser_name, headless,
470493
proxy_string, proxy_auth, proxy_user, proxy_pass, user_agent,
471494
disable_csp, enable_sync, user_data_dir,
472-
extension_zip, extension_dir):
495+
extension_zip, extension_dir,
496+
mobile_emulator, device_width, device_height, device_pixel_ratio):
473497
'''
474498
Spins up a new web browser and returns the driver.
475499
Can also be used to spin up additional browsers for the same test.
@@ -539,7 +563,8 @@ def get_local_driver(
539563
downloads_path, headless,
540564
proxy_string, proxy_auth, proxy_user, proxy_pass,
541565
user_agent, disable_csp, enable_sync, user_data_dir,
542-
extension_zip, extension_dir)
566+
extension_zip, extension_dir, mobile_emulator,
567+
device_width, device_height, device_pixel_ratio)
543568
return webdriver.Chrome(executable_path=LOCAL_EDGEDRIVER,
544569
options=chrome_options)
545570
else:
@@ -563,7 +588,8 @@ def get_local_driver(
563588
downloads_path, headless,
564589
proxy_string, proxy_auth, proxy_user, proxy_pass,
565590
user_agent, disable_csp, enable_sync, user_data_dir,
566-
extension_zip, extension_dir)
591+
extension_zip, extension_dir, mobile_emulator,
592+
device_width, device_height, device_pixel_ratio)
567593
if LOCAL_CHROMEDRIVER and os.path.exists(LOCAL_CHROMEDRIVER):
568594
make_driver_executable_if_not(LOCAL_CHROMEDRIVER)
569595
elif not is_chromedriver_on_path():

0 commit comments

Comments
 (0)