@vitest-agent/mcp 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -2
- package/index.d.ts +2 -2
- package/index.js +1 -1
- package/package.json +1 -1
- package/server.js +2 -4
- package/patterns/_meta.json +0 -67
- package/patterns/authoring-a-custom-vitest-agent-reporter.md +0 -82
- package/patterns/known-issues-and-caveats.md +0 -52
- package/patterns/operating-vitest-agent-as-an-agent.md +0 -62
- package/patterns/running-tests-via-mcp.md +0 -103
- package/patterns/silencing-leaking-output-in-tests.md +0 -91
- package/patterns/testing-effect-schema-definitions.md +0 -71
- package/patterns/testing-effect-services-with-mock-layers.md +0 -63
- package/resources/index.js +0 -166
- package/resources/indexes.js +0 -77
- package/resources/manifest-schema.js +0 -46
- package/resources/paths.js +0 -20
- package/resources/patterns.js +0 -22
- package/resources/upstream-docs.js +0 -22
- package/vendor/vitest-docs/ATTRIBUTION.md +0 -5
- package/vendor/vitest-docs/api/advanced/artifacts.md +0 -189
- package/vendor/vitest-docs/api/advanced/metadata.md +0 -68
- package/vendor/vitest-docs/api/advanced/plugin.md +0 -168
- package/vendor/vitest-docs/api/advanced/reporters.md +0 -342
- package/vendor/vitest-docs/api/advanced/runner.md +0 -334
- package/vendor/vitest-docs/api/advanced/test-case.md +0 -302
- package/vendor/vitest-docs/api/advanced/test-collection.md +0 -89
- package/vendor/vitest-docs/api/advanced/test-module.md +0 -140
- package/vendor/vitest-docs/api/advanced/test-project.md +0 -321
- package/vendor/vitest-docs/api/advanced/test-specification.md +0 -96
- package/vendor/vitest-docs/api/advanced/test-suite.md +0 -230
- package/vendor/vitest-docs/api/advanced/vitest.md +0 -684
- package/vendor/vitest-docs/api/assert-type.md +0 -22
- package/vendor/vitest-docs/api/assert.md +0 -1960
- package/vendor/vitest-docs/api/browser/assertions.md +0 -1277
- package/vendor/vitest-docs/api/browser/commands.md +0 -154
- package/vendor/vitest-docs/api/browser/context.md +0 -338
- package/vendor/vitest-docs/api/browser/interactivity.md +0 -681
- package/vendor/vitest-docs/api/browser/locators.md +0 -1171
- package/vendor/vitest-docs/api/browser/react.md +0 -346
- package/vendor/vitest-docs/api/browser/svelte.md +0 -292
- package/vendor/vitest-docs/api/browser/vue.md +0 -222
- package/vendor/vitest-docs/api/describe.md +0 -374
- package/vendor/vitest-docs/api/expect-typeof.md +0 -571
- package/vendor/vitest-docs/api/expect.md +0 -2304
- package/vendor/vitest-docs/api/hooks.md +0 -463
- package/vendor/vitest-docs/api/mock.md +0 -701
- package/vendor/vitest-docs/api/test.md +0 -926
- package/vendor/vitest-docs/api/vi.md +0 -1372
- package/vendor/vitest-docs/config/alias.md +0 -13
- package/vendor/vitest-docs/config/allowonly.md +0 -32
- package/vendor/vitest-docs/config/api.md +0 -27
- package/vendor/vitest-docs/config/attachmentsdir.md +0 -6
- package/vendor/vitest-docs/config/bail.md +0 -9
- package/vendor/vitest-docs/config/benchmark.md +0 -65
- package/vendor/vitest-docs/config/browser/api.md +0 -23
- package/vendor/vitest-docs/config/browser/commands.md +0 -6
- package/vendor/vitest-docs/config/browser/connecttimeout.md +0 -10
- package/vendor/vitest-docs/config/browser/detailspanelposition.md +0 -38
- package/vendor/vitest-docs/config/browser/enabled.md +0 -40
- package/vendor/vitest-docs/config/browser/expect.md +0 -250
- package/vendor/vitest-docs/config/browser/headless.md +0 -7
- package/vendor/vitest-docs/config/browser/instances.md +0 -47
- package/vendor/vitest-docs/config/browser/isolate.md +0 -11
- package/vendor/vitest-docs/config/browser/locators.md +0 -24
- package/vendor/vitest-docs/config/browser/orchestratorscripts.md +0 -39
- package/vendor/vitest-docs/config/browser/playwright.md +0 -214
- package/vendor/vitest-docs/config/browser/preview.md +0 -32
- package/vendor/vitest-docs/config/browser/provider.md +0 -79
- package/vendor/vitest-docs/config/browser/screenshotdirectory.md +0 -6
- package/vendor/vitest-docs/config/browser/screenshotfailures.md +0 -6
- package/vendor/vitest-docs/config/browser/testerhtmlpath.md +0 -5
- package/vendor/vitest-docs/config/browser/trace.md +0 -43
- package/vendor/vitest-docs/config/browser/trackunhandlederrors.md +0 -10
- package/vendor/vitest-docs/config/browser/ui.md +0 -7
- package/vendor/vitest-docs/config/browser/viewport.md +0 -6
- package/vendor/vitest-docs/config/browser/webdriverio.md +0 -64
- package/vendor/vitest-docs/config/cache.md +0 -26
- package/vendor/vitest-docs/config/chaiconfig.md +0 -29
- package/vendor/vitest-docs/config/clearmocks.md +0 -22
- package/vendor/vitest-docs/config/coverage.md +0 -455
- package/vendor/vitest-docs/config/css.md +0 -47
- package/vendor/vitest-docs/config/dangerouslyignoreunhandlederrors.md +0 -23
- package/vendor/vitest-docs/config/deps.md +0 -127
- package/vendor/vitest-docs/config/detectasyncleaks.md +0 -39
- package/vendor/vitest-docs/config/diff.md +0 -96
- package/vendor/vitest-docs/config/dir.md +0 -7
- package/vendor/vitest-docs/config/disableconsoleintercept.md +0 -15
- package/vendor/vitest-docs/config/env.md +0 -5
- package/vendor/vitest-docs/config/environment.md +0 -96
- package/vendor/vitest-docs/config/environmentoptions.md +0 -30
- package/vendor/vitest-docs/config/exclude.md +0 -49
- package/vendor/vitest-docs/config/execargv.md +0 -10
- package/vendor/vitest-docs/config/expandsnapshotdiff.md +0 -7
- package/vendor/vitest-docs/config/expect.md +0 -38
- package/vendor/vitest-docs/config/experimental.md +0 -510
- package/vendor/vitest-docs/config/faketimers.md +0 -51
- package/vendor/vitest-docs/config/fileparallelism.md +0 -11
- package/vendor/vitest-docs/config/forcereruntriggers.md +0 -19
- package/vendor/vitest-docs/config/globals.md +0 -42
- package/vendor/vitest-docs/config/globalsetup.md +0 -72
- package/vendor/vitest-docs/config/hideskippedtests.md +0 -7
- package/vendor/vitest-docs/config/hooktimeout.md +0 -7
- package/vendor/vitest-docs/config/include-source.md +0 -115
- package/vendor/vitest-docs/config/include.md +0 -71
- package/vendor/vitest-docs/config/includetasklocation.md +0 -17
- package/vendor/vitest-docs/config/index.md +0 -85
- package/vendor/vitest-docs/config/isolate.md +0 -13
- package/vendor/vitest-docs/config/logheapusage.md +0 -7
- package/vendor/vitest-docs/config/maxconcurrency.md +0 -9
- package/vendor/vitest-docs/config/maxworkers.md +0 -49
- package/vendor/vitest-docs/config/mockreset.md +0 -22
- package/vendor/vitest-docs/config/mode.md +0 -7
- package/vendor/vitest-docs/config/name.md +0 -111
- package/vendor/vitest-docs/config/onconsolelog.md +0 -25
- package/vendor/vitest-docs/config/onstacktrace.md +0 -32
- package/vendor/vitest-docs/config/onunhandlederror.md +0 -35
- package/vendor/vitest-docs/config/open.md +0 -7
- package/vendor/vitest-docs/config/outputfile.md +0 -7
- package/vendor/vitest-docs/config/passwithnotests.md +0 -7
- package/vendor/vitest-docs/config/pool.md +0 -45
- package/vendor/vitest-docs/config/printconsoletrace.md +0 -6
- package/vendor/vitest-docs/config/projects.md +0 -6
- package/vendor/vitest-docs/config/provide.md +0 -45
- package/vendor/vitest-docs/config/reporters.md +0 -69
- package/vendor/vitest-docs/config/resolvesnapshotpath.md +0 -36
- package/vendor/vitest-docs/config/restoremocks.md +0 -22
- package/vendor/vitest-docs/config/retry.md +0 -140
- package/vendor/vitest-docs/config/root.md +0 -6
- package/vendor/vitest-docs/config/runner.md +0 -6
- package/vendor/vitest-docs/config/sequence.md +0 -158
- package/vendor/vitest-docs/config/server.md +0 -68
- package/vendor/vitest-docs/config/setupfiles.md +0 -40
- package/vendor/vitest-docs/config/silent.md +0 -9
- package/vendor/vitest-docs/config/slowtestthreshold.md +0 -7
- package/vendor/vitest-docs/config/snapshotenvironment.md +0 -27
- package/vendor/vitest-docs/config/snapshotformat.md +0 -28
- package/vendor/vitest-docs/config/snapshotserializers.md +0 -6
- package/vendor/vitest-docs/config/stricttags.md +0 -30
- package/vendor/vitest-docs/config/tags.md +0 -141
- package/vendor/vitest-docs/config/teardowntimeout.md +0 -7
- package/vendor/vitest-docs/config/testnamepattern.md +0 -21
- package/vendor/vitest-docs/config/testtimeout.md +0 -7
- package/vendor/vitest-docs/config/typecheck.md +0 -77
- package/vendor/vitest-docs/config/ui.md +0 -15
- package/vendor/vitest-docs/config/unstubenvs.md +0 -20
- package/vendor/vitest-docs/config/unstubglobals.md +0 -20
- package/vendor/vitest-docs/config/update.md +0 -16
- package/vendor/vitest-docs/config/vmmemorylimit.md +0 -30
- package/vendor/vitest-docs/config/watch.md +0 -11
- package/vendor/vitest-docs/config/watchtriggerpatterns.md +0 -29
- package/vendor/vitest-docs/guide/advanced/index.md +0 -147
- package/vendor/vitest-docs/guide/advanced/pool.md +0 -148
- package/vendor/vitest-docs/guide/advanced/reporters.md +0 -93
- package/vendor/vitest-docs/guide/advanced/tests.md +0 -125
- package/vendor/vitest-docs/guide/browser/aria-snapshots.md +0 -470
- package/vendor/vitest-docs/guide/browser/component-testing.md +0 -571
- package/vendor/vitest-docs/guide/browser/index.md +0 -630
- package/vendor/vitest-docs/guide/browser/multiple-setups.md +0 -121
- package/vendor/vitest-docs/guide/browser/trace-view.md +0 -126
- package/vendor/vitest-docs/guide/browser/visual-regression-testing.md +0 -734
- package/vendor/vitest-docs/guide/cli-generated.md +0 -972
- package/vendor/vitest-docs/guide/cli.md +0 -234
- package/vendor/vitest-docs/guide/common-errors.md +0 -163
- package/vendor/vitest-docs/guide/coverage.md +0 -515
- package/vendor/vitest-docs/guide/debugging.md +0 -127
- package/vendor/vitest-docs/guide/environment.md +0 -101
- package/vendor/vitest-docs/guide/extending-matchers.md +0 -160
- package/vendor/vitest-docs/guide/features.md +0 -310
- package/vendor/vitest-docs/guide/filtering.md +0 -175
- package/vendor/vitest-docs/guide/ide.md +0 -43
- package/vendor/vitest-docs/guide/improving-performance.md +0 -245
- package/vendor/vitest-docs/guide/in-source.md +0 -159
- package/vendor/vitest-docs/guide/index.md +0 -128
- package/vendor/vitest-docs/guide/learn/async.md +0 -147
- package/vendor/vitest-docs/guide/learn/debugging-tests.md +0 -210
- package/vendor/vitest-docs/guide/learn/matchers.md +0 -277
- package/vendor/vitest-docs/guide/learn/mock-functions.md +0 -277
- package/vendor/vitest-docs/guide/learn/setup-teardown.md +0 -240
- package/vendor/vitest-docs/guide/learn/snapshots.md +0 -166
- package/vendor/vitest-docs/guide/learn/testing-in-practice.md +0 -430
- package/vendor/vitest-docs/guide/learn/writing-tests-with-ai.md +0 -127
- package/vendor/vitest-docs/guide/learn/writing-tests.md +0 -231
- package/vendor/vitest-docs/guide/lifecycle.md +0 -379
- package/vendor/vitest-docs/guide/migration.md +0 -863
- package/vendor/vitest-docs/guide/mocking/classes.md +0 -158
- package/vendor/vitest-docs/guide/mocking/dates.md +0 -52
- package/vendor/vitest-docs/guide/mocking/file-system.md +0 -74
- package/vendor/vitest-docs/guide/mocking/functions.md +0 -61
- package/vendor/vitest-docs/guide/mocking/globals.md +0 -20
- package/vendor/vitest-docs/guide/mocking/modules.md +0 -414
- package/vendor/vitest-docs/guide/mocking/requests.md +0 -114
- package/vendor/vitest-docs/guide/mocking/timers.md +0 -48
- package/vendor/vitest-docs/guide/mocking.md +0 -239
- package/vendor/vitest-docs/guide/open-telemetry.md +0 -156
- package/vendor/vitest-docs/guide/parallelism.md +0 -82
- package/vendor/vitest-docs/guide/profiling-test-performance.md +0 -243
- package/vendor/vitest-docs/guide/projects.md +0 -291
- package/vendor/vitest-docs/guide/recipes.md +0 -59
- package/vendor/vitest-docs/guide/reporters.md +0 -723
- package/vendor/vitest-docs/guide/snapshot.md +0 -620
- package/vendor/vitest-docs/guide/test-annotations.md +0 -103
- package/vendor/vitest-docs/guide/test-context.md +0 -902
- package/vendor/vitest-docs/guide/test-tags.md +0 -314
- package/vendor/vitest-docs/guide/testing-types.md +0 -149
- package/vendor/vitest-docs/guide/ui.md +0 -160
- package/vendor/vitest-docs/guide/using-plugins.md +0 -5
- package/vendor/vitest-docs/manifest.json +0 -1691
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
# Testing Effect Services with Mock Layers
|
|
2
|
-
|
|
3
|
-
## When to use
|
|
4
|
-
|
|
5
|
-
Reach for this pattern when an Effect service has external dependencies (filesystem, SQLite, network) that you do not want to exercise in unit tests. The codebase under `packages/sdk/src/services/` and `packages/sdk/src/layers/` follows this exact shape.
|
|
6
|
-
|
|
7
|
-
## The shape
|
|
8
|
-
|
|
9
|
-
Every Effect service has three pieces:
|
|
10
|
-
|
|
11
|
-
1. **The service tag** (`Context.Tag`) and interface — under `services/`.
|
|
12
|
-
2. **The live layer** — wires real I/O against `@effect/platform` adapters.
|
|
13
|
-
3. **The test layer** — usually a mutable state container that records what the service was asked to do, with read methods you assert against.
|
|
14
|
-
|
|
15
|
-
You compose the live layer in production wiring (`ReporterLive`, `CliLive`, `McpLive`); you compose the test layer in tests.
|
|
16
|
-
|
|
17
|
-
## Minimal example
|
|
18
|
-
|
|
19
|
-
```typescript
|
|
20
|
-
import { Effect, Layer, Ref } from "effect";
|
|
21
|
-
import { describe, expect, it } from "vitest";
|
|
22
|
-
import { DataStore } from "@vitest-agent/sdk";
|
|
23
|
-
|
|
24
|
-
// Test layer: accumulates writes into a Ref so tests can read them back.
|
|
25
|
-
const DataStoreTest = Layer.effect(
|
|
26
|
-
DataStore,
|
|
27
|
-
Effect.gen(function* () {
|
|
28
|
-
const writes = yield* Ref.make<{ runs: ReadonlyArray<unknown> }>({ runs: [] });
|
|
29
|
-
return DataStore.of({
|
|
30
|
-
writeRun: (input) =>
|
|
31
|
-
Ref.update(writes, (s) => ({ runs: [...s.runs, input] })).pipe(
|
|
32
|
-
Effect.as(1),
|
|
33
|
-
),
|
|
34
|
-
// ... other methods stubbed similarly
|
|
35
|
-
});
|
|
36
|
-
}),
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
it("records the run", async () => {
|
|
40
|
-
const program = Effect.gen(function* () {
|
|
41
|
-
const store = yield* DataStore;
|
|
42
|
-
yield* store.writeRun({ project: "x" });
|
|
43
|
-
// assert via your accumulator pattern
|
|
44
|
-
});
|
|
45
|
-
await Effect.runPromise(program.pipe(Effect.provide(DataStoreTest)));
|
|
46
|
-
});
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Why not mock the FileSystem directly?
|
|
50
|
-
|
|
51
|
-
Layer-level swapping is the testable seam Effect was designed around. Mocking `@effect/platform`'s FileSystem implementation works but loses the type checking that ensures your test layer matches the live interface. Layer.effect with the same Tag forces you to satisfy every method.
|
|
52
|
-
|
|
53
|
-
## Anti-patterns
|
|
54
|
-
|
|
55
|
-
- **Don't `Effect.runPromise` inside service implementations** — providing `DataStoreTest` and then having the service call `Effect.runPromise` internally bypasses the layer.
|
|
56
|
-
- **Don't share `Ref`s across tests** — construct the test layer per-test (or use `beforeEach`) so accumulated state doesn't bleed.
|
|
57
|
-
- **Don't assert on layer construction order** — Effect normalizes the merge graph.
|
|
58
|
-
|
|
59
|
-
## See also
|
|
60
|
-
|
|
61
|
-
- `vitest://docs/guide/mocking` — Vitest mocking guide (general background)
|
|
62
|
-
- `vitest-agent://patterns/testing-effect-schema-definitions` — Companion pattern; most service inputs are Schema-typed
|
|
63
|
-
- `packages/sdk/src/layers/DataStoreTest.ts` — The canonical example in this codebase
|
package/resources/index.js
DELETED
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
import { renderPatternsIndex, renderUpstreamIndex } from "./indexes.js";
|
|
2
|
-
import { decodePatternsManifest, decodeUpstreamManifest } from "./manifest-schema.js";
|
|
3
|
-
import { readPattern } from "./patterns.js";
|
|
4
|
-
import { readUpstreamDoc } from "./upstream-docs.js";
|
|
5
|
-
import { Effect } from "effect";
|
|
6
|
-
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
7
|
-
import { existsSync } from "node:fs";
|
|
8
|
-
import { readFile } from "node:fs/promises";
|
|
9
|
-
import { dirname, join } from "node:path";
|
|
10
|
-
import { fileURLToPath } from "node:url";
|
|
11
|
-
|
|
12
|
-
//#region src/resources/index.ts
|
|
13
|
-
/**
|
|
14
|
-
* Convert the readonly ResourceAnnotations decoded from a manifest into
|
|
15
|
-
* the mutable shape the MCP SDK's Annotations type expects. The shape is
|
|
16
|
-
* structurally identical; only the readonly-ness differs.
|
|
17
|
-
*/
|
|
18
|
-
function toSdkAnnotations(a) {
|
|
19
|
-
const out = {};
|
|
20
|
-
if (a.audience !== void 0) out.audience = [...a.audience];
|
|
21
|
-
if (a.priority !== void 0) out.priority = a.priority;
|
|
22
|
-
return out;
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Locate the `public/` directory that holds the served corpus across the
|
|
26
|
-
* source and built layouts. `@savvy-web/bundler` mirrors the package-root
|
|
27
|
-
* `public/` tree next to the emitted chunk (`<pkg>/public/`), so:
|
|
28
|
-
*
|
|
29
|
-
* built (bundle): index.js at dist/<env>/pkg/resources/ -> ../public
|
|
30
|
-
* source (vitest): index.ts at packages/mcp/src/resources/ -> ../../public
|
|
31
|
-
*
|
|
32
|
-
* Probe both and fail loudly when neither carries the patterns index, so a
|
|
33
|
-
* broken build surfaces a clear path list at startup instead of an opaque
|
|
34
|
-
* ENOENT the first time a consumer reads `vitest-agent://patterns/`.
|
|
35
|
-
*/
|
|
36
|
-
function resolveContentRoots() {
|
|
37
|
-
const here = dirname(fileURLToPath(import.meta.url));
|
|
38
|
-
const candidates = [join(here, "..", "public"), join(here, "..", "..", "public")];
|
|
39
|
-
for (const base of candidates) if (existsSync(join(base, "patterns", "_meta.json"))) return {
|
|
40
|
-
vendorRoot: join(base, "vendor", "vitest-docs"),
|
|
41
|
-
patternsRoot: join(base, "patterns")
|
|
42
|
-
};
|
|
43
|
-
throw new Error(`[vitest-agent-mcp] cannot locate the served corpus: no public/patterns/_meta.json found (tried: ${candidates.join(", ")})`);
|
|
44
|
-
}
|
|
45
|
-
async function listManifestPages(vendorRoot) {
|
|
46
|
-
const manifestPath = join(vendorRoot, "manifest.json");
|
|
47
|
-
if (!existsSync(manifestPath)) return [];
|
|
48
|
-
let raw;
|
|
49
|
-
try {
|
|
50
|
-
raw = await readFile(manifestPath, "utf8");
|
|
51
|
-
} catch {
|
|
52
|
-
return [];
|
|
53
|
-
}
|
|
54
|
-
let parsed;
|
|
55
|
-
try {
|
|
56
|
-
parsed = JSON.parse(raw);
|
|
57
|
-
} catch {
|
|
58
|
-
return [];
|
|
59
|
-
}
|
|
60
|
-
const decoded = await Effect.runPromise(decodeUpstreamManifest(parsed).pipe(Effect.catchAll(() => Effect.succeed(null))));
|
|
61
|
-
if (!decoded?.pages) return [];
|
|
62
|
-
return decoded.pages.map((page) => ({
|
|
63
|
-
relativePath: page.path,
|
|
64
|
-
title: page.title,
|
|
65
|
-
description: page.description,
|
|
66
|
-
...page.annotations ? { annotations: page.annotations } : {}
|
|
67
|
-
}));
|
|
68
|
-
}
|
|
69
|
-
async function listPatternEntries(patternsRoot) {
|
|
70
|
-
const metaPath = join(patternsRoot, "_meta.json");
|
|
71
|
-
if (!existsSync(metaPath)) return [];
|
|
72
|
-
let raw;
|
|
73
|
-
try {
|
|
74
|
-
raw = await readFile(metaPath, "utf8");
|
|
75
|
-
} catch {
|
|
76
|
-
return [];
|
|
77
|
-
}
|
|
78
|
-
let parsed;
|
|
79
|
-
try {
|
|
80
|
-
parsed = JSON.parse(raw);
|
|
81
|
-
} catch {
|
|
82
|
-
return [];
|
|
83
|
-
}
|
|
84
|
-
const decoded = await Effect.runPromise(decodePatternsManifest(parsed).pipe(Effect.catchAll(() => Effect.succeed(null))));
|
|
85
|
-
if (!decoded) return [];
|
|
86
|
-
return decoded.patterns.map((p) => ({
|
|
87
|
-
slug: p.slug,
|
|
88
|
-
title: p.title,
|
|
89
|
-
summary: p.summary,
|
|
90
|
-
...p.annotations ? { annotations: p.annotations } : {}
|
|
91
|
-
}));
|
|
92
|
-
}
|
|
93
|
-
function registerAllResources(server) {
|
|
94
|
-
const { vendorRoot, patternsRoot } = resolveContentRoots();
|
|
95
|
-
server.registerResource("vitest_docs_index", "vitest://docs/", {
|
|
96
|
-
title: "Vitest documentation: index",
|
|
97
|
-
description: "Use first when you need any Vitest API, configuration, or behavioral information and aren't sure which page covers it — lists every page in the vendored snapshot grouped by section (api, config, guide) so you can pick the right `vitest://docs/<path>` URI before fetching.",
|
|
98
|
-
mimeType: "text/markdown"
|
|
99
|
-
}, async (uri) => {
|
|
100
|
-
const result = await renderUpstreamIndex(vendorRoot);
|
|
101
|
-
return { contents: [{
|
|
102
|
-
uri: uri.href,
|
|
103
|
-
mimeType: result.mimeType,
|
|
104
|
-
text: result.content
|
|
105
|
-
}] };
|
|
106
|
-
});
|
|
107
|
-
server.registerResource("vitest_docs_page", new ResourceTemplate("vitest://docs/{+path}", { list: async () => {
|
|
108
|
-
return { resources: (await listManifestPages(vendorRoot)).map((page) => ({
|
|
109
|
-
name: `vitest_docs_${page.relativePath.replace(/\//g, "_")}`,
|
|
110
|
-
uri: `vitest://docs/${page.relativePath}`,
|
|
111
|
-
title: page.title ?? page.relativePath,
|
|
112
|
-
description: page.description ?? `Vitest docs page: ${page.relativePath}`,
|
|
113
|
-
mimeType: "text/markdown",
|
|
114
|
-
...page.annotations ? { annotations: toSdkAnnotations(page.annotations) } : {}
|
|
115
|
-
})) };
|
|
116
|
-
} }), {
|
|
117
|
-
title: "Vitest Documentation Page",
|
|
118
|
-
description: "A single page from the vendored vitest.dev docs.",
|
|
119
|
-
mimeType: "text/markdown"
|
|
120
|
-
}, async (uri, variables) => {
|
|
121
|
-
const path = variables.path;
|
|
122
|
-
const result = await readUpstreamDoc(vendorRoot, Array.isArray(path) ? path.join("/") : String(path));
|
|
123
|
-
return { contents: [{
|
|
124
|
-
uri: uri.href,
|
|
125
|
-
mimeType: result.mimeType,
|
|
126
|
-
text: result.content
|
|
127
|
-
}] };
|
|
128
|
-
});
|
|
129
|
-
server.registerResource("vitest_agent_patterns_index", "vitest-agent://patterns/", {
|
|
130
|
-
title: "vitest-agent patterns: index",
|
|
131
|
-
description: "Use first when you need a curated vitest-agent pattern and want to discover what's available — lists every pattern slug with its title and one-line summary so you can pick the right `vitest-agent://patterns/<slug>` URI before fetching.",
|
|
132
|
-
mimeType: "text/markdown"
|
|
133
|
-
}, async (uri) => {
|
|
134
|
-
const result = await renderPatternsIndex(patternsRoot);
|
|
135
|
-
return { contents: [{
|
|
136
|
-
uri: uri.href,
|
|
137
|
-
mimeType: result.mimeType,
|
|
138
|
-
text: result.content
|
|
139
|
-
}] };
|
|
140
|
-
});
|
|
141
|
-
server.registerResource("vitest_agent_pattern", new ResourceTemplate("vitest-agent://patterns/{slug}", { list: async () => {
|
|
142
|
-
return { resources: (await listPatternEntries(patternsRoot)).map((p) => ({
|
|
143
|
-
name: `vitest_agent_pattern_${p.slug.replace(/[^A-Za-z0-9]/g, "_")}`,
|
|
144
|
-
uri: `vitest-agent://patterns/${p.slug}`,
|
|
145
|
-
title: p.title,
|
|
146
|
-
description: p.summary,
|
|
147
|
-
mimeType: "text/markdown",
|
|
148
|
-
...p.annotations ? { annotations: toSdkAnnotations(p.annotations) } : {}
|
|
149
|
-
})) };
|
|
150
|
-
} }), {
|
|
151
|
-
title: "vitest-agent Pattern",
|
|
152
|
-
description: "A single curated pattern from the vitest-agent project.",
|
|
153
|
-
mimeType: "text/markdown"
|
|
154
|
-
}, async (uri, variables) => {
|
|
155
|
-
const slug = variables.slug;
|
|
156
|
-
const result = await readPattern(patternsRoot, Array.isArray(slug) ? slug[0] : String(slug));
|
|
157
|
-
return { contents: [{
|
|
158
|
-
uri: uri.href,
|
|
159
|
-
mimeType: result.mimeType,
|
|
160
|
-
text: result.content
|
|
161
|
-
}] };
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
//#endregion
|
|
166
|
-
export { registerAllResources, resolveContentRoots };
|
package/resources/indexes.js
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { readFile, readdir } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
|
|
4
|
-
//#region src/resources/indexes.ts
|
|
5
|
-
async function listMarkdownPages(root) {
|
|
6
|
-
const out = [];
|
|
7
|
-
async function walk(rel) {
|
|
8
|
-
const entries = await readdir(join(root, rel), { withFileTypes: true });
|
|
9
|
-
for (const entry of entries) {
|
|
10
|
-
if (entry.name.startsWith(".") || entry.name.startsWith("_")) continue;
|
|
11
|
-
const next = rel ? `${rel}/${entry.name}` : entry.name;
|
|
12
|
-
if (entry.isDirectory()) await walk(next);
|
|
13
|
-
else if (entry.isFile() && entry.name.endsWith(".md") && entry.name !== "ATTRIBUTION.md") out.push(next.replace(/\.md$/, ""));
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
await walk("");
|
|
17
|
-
out.sort();
|
|
18
|
-
return out;
|
|
19
|
-
}
|
|
20
|
-
async function renderUpstreamIndex(vendorRoot) {
|
|
21
|
-
const manifestRaw = await readFile(join(vendorRoot, "manifest.json"), "utf8");
|
|
22
|
-
const manifest = JSON.parse(manifestRaw);
|
|
23
|
-
const pages = await listMarkdownPages(vendorRoot);
|
|
24
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
25
|
-
for (const page of pages) {
|
|
26
|
-
const slash = page.indexOf("/");
|
|
27
|
-
const section = slash === -1 ? "root" : page.slice(0, slash);
|
|
28
|
-
const list = grouped.get(section) ?? [];
|
|
29
|
-
list.push(page);
|
|
30
|
-
grouped.set(section, list);
|
|
31
|
-
}
|
|
32
|
-
const lines = [];
|
|
33
|
-
lines.push("# Vitest Documentation (Upstream Snapshot)");
|
|
34
|
-
lines.push("");
|
|
35
|
-
lines.push(`Snapshotted from [vitest-dev/vitest](https://github.com/vitest-dev/vitest) at tag \`${manifest.tag}\` (commit \`${manifest.commitSha.slice(0, 12)}\`).`);
|
|
36
|
-
lines.push("");
|
|
37
|
-
lines.push("Fetch any page by URI: `vitest://docs/<path>` (e.g., `vitest://docs/api/mock`).");
|
|
38
|
-
lines.push("");
|
|
39
|
-
const sections = [...grouped.keys()].sort();
|
|
40
|
-
for (const section of sections) {
|
|
41
|
-
lines.push(`## ${section}`);
|
|
42
|
-
lines.push("");
|
|
43
|
-
for (const page of grouped.get(section) ?? []) {
|
|
44
|
-
const display = page.startsWith(`${section}/`) ? page.slice(section.length + 1) : page;
|
|
45
|
-
lines.push(`- \`vitest://docs/${page}\` — ${display}`);
|
|
46
|
-
}
|
|
47
|
-
lines.push("");
|
|
48
|
-
}
|
|
49
|
-
return {
|
|
50
|
-
content: lines.join("\n"),
|
|
51
|
-
mimeType: "text/markdown"
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
async function renderPatternsIndex(patternsRoot) {
|
|
55
|
-
const metaRaw = await readFile(join(patternsRoot, "_meta.json"), "utf8");
|
|
56
|
-
const meta = JSON.parse(metaRaw);
|
|
57
|
-
const lines = [];
|
|
58
|
-
lines.push("# vitest-agent Curated Patterns");
|
|
59
|
-
lines.push("");
|
|
60
|
-
lines.push("Hand-written patterns specific to the vitest-agent project. Fetch any pattern by URI: `vitest-agent://patterns/<slug>`.");
|
|
61
|
-
lines.push("");
|
|
62
|
-
for (const pattern of meta.patterns) {
|
|
63
|
-
lines.push(`## ${pattern.title}`);
|
|
64
|
-
lines.push("");
|
|
65
|
-
lines.push(pattern.summary);
|
|
66
|
-
lines.push("");
|
|
67
|
-
lines.push(`- URI: \`vitest-agent://patterns/${pattern.slug}\``);
|
|
68
|
-
lines.push("");
|
|
69
|
-
}
|
|
70
|
-
return {
|
|
71
|
-
content: lines.join("\n"),
|
|
72
|
-
mimeType: "text/markdown"
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
//#endregion
|
|
77
|
-
export { renderPatternsIndex, renderUpstreamIndex };
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { Schema } from "effect";
|
|
2
|
-
|
|
3
|
-
//#region src/resources/manifest-schema.ts
|
|
4
|
-
const RELATIVE_PATH = /^[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)*$/;
|
|
5
|
-
/**
|
|
6
|
-
* MCP 2025-11-25 resource annotations. Both sub-fields are optional so a
|
|
7
|
-
* partially-annotated manifest decodes cleanly during an editorial pass.
|
|
8
|
-
*
|
|
9
|
-
* `audience` is the set of client roles a resource is relevant to; today
|
|
10
|
-
* only `assistant` is meaningful for the vitest-agent MCP server.
|
|
11
|
-
* `priority` is a float in [0, 1] that lets a client rank or filter
|
|
12
|
-
* results before pulling content into context. See the editorial guide
|
|
13
|
-
* in `docs/superpowers/specs/2.0-resource-annotations.md` for the
|
|
14
|
-
* priority bands per content type.
|
|
15
|
-
*/
|
|
16
|
-
const ResourceAnnotations = Schema.Struct({
|
|
17
|
-
audience: Schema.optional(Schema.Array(Schema.Literal("user", "assistant"))),
|
|
18
|
-
priority: Schema.optional(Schema.Number.pipe(Schema.between(0, 1)))
|
|
19
|
-
});
|
|
20
|
-
const ManifestPage = Schema.Struct({
|
|
21
|
-
path: Schema.String.pipe(Schema.pattern(RELATIVE_PATH)),
|
|
22
|
-
title: Schema.NonEmptyString,
|
|
23
|
-
description: Schema.NonEmptyString,
|
|
24
|
-
annotations: Schema.optional(ResourceAnnotations)
|
|
25
|
-
});
|
|
26
|
-
const UpstreamManifest = Schema.Struct({
|
|
27
|
-
tag: Schema.NonEmptyString,
|
|
28
|
-
commitSha: Schema.NonEmptyString,
|
|
29
|
-
capturedAt: Schema.NonEmptyString,
|
|
30
|
-
source: Schema.NonEmptyString,
|
|
31
|
-
pages: Schema.optional(Schema.Array(ManifestPage))
|
|
32
|
-
});
|
|
33
|
-
const decodeUpstreamManifest = Schema.decodeUnknown(UpstreamManifest);
|
|
34
|
-
const encodeUpstreamManifest = Schema.encodeUnknown(UpstreamManifest);
|
|
35
|
-
const SLUG_PATTERN = /^[A-Za-z0-9._]+(?:[/-][A-Za-z0-9._]+)*$/;
|
|
36
|
-
const PatternEntry = Schema.Struct({
|
|
37
|
-
slug: Schema.String.pipe(Schema.pattern(SLUG_PATTERN)),
|
|
38
|
-
title: Schema.NonEmptyString,
|
|
39
|
-
summary: Schema.NonEmptyString,
|
|
40
|
-
annotations: Schema.optional(ResourceAnnotations)
|
|
41
|
-
});
|
|
42
|
-
const PatternsManifest = Schema.Struct({ patterns: Schema.Array(PatternEntry) });
|
|
43
|
-
const decodePatternsManifest = Schema.decodeUnknown(PatternsManifest);
|
|
44
|
-
|
|
45
|
-
//#endregion
|
|
46
|
-
export { ManifestPage, PatternEntry, PatternsManifest, ResourceAnnotations, UpstreamManifest, decodePatternsManifest, decodeUpstreamManifest };
|
package/resources/paths.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { isAbsolute, normalize, resolve, sep } from "node:path";
|
|
2
|
-
|
|
3
|
-
//#region src/resources/paths.ts
|
|
4
|
-
/**
|
|
5
|
-
* Resolves a user-provided relative path against a vendored root,
|
|
6
|
-
* appending `.md` if missing and rejecting traversal attempts.
|
|
7
|
-
*/
|
|
8
|
-
function resolveResourcePath(root, relativePath) {
|
|
9
|
-
if (relativePath === "") return root;
|
|
10
|
-
if (relativePath.includes("\0")) throw new Error("path contains null byte");
|
|
11
|
-
if (isAbsolute(relativePath)) throw new Error("absolute path not allowed");
|
|
12
|
-
const stripped = relativePath.replace(/^\/+/, "");
|
|
13
|
-
const resolved = resolve(root, normalize(stripped.endsWith(".md") ? stripped : `${stripped}.md`));
|
|
14
|
-
const rootWithSep = root.endsWith(sep) ? root : `${root}${sep}`;
|
|
15
|
-
if (!resolved.startsWith(rootWithSep) && resolved !== root) throw new Error("path escapes vendor root");
|
|
16
|
-
return resolved;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
//#endregion
|
|
20
|
-
export { resolveResourcePath };
|
package/resources/patterns.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
|
|
4
|
-
//#region src/resources/patterns.ts
|
|
5
|
-
const SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
6
|
-
async function readPattern(patternsRoot, slug) {
|
|
7
|
-
if (!SLUG_PATTERN.test(slug)) throw new Error(`invalid slug: ${slug}`);
|
|
8
|
-
const absPath = join(patternsRoot, `${slug}.md`);
|
|
9
|
-
try {
|
|
10
|
-
return {
|
|
11
|
-
content: await readFile(absPath, "utf8"),
|
|
12
|
-
mimeType: "text/markdown"
|
|
13
|
-
};
|
|
14
|
-
} catch (err) {
|
|
15
|
-
const code = err.code;
|
|
16
|
-
if (code === "ENOENT" || code === "EISDIR") throw new Error(`pattern not found: ${slug}. See vitest-agent://patterns/ for the index.`);
|
|
17
|
-
throw err;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
//#endregion
|
|
22
|
-
export { readPattern };
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { resolveResourcePath } from "./paths.js";
|
|
2
|
-
import { readFile } from "node:fs/promises";
|
|
3
|
-
|
|
4
|
-
//#region src/resources/upstream-docs.ts
|
|
5
|
-
async function readUpstreamDoc(vendorRoot, relativePath) {
|
|
6
|
-
const absPath = resolveResourcePath(vendorRoot, relativePath);
|
|
7
|
-
let content;
|
|
8
|
-
try {
|
|
9
|
-
content = await readFile(absPath, "utf8");
|
|
10
|
-
} catch (err) {
|
|
11
|
-
const code = err.code;
|
|
12
|
-
if (code === "ENOENT" || code === "EISDIR") throw new Error(`upstream doc not found: ${relativePath}`);
|
|
13
|
-
throw err;
|
|
14
|
-
}
|
|
15
|
-
return {
|
|
16
|
-
content,
|
|
17
|
-
mimeType: "text/markdown"
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
//#endregion
|
|
22
|
-
export { readUpstreamDoc };
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
# Test Artifacts <Advanced /> <Version type="experimental">4.0.11</Version> <Experimental />
|
|
2
|
-
|
|
3
|
-
::: warning
|
|
4
|
-
This is an advanced API. As a user, you most likely want to use [test annotations](/guide/test-annotations) to add notes or context to your tests instead. This is primarily used internally and by library authors.
|
|
5
|
-
:::
|
|
6
|
-
|
|
7
|
-
Test artifacts allow attaching or recording structured data, files, or metadata during test execution. This is a low-level feature primarily designed for:
|
|
8
|
-
|
|
9
|
-
- Internal use ([`annotate`](/guide/test-annotations) is built on top of the artifact system)
|
|
10
|
-
- Framework authors creating custom testing tools on top of Vitest
|
|
11
|
-
|
|
12
|
-
Each artifact includes:
|
|
13
|
-
|
|
14
|
-
- A type discriminator which is a unique identifier for the artifact type
|
|
15
|
-
- Custom data, can be any relevant information
|
|
16
|
-
- Optional attachments, either files or inline content associated with the artifact
|
|
17
|
-
- A source code location indicating where the artifact was created
|
|
18
|
-
|
|
19
|
-
Vitest automatically manages attachment serialization (files are copied to [`attachmentsDir`](/config/attachmentsdir)) and injects source location metadata, so you can focus on the data you want to record. All artifacts **must** extend from [`TestArtifactBase`](#testartifactbase) and all attachments from [`TestAttachment`](#testattachment) to be correctly handled internally.
|
|
20
|
-
|
|
21
|
-
## API
|
|
22
|
-
|
|
23
|
-
### `recordArtifact` <Experimental /> {#recordartifact}
|
|
24
|
-
|
|
25
|
-
::: warning
|
|
26
|
-
`recordArtifact` is an experimental API. Breaking changes might not follow SemVer, please pin Vitest's version when using it.
|
|
27
|
-
|
|
28
|
-
The API surface may change based on feedback. We encourage you to try it out and share your experience with the team.
|
|
29
|
-
:::
|
|
30
|
-
|
|
31
|
-
```ts
|
|
32
|
-
function recordArtifact<Artifact extends TestArtifact>(task: Test, artifact: Artifact): Promise<Artifact>
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
The `recordArtifact` function records an artifact during test execution and returns it. It expects a [task](/api/advanced/runner#tasks) as the first parameter and an object assignable to [`TestArtifact`](#testartifact) as the second.
|
|
36
|
-
|
|
37
|
-
::: info
|
|
38
|
-
Artifacts must be recorded before the task is reported. Any artifacts recorded after that will not be included in the task.
|
|
39
|
-
:::
|
|
40
|
-
|
|
41
|
-
When an artifact is recorded on a test, it emits an `onTestArtifactRecord` runner event and a [`onTestCaseArtifactRecord` reporter event](/api/advanced/reporters#ontestcaseartifactrecord). To retrieve recorded artifacts from a test case, use the [`artifacts()`](/api/advanced/test-case#artifacts) method.
|
|
42
|
-
|
|
43
|
-
Note: annotations, [even though they're built on top of this feature](#relationship-with-annotations), won't appear in the `task.artifacts` array for backwards compatibility reasons until the next major version.
|
|
44
|
-
|
|
45
|
-
### `TestArtifact`
|
|
46
|
-
|
|
47
|
-
The `TestArtifact` type is a union containing all artifacts Vitest can produce, including custom ones. All artifacts extend from [`TestArtifactBase`](#testartifactbase)
|
|
48
|
-
|
|
49
|
-
### `TestArtifactBase` <Experimental /> {#testartifactbase}
|
|
50
|
-
|
|
51
|
-
```ts
|
|
52
|
-
export interface TestArtifactBase {
|
|
53
|
-
/** File or data attachments associated with this artifact */
|
|
54
|
-
attachments?: TestAttachment[]
|
|
55
|
-
/** Source location where this artifact was created */
|
|
56
|
-
location?: TestArtifactLocation
|
|
57
|
-
}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
The `TestArtifactBase` interface is the base for all test artifacts.
|
|
61
|
-
|
|
62
|
-
Extend this interface when creating custom test artifacts. Vitest automatically manages the `attachments` array and injects the `location` property to indicate where the artifact was created in your test code.
|
|
63
|
-
|
|
64
|
-
::: danger
|
|
65
|
-
When running with [`api.allowWrite`](/config/api#api-allowwrite) or [`browser.api.allowWrite`](/config/browser/api#api-allowwrite) disabled, Vitest empties the `attachments` array on every artifact before reporting it.
|
|
66
|
-
|
|
67
|
-
If your custom artifact narrows the `attachments` type (e.g. to a tuple), include `| []` in the union so the type reflects what actually happens at runtime.
|
|
68
|
-
:::
|
|
69
|
-
|
|
70
|
-
### `TestAttachment`
|
|
71
|
-
|
|
72
|
-
```ts
|
|
73
|
-
export interface TestAttachment {
|
|
74
|
-
/** MIME type of the attachment (e.g., 'image/png', 'text/plain') */
|
|
75
|
-
contentType?: string
|
|
76
|
-
/** File system path to the attachment */
|
|
77
|
-
path?: string
|
|
78
|
-
/** Inline attachment content as a string or raw binary data */
|
|
79
|
-
body?: string | Uint8Array
|
|
80
|
-
/**
|
|
81
|
-
* @experimental
|
|
82
|
-
* How the string `body` is encoded.
|
|
83
|
-
* - `'base64'` (default): body is already base64-encoded
|
|
84
|
-
* - `'utf-8'`: body is a utf8 string
|
|
85
|
-
*/
|
|
86
|
-
bodyEncoding?: 'base64' | 'utf-8'
|
|
87
|
-
}
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
The `TestAttachment` interface represents a file or data attachment associated with a test artifact.
|
|
91
|
-
|
|
92
|
-
Attachments can be either file-based (via `path`) or inline content (via `body`). The `contentType` helps consumers understand how to interpret the attachment data.
|
|
93
|
-
|
|
94
|
-
If you pass a string `body`, Vitest assumes it is already base64-encoded unless you set `bodyEncoding: 'utf-8'`. When you pass `body` as a `Uint8Array`, Vitest automatically encodes it as base64. The `bodyEncoding` option only applies to inline `body` attachments, not `path` attachments.
|
|
95
|
-
|
|
96
|
-
### `TestArtifactLocation`
|
|
97
|
-
|
|
98
|
-
```ts
|
|
99
|
-
export interface TestArtifactLocation {
|
|
100
|
-
/** Line number in the source file (1-indexed) */
|
|
101
|
-
line: number
|
|
102
|
-
/** Column number in the line (1-indexed) */
|
|
103
|
-
column: number
|
|
104
|
-
/** Path to the source file */
|
|
105
|
-
file: string
|
|
106
|
-
}
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
The `TestArtifactLocation` interface represents the source code location information for a test artifact. It indicates where in the source code the artifact originated from.
|
|
110
|
-
|
|
111
|
-
### `TestArtifactRegistry`
|
|
112
|
-
|
|
113
|
-
The `TestArtifactRegistry` interface is a registry for custom test artifact types.
|
|
114
|
-
|
|
115
|
-
Augmenting this interface using [TypeScript's module augmentation feature](https://typescriptlang.org/docs/handbook/declaration-merging#module-augmentation) allows registering custom artifact types that tests can produce.
|
|
116
|
-
|
|
117
|
-
Each custom artifact should extend [`TestArtifactBase`](#testartifactbase) and include a unique `type` discriminator property.
|
|
118
|
-
|
|
119
|
-
Here are a few guidelines or best practices to follow:
|
|
120
|
-
|
|
121
|
-
- Try using a `Symbol` as the **registry key** to guarantee uniqueness
|
|
122
|
-
- The `type` property should follow the pattern `'package-name:artifact-name'`, **`'internal:'` is a reserved prefix**
|
|
123
|
-
- Use `attachments` to include files or data; extend [`TestAttachment`](#testattachment) for custom metadata
|
|
124
|
-
- If you narrow the `attachments` type (e.g. to a tuple), include `| []` in the union since Vitest may empty the array at runtime (see [`TestArtifactBase`](#testartifactbase))
|
|
125
|
-
- `location` property is automatically injected
|
|
126
|
-
|
|
127
|
-
## Custom Artifacts
|
|
128
|
-
|
|
129
|
-
To use and manage artifacts in a type-safe manner, you need to create its type and register it:
|
|
130
|
-
|
|
131
|
-
```ts
|
|
132
|
-
import type { TestArtifactBase, TestAttachment } from 'vitest'
|
|
133
|
-
|
|
134
|
-
interface A11yReportAttachment extends TestAttachment {
|
|
135
|
-
contentType: 'text/html'
|
|
136
|
-
path: string
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
interface AccessibilityArtifact extends TestArtifactBase {
|
|
140
|
-
type: 'a11y:report'
|
|
141
|
-
passed: boolean
|
|
142
|
-
wcagLevel: 'A' | 'AA' | 'AAA'
|
|
143
|
-
attachments: [A11yReportAttachment] | []
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const a11yReportKey = Symbol('report')
|
|
147
|
-
|
|
148
|
-
declare module 'vitest' {
|
|
149
|
-
interface TestArtifactRegistry {
|
|
150
|
-
[a11yReportKey]: AccessibilityArtifact
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
As long as the types are assignable to their bases and don't have errors, everything should work fine and you should be able to record artifacts using [`recordArtifact`](#recordartifact):
|
|
156
|
-
|
|
157
|
-
```ts
|
|
158
|
-
async function toBeAccessible(
|
|
159
|
-
this: MatcherState,
|
|
160
|
-
actual: Element,
|
|
161
|
-
wcagLevel: 'A' | 'AA' | 'AAA' = 'AA'
|
|
162
|
-
): AsyncExpectationResult {
|
|
163
|
-
const report = await runAccessibilityAudit(actual, wcagLevel)
|
|
164
|
-
|
|
165
|
-
await recordArtifact(this.task, {
|
|
166
|
-
type: 'a11y:report',
|
|
167
|
-
passed: report.violations.length === 0,
|
|
168
|
-
wcagLevel,
|
|
169
|
-
attachments: [{
|
|
170
|
-
contentType: 'text/html',
|
|
171
|
-
path: report.path,
|
|
172
|
-
}],
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
return {
|
|
176
|
-
pass: violations.length === 0,
|
|
177
|
-
message: () => `Found ${report.violations.length} accessibility violation(s)`
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
## Relationship with Annotations
|
|
183
|
-
|
|
184
|
-
Test annotations are built on top of the artifact system. When using annotations in tests, they create `internal:annotation` artifacts under the hood. However, annotations are:
|
|
185
|
-
|
|
186
|
-
- Simpler to use
|
|
187
|
-
- Designed for end-users, not developers
|
|
188
|
-
|
|
189
|
-
Use annotations if you just want to add notes to your tests. Use artifacts if you need custom data.
|