From c58767fd639e6816fd3b62409ae0b4d4d4afae6e Mon Sep 17 00:00:00 2001 From: Aleksey Strizhak Date: Fri, 7 Jan 2022 05:28:54 +0500 Subject: [PATCH 01/12] extend evaluate_func with any options ability --- lib/ferrum/frame/runtime.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ferrum/frame/runtime.rb b/lib/ferrum/frame/runtime.rb index 926cc783..cbd351fe 100644 --- a/lib/ferrum/frame/runtime.rb +++ b/lib/ferrum/frame/runtime.rb @@ -76,8 +76,8 @@ def execute(expression, *args) true end - def evaluate_func(expression, *args, on: nil) - call(expression: expression, arguments: args, on: on) + def evaluate_func(expression, *args, on: nil, **options) + call(expression: expression, arguments: args, on: on, **options) end def evaluate_on(node:, expression:, by_value: true, wait: 0) From 83ca882255aca06d75ef5884d854903cd10be16d Mon Sep 17 00:00:00 2001 From: Aleksey Strizhak Date: Fri, 7 Jan 2022 05:36:53 +0500 Subject: [PATCH 02/12] implement #wait_for_selector method for browser --- lib/ferrum/browser.rb | 2 +- lib/ferrum/frame/dom.rb | 30 ++++++++++++++++++ lib/ferrum/page.rb | 2 +- spec/browser_spec.rb | 58 ++++++++++++++++++++++++++++++++++ spec/support/views/with_js.erb | 17 ++++++++++ 5 files changed, 107 insertions(+), 2 deletions(-) diff --git a/lib/ferrum/browser.rb b/lib/ferrum/browser.rb index 8926195f..751366cf 100644 --- a/lib/ferrum/browser.rb +++ b/lib/ferrum/browser.rb @@ -28,7 +28,7 @@ class Browser evaluate evaluate_on evaluate_async execute evaluate_func add_script_tag add_style_tag bypass_csp on goto position position= - playback_rate playback_rate=] => :page + playback_rate playback_rate= wait_for_selector] => :page delegate %i[default_user_agent] => :process attr_reader :client, :process, :contexts, :logger, :js_errors, :pending_connection_errors, diff --git a/lib/ferrum/frame/dom.rb b/lib/ferrum/frame/dom.rb index b60c6b0a..95835fa7 100644 --- a/lib/ferrum/frame/dom.rb +++ b/lib/ferrum/frame/dom.rb @@ -36,6 +36,36 @@ def body evaluate("document.documentElement.outerHTML") end + def wait_for_selector(css: nil, xpath: nil, timeout: 1000, interval: 100) + tap do + evaluate_func(%( + function(selector, isXpath, timeout, interval) { + var attempts = 0; + var max = timeout / interval; + function waitForSelector(resolve, reject) { + if (attempts > ((max < 1) ? 1 : max)) { + return reject(new Error("Not found element match the selector:" + selector)); + } + var element = isXpath + ? document. + evaluate(selector, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue + : document.querySelector(selector); + if (element !== null) { + return resolve(element); + } + setTimeout(function () { + waitForSelector(resolve, reject); + }, interval); + attempts++; + } + return new Promise(function (resolve, reject) { + waitForSelector(resolve, reject); + }); + } + ), css || xpath, css.nil? && !xpath.nil?, timeout, interval, awaitPromise: true) + end + end + def xpath(selector, within: nil) expr = <<~JS function(selector, within) { diff --git a/lib/ferrum/page.rb b/lib/ferrum/page.rb index 5a5c25f7..52db751d 100644 --- a/lib/ferrum/page.rb +++ b/lib/ferrum/page.rb @@ -34,7 +34,7 @@ def reset delegate %i[at_css at_xpath css xpath current_url current_title url title body doctype content= execution_id evaluate evaluate_on evaluate_async execute evaluate_func - add_script_tag add_style_tag] => :main_frame + add_script_tag add_style_tag wait_for_selector] => :main_frame include Animation include Screenshot diff --git a/spec/browser_spec.rb b/spec/browser_spec.rb index 7bc6672e..b7193182 100644 --- a/spec/browser_spec.rb +++ b/spec/browser_spec.rb @@ -504,6 +504,64 @@ module Ferrum expect(browser.evaluate("window.last_hashchange")).to eq("#foo") end + context "wait_for_selector" do + before do + browser.go_to("/ferrum/with_js") + end + + it "waits for provided css selector" do + expect( + browser.wait_for_selector(css: "div#wait_for_selector").at_css("div#wait_for_selector") + ).not_to be_nil + end + + it "waits for provided css hidden selector" do + expect( + browser.wait_for_selector(css: "div#wait_for_hidden_selector").at_css("div#wait_for_hidden_selector") + ).not_to be_nil + end + + it "waits for provided xpath selector" do + expect( + browser.wait_for_selector(xpath: "//div[@id='wait_for_selector']").at_css("div#wait_for_selector") + ).not_to be_nil + end + + it "waits for provided xpath hidden selector" do + expect( + browser + .wait_for_selector(xpath: "//div[@id='wait_for_hidden_selector']") + .at_css("div#wait_for_hidden_selector") + ).not_to be_nil + end + + it "raises error when timeout exceed" do + expect do + browser.wait_for_selector(css: "div#wait_for_selector", timeout: 800) + end.to raise_error(Ferrum::JavaScriptError, /Not found element match the selector/) + end + + it "raises error when provided invalid css" do + expect do + browser.wait_for_selector(css: "//div[@id='wait_for_selector']") + end.to raise_error(Ferrum::JavaScriptError, /Failed to execute 'querySelector' on 'Document'/) + end + + it "raises error when provided invalid xpath" do + expect do + browser.wait_for_selector(xpath: "div#wait_for_selector") + end.to raise_error(Ferrum::JavaScriptError, /Failed to execute 'evaluate' on 'Document'/) + end + + it "waits less than provided timeout when node found" do + Timeout.timeout(1) do + expect( + browser.wait_for_selector(css: "div#wait_for_selector", timeout: 2000).at_css("div#wait_for_selector") + ).not_to be_nil + end + end + end + context "current_url" do it "supports whitespace characters" do browser.go_to("/ferrum/arbitrary_path/200/foo%20bar%20baz") diff --git a/spec/support/views/with_js.erb b/spec/support/views/with_js.erb index 2ddefe35..50375efe 100644 --- a/spec/support/views/with_js.erb +++ b/spec/support/views/with_js.erb @@ -24,6 +24,23 @@ display: inline; } + From 051617d14bcbf8fd2081e1059e920a99cdcf3cee Mon Sep 17 00:00:00 2001 From: Aleksey Strizhak Date: Fri, 7 Jan 2022 05:57:21 +0500 Subject: [PATCH 03/12] fix spec with hardcoded value of size for with_js view due to added lines --- spec/network/response_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/network/response_spec.rb b/spec/network/response_spec.rb index 6b65739f..588e497e 100644 --- a/spec/network/response_spec.rb +++ b/spec/network/response_spec.rb @@ -42,7 +42,7 @@ class Network %r{/ferrum/jquery.min.js$} => File.size("#{PROJECT_ROOT}/spec/support/public/jquery-1.11.3.min.js"), %r{/ferrum/jquery-ui.min.js$} => File.size("#{PROJECT_ROOT}/spec/support/public/jquery-ui-1.11.4.min.js"), %r{/ferrum/test.js$} => File.size("#{PROJECT_ROOT}/spec/support/public/test.js"), - %r{/ferrum/with_js$} => 2343 + %r{/ferrum/with_js$} => 2919 } resources_size.each do |resource, size| From 8d98b79952e443b108713c495adf4c09e00c9123 Mon Sep 17 00:00:00 2001 From: Aleksey Strizhak Date: Wed, 12 Jan 2022 17:26:02 +0500 Subject: [PATCH 04/12] increase default timeout to 5 sec --- lib/ferrum/frame/dom.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ferrum/frame/dom.rb b/lib/ferrum/frame/dom.rb index 95835fa7..f9186ade 100644 --- a/lib/ferrum/frame/dom.rb +++ b/lib/ferrum/frame/dom.rb @@ -36,7 +36,7 @@ def body evaluate("document.documentElement.outerHTML") end - def wait_for_selector(css: nil, xpath: nil, timeout: 1000, interval: 100) + def wait_for_selector(css: nil, xpath: nil, timeout: 5000, interval: 100) tap do evaluate_func(%( function(selector, isXpath, timeout, interval) { @@ -44,7 +44,7 @@ def wait_for_selector(css: nil, xpath: nil, timeout: 1000, interval: 100) var max = timeout / interval; function waitForSelector(resolve, reject) { if (attempts > ((max < 1) ? 1 : max)) { - return reject(new Error("Not found element match the selector:" + selector)); + return reject(new Error("Not found element match the selector: " + selector)); } var element = isXpath ? document. From 0f0fbe0a2dcf8bef9ad2f541d6b21a3f168e5ea3 Mon Sep 17 00:00:00 2001 From: Aleksey Strizhak Date: Sat, 15 Jan 2022 11:05:41 +0500 Subject: [PATCH 05/12] add spec: 'waits for selector within frame' --- spec/browser_spec.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/spec/browser_spec.rb b/spec/browser_spec.rb index b7193182..409601eb 100644 --- a/spec/browser_spec.rb +++ b/spec/browser_spec.rb @@ -560,6 +560,25 @@ module Ferrum ).not_to be_nil end end + + it "waits for selector within frame" do + browser.execute <<-JS + setTimeout(function(){ + document.body.innerHTML += "