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
|
# 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
# @api private
class Response
STACKTRACE_KEY = 'stackTrace'
attr_reader :code, :payload
attr_writer :payload
def initialize(code, payload = nil)
@code = code
@payload = payload || {}
assert_ok
end
def error
klass = Error.for_code(status) || return
ex = klass.new(error_message)
ex.set_backtrace(caller)
add_backtrace ex
ex
end
def error_message
val = value
case val
when Hash
msg = val['message']
return 'unknown error' unless msg
msg << ": #{val['alert']['text'].inspect}" if val['alert'].is_a?(Hash) && val['alert']['text']
msg << " (#{val['class']})" if val['class']
msg
when String
val
else
"unknown error, status=#{status}: #{val.inspect}"
end
end
def [](key)
@payload[key]
end
private
def assert_ok
e = error
raise e if e
return unless @code.nil? || @code >= 400
raise Error::ServerError, self
end
def add_backtrace(ex)
return unless error_payload.is_a?(Hash)
# Legacy Firefox returns String in ['value'], while we expect Hash.
# Use #dig when Firefox legacy is removed (4.0).
server_trace = error_payload[STACKTRACE_KEY] ||
error_payload[STACKTRACE_KEY.downcase] ||
(error_payload['value'] && error_payload['value'][STACKTRACE_KEY])
return unless server_trace
backtrace = case server_trace
when Array
backtrace_from_remote(server_trace)
when String
server_trace.split("\n")
end
ex.set_backtrace(backtrace + ex.backtrace)
end
def backtrace_from_remote(server_trace)
server_trace.map { |frame|
next unless frame.is_a?(Hash)
file = frame['fileName']
line = frame['lineNumber']
meth = frame['methodName']
class_name = frame['className']
file = "#{class_name}(#{file})" if class_name
meth = 'unknown' if meth.nil? || meth.empty?
"[remote server] #{file}:#{line}:in `#{meth}'"
}.compact
end
def error_payload
# Even errors are wrapped in 'value' for w3c
# Grab 'value' key for error, leave original payload alone and let the bridge process
@error_payload ||= !@payload.key?('sessionId') ? @payload['value'] : @payload
end
def status
return unless error_payload.is_a? Hash
@status ||= error_payload['status'] || error_payload['error']
end
def value
return unless error_payload.is_a? Hash
@value ||= error_payload['value'] || error_payload['message']
end
end # Response
end # Remote
end # WebDriver
end # Selenium
|