cuprite 0.19 → 0.20

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.
@@ -1,181 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "forwardable"
3
+ require "capybara/cuprite/page/frames"
4
+ require "capybara/cuprite/page/modals"
4
5
 
5
6
  module Capybara
6
7
  module Cuprite
7
- class ClosedPage
8
- def closed?
9
- true
10
- end
11
- end
12
-
13
8
  class Page < Ferrum::Page
14
- MODAL_WAIT = ENV.fetch("CUPRITE_MODAL_WAIT", 0.05).to_f
15
- TRIGGER_CLICK_WAIT = ENV.fetch("CUPRITE_TRIGGER_CLICK_WAIT", 0.1).to_f
9
+ EXTENSION_JS = File.read(Driver::EXTENSION).freeze
16
10
 
17
- extend Forwardable
18
-
19
- delegate %i[at_css at_xpath css xpath
20
- current_url current_title body execution_id execution_id!
21
- evaluate evaluate_on evaluate_async execute] => :active_frame
22
-
23
- def initialize(...)
24
- @frame_stack = []
25
- @accept_modal = []
26
- @modal_messages = []
27
- @modal_response = nil
28
- @unhandled_modal_error = nil
29
- super
30
- end
31
-
32
- # Keep a handle to Ferrum's own implementation before overriding it
33
- # below, so answering a dialog (see `handle_javascript_dialog`) can
34
- # bypass our override.
35
- alias ferrum_command command
36
-
37
- # The `Page.javascriptDialogOpening` event is handled on Ferrum's
38
- # background CDP dispatcher thread, so raising there wouldn't reach
39
- # the caller and would permanently kill that thread instead. The
40
- # dialog is always accepted immediately from that thread, and the
41
- # error (if any) is stashed here to be raised from the main thread
42
- # the next time it makes a command round trip.
43
- def command(...)
44
- raise_pending_unhandled_modal_error! { super }
45
- end
11
+ include Frames
12
+ include Modals
46
13
 
47
14
  def set_viewport(width: 0, height: 0, scale_factor: @options.scale_factor, mobile: @options.mobile)
48
15
  super
49
16
  end
50
17
 
51
- def set(node, value)
52
- object_id = command("DOM.resolveNode", nodeId: node.node_id).dig("object", "objectId")
53
- evaluate("_cuprite.set(arguments[0], arguments[1])", { "objectId" => object_id }, value)
54
- end
55
-
56
- def select(node, value)
57
- evaluate_on(node: node, expression: "_cuprite.select(this, #{value})")
58
- end
59
-
60
- def trigger(node, event)
61
- options = {}
62
- options.merge!(wait: TRIGGER_CLICK_WAIT) if event.to_s == "click" && TRIGGER_CLICK_WAIT.positive?
63
- evaluate_on(node: node, expression: %(_cuprite.trigger(this, "#{event}")), **options)
64
- end
65
-
66
- def hover(node)
67
- evaluate_on(node: node, expression: "_cuprite.scrollIntoViewport(this)")
68
- x, y = find_position(node)
69
- command("Input.dispatchMouseEvent", type: "mouseMoved", x: x, y: y)
70
- end
71
-
72
- def send_keys(node, keys)
73
- unless evaluate_on(node: node, expression: %(_cuprite.containsSelection(this) || _cuprite.hasFocus(this)))
74
- before_click(node, "click")
75
- node.click(mode: :left, keys: keys)
76
- end
77
-
78
- keyboard.type(keys)
79
- end
80
-
81
- def accept_confirm
82
- @accept_modal << true
83
- end
84
-
85
- def dismiss_confirm
86
- @accept_modal << false
87
- end
88
-
89
- def accept_prompt(modal_response)
90
- @accept_modal << true
91
- @modal_response = modal_response
92
- end
93
-
94
- def dismiss_prompt
95
- @accept_modal << false
96
- end
97
-
98
- def find_modal(options)
99
- start = Ferrum::Utils::ElapsedTime.monotonic_time
100
- expect_text = options[:text]
101
- expect_regexp = expect_text.is_a?(Regexp) ? expect_text : Regexp.escape(expect_text.to_s)
102
- not_found_msg = "Unable to find modal dialog"
103
- not_found_msg += " with #{expect_text}" if expect_text
104
- wait = options.fetch(:wait, timeout)
105
-
106
- begin
107
- modal_text = @modal_messages.shift
108
- raise Capybara::ModalNotFound if modal_text.nil? || (expect_text && !modal_text.match(expect_regexp))
109
- rescue Capybara::ModalNotFound => e
110
- raise e, not_found_msg if Ferrum::Utils::ElapsedTime.timeout?(start, wait)
111
-
112
- sleep(MODAL_WAIT)
113
- retry
114
- end
115
-
116
- modal_text
117
- end
118
-
119
- def reset_modals
120
- @accept_modal = []
121
- @modal_response = nil
122
- @modal_messages = []
123
- @unhandled_modal_error = nil
124
- end
125
-
126
- def before_click(node, name, _keys = [], offset = {})
127
- evaluate_on(node: node, expression: "_cuprite.scrollIntoViewport(this)")
128
-
129
- # If offset is given it may go outside of the element and likely error
130
- # will be raised that we detected another element at this position.
131
- return true if offset[:x] || offset[:y]
132
-
133
- x, y = find_position(node, **offset)
134
- evaluate_on(node: node, expression: "_cuprite.mouseEventTest(this, '#{name}', #{x}, #{y})")
135
- true
136
- rescue Ferrum::JavaScriptError => e
137
- raise MouseEventFailed, e.message if e.class_name == "MouseEventFailed"
138
- end
139
-
140
- def switch_to_frame(handle)
141
- case handle
142
- when :parent
143
- @frame_stack.pop
144
- when :top
145
- @frame_stack = []
146
- else
147
- @frame_stack << handle
148
- end
149
- end
150
-
151
- def frame_name
152
- evaluate("window.name")
153
- end
154
-
155
- def title
156
- active_frame.current_title
157
- end
158
-
159
- def closed?
160
- false
161
- end
162
-
163
18
  private
164
19
 
165
- def raise_pending_unhandled_modal_error!
166
- error = @unhandled_modal_error
167
- @unhandled_modal_error = nil
168
- raise error if error
169
-
170
- yield
171
- ensure
172
- error = @unhandled_modal_error
173
- @unhandled_modal_error = nil
174
- raise error if error
175
- end
176
-
177
20
  def prepare_page
178
21
  super
22
+ execute(EXTENSION_JS)
179
23
 
180
24
  width, height = @options.window_size
181
25
  width && height ? resize(width: width, height: height) : set_viewport
@@ -186,53 +30,6 @@ module Capybara
186
30
  elsif @options.url_whitelist.any?
187
31
  network.whitelist = @options.url_whitelist
188
32
  end
189
-
190
- on("Page.javascriptDialogOpening") do |params|
191
- accept_modal = @accept_modal.last
192
- unhandled_modal_error = nil
193
-
194
- if [true, false].include?(accept_modal)
195
- @accept_modal.pop
196
- @modal_messages << params["message"]
197
- options = { accept: accept_modal }
198
- response = @modal_response || params["defaultPrompt"]
199
- else
200
- with_text = params["message"] ? "with text `#{params['message']}` " : ""
201
- message = "Modal window #{with_text}has been opened, but you didn't wrap " \
202
- "your code into (`accept_prompt` | `dismiss_prompt` | " \
203
- "`accept_confirm` | `dismiss_confirm` | `accept_alert`), " \
204
- "accepting by default"
205
-
206
- if @options.raise_on_unhandled_modal
207
- unhandled_modal_error = UnhandledModalError.new(message)
208
- else
209
- warn message
210
- end
211
-
212
- options = { accept: true }
213
- response = params["defaultPrompt"]
214
- end
215
- options.merge!(promptText: response) if response
216
- ferrum_command("Page.handleJavaScriptDialog", **options)
217
-
218
- @unhandled_modal_error = unhandled_modal_error if unhandled_modal_error
219
- end
220
- end
221
-
222
- def find_position(node, **options)
223
- node.find_position(**options)
224
- rescue Ferrum::BrowserError => e
225
- raise MouseEventFailed, "MouseEventFailed: click, none, 0, 0" if e.message == "Could not compute content quads."
226
-
227
- raise
228
- end
229
-
230
- def active_frame
231
- if @frame_stack.empty?
232
- main_frame
233
- else
234
- @frames[@frame_stack.last]
235
- end
236
33
  end
237
34
  end
238
35
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Capybara
4
4
  module Cuprite
5
- VERSION = "0.19"
5
+ VERSION = "0.20"
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.19'
4
+ version: '0.20'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Vorotilin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-16 00:00:00.000000000 Z
11
+ date: 2026-10-30 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.19.1
33
+ version: 0.20.1
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.19.1
40
+ version: 0.20.1
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:
@@ -50,15 +50,24 @@ files:
50
50
  - README.md
51
51
  - lib/capybara/cuprite.rb
52
52
  - lib/capybara/cuprite/browser.rb
53
- - lib/capybara/cuprite/cookie.rb
53
+ - lib/capybara/cuprite/browser/deprecations.rb
54
+ - lib/capybara/cuprite/browser/extensions.rb
55
+ - lib/capybara/cuprite/deprecation.rb
54
56
  - lib/capybara/cuprite/devices.rb
55
57
  - lib/capybara/cuprite/driver.rb
58
+ - lib/capybara/cuprite/driver/deprecations.rb
59
+ - lib/capybara/cuprite/driver/extensions.rb
56
60
  - lib/capybara/cuprite/errors.rb
61
+ - lib/capybara/cuprite/finder.rb
57
62
  - lib/capybara/cuprite/javascripts/drag.js
58
63
  - lib/capybara/cuprite/javascripts/index.js
59
64
  - lib/capybara/cuprite/node.rb
65
+ - lib/capybara/cuprite/node/deprecations.rb
66
+ - lib/capybara/cuprite/node/extensions.rb
60
67
  - lib/capybara/cuprite/options.rb
61
68
  - lib/capybara/cuprite/page.rb
69
+ - lib/capybara/cuprite/page/frames.rb
70
+ - lib/capybara/cuprite/page/modals.rb
62
71
  - lib/capybara/cuprite/version.rb
63
72
  homepage: https://github.com/rubycdp/cuprite
64
73
  licenses:
@@ -77,7 +86,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
77
86
  requirements:
78
87
  - - ">="
79
88
  - !ruby/object:Gem::Version
80
- version: '3.1'
89
+ version: '3.2'
81
90
  required_rubygems_version: !ruby/object:Gem::Requirement
82
91
  requirements:
83
92
  - - ">="
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Capybara
4
- module Cuprite
5
- class Cookie
6
- def initialize(attributes)
7
- @attributes = attributes
8
- end
9
-
10
- def name
11
- @attributes["name"]
12
- end
13
-
14
- def value
15
- @attributes["value"]
16
- end
17
-
18
- def domain
19
- @attributes["domain"]
20
- end
21
-
22
- def path
23
- @attributes["path"]
24
- end
25
-
26
- def size
27
- @attributes["size"]
28
- end
29
-
30
- def secure?
31
- @attributes["secure"]
32
- end
33
-
34
- def httponly?
35
- @attributes["httpOnly"]
36
- end
37
-
38
- def session?
39
- @attributes["session"]
40
- end
41
-
42
- def expires
43
- Time.at(@attributes["expires"]) if @attributes["expires"].positive?
44
- end
45
- end
46
- end
47
- end