testrelic-appium 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 (53) hide show
  1. testrelic_appium-0.1.0/.gitignore +22 -0
  2. testrelic_appium-0.1.0/CHANGELOG.md +27 -0
  3. testrelic_appium-0.1.0/LICENSE +21 -0
  4. testrelic_appium-0.1.0/PKG-INFO +301 -0
  5. testrelic_appium-0.1.0/README.md +256 -0
  6. testrelic_appium-0.1.0/pyproject.toml +119 -0
  7. testrelic_appium-0.1.0/src/testrelic_appium/__init__.py +97 -0
  8. testrelic_appium-0.1.0/src/testrelic_appium/_html_assets/__init__.py +0 -0
  9. testrelic_appium-0.1.0/src/testrelic_appium/_html_assets/report.css +21 -0
  10. testrelic_appium-0.1.0/src/testrelic_appium/_html_assets/report.js +86 -0
  11. testrelic_appium-0.1.0/src/testrelic_appium/_html_assets/template.html +14 -0
  12. testrelic_appium-0.1.0/src/testrelic_appium/_version.py +1 -0
  13. testrelic_appium-0.1.0/src/testrelic_appium/adb.py +108 -0
  14. testrelic_appium-0.1.0/src/testrelic_appium/artifact_manager.py +71 -0
  15. testrelic_appium-0.1.0/src/testrelic_appium/assertion_tracker.py +172 -0
  16. testrelic_appium-0.1.0/src/testrelic_appium/browser_open.py +22 -0
  17. testrelic_appium-0.1.0/src/testrelic_appium/capability_detector.py +66 -0
  18. testrelic_appium-0.1.0/src/testrelic_appium/ci_detector.py +110 -0
  19. testrelic_appium-0.1.0/src/testrelic_appium/cli.py +105 -0
  20. testrelic_appium-0.1.0/src/testrelic_appium/cloud/__init__.py +75 -0
  21. testrelic_appium-0.1.0/src/testrelic_appium/cloud/artifact.py +373 -0
  22. testrelic_appium-0.1.0/src/testrelic_appium/cloud/auth.py +261 -0
  23. testrelic_appium-0.1.0/src/testrelic_appium/cloud/client.py +253 -0
  24. testrelic_appium-0.1.0/src/testrelic_appium/cloud/queue.py +146 -0
  25. testrelic_appium-0.1.0/src/testrelic_appium/cloud/repo_cache.py +86 -0
  26. testrelic_appium-0.1.0/src/testrelic_appium/cloud/reporter.py +216 -0
  27. testrelic_appium-0.1.0/src/testrelic_appium/cloud/upload.py +346 -0
  28. testrelic_appium-0.1.0/src/testrelic_appium/command_categories.py +183 -0
  29. testrelic_appium-0.1.0/src/testrelic_appium/command_tracker.py +217 -0
  30. testrelic_appium-0.1.0/src/testrelic_appium/config.py +222 -0
  31. testrelic_appium-0.1.0/src/testrelic_appium/console_log_collector.py +147 -0
  32. testrelic_appium-0.1.0/src/testrelic_appium/console_summary.py +45 -0
  33. testrelic_appium-0.1.0/src/testrelic_appium/device_log_collector.py +145 -0
  34. testrelic_appium-0.1.0/src/testrelic_appium/git_metadata.py +105 -0
  35. testrelic_appium-0.1.0/src/testrelic_appium/html_report.py +120 -0
  36. testrelic_appium-0.1.0/src/testrelic_appium/jsonl_stream.py +168 -0
  37. testrelic_appium-0.1.0/src/testrelic_appium/merge.py +70 -0
  38. testrelic_appium-0.1.0/src/testrelic_appium/network_interceptor.py +181 -0
  39. testrelic_appium-0.1.0/src/testrelic_appium/plugin.py +291 -0
  40. testrelic_appium-0.1.0/src/testrelic_appium/proxy_network_interceptor.py +144 -0
  41. testrelic_appium-0.1.0/src/testrelic_appium/redaction.py +132 -0
  42. testrelic_appium-0.1.0/src/testrelic_appium/report_server.py +71 -0
  43. testrelic_appium-0.1.0/src/testrelic_appium/reporter.py +272 -0
  44. testrelic_appium-0.1.0/src/testrelic_appium/run_type_env.py +22 -0
  45. testrelic_appium-0.1.0/src/testrelic_appium/screenshot_collector.py +159 -0
  46. testrelic_appium-0.1.0/src/testrelic_appium/service.py +227 -0
  47. testrelic_appium-0.1.0/src/testrelic_appium/service_registry.py +30 -0
  48. testrelic_appium-0.1.0/src/testrelic_appium/streaming_writer.py +98 -0
  49. testrelic_appium-0.1.0/src/testrelic_appium/summary_builder.py +33 -0
  50. testrelic_appium-0.1.0/src/testrelic_appium/test_data_extractor.py +85 -0
  51. testrelic_appium-0.1.0/src/testrelic_appium/timeline_builder.py +61 -0
  52. testrelic_appium-0.1.0/src/testrelic_appium/types.py +286 -0
  53. testrelic_appium-0.1.0/src/testrelic_appium/worker_coordination.py +142 -0
@@ -0,0 +1,22 @@
1
+ # Build artifacts (also covered by repo-root .gitignore but kept here for clarity)
2
+ dist/
3
+ build/
4
+ *.egg-info/
5
+
6
+ # Local virtualenv used during development
7
+ .venv/
8
+
9
+ # Caches
10
+ __pycache__/
11
+ .pytest_cache/
12
+ .mypy_cache/
13
+ .ruff_cache/
14
+ .coverage
15
+ htmlcov/
16
+
17
+ # Reporter output produced by the package's own pytest runs
18
+ test-results/
19
+
20
+ # Local cloud queue
21
+ .testrelic/queue/
22
+ .testrelic/cache/
@@ -0,0 +1,27 @@
1
+ # Changelog
2
+
3
+ All notable changes to `testrelic-appium` will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+ - Initial release. Python port of [`@testrelic/appium-analytics`](https://www.npmjs.com/package/@testrelic/appium-analytics).
12
+ - `pytest11` plugin that auto-loads on install and captures Appium WebDriver sessions with zero user configuration.
13
+ - Auto-detection and per-instance wrapping of `appium.webdriver.webdriver.WebDriver` instances; opt-out via `--testrelic-no-autowrap`.
14
+ - Command timeline capture with category inference (find / interaction / gesture / navigation / lifecycle / context / device / keyboard / alert / screenshot / other), timing, selector extraction, and sensitive-argument redaction.
15
+ - Device capability snapshot (platform, version, automation name, device name, app path, browser name, UDID).
16
+ - Device log capture: Android `logcat`, iOS `syslog` / `crashlog` (polling via `driver.get_log`).
17
+ - Console log capture: WebView / browser console via BiDi `log.entryAdded` with polling fallback (`browser`, `safariConsole`).
18
+ - Network capture: Chrome DevTools Protocol performance logs (Android), `safariNetwork` (iOS), plus optional `mitmproxy` fallback via `[network-proxy]` extra.
19
+ - Screenshots (off / on / on-failure / on-every-step) and 3-tier video recording (Appium `start_recording_screen` → `mobile: startScreenRecording` → `adb screenrecord`).
20
+ - Assertion tracking via `assert_that(...)` context manager and opportunistic `pytest_assertion_pass` hook.
21
+ - pytest-xdist parallel-worker coordination: single cloud run with per-worker manifest finalization in the master process.
22
+ - Cloud upload via the shared cloud client (batch / realtime / both / none), with offline queue at `.testrelic/queue/`, 7-day expiry sweep, and OAuth token refresh.
23
+ - Presigned-URL artifact upload (screenshots, videos, device logs as gzipped JSONL).
24
+ - Self-contained interactive HTML report with embedded JSON payload.
25
+ - `testrelic-appium` CLI: `version`, `merge`, `serve`, `drain`, `cleanup-queue`.
26
+ - CI metadata auto-detection (GitHub Actions, GitLab CI, Jenkins, CircleCI, Bitbucket Pipelines).
27
+ - Sensitive-data redaction shared with `testrelic-playwright` plus Appium-specific command-argument redaction (`elementSendKeys`, `setValueImmediate`, `replaceValue`).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TestRelic 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 all
13
+ 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 THE
21
+ SOFTWARE.
@@ -0,0 +1,301 @@
1
+ Metadata-Version: 2.4
2
+ Name: testrelic-appium
3
+ Version: 0.1.0
4
+ Summary: Appium/pytest test analytics reporter — command timeline, device log capture (logcat/syslog), network interception, screenshot/video artifacts, and interactive HTML reports for Android and iOS.
5
+ Project-URL: Homepage, https://testrelic.ai
6
+ Project-URL: Documentation, https://docs.testrelic.ai/appium
7
+ Project-URL: Repository, https://github.com/testrelic-ai/testrelic-python-sdk
8
+ Project-URL: Bug Tracker, https://github.com/testrelic-ai/testrelic-python-sdk/issues
9
+ Author-email: TestRelic AI <hello@testrelic.ai>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: analytics,android,appium,ci,device-testing,e2e-testing,html-report,ios,mobile-testing,network,pytest,reporter,test,test-automation,test-results,testing
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Framework :: Pytest
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Topic :: Software Development :: Quality Assurance
24
+ Classifier: Topic :: Software Development :: Testing
25
+ Requires-Python: >=3.9
26
+ Requires-Dist: appium-python-client<6.0,>=4.0
27
+ Requires-Dist: httpx<1.0,>=0.27
28
+ Requires-Dist: platformdirs>=4.0
29
+ Requires-Dist: pydantic<3.0,>=2.6
30
+ Requires-Dist: pytest>=7.0
31
+ Requires-Dist: selenium<5.0,>=4.0
32
+ Requires-Dist: tomli-w>=1.0
33
+ Requires-Dist: tomli>=2.0; python_version < '3.11'
34
+ Requires-Dist: typer<1.0,>=0.12
35
+ Provides-Extra: dev
36
+ Requires-Dist: build>=1.2; extra == 'dev'
37
+ Requires-Dist: mypy>=1.10; extra == 'dev'
38
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
39
+ Requires-Dist: respx>=0.21; extra == 'dev'
40
+ Requires-Dist: ruff>=0.6; extra == 'dev'
41
+ Requires-Dist: twine>=5.1; extra == 'dev'
42
+ Provides-Extra: network-proxy
43
+ Requires-Dist: mitmproxy>=10.0; extra == 'network-proxy'
44
+ Description-Content-Type: text/markdown
45
+
46
+ # testrelic-appium
47
+
48
+ Appium test analytics reporter for **pytest** — captures command timelines,
49
+ device logs (Android `logcat` / iOS `syslog`), console logs, network requests,
50
+ screenshots, and video, then generates a self-contained interactive HTML
51
+ report. Python port of
52
+ [`@testrelic/appium-analytics`](https://www.npmjs.com/package/@testrelic/appium-analytics).
53
+
54
+ ## What it does
55
+
56
+ When you run your Appium tests with `pytest` and the `testrelic-appium` plugin
57
+ installed, you get JSON + HTML reports that capture:
58
+
59
+ - **Command timeline** — every Appium / WebDriver command with timing,
60
+ category (`find`, `interaction`, `gesture`, `navigation`, `lifecycle`,
61
+ `context`, `device`, `keyboard`, `alert`, `screenshot`, `other`), selector,
62
+ arguments (sensitive ones redacted), and result.
63
+ - **Device logs** — Android `logcat`, iOS `syslog` and `crashlog`, polled in
64
+ the background and sliced per test.
65
+ - **Console logs** — WebView / browser console output via WebDriver BiDi
66
+ (`log.entryAdded`) when supported, with polling fallback (`browser`,
67
+ `safariConsole`).
68
+ - **Network capture** — Chrome DevTools Protocol performance logs (Android),
69
+ `safariNetwork` (iOS), with optional `mitmproxy` fallback for emulators
70
+ without CDP.
71
+ - **Screenshots & video** — configurable: off, on every test, on failure, or
72
+ on every step. Video uses Appium recording commands first, falling back to
73
+ `adb screenrecord`.
74
+ - **Assertions** — `assert_that(...)` context manager records named assertion
75
+ steps; bare `assert` statements are picked up by `pytest_assertion_pass`.
76
+ - **CI metadata** — auto-detect GitHub Actions, GitLab CI, Jenkins, CircleCI,
77
+ Bitbucket Pipelines.
78
+ - **Sensitive-data redaction** — AWS access keys, Bearer tokens, PEM private
79
+ keys, embedded URL credentials, plus Appium-specific command-argument
80
+ redaction (`elementSendKeys`, `setValueImmediate`, `replaceValue`).
81
+ - **HTML report** — self-contained, opens in any browser.
82
+
83
+ ## System requirements
84
+
85
+ The package itself has no platform requirements beyond Python, but to test
86
+ mobile apps you'll need the standard Appium tooling on your machine. None of
87
+ these are pip dependencies — they are subprocess dependencies the SDK invokes
88
+ when needed.
89
+
90
+ | Tool | Required for |
91
+ |---|---|
92
+ | Appium server (`npm install -g appium`) | All tests |
93
+ | `appium driver install uiautomator2` | Android |
94
+ | `appium driver install xcuitest` | iOS (macOS only) |
95
+ | `adb` on `PATH` | Android device-log capture, video recording fallback, proxy network capture |
96
+ | `xcrun simctl` | iOS simulators |
97
+
98
+ > **iOS testing requires macOS 13+ with Xcode 15+.** iOS tests cannot run on Windows or Linux.
99
+
100
+ ## Quick start
101
+
102
+ ### 1. Install
103
+
104
+ ```bash
105
+ pip install testrelic-appium
106
+ ```
107
+
108
+ The plugin activates automatically because `testrelic-appium` registers a
109
+ `pytest11` entry point. No `conftest.py` plumbing required.
110
+
111
+ For the optional HTTPS network-capture proxy:
112
+
113
+ ```bash
114
+ pip install "testrelic-appium[network-proxy]"
115
+ ```
116
+
117
+ ### 2. Write a test
118
+
119
+ Use any Appium driver instantiation pattern. The plugin walks
120
+ `item.funcargs` after each fixture resolves and per-instance wraps any
121
+ `appium.webdriver.webdriver.WebDriver` it finds — Selenium-only tests are
122
+ never touched.
123
+
124
+ ```python
125
+ # tests/test_login.py
126
+ import pytest
127
+ from appium import webdriver
128
+ from appium.options.android import UiAutomator2Options
129
+
130
+
131
+ @pytest.fixture
132
+ def driver():
133
+ options = UiAutomator2Options()
134
+ options.platform_name = "Android"
135
+ options.device_name = "emulator-5554"
136
+ options.app = "/path/to/MyApp.apk"
137
+ d = webdriver.Remote("http://127.0.0.1:4723", options=options)
138
+ yield d
139
+ d.quit()
140
+
141
+
142
+ def test_login_succeeds(driver):
143
+ driver.find_element("accessibility id", "username").send_keys("alice")
144
+ driver.find_element("accessibility id", "password").send_keys("hunter2")
145
+ driver.find_element("accessibility id", "submit").click()
146
+ assert driver.find_element("accessibility id", "welcome").is_displayed()
147
+ ```
148
+
149
+ ### 3. Run
150
+
151
+ ```bash
152
+ pytest
153
+ ```
154
+
155
+ Outputs land in:
156
+
157
+ ```
158
+ test-results/
159
+ ├── testrelic-timeline.json
160
+ └── testrelic-timeline.html
161
+ ```
162
+
163
+ ## Configuration
164
+
165
+ Reporter options can be supplied three ways (highest priority first):
166
+
167
+ 1. **Environment variables** (`TESTRELIC_API_KEY`, `TESTRELIC_CLOUD_ENDPOINT`,
168
+ `TESTRELIC_UPLOAD_STRATEGY`, `TESTRELIC_CLOUD_TIMEOUT`,
169
+ `TESTRELIC_RUN_TYPE`).
170
+ 2. **`pytest.ini` / `pyproject.toml`** under `testrelic_options`.
171
+ 3. **`.testrelic/testrelic-config.json`** in the project root.
172
+
173
+ ### `pyproject.toml`
174
+
175
+ ```toml
176
+ [tool.pytest.ini_options]
177
+ testrelic_options = [
178
+ "outputPath=./test-results/testrelic-timeline.json",
179
+ "includeDeviceLogs=true",
180
+ "includeNetworkLogs=true",
181
+ "screenshotOnEvery=failure",
182
+ "includeVideoRecording=false",
183
+ ]
184
+ ```
185
+
186
+ ### CLI flags
187
+
188
+ ```bash
189
+ pytest --testrelic-output ./test-results/custom.json
190
+ pytest --testrelic-disable # turn the reporter off for this run
191
+ pytest --testrelic-quiet # suppress banner output
192
+ pytest --testrelic-no-autowrap # disable WebDriver auto-detection
193
+ ```
194
+
195
+ ### Full option list
196
+
197
+ | Option | Type | Default | Description |
198
+ |---|---|---|---|
199
+ | `outputPath` | str | `./test-results/testrelic-timeline.json` | JSON report path |
200
+ | `htmlReportPath` | str | derived from `outputPath` | HTML report path |
201
+ | `openReport` | bool | `false` | Open the HTML report in the default browser after the run |
202
+ | `includeDeviceLogs` | bool | `true` | Capture Android `logcat` / iOS `syslog` and `crashlog` |
203
+ | `includeNetworkLogs` | bool | `true` | Capture HTTP network requests and responses |
204
+ | `includeConsoleLogs` | bool | `true` | Capture webview / browser console output |
205
+ | `includeScreenshots` | bool | `true` | Capture device screenshots |
206
+ | `screenshotOnEvery` | `off` \| `on` \| `on-failure` \| `on-every-step` | `on-failure` | When to take screenshots |
207
+ | `includeVideoRecording` | bool | `false` | Record the device screen for each test |
208
+ | `includeCommands` | bool | `true` | Record every Appium / WebDriver command |
209
+ | `includeAssertions` | bool | `true` | Record assertion steps |
210
+ | `deviceLogPollInterval` | int (ms) | `1000` | Interval between device log polls (min `100`) |
211
+ | `maxDeviceLogMb` | int | `20` | Truncate per-test device log buffer above this size |
212
+ | `preferBiDi` | bool | `true` | Try BiDi `log.entryAdded` before polling |
213
+ | `redactPatterns` | list[str] | `[]` | Extra regex patterns to redact (built-ins always apply) |
214
+ | `quiet` | bool | `false` | Suppress the console summary table at session end |
215
+ | `metadata` | dict | `null` | Arbitrary key-value pairs attached to the run report |
216
+ | `cloud` | object | — | Cloud upload configuration (see below) |
217
+
218
+ ### Cloud integration
219
+
220
+ Set `TESTRELIC_API_KEY` and the reporter uploads the run to
221
+ `https://platform.testrelic.ai/api/v1/runs` at session end.
222
+
223
+ ```bash
224
+ export TESTRELIC_API_KEY=tk_live_...
225
+ export TESTRELIC_UPLOAD_STRATEGY=both # realtime | batch | both | none
226
+ ```
227
+
228
+ Or use a project file:
229
+
230
+ ```json
231
+ // .testrelic/testrelic-config.json
232
+ {
233
+ "cloud": {
234
+ "apiKey": "$TESTRELIC_API_KEY",
235
+ "endpoint": "https://platform.testrelic.ai/api/v1",
236
+ "upload": "both",
237
+ "projectName": "my-mobile-app"
238
+ }
239
+ }
240
+ ```
241
+
242
+ Network failures are queued to `.testrelic/queue/` and retried with:
243
+
244
+ ```bash
245
+ testrelic-appium drain
246
+ testrelic-appium cleanup-queue --max-age-days 7
247
+ ```
248
+
249
+ ### Parallel runs (pytest-xdist)
250
+
251
+ `pytest -n 2` runs workers as separate subprocesses. The master process
252
+ creates the cloud run during `pytest_configure`, writes a manifest, and
253
+ finalizes the run after all workers exit. Each worker uploads its per-test
254
+ realtime data under the master's run ID — exactly one run shows up on the
255
+ dashboard.
256
+
257
+ ## CLI
258
+
259
+ ```bash
260
+ testrelic-appium version
261
+ testrelic-appium merge shard-1.json shard-2.json -o merged.json
262
+ testrelic-appium serve ./test-results --port 9323
263
+ testrelic-appium drain
264
+ testrelic-appium cleanup-queue --max-age-days 7
265
+ ```
266
+
267
+ ## Manual driver wrap
268
+
269
+ If auto-detection misses your driver (custom subclass, fixture indirection,
270
+ non-pytest harness), use the explicit fixture or wrap function:
271
+
272
+ ```python
273
+ import pytest
274
+ from testrelic_appium import testrelic_appium_driver # fixture
275
+
276
+ def test_with_explicit_fixture(testrelic_appium_driver):
277
+ driver = testrelic_appium_driver # already wrapped
278
+ driver.find_element("accessibility id", "btn").click()
279
+ ```
280
+
281
+ ```python
282
+ from testrelic_appium import wrap_driver
283
+
284
+ def test_with_manual_wrap(driver):
285
+ unwrap = wrap_driver(driver)
286
+ try:
287
+ driver.find_element("accessibility id", "btn").click()
288
+ finally:
289
+ unwrap()
290
+ ```
291
+
292
+ ## Compatibility
293
+
294
+ - Python 3.9 – 3.12
295
+ - pytest 7.0+
296
+ - Appium-Python-Client 4.x – 5.x
297
+ - Appium 2.x / 3.x
298
+
299
+ ## License
300
+
301
+ MIT — see [LICENSE](https://github.com/testrelic-ai/testrelic-python-sdk/blob/main/packages/testrelic-appium/LICENSE).
@@ -0,0 +1,256 @@
1
+ # testrelic-appium
2
+
3
+ Appium test analytics reporter for **pytest** — captures command timelines,
4
+ device logs (Android `logcat` / iOS `syslog`), console logs, network requests,
5
+ screenshots, and video, then generates a self-contained interactive HTML
6
+ report. Python port of
7
+ [`@testrelic/appium-analytics`](https://www.npmjs.com/package/@testrelic/appium-analytics).
8
+
9
+ ## What it does
10
+
11
+ When you run your Appium tests with `pytest` and the `testrelic-appium` plugin
12
+ installed, you get JSON + HTML reports that capture:
13
+
14
+ - **Command timeline** — every Appium / WebDriver command with timing,
15
+ category (`find`, `interaction`, `gesture`, `navigation`, `lifecycle`,
16
+ `context`, `device`, `keyboard`, `alert`, `screenshot`, `other`), selector,
17
+ arguments (sensitive ones redacted), and result.
18
+ - **Device logs** — Android `logcat`, iOS `syslog` and `crashlog`, polled in
19
+ the background and sliced per test.
20
+ - **Console logs** — WebView / browser console output via WebDriver BiDi
21
+ (`log.entryAdded`) when supported, with polling fallback (`browser`,
22
+ `safariConsole`).
23
+ - **Network capture** — Chrome DevTools Protocol performance logs (Android),
24
+ `safariNetwork` (iOS), with optional `mitmproxy` fallback for emulators
25
+ without CDP.
26
+ - **Screenshots & video** — configurable: off, on every test, on failure, or
27
+ on every step. Video uses Appium recording commands first, falling back to
28
+ `adb screenrecord`.
29
+ - **Assertions** — `assert_that(...)` context manager records named assertion
30
+ steps; bare `assert` statements are picked up by `pytest_assertion_pass`.
31
+ - **CI metadata** — auto-detect GitHub Actions, GitLab CI, Jenkins, CircleCI,
32
+ Bitbucket Pipelines.
33
+ - **Sensitive-data redaction** — AWS access keys, Bearer tokens, PEM private
34
+ keys, embedded URL credentials, plus Appium-specific command-argument
35
+ redaction (`elementSendKeys`, `setValueImmediate`, `replaceValue`).
36
+ - **HTML report** — self-contained, opens in any browser.
37
+
38
+ ## System requirements
39
+
40
+ The package itself has no platform requirements beyond Python, but to test
41
+ mobile apps you'll need the standard Appium tooling on your machine. None of
42
+ these are pip dependencies — they are subprocess dependencies the SDK invokes
43
+ when needed.
44
+
45
+ | Tool | Required for |
46
+ |---|---|
47
+ | Appium server (`npm install -g appium`) | All tests |
48
+ | `appium driver install uiautomator2` | Android |
49
+ | `appium driver install xcuitest` | iOS (macOS only) |
50
+ | `adb` on `PATH` | Android device-log capture, video recording fallback, proxy network capture |
51
+ | `xcrun simctl` | iOS simulators |
52
+
53
+ > **iOS testing requires macOS 13+ with Xcode 15+.** iOS tests cannot run on Windows or Linux.
54
+
55
+ ## Quick start
56
+
57
+ ### 1. Install
58
+
59
+ ```bash
60
+ pip install testrelic-appium
61
+ ```
62
+
63
+ The plugin activates automatically because `testrelic-appium` registers a
64
+ `pytest11` entry point. No `conftest.py` plumbing required.
65
+
66
+ For the optional HTTPS network-capture proxy:
67
+
68
+ ```bash
69
+ pip install "testrelic-appium[network-proxy]"
70
+ ```
71
+
72
+ ### 2. Write a test
73
+
74
+ Use any Appium driver instantiation pattern. The plugin walks
75
+ `item.funcargs` after each fixture resolves and per-instance wraps any
76
+ `appium.webdriver.webdriver.WebDriver` it finds — Selenium-only tests are
77
+ never touched.
78
+
79
+ ```python
80
+ # tests/test_login.py
81
+ import pytest
82
+ from appium import webdriver
83
+ from appium.options.android import UiAutomator2Options
84
+
85
+
86
+ @pytest.fixture
87
+ def driver():
88
+ options = UiAutomator2Options()
89
+ options.platform_name = "Android"
90
+ options.device_name = "emulator-5554"
91
+ options.app = "/path/to/MyApp.apk"
92
+ d = webdriver.Remote("http://127.0.0.1:4723", options=options)
93
+ yield d
94
+ d.quit()
95
+
96
+
97
+ def test_login_succeeds(driver):
98
+ driver.find_element("accessibility id", "username").send_keys("alice")
99
+ driver.find_element("accessibility id", "password").send_keys("hunter2")
100
+ driver.find_element("accessibility id", "submit").click()
101
+ assert driver.find_element("accessibility id", "welcome").is_displayed()
102
+ ```
103
+
104
+ ### 3. Run
105
+
106
+ ```bash
107
+ pytest
108
+ ```
109
+
110
+ Outputs land in:
111
+
112
+ ```
113
+ test-results/
114
+ ├── testrelic-timeline.json
115
+ └── testrelic-timeline.html
116
+ ```
117
+
118
+ ## Configuration
119
+
120
+ Reporter options can be supplied three ways (highest priority first):
121
+
122
+ 1. **Environment variables** (`TESTRELIC_API_KEY`, `TESTRELIC_CLOUD_ENDPOINT`,
123
+ `TESTRELIC_UPLOAD_STRATEGY`, `TESTRELIC_CLOUD_TIMEOUT`,
124
+ `TESTRELIC_RUN_TYPE`).
125
+ 2. **`pytest.ini` / `pyproject.toml`** under `testrelic_options`.
126
+ 3. **`.testrelic/testrelic-config.json`** in the project root.
127
+
128
+ ### `pyproject.toml`
129
+
130
+ ```toml
131
+ [tool.pytest.ini_options]
132
+ testrelic_options = [
133
+ "outputPath=./test-results/testrelic-timeline.json",
134
+ "includeDeviceLogs=true",
135
+ "includeNetworkLogs=true",
136
+ "screenshotOnEvery=failure",
137
+ "includeVideoRecording=false",
138
+ ]
139
+ ```
140
+
141
+ ### CLI flags
142
+
143
+ ```bash
144
+ pytest --testrelic-output ./test-results/custom.json
145
+ pytest --testrelic-disable # turn the reporter off for this run
146
+ pytest --testrelic-quiet # suppress banner output
147
+ pytest --testrelic-no-autowrap # disable WebDriver auto-detection
148
+ ```
149
+
150
+ ### Full option list
151
+
152
+ | Option | Type | Default | Description |
153
+ |---|---|---|---|
154
+ | `outputPath` | str | `./test-results/testrelic-timeline.json` | JSON report path |
155
+ | `htmlReportPath` | str | derived from `outputPath` | HTML report path |
156
+ | `openReport` | bool | `false` | Open the HTML report in the default browser after the run |
157
+ | `includeDeviceLogs` | bool | `true` | Capture Android `logcat` / iOS `syslog` and `crashlog` |
158
+ | `includeNetworkLogs` | bool | `true` | Capture HTTP network requests and responses |
159
+ | `includeConsoleLogs` | bool | `true` | Capture webview / browser console output |
160
+ | `includeScreenshots` | bool | `true` | Capture device screenshots |
161
+ | `screenshotOnEvery` | `off` \| `on` \| `on-failure` \| `on-every-step` | `on-failure` | When to take screenshots |
162
+ | `includeVideoRecording` | bool | `false` | Record the device screen for each test |
163
+ | `includeCommands` | bool | `true` | Record every Appium / WebDriver command |
164
+ | `includeAssertions` | bool | `true` | Record assertion steps |
165
+ | `deviceLogPollInterval` | int (ms) | `1000` | Interval between device log polls (min `100`) |
166
+ | `maxDeviceLogMb` | int | `20` | Truncate per-test device log buffer above this size |
167
+ | `preferBiDi` | bool | `true` | Try BiDi `log.entryAdded` before polling |
168
+ | `redactPatterns` | list[str] | `[]` | Extra regex patterns to redact (built-ins always apply) |
169
+ | `quiet` | bool | `false` | Suppress the console summary table at session end |
170
+ | `metadata` | dict | `null` | Arbitrary key-value pairs attached to the run report |
171
+ | `cloud` | object | — | Cloud upload configuration (see below) |
172
+
173
+ ### Cloud integration
174
+
175
+ Set `TESTRELIC_API_KEY` and the reporter uploads the run to
176
+ `https://platform.testrelic.ai/api/v1/runs` at session end.
177
+
178
+ ```bash
179
+ export TESTRELIC_API_KEY=tk_live_...
180
+ export TESTRELIC_UPLOAD_STRATEGY=both # realtime | batch | both | none
181
+ ```
182
+
183
+ Or use a project file:
184
+
185
+ ```json
186
+ // .testrelic/testrelic-config.json
187
+ {
188
+ "cloud": {
189
+ "apiKey": "$TESTRELIC_API_KEY",
190
+ "endpoint": "https://platform.testrelic.ai/api/v1",
191
+ "upload": "both",
192
+ "projectName": "my-mobile-app"
193
+ }
194
+ }
195
+ ```
196
+
197
+ Network failures are queued to `.testrelic/queue/` and retried with:
198
+
199
+ ```bash
200
+ testrelic-appium drain
201
+ testrelic-appium cleanup-queue --max-age-days 7
202
+ ```
203
+
204
+ ### Parallel runs (pytest-xdist)
205
+
206
+ `pytest -n 2` runs workers as separate subprocesses. The master process
207
+ creates the cloud run during `pytest_configure`, writes a manifest, and
208
+ finalizes the run after all workers exit. Each worker uploads its per-test
209
+ realtime data under the master's run ID — exactly one run shows up on the
210
+ dashboard.
211
+
212
+ ## CLI
213
+
214
+ ```bash
215
+ testrelic-appium version
216
+ testrelic-appium merge shard-1.json shard-2.json -o merged.json
217
+ testrelic-appium serve ./test-results --port 9323
218
+ testrelic-appium drain
219
+ testrelic-appium cleanup-queue --max-age-days 7
220
+ ```
221
+
222
+ ## Manual driver wrap
223
+
224
+ If auto-detection misses your driver (custom subclass, fixture indirection,
225
+ non-pytest harness), use the explicit fixture or wrap function:
226
+
227
+ ```python
228
+ import pytest
229
+ from testrelic_appium import testrelic_appium_driver # fixture
230
+
231
+ def test_with_explicit_fixture(testrelic_appium_driver):
232
+ driver = testrelic_appium_driver # already wrapped
233
+ driver.find_element("accessibility id", "btn").click()
234
+ ```
235
+
236
+ ```python
237
+ from testrelic_appium import wrap_driver
238
+
239
+ def test_with_manual_wrap(driver):
240
+ unwrap = wrap_driver(driver)
241
+ try:
242
+ driver.find_element("accessibility id", "btn").click()
243
+ finally:
244
+ unwrap()
245
+ ```
246
+
247
+ ## Compatibility
248
+
249
+ - Python 3.9 – 3.12
250
+ - pytest 7.0+
251
+ - Appium-Python-Client 4.x – 5.x
252
+ - Appium 2.x / 3.x
253
+
254
+ ## License
255
+
256
+ MIT — see [LICENSE](https://github.com/testrelic-ai/testrelic-python-sdk/blob/main/packages/testrelic-appium/LICENSE).