Snapshot testing
The snapshotTest method can be used to test stdin, stdout and stderr of
a single test case. It injects data to stdin and snapshots the stdout and
stderr output of each test case separately.
Usage
The snapshotTest method behaves like a combination of Deno.test() and the
assertSnapshot method from the deno std library. The name, meta and fn
options are required.
Basic usage
This example snapshots the output of console.log and console.error.
import { snapshotTest } from "@cliffy/testing";
await snapshotTest({
name: "should log to stdout and stderr",
meta: import.meta,
async fn() {
console.log("foo");
console.error("bar");
},
});
To update the snapshots, run deno test -- --update. This creates a snapshot
file at __snapshots__/[filename].snap with the following content:
export const snapshot = {};
snapshot[`should log to stdout and stderr 1`] = `
"stdout:
foo
stderr:
bar
"
`;
If you now run deno test (without -- --update), it will check if your test
function still has the same output as the snapshot file content has.
Script arguments
Arguments defined with the args option are injected into the test method as
script args. You can simply use Deno.args as you normally would to get the
script arguments.
import { snapshotTest } from "@cliffy/testing";
await snapshotTest({
name: "should log Deno.args",
meta: import.meta,
args: ["--foo", "bar"],
async fn() {
console.log(Deno.args);
},
});
You can use this to create snapshot tests for commands.
import { snapshotTest } from "@cliffy/testing";
import { Command } from "@cliffy/command";
await snapshotTest({
name: "should execute the command with the --foo option",
meta: import.meta,
args: ["--foo", "bar"],
async fn() {
await new Command()
.name("example")
.description("Example command.")
.option("-f, --foo <bar:string>", "Example option.")
.action(({ foo }) => {
console.log("foo: %s", foo);
})
.parse();
},
});
Stdin
The snapshotTest method can inject data to the test function with the stdin
option. You can simply read the data from Deno.stdin as you normally would
when reading data from stdin.
import { snapshotTest } from "@cliffy/testing";
await snapshotTest({
name: "should read cliffy from stdin",
meta: import.meta,
stdin: ["cliffy"],
async fn() {
let name = "";
const decoder = new TextDecoder();
for await (const chunk of Deno.stdin.readable) {
name += decoder.decode(chunk);
if (name === "cliffy") {
break;
}
}
console.log("name:", name);
},
});
You can use this to create snapshot tests for prompts. The ansi module can be
used to generate escape sequences to control the prompt.
import { snapshotTest } from "@cliffy/testing";
import { Select } from "@cliffy/prompt";
import { ansi } from "@cliffy/ansi";
await snapshotTest({
name: "should select a color",
meta: import.meta,
stdin: ansi
.cursorDown
.cursorDown
.text("\n")
.toArray(),
async fn() {
const name = await Select.prompt({
message: "Select a color",
options: ["red", "green", "blue"],
});
console.log("name:", name);
},
});
Test steps
You can also add multiple steps to the test function. The snapshotTest method
then calls the test function once for each step within a separate test step by
calling t.step() from the test context. Each step can have separate options
for stdin, args, and env.
import { snapshotTest } from "@cliffy/testing";
await snapshotTest({
name: "should log to stdout and stderr",
meta: import.meta,
steps: {
"step 1": { args: ["foo"], stdin: ["bar"] },
"step 2": { args: ["beep"], stdin: ["boop"] },
},
async fn() {
console.log(Deno.args);
},
});
You can use the env option to inject environment variables into each step:
import { snapshotTest } from "@cliffy/testing";
await snapshotTest({
name: "should use env vars per step",
meta: import.meta,
steps: {
"step 1": { env: { MY_VAR: "hello" } },
"step 2": { env: { MY_VAR: "world" } },
},
async fn() {
console.log(Deno.env.get("MY_VAR"));
},
});
You can also run only specific steps by setting only: true on a step. When any
step has only: true, all other steps are skipped and the test suite is marked
as failed (same semantics as Deno.test's only option).
import { snapshotTest } from "@cliffy/testing";
await snapshotTest({
name: "run only specific steps",
meta: import.meta,
steps: {
"step 1": { args: ["foo"] },
"step 2": { args: ["bar"], only: true }, // only this step runs
},
async fn() {
console.log(Deno.args);
},
});