Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: fix FirefoxManager capabilities merge logic #670

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
66 changes: 39 additions & 27 deletions src/ProcessManager/FirefoxManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -123,6 +97,44 @@ private function getDefaultArguments(): array
return $args;
}

private function buildCapabilities(): DesiredCapabilities
{
$capabilities = DesiredCapabilities::firefox();
$firefoxOptions = $capabilities->getCapability(FirefoxOptions::CAPABILITY);

if (isset($_SERVER['PANTHER_FIREFOX_BINARY'])) {
$firefoxOptions->setOption('binary', $_SERVER['PANTHER_FIREFOX_BINARY']);
}
if ($this->arguments) {
$firefoxOptions->addArguments($this->arguments);
}

// Prefer reduced motion, see https://developer.mozilla.org/fr/docs/Web/CSS/@media/prefers-reduced-motion
if (!filter_var($_SERVER['PANTHER_NO_REDUCED_MOTION'] ?? false, \FILTER_VALIDATE_BOOLEAN)) {
$firefoxOptions->setPreference('ui.prefersReducedMotion', 1);
} else {
$firefoxOptions->setPreference('ui.prefersReducedMotion', 0);
}

foreach ($this->options['capabilities'] as $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;
}

private function getDefaultOptions(): array
{
return [
Expand Down
7 changes: 7 additions & 0 deletions tests/ClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
123 changes: 123 additions & 0 deletions tests/ProcessManager/FirefoxManagerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?php

/*
* This file is part of the Panther project.
*
* (c) Kévin Dunglas <[email protected]>
*
* 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 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;

/**
* @author Tugdual Saunier <[email protected]>
*/
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();
}

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']);
}
}
Loading