It is Ruby clean and high-level API to Chrome. Runs headless by default, but you can configure it to run in a headful mode. All you need is Ruby and Chrome or Chromium. Ferrum connects to the browser by CDP protocol and there's no Selenium/WebDriver/ChromeDriver dependency. The emphasis was made on a raw CDP protocol because Chrome allows you to do so many things that are barely supported by WebDriver because it should have consistent design with other browsers.
-
Cuprite is a pure Ruby driver for Capybara based on Ferrum. If you are going to crawl sites you better use Ferrum or Vessel because you crawl, not test.
-
Vessel high-level web crawling framework based on Ferrum and Mechanize.
- Install
- Examples
- Docker
- Customization
- Navigation
- Finders
- Screenshots
- Screencast
- Network
- Downloads
- Proxy
- Mouse
- Keyboard
- Cookies
- Headers
- JavaScript
- Frames
- Frame
- Dialogs
- Animation
- Node
- Tracing
- Clean Up
- Thread safety
- Development
- Contributing
- License
There's no official Chrome or Chromium package for Linux don't install it this
way because it's either outdated or unofficial, both are bad. Download it from
official source for Chrome or
Chromium.
Chrome binary should be in the PATH or BROWSER_PATH and you can pass it as an
option to browser instance see :browser_path in
Customization.
Add this to your Gemfile and run bundle install.
gem "ferrum"Navigate to a website and save a screenshot:
browser = Ferrum::Browser.new
browser.go_to("https://google.com")
browser.screenshot(path: "google.png")
browser.quitWhen you work with browser instance Ferrum creates and maintains a default page for you, in fact all the methods above
are sent to the page instance that is created in the default_context of the browser instance. You can interact
with a page created manually and this is preferred:
browser = Ferrum::Browser.new
page = browser.create_page
page.go_to("https://google.com")
input = page.at_xpath("//input[@name='q']")
input.focus.type("Ruby headless driver for Chrome", :Enter)
page.at_css("a > h3").text # => "rubycdp/ferrum: Ruby Chrome/Chromium driver - GitHub"
browser.quitEvaluate some JavaScript and get full width/height:
browser = Ferrum::Browser.new
page = browser.create_page
page.go_to("https://www.google.com/search?q=Ruby+headless+driver+for+Capybara")
width, height = page.evaluate <<~JS
[document.documentElement.offsetWidth,
document.documentElement.offsetHeight]
JS
# => [1024, 1931]
browser.quitDo any mouse movements you like:
# Trace a 100x100 square
browser = Ferrum::Browser.new
page = browser.create_page
page.go_to("https://google.com")
page.mouse
.move(x: 0, y: 0)
.down
.move(x: 0, y: 100)
.move(x: 100, y: 100)
.move(x: 100, y: 0)
.move(x: 0, y: 0)
.up
browser.quitIn docker as root you must pass the no-sandbox browser option:
Ferrum::Browser.new(browser_options: { "no-sandbox": nil })It has also been reported that the Chrome process repeatedly crashes when running inside a Docker container on an M1 Mac preventing Ferrum from working. Ferrum should work as expected when deployed to a Docker container on a non-M1 Mac.
You can customize options with the following code in your test setup:
Ferrum::Browser.new(options)- options
Hash:headless(Boolean) - Set browser as headless or not,trueby default.:incognito(Boolean) - Create an incognito profile for the browser startup window,trueby default.:xvfb(Boolean) - Run browser in a virtual framebuffer,falseby default.:flatten(Boolean) - Use one websocket connection to the browser and all the pages in flatten mode.:window_size(Array) - The dimensions of the browser window in which to test, expressed as a 2-element array, e.g. [1024, 768]. Default: [1024, 768]:extensions(Array[String | Hash]) - An array of paths to files or JS source code to be preloaded into the browser e.g.:["/path/to/script.js", { source: "window.secret = 'top'" }]:logger(Object responding toputs) - When present, debug output is written to this object.:slowmo(Integer | Float) - Set a delay in seconds to wait before sending command. Useful companion of headless option, so that you have time to see changes.:timeout(Numeric) - The number of seconds we'll wait for a response when communicating with browser. Default is 5.:js_errors(Boolean) - When true, JavaScript errors get re-raised in Ruby.:pending_connection_errors(Boolean) - When main frame is still waiting for slow responses while timeout is reachedPendingConnectionsErroris raised. It's better to figure out why you have slow responses and fix or block them rather than turn this setting off. Default is true.:browser_name(Symbol) -:chromeby default, only experimental support for:firefoxfor now.:browser_path(String) - Path to Chrome binary, you can also set ENV variable asBROWSER_PATH=some/path/chrome bundle exec rspec.:browser_options(Hash) - Additional command line options, see them all e.g.{ "ignore-certificate-errors" => nil }:ignore_default_browser_options(Boolean) - Ferrum has a number of default options it passes to the browser, if you set this totruethen only options you put in:browser_optionswill be passed to the browser, except required ones of course.:port(Integer) - Remote debugging port for headless Chrome.:host(String) - Remote debugging address for headless Chrome.:url(String) - URL for a running instance of Chrome. If this is set, a browser process will not be spawned.:ws_url(String) - Websocket url for a running instance of Chrome. If this is set, a browser process will not be spawned. It's higher priority than:url, setting both doesn't make sense.:process_timeout(Integer) - How long to wait for the Chrome process to respond on startup.:ws_max_receive_size(Integer) - How big messages to accept from Chrome over the web socket, in bytes. Defaults to 64MB. Incoming messages larger than this will cause aFerrum::DeadBrowserError.:proxy(Hash) - Specify proxy settings, read more:save_path(String) - Path to save attachments with Content-Disposition header.:env(Hash) - Environment variables you'd like to pass through to the process
Navigate page to.
- url
StringThe url should include scheme unless you setbase_urlwhen configuring driver.
page.go_to("https://github.com/")Navigate to the previous page in history.
page.go_to("https://github.com/")
page.at_xpath("//a").click
page.backNavigate to the next page in history.
page.go_to("https://github.com/")
page.at_xpath("//a").click
page.back
page.forwardReload current page.
page.go_to("https://github.com/")
page.refreshStop all navigations and loading pending resources on the page
page.go_to("https://github.com/")
page.stopSet the position for the browser window
- options
Hash- :left
Integer - :top
Integer
- :left
browser.position = { left: 10, top: 20 }Get the position for the browser window
browser.position # => [10, 20]Set window bounds
- options
Hash- :left
Integer - :top
Integer - :width
Integer - :height
Integer - :window_state
String
- :left
browser.window_bounds = { left: 10, top: 20, width: 1024, height: 768, window_state: "normal" }Get window bounds
browser.window_bounds # => { "left": 0, "top": 1286, "width": 10, "height": 10, "windowState": "normal" }Current window id
browser.window_id # => 1Find node by selector. Runs document.querySelector within the document or
provided node.
- selector
String - options
Hash- :within
Node|nil
- :within
page.go_to("https://github.com/")
page.at_css("a[aria-label='Issues you created']") # => NodeFind nodes by selector. The method runs document.querySelectorAll within the
document or provided node.
- selector
String - options
Hash- :within
Node|nil
- :within
page.go_to("https://github.com/")
page.css("a[aria-label='Issues you created']") # => [Node]Find node by xpath.
- selector
String - options
Hash- :within
Node|nil
- :within
page.go_to("https://github.com/")
page.at_xpath("//a[@aria-label='Issues you created']") # => NodeFind nodes by xpath.
- selector
String - options
Hash- :within
Node|nil
- :within
page.go_to("https://github.com/")
page.xpath("//a[@aria-label='Issues you created']") # => [Node]Returns current top window location href.
page.go_to("https://google.com/")
page.current_url # => "https://www.google.com/"Returns current top window title
page.go_to("https://google.com/")
page.current_title # => "Google"Returns current page's html.
page.go_to("https://google.com/")
page.body # => '<html itemscope="" itemtype="http://schema.org/WebPage" lang="ru"><head>...Saves screenshot on a disk or returns it as base64.
- options
Hash- :path
Stringto save a screenshot on the disk.:encodingwill be set to:binaryautomatically - :encoding
Symbol:base64|:binaryyou can set it to return image as Base64 - :format
String"jpeg" ("jpg") | "png" | "webp" - :quality
Integer0-100 works for jpeg only - :full
Booleanwhether you need full page screenshot or a viewport - :selector
Stringcss selector for given element, optional - :area
Hasharea for screenshot, optional- :x
Integer - :y
Integer - :width
Integer - :height
Integer
- :x
- :scale
Floatzoom in/out - :background_color
Ferrum::RGBA.new(0, 0, 0, 0.0)to have specific background color
- :path
page.go_to("https://google.com/")
# Save on the disk in PNG
page.screenshot(path: "google.png") # => 134660
# Save on the disk in JPG
page.screenshot(path: "google.jpg") # => 30902
# Save to Base64 the whole page not only viewport and reduce quality
page.screenshot(full: true, quality: 60, encoding: :base64) # "iVBORw0KGgoAAAANSUhEUgAABAAAAAMACAYAAAC6uhUNAAAAAXNSR0IArs4c6Q...
# Save on the disk with the selected element in PNG
page.screenshot(path: "google.png", selector: "textarea") # => 11340
# Save to Base64 with an area of the page in PNG
page.screenshot(path: "google.png", area: { x: 0, y: 0, width: 400, height: 300 }) # => 54239
# Save with specific background color
page.screenshot(background_color: Ferrum::RGBA.new(0, 0, 0, 0.0))Saves PDF on a disk or returns it as base64.
- options
Hash-
:path
Stringto save a pdf on the disk.:encodingwill be set to:binaryautomatically -
:encoding
Symbol:base64|:binaryyou can set it to return pdf as Base64 -
:landscape
Booleanpaper orientation. Defaults to false. -
:scale
Floatzoom in/out -
:format
symbolstandard paper sizes :letter, :legal, :tabloid, :ledger, :A0, :A1, :A2, :A3, :A4, :A5, :A6 -
:paper_width
Floatset paper width -
:paper_height
Floatset paper height -
See other native options you can pass
-
page.go_to("https://google.com/")
# Save to disk as a PDF
page.pdf(path: "google.pdf", paper_width: 1.0, paper_height: 1.0) # => trueSaves MHTML on a disk or returns it as a string.
- options
Hash- :path
Stringto save a file on the disk.
- :path
page.go_to("https://google.com/")
page.mhtml(path: "google.mhtml") # => 87742Starts sending frames to record screencast to the given block.
-
options
Hash- :format
Symbol:jpeg|:pngThe format the image should be returned in. - :quality
IntegerThe image quality. Note: 0-100 works for JPEG only. - :max_width
IntegerMaximum screencast frame width. - :max_height
IntegerMaximum screencast frame height. - :every_nth_frame
IntegerSend every n-th frame.
- :format
-
Block inputs:
- data
StringBase64-encoded compressed image. - metadata
HashScreencast frame metadata.- "offsetTop"
IntegerTop offset in DIP. - "pageScaleFactor"
IntegerPage scale factor. - "deviceWidth"
IntegerDevice screen width in DIP. - "deviceHeight"
IntegerDevice screen height in DIP. - "scrollOffsetX"
IntegerPosition of horizontal scroll in CSS pixels. - "scrollOffsetY"
IntegerPosition of vertical scroll in CSS pixels. - "timestamp"
Float(optional) Frame swap timestamp in seconds since Unix epoch.
- "offsetTop"
- session_id
IntegerFrame number.
- data
require "base64"
page.go_to("https://apple.com/ipad")
page.start_screencast(format: :jpeg, quality: 75) do |data, metadata|
timestamp = (metadata["timestamp"] * 1000).to_i
File.binwrite("image_#{timestamp}.jpg", Base64.decode64(data))
end
sleep 10
page.stop_screencastChrome only sends new frames while page content is changing. For example, if there is an animation or a video on the page, Chrome sends frames at the rate requested. On the other hand, if the page is nothing but a wall of static text, Chrome sends frames while the page renders. Once Chrome has finished rendering the page, it sends no more frames until something changes (e.g., navigating to another location).
Stops sending frames.
page.network
Returns all information about network traffic as Network::Exchange instance
which in general is a wrapper around request, response and error.
page.go_to("https://github.com/")
page.network.traffic # => [#<Ferrum::Network::Exchange, ...]Page request of the main frame.
page.go_to("https://github.com/")
page.network.request # => #<Ferrum::Network::Request...Page response of the main frame.
page.go_to("https://github.com/")
page.network.response # => #<Ferrum::Network::Response...Contains the status code of the main page response (e.g., 200 for a
success). This is just a shortcut for response.status.
page.go_to("https://github.com/")
page.network.status # => 200Waits for network idle, returns true in case of success and false if there are still connections.
- options
Hash- :connections
Integerhow many connections are allowed for network to be idling,0by default - :duration
Floatsleep for given amount of time and check again,0.05by default - :timeout
Floatduring what time we try to check idle,browser.timeoutby default
- :connections
page.go_to("https://example.com/")
page.at_xpath("//a[text() = 'No UI changes button']").click
page.network.wait_for_idle # => trueWaits for network idle or raises Ferrum::TimeoutError error. Accepts same arguments as wait_for_idle.
page.go_to("https://example.com/")
page.at_xpath("//a[text() = 'No UI changes button']").click
page.network.wait_for_idle! # might raise an errorClear page's cache or collected traffic.
- type
Symbolit is either:trafficor:cache
traffic = page.network.traffic # => []
page.go_to("https://github.com/")
traffic.size # => 51
page.network.clear(:traffic)
traffic.size # => 0Set request interception for given options. This method is only sets request
interception, you should use on callback to catch requests and abort or
continue them.
- options
Hash- :pattern
String* by default - :resource_type
Symbolone of the resource types
- :pattern
browser = Ferrum::Browser.new
page = browser.create_page
page.network.intercept
page.on(:request) do |request|
if request.match?(/bla-bla/)
request.abort
elsif request.match?(/lorem/)
request.respond(body: "Lorem ipsum")
else
request.continue
end
end
page.go_to("https://google.com")If site or proxy uses authorization you can provide credentials using this method.
- options
Hash- :type
Symbol:server|:proxysite or proxy authorization - :user
String - :password
String
- :type
- &block accepts authenticated request, which you must subsequently allow or deny, if you don't
care about unwanted requests just call
request.continue.
page.network.authorize(user: "login", password: "pass") { |req| req.continue }
page.go_to("http://example.com/authenticated")
puts page.network.status # => 200
puts page.body # => Welcome, authenticated clientSince Chrome implements authorize using request interception you must continue or abort authorized requests. If you
already have code that uses interception you can use authorize without block, but if not you are obliged to pass
block, so this is version doesn't pass block and can work just fine:
browser = Ferrum::Browser.new
page = browser.create_page
page.network.intercept
page.on(:request) do |request|
if request.resource_type == "Image"
request.abort
else
request.continue
end
end
page.network.authorize(user: "login", password: "pass", type: :proxy)
page.go_to("https://google.com")You used to call authorize method without block, but since it's implemented using request interception there could be
a collision with another part of your code that also uses request interception, so that authorize allows the request
while your code denies but it's too late. The block is mandatory now.
Activates emulation of network conditions.
- options
Hash- :offline
Booleanemulate internet disconnection,falseby default - :latency
Integerminimum latency from request sent to response headers received (ms),0by default - :download_throughput
Integermaximal aggregated download throughput (bytes/sec),-1by default, disables download throttling - :upload_throughput
Integermaximal aggregated upload throughput (bytes/sec),-1by default, disables download throttling - :connection_type
Stringconnection type if known, one of: none, cellular2g, cellular3g, cellular4g, bluetooth, ethernet, wifi, wimax, other.nilby default
- :offline
page.network.emulate_network_conditions(connection_type: "cellular2g")
page.go_to("https://github.com/")Activates offline mode for a page.
page.network.offline_mode
page.go_to("https://github.com/") # => Ferrum::StatusError (Request to https://github.com/ failed(net::ERR_INTERNET_DISCONNECTED))Toggles ignoring cache for each request. If true, cache will not be used.
page.network.cache(disable: true)page.downloads
Returns all information about downloaded files as a Hash.
page.go_to("http://localhost/attachment.pdf")
page.downloads.files # => [{"frameId"=>"E3316DF1B5383D38F8ADF7485005FDE3", "guid"=>"11a68745-98ac-4d54-9b57-9f9016c268b3", "url"=>"http://localhost/attachment.pdf", "suggestedFilename"=>"attachment.pdf", "totalBytes"=>4911, "receivedBytes"=>4911, "state"=>"completed"}]Waits until the download is finished.
page.go_to("http://localhost/attachment.pdf")
page.downloads.waitor
page.go_to("http://localhost/page")
page.downloads.wait { page.at_css("#download").click }Sets behavior in case of file to be downloaded.
- options
Hash- :save_path
Stringabsolute path of where to store the file - :behavior
Symboldeny | allow | allowAndName | default,allowby default
- :save_path
page.go_to("https://example.com/")
page.downloads.set_behavior(save_path: "/tmp", behavior: :allow)You can set a proxy with a :proxy option:
Ferrum::Browser.new(proxy: { host: "x.x.x.x", port: "8800", user: "user", password: "pa$$" }):bypass can specify semi-colon-separated list of hosts for which proxy shouldn't be used:
Ferrum::Browser.new(proxy: { host: "x.x.x.x", port: "8800", bypass: "*.google.com;*foo.com" })In general passing a proxy option when instantiating a browser results in a browser running with proxy command line flags, so that it affects all pages and contexts. You can create a page in a new context which can use its own proxy settings:
browser = Ferrum::Browser.new
browser.create_page(proxy: { host: "x.x.x.x", port: 31337, user: "user", password: "password" }) do |page|
page.go_to("https://api.ipify.org?format=json")
page.body # => "x.x.x.x"
end
browser.create_page(proxy: { host: "y.y.y.y", port: 31337, user: "user", password: "password" }) do |page|
page.go_to("https://api.ipify.org?format=json")
page.body # => "y.y.y.y"
endpage.mouse
Scroll page to a given x, y
- x
Integerthe pixel along the horizontal axis of the document that you want displayed in the upper left - y
Integerthe pixel along the vertical axis of the document that you want displayed in the upper left
page.go_to("https://www.google.com/search?q=Ruby+headless+driver+for+Capybara")
page.mouse.scroll_to(0, 400)Click given coordinates, fires mouse move, down and up events.
- options
Hash- :x
Integer - :y
Integer - :delay
Floatdefaults to 0. Delay between mouse down and mouse up events - :button
Symbol:left | :right, defaults to :left - :count
Integerdefaults to 1 - :modifiers
Integerbitfield for key modifiers. Seekeyboard.modifiers
- :x
Mouse down for given coordinates.
- options
Hash- :button
Symbol:left | :right, defaults to :left - :count
Integerdefaults to 1 - :modifiers
Integerbitfield for key modifiers. Seekeyboard.modifiers
- :button
Mouse up for given coordinates.
- options
Hash- :button
Symbol:left | :right, defaults to :left - :count
Integerdefaults to 1 - :modifiers
Integerbitfield for key modifiers. Seekeyboard.modifiers
- :button
Mouse move to given x and y.
- options
Hash- :x
Integer - :y
Integer - :steps
Integerdefaults to 1. Sends intermediate mousemove events.
- :x
page.keyboard
Dispatches a keydown event.
- key
String|SymbolName of key such as "a", :enter, :backspace
Dispatches a keyup event.
- key
String|SymbolName of key such as "b", :enter, :backspace
Sends a keydown, keypress/input, and keyup event for each character in the text.
- text
String|Array<String> | Array<Symbol>A text to type into a focused element,[:Shift, "s"], "tring"
Returns bitfield for a given keys
- keys
Array<Symbol>:alt | :ctrl | :command | :shift
page.cookies
Returns cookies hash
page.cookies.all # => {"NID"=>#<Ferrum::Cookies::Cookie:0x0000558624b37a40 @attributes={"name"=>"NID", "value"=>"...", "domain"=>".google.com", "path"=>"/", "expires"=>1583211046.575681, "size"=>178, "httpOnly"=>true, "secure"=>false, "session"=>false}>}Returns cookie
- value
String
page.cookies["NID"] # => <Ferrum::Cookies::Cookie:0x0000558624b67a88 @attributes={"name"=>"NID", "value"=>"...", "domain"=>".google.com", "path"=>"/", "expires"=>1583211046.575681, "size"=>178, "httpOnly"=>true, "secure"=>false, "session"=>false}>Sets a cookie
- value
Hash- :name
String - :value
String - :domain
String - :expires
Integer - :samesite
String - :httponly
Boolean
- :name
page.cookies.set(name: "stealth", value: "omg", domain: "google.com") # => true- value
Cookie
nid_cookie = page.cookies["NID"] # => <Ferrum::Cookies::Cookie:0x0000558624b67a88>
page.cookies.set(nid_cookie) # => trueRemoves given cookie
- options
Hash- :name
String - :domain
String - :url
String
- :name
page.cookies.remove(name: "stealth", domain: "google.com") # => trueRemoves all cookies for current page
page.cookies.clear # => trueStores all cookies of current page in a file.
# Cookies are saved into cookies.yml
page.cookies.store # => 15657Loads all cookies from the file and sets them for current page.
# Cookies are loaded from cookies.yml
page.cookies.load # => truepage.headers
Get all headers
Set given headers. Eventually clear all headers and set given ones.
- headers
Hashkey-value pairs for example"User-Agent" => "Browser"
Adds given headers to already set ones.
- headers
Hashkey-value pairs for example"Referer" => "http://example.com"
Clear all headers.
Evaluate and return result for given JS expression
- expression
Stringshould be valid JavaScript - args
Objectyou can pass arguments, though it should be a validNodeor a simple value.
page.evaluate("[window.scrollX, window.scrollY]")Evaluate asynchronous expression and return result
- expression
Stringshould be valid JavaScript - wait_time How long we should wait for Promise to resolve or reject
- args
Objectyou can pass arguments, though it should be a validNodeor a simple value.
page.evaluate_async(%(arguments[0]({foo: "bar"})), 5) # => { "foo" => "bar" }Execute expression. Doesn't return the result
- expression
Stringshould be valid JavaScript - args
Objectyou can pass arguments, though it should be a validNodeor a simple value.
page.execute(%(1 + 1)) # => trueEvaluate JavaScript to modify things before a page load
- expression
Stringshould be valid JavaScript
browser.evaluate_on_new_document <<~JS
Object.defineProperty(navigator, "languages", {
get: function() { return ["tlh"]; }
});
JS- options
Hash- :url
String - :path
String - :content
String - :type
String-text/javascriptby default
- :url
page.add_script_tag(url: "http://example.com/stylesheet.css") # => true- options
Hash- :url
String - :path
String - :content
String
- :url
page.add_style_tag(content: "h1 { font-size: 40px; }") # => true- options
Hash- :enabled
Boolean,trueby default
- :enabled
page.bypass_csp # => true
page.go_to("https://github.com/ruby-concurrency/concurrent-ruby/blob/master/docs-source/promises.in.md")
page.refresh
page.add_script_tag(content: "window.__injected = 42")
page.evaluate("window.__injected") # => 42Disables Javascripts from the loaded HTML source.
You can still evaluate JavaScript with evaluate or execute.
Returns nothing.
page.disable_javascriptOverrides device screen dimensions and emulates viewport.
- options
Hash- :width
Integer, viewport width.0by default - :height
Integer, viewport height.0by default - :scale_factor
Float, device scale factor.0by default - :mobile
Boolean, whether to emulate mobile device.falseby default
- :width
page.set_viewport(width: 1000, height: 600, scale_factor: 3)Returns all the frames current page have.
page.go_to("https://www.w3schools.com/tags/tag_frame.asp")
page.frames # =>
# [
# #<Ferrum::Frame @id="C6D104CE454A025FBCF22B98DE612B12" @parent_id=nil @name=nil @state=:stopped_loading @execution_id=1>,
# #<Ferrum::Frame @id="C09C4E4404314AAEAE85928EAC109A93" @parent_id="C6D104CE454A025FBCF22B98DE612B12" @state=:stopped_loading @execution_id=2>,
# #<Ferrum::Frame @id="2E9C7F476ED09D87A42F2FEE3C6FBC3C" @parent_id="C6D104CE454A025FBCF22B98DE612B12" @state=:stopped_loading @execution_id=3>,
# ...
# ]Returns page's main frame, the top of the tree and the parent of all frames.
Find frame by given options.
- options
Hash- :id
String- Unique frame's id that browser provides - :name
String- Frame's name if there's one
- :id
page.frame_by(id: "C6D104CE454A025FBCF22B98DE612B12")Frame's unique id.
Parent frame id if this one is nested in another one.
Parent frame if this one is nested in another one.
Returns the element in which the window is embedded.
Execution context id which is used by JS, each frame has its own context in which JS evaluates.
If frame was given a name it should be here.
One of the states frame's in:
:started_loading:navigated:stopped_loading
Returns current frame's location href.
page.go_to("https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe")
frame = page.frames[1]
frame.url # => https://interactive-examples.mdn.mozilla.net/pages/tabbed/iframe.htmlReturns current frame's title.
page.go_to("https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe")
frame = page.frames[1]
frame.title # => HTML Demo: <iframe>If current frame is the main frame of the page (top of the tree).
page.go_to("https://www.w3schools.com/tags/tag_frame.asp")
frame = page.frame_by(id: "C09C4E4404314AAEAE85928EAC109A93")
frame.main? # => falseReturns current frame's top window location href.
page.go_to("https://www.w3schools.com/tags/tag_frame.asp")
frame = page.frame_by(id: "C09C4E4404314AAEAE85928EAC109A93")
frame.current_url # => "https://www.w3schools.com/tags/tag_frame.asp"Returns current frame's top window title.
page.go_to("https://www.w3schools.com/tags/tag_frame.asp")
frame = page.frame_by(id: "C09C4E4404314AAEAE85928EAC109A93")
frame.current_title # => "HTML frame tag"Returns current frame's html.
page.go_to("https://www.w3schools.com/tags/tag_frame.asp")
frame = page.frame_by(id: "C09C4E4404314AAEAE85928EAC109A93")
frame.body # => "<html><head></head><body></body></html>"Returns current frame's doctype.
page.go_to("https://www.w3schools.com/tags/tag_frame.asp")
page.main_frame.doctype # => "<!DOCTYPE html>"Sets a content of a given frame.
- html
String
page.go_to("https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe")
frame = page.frames[1]
frame.body # <html lang="en"><head><style>body {transition: opacity ease-in 0.2s; }...
frame.content = "<html><head></head><body><p>lol</p></body></html>"
frame.body # => <html><head></head><body><p>lol</p></body></html>Accept dialog with given text or default prompt if applicable
- text
String
Dismiss dialog
page.on(:dialog) do |dialog|
if dialog.match?(/bla-bla/)
dialog.accept
else
dialog.dismiss
end
end
page.go_to("https://google.com")You can slow down or speed up CSS animations.
Returns playback rate for CSS animations, defaults to 1.
Sets playback rate of CSS animations
- value
Integer
page.playback_rate = 2000
page.go_to("https://google.com")
page.playback_rate # => 2000Returns Frame object for current node, you can keep using Finders for that object:
frame = page.at_xpath("//iframe").frame # => Frame
frame.at_css("//a[text() = 'Log in']") # => Node(chainable) Selects options by passed attribute.
page.at_xpath("//*[select]").select(["1"]) # => Node (select)
page.at_xpath("//*[select]").select(["text"], by: :text) # => Node (select)Accept string, array or strings:
page.at_xpath("//*[select]").select("1")
page.at_xpath("//*[select]").select("1", "2")
page.at_xpath("//*[select]").select(["1", "2"])You can use tracing.record to create a trace file which can be opened in Chrome DevTools or
timeline viewer.
page.tracing.record(path: "trace.json") do
page.go_to("https://www.google.com")
endAccepts block, records trace and by default returns trace data from Tracing.tracingComplete event as output. When
path is specified returns true and stores trace data into file.
- options
Hash- :path
Stringsave data on the disk,nilby default - :encoding
Symbol:base64|:binaryencode output as Base64 or plain text.:binaryby default - :timeout
Floatwait until file streaming finishes in the specified time or raise error, defaults tonil - :screenshots
Booleancapture screenshots in the trace,falseby default - :trace_config
Hash<String, Object>config for trace, for categories see getCategories, only one trace config can be active at a time per browser.
- :path
Closes browser tabs opened by the Browser instance.
# connect to a long-running Chrome process
browser = Ferrum::Browser.new(url: "http://localhost:9222")
browser.go_to("https://github.com/")
# clean up, lest the tab stays there hanging forever
browser.reset
browser.quitFerrum is fully thread-safe. You can create one browser or a few as you wish and start playing around using threads. Example below shows how to create a few pages which share the same context. Context is similar to an incognito profile but you can have more than one, think of it like it's independent browser session:
browser = Ferrum::Browser.new
context = browser.contexts.create
t1 = Thread.new(context) do |c|
page = c.create_page
page.go_to("https://www.google.com/search?q=Ruby+headless+driver+for+Capybara")
page.screenshot(path: "t1.png")
end
t2 = Thread.new(context) do |c|
page = c.create_page
page.go_to("https://www.google.com/search?q=Ruby+static+typing")
page.screenshot(path: "t2.png")
end
t1.join
t2.join
context.dispose
browser.quitor you can create two independent contexts:
browser = Ferrum::Browser.new
t1 = Thread.new(browser) do |b|
context = b.contexts.create
page = context.create_page
page.go_to("https://www.google.com/search?q=Ruby+headless+driver+for+Capybara")
page.screenshot(path: "t1.png")
context.dispose
end
t2 = Thread.new(browser) do |b|
context = b.contexts.create
page = context.create_page
page.go_to("https://www.google.com/search?q=Ruby+static+typing")
page.screenshot(path: "t2.png")
context.dispose
end
t1.join
t2.join
browser.quitAfter checking out the repo, run bundle install to install dependencies.
Then, run bundle exec rake test to run the tests. You can also run bin/console for an interactive prompt that will
allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the
version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version,
push git commits and the created tag, and push the .gem file to rubygems.org.
Bug reports and pull requests are welcome on GitHub.
The gem is available as open source under the terms of the MIT License.