From 15ee6985ffa18a2ee45e67e232bedb8fa66dbf59 Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Wed, 31 Jan 2024 14:33:31 +0100 Subject: [PATCH] Allow selenium image configuration --- docs/CHANGELOG.md | 3 ++ docs/CLI.md | 14 +++++++-- src/Command/BehatCommand.php | 47 +++++++++++++--------------- tests/Command/BehatCommandTest.php | 42 ++++++++++++++++++++++++- tests/Command/PHPUnitCommandTest.php | 5 ++- tests/MoodleTestCase.php | 6 +++- 6 files changed, 86 insertions(+), 31 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 01ec3260..9989a77a 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -10,6 +10,9 @@ The format of this change log follows the advice given at [Keep a CHANGELOG](htt ## [Unreleased] +### Added +- `--selenium` option 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..09b54c2d 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_NONE, '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 getenv('MOODLE_BEHAT_SELENIUM_CHROME_IMAGE') ?: 'selenium/standalone-chrome:3'; + } + + if ($this->usesLegacyPhpWebdriver()) { + return 'selenium/standalone-firefox:2.53.1'; + } + + return getenv('MOODLE_BEHAT_SELENIUM_FIREFOX_IMAGE') ?: 'selenium/standalone-firefox:3'; + } } diff --git a/tests/Command/BehatCommandTest.php b/tests/Command/BehatCommandTest.php index 88751df4..9cc897ea 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,43 @@ 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 testExecuteWithSeleniumImageChromeEnv() + { + putenv('MOODLE_BEHAT_SELENIUM_IMAGE='); + putenv('MOODLE_BEHAT_SELENIUM_CHROME_IMAGE=selenium/standalone-chrome:4'); + + $commandTester = $this->executeCommand(null, null, ['--start-servers' => true, '--profile' => 'chrome']); + $this->assertSame(0, $commandTester->getStatusCode()); + $this->assertMatchesRegularExpression('/selenium\/standalone-chrome:4/', $this->allCmds[1]); + } + + public function testExecuteWithSeleniumImageFirefoxEnv() + { + putenv('MOODLE_BEHAT_SELENIUM_IMAGE='); + putenv('MOODLE_BEHAT_SELENIUM_FIREFOX_IMAGE=selenium/standalone-firefox:4'); + 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:4/', $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);