Skip to content

Commit

Permalink
Merge pull request #11 from YusukeIwaki/add_browser_spec
Browse files Browse the repository at this point in the history
add RSpec: Browser, BrowserContext
  • Loading branch information
YusukeIwaki authored Jul 24, 2020
2 parents 2f8ce36 + 37769b7 commit 07cd8b5
Show file tree
Hide file tree
Showing 8 changed files with 272 additions and 16 deletions.
6 changes: 5 additions & 1 deletion lib/puppeteer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,11 @@ def connect(
}.compact
browser = launcher.connect(options)
if block_given?
yield(browser)
begin
yield(browser)
ensure
browser.disconnect
end
else
browser
end
Expand Down
29 changes: 22 additions & 7 deletions lib/puppeteer/browser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def initialize(connection:, context_ids:, ignore_https_errors:, default_viewport
@default_context = Puppeteer::BrowserContext.new(@connection, self, nil)
@contexts = {}
context_ids.each do |context_id|
@contexts[context_id] = Puppeteer::BrowserContext.new(@connection, self. context_id)
@contexts[context_id] = Puppeteer::BrowserContext.new(@connection, self, context_id)
end
@targets = {}
@connection.on_event 'Events.Connection.Disconnected' do
Expand All @@ -70,6 +70,15 @@ def on(event_name, &block)
add_event_listener(EVENT_MAPPINGS[event_name.to_sym], &block)
end

# @param event_name [Symbol]
def once(event_name, &block)
unless EVENT_MAPPINGS.has_key?(event_name.to_sym)
raise ArgumentError.new("Unknown event name: #{event_name}. Known events are #{EVENT_MAPPINGS.keys.join(", ")}")
end

observe_first(EVENT_MAPPINGS[event_name.to_sym], &block)
end

# @return [Puppeteer::BrowserRunner::BrowserProcess]
def process
@process
Expand All @@ -94,7 +103,7 @@ def default_browser_context
# @param context_id [String]
def dispose_context(context_id)
@connection.send_message('Target.disposeBrowserContext', browserContextId: context_id)
@contexts.remove(context_id)
@contexts.delete(context_id)
end

class TargetAlreadyExistError < StandardError
Expand Down Expand Up @@ -166,7 +175,7 @@ def handle_target_info_changed(event)
end

# @return [String]
def websocket_endpoint
def ws_endpoint
@connection.url
end

Expand Down Expand Up @@ -204,12 +213,11 @@ def target
@targets[target_id]
end

# @param {function(!Target):boolean} predicate
# @param {{timeout?: number}=} options
# @return {!Promise<!Target>}
# @param predicate [Proc(Puppeteer::Target -> Boolean)]
# @return [Puppeteer::Target]
def wait_for_target(predicate:, timeout: nil)
timeout_in_sec = (timeout || 30000).to_i / 1000.0
existing_target = targets.first { |target| predicate.call(target) }
existing_target = targets.find { |target| predicate.call(target) }
return existing_target if existing_target

event_listening_ids = []
Expand All @@ -233,11 +241,18 @@ def wait_for_target(predicate:, timeout: nil)
else
target_promise.value!
end
rescue Timeout::Error
raise Puppeteer::TimeoutError.new("waiting for target failed: timeout #{timeout}ms exceeded")
ensure
remove_event_listener(*event_listening_ids)
end
end

# @!method async_wait_for_target(predicate:, timeout: nil)
#
# @param predicate [Proc(Puppeteer::Target -> Boolean)]
define_async_method :async_wait_for_target

# @return {!Promise<!Array<!Puppeteer.Page>>}
def pages
browser_contexts.flat_map(&:pages)
Expand Down
40 changes: 35 additions & 5 deletions lib/puppeteer/browser_context.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
class Puppeteer::BrowserContext
include Puppeteer::EventCallbackable
using Puppeteer::DefineAsyncMethod

# @param {!Puppeteer.Connection} connection
# @param {!Browser} browser
Expand All @@ -10,28 +11,57 @@ def initialize(connection, browser, context_id)
@id = context_id
end

EVENT_MAPPINGS = {
disconnected: 'Events.BrowserContext.Disconnected',
targetcreated: 'Events.BrowserContext.TargetCreated',
targetchanged: 'Events.BrowserContext.TargetChanged',
targetdestroyed: 'Events.BrowserContext.TargetDestroyed',
}

# @param event_name [Symbol] either of :disconnected, :targetcreated, :targetchanged, :targetdestroyed
def on(event_name, &block)
unless EVENT_MAPPINGS.has_key?(event_name.to_sym)
raise ArgumentError.new("Unknown event name: #{event_name}. Known events are #{EVENT_MAPPINGS.keys.join(", ")}")
end

add_event_listener(EVENT_MAPPINGS[event_name.to_sym], &block)
end

# @param event_name [Symbol]
def once(event_name, &block)
unless EVENT_MAPPINGS.has_key?(event_name.to_sym)
raise ArgumentError.new("Unknown event name: #{event_name}. Known events are #{EVENT_MAPPINGS.keys.join(", ")}")
end

observe_first(EVENT_MAPPINGS[event_name.to_sym], &block)
end

# @return {!Array<!Target>} target
def targets
@browser.targets.select { |target| target.browser_context == self }
end

# @param {function(!Target):boolean} predicate
# @param {{timeout?: number}=} options
# @return {!Promise<!Target>}
# @param predicate [Proc(Puppeteer::Target -> Boolean)]
# @return [Puppeteer::Target]
def wait_for_target(predicate:, timeout: nil)
@browser.wait_for_target(
predicate: ->(target) { target.browser_context == self && predicate.call(target) },
timeout: timeout,
)
end

# @!method async_wait_for_target(predicate:, timeout: nil)
#
# @param predicate [Proc(Puppeteer::Target -> Boolean)]
define_async_method :async_wait_for_target

# @return {!Promise<!Array<!Puppeteer.Page>>}
def pages
targets.select { |target| target.type == 'page' }.map(&:page).reject { |page| !page }
end

def incognito?
!@id
!!@id
end

# /**
Expand Down Expand Up @@ -82,7 +112,7 @@ def browser
end

def close
if !@id
unless @id
raise 'Non-incognito profiles cannot be closed!'
end
@browser.dispose_context(@id)
Expand Down
7 changes: 6 additions & 1 deletion lib/puppeteer/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,18 @@ def initialize(url, transport, delay = 0)
async_handle_message(message)
end
@transport.on_close do |reason, code|
handle_close(reason, code)
handle_close
end

@sessions = {}
@closed = false
end

# used only in Browser#connected?
def closed?
@closed
end

private def sleep_before_handling_message(message)
# Puppeteer doesn't handle any Network monitoring responses.
# So we don't have to sleep.
Expand Down
15 changes: 13 additions & 2 deletions lib/puppeteer/page.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1080,8 +1080,19 @@ def select(selector, *values)
define_async_method :async_select

# @param selector [String]
def tap(selector)
main_frame.tap(selector)
def tap(selector: nil, &block)
# resolves double meaning of tap.
if selector.nil? && block
# Original usage of Object#tap.
#
# browser.new_page.tap do |page|
# ...
# end
super(&block)
else
# Puppeteer's Page#tap.
main_frame.tap(selector)
end
end

define_async_method :async_tap
Expand Down
7 changes: 7 additions & 0 deletions lib/puppeteer/web_socket.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@ def initialize(url)

def write(data)
@socket.write(data)
rescue Errno::EPIPE
raise EOFError.new('already closed')
end

def readpartial(maxlen = 1024)
@socket.readpartial(maxlen)
rescue Errno::ECONNRESET
raise EOFError.new('closed by remote')
end
end

Expand All @@ -40,6 +44,9 @@ def initialize(url:, max_payload_size:)
rescue EOFError
# Google Chrome was gone.
# We have nothing todo. Just finish polling.
if @ready_state < STATE_CLOSING
handle_on_close(reason: 'Going Away', code: 1001)
end
end
end

Expand Down
Loading

0 comments on commit 07cd8b5

Please sign in to comment.