Path: blob/trunk/rb/spec/integration/selenium/webdriver/action_builder_spec.rb
4054 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.1819require_relative 'spec_helper'2021module Selenium22module WebDriver23describe ActionBuilder, exclusive: {bidi: false, reason: 'Not yet implemented with BiDi'} do24after { driver.action.clear_all_actions }2526describe '#send_keys' do27it 'sends keys to the active element', except: {browser: %i[safari safari_preview]} do28driver.navigate.to url_for('bodyTypingTest.html')29keylogger = driver.find_element(id: 'body_result')3031driver.find_element(css: 'body').click32driver.action.send_keys('ab').perform3334wait.until { keylogger.text.split.length == 2 }35expect(keylogger.text.strip).to eq('keypress keypress')3637expect(driver.find_element(id: 'result').text.strip).to be_empty38end3940it 'sends keys to element', only: {browser: %i[chrome edge firefox]} do41driver.navigate.to url_for('formPage.html')4243input = driver.find_element(css: '#working')44driver.execute_script('arguments[0].scrollIntoView({block: "center", inline: "nearest"});', input)4546driver.action.send_keys(input, 'abcd').perform47wait.until { input.property(:value).length == 4 }48expect(input.property(:value)).to eq('abcd')49end5051it 'sends keys with multiple arguments' do52driver.navigate.to url_for('formPage.html')5354input = driver.find_element(css: '#working')55input.click5657driver.action.send_keys('abcd', 'dcba').perform58wait.until { input.property(:value).length == 8 }59expect(input.property(:value)).to eq('abcddcba')60end6162it 'sends non-ASCII keys' do63driver.navigate.to url_for('formPage.html')6465input = driver.find_element(css: '#working')66input.click6768driver.action.send_keys('abcd', :left, 'a').perform69wait.until { input.property(:value).length == 5 }70expect(input.property(:value)).to eq('abcad')71end72end7374describe 'multiple key presses' do75it 'sends keys with shift pressed', except: {browser: %i[safari safari_preview]} do76driver.navigate.to url_for('javascriptPage.html')7778event_input = driver.find_element(id: 'theworks')79keylogger = driver.find_element(id: 'result')8081event_input.click8283driver.action.key_down(:shift).send_keys('ab').key_up(:shift).perform84wait.until { event_input.property(:value).length == 2 }8586expect(event_input.property(:value)).to eq('AB')87expected = keylogger.text.strip88expect(expected).to match(/^(focus )?keydown keydown keypress keyup keydown keypress keyup keyup$/)89end9091it 'press and release modifier keys' do92driver.navigate.to url_for('javascriptPage.html')9394event_input = driver.find_element(id: 'theworks')95keylogger = driver.find_element(id: 'result')9697event_input.click9899driver.action.key_down(:shift).perform100wait.until { keylogger.text.include? 'down' }101expect(keylogger.text).to match(/keydown *$/)102103driver.action.key_up(:shift).perform104wait.until { keylogger.text.include? 'up' }105expect(keylogger.text).to match(/keyup *$/)106end107end108109describe '#release_actions' do110it 'releases pressed keys' do111driver.navigate.to url_for('javascriptPage.html')112113event_input = driver.find_element(id: 'theworks')114keylogger = driver.find_element(id: 'result')115116event_input.click117118driver.action.key_down(:shift).perform119wait.until { keylogger.text.include? 'down' }120expect(keylogger.text).to match(/keydown *$/)121122driver.action.release_actions123wait.until { keylogger.text.include? 'up' }124expect(keylogger.text).to match(/keyup *$/)125end126127it 'releases pressed buttons', except: [{browser: %i[safari safari_preview]},128{driver: :remote, browser: :ie}] do129driver.navigate.to url_for('javascriptPage.html')130131event_input = driver.find_element(id: 'clickField')132133driver.action.click_and_hold(event_input).perform134expect(event_input.property(:value)).to eq('Hello')135136driver.action.release_actions137expect(event_input.property(:value)).to eq('Clicked')138end139end140141describe '#click' do142it 'clicks provided element' do143driver.navigate.to url_for('javascriptPage.html')144element = driver.find_element(id: 'clickField')145driver.action.click(element).perform146expect(element.property(:value)).to eq('Clicked')147end148149it 'executes with equivalent pointer methods' do150driver.navigate.to url_for('javascriptPage.html')151element = driver.find_element(id: 'clickField')152driver.action.move_to(element).pointer_down(:left).pointer_up(:left).perform153expect(element.property(:value)).to eq('Clicked')154end155end156157describe '#double_click' do158# https://issues.chromium.org/issues/400087471159before { reset_driver! if GlobalTestEnv.rbe? && GlobalTestEnv.browser == :chrome }160161it 'presses pointer twice', except: {browser: %i[safari safari_preview]} do162driver.navigate.to url_for('javascriptPage.html')163element = driver.find_element(id: 'doubleClickField')164165driver.action.double_click(element).perform166expect(element.property(:value)).to eq('DoubleClicked')167end168169it 'executes with equivalent pointer methods', except: {browser: %i[safari safari_preview]} do170driver.navigate.to url_for('javascriptPage.html')171element = driver.find_element(id: 'doubleClickField')172173driver.action.move_to(element)174.pointer_down(:left).pointer_up(:left)175.pointer_down(:left).pointer_up(:left)176.perform177expect(element.property(:value)).to eq('DoubleClicked')178end179end180181describe '#context_click' do182it 'right clicks an element' do183driver.navigate.to url_for('javascriptPage.html')184element = driver.find_element(id: 'doubleClickField')185186driver.action.context_click(element).perform187expect(element.property(:value)).to eq('ContextClicked')188end189190it 'executes with equivalent pointer methods' do191driver.navigate.to url_for('javascriptPage.html')192element = driver.find_element(id: 'doubleClickField')193194driver.action.move_to(element).pointer_down(:right).pointer_up(:right).perform195expect(element.property(:value)).to eq('ContextClicked')196end197end198199describe '#move_to' do200it 'moves to element' do201driver.navigate.to url_for('javascriptPage.html')202element = driver.find_element(id: 'clickField')203driver.action.move_to(element).click.perform204205expect(element.property(:value)).to eq('Clicked')206end207208it 'moves to element with offset' do209driver.navigate.to url_for('javascriptPage.html')210origin = driver.find_element(id: 'keyUpArea')211destination = driver.find_element(id: 'clickField')212origin_rect = origin.rect213destination_rect = destination.rect214x_offset = (destination_rect.x - origin_rect.x).ceil215y_offset = (destination_rect.y - origin_rect.y).ceil216217driver.action.move_to(origin, x_offset, y_offset).click.perform218expect(destination.property(:value)).to eq('Clicked')219end220end221222describe '#drag_and_drop' do223it 'moves one element to another' do224driver.navigate.to url_for('droppableItems.html')225226draggable = long_wait.until do227driver.find_element(id: 'draggable')228end229230droppable = driver.find_element(id: 'droppable')231232driver.action.drag_and_drop(draggable, droppable).perform233234text = droppable.find_element(tag_name: 'p').text235expect(text).to eq('Dropped!')236end237end238239describe '#drag_and_drop_by' do240it 'moves one element a provided distance' do241driver.navigate.to url_for('droppableItems.html')242243draggable = long_wait.until do244driver.find_element(id: 'draggable')245end246247driver.action.drag_and_drop_by(draggable, 138, 50).perform248249droppable = driver.find_element(id: 'droppable')250text = droppable.find_element(tag_name: 'p').text251expect(text).to eq('Dropped!')252end253end254255describe '#move_to_location' do256it 'moves pointer to specified coordinates' do257driver.navigate.to url_for('javascriptPage.html')258element = driver.find_element(id: 'clickField')259rect = element.rect260driver.action.move_to_location(rect.x.ceil, rect.y.ceil).click.perform261262expect(element.property(:value)).to eq('Clicked')263end264end265266describe 'pen stylus', except: [{browser: :firefox, reason: 'Unknown pointerType'},267{browser: :safari, reason: 'Some issues with resolution?'}] do268it 'sets pointer event properties' do269driver.navigate.to url_for('pointerActionsPage.html')270pointer_area = driver.find_element(id: 'pointerArea')271rect = pointer_area.rect272x_val = rect.x273y_val = rect.y274275pointer_options = {pressure: 0.8, tilt_x: -40, tilt_y: -10, twist: 177}276actions = driver.action(devices: :pen)277.move_by(x_val + 5, y_val + 5)278.pointer_down279.move_by(2, 2, duration: 0.8, **pointer_options)280.pointer_up281282start = Time.now283actions.perform284expect(Time.now - start).to be > 0.8285286moves = driver.find_elements(class: 'pointermove')287move_to = properties(moves[0])288down = properties(driver.find_element(class: 'pointerdown'))289move_by = properties(moves[1])290up = properties(driver.find_element(class: 'pointerup'))291292expect(move_to).to include('button' => '-1',293'pageX' => (x_val + 5).to_s,294'pageY' => (y_val + 5).floor.to_s)295expect(down).to include('button' => '0')296expect(move_by).to include('button' => '-1',297'pageX' => (x_val + 5 + 2).to_s,298'pageY' => (y_val + 5 + 2).floor.to_s,299'tiltX' => '-40',300'tiltY' => '-10',301'twist' => '177')302expect(up).to include('button' => '0',303'pageX' => (x_val + 5 + 2).to_s,304'pageY' => (y_val + 5 + 2).floor.to_s)305end306end307308describe '#scroll_to' do309it 'scrolls to element',310exclusive: {browser: %i[chrome edge], reason: 'incorrect MoveTargetOutOfBoundsError'} do311driver.navigate.to url_for('scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html')312iframe = driver.find_element(tag_name: 'iframe')313314expect(in_viewport?(iframe)).to be false315316driver.action.scroll_to(iframe).perform317318expect(in_viewport?(iframe)).to be true319end320end321322describe '#scroll_by' do323it 'scrolls by given amount',324exclusive: {browser: %i[chrome edge], reason: 'inconsistent behavior between versions'} do325driver.navigate.to url_for('scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html')326footer = driver.find_element(tag_name: 'footer')327delta_y = footer.rect.y.round328329driver.action.scroll_by(0, delta_y).perform330sleep 0.5331332expect(in_viewport?(footer)).to be true333end334end335336describe '#scroll_from' do337it 'scrolls from element by given amount',338exclusive: {browser: %i[chrome edge], reason: 'incorrect MoveTargetOutOfBoundsError in Firefox'} do339driver.navigate.to url_for('scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html')340iframe = driver.find_element(tag_name: 'iframe')341scroll_origin = WheelActions::ScrollOrigin.element(iframe)342343driver.action.scroll_from(scroll_origin, 0, 200).perform344345driver.switch_to.frame(iframe)346sleep 0.5347checkbox = driver.find_element(name: 'scroll_checkbox')348expect(in_viewport?(checkbox)).to be true349end350351it 'scrolls from element by given amount with offset',352exclusive: {browser: %i[chrome edge], reason: 'incorrect MoveTargetOutOfBoundsError in Firefox'} do353driver.navigate.to url_for('scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html')354footer = driver.find_element(tag_name: 'footer')355scroll_origin = WheelActions::ScrollOrigin.element(footer, 0, -50)356357driver.action.scroll_from(scroll_origin, 0, 200).perform358sleep 0.5359360iframe = driver.find_element(tag_name: 'iframe')361driver.switch_to.frame(iframe)362checkbox = driver.find_element(name: 'scroll_checkbox')363expect(in_viewport?(checkbox)).to be true364end365366it 'raises MoveTargetOutOfBoundsError when origin offset from element is out of viewport' do367driver.navigate.to url_for('scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html')368footer = driver.find_element(tag_name: 'footer')369scroll_origin = WheelActions::ScrollOrigin.element(footer, 0, 50)370371expect {372driver.action.scroll_from(scroll_origin, 0, 200).perform373}.to raise_error(Error::MoveTargetOutOfBoundsError)374end375376it 'scrolls by given amount with offset' do377driver.navigate.to url_for('scrolling_tests/frame_with_nested_scrolling_frame.html')378scroll_origin = WheelActions::ScrollOrigin.viewport(10, 10)379380driver.action.scroll_from(scroll_origin, 0, 200).perform381382iframe = driver.find_element(tag_name: 'iframe')383driver.switch_to.frame(iframe)384checkbox = driver.find_element(name: 'scroll_checkbox')385386expect {387wait.until { in_viewport?(checkbox) }388}.not_to raise_error389end390391it 'raises MoveTargetOutOfBoundsError when origin offset is out of viewport',392only: {browser: %i[chrome edge firefox]} do393driver.navigate.to url_for('scrolling_tests/frame_with_nested_scrolling_frame.html')394scroll_origin = WheelActions::ScrollOrigin.viewport(-10, -10)395396expect {397driver.action.scroll_from(scroll_origin, 0, 200).perform398}.to raise_error(Error::MoveTargetOutOfBoundsError)399end400end401402def properties(element)403element.text.sub(/.*?\s/, '').split(',').to_h { |item| item.lstrip.split(/\s*:\s*/) }404end405406def in_viewport?(element)407in_viewport = <<~IN_VIEWPORT408for(var e=arguments[0],f=e.offsetTop,t=e.offsetLeft,o=e.offsetWidth,n=e.offsetHeight;409e.offsetParent;)f+=(e=e.offsetParent).offsetTop,t+=e.offsetLeft;410return f<window.pageYOffset+window.innerHeight&&t<window.pageXOffset+window.innerWidth&&f+n>411window.pageYOffset&&t+o>window.pageXOffset412IN_VIEWPORT413414driver.execute_script(in_viewport, element)415end416end # ActionBuilder417end # WebDriver418end # Selenium419420421