Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ try {
(error as InvalidAddressError).chain; // "bitcoin"
}

identify("0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984").matches.length; // 13
const { matches } = identify("0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984");
matches.every((chain) => chain.type === "evm"); // true, and that's as far as a 0x address narrows
create("bitcoin").decimals; // 8
metadata("decred").symbol; // "DCR", read from a table, no Decred class behind it
```
Expand Down
30 changes: 30 additions & 0 deletions test/run-cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { createCli } from "@agntn/tools/cli";
import { vi } from "vite-plus/test";
import { cliOptions } from "../src/cli-commands.ts";

/** What one command line wrote and how it exited. */
export interface Run {
readonly code: typeof process.exitCode;
readonly stderr: string;
readonly stdout: string;
}

/**
* Runs one command line through the CLI the bin builds and collects both streams.
*
* @param args - Words after `chains`.
* @returns {Promise<Run>} The exit code and what reached stdout and stderr, trailing newline cut.
*/
export async function run(...args: readonly string[]): Promise<Run> {
const written = { stdout: "", stderr: "" };
for (const stream of ["stdout", "stderr"] as const) {
vi.spyOn(process[stream], "write").mockImplementation((chunk: unknown) => {
written[stream] += String(chunk);
return true;
});
}
await createCli(cliOptions).run(args);
const code = process.exitCode;
vi.restoreAllMocks();
return { code, stdout: written.stdout.trimEnd(), stderr: written.stderr.trimEnd() };
}
30 changes: 1 addition & 29 deletions test/unit/cli.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { spawn } from "node:child_process";
import { once } from "node:events";
import { resolve as resolvePath } from "node:path";
import { createCli } from "@agntn/tools/cli";
import { afterEach, describe, expect, it, vi } from "vite-plus/test";
import { cliOptions } from "../../src/cli-commands.ts";
import { Chain, register, type ChainKey } from "../../src/index.ts";
import { run } from "../run-cli.ts";

/** No built-in chain lacks a txid check any more, so the CLI message needs a registered stand-in. */
class Unvalidated extends Chain {
Expand All @@ -21,33 +20,6 @@ const ESCAPE = String.fromCodePoint(27);
const CSI = String.fromCodePoint(155);
const CONTROL = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u;

/** What one command line wrote and how it exited. */
interface Run {
readonly code: typeof process.exitCode;
readonly stderr: string;
readonly stdout: string;
}

/**
* Runs one command line through the CLI the bin builds and collects both streams.
*
* @param args - Words after `chains`.
* @returns {Promise<Run>} The exit code and what reached stdout and stderr, trailing newline cut.
*/
async function run(...args: readonly string[]): Promise<Run> {
const written = { stdout: "", stderr: "" };
for (const stream of ["stdout", "stderr"] as const) {
vi.spyOn(process[stream], "write").mockImplementation((chunk: unknown) => {
written[stream] += String(chunk);
return true;
});
}
await createCli(cliOptions).run(args);
const code = process.exitCode;
vi.restoreAllMocks();
return { code, stdout: written.stdout.trimEnd(), stderr: written.stderr.trimEnd() };
}

afterEach(() => {
vi.restoreAllMocks();
process.exitCode = undefined;
Expand Down
33 changes: 33 additions & 0 deletions test/unit/readme.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { readFileSync } from "node:fs";
import { afterEach, describe, expect, it } from "vite-plus/test";
import { run } from "../run-cli.ts";

/** One command the README runs and the output it prints right under it. */
const TRANSCRIPT = /```bash\n(?:npx @agntn\/chains|chains) ([^\n]+)\n```\n\n```\n(.+?)\n```/gsu;

const readme = readFileSync(new URL("../../README.md", import.meta.url), "utf8");

const transcripts = [...readme.matchAll(TRANSCRIPT)].map(
([, command = "", printed = ""]) => [command, printed] as const,
);

afterEach(() => {
process.exitCode = undefined;
});

describe("README transcripts", () => {
it("finds every command the README answers", () => {
expect(transcripts.map(([command]) => command)).toEqual([
"info matic",
"identify 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984",
"identify bc1qjvm9jkrjw9uvsn8905dwa6eau0guyc9laau03a",
"validate btc 1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN3",
]);
});

it.each(transcripts)("chains %s prints what the README shows", async (command, printed) => {
const { stderr, stdout } = await run(...command.split(" "));

expect(stdout || stderr).toBe(printed);
});
});
Loading