Skip to main content
Level: Advanced · Tags: Testing, Vitest, Golden transcripts, Flows Features shown: Testing (createTestBot, MockProvider, expectReply, expectNoReply) · Golden transcripts (redactTranscript, serializeTranscript, assertTranscript) · Flows (.ask with validate, .timeout) · Slash commands · Terminal simulator (defineBot)

Prerequisites

Node.js 22.12 or newer and an ESM project ("type": "module"), as in Getting started.
The project:

The bot under test

The complete test file

Run it:
The first run passes and writes tests/golden/order.json. Commit that file: from then on the last test replays it against createBot on every run.

How it works

1. One setup function, two entry points

setupBot(bot) registers everything on whatever Bot it receives. The factory (createBot) wraps it for npx botmesh dev and node bot.js, and the tests call it on the bot that createTestBot() builds. The tests and the real bot never drift apart.

2. Conversations with createTestBot

createTestBot() starts a Bot on a MockProvider: no phone, no network. chat.send() is a user message; chat.expectReply() waits for a reply that contains the text (or matches a RegExp) and fails with a readable TestKitError listing what the bot did send. chat.expectNoReply() proves the bot stays quiet. Stop each bot in afterEach so no timer outlives the test.

3. Recording the golden transcript

chat.transcript() returns the exchange in the golden-transcript format. redactTranscript() replaces ids and display names with placeholders (<user:1>, <name:1>), and phone numbers or emails the user typed, so the file is safe to commit. serializeTranscript() writes stable, diff-friendly JSON. Set UPDATE_GOLDEN=1 to record it again after an intended change.

4. Commands win

/cancel comes from slashCommandsPlugin. Commands always beat an active conversation, so the user can always leave a flow; the test proves the flow is really gone because 2 reaches the catch-all.

5. Replaying it

assertTranscript(golden, createBot) builds a fresh bot with the factory, replays every inbound turn and compares each reply. A changed, missing or extra reply throws a TranscriptMismatchError with an expected-vs-actual report. For replies that change on every run (dates, ids), pass masks: assertTranscript(golden, createBot, { masks: [REPLAY_MASKS.isoDate] }).

The golden file

The start of tests/golden/order.json after the first run:

Sample transcript

The same bot by hand, captured with npx botmesh dev bot.js:

Run it on WhatsApp or Telegram

node bot.js connects to WhatsApp: scan the QR with the phone that will be the bot. For Telegram, install @botmesh/telegram and pass new TelegramProvider({ token: process.env.TELEGRAM_TOKEN }) to createBot. The tests do not change: they never touch a real channel.

Next steps

  • Testing: several users and groups, Vitest matchers and the chaos kit
  • Golden transcripts: recording from production, redaction rules and replay options
  • Build a plugin: a plugin you can cover with the same tools