Home Doh Ref
Guides β–Ό
Dohballs β–Ό
  • πŸ“ doh_chat
  • πŸ“ doh_modules
    • πŸ“¦ dataforge
    • πŸ“¦ express
    • πŸ“ sso
    • πŸ“ user

Doh.js Test Framework

A comprehensive testing framework for the Doh.js ecosystem, featuring a test harness with rich assertions, a visual test launcher UI, automated test discovery, and full-stack testing with both server and browser components.

Overview

The test framework consists of four main components:

  1. Tests Harness (tests_harness): A robust assertion library with comprehensive testing utilities
  2. Test Launcher (tests_launcher): A visual UI for discovering and running tests in isolation
  3. Test Routes (tests_routes): Express routes for web-based test access with auto-discovery
  4. CLI Test Runner: Command-line interface for running tests from the terminal (doh test)

Quick Start

Running Tests via CLI

Run tests directly from the command line without starting a server:

# Run a single test module
doh test test_use_state

# Run multiple test modules
doh test test_use_state test_deep_observe test_use_array_advanced

# Show usage help
doh test

Output:

==================================================
Running: test_use_state
==================================================

=== USE State Test Suite ===
βœ” PASS: State object exists
βœ” PASS: Can set and get values
...

=== USE State Test Summary ===
Total: 15, Passed: 15, Failed: 0

==================================================
TEST SUMMARY
==================================================
Total: 15 | Passed: 15 | Failed: 0

Exit Codes:

  • 0 - All tests passed
  • 1 - One or more tests failed (CI-friendly)

Note: Some tests require browser APIs (like indexedDB) and will show errors when run via CLI. Use the Web UI for browser-only tests.

Running Tests via Web UI

  1. Start your Doh.js server
  2. Navigate to /tests in your browser
  3. Click any test module from the list to run it in isolation
  4. View real-time console output and results

Running Tests Programmatically

// Load and use the test harness
Doh.Module('my_test', ['tests_harness'], function(tests_harness) {
  const { expect, expectEqual, summarizeTests, resetTests } = tests_harness;

  resetTests();

  // Your tests here
  expect(true, 'Basic truth test');
  expectEqual(2 + 2, 4, 'Math works');

  // Summary with pass/fail counts
  summarizeTests('My Test Suite');
});

Full-Stack Testing

The test framework supports full-stack testing with both server-side and browser-side test modules in a single test package. This enables testing of complete features including API routes, server logic, and browser interactions.

Creating Full-Stack Tests

Use test packages (YAML files) to combine server and browser test modules:

# test_my_feature.doh.yaml
test_my_feature:
  load:
    - nodejs?? test_my_feature_server   # Runs on server first
    - test_my_feature_browser           # Runs in browser after server

Naming Conventions (Required)

For full-stack testing to work correctly via packages, you must follow these conventions:

  1. Server test modules:

    • βœ… MUST use nodejs?? condition in package load array
    • βœ… MUST have _server suffix in module name
    • Example: nodejs?? test_my_feature_server
  2. Browser test modules:

    • βœ… MUST NOT have nodejs?? condition
    • βœ… MUST have _browser suffix in module name
    • Example: test_my_feature_browser

The test framework uses these conventions to determine execution order and environment. Server modules execute first in the Node.js process, while browser modules execute second in the browser iframe.

Execution Flow

  1. Server tests execute first (nodejs?? modules)

    • Run in the live server process
    • Can create routes, modify server state
    • Console output captured and displayed
  2. Browser tests execute second (regular modules)

    • Run in iframe after server tests complete
    • Can call server routes created by server tests
    • Full DOM access and browser APIs
  3. Results combined in a single test run with unified console output

Example: Server Creates Route, Browser Tests It

Server Test (test_server_example_server.doh.js):

Doh.Module('test_server_example_server',
  ['tests_harness', 'express_router'],
  async function(tests_harness, Router) {

  const { expect, summarizeTests, resetTests } = tests_harness;
  resetTests();

  // Server-side tests
  expect(typeof process !== 'undefined', 'Node.js process exists');

  // Create API route for browser to test
  Router.AddRoute('/test-api/hello', async function(data, req, res) {
    Router.SendJSON(res, {
      success: true,
      message: 'Hello from server!',
      data: { value: 42 }
    });
  });

  expect(true, 'API route created');

  summarizeTests('test_server_example_server');
});

Browser Test (test_server_example_browser.doh.js):

Doh.Module('test_server_example_browser',
  ['tests_harness'],
  async function(tests_harness) {

  const { expect, summarizeTests, resetTests } = tests_harness;
  resetTests();

  // Browser-side tests
  expect(typeof document !== 'undefined', 'DOM is available');

  // Test the route created by server test
  const response = await fetch('/test-api/hello');
  const data = await response.json();

  expect(response.ok, 'Server route responds');
  expect(data.success === true, 'Response has success=true');
  expect(data.data.value === 42, 'Response data is correct');

  summarizeTests('test_server_example_browser');
});

Package File (test_server_example.doh.yaml):

test_server_example:
  load:
    - nodejs?? test_server_example_server
    - test_server_example_browser

Server Test Capabilities

Server tests can:

  • βœ… Access Node.js APIs (process, fs, etc.)
  • βœ… Create Express routes via express_router
  • βœ… Modify server state and configuration
  • βœ… Load server-only modules
  • βœ… Test database connections and operations
  • βœ… Validate server-side business logic

Browser Test Capabilities

Browser tests can:

  • βœ… Access DOM and browser APIs
  • βœ… Make HTTP requests to server routes
  • βœ… Test UI components and interactions
  • βœ… Validate server responses
  • βœ… Test full request/response cycles
  • βœ… Verify integration between client and server

Full-Stack Test Best Practices

  1. Server tests should be idempotent when possible
  2. Create cleanup routes if tests modify persistent state
  3. Use meaningful route prefixes like /test-api/ for test routes
  4. Test both success and error cases across the stack
  5. Browser tests should validate what server tests set up
  6. Consider test purity - will this affect other tests?

Environment Detection

The test harness automatically detects the environment:

// Server-safe - only runs in browser
if (IsBrowser()) {
  // Browser-specific code
}

// Only runs on server
if (IsNode()) {
  // Server-specific code
}

This is used internally to prevent postMessage calls on the server and other environment-specific behaviors.

Test Harness API (tests_harness)

Core Assertions

  • expect(condition, description) - Basic assertion with pass/fail logging
  • expectEqual(actual, expected, description) - Deep equality using JSON comparison
  • expectArrayEqual(actual, expected, description) - Array comparison with USE system proxy support

Comparison Assertions

  • expectGreater(actual, expected, description) - Greater than comparison
  • expectAtLeast(actual, expected, description) - Greater than or equal comparison
  • expectLess(actual, expected, description) - Less than comparison
  • expectAtMost(actual, expected, description) - Less than or equal comparison

Value Testing

  • expectTruthy(value, description) - Test for truthiness
  • expectFalsy(value, description) - Test for falsiness
  • expectType(value, expectedType, description) - Type checking with array detection

Exception Testing

  • expectThrows(fn, description) - Verify function throws an exception
  • expectNotThrows(fn, description) - Verify function doesn't throw

Test Management

  • summarizeTests(testName) - Print summary and return success boolean
  • resetTests() - Reset test counters for new test run
  • getTestStats() - Get current test statistics object
  • setTestTimeout(timeoutMs) - Set custom timeout for long-running tests (default: 15000ms)
  • startTestHeartbeat(testName) - Start heartbeat monitoring (auto-called)
  • stopTestHeartbeat() - Stop heartbeat monitoring (auto-called)

Debug Utilities

  • enableDebug(enable) - Enable/disable debug output
  • debugLog(...args) - Conditional debug logging

Async Utilities

  • waitForNotifications() - Wait for microtasks/promises
  • waitFor(ms) - Wait for specific duration

USE System Utilities

  • getCleanState() - Get clean USE state with all keys removed

Timeout Management

Tests run with a default 15-second timeout. If a test doesn't send heartbeat signals within this time, it's marked as failed. For long-running tests, use setTestTimeout():

Doh.Module('test_long_operation', ['tests_harness'], async function(tests_harness) {
  const { expect, resetTests, summarizeTests, setTestTimeout } = tests_harness;
  resetTests();

  // Set a 30-second timeout for this test
  setTestTimeout(30000);

  // Run your long operation...
  await someLongRunningOperation();

  return summarizeTests('Long Operation Test');
});

Timeout Features:

  • Visual countdown timer showing time remaining until timeout
  • Custom timeout value displayed when different from default
  • Color-coded warnings (red in last 3 seconds)
  • Automatic heartbeat every 2 seconds during test execution

Test Discovery & Execution

The test launcher automatically discovers test modules using these criteria:

  • Module names starting with test_ or debug_
  • Located in the doh_modules/tests/ directory
  • Registered in Doh.Packages by the Auto-Packager

Test Module Structure

Test modules should follow this pattern:

Doh.Module('test_my_feature', ['tests_harness'], async function(tests_harness) {
  const { expect, expectEqual, summarizeTests, resetTests } = tests_harness;

  console.log('\n=== My Feature Test Suite ===');
  resetTests();

  // Test implementation here
  expect(someCondition, 'Feature works as expected');
  expectEqual(actualValue, expectedValue, 'Values match');

  // Optional: Add async test logic
  await tests_harness.waitFor(100);

  // Always summarize at the end
  const success = summarizeTests('My Feature Tests');
  return success;
});

Visual Test Launcher

The test launcher provides a modern, comprehensive testing interface with real-time feedback and batch execution capabilities.

Key Features

  • Auto-discovery: Automatically finds all test modules in doh_modules/tests/
  • Folder Organization: Tests grouped by subdirectory with collapsible sections
  • Batch Execution: Run all tests in a folder or globally with progress tracking
  • Isolated Execution: Each test runs in its own iframe sandbox
  • Real-time Monitoring: Live console output, heartbeat tracking, and timeout countdown
  • Responsive UI: Modern dark theme with gradient accents and smooth animations

Test Status Indicators

Tests display color-coded status badges:

  • PENDING (gray) - Not yet run
  • RUNNING (blue, animated) - Currently executing with spinner
  • PASSED (green) - All assertions succeeded
  • FAILED (red) - One or more assertions failed
  • ERROR (dark red) - Test timed out or crashed

Interactive Features

Individual Test Execution:

  • Click any test to run it in isolation
  • Real-time console output with timestamps
  • Timeout countdown timer (shows custom timeouts)
  • Pop-out button (β†—) to open test in new tab

Batch Test Execution:

  • "Run All Tests" button executes entire suite sequentially
  • "Run All" per folder executes tests in that group
  • Progress bar shows completion percentage
  • Batch summary displays results after completion
  • Failed test details with console errors/warnings

Global Statistics:

  • Total tests run
  • Pass/fail counts
  • Success rate percentage
  • Live updates during batch runs

Console Output

Real-time console forwarding with:

  • Color-coded log levels:
    • log - Light gray (#d4d4d4)
    • info - Cyan (#00d4aa)
    • warn - Yellow (#ffcc02)
    • error - Red (#f85149)
    • debug - Gray (#8b949e)
  • Timestamps on all messages
  • Auto-scroll to latest output
  • Captured errors and warnings attached to failed tests

Timeout Monitoring

Visual countdown timer displays:

  • Time remaining until timeout (updates every second)
  • Custom timeout value when different from 15s default
  • Color changes to red in last 3 seconds
  • Format: "Custom Timeout: 30s | Time until timeout: 27s"

Batch Run Summary

After batch execution, a detailed summary shows:

  • Total tests, passed, failed counts
  • Execution duration
  • List of failed tests with:
    • Test name and file path
    • Error messages from test framework
    • Captured console errors with timestamps
    • Captured console warnings with timestamps
  • Copy button to export failure details

Test Routes

Express routes are automatically created for all test modules:

  • /tests - Main test launcher interface with folder organization
  • /test/{module_name} - Direct access to individual test execution
  • Auto-discovery - Automatically creates routes for all test_* and debug_* modules
  • Iframe support - Tests can run in iframe mode with ?iframe=true parameter
  • Debug mode - Pass ?debug=true to enable verbose debugging

Dynamic Route Generation

The framework automatically creates routes for every test module found in Doh.Packages:

// Automatically created routes:
/test/test_use_array_advanced
/test/test_use_state_simple
/test/test_undo_redo_system
// ... etc for all test modules

Example Tests

The framework includes comprehensive example tests in doh_modules/tests/use/:

USE System Tests

test_use_state_simple.doh.js

  • Basic USE state pattern testing
  • Array manipulation and DOM synchronization
  • Item creation, modification, and removal

test_use_state_pattern.doh.js

  • Pattern-based state management
  • USE state pattern integration
  • Dynamic instance creation

test_use_array_advanced.doh.js (117 assertions)

  • Array operations (push, pop, shift, unshift, splice)
  • Functional methods (map, filter, sort, reverse)
  • Index assignment and deletion
  • Observer notifications
  • Edge cases and type checking
  • Object arrays without IDs
  • Nested arrays and complex hierarchies

test_use_array_safe.doh.js

  • Boundary condition testing
  • Edge case validation
  • Error handling verification

Snapshot & Timeline Tests

test_undo_redo_system.doh.js

  • Document undo/redo with timeline
  • User action tracking
  • Conditional autosave system
  • Timeline navigation (undo/redo)
  • Custom timeout (30s) for async operations

test_use_snapshot.doh.js

  • AND/OR logic testing
  • Custom trigger function validation
  • Complex business logic scenarios
  • State persistence verification

Deep Observation Tests

test_deep_observation.doh.js

  • Nested object property watching
  • Path bubbling validation
  • Observer callback verification
  • Multi-level nesting support

Full-Stack Example Tests

example/test_server_example (Package)

  • Server-side route creation with express_router
  • Browser-side API testing with fetch
  • Server tests run first, create /test-api/hello route
  • Browser tests call the route and validate response
  • Demonstrates complete serverβ†’browser integration flow
  • 5 server assertions + 10 browser assertions

Best Practices

Required Test Structure

Every test module must follow this structure to work properly with the test launcher:

Doh.Module('test_my_feature', ['tests_harness'], async function(tests_harness) {
  // 1. Import harness functions (REQUIRED)
  const { expect, expectEqual, resetTests, summarizeTests } = tests_harness;

  // 2. Reset test counters (REQUIRED)
  resetTests();

  // 3. Optional: Set custom timeout for long tests
  // setTestTimeout(30000); // 30 seconds

  // 4. Run your tests - call expect/expectEqual directly
  expect(true, 'Basic truth test');
  expectEqual(2 + 2, 4, 'Math works');

  // 5. Summarize and return result (REQUIRED)
  return summarizeTests('My Feature Tests');
});

Critical Requirements

  1. βœ… Always call resetTests() at the start - initializes harness tracking
  2. βœ… Always call summarizeTests() at the end - sends completion message to launcher
  3. βœ… Always return the result from summarizeTests() - enables success tracking
  4. βœ… Call assertion functions directly - don't wrap them in custom functions
  5. βœ… Import resetTests and summarizeTests - both are required

Common Mistakes to Avoid

❌ Don't skip resetTests():

// BAD - Test counters not initialized
const { expect } = tests_harness;
expect(true, 'Test'); // Harness not ready!

❌ Don't skip summarizeTests():

// BAD - Test never completes, times out after 15 seconds
expect(true, 'Test');
// Missing: return summarizeTests('Test');

❌ Don't create custom test wrappers:

// BAD - Custom tracking instead of harness
let passed = 0, failed = 0;
function runTest(fn) { /* custom logic */ }
runTest(() => expect(true, 'Test'));

❌ Don't throw errors on failure:

// BAD - Throws instead of using harness
if (failed > 0) {
  throw new Error('Tests failed'); // Launcher never gets proper completion
}

Additional Best Practices

  1. Descriptive Names: Use clear, descriptive assertion messages
  2. Test Isolation: Each test should be independent and not rely on others
  3. Async Handling: Use await and async utilities for asynchronous operations
  4. Console Output: Use console.log() for test progress information
  5. Timeout Awareness: Set custom timeouts for tests taking >15 seconds
  6. Error Handling: Let assertions fail naturally; don't catch and suppress errors

File Structure

doh_modules/tests/
β”œβ”€β”€ tests.js              # Main test framework (harness + launcher + routes + CLI registration)
β”œβ”€β”€ cli_test_runner.js    # CLI test runner (doh test command)
β”œβ”€β”€ use/                  # USE system tests
β”‚   β”œβ”€β”€ test_*.doh.js    # Individual test modules
β”‚   └── debug_*.doh.js   # Debug/development tests
└── README.md            # This documentation

Integration with Doh.js

The test framework integrates seamlessly with:

  • Module System: Uses Doh.Module for dependency injection
  • Auto-Packager: Automatic discovery via Doh.Packages registry
  • USE System: Specialized testing utilities for USE state management
  • Express Router: Web-based test execution
  • Pattern System: HTML pattern-based UI components

Console Output Examples

Individual Test Output

=== My Feature Test Suite ===
βœ” PASS: Basic functionality works
βœ” PASS: Values match (expected 42, got 42)
✘ FAIL: Edge case handling (expected true, got false)

=== My Feature Test Suite Test Summary ===
Total: 3, Passed: 2, Failed: 1
Failures: ['Edge case handling (expected true, got false)']

Batch Run Console Output

Running test: test_use_array_advanced
βœ” PASS: Push operation works
βœ” PASS: Pop returns correct value
βœ” PASS: Splice removes correct elements
...
Test "test_use_array_advanced" completed: {total: 117, passed: 117, failed: 0}

Running test: test_undo_redo_system
Test timeout updated to 30000ms (30s)
βœ” PASS: Snapshot should complete
βœ” PASS: First edit should create timeline entry
...
Test "test_undo_redo_system" completed: {total: 25, passed: 25, failed: 0}

Batch run complete: 5 tests processed in 12.3s

Troubleshooting

Test Times Out

Problem: Test shows "ERROR" status with "Test timed out - no response for 15 seconds"

Solutions:

  1. Add setTestTimeout(30000) for long-running tests
  2. Ensure summarizeTests() is called at the end
  3. Check for infinite loops or hanging async operations
  4. Verify all async operations use await

Test Never Completes

Problem: Test shows "RUNNING" status indefinitely

Cause: Missing summarizeTests() call

Solution:

// Add at the end of your test:
return summarizeTests('Your Test Name');

Test Results Not Showing

Problem: No pass/fail statistics displayed

Cause: Missing resetTests() at the start

Solution:

// Add at the beginning of your test:
resetTests();

Custom Test Tracking Doesn't Work

Problem: Built your own test tracking system, but launcher shows no results

Cause: Not using the harness properly

Solution: Remove custom tracking and use harness functions directly:

// Remove: let passed = 0; function runTest() { ... }
// Use: const { expect, resetTests, summarizeTests } = tests_harness;

Architecture

Test Execution Flow

  1. User clicks test in launcher UI
  2. Launcher creates iframe and loads /test/{module_name}?iframe=true
  3. Test module loads and calls resetTests()
  4. Optional: Test calls setTestTimeout() for custom timeout
  5. Harness starts heartbeat (every 2s) and timeout countdown
  6. Test runs assertions via expect(), expectEqual(), etc.
  7. Test calls summarizeTests() which sends completion message
  8. Launcher receives completion, updates UI, stops countdown
  9. Statistics updated globally and per-folder

Message Protocol

Tests communicate with the launcher via postMessage:

  • test_heartbeat - Sent every 2s during test execution
  • test_set_timeout - Sent when test calls setTestTimeout()
  • test_completed - Sent when test calls summarizeTests()
  • console - Forwarded console output (log, error, warn, etc.)

Summary

The test framework provides a production-ready foundation for testing all aspects of Doh.js applications with:

βœ… Comprehensive Harness - Rich assertion library with 15+ test functions βœ… Full-Stack Testing - Server and browser tests in single packages with nodejs?? prefix βœ… Visual Test Runner - Modern UI with folder organization and batch execution βœ… CLI Test Runner - Run tests from command line with doh test (CI-friendly exit codes) βœ… Timeout Management - Configurable timeouts with visual countdown (default 15s) βœ… Real-time Monitoring - Live console output and heartbeat tracking βœ… Batch Execution - Run entire test suites with progress tracking βœ… Detailed Reporting - Summary views with error/warning capture βœ… Auto-discovery - Automatic test detection and route creation βœ… Iframe Isolation - Each test runs in its own sandbox βœ… Environment Detection - Automatic server/browser detection with IsBrowser()/IsNode()

The framework is battle-tested with 117+ assertions across multiple test modules covering arrays, state management, snapshots, observers, and complex async scenarios.

Last updated: 2/9/2026