Files
tstest/ts_tapbundle
..

@git.zone/tstest/tapbundle

🧪 Core TAP testing framework with enhanced assertions and lifecycle hooks

Installation

# tapbundle is typically included as part of @git.zone/tstest
pnpm install --save-dev @git.zone/tstest

Overview

@git.zone/tstest/tapbundle is the core testing framework module that provides the TAP (Test Anything Protocol) implementation for tstest. It offers a comprehensive API for writing and organizing tests with support for lifecycle hooks, test suites, enhanced assertions with diff generation, and flexible test configuration.

Key Features

  • 🎯 TAP Protocol Compliant - Full TAP version 13 support
  • 🔍 Enhanced Assertions - Built on smartexpect with automatic diff generation
  • 🏗️ Test Suites - Organize tests with describe() blocks
  • 🔄 Lifecycle Hooks - beforeEach/afterEach at suite and global levels
  • 🏷️ Test Tagging - Filter tests by tags for selective execution
  • Parallel Testing - Run tests concurrently with testParallel()
  • 🔁 Automatic Retries - Configure retry logic for flaky tests
  • ⏱️ Timeout Control - Set timeouts at global, file, or test level
  • 🎨 Fluent API - Chain test configurations with builder pattern
  • 📊 Protocol Events - Real-time test execution events

Basic Usage

Simple Test File

import { tap, expect } from '@git.zone/tstest/tapbundle';

tap.test('should add numbers correctly', async () => {
  const result = 2 + 2;
  expect(result).toEqual(4);
});

export default tap.start();

Using Test Suites

import { tap, expect } from '@git.zone/tstest/tapbundle';

tap.describe('Calculator', () => {
  tap.beforeEach(async (tapTools) => {
    // Setup before each test in this suite
  });

  tap.test('should add', async () => {
    expect(2 + 2).toEqual(4);
  });

  tap.test('should subtract', async () => {
    expect(5 - 3).toEqual(2);
  });

  tap.afterEach(async (tapTools) => {
    // Cleanup after each test in this suite
  });
});

export default tap.start();

API Reference

Main Test Methods

tap.test(description, testFunction)

Define a standard test that runs sequentially.

tap.test('should validate user input', async () => {
  // test code
});

tap.testParallel(description, testFunction)

Define a test that runs in parallel with other parallel tests.

tap.testParallel('should fetch user data', async () => {
  // test code
});

tap.describe(description, suiteFunction)

Create a test suite to group related tests.

tap.describe('User Authentication', () => {
  tap.test('should login', async () => { });
  tap.test('should logout', async () => { });
});

Test Modes

Skip Tests

tap.skip.test('not ready yet', async () => {
  // This test will be skipped
});

Only Mode

tap.only.test('focus on this test', async () => {
  // Only tests marked with 'only' will run
});

Todo Tests

tap.todo.test('implement feature X');

Fluent Test Builder

Chain test configurations for expressive test definitions:

tap
  .tags('integration', 'database')
  .priority('high')
  .retry(3)
  .timeout(5000)
  .test('should handle database connection', async () => {
    // test with configured settings
  });

Lifecycle Hooks

Suite-Level Hooks

tap.describe('Database Tests', () => {
  tap.beforeEach(async (tapTools) => {
    // Runs before each test in this suite
  });

  tap.afterEach(async (tapTools) => {
    // Runs after each test in this suite
  });

  tap.test('test 1', async () => { });
  tap.test('test 2', async () => { });
});

Global Hooks

tap.settings({
  beforeAll: async () => {
    // Runs once before all tests
  },
  afterAll: async () => {
    // Runs once after all tests
  },
  beforeEach: async (testName) => {
    // Runs before every test
  },
  afterEach: async (testName, passed) => {
    // Runs after every test
  }
});

Global Settings

Configure test behavior at the file level:

tap.settings({
  timeout: 10000,              // Default timeout for all tests
  retries: 2,                  // Retry failed tests
  retryDelay: 1000,            // Delay between retries
  bail: false,                 // Stop on first failure
  suppressConsole: false,      // Hide console output
  verboseErrors: true,         // Show full stack traces
  showTestDuration: true,      // Display test durations
  maxConcurrency: 4,           // Max parallel tests
});

Enhanced Assertions

The expect function is an enhanced wrapper around @push.rocks/smartexpect that automatically generates diffs for failed assertions.

import { expect } from '@git.zone/tstest/tapbundle';

tap.test('should compare objects', async () => {
  const actual = { name: 'John', age: 30 };
  const expected = { name: 'John', age: 31 };

  // Will show a detailed diff of the differences
  expect(actual).toEqual(expected);
});

Available Assertions

// Equality
expect(value).toEqual(expected);
expect(value).toBe(expected);

// Truthiness
expect(value).toBeTruthy();
expect(value).toBeFalsy();

// Type checks
expect(value).toBeType('string');

// Strings
expect(string).toMatch(/pattern/);
expect(string).toContain('substring');

// Arrays
expect(array).toContain(item);

// Exceptions
expect(fn).toThrow();
expect(fn).toThrow('error message');

// Async
await expect(promise).toResolve();
await expect(promise).toReject();

Test Tagging and Filtering

Tag tests for selective execution:

// Define tests with tags
tap.tags('integration', 'slow').test('complex test', async () => {
  // test code
});

tap.tags('unit').test('fast test', async () => {
  // test code
});

Filter tests by setting the environment variable:

TSTEST_FILTER_TAGS=unit tstest test/mytest.node.ts

TapTools

Each test receives a tapTools instance with utilities:

tap.test('should have utilities', async (tapTools) => {
  // Mark test as skipped
  tapTools.markAsSkipped('reason');

  // Mark as todo
  tapTools.todo('not implemented');

  // Configure retries
  tapTools.retry(3);

  // Log test output
  tapTools.log('debug message');
});

Advanced Features

Pre-Tasks

Run setup tasks before any tests execute:

tap.preTask('setup database', async () => {
  // Runs before any tests
});

tap.test('first test', async () => {
  // Database is ready
});

Test Priority

Organize tests by priority level:

tap.priority('high').test('critical test', async () => { });
tap.priority('medium').test('normal test', async () => { });
tap.priority('low').test('optional test', async () => { });

Nested Suites

Create deeply nested test organization:

tap.describe('API', () => {
  tap.describe('Users', () => {
    tap.describe('GET /users', () => {
      tap.test('should return all users', async () => { });
    });
  });
});

Protocol Events

Access real-time test events for custom tooling:

import { setProtocolEmitter } from '@git.zone/tstest/tapbundle';

// Get access to protocol emitter for custom event handling
// Events: test:started, test:completed, assertion:failed, suite:started, suite:completed

Best Practices

  1. Always export tap.start() at the end of test files:

    export default tap.start();
    
  2. Use descriptive test names that explain what is being tested:

    tap.test('should return 404 when user does not exist', async () => { });
    
  3. Group related tests with describe() blocks:

    tap.describe('User validation', () => {
      // All user validation tests
    });
    
  4. Leverage lifecycle hooks to reduce duplication:

    tap.beforeEach(async () => {
      // Common setup
    });
    
  5. Tag tests appropriately for flexible test execution:

    tap.tags('integration', 'database').test('...', async () => { });
    

TypeScript Support

tapbundle is written in TypeScript and provides full type definitions. The Tap class accepts a generic type for shared context:

interface MyTestContext {
  db: DatabaseConnection;
  user: User;
}

const tap = new Tap<MyTestContext>();

tap.test('should use context', async (tapTools) => {
  // tapTools is typed with MyTestContext
});

This project is licensed under MIT.

© 2025 Task Venture Capital GmbH. All rights reserved.