capybara-lightpanda 0.14.0 → 0.14.1
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 +4 -4
- data/CHANGELOG.md +7 -0
- data/lib/capybara/lightpanda/browser.rb +72 -9
- data/lib/capybara/lightpanda/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d38fc6a0a5cbba01bba3a0a1363d8e0577dc99c2cb8a77b81f407b273a91863b
|
|
4
|
+
data.tar.gz: 21cdea0aeba27736c52f800c22e850d80ea15d3c7bb1c6c9b173421bb227be7d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ec4a2743650df3602c43fc393a82af00c19c5b44d59fab31156aed0b3252e8fb7f3c5f06df1b6e2fe994c9b974a1b21b29707c22c6bff571d4c2b6aca195add4
|
|
7
|
+
data.tar.gz: ba19fbd3c2754eae2b6d3af02584d739eba498b93e0e0ff392f9dc7a9feefdc1af1ff68081cd5b6963ca41c768cf8cebcf3b48b9debd9923e5142b15ac1c9223
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.14.1] - 2026-10-12
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **A click that loads a new page waits for that page's scripts.** `click` used to return as soon as the new document existed, before its deferred and module scripts had run. Apps load Stimulus, Turbo and Bootstrap that way, so the next action could land on a page whose handlers were not wired yet: a Bootstrap collapse that did not open, a `confirm()` that never showed (`accept_confirm` raised `ModalNotFound`) while the form was submitted anyway, a form posted before its JavaScript was ready. The click now also waits for `DOMContentLoaded`, as Cuprite waits for the load event. `send_keys`, `go_back` and `go_forward` wait the same way when they load a new page. Clicks that don't navigate are as fast as before.
|
|
8
|
+
- **A click on a link that Turbo hands back to the browser waits for the new page.** With Turbo on the page, such a click returned immediately on the page being left, because Turbo reported activity and went idle again before the navigation started.
|
|
9
|
+
|
|
3
10
|
## [0.14.0] - 2026-10-11
|
|
4
11
|
|
|
5
12
|
### Fixed
|
|
@@ -167,6 +167,7 @@ module Capybara
|
|
|
167
167
|
subscribe_to_console_capture
|
|
168
168
|
subscribe_to_execution_context
|
|
169
169
|
subscribe_to_navigation
|
|
170
|
+
subscribe_to_document_ready
|
|
170
171
|
subscribe_to_turbo_signals
|
|
171
172
|
# Network owns the Network.* domain: enabling installs traffic
|
|
172
173
|
# tracking AND the navigation-response capture behind status_code.
|
|
@@ -425,13 +426,15 @@ module Capybara
|
|
|
425
426
|
#
|
|
426
427
|
# Sniff window: the action returns synchronously, but the CDP events
|
|
427
428
|
# signalling its async fallout arrive later on the dispatch thread. We
|
|
428
|
-
# poll briefly for
|
|
429
|
-
# subscribe_to_navigation)
|
|
430
|
-
#
|
|
431
|
-
#
|
|
432
|
-
#
|
|
433
|
-
#
|
|
434
|
-
#
|
|
429
|
+
# poll briefly for either of them — a main-frame navigation start (see
|
|
430
|
+
# subscribe_to_navigation) or Runtime.executionContextsCleared — and if
|
|
431
|
+
# none fires within the window, assume the action was inert and exit
|
|
432
|
+
# fast. Lightpanda announces a navigation ~5 ms after the click but only
|
|
433
|
+
# swaps the execution context once the response lands, so watching the
|
|
434
|
+
# context alone returned on the old document whenever the server took
|
|
435
|
+
# longer than the window. The turbo sentinel does not end the sniff
|
|
436
|
+
# (it is awaited after): Turbo goes busy then idle within a few ms on a
|
|
437
|
+
# link it hands back to the browser, before the navigation start lands.
|
|
435
438
|
SNIFF_WINDOW = 0.05
|
|
436
439
|
private_constant :SNIFF_WINDOW
|
|
437
440
|
|
|
@@ -444,7 +447,6 @@ module Capybara
|
|
|
444
447
|
navigating = @navigation_event.iteration > prior_navigation_iteration
|
|
445
448
|
break if navigating
|
|
446
449
|
break if @default_context_event.iteration > prior_context_iteration
|
|
447
|
-
break unless @turbo_event.set?
|
|
448
450
|
break if monotonic_time > sniff_deadline
|
|
449
451
|
|
|
450
452
|
sleep 0.001
|
|
@@ -452,7 +454,10 @@ module Capybara
|
|
|
452
454
|
|
|
453
455
|
# Only a navigation this action started is awaited: one left pending
|
|
454
456
|
# by an earlier load must not make every later click pay the timeout.
|
|
455
|
-
|
|
457
|
+
if navigating
|
|
458
|
+
@navigation_event.wait(@options.timeout)
|
|
459
|
+
wait_for_document_ready
|
|
460
|
+
end
|
|
456
461
|
@default_context_event.wait(@options.timeout)
|
|
457
462
|
@turbo_event.wait(@options.timeout)
|
|
458
463
|
check_unhandled_modal!
|
|
@@ -658,6 +663,64 @@ module Capybara
|
|
|
658
663
|
end
|
|
659
664
|
end
|
|
660
665
|
|
|
666
|
+
# The new document's context exists before its deferred and module
|
|
667
|
+
# scripts have run: this event marks the end of that phase, at
|
|
668
|
+
# Page.domContentEventFired or anything later that proves it is over
|
|
669
|
+
# (load, the frame stopping, the document request failing).
|
|
670
|
+
def subscribe_to_document_ready
|
|
671
|
+
@document_ready_event = Utils::Event.new
|
|
672
|
+
@document_ready_event.set
|
|
673
|
+
|
|
674
|
+
on("Page.frameStartedNavigating") do |params|
|
|
675
|
+
@document_ready_event.reset if main_frame_navigation_start?(params)
|
|
676
|
+
end
|
|
677
|
+
on("Page.domContentEventFired") { @document_ready_event.set }
|
|
678
|
+
on("Page.loadEventFired") { @document_ready_event.set }
|
|
679
|
+
on("Page.frameStoppedLoading") do |params|
|
|
680
|
+
@document_ready_event.set if params["frameId"] == @target_id
|
|
681
|
+
end
|
|
682
|
+
on("Network.loadingFailed") do |params|
|
|
683
|
+
@document_ready_event.set if params["requestId"] == @navigation_request_id
|
|
684
|
+
end
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
# After a click that navigated, the next action must not run before the
|
|
688
|
+
# new page's JS has wired its handlers: Stimulus, Bootstrap's data-api
|
|
689
|
+
# and Turbo load as module scripts, which run after the document commits
|
|
690
|
+
# and before DOMContentLoaded. Cuprite waits for the load event. The
|
|
691
|
+
# event is awaited briefly, then document.readyState is polled for the
|
|
692
|
+
# rest of the budget, as #go_to does (lightpanda-io/browser#1801: page
|
|
693
|
+
# events may never come). Never raises: a page that never settles costs
|
|
694
|
+
# the timeout once, then the action returns as it did before.
|
|
695
|
+
DOCUMENT_READY_EVENT_WAIT = 2
|
|
696
|
+
private_constant :DOCUMENT_READY_EVENT_WAIT
|
|
697
|
+
|
|
698
|
+
def wait_for_document_ready
|
|
699
|
+
deadline = monotonic_time + @options.timeout
|
|
700
|
+
return if @document_ready_event.wait([DOCUMENT_READY_EVENT_WAIT, @options.timeout].min)
|
|
701
|
+
|
|
702
|
+
remaining = deadline - monotonic_time
|
|
703
|
+
return unless remaining.positive?
|
|
704
|
+
|
|
705
|
+
Utils::Wait.until(timeout: remaining, interval: 0.1) do
|
|
706
|
+
@document_ready_event.set? || @client.closed? || document_complete?
|
|
707
|
+
end
|
|
708
|
+
rescue TimeoutError
|
|
709
|
+
nil
|
|
710
|
+
end
|
|
711
|
+
|
|
712
|
+
def document_complete?
|
|
713
|
+
response = @client.command(
|
|
714
|
+
"Runtime.evaluate",
|
|
715
|
+
{ expression: "document.readyState", returnByValue: true },
|
|
716
|
+
session_id: @session_id,
|
|
717
|
+
timeout: 2
|
|
718
|
+
)
|
|
719
|
+
response.dig("result", "value") == "complete"
|
|
720
|
+
rescue Error
|
|
721
|
+
false
|
|
722
|
+
end
|
|
723
|
+
|
|
661
724
|
def main_frame_navigation_start?(params)
|
|
662
725
|
params["frameId"] == @target_id && !params["navigationType"].to_s.match?(/same/i)
|
|
663
726
|
end
|