ferrum 0.6.1 → 0.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +1 -1
- data/README.md +370 -78
- data/lib/ferrum.rb +38 -4
- data/lib/ferrum/browser.rb +19 -12
- data/lib/ferrum/browser/client.rb +23 -10
- data/lib/ferrum/browser/command.rb +57 -0
- data/lib/ferrum/browser/options/base.rb +46 -0
- data/lib/ferrum/browser/options/chrome.rb +73 -0
- data/lib/ferrum/browser/options/firefox.rb +34 -0
- data/lib/ferrum/browser/process.rb +56 -108
- data/lib/ferrum/browser/subscriber.rb +9 -1
- data/lib/ferrum/browser/web_socket.rb +23 -4
- data/lib/ferrum/browser/xvfb.rb +37 -0
- data/lib/ferrum/context.rb +3 -3
- data/lib/ferrum/cookies.rb +7 -0
- data/lib/ferrum/dialog.rb +2 -2
- data/lib/ferrum/frame.rb +20 -5
- data/lib/ferrum/frame/dom.rb +34 -37
- data/lib/ferrum/frame/runtime.rb +89 -85
- data/lib/ferrum/headers.rb +1 -1
- data/lib/ferrum/keyboard.rb +3 -3
- data/lib/ferrum/mouse.rb +14 -3
- data/lib/ferrum/network.rb +81 -20
- data/lib/ferrum/network/error.rb +8 -15
- data/lib/ferrum/network/exchange.rb +24 -21
- data/lib/ferrum/network/intercepted_request.rb +12 -3
- data/lib/ferrum/network/response.rb +4 -0
- data/lib/ferrum/node.rb +70 -26
- data/lib/ferrum/page.rb +66 -26
- data/lib/ferrum/page/frames.rb +12 -15
- data/lib/ferrum/page/screenshot.rb +64 -12
- data/lib/ferrum/rbga.rb +38 -0
- data/lib/ferrum/target.rb +10 -2
- data/lib/ferrum/version.rb +1 -1
- metadata +13 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c605388d1ea1a0f54f5f1ef24e56a93b60d109ec6eb02798d9de606cd54c29ef
|
4
|
+
data.tar.gz: a0cb3328ad526e51beaefb5eb8140659b1dd277acda5305f46464200d02ed24a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3e44b234a458079b268b7af941af8404a7aa9e6ade2d018ca07fdabcc21518a5d1ad6daafbcdcd3ed7dffe5f94f9b5a4fef36aa1b317178b1604df7f96a8a2b
|
7
|
+
data.tar.gz: 8b4ea9e0c5b0d029d743b1314dba4b6111f87f26ac4cb5c87e1b06d04b9693fc0570908456c4ced0860b07cc748d8ba783b320481d2879ec5df7b65ec70e85ec
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,43 +1,83 @@
|
|
1
|
-
# Ferrum -
|
1
|
+
# Ferrum - high-level API to control Chrome in Ruby
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
<img align="right" width="95" height="95"
|
3
|
+
<img align="right"
|
4
|
+
width="320" height="241"
|
6
5
|
alt="Ferrum logo"
|
7
|
-
src="https://raw.githubusercontent.com/
|
8
|
-
|
9
|
-
As simple as Puppeteer, though even simpler.
|
10
|
-
|
11
|
-
It is Ruby clean and high-level API to Chrome. Runs headless by default,
|
12
|
-
|
13
|
-
Chrome/
|
6
|
+
src="https://raw.githubusercontent.com/rubycdp/ferrum/master/logo.svg?sanitize=true">
|
7
|
+
|
8
|
+
#### As simple as Puppeteer, though even simpler.
|
9
|
+
|
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
|
+
[Chrome](https://www.google.com/chrome/) or
|
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_
|
15
|
+
Selenium/WebDriver/ChromeDriver dependency. The emphasis was made on a raw CDP
|
16
|
+
protocol because Chrome allows you to do so many things that are barely
|
17
|
+
supported by WebDriver because it should have consistent design with other
|
18
|
+
browsers.
|
19
|
+
|
20
|
+
* [Cuprite](https://github.com/rubycdp/cuprite) is a pure Ruby driver for
|
21
|
+
[Capybara](https://github.com/teamcapybara/capybara) based on Ferrum. If you are
|
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. It looks like [Scrapy](https://scrapy.org/) except that it uses
|
27
|
+
a real browser in order to grab data.
|
28
|
+
|
29
|
+
Web design by [Evrone](https://evrone.com/), what else
|
30
|
+
[we build with Ruby on Rails](https://evrone.com/ruby), what else
|
31
|
+
[we do at Evrone](https://evrone.com/cases#case-studies).
|
32
|
+
|
33
|
+
|
34
|
+
## Index
|
35
|
+
|
36
|
+
* [Install](https://github.com/rubycdp/ferrum#install)
|
37
|
+
* [Examples](https://github.com/rubycdp/ferrum#examples)
|
38
|
+
* [Docker](https://github.com/rubycdp/ferrum#docker)
|
39
|
+
* [Customization](https://github.com/rubycdp/ferrum#customization)
|
40
|
+
* [Navigation](https://github.com/rubycdp/ferrum#navigation)
|
41
|
+
* [Finders](https://github.com/rubycdp/ferrum#finders)
|
42
|
+
* [Screenshots](https://github.com/rubycdp/ferrum#screenshots)
|
43
|
+
* [Network](https://github.com/rubycdp/ferrum#network)
|
44
|
+
* [Mouse](https://github.com/rubycdp/ferrum#mouse)
|
45
|
+
* [Keyboard](https://github.com/rubycdp/ferrum#keyboard)
|
46
|
+
* [Cookies](https://github.com/rubycdp/ferrum#cookies)
|
47
|
+
* [Headers](https://github.com/rubycdp/ferrum#headers)
|
48
|
+
* [JavaScript](https://github.com/rubycdp/ferrum#javascript)
|
49
|
+
* [Frames](https://github.com/rubycdp/ferrum#frames)
|
50
|
+
* [Frame](https://github.com/rubycdp/ferrum#frame)
|
51
|
+
* [Dialog](https://github.com/rubycdp/ferrum#dialog)
|
52
|
+
* [Thread safety](https://github.com/rubycdp/ferrum#thread-safety)
|
53
|
+
* [Development](https://github.com/rubycdp/ferrum#development)
|
54
|
+
* [Contributing](https://github.com/rubycdp/ferrum#contributing)
|
55
|
+
* [License](https://github.com/rubycdp/ferrum#license)
|
14
56
|
|
15
|
-
Relation to [Cuprite](https://github.com/machinio/cuprite). Cuprite used to have
|
16
|
-
this code inside in one form or another but the thing is you don't need capybara
|
17
|
-
if you are going to crawl sites. You crawl, not test. Besides that clean
|
18
|
-
lightweight API to browser is what Ruby was missing, so here it comes.
|
19
|
-
|
20
|
-
If you like this project, please consider to [become a backer](https://www.patreon.com/rferrum) on Patreon.
|
21
57
|
|
22
58
|
## Install
|
23
59
|
|
24
60
|
There's no official Chrome or Chromium package for Linux don't install it this
|
25
|
-
way because it either
|
26
|
-
|
61
|
+
way because it's either outdated or unofficial, both are bad. Download it from
|
62
|
+
official [source](https://www.chromium.org/getting-involved/download-chromium).
|
27
63
|
Chrome binary should be in the `PATH` or `BROWSER_PATH` or you can pass it as an
|
28
|
-
option to browser instance `:browser_path
|
64
|
+
option to browser instance see `:browser_path` in
|
65
|
+
[Customization](https://github.com/rubycdp/ferrum#customization).
|
29
66
|
|
30
|
-
Add this to your Gemfile
|
67
|
+
Add this to your `Gemfile` and run `bundle install`.
|
31
68
|
|
32
69
|
``` ruby
|
33
70
|
gem "ferrum"
|
34
71
|
```
|
35
72
|
|
73
|
+
|
74
|
+
## Examples
|
75
|
+
|
36
76
|
Navigate to a website and save a screenshot:
|
37
77
|
|
38
78
|
```ruby
|
39
79
|
browser = Ferrum::Browser.new
|
40
|
-
browser.
|
80
|
+
browser.go_to("https://google.com")
|
41
81
|
browser.screenshot(path: "google.png")
|
42
82
|
browser.quit
|
43
83
|
```
|
@@ -46,10 +86,10 @@ Interact with a page:
|
|
46
86
|
|
47
87
|
```ruby
|
48
88
|
browser = Ferrum::Browser.new
|
49
|
-
browser.
|
50
|
-
input = browser.at_xpath("//
|
51
|
-
input.focus.type("Ruby headless driver for
|
52
|
-
browser.at_css("a > h3").text # => "
|
89
|
+
browser.go_to("https://google.com")
|
90
|
+
input = browser.at_xpath("//input[@name='q']")
|
91
|
+
input.focus.type("Ruby headless driver for Chrome", :Enter)
|
92
|
+
browser.at_css("a > h3").text # => "rubycdp/ferrum: Ruby Chrome/Chromium driver - GitHub"
|
53
93
|
browser.quit
|
54
94
|
```
|
55
95
|
|
@@ -57,7 +97,7 @@ Evaluate some JavaScript and get full width/height:
|
|
57
97
|
|
58
98
|
```ruby
|
59
99
|
browser = Ferrum::Browser.new
|
60
|
-
browser.
|
100
|
+
browser.go_to("https://www.google.com/search?q=Ruby+headless+driver+for+Capybara")
|
61
101
|
width, height = browser.evaluate <<~JS
|
62
102
|
[document.documentElement.offsetWidth,
|
63
103
|
document.documentElement.offsetHeight]
|
@@ -71,7 +111,7 @@ Do any mouse movements you like:
|
|
71
111
|
```ruby
|
72
112
|
# Trace a 100x100 square
|
73
113
|
browser = Ferrum::Browser.new
|
74
|
-
browser.
|
114
|
+
browser.go_to("https://google.com")
|
75
115
|
browser.mouse
|
76
116
|
.move(x: 0, y: 0)
|
77
117
|
.down
|
@@ -84,7 +124,17 @@ browser.mouse
|
|
84
124
|
browser.quit
|
85
125
|
```
|
86
126
|
|
87
|
-
|
127
|
+
|
128
|
+
## Docker
|
129
|
+
|
130
|
+
In docker as root you must pass the no-sandbox browser option:
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
Ferrum::Browser.new(browser_options: { 'no-sandbox': nil })
|
134
|
+
```
|
135
|
+
|
136
|
+
|
137
|
+
## Customization
|
88
138
|
|
89
139
|
You can customize options with the following code in your test setup:
|
90
140
|
|
@@ -93,36 +143,48 @@ Ferrum::Browser.new(options)
|
|
93
143
|
```
|
94
144
|
|
95
145
|
* options `Hash`
|
96
|
-
* `:browser_path` (String) - Path to chrome binary, you can also set ENV
|
97
|
-
variable as `BROWSER_PATH=some/path/chrome bundle exec rspec`.
|
98
146
|
* `:headless` (Boolean) - Set browser as headless or not, `true` by default.
|
99
|
-
* `:
|
100
|
-
|
147
|
+
* `:xvfb` (Boolean) - Run browser in a virtual framebuffer, `false` by default.
|
148
|
+
* `:window_size` (Array) - The dimensions of the browser window in which to
|
149
|
+
test, expressed as a 2-element array, e.g. [1024, 768]. Default: [1024, 768]
|
150
|
+
* `:extensions` (Array[String | Hash]) - An array of paths to files or JS
|
151
|
+
source code to be preloaded into the browser e.g.:
|
152
|
+
`["/path/to/script.js", { source: "window.secret = 'top'" }]`
|
101
153
|
* `:logger` (Object responding to `puts`) - When present, debug output is
|
102
154
|
written to this object.
|
155
|
+
* `:slowmo` (Integer | Float) - Set a delay in seconds to wait before sending command.
|
156
|
+
Usefull companion of headless option, so that you have time to see changes.
|
103
157
|
* `:timeout` (Numeric) - The number of seconds we'll wait for a response when
|
104
158
|
communicating with browser. Default is 5.
|
105
159
|
* `:js_errors` (Boolean) - When true, JavaScript errors get re-raised in Ruby.
|
106
|
-
* `:
|
107
|
-
|
160
|
+
* `:pending_connection_errors` (Boolean) - When main frame is still waiting for slow responses while timeout is
|
161
|
+
reached `PendingConnectionsError` is raised. It's better to figure out why you have slow responses and fix or
|
162
|
+
block them rather than turn this setting off. Default is true.
|
163
|
+
* `:browser_name` (Symbol) - `:chrome` by default, only experimental support
|
164
|
+
for `:firefox` for now.
|
165
|
+
* `:browser_path` (String) - Path to Chrome binary, you can also set ENV
|
166
|
+
variable as `BROWSER_PATH=some/path/chrome bundle exec rspec`.
|
108
167
|
* `:browser_options` (Hash) - Additional command line options,
|
109
168
|
[see them all](https://peter.sh/experiments/chromium-command-line-switches/)
|
110
169
|
e.g. `{ "ignore-certificate-errors" => nil }`
|
111
|
-
* `:
|
170
|
+
* `:ignore_default_browser_options` (Boolean) - Ferrum has a number of default
|
171
|
+
options it passes to the browser, if you set this to `true` then only
|
172
|
+
options you put in `:browser_options` will be passed to the browser,
|
173
|
+
except required ones of course.
|
112
174
|
* `:port` (Integer) - Remote debugging port for headless Chrome
|
113
175
|
* `:host` (String) - Remote debugging address for headless Chrome
|
114
176
|
* `:url` (String) - URL for a running instance of Chrome. If this is set, a
|
115
177
|
browser process will not be spawned.
|
116
178
|
* `:process_timeout` (Integer) - How long to wait for the Chrome process to
|
117
179
|
respond on startup
|
118
|
-
|
119
|
-
|
120
|
-
|
180
|
+
* `:ws_max_receive_size` (Integer) - How big messages to accept from Chrome
|
181
|
+
over the web socket, in bytes. Defaults to 64MB. Incoming messages larger
|
182
|
+
than this will cause a `Ferrum::DeadBrowserError`.
|
121
183
|
|
122
184
|
|
123
185
|
## Navigation
|
124
186
|
|
125
|
-
####
|
187
|
+
#### go_to(url) : `String`
|
126
188
|
|
127
189
|
Navigate page to.
|
128
190
|
|
@@ -130,7 +192,7 @@ Navigate page to.
|
|
130
192
|
configuring driver.
|
131
193
|
|
132
194
|
```ruby
|
133
|
-
browser.
|
195
|
+
browser.go_to("https://github.com/")
|
134
196
|
```
|
135
197
|
|
136
198
|
#### back
|
@@ -138,7 +200,7 @@ browser.goto("https://github.com/")
|
|
138
200
|
Navigate to the previous page in history.
|
139
201
|
|
140
202
|
```ruby
|
141
|
-
browser.
|
203
|
+
browser.go_to("https://github.com/")
|
142
204
|
browser.at_xpath("//a").click
|
143
205
|
browser.back
|
144
206
|
```
|
@@ -148,7 +210,7 @@ browser.back
|
|
148
210
|
Navigate to the next page in history.
|
149
211
|
|
150
212
|
```ruby
|
151
|
-
browser.
|
213
|
+
browser.go_to("https://github.com/")
|
152
214
|
browser.at_xpath("//a").click
|
153
215
|
browser.back
|
154
216
|
browser.forward
|
@@ -159,10 +221,19 @@ browser.forward
|
|
159
221
|
Reload current page.
|
160
222
|
|
161
223
|
```ruby
|
162
|
-
browser.
|
224
|
+
browser.go_to("https://github.com/")
|
163
225
|
browser.refresh
|
164
226
|
```
|
165
227
|
|
228
|
+
#### stop
|
229
|
+
|
230
|
+
Stop all navigations and loading pending resources on the page
|
231
|
+
|
232
|
+
```ruby
|
233
|
+
browser.go_to("https://github.com/")
|
234
|
+
browser.stop
|
235
|
+
```
|
236
|
+
|
166
237
|
|
167
238
|
## Finders
|
168
239
|
|
@@ -176,7 +247,7 @@ provided node.
|
|
176
247
|
* :within `Node` | `nil`
|
177
248
|
|
178
249
|
```ruby
|
179
|
-
browser.
|
250
|
+
browser.go_to("https://github.com/")
|
180
251
|
browser.at_css("a[aria-label='Issues you created']") # => Node
|
181
252
|
```
|
182
253
|
|
@@ -191,7 +262,7 @@ document or provided node.
|
|
191
262
|
* :within `Node` | `nil`
|
192
263
|
|
193
264
|
```ruby
|
194
|
-
browser.
|
265
|
+
browser.go_to("https://github.com/")
|
195
266
|
browser.css("a[aria-label='Issues you created']") # => [Node]
|
196
267
|
```
|
197
268
|
|
@@ -204,7 +275,7 @@ Find node by xpath.
|
|
204
275
|
* :within `Node` | `nil`
|
205
276
|
|
206
277
|
```ruby
|
207
|
-
browser.
|
278
|
+
browser.go_to("https://github.com/")
|
208
279
|
browser.at_xpath("//a[@aria-label='Issues you created']") # => Node
|
209
280
|
```
|
210
281
|
|
@@ -217,7 +288,7 @@ Find nodes by xpath.
|
|
217
288
|
* :within `Node` | `nil`
|
218
289
|
|
219
290
|
```ruby
|
220
|
-
browser.
|
291
|
+
browser.go_to("https://github.com/")
|
221
292
|
browser.xpath("//a[@aria-label='Issues you created']") # => [Node]
|
222
293
|
```
|
223
294
|
|
@@ -226,7 +297,7 @@ browser.xpath("//a[@aria-label='Issues you created']") # => [Node]
|
|
226
297
|
Returns current top window location href.
|
227
298
|
|
228
299
|
```ruby
|
229
|
-
browser.
|
300
|
+
browser.go_to("https://google.com/")
|
230
301
|
browser.current_url # => "https://www.google.com/"
|
231
302
|
```
|
232
303
|
|
@@ -235,7 +306,7 @@ browser.current_url # => "https://www.google.com/"
|
|
235
306
|
Returns current top window title
|
236
307
|
|
237
308
|
```ruby
|
238
|
-
browser.
|
309
|
+
browser.go_to("https://google.com/")
|
239
310
|
browser.current_title # => "Google"
|
240
311
|
```
|
241
312
|
|
@@ -244,7 +315,7 @@ browser.current_title # => "Google"
|
|
244
315
|
Returns current page's html.
|
245
316
|
|
246
317
|
```ruby
|
247
|
-
browser.
|
318
|
+
browser.go_to("https://google.com/")
|
248
319
|
browser.body # => '<html itemscope="" itemtype="http://schema.org/WebPage" lang="ru"><head>...
|
249
320
|
```
|
250
321
|
|
@@ -265,18 +336,21 @@ Saves screenshot on a disk or returns it as base64.
|
|
265
336
|
* :full `Boolean` whether you need full page screenshot or a viewport
|
266
337
|
* :selector `String` css selector for given element
|
267
338
|
* :scale `Float` zoom in/out
|
339
|
+
* :background_color `Ferrum::RGBA.new(0, 0, 0, 0.0)` to have specific background color
|
268
340
|
|
269
341
|
```ruby
|
270
|
-
browser.
|
342
|
+
browser.go_to("https://google.com/")
|
271
343
|
# Save on the disk in PNG
|
272
344
|
browser.screenshot(path: "google.png") # => 134660
|
273
345
|
# Save on the disk in JPG
|
274
346
|
browser.screenshot(path: "google.jpg") # => 30902
|
275
347
|
# Save to Base64 the whole page not only viewport and reduce quality
|
276
348
|
browser.screenshot(full: true, quality: 60) # "iVBORw0KGgoAAAANSUhEUgAABAAAAAMACAYAAAC6uhUNAAAAAXNSR0IArs4c6Q...
|
349
|
+
# Save with specific background color
|
350
|
+
browser.screenshot(background_color: Ferrum::RGBA.new(0, 0, 0, 0.0))
|
277
351
|
```
|
278
352
|
|
279
|
-
#### pdf(\*\*options) : `String` | `
|
353
|
+
#### pdf(\*\*options) : `String` | `Boolean`
|
280
354
|
|
281
355
|
Saves PDF on a disk or returns it as base64.
|
282
356
|
|
@@ -294,9 +368,21 @@ Saves PDF on a disk or returns it as base64.
|
|
294
368
|
* See other [native options](https://chromedevtools.github.io/devtools-protocol/tot/Page#method-printToPDF) you can pass
|
295
369
|
|
296
370
|
```ruby
|
297
|
-
browser.
|
371
|
+
browser.go_to("https://google.com/")
|
298
372
|
# Save to disk as a PDF
|
299
|
-
browser.pdf(path: "google.pdf", paper_width: 1.0, paper_height: 1.0) # =>
|
373
|
+
browser.pdf(path: "google.pdf", paper_width: 1.0, paper_height: 1.0) # => true
|
374
|
+
```
|
375
|
+
|
376
|
+
#### mhtml(\*\*options) : `String` | `Integer`
|
377
|
+
|
378
|
+
Saves MHTML on a disk or returns it as a string.
|
379
|
+
|
380
|
+
* options `Hash`
|
381
|
+
* :path `String` to save a file on the disk.
|
382
|
+
|
383
|
+
```ruby
|
384
|
+
browser.go_to("https://google.com/")
|
385
|
+
browser.mhtml(path: "google.mhtml") # => 87742
|
300
386
|
```
|
301
387
|
|
302
388
|
|
@@ -310,7 +396,7 @@ Returns all information about network traffic as `Network::Exchange` instance
|
|
310
396
|
which in general is a wrapper around `request`, `response` and `error`.
|
311
397
|
|
312
398
|
```ruby
|
313
|
-
browser.
|
399
|
+
browser.go_to("https://github.com/")
|
314
400
|
browser.network.traffic # => [#<Ferrum::Network::Exchange, ...]
|
315
401
|
```
|
316
402
|
|
@@ -319,7 +405,7 @@ browser.network.traffic # => [#<Ferrum::Network::Exchange, ...]
|
|
319
405
|
Page request of the main frame.
|
320
406
|
|
321
407
|
```ruby
|
322
|
-
browser.
|
408
|
+
browser.go_to("https://github.com/")
|
323
409
|
browser.network.request # => #<Ferrum::Network::Request...
|
324
410
|
```
|
325
411
|
|
@@ -328,7 +414,7 @@ browser.network.request # => #<Ferrum::Network::Request...
|
|
328
414
|
Page response of the main frame.
|
329
415
|
|
330
416
|
```ruby
|
331
|
-
browser.
|
417
|
+
browser.go_to("https://github.com/")
|
332
418
|
browser.network.response # => #<Ferrum::Network::Response...
|
333
419
|
```
|
334
420
|
|
@@ -338,10 +424,28 @@ Contains the status code of the main page response (e.g., 200 for a
|
|
338
424
|
success). This is just a shortcut for `response.status`.
|
339
425
|
|
340
426
|
```ruby
|
341
|
-
browser.
|
427
|
+
browser.go_to("https://github.com/")
|
342
428
|
browser.network.status # => 200
|
343
429
|
```
|
344
430
|
|
431
|
+
#### wait_for_idle(\*\*options)
|
432
|
+
|
433
|
+
Waits for network idle or raises `Ferrum::TimeoutError` error
|
434
|
+
|
435
|
+
* options `Hash`
|
436
|
+
* :connections `Integer` how many connections are allowed for network to be
|
437
|
+
idling, `0` by default
|
438
|
+
* :duration `Float` sleep for given amount of time and check again, `0.05` by
|
439
|
+
default
|
440
|
+
* :timeout `Float` during what time we try to check idle, `browser.timeout`
|
441
|
+
by default
|
442
|
+
|
443
|
+
```ruby
|
444
|
+
browser.go_to("https://example.com/")
|
445
|
+
browser.at_xpath("//a[text() = 'No UI changes button']").click
|
446
|
+
browser.network.wait_for_idle
|
447
|
+
```
|
448
|
+
|
345
449
|
#### clear(type)
|
346
450
|
|
347
451
|
Clear browser's cache or collected traffic.
|
@@ -350,7 +454,7 @@ Clear browser's cache or collected traffic.
|
|
350
454
|
|
351
455
|
```ruby
|
352
456
|
traffic = browser.network.traffic # => []
|
353
|
-
browser.
|
457
|
+
browser.go_to("https://github.com/")
|
354
458
|
traffic.size # => 51
|
355
459
|
browser.network.clear(:traffic)
|
356
460
|
traffic.size # => 0
|
@@ -378,25 +482,52 @@ browser.on(:request) do |request|
|
|
378
482
|
request.continue
|
379
483
|
end
|
380
484
|
end
|
381
|
-
browser.
|
485
|
+
browser.go_to("https://google.com")
|
382
486
|
```
|
383
487
|
|
384
|
-
#### authorize(\*\*options)
|
488
|
+
#### authorize(\*\*options, &block)
|
385
489
|
|
386
|
-
If site uses authorization you can provide credentials using this method.
|
490
|
+
If site or proxy uses authorization you can provide credentials using this method.
|
387
491
|
|
388
492
|
* options `Hash`
|
389
493
|
* :type `Symbol` `:server` | `:proxy` site or proxy authorization
|
390
494
|
* :user `String`
|
391
495
|
* :password `String`
|
496
|
+
* &block accepts authenticated request, which you must subsequently allow or deny, if you don't
|
497
|
+
care about unwanted requests just call `request.continue`.
|
392
498
|
|
393
499
|
```ruby
|
394
|
-
browser.network.authorize(user: "login", password: "pass")
|
395
|
-
browser.
|
500
|
+
browser.network.authorize(user: "login", password: "pass") { |req| req.continue }
|
501
|
+
browser.go_to("http://example.com/authenticated")
|
396
502
|
puts browser.network.status # => 200
|
397
503
|
puts browser.body # => Welcome, authenticated client
|
398
504
|
```
|
399
505
|
|
506
|
+
Since Chrome implements authorize using request interception you must continue or abort authorized requests. If you
|
507
|
+
already have code that uses interception you can use `authorize` without block, but if not you are obliged to pass
|
508
|
+
block, so this is version doesn't pass block and can work just fine:
|
509
|
+
|
510
|
+
```ruby
|
511
|
+
browser = Ferrum::Browser.new
|
512
|
+
browser.network.intercept
|
513
|
+
browser.on(:request) do |request|
|
514
|
+
if request.resource_type == "Image"
|
515
|
+
request.abort
|
516
|
+
else
|
517
|
+
request.continue
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
browser.network.authorize(user: "login", password: "pass", type: :proxy)
|
522
|
+
|
523
|
+
browser.go_to("https://google.com")
|
524
|
+
|
525
|
+
```
|
526
|
+
|
527
|
+
You used to call `authorize` method without block, but since it's implemented using request interception there could be
|
528
|
+
a collision with another part of your code that also uses request interception, so that authorize allows the request
|
529
|
+
while your code denies but it's too late. The block is mandatory now.
|
530
|
+
|
400
531
|
|
401
532
|
### Mouse
|
402
533
|
|
@@ -412,7 +543,7 @@ Scroll page to a given x, y
|
|
412
543
|
displayed in the upper left
|
413
544
|
|
414
545
|
```ruby
|
415
|
-
browser.
|
546
|
+
browser.go_to("https://www.google.com/search?q=Ruby+headless+driver+for+Capybara")
|
416
547
|
browser.mouse.scroll_to(0, 400)
|
417
548
|
```
|
418
549
|
|
@@ -517,6 +648,8 @@ Sets given values as cookie
|
|
517
648
|
* :value `String`
|
518
649
|
* :domain `String`
|
519
650
|
* :expires `Integer`
|
651
|
+
* :samesite `String`
|
652
|
+
* :httponly `Boolean`
|
520
653
|
|
521
654
|
```ruby
|
522
655
|
browser.cookies.set(name: "stealth", value: "omg", domain: "google.com") # => true
|
@@ -628,22 +761,163 @@ browser.add_script_tag(url: "http://example.com/stylesheet.css") # => true
|
|
628
761
|
|
629
762
|
```ruby
|
630
763
|
browser.add_style_tag(content: "h1 { font-size: 40px; }") # => true
|
764
|
+
|
765
|
+
```
|
766
|
+
#### bypass_csp(enabled) : `Boolean`
|
767
|
+
|
768
|
+
* enabled `Boolean`, `true` by default
|
769
|
+
|
770
|
+
```ruby
|
771
|
+
browser.bypass_csp # => true
|
772
|
+
browser.go_to("https://github.com/ruby-concurrency/concurrent-ruby/blob/master/docs-source/promises.in.md")
|
773
|
+
browser.refresh
|
774
|
+
browser.add_script_tag(content: "window.__injected = 42")
|
775
|
+
browser.evaluate("window.__injected") # => 42
|
631
776
|
```
|
632
777
|
|
633
778
|
|
634
779
|
## Frames
|
635
780
|
|
636
|
-
#### frames
|
637
|
-
#### main_frame
|
638
|
-
#### frame_by
|
781
|
+
#### frames : `Array[Frame] | []`
|
639
782
|
|
640
|
-
|
783
|
+
Returns all the frames current page have.
|
641
784
|
|
642
785
|
```ruby
|
643
|
-
browser.
|
786
|
+
browser.go_to("https://www.w3schools.com/tags/tag_frame.asp")
|
787
|
+
browser.frames # =>
|
788
|
+
# [
|
789
|
+
# #<Ferrum::Frame @id="C6D104CE454A025FBCF22B98DE612B12" @parent_id=nil @name=nil @state=:stopped_loading @execution_id=1>,
|
790
|
+
# #<Ferrum::Frame @id="C09C4E4404314AAEAE85928EAC109A93" @parent_id="C6D104CE454A025FBCF22B98DE612B12" @state=:stopped_loading @execution_id=2>,
|
791
|
+
# #<Ferrum::Frame @id="2E9C7F476ED09D87A42F2FEE3C6FBC3C" @parent_id="C6D104CE454A025FBCF22B98DE612B12" @state=:stopped_loading @execution_id=3>,
|
792
|
+
# ...
|
793
|
+
# ]
|
794
|
+
```
|
795
|
+
|
796
|
+
#### main_frame : `Frame`
|
797
|
+
|
798
|
+
Returns page's main frame, the top of the tree and the parent of all frames.
|
799
|
+
|
800
|
+
#### frame_by(\*\*options) : `Frame | nil`
|
801
|
+
|
802
|
+
Find frame by given options.
|
803
|
+
|
804
|
+
* options `Hash`
|
805
|
+
* :id `String` - Unique frame's id that browser provides
|
806
|
+
* :name `String` - Frame's name if there's one
|
807
|
+
|
808
|
+
```ruby
|
809
|
+
browser.frame_by(id: "C6D104CE454A025FBCF22B98DE612B12")
|
810
|
+
```
|
811
|
+
|
812
|
+
|
813
|
+
## Frame
|
814
|
+
|
815
|
+
#### id : `String`
|
816
|
+
|
817
|
+
Frame's unique id.
|
818
|
+
|
819
|
+
#### parent_id : `String | nil`
|
820
|
+
|
821
|
+
Parent frame id if this one is nested in another one.
|
822
|
+
|
823
|
+
#### execution_id : `Integer`
|
824
|
+
|
825
|
+
Execution context id which is used by JS, each frame has it's own context in
|
826
|
+
which JS evaluates.
|
827
|
+
|
828
|
+
#### name : `String | nil`
|
829
|
+
|
830
|
+
If frame was given a name it should be here.
|
831
|
+
|
832
|
+
#### state : `Symbol | nil`
|
833
|
+
|
834
|
+
One of the states frame's in:
|
835
|
+
|
836
|
+
* `:started_loading`
|
837
|
+
* `:navigated`
|
838
|
+
* `:stopped_loading`
|
839
|
+
|
840
|
+
#### url : `String`
|
841
|
+
|
842
|
+
Returns current frame's location href.
|
843
|
+
|
844
|
+
```ruby
|
845
|
+
browser.go_to("https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe")
|
846
|
+
frame = browser.frames[1]
|
847
|
+
frame.url # => https://interactive-examples.mdn.mozilla.net/pages/tabbed/iframe.html
|
848
|
+
```
|
849
|
+
|
850
|
+
#### title
|
851
|
+
|
852
|
+
Returns current frame's title.
|
853
|
+
|
854
|
+
```ruby
|
855
|
+
browser.go_to("https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe")
|
856
|
+
frame = browser.frames[1]
|
857
|
+
frame.title # => HTML Demo: <iframe>
|
858
|
+
```
|
859
|
+
|
860
|
+
#### main? : `Boolean`
|
861
|
+
|
862
|
+
If current frame is the main frame of the page (top of the tree).
|
863
|
+
|
864
|
+
```ruby
|
865
|
+
browser.go_to("https://www.w3schools.com/tags/tag_frame.asp")
|
866
|
+
frame = browser.frame_by(id: "C09C4E4404314AAEAE85928EAC109A93")
|
867
|
+
frame.main? # => false
|
868
|
+
```
|
869
|
+
|
870
|
+
#### current_url : `String`
|
871
|
+
|
872
|
+
Returns current frame's top window location href.
|
873
|
+
|
874
|
+
```ruby
|
875
|
+
browser.go_to("https://www.w3schools.com/tags/tag_frame.asp")
|
876
|
+
frame = browser.frame_by(id: "C09C4E4404314AAEAE85928EAC109A93")
|
877
|
+
frame.current_url # => "https://www.w3schools.com/tags/tag_frame.asp"
|
878
|
+
```
|
879
|
+
|
880
|
+
#### current_title : `String`
|
881
|
+
|
882
|
+
Returns current frame's top window title.
|
883
|
+
|
884
|
+
```ruby
|
885
|
+
browser.go_to("https://www.w3schools.com/tags/tag_frame.asp")
|
886
|
+
frame = browser.frame_by(id: "C09C4E4404314AAEAE85928EAC109A93")
|
887
|
+
frame.current_title # => "HTML frame tag"
|
888
|
+
```
|
889
|
+
|
890
|
+
#### body : `String`
|
891
|
+
|
892
|
+
Returns current frame's html.
|
893
|
+
|
894
|
+
```ruby
|
895
|
+
browser.go_to("https://www.w3schools.com/tags/tag_frame.asp")
|
896
|
+
frame = browser.frame_by(id: "C09C4E4404314AAEAE85928EAC109A93")
|
897
|
+
frame.body # => "<html><head></head><body></body></html>"
|
898
|
+
```
|
899
|
+
|
900
|
+
#### doctype
|
901
|
+
|
902
|
+
Returns current frame's doctype.
|
903
|
+
|
904
|
+
```ruby
|
905
|
+
browser.go_to("https://www.w3schools.com/tags/tag_frame.asp")
|
906
|
+
browser.main_frame.doctype # => "<!DOCTYPE html>"
|
907
|
+
```
|
908
|
+
|
909
|
+
#### set_content(html)
|
910
|
+
|
911
|
+
Sets a content of a given frame.
|
912
|
+
|
913
|
+
* html `String`
|
914
|
+
|
915
|
+
```ruby
|
916
|
+
browser.go_to("https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe")
|
644
917
|
frame = browser.frames[1]
|
645
|
-
|
646
|
-
|
918
|
+
frame.body # <html lang="en"><head><style>body {transition: opacity ease-in 0.2s; }...
|
919
|
+
frame.set_content("<html><head></head><body><p>lol</p></body></html>")
|
920
|
+
frame.body # => <html><head></head><body><p>lol</p></body></html>
|
647
921
|
```
|
648
922
|
|
649
923
|
|
@@ -668,7 +942,7 @@ browser.on(:dialog) do |dialog|
|
|
668
942
|
dialog.dismiss
|
669
943
|
end
|
670
944
|
end
|
671
|
-
browser.
|
945
|
+
browser.go_to("https://google.com")
|
672
946
|
```
|
673
947
|
|
674
948
|
|
@@ -685,13 +959,13 @@ context = browser.contexts.create
|
|
685
959
|
|
686
960
|
t1 = Thread.new(context) do |c|
|
687
961
|
page = c.create_page
|
688
|
-
page.
|
962
|
+
page.go_to("https://www.google.com/search?q=Ruby+headless+driver+for+Capybara")
|
689
963
|
page.screenshot(path: "t1.png")
|
690
964
|
end
|
691
965
|
|
692
966
|
t2 = Thread.new(context) do |c|
|
693
967
|
page = c.create_page
|
694
|
-
page.
|
968
|
+
page.go_to("https://www.google.com/search?q=Ruby+static+typing")
|
695
969
|
page.screenshot(path: "t2.png")
|
696
970
|
end
|
697
971
|
|
@@ -710,7 +984,7 @@ browser = Ferrum::Browser.new
|
|
710
984
|
t1 = Thread.new(browser) do |b|
|
711
985
|
context = b.contexts.create
|
712
986
|
page = context.create_page
|
713
|
-
page.
|
987
|
+
page.go_to("https://www.google.com/search?q=Ruby+headless+driver+for+Capybara")
|
714
988
|
page.screenshot(path: "t1.png")
|
715
989
|
context.dispose
|
716
990
|
end
|
@@ -718,7 +992,7 @@ end
|
|
718
992
|
t2 = Thread.new(browser) do |b|
|
719
993
|
context = b.contexts.create
|
720
994
|
page = context.create_page
|
721
|
-
page.
|
995
|
+
page.go_to("https://www.google.com/search?q=Ruby+static+typing")
|
722
996
|
page.screenshot(path: "t2.png")
|
723
997
|
context.dispose
|
724
998
|
end
|
@@ -728,3 +1002,21 @@ t2.join
|
|
728
1002
|
|
729
1003
|
browser.quit
|
730
1004
|
```
|
1005
|
+
|
1006
|
+
## Development
|
1007
|
+
|
1008
|
+
After checking out the repo, run `bundle install` to install dependencies.
|
1009
|
+
|
1010
|
+
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.
|
1011
|
+
|
1012
|
+
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](https://rubygems.org).
|
1013
|
+
|
1014
|
+
|
1015
|
+
## Contributing
|
1016
|
+
|
1017
|
+
Bug reports and pull requests are welcome on [GitHub](https://github.com/rubycdp/ferrum).
|
1018
|
+
|
1019
|
+
## License
|
1020
|
+
|
1021
|
+
The gem is available as open source under the terms of the
|
1022
|
+
[MIT License](https://opensource.org/licenses/MIT).
|