Skip to content
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

feat: add waitForXPath to ElementHandle #224

Merged
merged 2 commits into from Jun 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions development/generate_api_coverage.rb
Expand Up @@ -269,13 +269,13 @@ def api_coverages
end
end

raw_md = RawApiMdReader.new('v13.5.2').read
raw_md = RawApiMdReader.new('v15.1.1').read
class_docs = ApiMdParser.new(raw_md).parse


File.open(File.join('.', 'docs', 'api_coverage.md'), 'w') do |f|
f.write("# API coverages\n")
f.write("- Puppeteer version: v13.5.2\n")
f.write("- Puppeteer version: v15.1.1\n")
f.write("- puppeteer-ruby version: #{Puppeteer::VERSION}\n")
end

Expand Down
3 changes: 2 additions & 1 deletion docs/api_coverage.md
@@ -1,5 +1,5 @@
# API coverages
- Puppeteer version: v13.5.2
- Puppeteer version: v15.1.1
- puppeteer-ruby version: 0.40.7

## Puppeteer
Expand Down Expand Up @@ -297,6 +297,7 @@
* type => `#type_text`
* uploadFile => `#upload_file`
* waitForSelector => `#wait_for_selector`
* ~~waitForXPath~~

## HTTPRequest

Expand Down
75 changes: 75 additions & 0 deletions lib/puppeteer/element_handle.rb
Expand Up @@ -76,6 +76,81 @@ def wait_for_selector(selector, visible: nil, hidden: nil, timeout: nil)

define_async_method :async_wait_for_selector

# Wait for the `xpath` within the element. If at the moment of calling the
# method the `xpath` already exists, the method will return immediately. If
# the `xpath` doesn't appear after the `timeout` milliseconds of waiting, the
# function will throw.
#
# If `xpath` starts with `//` instead of `.//`, the dot will be appended automatically.
#
# This method works across navigation
# ```js
# const puppeteer = require('puppeteer');
# (async () => {
# const browser = await puppeteer.launch();
# const page = await browser.newPage();
# let currentURL;
# page
# .waitForXPath('//img')
# .then(() => console.log('First URL with image: ' + currentURL));
# for (currentURL of [
# 'https://example.com',
# 'https://google.com',
# 'https://bbc.com',
# ]) {
# await page.goto(currentURL);
# }
# await browser.close();
# })();
# ```
# @param xpath - A
# {@link https://developer.mozilla.org/en-US/docs/Web/XPath | xpath} of an
# element to wait for
# @param options - Optional waiting parameters
# @returns Promise which resolves when element specified by xpath string is
# added to DOM. Resolves to `null` if waiting for `hidden: true` and xpath is
# not found in DOM.
# @remarks
# The optional Argument `options` have properties:
#
# - `visible`: A boolean to wait for element to be present in DOM and to be
# visible, i.e. to not have `display: none` or `visibility: hidden` CSS
# properties. Defaults to `false`.
#
# - `hidden`: A boolean wait for element to not be found in the DOM or to be
# hidden, i.e. have `display: none` or `visibility: hidden` CSS properties.
# Defaults to `false`.
#
# - `timeout`: A number which is maximum time to wait for in milliseconds.
# Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default
# value can be changed by using the {@link Page.setDefaultTimeout} method.
def wait_for_xpath(xpath, visible: nil, hidden: nil, timeout: nil)
frame = @context.frame

secondary_world = frame.secondary_world
adopted_root = secondary_world.execution_context.adopt_element_handle(self)
param_xpath =
if xpath.start_with?('//')
".#{xpath}"
else
xpath
end
unless param_xpath.start_with?('.//')
adopted_root.dispose
raise ArgumentError.new("Unsupported xpath expression: #{xpath}")
end
handle = secondary_world.wait_for_xpath(param_xpath, visible: visible, hidden: hidden, timeout: timeout, root: adopted_root)
adopted_root.dispose
return nil unless handle

main_world = frame.main_world
result = main_world.execution_context.adopt_element_handle(handle)
handle.dispose
result
end

define_async_method :async_wait_for_xpath

def as_element
self
end
Expand Down
21 changes: 21 additions & 0 deletions spec/integration/element_handle_spec.rb
Expand Up @@ -299,6 +299,27 @@
end
end

describe '#wait_for_xpath' do
it 'should wait correctly with waitForXPath on an element' do
# Set the page content after the waitFor has been started.
page.content = <<~HTML
`<div id=el1>
el1
<div id=el2>
el2
</div>
</div>
<div id=el3>
el3
</div>`
HTML

el2 = page.wait_for_selector('#el1')
expect(el2.wait_for_xpath('//div').evaluate('el => el.id')).to eq('el2')
expect(el2.wait_for_xpath('.//div').evaluate('el => el.id')).to eq('el2')
end
end

describe '#hover' do
it 'should work', sinatra: true do
page.goto("#{server_prefix}/input/scrollable.html")
Expand Down