cuprite 0.6.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 117f25092d57e0e61feeec4d161d3b9da6cdde4ea6963e624dca22af48de75fa
4
- data.tar.gz: aae39fa9139563a5870e66be1d564c69136df2e7d73c72ff744edf4bb5cccc8a
3
+ metadata.gz: 61e6eae56b712430083dd1dc25ae7888016899b9504fcb2795525fe2df0d9b63
4
+ data.tar.gz: 26f55efb799bff8ffa704fd1955dc01b03c11db3f44806f3a1c8284d84237ae8
5
5
  SHA512:
6
- metadata.gz: f77ad45ba3abe3a1a4dec994566cbf1d0ab0c7364fbc879bf89583e1602a5a5c16da3cc1d6e1d6b8c2b5eee601409d1c8cf31f291f01cc83217f495ae313e56a
7
- data.tar.gz: 2a976e8a5ece9441ba281b2521c43cf8ed209258bbb386254539341157dad7245933292458c7ccb7a1f019119b2334d15f3c68fb0d9af9df0061f74879d62cfb
6
+ metadata.gz: a8eb4c7caeeaf86f24ebef9bf37edf6bf3e6ccf474633be8c69a1b4296abfcb5df040ede0c5feccecc930393312408800a928f3671aeb658a7d5beadb08a3c0a
7
+ data.tar.gz: 44387ad8fa9b9a87a236d2df3d754155ad4ed280348d65da2479b1a5a08e6be215ebf2ad652c1159188f08f6f231fbeeeddd2ce767d5bc9a53b1c0e7f39fbdf1
data/README.md CHANGED
@@ -5,59 +5,97 @@
5
5
  Cuprite is a pure Ruby driver (read as _no_ Java/Selenium/WebDriver/ChromeDriver
6
6
  requirement) for [Capybara](https://github.com/teamcapybara/capybara). It allows
7
7
  you to run your Capybara tests on a headless [Chrome](https://www.google.com/chrome/)
8
- or [Chromium](https://www.chromium.org/) browser by [CDP protocol](https://chromedevtools.github.io/devtools-protocol/).
8
+ or [Chromium](https://www.chromium.org/) by [CDP protocol](https://chromedevtools.github.io/devtools-protocol/).
9
+ Under the hood it uses [Ferrum](https://github.com/route/ferrum) which is
10
+ high-level API to the browser again by CDP protocol.
9
11
 
10
12
  The emphasis was made on raw CDP protocol because Headless Chrome allows you to
11
- do so many cool things that are barely supported by WebDriver because it should
12
- have consistent design with other browsers. The design of the driver will be as
13
+ do so many things that are barely supported by WebDriver because it should have
14
+ consistent design with other browsers. The design of the driver will be as
13
15
  close to [Poltergeist](https://github.com/teampoltergeist/poltergeist) as
14
- possible but it's not a goal.
16
+ possible though it's not a goal.
15
17
 
16
- ## Installation ##
18
+ ## Install ##
19
+
20
+ Add these lines to your `Gemfile` and run `bundle install`.
17
21
 
18
22
  ``` ruby
19
- gem "cuprite"
23
+ gem "cuprite", group: :test
20
24
  ```
21
25
 
22
- and run `bundle install`.
23
-
24
26
  In your test setup add:
25
27
 
26
28
  ``` ruby
27
29
  require "capybara/cuprite"
28
30
  Capybara.javascript_driver = :cuprite
31
+ Capybara.register_driver(:cuprite) do |app|
32
+ Capybara::Cuprite::Driver.new(app, window_size: [1200, 800])
33
+ end
29
34
  ```
30
35
 
31
- ## Installing Chromium ##
36
+ If you already had tests on Poltergeist then it should simply work, for Selenium
37
+ you better check your code for `.manage` calls because things are much easier
38
+ with Cuprite, see the documentation below.
32
39
 
33
- As Chromium is stopped being built as a package for Linux don't even try to
34
- install it this way because it will either be outdated or unofficial package.
35
- Both are bad. Download it from official [source](https://www.chromium.org/getting-involved/download-chromium).
40
+ ## Install Chrome ##
36
41
 
37
- ## Supported features ##
42
+ There's no official Chrome or Chromium package for Linux don't install it this
43
+ way because it either will be outdated or unofficial, both are bad. Download it
44
+ from official [source](https://www.chromium.org/getting-involved/download-chromium).
45
+ Chrome binary should be in the `PATH` or `BROWSER_PATH` or you can pass it as an
46
+ option
38
47
 
39
- All the mandatory capybara features plus optional ones:
48
+ ## Customization ##
40
49
 
41
- * `page.evaluate_script` and `page.execute_script`
42
- * `page.within_frame`
43
- * `page.status_code`
44
- * `page.response_headers`
45
- * `page.save_screenshot`
46
- * `page.driver.render_base64(format, options)`
47
- * `page.driver.scroll_to(left, top)`
48
- * `page.driver.basic_authorize(user, password)`
49
- * `element.send_keys(*keys)`
50
- * `page.driver.set_proxy(ip, port, type, user, password)`
51
- * window API
52
- * cookie handling
50
+ You can customize options with the following code in your test setup:
51
+
52
+ ``` ruby
53
+ Capybara.register_driver(:cuprite) do |app|
54
+ Capybara::Cuprite::Driver.new(app, options)
55
+ end
56
+ ```
53
57
 
54
- ### Clicking coordinates ###
58
+ #### Running in Docker ####
55
59
 
56
- Sometimes its desirable to click a very specific area of the screen. You can
57
- accomplish this with `page.driver.click(x, y)`, where x and y are the screen
58
- coordinates.
60
+ In docker as root you must pass the no-sandbox browser option:
59
61
 
60
- ### Manipulating request headers ###
62
+ ```ruby
63
+ Capybara::Cuprite::Driver.new(app, browser_options: { 'no-sandbox': nil })
64
+ ```
65
+
66
+ * options `Hash`
67
+ * `:browser_path` (String) - Path to chrome binary, you can also set ENV
68
+ variable as `BROWSER_PATH=some/path/chrome bundle exec rspec`.
69
+ * `:headless` (Boolean) - Set browser as headless or not, `true` by default.
70
+ * `:slowmo` (Integer | Float) - Set a delay to wait before sending command.
71
+ Usefull companion of headless option, so that you have time to see changes.
72
+ * `:logger` (Object responding to `puts`) - When present, debug output is
73
+ written to this object.
74
+ * `:timeout` (Numeric) - The number of seconds we'll wait for a response when
75
+ communicating with browser. Default is 5.
76
+ * `:js_errors` (Boolean) - When true, JavaScript errors get re-raised in Ruby.
77
+ * `:window_size` (Array) - The dimensions of the browser window in which to
78
+ test, expressed as a 2-element array, e.g. [1024, 768]. Default: [1024, 768]
79
+ * `:browser_options` (Hash) - Additional command line options,
80
+ [see them all](https://peter.sh/experiments/chromium-command-line-switches/)
81
+ e.g. `{ "ignore-certificate-errors" => nil }`
82
+ * `:extensions` (Array) - An array of JS files to be preloaded into the browser
83
+ * `:port` (Integer) - Remote debugging port for headless Chrome
84
+ * `:host` (String) - Remote debugging address for headless Chrome
85
+ * `:url` (String) - URL for a running instance of Chrome. If this is set, a
86
+ browser process will not be spawned.
87
+ * `:url_blacklist` (Array) - array of strings to match against requested URLs
88
+ * `:url_whitelist` (Array) - array of strings to match against requested URLs
89
+ * `:process_timeout` (Integer) - How long to wait for the Chrome process to
90
+ respond on startup
91
+
92
+ ### Clicking/Scrolling ###
93
+
94
+ * `page.driver.click(x, y)` Click a very specific area of the screen.
95
+ * `page.driver.scroll_to(left, top)` Scroll to given position.
96
+ * `element.send_keys(*keys)` Send keys to given node.
97
+
98
+ ### Request headers ###
61
99
 
62
100
  Manipulate HTTP request headers like a boss:
63
101
 
@@ -73,12 +111,17 @@ Notice that `headers=` will overwrite already set headers. You should use
73
111
  subsequent HTTP requests (including requests for assets, AJAX, etc). They will
74
112
  be automatically cleared at the end of the test.
75
113
 
76
- ### Inspecting network traffic ###
114
+ ### Network traffic ###
115
+
116
+ * `page.driver.network_traffic` Inspect network traffic (resources have been
117
+ loaded) on the current page. This returns an array of request objects.
77
118
 
78
- You can inspect the network traffic (i.e. what resources have been loaded) on
79
- the current page by calling `page.driver.network_traffic`. This returns an array
80
- of request objects. A request object has a `response` method containing data
81
- about the response.
119
+
120
+ ```ruby
121
+ page.driver.network_traffic # => [Request, ...]
122
+ request = page.driver.network_traffic.first
123
+ request.response
124
+ ```
82
125
 
83
126
  Please note that network traffic is not cleared when you visit new page. You can
84
127
  manually clear the network traffic by calling `page.driver.clear_network_traffic`
@@ -99,41 +142,16 @@ The following methods are used to inspect and manipulate cookies:
99
142
  * `page.driver.remove_cookie(name)` - remove a cookie
100
143
  * `page.driver.clear_cookies` - clear all cookies
101
144
 
102
- ## Customization ##
145
+ ### Screenshot ###
103
146
 
104
- You can customize the way that Capybara sets up Cuprite via the following code
105
- in your test setup:
147
+ Besides capybara screenshot method you can get image as Base64:
106
148
 
107
- ``` ruby
108
- Capybara.register_driver :cuprite do |app|
109
- Capybara::Cuprite::Driver.new(app, options)
110
- end
111
- ```
149
+ * `page.driver.render_base64(format, options)`
112
150
 
113
- `options` is a hash of options. The following options are supported:
114
-
115
- * `:browser_path` (String) - Path to chrome binary, you can also set ENV
116
- variable as `BROWSER_PATH=some/path/chrome bundle exec rspec`.
117
- * `:headless` (Boolean) - Set browser as headless or not, `true` by default.
118
- * `:slowmo` (Integer | Float) - Set a delay to wait before sending command.
119
- Usefull companion of headless option, so that you have time to see changes.
120
- * `:logger` (Object responding to `puts`) - When present, debug output is
121
- written to this object.
122
- * `:timeout` (Numeric) - The number of seconds we'll wait for a response when
123
- communicating with browser. Default is 30.
124
- * `:js_errors` (Boolean) - When true, JavaScript errors get re-raised in Ruby.
125
- * `:window_size` (Array) - The dimensions of the browser window in which to
126
- test, expressed as a 2-element array, e.g. [1024, 768]. Default: [1024, 768]
127
- * `:browser_options` (Hash) - Additional command line options,
128
- [see them all](https://peter.sh/experiments/chromium-command-line-switches/)
129
- e.g. `{ "ignore-certificate-errors" => nil }`
130
- * `:extensions` (Array) - An array of JS files to be preloaded into the browser
131
- * `:port` (Integer) - Remote debugging port for headless Chrome
132
- * `:host` (String) - Remote debugging address for headless Chrome
133
- * `:url_blacklist` (Array) - array of strings to match against requested URLs
134
- * `:url_whitelist` (Array) - array of strings to match against requested URLs
135
- * `:process_timeout` (Integer) - How long to wait for the Chrome process to respond on startup
151
+ ### Authorization ###
136
152
 
153
+ * `page.driver.basic_authorize(user, password)`
154
+ * `page.driver.set_proxy(ip, port, type, user, password)`
137
155
 
138
156
  ### URL Blacklisting & Whitelisting ###
139
157
  Cuprite supports URL blacklisting, which allows you to prevent scripts from
@@ -154,6 +172,21 @@ If you are experiencing slower run times, consider creating a URL whitelist of
154
172
  domains that are essential or a blacklist of domains that are not essential,
155
173
  such as ad networks or analytics, to your testing environment.
156
174
 
175
+ ### Remote debugging ###
176
+
177
+ If you use the `inspector: true` option, remote debugging will be enabled. When
178
+ this option is enabled, you can insert `page.driver.debug` into your tests to
179
+ pause the test and launch a browser which gives you the Chrome inspector to view
180
+ all your open pages and inspect them.
181
+
182
+ You could set the inspector option via an environment variable:
183
+
184
+ ```ruby
185
+ Capybara.register_driver :cuprite do |app|
186
+ Capybara::Cuprite::Driver.new(app, inspector: ENV['INSPECTOR'])
187
+ end
188
+ ```
189
+
157
190
  ## License ##
158
191
 
159
192
  Copyright 2018-2019 Machinio
@@ -1,301 +1,143 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "base64"
4
3
  require "forwardable"
5
- require "capybara/cuprite/browser/targets"
6
- require "capybara/cuprite/browser/process"
7
- require "capybara/cuprite/browser/client"
8
- require "capybara/cuprite/browser/page"
9
4
 
10
5
  module Capybara::Cuprite
11
- class Browser
12
- TIMEOUT = 5
13
- WINDOW_SIZE = [1024, 768].freeze
14
- EXTENSIONS = [
15
- File.expand_path("browser/javascripts/index.js", __dir__)
16
- ].freeze
17
-
6
+ class Browser < Ferrum::Browser
18
7
  extend Forwardable
19
8
 
20
- attr_reader :headers, :window_size
21
-
22
- def self.start(*args)
23
- new(*args)
24
- end
9
+ delegate %i[find_or_create_page] => :targets
10
+ delegate %i[send_keys select set hover trigger before_click switch_to_frame
11
+ find_modal accept_confirm dismiss_confirm accept_prompt
12
+ dismiss_prompt reset_modals] => :page
25
13
 
26
- delegate subscribe: :@client
27
- delegate %i(window_handle window_handles switch_to_window open_new_window
28
- close_window within_window page) => :targets
29
- delegate %i(visit status_code body all_text property attributes attribute
30
- value visible? disabled? network_traffic clear_network_traffic
31
- path response_headers refresh click right_click double_click
32
- hover set click_coordinates drag drag_by select trigger
33
- scroll_to send_keys evaluate evaluate_on evaluate_async execute
34
- frame_url frame_title switch_to_frame current_url title go_back
35
- go_forward find_modal accept_confirm dismiss_confirm
36
- accept_prompt dismiss_prompt reset_modals authorize
37
- proxy_authorize) => :page
38
-
39
- attr_reader :process, :logger, :js_errors, :slowmo,
40
- :url_blacklist, :url_whitelist
41
- attr_writer :timeout
14
+ attr_reader :url_blacklist, :url_whitelist
42
15
 
43
16
  def initialize(options = nil)
44
- # Doesn't work on MacOS, so we need to set it by CDP as well
45
17
  options ||= {}
46
- @window_size = options.fetch(:window_size, WINDOW_SIZE)
47
- @original_window_size = @window_size
48
-
49
- @options = Hash(options.merge(window_size: @window_size))
50
- @logger, @timeout = @options.values_at(:logger, :timeout)
51
- @js_errors = @options.fetch(:js_errors, false)
52
- @slowmo = @options[:slowmo]
53
-
54
- self.url_blacklist = @options[:url_blacklist]
55
- self.url_whitelist = @options[:url_whitelist]
56
-
57
- if ENV["CUPRITE_DEBUG"] && !@logger
58
- STDOUT.sync = true
59
- @logger = STDOUT
60
- @options[:logger] = @logger
61
- end
62
-
63
- @options.freeze
18
+ self.url_blacklist = options[:url_blacklist]
19
+ self.url_whitelist = options[:url_whitelist]
64
20
 
65
- start
21
+ super
66
22
  end
67
23
 
68
- def extensions
69
- @extensions ||= begin
70
- exts = @options.fetch(:extensions, [])
71
- (EXTENSIONS + exts).map { |p| File.read(p) }
72
- end
24
+ def url_whitelist=(patterns)
25
+ @url_whitelist = prepare_wildcards(patterns)
26
+ page.network.intercept if @client && !@url_whitelist.empty?
73
27
  end
74
28
 
75
- def timeout
76
- @timeout || TIMEOUT
29
+ def url_blacklist=(patterns)
30
+ @url_blacklist = prepare_wildcards(patterns)
31
+ page.network.intercept if @client && !@url_blacklist.empty?
77
32
  end
78
33
 
79
- def source
80
- raise NotImplementedError
34
+ def visit(*args)
35
+ goto(*args)
81
36
  end
82
37
 
83
- def parents(node)
84
- evaluate_on(node: node, expr: "_cuprite.parents(this)", by_value: false)
38
+ def status_code
39
+ network.status
85
40
  end
86
41
 
87
42
  def find(method, selector)
88
43
  find_all(method, selector)
89
44
  end
90
45
 
46
+ def property(node, name)
47
+ node.property(name)
48
+ end
49
+
91
50
  def find_within(node, method, selector)
92
- resolved = page.command("DOM.resolveNode", nodeId: node["nodeId"])
51
+ resolved = page.command("DOM.resolveNode", nodeId: node.node_id)
93
52
  object_id = resolved.dig("object", "objectId")
94
53
  find_all(method, selector, { "objectId" => object_id })
95
54
  end
96
55
 
97
- def visible_text(node)
98
- evaluate_on(node: node, expr: "_cuprite.visibleText(this)")
99
- end
100
-
101
- def delete_text(node)
102
- evaluate_on(node: node, expr: "_cuprite.deleteText(this)")
103
- end
104
-
105
- def select_file(node, value)
106
- page.command("DOM.setFileInputFiles", nodeId: node["nodeId"], files: Array(value))
107
- end
108
-
109
- def render(path, options = {})
110
- format = options.delete(:format)
111
- options = options.merge(path: path)
112
- bin = Base64.decode64(render_base64(format, options))
113
- File.open(path.to_s, "wb") { |f| f.write(bin) }
114
- end
115
-
116
- def render_base64(format, options = {})
117
- options = render_options(format, options)
118
-
119
- if options[:format].to_s == "pdf"
120
- options = {}
121
- options[:paperWidth] = @paper_size[:width].to_f if @paper_size
122
- options[:paperHeight] = @paper_size[:height].to_f if @paper_size
123
- options[:scale] = @zoom_factor if @zoom_factor
124
- page.command("Page.printToPDF", **options)
125
- else
126
- page.command("Page.captureScreenshot", **options)
127
- end.fetch("data")
128
- end
129
-
130
- def set_zoom_factor(zoom_factor)
131
- @zoom_factor = zoom_factor.to_f
132
- end
133
-
134
- def set_paper_size(size)
135
- @paper_size = size
136
- end
137
-
138
- def headers=(headers)
139
- @headers = {}
140
- add_headers(headers)
141
- end
142
-
143
- def add_headers(headers, permanent: true)
144
- if headers["Referer"]
145
- page.referrer = headers["Referer"]
146
- headers.delete("Referer") unless permanent
56
+ def within_window(locator = nil, &block)
57
+ if Capybara::VERSION.to_f < 3.0
58
+ target_id = window_handles.find do |target_id|
59
+ page = find_or_create_page(target_id)
60
+ locator == page.frame_name
61
+ end
62
+ locator = target_id if target_id
147
63
  end
148
64
 
149
- @headers.merge!(headers)
150
- user_agent = @headers["User-Agent"]
151
- accept_language = @headers["Accept-Language"]
152
-
153
- set_overrides(user_agent: user_agent, accept_language: accept_language)
154
- page.command("Network.setExtraHTTPHeaders", headers: @headers)
155
- end
156
-
157
- def add_header(header, permanent: true)
158
- add_headers(header, permanent: permanent)
159
- end
160
-
161
- def set_overrides(user_agent: nil, accept_language: nil, platform: nil)
162
- options = Hash.new
163
- options[:userAgent] = user_agent if user_agent
164
- options[:acceptLanguage] = accept_language if accept_language
165
- options[:platform] if platform
166
-
167
- page.command("Network.setUserAgentOverride", **options) if !options.empty?
65
+ targets.within_window(locator, &block)
168
66
  end
169
67
 
170
- def cookies
171
- cookies = page.command("Network.getAllCookies")["cookies"]
172
- cookies.map { |c| [c["name"], Cookie.new(c)] }.to_h
68
+ def browser_error
69
+ evaluate("_cuprite.browserError()")
173
70
  end
174
71
 
175
- def set_cookie(cookie)
176
- page.command("Network.setCookie", **cookie)
72
+ def source
73
+ raise NotImplementedError
177
74
  end
178
75
 
179
- def remove_cookie(options)
180
- page.command("Network.deleteCookies", **options)
76
+ def drag(node, other)
77
+ raise NotImplementedError
181
78
  end
182
79
 
183
- def clear_cookies
184
- page.command("Network.clearBrowserCookies")
80
+ def drag_by(node, x, y)
81
+ raise NotImplementedError
185
82
  end
186
83
 
187
- def url_whitelist=(wildcards)
188
- @url_whitelist = prepare_wildcards(wildcards)
189
- page.intercept_request("*") if @client && !@url_whitelist.empty?
84
+ def select_file(node, value)
85
+ node.select_file(value)
190
86
  end
191
87
 
192
- def url_blacklist=(wildcards)
193
- @url_blacklist = prepare_wildcards(wildcards)
194
- page.intercept_request("*") if @client && !@url_blacklist.empty?
88
+ def parents(node)
89
+ evaluate_on(node: node, expression: "_cuprite.parents(this)", by_value: false)
195
90
  end
196
91
 
197
- def clear_memory_cache
198
- page.command("Network.clearBrowserCache")
92
+ def visible_text(node)
93
+ evaluate_on(node: node, expression: "_cuprite.visibleText(this)")
199
94
  end
200
95
 
201
- def reset
202
- @headers = {}
203
- @zoom_factor = nil
204
- @window_size = @original_window_size
205
- targets.reset
96
+ def delete_text(node)
97
+ evaluate_on(node: node, expression: "_cuprite.deleteText(this)")
206
98
  end
207
99
 
208
- def restart
209
- quit
210
- start
100
+ def attributes(node)
101
+ value = evaluate_on(node: node, expression: "_cuprite.getAttributes(this)")
102
+ JSON.parse(value)
211
103
  end
212
104
 
213
- def quit
214
- @client.close
215
- @process.stop
216
- @client = @process = @targets = nil
105
+ def attribute(node, name)
106
+ evaluate_on(node: node, expression: %Q(_cuprite.getAttribute(this, "#{name}")))
217
107
  end
218
108
 
219
- def crash
220
- command("Browser.crash")
109
+ def value(node)
110
+ evaluate_on(node: node, expression: "_cuprite.value(this)")
221
111
  end
222
112
 
223
- def browser_error
224
- page.evaluate("_cuprite.browserError()")
113
+ def visible?(node)
114
+ evaluate_on(node: node, expression: "_cuprite.isVisible(this)")
225
115
  end
226
116
 
227
- def resize(**options)
228
- @window_size = [options[:width], options[:height]]
229
- page.resize(**options)
117
+ def disabled?(node)
118
+ evaluate_on(node: node, expression: "_cuprite.isDisabled(this)")
230
119
  end
231
120
 
232
- def command(*args)
233
- id = @client.command(*args)
234
- @client.wait(id: id)
235
- rescue DeadBrowser
236
- restart
237
- raise
121
+ def path(node)
122
+ evaluate_on(node: node, expression: "_cuprite.path(this)")
238
123
  end
239
124
 
240
- def targets
241
- @targets ||= Targets.new(self)
125
+ def all_text(node)
126
+ node.text
242
127
  end
243
128
 
244
129
  private
245
130
 
246
- def start
247
- @headers = {}
248
- @process = Process.start(@options)
249
- @client = Client.new(self, @process.ws_url, false)
250
- end
251
-
252
- def render_options(format, opts)
253
- options = {}
254
-
255
- format ||= File.extname(opts[:path]).delete(".") || "png"
256
- format = "jpeg" if format == "jpg"
257
- raise "Not supported format: #{format}. jpeg | png | pdf" if format !~ /jpeg|png|pdf/i
258
- options.merge!(format: format)
259
-
260
- options.merge!(quality: opts[:quality] ? opts[:quality] : 75) if format == "jpeg"
261
-
262
- warn "Ignoring :selector in #render since full: true was given at #{caller(1..1).first}" if !!opts[:full] && opts[:selector]
263
-
264
- if !!opts[:full]
265
- width, height = page.evaluate("[document.documentElement.offsetWidth, document.documentElement.offsetHeight]")
266
- options.merge!(clip: { x: 0, y: 0, width: width, height: height, scale: @zoom_factor || 1.0 }) if width > 0 && height > 0
267
- elsif opts[:selector]
268
- rect = page.evaluate("document.querySelector('#{opts[:selector]}').getBoundingClientRect()")
269
- options.merge!(clip: { x: rect["x"], y: rect["y"], width: rect["width"], height: rect["height"], scale: @zoom_factor || 1.0 })
270
- end
271
-
272
- if @zoom_factor
273
- if !options[:clip]
274
- width, height = page.evaluate("[document.documentElement.clientWidth, document.documentElement.clientHeight]")
275
- options[:clip] = { x: 0, y: 0, width: width, height: height }
276
- end
277
-
278
- options[:clip].merge!(scale: @zoom_factor)
279
- end
280
-
281
- options
282
- end
283
-
284
131
  def find_all(method, selector, within = nil)
285
132
  begin
286
- elements = if within
133
+ nodes = if within
287
134
  evaluate("_cuprite.find(arguments[0], arguments[1], arguments[2])", method, selector, within)
288
135
  else
289
136
  evaluate("_cuprite.find(arguments[0], arguments[1])", method, selector)
290
137
  end
291
138
 
292
- elements.map do |element|
293
- # nodeType: 3, nodeName: "#text" e.g.
294
- target_id, node = element.values_at("target_id", "node")
295
- next if node["nodeType"] != 1
296
- within ? node : [target_id, node]
297
- end.compact
298
- rescue JavaScriptError => e
139
+ nodes.map { |n| n.node? ? n : next }.compact
140
+ rescue Ferrum::JavaScriptError => e
299
141
  if e.class_name == "InvalidSelector"
300
142
  raise InvalidSelector.new(e.response, method, selector)
301
143
  end