ferrum 0.17 → 0.17.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE +1 -1
- data/README.md +9 -1369
- data/lib/ferrum/browser/options/base.rb +2 -2
- data/lib/ferrum/browser/options/chrome.rb +53 -26
- data/lib/ferrum/browser/options.rb +2 -1
- data/lib/ferrum/browser/process.rb +5 -4
- data/lib/ferrum/browser.rb +7 -1
- data/lib/ferrum/client/web_socket.rb +1 -1
- data/lib/ferrum/client.rb +7 -6
- data/lib/ferrum/context.rb +2 -0
- data/lib/ferrum/contexts.rb +19 -15
- data/lib/ferrum/cookies/cookie.rb +1 -1
- data/lib/ferrum/errors.rb +4 -3
- data/lib/ferrum/frame/runtime.rb +1 -1
- data/lib/ferrum/network/response.rb +14 -1
- data/lib/ferrum/network.rb +1 -1
- data/lib/ferrum/page/animation.rb +2 -2
- data/lib/ferrum/page/screenshot.rb +3 -3
- data/lib/ferrum/page.rb +2 -4
- data/lib/ferrum/utils/platform.rb +1 -1
- data/lib/ferrum/version.rb +1 -1
- metadata +4 -7
data/README.md
CHANGED
|
@@ -1,70 +1,31 @@
|
|
|
1
1
|
# Ferrum - high-level API to control Chrome in Ruby
|
|
2
2
|
|
|
3
|
+
## [Documentation](https://docs.rubycdp.com/docs/ferrum/introduction)
|
|
4
|
+
|
|
3
5
|
<img align="right"
|
|
4
6
|
width="320" height="241"
|
|
5
7
|
alt="Ferrum logo"
|
|
6
8
|
src="https://raw.githubusercontent.com/rubycdp/ferrum/main/logo.svg?sanitize=true">
|
|
7
9
|
|
|
8
|
-
#### As simple as Puppeteer, though even simpler.
|
|
9
|
-
|
|
10
10
|
It is Ruby clean and high-level API to Chrome. Runs headless by default, but you
|
|
11
|
-
can configure it to run in a headful mode. All you need is Ruby and
|
|
12
|
-
[
|
|
13
|
-
[Chromium](https://www.chromium.org/). Ferrum connects to the browser by [CDP
|
|
14
|
-
protocol](https://chromedevtools.github.io/devtools-protocol/) and there's _no_
|
|
11
|
+
can configure it to run in a headful mode. All you need is Ruby and [Chrome](https://www.google.com/chrome/) or [Chromium](https://www.chromium.org/).
|
|
12
|
+
Ferrum connects to the browser by [CDP protocol](https://chromedevtools.github.io/devtools-protocol/) and there's _no_
|
|
15
13
|
Selenium/WebDriver/ChromeDriver dependency. The emphasis was made on a raw CDP
|
|
16
14
|
protocol because Chrome allows you to do so many things that are barely
|
|
17
15
|
supported by WebDriver because it should have consistent design with other
|
|
18
16
|
browsers.
|
|
19
17
|
|
|
20
|
-
* [Cuprite](https://github.com/rubycdp/cuprite) is a pure Ruby driver for
|
|
21
|
-
[
|
|
22
|
-
going to crawl sites you better use Ferrum or
|
|
23
|
-
[Vessel](https://github.com/rubycdp/vessel) because you crawl, not test.
|
|
24
|
-
|
|
25
|
-
* [Vessel](https://github.com/rubycdp/vessel) high-level web crawling framework
|
|
26
|
-
based on Ferrum and Mechanize.
|
|
27
|
-
|
|
28
|
-
## Index
|
|
29
|
-
|
|
30
|
-
* [Install](https://github.com/rubycdp/ferrum#install)
|
|
31
|
-
* [Examples](https://github.com/rubycdp/ferrum#examples)
|
|
32
|
-
* [Docker](https://github.com/rubycdp/ferrum#docker)
|
|
33
|
-
* [Customization](https://github.com/rubycdp/ferrum#customization)
|
|
34
|
-
* [Navigation](https://github.com/rubycdp/ferrum#navigation)
|
|
35
|
-
* [Finders](https://github.com/rubycdp/ferrum#finders)
|
|
36
|
-
* [Screenshots](https://github.com/rubycdp/ferrum#screenshots)
|
|
37
|
-
* [Screencast](https://github.com/rubycdp/ferrum#screencast)
|
|
38
|
-
* [Network](https://github.com/rubycdp/ferrum#network)
|
|
39
|
-
* [Downloads](https://github.com/rubycdp/ferrum#downloads)
|
|
40
|
-
* [Proxy](https://github.com/rubycdp/ferrum#proxy)
|
|
41
|
-
* [Mouse](https://github.com/rubycdp/ferrum#mouse)
|
|
42
|
-
* [Keyboard](https://github.com/rubycdp/ferrum#keyboard)
|
|
43
|
-
* [Cookies](https://github.com/rubycdp/ferrum#cookies)
|
|
44
|
-
* [Headers](https://github.com/rubycdp/ferrum#headers)
|
|
45
|
-
* [JavaScript](https://github.com/rubycdp/ferrum#javascript)
|
|
46
|
-
* [Frames](https://github.com/rubycdp/ferrum#frames)
|
|
47
|
-
* [Frame](https://github.com/rubycdp/ferrum#frame)
|
|
48
|
-
* [Dialogs](https://github.com/rubycdp/ferrum#dialogs)
|
|
49
|
-
* [Animation](https://github.com/rubycdp/ferrum#animation)
|
|
50
|
-
* [Node](https://github.com/rubycdp/ferrum#node)
|
|
51
|
-
* [Tracing](https://github.com/rubycdp/ferrum#tracing)
|
|
52
|
-
* [Clean Up](https://github.com/rubycdp/ferrum#clean-up)
|
|
53
|
-
* [Thread safety](https://github.com/rubycdp/ferrum#thread-safety)
|
|
54
|
-
* [Development](https://github.com/rubycdp/ferrum#development)
|
|
55
|
-
* [Contributing](https://github.com/rubycdp/ferrum#contributing)
|
|
56
|
-
* [License](https://github.com/rubycdp/ferrum#license)
|
|
57
|
-
|
|
18
|
+
* [Cuprite](https://github.com/rubycdp/cuprite) is a pure Ruby driver for[Capybara](https://github.com/teamcapybara/capybara) based on Ferrum.
|
|
19
|
+
* [Vessel](https://github.com/rubycdp/vessel) high-level web crawling framework based on Ferrum and Mechanize.
|
|
58
20
|
|
|
59
21
|
## Install
|
|
60
22
|
|
|
61
23
|
There's no official Chrome or Chromium package for Linux don't install it this
|
|
62
24
|
way because it's either outdated or unofficial, both are bad. Download it from
|
|
63
|
-
official source for [Chrome](https://www.google.com/chrome/) or
|
|
64
|
-
[Chromium](https://www.chromium.org/getting-involved/download-chromium).
|
|
25
|
+
official source for [Chrome](https://www.google.com/chrome/) or [Chromium](https://www.chromium.org/getting-involved/download-chromium).
|
|
65
26
|
Chrome binary should be in the `PATH` or `BROWSER_PATH` and you can pass it as an
|
|
66
27
|
option to browser instance see `:browser_path` in
|
|
67
|
-
[Customization](https://
|
|
28
|
+
[Customization](https://docs.rubycdp.com/docs/ferrum/customization).
|
|
68
29
|
|
|
69
30
|
Add this to your `Gemfile` and run `bundle install`.
|
|
70
31
|
|
|
@@ -72,8 +33,7 @@ Add this to your `Gemfile` and run `bundle install`.
|
|
|
72
33
|
gem "ferrum"
|
|
73
34
|
```
|
|
74
35
|
|
|
75
|
-
|
|
76
|
-
## Examples
|
|
36
|
+
## Quick Start
|
|
77
37
|
|
|
78
38
|
Navigate to a website and save a screenshot:
|
|
79
39
|
|
|
@@ -131,1326 +91,6 @@ page.mouse
|
|
|
131
91
|
browser.quit
|
|
132
92
|
```
|
|
133
93
|
|
|
134
|
-
|
|
135
|
-
## Docker
|
|
136
|
-
|
|
137
|
-
In docker as root you must pass the no-sandbox browser option:
|
|
138
|
-
|
|
139
|
-
```ruby
|
|
140
|
-
Ferrum::Browser.new(browser_options: { "no-sandbox": nil })
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
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.
|
|
144
|
-
|
|
145
|
-
## Customization
|
|
146
|
-
|
|
147
|
-
You can customize options with the following code in your test setup:
|
|
148
|
-
|
|
149
|
-
``` ruby
|
|
150
|
-
Ferrum::Browser.new(options)
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
* options `Hash`
|
|
154
|
-
* `:headless` (Boolean) - Set browser as headless or not, `true` by default.
|
|
155
|
-
* `:incognito` (Boolean) - Create an incognito profile for the browser startup window, `true` by default.
|
|
156
|
-
* `:xvfb` (Boolean) - Run browser in a virtual framebuffer, `false` by default.
|
|
157
|
-
* `:flatten` (Boolean) - Use one websocket connection to the browser and all the pages in flatten mode.
|
|
158
|
-
* `:window_size` (Array) - The dimensions of the browser window in which to
|
|
159
|
-
test, expressed as a 2-element array, e.g. [1024, 768]. Default: [1024, 768]
|
|
160
|
-
* `:extensions` (Array[String | Hash]) - An array of paths to files or JS
|
|
161
|
-
source code to be preloaded into the browser e.g.:
|
|
162
|
-
`["/path/to/script.js", { source: "window.secret = 'top'" }]`
|
|
163
|
-
* `:logger` (Object responding to `puts`) - When present, debug output is
|
|
164
|
-
written to this object.
|
|
165
|
-
* `:slowmo` (Integer | Float) - Set a delay in seconds to wait before sending command.
|
|
166
|
-
Useful companion of headless option, so that you have time to see changes.
|
|
167
|
-
* `:timeout` (Numeric) - The number of seconds we'll wait for a response when
|
|
168
|
-
communicating with browser. Default is 5.
|
|
169
|
-
* `:js_errors` (Boolean) - When true, JavaScript errors get re-raised in Ruby.
|
|
170
|
-
* `:pending_connection_errors` (Boolean) - When main frame is still waiting for slow responses while timeout is
|
|
171
|
-
reached `PendingConnectionsError` is raised. It's better to figure out why you have slow responses and fix or
|
|
172
|
-
block them rather than turn this setting off. Default is true.
|
|
173
|
-
* `:browser_name` (Symbol) - `:chrome` by default, only experimental support
|
|
174
|
-
for `:firefox` for now.
|
|
175
|
-
* `:browser_path` (String) - Path to Chrome binary, you can also set ENV
|
|
176
|
-
variable as `BROWSER_PATH=some/path/chrome bundle exec rspec`.
|
|
177
|
-
* `:browser_options` (Hash) - Additional command line options,
|
|
178
|
-
[see them all](https://peter.sh/experiments/chromium-command-line-switches/)
|
|
179
|
-
e.g. `{ "ignore-certificate-errors" => nil }`
|
|
180
|
-
* `:ignore_default_browser_options` (Boolean) - Ferrum has a number of default
|
|
181
|
-
options it passes to the browser, if you set this to `true` then only
|
|
182
|
-
options you put in `:browser_options` will be passed to the browser,
|
|
183
|
-
except required ones of course.
|
|
184
|
-
* `:port` (Integer) - Remote debugging port for headless Chrome.
|
|
185
|
-
* `:host` (String) - Remote debugging address for headless Chrome.
|
|
186
|
-
* `:url` (String) - URL for a running instance of Chrome. If this is set, a
|
|
187
|
-
browser process will not be spawned.
|
|
188
|
-
* `:ws_url` (String) - Websocket url for a running instance of Chrome. If this is set, a
|
|
189
|
-
browser process will not be spawned. It's higher priority than `:url`, setting both doesn't make sense.
|
|
190
|
-
* `:process_timeout` (Integer) - How long to wait for the Chrome process to
|
|
191
|
-
respond on startup.
|
|
192
|
-
* `:ws_max_receive_size` (Integer) - How big messages to accept from Chrome
|
|
193
|
-
over the web socket, in bytes. Defaults to 64MB. Incoming messages larger
|
|
194
|
-
than this will cause a `Ferrum::DeadBrowserError`.
|
|
195
|
-
* `:proxy` (Hash) - Specify proxy settings, [read more](https://github.com/rubycdp/ferrum#proxy)
|
|
196
|
-
* `:save_path` (String) - Path to save attachments with [Content-Disposition](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) header.
|
|
197
|
-
* `:env` (Hash) - Environment variables you'd like to pass through to the process
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
## Navigation
|
|
201
|
-
|
|
202
|
-
#### go_to(url) : `String`
|
|
203
|
-
|
|
204
|
-
Navigate page to.
|
|
205
|
-
|
|
206
|
-
* url `String` The url should include scheme unless you set `base_url` when
|
|
207
|
-
configuring driver.
|
|
208
|
-
|
|
209
|
-
```ruby
|
|
210
|
-
page.go_to("https://github.com/")
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
#### back
|
|
214
|
-
|
|
215
|
-
Navigate to the previous page in history.
|
|
216
|
-
|
|
217
|
-
```ruby
|
|
218
|
-
page.go_to("https://github.com/")
|
|
219
|
-
page.at_xpath("//a").click
|
|
220
|
-
page.back
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
#### forward
|
|
224
|
-
|
|
225
|
-
Navigate to the next page in history.
|
|
226
|
-
|
|
227
|
-
```ruby
|
|
228
|
-
page.go_to("https://github.com/")
|
|
229
|
-
page.at_xpath("//a").click
|
|
230
|
-
page.back
|
|
231
|
-
page.forward
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
#### refresh
|
|
235
|
-
|
|
236
|
-
Reload current page.
|
|
237
|
-
|
|
238
|
-
```ruby
|
|
239
|
-
page.go_to("https://github.com/")
|
|
240
|
-
page.refresh
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
#### stop
|
|
244
|
-
|
|
245
|
-
Stop all navigations and loading pending resources on the page
|
|
246
|
-
|
|
247
|
-
```ruby
|
|
248
|
-
page.go_to("https://github.com/")
|
|
249
|
-
page.stop
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
#### position = \*\*options
|
|
253
|
-
|
|
254
|
-
Set the position for the browser window
|
|
255
|
-
|
|
256
|
-
* options `Hash`
|
|
257
|
-
* :left `Integer`
|
|
258
|
-
* :top `Integer`
|
|
259
|
-
|
|
260
|
-
```ruby
|
|
261
|
-
browser.position = { left: 10, top: 20 }
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
#### position : `Array<Integer>`
|
|
265
|
-
|
|
266
|
-
Get the position for the browser window
|
|
267
|
-
|
|
268
|
-
```ruby
|
|
269
|
-
browser.position # => [10, 20]
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
#### window_bounds = \*\*options
|
|
273
|
-
|
|
274
|
-
Set window bounds
|
|
275
|
-
|
|
276
|
-
* options `Hash`
|
|
277
|
-
* :left `Integer`
|
|
278
|
-
* :top `Integer`
|
|
279
|
-
* :width `Integer`
|
|
280
|
-
* :height `Integer`
|
|
281
|
-
* :window_state `String`
|
|
282
|
-
|
|
283
|
-
```ruby
|
|
284
|
-
browser.window_bounds = { left: 10, top: 20, width: 1024, height: 768, window_state: "normal" }
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
#### window_bounds : `Hash<String, Integer | String>`
|
|
288
|
-
|
|
289
|
-
Get window bounds
|
|
290
|
-
|
|
291
|
-
```ruby
|
|
292
|
-
browser.window_bounds # => { "left": 0, "top": 1286, "width": 10, "height": 10, "windowState": "normal" }
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
#### window_id : `Integer`
|
|
296
|
-
|
|
297
|
-
Current window id
|
|
298
|
-
|
|
299
|
-
```ruby
|
|
300
|
-
browser.window_id # => 1
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
## Finders
|
|
304
|
-
|
|
305
|
-
#### at_css(selector, \*\*options) : `Node` | `nil`
|
|
306
|
-
|
|
307
|
-
Find node by selector. Runs `document.querySelector` within the document or
|
|
308
|
-
provided node.
|
|
309
|
-
|
|
310
|
-
* selector `String`
|
|
311
|
-
* options `Hash`
|
|
312
|
-
* :within `Node` | `nil`
|
|
313
|
-
|
|
314
|
-
```ruby
|
|
315
|
-
page.go_to("https://github.com/")
|
|
316
|
-
page.at_css("a[aria-label='Issues you created']") # => Node
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
#### css(selector, \*\*options) : `Array<Node>` | `[]`
|
|
321
|
-
|
|
322
|
-
Find nodes by selector. The method runs `document.querySelectorAll` within the
|
|
323
|
-
document or provided node.
|
|
324
|
-
|
|
325
|
-
* selector `String`
|
|
326
|
-
* options `Hash`
|
|
327
|
-
* :within `Node` | `nil`
|
|
328
|
-
|
|
329
|
-
```ruby
|
|
330
|
-
page.go_to("https://github.com/")
|
|
331
|
-
page.css("a[aria-label='Issues you created']") # => [Node]
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
#### at_xpath(selector, \*\*options) : `Node` | `nil`
|
|
335
|
-
|
|
336
|
-
Find node by xpath.
|
|
337
|
-
|
|
338
|
-
* selector `String`
|
|
339
|
-
* options `Hash`
|
|
340
|
-
* :within `Node` | `nil`
|
|
341
|
-
|
|
342
|
-
```ruby
|
|
343
|
-
page.go_to("https://github.com/")
|
|
344
|
-
page.at_xpath("//a[@aria-label='Issues you created']") # => Node
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
#### xpath(selector, \*\*options) : `Array<Node>` | `[]`
|
|
348
|
-
|
|
349
|
-
Find nodes by xpath.
|
|
350
|
-
|
|
351
|
-
* selector `String`
|
|
352
|
-
* options `Hash`
|
|
353
|
-
* :within `Node` | `nil`
|
|
354
|
-
|
|
355
|
-
```ruby
|
|
356
|
-
page.go_to("https://github.com/")
|
|
357
|
-
page.xpath("//a[@aria-label='Issues you created']") # => [Node]
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
#### current_url : `String`
|
|
361
|
-
|
|
362
|
-
Returns current top window location href.
|
|
363
|
-
|
|
364
|
-
```ruby
|
|
365
|
-
page.go_to("https://google.com/")
|
|
366
|
-
page.current_url # => "https://www.google.com/"
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
#### current_title : `String`
|
|
370
|
-
|
|
371
|
-
Returns current top window title
|
|
372
|
-
|
|
373
|
-
```ruby
|
|
374
|
-
page.go_to("https://google.com/")
|
|
375
|
-
page.current_title # => "Google"
|
|
376
|
-
```
|
|
377
|
-
|
|
378
|
-
#### body : `String`
|
|
379
|
-
|
|
380
|
-
Returns current page's html.
|
|
381
|
-
|
|
382
|
-
```ruby
|
|
383
|
-
page.go_to("https://google.com/")
|
|
384
|
-
page.body # => '<html itemscope="" itemtype="http://schema.org/WebPage" lang="ru"><head>...
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
## Screenshots
|
|
389
|
-
|
|
390
|
-
#### screenshot(\*\*options) : `String` | `Integer`
|
|
391
|
-
|
|
392
|
-
Saves screenshot on a disk or returns it as base64.
|
|
393
|
-
|
|
394
|
-
* options `Hash`
|
|
395
|
-
* :path `String` to save a screenshot on the disk. `:encoding` will be set to
|
|
396
|
-
`:binary` automatically
|
|
397
|
-
* :encoding `Symbol` `:base64` | `:binary` you can set it to return image as
|
|
398
|
-
Base64
|
|
399
|
-
* :format `String` "jpeg" ("jpg") | "png" | "webp"
|
|
400
|
-
* :quality `Integer` 0-100 works for jpeg only
|
|
401
|
-
* :full `Boolean` whether you need full page screenshot or a viewport
|
|
402
|
-
* :selector `String` css selector for given element, optional
|
|
403
|
-
* :area `Hash` area for screenshot, optional
|
|
404
|
-
* :x `Integer`
|
|
405
|
-
* :y `Integer`
|
|
406
|
-
* :width `Integer`
|
|
407
|
-
* :height `Integer`
|
|
408
|
-
* :scale `Float` zoom in/out
|
|
409
|
-
* :background_color `Ferrum::RGBA.new(0, 0, 0, 0.0)` to have specific background color
|
|
410
|
-
|
|
411
|
-
```ruby
|
|
412
|
-
page.go_to("https://google.com/")
|
|
413
|
-
# Save on the disk in PNG
|
|
414
|
-
page.screenshot(path: "google.png") # => 134660
|
|
415
|
-
# Save on the disk in JPG
|
|
416
|
-
page.screenshot(path: "google.jpg") # => 30902
|
|
417
|
-
# Save to Base64 the whole page not only viewport and reduce quality
|
|
418
|
-
page.screenshot(full: true, quality: 60, encoding: :base64) # "iVBORw0KGgoAAAANSUhEUgAABAAAAAMACAYAAAC6uhUNAAAAAXNSR0IArs4c6Q...
|
|
419
|
-
# Save on the disk with the selected element in PNG
|
|
420
|
-
page.screenshot(path: "google.png", selector: "textarea") # => 11340
|
|
421
|
-
# Save to Base64 with an area of the page in PNG
|
|
422
|
-
page.screenshot(path: "google.png", area: { x: 0, y: 0, width: 400, height: 300 }) # => 54239
|
|
423
|
-
# Save with specific background color
|
|
424
|
-
page.screenshot(background_color: Ferrum::RGBA.new(0, 0, 0, 0.0))
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
#### pdf(\*\*options) : `String` | `Boolean`
|
|
428
|
-
|
|
429
|
-
Saves PDF on a disk or returns it as base64.
|
|
430
|
-
|
|
431
|
-
* options `Hash`
|
|
432
|
-
* :path `String` to save a pdf on the disk. `:encoding` will be set to
|
|
433
|
-
`:binary` automatically
|
|
434
|
-
* :encoding `Symbol` `:base64` | `:binary` you can set it to return pdf as
|
|
435
|
-
Base64
|
|
436
|
-
* :landscape `Boolean` paper orientation. Defaults to false.
|
|
437
|
-
* :scale `Float` zoom in/out
|
|
438
|
-
* :format `symbol` standard paper sizes :letter, :legal, :tabloid, :ledger, :A0, :A1, :A2, :A3, :A4, :A5, :A6
|
|
439
|
-
|
|
440
|
-
* :paper_width `Float` set paper width
|
|
441
|
-
* :paper_height `Float` set paper height
|
|
442
|
-
* See other [native options](https://chromedevtools.github.io/devtools-protocol/tot/Page#method-printToPDF) you can pass
|
|
443
|
-
|
|
444
|
-
```ruby
|
|
445
|
-
page.go_to("https://google.com/")
|
|
446
|
-
# Save to disk as a PDF
|
|
447
|
-
page.pdf(path: "google.pdf", paper_width: 1.0, paper_height: 1.0) # => true
|
|
448
|
-
```
|
|
449
|
-
|
|
450
|
-
#### mhtml(\*\*options) : `String` | `Integer`
|
|
451
|
-
|
|
452
|
-
Saves MHTML on a disk or returns it as a string.
|
|
453
|
-
|
|
454
|
-
* options `Hash`
|
|
455
|
-
* :path `String` to save a file on the disk.
|
|
456
|
-
|
|
457
|
-
```ruby
|
|
458
|
-
page.go_to("https://google.com/")
|
|
459
|
-
page.mhtml(path: "google.mhtml") # => 87742
|
|
460
|
-
```
|
|
461
|
-
|
|
462
|
-
## Screencast
|
|
463
|
-
|
|
464
|
-
#### start_screencast(\*\*options) { |data, metadata, session_id| ... }
|
|
465
|
-
|
|
466
|
-
Starts sending frames to record screencast to the given block.
|
|
467
|
-
|
|
468
|
-
* options `Hash`
|
|
469
|
-
* :format `Symbol` `:jpeg` | `:png` The format the image should be returned in.
|
|
470
|
-
* :quality `Integer` The image quality. **Note:** 0-100 works for JPEG only.
|
|
471
|
-
* :max_width `Integer` Maximum screencast frame width.
|
|
472
|
-
* :max_height `Integer` Maximum screencast frame height.
|
|
473
|
-
* :every_nth_frame `Integer` Send every n-th frame.
|
|
474
|
-
|
|
475
|
-
* Block inputs:
|
|
476
|
-
* data `String` Base64-encoded compressed image.
|
|
477
|
-
* metadata `Hash` Screencast frame metadata.
|
|
478
|
-
* "offsetTop" `Integer` Top offset in DIP.
|
|
479
|
-
* "pageScaleFactor" `Integer` Page scale factor.
|
|
480
|
-
* "deviceWidth" `Integer` Device screen width in DIP.
|
|
481
|
-
* "deviceHeight" `Integer` Device screen height in DIP.
|
|
482
|
-
* "scrollOffsetX" `Integer` Position of horizontal scroll in CSS pixels.
|
|
483
|
-
* "scrollOffsetY" `Integer` Position of vertical scroll in CSS pixels.
|
|
484
|
-
* "timestamp" `Float` (optional) Frame swap timestamp in seconds since Unix epoch.
|
|
485
|
-
* session_id `Integer` Frame number.
|
|
486
|
-
|
|
487
|
-
```ruby
|
|
488
|
-
require "base64"
|
|
489
|
-
|
|
490
|
-
page.go_to("https://apple.com/ipad")
|
|
491
|
-
|
|
492
|
-
page.start_screencast(format: :jpeg, quality: 75) do |data, metadata|
|
|
493
|
-
timestamp = (metadata["timestamp"] * 1000).to_i
|
|
494
|
-
File.binwrite("image_#{timestamp}.jpg", Base64.decode64(data))
|
|
495
|
-
end
|
|
496
|
-
|
|
497
|
-
sleep 10
|
|
498
|
-
|
|
499
|
-
page.stop_screencast
|
|
500
|
-
```
|
|
501
|
-
|
|
502
|
-
> ### 📝 NOTE
|
|
503
|
-
>
|
|
504
|
-
> Chrome only sends new frames while page content is changing. For example, if
|
|
505
|
-
> there is an animation or a video on the page, Chrome sends frames at the rate
|
|
506
|
-
> requested. On the other hand, if the page is nothing but a wall of static text,
|
|
507
|
-
> Chrome sends frames while the page renders. Once Chrome has finished rendering
|
|
508
|
-
> the page, it sends no more frames until something changes (e.g., navigating to
|
|
509
|
-
> another location).
|
|
510
|
-
|
|
511
|
-
#### stop_screencast
|
|
512
|
-
|
|
513
|
-
Stops sending frames.
|
|
514
|
-
|
|
515
|
-
## Network
|
|
516
|
-
|
|
517
|
-
`page.network`
|
|
518
|
-
|
|
519
|
-
#### traffic `Array<Network::Exchange>`
|
|
520
|
-
|
|
521
|
-
Returns all information about network traffic as `Network::Exchange` instance
|
|
522
|
-
which in general is a wrapper around `request`, `response` and `error`.
|
|
523
|
-
|
|
524
|
-
```ruby
|
|
525
|
-
page.go_to("https://github.com/")
|
|
526
|
-
page.network.traffic # => [#<Ferrum::Network::Exchange, ...]
|
|
527
|
-
```
|
|
528
|
-
|
|
529
|
-
#### request : `Network::Request`
|
|
530
|
-
|
|
531
|
-
Page request of the main frame.
|
|
532
|
-
|
|
533
|
-
```ruby
|
|
534
|
-
page.go_to("https://github.com/")
|
|
535
|
-
page.network.request # => #<Ferrum::Network::Request...
|
|
536
|
-
```
|
|
537
|
-
|
|
538
|
-
#### response : `Network::Response`
|
|
539
|
-
|
|
540
|
-
Page response of the main frame.
|
|
541
|
-
|
|
542
|
-
```ruby
|
|
543
|
-
page.go_to("https://github.com/")
|
|
544
|
-
page.network.response # => #<Ferrum::Network::Response...
|
|
545
|
-
```
|
|
546
|
-
|
|
547
|
-
#### status : `Integer`
|
|
548
|
-
|
|
549
|
-
Contains the status code of the main page response (e.g., 200 for a
|
|
550
|
-
success). This is just a shortcut for `response.status`.
|
|
551
|
-
|
|
552
|
-
```ruby
|
|
553
|
-
page.go_to("https://github.com/")
|
|
554
|
-
page.network.status # => 200
|
|
555
|
-
```
|
|
556
|
-
|
|
557
|
-
#### wait_for_idle(\*\*options) : `Boolean`
|
|
558
|
-
|
|
559
|
-
Waits for network idle, returns `true` in case of success and `false` if there are still connections.
|
|
560
|
-
|
|
561
|
-
* options `Hash`
|
|
562
|
-
* :connections `Integer` how many connections are allowed for network to be
|
|
563
|
-
idling, `0` by default
|
|
564
|
-
* :duration `Float` sleep for given amount of time and check again, `0.05` by
|
|
565
|
-
default
|
|
566
|
-
* :timeout `Float` during what time we try to check idle, `browser.timeout`
|
|
567
|
-
by default
|
|
568
|
-
|
|
569
|
-
```ruby
|
|
570
|
-
page.go_to("https://example.com/")
|
|
571
|
-
page.at_xpath("//a[text() = 'No UI changes button']").click
|
|
572
|
-
page.network.wait_for_idle # => true
|
|
573
|
-
```
|
|
574
|
-
|
|
575
|
-
#### wait_for_idle!(\*\*options)
|
|
576
|
-
|
|
577
|
-
Waits for network idle or raises `Ferrum::TimeoutError` error. Accepts same arguments as `wait_for_idle`.
|
|
578
|
-
|
|
579
|
-
```ruby
|
|
580
|
-
page.go_to("https://example.com/")
|
|
581
|
-
page.at_xpath("//a[text() = 'No UI changes button']").click
|
|
582
|
-
page.network.wait_for_idle! # might raise an error
|
|
583
|
-
```
|
|
584
|
-
|
|
585
|
-
#### clear(type)
|
|
586
|
-
|
|
587
|
-
Clear page's cache or collected traffic.
|
|
588
|
-
|
|
589
|
-
* type `Symbol` it is either `:traffic` or `:cache`
|
|
590
|
-
|
|
591
|
-
```ruby
|
|
592
|
-
traffic = page.network.traffic # => []
|
|
593
|
-
page.go_to("https://github.com/")
|
|
594
|
-
traffic.size # => 51
|
|
595
|
-
page.network.clear(:traffic)
|
|
596
|
-
traffic.size # => 0
|
|
597
|
-
```
|
|
598
|
-
|
|
599
|
-
#### intercept(\*\*options)
|
|
600
|
-
|
|
601
|
-
Set request interception for given options. This method is only sets request
|
|
602
|
-
interception, you should use `on` callback to catch requests and abort or
|
|
603
|
-
continue them.
|
|
604
|
-
|
|
605
|
-
* options `Hash`
|
|
606
|
-
* :pattern `String` \* by default
|
|
607
|
-
* :resource_type `Symbol` one of the [resource types](https://chromedevtools.github.io/devtools-protocol/tot/Network#type-ResourceType)
|
|
608
|
-
|
|
609
|
-
```ruby
|
|
610
|
-
browser = Ferrum::Browser.new
|
|
611
|
-
page = browser.create_page
|
|
612
|
-
page.network.intercept
|
|
613
|
-
page.on(:request) do |request|
|
|
614
|
-
if request.match?(/bla-bla/)
|
|
615
|
-
request.abort
|
|
616
|
-
elsif request.match?(/lorem/)
|
|
617
|
-
request.respond(body: "Lorem ipsum")
|
|
618
|
-
else
|
|
619
|
-
request.continue
|
|
620
|
-
end
|
|
621
|
-
end
|
|
622
|
-
page.go_to("https://google.com")
|
|
623
|
-
```
|
|
624
|
-
|
|
625
|
-
#### authorize(\*\*options, &block)
|
|
626
|
-
|
|
627
|
-
If site or proxy uses authorization you can provide credentials using this method.
|
|
628
|
-
|
|
629
|
-
* options `Hash`
|
|
630
|
-
* :type `Symbol` `:server` | `:proxy` site or proxy authorization
|
|
631
|
-
* :user `String`
|
|
632
|
-
* :password `String`
|
|
633
|
-
* &block accepts authenticated request, which you must subsequently allow or deny, if you don't
|
|
634
|
-
care about unwanted requests just call `request.continue`.
|
|
635
|
-
|
|
636
|
-
```ruby
|
|
637
|
-
page.network.authorize(user: "login", password: "pass") { |req| req.continue }
|
|
638
|
-
page.go_to("http://example.com/authenticated")
|
|
639
|
-
puts page.network.status # => 200
|
|
640
|
-
puts page.body # => Welcome, authenticated client
|
|
641
|
-
```
|
|
642
|
-
|
|
643
|
-
Since Chrome implements authorize using request interception you must continue or abort authorized requests. If you
|
|
644
|
-
already have code that uses interception you can use `authorize` without block, but if not you are obliged to pass
|
|
645
|
-
block, so this is version doesn't pass block and can work just fine:
|
|
646
|
-
|
|
647
|
-
```ruby
|
|
648
|
-
browser = Ferrum::Browser.new
|
|
649
|
-
page = browser.create_page
|
|
650
|
-
page.network.intercept
|
|
651
|
-
page.on(:request) do |request|
|
|
652
|
-
if request.resource_type == "Image"
|
|
653
|
-
request.abort
|
|
654
|
-
else
|
|
655
|
-
request.continue
|
|
656
|
-
end
|
|
657
|
-
end
|
|
658
|
-
|
|
659
|
-
page.network.authorize(user: "login", password: "pass", type: :proxy)
|
|
660
|
-
|
|
661
|
-
page.go_to("https://google.com")
|
|
662
|
-
```
|
|
663
|
-
|
|
664
|
-
You used to call `authorize` method without block, but since it's implemented using request interception there could be
|
|
665
|
-
a collision with another part of your code that also uses request interception, so that authorize allows the request
|
|
666
|
-
while your code denies but it's too late. The block is mandatory now.
|
|
667
|
-
|
|
668
|
-
#### emulate_network_conditions(\*\*options)
|
|
669
|
-
|
|
670
|
-
Activates emulation of network conditions.
|
|
671
|
-
|
|
672
|
-
* options `Hash`
|
|
673
|
-
* :offline `Boolean` emulate internet disconnection, `false` by default
|
|
674
|
-
* :latency `Integer` minimum latency from request sent to response headers received (ms), `0` by
|
|
675
|
-
default
|
|
676
|
-
* :download_throughput `Integer` maximal aggregated download throughput (bytes/sec), `-1`
|
|
677
|
-
by default, disables download throttling
|
|
678
|
-
* :upload_throughput `Integer` maximal aggregated upload throughput (bytes/sec), `-1`
|
|
679
|
-
by default, disables download throttling
|
|
680
|
-
* :connection_type `String` connection type if known, one of: none, cellular2g, cellular3g, cellular4g,
|
|
681
|
-
bluetooth, ethernet, wifi, wimax, other. `nil` by default
|
|
682
|
-
|
|
683
|
-
```ruby
|
|
684
|
-
page.network.emulate_network_conditions(connection_type: "cellular2g")
|
|
685
|
-
page.go_to("https://github.com/")
|
|
686
|
-
```
|
|
687
|
-
|
|
688
|
-
#### offline_mode
|
|
689
|
-
|
|
690
|
-
Activates offline mode for a page.
|
|
691
|
-
|
|
692
|
-
```ruby
|
|
693
|
-
page.network.offline_mode
|
|
694
|
-
page.go_to("https://github.com/") # => Ferrum::StatusError (Request to https://github.com/ failed(net::ERR_INTERNET_DISCONNECTED))
|
|
695
|
-
```
|
|
696
|
-
|
|
697
|
-
#### cache(disable: `Boolean`)
|
|
698
|
-
|
|
699
|
-
Toggles ignoring cache for each request. If true, cache will not be used.
|
|
700
|
-
|
|
701
|
-
```ruby
|
|
702
|
-
page.network.cache(disable: true)
|
|
703
|
-
```
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
## Downloads
|
|
707
|
-
|
|
708
|
-
`page.downloads`
|
|
709
|
-
|
|
710
|
-
#### files `Array<Hash>`
|
|
711
|
-
|
|
712
|
-
Returns all information about downloaded files as a `Hash`.
|
|
713
|
-
|
|
714
|
-
```ruby
|
|
715
|
-
page.go_to("http://localhost/attachment.pdf")
|
|
716
|
-
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"}]
|
|
717
|
-
```
|
|
718
|
-
|
|
719
|
-
#### wait(timeout)
|
|
720
|
-
|
|
721
|
-
Waits until the download is finished.
|
|
722
|
-
|
|
723
|
-
```ruby
|
|
724
|
-
page.go_to("http://localhost/attachment.pdf")
|
|
725
|
-
page.downloads.wait
|
|
726
|
-
```
|
|
727
|
-
|
|
728
|
-
or
|
|
729
|
-
|
|
730
|
-
```ruby
|
|
731
|
-
page.go_to("http://localhost/page")
|
|
732
|
-
page.downloads.wait { page.at_css("#download").click }
|
|
733
|
-
```
|
|
734
|
-
|
|
735
|
-
#### set_behavior(\*\*options)
|
|
736
|
-
|
|
737
|
-
Sets behavior in case of file to be downloaded.
|
|
738
|
-
|
|
739
|
-
* options `Hash`
|
|
740
|
-
* :save_path `String` absolute path of where to store the file
|
|
741
|
-
* :behavior `Symbol` `deny | allow | allowAndName | default`, `allow` by default
|
|
742
|
-
|
|
743
|
-
```ruby
|
|
744
|
-
page.go_to("https://example.com/")
|
|
745
|
-
page.downloads.set_behavior(save_path: "/tmp", behavior: :allow)
|
|
746
|
-
```
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
## Proxy
|
|
750
|
-
|
|
751
|
-
You can set a proxy with a `:proxy` option:
|
|
752
|
-
|
|
753
|
-
```ruby
|
|
754
|
-
Ferrum::Browser.new(proxy: { host: "x.x.x.x", port: "8800", user: "user", password: "pa$$" })
|
|
755
|
-
```
|
|
756
|
-
|
|
757
|
-
`:bypass` can specify semi-colon-separated list of hosts for which proxy shouldn't be used:
|
|
758
|
-
|
|
759
|
-
```ruby
|
|
760
|
-
Ferrum::Browser.new(proxy: { host: "x.x.x.x", port: "8800", bypass: "*.google.com;*foo.com" })
|
|
761
|
-
```
|
|
762
|
-
|
|
763
|
-
In general passing a proxy option when instantiating a browser results in a browser running with proxy command line
|
|
764
|
-
flags, so that it affects all pages and contexts. You can create a page in a new context which can use its own proxy
|
|
765
|
-
settings:
|
|
766
|
-
|
|
767
|
-
```ruby
|
|
768
|
-
browser = Ferrum::Browser.new
|
|
769
|
-
|
|
770
|
-
browser.create_page(proxy: { host: "x.x.x.x", port: 31337, user: "user", password: "password" }) do |page|
|
|
771
|
-
page.go_to("https://api.ipify.org?format=json")
|
|
772
|
-
page.body # => "x.x.x.x"
|
|
773
|
-
end
|
|
774
|
-
|
|
775
|
-
browser.create_page(proxy: { host: "y.y.y.y", port: 31337, user: "user", password: "password" }) do |page|
|
|
776
|
-
page.go_to("https://api.ipify.org?format=json")
|
|
777
|
-
page.body # => "y.y.y.y"
|
|
778
|
-
end
|
|
779
|
-
```
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
### Mouse
|
|
783
|
-
|
|
784
|
-
`page.mouse`
|
|
785
|
-
|
|
786
|
-
#### scroll_to(x, y)
|
|
787
|
-
|
|
788
|
-
Scroll page to a given x, y
|
|
789
|
-
|
|
790
|
-
* x `Integer` the pixel along the horizontal axis of the document that you
|
|
791
|
-
want displayed in the upper left
|
|
792
|
-
* y `Integer` the pixel along the vertical axis of the document that you want
|
|
793
|
-
displayed in the upper left
|
|
794
|
-
|
|
795
|
-
```ruby
|
|
796
|
-
page.go_to("https://www.google.com/search?q=Ruby+headless+driver+for+Capybara")
|
|
797
|
-
page.mouse.scroll_to(0, 400)
|
|
798
|
-
```
|
|
799
|
-
|
|
800
|
-
#### click(\*\*options) : `Mouse`
|
|
801
|
-
|
|
802
|
-
Click given coordinates, fires mouse move, down and up events.
|
|
803
|
-
|
|
804
|
-
* options `Hash`
|
|
805
|
-
* :x `Integer`
|
|
806
|
-
* :y `Integer`
|
|
807
|
-
* :delay `Float` defaults to 0. Delay between mouse down and mouse up events
|
|
808
|
-
* :button `Symbol` :left | :right, defaults to :left
|
|
809
|
-
* :count `Integer` defaults to 1
|
|
810
|
-
* :modifiers `Integer` bitfield for key modifiers. See`keyboard.modifiers`
|
|
811
|
-
|
|
812
|
-
#### down(\*\*options) : `Mouse`
|
|
813
|
-
|
|
814
|
-
Mouse down for given coordinates.
|
|
815
|
-
|
|
816
|
-
* options `Hash`
|
|
817
|
-
* :button `Symbol` :left | :right, defaults to :left
|
|
818
|
-
* :count `Integer` defaults to 1
|
|
819
|
-
* :modifiers `Integer` bitfield for key modifiers. See`keyboard.modifiers`
|
|
820
|
-
|
|
821
|
-
#### up(\*\*options) : `Mouse`
|
|
822
|
-
|
|
823
|
-
Mouse up for given coordinates.
|
|
824
|
-
|
|
825
|
-
* options `Hash`
|
|
826
|
-
* :button `Symbol` :left | :right, defaults to :left
|
|
827
|
-
* :count `Integer` defaults to 1
|
|
828
|
-
* :modifiers `Integer` bitfield for key modifiers. See`keyboard.modifiers`
|
|
829
|
-
|
|
830
|
-
#### move(x:, y:, steps: 1) : `Mouse`
|
|
831
|
-
|
|
832
|
-
Mouse move to given x and y.
|
|
833
|
-
|
|
834
|
-
* options `Hash`
|
|
835
|
-
* :x `Integer`
|
|
836
|
-
* :y `Integer`
|
|
837
|
-
* :steps `Integer` defaults to 1. Sends intermediate mousemove events.
|
|
838
|
-
|
|
839
|
-
### Keyboard
|
|
840
|
-
|
|
841
|
-
`page.keyboard`
|
|
842
|
-
|
|
843
|
-
#### down(key) : `Keyboard`
|
|
844
|
-
|
|
845
|
-
Dispatches a keydown event.
|
|
846
|
-
|
|
847
|
-
* key `String` | `Symbol` Name of key such as "a", :enter, :backspace
|
|
848
|
-
|
|
849
|
-
#### up(key) : `Keyboard`
|
|
850
|
-
|
|
851
|
-
Dispatches a keyup event.
|
|
852
|
-
|
|
853
|
-
* key `String` | `Symbol` Name of key such as "b", :enter, :backspace
|
|
854
|
-
|
|
855
|
-
#### type(\*keys) : `Keyboard`
|
|
856
|
-
|
|
857
|
-
Sends a keydown, keypress/input, and keyup event for each character in the text.
|
|
858
|
-
|
|
859
|
-
* text `String` | `Array<String> | Array<Symbol>` A text to type into a focused
|
|
860
|
-
element, `[:Shift, "s"], "tring"`
|
|
861
|
-
|
|
862
|
-
#### modifiers(keys) : `Integer`
|
|
863
|
-
|
|
864
|
-
Returns bitfield for a given keys
|
|
865
|
-
|
|
866
|
-
* keys `Array<Symbol>` :alt | :ctrl | :command | :shift
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
## Cookies
|
|
870
|
-
|
|
871
|
-
`page.cookies`
|
|
872
|
-
|
|
873
|
-
#### all : `Hash<String, Cookie>`
|
|
874
|
-
|
|
875
|
-
Returns cookies hash
|
|
876
|
-
|
|
877
|
-
```ruby
|
|
878
|
-
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}>}
|
|
879
|
-
```
|
|
880
|
-
|
|
881
|
-
#### [](value) : `Cookie`
|
|
882
|
-
|
|
883
|
-
Returns cookie
|
|
884
|
-
|
|
885
|
-
* value `String`
|
|
886
|
-
|
|
887
|
-
```ruby
|
|
888
|
-
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}>
|
|
889
|
-
```
|
|
890
|
-
|
|
891
|
-
#### set(value) : `Boolean`
|
|
892
|
-
|
|
893
|
-
Sets a cookie
|
|
894
|
-
|
|
895
|
-
* value `Hash`
|
|
896
|
-
* :name `String`
|
|
897
|
-
* :value `String`
|
|
898
|
-
* :domain `String`
|
|
899
|
-
* :expires `Integer`
|
|
900
|
-
* :samesite `String`
|
|
901
|
-
* :httponly `Boolean`
|
|
902
|
-
|
|
903
|
-
```ruby
|
|
904
|
-
page.cookies.set(name: "stealth", value: "omg", domain: "google.com") # => true
|
|
905
|
-
```
|
|
906
|
-
|
|
907
|
-
* value `Cookie`
|
|
908
|
-
|
|
909
|
-
```ruby
|
|
910
|
-
nid_cookie = page.cookies["NID"] # => <Ferrum::Cookies::Cookie:0x0000558624b67a88>
|
|
911
|
-
page.cookies.set(nid_cookie) # => true
|
|
912
|
-
```
|
|
913
|
-
|
|
914
|
-
#### remove(\*\*options) : `Boolean`
|
|
915
|
-
|
|
916
|
-
Removes given cookie
|
|
917
|
-
|
|
918
|
-
* options `Hash`
|
|
919
|
-
* :name `String`
|
|
920
|
-
* :domain `String`
|
|
921
|
-
* :url `String`
|
|
922
|
-
|
|
923
|
-
```ruby
|
|
924
|
-
page.cookies.remove(name: "stealth", domain: "google.com") # => true
|
|
925
|
-
```
|
|
926
|
-
|
|
927
|
-
#### clear : `Boolean`
|
|
928
|
-
|
|
929
|
-
Removes all cookies for current page
|
|
930
|
-
|
|
931
|
-
```ruby
|
|
932
|
-
page.cookies.clear # => true
|
|
933
|
-
```
|
|
934
|
-
|
|
935
|
-
#### store(path) : `Boolean`
|
|
936
|
-
|
|
937
|
-
Stores all cookies of current page in a file.
|
|
938
|
-
|
|
939
|
-
```ruby
|
|
940
|
-
# Cookies are saved into cookies.yml
|
|
941
|
-
page.cookies.store # => 15657
|
|
942
|
-
```
|
|
943
|
-
|
|
944
|
-
#### load(path) : `Boolean`
|
|
945
|
-
|
|
946
|
-
Loads all cookies from the file and sets them for current page.
|
|
947
|
-
|
|
948
|
-
```ruby
|
|
949
|
-
# Cookies are loaded from cookies.yml
|
|
950
|
-
page.cookies.load # => true
|
|
951
|
-
```
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
## Headers
|
|
955
|
-
|
|
956
|
-
`page.headers`
|
|
957
|
-
|
|
958
|
-
#### get : `Hash`
|
|
959
|
-
|
|
960
|
-
Get all headers
|
|
961
|
-
|
|
962
|
-
#### set(headers) : `Boolean`
|
|
963
|
-
|
|
964
|
-
Set given headers. Eventually clear all headers and set given ones.
|
|
965
|
-
|
|
966
|
-
* headers `Hash` key-value pairs for example `"User-Agent" => "Browser"`
|
|
967
|
-
|
|
968
|
-
#### add(headers) : `Boolean`
|
|
969
|
-
|
|
970
|
-
Adds given headers to already set ones.
|
|
971
|
-
|
|
972
|
-
* headers `Hash` key-value pairs for example `"Referer" => "http://example.com"`
|
|
973
|
-
|
|
974
|
-
#### clear : `Boolean`
|
|
975
|
-
|
|
976
|
-
Clear all headers.
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
## JavaScript
|
|
980
|
-
|
|
981
|
-
#### evaluate(expression, \*args)
|
|
982
|
-
|
|
983
|
-
Evaluate and return result for given JS expression
|
|
984
|
-
|
|
985
|
-
* expression `String` should be valid JavaScript
|
|
986
|
-
* args `Object` you can pass arguments, though it should be a valid `Node` or a
|
|
987
|
-
simple value.
|
|
988
|
-
|
|
989
|
-
```ruby
|
|
990
|
-
page.evaluate("[window.scrollX, window.scrollY]")
|
|
991
|
-
```
|
|
992
|
-
|
|
993
|
-
#### evaluate_async(expression, wait_time, \*args)
|
|
994
|
-
|
|
995
|
-
Evaluate asynchronous expression and return result
|
|
996
|
-
|
|
997
|
-
* expression `String` should be valid JavaScript
|
|
998
|
-
* wait_time How long we should wait for Promise to resolve or reject
|
|
999
|
-
* args `Object` you can pass arguments, though it should be a valid `Node` or a
|
|
1000
|
-
simple value.
|
|
1001
|
-
|
|
1002
|
-
```ruby
|
|
1003
|
-
page.evaluate_async(%(arguments[0]({foo: "bar"})), 5) # => { "foo" => "bar" }
|
|
1004
|
-
```
|
|
1005
|
-
|
|
1006
|
-
#### execute(expression, \*args)
|
|
1007
|
-
|
|
1008
|
-
Execute expression. Doesn't return the result
|
|
1009
|
-
|
|
1010
|
-
* expression `String` should be valid JavaScript
|
|
1011
|
-
* args `Object` you can pass arguments, though it should be a valid `Node` or a
|
|
1012
|
-
simple value.
|
|
1013
|
-
|
|
1014
|
-
```ruby
|
|
1015
|
-
page.execute(%(1 + 1)) # => true
|
|
1016
|
-
```
|
|
1017
|
-
|
|
1018
|
-
#### evaluate_on_new_document(expression)
|
|
1019
|
-
|
|
1020
|
-
Evaluate JavaScript to modify things before a page load
|
|
1021
|
-
|
|
1022
|
-
* expression `String` should be valid JavaScript
|
|
1023
|
-
|
|
1024
|
-
```ruby
|
|
1025
|
-
browser.evaluate_on_new_document <<~JS
|
|
1026
|
-
Object.defineProperty(navigator, "languages", {
|
|
1027
|
-
get: function() { return ["tlh"]; }
|
|
1028
|
-
});
|
|
1029
|
-
JS
|
|
1030
|
-
```
|
|
1031
|
-
|
|
1032
|
-
#### add_script_tag(\*\*options) : `Boolean`
|
|
1033
|
-
|
|
1034
|
-
* options `Hash`
|
|
1035
|
-
* :url `String`
|
|
1036
|
-
* :path `String`
|
|
1037
|
-
* :content `String`
|
|
1038
|
-
* :type `String` - `text/javascript` by default
|
|
1039
|
-
|
|
1040
|
-
```ruby
|
|
1041
|
-
page.add_script_tag(url: "http://example.com/stylesheet.css") # => true
|
|
1042
|
-
```
|
|
1043
|
-
|
|
1044
|
-
#### add_style_tag(\*\*options) : `Boolean`
|
|
1045
|
-
|
|
1046
|
-
* options `Hash`
|
|
1047
|
-
* :url `String`
|
|
1048
|
-
* :path `String`
|
|
1049
|
-
* :content `String`
|
|
1050
|
-
|
|
1051
|
-
```ruby
|
|
1052
|
-
page.add_style_tag(content: "h1 { font-size: 40px; }") # => true
|
|
1053
|
-
|
|
1054
|
-
```
|
|
1055
|
-
#### bypass_csp(\*\*options) : `Boolean`
|
|
1056
|
-
|
|
1057
|
-
* options `Hash`
|
|
1058
|
-
* :enabled `Boolean`, `true` by default
|
|
1059
|
-
|
|
1060
|
-
```ruby
|
|
1061
|
-
page.bypass_csp # => true
|
|
1062
|
-
page.go_to("https://github.com/ruby-concurrency/concurrent-ruby/blob/master/docs-source/promises.in.md")
|
|
1063
|
-
page.refresh
|
|
1064
|
-
page.add_script_tag(content: "window.__injected = 42")
|
|
1065
|
-
page.evaluate("window.__injected") # => 42
|
|
1066
|
-
```
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
## Emulation
|
|
1070
|
-
|
|
1071
|
-
#### disable_javascript
|
|
1072
|
-
|
|
1073
|
-
Disables Javascripts from the loaded HTML source.
|
|
1074
|
-
You can still evaluate JavaScript with `evaluate` or `execute`.
|
|
1075
|
-
Returns nothing.
|
|
1076
|
-
|
|
1077
|
-
```ruby
|
|
1078
|
-
page.disable_javascript
|
|
1079
|
-
```
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
#### set_viewport
|
|
1083
|
-
|
|
1084
|
-
Overrides device screen dimensions and emulates viewport.
|
|
1085
|
-
|
|
1086
|
-
* options `Hash`
|
|
1087
|
-
* :width `Integer`, viewport width. `0` by default
|
|
1088
|
-
* :height `Integer`, viewport height. `0` by default
|
|
1089
|
-
* :scale_factor `Float`, device scale factor. `0` by default
|
|
1090
|
-
* :mobile `Boolean`, whether to emulate mobile device. `false` by default
|
|
1091
|
-
|
|
1092
|
-
```ruby
|
|
1093
|
-
page.set_viewport(width: 1000, height: 600, scale_factor: 3)
|
|
1094
|
-
```
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
## Frames
|
|
1098
|
-
|
|
1099
|
-
#### frames : `Array[Frame] | []`
|
|
1100
|
-
|
|
1101
|
-
Returns all the frames current page have.
|
|
1102
|
-
|
|
1103
|
-
```ruby
|
|
1104
|
-
page.go_to("https://www.w3schools.com/tags/tag_frame.asp")
|
|
1105
|
-
page.frames # =>
|
|
1106
|
-
# [
|
|
1107
|
-
# #<Ferrum::Frame @id="C6D104CE454A025FBCF22B98DE612B12" @parent_id=nil @name=nil @state=:stopped_loading @execution_id=1>,
|
|
1108
|
-
# #<Ferrum::Frame @id="C09C4E4404314AAEAE85928EAC109A93" @parent_id="C6D104CE454A025FBCF22B98DE612B12" @state=:stopped_loading @execution_id=2>,
|
|
1109
|
-
# #<Ferrum::Frame @id="2E9C7F476ED09D87A42F2FEE3C6FBC3C" @parent_id="C6D104CE454A025FBCF22B98DE612B12" @state=:stopped_loading @execution_id=3>,
|
|
1110
|
-
# ...
|
|
1111
|
-
# ]
|
|
1112
|
-
```
|
|
1113
|
-
|
|
1114
|
-
#### main_frame : `Frame`
|
|
1115
|
-
|
|
1116
|
-
Returns page's main frame, the top of the tree and the parent of all frames.
|
|
1117
|
-
|
|
1118
|
-
#### frame_by(\*\*options) : `Frame | nil`
|
|
1119
|
-
|
|
1120
|
-
Find frame by given options.
|
|
1121
|
-
|
|
1122
|
-
* options `Hash`
|
|
1123
|
-
* :id `String` - Unique frame's id that browser provides
|
|
1124
|
-
* :name `String` - Frame's name if there's one
|
|
1125
|
-
|
|
1126
|
-
```ruby
|
|
1127
|
-
page.frame_by(id: "C6D104CE454A025FBCF22B98DE612B12")
|
|
1128
|
-
```
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
## Frame
|
|
1132
|
-
|
|
1133
|
-
#### id : `String`
|
|
1134
|
-
|
|
1135
|
-
Frame's unique id.
|
|
1136
|
-
|
|
1137
|
-
#### parent_id : `String | nil`
|
|
1138
|
-
|
|
1139
|
-
Parent frame id if this one is nested in another one.
|
|
1140
|
-
|
|
1141
|
-
#### parent : `Frame | nil`
|
|
1142
|
-
|
|
1143
|
-
Parent frame if this one is nested in another one.
|
|
1144
|
-
|
|
1145
|
-
#### frame_element : `Node | nil`
|
|
1146
|
-
|
|
1147
|
-
Returns the element in which the window is embedded.
|
|
1148
|
-
|
|
1149
|
-
#### execution_id : `Integer`
|
|
1150
|
-
|
|
1151
|
-
Execution context id which is used by JS, each frame has its own context in
|
|
1152
|
-
which JS evaluates.
|
|
1153
|
-
|
|
1154
|
-
#### name : `String | nil`
|
|
1155
|
-
|
|
1156
|
-
If frame was given a name it should be here.
|
|
1157
|
-
|
|
1158
|
-
#### state : `Symbol | nil`
|
|
1159
|
-
|
|
1160
|
-
One of the states frame's in:
|
|
1161
|
-
|
|
1162
|
-
* `:started_loading`
|
|
1163
|
-
* `:navigated`
|
|
1164
|
-
* `:stopped_loading`
|
|
1165
|
-
|
|
1166
|
-
#### url : `String`
|
|
1167
|
-
|
|
1168
|
-
Returns current frame's location href.
|
|
1169
|
-
|
|
1170
|
-
```ruby
|
|
1171
|
-
page.go_to("https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe")
|
|
1172
|
-
frame = page.frames[1]
|
|
1173
|
-
frame.url # => https://interactive-examples.mdn.mozilla.net/pages/tabbed/iframe.html
|
|
1174
|
-
```
|
|
1175
|
-
|
|
1176
|
-
#### title
|
|
1177
|
-
|
|
1178
|
-
Returns current frame's title.
|
|
1179
|
-
|
|
1180
|
-
```ruby
|
|
1181
|
-
page.go_to("https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe")
|
|
1182
|
-
frame = page.frames[1]
|
|
1183
|
-
frame.title # => HTML Demo: <iframe>
|
|
1184
|
-
```
|
|
1185
|
-
|
|
1186
|
-
#### main? : `Boolean`
|
|
1187
|
-
|
|
1188
|
-
If current frame is the main frame of the page (top of the tree).
|
|
1189
|
-
|
|
1190
|
-
```ruby
|
|
1191
|
-
page.go_to("https://www.w3schools.com/tags/tag_frame.asp")
|
|
1192
|
-
frame = page.frame_by(id: "C09C4E4404314AAEAE85928EAC109A93")
|
|
1193
|
-
frame.main? # => false
|
|
1194
|
-
```
|
|
1195
|
-
|
|
1196
|
-
#### current_url : `String`
|
|
1197
|
-
|
|
1198
|
-
Returns current frame's top window location href.
|
|
1199
|
-
|
|
1200
|
-
```ruby
|
|
1201
|
-
page.go_to("https://www.w3schools.com/tags/tag_frame.asp")
|
|
1202
|
-
frame = page.frame_by(id: "C09C4E4404314AAEAE85928EAC109A93")
|
|
1203
|
-
frame.current_url # => "https://www.w3schools.com/tags/tag_frame.asp"
|
|
1204
|
-
```
|
|
1205
|
-
|
|
1206
|
-
#### current_title : `String`
|
|
1207
|
-
|
|
1208
|
-
Returns current frame's top window title.
|
|
1209
|
-
|
|
1210
|
-
```ruby
|
|
1211
|
-
page.go_to("https://www.w3schools.com/tags/tag_frame.asp")
|
|
1212
|
-
frame = page.frame_by(id: "C09C4E4404314AAEAE85928EAC109A93")
|
|
1213
|
-
frame.current_title # => "HTML frame tag"
|
|
1214
|
-
```
|
|
1215
|
-
|
|
1216
|
-
#### body : `String`
|
|
1217
|
-
|
|
1218
|
-
Returns current frame's html.
|
|
1219
|
-
|
|
1220
|
-
```ruby
|
|
1221
|
-
page.go_to("https://www.w3schools.com/tags/tag_frame.asp")
|
|
1222
|
-
frame = page.frame_by(id: "C09C4E4404314AAEAE85928EAC109A93")
|
|
1223
|
-
frame.body # => "<html><head></head><body></body></html>"
|
|
1224
|
-
```
|
|
1225
|
-
|
|
1226
|
-
#### doctype
|
|
1227
|
-
|
|
1228
|
-
Returns current frame's doctype.
|
|
1229
|
-
|
|
1230
|
-
```ruby
|
|
1231
|
-
page.go_to("https://www.w3schools.com/tags/tag_frame.asp")
|
|
1232
|
-
page.main_frame.doctype # => "<!DOCTYPE html>"
|
|
1233
|
-
```
|
|
1234
|
-
|
|
1235
|
-
#### content = html
|
|
1236
|
-
|
|
1237
|
-
Sets a content of a given frame.
|
|
1238
|
-
|
|
1239
|
-
* html `String`
|
|
1240
|
-
|
|
1241
|
-
```ruby
|
|
1242
|
-
page.go_to("https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe")
|
|
1243
|
-
frame = page.frames[1]
|
|
1244
|
-
frame.body # <html lang="en"><head><style>body {transition: opacity ease-in 0.2s; }...
|
|
1245
|
-
frame.content = "<html><head></head><body><p>lol</p></body></html>"
|
|
1246
|
-
frame.body # => <html><head></head><body><p>lol</p></body></html>
|
|
1247
|
-
```
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
## Dialogs
|
|
1251
|
-
|
|
1252
|
-
#### accept(text)
|
|
1253
|
-
|
|
1254
|
-
Accept dialog with given text or default prompt if applicable
|
|
1255
|
-
|
|
1256
|
-
* text `String`
|
|
1257
|
-
|
|
1258
|
-
#### dismiss
|
|
1259
|
-
|
|
1260
|
-
Dismiss dialog
|
|
1261
|
-
|
|
1262
|
-
```ruby
|
|
1263
|
-
page.on(:dialog) do |dialog|
|
|
1264
|
-
if dialog.match?(/bla-bla/)
|
|
1265
|
-
dialog.accept
|
|
1266
|
-
else
|
|
1267
|
-
dialog.dismiss
|
|
1268
|
-
end
|
|
1269
|
-
end
|
|
1270
|
-
page.go_to("https://google.com")
|
|
1271
|
-
```
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
## Animation
|
|
1275
|
-
|
|
1276
|
-
You can slow down or speed up CSS animations.
|
|
1277
|
-
|
|
1278
|
-
#### playback_rate : `Integer`
|
|
1279
|
-
|
|
1280
|
-
Returns playback rate for CSS animations, defaults to `1`.
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
#### playback_rate = value
|
|
1284
|
-
|
|
1285
|
-
Sets playback rate of CSS animations
|
|
1286
|
-
|
|
1287
|
-
* value `Integer`
|
|
1288
|
-
|
|
1289
|
-
```ruby
|
|
1290
|
-
page.playback_rate = 2000
|
|
1291
|
-
page.go_to("https://google.com")
|
|
1292
|
-
page.playback_rate # => 2000
|
|
1293
|
-
```
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
## Node
|
|
1297
|
-
|
|
1298
|
-
#### node? : `Boolean`
|
|
1299
|
-
#### frame_id
|
|
1300
|
-
#### frame : `Frame`
|
|
1301
|
-
|
|
1302
|
-
Returns [Frame](https://github.com/rubycdp/ferrum#frame) object for current node, you can keep using
|
|
1303
|
-
[Finders](https://github.com/rubycdp/ferrum#Finders) for that object:
|
|
1304
|
-
|
|
1305
|
-
```ruby
|
|
1306
|
-
frame = page.at_xpath("//iframe").frame # => Frame
|
|
1307
|
-
frame.at_css("//a[text() = 'Log in']") # => Node
|
|
1308
|
-
```
|
|
1309
|
-
|
|
1310
|
-
#### focus
|
|
1311
|
-
#### focusable?
|
|
1312
|
-
#### moving? : `Boolean`
|
|
1313
|
-
#### wait_for_stop_moving
|
|
1314
|
-
#### blur
|
|
1315
|
-
#### type
|
|
1316
|
-
#### click
|
|
1317
|
-
#### hover
|
|
1318
|
-
#### select_file
|
|
1319
|
-
#### at_xpath
|
|
1320
|
-
#### at_css
|
|
1321
|
-
#### xpath
|
|
1322
|
-
#### css
|
|
1323
|
-
#### text
|
|
1324
|
-
#### inner_text
|
|
1325
|
-
#### value
|
|
1326
|
-
#### property
|
|
1327
|
-
#### attribute
|
|
1328
|
-
#### evaluate
|
|
1329
|
-
#### selected : `Array<Node>`
|
|
1330
|
-
#### select
|
|
1331
|
-
#### scroll_into_view
|
|
1332
|
-
#### in_viewport?(of: `Node | nil`) : `Boolean`
|
|
1333
|
-
#### remove
|
|
1334
|
-
#### exists?
|
|
1335
|
-
|
|
1336
|
-
(chainable) Selects options by passed attribute.
|
|
1337
|
-
|
|
1338
|
-
```ruby
|
|
1339
|
-
page.at_xpath("//*[select]").select(["1"]) # => Node (select)
|
|
1340
|
-
page.at_xpath("//*[select]").select(["text"], by: :text) # => Node (select)
|
|
1341
|
-
```
|
|
1342
|
-
|
|
1343
|
-
Accept string, array or strings:
|
|
1344
|
-
```ruby
|
|
1345
|
-
page.at_xpath("//*[select]").select("1")
|
|
1346
|
-
page.at_xpath("//*[select]").select("1", "2")
|
|
1347
|
-
page.at_xpath("//*[select]").select(["1", "2"])
|
|
1348
|
-
```
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
## Tracing
|
|
1352
|
-
|
|
1353
|
-
You can use `tracing.record` to create a trace file which can be opened in Chrome DevTools or
|
|
1354
|
-
[timeline viewer](https://chromedevtools.github.io/timeline-viewer/).
|
|
1355
|
-
|
|
1356
|
-
```ruby
|
|
1357
|
-
page.tracing.record(path: "trace.json") do
|
|
1358
|
-
page.go_to("https://www.google.com")
|
|
1359
|
-
end
|
|
1360
|
-
```
|
|
1361
|
-
|
|
1362
|
-
#### tracing.record(\*\*options) : `String`
|
|
1363
|
-
|
|
1364
|
-
Accepts block, records trace and by default returns trace data from `Tracing.tracingComplete` event as output. When
|
|
1365
|
-
`path` is specified returns `true` and stores trace data into file.
|
|
1366
|
-
|
|
1367
|
-
* options `Hash`
|
|
1368
|
-
* :path `String` save data on the disk, `nil` by default
|
|
1369
|
-
* :encoding `Symbol` `:base64` | `:binary` encode output as Base64 or plain text. `:binary` by default
|
|
1370
|
-
* :timeout `Float` wait until file streaming finishes in the specified time or raise error, defaults to `nil`
|
|
1371
|
-
* :screenshots `Boolean` capture screenshots in the trace, `false` by default
|
|
1372
|
-
* :trace_config `Hash<String, Object>` config for
|
|
1373
|
-
[trace](https://chromedevtools.github.io/devtools-protocol/tot/Tracing/#type-TraceConfig), for categories
|
|
1374
|
-
see [getCategories](https://chromedevtools.github.io/devtools-protocol/tot/Tracing/#method-getCategories),
|
|
1375
|
-
only one trace config can be active at a time per browser.
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
## Clean Up
|
|
1379
|
-
|
|
1380
|
-
#### reset
|
|
1381
|
-
|
|
1382
|
-
Closes browser tabs opened by the `Browser` instance.
|
|
1383
|
-
|
|
1384
|
-
```ruby
|
|
1385
|
-
# connect to a long-running Chrome process
|
|
1386
|
-
browser = Ferrum::Browser.new(url: "http://localhost:9222")
|
|
1387
|
-
|
|
1388
|
-
browser.go_to("https://github.com/")
|
|
1389
|
-
|
|
1390
|
-
# clean up, lest the tab stays there hanging forever
|
|
1391
|
-
browser.reset
|
|
1392
|
-
|
|
1393
|
-
browser.quit
|
|
1394
|
-
```
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
## Thread safety ##
|
|
1398
|
-
|
|
1399
|
-
Ferrum is fully thread-safe. You can create one browser or a few as you wish and
|
|
1400
|
-
start playing around using threads. Example below shows how to create a few pages
|
|
1401
|
-
which share the same context. Context is similar to an incognito profile but you
|
|
1402
|
-
can have more than one, think of it like it's independent browser session:
|
|
1403
|
-
|
|
1404
|
-
```ruby
|
|
1405
|
-
browser = Ferrum::Browser.new
|
|
1406
|
-
context = browser.contexts.create
|
|
1407
|
-
|
|
1408
|
-
t1 = Thread.new(context) do |c|
|
|
1409
|
-
page = c.create_page
|
|
1410
|
-
page.go_to("https://www.google.com/search?q=Ruby+headless+driver+for+Capybara")
|
|
1411
|
-
page.screenshot(path: "t1.png")
|
|
1412
|
-
end
|
|
1413
|
-
|
|
1414
|
-
t2 = Thread.new(context) do |c|
|
|
1415
|
-
page = c.create_page
|
|
1416
|
-
page.go_to("https://www.google.com/search?q=Ruby+static+typing")
|
|
1417
|
-
page.screenshot(path: "t2.png")
|
|
1418
|
-
end
|
|
1419
|
-
|
|
1420
|
-
t1.join
|
|
1421
|
-
t2.join
|
|
1422
|
-
|
|
1423
|
-
context.dispose
|
|
1424
|
-
browser.quit
|
|
1425
|
-
```
|
|
1426
|
-
|
|
1427
|
-
or you can create two independent contexts:
|
|
1428
|
-
|
|
1429
|
-
```ruby
|
|
1430
|
-
browser = Ferrum::Browser.new
|
|
1431
|
-
|
|
1432
|
-
t1 = Thread.new(browser) do |b|
|
|
1433
|
-
context = b.contexts.create
|
|
1434
|
-
page = context.create_page
|
|
1435
|
-
page.go_to("https://www.google.com/search?q=Ruby+headless+driver+for+Capybara")
|
|
1436
|
-
page.screenshot(path: "t1.png")
|
|
1437
|
-
context.dispose
|
|
1438
|
-
end
|
|
1439
|
-
|
|
1440
|
-
t2 = Thread.new(browser) do |b|
|
|
1441
|
-
context = b.contexts.create
|
|
1442
|
-
page = context.create_page
|
|
1443
|
-
page.go_to("https://www.google.com/search?q=Ruby+static+typing")
|
|
1444
|
-
page.screenshot(path: "t2.png")
|
|
1445
|
-
context.dispose
|
|
1446
|
-
end
|
|
1447
|
-
|
|
1448
|
-
t1.join
|
|
1449
|
-
t2.join
|
|
1450
|
-
|
|
1451
|
-
browser.quit
|
|
1452
|
-
```
|
|
1453
|
-
|
|
1454
94
|
## Development
|
|
1455
95
|
|
|
1456
96
|
After checking out the repo, run `bundle install` to install dependencies.
|