Metadata-Version: 2.4
Name: visualcheck
Version: 0.2.2
Summary: Visual regression checking with figma-first baselines, pixel+SSIM diffing, ignore regions, and HTML reports.
Author-email: Arif Shah <ashah7775@gmail.com>
License: MIT
Project-URL: Homepage, https://bitbucket.org/arif_automation/visualcheck
Project-URL: Source, https://bitbucket.org/arif_automation/visualcheck
Keywords: visual-regression,playwright,ssim,qa,figma
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: playwright>=1.40
Requires-Dist: pillow>=10
Requires-Dist: numpy>=1.23
Requires-Dist: opencv-python-headless>=4.8
Requires-Dist: scikit-image>=0.20
Requires-Dist: PyYAML>=6
Requires-Dist: Jinja2>=3
Requires-Dist: typer>=0.9
Requires-Dist: rich>=13
Requires-Dist: jsonschema>=4
Requires-Dist: requests>=2.31

# visualcheck

Visual regression checking with:

- **Figma-first baselines** (optionally synced via Figma API)
- **Runtime baselines** (auto-create when missing; never overwrite by default)
- **Pixel diff % + SSIM** (with optional resize-on-mismatch + WARN)
- **Ignore regions** (mask dynamic areas via selectors + explicit rects)
- **Self-contained HTML report** + `report.json`

## Install

```bash
pip install visualcheck
playwright install chromium
```

## Config (`visualcheck.yaml`)

```yaml
project: consumer_website
suite: daily_sanity

envs:
  prod: "https://example.com"

views:
  desktop_profiles: ["desktop_1440x900", "macbook_1440x900"]
  mobile_devices: ["iPhone 15 Pro Max", "iPhone 13 mini", "Pixel 7"]

pages:
  - id: home
    url: "/"
    wait_for: "body"
    full_page: true

# Optional flows (multi-step user journeys)
flows:
  - id: search_flow
    start_url: "/"
    steps:
      - action: click
        selector: "text=Search"
      - action: wait
        ms: 500
    snapshots:
      - id: after_search
        wait_for: "body"
        full_page: true

baseline:
  # Locked resolution order:
  # figma -> runtime -> create runtime baseline (if enabled)
  priority: ["figma", "runtime"]
  create_if_missing: true
  never_overwrite: true
  on_created: "INFO"  # INFO|WARN|FAIL

compare:
  resize_on_mismatch: true
  mismatch_level: "WARN"  # WARN|FAIL
  thresholds:
    max_pixel_diff_pct: 0.10
    min_ssim: 0.995

ignore_regions:
  global:
    selectors: ["#cookie-banner", ".chat-widget"]
  by_snapshot:
    home:
      rects:
        - {x: 0, y: 0, width: 300, height: 120}

# Optional Figma sync (writes into visual_baseline/<project>/<suite>/figma/...)
# figma:
#   token_env: FIGMA_TOKEN
#   file_key: "<FIGMA_FILE_KEY>"
#   frames:
#     - id: home
#       node_id: "123:456"
#       view_id: "desktop_1440x900"   # optional
```

## Commands

### Run full check

```bash
visualcheck run --env prod
```

Outputs:

- Baselines:
  - `visual_baseline/<project>/<suite>/figma/<view>/<snapshot>.png`
  - `visual_baseline/<project>/<suite>/runtime/<view>/<snapshot>.png`
- Run artifacts:
  - `test_report/<project>/visual_runs/<run_id>/current/...`
  - `test_report/<project>/visual_runs/<run_id>/diff/...`
  - `test_report/<project>/visual_runs/<run_id>/report/report.html`
  - `test_report/<project>/visual_runs/<run_id>/report/report.json`

### Capture only

```bash
visualcheck capture --env prod --out current
```

### Diff only

```bash
visualcheck diff --baseline visual_baseline/myproj/mysuite/runtime --current current --out report
```

### Sync Figma baselines

```bash
export FIGMA_TOKEN="..."
visualcheck figma-sync
```

### Approve current run as runtime baseline (explicit)

```bash
visualcheck approve --env prod --run-id 20260207_235500
# Overwrite existing runtime baselines only with:
visualcheck approve --env prod --run-id 20260207_235500 --force
```

## Baseline rules (locked)

For each snapshot + view:

1) If a **Figma baseline** exists → use it
2) Else if a **runtime baseline** exists → use it
3) Else → capture and **create runtime baseline** (controlled by `baseline.create_if_missing`)

## Use from code (framework integration)

If you already navigate with **Playwright**/**Selenium** in your own framework and just want visualcheck to **capture + baseline + diff + report**, use the code API.

> Baselines still go to `visual_baseline/<project>/<suite>/...` and the HTML report goes to `test_report/<project>/visual_runs/<run_id>/report/report.html`.

### Playwright (sync) example

```python
from playwright.sync_api import sync_playwright

# NOTE: API object names may evolve; refer to the package docs in case of changes.
from visualcheck.api import VisualCheck

vc = VisualCheck(
    project="my_project",
    suite="daily_sanity",
    env="prod",
    base_url="https://example.com",
    view_id="desktop_1440x900",
    baseline_priority=["figma", "runtime"],
    create_if_missing=True,
    ignore_selectors=["#cookie-banner", ".chat-widget"],
)

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page(viewport={"width": 1440, "height": 900})

    page.goto("https://example.com/", wait_until="domcontentloaded")
    vc.check(snapshot_id="home", page=page)

    page.goto("https://example.com/pricing", wait_until="domcontentloaded")
    vc.check(snapshot_id="pricing", page=page)

    vc.finalize()
    print("Report:", vc.report_html)

    browser.close()
```

### Selenium example

```python
from selenium import webdriver
from visualcheck.api import VisualCheck

vc = VisualCheck(
    project="my_project",
    suite="daily_sanity",
    env="prod",
    base_url="https://example.com",
    view_id="desktop_1440x900",
)

driver = webdriver.Chrome()
driver.set_window_size(1440, 900)

driver.get("https://example.com/")
vc.check(snapshot_id="home", selenium_driver=driver)

driver.get("https://example.com/pricing")
vc.check(snapshot_id="pricing", selenium_driver=driver)

vc.finalize()
print("Report:", vc.report_html)

driver.quit()
```

## Notes

- Ignore regions are masked with a solid color before diffing.
- If screenshot sizes differ and `compare.resize_on_mismatch=true`, current is resized to baseline size and a warning is recorded.

## License
MIT


## Library & CLI usage (examples)

### Programmatic API (Python)

The package exposes a simple programmatic API to integrate with Playwright or Selenium.

Example (Playwright):

```python
from visualcheck.runner import run_visualcheck
from visualcheck.config import load_config

cfg = load_config(Path('examples/wakefit.yaml'))
result = run_visualcheck(cfg, env='prod', headed=False)
print('Report saved at', result.get('report_html'))
```

### CLI usage

- Sync Figma baselines:

```bash
export FIGMA_TOKEN="<your_token>"
visualcheck figma-sync --config examples/wakefit.yaml
```

- Run full check:

```bash
visualcheck run --env prod --config examples/wakefit.yaml
```

### Notes on FIGMA token & env handling

- The library reads Figma token from the environment variable name specified in config (defaults to `FIGMA_TOKEN`).
- Keep secrets out of git; use host-level secrets (e.g. `~/.openclaw/secrets.env`) or a CI secret store.
