Skip to content

PactJS UtilsReusable utilities for Pact.js contract testing

Opinionated guardrails for enterprise-scale contract testing.

Why Not Raw Pact?

Pact.js gives you primitives and lets each team wire them together. That flexibility becomes a liability at scale: team A configures selectors one way, team B forgets the breaking-change flag, team C hard-codes broker URLs.

This library enforces a single pattern across every repository:

Problem with raw PactWhat goes wrongpactjs-utils
Each team wires their own setupStyle drifts across repos; 30-line boilerplate diverges silently.buildVerifierOptions / buildMessageVerifierOptions — one call, same config shape, every repo.
DIY consumer version selectorsTeam A verifies mainBranch, team B doesn't. Deployments break silently.includeMainAndDeployed: true/false — one boolean, same selectors everywhere.
Manual broker URL / webhook handlingHardcoded URLs break when the broker moves; HTTP suite consumes a Kafka webhook URL (or vice versa).handlePactBrokerUrlAndSelectors reads env vars, routes URLs, ignores cross-execution mismatches.
No breaking-change workflowProvider tagged as dev while consumer hasn't caught up — broken prod.When your test maps PACT_BREAKING_CHANGE to includeMainAndDeployed: false, selectors narrow to matchingBranch and dev is dropped from provider version tags.
Auth injection left to the developerDouble-Bearer prefix (Bearer Bearer <token>) — 401 on every run.createRequestFilter adds Bearer exactly once; tokenGenerator returns the raw value.
Manual JsonMap casting[object Object] in provider state params — handler crashes.toJsonMap converts deterministically; createProviderState wraps the pattern.
Repetitive builder lambdasEvery interaction repeats builder.query(...) / builder.jsonBody(...).setJsonContent — one curried helper reusable across request and response builders.

Quick Start

Consumer: Creating provider states with typed params

typescript
// pact/http/consumer/movies-read.pacttest.ts
import {
  createProviderState,
  setJsonContent
} from '@seontechnologies/pactjs-utils'

const state = createProviderState({
  name: 'Has a movie with a specific ID',
  params: { id: 1, name: 'Inception', year: 2010 }
})

await pact
  .addInteraction()
  .given(...state)
  .withRequest(
    'GET',
    '/movies',
    setJsonContent({
      query: { name: 'Inception' }
    })
  )
  .willRespondWith(
    200,
    setJsonContent({
      body: {
        status: 200,
        data: [{ id: 1, name: 'Inception' }]
      }
    })
  )

Provider: Building verifier options with one call

typescript
// pact/http/provider/provider-contract.pacttest.ts
import { buildVerifierOptions } from '@seontechnologies/pactjs-utils'

const options = buildVerifierOptions({
  provider: 'SampleMoviesAPI',
  port: '3001',
  stateHandlers,
  includeMainAndDeployed: process.env.PACT_BREAKING_CHANGE !== 'true',
  requestFilter: createRequestFilter({
    tokenGenerator: () => generateAuthToken({ userIdentifier: 'admin' })
  })
})

const verifier = new Verifier(options)
await verifier.verifyProvider()

Auth: Injecting request filters

typescript
import {
  createRequestFilter,
  noOpRequestFilter
} from '@seontechnologies/pactjs-utils'

// Custom token generator
const filter = createRequestFilter({
  tokenGenerator: () =>
    `${new Date().toISOString()}:${JSON.stringify({ userId: 'admin' })}`
})

// Default (ISO timestamp token)
const defaultFilter = createRequestFilter()

Message/Kafka provider verification

typescript
// pact/message/provider/provider-message-queue.pacttest.ts
import { buildMessageVerifierOptions } from '@seontechnologies/pactjs-utils'

const options = buildMessageVerifierOptions({
  provider: 'SampleMoviesAPI-event-producer',
  messageProviders,
  stateHandlers,
  includeMainAndDeployed: true
})

const messagePact = new MessageProviderPact(options)
await messagePact.verify()

Utilities

CategoryUtilitiesDocs
ConsumertoJsonMap, createProviderState, setJsonContent, setJsonBodyConsumer Helpers
ProviderbuildVerifierOptions, buildMessageVerifierOptions, handlePactBrokerUrlAndSelectors, getProviderVersionTagsProvider Verifier
AuthcreateRequestFilter, noOpRequestFilterRequest Filter

Pact Testing Types

TypeStatusCostLocalDirectory
Consumer-Driven (HTTP)ImplementedFreeYespact/http/
Message Queue (Kafka/async)ImplementedFreeYespact/message/
Bi-Directional (provider-driven)ImplementedPactFlow reqNoscripts/ (OAS)

Read more about each type and the testing flows in Concepts.

CI Workflows

Each workflow is a standalone file that can be copied to its own repo.

WorkflowRepoTriggerPurpose
contract-test-consumer.ymlConsumerPR + push to mainGenerate pacts, publish, can-i-deploy
contract-test-provider.ymlProviderPR + push to mainVerify HTTP pacts, can-i-deploy
contract-test-provider-message.ymlProviderPR + push to mainVerify message pacts, can-i-deploy
contract-test-webhook.ymlProviderPactFlow webhookVerify when consumer publishes new pact
contract-test-publish-openapi.ymlProviderPush to main + manualPublish OAS to PactFlow (BDCT)
contract-test-local.ymlMonorepoPR + manualLocal consumer + provider (no broker)

Released under the MIT License.