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.
- testrelic_appium-0.1.0/.gitignore +22 -0
- testrelic_appium-0.1.0/CHANGELOG.md +27 -0
- testrelic_appium-0.1.0/LICENSE +21 -0
- testrelic_appium-0.1.0/PKG-INFO +301 -0
- testrelic_appium-0.1.0/README.md +256 -0
- testrelic_appium-0.1.0/pyproject.toml +119 -0
- testrelic_appium-0.1.0/src/testrelic_appium/__init__.py +97 -0
- testrelic_appium-0.1.0/src/testrelic_appium/_html_assets/__init__.py +0 -0
- testrelic_appium-0.1.0/src/testrelic_appium/_html_assets/report.css +21 -0
- testrelic_appium-0.1.0/src/testrelic_appium/_html_assets/report.js +86 -0
- testrelic_appium-0.1.0/src/testrelic_appium/_html_assets/template.html +14 -0
- testrelic_appium-0.1.0/src/testrelic_appium/_version.py +1 -0
- testrelic_appium-0.1.0/src/testrelic_appium/adb.py +108 -0
- testrelic_appium-0.1.0/src/testrelic_appium/artifact_manager.py +71 -0
- testrelic_appium-0.1.0/src/testrelic_appium/assertion_tracker.py +172 -0
- testrelic_appium-0.1.0/src/testrelic_appium/browser_open.py +22 -0
- testrelic_appium-0.1.0/src/testrelic_appium/capability_detector.py +66 -0
- testrelic_appium-0.1.0/src/testrelic_appium/ci_detector.py +110 -0
- testrelic_appium-0.1.0/src/testrelic_appium/cli.py +105 -0
- testrelic_appium-0.1.0/src/testrelic_appium/cloud/__init__.py +75 -0
- testrelic_appium-0.1.0/src/testrelic_appium/cloud/artifact.py +373 -0
- testrelic_appium-0.1.0/src/testrelic_appium/cloud/auth.py +261 -0
- testrelic_appium-0.1.0/src/testrelic_appium/cloud/client.py +253 -0
- testrelic_appium-0.1.0/src/testrelic_appium/cloud/queue.py +146 -0
- testrelic_appium-0.1.0/src/testrelic_appium/cloud/repo_cache.py +86 -0
- testrelic_appium-0.1.0/src/testrelic_appium/cloud/reporter.py +216 -0
- testrelic_appium-0.1.0/src/testrelic_appium/cloud/upload.py +346 -0
- testrelic_appium-0.1.0/src/testrelic_appium/command_categories.py +183 -0
- testrelic_appium-0.1.0/src/testrelic_appium/command_tracker.py +217 -0
- testrelic_appium-0.1.0/src/testrelic_appium/config.py +222 -0
- testrelic_appium-0.1.0/src/testrelic_appium/console_log_collector.py +147 -0
- testrelic_appium-0.1.0/src/testrelic_appium/console_summary.py +45 -0
- testrelic_appium-0.1.0/src/testrelic_appium/device_log_collector.py +145 -0
- testrelic_appium-0.1.0/src/testrelic_appium/git_metadata.py +105 -0
- testrelic_appium-0.1.0/src/testrelic_appium/html_report.py +120 -0
- testrelic_appium-0.1.0/src/testrelic_appium/jsonl_stream.py +168 -0
- testrelic_appium-0.1.0/src/testrelic_appium/merge.py +70 -0
- testrelic_appium-0.1.0/src/testrelic_appium/network_interceptor.py +181 -0
- testrelic_appium-0.1.0/src/testrelic_appium/plugin.py +291 -0
- testrelic_appium-0.1.0/src/testrelic_appium/proxy_network_interceptor.py +144 -0
- testrelic_appium-0.1.0/src/testrelic_appium/redaction.py +132 -0
- testrelic_appium-0.1.0/src/testrelic_appium/report_server.py +71 -0
- testrelic_appium-0.1.0/src/testrelic_appium/reporter.py +272 -0
- testrelic_appium-0.1.0/src/testrelic_appium/run_type_env.py +22 -0
- testrelic_appium-0.1.0/src/testrelic_appium/screenshot_collector.py +159 -0
- testrelic_appium-0.1.0/src/testrelic_appium/service.py +227 -0
- testrelic_appium-0.1.0/src/testrelic_appium/service_registry.py +30 -0
- testrelic_appium-0.1.0/src/testrelic_appium/streaming_writer.py +98 -0
- testrelic_appium-0.1.0/src/testrelic_appium/summary_builder.py +33 -0
- testrelic_appium-0.1.0/src/testrelic_appium/test_data_extractor.py +85 -0
- testrelic_appium-0.1.0/src/testrelic_appium/timeline_builder.py +61 -0
- testrelic_appium-0.1.0/src/testrelic_appium/types.py +286 -0
- 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).
|