require 'spec_helper' require 'capybara/webkit/driver' require 'base64' describe Capybara::Webkit::Driver do include AppRunner context "iframe app" do let(:driver) do driver_for_app do get "/" do if in_iframe_request? p_id = "farewell" msg = "goodbye" iframe = nil else p_id = "greeting" msg = "hello" iframe = "" end <<-HTML
#{iframe} HTML end def in_iframe_request? params[:iframe] == "true" end end end before do driver.visit("/") end it "finds frames by index" do driver.within_frame(0) do driver.find("//*[contains(., 'goodbye')]").should_not be_empty end end it "finds frames by id" do driver.within_frame("f") do driver.find("//*[contains(., 'goodbye')]").should_not be_empty end end it "raises error for missing frame by index" do expect { driver.within_frame(1) { } }. to raise_error(Capybara::Webkit::InvalidResponseError) end it "raise_error for missing frame by id" do expect { driver.within_frame("foo") { } }. to raise_error(Capybara::Webkit::InvalidResponseError) end it "returns an attribute's value" do driver.within_frame("f") do driver.find("//p").first["id"].should == "farewell" end end it "returns an attribute's innerHTML" do driver.find('//body').first.inner_html.should =~ %r{#{env['CONTENT_TYPE']}
" end get '/form' do <<-HTML HTML end post '/redirect' do redirect '/target' end get '/redirect-me' do redirect '/target' end end end it "should redirect without content type" do driver.visit("/form") driver.find("//input").first.click driver.find("//p").first.text.should == "" end it "returns the current URL when changed by pushState after a redirect" do driver.visit("/redirect-me") driver.current_url.should == driver_url(driver, "/target") driver.execute_script("window.history.pushState({}, '', '/pushed-after-redirect')") driver.current_url.should == driver_url(driver, "/pushed-after-redirect") end it "returns the current URL when changed by replaceState after a redirect" do driver.visit("/redirect-me") driver.current_url.should == driver_url(driver, "/target") driver.execute_script("window.history.replaceState({}, '', '/replaced-after-redirect')") driver.current_url.should == driver_url(driver, "/replaced-after-redirect") end it "should make headers available through response_headers" do driver.visit('/redirect-me') driver.response_headers['X-Redirected'].should == "true" end it "should make the status code available through status_code" do driver.visit('/redirect-me') driver.status_code.should == 200 end end context "css app" do let(:driver) do driver_for_app do get "/" do headers "Content-Type" => "text/css" "css" end end end before { driver.visit("/") } it "renders unsupported content types gracefully" do driver.body.should =~ /css/ end it "sets the response headers with respect to the unsupported request" do driver.response_headers["Content-Type"].should == "text/css" end end context "hello app" do let(:driver) do driver_for_html(<<-HTML)hello
HTML end end end before { driver.visit("/") } it "raises a webkit error and then continues" do driver.find("//input").first.click expect { driver.find("//p") }.to raise_error(Capybara::Webkit::InvalidResponseError) driver.visit("/") driver.find("//p").first.text.should == "hello" end end context "popup app" do let(:driver) do driver_for_app do get "/" do sleep(0.5) return <<-HTMLsuccess
HTML end end end before { driver.visit("/") } it "doesn't crash from alerts" do driver.find("//p").first.text.should == "success" end end context "custom header" do let(:driver) do driver_for_app do get "/" do <<-HTML#{env['HTTP_USER_AGENT']}
#{env['HTTP_X_CAPYBARA_WEBKIT_HEADER']}
#{env['HTTP_ACCEPT']}
/ HTML end end end before { driver.visit("/") } before do driver.header('user-agent', 'capybara-webkit/custom-user-agent') driver.header('x-capybara-webkit-header', 'x-capybara-webkit-header') driver.header('accept', 'text/html') driver.visit('/') end it "can set user_agent" do driver.find('id("user-agent")').first.text.should == 'capybara-webkit/custom-user-agent' driver.evaluate_script('navigator.userAgent').should == 'capybara-webkit/custom-user-agent' end it "keep user_agent in next page" do driver.find("//a").first.click driver.find('id("user-agent")').first.text.should == 'capybara-webkit/custom-user-agent' driver.evaluate_script('navigator.userAgent').should == 'capybara-webkit/custom-user-agent' end it "can set custom header" do driver.find('id("x-capybara-webkit-header")').first.text.should == 'x-capybara-webkit-header' end it "can set Accept header" do driver.find('id("accept")').first.text.should == 'text/html' end it "can reset all custom header" do driver.reset! driver.visit('/') driver.find('id("user-agent")').first.text.should_not == 'capybara-webkit/custom-user-agent' driver.evaluate_script('navigator.userAgent').should_not == 'capybara-webkit/custom-user-agent' driver.find('id("x-capybara-webkit-header")').first.text.should be_empty driver.find('id("accept")').first.text.should_not == 'text/html' end end context "no response app" do let(:driver) do driver_for_html(<<-HTML) HTML end before { driver.visit("/") } it "raises a webkit error for the requested url" do make_the_server_go_away expect { driver.find("//body") }. to raise_error(Capybara::Webkit::NoResponseError, %r{response}) make_the_server_come_back end def make_the_server_come_back driver.browser.instance_variable_get(:@connection).unstub!(:gets) driver.browser.instance_variable_get(:@connection).unstub!(:puts) driver.browser.instance_variable_get(:@connection).unstub!(:print) end def make_the_server_go_away driver.browser.instance_variable_get(:@connection).stub!(:gets).and_return(nil) driver.browser.instance_variable_get(:@connection).stub!(:puts) driver.browser.instance_variable_get(:@connection).stub!(:print) end end context "custom font app" do let(:driver) do driver_for_html(<<-HTML)Hello
HTML end before { driver.visit("/") } it "ignores custom fonts" do font_family = driver.evaluate_script(<<-SCRIPT) var element = document.getElementById("text"); element.ownerDocument.defaultView.getComputedStyle(element, null).getPropertyValue("font-family"); SCRIPT font_family.should == "Arial" end end context "cookie-based app" do let(:driver) do driver_for_app do get "/" do headers 'Set-Cookie' => 'cookie=abc; domain=127.0.0.1; path=/' <<-HTML HTML end end end before { driver.visit("/") } def echoed_cookie driver.find('id("cookie")').first.text end it "remembers the cookie on second visit" do echoed_cookie.should == "" driver.visit "/" echoed_cookie.should == "abc" end it "uses a custom cookie" do driver.browser.set_cookie 'cookie=abc; domain=127.0.0.1; path=/' driver.visit "/" echoed_cookie.should == "abc" end it "clears cookies" do driver.browser.clear_cookies driver.visit "/" echoed_cookie.should == "" end it "allows enumeration of cookies" do cookies = driver.browser.get_cookies cookies.size.should == 1 cookie = Hash[cookies[0].split(/\s*;\s*/).map { |x| x.split("=", 2) }] cookie["cookie"].should == "abc" cookie["domain"].should include "127.0.0.1" cookie["path"].should == "/" end it "allows reading access to cookies using a nice syntax" do driver.cookies["cookie"].should == "abc" end end context "remove node app" do let(:driver) do driver_for_html(<<-HTML)Hello
Chapter | Page |
Intro | 1 |
Chapter 1 | 1 |
Chapter 2 | 1 |
Written by me
Let's try out XPath
in capybara-webkit
This paragraph is fascinating.
But not as much as this one.
Let's try if we can select this
HTML end before { driver.visit("/") } it "builds up node paths correctly" do cases = { "//*[contains(@class, 'author')]" => "/html/head/meta[2]", "//*[contains(@class, 'td1')]" => "/html/body/div[@id='toc']/table/thead[@id='head']/tr/td[1]", "//*[contains(@class, 'td2')]" => "/html/body/div[@id='toc']/table/tbody/tr[2]/td[2]", "//h1" => "/html/body/h1", "//*[contains(@class, 'chapter2')]" => "/html/body/h2[2]", "//*[contains(@class, 'p1')]" => "/html/body/p[1]", "//*[contains(@class, 'p2')]" => "/html/body/div[@id='intro']/p[2]", "//*[contains(@class, 'p3')]" => "/html/body/p[3]", } cases.each do |xpath, path| nodes = driver.find(xpath) nodes.size.should == 1 nodes[0].path.should == path end end end context "css overflow app" do let(:driver) do driver_for_html(<<-HTML)finished
" end end end it "loads a page without error" do 10.times do driver.visit("/redirect") driver.find("//p").first.text.should == "finished" end end end context "localStorage works" do let(:driver) do driver_for_html(<<-HTML) HTML end before { driver.visit("/") } it "displays the message on subsequent page loads" do driver.find("//span[contains(.,'localStorage is enabled')]").should be_empty driver.visit "/" driver.find("//span[contains(.,'localStorage is enabled')]").should_not be_empty end end context "form app with server-side handler" do let(:driver) do driver_for_app do post "/" do "Congrats!
" end get "/" do <<-HTMLbananas
HTML end get "/" do sleep params['sleep'].to_i if params['sleep'] "finished
" end end end before { driver.visit("/") } it "has the expected text in the new window" do driver.visit("/new_window") driver.within_window(driver.window_handles.last) do driver.find("//p").first.text.should == "finished" end end it "waits for the new window to load" do driver.visit("/new_window?sleep=1") driver.within_window(driver.window_handles.last) do driver.find("//p").first.text.should == "finished" end end it "waits for the new window to load when the window location has changed" do driver.visit("/new_window?sleep=2") driver.execute_script("setTimeout(function() { window.location = 'about:blank' }, 1000)") driver.within_window(driver.window_handles.last) do driver.find("//p").first.text.should == "finished" end end it "switches back to the original window" do driver.visit("/new_window") driver.within_window(driver.window_handles.last) { } driver.find("//p").first.text.should == "bananas" end it "supports finding a window by name" do driver.visit("/new_window") driver.within_window('myWindow') do driver.find("//p").first.text.should == "finished" end end it "supports finding a window by title" do driver.visit("/new_window?sleep=5") driver.within_window('My New Window') do driver.find("//p").first.text.should == "finished" end end it "supports finding a window by url" do driver.visit("/new_window?test") driver.within_window(driver_url(driver, "/?test")) do driver.find("//p").first.text.should == "finished" end end it "raises an error if the window is not found" do expect { driver.within_window('myWindowDoesNotExist') }. to raise_error(Capybara::Webkit::InvalidResponseError) end it "has a number of window handles equal to the number of open windows" do driver.window_handles.size.should == 1 driver.visit("/new_window") driver.window_handles.size.should == 2 end it "closes new windows on reset" do driver.visit("/new_window") last_handle = driver.window_handles.last driver.reset! driver.window_handles.should_not include(last_handle) end end it "preserves cookies across windows" do session_id = '12345' driver = driver_for_app do get '/new_window' do <<-HTML HTML end get '/set_cookie' do response.set_cookie 'session_id', session_id end end driver.visit("/new_window") driver.cookies['session_id'].should == session_id end context "timers app" do let(:driver) do driver_for_app do get "/success" do '' end get "/not-found" do 404 end get "/outer" do <<-HTML HTML end get '/' do "" end end end before { driver.visit("/") } it "raises error for any loadFinished failure" do expect do driver.visit("/outer") sleep 1 driver.find("//body") end.to raise_error(Capybara::Webkit::InvalidResponseError) end end describe "basic auth" do let(:driver) do driver_for_app do get "/" do if env["HTTP_AUTHORIZATION"] env["HTTP_AUTHORIZATION"] else headers "WWW-Authenticate" => 'Basic realm="Secure Area"' status 401 "401 Unauthorized." end end end end it "can authenticate a request" do driver.browser.authenticate('user', 'password') driver.visit("/") driver.body.should include("Basic "+Base64.encode64("user:password").strip) end end describe "timeout for long requests" do let(:driver) do driver_for_app do html = <<-HTML HTML get "/" do sleep(2) html end post "/form" do sleep(4) html end end end it "should not raise a timeout error when zero" do driver.browser.timeout = 0 lambda { driver.visit("/") }.should_not raise_error(Capybara::TimeoutError) end it "should raise a timeout error" do driver.browser.timeout = 1 lambda { driver.visit("/") }.should raise_error(Capybara::TimeoutError, "Request timed out after 1 second") end it "should not raise an error when the timeout is high enough" do driver.browser.timeout = 10 lambda { driver.visit("/") }.should_not raise_error(Capybara::TimeoutError) end it "should set the timeout for each request" do driver.browser.timeout = 10 lambda { driver.visit("/") }.should_not raise_error(Capybara::TimeoutError) driver.browser.timeout = 1 lambda { driver.visit("/") }.should raise_error(Capybara::TimeoutError) end it "should set the timeout for each request" do driver.browser.timeout = 1 lambda { driver.visit("/") }.should raise_error(Capybara::TimeoutError) driver.reset! driver.browser.timeout = 10 lambda { driver.visit("/") }.should_not raise_error(Capybara::TimeoutError) end it "should raise a timeout on a slow form" do driver.browser.timeout = 3 driver.visit("/") driver.status_code.should == 200 driver.browser.timeout = 1 driver.find("//input").first.click lambda { driver.status_code }.should raise_error(Capybara::TimeoutError) end it "get timeout" do driver.browser.timeout = 10 driver.browser.timeout.should == 10 driver.browser.timeout = 3 driver.browser.timeout.should == 3 end end describe "logger app" do it "logs nothing before turning on the logger" do driver.visit("/") log.should == "" end it "logs its commands after turning on the logger" do driver.enable_logging driver.visit("/") log.should_not == "" end let(:driver) do command = "#{Capybara::Webkit::Connection::SERVER_PATH} 2>&1" connection = Capybara::Webkit::Connection.new(:command => command, :stdout => output) browser = Capybara::Webkit::Browser.new(connection) Capybara::Webkit::Driver.new(AppRunner.app, :browser => browser) end let(:output) { StringIO.new } def log output.rewind output.read end end def driver_url(driver, path) URI.parse(driver.current_url).merge(path).to_s end end