Testing
composurejs/testing runs your App the way a platform does. Each Interaction is signed with a real Ed25519 key and goes through the full pipeline, while Discord is replaced by a recording transport. Tests never reach Discord.
tsx
import { expect, test } from "vitest"
import { answer, chatInput } from "composurejs/testing"
import { app } from "./app.tsx"
test("the App is valid", () => {
app.check() // duplicate Commands, Discord's rules, ...
})
test("/greet greets", async () => {
const res = await answer(app, chatInput("greet").user("user", { id: "2", username: "bob", global_name: "Bob" }))
expect(res.text).toBe("hi Bob")
})
test("buttons update the message", async () => {
const first = await answer(app, chatInput("counter"))
const next = await answer(app, first.click("Go"))
expect(next.type).toBe(7) // updated in place
})Builders
chatInput("name"), with.option(name, value),.user(..),.role(..),.channel(..),.attachment(..)and.subcommand("group", "sub").autocomplete("name").focused("option", "partial input").userCommand("name").target(user)andmessageCommand("name").target(message).component(customId).values([..])andmodalSubmit(customId).field(name, value).- On any builder:
.by(user),.inGuild(id, member?),.inDm()and.locale("fr").
Responses
A response gives you:
- what came back:
res.text,res.type,res.isEphemeral,res.components,res.allComponents,res.component(customId)andres.files - the next Interaction, from what it shows:
res.click("+1"),res.select(id, ["a"])andres.submit({ reason: "spam" })
Background Work (deferred edits, follow-ups) is held until await res.settle(), which runs it and returns every REST request made:
tsx
import { expect, test } from "vitest"
import { answer, chatInput } from "composurejs/testing"
import { app } from "./app.tsx"
test("/report defers, then edits", async () => {
const res = await answer(app, chatInput("report"))
expect(res.body).toEqual({ type: 5 })
const [edit] = await res.settle()
expect(edit).toMatchObject({ method: "PATCH", json: { content: "done" } })
})Pass { transport: new MockTransport().reply(200, {..}) } to answer to script Discord's replies. sendEvent(app, webhookEvent("ENTITLEMENT_CREATE", data)) delivers a Webhook Event.