Configuration

Learn how to configure Probitas scenarios, steps, and clients. This guide covers all available options and common configuration patterns.

Project Configuration

Configure Probitas CLI defaults in a configuration file in your project root.

Configuration File Names

Probitas looks for configuration files in this priority order:

  1. probitas.json - Standard JSON format
  2. probitas.jsonc - JSON with comments (recommended)
  3. .probitas.json - Hidden JSON format
  4. .probitas.jsonc - Hidden JSON with comments

The first file found is used. JSONC files support // and /* */ comments.

Configuration Options

{
  // Glob patterns for scenario file discovery
  "includes": ["probitas/**/*.probitas.ts"],
  // Glob patterns to exclude from discovery
  "excludes": ["**/*.skip.probitas.ts"],
  // Output reporter (list, json)
  "reporter": "list",
  // Maximum parallel scenario execution (0 = unlimited)
  "maxConcurrency": 4,
  // Maximum failures before stopping (0 = unlimited)
  "maxFailures": 0,
  // Default timeout for scenarios
  "timeout": "30s",
  // Default selectors for filtering scenarios
  "selectors": ["!tag:wip"]
}
OptionDescriptionDefault
includesGlob patterns for scenario file discovery["probitas/**/*.probitas.ts"]
excludesGlob patterns to exclude from discoveryCommon build/dependency directories*
reporterOutput reporter: list, json"list"
maxConcurrencyMaximum parallel scenario executionunlimited (0)
maxFailuresMaximum failures before stoppingunlimited (0)
timeoutDefault timeout for scenarios"30s"
selectorsDefault selectors for filtering scenarios[]

*Default exclude patterns:

  • **/node_modules/**, **/target/**, **/.venv/**, **/venv/**, **/__pycache__/**, **/vendor/**, **/build/**, **/bin/**, **/obj/**, **/.git/**, **/dist/**, **/coverage/**

maxFailures

Controls when the test runner stops execution:

  • 0 (default): Run all scenarios regardless of failures
  • 1: Fail-fast mode - stop immediately on first failure
  • n: Stop after n failures
{
  // Fail-fast: stop on first failure
  "maxFailures": 1
}

Selectors

Selectors filter which scenarios to run. The format is [!][type:]value:

TypeDescriptionExample
tagMatch by scenario tagtag:api
nameMatch by scenario name (default)Login or name:Login
!Negation prefix!tag:slow

Multiple selectors use OR logic. Comma-separated values within a selector use AND logic:

{
  "selectors": ["tag:api,!tag:slow"]
}

This runs scenarios with api tag AND without slow tag.

CLI Override

Command-line options override configuration file settings:

# Override reporter
probitas run --reporter json

# Override concurrency
probitas run --max-concurrency 1

# Fail-fast mode
probitas run --max-failures 1

# Add selectors (combined with config selectors)
probitas run -s tag:smoke

Scenario Options

Configure scenarios using the options parameter of `scenario()`.

import { scenario } from "jsr:@probitas/probitas";

scenario("My Test", {
  tags: ["api", "integration"],
  stepOptions: {
    timeout: 60000,
    retry: { maxAttempts: 3, backoff: "exponential" },
  },
})
  .step(() => {})
  .build();
OptionDescriptionDefault
tagsTags for filtering scenarios during runs[]
stepOptions.timeoutDefault timeout for all steps (ms)30000
stepOptions.retry.maxAttemptsDefault max retry attempts for steps1
stepOptions.retry.backoffBackoff strategy: "linear" or "exponential""linear"

Tags

Use tags to categorize and filter scenarios:

import { scenario } from "jsr:@probitas/probitas";

scenario("API Integration", {
  tags: ["api", "integration", "slow"],
})
  .step(() => {})
  .build();

Run scenarios by tag using selectors:

# Run only "api" tagged scenarios
probitas run -s tag:api

# Exclude "slow" scenarios
probitas run -s "!tag:slow"

# Combine filters (AND logic)
probitas run -s "tag:api,!tag:slow"

Step Options

Override scenario defaults for individual steps:

import { scenario } from "jsr:@probitas/probitas";

scenario("Mixed Timeouts")
  .step(
    "Quick check",
    async (_ctx) => {
      // Must complete in 1 second
    },
    { timeout: 1000 },
  )
  .step(
    "Slow operation",
    async (_ctx) => {
      // Can take up to 60 seconds
    },
    { timeout: 60000 },
  )
  .step(
    "Flaky external call",
    async (_ctx) => {
      // Retry up to 5 times with exponential backoff
    },
    {
      retry: { maxAttempts: 5, backoff: "exponential" },
    },
  )
  .build();
OptionDescription
timeoutStep timeout in milliseconds
retry.maxAttemptsMaximum retry attempts for this step
retry.backoffBackoff strategy for this step

Timeout Behavior

When a step times out:

  1. The step's ctx.signal is aborted
  2. The step fails with TimeoutError
  3. Retry logic applies if configured
  4. Cleanup hooks still execute
import { scenario } from "jsr:@probitas/probitas";

const url = "https://api.example.com/data";

scenario("Timeout Example")
  .step(
    "Cancellable operation",
    async (ctx) => {
      // Pass signal to cancellable operations
      const response = await fetch(url, { signal: ctx.signal });
      return response.json();
    },
    { timeout: 5000 },
  )
  .build();

Retry Configuration

Configure automatic retries for flaky operations.

OptionDescriptionDefault
maxAttemptsMaximum number of attempts1
backoff"linear" or "exponential"
initialDelayFirst retry delay (ms)1000
maxDelayMaximum delay between retries (ms)30000
retryOnCustom predicate for retry decisions

Backoff Strategies

StrategyDelay Pattern
"linear"1s, 2s, 3s, 4s...
"exponential"1s, 2s, 4s, 8s...

Custom Retry Logic

import { client, scenario } from "jsr:@probitas/probitas";

scenario("Retry Example")
  .resource(
    "http",
    () => client.http.createHttpClient({ url: "http://localhost:8080" }),
  )
  .step(async (ctx) => {
    const { http } = ctx.resources;
    const res = await http.get("/endpoint", {
      retry: {
        maxAttempts: 3,
        backoff: "exponential",
        initialDelay: 500,
        maxDelay: 10000,
        retryOn: (error: Error) => {
          // Only retry on specific errors
          return error.message.includes("network") ||
            error.message.includes("timeout");
        },
      },
    });
    return res.json;
  })
  .build();

Client Configurations

HTTP Client

import { client } from "jsr:@probitas/probitas";

client.http.createHttpClient({
  url: "http://localhost:8080",
  headers: { "Content-Type": "application/json" },
  throwOnError: true,
  timeout: 10000,
});
OptionDescriptionDefault
urlBase URL for all requests (required)
headersDefault headers for all requests{}
throwOnErrorThrow on 4xx/5xx responsestrue
timeoutRequest timeout (ms)
redirect"follow", "manual", or "error""follow"

PostgreSQL Client

import { client } from "jsr:@probitas/probitas";

client.sql.postgres.createPostgresClient({
  url: {
    host: "localhost",
    port: 5432,
    database: "testdb",
    username: "testuser",
    password: "testpass",
  },
  pool: { max: 10 },
});
OptionDescriptionDefault
urlConnection string or config object
pool.minMinimum pool connections0
pool.maxMaximum pool connections10
pool.idleTimeoutIdle connection timeout (ms)30000

Connection can also be a URL string:

import { client } from "jsr:@probitas/probitas";

client.sql.postgres.createPostgresClient({
  url: "postgres://user:pass@localhost:5432/mydb",
});

gRPC Client

import { client } from "jsr:@probitas/probitas";

client.grpc.createGrpcClient({
  url: "localhost:50051",
  metadata: { authorization: "Bearer token" },
  tls: { insecure: false },
});
OptionDescriptionDefault
urlServer address (host:port)
metadataDefault metadata for all calls{}
tlsTLS configuration
schemaProto schema source"reflection"

GraphQL Client

import { client } from "jsr:@probitas/probitas";

client.graphql.createGraphqlClient({
  url: "http://localhost:4000/graphql",
  headers: { Authorization: "Bearer token" },
});
OptionDescriptionDefault
urlGraphQL HTTP endpoint (required)
headersDefault headers{}
wsUrlWebSocket endpoint for subscriptions
throwOnErrorThrow on GraphQL errorstrue

Redis Client

import { client } from "jsr:@probitas/probitas";

client.redis.createRedisClient({
  url: "redis://localhost:6379",
});
OptionDescriptionDefault
urlRedis connection URL (redis://host:port)"redis://localhost:6379"

MongoDB Client

import { client } from "jsr:@probitas/probitas";

client.mongodb.createMongoClient({
  url: "mongodb://localhost:27017",
  database: "testdb",
});
OptionDescriptionDefault
uriMongoDB connection URI
databaseDefault database name

RabbitMQ Client

import { client } from "jsr:@probitas/probitas";

client.rabbitmq.createRabbitMqClient({
  url: "amqp://guest:guest@localhost:5672",
});
OptionDescriptionDefault
urlAMQP connection URL"amqp://localhost"

SQS Client

import { client } from "jsr:@probitas/probitas";

client.sqs.createSqsClient({
  url: "http://localhost:4566",
  region: "us-east-1",
  credentials: {
    accessKeyId: "test",
    secretAccessKey: "test",
  },
});
OptionDescriptionDefault
urlSQS endpoint URL
regionAWS region
credentialsAWS access key and secret

Environment Variables

Probitas respects these environment variables:

VariablePurpose
NO_COLORDisable colored output

Using Environment Variables

import { client, scenario } from "jsr:@probitas/probitas";

scenario("Production Test")
  .resource("http", () =>
    client.http.createHttpClient({
      url: Deno.env.get("API_URL") ?? "http://localhost:8080",
      headers: {
        Authorization: `Bearer ${Deno.env.get("API_TOKEN")}`,
      },
    }))
  .resource("pg", () =>
    client.sql.postgres.createPostgresClient({
      url: Deno.env.get("DATABASE_URL") ??
        "postgres://user:pass@localhost/testdb",
    }))
  .step(() => {})
  .build();

Configuration Patterns

Environment-Aware Configuration

import { client, scenario } from "jsr:@probitas/probitas";

const isProduction = Deno.env.get("ENV") === "production";

scenario("API Test")
  .resource("http", () =>
    client.http.createHttpClient({
      url: isProduction ? "https://api.example.com" : "http://localhost:8080",
      timeout: isProduction ? 30000 : 5000,
      retry: isProduction
        ? { maxAttempts: 3, backoff: "exponential" }
        : undefined,
    }))
  .step(() => {})
  .build();

Shared Configuration

import { client, scenario } from "jsr:@probitas/probitas";

const httpDefaults = {
  timeout: 10000,
  headers: { "Content-Type": "application/json" },
};

const dbDefaults = {
  pool: { min: 1, max: 5 },
};

scenario("Test")
  .resource("http", () =>
    client.http.createHttpClient({
      url: "http://localhost:8080",
      ...httpDefaults,
    }))
  .resource("pg", () =>
    client.sql.postgres.createPostgresClient({
      url: "postgres://user:pass@localhost:5432/mydb",
      ...dbDefaults,
    }))
  .step(() => {})
  .build();

Resource Factory Pattern

import { client, scenario } from "jsr:@probitas/probitas";

function createApiClient() {
  return client.http.createHttpClient({
    url: Deno.env.get("API_URL") ?? "http://localhost:8080",
    timeout: 10000,
    retry: { maxAttempts: 2 },
  });
}

function createTestDatabase() {
  return client.sql.postgres.createPostgresClient({
    url: {
      host: Deno.env.get("DB_HOST") ?? "localhost",
      port: Number(Deno.env.get("DB_PORT") ?? 5432),
      database: "testdb",
      username: "testuser",
      password: "testpass",
    },
  });
}

scenario("Test")
  .resource("http", createApiClient)
  .resource("pg", createTestDatabase)
  .step(() => {})
  .build();

Conditional Resources

import { client, scenario, Skip } from "jsr:@probitas/probitas";

scenario("Conditional Features")
  .resource(
    "http",
    () => client.http.createHttpClient({ url: "http://localhost:8080" }),
  )
  .resource("redis", () => {
    const redisUrl = Deno.env.get("REDIS_URL");
    if (!redisUrl) {
      throw new Skip("Redis not configured");
    }
    return client.redis.createRedisClient({
      url: redisUrl,
    });
  })
  .step(() => {})
  .build();
Search Documentation