testmuai-playwright-bindings 0.1.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.
- testmuai_playwright_bindings-0.1.0/LICENSE +21 -0
- testmuai_playwright_bindings-0.1.0/MANIFEST.in +8 -0
- testmuai_playwright_bindings-0.1.0/PKG-INFO +118 -0
- testmuai_playwright_bindings-0.1.0/README.md +86 -0
- testmuai_playwright_bindings-0.1.0/pyproject.toml +52 -0
- testmuai_playwright_bindings-0.1.0/setup.cfg +4 -0
- testmuai_playwright_bindings-0.1.0/testmu/__init__.py +81 -0
- testmuai_playwright_bindings-0.1.0/testmu/_capability.py +263 -0
- testmuai_playwright_bindings-0.1.0/testmu/_config.py +13 -0
- testmuai_playwright_bindings-0.1.0/testmu/_configure.py +81 -0
- testmuai_playwright_bindings-0.1.0/testmu/_decorator.py +56 -0
- testmuai_playwright_bindings-0.1.0/testmu/_errors.py +6 -0
- testmuai_playwright_bindings-0.1.0/testmu/_heal_patch.py +59 -0
- testmuai_playwright_bindings-0.1.0/testmu/_helpers/__init__.py +70 -0
- testmuai_playwright_bindings-0.1.0/testmu/_helpers/_http.py +33 -0
- testmuai_playwright_bindings-0.1.0/testmu/_helpers/assertion.py +193 -0
- testmuai_playwright_bindings-0.1.0/testmu/_helpers/drag.py +6 -0
- testmuai_playwright_bindings-0.1.0/testmu/_helpers/execute_api.py +316 -0
- testmuai_playwright_bindings-0.1.0/testmu/_helpers/execute_db.py +117 -0
- testmuai_playwright_bindings-0.1.0/testmu/_helpers/execute_js.py +97 -0
- testmuai_playwright_bindings-0.1.0/testmu/_helpers/kane_cli.py +84 -0
- testmuai_playwright_bindings-0.1.0/testmu/_helpers/math.py +146 -0
- testmuai_playwright_bindings-0.1.0/testmu/_helpers/network.py +248 -0
- testmuai_playwright_bindings-0.1.0/testmu/_helpers/smartui.py +45 -0
- testmuai_playwright_bindings-0.1.0/testmu/_helpers/tabs.py +19 -0
- testmuai_playwright_bindings-0.1.0/testmu/_helpers/vision.py +486 -0
- testmuai_playwright_bindings-0.1.0/testmu/_helpers/wait.py +67 -0
- testmuai_playwright_bindings-0.1.0/testmu/_matchers.py +5 -0
- testmuai_playwright_bindings-0.1.0/testmu/_reporter/__init__.py +53 -0
- testmuai_playwright_bindings-0.1.0/testmu/_reporter/local.py +31 -0
- testmuai_playwright_bindings-0.1.0/testmu/_reporter/lt.py +82 -0
- testmuai_playwright_bindings-0.1.0/testmu/_reporter/null.py +10 -0
- testmuai_playwright_bindings-0.1.0/testmu/_session.py +169 -0
- testmuai_playwright_bindings-0.1.0/testmu/_step.py +60 -0
- testmuai_playwright_bindings-0.1.0/testmu/_test_config.py +112 -0
- testmuai_playwright_bindings-0.1.0/testmu/_vars.py +513 -0
- testmuai_playwright_bindings-0.1.0/testmu/playwright_async/__init__.py +67 -0
- testmuai_playwright_bindings-0.1.0/testmuai_playwright_bindings.egg-info/PKG-INFO +118 -0
- testmuai_playwright_bindings-0.1.0/testmuai_playwright_bindings.egg-info/SOURCES.txt +40 -0
- testmuai_playwright_bindings-0.1.0/testmuai_playwright_bindings.egg-info/dependency_links.txt +1 -0
- testmuai_playwright_bindings-0.1.0/testmuai_playwright_bindings.egg-info/requires.txt +9 -0
- testmuai_playwright_bindings-0.1.0/testmuai_playwright_bindings.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 TestMu AI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: testmuai-playwright-bindings
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: TestMu binding for Playwright Python — thin test runtime for TestMu-exported tests on the LambdaTest grid
|
|
5
|
+
Author-email: TestMu AI <engineering@testmu.ai>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/samyakLambda/testmu-bindings
|
|
8
|
+
Project-URL: Repository, https://github.com/samyakLambda/testmu-bindings
|
|
9
|
+
Project-URL: Issues, https://github.com/samyakLambda/testmu-bindings/issues
|
|
10
|
+
Keywords: testing,playwright,lambdatest,testmu,automation,e2e
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Natural Language :: English
|
|
14
|
+
Classifier: Topic :: Software Development :: Testing
|
|
15
|
+
Classifier: Framework :: Pytest
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: playwright>=1.57.0
|
|
24
|
+
Requires-Dist: pyotp>=2.9.0
|
|
25
|
+
Requires-Dist: aiohttp
|
|
26
|
+
Requires-Dist: httpx>=0.27.0
|
|
27
|
+
Requires-Dist: requests>=2.28.0
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# testmuai-playwright-bindings
|
|
34
|
+
|
|
35
|
+
[](https://pypi.org/project/testmuai-playwright-bindings/)
|
|
36
|
+
[](https://pypi.org/project/testmuai-playwright-bindings/)
|
|
37
|
+
[](LICENSE)
|
|
38
|
+
|
|
39
|
+
TestMu binding for Playwright Python — a thin test runtime for running TestMu-exported tests locally or on the LambdaTest grid.
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install testmuai-playwright-bindings
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Then install the Playwright browsers (one-time):
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
playwright install
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
Set credentials (LambdaTest grid) once in your environment:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
export LT_USERNAME="your_username"
|
|
59
|
+
export LT_ACCESS_KEY="your_access_key"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Then write a test:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
import testmu
|
|
66
|
+
|
|
67
|
+
@testmu.test
|
|
68
|
+
async def my_test(page):
|
|
69
|
+
async with testmu.step("Open site"):
|
|
70
|
+
await page.goto("https://example.com")
|
|
71
|
+
|
|
72
|
+
async with testmu.step("Verify title"):
|
|
73
|
+
await testmu.expect(page).to_have_title("Example Domain")
|
|
74
|
+
|
|
75
|
+
testmu.run(my_test)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`testmu.run()` is a synchronous entry point — it sets up the session,
|
|
79
|
+
launches the browser, runs your test, and tears everything down.
|
|
80
|
+
|
|
81
|
+
## Configuration
|
|
82
|
+
|
|
83
|
+
Configuration is sourced from environment variables. See [`.env.example`](.env.example) for the full list. The most common ones:
|
|
84
|
+
|
|
85
|
+
| Variable | Purpose | Required |
|
|
86
|
+
| ----------------- | ------------------------------------- | ------------------ |
|
|
87
|
+
| `LT_USERNAME` | LambdaTest grid username | For cloud features |
|
|
88
|
+
| `LT_ACCESS_KEY` | LambdaTest grid access key | For cloud features |
|
|
89
|
+
| `TESTMU_HEADLESS` | `true` (default) or `false` | No |
|
|
90
|
+
| `TESTMU_SMART` | `1` to enable vision/heal AI features | No |
|
|
91
|
+
|
|
92
|
+
Test-level metadata (build, name, variables, etc.) can be passed via
|
|
93
|
+
`testmu.configure(...)` at the top of generated test files.
|
|
94
|
+
|
|
95
|
+
## Features
|
|
96
|
+
|
|
97
|
+
- **`@testmu.test`** — decorator that wraps async Playwright tests with session lifecycle management
|
|
98
|
+
- **`testmu.step(...)`** — context manager for structured test steps
|
|
99
|
+
- **`testmu.var()` / `testmu.set_var()`** — template-based variable resolution (with ATMS lookup when authed)
|
|
100
|
+
- **Helpers** — `execute_js`, `execute_api`, `execute_db`, `vision_query`, `network_query`, `smartui_snapshot`, and more
|
|
101
|
+
- **Reporters** — local console reporter and TestMu cloud reporter (LambdaTest dashboard)
|
|
102
|
+
- **Self-healing locators** — automatic locator recovery via heal patch (when smart mode enabled)
|
|
103
|
+
|
|
104
|
+
## Requirements
|
|
105
|
+
|
|
106
|
+
- Python ≥ 3.11
|
|
107
|
+
- Playwright ≥ 1.57.0
|
|
108
|
+
|
|
109
|
+
## Links
|
|
110
|
+
|
|
111
|
+
- **PyPI**: https://pypi.org/project/testmuai-playwright-bindings/
|
|
112
|
+
- **TestMu**: https://testmu.ai
|
|
113
|
+
- **LambdaTest grid**: https://www.lambdatest.com
|
|
114
|
+
- **Issues**: https://github.com/samyakLambda/testmu-bindings/issues
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
Released under the [MIT License](LICENSE).
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# testmuai-playwright-bindings
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/testmuai-playwright-bindings/)
|
|
4
|
+
[](https://pypi.org/project/testmuai-playwright-bindings/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
TestMu binding for Playwright Python — a thin test runtime for running TestMu-exported tests locally or on the LambdaTest grid.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install testmuai-playwright-bindings
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Then install the Playwright browsers (one-time):
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
playwright install
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
Set credentials (LambdaTest grid) once in your environment:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
export LT_USERNAME="your_username"
|
|
27
|
+
export LT_ACCESS_KEY="your_access_key"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Then write a test:
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
import testmu
|
|
34
|
+
|
|
35
|
+
@testmu.test
|
|
36
|
+
async def my_test(page):
|
|
37
|
+
async with testmu.step("Open site"):
|
|
38
|
+
await page.goto("https://example.com")
|
|
39
|
+
|
|
40
|
+
async with testmu.step("Verify title"):
|
|
41
|
+
await testmu.expect(page).to_have_title("Example Domain")
|
|
42
|
+
|
|
43
|
+
testmu.run(my_test)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`testmu.run()` is a synchronous entry point — it sets up the session,
|
|
47
|
+
launches the browser, runs your test, and tears everything down.
|
|
48
|
+
|
|
49
|
+
## Configuration
|
|
50
|
+
|
|
51
|
+
Configuration is sourced from environment variables. See [`.env.example`](.env.example) for the full list. The most common ones:
|
|
52
|
+
|
|
53
|
+
| Variable | Purpose | Required |
|
|
54
|
+
| ----------------- | ------------------------------------- | ------------------ |
|
|
55
|
+
| `LT_USERNAME` | LambdaTest grid username | For cloud features |
|
|
56
|
+
| `LT_ACCESS_KEY` | LambdaTest grid access key | For cloud features |
|
|
57
|
+
| `TESTMU_HEADLESS` | `true` (default) or `false` | No |
|
|
58
|
+
| `TESTMU_SMART` | `1` to enable vision/heal AI features | No |
|
|
59
|
+
|
|
60
|
+
Test-level metadata (build, name, variables, etc.) can be passed via
|
|
61
|
+
`testmu.configure(...)` at the top of generated test files.
|
|
62
|
+
|
|
63
|
+
## Features
|
|
64
|
+
|
|
65
|
+
- **`@testmu.test`** — decorator that wraps async Playwright tests with session lifecycle management
|
|
66
|
+
- **`testmu.step(...)`** — context manager for structured test steps
|
|
67
|
+
- **`testmu.var()` / `testmu.set_var()`** — template-based variable resolution (with ATMS lookup when authed)
|
|
68
|
+
- **Helpers** — `execute_js`, `execute_api`, `execute_db`, `vision_query`, `network_query`, `smartui_snapshot`, and more
|
|
69
|
+
- **Reporters** — local console reporter and TestMu cloud reporter (LambdaTest dashboard)
|
|
70
|
+
- **Self-healing locators** — automatic locator recovery via heal patch (when smart mode enabled)
|
|
71
|
+
|
|
72
|
+
## Requirements
|
|
73
|
+
|
|
74
|
+
- Python ≥ 3.11
|
|
75
|
+
- Playwright ≥ 1.57.0
|
|
76
|
+
|
|
77
|
+
## Links
|
|
78
|
+
|
|
79
|
+
- **PyPI**: https://pypi.org/project/testmuai-playwright-bindings/
|
|
80
|
+
- **TestMu**: https://testmu.ai
|
|
81
|
+
- **LambdaTest grid**: https://www.lambdatest.com
|
|
82
|
+
- **Issues**: https://github.com/samyakLambda/testmu-bindings/issues
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
Released under the [MIT License](LICENSE).
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "testmuai-playwright-bindings"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "TestMu binding for Playwright Python — thin test runtime for TestMu-exported tests on the LambdaTest grid"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [
|
|
14
|
+
{name = "TestMu AI", email = "engineering@testmu.ai"},
|
|
15
|
+
]
|
|
16
|
+
keywords = ["testing", "playwright", "lambdatest", "testmu", "automation", "e2e"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 3 - Alpha",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"Natural Language :: English",
|
|
21
|
+
"Topic :: Software Development :: Testing",
|
|
22
|
+
"Framework :: Pytest",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Programming Language :: Python :: 3.13",
|
|
27
|
+
]
|
|
28
|
+
dependencies = [
|
|
29
|
+
"playwright>=1.57.0",
|
|
30
|
+
"pyotp>=2.9.0",
|
|
31
|
+
"aiohttp",
|
|
32
|
+
"httpx>=0.27.0",
|
|
33
|
+
"requests>=2.28.0",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
dev = [
|
|
38
|
+
"pytest>=7.0",
|
|
39
|
+
"pytest-asyncio>=0.21",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[project.urls]
|
|
43
|
+
Homepage = "https://github.com/samyakLambda/testmu-bindings"
|
|
44
|
+
Repository = "https://github.com/samyakLambda/testmu-bindings"
|
|
45
|
+
Issues = "https://github.com/samyakLambda/testmu-bindings/issues"
|
|
46
|
+
|
|
47
|
+
[tool.setuptools.packages.find]
|
|
48
|
+
include = ["testmu*"]
|
|
49
|
+
|
|
50
|
+
[tool.pytest.ini_options]
|
|
51
|
+
asyncio_mode = "auto"
|
|
52
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""TestMu binding for Playwright Python.
|
|
2
|
+
|
|
3
|
+
Public API:
|
|
4
|
+
testmu.test — decorator for test functions
|
|
5
|
+
testmu.step(...) — step context manager
|
|
6
|
+
testmu.run(fn) — session lifecycle runner
|
|
7
|
+
var(template) — variable/template resolver
|
|
8
|
+
set_var(name, value) — store variable
|
|
9
|
+
expect — PW expect with custom matchers
|
|
10
|
+
testmu.<helper>(...) — helper functions (execute_js, vision_query, etc.)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from importlib.metadata import PackageNotFoundError, version as _pkg_version
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
__version__ = _pkg_version("testmu-playwright-python")
|
|
17
|
+
except PackageNotFoundError:
|
|
18
|
+
__version__ = "0.0.0+unknown"
|
|
19
|
+
|
|
20
|
+
from testmu._configure import configure
|
|
21
|
+
from testmu._decorator import test
|
|
22
|
+
from testmu._step import step
|
|
23
|
+
from testmu._session import run
|
|
24
|
+
from testmu._vars import var, set_var
|
|
25
|
+
from testmu._matchers import expect
|
|
26
|
+
from testmu._errors import TestmuConfigError
|
|
27
|
+
|
|
28
|
+
# Helpers — vision functions ported; remaining stubs will be replaced in Layer B
|
|
29
|
+
from testmu._helpers import (
|
|
30
|
+
execute_js,
|
|
31
|
+
execute_api,
|
|
32
|
+
execute_db,
|
|
33
|
+
vision_query,
|
|
34
|
+
textual_query,
|
|
35
|
+
vision_wait,
|
|
36
|
+
vision_action,
|
|
37
|
+
verify_assertion,
|
|
38
|
+
evaluate_branch,
|
|
39
|
+
network_query,
|
|
40
|
+
evaluate_network_assertion,
|
|
41
|
+
evaluate_math,
|
|
42
|
+
smartui_snapshot,
|
|
43
|
+
switch_tab,
|
|
44
|
+
click_drag,
|
|
45
|
+
execute_kane_cli,
|
|
46
|
+
check_until_condition,
|
|
47
|
+
get_vision_coordinates,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Install PW-specific heal patch
|
|
51
|
+
import testmu.playwright_async # noqa: F401 — triggers _install_heal()
|
|
52
|
+
|
|
53
|
+
__all__ = [
|
|
54
|
+
"__version__",
|
|
55
|
+
"configure",
|
|
56
|
+
"test",
|
|
57
|
+
"step",
|
|
58
|
+
"run",
|
|
59
|
+
"var",
|
|
60
|
+
"set_var",
|
|
61
|
+
"expect",
|
|
62
|
+
"execute_js",
|
|
63
|
+
"execute_api",
|
|
64
|
+
"execute_db",
|
|
65
|
+
"vision_query",
|
|
66
|
+
"textual_query",
|
|
67
|
+
"vision_wait",
|
|
68
|
+
"vision_action",
|
|
69
|
+
"verify_assertion",
|
|
70
|
+
"evaluate_branch",
|
|
71
|
+
"network_query",
|
|
72
|
+
"evaluate_network_assertion",
|
|
73
|
+
"evaluate_math",
|
|
74
|
+
"smartui_snapshot",
|
|
75
|
+
"switch_tab",
|
|
76
|
+
"click_drag",
|
|
77
|
+
"execute_kane_cli",
|
|
78
|
+
"check_until_condition",
|
|
79
|
+
"get_vision_coordinates",
|
|
80
|
+
"TestmuConfigError",
|
|
81
|
+
]
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
"""TestMu capability builder for Playwright Python (LambdaTest grid).
|
|
2
|
+
|
|
3
|
+
Configuration is sourced from testmu.configure() data first, then falls back
|
|
4
|
+
to environment variables (no sys.argv / test-run JSON).
|
|
5
|
+
This module is the env-var-only equivalent of the capability.py template produced by
|
|
6
|
+
create_capabilities_file_playwright() in auteur-code-export.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import urllib.parse
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from testmu import _configure
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
from importlib.metadata import version as _pkg_version
|
|
18
|
+
|
|
19
|
+
_PW_VERSION = _pkg_version("playwright")
|
|
20
|
+
except Exception:
|
|
21
|
+
_PW_VERSION = "unknown"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
# Internal helpers (match template helpers exactly)
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _get_download_folder() -> Path:
|
|
30
|
+
"""Return the system Downloads folder, creating it if needed."""
|
|
31
|
+
if os.name == "nt":
|
|
32
|
+
downloads_path = Path(
|
|
33
|
+
os.path.join(os.environ.get("USERPROFILE", ""), "Downloads")
|
|
34
|
+
)
|
|
35
|
+
else:
|
|
36
|
+
downloads_path = Path(os.path.expanduser("~/Downloads"))
|
|
37
|
+
downloads_path.mkdir(parents=True, exist_ok=True)
|
|
38
|
+
return downloads_path
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _get_cap_value(cap_name: str, default_value=None):
|
|
42
|
+
"""Return env var value or default — mirrors get_cap_value(None, ...) path."""
|
|
43
|
+
return os.getenv(cap_name, default_value)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# ---------------------------------------------------------------------------
|
|
47
|
+
# Public API
|
|
48
|
+
# ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def get_capabilities(
|
|
52
|
+
browser: str = None,
|
|
53
|
+
browser_version: str = None,
|
|
54
|
+
resolution: str = None,
|
|
55
|
+
platform: str = None,
|
|
56
|
+
*,
|
|
57
|
+
# baked-in values (set at code-export time, overridable by env)
|
|
58
|
+
build: str = None,
|
|
59
|
+
name: str = None,
|
|
60
|
+
tc_id: str = None,
|
|
61
|
+
network_caps: bool = False,
|
|
62
|
+
multiple_profiles: bool = False,
|
|
63
|
+
chrome_options: list = None,
|
|
64
|
+
custom_headers: dict = None,
|
|
65
|
+
) -> dict:
|
|
66
|
+
"""Build and return the LambdaTest capabilities dict.
|
|
67
|
+
|
|
68
|
+
All parameters fall back to env vars when not supplied, mirroring the
|
|
69
|
+
template's get_cap_value(test_config, ...) with test_config=None path.
|
|
70
|
+
|
|
71
|
+
After building, sets smart_os / smart_os_version / smart_browser_name /
|
|
72
|
+
smart_browser_version env vars (same as the template).
|
|
73
|
+
"""
|
|
74
|
+
if chrome_options is None:
|
|
75
|
+
chrome_options = _configure.get("chrome_options", [])
|
|
76
|
+
if custom_headers is None:
|
|
77
|
+
custom_headers = _configure.get("custom_headers", {})
|
|
78
|
+
|
|
79
|
+
# Resolve top-level fields from env or defaults
|
|
80
|
+
_browser = browser or os.getenv("LT_BROWSER", "Chrome")
|
|
81
|
+
_browser_version = browser_version or os.getenv("LT_BROWSER_VERSION", "latest")
|
|
82
|
+
_resolution = resolution or os.getenv("LT_RESOLUTION", "1920x1080")
|
|
83
|
+
_platform = platform or os.getenv("LT_PLATFORM", "linux")
|
|
84
|
+
|
|
85
|
+
username = os.getenv("LT_USERNAME")
|
|
86
|
+
access_key = os.getenv("LT_ACCESS_KEY")
|
|
87
|
+
|
|
88
|
+
# HE test-run config — checked after configure(), before env vars
|
|
89
|
+
from testmu._test_config import get_cap_value as _he_cap
|
|
90
|
+
|
|
91
|
+
# video/visual/console/network coerce env strings to bool-like; template
|
|
92
|
+
# passes True/False from get_cap_value so booleans are fine here.
|
|
93
|
+
def _bool_env(key: str, default: bool) -> bool:
|
|
94
|
+
val = os.getenv(key)
|
|
95
|
+
if val is None:
|
|
96
|
+
return default
|
|
97
|
+
return val.lower() in ("1", "true", "yes")
|
|
98
|
+
|
|
99
|
+
def _bool_he(key: str, default: bool) -> bool:
|
|
100
|
+
"""Check test_config first, then env var, then default."""
|
|
101
|
+
val = _he_cap(key, None)
|
|
102
|
+
if val is None:
|
|
103
|
+
return default
|
|
104
|
+
if isinstance(val, bool):
|
|
105
|
+
return val
|
|
106
|
+
return str(val).lower() in ("1", "true", "yes")
|
|
107
|
+
|
|
108
|
+
# Build + name: configure() > test_config > env > default
|
|
109
|
+
_build = _configure.get("build") or _he_cap(
|
|
110
|
+
"BUILD", os.getenv("BUILD", build or "")
|
|
111
|
+
)
|
|
112
|
+
_name = _configure.get("name") or _he_cap(
|
|
113
|
+
"TEST_NAME", os.getenv("TEST_NAME", name or "")
|
|
114
|
+
)
|
|
115
|
+
_tc_id = _configure.get("tc_id") or tc_id or os.getenv("LT_TC_ID", "")
|
|
116
|
+
|
|
117
|
+
# For bool fields: configure() wins when explicitly set, then test_config > env > default.
|
|
118
|
+
_cfg_network = _configure.get("network")
|
|
119
|
+
if _configure.was_set("network"):
|
|
120
|
+
_network = bool(_cfg_network)
|
|
121
|
+
else:
|
|
122
|
+
_network = _bool_he("NETWORK", network_caps)
|
|
123
|
+
|
|
124
|
+
_cfg_multiple_profiles = _configure.get("multiple_profiles")
|
|
125
|
+
if _configure.was_set("multiple_profiles"):
|
|
126
|
+
multiple_profiles = _cfg_multiple_profiles
|
|
127
|
+
|
|
128
|
+
# Bool caps: configure() has no explicit setters for these, so test_config > env > default.
|
|
129
|
+
_video = _bool_he("VIDEO", True)
|
|
130
|
+
_visual = _bool_he("VISUAL", True)
|
|
131
|
+
_console = _bool_he("CONSOLE", True)
|
|
132
|
+
_tunnel = _bool_he("TUNNEL", False)
|
|
133
|
+
_performance = _bool_he("PERFORMANCE", False)
|
|
134
|
+
_dedicated_proxy = _bool_he("DEDICATED_PROXY", False)
|
|
135
|
+
_accessibility = _bool_he("ACCESSIBILITY", False)
|
|
136
|
+
_idle_timeout = int(_he_cap("IDLE_TIMEOUT", os.getenv("IDLE_TIMEOUT", "1800")))
|
|
137
|
+
|
|
138
|
+
capabilities = {
|
|
139
|
+
"browserName": _browser,
|
|
140
|
+
"browserVersion": _browser_version,
|
|
141
|
+
"LT:Options": {
|
|
142
|
+
"platform": _platform,
|
|
143
|
+
"user": username,
|
|
144
|
+
"accessKey": access_key,
|
|
145
|
+
"video": _video,
|
|
146
|
+
"resolution": _resolution,
|
|
147
|
+
"network": _network,
|
|
148
|
+
"network.full.har": _network,
|
|
149
|
+
"build": _build,
|
|
150
|
+
"project": "Auteur-Code-Export",
|
|
151
|
+
"name": _name,
|
|
152
|
+
"w3c": True,
|
|
153
|
+
"plugin": "python-python",
|
|
154
|
+
"visual": _visual,
|
|
155
|
+
"console": _console,
|
|
156
|
+
"tms.tc_id": _tc_id,
|
|
157
|
+
"loadExtensions": [os.getenv("EXTENSION")],
|
|
158
|
+
"playwrightClientVersion": _PW_VERSION,
|
|
159
|
+
"tunnel": _tunnel,
|
|
160
|
+
"performance": _performance,
|
|
161
|
+
"dedicatedProxy": _dedicated_proxy,
|
|
162
|
+
"idleTimeout": _idle_timeout,
|
|
163
|
+
"accessibility": _accessibility,
|
|
164
|
+
"hideInternalCommandLogs": True,
|
|
165
|
+
"dependentTestsInScenario": multiple_profiles,
|
|
166
|
+
},
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
# Geolocation — conditional (matches: if os.getenv("GEO_LOCATION", False))
|
|
170
|
+
if os.getenv("GEO_LOCATION", False):
|
|
171
|
+
capabilities["LT:Options"]["geoLocation"] = os.getenv("GEO_LOCATION")
|
|
172
|
+
|
|
173
|
+
# Timezone — configure() > test_config (dict with "region") > env var
|
|
174
|
+
_timezone = _configure.get("timezone") or ""
|
|
175
|
+
if not _timezone:
|
|
176
|
+
from testmu._test_config import load_test_config
|
|
177
|
+
|
|
178
|
+
_tc = load_test_config()
|
|
179
|
+
if _tc:
|
|
180
|
+
_tz_val = _tc.get("timezone", {})
|
|
181
|
+
if isinstance(_tz_val, dict) and _tz_val.get("region"):
|
|
182
|
+
_timezone = _tz_val["region"]
|
|
183
|
+
if not _timezone:
|
|
184
|
+
_timezone = os.getenv("TIMEZONE", "")
|
|
185
|
+
if _timezone:
|
|
186
|
+
capabilities["LT:Options"]["timezone"] = _timezone
|
|
187
|
+
|
|
188
|
+
# Custom headers — conditional
|
|
189
|
+
if custom_headers:
|
|
190
|
+
capabilities["LT:Options"]["customHeaders"] = custom_headers
|
|
191
|
+
|
|
192
|
+
# Browser-specific options
|
|
193
|
+
if _browser.lower() in ("chrome", "pw-chromium"):
|
|
194
|
+
default_args = [
|
|
195
|
+
"--enable-logging",
|
|
196
|
+
"--disable-notifications",
|
|
197
|
+
"--no-sandbox",
|
|
198
|
+
"--log-level=0",
|
|
199
|
+
"--ignore-certificate-errors",
|
|
200
|
+
"--disable-blink-features=AutomationControlled",
|
|
201
|
+
"--password-store=basic",
|
|
202
|
+
"--safebrowsing-disable-auto-update",
|
|
203
|
+
"--disable-sync",
|
|
204
|
+
]
|
|
205
|
+
capabilities["LT:Options"]["goog:chromeOptions"] = default_args
|
|
206
|
+
for op in chrome_options:
|
|
207
|
+
op_key = op.get("key", "")
|
|
208
|
+
op_type = op.get("type", "")
|
|
209
|
+
if op_type == "file":
|
|
210
|
+
op_value = os.path.join(
|
|
211
|
+
str(_get_download_folder()), op.get("value", "")
|
|
212
|
+
)
|
|
213
|
+
else:
|
|
214
|
+
op_value = op.get("value", "")
|
|
215
|
+
if op_type == "no-args":
|
|
216
|
+
capabilities["LT:Options"]["goog:chromeOptions"].append(f"{op_key}")
|
|
217
|
+
else:
|
|
218
|
+
capabilities["LT:Options"]["goog:chromeOptions"].append(
|
|
219
|
+
f"{op_key}={op_value}"
|
|
220
|
+
)
|
|
221
|
+
elif _browser.lower() == "microsoftedge":
|
|
222
|
+
default_args = [
|
|
223
|
+
"--enable-logging",
|
|
224
|
+
"--disable-notifications",
|
|
225
|
+
"--log-level=0",
|
|
226
|
+
"--disable-blink-features=AutomationControlled",
|
|
227
|
+
"--password-store=basic",
|
|
228
|
+
"--safebrowsing-disable-auto-update",
|
|
229
|
+
"--disable-sync",
|
|
230
|
+
]
|
|
231
|
+
capabilities["LT:Options"]["ms:edgeOptions"] = default_args
|
|
232
|
+
elif _browser.lower() in ("firefox", "pw-firefox"):
|
|
233
|
+
capabilities["LT:Options"]["firefoxUserPrefs"] = {
|
|
234
|
+
"dom.webnotifications.enabled": False,
|
|
235
|
+
"signon.rememberSignons": False,
|
|
236
|
+
"signon.autofillForms": False,
|
|
237
|
+
"permissions.default.desktop-notification": 2,
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
# Set smart env vars (same as template)
|
|
241
|
+
os.environ["smart_os"] = capabilities["LT:Options"]["platform"]
|
|
242
|
+
os.environ["smart_os_version"] = "latest"
|
|
243
|
+
os.environ["smart_browser_name"] = capabilities["browserName"]
|
|
244
|
+
os.environ["smart_browser_version"] = capabilities["browserVersion"]
|
|
245
|
+
|
|
246
|
+
return capabilities
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def get_cdp_url(capabilities: dict) -> str:
|
|
250
|
+
"""Build the LambdaTest CDP WebSocket URL from a capabilities dict."""
|
|
251
|
+
return "wss://cdp.lambdatest.com/playwright?capabilities=" + urllib.parse.quote(
|
|
252
|
+
json.dumps(capabilities)
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def get_viewport(resolution: str = None) -> dict:
|
|
257
|
+
"""Parse a resolution string like '1920x1080' into a viewport dict."""
|
|
258
|
+
_res = resolution or os.getenv("LT_RESOLUTION", "1920x1080")
|
|
259
|
+
try:
|
|
260
|
+
width_str, height_str = _res.lower().split("x")
|
|
261
|
+
return {"width": int(width_str), "height": int(height_str)}
|
|
262
|
+
except (ValueError, AttributeError):
|
|
263
|
+
return {"width": 1920, "height": 1080}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Run target, auth, and smart detection.
|
|
2
|
+
|
|
3
|
+
Three orthogonal flags:
|
|
4
|
+
- run_target: "local" (default) or "cloud". Only "cloud" connects to LT CDP.
|
|
5
|
+
- lt_auth: LT creds available. Enables ATMS/automind/downloads/API proxy
|
|
6
|
+
regardless of run target.
|
|
7
|
+
- smart: AI/heal features. Requires lt_auth; validated at testmu.run().
|
|
8
|
+
"""
|
|
9
|
+
import os
|
|
10
|
+
|
|
11
|
+
run_target = os.getenv("TESTMU_RUN_TARGET", "local").lower()
|
|
12
|
+
lt_auth = bool(os.getenv("LT_USERNAME")) and bool(os.getenv("LT_ACCESS_KEY"))
|
|
13
|
+
smart = os.getenv("TESTMU_SMART", "0") == "1"
|