capybara-lightpanda 0.7.0 → 0.8.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d1c82db6d6a5d5662098021d24688d5ff16592b1cb6de95c2c422307487d91d0
4
- data.tar.gz: 0f21620cae5dc33e640712565a64071a47d78e906520770bef8e2aa593b230ca
3
+ metadata.gz: f039526ec34b172b3824416cd6b7679720cabcb75455a1c3c46bbf0cc11b5d7b
4
+ data.tar.gz: '087e752b5baa7d09d43541e90654732e042f28535d53248fab9ab2769f656c01'
5
5
  SHA512:
6
- metadata.gz: 27f0ce27958fdbb057b68b684429222cae534d0b32f06521608862a6407a5ceb89935cbfeb4ebfee7de70d7d7d78a92a6a36d547ffdae27171bef1a731606728
7
- data.tar.gz: 492a709cf1117444126ac9429586dc861af46e8e52d779106478e7d96d4c3aff13c1756f9c6ece4d96f41610e4f79f3b7861d02d11eaf294cbc22594c5d28001
6
+ metadata.gz: b2430399381dbcb55f54eafbbb9fb81cc3c4ac5956e4bbc16d5dfe70fb1f347f7f8dfdf60044134c87ae739200911d22e986ce9b4d0a37a2f21ebf74887e50f1
7
+ data.tar.gz: 500584b0da1e6bd82737e213fdd13e10395334e0c971923e2f7fdcb78758251f36f50361272b66c1838a781bf134b724c7b1cc77761dfb28a9b6cfbd8de65d31
data/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.8.0] - 2026-06-12
4
+
5
+ > **Update Lightpanda before upgrading.** Requires a nightly build ≥ 6736 (published 2026-06-12). The driver refuses to start against older binaries.
6
+
7
+ ### Added
8
+
9
+ - `lightpanda:binary:*` rake tasks now load automatically inside Rails apps (via a Railtie), and the "Lightpanda is too old" error suggests a require-the-gem one-liner instead of those rake tasks. The old hint was a dead end: the tasks weren't loaded in Rails apps at all, and even with the Railtie they only exist under `RAILS_ENV=test` when the gem sits in the `:test` Gemfile group. (Found beta-testing a private Rails suite.)
10
+ - `Driver#render` as an alias of `save_screenshot`. capybara-screenshot calls `driver.render(path)` for drivers it doesn't know, so every failing test in a capybara-screenshot suite logged "Screenshot could not be saved: undefined method 'render'" — once per failure.
11
+ - `Driver#headers=` / `#add_headers` / `#headers`, delegating to the existing `Network` support. Cuprite exposes header writers on the driver and real suites call them there (`page.driver.headers = …`).
12
+ - `Cookies#[]` as an alias of `#get` — the Ferrum/Cuprite spelling (`browser.cookies["session_id"]`).
13
+ - `Browser#console_logs` / `#clear_console_logs` — console messages captured since the last session reset, as `{type:, text:, timestamp:, args:}` hashes (driver-internal Turbo sentinels excluded, buffer capped at 1,000 entries). Suites that assert "no JS errors leaked" no longer need to wire a custom Ferrum-style logger: `page.driver.browser.console_logs.select { |m| m[:type] == "error" }`. Note that Lightpanda currently reports both `console.log` and `console.warn` as type `"info"` — filter on `text` when you need to distinguish them.
14
+
15
+ ### Changed
16
+
17
+ - Modal assertions match dialogs by message text regardless of the alert/confirm/prompt type, like Selenium and Cuprite — `accept_alert` around a `data-confirm` delete button now works.
18
+ - Clicks dispatch the full pointer sequence (mousedown → mouseup → click). Widgets that open on mousedown — select2 v3 dropdowns, for example — now react to `click` / `select_from`.
19
+ - The driver no longer re-dispatches `readystatechange` itself. Lightpanda fires the event natively as of nightly 6736 (lightpanda-io/browser#2708), so the shim added in 0.7.0 became redundant and was removed. Behavior is unchanged for Turbo/Hotwire apps — `turbo:load` still fires on every visit, now from the browser's own event.
20
+
21
+ ### Fixed
22
+
23
+ - A failed binary download that falls back to a stale cached binary now warns loudly on stderr (with the original error and a re-provision one-liner) instead of logging only under `LIGHTPANDA_DEBUG`. VCR-guarded suites hit this path silently — VCR's blocking error is a `StandardError`, unlike raw WebMock's — and the only visible symptom was a confusing "Lightpanda is too old" error much later.
24
+ - Turbo Streams over ActionCable now connect. Page-initiated WebSocket upgrades carry the document's `Origin` header as of nightly 6736 (lightpanda-io/browser#2710, guaranteed by the new minimum build), so ActionCable's request-forgery protection accepts the connection instead of rejecting it with `Request origin not allowed: nil`. `turbo-cable-stream-source` elements reach `[connected]` and `turbo_stream_for` broadcasts (solid_cable or any adapter) arrive in specs. If you had added `config.action_cable.disable_request_forgery_protection = true` to your test environment to work around this, you can remove it.
25
+
3
26
  ## [0.7.0] - 2026-06-12
4
27
 
5
28
  ### Changed
@@ -33,6 +33,13 @@ module Capybara
33
33
 
34
34
  DEFAULT_CACHE_TIME = 86_400
35
35
 
36
+ # One-liner that re-provisions the binary from a process with no
37
+ # HTTP-stubbing loaded (VCR/WebMock guard the test process itself).
38
+ # Referenced from the stale-fallback warning and BETA_TESTING.md.
39
+ PROVISION_HINT =
40
+ "bundle exec ruby -r capybara-lightpanda " \
41
+ "-e 'Capybara::Lightpanda::Binary.remove; puts Capybara::Lightpanda::Binary.update'"
42
+
36
43
  class << self
37
44
  # Set a specific release tag (e.g. "0.3.0") to pin downloads to that
38
45
  # release. When nil, the rolling "nightly" tag is used. The pin only
@@ -96,7 +103,36 @@ module Capybara
96
103
  return system_path
97
104
  end
98
105
 
99
- download
106
+ # Stale-or-absent cache, nothing on PATH: refresh from the network.
107
+ # If that fails (GitHub 5xx, DNS/connect timeouts, SocketError) but a
108
+ # usable — if stale — binary is already cached, keep using it rather
109
+ # than hard-failing. A cold cache (nothing on disk) still surfaces the
110
+ # error. The MINIMUM_NIGHTLY_BUILD floor is enforced downstream in
111
+ # Process#start, so a sub-floor binary can't slip in.
112
+ #
113
+ # Deliberately StandardError, not Exception: WebMock's
114
+ # NetConnectNotAllowedError descends from Exception so it propagates
115
+ # through app rescue blocks by design — a test suite that blocks net
116
+ # connections SHOULD fail loudly here, not silently fall back. CI
117
+ # pre-provisions the binary outside that guard instead (real-apps.yml).
118
+ begin
119
+ download
120
+ rescue StandardError => e
121
+ raise unless File.executable?(destination)
122
+
123
+ # Kernel.warn, not log: log() is silent unless LIGHTPANDA_DEBUG or
124
+ # an explicit logger is set, and this fallback is exactly the
125
+ # moment the user needs to hear about — a VCR-guarded suite (whose
126
+ # UnhandledHTTPRequestError is a StandardError, unlike raw
127
+ # WebMock's Exception) lands here silently, keeps a stale binary,
128
+ # and later hits a confusing MINIMUM_NIGHTLY_BUILD floor error
129
+ # with no trace of the blocked download.
130
+ warn("[capybara-lightpanda] Binary download failed (#{e.class}: #{e.message}); " \
131
+ "falling back to the cached binary at #{destination}. " \
132
+ "If your suite stubs HTTP (VCR/WebMock), pre-provision from an " \
133
+ "unstubbed process: #{PROVISION_HINT}")
134
+ destination
135
+ end
100
136
  end
101
137
 
102
138
  # Delete the cached binary. Returns the path that was deleted, or nil
@@ -173,9 +209,15 @@ module Capybara
173
209
  # suggest `brew update && brew upgrade lightpanda` (brew pins
174
210
  # each user's binary at install time and doesn't refresh on its
175
211
  # own when the tap publishes a newer nightly).
176
- # - Path equals our own cache → suggest the gem's rake tasks; the
177
- # `remove` step is required because `update` honors `cache_time`
178
- # and would otherwise no-op on a too-old-but-not-yet-expired file.
212
+ # - Path equals our own cache → suggest the require-the-gem one-liner.
213
+ # NOT the lightpanda:binary:* rake tasks: in a Rails app the gem
214
+ # usually sits in the :test Gemfile group, so the tasks only exist
215
+ # under RAILS_ENV=test (the Railtie can't help a plain `bundle exec
216
+ # rake` in development), and outside Rails they're never loaded at
217
+ # all. The one-liner requires the gem explicitly, so it works from
218
+ # any environment. The `remove` step is required because `update`
219
+ # honors `cache_time` and would otherwise no-op on a
220
+ # too-old-but-not-yet-expired file.
179
221
  # - Anything else (user-managed install at a custom path) → keep
180
222
  # the curl-overwrite suggestion, since we don't know how the file
181
223
  # got there.
@@ -183,7 +225,7 @@ module Capybara
183
225
  if brew_managed?(binary_path)
184
226
  "brew update && brew upgrade lightpanda"
185
227
  elsif binary_path == install_path
186
- "bundle exec rake lightpanda:binary:remove lightpanda:binary:update"
228
+ PROVISION_HINT
187
229
  else
188
230
  "curl -sL #{GITHUB_RELEASE_URL}/nightly/#{platform_binary} " \
189
231
  "-o #{binary_path} && chmod +x #{binary_path}"
@@ -77,6 +77,8 @@ module Capybara
77
77
  @modal_messages = []
78
78
  @modal_messages_mutex = Mutex.new
79
79
  @modal_handler_installed = false
80
+ @console_logs = []
81
+ @console_logs_mutex = Mutex.new
80
82
  @frame_stack = []
81
83
  @turbo_event = Utils::Event.new
82
84
  @turbo_event.set
@@ -124,6 +126,7 @@ module Capybara
124
126
 
125
127
  @turbo_event.set
126
128
  subscribe_to_console_logs
129
+ subscribe_to_console_capture
127
130
  subscribe_to_execution_context
128
131
  subscribe_to_turbo_signals
129
132
  subscribe_to_navigation_response
@@ -172,6 +175,7 @@ module Capybara
172
175
  @page_events_enabled = false
173
176
  @modal_handler_installed = false
174
177
  @modal_messages_mutex.synchronize { @modal_messages.clear }
178
+ @console_logs_mutex.synchronize { @console_logs.clear }
175
179
  @last_navigation_response = nil
176
180
  @document_request_id = nil
177
181
  clear_frames
@@ -588,6 +592,22 @@ module Capybara
588
592
  @cookies ||= Cookies.new(self)
589
593
  end
590
594
 
595
+ # Console messages captured from `Runtime.consoleAPICalled` since the
596
+ # last `reset` (Turbo-tracker sentinels excluded). Loose hashes, like
597
+ # Network#traffic: `{type:, text:, timestamp:, args:}` where `type` is
598
+ # the console method name ("log", "error", "warning", ...), `text` joins
599
+ # the arguments' primitive values/descriptions, and `args` keeps the raw
600
+ # CDP RemoteObjects. Lets suites assert on JS console errors
601
+ # (`browser.console_logs.select { |m| m[:type] == "error" }`) the way
602
+ # peer drivers do via custom Ferrum loggers.
603
+ def console_logs
604
+ @console_logs_mutex.synchronize { @console_logs.dup }
605
+ end
606
+
607
+ def clear_console_logs
608
+ @console_logs_mutex.synchronize { @console_logs.clear }
609
+ end
610
+
591
611
  # -- Frame Support --
592
612
  # `frame_stack` (Array<Node>) is the Capybara `switch_to_frame` stack;
593
613
  # it drives where `find` resolves selectors. Stored as Nodes so
@@ -640,61 +660,52 @@ module Capybara
640
660
  page_command("LP.handleJavaScriptDialog", accept: false)
641
661
  end
642
662
 
663
+ # `type` is accepted for the error message only: like Selenium (where
664
+ # alert/confirm are indistinguishable) and Cuprite (whose dialog handler
665
+ # accepts whatever fires), we deliberately do NOT reject a dialog whose
666
+ # reported type differs from the one Capybara asked for. Real suites
667
+ # wrap `data-confirm` deletes in `accept_alert` (e.g. solidus admin) and
668
+ # expect it to work; only the message text is matched.
643
669
  def find_modal(type, text: nil, wait: options.timeout)
644
670
  regexp = text.is_a?(Regexp) ? text : (text && Regexp.new(Regexp.escape(text.to_s)))
645
- last_matching_type_message = nil
646
671
  last_seen_message = nil
647
672
  claimed = nil
648
673
  Utils::Wait.until(timeout: wait, interval: 0.05) do
649
- claimed = pop_modal_message(type.to_s, regexp)
674
+ claimed = pop_modal_message(regexp)
650
675
  next true if claimed
651
676
 
652
- last = peek_last_modal_message(type.to_s)
653
- last_matching_type_message = last[:matching_type] || last_matching_type_message
654
- last_seen_message = last[:any] || last_seen_message
677
+ last_seen_message = peek_last_modal_message || last_seen_message
655
678
  false
656
679
  end
657
680
  claimed[:message]
658
681
  rescue TimeoutError
659
- raise_modal_not_found(type, text, last_matching_type_message, last_seen_message)
682
+ raise_modal_not_found(type, text, last_seen_message)
660
683
  end
661
684
 
662
685
  private
663
686
 
664
- # Pop the first queued dialog whose type matches and (when `regexp` is
665
- # non-nil) whose message matches the requested pattern. Returns the
666
- # entry or nil. Serialized with the message-thread writer.
667
- def pop_modal_message(type, regexp)
687
+ # Pop the first queued dialog whose message matches the requested
688
+ # pattern (any dialog when `regexp` is nil). Returns the entry or nil.
689
+ # Serialized with the message-thread writer.
690
+ def pop_modal_message(regexp)
668
691
  @modal_messages_mutex.synchronize do
669
692
  match = @modal_messages.find do |m|
670
- m[:type] == type && (regexp.nil? || m[:message].to_s.match?(regexp))
693
+ regexp.nil? || m[:message].to_s.match?(regexp)
671
694
  end
672
695
  @modal_messages.delete(match) if match
673
696
  match
674
697
  end
675
698
  end
676
699
 
677
- # Inspect the queue for diagnostics. Returns the most recent message
678
- # of the requested type (if any) AND the most recent message of any
679
- # type so the failure message can hint at a type mismatch.
680
- def peek_last_modal_message(type)
681
- @modal_messages_mutex.synchronize do
682
- {
683
- matching_type: @modal_messages.reverse.find { |m| m[:type] == type }&.dig(:message),
684
- any: @modal_messages.last&.dig(:message),
685
- }
686
- end
700
+ # Most recent dialog message of any type, for diagnostics.
701
+ def peek_last_modal_message
702
+ @modal_messages_mutex.synchronize { @modal_messages.last&.dig(:message) }
687
703
  end
688
704
 
689
- def raise_modal_not_found(type, text, matching_type_message, any_message)
690
- if matching_type_message
691
- raise Capybara::ModalNotFound,
692
- "Unable to find modal dialog with #{text} - found '#{matching_type_message}' instead."
693
- end
694
- if any_message
705
+ def raise_modal_not_found(type, text, last_seen_message)
706
+ if last_seen_message
695
707
  raise Capybara::ModalNotFound,
696
- "Unable to find #{type} modal#{" with #{text}" if text} - " \
697
- "a different dialog fired with message '#{any_message}'."
708
+ "Unable to find #{type} modal with #{text} - found '#{last_seen_message}' instead."
698
709
  end
699
710
  raise Capybara::ModalNotFound, "Unable to find modal dialog#{" with #{text}" if text}"
700
711
  end
@@ -865,6 +876,35 @@ module Capybara
865
876
  TURBO_SENTINEL_PREFIX = "__lightpanda_turbo_"
866
877
  private_constant :TURBO_SENTINEL_PREFIX
867
878
 
879
+ # Oldest entries are dropped past this cap so a chatty page can't grow
880
+ # the buffer unbounded across a long session.
881
+ CONSOLE_LOGS_LIMIT = 1_000
882
+
883
+ # Ring-buffer every console.* call for `Browser#console_logs`. Separate
884
+ # from subscribe_to_console_logs (which streams to an optional IO logger)
885
+ # so capture works without any logger configured. Skips the Turbo
886
+ # activity-tracker sentinels — they're driver plumbing, not page output.
887
+ def subscribe_to_console_capture
888
+ on("Runtime.consoleAPICalled") do |params|
889
+ args = params["args"]
890
+ next unless args.is_a?(Array)
891
+
892
+ first = args.first&.dig("value")
893
+ next if first.is_a?(String) && first.start_with?(TURBO_SENTINEL_PREFIX)
894
+
895
+ entry = {
896
+ type: params["type"],
897
+ text: args.map { |a| a.fetch("value") { a["description"] }.to_s }.join(" "),
898
+ timestamp: params["timestamp"],
899
+ args: args,
900
+ }
901
+ @console_logs_mutex.synchronize do
902
+ @console_logs << entry
903
+ @console_logs.shift(@console_logs.size - CONSOLE_LOGS_LIMIT) if @console_logs.size > CONSOLE_LOGS_LIMIT
904
+ end
905
+ end
906
+ end
907
+
868
908
  # Wire @turbo_event to the JS-side _signalTurbo emissions. The JS calls
869
909
  # console.debug('__lightpanda_turbo_busy') / '_idle' on transitions across
870
910
  # zero pending ops; Lightpanda forwards those to Runtime.consoleAPICalled.
@@ -76,6 +76,8 @@ module Capybara
76
76
  def get(name)
77
77
  find { |cookie| cookie.name == name }
78
78
  end
79
+ # Ferrum/Cuprite spelling: `browser.cookies["session_id"]`.
80
+ alias [] get
79
81
 
80
82
  def set(name:, value:, domain: nil, path: "/", secure: false, http_only: false, # rubocop:disable Metrics/ParameterLists
81
83
  same_site: nil, expires: nil)
@@ -213,6 +213,29 @@ module Capybara
213
213
  nil
214
214
  end
215
215
 
216
+ # capybara-screenshot's fallback for unregistered drivers calls
217
+ # `driver.render(path)` (the Cuprite/Ferrum spelling). Without the alias
218
+ # every failed test in a capybara-screenshot suite logged
219
+ # "Screenshot could not be saved: undefined method 'render'".
220
+ alias render save_screenshot
221
+
222
+ # -- Headers (Cuprite-compatible driver surface) --
223
+ # Delegates to Network, which lazily enables the Network domain and
224
+ # remembers the headers across reset. Cuprite exposes these on the
225
+ # driver, and real suites call them there (page.driver.headers = ...).
226
+
227
+ def headers
228
+ browser.network.extra_headers
229
+ end
230
+
231
+ def headers=(headers)
232
+ browser.network.headers = headers
233
+ end
234
+
235
+ def add_headers(headers)
236
+ browser.network.add_headers(headers)
237
+ end
238
+
216
239
  # -- Lifecycle --
217
240
 
218
241
  # Thin Cuprite-style wrapper. The interesting work — disposing the
@@ -1,19 +1,6 @@
1
1
  (function() {
2
2
  if (window._lightpanda) return;
3
3
 
4
- // --- readystatechange re-dispatch (upstream gap, wishlist A36) ---
5
- // Lightpanda transitions document.readyState correctly but never fires the
6
- // readystatechange event. Turbo's PageObserver listens ONLY for that event
7
- // to reach pageLoaded() — without it, turbo:load never fires. Re-dispatch
8
- // from the two lifecycle events Lightpanda does fire: DOMContentLoaded
9
- // (readyState=interactive) and window load (readyState=complete).
10
- // Drop this block once the upstream fix is covered by MINIMUM_NIGHTLY_BUILD.
11
- function _fireReadyStateChange() {
12
- document.dispatchEvent(new Event('readystatechange'));
13
- }
14
- document.addEventListener('DOMContentLoaded', _fireReadyStateChange);
15
- window.addEventListener('load', _fireReadyStateChange);
16
-
17
4
  // --- Turbo activity tracking ---
18
5
  // Tracks pending Turbo operations so the driver can wait for Turbo to settle.
19
6
  // Inspired by the CapybaraLockstep approach for stabilizing Turbo integration tests.
@@ -55,6 +55,9 @@ module Capybara
55
55
  @enabled = false
56
56
  end
57
57
 
58
+ # Headers applied via headers= / add_headers. Backs Driver#headers.
59
+ def extra_headers = @extra_headers || {}
60
+
58
61
  # Setting extra headers also lazily enables the Network domain. Without
59
62
  # this, headers were silently ignored until the caller separately ran
60
63
  # `network.enable` (or `wait_for_network_idle`). Cuprite/Ferrum parity.
@@ -485,6 +485,13 @@ module Capybara
485
485
  CLICK_JS = <<~JS
486
486
  function() {
487
487
  var EventCtor = (typeof MouseEvent !== 'undefined') ? MouseEvent : Event;
488
+ // Real pointer clicks are a mousedown -> mouseup -> click sequence,
489
+ // and widgets like select2 open on `mousedown`, not `click`.
490
+ // Cancelling mousedown suppresses focus/text-selection in a real
491
+ // browser but never the click, so the click below is dispatched
492
+ // unconditionally.
493
+ this.dispatchEvent(new EventCtor('mousedown', { bubbles: true, cancelable: true }));
494
+ this.dispatchEvent(new EventCtor('mouseup', { bubbles: true, cancelable: true }));
488
495
  var clickEvt = new EventCtor('click', { bubbles: true, cancelable: true });
489
496
  var notCancelled = this.dispatchEvent(clickEvt);
490
497
  if (!notCancelled || clickEvt.defaultPrevented) return;
@@ -90,10 +90,16 @@ module Capybara
90
90
  # DragEvent, merged 2026-06-10) provides the APIs Node#drop's DROP_JS
91
91
  # assembles its payload from; on builds without it the drop JS raises
92
92
  # "DataTransfer is not defined".
93
- # Build 6699 = the #2671 merge (d1f4c409, 2026-06-10) — now the binding
94
- # floor. (The prior 6672 file-upload floor — and 6353 before it — are
95
- # subsumed.)
96
- MINIMUM_NIGHTLY_BUILD = Gem::Version.new("6699")
93
+ # Build 6699 = the #2671 merge (d1f4c409, 2026-06-10) — the Node#drop
94
+ # DataTransfer floor. (The prior 6672 file-upload floor — and 6353
95
+ # before it — are subsumed.)
96
+ # PR #2708 (document fires readystatechange on readiness changes,
97
+ # merged 2026-06-12) made the index.js readystatechange re-dispatch
98
+ # shim redundant, so it was removed — on builds without #2708 Turbo's
99
+ # PageObserver never reaches pageLoaded() and turbo:load never fires.
100
+ # Build 6736 = first published nightly carrying the #2708 merge — now
101
+ # the binding floor.
102
+ MINIMUM_NIGHTLY_BUILD = Gem::Version.new("6736")
97
103
 
98
104
  attr_reader :pid, :ws_url, :version, :nightly_build
99
105
 
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capybara
4
+ module Lightpanda
5
+ # Exposes the gem's rake tasks (lightpanda:binary:*) inside Rails apps.
6
+ # Without this, the BinaryError remediation hint ("bundle exec rake
7
+ # lightpanda:binary:remove lightpanda:binary:update") pointed at tasks
8
+ # that didn't exist in the app's rake namespace.
9
+ class Railtie < Rails::Railtie
10
+ rake_tasks do
11
+ load File.expand_path("tasks/binary.rake", __dir__)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Capybara
4
4
  module Lightpanda
5
- VERSION = "0.7.0"
5
+ VERSION = "0.8.0"
6
6
  end
7
7
  end
@@ -21,6 +21,7 @@ require_relative "capybara/lightpanda/auto_scripts"
21
21
  require_relative "capybara/lightpanda/node"
22
22
  require_relative "capybara/lightpanda/element_extension"
23
23
  require_relative "capybara/lightpanda/driver"
24
+ require_relative "capybara/lightpanda/railtie" if defined?(Rails::Railtie)
24
25
 
25
26
  module Capybara
26
27
  module Lightpanda
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capybara-lightpanda
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Navid Emad
@@ -89,6 +89,7 @@ files:
89
89
  - lib/capybara/lightpanda/node.rb
90
90
  - lib/capybara/lightpanda/options.rb
91
91
  - lib/capybara/lightpanda/process.rb
92
+ - lib/capybara/lightpanda/railtie.rb
92
93
  - lib/capybara/lightpanda/tasks/binary.rake
93
94
  - lib/capybara/lightpanda/utils/attempt.rb
94
95
  - lib/capybara/lightpanda/utils/event.rb