]> git.giacomolli.uk - onechat/blob - telegram_markdown_test.go
Check test assertions with testifylint
[onechat] / telegram_markdown_test.go
1 package main
2
3 import (
4 "testing"
5
6 "github.com/stretchr/testify/require"
7 )
8
9 func TestMarkdownEntities(t *testing.T) {
10 testCases := []struct {
11 name string
12 markdown string
13 text string
14 entities []TelegramEntity
15 }{
16 {
17 name: "double asterisks give bold",
18 markdown: "a **b** c",
19 text: "a b c",
20 entities: []TelegramEntity{
21 {
22 Type: "bold",
23 Offset: 2,
24 Length: 1,
25 },
26 },
27 },
28 {
29 name: "double underscores give bold",
30 markdown: "__bold__",
31 text: "bold",
32 entities: []TelegramEntity{
33 {
34 Type: "bold",
35 Offset: 0,
36 Length: 4,
37 },
38 },
39 },
40 {
41 name: "single asterisk gives italic",
42 markdown: "an *odd* word",
43 text: "an odd word",
44 entities: []TelegramEntity{
45 {
46 Type: "italic",
47 Offset: 3,
48 Length: 3,
49 },
50 },
51 },
52 {
53 name: "single underscore gives italic",
54 markdown: "an _odd_ word",
55 text: "an odd word",
56 entities: []TelegramEntity{
57 {
58 Type: "italic",
59 Offset: 3,
60 Length: 3,
61 },
62 },
63 },
64 {
65 name: "double tildes give strikethrough",
66 markdown: "~~old~~ new",
67 text: "old new",
68 entities: []TelegramEntity{
69 {
70 Type: "strikethrough",
71 Offset: 0,
72 Length: 3,
73 },
74 },
75 },
76 {
77 name: "backticks give inline code with no entity inside",
78 markdown: "run `go **test**` now",
79 text: "run go **test** now",
80 entities: []TelegramEntity{
81 {
82 Type: "code",
83 Offset: 4,
84 Length: 11,
85 },
86 },
87 },
88 {
89 name: "double backticks hold a backtick",
90 markdown: "``a ` b``",
91 text: "a ` b",
92 entities: []TelegramEntity{
93 {
94 Type: "code",
95 Offset: 0,
96 Length: 5,
97 },
98 },
99 },
100 {
101 name: "link gives a text link",
102 markdown: "see [the docs](https://example.com/a_b) here",
103 text: "see the docs here",
104 entities: []TelegramEntity{
105 {
106 Type: "text_link",
107 Offset: 4,
108 Length: 8,
109 URL: "https://example.com/a_b",
110 },
111 },
112 },
113 {
114 name: "bold inside a link nests",
115 markdown: "[a **b** c](https://example.com)",
116 text: "a b c",
117 entities: []TelegramEntity{
118 {
119 Type: "text_link",
120 Offset: 0,
121 Length: 5,
122 URL: "https://example.com",
123 },
124 {
125 Type: "bold",
126 Offset: 2,
127 Length: 1,
128 },
129 },
130 },
131 {
132 name: "code block with a language gives pre with the language",
133 markdown: "Commit:\n```text\nFix the **bug**\n\nBody\n```\nDone.",
134 text: "Commit:\nFix the **bug**\n\nBody\nDone.",
135 entities: []TelegramEntity{
136 {
137 Type: "pre",
138 Offset: 8,
139 Length: 21,
140 Language: "text",
141 },
142 },
143 },
144 {
145 name: "code block without a language gives pre",
146 markdown: "```\nls -l\n```",
147 text: "ls -l",
148 entities: []TelegramEntity{
149 {
150 Type: "pre",
151 Offset: 0,
152 Length: 5,
153 },
154 },
155 },
156 {
157 name: "heading becomes bold with its inline marks",
158 markdown: "## The `plan`\nnext",
159 text: "The plan\nnext",
160 entities: []TelegramEntity{
161 {
162 Type: "bold",
163 Offset: 0,
164 Length: 8,
165 },
166 {
167 Type: "code",
168 Offset: 4,
169 Length: 4,
170 },
171 },
172 },
173 {
174 name: "list, quote and table stay as text",
175 markdown: "- one\n* two\n1. three\n> quote\n| a | b |\n|---|---|",
176 text: "- one\n* two\n1. three\n> quote\n| a | b |\n|---|---|",
177 },
178 {
179 name: "asterisk of a list item with italic later stays",
180 markdown: "* item with *stress*",
181 text: "* item with stress",
182 entities: []TelegramEntity{
183 {
184 Type: "italic",
185 Offset: 12,
186 Length: 6,
187 },
188 },
189 },
190 {
191 name: "unclosed marks stay as text",
192 markdown: "**open, `tick, [link](, ~~gone and *star",
193 text: "**open, `tick, [link](, ~~gone and *star",
194 },
195 {
196 name: "unclosed code fence stays as text",
197 markdown: "```go\nfmt.Println()",
198 text: "```go\nfmt.Println()",
199 },
200 {
201 name: "snake case words stay as text",
202 markdown: "set snake_case_words and max_tokens_count",
203 text: "set snake_case_words and max_tokens_count",
204 },
205 {
206 name: "multiplication stays as text",
207 markdown: "2 * 3 * 4",
208 text: "2 * 3 * 4",
209 },
210 {
211 name: "mark does not cross a line break",
212 markdown: "**a\nb**",
213 text: "**a\nb**",
214 },
215 {
216 name: "backslash makes a mark plain",
217 markdown: `\*not italic\* and C:\Users`,
218 text: `*not italic* and C:\Users`,
219 },
220 {
221 name: "character outside the BMP moves the offset by two",
222 markdown: "\U0001F600 **b**",
223 text: "\U0001F600 b",
224 entities: []TelegramEntity{
225 {
226 Type: "bold",
227 Offset: 3,
228 Length: 1,
229 },
230 },
231 },
232 }
233
234 for _, tc := range testCases {
235 t.Run(tc.name, func(t *testing.T) {
236 text, entities := telegramMarkdownEntities(tc.markdown)
237 require.Equal(t, tc.text, text)
238 require.Equal(t, tc.entities, entities)
239 })
240 }
241 }
242
243 func TestEscapeTags(t *testing.T) {
244 testCases := []struct {
245 name string
246 markdown string
247 escaped string
248 }{
249 {
250 name: "tag in prose shows as text",
251 markdown: "Use a <details> tag for the rest.",
252 escaped: "Use a &lt;details> tag for the rest.",
253 },
254 {
255 name: "tag in inline code stays as written",
256 markdown: "Use `<details>` and `` a<b && c ``, not <b>.",
257 escaped: "Use `<details>` and `` a<b && c ``, not &lt;b>.",
258 },
259 {
260 name: "tag in a code block stays as written",
261 markdown: "<p>\n\n```html\n<details>&amp;\n```\n\n<p>",
262 escaped: "&lt;p>\n\n```html\n<details>&amp;\n```\n\n&lt;p>",
263 },
264 {
265 name: "code block that does not close stays as written",
266 markdown: "a <b>\n```\n<details>",
267 escaped: "a &lt;b>\n```\n<details>",
268 },
269 {
270 name: "tag in a table cell shows as text",
271 markdown: "| Tag | Use |\n|-----|-----|\n| <br> | `<br>` |",
272 escaped: "| Tag | Use |\n|-----|-----|\n| &lt;br> | `<br>` |",
273 },
274 {
275 name: "autolink shows as text",
276 markdown: "See <https://example.com>.",
277 escaped: "See &lt;https://example.com>.",
278 },
279 {
280 name: "entity in prose shows as written",
281 markdown: "Write &lt; or &amp; for R&D.",
282 escaped: "Write &amp;lt; or &amp;amp; for R&amp;D.",
283 },
284 {
285 name: "backslash before a bracket gets no special case",
286 markdown: `a \<b> and \&c`,
287 escaped: `a \&lt;b> and \&amp;c`,
288 },
289 {
290 name: "backtick after a backslash opens no code",
291 markdown: "a \\`<b>\\` c",
292 escaped: "a \\`&lt;b>\\` c",
293 },
294 {
295 name: "backtick run that opens no code stays as text",
296 markdown: "a ``b` <c>",
297 escaped: "a ``b` &lt;c>",
298 },
299 {
300 name: "dollar, pipe, and equals signs stay as written",
301 markdown: "$5 or $10, a || b, ==c==",
302 escaped: "$5 or $10, a || b, ==c==",
303 },
304 }
305
306 for _, tc := range testCases {
307 t.Run(tc.name, func(t *testing.T) {
308 require.Equal(t, tc.escaped, telegramEscapeTags(tc.markdown))
309 require.Equal(t, tc.markdown, telegramUnescapeTags(tc.escaped))
310 })
311 }
312 }