cuprite 0.13 → 0.14

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,295 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "forwardable"
4
-
5
- module Capybara::Cuprite
6
- class Node < Capybara::Driver::Node
7
- attr_reader :node
8
-
9
- extend Forwardable
10
-
11
- delegate %i(description) => :node
12
- delegate %i(browser) => :driver
13
-
14
- def initialize(driver, node)
15
- super(driver, self)
16
- @node = node
17
- end
18
-
19
- def command(name, *args)
20
- browser.send(name, node, *args)
21
- rescue Ferrum::NodeNotFoundError => e
22
- raise ObsoleteNode.new(self, e.response)
23
- rescue Ferrum::BrowserError => e
24
- case e.message
25
- when "Cuprite.MouseEventFailed"
26
- raise MouseEventFailed.new(self, e.response)
27
- else
28
- raise
29
- end
30
- end
31
-
32
- def parents
33
- command(:parents).map do |parent|
34
- self.class.new(driver, parent)
35
- end
36
- end
37
-
38
- def find_xpath(selector)
39
- find(:xpath, selector)
40
- end
41
-
42
- def find_css(selector)
43
- find(:css, selector)
44
- end
45
-
46
- def find(method, selector)
47
- command(:find_within, method, selector).map do |node|
48
- self.class.new(driver, node)
49
- end
50
- end
51
-
52
- def all_text
53
- filter_text(command(:all_text))
54
- end
55
-
56
- def visible_text
57
- if Capybara::VERSION.to_f < 3.0
58
- filter_text(command(:visible_text))
59
- else
60
- command(:visible_text).to_s
61
- .gsub(/\A[[:space:]&&[^\u00a0]]+/, "")
62
- .gsub(/[[:space:]&&[^\u00a0]]+\z/, "")
63
- .gsub(/\n+/, "\n")
64
- .tr("\u00a0", " ")
65
- end
66
- end
67
-
68
- def property(name)
69
- command(:property, name)
70
- end
71
-
72
- def [](name)
73
- # Although the attribute matters, the property is consistent. Return that in
74
- # preference to the attribute for links and images.
75
- if (tag_name == "img" && name == "src") ||
76
- (tag_name == "a" && name == "href")
77
- # if attribute exists get the property
78
- return command(:attribute, name) && command(:property, name)
79
- end
80
-
81
- value = property(name)
82
- value = command(:attribute, name) if value.nil? || value.is_a?(Hash)
83
-
84
- value
85
- end
86
-
87
- def attributes
88
- command(:attributes)
89
- end
90
-
91
- def value
92
- command(:value)
93
- end
94
-
95
- def set(value, options = {})
96
- warn "Options passed to Node#set but Cuprite doesn't currently support any - ignoring" unless options.empty?
97
-
98
- if tag_name == "input"
99
- case self[:type]
100
- when "radio"
101
- click
102
- when "checkbox"
103
- click if value != checked?
104
- when "file"
105
- files = value.respond_to?(:to_ary) ? value.to_ary.map(&:to_s) : value.to_s
106
- command(:select_file, files)
107
- when "color"
108
- node.evaluate("this.setAttribute('value', '#{value}')")
109
- else
110
- command(:set, value.to_s)
111
- end
112
- elsif tag_name == "textarea"
113
- command(:set, value.to_s)
114
- elsif self[:isContentEditable]
115
- command(:delete_text)
116
- send_keys(value.to_s)
117
- end
118
- end
119
-
120
- def select_option
121
- command(:select, true)
122
- end
123
-
124
- def unselect_option
125
- command(:select, false) ||
126
- raise(Capybara::UnselectNotAllowed, "Cannot unselect option from single select box.")
127
- end
128
-
129
- def tag_name
130
- @tag_name ||= description["nodeName"].downcase
131
- end
132
-
133
- def visible?
134
- command(:visible?)
135
- end
136
-
137
- def checked?
138
- self[:checked]
139
- end
140
-
141
- def selected?
142
- !!self[:selected]
143
- end
144
-
145
- def disabled?
146
- command(:disabled?)
147
- end
148
-
149
- def click(keys = [], **options)
150
- prepare_and_click(:left, __method__, keys, options)
151
- end
152
-
153
- def right_click(keys = [], **options)
154
- prepare_and_click(:right, __method__, keys, options)
155
- end
156
-
157
- def double_click(keys = [], **options)
158
- prepare_and_click(:double, __method__, keys, options)
159
- end
160
-
161
- def hover
162
- command(:hover)
163
- end
164
-
165
- def drag_to(other)
166
- command(:drag, other)
167
- end
168
-
169
- def drag_by(x, y)
170
- command(:drag_by, x, y)
171
- end
172
-
173
- def trigger(event)
174
- command(:trigger, event)
175
- end
176
-
177
- def scroll_to(element, location, position = nil)
178
- if element.is_a?(Node)
179
- scroll_element_to_location(element, location)
180
- elsif location.is_a?(Symbol)
181
- scroll_to_location(location)
182
- else
183
- scroll_to_coords(*position)
184
- end
185
- self
186
- end
187
-
188
- def scroll_by(x, y)
189
- driver.execute_script <<~JS, self, x, y
190
- var el = arguments[0];
191
- if (el.scrollBy){
192
- el.scrollBy(arguments[1], arguments[2]);
193
- } else {
194
- el.scrollTop = el.scrollTop + arguments[2];
195
- el.scrollLeft = el.scrollLeft + arguments[1];
196
- }
197
- JS
198
- end
199
-
200
- def ==(other)
201
- node == other.native.node
202
- end
203
-
204
- def send_keys(*keys)
205
- command(:send_keys, keys)
206
- end
207
- alias_method :send_key, :send_keys
208
-
209
- def path
210
- command(:path)
211
- end
212
-
213
- def inspect
214
- %(#<#{self.class} @node=#{@node.inspect}>)
215
- end
216
-
217
- # @api private
218
- def to_json(*)
219
- JSON.generate(as_json)
220
- end
221
-
222
- # @api private
223
- def as_json(*)
224
- # FIXME: Where is this method used and why attr is called id?
225
- { ELEMENT: { node: node, id: node.node_id } }
226
- end
227
-
228
- private
229
-
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 }
234
- command(:before_click, name, keys, offset)
235
- node.click(mode: mode, keys: keys, offset: offset, delay: delay)
236
- end
237
-
238
- def filter_text(text)
239
- if Capybara::VERSION.to_f < 3
240
- Capybara::Helpers.normalize_whitespace(text.to_s)
241
- else
242
- text.gsub(/[\u200b\u200e\u200f]/, "")
243
- .gsub(/[\ \n\f\t\v\u2028\u2029]+/, " ")
244
- .gsub(/\A[[:space:]&&[^\u00a0]]+/, "")
245
- .gsub(/[[:space:]&&[^\u00a0]]+\z/, "")
246
- .tr("\u00a0", " ")
247
- end
248
- end
249
-
250
- def scroll_element_to_location(element, location)
251
- scroll_opts = case location
252
- when :top
253
- 'true'
254
- when :bottom
255
- 'false'
256
- when :center
257
- "{behavior: 'instant', block: 'center'}"
258
- else
259
- raise ArgumentError, "Invalid scroll_to location: #{location}"
260
- end
261
- driver.execute_script <<~JS, element
262
- arguments[0].scrollIntoView(#{scroll_opts})
263
- JS
264
- end
265
-
266
- def scroll_to_location(location)
267
- scroll_y = case location
268
- when :top
269
- '0'
270
- when :bottom
271
- 'arguments[0].scrollHeight'
272
- when :center
273
- '(arguments[0].scrollHeight - arguments[0].clientHeight)/2'
274
- end
275
- driver.execute_script <<~JS, self
276
- if (arguments[0].scrollTo){
277
- arguments[0].scrollTo(0, #{scroll_y});
278
- } else {
279
- arguments[0].scrollTop = #{scroll_y};
280
- }
281
- JS
282
- end
283
-
284
- def scroll_to_coords(x, y)
285
- driver.execute_script <<~JS, self, x, y
286
- if (arguments[0].scrollTo){
287
- arguments[0].scrollTo(arguments[1], arguments[2]);
288
- } else {
289
- arguments[0].scrollTop = arguments[2];
290
- arguments[0].scrollLeft = arguments[1];
291
- }
292
- JS
293
- end
294
- end
295
- end
@@ -1,200 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "forwardable"
4
-
5
- module Capybara::Cuprite
6
- class Page < Ferrum::Page
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
14
-
15
- def initialize(*args)
16
- @frame_stack = []
17
- @accept_modal = []
18
- @modal_messages = []
19
- super
20
- end
21
-
22
- def set(node, value)
23
- object_id = command("DOM.resolveNode", nodeId: node.node_id).dig("object", "objectId")
24
- evaluate("_cuprite.set(arguments[0], arguments[1])", { "objectId" => object_id }, value)
25
- end
26
-
27
- def select(node, value)
28
- evaluate_on(node: node, expression: "_cuprite.select(this, #{value})")
29
- end
30
-
31
- def trigger(node, event)
32
- options = {}
33
- options.merge!(wait: TRIGGER_CLICK_WAIT) if event.to_s == "click" && TRIGGER_CLICK_WAIT > 0
34
- evaluate_on(node: node, expression: %(_cuprite.trigger(this, "#{event}")), **options)
35
- end
36
-
37
- def hover(node)
38
- evaluate_on(node: node, expression: "_cuprite.scrollIntoViewport(this)")
39
- x, y = find_position(node)
40
- command("Input.dispatchMouseEvent", type: "mouseMoved", x: x, y: y)
41
- end
42
-
43
- def send_keys(node, keys)
44
- if !evaluate_on(node: node, expression: %(_cuprite.containsSelection(this)))
45
- before_click(node, "click")
46
- node.click(mode: :left, keys: keys)
47
- end
48
-
49
- keyboard.type(keys)
50
- end
51
-
52
- def accept_confirm
53
- @accept_modal << true
54
- end
55
-
56
- def dismiss_confirm
57
- @accept_modal << false
58
- end
59
-
60
- def accept_prompt(modal_response)
61
- @accept_modal << true
62
- @modal_response = modal_response
63
- end
64
-
65
- def dismiss_prompt
66
- @accept_modal << false
67
- end
68
-
69
- def find_modal(options)
70
- start = Ferrum.monotonic_time
71
- timeout = options.fetch(:wait, browser.timeout)
72
- expect_text = options[:text]
73
- expect_regexp = expect_text.is_a?(Regexp) ? expect_text : Regexp.escape(expect_text.to_s)
74
- not_found_msg = "Unable to find modal dialog"
75
- not_found_msg += " with #{expect_text}" if expect_text
76
-
77
- begin
78
- modal_text = @modal_messages.shift
79
- raise Capybara::ModalNotFound if modal_text.nil? || (expect_text && !modal_text.match(expect_regexp))
80
- rescue Capybara::ModalNotFound => e
81
- raise e, not_found_msg if Ferrum.timeout?(start, timeout)
82
- sleep(MODAL_WAIT)
83
- retry
84
- end
85
-
86
- modal_text
87
- end
88
-
89
- def reset_modals
90
- @accept_modal = []
91
- @modal_response = nil
92
- @modal_messages = []
93
- end
94
-
95
- def before_click(node, name, keys = [], offset = {})
96
- evaluate_on(node: node, expression: "_cuprite.scrollIntoViewport(this)")
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)
103
- evaluate_on(node: node, expression: "_cuprite.mouseEventTest(this, '#{name}', #{x}, #{y})")
104
- true
105
- rescue Ferrum::JavaScriptError => e
106
- raise MouseEventFailed.new(e.message) if e.class_name == "MouseEventFailed"
107
- end
108
-
109
- def switch_to_frame(handle)
110
- case handle
111
- when :parent
112
- @frame_stack.pop
113
- when :top
114
- @frame_stack = []
115
- else
116
- @frame_stack << handle
117
- inject_extensions
118
- end
119
- end
120
-
121
- def frame_name
122
- evaluate("window.name")
123
- end
124
-
125
- def title
126
- active_frame.current_title
127
- end
128
-
129
- private
130
-
131
- def prepare_page
132
- super
133
-
134
- network.intercept if !Array(@browser.url_whitelist).empty? ||
135
- !Array(@browser.url_blacklist).empty?
136
-
137
- on(:request) do |request, index, total|
138
- if @browser.url_blacklist && !@browser.url_blacklist.empty?
139
- if @browser.url_blacklist.any? { |r| request.match?(r) }
140
- request.abort and next
141
- else
142
- request.continue and next
143
- end
144
- elsif @browser.url_whitelist && !@browser.url_whitelist.empty?
145
- if @browser.url_whitelist.any? { |r| request.match?(r) }
146
- request.continue and next
147
- else
148
- request.abort and next
149
- end
150
- elsif index + 1 < total
151
- # There are other callbacks that may handle this request
152
- next
153
- else
154
- # If there are no callbacks then just continue
155
- request.continue
156
- end
157
- end
158
-
159
- on("Page.javascriptDialogOpening") do |params|
160
- accept_modal = @accept_modal.last
161
- if accept_modal == true || accept_modal == false
162
- @accept_modal.pop
163
- @modal_messages << params["message"]
164
- options = { accept: accept_modal }
165
- response = @modal_response || params["defaultPrompt"]
166
- options.merge!(promptText: response) if response
167
- command("Page.handleJavaScriptDialog", **options)
168
- else
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"
174
- options = { accept: true }
175
- response = params["defaultPrompt"]
176
- options.merge!(promptText: response) if response
177
- command("Page.handleJavaScriptDialog", **options)
178
- end
179
- end
180
- end
181
-
182
- def find_position(node, **options)
183
- x, y = node.find_position(**options)
184
- rescue Ferrum::BrowserError => e
185
- if e.message == "Could not compute content quads."
186
- raise MouseEventFailed.new("MouseEventFailed: click, none, 0, 0")
187
- else
188
- raise
189
- end
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
199
- end
200
- end
@@ -1,7 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Capybara
4
- module Cuprite
5
- VERSION = "0.13"
6
- end
7
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "ferrum"
4
- require "capybara"
5
- require "capybara/cuprite/driver"
6
- require "capybara/cuprite/browser"
7
- require "capybara/cuprite/page"
8
- require "capybara/cuprite/node"
9
- require "capybara/cuprite/errors"
10
-
11
- Capybara.register_driver(:cuprite) do |app|
12
- Capybara::Cuprite::Driver.new(app)
13
- end