Marionette
Selenium alternative for Crystal. Browser manipulation without the Java overhead.
Install / Use
/learn @watzon/MarionetteREADME
Marionette
Looking for a co-maintainer for Marionette. If interested just respond in the issue titled "Looking for a co-maintainer".
Marionette is a one-size-fits-all approach to WebDriver adapters. It works with most all web driver implementations, including:
- [x] Chrome
- [x] Chromium
- [x] Firefox
- [x] Safari
- [x] Edge
- [x] Internet Explorer
- [x] Opera
- [x] PhantomJS
- [x] Webkit GTK
- [x] WPE Webkit
- [x] Android
Table of Contents
Installation
-
Make sure you have Crystal installed. This is a Crystal project and Crystal is required for usage. If you don't have it installed, see https://crystal-lang.org.
-
Add Marionette to an existing project by adding the dependency to your
shard.ymldependencies: marionette: github: watzon/marionette branch: master -
Run
shards installto download and install Marionette as a dependency. -
Download and have installed at least one WebDriver. See the #webdriver section below for links to various downloads.
WebDriver
WebDriver is a protocol which allows browser implementations to be remote controlled via a common interface. It's because of this functionality that frameworks like Marionette are possible. To use the protocol you first have to have installed one of the many WebDriver implementations, here are some of those:
Firefox
GeckoDriver is implemented and supported by Mozilla directly.
Chrome
ChromeDriver is implemented and supported by the Chromium Project.
Opera
OperaChromiumDriver is implemented and supported by Opera Software.
Safari
SafariDriver is implemented and supported directy by Apple. It comes pre-installed with Safari and Safari Technology Preview.
Edge
Microsoft is implementing and maintaining the Microsoft Edge WebDriver.
Internet Explorer
Only version 11 is supported, and it requires additional configuration.
Note: Marionette specific configuration instructions coming soon.
Getting Started
The goal of Marionette is simplicity, which is why it's written in Crystal. Once you have a webdriver installed and sitting comfortably on your path, using it couldn't be easier:
require "marionette"
session = Marionette::WebDriver.create_session(:chrome)
# Navigate to crystal-lang.org
session.navigate("https://crystal-lang.org")
# Start an action chain and perform it
session.perform_actions do
# Click the "INSTALL" link
click ".main-actions a:nth-child(1)"
end
sleep 5
session.close
Driver Capabilities
Different drivers have different capabilities available to them. To make setting them a little easier, there's the DriverOptions module which is extended by Marionette itself. Take, for instance, Chrome:
# Make this instance headless
options = Marionette.chrome_options(args: ["headless"])
# Create a Chrome session
session = Marionette::WebDriver.create_session(:chrome, capabilities: options)
args in this case are arguments to be passed to the browser itself rather than the driver. If you wish to pass arguments to the driver you can use the args parameter in the create_session method.
Browser Manipulation
As shown above, you can initialize a new driver session for whatever driver you want using Marionette::WebDriver.create_session, the first and most important argument to which is :browser. Browser can be any of :chrome, :firefox, :opera, :safari, :edge, :internet_explorer, :webkit_gtk, :wpe_webkit, or :android.
If the driver for the chosen browser is installed under its usual name that should be all you need to do, if not you may need to provide the binary location via the :exe_path argument. Other notable arguments are:
:port- sets the port you want the driver to listen on:env- a hash of environment variables for the driver to be aware of:args- a string array of arguments to pass to the webdriver process:options- a JSON compatible structure containing browser options. see here for some nice helpers.
Navigation
#navigate
The first thing you will want to do after launching a browser is to open your website. This can be achieved in a single line:
session.navigate("https://crystal-lang.org")
#current_url
You can read the current URL from the browser’s address bar using:
session.current_url
# => https://crystal-lang.org
#back
Pressing the browser’s back button:
session.back
#forward
Pressing the browser’s forward button:
session.forward
#refresh
Refresh the current page:
session.refresh
#title
You can read the current page title from the browser:
session.title
# => Crystal | The Crystal Programming Language
Windows and Tabs
WebDriver does not make the distinction between windows and tabs. If your site opens a new tab or window, Marionette will let you work with it using a window handle. Each window has a unique identifier which remains persistent in a single session.
#current_window
You can get the currently active window using:
session.current_window
This returns a Window instance containing a handle and allowing certain functions to be performed directly on the window instance.
#windows
You can get an array of all currently opened windows using:
session.windows
#new_window
You can create a new window or tab using:
session.new_window(:window) # default
session.new_window(:tab)
# Or using the Window object
Marionette::Window.new(:window) # default
Marionette::Window.new(:tab)
#switch_to_window
To interact with other windows you have to switch to them, this can be done with:
session.switch_to_window(window)
# Or using the Window object
window.switch
#close_window
When you are finished with a window or tab and it is not the last window or tab open in your browser, you should close it and switch back to the window you were using previously:
session.close_window(window)
# Or using the Window object
window.close
#close_current_window
Think of this is a shortcut to #close_window but for the currently active window:
session.close_current_window
#stop
When you are finished with the browser session you should call stop, instead of close:
session.stop
Stop will:
- Close all the windows and tabs associated with that WebDriver session
- Close the browser process
- Close the background driver process
Stop will be automatically closed on process exit.
Frames and IFrames
Frames are a now deprecated means of building a site layout from multiple documents on the same domain. You are unlikely to work with them unless you are working with an pre HTML5 webapp. Iframes allow the insertion of a document from an entirely different domain, and are still commonly used.
If you need to work with frames or iframes, WebDriver allows you to work with them in the same way. Consider a button within an iframe. If we inspect the element using the browser development tools, we might see the following:
<div id="modal">
<iframe id="buttonframe" name="myframe" src="https://watzon.github.io">
<button>Click here</button>
</iframe>
</div>
If it was not for the iframe we would expect to click on the button using something like:
session.find_element!("button").click
However, if there are no buttons outside of the iframe, you might instead get a no such element error. This happens because Marionette is only aware of the elements in the top level document. To interact with the button, we will need to first switch to the frame, in a similar way to how we switch windows. WebDriver offers three ways of switching to a frame.
#switch_to_frame
The switch_to_frame session method allows us to tell the WebDriver that
Related Skills
gh-issues
341.2kFetch GitHub issues, spawn sub-agents to implement fixes and open PRs, then monitor and address PR review comments. Usage: /gh-issues [owner/repo] [--label bug] [--limit 5] [--milestone v1.0] [--assignee @me] [--fork user/repo] [--watch] [--interval 5] [--reviews-only] [--cron] [--dry-run] [--model glm-5] [--notify-channel -1002381931352]
node-connect
341.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
oracle
341.2kBest practices for using the oracle CLI (prompt + file bundling, engines, sessions, and file attachment patterns).
tmux
341.2kRemote-control tmux sessions for interactive CLIs by sending keystrokes and scraping pane output.
