cuprite 0.13 → 0.14

Sign up to get free protection for your applications and to get access to all the features.
@@ -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