marionette
Marionette
Marionette is a Crystal shard that replaces the functionality of Selenium (Firefox only for now) by communicating directly with an instance of the browser. It provides a simple, but powserful API which allows everything from navigation to screenshots to executing JavaScript.
- Marionette
- Installation
- Usage
- Launch options
- The Browser Class
- new_session(capabilities)
- close_session
- on_request(&block : HTTP::Server::Context ->)
- on_headers(&block : HTTP::Headers ->)
- on_har_capture(&block : HAR::Entries ->)
- har_entries
- generate_har
- export_har(file, har = nil)
- goto(url)
- title
- url
- refresh
- back
- forward
- set_context
- context
- using_context(&block)
- current_window_handle
- current_chrome_window_handle
- window_handles
- switch_to_window(handle)
- window_rect
- set_window_rect(rect : WindowRect)
- maximize_window
- minimize_window
- fullscreeen
- close_window
- orientation
- set_orienation
- active_frame
- switch_to_frame(frame : String | HTMLElement | Nil, focus: true)
- switch_to_frame(by : LocatorStrategy, value, focus = true)
- switch_to_parent_frame
- cookies
- cookie(name)
- element_enabled?(el)
- element_selected?(el)
- element_displayed?(el)
- element_tag_name(el)
- element_text(el)
- element_attribute(el, name)
- element_css_property(el, property)
- element_rect(el)
- click_element(el)
- send_keys_to_element(el, *keys)
- clear_element(el)
- find_elements(by : LocatorStrategy, value, start_node = nil)
- find_element(by : LocatorStrategy, value, start_node = nil)
- take_screenshot(**options)
- save_screenshot(file, **options)
- execute_script(script, args = nil, timeout = @timeout, new_sandbox = true)
- execute_script_async(script, args = nil, timeout = @timeout, new_sandbox = true)
- dismiss_dialog
- accept_dialog
- get_text_from_dialog
- send_keys_to_dialog(*keys)
- quit
- restart
- clear_pref(pref)
- pref(pref, default_branch = false, value_type = "unspecified")
- set_pref(pref, value, default_branch = false)
- set_prefs(prefs, defualt_branch = false)
- using_prefs(prefs, default_branch = false, &block)
- Contributing
- Contributors
Installation
Add this to your application's shard.yml
:
dependencies:
marionette:
github: watzon/marionette
Usage
First, of course, you need to require marionette in your project.
require "marionette"
Marionette
itself is a module which exposes two methods:
launch(**options)
launch(**options, &block : Browser ->)
The first launch
method accepts the launch options listed below and returns a new Browser
instance. The browser will not be closed at the end of the program's execution, so it's important to remember to run Browser#quit
if the browser process was created with marionette.
The second launch
method accepts the same launch options and a block. The newly created Browser
instance is yielded to the block and the browser process will be closed automatically at the end of the block if the process was created with marionette.
Launch options
Marionette.launch
accepts all the same arguments as Launcher#launch
. These arguments are:
- address - The address that Firefox is listening on. (default: 127.0.0.1)
- port - The port that Firefox is listening on. (default: 2828)
- executable - The executable to launch. If
nil
an executable will be searched for. Iffalse
no executable will be launched. - args - Arguments to pass to the Firefox process (only if executable is not false)
- profile - User profile path to launch with (only if executable is not false)
- headless - Launch browser in headless mode (default: true) (only if executable is not false)
- stdout -
IO
to use for STDOUT (only if executable is not false) - stderr -
IO
to use for STDERR (only if executable is not false) - accept_insecure_certs - Open all connections, even if the cert is invalid
- env - Environment to pass to
Process
(only if executable is not false) - default_viewport - Default size of the browser window (default: {width: 800, height: 600})
- timeout - Universal timeout (default: 60000)
- proxy - NamedTuple with
address
andport
for proxy.
The Browser Class
Most of the time while using marionette you will be dealing directly with the Browser
class. As its name implies, the Browser
class represents the browser instance. It includes a plethora of methods for interacting with the browser, I'll try and document them here as best as I can.
new_session(capabilities)
Create a new browser session with provided capabilities
and returns the session_id
.
browser.new_session({"browserName": "chrome", "platformName": "linux"})
close_session
Closes the current session without shutting down the browser.
browser.close_session
on_request(&block : HTTP::Server::Context ->)
Note: To use this method the
extended
option must be set to true.
Passes the full HTTP::Server::Context
to the provided block for every request made by the browser. This method can be called more than once to add multiple handlers.
browser.on_request do |ctx|
pp ctx.requst
pp ctx.response
end
on_headers(&block : HTTP::Headers ->)
Note: To use this method the
extended
option must be set to true.
Passes the headers for every request to the provided block. This method can be called more than once to add multiple handlers.
browser.on_headers do |headers|
pp headers
end
on_har_capture(&block : HAR::Entries ->)
Note: To use this method the
extended
option must be set to true.
When the extended
option is set to true automatic HAR capturing will be enabled. on_har_capture
sends each captured HAR::Entries
object to the provided block.
browser.on_har do |har|
pp har
end
har_entries
Note: To use this method the
extended
option must be set to true.
Lists each an every HAR::Entries
object captured so far.
browser.har_entries
# => [] of HAR::Entries
generate_har
Note: To use this method the
extended
option must be set to true.
Generates a HAR::Data
object which can be modified or converted to json using to_json
. This represents a .har
file.
browser.generate_har
# => <#HAR::Data ...>
export_har(file, har = nil)
Note: To use this method the
extended
option must be set to true.
Generates and saves a .har
file to the specified path. You can optionally provide it with a HAR::Data
object to be saved.
browser.export_har("google.com.har")
goto(url)
Navigates the browser to the specified URL. If extended
is true this will use a proxy under the hood to fetch the content from the URL and deliver it to the browser, otherwise the default WebDriver method will be used.
browser.goto("https://www.google.com")
title
Gets the title of the current page.
browser.goto("https://www.google.com")
browser.title
# => "Google"
url
Gets the URL of the current page.
browser.goto("https://www.google.com")
browser.url
# => "https://www.google.com"
refresh
Refreshes the page.
browser.refresh
back
Goes back to the previous page.
browser.back
forward
Goes forward to the next page.
browser.forward
set_context
Sets the context of subsequent commands to be either :chrome
(allowing you access to the Firefox UI itself) or :content
(allowing access to the current page).
browser.set_context(:chrome)
browser.set_context(:content)
context
Gets the current browser context.
browser.context
# => BrowserContext::Content
browser.set_context(:chrome)
browser.context
# => BrowserContext::Chrome
using_context(&block)
Sets the context for the provided block, then returns it to the previous context.
using_context do
# Do stuff
end
current_window_handle
Gets the handle for the current window, useful for switching between window instances.
browser.current_window_handle
current_chrome_window_handle
Get the current chrome window's handle. Corresponds to a chrome window that may itself contain tabs identified by window_handles.
browser.current_chrome_window_handle
window_handles
Returns an array of handles for currently open windows.
browser.current_window_handles
# => ["123..."]
switch_to_window(handle)
Switches to the window with the provided handle.
browser.switch_to_window("123...")
window_rect
Gets the current window as a WindowRect
instance containing it's x
and y
positions as well as it's width
and height
.
browser.window_rect
# => <#Window:Rect x: 0, y: 0, width: 800, height: 600>
set_window_rect(rect : WindowRect)
Sets the window's size and position according to the provided WindowRect
instance.
rect = Marionette::WindowRect.new(x: 0, y: 0, width: 800, height: 600)
browser.set_window_rect(rect)
maximize_window
Maximizes the current window.
browser.maximize_window
minimize_window
Minimizes the current window.
browser.minimize_window
fullscreeen
Makes the current window fullscreen.
browser.fullscreen
close_window
Closes the current window.
browser.close_window
orientation
Get the screen orientation of the current browser. Returns either portrait-primary, landscape-primary, portrait-secondary, or landscape-secondary.
TODO: Use an enum for this.
browser.orientation
# => "portrait-primary"
set_orienation
Set the screen orientation to one of portrait-primary, landscape-primary, portrait-secondary, or landscape-secondary.
TODO: Use an enum for this.
browser.set_orientation("landscape-secondary")
active_frame
Gets the current frame as an HTMLElement
or nil if the top level frame is active. (Note that frame means iframe)
browser.active_frame
# => nil
switch_to_frame(frame : String | HTMLElement | Nil, focus: true)
Sets the active frame to the provided element or element id. If frame is nil the top level frame will be the active frame.
el = browser.find_element(:xpath, "//iframe")
browser.switch_to_frame(el) if el
browser.active_frame
# => <#HTML::Element ...>
switch_to_frame(by : LocatorStrategy, value, focus = true)
Convenience method for finding an element and switching the active frame to it. See find_element
.
switch_to_parent_frame
Switches the frame to the parent of the active frame.
browser.switch_to_parent_frame
browser.active_frame
# => nil
cookies
Get's all cookies for the current page as HTTP::Cookie
instaneces.
browser.cookies
# => [<#HTTP::Cookie name: "KBD", value: "8hq2eko2epoijADlkjh9">]
cookie(name)
Gets a single cookie by name. Retuns nil if the cookie doesn't exist.
browser.cookie("KBD")
# => <#HTTP::Cookie name: "KBD", value: "8hq2eko2epoijADlkjh9">
element_enabled?(el)
Returns true if the provided element is enabled. el
can be a HTMLElement
or the ID of an element.
browser.element_enabled?(el)
# => true
element_selected?(el)
Returns true if the provided element is selected. el
can be a HTMLElement
or the ID of an element.
browser.element_selected?(el)
# => false
element_displayed?(el)
Returns true if the provided element is displayed. el
can be a HTMLElement
or the ID of an element.
browser.element_displayed?(el)
# => true
element_tag_name(el)
Returns the tag name of el
. el
can be a HTMLElement
or the ID of an element.
browser.element_tag_name(el)
# => "input"
element_text(el)
Returns the text of el
. el
can be a HTMLElement
or the ID of an element.
browser.element_text(el)
# => ""
element_attribute(el, name)
Returns the value of the provided atribute name for el
. el
can be a HTMLElement
or the ID of an element.
browser.element_attribute(el, "value")
# => "Hello world"
element_css_property(el, property)
Returns the value of the provided css property for el
. el
can be a HTMLElement
or the ID of an element.
browser.element_css_property(el, "background-color")
# => "#FFFFFF"
element_rect(el)
Returns a ElementRect
instance representing the provided element's position and size within the browser. el
can be a HTMLElement
or the ID of an element.
browser.element_rect(el)
# => <#Elementrect x: 123.0, y: 99.0, width: 100.0, height: 80.0>
click_element(el)
Simulate a click on el
. el
can be a HTMLElement
or the ID of an element.
browser.click_element(el)
send_keys_to_element(el, *keys)
Sends keys to the provided element. Keys are strings. Keystrokes for special keys such as return anc backspace can be simulated by sending a special unicode character. To make this easier their is a convenience method [key
]. el
can be a HTMLElement
or the ID of an element.
browser.send_keys(input, "Hello world", key(:enter))
clear_element(el)
Clears a clearable element, such as an input
. el
can be a HTMLElement
or the ID of an element.
browser.clear_element(input)
find_elements(by : LocatorStrategy, value, start_node = nil)
Find all elements on the current page using the specified LocatorStrategy
. If a start_node
is provided it will be used as the container to search inside of. start_node
can be a HTMLElement
or the ID of an element.
inputs = browser.find_elements(:xpath, "//input")
find_element(by : LocatorStrategy, value, start_node = nil)
Find a single on the current page using the specified LocatorStrategy
. If a start_node
is provided it will be used as the container to search inside of. start_node
can be a HTMLElement
or the ID of an element.
input = browser.find_element(:xpath, "//input")
take_screenshot(**options)
Takes a screenshot of a particular element or the current frame. If the current context is set to :chrome
the screenshot will be of the entire browser, otherwise the screenshot will be of the current page or provided element.
Options
- element : HTMLElement | String | Nil - Element to take a screenshot of.
- hightlights : Array(HTMLElement | String) | Nil - Array of elements to highlight.
- full : Bool - Take a screenshot of the full page.
- scroll : Bool - Scroll to the provided element.
- format : ScreenshotFormat - Format to export the screenshot as.
browser.take_screenshot(
element = nil,
highlights = nil,
full = true,
scroll = true,
format = :binary
)
save_screenshot(file, **options)
Take and save the screenshot to the specified file. Accepts the same options as take_screenshot
execute_script(script, args = nil, timeout = @timeout, new_sandbox = true)
Execute JavaScript on the current page. script
should be a valid JavaScript document as a String. args
are the arguments to provide, which can be accessed with arguments[n]
in the script. You can also provide a timeout
and tell the browser to execute this code in a new sandbox (true by default). If sandbox
is false the variables from any previously executed script will still exist.
Scripts are executed in the browser context and have access to XPCOM bindings, allowing you to actually control aspects of the browser through JavaScript. Something that normally can't be done with sansboxed code executed by websites.
browser.execute_script("alert(arguments[0])", ["Hello world"])
execute_script_async(script, args = nil, timeout = @timeout, new_sandbox = true)
Like execute_script
, but asynchronous.
browser.execute_script_async("alert(arguments[0])", ["Hello world"])
dismiss_dialog
Dismisses a dialog/alert if there is one. Same as clicking no/cancel.
browser.dismiss_dialog
accept_dialog
Accepts a dialog if there is one. Same as clicking ok/yes.
browser.accept_dialog
get_text_from_dialog
Gets the text from the current dialog if there is one, otherwise returns nil.
browser.execute_script("alert(arguments[0])", ["Hello world"])
browser.get_text_from_dialog
# => "Hello world"
send_keys_to_dialog(*keys)
Sends keys to a dialog. Similar to 'send_keys_to_element`.
browser.send_keys_to_dialog("My name", key(:enter))
quit
Closes the browser.
browser.quit
restart
Attempts to restart the browser without closing it. (Not working yet)
browser.restart
clear_pref(pref)
Sets a preference back to it's default value.
pref(pref, default_branch = false, value_type = "unspecified")
Gets the value of a user defined preference.
set_pref(pref, value, default_branch = false)
Sets a preference to the provided value.
set_prefs(prefs, defualt_branch = false)
Accepts a Hash or Array of Tuples and sets each preference (key) to the value.
using_prefs(prefs, default_branch = false, &block)
Sets the preferences for the provided block and then resets them afterwards.
Contributing
- Fork it ( https://github.com/watzon/marionette/fork )
- Create your feature branch (git checkout -b my-new-feature)
- Commit your changes (git commit -am 'Add some feature')
- Push to the branch (git push origin my-new-feature)
- Create a new Pull Request
Contributors
- watzon - creator, maintainer