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