Sign inSign up

wvankuipers/inspectre

By wvankuipers

•Updated 11 days ago

Visual regression testing as a service

Image
Integration & delivery
Developer tools
0

6.9K

wvankuipers/inspectre repository overview

Inspectre logo

⁠Inspectre

CI Coverage Version API image size SPA image size License

Visual regression testing as a service. CI pipelines POST screenshots to Inspectre; Inspectre diffs them against approved baselines and reports pass/fail. A web UI lets your team review failures, inspect diff images, and promote new baselines with one click.

Built as a modern Python/Angular rewrite of the original Spectre⁠ Rails app.


⁠How it works

CI job
  └─ POST /runs          → creates a run
  └─ POST /tests         → uploads screenshot, returns immediately (~50 ms)
  └─ GET  /tests/:id/status  → poll until status == "done"

Celery worker (async)
  └─ ImageMagick compare vs stored baseline
  └─ Stores diff image + thumbnails to S3
  └─ Marks test passed / failed

Human
  └─ Opens http://localhost:4200
  └─ Reviews diff images, clicks "Set as baseline" to accept regressions

The first submission for any (project, suite, name, browser, size) combination has nothing to compare against, so it's stored with no comparison images, marked is_new_baseline: true, and marked not passed. A human must review it and click "Set as baseline" before it becomes the reference image subsequent submissions compare against. Later submissions that match an established baseline pass automatically, with no human involved.


⁠Architecture

LayerTechnology
API serverPython, Django, Django REST Framework
Task queueCelery + Valkey (Redis-protocol broker)
Web UIAngular, standalone/zoneless components, Angular Material
DatabasePostgreSQL
Object storageS3-compatible — MinIO in dev, AWS S3 in prod
Servinggunicorn (API) + nginx (SPA + reverse proxy)
OrchestrationDocker Compose
⁠Domain model
Project ──▶ Suite ──▶ Run ──▶ Test
                  └─▶ Baseline  (one per test "key", lives on the Suite)
  • Project — top-level grouping, e.g. "Acme Marketing Site"
  • Suite — category of tests inside a project, e.g. "Desktop" or "Mobile"
  • Run — one CI execution of a suite; the last 5 runs are kept per suite
  • Test — one screenshot submission; carries diff %, pass/fail, and S3 image URLs
  • Baseline — the accepted-good screenshot for a key; updated automatically when a test passes against an existing baseline, or manually via a human's "Set as baseline" click in the UI (required for the very first submission of a key, since there's nothing to compare it against and it never passes automatically)
⁠Image pipeline

POST /tests returns in ~50 ms. The heavy work runs in a Celery worker:

  1. Download staged upload from S3
  2. Optionally crop to a specified region
  3. Pad both images to the same canvas
  4. imagemagick compare -metric AE → diff pixel count → diff percentage
  5. Upload original, baseline snapshot, diff overlay, and 300 px JPEG thumbnails
  6. Upsert Baseline if passing against an existing baseline; set status = "done"
⁠API surfaces

Two URL namespaces:

  • /api/* — SPA endpoints; can evolve freely
  • Legacy (un-prefixed) — frozen contract for CI clients: POST /runs, POST /tests, GET /tests/:id/status, PATCH /tests/:id, GET /baselines/:key.png, GET /baselines/:key.json
⁠Docker images
ImageTags
wvankuipers/inspectre⁠api-latest, api-<version>
wvankuipers/inspectre⁠spa-latest, spa-<version>
⁠Documentation

⁠Development

⁠Prerequisites
  • Docker Desktop (or Docker Engine + Compose plugin)
  • make
⁠First-time setup
cp deploy/.env.example .env
# Edit .env — at minimum set ADMIN_PASSWORD
make build
make up
make migrate

Services:

ServiceURL
SPAhttp://localhost:4200⁠
APIhttp://localhost:8000⁠
Django adminhttp://localhost:8000/admin⁠
MinIO consolehttp://localhost:9001⁠ (login: minio / miniominio)

The SPA's nginx reverse-proxies /api, /runs, /tests, /baselines, and /admin to the API container — everything is same-origin from the browser.

⁠Seed demo data
make seed

Populates three projects with runs, pass/fail tests, and baselines so you can explore the UI without a real CI pipeline.

⁠Common commands
CommandWhat it does
make upStart the stack
make downStop the stack (volumes preserved)
make testAll backend + frontend tests
make test-fastBackend only, no ImageMagick (~1 s)
make test-slowBackend including image diff tests
make lint / make lint-fixRuff + Angular ESLint
make migrateRun Django migrations inside api container
make shell-apiDjango shell inside api container
make resetNuke and rebuild everything from scratch
make perfPerformance stress test (needs make up)
⁠Rebuilding after code changes

Backend / worker:

docker compose build api worker && docker compose up -d api worker

Frontend:

docker compose build spa && docker compose up -d spa

⁠Production deployment

⁠Environment variables
VariableRequiredNotes
DJANGO_SECRET_KEYyesGenerate with python -c 'import secrets; print(secrets.token_urlsafe(50))'
DEBUGno0 in prod
ALLOWED_HOSTSyesComma-separated hostnames
CORS_ALLOWED_ORIGINSnoComma-separated origins; empty means same-origin only
DATABASE_URLyesFull postgres DSN
S3_BUCKET_NAMEyesMust exist before first run
S3_REGIONyesAWS region or any value for MinIO
S3_ENDPOINT_URLnoSet for MinIO; omit for AWS S3
S3_ACCESS_KEY_IDyesAWS key or MinIO root user
S3_SECRET_ACCESS_KEYyesAWS secret or MinIO root password
S3_PUBLIC_BASE_URLnoCDN prefix for browser-reachable image URLs
ADMIN_USERNAMEnoDefault: admin
ADMIN_PASSWORDnoIf unset, ensure_admin_user skips bootstrap (no admin account created)
REDIS_URLnoValkey/Redis broker; default redis://localhost:6379/0
CELERY_WORKER_CONCURRENCYnoParallel image pipelines per worker; default 2
GUNICORN_WORKERSnoAPI server processes; default 3
IMAGE_DIFF_THRESHOLDnoDiff % to count as failure; default 0.1
RUN_RETENTION_PER_SUITEnoMax runs kept per suite; default 5
⁠Deploy
# First deploy
cp deploy/.env.example .env   # fill in production values
docker compose -f deploy/docker-compose.yml up -d
docker compose -f deploy/docker-compose.yml exec api python manage.py migrate

# Subsequent deploys
docker compose -f deploy/docker-compose.yml build api spa worker
docker compose -f deploy/docker-compose.yml up -d api spa worker
docker compose -f deploy/docker-compose.yml exec api python manage.py migrate
⁠Production checklist
  • DEBUG=0
  • DJANGO_SECRET_KEY is unique and random
  • ALLOWED_HOSTS includes the production hostname
  • S3 bucket exists; credentials have s3:PutObject, s3:GetObject, s3:DeleteObject
  • Database backed up before running migrations
  • S3_PUBLIC_BASE_URL set if serving images through a CDN

⁠Submitting screenshots from CI

# 1. Create a run
RUN=$(curl -s -X POST http://inspectre.example.com/runs \
  -d "project=My App&suite=Desktop" | jq -r .id)

# 2. Submit a screenshot (returns immediately with status=pending)
TEST_ID=$(curl -s -X POST http://inspectre.example.com/tests \
  -F "test[run_id]=$RUN" \
  -F "test[name]=Homepage" \
  -F "test[browser]=Chrome" \
  -F "test[size]=1440" \
  -F "test[screenshot][email protected]" | jq -r .id)

# 3. Poll for result
while true; do
  STATUS=$(curl -s http://inspectre.example.com/tests/$TEST_ID/status | jq -r .status)
  [ "$STATUS" = "done" ] || [ "$STATUS" = "failed" ] && break
  sleep 1
done

The pass field in the final response is true if the diff percentage is below the threshold.


⁠CI/CD

The GitHub Actions workflow (.github/workflows/ci.yml) runs lint and tests on every push and pull request to main, then builds and pushes multi-arch Docker images to Docker Hub on successful merges to main.

⁠Required repository secrets
SecretHow to obtain
DOCKERHUB_USERNAMEYour Docker Hub username
DOCKERHUB_TOKENDocker Hub → Account Settings → Security → New Access Token

Set these under Settings → Secrets and variables → Actions in the GitHub repository.

⁠Published images
ImageTag
<DOCKERHUB_USERNAME>/inspectreapi-latest
<DOCKERHUB_USERNAME>/inspectreapi-$VERSION
<DOCKERHUB_USERNAME>/inspectrespa-latest
<DOCKERHUB_USERNAME>/inspectrespa-$VERSION

⁠License

See LICENSE⁠.

Tag summary

Content type

Image

Digest

sha256:1e8c42a9a…

Size

145.9 MB

Last updated

11 days ago

docker pull wvankuipers/inspectre:api-1.6.1