jenkins-bot has submitted this change and it was merged. Change subject: Add unit tests for CSSParse and CSSRender ......................................................................
Add unit tests for CSSParse and CSSRender Test the roundtrip on a collection of correct and pathological stylesheets to ascertain whether the parse is behaving as expected. Bug: T483 Change-Id: I484cc856b5696c1fa6265769320f79853365e1d6 --- M TemplateStyles.hooks.php M extension.json A tests/CSSParseRenderTest.php 3 files changed, 245 insertions(+), 0 deletions(-) Approvals: BryanDavis: Looks good to me, approved jenkins-bot: Verified diff --git a/TemplateStyles.hooks.php b/TemplateStyles.hooks.php index 9a0c0c7..b75e356 100644 --- a/TemplateStyles.hooks.php +++ b/TemplateStyles.hooks.php @@ -15,6 +15,11 @@ return true; } + public static function onUnitTestsList( &$paths ) { + $paths[] = __DIR__ . '/tests/phpunit/'; + return true; + } + private static function decodeFromBlob( $blob ) { $tree = gzdecode( $blob ); if ( $tree ) { diff --git a/extension.json b/extension.json index 3e0f3e0..13f787a 100644 --- a/extension.json +++ b/extension.json @@ -36,6 +36,9 @@ ], "OutputPageParserOutput": [ "TemplateStylesHooks::onOutputPageParserOutput" + ], + "UnitTestList": [ + "TemplateStyleHooks::onUnitTestList" ] } } diff --git a/tests/CSSParseRenderTest.php b/tests/CSSParseRenderTest.php new file mode 100644 index 0000000..3ebff59 --- /dev/null +++ b/tests/CSSParseRenderTest.php @@ -0,0 +1,237 @@ +<?php + +/** + * @group TemplateStyles + */ +class CSSParseRenderTest extends MediaWikiTestCase { + + protected function setUp() { + parent::setUp(); + } + + public static function provideCSSParser() { + return [ + [ 'test' => 'Bare declaration', + 'expect' => '', + 'css' => <<<FIN + prop: val; +FIN + ], + [ 'test' => 'Blacklisted property', + 'expect' => '.X .sel {good:123;} ', + 'css' => <<<FIN + .sel { + good: 123; + -evil: "boo"; + } +FIN + ], + [ 'test' => 'Case insensivity', + 'expect' => '@media screen { .X .sel1 {prop:WhiteListed(foo);} } ', + 'css' => <<<FIN + @MEDIA screen { + .sel1 { + prop: WhiteListed(foo); + -EVIL: evil; + } + } +FIN + ], + [ 'test' => 'Comment trickery', + 'expect' => '.X .sel1 {} .X .sel2 .sel3 {prop3:val3;} ', + 'css' => <<<FIN + .sel1 { + -ev/* x */il: evil; + } + .sel2 /* { prop2: val2; } */ + .sel3 { + prop3: val3; + } /* unfinishe +FIN + ], + [ 'test' => 'Complex selectors', + 'expect' => '.X .sel1[foo=\'ba{r\'] #id a.foo::hover {prop1:val1;} ', + 'css' => <<<FIN + .sel1[foo='ba{r'] #id a.foo::hover { + prop1: val1; + } +FIN + ], + [ 'test' => 'Edge cases', + 'expect' => '.X :sel {} ', + 'css' => <<<FIN + :sel { + } +FIN + ], + [ 'test' => 'Function in function', + 'expect' => '.X .sel1 {} ', + 'css' => <<<FIN + .sel1 { + prop1: whitelisted(1, evil(2)); + } +FIN + ], + [ 'test' => 'Incomplete rule', + 'expect' => '.X .sel {prop:val;} ', + 'css' => <<<FIN + .sel { + prop: val; +FIN + ], + [ 'test' => 'Media block', + 'expect' => '.X .sel2 {prop2:val2;} @media print { .X .sel1 {prop1:val1;} } ', + 'css' => <<<FIN + @media print { + .sel1 { + prop1: val1; + } + } + + .sel2 { + prop2: val2; + } +FIN + ], + [ 'test' => 'Multiple rules', + 'expect' => '.X .sel1 A {prop1:val1;} .X T.sel2 {prop2:val2;} ', + 'css' => <<<FIN + .sel1 A { + prop1: val1; + } + + T.sel2 { + prop2: val2; + } +FIN + ], + [ 'test' => 'Multiple selectors', + 'expect' => '.X .sel1,.X TD .sel2["a,comma"],.X #id {prop:val;} ', + 'css' => <<<FIN + .sel1, TD .sel2["a,comma"], #id { + prop: val; + } +FIN + ], + [ 'test' => 'No selector', + 'expect' => '{prop1:val1;} ', + 'css' => <<<FIN + { + prop1: val1; + } +FIN + ], + [ 'test' => 'Not a declaration', + 'expect' => '.X .sel {prop:val;} ', + 'css' => <<<FIN + .sel { + not a declaration; + prop: val; + } +FIN + ], + [ 'test' => 'Obfuscated properties', + 'expect' => '.X .sel {good:val2;} ', + 'css' => <<<FIN + .sel { + -\\065 vil: val1; + go\\00006fd: val2; + } +FIN + ], + [ 'test' => 'Rule within rule', + 'expect' => '.X .sel1 {prop1:val1;} .X .sel3 {prop4:val4;} ', + 'css' => <<<FIN + .sel1 { + prop1: val1; + .sel2 { + prop2: val2; + } + prop3: val3; + } + + .sel3 { + prop4: val4; + } +FIN + ], + [ 'test' => 'String literals', + 'expect' => '.X .sel {prop1:\'val1\';prop3:"v/**/al\"3";bad:"broken" ;} ', + 'css' => <<<FIN + .sel { + prop1: 'val1'; + prop3: "v/**/al\"3"; + bad: "broken + } +FIN + ], + [ 'test' => 'Unsupported block', + 'expect' => '.X .sel {prop2:val2;} ', + 'css' => <<<FIN + @font-face { + prop1: val1; + } + + .sel { + prop2: val2; + } +FIN + ], + [ 'test' => 'Unwhitelisted function', + 'expect' => '.X .sel {prop1:whitelisted(val1);} ', + 'css' => <<<FIN + .sel { + prop1: whitelisted(val1); + prop2: evil(val2); + } +FIN + ], + [ 'test' => 'Values', + 'expect' => '.X .sel {prop:1em .5px 12% #FFF;} ', + 'css' => <<<FIN + .sel { + prop: 1em .5px 12% #FFF; + } +FIN + ], + [ 'test' => 'Whitespace', + 'expect' => '.X .sel1 #id{prop2:whitelisted ( val2 ) ;prop3:not whitelisted( val3 );} ', + 'css' => <<<FIN + .sel1 + #id{ + -evil + :val1; + prop2/* + comment */: whitelisted ( val2 ) + ;prop3 :not/**/whitelisted( val3 );} +FIN + ] + ]; + } + + /** + * @dataProvider provideCSSParser + */ + public function testCSSParser( $test, $expect, $source ) { + + $tree = new CSSParser( $source ); + $rules = $tree->rules( '.X ' ); + if ( !$rules ) { + $this->fail( "$test: Stylesheet did not parse." ); + return; + } + + $r = new CSSRenderer(); + $r->add( $rules ); + $css = $r->render( [ "whitelisted" ], [ "-evil" ] ); + + $this->assertEquals( + $expect, + preg_replace( '/[ \t\n]+/', ' ', $css ), + "$test: parse did not return expected output." + ); + + } + +} + -- To view, visit https://gerrit.wikimedia.org/r/283772 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I484cc856b5696c1fa6265769320f79853365e1d6 Gerrit-PatchSet: 3 Gerrit-Project: mediawiki/extensions/TemplateStyles Gerrit-Branch: master Gerrit-Owner: coren <m...@uberbox.org> Gerrit-Reviewer: Brion VIBBER <br...@wikimedia.org> Gerrit-Reviewer: BryanDavis <bda...@wikimedia.org> Gerrit-Reviewer: Jforrester <jforres...@wikimedia.org> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits