gridfleet-testkit 0.9.5__tar.gz → 0.10.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/.gitignore +1 -0
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/CHANGELOG.md +26 -0
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/PKG-INFO +9 -9
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/README.md +8 -8
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/examples/test_android_mobile_screenshot.py +2 -2
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/examples/test_android_tv_screenshot.py +2 -2
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/examples/test_firetv_screenshot.py +2 -2
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/examples/test_ios_simulator_screenshot.py +2 -2
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/examples/test_roku_screenshot.py +2 -2
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/examples/test_roku_sideload_screenshot.py +1 -1
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/examples/test_tvos_screenshot.py +2 -2
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/gridfleet_testkit/__init__.py +4 -2
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/gridfleet_testkit/appium.py +15 -4
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/gridfleet_testkit/client.py +11 -0
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/gridfleet_testkit/pytest_plugin.py +3 -2
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/pyproject.toml +1 -1
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/tests/test_appium.py +0 -1
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/tests/test_pytest_plugin.py +0 -1
- gridfleet_testkit-0.10.0/tests/test_run_scoped_url.py +131 -0
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/uv.lock +22 -22
- gridfleet_testkit-0.9.5/tests/test_pytest_plugin_grid_run_id_injection.py +0 -29
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/examples/__init__.py +0 -0
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/examples/_example_helpers.py +0 -0
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/examples/assets/hello-world.zip +0 -0
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/gridfleet_testkit/allocation.py +0 -0
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/gridfleet_testkit/py.typed +0 -0
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/gridfleet_testkit/sessions.py +0 -0
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/gridfleet_testkit/types.py +0 -0
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/tests/test_allocation.py +0 -0
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/tests/test_client.py +0 -0
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/tests/test_client_test_data.py +0 -0
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/tests/test_docs_contract.py +0 -0
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/tests/test_driver_agnostic_guard.py +0 -0
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/tests/test_package_metadata.py +0 -0
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/tests/test_pytest_plugin_test_data.py +0 -0
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/tests/test_sessions.py +0 -0
- {gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/tests/test_sessions_resolve_device_handle.py +0 -0
|
@@ -2,6 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the GridFleet testkit (`gridfleet-testkit` on PyPI) are documented here.
|
|
4
4
|
|
|
5
|
+
## [0.10.0](https://github.com/quidow/gridfleet/compare/gridfleet-testkit-v0.9.5...gridfleet-testkit-v0.10.0) (2026-06-07)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### ⚠ BREAKING CHANGES
|
|
9
|
+
|
|
10
|
+
* **testkit:** bind sessions to runs via the run-scoped grid url
|
|
11
|
+
|
|
12
|
+
### Features
|
|
13
|
+
|
|
14
|
+
* **testkit:** bind sessions to runs via the run-scoped grid url ([da34f10](https://github.com/quidow/gridfleet/commit/da34f10f095ed74b2733c0746f11632039bd109e))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
* **testkit:** route the pytest plugin driver fixture through the run-scoped url ([d976c8a](https://github.com/quidow/gridfleet/commit/d976c8afb7c10ade85b3b03fb1c7d40756e952ca))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Dependencies
|
|
23
|
+
|
|
24
|
+
* **deps:** bump ruff in /testkit in the python-dependencies group ([#513](https://github.com/quidow/gridfleet/issues/513)) ([f7e8fb9](https://github.com/quidow/gridfleet/commit/f7e8fb92da993155852e328dd2e8e174fe93bba7))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Documentation
|
|
28
|
+
|
|
29
|
+
* **docs:** sweep selenium grid references for router architecture ([d086bcb](https://github.com/quidow/gridfleet/commit/d086bcb7c1619fb21cdf5e59499ab8221b18a0e4))
|
|
30
|
+
|
|
5
31
|
## [0.9.5](https://github.com/quidow/gridfleet/compare/gridfleet-testkit-v0.9.4...gridfleet-testkit-v0.9.5) (2026-06-04)
|
|
6
32
|
|
|
7
33
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gridfleet-testkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
4
4
|
Summary: Supported pytest and run-orchestration helpers for GridFleet integrations
|
|
5
5
|
Project-URL: Homepage, https://github.com/quidow/gridfleet
|
|
6
6
|
Project-URL: Repository, https://github.com/quidow/gridfleet
|
|
@@ -68,7 +68,7 @@ Description-Content-Type: text/markdown
|
|
|
68
68
|
## What It Does Not Own
|
|
69
69
|
|
|
70
70
|
- Appium server installation or host-level driver setup
|
|
71
|
-
-
|
|
71
|
+
- WebDriver router lifecycle
|
|
72
72
|
- Device registration, verification, or readiness setup
|
|
73
73
|
- CI orchestration beyond the documented client helpers
|
|
74
74
|
|
|
@@ -107,15 +107,15 @@ The package supports Python 3.10 through 3.14.
|
|
|
107
107
|
|
|
108
108
|
| Variable | Default | Meaning |
|
|
109
109
|
| --- | --- | --- |
|
|
110
|
-
| `GRID_URL` | `http://localhost:4444` |
|
|
110
|
+
| `GRID_URL` | `http://localhost:4444` | WebDriver router URL used by the pytest Appium fixture |
|
|
111
111
|
| `GRIDFLEET_API_URL` | `http://localhost:8000/api` | GridFleet API base used for session reporting, config lookup, run helpers, and driver-pack catalog lookup |
|
|
112
112
|
| `GRIDFLEET_TESTKIT_USERNAME` | unset | Machine-auth username sent as HTTP Basic auth on every API call. Required when the manager runs with `GRIDFLEET_AUTH_ENABLED=true`. Use the same value as the manager's `GRIDFLEET_MACHINE_AUTH_USERNAME`. |
|
|
113
113
|
| `GRIDFLEET_TESTKIT_PASSWORD` | unset | Machine-auth password sent as HTTP Basic auth on every API call. Required when the manager runs with `GRIDFLEET_AUTH_ENABLED=true`. Use the same value as the manager's `GRIDFLEET_MACHINE_AUTH_PASSWORD`. |
|
|
114
114
|
| `GRIDFLEET_TESTKIT_PACK_ID` | unset | Optional default driver pack id for Appium option building |
|
|
115
115
|
| `GRIDFLEET_TESTKIT_PLATFORM_ID` | unset | Optional default platform id for Appium option building |
|
|
116
|
-
| `GRIDFLEET_RUN_ID` |
|
|
116
|
+
| `GRIDFLEET_RUN_ID` | unset | Optional run id. When set, drivers are created through the run-scoped grid endpoint `GRID_URL/run/{id}` so sessions land only on devices reserved for the run. Unset = free session on unreserved devices. Set this in the environment that launches pytest (e.g. the run launcher or CI step); the testkit reads it but does not set it. |
|
|
117
117
|
|
|
118
|
-
The package assumes a running GridFleet API, a reachable
|
|
118
|
+
The package assumes a running GridFleet API, a reachable WebDriver router, and platform-specific Appium driver setup on the registered hosts. When auth is disabled on the manager, leave `GRIDFLEET_TESTKIT_USERNAME` / `GRIDFLEET_TESTKIT_PASSWORD` unset and the testkit will send no `Authorization` header.
|
|
119
119
|
|
|
120
120
|
## Pytest Plugin
|
|
121
121
|
|
|
@@ -149,7 +149,7 @@ If you need raw Appium control instead, omit `pack_id` and `platform_id`, then p
|
|
|
149
149
|
|
|
150
150
|
- Creates an Appium session through `GRID_URL`
|
|
151
151
|
- Injects `gridfleet:testName` with the pytest test name
|
|
152
|
-
-
|
|
152
|
+
- Resolves the WebDriver endpoint from `GRIDFLEET_RUN_ID`: run-scoped URL inside a reserved run, bare grid URL otherwise. No GridFleet identity is injected into capabilities.
|
|
153
153
|
- Reports final session status back to `GRIDFLEET_API_URL`
|
|
154
154
|
- Exposes `device_config` for post-session config lookup using the runtime connection target
|
|
155
155
|
- Exposes `device_test_data` for post-session operator-attached test data using the runtime connection target
|
|
@@ -218,7 +218,7 @@ finally:
|
|
|
218
218
|
|
|
219
219
|
### Targeting Devices by Tag
|
|
220
220
|
|
|
221
|
-
GridFleet injects device tags into
|
|
221
|
+
GridFleet injects device tags into node stereotypes as `appium:gridfleet:tag:<key>` capabilities, so the router's backend allocation can route sessions to devices matching specific tags.
|
|
222
222
|
|
|
223
223
|
```python
|
|
224
224
|
@pytest.mark.parametrize(
|
|
@@ -254,7 +254,7 @@ The `gridfleet_worker_id` fixture is informational only: it returns the pytest-x
|
|
|
254
254
|
|
|
255
255
|
### Reservation Flow
|
|
256
256
|
|
|
257
|
-
GridFleet runs are
|
|
257
|
+
GridFleet runs are router-routed: once devices are reserved, the manager tags matching nodes with the run id, and the router routes new Appium sessions to those nodes automatically when they arrive through the run-scoped endpoint (`GRID_URL/run/{run_id}`). There are no per-worker claim or release calls.
|
|
258
258
|
|
|
259
259
|
```python
|
|
260
260
|
from gridfleet_testkit import GridFleetClient, register_run_cleanup
|
|
@@ -418,7 +418,7 @@ Advanced example:
|
|
|
418
418
|
|
|
419
419
|
The baseline examples share the same flow:
|
|
420
420
|
|
|
421
|
-
1. Create a session through
|
|
421
|
+
1. Create a session through the WebDriver router
|
|
422
422
|
2. Print the resolved connection context
|
|
423
423
|
3. Save a screenshot
|
|
424
424
|
4. Assert that the screenshot file exists and is non-empty
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
## What It Does Not Own
|
|
37
37
|
|
|
38
38
|
- Appium server installation or host-level driver setup
|
|
39
|
-
-
|
|
39
|
+
- WebDriver router lifecycle
|
|
40
40
|
- Device registration, verification, or readiness setup
|
|
41
41
|
- CI orchestration beyond the documented client helpers
|
|
42
42
|
|
|
@@ -75,15 +75,15 @@ The package supports Python 3.10 through 3.14.
|
|
|
75
75
|
|
|
76
76
|
| Variable | Default | Meaning |
|
|
77
77
|
| --- | --- | --- |
|
|
78
|
-
| `GRID_URL` | `http://localhost:4444` |
|
|
78
|
+
| `GRID_URL` | `http://localhost:4444` | WebDriver router URL used by the pytest Appium fixture |
|
|
79
79
|
| `GRIDFLEET_API_URL` | `http://localhost:8000/api` | GridFleet API base used for session reporting, config lookup, run helpers, and driver-pack catalog lookup |
|
|
80
80
|
| `GRIDFLEET_TESTKIT_USERNAME` | unset | Machine-auth username sent as HTTP Basic auth on every API call. Required when the manager runs with `GRIDFLEET_AUTH_ENABLED=true`. Use the same value as the manager's `GRIDFLEET_MACHINE_AUTH_USERNAME`. |
|
|
81
81
|
| `GRIDFLEET_TESTKIT_PASSWORD` | unset | Machine-auth password sent as HTTP Basic auth on every API call. Required when the manager runs with `GRIDFLEET_AUTH_ENABLED=true`. Use the same value as the manager's `GRIDFLEET_MACHINE_AUTH_PASSWORD`. |
|
|
82
82
|
| `GRIDFLEET_TESTKIT_PACK_ID` | unset | Optional default driver pack id for Appium option building |
|
|
83
83
|
| `GRIDFLEET_TESTKIT_PLATFORM_ID` | unset | Optional default platform id for Appium option building |
|
|
84
|
-
| `GRIDFLEET_RUN_ID` |
|
|
84
|
+
| `GRIDFLEET_RUN_ID` | unset | Optional run id. When set, drivers are created through the run-scoped grid endpoint `GRID_URL/run/{id}` so sessions land only on devices reserved for the run. Unset = free session on unreserved devices. Set this in the environment that launches pytest (e.g. the run launcher or CI step); the testkit reads it but does not set it. |
|
|
85
85
|
|
|
86
|
-
The package assumes a running GridFleet API, a reachable
|
|
86
|
+
The package assumes a running GridFleet API, a reachable WebDriver router, and platform-specific Appium driver setup on the registered hosts. When auth is disabled on the manager, leave `GRIDFLEET_TESTKIT_USERNAME` / `GRIDFLEET_TESTKIT_PASSWORD` unset and the testkit will send no `Authorization` header.
|
|
87
87
|
|
|
88
88
|
## Pytest Plugin
|
|
89
89
|
|
|
@@ -117,7 +117,7 @@ If you need raw Appium control instead, omit `pack_id` and `platform_id`, then p
|
|
|
117
117
|
|
|
118
118
|
- Creates an Appium session through `GRID_URL`
|
|
119
119
|
- Injects `gridfleet:testName` with the pytest test name
|
|
120
|
-
-
|
|
120
|
+
- Resolves the WebDriver endpoint from `GRIDFLEET_RUN_ID`: run-scoped URL inside a reserved run, bare grid URL otherwise. No GridFleet identity is injected into capabilities.
|
|
121
121
|
- Reports final session status back to `GRIDFLEET_API_URL`
|
|
122
122
|
- Exposes `device_config` for post-session config lookup using the runtime connection target
|
|
123
123
|
- Exposes `device_test_data` for post-session operator-attached test data using the runtime connection target
|
|
@@ -186,7 +186,7 @@ finally:
|
|
|
186
186
|
|
|
187
187
|
### Targeting Devices by Tag
|
|
188
188
|
|
|
189
|
-
GridFleet injects device tags into
|
|
189
|
+
GridFleet injects device tags into node stereotypes as `appium:gridfleet:tag:<key>` capabilities, so the router's backend allocation can route sessions to devices matching specific tags.
|
|
190
190
|
|
|
191
191
|
```python
|
|
192
192
|
@pytest.mark.parametrize(
|
|
@@ -222,7 +222,7 @@ The `gridfleet_worker_id` fixture is informational only: it returns the pytest-x
|
|
|
222
222
|
|
|
223
223
|
### Reservation Flow
|
|
224
224
|
|
|
225
|
-
GridFleet runs are
|
|
225
|
+
GridFleet runs are router-routed: once devices are reserved, the manager tags matching nodes with the run id, and the router routes new Appium sessions to those nodes automatically when they arrive through the run-scoped endpoint (`GRID_URL/run/{run_id}`). There are no per-worker claim or release calls.
|
|
226
226
|
|
|
227
227
|
```python
|
|
228
228
|
from gridfleet_testkit import GridFleetClient, register_run_cleanup
|
|
@@ -386,7 +386,7 @@ Advanced example:
|
|
|
386
386
|
|
|
387
387
|
The baseline examples share the same flow:
|
|
388
388
|
|
|
389
|
-
1. Create a session through
|
|
389
|
+
1. Create a session through the WebDriver router
|
|
390
390
|
2. Print the resolved connection context
|
|
391
391
|
3. Save a screenshot
|
|
392
392
|
4. Assert that the screenshot file exists and is non-empty
|
{gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/examples/test_android_mobile_screenshot.py
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Manual baseline example: connect to an Android mobile device through
|
|
2
|
+
Manual baseline example: connect to an Android mobile device through the WebDriver router and take a screenshot.
|
|
3
3
|
|
|
4
4
|
Requires:
|
|
5
|
-
-
|
|
5
|
+
- WebDriver router running on localhost:4444
|
|
6
6
|
- An Android mobile device registered and its Appium node running
|
|
7
7
|
- The supported GridFleet testkit installed
|
|
8
8
|
- Appium-Python-Client installed (`uv pip install -e ./testkit`)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Manual baseline example: connect to an Android TV device through
|
|
2
|
+
Manual baseline example: connect to an Android TV device through the WebDriver router and take a screenshot.
|
|
3
3
|
|
|
4
4
|
Requires:
|
|
5
|
-
-
|
|
5
|
+
- WebDriver router running on localhost:4444
|
|
6
6
|
- An Android TV device registered and its Appium node running
|
|
7
7
|
- The supported GridFleet testkit installed
|
|
8
8
|
- Appium-Python-Client installed (`uv pip install -e ./testkit`)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Manual baseline example: connect to a Fire TV device through
|
|
2
|
+
Manual baseline example: connect to a Fire TV device through the WebDriver router and take a screenshot.
|
|
3
3
|
|
|
4
4
|
Requires:
|
|
5
|
-
-
|
|
5
|
+
- WebDriver router running on localhost:4444
|
|
6
6
|
- A Fire TV device registered and its Appium node running
|
|
7
7
|
- The supported GridFleet testkit installed
|
|
8
8
|
- Appium-Python-Client installed (`uv pip install -e ./testkit`)
|
{gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/examples/test_ios_simulator_screenshot.py
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Manual baseline example: connect to an iOS simulator through
|
|
2
|
+
Manual baseline example: connect to an iOS simulator through the WebDriver router and take a screenshot.
|
|
3
3
|
|
|
4
4
|
Requires:
|
|
5
|
-
-
|
|
5
|
+
- WebDriver router running on localhost:4444
|
|
6
6
|
- An iOS simulator registered and its Appium node running
|
|
7
7
|
- The supported GridFleet testkit installed
|
|
8
8
|
- Appium with the XCUITest driver installed (`appium driver install xcuitest`)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Manual baseline example: connect to a Roku device through
|
|
2
|
+
Manual baseline example: connect to a Roku device through the WebDriver router and take a screenshot.
|
|
3
3
|
|
|
4
4
|
Requires:
|
|
5
|
-
-
|
|
5
|
+
- WebDriver router running on localhost:4444
|
|
6
6
|
- A Roku device registered with Roku dev credentials in device config
|
|
7
7
|
- Appium with the Roku driver installed (`appium driver install roku`)
|
|
8
8
|
- The supported GridFleet testkit installed
|
{gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/examples/test_roku_sideload_screenshot.py
RENAMED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Manual advanced example: connect to a Roku device, sideload a sample app, and take a screenshot.
|
|
3
3
|
|
|
4
4
|
Requires:
|
|
5
|
-
-
|
|
5
|
+
- WebDriver router running on localhost:4444
|
|
6
6
|
- A Roku device registered with Roku dev credentials in device config
|
|
7
7
|
- Appium with the Roku driver installed (`appium driver install roku`)
|
|
8
8
|
- The supported GridFleet testkit installed
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Manual baseline example: connect to a tvOS real device through
|
|
2
|
+
Manual baseline example: connect to a tvOS real device through the WebDriver router and take a screenshot.
|
|
3
3
|
|
|
4
4
|
Requires:
|
|
5
|
-
-
|
|
5
|
+
- WebDriver router running on localhost:4444
|
|
6
6
|
- A tvOS real device registered and its Appium node running
|
|
7
7
|
- The supported GridFleet testkit installed
|
|
8
8
|
- Appium with the XCUITest driver installed (`appium driver install xcuitest`)
|
|
@@ -6,7 +6,7 @@ the live Appium-side config can use `client.get_device_config(connection_target)
|
|
|
6
6
|
|
|
7
7
|
Environment variables read by the client:
|
|
8
8
|
|
|
9
|
-
- GRID_URL:
|
|
9
|
+
- GRID_URL: WebDriver router URL used by Appium helper defaults.
|
|
10
10
|
- GRIDFLEET_API_URL: GridFleet manager API base URL.
|
|
11
11
|
- GRIDFLEET_TESTKIT_USERNAME: optional Basic auth username.
|
|
12
12
|
- GRIDFLEET_TESTKIT_PASSWORD: optional Basic auth password.
|
|
@@ -41,13 +41,14 @@ from .client import (
|
|
|
41
41
|
_default_api_url,
|
|
42
42
|
_default_grid_url,
|
|
43
43
|
register_run_cleanup,
|
|
44
|
+
run_grid_url,
|
|
44
45
|
)
|
|
45
46
|
from .sessions import build_error_session_payload, resolve_device_handle_from_driver
|
|
46
47
|
|
|
47
48
|
try:
|
|
48
49
|
__version__ = version("gridfleet-testkit")
|
|
49
50
|
except PackageNotFoundError:
|
|
50
|
-
__version__ = "0.
|
|
51
|
+
__version__ = "0.10.0"
|
|
51
52
|
|
|
52
53
|
__all__ = [
|
|
53
54
|
"GRIDFLEET_API_URL",
|
|
@@ -72,6 +73,7 @@ __all__ = [
|
|
|
72
73
|
"hydrate_allocated_device_from_driver",
|
|
73
74
|
"register_run_cleanup",
|
|
74
75
|
"resolve_device_handle_from_driver",
|
|
76
|
+
"run_grid_url",
|
|
75
77
|
]
|
|
76
78
|
|
|
77
79
|
|
|
@@ -8,7 +8,7 @@ from typing import TYPE_CHECKING, cast
|
|
|
8
8
|
from appium import webdriver
|
|
9
9
|
from appium.options.common import AppiumOptions
|
|
10
10
|
|
|
11
|
-
from .client import GridFleetClient, _default_grid_url
|
|
11
|
+
from .client import GridFleetClient, _default_grid_url, run_grid_url
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
14
|
from collections.abc import Callable, Mapping
|
|
@@ -102,7 +102,6 @@ def build_appium_options(
|
|
|
102
102
|
) -> AppiumOptions:
|
|
103
103
|
"""Build Appium options from driver-pack catalog platform metadata."""
|
|
104
104
|
params = dict(capabilities or {})
|
|
105
|
-
params.setdefault("gridfleet:run_id", os.environ.get("GRIDFLEET_RUN_ID", "free"))
|
|
106
105
|
explicit_platform_name = params.get("platformName")
|
|
107
106
|
if explicit_platform_name is not None and (pack_id is not None or platform_id is not None):
|
|
108
107
|
raise ValueError("Use either pack_id/platform_id or the raw platformName capability, not both.")
|
|
@@ -126,6 +125,18 @@ def build_appium_options(
|
|
|
126
125
|
return options
|
|
127
126
|
|
|
128
127
|
|
|
128
|
+
def _resolve_grid_url(grid_url: str | None) -> str:
|
|
129
|
+
"""Executor resolution: explicit URL wins; GRIDFLEET_RUN_ID (set externally
|
|
130
|
+
by the run launcher or CI before pytest starts) composes the run-scoped
|
|
131
|
+
endpoint; otherwise the bare grid URL — an explicit free session."""
|
|
132
|
+
if grid_url is not None:
|
|
133
|
+
return grid_url
|
|
134
|
+
run_id = os.environ.get("GRIDFLEET_RUN_ID")
|
|
135
|
+
if run_id:
|
|
136
|
+
return run_grid_url(run_id)
|
|
137
|
+
return _default_grid_url()
|
|
138
|
+
|
|
139
|
+
|
|
129
140
|
def create_appium_driver(
|
|
130
141
|
*,
|
|
131
142
|
pack_id: str | None = None,
|
|
@@ -135,7 +146,7 @@ def create_appium_driver(
|
|
|
135
146
|
grid_url: str | None = None,
|
|
136
147
|
catalog_client: object | None = None,
|
|
137
148
|
) -> WebDriver:
|
|
138
|
-
"""Create an Appium remote driver through
|
|
149
|
+
"""Create an Appium remote driver through the WebDriver router."""
|
|
139
150
|
options = build_appium_options(
|
|
140
151
|
pack_id=pack_id,
|
|
141
152
|
platform_id=platform_id,
|
|
@@ -143,7 +154,7 @@ def create_appium_driver(
|
|
|
143
154
|
test_name=test_name,
|
|
144
155
|
catalog_client=catalog_client,
|
|
145
156
|
)
|
|
146
|
-
return webdriver.Remote(grid_url
|
|
157
|
+
return webdriver.Remote(_resolve_grid_url(grid_url), options=options)
|
|
147
158
|
|
|
148
159
|
|
|
149
160
|
def get_connection_target_from_driver(driver: WebDriver) -> str:
|
|
@@ -31,6 +31,17 @@ def _default_grid_url() -> str:
|
|
|
31
31
|
return os.getenv("GRID_URL", DEFAULT_GRID_URL)
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
def run_grid_url(run_id: str, *, base: str | None = None) -> str:
|
|
35
|
+
"""Run-scoped WebDriver endpoint for *run_id* (``{base}/run/{run_id}``).
|
|
36
|
+
|
|
37
|
+
Sessions created through it are admitted only to devices reserved for the
|
|
38
|
+
run; free sessions use the bare grid URL. Replaces the retired
|
|
39
|
+
``gridfleet:run_id`` capability.
|
|
40
|
+
"""
|
|
41
|
+
root = (base or _default_grid_url()).rstrip("/")
|
|
42
|
+
return f"{root}/run/{run_id}"
|
|
43
|
+
|
|
44
|
+
|
|
34
45
|
def _default_api_url() -> str:
|
|
35
46
|
return os.getenv("GRIDFLEET_API_URL", DEFAULT_GRIDFLEET_API_URL)
|
|
36
47
|
|
|
@@ -9,6 +9,7 @@ import pytest
|
|
|
9
9
|
from appium import webdriver
|
|
10
10
|
|
|
11
11
|
from .appium import (
|
|
12
|
+
_resolve_grid_url,
|
|
12
13
|
build_appium_options,
|
|
13
14
|
get_device_config_for_driver,
|
|
14
15
|
get_device_test_data_for_driver,
|
|
@@ -72,7 +73,7 @@ def appium_driver(
|
|
|
72
73
|
gridfleet_client: GridFleetClient,
|
|
73
74
|
) -> Generator[WebDriver, None, None]:
|
|
74
75
|
"""
|
|
75
|
-
Create an Appium Remote driver through the
|
|
76
|
+
Create an Appium Remote driver through the WebDriver router.
|
|
76
77
|
|
|
77
78
|
Parametrize with a dict of pack/catalog selection plus capabilities:
|
|
78
79
|
@pytest.mark.parametrize(
|
|
@@ -84,7 +85,7 @@ def appium_driver(
|
|
|
84
85
|
options = _build_driver_options(request, gridfleet_client)
|
|
85
86
|
|
|
86
87
|
try:
|
|
87
|
-
driver = webdriver.Remote(
|
|
88
|
+
driver = webdriver.Remote(_resolve_grid_url(None), options=options)
|
|
88
89
|
except Exception as exc:
|
|
89
90
|
# Driver creation failed before a Grid session was established (e.g.
|
|
90
91
|
# SessionNotCreatedException). Register a device-less error session so the
|
|
@@ -166,7 +166,6 @@ def test_create_appium_driver_uses_factory_options(monkeypatch: pytest.MonkeyPat
|
|
|
166
166
|
"platformName": "Android",
|
|
167
167
|
"appium:platform": "firetv_real",
|
|
168
168
|
"appium:automationName": "UiAutomator2",
|
|
169
|
-
"gridfleet:run_id": "free",
|
|
170
169
|
"gridfleet:testName": "manual-smoke",
|
|
171
170
|
},
|
|
172
171
|
)
|
|
@@ -286,7 +286,6 @@ def test_appium_driver_setup_failure_registers_device_less_error_session(monkeyp
|
|
|
286
286
|
"appium:connection_type": "network",
|
|
287
287
|
"appium:appPackage": "io.appium.android.apis",
|
|
288
288
|
"platformName": "Android",
|
|
289
|
-
"gridfleet:run_id": "free",
|
|
290
289
|
"gridfleet:testName": "test_broken",
|
|
291
290
|
"appium:platform": "android_mobile",
|
|
292
291
|
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import types
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
import gridfleet_testkit.appium as appium_mod
|
|
7
|
+
from gridfleet_testkit import pytest_plugin, run_grid_url
|
|
8
|
+
from gridfleet_testkit.appium import _resolve_grid_url, build_appium_options
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from collections.abc import Iterator
|
|
12
|
+
|
|
13
|
+
import pytest
|
|
14
|
+
|
|
15
|
+
RID = "0c8c057f-3ec1-4b9c-9d2e-9f3a86a2c001"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_run_grid_url_composes_from_explicit_base() -> None:
|
|
19
|
+
assert run_grid_url(RID, base="http://router:4444/") == f"http://router:4444/run/{RID}"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_run_grid_url_defaults_base_from_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
23
|
+
monkeypatch.setenv("GRID_URL", "http://lab-router:4444")
|
|
24
|
+
assert run_grid_url(RID) == f"http://lab-router:4444/run/{RID}"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_resolve_grid_url_explicit_wins(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
28
|
+
monkeypatch.setenv("GRIDFLEET_RUN_ID", RID)
|
|
29
|
+
assert _resolve_grid_url("http://explicit:4444") == "http://explicit:4444"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_resolve_grid_url_uses_run_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
33
|
+
monkeypatch.setenv("GRID_URL", "http://router:4444")
|
|
34
|
+
monkeypatch.setenv("GRIDFLEET_RUN_ID", RID)
|
|
35
|
+
assert _resolve_grid_url(None) == f"http://router:4444/run/{RID}"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_resolve_grid_url_unset_env_is_free_session(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
39
|
+
monkeypatch.setenv("GRID_URL", "http://router:4444")
|
|
40
|
+
monkeypatch.delenv("GRIDFLEET_RUN_ID", raising=False)
|
|
41
|
+
assert _resolve_grid_url(None) == "http://router:4444"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_no_run_id_capability_injected(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
45
|
+
"""The cap-era contract is dead: no gridfleet:run_id regardless of env."""
|
|
46
|
+
monkeypatch.setenv("GRIDFLEET_RUN_ID", RID)
|
|
47
|
+
options = build_appium_options(capabilities={"platformName": "Android"})
|
|
48
|
+
assert "gridfleet:run_id" not in dict(options.to_capabilities())
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# --- pytest plugin fixture URL routing ---
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class _FakeOptions:
|
|
55
|
+
def __init__(self) -> None:
|
|
56
|
+
self.platform_name: str | None = None
|
|
57
|
+
self.capabilities: dict[str, object] = {}
|
|
58
|
+
|
|
59
|
+
def set_capability(self, key: str, value: object) -> None:
|
|
60
|
+
self.capabilities[key] = value
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class _FakeDriver:
|
|
64
|
+
def __init__(self) -> None:
|
|
65
|
+
self.session_id = "sess-x"
|
|
66
|
+
self.capabilities: dict[str, object] = {}
|
|
67
|
+
|
|
68
|
+
def quit(self) -> None:
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class _FakeClient:
|
|
73
|
+
def get_driver_pack_catalog(self) -> dict[str, object]:
|
|
74
|
+
return {"packs": []}
|
|
75
|
+
|
|
76
|
+
def register_session_from_driver(self, driver: object, **kwargs: object) -> dict[str, object]:
|
|
77
|
+
return {"ok": True}
|
|
78
|
+
|
|
79
|
+
def register_session(self, **kwargs: object) -> dict[str, object]:
|
|
80
|
+
return {"ok": True}
|
|
81
|
+
|
|
82
|
+
def update_session_status(self, session_id: str, status: str, *, suppress_errors: bool = True) -> dict[str, object]:
|
|
83
|
+
return {"ok": True}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _make_plugin_generator(monkeypatch: pytest.MonkeyPatch) -> tuple[list[tuple[str, object]], Iterator[object]]:
|
|
87
|
+
"""Return (captured_calls, generator) after installing a minimal webdriver.Remote spy."""
|
|
88
|
+
captured: list[tuple[str, object]] = []
|
|
89
|
+
|
|
90
|
+
def fake_remote(url: str, *, options: object) -> _FakeDriver:
|
|
91
|
+
captured.append((url, options))
|
|
92
|
+
return _FakeDriver()
|
|
93
|
+
|
|
94
|
+
monkeypatch.setattr(appium_mod, "AppiumOptions", _FakeOptions)
|
|
95
|
+
# String target: `pytest_plugin.webdriver` is a transitive module attribute
|
|
96
|
+
# mypy strict (no_implicit_reexport) refuses to access statically.
|
|
97
|
+
monkeypatch.setattr("gridfleet_testkit.pytest_plugin.webdriver.Remote", fake_remote)
|
|
98
|
+
|
|
99
|
+
request = types.SimpleNamespace(
|
|
100
|
+
param={"platformName": "Android"},
|
|
101
|
+
node=types.SimpleNamespace(name="test_plugin_url"),
|
|
102
|
+
)
|
|
103
|
+
# pytest wraps fixtures in FixtureFunctionDefinition; __wrapped__ exists at
|
|
104
|
+
# runtime but not in pytest's stubs, so reach it via getattr.
|
|
105
|
+
fixture_fn = getattr(pytest_plugin.appium_driver, "__wrapped__") # noqa: B009
|
|
106
|
+
gen: Iterator[object] = fixture_fn(request, _FakeClient())
|
|
107
|
+
return captured, gen
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_plugin_fixture_uses_run_scoped_url_when_run_id_set(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
111
|
+
"""When GRIDFLEET_RUN_ID is set, the plugin driver connects to GRID_URL/run/{id}."""
|
|
112
|
+
monkeypatch.setenv("GRID_URL", "http://router:4444")
|
|
113
|
+
monkeypatch.setenv("GRIDFLEET_RUN_ID", RID)
|
|
114
|
+
|
|
115
|
+
captured, gen = _make_plugin_generator(monkeypatch)
|
|
116
|
+
next(gen)
|
|
117
|
+
|
|
118
|
+
assert len(captured) == 1
|
|
119
|
+
assert captured[0][0] == f"http://router:4444/run/{RID}"
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def test_plugin_fixture_uses_bare_url_when_run_id_unset(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
123
|
+
"""When GRIDFLEET_RUN_ID is absent, the plugin driver connects to the bare GRID_URL."""
|
|
124
|
+
monkeypatch.setenv("GRID_URL", "http://router:4444")
|
|
125
|
+
monkeypatch.delenv("GRIDFLEET_RUN_ID", raising=False)
|
|
126
|
+
|
|
127
|
+
captured, gen = _make_plugin_generator(monkeypatch)
|
|
128
|
+
next(gen)
|
|
129
|
+
|
|
130
|
+
assert len(captured) == 1
|
|
131
|
+
assert captured[0][0] == "http://router:4444"
|
|
@@ -136,7 +136,7 @@ wheels = [
|
|
|
136
136
|
|
|
137
137
|
[[package]]
|
|
138
138
|
name = "gridfleet-testkit"
|
|
139
|
-
version = "0.
|
|
139
|
+
version = "0.10.0"
|
|
140
140
|
source = { editable = "." }
|
|
141
141
|
dependencies = [
|
|
142
142
|
{ name = "appium-python-client" },
|
|
@@ -454,27 +454,27 @@ wheels = [
|
|
|
454
454
|
|
|
455
455
|
[[package]]
|
|
456
456
|
name = "ruff"
|
|
457
|
-
version = "0.15.
|
|
458
|
-
source = { registry = "https://pypi.org/simple" }
|
|
459
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
460
|
-
wheels = [
|
|
461
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
462
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
463
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
464
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
465
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
466
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
467
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
468
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
469
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
470
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
471
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
472
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
473
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
474
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
475
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
476
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
477
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
457
|
+
version = "0.15.16"
|
|
458
|
+
source = { registry = "https://pypi.org/simple" }
|
|
459
|
+
sdist = { url = "https://files.pythonhosted.org/packages/a6/bd/5f7ec371001337d8fa61701c186ff8b613ecac1651848c5950f4c4d5f2e9/ruff-0.15.16.tar.gz", hash = "sha256:d05e78d38c78caf020b03789e25106c93017db5a0cb6e2819885018c61343b78", size = 4714267, upload-time = "2026-06-04T16:33:09.974Z" }
|
|
460
|
+
wheels = [
|
|
461
|
+
{ url = "https://files.pythonhosted.org/packages/0c/42/53ef1c3953f157956db9bf7861e3bc50b9b887ce93300aa48cdba8336fe6/ruff-0.15.16-py3-none-linux_armv6l.whl", hash = "sha256:6ac3c0b3969cc6cf6b158c4e2f8f682acb58e7d700d8a44b65ecdc72d66ab0b2", size = 10709025, upload-time = "2026-06-04T16:32:51.935Z" },
|
|
462
|
+
{ url = "https://files.pythonhosted.org/packages/93/9a/a79159346f19134a956607754e57d8d128f7a4c00f4ad2f7514d224c172c/ruff-0.15.16-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:197c207ed75ffba54a0dec23db4aa939a27a3053073e085e0042433cbdc58e4a", size = 11063550, upload-time = "2026-06-04T16:32:42.24Z" },
|
|
463
|
+
{ url = "https://files.pythonhosted.org/packages/bc/72/3ce2ac000a5299ec238e01f51397b3b653c93b077d9b1bfe8715bb895f20/ruff-0.15.16-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3a39fec45ab316cc23e7558f23fea4a70403ddb5648ea9a4a3854a16973d0071", size = 10421345, upload-time = "2026-06-04T16:32:37.251Z" },
|
|
464
|
+
{ url = "https://files.pythonhosted.org/packages/b0/c2/cc7fad3ec9169373f5b6a18f1917b91080feec40c3f9658334a1d28e2f03/ruff-0.15.16-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba93191d79003116b95128c9d306e045200fdbd0bccb782b110f3cd1d4abc5cf", size = 10757217, upload-time = "2026-06-04T16:32:54.722Z" },
|
|
465
|
+
{ url = "https://files.pythonhosted.org/packages/69/d2/3474009eaa0a65b31fa7152a2fad5e2f050c640ceb1e6b02ee6922e94c82/ruff-0.15.16-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c6ee4b90520630120ef032aa5cc10db483852dff950e78b1d717e2993a61ac8d", size = 10507035, upload-time = "2026-06-04T16:33:05.343Z" },
|
|
466
|
+
{ url = "https://files.pythonhosted.org/packages/ca/81/b7ae6ccbd11f0c8dc3d5d67fc4be9b57ff57ca86ba56152021378e1277f2/ruff-0.15.16-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e4215bc938bc3c8215c1472c1aa437e310fee20cd427335fec9d7e609563628", size = 11255291, upload-time = "2026-06-04T16:32:49.49Z" },
|
|
467
|
+
{ url = "https://files.pythonhosted.org/packages/d9/e1/46e526f1a7cc90857ce6ddf25fbb77eb6568651ac38d71b033af07076dd5/ruff-0.15.16-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7c8d26be963b090f10e29abc8b3e74a2a321f6fa34e02424e30b5af89350ecbb", size = 12124922, upload-time = "2026-06-04T16:33:07.821Z" },
|
|
468
|
+
{ url = "https://files.pythonhosted.org/packages/1a/da/5c791b088b596b24d0deb967fa28ae02ad751a140c0b9ea81c5ab915d6c0/ruff-0.15.16-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f198cf4123602a2280ed46c307bcbafe41758d6fee5b456b6b6058ca1514b3b4", size = 11332186, upload-time = "2026-06-04T16:33:02.971Z" },
|
|
469
|
+
{ url = "https://files.pythonhosted.org/packages/72/11/5da87abe20047c8962361473923ebb2f62b595250126aadfad8c20649c1e/ruff-0.15.16-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb27515fa6240fb586ae82b901a59e67d24acff86f2190b433dc542fe0435aeb", size = 11373541, upload-time = "2026-06-04T16:32:47.007Z" },
|
|
470
|
+
{ url = "https://files.pythonhosted.org/packages/fe/2a/8554754c23a854ae3fd6b507e36ad61ddb121e298c6d5d617dec94ed0f14/ruff-0.15.16-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a267c46ba1593fc26b8eecbea050b39d40c0b6bb7781ee11c90a02cd10032951", size = 11353014, upload-time = "2026-06-04T16:32:34.795Z" },
|
|
471
|
+
{ url = "https://files.pythonhosted.org/packages/62/25/62ea41529ec89f742ea3fed9cb1059c72877ec7cf9b9e99ac9cf3294d1d9/ruff-0.15.16-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:528c68f39a91498a8d50e91ff5985df3d105782bab49cc378e73ac26bff083e8", size = 10737467, upload-time = "2026-06-04T16:32:26.348Z" },
|
|
472
|
+
{ url = "https://files.pythonhosted.org/packages/90/17/334d3ad9de4d40f9dd58fdd09e35ce64553bb501e2f19a839e2fb6be14fc/ruff-0.15.16-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7ed55c58950df60589a9a7a5d2f8fa5f54ebd287163be805adfe6ee95a9de123", size = 10521910, upload-time = "2026-06-04T16:32:32.54Z" },
|
|
473
|
+
{ url = "https://files.pythonhosted.org/packages/4d/bd/3ac7c6ae77a885c1004b3dda2446ea401768d24f851c14b4ad4b24f6639c/ruff-0.15.16-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d482feaf51512b50f9790ceb417a56a61dd1e9d9bf967662b9ed27c01b34f53a", size = 10979190, upload-time = "2026-06-04T16:32:57.492Z" },
|
|
474
|
+
{ url = "https://files.pythonhosted.org/packages/33/d7/609546e6a413c3f216fbf2a50c928f97c80939154f6a0503114094a86191/ruff-0.15.16-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1e15bc8c94513dae2a40cc9ef07c94fdd4ecc9e29dabebeebe170f952322c9e3", size = 11477014, upload-time = "2026-06-04T16:32:44.687Z" },
|
|
475
|
+
{ url = "https://files.pythonhosted.org/packages/74/0d/f2cd247ad32633a5c36e97141a2c21b11c6279f7957bc2ff360b1e08fddd/ruff-0.15.16-py3-none-win32.whl", hash = "sha256:580378f7bd4aa25f72e74aa54948a9622f142b1e509521dd10902e886681cc1e", size = 10735541, upload-time = "2026-06-04T16:32:30.145Z" },
|
|
476
|
+
{ url = "https://files.pythonhosted.org/packages/8b/9e/02e845ef151b1dee585e55c4739f8e1734ae1d9f1221dff65761c162208b/ruff-0.15.16-py3-none-win_amd64.whl", hash = "sha256:408256017284eddf98fff77b29aa4fb30f586042d535b2d9befc6512f400aaec", size = 11843403, upload-time = "2026-06-04T16:32:39.76Z" },
|
|
477
|
+
{ url = "https://files.pythonhosted.org/packages/15/19/016553f86f207450aebebc2b2b5088d086b901cc8186c02ac4284db3bd88/ruff-0.15.16-py3-none-win_arm64.whl", hash = "sha256:8cd61783afb39638a7133ef0d2dfb1e91277593962f81b5a8423eb0b888a6121", size = 11134555, upload-time = "2026-06-04T16:33:00.136Z" },
|
|
478
478
|
]
|
|
479
479
|
|
|
480
480
|
[[package]]
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import TYPE_CHECKING
|
|
4
|
-
|
|
5
|
-
from gridfleet_testkit.appium import build_appium_options
|
|
6
|
-
|
|
7
|
-
if TYPE_CHECKING:
|
|
8
|
-
import pytest
|
|
9
|
-
from appium.options.common import AppiumOptions
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def _caps(options: AppiumOptions) -> dict[str, object]:
|
|
13
|
-
return dict(options.to_capabilities())
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def test_injects_free_when_no_run_id_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
17
|
-
monkeypatch.delenv("GRIDFLEET_RUN_ID", raising=False)
|
|
18
|
-
|
|
19
|
-
options = build_appium_options(capabilities={"platformName": "Android"})
|
|
20
|
-
|
|
21
|
-
assert _caps(options)["gridfleet:run_id"] == "free"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def test_injects_run_id_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
25
|
-
monkeypatch.setenv("GRIDFLEET_RUN_ID", "run-123")
|
|
26
|
-
|
|
27
|
-
options = build_appium_options(capabilities={"platformName": "Android"})
|
|
28
|
-
|
|
29
|
-
assert _caps(options)["gridfleet:run_id"] == "run-123"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gridfleet_testkit-0.9.5 → gridfleet_testkit-0.10.0}/tests/test_sessions_resolve_device_handle.py
RENAMED
|
File without changes
|