JavaScript testkit reference
This guide is for JavaScript and TypeScript developers who want to run tests against a local Cardano devnet without starting Docker or a JVM test fixture.
@bloxbean/yano-testkit starts the native Yano binary in devnet mode, waits for
the HTTP API to be ready, and gives your test code a small helper object for
funding addresses, querying chain state, moving devnet time, snapshots, rollback,
transaction submission, and assertions.
What You Get
Section titled “What You Get”- A real Yano native process started from your JS test.
- RocksDB-backed devnet storage by default, isolated in a temporary directory.
- Random free HTTP and node-to-node ports by default.
- Yano’s production Blockfrost-compatible HTTP API under
/api/v1/. - Devnet-only helpers for faucet funding, time travel, snapshots, and rollback.
- No wallet custody inside Yano. Your JS app or Cardano SDK owns keys and signs transactions.
Install
Section titled “Install”npm install --save-dev @bloxbean/yano-testkitFor preview releases:
npm install --save-dev @bloxbean/yano-testkit@previewThe latest npm tag is reserved for stable releases. Until a stable package is
promoted, install prereleases explicitly with @preview.
The package depends on optional platform packages that contain the native Yano binary for the local OS and CPU. You normally do not need to set any binary environment variable after installing from npm.
Supported native packages:
- Linux x64
- Linux arm64
- macOS arm64
- Windows x64
Node.js 20.8 or newer is required.
Minimal Node Test
Section titled “Minimal Node Test”import assert from "node:assert/strict";import { test } from "node:test";import { startYanoDevnet } from "@bloxbean/yano-testkit";
test("starts a Yano devnet", async () => { const yano = await startYanoDevnet({ blockTimeMillis: 200 });
try { const tip = await yano.queries.tip(); assert.ok(tip);
await yano.time.advanceSlots(3); await yano.assertions.slotAtLeast(3); } finally { await yano.stop(); }});Plain Node.js tests must call await yano.stop(). Put it in a finally block
so the native process is stopped even when an assertion fails.
Vitest
Section titled “Vitest”Use the Vitest adapter when one devnet should be shared by a suite:
import { describe, expect, test } from "vitest";import { yanoDevnet } from "@bloxbean/yano-testkit/vitest";
const yano = yanoDevnet({ blockTimeMillis: 200 });
describe("my Cardano app", () => { test("reads the chain tip", async () => { const tip = await yano.queries.tip(); expect(tip).toBeTruthy(); });});The adapter starts Yano in beforeAll() and stops it in afterAll().
Devnet Object
Section titled “Devnet Object”startYanoDevnet() returns:
const yano = await startYanoDevnet();
console.log(yano.baseUrl); // http://127.0.0.1:<port>/console.log(yano.apiBaseUrl); // http://127.0.0.1:<port>/api/v1/console.log(yano.n2nPort);console.log(yano.storage.path);console.log(yano.workDir);Important fields and helpers:
baseUrl- root Quarkus URL, useful for health endpoints such as/q/health/ready.apiBaseUrl- Yano’s Blockfrost-compatible API root.url(path)- creates a URL underapiBaseUrl.logs()- returns the recent Yano stdout/stderr log tail.stop()- stops the native process and cleans temporary storage.process- the underlying NodeChildProcess, mainly for diagnostics.
Start Options
Section titled “Start Options”const yano = await startYanoDevnet({ blockTimeMillis: 200, timeoutMs: 60_000, networkMagic: 42, httpPort: 0, n2nPort: 0, storage: "temp-rocksdb", onStdout: line => console.log(`[yano] ${line}`), onStderr: line => console.error(`[yano] ${line}`)});Common options:
blockTimeMillis- devnet block production interval.timeoutMs- startup readiness timeout.httpPortandn2nPort- set fixed ports, or omit them for random free ports.networkMagic- protocol magic for the devnet.storage-"temp-rocksdb"or"persistent-rocksdb".workDir/storagePath- explicit directories for persistent tests.preserveWorkDir- keep temporary work directory afterstop().binaryPath- path to a local Yano binary.cwd- source directory containingconfig/when using a local app build.env- extra environment variables for the Yano process.extraArgs- extra-D...JVM/native image system properties.onStdout/onStderr- stream Yano logs into your test output.
Storage Modes
Section titled “Storage Modes”The default is temp-rocksdb:
const yano = await startYanoDevnet({ storage: "temp-rocksdb"});This uses real RocksDB storage in a test-owned temporary directory. The wrapper
copies cwd/config or the packaged platform config into that directory and
runs Yano from the copy, so devnet time-travel and genesis rewrites do not
mutate installed package files. The wrapper deletes the directory when stop()
succeeds.
The JavaScript testkit is RocksDB-only. It does not expose an in-memory storage mode.
Use persistent storage when you need to inspect chain state after shutdown:
const yano = await startYanoDevnet({ storage: "persistent-rocksdb", workDir: "./tmp/yano-case-1"});persistent-rocksdb requires workDir or storagePath.
Wallets And Funding
Section titled “Wallets And Funding”Yano HTTP does not own wallets, mnemonics, private keys, or signing callbacks. That boundary is intentional. Create test wallets with your normal Cardano JS library, then fund their addresses through the devnet faucet.
const address = "addr_test1...";
const result = await yano.faucet.fundAddress(address, 1000);console.log(result.tx_hash, result.index, result.lovelace);Funding helpers:
await yano.faucet.fundAddress(address, 1000); // ADAawait yano.faucet.fundAddressLovelace(address, 1_000_000n);
await yano.faucet.fundAll([ { address: alice, ada: 1000 }, { address: bob, lovelace: 2_000_000n }]);fundAll() is sequential and non-atomic. If one funding request fails, previous
funding transactions are not rolled back.
There is also a compatibility alias:
await yano.fundAddress(address, 1000);Use With Cardano JavaScript SDKs
Section titled “Use With Cardano JavaScript SDKs”Yano exposes a Blockfrost-compatible API at yano.apiBaseUrl. Cardano JS
libraries that can use a Blockfrost-style provider should point at that URL.
MeshJS example:
import { BlockfrostProvider, MeshWallet } from "@meshsdk/core";
const provider = new BlockfrostProvider(yano.apiBaseUrl);const wallet = new MeshWallet({ networkId: 0, fetcher: provider, submitter: provider, key: { type: "mnemonic", words: testMnemonicWords }});
await wallet.init();
const address = await wallet.getChangeAddress();const funding = await yano.faucet.fundAddress(address, 20);await yano.await.untilTxVisible(funding.tx_hash);After funding, build and sign transactions with the SDK, then submit through the
SDK provider or through yano.transactions.
Query Chain State
Section titled “Query Chain State”Use yano.queries for common Blockfrost-compatible and Yano query endpoints:
const status = await yano.queries.status();const tip = await yano.queries.tip();const config = await yano.queries.config();const latestBlock = await yano.queries.latestBlock();const protocolParams = await yano.queries.protocolParameters();
const utxos = await yano.queries.utxosByAddress(address);const tx = await yano.queries.tx(txHash);const txUtxos = await yano.queries.txUtxos(txHash);protocolParameters() calls /api/v1/epochs/latest/parameters. With the
default devnet profile, protocol parameters come from Yano’s epoch-param
tracker. If a test disables tracking:
const yano = await startYanoDevnet({ extraArgs: ["-Dyano.epoch-params.tracking-enabled=false"]});then Yano falls back to static protocol-param.json content for the
Blockfrost-compatible parameters endpoint.
Useful derived queries:
const slot = await yano.queries.currentSlot();const block = await yano.queries.currentBlockNumber();const epoch = await yano.queries.currentEpoch();const epochStart = await yano.queries.epochStartSlot(2);Response objects preserve Yano’s HTTP JSON shape. For Blockfrost-compatible
endpoints this usually means snake_case fields such as tx_hash,
output_index, and block_number.
Devnet Time
Section titled “Devnet Time”Use time helpers to move the devnet quickly:
await yano.time.advanceSlots(5);await yano.time.advanceSeconds(10);await yano.time.advanceEpochs(1);
await yano.time.advanceToSlot(50);await yano.time.advanceToEpoch(2);await yano.time.crossEpochBoundary();advanceToSlot(), advanceToEpoch(), and crossEpochBoundary() are
best-effort helpers. They read current chain state and then request a relative
advance. When a producer is running, the chain can move during that calculation.
Follow them with an await helper or assertion when the exact final state matters:
await yano.time.advanceToEpoch(2);await yano.await.untilEpochAtLeast(2);Epoch maintenance helpers:
await yano.time.shiftGenesisAndStartProducer(3);await yano.time.catchUpToWallClock();Snapshots And Rollback
Section titled “Snapshots And Rollback”Create snapshots around destructive test cases:
await yano.snapshots.create("before-case");
try { await runCase();} finally { await yano.snapshots.restore("before-case");}Or use withSnapshot():
await yano.snapshots.withSnapshot("case", async () => { await yano.time.advanceSlots(10); await yano.devnet.rollback({ count: 1 });});Other snapshot helpers:
const snapshots = await yano.snapshots.list();const exists = await yano.snapshots.exists("case");await yano.snapshots.delete("case");Rollback can target exactly one of slot, blockNumber, or count:
await yano.devnet.rollback({ count: 1 });await yano.devnet.rollback({ slot: 20 });await yano.devnet.rollback({ blockNumber: 5 });Submit And Evaluate Transactions
Section titled “Submit And Evaluate Transactions”Build and sign transactions in your app or Cardano JS SDK. Submit the serialized transaction through Yano:
const txHash = await yano.transactions.submitHex(signedTxHex);await yano.await.untilTxVisible(txHash);For CBOR bytes:
const txHash = await yano.transactions.submitCbor(signedTxCborBytes);Submit and wait in one call:
const txHash = await yano.transactions.submitAndAwait(signedTxHex, { timeoutMs: 20_000, pollIntervalMs: 200});Evaluate transactions:
const result = await yano.transactions.evaluateHex(unsignedOrSignedTxHex);The MeshJS example in test-examples/yano-testkit-meshjs shows the complete
flow: create a Mesh wallet, fund it, build a self-transfer, sign, submit, wait,
and verify.
Await Helpers
Section titled “Await Helpers”Await helpers poll until the condition is true or a timeout is reached:
await yano.await.untilReady();await yano.await.untilSlotAtLeast(10);await yano.await.untilBlockAtLeast(5);await yano.await.untilEpochAtLeast(1);await yano.await.untilTxVisible(txHash);Override polling per call:
await yano.await.untilTxVisible(txHash, { timeoutMs: 30_000, pollIntervalMs: 200});Custom condition:
await yano.await.until( async () => (await yano.queries.utxosByAddress(address)).length > 0, "address to have at least one UTXO");Assertions
Section titled “Assertions”Assertions are runner-neutral. They throw normal errors, so they work with Node’s test runner, Vitest, Jest, Mocha, and custom runners.
await yano.assertions.nodeIsRunning();await yano.assertions.runtimeNotDegraded();await yano.assertions.slotAtLeast(10);await yano.assertions.blockAtLeast(5);await yano.assertions.epochAtLeast(1);await yano.assertions.snapshotExists("case");await yano.assertions.snapshotMissing("case");Address assertions:
await yano.assertions.address(address).hasAtLeastAda(1000);await yano.assertions.address(address).hasAtLeast(1_000_000n);await yano.assertions.address(address).hasExactly(2_000_000n);
const balance = await yano.assertions.address(address).balanceLovelace();ADA helpers accept JavaScript numbers for convenience. Use lovelace helpers with
bigint or string values when exact arithmetic matters.
Low-Level HTTP Client
Section titled “Low-Level HTTP Client”Use yano.client when a named helper does not exist:
const params = await yano.client.getJson("epochs/latest/parameters");const bytes = await yano.client.getBytes("devnet/genesis/download");Available methods:
getJson(path, options)postJson(path, body, options)deleteJson(path, options)postCbor(path, body, options)postText(path, body, options)getBytes(path, options)
The path is relative to yano.apiBaseUrl.
Error Handling
Section titled “Error Handling”HTTP failures throw YanoHttpError:
import { YanoHttpError } from "@bloxbean/yano-testkit";
try { await yano.queries.tx("missing");} catch (error) { if (error instanceof YanoHttpError) { console.error(error.method, error.url, error.status); console.error(error.bodyText); }}Startup failures include the recent Yano log tail in the error message. You can also print logs after a failed test:
try { await runTest();} catch (error) { console.error(yano?.logs()); throw error;}Use a downloaded native binary
Section titled “Use a downloaded native binary”The npm platform package normally supplies the native binary. To select a particular release, download and extract a matching native ZIP, then point the testkit at that binary and its containing directory:
const yano = await startYanoDevnet({ binaryPath: "/absolute/path/to/yano-native-0.1.0-pre12-macos-arm64/yano", cwd: "/absolute/path/to/yano-native-0.1.0-pre12-macos-arm64"});Use the path for your platform; on Windows the executable is yano.exe. Keep config/ beside the executable. Choose a testkit version compatible with the selected binary; overriding it does not add newer endpoints to an older release.
YANO_TESTKIT_BINARY can also override binary discovery. If you need an unreleased native build, follow the separate source-build guide.
TypeScript
Section titled “TypeScript”The package ships TypeScript declarations:
import { startYanoDevnet, type YanoDevnet } from "@bloxbean/yano-testkit";
let yano: YanoDevnet | undefined;
yano = await startYanoDevnet();No separate @types package is needed.
Troubleshooting
Section titled “Troubleshooting”The process does not start
Section titled “The process does not start”- Increase
timeoutMsor setYANO_TESTKIT_TIMEOUT_MS. - Pass
onStdoutandonStderrto see Yano logs while the test runs. - Print
yano.logs()in failure handlers when a process started but the test later failed. - For local binaries, verify
cwdpoints to a directory containingconfig/.
The package cannot find a binary
Section titled “The package cannot find a binary”- Verify the local platform is supported by the optional native packages.
- Reinstall dependencies so npm installs optional dependencies for your OS/CPU.
- For unsupported platforms or local development, set
YANO_TESTKIT_BINARY.
Tests hang after completion
Section titled “Tests hang after completion”- Plain Node.js tests must call
await yano.stop(). - Use
try/finally. - In Vitest, prefer
yanoDevnet()soafterAll()stops the process.
A funded address has no visible UTXO yet
Section titled “A funded address has no visible UTXO yet”- Wait for the faucet transaction:
const result = await yano.faucet.fundAddress(address, 1000);await yano.await.untilTxVisible(result.tx_hash);- Then query UTXOs or use address assertions.
Examples
Section titled “Examples”test-examples/yano-testkit-js- minimal published-package smoke test.test-examples/yano-testkit-meshjs- MeshJS wallet, funding, signed transfer, submit, await, and verification.