Skip to content

Commit aba540c

Browse files
authored
[build] use pinned browser and driver for starting grid in python and ruby tests (#17610)
* [py][rb] Use pinned browser and driver for remote Grid tests instead of Selenium Manager * [java] Fix --driver-configuration CLI example to use webdriver-executable * [py] guard selenium manager test from running on rbe
1 parent 990e7d7 commit aba540c

9 files changed

Lines changed: 96 additions & 22 deletions

File tree

.bazelrc.remote

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ build:rbe --action_env=PATH=/bin:/usr/bin:/usr/local/bin
4141
test:rbe --test_env=PATH=/bin:/usr/bin:/usr/local/bin
4242
test:rbe --test_env=HOME=/home/dev
4343

44+
# This should only be set on GitHub runs
45+
test:rbe --test_env=SE_CACHE_PATH=
46+
4447
# Make sure we sniff credentials properly
4548
build:rbe --credential_helper=gypsum.cluster.engflow.com=%workspace%/scripts/credential-helper.sh
4649

java/src/org/openqa/selenium/grid/node/config/NodeFlags.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ public class NodeFlags implements HasRoles {
161161
+ "It is recommended to provide this type of configuration through a toml config "
162162
+ "file to improve readability. Command line example: "
163163
+ "--driver-configuration display-name=\"Firefox Nightly\" max-sessions=2 "
164-
+ "webdriver-path=\"/usr/local/bin/geckodriver\" "
164+
+ "webdriver-executable=\"/usr/local/bin/geckodriver\" "
165165
+ "stereotype=\"{\\\"browserName\\\": \\\"firefox\\\", "
166166
+ "\\\"browserVersion\\\": \\\"86\\\", "
167167
+ "\\\"moz:firefoxOptions\\\": "

py/conftest.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
# under the License.
1717

1818
import http.server
19+
import json
1920
import os
2021
import socketserver
2122
import sys
@@ -208,6 +209,43 @@ def _resolve_bazel_path(path):
208209
return path
209210

210211

212+
# Maps the test driver name to its (browserName, vendor options key).
213+
_GRID_VENDOR_OPTIONS = {
214+
"chrome": ("chrome", "goog:chromeOptions"),
215+
"edge": ("MicrosoftEdge", "ms:edgeOptions"),
216+
"firefox": ("firefox", "moz:firefoxOptions"),
217+
}
218+
219+
220+
def _pinned_grid_args(config):
221+
"""Pin the driver and browser in a driver-configuration so the Grid node skips Selenium Manager.
222+
223+
Returns an empty list when nothing is pinned, keeping the default Selenium Manager behavior.
224+
"""
225+
drivers_opt = config.option.drivers or []
226+
driver = next((d for d in drivers_opt if d.lower() in _GRID_VENDOR_OPTIONS), None)
227+
executable = config.option.executable # --driver-binary
228+
binary = config.option.binary # --browser-binary
229+
if not (driver and executable and binary):
230+
return []
231+
232+
driver_path = _resolve_bazel_path(executable).strip("'")
233+
browser_path = _resolve_bazel_path(binary).strip("'")
234+
browser_name, vendor_key = _GRID_VENDOR_OPTIONS[driver.lower()]
235+
stereotype = json.dumps({"browserName": browser_name, vendor_key: {"binary": browser_path}})
236+
return [
237+
"--enable-managed-downloads",
238+
"true",
239+
"--detect-drivers",
240+
"false",
241+
"--driver-configuration",
242+
f"display-name={driver}",
243+
"max-sessions=1",
244+
f"webdriver-executable={driver_path}",
245+
f"stereotype={stereotype}",
246+
]
247+
248+
211249
def get_extensions_location():
212250
"""Locate the test extensions directory.
213251
@@ -551,7 +589,7 @@ def server(request):
551589
# under Wayland, so we use XWayland instead.
552590
remote_env["MOZ_ENABLE_WAYLAND"] = "0"
553591

554-
server = Server(env=remote_env, startup_timeout=60)
592+
server = Server(env=remote_env, startup_timeout=60, args=_pinned_grid_args(request.config) or None)
555593

556594
repo_root = Path(__file__).parent.parent # py/conftest.py -> py/ -> selenium/
557595
jar_path = "java/src/org/openqa/selenium/grid/selenium_server_deploy.jar"

py/selenium/webdriver/remote/server.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,13 @@ class Server:
4444
Available levels: "SEVERE", "WARNING", "INFO", "CONFIG", "FINE", "FINER", "FINEST".
4545
env: Mapping that defines the environment variables for the server process.
4646
java_path: Path to the java executable to run the server.
47+
args: Arguments for the standalone server command. Defaults to enabling Selenium
48+
Manager and managed downloads; pass a list to override these entirely (e.g. to
49+
pin drivers with "--driver-configuration").
4750
"""
4851

52+
DEFAULT_ARGS = ("--selenium-manager", "true", "--enable-managed-downloads", "true")
53+
4954
def __init__(
5055
self,
5156
host=None,
@@ -56,6 +61,7 @@ def __init__(
5661
env=None,
5762
java_path=None,
5863
startup_timeout=10,
64+
args=None,
5965
):
6066
if path and version:
6167
raise TypeError("Not allowed to specify a version when using an existing server path")
@@ -68,6 +74,7 @@ def __init__(
6874
self.env = env
6975
self.java_path = java_path
7076
self.startup_timeout = startup_timeout
77+
self.args = list(args) if args is not None else list(self.DEFAULT_ARGS)
7178
self.process = None
7279

7380
@property
@@ -190,10 +197,7 @@ def start(self):
190197
str(self.port),
191198
"--log-level",
192199
self.log_level,
193-
"--selenium-manager",
194-
"true",
195-
"--enable-managed-downloads",
196-
"true",
200+
*self.args,
197201
]
198202
if self.host is not None:
199203
command.extend(["--host", self.host])

py/test/selenium/webdriver/remote/remote_server_tests.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ def remove_file(path):
3838
pass
3939

4040

41+
@pytest.mark.skipif(
42+
"REMOTE_BUILD" in os.environ,
43+
reason="Downloads the Selenium Server from the network, which is unavailable on RBE",
44+
)
4145
def test_download_latest_server(standalone_server):
4246
server_path = standalone_server.download_if_needed()
4347
assert os.path.exists(server_path)

py/test/unit/selenium/webdriver/remote/remote_server_tests.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,18 @@ def test_server_with_kwargs():
4949
assert server.env == {"FOO": "bar"}
5050

5151

52+
def test_server_with_default_args():
53+
server = Server()
54+
assert server.args == list(Server.DEFAULT_ARGS)
55+
56+
57+
def test_server_with_args_override():
58+
args = ["--detect-drivers", "false", "--driver-configuration", "display-name=chrome"]
59+
server = Server(args=args)
60+
assert server.args == args
61+
assert server.args is not args
62+
63+
5264
def test_server_with_invalid_port():
5365
port = "invalid"
5466
msg = f"Server.__init__() got an invalid port: '{port}'"

rb/spec/integration/selenium/webdriver/spec_support/test_environment.rb

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -98,17 +98,9 @@ def app_server
9898
end
9999

100100
def remote_server
101-
args = if ENV.key?('CHROMEDRIVER_BINARY')
102-
["-Dwebdriver.chrome.driver=#{rlocation(ENV['CHROMEDRIVER_BINARY'])}"]
103-
elsif ENV.key?('MSEDGEDRIVER_BINARY')
104-
["-Dwebdriver.edge.driver=#{rlocation(ENV['MSEDGEDRIVER_BINARY'])}"]
105-
elsif ENV.key?('GECKODRIVER_BINARY')
106-
["-Dwebdriver.gecko.driver=#{rlocation(ENV['GECKODRIVER_BINARY'])}"]
107-
else
108-
%w[--selenium-manager true]
109-
end
101+
args = driver_path ? %w[--detect-drivers false] : %w[--selenium-manager true]
110102
args += %w[--enable-managed-downloads true]
111-
args += version_stereotype_args unless browser_version == 'stable'
103+
args += driver_configuration if driver_path || browser_version != 'stable'
112104

113105
@remote_server ||= Selenium::Server.new(
114106
remote_server_jar,
@@ -121,12 +113,31 @@ def remote_server
121113
)
122114
end
123115

124-
def version_stereotype_args
125-
stereotype = {browserName: w3c_browser_name, browserVersion: browser_version}.to_json
126-
['--driver-configuration',
127-
"display-name=#{browser} #{browser_version}",
128-
'max-sessions=5',
129-
"stereotype=#{stereotype}"]
116+
def driver_configuration
117+
stereotype = {browserName: w3c_browser_name}
118+
stereotype[:browserVersion] = browser_version unless browser_version == 'stable'
119+
stereotype[options_key] = {binary: browser_path} if browser_path
120+
121+
config = ['--driver-configuration',
122+
"display-name=#{browser} #{browser_version}",
123+
'max-sessions=5']
124+
config << "webdriver-executable=#{driver_path}" if driver_path
125+
config << "stereotype=#{stereotype.to_json}"
126+
config
127+
end
128+
129+
def driver_path
130+
env = {chrome: 'CHROMEDRIVER_BINARY', edge: 'MSEDGEDRIVER_BINARY', firefox: 'GECKODRIVER_BINARY'}[browser]
131+
rlocation(ENV.fetch(env)) if env && ENV.key?(env)
132+
end
133+
134+
def browser_path
135+
env = "#{browser.to_s.upcase}_BINARY"
136+
rlocation(ENV.fetch(env)) if ENV.key?(env)
137+
end
138+
139+
def options_key
140+
{chrome: 'goog:chromeOptions', edge: 'ms:edgeOptions', firefox: 'moz:firefoxOptions'}[browser]
130141
end
131142

132143
def w3c_browser_name

rust/tests/cache_tests.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ mod common;
2929
#[case("../テスト")]
3030
fn cache_path_test(#[case] tmp_cache_folder_name: String) {
3131
let mut cmd = get_selenium_manager();
32+
cmd.env_remove("SE_CACHE_PATH");
3233
cmd.args([
3334
"--browser",
3435
"chrome",

rust/tests/config_tests.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ fn config_test(#[case] browser_name: String) {
4040
writer.flush().unwrap();
4141

4242
let mut cmd = get_selenium_manager();
43+
cmd.env_remove("SE_CACHE_PATH");
4344
cmd.args([
4445
"--output",
4546
"json",

0 commit comments

Comments
 (0)