Quick Start Guide

Get up and running with Pilaf in 5 minutes.

Step 1: Create Your First Test

Create a file named my-first-test.pilaf.test.js:

const { describe, it, expect } = require('@jest/globals');
const { StoryRunner } = require('@pilaf/framework');

describe('My First Pilaf Test', () => {
  it('should execute a simple RCON command', async () => {
    const runner = new StoryRunner();

    const story = {
      name: 'Simple RCON Test',
      description: 'Test basic RCON command execution',

      setup: {
        server: { type: 'paper', version: '1.21.8' }
      },

      steps: [
        {
          name: 'Get server version',
          action: 'execute_command',
          command: 'version'
        }
      ],

      teardown: {
        stop_server: false
      }
    };

    const result = await runner.execute(story);
    expect(result.success).toBe(true);
  });
});

Step 2: Run the Test

# Run with Pilaf CLI
pilaf test my-first-test.pilaf.test.js

# Or use Jest directly
npx jest my-first-test.pilaf.test.js

Step 3: Create a Test with Players

Create player-test.pilaf.test.js:

const { describe, it, expect } = require('@jest/globals');
const { StoryRunner } = require('@pilaf/framework');

describe('Player Interaction Test', () => {
  it('should connect a player and execute commands', async () => {
    const runner = new StoryRunner();

    const story = {
      name: 'Player Commands Test',
      description: 'Test player interaction with server',

      setup: {
        server: { type: 'paper', version: '1.21.8' },
        players: [
          { name: 'Test Player', username: 'testplayer' }
        ]
      },

      steps: [
        {
          name: 'Make player operator',
          action: 'execute_command',
          command: 'op testplayer'
        },
        {
          name: 'Send chat message',
          action: 'chat',
          player: 'testplayer',
          message: 'Hello from Pilaf!'
        },
        {
          name: 'Wait for processing',
          action: 'wait',
          duration: 2
        }
      ],

      teardown: {
        stop_server: false
      }
    };

    const result = await runner.execute(story);
    expect(result.success).toBe(true);
  });
});

Step 4: Run with Variables

Create variable-test.pilaf.test.js:

const { describe, it, expect } = require('@jest/globals');
const { StoryRunner } = require('@pilaf/framework');

describe('Variable Storage Test', () => {
  it('should store and use variables between steps', async () => {
    const runner = new StoryRunner();

    const story = {
      name: 'Variable Test',
      description: 'Test variable storage and retrieval',

      setup: {
        server: { type: 'paper', version: '1.21.8' },
        players: [
          { name: 'Test Player', username: 'testplayer' }
        ]
      },

      steps: [
        {
          name: 'Get player location',
          action: 'get_player_location',
          player: 'testplayer',
          store_as: 'initial_position'
        },
        {
          name: 'Move player forward',
          action: 'move_forward',
          player: 'testplayer',
          duration: 2
        },
        {
          name: 'Get new position',
          action: 'get_player_location',
          player: 'testplayer',
          store_as: 'new_position'
        },
        {
          name: 'Calculate distance',
          action: 'calculate_distance',
          from: '{initial_position}',
          to: '{new_position}',
          store_as: 'distance_traveled'
        },
        {
          name: 'Verify movement',
          action: 'assert',
          condition: 'greater_than',
          actual: '{distance_traveled}',
          expected: 0
        }
      ],

      teardown: {
        stop_server: false
      }
    };

    const result = await runner.execute(story);
    expect(result.success).toBe(true);
  });
});

Step 5: Setting Up a Test Server (Optional)

For local development, you can use Docker to run a deterministic test server.

Create docker-compose.dev.yml:

version: '3.8'
services:
  minecraft:
    image: itzg/minecraft-server
    container_name: pilaf-minecraft-dev
    ports:
      - "${MC_PORT:-25566}:25565"
      - "${RCON_PORT:-25576}:25575"
    environment:
      EULA: 'TRUE'
      ONLINE_MODE: 'false'
      TYPE: 'PAPER'
      VERSION: '1.21.8'
      RCON_PASSWORD: '${RCON_PASSWORD:-cavarest}'
      ENABLE_RCON: 'true'
      RCON_PORT: '25575'
      MAX_PLAYERS: '5'
      MEMORY: '1G'
      SPAWN_PROTECTION: '0'
      WHITELIST: ''

      # === DETERMINISTIC FLAT WORLD FOR TESTING ===
      LEVEL: 'pilaf-test'
      LEVEL_TYPE: 'FLAT'
      SEED: '1234567890'
      GENERATE_STRUCTURES: 'false'
      MAX_WORLD_SIZE: '50'
      MODE: 'creative'
      DIFFICULTY: 'peaceful'
      PVP: 'false'
      ALLOW_NETHER: 'false'

      # === PERFORMANCE FOR TESTING ===
      VIEW_DISTANCE: '4'
      SIMULATION_DISTANCE: '4'

      # === ENTITY SPAWNING FOR TESTING ===
      SPAWN_ANIMALS: 'true'     # Enable for entity testing
      SPAWN_MONSTERS: 'false'   # Disable hostile mobs
      SPAWN_NPCS: 'false'

      # === CUSTOM FLAT WORLD LAYERS ===
      GENERATOR_SETTINGS: >-
        {
          "layers": [
            {"block": "minecraft:bedrock", "height": 1},
            {"block": "minecraft:dirt", "height": 2},
            {"block": "minecraft:grass_block", "height": 1}
          ],
          "biome": "minecraft:plains"
        }
    volumes:
      - mc-data:/data
    healthcheck:
      test: ["CMD", "mc-health"]
      interval: 30s
      timeout: 10s
      retries: 15
      start_period: 120s

volumes:
  mc-data:

Start the server:

# Start the server
docker-compose -f docker-compose.dev.yml up -d

# Run tests with the configured environment
RCON_HOST=localhost RCON_PORT=25576 RCON_PASSWORD=cavarest MC_HOST=localhost MC_PORT=25566 pilaf test

# View logs
docker-compose -f docker-compose.dev.yml logs -f

# Stop the server
docker-compose -f docker-compose.dev.yml down

# Clean world data for fresh tests
docker-compose -f docker-compose.dev.yml down -v

The test configuration uses:

  • Deterministic seed - Same world every run (seed: 1234567890)

  • Flat world - 4 layers: bedrock, 2 dirt, 1 grass block

  • Small world - 50 block radius for fast generation

  • Peaceful mode - No hostile mobs, creative mode for easy testing

  • Animals enabled - For entity spawn/persistence testing

  • Reduced view distance - Faster chunk loading

Step 6: Run Your Tests and Generate Reports

# Run all Pilaf tests
pilaf test

# Run specific test file
pilaf test player-test.pilaf.test.js

# Run with verbose output
pilaf test --verbose

# Generate HTML report (default: target/pilaf-reports/index.html)
pilaf test --report-html

# Set custom report output path
pilaf test --report-path ./my-report.html

Viewing the HTML Report

When you run tests with --report-html, Pilaf generates an interactive HTML report that shows:

  • Story Overview - Test name, pass/fail status, step count

  • Step Details - Each step with its executor (RCON, player, ASSERT)

  • Action/Response Pairs - Raw actions and responses:

    • → action (yellow) - The action performed

    • ← response (green) - The response received

  • Console Logs - Full log output for debugging

Open the report in your browser:

# Default location
open target/pilaf-reports/index.html

# Custom location
open ./my-report.html----

=== What's Next?

* link:../guides/testing-environment.html[Testing Environment Setup] - Configure Docker for deterministic tests
* link:../guides/writing-tests.html[Writing Tests Guide] - Learn advanced test patterns
* link:../guides/actions-reference.html[Actions Reference] - Complete list of available actions
* link:../guides/assertions.html[Assertions Guide] - Learn about assertion types
* link:architecture.adoc[Architecture Overview] - Understand Pilaf's internals
* link:../core/story-runner.html[StoryRunner Deep Dive] - Internal implementation details

Back to top

Copyright © 2025 Pilaf Contributors. Open source under the MIT license.

This site uses Just the Docs, a documentation theme for Jekyll.