cuprite 0.15 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '06038d0fa139b71bda9f95ce0d353bfbd0394ff6a09f95c8dc0acb4f0b7f86f9'
4
- data.tar.gz: 91a79735083b3949663d38d8ed1703c7955ada3ec2299830e7105348a72dd337
3
+ metadata.gz: 78b7dde58d02147e2985473da0a7527d338ff086e4a4aaf0fd9302e5faf3ee11
4
+ data.tar.gz: 777726c6161975950df923a305b0bdb9da54b5531b7a5339ed0d4beaf264beb6
5
5
  SHA512:
6
- metadata.gz: d1ab5955459610619f457b8ff7ac76fe761e003d20664da614f8b613da2849a65ba23c0c772753864321bf6b346a3e7d468da82dc55d816f33bbb7b13c6784ad
7
- data.tar.gz: e618d937daea3246787cef4084f07fd7a5b2109fd7e34ff0bfa7f3d7e954c3b258539338a52378e8f9a80d96e4a8d29de3041d568b5dac8850aba61abba614c4
6
+ metadata.gz: 04e88a47d9d2d2df68ad11dca8224f6d24001340b1743f538e6fdb64e818381037da7987b9db25284336b469c986d9d9e883192743749c4c6cad6c43adc160a1
7
+ data.tar.gz: a059dae9790af13c8a56999d6d76d5920e863e217b346a721a4051f0cc786e295a35c0e574c00e425b87e2d0c7a8b140c991183ab3246d1678566318745c9ecf
data/README.md CHANGED
@@ -4,9 +4,7 @@ Cuprite is a pure Ruby driver (read as _no_ Selenium/WebDriver/ChromeDriver
4
4
  dependency) for [Capybara](https://github.com/teamcapybara/capybara). It allows
5
5
  you to run Capybara tests on a headless Chrome or Chromium. Under the hood it
6
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.
7
+ to the browser by CDP protocol.
10
8
 
11
9
 
12
10
  ## Install
@@ -43,8 +41,7 @@ browser = page.driver.browser
43
41
  browser.mouse.move(x: 123, y: 456).down.up
44
42
  ```
45
43
 
46
- If you already have tests on Poltergeist then it should simply work, for
47
- Selenium you better check your code for `manage` calls because it works
44
+ For Selenium you better check your code for `manage` calls because it works
48
45
  differently in Cuprite, see the documentation below.
49
46
 
50
47
 
@@ -64,8 +61,8 @@ end
64
61
  `Cuprite`-specific options are:
65
62
 
66
63
  * options `Hash`
67
- * `:url_blacklist` (Array) - array of strings to match against requested URLs
68
- * `:url_whitelist` (Array) - array of strings to match against requested URLs
64
+ * `:url_blacklist` (Array) - array of regexes to match against requested URLs
65
+ * `:url_whitelist` (Array) - array of regexes to match against requested URLs
69
66
 
70
67
 
71
68
  ## Debugging
@@ -11,50 +11,63 @@ module Capybara
11
11
  find_modal accept_confirm dismiss_confirm accept_prompt
12
12
  dismiss_prompt reset_modals] => :page
13
13
 
14
- attr_reader :url_blacklist, :url_whitelist
15
- alias url_blocklist url_blacklist
16
- alias url_allowlist url_whitelist
17
-
18
14
  def initialize(options = nil)
19
- options ||= {}
20
- @client = nil
21
- self.url_blacklist = options[:url_blacklist]
22
- self.url_whitelist = options[:url_whitelist]
23
-
24
15
  super
25
- @page = false
16
+
17
+ @options.url_blacklist = prepare_wildcards(options&.dig(:url_blacklist))
18
+ @options.url_whitelist = prepare_wildcards(options&.dig(:url_whitelist))
19
+
20
+ @page = nil
26
21
  end
27
22
 
28
- def timeout=(value)
23
+ def command(...)
29
24
  super
30
- @page.timeout = value unless @page.nil?
25
+ rescue Ferrum::DeadBrowserError
26
+ restart
27
+ raise
31
28
  end
32
29
 
33
30
  def page
34
- raise Ferrum::NoSuchPageError if @page.nil?
31
+ raise Ferrum::NoSuchPageError if @page&.closed?
35
32
 
36
33
  @page ||= attach_page
37
34
  end
38
35
 
39
36
  def reset
40
37
  super
41
- @page = attach_page
38
+ @options.reset_window_size
39
+ @page = nil
42
40
  end
43
41
 
44
42
  def quit
45
43
  super
46
- @page = false
44
+ @page = nil
45
+ end
46
+
47
+ def resize(**options)
48
+ @options.window_size = [options[:width], options[:height]]
49
+ super
47
50
  end
48
51
 
52
+ def url_whitelist
53
+ @options.url_whitelist
54
+ end
55
+ alias url_allowlist url_whitelist
56
+
49
57
  def url_whitelist=(patterns)
50
- @url_whitelist = prepare_wildcards(patterns)
51
- page.network.whitelist = @url_whitelist if @client && @url_whitelist.any?
58
+ @options.url_whitelist = prepare_wildcards(patterns)
59
+ page.network.whitelist = @options.url_whitelist if @client && @options.url_whitelist.any?
52
60
  end
53
61
  alias url_allowlist= url_whitelist=
54
62
 
63
+ def url_blacklist
64
+ @options.url_blacklist
65
+ end
66
+ alias url_blocklist url_blacklist
67
+
55
68
  def url_blacklist=(patterns)
56
- @url_blacklist = prepare_wildcards(patterns)
57
- page.network.blacklist = @url_blacklist if @client && @url_blacklist.any?
69
+ @options.url_blacklist = prepare_wildcards(patterns)
70
+ page.network.blacklist = @options.url_blacklist if @client && @options.url_blacklist.any?
58
71
  end
59
72
  alias url_blocklist= url_blacklist=
60
73
 
@@ -109,8 +122,13 @@ module Capybara
109
122
  target = targets[target_id]
110
123
  raise Ferrum::NoSuchPageError unless target
111
124
 
112
- @page = nil if @page.target_id == target.id
125
+ @page = ClosedPage.new if @page.target_id == target.id
113
126
  target.page.close
127
+ targets.delete(target_id) # page.close is async, delete target asap
128
+ end
129
+
130
+ def active_element
131
+ evaluate("document.activeElement")
114
132
  end
115
133
 
116
134
  def browser_error
@@ -121,23 +139,25 @@ module Capybara
121
139
  raise NotImplementedError
122
140
  end
123
141
 
124
- def drag(node, other, steps)
142
+ def drag(node, other, steps, delay = nil)
125
143
  x1, y1 = node.find_position
126
144
  x2, y2 = other.find_position
127
145
 
128
146
  mouse.move(x: x1, y: y1)
129
147
  mouse.down
148
+ sleep delay if delay
130
149
  mouse.move(x: x2, y: y2, steps: steps)
131
150
  mouse.up
132
151
  end
133
152
 
134
- def drag_by(node, x, y, steps)
153
+ def drag_by(node, x, y, steps, delay = nil)
135
154
  x1, y1 = node.find_position
136
155
  x2 = x1 + x
137
156
  y2 = y1 + y
138
157
 
139
158
  mouse.move(x: x1, y: y1)
140
159
  mouse.down
160
+ sleep delay if delay
141
161
  mouse.move(x: x2, y: y2, steps: steps)
142
162
  mouse.up
143
163
  end
@@ -224,10 +244,10 @@ module Capybara
224
244
  def attach_page(target_id = nil)
225
245
  target = targets[target_id] if target_id
226
246
  target ||= default_context.default_target
227
- return target.page if target.attached?
247
+ return target.page if target.connected?
228
248
 
229
249
  target.maybe_sleep_if_new_window
230
- target.page = Page.new(target.id, self)
250
+ target.page = Page.new(target.client, context_id: target.context_id, target_id: target.id)
231
251
  target.page
232
252
  end
233
253
  end
@@ -15,6 +15,7 @@ module Capybara
15
15
  delegate %i[restart quit status_code timeout timeout= current_url title body
16
16
  window_handles close_window switch_to_window within_window window_handle
17
17
  back forward refresh wait_for_reload viewport_size device_pixel_ratio] => :browser
18
+ delegate %i[send_keys] => :active_element
18
19
  alias html body
19
20
  alias current_window_handle window_handle
20
21
  alias go_back back
@@ -34,7 +35,11 @@ module Capybara
34
35
  @screen_size ||= DEFAULT_MAXIMIZE_SCREEN_SIZE
35
36
  @options[:save_path] ||= File.expand_path(Capybara.save_path) if Capybara.save_path
36
37
 
37
- ENV["FERRUM_DEBUG"] = "true" if ENV["CUPRITE_DEBUG"]
38
+ # It's set for debug() to make devtools tab open correctly.
39
+ @options[:browser_options] ||= {}
40
+ unless @options[:browser_options][:"remote-allow-origins"]
41
+ @options[:browser_options].merge!("remote-allow-origins": "*")
42
+ end
38
43
 
39
44
  super()
40
45
  end
@@ -64,6 +69,10 @@ module Capybara
64
69
  evaluate_script("document.title")
65
70
  end
66
71
 
72
+ def active_element
73
+ Node.new(self, browser.active_element)
74
+ end
75
+
67
76
  def find_xpath(selector)
68
77
  find(:xpath, selector)
69
78
  end
@@ -109,7 +118,7 @@ module Capybara
109
118
  def open_new_window
110
119
  target = browser.default_context.create_target
111
120
  target.maybe_sleep_if_new_window
112
- target.page = Page.new(target.id, browser)
121
+ target.page = Page.new(target.client, context_id: target.context_id, target_id: target.id)
113
122
  target.page
114
123
  end
115
124
 
@@ -265,7 +274,12 @@ module Capybara
265
274
  alias authorize basic_authorize
266
275
 
267
276
  def debug_url
268
- "http://#{browser.process.host}:#{browser.process.port}"
277
+ response = JSON.parse(Net::HTTP.get(URI(build_remote_debug_url(path: "/json"))))
278
+
279
+ devtools_frontend_path = response[0]&.[]("devtoolsFrontendUrl")
280
+ raise "Could not generate debug url for remote debugging session" unless devtools_frontend_path
281
+
282
+ build_remote_debug_url(path: devtools_frontend_path)
269
283
  end
270
284
 
271
285
  def debug(binding = nil)
@@ -363,6 +377,10 @@ module Capybara
363
377
 
364
378
  private
365
379
 
380
+ def build_remote_debug_url(path:)
381
+ "http://#{browser.process.host}:#{browser.process.port}#{path}"
382
+ end
383
+
366
384
  def default_domain
367
385
  if @started
368
386
  URI.parse(browser.current_url).host
@@ -84,7 +84,6 @@ class Cuprite {
84
84
  return true;
85
85
  }
86
86
 
87
-
88
87
  isDisabled(node) {
89
88
  let xpath = "parent::optgroup[@disabled] | \
90
89
  ancestor::select[@disabled] | \
@@ -344,10 +343,24 @@ class Cuprite {
344
343
 
345
344
  _isInViewport(node) {
346
345
  let rect = node.getBoundingClientRect();
347
- return rect.top >= 0 &&
348
- rect.left >= 0 &&
349
- rect.bottom <= window.innerHeight &&
350
- rect.right <= window.innerWidth;
346
+
347
+ let inViewport = rect.top >= 0 &&
348
+ rect.left >= 0 &&
349
+ rect.bottom <= window.innerHeight &&
350
+ rect.right <= window.innerWidth;
351
+
352
+ if (inViewport) {
353
+ // check if obscured by another element
354
+ let x = rect.width/2;
355
+ let y = rect.height/2 ;
356
+
357
+ let px = rect.left + x,
358
+ py = rect.top + y,
359
+ e = document.elementFromPoint(px, py);
360
+ return node == e;
361
+ }
362
+
363
+ return false;
351
364
  }
352
365
 
353
366
  select(node, value) {
@@ -356,12 +369,13 @@ class Cuprite {
356
369
  } else if (value == false && !node.parentNode.multiple) {
357
370
  return false;
358
371
  } else {
359
- this.trigger(node.parentNode, "focus");
372
+ let parentNode = node.parentNode;
373
+ this.trigger(parentNode, "focus");
360
374
 
361
375
  node.selected = value;
362
376
  this.changed(node);
363
377
 
364
- this.trigger(node.parentNode, "blur");
378
+ this.trigger(parentNode, "blur");
365
379
  return true;
366
380
  }
367
381
  }
@@ -162,13 +162,13 @@ module Capybara
162
162
  def drag_to(other, **options)
163
163
  options[:steps] ||= 1
164
164
 
165
- command(:drag, other.node, options[:steps])
165
+ command(:drag, other.node, options[:steps], options[:delay])
166
166
  end
167
167
 
168
168
  def drag_by(x, y, **options)
169
169
  options[:steps] ||= 1
170
170
 
171
- command(:drag_by, x, y, options[:steps])
171
+ command(:drag_by, x, y, options[:steps], options[:delay])
172
172
  end
173
173
 
174
174
  def trigger(event)
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ferrum
4
+ class Browser
5
+ class Options
6
+ attr_writer :window_size
7
+ attr_accessor :url_blacklist, :url_whitelist
8
+
9
+ def reset_window_size
10
+ @window_size = @options[:window_size]
11
+ end
12
+ end
13
+ end
14
+ end
@@ -4,6 +4,12 @@ require "forwardable"
4
4
 
5
5
  module Capybara
6
6
  module Cuprite
7
+ class ClosedPage
8
+ def closed?
9
+ true
10
+ end
11
+ end
12
+
7
13
  class Page < Ferrum::Page
8
14
  MODAL_WAIT = ENV.fetch("CUPRITE_MODAL_WAIT", 0.05).to_f
9
15
  TRIGGER_CLICK_WAIT = ENV.fetch("CUPRITE_TRIGGER_CLICK_WAIT", 0.1).to_f
@@ -13,7 +19,7 @@ module Capybara
13
19
  current_url current_title body execution_id execution_id!
14
20
  evaluate evaluate_on evaluate_async execute] => :active_frame
15
21
 
16
- def initialize(*args)
22
+ def initialize(...)
17
23
  @frame_stack = []
18
24
  @accept_modal = []
19
25
  @modal_messages = []
@@ -70,17 +76,17 @@ module Capybara
70
76
 
71
77
  def find_modal(options)
72
78
  start = Ferrum::Utils::ElapsedTime.monotonic_time
73
- timeout = options.fetch(:wait, browser.timeout)
74
79
  expect_text = options[:text]
75
80
  expect_regexp = expect_text.is_a?(Regexp) ? expect_text : Regexp.escape(expect_text.to_s)
76
81
  not_found_msg = "Unable to find modal dialog"
77
82
  not_found_msg += " with #{expect_text}" if expect_text
83
+ wait = options.fetch(:wait, timeout)
78
84
 
79
85
  begin
80
86
  modal_text = @modal_messages.shift
81
87
  raise Capybara::ModalNotFound if modal_text.nil? || (expect_text && !modal_text.match(expect_regexp))
82
88
  rescue Capybara::ModalNotFound => e
83
- raise e, not_found_msg if Ferrum::Utils::ElapsedTime.timeout?(start, timeout)
89
+ raise e, not_found_msg if Ferrum::Utils::ElapsedTime.timeout?(start, wait)
84
90
 
85
91
  sleep(MODAL_WAIT)
86
92
  retry
@@ -129,15 +135,22 @@ module Capybara
129
135
  active_frame.current_title
130
136
  end
131
137
 
138
+ def closed?
139
+ false
140
+ end
141
+
132
142
  private
133
143
 
134
144
  def prepare_page
135
145
  super
136
146
 
137
- if @browser.url_blacklist.any?
138
- network.blacklist = @browser.url_blacklist
139
- elsif @browser.url_whitelist.any?
140
- network.whitelist = @browser.url_whitelist
147
+ width, height = @options.window_size
148
+ resize(width: width, height: height)
149
+
150
+ if @options.url_blacklist.any?
151
+ network.blacklist = @options.url_blacklist
152
+ elsif @options.url_whitelist.any?
153
+ network.whitelist = @options.url_whitelist
141
154
  end
142
155
 
143
156
  on("Page.javascriptDialogOpening") do |params|
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Capybara
4
4
  module Cuprite
5
- VERSION = "0.15"
5
+ VERSION = "0.15.1"
6
6
  end
7
7
  end
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ ENV["FERRUM_DEBUG"] = "true" if ENV["CUPRITE_DEBUG"]
4
+
3
5
  require "ferrum"
4
6
  require "capybara"
5
7
  require "capybara/cuprite/driver"
6
8
  require "capybara/cuprite/browser"
7
9
  require "capybara/cuprite/page"
10
+ require "capybara/cuprite/options"
8
11
  require "capybara/cuprite/node"
9
12
  require "capybara/cuprite/errors"
10
13
 
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.15'
4
+ version: 0.15.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Vorotilin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-11-04 00:00:00.000000000 Z
11
+ date: 2024-06-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: capybara
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.14.0
33
+ version: 0.15.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.14.0
40
+ version: 0.15.0
41
41
  description: Cuprite is a driver for Capybara that allows you to run your tests on
42
42
  a headless Chrome browser
43
43
  email:
@@ -55,6 +55,7 @@ files:
55
55
  - lib/capybara/cuprite/errors.rb
56
56
  - lib/capybara/cuprite/javascripts/index.js
57
57
  - lib/capybara/cuprite/node.rb
58
+ - lib/capybara/cuprite/options.rb
58
59
  - lib/capybara/cuprite/page.rb
59
60
  - lib/capybara/cuprite/version.rb
60
61
  homepage: https://github.com/rubycdp/cuprite
@@ -81,7 +82,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
81
82
  - !ruby/object:Gem::Version
82
83
  version: '0'
83
84
  requirements: []
84
- rubygems_version: 3.4.13
85
+ rubygems_version: 3.5.11
85
86
  signing_key:
86
87
  specification_version: 4
87
88
  summary: Headless Chrome driver for Capybara