describe(me) docs
early v0.12.1 browser mode · jsdom Live · Foundations
Examples npm GitHub

Performance

Recording costs time in every test, and the plugin costs a little in every run. The numbers come from the react-browser and react-jsdom suites.

In short #

The time goes to three places:

In the jsdom example, recording adds 310 ms to the sum of test durations and 728 ms to the wall clock of the run. In browser mode it adds 101 ms and 717 ms. The plugin alone adds 309 ms to the jsdom run and 91 ms to the browser-mode run.

Example suites #

In each example directory, node ../../scripts/bench-macro.mjs 9 <variants> runs vitest run 9 times per variant. The variants take turns, after one warm-up run that does not count, and the tables show medians. Every variant reports through the same JSON reporter, so the console reporter never differs between them. Measured on an Apple M2 with Node 20.19.5.

off
No plugin.
idle
The plugin with exclude: '**', so it records nothing.
on
Recording.

The plugin cost is idle − off, the recording cost on − idle. The examples switch variants with an environment variable, see DESCRIBE_ME.

jsdom

examples/react-jsdom: 28 tests, 23 of them in the manifest, 33 frames.

off idle on plugin cost recording cost
sum of test durations 543 ms 536 ms 846 ms -8 ms (-1 %) +310 ms (+58 %)
wall clock of vitest run 1956 ms 2265 ms 2993 ms +309 ms (+16 %) +728 ms (+32 %)

The wall clock of the jsdom run without recording, with one part of the plugin switched off, against idle. These are the variants idle-no-css, idle-no-styled and idle-no-exports:

wall clock change
test.css: false 2272 ms +7 ms (+0 %)
styledComponentsBrowserBuild: false 2235 ms -30 ms (-1 %)
registerExports: false 2207 ms -58 ms (-3 %)
Not settings to recommend. The first two drop stylesheets from the snapshots, and the third weakens the lookup of component names and props.

Browser mode

examples/react-browser in headless Chromium: 25 tests, 44 frames.

off idle on plugin cost recording cost
sum of test durations 1429 ms 1493 ms 1594 ms +65 ms (+5 %) +101 ms (+7 %)
wall clock of vitest run 2525 ms 2617 ms 3334 ms +91 ms (+4 %) +717 ms (+27 %)

The wall clock also covers the reporter: component docs, snapshot files, stylesheets and assets.

Capture cost #

One step() capture by DOM size, the median of pnpm bench:micro in each example:

DOM size Chromium jsdom
about 40 nodes 0.4 ms (48 nodes) 1.8 ms (35 nodes)
about 300 nodes 1.3 ms (318 nodes) 3.7 ms (305 nodes)
about 3 000 nodes 10.7 ms (3 018 nodes) 26.6 ms (3 005 nodes)
about 15 000 nodes 57.4 ms (15 018 nodes) 136.8 ms (15 005 nodes)

In jsdom, the 1 ms setTimeout(0) of the settle is part of every capture. Most of the rest is rrweb's serialization. JSON.stringify of the result takes 13–14 % of a capture from 3 000 nodes up in Chromium, and 7–8 % in jsdom. Comparing that text with the previous frame's takes at most 0.3 ms in Chromium and 0.52 ms in jsdom, at 15 000 nodes.

Naming a test after the first registered component inside a provider takes 0.14 ms at 3 008 nodes in jsdom (median, pnpm bench:micro in examples/react-jsdom).

There is no requestAnimationFrame wait before a capture. The first version had one, which cost a full vsync (about 16 ms) per frame and made tests 1.8× slower. A snapshot is DOM and stylesheets, not layout, so one macrotask is enough for React to commit.

Output size #

Every snapshot file is named after a hash of its content, so identical DOM is stored once, however many frames, tests and runs produce it. Long stylesheets move into styles/, split into chunks that snapshots share. See How it works.

pnpm measure-output finds:

Example Snapshot files Distinct DOMs styles/
examples/react-browser 37 37 4 files, 3.8 kB
examples/react-jsdom 26 26 10 files, 5.2 kB

Web fonts add weight to a static site. describe-me build downloads every unicode-range subset of a font, so Inter in three weights adds about 220 kB to the site. See Vendoring.

Live in the static site

describe-me build --live adds the static live preview to the site as __live/. In each example directory, describe-me build --no-vendor-fonts ran 9 times with --live and 9 times without, taking turns after one warm-up run of each. The times are medians of the whole command. Measured on an Apple M2 with Node 20.19.5 and Vite 8.3.0.

Example build build --live __live/
examples/react-browser 0.17 s 0.35 s 23 files, 1.1 MB
examples/react-jsdom 0.17 s 0.32 s 25 files, 1.7 MB

The build is not minified. Most of __live/ is one chunk that every test shares, with React's development build: 1.0 MB in react-browser, and 1.3 MB in react-jsdom, where Testing Library joins it. The runtime of the live page adds about 70 kB, and each test file a chunk of its own.

Measure your project #

Compare three variants of the same run:

  1. Off enabled: false, the baseline.
  2. Idle exclude: '**', the plugin without recording. Point outDir elsewhere, or its cleanup empties your .describe-me/.
  3. On recording, as you run it for the docs.

vitest.config.ts

import { defineConfig } from 'vitest/config'
import { describeMe } from '@describe-me/vitest/plugin'

const variant = process.env.VARIANT ?? 'on'
const idle = variant === 'idle'

export default defineConfig({
  plugins: [
    describeMe({
      enabled: variant !== 'off',
      exclude: idle ? '**' : undefined,
      outDir: idle ? 'node_modules/.describe-me-idle' : undefined,
    }),
  ],
  test: {
    // The same console reporter in every variant.
    reporters: ['default'],
  },
})

Name the reporters in the config, as above. The plugin then adds only its own, so all three variants print through the same ones. Do not pass --reporter: it replaces the whole list, describe-me's reporter included. A reporter difference distorts the comparison. An AI agent session, for one, picks minimal, which prints less than default.

Run the variants in turn (off, idle, on, off, idle, on…) rather than one after the other, so warm caches and machine load hit them alike. Skip the first round, and take the median of several. scripts/bench-macro.mjs in the repository does this for the examples. pnpm bench:macro does 5 runs of off, idle and on in examples/react-browser, and of all six variants in examples/react-jsdom.

Making it cheaper #

test.css: false, styledComponentsBrowserBuild: false and registerExports: false save little, as the jsdom table shows, and cost output quality.