docs/architecture.md

# Cucumber Architecture

This document provides an overview of the Cucumber implementation architecture, explaining the core components and how they interact.

## Core Components

```
Cucumber
  ├── Discovery (Feature/Step Finding)
  ├── Gherkin (Parser)
  ├── Expression (Parameter Matching)
  ├── Compiler (Test Generation)
  ├── Runtime (Step Execution)
  └── StepError (Error Reporting)
```

### Discovery System

The discovery system automatically finds and loads feature files and step definitions:

```elixir
Discovery.discover()
  ├── Scans for features (test/features/**/*.feature)
  ├── Loads step definitions (test/features/step_definitions/**/*.exs)
  └── Returns: %{features: [...], step_registry: %{...}}
```

### Gherkin Parser

The Gherkin parser is responsible for parsing `.feature` files into a structured format that can be executed. It handles the syntax of Gherkin, including:

- Feature declarations
- Scenario outlines
- Backgrounds
- Steps (Given, When, Then)
- Tables and doc strings
- Tags

The parser produces an Abstract Syntax Tree (AST) that represents the structure of the feature file.

```elixir
# Simplified representation of the Gherkin parser flow
Feature File (Text) → Lexer → Tokens → Parser → AST
```

### Expression Engine

The Expression engine is responsible for matching step text against step definitions. It supports:

- Regular expressions
- Cucumber expressions (a simplified syntax with parameter types)
- Parameter conversion (string to typed values)

```elixir
defmodule Cucumber.Expression do
  # Converts a cucumber expression into a regex and parameter converters
  def compile(pattern) do
    # Transforms {string}, {int}, etc. into regex patterns
    # Returns {regex, converters}
  end

  # Matches text against a compiled expression
  def match(text, {regex, converters}) do
    # Returns {:match, args} or :no_match
  end
end
```

### Compiler

The compiler generates ExUnit test modules from discovered features:

```elixir
Compiler.compile_features!()
  ├── For each feature file:
  │   ├── Generates a test module
  │   ├── Creates setup from Background
  │   ├── Creates test cases from Scenarios
  │   └── Adds appropriate tags
  └── Compiles modules into memory
```

### Runtime

The runtime executes steps during test runs:

```elixir
Runtime.execute_step(context, step, step_registry)
  ├── Finds matching step definition
  ├── Prepares context with args, datatables, docstrings
  ├── Executes step function
  └── Processes return value
```

### StepDefinition Macro

The StepDefinition module provides the DSL for defining steps:

```elixir
defmodule MySteps do
  use Cucumber.StepDefinition

  step "pattern", context do
    # implementation
  end
end
```

## Execution Flow

1. **Discovery Phase** (at compile time)
   - `Cucumber.compile_features!()` is called in test_helper.exs
   - Discovery system finds all features and step definitions
   - Step registry is built with pattern → module mappings

2. **Compilation Phase**
   - For each feature, a test module is generated
   - Background steps become setup blocks
   - Scenarios become test cases
   - Tags are added for filtering

3. **Execution Phase** (at runtime)
   - ExUnit runs the generated test modules
   - Each test executes its steps via Runtime
   - Context is passed between steps
   - Errors are reported with helpful messages

## Data Flow

```
Feature File → Parser → AST
                          ↓
Step Files → Discovery → Registry
                          ↓
                      Compiler → Test Modules
                                      ↓
                                  ExUnit → Results
```

## Key Design Decisions

### Auto-Discovery
- Features and steps are automatically discovered
- No need to explicitly wire features to test modules
- Follows Ruby Cucumber's convention-over-configuration approach

### ExUnit Integration
- Generated tests are standard ExUnit test modules
- Full support for ExUnit features (tags, setup, async)
- Works with existing test tooling

### Runtime Compilation
- Tests are generated at runtime when `mix test` runs
- Allows for dynamic test generation
- No generated files to manage

### Context Management
- ExUnit context is used directly
- Background steps modify context in setup
- Each step can read and modify context

## Error Handling

Cucumber provides enhanced error messages with rich context:

1. **Undefined Steps**: Shows the exact step text with clickable file:line references and suggests implementation
2. **Step Failures**: Displays comprehensive error information including:
   - The failing step text with proper formatting
   - Clickable file:line reference to the scenario location (e.g., `test/features/example.feature:25`)
   - Visual step execution history with ✓ for passed and ✗ for failed steps
   - Formatted assertion errors extracted from ExUnit
   - Properly indented HTML output for PhoenixTest errors
   - Full stack traces for debugging
3. **Duplicate Steps**: Detected at load time with file/line information

The StepError module handles all error formatting, ensuring consistent and helpful error messages throughout the framework.

## Extensibility

The architecture supports several extension points:

1. **Custom Parameter Types**: Add new parameter types to Expression
2. **Custom Formatters**: Create custom output formats
3. **Hooks**: Before/after scenario hooks (via ExUnit setup/teardown)
4. **Step Libraries**: Create reusable step definition modules