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

Initial version of Selenium Manager (1.0.0-M1) #11078

Merged
merged 4 commits into from
Oct 6, 2022
Merged

Conversation

bonigarcia
Copy link
Member

Description

This PR contains the initial version of Selenium Manager (1.0.0-M1). Selenium Manager is a CLI tool that can be executed (using Cargo) as follows:

$ cargo run -- --help
selenium-manager 1.0.0-M1
Automated driver management for Selenium

Usage: selenium-manager [OPTIONS] --browser <BROWSER>
Options:
  -b, --browser <BROWSER>  Browser type (e.g., chrome, firefox, edge)
  -v, --version <VERSION>  Major browser version (e.g., 105, 106, etc.) [default: ]
  -d, --debug              Display DEBUG messages
  -t, --trace              Display TRACE messages
  -c, --clear-cache        Clear driver cache
  -h, --help               Print help information

For instance, the command required to manage chromedriver is the following:

$ cargo run -- --browser chrome
INFO	/home/boni/.cache/selenium/chromedriver/linux64/106.0.5249.61/chromedriver

If everything is correct, the last line contains the path to the driver (which will be used in the bindings). To get DEBUG traces, we can use:

$ cargo run -- --browser chrome --debug
DEBUG	Clearing cache at: /home/boni/.cache/selenium
DEBUG	Using shell command to find out chrome version
DEBUG	Running sh command: "google-chrome --version"
DEBUG	Output { status: ExitStatus(unix_wait_status(0)), stdout: "Google Chrome 106.0.5249.91 \n", stderr: "" }
DEBUG	The version of chrome is 106.0.5249.91
DEBUG	Detected browser: chrome 106
DEBUG	Reading chromedriver version from https://chromedriver.storage.googleapis.com/LATEST_RELEASE_106
DEBUG	starting new connection: https://chromedriver.storage.googleapis.com/
DEBUG	Required driver: chromedriver 106.0.5249.61
DEBUG	starting new connection: https://chromedriver.storage.googleapis.com/
DEBUG	File extracted to /home/boni/.cache/selenium/chromedriver/linux64/106.0.5249.61/chromedriver (13158208 bytes)
INFO	/home/boni/.cache/selenium/chromedriver/linux64/106.0.5249.61/chromedriver

The next steps are:

  • Migrate Cargo to Bazel.
  • Use Selenium Manager from the different language bindings.

Motivation and Context

As discussed in the TLC, Selenium Manager will be the tool implementing the "batteries included" concept in Selenium. The first milestone (M1) of Selenium Manager is devoted to automated driver management for Chrome (chomedriver), Firefox (geckodriver), and Edge (msedgedriver).

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist

  • I have read the contributing document.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

@titusfortner
Copy link
Member

Excellent!

The way I'm expecting to use this, is to build it, put it in rb/bin/selenium-manager.

When I put it in that location manually right now on Mac, the below code works:

      # Line 1 it gets passed in by webdrivers gem
      # Line 2 it is on PATH
      # Line 3 it gets the location from the Manager

      def binary_path(path = nil)
        path = path.call if path.is_a?(Proc)
        path ||= Platform.find_binary(self.class::EXECUTABLE)
        path ||= SeleniumManager.driver_location(self.class::BROWSER_NAME)

        raise Error::WebDriverError, self.class::MISSING_TEXT unless path

        Platform.assert_executable path
        path
      end

SeleniumManager itself looks like:

    class SeleniumManager
      ROOT_PATH = "../../../../bin/selenium-manager"

      def self.driver_location(browser_name)
        command = "#{File.expand_path(ROOT_PATH, __dir__)} --browser #{browser_name}"
        `#{command}`.split("\t").last.strip
      end
    end # SeleniumManager

Copy link
Member

@titusfortner titusfortner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Please add a README
    • link to installing Rust
    • usage
    • status / roadmap (checkboxes?)
  2. I think the output should just be the location of the file without formatting to make it easier to use the result. Would that be possible?

@AutomatedTester
Copy link
Member

We should also look at building this with bazel

@titusfortner
Copy link
Member

chromedriver & geckodriver are downloading as executable, but msedgedriver isn't

@titusfortner
Copy link
Member

This is otherwise working code for Ruby. ac90661

@bonigarcia bonigarcia force-pushed the selenium-manager-m1 branch 2 times, most recently from 893b921 to 82554bd Compare October 4, 2022 07:58
@bonigarcia
Copy link
Member Author

bonigarcia commented Oct 4, 2022

Thanks a lot for your feedback, @titusfortner, and @AutomatedTester! Some comments about it:

  • For the first version (M1), as discussed, each binding should use Selenium Manager as a fallback mechanism (when no driver is found). To that aim, the early adopters of Selenium Manager should place its binary in a known folder. This way, the bindings will be able to use it. Since the cache folder for drivers will be ~/.cache/selenium, I think a good place for Selenium Manager is ~/.cache/selenium/manager/selenium-manager (%homepath%\.cache\selenium\manager\selenium-manager.exe in the case of Windows).
  • For each binding, if the Selenium Manager binary is found, it uses it, simply invoking it as a CLI tool. The resulting driver path should be parsed from the standard output. If everything goes well, it follows the format INFO\t<driver_path>. Answering your question, yes, we can simply put the driver path instead of that format. Nevertheless, I did it on purpose in that way to implement an error control mechanism in the bindings. I mean, if the output of Selenium Manager does not match the format INFO\t<driver_path>, something wrong has happened, and the driver is unavailable. If the driver is resolved correctly, there is only one INFO\t<driver_path line. Parsing that string from each binding should not be that difficult, IMO. Moreover, the labels INFO, DEBUG, TRACE WARN, and ERROR allows debugging Selenium Manager when executed as a regular CLI tool (also for future uses of the tool).
  • Indeed, there was an issue with msedgedriver in macOS. The permissions were correct on Linux for every driver. Still, for some reason, msedgedriver in macOS was not executable (although I implemented the logic to get the permissions from the original driver when unzipped). In any case, I solved it by forcing the permissions of the binary drivers to 755in Unix-like systems. I updated the PR accordingly.
  • I included a brief README as requested.

@bonigarcia bonigarcia force-pushed the selenium-manager-m1 branch from 82554bd to eb56945 Compare October 4, 2022 10:42
@titusfortner
Copy link
Member

The code I implemented is the fallback option implementation (it is not used when the system finds a driver on PATH, even when that driver is wrong version, etc). Now that I'm actually working with it, I see that our conversation before doesn't matter. If we can build it, we can package it, if we package it we can put it in a known location within the package. There's no reason to then put it on the system, or for a user to have to (or be suggested to) download it to use it with latest Selenium.

I see what you mean about using output to manage errors, it feels a little weird for some reason, but I've never created a standalone process before, so my instincts are probably off. :)

@titusfortner
Copy link
Member

The only blocker I see for incorporating "beta1" into bindings is ability to generate Mac, Linux, windows binaries with bazel.

@bonigarcia bonigarcia merged commit dcdff48 into trunk Oct 6, 2022
@bonigarcia bonigarcia deleted the selenium-manager-m1 branch October 6, 2022 19:33
@diemol diemol added the hacktoberfest-accepted Temporary label Oct 31, 2022

use crate::files::parse_version;

#[tokio::main]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should just be async instead of using the tokio::main macro, this prevents it from being used in existing projects that are async, this code can also just be sync.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you like to send us a pull request to improve the code?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants