start c++ cleanup
This commit is contained in:
parent
316383671a
commit
60713fc88f
3
.gitignore
vendored
3
.gitignore
vendored
@ -6,3 +6,6 @@ Makefile
|
|||||||
specrunner.moc
|
specrunner.moc
|
||||||
specrunner.o
|
specrunner.o
|
||||||
ext/jasmine-webkit-specrunner/jasmine-webkit-specrunner
|
ext/jasmine-webkit-specrunner/jasmine-webkit-specrunner
|
||||||
|
*.o
|
||||||
|
moc_*.*
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
guard 'shell' do
|
guard 'shell' do
|
||||||
watch(%r{ext/jasmine-webkit-specrunner/specrunner.cpp}) { compile }
|
watch(%r{ext/jasmine-webkit-specrunner/.*\.(cpp|h)}) { compile }
|
||||||
end
|
end
|
||||||
# A sample Guardfile
|
# A sample Guardfile
|
||||||
# More info at https://github.com/guard/guard#readme
|
# More info at https://github.com/guard/guard#readme
|
||||||
|
30
ext/jasmine-webkit-specrunner/HeadlessSpecRunner/Page.cpp
Normal file
30
ext/jasmine-webkit-specrunner/HeadlessSpecRunner/Page.cpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#include <QtGui>
|
||||||
|
#include <QtWebKit>
|
||||||
|
|
||||||
|
#include "Page.h"
|
||||||
|
|
||||||
|
namespace HeadlessSpecRunner {
|
||||||
|
Page::Page() : QWebPage(), confirmResult(true) {}
|
||||||
|
|
||||||
|
void Page::javaScriptConsoleMessage(const QString &message, int lineNumber, const QString &sourceID) {
|
||||||
|
emit consoleLog(message, lineNumber, sourceID);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Page::javaScriptConfirm(QWebFrame *frame, const QString &msg) {
|
||||||
|
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 *frame, const QString &msg) {
|
||||||
|
emit internalLog("alert", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Page::oneFalseConfirm() {
|
||||||
|
confirmResult = false;
|
||||||
|
}
|
||||||
|
}
|
25
ext/jasmine-webkit-specrunner/HeadlessSpecRunner/Page.h
Normal file
25
ext/jasmine-webkit-specrunner/HeadlessSpecRunner/Page.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef JHW_PAGE
|
||||||
|
#define JHW_PAGE
|
||||||
|
|
||||||
|
#include <QtGui>
|
||||||
|
#include <QtWebKit>
|
||||||
|
|
||||||
|
namespace HeadlessSpecRunner {
|
||||||
|
class Page: public QWebPage {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
Page();
|
||||||
|
void oneFalseConfirm();
|
||||||
|
signals:
|
||||||
|
void consoleLog(const QString &msg, int lineNumber, const QString &sourceID);
|
||||||
|
void internalLog(const QString ¬e, const QString &msg);
|
||||||
|
protected:
|
||||||
|
void javaScriptConsoleMessage(const QString & message, int lineNumber, const QString & sourceID);
|
||||||
|
bool javaScriptConfirm(QWebFrame *frame, const QString &msg);
|
||||||
|
void javaScriptAlert(QWebFrame *frame, const QString &msg);
|
||||||
|
private:
|
||||||
|
bool confirmResult;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
266
ext/jasmine-webkit-specrunner/HeadlessSpecRunner/Runner.cpp
Normal file
266
ext/jasmine-webkit-specrunner/HeadlessSpecRunner/Runner.cpp
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
#include <QtGui>
|
||||||
|
#include <QtWebKit>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <QQueue>
|
||||||
|
|
||||||
|
#include "Runner.h"
|
||||||
|
|
||||||
|
namespace HeadlessSpecRunner {
|
||||||
|
Runner::Runner() : QObject()
|
||||||
|
, m_runs(0)
|
||||||
|
, hasErrors(false)
|
||||||
|
, usedConsole(false)
|
||||||
|
, showColors(false)
|
||||||
|
, isFinished(false)
|
||||||
|
, didFail(false)
|
||||||
|
, consoleNotUsedThisRun(false) {
|
||||||
|
m_page.settings()->enablePersistentStorage();
|
||||||
|
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(internalLog(QString, QString)), this, SLOT(internalLog(QString, QString)));
|
||||||
|
connect(m_page.mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(addJHW()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Runner::addFile(const QString &spec) {
|
||||||
|
runnerFiles.enqueue(spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Runner::go()
|
||||||
|
{
|
||||||
|
m_ticker.stop();
|
||||||
|
m_page.setPreferredContentsSize(QSize(1024, 600));
|
||||||
|
addJHW();
|
||||||
|
loadSpec();
|
||||||
|
}
|
||||||
|
void Runner::addJHW()
|
||||||
|
{
|
||||||
|
m_page.mainFrame()->addToJavaScriptWindowObject("JHW", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Runner::loadSpec()
|
||||||
|
{
|
||||||
|
m_page.mainFrame()->load(runnerFiles.dequeue());
|
||||||
|
m_ticker.start(200, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Runner::watch(bool ok)
|
||||||
|
{
|
||||||
|
if (!ok) {
|
||||||
|
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 << "Try running your tests in your browser with the Jasmine server and see what happens." << std::endl;
|
||||||
|
QApplication::instance()->exit(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ticker.start(200, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Runner::hasElement(const char *select)
|
||||||
|
{
|
||||||
|
return !m_page.mainFrame()->findFirstElement(select).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Runner::setColors(bool colors)
|
||||||
|
{
|
||||||
|
showColors = colors;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Runner::reportFile(const QString &file)
|
||||||
|
{
|
||||||
|
reportFilename = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Runner::red()
|
||||||
|
{
|
||||||
|
if (showColors) std::cout << "\033[0;31m";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Runner::green()
|
||||||
|
{
|
||||||
|
if (showColors) std::cout << "\033[0;32m";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Runner::yellow()
|
||||||
|
{
|
||||||
|
if (showColors) std::cout << "\033[0;33m";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Runner::clear()
|
||||||
|
{
|
||||||
|
if (showColors) std::cout << "\033[m";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Runner::specPassed()
|
||||||
|
{
|
||||||
|
consoleNotUsedThisRun = true;
|
||||||
|
green();
|
||||||
|
std::cout << '.';
|
||||||
|
clear();
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Runner::specFailed(const QString &specDetail)
|
||||||
|
{
|
||||||
|
consoleNotUsedThisRun = true;
|
||||||
|
didFail = true;
|
||||||
|
red();
|
||||||
|
std::cout << 'F';
|
||||||
|
failedSpecs.push(specDetail);
|
||||||
|
clear();
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Runner::errorLog(const QString &msg, int lineNumber, const QString &sourceID)
|
||||||
|
{
|
||||||
|
red();
|
||||||
|
std::cout << "[error] ";
|
||||||
|
clear();
|
||||||
|
std::cout << qPrintable(sourceID) << ":" << lineNumber << " : " << qPrintable(msg);
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
hasErrors = true;
|
||||||
|
m_runs = 0;
|
||||||
|
m_ticker.start(200, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Runner::internalLog(const QString ¬e, const QString &msg) {
|
||||||
|
red();
|
||||||
|
std::cout << "[" << qPrintable(note) << "] ";
|
||||||
|
clear();
|
||||||
|
std::cout << qPrintable(msg);
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Runner::log(const QString &msg)
|
||||||
|
{
|
||||||
|
usedConsole = true;
|
||||||
|
green();
|
||||||
|
if (consoleNotUsedThisRun) {
|
||||||
|
std::cout << std::endl;
|
||||||
|
consoleNotUsedThisRun = false;
|
||||||
|
}
|
||||||
|
std::cout << "[console] ";
|
||||||
|
clear();
|
||||||
|
if (msg.contains("\n"))
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::cout << qPrintable(msg);
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Runner::leavePageAttempt(const QString &msg)
|
||||||
|
{
|
||||||
|
red();
|
||||||
|
std::cout << "[error] ";
|
||||||
|
clear();
|
||||||
|
std::cout << qPrintable(msg) << std::endl;
|
||||||
|
m_page.oneFalseConfirm();
|
||||||
|
hasErrors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Runner::printName(const QString &name)
|
||||||
|
{
|
||||||
|
std::cout << std::endl << std::endl;
|
||||||
|
red();
|
||||||
|
std::cout << qPrintable(name) << std::endl;
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Runner::printResult(const QString &result)
|
||||||
|
{
|
||||||
|
red();
|
||||||
|
std::cout << " " << qPrintable(result) << std::endl;
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Runner::finishSuite(const QString &duration, const QString &total, const QString& failed)
|
||||||
|
{
|
||||||
|
std::cout << std::endl;
|
||||||
|
if (didFail) {
|
||||||
|
red();
|
||||||
|
std::cout << "FAIL: ";
|
||||||
|
} else {
|
||||||
|
green();
|
||||||
|
std::cout << "PASS";
|
||||||
|
|
||||||
|
if (hasErrors) {
|
||||||
|
std::cout << " with JS errors";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << ": ";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << qPrintable(total) << " tests, " << qPrintable(failed) << " failures, " << qPrintable(duration) << " secs.";
|
||||||
|
clear();
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
if (!reportFilename.isEmpty()) {
|
||||||
|
QFile reportFH(reportFilename);
|
||||||
|
|
||||||
|
if (reportFH.open(QFile::WriteOnly)) {
|
||||||
|
QTextStream report(&reportFH);
|
||||||
|
report << qPrintable(total) << "/" << qPrintable(failed) << "/";
|
||||||
|
report << (usedConsole ? "T" : "F");
|
||||||
|
report << "/" << qPrintable(duration) << "\n";
|
||||||
|
|
||||||
|
QString failedSpec;
|
||||||
|
|
||||||
|
while (!failedSpecs.isEmpty()) {
|
||||||
|
failedSpec = failedSpecs.pop();
|
||||||
|
report << qPrintable(failedSpec) << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
reportFH.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isFinished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Runner::timerEvent(QTimerEvent *event)
|
||||||
|
{
|
||||||
|
++m_runs;
|
||||||
|
|
||||||
|
if (event->timerId() != m_ticker.timerId())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (hasErrors && m_runs > 2)
|
||||||
|
QApplication::instance()->exit(1);
|
||||||
|
|
||||||
|
if (isFinished) {
|
||||||
|
int exitCode = 0;
|
||||||
|
if (didFail || hasErrors) {
|
||||||
|
exitCode = 1;
|
||||||
|
} else {
|
||||||
|
if (usedConsole) {
|
||||||
|
exitCode = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool runAgain = true;
|
||||||
|
|
||||||
|
if (runnerFiles.count() == 0) {
|
||||||
|
runAgain = false;
|
||||||
|
} else {
|
||||||
|
if (exitCode == 1) {
|
||||||
|
runAgain = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runAgain) {
|
||||||
|
isFinished = false;
|
||||||
|
loadSpec();
|
||||||
|
} else {
|
||||||
|
QApplication::instance()->exit(exitCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_runs > 30) {
|
||||||
|
std::cout << "WARNING: too many runs and the test is still not finished!" << std::endl;
|
||||||
|
QApplication::instance()->exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
60
ext/jasmine-webkit-specrunner/HeadlessSpecRunner/Runner.h
Normal file
60
ext/jasmine-webkit-specrunner/HeadlessSpecRunner/Runner.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#ifndef JHW_RUNNER
|
||||||
|
#define JHW_RUNNER
|
||||||
|
|
||||||
|
#include <QtGui>
|
||||||
|
#include <QtWebKit>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <QQueue>
|
||||||
|
|
||||||
|
#include "Page.h"
|
||||||
|
|
||||||
|
namespace HeadlessSpecRunner {
|
||||||
|
class Runner: public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
Runner();
|
||||||
|
void setColors(bool colors);
|
||||||
|
void reportFile(const QString &file);
|
||||||
|
void addFile(const QString &spec);
|
||||||
|
void go();
|
||||||
|
public slots:
|
||||||
|
void log(const QString &msg);
|
||||||
|
void leavePageAttempt(const QString &msg);
|
||||||
|
void specPassed();
|
||||||
|
void specFailed(const QString &specDetail);
|
||||||
|
void printName(const QString &name);
|
||||||
|
void printResult(const QString &result);
|
||||||
|
void finishSuite(const QString &duration, const QString &total, const QString& failed);
|
||||||
|
private slots:
|
||||||
|
void watch(bool ok);
|
||||||
|
void errorLog(const QString &msg, int lineNumber, const QString &sourceID);
|
||||||
|
void internalLog(const QString ¬e, const QString &msg);
|
||||||
|
void addJHW();
|
||||||
|
protected:
|
||||||
|
bool hasElement(const char *select);
|
||||||
|
void timerEvent(QTimerEvent *event);
|
||||||
|
private:
|
||||||
|
HeadlessSpecRunner::Page m_page;
|
||||||
|
QBasicTimer m_ticker;
|
||||||
|
int m_runs;
|
||||||
|
bool hasErrors;
|
||||||
|
bool usedConsole;
|
||||||
|
bool showColors;
|
||||||
|
bool isFinished;
|
||||||
|
bool didFail;
|
||||||
|
bool consoleNotUsedThisRun;
|
||||||
|
QQueue<QString> runnerFiles;
|
||||||
|
QString reportFilename;
|
||||||
|
QStack<QString> failedSpecs;
|
||||||
|
|
||||||
|
void red();
|
||||||
|
void green();
|
||||||
|
void yellow();
|
||||||
|
void clear();
|
||||||
|
void loadSpec();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -1,3 +1,5 @@
|
|||||||
|
require 'fileutils'
|
||||||
|
|
||||||
$: << File.expand_path("../../../lib", __FILE__)
|
$: << File.expand_path("../../../lib", __FILE__)
|
||||||
|
|
||||||
require 'qt/qmake'
|
require 'qt/qmake'
|
||||||
|
@ -1,429 +1,68 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2010 Sencha Inc.
|
Copyright (c) 2010 Sencha Inc.
|
||||||
Copyright (c) 2011 John Bintz
|
Copyright (c) 2011 John Bintz
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
in the Software without restriction, including without limitation the rights
|
in the Software without restriction, including without limitation the rights
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
furnished to do so, subject to the following conditions:
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
The above copyright notice and this permission notice shall be included in
|
||||||
all copies or substantial portions of the Software.
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QtGui>
|
#include "HeadlessSpecRunner/Page.h"
|
||||||
#include <QtWebKit>
|
#include "HeadlessSpecRunner/Runner.h"
|
||||||
#include <QFile>
|
|
||||||
#include <QTextStream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <QQueue>
|
|
||||||
|
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
|
#if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
|
||||||
#error Use Qt 4.7 or later version
|
#error Use Qt 4.7 or later version
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class HeadlessSpecRunnerPage: public QWebPage
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
HeadlessSpecRunnerPage();
|
|
||||||
void oneFalseConfirm();
|
|
||||||
signals:
|
|
||||||
void consoleLog(const QString &msg, int lineNumber, const QString &sourceID);
|
|
||||||
void internalLog(const QString ¬e, const QString &msg);
|
|
||||||
protected:
|
|
||||||
void javaScriptConsoleMessage(const QString & message, int lineNumber, const QString & sourceID);
|
|
||||||
bool javaScriptConfirm(QWebFrame *frame, const QString &msg);
|
|
||||||
void javaScriptAlert(QWebFrame *frame, const QString &msg);
|
|
||||||
private:
|
|
||||||
bool confirmResult;
|
|
||||||
};
|
|
||||||
|
|
||||||
HeadlessSpecRunnerPage::HeadlessSpecRunnerPage()
|
|
||||||
: QWebPage()
|
|
||||||
, confirmResult(true)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeadlessSpecRunnerPage::javaScriptConsoleMessage(const QString &message, int lineNumber, const QString &sourceID)
|
|
||||||
{
|
|
||||||
emit consoleLog(message, lineNumber, sourceID);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HeadlessSpecRunnerPage::javaScriptConfirm(QWebFrame *frame, const QString &msg)
|
|
||||||
{
|
|
||||||
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 HeadlessSpecRunnerPage::javaScriptAlert(QWebFrame *frame, const QString &msg)
|
|
||||||
{
|
|
||||||
emit internalLog("alert", msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeadlessSpecRunnerPage::oneFalseConfirm()
|
|
||||||
{
|
|
||||||
confirmResult = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
class HeadlessSpecRunner: public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
HeadlessSpecRunner();
|
|
||||||
void setColors(bool colors);
|
|
||||||
void reportFile(const QString &file);
|
|
||||||
void addFile(const QString &spec);
|
|
||||||
void go();
|
|
||||||
public slots:
|
|
||||||
void log(const QString &msg);
|
|
||||||
void leavePageAttempt(const QString &msg);
|
|
||||||
void specPassed();
|
|
||||||
void specFailed(const QString &specDetail);
|
|
||||||
void printName(const QString &name);
|
|
||||||
void printResult(const QString &result);
|
|
||||||
void finishSuite(const QString &duration, const QString &total, const QString& failed);
|
|
||||||
private slots:
|
|
||||||
void watch(bool ok);
|
|
||||||
void errorLog(const QString &msg, int lineNumber, const QString &sourceID);
|
|
||||||
void internalLog(const QString ¬e, const QString &msg);
|
|
||||||
void addJHW();
|
|
||||||
protected:
|
|
||||||
bool hasElement(const char *select);
|
|
||||||
void timerEvent(QTimerEvent *event);
|
|
||||||
private:
|
|
||||||
HeadlessSpecRunnerPage m_page;
|
|
||||||
QBasicTimer m_ticker;
|
|
||||||
int m_runs;
|
|
||||||
bool hasErrors;
|
|
||||||
bool usedConsole;
|
|
||||||
bool showColors;
|
|
||||||
bool isFinished;
|
|
||||||
bool didFail;
|
|
||||||
bool consoleNotUsedThisRun;
|
|
||||||
QQueue<QString> runnerFiles;
|
|
||||||
QString reportFilename;
|
|
||||||
QStack<QString> failedSpecs;
|
|
||||||
|
|
||||||
void red();
|
|
||||||
void green();
|
|
||||||
void yellow();
|
|
||||||
void clear();
|
|
||||||
void loadSpec();
|
|
||||||
};
|
|
||||||
|
|
||||||
HeadlessSpecRunner::HeadlessSpecRunner()
|
|
||||||
: QObject()
|
|
||||||
, m_runs(0)
|
|
||||||
, hasErrors(false)
|
|
||||||
, usedConsole(false)
|
|
||||||
, showColors(false)
|
|
||||||
, isFinished(false)
|
|
||||||
, didFail(false)
|
|
||||||
, consoleNotUsedThisRun(false)
|
|
||||||
{
|
|
||||||
m_page.settings()->enablePersistentStorage();
|
|
||||||
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(internalLog(QString, QString)), this, SLOT(internalLog(QString, QString)));
|
|
||||||
connect(m_page.mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(addJHW()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeadlessSpecRunner::addFile(const QString &spec)
|
|
||||||
{
|
|
||||||
runnerFiles.enqueue(spec);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeadlessSpecRunner::go()
|
|
||||||
{
|
|
||||||
m_ticker.stop();
|
|
||||||
m_page.setPreferredContentsSize(QSize(1024, 600));
|
|
||||||
addJHW();
|
|
||||||
loadSpec();
|
|
||||||
}
|
|
||||||
void HeadlessSpecRunner::addJHW()
|
|
||||||
{
|
|
||||||
m_page.mainFrame()->addToJavaScriptWindowObject("JHW", this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeadlessSpecRunner::loadSpec()
|
|
||||||
{
|
|
||||||
m_page.mainFrame()->load(runnerFiles.dequeue());
|
|
||||||
m_ticker.start(200, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeadlessSpecRunner::watch(bool ok)
|
|
||||||
{
|
|
||||||
if (!ok) {
|
|
||||||
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 << "Try running your tests in your browser with the Jasmine server and see what happens." << std::endl;
|
|
||||||
QApplication::instance()->exit(1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_ticker.start(200, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HeadlessSpecRunner::hasElement(const char *select)
|
|
||||||
{
|
|
||||||
return !m_page.mainFrame()->findFirstElement(select).isNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeadlessSpecRunner::setColors(bool colors)
|
|
||||||
{
|
|
||||||
showColors = colors;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeadlessSpecRunner::reportFile(const QString &file)
|
|
||||||
{
|
|
||||||
reportFilename = file;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeadlessSpecRunner::red()
|
|
||||||
{
|
|
||||||
if (showColors) std::cout << "\033[0;31m";
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeadlessSpecRunner::green()
|
|
||||||
{
|
|
||||||
if (showColors) std::cout << "\033[0;32m";
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeadlessSpecRunner::yellow()
|
|
||||||
{
|
|
||||||
if (showColors) std::cout << "\033[0;33m";
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeadlessSpecRunner::clear()
|
|
||||||
{
|
|
||||||
if (showColors) std::cout << "\033[m";
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeadlessSpecRunner::specPassed()
|
|
||||||
{
|
|
||||||
consoleNotUsedThisRun = true;
|
|
||||||
green();
|
|
||||||
std::cout << '.';
|
|
||||||
clear();
|
|
||||||
fflush(stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeadlessSpecRunner::specFailed(const QString &specDetail)
|
|
||||||
{
|
|
||||||
consoleNotUsedThisRun = true;
|
|
||||||
didFail = true;
|
|
||||||
red();
|
|
||||||
std::cout << 'F';
|
|
||||||
failedSpecs.push(specDetail);
|
|
||||||
clear();
|
|
||||||
fflush(stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeadlessSpecRunner::errorLog(const QString &msg, int lineNumber, const QString &sourceID)
|
|
||||||
{
|
|
||||||
red();
|
|
||||||
std::cout << "[error] ";
|
|
||||||
clear();
|
|
||||||
std::cout << qPrintable(sourceID) << ":" << lineNumber << " : " << qPrintable(msg);
|
|
||||||
std::cout << std::endl;
|
|
||||||
|
|
||||||
hasErrors = true;
|
|
||||||
m_runs = 0;
|
|
||||||
m_ticker.start(200, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeadlessSpecRunner::internalLog(const QString ¬e, const QString &msg) {
|
|
||||||
red();
|
|
||||||
std::cout << "[" << qPrintable(note) << "] ";
|
|
||||||
clear();
|
|
||||||
std::cout << qPrintable(msg);
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeadlessSpecRunner::log(const QString &msg)
|
|
||||||
{
|
|
||||||
usedConsole = true;
|
|
||||||
green();
|
|
||||||
if (consoleNotUsedThisRun) {
|
|
||||||
std::cout << std::endl;
|
|
||||||
consoleNotUsedThisRun = false;
|
|
||||||
}
|
|
||||||
std::cout << "[console] ";
|
|
||||||
clear();
|
|
||||||
if (msg.contains("\n"))
|
|
||||||
std::cout << std::endl;
|
|
||||||
std::cout << qPrintable(msg);
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeadlessSpecRunner::leavePageAttempt(const QString &msg)
|
|
||||||
{
|
|
||||||
red();
|
|
||||||
std::cout << "[error] ";
|
|
||||||
clear();
|
|
||||||
std::cout << qPrintable(msg) << std::endl;
|
|
||||||
m_page.oneFalseConfirm();
|
|
||||||
hasErrors = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeadlessSpecRunner::printName(const QString &name)
|
|
||||||
{
|
|
||||||
std::cout << std::endl << std::endl;
|
|
||||||
red();
|
|
||||||
std::cout << qPrintable(name) << std::endl;
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeadlessSpecRunner::printResult(const QString &result)
|
|
||||||
{
|
|
||||||
red();
|
|
||||||
std::cout << " " << qPrintable(result) << std::endl;
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeadlessSpecRunner::finishSuite(const QString &duration, const QString &total, const QString& failed)
|
|
||||||
{
|
|
||||||
std::cout << std::endl;
|
|
||||||
if (didFail) {
|
|
||||||
red();
|
|
||||||
std::cout << "FAIL: ";
|
|
||||||
} else {
|
|
||||||
green();
|
|
||||||
std::cout << "PASS";
|
|
||||||
|
|
||||||
if (hasErrors) {
|
|
||||||
std::cout << " with JS errors";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << ": ";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << qPrintable(total) << " tests, " << qPrintable(failed) << " failures, " << qPrintable(duration) << " secs.";
|
|
||||||
clear();
|
|
||||||
std::cout << std::endl;
|
|
||||||
|
|
||||||
if (!reportFilename.isEmpty()) {
|
|
||||||
QFile reportFH(reportFilename);
|
|
||||||
|
|
||||||
if (reportFH.open(QFile::WriteOnly)) {
|
|
||||||
QTextStream report(&reportFH);
|
|
||||||
report << qPrintable(total) << "/" << qPrintable(failed) << "/";
|
|
||||||
report << (usedConsole ? "T" : "F");
|
|
||||||
report << "/" << qPrintable(duration) << "\n";
|
|
||||||
|
|
||||||
QString failedSpec;
|
|
||||||
|
|
||||||
while (!failedSpecs.isEmpty()) {
|
|
||||||
failedSpec = failedSpecs.pop();
|
|
||||||
report << qPrintable(failedSpec) << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
reportFH.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isFinished = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeadlessSpecRunner::timerEvent(QTimerEvent *event)
|
|
||||||
{
|
|
||||||
++m_runs;
|
|
||||||
|
|
||||||
if (event->timerId() != m_ticker.timerId())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (hasErrors && m_runs > 2)
|
|
||||||
QApplication::instance()->exit(1);
|
|
||||||
|
|
||||||
if (isFinished) {
|
|
||||||
int exitCode = 0;
|
|
||||||
if (didFail || hasErrors) {
|
|
||||||
exitCode = 1;
|
|
||||||
} else {
|
|
||||||
if (usedConsole) {
|
|
||||||
exitCode = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool runAgain = true;
|
|
||||||
|
|
||||||
if (runnerFiles.count() == 0) {
|
|
||||||
runAgain = false;
|
|
||||||
} else {
|
|
||||||
if (exitCode == 1) {
|
|
||||||
runAgain = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (runAgain) {
|
|
||||||
isFinished = false;
|
|
||||||
loadSpec();
|
|
||||||
} else {
|
|
||||||
QApplication::instance()->exit(exitCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_runs > 30) {
|
|
||||||
std::cout << "WARNING: too many runs and the test is still not finished!" << std::endl;
|
|
||||||
QApplication::instance()->exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "specrunner.moc"
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
char *reporter = NULL;
|
char *reporter = NULL;
|
||||||
char showColors = false;
|
char showColors = false;
|
||||||
|
|
||||||
int c, index;
|
int c, index;
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, "cr:")) != -1) {
|
while ((c = getopt(argc, argv, "cr:")) != -1) {
|
||||||
switch(c) {
|
switch(c) {
|
||||||
case 'c':
|
case 'c':
|
||||||
showColors = true;
|
showColors = true;
|
||||||
break;
|
break;
|
||||||
case 'r':
|
case 'r':
|
||||||
reporter = optarg;
|
reporter = optarg;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (optind == argc) {
|
if (optind == argc) {
|
||||||
std::cerr << "Run Jasmine's SpecRunner headlessly" << std::endl << std::endl;
|
std::cerr << "Run Jasmine's SpecRunner headlessly" << std::endl << std::endl;
|
||||||
std::cerr << " specrunner [-c] [-r <report file>] specrunner.html ..." << std::endl;
|
std::cerr << " specrunner [-c] [-r <report file>] specrunner.html ..." << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
app.setApplicationName("jasmine-headless-webkit");
|
app.setApplicationName("jasmine-headless-webkit");
|
||||||
HeadlessSpecRunner runner;
|
HeadlessSpecRunner::Runner runner;
|
||||||
runner.setColors(showColors);
|
runner.setColors(showColors);
|
||||||
runner.reportFile(reporter);
|
runner.reportFile(reporter);
|
||||||
|
|
||||||
for (index = optind; index < argc; index++) {
|
for (index = optind; index < argc; index++) {
|
||||||
runner.addFile(QString::fromLocal8Bit(argv[index]));
|
runner.addFile(QString::fromLocal8Bit(argv[index]));
|
||||||
}
|
}
|
||||||
runner.go();
|
runner.go();
|
||||||
|
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
TEMPLATE = app
|
TEMPLATE = app
|
||||||
CONFIG -= app_bundle
|
CONFIG -= app_bundle
|
||||||
TARGET = jasmine-webkit-specrunner
|
TARGET = jasmine-webkit-specrunner
|
||||||
SOURCES = specrunner.cpp
|
SOURCES = HeadlessSpecRunner/Page.cpp HeadlessSpecRunner/Runner.cpp specrunner.cpp
|
||||||
|
HEADERS = HeadlessSpecRunner/Page.h HeadlessSpecRunner/Runner.h
|
||||||
QT += network webkit
|
QT += network webkit
|
||||||
QMAKE_INFO_PLIST = Info.plist
|
QMAKE_INFO_PLIST = Info.plist
|
||||||
QMAKESPEC = macx-gcc
|
QMAKESPEC = macx-gcc
|
||||||
|
Loading…
Reference in New Issue
Block a user