References ($ref)

Share and reuse items, variables, and UI action steps across test files.

Basic usage

Items can reference shared fragments using $ref with a relative path:

{
  "items": [
    { "$ref": "../shared/api-gateway.json" },
    { "$ref": "../shared/postgres-db.json" }
  ]
}

The path is relative to the file containing the $ref. At resolution time, Dokkimi replaces the $ref with the full contents of the referenced file.

Overriding fields

Additional fields are shallow-merged as overrides on top of the fragment:

{
  "items": [
    {
      "$ref": "../shared/postgres.json",
      "initFilePath": "../init-files/users-seed.sql"
    }
  ]
}

The fragment's fields are used as the base, and inline fields override them.

Don't override name on a $ref unless you intentionally want a second instance with a different name. The fragment already has a name — overriding it creates a differently-named copy.

Multi-ref

$ref can also be an array of paths. Fragments are resolved and merged left-to-right, with later files overriding earlier ones. Inline fields still win over all fragments:

{
  "items": [
    {
      "$ref": ["../shared/base-service.json", "../shared/staging-overrides.json"],
      "env": ["...$ref.env", { "name": "EXTRA", "value": "inline-wins" }]
    }
  ]
}

Array spreading

To extend an array field from the fragment instead of replacing it, use "...$ref.<path>" as an element in the override array:

{
  "$ref": "../shared/my-service.json",
  "env": ["...$ref.env", { "name": "EXTRA_VAR", "value": "123" }]
}

The "...$ref.env" marker is replaced with the fragment's env array. Position controls order — entries before the marker are prepended, entries after are appended. If the path doesn't resolve to an array, the marker expands to nothing.

The path supports dot notation for nested fields (e.g., "...$ref.some.nested.array").

$ref for variables

Both definition-level and test-level variables support $ref to load values from shared files:

{
  "variables": {
    "$ref": "../shared/db-vars.json",
    "localOverride": "my-value"
  }
}

Multi-ref also works for variables:

{
  "variables": {
    "$ref": ["../shared/db-vars.json", "../shared/test-users.json"],
    "extraVar": "inline-wins"
  }
}

Referenced files must be plain { "key": "value" } objects (no nested objects). Variable files can themselves use $ref to load from another variables file — recursive resolution with circular reference detection.

$ref for actions

Step actions support $ref to load reusable action definitions from shared fragments. This works for all action types — httpRequest, dbQuery, ui, and wait.

The fragment file must have an action field, and can include optional name and description metadata:

# shared/create-user.yaml
name: Create user
description: POST to create a new user
action:
  type: httpRequest
  method: POST
  url: api-gateway/api/users
  headers:
    Content-Type: application/json
  body:
    name: "{{userName}}"

Reference it on a step's action with $ref. Inline fields are shallow-merged as overrides:

- name: Create a user
  action:
    $ref: ../shared/create-user.yaml
    body:
      name: custom-name
  assertions:
    - assertions:
        - path: response.statusCode
          operator: eq
          value: 201

UI sub-step $ref

UI actions also support $ref inside the steps array to splice in reusable sub-step sequences (e.g., a login flow). The fragment has a steps array instead of action:

# shared/login-flow.yaml
name: OAuth login flow
description: Signs in via Google OAuth mock
steps:
  - visit: /login
  - click: "[data-testid='login-btn']"
  - waitFor: "[data-testid='dashboard']"

Reference it inside a UI action's steps array:

action:
  type: ui
  target: my-app
  steps:
    - $ref: ../shared/login-flow.yaml
    - visit: /settings
    - click: "[data-testid='save']"

The $ref entry is replaced by the fragment's steps array, spliced into position. Multiple $ref entries can appear in the same steps array.

Recursive refs

Fragments can themselves use $ref to build on other fragments. This lets you create layered definitions — a base service with common settings, then variants that override a few fields:

# shared/base-service.yaml
type: SERVICE
port: 3000
healthCheck: /health
env:
  - name: NODE_ENV
    value: production
# shared/api-gateway.yaml
$ref: ./base-service.yaml
name: api-gateway
image: my-registry/api-gateway:latest
env:
  - ...$ref.env
  - name: DATABASE_URL
    value: postgresql://dokkimi:dokkimi@postgres-db:5432/dokkimi

At resolution time, Dokkimi walks the chain: base-service.yaml is loaded first, then api-gateway.yaml overrides on top, then any inline overrides in the test file win last. Each level is a shallow merge, same as single-level $ref.

This also works for action refs, UI sub-step refs, and variable refs. Circular references are detected and reported as validation errors. Chains are limited to 10 levels deep.

Rules