Path: blob/trunk/rb/spec/integration/selenium/webdriver/driver_spec.rb
1864 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 Driver, exclusive: {bidi: false, reason: 'Not yet implemented with BiDi'} do24after { reset_driver! if GlobalTestEnv.rbe? && GlobalTestEnv.browser == :chrome }2526it_behaves_like 'driver that can be started concurrently', exclude: [27{browser: %i[safari safari_preview]},28{browser: :firefox, reason: 'https://github.com/SeleniumHQ/selenium/issues/15451'},29{driver: :remote, rbe: true, reason: 'Cannot start 2+ drivers at once.'}30]3132it 'creates default capabilities', exclude: {browser: %i[safari safari_preview]} do33reset_driver! do |driver|34caps = driver.capabilities35expect(caps.proxy).to be_nil36expect(caps.browser_version).to match(/^\d\d\d?\./)37expect(caps.platform_name).not_to be_nil3839expect(caps.accept_insecure_certs).to eq(caps.browser_name == 'firefox')40expect(caps.page_load_strategy).to eq 'normal'41expect(caps.implicit_timeout).to be_zero42expect(caps.page_load_timeout).to eq 30000043expect(caps.script_timeout).to eq 3000044end45end4647it 'gets driver status' do48status = driver.status49expect(status).to include('ready', 'message')50end5152it 'gets the page title' do53driver.navigate.to url_for('xhtmlTest.html')54expect(driver.title).to eq('XHTML Test Page')55end5657it 'gets the page source' do58driver.navigate.to url_for('xhtmlTest.html')59expect(driver.page_source).to match(%r{<title>XHTML Test Page</title>}i)60end6162it 'refreshes the page' do63driver.navigate.to url_for('javascriptPage.html')64short_wait { driver.find_element(id: 'updatediv') }.click65expect(driver.find_element(id: 'dynamo').text).to eq('Fish and chips!')66driver.navigate.refresh67wait_for_element(id: 'dynamo')68expect(driver.find_element(id: 'dynamo').text).to eq("What's for dinner?")69end7071describe 'one element' do72it 'finds by id' do73driver.navigate.to url_for('xhtmlTest.html')74element = driver.find_element(id: 'id1')75expect(element).to be_a(WebDriver::Element)76expect(element.text).to eq('Foo')77end7879it 'finds by field name' do80driver.navigate.to url_for('formPage.html')81expect(driver.find_element(name: 'x').attribute('value')).to eq('name')82end8384it 'finds by class name' do # rubocop:disable RSpec/RepeatedExample85driver.navigate.to url_for('xhtmlTest.html')86expect(driver.find_element(class: 'header').text).to eq('XHTML Might Be The Future')87end8889# TODO: Rewrite this test so it's not a duplicate of above or remove90it 'finds elements with a hash selector' do # rubocop:disable RSpec/RepeatedExample91driver.navigate.to url_for('xhtmlTest.html')92expect(driver.find_element(class: 'header').text).to eq('XHTML Might Be The Future')93end9495it 'finds by link text' do96driver.navigate.to url_for('xhtmlTest.html')97expect(driver.find_element(link: 'Foo').text).to eq('Foo')98end99100it 'finds by xpath' do101driver.navigate.to url_for('xhtmlTest.html')102expect(driver.find_element(xpath: '//h1').text).to eq('XHTML Might Be The Future')103end104105it 'finds by css selector' do106driver.navigate.to url_for('xhtmlTest.html')107expect(driver.find_element(css: 'div.content').attribute('class')).to eq('content')108end109110it 'finds by tag name' do111driver.navigate.to url_for('xhtmlTest.html')112expect(driver.find_element(tag_name: 'div').attribute('class')).to eq('navigation')113end114115it 'finds above another' do116driver.navigate.to url_for('relative_locators.html')117118above = driver.find_element(relative: {tag_name: 'td', above: {id: 'center'}})119expect(above.attribute('id')).to eq('top')120end121122it 'finds child element' do123driver.navigate.to url_for('nestedElements.html')124125element = driver.find_element(name: 'form2')126child = element.find_element(name: 'selectomatic')127128expect(child.attribute('id')).to eq('2')129end130131it 'finds child element by tag name' do132driver.navigate.to url_for('nestedElements.html')133134element = driver.find_element(name: 'form2')135child = element.find_element(tag_name: 'select')136137expect(child.attribute('id')).to eq('2')138end139140it 'finds elements with the shortcut syntax' do141driver.navigate.to url_for('xhtmlTest.html')142143expect(driver[:id1]).to be_a(WebDriver::Element)144expect(driver[xpath: '//h1']).to be_a(WebDriver::Element)145end146147it 'raises if element not found' do148driver.navigate.to url_for('xhtmlTest.html')149expect {150driver.find_element(id: 'not-there')151}.to raise_error(Error::NoSuchElementError, /errors#no-such-element-exception/)152end153154it 'raises if invalid locator',155exclude: {browser: %i[safari safari_preview], reason: 'Safari TimeoutError'} do156driver.navigate.to url_for('xhtmlTest.html')157expect {158driver.find_element(xpath: '*?//-')159}.to raise_error(Error::InvalidSelectorError, /errors#invalid-selector-exception/)160end161end162163describe 'many elements' do164it 'finds by class name' do165driver.navigate.to url_for('xhtmlTest.html')166expect(driver.find_elements(class: 'nameC').size).to eq(2)167end168169it 'finds by css selector' do170driver.navigate.to url_for('xhtmlTest.html')171expect(driver.find_elements(css: 'p').size).to be_positive172end173174it 'finds above element' do175driver.navigate.to url_for('relative_locators.html')176177lowest = driver.find_element(id: 'below')178above = driver.find_elements(relative: {tag_name: 'p', above: lowest})179expect(above.map { |e| e.attribute('id') }).to eq(%w[mid above])180end181182it 'finds above another' do183driver.navigate.to url_for('relative_locators.html')184185above = driver.find_elements(relative: {css: 'td', above: {id: 'center'}})186expect(above.map { |e| e.attribute('id') }).to eq(%w[top topLeft topRight])187end188189it 'finds below element' do190driver.navigate.to url_for('relative_locators.html')191192midpoint = driver.find_element(id: 'mid')193above = driver.find_elements(relative: {id: 'below', below: midpoint})194expect(above.map { |e| e.attribute('id') }).to eq(['below'])195end196197it 'finds near another within default distance' do198driver.navigate.to url_for('relative_locators.html')199200near = driver.find_elements(relative: {tag_name: 'td', near: {id: 'right'}})201expect(near.map { |e| e.attribute('id') }).to eq(%w[topRight bottomRight center top bottom])202end203204it 'finds near another within custom distance', except: {browser: %i[safari safari_preview]} do205driver.navigate.to url_for('relative_locators.html')206207near = driver.find_elements(relative: {tag_name: 'td', near: {id: 'right', distance: 100}})208expect(near.map { |e| e.attribute('id') }).to eq(%w[topRight bottomRight center top bottom])209end210211it 'finds to the left of another' do212driver.navigate.to url_for('relative_locators.html')213214left = driver.find_elements(relative: {tag_name: 'td', left: {id: 'center'}})215expect(left.map { |e| e.attribute('id') }).to eq(%w[left topLeft bottomLeft])216end217218it 'finds to the right of another' do219driver.navigate.to url_for('relative_locators.html')220221right = driver.find_elements(relative: {tag_name: 'td', right: {id: 'center'}})222expect(right.map { |e| e.attribute('id') }).to eq(%w[right topRight bottomRight])223end224225it 'finds by combined relative locators' do226driver.navigate.to url_for('relative_locators.html')227228found = driver.find_elements(relative: {tag_name: 'td', right: {id: 'top'}, above: {id: 'center'}})229expect(found.map { |e| e.attribute('id') }).to eq(['topRight'])230end231232it 'finds all by empty relative locator' do233driver.navigate.to url_for('relative_locators.html')234235expected = driver.find_elements(tag_name: 'p')236actual = driver.find_elements(relative: {tag_name: 'p'})237expect(actual).to eq(expected)238end239240it 'finds children by field name' do241driver.navigate.to url_for('nestedElements.html')242element = driver.find_element(name: 'form2')243children = element.find_elements(name: 'selectomatic')244expect(children.size).to eq(2)245end246end247248describe '#execute_script' do249it 'returns strings' do250driver.navigate.to url_for('xhtmlTest.html')251expect(driver.execute_script('return document.title;')).to eq('XHTML Test Page')252end253254it 'returns numbers' do255driver.navigate.to url_for('xhtmlTest.html')256expect(driver.execute_script('return document.title.length;')).to eq(15)257end258259it 'returns elements' do260driver.navigate.to url_for('xhtmlTest.html')261element = driver.execute_script("return document.getElementById('id1');")262expect(element).to be_a(WebDriver::Element)263expect(element.text).to eq('Foo')264end265266it 'unwraps elements in deep objects' do267driver.navigate.to url_for('xhtmlTest.html')268result = driver.execute_script(<<~SCRIPT)269var e1 = document.getElementById('id1');270var body = document.body;271272return {273elements: {'body' : body, other: [e1] }274};275SCRIPT276277expect(result).to be_a(Hash)278expect(result['elements']['body']).to be_a(WebDriver::Element)279expect(result['elements']['other'].first).to be_a(WebDriver::Element)280end281282it 'returns booleans' do283driver.navigate.to url_for('xhtmlTest.html')284expect(driver.execute_script('return true;')).to be(true)285end286287it 'raises if the script is bad' do288driver.navigate.to url_for('xhtmlTest.html')289expect {290driver.execute_script('return squiggle();')291}.to raise_error(Selenium::WebDriver::Error::JavascriptError)292end293294it 'returns arrays' do295driver.navigate.to url_for('xhtmlTest.html')296expect(driver.execute_script('return ["zero", "one", "two"];')).to eq(%w[zero one two])297end298299it 'is able to call functions on the page' do300driver.navigate.to url_for('javascriptPage.html')301driver.execute_script("displayMessage('I like cheese');")302expect(driver.find_element(id: 'result').text.strip).to eq('I like cheese')303end304305it 'is able to pass string arguments' do306driver.navigate.to url_for('javascriptPage.html')307expect(driver.execute_script("return arguments[0] == 'fish' ? 'fish' : 'not fish';", 'fish')).to eq('fish')308end309310it 'is able to pass boolean arguments' do311driver.navigate.to url_for('javascriptPage.html')312expect(driver.execute_script('return arguments[0] == true;', true)).to be(true)313end314315it 'is able to pass numeric arguments' do316driver.navigate.to url_for('javascriptPage.html')317expect(driver.execute_script('return arguments[0] == 1 ? 1 : 0;', 1)).to eq(1)318end319320it 'is able to pass null arguments' do321driver.navigate.to url_for('javascriptPage.html')322expect(driver.execute_script('return arguments[0];', nil)).to be_nil323end324325it 'is able to pass array arguments' do326driver.navigate.to url_for('javascriptPage.html')327expect(driver.execute_script('return arguments[0];', [1, '2', 3])).to eq([1, '2', 3])328end329330it 'is able to pass element arguments' do331driver.navigate.to url_for('javascriptPage.html')332button = driver.find_element(id: 'plainButton')333js = "arguments[0]['flibble'] = arguments[0].getAttribute('id'); return arguments[0]['flibble'];"334expect(driver.execute_script(js, button))335.to eq('plainButton')336end337338it 'is able to pass in multiple arguments' do339driver.navigate.to url_for('javascriptPage.html')340expect(driver.execute_script('return arguments[0] + arguments[1];', 'one', 'two')).to eq('onetwo')341end342end343344describe 'execute async script' do345before do346driver.manage.timeouts.script = 1347driver.navigate.to url_for('ajaxy_page.html')348end349350it 'is able to return arrays of primitives from async scripts' do351result = driver.execute_async_script "arguments[arguments.length - 1]([null, 123, 'abc', true, false]);"352expect(result).to eq([nil, 123, 'abc', true, false])353end354355it 'is able to pass multiple arguments to async scripts' do356result = driver.execute_async_script 'arguments[arguments.length - 1](arguments[0] + arguments[1]);', 1, 2357expect(result).to eq(3)358end359360# Safari raises TimeoutError instead361it 'times out if the callback is not invoked', except: {browser: %i[safari safari_preview]} do362expect {363# Script is expected to be async and explicitly callback, so this should timeout.364driver.execute_async_script 'return 1 + 2;'365}.to raise_error(Selenium::WebDriver::Error::ScriptTimeoutError)366end367end368end369end # WebDriver370end # Selenium371372373