Path: blob/trunk/rb/lib/selenium/webdriver/common/options.rb
4010 views
# frozen_string_literal: true12# Licensed to the Software Freedom Conservancy (SFC) under one3# or more contributor license agreements. See the NOTICE file4# distributed with this work for additional information5# regarding copyright ownership. The SFC licenses this file6# to you under the Apache License, Version 2.0 (the7# "License"); you may not use this file except in compliance8# with the License. You may obtain a copy of the License at9#10# http://www.apache.org/licenses/LICENSE-2.011#12# Unless required by applicable law or agreed to in writing,13# software distributed under the License is distributed on an14# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY15# KIND, either express or implied. See the License for the16# specific language governing permissions and limitations17# under the License.1819module Selenium20module WebDriver21class Options22W3C_OPTIONS = %i[browser_name browser_version platform_name accept_insecure_certs page_load_strategy proxy23set_window_rect timeouts unhandled_prompt_behavior strict_file_interactability24web_socket_url].freeze2526GRID_OPTIONS = %i[enable_downloads].freeze2728class << self29attr_reader :driver_path3031def chrome(**)32Chrome::Options.new(**)33end3435def firefox(**)36Firefox::Options.new(**)37end3839def ie(**)40IE::Options.new(**)41end42alias internet_explorer ie4344def edge(**)45Edge::Options.new(**)46end47alias microsoftedge edge4849def safari(**)50Safari::Options.new(**)51end5253def set_capabilities54(W3C_OPTIONS + GRID_OPTIONS + self::CAPABILITIES.keys).each do |key|55next if method_defined? key5657define_method key do58@options[key]59end6061define_method :"#{key}=" do |value|62@options[key] = value63end64end65end66end6768attr_accessor :options6970def initialize(**opts)71self.class.set_capabilities7273opts[:web_socket_url] = opts.delete(:bidi) if opts.key?(:bidi)7475@options = opts76@options[:browser_name] = self.class::BROWSER77end7879#80# Add a new option not yet handled by bindings.81#82# @example Leave Chrome open when chromedriver is killed83# options = Selenium::WebDriver::Chrome::Options.new84# options.add_option(:detach, true)85#86# @param [String, Symbol] name Name of the option87# @param [Boolean, String, Integer] value Value of the option88#8990def add_option(name, value = nil)91name, value = name.first if value.nil? && name.is_a?(Hash)92@options[name] = value93end9495def enable_bidi!96@options[:web_socket_url] = true97end9899def bidi?100!!@options[:web_socket_url]101end102103def ==(other)104return false unless other.is_a? self.class105106as_json == other.as_json107end108109alias eql? ==110111#112# @api private113#114115def as_json(*)116options = @options.dup117118downloads = options.delete(:enable_downloads)119options['se:downloadsEnabled'] = downloads unless downloads.nil?120w3c_options = process_w3c_options(options)121122browser_options = self.class::CAPABILITIES.each_with_object({}) do |(capability_alias, capability_name), hash|123capability_value = options.delete(capability_alias)124hash[capability_name] = capability_value unless capability_value.nil?125end126127raise Error::WebDriverError, "These options are not w3c compliant: #{options}" unless options.empty?128129browser_options = {self.class::KEY => browser_options} if defined?(self.class::KEY)130131process_browser_options(browser_options)132generate_as_json(w3c_options.merge(browser_options))133end134135private136137def w3c?(key)138W3C_OPTIONS.include?(key) || key.to_s.include?(':')139end140141def process_w3c_options(options)142w3c_options = options.select { |key, val| w3c?(key) && !val.nil? }143w3c_options[:unhandled_prompt_behavior] &&=144process_unhandled_prompt_behavior_value(w3c_options[:unhandled_prompt_behavior])145options.delete_if { |key, _val| w3c?(key) }146w3c_options147end148149def process_unhandled_prompt_behavior_value(value)150if value.is_a?(Hash)151value.transform_values { |v| process_unhandled_prompt_behavior_value(v) }152else153value&.to_s&.tr('_', ' ')154end155end156157def process_browser_options(_browser_options)158nil159end160161def camelize?(_key)162true163end164165def generate_as_json(value, camelize_keys: true)166if value.is_a?(Hash)167process_json_hash(value, camelize_keys)168elsif value.respond_to?(:as_json)169value.as_json170elsif value.is_a?(Array)171value.map { |val| generate_as_json(val, camelize_keys: camelize_keys) }172elsif value.is_a?(Symbol)173value.to_s174else175value176end177end178179def process_json_hash(value, camelize_keys)180value.each_with_object({}) do |(key, val), hash|181next if val.respond_to?(:empty?) && val.empty?182183camelize = camelize_keys ? camelize?(key) : false184key = convert_json_key(key, camelize: camelize)185hash[key] = generate_as_json(val, camelize_keys: camelize)186end187end188189def convert_json_key(key, camelize: true)190key = key.to_s if key.is_a?(Symbol)191key = camel_case(key) if camelize192return key if key.is_a?(String)193194raise TypeError, "expected String or Symbol, got #{key.inspect}:#{key.class}"195end196197def camel_case(str)198str.gsub(/_([a-z])/) { Regexp.last_match(1)&.upcase }199end200end # Options201end # WebDriver202end # Selenium203204205