Skip to main content
chapterjs test runs your bot in a Discord that lives in memory: one server, a few people, and nothing to connect to. No token, no test server, nothing shown to anyone. It is what the test script of your project runs:
That is all: with nothing written, every command, button, menu, form, event and task of your project is used once, with ordinary values and with hard ones, and must answer without throwing. Then come your scenarios, when you have some: files that say what the bot should answer.
What a scenario can do is in the reference; the exit codes of the command are in the CLI reference.

What is checked by itself

Every file of src/ is loaded, like chapterjs dev does. A file that can not run stops everything, with its line: nothing is tested until it is fixed. Then, in a server where the bot is an admin, each case on a fresh one (so a /ban tried on someone never leaves the next case without them):
  • Every command is used with every option filled in, then with the required ones only, then once per hard value: a text at its longest and in unicode, a number at its lowest, its highest and at zero, each of its choices, a person who left the server, the bot itself, the owner. A command that only works in private messages is used there; one that works in both places is used in both. Someone with the permissions the command asks for uses it.
  • Every button, menu and form is used with its data, then once per hard value of that data: an empty text, a : in it, a huge number. A form is also sent with its required fields only.
  • Every event happens once: a message, someone joining or leaving, a reaction, a new channel… An event the test server can not make happen yet is named with ℹ, and its file is left alone.
  • Every menu command is used on an ordinary message or person, then on the hard ones: a message at its longest in unicode, a message of the owner or of someone who left; the bot, the owner, who uses it, someone who left.
  • Every task runs once, as if its time had come.
  • The presence, when it has functions: each runs once.
The files that pass are one line at the end of these checks: what was checked in them is not listed, since none of it is yours to read. While a file is checked, the terminal shows it with the thing and the case being tried (↻ Checking src/tickets/close.ts, then → button close(), with userId = ""…), and erases that once done. A check passes when the bot answered and nothing threw. A check has 5 seconds to answer, as on Discord (a command that takes longer is deferred by the framework, which counts), then 30 seconds to finish. It fails when a function threw, with the file and the line, when a command finished without answering, or when it ran out of time; only then does a file get its own lines, with the thing named:
What your files print while they are tested (a console.log in an event, for example) stays out of the report: a line saying “online” there would read as the bot being online, and it is not. What was printed is shown under a check that failed, where it can help. What the bot answers is not judged here: that is what scenarios are for.

Scenarios

A scenario is a test you write, in a *.test.ts file anywhere in src/, with scenario() from chapterjs/test. The bot never loads these files; chapterjs test runs them, each on a fresh server with empty stores.
src/checks/pong.ts
src/checks/pong.test.ts
bot is your bot in its test server. bot.use() uses a command as a person would, with its options typed from the file: a person for a user option, a number for an integer one, one of the choices. bot.click() presses a button, bot.select() picks in a menu, bot.submit() sends a form, bot.say() writes a message, bot.join() and bot.leave() make someone come and go, bot.run() runs a task. Each resolves once the bot is done with it. What comes back says what the bot answered: says(), isEphemeral(), hasButton(), opensForm(), updatesMessage()… Each check throws a plain sentence when it fails. The report has one line per test file, and names a scenario only when it failed, with the line of your scenario:
For everything else, node:assert works as in any test:
src/checks/strikes.ts
src/checks/strikes.test.ts
A scenario fails too when a function of the bot threw while it ran, even if every check passed: a person would have seen “Something went wrong”. A menu command is used with on: bot.use(quote, { on: message }), bot.use(avatar, { on: alice }) (see Menu commands). A function that waits for an answer lets bot.use() resolve while it waits: the next bot.say(), bot.click() or bot.react() is what it waits for, and bot.endWaits() ends every wait as if its time was up.

The test server

It has one channel, #general, its owner (bot.owner), the bot as an admin (bot.me), and bot.someone: a person with no special rights, who does everything when nobody is named. bot.member('alice') makes a person the first time the name is given (with permissions or roles when they need some), bot.role() a role and bot.channel() a channel. Everything the bot does is kept where the scenario can read it: channel.messages and channel.last, member.banned, member.timedOut, member.roles, member.privateMessages, channel.threads, thread.archived… And the server refuses what Discord would refuse: a second answer to the same interaction, a member who is not there, a channel that was deleted, and, as on Discord, kicking, banning, renaming or giving a role to the owner or to someone whose highest role is above the bot’s (a role the scenario makes goes below the bot’s, as a new role does on Discord). A form sent without a required field, or a command used without one, is refused before it reaches the bot, as the Discord app would.

What it does not do yet

Time does not pass: a cooldown or a task that runs every so often can not be tested through the clock, and a wait only ends when the test answers it or calls bot.endWaits(). Voice is not there: join() throws at once, and what comes after it is named with ℹ instead of failing. Nor are polls, scheduled events and auto-moderation: an event the server can not make happen is named in the report, and a request for something it does not model answers as Discord does for something that does not exist, so your function sees the same error it would online.

Testing reference

Everything a scenario can do, and what an answer can be checked for.

chapterjs build

Check the types and compile the bot for production.