X-Git-Url: https://git.heureux-cyclage.org/?p=lhc%2Fweb%2Fwiklou.git;a=blobdiff_plain;f=tests%2Fphpunit%2Fincludes%2FSanitizerTest.php;h=d5066235ec2a5d569d27fdf3d4ba9278e8dee2db;hp=abcf1d4283f928ba5d589335515f3c935a5aa643;hb=a0947c9507065a83afe52b078f0f6d1c6163875e;hpb=313dde5d06c42be1eb7da9127ec92bb5d360ddab diff --git a/tests/phpunit/includes/SanitizerTest.php b/tests/phpunit/includes/SanitizerTest.php index abcf1d4283..d5066235ec 100644 --- a/tests/phpunit/includes/SanitizerTest.php +++ b/tests/phpunit/includes/SanitizerTest.php @@ -3,6 +3,8 @@ /** * @todo Tests covering decodeCharReferences can be refactored into a single * method and dataprovider. + * + * @group Sanitizer */ class SanitizerTest extends MediaWikiTestCase { @@ -344,7 +346,42 @@ class SanitizerTest extends MediaWikiTestCase { } /** - * Test escapeIdReferenceList for consistency with escapeId + * Test Sanitizer::escapeId + * + * @dataProvider provideEscapeId + * @covers Sanitizer::escapeId + */ + public function testEscapeId( $input, $output ) { + $this->assertEquals( + $output, + Sanitizer::escapeId( $input, [ 'noninitial', 'legacy' ] ) + ); + } + + public static function provideEscapeId() { + return [ + [ '+', '.2B' ], + [ '&', '.26' ], + [ '=', '.3D' ], + [ ':', ':' ], + [ ';', '.3B' ], + [ '@', '.40' ], + [ '$', '.24' ], + [ '-_.', '-_.' ], + [ '!', '.21' ], + [ '*', '.2A' ], + [ '/', '.2F' ], + [ '[]', '.5B.5D' ], + [ '<>', '.3C.3E' ], + [ '\'', '.27' ], + [ '§', '.C2.A7' ], + [ 'Test:A & B/Here', 'Test:A_.26_B.2FHere' ], + [ 'A&B&C&amp;D&amp;amp;E', 'A.26B.26C.26amp.3BD.26amp.3Bamp.3BE' ], + ]; + } + + /** + * Test escapeIdReferenceList for consistency with escapeIdForAttribute * * @dataProvider provideEscapeIdReferenceList * @covers Sanitizer::escapeIdReferenceList @@ -352,9 +389,9 @@ class SanitizerTest extends MediaWikiTestCase { public function testEscapeIdReferenceList( $referenceList, $id1, $id2 ) { $this->assertEquals( Sanitizer::escapeIdReferenceList( $referenceList, 'noninitial' ), - Sanitizer::escapeId( $id1, 'noninitial' ) + Sanitizer::escapeIdForAttribute( $id1 ) . ' ' - . Sanitizer::escapeId( $id2, 'noninitial' ) + . Sanitizer::escapeIdForAttribute( $id2 ) ); } @@ -387,4 +424,119 @@ class SanitizerTest extends MediaWikiTestCase { [ 'data-mwfoo', true ], // could be false but this is how it's implemented currently ]; } + + /** + * @dataProvider provideEscapeIdForStuff + * + * @covers Sanitizer::escapeIdForAttribute() + * @covers Sanitizer::escapeIdForLink() + * @covers Sanitizer::escapeIdForExternalInterwiki() + * @covers Sanitizer::escapeIdInternal() + * @covers Sanitizer::urlEscapeId() + * + * @param string $stuff + * @param string[] $config + * @param string $id + * @param string|false $expected + * @param int|null $mode + */ + public function testEscapeIdForStuff( $stuff, array $config, $id, $expected, $mode = null ) { + $func = "Sanitizer::escapeIdFor{$stuff}"; + $iwFlavor = array_pop( $config ); + $this->setMwGlobals( [ + 'wgFragmentMode' => $config, + 'wgExternalInterwikiFragmentMode' => $iwFlavor, + ] ); + $escaped = call_user_func( $func, $id, $mode ); + self::assertEquals( $expected, $escaped ); + } + + public function provideEscapeIdForStuff() { + // Test inputs and outputs + $text = 'foo тест_#%!\'()[]:<>'; + $legacyEncoded = 'foo_.D1.82.D0.B5.D1.81.D1.82_.23.25.21.27.28.29.5B.5D:.3C.3E'; + $html5Encoded = 'foo_тест_#%!\'()[]:<>'; + $html5Escaped = 'foo_%D1%82%D0%B5%D1%81%D1%82_%23%25%21%27%28%29%5B%5D:%3C%3E'; + $html5Experimental = 'foo_тест_!_()[]:<>'; + + // Settings: last element is $wgExternalInterwikiFragmentMode, the rest is $wgFragmentMode + $legacy = [ 'legacy', 'legacy' ]; + $legacyNew = [ 'legacy', 'html5', 'legacy' ]; + $newLegacy = [ 'html5', 'legacy', 'legacy' ]; + $new = [ 'html5', 'legacy' ]; + $allNew = [ 'html5', 'html5' ]; + $experimentalLegacy = [ 'html5-legacy', 'legacy', 'legacy' ]; + $newExperimental = [ 'html5', 'html5-legacy', 'legacy' ]; + + return [ + // Pure legacy: how MW worked before 2017 + [ 'Attribute', $legacy, $text, $legacyEncoded, Sanitizer::ID_PRIMARY ], + [ 'Attribute', $legacy, $text, false, Sanitizer::ID_FALLBACK ], + [ 'Link', $legacy, $text, $legacyEncoded ], + [ 'ExternalInterwiki', $legacy, $text, $legacyEncoded ], + + // Transition to a new world: legacy links with HTML5 fallback + [ 'Attribute', $legacyNew, $text, $legacyEncoded, Sanitizer::ID_PRIMARY ], + [ 'Attribute', $legacyNew, $text, $html5Encoded, Sanitizer::ID_FALLBACK ], + [ 'Link', $legacyNew, $text, $legacyEncoded ], + [ 'ExternalInterwiki', $legacyNew, $text, $legacyEncoded ], + + // New world: HTML5 links, legacy fallbacks + [ 'Attribute', $newLegacy, $text, $html5Encoded, Sanitizer::ID_PRIMARY ], + [ 'Attribute', $newLegacy, $text, $legacyEncoded, Sanitizer::ID_FALLBACK ], + [ 'Link', $newLegacy, $text, $html5Escaped ], + [ 'ExternalInterwiki', $newLegacy, $text, $legacyEncoded ], + + // Distant future: no legacy fallbacks, but still linking to leagacy wikis + [ 'Attribute', $new, $text, $html5Encoded, Sanitizer::ID_PRIMARY ], + [ 'Attribute', $new, $text, false, Sanitizer::ID_FALLBACK ], + [ 'Link', $new, $text, $html5Escaped ], + [ 'ExternalInterwiki', $new, $text, $legacyEncoded ], + + // Just before the heat death of universe: external interwikis are also HTML5 \m/ + [ 'Attribute', $allNew, $text, $html5Encoded, Sanitizer::ID_PRIMARY ], + [ 'Attribute', $allNew, $text, false, Sanitizer::ID_FALLBACK ], + [ 'Link', $allNew, $text, $html5Escaped ], + [ 'ExternalInterwiki', $allNew, $text, $html5Escaped ], + + // Someone flipped $wgExperimentalHtmlIds on + [ 'Attribute', $experimentalLegacy, $text, $html5Experimental, Sanitizer::ID_PRIMARY ], + [ 'Attribute', $experimentalLegacy, $text, $legacyEncoded, Sanitizer::ID_FALLBACK ], + [ 'Link', $experimentalLegacy, $text, $html5Experimental ], + [ 'ExternalInterwiki', $experimentalLegacy, $text, $legacyEncoded ], + + // Migration from $wgExperimentalHtmlIds to modern HTML5 + [ 'Attribute', $newExperimental, $text, $html5Encoded, Sanitizer::ID_PRIMARY ], + [ 'Attribute', $newExperimental, $text, $html5Experimental, Sanitizer::ID_FALLBACK ], + [ 'Link', $newExperimental, $text, $html5Escaped ], + [ 'ExternalInterwiki', $newExperimental, $text, $legacyEncoded ], + ]; + } + + /** + * @expectedException InvalidArgumentException + * @covers Sanitizer::escapeIdInternal() + */ + public function testInvalidFragmentThrows() { + $this->setMwGlobals( 'wgFragmentMode', [ 'boom!' ] ); + Sanitizer::escapeIdForAttribute( 'This should throw' ); + } + + /** + * @expectedException UnexpectedValueException + * @covers Sanitizer::escapeIdForAttribute() + */ + public function testNoPrimaryFragmentModeThrows() { + $this->setMwGlobals( 'wgFragmentMode', [ 666 => 'html5' ] ); + Sanitizer::escapeIdForAttribute( 'This should throw' ); + } + + /** + * @expectedException UnexpectedValueException + * @covers Sanitizer::escapeIdForLink() + */ + public function testNoPrimaryFragmentModeThrows2() { + $this->setMwGlobals( 'wgFragmentMode', [ 666 => 'html5' ] ); + Sanitizer::escapeIdForLink( 'This should throw' ); + } }