LogoRobo.js

Testing API

Testing utilities reference

Complete reference for @robojs/mock/testing utilities.

Session Management

createTestSession

Create an isolated test session:

import { createTestSession } from '@robojs/mock/testing'

const session = await createTestSession(__filename, {
  name: 'my-test',
  config: {
    botUser: { username: 'TestBot' },
    guilds: [{ name: 'Test Server' }]
  }
})
import { createTestSession } from '@robojs/mock/testing'

const session = await createTestSession(__filename, {
  name: 'my-test',
  config: {
    botUser: { username: 'TestBot' },
    guilds: [{ name: 'Test Server' }]
  }
})

Parameters:

ParameterTypeDescription
testFilePathstringPath to test file (use __filename)
options.namestringSession display name
options.ttlnumberSession TTL in ms (default: 3600000 = 1 hour)
options.configobjectSession configuration

Session Config Options:

OptionTypeDefaultDescription
botUserobjectauto-generatedBot user identity (username, id)
guildsarrayone default guildPre-seeded guilds with channels
usersarray-Pre-seeded users
applicationIdstringauto-generatedApplication ID for the bot
commandsarray-Commands to seed in session
maxActionsnumber10000Max recorded actions before LRU eviction
maxLogsnumber10000Max recorded logs before LRU eviction
enforceIntentsbooleanfalseFilter events by declared intents
approvedPrivilegedIntentsbigint-Privileged intents bitmask
permissionEnforcementstring'none'Permission enforcement level

Returns: Promise<TestSession>

TestSession

interface TestSession {
  id: string                    // Session ID
  token: string                 // Bot token (mock:sess_xxx)
  name?: string                 // Display name
  botUser: {
    id: string
    username: string
  }
  guilds: Array<{
    id: string
    name: string
  }>
  channels: Array<{
    id: string
    name: string
    guildId?: string
    type: number
  }>
  guildId: string               // First guild ID
  testFilePath?: string         // Associated test file
  destroy(): Promise<void>      // Cleanup
}
// TestSession shape:
// {
//   id: string,                    // Session ID
//   token: string,                 // Bot token (mock:sess_xxx)
//   name: string,                  // Display name
//   botUser: { id, username },
//   guilds: [{ id, name }],
//   channels: [{ id, name, guildId, type }],
//   guildId: string,               // First guild ID
//   testFilePath: string,          // Associated test file
//   destroy(): Promise<void>       // Cleanup
// }

deleteSession

Delete a session:

import { deleteSession } from '@robojs/mock/testing'

await deleteSession(session.id)
import { deleteSession } from '@robojs/mock/testing'

await deleteSession(session.id)

resetSession

Reset session to initial state:

import { resetSession } from '@robojs/mock/testing'

await resetSession(session.id)
import { resetSession } from '@robojs/mock/testing'

await resetSession(session.id)

Clears messages, actions, and state changes while preserving configuration.

Event Dispatch

dispatchEvent

Inject any Discord Gateway event:

import { dispatchEvent } from '@robojs/mock/testing'

await dispatchEvent(sessionId, 'MESSAGE_CREATE', {
  id: '123456789',
  content: 'Hello',
  channel_id: channelId,
  author: {
    id: userId,
    username: 'TestUser'
  }
})
import { dispatchEvent } from '@robojs/mock/testing'

await dispatchEvent(sessionId, 'MESSAGE_CREATE', {
  id: '123456789',
  content: 'Hello',
  channel_id: channelId,
  author: {
    id: userId,
    username: 'TestUser'
  }
})

Parameters:

ParameterTypeDescription
sessionIdstringTarget session
eventNamestringDiscord event type
dataRecord<string, unknown>Event payload

Returns: Promise<{ success: boolean; dispatched: number }>

dispatchInteraction

Trigger interactions (commands, buttons, etc.):

import { dispatchInteraction } from '@robojs/mock/testing'

// Slash command
await dispatchInteraction(sessionId, {
  type: 2,
  data: {
    name: 'ping',
    type: 1
  },
  channel_id: channelId,
  guild_id: guildId
})

// Button
await dispatchInteraction(sessionId, {
  type: 3,
  data: {
    component_type: 2,
    custom_id: 'my-button'
  },
  channel_id: channelId,
  message: { id: messageId }
})
import { dispatchInteraction } from '@robojs/mock/testing'

// Slash command
await dispatchInteraction(sessionId, {
  type: 2,
  data: {
    name: 'ping',
    type: 1
  },
  channel_id: channelId,
  guild_id: guildId
})

// Button
await dispatchInteraction(sessionId, {
  type: 3,
  data: {
    component_type: 2,
    custom_id: 'my-button'
  },
  channel_id: channelId,
  message: { id: messageId }
})

Interaction Types:

TypeDescription
2Application Command
3Message Component
4Autocomplete
5Modal Submit

Returns: Promise<{ success: boolean; interaction_id: string }>

Assertions

expectAction

Assert an action occurred:

import { expectAction } from '@robojs/mock/testing'

await expectAction(sessionId, {
  description: 'Bot should respond with pong',
  type: 'interaction_response',
  expected: {
    response_data: {
      content: expect.stringContaining('Pong')
    }
  },
  timeout: 5000
})
import { expectAction } from '@robojs/mock/testing'

await expectAction(sessionId, {
  description: 'Bot should respond with pong',
  type: 'interaction_response',
  expected: {
    response_data: {
      content: expect.stringContaining('Pong')
    }
  },
  timeout: 5000
})

Parameters:

ParameterTypeDefaultDescription
descriptionstring-Human-readable description
typeActionType-Action type to match
expectedunknown-Expected data shape
timeoutnumber5000Wait timeout in ms

Returns: Promise<RecordedAction> - The matched action (useful for further assertions)

Action Types:

TypeDescription
interaction_responseInteraction reply
message_sentMessage created via REST
message_editedMessage edited via REST
message_deletedMessage deleted via REST
reaction_addedReaction added to message

These are just the most common action types. See the Complete Action Type Reference below for the full list of 80+ types covering messages, interactions, channels, guilds, voice, and more.

expectNoAction

Assert an action did not occur:

import { expectNoAction } from '@robojs/mock/testing'

await expectNoAction(sessionId, {
  description: 'Bot should not respond',
  type: 'message_sent',
  waitMs: 2000
})
import { expectNoAction } from '@robojs/mock/testing'

await expectNoAction(sessionId, {
  description: 'Bot should not respond',
  type: 'message_sent',
  waitMs: 2000
})

Parameters:

ParameterTypeDefaultDescription
descriptionstring-Human-readable description
typestring-Action type to check
waitMsnumber500Time to wait before asserting (ms)

Note: expectNoAction uses waitMs with a 500ms default, different from expectAction which uses timeout with a 5000ms default.

waitForAction

Wait for actions and return them:

import { waitForAction } from '@robojs/mock/testing'

const actions = await waitForAction(sessionId, {
  type: 'interaction_response',
  timeout: 5000
})

// Inspect result -- use a typed cast to avoid `as any`
const data = actions[0].data as { response_data?: { content?: string } }
expect(data.response_data?.content).toBe('Success')

// Shorthand: pass a string instead of options object
const actions = await waitForAction(sessionId, 'message_sent')
import { waitForAction } from '@robojs/mock/testing'

const actions = await waitForAction(sessionId, {
  type: 'interaction_response',
  timeout: 5000
})

// Inspect result
expect(actions[0].data.response_data?.content).toBe('Success')

// Shorthand: pass a string instead of options object
const actions = await waitForAction(sessionId, 'message_sent')

Options:

// Full options object
interface WaitForActionOptions {
  type: string                                  // Action type to wait for
  timeout?: number                              // Default: 5000ms
  filter?: (action: RecordedAction) => boolean  // Additional filtering
}

// Or pass a string (treated as { type: string })
waitForAction(sessionId, 'message_sent')
// Full options object: { type, timeout, filter }
// Or pass a string (treated as { type: string })
waitForAction(sessionId, 'message_sent')

Returns: Promise<RecordedAction[]>

Avoiding as any casts: Instead of (actions[0].data as any).content, use a typed cast like actions[0].data as { response_data?: { content?: string } }. For common cases, prefer the more specific helpers waitForMessage or waitForInteractionResponse which return targeted results.

waitForAnyAction

Wait for any action matching a filter:

import { waitForAnyAction } from '@robojs/mock/testing'

const action = await waitForAnyAction(sessionId, (action) => {
  return action.type === 'message_sent' &&
         (action.data as any).content?.includes('welcome')
})

// With custom timeout
const action = await waitForAnyAction(
  sessionId,
  (action) => action.type === 'interaction_response',
  10000  // 10 second timeout
)
import { waitForAnyAction } from '@robojs/mock/testing'

const action = await waitForAnyAction(sessionId, (action) => {
  return action.type === 'message_sent' &&
         action.data.content?.includes('welcome')
})

// With custom timeout
const action = await waitForAnyAction(
  sessionId,
  (action) => action.type === 'interaction_response',
  10000  // 10 second timeout
)

Parameters:

ParameterTypeDefaultDescription
sessionIdstring-Session ID
filterfunction-Filter (action: RecordedAction) => boolean
timeoutnumber5000Wait timeout in ms

Returns: Promise<RecordedAction>

waitForMessage

Wait for a message:

import { waitForMessage } from '@robojs/mock/testing'

const action = await waitForMessage(sessionId, {
  channelId: channelId,
  content: /hello/i,  // Optional: filter by content (string or RegExp)
  timeout: 5000
})

// All options are optional
const action = await waitForMessage(sessionId)
import { waitForMessage } from '@robojs/mock/testing'

const action = await waitForMessage(sessionId, {
  channelId: channelId,
  content: /hello/i,  // Optional: filter by content (string or RegExp)
  timeout: 5000
})

// All options are optional
const action = await waitForMessage(sessionId)

Options:

OptionTypeDescription
channelIdstringFilter by channel (optional)
contentstring | RegExpFilter by message content (optional)
timeoutnumberWait timeout in ms (default: 5000)

Returns: Promise<RecordedAction>

waitForInteractionResponse

Wait for an interaction response:

import { waitForInteractionResponse } from '@robojs/mock/testing'

const action = await waitForInteractionResponse(sessionId, {
  type: 4,      // Optional: filter by response type
  timeout: 5000
})
import { waitForInteractionResponse } from '@robojs/mock/testing'

const action = await waitForInteractionResponse(sessionId, {
  type: 4,      // Optional: filter by response type
  timeout: 5000
})

Options:

OptionTypeDescription
typenumberFilter by interaction response type (4 = Reply, 5 = Deferred)
timeoutnumberWait timeout in ms (default: 5000)

Returns: Promise<RecordedAction>

State Inspection

getSessionState

Get current session state:

import { getSessionState } from '@robojs/mock/testing'

const state = await getSessionState(sessionId)

// Inspect state
console.log(state.botUser)
console.log(state.guilds)
console.log(state.channels)
import { getSessionState } from '@robojs/mock/testing'

const state = await getSessionState(sessionId)

// Inspect state
console.log(state.botUser)
console.log(state.guilds)
console.log(state.channels)

getSessionActions

Get recorded actions:

import { getSessionActions } from '@robojs/mock/testing'

// All actions
const result = await getSessionActions(sessionId)
const actions = result.actions

// Filtered by type with limit
const result = await getSessionActions(sessionId, {
  type: 'message_sent',
  limit: 100,
  since: Date.now() - 60000  // Last minute
})
import { getSessionActions } from '@robojs/mock/testing'

// All actions
const result = await getSessionActions(sessionId)
const actions = result.actions

// Filtered by type with limit
const result = await getSessionActions(sessionId, {
  type: 'message_sent',
  limit: 100,
  since: Date.now() - 60000  // Last minute
})

Options:

OptionTypeDescription
typestringFilter by action type
limitnumberMaximum actions to return
sincenumberOnly actions after this timestamp (ms)

Returns: Promise<{ actions: RecordedAction[] }>

getChannelMessages

Get messages from a channel:

import { getChannelMessages } from '@robojs/mock/testing'

// Get last 50 messages (default)
const messages = await getChannelMessages(sessionId, channelId)

// Get last 100 messages
const messages = await getChannelMessages(sessionId, channelId, 100)
import { getChannelMessages } from '@robojs/mock/testing'

// Get last 50 messages (default)
const messages = await getChannelMessages(sessionId, channelId)

// Get last 100 messages
const messages = await getChannelMessages(sessionId, channelId, 100)

Parameters:

ParameterTypeDefaultDescription
sessionIdstring-Session ID
channelIdstring-Channel ID
limitnumber50Maximum messages to return

Returns: Promise<MockMessage[]>

getHistoricalActions

Get all actions including pre-test:

import { getHistoricalActions } from '@robojs/mock/testing'

// Get all historical actions
const allActions = await getHistoricalActions(sessionId)

// Filter by type
const interactions = await getHistoricalActions(sessionId, {
  type: 'interaction_response'
})

// Custom filter
const filtered = await getHistoricalActions(sessionId, {
  filter: (action) => (action.data as any)?.content?.includes('hello')
})
import { getHistoricalActions } from '@robojs/mock/testing'

// Get all historical actions
const allActions = await getHistoricalActions(sessionId)

// Filter by type
const interactions = await getHistoricalActions(sessionId, {
  type: 'interaction_response'
})

// Custom filter
const filtered = await getHistoricalActions(sessionId, {
  filter: (action) => action.data?.content?.includes('hello')
})

Options:

OptionTypeDescription
typestringFilter by action type
filterfunctionCustom filter (action: RecordedAction) => boolean

Returns: Promise<RecordedAction[]>

Bot Lifecycle

startMockRobo

Start a bot connected to mock server:

import { startMockRobo } from '@robojs/mock/testing'

const handle = await startMockRobo({
  name: 'test-bot',
  testFilePath: __filename,
  hmr: true,
  logLevel: 'debug'
})

// ... run tests ...

await handle.stop()
import { startMockRobo } from '@robojs/mock/testing'

const handle = await startMockRobo({
  name: 'test-bot',
  testFilePath: __filename,
  hmr: true,
  logLevel: 'debug'
})

// ... run tests ...

await handle.stop()

Options:

interface StartMockRoboOptions {
  name?: string                                      // Bot name
  port?: number                                      // Server port
  timeout?: number                                   // Connection timeout (default: 30000ms)
  verbose?: boolean                                  // Verbose output
  testFilePath?: string                              // Associated test file
  logFile?: boolean | string                         // Log to file
  logLevel?: 'trace' | 'debug' | 'info' | 'warn' | 'error'
  hmr?: boolean                                      // Hot module replacement
}
// StartMockRoboOptions shape:
// {
//   name: string,          // Bot name
//   port: number,          // Server port
//   timeout: number,       // Connection timeout (default: 30000ms)
//   verbose: boolean,      // Verbose output
//   testFilePath: string,  // Associated test file
//   logFile: boolean | string,  // Log to file
//   logLevel: string,      // 'trace' | 'debug' | 'info' | 'warn' | 'error'
//   hmr: boolean           // Hot module replacement
// }
OptionTypeDefaultDescription
namestring-Bot name
portnumber-Server port
timeoutnumber30000Connection timeout (ms). HMR mode uses 60000ms.
verbosebooleanfalseVerbose output
testFilePathstring-Associated test file
logFileboolean | string-Log to file (true = auto, string = path)
logLevelstringdebugLog verbosity level
hmrbooleanfalseEnable hot module replacement

Returns: Promise<MockRoboHandle>

MockRoboHandle

interface MockRoboHandle {
  client: unknown              // Discord.js client (null when hmr: true)
  sessionId: string            // Session ID
  token: string                // Bot token
  botUser: {                   // Bot user info
    id: string
    username: string
  }
  guilds: Array<{              // Guild list
    id: string
    name: string
  }>
  channels: Array<{            // Channel list
    id: string
    name: string
    guildId?: string
    type: number
  }>
  guildId: string              // First guild ID (convenience)
  stop(): Promise<void>        // Cleanup

  // HMR-only properties (available when hmr: true)
  process?: ChildProcess       // Child process handle
  getHmrCount?: () => number   // Get current HMR reload count
  getRestartCount?: () => number  // Get current restart count
  waitForHmrReload?: (timeout?: number, fromCount?: number) => Promise<void>
  waitForFullRestart?: (timeout?: number, fromCount?: number) => Promise<void>
}
// MockRoboHandle shape:
// {
//   client,              // Discord.js client (null when hmr: true)
//   sessionId,           // Session ID
//   token,               // Bot token
//   botUser: { id, username },
//   guilds: [{ id, name }],
//   channels: [{ id, name, guildId, type }],
//   guildId,             // First guild ID (convenience)
//   stop(),              // Cleanup
//   // HMR-only properties:
//   process,             // Child process handle
//   getHmrCount(),       // Get current HMR reload count
//   getRestartCount(),   // Get current restart count
//   waitForHmrReload(timeout, fromCount),
//   waitForFullRestart(timeout, fromCount)
// }

waitForMockServer

Wait for mock server to be ready:

import { waitForMockServer } from '@robojs/mock/testing'

await waitForMockServer({
  timeout: 30000
})

// With custom URL and poll interval
await waitForMockServer({
  url: 'http://localhost:4000/api/control',
  timeout: 60000,
  interval: 1000  // Check every 1 second
})
import { waitForMockServer } from '@robojs/mock/testing'

await waitForMockServer({
  timeout: 30000
})

// With custom URL and poll interval
await waitForMockServer({
  url: 'http://localhost:4000/api/control',
  timeout: 60000,
  interval: 1000  // Check every 1 second
})

Options:

OptionTypeDefaultDescription
urlstringauto-detectedControl API URL to check
timeoutnumber30000Maximum wait time in ms
intervalnumber500Polling interval in ms

User Testing

TestUsers

Manage test users:

import { TestUsers } from '@robojs/mock/testing'

const users = new TestUsers(session)

// Create users
const alice = users.create('Alice')
const bob = users.create('Bob', { status: 'online' })
const bot = users.create('HelperBot', { bot: true, avatar: 'abc123' })

// Create multiple users at once
const [charlie, diana, eve] = users.createMany(['Charlie', 'Diana', 'Eve'])

// Switch active user
users.switchTo(alice)

// Get users
const current = users.current()
const byName = users.byName('Alice')
const byId = users.byId('123456789')  // Find by user ID
const allHumans = users.allHumans()
const allBots = users.allBots()

// Execute as user
await users.as(bob, async () => {
  // Actions here are from Bob's perspective
})
import { TestUsers } from '@robojs/mock/testing'

const users = new TestUsers(session)

// Create users
const alice = users.create('Alice')
const bob = users.create('Bob', { status: 'online' })
const bot = users.create('HelperBot', { bot: true, avatar: 'abc123' })

// Create multiple users at once
const [charlie, diana, eve] = users.createMany(['Charlie', 'Diana', 'Eve'])

// Switch active user
users.switchTo(alice)

// Get users
const current = users.current()
const byName = users.byName('Alice')
const byId = users.byId('123456789')  // Find by user ID
const allHumans = users.allHumans()
const allBots = users.allBots()

// Execute as user
await users.as(bob, async () => {
  // Actions here are from Bob's perspective
})

create() Options:

OptionTypeDescription
botbooleanCreate as bot user
avatarstring | nullAvatar hash
statusstringUser status: 'online' | 'offline' | 'idle' | 'dnd'

createTestUtils

Create session-bound helpers. Accepts either a Session instance or a sessionId string:

import { startMockRobo, createTestUtils } from '@robojs/mock/testing'

const bot = await startMockRobo({ name: 'test' })
const { users, interactions } = createTestUtils(bot.sessionId)

// Send messages
await interactions.sendMessage(users.current(), channelId, 'Hello')

// Invoke commands
await interactions.invokeCommand(users.current(), 'ping')

// Invoke commands with options
await interactions.invokeCommand(users.current(), 'echo', { message: 'Hello' })

// Click buttons
await interactions.clickButton(users.current(), messageId, 'button-custom-id')

// Simulate a multi-user conversation (user is a username string)
await interactions.conversation(channelId, [
  { user: 'Alice', content: 'Hello everyone!' },
  { user: 'Bob', content: 'Hi Alice!' },
  { user: 'Alice', content: 'How are you?' }
])
import { startMockRobo, createTestUtils } from '@robojs/mock/testing'

const bot = await startMockRobo({ name: 'test' })
const { users, interactions } = createTestUtils(bot.sessionId)

// Send messages
await interactions.sendMessage(users.current(), channelId, 'Hello')

// Invoke commands
await interactions.invokeCommand(users.current(), 'ping')

// Invoke commands with options
await interactions.invokeCommand(users.current(), 'echo', { message: 'Hello' })

// Click buttons
await interactions.clickButton(users.current(), messageId, 'button-custom-id')

// Simulate a multi-user conversation (user is a username string)
await interactions.conversation(channelId, [
  { user: 'Alice', content: 'Hello everyone!' },
  { user: 'Bob', content: 'Hi Alice!' },
  { user: 'Alice', content: 'How are you?' }
])

Parameters:

ParameterTypeDescription
sessionOrIdSession | stringSession instance or session ID string

When given a string, the session is resolved from the in-process session manager. This requires the mock server to be running in the same process (standard mode, not HMR mode).

Returns: TestUtils

Utilities

sleep

Wait for specified duration:

import { sleep } from '@robojs/mock/testing'

await sleep(1000)  // Wait 1 second
import { sleep } from '@robojs/mock/testing'

await sleep(1000)  // Wait 1 second

generateSnowflake

Generate a Discord snowflake ID:

import { generateSnowflake } from '@robojs/mock/testing'

const id = generateSnowflake()  // '1234567890123456789'
import { generateSnowflake } from '@robojs/mock/testing'

const id = generateSnowflake()  // '1234567890123456789'

deepEquals

Deep equality comparison:

import { deepEquals } from '@robojs/mock/testing'

const equal = deepEquals(actual, expected)
import { deepEquals } from '@robojs/mock/testing'

const equal = deepEquals(actual, expected)

generateDiff

Generate diff between values:

import { generateDiff } from '@robojs/mock/testing'

const diff = generateDiff(expected, actual)
import { generateDiff } from '@robojs/mock/testing'

const diff = generateDiff(expected, actual)

clearSessionActions

Clear all recorded actions for a session:

import { clearSessionActions } from '@robojs/mock/testing'

await clearSessionActions(sessionId)
import { clearSessionActions } from '@robojs/mock/testing'

await clearSessionActions(sessionId)

Useful for resetting action history between test phases without resetting the full session state.

mockRestAPI

Make direct REST API calls to the mock server:

import { mockRestAPI } from '@robojs/mock/testing'

// Get user's guilds
const guilds = await mockRestAPI(token, '/users/@me/guilds')

// Create a message directly
const message = await mockRestAPI(token, `/channels/${channelId}/messages`, {
  method: 'POST',
  body: { content: 'Hello from REST!' }
})
import { mockRestAPI } from '@robojs/mock/testing'

// Get user's guilds
const guilds = await mockRestAPI(token, '/users/@me/guilds')

// Create a message directly
const message = await mockRestAPI(token, `/channels/${channelId}/messages`, {
  method: 'POST',
  body: { content: 'Hello from REST!' }
})

Useful for testing REST API interactions directly without going through Discord.js.

controlAPI

Make direct calls to the mock server's control API:

import { controlAPI } from '@robojs/mock/testing'

// Get session info
const session = await controlAPI(`/sessions/${sessionId}`)

// Create a new session
const newSession = await controlAPI('/sessions', {
  method: 'POST',
  body: { name: 'test' }
})

// Delete a session
await controlAPI(`/sessions/${sessionId}`, { method: 'DELETE' })
import { controlAPI } from '@robojs/mock/testing'

// Get session info
const session = await controlAPI(`/sessions/${sessionId}`)

// Create a new session
const newSession = await controlAPI('/sessions', {
  method: 'POST',
  body: { name: 'test' }
})

// Delete a session
await controlAPI(`/sessions/${sessionId}`, { method: 'DELETE' })

Parameters:

ParameterTypeDescription
endpointstringControl API endpoint (e.g., /sessions)
options.methodstringHTTP method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
options.bodyunknownRequest body (automatically serialized to JSON)

Returns: Promise<T> (generic, based on endpoint)

Useful for advanced scenarios where you need direct control over the mock server.

Configuration

configureMock

Configure the mock server connection:

import { configureMock } from '@robojs/mock/testing'

configureMock({
  baseUrl: 'http://localhost:3000/mock',
  defaultTimeout: 10000
})
import { configureMock } from '@robojs/mock/testing'

configureMock({
  baseUrl: 'http://localhost:3000/mock',
  defaultTimeout: 10000
})

getMockConfig

Get current mock configuration:

import { getMockConfig } from '@robojs/mock/testing'

const config = getMockConfig()
// { baseUrl, controlUrl, restUrl, gatewayUrl, defaultTimeout }
import { getMockConfig } from '@robojs/mock/testing'

const config = getMockConfig()
// { baseUrl, controlUrl, restUrl, gatewayUrl, defaultTimeout }

Returns:

interface MockConfig {
  baseUrl: string        // Base URL for the mock server
  controlUrl: string     // Control API URL
  restUrl: string        // REST API URL
  gatewayUrl: string     // Gateway WebSocket URL
  defaultTimeout: number // Default timeout for operations (ms)
}
// MockConfig shape:
// {
//   baseUrl: string,        // Base URL for the mock server
//   controlUrl: string,     // Control API URL
//   restUrl: string,        // REST API URL
//   gatewayUrl: string,     // Gateway WebSocket URL
//   defaultTimeout: number  // Default timeout for operations (ms)
// }

resetMockConfig

Reset configuration to defaults:

import { resetMockConfig } from '@robojs/mock/testing'

resetMockConfig()
import { resetMockConfig } from '@robojs/mock/testing'

resetMockConfig()

RecordedAction

Structure of recorded actions:

interface RecordedAction {
  id: string               // Unique ID
  type: string             // Action type (e.g., 'interaction_response')
  data: unknown            // Action payload
  timestamp: number        // Unix timestamp (ms)
  sequence?: number        // Optional sequence number
}
// RecordedAction shape:
// {
//   id: string,               // Unique ID
//   type: string,             // Action type (e.g., 'interaction_response')
//   data: any,                // Action payload
//   timestamp: number,        // Unix timestamp (ms)
//   sequence: number          // Optional sequence number
// }

At runtime, actions returned from the control API may include additional fields beyond the typed interface, such as endpoint, method, interactionId, responseType, triggeredBy, and metadata. These are useful for debugging but not part of the exported TypeScript type.

Action Types

Action types follow patterns based on their source:

PatternExampleDescription
Interactioninteraction_responseBot replied to an interaction
Messagemessage_sentBot sent/edited/deleted a message via REST
Gatewaygateway_messageGateway WebSocket activity (client to server)
DispatchdispatchEvent dispatched to client (server to client)

waitForMessage internally filters for message_sent type actions. Use waitForAction with interaction_response for slash command responses.

Complete Action Type Reference

Message Actions:

TypeDescription
message_sentMessage created (used by waitForMessage)
message_editedMessage content updated
message_deletedMessage removed
message_pinnedMessage pinned to channel
message_unpinnedMessage unpinned
message_crosspostedMessage published to following channels
messages_bulk_deletedMultiple messages deleted

Interaction Actions:

TypeDescription
interaction_responseBot replied to interaction
interaction_followupFollowup message sent
interaction_editInteraction response edited

Channel Actions:

TypeDescription
channel_createdNew channel created
channel_updatedChannel settings changed
channel_deletedChannel removed
channels_positions_updatedChannel positions reordered
channel_overwrite_updatedPermission overwrite changed
channel_overwrite_deletedPermission overwrite removed

Thread Actions:

TypeDescription
thread_createdNew thread started
thread_updatedThread settings changed
thread_deletedThread removed
thread_member_addedUser joined thread
thread_member_removedUser left thread

Role & Member Actions:

TypeDescription
role_createdNew role created
role_updatedRole settings changed
role_deletedRole removed
role_positions_updatedRole positions reordered
guild_member_addedUser joined guild
guild_member_updatedMember updated
guild_member_removedUser left guild
member_role_addedRole added to member
member_role_removedRole removed from member
member_updatedMember properties changed
member_kickedMember was kicked

Reaction Actions:

TypeDescription
reaction_addedReaction added to message
reaction_removedReaction removed

Voice Actions:

TypeDescription
voice_state_updatedVoice state changed
voice_channel_status_updatedVoice channel status changed

Gateway Actions:

TypeDescription
gateway_identifyBot identified with gateway
gateway_heartbeatHeartbeat sent
gateway_presence_updatePresence updated
gateway_voice_state_updateVoice state update sent
gateway_resumeSession resumed after disconnect
gateway_messageRaw gateway message received
gateway_request_guild_membersGuild members requested
dispatchEvent dispatched to client

Poll Actions:

TypeDescription
poll_voters_fetchedPoll voters retrieved
poll_expiredPoll ended

Sticker Actions:

TypeDescription
sticker_createdNew sticker created
sticker_updatedSticker settings changed
sticker_deletedSticker removed

Webhook Actions:

TypeDescription
webhook_createdNew webhook created
webhook_updatedWebhook settings changed
webhook_deletedWebhook removed
webhook_executedWebhook message sent

Emoji Actions:

TypeDescription
emoji_createdNew emoji created
emoji_updatedEmoji settings changed
emoji_deletedEmoji removed

Stage Instance Actions:

TypeDescription
stage_instance_createdStage instance started
stage_instance_updatedStage instance settings changed
stage_instance_deletedStage instance ended

Ban Actions:

TypeDescription
ban_createdUser banned
ban_removedUser unbanned
bulk_banMultiple users banned

Invite Actions:

TypeDescription
invite_createdInvite created
invite_deletedInvite deleted or revoked

Scheduled Event Actions:

TypeDescription
scheduled_event_createdScheduled event created
scheduled_event_updatedScheduled event updated
scheduled_event_deletedScheduled event deleted

AutoMod Actions:

TypeDescription
automod_rule_createdAutoMod rule created
automod_rule_updatedAutoMod rule updated
automod_rule_deletedAutoMod rule deleted

Other Actions:

TypeDescription
typing_startedTyping indicator sent
rest_requestGeneric REST API request
dm_channel_openedDM channel created
guild_updateGuild settings changed

Additional APIs

recordAssertion

Record an assertion result to the test registry for UI persistence:

import { recordAssertion } from '@robojs/mock/testing'

recordAssertion(sessionId, {
  description: 'Response contains greeting',
  passed: true,
  expected: 'Hello',
  actual: 'Hello',
  diff: undefined
})
import { recordAssertion } from '@robojs/mock/testing'

recordAssertion(sessionId, {
  description: 'Response contains greeting',
  passed: true,
  expected: 'Hello',
  actual: 'Hello',
  diff: undefined
})

Parameters:

ParameterTypeDescription
sessionIdstringSession to record assertion for
resultAssertionResultAssertion result object

Assertions are automatically recorded by expectAction and expectNoAction. Use this directly when implementing custom assertion logic.

createSession

Lower-level session creation without test file tracking:

import { createSession } from '@robojs/mock/testing'

const session = await createSession({
  name: 'my-session',
  config: {
    botUser: { username: 'TestBot' },
    guilds: [{ name: 'Test Guild' }]
  }
})

console.log(session.id)    // Session ID
console.log(session.token) // Bot token
import { createSession } from '@robojs/mock/testing'

const session = await createSession({
  name: 'my-session',
  config: {
    botUser: { username: 'TestBot' },
    guilds: [{ name: 'Test Guild' }]
  }
})

console.log(session.id)    // Session ID
console.log(session.token) // Bot token

Unlike createTestSession, this does not register the session with the test file registry. Use createTestSession for typical test scenarios and createSession when you need manual control over registry tracking.

Parameters:

ParameterTypeDescription
configCreateTestSessionConfigOptional session configuration

Returns: Promise<{ id, token, botUser, guilds, channels, guildId }>

DEFAULT_MOCK_CONFIG

The default MockConfig constant used as a fallback when no explicit configuration is set:

import { DEFAULT_MOCK_CONFIG } from '@robojs/mock/testing'

console.log(DEFAULT_MOCK_CONFIG)
// {
//   baseUrl: 'http://localhost:3000',
//   controlUrl: 'http://localhost:3000/api/control',
//   restUrl: 'http://localhost:3000/api',
//   gatewayUrl: 'ws://localhost:3000',
//   defaultTimeout: 5000
// }
import { DEFAULT_MOCK_CONFIG } from '@robojs/mock/testing'

console.log(DEFAULT_MOCK_CONFIG)
// {
//   baseUrl: 'http://localhost:3000',
//   controlUrl: 'http://localhost:3000/api/control',
//   restUrl: 'http://localhost:3000/api',
//   gatewayUrl: 'ws://localhost:3000',
//   defaultTimeout: 5000
// }

These are fallback values. In practice, getMockConfig() builds URLs dynamically using the ROBO_MOCK_PORT environment variable or server info file.

Types

StartMockRoboOptions

Options for startMockRobo:

interface StartMockRoboOptions {
  name?: string               // Session name
  port?: number               // Mock server port (auto-discovered if not set)
  timeout?: number            // Bot connection timeout in ms (default: 30000)
  verbose?: boolean           // Show logs in console
  testFilePath?: string       // Test file path for registry tracking
  logFile?: boolean | string  // Enable file logging (true = auto, string = path)
  logLevel?: 'trace' | 'debug' | 'info' | 'warn' | 'error'  // Log level (default: 'debug')
  hmr?: boolean               // Enable HMR mode (spawns child process)
}
// StartMockRoboOptions shape:
// {
//   name: string,               // Session name
//   port: number,               // Mock server port (auto-discovered if not set)
//   timeout: number,            // Bot connection timeout in ms (default: 30000)
//   verbose: boolean,           // Show logs in console
//   testFilePath: string,       // Test file path for registry tracking
//   logFile: boolean | string,  // Enable file logging (true = auto, string = path)
//   logLevel: string,           // Log level (default: 'debug')
//   hmr: boolean                // Enable HMR mode (spawns child process)
// }

TestUtils

Object returned by createTestUtils:

interface TestUtils {
  users: TestUsers             // User management utilities
  interactions: TestInteractions  // Interaction simulation utilities
}
// TestUtils shape:
// {
//   users: TestUsers,             // User management utilities
//   interactions: TestInteractions // Interaction simulation utilities
// }

MockMessage

Message returned from getChannelMessages:

interface MockMessage {
  id: string
  content: string
  author: {
    id: string
    username?: string
    bot?: boolean
  }
  [key: string]: unknown       // Additional fields vary by message type
}
// MockMessage shape:
// {
//   id: string,
//   content: string,
//   author: { id, username, bot },
//   ...additionalFields         // Additional fields vary by message type
// }

AssertionResult

Result of a single assertion, used for UI persistence:

interface AssertionResult {
  description: string          // Human-readable description
  passed: boolean              // Whether the assertion passed
  expected: unknown            // Expected value
  actual: unknown              // Actual value
  diff?: string                // Human-readable diff for UI display
}
// AssertionResult shape:
// {
//   description: string,  // Human-readable description
//   passed: boolean,      // Whether the assertion passed
//   expected: any,        // Expected value
//   actual: any,          // Actual value
//   diff: string          // Human-readable diff for UI display
// }

Protocol Constants

Internal constants used by Mock Server:

ConstantValueDescription
Gateway Version10Discord Gateway API version
Heartbeat Interval41250msGateway heartbeat timing
Voice Gateway Port50001Separate port for voice (WSS)
Session TTL3600000ms1 hour default
Cleanup Interval60000msSession cleanup check
Max Actions10000Per-session action limit
LRU Eviction10%Oldest actions removed
Stage Buffer1000Event replay buffer
Loop Threshold10/secEvents before detection
Loop Cooldown5000msPause after loop detected

Activity Commands

Stage WebSocket commands for controlling activity testing. These commands are sent through the Stage WS connection, not the testing API.

launch_activity

Launch an activity in the current session:

// Via Stage WS
{
  command: 'launch_activity',
  data: {
    launch_url: 'http://localhost:5173',
    application_id: '1234567890',
    guild_id: guildId,
    channel_id: channelId
  }
}
// Via Stage WS
{
  command: 'launch_activity',
  data: {
    launch_url: 'http://localhost:5173',
    application_id: '1234567890',
    guild_id: guildId,
    channel_id: channelId
  }
}

Returns frame_id, instance_id, proxy_origin, iframe_url, and sdk_shim_enabled.

close_activity

Close the running activity for the current session:

{
  command: 'close_activity'
}
{
  command: 'close_activity'
}

The session is determined from the WebSocket connection. No data payload is required.

activity_rpc

Forward an RPC message to or from the activity:

{
  command: 'activity_rpc',
  data: {
    message: [1, { cmd: 'GET_USER', nonce: 'abc123' }],
    frame_id: frameId,
    instance_id: instanceId
  }
}
{
  command: 'activity_rpc',
  data: {
    message: [1, { cmd: 'GET_USER', nonce: 'abc123' }],
    frame_id: frameId,
    instance_id: instanceId
  }
}

activity_set_auth_settings

Change the authentication simulation mode:

{
  command: 'activity_set_auth_settings',
  data: {
    mode: 'manual'  // 'auto_approve' | 'auto_deny' | 'manual'
  }
}
{
  command: 'activity_set_auth_settings',
  data: {
    mode: 'manual'  // 'auto_approve' | 'auto_deny' | 'manual'
  }
}

activity_set_platform_state

Update platform state to trigger SDK events:

{
  command: 'activity_set_platform_state',
  data: {
    layout_mode: 1,            // 0=focused, 1=pip, 2=grid
    screen_orientation: 1,     // numeric screen orientation
    orientation: 'landscape',  // 'portrait' | 'landscape'
    thermal_state: 0           // 0=nominal, 1=fair, 2=serious, 3=critical
  }
}
{
  command: 'activity_set_platform_state',
  data: {
    layout_mode: 1,            // 0=focused, 1=pip, 2=grid
    screen_orientation: 1,     // numeric screen orientation
    orientation: 'landscape',  // 'portrait' | 'landscape'
    thermal_state: 0           // 0=nominal, 1=fair, 2=serious, 3=critical
  }
}

Triggers ACTIVITY_LAYOUT_MODE_UPDATE, ORIENTATION_UPDATE, and/or THERMAL_STATE_UPDATE events for subscribed activities.

activity_set_iap_state

Update in-app purchase state:

{
  command: 'activity_set_iap_state',
  data: {
    skus: [{ id: '123', name: 'Item', type: 5, price: { amount: 499, currency: 'usd' } }],
    entitlements: [{ id: '456', sku_id: '123', user_id: userId }]
  }
}
{
  command: 'activity_set_iap_state',
  data: {
    skus: [{ id: '123', name: 'Item', type: 5, price: { amount: 499, currency: 'usd' } }],
    entitlements: [{ id: '456', sku_id: '123', user_id: userId }]
  }
}

activity_set_url_mappings

Update URL mapping rules:

{
  command: 'activity_set_url_mappings',
  data: {
    url_mappings: [
      { prefix: '/api', target: 'localhost:8080' }
    ]
  }
}
{
  command: 'activity_set_url_mappings',
  data: {
    url_mappings: [
      { prefix: '/api', target: 'localhost:8080' }
    ]
  }
}

activity_set_csp_mode

Switch Content Security Policy mode:

{
  command: 'activity_set_csp_mode',
  data: {
    mode: 'discord_strict'  // 'relaxed' | 'discord_strict'
  }
}
{
  command: 'activity_set_csp_mode',
  data: {
    mode: 'discord_strict'  // 'relaxed' | 'discord_strict'
  }
}

activity_emit_event

Manually emit an SDK event to the activity:

{
  command: 'activity_emit_event',
  data: {
    event_name: 'ACTIVITY_LAYOUT_MODE_UPDATE',
    data: { layout_mode: 1 }
  }
}
{
  command: 'activity_emit_event',
  data: {
    event_name: 'ACTIVITY_LAYOUT_MODE_UPDATE',
    data: { layout_mode: 1 }
  }
}

Useful for testing specific event handlers without triggering the full state change flow.

Activity commands require a running activity in the session. Launch an activity first with launch_activity before sending other activity commands.

Package Exports

@robojs/mock provides five export paths for different use cases:

Import PathPurpose
@robojs/mockCore classes, factories, and constants (Session, SessionManager, MockServerState, factory functions, snowflake utilities)
@robojs/mock/sessionSession-related classes for advanced usage (Session, SessionManager, InMemoryStorage, ActionRecorder, RecordingPlayer, MockServerState)
@robojs/mock/testingTesting utilities and Control API helpers (createTestSession, dispatchEvent, expectAction, waitForAction, etc.)
@robojs/mock/testing/jest-reporterCustom Jest reporter for tracking test results in Stage UI
@robojs/mock/typesTypeScript type definitions (Session, SessionConfig, ActionType, ScenarioDefinition, etc.)

Most test files only need the testing export:

import {
  createTestSession,
  dispatchEvent,
  expectAction,
  waitForAction
} from '@robojs/mock/testing'
import {
  createTestSession,
  dispatchEvent,
  expectAction,
  waitForAction
} from '@robojs/mock/testing'

For custom integrations or advanced scenarios, import from the core or session paths:

import { sessionManager, createMockGuild } from '@robojs/mock'
import { MockServerState } from '@robojs/mock/session'
import type { ActionType, ScenarioDefinition } from '@robojs/mock/types'
import { sessionManager, createMockGuild } from '@robojs/mock'
import { MockServerState } from '@robojs/mock/session'

Next Steps

On this page