1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
|
# frozen_string_literal: true
# Licensed to the Software Freedom Conservancy (SFC) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The SFC licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
module Selenium
module WebDriver
module Remote
class Bridge
include Atoms
include BridgeHelper
PORT = 4444
COMMANDS = {
new_session: [:post, 'session']
}.freeze
attr_accessor :context, :http, :file_detector
attr_reader :capabilities, :dialect
#
# Implements protocol handshake which:
#
# 1. Creates session with driver.
# 2. Sniffs response.
# 3. Based on the response, understands which dialect we should use.
#
# @return [OSS:Bridge, W3C::Bridge]
#
def self.handshake(**opts)
desired_capabilities = opts.delete(:desired_capabilities) { Capabilities.new }
if desired_capabilities.is_a?(Symbol)
unless Capabilities.respond_to?(desired_capabilities)
raise Error::WebDriverError, "invalid desired capability: #{desired_capabilities.inspect}"
end
desired_capabilities = Capabilities.__send__(desired_capabilities)
end
bridge = new(opts)
capabilities = bridge.create_session(desired_capabilities, opts.delete(:options))
case bridge.dialect
when :oss
Remote::OSS::Bridge.new(capabilities, bridge.session_id, **opts)
when :w3c
Remote::W3C::Bridge.new(capabilities, bridge.session_id, **opts)
else
raise WebDriverError, 'cannot understand dialect'
end
end
#
# Initializes the bridge with the given server URL
# @param [Hash] opts options for the driver
# @option opts [String] :url url for the remote server
# @option opts [Object] :http_client an HTTP client instance that implements the same protocol as Http::Default
# @option opts [Capabilities] :desired_capabilities an instance of Remote::Capabilities describing the capabilities you want
# @api private
#
def initialize(opts = {})
opts = opts.dup
http_client = opts.delete(:http_client) { Http::Default.new }
url = opts.delete(:url) { "http://#{Platform.localhost}:#{PORT}/wd/hub" }
opts.delete(:options)
unless opts.empty?
raise ArgumentError, "unknown option#{'s' if opts.size != 1}: #{opts.inspect}"
end
uri = url.is_a?(URI) ? url : URI.parse(url)
uri.path += '/' unless uri.path =~ %r{\/$}
http_client.server_url = uri
@http = http_client
@file_detector = nil
end
#
# Creates session handling both OSS and W3C dialects.
#
def create_session(desired_capabilities, options = nil)
response = execute(:new_session, {}, merged_capabilities(desired_capabilities, options))
@session_id = response['sessionId']
oss_status = response['status']
value = response['value']
if value.is_a?(Hash)
@session_id = value['sessionId'] if value.key?('sessionId')
if value.key?('capabilities')
value = value['capabilities']
elsif value.key?('value')
value = value['value']
end
end
raise Error::WebDriverError, 'no sessionId in returned payload' unless @session_id
if oss_status
WebDriver.logger.info 'Detected OSS dialect.'
@dialect = :oss
Capabilities.json_create(value)
else
WebDriver.logger.info 'Detected W3C dialect.'
@dialect = :w3c
W3C::Capabilities.json_create(value)
end
end
#
# Returns the current session ID.
#
def session_id
@session_id || raise(Error::WebDriverError, 'no current session exists')
end
def browser
@browser ||= begin
name = @capabilities.browser_name
name ? name.tr(' ', '_').to_sym : 'unknown'
end
end
private
#
# executes a command on the remote server.
#
# @return [WebDriver::Remote::Response]
#
def execute(command, opts = {}, command_hash = nil)
verb, path = commands(command) || raise(ArgumentError, "unknown command: #{command.inspect}")
path = path.dup
path[':session_id'] = session_id if path.include?(':session_id')
begin
opts.each { |key, value| path[key.inspect] = escaper.escape(value.to_s) }
rescue IndexError
raise ArgumentError, "#{opts.inspect} invalid for #{command.inspect}"
end
WebDriver.logger.info("-> #{verb.to_s.upcase} #{path}")
http.call(verb, path, command_hash)
end
def escaper
@escaper ||= defined?(URI::Parser) ? URI::DEFAULT_PARSER : URI
end
def commands(command)
raise NotImplementedError unless command == :new_session
COMMANDS[command]
end
def merged_capabilities(oss_capabilities, options = nil)
w3c_capabilities = W3C::Capabilities.from_oss(oss_capabilities)
w3c_capabilities.merge!(options.as_json) if options
{
desiredCapabilities: oss_capabilities,
capabilities: {
firstMatch: [w3c_capabilities]
}
}
end
end # Bridge
end # Remote
end # WebDriver
end # Selenium
|