Snapline Integration

Push test reports from Snapline for Node.js and Python into Hub. Covers programmatic APIs, environment variables, CLI flags, and CI pipelines.

Prerequisite: Snapline Hub must be running and reachable at the URL you configure. See Installation.

Overview

Snapline produces TestSuiteResult objects from every test run. To send data to Hub:

  1. Collect one or more TestSuiteResult values
  2. Build a TestRunReport with buildReport() / build_report()
  3. Push with pushTestReportToHub() / push_test_report_to_hub()

You can also write a local JSON file and push to Hub in the same run — they are independent.

Node.js integration

Package: @vaagatech/snapline-core

Exports

FunctionPurpose
buildReport(suites, meta?)Build TestRunReport from suite results
pushTestReportToHub(report, config)POST report to Hub API
resolveHubConfig(argv?)Read SNAPLINE_HUB_URL / CLI flags
writeTestReport(suites, config, meta?)Write local file (optional, independent of Hub)
resolveReportConfig(argv?)Read REPORT_FORMAT / CLI flags for local files

Basic push

import {
  testSuite,
  buildReport,
  pushTestReportToHub,
} from '@vaagatech/snapline-core';

const startedAt = Date.now();
const result = await testSuite('User sync', {
  baseUrl: process.env.API_BASE_URL,
  api: {
    endpoint: '/api/v1/users/me',
    method: 'GET',
    expectedFile: './fixtures/expected.json',
  },
});

const report = buildReport([result], {
  durationMs: Date.now() - startedAt,
  environment: {
    baseUrl: process.env.API_BASE_URL ?? '',
    branch: process.env.CI_BRANCH ?? 'local',
  },
});

await pushTestReportToHub(report, {
  hubUrl: 'http://localhost:3847',
  label: 'User sync — staging',
});

process.exitCode = result.passed ? 0 : 1;

Fixture cases

import {
  fixturesDir,
  runApiFixtureCases,
  buildReport,
  pushTestReportToHub,
} from '@vaagatech/snapline-core';

const startedAt = Date.now();
const result = await runApiFixtureCases({
  suiteName: 'Customer account — GraphQL cases',
  fixturesRoot: fixturesDir(import.meta.url),
  baseUrl: process.env.API_BASE_URL,
  defaults: { endpoint: '/graphql', protocol: 'graphql' },
});

const report = buildReport([result], {
  durationMs: Date.now() - startedAt,
  environment: { baseUrl: process.env.API_BASE_URL ?? '' },
});

await pushTestReportToHub(report, {
  hubUrl: process.env.SNAPLINE_HUB_URL!,
  label: 'GraphQL fixture suite',
});

Environment variables (Node.js)

VariableDescription
SNAPLINE_HUB_URLHub base URL, e.g. http://localhost:3847 or https://hub.yourcompany.com
SNAPLINE_HUB_LABELOptional label shown in the Hub UI
SNAPLINE_HUB_PROJECTLogical project grouping (e.g. snapline-demo, my-app)
SNAPLINE_HUB_TAGSComma-separated tags (e.g. node,demo,ci)

CLI flags (Node.js)

FlagDescription
--hub-url=<url>Hub base URL (overrides env)
--hub-label=<label>Run label (overrides env)
node tests/run.mjs --hub-url=http://localhost:3847 --hub-label="PR #42"

Using resolveHubConfig

import { buildReport, pushTestReportToHub, resolveHubConfig } from '@vaagatech/snapline-core';

const report = buildReport(results, { durationMs });
const hubConfig = resolveHubConfig();

if (hubConfig) {
  const { url } = await pushTestReportToHub(report, hubConfig);
  console.log(`Report pushed to ${url}`);
}

Full demo runner

The Snapline monorepo demo automatically pushes when SNAPLINE_HUB_URL is set:

cd snapline
npm install
SNAPLINE_HUB_URL=http://localhost:3847 npm run demo

Pushes all 21 scenario results as one report labeled Full integration demo.

Python integration

Package: snapline-core (import snapline.core)

Exports

FunctionNode.js equivalent
build_report(suites, meta=None)buildReport()
push_test_report_to_hub(report, ...)pushTestReportToHub()
resolve_hub_config(argv=None)resolveHubConfig()
write_test_report(suites, config, meta=None)writeTestReport()

Basic push

import asyncio
import time
from snapline.core import test_suite, build_report, push_test_report_to_hub

async def main():
    started = time.time() * 1000
    result = await test_suite("User sync", {
        "baseUrl": "https://api.example.com",
        "api": {
            "endpoint": "/api/v1/users/me",
            "method": "GET",
            "expectedFile": "fixtures/expected.json",
        },
    })

    report = build_report([result], {
        "durationMs": int(time.time() * 1000 - started),
        "environment": {"baseUrl": "https://api.example.com"},
    })

    push_test_report_to_hub(
        report,
        hub_url="http://localhost:3847",
        label="User sync — staging",
    )

asyncio.run(main())

Using resolve_hub_config

from snapline.core import build_report, push_test_report_to_hub, resolve_hub_config

report = build_report(results, {"durationMs": duration_ms})
hub_config = resolve_hub_config()

if hub_config:
    result = push_test_report_to_hub(report, config=hub_config)
    print(f"Report pushed to {result['url']}")

Environment variables (Python)

Same as Node.js: SNAPLINE_HUB_URL and SNAPLINE_HUB_LABEL.

SNAPLINE_HUB_URL=http://localhost:3847 uv run demo

CLI flags (Python)

python run_demo.py --hub-url=http://localhost:3847 --hub-label="nightly"

CI integration

Typical pattern: run tests in CI, push report to a persistent Hub instance, fail the job if tests failed.

GitHub Actions (Node.js)

jobs:
  integration-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - name: Run integration tests
        env:
          API_BASE_URL: ${{ secrets.STAGING_API_URL }}
          SNAPLINE_HUB_URL: ${{ vars.SNAPLINE_HUB_URL }}
          SNAPLINE_HUB_LABEL: "CI ${{ github.run_number }} — ${{ github.sha }}"
        run: node tests/run.mjs
      # tests/run.mjs should call pushTestReportToHub when resolveHubConfig() returns config

GitHub Actions (Python)

      - name: Run integration tests
        env:
          SNAPLINE_HUB_URL: ${{ vars.SNAPLINE_HUB_URL }}
          SNAPLINE_HUB_LABEL: "CI ${{ github.run_number }}"
        run: uv run pytest tests/ -v

Combined: local file + Hub

import { buildReport, pushTestReportToHub, resolveHubConfig, resolveReportConfig, writeTestReport } from '@vaagatech/snapline-core';

const report = buildReport(results, { durationMs, environment: { baseUrl } });

const reportConfig = resolveReportConfig();
if (reportConfig) {
  writeTestReport(results, reportConfig, { durationMs, environment: { baseUrl } });
}

const hubConfig = resolveHubConfig();
if (hubConfig) {
  await pushTestReportToHub(report, hubConfig);
}

TestRunReport schema

Hub accepts the same JSON schema produced by Snapline's buildReport:

{
  "generatedAt": "2026-07-07T10:00:00.000Z",
  "framework": "@vaagatech/snapline-engine",
  "summary": {
    "total": 2,
    "passed": 1,
    "failed": 1,
    "durationMs": 1200
  },
  "environment": {
    "baseUrl": "http://localhost:3000",
    "branch": "main"
  },
  "suites": [
    {
      "name": "GraphQL fixture cases",
      "passed": false,
      "results": [
        { "step": "01-pass-customer-account", "passed": true },
        {
          "step": "04-fail-segment-mapping",
          "passed": false,
          "message": "Expected mismatch at segment",
          "diff": {
            "path": "segment",
            "actual": "premium",
            "expected": "standard",
            "message": "Value mismatch"
          }
        }
      ]
    }
  ]
}

See Architecture for the full type reference.

Error handling

SituationBehavior
Hub unreachablepushTestReportToHub throws with HTTP status and body
Invalid report JSONHub returns 400 with validation hint
SNAPLINE_HUB_URL not setresolveHubConfig() returns undefined — skip push silently
Tests failed but push succeededHub stores the report; CI should still fail on result.passed === false

Recommended: don't fail tests when Hub push fails

const hubConfig = resolveHubConfig();
if (hubConfig) {
  try {
    const { url } = await pushTestReportToHub(report, hubConfig);
    console.log(`Hub: ${url}`);
  } catch (err) {
    console.warn('Hub push failed (tests still evaluated):', err);
  }
}

Redaction

Sensitive fields should be redacted before pushing. Use Snapline's built-in redaction when building the report:

import { buildReport, redactSuiteResults } from '@vaagatech/snapline-core';

const sanitized = redactSuiteResults(results, ['data.token', 'processed.password']);
const report = buildReport(sanitized, { durationMs });

Hub stores whatever JSON you send. Treat the Hub database like any test artifact store — do not push secrets.