File: //lib/ruby/vendor_ruby/selenium/webdriver/common/w3c_action_builder.rb
# 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
class W3CActionBuilder
include KeyActions # Actions specific to key inputs
include PointerActions # Actions specific to pointer inputs
attr_reader :devices
#
# Initialize a W3C Action Builder. Differs from previous by requiring a bridge and allowing asynchronous actions.
# The W3C implementation allows asynchronous actions per device. e.g. A key can be pressed at the same time that
# the mouse is moving. Keep in mind that pauses must be added for other devices in order to line up the actions
# correctly when using asynchronous.
#
# @param [Selenium::WebDriver::Remote::W3CBridge] bridge the bridge for the current driver instance
# @param [Selenium::WebDriver::Interactions::PointerInput] mouse PointerInput for the mouse.
# @param [Selenium::WebDriver::Interactions::KeyInput] keyboard KeyInput for the keyboard.
# @param [Boolean] async Whether to perform the actions asynchronously per device. Defaults to false for
# backwards compatibility.
# @return [W3CActionBuilder] A self reference.
#
def initialize(bridge, mouse, keyboard, async = false)
# For backwards compatibility, automatically include mouse & keyboard
@bridge = bridge
@devices = [mouse, keyboard]
@async = async
end
#
# Adds a PointerInput device of the given kind
#
# @example Add a touch pointer input device
#
# builder = device.action
# builder.add_pointer_input('touch', :touch)
#
# @param [String] name name for the device
# @param [Symbol] kind kind of pointer device to create
# @return [Interactions::PointerInput] The pointer input added
#
#
def add_pointer_input(kind, name)
new_input = Interactions.pointer(kind, name: name)
add_input(new_input)
new_input
end
#
# Adds a KeyInput device
#
# @example Add a key input device
#
# builder = device.action
# builder.add_key_input('keyboard2')
#
# @param [String] name name for the device
# @return [Interactions::KeyInput] The key input added
#
def add_key_input(name)
new_input = Interactions.key(name)
add_input(new_input)
new_input
end
#
# Retrieves the input device for the given name
#
# @param [String] name name of the input device
# @return [Selenium::WebDriver::Interactions::InputDevice] input device with given name
#
def get_device(name)
@devices.find { |device| device.name == name.to_s }
end
#
# Retrieves the current PointerInput devices
#
# @return [Array] array of current PointerInput devices
#
def pointer_inputs
@devices.select { |device| device.type == Interactions::POINTER }
end
#
# Retrieves the current KeyInput device
#
# @return [Selenium::WebDriver::Interactions::InputDevice] current KeyInput device
#
def key_inputs
@devices.select { |device| device.type == Interactions::KEY }
end
#
# Creates a pause for the given device of the given duration. If no duration is given, the pause will only wait
# for all actions to complete in that tick.
#
# @example Send keys to an element
#
# action_builder = driver.action
# keyboard = action_builder.key_input
# el = driver.find_element(id: "some_id")
# driver.action.click(el).pause(keyboard).pause(keyboard).pause(keyboard).send_keys('keys').perform
#
# @param [InputDevice] device Input device to pause
# @param [Float] duration Duration to pause
# @return [W3CActionBuilder] A self reference.
#
def pause(device, duration = nil)
device.create_pause(duration)
self
end
#
# Creates multiple pauses for the given device of the given duration.
#
# @example Send keys to an element
#
# action_builder = driver.action
# keyboard = action_builder.key_input
# el = driver.find_element(id: "some_id")
# driver.action.click(el).pauses(keyboard, 3).send_keys('keys').perform
#
# @param [InputDevice] device Input device to pause
# @param [Integer] number of pauses to add for the device
# @param [Float] duration Duration to pause
# @return [W3CActionBuilder] A self reference.
#
def pauses(device, number, duration = nil)
number.times { device.create_pause(duration) }
self
end
#
# Executes the actions added to the builder.
#
def perform
@bridge.send_actions @devices.map(&:encode).compact
clear_all_actions
nil
end
#
# Clears all actions from the builder.
#
def clear_all_actions
@devices.each(&:clear_actions)
end
#
# Releases all action states from the browser.
#
def release_actions
@bridge.release_actions
end
private
#
# Adds pauses for all devices but the given devices
#
# @param [Array[InputDevice]] action_devices Array of Input Devices performing an action in this tick.
#
def tick(*action_devices)
return if @async
@devices.each { |device| device.create_pause unless action_devices.include? device }
end
#
# Adds an InputDevice
#
def add_input(device)
unless @async
max_device = @devices.max { |a, b| a.actions.length <=> b.actions.length }
pauses(device, max_device.actions.length)
end
@devices << device
end
end # W3CActionBuilder
end # WebDriver
end # Selenium