1<?php
2
3use DOMWrap\Document;
4
5require_once DOKU_INC . 'inc/parser/xhtml.php';
6
7/**
8 * @group plugin_indexmenu
9 */
10class IndexmenuSyntaxTest extends DokuWikiTest
11{
12
13    public function setup(): void
14    {
15//        global $conf;
16        $this->pluginsEnabled[] = 'indexmenu';
17        parent::setup();
18
19        //$conf['plugin']['indexmenu']['headpage'] = '';
20        //$conf['plugin']['indexmenu']['hide_headpage'] = false;
21
22        //saveWikiText('titleonly:sub:test', "====== Title ====== \n content", 'created');
23        //saveWikiText('test', "====== Title ====== \n content", 'created');
24        //idx_addPage('titleonly:sub:test');
25        //idx_addPage('test');
26    }
27
28//    public function __construct() {
29////        $this->exampleIndex = "{{indexmenu>:}}";
30//
31//        parent::__construct();
32//    }
33
34    /**
35     * Create from list of values the output array of handle()
36     *
37     * @param array $values
38     * @return array aligned similar to output of handle()
39     */
40    private function createData($values)
41    {
42
43        [
44            $ns, $theme, $identifier, $nocookie, $navbar, $noscroll, $maxjs, $notoc, $jsajax, $context, $nomenu,
45            $sort, $msort, $rsort, $nsort, $level, $nons, $nopg, $subnss, $max, $maxAjax, $js, $skipns, $skipfile,
46            $skipnscombined, $skipfilecombined, $hsort, $headpage, $hide_headpage, $jsVersion
47        ] = $values;
48
49        return [
50            $ns,
51            [
52                'theme' => $theme,
53                'identifier' => $identifier,
54                'nocookie' => $nocookie,
55                'navbar' => $navbar,
56                'noscroll' => $noscroll,
57                'maxJs' => $maxjs,
58                'notoc' => $notoc,
59                'jsAjax' => $jsajax,
60                'context' => $context,
61                'nomenu' => $nomenu,
62            ],
63            [
64                'sort' => $sort,
65                'msort' => $msort,
66                'rsort' => $rsort,
67                'nsort' => $nsort,
68                'hsort' => $hsort,
69            ],
70            [
71                'level' => $level,
72                'nons' => $nons,
73                'nopg' => $nopg,
74                'subnss' => $subnss,
75                'max' => $max,
76                'js' => $js,
77                'skipns' => $skipns,
78                'skipfile' => $skipfile,
79                'skipnscombined' => $skipnscombined,
80                'skipfilecombined' => $skipfilecombined,
81                'headpage' => $headpage,
82                'hide_headpage' => $hide_headpage,
83                'maxajax' => $maxAjax,
84                'navbar' => $navbar,
85                'theme' => $theme
86            ],
87            $jsVersion
88        ];
89    }
90
91    /**
92     * Data provider
93     *
94     * @return array[]
95     */
96    public static function someSyntaxes()
97    {
98        return [
99            //root ns (empty is not recognized..)
100            // [syntax, data]
101            [
102                "{{indexmenu>:}}",
103                [
104                    '', 'default', 'random', false, false, false, 1, false, '', false, false,
105                    0, false, false, false, -1, false, false, [], 0, 1, false, '', '', [''], [''], false,
106                    ":start:,:same:,:inside:", 1, 1
107                ]
108            ],
109            //root ns, #levels=1, js renderer
110            [
111                "{{indexmenu>#1|js}}",
112                [
113                    '', 'default', 'random', false, false, false, 1, false, '', false, false,
114                    0, false, false, false, 1, false, false, [], 0, 1, true, '', '', [''], [''], false,
115                    ":start:,:same:,:inside:", 1, 1
116                ]
117            ],
118            //root ns, #levels=2, all not js specific options  (nocookie is from context)
119            [
120                "{{indexmenu>#2 test#6|navbar context tsort dsort msort hsort rsort nsort nons nopg}}",
121                [
122                    '', 'default', 'random', true, true, false, 1, false, '&sort=t&msort=indexmenu_n&rsort=1&nsort=1&hsort=1&nopg=1', true, false,
123                    't', 'indexmenu_n', true, true, 2, true, true, [['test', 6]], 0, 1, false, '', '', [''], [''], true,
124                    ":start:,:same:,:inside:", 1, 1
125                ]
126            ],
127            //root ns, #levels=2, js renderer, all not js specific options
128            [
129                "{{indexmenu>#2 test#6|navbar js#bj_ubuntu.png context tsort dsort msort hsort rsort nsort nons nopg}}",
130                [
131                    '', 'bj_ubuntu.png', 'random', true, true, false, 1, false, '&sort=t&msort=indexmenu_n&rsort=1&nsort=1&hsort=1&nopg=1', true, false,
132                    't', 'indexmenu_n', true, true, 2, true, true, [['test', 6]], 0, 1, true, '', '', [''], [''], true,
133                    ":start:,:same:,:inside:", 1, 1
134                ]
135            ],
136            //root ns, #levels=1, all options
137            [
138                "{{indexmenu>#1|navbar context nocookie noscroll notoc nomenu dsort msort#date:modified hsort rsort nsort nons nopg max#2#4 maxjs#3 id#54321}}",
139                [
140                    '', 'default', 'random', true, true, true, 1, true, '&sort=d&msort=date modified&rsort=1&nsort=1&hsort=1&nopg=1', true, true,
141                    'd', 'date modified', true, true, 1, true, true, [], 0, 1, false, '', '', [''], [''], true,
142                    ":start:,:same:,:inside:", 1, 1
143                ]
144            ],
145            //root ns, #levels=1, js renderer, all options
146            [
147                "{{indexmenu>#1|js#bj_ubuntu.png navbar context nocookie noscroll notoc nomenu dsort msort#date:modified hsort rsort nsort nons nopg max#2#4 maxjs#3 id#54321}}",
148                [
149                    '', 'bj_ubuntu.png', 54321, true, true, true, 3, true, '&sort=d&msort=date modified&rsort=1&nsort=1&hsort=1&nopg=1&max=4', true, true,
150                    'd', 'date modified', true, true, 1, true, true, [], 2, 4, true, '', '', [''], [''], true,
151                    ":start:,:same:,:inside:", 1, 1
152                ]
153            ],
154            //root ns, #levels=1, skipfile and ns
155
156            [
157                "{{indexmenu>#1 test|skipfile+/(^myusers:spaces$|privatens:userss)/ skipns=/(^myusers:spaces$|privatens:users)/ id#ns}}",
158                [
159                    '', 'default', 'random', false, false, false, 1, false, '&skipns=%3D/%28%5Emyusers%3Aspaces%24%7Cprivatens%3Ausers%29/&skipfile=%2B/%28%5Emyusers%3Aspaces%24%7Cprivatens%3Auserss%29/', false, false,
160                    0, false, false, false, 1, false, false, [['test', -1]], 0, 1, false, '=/(^myusers:spaces$|privatens:users)/',
161                    '+/(^myusers:spaces$|privatens:userss)/', ['/(^myusers:spaces$|privatens:users)/'], ['', '/(^myusers:spaces$|privatens:userss)/'], false,
162                    ":start:,:same:,:inside:", 1, 1
163                ]
164            ],
165            //root ns, #levels=1, js renderer, skipfile and ns
166            [
167                "{{indexmenu>#1 test|js skipfile=/(^myusers:spaces$|privatens:userss)/ skipns+/(^myusers:spaces$|privatens:userssss)/ id#ns}}",
168                [
169                    '', 'default', 0, false, false, false, 1, false, '&skipns=%2B/%28%5Emyusers%3Aspaces%24%7Cprivatens%3Auserssss%29/&skipfile=%3D/%28%5Emyusers%3Aspaces%24%7Cprivatens%3Auserss%29/', false, false,
170                    0, false, false, false, 1, false, false, [['test', -1]], 0, 1, true, '+/(^myusers:spaces$|privatens:userssss)/',
171                    '=/(^myusers:spaces$|privatens:userss)/', ['', '/(^myusers:spaces$|privatens:userssss)/'], ['/(^myusers:spaces$|privatens:userss)/'], false,
172                    ":start:,:same:,:inside:", 1, 1
173                ]
174            ]
175        ];
176    }
177
178    /**
179     * Parse the syntax to options
180     * expect: different combinations with or without js option, covers recognizing all syntax options
181     *
182     * @dataProvider someSyntaxes
183     */
184    public function testHandle($syntax, $changedData)
185    {
186        $plugin = new syntax_plugin_indexmenu_indexmenu();
187
188        $null = new Doku_Handler();
189        $result = $plugin->handle($syntax, 0, 40, $null);
190
191        //copy unique generated number, which is about 23 characters
192        $len_id = strlen($result[1]['identifier']);
193        if (!is_numeric($changedData[2]) && ($len_id > 18 && $len_id <= 23)) {
194            $changedData[2] = $result[1]['identifier'];
195        }
196        $data = $this->createData($changedData);
197
198        $this->assertEquals($data, $result, 'Data array corrupted');
199    }
200
201
202    /**
203     * Data provider
204     *
205     * @return array[]
206     */
207    public static function differentNSs()
208    {
209        $pageInRoot = 'page';
210        $pageInLvl1 = 'ns:page';
211        $pageInLvl2 = 'ns1:ns2:page';
212        return [
213            //indexmenu on page at root level
214            ['{{indexmenu>|}}', '', [], $pageInRoot],
215            ['{{indexmenu>#1}}', '', [], $pageInRoot],
216            ['{{indexmenu>:}}', '', [], $pageInRoot],
217            ['{{indexmenu>.}}', '', [], $pageInRoot],
218            ['{{indexmenu>.:}}', '', [], $pageInRoot],
219            ['{{indexmenu>..}}', '', [], $pageInRoot],
220            ['{{indexmenu>..:}}', '', [], $pageInRoot],
221            ['{{indexmenu>myns}}', 'myns', [], $pageInRoot],
222            ['{{indexmenu>:myns}}', 'myns', [], $pageInRoot],
223            ['{{indexmenu>.myns}}', 'myns', [], $pageInRoot],
224            ['{{indexmenu>.:myns}}', 'myns', [], $pageInRoot],
225            ['{{indexmenu>..myns}}', 'myns', [], $pageInRoot],
226            ['{{indexmenu>..:myns}}', 'myns', [], $pageInRoot],
227
228            //indexmenu on page in a namespace
229            ['{{indexmenu>|}}', '', [], $pageInLvl1],
230            ['{{indexmenu>#1}}', '', [], $pageInLvl1],
231            ['{{indexmenu>:}}', '', [], $pageInLvl1],
232            ['{{indexmenu>.}}', 'ns', [], $pageInLvl1],
233            ['{{indexmenu>.:}}', 'ns', [], $pageInLvl1],
234            ['{{indexmenu>..}}', '', [], $pageInLvl1],
235            ['{{indexmenu>..:}}', '', [], $pageInLvl1],
236            ['{{indexmenu>myns}}', 'myns', [], $pageInLvl1], //was ns:myns
237            ['{{indexmenu>:myns}}', 'myns', [], $pageInLvl1],
238            ['{{indexmenu>.myns}}', 'ns:myns', [], $pageInLvl1],
239            ['{{indexmenu>.:myns}}', 'ns:myns', [], $pageInLvl1],
240            ['{{indexmenu>..myns}}', 'myns', [], $pageInLvl1],
241            ['{{indexmenu>..:myns}}', 'myns', [], $pageInLvl1],
242            ['{{indexmenu>myns:myns}}', 'myns:myns', [], $pageInLvl2],
243
244            //indexmenu on page in a namespace
245            ['{{indexmenu>|}}', '', [], $pageInLvl2],
246            ['{{indexmenu>#1}}', '', [], $pageInLvl2],
247            ['{{indexmenu>:}}', '', [], $pageInLvl2],
248            ['{{indexmenu>.}}', 'ns1:ns2', [], $pageInLvl2],
249            ['{{indexmenu>.:}}', 'ns1:ns2', [], $pageInLvl2],
250            ['{{indexmenu>..}}', '', [], $pageInLvl2], //strange indexmenu specific exception! TODO remove?
251            ['{{indexmenu>..:}}', 'ns1', [], $pageInLvl2],
252            ['{{indexmenu>myns}}', 'myns', [], $pageInLvl2], //was ns1:ns2:myns
253            ['{{indexmenu>:myns}}', 'myns', [], $pageInLvl2],
254            ['{{indexmenu>.myns}}', 'ns1:ns2:myns', [], $pageInLvl2],
255            ['{{indexmenu>.:myns}}', 'ns1:ns2:myns', [], $pageInLvl2],
256            ['{{indexmenu>..myns}}', 'ns1:myns', [], $pageInLvl2],
257            ['{{indexmenu>..:myns}}', 'ns1:myns', [], $pageInLvl2],
258            ['{{indexmenu>myns:myns}}', 'myns:myns', [], $pageInLvl2],
259
260            ['{{indexmenu>..:..:myns}}', 'ns1:myns', [], 'ns1:ns2:ns3:page'],
261            ['{{indexmenu>0}}', '0', [], 'ns1:page'], //was ns1:0
262
263            //indexmenu on page at root level and subns
264            ['{{indexmenu> #1|}}', '', [], $pageInLvl2], //no subns, spaces before are removed
265            ['{{indexmenu>#1 #1}}', '', [['', 1]], $pageInLvl2],
266            ['{{indexmenu>: :}}', '', [['', -1]], $pageInLvl2],
267            ['{{indexmenu>. .}}', 'ns1:ns2', [['ns1:ns2', -1]], $pageInLvl2],
268            ['{{indexmenu>.: .:}}', 'ns1:ns2', [['ns1:ns2', -1]], $pageInLvl2],
269            ['{{indexmenu>.. ..}}', '', [['', -1]], $pageInLvl2],
270            ['{{indexmenu>..: ..:}}', 'ns1', [['ns1', -1]], $pageInLvl2],
271            ['{{indexmenu>myns myns}}', 'myns', [['myns', -1]], $pageInLvl2], //was ns1:ns2:myns
272            ['{{indexmenu>:myns :myns}}', 'myns', [['myns', -1]], $pageInLvl2],
273            ['{{indexmenu>.myns .myns}}', 'ns1:ns2:myns', [['ns1:ns2:myns', -1]], $pageInLvl2],
274            ['{{indexmenu>.:myns .:myns}}', 'ns1:ns2:myns', [['ns1:ns2:myns', -1]], $pageInLvl2],
275            ['{{indexmenu>..myns ..myns}}', 'ns1:myns', [['ns1:myns', -1]], $pageInLvl2],
276            ['{{indexmenu>..:myns ..myns}}', 'ns1:myns', [['ns1:myns', -1]], $pageInLvl2],
277            ['{{indexmenu>myns:myns myns:myns}}', 'myns:myns', [['myns:myns', -1]], $pageInLvl2],
278
279            //indexmenu on page in a namespace
280            ['{{indexmenu>|}}', '', [], $pageInLvl2],
281            ['{{indexmenu>#1}}', '', [], $pageInLvl2],
282            ['{{indexmenu>:}}', '', [], $pageInLvl2],
283            ['{{indexmenu>.}}', 'ns1:ns2', [], $pageInLvl2],
284            ['{{indexmenu>.:}}', 'ns1:ns2', [], $pageInLvl2],
285            ['{{indexmenu>..}}', '', [], $pageInLvl2], //strange indexmenu specific exception! TODO remove?
286            ['{{indexmenu>..:}}', 'ns1', [], $pageInLvl2],
287            ['{{indexmenu>myns:}}', 'myns', [], $pageInLvl2], //was ns1:ns2:myns
288            ['{{indexmenu>:myns:}}', 'myns', [], $pageInLvl2],
289            ['{{indexmenu>.myns:}}', 'ns1:ns2:myns', [], $pageInLvl2],
290            ['{{indexmenu>.:myns:}}', 'ns1:ns2:myns', [], $pageInLvl2],
291            ['{{indexmenu>..myns:}}', 'ns1:myns', [], $pageInLvl2],
292            ['{{indexmenu>..:myns:}}', 'ns1:myns', [], $pageInLvl2],
293            ['{{indexmenu>myns:myns:}}', 'myns:myns', [], $pageInLvl2],
294        ];
295    }
296
297    /**
298     * Parse the syntax to options
299     * expect: different combinations with or without js option, covers recognizing all syntax options
300     *
301     * @dataProvider differentNSs
302     */
303    public function testResolving($syntax, $expectedNs, $expectedSubNss, $pageWithIndexmenu)
304    {
305        global $ID;
306        $ID = $pageWithIndexmenu;
307
308        $plugin = new syntax_plugin_indexmenu_indexmenu();
309
310        $null = new Doku_Handler();
311        $result = $plugin->handle($syntax, 0, 40, $null);
312
313        $this->assertEquals($expectedNs, $result[0], 'check resolved ns');
314        $this->assertEquals($expectedSubNss, $result[3]['subnss'], 'check resolved subNSs');
315    }
316
317    /**
318     * Rendering for nonexisting namespace
319     * expect: no paragraph due to no message set
320     * expect: one paragraph, since message set
321     * expect: contains namespace which replaced {{ns}}
322     * expect: message contained rendered italic syntax
323     */
324    public function testRenderEmptymsg()
325    {
326        global $conf;
327
328        $noexistns = 'nonexisting:namespace';
329        $emptyindexsyntax = "{{indexmenu>$noexistns}}";
330
331        $xhtml = new Doku_Renderer_xhtml();
332        $plugin = new syntax_plugin_indexmenu_indexmenu();
333
334        $null = new Doku_Handler();
335        $result = $plugin->handle($emptyindexsyntax, 0, 10, $null);
336
337        //no empty message
338        $plugin->render('xhtml', $xhtml, $result);
339
340        $doc = (new Document())->html($xhtml->doc);
341        $this->assertEquals(0, $doc->find('p')->count());
342
343        // Fill in empty message
344        $conf['plugin']['indexmenu']['empty_msg'] = 'This namespace is //empty//: {{ns}}';
345        $plugin->render('xhtml', $xhtml, $result);
346        $doc = (new Document())->html($xhtml->doc);
347
348        $this->assertEquals(1, $doc->find('p')->count());
349//        $this->assertEquals(1, $doc->find("p:contains($noexistns)")->count());
350        $this->assertEquals(1, $doc->find("p em")->count());
351    }
352
353}
354