butr

Testing

@usebutr/testing: fake adapters and fake persistence so unit tests never touch a real wallet or localStorage.

@usebutr/testing provides deterministic test doubles. Install it as a dev dependency:

npm install --save-dev @usebutr/testing

createFakeAdapter(options?): WalletAdapter

Every method resolves to a deterministic stub. Override individual methods after construction to inject failure modes.

import { createFakeAdapter } from "@usebutr/testing";

const adapter = createFakeAdapter({ id: "metamask" });
adapter.connect = () => Promise.reject(new Error("user rejected"));

FakeAdapterOptions

FieldDefault
id"fake"
name"Fake Wallet"
chainPlatform"evm"
accounts[]
icon
capabilitiesall true (merged over your overrides)

Stub returns include: getAccount() → first account or null; sendTx()"0xfakehash"; signMessage(msg){ signature: msg, signedMessage: msg }; getBalance(){ decimals: 18, formatted: "0", symbol: "ETH" | "SOL", value: 0n }; subscribe() → no-op unsubscribe.

Wire it through createConnector to test the store or React hooks:

const adapter = createFakeAdapter({ id: "fake" });
const config: WalletManagerConfig = {
  connectors: [{ id: "fake", name: "Fake", chainPlatform: "evm" }],
  createConnector: (id) => (id === "fake" ? adapter : null),
};

createFakeConnectedWallet(options?): ConnectedWallet

A pool entry: what usePool, useActiveWallet, and useConnectedWallets hand your components, and therefore what UI tests render. Accounts are built through core's buildAccount, so the <chain>:<address> id format stays in one place instead of being restated in every fixture.

import { createFakeConnectedWallet } from "@usebutr/testing";

const wallet = createFakeConnectedWallet({ chainPlatform: "svm", id: "phantom" });
render(<WalletCard wallet={wallet} />);

FakeConnectedWalletOptions

Every FakeAdapterOptions field, plus:

FieldDefault
chainthe platform's mainnet (eip155:1, solana:mainnet, sui:mainnet, …)
addressesone deterministic address for the platform
adapterbuilt from the adapter options

accounts[0] is always account, matching the invariant on ConnectedWallet. Pass adapter to wrap a connector you already built, in which case the entry takes its chainPlatform and the adapter-shaping options are ignored:

const adapter = createFakeAdapter({ id: "phantom", chainPlatform: "svm" });
adapter.signMessage = () => Promise.reject(new Error("user rejected"));
const wallet = createFakeConnectedWallet({ adapter });

createFakePersistence(seed?): WalletPersistence

In-memory WalletPersistence mirroring WalletStorage's shape: no localStorage, no cookies. Reads/writes resolve synchronously (wrapped in Promise.resolve).

import { createFakePersistence } from "@usebutr/testing";

const persistence = createFakePersistence({
  activeConnectorId: "fake",
  pool: {
    fake: {
      account: seedAccount,
      accounts: [seedAccount],
      chainPlatform: "evm",
      connectorId: "fake",
      name: "Fake Wallet",
    },
  },
  selection: { evm: "fake" },
  userDisconnected: true,
});

FakePersistenceSeed fields: pool, selection, activeConnectorId, userDisconnected. Pass it as the storage in WalletManagerConfig to test hydration and persistence without side effects. clearAll() resets everything.

Source: packages/testing/src (fake-adapter.ts, fake-persistence.ts, and their __tests__) in the butr repository.

On this page