Advanced FireWatir – cheat sheet

by matt 20. October 2009 23:18

OK, so the last post was a bit heavy. Er, detailed. The upshot is that FireWatir, a Ruby library for automating Firefox via the JSSh (JavaScript Shell) extension can get access to the same APIs used by JavaScript Firefox extensions, allowing for some very low level automation of Firefox. The example in that post showed how to send arbitrary extra headers when navigating to a page:

# Essentially a copy of goto(uri) but can pass headers through the request
# Pass a list of headers, e.g. [ "X-Forwarded-For: 10.15.142.22", "cheese: toast" ]
# (Note that we should also be able to post data through this mozilla method)
def goto_with_headers(url, headers)
  #set_defaults()
  get_window_number()
  set_browser_document()
  h = ""
  headers.each {|value| h += "#{value}\\r\\n" }
  # Load the given url.
  jssh_command = "var headers ="
jssh_command += "Components.classes['@mozilla.org/io/string-input-stream;1']"
jssh_command += ".createInstance(Components.interfaces.nsIStringInputStream);
" jssh_command += " headers.setData(\"#{h}\", #{h.length});" # first null is referrer, second is postData jssh_command += " #{BROWSER_VAR}.webNavigation.loadURI(\"#{url}\", 0, null,”
jssh_command += "null, headers);
" $jssh_socket.send("#{jssh_command}\n", 0) read_socket() wait() end

I thought it might be useful to show another example, without having to explain the background. If you’re interested in details, read the last post.

Right. Let’s automate Firefox’s preferences. Again, searching for what extensions do, they use the nsIPrefBranch interface to get and set preferences. We’ll do the same.

Instead of using the createInstance method to get the interface, we’re going to use getService (presumably because the preferences service object is already in memory, and we want to get a handle on that, rather than a new instance). In Javascript:

var prefs = Components.classes[\"@mozilla.org/preferences-service;1\"]
    .getService(Components.interfaces.nsIPrefBranch);"
prefs.setBoolPref("app.update.enabled", true);

And the Ruby helper method from my version of FireWatir::Firefox (note the escaped quotes):

def set_bool_preference(key, value)
  jssh_command = "var prefs = Components"
jssh_command += ".classes[\"@mozilla.org/preferences-service;1\"]
" jssh_command += ".getService(Components.interfaces.nsIPrefBranch);" jssh_command += " prefs.setBoolPref(\"#{key}\", #{value});" $jssh_socket.send("#{jssh_command}\n", 0) read_socket() end

Other preferences are stored as strings and as comma separated string values:

def set_string_preference(key, value)
  jssh_command = "var prefs = Components"
jssh_command += ".classes[\"@mozilla.org/preferences-service;1\"]
" jssh_command += ".getService(Components.interfaces.nsIPrefBranch);" jssh_command += " prefs.setCharPref(\"#{key}\", \"#{value}\");" $jssh_socket.send("#{jssh_command}\n", 0) read_socket() end def add_string_preference(key, value) jssh_command = "var prefs = Components"
jssh_command += ".classes[\"@mozilla.org/preferences-service;1\"]
" jssh_command += ".getService(Components.interfaces.nsIPrefBranch);" jssh_command += " var value = prefs.getCharPref(\"#{key}\");" jssh_command += " if (value === '') { value = \"#{value}\"; } else {" jssh_command += " var values = value.split(\",\");" jssh_command += " if (values.indexOf(\"#{value}\") === -1) {"
jssh_command += " values.push(\"#{value}\");"
jssh_command += " }
" jssh_command += " value = values.join(\",\");" jssh_command += " }" jssh_command += " prefs.setCharPref(\"#{key}\", value);" $jssh_socket.send("#{jssh_command}\n", 0) read_socket() end

These can be used to make sure that your test environment’s version of Firefox is correctly setup before using. And if you’re wondering what to use for “key”, then take a browse through Firefox’s about:config (then Google it to see what it does). Here’s what I’m using at work:

browser.set_bool_preference("browser.download.manager.showWhenStarting", false)
# Convert "/" to "\". Double escaped because we're putting it into a JS string
browser.set_string_preference("browser.download.dir",
default_download_dir.gsub(/\//, '\\\\\\')) browser.add_string_preference("browser.helperApps.neverAsk.saveToDisk",
"application/zip") browser.add_string_preference("browser.helperApps.neverAsk.saveToDisk",
"application/postscript") browser.set_bool_preference("app.update.enabled", false) browser.set_bool_preference("browser.search.update", false) browser.set_bool_preference("extensions.update.enabled", false) browser.set_bool_preference("browser.sessionstore.resume_from_crash", false) browser.add_string_preference("network.automatic-ntlm-auth.trusted-uris",
"http://xxx.example.com") browser.add_string_preference("network.automatic-ntlm-auth.trusted-uris",
"http://yyy.examplecom") browser.add_string_preference("network.automatic-ntlm-auth.trusted-uris",
"http://zzz.example.com")

Most useful keys?

  • network.automatic-ntlm-auth.trusted-uris. With this, I can add a domain to automatically get NTLM authentication, so I know my tests won’t get stuck at an authentication prompt.
  • browser.helperApps.neverAsk.saveToDisk. I didn’t even know this one existed, but it’s great. This is a comma separated list of mime types that will always get saved to disk, so again, I know that I won’t get stuck at the confirm download prompt. (This is my favourite – previously, we’ve had to set these up by hand by actually downloading an instance of the file. I’m very happy to be automating this.)

Handy, eh?

One more post to go – let’s dive a bit deeper.

Tags: , , ,

Comments

10/22/2009 8:02:01 PM #

Ethan

You may be interested in work that I have done with JsshSocket, relating to other work I have done with FireWatir. You can see JsshSocket, and related JsshObject, at:

github.com/.../jssh_socket.rb

I have change a lot of FireWatir code (well, most of the firewatir code in fact) to rely on JsshObjects.

Basically, this exposes properties and methods of javascript objects into ruby in a similar manner to how WIN32OLE does. For example, I've rewritten the preference functions you have to use JsshObjects, much more simply, and much more ruby-like:

def set_bool_preference(key, value)
  prefs=jssh_socket.Components.classes['@mozilla.org/preferences-service;1'].getService(jssh_socket.Components.interfaces.nsIPrefBranch)
  prefs.setBoolPref(key, value)
end

def set_string_preference(key, value)
  prefs=jssh_socket.Components.classes['@mozilla.org/preferences-service;1'].getService(jssh_socket.Components.interfaces.nsIPrefBranch)
  prefs.setCharPref(key, value)
end

def add_string_preference(key, new_value)
  prefs=jssh_socket.Components.classes['@mozilla.org/preferences-service;1'].getService(jssh_socket.Components.interfaces.nsIPrefBranch)
  curr_value = prefs.getCharPref(key)
  if curr_value != ''
    values = curr_value.split(',')
    unless values.include?(new_value)
      values << new_value
    end
    new_value=values.join(',')
  end
  prefs.setCharPref(key, new_value)
end

Ethan | Reply

11/15/2009 10:34:47 PM #

matt

I love this! It's a much nicer way of defining the methods than I had. I hope this makes it into the main FireWatir code... Thanks for the heads up!

matt | Reply

Add comment


(Will show your Gravatar icon)

biuquote
  • Comment
  • Preview
Loading



About the author

Something about the author

Calendar

<<March 2010>>
MoTuWeThFrSaSu
22232425262728
1234567
891011121314
15161718192021
22232425262728
2930311234

View posts in large calendar

RecentComments

Comment RSS

License

Creative Commons License
Except where otherwise noted, content on this site is by Matt Ellis and is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.

©2010 Matt Ellis