# In tests (/docs/javascript/testing)

The Vitest plugin turns what Tracepanel finds into test failures, so an N+1
query fails CI before it reaches production. It comes with the
`@tracepanel/js` package and needs Vitest 4.1 or later.

## Turn it on [#turn-it-on]

Add the plugin to the Vitest config:

```ts
// vitest.config.ts
import { defineConfig } from "vitest/config";
import { tracepanel } from "@tracepanel/js/vitest";

export default defineConfig({
  plugins: [tracepanel()],
});
```

`failOn` sets the kinds of issues that fail a test:

```ts
tracepanel({ failOn: ["n+1", "duplicates", "slow-sql"] });
```

It takes the [issue](/docs/issues) names that `has:` takes in a search, such
as `slow-sql` or `error`. The default is `n+1` and `duplicates`.

The plugin turns Tracepanel on in the test processes, with the widget off,
and instruments `pg`, `mysql2` and the ORMs on them before your tests import
them. It starts the project's agent when none is running.

## What a test records [#what-a-test-records]

Each test runs as a `test` trace named after the test. A request the test
makes of a server it started, through supertest or `fetch()`, is a trace of
its own, linked to the test's in the console. `beforeAll` runs outside the
tests' traces, so seeding the database doesn't count. `beforeEach` and
`afterEach` run inside each test's trace.

When the test passes, the agent looks at its traces. An issue of a kind in
`failOn` fails the test with the issue, its query, the line of code that ran
it and a link to the trace:

```text
AssertionError: tracepanel found 1 issue in this test:

N+1: 4 similar queries · SELECT authors
  SELECT name FROM authors WHERE id = $1
  at src/books.ts:28 in listBooks
  in GET /books → http://127.0.0.1:52068/#/t/01M60NB74Q6T9AXZGMFJ7V8FB1
  Load the related rows with the parent rows (a join, or one batched query) ...

The test's trace: http://127.0.0.1:52068/#/t/01M60NB74J4Q5VDHCJD1Z4T1J7
Allowed here with the test's option { meta: { tracepanel: { allow: ["n+1"] } } }.
```

When a query goes through Prisma, Drizzle, TypeORM, Sequelize or Knex, the
hint is that ORM's own way to load the related rows.

## Change the rules for a test [#change-the-rules-for-a-test]

The `tracepanel` meta changes the rules for a test or a `describe()`. A
test's own rules come first, then those of the suites around it:

| Rule | What it does |
| --- | --- |
| `maxQueries` | Fails the test when it and its requests make more queries than this. |
| `failOn` | Replaces the kinds of issues that fail the test. |
| `allow` | Takes kinds of issues out of those that fail the test. |

`testTrace()` gives the test its trace: `.queries` counts the queries so
far, and `await .issues()` lists what the agent found in the test's requests
so far. `maxQueries(n, fn)` fails when `fn` makes more than `n` queries, its
requests' included:

```ts
import { expect, test } from "vitest";
import { maxQueries, testTrace } from "@tracepanel/js/vitest";

test("feed", { meta: { tracepanel: { maxQueries: 10, allow: ["n+1"] } } }, async () => {
  await request(app).get("/feed");
  expect(testTrace().queries).toBeLessThanOrEqual(10);
  await maxQueries(2, () => request(app).get("/feed?page=2"));
});
```

## Without an agent [#without-an-agent]

With `TRACEPANEL_AGENT=off`, the tests run as usual and one warning says that
issues aren't checked. Query counts need no agent, so `maxQueries` still
works.

The plugin works with Vitest's `forks` and `threads` pools, where every worker
sends its tests to the same agent, and with concurrent tests.
