diff --git a/lib/capybara/driver/webkit.rb b/lib/capybara/driver/webkit.rb index 48373f5a..f897ab65 100644 --- a/lib/capybara/driver/webkit.rb +++ b/lib/capybara/driver/webkit.rb @@ -2,6 +2,7 @@ require "capybara/driver/webkit/node" require "capybara/driver/webkit/browser" require "capybara/driver/webkit/socket_debugger" +require "capybara/driver/webkit/cookie_jar" class Capybara::Driver::Webkit class WebkitInvalidResponseError < StandardError @@ -104,6 +105,10 @@ def server_port @rack_server.port end + def cookies + @cookie_jar ||= CookieJar.new(browser) + end + private def url(path) diff --git a/lib/capybara/driver/webkit/cookie_jar.rb b/lib/capybara/driver/webkit/cookie_jar.rb new file mode 100644 index 00000000..c83e7d1c --- /dev/null +++ b/lib/capybara/driver/webkit/cookie_jar.rb @@ -0,0 +1,55 @@ +require 'webrick' + +# A simple cookie jar implementation. +# Does not take special cookie attributes +# into account like expire, max-age, httponly, secure +class Capybara::Driver::Webkit::CookieJar + attr_reader :browser + + def initialize(browser) + @browser = browser + end + + def [](*args) + cookie = find(*args) + cookie && cookie.value + end + + def find(name, domain = nil, path = "/") + # we are sorting by path size because more specific paths take + # precendence + cookies.sort_by { |c| -c.path.size }.find { |c| + c.name.downcase == name.downcase && + (!domain || valid_domain?(c, domain)) && + (!path || valid_path?(c, path)) + } + end + + protected + + def valid_domain?(cookie, domain) + ends_with?(("." + domain).downcase, + normalize_domain(cookie.domain).downcase) + end + + def normalize_domain(domain) + domain = "." + domain unless domain[0,1] == "." + domain + end + + def valid_path?(cookie, path) + starts_with?(path, cookie.path) + end + + def ends_with?(str, suffix) + str[-suffix.size..-1] == suffix + end + + def starts_with?(str, prefix) + str[0, prefix.size] == prefix + end + + def cookies + browser.get_cookies.map { |c| WEBrick::Cookie.parse_set_cookie(c) } + end +end diff --git a/spec/cookie_jar_spec.rb b/spec/cookie_jar_spec.rb new file mode 100644 index 00000000..c9907cb0 --- /dev/null +++ b/spec/cookie_jar_spec.rb @@ -0,0 +1,48 @@ +require 'spec_helper' +require 'capybara/driver/webkit/cookie_jar' + +describe Capybara::Driver::Webkit::CookieJar do + let(:browser) { + browser = double("Browser") + browser.stub(:get_cookies) { [ + "cookie1=1; domain=.example.org; path=/", + "cookie1=2; domain=.example.org; path=/dir1/", + "cookie1=3; domain=.facebook.com; path=/", + "cookie2=4; domain=.sub1.example.org; path=/", + ] } + browser + } + + subject { Capybara::Driver::Webkit::CookieJar.new(browser) } + + describe "#find" do + it "returns a cookie object" do + subject.find("cookie1", "www.facebook.com").domain.should == ".facebook.com" + end + + it "returns the right cookie for every given domain/path" do + subject.find("cookie1", "example.org").value.should == "1" + subject.find("cookie1", "www.facebook.com").value.should == "3" + subject.find("cookie2", "sub1.example.org").value.should == "4" + end + + it "does not return a cookie from other domain" do + subject.find("cookie2", "www.example.org").should == nil + end + + it "respects path precedence rules" do + subject.find("cookie1", "www.example.org").value.should == "1" + subject.find("cookie1", "www.example.org", "/dir1/123").value.should == "2" + end + end + + describe "#[]" do + it "returns the first matching cookie's value" do + subject["cookie1", "example.org"].should == "1" + end + + it "returns nil if no cookie is found" do + subject["notexisting"].should == nil + end + end +end diff --git a/spec/driver_spec.rb b/spec/driver_spec.rb index 7ffaee7e..b0c18a9e 100644 --- a/spec/driver_spec.rb +++ b/spec/driver_spec.rb @@ -947,6 +947,10 @@ def echoed_cookie cookie["domain"].should include "127.0.0.1" cookie["path"].should == "/" end + + it "allows reading access to cookies using a nice syntax" do + subject.cookies["cookie"].should == "abc" + end end context "with socket debugger" do