diff --git a/development/generate_api_coverage.rb b/development/generate_api_coverage.rb index 0f941c80..b0c4ef73 100644 --- a/development/generate_api_coverage.rb +++ b/development/generate_api_coverage.rb @@ -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 diff --git a/docs/api_coverage.md b/docs/api_coverage.md index 1c731e18..c526d76d 100644 --- a/docs/api_coverage.md +++ b/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 @@ -297,6 +297,7 @@ * type => `#type_text` * uploadFile => `#upload_file` * waitForSelector => `#wait_for_selector` +* ~~waitForXPath~~ ## HTTPRequest diff --git a/lib/puppeteer/element_handle.rb b/lib/puppeteer/element_handle.rb index d37c926e..7b687daa 100644 --- a/lib/puppeteer/element_handle.rb +++ b/lib/puppeteer/element_handle.rb @@ -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 diff --git a/spec/integration/element_handle_spec.rb b/spec/integration/element_handle_spec.rb index bd5fb8a6..e4f88563 100644 --- a/spec/integration/element_handle_spec.rb +++ b/spec/integration/element_handle_spec.rb @@ -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 + `
+ el1 +
+ el2 +
+
+
+ el3 +
` + 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")