1<?php 2 3namespace dokuwiki\plugin\struct\test\types; 4 5use dokuwiki\plugin\struct\meta\Value; 6use dokuwiki\plugin\struct\test\mock\Search; 7use dokuwiki\plugin\struct\test\StructTest; 8use dokuwiki\plugin\struct\types\Page; 9 10/** 11 * Testing the Page Type 12 * 13 * @group plugin_struct 14 * @group plugins 15 */ 16class PageTest extends StructTest 17{ 18 19 public function setUp(): void 20 { 21 parent::setUp(); 22 23 saveWikiText('syntax', 'dummy', 'test'); 24 saveWikiText('foo:syntax:test_special.characters', 'dummy text', 'dummy summary'); 25 26 // make sure the search index is initialized 27 idx_addPage('wiki:syntax'); 28 idx_addPage('syntax'); 29 idx_addPage('wiki:welcome'); 30 idx_addPage('wiki:dokuwiki'); 31 idx_addPage('foo:syntax:test_special.characters'); 32 } 33 34 public function test_sort() 35 { 36 37 saveWikiText('title1', 'test', 'test'); 38 $pageMeta = new \dokuwiki\plugin\struct\meta\PageMeta('title1'); 39 $pageMeta->setTitle('This is a title'); 40 41 saveWikiText('title2', 'test', 'test'); 42 $pageMeta = new \dokuwiki\plugin\struct\meta\PageMeta('title2'); 43 $pageMeta->setTitle('This is a title'); 44 45 saveWikiText('title3', 'test', 'test'); 46 $pageMeta = new \dokuwiki\plugin\struct\meta\PageMeta('title3'); 47 $pageMeta->setTitle('Another Title'); 48 49 50 $this->loadSchemaJSON('pageschema'); 51 $this->saveData('test1', 'pageschema', ['singletitle' => 'title1']); 52 $this->saveData('test2', 'pageschema', ['singletitle' => 'title2']); 53 $this->saveData('test3', 'pageschema', ['singletitle' => 'title3']); 54 55 $search = new Search(); 56 $search->addSchema('pageschema'); 57 $search->addColumn('%pageid%'); 58 $search->addColumn('singletitle'); 59 $search->addSort('singletitle', true); 60 /** @var Value[][] $result */ 61 $result = $search->getRows(); 62 63 $this->assertEquals(3, count($result)); 64 $this->assertEquals('test3', $result[0][0]->getValue()); 65 $this->assertEquals('test1', $result[1][0]->getValue()); 66 $this->assertEquals('test2', $result[2][0]->getValue()); 67 } 68 69 70 public function test_search() 71 { 72 // prepare some data 73 $this->loadSchemaJSON('pageschema'); 74 $this->saveData( 75 'syntax', 76 'pageschema', 77 [ 78 'singlepage' => 'wiki:dokuwiki', 79 'multipage' => ['wiki:dokuwiki', 'wiki:syntax', 'wiki:welcome'], 80 'singletitle' => 'wiki:dokuwiki', 81 'multititle' => ['wiki:dokuwiki', 'wiki:syntax', 'wiki:welcome'], 82 ] 83 ); 84 85 // make sure titles for some pages are known (not for wiki:welcome) 86 $pageMeta = new \dokuwiki\plugin\struct\meta\PageMeta('wiki:dokuwiki'); 87 $pageMeta->setTitle('DokuWiki Overview'); 88 $pageMeta = new \dokuwiki\plugin\struct\meta\PageMeta('wiki:syntax'); 89 $pageMeta->setTitle('DokuWiki Foobar Syntax'); 90 $pageMeta->savePageData(); 91 92 // search 93 $search = new Search(); 94 $search->addSchema('pageschema'); 95 $search->addColumn('singlepage'); 96 $search->addColumn('multipage'); 97 $search->addColumn('singletitle'); 98 $search->addColumn('multititle'); 99 100 /** @var Value[][] $result */ 101 $result = $search->getRows(); 102 103 // no titles: 104 $this->assertEquals('wiki:dokuwiki', $result[0][0]->getValue()); 105 $this->assertEquals(['wiki:dokuwiki', 'wiki:syntax', 'wiki:welcome'], $result[0][1]->getValue()); 106 // titles as JSON: 107 $this->assertEquals('["wiki:dokuwiki","DokuWiki Overview"]', $result[0][2]->getValue()); 108 $this->assertEquals( 109 [ 110 '["wiki:dokuwiki","DokuWiki Overview"]', 111 '["wiki:syntax","DokuWiki Foobar Syntax"]', 112 '["wiki:welcome",null]' // no title for this 113 ], 114 $result[0][3]->getValue() 115 ); 116 117 // if there is no title in the database display the pageid 118 $this->assertEquals( 119 [ 120 'DokuWiki Overview', 121 'DokuWiki Foobar Syntax', 122 'wiki:welcome' 123 ], 124 $result[0][3]->getDisplayValue() 125 ); 126 127 // search single with title 128 $single = clone $search; 129 $single->addFilter('singletitle', 'Overview', '*~', 'AND'); 130 $result = $single->getRows(); 131 $this->assertTrue(is_array($result)); 132 $this->assertEquals(1, count($result)); 133 134 // search multi with title 135 $multi = clone $search; 136 $multi->addFilter('multititle', 'Foobar', '*~', 'AND'); 137 $result = $multi->getRows(); 138 $this->assertTrue(is_array($result)); 139 $this->assertEquals(1, count($result)); 140 141 // search single with page 142 $single = clone $search; 143 $single->addFilter('singletitle', 'wiki:dokuwiki', '*~', 'AND'); 144 $result = $single->getRows(); 145 $this->assertTrue(is_array($result)); 146 $this->assertEquals(1, count($result)); 147 148 // search multi with page 149 $multi = clone $search; 150 $multi->addFilter('multititle', 'welcome', '*~', 'AND'); 151 $result = $multi->getRows(); 152 $this->assertTrue(is_array($result)); 153 $this->assertEquals(1, count($result)); 154 } 155 156 157 /** 158 * This provides the testdata for @see Type_Page_struct_test::test_validate 159 */ 160 public static function validate_testdata() 161 { 162 return [ 163 [ 164 'namespace:page', 165 'namespace:page', 166 'do not change clean valid page' 167 ], 168 [ 169 'namespace:page#headline', 170 'namespace:page#headline', 171 'keep fragments' 172 ], 173 [ 174 'namespace:page#headline#second', 175 'namespace:page#headline_second', 176 'keep fragments, but only the first one' 177 ], 178 [ 179 'namespace:page?do=something', 180 'namespace:page_do_something', 181 'clean query strings' 182 ] 183 ]; 184 } 185 186 /** 187 * @param string $rawvalue 188 * @param string $validatedValue 189 * @param string $msg 190 * 191 * @dataProvider validate_testdata 192 */ 193 public function test_validate($rawvalue, $validatedValue, $msg) 194 { 195 $page = new Page(); 196 $this->assertEquals($validatedValue, $page->validate($rawvalue), $msg); 197 } 198 199 public function test_ajax_default() 200 { 201 global $INPUT; 202 203 $page = new Page( 204 [ 205 'autocomplete' => [ 206 'mininput' => 2, 207 'maxresult' => 5, 208 'filter' => '', 209 'postfix' => '', 210 ], 211 ] 212 ); 213 214 $INPUT->set('search', 'syntax'); 215 $this->assertEquals( 216 [ 217 ['label' => 'syntax', 'value' => 'syntax'], 218 ['label' => 'syntax (wiki)', 'value' => 'wiki:syntax'], 219 ['label' => 'test_special.characters (foo:syntax)', 'value' => 'foo:syntax:test_special.characters'], 220 ], $page->handleAjax() 221 ); 222 223 $INPUT->set('search', 'ynt'); 224 $this->assertEquals( 225 [ 226 ['label' => 'syntax', 'value' => 'syntax'], 227 ['label' => 'syntax (wiki)', 'value' => 'wiki:syntax'], 228 ['label' => 'test_special.characters (foo:syntax)', 'value' => 'foo:syntax:test_special.characters'], 229 ], $page->handleAjax() 230 ); 231 232 $INPUT->set('search', 's'); // under mininput 233 $this->assertEquals([], $page->handleAjax()); 234 235 $INPUT->set('search', 'test_special.char'); // special characters in id 236 $this->assertEquals([ 237 ['label' => 'test_special.characters (foo:syntax)', 'value' => 'foo:syntax:test_special.characters'] 238 ], $page->handleAjax()); 239 } 240 241 /** 242 * Test deprecated option namespace 243 * @return void 244 */ 245 public function test_ajax_namespace() 246 { 247 global $INPUT; 248 249 $page = new Page( 250 [ 251 'autocomplete' => [ 252 'mininput' => 2, 253 'maxresult' => 5, 254 'namespace' => 'wiki', 255 'postfix' => '', 256 ], 257 ] 258 ); 259 260 $INPUT->set('search', 'ynt'); 261 $this->assertEquals([['label' => 'syntax (wiki)', 'value' => 'wiki:syntax']], $page->handleAjax()); 262 } 263 264 public function test_ajax_filter_multiple() 265 { 266 global $INPUT; 267 268 $page = new Page( 269 [ 270 'autocomplete' => [ 271 'mininput' => 2, 272 'maxresult' => 5, 273 'filter' => '(wiki|foo)', 274 'postfix' => '', 275 ], 276 ] 277 ); 278 279 $INPUT->set('search', 'ynt'); 280 $this->assertEquals([ 281 ['label' => 'syntax (wiki)', 'value' => 'wiki:syntax'], 282 ['label' => 'test_special.characters (foo:syntax)', 'value' => 'foo:syntax:test_special.characters'] 283 ], $page->handleAjax()); 284 } 285 286 /** 287 * Test deprecated option postfix 288 * @return void 289 */ 290 public function test_ajax_postfix() 291 { 292 global $INPUT; 293 294 $page = new Page( 295 [ 296 'autocomplete' => [ 297 'mininput' => 2, 298 'maxresult' => 5, 299 'namespace' => '', 300 'postfix' => 'iki', 301 ], 302 ] 303 ); 304 305 $INPUT->set('search', 'oku'); 306 $this->assertEquals([['label' => 'dokuwiki (wiki)', 'value' => 'wiki:dokuwiki']], $page->handleAjax()); 307 308 $page = new Page( 309 [ 310 'autocomplete' => [ 311 'mininput' => 2, 312 'maxresult' => 5, 313 'namespace' => 'wiki', 314 'postfix' => 'iki', 315 ], 316 ] 317 ); 318 319 $INPUT->set('search', 'oku'); 320 $this->assertEquals([['label' => 'dokuwiki (wiki)', 'value' => 'wiki:dokuwiki']], $page->handleAjax()); 321 } 322 323 /** 324 * Test simple filter matching in autocompletion 325 * 326 * @return void 327 */ 328 public function test_filter_matching_simple() 329 { 330 $page = new Page(); 331 332 $this->assertTrue($page->filterMatch('foo:start', 'foo')); 333 $this->assertTrue($page->filterMatch('start#foo', 'foo')); 334 $this->assertFalse($page->filterMatch('ns:foo', ':foo')); 335 $this->assertTrue($page->filterMatch('foo-bar:start', 'foo-bar')); 336 $this->assertTrue($page->filterMatch('foo-bar:start-with_special.chars', 'foo-bar')); 337 $this->assertTrue($page->filterMatch('foo.bar:start', 'foo.bar')); 338 $this->assertTrue($page->filterMatch('ns:foo.bar', 'foo.bar')); 339 $this->assertTrue($page->filterMatch('ns:foo.bar:start', 'foo.bar')); 340 $this->assertFalse($page->filterMatch('ns:foo_bar:start', ':foo_bar')); 341 $this->assertTrue($page->filterMatch('8bar:start', '8bar')); 342 $this->assertTrue($page->filterMatch('ns:8bar:start', '8bar')); 343 $this->assertTrue($page->filterMatch('ns:98bar:start', '8bar')); 344 } 345 346 /** 347 * Test pattern matching in autocompletion 348 * 349 * @return void 350 */ 351 public function test_filter_matching_regex() 352 { 353 $page = new Page(); 354 355 $filter = '(foo:|^:foo:|(?::|^)bar:|foo:bar|foo-bar:|^:foo_bar:|foo\.bar:|(?::|^)8bar:)'; 356 357 $this->assertTrue($page->filterMatch('foo:start', $filter)); 358 $this->assertFalse($page->filterMatch('start#foo', $filter)); 359 $this->assertFalse($page->filterMatch('ns:foo', $filter)); 360 $this->assertTrue($page->filterMatch('bar:foo', $filter)); 361 $this->assertTrue($page->filterMatch('ns:foo:start', $filter)); 362 $this->assertTrue($page->filterMatch('ns:foo:start#headline', $filter)); 363 $this->assertTrue($page->filterMatch('foo-bar:start', $filter)); 364 $this->assertTrue($page->filterMatch('foo-bar:start-with_special.chars', $filter)); 365 $this->assertTrue($page->filterMatch('foo.bar:start', $filter)); 366 $this->assertFalse($page->filterMatch('ns:foo.bar', $filter)); 367 $this->assertTrue($page->filterMatch('ns:foo.bar:start', $filter)); 368 $this->assertFalse($page->filterMatch('ns:foo_bar:start', $filter)); 369 $this->assertTrue($page->filterMatch('8bar:start', $filter)); 370 $this->assertTrue($page->filterMatch('ns:8bar:start', $filter)); 371 $this->assertFalse($page->filterMatch('ns:98bar:start', $filter)); 372 373 $filter = '^:systems:[^:]+:components:([^:]+:){1,2}[^:]+$'; 374 $this->assertTrue($page->filterMatch('systems:system1:components:sub1:sub2:start', $filter)); 375 $this->assertFalse($page->filterMatch('systems:system1:components:sub1:sub2:sub3:start', $filter)); 376 } 377} 378