xref: /dokuwiki/_test/tests/File/PageResolverTest.php (revision b646f7836bc4aabdbd7b0e4ad1b5c9d429c0481d)
1<?php
2
3namespace dokuwiki\test\File;
4
5use dokuwiki\File\PageResolver;
6
7/**
8 * @todo tests that make use of revisions might be wanted
9 */
10class PageResolverTest extends \DokuWikiTest
11{
12    /**
13     * @return \Generator|array
14     * @see testResolveID
15     */
16    public function provideResolveData()
17    {
18        $data = [
19            // relative current in root
20            ['context', 'page', 'page'],
21            ['context', '.page', 'page'],
22            ['context', '.:page', 'page'],
23
24            // relative current in namespace
25            ['lev1:lev2:context', 'page', 'lev1:lev2:page'],
26            ['lev1:lev2:context', '.page', 'lev1:lev2:page'],
27            ['lev1:lev2:context', '.:page', 'lev1:lev2:page'],
28
29            // relative upper in root
30            ['context', '..page', 'page'],
31            ['context', '..:page', 'page'],
32
33            // relative upper in namespace
34            ['lev1:lev2:context', '..page', 'lev1:page'],
35            ['lev1:lev2:context', '..:page', 'lev1:page'],
36            ['lev1:lev2:context', '..:..:page', 'page'],
37            ['lev1:lev2:context', '..:..:..:page', 'page'],
38
39            // deeper nesting
40            ['lev1:lev2:lev3:context', '..page', 'lev1:lev2:page'],
41            ['lev1:lev2:lev3:context', '..:page', 'lev1:lev2:page'],
42            ['lev1:lev2:lev3:context', '..:..page', 'lev1:page'],
43            ['lev1:lev2:lev3:context', '..:..:page', 'lev1:page'],
44            ['lev1:lev2:lev3:context', '..:..:..page', 'page'],
45            ['lev1:lev2:lev3:context', '..:..:..:page', 'page'],
46            ['lev1:lev2:lev3:context', '..:..:..:..page', 'page'],
47            ['lev1:lev2:lev3:context', '..:..:..:..:page', 'page'],
48
49            // strange and broken ones
50            ['lev1:lev2:context', '....:....:page', 'lev1:lev2:page'],
51            ['lev1:lev2:context', '..:..:lev3:page', 'lev3:page'],
52            ['lev1:lev2:context', '..:..:lev3:..:page', 'page'],
53            ['lev1:lev2:context', '..:..:lev3:..:page:....:...', 'page'],
54
55            // relative to current page
56            ['context', '~page', 'context:page'],
57            ['context', '~:page', 'context:page'],
58            ['lev1:lev2:context', '~page', 'lev1:lev2:context:page'],
59            ['lev1:lev2:context', '~:page', 'lev1:lev2:context:page'],
60
61            // start pages
62            ['context', '.:', 'start'],
63            ['foo:context', '.:', 'foo:start'],
64            ['context', 'foo:', 'foo:start'],
65            ['foo:context', 'foo:', 'foo:start'],
66            ['context', '~foo:', 'context:foo:start'],
67            ['foo:context', '~foo:', 'foo:context:foo:start'],
68
69            // empty page links to itself
70            ['context', '', 'context'],
71            ['foo:context', '', 'foo:context'],
72        ];
73
74        // run each test without a hash
75        foreach ($data as $row) {
76            yield $row;
77        }
78
79        // run each test with a hash
80        foreach ($data as $row) {
81            $row[1] .= '#somehash';
82            $row[2] .= '#somehash';
83            yield $row;
84        }
85    }
86
87    /**
88     * @dataProvider provideResolveData
89     * @param string $context
90     * @param string $id
91     * @param string $expected
92     */
93    public function testResolveID($context, $id, $expected)
94    {
95        $resolver = new PageResolver($context);
96        $this->assertEquals($expected, $resolver->resolveId($id));
97    }
98
99    /**
100     * Tilde start page bahaviour
101     *
102     * Please note that a ~ alone is the same as ~:
103     */
104    public function testTildeStartPage()
105    {
106        $context = 'foo:context';
107        $resolver = new PageResolver($context);
108
109        // the $context page itself does not exist
110        // a link like that is usually not possible, but we fall back to standard start
111        // page behaviour
112        $this->assertEquals("$context:start", $resolver->resolveId('~:'));
113        $this->assertEquals("$context:start", $resolver->resolveId('~'));
114
115        // now $context has become the start page
116        saveWikiText($context, 'test', 'test');
117        $this->assertEquals($context, $resolver->resolveId('~:'));
118
119        // now we have a startpage named like the namespace
120        saveWikiText("$context:context", 'test', 'test');
121        $this->assertEquals("$context:context", $resolver->resolveId('~:'));
122        $this->assertEquals("$context:context", $resolver->resolveId('~'));
123
124        // now we have a dedicated start page
125        saveWikiText("$context:start", 'test', 'test');
126        $this->assertEquals("$context:start", $resolver->resolveId('~:'));
127        $this->assertEquals("$context:start", $resolver->resolveId('~'));
128    }
129
130    public function testResolveStartPage()
131    {
132
133        $resolver = new PageResolver('arbitrary');
134
135        $expect = 'foo:start';
136        $actual = $this->callInaccessibleMethod($resolver, 'resolveStartPage', ['foo:', false, false]);
137        $this->assertEquals($expect, $actual, 'default non-existing');
138
139        saveWikiText('foo', 'test', 'test');
140        $expect = 'foo';
141        $actual = $this->callInaccessibleMethod($resolver, 'resolveStartPage', ['foo:', false, false]);
142        $this->assertEquals($expect, $actual, 'page like namespace outside');
143
144        saveWikiText('foo:foo', 'test', 'test');
145        $expect = 'foo:foo';
146        $actual = $this->callInaccessibleMethod($resolver, 'resolveStartPage', ['foo:', false, false]);
147        $this->assertEquals($expect, $actual, 'page like namespace inside');
148
149        saveWikiText('foo:start', 'test', 'test');
150        $expect = 'foo:start';
151        $actual = $this->callInaccessibleMethod($resolver, 'resolveStartPage', ['foo:', false, false]);
152        $this->assertEquals($expect, $actual, 'default existing');
153    }
154
155    /**
156     * @return array
157     * @see testResolveRelatives
158     */
159    public function provideResolveRelatives()
160    {
161        return [
162            ['foo', 'foo'],
163            ['foo:bar', 'foo:bar'],
164            ['foo:..:bar', 'bar'],
165            ['foo:..:..:bar', 'bar'],
166            ['foo:.:bar', 'foo:bar'],
167            ['foo:.:..:.:bar', 'bar'],
168            ['foo:.:.:.:bar', 'foo:bar'],
169            ['foo::::bar', 'foo:bar'],
170            ['foo::::bar:', 'foo:bar:'],
171            ['foo:bar:', 'foo:bar:'],
172        ];
173    }
174
175    /**
176     * @dataProvider provideResolveRelatives
177     * @param string $input
178     * @param string $expected
179     */
180    public function testResolveRelatives($input, $expected)
181    {
182        $resolver = new PageResolver('arbitrary');
183
184        $actual = $this->callInaccessibleMethod($resolver, 'resolveRelatives', [$input]);
185        $this->assertEquals($expected, $actual);
186    }
187
188    public function testAutoPlural()
189    {
190        $resolver = new PageResolver('arbitrary');
191
192        $singular = 'some:page';
193        $plural = 'some:pages';
194
195        $actual = $this->callInaccessibleMethod($resolver, 'resolveAutoPlural', [$singular, '', false]);
196        $this->assertEquals($singular, $actual); // no pages exist
197
198        saveWikiText($plural, 'plural', 'plural');
199        $actual = $this->callInaccessibleMethod($resolver, 'resolveAutoPlural', [$singular, '', false]);
200        $this->assertEquals($plural, $actual); // plural exists
201
202        saveWikiText($singular, 'singular', 'singular');
203        $actual = $this->callInaccessibleMethod($resolver, 'resolveAutoPlural', [$singular, '', false]);
204        $this->assertEquals($singular, $actual); // requested singular has preference
205    }
206}
207