cuprite 0.8 → 0.13

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: dcb46f5716c12a1a33acb66d1e7827897f1331447806f52379571226ac4dfe9c
4
- data.tar.gz: 8ca919e8ebdfef7a4a16613b0c0620084365d3231dbbe5b7d96bc18bf0f1cf9a
3
+ metadata.gz: e515ee98354d04170f435ea87fc5d9256a21f2ca7484a83b8078c76478bfeb73
4
+ data.tar.gz: 4a08d6b9957090cad3bb732199a9706bf0aa7e6dfe152cf41b1364d4f11a95d0
5
5
  SHA512:
6
- metadata.gz: a3451efdac94c66d55d8afb49ee5497a91c48d7b2b9b0e9728b49f9c44ad4fa7c5d780a81ec7a4650f9a35a2370846b7da5782e9c6867cd0774d3204509e085a
7
- data.tar.gz: 6e33306c0157a88640d5b546faddef248576208eca188fe4898ff898023717a2439a138dbdfe98670b6cae4b03a5b81cce2b5eaa93d73c2f266f6761bd72603d
6
+ metadata.gz: '049bfdaf42c6bd78fdc40fcd6cc059ef6b38b5b3e0a7e03e4ee871fcb8c072cd5ec6215f5b5640f2e289505d582ffe9c3b9e84f688af9afc4e48616bfa5f7473'
7
+ data.tar.gz: 9b6c78303752248b85c6ca1dc2068dcc57687d94c141e2b10c75a54e9a6a8e767de3a804d98fe788580be0ec36d1230006ec784eb4b675c48752efee61e2a1fd
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2018-2019 Machinio
3
+ Copyright (c) 2018-2021 Dmitry Vorotilin
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,26 +1,25 @@
1
- # Cuprite - Headless Chrome driver for Capybara #
1
+ # Cuprite - Headless Chrome driver for Capybara
2
2
 
3
- [![Build Status](https://travis-ci.org/machinio/cuprite.svg?branch=master)](https://travis-ci.org/machinio/cuprite)
3
+ Cuprite is a pure Ruby driver (read as _no_ Selenium/WebDriver/ChromeDriver
4
+ dependency) for [Capybara](https://github.com/teamcapybara/capybara). It allows
5
+ you to run Capybara tests on a headless Chrome or Chromium. Under the hood it
6
+ uses [Ferrum](https://github.com/rubycdp/ferrum#index) which is high-level API
7
+ to the browser by CDP protocol. The design of the driver is as close to
8
+ [Poltergeist](https://github.com/teampoltergeist/poltergeist) as possible though
9
+ it's not a goal.
4
10
 
5
- Cuprite is a pure Ruby driver (read as _no_ Java/Selenium/WebDriver/ChromeDriver
6
- requirement) for [Capybara](https://github.com/teamcapybara/capybara). It allows
7
- you to run your Capybara tests on a headless [Chrome](https://www.google.com/chrome/)
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.
11
+ [Cuprite](https://evrone.com/cuprite) website is designed & supported by [Evrone](https://evrone.com/)
12
+ What else we build [with Ruby](https://evrone.com/ruby)
11
13
 
12
- The emphasis was made on raw CDP protocol because Headless Chrome allows you to
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
15
- close to [Poltergeist](https://github.com/teampoltergeist/poltergeist) as
16
- possible though it's not a goal.
17
14
 
18
- ## Install ##
15
+ ## Install
19
16
 
20
- Add these lines to your `Gemfile` and run `bundle install`.
17
+ Add this to your `Gemfile` and run `bundle install`.
21
18
 
22
19
  ``` ruby
23
- gem "cuprite", group: :test
20
+ group :test do
21
+ gem "cuprite"
22
+ end
24
23
  ```
25
24
 
26
25
  In your test setup add:
@@ -33,21 +32,31 @@ Capybara.register_driver(:cuprite) do |app|
33
32
  end
34
33
  ```
35
34
 
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.
35
+ if you use `Docker` don't forget to pass `no-sandbox` option:
36
+
37
+ ```ruby
38
+ Capybara::Cuprite::Driver.new(app, browser_options: { 'no-sandbox': nil })
39
+ ```
40
+
41
+ Since Cuprite uses [Ferrum](https://github.com/rubycdp/ferrum#examples) there
42
+ are many useful methods you can call even using this driver:
43
+
44
+ ```ruby
45
+ browser = page.driver.browser
46
+ browser.mouse.move(x: 123, y: 456).down.up
47
+ ```
48
+
49
+ If you already have tests on Poltergeist then it should simply work, for
50
+ Selenium you better check your code for `manage` calls because it works
51
+ differently in Cuprite, see the documentation below.
39
52
 
40
- ## Install Chrome ##
41
53
 
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
54
+ ## Customization
47
55
 
48
- ## Customization ##
56
+ See the full list of options for
57
+ [Ferrum](https://github.com/rubycdp/ferrum#customization).
49
58
 
50
- You can customize options with the following code in your test setup:
59
+ You can pass options with the following code in your test setup:
51
60
 
52
61
  ``` ruby
53
62
  Capybara.register_driver(:cuprite) do |app|
@@ -55,47 +64,52 @@ Capybara.register_driver(:cuprite) do |app|
55
64
  end
56
65
  ```
57
66
 
58
- #### Running in Docker ####
67
+ `Cuprite`-specific options are:
68
+
69
+ * options `Hash`
70
+ * `:url_blacklist` (Array) - array of strings to match against requested URLs
71
+ * `:url_whitelist` (Array) - array of strings to match against requested URLs
59
72
 
60
- In docker as root you must pass the no-sandbox browser option:
73
+
74
+ ## Debugging
75
+
76
+ If you pass `inspector` option, remote debugging will be enabled if you run
77
+ tests with `INSPECTOR=true`. Then you can put `page.driver.debug` or
78
+ `page.driver.debug(binding)` in your test to pause it. This will launch the
79
+ browser where you can inspect the content.
61
80
 
62
81
  ```ruby
63
- Capybara::Cuprite::Driver.new(app, browser_options: { 'no-sandbox': nil })
82
+ Capybara.register_driver :cuprite do |app|
83
+ Capybara::Cuprite::Driver.new(app, inspector: ENV['INSPECTOR'])
84
+ end
64
85
  ```
65
86
 
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
87
+ then somewhere in the test:
88
+
89
+ ```ruby
90
+ it "does something useful" do
91
+ visit root_path
92
+
93
+ fill_in "field", with: "value"
94
+ page.driver.debug(binding)
95
+
96
+ expect(page).to have_content("value")
97
+ end
98
+ ```
99
+
100
+ In the middle of the execution Chrome will open a new tab where you can inspect
101
+ the content and also if you passed `binding` an `irb` or `pry` console will be
102
+ opened where you can further experiment with the test.
103
+
91
104
 
92
- ### Clicking/Scrolling ###
105
+ ## Clicking/Scrolling
93
106
 
94
107
  * `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.
108
+ * `page.driver.scroll_to(left, top)` Scroll to a given position.
109
+ * `element.send_keys(*keys)` Send keys to a given node.
97
110
 
98
- ### Request headers ###
111
+
112
+ ## Request headers
99
113
 
100
114
  Manipulate HTTP request headers like a boss:
101
115
 
@@ -111,11 +125,11 @@ Notice that `headers=` will overwrite already set headers. You should use
111
125
  subsequent HTTP requests (including requests for assets, AJAX, etc). They will
112
126
  be automatically cleared at the end of the test.
113
127
 
114
- ### Network traffic ###
115
128
 
116
- * `page.driver.network_traffic` Inspect network traffic (resources have been
117
- loaded) on the current page. This returns an array of request objects.
129
+ ## Network traffic
118
130
 
131
+ * `page.driver.network_traffic` Inspect network traffic (loaded resources) on
132
+ the current page. This returns an array of request objects.
119
133
 
120
134
  ```ruby
121
135
  page.driver.network_traffic # => [Request, ...]
@@ -123,11 +137,30 @@ request = page.driver.network_traffic.first
123
137
  request.response
124
138
  ```
125
139
 
140
+ * `page.driver.wait_for_network_idle` Natively waits for network idle and if
141
+ there are no active connections returns or raises `TimeoutError` error. Accepts
142
+ the same options as
143
+ [`wait_for_idle`](https://github.com/rubycdp/ferrum#wait_for_idleoptions)
144
+
145
+ ```ruby
146
+ page.driver.wait_for_network_idle
147
+ page.driver.refresh
148
+ ```
149
+
126
150
  Please note that network traffic is not cleared when you visit new page. You can
127
151
  manually clear the network traffic by calling `page.driver.clear_network_traffic`
128
152
  or `page.driver.reset`
129
153
 
130
- ### Manipulating cookies ###
154
+ * `page.driver.wait_for_reload` unlike `wait_for_network_idle` will wait until
155
+ the whole page is reloaded or raise a timeout error. It's useful when you know
156
+ that for example after clicking autocomplete suggestion you expect page to be
157
+ reloaded, you have a few choices - put sleep or wait for network idle, but both
158
+ are bad. Sleep makes you wait longer or less than needed, network idle can
159
+ return earlier even before the whole page is started to reload. Here's the
160
+ rescue.
161
+
162
+
163
+ ## Manipulating cookies
131
164
 
132
165
  The following methods are used to inspect and manipulate cookies:
133
166
 
@@ -142,18 +175,22 @@ The following methods are used to inspect and manipulate cookies:
142
175
  * `page.driver.remove_cookie(name)` - remove a cookie
143
176
  * `page.driver.clear_cookies` - clear all cookies
144
177
 
145
- ### Screenshot ###
178
+
179
+ ## Screenshot
146
180
 
147
181
  Besides capybara screenshot method you can get image as Base64:
148
182
 
149
183
  * `page.driver.render_base64(format, options)`
150
184
 
151
- ### Authorization ###
185
+
186
+ ## Authorization
152
187
 
153
188
  * `page.driver.basic_authorize(user, password)`
154
189
  * `page.driver.set_proxy(ip, port, type, user, password)`
155
190
 
156
- ### URL Blacklisting & Whitelisting ###
191
+
192
+ ## URL Blacklisting & Whitelisting
193
+
157
194
  Cuprite supports URL blacklisting, which allows you to prevent scripts from
158
195
  running on designated domains:
159
196
 
@@ -161,8 +198,8 @@ running on designated domains:
161
198
  page.driver.browser.url_blacklist = ["http://www.example.com"]
162
199
  ```
163
200
 
164
- and also URL whitelisting, which allows scripts to only run
165
- on designated domains:
201
+ and also URL whitelisting, which allows scripts to only run on designated
202
+ domains:
166
203
 
167
204
  ```ruby
168
205
  page.driver.browser.url_whitelist = ["http://www.example.com"]
@@ -172,40 +209,7 @@ If you are experiencing slower run times, consider creating a URL whitelist of
172
209
  domains that are essential or a blacklist of domains that are not essential,
173
210
  such as ad networks or analytics, to your testing environment.
174
211
 
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
-
190
- ## License ##
191
-
192
- Copyright 2018-2019 Machinio
193
-
194
- Permission is hereby granted, free of charge, to any person obtaining
195
- a copy of this software and associated documentation files (the
196
- "Software"), to deal in the Software without restriction, including
197
- without limitation the rights to use, copy, modify, merge, publish,
198
- distribute, sublicense, and/or sell copies of the Software, and to
199
- permit persons to whom the Software is furnished to do so, subject to
200
- the following conditions:
201
-
202
- The above copyright notice and this permission notice shall be
203
- included in all copies or substantial portions of the Software.
212
+ ## License
204
213
 
205
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
206
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
207
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
208
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
209
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
210
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
211
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
214
+ The gem is available as open source under the terms of the
215
+ [MIT License](https://opensource.org/licenses/MIT).
@@ -2,18 +2,12 @@
2
2
 
3
3
  require "ferrum"
4
4
  require "capybara"
5
-
6
- module Capybara::Cuprite
7
- end
8
-
9
5
  require "capybara/cuprite/driver"
10
6
  require "capybara/cuprite/browser"
11
7
  require "capybara/cuprite/page"
12
8
  require "capybara/cuprite/node"
13
9
  require "capybara/cuprite/errors"
14
10
 
15
- Ferrum::Page.prepend(Capybara::Cuprite::Page)
16
-
17
11
  Capybara.register_driver(:cuprite) do |app|
18
12
  Capybara::Cuprite::Driver.new(app)
19
13
  end
@@ -23,12 +23,12 @@ module Capybara::Cuprite
23
23
 
24
24
  def page
25
25
  raise Ferrum::NoSuchPageError if @page.nil?
26
- @page ||= default_context.page
26
+ @page ||= attach_page
27
27
  end
28
28
 
29
29
  def reset
30
30
  super
31
- @page = default_context.page
31
+ @page = attach_page
32
32
  end
33
33
 
34
34
  def quit
@@ -81,7 +81,7 @@ module Capybara::Cuprite
81
81
 
82
82
  if Capybara::VERSION.to_f < 3.0
83
83
  target_id = window_handles.find do |target_id|
84
- page = targets[target_id].page
84
+ page = attach_page(target_id)
85
85
  locator == page.frame_name
86
86
  end
87
87
  locator = target_id if target_id
@@ -100,13 +100,13 @@ module Capybara::Cuprite
100
100
  def switch_to_window(target_id)
101
101
  target = targets[target_id]
102
102
  raise Ferrum::NoSuchPageError unless target
103
- @page = target.page
103
+ @page = attach_page(target.id)
104
104
  end
105
105
 
106
106
  def close_window(target_id)
107
107
  target = targets[target_id]
108
108
  raise Ferrum::NoSuchPageError unless target
109
- @page = nil if @page == target.page
109
+ @page = nil if @page.target_id == target.id
110
110
  target.page.close
111
111
  end
112
112
 
@@ -200,5 +200,15 @@ module Capybara::Cuprite
200
200
  end
201
201
  end
202
202
  end
203
+
204
+ def attach_page(target_id = nil)
205
+ target = targets[target_id] if target_id
206
+ target ||= default_context.default_target
207
+ return target.page if target.attached?
208
+
209
+ target.maybe_sleep_if_new_window
210
+ target.page = Page.new(target.id, self)
211
+ target.page
212
+ end
203
213
  end
204
214
  end
@@ -26,6 +26,8 @@ module Capybara::Cuprite
26
26
  @screen_size ||= DEFAULT_MAXIMIZE_SCREEN_SIZE
27
27
 
28
28
  @options[:save_path] = Capybara.save_path.to_s if Capybara.save_path
29
+
30
+ ENV["FERRUM_DEBUG"] = "true" if ENV["CUPRITE_DEBUG"]
29
31
  end
30
32
 
31
33
  def needs_server?
@@ -129,7 +131,10 @@ module Capybara::Cuprite
129
131
  end
130
132
 
131
133
  def open_new_window
132
- browser.create_page
134
+ target = browser.default_context.create_target
135
+ target.maybe_sleep_if_new_window
136
+ target.page = Page.new(target.id, browser)
137
+ target.page
133
138
  end
134
139
 
135
140
  def switch_to_window(handle)
@@ -238,7 +243,9 @@ module Capybara::Cuprite
238
243
  server = type ? "#{type}=#{ip}:#{port}" : "#{ip}:#{port}"
239
244
  @options[:browser_options].merge!("proxy-server" => server)
240
245
  @options[:browser_options].merge!("proxy-bypass-list" => bypass) if bypass
241
- browser.network.authorize(type: :proxy, user: user, password: password)
246
+ browser.network.authorize(type: :proxy, user: user, password: password) do |request|
247
+ request.continue
248
+ end
242
249
  end
243
250
 
244
251
  def headers
@@ -282,22 +289,41 @@ module Capybara::Cuprite
282
289
  browser.cookies.clear
283
290
  end
284
291
 
292
+ def wait_for_network_idle(**options)
293
+ browser.network.wait_for_idle(**options)
294
+ end
295
+
285
296
  def clear_memory_cache
286
297
  browser.network.clear(:cache)
287
298
  end
288
299
 
289
300
  def basic_authorize(user, password)
290
- browser.network.authorize(user: user, password: password)
301
+ browser.network.authorize(user: user, password: password) do |request|
302
+ request.continue
303
+ end
291
304
  end
292
305
  alias_method :authorize, :basic_authorize
293
306
 
294
- def debug
307
+ def debug_url
308
+ "http://#{browser.process.host}:#{browser.process.port}"
309
+ end
310
+
311
+ def debug(binding = nil)
295
312
  if @options[:inspector]
296
- Process.spawn(browser.process.path, "http://#{browser.process.host}:#{browser.process.port}")
297
- pause
313
+ Process.spawn(browser.process.path, debug_url)
314
+
315
+ if binding&.respond_to?(:pry)
316
+ Pry.start(binding)
317
+ elsif binding&.respond_to?(:irb)
318
+ binding.irb
319
+ else
320
+ pause
321
+ end
298
322
  else
299
323
  raise Error, "To use the remote debugging, you have to launch " \
300
- "the driver with `inspector: true` configuration option"
324
+ "the driver with `inspector: ENV['INSPECTOR']` " \
325
+ "configuration option and run your test suite passing " \
326
+ "env variable"
301
327
  end
302
328
  end
303
329
 
@@ -308,7 +334,7 @@ module Capybara::Cuprite
308
334
  # In jRuby - STDIN returns immediately from select
309
335
  # see https://github.com/jruby/jruby/issues/1783
310
336
  read, write = IO.pipe
311
- Thread.new { IO.copy_stream(STDIN, write); write.close }
337
+ thread = Thread.new { IO.copy_stream(STDIN, write); write.close }
312
338
 
313
339
  STDERR.puts "Cuprite execution paused. Press enter (or run 'kill -CONT #{Process.pid}') to continue."
314
340
 
@@ -324,6 +350,8 @@ module Capybara::Cuprite
324
350
  end
325
351
  end
326
352
  ensure
353
+ thread.kill
354
+ read.close
327
355
  trap("SIGCONT", old_trap) # Restore the previous signal handler, if there was one.
328
356
  STDERR.puts "Continuing"
329
357
  end
@@ -335,7 +363,9 @@ module Capybara::Cuprite
335
363
  def invalid_element_errors
336
364
  [Capybara::Cuprite::ObsoleteNode,
337
365
  Capybara::Cuprite::MouseEventFailed,
338
- Ferrum::NoExecutionContextError]
366
+ Ferrum::CoordinatesNotFoundError,
367
+ Ferrum::NoExecutionContextError,
368
+ Ferrum::NodeNotFoundError]
339
369
  end
340
370
 
341
371
  def go_back
@@ -350,6 +380,10 @@ module Capybara::Cuprite
350
380
  browser.refresh
351
381
  end
352
382
 
383
+ def wait_for_reload(*args)
384
+ browser.wait_for_reload(*args)
385
+ end
386
+
353
387
  def accept_modal(type, options = {})
354
388
  case type
355
389
  when :alert, :confirm
@@ -41,7 +41,7 @@ class Cuprite {
41
41
  parents(node) {
42
42
  let nodes = [];
43
43
  let parent = node.parentNode;
44
- while (parent != document) {
44
+ while (parent != document && parent !== null) {
45
45
  nodes.push(parent);
46
46
  parent = parent.parentNode;
47
47
  }
@@ -97,7 +97,7 @@ class Cuprite {
97
97
  path(node) {
98
98
  let nodes = [node];
99
99
  let parent = node.parentNode;
100
- while (parent !== document) {
100
+ while (parent !== document && parent !== null) {
101
101
  nodes.unshift(parent);
102
102
  parent = parent.parentNode;
103
103
  }
@@ -128,7 +128,7 @@ class Cuprite {
128
128
  this.trigger(node, "focus");
129
129
  this.setValue(node, "");
130
130
 
131
- if (node.type == "number" || node.type == "date") {
131
+ if (node.type == "number" || node.type == "date" || node.type == "range") {
132
132
  this.setValue(node, value);
133
133
  this.input(node);
134
134
  } else if (node.type == "time") {
@@ -104,6 +104,8 @@ module Capybara::Cuprite
104
104
  when "file"
105
105
  files = value.respond_to?(:to_ary) ? value.to_ary.map(&:to_s) : value.to_s
106
106
  command(:select_file, files)
107
+ when "color"
108
+ node.evaluate("this.setAttribute('value', '#{value}')")
107
109
  else
108
110
  command(:set, value.to_s)
109
111
  end
@@ -144,16 +146,16 @@ module Capybara::Cuprite
144
146
  command(:disabled?)
145
147
  end
146
148
 
147
- def click(keys = [], offset = {})
148
- prepare_and_click(:left, __method__, keys, offset)
149
+ def click(keys = [], **options)
150
+ prepare_and_click(:left, __method__, keys, options)
149
151
  end
150
152
 
151
- def right_click(keys = [], offset = {})
152
- prepare_and_click(:right, __method__, keys, offset)
153
+ def right_click(keys = [], **options)
154
+ prepare_and_click(:right, __method__, keys, options)
153
155
  end
154
156
 
155
- def double_click(keys = [], offset = {})
156
- prepare_and_click(:double, __method__, keys, offset)
157
+ def double_click(keys = [], **options)
158
+ prepare_and_click(:double, __method__, keys, options)
157
159
  end
158
160
 
159
161
  def hover
@@ -225,9 +227,12 @@ module Capybara::Cuprite
225
227
 
226
228
  private
227
229
 
228
- def prepare_and_click(mode, name, keys, offset)
230
+ def prepare_and_click(mode, name, keys, options)
231
+ delay = options[:delay].to_i
232
+ x, y = options.values_at(:x, :y)
233
+ offset = { x: x, y: y, position: options[:offset] || :top }
229
234
  command(:before_click, name, keys, offset)
230
- node.click(mode: mode, keys: keys, offset: offset)
235
+ node.click(mode: mode, keys: keys, offset: offset, delay: delay)
231
236
  end
232
237
 
233
238
  def filter_text(text)
@@ -3,15 +3,14 @@
3
3
  require "forwardable"
4
4
 
5
5
  module Capybara::Cuprite
6
- module Page
6
+ class Page < Ferrum::Page
7
7
  MODAL_WAIT = ENV.fetch("CUPRITE_MODAL_WAIT", 0.05).to_f
8
8
  TRIGGER_CLICK_WAIT = ENV.fetch("CUPRITE_TRIGGER_CLICK_WAIT", 0.1).to_f
9
9
 
10
10
  extend Forwardable
11
11
  delegate %i[at_css at_xpath css xpath
12
- current_url current_title body
13
- execution_id evaluate evaluate_on evaluate_async execute] => :active_frame
14
-
12
+ current_url current_title body execution_id
13
+ evaluate evaluate_on evaluate_async execute] => :active_frame
15
14
 
16
15
  def initialize(*args)
17
16
  @frame_stack = []
@@ -95,7 +94,12 @@ module Capybara::Cuprite
95
94
 
96
95
  def before_click(node, name, keys = [], offset = {})
97
96
  evaluate_on(node: node, expression: "_cuprite.scrollIntoViewport(this)")
98
- x, y = find_position(node, offset[:x], offset[:y])
97
+
98
+ # If offset is given it may go outside of the element and likely error
99
+ # will be raised that we detected another element at this position.
100
+ return true if offset[:x] || offset[:y]
101
+
102
+ x, y = find_position(node, **offset)
99
103
  evaluate_on(node: node, expression: "_cuprite.mouseEventTest(this, '#{name}', #{x}, #{y})")
100
104
  true
101
105
  rescue Ferrum::JavaScriptError => e
@@ -114,6 +118,10 @@ module Capybara::Cuprite
114
118
  end
115
119
  end
116
120
 
121
+ def frame_name
122
+ evaluate("window.name")
123
+ end
124
+
117
125
  def title
118
126
  active_frame.current_title
119
127
  end
@@ -129,15 +137,15 @@ module Capybara::Cuprite
129
137
  on(:request) do |request, index, total|
130
138
  if @browser.url_blacklist && !@browser.url_blacklist.empty?
131
139
  if @browser.url_blacklist.any? { |r| request.match?(r) }
132
- request.abort and return
140
+ request.abort and next
133
141
  else
134
- request.continue and return
142
+ request.continue and next
135
143
  end
136
144
  elsif @browser.url_whitelist && !@browser.url_whitelist.empty?
137
145
  if @browser.url_whitelist.any? { |r| request.match?(r) }
138
- request.continue and return
146
+ request.continue and next
139
147
  else
140
- request.abort and return
148
+ request.abort and next
141
149
  end
142
150
  elsif index + 1 < total
143
151
  # There are other callbacks that may handle this request
@@ -171,8 +179,8 @@ module Capybara::Cuprite
171
179
  end
172
180
  end
173
181
 
174
- def find_position(node, *args)
175
- x, y = node.find_position(*args)
182
+ def find_position(node, **options)
183
+ x, y = node.find_position(**options)
176
184
  rescue Ferrum::BrowserError => e
177
185
  if e.message == "Could not compute content quads."
178
186
  raise MouseEventFailed.new("MouseEventFailed: click, none, 0, 0")
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Capybara
4
4
  module Cuprite
5
- VERSION = "0.8"
5
+ VERSION = "0.13"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cuprite
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.8'
4
+ version: '0.13'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Vorotilin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-10-29 00:00:00.000000000 Z
11
+ date: 2021-03-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: capybara
@@ -36,14 +36,14 @@ dependencies:
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: 0.6.0
39
+ version: 0.11.0
40
40
  type: :runtime
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: 0.6.0
46
+ version: 0.11.0
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: image_size
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -189,10 +189,14 @@ files:
189
189
  - lib/capybara/cuprite/node.rb
190
190
  - lib/capybara/cuprite/page.rb
191
191
  - lib/capybara/cuprite/version.rb
192
- homepage: https://github.com/machinio/cuprite
192
+ homepage: https://github.com/rubycdp/cuprite
193
193
  licenses:
194
194
  - MIT
195
- metadata: {}
195
+ metadata:
196
+ homepage_uri: https://cuprite.rubycdp.com/
197
+ bug_tracker_uri: https://github.com/rubycdp/cuprite/issues
198
+ documentation_uri: https://github.com/rubycdp/cuprite/blob/master/README.md
199
+ source_code_uri: https://github.com/rubycdp/cuprite
196
200
  post_install_message:
197
201
  rdoc_options: []
198
202
  require_paths:
@@ -208,7 +212,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
208
212
  - !ruby/object:Gem::Version
209
213
  version: '0'
210
214
  requirements: []
211
- rubygems_version: 3.0.3
215
+ rubygems_version: 3.1.2
212
216
  signing_key:
213
217
  specification_version: 4
214
218
  summary: Headless Chrome driver for Capybara