class Capybara::Apparition::Page
Constants
- CSS_FIND_JS
- EVALUATE_ASYNC_JS
- EVALUATE_WITH_ID_JS
- EXECUTE_JS
- XPATH_FIND_JS
Attributes
browser_context_id[R]
keyboard[R]
modal_messages[R]
mouse[R]
network_traffic[R]
perm_headers[RW]
status_code[R]
target_id[R]
temp_headers[RW]
temp_no_redirect_headers[RW]
url_blacklist[R]
url_whitelist[R]
viewport_size[R]
Public Class Methods
create(browser, session, id, browser_context_id, ignore_https_errors: false, **options)
click to toggle source
# File lib/capybara/apparition/page.rb, line 18 def self.create(browser, session, id, browser_context_id, ignore_https_errors: false, **options) session.async_command 'Page.enable' # Provides a lot of info - but huge overhead # session.command 'Page.setLifecycleEventsEnabled', enabled: true page = Page.new(browser, session, id, browser_context_id, **options) session.async_commands 'Network.enable', 'Runtime.enable', 'Security.enable', 'DOM.enable' session.async_command 'Security.setIgnoreCertificateErrors', ignore: !!ignore_https_errors if Capybara.save_path session.async_command 'Page.setDownloadBehavior', behavior: 'allow', downloadPath: Capybara.save_path end page end
new(browser, session, target_id, browser_context_id, js_errors: false, url_blacklist: [], url_whitelist: [], extensions: [])
click to toggle source
# File lib/capybara/apparition/page.rb, line 35 def initialize(browser, session, target_id, browser_context_id, js_errors: false, url_blacklist: [], url_whitelist: [], extensions: []) @target_id = target_id @browser_context_id = browser_context_id @browser = browser @session = session @keyboard = Keyboard.new(self) @mouse = Mouse.new(self, @keyboard) @modals = [] @modal_messages = [] @frames = Capybara::Apparition::FrameManager.new(@target_id) @response_headers = {} @status_code = 0 @url_blacklist = url_blacklist || [] @url_whitelist = url_whitelist || [] @credentials = nil @auth_attempts = [] @proxy_credentials = nil @proxy_auth_attempts = [] @perm_headers = {} @temp_headers = {} @temp_no_redirect_headers = {} @viewport_size = nil @network_traffic = [] @open_resource_requests = {} @raise_js_errors = js_errors @js_error = nil @modal_mutex = Mutex.new @modal_closed = ConditionVariable.new register_event_handlers register_js_error_handler # if js_errors extensions.each do |name| add_extension(name) end setup_network_interception if browser.proxy_auth end
Public Instance Methods
add_extension(filename)
click to toggle source
# File lib/capybara/apparition/page.rb, line 90 def add_extension(filename) command('Page.addScriptToEvaluateOnNewDocument', source: File.read(filename)) rescue Errno::ENOENT raise ::Capybara::Apparition::BrowserError.new('name' => "Unable to load extension: #{filename}", 'args' => nil) end
add_modal(modal_response)
click to toggle source
# File lib/capybara/apparition/page.rb, line 96 def add_modal(modal_response) @last_modal_message = nil @modals.push(modal_response) end
async_command(name, **params)
click to toggle source
# File lib/capybara/apparition/page.rb, line 368 def async_command(name, **params) @browser.command_for_session(@session.session_id, name, params).discard_result end
clear_network_traffic()
click to toggle source
# File lib/capybara/apparition/page.rb, line 121 def clear_network_traffic @network_traffic = [] end
click_at(x, y)
click to toggle source
# File lib/capybara/apparition/page.rb, line 130 def click_at(x, y) wait_for_loaded @mouse.click_at(x: x, y: y) end
command(name, **params)
click to toggle source
# File lib/capybara/apparition/page.rb, line 364 def command(name, **params) @browser.command_for_session(@session.session_id, name, params).result end
content()
click to toggle source
# File lib/capybara/apparition/page.rb, line 270 def content wait_for_loaded _raw_evaluate("(function(){ let val = ''; if (document.doctype) val = new XMLSerializer().serializeToString(document.doctype); if (document.documentElement) val += document.documentElement.outerHTML; return val; })()") end
credentials=(creds)
click to toggle source
# File lib/capybara/apparition/page.rb, line 106 def credentials=(creds) @credentials = creds setup_network_interception end
current_frame_offset()
click to toggle source
# File lib/capybara/apparition/page.rb, line 139 def current_frame_offset frame_offset(current_frame) end
current_state()
click to toggle source
# File lib/capybara/apparition/page.rb, line 135 def current_state main_frame.state end
current_url()
click to toggle source
# File lib/capybara/apparition/page.rb, line 296 def current_url wait_for_loaded _raw_evaluate('window.location.href', context_id: main_frame.context_id) end
element_from_point(x:, y:)
click to toggle source
# File lib/capybara/apparition/page.rb, line 301 def element_from_point(x:, y:) r_o = _raw_evaluate("document.elementFromPoint(#{x}, #{y})", context_id: main_frame.context_id) while r_o&.[]('description')&.start_with?('iframe') frame_node = command('DOM.describeNode', objectId: r_o['objectId']) frame = @frames.get(frame_node.dig('node', 'frameId')) fo = frame_offset(frame) r_o = _raw_evaluate("document.elementFromPoint(#{x - fo[:x]}, #{y - fo[:y]})", context_id: frame.context_id) end r_o end
evaluate(script, *args)
click to toggle source
# File lib/capybara/apparition/page.rb, line 212 def evaluate(script, *args) eval_wrapped_script(EVALUATE_WITH_ID_JS, script, args) end
evaluate_async(script, _wait_time, *args)
click to toggle source
# File lib/capybara/apparition/page.rb, line 216 def evaluate_async(script, _wait_time, *args) eval_wrapped_script(EVALUATE_ASYNC_JS, script, args) end
execute(script, *args)
click to toggle source
# File lib/capybara/apparition/page.rb, line 207 def execute(script, *args) eval_wrapped_script(EXECUTE_JS, script, args) nil end
extra_headers()
click to toggle source
# File lib/capybara/apparition/page.rb, line 372 def extra_headers temp_headers.merge(perm_headers).merge(temp_no_redirect_headers) end
find(method, selector)
click to toggle source
# File lib/capybara/apparition/page.rb, line 195 def find(method, selector) wait_for_loaded js_escaped_selector = selector.gsub('\\', '\\\\\\').gsub('"', '\"') query = method == :css ? CSS_FIND_JS : XPATH_FIND_JS result = _raw_evaluate(format(query, selector: js_escaped_selector)) (result || []).map { |r_o| [self, r_o['objectId'], tag_name: r_o['description'].split(/[.#]/, 2)[0]] } rescue ::Capybara::Apparition::BrowserError => e raise unless /is not a valid (XPath expression|selector)/.match? e.name raise Capybara::Apparition::InvalidSelector, 'args' => [method, selector] end
frame_title()
click to toggle source
# File lib/capybara/apparition/page.rb, line 359 def frame_title wait_for_loaded _raw_evaluate('document.title') end
frame_url()
click to toggle source
# File lib/capybara/apparition/page.rb, line 312 def frame_url wait_for_loaded _raw_evaluate('window.location.href') end
fullscreen()
click to toggle source
# File lib/capybara/apparition/page.rb, line 341 def fullscreen result = @browser.command('Browser.getWindowForTarget', targetId: @target_id) @browser.command('Browser.setWindowBounds', windowId: result['windowId'], bounds: { windowState: 'fullscreen' }) end
go_back()
click to toggle source
# File lib/capybara/apparition/page.rb, line 227 def go_back wait_for_loaded go_history(-1) end
go_forward()
click to toggle source
# File lib/capybara/apparition/page.rb, line 232 def go_forward wait_for_loaded go_history(+1) end
inherit(page)
click to toggle source
# File lib/capybara/apparition/page.rb, line 383 def inherit(page) if page self.url_whitelist = page.url_whitelist.dup self.url_blacklist = page.url_blacklist.dup set_viewport(**page.viewport_size) if page.viewport_size end self end
js_error()
click to toggle source
# File lib/capybara/apparition/page.rb, line 392 def js_error res = @js_error @js_error = nil res end
maximize()
click to toggle source
# File lib/capybara/apparition/page.rb, line 346 def maximize screen_width, screen_height = *evaluate('[window.screen.width, window.screen.height]') set_viewport(width: screen_width, height: screen_height) result = @browser.command('Browser.getWindowForTarget', targetId: @target_id) @browser.command('Browser.setWindowBounds', windowId: result['windowId'], bounds: { windowState: 'maximized' }) end
pop_frame(top: false)
click to toggle source
# File lib/capybara/apparition/page.rb, line 191 def pop_frame(top: false) @frames.pop_frame(top: top) end
proxy_credentials=(creds)
click to toggle source
# File lib/capybara/apparition/page.rb, line 101 def proxy_credentials=(creds) @proxy_credentials = creds setup_network_interception end
push_frame(frame_el)
click to toggle source
# File lib/capybara/apparition/page.rb, line 172 def push_frame(frame_el) node = command('DOM.describeNode', objectId: frame_el.base.id) frame_id = node['node']['frameId'] timer = Capybara::Helpers.timer(expire_in: 10) while (frame = @frames[frame_id]).nil? || frame.loading? # Wait for the frame creation messages to be processed if timer.expired? puts 'Timed out waiting for frame to be ready' raise TimeoutError.new('push_frame') end sleep 0.1 end frame.element_id = frame_el.base.id @frames.push_frame(frame.id) frame end
refresh()
click to toggle source
# File lib/capybara/apparition/page.rb, line 220 def refresh wait_for_loaded main_frame.reloading! command('Page.reload', ignoreCache: true) wait_for_loaded end
render(options)
click to toggle source
# File lib/capybara/apparition/page.rb, line 143 def render(options) wait_for_loaded pixel_ratio = evaluate('window.devicePixelRatio') scale = (@browser.zoom_factor || 1).to_f / pixel_ratio if options[:format].to_s == 'pdf' params = { scale: scale } if @browser.paper_size params[:paperWidth] = @browser.paper_size[:width].to_f params[:paperHeight] = @browser.paper_size[:height].to_f end command('Page.printToPDF', **params) else clip_options = if options[:selector] pos = evaluate("document.querySelector('#{options.delete(:selector)}').getBoundingClientRect().toJSON();") %w[x y width height].each_with_object({}) { |key, hash| hash[key] = pos[key] } elsif options[:full] evaluate <<~JS { width: document.documentElement.clientWidth, height: document.documentElement.clientHeight} JS else evaluate <<~JS { width: window.innerWidth, height: window.innerHeight } JS end options[:clip] = { x: 0, y: 0, scale: scale }.merge(clip_options) command('Page.captureScreenshot', **options) end['data'] end
reset()
click to toggle source
# File lib/capybara/apparition/page.rb, line 80 def reset @modals.clear @modal_messages.clear @response_headers = {} @status_code = 0 @auth_attempts = [] @proxy_auth_attempts = [] @perm_headers = {} end
response_headers()
click to toggle source
# File lib/capybara/apparition/page.rb, line 237 def response_headers @response_headers[current_frame.id] || {} end
scroll_to(left, top)
click to toggle source
# File lib/capybara/apparition/page.rb, line 125 def scroll_to(left, top) wait_for_loaded execute('window.scrollTo(arguments[0], arguments[1])', left, top) end
set_viewport(width:, height:, screen: nil)
click to toggle source
# File lib/capybara/apparition/page.rb, line 317 def set_viewport(width:, height:, screen: nil) # wait_for_loaded @viewport_size = { width: width, height: height } result = @browser.command('Browser.getWindowForTarget', targetId: @target_id) begin @browser.command('Browser.setWindowBounds', windowId: result['windowId'], bounds: { width: width, height: height }) rescue WrongWorld # TODO: Fix Error naming here @browser.command('Browser.setWindowBounds', windowId: result['windowId'], bounds: { windowState: 'normal' }) retry end metrics = { mobile: false, width: width, height: height, deviceScaleFactor: 1 } metrics[:screenWidth], metrics[:screenHeight] = *screen if screen command('Emulation.setDeviceMetricsOverride', **metrics) end
title()
click to toggle source
# File lib/capybara/apparition/page.rb, line 354 def title wait_for_loaded _raw_evaluate('document.title', context_id: main_frame.context_id) end
update_headers(async: false)
click to toggle source
# File lib/capybara/apparition/page.rb, line 376 def update_headers(async: false) if (ua = extra_headers.find { |k, _v| /^User-Agent$/i.match? k }) send(async ? :async_command : :command, 'Network.setUserAgentOverride', userAgent: ua[1]) end setup_network_interception end
url_blacklist=(blacklist)
click to toggle source
# File lib/capybara/apparition/page.rb, line 111 def url_blacklist=(blacklist) @url_blacklist = blacklist setup_network_blocking end
url_whitelist=(whitelist)
click to toggle source
# File lib/capybara/apparition/page.rb, line 116 def url_whitelist=(whitelist) @url_whitelist = whitelist setup_network_blocking end
usable?()
click to toggle source
# File lib/capybara/apparition/page.rb, line 76 def usable? !!current_frame&.context_id end
visit(url)
click to toggle source
# File lib/capybara/apparition/page.rb, line 282 def visit(url) wait_for_loaded @status_code = 0 navigate_opts = { url: url, transitionType: 'reload' } navigate_opts[:referrer] = extra_headers['Referer'] if extra_headers['Referer'] response = command('Page.navigate', **navigate_opts) raise StatusFailError, 'args' => [url, response['errorText']] if response['errorText'] main_frame.loading(response['loaderId']) wait_for_loaded rescue TimeoutError raise StatusFailError.new('args' => [url]) end
wait_for_loaded(allow_obsolete: false)
click to toggle source
# File lib/capybara/apparition/page.rb, line 243 def wait_for_loaded(allow_obsolete: false) # We can't reliably detect if the page is loaded, so just ensure the context # is usable timer = Capybara::Helpers.timer(expire_in: 30) page_function = '(function(){ return 1 == 1; })()' begin response = command('Runtime.evaluate', expression: page_function, contextId: current_frame.context_id, returnByValue: false, awaitPromise: true) process_response(response) current_frame.loaded! rescue # rubocop:disable Style/RescueStandardError return if allow_obsolete && current_frame.obsolete? unless timer.expired? sleep 0.05 retry end puts 'Timedout waiting for page to be loaded' if ENV['DEBUG'] raise TimeoutError.new('wait_for_loaded') end raise JavascriptError.new(js_error) if @js_error end
Protected Instance Methods
current_frame()
click to toggle source
# File lib/capybara/apparition/page.rb, line 402 def current_frame @frames.current end
main_frame()
click to toggle source
# File lib/capybara/apparition/page.rb, line 406 def main_frame @frames.main end
Private Instance Methods
_execute_script(script, *args)
click to toggle source
# File lib/capybara/apparition/page.rb, line 689 def _execute_script(script, *args) args = args.map do |arg| if arg.is_a? Capybara::Apparition::Node { objectId: arg.id } else { value: arg } end end context_id = current_frame&.context_id response = command('Runtime.callFunctionOn', functionDeclaration: script, executionContextId: context_id, arguments: args, returnByValue: false, awaitPromise: true, userGesture: true) process_response(response) end
_raw_evaluate(page_function, context_id: nil)
click to toggle source
# File lib/capybara/apparition/page.rb, line 708 def _raw_evaluate(page_function, context_id: nil) wait_for_loaded return unless page_function.is_a? String context_id ||= current_frame.context_id response = command('Runtime.evaluate', expression: page_function, contextId: context_id, returnByValue: false, awaitPromise: true) process_response(response) end
accept_modal?(type, message:, manual:)
click to toggle source
# File lib/capybara/apparition/page.rb, line 675 def accept_modal?(type, message:, manual:) if type == :beforeunload true else response = @modals.pop if !response&.key?(type) manual ? manual_unexpected_modal(type) : auto_unexpected_modal(type) else @modal_messages.push(message) response[type].nil? ? true : response[type] end end end
auto_unexpected_modal(type)
click to toggle source
# File lib/capybara/apparition/page.rb, line 748 def auto_unexpected_modal(type) case type when :prompt warn 'Unexpected prompt modal - accepting with the default value.' \ 'You should be using `accept_prompt` or `dismiss_prompt`.' when :confirm warn 'Unexpected confirm modal - accepting.' \ 'You should be using `accept_confirm` or `dismiss_confirm`.' else warn 'Unexpected alert modal - clearing.' \ 'You should be using `accept_alert`.' end true end
eval_wrapped_script(wrapper, script, args)
click to toggle source
# File lib/capybara/apparition/page.rb, line 412 def eval_wrapped_script(wrapper, script, args) wait_for_loaded _execute_script format(wrapper, script: script), *args end
frame_offset(frame)
click to toggle source
# File lib/capybara/apparition/page.rb, line 417 def frame_offset(frame) return { x: 0, y: 0 } if frame.id == main_frame.id result = command('DOM.getBoxModel', objectId: frame.element_id) x, y = result['model']['content'] { x: x, y: y } end
go_history(delta)
click to toggle source
# File lib/capybara/apparition/page.rb, line 665 def go_history(delta) history = command('Page.getNavigationHistory') entry = history['entries'][history['currentIndex'] + delta] return nil unless entry main_frame.loading(-1) command('Page.navigateToHistoryEntry', entryId: entry['id']) wait_for_loaded end
handle_proxy_auth(interception_id)
click to toggle source
# File lib/capybara/apparition/page.rb, line 763 def handle_proxy_auth(interception_id) credentials_response = if @proxy_auth_attempts.include?(interception_id) puts 'Cancelling proxy auth' if ENV['DEBUG'] { response: 'CancelAuth' } else puts 'Replying with proxy auth credentials' if ENV['DEBUG'] @proxy_auth_attempts.push(interception_id) { response: 'ProvideCredentials' }.merge(@browser.proxy_auth || {}) end continue_request(interception_id, authChallengeResponse: credentials_response) end
handle_user_auth(interception_id)
click to toggle source
# File lib/capybara/apparition/page.rb, line 775 def handle_user_auth(interception_id) credentials_response = if @auth_attempts.include?(interception_id) puts 'Cancelling auth' if ENV['DEBUG'] { response: 'CancelAuth' } else @auth_attempts.push(interception_id) puts 'Replying with auth credentials' if ENV['DEBUG'] { response: 'ProvideCredentials' }.merge(@credentials || {}) end continue_request(interception_id, authChallengeResponse: credentials_response) end
manual_unexpected_modal(type)
click to toggle source
# File lib/capybara/apparition/page.rb, line 740 def manual_unexpected_modal(type) warn "An unexpected #{type} modal has opened - please close" @modal_mutex.synchronize do @modal_closed.wait(@modal_mutex) end nil end
process_intercepted_fetch(interception_id, request, resource_type)
click to toggle source
# File lib/capybara/apparition/page.rb, line 633 def process_intercepted_fetch(interception_id, request, resource_type) navigation = (resource_type == 'Document') headers, url = request.values_at('headers', 'url') headers = headers.merge(extra_headers) unless @temp_headers.empty? || navigation # rubocop:disable Style/IfUnlessModifier headers.delete_if { |name, value| @temp_headers[name] == value } end unless @temp_no_redirect_headers.empty? || !navigation headers.delete_if { |name, value| @temp_no_redirect_headers[name] == value } end if (accept = perm_headers.keys.find { |k| /accept/i.match? k }) headers[accept] = perm_headers[accept] end if @url_blacklist.any? { |r| url.match Regexp.escape(r).gsub('\*', '.*?') } async_command('Fetch.failRequest', errorReason: 'Failed', requestId: interception_id) elsif @url_whitelist.any? if @url_whitelist.any? { |r| url.match Regexp.escape(r).gsub('\*', '.*?') } async_command('Fetch.continueRequest', requestId: interception_id, headers: headers.map { |k, v| { name: k, value: v } }) else async_command('Fetch.failRequest', errorReason: 'Failed', requestId: interception_id) end else async_command('Fetch.continueRequest', requestId: interception_id, headers: headers.map { |k, v| { name: k, value: v } }) end end
process_response(response)
click to toggle source
# File lib/capybara/apparition/page.rb, line 722 def process_response(response) return nil if response.nil? exception = response['exceptionDetails']&.dig('exception') if exception case exception['className'] when 'DOMException' raise ::Capybara::Apparition::BrowserError.new('name' => exception['description'], 'args' => nil) when 'ObsoleteException' raise ::Capybara::Apparition::ObsoleteNode.new(self, '') if exception['value'] == 'ObsoleteNode' else raise Capybara::Apparition::JavascriptError, [exception['description']] end end DevToolsProtocol::RemoteObject.new(self, response['result']).value end
register_event_handlers()
click to toggle source
# File lib/capybara/apparition/page.rb, line 425 def register_event_handlers @session.on 'Page.javascriptDialogOpening' do |type:, message:, has_browser_handler:, **params| type = type.to_sym accept = accept_modal?(type, message: message, manual: has_browser_handler) next if accept.nil? if type == :prompt case accept when false async_command('Page.handleJavaScriptDialog', accept: false) when true async_command('Page.handleJavaScriptDialog', accept: true, promptText: params[:default_prompt]) else async_command('Page.handleJavaScriptDialog', accept: true, promptText: accept) end else async_command('Page.handleJavaScriptDialog', accept: accept) end end @session.on 'Page.javascriptDialogClosed' do @modal_mutex.synchronize do @modal_closed.signal end end @session.on 'Page.windowOpen' do |**params| puts "**** windowOpen was called with: #{params}" if ENV['DEBUG'] @browser.refresh_pages(opener: self) end @session.on 'Page.frameAttached' do |**params| puts "**** frameAttached called with #{params}" if ENV['DEBUG'] # @frames.get(params["frameId"]) = Frame.new(params) end @session.on 'Page.frameDetached' do |frame_id:, **params| @frames.delete(frame_id) puts "**** frameDetached called with #{frame_id} : #{params}" if ENV['DEBUG'] end @session.on 'Page.frameNavigated' do |frame:| puts "**** frameNavigated called with #{frame}" if ENV['DEBUG'] unless @frames.exists?(frame['id']) puts "**** creating frame for #{frame['id']}" if ENV['DEBUG'] @frames.add(frame['id'], frame) end @frames.get(frame['id'])&.loading(frame['loaderId'] || -1) end @session.on 'Page.frameStartedLoading' do |frame_id:| puts "Setting loading for #{frame_id}" if ENV['DEBUG'] @frames.get(frame_id)&.loading(-1) end @session.on 'Page.frameStoppedLoading' do |frame_id:| puts "Setting loaded for #{frame_id}" if ENV['DEBUG'] @frames.get(frame_id)&.loaded! end # @session.on 'Page.lifecycleEvent' do |params| # # Provides a lot of useful info - but lots of overhead # puts "Lifecycle: #{params['name']} - frame: #{params['frameId']} - loader: #{params['loaderId']}" if ENV['DEBUG'] # case params['name'] # when 'init' # @frames.get(params['frameId'])&.loading(params['loaderId']) # when 'firstMeaningfulPaint', # 'networkIdle' # @frames.get(params['frameId']).tap do |frame| # frame.loaded! if frame.loader_id == params['loaderId'] # end # end # end @session.on('Page.domContentEventFired') do # TODO: Really need something better than this main_frame.loaded! if @status_code != 200 end @session.on 'Page.navigatedWithinDocument' do |frame_id:, **params| puts "**** navigatedWithinDocument called with #{frame_id}: #{params}" if ENV['DEBUG'] @frames.get(frame_id).loaded! if frame_id == main_frame.id end @session.on 'Runtime.executionContextCreated' do |context:| frame_id = context.dig('auxData', 'frameId') if context.dig('auxData', 'isDefault') && frame_id if (frame = @frames.get(frame_id)) frame.context_id = context['id'] elsif ENV['DEBUG'] puts "unknown frame for context #{frame_id}" end end end @session.on 'Runtime.executionContextDestroyed' do |execution_context_id:, **params| puts "executionContextDestroyed: #{execution_context_id} : #{params}" if ENV['DEBUG'] @frames.destroy_context(execution_context_id) end @session.on 'Network.requestWillBeSent' do |request_id:, request: nil, **| @open_resource_requests[request_id] = request&.dig('url') end @session.on 'Network.responseReceived' do |request_id:, **| @open_resource_requests.delete(request_id) temp_headers.clear update_headers(async: true) end @session.on 'Network.requestWillBeSent' do |**params| @network_traffic.push(NetworkTraffic::Request.new(params)) end @session.on 'Network.responseReceived' do |request_id:, response:, **| req = @network_traffic.find { |request| request.request_id == request_id } req.response = NetworkTraffic::Response.new(response) if req end @session.on 'Network.responseReceived' do |type:, frame_id: nil, response: nil, **| if type == 'Document' @response_headers[frame_id] = response['headers'] @status_code = response['status'] end end @session.on 'Network.loadingFailed' do |type:, request_id:, blocked_reason: nil, error_text: nil, **params| req = @network_traffic.find { |request| request.request_id == request_id } req&.blocked_params = params if blocked_reason if type == 'Document' puts "Loading Failed - request: #{request_id} : #{error_text}" if ENV['DEBUG'] end end @session.on 'Fetch.requestPaused' do |request:, request_id:, resource_type:, **| process_intercepted_fetch(request_id, request, resource_type) end @session.on 'Fetch.authRequired' do |request_id:, auth_challenge: nil, **| next unless auth_challenge credentials_response = if auth_challenge['source'] == 'Proxy' if @proxy_auth_attempts.include?(request_id) puts 'Cancelling proxy auth' if ENV['DEBUG'] { response: 'CancelAuth' } else puts 'Replying with proxy auth credentials' if ENV['DEBUG'] @proxy_auth_attempts.push(request_id) { response: 'ProvideCredentials' }.merge(@browser.proxy_auth || {}) end elsif @auth_attempts.include?(request_id) puts 'Cancelling auth' if ENV['DEBUG'] { response: 'CancelAuth' } else @auth_attempts.push(request_id) puts 'Replying with auth credentials' if ENV['DEBUG'] { response: 'ProvideCredentials' }.merge(@credentials || {}) end async_command('Fetch.continueWithAuth', requestId: request_id, authChallengeResponse: credentials_response) end @session.on 'Runtime.consoleAPICalled' do |**params| # {"type"=>"log", "args"=>[{"type"=>"string", "value"=>"hello"}], "executionContextId"=>2, "timestamp"=>1548722854903.285, "stackTrace"=>{"callFrames"=>[{"functionName"=>"", "scriptId"=>"15", "url"=>"http://127.0.0.1:53977/", "lineNumber"=>6, "columnNumber"=>22}]}} details = params.dig(:stack_trace, 'callFrames')&.first @browser.console.log(params[:type], params[:args].map { |arg| arg['description'] || arg['value'] }.join(' ').to_s, source: details['url'].empty? ? nil : details['url'], line_number: details['lineNumber'].zero? ? nil : details['lineNumber'], columnNumber: details['columnNumber'].zero? ? nil : details['columnNumber']) end # @session.on 'Security.certificateError' do |params| # async_command 'Network.continueInterceptedRequest', interceptionId: id, **params # end # @session.on 'Log.entryAdded' do |params| # log_entry = params['entry'] # if params.values_at('source', 'level') == ['javascript', 'error'] # @js_error ||= params['string'] # end # end end
register_js_error_handler()
click to toggle source
# File lib/capybara/apparition/page.rb, line 609 def register_js_error_handler @session.on 'Runtime.exceptionThrown' do |exception_details: nil, **| @js_error ||= exception_details&.dig('exception', 'description') if @raise_js_errors details = exception_details&.dig('stackTrace', 'callFrames')&.first || exception_details || {} @browser.console.log('error', exception_details&.dig('exception', 'description'), source: details['url'].to_s.empty? ? nil : details['url'], line_number: details['lineNumber'].to_i.zero? ? nil : details['lineNumber'], columnNumber: details['columnNumber'].to_i.zero? ? nil : details['columnNumber']) end end
setup_network_blocking()
click to toggle source
# File lib/capybara/apparition/page.rb, line 623 def setup_network_blocking command 'Network.setBlockedURLs', urls: @url_blacklist setup_network_interception end
setup_network_interception()
click to toggle source
# File lib/capybara/apparition/page.rb, line 628 def setup_network_interception async_command 'Network.setCacheDisabled', cacheDisabled: true async_command 'Fetch.enable', handleAuthRequests: true end