Serialize errors as JSON
This commit is contained in:
parent
fe6eff79e6
commit
6c72a99174
|
@ -202,26 +202,12 @@ module Capybara::Webkit
|
|||
if result.nil?
|
||||
raise NoResponseError, "No response received from the server."
|
||||
elsif result != 'ok'
|
||||
case response = read_response
|
||||
when "timeout"
|
||||
raise Timeout::Error, "Request timed out after #{timeout_seconds}"
|
||||
else
|
||||
raise InvalidResponseError, response
|
||||
end
|
||||
raise JSON.parse(read_response)
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def timeout_seconds
|
||||
seconds = timeout
|
||||
if seconds > 1
|
||||
"#{seconds} seconds"
|
||||
else
|
||||
"1 second"
|
||||
end
|
||||
end
|
||||
|
||||
def read_response
|
||||
response_length = @connection.gets.to_i
|
||||
if response_length > 0
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
module Capybara::Webkit
|
||||
class InvalidResponseError < StandardError
|
||||
def self.json_create(o)
|
||||
new(o["message"])
|
||||
end
|
||||
end
|
||||
|
||||
class NoResponseError < StandardError
|
||||
|
@ -9,5 +12,14 @@ module Capybara::Webkit
|
|||
end
|
||||
|
||||
class ClickFailed < StandardError
|
||||
def self.json_create(o)
|
||||
new
|
||||
end
|
||||
end
|
||||
|
||||
class TimeoutError < Timeout::Error
|
||||
def self.json_create(o)
|
||||
new(o["message"])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -342,6 +342,11 @@ describe Capybara::Webkit::Driver do
|
|||
to raise_error(Capybara::Webkit::InvalidResponseError, /xpath/i)
|
||||
end
|
||||
|
||||
it "raises an error for an invalid xpath query within an element" do
|
||||
expect { driver.find("//body").first.find("totally invalid salad") }.
|
||||
to raise_error(Capybara::Webkit::InvalidResponseError, /xpath/i)
|
||||
end
|
||||
|
||||
it "returns an attribute's value" do
|
||||
driver.find("//p").first["id"].should == "greeting"
|
||||
end
|
||||
|
@ -2059,7 +2064,7 @@ describe Capybara::Webkit::Driver do
|
|||
|
||||
it "should raise a timeout error" do
|
||||
driver.browser.timeout = 1
|
||||
lambda { visit("/") }.should raise_error(Timeout::Error, "Request timed out after 1 second")
|
||||
lambda { visit("/") }.should raise_error(Timeout::Error, "Request timed out after 1 second(s)")
|
||||
end
|
||||
|
||||
it "should not raise an error when the timeout is high enough" do
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "SocketCommand.h"
|
||||
#include "Command.h"
|
||||
#include "ErrorMessage.h"
|
||||
|
||||
Command::Command(QObject *parent) : QObject(parent) {
|
||||
}
|
||||
|
@ -19,3 +20,6 @@ void Command::emitFinished(bool success, QByteArray message) {
|
|||
emit finished(new Response(success, message, this));
|
||||
}
|
||||
|
||||
void Command::emitFinished(bool success, ErrorMessage *message) {
|
||||
emit finished(new Response(success, message, this));
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
#ifndef COMMAND_H
|
||||
#define COMMAND_H
|
||||
|
||||
#include <QObject>
|
||||
#include "Response.h"
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
class ErrorMessage;
|
||||
|
||||
class Command : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -17,6 +19,7 @@ class Command : public QObject {
|
|||
void emitFinished(bool success);
|
||||
void emitFinished(bool success, QString message);
|
||||
void emitFinished(bool success, QByteArray message);
|
||||
void emitFinished(bool success, ErrorMessage *message);
|
||||
|
||||
signals:
|
||||
void finished(Response *response);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "PageLoadingCommand.h"
|
||||
#include "TimeoutCommand.h"
|
||||
#include "SocketCommand.h"
|
||||
#include "ErrorMessage.h"
|
||||
|
||||
#include <QTcpSocket>
|
||||
|
||||
|
@ -43,9 +44,8 @@ void Connection::pendingLoadFinished(bool success) {
|
|||
void Connection::writePageLoadFailure() {
|
||||
m_pageSuccess = true;
|
||||
QString message = currentPage()->failureString();
|
||||
Response *response = new Response(false, message, this);
|
||||
writeResponse(response);
|
||||
delete response;
|
||||
Response response(false, new ErrorMessage(message));
|
||||
writeResponse(&response);
|
||||
}
|
||||
|
||||
void Connection::finishCommand(Response *response) {
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
#include "ErrorMessage.h"
|
||||
#include "JsonSerializer.h"
|
||||
|
||||
ErrorMessage::ErrorMessage(QString message, QObject *parent) : QObject(parent) {
|
||||
m_message = message;
|
||||
}
|
||||
|
||||
ErrorMessage::ErrorMessage(QString type, QString message, QObject *parent) : QObject(parent) {
|
||||
m_type = type;
|
||||
m_message = message;
|
||||
}
|
||||
|
||||
QByteArray ErrorMessage::toString() {
|
||||
JsonSerializer serializer;
|
||||
|
||||
QVariantMap map;
|
||||
|
||||
if (m_type.isNull())
|
||||
map["json_class"] = "Capybara::Webkit::InvalidResponseError";
|
||||
else
|
||||
map["json_class"] = m_type;
|
||||
|
||||
map["message"] = m_message;
|
||||
|
||||
return serializer.serialize(map);
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef __ERROR_MESSAGE_H
|
||||
#define __ERROR_MESSAGE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
|
||||
class ErrorMessage : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ErrorMessage(QString message, QObject *parent = 0);
|
||||
ErrorMessage(QString type, QString message, QObject *parent = 0);
|
||||
QByteArray toString();
|
||||
|
||||
private:
|
||||
QString m_type;
|
||||
QString m_message;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,6 +1,7 @@
|
|||
#include "Execute.h"
|
||||
#include "WebPage.h"
|
||||
#include "WebPageManager.h"
|
||||
#include "ErrorMessage.h"
|
||||
|
||||
Execute::Execute(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
|
||||
}
|
||||
|
@ -11,7 +12,7 @@ void Execute::start() {
|
|||
if (result.isValid()) {
|
||||
emitFinished(true);
|
||||
} else {
|
||||
emitFinished(false, QString("Javascript failed to execute"));
|
||||
emitFinished(false, new ErrorMessage("Javascript failed to execute"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ void Find::start() {
|
|||
InvocationResult result = page()->invokeCapybaraFunction("find", arguments());
|
||||
|
||||
if (result.hasError())
|
||||
return emitFinished(false, QString("Invalid XPath expression"));
|
||||
return emitFinished(false, result.errorMessage());
|
||||
|
||||
QString message;
|
||||
message = result.result().toString();
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "SocketCommand.h"
|
||||
#include "WebPage.h"
|
||||
#include "WebPageManager.h"
|
||||
#include "ErrorMessage.h"
|
||||
|
||||
FrameFocus::FrameFocus(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
|
||||
}
|
||||
|
@ -51,7 +52,7 @@ void FrameFocus::focusId(QString name) {
|
|||
|
||||
void FrameFocus::focusParent() {
|
||||
if (page()->currentFrame()->parentFrame() == 0) {
|
||||
emitFinished(false, QString("Already at parent frame."));
|
||||
emitFinished(false, new ErrorMessage("Already at parent frame."));
|
||||
} else {
|
||||
page()->currentFrame()->parentFrame()->setFocus();
|
||||
success();
|
||||
|
@ -59,7 +60,7 @@ void FrameFocus::focusParent() {
|
|||
}
|
||||
|
||||
void FrameFocus::frameNotFound() {
|
||||
emitFinished(false, QString("Unable to locate frame. "));
|
||||
emitFinished(false, new ErrorMessage("Unable to locate frame."));
|
||||
}
|
||||
|
||||
void FrameFocus::success() {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "InvocationResult.h"
|
||||
#include "ErrorMessage.h"
|
||||
|
||||
InvocationResult::InvocationResult(QVariant result, bool error) {
|
||||
m_result = result;
|
||||
|
@ -12,3 +13,17 @@ const QVariant &InvocationResult::result() const {
|
|||
bool InvocationResult::hasError() {
|
||||
return m_error;
|
||||
}
|
||||
|
||||
ErrorMessage *InvocationResult::errorMessage() {
|
||||
if (!m_result.canConvert<QVariantMap>())
|
||||
return new ErrorMessage(m_result.toString());
|
||||
|
||||
QVariantMap error = m_result.toMap();
|
||||
|
||||
QString message = error["message"].toString();
|
||||
|
||||
if (error["name"] == "Capybara.ClickFailed")
|
||||
return new ErrorMessage("Capybara::Webkit::ClickFailed", message);
|
||||
else
|
||||
return new ErrorMessage(message);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
#include <QVariant>
|
||||
|
||||
class ErrorMessage;
|
||||
|
||||
class InvocationResult {
|
||||
public:
|
||||
InvocationResult(QVariant result, bool error = false);
|
||||
const QVariant &result() const;
|
||||
bool hasError();
|
||||
ErrorMessage *errorMessage();
|
||||
|
||||
private:
|
||||
QVariant m_result;
|
||||
|
|
|
@ -17,21 +17,21 @@ QStringList &JavascriptInvocation::arguments() {
|
|||
return m_arguments;
|
||||
}
|
||||
|
||||
QVariantMap JavascriptInvocation::getError() {
|
||||
QVariant JavascriptInvocation::getError() {
|
||||
return m_error;
|
||||
}
|
||||
|
||||
void JavascriptInvocation::setError(QVariantMap error) {
|
||||
void JavascriptInvocation::setError(QVariant error) {
|
||||
m_error = error;
|
||||
}
|
||||
|
||||
InvocationResult JavascriptInvocation::invoke(QWebFrame *frame) {
|
||||
frame->addToJavaScriptWindowObject("CapybaraInvocation", this);
|
||||
QVariant result = frame->evaluateJavaScript("Capybara.invoke()");
|
||||
if (getError().isEmpty())
|
||||
return InvocationResult(result);
|
||||
else
|
||||
if (getError().isValid())
|
||||
return InvocationResult(getError(), true);
|
||||
else
|
||||
return InvocationResult(result);
|
||||
}
|
||||
|
||||
bool JavascriptInvocation::click(QWebElement element, int left, int top, int width, int height) {
|
||||
|
|
|
@ -10,15 +10,15 @@ class JavascriptInvocation : public QObject {
|
|||
Q_OBJECT
|
||||
Q_PROPERTY(QString functionName READ functionName)
|
||||
Q_PROPERTY(QStringList arguments READ arguments)
|
||||
Q_PROPERTY(QVariantMap error READ getError WRITE setError)
|
||||
Q_PROPERTY(QVariant error READ getError WRITE setError)
|
||||
|
||||
public:
|
||||
JavascriptInvocation(const QString &functionName, const QStringList &arguments, WebPage *page, QObject *parent = 0);
|
||||
QString &functionName();
|
||||
QStringList &arguments();
|
||||
Q_INVOKABLE bool click(QWebElement element, int left, int top, int width, int height);
|
||||
QVariantMap getError();
|
||||
void setError(QVariantMap error);
|
||||
QVariant getError();
|
||||
void setError(QVariant error);
|
||||
InvocationResult invoke(QWebFrame *);
|
||||
|
||||
private:
|
||||
|
@ -26,6 +26,6 @@ class JavascriptInvocation : public QObject {
|
|||
QStringList m_arguments;
|
||||
WebPage *m_page;
|
||||
void execClick(QPoint mousePos);
|
||||
QVariantMap m_error;
|
||||
QVariant m_error;
|
||||
};
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ void Node::start() {
|
|||
InvocationResult result = page()->invokeCapybaraFunction(functionName, functionArguments);
|
||||
|
||||
if (result.hasError())
|
||||
return emitFinished(false);
|
||||
return emitFinished(false, result.errorMessage());
|
||||
|
||||
QString attributeValue = result.result().toString();
|
||||
emitFinished(true, attributeValue);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "NullCommand.h"
|
||||
#include "WebPage.h"
|
||||
#include "WebPageManager.h"
|
||||
#include "ErrorMessage.h"
|
||||
|
||||
NullCommand::NullCommand(QString name, QObject *parent) : Command(parent) {
|
||||
m_name = name;
|
||||
|
@ -8,6 +9,6 @@ NullCommand::NullCommand(QString name, QObject *parent) : Command(parent) {
|
|||
|
||||
void NullCommand::start() {
|
||||
QString failure = QString("[Capybara WebKit] Unknown command: ") + m_name + "\n";
|
||||
emitFinished(false, failure);
|
||||
emitFinished(false, new ErrorMessage(failure));
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "SocketCommand.h"
|
||||
#include "WebPage.h"
|
||||
#include "WebPageManager.h"
|
||||
#include "ErrorMessage.h"
|
||||
|
||||
PageLoadingCommand::PageLoadingCommand(Command *command, WebPageManager *manager, QObject *parent) : Command(parent) {
|
||||
m_manager = manager;
|
||||
|
@ -30,7 +31,7 @@ void PageLoadingCommand::pendingLoadFinished(bool success) {
|
|||
emit finished(m_pendingResponse);
|
||||
} else {
|
||||
QString message = m_manager->currentPage()->failureString();
|
||||
emitFinished(false, message);
|
||||
emitFinished(false, new ErrorMessage(message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "Response.h"
|
||||
#include "ErrorMessage.h"
|
||||
#include <iostream>
|
||||
|
||||
Response::Response(bool success, QString message, QObject *parent) : QObject(parent) {
|
||||
|
@ -11,6 +12,12 @@ Response::Response(bool success, QByteArray message, QObject *parent) : QObject(
|
|||
m_message = message;
|
||||
}
|
||||
|
||||
Response::Response(bool success, ErrorMessage *message, QObject *parent) : QObject(parent) {
|
||||
m_success = success;
|
||||
m_message = message->toString();
|
||||
message->deleteLater();
|
||||
}
|
||||
|
||||
Response::Response(bool success, QObject *parent) : QObject(parent) {
|
||||
m_success = success;
|
||||
}
|
||||
|
|
|
@ -5,20 +5,25 @@
|
|||
#include <QString>
|
||||
#include <QByteArray>
|
||||
|
||||
class ErrorMessage;
|
||||
|
||||
class Response : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Response(bool success, QString message, QObject *parent);
|
||||
Response(bool success, QByteArray message, QObject *parent);
|
||||
Response(bool success, QString message, QObject *parent = 0);
|
||||
Response(bool success, QByteArray message, QObject *parent = 0);
|
||||
Response(bool success, ErrorMessage *message, QObject *parent = 0);
|
||||
Response(bool success, QObject *parent);
|
||||
bool isSuccess() const;
|
||||
QByteArray message() const;
|
||||
QString toString() const;
|
||||
|
||||
protected:
|
||||
QByteArray m_message;
|
||||
|
||||
private:
|
||||
bool m_success;
|
||||
QByteArray m_message;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "SetTimeout.h"
|
||||
#include "WebPageManager.h"
|
||||
#include "ErrorMessage.h"
|
||||
|
||||
SetTimeout::SetTimeout(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
|
||||
}
|
||||
|
@ -13,7 +14,7 @@ void SetTimeout::start() {
|
|||
manager()->setTimeout(timeout);
|
||||
emitFinished(true);
|
||||
} else {
|
||||
emitFinished(false, QString("Invalid value for timeout"));
|
||||
emitFinished(false, new ErrorMessage("Invalid value for timeout"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "Command.h"
|
||||
#include "WebPageManager.h"
|
||||
#include "WebPage.h"
|
||||
#include "ErrorMessage.h"
|
||||
#include <QTimer>
|
||||
#include <QApplication>
|
||||
|
||||
|
@ -45,7 +46,7 @@ void TimeoutCommand::pendingLoadFinished(bool success) {
|
|||
} else {
|
||||
disconnect(m_timer, SIGNAL(timeout()), this, SLOT(commandTimeout()));
|
||||
disconnect(m_manager, SIGNAL(loadStarted()), this, SLOT(pageLoadingFromCommand()));
|
||||
emitFinished(false, m_manager->currentPage()->failureString());
|
||||
emitFinished(false, new ErrorMessage(m_manager->currentPage()->failureString()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,7 +59,8 @@ void TimeoutCommand::commandTimeout() {
|
|||
disconnect(m_manager, SIGNAL(pageFinished(bool)), this, SLOT(pendingLoadFinished(bool)));
|
||||
disconnect(m_command, SIGNAL(finished(Response *)), this, SLOT(commandFinished(Response *)));
|
||||
m_manager->currentPage()->triggerAction(QWebPage::Stop);
|
||||
emit finished(new Response(false, QString("timeout"), this));
|
||||
QString message = QString("Request timed out after %1 second(s)").arg(m_manager->getTimeout());
|
||||
emitFinished(false, new ErrorMessage("Capybara::Webkit::TimeoutError", message));
|
||||
}
|
||||
|
||||
void TimeoutCommand::commandFinished(Response *response) {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "WebPage.h"
|
||||
#include "CommandFactory.h"
|
||||
#include "WebPageManager.h"
|
||||
#include "ErrorMessage.h"
|
||||
|
||||
WindowFocus::WindowFocus(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
|
||||
}
|
||||
|
@ -12,7 +13,7 @@ void WindowFocus::start() {
|
|||
}
|
||||
|
||||
void WindowFocus::windowNotFound() {
|
||||
emitFinished(false, QString("Unable to locate window. "));
|
||||
emitFinished(false, new ErrorMessage("Unable to locate window."));
|
||||
}
|
||||
|
||||
void WindowFocus::success(WebPage *page) {
|
||||
|
|
|
@ -57,7 +57,8 @@ HEADERS = \
|
|||
SetUrlBlacklist.h \
|
||||
NoOpReply.h \
|
||||
JsonSerializer.h \
|
||||
InvocationResult.h
|
||||
InvocationResult.h \
|
||||
ErrorMessage.h
|
||||
|
||||
SOURCES = \
|
||||
Version.cpp \
|
||||
|
@ -116,7 +117,8 @@ SOURCES = \
|
|||
SetUrlBlacklist.cpp \
|
||||
NoOpReply.cpp \
|
||||
JsonSerializer.cpp \
|
||||
InvocationResult.cpp
|
||||
InvocationResult.cpp \
|
||||
ErrorMessage.cpp
|
||||
|
||||
RESOURCES = webkit_server.qrc
|
||||
QT += network webkit
|
||||
|
|
Loading…
Reference in New Issue