xref: /dokuwiki/_test/tests/Parsing/ParserMode/GfmMediaTest.php (revision 95f694202286c1add4c442936a5caa38db0dd603)
1<?php
2
3namespace dokuwiki\test\Parsing\ParserMode;
4
5use dokuwiki\Parsing\ModeRegistry;
6use dokuwiki\Parsing\ParserMode\GfmLink;
7use dokuwiki\Parsing\ParserMode\GfmMedia;
8
9/**
10 * Tests for GFM inline image syntax `![alt](url)` dispatching to DokuWiki's
11 * internalmedia / externalmedia handler instructions.
12 */
13class GfmMediaTest extends ParserTestBase
14{
15    public function setUp(): void
16    {
17        parent::setUp();
18        global $conf;
19        $conf['syntax'] = 'md';
20        ModeRegistry::reset();
21    }
22
23    public function tearDown(): void
24    {
25        ModeRegistry::reset();
26        parent::tearDown();
27    }
28
29    function testInternalMedia()
30    {
31        $this->P->addMode('gfm_media', new GfmMedia());
32        $this->P->parse('Foo ![alt](wiki:image.png) Bar');
33        $calls = [
34            ['document_start', []],
35            ['p_open', []],
36            ['cdata', ["\nFoo "]],
37            ['internalmedia', ['wiki:image.png', 'alt', null, null, null, 'cache', 'details']],
38            ['cdata', [' Bar']],
39            ['p_close', []],
40            ['document_end', []],
41        ];
42        $this->assertCalls($calls, $this->H->calls);
43    }
44
45    function testExternalMediaHttps()
46    {
47        $this->P->addMode('gfm_media', new GfmMedia());
48        $this->P->parse('Foo ![logo](https://example.com/img.png) Bar');
49        $calls = [
50            ['document_start', []],
51            ['p_open', []],
52            ['cdata', ["\nFoo "]],
53            ['externalmedia', ['https://example.com/img.png', 'logo', null, null, null, 'cache', 'details']],
54            ['cdata', [' Bar']],
55            ['p_close', []],
56            ['document_end', []],
57        ];
58        $this->assertCalls($calls, $this->H->calls);
59    }
60
61    function testExternalMediaInterwiki()
62    {
63        $this->P->addMode('gfm_media', new GfmMedia());
64        $this->P->parse('Foo ![foo](wp>Example.png) Bar');
65        $calls = [
66            ['document_start', []],
67            ['p_open', []],
68            ['cdata', ["\nFoo "]],
69            ['externalmedia', ['wp>Example.png', 'foo', null, null, null, 'cache', 'details']],
70            ['cdata', [' Bar']],
71            ['p_close', []],
72            ['document_end', []],
73        ];
74        $this->assertCalls($calls, $this->H->calls);
75    }
76
77    function testEmptyAlt()
78    {
79        // GFM allows `![](/url)` with empty alt; we pass null in the caption
80        // slot to match how DW's Media mode emits no-caption media.
81        $this->P->addMode('gfm_media', new GfmMedia());
82        $this->P->parse('Foo ![](image.png) Bar');
83        $calls = [
84            ['document_start', []],
85            ['p_open', []],
86            ['cdata', ["\nFoo "]],
87            ['internalmedia', ['image.png', null, null, null, null, 'cache', 'details']],
88            ['cdata', [' Bar']],
89            ['p_close', []],
90            ['document_end', []],
91        ];
92        $this->assertCalls($calls, $this->H->calls);
93    }
94
95    function testTitleInDoubleQuotesIsDiscarded()
96    {
97        // GFM allows ![alt](url "title") but DokuWiki's media handler has
98        // no separate title slot (alt doubles as caption). The title parses
99        // cleanly but is dropped.
100        $this->P->addMode('gfm_media', new GfmMedia());
101        $this->P->parse('Foo ![alt](wiki:image.png "caption") Bar');
102        $calls = [
103            ['document_start', []],
104            ['p_open', []],
105            ['cdata', ["\nFoo "]],
106            ['internalmedia', ['wiki:image.png', 'alt', null, null, null, 'cache', 'details']],
107            ['cdata', [' Bar']],
108            ['p_close', []],
109            ['document_end', []],
110        ];
111        $this->assertCalls($calls, $this->H->calls);
112    }
113
114    function testTitleInSingleQuotesIsDiscarded()
115    {
116        $this->P->addMode('gfm_media', new GfmMedia());
117        $this->P->parse("Foo ![alt](wiki:image.png 'caption') Bar");
118        $calls = [
119            ['document_start', []],
120            ['p_open', []],
121            ['cdata', ["\nFoo "]],
122            ['internalmedia', ['wiki:image.png', 'alt', null, null, null, 'cache', 'details']],
123            ['cdata', [' Bar']],
124            ['p_close', []],
125            ['document_end', []],
126        ];
127        $this->assertCalls($calls, $this->H->calls);
128    }
129
130    function testMultibyteAlt()
131    {
132        $this->P->addMode('gfm_media', new GfmMedia());
133        $this->P->parse('Foo ![日本語](pic.png) Bar');
134        $calls = [
135            ['document_start', []],
136            ['p_open', []],
137            ['cdata', ["\nFoo "]],
138            ['internalmedia', ['pic.png', '日本語', null, null, null, 'cache', 'details']],
139            ['cdata', [' Bar']],
140            ['p_close', []],
141            ['document_end', []],
142        ];
143        $this->assertCalls($calls, $this->H->calls);
144    }
145
146    function testPlainLinkNotImage()
147    {
148        // With both gfm_media and gfm_link loaded, `[text](url)` (no leading
149        // `!`) must dispatch through gfm_link, not gfm_media.
150        $this->P->addMode('gfm_media', new GfmMedia());
151        $this->P->addMode('gfm_link', new GfmLink());
152        $this->P->parse('Foo [text](page) Bar');
153        $modes = array_column($this->H->calls, 0);
154        $this->assertContains('internallink', $modes);
155        $this->assertNotContains('internalmedia', $modes);
156    }
157
158    function testBangAloneIsNotImage()
159    {
160        $this->P->addMode('gfm_media', new GfmMedia());
161        $this->P->parse('Foo ![alt] Bar');
162        $modes = array_column($this->H->calls, 0);
163        $this->assertNotContains('internalmedia', $modes);
164        $this->assertNotContains('externalmedia', $modes);
165    }
166
167    function testSpaceBetweenBracketsAndParensIsNotAnImage()
168    {
169        $this->P->addMode('gfm_media', new GfmMedia());
170        $this->P->parse('![foo] (bar)');
171        $modes = array_column($this->H->calls, 0);
172        $this->assertNotContains('internalmedia', $modes);
173        $this->assertNotContains('externalmedia', $modes);
174    }
175
176    function testReferenceStyleImageNotMatched()
177    {
178        $this->P->addMode('gfm_media', new GfmMedia());
179        $this->P->parse('![foo][bar]');
180        $modes = array_column($this->H->calls, 0);
181        $this->assertNotContains('internalmedia', $modes);
182        $this->assertNotContains('externalmedia', $modes);
183    }
184
185    function testTwoImagesInOneLine()
186    {
187        $this->P->addMode('gfm_media', new GfmMedia());
188        $this->P->parse('Foo ![one](a.png) and ![two](b.png) Bar');
189        $calls = [
190            ['document_start', []],
191            ['p_open', []],
192            ['cdata', ["\nFoo "]],
193            ['internalmedia', ['a.png', 'one', null, null, null, 'cache', 'details']],
194            ['cdata', [' and ']],
195            ['internalmedia', ['b.png', 'two', null, null, null, 'cache', 'details']],
196            ['cdata', [' Bar']],
197            ['p_close', []],
198            ['document_end', []],
199        ];
200        $this->assertCalls($calls, $this->H->calls);
201    }
202
203    function testWidthOnlyParameter()
204    {
205        $this->P->addMode('gfm_media', new GfmMedia());
206        $this->P->parse('Foo ![alt](wiki:image.png?200) Bar');
207        $calls = [
208            ['document_start', []],
209            ['p_open', []],
210            ['cdata', ["\nFoo "]],
211            ['internalmedia', ['wiki:image.png', 'alt', null, '200', null, 'cache', 'details']],
212            ['cdata', [' Bar']],
213            ['p_close', []],
214            ['document_end', []],
215        ];
216        $this->assertCalls($calls, $this->H->calls);
217    }
218
219    function testWidthAndHeightParameter()
220    {
221        $this->P->addMode('gfm_media', new GfmMedia());
222        $this->P->parse('Foo ![alt](wiki:image.png?200x100) Bar');
223        $calls = [
224            ['document_start', []],
225            ['p_open', []],
226            ['cdata', ["\nFoo "]],
227            ['internalmedia', ['wiki:image.png', 'alt', null, '200', '100', 'cache', 'details']],
228            ['cdata', [' Bar']],
229            ['p_close', []],
230            ['document_end', []],
231        ];
232        $this->assertCalls($calls, $this->H->calls);
233    }
234
235    function testLinkingParameter()
236    {
237        $this->P->addMode('gfm_media', new GfmMedia());
238        $this->P->parse('Foo ![alt](wiki:image.png?nolink) Bar');
239        $calls = [
240            ['document_start', []],
241            ['p_open', []],
242            ['cdata', ["\nFoo "]],
243            ['internalmedia', ['wiki:image.png', 'alt', null, null, null, 'cache', 'nolink']],
244            ['cdata', [' Bar']],
245            ['p_close', []],
246            ['document_end', []],
247        ];
248        $this->assertCalls($calls, $this->H->calls);
249    }
250
251    function testCacheParameter()
252    {
253        $this->P->addMode('gfm_media', new GfmMedia());
254        $this->P->parse('Foo ![alt](wiki:image.png?recache) Bar');
255        $calls = [
256            ['document_start', []],
257            ['p_open', []],
258            ['cdata', ["\nFoo "]],
259            ['internalmedia', ['wiki:image.png', 'alt', null, null, null, 'recache', 'details']],
260            ['cdata', [' Bar']],
261            ['p_close', []],
262            ['document_end', []],
263        ];
264        $this->assertCalls($calls, $this->H->calls);
265    }
266
267    function testCombinedParameters()
268    {
269        $this->P->addMode('gfm_media', new GfmMedia());
270        $this->P->parse('Foo ![alt](wiki:image.png?200x100&nolink&nocache) Bar');
271        $calls = [
272            ['document_start', []],
273            ['p_open', []],
274            ['cdata', ["\nFoo "]],
275            ['internalmedia', ['wiki:image.png', 'alt', null, '200', '100', 'nocache', 'nolink']],
276            ['cdata', [' Bar']],
277            ['p_close', []],
278            ['document_end', []],
279        ];
280        $this->assertCalls($calls, $this->H->calls);
281    }
282
283    function testLastQuestionMarkIsTheParameterDelimiter()
284    {
285        // URLs may carry their own query string; DW splits on the *last* `?`
286        // so the URL query survives as part of the src.
287        $this->P->addMode('gfm_media', new GfmMedia());
288        $this->P->parse('Foo ![alt](https://example.com/img?v=2?200x100) Bar');
289        $calls = [
290            ['document_start', []],
291            ['p_open', []],
292            ['cdata', ["\nFoo "]],
293            ['externalmedia', ['https://example.com/img?v=2', 'alt', null, '200', '100', 'cache', 'details']],
294            ['cdata', [' Bar']],
295            ['p_close', []],
296            ['document_end', []],
297        ];
298        $this->assertCalls($calls, $this->H->calls);
299    }
300
301    function testAlignRight()
302    {
303        // GFM has no native image-alignment syntax, so GfmMedia borrows
304        // DW's ?right/?left/?center keyword from the shared URL parameter
305        // block — the only way to align an inline GFM image.
306        $this->P->addMode('gfm_media', new GfmMedia());
307        $this->P->parse('Foo ![alt](wiki:image.png?right) Bar');
308        $calls = [
309            ['document_start', []],
310            ['p_open', []],
311            ['cdata', ["\nFoo "]],
312            ['internalmedia', ['wiki:image.png', 'alt', 'right', null, null, 'cache', 'details']],
313            ['cdata', [' Bar']],
314            ['p_close', []],
315            ['document_end', []],
316        ];
317        $this->assertCalls($calls, $this->H->calls);
318    }
319
320    function testAlignWithSizeAndLinking()
321    {
322        $this->P->addMode('gfm_media', new GfmMedia());
323        $this->P->parse('Foo ![alt](wiki:image.png?200x100&center&nolink) Bar');
324        $calls = [
325            ['document_start', []],
326            ['p_open', []],
327            ['cdata', ["\nFoo "]],
328            ['internalmedia', ['wiki:image.png', 'alt', 'center', '200', '100', 'cache', 'nolink']],
329            ['cdata', [' Bar']],
330            ['p_close', []],
331            ['document_end', []],
332        ];
333        $this->assertCalls($calls, $this->H->calls);
334    }
335
336    function testSortValue()
337    {
338        $this->assertSame(310, (new GfmMedia())->getSort());
339    }
340}
341