Repository navigation
Support Capybara's finder hints in find_css and find_xpath #347
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,6 +41,38 @@ | |
| } | ||
| } | ||
|
|
||
| findWithHints(method, selector, hints, within) { | ||
| let nodes = this.find(method, selector, within).filter(node => node.nodeType == Node.ELEMENT_NODE); | ||
|
|
||
| if (hints.texts.length > 0) { | ||
| nodes = nodes.filter(node => this.containsTexts(node, hints.texts, hints.visible)); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] Bound eager hint work for large candidate sets
An experimental fallback above 512 candidates reduced the median to approximately 17 ms. Could we bound eager work and leave larger candidate sets to Capybara’s lazy filtering? The cutoff is experimental; a fallback would still need to exclude non-elements and preserve the node/cache representation. |
||
| } | ||
|
|
||
| return nodes.map(node => { | ||
| let cache = {}; | ||
| if (hints.visible) { | ||
| cache.visible = this.isVisible(node); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P3, non-blocking] Make the changed lazy-result semantics explicit This changes how not-yet-filtered candidates behave after the DOM changes. Starting with result = page.all("div", minimum: 1)
page.execute_script("document.querySelector('#second').style.display = 'none'")
result.map { |node| node[:id] }Base returns Capybara’s Selenium optimization already uses these caches for larger candidate sets, so this is a compatibility decision rather than an unsupported use of |
||
| } | ||
| if (hints.styles) { | ||
| let style = window.getComputedStyle(node); | ||
| cache.style = Object.fromEntries(hints.styles.map(name => [name, style.getPropertyValue(name)])); | ||
| } | ||
| if (hints.position) { | ||
| cache.position = node.getBoundingClientRect().toJSON(); | ||
| } | ||
| return [node, cache]; | ||
| }); | ||
| } | ||
|
|
||
| // Capybara matches text case sensitively against the visible text, or against all text | ||
| // with zero width characters removed. The texts hold no whitespace, so a node can only | ||
| // match when one of these contains every text. | ||
| containsTexts(node, texts, visible) { | ||
| let containsAll = content => texts.every(text => content.includes(text)); | ||
| return containsAll(node.textContent.replace(/[\u200b\u200e\u200f]/g, "")) || | ||
| (visible && containsAll(this.visibleText(node) || "")); | ||
| } | ||
|
|
||
| parents(node) { | ||
| let nodes = []; | ||
| let parent = node.parentNode; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| <!DOCTYPE html> | ||
| <html> | ||
| <body> | ||
| <ul id="items"> | ||
| <li id="shown" style="color: rgb(255, 0, 0)">Shown item</li> | ||
| <li id="hidden" style="display: none">Hidden item</li> | ||
| <li id="split">Split<span style="display: none">hidden</span>word</li> | ||
| <li id="zero_width">Zero​width</li> | ||
| <li id="shouted" style="text-transform: uppercase">shouted</li> | ||
| <li id="spaced">Spaced | ||
| out</li> | ||
| <li id="non_breaking">Non breaking</li> | ||
| </ul> | ||
| <p id="outside">Shown item</p> | ||
| </body> | ||
| </html> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P2] Avoid nested transport overhead on supported Ferrum 0.19.1
The gemspec allows Ferrum
~> 0.19.1, whose serializer recursively fetches object properties. Returning[node, {}]pairs adds twoRuntime.getPropertiesrequests per match even when no hints are requested. For 300 list items,page.all("li", visible: :all).sizeincreases from 303 to 903 CDP commands; directBrowser#findhas the same regression.Ferrum main’s deep serializer avoids this (both variants use one command), but the Gemfile’s main-branch dependency does not protect released-gem users. Could the no-hints path transport a plain node array and construct empty-cache pairs in Ruby, or require a released Ferrum version containing deep serialization before shipping?