1
0
Fork 0
mirror of https://github.com/teampoltergeist/poltergeist.git synced 2022-11-09 12:05:00 -05:00
teampoltergeist--poltergeist/spec/integration/driver_spec.rb
Adam Prescott b67c643b74 Don't alter focus with send_keys if we're already in the target element.
If the click event is fired on the target element, then the caret
position that was previously set by, e.g.,

  send_keys(:Left)

is lost. This addresses that by first checking if the target element
already contains the current selection, meaning we have focus within the
target element and don't need to first give it focus.

Fixes #493.
2014-05-16 20:01:22 -04:00

854 lines
26 KiB
Ruby

require 'spec_helper'
require 'image_size'
require 'pdf/reader'
module Capybara::Poltergeist
describe Driver do
before do
@session = TestSessions::Poltergeist
@driver = @session.driver
end
after do
@driver.reset!
end
def session_url(path)
server = @session.server
"http://#{server.host}:#{server.port}#{path}"
end
it 'supports a custom phantomjs path' do
begin
file = POLTERGEIST_ROOT + '/spec/support/custom_phantomjs_called'
path = POLTERGEIST_ROOT + '/spec/support/custom_phantomjs'
FileUtils.rm_f file
driver = Capybara::Poltergeist::Driver.new(nil, :phantomjs => path)
driver.browser
# If the correct custom path is called, it will touch the file.
# We allow at least 10 secs for this to happen before failing.
tries = 0
until File.exist?(file) || tries == 100
sleep 0.1
tries += 1
end
expect(File.exist?(file)).to be_true
ensure
driver.quit if driver
end
end
it 'supports capturing console.log' do
begin
output = StringIO.new
Capybara.register_driver :poltergeist_with_logger do |app|
Capybara::Poltergeist::Driver.new(app, :phantomjs_logger => output)
end
session = Capybara::Session.new(:poltergeist_with_logger, TestApp)
session.visit('/poltergeist/console_log')
expect(output.string).to include('Hello world')
ensure
session.driver.quit
end
end
it 'raises an error and restarts the client if the client dies while executing a command' do
expect { @driver.browser.command('exit') }.to raise_error(DeadClient)
@session.visit('/')
expect(@driver.html).to include('Hello world')
end
it 'quits silently before visit call' do
driver = Capybara::Poltergeist::Driver.new(nil)
expect { driver.quit }.not_to raise_error
end
it 'has a viewport size of 1024x768 by default' do
@session.visit('/')
expect(
@driver.evaluate_script('[window.innerWidth, window.innerHeight]')
).to eq([1024, 768])
end
it 'allows the viewport to be resized' do
@session.visit('/')
@driver.resize(200, 400)
expect(
@driver.evaluate_script('[window.innerWidth, window.innerHeight]')
).to eq([200, 400])
end
it 'allows the page to be scrolled' do
@session.visit('/poltergeist/long_page')
@driver.resize(10, 10)
@driver.scroll_to(200, 100)
expect(
@driver.evaluate_script('[window.scrollX, window.scrollY]')
).to eq([200, 100])
end
it 'supports specifying viewport size with an option' do
begin
Capybara.register_driver :poltergeist_with_custom_window_size do |app|
Capybara::Poltergeist::Driver.new(
app,
:logger => TestSessions.logger,
:window_size => [800, 600]
)
end
driver = Capybara::Session.new(:poltergeist_with_custom_window_size, TestApp).driver
driver.visit(session_url '/')
expect(
driver.evaluate_script('[window.innerWidth, window.innerHeight]')
).to eq([800, 600])
ensure
driver.quit if driver
end
end
shared_examples 'render screen' do
it 'supports rendering the whole of a page that goes outside the viewport' do
@session.visit('/poltergeist/long_page')
create_screenshot file
File.open(file, 'rb') do |f|
expect(ImageSize.new(f.read).size).to eq(
@driver.evaluate_script('[window.innerWidth, window.innerHeight]')
)
end
create_screenshot file, full: true
File.open(file, 'rb') do |f|
expect(ImageSize.new(f.read).size).to eq(
@driver.evaluate_script('[document.documentElement.clientWidth, document.documentElement.clientHeight]')
)
end
end
it 'supports rendering the entire window when documentElement has no height' do
@session.visit('/poltergeist/fixed_positioning')
create_screenshot file, full: true
File.open(file, 'rb') do |f|
expect(ImageSize.new(f.read).size).to eq(
@driver.evaluate_script('[window.innerWidth, window.innerHeight]')
)
end
end
it 'supports rendering just the selected element' do
@session.visit('/poltergeist/long_page')
create_screenshot file, selector: '#penultimate'
File.open(file, 'rb') do |f|
size = @driver.evaluate_script <<-EOS
function() {
var ele = document.getElementById('penultimate');
var rect = ele.getBoundingClientRect();
return [rect.width, rect.height];
}();
EOS
expect(ImageSize.new(f.read).size).to eq(size)
end
end
it 'ignores :selector in #save_screenshot if :full => true' do
@session.visit('/poltergeist/long_page')
@driver.browser.should_receive(:warn).with(/Ignoring :selector/)
create_screenshot file, full: true, selector: '#penultimate'
File.open(file, 'rb') do |f|
expect(ImageSize.new(f.read).size).to eq(
@driver.evaluate_script('[document.documentElement.clientWidth, document.documentElement.clientHeight]')
)
end
end
end
describe '#save_screenshot' do
let(:format) { :png }
let(:file) { POLTERGEIST_ROOT + "/spec/tmp/screenshot.#{format}" }
def create_screenshot(file, *args)
@driver.save_screenshot(file, *args)
end
it 'supports rendering the page' do
FileUtils.rm_f file
@session.visit('/')
@driver.save_screenshot(file)
expect(File.exist?(file)).to be_true
end
it 'supports rendering the page with a nonstring path' do
FileUtils.rm_f file
@session.visit('/')
@driver.save_screenshot(Pathname(file))
expect(File.exist?(file)).to be_true
end
shared_examples 'when #zoom_factor= is set' do
let(:format) {:xbm}
it 'changes image dimensions' do
@session.visit('/poltergeist/zoom_test')
black_pixels_count = ->(file) {
File.read(file).to_s[/{.*}/m][1...-1].split(/\W/).map{|n| n.hex.to_s(2).count('1')}.reduce(:+)
}
@driver.save_screenshot(file)
before = black_pixels_count[file]
@driver.zoom_factor = zoom_factor
@driver.save_screenshot(file)
after = black_pixels_count[file]
expect(after.to_f/before.to_f).to eq(zoom_factor**2)
end
end
context 'zoom in' do
let(:zoom_factor) { 2 }
include_examples 'when #zoom_factor= is set'
end
context 'zoom out' do
let(:zoom_factor) { 0.5 }
include_examples 'when #zoom_factor= is set'
end
context 'when #paper_size= is set' do
let(:format) { :pdf }
it 'changes pdf size' do
@session.visit('/poltergeist/long_page')
@driver.paper_size = { width: '1in', height: '1in' }
@driver.save_screenshot(file)
reader = PDF::Reader.new(file)
reader.pages.each do |page|
bbox = page.attributes[:MediaBox]
width = (bbox[2] - bbox[0]) / 72
expect(width).to eq(1)
end
end
end
include_examples 'render screen'
end
describe '#render_base64' do
let(:file) { POLTERGEIST_ROOT + "/spec/tmp/screenshot.#{format}" }
def create_screenshot(file, *args)
image = @driver.render_base64(format, *args)
File.open(file, 'wb') { |f| f.write Base64.decode64(image) }
end
it 'supports rendering the page in base64' do
@session.visit('/')
screenshot = @driver.render_base64
expect(screenshot.length).to be > 100
end
context 'png' do
let(:format) { :png }
include_examples 'render screen'
end
context 'jpeg' do
let(:format) { :jpeg }
include_examples 'render screen'
end
end
context 'setting headers' do
it 'allows headers to be set' do
@driver.headers = {
"Cookie" => "foo=bar",
"Host" => "foo.com"
}
@session.visit('/poltergeist/headers')
expect(@driver.body).to include('COOKIE: foo=bar')
expect(@driver.body).to include('HOST: foo.com')
end
it 'allows headers to be read' do
expect(@driver.headers).to eq({})
@driver.headers = { 'User-Agent' => 'PhantomJS', 'Host' => 'foo.com' }
expect(@driver.headers).to eq('User-Agent' => 'PhantomJS', 'Host' => 'foo.com')
end
it 'supports User-Agent' do
@driver.headers = { 'User-Agent' => 'foo' }
@session.visit '/'
expect(@driver.evaluate_script('window.navigator.userAgent')).to eq('foo')
end
it 'sets headers for all HTTP requests' do
@driver.headers = { 'X-Omg' => 'wat' }
@session.visit '/'
@driver.execute_script <<-JS
var request = new XMLHttpRequest();
request.open('GET', '/poltergeist/headers', false);
request.send();
if (request.status === 200) {
document.body.innerHTML = request.responseText;
}
JS
expect(@driver.body).to include('X_OMG: wat')
end
it 'adds new headers' do
@driver.headers = { 'User-Agent' => 'PhantomJS', 'Host' => 'foo.com' }
@driver.add_headers('User-Agent' => 'Poltergeist', 'Appended' => 'true')
@session.visit('/poltergeist/headers')
expect(@driver.body).to include('USER_AGENT: Poltergeist')
expect(@driver.body).to include('HOST: foo.com')
expect(@driver.body).to include('APPENDED: true')
end
it 'sets headers on the initial request' do
@driver.headers = { 'PermanentA' => 'a' }
@driver.add_headers('PermanentB' => 'b')
@driver.add_header('Referer', 'http://google.com', :permanent => false)
@driver.add_header('TempA', 'a', :permanent => false)
@session.visit('/poltergeist/headers_with_ajax')
initial_request = @session.find(:css, '#initial_request').text
ajax_request = @session.find(:css, '#ajax_request').text
expect(initial_request).to include('PERMANENTA: a')
expect(initial_request).to include('PERMANENTB: b')
expect(initial_request).to include('REFERER: http://google.com')
expect(initial_request).to include('TEMPA: a')
expect(ajax_request).to include('PERMANENTA: a')
expect(ajax_request).to include('PERMANENTB: b')
expect(ajax_request).to_not include('REFERER: http://google.com')
expect(ajax_request).to_not include('TEMPA: a')
end
end
it 'supports clicking precise coordinates' do
@session.visit('/poltergeist/click_coordinates')
@driver.click(100, 150)
expect(@driver.body).to include('x: 100, y: 150')
end
it 'supports executing multiple lines of javascript' do
@driver.execute_script <<-JS
var a = 1
var b = 2
window.result = a + b
JS
expect(@driver.evaluate_script("result")).to eq(3)
end
it 'operates a timeout when communicating with phantomjs' do
begin
prev_timeout = @driver.timeout
@driver.timeout = 0.001
expect { @driver.browser.command 'noop' }.to raise_error(TimeoutError)
ensure
@driver.timeout = prev_timeout
end
end
unless Capybara::Poltergeist.windows?
# Not sure how to do this on Windows, so skipping
it 'supports quitting the session' do
driver = Capybara::Poltergeist::Driver.new(nil)
pid = driver.client_pid
expect(Process.kill(0, pid)).to eq(1)
driver.quit
begin
Process.kill(0, pid)
rescue Errno::ESRCH
else
raise "process is still alive"
end
end
end
context 'extending browser javascript' do
before do
@extended_driver = Capybara::Poltergeist::Driver.new(
@session.app,
:logger => TestSessions.logger,
:inspector => (ENV['DEBUG'] != nil),
:extensions => %W% #{File.expand_path '../../support/geolocation.js', __FILE__ } %
)
end
it 'supports extending the phantomjs world' do
begin
@extended_driver.visit session_url("/poltergeist/requiring_custom_extension")
expect(@extended_driver.body).
to include(%Q%Location: <span id="location">1,-1</span>%)
expect(
@extended_driver.evaluate_script("document.getElementById('location').innerHTML")
).to eq('1,-1')
expect(
@extended_driver.evaluate_script('navigator.geolocation')
).to_not eq(nil)
ensure
@extended_driver.quit
end
end
end
context 'javascript errors' do
it 'propagates a Javascript error inside Poltergeist to a ruby exception' do
expect { @driver.browser.command 'browser_error' }.to raise_error(BrowserError)
begin
@driver.browser.command 'browser_error'
rescue BrowserError => e
expect(e.message).to include("Error: zomg")
expect(e.message).to include("compiled/browser.js")
else
raise "BrowserError expected"
end
end
it 'propagates an asynchronous Javascript error on the page to a ruby exception' do
@driver.execute_script "setTimeout(function() { omg }, 0)"
sleep 0.01
expect { @driver.execute_script "" }.to raise_error(JavascriptError)
begin
@driver.execute_script "setTimeout(function() { omg }, 0)"
sleep 0.01
@driver.execute_script ""
rescue JavascriptError => e
expect(e.message).to include("omg")
expect(e.message).to include("ReferenceError")
else
raise "expected JavascriptError"
end
end
it 'propagates a synchronous Javascript error on the page to a ruby exception' do
expect { @driver.execute_script "omg" }.to raise_error(JavascriptError)
begin
@driver.execute_script "omg"
rescue JavascriptError => e
expect(e.message).to include("omg")
expect(e.message).to include("ReferenceError")
else
raise "expected JavascriptError"
end
end
it "doesn't re-raise a Javascript error if it's rescued" do
begin
@driver.execute_script "setTimeout(function() { omg }, 0)"
sleep 0.01
@driver.execute_script ""
rescue JavascriptError
else
raise "expected JavascriptError"
end
# should not raise again
expect(@driver.evaluate_script("1+1")).to eq(2)
end
it 'propagates a Javascript error during page load to a ruby exception' do
expect { @session.visit "/poltergeist/js_error" }.to raise_error(JavascriptError)
end
it "doesn't propagate a Javascript error to ruby if error raising disabled" do
begin
driver = Capybara::Poltergeist::Driver.new(@session.app, :js_errors => false, logger: TestSessions.logger)
driver.visit session_url("/poltergeist/js_error")
driver.execute_script "setTimeout(function() { omg }, 0)"
sleep 0.1
expect(driver.body).to include("hello")
ensure
driver.quit if driver
end
end
end
context "phantomjs {'status': 'fail'} responses" do
before { @port = @session.server.port }
it "Do not occur when DNS correct" do
expect { @session.visit("http://localhost:#{@port}/") }.not_to raise_error
end
it "Should handle when DNS incorrect" do
expect { @session.visit("http://nope:#{@port}/") }.to raise_error(StatusFailError)
end
it "Should have a descriptive message when DNS incorrect" do
begin
@session.visit("http://nope:#{@port}/")
rescue StatusFailError => e
expect(e.message).to include("Request failed to reach server, check DNS and/or server status")
else
raise "expected StatusFailError"
end
end
end
context "network traffic" do
before do
@driver.restart
end
it "keeps track of network traffic" do
@session.visit('/poltergeist/with_js')
urls = @driver.network_traffic.map(&:url)
expect(urls.grep(%r{/poltergeist/jquery-1.6.2.min.js$}).size).to eq(1)
expect(urls.grep(%r{/poltergeist/jquery-ui-1.8.14.min.js$}).size).to eq(1)
expect(urls.grep(%r{/poltergeist/test.js$}).size).to eq(1)
end
it "captures responses" do
@session.visit('/poltergeist/with_js')
request = @driver.network_traffic.last
expect(request.response_parts.last.status).to eq(200)
end
it "keeps a running list between multiple web page views" do
@session.visit('/poltergeist/with_js')
expect(@driver.network_traffic.length).to eq(4)
@session.visit('/poltergeist/with_js')
expect(@driver.network_traffic.length).to eq(8)
end
it "gets cleared on restart" do
@session.visit('/poltergeist/with_js')
expect(@driver.network_traffic.length).to eq(4)
@driver.restart
@session.visit('/poltergeist/with_js')
expect(@driver.network_traffic.length).to eq(4)
end
it "gets cleared when being cleared" do
@session.visit('/poltergeist/with_js')
expect(@driver.network_traffic.length).to eq(4)
@driver.clear_network_traffic
expect(@driver.network_traffic.length).to eq(0)
end
end
context 'status code support' do
it 'determines status from the simple response' do
@session.visit('/poltergeist/status/500')
expect(@driver.status_code).to eq(500)
end
it 'determines status code when the page has a few resources' do
@session.visit('/poltergeist/with_different_resources')
expect(@driver.status_code).to eq(200)
end
it 'determines status code even after redirect' do
@session.visit('/poltergeist/redirect')
expect(@driver.status_code).to eq(200)
end
end
context 'cookies support' do
it 'returns set cookies' do
@session.visit('/set_cookie')
cookie = @driver.cookies['capybara']
expect(cookie.name).to eq('capybara')
expect(cookie.value).to eq('test_cookie')
expect(cookie.domain).to eq('127.0.0.1')
expect(cookie.path).to eq('/')
expect(cookie.secure?).to be_false
expect(cookie.httponly?).to be_false
expect(cookie.expires).to be_nil
end
it 'can set cookies' do
@driver.set_cookie 'capybara', 'omg'
@session.visit('/get_cookie')
expect(@driver.body).to include('omg')
end
it 'can set cookies when a space exists in the url' do
@session.visit '/poltergeist/arbitrary_path/200/foo bar'
@driver.set_cookie 'capybara', 'omg'
end
it 'can set cookies with custom settings' do
@driver.set_cookie 'capybara', 'omg', :path => '/poltergeist'
@session.visit('/get_cookie')
expect(@driver.body).to_not include('omg')
@session.visit('/poltergeist/get_cookie')
expect(@driver.body).to include('omg')
expect(@driver.cookies['capybara'].path).to eq('/poltergeist')
end
it 'can remove a cookie' do
@session.visit('/set_cookie')
@session.visit('/get_cookie')
expect(@driver.body).to include('test_cookie')
@driver.remove_cookie 'capybara'
@session.visit('/get_cookie')
expect(@driver.body).to_not include('test_cookie')
end
it 'can set cookies with an expires time' do
time = Time.at(Time.now.to_i + 10000)
@session.visit '/'
@driver.set_cookie 'foo', 'bar', :expires => time
expect(@driver.cookies['foo'].expires).to eq(time)
end
it 'can set cookies for given domain' do
port = @session.server.port
@driver.set_cookie 'capybara', '127.0.0.1'
@driver.set_cookie 'capybara', 'localhost', :domain => 'localhost'
@session.visit("http://localhost:#{port}/poltergeist/get_cookie")
expect(@driver.body).to include('localhost')
@session.visit("http://127.0.0.1:#{port}/poltergeist/get_cookie")
expect(@driver.body).to include('127.0.0.1')
end
it 'can enable and disable cookies' do
@driver.cookies_enabled = false
@session.visit('/set_cookie')
expect(@driver.cookies).to be_empty
@driver.cookies_enabled = true
@session.visit('/set_cookie')
expect(@driver.cookies).to_not be_empty
end
end
it "allows the driver to have a fixed port" do
begin
driver = Capybara::Poltergeist::Driver.new(@driver.app, port: 12345)
driver.visit session_url('/')
expect { TCPServer.new("127.0.0.1", 12345) }.to raise_error(Errno::EADDRINUSE)
ensure
driver.quit
end
end
it 'lists the open windows' do
@session.visit '/'
@session.evaluate_script <<-CODE
window.open('/poltergeist/simple', 'popup')
CODE
@session.evaluate_script <<-CODE
window.open('/poltergeist/simple', 'popup2')
CODE
@session.within_window 'popup2' do
expect(@session.html).to include('Test')
@session.evaluate_script('window.close()')
end
sleep 0.1;
expect(@driver.browser.window_handles).to eq(["popup"])
expect(@driver.window_handles).to eq(["popup"])
end
context 'basic http authentication' do
it 'denies without credentials' do
@session.visit '/poltergeist/basic_auth'
expect(@session.status_code).to eq(401)
expect(@session).not_to have_content('Welcome, authenticated client')
end
it 'allows with given credentials' do
@driver.basic_authorize('login', 'pass')
@session.visit '/poltergeist/basic_auth'
expect(@session.status_code).to eq(200)
expect(@session).to have_content('Welcome, authenticated client')
end
it 'allows even overwriting headers' do
@driver.basic_authorize('login', 'pass')
@driver.headers = [{ 'Poltergeist' => 'true' }]
@session.visit '/poltergeist/basic_auth'
expect(@session.status_code).to eq(200)
expect(@session).to have_content('Welcome, authenticated client')
end
it 'denies with wrong credentials' do
@driver.basic_authorize('user', 'pass!')
@session.visit '/poltergeist/basic_auth'
expect(@session.status_code).to eq(401)
expect(@session).not_to have_content('Welcome, authenticated client')
end
it 'allows on POST request' do
@driver.basic_authorize('login', 'pass')
@session.visit '/poltergeist/basic_auth'
@session.click_button('Submit')
expect(@session.status_code).to eq(200)
expect(@session).to have_content('Authorized POST request')
end
end
context 'has ability to send keys' do
before { @session.visit('/poltergeist/send_keys') }
it 'sends keys to empty input' do
input = @session.find(:css, '#empty_input')
input.native.send_keys('Input')
expect(input.value).to eq('Input')
end
it 'sends keys to filled input' do
input = @session.find(:css, '#filled_input')
input.native.send_keys(' appended')
expect(input.value).to eq('Text appended')
end
it 'sends keys to empty textarea' do
input = @session.find(:css, '#empty_textarea')
input.native.send_keys('Input')
expect(input.value).to eq('Input')
end
it 'sends keys to filled textarea' do
input = @session.find(:css, '#filled_textarea')
input.native.send_keys(' appended')
expect(input.value).to eq('Description appended')
end
it 'sends keys to empty contenteditable div' do
input = @session.find(:css, '#empty_div')
input.native.send_keys('Input')
expect(input.text).to eq('Input')
end
it 'persists focus across calls' do
input = @session.find(:css, '#empty_div')
input.native.send_keys('helo')
input.native.send_keys(:Left)
input.native.send_keys('l')
expect(input.text).to eq('hello')
end
it 'sends keys to filled contenteditable div' do
input = @session.find(:css, '#filled_div')
input.native.send_keys(' appended')
expect(input.text).to eq('Content appended')
end
it 'sends sequences' do
input = @session.find(:css, '#empty_input')
input.native.send_keys(:Shift, 'S', :Alt, 't', 'r', 'i', 'g', :Left, 'n')
expect(input.value).to eq('String')
end
it 'submits the form with sequence' do
input = @session.find(:css, '#without_submit_button input')
input.native.send_keys(:Enter)
expect(input.value).to eq('Submitted')
end
it 'raises error on modifier' do
input = @session.find(:css, '#empty_input')
expect { input.native.send_keys([:Shift, 's'], 'tring') }
.to raise_error(Capybara::Poltergeist::Error)
end
it 'has an alias' do
input = @session.find(:css, '#empty_input')
input.native.send_key('S')
expect(input.value).to eq('S')
end
end
context "set" do
before { @session.visit('/poltergeist/set') }
it "sets a contenteditable's content" do
input = @session.find(:css, '#filled_div')
input.set('new text')
expect(input.text).to eq('new text')
end
it "sets multiple contenteditables' content" do
input = @session.find(:css, '#empty_div')
input.set('new text')
expect(input.text).to eq('new text')
input = @session.find(:css, '#filled_div')
input.set('replacement text')
expect(input.text).to eq('replacement text')
end
end
end
end