testmu-playwright-python 0.1.0__tar.gz → 0.1.3__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.
- {testmu_playwright_python-0.1.0/testmu_playwright_python.egg-info → testmu_playwright_python-0.1.3}/PKG-INFO +14 -21
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/README.md +6 -8
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/pyproject.toml +10 -19
- testmu_playwright_python-0.1.3/testmu/_config.py +13 -0
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_decorator.py +10 -7
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_heal_patch.py +13 -3
- testmu_playwright_python-0.1.3/testmu/_helpers/_http.py +20 -0
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_helpers/assertion.py +13 -10
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_helpers/execute_api.py +9 -9
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_helpers/execute_db.py +9 -8
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_helpers/execute_js.py +2 -2
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_helpers/kane_cli.py +12 -8
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_helpers/network.py +10 -8
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_helpers/smartui.py +9 -2
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_helpers/vision.py +60 -73
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_helpers/wait.py +6 -4
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_reporter/__init__.py +10 -7
- testmu_playwright_python-0.1.3/testmu/_reporter/local.py +31 -0
- testmu_playwright_python-0.1.3/testmu/_reporter/lt.py +75 -0
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_reporter/null.py +1 -1
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_session.py +50 -7
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_vars.py +14 -14
- testmu_playwright_python-0.1.3/testmu/playwright_async/__init__.py +67 -0
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3/testmu_playwright_python.egg-info}/PKG-INFO +14 -21
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu_playwright_python.egg-info/SOURCES.txt +20 -3
- testmu_playwright_python-0.1.3/tests/test_capability.py +423 -0
- testmu_playwright_python-0.1.3/tests/test_config.py +65 -0
- testmu_playwright_python-0.1.3/tests/test_configure.py +73 -0
- testmu_playwright_python-0.1.3/tests/test_decorator.py +60 -0
- testmu_playwright_python-0.1.3/tests/test_heal_patch.py +97 -0
- testmu_playwright_python-0.1.3/tests/test_helper_assertion.py +361 -0
- testmu_playwright_python-0.1.3/tests/test_helper_execute_api.py +307 -0
- testmu_playwright_python-0.1.3/tests/test_helper_execute_db.py +269 -0
- testmu_playwright_python-0.1.3/tests/test_helper_execute_js.py +157 -0
- testmu_playwright_python-0.1.3/tests/test_helper_network_math.py +215 -0
- testmu_playwright_python-0.1.3/tests/test_helper_remaining.py +438 -0
- testmu_playwright_python-0.1.3/tests/test_helper_tabs_drag.py +150 -0
- testmu_playwright_python-0.1.3/tests/test_helper_vision.py +477 -0
- testmu_playwright_python-0.1.3/tests/test_helpers_stub.py +39 -0
- testmu_playwright_python-0.1.3/tests/test_integration.py +34 -0
- testmu_playwright_python-0.1.3/tests/test_step.py +54 -0
- testmu_playwright_python-0.1.3/tests/test_test_config.py +85 -0
- testmu_playwright_python-0.1.3/tests/test_vars.py +474 -0
- testmu_playwright_python-0.1.0/LICENSE +0 -21
- testmu_playwright_python-0.1.0/MANIFEST.in +0 -8
- testmu_playwright_python-0.1.0/testmu/_config.py +0 -9
- testmu_playwright_python-0.1.0/testmu/_reporter/local.py +0 -25
- testmu_playwright_python-0.1.0/testmu/_reporter/lt.py +0 -72
- testmu_playwright_python-0.1.0/testmu/playwright_async/__init__.py +0 -36
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/setup.cfg +0 -0
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/__init__.py +0 -0
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_capability.py +0 -0
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_configure.py +0 -0
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_errors.py +0 -0
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_helpers/__init__.py +0 -0
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_helpers/drag.py +0 -0
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_helpers/math.py +0 -0
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_helpers/tabs.py +0 -0
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_matchers.py +0 -0
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_step.py +0 -0
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_test_config.py +0 -0
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu_playwright_python.egg-info/dependency_links.txt +0 -0
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu_playwright_python.egg-info/requires.txt +0 -0
- {testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu_playwright_python.egg-info/top_level.txt +0 -0
|
@@ -1,25 +1,21 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: testmu-playwright-python
|
|
3
|
-
Version: 0.1.
|
|
4
|
-
Summary:
|
|
5
|
-
Author-email:
|
|
6
|
-
License-Expression:
|
|
7
|
-
Project-URL: Homepage, https://testmu
|
|
8
|
-
Project-URL:
|
|
9
|
-
Project-URL: Repository, https://github.com/testmuai/testmu-bindings
|
|
10
|
-
Keywords: testing,ai,agents,playwright,lambdatest,testmu
|
|
3
|
+
Version: 0.1.3
|
|
4
|
+
Summary: Testmu binding for Playwright Python — thin test runtime for LambdaTest exports
|
|
5
|
+
Author-email: LambdaTest <engineering@lambdatest.com>
|
|
6
|
+
License-Expression: LicenseRef-Proprietary
|
|
7
|
+
Project-URL: Homepage, https://github.com/shravan-lambdatest/testmu-bindings
|
|
8
|
+
Project-URL: Repository, https://github.com/shravan-lambdatest/testmu-bindings
|
|
11
9
|
Classifier: Development Status :: 3 - Alpha
|
|
12
10
|
Classifier: Intended Audience :: Developers
|
|
13
|
-
Classifier: Natural Language :: English
|
|
14
|
-
Classifier: Topic :: Software Development :: Testing
|
|
15
|
-
Classifier: Framework :: Pytest
|
|
16
11
|
Classifier: Programming Language :: Python :: 3
|
|
17
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
14
|
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Framework :: Pytest
|
|
16
|
+
Classifier: Topic :: Software Development :: Testing
|
|
20
17
|
Requires-Python: >=3.11
|
|
21
18
|
Description-Content-Type: text/markdown
|
|
22
|
-
License-File: LICENSE
|
|
23
19
|
Requires-Dist: playwright>=1.57.0
|
|
24
20
|
Requires-Dist: pyotp>=2.9.0
|
|
25
21
|
Requires-Dist: aiohttp
|
|
@@ -28,16 +24,18 @@ Requires-Dist: requests>=2.28.0
|
|
|
28
24
|
Provides-Extra: dev
|
|
29
25
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
30
26
|
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
|
|
31
|
-
Dynamic: license-file
|
|
32
27
|
|
|
33
28
|
# testmu-playwright-python
|
|
34
29
|
|
|
35
|
-
|
|
30
|
+
Testmu binding for Playwright Python — a thin test runtime for running LambdaTest exported tests locally or on the LambdaTest grid.
|
|
36
31
|
|
|
37
32
|
## Installation
|
|
38
33
|
|
|
39
34
|
```bash
|
|
40
|
-
|
|
35
|
+
# From TestPyPI (pre-release)
|
|
36
|
+
pip install --index-url https://test.pypi.org/simple/ \
|
|
37
|
+
--extra-index-url https://pypi.org/simple/ \
|
|
38
|
+
testmu-playwright-python
|
|
41
39
|
```
|
|
42
40
|
|
|
43
41
|
## Quick Start
|
|
@@ -75,11 +73,6 @@ testmu.run(my_test)
|
|
|
75
73
|
- Python >= 3.11
|
|
76
74
|
- Playwright >= 1.57.0
|
|
77
75
|
|
|
78
|
-
## Links
|
|
79
|
-
|
|
80
|
-
- Homepage: [testmu.ai](https://testmu.ai)
|
|
81
|
-
- Documentation: [docs.testmu.ai](https://docs.testmu.ai)
|
|
82
|
-
|
|
83
76
|
## License
|
|
84
77
|
|
|
85
|
-
|
|
78
|
+
Proprietary - LambdaTest
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
# testmu-playwright-python
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Testmu binding for Playwright Python — a thin test runtime for running LambdaTest exported tests locally or on the LambdaTest grid.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
8
|
+
# From TestPyPI (pre-release)
|
|
9
|
+
pip install --index-url https://test.pypi.org/simple/ \
|
|
10
|
+
--extra-index-url https://pypi.org/simple/ \
|
|
11
|
+
testmu-playwright-python
|
|
9
12
|
```
|
|
10
13
|
|
|
11
14
|
## Quick Start
|
|
@@ -43,11 +46,6 @@ testmu.run(my_test)
|
|
|
43
46
|
- Python >= 3.11
|
|
44
47
|
- Playwright >= 1.57.0
|
|
45
48
|
|
|
46
|
-
## Links
|
|
47
|
-
|
|
48
|
-
- Homepage: [testmu.ai](https://testmu.ai)
|
|
49
|
-
- Documentation: [docs.testmu.ai](https://docs.testmu.ai)
|
|
50
|
-
|
|
51
49
|
## License
|
|
52
50
|
|
|
53
|
-
|
|
51
|
+
Proprietary - LambdaTest
|
|
@@ -1,29 +1,24 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["setuptools>=68.0"
|
|
2
|
+
requires = ["setuptools>=68.0"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "testmu-playwright-python"
|
|
7
|
-
version = "0.1.
|
|
8
|
-
description = "
|
|
9
|
-
readme = "README.md"
|
|
7
|
+
version = "0.1.3"
|
|
8
|
+
description = "Testmu binding for Playwright Python — thin test runtime for LambdaTest exports"
|
|
10
9
|
requires-python = ">=3.11"
|
|
11
|
-
license = "
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
{name = "TestMu AI", email = "engineering@testmu.ai"},
|
|
15
|
-
]
|
|
16
|
-
keywords = ["testing", "ai", "agents", "playwright", "lambdatest", "testmu"]
|
|
10
|
+
license = "LicenseRef-Proprietary"
|
|
11
|
+
authors = [{name = "LambdaTest", email = "engineering@lambdatest.com"}]
|
|
12
|
+
readme = "README.md"
|
|
17
13
|
classifiers = [
|
|
18
14
|
"Development Status :: 3 - Alpha",
|
|
19
15
|
"Intended Audience :: Developers",
|
|
20
|
-
"Natural Language :: English",
|
|
21
|
-
"Topic :: Software Development :: Testing",
|
|
22
|
-
"Framework :: Pytest",
|
|
23
16
|
"Programming Language :: Python :: 3",
|
|
24
17
|
"Programming Language :: Python :: 3.11",
|
|
25
18
|
"Programming Language :: Python :: 3.12",
|
|
26
19
|
"Programming Language :: Python :: 3.13",
|
|
20
|
+
"Framework :: Pytest",
|
|
21
|
+
"Topic :: Software Development :: Testing",
|
|
27
22
|
]
|
|
28
23
|
dependencies = [
|
|
29
24
|
"playwright>=1.57.0",
|
|
@@ -40,12 +35,8 @@ dev = [
|
|
|
40
35
|
]
|
|
41
36
|
|
|
42
37
|
[project.urls]
|
|
43
|
-
Homepage = "https://testmu
|
|
44
|
-
|
|
45
|
-
Repository = "https://github.com/testmuai/testmu-bindings"
|
|
46
|
-
|
|
47
|
-
[tool.setuptools.packages.find]
|
|
48
|
-
include = ["testmu*"]
|
|
38
|
+
Homepage = "https://github.com/shravan-lambdatest/testmu-bindings"
|
|
39
|
+
Repository = "https://github.com/shravan-lambdatest/testmu-bindings"
|
|
49
40
|
|
|
50
41
|
[tool.pytest.ini_options]
|
|
51
42
|
asyncio_mode = "auto"
|
|
@@ -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"
|
|
@@ -34,13 +34,16 @@ def test(fn):
|
|
|
34
34
|
await rep.pass_test()
|
|
35
35
|
return result
|
|
36
36
|
except Exception as e:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
# Screenshot-on-failure disabled — attach_screenshot is a stub
|
|
38
|
+
# that doesn't upload bytes anywhere. Re-enable once LT CDP upload
|
|
39
|
+
# is wired up.
|
|
40
|
+
# page = _find_page(args, kwargs)
|
|
41
|
+
# if page is not None:
|
|
42
|
+
# try:
|
|
43
|
+
# screenshot = await page.screenshot()
|
|
44
|
+
# await rep.attach_screenshot(screenshot)
|
|
45
|
+
# except Exception:
|
|
46
|
+
# pass
|
|
44
47
|
await rep.fail_test(e)
|
|
45
48
|
raise
|
|
46
49
|
return async_wrapper
|
|
@@ -5,6 +5,16 @@ Installs wrappers on Locator action methods that catch TimeoutError
|
|
|
5
5
|
|
|
6
6
|
The patch only fires inside a testmu.step() block (ContextVar check).
|
|
7
7
|
Outside a step, the wrapper is a pure passthrough.
|
|
8
|
+
|
|
9
|
+
heal_fn contract:
|
|
10
|
+
async def heal_fn(page, description, method_name, *args, **kwargs) -> bool
|
|
11
|
+
|
|
12
|
+
Returns True if heal handled the action (wrapper returns normally).
|
|
13
|
+
Returns False if heal couldn't help (wrapper re-raises original error).
|
|
14
|
+
|
|
15
|
+
Today's default heal does a coordinate lookup via vision API and clicks
|
|
16
|
+
at the returned coordinates for click/hover methods. Other methods are
|
|
17
|
+
not supported until locator re-resolution lands.
|
|
8
18
|
"""
|
|
9
19
|
import functools
|
|
10
20
|
from testmu._step import _current_step
|
|
@@ -41,9 +51,9 @@ def _make_wrapper(name, original, heal_fn):
|
|
|
41
51
|
except TimeoutError:
|
|
42
52
|
if await self.count() > 0:
|
|
43
53
|
raise # element exists but not actionable — heal won't help
|
|
44
|
-
|
|
45
|
-
if
|
|
54
|
+
handled = await heal_fn(self.page, step.description, name, *args, **kwargs)
|
|
55
|
+
if not handled:
|
|
46
56
|
raise
|
|
47
|
-
|
|
57
|
+
# heal performed the action (e.g. via page.mouse) — nothing to return
|
|
48
58
|
|
|
49
59
|
return wrapper
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Shared HTTP session factory for AI API calls.
|
|
2
|
+
|
|
3
|
+
All smart helpers (vision, assertion, wait, network) call LT-hosted
|
|
4
|
+
endpoints that require Basic auth via LT_USERNAME + LT_ACCESS_KEY.
|
|
5
|
+
"""
|
|
6
|
+
import base64
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
import aiohttp
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def create_session(**kwargs) -> aiohttp.ClientSession:
|
|
13
|
+
"""Create an aiohttp session with LT Basic auth headers."""
|
|
14
|
+
headers = kwargs.pop("headers", {})
|
|
15
|
+
username = os.getenv("LT_USERNAME", "")
|
|
16
|
+
access_key = os.getenv("LT_ACCESS_KEY", "")
|
|
17
|
+
if username and access_key:
|
|
18
|
+
auth = base64.b64encode(f"{username}:{access_key}".encode()).decode()
|
|
19
|
+
headers["Authorization"] = f"Basic {auth}"
|
|
20
|
+
return aiohttp.ClientSession(headers=headers, **kwargs)
|
{testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_helpers/assertion.py
RENAMED
|
@@ -15,9 +15,14 @@ there are no store_keys (skips the visual fallback).
|
|
|
15
15
|
"""
|
|
16
16
|
import base64
|
|
17
17
|
import logging
|
|
18
|
+
import os
|
|
18
19
|
|
|
19
20
|
import aiohttp
|
|
20
21
|
|
|
22
|
+
from testmu._helpers._http import create_session
|
|
23
|
+
|
|
24
|
+
_AI_API_HOST = os.getenv("TESTMU_AI_API_HOST", "http://localhost:8000")
|
|
25
|
+
|
|
21
26
|
from testmu import _config
|
|
22
27
|
from testmu._vars import _variable_store, var
|
|
23
28
|
|
|
@@ -68,9 +73,9 @@ async def _call_evaluate_api(claim, composite_op, eval_sub_checks):
|
|
|
68
73
|
"composite_operator": composite_op,
|
|
69
74
|
"sub_checks": eval_sub_checks,
|
|
70
75
|
}
|
|
71
|
-
async with
|
|
76
|
+
async with create_session() as session:
|
|
72
77
|
async with session.post(
|
|
73
|
-
"
|
|
78
|
+
f"{_AI_API_HOST}/api/v1/evaluate",
|
|
74
79
|
json=request_body,
|
|
75
80
|
timeout=aiohttp.ClientTimeout(total=60),
|
|
76
81
|
) as response:
|
|
@@ -82,17 +87,16 @@ async def _call_evaluate_api(claim, composite_op, eval_sub_checks):
|
|
|
82
87
|
|
|
83
88
|
async def _evaluate_deterministic(claim, composite_op, sub_checks):
|
|
84
89
|
"""Deterministic path: resolve variables then call /api/v1/evaluate."""
|
|
85
|
-
_log.
|
|
90
|
+
_log.info(" [assertion] deterministic claim=%s", claim[:80])
|
|
86
91
|
eval_sub_checks = await _resolve_sub_checks(sub_checks)
|
|
87
|
-
_log.debug(f"[AssertionAPI] resolved sub_checks={eval_sub_checks!r}")
|
|
88
92
|
result = await _call_evaluate_api(claim, composite_op, eval_sub_checks)
|
|
89
|
-
_log.
|
|
93
|
+
_log.info(" [assertion] result status=%s", result.get("status", "unknown"))
|
|
90
94
|
return result
|
|
91
95
|
|
|
92
96
|
|
|
93
97
|
async def _verify_visual(page, claim, composite_op, sub_checks, assertion_tree):
|
|
94
98
|
"""Visual path: screenshot + LLM-based assertion via /api/v1/assertions/verify."""
|
|
95
|
-
_log.
|
|
99
|
+
_log.info(" [assertion] visual claim=%s", claim[:80])
|
|
96
100
|
|
|
97
101
|
screenshot_bytes = await page.screenshot()
|
|
98
102
|
screenshot_b64 = base64.b64encode(screenshot_bytes).decode("utf-8")
|
|
@@ -110,10 +114,9 @@ async def _verify_visual(page, claim, composite_op, sub_checks, assertion_tree):
|
|
|
110
114
|
if verification:
|
|
111
115
|
request_body["verification"] = verification
|
|
112
116
|
|
|
113
|
-
|
|
114
|
-
async with aiohttp.ClientSession() as session:
|
|
117
|
+
async with create_session() as session:
|
|
115
118
|
async with session.post(
|
|
116
|
-
"
|
|
119
|
+
f"{_AI_API_HOST}/api/v1/assertions/verify",
|
|
117
120
|
json=request_body,
|
|
118
121
|
timeout=aiohttp.ClientTimeout(total=60),
|
|
119
122
|
) as response:
|
|
@@ -122,7 +125,7 @@ async def _verify_visual(page, claim, composite_op, sub_checks, assertion_tree):
|
|
|
122
125
|
raise Exception(f"Assertion API error {response.status}: {text}")
|
|
123
126
|
result = await response.json()
|
|
124
127
|
|
|
125
|
-
_log.
|
|
128
|
+
_log.info(" [assertion] result status=%s", result.get("status", "unknown"))
|
|
126
129
|
return result
|
|
127
130
|
|
|
128
131
|
|
{testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_helpers/execute_api.py
RENAMED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
Variable resolution: {{var}} templates in URL, headers, body, and params are
|
|
4
4
|
resolved via testmu._vars.var() before the request is made.
|
|
5
5
|
|
|
6
|
-
Proxy routing: when running on HyperExecute (
|
|
7
|
-
requests through the local proxy at 127.0.0.1:22000.
|
|
8
|
-
direct requests.
|
|
6
|
+
Proxy routing: when running on HyperExecute (run_target == "cloud"),
|
|
7
|
+
routes all requests through the local proxy at 127.0.0.1:22000. On local
|
|
8
|
+
run target, makes direct requests.
|
|
9
9
|
|
|
10
10
|
Raises RuntimeError on request failure (network error or bad/unsupported input).
|
|
11
11
|
Returns the response dict directly on success.
|
|
@@ -166,8 +166,8 @@ def _do_request(method, url, headers, body, params, authorization, timeout, veri
|
|
|
166
166
|
_log.debug(f"AWS Signature auth failed: {e}")
|
|
167
167
|
|
|
168
168
|
# -- Proxy ---------------------------------------------------------------
|
|
169
|
-
# Route through HyperExecute local proxy when on
|
|
170
|
-
proxy = "http://127.0.0.1:22000" if _config.
|
|
169
|
+
# Route through HyperExecute local proxy only when running on cloud target.
|
|
170
|
+
proxy = "http://127.0.0.1:22000" if _config.run_target == "cloud" else None
|
|
171
171
|
|
|
172
172
|
# -- Build request kwargs ------------------------------------------------
|
|
173
173
|
_method = method.upper()
|
|
@@ -223,7 +223,7 @@ def _do_request(method, url, headers, body, params, authorization, timeout, veri
|
|
|
223
223
|
raise RuntimeError(f"Unsupported HTTP method: {_method!r}")
|
|
224
224
|
|
|
225
225
|
elapsed_ms = (time.time() - start) * 1000
|
|
226
|
-
_log.
|
|
226
|
+
_log.info(" [execute_api] status=%d time=%.0fms", response.status_code, elapsed_ms)
|
|
227
227
|
|
|
228
228
|
# -- Build response dict -------------------------------------------------
|
|
229
229
|
resp = {
|
|
@@ -281,8 +281,8 @@ async def execute_api(
|
|
|
281
281
|
):
|
|
282
282
|
"""Execute an HTTP API request.
|
|
283
283
|
|
|
284
|
-
Routes through the HyperExecute proxy (127.0.0.1:22000) when
|
|
285
|
-
|
|
284
|
+
Routes through the HyperExecute proxy (127.0.0.1:22000) when
|
|
285
|
+
run_target == "cloud"; makes direct requests on local runs.
|
|
286
286
|
|
|
287
287
|
Variable templates ({{var}}) in url, headers, body, and params are resolved
|
|
288
288
|
via testmu._vars.var() before the request is made.
|
|
@@ -304,7 +304,7 @@ async def execute_api(
|
|
|
304
304
|
Raises:
|
|
305
305
|
RuntimeError: On invalid URL, unsupported method, or request failure.
|
|
306
306
|
"""
|
|
307
|
-
_log.
|
|
307
|
+
_log.info(" [execute_api] %s %s", method, url[:80])
|
|
308
308
|
try:
|
|
309
309
|
return await asyncio.to_thread(
|
|
310
310
|
_do_request, method, url, headers, body, params,
|
{testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_helpers/execute_db.py
RENAMED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""execute_db helper — runs a SQL query via the automind /db-query endpoint.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Auth-gated: requires LT_USERNAME + LT_ACCESS_KEY to be set (automind is an
|
|
4
|
+
LT-hosted service). Works in both local and cloud run targets as long as
|
|
5
|
+
creds are present. Raises RuntimeError when creds are missing.
|
|
5
6
|
|
|
6
7
|
Env vars consumed (when not passed explicitly):
|
|
7
8
|
LT_USERNAME — LambdaTest username (used to build auth header)
|
|
@@ -10,7 +11,7 @@ Env vars consumed (when not passed explicitly):
|
|
|
10
11
|
LT_PROXY_TUNNEL_ID — Tunnel ID for network connectivity
|
|
11
12
|
|
|
12
13
|
Raises:
|
|
13
|
-
RuntimeError: When
|
|
14
|
+
RuntimeError: When LT creds are missing, on non-200 response, or on network failure.
|
|
14
15
|
|
|
15
16
|
Returns:
|
|
16
17
|
Query result dict on success.
|
|
@@ -71,7 +72,7 @@ async def execute_db(
|
|
|
71
72
|
):
|
|
72
73
|
"""Execute a database query via the automind /db-query endpoint.
|
|
73
74
|
|
|
74
|
-
|
|
75
|
+
Auth-gated: raises RuntimeError when LT_USERNAME/LT_ACCESS_KEY are not set.
|
|
75
76
|
|
|
76
77
|
Args:
|
|
77
78
|
query: Base64-encoded SQL query string.
|
|
@@ -86,13 +87,13 @@ async def execute_db(
|
|
|
86
87
|
Query result dict on success.
|
|
87
88
|
|
|
88
89
|
Raises:
|
|
89
|
-
RuntimeError: When
|
|
90
|
+
RuntimeError: When LT creds are missing, on non-200 response,
|
|
90
91
|
or on network/query failure.
|
|
91
92
|
"""
|
|
92
93
|
from testmu import _config
|
|
93
94
|
|
|
94
|
-
if not _config.
|
|
95
|
-
raise RuntimeError("DB query requires
|
|
95
|
+
if not _config.lt_auth:
|
|
96
|
+
raise RuntimeError("DB query requires LT_USERNAME and LT_ACCESS_KEY")
|
|
96
97
|
|
|
97
98
|
# Fall back to environment variables
|
|
98
99
|
if not automind_url:
|
|
@@ -105,7 +106,7 @@ async def execute_db(
|
|
|
105
106
|
if not tunnel_id:
|
|
106
107
|
tunnel_id = os.environ.get("LT_PROXY_TUNNEL_ID", "")
|
|
107
108
|
|
|
108
|
-
_log.
|
|
109
|
+
_log.info(" [execute_db] db_id=%r db_name=%r", db_id, db_name)
|
|
109
110
|
try:
|
|
110
111
|
return await asyncio.to_thread(
|
|
111
112
|
_do_query, query, db_id, db_name, timeout, tunnel_id, auth_header, automind_url
|
{testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_helpers/execute_js.py
RENAMED
|
@@ -35,7 +35,7 @@ async def execute_js(page, script: str):
|
|
|
35
35
|
from testmu._vars import _variable_store
|
|
36
36
|
|
|
37
37
|
preview = script[:100] + ("..." if len(script) > 100 else "")
|
|
38
|
-
_log.
|
|
38
|
+
_log.info(" [execute_js] %s", preview)
|
|
39
39
|
|
|
40
40
|
try:
|
|
41
41
|
user_js_code = script + "\n"
|
|
@@ -88,7 +88,7 @@ async def execute_js(page, script: str):
|
|
|
88
88
|
if result is None or result == "":
|
|
89
89
|
result = "null"
|
|
90
90
|
|
|
91
|
-
_log.
|
|
91
|
+
_log.info(" [execute_js] result=%s", str(result)[:100])
|
|
92
92
|
return result
|
|
93
93
|
|
|
94
94
|
except RuntimeError:
|
{testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_helpers/kane_cli.py
RENAMED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
"""Kane CLI helper — execute_kane_cli.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
When smart is OFF: logs "branch unresolved", returns None.
|
|
3
|
+
Gated: smart AND run_target == "cloud". kane-cli needs the HyperExecute
|
|
4
|
+
relay proxy to connect back to the running test, which is only available
|
|
5
|
+
in cloud runs. On local or smart-off runs, logs a warning and returns None.
|
|
7
6
|
|
|
8
7
|
Note: relay proxy disconnect/reconnect is NOT ported here — that's a
|
|
9
8
|
deferred gap. The subprocess runs kane-cli directly; if relay proxy is
|
|
10
|
-
absent the test may fail when kane-cli tries to connect
|
|
11
|
-
until relay proxy support is ported.
|
|
9
|
+
absent the test may fail when kane-cli tries to connect.
|
|
12
10
|
"""
|
|
13
11
|
import asyncio
|
|
14
12
|
import logging
|
|
@@ -22,8 +20,8 @@ _log = logging.getLogger("testmu")
|
|
|
22
20
|
async def execute_kane_cli(objective: str):
|
|
23
21
|
"""Execute a test objective via kane-cli subprocess.
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
Gated: smart ON AND run_target == "cloud". Logs a warning and returns
|
|
24
|
+
None without running kane-cli otherwise (branch unresolved).
|
|
27
25
|
|
|
28
26
|
Uses env vars:
|
|
29
27
|
LT_USERNAME — LambdaTest username (required by kane-cli auth)
|
|
@@ -42,6 +40,12 @@ async def execute_kane_cli(objective: str):
|
|
|
42
40
|
"[execute_kane_cli] smart is OFF — branch unresolved, skipping kane-cli"
|
|
43
41
|
)
|
|
44
42
|
return None
|
|
43
|
+
if _config.run_target != "cloud":
|
|
44
|
+
_log.warning(
|
|
45
|
+
"[execute_kane_cli] run_target is not 'cloud' — kane-cli needs the "
|
|
46
|
+
"HyperExecute relay proxy, skipping (branch unresolved)"
|
|
47
|
+
)
|
|
48
|
+
return None
|
|
45
49
|
|
|
46
50
|
username = os.getenv("LT_USERNAME", "")
|
|
47
51
|
access_key = os.getenv("LT_ACCESS_KEY", "")
|
{testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_helpers/network.py
RENAMED
|
@@ -104,13 +104,14 @@ def evaluate_network_assertion(assertion_tree: dict) -> bool:
|
|
|
104
104
|
|
|
105
105
|
|
|
106
106
|
# ---------------------------------------------------------------------------
|
|
107
|
-
# network_query —
|
|
107
|
+
# network_query — cloud-only, polls HAR server at localhost:8181
|
|
108
108
|
# ---------------------------------------------------------------------------
|
|
109
109
|
|
|
110
110
|
async def network_query(method, url, index, network_log_id="", polling_interval=2, max_polling_time=10):
|
|
111
111
|
"""Poll the HAR server for a matching network entry.
|
|
112
112
|
|
|
113
|
-
|
|
113
|
+
Cloud-only: the HAR server at localhost:8181 is a HyperExecute sidecar.
|
|
114
|
+
Returns None when run_target is not "cloud".
|
|
114
115
|
|
|
115
116
|
Args:
|
|
116
117
|
method: HTTP method to match (e.g. "GET", "POST").
|
|
@@ -121,16 +122,17 @@ async def network_query(method, url, index, network_log_id="", polling_interval=
|
|
|
121
122
|
max_polling_time: Total seconds before giving up.
|
|
122
123
|
|
|
123
124
|
Returns:
|
|
124
|
-
Decoded HAR entry dict, or None when
|
|
125
|
+
Decoded HAR entry dict, or None when not on cloud / no match found.
|
|
125
126
|
"""
|
|
126
127
|
from testmu import _config
|
|
127
128
|
|
|
128
|
-
if
|
|
129
|
-
_log.info("[network_query]
|
|
129
|
+
if _config.run_target != "cloud":
|
|
130
|
+
_log.info("[network_query] run_target is not 'cloud' — HAR server unavailable, skipping")
|
|
130
131
|
return None
|
|
131
132
|
|
|
132
133
|
import asyncio
|
|
133
134
|
import aiohttp
|
|
135
|
+
from testmu._helpers._http import create_session
|
|
134
136
|
|
|
135
137
|
_har_base = "http://127.0.0.1:8181"
|
|
136
138
|
|
|
@@ -141,7 +143,7 @@ async def network_query(method, url, index, network_log_id="", polling_interval=
|
|
|
141
143
|
|
|
142
144
|
if network_log_id:
|
|
143
145
|
try:
|
|
144
|
-
async with
|
|
146
|
+
async with create_session() as session:
|
|
145
147
|
async with session.get(
|
|
146
148
|
f"{_har_base}/logs/entry?id={network_log_id}",
|
|
147
149
|
timeout=aiohttp.ClientTimeout(total=30),
|
|
@@ -162,7 +164,7 @@ async def network_query(method, url, index, network_log_id="", polling_interval=
|
|
|
162
164
|
num_tries += 1
|
|
163
165
|
_log.info(f"[network_query] polling attempt {num_tries}/{max_tries}")
|
|
164
166
|
try:
|
|
165
|
-
async with
|
|
167
|
+
async with create_session() as session:
|
|
166
168
|
async with session.get(
|
|
167
169
|
f"{_har_base}/logs",
|
|
168
170
|
timeout=aiohttp.ClientTimeout(total=30),
|
|
@@ -187,7 +189,7 @@ async def network_query(method, url, index, network_log_id="", polling_interval=
|
|
|
187
189
|
f" entry_id={entry_id!r}"
|
|
188
190
|
)
|
|
189
191
|
if entry_id:
|
|
190
|
-
async with
|
|
192
|
+
async with create_session() as s2:
|
|
191
193
|
async with s2.get(
|
|
192
194
|
f"{_har_base}/logs/entry?id={entry_id}",
|
|
193
195
|
timeout=aiohttp.ClientTimeout(total=30),
|
{testmu_playwright_python-0.1.0 → testmu_playwright_python-0.1.3}/testmu/_helpers/smartui.py
RENAMED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"""SmartUI visual comparison screenshot.
|
|
2
2
|
|
|
3
3
|
Captures a screenshot and sends it to LambdaTest's SmartUI via the
|
|
4
|
-
lambdatest_action CDP protocol
|
|
4
|
+
lambdatest_action CDP protocol — cloud only (CDP channel is intercepted
|
|
5
|
+
server-side by LT infrastructure).
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
Gated: smart ON AND run_target == "cloud". Warns and no-ops otherwise.
|
|
7
8
|
"""
|
|
8
9
|
import json
|
|
9
10
|
import logging
|
|
@@ -22,6 +23,12 @@ async def smartui_snapshot(page, name):
|
|
|
22
23
|
"""
|
|
23
24
|
if not _config.smart:
|
|
24
25
|
return None
|
|
26
|
+
if _config.run_target != "cloud":
|
|
27
|
+
_log.warning(
|
|
28
|
+
f"SmartUI snapshot '{name}' skipped — requires cloud run target "
|
|
29
|
+
f"(TESTMU_RUN_TARGET=cloud)"
|
|
30
|
+
)
|
|
31
|
+
return None
|
|
25
32
|
|
|
26
33
|
try:
|
|
27
34
|
payload = json.dumps({
|