Skip to content

Commit b397777

Browse files
authored
Merge pull request #1382 from seleniumbase/proxy-pac-features-and-bug-fixes
Proxy PAC features and bug fixes
2 parents f6a811c + 8506e12 commit b397777

File tree

15 files changed

+268
-62
lines changed

15 files changed

+268
-62
lines changed

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,8 @@ The code above will leave your browser window open in case there's a failure. (i
440440
--proxy=SERVER:PORT # (Connect to a proxy server:port for tests.)
441441
--proxy=USERNAME:PASSWORD@SERVER:PORT # (Use authenticated proxy server.)
442442
--proxy-bypass-list=STRING # (";"-separated hosts to bypass, Eg "*.foo.com")
443+
--proxy-pac-url=URL # (Connect to a proxy server using a PAC_URL.pac file.)
444+
--proxy-pac-url=USERNAME:PASSWORD@URL # (Authenticated proxy with PAC URL.)
443445
--agent=STRING # (Modify the web browser's User-Agent string.)
444446
--mobile # (Use the mobile device emulator while running tests.)
445447
--metrics=STRING # (Set mobile metrics: "CSSWidth,CSSHeight,PixelRatio".)
@@ -963,13 +965,13 @@ def get_mirror_universe_captain_picard_superbowl_ad(superbowl_year):
963965
self.switch_to_window(1) # This switches to the new tab (0 is the first one)
964966
```
965967
966-
🔵 <b>ProTip™:</b> iFrames follow the same principle as new windows - you need to specify the iFrame if you want to take action on something in there
968+
🔵 <b>ProTip™:</b> iframes follow the same principle as new windows - you need to specify the iframe if you want to take action on something in there
967969
968970
```python
969971
self.switch_to_frame('ContentManagerTextBody_ifr')
970-
# Now you can act inside the iFrame
972+
# Now you can act inside the iframe
971973
# .... Do something cool (here)
972-
self.switch_to_default_content() # Exit the iFrame when you're done
974+
self.switch_to_default_content() # Exit the iframe when you're done
973975
```
974976
975977
🔵 Executing Custom jQuery Scripts:
@@ -1109,3 +1111,4 @@ pytest --reruns=1 --reruns-delay=1
11091111
11101112
<p><a href="https://github.com/seleniumbase/SeleniumBase/"><img src="https://seleniumbase.io/cdn/img/super_logo_sb.png" alt="SeleniumBase" title="SeleniumBase" width="200" /></a></p>
11111113
<p><a href="https://pypi.python.org/pypi/seleniumbase" target="_blank"><img src="https://badge.fury.io/py/seleniumbase.svg" alt="PyPI version" /></a></p>
1114+
<p><a href="https://www.python.org/downloads/" target="_blank"><img src="https://img.shields.io/pypi/pyversions/seleniumbase.svg?color=22AAEE&logo=python" title="Supported Python Versions" /></a></p>

examples/raw_parameter_script.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@
102102
sb.firefox_pref = None
103103
sb.proxy_string = None
104104
sb.proxy_bypass_list = None
105+
sb.proxy_pac_url = None
105106
sb.swiftshader = False
106107
sb.ad_block_on = False
107108
sb.highlights = None

help_docs/customizing_test_runs.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ pytest my_first_test.py --settings-file=custom_settings.py
122122
--proxy=SERVER:PORT # (Connect to a proxy server:port for tests.)
123123
--proxy=USERNAME:PASSWORD@SERVER:PORT # (Use authenticated proxy server.)
124124
--proxy-bypass-list=STRING # (";"-separated hosts to bypass, Eg "*.foo.com")
125+
--proxy-pac-url=URL # (Connect to a proxy server using a PAC_URL.pac file.)
126+
--proxy-pac-url=USERNAME:PASSWORD@URL # (Authenticated proxy with PAC URL.)
125127
--agent=STRING # (Modify the web browser's User-Agent string.)
126128
--mobile # (Use the mobile device emulator while running tests.)
127129
--metrics=STRING # (Set mobile metrics: "CSSWidth,CSSHeight,PixelRatio".)

help_docs/features_list.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,20 @@
99
* Supports multiple browsers, tabs, iframes, and proxies in the same test.
1010
* Automatic smart-waiting improves reliability and prevents flaky tests.
1111
* Supports [pytest](https://docs.pytest.org/en/latest/), [unittest](https://docs.python.org/3/library/unittest.html), [nose](http://nose.readthedocs.io/en/latest/), and [behave](https://behave.readthedocs.io/en/stable/index.html) for finding/running tests.
12-
* All the code is open source. Dive in to see details about any feature.
12+
* All the code is open source. Look inside to learn about any feature.
1313
* Powerful logging tools for [dashboards, reports, and screenshots](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/example_logs/ReadMe.md).
1414
* Can run tests in Headless Mode to hide the browser. (``--headless``)
1515
* Can run tests multithreaded from parallel browsers. (``-n NUM_THREADS``)
1616
* Can run tests from a shared browser session. (``--reuse-session``/``--rs``)
1717
* Can run tests using Chromium's mobile device emulator. (``--mobile``)
1818
* Can run tests through a proxy server. (``--proxy=IP_ADDRESS:PORT``)
19+
* Can run tests with proxy settings via PAC URL. (``--proxy-pac-url=URL.pac``)
1920
* Can run tests through an authenticated proxy server. (``--proxy=USER:PASS@HOST:PORT``)
21+
* Can run tests with proxy+auth via PAC URL. (``--proxy-pac-url=USER:[email protected]``)
2022
* Can run tests with a customized browser user agent. (``--agent=USER_AGENT_STRING``)
21-
* Can set a Chromium User Data Directory/Profile to load. (``--user_data_dir=DIR``)
22-
* Can load Chrome Extension ZIP files. (``--extension_zip=ZIP``)
23-
* Can load Chrome Extension folders. (``--extension_dir=DIR``)
23+
* Can set a Chromium User Data Directory/Profile to load. (``--user-data-dir=DIR``)
24+
* Can load Chrome Extension ZIP files. (``--extension-zip=ZIP``)
25+
* Can load Chrome Extension folders. (``--extension-dir=DIR``)
2426
* Powerful [console scripts](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/console_scripts/ReadMe.md). (Type **``seleniumbase``** or **``sbase``** to use.)
2527
* Has the ability to translate tests into [multiple spoken languages](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/translations).
2628
* Has a flexible [command-line interface](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/customizing_test_runs.md) for customizing test runs.

mkdocs_build/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ ghp-import==2.1.0
1919
readme-renderer==35.0
2020
pymdown-extensions==9.5
2121
importlib-metadata==4.12.0
22-
bleach==5.0.0
22+
bleach==5.0.1
2323
jsmin==3.0.1
2424
lunr==0.6.2
2525
nltk==3.7

requirements.txt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ attrs>=21.4.0
1414
PyYAML>=6.0;python_version>="3.6"
1515
traitlets>=4.3.3;python_version<"3.7"
1616
traitlets>=5.3.0;python_version>="3.7"
17-
certifi>=2021.10.8;python_version<"3.5"
18-
certifi>=2022.5.18;python_version>="3.5" and python_version<"3.6"
17+
certifi>=2021.10.8;python_version<"3.6"
1918
certifi>=2022.6.15;python_version>="3.6"
2019
filelock>=3.2.1;python_version<"3.6"
2120
filelock>=3.4.1;python_version>="3.6" and python_version<"3.7"
@@ -101,7 +100,7 @@ pygments==2.11.2;python_version>="3.5" and python_version<"3.6"
101100
pygments==2.12.0;python_version>="3.6"
102101
prompt-toolkit==1.0.18;python_version<"3.5"
103102
prompt-toolkit==2.0.10;python_version>="3.5" and python_version<"3.6"
104-
prompt-toolkit==3.0.29;python_version>="3.6"
103+
prompt-toolkit==3.0.30;python_version>="3.6"
105104
decorator==4.4.2;python_version<"3.5"
106105
decorator==5.1.1;python_version>="3.5"
107106
ipython==5.10.0;python_version<"3.5"

seleniumbase/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# seleniumbase package
2-
__version__ = "3.3.2"
2+
__version__ = "3.3.3"

seleniumbase/behave/behave_helper.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,14 @@ def generate_gherkin(srt_actions):
1717
action[2] = unquote(action[2], errors="strict")
1818
except Exception:
1919
pass
20-
sb_actions.append('Open "%s"' % action[2])
20+
if '"' not in action[2]:
21+
sb_actions.append('Open "%s"' % action[2])
22+
elif "'" not in action[2]:
23+
sb_actions.append("Open '%s'" % action[2])
24+
else:
25+
sb_actions.append(
26+
'Open "%s"' % action[2].replace('"', '\\"')
27+
)
2128
elif action[0] == "f_url":
2229
if "%" in action[2] and python3:
2330
try:
@@ -26,7 +33,14 @@ def generate_gherkin(srt_actions):
2633
action[2] = unquote(action[2], errors="strict")
2734
except Exception:
2835
pass
29-
sb_actions.append('Open if not "%s"' % action[2])
36+
if '"' not in action[2]:
37+
sb_actions.append('Open if not "%s"' % action[2])
38+
elif "'" not in action[2]:
39+
sb_actions.append("Open if not '%s'" % action[2])
40+
else:
41+
sb_actions.append(
42+
'Open if not "%s"' % action[2].replace('"', '\\"')
43+
)
3044
elif action[0] == "click":
3145
if '"' not in action[1]:
3246
sb_actions.append('Click "%s"' % action[1])

seleniumbase/behave/behave_sb.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
-D proxy=SERVER:PORT (Connect to a proxy server:port for tests.)
3232
-D proxy=USERNAME:PASSWORD@SERVER:PORT (Use authenticated proxy server.)
3333
-D proxy-bypass-list=STRING (";"-separated hosts to bypass, Eg "*.foo.com")
34+
-D proxy-pac-url=URL (Connect to a proxy server using a PAC_URL.pac file.)
35+
-D proxy-pac-url=USERNAME:PASSWORD@URL (Authenticated proxy with PAC URL.)
3436
-D agent=STRING (Modify the web browser's User-Agent string.)
3537
-D mobile (Use the mobile device emulator while running tests.)
3638
-D metrics=STRING (Set mobile metrics: "CSSWidth,CSSHeight,PixelRatio".)
@@ -195,6 +197,7 @@ def get_configured_sb(context):
195197
sb.firefox_pref = None
196198
sb.proxy_string = None
197199
sb.proxy_bypass_list = None
200+
sb.proxy_pac_url = None
198201
sb.swiftshader = False
199202
sb.ad_block_on = False
200203
sb.highlights = None
@@ -619,7 +622,7 @@ def get_configured_sb(context):
619622
sb.firefox_pref = firefox_pref
620623
continue
621624
# Handle: -D proxy=SERVER:PORT / proxy=USERNAME:PASSWORD@SERVER:PORT
622-
if low_key == "proxy":
625+
if low_key in ["proxy", "proxy-server", "proxy-string"]:
623626
proxy_string = userdata[key]
624627
if proxy_string == "true":
625628
proxy_string = sb.proxy_string # revert to default
@@ -632,6 +635,13 @@ def get_configured_sb(context):
632635
proxy_bypass_list = sb.proxy_bypass_list # revert to default
633636
sb.proxy_bypass_list = proxy_bypass_list
634637
continue
638+
# Handle: -D proxy-pac-url=URL / proxy-pac-url=USERNAME:PASSWORD@URL
639+
if low_key in ["proxy-pac-url", "proxy_pac_url", "pac-url", "pac_url"]:
640+
proxy_pac_url = userdata[key]
641+
if proxy_pac_url == "true":
642+
proxy_pac_url = sb.proxy_pac_url # revert to default
643+
sb.proxy_pac_url = proxy_pac_url
644+
continue
635645
# Handle: -D swiftshader
636646
if low_key == "swiftshader":
637647
sb.swiftshader = True

seleniumbase/core/browser_launcher.py

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ def _set_chrome_options(
261261
proxy_user,
262262
proxy_pass,
263263
proxy_bypass_list,
264+
proxy_pac_url,
264265
user_agent,
265266
recorder_ext,
266267
disable_csp,
@@ -458,6 +459,12 @@ def _set_chrome_options(
458459
chrome_options.add_argument(
459460
"--proxy-bypass-list=%s" % proxy_bypass_list
460461
)
462+
elif proxy_pac_url:
463+
if proxy_auth:
464+
chrome_options = _add_chrome_proxy_extension(
465+
chrome_options, None, proxy_user, proxy_pass
466+
)
467+
chrome_options.add_argument("--proxy-pac-url=%s" % proxy_pac_url)
461468
if headless:
462469
if not proxy_auth and not browser_name == constants.Browser.OPERA:
463470
# Headless Chrome doesn't support extensions, which are
@@ -509,6 +516,7 @@ def _set_firefox_options(
509516
locale_code,
510517
proxy_string,
511518
proxy_bypass_list,
519+
proxy_pac_url,
512520
user_agent,
513521
disable_csp,
514522
firefox_arg,
@@ -565,6 +573,9 @@ def _set_firefox_options(
565573
options.set_preference("network.proxy.ssl_port", int(proxy_port))
566574
if proxy_bypass_list:
567575
options.set_preference("no_proxies_on", proxy_bypass_list)
576+
elif proxy_pac_url:
577+
options.set_preference("network.proxy.type", 2)
578+
options.set_preference("network.proxy.autoconfig_url", proxy_pac_url)
568579
if user_agent:
569580
options.set_preference("general.useragent.override", user_agent)
570581
options.set_preference(
@@ -716,6 +727,7 @@ def get_driver(
716727
port=4444,
717728
proxy_string=None,
718729
proxy_bypass_list=None,
730+
proxy_pac_url=None,
719731
user_agent=None,
720732
cap_file=None,
721733
cap_string=None,
@@ -775,6 +787,33 @@ def get_driver(
775787
proxy_string = validate_proxy_string(proxy_string)
776788
if proxy_string and proxy_user and proxy_pass:
777789
proxy_auth = True
790+
elif proxy_pac_url:
791+
username_and_password = None
792+
if "@" in proxy_pac_url:
793+
# Format => username:password@PAC_URL.pac
794+
try:
795+
username_and_password = proxy_pac_url.split("@")[0]
796+
proxy_pac_url = proxy_pac_url.split("@")[1]
797+
proxy_user = username_and_password.split(":")[0]
798+
proxy_pass = username_and_password.split(":")[1]
799+
except Exception:
800+
raise Exception(
801+
"The format for using a PAC URL with authentication "
802+
'is: "username:password@PAC_URL.pac". If using a PAC '
803+
'URL without auth, the format is: "PAC_URL.pac".'
804+
)
805+
if browser_name != constants.Browser.GOOGLE_CHROME and (
806+
browser_name != constants.Browser.EDGE
807+
):
808+
raise Exception(
809+
"Chrome or Edge is required when using a PAC URL "
810+
"that has authentication! (If using a PAC URL "
811+
"without auth, Chrome, Edge, or Firefox may be used.)"
812+
)
813+
if not proxy_pac_url.lower().endswith(".pac"):
814+
raise Exception('The proxy PAC URL must end with ".pac"!')
815+
if proxy_pac_url and proxy_user and proxy_pass:
816+
proxy_auth = True
778817
if browser_name == "chrome" and user_data_dir and len(user_data_dir) < 3:
779818
raise Exception(
780819
"Name length of Chrome's User Data Directory must be >= 3."
@@ -792,6 +831,7 @@ def get_driver(
792831
proxy_user,
793832
proxy_pass,
794833
proxy_bypass_list,
834+
proxy_pac_url,
795835
user_agent,
796836
cap_file,
797837
cap_string,
@@ -833,6 +873,7 @@ def get_driver(
833873
proxy_user,
834874
proxy_pass,
835875
proxy_bypass_list,
876+
proxy_pac_url,
836877
user_agent,
837878
recorder_ext,
838879
disable_csp,
@@ -874,6 +915,7 @@ def get_remote_driver(
874915
proxy_user,
875916
proxy_pass,
876917
proxy_bypass_list,
918+
proxy_pac_url,
877919
user_agent,
878920
cap_file,
879921
cap_string,
@@ -969,6 +1011,7 @@ def get_remote_driver(
9691011
proxy_user,
9701012
proxy_pass,
9711013
proxy_bypass_list,
1014+
proxy_pac_url,
9721015
user_agent,
9731016
recorder_ext,
9741017
disable_csp,
@@ -1050,6 +1093,7 @@ def get_remote_driver(
10501093
locale_code,
10511094
proxy_string,
10521095
proxy_bypass_list,
1096+
proxy_pac_url,
10531097
user_agent,
10541098
disable_csp,
10551099
firefox_arg,
@@ -1175,6 +1219,7 @@ def get_remote_driver(
11751219
proxy_user,
11761220
proxy_pass,
11771221
proxy_bypass_list,
1222+
proxy_pac_url,
11781223
user_agent,
11791224
recorder_ext,
11801225
disable_csp,
@@ -1356,6 +1401,7 @@ def get_local_driver(
13561401
proxy_user,
13571402
proxy_pass,
13581403
proxy_bypass_list,
1404+
proxy_pac_url,
13591405
user_agent,
13601406
recorder_ext,
13611407
disable_csp,
@@ -1396,6 +1442,7 @@ def get_local_driver(
13961442
locale_code,
13971443
proxy_string,
13981444
proxy_bypass_list,
1445+
proxy_pac_url,
13991446
user_agent,
14001447
disable_csp,
14011448
firefox_arg,
@@ -1704,10 +1751,16 @@ def get_local_driver(
17041751
edge_options, proxy_string, proxy_user, proxy_pass
17051752
)
17061753
edge_options.add_argument("--proxy-server=%s" % proxy_string)
1707-
if proxy_bypass_list:
1708-
edge_options.add_argument(
1709-
"--proxy-bypass-list=%s" % proxy_bypass_list
1710-
)
1754+
if proxy_bypass_list:
1755+
edge_options.add_argument(
1756+
"--proxy-bypass-list=%s" % proxy_bypass_list
1757+
)
1758+
elif proxy_pac_url:
1759+
if proxy_auth:
1760+
edge_options = _add_chrome_proxy_extension(
1761+
edge_options, None, proxy_user, proxy_pass
1762+
)
1763+
edge_options.add_argument("--proxy-pac-url=%s" % proxy_pac_url)
17111764
edge_options.add_argument("--test-type")
17121765
edge_options.add_argument("--log-level=3")
17131766
edge_options.add_argument("--no-first-run")
@@ -1862,6 +1915,7 @@ def get_local_driver(
18621915
proxy_user,
18631916
proxy_pass,
18641917
proxy_bypass_list,
1918+
proxy_pac_url,
18651919
user_agent,
18661920
recorder_ext,
18671921
disable_csp,
@@ -1917,6 +1971,7 @@ def get_local_driver(
19171971
proxy_user,
19181972
proxy_pass,
19191973
proxy_bypass_list,
1974+
proxy_pac_url,
19201975
user_agent,
19211976
recorder_ext,
19221977
disable_csp,
@@ -2036,6 +2091,7 @@ def get_local_driver(
20362091
proxy_user,
20372092
proxy_pass,
20382093
proxy_bypass_list,
2094+
proxy_pac_url,
20392095
user_agent,
20402096
recorder_ext,
20412097
disable_csp,

0 commit comments

Comments
 (0)