From 69700823949895e97c87eb3203a2de3e44981a48 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Sun, 31 Dec 2023 18:31:45 -0500 Subject: [PATCH 1/5] Add YAML option for parsing capabilities --- seleniumbase/core/capabilities_parser.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/seleniumbase/core/capabilities_parser.py b/seleniumbase/core/capabilities_parser.py index 1f8a124b32c..a1dab1cd248 100644 --- a/seleniumbase/core/capabilities_parser.py +++ b/seleniumbase/core/capabilities_parser.py @@ -1,6 +1,7 @@ import re import ast import json +import yaml # Requires pyyaml def _analyze_ast(contents): @@ -183,28 +184,33 @@ def _read_file(file): def _parse_py_file(cap_file): all_code = _read_file(cap_file) capabilities = _analyze_ast(all_code) - if not capabilities: capabilities = _analyze_manual(all_code) - return capabilities def _parse_json_file(cap_file): all_code = _read_file(cap_file) - return json.loads(all_code) +def _parse_yaml_file(cap_file): + all_code = _read_file(cap_file) + return yaml.safe_load(all_code) + + def get_desired_capabilities(cap_file): if cap_file.endswith(".py"): capabilities = _parse_py_file(cap_file) elif cap_file.endswith(".json"): capabilities = _parse_json_file(cap_file) + elif (cap_file.endswith(".yml") or cap_file.endswith(".yaml")): + capabilities = _parse_yaml_file(cap_file) else: - raise Exception("\n\n`%s` is not a Python or JSON file!\n" % cap_file) - + raise Exception( + '\n\n`%s` must end in ".py", ".json", ".yml", or ".yaml"!\n' + % cap_file + ) if len(capabilities.keys()) == 0: raise Exception("Unable to parse desired capabilities file!") - return capabilities From c4771b17de4f1f7a0673a52872d231f5f2b023b1 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Sun, 31 Dec 2023 18:33:05 -0500 Subject: [PATCH 2/5] Update sample capabilities files --- examples/capabilities/sample_cap_file_BS.py | 13 +++++++------ examples/capabilities/sample_cap_file_BS.yml | 12 ++++++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 examples/capabilities/sample_cap_file_BS.yml diff --git a/examples/capabilities/sample_cap_file_BS.py b/examples/capabilities/sample_cap_file_BS.py index 50c9773a894..41218235e10 100644 --- a/examples/capabilities/sample_cap_file_BS.py +++ b/examples/capabilities/sample_cap_file_BS.py @@ -1,11 +1,12 @@ -# Desired capabilities example file for BrowserStack -# Generate from https://www.browserstack.com/automate/capabilities +# Desired capabilities example .py file for BrowserStack: +# https://www.browserstack.com/docs/automate/capabilities desired_cap = { + "browser": "Chrome", "os": "Windows", "os_version": "11", - "browser": "Chrome", - "browser_version": "101.0", - "browserstack.local": "false", + "browser_version": "latest", + "browserstack.console": "info", "browserstack.debug": "true", - "browserstack.selenium_version": "4.1.2", + "browserstack.networkLogs": "true", + "browserstack.local": "true", } diff --git a/examples/capabilities/sample_cap_file_BS.yml b/examples/capabilities/sample_cap_file_BS.yml new file mode 100644 index 00000000000..c1b3a9a0edd --- /dev/null +++ b/examples/capabilities/sample_cap_file_BS.yml @@ -0,0 +1,12 @@ +# Desired capabilities example YML file for BrowserStack: +# https://www.browserstack.com/docs/automate/capabilities +platforms: + - browserName: safari + osVersion: 17 + deviceName: iPhone 15 Pro Max +buildIdentifier: ${BUILD_NUMBER} +parallelsPerPlatform: 1 +projectName: My Project +browserstackLocal: true +debug: true +networkLogs: true From 0f9d65d3bd8f0efb5ed5c03de0d1ee968aaac01c Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Sun, 31 Dec 2023 18:35:57 -0500 Subject: [PATCH 3/5] Update the documentation --- examples/capabilities/ReadMe.md | 47 ++++++++++++++++++++----------- help_docs/chinese.md | 31 ++++++++++++-------- help_docs/desired_capabilities.md | 38 +++++++++++++++++-------- mkdocs_build/requirements.txt | 9 +++--- 4 files changed, 81 insertions(+), 44 deletions(-) diff --git a/examples/capabilities/ReadMe.md b/examples/capabilities/ReadMe.md index a50dacb552e..6f38c96f0b7 100644 --- a/examples/capabilities/ReadMe.md +++ b/examples/capabilities/ReadMe.md @@ -1,8 +1,8 @@ -

Using Desired Capabilities

+## [](https://github.com/seleniumbase/SeleniumBase/) Using Desired Capabilities -You can specify browser capabilities when running SeleniumBase tests on a remote Selenium Grid server such as BrowserStack, Sauce Labs, or another. +You can specify browser capabilities when running SeleniumBase tests on a remote Selenium Grid server (such as BrowserStack or Sauce Labs). Sample run commands may look like this when run from the [SeleniumBase/examples/](https://github.com/seleniumbase/SeleniumBase/tree/master/examples) folder: (The browser is now specified in the capabilities file.) @@ -16,17 +16,33 @@ pytest test_demo_site.py --browser=remote --server=USERNAME:KEY@ondemand.us-east (Parameters: ``--browser=remote``, ``--server=SERVER``, ``--port=PORT``, ``--protocol=PROTOCOL``, and ``--cap_file=CAP_FILE.py``) -Here's an example desired capabilities file for BrowserStack: +Here's an example desired capabilities file for BrowserStack using the newer SDK format in a `.yml` / `.yaml` file: + +```yml +platforms: + - browserName: safari + osVersion: 17 + deviceName: iPhone 15 Pro Max +buildIdentifier: ${BUILD_NUMBER} +parallelsPerPlatform: 1 +projectName: My Project +browserstackLocal: true +debug: true +networkLogs: true +``` + +Here's an example desired capabilities file for BrowserStack using the legacy JSONWP format in a `.py` file: ```python desired_cap = { - "os" : "Windows", - "os_version" : "11", - "browser" : "Chrome", - "browser_version" : "101.0", - "browserstack.local" : "false", - "browserstack.debug" : "true", - "browserstack.selenium_version" : "4.1.2", + "browser": "Chrome", + "os": "Windows", + "os_version": "11", + "browser_version": "latest", + "browserstack.console": "info", + "browserstack.debug": "true", + "browserstack.networkLogs": "true", + "browserstack.local": "true", } ``` @@ -41,12 +57,12 @@ capabilities = { } ``` -(Note that the browser is now being specified in the capabilities file, rather than with ``--browser=BROWSER`` when using a **remote** Selenium Grid. If using a **local** Selenium Grid, specify the browser, eg: ``--browser=chrome`` or ``--browser=firefox``.) +(Note that the browser is now being specified in the capabilities file, rather than with ``--BROWSER`` when using a **remote** Selenium Grid. If using a **local** Selenium Grid, specify the browser, eg: ``--firefox``.)
You can generate specific desired capabilities using:
@@ -65,7 +81,7 @@ caps['KEY'] = False (Each pair must be on a separate line. You can interchange single and double quotes.) -You can also swap ``--browser=remote`` with an actual browser, eg ``--browser=chrome``, which will combine the default SeleniumBase desired capabilities with those that were specified in the capabilities file when using ``--cap_file=FILE.py``. Capabilities will override other parameters, so if you set the browser to one thing and the capabilities browser to another, SeleniumBase will use the capabilities browser as the browser. +You can also swap ``--browser=remote`` with an actual browser, eg ``--browser=chrome``, which will combine the default SeleniumBase desired capabilities with those that were specified in the capabilities file when using ``--cap_file=FILE.py``. Capabilities will override other parameters, so if you set the browser to one thing and the capabilities browser to another, SeleniumBase will use the capabilities browser. You'll need default SeleniumBase capabilities for: * Using a proxy server (not the same as a Selenium Grid server) @@ -74,8 +90,7 @@ You'll need default SeleniumBase capabilities for: * Overriding a website's Content Security Policy on Chrome * Other possible reasons -You can also set browser desired capabilities from a command line string: -Example: +You can also set browser desired capabilities from a command-line string. Eg: ```bash pytest test_swag_labs.py --cap-string='{"browserName":"chrome","name":"test1"}' --server="127.0.0.1" --browser=remote @@ -83,7 +98,7 @@ pytest test_swag_labs.py --cap-string='{"browserName":"chrome","name":"test1"}' (Enclose cap-string in single quotes. Enclose parameter keys in double quotes.) -If you pass ``"*"`` into the ``"name"`` field of ``--cap-string``, the name will become the test identifier. Example: +If you pass ``"*"`` into the ``"name"`` field of ``--cap-string``, the name will become the test identifier. Eg: ```bash pytest my_first_test.py --cap-string='{"browserName":"chrome","name":"*"}' --server="127.0.0.1" --browser=chrome diff --git a/help_docs/chinese.md b/help_docs/chinese.md index 45260c6aa58..fa1654bbfbf 100644 --- a/help_docs/chinese.md +++ b/help_docs/chinese.md @@ -114,6 +114,7 @@ pytest my_first_test.py --demo ```python from seleniumbase import BaseCase +BaseCase.main(__name__, __file__) class MyTestClass(BaseCase): def test_swag_labs(self): @@ -121,20 +122,21 @@ class MyTestClass(BaseCase): self.type("#user-name", "standard_user") self.type("#password", "secret_sauce\n") self.assert_element("div.inventory_list") - self.assert_text("PRODUCTS", "span.title") + self.assert_exact_text("Products", "span.title") self.click('button[name*="backpack"]') self.click("#shopping_cart_container a") - self.assert_text("YOUR CART", "span.title") + self.assert_exact_text("Your Cart", "span.title") self.assert_text("Backpack", "div.cart_item") self.click("button#checkout") self.type("#first-name", "SeleniumBase") self.type("#last-name", "Automation") self.type("#postal-code", "77123") self.click("input#continue") - self.assert_text("CHECKOUT: OVERVIEW") + self.assert_text("Checkout: Overview") self.assert_text("Backpack", "div.cart_item") + self.assert_text("29.99", "div.inventory_item_price") self.click("button#finish") - self.assert_exact_text("THANK YOU FOR YOUR ORDER", "h2") + self.assert_exact_text("Thank you for your order!", "h2") self.assert_element('img[alt="Pony Express"]') self.js_click("a#logout_sidebar_link") self.assert_element("div#login_button_container") @@ -173,23 +175,28 @@ self.save_screenshot(FILE_NAME) # 保存当前页面的截图 ```python from seleniumbase.translate.chinese import 硒测试用例 +硒测试用例.main(__name__, __file__) class 我的测试类(硒测试用例): def test_例子1(self): self.开启("https://zh.wikipedia.org/wiki/") self.断言标题("维基百科,自由的百科全书") - self.断言元素('a[title="首页"]') + self.断言元素('a[title="Wikipedia:关于"]') + self.断言元素('span:contains("创建账号")') + self.断言元素('span:contains("登录")') self.断言文本("新闻动态", "span#新闻动态") - self.输入文本("#searchInput", "舞龍") - self.单击("#searchButton") + self.输入文本('input[name="search"]', "舞龍") + self.单击('button:contains("搜索")') self.断言文本("舞龍", "#firstHeading") self.断言元素('img[src*="Chinese_draak.jpg"]') - self.输入文本("#searchInput", "麻婆豆腐") - self.单击("#searchButton") + self.回去() + self.输入文本('input[name="search"]', "麻婆豆腐") + self.单击('button:contains("搜索")') self.断言文本("麻婆豆腐", "#firstHeading") - self.断言元素('div.thumb div:contains("一家中餐館的麻婆豆腐")') - self.输入文本("#searchInput", "精武英雄") - self.单击("#searchButton") + self.断言元素('figure:contains("一家中餐館的麻婆豆腐")') + self.回去() + self.输入文本('input[name="search"]', "精武英雄") + self.单击('button:contains("搜索")') self.断言元素('img[src*="Fist_of_legend.jpg"]') self.断言文本("李连杰", 'li a[title="李连杰"]') ``` diff --git a/help_docs/desired_capabilities.md b/help_docs/desired_capabilities.md index 768db3fd6d0..f7183d6b2d5 100644 --- a/help_docs/desired_capabilities.md +++ b/help_docs/desired_capabilities.md @@ -2,7 +2,7 @@ ## [](https://github.com/seleniumbase/SeleniumBase/) Using Desired Capabilities -You can specify browser capabilities when running SeleniumBase tests on a remote Selenium Grid server such as BrowserStack or Sauce Labs. +You can specify browser capabilities when running SeleniumBase tests on a remote Selenium Grid server (such as BrowserStack or Sauce Labs). Sample run commands may look like this when run from the [SeleniumBase/examples/](https://github.com/seleniumbase/SeleniumBase/tree/master/examples) folder: (The browser is now specified in the capabilities file.) @@ -16,17 +16,33 @@ pytest test_demo_site.py --browser=remote --server=USERNAME:KEY@ondemand.us-east (Parameters: ``--browser=remote``, ``--server=SERVER``, ``--port=PORT``, and ``--cap_file=CAP_FILE.py``) -Here's an example desired capabilities file for BrowserStack: +Here's an example desired capabilities file for BrowserStack using the newer SDK format in a `.yml` / `.yaml` file: + +```yml +platforms: + - browserName: safari + osVersion: 17 + deviceName: iPhone 15 Pro Max +buildIdentifier: ${BUILD_NUMBER} +parallelsPerPlatform: 1 +projectName: My Project +browserstackLocal: true +debug: true +networkLogs: true +``` + +Here's an example desired capabilities file for BrowserStack using the legacy JSONWP format in a `.py` file: ```python desired_cap = { - "os" : "Windows", - "os_version" : "11", - "browser" : "Chrome", - "browser_version" : "101.0", - "browserstack.local" : "false", - "browserstack.debug" : "true", - "browserstack.selenium_version" : "4.1.2", + "browser": "Chrome", + "os": "Windows", + "os_version": "11", + "browser_version": "latest", + "browserstack.console": "info", + "browserstack.debug": "true", + "browserstack.networkLogs": "true", + "browserstack.local": "true", } ``` @@ -46,7 +62,7 @@ capabilities = {
You can generate specific desired capabilities using:
@@ -82,7 +98,7 @@ pytest test_swag_labs.py --cap-string='{"browserName":"chrome","name":"test1"}' (Enclose cap-string in single quotes. Enclose parameter keys in double quotes.) -If you pass ``"*"`` into the ``"name"`` field of ``--cap-string``, the name will become the test identifier. Example: +If you pass ``"*"`` into the ``"name"`` field of ``--cap-string``, the name will become the test identifier. Eg: ```bash pytest my_first_test.py --cap-string='{"browserName":"chrome","name":"*"}' --server="127.0.0.1" --browser=chrome diff --git a/mkdocs_build/requirements.txt b/mkdocs_build/requirements.txt index 56c36af0f86..7921b55a513 100644 --- a/mkdocs_build/requirements.txt +++ b/mkdocs_build/requirements.txt @@ -1,9 +1,8 @@ # mkdocs dependencies for generating the seleniumbase.io website # Minimum Python version: 3.8 (for generating docs only) -regex>=2023.10.3 -PyYAML>=6.0.1 -pymdown-extensions>=10.5 +regex>=2023.12.25 +pymdown-extensions>=10.7 pipdeptree>=2.13.1 python-dateutil>=2.8.2 Markdown==3.5.1 @@ -17,11 +16,11 @@ cairocffi==1.6.1 pathspec==0.12.1 Babel==2.14.0 paginate==0.5.6 -lxml==4.9.4 +lxml==5.0.0 pyquery==2.0.0 readtime==3.0.0 mkdocs==1.5.3 -mkdocs-material==9.5.2 +mkdocs-material==9.5.3 mkdocs-exclude-search==0.6.6 mkdocs-simple-hooks==0.1.5 mkdocs-material-extensions==1.3.1 From 060190a7d8eff07424e2df50600e55486df286b8 Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Sun, 31 Dec 2023 18:37:42 -0500 Subject: [PATCH 4/5] Refresh Python dependencies --- requirements.txt | 7 ++++--- seleniumbase/fixtures/base_case.py | 7 ++++++- setup.py | 14 ++++++++++---- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/requirements.txt b/requirements.txt index 7504a56e2f1..7329a17ee39 100755 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ packaging>=23.2 setuptools>=68.0.0;python_version<"3.8" setuptools>=69.0.3;python_version>="3.8" wheel>=0.42.0 -attrs>=23.1.0 +attrs>=23.2.0 certifi>=2023.11.17 filelock>=3.12.2;python_version<"3.8" filelock>=3.13.1;python_version>="3.8" @@ -11,6 +11,7 @@ platformdirs>=4.0.0;python_version<"3.8" platformdirs>=4.1.0;python_version>="3.8" parse>=1.20.0 parse-type>=0.6.2 +pyyaml>=6.0.1 six==1.16.0 idna==3.6 chardet==5.2.0 @@ -36,7 +37,7 @@ iniconfig==2.0.0 pluggy==1.2.0;python_version<"3.8" pluggy==1.3.0;python_version>="3.8" py==1.11.0 -pytest==7.4.3 +pytest==7.4.4 pytest-html==2.0.1 pytest-metadata==3.0.0 pytest-ordering==0.6 @@ -65,7 +66,7 @@ rich==13.7.0 coverage==6.2;python_version<"3.7" coverage==7.2.7;python_version>="3.7" and python_version<"3.8" -coverage==7.3.4;python_version>="3.8" +coverage==7.4.0;python_version>="3.8" pytest-cov==4.0.0;python_version<"3.7" pytest-cov==4.1.0;python_version>="3.7" flake8==5.0.4;python_version<"3.9" diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py index f8bc8aefaf7..1c89db100e3 100644 --- a/seleniumbase/fixtures/base_case.py +++ b/seleniumbase/fixtures/base_case.py @@ -6650,7 +6650,12 @@ def get_pdf_text( try: from pdfminer.high_level import extract_text except Exception: - shared_utils.pip_install("pdfminer.six") + if not sys.version_info >= (3, 8): + shared_utils.pip_install( + "pdfminer.six", version="20221105" + ) + else: + shared_utils.pip_install("pdfminer.six") from pdfminer.high_level import extract_text if not password: password = "" diff --git a/setup.py b/setup.py index 286efcaf0bd..1d734c2e784 100755 --- a/setup.py +++ b/setup.py @@ -136,7 +136,7 @@ 'setuptools>=68.0.0;python_version<"3.8"', 'setuptools>=69.0.3;python_version>="3.8"', 'wheel>=0.42.0', - 'attrs>=23.1.0', + 'attrs>=23.2.0', "certifi>=2023.11.17", 'filelock>=3.12.2;python_version<"3.8"', 'filelock>=3.13.1;python_version>="3.8"', @@ -144,6 +144,7 @@ 'platformdirs>=4.1.0;python_version>="3.8"', 'parse>=1.20.0', 'parse-type>=0.6.2', + 'pyyaml>=6.0.1', "six==1.16.0", "idna==3.6", 'chardet==5.2.0', @@ -169,7 +170,7 @@ 'pluggy==1.2.0;python_version<"3.8"', 'pluggy==1.3.0;python_version>="3.8"', "py==1.11.0", - 'pytest==7.4.3', + 'pytest==7.4.4', "pytest-html==2.0.1", # Newer ones had issues 'pytest-metadata==3.0.0', "pytest-ordering==0.6", @@ -206,7 +207,7 @@ # Usage: coverage run -m pytest; coverage html; coverage report "coverage": [ 'coverage==7.2.7;python_version<"3.8"', - 'coverage==7.3.4;python_version>="3.8"', + 'coverage==7.4.0;python_version>="3.8"', 'pytest-cov==4.1.0', ], # pip install -e .[flake8] @@ -229,7 +230,8 @@ # pip install -e .[pdfminer] # (An optional library for parsing PDF files.) "pdfminer": [ - 'pdfminer.six==20221105', + 'pdfminer.six==20221105;python_version<"3.8"', + 'pdfminer.six==20231228;python_version>="3.8"', 'cryptography==39.0.2;python_version<"3.9"', 'cryptography==41.0.7;python_version>="3.9"', 'cffi==1.15.1;python_version<"3.8"', @@ -247,6 +249,10 @@ "psutil": [ "psutil==5.9.6", ], + # pip install -e .[selenium-stealth] + "selenium-stealth": [ + 'selenium-stealth==1.0.6', + ], # pip install -e .[selenium-wire] "selenium-wire": [ 'selenium-wire==5.1.0', From e72ae5fb3165d7194d85da4c174b472bd5bea6ff Mon Sep 17 00:00:00 2001 From: Michael Mintz Date: Sun, 31 Dec 2023 18:38:27 -0500 Subject: [PATCH 5/5] Version 4.22.4 --- seleniumbase/__version__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py index fb51e17fd7c..7354291cd07 100755 --- a/seleniumbase/__version__.py +++ b/seleniumbase/__version__.py @@ -1,2 +1,2 @@ # seleniumbase package -__version__ = "4.22.3" +__version__ = "4.22.4"