# 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 # # Specification of the desired and/or actual capabilities of the browser that the # server is being asked to create. # class Capabilities DEFAULTS = { browser_name: '', version: '', platform: :any, javascript_enabled: false, css_selectors_enabled: false, takes_screenshot: false, native_events: false, rotatable: false, firefox_profile: nil, proxy: nil }.freeze DEFAULTS.each_key do |key| define_method key do @capabilities.fetch(key) end define_method "#{key}=" do |value| @capabilities[key] = value end end # # Returns javascript_enabled capability. # It is true if not set explicitly. # def javascript_enabled javascript_enabled = @capabilities.fetch(:javascript_enabled) javascript_enabled.nil? ? true : javascript_enabled end alias_method :css_selectors_enabled?, :css_selectors_enabled alias_method :javascript_enabled?, :javascript_enabled alias_method :native_events?, :native_events alias_method :takes_screenshot?, :takes_screenshot alias_method :rotatable?, :rotatable # # Convenience methods for the common choices. # class << self def chrome(opts = {}) new({ browser_name: 'chrome', javascript_enabled: true, css_selectors_enabled: true }.merge(opts)) end def edge(opts = {}) new({ browser_name: 'MicrosoftEdge', platform: :windows, javascript_enabled: true, takes_screenshot: true, css_selectors_enabled: true }.merge(opts)) end def firefox(opts = {}) new({ browser_name: 'firefox', javascript_enabled: true, takes_screenshot: true, css_selectors_enabled: true }.merge(opts)) end def htmlunit(opts = {}) new({ browser_name: 'htmlunit' }.merge(opts)) end def htmlunitwithjs(opts = {}) new({ browser_name: 'htmlunit', javascript_enabled: true }.merge(opts)) end def internet_explorer(opts = {}) new({ browser_name: 'internet explorer', platform: :windows, takes_screenshot: true, css_selectors_enabled: true, native_events: true }.merge(opts)) end alias_method :ie, :internet_explorer def phantomjs(opts = {}) WebDriver.logger.deprecate 'Selenium support for PhantomJS', 'headless Chrome/Firefox or HTMLUnit' new({ browser_name: 'phantomjs', javascript_enabled: true, takes_screenshot: true, css_selectors_enabled: true }.merge(opts)) end def safari(opts = {}) new({ browser_name: 'safari', platform: :mac, javascript_enabled: true, takes_screenshot: true, css_selectors_enabled: true }.merge(opts)) end # # @api private # def json_create(data) data = data.dup caps = new caps.browser_name = data.delete('browserName') caps.version = data.delete('version') caps.platform = data.delete('platform').downcase.tr(' ', '_').to_sym if data.key?('platform') caps.javascript_enabled = data.delete('javascriptEnabled') caps.css_selectors_enabled = data.delete('cssSelectorsEnabled') caps.takes_screenshot = data.delete('takesScreenshot') caps.native_events = data.delete('nativeEvents') caps.rotatable = data.delete('rotatable') caps.proxy = Proxy.json_create(data['proxy']) if data.key?('proxy') && !data['proxy'].empty? # any remaining pairs will be added as is, with no conversion caps.merge!(data) caps end end # # @option :browser_name [String] required browser name # @option :version [String] required browser version number # @option :platform [Symbol] one of :any, :win, :mac, or :x # @option :javascript_enabled [Boolean] does the driver have javascript enabled? # @option :css_selectors_enabled [Boolean] does the driver support CSS selectors? # @option :takes_screenshot [Boolean] can this driver take screenshots? # @option :native_events [Boolean] does this driver use native events? # @option :proxy [Selenium::WebDriver::Proxy, Hash] proxy configuration # # Firefox-specific options: # # @option :firefox_profile [Selenium::WebDriver::Firefox::Profile] the firefox profile to use # # @api public # def initialize(opts = {}) @capabilities = DEFAULTS.merge(opts) self.proxy = opts.delete(:proxy) end # # Allows setting arbitrary capabilities. # def []=(key, value) @capabilities[key] = value end def [](key) @capabilities[key] end def merge!(other) if other.respond_to?(:capabilities, true) && other.capabilities.is_a?(Hash) @capabilities.merge! other.capabilities elsif other.is_a? Hash @capabilities.merge! other else raise ArgumentError, 'argument should be a Hash or implement #capabilities' end end def proxy=(proxy) case proxy when Hash @capabilities[:proxy] = Proxy.new(proxy) when Proxy, nil @capabilities[:proxy] = proxy else raise TypeError, "expected Hash or #{Proxy.name}, got #{proxy.inspect}:#{proxy.class}" end end # # @api private # def as_json(*) hash = {} @capabilities.each do |key, value| case key when :platform hash['platform'] = value.to_s.upcase when :firefox_profile hash['firefox_profile'] = value.as_json['zip'] if value when :proxy hash['proxy'] = value.as_json if value when String, :firefox_binary hash[key.to_s] = value when Symbol hash[camel_case(key.to_s)] = value else raise TypeError, "expected String or Symbol, got #{key.inspect}:#{key.class} / #{value.inspect}" end end hash end def to_json(*) JSON.generate as_json end def ==(other) return false unless other.is_a? self.class as_json == other.as_json end alias_method :eql?, :== protected attr_reader :capabilities private def camel_case(str) str.gsub(/_([a-z])/) { Regexp.last_match(1).upcase } end end # Capabilities end # Remote end # WebDriver end # Selenium