Testing & ChatMock
The ChatMock utility is a purpose-built simulation wrapper native to the WhatsBotCord library. It allows you to execute your commands locally, feed them synthetic inbound payloads, and assert their exact outbound responses within standard testing frameworks (like bun:test or jest).
This means you don’t need an active WhatsApp connection, a testing device, or QR scans to rigorously validate logic flows!
Core Mechanics
Section titled “Core Mechanics”A standard testing implementation involves three specific phases: Configuration, Simulation Enqueuing, and Assertion Validation.
import { test, expect } from "your-testing-framework";import { ChatMock, SenderType } from "whatsbotcord";// Import the command you want to testimport MyCommand from "./myCommand.js";
test("MyCommand should respond correctly", async () => { // 1. Configuration const chat = new ChatMock(new MyCommand(), { senderType: SenderType.Individual, });
// 2. Simulation Enqueueing (Used if your command awaits input) // chat.EnqueueIncoming_Text("yes");
// 3. Execution await chat.StartChatSimulation();
// 4. Assertion Validation expect(chat.SentFromCommand.Texts).toHaveLength(1); expect(chat.SentFromCommand.Texts[0].text).toEqual("Expected reply!");});1. Configuration (MockingChatParams)
Section titled “1. Configuration (MockingChatParams)”When instantiating ChatMock, you can forge the origin state. The MockingChatParams parameter accepts comprehensive customization overrides:
Chat & Sender Targeting
Section titled “Chat & Sender Targeting”senderType: Simulates whether the command executes inside an individual direct message (SenderType.Individualtargeting@s.whatsapp.netidentifiers) or a group chat (SenderType.Grouptargeting@g.usor@lididentifiers). See WhatsApp IDs to understand how the library differentiates these routing scopes. Defaults toIndividualunless a participant ID is explicitly provided.chatId: The simulated chat ID. If omitted, an ID with the correct WhatsApp suffix (@g.usor@s.whatsapp.net) is automatically generated.participantId_LID: The modern Linked Device identifier (@lid) of the simulated sender. Supplying this automatically scopes the simulation to aGroupsender type.participantId_PN: The standard phone number format ID (@s.whatsapp.net) of the simulated sender.
Message Setup
Section titled “Message Setup”args: Simulates text appended to the command trigger (e.g.,["@user", "value"]). This maps directly to theCommandArgspayload.msgType: The explicit type of the initial command-triggering message. Defaults toMsgType.Text.
Context Overrides
Section titled “Context Overrides”botSettings: Temporarily overwrites internalBotproperties specifically for this test run. Allows you to defineinitialCommandsToAddto pre-register dependent commands in the environment.chatContextConfig: Granular overrides for theChatContextpipeline (e.g.,timeoutSeconds,ignoreSelfMessages, or overriding standard feedback messages likecancelFeedbackMsg).cancelKeywords: An array of string payloads that will immediately trigger an explicit Wait cancellation error during the execution. By default["cancel"].
const chat = new ChatMock(command, { senderType: SenderType.Group, participantId_LID: "12345678@lid", args: ["testArg"], chatContextConfig: { timeoutSeconds: 30 }, botSettings: { initialCommandsToAdd: [{ command: new HelperTargetCMD(), commandType: CommandType.Normal }] }});2. Simulation Enqueuing
Section titled “2. Simulation Enqueuing”If your command implements conversational flows utilizing ctx.WaitText or ctx.WaitMultimedia, the test will pause indefinitely and throw a deadlock error unless you provide the expected simulated responses preemptively.
ChatMock exposes strict enqueuing methods resolving precisely into the awaited flows:
EnqueueIncoming_Text(text): Simulates an incoming raw string.EnqueueIncoming_Img(urlOrOpts)/EnqueueIncoming_Video: Simulates rich media components. You can leverage the parameter configurations to append faux buffers directly viaimgContentBufferMock.EnqueueIncoming_Contact(contacts): Pushes specific virtual card datasets.
test("Multi-step configuration command", async () => { const chat = new ChatMock(new ConfigCommand(), { senderType: SenderType.Group });
// Enqueue what the user would type when the command halts via `WaitText` chat.EnqueueIncoming_Text("Confirm settings"); // Enqueue what the user replies when it halts again via `WaitYesOrNoAnswer` chat.EnqueueIncoming_Text("yes");
// Runs the command logic uninterrupted through the queues! await chat.StartChatSimulation();});3. Assertion Validation
Section titled “3. Assertion Validation”Once StartChatSimulation() finishes resolving, the ChatMock instance formally exposes read-only property structures spanning everything your command did.
Validating Outbound Sends
Section titled “Validating Outbound Sends”The chat.SentFromCommand property offers specific arrays storing chronological outbound payloads issued through the IChatContext:
Texts: Stores all payloads fired viactx.SendText(). Access output strings viachat.SentFromCommand.Texts[0].text.Images/Videos/Audios/Documents/Stickers: Stores multimedia outputs and their captions.ReactedEmojis: Specific emoji metrics logging responses toctx.SendReactEmojiToInitialMsg(),ctx.Ok(), etc.Polls/Locations/Contacts: Complex associative payload objects evaluated inside specific test scopes.
Validating Inbound Requests
Section titled “Validating Inbound Requests”The chat.WaitedFromCommand array explicitly chronicles what WaitX() calls the command initiated, capturing exact timestamp delays and expected return configurations dynamically queued inside your script.
Validating Raw Socket Behavior
Section titled “Validating Raw Socket Behavior”If your command leveraged the raw global bot properties (e.g., executing api.InternalSocket.Send...) rather than relying on context wrappers, you can validate system calls via:
SentFromCommandSocketQueue: Simulated payloads specifically pushed through standard queue management arrays.SentFromCommandSocketWithoutQueue: Simulated payloads directly dispatched entirely bypassing rate-limits.
Full Implementation Reference
Section titled “Full Implementation Reference”Here is a holistic robust test ensuring a command correctly handles missing parameters, confirmation loops, and positive verification outcomes utilizing ChatMock:
import { describe, expect, test } from "your-testing-framework";import { ChatMock, SenderType } from "whatsbotcord";import AddMemberCommand from "./commands/AddMemberCommand.js";
describe("Add Member Command Workflows", () => { test("Should execute failure payload when no arguments are provided", async () => { const command = new AddMemberCommand(); const chat = new ChatMock(command, { senderType: SenderType.Group, args: [], // Represents missing arguments! });
await chat.StartChatSimulation();
// Assert the response texts expect(chat.SentFromCommand.Texts).toHaveLength(1); expect(chat.SentFromCommand.Texts[0].text).toContain("Required arguments missing");
// Assert a Failure emoji reaction was posted over the original command msg expect(chat.SentFromCommand.ReactedEmojis[0].emoji).toBe("❌"); });
test("Should prompt for confirmation and finalize logic upon confirmation", async () => { const command = new AddMemberCommand(); const chat = new ChatMock(command, { senderType: SenderType.Group, args: ["@123456789"], });
// We enqueue 'yes' so the WaitYesOrNoAnswer method resolves correctly automatically chat.EnqueueIncoming_Text("yes"); // We enqueue a generic PNG buffer mimicking a user sending a profile photo chat.EnqueueIncoming_Img({ imgContentBufferMock: Buffer.from("mockImgData") });
await chat.StartChatSimulation();
// Confirm execution sequence text output expect(chat.SentFromCommand.Texts).toHaveLength(2); expect(chat.SentFromCommand.Texts[0].text).toBe("Are you sure you want to add this member? (yes/no)"); expect(chat.SentFromCommand.Texts[1].text).toBe("Member added successfully.");
// Validate a success payload was emitted expect(chat.SentFromCommand.ReactedEmojis[0].emoji).toBe("✅"); });});