getUser(); $status = $page->doEditContent( $content, 'Test for TemplateStyles', 0, false, $user ); if ( !$status->isOk() ) { $this->fail( "Failed to create $title: " . $status->getWikiText( false, false, 'en' ) ); } } public function addDBDataOnce() { $this->addPage( 'wikitext', '.foo { color: red; }', CONTENT_MODEL_WIKITEXT ); $this->addPage( 'nonsanitized.css', '.foo { color: red; }', CONTENT_MODEL_CSS ); $this->addPage( 'styles1.css', '.foo { color: blue; }', 'sanitized-css' ); $this->addPage( 'styles2.css', '.bar { color: green; }', 'sanitized-css' ); $this->addPage( 'styles3.css', 'html.no-js body.skin-minerva .bar { color: yellow; }', 'sanitized-css' ); } /** * @expectedException InvalidArgumentException * @expectedExceptionMessage Invalid value for $extraWrapper: .foo>.bar */ public function testGetSanitizerInvalidWrapper() { TemplateStylesHooks::getSanitizer( 'foo', '.foo>.bar' ); } /** * @dataProvider provideOnRegistration * @param array $textModelsToParse * @param bool $autoParseContent * @param array $expect */ public function testOnRegistration( $textModelsToParse, $autoParseContent, $expect ) { $this->setMwGlobals( [ 'wgTextModelsToParse' => $textModelsToParse, 'wgTemplateStylesAutoParseContent' => $autoParseContent, ] ); global $wgTextModelsToParse; TemplateStylesHooks::onRegistration(); $this->assertSame( $expect, $wgTextModelsToParse ); } public static function provideOnRegistration() { return [ [ [ CONTENT_MODEL_WIKITEXT ], true, [ CONTENT_MODEL_WIKITEXT ] ], [ [ CONTENT_MODEL_WIKITEXT, CONTENT_MODEL_CSS ], true, [ CONTENT_MODEL_WIKITEXT, CONTENT_MODEL_CSS, 'sanitized-css' ], ], [ [ CONTENT_MODEL_WIKITEXT, CONTENT_MODEL_CSS ], false, [ CONTENT_MODEL_WIKITEXT, CONTENT_MODEL_CSS ], ], ]; } /** * @dataProvider provideOnParserAfterTidy */ public function testOnParserAfterTidy( $text, $expect ) { $p = new Parser(); TemplateStylesHooks::onParserAfterTidy( $p, $text ); $this->assertSame( $expect, $text ); } public static function provideOnParserAfterTidy() { return [ [ "", "", ], [ "", "", ], [ "", "", ], [ "", "", ], [ "", "", ], [ "", "", ], ]; } /** * @dataProvider provideOnContentHandlerDefaultModelFor */ public function testOnContentHandlerDefaultModelFor( $ns, $title, $expect ) { $this->setMwGlobals( [ 'wgTemplateStylesNamespaces' => [ 10 => true, 2 => false, 3000 => true, 3002 => true, 3006 => false, ], 'wgNamespacesWithSubpages' => [ 10 => true, 2 => true, 3000 => true, 3002 => false, 3004 => true, 3006 => true ], ] ); $reset = ExtensionRegistry::getInstance()->setAttributeForTest( 'TemplateStylesNamespaces', [ 3004, 3006 ] ); $model = 'unchanged'; $ret = TemplateStylesHooks::onContentHandlerDefaultModelFor( Title::makeTitle( $ns, $title ), $model ); $this->assertSame( !$expect, $ret ); $this->assertSame( $expect ? 'sanitized-css' : 'unchanged', $model ); } public static function provideOnContentHandlerDefaultModelFor() { return [ [ 10, 'Test/test.css', true ], [ 10, 'Test.css', false ], [ 10, 'Test/test.xss', false ], [ 10, 'Test/test.CSS', false ], [ 3000, 'Test/test.css', true ], [ 3002, 'Test/test.css', false ], [ 2, 'Test/test.css', false ], [ 3004, 'Test/test.css', true ], [ 3006, 'Test/test.css', false ], ]; } /** * @dataProvider provideOnCodeEditorGetPageLanguage */ public function testOnCodeEditorGetPageLanguage( $useCodeEditor, $model, $expect ) { $this->setMwGlobals( [ 'wgTemplateStylesUseCodeEditor' => $useCodeEditor, ] ); $title = Title::makeTitle( NS_TEMPLATE, 'Test.css' ); $lang = 'unchanged'; $ret = TemplateStylesHooks::onCodeEditorGetPageLanguage( $title, $lang, $model, 'text/x-whatever' ); $this->assertSame( !$expect, $ret ); $this->assertSame( $expect ? 'css' : 'unchanged', $lang ); } public static function provideOnCodeEditorGetPageLanguage() { return [ [ true, 'wikitext', false ], [ true, 'css', false ], [ true, 'sanitized-css', true ], [ false, 'sanitized-css', false ], ]; } /** * Unfortunately we can't just use a parserTests.txt file because our * tag's output depends on the revision IDs of the input pages. * @dataProvider provideTag */ public function testTag( ParserOptions $popt, $getTextOptions, $wikitext, $expect, $globals = [] ) { global $wgParserConf; $this->setMwGlobals( $globals + [ 'wgScriptPath' => '', 'wgScript' => '/index.php', 'wgArticlePath' => '/wiki/$1', ] ); $oldCurrentRevisionCallback = $popt->setCurrentRevisionCallback( function ( Title $title, $parser = false ) use ( &$oldCurrentRevisionCallback ) { if ( $title->getPrefixedText() === 'Template:Test replacement' ) { $user = RequestContext::getMain()->getUser(); return new Revision( [ 'page' => $title->getArticleID(), 'user_text' => $user->getName(), 'user' => $user->getId(), 'parent_id' => $title->getLatestRevID(), 'title' => $title, 'content' => new TemplateStylesContent( '.baz { color:orange; bogus:bogus; }' ) ] ); } return call_user_func( $oldCurrentRevisionCallback, $title, $parser ); } ); $class = $wgParserConf['class']; $parser = new $class( $wgParserConf ); /** @var Parser $parser */ $parser->firstCallInit(); if ( !isset( $parser->mTagHooks['templatestyles'] ) ) { throw new Exception( 'templatestyles tag hook is not in the parser' ); } $out = $parser->parse( $wikitext, Title::newFromText( 'Test' ), $popt ); $parser->mPreprocessor = null; # Break the Parser <-> Preprocessor cycle $expect = preg_replace_callback( '/\{\{REV:(.*?)\}\}/', function ( $m ) { return Title::newFromText( 'Template:TemplateStyles test/' . $m[1] )->getLatestRevID(); }, $expect ); $this->assertEquals( $expect, $out->getText( $getTextOptions ) ); } public static function provideTag() { $popt = ParserOptions::newFromContext( RequestContext::getMain() ); $popt->setWrapOutputClass( 'templatestyles-test' ); $popt2 = ParserOptions::newFromContext( RequestContext::getMain() ); $popt3 = ParserOptions::newFromContext( RequestContext::getMain() ); Wikimedia\quietCall( [ $popt3, 'setWrapOutputClass' ], false ); return [ 'Tag without src' => [ $popt, [], '', // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong "

TemplateStyles' src attribute must not be empty.\n

", // @codingStandardsIgnoreEnd ], 'Tag with invalid src' => [ $popt, [], '', // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong "

Invalid title for TemplateStyles' src attribute.\n

", // @codingStandardsIgnoreEnd ], 'Tag with valid but nonexistent title' => [ $popt, [], '', // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong "

Page Template:ThisDoes'''Not'''Exist has no content.\n

", // @codingStandardsIgnoreEnd ], 'Tag with valid but nonexistent title, main namespace' => [ $popt, [], '', // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong "

Page ThisDoes'''Not'''Exist has no content.\n

", // @codingStandardsIgnoreEnd ], 'Tag with wikitext page' => [ $popt, [], '', // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong "

Page Template:TemplateStyles test/wikitext must have content model \"Sanitized CSS\" for TemplateStyles (current model is \"wikitext\").\n

", // @codingStandardsIgnoreEnd ], 'Tag with CSS (not sanitized-css) page' => [ $popt, [], '', // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong "

Page Template:TemplateStyles test/nonsanitized.css must have content model \"Sanitized CSS\" for TemplateStyles (current model is \"CSS\").\n

", // @codingStandardsIgnoreEnd ], 'Working tag' => [ $popt, [], '', // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong "
", // @codingStandardsIgnoreEnd ], 'Disabled' => [ $popt, [], '', "
", [ 'wgTemplateStylesDisable' => true ], ], 'Replaced content (which includes sanitization errors)' => [ $popt, [], '', // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong "
", // @codingStandardsIgnoreEnd ], 'Hoistable selectors are hoisted' => [ $popt, [], '', // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong "
", // @codingStandardsIgnoreEnd ], 'Still prefixed despite no wrapping class' => [ $popt2, [ 'unwrap' => true ], '', // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong "", // @codingStandardsIgnoreEnd ], 'Still prefixed despite deprecated no wrapping class' => [ $popt3, [], '', // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong "", // @codingStandardsIgnoreEnd ], 'Deduplicated tags' => [ $popt, [], trim( ' ' ), // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong trim( '
' ), // @codingStandardsIgnoreEnd ], 'Wrapper parameter' => [ $popt2, [], '', // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong "
", // @codingStandardsIgnoreEnd ], 'Invalid wrapper parameter' => [ $popt, [], '', // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong "

Invalid value for TemplateStyles' wrapper attribute.\n

", // @codingStandardsIgnoreEnd ], 'Invalid wrapper parameter (2)' => [ $popt, [], '', // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong "

Invalid value for TemplateStyles' wrapper attribute.\n

", // @codingStandardsIgnoreEnd ], 'Wrapper parameter and proper deduplication' => [ $popt2, [], trim( ' ' ), // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong trim( '
' ), // @codingStandardsIgnoreEnd ], ]; } }