Skip to main content
On this page

Documentation tests

Deno can evaluate the code snippets written in your JSDoc comments and markdown files and run them as tests. This keeps the examples in your documentation honest: when an API changes, its outdated examples fail in CI instead of misleading readers.

Example code blocks Jump to heading

example.ts
/**
 * # Examples
 *
 * ```ts
 * import { assertEquals } from "jsr:@std/assert/equals";
 *
 * const sum = add(1, 2);
 * assertEquals(sum, 3);
 * ```
 */
export function add(a: number, b: number): number {
  return a + b;
}

The triple backticks mark the start and end of code blocks, the language is determined by the language identifier attribute which may be one of the following:

  • js
  • javascript
  • mjs
  • cjs
  • jsx
  • ts
  • typescript
  • mts
  • cts
  • tsx

If no language identifier is specified then the language is inferred from media type of the source document that the code block is extracted from.

>_
deno test --doc example.ts

The above command will extract this example, turn it into a pseudo test case that looks like below:

example.ts$4-10.ts
import { assertEquals } from "jsr:@std/assert/equals";
import { add } from "file:///path/to/example.ts";

Deno.test("example.ts$4-10.ts", async () => {
  const sum = add(1, 2);
  assertEquals(sum, 3);
});

and then run it as a standalone module living in the same directory as the module being documented.

Want to type-check only?

If you want to type-check your code snippets in JSDoc and markdown files without actually running them, you can use deno check command with --doc option (for JSDoc) or with --doc-only option (for markdown) instead.

Exported items are automatically imported Jump to heading

Looking at the generated test code above, you will notice that it includes the import statement to import the add function even though the original code block does not have it. When documenting a module, any items exported from the module are automatically included in the generated test code using the same name.

Let's say we have the following module:

example.ts
/**
 * # Examples
 *
 * ```ts
 * import { assertEquals } from "jsr:@std/assert/equals";
 *
 * const sum = add(ONE, getTwo());
 * assertEquals(sum, 3);
 * ```
 */
export function add(a: number, b: number): number {
  return a + b;
}

export const ONE = 1;
export default function getTwo() {
  return 2;
}

This will get converted to the following test case:

example.ts$4-10.ts
import { assertEquals } from "jsr:@std/assert/equals";
import { add, ONE }, getTwo from "file:///path/to/example.ts";

Deno.test("example.ts$4-10.ts", async () => {
  const sum = add(ONE, getTwo());
  assertEquals(sum, 3);
});

Hashbang and narrowed permissions Jump to heading

If a code example starts with a hashbang, it is validated against supported Deno cli flags, and permissions flags are forwarded to the generated Deno.test.

/**
 * Prints the value of an environment variable.
 *
 * ```ts
 * #!/usr/bin/env -S deno run --allow-env=MY_ENV_VAR
 * console.log(Deno.env.get("MY_ENV_VAR"));
 * ```
 */

Below is how each permission flag is interpreted:

Flag Generated permissions
--allow-all Inherits all permissions from deno test
--allow-* Inherits the specified permission from deno test
--allow-*=… Restrict the specified permission to provided values
--deny-* Explicitly revoke the specified permission
--deny-*=… Currently unsupported (results in a test failure)
--permission-set Currently unsupported (ignored)
--ignore-* Currently unsupported (ignored)
No permission flags None

Note

A code example never runs with broader permissions than what deno test was granted, even if the hashbang itself specifies broader ones.

Skipping code blocks Jump to heading

You can skip the evaluation of code blocks by adding the ignore attribute.

/**
 * This code block will not be run.
 *
 * ```ts ignore
 * await sendEmail("deno@example.com");
 * ```
 */
export async function sendEmail(to: string) {
  // send an email to the given address...
}

Last updated on

Did you find what you needed?

Edit this page
Privacy policy