xref: /dokuwiki/_test/tests/Parsing/ParserMode/GfmLinkTest.php (revision e89aeebd5989e476b6a69236d9aabf72a9a01f14)
1<?php
2
3namespace dokuwiki\test\Parsing\ParserMode;
4
5use dokuwiki\Parsing\ModeRegistry;
6use dokuwiki\Parsing\ParserMode\GfmLink;
7use dokuwiki\Parsing\ParserMode\Internallink;
8
9/**
10 * Tests for GFM inline links `[text](url)` dispatching to DokuWiki's
11 * internal / external / interwiki / email / windowsshare / local link
12 * handler instructions.
13 */
14class GfmLinkTest extends ParserTestBase
15{
16    public function setUp(): void
17    {
18        parent::setUp();
19        global $conf;
20        $conf['syntax'] = 'markdown';
21        ModeRegistry::reset();
22    }
23
24    public function tearDown(): void
25    {
26        ModeRegistry::reset();
27        parent::tearDown();
28    }
29
30    function testInternalPage()
31    {
32        $this->P->addMode('gfm_link', new GfmLink());
33        $this->P->parse('Foo [text](page) Bar');
34        $calls = [
35            ['document_start', []],
36            ['p_open', []],
37            ['cdata', ["\nFoo "]],
38            ['internallink', ['page', 'text']],
39            ['cdata', [' Bar']],
40            ['p_close', []],
41            ['document_end', []],
42        ];
43        $this->assertCalls($calls, $this->H->calls);
44    }
45
46    function testInternalPageWithNamespace()
47    {
48        $this->P->addMode('gfm_link', new GfmLink());
49        $this->P->parse('Foo [Syntax](wiki:syntax#internal) Bar');
50        $calls = [
51            ['document_start', []],
52            ['p_open', []],
53            ['cdata', ["\nFoo "]],
54            ['internallink', ['wiki:syntax#internal', 'Syntax']],
55            ['cdata', [' Bar']],
56            ['p_close', []],
57            ['document_end', []],
58        ];
59        $this->assertCalls($calls, $this->H->calls);
60    }
61
62    function testExternalLink()
63    {
64        $this->P->addMode('gfm_link', new GfmLink());
65        $this->P->parse('Foo [Google](http://google.com) Bar');
66        $calls = [
67            ['document_start', []],
68            ['p_open', []],
69            ['cdata', ["\nFoo "]],
70            ['externallink', ['http://google.com', 'Google']],
71            ['cdata', [' Bar']],
72            ['p_close', []],
73            ['document_end', []],
74        ];
75        $this->assertCalls($calls, $this->H->calls);
76    }
77
78    function testInterwikiLink()
79    {
80        $this->P->addMode('gfm_link', new GfmLink());
81        $this->P->parse('Foo [callbacks](wp>Callback) Bar');
82        $calls = [
83            ['document_start', []],
84            ['p_open', []],
85            ['cdata', ["\nFoo "]],
86            ['interwikilink', ['wp>Callback', 'callbacks', 'wp', 'Callback']],
87            ['cdata', [' Bar']],
88            ['p_close', []],
89            ['document_end', []],
90        ];
91        $this->assertCalls($calls, $this->H->calls);
92    }
93
94    function testInterwikiLinkCaseNormalized()
95    {
96        $this->P->addMode('gfm_link', new GfmLink());
97        $this->P->parse('Foo [Page](IW>somepage) Bar');
98        $calls = [
99            ['document_start', []],
100            ['p_open', []],
101            ['cdata', ["\nFoo "]],
102            ['interwikilink', ['IW>somepage', 'Page', 'iw', 'somepage']],
103            ['cdata', [' Bar']],
104            ['p_close', []],
105            ['document_end', []],
106        ];
107        $this->assertCalls($calls, $this->H->calls);
108    }
109
110    function testEmailLink()
111    {
112        $this->P->addMode('gfm_link', new GfmLink());
113        $this->P->parse('Foo [mail](user@example.com) Bar');
114        $calls = [
115            ['document_start', []],
116            ['p_open', []],
117            ['cdata', ["\nFoo "]],
118            ['emaillink', ['user@example.com', 'mail']],
119            ['cdata', [' Bar']],
120            ['p_close', []],
121            ['document_end', []],
122        ];
123        $this->assertCalls($calls, $this->H->calls);
124    }
125
126    function testLocalAnchor()
127    {
128        $this->P->addMode('gfm_link', new GfmLink());
129        $this->P->parse('Foo [section](#anchor) Bar');
130        $calls = [
131            ['document_start', []],
132            ['p_open', []],
133            ['cdata', ["\nFoo "]],
134            ['locallink', ['anchor', 'section']],
135            ['cdata', [' Bar']],
136            ['p_close', []],
137            ['document_end', []],
138        ];
139        $this->assertCalls($calls, $this->H->calls);
140    }
141
142    function testWindowsShare()
143    {
144        $this->P->addMode('gfm_link', new GfmLink());
145        $this->P->parse('Foo [share](\\\\server\\share) Bar');
146        $calls = [
147            ['document_start', []],
148            ['p_open', []],
149            ['cdata', ["\nFoo "]],
150            ['windowssharelink', ['\\\\server\\share', 'share']],
151            ['cdata', [' Bar']],
152            ['p_close', []],
153            ['document_end', []],
154        ];
155        $this->assertCalls($calls, $this->H->calls);
156    }
157
158    function testTitleInDoubleQuotesIsDiscarded()
159    {
160        // GFM allows [text](url "title") but DokuWiki's link handler
161        // instructions have no title-attribute slot. The title parses
162        // cleanly but is dropped; the resulting handler call is identical
163        // to the no-title case.
164        $this->P->addMode('gfm_link', new GfmLink());
165        $this->P->parse('Foo [Google](http://google.com "Search engine") Bar');
166        $calls = [
167            ['document_start', []],
168            ['p_open', []],
169            ['cdata', ["\nFoo "]],
170            ['externallink', ['http://google.com', 'Google']],
171            ['cdata', [' Bar']],
172            ['p_close', []],
173            ['document_end', []],
174        ];
175        $this->assertCalls($calls, $this->H->calls);
176    }
177
178    function testTitleInSingleQuotesIsDiscarded()
179    {
180        $this->P->addMode('gfm_link', new GfmLink());
181        $this->P->parse("Foo [page](target 'a title') Bar");
182        $calls = [
183            ['document_start', []],
184            ['p_open', []],
185            ['cdata', ["\nFoo "]],
186            ['internallink', ['target', 'page']],
187            ['cdata', [' Bar']],
188            ['p_close', []],
189            ['document_end', []],
190        ];
191        $this->assertCalls($calls, $this->H->calls);
192    }
193
194    function testSpaceBetweenBracketsAndParensIsNotALink()
195    {
196        // GFM explicitly forbids whitespace between `]` and `(`.
197        $this->P->addMode('gfm_link', new GfmLink());
198        $this->P->parse('[foo] (bar)');
199        $modes = array_column($this->H->calls, 0);
200        $this->assertNotContains('internallink', $modes);
201        $this->assertNotContains('externallink', $modes);
202    }
203
204    function testDwDoubleBracketNotConsumedByGfmLink()
205    {
206        // With both gfm_link and DW internallink loaded (mixed syntax),
207        // `[[foo]]` must go to Internallink. GfmLink's `\[(?!\[)` guard
208        // refuses single-bracket matches that are actually part of `[[`.
209        $this->P->addMode('gfm_link', new GfmLink());
210        $this->P->addMode('internallink', new Internallink());
211        $this->P->parse('Foo [[bar]] Baz');
212        $calls = [
213            ['document_start', []],
214            ['p_open', []],
215            ['cdata', ["\nFoo "]],
216            ['internallink', ['bar', null]],
217            ['cdata', [' Baz']],
218            ['p_close', []],
219            ['document_end', []],
220        ];
221        $this->assertCalls($calls, $this->H->calls);
222    }
223
224    function testMultibyteLinkText()
225    {
226        $this->P->addMode('gfm_link', new GfmLink());
227        $this->P->parse('Foo [日本語](page) Bar');
228        $calls = [
229            ['document_start', []],
230            ['p_open', []],
231            ['cdata', ["\nFoo "]],
232            ['internallink', ['page', '日本語']],
233            ['cdata', [' Bar']],
234            ['p_close', []],
235            ['document_end', []],
236        ];
237        $this->assertCalls($calls, $this->H->calls);
238    }
239
240    function testReferenceStyleLinkNotMatched()
241    {
242        // `[foo][bar]` (reference-style) requires a reference definition
243        // we do not support; each `[...]` should stay literal text.
244        $this->P->addMode('gfm_link', new GfmLink());
245        $this->P->parse('[foo][bar]');
246        $modes = array_column($this->H->calls, 0);
247        $this->assertNotContains('internallink', $modes);
248        $this->assertNotContains('externallink', $modes);
249    }
250
251    function testTwoLinksInOneLine()
252    {
253        $this->P->addMode('gfm_link', new GfmLink());
254        $this->P->parse('Foo [one](a) and [two](b) Bar');
255        $calls = [
256            ['document_start', []],
257            ['p_open', []],
258            ['cdata', ["\nFoo "]],
259            ['internallink', ['a', 'one']],
260            ['cdata', [' and ']],
261            ['internallink', ['b', 'two']],
262            ['cdata', [' Bar']],
263            ['p_close', []],
264            ['document_end', []],
265        ];
266        $this->assertCalls($calls, $this->H->calls);
267    }
268
269    function testFragmentInExternalUrl()
270    {
271        $this->P->addMode('gfm_link', new GfmLink());
272        $this->P->parse('Foo [x](http://example.com#fragment) Bar');
273        $calls = [
274            ['document_start', []],
275            ['p_open', []],
276            ['cdata', ["\nFoo "]],
277            ['externallink', ['http://example.com#fragment', 'x']],
278            ['cdata', [' Bar']],
279            ['p_close', []],
280            ['document_end', []],
281        ];
282        $this->assertCalls($calls, $this->H->calls);
283    }
284
285    function testSortValue()
286    {
287        $this->assertSame(300, (new GfmLink())->getSort());
288    }
289}
290