Metadata-Version: 2.4
Name: ureport-pytest-reporter
Version: 1.0.1
Summary: Pytest plugin for UReport — ships test results automatically
Author-email: yizhongji@gmail.com
License: MIT
Keywords: pytest,reporter,testing,ureport
Classifier: Framework :: Pytest
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Testing
Requires-Python: >=3.8
Requires-Dist: pytest>=7.0.0
Requires-Dist: requests>=2.28.0
Provides-Extra: dev
Requires-Dist: pytest-rerunfailures>=12.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Requires-Dist: requests-mock>=1.11.0; extra == 'dev'
Description-Content-Type: text/markdown

# ureport-pytest-reporter

A pytest plugin that automatically ships test results to [UReport](https://github.com/ureport) after every test run. Zero code changes required in your tests — configure once and it hooks into pytest's lifecycle automatically.

## Installation

```bash
pip install ureport-pytest-reporter
```

## Quick Start

Add the following to your `pytest.ini` or `pyproject.toml`:

```ini
# pytest.ini
[pytest]
ureport_server_url = http://your-ureport-server:4100
ureport_api_token  = your-api-token
ureport_product    = MyProduct
ureport_type       = E2E
```

```toml
# pyproject.toml
[tool.pytest.ini_options]
ureport_server_url = "http://your-ureport-server:4100"
ureport_api_token  = "your-api-token"
ureport_product    = "MyProduct"
ureport_type       = "E2E"
```

That's it. Run `pytest` as normal — results are submitted to UReport at the end of the session.

---

## Configuration Reference

### Required

| Option | Env Variable | Description |
|--------|-------------|-------------|
| `ureport_server_url` | `UREPORT_SERVER_URL` | UReport server base URL |
| `ureport_api_token` | `UREPORT_API_TOKEN` | API token from UReport → User Settings → API Token |
| `ureport_product` | `UREPORT_PRODUCT` | Product name in UReport (groups tests across builds) |
| `ureport_type` | `UREPORT_TYPE` | Build type: `E2E`, `API`, `UI`, or any custom string |

### Build Metadata (Optional)

| Option | Env Variable | Default | Description |
|--------|-------------|---------|-------------|
| `ureport_build_number` | `UREPORT_BUILD_NUMBER` | current timestamp | CI build / run number. Use your CI's build ID (e.g. `$GITHUB_RUN_NUMBER`) to correlate runs |
| `ureport_team` | `UREPORT_TEAM` | — | Team name for organisational tracking |
| `ureport_browser` | `UREPORT_BROWSER` | — | Browser name (e.g. `CHROME`, `FIREFOX`) |
| `ureport_device` | `UREPORT_DEVICE` | — | Device name (e.g. `DESKTOP-WINDOWS`, `MOBILE-PIXEL 5`) |
| `ureport_platform` | `UREPORT_PLATFORM` | auto-detected | OS platform (`darwin`, `linux`, `win32`) |
| `ureport_platform_version` | `UREPORT_PLATFORM_VERSION` | auto-detected | OS release version |
| `ureport_stage` | `UREPORT_STAGE` | — | Deployment stage (e.g. `staging`, `prod`) |
| `ureport_version` | `UREPORT_VERSION` | — | Application version under test |

### Behaviour (Optional)

| Option | Default | Description |
|--------|---------|-------------|
| `ureport_batch_size` | `50` | Number of tests per POST request to UReport |
| `ureport_include_steps` | `true` | Send step-level detail (steps added via `step()`) |
| `ureport_include_screenshots` | `true` | Embed screenshots as base64 in step payloads |
| `ureport_save_relations` | `true` | Save test metadata (tags, components, teams) for cross-run tracking |
| `ureport_auto_detect_platform` | `true` | Auto-detect `platform` and `platform_version` from the OS |
| `ureport_auto_token` | `true` | Auto-derive a failure token (`path/to/file.py:line`) from the stack trace |
| `ureport_output_file` | — | Write the full submitted payload to this JSON file (useful for debugging) |
| `ureport_quick_info_markers` | — | Space-separated marker names stored as quick-info (execution-specific, never saved to relations) |

### Environment Variables

All required and build metadata options can be set via environment variables. Environment variables take priority over ini options. This is the recommended approach for CI/CD pipelines:

```bash
export UREPORT_SERVER_URL=http://your-ureport-server:4100
export UREPORT_API_TOKEN=your-api-token
export UREPORT_PRODUCT=MyProduct
export UREPORT_TYPE=E2E
export UREPORT_BUILD_NUMBER=$GITHUB_RUN_NUMBER
export UREPORT_STAGE=staging
```

---

## Adding Steps

Use the `step()` context manager to add named steps to your tests. Steps appear in UReport's step comparison view and can be nested.

```python
from ureport_reporter import step

def test_user_checkout():
    with step("Login"):
        page.goto("/login")
        page.fill("#email", "user@example.com")
        page.fill("#password", "secret")
        page.click("button[type=submit]")

    with step("Add item to cart"):
        page.click(".product-card:first-child .add-to-cart")
        with step("Verify cart badge"):          # nested step
            assert page.locator(".cart-count").inner_text() == "1"

    with step("Checkout"):
        page.click(".checkout-btn")
        page.fill("#card-number", "4242424242424242")
        page.click("#pay-now")
        assert page.locator(".order-confirmation").is_visible()
```

Steps are automatically routed to the correct section based on the pytest phase they run in:
- Steps inside **fixtures with `autouse`/setup scope** → `setup` section
- Steps inside the **test body** → `body` section
- Steps inside **teardown fixtures** → `teardown` section

---

## Attaching Files

Use `attach()` to attach screenshots, API responses, or any content to the current step (or directly to the test if no step is active).

```python
from ureport_reporter import step, attach

def test_api_response():
    with step("Call /api/users"):
        response = requests.get("/api/users")
        attach("response_body", body=response.content, content_type="application/json")
        attach("request_curl",  body=b"curl -X GET /api/users", content_type="text/x-curl")
        assert response.status_code == 200

def test_ui_screenshot(page):
    page.goto("/dashboard")
    screenshot = page.screenshot()
    attach("dashboard", body=screenshot, content_type="image/png")
```

**Supported content types:**

| Content-Type | Rendered in UReport as |
|-------------|------------------------|
| `image/png`, `image/jpeg` | Screenshot thumbnail |
| `application/json` | Syntax-highlighted JSON |
| `application/xml`, `text/xml` | Syntax-highlighted XML |
| `text/x-curl` | Curl command |
| `text/plain` | Plain text |

You can also attach from a file path instead of bytes:

```python
attach("report", path="/tmp/report.xml", content_type="application/xml")
```

---

## Markers

Add metadata to tests using pytest markers. This metadata is saved to UReport and used for filtering, grouping, and cross-run tracking.

```python
import pytest

@pytest.mark.ureport_uid("checkout-happy-path")
@pytest.mark.ureport_components("Cart", "Checkout", "Payment")
@pytest.mark.ureport_teams("frontend")
@pytest.mark.ureport_tags("@smoke", "@p1")
def test_checkout():
    ...
```

| Marker | Description |
|--------|-------------|
| `@pytest.mark.ureport_uid("my-uid")` | Override the test's UID in UReport. Useful for stable identifiers across file renames |
| `@pytest.mark.ureport_components("A", "B")` | Component names this test covers |
| `@pytest.mark.ureport_teams("team-name")` | Team responsible for this test |
| `@pytest.mark.ureport_tags("@smoke", "@p1")` | Additional tags (native pytest marks like `@pytest.mark.smoke` are also captured automatically) |

### Tags from Native pytest Marks

Any native pytest mark without arguments is automatically captured as a tag:

```python
@pytest.mark.smoke     # captured as @smoke
@pytest.mark.regression
def test_login():
    ...
```

### Quick Info Markers

Quick info markers store execution-specific values (e.g. environment URL, run ID) that appear as copy-to-clipboard fields in UReport but are **never** saved to test relations.

Configure which markers are treated as quick info:

```ini
# pytest.ini
ureport_quick_info_markers = env build_url run_id
```

Then use them in tests:

```python
@pytest.mark.env("staging")
@pytest.mark.build_url("https://ci.example.com/builds/123")
def test_something():
    ...
```

---

## Using the `ureport` Fixture

Alternatively, use the built-in `ureport` fixture instead of importing `step` and `attach` directly:

```python
def test_something(ureport):
    with ureport.step("Do something"):
        result = do_something()
        ureport.attach("result", body=result.encode(), content_type="application/json")
    assert result == "ok"
```

---

## Retry Support

Works with [pytest-rerunfailures](https://github.com/pytest-dev/pytest-rerunfailures) out of the box. The final attempt's result is what gets submitted to UReport:

- Final attempt **passes** after retries → `RERUN_PASS`
- Final attempt **fails** after retries → `FAIL` with `is_rerun: true`

```ini
# pytest.ini
addopts = --reruns 2 --reruns-delay 1
```

---

## CI/CD Example

### GitHub Actions

```yaml
- name: Run tests
  env:
    UREPORT_SERVER_URL: ${{ secrets.UREPORT_SERVER_URL }}
    UREPORT_API_TOKEN:  ${{ secrets.UREPORT_API_TOKEN }}
    UREPORT_PRODUCT:    MyProduct
    UREPORT_TYPE:       E2E
    UREPORT_BUILD_NUMBER: ${{ github.run_number }}
    UREPORT_STAGE:      ${{ github.ref == 'refs/heads/main' && 'prod' || 'staging' }}
  run: pytest
```

### GitLab CI

```yaml
test:
  variables:
    UREPORT_SERVER_URL: "http://your-ureport-server:4100"
    UREPORT_API_TOKEN: "$UREPORT_TOKEN"
    UREPORT_PRODUCT: "MyProduct"
    UREPORT_TYPE: "E2E"
    UREPORT_BUILD_NUMBER: "$CI_PIPELINE_IID"
  script:
    - pytest
```

---

## Debugging

### Inspect the payload

Use `ureport_output_file` to dump the full payload to a JSON file after the run:

```ini
ureport_output_file = ureport-payload.json
```

### Disable the reporter temporarily

Remove or comment out `ureport_server_url` (or unset `UREPORT_SERVER_URL`). If none of the four required fields are present, the plugin stays inactive and adds zero overhead.

---

## Full Configuration Example

```ini
# pytest.ini
[pytest]
ureport_server_url          = http://localhost:4100
ureport_api_token           = abc123
ureport_product             = MyProduct
ureport_type                = E2E
ureport_build_number        = 42
ureport_team                = platform
ureport_browser             = CHROME
ureport_stage               = staging
ureport_version             = 2.1.0
ureport_batch_size          = 100
ureport_include_steps       = true
ureport_include_screenshots = true
ureport_save_relations      = true
ureport_auto_token          = true
ureport_output_file         = ureport-payload.json
ureport_quick_info_markers  = env build_url
```
