Spawn the decipher-qa CLI as a subprocess and parse its final result block. Below is a copy-pasteable reference implementation.
This is boilerplate — a thin wrapper around the CLI to get you started. Fork it, rename the functions, add retry logic, wire it into pytest fixtures, emit your own telemetry — whatever fits your pipeline. The CLI is the contract; the wrapper is just convenience.
The Runner
The CLI prints a final === RESULT === block with the run summary as JSON. The runner spawns the process, streams its output, and parses that block on exit.
decipher_qa_runner.py
import json
import re
import subprocess
import sys
from dataclasses import dataclass
from typing import Literal, Optional
TestRunStatus = Literal["passed", "failed", "failed_internal"]
_ANSI_RE = re.compile(r"\x1b\[[0-9;]*m")
_RESULT_MARKER = "=== RESULT ==="
@dataclass
class RunTestResult:
status: TestRunStatus
duration: int # milliseconds
run_id: int
run_url: str
def run_decipher_qa_test(
test_id: int,
*,
cdp_url: Optional[str] = None,
headful: bool = False,
origin: Optional[str] = None,
stream: bool = True,
) -> RunTestResult:
args = ["decipher-qa", "test", "run-cdp", "--testId", str(test_id)]
if cdp_url:
args += ["--cdp", cdp_url]
if headful:
args.append("--headful")
if origin:
args += ["--origin", origin]
process = subprocess.Popen(
args,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1,
)
captured: list[str] = []
assert process.stdout is not None
for line in process.stdout:
if stream:
sys.stdout.write(line)
sys.stdout.flush()
captured.append(line)
process.wait()
clean = _ANSI_RE.sub("", "".join(captured))
marker = clean.rfind(_RESULT_MARKER)
if marker == -1:
raise RuntimeError("decipher-qa exited without a result block")
payload = clean[marker + len(_RESULT_MARKER) :].strip()
data = json.loads(payload)
return RunTestResult(
status=data["status"],
duration=data["duration"],
run_id=data["runId"],
run_url=data["runUrl"],
)
Example Script
Here’s a self-contained script that runs test 1522 against a local headful Chromium and logs the result. Swap in your own test_id and options:
from decipher_qa_runner import run_decipher_qa_test
def main() -> None:
result = run_decipher_qa_test(
test_id=1522,
headful=True,
origin="https://staging.myapp.com",
stream=True,
)
print("\n=== RESULT ===")
print(result)
if result.status != "passed":
raise SystemExit(1)
if __name__ == "__main__":
main()
Usage
You’ll get a streaming view of the run followed by the parsed result:
RunTestResult(status='passed', duration=127000, run_id=48291, run_url='https://app.getdecipher.com/tests/1522/runs/48291')
The process exits 0 on pass and 1 on fail, so it slots into any CI pipeline that already expects conventional exit codes.
Need help? Contact our support team.