From e91d3e4be4ff702d227443ed6acdcdf94acf4b57 Mon Sep 17 00:00:00 2001 From: Tugdual Saunier Date: Tue, 4 Mar 2025 11:33:09 +0100 Subject: [PATCH 1/5] fix: add basic tests for FirefoxManager --- tests/ProcessManager/FirefoxManagerTest.php | 71 +++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 tests/ProcessManager/FirefoxManagerTest.php diff --git a/tests/ProcessManager/FirefoxManagerTest.php b/tests/ProcessManager/FirefoxManagerTest.php new file mode 100644 index 00000000..cca576a6 --- /dev/null +++ b/tests/ProcessManager/FirefoxManagerTest.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Symfony\Component\Panther\Tests\ProcessManager; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Panther\Exception\RuntimeException; +use Symfony\Component\Panther\ProcessManager\FirefoxManager; + +/** + * @author Tugdual Saunier + */ +class FirefoxManagerTest extends TestCase +{ + public function testRun(): void + { + $manager = new FirefoxManager(); + $client = $manager->start(); + $this->assertNotEmpty($client->getCurrentURL()); + $manager->quit(); + } + + public function testAlreadyRunning(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The port 4444 is already in use.'); + + $driver1 = new FirefoxManager(); + $driver1->start(); + + $driver2 = new FirefoxManager(); + try { + $driver2->start(); + } finally { + $driver1->quit(); + } + } + + public function testNonDefaultPort(): void + { + $manager = new FirefoxManager(null, null, ['port' => 4445]); + $client = $manager->start(); + $this->assertNotEmpty($client->getCurrentURL()); + $manager->quit(); + } + + public function testMultipleInstances(): void + { + $driver1 = new FirefoxManager(); + $client1 = $driver1->start(); + + $driver2 = new FirefoxManager(null, null, ['port' => 4445]); + $client2 = $driver2->start(); + + $this->assertNotEmpty($client1->getCurrentURL()); + $this->assertNotEmpty($client2->getCurrentURL()); + + $driver1->quit(); + $driver2->quit(); + } +} From c4ec484395c08015675835129b2f87c9829b8c09 Mon Sep 17 00:00:00 2001 From: Tugdual Saunier Date: Tue, 4 Mar 2025 16:29:35 +0100 Subject: [PATCH 2/5] refactor: make it testable --- src/ProcessManager/FirefoxManager.php | 59 +++++++++++++++------------ 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/src/ProcessManager/FirefoxManager.php b/src/ProcessManager/FirefoxManager.php index 370460cf..8ddd14ac 100644 --- a/src/ProcessManager/FirefoxManager.php +++ b/src/ProcessManager/FirefoxManager.php @@ -54,33 +54,7 @@ public function start(): WebDriver $this->waitUntilReady($this->process, $url.$this->options['path'], 'firefox'); } - $firefoxOptions = []; - if (isset($_SERVER['PANTHER_FIREFOX_BINARY'])) { - $firefoxOptions['binary'] = $_SERVER['PANTHER_FIREFOX_BINARY']; - } - if ($this->arguments) { - $firefoxOptions['args'] = $this->arguments; - } - - $capabilities = DesiredCapabilities::firefox(); - $capabilities->setCapability('moz:firefoxOptions', $firefoxOptions); - - // Prefer reduced motion, see https://developer.mozilla.org/fr/docs/Web/CSS/@media/prefers-reduced-motion - /** @var FirefoxOptions|array $firefoxOptions */ - $firefoxOptions = $capabilities->getCapability('moz:firefoxOptions') ?? []; - $firefoxOptions = $firefoxOptions instanceof FirefoxOptions ? $firefoxOptions->toArray() : $firefoxOptions; - if (!filter_var($_SERVER['PANTHER_NO_REDUCED_MOTION'] ?? false, \FILTER_VALIDATE_BOOLEAN)) { - $firefoxOptions['prefs']['ui.prefersReducedMotion'] = 1; - } else { - $firefoxOptions['prefs']['ui.prefersReducedMotion'] = 0; - } - $capabilities->setCapability('moz:firefoxOptions', $firefoxOptions); - - foreach ($this->options['capabilities'] as $capability => $value) { - $capabilities->setCapability($capability, $value); - } - - return RemoteWebDriver::create($url, $capabilities, $this->options['connection_timeout_in_ms'] ?? null, $this->options['request_timeout_in_ms'] ?? null); + return RemoteWebDriver::create($url, $this->buildCapabilities(), $this->options['connection_timeout_in_ms'] ?? null, $this->options['request_timeout_in_ms'] ?? null); } public function quit(): void @@ -123,6 +97,37 @@ private function getDefaultArguments(): array return $args; } + private function buildCapabilities(): DesiredCapabilities + { + $firefoxOptions = []; + if (isset($_SERVER['PANTHER_FIREFOX_BINARY'])) { + $firefoxOptions['binary'] = $_SERVER['PANTHER_FIREFOX_BINARY']; + } + if ($this->arguments) { + $firefoxOptions['args'] = $this->arguments; + } + + $capabilities = DesiredCapabilities::firefox(); + $capabilities->setCapability('moz:firefoxOptions', $firefoxOptions); + + // Prefer reduced motion, see https://developer.mozilla.org/fr/docs/Web/CSS/@media/prefers-reduced-motion + /** @var FirefoxOptions|array $firefoxOptions */ + $firefoxOptions = $capabilities->getCapability('moz:firefoxOptions') ?? []; + $firefoxOptions = $firefoxOptions instanceof FirefoxOptions ? $firefoxOptions->toArray() : $firefoxOptions; + if (!filter_var($_SERVER['PANTHER_NO_REDUCED_MOTION'] ?? false, \FILTER_VALIDATE_BOOLEAN)) { + $firefoxOptions['prefs']['ui.prefersReducedMotion'] = 1; + } else { + $firefoxOptions['prefs']['ui.prefersReducedMotion'] = 0; + } + $capabilities->setCapability('moz:firefoxOptions', $firefoxOptions); + + foreach ($this->options['capabilities'] as $capability => $value) { + $capabilities->setCapability($capability, $value); + } + + return $capabilities; + } + private function getDefaultOptions(): array { return [ From 772a8bea1d443bbb51cb3d2b1935ab099b8e900d Mon Sep 17 00:00:00 2001 From: Tugdual Saunier Date: Tue, 4 Mar 2025 16:55:21 +0100 Subject: [PATCH 3/5] fix: FirefoxManager should keep default capabilities when they are not explicitely set --- src/ProcessManager/FirefoxManager.php | 33 +++++++------ tests/ProcessManager/FirefoxManagerTest.php | 52 +++++++++++++++++++++ 2 files changed, 72 insertions(+), 13 deletions(-) diff --git a/src/ProcessManager/FirefoxManager.php b/src/ProcessManager/FirefoxManager.php index 8ddd14ac..9b39bff8 100644 --- a/src/ProcessManager/FirefoxManager.php +++ b/src/ProcessManager/FirefoxManager.php @@ -99,30 +99,37 @@ private function getDefaultArguments(): array private function buildCapabilities(): DesiredCapabilities { - $firefoxOptions = []; + $capabilities = DesiredCapabilities::firefox(); + $firefoxOptions = $capabilities->getCapability(FirefoxOptions::CAPABILITY); + if (isset($_SERVER['PANTHER_FIREFOX_BINARY'])) { - $firefoxOptions['binary'] = $_SERVER['PANTHER_FIREFOX_BINARY']; + $firefoxOptions->setOption('binary', $_SERVER['PANTHER_FIREFOX_BINARY']); } if ($this->arguments) { - $firefoxOptions['args'] = $this->arguments; + $firefoxOptions->addArguments($this->arguments); } - $capabilities = DesiredCapabilities::firefox(); - $capabilities->setCapability('moz:firefoxOptions', $firefoxOptions); - // Prefer reduced motion, see https://developer.mozilla.org/fr/docs/Web/CSS/@media/prefers-reduced-motion - /** @var FirefoxOptions|array $firefoxOptions */ - $firefoxOptions = $capabilities->getCapability('moz:firefoxOptions') ?? []; - $firefoxOptions = $firefoxOptions instanceof FirefoxOptions ? $firefoxOptions->toArray() : $firefoxOptions; if (!filter_var($_SERVER['PANTHER_NO_REDUCED_MOTION'] ?? false, \FILTER_VALIDATE_BOOLEAN)) { - $firefoxOptions['prefs']['ui.prefersReducedMotion'] = 1; + $firefoxOptions->setPreference('ui.prefersReducedMotion', 1); } else { - $firefoxOptions['prefs']['ui.prefersReducedMotion'] = 0; + $firefoxOptions->setPreference('ui.prefersReducedMotion', 0); } - $capabilities->setCapability('moz:firefoxOptions', $firefoxOptions); foreach ($this->options['capabilities'] as $capability => $value) { - $capabilities->setCapability($capability, $value); + if (FirefoxOptions::CAPABILITY !== $capability) { + $capabilities->setCapability($capability, $value); + continue; + } + + if (isset($value[FirefoxOptions::OPTION_ARGS])) { + $firefoxOptions->addArguments($value[FirefoxOptions::OPTION_ARGS]); + } + if (isset($value[FirefoxOptions::OPTION_PREFS])) { + foreach ($value[FirefoxOptions::OPTION_PREFS] as $preference => $preferenceValue) { + $firefoxOptions->setPreference($preference, $preferenceValue); + } + } } return $capabilities; diff --git a/tests/ProcessManager/FirefoxManagerTest.php b/tests/ProcessManager/FirefoxManagerTest.php index cca576a6..5a5049bd 100644 --- a/tests/ProcessManager/FirefoxManagerTest.php +++ b/tests/ProcessManager/FirefoxManagerTest.php @@ -13,6 +13,8 @@ namespace Symfony\Component\Panther\Tests\ProcessManager; +use Facebook\WebDriver\Firefox\FirefoxOptions; +use Facebook\WebDriver\Remote\DesiredCapabilities; use PHPUnit\Framework\TestCase; use Symfony\Component\Panther\Exception\RuntimeException; use Symfony\Component\Panther\ProcessManager\FirefoxManager; @@ -68,4 +70,54 @@ public function testMultipleInstances(): void $driver1->quit(); $driver2->quit(); } + + public function testCanOverrideOptions(): void + { + $manager = new FirefoxManager(null, null, [ + 'capabilities' => [ + 'platform' => 'LINUX', + 'browserName' => 'firefox-esr', + 'moz:firefoxOptions' => [ + 'prefs' => [ + 'devtools.console.stdout.content' => true, + 'reader.parse-on-load.enabled' => true, + ], + 'args' => [ + '--new-instance', + ], + ], + ], + ]); + $refl = new \ReflectionMethod($manager, 'buildCapabilities'); + $refl->setAccessible(true); + $capabilities = $refl->invoke($manager); + + $this->assertInstanceOf(DesiredCapabilities::class, $capabilities); + $this->assertEquals('LINUX', $capabilities->getCapability('platform')); + $this->assertEquals('firefox-esr', $capabilities->getCapability('browserName')); + + $this->assertInstanceOf(FirefoxOptions::class, $capabilities->getCapability('moz:firefoxOptions')); + $mozFirefoxOptions = $capabilities->getCapability('moz:firefoxOptions')->toArray(); + $this->assertArrayHasKey('prefs', $mozFirefoxOptions); + + // // our preferences should be set + $this->assertArrayHasKey('devtools.console.stdout.content', $mozFirefoxOptions['prefs']); + $this->assertTrue($mozFirefoxOptions['prefs']['devtools.console.stdout.content']); + + // but the default one should still be there + $this->assertArrayHasKey('ui.prefersReducedMotion', $mozFirefoxOptions['prefs']); + $this->assertEquals('1', $mozFirefoxOptions['prefs']['ui.prefersReducedMotion']); + $this->assertArrayHasKey('devtools.jsonview.enabled', $mozFirefoxOptions['prefs']); + $this->assertFalse($mozFirefoxOptions['prefs']['devtools.jsonview.enabled']); + + // except if we override then + $this->assertArrayHasKey('reader.parse-on-load.enabled', $mozFirefoxOptions['prefs']); + $this->assertTrue($mozFirefoxOptions['prefs']['reader.parse-on-load.enabled']); + + // default arguments should still be there + $this->assertContains('--headless', $mozFirefoxOptions['args']); + + // but our custom one should be there too + $this->assertContains('--new-instance', $mozFirefoxOptions['args']); + } } From 62d42151bdc73a4854d73503d1bc2c09f1cd3c2f Mon Sep 17 00:00:00 2001 From: Tugdual Saunier Date: Wed, 5 Mar 2025 15:59:33 +0100 Subject: [PATCH 4/5] fix testPrefersReducedMotionDisabled should restore `PANTHER_NO_REDUCED_MOTION` state --- tests/ClientTest.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/ClientTest.php b/tests/ClientTest.php index edda41dd..f6662cd1 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -615,11 +615,18 @@ public function testPrefersReducedMotionDisabled(string $browser): void { $this->expectException(ElementClickInterceptedException::class); + $previous = $_SERVER['PANTHER_NO_REDUCED_MOTION'] ?? null; $_SERVER['PANTHER_NO_REDUCED_MOTION'] = true; $client = self::createPantherClient(['browser' => $browser]); $client->request('GET', '/prefers-reduced-motion.html'); $client->clickLink('Click me!'); + + if (null === $previous) { + unset($_SERVER['PANTHER_NO_REDUCED_MOTION']); + } else { + $_SERVER['PANTHER_NO_REDUCED_MOTION'] = $previous; + } } public static function providePrefersReducedMotion(): iterable From 1e128ebb951a24d8f1c8a8038b5ac88f9cf1ec74 Mon Sep 17 00:00:00 2001 From: Tugdual Saunier Date: Wed, 5 Mar 2025 17:28:09 +0100 Subject: [PATCH 5/5] bump mininium php-webdriver/webdriver version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 41801fec..1099a0ae 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,7 @@ "php": ">=8.0", "ext-dom": "*", "ext-libxml": "*", - "php-webdriver/webdriver": "^1.8.2", + "php-webdriver/webdriver": "^1.11.0", "symfony/browser-kit": "^5.4 || ^6.4 || ^7.0", "symfony/dependency-injection": "^5.4 || ^6.4 || ^7.0", "symfony/deprecation-contracts": "^2.4 || ^3",