Snapshots

Use toMatchSnapshot for a reviewed, versioned record of a value. The snapshot file lives in an adjacent __snapshots__ directory, so each test module owns its expectations whether files run in separate processes or share one cache.

JavaScript
import { test, expect } from "runtime:test";

test("response", () => {
  expect({ ok: true, ids: [1, 2] }).toMatchSnapshot();
  expect("<main>ok</main>\n").toMatchFileSnapshot("home.html");
});

The first local run writes a missing snapshot and reports it. In CI, missing snapshots fail so every assertion must be committed. Review and explicitly update changed snapshots with:

Shell
esdev test --update-snapshots

The file has a format version and sorted object keys. Each snapshot is stored under the test's name and its own name, numbered in order: toMatchSnapshot("response body") is response body 1, and unnamed calls are snapshot 1, snapshot 2 and so on. Adding a snapshot of one name does not renumber the others.

Two tests in one file that take snapshots need different names; the second is refused. Snapshots of a test that was skipped or did not pass are not reported as obsolete, and with retry, only the last attempt's results count. Value snapshots live at __snapshots__/response.test.mjs.snap; a file snapshot lives at __snapshots__/response.test.mjs/home.html. A file snapshot name is a single filename, never a path, so it cannot write outside that directory.

Mask a volatile field without hiding the rest of the value:

JavaScript
expect(user).toMatchSnapshot({ id: expect.any(String) });

assertSnapshot(value, name?) is the assertion spelling, and expect(fn).toThrowErrorMatchingSnapshot() stores a thrown error. A missing snapshot is created only outside CI; --ci (and CI) requires it to exist. --update-snapshots updates changed entries and prunes obsolete entries only after a complete, unfiltered, all-passing file run. When a value changes, the failing test prints a line-by-line unified diff: removed snapshot lines begin with - and received lines begin with +. Large diffs are review-sized by default; pass --full-diff for every changed line. Ordinary diffs retain three unchanged context lines around each change. Byte snapshots instead identify sizes, the first differing byte, and a small hexadecimal window. Text diffs render trailing spaces as ·, tabs as , CRLF's carriage return as , and report a missing final newline. Each passing run that used snapshots prints a summary such as snapshots: 3 matched, 0 failed, 1 written.

Inline snapshots

toMatchInlineSnapshot() keeps the snapshot in the test file, as the call's argument:

JavaScript
test("user", () => {
  expect(user).toMatchInlineSnapshot({ id: expect.any(Number) });
});

The first local run writes the value into the call, in the format Jest and Vitest use:

JavaScript
test("user", () => {
  expect(user).toMatchInlineSnapshot({ id: expect.any(Number) }, `
    {
      "id": any(Number),
      "name": "ada",
    }
  `);
});

After that, a different value fails with a diff, --update-snapshots rewrites the argument, and --ci fails rather than writing. A one-line value is written on one line. toThrowErrorMatchingInlineSnapshot() does the same for a thrown error. An inline snapshot cannot be taken in a loop with different values; use toMatchSnapshot there.

Browser runs read and write the same snapshot files, and write inline snapshots into the source the same way.

Coming from Jest or Vitest

The matchers, their arguments and the update workflow are the same. Plain objects, arrays, strings and numbers are written the same way too, so those snapshots carry over as they are.

A few values are written more precisely here:

ValueJestesdev
A thrown error"bad input"TypeError("bad input")
A date1970-01-01T00:00:00.000ZDate("1970-01-01T00:00:00.000Z")
expect.any(Number)Any<Number>any(Number)

Snapshots holding these fail once after moving. Review them and run esdev test --update-snapshots.

Last updated on
Edit this page