Skip to content

Commit 379dc50

Browse files
authored
Merge pull request #355 from seleniumbase/custom-settings
Add a settings file parser for overriding default SeleniumBase settings
2 parents c64d9a9 + b151c8c commit 379dc50

File tree

10 files changed

+292
-19
lines changed

10 files changed

+292
-19
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
language: python
22
sudo: false
3+
dist: trusty
34
matrix:
45
include:
56
- python: 2.7

README.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
[<img src="https://cdn2.hubspot.net/hubfs/100006/images/sb_media_logo_4.png" title="SeleniumBase" height="170">](https://github.com/seleniumbase/SeleniumBase/blob/master/README.md)
22

3-
[<img src="https://img.shields.io/github/release/seleniumbase/SeleniumBase.svg" />](https://github.com/seleniumbase/SeleniumBase/releases) [<img src="https://dev.azure.com/seleniumbase/seleniumbase/_apis/build/status/seleniumbase.SeleniumBase?branchName=master" />](https://dev.azure.com/seleniumbase/seleniumbase/_build/latest?definitionId=1&branchName=master) [<img src="https://travis-ci.org/seleniumbase/SeleniumBase.svg?branch=master" alt="Build Status" />](https://travis-ci.org/seleniumbase/SeleniumBase) [<img src="https://badges.gitter.im/seleniumbase/SeleniumBase.svg" alt="Join the SeleniumBase Gitter chat" />](https://gitter.im/seleniumbase/SeleniumBase) [<img src="https://img.shields.io/badge/license-MIT-22BBCC.svg" alt="MIT License" />](https://github.com/seleniumbase/SeleniumBase/blob/master/LICENSE) [<img src="https://img.shields.io/github/stars/seleniumbase/seleniumbase.svg" alt="Stars" />](https://github.com/seleniumbase/SeleniumBase/stargazers)<br />
3+
[<img src="https://img.shields.io/github/release/seleniumbase/SeleniumBase.svg" />](https://github.com/seleniumbase/SeleniumBase/releases) [<img src="https://dev.azure.com/seleniumbase/seleniumbase/_apis/build/status/seleniumbase.SeleniumBase?branchName=master" />](https://dev.azure.com/seleniumbase/seleniumbase/_build/latest?definitionId=1&branchName=master) [<img src="https://travis-ci.org/seleniumbase/SeleniumBase.svg?branch=master" alt="Build Status" />](https://travis-ci.org/seleniumbase/SeleniumBase) [<img src="https://badges.gitter.im/seleniumbase/SeleniumBase.svg" alt="Join the SeleniumBase Gitter chat" />](https://gitter.im/seleniumbase/SeleniumBase) [<img src="https://img.shields.io/badge/license-MIT-22BBCC.svg" alt="MIT License" />](https://github.com/seleniumbase/SeleniumBase/blob/master/LICENSE) [<img src="https://img.shields.io/github/stars/seleniumbase/seleniumbase.svg" alt="Stars" />](https://github.com/seleniumbase/SeleniumBase/stargazers) [<img src="https://img.shields.io/github/repo-size/seleniumbase/seleniumbase.svg" alt="Size" />](https://github.com/seleniumbase/SeleniumBase/releases)<br />
44

5-
Automate & test more in less time with [Selenium-WebDriver](https://www.seleniumhq.org/) and [Pytest](https://docs.pytest.org/en/latest/).
5+
✅ Everything you need to automate Web/UI testing.
66

77
<img src="https://cdn2.hubspot.net/hubfs/100006/sb_demo_mode.gif" title="SeleniumBase" height="236"><br />
88
(<i>Above: [my_first_test.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/my_first_test.py) from [examples/](https://github.com/seleniumbase/SeleniumBase/tree/master/examples) running in demo mode, which adds JavaScript for highlighting page actions.</i>)<br />
99
```
1010
pytest my_first_test.py --demo_mode
1111
```
1212

13+
SeleniumBase is an all-in-one test automation framework that uses WebDriver APIs for spinning up web browsers while using pytest and nosetests for running tests.
14+
1315
## <img src="https://cdn2.hubspot.net/hubfs/100006/images/super_square_logo_3a.png" title="SeleniumBase" height="32"> Quick Start:
1416

1517
You'll need **[Python](https://www.python.org/downloads/)** [<img src="https://img.shields.io/badge/python-2.7,_3.5,_3.6,_3.7-22AADD.svg" alt="Python versions" />](https://www.python.org/downloads/)
@@ -71,13 +73,13 @@ pytest my_first_test.py --browser=chrome
7173
SeleniumBase automatically handles common WebDriver actions such as spinning up web browsers and saving screenshots during test failures. (<i>[Read more about customizing test runs](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/customizing_test_runs.md).</i>)
7274

7375
#### **Simplified code:**<br />
74-
Instead of using messy WebDriver commands such as:
76+
SeleniumBase uses simple syntax for commands, such as:
7577
```python
76-
self.driver.find_element_by_css_selector("textarea").send_keys("text")
78+
self.update_text("textarea", "text")
7779
```
78-
...you can do the following with SeleniumBase:
80+
The same command with regular WebDriver is very messy:
7981
```python
80-
self.update_text("textarea", "text")
82+
self.driver.find_element_by_css_selector("textarea").send_keys("text")
8183
```
8284
(<i>You can still use ``self.driver`` in your code.</i>)
8385

examples/custom_settings.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
"""
2+
To override default settings stored in seleniumbase/config/settings.py,
3+
change the values here and add "--settings=custom_settings.py" when running.
4+
"""
5+
6+
# Default timeout values for waiting for page elements to appear.
7+
MINI_TIMEOUT = 2
8+
SMALL_TIMEOUT = 6
9+
LARGE_TIMEOUT = 10
10+
EXTREME_TIMEOUT = 30
11+
12+
# If False, only logs from the most recent test run will be saved.
13+
ARCHIVE_EXISTING_LOGS = False
14+
ARCHIVE_EXISTING_DOWNLOADS = False
15+
16+
# Waiting for Document.readyState to be "Complete" after browser actions.
17+
WAIT_FOR_RSC_ON_PAGE_LOADS = True
18+
WAIT_FOR_RSC_ON_CLICKS = True
19+
WAIT_FOR_ANGULARJS = True
20+
21+
# Changing the default behavior of Demo Mode. Activate with: --demo_mode
22+
DEFAULT_DEMO_MODE_TIMEOUT = 0.5
23+
HIGHLIGHTS = 4
24+
DEFAULT_MESSAGE_DURATION = 2.55
25+
26+
# Disabling the Content Security Policy of the browser by default.
27+
DISABLE_CSP_ON_FIREFOX = True
28+
DISABLE_CSP_ON_CHROME = False
29+
30+
# If True and --proxy=IP_ADDRESS:PORT is invalid, then error immediately.
31+
RAISE_INVALID_PROXY_STRING_EXCEPTION = True
32+
33+
# Changing the default behavior of MasterQA Mode.
34+
MASTERQA_DEFAULT_VALIDATION_MESSAGE = "Does the page look good?"
35+
MASTERQA_WAIT_TIME_BEFORE_VERIFY = 0.5
36+
MASTERQA_START_IN_FULL_SCREEN_MODE = False
37+
MASTERQA_MAX_IDLE_TIME_BEFORE_QUIT = 600
38+
39+
# Google Authenticator
40+
# (For 2-factor authentication using a time-based one-time password algorithm)
41+
# (See https://github.com/pyotp/pyotp and https://pypi.org/project/pyotp/ )
42+
# (Also works with Authy and other compatible apps.)
43+
TOTP_KEY = "base32secretABCD"
44+
45+
# MySQL DB Credentials
46+
# (For saving data from tests to a MySQL DB)
47+
# Add "--with-db_reporting" to save test data to a MySQL DB during test runs
48+
DB_HOST = "127.0.0.1"
49+
DB_USERNAME = "root"
50+
DB_PASSWORD = "test"
51+
DB_SCHEMA = "test_db"
52+
53+
# Amazon S3 Bucket Credentials
54+
# (For saving screenshots and other log files from tests)
55+
# (Bucket names are unique across all existing bucket names in Amazon S3)
56+
# Add "--with-s3_logging" to save test results to S3
57+
S3_LOG_BUCKET = "[S3 BUCKET NAME]"
58+
S3_BUCKET_URL = "https://s3.amazonaws.com/[S3 BUCKET NAME]/"
59+
S3_SELENIUM_ACCESS_KEY = "[S3 ACCESS KEY]"
60+
S3_SELENIUM_SECRET_KEY = "[S3 SECRET KEY]"
61+
62+
# Encryption Settings
63+
# (Used for string/password obfuscation)
64+
# (You should reset the Encryption Key for every clone of SeleniumBase)
65+
ENCRYPTION_KEY = "Pg^.l!8UdJ+Y7dMIe&fl*%!p9@ej]/#tL~3E4%6?"
66+
# These tokens are added to the beginning and end of obfuscated passwords.
67+
# Helps identify which strings/passwords have been obfuscated.
68+
OBFUSCATION_START_TOKEN = "$^*ENCRYPT="
69+
OBFUSCATION_END_TOKEN = "?&#$"
70+
71+
# Default Email Credentials
72+
# (If tests send out emails, you can scan and verify them by using IMAP)
73+
# Here's a list of imap strings for known email providers:
74+
# - Gmail: imap.gmail.com
75+
# - Outlook/Live: imap-mail.outlook.com
76+
# - Yahoo Mail: imap.mail.yahoo.com
77+
# - AT&T: imap.mail.att.net
78+
# - Comcast: imap.comcast.net
79+
# - Verizon: incoming.verizon.net
80+
EMAIL_USERNAME = "[TEST ACCOUNT GMAIL USERNAME]@gmail.com"
81+
EMAIL_PASSWORD = "[TEST ACCOUNT GMAIL PASSWORD]"
82+
EMAIL_IMAP_STRING = "imap.gmail.com"
83+
EMAIL_IMAP_PORT = 993

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pytest>=4.6.5;python_version<"3"
1515
pytest>=5.0.1;python_version>="3"
1616
pytest-cov>=2.7.1
1717
pytest-forked>=1.0.2
18-
pytest-html==1.20.0
18+
pytest-html==1.22.0
1919
pytest-metadata>=1.8.0
2020
pytest-ordering>=0.6
2121
pytest-rerunfailures>=7.0

seleniumbase/core/settings_parser.py

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
import re
2+
from seleniumbase.config import settings
3+
4+
5+
def set_settings(settings_file):
6+
if not settings_file.endswith('.py'):
7+
raise Exception("\n\n`%s` is not a Python file!\n\n" % settings_file)
8+
9+
f = open(settings_file, 'r')
10+
all_code = f.read()
11+
f.close()
12+
13+
override_settings = {}
14+
num_settings = 0
15+
16+
code_lines = all_code.split('\n')
17+
for line in code_lines:
18+
19+
# KEY = "VALUE"
20+
data = re.match(r'^\s*([\S]+)\s*=\s*"([\S\s]+)"\s*$', line)
21+
if data:
22+
key = data.group(1)
23+
value = data.group(2)
24+
override_settings[key] = value
25+
num_settings += 1
26+
continue
27+
28+
# KEY = 'VALUE'
29+
data = re.match(r"^\s*([\S]+)\s*=\s*'([\S\s]+)'\s*$", line)
30+
if data:
31+
key = data.group(1)
32+
value = data.group(2)
33+
override_settings[key] = value
34+
num_settings += 1
35+
continue
36+
37+
# KEY = VALUE
38+
data = re.match(r'^\s*([\S]+)\s*=\s*([\S]+)\s*$', line)
39+
if data:
40+
key = data.group(1)
41+
value = data.group(2)
42+
override_settings[key] = value
43+
num_settings += 1
44+
continue
45+
46+
for key in override_settings.keys():
47+
value = override_settings[key]
48+
if value.replace('.', '1').isdigit():
49+
if value.count('.') == 1:
50+
override_settings[key] = float(value)
51+
elif value.count('.') == 0:
52+
override_settings[key] = int(value)
53+
else:
54+
continue
55+
elif value == "True":
56+
override_settings[key] = True
57+
elif value == "False":
58+
override_settings[key] = False
59+
else:
60+
override_settings[key] = value
61+
62+
if key == "MINI_TIMEOUT":
63+
settings.MINI_TIMEOUT = override_settings[key]
64+
elif key == "SMALL_TIMEOUT":
65+
settings.SMALL_TIMEOUT = override_settings[key]
66+
elif key == "LARGE_TIMEOUT":
67+
settings.LARGE_TIMEOUT = override_settings[key]
68+
elif key == "EXTREME_TIMEOUT":
69+
settings.EXTREME_TIMEOUT = override_settings[key]
70+
elif key == "ARCHIVE_EXISTING_LOGS":
71+
settings.ARCHIVE_EXISTING_LOGS = override_settings[key]
72+
elif key == "ARCHIVE_EXISTING_DOWNLOADS":
73+
settings.ARCHIVE_EXISTING_DOWNLOADS = override_settings[key]
74+
elif key == "SCREENSHOT_NAME":
75+
settings.SCREENSHOT_NAME = override_settings[key]
76+
elif key == "BASIC_INFO_NAME":
77+
settings.BASIC_INFO_NAME = override_settings[key]
78+
elif key == "PAGE_SOURCE_NAME":
79+
settings.PAGE_SOURCE_NAME = override_settings[key]
80+
elif key == "LATEST_REPORT_DIR":
81+
settings.LATEST_REPORT_DIR = override_settings[key]
82+
elif key == "REPORT_ARCHIVE_DIR":
83+
settings.REPORT_ARCHIVE_DIR = override_settings[key]
84+
elif key == "HTML_REPORT":
85+
settings.HTML_REPORT = override_settings[key]
86+
elif key == "RESULTS_TABLE":
87+
settings.RESULTS_TABLE = override_settings[key]
88+
elif key == "WAIT_FOR_RSC_ON_PAGE_LOADS":
89+
settings.WAIT_FOR_RSC_ON_PAGE_LOADS = override_settings[key]
90+
elif key == "WAIT_FOR_RSC_ON_CLICKS":
91+
settings.WAIT_FOR_RSC_ON_CLICKS = override_settings[key]
92+
elif key == "WAIT_FOR_ANGULARJS":
93+
settings.WAIT_FOR_ANGULARJS = override_settings[key]
94+
elif key == "DEFAULT_DEMO_MODE_TIMEOUT":
95+
settings.DEFAULT_DEMO_MODE_TIMEOUT = override_settings[key]
96+
elif key == "HIGHLIGHTS":
97+
settings.HIGHLIGHTS = override_settings[key]
98+
elif key == "DEFAULT_MESSAGE_DURATION":
99+
settings.DEFAULT_MESSAGE_DURATION = override_settings[key]
100+
elif key == "DISABLE_CSP_ON_FIREFOX":
101+
settings.DISABLE_CSP_ON_FIREFOX = override_settings[key]
102+
elif key == "DISABLE_CSP_ON_CHROME":
103+
settings.DISABLE_CSP_ON_CHROME = override_settings[key]
104+
elif key == "RAISE_INVALID_PROXY_STRING_EXCEPTION":
105+
settings.RAISE_INVALID_PROXY_STRING_EXCEPTION = (
106+
override_settings[key])
107+
elif key == "MASTERQA_DEFAULT_VALIDATION_MESSAGE":
108+
settings.MASTERQA_DEFAULT_VALIDATION_MESSAGE = (
109+
override_settings[key])
110+
elif key == "MASTERQA_WAIT_TIME_BEFORE_VERIFY":
111+
settings.MASTERQA_WAIT_TIME_BEFORE_VERIFY = (
112+
override_settings[key])
113+
elif key == "MASTERQA_START_IN_FULL_SCREEN_MODE":
114+
settings.MASTERQA_START_IN_FULL_SCREEN_MODE = (
115+
override_settings[key])
116+
elif key == "MASTERQA_MAX_IDLE_TIME_BEFORE_QUIT":
117+
settings.MASTERQA_MAX_IDLE_TIME_BEFORE_QUIT = (
118+
override_settings[key])
119+
elif key == "TOTP_KEY":
120+
settings.TOTP_KEY = override_settings[key]
121+
elif key == "DB_HOST":
122+
settings.DB_HOST = override_settings[key]
123+
elif key == "DB_USERNAME":
124+
settings.DB_USERNAME = override_settings[key]
125+
elif key == "DB_PASSWORD":
126+
settings.DB_PASSWORD = override_settings[key]
127+
elif key == "DB_SCHEMA":
128+
settings.DB_SCHEMA = override_settings[key]
129+
elif key == "S3_LOG_BUCKET":
130+
settings.S3_LOG_BUCKET = override_settings[key]
131+
elif key == "S3_BUCKET_URL":
132+
settings.S3_BUCKET_URL = override_settings[key]
133+
elif key == "S3_SELENIUM_ACCESS_KEY":
134+
settings.S3_SELENIUM_ACCESS_KEY = override_settings[key]
135+
elif key == "S3_SELENIUM_SECRET_KEY":
136+
settings.S3_SELENIUM_SECRET_KEY = override_settings[key]
137+
elif key == "ENCRYPTION_KEY":
138+
settings.ENCRYPTION_KEY = override_settings[key]
139+
elif key == "OBFUSCATION_START_TOKEN":
140+
settings.OBFUSCATION_START_TOKEN = override_settings[key]
141+
elif key == "OBFUSCATION_END_TOKEN":
142+
settings.OBFUSCATION_END_TOKEN = override_settings[key]
143+
elif key == "EMAIL_USERNAME":
144+
settings.EMAIL_USERNAME = override_settings[key]
145+
elif key == "EMAIL_PASSWORD":
146+
settings.EMAIL_PASSWORD = override_settings[key]
147+
elif key == "EMAIL_IMAP_STRING":
148+
settings.EMAIL_IMAP_STRING = override_settings[key]
149+
elif key == "EMAIL_IMAP_PORT":
150+
settings.EMAIL_IMAP_PORT = override_settings[key]
151+
else:
152+
continue
153+
154+
if num_settings == 0:
155+
raise Exception("Unable to parse the settings file!")
156+
157+
return override_settings

seleniumbase/fixtures/base_case.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ def test_anything(self):
4747
from seleniumbase.core.testcase_manager import TestcaseManager
4848
from seleniumbase.core import download_helper
4949
from seleniumbase.core import log_helper
50+
from seleniumbase.core import settings_parser
5051
from seleniumbase.core import tour_helper
5152
from seleniumbase.core import visual_helper
5253
from seleniumbase.fixtures import constants
@@ -3195,6 +3196,7 @@ def setUp(self, masterqa_mode=False):
31953196
self.proxy_string = sb_config.proxy_string
31963197
self.user_agent = sb_config.user_agent
31973198
self.cap_file = sb_config.cap_file
3199+
self.settings_file = sb_config.settings_file
31983200
self.database_env = sb_config.database_env
31993201
self.message_duration = sb_config.message_duration
32003202
self.js_checking_on = sb_config.js_checking_on
@@ -3266,11 +3268,16 @@ def setUp(self, masterqa_mode=False):
32663268
# Chrome and Firefox now have built-in headless displays
32673269
pass
32683270

3269-
# Launch WebDriver for both Pytest and Nosetests
3271+
# Verify that SeleniumBase is installed successfully
32703272
if not hasattr(self, "browser"):
32713273
raise Exception("""SeleniumBase plugins did not load! """
32723274
"""Please reinstall using:\n"""
3273-
""" >>> "python setup.py install" <<< """)
3275+
""" >>> "pip install -r requirements.txt" <<<\n"""
3276+
""" >>> "python setup.py develop" <<< """)
3277+
if self.settings_file:
3278+
settings_parser.set_settings(self.settings_file)
3279+
3280+
# Launch WebDriver for both Pytest and Nosetests
32743281
self.driver = self.get_new_driver(browser=self.browser,
32753282
headless=self.headless,
32763283
servername=self.servername,

seleniumbase/plugins/base_plugin.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# -*- coding: utf-8 -*-
2-
""" This is the Nose plugin for saving logs and setting a test environment. """
2+
""" This is the Nose plugin for setting a test environment and saving logs. """
33

44
import os
55
import sys
@@ -16,6 +16,7 @@ class Base(Plugin):
1616
This parser plugin includes the following command-line options for Nose:
1717
--env=ENV (Set a test environment. Use "self.env" to use this in tests.)
1818
--data=DATA (Extra data to pass to tests. Use "self.data" in tests.)
19+
--settings_file=FILE (Overrides SeleniumBase settings.py values.)
1920
--log_path=LOG_PATH (The directory where log files get saved to.)
2021
--archive_logs (Archive old log files instead of deleting them.)
2122
--report (The option to create a fancy report after tests complete.)
@@ -45,6 +46,13 @@ def options(self, parser, env):
4546
'--data', dest='data',
4647
default=None,
4748
help='Extra data to pass from the command line.')
49+
parser.add_option(
50+
'--settings_file', '--settings-file', '--settings',
51+
action='store',
52+
dest='settings_file',
53+
default=None,
54+
help="""The file that stores key/value pairs for overriding
55+
values in the SeleniumBase settings.py file.""")
4856
parser.add_option(
4957
'--log_path', dest='log_path',
5058
default='latest_logs/',
@@ -100,6 +108,7 @@ def beforeTest(self, test):
100108
test.test.environment = self.options.environment
101109
test.test.env = self.options.environment # Add a shortened version
102110
test.test.data = self.options.data
111+
test.test.settings_file = self.options.settings_file
103112
test.test.log_path = self.options.log_path
104113
test.test.args = self.options
105114
test.test.report_on = self.report_on

seleniumbase/plugins/pytest_plugin.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ def pytest_addoption(parser):
1515
This parser plugin includes the following command-line options for pytest:
1616
--browser=BROWSER (The web browser to use.)
1717
--cap_file=FILE (The web browser's desired capabilities to use.)
18+
--settings_file=FILE (Overrides SeleniumBase settings.py values.)
1819
--env=ENV (Set a test environment. Use "self.env" to use this in tests.)
1920
--data=DATA (Extra data to pass to tests. Use "self.data" in tests.)
2021
--user_data_dir=DIR (Set the Chrome user data directory to use.)
@@ -82,6 +83,12 @@ def pytest_addoption(parser):
8283
default=None,
8384
help="""The file that stores browser desired capabilities
8485
for BrowserStack or Sauce Labs web drivers.""")
86+
parser.addoption('--settings_file', '--settings-file', '--settings',
87+
action='store',
88+
dest='settings_file',
89+
default=None,
90+
help="""The file that stores key/value pairs for overriding
91+
values in the SeleniumBase settings.py file.""")
8592
parser.addoption('--user_data_dir', '--user-data-dir',
8693
dest='user_data_dir',
8794
default=None,
@@ -320,6 +327,7 @@ def pytest_configure(config):
320327
sb_config.port = config.getoption('port')
321328
sb_config.proxy_string = config.getoption('proxy_string')
322329
sb_config.cap_file = config.getoption('cap_file')
330+
sb_config.settings_file = config.getoption('settings_file')
323331
sb_config.user_data_dir = config.getoption('user_data_dir')
324332
sb_config.database_env = config.getoption('database_env')
325333
sb_config.log_path = config.getoption('log_path')

0 commit comments

Comments
 (0)