Class: Capybara::Chrome::Node

Inherits:
Driver::Node
  • Object
show all
Includes:
Debug
Defined in:
lib/capybara/chrome/node.rb

Constant Summary collapse

KEY_DATA =
Hash.new do |h, k|
  h[k] = {text: k.to_s}
end.merge!({
  cancel: { key_code: 3, code: "Abort", key: "Cancel"},
  help: { key_code: 6, code: "Help", key: "Help"},
  backspace: { key_code: 8, code: "Backspace", key: "Backspace"},
  tab: { key_code: 9, code: "Tab", key: "Tab"},
  delete: { key_code: 46, code: "Delete", key: "Delete"},
  home: { key_code: 36, code: "Home", key: "Home"},
  end: { key_code: 35, code: "End", key: "End"},
  left: { key_code: 37, code: "ArrowLeft", key: "ArrowLeft"},
  right: { key_code: 39, code: "ArrowRight", key: "ArrowRight"},
  up: { key_code: 38, code: "ArrowUp", key: "ArrowUp"},
  down: { key_code: 40, code: "ArrowDown", key: "ArrowDown"},
  return: { key_code: 13, code: "Enter", key: "Enter"},
  enter: { key_code: 13, code: "Enter", key: "Enter"},
  "\r" => { key_code: 13, code: "Enter", key: "Enter", text: "\r"},
  "\n" => { key_code: 13, code: "Enter", key: "Enter", text: "\r"},
  space: { key_code: 32, code: "Space", key: " ", text: " "},
  shift: { key_code: 16, code: "Shift", key: "ShiftLeft", location: 1},
  control: { key_code: 17, code: "ControlLeft", key: "Control", text: "\r"},
  alt: { key_code: 18, code: "AltLeft", key: "Alt", location: 1},
  meta: { key_code: 91, code: "MetaLeft", key: "Meta", location: 1},
})
TEXT_TYPES =
%w(date email number password search tel text textarea url)

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Debug

#debug, #info

Constructor Details

#initialize(driver, browser, id) ⇒ Node

Returns a new instance of Node.



31
32
33
34
35
36
37
38
# File 'lib/capybara/chrome/node.rb', line 31

def initialize(driver, browser, id)
  raise "hell" if id == 0
  @driver = driver
  @browser = browser
  @id = id
  @mouse_x = 0
  @mouse_y = 0
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args) ⇒ Object



273
274
275
276
# File 'lib/capybara/chrome/node.rb', line 273

def method_missing(method, *args)
  debug ["method missing", method, args]
  raise "method missing #{method} #{args.inspect}"
end

Instance Attribute Details

#browserObject (readonly)

Returns the value of attribute browser.



4
5
6
# File 'lib/capybara/chrome/node.rb', line 4

def browser
  @browser
end

#idObject (readonly)

Returns the value of attribute id.



4
5
6
# File 'lib/capybara/chrome/node.rb', line 4

def id
  @id
end

Instance Method Details

#[](attr) ⇒ Object



278
279
280
# File 'lib/capybara/chrome/node.rb', line 278

def [](attr)
  on_self_value %(return this.getAttribute("#{attr}") )
end

#all_textObject



45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/capybara/chrome/node.rb', line 45

def all_text
  browser.evaluate_script %( ChromeRemoteHelper.waitDOMContentLoaded(); )
  text = browser.evaluate_script %( ChromeRemoteHelper.onSelfValue(#{id}, "textContent") )
  if Capybara::VERSION.to_f < 3.0
    Capybara::Helpers.normalize_whitespace(text)
  else
    text.gsub(/[\u200b\u200e\u200f]/, '')
      .gsub(/[\ \n\f\t\v\u2028\u2029]+/, ' ')
      .gsub(/\A[[:space:]&&[^\u00a0]]+/, "")
      .gsub(/[[:space:]&&[^\u00a0]]+\z/, "")
      .tr("\u00a0", ' ')
  end
end

#checked?Boolean

Returns:

  • (Boolean)


234
235
236
# File 'lib/capybara/chrome/node.rb', line 234

def checked?
  val = browser.evaluate_script %( ChromeRemoteHelper.onSelfValue(#{id}, "checked") )
end

#clickObject



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/capybara/chrome/node.rb', line 107

def click
  browser.with_retry do
    on_self("this.scrollIntoViewIfNeeded();");
    dim = get_dimensions
    if dim["width"] == 0 && dim["height"] == 0
      puts "DIM IS 0"
      puts html
      raise Capybara::ElementNotFound
    end
    xd = [1, dim["width"]/2, dim["width"]/3]
    yd = [1, dim["height"]/2, dim["height"]/3]
    strategy = rand(0..xd.size-1)
    cx = (dim["x"] + xd[strategy]).floor
    cy = (dim["y"] + yd[strategy]).floor
    move_mouse(cx, cy, steps: [cx, cy].max)
    expect_node_at_position(cx, cy)
    send_cmd! "Input.dispatchMouseEvent", type: "mousePressed", x: cx, y: cy, clickCount: 1, button: "left"
    send_cmd! "Input.dispatchMouseEvent", type: "mouseReleased", x: cx, y: cy, clickCount: 1, button: "left"
    vv = browser.wait_for_load
  end
end

#disabled?Boolean

Returns:

  • (Boolean)


142
143
144
145
146
# File 'lib/capybara/chrome/node.rb', line 142

def disabled?
  val = browser.evaluate_script %( ChromeRemoteHelper.onSelfValue(#{id}, "disabled") )
  debug val
  val
end

#expect_node_at_position(x, y) ⇒ Object



88
89
90
91
92
93
# File 'lib/capybara/chrome/node.rb', line 88

def expect_node_at_position(x,y)
  in_position = browser.evaluate_script %( ChromeRemoteHelper.nodeIsNodeAtPosition(#{id}, {relativeX: #{x}, relativeY: #{y}}) )
  if !in_position
    raise "Element '<#{tag_name}>' not at expected position: #{x},#{y}"
  end
end

#find_css(query) ⇒ Object



129
130
131
# File 'lib/capybara/chrome/node.rb', line 129

def find_css(query)
  browser.query_selector_all(query, id)
end

#find_xpath(query) ⇒ Object



133
134
135
# File 'lib/capybara/chrome/node.rb', line 133

def find_xpath(query)
  browser.find_xpath query, id
end

#focusObject



225
226
227
# File 'lib/capybara/chrome/node.rb', line 225

def focus
  val = browser.evaluate_script %( ChromeRemoteHelper.onSelfValue(#{id}, "focus") )
end

#get_dimensionsObject



81
82
83
84
85
86
# File 'lib/capybara/chrome/node.rb', line 81

def get_dimensions
  browser.wait_for_load
  val = browser.evaluate_script %( ChromeRemoteHelper.nodeGetDimensions(#{id}) )
  val = JSON.parse(val) rescue {}
  val
end

#get_key_data(key) ⇒ Object



195
196
197
# File 'lib/capybara/chrome/node.rb', line 195

def get_key_data(key)
  KEY_DATA[key]
end

#get_modifiers(ary) ⇒ Object



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/capybara/chrome/node.rb', line 205

def get_modifiers(ary)
  mods = []
  keys = []
  ary.each do |k|
    case k
    when :alt
      mods << 1
    when :control
      mods << 2
    when :meta, :command
      mods << 4
    when :shift
      mods << 8
    else
      keys << k
    end
  end
  [mods.inject(&:|), keys]
end

#htmlObject



40
41
42
43
# File 'lib/capybara/chrome/node.rb', line 40

def html
  browser.evaluate_script %( ChromeRemoteHelper.waitDOMContentLoaded(); )
  browser.evaluate_script %( ChromeRemoteHelper.onSelfValue(#{id}, "outerHTML") )
end

#is_connected?Boolean

Returns:

  • (Boolean)


76
77
78
79
# File 'lib/capybara/chrome/node.rb', line 76

def is_connected?
  # on_self_value %( return this.isConnected )
  val = browser.evaluate_script %( ChromeRemoteHelper.onSelfValue(#{id}, "isConnected") )
end

#move_mouse(x, y, steps: 1) ⇒ Object



95
96
97
98
99
100
101
102
103
104
105
# File 'lib/capybara/chrome/node.rb', line 95

def move_mouse(x, y, steps: 1)
  if steps >= 1
    (0..x).step(steps).each do |dx|
      send_cmd! "Input.dispatchMouseEvent", type: "mouseMoved", x: dx, y: 0
    end
    (0..y).step(steps).each do |dy|
      send_cmd! "Input.dispatchMouseEvent", type: "mouseMoved", x: x, y: dy
    end
  end
  send_cmd! "Input.dispatchMouseEvent", type: "mouseMoved", x: x, y: y
end

#node_descriptionObject



282
283
284
285
286
287
288
289
290
# File 'lib/capybara/chrome/node.rb', line 282

def node_description
  # return @node_description if @node_description
  @node_description = send_cmd("DOM.describeNode", nodeId: id)
  debug ["node_desc", @node_description, id]
  if @node_description.nil?
    raise Capybara::ExpectationNotMet
  end
  @node_description
end

#node_idObject



167
168
169
170
171
# File 'lib/capybara/chrome/node.rb', line 167

def node_id
  browser.get_document
  val = browser.execute_script %(ChromeRemoteHelper.onSelf(#{id}, "return this;"))
  send_cmd("DOM.requestNode", objectId: val["result"]["objectId"])["nodeId"]
end

#on_self(function_body, options = {}) ⇒ Object



310
311
312
313
314
# File 'lib/capybara/chrome/node.rb', line 310

def on_self(function_body, options={})
  function_body = function_body.gsub('"', '\"').gsub(/\s+/, " ")
  browser.evaluate_script %( window.ChromeRemoteHelper && ChromeRemoteHelper.waitDOMContentLoaded(); )
  browser.evaluate_script %(window.ChromeRemoteHelper && ChromeRemoteHelper.onSelf(#{id}, "#{function_body}"))
end

#on_self!(function_body, options = {}) ⇒ Object



316
317
318
319
320
321
322
# File 'lib/capybara/chrome/node.rb', line 316

def on_self!(function_body, options={})
  function_body = function_body.gsub('"', '\"').gsub(/\s+/, " ")

  browser.evaluate_script %( ChromeRemoteHelper.waitDOMContentLoaded(); )

  browser.execute_script! %(ChromeRemoteHelper.onSelf(#{id}, "#{function_body}"))
end

#on_self_value(function_body, options = {}) ⇒ Object



305
306
307
308
# File 'lib/capybara/chrome/node.rb', line 305

def on_self_value(function_body, options={})
  function_body = function_body.gsub('"', '\"').gsub(/\s+/, " ")
  browser.evaluate_script %(ChromeRemoteHelper.onSelf(#{id}, "#{function_body}"))
end

#pathObject Also known as: get_xpath



137
138
139
# File 'lib/capybara/chrome/node.rb', line 137

def path
  browser.evaluate_script %( ChromeRemoteHelper.nodePathForNode(#{id}) )
end

#raw_textObject



68
69
70
# File 'lib/capybara/chrome/node.rb', line 68

def raw_text
  browser.evaluate_script %( ChromeRemoteHelper.nodeText(#{id}) )
end

#remote_objectObject



328
329
330
331
332
333
334
335
336
# File 'lib/capybara/chrome/node.rb', line 328

def remote_object
  return @remote_object if @remote_object
  @remote_object = send_cmd("DOM.resolveNode", nodeId: id)
  debug @remote_object, id
  if @remote_object.nil?
    raise Capybara::ExpectationNotMet
  end
  @remote_object
end

#remote_object_idObject



324
325
326
# File 'lib/capybara/chrome/node.rb', line 324

def remote_object_id
  remote_object["object"]["objectId"]
end

#request_node(remote_object_id) ⇒ Object



338
339
340
# File 'lib/capybara/chrome/node.rb', line 338

def request_node(remote_object_id)
  @request_node ||= send_cmd("DOM.requestNode", objectId: remote_object_id)
end

#select_option(*args) ⇒ Object



246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/capybara/chrome/node.rb', line 246

def select_option(*args)
  on_self_value %(
    if (this.disabled)
      return;

    var selectNode = this.parentNode;
    if (selectNode.tagName == "OPTGROUP")
      selectNode = selectNode.parentNode;

    ChromeRemoteHelper.dispatchEvent(selectNode, "mousedown");
    selectNode.focus();
    ChromeRemoteHelper.dispatchEvent(selectNode, "input");

    if (!this.selected) {
      this.selected = true;
      ChromeRemoteHelper.dispatchEvent(selectNode, "change");
    }

    ChromeRemoteHelper.dispatchEvent(selectNode, "mouseup");
    ChromeRemoteHelper.dispatchEvent(selectNode, "click");
  )
end

#selected?Boolean

Returns:

  • (Boolean)


238
239
240
# File 'lib/capybara/chrome/node.rb', line 238

def selected?
  val = browser.evaluate_script %( ChromeRemoteHelper.onSelfValue(#{id}, "selected") )
end

#send_cmd(cmd, args = {}) ⇒ Object



298
299
300
# File 'lib/capybara/chrome/node.rb', line 298

def send_cmd(cmd, args={})
  browser.remote.send_cmd(cmd, args)
end

#send_cmd!(cmd, args = {}) ⇒ Object



301
302
303
# File 'lib/capybara/chrome/node.rb', line 301

def send_cmd!(cmd, args={})
  browser.remote.send_cmd!(cmd, args)
end

#send_key_data(data, modifiers: 0) ⇒ Object



199
200
201
202
203
# File 'lib/capybara/chrome/node.rb', line 199

def send_key_data(data, modifiers: 0)
  type = data[:text] ? "keyDown" : "rawKeyDown"
  send_cmd! "Input.dispatchKeyEvent", {type: type, text: data[:text].to_s, windowsVirtualKeyCode: data[:key_code].to_i, code: data[:code].to_s, key: data[:key].to_s, modifiers: modifiers}
  send_cmd! "Input.dispatchKeyEvent", {type: "keyUp", modifiers: modifiers}
end

#send_keys(*keys) ⇒ Object



182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/capybara/chrome/node.rb', line 182

def send_keys(*keys)
  keys.each do |key|
    if key.is_a? Array
      mods, new_keys = get_modifiers(key)
      new_keys.each do |k|
        send_key_data(get_key_data(k), modifiers: mods)
      end
    else
      send_key_data(get_key_data(key))
    end
  end
end

#set(value, options = {}) ⇒ Object



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/capybara/chrome/node.rb', line 149

def set(value, options={})
  value = value.to_s.gsub('"', '\"')
  type = browser.evaluate_script %( ChromeRemoteHelper.nodeSetType(#{id}) )

  if type == "file"
    send_cmd "DOM.setFileInputFiles", files: [value.to_s], nodeId: node_id
  elsif TEXT_TYPES.include?(type)
    script = "this.value = ''; this.focus();"
    if value.blank?
      script << %(ChromeRemoteHelper.dispatchEvent(this, "change"))
    end
    on_self script
    type_string(value.to_s)
  else
    browser.evaluate_script %( ChromeRemoteHelper.nodeSet(#{id}, "#{value}", "#{type}") )
  end
end

#tag_nameObject Also known as: local_name



292
293
294
295
# File 'lib/capybara/chrome/node.rb', line 292

def tag_name
  # node_description["node"]["localName"]
  on_self_value("return this.tagName.toLowerCase()")
end

#trigger(event_name) ⇒ Object



269
270
271
# File 'lib/capybara/chrome/node.rb', line 269

def trigger(event_name)
  on_self %( ChromeRemoteHelper.dispatchEvent(this, '#{event_name}') )
end

#type_string(string) ⇒ Object



173
174
175
176
177
178
179
180
# File 'lib/capybara/chrome/node.rb', line 173

def type_string(string)
  ary = string.chars
  ary.each do |char|
    char.tr!("\n", "\r")
    send_cmd! "Input.dispatchKeyEvent", {type: "keyDown", text: char}
    send_cmd! "Input.dispatchKeyEvent", {type: "keyUp"}
  end
end

#value(*args) ⇒ Object



229
230
231
232
# File 'lib/capybara/chrome/node.rb', line 229

def value(*args)
  debug args
  val = browser.evaluate_script %( ChromeRemoteHelper.onSelfValue(#{id}, "value") )
end

#visible?Boolean

Returns:

  • (Boolean)


72
73
74
# File 'lib/capybara/chrome/node.rb', line 72

def visible?
  browser.evaluate_script %( ChromeRemoteHelper.nodeVisible(#{id}) )
end

#visible_textObject Also known as: text



59
60
61
62
63
64
65
# File 'lib/capybara/chrome/node.rb', line 59

def visible_text
  raw_text.to_s.gsub(/\ +/, ' ')
    .gsub(/[\ \n]*\n[\ \n]*/, "\n")
    .gsub(/\A[[:space:]&&[^\u00a0]]+/, "")
    .gsub(/[[:space:]&&[^\u00a0]]+\z/, "")
    .tr("\u00a0", ' ')
end