Skip to content
This repository has been archived by the owner on Mar 3, 2020. It is now read-only.

Commit

Permalink
Serialize errors as JSON
Browse files Browse the repository at this point in the history
  • Loading branch information
mhoran committed Feb 11, 2013
1 parent fe6eff7 commit 6c72a99
Show file tree
Hide file tree
Showing 24 changed files with 143 additions and 46 deletions.
16 changes: 1 addition & 15 deletions lib/capybara/webkit/browser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -202,26 +202,12 @@ def check
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
Expand Down
12 changes: 12 additions & 0 deletions lib/capybara/webkit/errors.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
module Capybara::Webkit
class InvalidResponseError < StandardError
def self.json_create(o)
new(o["message"])
end
end

class NoResponseError < StandardError
Expand All @@ -9,5 +12,14 @@ class NodeNotAttachedError < Capybara::ElementNotFound
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
7 changes: 6 additions & 1 deletion spec/driver_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,11 @@ def visit(url, driver=driver)
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
Expand Down Expand Up @@ -2059,7 +2064,7 @@ def which_for(character)

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
Expand Down
6 changes: 5 additions & 1 deletion src/Command.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "SocketCommand.h"
#include "Command.h"
#include "ErrorMessage.h"

Command::Command(QObject *parent) : QObject(parent) {
}
Expand All @@ -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));
}
5 changes: 4 additions & 1 deletion src/Command.h
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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);
Expand Down
6 changes: 3 additions & 3 deletions src/Connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "PageLoadingCommand.h"
#include "TimeoutCommand.h"
#include "SocketCommand.h"
#include "ErrorMessage.h"

#include <QTcpSocket>

Expand Down Expand Up @@ -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) {
Expand Down
26 changes: 26 additions & 0 deletions src/ErrorMessage.cpp
Original file line number Diff line number Diff line change
@@ -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);
}
21 changes: 21 additions & 0 deletions src/ErrorMessage.h
Original file line number Diff line number Diff line change
@@ -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
3 changes: 2 additions & 1 deletion src/Execute.cpp
Original file line number Diff line number Diff line change
@@ -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) {
}
Expand All @@ -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"));
}
}

2 changes: 1 addition & 1 deletion src/Find.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
5 changes: 3 additions & 2 deletions src/FrameFocus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
}
Expand Down Expand Up @@ -51,15 +52,15 @@ 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();
}
}

void FrameFocus::frameNotFound() {
emitFinished(false, QString("Unable to locate frame. "));
emitFinished(false, new ErrorMessage("Unable to locate frame."));
}

void FrameFocus::success() {
Expand Down
15 changes: 15 additions & 0 deletions src/InvocationResult.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "InvocationResult.h"
#include "ErrorMessage.h"

InvocationResult::InvocationResult(QVariant result, bool error) {
m_result = result;
Expand All @@ -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);
}
3 changes: 3 additions & 0 deletions src/InvocationResult.h
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
10 changes: 5 additions & 5 deletions src/JavascriptInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
8 changes: 4 additions & 4 deletions src/JavascriptInvocation.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,22 @@ 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:
QString m_functionName;
QStringList m_arguments;
WebPage *m_page;
void execClick(QPoint mousePos);
QVariantMap m_error;
QVariant m_error;
};

2 changes: 1 addition & 1 deletion src/Node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion src/NullCommand.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
#include "NullCommand.h"
#include "WebPage.h"
#include "WebPageManager.h"
#include "ErrorMessage.h"

NullCommand::NullCommand(QString name, QObject *parent) : Command(parent) {
m_name = name;
}

void NullCommand::start() {
QString failure = QString("[Capybara WebKit] Unknown command: ") + m_name + "\n";
emitFinished(false, failure);
emitFinished(false, new ErrorMessage(failure));
}

3 changes: 2 additions & 1 deletion src/PageLoadingCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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));
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/Response.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "Response.h"
#include "ErrorMessage.h"
#include <iostream>

Response::Response(bool success, QString message, QObject *parent) : QObject(parent) {
Expand All @@ -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;
}
Expand Down
Loading

0 comments on commit 6c72a99

Please sign in to comment.