390 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			390 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # @git.zone/tstest/tapbundle
 | |
| 
 | |
| > 🧪 Core TAP testing framework with enhanced assertions and lifecycle hooks
 | |
| 
 | |
| ## Installation
 | |
| 
 | |
| ```bash
 | |
| # 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
 | |
| 
 | |
| ```typescript
 | |
| 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
 | |
| 
 | |
| ```typescript
 | |
| 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.
 | |
| 
 | |
| ```typescript
 | |
| tap.test('should validate user input', async () => {
 | |
|   // test code
 | |
| });
 | |
| ```
 | |
| 
 | |
| #### `tap.testParallel(description, testFunction)`
 | |
| 
 | |
| Define a test that runs in parallel with other parallel tests.
 | |
| 
 | |
| ```typescript
 | |
| tap.testParallel('should fetch user data', async () => {
 | |
|   // test code
 | |
| });
 | |
| ```
 | |
| 
 | |
| #### `tap.describe(description, suiteFunction)`
 | |
| 
 | |
| Create a test suite to group related tests.
 | |
| 
 | |
| ```typescript
 | |
| tap.describe('User Authentication', () => {
 | |
|   tap.test('should login', async () => { });
 | |
|   tap.test('should logout', async () => { });
 | |
| });
 | |
| ```
 | |
| 
 | |
| ### Test Modes
 | |
| 
 | |
| #### Skip Tests
 | |
| 
 | |
| ```typescript
 | |
| tap.skip.test('not ready yet', async () => {
 | |
|   // This test will be skipped
 | |
| });
 | |
| ```
 | |
| 
 | |
| #### Only Mode
 | |
| 
 | |
| ```typescript
 | |
| tap.only.test('focus on this test', async () => {
 | |
|   // Only tests marked with 'only' will run
 | |
| });
 | |
| ```
 | |
| 
 | |
| #### Todo Tests
 | |
| 
 | |
| ```typescript
 | |
| tap.todo.test('implement feature X');
 | |
| ```
 | |
| 
 | |
| ### Fluent Test Builder
 | |
| 
 | |
| Chain test configurations for expressive test definitions:
 | |
| 
 | |
| ```typescript
 | |
| 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
 | |
| 
 | |
| ```typescript
 | |
| 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
 | |
| 
 | |
| ```typescript
 | |
| 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:
 | |
| 
 | |
| ```typescript
 | |
| 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](https://code.foss.global/push.rocks/smartexpect) that automatically generates diffs for failed assertions.
 | |
| 
 | |
| ```typescript
 | |
| 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
 | |
| 
 | |
| ```typescript
 | |
| // 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:
 | |
| 
 | |
| ```typescript
 | |
| // 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:
 | |
| 
 | |
| ```bash
 | |
| TSTEST_FILTER_TAGS=unit tstest test/mytest.node.ts
 | |
| ```
 | |
| 
 | |
| ### TapTools
 | |
| 
 | |
| Each test receives a `tapTools` instance with utilities:
 | |
| 
 | |
| ```typescript
 | |
| 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:
 | |
| 
 | |
| ```typescript
 | |
| tap.preTask('setup database', async () => {
 | |
|   // Runs before any tests
 | |
| });
 | |
| 
 | |
| tap.test('first test', async () => {
 | |
|   // Database is ready
 | |
| });
 | |
| ```
 | |
| 
 | |
| ### Test Priority
 | |
| 
 | |
| Organize tests by priority level:
 | |
| 
 | |
| ```typescript
 | |
| 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:
 | |
| 
 | |
| ```typescript
 | |
| 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:
 | |
| 
 | |
| ```typescript
 | |
| 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:
 | |
|    ```typescript
 | |
|    export default tap.start();
 | |
|    ```
 | |
| 
 | |
| 2. **Use descriptive test names** that explain what is being tested:
 | |
|    ```typescript
 | |
|    tap.test('should return 404 when user does not exist', async () => { });
 | |
|    ```
 | |
| 
 | |
| 3. **Group related tests** with `describe()` blocks:
 | |
|    ```typescript
 | |
|    tap.describe('User validation', () => {
 | |
|      // All user validation tests
 | |
|    });
 | |
|    ```
 | |
| 
 | |
| 4. **Leverage lifecycle hooks** to reduce duplication:
 | |
|    ```typescript
 | |
|    tap.beforeEach(async () => {
 | |
|      // Common setup
 | |
|    });
 | |
|    ```
 | |
| 
 | |
| 5. **Tag tests appropriately** for flexible test execution:
 | |
|    ```typescript
 | |
|    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:
 | |
| 
 | |
| ```typescript
 | |
| interface MyTestContext {
 | |
|   db: DatabaseConnection;
 | |
|   user: User;
 | |
| }
 | |
| 
 | |
| const tap = new Tap<MyTestContext>();
 | |
| 
 | |
| tap.test('should use context', async (tapTools) => {
 | |
|   // tapTools is typed with MyTestContext
 | |
| });
 | |
| ```
 | |
| 
 | |
| ## Legal
 | |
| 
 | |
| This project is licensed under MIT.
 | |
| 
 | |
| © 2025 Task Venture Capital GmbH. All rights reserved.
 |