Skip to content

Commit 36d410b

Browse files
authored
Merge pull request #2405 from seleniumbase/add-yaml-option-for-capabilities
Add YAML option for capabilities
2 parents 2036a7b + e72ae5f commit 36d410b

File tree

11 files changed

+133
-65
lines changed

11 files changed

+133
-65
lines changed

examples/capabilities/ReadMe.md

+31-16
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<!-- SeleniumBase Docs -->
22

3-
<h3><img src="https://seleniumbase.github.io/img/green_logo.png" title="SeleniumBase" width="32" /> Using Desired Capabilities</h3>
3+
## [<img src="https://seleniumbase.github.io/img/logo6.png" title="SeleniumBase" width="32">](https://github.com/seleniumbase/SeleniumBase/) Using Desired Capabilities
44

5-
You can specify browser capabilities when running SeleniumBase tests on a remote Selenium Grid server such as <a href="https://www.browserstack.com/automate/capabilities" target="_blank">BrowserStack</a>, <a href="https://saucelabs.com/products/platform-configurator" target="_blank">Sauce Labs</a>, or another.
5+
You can specify browser capabilities when running SeleniumBase tests on a remote Selenium Grid server (such as <a href="https://www.browserstack.com/automate/capabilities" target="_blank">BrowserStack</a> or <a href="https://saucelabs.com/products/platform-configurator" target="_blank">Sauce Labs</a>).
66

77
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.)
88

@@ -16,17 +16,33 @@ pytest test_demo_site.py --browser=remote --server=USERNAME:[email protected]
1616

1717
(Parameters: ``--browser=remote``, ``--server=SERVER``, ``--port=PORT``, ``--protocol=PROTOCOL``, and ``--cap_file=CAP_FILE.py``)
1818

19-
Here's an example desired capabilities file for BrowserStack:
19+
Here's an example desired capabilities file for BrowserStack using the newer SDK format in a `.yml` / `.yaml` file:
20+
21+
```yml
22+
platforms:
23+
- browserName: safari
24+
osVersion: 17
25+
deviceName: iPhone 15 Pro Max
26+
buildIdentifier: ${BUILD_NUMBER}
27+
parallelsPerPlatform: 1
28+
projectName: My Project
29+
browserstackLocal: true
30+
debug: true
31+
networkLogs: true
32+
```
33+
34+
Here's an example desired capabilities file for BrowserStack using the legacy JSONWP format in a `.py` file:
2035

2136
```python
2237
desired_cap = {
23-
"os" : "Windows",
24-
"os_version" : "11",
25-
"browser" : "Chrome",
26-
"browser_version" : "101.0",
27-
"browserstack.local" : "false",
28-
"browserstack.debug" : "true",
29-
"browserstack.selenium_version" : "4.1.2",
38+
"browser": "Chrome",
39+
"os": "Windows",
40+
"os_version": "11",
41+
"browser_version": "latest",
42+
"browserstack.console": "info",
43+
"browserstack.debug": "true",
44+
"browserstack.networkLogs": "true",
45+
"browserstack.local": "true",
3046
}
3147
```
3248

@@ -41,12 +57,12 @@ capabilities = {
4157
}
4258
```
4359

44-
(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``.)
60+
(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``.)
4561

4662
<div><b>You can generate specific desired capabilities using:</b></div>
4763

4864
<ul>
49-
<li><a href="https://www.browserstack.com/automate/capabilities" target="_blank">BrowserStack desired capabilities</a></li>
65+
<li><a href="https://www.browserstack.com/docs/automate/capabilities" target="_blank">BrowserStack desired capabilities</a></li>
5066
<li><a href="https://saucelabs.com/products/platform-configurator" target="_blank">Sauce Labs desired capabilities</a></li>
5167
</ul>
5268

@@ -65,7 +81,7 @@ caps['KEY'] = False
6581

6682
(Each pair must be on a separate line. You can interchange single and double quotes.)
6783

68-
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.
84+
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.
6985

7086
You'll need default SeleniumBase capabilities for:
7187
* Using a proxy server (not the same as a Selenium Grid server)
@@ -74,16 +90,15 @@ You'll need default SeleniumBase capabilities for:
7490
* Overriding a website's Content Security Policy on Chrome
7591
* Other possible reasons
7692

77-
You can also set browser desired capabilities from a command line string:
78-
Example:
93+
You can also set browser desired capabilities from a command-line string. Eg:
7994

8095
```bash
8196
pytest test_swag_labs.py --cap-string='{"browserName":"chrome","name":"test1"}' --server="127.0.0.1" --browser=remote
8297
```
8398

8499
(Enclose cap-string in single quotes. Enclose parameter keys in double quotes.)
85100

86-
If you pass ``"*"`` into the ``"name"`` field of ``--cap-string``, the name will become the test identifier. Example:
101+
If you pass ``"*"`` into the ``"name"`` field of ``--cap-string``, the name will become the test identifier. Eg:
87102

88103
```bash
89104
pytest my_first_test.py --cap-string='{"browserName":"chrome","name":"*"}' --server="127.0.0.1" --browser=chrome
+7-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
# Desired capabilities example file for BrowserStack
2-
# Generate from https://www.browserstack.com/automate/capabilities
1+
# Desired capabilities example .py file for BrowserStack:
2+
# https://www.browserstack.com/docs/automate/capabilities
33
desired_cap = {
4+
"browser": "Chrome",
45
"os": "Windows",
56
"os_version": "11",
6-
"browser": "Chrome",
7-
"browser_version": "101.0",
8-
"browserstack.local": "false",
7+
"browser_version": "latest",
8+
"browserstack.console": "info",
99
"browserstack.debug": "true",
10-
"browserstack.selenium_version": "4.1.2",
10+
"browserstack.networkLogs": "true",
11+
"browserstack.local": "true",
1112
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Desired capabilities example YML file for BrowserStack:
2+
# https://www.browserstack.com/docs/automate/capabilities
3+
platforms:
4+
- browserName: safari
5+
osVersion: 17
6+
deviceName: iPhone 15 Pro Max
7+
buildIdentifier: ${BUILD_NUMBER}
8+
parallelsPerPlatform: 1
9+
projectName: My Project
10+
browserstackLocal: true
11+
debug: true
12+
networkLogs: true

help_docs/chinese.md

+19-12
Original file line numberDiff line numberDiff line change
@@ -114,27 +114,29 @@ pytest my_first_test.py --demo
114114

115115
```python
116116
from seleniumbase import BaseCase
117+
BaseCase.main(__name__, __file__)
117118

118119
class MyTestClass(BaseCase):
119120
def test_swag_labs(self):
120121
self.open("https://www.saucedemo.com")
121122
self.type("#user-name", "standard_user")
122123
self.type("#password", "secret_sauce\n")
123124
self.assert_element("div.inventory_list")
124-
self.assert_text("PRODUCTS", "span.title")
125+
self.assert_exact_text("Products", "span.title")
125126
self.click('button[name*="backpack"]')
126127
self.click("#shopping_cart_container a")
127-
self.assert_text("YOUR CART", "span.title")
128+
self.assert_exact_text("Your Cart", "span.title")
128129
self.assert_text("Backpack", "div.cart_item")
129130
self.click("button#checkout")
130131
self.type("#first-name", "SeleniumBase")
131132
self.type("#last-name", "Automation")
132133
self.type("#postal-code", "77123")
133134
self.click("input#continue")
134-
self.assert_text("CHECKOUT: OVERVIEW")
135+
self.assert_text("Checkout: Overview")
135136
self.assert_text("Backpack", "div.cart_item")
137+
self.assert_text("29.99", "div.inventory_item_price")
136138
self.click("button#finish")
137-
self.assert_exact_text("THANK YOU FOR YOUR ORDER", "h2")
139+
self.assert_exact_text("Thank you for your order!", "h2")
138140
self.assert_element('img[alt="Pony Express"]')
139141
self.js_click("a#logout_sidebar_link")
140142
self.assert_element("div#login_button_container")
@@ -173,23 +175,28 @@ self.save_screenshot(FILE_NAME) # 保存当前页面的截图
173175

174176
```python
175177
from seleniumbase.translate.chinese import 硒测试用例
178+
硒测试用例.main(__name__, __file__)
176179

177180
class 我的测试类(硒测试用例):
178181
def test_例子1(self):
179182
self.开启("https://zh.wikipedia.org/wiki/")
180183
self.断言标题("维基百科,自由的百科全书")
181-
self.断言元素('a[title="首页"]')
184+
self.断言元素('a[title="Wikipedia:关于"]')
185+
self.断言元素('span:contains("创建账号")')
186+
self.断言元素('span:contains("登录")')
182187
self.断言文本("新闻动态", "span#新闻动态")
183-
self.输入文本("#searchInput", "舞龍")
184-
self.单击("#searchButton")
188+
self.输入文本('input[name="search"]', "舞龍")
189+
self.单击('button:contains("搜索")')
185190
self.断言文本("舞龍", "#firstHeading")
186191
self.断言元素('img[src*="Chinese_draak.jpg"]')
187-
self.输入文本("#searchInput", "麻婆豆腐")
188-
self.单击("#searchButton")
192+
self.回去()
193+
self.输入文本('input[name="search"]', "麻婆豆腐")
194+
self.单击('button:contains("搜索")')
189195
self.断言文本("麻婆豆腐", "#firstHeading")
190-
self.断言元素('div.thumb div:contains("一家中餐館的麻婆豆腐")')
191-
self.输入文本("#searchInput", "精武英雄")
192-
self.单击("#searchButton")
196+
self.断言元素('figure:contains("一家中餐館的麻婆豆腐")')
197+
self.回去()
198+
self.输入文本('input[name="search"]', "精武英雄")
199+
self.单击('button:contains("搜索")')
193200
self.断言元素('img[src*="Fist_of_legend.jpg"]')
194201
self.断言文本("李连杰", 'li a[title="李连杰"]')
195202
```

help_docs/desired_capabilities.md

+27-11
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## [<img src="https://seleniumbase.github.io/img/logo6.png" title="SeleniumBase" width="32">](https://github.com/seleniumbase/SeleniumBase/) Using Desired Capabilities
44

5-
You can specify browser capabilities when running SeleniumBase tests on a remote Selenium Grid server such as <a href="https://www.browserstack.com/automate/capabilities" target="_blank">BrowserStack</a> or <a href="https://saucelabs.com/products/platform-configurator" target="_blank">Sauce Labs</a>.
5+
You can specify browser capabilities when running SeleniumBase tests on a remote Selenium Grid server (such as <a href="https://www.browserstack.com/automate/capabilities" target="_blank">BrowserStack</a> or <a href="https://saucelabs.com/products/platform-configurator" target="_blank">Sauce Labs</a>).
66

77
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.)
88

@@ -16,17 +16,33 @@ pytest test_demo_site.py --browser=remote --server=USERNAME:[email protected]
1616

1717
(Parameters: ``--browser=remote``, ``--server=SERVER``, ``--port=PORT``, and ``--cap_file=CAP_FILE.py``)
1818

19-
Here's an example desired capabilities file for BrowserStack:
19+
Here's an example desired capabilities file for BrowserStack using the newer SDK format in a `.yml` / `.yaml` file:
20+
21+
```yml
22+
platforms:
23+
- browserName: safari
24+
osVersion: 17
25+
deviceName: iPhone 15 Pro Max
26+
buildIdentifier: ${BUILD_NUMBER}
27+
parallelsPerPlatform: 1
28+
projectName: My Project
29+
browserstackLocal: true
30+
debug: true
31+
networkLogs: true
32+
```
33+
34+
Here's an example desired capabilities file for BrowserStack using the legacy JSONWP format in a `.py` file:
2035

2136
```python
2237
desired_cap = {
23-
"os" : "Windows",
24-
"os_version" : "11",
25-
"browser" : "Chrome",
26-
"browser_version" : "101.0",
27-
"browserstack.local" : "false",
28-
"browserstack.debug" : "true",
29-
"browserstack.selenium_version" : "4.1.2",
38+
"browser": "Chrome",
39+
"os": "Windows",
40+
"os_version": "11",
41+
"browser_version": "latest",
42+
"browserstack.console": "info",
43+
"browserstack.debug": "true",
44+
"browserstack.networkLogs": "true",
45+
"browserstack.local": "true",
3046
}
3147
```
3248

@@ -46,7 +62,7 @@ capabilities = {
4662
<div><b>You can generate specific desired capabilities using:</b></div>
4763

4864
<ul>
49-
<li><a href="https://www.browserstack.com/automate/capabilities" target="_blank">BrowserStack desired capabilities</a></li>
65+
<li><a href="https://www.browserstack.com/docs/automate/capabilities" target="_blank">BrowserStack desired capabilities</a></li>
5066
<li><a href="https://saucelabs.com/products/platform-configurator" target="_blank">Sauce Labs desired capabilities</a></li>
5167
</ul>
5268

@@ -82,7 +98,7 @@ pytest test_swag_labs.py --cap-string='{"browserName":"chrome","name":"test1"}'
8298

8399
(Enclose cap-string in single quotes. Enclose parameter keys in double quotes.)
84100

85-
If you pass ``"*"`` into the ``"name"`` field of ``--cap-string``, the name will become the test identifier. Example:
101+
If you pass ``"*"`` into the ``"name"`` field of ``--cap-string``, the name will become the test identifier. Eg:
86102

87103
```bash
88104
pytest my_first_test.py --cap-string='{"browserName":"chrome","name":"*"}' --server="127.0.0.1" --browser=chrome

mkdocs_build/requirements.txt

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
# mkdocs dependencies for generating the seleniumbase.io website
22
# Minimum Python version: 3.8 (for generating docs only)
33

4-
regex>=2023.10.3
5-
PyYAML>=6.0.1
6-
pymdown-extensions>=10.5
4+
regex>=2023.12.25
5+
pymdown-extensions>=10.7
76
pipdeptree>=2.13.1
87
python-dateutil>=2.8.2
98
Markdown==3.5.1
@@ -17,11 +16,11 @@ cairocffi==1.6.1
1716
pathspec==0.12.1
1817
Babel==2.14.0
1918
paginate==0.5.6
20-
lxml==4.9.4
19+
lxml==5.0.0
2120
pyquery==2.0.0
2221
readtime==3.0.0
2322
mkdocs==1.5.3
24-
mkdocs-material==9.5.2
23+
mkdocs-material==9.5.3
2524
mkdocs-exclude-search==0.6.6
2625
mkdocs-simple-hooks==0.1.5
2726
mkdocs-material-extensions==1.3.1

requirements.txt

+4-3
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ packaging>=23.2
33
setuptools>=68.0.0;python_version<"3.8"
44
setuptools>=69.0.3;python_version>="3.8"
55
wheel>=0.42.0
6-
attrs>=23.1.0
6+
attrs>=23.2.0
77
certifi>=2023.11.17
88
filelock>=3.12.2;python_version<"3.8"
99
filelock>=3.13.1;python_version>="3.8"
1010
platformdirs>=4.0.0;python_version<"3.8"
1111
platformdirs>=4.1.0;python_version>="3.8"
1212
parse>=1.20.0
1313
parse-type>=0.6.2
14+
pyyaml>=6.0.1
1415
six==1.16.0
1516
idna==3.6
1617
chardet==5.2.0
@@ -36,7 +37,7 @@ iniconfig==2.0.0
3637
pluggy==1.2.0;python_version<"3.8"
3738
pluggy==1.3.0;python_version>="3.8"
3839
py==1.11.0
39-
pytest==7.4.3
40+
pytest==7.4.4
4041
pytest-html==2.0.1
4142
pytest-metadata==3.0.0
4243
pytest-ordering==0.6
@@ -65,7 +66,7 @@ rich==13.7.0
6566

6667
coverage==6.2;python_version<"3.7"
6768
coverage==7.2.7;python_version>="3.7" and python_version<"3.8"
68-
coverage==7.3.4;python_version>="3.8"
69+
coverage==7.4.0;python_version>="3.8"
6970
pytest-cov==4.0.0;python_version<"3.7"
7071
pytest-cov==4.1.0;python_version>="3.7"
7172
flake8==5.0.4;python_version<"3.9"

seleniumbase/__version__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# seleniumbase package
2-
__version__ = "4.22.3"
2+
__version__ = "4.22.4"

seleniumbase/core/capabilities_parser.py

+12-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import re
22
import ast
33
import json
4+
import yaml # Requires pyyaml
45

56

67
def _analyze_ast(contents):
@@ -183,28 +184,33 @@ def _read_file(file):
183184
def _parse_py_file(cap_file):
184185
all_code = _read_file(cap_file)
185186
capabilities = _analyze_ast(all_code)
186-
187187
if not capabilities:
188188
capabilities = _analyze_manual(all_code)
189-
190189
return capabilities
191190

192191

193192
def _parse_json_file(cap_file):
194193
all_code = _read_file(cap_file)
195-
196194
return json.loads(all_code)
197195

198196

197+
def _parse_yaml_file(cap_file):
198+
all_code = _read_file(cap_file)
199+
return yaml.safe_load(all_code)
200+
201+
199202
def get_desired_capabilities(cap_file):
200203
if cap_file.endswith(".py"):
201204
capabilities = _parse_py_file(cap_file)
202205
elif cap_file.endswith(".json"):
203206
capabilities = _parse_json_file(cap_file)
207+
elif (cap_file.endswith(".yml") or cap_file.endswith(".yaml")):
208+
capabilities = _parse_yaml_file(cap_file)
204209
else:
205-
raise Exception("\n\n`%s` is not a Python or JSON file!\n" % cap_file)
206-
210+
raise Exception(
211+
'\n\n`%s` must end in ".py", ".json", ".yml", or ".yaml"!\n'
212+
% cap_file
213+
)
207214
if len(capabilities.keys()) == 0:
208215
raise Exception("Unable to parse desired capabilities file!")
209-
210216
return capabilities

seleniumbase/fixtures/base_case.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -6650,7 +6650,12 @@ def get_pdf_text(
66506650
try:
66516651
from pdfminer.high_level import extract_text
66526652
except Exception:
6653-
shared_utils.pip_install("pdfminer.six")
6653+
if not sys.version_info >= (3, 8):
6654+
shared_utils.pip_install(
6655+
"pdfminer.six", version="20221105"
6656+
)
6657+
else:
6658+
shared_utils.pip_install("pdfminer.six")
66546659
from pdfminer.high_level import extract_text
66556660
if not password:
66566661
password = ""

0 commit comments

Comments
 (0)