Skip to content

Commit a39a3c0

Browse files
test: add unit tests for W3C capability filtering
Add WebDriverFilterTest covering 8 test cases: - Non-W3C capabilities are stripped - All W3C standard capabilities pass through - Vendor extension capabilities (with ':') are preserved - Falsy values (false, 0) are NOT stripped (addresses review concern) - Null capabilities handled gracefully - requiredCapabilities (alwaysMatch) also filtered - Legacy capability names remapped to W3C equivalents - Key-value mapping preserved (not reindexed numerically)
1 parent 6efda91 commit a39a3c0

1 file changed

Lines changed: 278 additions & 0 deletions

File tree

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
<?php
2+
3+
/**
4+
* Copyright 2026 Daif Abderrahman. All Rights Reserved.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*
18+
* @package WebDriver
19+
*
20+
* @author Daif Abderrahman <daif.abderrahman@gmail.com>
21+
*/
22+
23+
namespace Test\WebDriver;
24+
25+
use PHPUnit\Framework\TestCase;
26+
use WebDriver\Browser;
27+
use WebDriver\Capability;
28+
use WebDriver\Service\CurlService;
29+
use WebDriver\ServiceFactory;
30+
use WebDriver\WebDriver;
31+
32+
/**
33+
* Test WebDriver capability filtering in session creation
34+
*
35+
* @package WebDriver
36+
*
37+
* @group Unit
38+
*/
39+
class WebDriverFilterTest extends TestCase
40+
{
41+
/**
42+
* @var array|null
43+
*/
44+
private $capturedParameters;
45+
46+
/**
47+
* Create a WebDriver instance with mocked CurlService that captures request parameters
48+
*
49+
* @return WebDriver
50+
*/
51+
private function createDriverWithMock()
52+
{
53+
$mockCurlService = $this->createMock(CurlService::class);
54+
$mockCurlService->expects($this->any())
55+
->method('execute')
56+
->will($this->returnCallback(function ($requestMethod, $url, $parameters) {
57+
$this->capturedParameters = $parameters;
58+
59+
$info = [
60+
'url' => $url,
61+
'request_method' => $requestMethod,
62+
'http_code' => 200,
63+
];
64+
65+
$result = json_encode([
66+
'value' => [
67+
'sessionId' => 'mock-session-id',
68+
'capabilities' => [
69+
'browserName' => 'chrome',
70+
],
71+
],
72+
]);
73+
74+
return [$result, $info];
75+
}));
76+
77+
ServiceFactory::getInstance()->setService('service.curl', $mockCurlService);
78+
79+
return new WebDriver('http://localhost:4444/wd/hub');
80+
}
81+
82+
/**
83+
* Non-W3C capabilities should be stripped from desiredCapabilities
84+
*/
85+
public function testSessionFiltersNonW3cCapabilities()
86+
{
87+
$driver = $this->createDriverWithMock();
88+
89+
$driver->session(Browser::CHROME, [
90+
Capability::BROWSER_NAME => 'chrome',
91+
'unknownCapability' => 'value',
92+
'anotherInvalidCap' => true,
93+
]);
94+
95+
$firstMatch = $this->capturedParameters['capabilities']['firstMatch'];
96+
$filtered = $firstMatch[0];
97+
98+
$this->assertArrayHasKey(Capability::BROWSER_NAME, $filtered);
99+
$this->assertArrayNotHasKey('unknownCapability', $filtered);
100+
$this->assertArrayNotHasKey('anotherInvalidCap', $filtered);
101+
}
102+
103+
/**
104+
* All W3C standard capabilities should pass through the filter
105+
*/
106+
public function testSessionPreservesW3cCapabilities()
107+
{
108+
$driver = $this->createDriverWithMock();
109+
110+
$capabilities = [
111+
Capability::BROWSER_NAME => 'firefox',
112+
Capability::BROWSER_VERSION => '120.0',
113+
Capability::PLATFORM_NAME => 'linux',
114+
Capability::ACCEPT_INSECURE_CERTS => true,
115+
Capability::PAGE_LOAD_STRATEGY => 'normal',
116+
Capability::SET_WINDOW_RECT => true,
117+
Capability::STRICT_FILE_INTERACTABILITY => false,
118+
Capability::TIMEOUTS => ['implicit' => 5000],
119+
Capability::UNHANDLED_PROMPT_BEHAVIOR => 'dismiss',
120+
];
121+
122+
$driver->session(Browser::FIREFOX, $capabilities);
123+
124+
$firstMatch = $this->capturedParameters['capabilities']['firstMatch'];
125+
$filtered = $firstMatch[0];
126+
127+
foreach ($capabilities as $key => $value) {
128+
$this->assertArrayHasKey($key, $filtered, "W3C capability '$key' should be preserved");
129+
$this->assertSame($value, $filtered[$key], "W3C capability '$key' value should be unchanged");
130+
}
131+
}
132+
133+
/**
134+
* Vendor extension capabilities (containing ':') should pass through the filter
135+
*/
136+
public function testSessionPreservesExtensionCapabilities()
137+
{
138+
$driver = $this->createDriverWithMock();
139+
140+
$chromeOptions = ['args' => ['--headless', '--no-sandbox']];
141+
$firefoxOptions = ['prefs' => ['dom.webnotifications.enabled' => false]];
142+
143+
$driver->session(Browser::CHROME, [
144+
Capability::BROWSER_NAME => 'chrome',
145+
'goog:chromeOptions' => $chromeOptions,
146+
'moz:firefoxOptions' => $firefoxOptions,
147+
'ms:edgeOptions' => ['args' => ['--start-maximized']],
148+
]);
149+
150+
$firstMatch = $this->capturedParameters['capabilities']['firstMatch'];
151+
$filtered = $firstMatch[0];
152+
153+
$this->assertArrayHasKey('goog:chromeOptions', $filtered);
154+
$this->assertSame($chromeOptions, $filtered['goog:chromeOptions']);
155+
$this->assertArrayHasKey('moz:firefoxOptions', $filtered);
156+
$this->assertSame($firefoxOptions, $filtered['moz:firefoxOptions']);
157+
$this->assertArrayHasKey('ms:edgeOptions', $filtered);
158+
}
159+
160+
/**
161+
* Capabilities with falsy values (false, empty string, empty array) must NOT be stripped.
162+
*
163+
* This is critical: a user may explicitly set acceptInsecureCerts => false to disable
164+
* a capability that is enabled by default. The filter must preserve user intent.
165+
*/
166+
public function testSessionPreservesFalsyCapabilityValues()
167+
{
168+
$driver = $this->createDriverWithMock();
169+
170+
$driver->session(Browser::CHROME, [
171+
Capability::ACCEPT_INSECURE_CERTS => false,
172+
Capability::SET_WINDOW_RECT => false,
173+
Capability::STRICT_FILE_INTERACTABILITY => false,
174+
]);
175+
176+
$firstMatch = $this->capturedParameters['capabilities']['firstMatch'];
177+
$filtered = $firstMatch[0];
178+
179+
$this->assertArrayHasKey(Capability::ACCEPT_INSECURE_CERTS, $filtered, 'acceptInsecureCerts => false must not be stripped');
180+
$this->assertFalse($filtered[Capability::ACCEPT_INSECURE_CERTS]);
181+
182+
$this->assertArrayHasKey(Capability::SET_WINDOW_RECT, $filtered, 'setWindowRect => false must not be stripped');
183+
$this->assertFalse($filtered[Capability::SET_WINDOW_RECT]);
184+
185+
$this->assertArrayHasKey(Capability::STRICT_FILE_INTERACTABILITY, $filtered, 'strictFileInteractability => false must not be stripped');
186+
$this->assertFalse($filtered[Capability::STRICT_FILE_INTERACTABILITY]);
187+
}
188+
189+
/**
190+
* Null desiredCapabilities should not cause errors
191+
*/
192+
public function testSessionWithNullCapabilities()
193+
{
194+
$driver = $this->createDriverWithMock();
195+
196+
$session = $driver->session(Browser::CHROME, null);
197+
198+
$firstMatch = $this->capturedParameters['capabilities']['firstMatch'];
199+
200+
// Without capabilities, firstMatch should only contain the default chrome entry
201+
$this->assertCount(1, $firstMatch);
202+
$this->assertEquals([Capability::BROWSER_NAME => Browser::CHROME], $firstMatch[0]);
203+
}
204+
205+
/**
206+
* requiredCapabilities (alwaysMatch) should also be filtered
207+
*/
208+
public function testSessionFiltersRequiredCapabilities()
209+
{
210+
$driver = $this->createDriverWithMock();
211+
212+
$driver->session(Browser::CHROME, null, [
213+
Capability::PLATFORM_NAME => 'linux',
214+
'unknownCap' => 'value',
215+
'goog:chromeOptions' => ['args' => ['--headless']],
216+
]);
217+
218+
$alwaysMatch = $this->capturedParameters['capabilities']['alwaysMatch'];
219+
220+
$this->assertArrayHasKey(Capability::PLATFORM_NAME, $alwaysMatch);
221+
$this->assertArrayNotHasKey('unknownCap', $alwaysMatch);
222+
$this->assertArrayHasKey('goog:chromeOptions', $alwaysMatch);
223+
}
224+
225+
/**
226+
* Legacy JSON Wire Protocol capability names should be remapped to W3C names
227+
*/
228+
public function testSessionRemapsLegacyCapabilities()
229+
{
230+
$driver = $this->createDriverWithMock();
231+
232+
$driver->session(Browser::CHROME, [
233+
Capability::PLATFORM => 'LINUX',
234+
Capability::VERSION => '120.0',
235+
Capability::ACCEPT_SSL_CERTS => true,
236+
]);
237+
238+
$firstMatch = $this->capturedParameters['capabilities']['firstMatch'];
239+
$filtered = $firstMatch[0];
240+
241+
// Legacy keys should be remapped to W3C equivalents
242+
$this->assertArrayHasKey(Capability::PLATFORM_NAME, $filtered);
243+
$this->assertEquals('LINUX', $filtered[Capability::PLATFORM_NAME]);
244+
245+
$this->assertArrayHasKey(Capability::BROWSER_VERSION, $filtered);
246+
$this->assertEquals('120.0', $filtered[Capability::BROWSER_VERSION]);
247+
248+
$this->assertArrayHasKey(Capability::ACCEPT_INSECURE_CERTS, $filtered);
249+
$this->assertTrue($filtered[Capability::ACCEPT_INSECURE_CERTS]);
250+
251+
// Original legacy keys should not remain
252+
$this->assertArrayNotHasKey(Capability::PLATFORM, $filtered);
253+
$this->assertArrayNotHasKey(Capability::VERSION, $filtered);
254+
$this->assertArrayNotHasKey(Capability::ACCEPT_SSL_CERTS, $filtered);
255+
}
256+
257+
/**
258+
* Key-value mapping must be preserved (not reindexed numerically)
259+
*/
260+
public function testSessionPreservesKeyValueMapping()
261+
{
262+
$driver = $this->createDriverWithMock();
263+
264+
$driver->session(Browser::CHROME, [
265+
Capability::BROWSER_NAME => 'chrome',
266+
Capability::PLATFORM_NAME => 'linux',
267+
Capability::ACCEPT_INSECURE_CERTS => true,
268+
]);
269+
270+
$firstMatch = $this->capturedParameters['capabilities']['firstMatch'];
271+
$filtered = $firstMatch[0];
272+
273+
// Keys must be string capability names, not numeric indices
274+
$this->assertIsString(array_key_first($filtered));
275+
$this->assertSame('chrome', $filtered[Capability::BROWSER_NAME]);
276+
$this->assertSame('linux', $filtered[Capability::PLATFORM_NAME]);
277+
}
278+
}

0 commit comments

Comments
 (0)