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:
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 ofsrc/ 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 thepermissionsthe 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.
↻ 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:
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:
node:assert works as in any test:
src/checks/strikes.ts
src/checks/strikes.test.ts
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: acooldown 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.