Specify a custom user data directory in Firefox using Watir-Webdriver/Selenium
Asked Answered
P

4

6

I can do the following using Chrome Driver:

b = Watir::Browser.new :chrome, :switches => ['--user-data-dir=C:/some_folder/'] # same philosophy for selenium, just a bit of a different syntax.

Which is going to create a new user data directory where all the cookies, bookmarks, cache etc. will be stored. Basically, create a new profile. If such a folder doesn't exist, it will create it. If it does exist, it will load the cookies/all related files from it.

Is there a way to do the same thing using Firefox Driver? I've been looking into ways to create a Firefox profile and all I found was this article: Creating a new Firefox Profile which doesn't solve my problem because I'm looking to do it automatically, like with Chrome driver above. Also, it seems you can create a new profile with:

profile = Selenium::WebDriver::Firefox::Profile.new

but I haven't found a way to SAVE that profile with a name I specify.

Puncture answered 29/10, 2014 at 23:1 Comment(6)
You should clarify what you are trying to achieve. Why do you want to save the profile? Why do you want to give it a name? The answers below propose a bunch of hacks to do what you've asked, but there may very well be better ways to achieve your end goal.Heimlich
I need to test my site simultaneously with multiple logged-in users (thus, multiple instances). These cookies MUST persist. I've tried Chrome, but it's producing some errors. I'd appreciate any suggestion of other driver (IE, opera maybe?) that can support this. So far Justin gave a really good answer and plan to award him unless someone comes up with a solution to support multi-thread working.Puncture
If all you need is cookies to persist, you might be able to achieve your goal by manually adding them through Watir's cookie API instead. There is also recently added functionality that allows saving/loading cookies to/from a file.Marylynnmarylynne
Do you have some references to that functionality? Can't seem to find it.Puncture
The cookies API is accessed via the Browser#cookies method. The methods available can be seen in the Watir::Cookies documentation.Marylynnmarylynne
Similar question for Python: How to save and load cookies using Python + Selenium WebDriver - Stack OverflowPren
M
2

Based on Selenium Issue 1954 and Issue 7374, the Firefox driver does not currently have this functionality. It is hard to tell if it will be implemented given that some of the project members are opposing the idea.

For now, I think you will have to monkey-patch your Selenium-WebDiver version to add this functionality. I reached the same conclusion as @shri046's answer, which is to modify the layout_on_disk method.

After you have required Selenium-WebDriver, add the following monkey patch for the Selenium::WebDriver::FirefoxProfile. The logic in this patch is that:

  • If a profile directory is not specified, use the normal/existing behaviour, which means copying an existing profile and deleting it on exit.
  • If a profile directory is specified:
    • If the directory exists, use that directory for the profile. It is assumed that the directory is a valid profile (ie I did not add checks to ensure it was valid).
    • If the directory does not exist, a copy of an existing profile will be created (ie the normal behaviour). This profile will then be moved to the specified directory. The profile is not deleted on exit and therefore can be re-used again later.

Patch:

require 'watir-webdriver'
require 'selenium-webdriver'

module Selenium
  module WebDriver
    module Firefox
      class Profile
        class << self
          attr_accessor :webdriver_profile_directory
        end

        def layout_on_disk
          # When a directory is specified, ensure it is not deleted at exit
          if Profile.webdriver_profile_directory
            FileReaper.reap = false
          end

          # Use the specified directory if it already exists (ie assuming an existing profile)
          if Profile.webdriver_profile_directory && Dir.exists?(Profile.webdriver_profile_directory)
            return Profile.webdriver_profile_directory
          end

          # Create the profile directory as usual when it does not exist
          profile_dir = @model ? create_tmp_copy(@model) : Dir.mktmpdir("webdriver-profile")
          FileReaper << profile_dir

          install_extensions(profile_dir)
          delete_lock_files(profile_dir)
          delete_extensions_cache(profile_dir)
          update_user_prefs_in(profile_dir)

          # If a directory is specified, move the created profile to that directory
          if Profile.webdriver_profile_directory
            FileUtils.cp_r(profile_dir, Profile.webdriver_profile_directory)
            profile_dir = Profile.webdriver_profile_directory
          end

          profile_dir
        end
      end # Profile
    end # Firefox
  end # WebDriver
end # Selenium

To specify the profile location to use do the following:

Selenium::WebDriver::Firefox::Profile.webdriver_profile_directory = 'C:/temp/test-profile'
browser = Watir::Browser.new :firefox

This patch may have limitations and untested areas. For example, it probably will not handle creating multiple instances of Firefox. However, for a single instance, it at least seems to work with reloading bookmarks created (which was the limit of my testing).

Marylynnmarylynne answered 4/11, 2014 at 17:29 Comment(2)
Thank you Justin, I was wondering, are you familiar with any other browser driver except Chrome that can save the user directory to disc?Puncture
Unfortunately I do not know. Profiles are not an area that I have had much work in.Marylynnmarylynne
G
5

This is for firefox 60+ and the new marionette/geckodriver.

After fiddling a lot, this is how I got it working with a custom profile:

$ xvfb-run firefox -CreateProfile test
options = Selenium::WebDriver::Firefox::Options.new # no profile specified here
options.add_argument "--profile"
options.add_argument "/home/jenkins/.mozilla/firefox/f0jecsmr.test"
@browser = Watir::Browser.new :firefox, options: options, driver_opts: {marionette_port: 2828}, **capabilities

It is close to how documentation suggests to do with python. It took reading the source to figure out these driver_opts though (or I didn't find relevant documentation).

HTH somebody.

Gillian answered 11/4, 2019 at 15:27 Comment(0)
M
2

Based on Selenium Issue 1954 and Issue 7374, the Firefox driver does not currently have this functionality. It is hard to tell if it will be implemented given that some of the project members are opposing the idea.

For now, I think you will have to monkey-patch your Selenium-WebDiver version to add this functionality. I reached the same conclusion as @shri046's answer, which is to modify the layout_on_disk method.

After you have required Selenium-WebDriver, add the following monkey patch for the Selenium::WebDriver::FirefoxProfile. The logic in this patch is that:

  • If a profile directory is not specified, use the normal/existing behaviour, which means copying an existing profile and deleting it on exit.
  • If a profile directory is specified:
    • If the directory exists, use that directory for the profile. It is assumed that the directory is a valid profile (ie I did not add checks to ensure it was valid).
    • If the directory does not exist, a copy of an existing profile will be created (ie the normal behaviour). This profile will then be moved to the specified directory. The profile is not deleted on exit and therefore can be re-used again later.

Patch:

require 'watir-webdriver'
require 'selenium-webdriver'

module Selenium
  module WebDriver
    module Firefox
      class Profile
        class << self
          attr_accessor :webdriver_profile_directory
        end

        def layout_on_disk
          # When a directory is specified, ensure it is not deleted at exit
          if Profile.webdriver_profile_directory
            FileReaper.reap = false
          end

          # Use the specified directory if it already exists (ie assuming an existing profile)
          if Profile.webdriver_profile_directory && Dir.exists?(Profile.webdriver_profile_directory)
            return Profile.webdriver_profile_directory
          end

          # Create the profile directory as usual when it does not exist
          profile_dir = @model ? create_tmp_copy(@model) : Dir.mktmpdir("webdriver-profile")
          FileReaper << profile_dir

          install_extensions(profile_dir)
          delete_lock_files(profile_dir)
          delete_extensions_cache(profile_dir)
          update_user_prefs_in(profile_dir)

          # If a directory is specified, move the created profile to that directory
          if Profile.webdriver_profile_directory
            FileUtils.cp_r(profile_dir, Profile.webdriver_profile_directory)
            profile_dir = Profile.webdriver_profile_directory
          end

          profile_dir
        end
      end # Profile
    end # Firefox
  end # WebDriver
end # Selenium

To specify the profile location to use do the following:

Selenium::WebDriver::Firefox::Profile.webdriver_profile_directory = 'C:/temp/test-profile'
browser = Watir::Browser.new :firefox

This patch may have limitations and untested areas. For example, it probably will not handle creating multiple instances of Firefox. However, for a single instance, it at least seems to work with reloading bookmarks created (which was the limit of my testing).

Marylynnmarylynne answered 4/11, 2014 at 17:29 Comment(2)
Thank you Justin, I was wondering, are you familiar with any other browser driver except Chrome that can save the user directory to disc?Puncture
Unfortunately I do not know. Profiles are not an area that I have had much work in.Marylynnmarylynne
A
0

Looking at the source code for Firefox profile it appears that there is a method that will allow you to write a newly created profile to disk

  def layout_on_disk
    profile_dir = @model ? create_tmp_copy(@model) : Dir.mktmpdir("webdriver-profile")
    FileReaper << profile_dir

    install_extensions(profile_dir)
    delete_lock_files(profile_dir)
    delete_extensions_cache(profile_dir)
    update_user_prefs_in(profile_dir)

    profile_dir
  end

Now there isn't much documentation around the Ruby implementation but the Java equivalent of this method has these details

 /**
  * Call this to cause the current profile to be written to disk. The profile directory is
  * returned. Note that this profile directory is a temporary one and will be deleted when the JVM
  * exists (at the latest)
  * 
  * This method should be called immediately before starting to use the profile and should only be
  * called once per instance of the {@link org.openqa.selenium.firefox.FirefoxDriver}.
  * 
  * @return The directory containing the profile.
  */

To make all this work here is an outline of what has to be done

  1. Set webdriver.reap_profile system property to false. This will prevent clean up of the temporary Firefox profile created.
  2. Create a new Firefox profile, invoke the layoutOnDisk() method to write the profile to disk.
  3. The file path returned from the above step will be stored in a common variable for all tests.
  4. Initiate WebDriver with the newly created profile.

I have not tried to implement this and test it so this may not be the most accurate or workable solution - if it indeed works. It depends on your use case as to why you would want to re-use the same profile across all tests. Hope this helps.

Ambiguity answered 4/11, 2014 at 16:13 Comment(1)
Thank you but I'm looking for a concrete, not a conceptual solution.Puncture
C
-3

You can create a Firefox profile using:

profile = Selenium::WebDriver::Firefox::Profile.new
b = Watir::Browser.new :firefox, :profile => profile`
Cannibalize answered 4/11, 2014 at 10:59 Comment(2)
Have you read my question? This creates the new profile but my question wasn't only how to create it, but load it as well. This also doesn't allow for creation of multiple profiles.Puncture
This is a question that has been passed to Watir contributors and so far I've received answers from 2 that they don't know how to do this. Thus, the reason for the bounty. It's a hard question which requires research which I attempted to do but wasn't able to accomplish what I wanted.Puncture

© 2022 - 2024 — McMap. All rights reserved.