@yaebal/scenes 0.0.2 → 0.0.3

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.
package/lib/index.test.js CHANGED
@@ -1,25 +1,9 @@
1
1
  import assert from "node:assert/strict";
2
2
  import test from "node:test";
3
- import { Composer, Context } from "@yaebal/core";
3
+ import { Composer } from "@yaebal/core";
4
4
  import { MemoryStorage } from "@yaebal/session";
5
+ import { createTestEnv } from "@yaebal/test";
5
6
  import { scenes } from "./index.js";
6
- const noop = async () => { };
7
- const entry = (c) => c.toMiddleware();
8
- const api = {};
9
- const msgCtx = (text, chatId) => new Context({
10
- api,
11
- update: {
12
- update_id: 1,
13
- message: {
14
- message_id: 1,
15
- date: 0,
16
- chat: { id: chatId, type: "private" },
17
- from: { id: chatId, is_bot: false, first_name: "u" },
18
- text,
19
- },
20
- },
21
- updateType: "message",
22
- });
23
7
  test("a wizard collects input across messages and leaves", async () => {
24
8
  const collected = {};
25
9
  const asked = [];
@@ -43,30 +27,36 @@ test("a wizard collects input across messages and leaves", async () => {
43
27
  };
44
28
  const storage = new MemoryStorage();
45
29
  let fellThrough = 0;
46
- const mw = entry(new Composer()
30
+ const bot = new Composer()
47
31
  .install(scenes(defs, { storage }))
48
32
  .command("reg", (ctx) => ctx.scene.enter("reg"))
49
33
  .on("message:text", () => {
50
34
  fellThrough++;
51
- }));
52
- await mw(msgCtx("/reg", 1), noop); // enter → asks name
35
+ });
36
+ const env = createTestEnv(bot);
37
+ const user = env.createUser();
38
+ const key = String(user.pmChat.id);
39
+ await user.sendCommand("reg"); // enter → asks name
53
40
  assert.deepEqual(asked, ["name?"]);
54
- assert.deepEqual((await storage.get("1")) ?? null, { scene: "reg", step: 0 });
55
- await mw(msgCtx("Alice", 1), noop); // step 0 → name, advance, asks age
56
- assert.equal(collected.name, "Alice");
57
- assert.deepEqual((await storage.get("1")) ?? null, { scene: "reg", step: 1 });
58
- await mw(msgCtx("30", 1), noop); // step 1 → age, leave
41
+ assert.deepEqual((await storage.get(key)) ?? null, { scene: "reg", step: 0 });
42
+ await user.sendMessage("Linia"); // step 0 → name, advance, asks age
43
+ assert.equal(collected.name, "Linia");
44
+ assert.deepEqual((await storage.get(key)) ?? null, { scene: "reg", step: 1 });
45
+ await user.sendMessage("30"); // step 1 → age, leave
59
46
  assert.equal(collected.age, "30");
60
- assert.equal(await storage.get("1"), undefined); // scene cleared
47
+ assert.equal(await storage.get(key), undefined); // scene cleared
61
48
  // scene messages were consumed — the fallthrough handler never saw them
62
49
  assert.equal(fellThrough, 0);
63
50
  });
64
51
  test("messages outside a scene fall through to normal handlers", async () => {
65
52
  let seen = "";
66
- const mw = entry(new Composer().install(scenes({ reg: { steps: [] } })).on("message:text", (ctx) => {
67
- seen = ctx.text;
68
- }));
69
- await mw(msgCtx("hello", 2), noop);
53
+ const bot = new Composer()
54
+ .install(scenes({ reg: { steps: [] } }))
55
+ .on("message:text", (ctx) => {
56
+ seen = ctx.text ?? "";
57
+ });
58
+ const env = createTestEnv(bot);
59
+ await env.createUser().sendMessage("hello");
70
60
  assert.equal(seen, "hello");
71
61
  });
72
62
  test("a step can switch scenes via enter()", async () => {
@@ -75,14 +65,17 @@ test("a step can switch scenes via enter()", async () => {
75
65
  b: { steps: [(ctx) => ctx.scene.leave()] },
76
66
  };
77
67
  const storage = new MemoryStorage();
78
- const mw = entry(new Composer()
68
+ const bot = new Composer()
79
69
  .install(scenes(defs, { storage }))
80
- .command("a", (ctx) => ctx.scene.enter("a")));
81
- await mw(msgCtx("/a", 5), noop); // in scene a
82
- await mw(msgCtx("go", 5), noop); // step a[0] → enter("b")
83
- assert.deepEqual((await storage.get("5")) ?? null, { scene: "b", step: 0 });
84
- await mw(msgCtx("done", 5), noop); // step b[0] → leave
85
- assert.equal(await storage.get("5"), undefined);
70
+ .command("a", (ctx) => ctx.scene.enter("a"));
71
+ const env = createTestEnv(bot);
72
+ const user = env.createUser();
73
+ const key = String(user.pmChat.id);
74
+ await user.sendCommand("a"); // in scene a
75
+ await user.sendMessage("go"); // step a[0] → enter("b")
76
+ assert.deepEqual((await storage.get(key)) ?? null, { scene: "b", step: 0 });
77
+ await user.sendMessage("done"); // step b[0] → leave
78
+ assert.equal(await storage.get(key), undefined);
86
79
  });
87
80
  test("a step that does not advance re-runs (validation)", async () => {
88
81
  let attempts = 0;
@@ -99,14 +92,17 @@ test("a step that does not advance re-runs (validation)", async () => {
99
92
  },
100
93
  };
101
94
  const storage = new MemoryStorage();
102
- const mw = entry(new Composer()
95
+ const bot = new Composer()
103
96
  .install(scenes(defs, { storage }))
104
- .command("ask", (ctx) => ctx.scene.enter("ask")));
105
- await mw(msgCtx("/ask", 3), noop);
106
- await mw(msgCtx("nope", 3), noop); // stays
107
- assert.deepEqual((await storage.get("3")) ?? null, { scene: "ask", step: 0 });
108
- await mw(msgCtx("ok", 3), noop); // leaves
109
- assert.equal(await storage.get("3"), undefined);
97
+ .command("ask", (ctx) => ctx.scene.enter("ask"));
98
+ const env = createTestEnv(bot);
99
+ const user = env.createUser();
100
+ const key = String(user.pmChat.id);
101
+ await user.sendCommand("ask");
102
+ await user.sendMessage("nope"); // stays
103
+ assert.deepEqual((await storage.get(key)) ?? null, { scene: "ask", step: 0 });
104
+ await user.sendMessage("ok"); // leaves
105
+ assert.equal(await storage.get(key), undefined);
110
106
  assert.equal(attempts, 2);
111
107
  });
112
108
  //# sourceMappingURL=index.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yaebal/scenes",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "yaebal scenes plugin — step-by-step wizards over multiple messages.",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -16,11 +16,12 @@
16
16
  "src"
17
17
  ],
18
18
  "dependencies": {
19
- "@yaebal/core": "0.0.5",
20
- "@yaebal/session": "0.0.2"
19
+ "@yaebal/core": "0.0.8",
20
+ "@yaebal/session": "0.0.3"
21
21
  },
22
22
  "devDependencies": {
23
- "@types/node": "latest"
23
+ "@types/node": "latest",
24
+ "@yaebal/test": "0.2.0"
24
25
  },
25
26
  "engines": {
26
27
  "node": ">=20"
@@ -45,6 +46,6 @@
45
46
  "scripts": {
46
47
  "build": "tsc -p tsconfig.json",
47
48
  "typecheck": "tsc -p tsconfig.json --noEmit",
48
- "test": "node --test lib"
49
+ "test": "node --test lib/*.test.js"
49
50
  }
50
51
  }
package/src/index.test.ts CHANGED
@@ -1,30 +1,10 @@
1
1
  import assert from "node:assert/strict";
2
2
  import test from "node:test";
3
- import { Composer, Context, type Middleware } from "@yaebal/core";
3
+ import { Composer, type Context } from "@yaebal/core";
4
4
  import { MemoryStorage } from "@yaebal/session";
5
+ import { createTestEnv } from "@yaebal/test";
5
6
  import { type SceneContext, type SceneDef, scenes } from "./index.js";
6
7
 
7
- const noop = async () => {};
8
- const entry = <C extends Context>(c: Composer<C>) =>
9
- c.toMiddleware() as unknown as Middleware<Context>;
10
-
11
- const api = {} as never;
12
- const msgCtx = (text: string, chatId: number) =>
13
- new Context({
14
- api,
15
- update: {
16
- update_id: 1,
17
- message: {
18
- message_id: 1,
19
- date: 0,
20
- chat: { id: chatId, type: "private" },
21
- from: { id: chatId, is_bot: false, first_name: "u" },
22
- text,
23
- },
24
- } as never,
25
- updateType: "message",
26
- });
27
-
28
8
  test("a wizard collects input across messages and leaves", async () => {
29
9
  const collected: Record<string, string> = {};
30
10
  const asked: string[] = [];
@@ -52,26 +32,28 @@ test("a wizard collects input across messages and leaves", async () => {
52
32
  const storage = new MemoryStorage<{ scene: string; step: number }>();
53
33
  let fellThrough = 0;
54
34
 
55
- const mw = entry(
56
- new Composer<Context>()
57
- .install(scenes(defs, { storage }))
58
- .command("reg", (ctx) => ctx.scene.enter("reg"))
59
- .on("message:text", () => {
60
- fellThrough++;
61
- }),
62
- );
35
+ const bot = new Composer<Context>()
36
+ .install(scenes(defs, { storage }))
37
+ .command("reg", (ctx) => ctx.scene.enter("reg"))
38
+ .on("message:text", () => {
39
+ fellThrough++;
40
+ });
41
+
42
+ const env = createTestEnv(bot);
43
+ const user = env.createUser();
44
+ const key = String(user.pmChat.id);
63
45
 
64
- await mw(msgCtx("/reg", 1), noop); // enter → asks name
46
+ await user.sendCommand("reg"); // enter → asks name
65
47
  assert.deepEqual(asked, ["name?"]);
66
- assert.deepEqual((await storage.get("1")) ?? null, { scene: "reg", step: 0 });
48
+ assert.deepEqual((await storage.get(key)) ?? null, { scene: "reg", step: 0 });
67
49
 
68
- await mw(msgCtx("Alice", 1), noop); // step 0 → name, advance, asks age
69
- assert.equal(collected.name, "Alice");
70
- assert.deepEqual((await storage.get("1")) ?? null, { scene: "reg", step: 1 });
50
+ await user.sendMessage("Linia"); // step 0 → name, advance, asks age
51
+ assert.equal(collected.name, "Linia");
52
+ assert.deepEqual((await storage.get(key)) ?? null, { scene: "reg", step: 1 });
71
53
 
72
- await mw(msgCtx("30", 1), noop); // step 1 → age, leave
54
+ await user.sendMessage("30"); // step 1 → age, leave
73
55
  assert.equal(collected.age, "30");
74
- assert.equal(await storage.get("1"), undefined); // scene cleared
56
+ assert.equal(await storage.get(key), undefined); // scene cleared
75
57
 
76
58
  // scene messages were consumed — the fallthrough handler never saw them
77
59
  assert.equal(fellThrough, 0);
@@ -80,13 +62,14 @@ test("a wizard collects input across messages and leaves", async () => {
80
62
  test("messages outside a scene fall through to normal handlers", async () => {
81
63
  let seen = "";
82
64
 
83
- const mw = entry(
84
- new Composer<Context>().install(scenes({ reg: { steps: [] } })).on("message:text", (ctx) => {
85
- seen = ctx.text;
86
- }),
87
- );
65
+ const bot = new Composer<Context>()
66
+ .install(scenes({ reg: { steps: [] } }))
67
+ .on("message:text", (ctx) => {
68
+ seen = ctx.text ?? "";
69
+ });
88
70
 
89
- await mw(msgCtx("hello", 2), noop);
71
+ const env = createTestEnv(bot);
72
+ await env.createUser().sendMessage("hello");
90
73
  assert.equal(seen, "hello");
91
74
  });
92
75
 
@@ -97,18 +80,20 @@ test("a step can switch scenes via enter()", async () => {
97
80
  };
98
81
 
99
82
  const storage = new MemoryStorage<{ scene: string; step: number }>();
100
- const mw = entry(
101
- new Composer<Context>()
102
- .install(scenes(defs, { storage }))
103
- .command("a", (ctx) => ctx.scene.enter("a")),
104
- );
105
-
106
- await mw(msgCtx("/a", 5), noop); // in scene a
107
- await mw(msgCtx("go", 5), noop); // step a[0] → enter("b")
108
- assert.deepEqual((await storage.get("5")) ?? null, { scene: "b", step: 0 });
109
-
110
- await mw(msgCtx("done", 5), noop); // step b[0] → leave
111
- assert.equal(await storage.get("5"), undefined);
83
+ const bot = new Composer<Context>()
84
+ .install(scenes(defs, { storage }))
85
+ .command("a", (ctx) => ctx.scene.enter("a"));
86
+
87
+ const env = createTestEnv(bot);
88
+ const user = env.createUser();
89
+ const key = String(user.pmChat.id);
90
+
91
+ await user.sendCommand("a"); // in scene a
92
+ await user.sendMessage("go"); // step a[0] → enter("b")
93
+ assert.deepEqual((await storage.get(key)) ?? null, { scene: "b", step: 0 });
94
+
95
+ await user.sendMessage("done"); // step b[0] → leave
96
+ assert.equal(await storage.get(key), undefined);
112
97
  });
113
98
 
114
99
  test("a step that does not advance re-runs (validation)", async () => {
@@ -126,17 +111,19 @@ test("a step that does not advance re-runs (validation)", async () => {
126
111
  };
127
112
 
128
113
  const storage = new MemoryStorage<{ scene: string; step: number }>();
129
- const mw = entry(
130
- new Composer<Context>()
131
- .install(scenes(defs, { storage }))
132
- .command("ask", (ctx) => ctx.scene.enter("ask")),
133
- );
134
-
135
- await mw(msgCtx("/ask", 3), noop);
136
- await mw(msgCtx("nope", 3), noop); // stays
137
- assert.deepEqual((await storage.get("3")) ?? null, { scene: "ask", step: 0 });
138
-
139
- await mw(msgCtx("ok", 3), noop); // leaves
140
- assert.equal(await storage.get("3"), undefined);
114
+ const bot = new Composer<Context>()
115
+ .install(scenes(defs, { storage }))
116
+ .command("ask", (ctx) => ctx.scene.enter("ask"));
117
+
118
+ const env = createTestEnv(bot);
119
+ const user = env.createUser();
120
+ const key = String(user.pmChat.id);
121
+
122
+ await user.sendCommand("ask");
123
+ await user.sendMessage("nope"); // stays
124
+ assert.deepEqual((await storage.get(key)) ?? null, { scene: "ask", step: 0 });
125
+
126
+ await user.sendMessage("ok"); // leaves
127
+ assert.equal(await storage.get(key), undefined);
141
128
  assert.equal(attempts, 2);
142
129
  });