Skip to content

Testing Framework: hx test #6

Description

@ashigirl96

Summary

A test framework that lets developers verify hooks without launching Claude Code.

Motivation

Currently, the only way to test a hook is to run Claude Code and trigger the event manually. This makes the development cycle slow and edge-case coverage impractical.

Proposed API

hx test                          # run all extension tests
hx test guard --event PreToolUse # simulate a specific event

Key Features

  • Mock event payload factories for every event type (with tool_input as Record<string, any>)
  • Assertions on HookOutput — verify deny(), allow(), addContext() results without checking wire format
  • HookBlockError verification (exit code 2)
  • LLM response mocking for prompt/agent hooks
  • Snapshot testing support
  • hx new templates include a test file by default

Example

import { testHook, mockEvent } from "@dawkinsuke/hooks/testing"
import { deny } from "@dawkinsuke/hooks"

test("blocks rm -rf /", async () => {
  const event = mockEvent("PreToolUse", "Bash", {
    input: { command: "rm -rf /" },
  })
  const result = await testHook(guardExtension, event)
  // Can assert on HookOutput directly
  expect(result).toBeInstanceOf(HookOutput)
  expect(result._decision).toBe("deny")
})

test("allows safe commands", async () => {
  const event = mockEvent("PreToolUse", "Bash", {
    input: { command: "ls -la" },
  })
  const result = await testHook(guardExtension, event)
  expect(result).toBeUndefined() // pass-through
})

Design Notes

  • mockEvent() should produce payloads matching HookEventMap types (with relaxed tool_input: Record<string, any>)
  • testHook() returns the raw handler result (HookOutput | HookJSONOutput | void), not wire-format JSON
  • For wire-format assertions, use result._resolve("PreToolUse") on HookOutput instances
  • Should integrate with isHookOutput() for type-safe assertions

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions