diff --git a/CHANGELOG.md b/CHANGELOG.md index 649e9bfe..286a560a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,12 @@ ## Unreleased +### Added +- Check if browser given to `run-tests` command is supported (this helps avoiding typos, interchange of browser and environment etc.). + ### Changed - The `logs/results.xml` could now be also accessed locally (the XSLT is now embedded right into the file, so we don't encounter same-origin policy problems). -- Use tagged version 0.6.0 of [php-webdriver](https://github.com/facebook/php-webdriver) +- Use tagged version 0.6.0 of [php-webdriver](https://github.com/facebook/php-webdriver). ### Fixed - Properly trigger PHPUnit colored (ANSI) mode when Steward itself is in ANSI mode. diff --git a/src-tests/Console/Command/RunTestsCommandTest.php b/src-tests/Console/Command/RunTestsCommandTest.php index 6596cbe8..e30ec9bc 100644 --- a/src-tests/Console/Command/RunTestsCommandTest.php +++ b/src-tests/Console/Command/RunTestsCommandTest.php @@ -9,6 +9,7 @@ use Lmc\Steward\Process\ProcessSetCreator; use Lmc\Steward\Selenium\SeleniumServerAdapter; use Symfony\Component\Console\Application; +use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\Finder\Finder; @@ -69,13 +70,20 @@ public function testShouldFailWithoutEnvironmentSpecified() /** * @dataProvider directoryOptionsProvider * @param string $directoryOption Passed path type option - * @param string $errorBeginning Beginning of error message + * @param string $errorBeginning Beginning of exception message */ - public function testShouldStopIfAnyRequiredDirectoryIsNotAccessible($directoryOption, $errorBeginning) + public function testShouldThrowExceptionIfAnyRequiredDirectoryIsNotAccessible($directoryOption, $errorBeginning) { $seleniumAdapterMock = $this->getSeleniumAdapterMock(); $this->command->setSeleniumAdapter($seleniumAdapterMock); + $expectedError = sprintf( + '%s, make sure it is accessible or define your own path using --%s option', + $errorBeginning, + $directoryOption + ); + $this->setExpectedException('\RuntimeException', $expectedError); + $this->tester->execute( [ 'command' => $this->command->getName(), @@ -84,15 +92,6 @@ public function testShouldStopIfAnyRequiredDirectoryIsNotAccessible($directoryOp '--' . $directoryOption => '/not/accessible' ] ); - - $expectedError = sprintf( - '%s, make sure it is accessible or define your own path using --%s option', - $errorBeginning, - $directoryOption - ); - - $this->assertContains($expectedError, $this->tester->getDisplay()); - $this->assertSame(1, $this->tester->getStatusCode()); } /** @@ -107,6 +106,70 @@ public function directoryOptionsProvider() ]; } + public function testShouldOutputAssembledPathsToDirectoriesInDebugMode() + { + $seleniumAdapterMock = $this->getSeleniumAdapterMock(); + $this->command->setSeleniumAdapter($seleniumAdapterMock); + + $this->tester->execute( + [ + 'command' => $this->command->getName(), + 'environment' => 'staging', + 'browser' => 'firefox', + '--tests-dir' => __DIR__ . '/Fixtures/DummyTests', + '--pattern' => 'NotExisting.foo' // so the test stops execution + ], + ['verbosity' => OutputInterface::VERBOSITY_DEBUG] + ); + + $output = $this->tester->getDisplay(); + $this->assertContains('Base path to fixtures results: ' . realpath(__DIR__) . '/Fixtures/tests', $output); + $this->assertContains('Path to logs: ' . realpath(__DIR__) . '/Fixtures/logs', $output); + $this->assertContains(' - in directory "' . realpath(__DIR__) . '/Fixtures/DummyTests"', $output); + $this->assertSame(1, $this->tester->getStatusCode()); + } + + /** + * @dataProvider browserNameProvider + * @param string $browserName + * @param bool $shouldThrowException + */ + public function testShouldThrowExceptionIfUnsupportedBrowserSelected($browserName, $shouldThrowException) + { + if ($shouldThrowException) { + $this->setExpectedException('\RuntimeException', 'Browser "' . $browserName . '" is not supported'); + } + + $seleniumAdapterMock = $this->getSeleniumAdapterMock(); + $this->command->setSeleniumAdapter($seleniumAdapterMock); + + $this->tester->execute( + ['command' => $this->command->getName(), 'environment' => 'prod', 'browser' => $browserName] + ); + + if (!$shouldThrowException) { + $output = $this->tester->getDisplay(); + $this->assertContains('Browser: ' . strtolower($browserName), $output); + $this->assertContains('No testcases found, exiting.', $output); + } + } + + /** + * @return array + */ + public function browserNameProvider() + { + return [ + // $browserName, $shouldThrowException + 'firefox is supported' => ['firefox', false], + 'chrome is supported' => ['chrome', false], + 'phantomjs is supported' => ['phantomjs', false], + 'browser name is case insensitive' => ['FIREFOX', false], + 'not supported browser' => ['mosaic', true], + 'unprintable character in browser name' => ['firefox​', true], + ]; + } + public function testShouldStopIfServerIsNotResponding() { $seleniumAdapterMock = $this->getMockBuilder(SeleniumServerAdapter::class) diff --git a/src/Console/Command/RunTestsCommand.php b/src/Console/Command/RunTestsCommand.php index e4f54a88..4c871a79 100644 --- a/src/Console/Command/RunTestsCommand.php +++ b/src/Console/Command/RunTestsCommand.php @@ -28,6 +28,14 @@ class RunTestsCommand extends Command protected $seleniumAdapter; /** @var ProcessSetCreator */ protected $processSetCreator; + /** @var array */ + protected $supportedBrowsers = [ + \WebDriverBrowserType::FIREFOX, + \WebDriverBrowserType::CHROME, + \WebDriverBrowserType::IE, + \WebDriverBrowserType::SAFARI, + \WebDriverBrowserType::PHANTOMJS, + ]; const ARGUMENT_ENVIRONMENT = 'environment'; const ARGUMENT_BROWSER = 'browser'; @@ -130,14 +138,15 @@ protected function configure() } /** - * Execute command + * Initialize, check arguments and options values etc. * * @param InputInterface $input * @param OutputInterface $output - * @return int|null|void */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function initialize(InputInterface $input, OutputInterface $output) { + parent::initialize($input, $output); + $output->writeln( sprintf( 'Steward %s is running the tests...%s', @@ -146,11 +155,33 @@ protected function execute(InputInterface $input, OutputInterface $output) ) ); - $output->writeln(sprintf('Browser: %s', $input->getArgument(self::ARGUMENT_BROWSER))); + // If browser name or env is empty, ends initialization and let the Console/Command fail on input validation + if (empty($input->getArgument(self::ARGUMENT_BROWSER)) + || empty($input->getArgument(self::ARGUMENT_ENVIRONMENT)) + ) { + return; + } + + // Browser name is case insensitive, normalize it to lower case + $input->setArgument(self::ARGUMENT_BROWSER, strtolower($input->getArgument(self::ARGUMENT_BROWSER))); + $browser = $input->getArgument(self::ARGUMENT_BROWSER); + + // Check if browser is supported + if (!in_array($browser, $this->supportedBrowsers)) { + throw new \RuntimeException( + sprintf( + 'Browser "%s" is not supported (use one of: %s)', + $browser, + implode(', ', $this->supportedBrowsers) + ) + ); + } + + $output->writeln(sprintf('Browser: %s', $browser)); $output->writeln(sprintf('Environment: %s', $input->getArgument(self::ARGUMENT_ENVIRONMENT))); - // Tests directories exists - $testDirectoriesResult = $this->testDirectories( + // Check if directories exists + $this->testDirectories( $input, $output, [ @@ -159,9 +190,6 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->getDefinition()->getOption(self::OPTION_FIXTURES_DIR), ] ); - if (!$testDirectoriesResult) { - return 1; - } if ($output->isDebug()) { $output->writeln( @@ -174,7 +202,17 @@ protected function execute(InputInterface $input, OutputInterface $output) sprintf('Publish results: %s', ($input->getOption(self::OPTION_PUBLISH_RESULTS)) ? 'yes' : 'no') ); } + } + /** + * Execute command + * + * @param InputInterface $input + * @param OutputInterface $output + * @return int|null|void + */ + protected function execute(InputInterface $input, OutputInterface $output) + { $this->getDispatcher()->dispatch( CommandEvents::RUN_TESTS_INIT, new ExtendedConsoleEvent($this, $input, $output) @@ -412,7 +450,7 @@ protected function getProcessOutput(Process $process) * @param InputInterface $input * @param OutputInterface $output * @param InputOption[] $dirs Option defining directories - * @return bool + * @throws \RuntimeException Thrown when directory is not accessible */ protected function testDirectories(InputInterface $input, OutputInterface $output, array $dirs) { @@ -421,18 +459,15 @@ protected function testDirectories(InputInterface $input, OutputInterface $outpu $currentValue = $input->getOption($dir->getName()); if ($currentValue === false || realpath($currentValue) === false) { - $output->writeln(sprintf( - '%s does not exist, make sure it is accessible or define your own path using %s' - . ' option', - $dir->getDescription(), - '--' . $dir->getName() - )); - - return false; + throw new \RuntimeException( + sprintf( + '%s does not exist, make sure it is accessible or define your own path using %s option', + $dir->getDescription(), + '--' . $dir->getName() + ) + ); } } - - return true; } /** diff --git a/src/Listener/WebdriverListener.php b/src/Listener/WebDriverListener.php similarity index 97% rename from src/Listener/WebdriverListener.php rename to src/Listener/WebDriverListener.php index 164b4ff1..ceb6b505 100644 --- a/src/Listener/WebdriverListener.php +++ b/src/Listener/WebDriverListener.php @@ -16,7 +16,7 @@ * If taking screenshot using addFailure(), tearDown() would have already been called and the * browser would be closed. */ -class WebdriverListener extends \PHPUnit_Framework_BaseTestListener +class WebDriverListener extends \PHPUnit_Framework_BaseTestListener { const NO_BROWSER_ANNOTATION = 'noBrowser'; @@ -32,7 +32,7 @@ public function startTest(\PHPUnit_Framework_Test $test) $config = ConfigProvider::getInstance(); - // Initialize NullWebdriver if self::NO_BROWSER_ANNOTATION is used on testcase class or test method + // Initialize NullWebDriver if self::NO_BROWSER_ANNOTATION is used on testcase class or test method $testCaseAnnotations = AnnotationsParser::getAll(new \ReflectionClass($test)); $testAnnotations = AnnotationsParser::getAll(new \ReflectionMethod($test, $test->getName(false))); @@ -41,7 +41,7 @@ public function startTest(\PHPUnit_Framework_Test $test) ) { $test->wd = new NullWebDriver(); $test->log( - 'Initializing Null webdriver for "%s::%s" (@%s annotation used %s)', + 'Initializing Null WebDriver for "%s::%s" (@%s annotation used %s)', get_class($test), $test->getName(), self::NO_BROWSER_ANNOTATION, @@ -88,7 +88,7 @@ public function endTest(\PHPUnit_Framework_Test $test, $time) if ($test->wd instanceof \RemoteWebDriver) { $test->log( - 'Destroying "%s" webdriver for "%s::%s" (session %s)', + 'Destroying "%s" WebDriver for "%s::%s" (session %s)', ConfigProvider::getInstance()->browserName, get_class($test), $test->getName(), diff --git a/src/Process/ProcessSet.php b/src/Process/ProcessSet.php index ca7e44ec..078b6c59 100644 --- a/src/Process/ProcessSet.php +++ b/src/Process/ProcessSet.php @@ -39,7 +39,7 @@ class ProcessSet implements \Countable const PROCESS_RESULT_PASSED = 'passed'; /** Process failed - some tests have failed or are broken */ const PROCESS_RESULT_FAILED = 'failed'; - /** Process fatally failed (PHP fatal error occurred - eg. no webdriver available) */ + /** Process fatally failed (PHP fatal error occurred - eg. no WebDriver available) */ const PROCESS_RESULT_FATAL = 'fatal'; /** @var array List of possible process statuses */ diff --git a/src/phpunit.xml b/src/phpunit.xml index 93c72747..0f1bfb56 100644 --- a/src/phpunit.xml +++ b/src/phpunit.xml @@ -5,7 +5,7 @@ > - +