@llblab/pi-telegram 0.2.9 → 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.
@@ -1,475 +0,0 @@
1
- /**
2
- * Regression tests for Telegram markdown rendering helpers
3
- * Covers nested lists, code blocks, tables, links, quotes, chunking, and other Telegram-specific render edge cases
4
- */
5
-
6
- import assert from "node:assert/strict";
7
- import test from "node:test";
8
-
9
- import { __telegramTestUtils } from "../index.ts";
10
- import {
11
- buildTelegramPreviewSnapshot,
12
- renderMarkdownPreviewText,
13
- } from "../lib/rendering.ts";
14
-
15
- test("Nested lists stay out of code blocks", () => {
16
- const chunks = __telegramTestUtils.renderTelegramMessage(
17
- "- Level 1\n - Level 2\n - Level 3 with **bold** text",
18
- { mode: "markdown" },
19
- );
20
- assert.ok(chunks.length > 0);
21
- assert.equal(
22
- chunks.some((chunk) => chunk.text.includes("<pre><code>")),
23
- false,
24
- );
25
- assert.equal(
26
- chunks.some((chunk) =>
27
- chunk.text.includes("<code>-</code> Level 3 with <b>bold</b> text"),
28
- ),
29
- true,
30
- );
31
- });
32
-
33
- test("Fenced code blocks preserve literal markdown", () => {
34
- const chunks = __telegramTestUtils.renderTelegramMessage(
35
- '~~~ts\nconst value = "**raw**";\n~~~',
36
- { mode: "markdown" },
37
- );
38
- assert.equal(chunks.length, 1);
39
- assert.match(chunks[0]?.text ?? "", /<pre><code class="language-ts">/);
40
- assert.match(chunks[0]?.text ?? "", /\*\*raw\*\*/);
41
- });
42
-
43
- test("Underscores inside words do not become italic", () => {
44
- const chunks = __telegramTestUtils.renderTelegramMessage(
45
- "Path: foo_bar_baz.txt and **bold**",
46
- { mode: "markdown" },
47
- );
48
- assert.equal(chunks.length, 1);
49
- assert.equal((chunks[0]?.text ?? "").includes("<i>bar</i>"), false);
50
- assert.match(chunks[0]?.text ?? "", /<b>bold<\/b>/);
51
- });
52
-
53
- test("Quoted nested lists stay in blockquote rendering", () => {
54
- const chunks = __telegramTestUtils.renderTelegramMessage(
55
- "> Quoted intro\n> - nested item\n> - deeper item",
56
- { mode: "markdown" },
57
- );
58
- assert.equal(chunks.length, 1);
59
- assert.match(chunks[0]?.text ?? "", /<blockquote>/);
60
- assert.match(chunks[0]?.text ?? "", /nested item/);
61
- assert.match(chunks[0]?.text ?? "", /<code>-<\/code> nested item/);
62
- assert.equal((chunks[0]?.text ?? "").includes("<pre><code>"), false);
63
- });
64
-
65
- test("Numbered lists use monospace numeric markers", () => {
66
- const chunks = __telegramTestUtils.renderTelegramMessage(
67
- "1. first\n 2. second",
68
- { mode: "markdown" },
69
- );
70
- assert.equal(chunks.length, 1);
71
- assert.match(chunks[0]?.text ?? "", /<code>1\.<\/code> first/);
72
- assert.match(chunks[0]?.text ?? "", /<code>2\.<\/code> second/);
73
- });
74
-
75
- test("Ordered task lists preserve numeric markers in previews and final rendering", () => {
76
- const markdown = "1. [x] first\n2. [ ] second";
77
- assert.equal(renderMarkdownPreviewText(markdown), markdown);
78
- const chunks = __telegramTestUtils.renderTelegramMessage(markdown, {
79
- mode: "markdown",
80
- });
81
- assert.equal(chunks.length, 1);
82
- assert.match(
83
- chunks[0]?.text ?? "",
84
- /<code>1\.<\/code> <code>\[x\]<\/code> first/,
85
- );
86
- assert.match(
87
- chunks[0]?.text ?? "",
88
- /<code>2\.<\/code> <code>\[ \]<\/code> second/,
89
- );
90
- });
91
-
92
- test("Leading indentation on the first markdown line stays intact", () => {
93
- const markdown = " - nested bullet\n - nested child";
94
- assert.equal(renderMarkdownPreviewText(markdown), markdown);
95
- const chunks = __telegramTestUtils.renderTelegramMessage(markdown, {
96
- mode: "markdown",
97
- });
98
- assert.equal(chunks.length, 1);
99
- assert.match(
100
- chunks[0]?.text ?? "",
101
- /^\u00A0\u00A0<code>-<\/code> nested bullet/m,
102
- );
103
- assert.match(
104
- chunks[0]?.text ?? "",
105
- /^\u00A0\u00A0\u00A0\u00A0<code>-<\/code> nested child/m,
106
- );
107
- });
108
-
109
- test("Preview and final rendering preserve multiple blank lines between blocks", () => {
110
- const markdown = "# Title\n\n\nParagraph\n\n\n> Quote";
111
- assert.equal(
112
- renderMarkdownPreviewText(markdown),
113
- "Title\n\n\nParagraph\n\n\n> Quote",
114
- );
115
- const chunks = __telegramTestUtils.renderTelegramMessage(markdown, {
116
- mode: "markdown",
117
- });
118
- assert.equal(chunks.length, 1);
119
- assert.match(
120
- chunks[0]?.text ?? "",
121
- /<b>Title<\/b>\n\n\nParagraph\n\n\n<blockquote>Quote<\/blockquote>/,
122
- );
123
- });
124
-
125
- test("Rendering preserves original blank-line spacing across block transitions", () => {
126
- const cases = [
127
- {
128
- markdown: "Para\n\n\n```ts\nconst x = 1\n```",
129
- finalText:
130
- 'Para\n\n\n<pre><code class="language-ts">const x = 1</code></pre>',
131
- previewText:
132
- 'Para\n\n\n<pre><code class="language-ts">const x = 1</code></pre>',
133
- },
134
- {
135
- markdown: "```ts\nconst x = 1\n```\n\n\nPara",
136
- finalText:
137
- '<pre><code class="language-ts">const x = 1</code></pre>\n\n\nPara',
138
- previewText:
139
- '<pre><code class="language-ts">const x = 1</code></pre>\n\n\nPara',
140
- },
141
- {
142
- markdown: "Para\n\n\n- item",
143
- finalText: "Para\n\n\n<code>-</code> item",
144
- previewText: "Para\n\n\n- item",
145
- },
146
- {
147
- markdown: "Para\n\n\n> Quote",
148
- finalText: "Para\n\n\n<blockquote>Quote</blockquote>",
149
- previewText: "Para\n\n\n&gt; Quote",
150
- },
151
- ];
152
- for (const testCase of cases) {
153
- const finalChunks = __telegramTestUtils.renderTelegramMessage(
154
- testCase.markdown,
155
- { mode: "markdown" },
156
- );
157
- assert.equal(finalChunks.length, 1);
158
- assert.equal(finalChunks[0]?.text ?? "", testCase.finalText);
159
- const preview = buildTelegramPreviewSnapshot({
160
- state: { pendingText: testCase.markdown, lastSentText: "" },
161
- maxMessageLength: __telegramTestUtils.MAX_MESSAGE_LENGTH,
162
- renderPreviewText: renderMarkdownPreviewText,
163
- renderTelegramMessage: __telegramTestUtils.renderTelegramMessage,
164
- });
165
- assert.equal(preview?.text ?? "", testCase.previewText);
166
- }
167
- });
168
-
169
- test("Headings keep visible spacing before following code blocks even without source blank lines", () => {
170
- const markdown = "### Title\n```ts\nconst x = 1\n```";
171
- const chunks = __telegramTestUtils.renderTelegramMessage(markdown, {
172
- mode: "markdown",
173
- });
174
- assert.equal(chunks.length, 1);
175
- assert.match(
176
- chunks[0]?.text ?? "",
177
- /<b>Title<\/b>\n\n<pre><code class="language-ts">const x = 1<\/code><\/pre>/,
178
- );
179
- });
180
-
181
- test("Standalone checkbox-looking prose stays literal outside task lists", () => {
182
- const markdown = "Use [ ] as a placeholder and keep [x] literal";
183
- assert.equal(renderMarkdownPreviewText(markdown), markdown);
184
- const chunks = __telegramTestUtils.renderTelegramMessage(markdown, {
185
- mode: "markdown",
186
- });
187
- assert.equal(chunks.length, 1);
188
- assert.equal((chunks[0]?.text ?? "").includes("<code>[ ]</code>"), false);
189
- assert.equal((chunks[0]?.text ?? "").includes("<code>[x]</code>"), false);
190
- assert.match(chunks[0]?.text ?? "", /Use \[ \] as a placeholder/);
191
- assert.match(chunks[0]?.text ?? "", /keep \[x\] literal/);
192
- });
193
-
194
- test("Nested blockquotes flatten into one Telegram blockquote with indentation", () => {
195
- const chunks = __telegramTestUtils.renderTelegramMessage(
196
- "> outer\n>> inner\n>>> deepest",
197
- { mode: "markdown" },
198
- );
199
- assert.equal(chunks.length, 1);
200
- assert.equal((chunks[0]?.text.match(/<blockquote>/g) ?? []).length, 1);
201
- assert.equal((chunks[0]?.text.match(/<\/blockquote>/g) ?? []).length, 1);
202
- assert.match(chunks[0]?.text ?? "", /outer/);
203
- assert.match(chunks[0]?.text ?? "", /\u00A0\u00A0inner/);
204
- assert.match(chunks[0]?.text ?? "", /\u00A0\u00A0\u00A0\u00A0deepest/);
205
- });
206
-
207
- test("Markdown tables render as literal monospace blocks without outer side borders", () => {
208
- const chunks = __telegramTestUtils.renderTelegramMessage(
209
- "| Name | Value |\n| --- | --- |\n| **x** | `y` |",
210
- { mode: "markdown" },
211
- );
212
- assert.equal(chunks.length, 1);
213
- assert.match(chunks[0]?.text ?? "", /<pre><code class="language-markdown">/);
214
- assert.equal((chunks[0]?.text ?? "").includes("<b>x</b>"), false);
215
- assert.match(chunks[0]?.text ?? "", /Name\s+\|\s+Value/);
216
- assert.match(chunks[0]?.text ?? "", /x\s+\|\s+y/);
217
- assert.equal((chunks[0]?.text ?? "").includes("| Name |"), false);
218
- assert.equal((chunks[0]?.text ?? "").includes("| x |"), false);
219
- });
220
-
221
- test("Links, code spans, and underscore-heavy text coexist safely", () => {
222
- const chunks = __telegramTestUtils.renderTelegramMessage(
223
- "See [docs](https://example.com), run `foo_bar()` and keep foo_bar.txt literal",
224
- { mode: "markdown" },
225
- );
226
- assert.equal(chunks.length, 1);
227
- assert.match(
228
- chunks[0]?.text ?? "",
229
- /<a href="https:\/\/example.com">docs<\/a>/,
230
- );
231
- assert.match(chunks[0]?.text ?? "", /<code>foo_bar\(\)<\/code>/);
232
- assert.equal((chunks[0]?.text ?? "").includes("<i>bar</i>"), false);
233
- });
234
-
235
- test("Links degrade or normalize safely across supported and unsupported markdown forms", () => {
236
- const markdown = [
237
- "[**Bold** label](https://example.com/path)",
238
- "[Docs](https://example.com/a_(b))",
239
- '[Title](https://example.com/path "Tooltip")',
240
- "[Relative](./docs/README.md)",
241
- "[Ref][docs]",
242
- "",
243
- "[docs]: https://example.com/ref",
244
- "",
245
- "Footnote[^1]",
246
- "",
247
- "[^1]: Footnote body",
248
- ].join("\n");
249
- const chunks = __telegramTestUtils.renderTelegramMessage(markdown, {
250
- mode: "markdown",
251
- });
252
- assert.equal(chunks.length, 1);
253
- assert.match(
254
- chunks[0]?.text ?? "",
255
- /<a href="https:\/\/example.com\/path">Bold label<\/a>/,
256
- );
257
- assert.match(
258
- chunks[0]?.text ?? "",
259
- /<a href="https:\/\/example.com\/a_\(b\)">Docs<\/a>/,
260
- );
261
- assert.match(
262
- chunks[0]?.text ?? "",
263
- /<a href="https:\/\/example.com\/path">Title<\/a>/,
264
- );
265
- assert.equal(
266
- (chunks[0]?.text ?? "").includes('<a href="./docs/README.md">'),
267
- false,
268
- );
269
- assert.match(chunks[0]?.text ?? "", /Relative/);
270
- assert.equal(
271
- (chunks[0]?.text ?? "").includes('<a href="https://example.com/ref">'),
272
- false,
273
- );
274
- assert.match(chunks[0]?.text ?? "", /\[Ref\]\[docs\]/);
275
- assert.match(chunks[0]?.text ?? "", /Footnote\[\^1\]/);
276
- assert.match(chunks[0]?.text ?? "", /\[\^1\]: Footnote body/);
277
- });
278
-
279
- test("Long quoted blocks stay chunked with balanced blockquote tags", () => {
280
- const markdown = Array.from(
281
- { length: 500 },
282
- (_, index) => `> quoted **${index}** line`,
283
- ).join("\n");
284
- const chunks = __telegramTestUtils.renderTelegramMessage(markdown, {
285
- mode: "markdown",
286
- });
287
- assert.ok(chunks.length > 1);
288
- for (const chunk of chunks) {
289
- assert.ok(chunk.text.length <= __telegramTestUtils.MAX_MESSAGE_LENGTH);
290
- assert.equal(
291
- (chunk.text.match(/<blockquote>/g) ?? []).length,
292
- (chunk.text.match(/<\/blockquote>/g) ?? []).length,
293
- );
294
- }
295
- });
296
-
297
- test("Long markdown replies stay chunked below Telegram limits", () => {
298
- const markdown = Array.from(
299
- { length: 600 },
300
- (_, index) => `- item **${index}**`,
301
- ).join("\n");
302
- const chunks = __telegramTestUtils.renderTelegramMessage(markdown, {
303
- mode: "markdown",
304
- });
305
- assert.ok(chunks.length > 1);
306
- for (const chunk of chunks) {
307
- assert.ok(chunk.text.length <= __telegramTestUtils.MAX_MESSAGE_LENGTH);
308
- assert.equal(
309
- (chunk.text.match(/<b>/g) ?? []).length,
310
- (chunk.text.match(/<\/b>/g) ?? []).length,
311
- );
312
- }
313
- });
314
-
315
- test("Long mixed links and code spans stay chunked with balanced inline tags", () => {
316
- const markdown = Array.from(
317
- { length: 450 },
318
- (_, index) =>
319
- `Paragraph ${index}: see [docs ${index}](https://example.com/${index}), run \`code_${index}()\`, and keep foo_bar_${index}.txt literal`,
320
- ).join("\n\n");
321
- const chunks = __telegramTestUtils.renderTelegramMessage(markdown, {
322
- mode: "markdown",
323
- });
324
- assert.ok(chunks.length > 1);
325
- for (const chunk of chunks) {
326
- assert.ok(chunk.text.length <= __telegramTestUtils.MAX_MESSAGE_LENGTH);
327
- assert.equal(
328
- (chunk.text.match(/<a /g) ?? []).length,
329
- (chunk.text.match(/<\/a>/g) ?? []).length,
330
- );
331
- assert.equal(
332
- (chunk.text.match(/<code>/g) ?? []).length,
333
- (chunk.text.match(/<\/code>/g) ?? []).length,
334
- );
335
- assert.equal((chunk.text ?? "").includes("<i>bar</i>"), false);
336
- }
337
- });
338
-
339
- test("Long multi-block markdown keeps quotes and code fences structurally balanced", () => {
340
- const markdown = Array.from({ length: 120 }, (_, index) => {
341
- return [
342
- `## Section ${index}`,
343
- `> quoted **${index}** line`,
344
- `- item ${index}`,
345
- "```ts",
346
- `const value_${index} = \"**raw**\";`,
347
- "```",
348
- ].join("\n");
349
- }).join("\n\n");
350
- const chunks = __telegramTestUtils.renderTelegramMessage(markdown, {
351
- mode: "markdown",
352
- });
353
- assert.ok(chunks.length > 1);
354
- for (const chunk of chunks) {
355
- assert.ok(chunk.text.length <= __telegramTestUtils.MAX_MESSAGE_LENGTH);
356
- assert.equal(
357
- (chunk.text.match(/<blockquote>/g) ?? []).length,
358
- (chunk.text.match(/<\/blockquote>/g) ?? []).length,
359
- );
360
- assert.equal(
361
- (chunk.text.match(/<pre><code/g) ?? []).length,
362
- (chunk.text.match(/<\/code><\/pre>/g) ?? []).length,
363
- );
364
- }
365
- });
366
-
367
- test("Chunked mixed block transitions keep quote and list structure balanced", () => {
368
- const markdown = Array.from({ length: 260 }, (_, index) => {
369
- return [
370
- `> quoted **${index}** intro`,
371
- `> continuation ${index}`,
372
- `- item ${index}`,
373
- `plain paragraph ${index} with [link](https://example.com/${index})`,
374
- ].join("\n");
375
- }).join("\n\n");
376
- const chunks = __telegramTestUtils.renderTelegramMessage(markdown, {
377
- mode: "markdown",
378
- });
379
- assert.ok(chunks.length > 1);
380
- for (const chunk of chunks) {
381
- assert.ok(chunk.text.length <= __telegramTestUtils.MAX_MESSAGE_LENGTH);
382
- assert.equal(
383
- (chunk.text.match(/<blockquote>/g) ?? []).length,
384
- (chunk.text.match(/<\/blockquote>/g) ?? []).length,
385
- );
386
- assert.equal(
387
- (chunk.text.match(/<a /g) ?? []).length,
388
- (chunk.text.match(/<\/a>/g) ?? []).length,
389
- );
390
- }
391
- });
392
-
393
- test("Chunked code fence transitions keep code blocks closed before following prose", () => {
394
- const markdown = Array.from({ length: 220 }, (_, index) => {
395
- return [
396
- "```ts",
397
- `const block_${index} = \"value_${index}\";`,
398
- "```",
399
- `After code **${index}** and \`inline_${index}()\``,
400
- ].join("\n");
401
- }).join("\n\n");
402
- const chunks = __telegramTestUtils.renderTelegramMessage(markdown, {
403
- mode: "markdown",
404
- });
405
- assert.ok(chunks.length > 1);
406
- for (const chunk of chunks) {
407
- assert.ok(chunk.text.length <= __telegramTestUtils.MAX_MESSAGE_LENGTH);
408
- assert.equal(
409
- (chunk.text.match(/<pre><code/g) ?? []).length,
410
- (chunk.text.match(/<\/code><\/pre>/g) ?? []).length,
411
- );
412
- assert.equal(
413
- (chunk.text.match(/<code(?: class="[^"]+")?>/g) ?? []).length,
414
- (chunk.text.match(/<\/code>/g) ?? []).length,
415
- );
416
- }
417
- });
418
-
419
- test("Long inline formatting paragraphs stay balanced across chunk boundaries", () => {
420
- const markdown = Array.from({ length: 500 }, (_, index) => {
421
- return `Segment ${index} keeps **bold_${index}** with \`code_${index}()\`, [link_${index}](https://example.com/${index}), and foo_bar_${index}.txt literal.`;
422
- }).join(" ");
423
- const chunks = __telegramTestUtils.renderTelegramMessage(markdown, {
424
- mode: "markdown",
425
- });
426
- assert.ok(chunks.length > 1);
427
- for (const chunk of chunks) {
428
- assert.ok(chunk.text.length <= __telegramTestUtils.MAX_MESSAGE_LENGTH);
429
- assert.equal(
430
- (chunk.text.match(/<b>/g) ?? []).length,
431
- (chunk.text.match(/<\/b>/g) ?? []).length,
432
- );
433
- assert.equal(
434
- (chunk.text.match(/<a /g) ?? []).length,
435
- (chunk.text.match(/<\/a>/g) ?? []).length,
436
- );
437
- assert.equal(
438
- (chunk.text.match(/<code>/g) ?? []).length,
439
- (chunk.text.match(/<\/code>/g) ?? []).length,
440
- );
441
- assert.equal(chunk.text.includes("<i>bar</i>"), false);
442
- }
443
- });
444
-
445
- test("Chunked list, code, quote, and prose cycles stay balanced across transitions", () => {
446
- const markdown = Array.from({ length: 180 }, (_, index) => {
447
- return [
448
- `- list item **${index}**`,
449
- "```ts",
450
- `const cycle_${index} = \"value_${index}\";`,
451
- "```",
452
- `> quoted ${index} with [link](https://example.com/${index})`,
453
- `Plain paragraph ${index} with \`inline_${index}()\``,
454
- ].join("\n");
455
- }).join("\n\n");
456
- const chunks = __telegramTestUtils.renderTelegramMessage(markdown, {
457
- mode: "markdown",
458
- });
459
- assert.ok(chunks.length > 1);
460
- for (const chunk of chunks) {
461
- assert.ok(chunk.text.length <= __telegramTestUtils.MAX_MESSAGE_LENGTH);
462
- assert.equal(
463
- (chunk.text.match(/<pre><code/g) ?? []).length,
464
- (chunk.text.match(/<\/code><\/pre>/g) ?? []).length,
465
- );
466
- assert.equal(
467
- (chunk.text.match(/<blockquote>/g) ?? []).length,
468
- (chunk.text.match(/<\/blockquote>/g) ?? []).length,
469
- );
470
- assert.equal(
471
- (chunk.text.match(/<a /g) ?? []).length,
472
- (chunk.text.match(/<\/a>/g) ?? []).length,
473
- );
474
- }
475
- });
@@ -1,142 +0,0 @@
1
- /**
2
- * Regression tests for Telegram reply delivery helpers
3
- * Covers rendered-message transport, chunk delivery, and plain or markdown final reply sending
4
- */
5
-
6
- import assert from "node:assert/strict";
7
- import test from "node:test";
8
-
9
- import {
10
- buildTelegramReplyTransport,
11
- editTelegramRenderedMessage,
12
- sendTelegramMarkdownReply,
13
- sendTelegramPlainReply,
14
- sendTelegramRenderedChunks,
15
- } from "../lib/replies.ts";
16
-
17
- test("Reply transport forwards send and edit operations through delivery helpers", async () => {
18
- const events: string[] = [];
19
- const transport = buildTelegramReplyTransport({
20
- sendMessage: async (body) => {
21
- events.push(`send:${body.chat_id}:${body.text}`);
22
- return { message_id: 5 };
23
- },
24
- editMessage: async (body) => {
25
- events.push(`edit:${body.chat_id}:${body.message_id}:${body.text}`);
26
- },
27
- });
28
- assert.equal(await transport.sendRenderedChunks(7, [{ text: "one" }]), 5);
29
- assert.equal(await transport.editRenderedMessage(7, 9, [{ text: "two" }]), 9);
30
- assert.deepEqual(events, ["send:7:one", "edit:7:9:two"]);
31
- });
32
-
33
- test("Reply delivery sends chunks and applies reply markup only to the last chunk", async () => {
34
- const sentBodies: Array<Record<string, unknown>> = [];
35
- const messageId = await sendTelegramRenderedChunks(
36
- 7,
37
- [{ text: "one" }, { text: "two", parseMode: "HTML" }],
38
- {
39
- sendMessage: async (body) => {
40
- sentBodies.push(body);
41
- return { message_id: sentBodies.length };
42
- },
43
- editMessage: async () => {},
44
- },
45
- {
46
- replyMarkup: {
47
- inline_keyboard: [[{ text: "ok", callback_data: "noop" }]],
48
- },
49
- },
50
- );
51
- assert.equal(messageId, 2);
52
- assert.deepEqual(sentBodies, [
53
- { chat_id: 7, text: "one", parse_mode: undefined, reply_markup: undefined },
54
- {
55
- chat_id: 7,
56
- text: "two",
57
- parse_mode: "HTML",
58
- reply_markup: {
59
- inline_keyboard: [[{ text: "ok", callback_data: "noop" }]],
60
- },
61
- },
62
- ]);
63
- });
64
-
65
- test("Reply delivery edits the first chunk and sends remaining chunks separately", async () => {
66
- const editedBodies: Array<Record<string, unknown>> = [];
67
- const sentBodies: Array<Record<string, unknown>> = [];
68
- const result = await editTelegramRenderedMessage(
69
- 7,
70
- 99,
71
- [{ text: "first", parseMode: "HTML" }, { text: "second" }],
72
- {
73
- sendMessage: async (body) => {
74
- sentBodies.push(body);
75
- return { message_id: 123 };
76
- },
77
- editMessage: async (body) => {
78
- editedBodies.push(body);
79
- },
80
- },
81
- {
82
- replyMarkup: {
83
- inline_keyboard: [[{ text: "ok", callback_data: "noop" }]],
84
- },
85
- },
86
- );
87
- assert.equal(result, 123);
88
- assert.deepEqual(editedBodies, [
89
- {
90
- chat_id: 7,
91
- message_id: 99,
92
- text: "first",
93
- parse_mode: "HTML",
94
- reply_markup: undefined,
95
- },
96
- ]);
97
- assert.deepEqual(sentBodies, [
98
- {
99
- chat_id: 7,
100
- text: "second",
101
- parse_mode: undefined,
102
- reply_markup: {
103
- inline_keyboard: [[{ text: "ok", callback_data: "noop" }]],
104
- },
105
- },
106
- ]);
107
- });
108
-
109
- test("Reply runtime sends plain replies using the requested parse mode", async () => {
110
- const sent: string[] = [];
111
- const messageId = await sendTelegramPlainReply(
112
- "hello",
113
- {
114
- renderTelegramMessage: (_text, options) => [
115
- { text: options?.mode === "html" ? "html" : "plain" },
116
- ],
117
- sendRenderedChunks: async (chunks) => {
118
- sent.push(chunks[0]?.text ?? "");
119
- return 7;
120
- },
121
- },
122
- { parseMode: "HTML" },
123
- );
124
- assert.equal(messageId, 7);
125
- assert.deepEqual(sent, ["html"]);
126
- });
127
-
128
- test("Reply runtime falls back to plain delivery when markdown rendering yields no chunks", async () => {
129
- const calls: Array<string> = [];
130
- const messageId = await sendTelegramMarkdownReply("hello", {
131
- renderTelegramMessage: (_text, options) => {
132
- if (options?.mode === "markdown") return [];
133
- return [{ text: options?.mode ?? "plain" }];
134
- },
135
- sendRenderedChunks: async (chunks) => {
136
- calls.push(chunks[0]?.text ?? "");
137
- return 9;
138
- },
139
- });
140
- assert.equal(messageId, 9);
141
- assert.deepEqual(calls, ["plain"]);
142
- });