testdino caches test metadata, retrieves failed tests, and uploads reports with all their attachments.
Prerequisites
testdino PyPI package version 1.0.22+
- Python
>= 3.9
pytest with Playwright, typically via pytest-playwright
- Install package
pytest-playwright-json
- TestDino API token
- Git initialized repository (required for commit and branch metadata)
Quick Start Steps
The upload flow requires a JSON report. Generate it during the pytest run.
Install the required packages
pip install pytest-playwright-json pytest-html testdino
Run tests and write JSON and HTML reports
pytest \
--playwright-json=test-results/report.json \
--html=test-results/index.html \
--self-contained-html
Upload the report directory
testdino upload ./test-results --token="your-token"
Cache metadata for failed test reruns
testdino cache --working-dir test-results --token="your-token"
Common Workflows
1. Cache metadata after a run
2. Rerun only failed tests
Print failed tests:testdino last-failed --token="your-token"
Run only failed tests:pytest $(testdino last-failed --token="your-token")
3. Upload reports and artifacts
Upload the report directory:testdino upload ./test-results --token="your-token"
Upload all supported attachments:testdino upload ./test-results --token="your-token" --upload-full-json
4. Complete CI/CD Workflow
pytest \
--playwright-json=test-results/report.json \
--html=test-results/index.html \
--self-contained-html
Cache test metadata:testdino cache --working-dir test-results --token="$TESTDINO_TOKEN"
Rerun failed tests if the first run had failures:if [ $? -ne 0 ]; then
FAILED=$(testdino last-failed --token="$TESTDINO_TOKEN")
[ -n "$FAILED" ] && pytest $FAILED
fi
Commands
1. cache
Store test execution metadata after Playwright runs. This data powers the last-failed command and feeds into TestDino analytics.
Usage
Basic usage:
testdino cache --token="your-token"
With a custom working directory:
testdino cache --working-dir ./test-results --token="your-token"
With verbose logging:
testdino cache --verbose --token="your-token"
Options
| Option | Description | Default |
|---|
--working-dir <path> | Directory to scan for test results | Current directory |
--cache-id <value> | Custom cache ID override | Auto detected |
-t, --token <value> | TestDino API token | Required |
-v, --verbose | Enable verbose logging | false |
2. last-failed
Retrieve cached test failures for intelligent reruns. Outputs test identifiers that pass directly to pytest.
Usage
Basic usage:
testdino last-failed --token="your-token"
Run only failed tests:
pytest $(testdino last-failed --token="your-token")
Get failed tests for specific shard:
testdino last-failed --shard="2/5" --token="your-token"
With custom branch and commit:
testdino last-failed --branch="main" --commit="abc123" --token="your-token"
Options
| Option | Description | Default |
|---|
--cache-id <value> | Custom cache ID override | Auto detected |
--branch <value> | Custom branch name override | Auto detected |
--commit <value> | Custom commit hash override | Auto detected |
--shard <value> | Shard specification (e.g., “2/5” for shard 2 of 5) | None |
--environment <value> | Environment name for filtering tests | None |
-t, --token <value> | TestDino API token | Required |
-v, --verbose | Enable verbose logging | false |
3. upload <report-directory>
Upload Playwright test reports with attachments to TestDino.
The testdino upload command requires a JSON report to function.
You must:
- Install required plugins:
pip install pytest-playwright-json pytest-html
- Run your tests with the
--playwright-json flag:
pytest --playwright-json=test-results/report.json
- Optionally generate HTML reports:
pytest --html=test-results/index.html --self-contained-html
Usage
Basic upload:
testdino upload ./test-results --token="your-token"
Upload with environment tag:
testdino upload ./test-results --environment="staging" --token="your-token"
Upload with specific attachments:
testdino upload ./test-results --token="your-token" --upload-images --upload-videos
Upload all attachments:
testdino upload ./test-results --token="your-token" --upload-full-json
Options
| Option | Description |
|---|
<report-directory> | Directory containing Playwright reports (required) |
-t, --token <value> | TestDino API token (required) |
--environment <value> | Environment name (e.g., “staging”, “production”) |
--upload-images | Upload image attachments |
--upload-videos | Upload video attachments |
--upload-html | Upload HTML reports |
--upload-traces | Upload trace files |
--upload-files | Upload file attachments (.md, .pdf, .txt, .log) |
--upload-full-json | Upload all attachments |
-v, --verbose | Enable verbose logging |
Environment Variables
Set your token as an environment variable instead of passing it with each command:
export TESTDINO_TOKEN="your-api-token"
Set a custom API endpoint:
export TESTDINO_API_URL="https://api.testdino.com"
Set a default environment tag:
export TESTDINO_TARGET_ENV="staging"
CI/CD Integration
.github/workflows/test.yml
name: Playwright Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3, 4]
shardTotal: [4]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install pytest pytest-playwright pytest-playwright-json pytest-html testdino
playwright install chromium --with-deps
- name: Run Playwright Tests
shell: bash
env:
TESTDINO_TOKEN: ${{ secrets.TESTDINO_TOKEN }}
TESTDINO_API_URL: ${{ env.TESTDINO_API_URL }}
SHARD_INDEX: ${{ matrix.shardIndex }}
SHARD_TOTAL: ${{ matrix.shardTotal }}
run: |
mkdir -p test-results
echo "GitHub run attempt: ${{ github.run_attempt }}"
# Case 1: Re-run failed jobs → run only failed tests
if [[ "${{ github.run_attempt }}" -gt 1 ]]; then
echo "Detected re-run. Executing only last failed tests via TestDino."
testdino last-failed --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --token="$TESTDINO_TOKEN" > last-failed-flags.txt
FAILED_TESTS="$(cat last-failed-flags.txt | tail -1)"
if [[ -z "$FAILED_TESTS" ]]; then
echo "No failed tests found. Exiting."
exit 0
fi
echo "Running failed tests without sharding:"
echo "$FAILED_TESTS"
# IMPORTANT: Use eval to preserve quotes in the -k expression
# This matches the JavaScript workflow pattern and ensures quotes are preserved
eval "pytest $FAILED_TESTS --playwright-json=test-results/report.json --html=test-results/index.html --self-contained-html -p no:selenium -v" || true
exit 0
fi
# Case 2: Normal execution (first run)
echo "Running all Playwright tests"
pytest \
--playwright-json=test-results/report.json \
--html=test-results/index.html \
--self-contained-html \
-p no:selenium \
-v || true
- name: Cache rerun metadata
if: always()
run: testdino cache --working-dir test-results --token="${{ secrets.TESTDINO_TOKEN }}" -v
- name: Upload test reports
if: always()
run: testdino upload ./test-results --token="${{ secrets.TESTDINO_TOKEN }}" --upload-full-json
.github/workflows/test.yml
name: Playwright Tests with pytest-shard
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3, 4]
shardTotal: [4]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest pytest-playwright pytest-playwright-json pytest-html pytest-xdist pytest-shard
pip install testdino
playwright install --with-deps chromium
- name: Run Playwright Tests
shell: bash
env:
TESTDINO_TOKEN: ${{ secrets.TESTDINO_TOKEN }}
SHARD_INDEX: ${{ matrix.shardIndex }}
SHARD_TOTAL: ${{ matrix.shardTotal }}
run: |
mkdir -p test-results
# Case 1: Re-run failed jobs → run only failed tests
if [[ "${{ github.run_attempt }}" -gt 1 ]]; then
testdino last-failed --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --token="$TESTDINO_TOKEN" > last-failed-flags.txt
FAILED_TESTS="$(cat last-failed-flags.txt | tail -1)"
if [[ -z "$FAILED_TESTS" ]]; then
exit 0
fi
# IMPORTANT: Use eval to preserve quotes in the -k expression
# pytest-shard uses 0-indexed shard IDs, so subtract 1 from SHARD_INDEX
# Use shard-specific report name
SHARD_ID=$(( $SHARD_INDEX - 1 ))
eval "pytest $FAILED_TESTS --shard-id=$SHARD_ID --num-shards=$SHARD_TOTAL --playwright-json=test-results/report-${{ matrix.shardIndex }}.json --html=test-results/index.html --self-contained-html -p no:selenium -v" || true
exit 0
fi
# Case 2: Normal execution (first run) with sharding
# pytest-shard uses 0-indexed shard IDs, so subtract 1 from SHARD_INDEX
SHARD_ID=$(( $SHARD_INDEX - 1 ))
# Use shard-specific report name to prevent overwriting when artifacts are merged
pytest \
--shard-id=$SHARD_ID \
--num-shards=$SHARD_TOTAL \
--playwright-json=test-results/report-${{ matrix.shardIndex }}.json \
--html=test-results/index.html \
--self-contained-html \
-p no:selenium \
-v || true
- name: Cache rerun metadata
if: always()
run: testdino cache --working-dir test-results --token="${{ secrets.TESTDINO_TOKEN }}"
- name: Upload test reports
if: always()
run: testdino upload ./test-results --token="${{ secrets.TESTDINO_TOKEN }}" --upload-full-json
image: python:3.11
stages:
- test
playwright-tests:
stage: test
script:
- pip install pytest pytest-playwright pytest-playwright-json pytest-html testdino
- playwright install chromium --with-deps
- pytest --playwright-json=test-results/report.json --html=test-results/index.html --self-contained-html
- testdino upload ./test-results --token="$TESTDINO_TOKEN" --upload-full-json
when: always
pipeline {
agent any
environment {
TESTDINO_TOKEN = credentials('testdino-token')
}
stages {
stage('Test') {
steps {
sh 'pip install pytest pytest-playwright pytest-playwright-json pytest-html testdino'
sh 'playwright install chromium --with-deps'
sh 'pytest --playwright-json=test-results/report.json --html=test-results/index.html --self-contained-html'
sh 'testdino upload ./test-results --token="$TESTDINO_TOKEN" --upload-full-json'
}
}
}
}
Authentication
Generate an API token in the TestDino app and store it as a secret.
- Store the token in CI secrets or environment variables
- Do not commit tokens to version control
- Rotate tokens on a regular schedule