xref: /dokuwiki/_test/tests/Parsing/ParserMode/FootnoteTest.php (revision 884caed926ca0aa0af6ce3f34ae3aa7317a3361a)
1<?php
2
3namespace dokuwiki\test\Parsing\ParserMode;
4
5use dokuwiki\Parsing\Handler\Lists;
6use dokuwiki\Parsing\ParserMode\Code;
7use dokuwiki\Parsing\ParserMode\Eol;
8use dokuwiki\Parsing\ParserMode\Footnote;
9use dokuwiki\Parsing\ParserMode\Strong;
10use dokuwiki\Parsing\ParserMode\GfmHr;
11use dokuwiki\Parsing\ParserMode\Listblock;
12use dokuwiki\Parsing\ParserMode\GfmQuote;
13use dokuwiki\Parsing\ParserMode\Preformatted;
14use dokuwiki\Parsing\ParserMode\Table;
15use dokuwiki\Parsing\ParserMode\Unformatted;
16
17class FootnoteTest extends ParserTestBase
18{
19
20    function setUp() : void {
21        parent::setUp();
22        $this->P->addMode('footnote',new Footnote());
23    }
24
25    function testFootnote() {
26        $this->P->parse('Foo (( testing )) Bar');
27        $calls = [
28            ['document_start',[]],
29            ['p_open',[]],
30            ['cdata',["\n".'Foo ']],
31            ['nest', [ [
32              ['footnote_open',[]],
33              ['cdata',[' testing ']],
34              ['footnote_close',[]],
35            ]]],
36            ['cdata',[' Bar']],
37            ['p_close',[]],
38            ['document_end',[]],
39        ];
40        $this->assertCalls($calls, $this->H->calls);
41    }
42
43    function testNotAFootnote() {
44        $this->P->parse("Foo (( testing\n Bar");
45        $calls = [
46            ['document_start',[]],
47            ['p_open',[]],
48            ['cdata',["\nFoo (( testing\n Bar"]],
49            ['p_close',[]],
50            ['document_end',[]],
51        ];
52        $this->assertCalls($calls, $this->H->calls);
53    }
54
55    function testFootnoteLinefeed() {
56        $this->P->addMode('eol',new Eol());
57        $this->P->parse("Foo (( testing\ntesting )) Bar");
58        $calls = [
59            ['document_start',[]],
60            ['p_open',[]],
61            ['cdata',['Foo ']],
62            ['nest', [ [
63              ['footnote_open',[]],
64              ['cdata',[" testing\ntesting "]],
65              ['footnote_close',[]],
66            ]]],
67            ['cdata',[' Bar']],
68            ['p_close',[]],
69            ['document_end',[]],
70        ];
71        $this->assertCalls($calls, $this->H->calls);
72    }
73
74    function testFootnoteNested() {
75        $this->P->parse('Foo (( x((y))z )) Bar');
76        $calls = [
77            ['document_start',[]],
78            ['p_open',[]],
79            ['cdata',["\n".'Foo ']],
80            ['nest', [ [
81              ['footnote_open',[]],
82              ['cdata',[' x((y']],
83              ['footnote_close',[]],
84            ]]],
85            ['cdata',['z )) Bar']],
86            ['p_close',[]],
87            ['document_end',[]],
88        ];
89        $this->assertCalls($calls, $this->H->calls);
90    }
91
92    function testFootnoteEol() {
93        $this->P->addMode('eol',new Eol());
94        $this->P->parse("Foo \nX(( test\ning ))Y\n Bar");
95        $calls = [
96            ['document_start',[]],
97            ['p_open',[]],
98            ['cdata',['Foo'."\n".'X']],
99            ['nest', [ [
100              ['footnote_open',[]],
101              ['cdata',[" test\ning "]],
102              ['footnote_close',[]],
103            ]]],
104            ['cdata',['Y'."\n".'Bar']],
105            ['p_close',[]],
106            ['document_end',[]],
107        ];
108        $this->assertCalls($calls, $this->H->calls);
109    }
110
111    function testFootnoteStrong() {
112        $this->P->addMode('strong',new Strong());
113        $this->P->parse('Foo (( **testing** )) Bar');
114        $calls = [
115            ['document_start',[]],
116            ['p_open',[]],
117            ['cdata',["\n".'Foo ']],
118            ['nest', [ [
119              ['footnote_open',[]],
120              ['cdata',[' ']],
121              ['strong_open',[]],
122              ['cdata',['testing']],
123              ['strong_close',[]],
124              ['cdata',[' ']],
125              ['footnote_close',[]],
126            ]]],
127            ['cdata',[' Bar']],
128            ['p_close',[]],
129            ['document_end',[]],
130        ];
131        $this->assertCalls($calls, $this->H->calls);
132    }
133
134    function testFootnoteHr() {
135        $this->P->addMode('gfm_hr',new GfmHr());
136        $this->P->parse("Foo ((\n----\n)) Bar");
137        $calls = [
138            ['document_start',[]],
139            ['p_open',[]],
140            ['cdata',["\n".'Foo ']],
141            ['nest', [ [
142              ['footnote_open',[]],
143              ['hr',[]],
144              ['cdata',["\n"]],
145              ['footnote_close',[]],
146            ]]],
147            ['cdata',[' Bar']],
148            ['p_close',[]],
149            ['document_end',[]],
150        ];
151        $this->assertCalls($calls, $this->H->calls);
152    }
153
154    function testFootnoteCode() {
155        $this->P->addMode('code',new Code());
156        $this->P->parse("Foo (( <code>Test</code> )) Bar");
157        $calls = [
158            ['document_start',[]],
159            ['p_open',[]],
160            ['cdata',["\n".'Foo ']],
161            ['nest', [ [
162              ['footnote_open',[]],
163              ['cdata',[' ']],
164              ['code',['Test',null,null]],
165              ['cdata',[' ']],
166              ['footnote_close',[]],
167            ]]],
168            ['cdata',[' Bar']],
169            ['p_close',[]],
170            ['document_end',[]],
171        ];
172        $this->assertCalls($calls, $this->H->calls);
173    }
174
175    function testFootnotePreformatted() {
176        $this->P->addMode('preformatted',new Preformatted());
177        $this->P->parse("Foo (( \n  Test\n )) Bar");
178        $calls = [
179            ['document_start',[]],
180            ['p_open',[]],
181            ['cdata',["\n".'Foo ']],
182            ['nest', [ [
183              ['footnote_open',[]],
184              ['cdata',[' ']],
185              ['preformatted',['Test']],
186              ['cdata',[' ']],
187              ['footnote_close',[]],
188            ]]],
189            ['cdata',[' Bar']],
190            ['p_close',[]],
191            ['document_end',[]],
192        ];
193        $this->assertCalls($calls, $this->H->calls);
194    }
195
196    function testFootnotePreformattedEol() {
197        $this->P->addMode('preformatted',new Preformatted());
198        $this->P->addMode('eol',new Eol());
199        $this->P->parse("Foo (( \n  Test\n )) Bar");
200        $calls = [
201            ['document_start',[]],
202            ['p_open',[]],
203            ['cdata',['Foo ']],
204            ['nest', [ [
205              ['footnote_open',[]],
206              ['cdata',[' ']],
207              ['preformatted',['Test']],
208              ['cdata',[' ']],
209              ['footnote_close',[]],
210            ]]],
211            ['cdata',[' Bar']],
212            ['p_close',[]],
213            ['document_end',[]],
214        ];
215
216        $this->assertCalls($calls, $this->H->calls);
217    }
218
219    function testFootnoteUnformatted() {
220        $this->P->addMode('unformatted',new Unformatted());
221        $this->P->parse("Foo (( <nowiki>Test</nowiki> )) Bar");
222        $calls = [
223            ['document_start',[]],
224            ['p_open',[]],
225            ['cdata',["\n".'Foo ']],
226            ['nest', [ [
227              ['footnote_open',[]],
228              ['cdata',[' ']],
229              ['unformatted',['Test']],
230              ['cdata',[' ']],
231              ['footnote_close',[]],
232            ]]],
233            ['cdata',[' Bar']],
234            ['p_close',[]],
235            ['document_end',[]],
236        ];
237        $this->assertCalls($calls, $this->H->calls);
238    }
239
240    function testFootnoteNotHeader() {
241        $this->P->addMode('unformatted',new Unformatted());
242        $this->P->parse("Foo (( \n====Test====\n )) Bar");
243        $calls = [
244            ['document_start',[]],
245            ['p_open',[]],
246            ['cdata',["\n".'Foo ']],
247            ['nest', [ [
248              ['footnote_open',[]],
249              ['cdata',[" \n====Test====\n "]],
250              ['footnote_close',[]],
251            ]]],
252            ['cdata',[' Bar']],
253            ['p_close',[]],
254            ['document_end',[]],
255        ];
256        $this->assertCalls($calls, $this->H->calls);
257    }
258
259    function testFootnoteTable() {
260        $this->P->addMode('table',new Table());
261        $this->P->parse("Foo ((
262| Row 0 Col 1    | Row 0 Col 2     | Row 0 Col 3        |
263| Row 1 Col 1    | Row 1 Col 2     | Row 1 Col 3        |
264 )) Bar");
265        $calls = [
266            ['document_start',[]],
267            ['p_open',[]],
268            ['cdata',["\n".'Foo ']],
269            ['nest', [ [
270              ['footnote_open',[]],
271              ['table_open',[3, 2, 8]],
272              ['tablerow_open',[]],
273              ['tablecell_open',[1,'left',1]],
274              ['cdata',[' Row 0 Col 1    ']],
275              ['tablecell_close',[]],
276              ['tablecell_open',[1,'left',1]],
277              ['cdata',[' Row 0 Col 2     ']],
278              ['tablecell_close',[]],
279              ['tablecell_open',[1,'left',1]],
280              ['cdata',[' Row 0 Col 3        ']],
281              ['tablecell_close',[]],
282              ['tablerow_close',[]],
283              ['tablerow_open',[]],
284              ['tablecell_open',[1,'left',1]],
285              ['cdata',[' Row 1 Col 1    ']],
286              ['tablecell_close',[]],
287              ['tablecell_open',[1,'left',1]],
288              ['cdata',[' Row 1 Col 2     ']],
289              ['tablecell_close',[]],
290              ['tablecell_open',[1,'left',1]],
291              ['cdata',[' Row 1 Col 3        ']],
292              ['tablecell_close',[]],
293              ['tablerow_close',[]],
294              ['table_close',[123]],
295              ['cdata',[' ']],
296              ['footnote_close',[]],
297            ]]],
298            ['cdata',[' Bar']],
299            ['p_close',[]],
300            ['document_end',[]],
301        ];
302        $this->assertCalls($calls, $this->H->calls);
303    }
304
305    function testFootnoteList() {
306        $this->P->addMode('listblock',new ListBlock());
307        $this->P->parse("Foo ((
308  *A
309    * B
310  * C
311 )) Bar");
312        $calls = [
313            ['document_start',[]],
314            ['p_open',[]],
315            ['cdata',["\n".'Foo ']],
316            ['nest', [ [
317              ['footnote_open',[]],
318              ['listu_open',[]],
319              ['listitem_open',[1,Lists::NODE]],
320              ['listcontent_open',[]],
321              ['cdata',["A"]],
322              ['listcontent_close',[]],
323              ['listu_open',[]],
324              ['listitem_open',[2]],
325              ['listcontent_open',[]],
326              ['cdata',[' B']],
327              ['listcontent_close',[]],
328              ['listitem_close',[]],
329              ['listu_close',[]],
330              ['listitem_close',[]],
331              ['listitem_open',[1]],
332              ['listcontent_open',[]],
333              ['cdata',[' C']],
334              ['listcontent_close',[]],
335              ['listitem_close',[]],
336              ['listu_close',[]],
337              ['cdata',[' ']],
338              ['footnote_close',[]],
339            ]]],
340            ['cdata',[' Bar']],
341            ['p_close',[]],
342            ['document_end',[]],
343        ];
344        $this->assertCalls($calls, $this->H->calls);
345    }
346
347    function testFootnoteQuote() {
348        // GfmQuote is the unified quote mode (replaces DW Quote). Under
349        // the test's default DW-preferred syntax the post-pass flattens
350        // sub-parsed paragraph wrapping into linebreak-separated cdata,
351        // and nested `>>` produces a nested `quote_open` pair. The body
352        // sub-parsed call list is wrapped in a `nest` instruction.
353        $this->P->addMode('gfm_quote', new GfmQuote());
354        $this->P->parse("Foo ((
355> def
356>>ghi
357 )) Bar");
358        $calls = [
359            ['document_start',[]],
360            ['p_open',[]],
361            ['cdata',["\n".'Foo ']],
362            ['nest', [ [
363              ['footnote_open',[]],
364              ['quote_open',[]],
365              ['nest', [ [ ['cdata', ['def']] ] ]],
366              ['quote_open',[]],
367              ['nest', [ [ ['cdata', ['ghi']] ] ]],
368              ['quote_close',[]],
369              ['quote_close',[]],
370              ['cdata',["\n "]],
371              ['footnote_close',[]],
372            ]]],
373            ['cdata',[' Bar']],
374            ['p_close',[]],
375            ['document_end',[]],
376        ];
377
378        $this->assertCalls($calls, $this->H->calls);
379    }
380
381    /**
382     * Footnotes are block-level containers (they can hold tables, lists,
383     * blockquotes, etc.), so unlike inline formatting they are allowed to
384     * span paragraph breaks. Pins this behavior down so future refactors
385     * of the inline-formatting paragraph guard don't accidentally restrict
386     * footnotes.
387     */
388    function testFootnoteSpansParagraphBoundary() {
389        $this->P->addMode('eol', new Eol());
390        $this->P->parse("Foo (( para one\n\npara two )) Bar");
391        $calls = [
392            ['document_start', []],
393            ['p_open', []],
394            ['cdata', ['Foo ']],
395            ['nest', [[
396                ['footnote_open', []],
397                ['cdata', [" para one\n\npara two "]],
398                ['footnote_close', []],
399            ]]],
400            ['cdata', [' Bar']],
401            ['p_close', []],
402            ['document_end', []],
403        ];
404        $this->assertCalls($calls, $this->H->calls);
405    }
406
407    function testFootnoteNesting() {
408        // Strong no longer opens where its inner `**` is adjacent to spaces
409        // (flanking rule). So `** (( b ` inside the footnote stays literal,
410        // and the footnote closes at the first `))`. The trailing `** c ))`
411        // outside has `**` adjacent to space too — also literal.
412        $this->P->addMode('strong',new Strong());
413        $this->P->parse("(( a ** (( b )) ** c ))");
414
415        $calls = [
416            ['document_start',[]],
417            ['p_open',[]],
418            ['cdata',["\n"]],
419            ['nest', [ [
420              ['footnote_open',[]],
421              ['cdata',[' a ** (( b ']],
422              ['footnote_close',[]],
423            ]]],
424            ['cdata',[" ** c ))"]],
425            ['p_close',[]],
426            ['document_end',[]],
427        ];
428
429        $this->assertCalls($calls, $this->H->calls);
430    }
431}
432