]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - tests/phpunit/includes/MessageTest.php
MediaWiki 1.30.2-scripts
[autoinstallsdev/mediawiki.git] / tests / phpunit / includes / MessageTest.php
1 <?php
2
3 use Wikimedia\TestingAccessWrapper;
4
5 class MessageTest extends MediaWikiLangTestCase {
6
7         protected function setUp() {
8                 parent::setUp();
9
10                 $this->setMwGlobals( [
11                         'wgForceUIMsgAsContentMsg' => [],
12                 ] );
13                 $this->setUserLang( 'en' );
14         }
15
16         /**
17          * @covers Message::__construct
18          * @dataProvider provideConstructor
19          */
20         public function testConstructor( $expectedLang, $key, $params, $language ) {
21                 $message = new Message( $key, $params, $language );
22
23                 $this->assertSame( $key, $message->getKey() );
24                 $this->assertSame( $params, $message->getParams() );
25                 $this->assertEquals( $expectedLang, $message->getLanguage() );
26
27                 $messageSpecifier = $this->getMockForAbstractClass( 'MessageSpecifier' );
28                 $messageSpecifier->expects( $this->any() )
29                         ->method( 'getKey' )->will( $this->returnValue( $key ) );
30                 $messageSpecifier->expects( $this->any() )
31                         ->method( 'getParams' )->will( $this->returnValue( $params ) );
32                 $message = new Message( $messageSpecifier, [], $language );
33
34                 $this->assertSame( $key, $message->getKey() );
35                 $this->assertSame( $params, $message->getParams() );
36                 $this->assertEquals( $expectedLang, $message->getLanguage() );
37         }
38
39         public static function provideConstructor() {
40                 $langDe = Language::factory( 'de' );
41                 $langEn = Language::factory( 'en' );
42
43                 return [
44                         [ $langDe, 'foo', [], $langDe ],
45                         [ $langDe, 'foo', [ 'bar' ], $langDe ],
46                         [ $langEn, 'foo', [ 'bar' ], null ]
47                 ];
48         }
49
50         public static function provideConstructorParams() {
51                 return [
52                         [
53                                 [],
54                                 [],
55                         ],
56                         [
57                                 [],
58                                 [ [] ],
59                         ],
60                         [
61                                 [ 'foo' ],
62                                 [ 'foo' ],
63                         ],
64                         [
65                                 [ 'foo', 'bar' ],
66                                 [ 'foo', 'bar' ],
67                         ],
68                         [
69                                 [ 'baz' ],
70                                 [ [ 'baz' ] ],
71                         ],
72                         [
73                                 [ 'baz', 'foo' ],
74                                 [ [ 'baz', 'foo' ] ],
75                         ],
76                         [
77                                 [ Message::rawParam( 'baz' ) ],
78                                 [ Message::rawParam( 'baz' ) ],
79                         ],
80                         [
81                                 [ Message::rawParam( 'baz' ), 'foo' ],
82                                 [ Message::rawParam( 'baz' ), 'foo' ],
83                         ],
84                         [
85                                 [ Message::rawParam( 'baz' ) ],
86                                 [ [ Message::rawParam( 'baz' ) ] ],
87                         ],
88                         [
89                                 [ Message::rawParam( 'baz' ), 'foo' ],
90                                 [ [ Message::rawParam( 'baz' ), 'foo' ] ],
91                         ],
92
93                         // Test handling of erroneous input, to detect if it changes
94                         [
95                                 [ [ 'baz', 'foo' ], 'hhh' ],
96                                 [ [ 'baz', 'foo' ], 'hhh' ],
97                         ],
98                         [
99                                 [ [ 'baz', 'foo' ], 'hhh', [ 'ahahahahha' ] ],
100                                 [ [ 'baz', 'foo' ], 'hhh', [ 'ahahahahha' ] ],
101                         ],
102                         [
103                                 [ [ 'baz', 'foo' ], [ 'ahahahahha' ] ],
104                                 [ [ 'baz', 'foo' ], [ 'ahahahahha' ] ],
105                         ],
106                         [
107                                 [ [ 'baz' ], [ 'ahahahahha' ] ],
108                                 [ [ 'baz' ], [ 'ahahahahha' ] ],
109                         ],
110                 ];
111         }
112
113         /**
114          * @covers Message::__construct
115          * @covers Message::getParams
116          * @dataProvider provideConstructorParams
117          */
118         public function testConstructorParams( $expected, $args ) {
119                 $msg = new Message( 'imasomething' );
120
121                 $returned = call_user_func_array( [ $msg, 'params' ], $args );
122
123                 $this->assertSame( $msg, $returned );
124                 $this->assertSame( $expected, $msg->getParams() );
125         }
126
127         public static function provideConstructorLanguage() {
128                 return [
129                         [ 'foo', [ 'bar' ], 'en' ],
130                         [ 'foo', [ 'bar' ], 'de' ]
131                 ];
132         }
133
134         /**
135          * @covers Message::__construct
136          * @covers Message::getLanguage
137          * @dataProvider provideConstructorLanguage
138          */
139         public function testConstructorLanguage( $key, $params, $languageCode ) {
140                 $language = Language::factory( $languageCode );
141                 $message = new Message( $key, $params, $language );
142
143                 $this->assertEquals( $language, $message->getLanguage() );
144         }
145
146         public static function provideKeys() {
147                 return [
148                         'string' => [
149                                 'key' => 'mainpage',
150                                 'expected' => [ 'mainpage' ],
151                         ],
152                         'single' => [
153                                 'key' => [ 'mainpage' ],
154                                 'expected' => [ 'mainpage' ],
155                         ],
156                         'multi' => [
157                                 'key' => [ 'mainpage-foo', 'mainpage-bar', 'mainpage' ],
158                                 'expected' => [ 'mainpage-foo', 'mainpage-bar', 'mainpage' ],
159                         ],
160                         'empty' => [
161                                 'key' => [],
162                                 'expected' => null,
163                                 'exception' => 'InvalidArgumentException',
164                         ],
165                         'null' => [
166                                 'key' => null,
167                                 'expected' => null,
168                                 'exception' => 'InvalidArgumentException',
169                         ],
170                         'bad type' => [
171                                 'key' => 123,
172                                 'expected' => null,
173                                 'exception' => 'InvalidArgumentException',
174                         ],
175                 ];
176         }
177
178         /**
179          * @covers Message::__construct
180          * @covers Message::getKey
181          * @covers Message::isMultiKey
182          * @covers Message::getKeysToTry
183          * @dataProvider provideKeys
184          */
185         public function testKeys( $key, $expected, $exception = null ) {
186                 if ( $exception ) {
187                         $this->setExpectedException( $exception );
188                 }
189
190                 $msg = new Message( $key );
191                 $this->assertContains( $msg->getKey(), $expected );
192                 $this->assertSame( $expected, $msg->getKeysToTry() );
193                 $this->assertSame( count( $expected ) > 1, $msg->isMultiKey() );
194         }
195
196         /**
197          * @covers ::wfMessage
198          */
199         public function testWfMessage() {
200                 $this->assertInstanceOf( 'Message', wfMessage( 'mainpage' ) );
201                 $this->assertInstanceOf( 'Message', wfMessage( 'i-dont-exist-evar' ) );
202         }
203
204         /**
205          * @covers Message::newFromKey
206          */
207         public function testNewFromKey() {
208                 $this->assertInstanceOf( 'Message', Message::newFromKey( 'mainpage' ) );
209                 $this->assertInstanceOf( 'Message', Message::newFromKey( 'i-dont-exist-evar' ) );
210         }
211
212         /**
213          * @covers ::wfMessage
214          * @covers Message::__construct
215          */
216         public function testWfMessageParams() {
217                 $this->assertSame( 'Return to $1.', wfMessage( 'returnto' )->text() );
218                 $this->assertSame( 'Return to $1.', wfMessage( 'returnto', [] )->text() );
219                 $this->assertSame(
220                         'Return to 1,024.',
221                         wfMessage( 'returnto', Message::numParam( 1024 ) )->text()
222                 );
223                 $this->assertSame(
224                         'Return to 1,024.',
225                         wfMessage( 'returnto', [ Message::numParam( 1024 ) ] )->text()
226                 );
227                 $this->assertSame(
228                         'You have foo (bar).',
229                         wfMessage( 'youhavenewmessages', 'foo', 'bar' )->text()
230                 );
231                 $this->assertSame(
232                         'You have foo (bar).',
233                         wfMessage( 'youhavenewmessages', [ 'foo', 'bar' ] )->text()
234                 );
235                 $this->assertSame(
236                         'You have 1,024 (bar).',
237                         wfMessage(
238                                 'youhavenewmessages',
239                                 Message::numParam( 1024 ), 'bar'
240                         )->text()
241                 );
242                 $this->assertSame(
243                         'You have foo (2,048).',
244                         wfMessage(
245                                 'youhavenewmessages',
246                                 'foo', Message::numParam( 2048 )
247                         )->text()
248                 );
249                 $this->assertSame(
250                         'You have 1,024 (2,048).',
251                         wfMessage(
252                                 'youhavenewmessages',
253                                 [ Message::numParam( 1024 ), Message::numParam( 2048 ) ]
254                         )->text()
255                 );
256         }
257
258         /**
259          * @covers Message::exists
260          */
261         public function testExists() {
262                 $this->assertTrue( wfMessage( 'mainpage' )->exists() );
263                 $this->assertTrue( wfMessage( 'mainpage' )->params( [] )->exists() );
264                 $this->assertTrue( wfMessage( 'mainpage' )->rawParams( 'foo', 123 )->exists() );
265                 $this->assertFalse( wfMessage( 'i-dont-exist-evar' )->exists() );
266                 $this->assertFalse( wfMessage( 'i-dont-exist-evar' )->params( [] )->exists() );
267                 $this->assertFalse( wfMessage( 'i-dont-exist-evar' )->rawParams( 'foo', 123 )->exists() );
268         }
269
270         /**
271          * @covers Message::__construct
272          * @covers Message::text
273          * @covers Message::plain
274          * @covers Message::escaped
275          * @covers Message::toString
276          */
277         public function testToStringKey() {
278                 $this->assertSame( 'Main Page', wfMessage( 'mainpage' )->text() );
279                 $this->assertSame( '⧼i-dont-exist-evar⧽', wfMessage( 'i-dont-exist-evar' )->text() );
280                 $this->assertSame( '⧼i&lt;dont&gt;exist-evar⧽', wfMessage( 'i<dont>exist-evar' )->text() );
281                 $this->assertSame( '⧼i-dont-exist-evar⧽', wfMessage( 'i-dont-exist-evar' )->plain() );
282                 $this->assertSame( '⧼i&lt;dont&gt;exist-evar⧽', wfMessage( 'i<dont>exist-evar' )->plain() );
283                 $this->assertSame( '⧼i-dont-exist-evar⧽', wfMessage( 'i-dont-exist-evar' )->escaped() );
284                 $this->assertSame(
285                         '⧼i&lt;dont&gt;exist-evar⧽',
286                         wfMessage( 'i<dont>exist-evar' )->escaped()
287                 );
288         }
289
290         public static function provideToString() {
291                 return [
292                         // key, transformation, transformed, transformed implicitly
293                         [ 'mainpage', 'plain', 'Main Page', 'Main Page' ],
294                         [ 'i-dont-exist-evar', 'plain', '⧼i-dont-exist-evar⧽', '⧼i-dont-exist-evar⧽' ],
295                         [ 'i-dont-exist-evar', 'escaped', '⧼i-dont-exist-evar⧽', '⧼i-dont-exist-evar⧽' ],
296                         [ 'script>alert(1)</script', 'escaped', '⧼script&gt;alert(1)&lt;/script⧽',
297                                 '⧼script&gt;alert(1)&lt;/script⧽' ],
298                         [ 'script>alert(1)</script', 'plain', '⧼script&gt;alert(1)&lt;/script⧽',
299                                 '⧼script&gt;alert(1)&lt;/script⧽' ],
300                 ];
301         }
302
303         /**
304          * @covers Message::toString
305          * @covers Message::__toString
306          * @dataProvider provideToString
307          */
308         public function testToString( $key, $format, $expect, $expectImplicit ) {
309                 $msg = new Message( $key );
310                 $this->assertSame( $expect, $msg->$format() );
311                 $this->assertSame( $expect, $msg->toString(), 'toString is unaffected by previous call' );
312                 $this->assertSame( $expectImplicit, $msg->__toString() );
313                 $this->assertSame( $expect, $msg->toString(), 'toString is unaffected by __toString' );
314         }
315
316         public static function provideToString_raw() {
317                 return [
318                         [ '<span>foo</span>', 'parse', '<span>foo</span>', '<span>foo</span>' ],
319                         [ '<span>foo</span>', 'escaped', '&lt;span&gt;foo&lt;/span&gt;',
320                                 '<span>foo</span>' ],
321                         [ '<span>foo</span>', 'plain', '<span>foo</span>', '<span>foo</span>' ],
322                         [ '<script>alert(1)</script>', 'parse', '&lt;script&gt;alert(1)&lt;/script&gt;',
323                                 '&lt;script&gt;alert(1)&lt;/script&gt;' ],
324                         [ '<script>alert(1)</script>', 'escaped', '&lt;script&gt;alert(1)&lt;/script&gt;',
325                                 '&lt;script&gt;alert(1)&lt;/script&gt;' ],
326                         [ '<script>alert(1)</script>', 'plain', '<script>alert(1)</script>',
327                                 '&lt;script&gt;alert(1)&lt;/script&gt;' ],
328                 ];
329         }
330
331         /**
332          * @covers Message::toString
333          * @covers Message::__toString
334          * @dataProvider provideToString_raw
335          */
336         public function testToString_raw( $message, $format, $expect, $expectImplicit ) {
337                 // make the message behave like RawMessage and use the key as-is
338                 $msg = $this->getMockBuilder( Message::class )->setMethods( [ 'fetchMessage' ] )
339                         ->disableOriginalConstructor()
340                         ->getMock();
341                 $msg->expects( $this->any() )->method( 'fetchMessage' )->willReturn( $message );
342                 /** @var Message $msg */
343                 $this->assertSame( $expect, $msg->$format() );
344                 $this->assertSame( $expect, $msg->toString(), 'toString is unaffected by previous call' );
345                 $this->assertSame( $expectImplicit, $msg->__toString() );
346                 $this->assertSame( $expect, $msg->toString(), 'toString is unaffected by __toString' );
347         }
348
349         /**
350          * @covers Message::inLanguage
351          */
352         public function testInLanguage() {
353                 $this->assertSame( 'Main Page', wfMessage( 'mainpage' )->inLanguage( 'en' )->text() );
354                 $this->assertSame( 'Заглавная страница',
355                         wfMessage( 'mainpage' )->inLanguage( 'ru' )->text() );
356
357                 // NOTE: make sure internal caching of the message text is reset appropriately
358                 $msg = wfMessage( 'mainpage' );
359                 $this->assertSame( 'Main Page', $msg->inLanguage( Language::factory( 'en' ) )->text() );
360                 $this->assertSame(
361                         'Заглавная страница',
362                         $msg->inLanguage( Language::factory( 'ru' ) )->text()
363                 );
364         }
365
366         /**
367          * @covers Message::rawParam
368          * @covers Message::rawParams
369          */
370         public function testRawParams() {
371                 $this->assertSame(
372                         '(Заглавная страница)',
373                         wfMessage( 'parentheses', 'Заглавная страница' )->plain()
374                 );
375                 $this->assertSame(
376                         '(Заглавная страница $1)',
377                         wfMessage( 'parentheses', 'Заглавная страница $1' )->plain()
378                 );
379                 $this->assertSame(
380                         '(Заглавная страница)',
381                         wfMessage( 'parentheses' )->rawParams( 'Заглавная страница' )->plain()
382                 );
383                 $this->assertSame(
384                         '(Заглавная страница $1)',
385                         wfMessage( 'parentheses' )->rawParams( 'Заглавная страница $1' )->plain()
386                 );
387         }
388
389         /**
390          * @covers RawMessage::__construct
391          * @covers RawMessage::fetchMessage
392          */
393         public function testRawMessage() {
394                 $msg = new RawMessage( 'example &' );
395                 $this->assertSame( 'example &', $msg->plain() );
396                 $this->assertSame( 'example &amp;', $msg->escaped() );
397         }
398
399         public function testRawHtmlInMsg() {
400                 global $wgParserConf;
401                 $this->setMwGlobals( 'wgRawHtml', true );
402                 // We have to reset the core hook registration.
403                 // to register the html hook
404                 MessageCache::destroyInstance();
405                 $this->setMwGlobals( 'wgParser',
406                         ObjectFactory::constructClassInstance( $wgParserConf['class'], [ $wgParserConf ] )
407                 );
408
409                 $msg = new RawMessage( '<html><script>alert("xss")</script></html>' );
410                 $txt = '<span class="error">&lt;html&gt; tags cannot be' .
411                         ' used outside of normal pages.</span>';
412                 $this->assertSame( $txt, $msg->parse() );
413         }
414
415         /**
416          * @covers Message::params
417          * @covers Message::toString
418          * @covers Message::replaceParameters
419          */
420         public function testReplaceManyParams() {
421                 $msg = new RawMessage( '$1$2$3$4$5$6$7$8$9$10$11$12' );
422                 // One less than above has placeholders
423                 $params = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k' ];
424                 $this->assertSame(
425                         'abcdefghijka2',
426                         $msg->params( $params )->plain(),
427                         'Params > 9 are replaced correctly'
428                 );
429
430                 $msg = new RawMessage( 'Params$*' );
431                 $params = [ 'ab', 'bc', 'cd' ];
432                 $this->assertSame(
433                         'Params: ab, bc, cd',
434                         $msg->params( $params )->text()
435                 );
436         }
437
438         /**
439          * @covers Message::numParam
440          * @covers Message::numParams
441          */
442         public function testNumParams() {
443                 $lang = Language::factory( 'en' );
444                 $msg = new RawMessage( '$1' );
445
446                 $this->assertSame(
447                         $lang->formatNum( 123456.789 ),
448                         $msg->inLanguage( $lang )->numParams( 123456.789 )->plain(),
449                         'numParams is handled correctly'
450                 );
451         }
452
453         /**
454          * @covers Message::durationParam
455          * @covers Message::durationParams
456          */
457         public function testDurationParams() {
458                 $lang = Language::factory( 'en' );
459                 $msg = new RawMessage( '$1' );
460
461                 $this->assertSame(
462                         $lang->formatDuration( 1234 ),
463                         $msg->inLanguage( $lang )->durationParams( 1234 )->plain(),
464                         'durationParams is handled correctly'
465                 );
466         }
467
468         /**
469          * FIXME: This should not need database, but Language#formatExpiry does (T57912)
470          * @group Database
471          * @covers Message::expiryParam
472          * @covers Message::expiryParams
473          */
474         public function testExpiryParams() {
475                 $lang = Language::factory( 'en' );
476                 $msg = new RawMessage( '$1' );
477
478                 $this->assertSame(
479                         $lang->formatExpiry( wfTimestampNow() ),
480                         $msg->inLanguage( $lang )->expiryParams( wfTimestampNow() )->plain(),
481                         'expiryParams is handled correctly'
482                 );
483         }
484
485         /**
486          * @covers Message::timeperiodParam
487          * @covers Message::timeperiodParams
488          */
489         public function testTimeperiodParams() {
490                 $lang = Language::factory( 'en' );
491                 $msg = new RawMessage( '$1' );
492
493                 $this->assertSame(
494                         $lang->formatTimePeriod( 1234 ),
495                         $msg->inLanguage( $lang )->timeperiodParams( 1234 )->plain(),
496                         'timeperiodParams is handled correctly'
497                 );
498         }
499
500         /**
501          * @covers Message::sizeParam
502          * @covers Message::sizeParams
503          */
504         public function testSizeParams() {
505                 $lang = Language::factory( 'en' );
506                 $msg = new RawMessage( '$1' );
507
508                 $this->assertSame(
509                         $lang->formatSize( 123456 ),
510                         $msg->inLanguage( $lang )->sizeParams( 123456 )->plain(),
511                         'sizeParams is handled correctly'
512                 );
513         }
514
515         /**
516          * @covers Message::bitrateParam
517          * @covers Message::bitrateParams
518          */
519         public function testBitrateParams() {
520                 $lang = Language::factory( 'en' );
521                 $msg = new RawMessage( '$1' );
522
523                 $this->assertSame(
524                         $lang->formatBitrate( 123456 ),
525                         $msg->inLanguage( $lang )->bitrateParams( 123456 )->plain(),
526                         'bitrateParams is handled correctly'
527                 );
528         }
529
530         public static function providePlaintextParams() {
531                 return [
532                         [
533                                 'one $2 <div>foo</div> [[Bar]] {{Baz}} &lt;',
534                                 'plain',
535                         ],
536
537                         [
538                                 // expect
539                                 'one $2 <div>foo</div> [[Bar]] {{Baz}} &lt;',
540                                 // format
541                                 'text',
542                         ],
543                         [
544                                 'one $2 &lt;div&gt;foo&lt;/div&gt; [[Bar]] {{Baz}} &amp;lt;',
545                                 'escaped',
546                         ],
547
548                         [
549                                 'one $2 &lt;div&gt;foo&lt;/div&gt; [[Bar]] {{Baz}} &amp;lt;',
550                                 'parse',
551                         ],
552
553                         [
554                                 "<p>one $2 &lt;div&gt;foo&lt;/div&gt; [[Bar]] {{Baz}} &amp;lt;\n</p>",
555                                 'parseAsBlock',
556                         ],
557                 ];
558         }
559
560         /**
561          * @covers Message::plaintextParam
562          * @covers Message::plaintextParams
563          * @covers Message::formatPlaintext
564          * @covers Message::toString
565          * @covers Message::parse
566          * @covers Message::parseAsBlock
567          * @dataProvider providePlaintextParams
568          */
569         public function testPlaintextParams( $expect, $format ) {
570                 $lang = Language::factory( 'en' );
571
572                 $msg = new RawMessage( '$1 $2' );
573                 $params = [
574                         'one $2',
575                         '<div>foo</div> [[Bar]] {{Baz}} &lt;',
576                 ];
577                 $this->assertSame(
578                         $expect,
579                         $msg->inLanguage( $lang )->plaintextParams( $params )->$format(),
580                         "Fail formatting for $format"
581                 );
582         }
583
584         public static function provideListParam() {
585                 $lang = Language::factory( 'de' );
586                 $msg1 = new Message( 'mainpage', [], $lang );
587                 $msg2 = new RawMessage( "''link''", [], $lang );
588
589                 return [
590                         'Simple comma list' => [
591                                 [ 'a', 'b', 'c' ],
592                                 'comma',
593                                 'text',
594                                 'a, b, c'
595                         ],
596
597                         'Simple semicolon list' => [
598                                 [ 'a', 'b', 'c' ],
599                                 'semicolon',
600                                 'text',
601                                 'a; b; c'
602                         ],
603
604                         'Simple pipe list' => [
605                                 [ 'a', 'b', 'c' ],
606                                 'pipe',
607                                 'text',
608                                 'a | b | c'
609                         ],
610
611                         'Simple text list' => [
612                                 [ 'a', 'b', 'c' ],
613                                 'text',
614                                 'text',
615                                 'a, b and c'
616                         ],
617
618                         'Empty list' => [
619                                 [],
620                                 'comma',
621                                 'text',
622                                 ''
623                         ],
624
625                         'List with all "before" params, ->text()' => [
626                                 [ "''link''", Message::numParam( 12345678 ) ],
627                                 'semicolon',
628                                 'text',
629                                 '\'\'link\'\'; 12,345,678'
630                         ],
631
632                         'List with all "before" params, ->parse()' => [
633                                 [ "''link''", Message::numParam( 12345678 ) ],
634                                 'semicolon',
635                                 'parse',
636                                 '<i>link</i>; 12,345,678'
637                         ],
638
639                         'List with all "after" params, ->text()' => [
640                                 [ $msg1, $msg2, Message::rawParam( '[[foo]]' ) ],
641                                 'semicolon',
642                                 'text',
643                                 'Main Page; \'\'link\'\'; [[foo]]'
644                         ],
645
646                         'List with all "after" params, ->parse()' => [
647                                 [ $msg1, $msg2, Message::rawParam( '[[foo]]' ) ],
648                                 'semicolon',
649                                 'parse',
650                                 'Main Page; <i>link</i>; [[foo]]'
651                         ],
652
653                         'List with both "before" and "after" params, ->text()' => [
654                                 [ $msg1, $msg2, Message::rawParam( '[[foo]]' ), "''link''", Message::numParam( 12345678 ) ],
655                                 'semicolon',
656                                 'text',
657                                 'Main Page; \'\'link\'\'; [[foo]]; \'\'link\'\'; 12,345,678'
658                         ],
659
660                         'List with both "before" and "after" params, ->parse()' => [
661                                 [ $msg1, $msg2, Message::rawParam( '[[foo]]' ), "''link''", Message::numParam( 12345678 ) ],
662                                 'semicolon',
663                                 'parse',
664                                 'Main Page; <i>link</i>; [[foo]]; <i>link</i>; 12,345,678'
665                         ],
666                 ];
667         }
668
669         /**
670          * @covers Message::listParam
671          * @covers Message::extractParam
672          * @covers Message::formatListParam
673          * @dataProvider provideListParam
674          */
675         public function testListParam( $list, $type, $format, $expect ) {
676                 $lang = Language::factory( 'en' );
677
678                 $msg = new RawMessage( '$1' );
679                 $msg->params( [ Message::listParam( $list, $type ) ] );
680                 $this->assertEquals(
681                         $expect,
682                         $msg->inLanguage( $lang )->$format()
683                 );
684         }
685
686         /**
687          * @covers Message::extractParam
688          */
689         public function testMessageAsParam() {
690                 $this->setMwGlobals( [
691                         'wgScript' => '/wiki/index.php',
692                         'wgArticlePath' => '/wiki/$1',
693                 ] );
694
695                 $msg = new Message( 'returnto', [
696                         new Message( 'apihelp-link', [
697                                 'foo', new Message( 'mainpage', [], Language::factory( 'en' ) )
698                         ], Language::factory( 'de' ) )
699                 ], Language::factory( 'es' ) );
700
701                 $this->assertEquals(
702                         'Volver a [[Special:ApiHelp/foo|Página principal]].',
703                         $msg->text(),
704                         'Process with ->text()'
705                 );
706                 $this->assertEquals(
707                         '<p>Volver a <a href="/wiki/Special:ApiHelp/foo" title="Special:ApiHelp/foo">Página '
708                                 . "principal</a>.\n</p>",
709                         $msg->parseAsBlock(),
710                         'Process with ->parseAsBlock()'
711                 );
712         }
713
714         public static function provideParser() {
715                 return [
716                         [
717                                 "''&'' <x><!-- x -->",
718                                 'plain',
719                         ],
720
721                         [
722                                 "''&'' <x><!-- x -->",
723                                 'text',
724                         ],
725                         [
726                                 '<i>&amp;</i> &lt;x&gt;',
727                                 'parse',
728                         ],
729
730                         [
731                                 "<p><i>&amp;</i> &lt;x&gt;\n</p>",
732                                 'parseAsBlock',
733                         ],
734                 ];
735         }
736
737         /**
738          * @covers Message::text
739          * @covers Message::parse
740          * @covers Message::parseAsBlock
741          * @covers Message::toString
742          * @covers Message::transformText
743          * @covers Message::parseText
744          * @dataProvider provideParser
745          */
746         public function testParser( $expect, $format ) {
747                 $msg = new RawMessage( "''&'' <x><!-- x -->" );
748                 $this->assertSame(
749                         $expect,
750                         $msg->inLanguage( 'en' )->$format()
751                 );
752         }
753
754         /**
755          * @covers Message::inContentLanguage
756          */
757         public function testInContentLanguage() {
758                 $this->setUserLang( 'fr' );
759
760                 // NOTE: make sure internal caching of the message text is reset appropriately
761                 $msg = wfMessage( 'mainpage' );
762                 $this->assertSame( 'Hauptseite', $msg->inLanguage( 'de' )->plain(), "inLanguage( 'de' )" );
763                 $this->assertSame( 'Main Page', $msg->inContentLanguage()->plain(), "inContentLanguage()" );
764                 $this->assertSame( 'Accueil', $msg->inLanguage( 'fr' )->plain(), "inLanguage( 'fr' )" );
765         }
766
767         /**
768          * @covers Message::inContentLanguage
769          */
770         public function testInContentLanguageOverride() {
771                 $this->setMwGlobals( [
772                         'wgForceUIMsgAsContentMsg' => [ 'mainpage' ],
773                 ] );
774                 $this->setUserLang( 'fr' );
775
776                 // NOTE: make sure internal caching of the message text is reset appropriately.
777                 // NOTE: wgForceUIMsgAsContentMsg forces the messages *current* language to be used.
778                 $msg = wfMessage( 'mainpage' );
779                 $this->assertSame(
780                         'Accueil',
781                         $msg->inContentLanguage()->plain(),
782                         'inContentLanguage() with ForceUIMsg override enabled'
783                 );
784                 $this->assertSame( 'Main Page', $msg->inLanguage( 'en' )->plain(), "inLanguage( 'en' )" );
785                 $this->assertSame(
786                         'Main Page',
787                         $msg->inContentLanguage()->plain(),
788                         'inContentLanguage() with ForceUIMsg override enabled'
789                 );
790                 $this->assertSame( 'Hauptseite', $msg->inLanguage( 'de' )->plain(), "inLanguage( 'de' )" );
791         }
792
793         /**
794          * @expectedException MWException
795          * @covers Message::inLanguage
796          */
797         public function testInLanguageThrows() {
798                 wfMessage( 'foo' )->inLanguage( 123 );
799         }
800
801         /**
802          * @covers Message::serialize
803          * @covers Message::unserialize
804          */
805         public function testSerialization() {
806                 $msg = new Message( 'parentheses' );
807                 $msg->rawParams( '<a>foo</a>' );
808                 $msg->title( Title::newFromText( 'Testing' ) );
809                 $this->assertSame( '(<a>foo</a>)', $msg->parse(), 'Sanity check' );
810                 $msg = unserialize( serialize( $msg ) );
811                 $this->assertSame( '(<a>foo</a>)', $msg->parse() );
812                 $title = TestingAccessWrapper::newFromObject( $msg )->title;
813                 $this->assertInstanceOf( 'Title', $title );
814                 $this->assertSame( 'Testing', $title->getFullText() );
815
816                 $msg = new Message( 'mainpage' );
817                 $msg->inLanguage( 'de' );
818                 $this->assertSame( 'Hauptseite', $msg->plain(), 'Sanity check' );
819                 $msg = unserialize( serialize( $msg ) );
820                 $this->assertSame( 'Hauptseite', $msg->plain() );
821         }
822
823         /**
824          * @covers Message::newFromSpecifier
825          * @dataProvider provideNewFromSpecifier
826          */
827         public function testNewFromSpecifier( $value, $expectedText ) {
828                 $message = Message::newFromSpecifier( $value );
829                 $this->assertInstanceOf( Message::class, $message );
830                 if ( $value instanceof Message ) {
831                         $this->assertInstanceOf( get_class( $value ), $message );
832                         $this->assertEquals( $value, $message );
833                 }
834                 $this->assertSame( $expectedText, $message->text() );
835         }
836
837         public function provideNewFromSpecifier() {
838                 $messageSpecifier = $this->getMockForAbstractClass( MessageSpecifier::class );
839                 $messageSpecifier->expects( $this->any() )->method( 'getKey' )->willReturn( 'mainpage' );
840                 $messageSpecifier->expects( $this->any() )->method( 'getParams' )->willReturn( [] );
841
842                 return [
843                         'string' => [ 'mainpage', 'Main Page' ],
844                         'array' => [ [ 'youhavenewmessages', 'foo', 'bar' ], 'You have foo (bar).' ],
845                         'Message' => [ new Message( 'youhavenewmessages', [ 'foo', 'bar' ] ), 'You have foo (bar).' ],
846                         'RawMessage' => [ new RawMessage( 'foo ($1)', [ 'bar' ] ), 'foo (bar)' ],
847                         'ApiMessage' => [ new ApiMessage( [ 'mainpage' ], 'code', [ 'data' ] ), 'Main Page' ],
848                         'MessageSpecifier' => [ $messageSpecifier, 'Main Page' ],
849                         'nested RawMessage' => [ [ new RawMessage( 'foo ($1)', [ 'bar' ] ) ], 'foo (bar)' ],
850                 ];
851         }
852 }