Expand description
Test helpers for plugin authors.
A plugin’s command is a pure function: it is handed a
Context and answers with a command. That makes it testable without a
project, a host, or a process – and this crate is the two ways of doing it:
- By hand, fast.
contextis the builder, re-exported so a test needs one import. Build the exact context a case is about and assert the answer. - For real.
Fixturewrites a directory of files, runs the real detection over them (the samepmpx-detectthe host uses, with the markers your manifest declares), and hands you the context your plugin would have been called with. This is what catches “my plugin stopped matching this project”.
And one thing a plugin cannot do alone: capture installs a fake host, so a test can assert the
lines the plugin wrote with debug! – which is the only channel its text has.
use pmpx_testkit::{capture, context, Fixture};
// The case itself: two matched files, one of them read.
let ctx = context()
.project_root("/work")
.matched(["pnpm-lock.yaml"])
.file("package.json", "{\"name\":\"x\"}")
.build();
let plan = Mine.command(&ctx, Verb::Install, &[]).unwrap();
assert_eq!(plan.program, "tool");
// The same case, from a directory and the real detection.
let fixture = Fixture::new()
.file("pnpm-lock.yaml", "")
.plugin("mine", "node", &["pnpm-lock.yaml"], &["package.json"])
.declares(["package.json"])
.file("package.json", "{\"name\":\"x\"}");
let ctx = fixture.context();
assert!(ctx.has_matched("pnpm-lock.yaml"));
// And what the plugin says while it runs.
let host = capture();
let out = Mine.command(&ctx, Verb::Install, &[]);
assert!(host.take().is_empty(), "this plugin is quiet");Structs§
- Captured
- A handle on the fake host: install it, run the plugin, assert on the lines.
- Fixture
- One project, on disk, detected the way the host detects it.