more gutting and cleaning, just like a fish

This commit is contained in:
John Bintz 2011-10-25 15:41:22 -04:00
parent 7db116fb45
commit 9289f97f16
17 changed files with 168 additions and 87 deletions

View File

@ -27,7 +27,7 @@ end
def compile def compile
#system %{cd ext/jasmine-webkit-specrunner && ruby test.rb && ruby extconf.rb} #system %{cd ext/jasmine-webkit-specrunner && ruby test.rb && ruby extconf.rb}
system %{cd ext/jasmine-webkit-specrunner && ruby test.rb && ruby extconf.rb} system %{cd ext/jasmine-webkit-specrunner && ruby extconf.rb}
end end
compile compile

View File

@ -4,27 +4,8 @@
#include "Page.h" #include "Page.h"
Page::Page() : QWebPage(), confirmResult(true) {} Page::Page() : QWebPage() {}
void Page::javaScriptConsoleMessage(const QString &message, int lineNumber, const QString &sourceID) { void Page::javaScriptConsoleMessage(const QString & message, int lineNumber, const QString & sourceID) {
emit consoleLog(message, lineNumber, sourceID); emit handleError(message, lineNumber, sourceID);
} }
bool Page::javaScriptConfirm(QWebFrame*, const QString&) {
if (confirmResult) {
emit internalLog("TODO", "jasmine-headless-webkit can't handle confirm() yet! You should mock window.confirm for now. Returning true.");
return true;
} else {
confirmResult = true;
return false;
}
}
void Page::javaScriptAlert(QWebFrame*, const QString &msg) {
emit internalLog("alert", msg);
}
void Page::oneFalseConfirm() {
confirmResult = false;
}

View File

@ -4,20 +4,14 @@
#include <QtGui> #include <QtGui>
#include <QtWebKit> #include <QtWebKit>
class Page: public QWebPage { class Page: public QWebPage {
Q_OBJECT Q_OBJECT
public: public:
Page(); Page();
void oneFalseConfirm();
signals:
void consoleLog(const QString &msg, int lineNumber, const QString &sourceID);
void internalLog(const QString &note, const QString &msg);
protected: protected:
void javaScriptConsoleMessage(const QString & message, int lineNumber, const QString & sourceID); void javaScriptConsoleMessage(const QString & message, int lineNumber, const QString & sourceID);
bool javaScriptConfirm(QWebFrame *frame, const QString &msg); signals:
void javaScriptAlert(QWebFrame *frame, const QString &msg); void handleError(const QString & message, int lineNumber, const QString & sourceID);
private: };
bool confirmResult;
};
#endif #endif

View File

@ -3,9 +3,11 @@
#include <QFile> #include <QFile>
#include <QTextStream> #include <QTextStream>
#include <iostream> #include <iostream>
#include <sstream>
#include <QQueue> #include <QQueue>
#include "Runner.h" #include "Runner.h"
#include "Page.h"
using namespace std; using namespace std;
@ -15,14 +17,14 @@ Runner::Runner() : QObject()
, usedConsole(false) , usedConsole(false)
, isFinished(false) , isFinished(false)
, didFail(false) , didFail(false)
, useColors(false)
{ {
m_page.settings()->enablePersistentStorage(); m_page.settings()->enablePersistentStorage();
m_ticker.setInterval(TIMER_TICK); m_ticker.setInterval(TIMER_TICK);
connect(&m_ticker, SIGNAL(timeout()), this, SLOT(timerEvent())); connect(&m_ticker, SIGNAL(timeout()), this, SLOT(timerEvent()));
connect(&m_page, SIGNAL(loadFinished(bool)), this, SLOT(watch(bool))); connect(&m_page, SIGNAL(loadFinished(bool)), this, SLOT(watch(bool)));
connect(&m_page, SIGNAL(consoleLog(QString, int, QString)), this, SLOT(errorLog(QString, int, QString))); connect(&m_page, SIGNAL(handleError(const QString &, int, const QString &)), this, SLOT(handleError(const QString &, int, const QString &)));
connect(&m_page, SIGNAL(internalLog(QString, QString)), this, SLOT(internalLog(QString, QString)));
connect(m_page.mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(addJHW())); connect(m_page.mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(addJHW()));
} }
@ -30,24 +32,40 @@ void Runner::addFile(const QString &spec) {
runnerFiles.enqueue(spec); runnerFiles.enqueue(spec);
} }
void Runner::go() void Runner::go() {
{
m_ticker.stop(); m_ticker.stop();
m_page.setPreferredContentsSize(QSize(1024, 600)); m_page.setPreferredContentsSize(QSize(1024, 600));
addJHW(); addJHW();
loadSpec(); loadSpec();
m_ticker.start();
} }
void Runner::addJHW() void Runner::addJHW() {
{
m_page.mainFrame()->addToJavaScriptWindowObject("JHW", this); m_page.mainFrame()->addToJavaScriptWindowObject("JHW", this);
} }
void Runner::handleError(const QString &message, int lineNumber, const QString &sourceID) {
QString messageEscaped = QString(message);
QString sourceIDEscaped = QString(sourceID);
messageEscaped.replace(QString("\""), QString("\\\""));
sourceIDEscaped.replace(QString("\""), QString("\\\""));
std::stringstream ss;
ss << lineNumber;
QString command("JHW._handleError(\"" + messageEscaped + "\", " + QString(ss.str().c_str()) + ", \"" + sourceIDEscaped + "\"); false;");
m_page.mainFrame()->evaluateJavaScript(command);
hasErrors = true;
}
void Runner::loadSpec() void Runner::loadSpec()
{ {
if (!reportFileName.isEmpty()) { if (reportFileName.isEmpty()) {
outputFile = 0;
ts = 0;
} else {
outputFile = new QFile(reportFileName); outputFile = new QFile(reportFileName);
outputFile->open(QIODevice::WriteOnly); outputFile->open(QIODevice::WriteOnly);
@ -58,8 +76,7 @@ void Runner::loadSpec()
m_ticker.start(); m_ticker.start();
} }
void Runner::watch(bool ok) void Runner::watch(bool ok) {
{
if (!ok) { if (!ok) {
std::cerr << "Can't load " << qPrintable(m_page.mainFrame()->url().toString()) << ", the file may be broken." << std::endl; std::cerr << "Can't load " << qPrintable(m_page.mainFrame()->url().toString()) << ", the file may be broken." << std::endl;
std::cerr << "Out of curiosity, did your tests try to submit a form and you haven't prevented that?" << std::endl; std::cerr << "Out of curiosity, did your tests try to submit a form and you haven't prevented that?" << std::endl;
@ -67,13 +84,12 @@ void Runner::watch(bool ok)
QApplication::instance()->exit(1); QApplication::instance()->exit(1);
return; return;
} }
}
bool Runner::hasElement(const char *select) { m_page.mainFrame()->evaluateJavaScript(QString("JHW._setColors(") + (useColors ? QString("true") : QString("false")) + QString("); false;"));
return !m_page.mainFrame()->findFirstElement(select).isNull();
} }
void Runner::setColors(bool colors) { void Runner::setColors(bool colors) {
useColors = colors;
} }
void Runner::reportFile(const QString &file) { void Runner::reportFile(const QString &file) {
@ -99,27 +115,12 @@ void Runner::print(const QString &fh, const QString &content) {
std::cerr.flush(); std::cerr.flush();
} }
if (fh == "report") { if (fh == "report" && outputFile) {
*ts << qPrintable(content); *ts << qPrintable(content);
ts->flush(); ts->flush();
} }
} }
void Runner::errorLog(const QString &msg, int lineNumber, const QString &sourceID)
{
hasErrors = true;
m_runs = 0;
m_ticker.start();
}
void Runner::internalLog(const QString &note, const QString &msg) {}
void Runner::leavePageAttempt(const QString &msg)
{
m_page.oneFalseConfirm();
hasErrors = true;
}
void Runner::finishSuite() { void Runner::finishSuite() {
isFinished = true; isFinished = true;
} }
@ -131,7 +132,9 @@ void Runner::timerEvent() {
QApplication::instance()->exit(1); QApplication::instance()->exit(1);
if (isFinished) { if (isFinished) {
if (outputFile) {
outputFile->close(); outputFile->close();
}
int exitCode = 0; int exitCode = 0;
if (didFail || hasErrors) { if (didFail || hasErrors) {

View File

@ -10,15 +10,13 @@
#include <QQueue> #include <QQueue>
#include "Page.h" #include "Page.h"
#include "ConsoleOutput.h"
#include "ReportFileOutput.h"
using namespace std; using namespace std;
class Runner: public QObject { class Runner: public QObject {
Q_OBJECT Q_OBJECT
public: public:
enum { TIMER_TICK = 200, MAX_LOOPS = 25 }; enum { TIMER_TICK = 200, MAX_LOOPS = 50 };
Runner(); Runner();
void setColors(bool colors); void setColors(bool colors);
@ -26,7 +24,6 @@ class Runner: public QObject {
void addFile(const QString &spec); void addFile(const QString &spec);
void go(); void go();
public slots: public slots:
void leavePageAttempt(const QString &msg);
void timerPause(); void timerPause();
void timerDone(); void timerDone();
@ -35,10 +32,9 @@ class Runner: public QObject {
void finishSuite(); void finishSuite();
private slots: private slots:
void watch(bool ok); void watch(bool ok);
void errorLog(const QString &msg, int lineNumber, const QString &sourceID);
void internalLog(const QString &note, const QString &msg);
void addJHW(); void addJHW();
void timerEvent(); void timerEvent();
void handleError(const QString & message, int lineNumber, const QString & sourceID);
protected: protected:
bool hasElement(const char *select); bool hasElement(const char *select);
private: private:
@ -49,6 +45,8 @@ class Runner: public QObject {
bool usedConsole; bool usedConsole;
bool isFinished; bool isFinished;
bool didFail; bool didFail;
bool useColors;
QQueue<QString> runnerFiles; QQueue<QString> runnerFiles;
QStack<QString> failedSpecs; QStack<QString> failedSpecs;

View File

@ -4,6 +4,6 @@ QMAKE_INFO_PLIST = Info.plist
QMAKESPEC = macx-g++ QMAKESPEC = macx-g++
QT += network webkit QT += network webkit
SOURCES = Page.cpp Runner.cpp ConsoleOutput.cpp ReportFileOutput.cpp SOURCES = Page.cpp Runner.cpp
HEADERS = Page.h Runner.h ConsoleOutput.h ReportFileOutput.h HEADERS = Page.h Runner.h

View File

@ -21,7 +21,6 @@
THE SOFTWARE. THE SOFTWARE.
*/ */
#include "Page.h"
#include "Runner.h" #include "Runner.h"
#if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) #if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)

View File

@ -2,3 +2,4 @@ include(common.pri)
SOURCES += specrunner.cpp SOURCES += specrunner.cpp
TARGET = jasmine-webkit-specrunner TARGET = jasmine-webkit-specrunner

View File

@ -10,9 +10,16 @@ window.Intense = {
white: 7 white: 7
methods: methods:
foreground: (color) -> foreground: (color) ->
if Intense.useColors
"\033[3#{Intense.colors[color]}m#{this}\033[0m" "\033[3#{Intense.colors[color]}m#{this}\033[0m"
else
this
bright: -> bright: ->
if Intense.useColors
"\033[1m#{this}\033[0m" "\033[1m#{this}\033[0m"
else
this
useColors: true
} }
for method, code of Intense.methods for method, code of Intense.methods

View File

@ -13,12 +13,21 @@
}, },
methods: { methods: {
foreground: function(color) { foreground: function(color) {
if (Intense.useColors) {
return "\033[3" + Intense.colors[color] + "m" + this + "\033[0m"; return "\033[3" + Intense.colors[color] + "m" + this + "\033[0m";
} else {
return this;
}
}, },
bright: function() { bright: function() {
if (Intense.useColors) {
return "\033[1m" + this + "\033[0m"; return "\033[1m" + this + "\033[0m";
} else {
return this;
} }
} }
},
useColors: true
}; };
_ref = Intense.methods; _ref = Intense.methods;
for (method in _ref) { for (method in _ref) {

View File

@ -61,7 +61,7 @@ class jasmine.HeadlessReporter
reportSuiteResults: (suite) -> reportSuiteResults: (suite) ->
hasError: -> hasError: ->
@_hasError == true JHW._hasErrors
_formatResultLine: (runtime) -> _formatResultLine: (runtime) ->
line = [] line = []

View File

@ -75,7 +75,7 @@
}; };
HeadlessReporter.prototype.reportSuiteResults = function(suite) {}; HeadlessReporter.prototype.reportSuiteResults = function(suite) {};
HeadlessReporter.prototype.hasError = function() { HeadlessReporter.prototype.hasError = function() {
return this._hasError === true; return JHW._hasErrors;
}; };
HeadlessReporter.prototype._formatResultLine = function(runtime) { HeadlessReporter.prototype._formatResultLine = function(runtime) {
var line; var line;

View File

@ -41,14 +41,23 @@ if window.JHW
window.alert = (message) -> window.alert = (message) ->
JHW.stderr.puts(message) JHW.stderr.puts(message)
JHW.error = (message, sourceUrl, lineNumber) -> JHW._hasErrors = false
JHW._handleError = (message, lineNumber, sourceURL) ->
JHW.stderr.puts(message)
JHW._hasErrors = true
false
for handle in [ 'stdout', 'stderr', 'report' ] JHW._setColors = (what) ->
Intense.useColors = what
createHandle = (handle) ->
JHW[handle] = JHW[handle] =
print: (content) -> JHW.print(handle, content) print: (content) -> JHW.print(handle, content)
puts: (content) -> JHW.print(handle, content + "\n") puts: (content) -> JHW.print(handle, content + "\n")
createHandle(handle) for handle in [ 'stdout', 'stderr', 'report' ]
JHW.log = (msg) -> JHW.log = (msg) ->
JHW.usedConsole() JHW.usedConsole()
JHW.stdout.puts(msg) JHW.stdout.puts(msg)

View File

@ -1,3 +1,83 @@
(function() { (function() {
var createHandle, handle, _i, _len, _ref;
if (window.JHW) {
window.console = {
log: function(data) {
var dump, useJsDump;
if (typeof jQuery !== 'undefined' && data instanceof jQuery) {
return JHW.log(style_html($("<div />").append(data).html(), {
indent_size: 2
}));
} else {
useJsDump = true;
try {
if (typeof data.toJSON === 'function') {
JHW.log("JSON: " + (JSON.stringify(data, null, 2)));
useJsDump = false;
}
} catch (e) {
}
if (useJsDump) {
dump = jsDump.doParse(data);
if (dump.indexOf("\n") === -1) {
return JHW.log(dump);
} else {
return JHW.log("jsDump: " + dump);
}
}
}
},
pp: function(data) {
return JHW.log(jasmine ? jasmine.pp(data) : console.log(data));
},
peek: function(data) {
console.log(data);
return data;
}
};
window.onbeforeunload = function(e) {
JHW.leavePageAttempt();
JHW.stderr.puts('The code tried to leave the test page. Check for unhandled form submits and link clicks.');
if (e = e || window.event) {
e.returnValue = "leaving";
}
return "leaving";
};
window.confirm = function(message) {
JHW.stderr.puts("jasmine-headless-webkit can't handle confirm() yet! You should mock window.confirm. Returning true.");
return true;
};
window.alert = function(message) {
return JHW.stderr.puts(message);
};
JHW._hasErrors = false;
JHW._handleError = function(message, lineNumber, sourceURL) {
JHW.stderr.puts(message);
JHW._hasErrors = true;
return false;
};
JHW._setColors = function(what) {
return Intense.useColors = what;
};
createHandle = function(handle) {
return JHW[handle] = {
print: function(content) {
return JHW.print(handle, content);
},
puts: function(content) {
return JHW.print(handle, content + "\n");
}
};
};
_ref = ['stdout', 'stderr', 'report'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
handle = _ref[_i];
createHandle(handle);
}
JHW.log = function(msg) {
JHW.usedConsole();
return JHW.stdout.puts(msg);
};
}
}).call(this); }).call(this);