mirror of
https://github.com/thoughtbot/capybara-webkit
synced 2023-03-27 23:22:28 -04:00
adds cookie management functions to Webkit::Browser
This commit is contained in:
parent
dc5130ce6b
commit
fb119f6ff8
15 changed files with 298 additions and 2 deletions
|
@ -86,6 +86,20 @@ class Capybara::Driver::Webkit
|
|||
command "Render", path, width, height
|
||||
end
|
||||
|
||||
def set_cookie(cookie)
|
||||
command "SetCookie", cookie
|
||||
end
|
||||
|
||||
def clear_cookies
|
||||
command "ClearCookies"
|
||||
end
|
||||
|
||||
def get_cookies
|
||||
command("GetCookies").lines
|
||||
.map { |line| line.strip }
|
||||
.select { |line| !line.empty? }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def start_server
|
||||
|
|
|
@ -884,6 +884,61 @@ describe Capybara::Driver::Webkit do
|
|||
end
|
||||
end
|
||||
|
||||
context "cookie-based app" do
|
||||
before(:all) do
|
||||
@cookie = 'cookie=abc; domain=127.0.0.1; path=/'
|
||||
@app = lambda do |env|
|
||||
request = ::Rack::Request.new(env)
|
||||
|
||||
body = <<-HTML
|
||||
<html><body>
|
||||
<p id="cookie">#{request.cookies["cookie"] || ""}</p>
|
||||
</body></html>
|
||||
HTML
|
||||
[200,
|
||||
{ 'Content-Type' => 'text/html; charset=UTF-8',
|
||||
'Content-Length' => body.length.to_s,
|
||||
'Set-Cookie' => @cookie,
|
||||
},
|
||||
[body]]
|
||||
end
|
||||
end
|
||||
|
||||
def echoed_cookie
|
||||
subject.find('id("cookie")').first.text
|
||||
end
|
||||
|
||||
it "remembers the cookie on second visit" do
|
||||
echoed_cookie.should == ""
|
||||
subject.visit "/"
|
||||
echoed_cookie.should == "abc"
|
||||
end
|
||||
|
||||
it "uses a custom cookie" do
|
||||
subject.browser.set_cookie @cookie
|
||||
subject.visit "/"
|
||||
echoed_cookie.should == "abc"
|
||||
end
|
||||
|
||||
it "clears cookies" do
|
||||
subject.browser.clear_cookies
|
||||
subject.visit "/"
|
||||
echoed_cookie.should == ""
|
||||
end
|
||||
|
||||
it "allows enumeration of cookies" do
|
||||
cookies = subject.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
|
||||
end
|
||||
|
||||
context "with socket debugger" do
|
||||
let(:socket_debugger_class){ Capybara::Driver::Webkit::SocketDebugger }
|
||||
let(:browser_with_debugger){
|
||||
|
|
18
src/ClearCookies.cpp
Normal file
18
src/ClearCookies.cpp
Normal file
|
@ -0,0 +1,18 @@
|
|||
#include "ClearCookies.h"
|
||||
#include "WebPage.h"
|
||||
#include "NetworkCookieJar.h"
|
||||
#include <QNetworkCookie>
|
||||
|
||||
ClearCookies::ClearCookies(WebPage *page, QObject *parent)
|
||||
: Command(page, parent)
|
||||
{ }
|
||||
|
||||
void ClearCookies::start(QStringList &arguments)
|
||||
{
|
||||
Q_UNUSED(arguments);
|
||||
NetworkCookieJar *jar = qobject_cast<NetworkCookieJar*>(page()
|
||||
->networkAccessManager()
|
||||
->cookieJar());
|
||||
jar->clearCookies();
|
||||
emit finished(new Response(true));
|
||||
}
|
11
src/ClearCookies.h
Normal file
11
src/ClearCookies.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#include "Command.h"
|
||||
|
||||
class WebPage;
|
||||
|
||||
class ClearCookies : public Command {
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
ClearCookies(WebPage *page, QObject *parent = 0);
|
||||
virtual void start(QStringList &arguments);
|
||||
};
|
|
@ -16,6 +16,9 @@
|
|||
#include "Body.h"
|
||||
#include "Status.h"
|
||||
#include "Headers.h"
|
||||
#include "SetCookie.h"
|
||||
#include "ClearCookies.h"
|
||||
#include "GetCookies.h"
|
||||
|
||||
#include <QTcpSocket>
|
||||
#include <iostream>
|
||||
|
|
22
src/GetCookies.cpp
Normal file
22
src/GetCookies.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
#include "GetCookies.h"
|
||||
#include "WebPage.h"
|
||||
#include "NetworkCookieJar.h"
|
||||
|
||||
GetCookies::GetCookies(WebPage *page, QObject *parent)
|
||||
: Command(page, parent)
|
||||
{
|
||||
m_buffer = "";
|
||||
}
|
||||
|
||||
void GetCookies::start(QStringList &arguments)
|
||||
{
|
||||
Q_UNUSED(arguments);
|
||||
NetworkCookieJar *jar = qobject_cast<NetworkCookieJar*>(page()
|
||||
->networkAccessManager()
|
||||
->cookieJar());
|
||||
foreach (QNetworkCookie cookie, jar->getAllCookies()) {
|
||||
m_buffer.append(cookie.toRawForm());
|
||||
m_buffer.append("\n");
|
||||
}
|
||||
emit finished(new Response(true, m_buffer));
|
||||
}
|
14
src/GetCookies.h
Normal file
14
src/GetCookies.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#include "Command.h"
|
||||
|
||||
class WebPage;
|
||||
|
||||
class GetCookies : public Command {
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
GetCookies(WebPage *page, QObject *parent = 0);
|
||||
virtual void start(QStringList &arguments);
|
||||
|
||||
private:
|
||||
QString m_buffer;
|
||||
};
|
101
src/NetworkCookieJar.cpp
Normal file
101
src/NetworkCookieJar.cpp
Normal file
|
@ -0,0 +1,101 @@
|
|||
#include "NetworkCookieJar.h"
|
||||
#include "QtCore/qdatetime.h"
|
||||
|
||||
NetworkCookieJar::NetworkCookieJar(QObject *parent)
|
||||
: QNetworkCookieJar(parent)
|
||||
{ }
|
||||
|
||||
QList<QNetworkCookie> NetworkCookieJar::getAllCookies() const
|
||||
{
|
||||
return allCookies();
|
||||
}
|
||||
|
||||
void NetworkCookieJar::clearCookies()
|
||||
{
|
||||
setAllCookies(QList<QNetworkCookie>());
|
||||
}
|
||||
|
||||
static inline bool isParentDomain(QString domain, QString reference)
|
||||
{
|
||||
if (!reference.startsWith(QLatin1Char('.')))
|
||||
return domain == reference;
|
||||
|
||||
return domain.endsWith(reference) || domain == reference.mid(1);
|
||||
}
|
||||
|
||||
void NetworkCookieJar::overwriteCookies(const QList<QNetworkCookie>& cookieList)
|
||||
{
|
||||
/* this function is basically a copy-and-paste of the original
|
||||
QNetworkCookieJar::setCookiesFromUrl with the domain and
|
||||
path validations removed */
|
||||
|
||||
QString defaultPath(QLatin1Char('/'));
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
QList<QNetworkCookie> newCookies = allCookies();
|
||||
|
||||
foreach (QNetworkCookie cookie, cookieList) {
|
||||
bool isDeletion = (!cookie.isSessionCookie() &&
|
||||
cookie.expirationDate() < now);
|
||||
|
||||
// validate the cookie & set the defaults if unset
|
||||
if (cookie.path().isEmpty())
|
||||
cookie.setPath(defaultPath);
|
||||
|
||||
// don't do path checking. See http://bugreports.qt.nokia.com/browse/QTBUG-5815
|
||||
// else if (!isParentPath(pathAndFileName, cookie.path())) {
|
||||
// continue; // not accepted
|
||||
// }
|
||||
|
||||
if (cookie.domain().isEmpty()) {
|
||||
continue;
|
||||
} else {
|
||||
// Ensure the domain starts with a dot if its field was not empty
|
||||
// in the HTTP header. There are some servers that forget the
|
||||
// leading dot and this is actually forbidden according to RFC 2109,
|
||||
// but all browsers accept it anyway so we do that as well.
|
||||
if (!cookie.domain().startsWith(QLatin1Char('.')))
|
||||
cookie.setDomain(QLatin1Char('.') + cookie.domain());
|
||||
|
||||
QString domain = cookie.domain();
|
||||
|
||||
// the check for effective TLDs makes the "embedded dot" rule from RFC 2109 section 4.3.2
|
||||
// redundant; the "leading dot" rule has been relaxed anyway, see above
|
||||
// we remove the leading dot for this check
|
||||
/*
|
||||
if (QNetworkCookieJarPrivate::isEffectiveTLD(domain.remove(0, 1)))
|
||||
continue; // not accepted
|
||||
*/
|
||||
}
|
||||
|
||||
for (int i = 0; i < newCookies.size(); ++i) {
|
||||
// does this cookie already exist?
|
||||
const QNetworkCookie ¤t = newCookies.at(i);
|
||||
if (cookie.name() == current.name() &&
|
||||
cookie.domain() == current.domain() &&
|
||||
cookie.path() == current.path()) {
|
||||
// found a match
|
||||
newCookies.removeAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// did not find a match
|
||||
if (!isDeletion) {
|
||||
int countForDomain = 0;
|
||||
for (int i = newCookies.size() - 1; i >= 0; --i) {
|
||||
// Start from the end and delete the oldest cookies to keep a maximum count of 50.
|
||||
const QNetworkCookie ¤t = newCookies.at(i);
|
||||
if (isParentDomain(cookie.domain(), current.domain())
|
||||
|| isParentDomain(current.domain(), cookie.domain())) {
|
||||
if (countForDomain >= 49)
|
||||
newCookies.removeAt(i);
|
||||
else
|
||||
++countForDomain;
|
||||
}
|
||||
}
|
||||
|
||||
newCookies += cookie;
|
||||
}
|
||||
}
|
||||
setAllCookies(newCookies);
|
||||
}
|
15
src/NetworkCookieJar.h
Normal file
15
src/NetworkCookieJar.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
#include <QtNetwork/QNetworkCookieJar>
|
||||
#include <QtNetwork/QNetworkCookie>
|
||||
|
||||
class NetworkCookieJar : public QNetworkCookieJar {
|
||||
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
|
||||
NetworkCookieJar(QObject *parent = 0);
|
||||
|
||||
QList<QNetworkCookie> getAllCookies() const;
|
||||
void clearCookies();
|
||||
void overwriteCookies(const QList<QNetworkCookie>& cookieList);
|
||||
};
|
|
@ -1,6 +1,7 @@
|
|||
#include "Reset.h"
|
||||
#include "WebPage.h"
|
||||
#include "NetworkAccessManager.h"
|
||||
#include "NetworkCookieJar.h"
|
||||
|
||||
Reset::Reset(WebPage *page, QObject *parent) : Command(page, parent) {
|
||||
}
|
||||
|
@ -10,7 +11,7 @@ void Reset::start(QStringList &arguments) {
|
|||
|
||||
page()->triggerAction(QWebPage::Stop);
|
||||
page()->currentFrame()->setHtml("<html><body></body></html>");
|
||||
page()->networkAccessManager()->setCookieJar(new QNetworkCookieJar());
|
||||
page()->networkAccessManager()->setCookieJar(new NetworkCookieJar());
|
||||
page()->setCustomNetworkAccessManager();
|
||||
page()->setUserAgent(NULL);
|
||||
page()->resetResponseHeaders();
|
||||
|
|
18
src/SetCookie.cpp
Normal file
18
src/SetCookie.cpp
Normal file
|
@ -0,0 +1,18 @@
|
|||
#include "SetCookie.h"
|
||||
#include "WebPage.h"
|
||||
#include "NetworkCookieJar.h"
|
||||
#include <QNetworkCookie>
|
||||
|
||||
SetCookie::SetCookie(WebPage *page, QObject *parent)
|
||||
: Command(page, parent)
|
||||
{ }
|
||||
|
||||
void SetCookie::start(QStringList &arguments)
|
||||
{
|
||||
QList<QNetworkCookie> cookies = QNetworkCookie::parseCookies(arguments[0].toAscii());
|
||||
NetworkCookieJar *jar = qobject_cast<NetworkCookieJar*>(page()
|
||||
->networkAccessManager()
|
||||
->cookieJar());
|
||||
jar->overwriteCookies(cookies);
|
||||
emit finished(new Response(true));
|
||||
}
|
11
src/SetCookie.h
Normal file
11
src/SetCookie.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#include "Command.h"
|
||||
|
||||
class WebPage;
|
||||
|
||||
class SetCookie : public Command {
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
SetCookie(WebPage *page, QObject *parent = 0);
|
||||
virtual void start(QStringList &arguments);
|
||||
};
|
|
@ -1,6 +1,7 @@
|
|||
#include "WebPage.h"
|
||||
#include "JavascriptInvocation.h"
|
||||
#include "NetworkAccessManager.h"
|
||||
#include "NetworkCookieJar.h"
|
||||
#include "UnsupportedContentHandler.h"
|
||||
#include <QResource>
|
||||
#include <iostream>
|
||||
|
@ -23,6 +24,7 @@ WebPage::WebPage(QObject *parent) : QWebPage(parent) {
|
|||
|
||||
void WebPage::setCustomNetworkAccessManager() {
|
||||
NetworkAccessManager *manager = new NetworkAccessManager();
|
||||
manager->setCookieJar(new NetworkCookieJar());
|
||||
this->setNetworkAccessManager(manager);
|
||||
connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(replyFinished(QNetworkReply *)));
|
||||
}
|
||||
|
|
|
@ -16,4 +16,7 @@ CHECK_COMMAND(Header)
|
|||
CHECK_COMMAND(Render)
|
||||
CHECK_COMMAND(Body)
|
||||
CHECK_COMMAND(Status)
|
||||
CHECK_COMMAND(Headers)
|
||||
CHECK_COMMAND(Headers)
|
||||
CHECK_COMMAND(SetCookie)
|
||||
CHECK_COMMAND(ClearCookies)
|
||||
CHECK_COMMAND(GetCookies)
|
||||
|
|
|
@ -18,12 +18,16 @@ HEADERS = \
|
|||
FrameFocus.h \
|
||||
Response.h \
|
||||
NetworkAccessManager.h \
|
||||
NetworkCookieJar.h \
|
||||
Header.h \
|
||||
Render.h \
|
||||
body.h \
|
||||
Status.h \
|
||||
Headers.h \
|
||||
UnsupportedContentHandler.h \
|
||||
SetCookie.h \
|
||||
ClearCookies.h \
|
||||
GetCookies.h \
|
||||
|
||||
SOURCES = \
|
||||
main.cpp \
|
||||
|
@ -43,12 +47,16 @@ SOURCES = \
|
|||
FrameFocus.cpp \
|
||||
Response.cpp \
|
||||
NetworkAccessManager.cpp \
|
||||
NetworkCookieJar.cpp \
|
||||
Header.cpp \
|
||||
Render.cpp \
|
||||
body.cpp \
|
||||
Status.cpp \
|
||||
Headers.cpp \
|
||||
UnsupportedContentHandler.cpp \
|
||||
SetCookie.cpp \
|
||||
ClearCookies.cpp \
|
||||
GetCookies.cpp \
|
||||
|
||||
RESOURCES = webkit_server.qrc
|
||||
QT += network webkit
|
||||
|
|
Loading…
Reference in a new issue