diff --git a/.gitignore b/.gitignore index 2c8800d..78852f2 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,3 @@ Makefile specrunner.moc specrunner.o ext/jasmine-webkit-specrunner/jasmine-webkit-specrunner -*.o -moc_*.* - diff --git a/CHANGELOG.md b/CHANGELOG.md index 21ab17a..7844fcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,7 @@ -## 0.6.1 - -* Ensure YAML is loaded before use -* Make sure specs can't break out of the page they're running in -* Fix compilation on FreeBSD - -## 0.6.0 +## 0.5.1 * File and line number information for failing specs * Try to build the runner if it's missing -* Kill warnings and streamline includes ## 0.5.0 diff --git a/Gemfile b/Gemfile index c16218b..5705358 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,4 @@ -source :rubygems +source "http://rubygems.org" # Specify your gem's dependencies in jasmine-headless-webkit.gemspec gemspec diff --git a/Guardfile b/Guardfile index f0c26ca..0db8fe8 100644 --- a/Guardfile +++ b/Guardfile @@ -4,7 +4,7 @@ # guard 'shell' do - watch(%r{ext/jasmine-webkit-specrunner/.*\.(cpp|h)}) { compile } + watch(%r{ext/jasmine-webkit-specrunner/specrunner.cpp}) { compile } end # A sample Guardfile # More info at https://github.com/guard/guard#readme diff --git a/Rakefile b/Rakefile index 02ec64b..47c0fb7 100644 --- a/Rakefile +++ b/Rakefile @@ -38,9 +38,3 @@ end task :default => [ 'spec:platforms', 'jasmine:headless' ] -desc "Build the runner" -task :build do - Dir.chdir 'ext/jasmine-headless-specrunner' do - system %{ruby extconf.rb} - end -end diff --git a/dev-bin/hooks/pre-commit b/dev-bin/hooks/pre-commit deleted file mode 100755 index 75c11e3..0000000 --- a/dev-bin/hooks/pre-commit +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -bundle exec rake -if [ $? -ne 0 ]; then exit 1; fi - diff --git a/dev-bin/install-hooks b/dev-bin/install-hooks deleted file mode 100755 index 5a7809c..0000000 --- a/dev-bin/install-hooks +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -for i in $PWD/dev-bin/hooks/*; do - ln -sf $i .git/hooks/${i##*/} -done - diff --git a/ext/jasmine-webkit-specrunner/HeadlessSpecRunner/ConsoleOutput.cpp b/ext/jasmine-webkit-specrunner/HeadlessSpecRunner/ConsoleOutput.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/ext/jasmine-webkit-specrunner/HeadlessSpecRunner/ConsoleOutput.h b/ext/jasmine-webkit-specrunner/HeadlessSpecRunner/ConsoleOutput.h deleted file mode 100644 index e69de29..0000000 diff --git a/ext/jasmine-webkit-specrunner/HeadlessSpecRunner/Page.cpp b/ext/jasmine-webkit-specrunner/HeadlessSpecRunner/Page.cpp deleted file mode 100644 index aab256e..0000000 --- a/ext/jasmine-webkit-specrunner/HeadlessSpecRunner/Page.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include - -#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; - } -} diff --git a/ext/jasmine-webkit-specrunner/HeadlessSpecRunner/Page.h b/ext/jasmine-webkit-specrunner/HeadlessSpecRunner/Page.h deleted file mode 100644 index 4c7e151..0000000 --- a/ext/jasmine-webkit-specrunner/HeadlessSpecRunner/Page.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef JHW_PAGE -#define JHW_PAGE - -#include -#include - -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 diff --git a/ext/jasmine-webkit-specrunner/HeadlessSpecRunner/Runner.cpp b/ext/jasmine-webkit-specrunner/HeadlessSpecRunner/Runner.cpp deleted file mode 100644 index 2ec04d1..0000000 --- a/ext/jasmine-webkit-specrunner/HeadlessSpecRunner/Runner.cpp +++ /dev/null @@ -1,266 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#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); - } - } -} - diff --git a/ext/jasmine-webkit-specrunner/HeadlessSpecRunner/Runner.h b/ext/jasmine-webkit-specrunner/HeadlessSpecRunner/Runner.h deleted file mode 100644 index 46d3bf7..0000000 --- a/ext/jasmine-webkit-specrunner/HeadlessSpecRunner/Runner.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef JHW_RUNNER -#define JHW_RUNNER - -#include -#include -#include -#include -#include -#include - -#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 runnerFiles; - QString reportFilename; - QStack failedSpecs; - - void red(); - void green(); - void yellow(); - void clear(); - void loadSpec(); - }; -} - -#endif diff --git a/ext/jasmine-webkit-specrunner/extconf.rb b/ext/jasmine-webkit-specrunner/extconf.rb index df888d3..cbc77ca 100644 --- a/ext/jasmine-webkit-specrunner/extconf.rb +++ b/ext/jasmine-webkit-specrunner/extconf.rb @@ -1,8 +1,8 @@ -require 'fileutils' - -$: << File.expand_path("../../../lib", __FILE__) - -require 'qt/qmake' - -Qt::Qmake.make!('jasmine-headless-webkit') +case RUBY_PLATFORM +when /linux/ + system %{qmake -spec linux-g++} +else + system %{qmake -spec macx-g++} +end +system %{make} diff --git a/ext/jasmine-webkit-specrunner/specrunner.cpp b/ext/jasmine-webkit-specrunner/specrunner.cpp index 4827ac9..2b3a00b 100644 --- a/ext/jasmine-webkit-specrunner/specrunner.cpp +++ b/ext/jasmine-webkit-specrunner/specrunner.cpp @@ -1,68 +1,395 @@ /* - Copyright (c) 2010 Sencha Inc. - Copyright (c) 2011 John Bintz + Copyright (c) 2010 Sencha Inc. + Copyright (c) 2011 John Bintz - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - 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 - THE SOFTWARE. - */ + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + 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 + THE SOFTWARE. + */ -#include "HeadlessSpecRunner/Page.h" -#include "HeadlessSpecRunner/Runner.h" +#include +#include +#include +#include +#include +#include #if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) #error Use Qt 4.7 or later version #endif -int main(int argc, char** argv) +class HeadlessSpecRunnerPage: public QWebPage { - char *reporter = NULL; - char showColors = false; + Q_OBJECT +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); +}; - int c, index; +void HeadlessSpecRunnerPage::javaScriptConsoleMessage(const QString &message, int lineNumber, const QString &sourceID) +{ + emit consoleLog(message, lineNumber, sourceID); +} - while ((c = getopt(argc, argv, "cr:")) != -1) { - switch(c) { - case 'c': - showColors = true; - break; - case 'r': - reporter = optarg; - break; +bool HeadlessSpecRunnerPage::javaScriptConfirm(QWebFrame *frame, const QString &msg) +{ + emit internalLog("TODO", "jasmine-headless-webkit can't handle confirm() yet! You should mock window.confirm for now. Returning true."); + return true; +} + +void HeadlessSpecRunnerPage::javaScriptAlert(QWebFrame *frame, const QString &msg) +{ + emit internalLog("alert", msg); +} + +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 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 runnerFiles; + QString reportFilename; + QStack 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::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(); } } - if (optind == argc) { - std::cerr << "Run Jasmine's SpecRunner headlessly" << std::endl << std::endl; - std::cerr << " specrunner [-c] [-r ] specrunner.html ..." << std::endl; - return 1; - } - - QApplication app(argc, argv); - app.setApplicationName("jasmine-headless-webkit"); - HeadlessSpecRunner::Runner runner; - runner.setColors(showColors); - runner.reportFile(reporter); - - for (index = optind; index < argc; index++) { - runner.addFile(QString::fromLocal8Bit(argv[index])); - } - runner.go(); - - return app.exec(); + 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) +{ + char *reporter = NULL; + char showColors = false; + + int c, index; + + while ((c = getopt(argc, argv, "cr:")) != -1) { + switch(c) { + case 'c': + showColors = true; + break; + case 'r': + reporter = optarg; + break; + } + } + + if (optind == argc) { + std::cerr << "Run Jasmine's SpecRunner headlessly" << std::endl << std::endl; + std::cerr << " specrunner [-c] [-r ] specrunner.html ..." << std::endl; + return 1; + } + + QApplication app(argc, argv); + HeadlessSpecRunner runner; + runner.setColors(showColors); + runner.reportFile(reporter); + + for (index = optind; index < argc; index++) { + runner.addFile(QString::fromLocal8Bit(argv[index])); + } + runner.go(); + + return app.exec(); +} + + diff --git a/ext/jasmine-webkit-specrunner/specrunner.pro b/ext/jasmine-webkit-specrunner/specrunner.pro index 43201b9..78cddcb 100644 --- a/ext/jasmine-webkit-specrunner/specrunner.pro +++ b/ext/jasmine-webkit-specrunner/specrunner.pro @@ -1,8 +1,7 @@ TEMPLATE = app CONFIG -= app_bundle TARGET = jasmine-webkit-specrunner -SOURCES = HeadlessSpecRunner/Page.cpp HeadlessSpecRunner/Runner.cpp specrunner.cpp -HEADERS = HeadlessSpecRunner/Page.h HeadlessSpecRunner/Runner.h +SOURCES = specrunner.cpp QT += network webkit QMAKE_INFO_PLIST = Info.plist QMAKESPEC = macx-gcc diff --git a/jasmine-headless-webkit.gemspec b/jasmine-headless-webkit.gemspec index 17aeb06..dc2cb46 100644 --- a/jasmine-headless-webkit.gemspec +++ b/jasmine-headless-webkit.gemspec @@ -20,7 +20,7 @@ Gem::Specification.new do |s| s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] - s.add_dependency 'jasmine-core', '~>1.1.beta' + s.add_dependency 'jasmine', '~>1.1.beta' s.add_dependency 'coffee-script', '>= 2.2' s.add_dependency 'rainbow' s.add_dependency 'multi_json' diff --git a/jasmine/jasmine.headless-reporter.coffee b/jasmine/jasmine.headless-reporter.coffee index 45ec549..447c102 100644 --- a/jasmine/jasmine.headless-reporter.coffee +++ b/jasmine/jasmine.headless-reporter.coffee @@ -13,7 +13,8 @@ class window.HeadlessReporterResult JHW.printName(output) for result in @results - JHW.printResult(result) + do (result) => + JHW.printResult(result) _findSpecLine: -> bestChoice = { accuracy: 0, file: null, lineNumber: null } @@ -49,15 +50,15 @@ jasmine.Spec.prototype.getSpecSplitName = -> parts class jasmine.HeadlessReporter - constructor: (@callback = null) -> + constructor: -> @results = [] @failedCount = 0 @length = 0 reportRunnerResults: (runner) -> for result in @results - result.print() + do (result) => + result.print() - this.callback() if @callback JHW.finishSuite((new Date() - @startTime) / 1000.0, @length, @failedCount) reportRunnerStarting: (runner) -> @startTime = new Date() @@ -71,8 +72,9 @@ class jasmine.HeadlessReporter @failedCount++ failureResult = new HeadlessReporterResult(spec.getFullName(), spec.getSpecSplitName()) for result in results.getItems() - if result.type == 'expect' and !result.passed_ - failureResult.addResult(result.message) + do (result) => + if result.type == 'expect' and !result.passed_ + failureResult.addResult(result.message) @results.push(failureResult) reportSpecStarting: (spec) -> reportSuiteResults: (suite) -> diff --git a/jasmine/jasmine.headless-reporter.js b/jasmine/jasmine.headless-reporter.js index b95b23c..d9b4eb5 100644 --- a/jasmine/jasmine.headless-reporter.js +++ b/jasmine/jasmine.headless-reporter.js @@ -1,4 +1,5 @@ (function() { + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; if (!(typeof jasmine !== "undefined" && jasmine !== null)) { throw new Error("jasmine not laoded!"); } @@ -23,7 +24,9 @@ _results = []; for (_i = 0, _len = _ref.length; _i < _len; _i++) { result = _ref[_i]; - _results.push(JHW.printResult(result)); + _results.push(__bind(function(result) { + return JHW.printResult(result); + }, this)(result)); } return _results; }; @@ -80,21 +83,20 @@ return parts; }; jasmine.HeadlessReporter = (function() { - function HeadlessReporter(callback) { - this.callback = callback != null ? callback : null; + function HeadlessReporter() { this.results = []; this.failedCount = 0; this.length = 0; } HeadlessReporter.prototype.reportRunnerResults = function(runner) { - var result, _i, _len, _ref; + var result, _fn, _i, _len, _ref; _ref = this.results; + _fn = __bind(function(result) { + return result.print(); + }, this); for (_i = 0, _len = _ref.length; _i < _len; _i++) { result = _ref[_i]; - result.print(); - } - if (this.callback) { - this.callback(); + _fn(result); } return JHW.finishSuite((new Date() - this.startTime) / 1000.0, this.length, this.failedCount); }; @@ -102,7 +104,7 @@ return this.startTime = new Date(); }; HeadlessReporter.prototype.reportSpecResults = function(spec) { - var failureResult, result, results, _i, _len, _ref; + var failureResult, result, results, _fn, _i, _len, _ref; results = spec.results(); this.length++; if (results.passed()) { @@ -112,11 +114,14 @@ this.failedCount++; failureResult = new HeadlessReporterResult(spec.getFullName(), spec.getSpecSplitName()); _ref = results.getItems(); + _fn = __bind(function(result) { + if (result.type === 'expect' && !result.passed_) { + return failureResult.addResult(result.message); + } + }, this); for (_i = 0, _len = _ref.length; _i < _len; _i++) { result = _ref[_i]; - if (result.type === 'expect' && !result.passed_) { - failureResult.addResult(result.message); - } + _fn(result); } return this.results.push(failureResult); } diff --git a/lib/jasmine-headless-webkit.rb b/lib/jasmine-headless-webkit.rb index d5b1fb7..1813185 100644 --- a/lib/jasmine-headless-webkit.rb +++ b/lib/jasmine-headless-webkit.rb @@ -5,5 +5,5 @@ module Jasmine end end -require 'jasmine/headless/railtie' if defined?(Rails) && Rails::VERSION::MAJOR >= 3 +require 'jasmine/headless/railtie' if defined?(Rails) diff --git a/lib/jasmine-headless-webkit/version.rb b/lib/jasmine-headless-webkit/version.rb index c551a15..6adea70 100644 --- a/lib/jasmine-headless-webkit/version.rb +++ b/lib/jasmine-headless-webkit/version.rb @@ -1,7 +1,7 @@ module Jasmine module Headless module Webkit - VERSION = "0.6.2" + VERSION = "0.5.1" end end end diff --git a/lib/jasmine/files_list.rb b/lib/jasmine/files_list.rb index b1313f4..9f0c8d3 100644 --- a/lib/jasmine/files_list.rb +++ b/lib/jasmine/files_list.rb @@ -1,4 +1,13 @@ -require 'jasmine-core' +begin + require 'jasmine' +rescue NameError => e + if e.message['ActiveSupport::Concern'] + $stderr.puts "[%s] %s (%s)" % [ "jasmine-gem".color(:red), e.message.color(:white), e.class.name.color(:yellow) ] + $stderr.puts "#{'Jasmine'.color(:red)} believes Rails 3 is available. Try using #{'Bundler'.color(:green)} and running via #{'bundle exec'.color(:green)}." + else + raise e + end +end module Jasmine class FilesList diff --git a/lib/jasmine/headless/runner.rb b/lib/jasmine/headless/runner.rb index e4757d7..a9d48a0 100644 --- a/lib/jasmine/headless/runner.rb +++ b/lib/jasmine/headless/runner.rb @@ -3,14 +3,13 @@ require 'jasmine/headless/options' require 'fileutils' +require 'jasmine/base' require 'coffee-script' require 'rainbow' require 'jasmine/files_list' require 'jasmine/template_writer' -require 'yaml' - module Jasmine module Headless class Runner diff --git a/lib/jasmine/headless/task.rb b/lib/jasmine/headless/task.rb index 113030e..a727378 100644 --- a/lib/jasmine/headless/task.rb +++ b/lib/jasmine/headless/task.rb @@ -42,7 +42,7 @@ module Jasmine private def create_rails_compliant_task - if Rails.respond_to?(:version) && Rails.version >= "3.1.0" + if Rails.version >= "3.1.0" desc 'Force generate static assets without an MD5 hash, all assets end with -test.' task 'assets:precompile:for_testing' => :environment do Rails.application.assets.digest_class = Digest::JasmineTest diff --git a/lib/jasmine/template_writer.rb b/lib/jasmine/template_writer.rb index 762dd7e..f88e699 100644 --- a/lib/jasmine/template_writer.rb +++ b/lib/jasmine/template_writer.rb @@ -24,6 +24,7 @@ module Jasmine + Jasmine Test Runner #{files.join("\n")} diff --git a/lib/qt/qmake.rb b/lib/qt/qmake.rb deleted file mode 100644 index e4315ed..0000000 --- a/lib/qt/qmake.rb +++ /dev/null @@ -1,141 +0,0 @@ -require 'rbconfig' -require 'rubygems/version' - -module Qt - class NotInstalledError < StandardError; end - - class Qmake - class << self - QMAKES = %w{qmake-qt4 qmake} - - def installed? - path != nil - end - - def make_installed? - make_path != nil - end - - def command - case platform - when :linux - "#{path} -spec linux-g++" - when :freebsd - "#{path} -spec freebsd-g++" - when :mac_os_x - "#{path} -spec macx-g++" - end - end - - def make!(name) - @name = name - - check_make! - check_qmake! - - system command - system %{make} - end - - # - # We need integration tests for these! - # - def path - @path ||= best_qmake - end - - def make_path - get_exe_path('gmake') || get_exe_path('make') - end - - def platform - case RbConfig::CONFIG['host_os'] - when /linux/ - :linux - when /freebsd/i - :freebsd - when /darwin/ - :mac_os_x - end - end - - def qt_version_of(qmake_path) - Gem::Version.new(%x{#{qmake_path} -v}.lines.to_a[1][%r{Using Qt version ([^ ]+) },1]) - end - - def best_qmake - if qmake_path = QMAKES.collect do |path| - result = nil - if qmake_path = get_exe_path(path) - if (qt_version = qt_version_of(qmake_path)) >= Gem::Version.create('4.7') - result = [ qmake_path, qt_version ] - end - end - result - end.compact.sort { |a, b| b.last <=> a.last }.first - qmake_path.first - else - nil - end - end - - private - def get_exe_path(command) - path = %x{which #{command}}.strip - path = nil if path == '' - path - end - - def check_make! - if !make_installed? - install_method = ( - case platform - when :linux - %{sudo apt-get install make or sudo yum install make} - when :freebsd - %{install /usr/ports/devel/gmake} - when :darwin - %{Install XCode, and/or sudo port install make} - end - ) - - $stderr.puts <<-MSG -make is not installed. You'll need to install it to build #{@name}. -#{install_method} should do it for you. -MSG - raise NotInstalledError - end - end - - def check_qmake! - if !installed? - install_method = ( - case platform - when :linux - <<-MSG -sudo apt-get install libqt4-dev qt4-qmake on Debian-based systems, or downloading -Nokia's prebuilt binary at http://qt.nokia.com/downloads/ -MSG - when :freebsd - <<-MSG -Install /usr/ports/www/qt4-webkit and /usr/ports/devel/qmake4. -MSG - when :darwin - <<-MSG -sudo port install qt4-mac (for the patient) or downloading Nokia's pre-built binary -at http://qt.nokia.com/downloads/ -MSG - end - ).strip - - $stderr.puts <<-MSG -qmake is not installed or is not the right version (#{@name} needs Qt 4.7 or above). -You'll need to install it to build #{@name}. -#{install_method} should do it for you. -MSG - end - end - end - end -end - diff --git a/spec/bin/jasmine-headless-webkit_spec.rb b/spec/bin/jasmine-headless-webkit_spec.rb index ebe627d..cee4707 100644 --- a/spec/bin/jasmine-headless-webkit_spec.rb +++ b/spec/bin/jasmine-headless-webkit_spec.rb @@ -60,24 +60,6 @@ describe "jasmine-headless-webkit" do end end - describe 'tries to leave page' do - it "should not leave the page nor loop" do - system %{bin/jasmine-headless-webkit -j spec/jasmine/leave_page/leave_page.yml --report #{report}} - $?.exitstatus.should == 1 - - report.should be_a_report_containing(2, 0, false) - end - end - - describe 'tries to click a button' do - it "should not leave the page nor loop" do - system %{bin/jasmine-headless-webkit -j spec/jasmine/click_button/click_button.yml --report #{report}} - $?.exitstatus.should == 0 - - report.should be_a_report_containing(0, 0, false) - end - end - describe 'with filtered run' do context "don't run a full run, just the filtered run" do it "should succeed and run both" do diff --git a/spec/jasmine/click_button/click_button.js b/spec/jasmine/click_button/click_button.js deleted file mode 100644 index 628dae2..0000000 --- a/spec/jasmine/click_button/click_button.js +++ /dev/null @@ -1,5 +0,0 @@ -function yes() { - $('body').append('