Configuration

Understanding TestFlowKit's YAML configuration system

Configuration

TestFlowKit uses YAML configuration files to define test environments, element selectors, and execution settings. This page covers all configuration options available.

Configuration File

By default, TestFlowKit looks for config.yml in the current directory. You can specify a different file:

tkit run --config my-config.yml

Basic Structure

# Global settings
settings:
  think_time: 1000
  concurrency: 1
  report_format: "html"
  gherkin_location: "features"
  env_file: ".env.yml"  # Optional: external env file

# Environment Variables
# Access these using {{ env.variable_name }} in Gherkin and config
env:
  base_url: "http://localhost:3000"
  jsonplaceholder_base_url: "http://localhost:3001"
  my_graphql_endpoint: "http://localhost:3001/graphql"

# Frontend testing configuration
frontend:
  base_url: "{{ env.base_url }}"
  driver: "rod"  # Browser driver: "rod" or "playwright"
  base_url: "{{ env.base_url }}"
  default_timeout: 1000
  headless: false
  screenshot_on_failure: true
  user_agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
  locale: "en-US"
  timezone_id: "America/New_York"
  pages:
    home: "/"
  elements:
    common:
      submit_button:
        - "#submit-btn"

# API testing configuration
apis:
  default_timeout: 30000
  definitions:
    jsonplaceholder:
      type: rest
      base_url: "{{ env.jsonplaceholder_base_url }}"
      default_headers:
        Content-Type: "application/json"
      endpoints:
        get_users:
          method: "GET"
          path: "/users"
          description: "Get all users"

Environment Variables

Environment variables allow you to define configuration values that can be accessed throughout your tests and configuration files using {{ env.variable_name }} syntax.

Inline Environment Variables

Define variables directly in your config file:

settings:
  concurrency: 1
  gherkin_location: "features"

# Inline environment variables
env:
  base_url: "http://localhost:3000"
  jsonplaceholder_base_url: "http://localhost:3001"
  my_graphql_endpoint: "http://localhost:3001/graphql"
  api_key: "your-api-key"
  
  # Nested variables (accessed with dot notation)
  database:
    host: "localhost"
    port: "5432"
    name: "testdb"

frontend:
  base_url: "{{ env.base_url }}"

External Environment Files

For better organization and environment-specific configurations, use external YAML files:

config.yml:

settings:
  env_file: ".env.local.yml"  # Default env file
  concurrency: 1

.env.local.yml:

base_url: "http://localhost:3000"
jsonplaceholder_base_url: "http://localhost:3001"
my_graphql_endpoint: "http://localhost:3001/graphql"
api_key: "local-dev-key"

database:
  host: "localhost"
  port: "5432"

.env.staging.yml:

base_url: "https://staging.example.com"
jsonplaceholder_base_url: "https://api-staging.example.com"
my_graphql_endpoint: "https://api-staging.example.com/graphql"
api_key: "staging-api-key"

database:
  host: "staging-db.example.com"
  port: "5432"

CLI Override

Override the env file at runtime:

# Use staging environment
tkit run --env-file .env.staging.yml

# Use production environment
tkit run --env-file .env.production.yml

Variable Priority

When the same variable is defined in multiple places, TestFlowKit uses this priority order:

  1. CLI --env-file (highest priority)
  2. Config settings.env_file
  3. Inline env block (lowest priority)

Using Environment Variables

Access environment variables using {{ env.variable_name }} syntax:

In Gherkin:

Given the user goes to "{{ env.base_url }}"
When I prepare a request to "jsonplaceholder.get_user"
And I set the header "Authorization" to "Bearer {{ env.api_key }}"

In Config:

apis:
  definitions:
    jsonplaceholder:
      type: rest
      base_url: "{{ env.jsonplaceholder_base_url }}"
      default_headers:
        Authorization: "Bearer {{ env.api_key }}"
      endpoints:
        get_users:
          method: "GET"
          path: "/users"
          description: "Get all users"

Nested Variables:

# Access nested variables with dot notation
Given I connect to database at "{{ env.database.host }}:{{ env.database.port }}"

Settings Configuration

Global test execution settings:

settings:
  # Delay between actions (ms) - useful for debugging
  think_time: 1000
  
  # Number of parallel test executions (1-20)
  concurrency: 1
  
  # Report output format: html, json, junit
  report_format: "html"
  
  # Location of Gherkin feature files
  gherkin_location: "./features"
  
  # Optional: Path to environment variables file
  env_file: ".env.yml"
  
  # Optional: Filter tests by tags
  tags: "@smoke"

Frontend Configuration

Configure browser-based testing:

frontend:
  # Base URL for the frontend application (required for page navigation)
  # Supports variable interpolation: "{{ env.base_url }}"
  base_url: "http://localhost:3000"
  # Browser driver to use (required)
  driver: "rod"  # Options: "rod" or "playwright"
  
  # Default timeout for element interactions (ms)
  default_timeout: 1000
  
  # Run without visible browser window
  headless: false
  
  # Capture screenshots when tests fail
  screenshot_on_failure: true
  
  # Delay between browser actions in milliseconds (simulate human behavior)
  think_time: 1000
  
  # Browser user agent string
  user_agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
  
  # Browser locale (e.g., "en-US", "fr-FR", "de-DE")
  locale: "en-US"
  
  # IANA timezone ID (e.g., "America/New_York", "Europe/Paris", "Asia/Tokyo")
  timezone_id: "America/New_York"
  
  # Page definitions
  pages:
    home: "/"
    login: "/login"
    dashboard: "/dashboard"
    settings: "/settings/profile"
  
  # Element selectors grouped by page or feature
  elements:
    common:
      login_button:
        - "#login-btn"
        - "[data-testid='login']"
        - "button.login"
    
    login_page:
      email_input:
        - "#email"
        - "[name='email']"
      password_input:
        - "#password"
        - "[name='password']"

Browser Configuration

Configure browser behavior for frontend testing:

frontend:
  # Base URL (required) - defines the application URL
  base_url: "http://localhost:3000"
  
  # Timeouts and delays
  default_timeout: 10000      # Element search timeout in milliseconds
  think_time: 1000            # Delay between actions (ms) - simulates human behavior
  
  # Browser mode
  headless: false             # Set to true to run without visible window
  screenshot_on_failure: true # Capture screenshots on test failures
  
  # User agent emulation - customize how the browser identifies itself
  user_agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
  
  # Locale settings - affects language and formatting
  locale: "en-US"             # Browser locale (e.g., "en-US", "fr-FR", "de-DE")
  
  # Timezone - sets the browser timezone
  timezone_id: "America/New_York"  # IANA timezone (e.g., "Europe/Paris", "Asia/Tokyo")

Browser Driver

The driver field specifies which browser automation driver to use. This field is required when the frontend block is defined.

Available drivers:

  • rod - Default driver using go-rod library with Chromium. Fast, stable, and lower resource consumption. No additional installation required.
  • playwright - Alternative driver using playwright-go library. Advanced features and modern web platform capabilities. Requires Go and Playwright installation (see below).
frontend:
  driver: "rod"  # or "playwright"

Choose rod for most use cases. Use playwright when you need advanced browser features or specific capabilities not available in Rod.

Playwright Installation: To use driver: "playwright", run the install command:

tkit install --config=config.yml

Requires Go 1.19+ and internet connection.

Browser Configuration

Configure browser behavior for frontend testing:

frontend:
  # Base URL (required) - defines the application URL
  base_url: "http://localhost:3000"
  
  # Timeouts and delays
  default_timeout: 10000      # Element search timeout in milliseconds
  think_time: 1000            # Delay between actions (ms) - simulates human behavior
  
  # Browser mode
  headless: false             # Set to true to run without visible window
  screenshot_on_failure: true # Capture screenshots on test failures
  
  # Browser driver selection
  driver: "rod"               # Choose driver: "rod" (default) or "playwright"
  
  # User agent emulation - customize how the browser identifies itself
  user_agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
  
  # Locale settings - affects language and formatting
  locale: "en-US"             # Browser locale (e.g., "en-US", "fr-FR", "de-DE")
  
  # Timezone - sets the browser timezone
  timezone_id: "America/New_York"  # IANA timezone (e.g., "Europe/Paris", "Asia/Tokyo")

Page Definitions

Define named pages to use in your tests:

frontend:
  pages:
    home: "/"
    login: "/auth/login"
    register: "/auth/register"
    dashboard: "/app/dashboard"
    profile: "/app/settings/profile"

Use in tests:

Given the user goes to the "login" page
# Navigates to https://example.com/auth/login

Element Selectors

Define elements with fallback selectors for robustness. Elements should be grouped by page or feature:

frontend:
  elements:
    # Common elements used across multiple pages
    common:
      header:
        - "#main-header"
      submit_button:
        - "#submit-btn"
        - "[data-testid='submit']"
        - "button[type='submit']"
        - "xpath://button[contains(text(), 'Submit')]"
      user_menu:
        - "[data-testid='user-menu']"
        - ".user-dropdown"
    
    # Login page specific elements
    login_page:
      email_input:
        - "#email"
        - "[name='email']"
        - "input[type='email']"
      password_input:
        - "#password"
        - "[name='password']"
      login_button:
        - "#login-btn"
        - "button[type='submit']"
    
    # Form page elements
    form_page:
      text_field:
        - "xpath://*[@id='text']"
        - "#text-input"
      checkbox:
        - "#checkbox"
        - "input[type='checkbox']"

💡 Selector Priority: TestFlowKit tries selectors in parallel and uses the first one that matches.

💡 Element Grouping: Group elements by their context for better maintainability. Use common for elements shared across pages, or create page-specific groups matching the page names defined in your pages configuration (e.g., if you have a page named login, create a login_page group for its elements).

APIs Configuration

TestFlowKit supports multiple REST and GraphQL APIs with a unified configuration structure. Each API can have its own base URL, headers, timeout, and endpoints/operations.

Structure

apis:
  # Default timeout for all APIs (optional, defaults to 30000ms)
  default_timeout: 30000
  
  # Named API definitions
  definitions:
    # REST API example
    jsonplaceholder:
      type: rest
      base_url: "{{ env.jsonplaceholder_base_url }}"
      timeout: 5000  # Optional: override default timeout
      default_headers:
        Content-Type: "application/json"
        Accept: "application/json"
        User-Agent: "TestFlowKit/1.0"
      endpoints:
        get_users:
          method: "GET"
          path: "/users"
          description: "Retrieve all users"
        
        get_user:
          method: "GET"
          path: "/users/{id}"
          description: "Retrieve a specific user by ID"
        
        create_user:
          method: "POST"
          path: "/users"
          description: "Create a new user"
    
    # GraphQL API example
    my_graphql:
      type: graphql
      endpoint: "{{ env.my_graphql_endpoint }}"
      default_headers:
        Content-Type: "application/json"
        Authorization: "Bearer {{ env.api_token }}"
      operations:
        getUser:
          type: "query"
          operation: |
            query GetUser($id: ID!) {
              user(id: $id) {
                id
                username
                email
              }
            }
          description: "Fetch user by ID"
        
        createPost:
          type: "mutation"
          operation: |
            mutation CreatePost($input: PostInput!) {
              createPost(input: $input) {
                id
                title
                content
              }
            }
          description: "Create a new post"

Using APIs in Tests

Reference APIs using the api_name.endpoint_name syntax:

# REST API
Given I prepare a request to "jsonplaceholder.get_user"
And I set the following path parameters:
  | id | 123 |
When I send the request
Then the response status code should be 200

# GraphQL API
Given I prepare a request to "my_graphql.getUser"
And I set the following GraphQL variables:
  | id | 123 |
When I send the request
Then the GraphQL response should not have errors

REST API Configuration

REST APIs require:

  • type: rest
  • base_url: Base URL for the API (supports environment variables)
  • endpoints: Map of named endpoints with method, path, and description

Optional:

  • default_headers: Headers applied to all requests
  • timeout: Override default timeout for this API

GraphQL API Configuration

GraphQL APIs require:

  • type: graphql
  • endpoint: Full GraphQL endpoint URL (supports environment variables)
  • operations: Map of named operations with type (query/mutation), operation definition, and description

Optional:

  • default_headers: Headers applied to all requests
  • timeout: Override default timeout for this API

Authentication with security_schemes

Use security_schemes to define reusable auth providers, then reference them from APIs or operations with security_ref.

Reference a scheme by name using the object form:

security_ref:
  name: enterprise_idp
security_schemes:
  enterprise_idp:
    type: oauth2
    token_url: "{{ env.auth_url }}"
    client_id: "{{ env.client_id }}"
    client_secret: "{{ env.client_secret }}"
    token_endpoint_auth_method: client_secret_post  # required for oauth2
    scopes: ["read", "write"]
    retry_on_401: true

  admin_key:
    type: apikey
    key: "{{ env.admin_key }}"
    placement: header
    header_name: "X-Api-Key"

default_security: "enterprise_idp"

apis:
  definitions:
    inventory_api:
      type: rest
      base_url: "{{ env.inventory_api_base_url }}"
      security_ref:
        name: enterprise_idp
      endpoints:
        list_items:
          method: GET
          path: "/items"

    admin_api:
      type: rest
      base_url: "{{ env.admin_api_base_url }}"
      security_ref:
        name: admin_key
      endpoints:
        health:
          method: GET
          path: "/health"

For OAuth2 schemes, token_endpoint_auth_method is mandatory and must be one of:

  • client_secret_post
  • client_secret_basic

When client_secret_basic is used, credentials are sent in the Authorization: Basic ... header and are not added to the token form body.

Supported Security Scheme Types

The following security mechanisms are implemented and ready to use:

1. Bearer

security_schemes:
  my_bearer:
    type: bearer
    token: "{{ env.api_token }}"

2. Basic

security_schemes:
  my_basic:
    type: basic
    username: "{{ env.api_user }}"
    password: "{{ env.api_pass }}"

3. API Key

security_schemes:
  my_apikey:
    type: apikey
    key: "{{ env.api_key }}"
    placement: header        # header | query | cookie
    header_name: "X-Api-Key"

4. OAuth2 (client credentials)

security_schemes:
  my_oauth2:
    type: oauth2
    token_url: "{{ env.auth_url }}"
    client_id: "{{ env.client_id }}"
    client_secret: "{{ env.client_secret }}"
    token_endpoint_auth_method: client_secret_post  # required
    scopes: ["read", "write"]
    retry_on_401: true

5. none sentinel (disable inherited security)

Use none to explicitly disable inherited authentication at API/endpoint/operation level:

apis:
  definitions:
    public_api:
      type: rest
      base_url: "{{ env.public_api_base_url }}"
      security_ref:
        name: none
      endpoints:
        health:
          method: GET
          path: "/health"

Where security_ref can be set

  • API level (apis.definitions.<api>.security_ref)
  • REST endpoint level (apis.definitions.<api>.endpoints.<endpoint>.security_ref)
  • GraphQL operation level (apis.definitions.<api>.operations.<operation>.security_ref)

oidc and certificate types are defined in configuration but currently not implemented at runtime. Using them will return a not-implemented provider error.

Path Parameters

Use {param} syntax for path parameters in REST endpoints:

endpoints:
  get_comment:
    method: GET
    path: "/posts/{postId}/comments/{commentId}"
    description: "Get a specific comment"

Set parameters in tests:

Given I prepare a request to "jsonplaceholder.get_comment"
And I set the following path parameters:
  | postId    | 123 |
  | commentId | 456 |
When I send the request

Header Merging

Headers are merged with this priority (highest to lowest):

  1. Request-specific headers (set in test steps)
  2. API default headers
  3. Global headers
# API default headers are automatically applied
Given I prepare a request to "jsonplaceholder.get_user"
# Additional headers override defaults
And I set the request header "Authorization" to "Bearer custom-token"
When I send the request

Files Configuration

Configure file paths for file upload testing:

files:
  # Base directory for test files
  base_directory: "./test-files"
  
  # Named file definitions
  definitions:
    avatar_image: "images/avatar.png"
    gallery_image1: "images/gallery/image1.jpg"
    gallery_image2: "images/gallery/image2.jpg"
    test_document: "documents/test.pdf"
    sample_csv: "data/sample.csv"

Use in tests:

When I upload the "avatar_image" file to the "avatar_field" field
And I upload the following files to the "gallery_field" field:
  | gallery_image1 |
  | gallery_image2 |

Switching Environments

Switch between environments using different environment files:

Using default env file in config:

settings:
  env_file: ".env.local.yml"  # Default environment

Override with CLI:

# Use staging environment
tkit run --env-file .env.staging.yml

# Use production environment
tkit run --env-file .env.production.yml

Or use inline env variables:

env:
  base_url: "http://localhost:3000"
  rest_api_base_url: "http://localhost:3001"
  graphql_endpoint: "http://localhost:3001/graphql"

frontend:
  base_url: "{{ env.base_url }}"

Complete Example

settings:
  think_time: 1000
  concurrency: 1
  report_format: "html"
  gherkin_location: "./features"
  tags: "@smoke"
  env_file: ".env.local.yml"  # Optional: default env file

# Inline environment variables (or use external files)
env:
  base_url: "http://localhost:3000"
  rest_api_base_url: "http://localhost:3001"
  graphql_endpoint: "http://localhost:3001/graphql"
  api_key: "test-api-key-123"
  auth_url: "http://localhost:3001/auth/token"
  client_id: "test-client-id"
  client_secret: "test-client-secret"

security_schemes:
  api_key_auth:
    type: apikey
    key: "{{ env.api_key }}"
    placement: header
    header_name: "X-Api-Key"
  
  bearer_auth:
    type: bearer
    token: "{{ env.api_key }}"
  
  oauth2_auth:
    type: oauth2
    token_url: "{{ env.auth_url }}"
    client_id: "{{ env.client_id }}"
    client_secret: "{{ env.client_secret }}"
    token_endpoint_auth_method: client_secret_post
    scopes: ["read", "write"]
    retry_on_401: true

default_security: "bearer_auth"

frontend:
  base_url: "{{ env.base_url }}"
  default_timeout: 1000
  headless: false
  screenshot_on_failure: true
  driver: "rod"
  user_agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
  locale: "en-US"
  timezone_id: "America/New_York"
  
  pages:
    home: "/"
    login: "/login"
    register: "/register"
    dashboard: "/dashboard"
    profile: "/settings/profile"
  
  elements:
    common:
      nav_home:
        - "[data-testid='nav-home']"
      nav_dashboard:
        - "[data-testid='nav-dashboard']"
      user_menu:
        - "[data-testid='user-menu']"
        - "#user-dropdown"
    
    login_page:
      email_input:
        - "#email"
        - "[name='email']"
        - "input[type='email']"
      password_input:
        - "#password"
        - "[name='password']"
      login_button:
        - "#login-btn"
        - "button[type='submit']"
    
    messages:
      error_message:
        - ".alert-error"
      success_message:
        - ".alert-success"

apis:
  default_timeout: 30000
  definitions:
    jsonplaceholder:
      type: rest
      base_url: "{{ env.rest_api_base_url }}"
      security_ref:
        name: "api_key_auth"
      default_headers:
        Content-Type: "application/json"
        Accept: "application/json"
      endpoints:
        get_users:
          method: GET
          path: "/users"
          description: "Get all users"
        get_user:
          method: GET
          path: "/users/{id}"
          description: "Get user by ID"
        create_post:
          method: POST
          path: "/posts"
          security_ref:
            name: "bearer_auth"
          description: "Create a new post"
    
    my_graphql:
      type: graphql
      endpoint: "{{ env.graphql_endpoint }}"
      security_ref:
        name: "oauth2_auth"
      default_headers:
        Content-Type: "application/json"
      operations:
        getUser:
          type: query
          operation: |
            query GetUser($id: ID!) {
              user(id: $id) {
                id
                username
                email
              }
            }
          description: "Fetch user by ID"
        createPost:
          type: mutation
          operation: |
            mutation CreatePost($title: String!, $body: String!) {
              createPost(title: $title, body: $body) {
                id
                title
                body
              }
            }
          description: "Create a new post"

files:
  base_directory: "./test-files"
  definitions:
    avatar_image: "images/avatar.png"
    test_document: "documents/test.pdf"
    sample_data: "data/sample.csv"

Next Steps