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.
Files changed (42) hide show
  1. testmuai_playwright_bindings-0.1.0/LICENSE +21 -0
  2. testmuai_playwright_bindings-0.1.0/MANIFEST.in +8 -0
  3. testmuai_playwright_bindings-0.1.0/PKG-INFO +118 -0
  4. testmuai_playwright_bindings-0.1.0/README.md +86 -0
  5. testmuai_playwright_bindings-0.1.0/pyproject.toml +52 -0
  6. testmuai_playwright_bindings-0.1.0/setup.cfg +4 -0
  7. testmuai_playwright_bindings-0.1.0/testmu/__init__.py +81 -0
  8. testmuai_playwright_bindings-0.1.0/testmu/_capability.py +263 -0
  9. testmuai_playwright_bindings-0.1.0/testmu/_config.py +13 -0
  10. testmuai_playwright_bindings-0.1.0/testmu/_configure.py +81 -0
  11. testmuai_playwright_bindings-0.1.0/testmu/_decorator.py +56 -0
  12. testmuai_playwright_bindings-0.1.0/testmu/_errors.py +6 -0
  13. testmuai_playwright_bindings-0.1.0/testmu/_heal_patch.py +59 -0
  14. testmuai_playwright_bindings-0.1.0/testmu/_helpers/__init__.py +70 -0
  15. testmuai_playwright_bindings-0.1.0/testmu/_helpers/_http.py +33 -0
  16. testmuai_playwright_bindings-0.1.0/testmu/_helpers/assertion.py +193 -0
  17. testmuai_playwright_bindings-0.1.0/testmu/_helpers/drag.py +6 -0
  18. testmuai_playwright_bindings-0.1.0/testmu/_helpers/execute_api.py +316 -0
  19. testmuai_playwright_bindings-0.1.0/testmu/_helpers/execute_db.py +117 -0
  20. testmuai_playwright_bindings-0.1.0/testmu/_helpers/execute_js.py +97 -0
  21. testmuai_playwright_bindings-0.1.0/testmu/_helpers/kane_cli.py +84 -0
  22. testmuai_playwright_bindings-0.1.0/testmu/_helpers/math.py +146 -0
  23. testmuai_playwright_bindings-0.1.0/testmu/_helpers/network.py +248 -0
  24. testmuai_playwright_bindings-0.1.0/testmu/_helpers/smartui.py +45 -0
  25. testmuai_playwright_bindings-0.1.0/testmu/_helpers/tabs.py +19 -0
  26. testmuai_playwright_bindings-0.1.0/testmu/_helpers/vision.py +486 -0
  27. testmuai_playwright_bindings-0.1.0/testmu/_helpers/wait.py +67 -0
  28. testmuai_playwright_bindings-0.1.0/testmu/_matchers.py +5 -0
  29. testmuai_playwright_bindings-0.1.0/testmu/_reporter/__init__.py +53 -0
  30. testmuai_playwright_bindings-0.1.0/testmu/_reporter/local.py +31 -0
  31. testmuai_playwright_bindings-0.1.0/testmu/_reporter/lt.py +82 -0
  32. testmuai_playwright_bindings-0.1.0/testmu/_reporter/null.py +10 -0
  33. testmuai_playwright_bindings-0.1.0/testmu/_session.py +169 -0
  34. testmuai_playwright_bindings-0.1.0/testmu/_step.py +60 -0
  35. testmuai_playwright_bindings-0.1.0/testmu/_test_config.py +112 -0
  36. testmuai_playwright_bindings-0.1.0/testmu/_vars.py +513 -0
  37. testmuai_playwright_bindings-0.1.0/testmu/playwright_async/__init__.py +67 -0
  38. testmuai_playwright_bindings-0.1.0/testmuai_playwright_bindings.egg-info/PKG-INFO +118 -0
  39. testmuai_playwright_bindings-0.1.0/testmuai_playwright_bindings.egg-info/SOURCES.txt +40 -0
  40. testmuai_playwright_bindings-0.1.0/testmuai_playwright_bindings.egg-info/dependency_links.txt +1 -0
  41. testmuai_playwright_bindings-0.1.0/testmuai_playwright_bindings.egg-info/requires.txt +9 -0
  42. 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,8 @@
1
+ include LICENSE
2
+ include README.md
3
+ include pyproject.toml
4
+ recursive-include testmu *.py
5
+
6
+ # Exclude tests and dev artifacts from sdist
7
+ prune tests
8
+ prune .github
@@ -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
+ [![PyPI version](https://img.shields.io/pypi/v/testmuai-playwright-bindings.svg)](https://pypi.org/project/testmuai-playwright-bindings/)
36
+ [![Python versions](https://img.shields.io/pypi/pyversions/testmuai-playwright-bindings.svg)](https://pypi.org/project/testmuai-playwright-bindings/)
37
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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
+ [![PyPI version](https://img.shields.io/pypi/v/testmuai-playwright-bindings.svg)](https://pypi.org/project/testmuai-playwright-bindings/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/testmuai-playwright-bindings.svg)](https://pypi.org/project/testmuai-playwright-bindings/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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"