chapterjs 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +65 -0
  2. package/dist/cli/cli.js +41 -6
  3. package/dist/cli/project.d.ts +19 -0
  4. package/dist/cli/project.js +18 -8
  5. package/dist/cli/test.d.ts +29 -0
  6. package/dist/cli/test.js +444 -0
  7. package/dist/commands/autocomplete.d.ts +1 -1
  8. package/dist/commands/autocomplete.js +37 -41
  9. package/dist/commands/router.d.ts +4 -1
  10. package/dist/commands/router.js +15 -17
  11. package/dist/components/router.d.ts +7 -2
  12. package/dist/components/router.js +16 -16
  13. package/dist/discord/types/channel.d.ts +9 -1
  14. package/dist/discord/types/guild.d.ts +5 -5
  15. package/dist/discord/types/permissions.d.ts +6 -0
  16. package/dist/discord/types/poll.d.ts +7 -1
  17. package/dist/events/router.d.ts +9 -4
  18. package/dist/events/router.js +24 -11
  19. package/dist/interactions/dispatch.d.ts +5 -1
  20. package/dist/interactions/dispatch.js +6 -2
  21. package/dist/loader/hot.js +22 -2
  22. package/dist/loader/loader.d.ts +7 -0
  23. package/dist/loader/loader.js +22 -12
  24. package/dist/store/memory-backend.d.ts +15 -0
  25. package/dist/store/memory-backend.js +56 -0
  26. package/dist/store/store.js +4 -1
  27. package/dist/tasks/scheduler.d.ts +6 -0
  28. package/dist/tasks/scheduler.js +9 -3
  29. package/dist/testing/failure.d.ts +9 -0
  30. package/dist/testing/failure.js +20 -0
  31. package/dist/testing/fake-discord.d.ts +59 -0
  32. package/dist/testing/fake-discord.js +120 -0
  33. package/dist/testing/fake-gateway.d.ts +59 -0
  34. package/dist/testing/fake-gateway.js +123 -0
  35. package/dist/testing/harness.d.ts +387 -0
  36. package/dist/testing/harness.js +1418 -0
  37. package/dist/testing/index.d.ts +4 -0
  38. package/dist/testing/index.js +5 -0
  39. package/dist/testing/scenario.d.ts +39 -0
  40. package/dist/testing/scenario.js +58 -0
  41. package/dist/testing/smoke.d.ts +20 -0
  42. package/dist/testing/smoke.js +659 -0
  43. package/dist/testing/websocket.d.ts +34 -0
  44. package/dist/testing/websocket.js +167 -0
  45. package/dist/testing/world.d.ts +145 -0
  46. package/dist/testing/world.js +1479 -0
  47. package/package.json +20 -3
@@ -0,0 +1,4 @@
1
+ export { scenario } from './scenario.js';
2
+ export type { ScenarioDeclaration } from './scenario.js';
3
+ export { TestFailure } from './failure.js';
4
+ export type { ButtonTarget, FieldInputsOf, FormTarget, OptionInputsOf, SelectInput, SelectTarget, TestAssertions, TestBot, TestButton, TestChannel, TestChannelOptions, TestChannelType, TestFile, TestForm, TestMember, TestMemberOptions, TestMenu, TestMessage, TestReply, TestRole, TestRoleOptions, TestSuggestion, TestWhere, } from './harness.js';
@@ -0,0 +1,5 @@
1
+ // What `import ... from 'chapterjs/test'` gives: how a test of the bot is
2
+ // written. The bot under test, the people and channels of its server and
3
+ // what it answers are typed here; `chapterjs test` runs the scenarios.
4
+ export { scenario } from './scenario.js';
5
+ export { TestFailure } from './failure.js';
@@ -0,0 +1,39 @@
1
+ import type { Declaration } from '../loader/loader.js';
2
+ import type { TestBot } from './harness.js';
3
+ /** What a file gives to `scenario()`. */
4
+ export interface ScenarioDeclaration {
5
+ /** What the file gave to `scenario()`, not checked yet. */
6
+ readonly config: {
7
+ label: string;
8
+ run: (bot: TestBot) => unknown;
9
+ };
10
+ }
11
+ /** A scenario, checked: what the runner works with. */
12
+ export interface LoadedScenario {
13
+ readonly label: string;
14
+ readonly run: (bot: TestBot) => unknown;
15
+ }
16
+ /**
17
+ * Declares a test of your bot. Export the result from a `*.test.ts` file of
18
+ * `src/`: `chapterjs test` runs it against a Discord in memory, where the
19
+ * bot is in a server with a few people in it.
20
+ *
21
+ * ```ts
22
+ * import { scenario } from 'chapterjs/test';
23
+ * import { ping } from './ping';
24
+ *
25
+ * export default scenario('ping answers Pong', async bot => {
26
+ * const reply = await bot.use(ping);
27
+ * reply.says('Pong');
28
+ * });
29
+ * ```
30
+ *
31
+ * `bot` is the bot in its test server: `bot.use()` runs a command,
32
+ * `bot.click()` presses a button, `bot.say()` sends a message as someone,
33
+ * and what comes back says what the bot answered.
34
+ */
35
+ export declare function scenario(label: string, run: (bot: TestBot) => unknown): ScenarioDeclaration;
36
+ /** Whether a value was made by `scenario()`. */
37
+ export declare function isScenarioDeclaration(value: unknown): value is ScenarioDeclaration;
38
+ /** The `Declaration` of scenarios, for the loader. */
39
+ export declare const scenarioDeclaration: Declaration<LoadedScenario>;
@@ -0,0 +1,58 @@
1
+ // What a file of `src/` declares with `scenario()`: a test of the bot,
2
+ // written by the developer, that `chapterjs test` runs against a Discord
3
+ // in memory. A scenario names itself by its label, since that is what the
4
+ // report shows: a file may export several, in a list too.
5
+ const BRAND = Symbol.for('chapterjs.scenario');
6
+ /**
7
+ * Declares a test of your bot. Export the result from a `*.test.ts` file of
8
+ * `src/`: `chapterjs test` runs it against a Discord in memory, where the
9
+ * bot is in a server with a few people in it.
10
+ *
11
+ * ```ts
12
+ * import { scenario } from 'chapterjs/test';
13
+ * import { ping } from './ping';
14
+ *
15
+ * export default scenario('ping answers Pong', async bot => {
16
+ * const reply = await bot.use(ping);
17
+ * reply.says('Pong');
18
+ * });
19
+ * ```
20
+ *
21
+ * `bot` is the bot in its test server: `bot.use()` runs a command,
22
+ * `bot.click()` presses a button, `bot.say()` sends a message as someone,
23
+ * and what comes back says what the bot answered.
24
+ */
25
+ export function scenario(label, run) {
26
+ return Object.freeze({ [BRAND]: true, config: { label, run } });
27
+ }
28
+ /** Whether a value was made by `scenario()`. */
29
+ export function isScenarioDeclaration(value) {
30
+ return (typeof value === 'object' &&
31
+ value !== null &&
32
+ value[BRAND] === true);
33
+ }
34
+ const fail = (message) => {
35
+ throw new TypeError(message);
36
+ };
37
+ /** The longest label shown on one line of the report. */
38
+ const MAX_LABEL = 120;
39
+ /** The `Declaration` of scenarios, for the loader. */
40
+ export const scenarioDeclaration = {
41
+ one: 'scenario',
42
+ many: 'scenarios',
43
+ is: isScenarioDeclaration,
44
+ list: true,
45
+ read(value) {
46
+ const { config } = value;
47
+ if (typeof config.label !== 'string' || config.label.trim() === '') {
48
+ fail(`scenario() needs a label first, what the test checks: scenario('ping answers Pong', async bot => { ... }).`);
49
+ }
50
+ if (config.label.length > MAX_LABEL) {
51
+ fail(`The label of this scenario is ${config.label.length} characters long: keep it under ${MAX_LABEL}, it is a title.`);
52
+ }
53
+ if (typeof config.run !== 'function') {
54
+ fail(`scenario('${config.label}') needs a function after its label, what the test does: async bot => { ... }.`);
55
+ }
56
+ return Object.freeze({ label: config.label.trim(), run: config.run });
57
+ },
58
+ };
@@ -0,0 +1,20 @@
1
+ import type { Project } from '../cli/project.js';
2
+ import type { Session } from './harness.js';
3
+ /** One thing to check, with the cases it is checked in. */
4
+ export interface Check {
5
+ /** What is checked: `command /ping`, `button confirm()`, `event memberJoin`... */
6
+ subject: string;
7
+ /** The file it comes from. */
8
+ file: string;
9
+ cases: Case[];
10
+ }
11
+ export interface Case {
12
+ /** How this case differs: `with everything`, `with reason at its longest`... */
13
+ label: string;
14
+ run(session: Session): Promise<void>;
15
+ }
16
+ /** Every check of the project, in the order of the report. */
17
+ export declare function smokeChecks(project: Project): {
18
+ checks: Check[];
19
+ notSimulated: string[];
20
+ };