<!-- deno-fmt-ignore-file -->

Testing utilities for LogTape
=============================

[![JSR][JSR badge]][JSR]
[![npm][npm badge]][npm]

This package provides testing utilities for [LogTape].  It includes a log
recorder that collects `LogRecord` values in memory and provides matcher-based
assertions for category, level, rendered message, raw message, and structured
properties.  It also includes a failure log reporter that buffers records while
a test callback runs and reports them only when the callback fails.

[JSR badge]: https://jsr.io/badges/@logtape/testing
[JSR]: https://jsr.io/@logtape/testing
[npm badge]: https://img.shields.io/npm/v/@logtape/testing?logo=npm
[npm]: https://www.npmjs.com/package/@logtape/testing
[LogTape]: https://logtape.org/


Installation
------------

This package is available on [JSR] and [npm].  You can install it for various
JavaScript runtimes and package managers:

~~~~ sh
deno add jsr:@logtape/testing  # for Deno
npm  add     @logtape/testing  # for npm
pnpm add     @logtape/testing  # for pnpm
yarn add     @logtape/testing  # for Yarn
bun  add     @logtape/testing  # for Bun
~~~~


Usage
-----

Use `createLogRecorder()` as a sink in tests:

~~~~ typescript
import { configure, getLogger, reset } from "@logtape/logtape";
import { createLogRecorder } from "@logtape/testing/recorder";

const recorder = createLogRecorder();

try {
  await configure({
    sinks: { recorder: recorder.sink },
    loggers: [
      { category: ["my-lib"], lowestLevel: "debug", sinks: ["recorder"] },
      { category: ["logtape", "meta"], sinks: [] },
    ],
  });

  getLogger(["my-lib"]).info("User {userId} logged in.", {
    userId: 123,
  });

  recorder.assertLogged({
    category: ["my-lib"],
    level: "info",
    message: "User 123 logged in.",
    properties: { userId: 123 },
  });
} finally {
  await reset();
}
~~~~

The recorder snapshots lazy callback messages when its sink receives them, so
assertions see the same message a normal sink would observe at emit time.  The
`records` property returns a snapshot, and the recorder also provides
`clear()`, `take()`, `find()`, `filter()`, and `assertNotLogged()` for tests
that need lower-level access.  Most property values are compared with
`Object.is()`, `Date` values are compared by timestamp, and regular expression
matcher values match string property values.  Rendered message matching uses
the same value rendering as LogTape's default text formatter.

Use `createFailureLogReporter()` when logs are useful only after a test fails:

~~~~ typescript
import { createFailureLogReporter } from "@logtape/testing/reporter";

const reporter = createFailureLogReporter({
  lowestLevel: "debug",
});

test("case", reporter.wrap(async () => {
  // Logs emitted here are reported only if this callback throws.
}));
~~~~

The root `@logtape/testing` entry point re-exports both utilities for
compatibility, but new code can import `@logtape/testing/recorder` or
`@logtape/testing/reporter` to depend on only the relevant API surface.
Use `getFailureLogReporterOptionsFromEnv()` when an integration needs to parse
`LOGTAPE_TEST_MODE` and `LOGTAPE_TEST_LOWEST_LEVEL` from a runtime-supplied
environment variable getter.


Docs
----

The docs of this package is available at
<https://logtape.org/manual/testing>. For the API references, see
<https://jsr.io/@logtape/testing>.
