From 0de6d13f503419a28c50b5984029debc8004d623 Mon Sep 17 00:00:00 2001 From: Priyanka Dhanda Date: Wed, 22 Sep 2010 00:16:43 +0000 Subject: [PATCH] RunSeleniumTests.php: Cleaned up a few options that this script takes. Settings are now through a file passed into seleniumConfig or via a hook SeleniumSettings. Reasonable default values when some value is not found. --- includes/AutoLoader.php | 3 +- includes/DefaultSettings.php | 7 + maintenance/tests/RunSeleniumTests.php | 87 ++++++--- .../includes/SeleniumConfigurationTest.php | 174 ++++++++++++++++++ maintenance/tests/selenium/Selenium.php | 36 ++-- .../tests/selenium/SeleniumTestConfig.php | 46 +++++ .../tests/selenium/SeleniumTestListener.php | 11 +- .../tests/selenium/SeleniumTestSuite.php | 7 +- .../tests/selenium/SimpleSeleniumTestCase.php | 20 +- .../selenium/SimpleSeleniumTestSuite.php | 14 +- .../selenium/selenium_settings.ini.sample | 17 ++ 11 files changed, 352 insertions(+), 70 deletions(-) create mode 100644 maintenance/tests/phpunit/includes/SeleniumConfigurationTest.php create mode 100644 maintenance/tests/selenium/SeleniumTestConfig.php create mode 100644 maintenance/tests/selenium/selenium_settings.ini.sample diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index 2e033c19d7..02602b5548 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -633,7 +633,6 @@ $wgAutoloadLocalClasses = array( 'SpecialRecentChanges' => 'includes/specials/SpecialRecentchanges.php', 'SpecialRecentchangeslinked' => 'includes/specials/SpecialRecentchangeslinked.php', 'SpecialSearch' => 'includes/specials/SpecialSearch.php', - 'SpecialSelenium' => 'includes/specials/SpecialSelenium.php', 'SpecialSpecialpages' => 'includes/specials/SpecialSpecialpages.php', 'SpecialStatistics' => 'includes/specials/SpecialStatistics.php', 'SpecialTags' => 'includes/specials/SpecialTags.php', @@ -705,7 +704,6 @@ $wgAutoloadLocalClasses = array( 'UserDupes' => 'maintenance/userDupes.inc', # maintenance/tests/selenium - 'SimpleSeleniumTestSuite' => 'maintenance/tests/selenium/SimpleSeleniumTestSuite.php', 'Selenium' => 'maintenance/tests/selenium/Selenium.php', 'SeleniumLoader' => 'maintenance/tests/selenium/SeleniumLoader.php', 'SeleniumTestCase' => 'maintenance/tests/selenium/SeleniumTestCase.php', @@ -713,6 +711,7 @@ $wgAutoloadLocalClasses = array( 'SeleniumTestHTMLLogger' => 'maintenance/tests/selenium/SeleniumTestHTMLLogger.php', 'SeleniumTestListener' => 'maintenance/tests/selenium/SeleniumTestListener.php', 'SeleniumTestSuite' => 'maintenance/tests/selenium/SeleniumTestSuite.php', + 'SeleniumTestConfig' => 'maintenance/tests/selenium/SeleniumTestConfig.php', # maintenance/language 'csvStatsOutput' => 'maintenance/language/StatOutputs.php', diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 02fda8a248..65276e9291 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -5116,6 +5116,13 @@ $wgRepositoryPackageStates = array( //'deprecated', ); +/** + * Allows running of selenium tests via maintenance/tests/RunSeleniumTests.php + */ +$wgEnableSelenium = false; + + + /** * For really cool vim folding this needs to be at the end: * vim: foldmarker=@{,@} foldmethod=marker diff --git a/maintenance/tests/RunSeleniumTests.php b/maintenance/tests/RunSeleniumTests.php index 58fbfcd027..4aa8800e41 100644 --- a/maintenance/tests/RunSeleniumTests.php +++ b/maintenance/tests/RunSeleniumTests.php @@ -35,11 +35,14 @@ class SeleniumTester extends Maintenance { public function __construct() { parent::__construct(); - - $this->addOption( 'port', 'Port used by selenium server' ); - $this->addOption( 'host', 'Host selenium server' ); - $this->addOption( 'browser', 'The browser he used during testing' ); - $this->addOption( 'url', 'The Mediawiki installation to point to.' ); + $this->mDescription = "Selenium Test Runner. For documentation, visit http://www.mediawiki.org/wiki/SeleniumFramework"; + $this->addOption( 'port', 'Port used by selenium server. Default: 4444' ); + $this->addOption( 'host', 'Host selenium server. Default: $wgServer . $wgScriptPath' ); + $this->addOption( 'testBrowser', 'The browser he used during testing. Default: firefox' ); + $this->addOption( 'wikiUrl', 'The Mediawiki installation to point to. Default: http://localhost' ); + $this->addOption( 'username', 'The login username for sunning tests. Default: empty' ); + $this->addOption( 'userPassword', 'The login password for running tests. Default: empty' ); + $this->addOption( 'seleniumConfig', 'Location of the selenium config file. Default: empty' ); $this->addOption( 'list-browsers', 'List the available browsers.' ); $this->addOption( 'verbose', 'Be noisier.' ); @@ -52,46 +55,76 @@ class SeleniumTester extends Maintenance { public function listBrowsers() { $desc = "Available browsers:\n"; - $sel = new Selenium; - foreach ($sel->setupBrowsers() as $k => $v) { + foreach ($this->selenium->getAvailableBrowsers() as $k => $v) { $desc .= " $k => $v\n"; } echo $desc; } - protected function getTestSuites() { - return array( 'SimpleSeleniumTestSuite' ); - } - - protected function runTests( ) { + protected function runTests( $seleniumTestSuites = array() ) { $result = new PHPUnit_Framework_TestResult; $result->addListener( new SeleniumTestListener( $this->selenium->getLogger() ) ); - - foreach ( $this->getTestSuites() as $testSuiteName ) { - $suite = new $testSuiteName; + + foreach ( $seleniumTestSuites as $testSuiteName => $testSuiteFile ) { + require( $testSuiteFile ); + $suite = new $testSuiteName(); $suite->addTests(); + try { $suite->run( $result ); } catch ( Testing_Selenium_Exception $e ) { + $suite->tearDown(); throw new MWException( $e->getMessage() ); } } } public function execute() { - global $wgServer, $wgScriptPath; - - /** - * @todo Add an alternative where settings are read from an INI file. - */ + global $wgServer, $wgScriptPath, $wgHooks; + + $seleniumSettings; + $seleniumBrowsers; + $seleniumTestSuites; + + $configFile = $this->getOption( 'seleniumConfig', '' ); + if ( strlen( $configFile ) > 0 ) { + $this->output("Using Selenium Configuration file: " . $configFile . "\n"); + SeleniumTestConfig::getSeleniumSettings( &$seleniumSettings, + &$seleniumBrowsers, + &$seleniumTestSuites, + $configFile ); + } else if ( !isset( $wgHooks['SeleniumSettings'] ) ) { + $this->output("Using default Selenium Configuration file: selenium_settings.ini in the root directory.\n"); + SeleniumTestConfig::getSeleniumSettings( &$seleniumSettings, + &$seleniumBrowsers, + &$seleniumTestSuites + ); + } else { + $this->output("Using 'SeleniumSettings' hook for configuration.\n"); + wfRunHooks('SeleniumSettings', array( &$seleniumSettings, + &$seleniumBrowsers, + &$seleniumTestSuites ) ); + } + + + //set reasonable defaults if we did not find the settings + if ( !isset( $seleniumBrowsers ) ) $seleniumBrowsers = array ('firefox' => '*firefox'); + if ( !isset( $seleniumSettings['host'] ) ) $seleniumSettings['host'] = $wgServer . $wgScriptPath; + if ( !isset( $seleniumSettings['port'] ) ) $seleniumSettings['port'] = '4444'; + if ( !isset( $seleniumSettings['wikiUrl'] ) ) $seleniumSettings['wikiUrl'] = 'http://localhost'; + if ( !isset( $seleniumSettings['username'] ) ) $seleniumSettings['username'] = ''; + if ( !isset( $seleniumSettings['userPassword'] ) ) $seleniumSettings['userPassword'] = ''; + if ( !isset( $seleniumSettings['testBrowser'] ) ) $seleniumSettings['testBrowser'] = 'firefox'; + $this->selenium = new Selenium( ); - $this->selenium->setUrl( $this->getOption( 'url', $wgServer . $wgScriptPath ) ); - $this->selenium->setBrowser( $this->getOption( 'browser', 'firefox' ) ); - $this->selenium->setPort( $this->getOption( 'port', 4444 ) ); - $this->selenium->setHost( $this->getOption( 'host', 'localhost' ) ); - $this->selenium->setUser( $this->getOption( 'user', 'WikiSysop' ) ); - $this->selenium->setPass( $this->getOption( 'pass', 'Password' ) ); + $this->selenium->setAvailableBrowsers( $seleniumBrowsers ); + $this->selenium->setUrl( $this->getOption( 'wikiUrl', $seleniumSettings['wikiUrl'] ) ); + $this->selenium->setBrowser( $this->getOption( 'testBrowser', $seleniumSettings['testBrowser'] ) ); + $this->selenium->setPort( $this->getOption( 'port', $seleniumSettings['port'] ) ); + $this->selenium->setHost( $this->getOption( 'host', $seleniumSettings['host'] ) ); + $this->selenium->setUser( $this->getOption( 'username', $seleniumSettings['username'] ) ); + $this->selenium->setPass( $this->getOption( 'userPassword', $seleniumSettings['userPassword'] ) ); $this->selenium->setVerbose( $this->hasOption( 'verbose' ) ); if( $this->hasOption( 'list-browsers' ) ) { @@ -102,7 +135,7 @@ class SeleniumTester extends Maintenance { $logger = new SeleniumTestConsoleLogger; $this->selenium->setLogger( $logger ); - $this->runTests( ); + $this->runTests( $seleniumTestSuites ); } } diff --git a/maintenance/tests/phpunit/includes/SeleniumConfigurationTest.php b/maintenance/tests/phpunit/includes/SeleniumConfigurationTest.php new file mode 100644 index 0000000000..5007828f37 --- /dev/null +++ b/maintenance/tests/phpunit/includes/SeleniumConfigurationTest.php @@ -0,0 +1,174 @@ + '*firefox', + 'iexplorer' => '*iexploreproxy', + 'chrome' => '*chrome' + ); + /* + * Array of expected selenium settings from $testConfig0 + */ + private $testSettings0 = array( + 'host' => 'localhost', + 'port' => 'foobarr', + 'wikiUrl' => 'http://localhost/deployment', + 'username' => 'xxxxxxx', + 'userPassword' => '', + 'testBrowser' => 'chrome' + ); + /* + * Array of expected testSuites from $testConfig0 + */ + private $testSuites0 = array( + 'SimpleSeleniumTestSuite' => 'maintenance/tests/selenium/SimpleSeleniumTestSuite.php', + 'TestSuiteName' => 'testSuitePath' + ); + + + /* + * Another sample selenium settings file contents + */ + private $testConfig1 = +' +[SeleniumSettings] +host = "localhost" +testBrowser = "firefox" +'; + /* + * Expected browsers from $testConfig1 + */ + private $testBrowsers1 = null; + /* + * Expected selenium settings from $testConfig1 + */ + private $testSettings1 = array( + 'host' => 'localhost', + 'port' => null, + 'wikiUrl' => null, + 'username' => null, + 'userPassword' => null, + 'testBrowser' => 'firefox' + ); + /* + * Expected test suites from $testConfig1 + */ + private $testSuites1 = null; + + + /* + * Clean up the temporary file used to sore the selenium settings. + */ + public function tearDown() { + if ( strlen( $this->tempFileName ) > 0 ) { + unlink( $this->tempFileName ); + unset( $this->tempFileName ); + } + parent::tearDown(); + } + + /** + * @expectedException MWException + * @group SeleniumFramework + * This test will throw warnings unless you have the following setting in your php.ini + * allow_call_time_pass_reference = On + */ + public function testErrorOnMissingConfigFile() { + $seleniumSettings; + $seleniumBrowsers; + $seleniumTestSuites; + + SeleniumTestConfig::getSeleniumSettings(&$seleniumSettings, + &$seleniumBrowsers, + &$seleniumTestSuites, + "Some_fake_settings_file.ini" ); + } + + /** + * @group SeleniumFramework + * @dataProvider sampleConfigs + * This test will throw warnings unless you have the following setting in your php.ini + * allow_call_time_pass_reference = On + */ + public function testgetSeleniumSettings($sampleConfig, $expectedSettings, $expectedBrowsers, $expectedSuites ) { + //print "SAMPLE_CONFIG " . $sampleConfig . "\n\n"; + $this->writeToTempFile( $sampleConfig ); + $seleniumSettings; + $seleniumBrowsers; + $seleniumTestSuites; + + SeleniumTestConfig::getSeleniumSettings(&$seleniumSettings, + &$seleniumBrowsers, + &$seleniumTestSuites, + $this->tempFileName ); + + + $this->assertEquals($seleniumSettings, $expectedSettings, + "The selenium settings for the following test configuration was not retrieved correctly" . $sampleConfig + ); + $this->assertEquals($seleniumBrowsers, $expectedBrowsers, + "The available browsers for the following test configuration was not retrieved correctly" . $sampleConfig + ); + $this->assertEquals($seleniumTestSuites, $expectedSuites, + "The test suites for the following test configuration was not retrieved correctly" . $sampleConfig + ); + + + } + + /* + * create a temp file and write text to it. + * @param $testToWrite the text to write to the temp file + */ + private function writeToTempFile($textToWrite) { + $this->tempFileName = tempnam(sys_get_temp_dir(), 'test_settings.'); + $tempFile = fopen( $this->tempFileName, "w" ); + fwrite($tempFile , $textToWrite); + fclose($tempFile); + } + + /* + * Returns an array containing: + * The contents of the selenium cingiguration ini file + * The expected selenium configuration array that getSeleniumSettings should return + * The expected available browsers array that getSeleniumSettings should return + * The expected test suites arrya that getSeleniumSettings should return + */ + public function sampleConfigs() { + return array( + array($this->testConfig0, $this->testSettings0, $this->testBrowsers0, $this->testSuites0 ), + array($this->testConfig1, $this->testSettings1, $this->testBrowsers1, $this->testSuites1 ) + ); + } + + +} \ No newline at end of file diff --git a/maintenance/tests/selenium/Selenium.php b/maintenance/tests/selenium/Selenium.php index d0521a00bb..925d335c09 100644 --- a/maintenance/tests/selenium/Selenium.php +++ b/maintenance/tests/selenium/Selenium.php @@ -8,6 +8,7 @@ require( 'Testing/Selenium.php' ); class Selenium { protected static $_instance = null; + public $isStarted = false; public $tester; @@ -56,27 +57,15 @@ class Selenium { $this->isStarted = false; } - protected function setupBrowsers() { - /** - * @todo This needs to be replaced with something not hard - * coded. This would be entries in a .ini file or - * screen-scraping - * http://grid.tesla.usability.wikimedia.org:4444/console for - * example. - */ - return array( - 'firefox' => 'Firefox 3.5 on Linux', - 'iexplorer' => '*iexploreproxy', - 'chrome' => '*googlechrome', - ); - } - public function login() { + if ( strlen( $this->user ) == 0 ) { + return; + } $this->open( self::$url . '/index.php?title=Special:Userlogin' ); $this->type( 'wpName1', $this->user ); $this->type( 'wpPassword1', $this->pass ); $this->click( "//input[@id='wpLoginAttempt']" ); - $this->waitForPageToLoad( 5000 ); + $this->waitForPageToLoad( 10000 ); // after login we redirect to the main page. So check whether the "Prefernces" top menu item exists $value = $this->isElementPresent( "//li[@id='pt-preferences']" ); @@ -138,16 +127,21 @@ class Selenium { public function setVerbose( $verbose ) { $this->verbose = $verbose; } + + public function setAvailableBrowsers( $availableBrowsers ) { + $this->browsers = $availableBrowsers; + } public function setBrowser( $b ) { - $browsers = $this->setupBrowsers(); - - - if ( !isset( $browsers[$b] ) ) { + if ( !isset( $this->browsers[$b] ) ) { throw new MWException( "Invalid Browser: $b.\n" ); } - $this->browser = $browsers[$b]; + $this->browser = $this->browsers[$b]; + } + + public function getAvailableBrowsers() { + return $this->browsers; } public function __call( $name, $args ) { diff --git a/maintenance/tests/selenium/SeleniumTestConfig.php b/maintenance/tests/selenium/SeleniumTestConfig.php new file mode 100644 index 0000000000..d3f0e4a7e1 --- /dev/null +++ b/maintenance/tests/selenium/SeleniumTestConfig.php @@ -0,0 +1,46 @@ +logger->write( 'Testsuite ' . $suite->getName() . ' started.' ); $this->tests_ok = 0; + $this->tests_failed = 0; } public function endTestSuite( PHPUnit_Framework_TestSuite $suite ) { - $this->logger->write( - 'Testsuite ' . $suite->getName() . ' ended. OK: ' . - $this->tests_ok . ' Failed: ' . $this->tests_failed - ); + $this->logger->write('Testsuite ' . $suite->getName() . ' ended.' ); + if ( $this->tests_ok > 0 || $this->tests_failed > 0 ) { + $this->logger->write( ' OK: ' . $this->tests_ok . ' Failed: ' . $this->tests_failed ); + } + $this->tests_ok = 0; + $this->tests_failed = 0; } public function statusMessage( $message ) { diff --git a/maintenance/tests/selenium/SeleniumTestSuite.php b/maintenance/tests/selenium/SeleniumTestSuite.php index 0b31c833af..88bd9b1fc5 100644 --- a/maintenance/tests/selenium/SeleniumTestSuite.php +++ b/maintenance/tests/selenium/SeleniumTestSuite.php @@ -1,6 +1,6 @@ isSetUp = true; $this->selenium = Selenium::getInstance(); $this->selenium->start(); + //$this->selenium->open( $this->selenium->getUrl() . '/index.php?setupTestSuite=' . $this->getName() ); $this->login(); - // $this->loadPage( 'Testpage', 'edit' ); } public function tearDown() { + //$this->selenium->open( $this->selenium->getUrl() . '/index.php?clearTestSuite=' . $this->getName() ); $this->selenium->stop(); } diff --git a/maintenance/tests/selenium/SimpleSeleniumTestCase.php b/maintenance/tests/selenium/SimpleSeleniumTestCase.php index 848cba82f8..ea44e7357b 100644 --- a/maintenance/tests/selenium/SimpleSeleniumTestCase.php +++ b/maintenance/tests/selenium/SimpleSeleniumTestCase.php @@ -2,13 +2,9 @@ class SimpleSeleniumTestCase extends SeleniumTestCase { - public function __construct( $name = 'Basic selenium test') { - parent::__construct( $name ); - } - - public function runTest() + public function testBasic() { - $this->open( Selenium::getBaseUrl() . + $this->open( $this->getUrl() . '/index.php?title=Selenium&action=edit' ); $this->type( "wpTextbox1", "This is a basic test" ); $this->click( "wpPreview" ); @@ -20,5 +16,17 @@ class SimpleSeleniumTestCase extends SeleniumTestCase $this->assertEquals( $correct, true ); } + + /* + * Needs the following in your LocalConfig or an alternative method of configuration (coming soon) + * require_once( "$IP/extensions/UsabilityInitiative/Vector/Vector.php" ); + * $wgDefaultSkin='vector'; + */ + public function testGlobalVariable1() + { + $this->open( $this->getUrl() . '/index.php?&action=purge' ); + $bodyClass = $this->getAttribute( "//body/@class" ); + $this-> assertContains('skin-vector', $bodyClass, 'Vector skin not set'); + } } diff --git a/maintenance/tests/selenium/SimpleSeleniumTestSuite.php b/maintenance/tests/selenium/SimpleSeleniumTestSuite.php index dc7d20d4a3..bad4ca376a 100644 --- a/maintenance/tests/selenium/SimpleSeleniumTestSuite.php +++ b/maintenance/tests/selenium/SimpleSeleniumTestSuite.php @@ -1,15 +1,13 @@