playwright_core/
driver.rs1use crate::{Error, Result};
7use std::path::{Path, PathBuf};
8use std::process::Command;
9
10pub fn get_driver_executable() -> Result<(PathBuf, PathBuf)> {
36 if let Some(result) = try_bundled_driver()? {
38 return Ok(result);
39 }
40
41 if let Some(result) = try_driver_path_env()? {
43 return Ok(result);
44 }
45
46 if let Some(result) = try_node_cli_env()? {
48 return Ok(result);
49 }
50
51 if let Some(result) = try_npm_global()? {
53 return Ok(result);
54 }
55
56 if let Some(result) = try_npm_local()? {
58 return Ok(result);
59 }
60
61 Err(Error::ServerNotFound)
62}
63
64fn try_bundled_driver() -> Result<Option<(PathBuf, PathBuf)>> {
69 if let (Some(node_exe), Some(cli_js)) = (
71 option_env!("PLAYWRIGHT_NODE_EXE"),
72 option_env!("PLAYWRIGHT_CLI_JS"),
73 ) {
74 let node_path = PathBuf::from(node_exe);
75 let cli_path = PathBuf::from(cli_js);
76
77 if node_path.exists() && cli_path.exists() {
78 return Ok(Some((node_path, cli_path)));
79 }
80 }
81
82 if let Some(driver_dir) = option_env!("PLAYWRIGHT_DRIVER_DIR") {
84 let driver_path = PathBuf::from(driver_dir);
85 let node_exe = if cfg!(windows) {
86 driver_path.join("node.exe")
87 } else {
88 driver_path.join("node")
89 };
90 let cli_js = driver_path.join("package").join("cli.js");
91
92 if node_exe.exists() && cli_js.exists() {
93 return Ok(Some((node_exe, cli_js)));
94 }
95 }
96
97 Ok(None)
98}
99
100fn try_driver_path_env() -> Result<Option<(PathBuf, PathBuf)>> {
106 if let Ok(driver_path) = std::env::var("PLAYWRIGHT_DRIVER_PATH") {
107 let driver_dir = PathBuf::from(driver_path);
108 let node_exe = if cfg!(windows) {
109 driver_dir.join("node.exe")
110 } else {
111 driver_dir.join("node")
112 };
113 let cli_js = driver_dir.join("package").join("cli.js");
114
115 if node_exe.exists() && cli_js.exists() {
116 return Ok(Some((node_exe, cli_js)));
117 }
118 }
119
120 Ok(None)
121}
122
123fn try_node_cli_env() -> Result<Option<(PathBuf, PathBuf)>> {
127 if let (Ok(node_exe), Ok(cli_js)) = (
128 std::env::var("PLAYWRIGHT_NODE_EXE"),
129 std::env::var("PLAYWRIGHT_CLI_JS"),
130 ) {
131 let node_path = PathBuf::from(node_exe);
132 let cli_path = PathBuf::from(cli_js);
133
134 if node_path.exists() && cli_path.exists() {
135 return Ok(Some((node_path, cli_path)));
136 }
137 }
138
139 Ok(None)
140}
141
142fn try_npm_global() -> Result<Option<(PathBuf, PathBuf)>> {
144 let output = Command::new("npm").args(["root", "-g"]).output();
145
146 if let Ok(output) = output {
147 if output.status.success() {
148 let npm_root = String::from_utf8_lossy(&output.stdout).trim().to_string();
149 let node_modules = PathBuf::from(npm_root);
150 if node_modules.exists() {
151 if let Ok(paths) = find_playwright_in_node_modules(&node_modules) {
152 return Ok(Some(paths));
153 }
154 }
155 }
156 }
157
158 Ok(None)
159}
160
161fn try_npm_local() -> Result<Option<(PathBuf, PathBuf)>> {
163 let output = Command::new("npm").args(["root"]).output();
164
165 if let Ok(output) = output {
166 if output.status.success() {
167 let npm_root = String::from_utf8_lossy(&output.stdout).trim().to_string();
168 let node_modules = PathBuf::from(npm_root);
169 if node_modules.exists() {
170 if let Ok(paths) = find_playwright_in_node_modules(&node_modules) {
171 return Ok(Some(paths));
172 }
173 }
174 }
175 }
176
177 Ok(None)
178}
179
180fn find_playwright_in_node_modules(node_modules: &Path) -> Result<(PathBuf, PathBuf)> {
184 let playwright_dirs = [
186 node_modules.join("playwright"),
187 node_modules.join("@playwright").join("test"),
188 ];
189
190 for playwright_dir in &playwright_dirs {
191 if !playwright_dir.exists() {
192 continue;
193 }
194
195 let cli_js = playwright_dir.join("cli.js");
197 if !cli_js.exists() {
198 continue;
199 }
200
201 if let Ok(node_exe) = find_node_executable() {
203 return Ok((node_exe, cli_js));
204 }
205 }
206
207 Err(Error::ServerNotFound)
208}
209
210fn find_node_executable() -> Result<PathBuf> {
212 #[cfg(not(windows))]
214 let which_cmd = "which";
215 #[cfg(windows)]
216 let which_cmd = "where";
217
218 if let Ok(output) = Command::new(which_cmd).arg("node").output() {
219 if output.status.success() {
220 let node_path = String::from_utf8_lossy(&output.stdout).trim().to_string();
221 if !node_path.is_empty() {
222 let path = PathBuf::from(node_path.lines().next().unwrap_or(&node_path));
223 if path.exists() {
224 return Ok(path);
225 }
226 }
227 }
228 }
229
230 #[cfg(not(windows))]
232 let common_locations = [
233 "/usr/local/bin/node",
234 "/usr/bin/node",
235 "/opt/homebrew/bin/node",
236 "/opt/local/bin/node",
237 ];
238
239 #[cfg(windows)]
240 let common_locations = [
241 "C:\\Program Files\\nodejs\\node.exe",
242 "C:\\Program Files (x86)\\nodejs\\node.exe",
243 ];
244
245 for location in &common_locations {
246 let path = PathBuf::from(location);
247 if path.exists() {
248 return Ok(path);
249 }
250 }
251
252 Err(Error::LaunchFailed(
253 "Node.js executable not found. Please install Node.js or set PLAYWRIGHT_NODE_EXE."
254 .to_string(),
255 ))
256}
257
258#[cfg(test)]
259mod tests {
260 use super::*;
261
262 #[test]
263 fn test_find_node_executable() {
264 let result = find_node_executable();
266 match result {
267 Ok(node_path) => {
268 println!("Found node at: {:?}", node_path);
269 assert!(node_path.exists());
270 }
271 Err(e) => {
272 println!(
273 "Node.js not found (expected if Node.js not installed): {:?}",
274 e
275 );
276 }
278 }
279 }
280
281 #[test]
282 fn test_get_driver_executable() {
283 let result = get_driver_executable();
285 match result {
286 Ok((node, cli)) => {
287 println!("Found Playwright driver:");
288 println!(" Node: {:?}", node);
289 println!(" CLI: {:?}", cli);
290 assert!(node.exists());
291 assert!(cli.exists());
292 }
293 Err(Error::ServerNotFound) => {
294 println!("Playwright driver not found (expected in some environments)");
295 println!(
296 "This is OK - driver will be bundled at build time or can be installed via npm"
297 );
298 }
299 Err(e) => panic!("Unexpected error: {:?}", e),
300 }
301 }
302
303 #[test]
304 fn test_bundled_driver_detection() {
305 let result = try_bundled_driver();
307 match result {
308 Ok(Some((node, cli))) => {
309 println!("Found bundled driver:");
310 println!(" Node: {:?}", node);
311 println!(" CLI: {:?}", cli);
312 assert!(node.exists());
313 assert!(cli.exists());
314 }
315 Ok(None) => {
316 println!("No bundled driver (expected during development)");
317 }
318 Err(e) => panic!("Unexpected error: {:?}", e),
319 }
320 }
321}