tstest/readme.hints.md

5.8 KiB
Raw Blame History

Architecture Overview

Project Structure

This project integrates tstest with tapbundle through a modular architecture:

  1. tstest (/ts/) - The test runner that discovers and executes test files
  2. tapbundle (/ts_tapbundle/) - The TAP testing framework for writing tests
  3. tapbundle_node (/ts_tapbundle_node/) - Node.js-specific testing utilities

How Components Work Together

Test Execution Flow

  1. CLI Entry Point (cli.js <20> cli.ts.js <20> cli.child.ts)

    • The CLI uses tsx to run TypeScript files directly
    • Accepts glob patterns to find test files
    • Supports options like --verbose, --quiet, --web
  2. Test Discovery

    • tstest scans for test files matching the provided pattern
    • Defaults to test/**/*.ts when no pattern is specified
    • Supports both file and directory modes
  3. Test Runner

    • Each test file imports tap and expect from tapbundle
    • Tests are written using tap.test() with async functions
    • Browser tests are compiled with esbuild and run in Chromium via Puppeteer

Key Integration Points

  1. Import Structure

    • Test files import from local tapbundle: import { tap, expect } from '../../ts_tapbundle/index.js'
    • Node-specific tests also import from tapbundle_node: import { tapNodeTools } from '../../ts_tapbundle_node/index.js'
  2. WebHelpers

    • Browser tests can use webhelpers for DOM manipulation
    • webhelpers.html - Template literal for creating HTML strings
    • webhelpers.fixture - Creates DOM elements from HTML strings
    • Automatically detects browser environment and only enables in browser context
  3. Build System

    • Uses tsbuild tsfolders to compile TypeScript (invoked by pnpm build)
    • Maintains separate output directories: /dist_ts/, /dist_ts_tapbundle/, /dist_ts_tapbundle_node/, /dist_ts_tapbundle_protocol/
    • Compilation order is resolved automatically based on dependencies in tspublish.json files
    • Protocol imports use compiled dist directories:
      // In ts/tstest.classes.tap.parser.ts
      import { ProtocolParser } from '../dist_ts_tapbundle_protocol/index.js';
      
      // In ts_tapbundle/tapbundle.classes.tap.ts  
      import { ProtocolEmitter } from '../dist_ts_tapbundle_protocol/index.js';
      

Test Scripts

The package.json defines several test scripts:

  • test - Builds and runs all tests (tapbundle and tstest)
  • test:tapbundle - Runs tapbundle framework tests
  • test:tstest - Runs tstest's own tests
  • Both support :verbose variants for detailed output

Environment Detection

The framework automatically detects the runtime environment:

  • Node.js tests run directly via tsx
  • Browser tests are compiled and served via a local server
  • WebHelpers are only enabled in browser environment

This architecture allows for seamless testing across both Node.js and browser environments while maintaining a clean separation of concerns.

Logging System

Log File Naming (Fixed in v1.9.1)

When using the --logfile flag, tstest creates log files in .nogit/testlogs/. The log file naming was updated to preserve directory structure and prevent collisions:

  • Old behavior: test/tapbundle/test.ts.nogit/testlogs/test.log
  • New behavior: test/tapbundle/test.ts.nogit/testlogs/test__tapbundle__test.log

This fix ensures that test files with the same basename in different directories don't overwrite each other's logs. The implementation:

  1. Takes the relative path from the current working directory
  2. Replaces path separators (/) with double underscores (__)
  3. Removes the .ts extension
  4. Creates a flat filename that preserves the directory structure

Test Timing Display (Fixed in v1.9.2)

Fixed an issue where test timing was displayed incorrectly with duplicate values like:

  • Before: ✅ test name # time=133ms (0ms)
  • After: ✅ test name (133ms)

The issue was in the TAP parser regex which was greedily capturing the entire line including the TAP timing comment. Changed the regex from (.*) to (.*?) to make it non-greedy, properly separating the test name from the timing metadata.

Protocol Limitations and Improvements

Current TAP Protocol Issues

The current implementation uses standard TAP format with metadata in comments:

ok 1 - test name # time=123ms

This has several limitations:

  1. Delimiter Conflict: Test descriptions containing # can break parsing
  2. Regex Fragility: Complex regex patterns that are hard to maintain
  3. Limited Metadata: Difficult to add rich error information or custom data

Planned Protocol V2

A new internal protocol is being designed that will:

  • Use Unicode delimiters ⟦TSTEST:⟧ that won't conflict with test content
  • Support structured JSON metadata
  • Allow rich error reporting with stack traces and diffs
  • Completely replace v1 protocol (no backwards compatibility)

ts_tapbundle_protocol Directory

The protocol v2 implementation is contained in a separate ts_tapbundle_protocol directory:

  • Isomorphic Code: All protocol code works in both browser and Node.js environments
  • No Platform Dependencies: No Node.js-specific imports, ensuring true cross-platform compatibility
  • Clean Separation: Protocol logic is isolated from platform-specific code in tstest and tapbundle
  • Shared Implementation: Both tstest (parser) and tapbundle (emitter) use the same protocol classes
  • Build Process:
    • Compiled by pnpm build via tsbuild to dist_ts_tapbundle_protocol/
    • Build order managed through tspublish.json files
    • Other modules import from the compiled dist directory, not source

This architectural decision ensures the protocol can be used in any JavaScript environment without modification and maintains proper build dependencies.

See readme.protocol.md for the full specification and ts_tapbundle_protocol/ for the implementation.