Path: blob/trunk/rb/lib/selenium/webdriver/common/action_builder.rb
1865 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 ActionBuilder22include KeyActions # Actions specific to key inputs23include PointerActions # Actions specific to pointer inputs24include WheelActions # Actions specific to wheel inputs2526attr_reader :devices2728#29# Initialize a W3C Action Builder. Differs from previous by requiring a bridge and allowing asynchronous actions.30# The W3C implementation allows asynchronous actions per device. e.g. A key can be pressed at the same time that31# the mouse is moving. Keep in mind that pauses must be added for other devices in order to line up the actions32# correctly when using asynchronous.33#34# @param [Selenium::WebDriver::Remote::Bridge] bridge the bridge for the current driver instance.35# @param [Array<Selenium::WebDriver::Interactions::InputDevices>] devices list of valid sources of input.36# @param [Boolean] async Whether to perform the actions asynchronously per device.37# @return [ActionBuilder] A self reference.38#3940def initialize(bridge, devices: [], async: false, duration: 250)41@bridge = bridge42@duration = duration43@async = async44@devices = []4546Array(devices).each { |device| add_input(device) }47end4849#50# Adds a PointerInput device of the given kind51#52# @example Add a touch pointer input device53#54# builder = device.action55# builder.add_pointer_input('touch', :touch)56#57# @param [String] name name for the device58# @param [Symbol] kind kind of pointer device to create59# @return [Interactions::PointerInput] The pointer input added60#61#6263def add_pointer_input(kind, name)64add_input(Interactions.pointer(kind, name: name))65end6667#68# Adds a KeyInput device69#70# @example Add a key input device71#72# builder = device.action73# builder.add_key_input('keyboard2')74#75# @param [String] name name for the device76# @return [Interactions::KeyInput] The key input added77#7879def add_key_input(name)80add_input(Interactions.key(name))81end8283#84# Adds a WheelInput device85#86# @example Add a wheel input device87#88# builder = device.action89# builder.add_wheel_input('wheel2')90#91# @param [String] name name for the device92# @return [Interactions::WheelInput] The wheel input added93#9495def add_wheel_input(name)96add_input(Interactions.wheel(name))97end9899#100# Retrieves the input device for the given name or type101#102# @param [String] name name of the input device103# @param [String] type name of the input device104# @return [Selenium::WebDriver::Interactions::InputDevice] input device with given name or type105#106107def device(name: nil, type: nil)108input = @devices.find { |device| (device.name == name.to_s || name.nil?) && (device.type == type || type.nil?) }109110raise(ArgumentError, "Can not find device: #{name}") if name && input.nil?111112input113end114115#116# Retrieves the current PointerInput devices117#118# @return [Array] array of current PointerInput devices119#120121def pointer_inputs122@devices.select { |device| device.type == Interactions::POINTER }123end124125#126# Retrieves the current KeyInput device127#128# @return [Selenium::WebDriver::Interactions::InputDevice] current KeyInput device129#130131def key_inputs132@devices.select { |device| device.type == Interactions::KEY }133end134135#136# Retrieves the current WheelInput device137#138# @return [Selenium::WebDriver::Interactions::InputDevice] current WheelInput devices139#140141def wheel_inputs142@devices.select { |device| device.type == Interactions::WHEEL }143end144145#146# Creates a pause for the given device of the given duration. If no duration is given, the pause will only wait147# for all actions to complete in that tick.148#149# @example Send keys to an element150#151# action_builder = driver.action152# keyboard = action_builder.key_input153# el = driver.find_element(id: "some_id")154# driver.action.click(el).pause(keyboard).pause(keyboard).pause(keyboard).send_keys('keys').perform155#156# @param [InputDevice] device Input device to pause157# @param [Float] duration Duration to pause158# @return [ActionBuilder] A self reference.159#160161def pause(device: nil, duration: 0)162device ||= pointer_input163device.create_pause(duration)164self165end166167#168# Creates multiple pauses for the given device of the given duration.169#170# @example Send keys to an element171#172# action_builder = driver.action173# keyboard = action_builder.key_input174# el = driver.find_element(id: "some_id")175# driver.action.click(el).pauses(keyboard, 3).send_keys('keys').perform176#177# @param [InputDevice] device Input device to pause178# @param [Integer] number of pauses to add for the device179# @param [Float] duration Duration to pause180# @return [ActionBuilder] A self reference.181#182183def pauses(device: nil, number: nil, duration: 0)184number ||= 2185device ||= pointer_input186duration ||= 0187188number.times { device.create_pause(duration) }189self190end191192#193# Executes the actions added to the builder.194#195196def perform197@bridge.send_actions @devices.filter_map(&:encode)198clear_all_actions199nil200end201202#203# Clears all actions from the builder.204#205206def clear_all_actions207@devices.each(&:clear_actions)208end209210#211# Releases all action states from the browser.212#213214def release_actions215@bridge.release_actions216end217218private219220#221# Adds pauses for all devices but the given devices222#223# @param [Array[InputDevice]] action_devices Array of Input Devices performing an action in this tick.224#225226def tick(*action_devices)227return if @async228229@devices.each { |device| device.create_pause unless action_devices.include? device }230end231232#233# Adds an InputDevice234#235236def add_input(device)237device = Interactions.send(device) if device.is_a?(Symbol) && Interactions.respond_to?(device)238239raise TypeError, "#{device.inspect} is not a valid InputDevice" unless device.is_a?(Interactions::InputDevice)240241unless @async242max_device = @devices.max { |a, b| a.actions.length <=> b.actions.length }243pauses(device: device, number: max_device.actions.length) if max_device244end245@devices << device246device247end248end # ActionBuilder249end # WebDriver250end # Selenium251252253