diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 3d692c03..c3757c97 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -9,6 +9,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). The format of this change log follows the advice given at [Keep a CHANGELOG](http://keepachangelog.com). ## [Unreleased] + +### Added +- `--selenium` option or `MOODLE_BEHAT_SELENIUM_IMAGE` env variable to `behat` to specify Selenium Docker image. + ### Changed - Updated all uses of `actions/checkout` from `v3` (using node 16) to `v4` (using node 20), because [actions using node 16 are deprecated](https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20/) and will stop working in the future. - ACTION SUGGESTED: In order to avoid the node 16 deprecation warnings, update your workflows to use `actions/checkout@v4`. diff --git a/docs/CLI.md b/docs/CLI.md index df8a3bb5..5fedbd7d 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -241,7 +241,7 @@ Run Behat on a plugin ### Usage -* `behat [-m|--moodle MOODLE] [-p|--profile PROFILE] [--suite SUITE] [--tags TAGS] [--name NAME] [--start-servers] [--auto-rerun AUTO-RERUN] [--dump] [--] ` +* `behat [-m|--moodle MOODLE] [-p|--profile PROFILE] [--suite SUITE] [--tags TAGS] [--name NAME] [--start-servers] [--auto-rerun AUTO-RERUN] [--selenium SELENIUM] [--dump] [--] ` Run Behat on a plugin @@ -327,6 +327,16 @@ Number of times to rerun failures * Is negatable: no * Default: `2` +#### `--selenium` + +Selenium Docker image + +* Accept value: yes +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `NULL` + #### `--dump` Print contents of Behat failure HTML files @@ -2385,4 +2395,4 @@ Do not ask any interactive question * Is value required: no * Is multiple: no * Is negatable: no -* Default: `false` \ No newline at end of file +* Default: `false` diff --git a/src/Command/BehatCommand.php b/src/Command/BehatCommand.php index 3da0074f..6a7ab2ab 100644 --- a/src/Command/BehatCommand.php +++ b/src/Command/BehatCommand.php @@ -25,25 +25,6 @@ class BehatCommand extends AbstractMoodleCommand { use ExecuteTrait; - /** - * Selenium legacy Firefox image. - */ - private string $seleniumLegacyFirefoxImage = 'selenium/standalone-firefox:2.53.1'; - - /** - * Selenium standalone Firefox image. - * - * @todo: Make this configurable. - */ - private string $seleniumFirefoxImage = 'selenium/standalone-firefox:3'; - - /** - * Selenium standalone Chrome image. - * - * @todo: Make this configurable. - */ - private string $seleniumChromeImage = 'selenium/standalone-chrome:3'; - /** * Wait this many microseconds for Selenium server to start/stop. * @@ -65,6 +46,7 @@ protected function configure(): void ->addOption('name', null, InputOption::VALUE_REQUIRED, 'Behat name option to use', '') ->addOption('start-servers', null, InputOption::VALUE_NONE, 'Start Selenium and PHP servers') ->addOption('auto-rerun', null, InputOption::VALUE_REQUIRED, 'Number of times to rerun failures', 2) + ->addOption('selenium', null, InputOption::VALUE_REQUIRED, 'Selenium Docker image') ->addOption('dump', null, InputOption::VALUE_NONE, 'Print contents of Behat failure HTML files') ->setDescription('Run Behat on a plugin'); } @@ -150,13 +132,7 @@ private function startServerProcesses(InputInterface $input): void $profile = getenv('MOODLE_BEHAT_DEFAULT_BROWSER') ?: (getenv('MOODLE_APP') ? 'chrome' : 'firefox'); } - if ($profile === 'chrome') { - $image = $this->seleniumChromeImage; - } elseif ($this->usesLegacyPhpWebdriver()) { - $image = $this->seleniumLegacyFirefoxImage; - } else { - $image = $this->seleniumFirefoxImage; - } + $image = $this->getSeleniumImage($input, $profile); $cmd = [ 'docker', @@ -226,4 +202,23 @@ private function usesLegacyPhpWebdriver(): bool return strpos(file_get_contents($composerlock), 'instaclick/php-webdriver') !== false; } + + private function getSeleniumImage(InputInterface $input, string $profile): string + { + $image = $input->getOption('selenium') ?: getenv('MOODLE_BEHAT_SELENIUM_IMAGE'); + + if (!empty($image)) { + return $image; + } + + if ($profile === 'chrome') { + return 'selenium/standalone-chrome:3'; + } + + if ($this->usesLegacyPhpWebdriver()) { + return 'selenium/standalone-firefox:2.53.1'; + } + + return 'selenium/standalone-firefox:3'; + } } diff --git a/tests/Command/BehatCommandTest.php b/tests/Command/BehatCommandTest.php index 88751df4..653a61e1 100644 --- a/tests/Command/BehatCommandTest.php +++ b/tests/Command/BehatCommandTest.php @@ -46,7 +46,10 @@ protected function executeCommand($pluginDir = null, $moodleDir = null, array $c $cmdOptions ); $commandTester->execute($cmdOptions); - $this->lastCmd = $command->execute->lastCmd; // We need this for assertions against the command run. + + // We need these for assertions against the commands run. + $this->allCmds = $command->execute->allCmds; + $this->lastCmd = $command->execute->lastCmd; return $commandTester; } @@ -69,6 +72,51 @@ public function testExecuteWithTags() $this->assertDoesNotMatchRegularExpression('/--tags=@local_ci/', $this->lastCmd); } + public function testExecuteWithSeleniumImageOption() + { + $commandTester = $this->executeCommand(null, null, ['--start-servers' => true, '--selenium' => 'seleniarm/standalone-chromium:latest']); + $this->assertSame(0, $commandTester->getStatusCode()); + $this->assertMatchesRegularExpression('/seleniarm\/standalone-chromium:latest/', $this->allCmds[1]); + } + + public function testExecuteWithSeleniumImageEnv() + { + putenv('MOODLE_BEHAT_SELENIUM_IMAGE=seleniarm/standalone-chromium:latest'); + + $commandTester = $this->executeCommand(null, null, ['--start-servers' => true]); + $this->assertSame(0, $commandTester->getStatusCode()); + $this->assertMatchesRegularExpression('/seleniarm\/standalone-chromium:latest/', $this->allCmds[1]); + } + + public function testExecuteWithChromeProfile() + { + putenv('MOODLE_BEHAT_SELENIUM_IMAGE='); + + $commandTester = $this->executeCommand(null, null, ['--start-servers' => true, '--profile' => 'chrome']); + $this->assertSame(0, $commandTester->getStatusCode()); + $this->assertMatchesRegularExpression('/selenium\/standalone-chrome:3/', $this->allCmds[1]); + } + + public function testExecuteWithFirefoxProfile() + { + putenv('MOODLE_BEHAT_SELENIUM_IMAGE='); + file_put_contents("{$this->moodleDir}/composer.lock", ''); + + $commandTester = $this->executeCommand(null, null, ['--start-servers' => true, '--profile' => 'firefox']); + $this->assertSame(0, $commandTester->getStatusCode()); + $this->assertMatchesRegularExpression('/selenium\/standalone-firefox:3/', $this->allCmds[1]); + } + + public function testExecuteWithLegacyFirefoxProfile() + { + putenv('MOODLE_BEHAT_SELENIUM_IMAGE='); + file_put_contents("{$this->moodleDir}/composer.lock", 'instaclick/php-webdriver'); + + $commandTester = $this->executeCommand(null, null, ['--start-servers' => true, '--profile' => 'firefox']); + $this->assertSame(0, $commandTester->getStatusCode()); + $this->assertMatchesRegularExpression('/selenium\/standalone-firefox:2.53.1/', $this->allCmds[1]); + } + public function testExecuteWithName() { $featureName = 'With "double quotes" and \'single quotes\''; diff --git a/tests/Command/PHPUnitCommandTest.php b/tests/Command/PHPUnitCommandTest.php index 959a3a34..1d3f7769 100644 --- a/tests/Command/PHPUnitCommandTest.php +++ b/tests/Command/PHPUnitCommandTest.php @@ -45,7 +45,10 @@ protected function executeCommand($pluginDir = null, $moodleDir = null, array $c $cmdOptions ); $commandTester->execute($cmdOptions); - $this->lastCmd = $command->execute->lastCmd; // We need this for assertions against the command run. + + // We need these for assertions against the commands run. + $this->allCmds = $command->execute->allCmds; + $this->lastCmd = $command->execute->lastCmd; return $commandTester; } diff --git a/tests/MoodleTestCase.php b/tests/MoodleTestCase.php index 00e269e3..26501f5f 100644 --- a/tests/MoodleTestCase.php +++ b/tests/MoodleTestCase.php @@ -16,7 +16,10 @@ class MoodleTestCase extends FilesystemTestCase { protected string $moodleDir; protected string $pluginDir; - protected string $lastCmd = ''; // We need this for assertions against the command run. + + // We need these for assertions against the commands run. + protected array $allCmds = []; + protected string $lastCmd = ''; protected function setUp(): void { @@ -24,6 +27,7 @@ protected function setUp(): void $this->moodleDir = $this->tempDir; $this->pluginDir = $this->tempDir . '/local/ci'; + $this->allCmds = []; $this->lastCmd = ''; $this->fs->mirror(__DIR__ . '/Fixture/moodle', $this->moodleDir);