@mks2508/telegram-message-builder 0.2.0 → 0.3.1

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 (57) hide show
  1. package/dist/builder/builder.d.ts +87 -0
  2. package/dist/builder/index.d.ts +2 -0
  3. package/dist/builder/media.d.ts +351 -0
  4. package/dist/formatters/index.d.ts +86 -0
  5. package/dist/formatters/markdown.d.ts +178 -0
  6. package/dist/formatters/markdownv2.d.ts +183 -0
  7. package/dist/index.cjs +1057 -12
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.ts +11 -0
  10. package/dist/index.js +1022 -13
  11. package/dist/index.js.map +1 -1
  12. package/dist/keyboard/index.d.ts +113 -0
  13. package/dist/types/constants.d.ts +13 -0
  14. package/dist/types/core.types.d.ts +74 -0
  15. package/dist/types/index.d.ts +4 -0
  16. package/dist/types/keyboard-types.index.d.ts +1 -0
  17. package/dist/types/keyboard.types.d.ts +95 -0
  18. package/dist/types/main.types.d.ts +22 -0
  19. package/dist/types/media.types.d.ts +157 -0
  20. package/package.json +1 -1
  21. package/src/builder/builder.d.ts +55 -0
  22. package/src/builder/builder.d.ts.map +1 -1
  23. package/src/builder/builder.ts +145 -10
  24. package/src/builder/index.d.ts +2 -1
  25. package/src/builder/index.d.ts.map +1 -1
  26. package/src/builder/index.ts +2 -1
  27. package/src/builder/media.d.ts +352 -0
  28. package/src/builder/media.d.ts.map +1 -0
  29. package/src/builder/media.test.ts +664 -0
  30. package/src/builder/media.ts +484 -0
  31. package/src/builder.test.ts +465 -0
  32. package/src/escaping.test.ts +2 -2
  33. package/src/formatters/index.d.ts +47 -0
  34. package/src/formatters/index.d.ts.map +1 -1
  35. package/src/formatters/index.ts +92 -1
  36. package/src/formatters/markdown.d.ts +179 -0
  37. package/src/formatters/markdown.d.ts.map +1 -0
  38. package/src/formatters/markdown.test.ts +417 -0
  39. package/src/formatters/markdown.ts +220 -0
  40. package/src/formatters/markdownv2.d.ts +184 -0
  41. package/src/formatters/markdownv2.d.ts.map +1 -0
  42. package/src/formatters/markdownv2.ts +235 -0
  43. package/src/formatters.test.ts +17 -7
  44. package/src/index.d.ts +2 -0
  45. package/src/index.d.ts.map +1 -1
  46. package/src/index.ts +12 -0
  47. package/src/integration.test.ts +523 -0
  48. package/src/media-integration.test.ts +384 -0
  49. package/src/types/index.d.ts +1 -0
  50. package/src/types/index.d.ts.map +1 -1
  51. package/src/types/index.ts +1 -0
  52. package/src/types/media.types.d.ts +158 -0
  53. package/src/types/media.types.d.ts.map +1 -0
  54. package/src/types/media.types.ts +178 -0
  55. package/src/types.test.ts +539 -0
  56. package/src/utils/index.d.ts +1 -1
  57. package/src/utils/index.ts +0 -5
@@ -0,0 +1,179 @@
1
+ /**
2
+ * @fileoverview Markdown (Legacy) Formatters
3
+ * @description Text formatting functions for Telegram's legacy Markdown parse mode
4
+ * @module telegram-message-builder/formatters
5
+ *
6
+ * @see {@link https://core.telegram.org/bots/api#formatting-options | Telegram Bot API - Formatting Options}
7
+ */
8
+ /**
9
+ * Formats text as bold in Markdown
10
+ *
11
+ * @param text - The text to format
12
+ * @returns Bold formatted text
13
+ * @example
14
+ * ```typescript
15
+ * boldMD("Hello") // "*Hello*"
16
+ * ```
17
+ */
18
+ export declare function boldMD(text: string): string;
19
+ /**
20
+ * Formats text as italic in Markdown
21
+ *
22
+ * @param text - The text to format
23
+ * @returns Italic formatted text
24
+ * @example
25
+ * ```typescript
26
+ * italicMD("Hello") // "_Hello_"
27
+ * ```
28
+ */
29
+ export declare function italicMD(text: string): string;
30
+ /**
31
+ * Formats text as monospace code in Markdown
32
+ *
33
+ * @param text - The text to format
34
+ * @returns Code formatted text
35
+ * @example
36
+ * ```typescript
37
+ * codeMD("const x = 1") // "`const x = 1`"
38
+ * ```
39
+ */
40
+ export declare function codeMD(text: string): string;
41
+ /**
42
+ * Formats text as a code block in Markdown
43
+ *
44
+ * @param text - The code to format
45
+ * @param language - Optional programming language for syntax highlighting
46
+ * @returns Code block formatted text
47
+ * @example
48
+ * ```typescript
49
+ * codeBlockMD("console.log('Hello')") // "```console.log('Hello')```"
50
+ * codeBlockMD("const x = 1", "javascript") // "```javascript\nconst x = 1\n```"
51
+ * ```
52
+ */
53
+ export declare function codeBlockMD(text: string, language?: string): string;
54
+ /**
55
+ * Creates a link in Markdown format
56
+ *
57
+ * @param text - The link text
58
+ * @param url - The URL to link to
59
+ * @returns Link formatted text
60
+ * @example
61
+ * ```typescript
62
+ * linkMD("Google", "https://google.com") // "[Google](https://google.com)"
63
+ * ```
64
+ */
65
+ export declare function linkMD(text: string, url: string): string;
66
+ /**
67
+ * Creates a user mention link in Markdown format
68
+ *
69
+ * @param userId - The user's ID
70
+ * @param name - Optional display name
71
+ * @returns Mention formatted text
72
+ * @example
73
+ * ```typescript
74
+ * mentionMD(123456) // "tg://user?id=123456"
75
+ * mentionMD(123456, "John") // "tg://user?id=123456"
76
+ * ```
77
+ */
78
+ export declare function mentionMD(userId: number, _name?: string): string;
79
+ /**
80
+ * Creates a hashtag link in Markdown format
81
+ *
82
+ * @param tag - The hashtag text (without #)
83
+ * @returns Hashtag formatted text
84
+ * @example
85
+ * ```typescript
86
+ * hashtagMD("test") // "#test"
87
+ * ```
88
+ */
89
+ export declare function hashtagMD(tag: string): string;
90
+ /**
91
+ * Formats text as underline in Markdown (limited support)
92
+ *
93
+ * Note: Standard Markdown doesn't support underline. This uses HTML which
94
+ * may not work in all Telegram clients.
95
+ *
96
+ * @param text - The text to format
97
+ * @returns Underline formatted text (HTML fallback)
98
+ * @example
99
+ * ```typescript
100
+ * underlineMD("Hello") // "<u>Hello</u>"
101
+ * ```
102
+ */
103
+ export declare function underlineMD(text: string): string;
104
+ /**
105
+ * Formats text as strikethrough in Markdown (limited support)
106
+ *
107
+ * Note: Standard Markdown doesn't support strikethrough. This uses HTML which
108
+ * may not work in all Telegram clients.
109
+ *
110
+ * @param text - The text to format
111
+ * @returns Strikethrough formatted text (HTML fallback)
112
+ * @example
113
+ * ```typescript
114
+ * strikethroughMD("Hello") // "<s>Hello</s>"
115
+ * ```
116
+ */
117
+ export declare function strikethroughMD(text: string): string;
118
+ /**
119
+ * Formats text as spoiler in Markdown (limited support)
120
+ *
121
+ * Note: Standard Markdown doesn't support spoiler. This uses HTML which
122
+ * may not work in all Telegram clients.
123
+ *
124
+ * @param text - The text to format
125
+ * @returns Spoiler formatted text (HTML fallback)
126
+ * @example
127
+ * ```typescript
128
+ * spoilerMD("Secret") // "<tg-spoiler>Secret</tg-spoiler>"
129
+ * ```
130
+ */
131
+ export declare function spoilerMD(text: string): string;
132
+ /**
133
+ * Creates an email link in Markdown format
134
+ *
135
+ * @param email - The email address
136
+ * @returns Email link formatted text
137
+ * @example
138
+ * ```typescript
139
+ * emailMD("test@example.com") // "[test@example.com](mailto:test@example.com)"
140
+ * ```
141
+ */
142
+ export declare function emailMD(email: string): string;
143
+ /**
144
+ * Creates a URL link in Markdown format
145
+ *
146
+ * @param url - The URL
147
+ * @returns URL link formatted text
148
+ * @example
149
+ * ```typescript
150
+ * urlMD("https://example.com") // "[https://example.com](https://example.com)"
151
+ * ```
152
+ */
153
+ export declare function urlMD(url: string): string;
154
+ /**
155
+ * Creates a custom emoji in Markdown format (limited support)
156
+ *
157
+ * Note: Standard Markdown doesn't support custom emoji. This uses HTML which
158
+ * may not work in all Telegram clients.
159
+ *
160
+ * @param emojiId - The custom emoji ID
161
+ * @returns Custom emoji formatted text (HTML fallback)
162
+ * @example
163
+ * ```typescript
164
+ * customEmojiMD("5368324170672642286") // "<tg-emoji emoji-id=\"5368324170672642286\">👻</tg-emoji>"
165
+ * ```
166
+ */
167
+ export declare function customEmojiMD(emojiId: string): string;
168
+ /**
169
+ * Raw Markdown string - bypasses any automatic formatting
170
+ *
171
+ * @param markdown - Pre-formatted Markdown string
172
+ * @returns The Markdown string unchanged
173
+ * @example
174
+ * ```typescript
175
+ * rawMD("*Hello*") // "*Hello*"
176
+ * ```
177
+ */
178
+ export declare function rawMD(markdown: string): string;
179
+ //# sourceMappingURL=markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["markdown.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEzC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE9C"}
@@ -0,0 +1,417 @@
1
+ import { describe, it, expect } from "bun:test";
2
+ import * as fmt from "..";
3
+
4
+ describe("Markdown (Legacy) Formatters", () => {
5
+ describe("Text Formatting", () => {
6
+ it("should format bold text", () => {
7
+ const result = fmt.boldMD("Hello");
8
+ expect(result).toBe("*Hello*");
9
+ });
10
+
11
+ it("should format italic text", () => {
12
+ const result = fmt.italicMD("Hello");
13
+ expect(result).toBe("_Hello_");
14
+ });
15
+
16
+ it("should format code text", () => {
17
+ const result = fmt.codeMD("const x = 1");
18
+ expect(result).toBe("`const x = 1`");
19
+ });
20
+
21
+ it("should format code block without language", () => {
22
+ const result = fmt.codeBlockMD("const x = 1");
23
+ expect(result).toBe("const x = 1\n");
24
+ });
25
+
26
+ it("should format code block with language", () => {
27
+ const result = fmt.codeBlockMD("const x = 1", "javascript");
28
+ expect(result).toBe("javascript\nconst x = 1\n");
29
+ });
30
+
31
+ it("should format multiline code block", () => {
32
+ const code = "const x = 1;\nconst y = 2;";
33
+ const result = fmt.codeBlockMD(code);
34
+ expect(result).toBe("const x = 1;\nconst y = 2;\n");
35
+ });
36
+
37
+ it("should format underline (HTML fallback)", () => {
38
+ const result = fmt.underlineMD("Hello");
39
+ expect(result).toBe("<u>Hello</u>");
40
+ });
41
+
42
+ it("should format strikethrough (HTML fallback)", () => {
43
+ const result = fmt.strikethroughMD("Hello");
44
+ expect(result).toBe("<s>Hello</s>");
45
+ });
46
+
47
+ it("should format spoiler (HTML fallback)", () => {
48
+ const result = fmt.spoilerMD("Secret");
49
+ expect(result).toBe("<tg-spoiler>Secret</tg-spoiler>");
50
+ });
51
+ });
52
+
53
+ describe("Links and Mentions", () => {
54
+ it("should format link", () => {
55
+ const result = fmt.linkMD("Google", "https://google.com");
56
+ expect(result).toBe("[Google](https://google.com)");
57
+ });
58
+
59
+ it("should format mention without name", () => {
60
+ const result = fmt.mentionMD(123456);
61
+ expect(result).toBe("tg://user?id=123456");
62
+ });
63
+
64
+ it("should format mention with name (ignored in MD)", () => {
65
+ const result = fmt.mentionMD(123456, "John Doe");
66
+ expect(result).toBe("tg://user?id=123456");
67
+ });
68
+
69
+ it("should format hashtag", () => {
70
+ const result = fmt.hashtagMD("test");
71
+ expect(result).toBe("#test");
72
+ });
73
+
74
+ it("should format email link", () => {
75
+ const result = fmt.emailMD("test@example.com");
76
+ expect(result).toBe("[test@example.com](mailto:test@example.com)");
77
+ });
78
+
79
+ it("should format URL link", () => {
80
+ const result = fmt.urlMD("https://example.com");
81
+ expect(result).toBe("[https://example.com](https://example.com)");
82
+ });
83
+
84
+ it("should format custom emoji (HTML fallback)", () => {
85
+ const result = fmt.customEmojiMD("5368324170672642286");
86
+ expect(result).toContain('<tg-emoji emoji-id="5368324170672642286">');
87
+ expect(result).toContain("👻");
88
+ });
89
+ });
90
+
91
+ describe("Raw Helper", () => {
92
+ it("should return input unchanged", () => {
93
+ const markdown = "*Hello* _World_";
94
+ const result = fmt.rawMD(markdown);
95
+ expect(result).toBe(markdown);
96
+ });
97
+
98
+ it("should preserve complex markdown", () => {
99
+ const markdown = "[Link](url) `code` **bold**";
100
+ const result = fmt.rawMD(markdown);
101
+ expect(result).toBe(markdown);
102
+ });
103
+ });
104
+
105
+ describe("Combined Formatting", () => {
106
+ it("should format bold + italic", () => {
107
+ const result = `${fmt.boldMD("Bold")} ${fmt.italicMD("Italic")}`;
108
+ expect(result).toBe("*Bold* _Italic_");
109
+ });
110
+
111
+ it("should format link + code", () => {
112
+ const result = `${fmt.linkMD("Docs", "https://example.com")} - ${fmt.codeMD("v1.0")}`;
113
+ expect(result).toBe("[Docs](https://example.com) - `v1.0`");
114
+ });
115
+
116
+ it("should format complex message", () => {
117
+ const message = `${fmt.boldMD("Title")}\n\n${fmt.codeBlockMD("const x = 1;", "js")}\n\nLink: ${fmt.linkMD("Click", "https://example.com")}`;
118
+ expect(message).toContain("*Title*");
119
+ expect(message).toContain("js"); // language prefix
120
+ expect(message).toContain("const x = 1;");
121
+ expect(message).toContain("[Click](https://example.com)");
122
+ });
123
+ });
124
+
125
+ describe("Edge Cases", () => {
126
+ it("should handle empty string", () => {
127
+ expect(fmt.boldMD("")).toBe("**"); // Empty string between asterisks
128
+ expect(fmt.italicMD("")).toBe("__"); // Empty string between underscores
129
+ expect(fmt.codeMD("")).toBe("``"); // Empty string between backticks
130
+ });
131
+
132
+ it("should handle special characters", () => {
133
+ const result = fmt.boldMD("Test [with] (special) chars");
134
+ expect(result).toBe("*Test [with] (special) chars*");
135
+ });
136
+
137
+ it("should handle unicode", () => {
138
+ const result = fmt.boldMD("🎉 Party! 🎊");
139
+ expect(result).toBe("*🎉 Party! 🎊*");
140
+ });
141
+
142
+ it("should handle very long text", () => {
143
+ const longText = "a".repeat(1000);
144
+ const result = fmt.boldMD(longText);
145
+ expect(result).toBe(`*${longText}*`);
146
+ expect(result.length).toBe(1002);
147
+ });
148
+ });
149
+ });
150
+
151
+ describe("MarkdownV2 Formatters", () => {
152
+ describe("Text Formatting", () => {
153
+ it("should format bold text", () => {
154
+ const result = fmt.boldMDv2("Hello");
155
+ expect(result).toBe("*Hello*");
156
+ });
157
+
158
+ it("should format italic text", () => {
159
+ const result = fmt.italicMDv2("Hello");
160
+ expect(result).toBe("_Hello_");
161
+ });
162
+
163
+ it("should format underline text", () => {
164
+ const result = fmt.underlineMDv2("Hello");
165
+ expect(result).toBe("__Hello__");
166
+ });
167
+
168
+ it("should format strikethrough text", () => {
169
+ const result = fmt.strikethroughMDv2("Hello");
170
+ expect(result).toBe("~Hello~");
171
+ });
172
+
173
+ it("should format spoiler text", () => {
174
+ const result = fmt.spoilerMDv2("Secret");
175
+ expect(result).toBe("||Secret||");
176
+ });
177
+
178
+ it("should format code text", () => {
179
+ const result = fmt.codeMDv2("const x = 1");
180
+ expect(result).toBe("`const x = 1`");
181
+ });
182
+
183
+ it("should format code block without language", () => {
184
+ const result = fmt.codeBlockMDv2("const x = 1");
185
+ expect(result).toBe("const x = 1\n");
186
+ });
187
+
188
+ it("should format code block with language", () => {
189
+ const result = fmt.codeBlockMDv2("const x = 1", "javascript");
190
+ expect(result).toBe("javascript\nconst x = 1\n");
191
+ });
192
+ });
193
+
194
+ describe("Links and Mentions", () => {
195
+ it("should format link", () => {
196
+ const result = fmt.linkMDv2("Google", "https://google.com");
197
+ expect(result).toBe("[Google](https://google.com)");
198
+ });
199
+
200
+ it("should format mention without name", () => {
201
+ const result = fmt.mentionMDv2(123456);
202
+ expect(result).toBe("[user](tg://user?id=123456)");
203
+ });
204
+
205
+ it("should format mention with custom name", () => {
206
+ const result = fmt.mentionMDv2(123456, "John Doe");
207
+ expect(result).toBe("[John Doe](tg://user?id=123456)");
208
+ });
209
+
210
+ it("should format hashtag", () => {
211
+ const result = fmt.hashtagMDv2("test");
212
+ expect(result).toBe("#test");
213
+ });
214
+
215
+ it("should format email link", () => {
216
+ const result = fmt.emailMDv2("test@example.com");
217
+ expect(result).toBe("[test@example.com](mailto:test@example.com)");
218
+ });
219
+
220
+ it("should format URL link", () => {
221
+ const result = fmt.urlMDv2("https://example.com");
222
+ expect(result).toBe("[https://example.com](https://example.com)");
223
+ });
224
+
225
+ it("should format custom emoji", () => {
226
+ const result = fmt.customEmojiMDv2("5368324170672642286");
227
+ expect(result).toContain("👻");
228
+ expect(result).toContain("5368324170672642286");
229
+ });
230
+ });
231
+
232
+ describe("Escape Function", () => {
233
+ it("should escape all reserved characters", () => {
234
+ const text = "_*[]()~`>#+-=|{}.!";
235
+ const result = fmt.escapeMDv2(text);
236
+ expect(result).toBe(
237
+ "\\_\\*\\[\\]\\(\\)\\~\\`\\>\\#\\+\\-\\=\\|\\{\\}\\.\\!",
238
+ );
239
+ });
240
+
241
+ it("should not escape regular text", () => {
242
+ const text = "Hello World 123";
243
+ const result = fmt.escapeMDv2(text);
244
+ expect(result).toBe("Hello World 123");
245
+ });
246
+
247
+ it("should escape mixed content", () => {
248
+ const text = "Click [here](url) for *bold* text";
249
+ const result = fmt.escapeMDv2(text);
250
+ expect(result).toContain("\\[");
251
+ expect(result).toContain("\\]");
252
+ expect(result).toContain("\\*");
253
+ });
254
+
255
+ it("should handle empty string", () => {
256
+ const result = fmt.escapeMDv2("");
257
+ expect(result).toBe("");
258
+ });
259
+
260
+ it("should escape repeated characters", () => {
261
+ const text = "***";
262
+ const result = fmt.escapeMDv2(text);
263
+ expect(result).toBe("\\*\\*\\*");
264
+ });
265
+ });
266
+
267
+ describe("Raw Helper", () => {
268
+ it("should return input unchanged", () => {
269
+ const markdown = "*Hello* _World_";
270
+ const result = fmt.rawMDv2(markdown);
271
+ expect(result).toBe(markdown);
272
+ });
273
+ });
274
+
275
+ describe("Combined Formatting", () => {
276
+ it("should format bold + italic + underline", () => {
277
+ const result = `${fmt.boldMDv2("Bold")} ${fmt.italicMDv2("Italic")} ${fmt.underlineMDv2("Underline")}`;
278
+ expect(result).toBe("*Bold* _Italic_ __Underline__");
279
+ });
280
+
281
+ it("should format strikethrough + spoiler", () => {
282
+ const result = `${fmt.strikethroughMDv2("Old")} ${fmt.spoilerMDv2("Secret")}`;
283
+ expect(result).toBe("~Old~ ||Secret||");
284
+ });
285
+
286
+ it("should format complex message", () => {
287
+ const message =
288
+ `${fmt.boldMDv2("Title")}\n\n` +
289
+ `${fmt.codeBlockMDv2("const x = 1;", "js")}\n\n` +
290
+ `Link: ${fmt.linkMDv2("Click", "https://example.com")}\n` +
291
+ `Mention: ${fmt.mentionMDv2(123456, "User")}\n` +
292
+ `Hashtag: ${fmt.hashtagMDv2("test")}`;
293
+
294
+ expect(message).toContain("*Title*");
295
+ expect(message).toContain("js"); // language prefix
296
+ expect(message).toContain("const x = 1;");
297
+ expect(message).toContain("[Click]");
298
+ expect(message).toContain("[User](tg://user?id=123456)");
299
+ expect(message).toContain("#test");
300
+ });
301
+ });
302
+
303
+ describe("Edge Cases", () => {
304
+ it("should handle empty string", () => {
305
+ expect(fmt.boldMDv2("")).toBe("**"); // Empty string between asterisks
306
+ expect(fmt.italicMDv2("")).toBe("__"); // Empty string between underscores
307
+ expect(fmt.underlineMDv2("")).toBe("____"); // Empty string between double underscores
308
+ expect(fmt.strikethroughMDv2("")).toBe("~~"); // Empty string between tildes
309
+ expect(fmt.spoilerMDv2("")).toBe("||||"); // Empty string between double pipes
310
+ });
311
+
312
+ it("should handle special characters that need escaping", () => {
313
+ const text = "Text with [brackets] and (parens)";
314
+ const escaped = fmt.escapeMDv2(text);
315
+ expect(escaped).toContain("\\[");
316
+ expect(escaped).toContain("\\]");
317
+ expect(escaped).toContain("\\(");
318
+ expect(escaped).toContain("\\)");
319
+ });
320
+
321
+ it("should handle unicode", () => {
322
+ const result = fmt.boldMDv2("🎉 Party! 🎊");
323
+ expect(result).toBe("*🎉 Party! 🎊*");
324
+ });
325
+
326
+ it("should handle very long text", () => {
327
+ const longText = "a".repeat(1000);
328
+ const result = fmt.boldMDv2(longText);
329
+ expect(result).toBe(`*${longText}*`);
330
+ expect(result.length).toBe(1002);
331
+ });
332
+
333
+ it("should escape all reserved chars in formatter context", () => {
334
+ const text = "Text with * and _ and ~ and |";
335
+ const escaped = fmt.escapeMDv2(text);
336
+ expect(escaped).toContain("\\*");
337
+ expect(escaped).toContain("\\_");
338
+ expect(escaped).toContain("\\~");
339
+ expect(escaped).toContain("\\|");
340
+ });
341
+ });
342
+
343
+ describe("MarkdownV2-Specific Features", () => {
344
+ it("should format underline which is MDv2-only", () => {
345
+ const result = fmt.underlineMDv2("Important");
346
+ expect(result).toBe("__Important__");
347
+ });
348
+
349
+ it("should format strikethrough which is MDv2-only", () => {
350
+ const result = fmt.strikethroughMDv2("Deleted");
351
+ expect(result).toBe("~Deleted~");
352
+ });
353
+
354
+ it("should format spoiler which is MDv2-only", () => {
355
+ const result = fmt.spoilerMDv2("Surprise!");
356
+ expect(result).toBe("||Surprise!||");
357
+ });
358
+
359
+ it("should combine multiple MDv2-exclusive features", () => {
360
+ const result =
361
+ `${fmt.underlineMDv2("Under")} ` +
362
+ `${fmt.strikethroughMDv2("Strike")} ` +
363
+ `${fmt.spoilerMDv2("Spoiler")}`;
364
+ expect(result).toBe("__Under__ ~Strike~ ||Spoiler||");
365
+ });
366
+ });
367
+ });
368
+
369
+ describe("Markdown vs MarkdownV2 Comparison", () => {
370
+ it("should format bold the same way", () => {
371
+ expect(fmt.boldMD("Test")).toBe(fmt.boldMDv2("Test"));
372
+ });
373
+
374
+ it("should format italic the same way", () => {
375
+ expect(fmt.italicMD("Test")).toBe(fmt.italicMDv2("Test"));
376
+ });
377
+
378
+ it("should format code the same way", () => {
379
+ expect(fmt.codeMD("Test")).toBe(fmt.codeMDv2("Test"));
380
+ });
381
+
382
+ it("should format code block the same way", () => {
383
+ expect(fmt.codeBlockMD("Test")).toBe(fmt.codeBlockMDv2("Test"));
384
+ expect(fmt.codeBlockMD("Test", "js")).toBe(fmt.codeBlockMDv2("Test", "js"));
385
+ });
386
+
387
+ it("should differ in underline (MD uses HTML, MDv2 uses __)", () => {
388
+ expect(fmt.underlineMD("Test")).toBe("<u>Test</u>");
389
+ expect(fmt.underlineMDv2("Test")).toBe("__Test__");
390
+ });
391
+
392
+ it("should differ in strikethrough (MD uses HTML, MDv2 uses ~)", () => {
393
+ expect(fmt.strikethroughMD("Test")).toBe("<s>Test</s>");
394
+ expect(fmt.strikethroughMDv2("Test")).toBe("~Test~");
395
+ });
396
+
397
+ it("should differ in spoiler (MD uses HTML, MDv2 uses ||)", () => {
398
+ expect(fmt.spoilerMD("Test")).toBe("<tg-spoiler>Test</tg-spoiler>");
399
+ expect(fmt.spoilerMDv2("Test")).toBe("||Test||");
400
+ });
401
+
402
+ it("should differ in mention (MD is URL-only, MDv2 is link)", () => {
403
+ expect(fmt.mentionMD(123456, "John")).toBe("tg://user?id=123456");
404
+ expect(fmt.mentionMDv2(123456, "John")).toBe("[John](tg://user?id=123456)");
405
+ });
406
+
407
+ it("should have different escape functions", () => {
408
+ const text = "*_[]()~`>#+-=|{}.!";
409
+ const mdEscaped = fmt.escapeMarkdown(text);
410
+ const mdv2Escaped = fmt.escapeMarkdownV2(text);
411
+
412
+ expect(mdEscaped).not.toBe(mdv2Escaped);
413
+ expect(mdv2Escaped).toContain("\\~");
414
+ expect(mdv2Escaped).toContain("\\>");
415
+ expect(mdv2Escaped).toContain("\\#");
416
+ });
417
+ });