capybara-lightpanda 0.15.0 → 0.16.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: bcfaa2e5c56f74e8f6068a969ee03d669d11d49de80ae360fb4a62a96833c8f3
4
- data.tar.gz: 3a22106094a7f2de8c3447f9573286e90a4158f25f88b06701cf4540ab9e2147
3
+ metadata.gz: a8f6c1cfdad13de4569b3d79828d9d19848b166e220b6a91329c7c0cb2fa9781
4
+ data.tar.gz: 2f7c90bb35b746682f5533c9d7ebfe1b64fbf7aea1f53a6c93f563428d9f3b46
5
5
  SHA512:
6
- metadata.gz: ebf7c506245fdc550ff5ac3bc643dfe6369c4324642bffe8fb4a27f891f009eaaa842effe0bb7386dfc6d50819ad934d0a3df0e7f67e4fb222116baa5a957955
7
- data.tar.gz: 2aaef86e3c5a6c1a4e25c3476790352564df15c0b46c18c02745b5999e587a35090ff88837340dec8e00f977a828ec3ce1eaf9a505b10abfb0412e2538cf4d13
6
+ metadata.gz: 1d1632dc29185af1e1d1c594abd7a9fe4b0af584585aa6c6534428d59b23316174db581d901bd7d67430b2e708c664c9c7175918cfeca4d386ba95453816b910
7
+ data.tar.gz: 76a79eed2e960c294a09c1ee8882014d543c5695cd519d51c3d9560ca7b153b085ae4161f18e2a11a301465b95c0cc71140a269c3c8970c86e93d291430630cb
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.16.0] - 2026-10-26
4
+
5
+ ### Fixed
6
+
7
+ - **`trigger(:click)` waits for the page it loads.** Calling `element.trigger(:click)` on a link or submit button returned before the new page arrived, so the next step ran against the old page. It now waits the way `click` does.
8
+ - **Clicking a `<a download>` link no longer cancels the download on recent nightlies.** From nightly 11097 (and the upcoming 1.1 release line), Lightpanda starts the download itself when a link has a `download` attribute. The driver then also went to the same URL, which cancelled the download and replaced the page. The driver now leaves the download to the browser. Older browsers behave as before.
9
+ - **`obscured?` sees overlays inside a shadow root.** On browsers that can hit-test inside shadow trees (recent nightlies), an element covered by another element in the same shadow root now counts as obscured; before, it was always reported as not obscured. Older browsers give the same answer as before.
10
+
3
11
  ## [0.15.0] - 2026-10-23
4
12
 
5
13
  > **Update Lightpanda before upgrading.** This release requires Lightpanda **1.0.2** or a nightly build ≥ 10965. The driver refuses anything older and names the version it found. Pin `1.0.2` for a reproducible browser.
@@ -12,10 +12,10 @@ module Capybara
12
12
  # `Browser.downloadProgress`. We mirror those into a completed-files list +
13
13
  # a blocking `wait`, the way ferrum's Downloads does.
14
14
  #
15
- # IMPORTANT: the trigger is `Content-Disposition: attachment`, NOT MIME
16
- # type. A `text/csv` (or any) response without that header is rendered as a
17
- # normal navigation, not downloaded — which is why Capybara's MIME-triggered
18
- # `:download` shared spec stays in capybara_skip.
15
+ # Navigation downloads require Content-Disposition: attachment, not just a
16
+ # MIME type. New browsers also honor an anchor's download attribute (build
17
+ # 11097 / 1.1 release line); Node#click lets that native activation finish.
18
+ # Capybara's MIME-only :download shared spec still stays in capybara_skip.
19
19
  #
20
20
  # Structure deliberately mirrors Network (same browser ref, same mutex +
21
21
  # subscribe/unsubscribe lifecycle, same reset-after-disposeBrowserContext
@@ -54,17 +54,24 @@ function isObscured(el) {
54
54
  if (cx < 0 || cy < 0 || cx > w || cy > h) return true;
55
55
  var hit = doc.elementFromPoint(cx, cy);
56
56
  if (!hit) return true;
57
- if (hit === el) return false;
58
- if (el.contains(hit)) return false;
59
- // A hit inside a shadow tree is retargeted to its host, and there is no
60
- // ShadowRoot.elementFromPoint to look further, so landing on one of el's own
61
- // hosts is the best "not obscured" answer available.
62
- var root = el.getRootNode ? el.getRootNode() : doc;
57
+ // Document hit testing retargets a shadow child to its outermost host.
58
+ // Check each enclosing shadow tree from the outside in: an inner root must
59
+ // never let us see through an overlay covering its host in an outer root.
60
+ var roots = [], root = el.getRootNode ? el.getRootNode() : doc;
63
61
  while (root && root !== doc && root.host) {
64
- if (hit === root.host) return false;
62
+ roots.push(root);
65
63
  root = root.host.getRootNode();
66
64
  }
67
- return true;
65
+ for (var i = roots.length - 1; i >= 0; i--) {
66
+ root = roots[i];
67
+ if (hit !== root.host) break;
68
+ // Older supported builds have no shadow hit testing. A hit on our own
69
+ // host remains the best available evidence that the child is actionable.
70
+ if (typeof root.elementFromPoint !== 'function') return false;
71
+ hit = root.elementFromPoint(cx, cy);
72
+ if (!hit) return true;
73
+ }
74
+ return hit !== el && !el.contains(hit);
68
75
  }
69
76
 
70
77
  // Capybara's `Node#disabled?` is more permissive than CSS `:disabled`:
@@ -135,7 +135,7 @@ module Capybara
135
135
  end
136
136
 
137
137
  def click(_keys = [], **_options)
138
- call(CLICK_JS)
138
+ call(CLICK_JS, native_anchor_downloads?)
139
139
  driver.browser.wait_for_idle
140
140
  end
141
141
 
@@ -178,7 +178,9 @@ module Capybara
178
178
  # generic Event for everything else (so callers can fire custom events
179
179
  # like `node.trigger('lp:custom')`).
180
180
  def trigger(event)
181
- call(TRIGGER_JS, event.to_s)
181
+ name = event.to_s
182
+ call(TRIGGER_JS, name)
183
+ driver.browser.wait_for_idle if name == "click"
182
184
  end
183
185
 
184
186
  def set(value, **_options)
@@ -374,6 +376,18 @@ module Capybara
374
376
 
375
377
  private
376
378
 
379
+ # Below #4124 a download attribute suppresses native activation entirely,
380
+ # so the manual navigation is still needed for attachment responses.
381
+ # The API has no feature marker: keep the floor path through 1.0.x and
382
+ # enable native download activation on build 11097 / the 1.1 release line.
383
+ def native_anchor_downloads?
384
+ browser = driver.browser
385
+ build = browser.nightly_build
386
+ return build >= Gem::Version.new("11097") if build
387
+
388
+ browser.release && browser.release >= Gem::Version.new("1.1.0")
389
+ end
390
+
377
391
  def find_hinted(method, selector, hints)
378
392
  driver.browser.find_within_hinted(@remote_object_id, method, selector, self.class.hint_spec(**hints))
379
393
  .map { |oid, cache| self.class.new(driver, oid, cache, frame_scoped: @frame_scoped) }
@@ -593,7 +607,7 @@ module Capybara
593
607
  # 6167 as duplicate `turbo:submit-start` events; the first request
594
608
  # gets aborted by the second and Turbo never renders the response.
595
609
  CLICK_JS = <<~JS
596
- function() {
610
+ function(nativeAnchorDownloads) {
597
611
  var EventCtor = (typeof MouseEvent !== 'undefined') ? MouseEvent : Event;
598
612
  // Emulate coordinate hit-testing without a layout engine. In a real
599
613
  // browser a pointer click on a container lands on its frontmost
@@ -658,6 +672,11 @@ module Capybara
658
672
  var external = !!scheme && !/^(https?|about|blob|data|file|javascript)$/i.test(scheme[1]);
659
673
  var notCancelled = hit.dispatchEvent(clickEvt);
660
674
  if (external || !notCancelled || clickEvt.defaultPrevented) return;
675
+ // Native activation has already started the download. Navigating
676
+ // again cancels it and replaces the document. Keep the complete
677
+ // synthetic event sequence and its cancellation, but stop here on
678
+ // browsers that support the download attribute.
679
+ if (nativeAnchorDownloads && link && link.hasAttribute('download')) return;
661
680
  var tag = hit.tagName;
662
681
  // A `javascript:` link never navigates: the click's own activation
663
682
  // behavior has already run its code. Assigning it to location.href
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Capybara
4
4
  module Lightpanda
5
- VERSION = "0.15.0"
5
+ VERSION = "0.16.0"
6
6
  end
7
7
  end
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.15.0
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Navid Emad