cuprite 0.15 → 0.15.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: '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