punkweb-bb 0.2.3__py3-none-any.whl → 0.3.0__py3-none-any.whl

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 (106) hide show
  1. punkweb_bb/__pycache__/admin.cpython-311.pyc +0 -0
  2. punkweb_bb/__pycache__/admin_forms.cpython-311.pyc +0 -0
  3. punkweb_bb/__pycache__/forms.cpython-311.pyc +0 -0
  4. punkweb_bb/__pycache__/models.cpython-311.pyc +0 -0
  5. punkweb_bb/__pycache__/settings.cpython-311.pyc +0 -0
  6. punkweb_bb/__pycache__/tests.cpython-311.pyc +0 -0
  7. punkweb_bb/__pycache__/urls.cpython-311.pyc +0 -0
  8. punkweb_bb/__pycache__/utils.cpython-311.pyc +0 -0
  9. punkweb_bb/__pycache__/views.cpython-311.pyc +0 -0
  10. punkweb_bb/__pycache__/widgets.cpython-311.pyc +0 -0
  11. punkweb_bb/admin.py +0 -4
  12. punkweb_bb/admin_forms.py +6 -5
  13. punkweb_bb/forms.py +13 -5
  14. punkweb_bb/migrations/0005_alter_thread_options.py +24 -0
  15. punkweb_bb/migrations/0006_remove_boardprofile__signature_rendered_and_more.py +60 -0
  16. punkweb_bb/migrations/__pycache__/0005_alter_thread_options.cpython-311.pyc +0 -0
  17. punkweb_bb/migrations/__pycache__/0006_remove_boardprofile__signature_rendered_and_more.cpython-311.pyc +0 -0
  18. punkweb_bb/models.py +6 -6
  19. punkweb_bb/settings.py +1 -0
  20. punkweb_bb/static/punkweb_bb/css/thread.css +24 -0
  21. punkweb_bb/static/punkweb_bb/editor/markdown-editor.js +23 -0
  22. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/.eslintrc.json +15 -0
  23. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/.gitignore +108 -0
  24. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/.prettierrc.json +1 -0
  25. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/LICENSE +21 -0
  26. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/README.md +240 -0
  27. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/babel.config.json +14 -0
  28. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/dist/blank.html +18 -0
  29. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/dist/demo.html +126 -0
  30. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/dist/tiny-mde.css +231 -0
  31. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/dist/tiny-mde.js +3086 -0
  32. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/dist/tiny-mde.min.css +1 -0
  33. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/dist/tiny-mde.min.js +1 -0
  34. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/dist/tiny-mde.tiny.js +1 -0
  35. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/docs/_config.yml +1 -0
  36. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/docs/_layouts/default.html +50 -0
  37. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/docs/index.md +174 -0
  38. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/globals.d.ts +172 -0
  39. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/gulpfile.mjs +226 -0
  40. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/jest/block.test.js +696 -0
  41. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/jest/commandbar.test.js +84 -0
  42. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/jest/inline.test.js +486 -0
  43. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/jest/interaction.test.js +31 -0
  44. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/jest/setup.test.js +164 -0
  45. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/jest/util/config.js +2 -0
  46. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/jest/util/server.js +9 -0
  47. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/jest/util/setup.js +1 -0
  48. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/jest/util/test-helpers.js +98 -0
  49. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/jest-puppeteer.config.js +8 -0
  50. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/jest.config.js +13 -0
  51. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/package-lock.json +16295 -0
  52. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/package.json +72 -0
  53. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/TinyMDE.js +1926 -0
  54. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/TinyMDECommandBar.js +256 -0
  55. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/css/commandbar.css +72 -0
  56. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/css/editor.css +157 -0
  57. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/css/index.css +3 -0
  58. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/grammar.js +300 -0
  59. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/html/blank.html +18 -0
  60. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/html/demo.html +126 -0
  61. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/index.js +4 -0
  62. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/blockquote.svg +1 -0
  63. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/bold.svg +1 -0
  64. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/clear_formatting.svg +1 -0
  65. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/code.svg +1 -0
  66. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/h1.svg +1 -0
  67. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/h2.svg +1 -0
  68. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/hr.svg +1 -0
  69. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/image.svg +1 -0
  70. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/italic.svg +1 -0
  71. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/link.svg +1 -0
  72. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/ol.svg +1 -0
  73. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/strikethrough.svg +1 -0
  74. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/svg.js +17 -0
  75. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/ul.svg +1 -0
  76. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/tiny.js +3 -0
  77. punkweb_bb/templates/punkweb_bb/base_delete_modal.html +13 -0
  78. punkweb_bb/templates/punkweb_bb/index.html +2 -2
  79. punkweb_bb/templates/punkweb_bb/partials/category_delete.html +4 -8
  80. punkweb_bb/templates/punkweb_bb/partials/post_delete.html +4 -8
  81. punkweb_bb/templates/punkweb_bb/partials/shout_delete.html +4 -8
  82. punkweb_bb/templates/punkweb_bb/partials/subcategory_delete.html +4 -8
  83. punkweb_bb/templates/punkweb_bb/partials/thread_delete.html +4 -8
  84. punkweb_bb/templates/punkweb_bb/partials/thread_move.html +24 -0
  85. punkweb_bb/templates/punkweb_bb/profile.html +2 -2
  86. punkweb_bb/templates/punkweb_bb/shoutbox/shout_list.html +2 -2
  87. punkweb_bb/templates/punkweb_bb/subcategory.html +1 -1
  88. punkweb_bb/templates/punkweb_bb/thread.html +24 -14
  89. punkweb_bb/templates/punkweb_bb/widgets/markdown-editor.html +3 -0
  90. punkweb_bb/templatetags/__pycache__/markdown.cpython-311.pyc +0 -0
  91. punkweb_bb/templatetags/__pycache__/render.cpython-311.pyc +0 -0
  92. punkweb_bb/templatetags/__pycache__/shoutbox_bbcode.cpython-311.pyc +0 -0
  93. punkweb_bb/templatetags/__pycache__/shoutbox_render.cpython-311.pyc +0 -0
  94. punkweb_bb/templatetags/render.py +48 -0
  95. punkweb_bb/templatetags/shoutbox_render.py +22 -0
  96. punkweb_bb/tests.py +3 -3
  97. punkweb_bb/urls.py +1 -0
  98. punkweb_bb/utils.py +19 -3
  99. punkweb_bb/views.py +35 -4
  100. punkweb_bb/widgets.py +20 -0
  101. {punkweb_bb-0.2.3.dist-info → punkweb_bb-0.3.0.dist-info}/METADATA +56 -41
  102. {punkweb_bb-0.2.3.dist-info → punkweb_bb-0.3.0.dist-info}/RECORD +105 -38
  103. punkweb_bb/templatetags/shoutbox_bbcode.py +0 -14
  104. {punkweb_bb-0.2.3.dist-info → punkweb_bb-0.3.0.dist-info}/LICENSE +0 -0
  105. {punkweb_bb-0.2.3.dist-info → punkweb_bb-0.3.0.dist-info}/WHEEL +0 -0
  106. {punkweb_bb-0.2.3.dist-info → punkweb_bb-0.3.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,696 @@
1
+ // ATX headings -----------------------------------------------------------------------------------
2
+
3
+ test('Correctly parses ATX headings: # H1', async () => {
4
+ let heading = ' XXXA';
5
+ for (let level = 1; level <= 6; level++) {
6
+ heading = `#${heading}`;
7
+ const editor = await initTinyMDE(heading);
8
+ expect(await editor.lineType(0)).toMatch(`TMH${level}`);
9
+ editor.destroy();
10
+ }
11
+ });
12
+
13
+ test('Inline content processed in ATX heading: # *em*', async () => {
14
+ const editor = await initTinyMDE('# *XXXA*');
15
+ expect(await editor.lineHTML(0)).toMatch(/<em[^>]*>XXXA/);
16
+ expect(await editor.lineType(0)).toMatch('TMH1');
17
+ editor.destroy();
18
+ })
19
+
20
+ test('ATX headings can include any number of trailing #s: # H1 #####', async () => {
21
+ const editor = await initTinyMDE('# H1 ##### ');
22
+ expect(await editor.lineType(0)).toMatch('TMH1');
23
+ expect(await editor.lineHTML(0)).toMatch(/<span[^>]* class\s*=\s*["']?[^"'>]*TMMark[^>]*>\s*#####/);
24
+ editor.destroy();
25
+ });
26
+
27
+ test('ATX headings\' trailing #s must be preceded by space: # H1#####', async () => {
28
+ const editor = await initTinyMDE('# H1##### ');
29
+ expect(await editor.lineType(0)).toMatch('TMH1');
30
+ expect(await editor.lineHTML(0)).not.toMatch(/<span[^>]* class\s*=\s*["']?[^"'>]*TMMark[^>]*>\s*#####/);
31
+ editor.destroy();
32
+ });
33
+
34
+ // // Blank lines -----------------------------------------------------------------------------------
35
+
36
+ test('Lines including only whitespace are considered blank', async () => {
37
+ const editor = await initTinyMDE('\n \n \n');
38
+ for (let line = 0; line < 3; line++) {
39
+ expect(await editor.lineType(line)).toMatch('TMBlankLine');
40
+ }
41
+ editor.destroy();
42
+ });
43
+
44
+ test('Blank lines include a <br>', async () => {
45
+ const editor = await initTinyMDE('\n\n\n');
46
+ for (let line = 0; line < 3; line++) {
47
+ expect(await editor.lineHTML(line)).toMatch(/<br[^>]*>/);
48
+ }
49
+ editor.destroy();
50
+ });
51
+
52
+ // // Thematic breaks (HR) -----------------------------------------------------------------------------------
53
+
54
+ test('Simple thematic breaks are recognized with all characters: ---, ***, ___', async () => {
55
+ const breaks = ['---', '***', '___'];
56
+ for (let br of breaks) {
57
+ const editor = await initTinyMDE(br);
58
+ expect(await editor.lineType(0)).toMatch('TMHR');
59
+ editor.destroy();
60
+ }
61
+ });
62
+
63
+ test('Thematic breaks may include spaces: * * *', async () => {
64
+ const editor = await initTinyMDE(' * * * ');
65
+ expect(await editor.lineType(0)).toMatch('TMHR');
66
+ editor.destroy();
67
+ });
68
+
69
+ test('Thematic breaks may include more than 3 symbols: *****', async () => {
70
+ const editor = await initTinyMDE('*****');
71
+ expect(await editor.lineType(0)).toMatch('TMHR');
72
+ editor.destroy();
73
+ });
74
+
75
+ test('Thematic breaks can\'t be mixed and matched: ***---', async () => {
76
+ const editor = await initTinyMDE('***---');
77
+ expect(await editor.lineType(0)).not.toMatch('TMHR');
78
+ editor.destroy();
79
+ });
80
+
81
+ test('Thematic break take precendence over UL: * * *, - - -', async () => {
82
+ const breaks = ['- - -', '* * *'];
83
+ for (let br of breaks) {
84
+ const editor = await initTinyMDE(br);
85
+ expect(await editor.lineType(0)).toMatch('TMHR');
86
+ editor.destroy();
87
+ }
88
+ });
89
+
90
+ // // Setext headings -----------------------------------------------------------------------------------
91
+
92
+ test('Basic setext H1 works: H1\\n==', async () => {
93
+ const editor = await initTinyMDE('H1\n==');
94
+ expect(await editor.lineType(0)).toMatch('TMSetextH1');
95
+ expect(await editor.lineType(1)).toMatch('TMSetextH1Marker');
96
+ editor.destroy();
97
+ });
98
+
99
+ test('Setext marker can have up to 3 leading and any number of trailing spaces: H1\\n ==', async () => {
100
+ const editor = await initTinyMDE('H1\n == ');
101
+ expect(await editor.lineType(0)).toMatch('TMSetextH1');
102
+ expect(await editor.lineType(1)).toMatch('TMSetextH1Marker');
103
+ editor.destroy();
104
+ });
105
+
106
+ test('Setext H1 can span multiple lines: H1\\nStill H1\\n==', async () => {
107
+ const editor = await initTinyMDE('H1\nH1\n==');
108
+ expect(await editor.lineType(0)).toMatch('TMSetextH1');
109
+ expect(await editor.lineType(1)).toMatch('TMSetextH1');
110
+ expect(await editor.lineType(2)).toMatch('TMSetextH1Marker');
111
+ editor.destroy();
112
+ });
113
+
114
+ test('Setext H1 marker after blank line is invalid: \\n\\n==', async () => {
115
+ const editor = await initTinyMDE('\n==');
116
+ expect(await editor.lineType(0)).toMatch('TMBlankLine');
117
+ expect(await editor.lineType(1)).toMatch('TMPara');
118
+ editor.destroy();
119
+ });
120
+
121
+ test('Setext H1 marker after the following blocks is invalid: code fence, ATX heading, block quote, thematic break, list item, or HTML block', async () => {
122
+ const previousBlocks = ['```\nXXXA\n```\n', '# XXXA\n', '> XXXA\n', '---\n', '- XXXA\n', '<!-- Comment -->'];
123
+ for (let block of previousBlocks) {
124
+ const editor = await initTinyMDE(`${block}===`);
125
+ expect(await editor.lineType(await editor.numLines() - 1)).not.toMatch('TMSetextH1Marker');
126
+ editor.destroy();
127
+ }
128
+ });
129
+
130
+ test('Blank lines break setext heading', async () => {
131
+ const editor = await initTinyMDE('Not H1\n\nH1\nH1\n==');
132
+ expect(await editor.lineType(0)).toMatch('TMPara');
133
+ expect(await editor.lineType(1)).toMatch('TMBlankLine');
134
+ expect(await editor.lineType(2)).toMatch('TMSetextH1');
135
+ expect(await editor.lineType(3)).toMatch('TMSetextH1');
136
+ expect(await editor.lineType(4)).toMatch('TMSetextH1Marker');
137
+ editor.destroy();
138
+ });
139
+
140
+ test('Setext H2 works: H2\\nStill H2\\n--', async () => {
141
+ const editor = await initTinyMDE('H2\nH2\n--');
142
+ expect(await editor.lineType(0)).toMatch('TMSetextH2');
143
+ expect(await editor.lineType(1)).toMatch('TMSetextH2');
144
+ expect(await editor.lineType(2)).toMatch('TMSetextH2Marker');
145
+ editor.destroy();
146
+ });
147
+
148
+ test('Setext H2 takes precedence over thematic break: H2\\n---', async () => {
149
+ const editor = await initTinyMDE('H2\n---');
150
+ expect(await editor.lineType(0)).toMatch('TMSetextH2');
151
+ expect(await editor.lineType(1)).toMatch('TMSetextH2Marker');
152
+ editor.destroy();
153
+ });
154
+
155
+ test('Empty list item takes precedence over setext H2: Not H2\\n-', async () => {
156
+ const editor = await initTinyMDE('Not H2\n- ');
157
+ expect(await editor.lineType(0)).toMatch('TMPara');
158
+ expect(await editor.lineType(1)).toMatch('TMUL');
159
+ editor.destroy();
160
+ });
161
+
162
+ // // Indented code -----------------------------------------------------------------------------------
163
+
164
+ test('Indented code block parsed correctly: code', async () => {
165
+ const editor = await initTinyMDE(' code');
166
+ expect(await editor.lineType(0)).toMatch('TMIndentedCode');
167
+ editor.destroy();
168
+ });
169
+
170
+ test('Indented code can\'t interrupt paragraph: Paragraph\\n not code', async () => {
171
+ // Paragraphs contained in: TMPara, TMUL, TMOL, TMBlockquote
172
+ const testCases = [
173
+ 'Para\n Not code',
174
+ '- UL\n Not code',
175
+ '1. OL\n Not code',
176
+ '> Blockquote\n Not code'
177
+ ];
178
+ for (let testCase of testCases) {
179
+ const editor = await initTinyMDE(testCase);
180
+ expect(await editor.lineType(1)).not.toMatch('TMIndentedCode');
181
+ editor.destroy();
182
+ }
183
+ });
184
+
185
+ test('Indented code can follow after non-paragraph: # Heading\\n code', async () => {
186
+ const testCases = [
187
+ '# Heading\n Code',
188
+ 'Setext heading\n====\n Code',
189
+ '<pre>HTML block</pre>\n Code',
190
+ '---\n Code',
191
+ '~~~\nFenced code\n~~~\n Code',
192
+ '[ref]: https://abc.de\n Code',
193
+ ];
194
+ for (let testCase of testCases) {
195
+ const editor = await initTinyMDE(testCase);
196
+ expect(await editor.lineType((await editor.numLines()) - 1)).toMatch('TMIndentedCode');
197
+ editor.destroy();
198
+ }
199
+ });
200
+
201
+ // Fenced code blocks -----------------------------------------------------------------------------------
202
+ test('Basic fenced code block works: ```\\nThis is\\ncode\\n```', async () => {
203
+ const testCases = [
204
+ '~~~\nFenced\nCode\n~~~',
205
+ '```\nFenced\nCode\n```'
206
+ ];
207
+ for (let testCase of testCases) {
208
+ const editor = await initTinyMDE(testCase);
209
+ expect(await editor.lineType(0)).toMatch(/^TMCodeFence[A-Za-z]*Open$/);
210
+ for (let i = 1; i < editor.numLines() - 1; i++) {
211
+ expect(await editor.lineType(i)).toMatch(/^TMFencedCode[A-Za-z]*$/);
212
+ }
213
+ expect(await editor.lineType(await editor.numLines()-1)).toMatch(/^TMCodeFence[A-Za-z]*Close$/);
214
+ editor.destroy();
215
+ }
216
+ });
217
+
218
+ test('Opening fence can contain info string: ```js\\nthis.is();\\ncode = {};\\n```', async () => {
219
+ const testCases = [
220
+ '~~~javascript\nFenced\nCode\n~~~',
221
+ '```javascript\nFenced\nCode\n```',
222
+ '~~~ javascript \nFenced\nCode\n~~~',
223
+ '``` javascript \nFenced\nCode\n```'
224
+ ];
225
+ for (let testCase of testCases) {
226
+ const editor = await initTinyMDE(testCase);
227
+ expect(await editor.lineType(0)).toMatch(/^TMCodeFence[A-Za-z]*Open$/);
228
+ for (let i = 1; i < editor.numLines() - 1; i++) {
229
+ expect(await editor.lineType(i)).toMatch(/^TMFencedCode[A-Za-z]*$/);
230
+ }
231
+ expect(await editor.lineType(await editor.numLines()-1)).toMatch(/^TMCodeFence[A-Za-z]*Close$/);
232
+ expect(await editor.lineHTML(0)).toMatch(classTagRegExp('javascript', 'TMInfoString'));
233
+ editor.destroy();
234
+ }
235
+ });
236
+
237
+ test('Opening and closing fence must match: ```\\ncode\\n~~~\\nstill\\n```', async () => {
238
+ const testCases = [
239
+ '```\ncode\n~~~\ncode\n```',
240
+ '~~~\ncode\n```\ncode\n~~~',
241
+ ];
242
+ for (let testCase of testCases) {
243
+ const editor = await initTinyMDE(testCase);
244
+ expect(await editor.lineType(0)).toMatch(/^TMCodeFence[A-Za-z]*Open$/);
245
+ for (let i = 1; i < editor.numLines() - 1; i++) {
246
+ expect(await editor.lineType(i)).toMatch(/^TMFencedCode[A-Za-z]*$/);
247
+ }
248
+ expect(await editor.lineType(await editor.numLines()-1)).toMatch(/^TMCodeFence[A-Za-z]*Close$/);
249
+ editor.destroy();
250
+ }
251
+ });
252
+
253
+ test('Info string for backtick fenced code can\'t contain backtick: ```js`\\nThis is not code\\n```', async () => {
254
+ const editor = await initTinyMDE('```javascript`\nThis is not code\n```');
255
+ expect(await editor.lineType(0)).toMatch('TMPara');
256
+ expect(await editor.lineType(1)).toMatch('TMPara');
257
+ editor.destroy();
258
+ });
259
+
260
+
261
+ test('Closing code fence can\'t contain info string: ```\\ncode\\n```not closing\\ncode\\n```', async () => {
262
+ const testCases = [
263
+ '~~~\nFenced\n~~~still\nhere\n~~~',
264
+ '```\nFenced\n```still\nhere\n```',
265
+ ];
266
+ for (let testCase of testCases) {
267
+ const editor = await initTinyMDE(testCase);
268
+ expect(await editor.lineType(0)).toMatch(/^TMCodeFence[A-Za-z]*Open$/);
269
+ for (let i = 1; i < editor.numLines() - 1; i++) {
270
+ expect(await editor.lineType(i)).toMatch(/^TMFencedCode[A-Za-z]*$/);
271
+ }
272
+ expect(await editor.lineType(await editor.numLines()-1)).toMatch(/^TMCodeFence[A-Za-z]*Close$/);
273
+ editor.destroy();
274
+ }
275
+ });
276
+
277
+ test('Closing code fence has to be same length or longer than opening: `````\\ncode\\n```\\nstill code\\n`````', async () => {
278
+ const testCases = [
279
+ '~~~~~\nFenced\n~~~\nhere\n~~~~~',
280
+ '`````\nFenced\n```\nhere\n`````',
281
+ ];
282
+ for (let testCase of testCases) {
283
+ const editor = await initTinyMDE(testCase);
284
+ expect(await editor.lineType(0)).toMatch(/^TMCodeFence[A-Za-z]*Open$/);
285
+ for (let i = 1; i < editor.numLines() - 1; i++) {
286
+ expect(await editor.lineType(i)).toMatch(/^TMFencedCode[A-Za-z]*$/);
287
+ }
288
+ expect(await editor.lineType(await editor.numLines()-1)).toMatch(/^TMCodeFence[A-Za-z]*Close$/);
289
+ editor.destroy();
290
+ }
291
+ });
292
+
293
+ test('Empty fenced code includes a <br>: ~~~\\n\\n~~~', async () => {
294
+ const testCases = [
295
+ '~~~\n\n~~~',
296
+ '```\n\n```',
297
+ ];
298
+ for (let testCase of testCases) {
299
+ const editor = await initTinyMDE(testCase);
300
+ expect(await editor.lineHTML(1)).toMatch(/<br[^>]*>/);
301
+ editor.destroy();
302
+ }
303
+ });
304
+
305
+
306
+ // // Link reference definition -----------------------------------------------------------------------------------
307
+
308
+ test('Link reference definition cannot interrupt a paragraph: Paragraph\\n[ref]: (Not a ref definition)', async () => {
309
+ // Paragraphs contained in: TMPara, TMUL, TMOL, TMBlockquote
310
+ const testCases = [
311
+ 'Para\n[ref]: https://abc.de',
312
+ '- UL\n[ref]: https://abc.de',
313
+ '1. OL\n[ref]: https://abc.de',
314
+ '> Blockquote\n[ref]: https://abc.de'
315
+ ];
316
+ for (let testCase of testCases) {
317
+ const editor = await initTinyMDE(testCase);
318
+ expect(await editor.lineType(1)).not.toMatch('TMLinkReferenceDefinition');
319
+ editor.destroy();
320
+ }
321
+ });
322
+
323
+ test('Link reference definition can follow after non-paragraph: # Heading\\n[ref]: (valid ref definition)', async () => {
324
+ const testCases = [
325
+ '# Heading\n[ref]: https://abc.de',
326
+ 'Setext heading\n====\n[ref]: https://abc.de',
327
+ '<pre>HTML block</pre>\n[ref]: https://abc.de',
328
+ '---\n[ref]: https://abc.de',
329
+ '~~~\nFenced code\n~~~\n[ref]: https://abc.de',
330
+ ' Link reference definition\n[ref]: https://abc.de',
331
+ ];
332
+ for (let testCase of testCases) {
333
+ const editor = await initTinyMDE(testCase);
334
+ expect(await editor.lineType(await editor.numLines() - 1)).toMatch('TMLinkReferenceDefinition');
335
+ editor.destroy();
336
+ }
337
+ });
338
+
339
+ test('Empty link reference definitions work', async () => {
340
+ const testCases = [
341
+ '[ref]:',
342
+ '[ref]:""',
343
+ '[ref]:\'\'',
344
+ '[ref]:()',
345
+ '[ref]:<>',
346
+ '[ref]:<>""',
347
+ '[ref]:<>\'\'',
348
+ '[ref]:<>()',
349
+ ];
350
+ for (let testCase of testCases) {
351
+ const editor = await initTinyMDE(testCase);
352
+ expect(await editor.lineType(0)).toMatch('TMLinkReferenceDefinition');
353
+ editor.destroy();
354
+ }
355
+ });
356
+
357
+ test('Escaping works in link destination and title of reference definition', async () => {
358
+ const testCases = [
359
+ ['[ref]: <\\>>', '<\\>>', ''],
360
+ ['[ref]: XXXA "\\""', 'XXXA', '"\\""'],
361
+ ['[ref]: XXXA \'\\\'\'', 'XXXA', `'\\''`],
362
+ ['[ref]: XXXA (\\))', 'XXXA', '(\\))'],
363
+ ];
364
+ for (let testCase of testCases) {
365
+ const editor = await initTinyMDE(testCase[0]);
366
+ expect(await editor.lineType(0)).toMatch('TMLinkReferenceDefinition');
367
+ expect(await editor.lineHTML(0)).toMatch(classTagRegExp(testCase[1], 'TMLinkDestination'));
368
+ expect(await editor.lineHTML(0)).toMatch(classTagRegExp(testCase[2], 'TMLinkTitle'));
369
+ editor.destroy();
370
+ }
371
+ });
372
+
373
+ test('Invalid link reference definitions not recognized as such', async () => {
374
+ const testCases = [
375
+ '[ref]: A B', // Link destinations with whitespace need to be enclosed in <>
376
+ '[ref]: <A B \\>', // Closing angle bracket is escaped
377
+ '[ref] : XXXA', // No whitespace allowed after link label
378
+ '[ref]: XXXA (XXXA\\)', // Closing parenthesis is escaped
379
+ '[ref]: XXXA "XXXA\\"', // Closing quote is escaped
380
+ '[ref]: XXXA \'XXXA\\\'', // Closing quote is escaped
381
+ '[ref]: XXXA>', // Destination may not include < or >
382
+ '[ref]: XXXA "XXXB\'', // Delimiters of title have to be matched
383
+ ];
384
+ for (let testCase of testCases) {
385
+ const editor = await initTinyMDE(testCase);
386
+ expect(await editor.lineType(0)).not.toMatch('TMLinkReferenceDefinition');
387
+ editor.destroy();
388
+ }
389
+ })
390
+
391
+ // HTML Block --------------------------------------------------------------------
392
+
393
+ test('HTML block: script, pre, style recognized (case 1): <script>', async () => {
394
+ const testCases = [
395
+ ' <script>\nXXXA\n\n\nXXXB </script>\nXXXC',
396
+ '<SCRIPT type="text/javascript">XXXA</SCRiPT> XXXD\nXXXC',
397
+ ' <pre>\nXXXA\n\n\nXXXB </pre>\nXXXC',
398
+ '<PRE class="abc">XXXA</pRe> XXXB\nXXXC',
399
+ ' <style>\nXXXA\n\n\nXXXB </style>\nXXXC',
400
+ '<STYLE type="text/css">XXXA</STyLE>XXXB\nXXXC',
401
+ ];
402
+ for (let testCase of testCases) {
403
+ const editor = await initTinyMDE(testCase);
404
+ for (let l = 0; l < editor.numLines() - 1; l++) {
405
+ expect(await editor.lineType(l)).toMatch('TMHTMLBlock');
406
+ }
407
+ expect(await editor.lineType(await editor.numLines() - 1)).not.toMatch('TMHTMLBlock');
408
+ editor.destroy();
409
+ }
410
+ });
411
+
412
+ test('HTML block: comment (case 2): <!-- -->', async () => {
413
+ const testCases = [
414
+ '<!-- Comment -->\nXXXC',
415
+ ' <!-- \n Comment \n\n\n-->\nXXXC',
416
+ ' <!-- \n Comment \n--\n->\nXXXX-->YYYY\nXXXC',
417
+
418
+ ];
419
+ for (let testCase of testCases) {
420
+ const editor = await initTinyMDE(testCase);
421
+ for (let l = 0; l < editor.numLines() - 1; l++) {
422
+ expect(await editor.lineType(l)).toMatch('TMHTMLBlock');
423
+ }
424
+ expect(await editor.lineType(await editor.numLines() - 1)).not.toMatch('TMHTMLBlock');
425
+ editor.destroy();
426
+ }
427
+ });
428
+
429
+ test('HTML block: processing instruction (case 3): <!-- -->', async () => {
430
+ const testCases = [
431
+ '<? Processing instruction ?>\nXXXC',
432
+ ' <? \n Processing instruction \n\n\n?>\nXXXC',
433
+ ' <? \n Processing instruction \n?\n>\nXXXX?>YYYY\nXXXC',
434
+ ];
435
+ for (let testCase of testCases) {
436
+ const editor = await initTinyMDE(testCase);
437
+ for (let l = 0; l < editor.numLines() - 1; l++) {
438
+ expect(await editor.lineType(l)).toMatch('TMHTMLBlock');
439
+ }
440
+ expect(await editor.lineType(await editor.numLines() - 1)).not.toMatch('TMHTMLBlock');
441
+ editor.destroy();
442
+ }
443
+ });
444
+
445
+ test('HTML block: document type (case 4): <!DOCTYPE html>', async () => {
446
+ const testCases = [
447
+ '<!DOCTYPE html>\nXXXC',
448
+ ' <!X \n Document type \n\n\n>\nXXXC',
449
+ ' <!X \n Document type \n\n\nXXXX>YYYY\nXXXC',
450
+
451
+ ];
452
+ for (let testCase of testCases) {
453
+ const editor = await initTinyMDE(testCase);
454
+ for (let l = 0; l < editor.numLines() - 1; l++) {
455
+ expect(await editor.lineType(l)).toMatch('TMHTMLBlock');
456
+ }
457
+ expect(await editor.lineType(await editor.numLines() - 1)).not.toMatch('TMHTMLBlock');
458
+ editor.destroy();
459
+ }
460
+ });
461
+
462
+ test('HTML block: CDATA (case 5): <![CDATA[ ]]>', async () => {
463
+ const testCases = [
464
+ '<![CDATA[ XXXA ]]>\nXXXC',
465
+ ' <![CDATA[\nXXXA\n\n\nXXXB]]>XXXC\nXXXD',
466
+ ];
467
+ for (let testCase of testCases) {
468
+ const editor = await initTinyMDE(testCase);
469
+ for (let l = 0; l < editor.numLines() - 1; l++) {
470
+ expect(await editor.lineType(l)).toMatch('TMHTMLBlock');
471
+ }
472
+ expect(await editor.lineType(await editor.numLines() - 1)).not.toMatch('TMHTMLBlock');
473
+ editor.destroy();
474
+ }
475
+ });
476
+
477
+ test('HTML block: Specific HTML tag (case 6): <p> </p>', async () => {
478
+ const testCases = [
479
+ '<p class="abc">\nXXXA\nXXXB\n',
480
+ ' </HTML>XXXZ\nXXXA\nXXXB\n',
481
+ '<br/>\n',
482
+ '<mAiN\nXXXA\nXXXB\n'
483
+ ];
484
+ for (let testCase of testCases) {
485
+ const editor = await initTinyMDE(testCase);
486
+ for (let l = 0; l < editor.numLines() - 1; l++) {
487
+ expect(await editor.lineType(l)).toMatch('TMHTMLBlock');
488
+ }
489
+ expect(await editor.lineType(await editor.numLines() - 1)).not.toMatch('TMHTMLBlock');
490
+ editor.destroy();
491
+ }
492
+ });
493
+
494
+ test('HTML block: Generic tag (case 7): <ab-33>', async () => {
495
+ const testCases = [
496
+ '<ab-33 class="abc">\nXXXA\nXXXB\n',
497
+ ' </W00T >\nXXXZ\nXXXA\nXXXB\n',
498
+ ];
499
+ for (let testCase of testCases) {
500
+ const editor = await initTinyMDE(testCase);
501
+ for (let l = 0; l < editor.numLines() - 1; l++) {
502
+ expect(await editor.lineType(l)).toMatch('TMHTMLBlock');
503
+ }
504
+ expect(await editor.lineType(await editor.numLines() - 1)).not.toMatch('TMHTMLBlock');
505
+ editor.destroy();
506
+ }
507
+ });
508
+
509
+ test('HTML block: Cases 1-6 can interrupt a paragraph', async () => {
510
+ const testCases = [
511
+ 'XXXA\n<script>',
512
+ 'XXXA\n<!--',
513
+ 'XXXA\n<?',
514
+ 'XXXA\n<!DOCTYPE',
515
+ 'XXXA\n<![CDATA[',
516
+ 'XXXA\n<p>'
517
+ ];
518
+ for (let testCase of testCases) {
519
+ const editor = await initTinyMDE(testCase);
520
+ expect(await editor.lineType(1)).toMatch('TMHTMLBlock');
521
+ editor.destroy();
522
+ }
523
+ });
524
+
525
+ test('HTML block: Case 7 can\'t interrupt a paragraph', async () => {
526
+ const testCases = [
527
+ 'XXXA\n<bla>',
528
+ '> XXXA\n<bla>',
529
+ '- XXXA\n<bla>',
530
+ '1. XXXA\n<bla>',
531
+ ];
532
+ for (let testCase of testCases) {
533
+ const editor = await initTinyMDE(testCase);
534
+ expect(await editor.lineType(1)).not.toMatch('TMHTMLBlock');
535
+ editor.destroy();
536
+ }
537
+ });
538
+
539
+ test('HTML block: Invalid cases not recognized', async () => {
540
+ const testCases = [
541
+ '<bla', // Incomplete open tag
542
+ '<bla><p>', // Open tag not alone on its line
543
+ ' <p>', // Too much indentation
544
+ '</bla a="b">', // Invalid closing tag
545
+ ];
546
+ for (let testCase of testCases) {
547
+ const editor = await initTinyMDE(testCase);
548
+ expect(await editor.lineType(0)).not.toMatch('TMHTMLBlock');
549
+ editor.destroy();
550
+ }
551
+ });
552
+
553
+ // // List items -----------------------------------------------
554
+ test('Simple unordered list items recognized: - item', async () => {
555
+ const testCases = [
556
+ '- XXXA',
557
+ ' - XXXA',
558
+ '* XXXA',
559
+ ' * XXXA',
560
+ '+ XXXA',
561
+ ' + XXXA',
562
+ ];
563
+ for (let testCase of testCases) {
564
+ const editor = await initTinyMDE(testCase);
565
+ expect(await editor.lineType(0)).toMatch('TMUL');
566
+ editor.destroy();
567
+ }
568
+ });
569
+
570
+
571
+
572
+ test('Empty unordered list items recognized: -', async () => {
573
+ const testCases = [
574
+ '- ',
575
+ ' - ',
576
+ '* ',
577
+ ' * ',
578
+ '+ ',
579
+ ' + ',
580
+ ];
581
+ for (let testCase of testCases) {
582
+ const editor = await initTinyMDE(testCase);
583
+ expect(await editor.lineType(0)).toMatch('TMUL');
584
+ editor.destroy();
585
+ }
586
+ });
587
+
588
+ test('Simple ordered list items recognized: 1. item', async () => {
589
+ const testCases = [
590
+ '1. XXXA',
591
+ ' 123456789. XXXA',
592
+ '0. XXXA',
593
+ '1) XXXA',
594
+ ' 123456789) XXXA',
595
+ '0) XXXA',
596
+ ];
597
+ for (let testCase of testCases) {
598
+ const editor = await initTinyMDE(testCase);
599
+ expect(await editor.lineType(0)).toMatch('TMOL');
600
+ editor.destroy();
601
+ }
602
+ });
603
+
604
+ test('Empty ordered list items recognized: 1.', async () => {
605
+ const testCases = [
606
+ '1. ',
607
+ ' 123456789. ',
608
+ '0. ',
609
+ '1) ',
610
+ ' 123456789) ',
611
+ '0) ',
612
+ ];
613
+ for (let testCase of testCases) {
614
+ const editor = await initTinyMDE(testCase);
615
+ expect(await editor.lineType(0)).toMatch('TMOL');
616
+ editor.destroy();
617
+ }
618
+ });
619
+
620
+ test('Line item content parsed as Markdown: - *em*', async () => {
621
+ const testCases = [
622
+ '- *XXXA*',
623
+ '* *XXXA*',
624
+ '+ *XXXA*',
625
+ '1) *XXXA*',
626
+ '1. *XXXA*',
627
+ ];
628
+ for (let testCase of testCases) {
629
+ const editor = await initTinyMDE(testCase);
630
+ expect(await editor.lineType(0)).toMatch(/TM[OU]L/);
631
+ expect(await editor.lineHTML(0)).toMatch(classTagRegExp('XXXA', 'TMEm', 'em'));
632
+ editor.destroy();
633
+ }
634
+ });
635
+
636
+ // TODO Make this test pass
637
+ // test('Sublists recognized: - ', async () => {
638
+ // const testCases = [
639
+ // '- A\n- B\n - C\n - D\n - E',
640
+ // '1. A\n2. B\n 1) C\n 1. d'
641
+ // ];
642
+ // for (let testCase of testCases) {
643
+ // const editor = await initTinyMDE(testCase);
644
+ // for (let l = 0; l < editor.numLines(); l++) {
645
+ // expect(await editor.lineType(l)).toMatch(/TM[OU]L/);
646
+ // }
647
+ // }
648
+ // });
649
+
650
+ // TODO Make this test pass
651
+ // test('Indented lines following list item continue that list item: 1. Text\\n continued', async () => {
652
+ // const cases = [
653
+ // '1. List item\n Continued',
654
+ // '- List item\n Continued',
655
+ // ' 1. List item\n Continued',
656
+ // ' - List item\n Continued',
657
+ // // '- List item\n Continued\n\n Still continued\n Indented code in list item\n > Blockquote in list item'
658
+ // ];
659
+ // for (let testCase of cases) {
660
+ // let editor = await initTinyMDE(testCase);
661
+ // expect(await editor.lineType(0)).toMatch(/^TM[OU]L$/);
662
+ // for (let l = 1; l <= editor.numLines(); l++) {
663
+ // expect(await editor.lineType(l)).toMatch(/^TM[OU]L/);
664
+ // }
665
+ // }
666
+ // });
667
+
668
+ // Blockquote -----------------------------------------------------------------------
669
+
670
+ test("Basic blockquote works: > Quote", async () => {
671
+ const editor = await initTinyMDE('> Quote');
672
+ expect(await editor.lineType(0)).toMatch('TMBlockquote');
673
+ editor.destroy();
674
+ })
675
+
676
+ test('Space after > can be omitted: >Quote', async () => {
677
+ const editor = await initTinyMDE('>Quote');
678
+ expect(await editor.lineType(0)).toMatch('TMBlockquote');
679
+ editor.destroy();
680
+ })
681
+
682
+ test('Blockquote content parsed as markdown: > *em*', async () => {
683
+ const editor = await initTinyMDE('> *XXXA*');
684
+ expect(await editor.lineType(0)).toMatch('TMBlockquote');
685
+ expect(await editor.lineHTML(0)).toMatch(classTagRegExp('XXXA', 'TMEm', 'em'));
686
+ editor.destroy();
687
+ });
688
+
689
+ // Miscellaneous
690
+
691
+ test("Content with dollar signs is parsed correctly", async() => {
692
+ const editor = await initTinyMDE('$1');
693
+ expect(await editor.lineHTML(0)).toMatch(/\$1/);
694
+ editor.destroy();
695
+ });
696
+