Skip to content

Commit

Permalink
[rb] implement navigation commands with BiDi (#14094)
Browse files Browse the repository at this point in the history
* [rb] create context manager and implement navigation with BiDi

* replace BrowsingContext class instead of creating new context manager

* Fix formatting issues

---------

Co-authored-by: Augustin Gottlieb <[email protected]>
Co-authored-by: aguspe <[email protected]>
  • Loading branch information
3 people authored Nov 20, 2024
1 parent 789e8d4 commit fefdba1
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 230 deletions.
110 changes: 61 additions & 49 deletions rb/lib/selenium/webdriver/bidi/browsing_context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,72 +17,84 @@
# specific language governing permissions and limitations
# under the License.

require_relative 'navigate_result'
require_relative 'browsing_context_info'

module Selenium
module WebDriver
class BiDi
# Implements the browsingContext Module of the WebDriver-BiDi specification
#
# @api private
#
class BrowsingContext
attr_accessor :id

READINESS_STATE = {
none: 'none',
interactive: 'interactive',
complete: 'complete'
'none' => 'none',
'eager' => 'interactive',
'normal' => 'complete'
}.freeze

def initialize(driver:, browsing_context_id: nil, type: nil, reference_context: nil)
unless driver.capabilities.web_socket_url
raise Error::WebDriverError,
'WebDriver instance must support BiDi protocol'
end

unless type.nil? || %i[window tab].include?(type)
raise ArgumentError,
"Valid types are :window & :tab. Received: #{type.inspect}"
end

@bidi = driver.bidi
@id = browsing_context_id.nil? ? create(type, reference_context)['context'] : browsing_context_id
# TODO: store current window handle in bridge object instead of always calling it
def initialize(bridge)
@bridge = bridge
@bidi = @bridge.bidi
page_load_strategy = bridge.capabilities[:page_load_strategy]
@readiness = READINESS_STATE[page_load_strategy]
end

def navigate(url:, readiness_state: nil)
unless readiness_state.nil? || READINESS_STATE.key?(readiness_state)
raise ArgumentError,
"Valid readiness states are :none, :interactive & :complete. Received: #{readiness_state.inspect}"
end

navigate_result = @bidi.send_cmd('browsingContext.navigate', context: @id, url: url,
wait: READINESS_STATE[readiness_state])

NavigateResult.new(
url: navigate_result['url'],
navigation_id: navigate_result['navigation']
)
# Navigates to the specified URL in the given browsing context.
#
# @param url [String] The URL to navigate to.
# @param context_id [String, NilClass] The ID of the browsing context to navigate in.
# Defaults to the window handle of the current context.
def navigate(url, context_id: nil)
context_id ||= @bridge.window_handle
@bidi.send_cmd('browsingContext.navigate', context: context_id, url: url, wait: @readiness)
end

def get_tree(max_depth: nil)
result = @bidi.send_cmd('browsingContext.getTree', root: @id, maxDepth: max_depth).dig('contexts', 0)

BrowsingContextInfo.new(
id: result['context'],
url: result['url'],
children: result['children'],
parent_context: result['parent']
)
# Traverses the browsing context history by a given delta.
#
# @param delta [Integer] The number of steps to traverse.
# Positive values go forwards, negative values go backwards.
# @param context_id [String, NilClass] The ID of the context to traverse.
# Defaults to the window handle of the current context.
def traverse_history(delta, context_id: nil)
context_id ||= @bridge.window_handle
@bidi.send_cmd('browsingContext.traverseHistory', context: context_id, delta: delta)
end

def close
@bidi.send_cmd('browsingContext.close', context: @id)
# Reloads the browsing context.
# @param [String, NilClass] context_id The ID of the context to reload.
# Defaults to the window handle of the current context.
# @param [Boolean] ignore_cache Whether to bypass the cache when reloading.
# Defaults to false.
def reload(context_id: nil, ignore_cache: false)
context_id ||= @bridge.window_handle
params = {context: context_id, ignore_cache: ignore_cache, wait: @readiness}
@bidi.send_cmd('browsingContext.reload', **params)
end

private
# Closes the browsing context.
#
# @param [String] context_id The ID of the context to close.
# Defaults to the window handle of the current context.
def close(context_id: nil)
context_id ||= @bridge.window_handle
@bidi.send_cmd('browsingContext.close', context: context_id)
end

def create(type, reference_context)
@bidi.send_cmd('browsingContext.create', type: type.to_s, referenceContext: reference_context)
# Create a new browsing context.
#
# @param [Symbol] type The type of browsing context to create.
# Valid options are :tab and :window with :window being the default
# @param [String] context_id The reference context for the new browsing context.
# Defaults to the current window handle.
#
# @return [String] The context ID of the created browsing context.
def create(type: nil, context_id: nil)
type ||= :window
context_id ||= @bridge.window_handle
result = @bidi.send_cmd('browsingContext.create', type: type.to_s, referenceContext: context_id)
result['context']
end
end # BrowsingContext
end
end # BiDi
end # WebDriver
end # Selenium
35 changes: 0 additions & 35 deletions rb/lib/selenium/webdriver/bidi/browsing_context_info.rb

This file was deleted.

33 changes: 0 additions & 33 deletions rb/lib/selenium/webdriver/bidi/navigate_result.rb

This file was deleted.

22 changes: 22 additions & 0 deletions rb/lib/selenium/webdriver/remote/bidi_bridge.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,22 @@ def create_session(capabilities)
@bidi = Selenium::WebDriver::BiDi.new(url: socket_url)
end

def get(url)
browsing_context.navigate(url)
end

def go_back
browsing_context.traverse_history(-1)
end

def go_forward
browsing_context.traverse_history(1)
end

def refresh
browsing_context.reload
end

def quit
super
ensure
Expand All @@ -38,6 +54,12 @@ def quit
def close
execute(:close_window).tap { |handles| bidi.close if handles.empty? }
end

private

def browsing_context
@browsing_context ||= WebDriver::BiDi::BrowsingContext.new(self)
end
end # BiDiBridge
end # Remote
end # WebDriver
Expand Down
20 changes: 8 additions & 12 deletions rb/sig/lib/selenium/webdriver/bidi/browsing_context.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,21 @@ module Selenium
module WebDriver
class BiDi
class BrowsingContext
@bidi: untyped
@bidi: BiDi

@id: untyped
READINESS_STATE: Hash[String, String]

attr_accessor id: untyped
def initialize: (Remote::Bridge bridge) -> void

READINESS_STATE: Hash[Symbol, String]
def navigate: (String url, String? context_id) -> void

def initialize: (driver: untyped, ?browsing_context_id: untyped?, ?type: untyped?, ?reference_context: untyped?) -> void
def traverse_history: (Integer delta, String? context_id) -> void

def navigate: (url: untyped, ?readiness_state: untyped?) -> untyped
def reload: (String? context_id, ?ignore_cache: bool) -> void

def get_tree: (?max_depth: untyped?) -> untyped
def close: (String? context_id) -> void

def close: () -> untyped

private

def create: (untyped type, untyped reference_context) -> untyped
def create: (?type: Symbol | String | nil, ?context_id: String | nil) -> String
end
end
end
Expand Down
24 changes: 18 additions & 6 deletions rb/sig/lib/selenium/webdriver/remote/bidi_bridge.rbs
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
module Selenium
module WebDriver
module Remote
class BiDiBridge < Bridge
@bidi: untyped
class BiDiBridge
@browsing_context: BiDi::BrowsingContext

attr_reader bidi: untyped
attr_reader bidi: BiDi

def create_session: (Hash[Symbol, String] capabilities) -> BiDi
def create_session: (untyped capabilities) -> void

def quit: () -> nil
def get: (String url) -> void

def close: () -> untyped
def go_back: () -> void

def go_forward: () -> void

def refresh: () -> void

def quit: () -> void

def close: () -> void

private

def browsing_context: () -> BiDi::BrowsingContext
end
end
end
Expand Down
Loading

0 comments on commit fefdba1

Please sign in to comment.