poku

repository·main·Indexed 22 days ago

https://github.com/wellwelwel/poku

A high-performance, cross-platform test runner for Node.js, Bun, and Deno. Poku provides high isolation per file and supports ESM, CJS, and TypeScript. It includes a strict assertion API, lifecycle hooks (beforeEach, afterEach), and a plugin system for extending the runner and customizing reporters. Key features include environment file processing, port management utilities, and integrated Docker and coverage plugins.

Tokens
62.1K
Snippets
258
Records
300
Agent score
78%

What's inside poku

  1. Overview of Poku features and helpers

    main

    Poku is a high-isolation, performant, and lightweight cross-platform test runner. It supports ESM, CJS, and TypeScript, and can run the same test suite across Node.js, Bun, and Deno.

    Core Capabilities

    • Test Runner: Core engine for executing tests.
    • Assertions: assert (strict, Node.js familiar API).
    • Test Organization: test, describe, and it for grouping and isolating tests.
    • Lifecycle Hooks: beforeEach and afterEach for setup and teardown.
    • Environment & Process Management:
      • envFile to process environment files.
      • startScript and startService to run scripts/files in the background.
      • kill to terminate ports, port ranges, or PIDs.
      • waitForPort to wait for active ports.
      • waitForExpectedResult to retry until a result is met or it times out.
    • Flow Control: skip to bypass tests and only for selective debugging.
    • Infrastructure & Plugins:
      • docker plugin for container management (build, start, compose, stop, remove).
      • coverage plugin for collecting code coverage.
    • Configuration & CLI:
      • config files to customize behavior.
      • watch mode to re-run tests on file changes.
      • debug mode to show all logs.
  2. Compare Poku against other test runners

    main

    Poku is designed for high performance and minimal footprint compared to mainstream test runners.

    Performance

    • ~5.3x faster than Jest (v30.4.2)
    • ~4.5x faster than Vitest (v4.1.6)

    Installation Size

    • ~145x lighter than Jest
    • ~124x lighter than Vitest
    • ~83x lighter than AVA
    • ~47x lighter than Mocha
    • ~2.8x lighter than uvu

    Feature Matrix

    Test RunnerIsolationCJSESMBunDeno
    Poku
    Jestexperimental
    Vitest
    AVA
    Mocha
    uvu
  3. How `retry` handles nesting and `describe` blocks

    main

    The retry helper uses a stack-based context to support complex testing scenarios:

    • Nested Retries: You can nest retry blocks. Each block operates independently with its own attempt counter. For example, an outer retry of 2 attempts with an inner retry of 3 attempts will result in up to 3 attempts per outer attempt.
    • Retry around describe: You can wrap entire describe blocks with retry. If any test within the describe block fails, the failure status propagates to the retry context, triggering a re-run of the entire suite.
    • Memory Efficiency: retry uses lazy allocation; the stack is only created when retry is called and is reset to null when empty, ensuring zero overhead if not used.
    import { retry, describe, it, assert } from 'poku';
    
    // Wrap entire suite
    await retry(2, () => {
      describe('flaky suite', () => {
        it('test 1', () => {
          assert.strictEqual(1, 1);
        });
    
        it('test 2', () => {
          assert.strictEqual(Math.random() > 0.5, true);
        });
      });
    });
  4. How Multi Suite works and executes

    main

    Multi Suite orchestrates multiple independent poku executions. Key behaviors include:

    • Sequential Execution: Suites run one after another. A failure in one suite does not stop the execution of subsequent suites.
    • Isolation: Each suite is a fully independent execution. They can have different environment files, plugins, and concurrency settings.
    • Reporting: Results (passed, failed, skipped) are accumulated across all suites. Individual file results are shown live using the suite's specific reporter, but onRunResult and onExit hooks are suppressed for individual suites; only the final consolidated report triggers them.
    • Exit Codes: The process exits with code 1 if any suite fails, and 0 if all suites pass.
    • Termination: Pressing Ctrl+C stops all suites immediately.
  5. How test isolation works in Poku

    main

    Poku provides two modes for running test files, controlled by the isolation option. This setting determines how much separation exists between individual test files.

    isolation: 'process' (Default)

    Each test file is spawned in its own separate child process. This provides full isolation, meaning each file has its own:

    • process.exitCode
    • Module cache
    • Global state
    • stdout and stderr handling

    This mode allows for concurrent execution and ensures that a process.exit() call or a crash in one test file does not affect the rest of the test suite.

    isolation: 'none'

    All test files run within the same process. This mode is primarily used for debugging, as it allows you to attach a debugger (like node --inspect) to the main Poku process and step through your test code directly.

    npx poku --isolation=process ./test # default
    npx poku --isolation=none ./test
  6. Run tests in parallel or wait for multiple promises

    main

    Poku allows for concurrent test execution.

    • Parallel Execution: Simply calling multiple test blocks without await allows them to run in parallel.
    • Waiting for Multiple Tests: To run multiple tests in parallel and wait for all of them to complete (similar to a beforeAll/afterAll pattern), wrap them in Promise.all().
    import { test } from 'poku';
    
    // Run multiple tests in parallel and wait for all to finish
    await Promise.all([
      test(async () => {
        // async task 1
      }),
    
      test(async () => {
        // async task 2
      }),
    ]);
  7. Understand Assertions in Poku

    main

    In Poku, assertions are used to verify that a result matches an expected value. Unlike standard JavaScript conditional logic (if/else), which allows the script to continue even if a condition is false, a failed assertion in Poku will immediately abort the script and exit the process with an error.

    • Successful Assertion: The script continues execution normally.
    • Failed Assertion: The script stops immediately and reports an error.
  8. Poku Official Plugins

    main

    Extend Poku's capabilities with official plugins:

    • Coverage: Enable code coverage using --coverage with plugins like @pokujs/c8, @pokujs/monocart, @pokujs/istanbul, or @pokujs/one-double-zero.
    • @pokujs/react: Render and assert on React components with a real DOM environment.
    • @pokujs/vue: Render and assert on Vue components with a real DOM environment and SFC support.
    • @pokujs/docker: Mount/unmount containers around tests using Docker Compose or Dockerfiles.
    • @pokujs/multi-suite: Run multiple independent test suites with different configurations in one execution.
    • @pokujs/shared-resources: Share state, servers, and database connections across parallel test files.
  9. How Poku's testing model works

    main

    Unlike traditional test runners that build an internal tree of describe and it blocks to schedule execution, Poku treats test files as ordinary JavaScript or TypeScript files. When you call describe, it, or test, Poku executes the callback immediately in place and reports the result.

    Key characteristics:

    • No Global Registry: There is no hidden structure collecting tests to run later. A single assertion in a file is a complete test.
    • Ownership of Lifecycle: You control the execution order using standard JavaScript control flow.
    • test is an alias: The test function is simply an alias for it.
    • Sequential vs Concurrent: Tests run sequentially when you await them, and concurrently when you do not.
  10. How Poku plugins work

    main

    A Poku plugin is an object that hooks into the test runner's lifecycle. Plugins can intercept file discovery, modify the execution command for test files, enable IPC communication, and run setup/teardown logic.

    Key Lifecycle Rules:

    • Runner Hook: Only the first plugin in the plugins array that defines a runner hook will be used. Subsequent runner hooks are ignored.
    • IPC: If any plugin in the plugins array sets ipc: true, all test processes will have IPC enabled.
    • Discovery Hook: Only the first plugin in the plugins array that defines a discoverFiles hook will be used.
    // Example of a plugin structure
    export const myPlugin = definePlugin({
      name: 'my-plugin',
      setup: async (ctx) => { /* ... */ },
      teardown: async (ctx) => { /* ... */ },
    });