Skip to content

Testing Guide

A stub wallet adapter

The SDK does not ship a mock wallet adapter — the package contains no test scaffolding, so nothing here is importable from @tokenflight/swap. Write your own stub against the public IWalletAdapter interface; it is small enough to keep in your test utilities and you stay in control of what it returns.

ts
import type {
  IWalletAdapter,
  WalletAction,
  WalletActionResult,
  WalletActionType,
  WalletEvent,
  WalletEventType,
} from '@tokenflight/swap';

const TEST_ADDRESS = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045';

export class StubWalletAdapter implements IWalletAdapter {
  readonly name = 'Stub Wallet';
  readonly supportedActionTypes: WalletActionType[] = ['eip1193_request'];

  private connected = false;
  private handlers = new Map<WalletEventType, Set<(event: WalletEvent) => void>>();

  async connect(): Promise<void> {
    this.connected = true;
    this.emit({ type: 'connect', data: { address: TEST_ADDRESS } });
  }

  async disconnect(): Promise<void> {
    this.connected = false;
    this.emit({ type: 'disconnect' });
  }

  isConnected(): boolean {
    return this.connected;
  }

  async getAddress(): Promise<string | null> {
    return this.connected ? TEST_ADDRESS : null;
  }

  async executeWalletAction(_action: WalletAction): Promise<WalletActionResult> {
    return { success: true, txHash: `0x${'a'.repeat(64)}` };
  }

  on(event: WalletEventType, handler: (event: WalletEvent) => void): void {
    if (!this.handlers.has(event)) this.handlers.set(event, new Set());
    this.handlers.get(event)!.add(handler);
  }

  off(event: WalletEventType, handler: (event: WalletEvent) => void): void {
    this.handlers.get(event)?.delete(handler);
  }

  private emit(event: WalletEvent): void {
    this.handlers.get(event.type)?.forEach((handler) => handler(event));
  }
}

Extend it per test: reject in connect() to exercise your onSwapError path, return { success: false, error: 'User rejected' } from executeWalletAction() to exercise a declined signature, or emit accountsChanged to check that your UI follows.

Unit testing

Vitest / Jest Example

ts
describe('PaymentWidget', () => {
  let widget: InstanceType<typeof TokenFlightWidget>;
  let container: HTMLDivElement;

  afterEach(() => {
    widget?.destroy();
    container?.remove();
  });

  it('should initialize without errors', () => {
    container = document.createElement('div');
    document.body.appendChild(container);

    const adapter = new StubWalletAdapter();

    widget = new TokenFlightWidget({
      container,
      config: {
        toToken: { chainId: 8453, address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' },
        tradeType: 'EXACT_OUTPUT',
        amount: '100',
        theme: 'dark',
      },
      walletAdapter: adapter,
    });

    expect(() => widget.initialize()).not.toThrow();
  });

  it('should fire onDepositError callback', async () => {
    container = document.createElement('div');
    document.body.appendChild(container);

    let capturedError: SwapErrorData | null = null;

    widget = new TokenFlightWidget({
      container,
      config: {
        toToken: { chainId: 8453, address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' },
        tradeType: 'EXACT_OUTPUT',
        amount: '100',
        theme: 'dark',
      },
      walletAdapter: new StubWalletAdapter(),
      callbacks: {
        onDepositError: (error) => {
          capturedError = error;
        },
      },
    });

    widget.initialize();

    // The callback will fire when an error occurs during widget operation
    // Test your error handling logic here
  });
});

React Testing (React Testing Library)

tsx
it('renders the payment widget container', () => {
  const adapter = new StubWalletAdapter();

  const { container } = render(
    <PaymentWidget walletAdapter={adapter} theme="dark" />
  );

  // The widget mounts inside a Shadow DOM — use container queries
  expect(container.querySelector('div')).toBeTruthy();
});

E2E Testing with Playwright

The SDK's own E2E tests use Playwright with Chromium. You can follow the same pattern:

ts
test('payment widget loads and displays', async ({ page }) => {
  await page.goto('/your-page-with-widget');

  // Wait for the custom element to be defined
  await page.waitForFunction(() =>
    customElements.get('tokenflight-widget') !== undefined
  );

  // Access Shadow DOM content
  const widget = page.locator('tokenflight-widget');
  const shadow = widget.locator('internal:shadow=.tf-container');

  await expect(shadow).toBeVisible();
});

Testnet / Sandbox Mode

⚠️ Coming soon: A dedicated testnet mode with test tokens is not yet available. Currently, the widget connects to the production Hyperstream API.

For development and testing:

  • Use your own stub adapter for automated tests
  • Test with small amounts on supported chains
  • Use the onConnectWallet callback to intercept wallet actions during integration testing

CI Integration

For CI pipelines, attach your stub adapter to the widget on the test page — set it as the __walletAdapter property on the element, or pass it to registerWidgetElement({ walletAdapter }) before the element upgrades:

ts
// playwright.config.ts
export default defineConfig({
  webServer: {
    command: 'pnpm build && pnpm preview',
    port: 4173,
  },
  use: {
    browserName: 'chromium',
  },
});

Key points for CI:

  • Build the project before running E2E tests
  • Chromium is the primary supported browser for testing
  • Shadow DOM selectors need special handling (use locator('internal:shadow=...') in Playwright)
  • API calls can be mocked with Playwright's page.route() for deterministic tests