Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 7 additions & 25 deletions lib/appium_lib_core/common/base/bridge.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ class Bridge < ::Selenium::WebDriver::Remote::Bridge
# No 'browserName' means the session is native appium connection
APPIUM_NATIVE_BROWSER_NAME = 'appium'

attr_reader :available_commands

def browser
@browser ||= begin
name = @capabilities&.browser_name
Expand All @@ -74,7 +72,6 @@ def browser
# )
#
def attach_to(session_id, platform_name, automation_name)
@available_commands = ::Appium::Core::Commands::COMMANDS.dup
@session_id = session_id

# generate a dummy capabilities instance which only has the given platformName and automationName
Expand Down Expand Up @@ -109,8 +106,6 @@ def attach_to(session_id, platform_name, automation_name)
# driver = core.start_driver
#
def create_session(capabilities)
@available_commands = ::Appium::Core::Commands::COMMANDS.dup

always_match = add_appium_prefix(capabilities)
response = execute(:new_session, {}, { capabilities: { alwaysMatch: always_match, firstMatch: [{}] } })

Expand Down Expand Up @@ -162,29 +157,16 @@ def json_create(value)

public

# command for Appium 2.0.

# Example:
# driver.add_command(name: :available_contexts, method: :get, url: 'session/:session_id/contexts') do
# execute(:available_contexts, {}) || []
# end
# Then,
# driver.available_contexts #=> ["NATIVE_APP"]

# def add_command(method:, url:, name:, &block)
# Bridge.add_command name, method, url, &block
# end

def add_command(method:, url:, name:, &block)
::Appium::Logger.info "Overriding the method '#{name}' for '#{url}'" if @available_commands.key? name

@available_commands[name] = [method, url]

::Appium::Core::Device.add_endpoint_method name, &block
# Override Selenium's command_list to use Appium's command definitions
# instead of the default W3C COMMANDS from Selenium.
def command_list
::Appium::Core::Commands::COMMANDS
end

# Override Selenium's commands to resolve extra_commands from this subclass
# rather than the parent Selenium::WebDriver::Remote::Bridge.
def commands(command)
@available_commands[command] || Bridge.extra_commands[command]
command_list[command] || self.class.extra_commands&.[](command)
end

def status
Expand Down
61 changes: 41 additions & 20 deletions lib/appium_lib_core/common/base/driver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -135,25 +135,21 @@ def update_sending_request_to(protocol:, host:, port:, path:)
# drivers/plugins in Appium 2.0. Appium 2.0 and its custom drivers/plugins allow you
# to define custom commands that are not part of W3C spec.
#
# Uses Selenium's +Bridge.add_command+ under the hood to register the command
# and define the method on the bridge.
#
# @param [Symbol] method HTTP request method as https://www.w3.org/TR/webdriver/#endpoints
# @param [string] url The url to URL template as https://www.w3.org/TR/webdriver/#endpoints.
# +:session_id+ is the placeholder of 'session id'.
# Other place holders can be specified with +:+ prefix like +:id+.
# Then, the +:id+ will be replaced with a given value as the seconds argument of +execute+
# @param [Symbol] name The name of method that is called as the driver instance method.
# @param [Proc] block The block to involve as the method.
# Please define a method that has the same +name+ with arguments you want.
# The method must has +execute+ method. tHe +execute+ is calls the +url+
# with the given parameters.
# The first argument should be +name+ as symbol.
# The second argument should be hash. If keys in the hash matches +:+ prefix
# string in the given url, the matched string in the given url will be
# values in the hash.
# The third argument should be hash. The hash will be the request body.
# Please read examples below for more details.
# @param [Proc] block The block becomes the method body. It is executed in the context of
# the bridge instance, so +execute+ is available. When no block is given,
# a default implementation calling +execute(name)+ is used.
# @raise [ArgumentError] If the given +method+ is invalid value.
#
# @example
# @example Simple GET command (no block)
#
# @driver.add_command(
# method: :get,
Expand All @@ -163,30 +159,37 @@ def update_sending_request_to(protocol:, host:, port:, path:)
# # Send a GET request to 'session/<session id>/path/to/custom/url'
# @driver.test_command
#
# @example POST command with arguments (do/end block)
#
# @driver.add_command(
# method: :post,
# url: 'session/:session_id/path/to/custom/url',
# name: :test_command
# ) do
# def test_command(argument)
# execute(:test_command, {}, { dummy: argument })
# end
# ) do |argument|
# execute(:test_command, {}, { dummy: argument })
# end
# # Send a POST request to 'session/<session id>/path/to/custom/url'
# # with body "{ dummy: 1 }" as JSON object. "1" is the argument.
# # ':session_id' in the given 'url' is replaced with current 'session id'.
# @driver.test_command(1)
#
# @example POST command with arguments (inline block)
#
# @driver.add_command(
# method: :post,
# url: 'session/:session_id/path/to/custom/url',
# name: :test_command
# ) { |argument| execute(:test_command, {}, { dummy: argument }) }
# @driver.test_command(1)
#
# @example POST command with URL placeholders (do/end block)
#
# @driver.add_command(
# method: :post,
# url: 'session/:session_id/element/:id/custom/action',
# name: :test_action_command
# ) do
# def test_action_command(element_id, action)
# execute(:test_action_command, {id: element_id}, { dummy_action: action })
# end
# ) do |element_id, action|
# execute(:test_action_command, {id: element_id}, { dummy_action: action })
# end
# # Send a POST request to 'session/<session id>/element/<element id>/custom/action'
# # with body "{ dummy_action: #{action} }" as JSON object. "action" is the seconds argument.
Expand All @@ -195,10 +198,28 @@ def update_sending_request_to(protocol:, host:, port:, path:)
# e = @driver.find_element :accessibility_id, 'an element'
# @driver.test_action_command(e.id, 'action')
#
# @example POST command with URL placeholders (inline block)
#
# @driver.add_command(
# method: :post,
# url: 'session/:session_id/element/:id/custom/action',
# name: :test_action_command
# ) { |element_id, action| execute(:test_action_command, {id: element_id}, { dummy_action: action }) }
# e = @driver.find_element :accessibility_id, 'an element'
# @driver.test_action_command(e.id, 'action')
#
def add_command(method:, url:, name:, &block)
raise ::Appium::Core::Error::ArgumentError, "Available method is either #{AVAILABLE_METHODS}" unless AVAILABLE_METHODS.include? method

@bridge.add_command method: method, url: url, name: name, &block
::Appium::Logger.info "Overriding the command '#{name}' for '#{url}'" if Bridge.extra_commands&.key?(name)

# Use Selenium's Bridge.add_command to register the command and define the method.
# When no block is given, create a default implementation that calls execute.
block ||= proc { execute(name) }
Bridge.add_command(name, method, url, &block)

# Ensure the driver delegates the new method to bridge
self.class.class_eval { def_delegator :@bridge, name } unless self.class.method_defined?(name)
end

### Methods for Appium
Expand Down
8 changes: 3 additions & 5 deletions sig/lib/appium_lib_core/common/base/bridge.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ module Appium

@browser: untyped

@available_commands: untyped

@session_id: untyped

# generate a dummy capabilities instance which only has the given platformName and automationName
Expand Down Expand Up @@ -51,8 +49,6 @@ module Appium
# No 'browserName' means the session is native appium connection
APPIUM_NATIVE_BROWSER_NAME: "appium"

attr_reader available_commands: untyped

def browser: () -> untyped

# Appium only.
Expand Down Expand Up @@ -115,8 +111,10 @@ module Appium

public

def add_command: (method: untyped, url: untyped, name: untyped) { (?) -> untyped } -> untyped
# Override Selenium's command_list to use Appium's command definitions
def command_list: () -> Hash[Symbol, Array[Symbol | String]]

# Override Selenium's commands to resolve extra_commands from this subclass
def commands: (untyped command) -> untyped

def status: () -> untyped
Expand Down
12 changes: 4 additions & 8 deletions test/unit/android/webdriver/w3c/commands_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,8 @@ def test_add_command_block
method: :post,
url: 'session/:session_id/path/to/custom/url',
name: :test_command
) do
def test_command(argument)
execute(:test_command, {}, { dummy: argument })
end
) do |argument|
execute(:test_command, {}, { dummy: argument })
end

assert_equal @driver.respond_to?(:test_command), true
Expand All @@ -72,10 +70,8 @@ def test_add_command_block_element_id
method: :post,
url: 'session/:session_id/path/to/custom/:element_id/url',
name: :test_command
) do
def test_command(argument)
execute(:test_command, { element_id: 'dummy_element_id' }, { dummy: argument })
end
) do |argument|
execute(:test_command, { element_id: 'dummy_element_id' }, { dummy: argument })
end

assert_equal @driver.respond_to?(:test_command), true
Expand Down
Loading