cuprite 0.7.0 → 0.11

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: 07b0203f3647868b796a4649f28e2ed0a549412156e08a3ef50e83625acaca11
4
- data.tar.gz: 9854d6126698f53691c30b04c9a8677f24091c76720227ceb474101dab06a475
3
+ metadata.gz: 8ec561634fc66a075a61f4221aef5276b57e447c0810865fdc6562a41842ce42
4
+ data.tar.gz: af475a5d00fe0f32dc8ddf643bd7bc0cfb80409ae8ec4cb627a976cc9b1b753c
5
5
  SHA512:
6
- metadata.gz: 2fe300cdefbb2a2c7d09d37329ab7a31dcf2d7cda9177ffafc91bdec2923fabf24c933f6894e5e959bdb6218e03ac9211afedb7ae588ae97f87d7142fb249e7c
7
- data.tar.gz: ede07bd3d885c5e711a80a6698d10ad28d855d7b6917f10ebc2cfe0153fde5af261ef553cdee62ad3a6cabdacfee659e12d13b95ebc8c2470d4d4b33772ad5be
6
+ metadata.gz: f460096e3ce6e8613be751bc237d7efde7a3c6a8b63ba54283b677d6f7a6d7c25bb1182bb38dc3762a42e7eb3c8f19ca4678f7d82591c79b6849606e021dd501
7
+ data.tar.gz: 95b4633594b2581bd3e4259b110652668b75362fccca572c43b0020e11e02701f077fed5be558abfa1960955e9697c79961b0408c461020eade8c609acb9e731
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2018 Machinio
3
+ Copyright (c) 2018-2020 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,27 @@
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
+ [![CircleCI](https://circleci.com/gh/rubycdp/cuprite.svg?style=shield)](https://circleci.com/gh/rubycdp/cuprite)
4
4
 
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.
5
+ Cuprite is a pure Ruby driver (read as _no_ Selenium/WebDriver/ChromeDriver
6
+ dependency) for [Capybara](https://github.com/teamcapybara/capybara). It allows
7
+ you to run Capybara tests on a headless Chrome or Chromium. Under the hood it
8
+ uses [Ferrum](https://github.com/rubycdp/ferrum#index) which is high-level API
9
+ to the browser by CDP protocol. The design of the driver is as close to
10
+ [Poltergeist](https://github.com/teampoltergeist/poltergeist) as possible though
11
+ it's not a goal.
11
12
 
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.
13
+ [Cuprite](https://evrone.com/cuprite) designed & supported by [Evrone](https://evrone.com/)
14
+ What else we build [with Ruby](https://evrone.com/ruby)
17
15
 
18
- ## Install ##
19
16
 
20
- Add these lines to your `Gemfile` and run `bundle install`.
17
+ ## Install
18
+
19
+ Add this to your `Gemfile` and run `bundle install`.
21
20
 
22
21
  ``` ruby
23
- gem "cuprite", group: :test
22
+ group :test do
23
+ gem "cuprite"
24
+ end
24
25
  ```
25
26
 
26
27
  In your test setup add:
@@ -33,21 +34,31 @@ Capybara.register_driver(:cuprite) do |app|
33
34
  end
34
35
  ```
35
36
 
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.
37
+ if you use `Docker` don't forget to pass `no-sandbox` option:
38
+
39
+ ```ruby
40
+ Capybara::Cuprite::Driver.new(app, browser_options: { 'no-sandbox': nil })
41
+ ```
42
+
43
+ Since Cuprite uses [Ferrum](https://github.com/rubycdp/ferrum#examples) there
44
+ are many useful methods you can call even using this driver:
45
+
46
+ ```ruby
47
+ browser = page.driver.browser
48
+ browser.mouse.move(x: 123, y: 456).down.up
49
+ ```
50
+
51
+ If you already have tests on Poltergeist then it should simply work, for
52
+ Selenium you better check your code for `manage` calls because it works
53
+ differently in Cuprite, see the documentation below.
39
54
 
40
- ## Install Chrome ##
41
55
 
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
56
+ ## Customization
47
57
 
48
- ## Customization ##
58
+ See the full list of options for
59
+ [Ferrum](https://github.com/rubycdp/ferrum#customization).
49
60
 
50
- You can customize options with the following code in your test setup:
61
+ You can pass options with the following code in your test setup:
51
62
 
52
63
  ``` ruby
53
64
  Capybara.register_driver(:cuprite) do |app|
@@ -55,47 +66,52 @@ Capybara.register_driver(:cuprite) do |app|
55
66
  end
56
67
  ```
57
68
 
58
- #### Running in Docker ####
69
+ `Cuprite`-specific options are:
59
70
 
60
- In docker as root you must pass the no-sandbox browser option:
71
+ * options `Hash`
72
+ * `:url_blacklist` (Array) - array of strings to match against requested URLs
73
+ * `:url_whitelist` (Array) - array of strings to match against requested URLs
74
+
75
+
76
+ ## Debugging
77
+
78
+ If you pass `inspector` option, remote debugging will be enabled if you run
79
+ tests with `INSPECTOR=true`. Then you can put `page.driver.debug` or
80
+ `page.driver.debug(binding)` in your test to pause it. This will launch the
81
+ browser where you can inspect the content.
61
82
 
62
83
  ```ruby
63
- Capybara::Cuprite::Driver.new(app, browser_options: { 'no-sandbox': nil })
84
+ Capybara.register_driver :cuprite do |app|
85
+ Capybara::Cuprite::Driver.new(app, inspector: ENV['INSPECTOR'])
86
+ end
64
87
  ```
65
88
 
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
89
+ then somewhere in the test:
90
+
91
+ ```ruby
92
+ it "does something useful" do
93
+ visit root_path
94
+
95
+ fill_in "field", with: "value"
96
+ page.driver.debug(binding)
97
+
98
+ expect(page).to have_content("value")
99
+ end
100
+ ```
101
+
102
+ In the middle of the execution Chrome will open a new tab where you can inspect
103
+ the content and also if you passed `binding` an `irb` or `pry` console will be
104
+ opened where you can further experiment with the test.
91
105
 
92
- ### Clicking/Scrolling ###
106
+
107
+ ## Clicking/Scrolling
93
108
 
94
109
  * `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.
110
+ * `page.driver.scroll_to(left, top)` Scroll to a given position.
111
+ * `element.send_keys(*keys)` Send keys to a given node.
112
+
97
113
 
98
- ### Request headers ###
114
+ ## Request headers
99
115
 
100
116
  Manipulate HTTP request headers like a boss:
101
117
 
@@ -111,11 +127,11 @@ Notice that `headers=` will overwrite already set headers. You should use
111
127
  subsequent HTTP requests (including requests for assets, AJAX, etc). They will
112
128
  be automatically cleared at the end of the test.
113
129
 
114
- ### Network traffic ###
115
130
 
116
- * `page.driver.network_traffic` Inspect network traffic (resources have been
117
- loaded) on the current page. This returns an array of request objects.
131
+ ## Network traffic
118
132
 
133
+ * `page.driver.network_traffic` Inspect network traffic (loaded resources) on
134
+ the current page. This returns an array of request objects.
119
135
 
120
136
  ```ruby
121
137
  page.driver.network_traffic # => [Request, ...]
@@ -123,11 +139,30 @@ request = page.driver.network_traffic.first
123
139
  request.response
124
140
  ```
125
141
 
142
+ * `page.driver.wait_for_network_idle` Natively waits for network idle and if
143
+ there are no active connections returns or raises `TimeoutError` error. Accepts
144
+ the same options as
145
+ [`wait_for_idle`](https://github.com/rubycdp/ferrum#wait_for_idleoptions)
146
+
147
+ ```ruby
148
+ page.driver.wait_for_network_idle
149
+ page.driver.refresh
150
+ ```
151
+
126
152
  Please note that network traffic is not cleared when you visit new page. You can
127
153
  manually clear the network traffic by calling `page.driver.clear_network_traffic`
128
154
  or `page.driver.reset`
129
155
 
130
- ### Manipulating cookies ###
156
+ * `page.driver.wait_for_reload` unlike `wait_for_network_idle` will wait until
157
+ the whole page is reloaded or raise a timeout error. It's useful when you know
158
+ that for example after clicking autocomplete suggestion you expect page to be
159
+ reloaded, you have a few choices - put sleep or wait for network idle, but both
160
+ are bad. Sleep makes you wait longer or less than needed, network idle can
161
+ return earlier even before the whole page is started to reload. Here's the
162
+ rescue.
163
+
164
+
165
+ ## Manipulating cookies
131
166
 
132
167
  The following methods are used to inspect and manipulate cookies:
133
168
 
@@ -142,18 +177,22 @@ The following methods are used to inspect and manipulate cookies:
142
177
  * `page.driver.remove_cookie(name)` - remove a cookie
143
178
  * `page.driver.clear_cookies` - clear all cookies
144
179
 
145
- ### Screenshot ###
180
+
181
+ ## Screenshot
146
182
 
147
183
  Besides capybara screenshot method you can get image as Base64:
148
184
 
149
185
  * `page.driver.render_base64(format, options)`
150
186
 
151
- ### Authorization ###
187
+
188
+ ## Authorization
152
189
 
153
190
  * `page.driver.basic_authorize(user, password)`
154
191
  * `page.driver.set_proxy(ip, port, type, user, password)`
155
192
 
156
- ### URL Blacklisting & Whitelisting ###
193
+
194
+ ## URL Blacklisting & Whitelisting
195
+
157
196
  Cuprite supports URL blacklisting, which allows you to prevent scripts from
158
197
  running on designated domains:
159
198
 
@@ -161,8 +200,8 @@ running on designated domains:
161
200
  page.driver.browser.url_blacklist = ["http://www.example.com"]
162
201
  ```
163
202
 
164
- and also URL whitelisting, which allows scripts to only run
165
- on designated domains:
203
+ and also URL whitelisting, which allows scripts to only run on designated
204
+ domains:
166
205
 
167
206
  ```ruby
168
207
  page.driver.browser.url_whitelist = ["http://www.example.com"]
@@ -171,44 +210,3 @@ page.driver.browser.url_whitelist = ["http://www.example.com"]
171
210
  If you are experiencing slower run times, consider creating a URL whitelist of
172
211
  domains that are essential or a blacklist of domains that are not essential,
173
212
  such as ad networks or analytics, to your testing environment.
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 can register this debugger driver with a different name and set it
183
- as the current javascript driver. By example, in your helper file:
184
-
185
- ```ruby
186
- Capybara.register_driver :cuprite_debug do |app|
187
- Capybara::Cuprite::Driver.new(app, inspector: true)
188
- end
189
-
190
- Capybara.javascript_driver = :cuprite_debug
191
- ```
192
-
193
- ## License ##
194
-
195
- Copyright 2018-2019 Machinio
196
-
197
- Permission is hereby granted, free of charge, to any person obtaining
198
- a copy of this software and associated documentation files (the
199
- "Software"), to deal in the Software without restriction, including
200
- without limitation the rights to use, copy, modify, merge, publish,
201
- distribute, sublicense, and/or sell copies of the Software, and to
202
- permit persons to whom the Software is furnished to do so, subject to
203
- the following conditions:
204
-
205
- The above copyright notice and this permission notice shall be
206
- included in all copies or substantial portions of the Software.
207
-
208
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
209
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
210
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
211
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
212
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
213
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
214
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -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
@@ -6,7 +6,6 @@ module Capybara::Cuprite
6
6
  class Browser < Ferrum::Browser
7
7
  extend Forwardable
8
8
 
9
- delegate %i[find_or_create_page] => :targets
10
9
  delegate %i[send_keys select set hover trigger before_click switch_to_frame
11
10
  find_modal accept_confirm dismiss_confirm accept_prompt
12
11
  dismiss_prompt reset_modals] => :page
@@ -19,16 +18,32 @@ module Capybara::Cuprite
19
18
  self.url_whitelist = options[:url_whitelist]
20
19
 
21
20
  super
21
+ @page = false
22
+ end
23
+
24
+ def page
25
+ raise Ferrum::NoSuchPageError if @page.nil?
26
+ @page ||= attach_page
27
+ end
28
+
29
+ def reset
30
+ super
31
+ @page = attach_page
32
+ end
33
+
34
+ def quit
35
+ super
36
+ @page = false
22
37
  end
23
38
 
24
39
  def url_whitelist=(patterns)
25
40
  @url_whitelist = prepare_wildcards(patterns)
26
- page.intercept_request if @client && !@url_whitelist.empty?
41
+ page.network.intercept if @client && !@url_whitelist.empty?
27
42
  end
28
43
 
29
44
  def url_blacklist=(patterns)
30
45
  @url_blacklist = prepare_wildcards(patterns)
31
- page.intercept_request if @client && !@url_blacklist.empty?
46
+ page.network.intercept if @client && !@url_blacklist.empty?
32
47
  end
33
48
 
34
49
  def visit(*args)
@@ -36,7 +51,7 @@ module Capybara::Cuprite
36
51
  end
37
52
 
38
53
  def status_code
39
- status
54
+ network.status
40
55
  end
41
56
 
42
57
  def find(method, selector)
@@ -53,16 +68,46 @@ module Capybara::Cuprite
53
68
  find_all(method, selector, { "objectId" => object_id })
54
69
  end
55
70
 
71
+ def window_handle
72
+ page.target_id
73
+ end
74
+
75
+ def window_handles
76
+ targets.keys
77
+ end
78
+
56
79
  def within_window(locator = nil, &block)
80
+ original = window_handle
81
+
57
82
  if Capybara::VERSION.to_f < 3.0
58
83
  target_id = window_handles.find do |target_id|
59
- page = find_or_create_page(target_id)
84
+ page = attach_page(target_id)
60
85
  locator == page.frame_name
61
86
  end
62
87
  locator = target_id if target_id
63
88
  end
64
89
 
65
- targets.within_window(locator, &block)
90
+ if window_handles.include?(locator)
91
+ switch_to_window(locator)
92
+ yield
93
+ else
94
+ raise Ferrum::NoSuchPageError
95
+ end
96
+ ensure
97
+ switch_to_window(original)
98
+ end
99
+
100
+ def switch_to_window(target_id)
101
+ target = targets[target_id]
102
+ raise Ferrum::NoSuchPageError unless target
103
+ @page = attach_page(target.id)
104
+ end
105
+
106
+ def close_window(target_id)
107
+ target = targets[target_id]
108
+ raise Ferrum::NoSuchPageError unless target
109
+ @page = nil if @page.target_id == target.id
110
+ target.page.close
66
111
  end
67
112
 
68
113
  def browser_error
@@ -155,5 +200,15 @@ module Capybara::Cuprite
155
200
  end
156
201
  end
157
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
158
213
  end
159
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?
@@ -50,7 +52,7 @@ module Capybara::Cuprite
50
52
  end
51
53
 
52
54
  def frame_url
53
- browser.frame_url
55
+ evaluate_script("window.location.href")
54
56
  end
55
57
 
56
58
  def html
@@ -71,7 +73,7 @@ module Capybara::Cuprite
71
73
  end
72
74
 
73
75
  def frame_title
74
- browser.frame_title
76
+ evaluate_script("document.title")
75
77
  end
76
78
 
77
79
  def find_xpath(selector)
@@ -129,7 +131,10 @@ module Capybara::Cuprite
129
131
  end
130
132
 
131
133
  def open_new_window
132
- browser.open_new_window
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)
@@ -141,7 +146,7 @@ module Capybara::Cuprite
141
146
  end
142
147
 
143
148
  def no_such_window_error
144
- Ferrum::NoSuchWindowError
149
+ Ferrum::NoSuchPageError
145
150
  end
146
151
 
147
152
  def reset!
@@ -216,11 +221,21 @@ module Capybara::Cuprite
216
221
  end
217
222
 
218
223
  def network_traffic(type = nil)
219
- browser.network_traffic(type)
224
+ traffic = browser.network.traffic
225
+
226
+ case type.to_s
227
+ when "all"
228
+ traffic
229
+ when "blocked"
230
+ traffic.select(&:blocked?)
231
+ else
232
+ # when request isn't blocked
233
+ traffic.reject(&:blocked?)
234
+ end
220
235
  end
221
236
 
222
237
  def clear_network_traffic
223
- browser.clear_network_traffic
238
+ browser.network.clear(:traffic)
224
239
  end
225
240
 
226
241
  def set_proxy(ip, port, type = nil, user = nil, password = nil, bypass = nil)
@@ -228,7 +243,7 @@ module Capybara::Cuprite
228
243
  server = type ? "#{type}=#{ip}:#{port}" : "#{ip}:#{port}"
229
244
  @options[:browser_options].merge!("proxy-server" => server)
230
245
  @options[:browser_options].merge!("proxy-bypass-list" => bypass) if bypass
231
- browser.authorize(type: :proxy, user: user, password: password)
246
+ browser.network.authorize(type: :proxy, user: user, password: password)
232
247
  end
233
248
 
234
249
  def headers
@@ -248,7 +263,7 @@ module Capybara::Cuprite
248
263
  end
249
264
 
250
265
  def response_headers
251
- browser.response_headers
266
+ browser.network.response&.headers
252
267
  end
253
268
 
254
269
  def cookies
@@ -272,22 +287,39 @@ module Capybara::Cuprite
272
287
  browser.cookies.clear
273
288
  end
274
289
 
290
+ def wait_for_network_idle(**options)
291
+ browser.network.wait_for_idle(**options)
292
+ end
293
+
275
294
  def clear_memory_cache
276
- browser.clear_memory_cache
295
+ browser.network.clear(:cache)
277
296
  end
278
297
 
279
298
  def basic_authorize(user, password)
280
- browser.authorize(user: user, password: password)
299
+ browser.network.authorize(user: user, password: password)
281
300
  end
282
301
  alias_method :authorize, :basic_authorize
283
302
 
284
- def debug
303
+ def debug_url
304
+ "http://#{browser.process.host}:#{browser.process.port}"
305
+ end
306
+
307
+ def debug(binding = nil)
285
308
  if @options[:inspector]
286
- Process.spawn(browser.process.path, "http://#{browser.process.host}:#{browser.process.port}")
287
- pause
309
+ Process.spawn(browser.process.path, debug_url)
310
+
311
+ if binding&.respond_to?(:pry)
312
+ Pry.start(binding)
313
+ elsif binding&.respond_to?(:irb)
314
+ binding.irb
315
+ else
316
+ pause
317
+ end
288
318
  else
289
319
  raise Error, "To use the remote debugging, you have to launch " \
290
- "the driver with `inspector: true` configuration option"
320
+ "the driver with `inspector: ENV['INSPECTOR']` " \
321
+ "configuration option and run your test suite passing " \
322
+ "env variable"
291
323
  end
292
324
  end
293
325
 
@@ -298,7 +330,7 @@ module Capybara::Cuprite
298
330
  # In jRuby - STDIN returns immediately from select
299
331
  # see https://github.com/jruby/jruby/issues/1783
300
332
  read, write = IO.pipe
301
- Thread.new { IO.copy_stream(STDIN, write); write.close }
333
+ thread = Thread.new { IO.copy_stream(STDIN, write); write.close }
302
334
 
303
335
  STDERR.puts "Cuprite execution paused. Press enter (or run 'kill -CONT #{Process.pid}') to continue."
304
336
 
@@ -314,6 +346,8 @@ module Capybara::Cuprite
314
346
  end
315
347
  end
316
348
  ensure
349
+ thread.kill
350
+ read.close
317
351
  trap("SIGCONT", old_trap) # Restore the previous signal handler, if there was one.
318
352
  STDERR.puts "Continuing"
319
353
  end
@@ -340,6 +374,10 @@ module Capybara::Cuprite
340
374
  browser.refresh
341
375
  end
342
376
 
377
+ def wait_for_reload(*args)
378
+ browser.wait_for_reload(*args)
379
+ end
380
+
343
381
  def accept_modal(type, options = {})
344
382
  case type
345
383
  when :alert, :confirm
@@ -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") {
@@ -144,16 +144,16 @@ module Capybara::Cuprite
144
144
  command(:disabled?)
145
145
  end
146
146
 
147
- def click(keys = [], offset = {})
148
- prepare_and_click(:left, __method__, keys, offset)
147
+ def click(keys = [], **options)
148
+ prepare_and_click(:left, __method__, keys, options)
149
149
  end
150
150
 
151
- def right_click(keys = [], offset = {})
152
- prepare_and_click(:right, __method__, keys, offset)
151
+ def right_click(keys = [], **options)
152
+ prepare_and_click(:right, __method__, keys, options)
153
153
  end
154
154
 
155
- def double_click(keys = [], offset = {})
156
- prepare_and_click(:double, __method__, keys, offset)
155
+ def double_click(keys = [], **options)
156
+ prepare_and_click(:double, __method__, keys, options)
157
157
  end
158
158
 
159
159
  def hover
@@ -225,9 +225,12 @@ module Capybara::Cuprite
225
225
 
226
226
  private
227
227
 
228
- def prepare_and_click(mode, name, keys, offset)
228
+ def prepare_and_click(mode, name, keys, options)
229
+ delay = options[:delay].to_i
230
+ x, y = options.values_at(:x, :y)
231
+ offset = { x: x, y: y, position: options[:offset] || :top }
229
232
  command(:before_click, name, keys, offset)
230
- node.click(mode: mode, keys: keys, offset: offset)
233
+ node.click(mode: mode, keys: keys, offset: offset, delay: delay)
231
234
  end
232
235
 
233
236
  def filter_text(text)
@@ -1,13 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "forwardable"
4
+
3
5
  module Capybara::Cuprite
4
- module Page
6
+ class Page < Ferrum::Page
5
7
  MODAL_WAIT = ENV.fetch("CUPRITE_MODAL_WAIT", 0.05).to_f
8
+ TRIGGER_CLICK_WAIT = ENV.fetch("CUPRITE_TRIGGER_CLICK_WAIT", 0.1).to_f
9
+
10
+ extend Forwardable
11
+ delegate %i[at_css at_xpath css xpath
12
+ current_url current_title body execution_id
13
+ evaluate evaluate_on evaluate_async execute] => :active_frame
6
14
 
7
15
  def initialize(*args)
8
- super
16
+ @frame_stack = []
9
17
  @accept_modal = []
10
18
  @modal_messages = []
19
+ super
11
20
  end
12
21
 
13
22
  def set(node, value)
@@ -21,7 +30,7 @@ module Capybara::Cuprite
21
30
 
22
31
  def trigger(node, event)
23
32
  options = {}
24
- options.merge!(wait: Ferrum::Mouse::CLICK_WAIT) if event.to_s == "click"
33
+ options.merge!(wait: TRIGGER_CLICK_WAIT) if event.to_s == "click" && TRIGGER_CLICK_WAIT > 0
25
34
  evaluate_on(node: node, expression: %(_cuprite.trigger(this, "#{event}")), **options)
26
35
  end
27
36
 
@@ -59,7 +68,7 @@ module Capybara::Cuprite
59
68
 
60
69
  def find_modal(options)
61
70
  start = Ferrum.monotonic_time
62
- timeout = options.fetch(:wait) { session_wait_time }
71
+ timeout = options.fetch(:wait, browser.timeout)
63
72
  expect_text = options[:text]
64
73
  expect_regexp = expect_text.is_a?(Regexp) ? expect_text : Regexp.escape(expect_text.to_s)
65
74
  not_found_msg = "Unable to find modal dialog"
@@ -85,7 +94,12 @@ module Capybara::Cuprite
85
94
 
86
95
  def before_click(node, name, keys = [], offset = {})
87
96
  evaluate_on(node: node, expression: "_cuprite.scrollIntoViewport(this)")
88
- 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)
89
103
  evaluate_on(node: node, expression: "_cuprite.mouseEventTest(this, '#{name}', #{x}, #{y})")
90
104
  true
91
105
  rescue Ferrum::JavaScriptError => e
@@ -104,26 +118,34 @@ module Capybara::Cuprite
104
118
  end
105
119
  end
106
120
 
121
+ def frame_name
122
+ evaluate("window.name")
123
+ end
124
+
125
+ def title
126
+ active_frame.current_title
127
+ end
128
+
107
129
  private
108
130
 
109
131
  def prepare_page
110
132
  super
111
133
 
112
- intercept_request if !Array(@browser.url_whitelist).empty? ||
134
+ network.intercept if !Array(@browser.url_whitelist).empty? ||
113
135
  !Array(@browser.url_blacklist).empty?
114
136
 
115
- on(:request_intercepted) do |request, index, total|
137
+ on(:request) do |request, index, total|
116
138
  if @browser.url_blacklist && !@browser.url_blacklist.empty?
117
139
  if @browser.url_blacklist.any? { |r| request.match?(r) }
118
- request.abort and return
140
+ request.abort and next
119
141
  else
120
- request.continue and return
142
+ request.continue and next
121
143
  end
122
144
  elsif @browser.url_whitelist && !@browser.url_whitelist.empty?
123
145
  if @browser.url_whitelist.any? { |r| request.match?(r) }
124
- request.continue and return
146
+ request.continue and next
125
147
  else
126
- request.abort and return
148
+ request.abort and next
127
149
  end
128
150
  elsif index + 1 < total
129
151
  # There are other callbacks that may handle this request
@@ -134,7 +156,7 @@ module Capybara::Cuprite
134
156
  end
135
157
  end
136
158
 
137
- @client.on("Page.javascriptDialogOpening") do |params|
159
+ on("Page.javascriptDialogOpening") do |params|
138
160
  accept_modal = @accept_modal.last
139
161
  if accept_modal == true || accept_modal == false
140
162
  @accept_modal.pop
@@ -144,7 +166,11 @@ module Capybara::Cuprite
144
166
  options.merge!(promptText: response) if response
145
167
  command("Page.handleJavaScriptDialog", **options)
146
168
  else
147
- warn "Modal window has been opened, but you didn't wrap your code into (`accept_prompt` | `dismiss_prompt` | `accept_confirm` | `dismiss_confirm` | `accept_alert`), accepting by default"
169
+ with_text = params["message"] ? "with text `#{params["message"]}` " : ""
170
+ warn "Modal window #{with_text}has been opened, but you didn't wrap "\
171
+ "your code into (`accept_prompt` | `dismiss_prompt` | "\
172
+ "`accept_confirm` | `dismiss_confirm` | `accept_alert`), "\
173
+ "accepting by default"
148
174
  options = { accept: true }
149
175
  response = params["defaultPrompt"]
150
176
  options.merge!(promptText: response) if response
@@ -153,8 +179,8 @@ module Capybara::Cuprite
153
179
  end
154
180
  end
155
181
 
156
- def find_position(node, *args)
157
- x, y = node.find_position(*args)
182
+ def find_position(node, **options)
183
+ x, y = node.find_position(**options)
158
184
  rescue Ferrum::BrowserError => e
159
185
  if e.message == "Could not compute content quads."
160
186
  raise MouseEventFailed.new("MouseEventFailed: click, none, 0, 0")
@@ -162,5 +188,13 @@ module Capybara::Cuprite
162
188
  raise
163
189
  end
164
190
  end
191
+
192
+ def active_frame
193
+ if @frame_stack.empty?
194
+ main_frame
195
+ else
196
+ @frames[@frame_stack.last]
197
+ end
198
+ end
165
199
  end
166
200
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Capybara
4
4
  module Cuprite
5
- VERSION = "0.7.0"
5
+ VERSION = "0.11"
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.7.0
4
+ version: '0.11'
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-09-12 00:00:00.000000000 Z
11
+ date: 2020-07-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: capybara
@@ -34,16 +34,16 @@ dependencies:
34
34
  name: ferrum
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
- - - ">="
37
+ - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: 0.2.1
39
+ version: 0.9.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.2.1
46
+ version: 0.9.0
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: image_size
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -189,7 +189,7 @@ 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
195
  metadata: {}
@@ -208,7 +208,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
208
208
  - !ruby/object:Gem::Version
209
209
  version: '0'
210
210
  requirements: []
211
- rubygems_version: 3.0.3
211
+ rubygems_version: 3.1.2
212
212
  signing_key:
213
213
  specification_version: 4
214
214
  summary: Headless Chrome driver for Capybara