Skip to content

main.yml Specification

The main.yml file is the central workspace configuration manifest. It configures the required Testform version, state storage backend, external providers, artifact scopes, and custom metadata fields schema.

main.yml
testform:
required_version: ">= 1.0.0"
backend:
local:
path: "testform.state"
# 1. Provider Configuration (GitHub, Jira, GitLab, Local)
provider:
github:
owner: "my-organization"
repository: "my-application"
project_number: 12
token_env: "GITHUB_TOKEN"
# 2. Input Variables and Local Values
variable:
app_url:
type: string
description: "Base application URL"
default: "https://www.saucedemo.com"
environment:
type: string
default: "dev"
locals:
base_url: "${var.app_url}"
api_health: "${local.base_url}/health"
# 3. Test Resources Declaration (resource)
# Strictly allows: testcase, testrun, testplan (and global)
resource:
global:
scaffold:
directory: "tests"
fields:
- name: priority
source: "tag"
enum: ["@low", "@medium", "@high", "@critical"]
default: "@medium"
testcase:
match: "*.case.feature"
scaffold:
directory: "tests/features"
filename: "{slug}.case.feature"
fields:
- name: module
source: "step"
required: true
testrun:
match: "*.run.feature"
scaffold:
directory: "tests/runs"
filename: "{slug}.run.feature"
fields:
- name: environment
source: "step"
default: "qa"
testplan:
match: "*.plan.feature"
scaffold:
directory: "tests/plans"
filename: "{slug}.plan.feature"

Compilation and Configuration Processing Pipeline

Section titled “Compilation and Configuration Processing Pipeline”

Testform compiles and resolves all configuration layers via a sequential in-memory pipeline:

┌────────────────┐ ┌────────────────┐ ┌────────────────┐
│ main.yml │ │ variables.yml │ │ resource.yml │
└───────┬────────┘ └───────┬────────┘ └───────┬────────┘
│ │ │
▼ ▼ ▼
[PHASE 1: IN-MEMORY DISCOVERY & SYMBOL SCHEMA MERGE]
│
▼
[PHASE 2: VALUE INGESTION & PRECEDENCE (*.vars.yml, CLI)]
│
▼
[PHASE 3: STATIC TYPE & AST VALIDATION (enum, condition)]
│
▼
[PHASE 4: LOCALS & HCL FUNCTION EVALUATION]
│
▼
[PHASE 5: INJECTION INTO GHERKIN RESOURCES (*.case.feature) + DAG]
│
▼
[PHASE 6: RECONCILIATION DIFF AGAINST testform.state (plan / apply)]

Defines where the state ledger (testform.state) resides and how distributed mutex locks are acquired:

Type (type) Description Required Parameters
local Local file storage (intended for solo development and testing). path
oci Oracle Cloud Infrastructure Object Storage with ETag conditional locking. namespace, bucket, object_name, tenancy_id
s3 Amazon S3 with distributed mutex locking via Amazon DynamoDB. bucket, key, dynamodb_table, region
azure Azure Blob Storage with exclusive lease locking. storage_account, container, blob_name
gcs Google Cloud Storage with object generation preconditions. bucket, prefix

2. resource Block: Gherkin Resource Governance

Section titled “2. resource Block: Gherkin Resource Governance”

In Testform, the managed testing artifacts (testcase, testrun, testplan) are Resources, following Terraform architecture. To prevent misconfigurations, Testform strictly validates that only authorized resource types are declared: testcase, testrun, testplan (and global for common inherited rules).

To prevent main.yml from becoming bloated, Testform supports declaring the resource: block directly in main.yml or inside an independent modular file resource.yml (or resource.yaml).

Data Flow: From Gherkin to resource.ts and the Remote Provider

Section titled “Data Flow: From Gherkin to resource.ts and the Remote Provider”

The declared fields are not direct API attributes, but the quality contract for the Gherkin author. The resource.ts adapter maps and extracts those fields to feed the remote Provider:

Gherkin (.feature) resource.yml resource.ts Provider (GitHub/Jira)
┌──────────────────────┐ ┌────────────────────────┐ ┌───────────────────────┐ ┌──────────────────────┐
│ @high @tc-login-001 │ │ resource: │ │ resource.registry({ │ │ GitHub Project V2 │
│ Scenario: Login │ │ global: │ │ fields: [ │ │ Status: "Done" │
│ * field module = │ ──► │ fields: [module, │ ──► │ custom_fields: │ ──► │ Project: "HT 360" │
│ "auth" │ │ priority, │ │ s.custom.fields │ │ Module: "auth" │
│ │ │ Status] │ │ ] │ │ Priority: "high" │
└──────────────────────┘ └────────────────────────┘ └───────────────────────┘ └──────────────────────┘
resource.yml
resource:
# 1. Workspace-wide rules inherited by ALL resources
global:
scaffold:
filename: "{YYYYMMDD}_{HHmmss}"
fields:
- name: project
source: step
required: true
default: "Hirent Talent 360"
- name: assignees
source: step
required: true
default: "fromeroc9"
- name: priority
source: tag
enum: ["@low", "@medium", "@high"]
default: "@medium"
- name: Status
source: step
required: true
default: "Done"
# 2. testcase Resource (*.case.feature)
testcase:
match: "*.case.feature"
identity: "tc-*"
fields:
- name: module
source: step
required: true
- name: functional
source: step
required: true
# 3. testrun Resource (*.run.feature)
testrun:
match: "*.run.feature"
identity: "tr-*"
fields:
- name: startDate
source: step
- name: endDate
source: step
# 4. testplan Resource (*.plan.feature)
testplan:
match: "*.plan.feature"
identity: "tp-*"
fields: []

In Testform, custom fields are declared within scopes.<scope>.fields or scopes.global.fields. They are configured using the following core properties:

  • source (Extraction Origin): Instructs Testform where to read the field value from:
    • tag: Extracted from scenario or feature tags (e.g., @high -> field priority gets "@high").
    • step: Defined via a Gherkin step using the * field <name> = <value> directive (e.g., * field module = auth).
  • enum (Allowed Values): Closed list of valid values for the field (e.g., ["@low", "@medium", "@high"]).
  • required (Mandatory): Boolean flag requiring the scenario to provide a value (or have a default). If missing, testform validate raises a policy violation.
  • default (Default Value): Value automatically assigned when not explicitly provided in the scenario.

Testform supports remote issue trackers (such as GitHub and GitLab) as well as a completely offline local provider (local).

The local provider allows managing testcases, runs, and plans without relying on any external cloud service or network credentials. All simulated issues, comments, and project fields are saved locally into .testform/cache/data.json.

This is ideal for offline development, air-gapped environments, or solo verification:

# Direct scalar mode
provider: local

Or using dictionary syntax:

provider:
local: {}

When using provider: local, testform plan and testform apply assign local IDs prefixed with local:<number> (such as local:1, local:2), guaranteeing complete offline reproducibility.

Synchronizes test resources as issues and project items in GitHub Projects V2. Testform supports both human-readable project numbers (projectId: 12) and GraphQL node IDs (projectId: "PVT_kwDOB12345").

provider:
github:
owner: "my-organization"
repository: "my-application"
projectId: 12
token_env: "GITHUB_TOKEN"
Parameter Description Required
owner GitHub organization or username owning the repository. Yes
repository GitHub repository name. Yes
projectId Numeric or GraphQL project ID in GitHub Projects V2. No
token_env Environment variable name providing the personal access token (default GITHUB_TOKEN). No

Authentication tokens are securely injected via the GITHUB_TOKEN environment variable or the custom variable designated in token_env.

Synchronizes test resources with issues in GitLab projects:

provider:
gitlab:
projectId: 12345678
token_env: "GITLAB_TOKEN"

5. Input Variables (variable:) and Local Values (locals:)

Section titled “5. Input Variables (variable:) and Local Values (locals:)”

Testform allows parameterizing both Gherkin step texts and tag metadata through input variables and locally computed values:

  • variable:: Variable declaration with data type (string, number, boolean), description, allowed catalog enum: [...], validation: constraints, and default values.
  • locals:: Computed values and derived expressions evaluated via the sandboxed AST engine (${var.name}, ${local.name}, ${lower()}, ${format()}).

For further information on precedence hierarchies and type rules, refer to the Variables and Types Guide.