Skip to content

Commit

Permalink
[rust] Include support for Safari in Selenium Manager (#11609)
Browse files Browse the repository at this point in the history
* [rust] Include support for Safari in Selenium Manager

* [rust] Include separate test for Safari

* [rust] Fix Safari logic after latest refactoring

* [rust] Include SafariTP as a different browser than Safari
  • Loading branch information
bonigarcia authored Feb 21, 2023
1 parent b5a118a commit 50d1ae9
Show file tree
Hide file tree
Showing 4 changed files with 346 additions and 35 deletions.
100 changes: 65 additions & 35 deletions rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::edge::EdgeManager;
use crate::files::compose_cache_folder;
use crate::firefox::FirefoxManager;
use crate::iexplorer::IExplorerManager;
use crate::safari::{SafariManager, SAFARI};
use std::fs;

use crate::config::OS::WINDOWS;
Expand All @@ -37,6 +38,7 @@ use crate::logger::Logger;
use crate::metadata::{
create_browser_metadata, get_browser_version_from_metadata, get_metadata, write_metadata,
};
use crate::safaritp::SafariTPManager;

pub mod chrome;
pub mod config;
Expand All @@ -47,6 +49,8 @@ pub mod firefox;
pub mod iexplorer;
pub mod logger;
pub mod metadata;
pub mod safari;
pub mod safaritp;

pub const REQUEST_TIMEOUT_SEC: u64 = 120; // The timeout is applied from when the request starts connecting until the response body has finished
pub const STABLE: &str = "stable";
Expand All @@ -57,6 +61,8 @@ pub const NIGHTLY: &str = "nightly";
pub const WMIC_COMMAND: &str = r#"wmic datafile where name='{}' get Version /value"#;
pub const WMIC_COMMAND_ENV: &str = r#"wmic datafile where name='%{}:\=\\%{}' get Version /value"#;
pub const REG_QUERY: &str = r#"REG QUERY {} /v version"#;
pub const PLIST_COMMAND: &str =
r#"/usr/libexec/PlistBuddy -c "print :CFBundleShortVersionString" {}/Contents/Info.plist"#;
pub const DASH_VERSION: &str = "{} -v";
pub const DASH_DASH_VERSION: &str = "{} --version";
pub const ENV_PROGRAM_FILES: &str = "PROGRAMFILES";
Expand Down Expand Up @@ -160,11 +166,12 @@ pub trait SeleniumManager {
}
break;
}

metadata
.browsers
.push(create_browser_metadata(browser_name, &browser_version));
write_metadata(&metadata, self.get_logger());
if !self.is_safari() {
metadata
.browsers
.push(create_browser_metadata(browser_name, &browser_version));
write_metadata(&metadata, self.get_logger());
}
if !browser_version.is_empty() {
Some(browser_version)
} else {
Expand All @@ -179,12 +186,14 @@ pub trait SeleniumManager {
if browser_version.is_empty() || self.is_browser_version_unstable() {
match self.discover_browser_version() {
Some(version) => {
self.get_logger().debug(format!(
"Detected browser: {} {}",
self.get_browser_name(),
version
));
self.set_browser_version(version);
if !self.is_safari() {
self.get_logger().debug(format!(
"Detected browser: {} {}",
self.get_browser_name(),
version
));
self.set_browser_version(version);
}
}
None => {
if self.is_browser_version_unstable() {
Expand Down Expand Up @@ -247,6 +256,10 @@ pub trait SeleniumManager {
}
}

fn is_safari(&self) -> bool {
self.get_browser_name().contains(SAFARI)
}

fn is_browser_version_unstable(&self) -> bool {
let browser_version = self.get_browser_version();
browser_version.eq_ignore_ascii_case(BETA)
Expand All @@ -261,34 +274,38 @@ pub trait SeleniumManager {
self.set_driver_version(driver_version);
}

let (in_path_driver_version, in_path_driver_path) = self.find_driver_in_path();
if let (Some(found_driver_version), Some(found_driver_path)) =
(in_path_driver_version, in_path_driver_path)
{
if found_driver_version.eq(self.get_driver_version()) {
self.get_logger().debug(format!(
"Found {} {} in PATH: {}",
self.get_driver_name(),
found_driver_version,
found_driver_path
));
return Ok(PathBuf::from(found_driver_path));
} else {
self.get_logger().warn(format!(
"Incompatible release of {} (version {}) detected in PATH: {}",
self.get_driver_name(),
found_driver_version,
found_driver_path
));
if !self.is_safari() {
let (in_path_driver_version, in_path_driver_path) = self.find_driver_in_path();
if let (Some(found_driver_version), Some(found_driver_path)) =
(in_path_driver_version, in_path_driver_path)
{
if found_driver_version.eq(self.get_driver_version()) {
self.get_logger().debug(format!(
"Found {} {} in PATH: {}",
self.get_driver_name(),
found_driver_version,
found_driver_path
));
return Ok(PathBuf::from(found_driver_path));
} else {
self.get_logger().warn(format!(
"Incompatible release of {} (version {}) detected in PATH: {}",
self.get_driver_name(),
found_driver_version,
found_driver_path
));
}
}
}
let driver_path = self.get_driver_path_in_cache();
if driver_path.exists() {
self.get_logger().debug(format!(
"{} {} already in the cache",
self.get_driver_name(),
self.get_driver_version()
));
if !self.is_safari() {
self.get_logger().debug(format!(
"{} {} already in the cache",
self.get_driver_name(),
self.get_driver_version()
));
}
} else {
self.download_driver()?;
}
Expand Down Expand Up @@ -434,6 +451,17 @@ pub fn get_manager_by_browser(browser_name: String) -> Result<Box<dyn SeleniumMa
.contains(&browser_name_lower_case.as_str())
{
Ok(IExplorerManager::new())
} else if browser_name.eq("safari") {
Ok(SafariManager::new())
} else if vec![
"safari technology preview",
r#"safari\ technology\ preview"#,
"safaritechnologypreview",
"safaritp",
]
.contains(&browser_name_lower_case.as_str())
{
Ok(SafariTPManager::new())
} else {
Err(format!("Invalid browser name: {browser_name}"))
}
Expand All @@ -448,6 +476,8 @@ pub fn get_manager_by_driver(driver_name: String) -> Result<Box<dyn SeleniumMana
Ok(EdgeManager::new())
} else if driver_name.eq_ignore_ascii_case("iedriverserver") {
Ok(IExplorerManager::new())
} else if driver_name.eq_ignore_ascii_case("safaridriver") {
Ok(SafariManager::new())
} else {
Err(format!("Invalid driver name: {driver_name}"))
}
Expand Down
125 changes: 125 additions & 0 deletions rust/src/safari.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

use crate::config::ManagerConfig;
use reqwest::Client;
use std::collections::HashMap;
use std::error::Error;
use std::path::PathBuf;
use std::string::ToString;

use crate::files::BrowserPath;

use crate::config::OS::MACOS;
use crate::{
create_default_http_client, format_one_arg, Logger, SeleniumManager, PLIST_COMMAND,
STABLE,
};

pub const SAFARI: &str = "safari";
const DRIVER_NAME: &str = "safaridriver";

pub struct SafariManager {
pub browser_name: &'static str,
pub driver_name: &'static str,
pub config: ManagerConfig,
pub http_client: Client,
pub log: Logger,
}

impl SafariManager {
pub fn new() -> Box<Self> {
Box::new(SafariManager {
browser_name: SAFARI,
driver_name: DRIVER_NAME,
config: ManagerConfig::default(),
http_client: create_default_http_client(),
log: Logger::default(),
})
}
}

impl SeleniumManager for SafariManager {
fn get_browser_name(&self) -> &str {
self.browser_name
}

fn get_http_client(&self) -> &Client {
&self.http_client
}

fn set_http_client(&mut self, http_client: Client) {
self.http_client = http_client;
}

fn get_browser_path_map(&self) -> HashMap<BrowserPath, &str> {
HashMap::from([(
BrowserPath::new(MACOS, STABLE),
r#"/Applications/Safari.app"#,
)])
}

fn discover_browser_version(&self) -> Option<String> {
let mut browser_path = self.get_browser_path();
if browser_path.is_empty() {
match self.detect_browser_path() {
Some(path) => {
browser_path = path;
}
_ => return None,
}
}
let command = if MACOS.is(self.get_os()) {
vec![format_one_arg(PLIST_COMMAND, browser_path)]
} else {
return None;
};
self.detect_browser_version(command)
}

fn get_driver_name(&self) -> &str {
self.driver_name
}

fn request_driver_version(&self) -> Result<String, Box<dyn Error>> {
Ok("(local)".to_string())
}

fn get_driver_url(&self) -> Result<String, Box<dyn Error>> {
Err(format!("{} not available for download", self.get_driver_name()).into())
}

fn get_driver_path_in_cache(&self) -> PathBuf {
PathBuf::from("/usr/bin/safaridriver")
}

fn get_config(&self) -> &ManagerConfig {
&self.config
}

fn set_config(&mut self, config: ManagerConfig) {
self.config = config;
}

fn get_logger(&self) -> &Logger {
&self.log
}

fn set_logger(&mut self, log: Logger) {
self.log = log;
}
}
Loading

0 comments on commit 50d1ae9

Please sign in to comment.