Class: Capybara::Simulated::Node

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

Constant Summary

Constants included from WhitespaceNormalizer

WhitespaceNormalizer::BREAKING_SPACES, WhitespaceNormalizer::EMPTY_LINES, WhitespaceNormalizer::LEADING_SPACES, WhitespaceNormalizer::LEFT_TO_RIGHT_MARK, WhitespaceNormalizer::LINE_SEPERATOR, WhitespaceNormalizer::NON_BREAKING_SPACE, WhitespaceNormalizer::PARAGRAPH_SEPERATOR, WhitespaceNormalizer::REMOVED_CHARACTERS, WhitespaceNormalizer::RIGHT_TO_LEFT_MARK, WhitespaceNormalizer::SQUEEZED_SPACES, WhitespaceNormalizer::TRAILING_SPACES, WhitespaceNormalizer::ZERO_WIDTH_SPACE

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from WhitespaceNormalizer

#normalize_spacing, #normalize_visible_spacing

Constructor Details

#initialize(driver, handle) ⇒ Node

Returns a new instance of Node.



12
13
14
15
16
17
18
19
20
# File 'lib/capybara/simulated/node.rb', line 12

def initialize(driver, handle)
  super(driver, self)
  @handle_id    = handle
  # Pin the Browser at construction so nodes from one window
  # stay valid even after `switch_to_window` flips to another.
  @browser      = driver.current_browser
  @initial_node = @browser.lookup_node(handle)
  @context_gen  = @browser.context_gen
end

Instance Attribute Details

#context_genObject (readonly)

Returns the value of attribute context_gen.



22
23
24
# File 'lib/capybara/simulated/node.rb', line 22

def context_gen
  @context_gen
end

#handle_idObject (readonly)

Returns the value of attribute handle_id.



22
23
24
# File 'lib/capybara/simulated/node.rb', line 22

def handle_id
  @handle_id
end

Instance Method Details

#==(other) ⇒ Object

Both handle_id and context_gen are part of identity: after a cross-page reload the new element can land on the same handle id (JS-side counter resets per ctx) but a different generation. Capybara’s synchronize uses ‘old_base == @base` to decide whether reload made progress, so id-only equality would mark a successful reload as a no-op and raise the original error.



211
212
213
214
215
# File 'lib/capybara/simulated/node.rb', line 211

def ==(other)
  other.is_a?(Node) &&
    other.handle_id == @handle_id &&
    other.context_gen == @context_gen
end

#[](name) ⇒ Object



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/capybara/simulated/node.rb', line 73

def [](name)
  # Tick the virtual clock so Capybara's helpers that poll an
  # attribute in a tight `sleep(0.1) until` loop (e.g. Avo's
  # `wait_for_body_class_missing`) actually see the JS chain
  # progress. Without this the body class never transitions
  # because we only advance time during `find`, and the helper
  # caches the body node before its poll loop.
  #
  # Gated behind the same wall-clock throttle `find_css` uses
  # (`timer_wait_elapsed?`): on framework-runloop pages that keep
  # `@timers_active` permanently true, an ungated tick here would
  # drain timers on every per-result attribute filter / poll
  # iteration. The throttle still ticks the first time and once
  # per ~50 ms window thereafter, so poll loops that wait for a
  # timer to fire between reads still make progress.
  #
  # `tick_real_time` also drains the Worker / EventSource / hijacked
  # -fetch outboxes, so an attribute poll whose value is delivered
  # only by one of those channels (with no active timer) would
  # otherwise never see it. `async_io_pending?` is an O(1) gate
  # (three empty? checks) that lets those cases drain without paying
  # an unconditional tick on timer-driven runloop pages.
  browser.tick_real_time if browser.timer_wait_elapsed? || browser.async_io_pending?
  check_stale
  browser.attr(handle_id, name.to_s)
end

#all_textObject

Tick the virtual clock unconditionally on the text path. Unlike ‘Node#[]` (attribute reads), the text readers are NOT preceded by a `find_css` that ticks under the wall throttle: `have_text` / `assert_text` polls `.text` directly against an already-found, cached scope node, so the find loop short-circuits without re-ticking. Gating these behind `timer_wait_elapsed?` therefore stalls a pure text-poll loop’s virtual clock and scheduled ‘setTimeout`s never fire (smoke_spec virtual-clock contract). They run ~once per poll-scope (not once per matched result like the attribute filters the audit targeted), so they are not the O(N) hot path and are safe to tick every call.



35
36
37
38
39
# File 'lib/capybara/simulated/node.rb', line 35

def all_text
  browser.tick_real_time
  check_stale
  normalize_spacing(browser.all_text(handle_id))
end

#checked?Boolean

Returns:

  • (Boolean)


192
# File 'lib/capybara/simulated/node.rb', line 192

def checked?         = !!self['checked']

#click(keys = [], **opts) ⇒ Object



100
101
102
103
# File 'lib/capybara/simulated/node.rb', line 100

def click(keys = [], **opts)
  check_stale
  browser.click(handle_id, keys, **opts)
end

#disabled?Boolean

Returns:

  • (Boolean)


190
# File 'lib/capybara/simulated/node.rb', line 190

def disabled?        = browser.disabled?(handle_id)

#double_click(keys = [], **opts) ⇒ Object



110
111
112
113
# File 'lib/capybara/simulated/node.rb', line 110

def double_click(keys = [], **opts)
  check_stale
  browser.double_click(handle_id, keys, **opts)
end

#drag_to(target_node, **opts) ⇒ Object



150
151
152
153
154
155
156
# File 'lib/capybara/simulated/node.rb', line 150

def drag_to(target_node, **opts)
  check_stale
  target_node.check_stale if target_node.respond_to?(:check_stale)
  target_handle = target_node.respond_to?(:handle_id) ? target_node.handle_id : target_node.native
  browser.drag_to(handle_id, target_handle, **opts)
  self
end

#drop(*args) ⇒ Object



144
145
146
147
148
# File 'lib/capybara/simulated/node.rb', line 144

def drop(*args)
  check_stale
  browser.drop(handle_id, args)
  true
end

#find_css(query) ⇒ Object



181
182
183
# File 'lib/capybara/simulated/node.rb', line 181

def find_css(query)
  browser.find_css(query, handle_id).map {|id| self.class.new(driver, id) }
end

#find_xpath(query) ⇒ Object



177
178
179
# File 'lib/capybara/simulated/node.rb', line 177

def find_xpath(query)
  browser.find_xpath(query, handle_id).map {|id| self.class.new(driver, id) }
end

#hover(**_opts) ⇒ Object



115
116
117
118
119
# File 'lib/capybara/simulated/node.rb', line 115

def hover(**_opts)
  check_stale
  browser.hover(handle_id)
  self
end

#inner_htmlObject

Convenience accessors mirroring real browser nodes — Discourse tests reach for ‘.native.inner_html` (e.g. reviewables XSS checks); without this method `.native` (= self) raised NoMethodError.



63
64
65
66
# File 'lib/capybara/simulated/node.rb', line 63

def inner_html
  check_stale
  browser.inner_html(handle_id)
end

#obscured?Boolean

Returns:

  • (Boolean)


194
# File 'lib/capybara/simulated/node.rb', line 194

def obscured?(*)     = !visible?

#outer_htmlObject



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

def outer_html
  check_stale
  browser.outer_html(handle_id)
end

#pathObject



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

def path
  check_stale
  browser.node_path(handle_id)
end

#readonly?Boolean

Returns:

  • (Boolean)


193
# File 'lib/capybara/simulated/node.rb', line 193

def readonly?        = !!self['readonly']

#rectObject

Capybara’s standard rect API. No layout engine — but Discourse’s ‘wait_for_animation` helper polls `element.rect` twice and waits for the values to stabilise, which they immediately do here (constant zeros) since we never animate. That unblocks every test guarded by an animation settle.



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

def rect
  check_stale
  {x: 0, y: 0, width: 0, height: 0, top: 0, left: 0, bottom: 0, right: 0}
end

#right_click(keys = [], **opts) ⇒ Object



105
106
107
108
# File 'lib/capybara/simulated/node.rb', line 105

def right_click(keys = [], **opts)
  check_stale
  browser.right_click(handle_id, keys, **opts)
end

#scroll_toObject



121
# File 'lib/capybara/simulated/node.rb', line 121

def scroll_to(*, **)       ; self ; end

#select_optionObject



162
163
164
165
# File 'lib/capybara/simulated/node.rb', line 162

def select_option
  check_stale
  browser.select_option(handle_id)
end

#selected?Boolean

Returns:

  • (Boolean)


191
# File 'lib/capybara/simulated/node.rb', line 191

def selected?        = browser.option_selected?(handle_id)

#send_keys(*keys) ⇒ Object



133
134
135
136
137
# File 'lib/capybara/simulated/node.rb', line 133

def send_keys(*keys)
  check_stale
  browser.send_keys(handle_id, keys)
  true
end

#set(value, **_) ⇒ Object



157
158
159
160
# File 'lib/capybara/simulated/node.rb', line 157

def set(value, **_)
  check_stale
  browser.set_value_with_events(handle_id, value)
end

#shadow_rootObject



185
186
187
188
189
# File 'lib/capybara/simulated/node.rb', line 185

def shadow_root
  check_stale
  h = browser.shadow_root_handle(handle_id)
  h && self.class.new(driver, h)
end

#style(names = []) ⇒ Object



196
197
198
199
# File 'lib/capybara/simulated/node.rb', line 196

def style(names = [])
  check_stale
  browser.computed_style(handle_id, Array(names))
end

#submit(*_) ⇒ Object



172
173
174
175
# File 'lib/capybara/simulated/node.rb', line 172

def submit(*_)
  check_stale
  browser.submit_form(handle_id)
end

#synchronizeObject



195
# File 'lib/capybara/simulated/node.rb', line 195

def synchronize(*)   = yield

#tag_nameObject



57
# File 'lib/capybara/simulated/node.rb', line 57

def tag_name = browser.tag_name(handle_id)

#trigger(event) ⇒ Object



139
140
141
142
143
# File 'lib/capybara/simulated/node.rb', line 139

def trigger(event)
  check_stale
  browser.dispatch_event(handle_id, event.to_s)
  true
end

#unselect_optionObject



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

def unselect_option
  check_stale
  browser.unselect_option(handle_id)
end

#valueObject



47
48
49
50
# File 'lib/capybara/simulated/node.rb', line 47

def value
  check_stale
  browser.value(handle_id)
end

#visible?Boolean

Returns:

  • (Boolean)


52
53
54
55
# File 'lib/capybara/simulated/node.rb', line 52

def visible?
  check_stale
  browser.visible?(handle_id)
end

#visible_textObject



41
42
43
44
45
# File 'lib/capybara/simulated/node.rb', line 41

def visible_text
  browser.tick_real_time
  check_stale
  normalize_visible_spacing(browser.visible_text(handle_id))
end