Class: Capybara::Lightpanda::Driver

Inherits:
Driver::Base
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/capybara/lightpanda/driver.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app, options = {}) ⇒ Driver

Returns a new instance of Driver.



15
16
17
18
19
20
21
# File 'lib/capybara/lightpanda/driver.rb', line 15

def initialize(app, options = {})
  super()
  @app = app
  @options = options
  @browser = nil
  @started = false
end

Instance Attribute Details

#appObject (readonly)

Returns the value of attribute app.



11
12
13
# File 'lib/capybara/lightpanda/driver.rb', line 11

def app
  @app
end

#optionsObject (readonly)

Returns the value of attribute options.



11
12
13
# File 'lib/capybara/lightpanda/driver.rb', line 11

def options
  @options
end

Instance Method Details

#accept_modal(type, **options, &block) ⇒ Object

– Modal/Dialog Support –



169
170
171
172
173
174
175
# File 'lib/capybara/lightpanda/driver.rb', line 169

def accept_modal(type, **options, &block)
  browser.accept_modal(type, text: options[:with])
  block&.call
  browser.find_modal(type,
                     text: options[:text],
                     wait: options.fetch(:wait, browser.options.timeout))
end

#active_elementObject



56
57
58
59
# File 'lib/capybara/lightpanda/driver.rb', line 56

def active_element
  oid = browser.active_element
  oid && Node.new(self, oid)
end

#browserObject



23
24
25
26
# File 'lib/capybara/lightpanda/driver.rb', line 23

def browser
  @browser = nil if @browser && !browser_alive?
  @browser ||= Browser.new(@options)
end

#browser_alive?Boolean

Returns:

  • (Boolean)


28
29
30
31
32
# File 'lib/capybara/lightpanda/driver.rb', line 28

def browser_alive?
  @browser.client && !@browser.client.closed?
rescue StandardError
  false
end

#clear_cookiesObject



121
122
123
# File 'lib/capybara/lightpanda/driver.rb', line 121

def clear_cookies
  browser.cookies.clear
end

#dismiss_modal(type, **options, &block) ⇒ Object



177
178
179
180
181
182
183
# File 'lib/capybara/lightpanda/driver.rb', line 177

def dismiss_modal(type, **options, &block)
  browser.dismiss_modal(type)
  block&.call
  browser.find_modal(type,
                     text: options[:text],
                     wait: options.fetch(:wait, browser.options.timeout))
end

#evaluate_async_script(script, *args) ⇒ Object



86
87
88
# File 'lib/capybara/lightpanda/driver.rb', line 86

def evaluate_async_script(script, *args)
  unwrap_script_result(browser.evaluate_async(script.strip, *native_args(args)))
end

#evaluate_script(script, *args) ⇒ Object



77
78
79
# File 'lib/capybara/lightpanda/driver.rb', line 77

def evaluate_script(script, *args)
  unwrap_script_result(browser.evaluate(script.strip, *native_args(args)))
end

#execute_script(script, *args) ⇒ Object



81
82
83
84
# File 'lib/capybara/lightpanda/driver.rb', line 81

def execute_script(script, *args)
  browser.execute(script.strip, *native_args(args))
  nil
end

#find_css(selector) ⇒ Object



72
73
74
75
# File 'lib/capybara/lightpanda/driver.rb', line 72

def find_css(selector)
  object_ids = browser.find("css", selector)
  object_ids.map { |oid| Node.new(self, oid) }
end

#find_xpath(selector) ⇒ Object



67
68
69
70
# File 'lib/capybara/lightpanda/driver.rb', line 67

def find_xpath(selector)
  object_ids = browser.find("xpath", selector)
  object_ids.map { |oid| Node.new(self, oid) }
end

#frame_titleObject



159
160
161
162
163
164
165
# File 'lib/capybara/lightpanda/driver.rb', line 159

def frame_title
  frame = browser.frame_stack.last
  return browser.title unless frame

  browser.call_function_on(frame.remote_object_id,
                           "function() { return this.contentDocument.title }")
end

#frame_urlObject

Capybara::Driver::Base falls back to running these via the top execution context, which always reports the parent document. Resolve them through the iframe element’s contentWindow / contentDocument so they reflect the active frame.



151
152
153
154
155
156
157
# File 'lib/capybara/lightpanda/driver.rb', line 151

def frame_url
  frame = browser.frame_stack.last
  return browser.current_url unless frame

  browser.call_function_on(frame.remote_object_id,
                           "function() { return this.contentWindow.location.href }")
end

#go_backObject



39
40
41
# File 'lib/capybara/lightpanda/driver.rb', line 39

def go_back
  browser.back
end

#go_forwardObject



43
44
45
# File 'lib/capybara/lightpanda/driver.rb', line 43

def go_forward
  browser.forward
end

#htmlObject Also known as: body



51
52
53
# File 'lib/capybara/lightpanda/driver.rb', line 51

def html
  browser.body
end

#invalid_element_errorsObject

Expanded error list for Capybara retry logic (Cuprite pattern).



224
225
226
227
228
229
230
231
# File 'lib/capybara/lightpanda/driver.rb', line 224

def invalid_element_errors
  [
    NodeNotFoundError,
    NoExecutionContextError,
    ObsoleteNode,
    MouseEventFailed,
  ]
end

#needs_server?Boolean

Returns:

  • (Boolean)


215
216
217
# File 'lib/capybara/lightpanda/driver.rb', line 215

def needs_server?
  true
end

#networkObject

Network tracker (lazily auto-enabled). Exposes ‘traffic`, `clear`, `wait_for_idle`, header overrides, etc. Cuprite parity.



94
95
96
# File 'lib/capybara/lightpanda/driver.rb', line 94

def network
  browser.network
end

#pauseObject

Pause execution for interactive debugging.



234
235
236
237
238
239
240
241
242
243
# File 'lib/capybara/lightpanda/driver.rb', line 234

def pause
  if $stdin.tty?
    warn "\nPaused. Press Enter to continue."
    $stdin.gets
  else
    warn "\nPaused. Send SIGCONT (kill -CONT #{::Process.pid}) to continue."
    trap("CONT") {} # rubocop:disable Lint/EmptyBlock
    ::Process.kill("STOP", ::Process.pid)
  end
end

#quitObject



210
211
212
213
# File 'lib/capybara/lightpanda/driver.rb', line 210

def quit
  @browser&.quit
  @browser = nil
end

#refreshObject



47
48
49
# File 'lib/capybara/lightpanda/driver.rb', line 47

def refresh
  browser.refresh
end


125
126
127
# File 'lib/capybara/lightpanda/driver.rb', line 125

def remove_cookie(name, **)
  browser.cookies.remove(name: name, **)
end

#reset!Object

– Lifecycle –



199
200
201
202
203
204
205
206
207
208
# File 'lib/capybara/lightpanda/driver.rb', line 199

def reset!
  browser.clear_frames
  browser.reset_modals
  browser.cookies.clear
  browser.network.clear
  browser.go_to("about:blank")
rescue StandardError
  @browser&.quit
  @browser = nil
end

#save_screenshot(path, **_options) ⇒ Object

– Screenshots – Lightpanda has no rendering engine so screenshots are blank, but we handle the call gracefully so Rails’ before_teardown (screenshot on failure) doesn’t raise NotSupportedByDriverError.



190
191
192
193
194
195
# File 'lib/capybara/lightpanda/driver.rb', line 190

def save_screenshot(path, **_options)
  browser.screenshot(path: path)
rescue BinaryError, BinaryNotFoundError
  # Browser can't start (e.g., version too old) — don't crash teardown
  nil
end

#send_keys(*keys) ⇒ Object

Capybara’s Session#send_keys routes to Driver#send_keys; Cuprite’s pattern is to fan that out to whatever element currently has focus.



63
64
65
# File 'lib/capybara/lightpanda/driver.rb', line 63

def send_keys(*keys)
  active_element&.send_keys(*keys)
end

– Cookie Management –



108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/capybara/lightpanda/driver.rb', line 108

def set_cookie(name, value, **options)
  cookie_options = { domain: options[:domain] || default_domain }
  cookie_options[:path] = options[:path] if options[:path]
  cookie_options[:secure] = options[:secure] if options.key?(:secure)
  if options.key?(:httpOnly) || options.key?(:http_only)
    cookie_options[:http_only] =
      options[:httpOnly] || options[:http_only]
  end
  cookie_options[:expires] = options[:expires] if options[:expires]

  browser.cookies.set(name: name, value: value, **cookie_options)
end

#switch_to_frame(frame) ⇒ Object

– Frame Support – Passes Node objects (with remote_object_id) to Browser’s frame stack. callFunctionOn on the iframe element scopes finding to its contentDocument.



133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/capybara/lightpanda/driver.rb', line 133

def switch_to_frame(frame)
  case frame
  when :top
    browser.clear_frames
  when :parent
    browser.pop_frame
  when Node
    browser.push_frame(frame)
  else
    # Capybara passes a Capybara::Node::Element; extract our driver Node
    browser.push_frame(frame.base)
  end
end

#visit(url) ⇒ Object



34
35
36
37
# File 'lib/capybara/lightpanda/driver.rb', line 34

def visit(url)
  @started = true
  browser.go_to(url)
end

#wait?Boolean

Returns:

  • (Boolean)


219
220
221
# File 'lib/capybara/lightpanda/driver.rb', line 219

def wait?
  true
end

#wait_for_network_idle(timeout: 5, connections: 0) ⇒ Object

Block until in-flight HTTP traffic settles. Auto-enables the tracker on first call so callers don’t have to remember to flip it on. Returns true on success, false on timeout.



101
102
103
104
# File 'lib/capybara/lightpanda/driver.rb', line 101

def wait_for_network_idle(timeout: 5, connections: 0)
  network.enable
  network.wait_for_idle(timeout: timeout, connections: connections)
end