MediaWiki  1.32.0
ParserOutputTest.php
Go to the documentation of this file.
1 <?php
2 use Wikimedia\TestingAccessWrapper;
3 
9 
10  public static function provideIsLinkInternal() {
11  return [
12  // Different domains
13  [ false, 'http://example.org', 'http://mediawiki.org' ],
14  // Same domains
15  [ true, 'http://example.org', 'http://example.org' ],
16  [ true, 'https://example.org', 'https://example.org' ],
17  [ true, '//example.org', '//example.org' ],
18  // Same domain different cases
19  [ true, 'http://example.org', 'http://EXAMPLE.ORG' ],
20  // Paths, queries, and fragments are not relevant
21  [ true, 'http://example.org', 'http://example.org/wiki/Main_Page' ],
22  [ true, 'http://example.org', 'http://example.org?my=query' ],
23  [ true, 'http://example.org', 'http://example.org#its-a-fragment' ],
24  // Different protocols
25  [ false, 'http://example.org', 'https://example.org' ],
26  [ false, 'https://example.org', 'http://example.org' ],
27  // Protocol relative servers always match http and https links
28  [ true, '//example.org', 'http://example.org' ],
29  [ true, '//example.org', 'https://example.org' ],
30  // But they don't match strange things like this
31  [ false, '//example.org', 'irc://example.org' ],
32  ];
33  }
34 
35  public function tearDown() {
36  MWTimestamp::setFakeTime( false );
37 
38  parent::tearDown();
39  }
40 
46  public function testIsLinkInternal( $shouldMatch, $server, $url ) {
47  $this->assertEquals( $shouldMatch, ParserOutput::isLinkInternal( $server, $url ) );
48  }
49 
54  public function testExtensionData() {
55  $po = new ParserOutput();
56 
57  $po->setExtensionData( "one", "Foo" );
58 
59  $this->assertEquals( "Foo", $po->getExtensionData( "one" ) );
60  $this->assertNull( $po->getExtensionData( "spam" ) );
61 
62  $po->setExtensionData( "two", "Bar" );
63  $this->assertEquals( "Foo", $po->getExtensionData( "one" ) );
64  $this->assertEquals( "Bar", $po->getExtensionData( "two" ) );
65 
66  $po->setExtensionData( "one", null );
67  $this->assertNull( $po->getExtensionData( "one" ) );
68  $this->assertEquals( "Bar", $po->getExtensionData( "two" ) );
69  }
70 
77  public function testProperties() {
78  $po = new ParserOutput();
79 
80  $po->setProperty( 'foo', 'val' );
81 
82  $properties = $po->getProperties();
83  $this->assertEquals( $po->getProperty( 'foo' ), 'val' );
84  $this->assertEquals( $properties['foo'], 'val' );
85 
86  $po->setProperty( 'foo', 'second val' );
87 
88  $properties = $po->getProperties();
89  $this->assertEquals( $po->getProperty( 'foo' ), 'second val' );
90  $this->assertEquals( $properties['foo'], 'second val' );
91 
92  $po->unsetProperty( 'foo' );
93 
94  $properties = $po->getProperties();
95  $this->assertEquals( $po->getProperty( 'foo' ), false );
96  $this->assertArrayNotHasKey( 'foo', $properties );
97  }
98 
105  public function testWrapperDivClass() {
106  $po = new ParserOutput();
107 
108  $po->setText( 'Kittens' );
109  $this->assertContains( 'Kittens', $po->getText() );
110  $this->assertNotContains( '<div', $po->getText() );
111  $this->assertSame( 'Kittens', $po->getRawText() );
112 
113  $po->addWrapperDivClass( 'foo' );
114  $text = $po->getText();
115  $this->assertContains( 'Kittens', $text );
116  $this->assertContains( '<div', $text );
117  $this->assertContains( 'class="foo"', $text );
118 
119  $po->addWrapperDivClass( 'bar' );
120  $text = $po->getText();
121  $this->assertContains( 'Kittens', $text );
122  $this->assertContains( '<div', $text );
123  $this->assertContains( 'class="foo bar"', $text );
124 
125  $po->addWrapperDivClass( 'bar' ); // second time does nothing, no "foo bar bar".
126  $text = $po->getText( [ 'unwrap' => true ] );
127  $this->assertContains( 'Kittens', $text );
128  $this->assertNotContains( '<div', $text );
129  $this->assertNotContains( 'class="foo bar"', $text );
130 
131  $text = $po->getText( [ 'wrapperDivClass' => '' ] );
132  $this->assertContains( 'Kittens', $text );
133  $this->assertNotContains( '<div', $text );
134  $this->assertNotContains( 'class="foo bar"', $text );
135 
136  $text = $po->getText( [ 'wrapperDivClass' => 'xyzzy' ] );
137  $this->assertContains( 'Kittens', $text );
138  $this->assertContains( '<div', $text );
139  $this->assertContains( 'class="xyzzy"', $text );
140  $this->assertNotContains( 'class="foo bar"', $text );
141 
142  $text = $po->getRawText();
143  $this->assertSame( 'Kittens', $text );
144 
145  $po->clearWrapperDivClass();
146  $text = $po->getText();
147  $this->assertContains( 'Kittens', $text );
148  $this->assertNotContains( '<div', $text );
149  $this->assertNotContains( 'class="foo bar"', $text );
150  }
151 
159  public function testGetText( $options, $text, $expect ) {
160  $this->setMwGlobals( [
161  'wgArticlePath' => '/wiki/$1',
162  'wgScriptPath' => '/w',
163  'wgScript' => '/w/index.php',
164  ] );
165 
166  $po = new ParserOutput( $text );
167  $actual = $po->getText( $options );
168  $this->assertSame( $expect, $actual );
169  }
170 
171  public static function provideGetText() {
172  // phpcs:disable Generic.Files.LineLength
173  $text = <<<EOF
174 <p>Test document.
175 </p>
176 <mw:toc><div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div>
177 <ul>
178 <li class="toclevel-1 tocsection-1"><a href="#Section_1"><span class="tocnumber">1</span> <span class="toctext">Section 1</span></a></li>
179 <li class="toclevel-1 tocsection-2"><a href="#Section_2"><span class="tocnumber">2</span> <span class="toctext">Section 2</span></a>
180 <ul>
181 <li class="toclevel-2 tocsection-3"><a href="#Section_2.1"><span class="tocnumber">2.1</span> <span class="toctext">Section 2.1</span></a></li>
182 </ul>
183 </li>
184 <li class="toclevel-1 tocsection-4"><a href="#Section_3"><span class="tocnumber">3</span> <span class="toctext">Section 3</span></a></li>
185 </ul>
186 </div>
187 </mw:toc>
188 <h2><span class="mw-headline" id="Section_1">Section 1</span><mw:editsection page="Test Page" section="1">Section 1</mw:editsection></h2>
189 <p>One
190 </p>
191 <h2><span class="mw-headline" id="Section_2">Section 2</span><mw:editsection page="Test Page" section="2">Section 2</mw:editsection></h2>
192 <p>Two
193 </p>
194 <h3><span class="mw-headline" id="Section_2.1">Section 2.1</span><mw:editsection page="Test Page" section="3">Section 2.1</mw:editsection></h3>
195 <p>Two point one
196 </p>
197 <h2><span class="mw-headline" id="Section_3">Section 3</span><mw:editsection page="Test Page" section="4">Section 3</mw:editsection></h2>
198 <p>Three
199 </p>
200 EOF;
201 
202  $dedupText = <<<EOF
203 <p>This is a test document.</p>
204 <style data-mw-deduplicate="duplicate1">.Duplicate1 {}</style>
205 <style data-mw-deduplicate="duplicate1">.Duplicate1 {}</style>
206 <style data-mw-deduplicate="duplicate2">.Duplicate2 {}</style>
207 <style data-mw-deduplicate="duplicate1">.Duplicate1 {}</style>
208 <style data-mw-deduplicate="duplicate2">.Duplicate2 {}</style>
209 <style data-mw-not-deduplicate="duplicate1">.Duplicate1 {}</style>
210 <style data-mw-deduplicate="duplicate1">.Same-attribute-different-content {}</style>
211 <style data-mw-deduplicate="duplicate3">.Duplicate1 {}</style>
212 <style>.Duplicate1 {}</style>
213 EOF;
214 
215  return [
216  'No options' => [
217  [], $text, <<<EOF
218 <p>Test document.
219 </p>
220 <div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div>
221 <ul>
222 <li class="toclevel-1 tocsection-1"><a href="#Section_1"><span class="tocnumber">1</span> <span class="toctext">Section 1</span></a></li>
223 <li class="toclevel-1 tocsection-2"><a href="#Section_2"><span class="tocnumber">2</span> <span class="toctext">Section 2</span></a>
224 <ul>
225 <li class="toclevel-2 tocsection-3"><a href="#Section_2.1"><span class="tocnumber">2.1</span> <span class="toctext">Section 2.1</span></a></li>
226 </ul>
227 </li>
228 <li class="toclevel-1 tocsection-4"><a href="#Section_3"><span class="tocnumber">3</span> <span class="toctext">Section 3</span></a></li>
229 </ul>
230 </div>
231 
232 <h2><span class="mw-headline" id="Section_1">Section 1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=1" title="Edit section: Section 1">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
233 <p>One
234 </p>
235 <h2><span class="mw-headline" id="Section_2">Section 2</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=2" title="Edit section: Section 2">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
236 <p>Two
237 </p>
238 <h3><span class="mw-headline" id="Section_2.1">Section 2.1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=3" title="Edit section: Section 2.1">edit</a><span class="mw-editsection-bracket">]</span></span></h3>
239 <p>Two point one
240 </p>
241 <h2><span class="mw-headline" id="Section_3">Section 3</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=4" title="Edit section: Section 3">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
242 <p>Three
243 </p>
244 EOF
245  ],
246  'Disable section edit links' => [
247  [ 'enableSectionEditLinks' => false ], $text, <<<EOF
248 <p>Test document.
249 </p>
250 <div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div>
251 <ul>
252 <li class="toclevel-1 tocsection-1"><a href="#Section_1"><span class="tocnumber">1</span> <span class="toctext">Section 1</span></a></li>
253 <li class="toclevel-1 tocsection-2"><a href="#Section_2"><span class="tocnumber">2</span> <span class="toctext">Section 2</span></a>
254 <ul>
255 <li class="toclevel-2 tocsection-3"><a href="#Section_2.1"><span class="tocnumber">2.1</span> <span class="toctext">Section 2.1</span></a></li>
256 </ul>
257 </li>
258 <li class="toclevel-1 tocsection-4"><a href="#Section_3"><span class="tocnumber">3</span> <span class="toctext">Section 3</span></a></li>
259 </ul>
260 </div>
261 
262 <h2><span class="mw-headline" id="Section_1">Section 1</span></h2>
263 <p>One
264 </p>
265 <h2><span class="mw-headline" id="Section_2">Section 2</span></h2>
266 <p>Two
267 </p>
268 <h3><span class="mw-headline" id="Section_2.1">Section 2.1</span></h3>
269 <p>Two point one
270 </p>
271 <h2><span class="mw-headline" id="Section_3">Section 3</span></h2>
272 <p>Three
273 </p>
274 EOF
275  ],
276  'Disable TOC, but wrap' => [
277  [ 'allowTOC' => false, 'wrapperDivClass' => 'mw-parser-output' ], $text, <<<EOF
278 <div class="mw-parser-output"><p>Test document.
279 </p>
280 
281 <h2><span class="mw-headline" id="Section_1">Section 1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=1" title="Edit section: Section 1">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
282 <p>One
283 </p>
284 <h2><span class="mw-headline" id="Section_2">Section 2</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=2" title="Edit section: Section 2">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
285 <p>Two
286 </p>
287 <h3><span class="mw-headline" id="Section_2.1">Section 2.1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=3" title="Edit section: Section 2.1">edit</a><span class="mw-editsection-bracket">]</span></span></h3>
288 <p>Two point one
289 </p>
290 <h2><span class="mw-headline" id="Section_3">Section 3</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=4" title="Edit section: Section 3">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
291 <p>Three
292 </p></div>
293 EOF
294  ],
295  'Style deduplication' => [
296  [], $dedupText, <<<EOF
297 <p>This is a test document.</p>
298 <style data-mw-deduplicate="duplicate1">.Duplicate1 {}</style>
299 <link rel="mw-deduplicated-inline-style" href="mw-data:duplicate1"/>
300 <style data-mw-deduplicate="duplicate2">.Duplicate2 {}</style>
301 <link rel="mw-deduplicated-inline-style" href="mw-data:duplicate1"/>
302 <link rel="mw-deduplicated-inline-style" href="mw-data:duplicate2"/>
303 <style data-mw-not-deduplicate="duplicate1">.Duplicate1 {}</style>
304 <link rel="mw-deduplicated-inline-style" href="mw-data:duplicate1"/>
305 <style data-mw-deduplicate="duplicate3">.Duplicate1 {}</style>
306 <style>.Duplicate1 {}</style>
307 EOF
308  ],
309  'Style deduplication disabled' => [
310  [ 'deduplicateStyles' => false ], $dedupText, $dedupText
311  ],
312  ];
313  // phpcs:enable
314  }
315 
319  public function testHasText() {
320  $po = new ParserOutput();
321  $this->assertTrue( $po->hasText() );
322 
323  $po = new ParserOutput( null );
324  $this->assertFalse( $po->hasText() );
325 
326  $po = new ParserOutput( '' );
327  $this->assertTrue( $po->hasText() );
328 
329  $po = new ParserOutput( null );
330  $po->setText( '' );
331  $this->assertTrue( $po->hasText() );
332  }
333 
337  public function testGetText_failsIfNoText() {
338  $po = new ParserOutput( null );
339 
340  $this->setExpectedException( LogicException::class );
341  $po->getText();
342  }
343 
347  public function testGetRawText_failsIfNoText() {
348  $po = new ParserOutput( null );
349 
350  $this->setExpectedException( LogicException::class );
351  $po->getRawText();
352  }
353 
354  public function provideMergeHtmlMetaDataFrom() {
355  // title text ------------
356  $a = new ParserOutput();
357  $a->setTitleText( 'X' );
358  $b = new ParserOutput();
359  yield 'only left title text' => [ $a, $b, [ 'getTitleText' => 'X' ] ];
360 
361  $a = new ParserOutput();
362  $b = new ParserOutput();
363  $b->setTitleText( 'Y' );
364  yield 'only right title text' => [ $a, $b, [ 'getTitleText' => 'Y' ] ];
365 
366  $a = new ParserOutput();
367  $a->setTitleText( 'X' );
368  $b = new ParserOutput();
369  $b->setTitleText( 'Y' );
370  yield 'left title text wins' => [ $a, $b, [ 'getTitleText' => 'X' ] ];
371 
372  // index policy ------------
373  $a = new ParserOutput();
374  $a->setIndexPolicy( 'index' );
375  $b = new ParserOutput();
376  yield 'only left index policy' => [ $a, $b, [ 'getIndexPolicy' => 'index' ] ];
377 
378  $a = new ParserOutput();
379  $b = new ParserOutput();
380  $b->setIndexPolicy( 'index' );
381  yield 'only right index policy' => [ $a, $b, [ 'getIndexPolicy' => 'index' ] ];
382 
383  $a = new ParserOutput();
384  $a->setIndexPolicy( 'noindex' );
385  $b = new ParserOutput();
386  $b->setIndexPolicy( 'index' );
387  yield 'left noindex wins' => [ $a, $b, [ 'getIndexPolicy' => 'noindex' ] ];
388 
389  $a = new ParserOutput();
390  $a->setIndexPolicy( 'index' );
391  $b = new ParserOutput();
392  $b->setIndexPolicy( 'noindex' );
393  yield 'right noindex wins' => [ $a, $b, [ 'getIndexPolicy' => 'noindex' ] ];
394 
395  // head items and friends ------------
396  $a = new ParserOutput();
397  $a->addHeadItem( '<foo1>' );
398  $a->addHeadItem( '<bar1>', 'bar' );
399  $a->addModules( 'test-module-a' );
400  $a->addModuleScripts( 'test-module-script-a' );
401  $a->addModuleStyles( 'test-module-styles-a' );
402  $b->addJsConfigVars( 'test-config-var-a', 'a' );
403 
404  $b = new ParserOutput();
405  $b->setIndexPolicy( 'noindex' );
406  $b->addHeadItem( '<foo2>' );
407  $b->addHeadItem( '<bar2>', 'bar' );
408  $b->addModules( 'test-module-b' );
409  $b->addModuleScripts( 'test-module-script-b' );
410  $b->addModuleStyles( 'test-module-styles-b' );
411  $b->addJsConfigVars( 'test-config-var-b', 'b' );
412  $b->addJsConfigVars( 'test-config-var-a', 'X' );
413 
414  yield 'head items and friends' => [ $a, $b, [
415  'getHeadItems' => [
416  '<foo1>',
417  '<foo2>',
418  'bar' => '<bar2>', // overwritten
419  ],
420  'getModules' => [
421  'test-module-a',
422  'test-module-b',
423  ],
424  'getModuleScripts' => [
425  'test-module-script-a',
426  'test-module-script-b',
427  ],
428  'getModuleStyles' => [
429  'test-module-styles-a',
430  'test-module-styles-b',
431  ],
432  'getJsConfigVars' => [
433  'test-config-var-a' => 'X', // overwritten
434  'test-config-var-b' => 'b',
435  ],
436  ] ];
437 
438  // TOC ------------
439  $a = new ParserOutput();
440  $a->setTOCHTML( '<p>TOC A</p>' );
441  $a->setSections( [ [ 'fromtitle' => 'A1' ], [ 'fromtitle' => 'A2' ] ] );
442 
443  $b = new ParserOutput();
444  $b->setTOCHTML( '<p>TOC B</p>' );
445  $b->setSections( [ [ 'fromtitle' => 'B1' ], [ 'fromtitle' => 'B2' ] ] );
446 
447  yield 'concat TOC' => [ $a, $b, [
448  'getTOCHTML' => '<p>TOC A</p><p>TOC B</p>',
449  'getSections' => [
450  [ 'fromtitle' => 'A1' ],
451  [ 'fromtitle' => 'A2' ],
452  [ 'fromtitle' => 'B1' ],
453  [ 'fromtitle' => 'B2' ]
454  ],
455  ] ];
456 
457  // Skin Control ------------
458  $a = new ParserOutput();
459  $a->setNewSection( true );
460  $a->hideNewSection( true );
461  $a->setNoGallery( true );
462  $a->addWrapperDivClass( 'foo' );
463 
464  $a->setIndicator( 'foo', 'Foo!' );
465  $a->setIndicator( 'bar', 'Bar!' );
466 
467  $a->setExtensionData( 'foo', 'Foo!' );
468  $a->setExtensionData( 'bar', 'Bar!' );
469 
470  $b = new ParserOutput();
471  $b->setNoGallery( true );
472  $b->setEnableOOUI( true );
473  $b->preventClickjacking( true );
474  $a->addWrapperDivClass( 'bar' );
475 
476  $b->setIndicator( 'zoo', 'Zoo!' );
477  $b->setIndicator( 'bar', 'Barrr!' );
478 
479  $b->setExtensionData( 'zoo', 'Zoo!' );
480  $b->setExtensionData( 'bar', 'Barrr!' );
481 
482  yield 'skin control flags' => [ $a, $b, [
483  'getNewSection' => true,
484  'getHideNewSection' => true,
485  'getNoGallery' => true,
486  'getEnableOOUI' => true,
487  'preventClickjacking' => true,
488  'getIndicators' => [
489  'foo' => 'Foo!',
490  'bar' => 'Barrr!',
491  'zoo' => 'Zoo!',
492  ],
493  'getWrapperDivClass' => 'foo bar',
494  '$mExtensionData' => [
495  'foo' => 'Foo!',
496  'bar' => 'Barrr!',
497  'zoo' => 'Zoo!',
498  ],
499  ] ];
500  }
501 
510  public function testMergeHtmlMetaDataFrom( ParserOutput $a, ParserOutput $b, $expected ) {
511  $a->mergeHtmlMetaDataFrom( $b );
512 
513  $this->assertFieldValues( $a, $expected );
514 
515  // test twice, to make sure the operation is idempotent (except for the TOC, see below)
516  $a->mergeHtmlMetaDataFrom( $b );
517 
518  // XXX: TOC joining should get smarter. Can we make it idempotent as well?
519  unset( $expected['getTOCHTML'] );
520  unset( $expected['getSections'] );
521 
522  $this->assertFieldValues( $a, $expected );
523  }
524 
525  private function assertFieldValues( ParserOutput $po, $expected ) {
526  $po = TestingAccessWrapper::newFromObject( $po );
527 
528  foreach ( $expected as $method => $value ) {
529  if ( $method[0] === '$' ) {
530  $field = substr( $method, 1 );
531  $actual = $po->__get( $field );
532  } else {
533  $actual = $po->__call( $method, [] );
534  }
535 
536  $this->assertEquals( $value, $actual, $method );
537  }
538  }
539 
541  // links ------------
542  $a = new ParserOutput();
543  $a->addLink( Title::makeTitle( NS_MAIN, 'Kittens' ), 6 );
544  $a->addLink( Title::makeTitle( NS_TALK, 'Kittens' ), 16 );
545  $a->addLink( Title::makeTitle( NS_MAIN, 'Goats' ), 7 );
546 
547  $a->addTemplate( Title::makeTitle( NS_TEMPLATE, 'Goats' ), 107, 1107 );
548 
549  $a->addLanguageLink( 'de' );
550  $a->addLanguageLink( 'ru' );
551  $a->addInterwikiLink( Title::makeTitle( NS_MAIN, 'Kittens DE', '', 'de' ) );
552  $a->addInterwikiLink( Title::makeTitle( NS_MAIN, 'Kittens RU', '', 'ru' ) );
553  $a->addExternalLink( 'https://kittens.wikimedia.test' );
554  $a->addExternalLink( 'https://goats.wikimedia.test' );
555 
556  $a->addCategory( 'Foo', 'X' );
557  $a->addImage( 'Billy.jpg', '20180101000013', 'DEAD' );
558 
559  $b = new ParserOutput();
560  $b->addLink( Title::makeTitle( NS_MAIN, 'Goats' ), 7 );
561  $b->addLink( Title::makeTitle( NS_TALK, 'Goats' ), 17 );
562  $b->addLink( Title::makeTitle( NS_MAIN, 'Dragons' ), 8 );
563  $b->addLink( Title::makeTitle( NS_FILE, 'Dragons.jpg' ), 28 );
564 
565  $b->addTemplate( Title::makeTitle( NS_TEMPLATE, 'Dragons' ), 108, 1108 );
566  $a->addTemplate( Title::makeTitle( NS_MAIN, 'Dragons' ), 118, 1118 );
567 
568  $b->addLanguageLink( 'fr' );
569  $b->addLanguageLink( 'ru' );
570  $b->addInterwikiLink( Title::makeTitle( NS_MAIN, 'Kittens FR', '', 'fr' ) );
571  $b->addInterwikiLink( Title::makeTitle( NS_MAIN, 'Dragons RU', '', 'ru' ) );
572  $b->addExternalLink( 'https://dragons.wikimedia.test' );
573  $b->addExternalLink( 'https://goats.wikimedia.test' );
574 
575  $b->addCategory( 'Bar', 'Y' );
576  $b->addImage( 'Puff.jpg', '20180101000017', 'BEEF' );
577 
578  yield 'all kinds of links' => [ $a, $b, [
579  'getLinks' => [
580  NS_MAIN => [
581  'Kittens' => 6,
582  'Goats' => 7,
583  'Dragons' => 8,
584  ],
585  NS_TALK => [
586  'Kittens' => 16,
587  'Goats' => 17,
588  ],
589  NS_FILE => [
590  'Dragons.jpg' => 28,
591  ],
592  ],
593  'getTemplates' => [
594  NS_MAIN => [
595  'Dragons' => 118,
596  ],
597  NS_TEMPLATE => [
598  'Dragons' => 108,
599  'Goats' => 107,
600  ],
601  ],
602  'getTemplateIds' => [
603  NS_MAIN => [
604  'Dragons' => 1118,
605  ],
606  NS_TEMPLATE => [
607  'Dragons' => 1108,
608  'Goats' => 1107,
609  ],
610  ],
611  'getLanguageLinks' => [ 'de', 'ru', 'fr' ],
612  'getInterwikiLinks' => [
613  'de' => [ 'Kittens_DE' => 1 ],
614  'ru' => [ 'Kittens_RU' => 1, 'Dragons_RU' => 1, ],
615  'fr' => [ 'Kittens_FR' => 1 ],
616  ],
617  'getCategories' => [ 'Foo' => 'X', 'Bar' => 'Y' ],
618  'getImages' => [ 'Billy.jpg' => 1, 'Puff.jpg' => 1 ],
619  'getFileSearchOptions' => [
620  'Billy.jpg' => [ 'time' => '20180101000013', 'sha1' => 'DEAD' ],
621  'Puff.jpg' => [ 'time' => '20180101000017', 'sha1' => 'BEEF' ],
622  ],
623  'getExternalLinks' => [
624  'https://dragons.wikimedia.test' => 1,
625  'https://kittens.wikimedia.test' => 1,
626  'https://goats.wikimedia.test' => 1,
627  ]
628  ] ];
629 
630  // properties ------------
631  $a = new ParserOutput();
632 
633  $a->setProperty( 'foo', 'Foo!' );
634  $a->setProperty( 'bar', 'Bar!' );
635 
636  $a->setExtensionData( 'foo', 'Foo!' );
637  $a->setExtensionData( 'bar', 'Bar!' );
638 
639  $b = new ParserOutput();
640 
641  $b->setProperty( 'zoo', 'Zoo!' );
642  $b->setProperty( 'bar', 'Barrr!' );
643 
644  $b->setExtensionData( 'zoo', 'Zoo!' );
645  $b->setExtensionData( 'bar', 'Barrr!' );
646 
647  yield 'properties' => [ $a, $b, [
648  'getProperties' => [
649  'foo' => 'Foo!',
650  'bar' => 'Barrr!',
651  'zoo' => 'Zoo!',
652  ],
653  '$mExtensionData' => [
654  'foo' => 'Foo!',
655  'bar' => 'Barrr!',
656  'zoo' => 'Zoo!',
657  ],
658  ] ];
659  }
660 
669  public function testMergeTrackingMetaDataFrom( ParserOutput $a, ParserOutput $b, $expected ) {
670  $a->mergeTrackingMetaDataFrom( $b );
671 
672  $this->assertFieldValues( $a, $expected );
673 
674  // test twice, to make sure the operation is idempotent
675  $a->mergeTrackingMetaDataFrom( $b );
676 
677  $this->assertFieldValues( $a, $expected );
678  }
679 
681  // hooks
682  $a = new ParserOutput();
683 
684  $a->addOutputHook( 'foo', 'X' );
685  $a->addOutputHook( 'bar' );
686 
687  $b = new ParserOutput();
688 
689  $b->addOutputHook( 'foo', 'Y' );
690  $b->addOutputHook( 'bar' );
691  $b->addOutputHook( 'zoo' );
692 
693  yield 'hooks' => [ $a, $b, [
694  'getOutputHooks' => [
695  [ 'foo', 'X' ],
696  [ 'bar', false ],
697  [ 'foo', 'Y' ],
698  [ 'zoo', false ],
699  ],
700  ] ];
701 
702  // flags & co
703  $a = new ParserOutput();
704 
705  $a->addWarning( 'Oops' );
706  $a->addWarning( 'Whoops' );
707 
708  $a->setFlag( 'foo' );
709  $a->setFlag( 'bar' );
710 
711  $a->recordOption( 'Foo' );
712  $a->recordOption( 'Bar' );
713 
714  $b = new ParserOutput();
715 
716  $b->addWarning( 'Yikes' );
717  $b->addWarning( 'Whoops' );
718 
719  $b->setFlag( 'zoo' );
720  $b->setFlag( 'bar' );
721 
722  $b->recordOption( 'Zoo' );
723  $b->recordOption( 'Bar' );
724 
725  yield 'flags' => [ $a, $b, [
726  'getWarnings' => [ 'Oops', 'Whoops', 'Yikes' ],
727  '$mFlags' => [ 'foo' => true, 'bar' => true, 'zoo' => true ],
728  'getUsedOptions' => [ 'Foo', 'Bar', 'Zoo' ],
729  ] ];
730 
731  // timestamp ------------
732  $a = new ParserOutput();
733  $a->setTimestamp( '20180101000011' );
734  $b = new ParserOutput();
735  yield 'only left timestamp' => [ $a, $b, [ 'getTimestamp' => '20180101000011' ] ];
736 
737  $a = new ParserOutput();
738  $b = new ParserOutput();
739  $b->setTimestamp( '20180101000011' );
740  yield 'only right timestamp' => [ $a, $b, [ 'getTimestamp' => '20180101000011' ] ];
741 
742  $a = new ParserOutput();
743  $a->setTimestamp( '20180101000011' );
744  $b = new ParserOutput();
745  $b->setTimestamp( '20180101000001' );
746  yield 'left timestamp wins' => [ $a, $b, [ 'getTimestamp' => '20180101000011' ] ];
747 
748  $a = new ParserOutput();
749  $a->setTimestamp( '20180101000001' );
750  $b = new ParserOutput();
751  $b->setTimestamp( '20180101000011' );
752  yield 'right timestamp wins' => [ $a, $b, [ 'getTimestamp' => '20180101000011' ] ];
753 
754  // speculative rev id ------------
755  $a = new ParserOutput();
756  $a->setSpeculativeRevIdUsed( 9 );
757  $b = new ParserOutput();
758  yield 'only left speculative rev id' => [ $a, $b, [ 'getSpeculativeRevIdUsed' => 9 ] ];
759 
760  $a = new ParserOutput();
761  $b = new ParserOutput();
762  $b->setSpeculativeRevIdUsed( 9 );
763  yield 'only right speculative rev id' => [ $a, $b, [ 'getSpeculativeRevIdUsed' => 9 ] ];
764 
765  $a = new ParserOutput();
766  $a->setSpeculativeRevIdUsed( 9 );
767  $b = new ParserOutput();
768  $b->setSpeculativeRevIdUsed( 9 );
769  yield 'same speculative rev id' => [ $a, $b, [ 'getSpeculativeRevIdUsed' => 9 ] ];
770 
771  // limit report (recursive max) ------------
772  $a = new ParserOutput();
773 
774  $a->setLimitReportData( 'naive1', 7 );
775  $a->setLimitReportData( 'naive2', 27 );
776 
777  $a->setLimitReportData( 'limitreport-simple1', 7 );
778  $a->setLimitReportData( 'limitreport-simple2', 27 );
779 
780  $a->setLimitReportData( 'limitreport-pair1', [ 7, 9 ] );
781  $a->setLimitReportData( 'limitreport-pair2', [ 27, 29 ] );
782 
783  $a->setLimitReportData( 'limitreport-more1', [ 7, 9, 1 ] );
784  $a->setLimitReportData( 'limitreport-more2', [ 27, 29, 21 ] );
785 
786  $a->setLimitReportData( 'limitreport-only-a', 13 );
787 
788  $b = new ParserOutput();
789 
790  $b->setLimitReportData( 'naive1', 17 );
791  $b->setLimitReportData( 'naive2', 17 );
792 
793  $b->setLimitReportData( 'limitreport-simple1', 17 );
794  $b->setLimitReportData( 'limitreport-simple2', 17 );
795 
796  $b->setLimitReportData( 'limitreport-pair1', [ 17, 19 ] );
797  $b->setLimitReportData( 'limitreport-pair2', [ 17, 19 ] );
798 
799  $b->setLimitReportData( 'limitreport-more1', [ 17, 19, 11 ] );
800  $b->setLimitReportData( 'limitreport-more2', [ 17, 19, 11 ] );
801 
802  $b->setLimitReportData( 'limitreport-only-b', 23 );
803 
804  // first write wins
805  yield 'limit report' => [ $a, $b, [
806  'getLimitReportData' => [
807  'naive1' => 7,
808  'naive2' => 27,
809  'limitreport-simple1' => 7,
810  'limitreport-simple2' => 27,
811  'limitreport-pair1' => [ 7, 9 ],
812  'limitreport-pair2' => [ 27, 29 ],
813  'limitreport-more1' => [ 7, 9, 1 ],
814  'limitreport-more2' => [ 27, 29, 21 ],
815  'limitreport-only-a' => 13,
816  ],
817  'getLimitReportJSData' => [
818  'naive1' => 7,
819  'naive2' => 27,
820  'limitreport' => [
821  'simple1' => 7,
822  'simple2' => 27,
823  'pair1' => [ 'value' => 7, 'limit' => 9 ],
824  'pair2' => [ 'value' => 27, 'limit' => 29 ],
825  'more1' => [ 7, 9, 1 ],
826  'more2' => [ 27, 29, 21 ],
827  'only-a' => 13,
828  ],
829  ],
830  ] ];
831  }
832 
841  public function testMergeInternalMetaDataFrom( ParserOutput $a, ParserOutput $b, $expected ) {
842  $a->mergeInternalMetaDataFrom( $b );
843 
844  $this->assertFieldValues( $a, $expected );
845 
846  // test twice, to make sure the operation is idempotent
847  $a->mergeInternalMetaDataFrom( $b );
848 
849  $this->assertFieldValues( $a, $expected );
850  }
851 
854  $a = new ParserOutput();
855  $a = TestingAccessWrapper::newFromObject( $a );
856 
857  $a->resetParseStartTime();
858  $aClocks = $a->mParseStartTime;
859 
860  $b = new ParserOutput();
861 
862  $a->mergeInternalMetaDataFrom( $b );
863  $mergedClocks = $a->mParseStartTime;
864 
865  foreach ( $mergedClocks as $clock => $timestamp ) {
866  $this->assertSame( $aClocks[$clock], $timestamp, $clock );
867  }
868 
869  // try again, with times in $b also set, and later than $a's
870  usleep( 1234 );
871 
873  $b = new ParserOutput();
874  $b = TestingAccessWrapper::newFromObject( $b );
875 
876  $b->resetParseStartTime();
877 
878  $bClocks = $b->mParseStartTime;
879 
880  $a->mergeInternalMetaDataFrom( $b->object, 'b' );
881  $mergedClocks = $a->mParseStartTime;
882 
883  foreach ( $mergedClocks as $clock => $timestamp ) {
884  $this->assertSame( $aClocks[$clock], $timestamp, $clock );
885  $this->assertLessThanOrEqual( $bClocks[$clock], $timestamp, $clock );
886  }
887 
888  // try again, with $a's times being later
889  usleep( 1234 );
890  $a->resetParseStartTime();
891  $aClocks = $a->mParseStartTime;
892 
893  $a->mergeInternalMetaDataFrom( $b->object, 'b' );
894  $mergedClocks = $a->mParseStartTime;
895 
896  foreach ( $mergedClocks as $clock => $timestamp ) {
897  $this->assertSame( $bClocks[$clock], $timestamp, $clock );
898  $this->assertLessThanOrEqual( $aClocks[$clock], $timestamp, $clock );
899  }
900 
901  // try again, with no times in $a set
902  $a = new ParserOutput();
903  $a = TestingAccessWrapper::newFromObject( $a );
904 
905  $a->mergeInternalMetaDataFrom( $b->object, 'b' );
906  $mergedClocks = $a->mParseStartTime;
907 
908  foreach ( $mergedClocks as $clock => $timestamp ) {
909  $this->assertSame( $bClocks[$clock], $timestamp, $clock );
910  }
911  }
912 
917  public function testGetCacheTime() {
918  $clock = MWTimestamp::convert( TS_UNIX, '20100101000000' );
919  MWTimestamp::setFakeTime( function () use ( &$clock ) {
920  return $clock++;
921  } );
922 
923  $po = new ParserOutput();
924  $time = $po->getCacheTime();
925 
926  // Use current (fake) time per default. Ignore the last digit.
927  // Subsequent calls must yield the exact same timestamp as the first.
928  $this->assertStringStartsWith( '2010010100000', $time );
929  $this->assertSame( $time, $po->getCacheTime() );
930 
931  // After setting, the getter must return the time that was set.
932  $time = '20110606112233';
933  $po->setCacheTime( $time );
934  $this->assertSame( $time, $po->getCacheTime() );
935 
936  // support -1 as a marker for "not cacheable"
937  $time = -1;
938  $po->setCacheTime( $time );
939  $this->assertSame( $time, $po->getCacheTime() );
940  }
941 
942 }
ParserOutputTest\testWrapperDivClass
testWrapperDivClass()
ParserOutput::getWrapperDivClass ParserOutput::addWrapperDivClass ParserOutput::clearWrapperDivClass ...
Definition: ParserOutputTest.php:105
ParserOutput
Definition: ParserOutput.php:25
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:187
content
per default it will return the text for text based content
Definition: contenthandler.txt:104
ParserOutputTest\testMergeInternalMetaDataFrom_parseStartTime
testMergeInternalMetaDataFrom_parseStartTime()
Definition: ParserOutputTest.php:852
ParserOutputTest\provideMergeInternalMetaDataFrom
provideMergeInternalMetaDataFrom()
Definition: ParserOutputTest.php:680
ParserOutputTest\provideMergeHtmlMetaDataFrom
provideMergeHtmlMetaDataFrom()
Definition: ParserOutputTest.php:354
ParserOutput\mergeTrackingMetaDataFrom
mergeTrackingMetaDataFrom(ParserOutput $source)
Merges dependency tracking metadata such as backlinks, images used, and extension data from $source i...
Definition: ParserOutput.php:1399
NS_FILE
const NS_FILE
Definition: Defines.php:70
NS_TEMPLATE
const NS_TEMPLATE
Definition: Defines.php:74
link
usually copyright or history_copyright This message must be in HTML not wikitext if the section is included from a template to be included in the link
Definition: hooks.txt:3090
a
</source > ! result< div class="mw-highlight mw-content-ltr" dir="ltr">< pre >< span ></span >< span class="kd"> var</span >< span class="nx"> a</span >< span class="p"></span ></pre ></div > ! end ! test Multiline< source/> in lists !input *< source > a b</source > *foo< source > a b</source > ! html< ul >< li >< div class="mw-highlight mw-content-ltr" dir="ltr">< pre > a b</pre ></div ></li ></ul >< ul >< li > foo< div class="mw-highlight mw-content-ltr" dir="ltr">< pre > a b</pre ></div ></li ></ul > ! html tidy< ul >< li >< div class="mw-highlight mw-content-ltr" dir="ltr">< pre > a b</pre ></div ></li ></ul >< ul >< li > foo< div class="mw-highlight mw-content-ltr" dir="ltr">< pre > a b</pre ></div ></li ></ul > ! end ! test Custom attributes !input< source lang="javascript" id="foo" class="bar" dir="rtl" style="font-size: larger;"> var a
Definition: parserTests.txt:89
page
target page
Definition: All_system_messages.txt:1267
ParserOutputTest\testProperties
testProperties()
ParserOutput::setProperty ParserOutput::getProperty ParserOutput::unsetProperty ParserOutput::getProp...
Definition: ParserOutputTest.php:77
data
and how to run hooks for an and one after Each event has a preferably in CamelCase For ArticleDelete hook A clump of code and data that should be run when an event happens This can be either a function and a chunk of data
Definition: hooks.txt:6
is
This document provides an overview of the usage of PageUpdater and that is
Definition: pageupdater.txt:3
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
ParserOutputTest\testMergeInternalMetaDataFrom
testMergeInternalMetaDataFrom(ParserOutput $a, ParserOutput $b, $expected)
provideMergeInternalMetaDataFrom ParserOutput::mergeInternalMetaDataFrom
Definition: ParserOutputTest.php:841
NS_MAIN
const NS_MAIN
Definition: Defines.php:64
ParserOutputTest\testGetCacheTime
testGetCacheTime()
ParserOutput::getCacheTime ParserOutput::setCacheTime.
Definition: ParserOutputTest.php:917
ParserOutput\isLinkInternal
static isLinkInternal( $internal, $url)
Checks, if a url is pointing to the own server.
Definition: ParserOutput.php:689
MediaWikiTestCase\setMwGlobals
setMwGlobals( $pairs, $value=null)
Sets a global, maintaining a stashed version of the previous global to be restored in tearDown.
Definition: MediaWikiTestCase.php:706
ParserOutput\mergeHtmlMetaDataFrom
mergeHtmlMetaDataFrom(ParserOutput $source)
Merges HTML metadata such as head items, JS config vars, and HTTP cache control info from $source int...
Definition: ParserOutput.php:1341
not
if not
Definition: COPYING.txt:307
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
div
div
Definition: parserTests.txt:6868
$time
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition: hooks.txt:1841
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:545
ParserOutput\mergeInternalMetaDataFrom
mergeInternalMetaDataFrom(ParserOutput $source)
Merges internal metadata such as flags, accessed options, and profiling info from $source into this P...
Definition: ParserOutput.php:1300
ParserOutputTest\testGetText
testGetText( $options, $text, $expect)
ParserOutput::getText provideGetText.
Definition: ParserOutputTest.php:159
ParserOutputTest\testGetText_failsIfNoText
testGetText_failsIfNoText()
ParserOutput::getText.
Definition: ParserOutputTest.php:337
ParserOutputTest\provideGetText
static provideGetText()
Definition: ParserOutputTest.php:171
ParserOutputTest
Database ^— trigger DB shadowing because we are using Title magic.
Definition: ParserOutputTest.php:8
$value
$value
Definition: styleTest.css.php:49
ParserOutputTest\testMergeTrackingMetaDataFrom
testMergeTrackingMetaDataFrom(ParserOutput $a, ParserOutput $b, $expected)
provideMergeTrackingMetaDataFrom ParserOutput::mergeTrackingMetaDataFrom
Definition: ParserOutputTest.php:669
title
title
Definition: parserTests.txt:239
ParserOutputTest\provideIsLinkInternal
static provideIsLinkInternal()
Definition: ParserOutputTest.php:10
MediaWikiLangTestCase
Base class that store and restore the Language objects.
Definition: MediaWikiLangTestCase.php:8
ParserOutputTest\testHasText
testHasText()
ParserOutput::hasText.
Definition: ParserOutputTest.php:319
ParserOutputTest\tearDown
tearDown()
Definition: ParserOutputTest.php:35
ParserOutputTest\testIsLinkInternal
testIsLinkInternal( $shouldMatch, $server, $url)
Test to make sure ParserOutput::isLinkInternal behaves properly provideIsLinkInternal ParserOutput::i...
Definition: ParserOutputTest.php:46
captcha-old.p
p
Definition: captcha-old.py:275
style
Bar style
Definition: parserTests.txt:212
different
it is up to the author donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License If the distribution and or use of the Program is restricted in certain countries either by patents or by copyrighted the original copyright holder who places the Program under this License may add an geographical distribution limitation excluding those so that distribution is permitted only in or among countries not thus excluded In such this License incorporates the limitation as if written in the body of this License The Free Software Foundation may publish revised and or new versions of the General Public License from time to time Such new versions will be similar in spirit to the present but may differ in detail to address new problems or concerns Each version is given a distinguishing version number If the Program specifies a version number of this License which applies to it and any later you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation If the Program does not specify a version number of this you may choose any version ever published by the Free Software Foundation If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different
Definition: COPYING.txt:230
ParserOutputTest\provideMergeTrackingMetaDataFrom
provideMergeTrackingMetaDataFrom()
Definition: ParserOutputTest.php:540
$options
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:2036
ParserOutputTest\testMergeHtmlMetaDataFrom
testMergeHtmlMetaDataFrom(ParserOutput $a, ParserOutput $b, $expected)
provideMergeHtmlMetaDataFrom ParserOutput::mergeHtmlMetaDataFrom
Definition: ParserOutputTest.php:510
document
GNU GENERAL PUBLIC LICENSE June Free Software Franklin Fifth MA USA Everyone is permitted to copy and distribute verbatim copies of this license document
Definition: COPYING.txt:4
Test
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
true
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return true
Definition: hooks.txt:2036
NS_TALK
const NS_TALK
Definition: Defines.php:65
ParserOutputTest\testExtensionData
testExtensionData()
ParserOutput::setExtensionData ParserOutput::getExtensionData.
Definition: ParserOutputTest.php:54
attribute
</source > ! result< div id="foo" class="bar mw-highlight mw-content-rtl" dir="rtl" style="font-size: larger;">< pre >< span ></span >< span class="kd"> var</span >< span class="nx"> a</span >< span class="p"></span ></pre ></div > ! end ! test Inline attribute(inline code) !!input Text< source lang
class
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
ParserOutputTest\testGetRawText_failsIfNoText
testGetRawText_failsIfNoText()
ParserOutput::getRawText.
Definition: ParserOutputTest.php:347
href
shown</td >< td > a href
Definition: All_system_messages.txt:2667
ParserOutputTest\assertFieldValues
assertFieldValues(ParserOutput $po, $expected)
Definition: ParserOutputTest.php:525