Compare commits

...

No commits in common. "nicoder/patch-1" and "master" have entirely different histories.

224 changed files with 6748 additions and 1018 deletions

24
.gitignore vendored
View File

@ -1,11 +1,19 @@
.sass-cache/ *.gem
_site/ .bundle
.idea/* Gemfile.lock
pkg/*
Makefile
specrunner.moc
specrunner.o
ext/jasmine-webkit-specrunner/jasmine-webkit-specrunner
*.o
moc_*.*
.DS_Store
hydra-runner.log
jhw-test
.jhw-cache/ .jhw-cache/
ext/ _site/
pkg/ jhw.*.html
coverage/ coverage/
tmp/ tmp/
vendor/ cache dir/
.DS_Store

1
.rspec Normal file
View File

@ -0,0 +1 @@
-c

89
CHANGELOG.md Normal file
View File

@ -0,0 +1,89 @@
## 0.7.2
* Improved finding of CoffeeScript spec line locations
* Improved Runner reporting of which expectations failed
* Initial vendored helper support, paving the way for Sprockets integratioon (maybe!)
* Add 1.9.3-rc1 test support and fixes
* Add console.peek()
## 0.7.1
* Bugfix for missing digest/sha1 import
## 0.7.0
* Major C++ cleanup, now much more modular
* Greatly improved object inspection and printing provided by jsDump and beautify-js
## 0.6.3
* Ensure Rubygems is available before doing version comparison
* Fix other build problems
* Better output for jQuery nodes
## 0.6.2
* Clean up C++ and test running
## 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
* 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
* Run all tests after focused run if `console.log` was used
* Ensure Rake task works outside of Rails
* Ensure focused tests aren't run when CLI called with no files
* Support globs in focused test filters
* Raise exceptions on Rake task failures
* Update to use Jasmine 1.1
## 0.4.2
* Fix Rails 3.1 Railtie so it's included properly
* Fix compilation of runner on Linux
* Run files that are outside of the project's scope
## 0.4.1
* Fix CoffeeScript concatenation bug
* Report CoffeeScript errors better
## 0.4.0
* Change how tests are counted for totals
* Run targeted and full tests in the same runner instance for speed!
* Concatenate adjacent CoffeeScript files before compilation for more speed!
* Ensure files are not required twice
* Break out runner usage from CLI so that it can be resued in Rake tasks and elsewhere
* Add a Rails 3.1 task to precompile all assets with a specific "MD5 hash"
## 0.2.3
* Better messages for JavaScript errors
* `console.pp` added for more in-depth object inspection
## 0.2.2
* Write out a reporting file that can be used for Guard notification
## 0.2.1
* Avoid a Railtie so JHW works outside of Rails
## 0.2.0
* Add a Rake task and a default task for Rails
* Global runner configuration via ~/.jasmine-headless-webkit
* Custom Jasmine reporter for better user feedback
* Specify the specs to be run, instead of always running them all
* Move README to gh-pages site

33
Gemfile
View File

@ -1,3 +1,30 @@
gem 'jekyll' source :rubygems
gem 'liquid', '2.2.2'
gem 'redcarpet' # Specify your gem's dependencies in jasmine-headless-webkit.gemspec
gemspec
gem 'rspec'
gem 'fakefs', :require => nil
gem 'guard'
gem 'guard-rspec'
gem 'guard-shell'
gem 'guard-coffeescript'
gem 'guard-cucumber'
require 'rbconfig'
case RbConfig::CONFIG['host_os']
when /darwin/
when /linux/
gem 'libnotify'
end
gem 'mocha'
gem 'cucumber'
gem 'jquery-rails', '~> 1.0.0'
gem 'ejs'
gem 'guard-jasmine-headless-webkit', :git => 'git://github.com/johnbintz/guard-jasmine-headless-webkit.git'

View File

@ -1,30 +0,0 @@
GEM
specs:
albino (1.3.3)
posix-spawn (>= 0.3.6)
classifier (1.3.3)
fast-stemmer (>= 1.0.0)
directory_watcher (1.4.1)
fast-stemmer (1.0.0)
jekyll (0.11.0)
albino (>= 1.3.2)
classifier (>= 1.3.1)
directory_watcher (>= 1.1.1)
kramdown (>= 0.13.2)
liquid (>= 1.9.0)
maruku (>= 0.5.9)
kramdown (0.13.3)
liquid (2.2.2)
maruku (0.6.0)
syntax (>= 1.0.0)
posix-spawn (0.3.6)
redcarpet (1.17.2)
syntax (1.0.0)
PLATFORMS
ruby
DEPENDENCIES
jekyll
liquid (= 2.2.2)
redcarpet

View File

@ -1,6 +1,41 @@
guard 'livereload' do
watch('index.md') # Add files and commands to this file, like the example:
watch(%r{^images/}) # watch('file/path') { `command(s)` }
watch(%r{^stylesheets/}) #
guard 'coffeescript', :input => 'vendor/assets/coffeescripts', :output => 'vendor/assets/javascripts'
guard 'shell' do
watch(%r{ext/jasmine-webkit-specrunner/.*\.(cpp|h|pro|pri)}) { |m|
if !m[0]['moc_']
compile
end
}
end
# A sample Guardfile
# More info at https://github.com/guard/guard#readme
guard 'rspec', :version => 2, :all_on_start => false do
watch(%r{^spec/.+_spec\.rb})
watch(%r{^lib/(.+)\.rb}) { |m| "spec/lib/#{m[1]}_spec.rb" }
watch(%r{^bin/(.+)}) { |m| "spec/bin/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { "spec" }
end end
guard 'cucumber', :cli => '-r features --format pretty' do
watch(%r{^features/.+\.feature$})
watch(%r{^features/support/.+$}) { 'features' }
watch(%r{^features/steps/(.+)_steps\.rb$}) { 'features' }
end
guard 'jasmine-headless-webkit', :all_on_start => false do
watch(%r{^spec/javascripts/.+_spec\.coffee})
watch(%r{^jasmine/(.+)\.coffee$}) { |m| "spec/javascripts/#{m[1]}_spec.coffee" }
end
def compile
system %{cd ext/jasmine-webkit-specrunner && ruby extconf.rb}
end
compile

46
README.md Normal file
View File

@ -0,0 +1,46 @@
_This project is dead. You should use [Karma](http://karma-runner.github.io/) instead. I do._
# Jasmine Headless WebKit runner
Run your specs at sonic boom speed! No pesky reload button or page rendering slowdowns!
http://johnbintz.github.com/jasmine-headless-webkit/ has the most up-to-date information on using
this project. You can see the source of that site on the gh-pages branch.
## For those who want to hack on the project...
The best way to get everything running that you need for development and testing is
to use Guard:
``` bash
bundle install
bundle exec guard
... build Qt runner ...
... compile CoffeeScript to JS ...
... run RSpec and JHW ...
```
## License
* Copyright (c) 2011 John Bintz
* Original Qt WebKit runner Copyright (c) 2010 Sencha Inc.
* Jasmine JavaScript library Copyright (c) 2008-2011 Pivotal Labs
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 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.

65
Rakefile Normal file
View File

@ -0,0 +1,65 @@
include Rake::DSL if defined?(Rake::DSL)
require 'bundler'
Bundler::GemHelper.install_tasks
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec)
$: << File.expand_path('../lib', __FILE__)
require 'jasmine-headless-webkit'
require 'jasmine/headless/task'
Jasmine::Headless::Task.new
PLATFORMS = %w{1.9.2 1.9.3}
def rvm_bundle(command = '')
Bundler.with_clean_env do
system %{bash -c 'unset BUNDLE_BIN_PATH && unset BUNDLE_GEMFILE && rvm #{PLATFORMS.join(',')} do bundle #{command}'}
end
end
class SpecFailure < StandardError; end
class BundleFailure < StandardError; end
namespace :spec do
desc "Run on three Rubies"
task :platforms do
rvm_bundle
rvm_bundle "exec rspec spec"
rvm_bundle "exec cucumber"
raise SpecError.new if $?.exitstatus != 0
end
end
task :default => [ 'spec:platforms', 'jasmine:headless' ]
desc "Build the runner"
task :build_runner do
Dir.chdir 'ext/jasmine-webkit-specrunner' do
system %{ruby extconf.rb}
end
end
desc "Generate vendored JS"
task :generate_js do
require 'sprockets'
source = 'vendor/assets/coffeescripts'
target = 'vendor/assets/javascripts'
env = Sprockets::Environment.new { |s| s.append_path 'vendor/assets/coffeescripts' }
Dir[File.join(File.expand_path(source), '*.coffee')].each do |file|
file_target = file.gsub(source, target).gsub('.coffee', '.js')
puts "#{file} => #{file_target}"
File.open(file_target, 'wb') do |fh|
fh.print env.find_asset(File.expand_path(file)).to_s
end
end
end

View File

@ -1,4 +0,0 @@
markdown: redcarpet
auto: true
server: true
pygments: true

View File

@ -1,16 +0,0 @@
<html>
<head>
<title>{{ page.title }}</title>
<link href='http://fonts.googleapis.com/css?family=Neuton' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Crimson+Text' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="stylesheets/screen.css" type="text/css" />
</head>
<body>
<div id="container">
<div id="inner-container">
{{ content }}
</div>
</div>
</body>
</html>

11
bin/jasmine-headless-webkit Executable file
View File

@ -0,0 +1,11 @@
#!/usr/bin/env ruby
require 'rubygems'
$: << File.expand_path('../../lib', __FILE__)
require 'jasmine-headless-webkit'
require 'coffee-script'
Jasmine::Headless::CommandLine.run!

View File

@ -1,26 +0,0 @@
require 'susy'
# Require any additional compass plugins here.
# Set this to the root of your project when deployed:
http_path = "/"
css_dir = "stylesheets"
sass_dir = "sass"
images_dir = "images"
javascripts_dir = "javascripts"
# You can select your preferred output style here (can be overridden via the command line):
# output_style = :expanded or :nested or :compact or :compressed
# To enable relative paths to assets via compass helper functions. Uncomment:
# relative_assets = true
# To disable debugging comments that display the original location of your selectors. Uncomment:
# line_comments = false
# If you prefer the indented syntax, you might want to regenerate this
# project again passing --syntax sass, or you can uncomment this:
# preferred_syntax = :sass
# and then run:
# sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass

2
config/cucumber.yml Normal file
View File

@ -0,0 +1,2 @@
default: -r features

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
<plist version="0.9">
<dict>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleGetInfoString</key>
<string>Created by Qt/QMake</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleExecutable</key>
<string>specrunner</string>
<key>CFBundleIdentifier</key>
<string>com.yourcompany.specrunner</string>
<key>NOTE</key>
<string>This file was generated by Qt/QMake.</string>
<key>LSUIElement</key>
<string>1</string>
</dict>
</plist>

View File

@ -0,0 +1,5 @@
build:
true
install:
true

View File

@ -0,0 +1,15 @@
#include <QtGui>
#include <QtWebKit>
#include <iostream>
#include "Page.h"
Page::Page() : QWebPage() {}
void Page::javaScriptConsoleMessage(const QString & message, int lineNumber, const QString & sourceID) {
emit handleError(message, lineNumber, sourceID);
}
void Page::javaScriptAlert(QWebFrame *, const QString &) {}
bool Page::javaScriptConfirm(QWebFrame *, const QString &) { return false; }
bool Page::javaScriptPrompt(QWebFrame *, const QString &, const QString &, QString *) { return false; }

View File

@ -0,0 +1,20 @@
#ifndef JHW_PAGE
#define JHW_PAGE
#include <QtGui>
#include <QtWebKit>
class Page: public QWebPage {
Q_OBJECT
public:
Page();
protected:
void javaScriptConsoleMessage(const QString & message, int lineNumber, const QString & sourceID);
void javaScriptAlert(QWebFrame *, const QString &);
bool javaScriptConfirm(QWebFrame *, const QString &);
bool javaScriptPrompt(QWebFrame *, const QString &, const QString &, QString *);
signals:
void handleError(const QString & message, int lineNumber, const QString & sourceID);
};
#endif

View File

@ -0,0 +1,208 @@
#include <QtGui>
#include <QtWebKit>
#include <QFile>
#include <QTextStream>
#include <iostream>
#include <sstream>
#include <QQueue>
#include "Runner.h"
#include "Page.h"
using namespace std;
Runner::Runner() : QObject()
, runs(0)
, hasErrors(false)
, _hasSpecFailure(false)
, usedConsole(false)
, isFinished(false)
, useColors(false)
, quiet(false)
{
page.settings()->enablePersistentStorage();
ticker.setInterval(TIMER_TICK);
connect(&ticker, SIGNAL(timeout()), this, SLOT(timerEvent()));
connect(&page, SIGNAL(loadFinished(bool)), this, SLOT(watch(bool)));
connect(&page, SIGNAL(handleError(const QString &, int, const QString &)), this, SLOT(handleError(const QString &, int, const QString &)));
connect(page.mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(addJHW()));
}
void Runner::addFile(const QString &spec) {
runnerFiles.enqueue(spec);
}
void Runner::go() {
ticker.stop();
page.setPreferredContentsSize(QSize(1024, 600));
addJHW();
loadSpec();
}
void Runner::addJHW() {
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;");
page.mainFrame()->evaluateJavaScript(command);
hasErrors = true;
}
void Runner::loadSpec()
{
QVectorIterator<QString> iterator(reportFiles);
while (iterator.hasNext()) {
QFile *outputFile = new QFile(iterator.next());
outputFile->open(QIODevice::WriteOnly);
outputFiles.enqueue(outputFile);
}
QString runnerFile = runnerFiles.dequeue();
page.mainFrame()->load(runnerFile);
ticker.start();
}
void Runner::watch(bool ok) {
if (!ok) {
std::cerr << "Can't load " << qPrintable(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;
}
page.mainFrame()->evaluateJavaScript(QString("JHW._setColors(") + (useColors ? QString("true") : QString("false")) + QString("); false;"));
}
void Runner::setColors(bool colors) {
useColors = colors;
}
void Runner::hasUsedConsole() {
usedConsole = true;
}
void Runner::hasError() {
hasErrors = true;
}
void Runner::hasSpecFailure() {
_hasSpecFailure = true;
}
void Runner::setReportFiles(QStack<QString> &files) {
reportFiles = files;
}
void Runner::timerPause() {
ticker.stop();
}
void Runner::timerDone() {
ticker.start();
}
void Runner::ping() {
runs = 0;
}
void Runner::setSeed(QString s) {
seed = s;
}
void Runner::setQuiet(bool q) {
quiet = q;
}
QString Runner::getSeed() {
return seed;
}
bool Runner::isQuiet() {
return quiet;
}
void Runner::print(const QString &fh, const QString &content) {
if (fh == "stdout") {
std::cout << qPrintable(content);
std::cout.flush();
}
if (fh == "stderr") {
std::cerr << qPrintable(content);
std::cerr.flush();
}
if (fh.contains("report")) {
int index = (int)fh.split(":").last().toUInt();
QTextStream ts(outputFiles.at(index));
ts << qPrintable(content);
ts.flush();
}
}
void Runner::finishSuite() {
isFinished = true;
runs = 0;
}
void Runner::timerEvent() {
++runs;
if (hasErrors && runs > 2)
QApplication::instance()->exit(1);
if (isFinished && runs > 2) {
while (!outputFiles.isEmpty()) {
outputFiles.dequeue()->close();
}
int exitCode = 0;
if (_hasSpecFailure || 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 (runs > MAX_LOOPS) {
std::cerr << "WARNING: too many runs and the test is still not finished!" << std::endl;
QApplication::instance()->exit(1);
}
}

View File

@ -0,0 +1,73 @@
#ifndef JHW_RUNNER
#define JHW_RUNNER
#include <QtGui>
#include <QtWebKit>
#include <QFile>
#include <QTextStream>
#include <iostream>
#include <fstream>
#include <sstream>
#include <QQueue>
#include <QApplication>
#include "Page.h"
using namespace std;
class Runner: public QObject {
Q_OBJECT
public:
enum { TIMER_TICK = 200, MAX_LOOPS = 50 };
Runner();
void setColors(bool colors);
void setReportFiles(QStack<QString> &files);
void setSeed(QString s);
void setQuiet(bool q);
void addFile(const QString &spec);
void go();
public slots:
void timerPause();
void timerDone();
void hasUsedConsole();
void hasError();
void hasSpecFailure();
bool isQuiet();
QString getSeed();
void print(const QString &fh, const QString &content);
void finishSuite();
void ping();
private slots:
void watch(bool ok);
void addJHW();
void timerEvent();
void handleError(const QString & message, int lineNumber, const QString & sourceID);
private:
Page page;
QTimer ticker;
int runs;
bool hasErrors;
bool _hasSpecFailure;
bool usedConsole;
bool isFinished;
bool useColors;
bool quiet;
QString seed;
QQueue<QString> runnerFiles;
QStack<QString> reportFiles;
void loadSpec();
QQueue<QFile *> outputFiles;
};
#endif

View File

@ -0,0 +1,8 @@
TEMPLATE = app
CONFIG -= app_bundle
QMAKE_INFO_PLIST = Info.plist
QT += network webkit
SOURCES = Page.cpp Runner.cpp
HEADERS = Page.h Runner.h

View File

@ -0,0 +1,10 @@
require 'fileutils'
$: << File.expand_path("../../../lib", __FILE__)
require 'qt/qmake'
system %{make clean}
Qt::Qmake.make!('jasmine-headless-webkit', 'specrunner.pro')
FileUtils.cp File.expand_path('../Makefile.dummy', __FILE__), File.expand_path('../Makefile', __FILE__)

View File

@ -0,0 +1,83 @@
/*
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:
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.
*/
#include "Runner.h"
#if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)
#include <getopt.h>
#endif
#if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
#error Use Qt 4.7 or later version
#endif
int main(int argc, char** argv)
{
bool showColors = false;
bool isQuiet = false;
QString seed;
QStack<QString> reporterFiles;
int c, index;
while ((c = getopt(argc, argv, "cr:s:q")) != -1) {
switch(c) {
case 'c':
showColors = true;
break;
case 'q':
isQuiet = true;
break;
case 'r':
reporterFiles.push(QString(optarg));
break;
case 's':
seed = QString(optarg);
break;
}
}
if (optind == argc) {
std::cerr << "Run Jasmine's SpecRunner headlessly" << std::endl << std::endl;
std::cerr << " specrunner [-c] [-s seed] [-r report file ...] specrunner.html ..." << std::endl;
return 1;
}
QApplication app(argc, argv);
app.setApplicationName("jasmine-headless-webkit");
Runner runner;
runner.setColors(showColors);
runner.setQuiet(isQuiet);
runner.setReportFiles(reporterFiles);
runner.setSeed(seed);
for (index = optind; index < argc; index++) {
runner.addFile(QString::fromLocal8Bit(argv[index]));
}
runner.go();
return app.exec();
}

View File

@ -0,0 +1,5 @@
include(common.pri)
SOURCES += specrunner.cpp
TARGET = jasmine-webkit-specrunner

View File

@ -0,0 +1,7 @@
Feature: Bin - Failure
Scenario: Run a failing test
Given there is no existing "spec/report.txt" file
When I run `bin/jasmine-headless-webkit -j spec/jasmine/failure/failure.yml -f File:spec/report.txt`
Then the exit status should be 1
And the report file "spec/report.txt" should have 1 total, 1 failure, no console usage

View File

@ -0,0 +1,7 @@
Feature: Bin - Files
Scenario: List the files a test suite will use
Given I have a test suite
When I run `bin/jasmine-headless-webkit -j spec/jasmine/success/success.yml -l`
Then the exit status should be 0
And the output should include "spec/jasmine/success/success.js"
And the output should include "spec/jasmine/success/success_spec.js"

View File

@ -0,0 +1,18 @@
Feature: Bin - Filtered Run - Both Runs
Background:
Given there is no existing "spec/report.txt" file
Scenario: Run one and fail
When I run `bin/jasmine-headless-webkit -j spec/jasmine/filtered_failure/filtered_failure.yml -f File:spec/report.txt ./spec/jasmine/filtered_failure/failure_spec.js`
Then the exit status should be 1
And the report file "spec/report.txt" should have 1 total, 1 failure, no console usage
Scenario: Run both and succeed
When I run `bin/jasmine-headless-webkit -j spec/jasmine/filtered_success/filtered_success.yml -f File:spec/report.txt ./spec/jasmine/filtered_success/success_one_spec.js`
Then the exit status should be 0
And the report file "spec/report.txt" should have 2 total, 0 failures, no console usage
Scenario: Run both with console.log
When I run `bin/jasmine-headless-webkit -j spec/jasmine/filtered_success_with_console/filtered_success.yml -f File:spec/report.txt ./spec/jasmine/filtered_success_with_console/success_one_spec.js`
Then the exit status should be 2
And the report file "spec/report.txt" should have 2 total, 0 failures, yes console usage

View File

@ -0,0 +1,14 @@
Feature: Bin - No Full Run
Background:
Given there is no existing "spec/report.txt" file
Scenario: Only run the filtered run
When I run `bin/jasmine-headless-webkit -j spec/jasmine/filtered_success/filtered_success.yml -f File:spec/report.txt --no-full-run ./spec/jasmine/filtered_success/success_one_spec.js`
Then the exit status should be 0
And the report file "spec/report.txt" should have 1 total, 0 failure, no console usage
Scenario: Use a file outside of the normal test run
When I run `bin/jasmine-headless-webkit -j spec/jasmine/filtered_success/filtered_success.yml -f File:spec/report.txt ./spec/jasmine/filtered_success/success_other_file.js`
Then the exit status should be 0
And the report file "spec/report.txt" should have 1 total, 0 failure, no console usage

View File

@ -0,0 +1,7 @@
Feature: Bin - Help
Scenario: Display the Help
Given I have a test suite
When I run `bin/jasmine-headless-webkit -h`
Then I should get help output
And the exit status should be 0

View File

@ -0,0 +1,8 @@
Feature: Bin - Quiet Messages
Scenario: Run a test that would cause a lot of messages to be displayed and silence them all
Given I have a test suite
When I run `bin/jasmine-headless-webkit -q -j spec/jasmine/noisy/noisy.yml`
Then the exit status should be 0
And the output should not include "[Skipping File]"
And the output should not include "You should mock"

View File

@ -0,0 +1,8 @@
Feature: Bin - Runner Out
Scenario: Write out the runner to a specified file
Given I have a test suite
When I run `bin/jasmine-headless-webkit -j spec/jasmine/success/success.yml --runner-out spec/runner.html`
Then the exit status should be 0
And the file "spec/runner.html" should contain a JHW runner
When I delete the file "spec/runner.html"

View File

@ -0,0 +1,7 @@
Feature: Bin - Two spec files with same basename
Scenario: Run both files
Given there is no existing "spec/report.txt" file
When I run `bin/jasmine-headless-webkit -j spec/jasmine/two_spec_files_same_basename/jasmine.yml -f File:spec/report.txt`
Then the exit status should be 0
And the report file "spec/report.txt" should have 2 total, 0 failures, no console usage

View File

@ -0,0 +1,20 @@
Feature: Bin - Success
Scenario: Run a successful test with long format definition
Given there is no existing "spec/report.txt" file
When I run `bin/jasmine-headless-webkit --seed 1234 -j spec/jasmine/success/success.yml --format File --out spec/report.txt`
Then the exit status should be 0
And the report file "spec/report.txt" should have 1 total, 0 failures, no console usage
And the report file "spec/report.txt" should have seed 1234
Scenario: Run a successful test with legacy file reporting
Given there is no existing "spec/report.txt" file
When I run `bin/jasmine-headless-webkit -j spec/jasmine/success/success.yml --report spec/report.txt`
Then the exit status should be 0
And the report file "spec/report.txt" should have 1 total, 0 failures, no console usage
Scenario: Run a successful test with shortened format definition
Given there is no existing "spec/report.txt" file
When I run `bin/jasmine-headless-webkit -j spec/jasmine/success/success.yml -f File:spec/report.txt`
Then the exit status should be 0
And the report file "spec/report.txt" should have 1 total, 0 failures, no console usage

View File

@ -0,0 +1,5 @@
Feature: Bin - Success with JS Error
Scenario: Succeed
Given there is no existing "spec/report.txt" file
When I run `bin/jasmine-headless-webkit -j spec/jasmine/success_with_error/success_with_error.yml -f File:spec/report.txt`
Then the exit status should be 1

View File

@ -0,0 +1,7 @@
Feature: Bin - Try to Leave Page
Scenario: Fail on trying to leave the page
Given there is no existing "spec/report.txt" file
When I run `bin/jasmine-headless-webkit -j spec/jasmine/leave_page/leave_page.yml -f File:spec/report.txt`
Then the exit status should be 1
And the report file "spec/report.txt" should exist

View File

@ -0,0 +1,7 @@
Feature: Bin - Try to Click A Button
Scenario: Don't leave page when clicking a button
Given there is no existing "spec/report.txt" file
When I run `bin/jasmine-headless-webkit -j spec/jasmine/click_button/click_button.yml -f File:spec/report.txt`
Then the exit status should be 0
And the report file "spec/report.txt" should have 0 total, 0 failures, no console usage

View File

@ -0,0 +1,11 @@
Feature: Two files from source files
Scenario: Files are ordered directly
Given I have a test suite
When I run `bin/jasmine-headless-webkit -j spec/jasmine/two_files_from_src_files/jasmine.yml -l`
Then the exit status should be 0
And the following files should be loaded in order:
| vendor/vendor-file.js |
| vendor/vendor.js |
| app/app-file.js |
| app/app.js |

View File

@ -0,0 +1,7 @@
Feature: Bin - With CoffeeScript error
Scenario: Fail on CoffeeScript error
Given there is no existing "spec/report.txt" file
When I run `bin/jasmine-headless-webkit -j spec/jasmine/coffeescript_error/coffeescript_error.yml -f File:spec/report.txt`
Then the exit status should be 1
And the report file "spec/report.txt" should not exist

View File

@ -0,0 +1,7 @@
Feature: Use console.log
Scenario: Run a successful test that uses console.log
Given there is no existing "spec/report.txt" file
When I run `bin/jasmine-headless-webkit -j spec/jasmine/console_log/console_log.yml -f File:spec/report.txt`
Then the exit status should be 2
And the report file "spec/report.txt" should have 1 total, 0 failures, yes console usage

View File

@ -0,0 +1,7 @@
Feature: Bin - With Server
Scenario: Run using an HTTP server
Given there is no existing "spec/report.txt" file
When I run `bin/jasmine-headless-webkit --use-server -j spec/jasmine/success/success.yml -f File:spec/report.txt`
Then the exit status should be 0
And the report file "spec/report.txt" should have 1 total, 0 failures, no console usage

View File

@ -0,0 +1,7 @@
Feature: Bin - With window.prompt()
Scenario: Alert the user that window.prompt() needs to be stubbed
Given I have a test suite
When I run `bin/jasmine-headless-webkit -j spec/jasmine/window_prompt/window_prompt.yml`
Then the exit status should be 0
And the output should include "You should mock window.prompt"

View File

@ -0,0 +1,24 @@
Feature: Reporters
In order to allow for multiple types of output
I should be able to
Manage reporters and decide which ones to use
Scenario: Use default reporters
Given I have the default runner options
When I get a runner
And I get a template writer
Then the template should use the "Console" reporter to "stdout"
And the command to run the runner should not include a report file
Scenario: Use a file reporter
Given I have the default runner options
And I have the following reporters:
| Name | File |
| Console | |
| File | file |
When I get a runner
And I get a template writer
Then the template should use the "Console" reporter to "stdout"
And the template should use the "File" reporter to "report:0"
And the command to run the runner should include the report file "file"

34
features/runner.feature Normal file
View File

@ -0,0 +1,34 @@
Feature: Using the Runner directly
Scenario: Succeed
Given I have the following runner options:
"""
:jasmine_config: spec/jasmine/success/success.yml
:reporters:
- [ 'File', 'spec/report.txt' ]
"""
When I get a runner
And I run the runner
Then the runner should have an exit status of 0
And the report file "spec/report.txt" should have 1 total, 0 failures, no console usage
Scenario: JavaScript Error
Given I have the following runner options:
"""
:jasmine_config: spec/jasmine/success_with_error/success_with_error.yml
"""
When I get a runner
And I run the runner
Then the runner should have an exit status of 1
Scenario: Failure
Given I have the following runner options:
"""
:jasmine_config: spec/jasmine/failure/failure.yml
:reporters:
- [ 'File', 'spec/report.txt' ]
"""
When I get a runner
And I run the runner
Then the runner should have an exit status of 1
And the report file "spec/report.txt" should have 1 total, 1 failure, no console usage

View File

@ -0,0 +1,2 @@
Given /^I have a test suite$/ do
end

View File

@ -0,0 +1,4 @@
Given /^there is no existing "([^"]*)" file$/ do |file|
FileUtils.rm_rf file
end

View File

@ -0,0 +1,4 @@
Given /^I have the default runner options$/ do
@options = Jasmine::Headless::Options.new
end

View File

@ -0,0 +1,10 @@
Given /^I have the following reporters:$/ do |table|
@options[:reporters] = []
table.hashes.each do |hash|
reporter = [ hash['Name'] ]
reporter << hash['File'] if !hash['File'].empty?
@options[:reporters] << reporter
end
end

View File

@ -0,0 +1,3 @@
Given /^I have the following runner options:$/ do |string|
@options = YAML.load(string)
end

View File

@ -0,0 +1,3 @@
Then /^the exit status should be (\d+)$/ do |exitstatus|
$?.exitstatus.should == exitstatus.to_i
end

View File

@ -0,0 +1,4 @@
Then /^the file "([^"]*)" should contain a JHW runner$/ do |file|
File.read(file).should include('jasmine.HeadlessReporter')
end

View File

@ -0,0 +1,10 @@
Then /^the following files should be loaded in order:$/ do |table|
files = table.raw.flatten
@output.lines.collect(&:strip).each do |line|
files.shift if line[files.first]
end
files.should be_empty
end

View File

@ -0,0 +1,4 @@
Then /^I should get help output$/ do
@output.should include("Usage:")
end

View File

@ -0,0 +1,3 @@
Then /^the output should include "([^"]*)"$/ do |string|
@output.should include(string)
end

View File

@ -0,0 +1,4 @@
Then /^the output should not include "([^"]*)"$/ do |string|
@output.should_not include(string)
end

View File

@ -0,0 +1,4 @@
Then /^the report file "([^"]*)" should not exist$/ do |file|
File.file?(file).should be_false
end

View File

@ -0,0 +1,3 @@
Then /^the report file "([^"]*)" should exist$/ do |file|
File.file?(file).should be_true
end

View File

@ -0,0 +1,7 @@
Then /^the report file "(.*)" should have (\d+) total, (\d+) failures?, (no|yes) console usage$/ do |file, total, failures, console_usage|
report = Jasmine::Headless::Report.load(file)
report.total.should == total.to_i
report.failed.should == failures.to_i
report.has_used_console?.should == (console_usage == 'yes')
end

View File

@ -0,0 +1,4 @@
Then /^the report file "([^"]*)" should have seed (\d+)$/ do |file, seed|
report = Jasmine::Headless::Report.load(file)
report.seed.should == seed.to_i
end

View File

@ -0,0 +1,3 @@
Then /^the runner should have an exit status of (\d+)$/ do |exit_status|
@result.should == exit_status.to_i
end

View File

@ -0,0 +1,4 @@
Then /^the command to run the runner should include the report file "([^"]*)"$/ do |file|
@runner.jasmine_command.should include("-r #{file}")
end

View File

@ -0,0 +1,3 @@
Then /^the command to run the runner should not include a report file$/ do
@runner.jasmine_command.should_not include('-r')
end

View File

@ -0,0 +1,6 @@
Then /^the template should use the "([^"]*)" reporter to "([^"]*)"$/ do |reporter, target|
output = @template_writer.render
output.should include(%{jasmine.HeadlessReporter.#{reporter}("#{target}")})
end

View File

@ -0,0 +1,3 @@
When /^I delete the file "([^"]*)"$/ do |file|
FileUtils.rm_f(file)
end

View File

@ -0,0 +1,4 @@
When /^I get a runner$/ do
@runner = Jasmine::Headless::Runner.new(@options)
end

View File

@ -0,0 +1,4 @@
When /^I get a template writer$/ do
@template_writer = Jasmine::Headless::TemplateWriter.new(@runner)
end

View File

@ -0,0 +1,4 @@
When /^I run `(.*)`$/ do |command|
@output = `#{command}`
end

View File

@ -0,0 +1,4 @@
When /^I run the runner$/ do
@result = @runner.run
end

7
features/support/env.rb Normal file
View File

@ -0,0 +1,7 @@
require 'jasmine-headless-webkit'
After do
FileUtils.rm_f 'spec/report.txt'
FileUtils.rm_f 'spec/runner.html'
end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

671
index.md
View File

@ -1,671 +0,0 @@
---
title: jasmine-headless-webkit -- The fastest way to run your Jasmine specs!
layout: default
---
# Jasmine Headless WebKit
## Run your Jasmine specs at sonic boom speed!
<img src="images/f5.png" alt="Colored Output" />
[Jasmine](http://pivotal.github.com/jasmine/) is great. I love it. But running Jasmine when you need to test code that will run
in a browser environment can be problematic and slow:
* The [Jasmine gem](https://github.com/pivotal/jasmine-gem)'s server makes getting up and testing very fast, but F5-ing your browser for each test run is distracting.
* Jasmine CI uses Selenium, which speeds up the process a bit, but you're still rendering pixels in a browser, albeit with the option of rendering those pixels in a lot of different browsers at once.
* Node.js, EnvJS, and Rhino solutions for running Jasmine are great for anything that will never run in a real browser. I'm a big believer of running code destined for a browser in a browser itself, not a simulator.
* [Evergreen](https://github.com/jnicklas/evergreen) makes Jasmine testing in a Rails app as easy as pie, but not everyone writes for Rails.
But there's a solution for fast, accurate browser-based testing. with a focus on continuous testing,
using one of the most popular browser cores, and that dovetails perfectly into the Jasmine gem's already established protocols.
## Enter `jasmine-headless-webkit`
`jasmine-headless-webkit` uses the [QtWebKit widget](http://trac.webkit.org/wiki/QtWebKit) to run your specs without needing to render a pixel. It's nearly
as fast as running in a JavaScript engine like Node.js, and, since it's a real browser environment, all the modules
you would normally use, like jQuery and Backbone.js, work without any modifications. If you write your tests correctly,
they'll even work when running in the Jasmine gem's server with no changes to your code.
`jasmine-headless-webkit` also streamlines your workflow in other ways:
* It integrates with [Guard](https://github.com/guard/guard) for a continuous testing setup when using [`guard-jasmine-headless-webkit`](https://github.com/guard/guard-jasmine-headless-webkit).
* It integrates with [Sprockets](https://github.com/sstephenson/sprockets) to handle code requires & preprocessing and JavaScript templates.
* It compiles [CoffeeScript](http://jashkenas.github.com/coffee-script/), both for your tests and for your application logic.
* It can be configured like RSpec, and its output is very similar to RSpec's output, so you don't need to learn too much new stuff to use and integrate it.
* It provides cleaner debugging and backtrace output than a lot of other console-based test tools provide.
* It's *fast*.
## Is this for me?
That depends on what you need:
* If you're new to JavaScript testing, drop in [Pivotal's Jasmine gem](https://github.com/pivotal/jasmine-gem) and point your browser at http://localhost:8888/.
* If you're used to how the Jasmine gem works and want to move to a faster solution geared toward continuous testing, you're in the right place!
* If you want an even simpler config and access to all of your Rails routes and resources for a quasi-integration testing setup, use [Evergreen](https://github.com/jnicklas/evergreen).
You even get your choice of browser drivers for free (as opposed to just Selenium or WebKit) as well as headless testing!
* If you want true integration testing, where you test the whole application stack, use Cucumber and/or Capybara.
* If you're not using Rails and still want to unit test, the Jasmine gem or `jasmine-headless-webkit` is what you want.
'round here, we focus on unit testing and mocking external interfaces. No using your app's views or routes, no hitting the app server to
get resources, just mocking and stubbing the JavaScript code all by itself.
## How do I use this wonderful toy?
You can use it standalone:
gem install jasmine-headless-webkit
Or you can use it with Bundler:
gem 'jasmine-headless-webkit'
However you install it, you'll get a `jasmine-headless-webkit` executable. You'll also need to set up your project
to use the Jasmine gem:
gem install jasmine
jasmine init
Once you're good enough, you can make the `spec/javascripts/support/jasmine.yml` file yourself and skip the Pivotal Jasmine gem entirely.
It's what the cool kids do.
### What do I need to get it working?
Installation requires Qt 4.7. `jasmine-headless-webkit` has been tested in the following environments:
* Mac OS X 10.6 and 10.7, with MacPorts Qt, Homebrew Qt and Nokia Qt.mpkg
* Kubuntu 110.04, 10.10 and 10.04
* Ubuntu 11.04 and 9.10
* Arch Linux
If it works in yours, [leave me a message on GitHub](https://github.com/johnbintz) or
[fork this site](https://github.com/johnbintz/jasmine-headless-webkit/tree/gh-pages) and add your setup.
## Qt 4.7.X
The gem is compiled using `qt4-qmake` and you will need Qt 4.7.x or greater.
The version you have installed should be detected correctly, and the appropriate message for installing Qt should
be given if it's wrong. If it's not, please file a new issue!
### Manually checking the Qt version
Test that `qt4-qmake` it is installed and verify your version.
qmake --version
If you have the Qt 4.7.x or greater, you are ready to install jasmine-headless-webkit.
QMake version 2.01a
Using Qt version 4.7.2 in /usr/lib
If you receive a different message, you can install `qt4-qmake` using one of the following commands as root:
### Ubuntu 11.04
{% highlight bash %}
sudo apt-get install libqt4-dev
sudo apt-get install qt4-qmake
sudo update-alternatives --config qmake # and select Qt 4's qmake
{% endhighlight %}
### Ubuntu 9.10
Running `sudo apt-get install libqt4-dev` and `sudo apt-get install qt4-qmake` will install qt4,
but it installs **version 4.5.2**, which will not be able to compile
**jasmine-headless-webkit**, as it requires Qt 4.7.X or greater.
You will need to compile qt4-qmake from source
[Qt version 4.7.0](http://get.qt.nokia.com/qt/source/qt-everywhere-opensource-src-4.7.0.tar.gz).
There are excellent [directions](http://doc.qt.nokia.com/latest/install-x11.html) on how to compile
the source code. You will need to ensure Qt is exported to your `$PATH` before using qmake, as the source code will
install to `/usr/local/Trolltech/`.
### Mac OS X 10.6 & 10.7
#### MacPorts
{% highlight bash %}
sudo port install qt4-mac
{% endhighlight %}
#### Homebrew
{% highlight bash %}
brew install qt
{% endhighlight %}
__(you may need to use `--build-from-source` on Lion)__
### My OS isn't on here!
[`capybara-webkit`](https://github.com/thoughtbot/capybara-webkit) has the best instructions for installing Qt on various other
systems that may not be covered here.
### How does it work?
`jasmine-headless-webkit` generates a static HTML file that includes the Jasmine JavaScript library from the Jasmine
gem, your application and spec files, and any helpers you may need. The runner then creates a WebKit widget that
loads the HTML file, runs the tests, and grabs the results of the test to show back to you. Awesome!
`jasmine-headless-webkit` uses the same `jasmine.yml` file that the Jasmine gem uses to define where particular
files for the testing process are located:
{% highlight yaml %}
src_files:
- "**/*"
helpers:
- helpers/**/*
spec_files:
- "**/*_spec.*"
src_dir: app/assets/javascripts
spec_dir: spec/javascripts
{% endhighlight %}
It also brings in the same copy of the Jasmine library that the Jasmine gem includes, so if you're testing in both environments,
you're guaranteed to get the same results in your tests.
#### `*.coffee` in my `jasmine.yml` file?!
Yes, `jasmine-headless-webkit` will support `*.coffee` files in `jasmine.yml`, which the normal Jasmine server currently
does not support out of the box. Once there's official support, you'll be able to easily switch between `jasmine-headless-webkit`
and the Jasmine test server when you're using CoffeeScript. CoffeeScript files are compiled and injected into the generated HTML
files.
Never done Jasmine in CoffeeScript? It looks like this:
{% highlight coffeescript %}
describe 'Component', ->
describe 'StorylineNode', ->
model = null
beforeEach ->
model = new ComponentStorylineNode({id: 1})
it 'should not be new', ->
expect(model.isNew()).toEqual(false)
{% endhighlight %}
...and it turns into this...
{% highlight js %}
describe('Component', function() {
return describe('StorylineNode', function() {
var model;
model = null;
beforeEach(function() {
return model = new ComponentStorylineNode({
id: 1
});
});
return it('should not be new', function() {
return expect(model.isNew()).toEqual(false);
});
});
});
{% endhighlight %}
#### Server interaction
Since there's no Jasmine server running, there's no way to grab test files from the filesystem via Ajax.
If you need to test server interaction, do one of the following:
* Stub your server responses using [Sinon.JS](http://sinonjs.org/), the recommended way.
* Use [PhantomJS](http://www.phantomjs.org/) against a running copy of a Jasmine server, instead of this project.
#### Sprockets support
Nearly all of Sprockets is accessible to your test suite when using `jasmine-headless-webkit`.
It's easier to list the parts that aren't accessible:
* `*.erb` files are not processed at all (and are actually ignored) because it's assumed the contents of those files depend on an
outside source, like a Rails app. That integration puts testing those files squarely in the "integration testing" realm, so
it's not valid to support them in a unit testing tool.
* No CSS compilation happens, so no Sass or LESS.
If any gems have `vendor/assets/javascripts` in their list of files, such as `jquery-rails`, those are put in the asset
path along with the paths you define in `src_dir`:
{% highlight yaml %}
src_dir:
- app/assets/javascripts
- vendor/assets/javascripts
{% endhighlight %}
_Technically, `spec_dir` is in your asset path, too, but Jasmine's typical behavior of including `helpers` before `spec_dir` should
give you all the include power you need for defining specs._
If you want to keep `src_dir` as a string for backwards compatibility, you can add additional asset paths with, you guessed it, `asset_paths`:
{% highlight yaml %}
src_dir: app/assets/javascripts
asset_paths:
- vendor/assets/javascripts
{% endhighlight %}
`asset_paths` are added to the Sprockets asset paths after `src_dir`.
In order for Sprockets support to work as intended, you should define your `src_files` and `spec_files` as such:
{% highlight yaml %}
src_files:
- "**/*.*"
spec_files:
- "**/*[Ss]pec.*"
{% endhighlight %}
This will include everything that Sprockets understands in all your `src_dir` and `spec_dir` paths. At that point, use Sprockets `require`
statements to define the include order of your files. Using the `--list` option on the command line to list the load order of files, combined
with the `--runner-out` option to write HTML runner files to a place where the browser can easily get to them, is very helpful when moving to
a Sprockets-managed project.
JavaScript Templates are supported too, including [haml-sprockets](https://github.com/dharanasoft/haml-sprockets). Use them as you would any other
JavaScript file, and ensure the load order is right, and the necessary code in the JST namespace will be created.
Since any gem with `vendor/assets/javascripts` is usable, that means Jasmine-specific gems are possible now. [jasmine-spec-extras](https://github.com/johnbintz/jasmine-spec-extras)
is the first such gem, which provides `jasmine-jquery`, `sinon`, and any other useful Jasmine helpers, vendored into the gem so you can easily include
them into your project without having to manually manage them yourself:
{% highlight coffeescript %}
#= require sinon
#= require backbone
describe "Spy thing", ->
it 'should fire a callback', ->
collection = new Backbone.Collection()
spy = sinon.spy()
collection.bind('reset', spy)
collection.reset()
{% endhighlight %}
If you have to use ERB to inject information into the JavaScript or CoffeeScript files in your project, I recommend that you move those
injections to a file that is included separately from the code, or include them in `application.*.erb` like this:
{% highlight coffeescript %}
# File: app/assets/javascripts/application.coffee.erb
#= require 'jquery'
#= require 'my_library'
MyLibrary.root_url = <%= api_root_path %>
{% endhighlight %}
Sprockets support is still pretty new, so as myself and others discover the best way to set up code that can be used in both places, those
practices will be outlined here.
#### Caching, caching, caching
`jasmine-headless-webkit` does two things that are CPU intensive (besides running tests): compiling CoffeeScript and analyzing
spec files to get line number information for nicer spec failure messages (_did I mention you get really nice spec failure
messages with `jasmine-headless-webkit`, too?_). These two operations are cached into the `.jhw-cache/` folder from where the
runner is executed. When this cache is combined with running tests continuously using Guard, runtime overhead is reduced to almost
nothing.
Of course, being a cache, it takes time to warm up. The first time you run `jasmine-headless-webkit` on a big project, it can take
several seconds to warm the cache. After that, enjoy an almost 20% speedup in runtime (tested on exactly one project's runtime,
YMMV).
#### What else works?
`alert()` and `confirm()` work, though the latter always returns `true`. You should be mocking calls to `confirm()`,
of course:
{% highlight js %}
spyOn(window, 'confirm').andReturn(false)
{% endhighlight %}
`console.log()` also works. It uses one of three methods for serializing what you've provided:
* If the object given responds to `toJSON`, `jasmine-headless-webkit` uses `JSON.stringify(object.toJSON())`.
* If the object is a jQuery object, it is serialized with `jQuery('<div>').append(object).html()`
and pretty-printed using the HTML beautifier from [JS Beautifier](https://github.com/einars/js-beautify).
* If none of these apply, it uses a hacked-up version of [jsDump](https://github.com/NV/jsDump) that ignores
Functions on objects and prevents cyclical references.
If you need a heavy-weight object printer, you also have `console.pp()`, which uses Jasmine's built-in pretty-printer if available, and falls back to `JSON.stringify()` if it's not.
You also get an additional method, `console.peek()`, which calls `console.log()` with the provided parameter, then passes the parameter back along so you
can continue to work with it. It's the equivalent of `.tap { |o| p o }` in Ruby.
## Running the runner
{% highlight bash %}
jasmine-headless-webkit [ -c / --colors ]
[ --no-colors ]
[ --no-full-run ]
[ --keep ]
[ -l / --list ]
[ --report <report file> ]
[ --runner-out <html file> ]
[ --use-server ]
[ --server-port <port number> ]
[ -j / --jasmine-config <path to jasmine.yml> ]
[ --seed <random seed> ]
<spec files to run>
{% endhighlight %}
The runner will return one of three exit codes:
* __0__ means your tests passed sucessfully.
* __1__ means you had a failure in your tests.
* __2__ means your tests passed, but you used `console.log()` somewhere.
### Setting default options
Much like RSpec, you can define the default options for each run of the runner. Place your global options into a
`~/.jasmine-headless-webkit` file and your per-project settings in a `.jasmine-headless-webkit` file at the root of
the project.
### Listng what files `jasmine-headless-webkit` will include
If your tests are not picking up a file you thought they should be, or they're being included in the wrong order,
run with the `-l` flag to get a list of the files that `jasmine-headless-webkit` will include in the generated HTML file.
*Very* handy for making sure your Sprockets requires are working correctly.
### Coloring the output
`jasmine-headless-webkit` will not color output by default. This makes it easier to integrate with CI servers. If you want
colored output, use the `-c` flag. With colored output, your tests will look like this:
<img class="large" src="images/colored-output.png" alt="Colored Output" />
If you have colors turned on globally, you can turn them off per-project or per-run with `--no-colors`.
### Preserving compiled output on errors
CoffeeScript logic errors can be hard to track down. Keep the generated HTML files with the `--keep` flag and you'll
get `specrunner.$$.html` files in your working directory.
### Writing out a machine-readable report
Use the `--report` option to create a detailed report file:
PASS||Statement||One||file.js:23
FAIL||Statement||Two||file.js:23
CONSOLE||Yes
ERROR||Uh oh||file.js:23
TOTAL||1||2||3||T
[`guard-jasmine-headless-webkit`](http://github.com/guard/guard-jasmine-headless-webkit/) uses this for the Growl notifications.
You can also use it in your own setups, to run specs remotely and stick the results into a CI system. You can use
`Jasmine::Headless::Report` to interpret the file and transform the output.
### Using a different `jasmine.yml` file
If for some reason you're not using the default path for a `jasmine.yml` file (which is `spec/javascripts/support/jasmine.yml`),
you can provide that path with `-j`.
### Randomizing the order of spec files
Spec files are shuffled into a random order before each run. This lets you find issues where spec files may depend on state
established in prior executed spec files -- a bad thing. After each run, you'll get the random seed used to randomize the files:
`Test ordering seed: --seed 1234`
If you're getting weird results related to the particular order of a run of specs, pass that same seed value back in
and get to work!
### Running only certain spec files
By default, if no files are passed into `jasmine-headless-webkit`, all possible spec files in the `spec_files` definition
will be run. You can limit the run to only certain files by passing those to `jasmine-headless-webkit`:
{% highlight bash %}
jasmine-headless-webkit spec/javascripts/models/node_viewer.coffee
{% endhighlight %}
### Serving files from an HTTP server
The `--use-server` flag will start up a simple WEBrick server for serving files to the WebKit runner, as opposed to serving
them from the filesystem. This also lets you test things like HTML5 history stuff and other things that require an `http://`
URL when loading files. Normally, the port this server runs on is random, but you can specify it with `--server-port`.
#### Filtered runs and full runs
Typically, targeted spec running is done by a tool like Guard, and the order of running goes like this:
* Run the filtered spec
* If it fails, stop processing and alert the user
* If it succeeds, run all specs and alert on success or failure
Having your test running tool re-run `jasmine-headless-webkit` is fast, but there's still the cost of instantiating QtWebKit and Ruby
with each run. Versions of `jasmine-headless-webkit` 0.3.0 and greater will do this for you, keeping the widget in memory and running
Jasmine tests on first the filtered suite, and then the complete suite. The results you'll get are for the last run that's executed, which
is typically what you want to know anyway. Newer versions of `guard-jasmine-headless-webkit` also support this behavior. This trims
valuable seconds off of testing with every run, saving you enough time every day to run to the coffee shop and get some delicious brew!
If you don't want this behavior, pass in `--no-full-run` and filtered runs will be the only thing that runs when you request one.
### Writing the HTML runner to another location
If you want to use the runner file in other places, use the `--runner-out` parameter with the name of the target file.
The HTML produced uses the Jasmine `HtmlReporter` if not loaded in `jasmine-headless-webkit`, so you should be able
to just open it in a browser and have it work.
If you always want the reporter written to a particular location, you can define that location in `jasmine.yml`:
{% highlight yaml %}
runner_output: "runner.html"
{% endhighlight %}
## Running the runner from a Ruby program
You can call the runner from Ruby:
{% highlight ruby %}
require 'jasmine-headless-webkit'
status_code = Jasmine::Headless::Runner.run(
:colors => false,
#=> true to get colors
:remove_html_file => true,
#=> false to keep specrunners on failure
:jasmine_config => 'spec/javascripts/support/jasmine.yml',
#=> run a different config
:report => false,
#=> filename if a report file should be written
:full_run => true,
#=> false to not run a full run after a targeted run
:files => ['file_one_spec.js', 'file_two_spec.coffee']
#=> files to use for a targeted run, [] to run all
)
{% endhighlight %}
## Automated testing during development
`jasmine-headless-webkit` works best when it's running all the time, re-running tests when you update the appropriate files.
If you use [Guard](https://github.com/guard/guard/), install [`guard-jasmine-headless-webkit`](http://github.com/guard/guard-jasmine-headless-webkit/)
and run `guard init jasmine-headless-webkit` to add the necessary bits to your `Guardfile` to test a Rails 3.1 (or a well-structured Rails 3.0) app.
### guard-rails-assets
With Sprockets support now in `jasmine-headless-webkit`, there's less of a need for most users for `guard-rails-assets`, unless you really need to get
at those ERB files in your project. [`guard-rails-assets`](http://github.com/dnagir/guard-rails-assets) is what you want to use in this case.
It will watch your app's code for changes and rebuild your pipelined JS code, ready to be tested with `jasmine-headless-webkit`:
{% highlight ruby %}
guard 'rails-assets' do
watch(%r{^app/assets/javascripts/(.*)\.(js|coffee)$})
end
guard 'jasmine-headless-webkit' do
watch(%r{^public/assets/.*\.js$})
watch(%r{^spec/javascripts/.*\.coffee$})
end
{% endhighlight %}
### Jammit for JS templates
If you're still using Jammit it shove your JS templates into one file, you can use a Guard for that, too! [`guard-jammit`](http://github.com/guard/guard-jammit)
provides Jammit watching support, but the current version (as of 2011-06-18) does not support some changes to Jammit's internals. Use [my fork](http://github.com/johnbintz/guard-jammit)
until that gets fixed.
{% highlight ruby %}
guard 'jammit' do
watch(%r{^app/views/.*\.jst$$})
end
guard 'jasmine-headless-webkit' do
watch(%r{^public/assets/.*\.js$})
watch(%r{^spec/javascripts/.*\.coffee$})
end
{% endhighlight %}
## Rake tasks
You can create a Rake task for your headless Jasmine specs:
{% highlight ruby %}
require 'jasmine-headless-webkit'
Jasmine::Headless::Task.new('jasmine:headless') do |t|
t.colors = true
t.keep_on_error = true
t.jasmine_config = 'this/is/the/path.yml'
end
{% endhighlight %}
If you've bundled `jasmine-headless-webkit` in with Rails, you'll also get a basic task for running your
Jasmine specs. Be sure to include the gem in the development group so you get with a normal call to `rake -T`:
{% highlight ruby %}
group :test, :development do
gem 'jasmine-headless-webkit'
end
{% endhighlight %}
<pre>
# rake -T
rake jasmine:headless # Run Jasmine specs headlessly
</pre>
This is the same as running `jasmine-headless-webkit -c`.
## Continuous integration & testing using Xvfb
Since most continuous integration servers do not have a display, you will need to use
Xvfb or virtual framebuffer Xserver for Version 11. If you elect not to use Xvfb, you will
need to have a browser and graphical display to run `jasmine-headless-webkit -c`.
Reference: [Xvfb Manpages](http://manpages.ubuntu.com/manpages/natty/man1/Xvfb.1.html)
### Install Xvfb
sudo apt-get install xvfb
### Resolve missing dependencies
To resolve missing dependencies, you will need to know what to install.
$ Xvfb :99 -ac
You will see a long list of warning messages:
[dix] Could not init font path element /usr/share/fonts/X11/misc,
removing from list!
[dix] Could not init font path element /usr/share/fonts/X11/cyrillic,
removing from list!
[dix] Could not init font path element
/usr/share/fonts/X11/100dpi/:unscaled, removing from list!
[dix] Could not init font path element
/usr/share/fonts/X11/75dpi/:unscaled, removing from list!
[dix] Could not init font path element
/usr/share/fonts/X11/Type1, removing from list!
[dix] Could not init font path element
/usr/share/fonts/X11/100dpi, removing from list!
[dix] Could not init font path element
/usr/share/fonts/X11/75dpi, removing from list!
sh: /usr/bin/xkbcomp: not found
(EE) Error compiling keymap (server-42)
(EE) XKB: Couldn't compile keymap
[config/dbus] couldn't take over org.x.config:
org.freedesktop.DBus.Error.AccessDenied
(Connection ":1.74" is not allowed to
own the service "org.x.config.display99"
due to security policies in the configuration file)
Installing the following packages would resolve the above warning messages. Your
missing packages may be different depending on the packages you have installed.
sudo apt-get install x11-xkb-utils
sudo apt-get install xfonts-100dpi xfonts-75dpi
sudo apt-get install xfonts-scalable xfonts-cyrillic
sudo apt-get install xserver-xorg-core
Once you have resolved these dependencies, you should see:
[dix] Could not init font path element /usr/share/fonts/X11/misc,
removing from list!
[dix] Could not init font path element /usr/share/fonts/X11/cyrillic,
removing from list!
[dix] Could not init font path element
/usr/share/fonts/X11/100dpi/:unscaled, removing from list!
[dix] Could not init font path element
/usr/share/fonts/X11/75dpi/:unscaled, removing from list!
[dix] Could not init font path element
/usr/share/fonts/X11/Type1, removing from list!
[dix] Could not init font path element
/usr/share/fonts/X11/100dpi, removing from list!
[dix] Could not init font path element
/usr/share/fonts/X11/75dpi, removing from list!
### Run with Xvfb
#### ...as a Rake task
xvfb-run rake jasmine:headless
# ...or...
xvfb-run jasmine-headless-webkit -c
Reference: [MARTIN DALE LYNESS](http://blog.martin-lyness.com/archives/installing-xvfb-on-ubuntu-9-10-karmic-koala)
#### ...seamlessly
First run Xvfb in the background:
Xvfb :0 -screen 0 1024x768x24 > /dev/null 2>&1 &
Then, set your `DISPLAY` to point at the Xvfb instance. Putting all this in your `.bash_profile` or equivalent startup
script makes this a lot easier:
xdpyinfo -display :0 &>/dev/null && export DISPLAY=:0
See [Paul Goscicki's post](http://paulgoscicki.com/archives/2011/09/run-guard-jasmine-headless-webkit-without-x-server/) for
more details on the setup. Thanks, Paul!
## RubyMine
RubyMine may throw an error when running rake spec, you will need to provide a
JavaScript runtime environment.
rake aborted!
Could not find a JavaScript runtime.
See https://github.com/sstephenson/execjs
for a list of available runtimes.
To resolve this problem, install and use the `therubyracer` gem, which is the embed V8 JavaScript interpreter into Ruby.
Additionally, you can set the `EXECJS_RUNTIME` environment variable to a [valid ExecJS runtime name](https://github.com/sstephenson/execjs/blob/master/lib/execjs/runtimes.rb#L55).
export EXECJS_RUNTIME=Node
## I have a problem or helpful suggestion, good sir.
Here's what you can do:
* Leave a ticket on [the Issues tracker](https://github.com/johnbintz/jasmine-headless-webkit/issues).
* [Fork'n'fix the code](https://github.com/johnbintz/jasmine-headless-webkit). Feel free to add a bunch of tests, too. I cowboyed this project when starting it, and I'm slowly getting back to being a good boy.
* Ping me on [Twitter](http://twitter.com/johnbintz) or on [GitHub](https://github.com/johnbintz).
## Credits & License
* Copyright (c) 2011 John Bintz
* Original Qt WebKit runner Copyright (c) 2010 Sencha Inc.
* Jasmine JavaScript library Copyright (c) 2008-2011 Pivotal Labs
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 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.

View File

@ -0,0 +1,30 @@
# -*- encoding: utf-8 -*-
$:.push File.expand_path("../lib", __FILE__)
require "jasmine/headless/version"
Gem::Specification.new do |s|
s.name = "jasmine-headless-webkit"
s.version = Jasmine::Headless::VERSION
s.platform = Gem::Platform::RUBY
s.authors = ["John Bintz", "Sencha Inc.", "Pivotal Labs"]
s.email = ["john@coswellproductions.com"]
s.homepage = ""
s.summary = %q{Run Jasmine specs headlessly in a WebKit browser}
s.description = %q{Run Jasmine specs headlessly}
s.rubyforge_project = "jasmine-headless-webkit"
s.extensions = `git ls-files -- ext/**/extconf.rb`.split("\n")
s.files = `git ls-files`.split("\n") + Dir['jasmine/lib/*']
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]
s.add_runtime_dependency 'jasmine-core'
s.add_runtime_dependency 'coffee-script'
s.add_runtime_dependency 'rainbow'
s.add_runtime_dependency 'multi_json', '>= 1.2.0'
s.add_runtime_dependency 'sprockets'
s.add_runtime_dependency 'sprockets-vendor_gems'
end

1
lib/autotest/discover.rb Normal file
View File

@ -0,0 +1 @@
Autotest.add_discovery { 'jasmine' if File.file?('./.jasmine-headless-webkit') }

7
lib/autotest/jasmine.rb Normal file
View File

@ -0,0 +1,7 @@
require 'autotest'
require 'autotest/jasmine_mixin'
class Autotest::Jasmine < Autotest
include JasmineMixin
end

View File

@ -0,0 +1,111 @@
module JasmineMixin
JASMINE_PROGRAM = File.expand_path('../../../bin/jasmine-headless-webkit', __FILE__)
JAVASCRIPT_EXTENSIONS = %w{js coffee}
def self.included(klass)
klass::ALL_HOOKS << [ :run_jasmine, :ran_jasmine ]
end
attr_accessor :is_jasmine_running, :jasmine_to_run, :jasmine_ran_once
def initialize
super()
setup_jasmine_project_mappings
jasmine_ran_once = false
end
def get_to_green
begin
reset_jasmine(:no)
super if find_files_to_test
reset_jasmine(:yes)
self.last_mtime = Time.at(0) if !options[:no_full_after_start] && !jasmine_ran_once
run_jasmine if find_files_to_test
self.is_jasmine_running = :all
wait_for_changes unless all_jasmine_good
end until all_jasmine_good
reset_jasmine(:all)
end
def rerun_all_tests
reset_jasmine(:no)
super
reset_jasmine(:yes)
run_jasmine
reset_jasmine(:all)
end
def reset_jasmine(method)
self.files_to_test = new_hash_of_arrays
self.is_jasmine_running = method
end
def run_jasmine
hook :run_jasmine
self.jasmine_to_run = :all
if mtime = find_files_to_test
self.last_mtime = mtime
end
begin
system make_jasmine_cmd
self.jasmine_to_run = ($?.exitstatus == 0) ? :none : :all
end
hook :ran_jasmine
jasmine_ran_once = true
end
def all_jasmine_good
self.jasmine_to_run == :none
self.files_to_test = new_hash_of_arrays
end
def find_files
Hash[super.find_all { |file, mtime|
is_js = (file[%r{\.(#{JAVASCRIPT_EXTENSIONS.join('|')})$}] != nil)
case self.is_jasmine_running
when :all
true
when :no
!is_js
when :yes
is_js
end
}]
end
def make_jasmine_cmd
self.files_to_test.empty? ? '' :
%{#{JASMINE_PROGRAM} #{self.files_to_test.keys.collect { |key| %{'#{key}'} }.join(' ')}}.tap { |o| p o }
end
def setup_jasmine_project_mappings
add_mapping(%r{spec/javascripts/.*_spec\.(js|coffee)}) { |filename, _|
filename
}
add_mapping(%r{public/javascripts/(.*)\.js}) { |_, m|
files_matching(%r{spec/javascripts/#{m[1]}_spec\..*$})
}
add_mapping(%r{app/coffeescripts/(.*)\.coffee}) { |_, m|
files_matching(%r{spec/javascripts/#{m[1]}_spec\..*$})
}
end
def add_javascript_extensions(*extensions)
self.class::JAVASCRIPT_EXTENSIONS << extensions
end
end

View File

@ -0,0 +1,7 @@
require 'autotest/rspec2'
require 'autotest/jasmine_mixin'
class Autotest::JasmineRspec2 < Autotest::Rspec2
include JasmineMixin
end

View File

@ -0,0 +1,3 @@
require 'jasmine/headless'
require 'jasmine/headless/railtie' if defined?(Rails) && Rails::VERSION::MAJOR >= 3

61
lib/jasmine/headless.rb Normal file
View File

@ -0,0 +1,61 @@
require 'pathname'
module Jasmine
module Headless
EXCLUDED_FORMATS = %w{less sass scss erb str}
autoload :CommandLine, 'jasmine/headless/command_line'
autoload :CoffeeScriptCache, 'jasmine/headless/coffee_script_cache'
autoload :SpecFileAnalyzer, 'jasmine/headless/spec_file_analyzer'
autoload :CacheableAction, 'jasmine/headless/cacheable_action'
autoload :VERSION, 'jasmine/headless/version'
autoload :Runner, 'jasmine/headless/runner'
autoload :Options, 'jasmine/headless/options'
autoload :Task, 'jasmine/headless/task'
autoload :FilesList, 'jasmine/headless/files_list'
autoload :UniqueAssetList, 'jasmine/headless/unique_asset_list'
autoload :TemplateWriter, 'jasmine/headless/template_writer'
autoload :FileChecker, 'jasmine/headless/file_checker'
autoload :CoffeeTemplate, 'jasmine/headless/coffee_template'
autoload :JSTemplate, 'jasmine/headless/js_template'
autoload :JSTTemplate, 'jasmine/headless/jst_template'
autoload :CSSTemplate, 'jasmine/headless/css_template'
autoload :NilTemplate, 'jasmine/headless/nil_template'
autoload :Report, 'jasmine/headless/report'
autoload :ReportMessage, 'jasmine/headless/report_message'
class << self
def root
@root ||= Pathname(File.expand_path('../../..', __FILE__))
end
def warn(message)
output.puts message if show_warnings?
end
def show_warnings=(show)
@show_warnings = show
end
def show_warnings?
@show_warnings = true if @show_warnings.nil?
@show_warnings
end
def output
$stdout
end
end
end
end
require 'jasmine/headless/errors'

View File

@ -0,0 +1,85 @@
module Jasmine::Headless
class CacheableAction
class << self
def enabled=(bool)
@enabled = bool
end
def enabled?
@enabled = true if @enabled == nil
@enabled
end
def cache_type
raise ArgumentError.new("No cache type defined for #{self.name}") if @cache_type == nil
@cache_type
end
def cache_type=(type)
@cache_type = type
end
def cache_dir=(dir)
@cache_dir = dir
end
def cache_dir
@cache_dir ||= '.jhw-cache'
end
def for(file)
new(file).handle
end
end
attr_reader :file
def initialize(file)
@file = file
end
def handle
if CacheableAction.enabled?
if fresh?
unserialize(File.read(cache_file))
else
result = action
FileUtils.mkdir_p File.split(cache_file).first
File.open(cache_file, 'wb') { |fh| fh.print serialize(result) }
result
end
else
action
end
end
def cache_file
@cache_file ||= File.expand_path(relative_cache_file) + '.js'
end
def relative_cache_file
File.join(self.class.cache_dir, self.class.cache_type, file.gsub(Dir.pwd + '/', ''))
end
def fresh?
cached? && (File.mtime(file) < File.mtime(cache_file))
end
def cached?
File.exist?(cache_file)
end
def action
raise StandardError.new("Override action")
end
def serialize(data)
data
end
def unserialize(data)
data
end
end
end

View File

@ -0,0 +1,20 @@
require 'coffee_script'
require 'digest/sha1'
require 'fileutils'
module Jasmine
module Headless
class CoffeeScriptCache < CacheableAction
class << self
def cache_type
"coffee_script"
end
end
def action
CoffeeScript.compile(File.read(file))
end
end
end
end

View File

@ -0,0 +1,36 @@
require 'tilt/template'
require 'rainbow'
module Jasmine::Headless
class CoffeeTemplate < Tilt::Template
include Jasmine::Headless::FileChecker
self.default_mime_type = 'application/javascript'
def prepare ; end
def evaluate(scope, locals, &block)
if bad_format?(file)
alert_bad_format(file)
return ''
end
begin
cache = Jasmine::Headless::CoffeeScriptCache.new(file)
source = cache.handle
if cache.cached?
%{<script type="text/javascript" src="#{cache.cache_file}"></script>
<script type="text/javascript">window.CSTF['#{File.split(cache.cache_file).last}'] = '#{file}';</script>}
else
%{<script type="text/javascript">#{source}</script>}
end
rescue CoffeeScript::CompilationError => ne
puts "[%s] %s: %s" % [ 'coffeescript'.color(:red), file.color(:yellow), "#{ne.message}".color(:white) ]
raise ne
rescue StandardError => e
puts "[%s] Error in compiling file: %s" % [ 'coffeescript'.color(:red), file.color(:yellow) ]
raise e
end
end
end
end

View File

@ -0,0 +1,31 @@
module Jasmine::Headless
class CommandLine
class << self
def run!
require 'coffee-script'
require 'rainbow'
begin
options = Options.from_command_line
runner = Runner.new(options)
if options[:do_list]
FilesList.reset!
files_list = FilesList.new(:config => runner.jasmine_config)
files_list.files.each { |file| puts file }
else
exit runner.run
end
rescue CoffeeScript::CompilationError
exit 1
rescue StandardError => e
$stderr.puts "[%s] %s (%s)" % [ "jasmine-headless-webkit".color(:red), e.message.color(:white), e.class.name.color(:yellow) ]
$stderr.puts e.backtrace.collect { |line| " #{line}" }.join("\n")
exit 1
end
end
end
end
end

View File

@ -0,0 +1,19 @@
require 'tilt/template'
module Jasmine::Headless
class CSSTemplate < Tilt::Template
include Jasmine::Headless::FileChecker
self.default_mime_type = 'text/css'
def prepare ; end
def evaluate(scope, locals, &block)
if bad_format?(file)
alert_bad_format(file)
return ''
end
file ? %{<link rel="stylesheet" href="#{file}" type="text/css" />} : data
end
end
end

View File

@ -0,0 +1,16 @@
module Jasmine
module Headless
class NoRunnerError < StandardError
def message
"The jasmine-headless-webkit specrunner (jasmine-webkit-specrunner) could not be found! Try reinstalling the gem."
end
end
class TestFailure < StandardError; end
class ConsoleLogUsage < StandardError ; end
class JasmineConfigNotFound < Errno::ENOENT ; end
class InvalidReport < StandardError ; end
end
end

View File

@ -0,0 +1,25 @@
module Jasmine::Headless::FileChecker
def excluded_formats
::Jasmine::Headless::EXCLUDED_FORMATS
end
def bad_format?(file)
return if file.nil?
excluded_formats.any? do |format|
file[%r{\.#{format}(\.|$)}]
end
end
def alert_bad_format(file)
Jasmine::Headless.warn("[%s] %s: %s" % [ 'Skipping File'.color(:red), file.color(:yellow), "unsupported format".color(:white) ])
end
def alert_if_bad_format?(file)
if result = bad_format?(file)
alert_bad_format(file)
end
result
end
end

View File

@ -0,0 +1,332 @@
require 'jasmine-core'
require 'time'
require 'multi_json'
require 'set'
require 'sprockets'
require 'sprockets/engines'
require 'sprockets-vendor_gems'
module Jasmine::Headless
class FilesList
include FileChecker
class << self
def asset_paths
@asset_paths ||= Sprockets.find_gem_vendor_paths(:for => 'javascripts')
end
def reset!
@asset_paths = nil
@registered_engines = {}
# register haml-sprockets and handlebars_assets if it's available...
%w{haml-sprockets handlebars_assets}.each do |library|
begin
require library
rescue LoadError
end
end
if ENV['JHW_ENV']
begin
require 'bundler'
Bundler.require(ENV['JHW_ENV'].to_sym)
rescue LoadError
end
end
@sprockets_environment = nil
end
def registered_engines
@registered_engines ||= {}
end
def register_engine(file_extension, template_class)
registered_engines[file_extension] = template_class
end
def register_engines!
registered_engines.each do |file_extension, template_class|
Sprockets.register_engine file_extension, template_class
end
end
def default_files
%w{jasmine.js jasmine-html jasmine.css jasmine-extensions
intense headless_reporter_result jasmine.HeadlessReporter
jasmine.HeadlessReporter.ConsoleBase
jsDump beautify-html}
end
def extension_filter
extensions = (%w{.js .css} + Sprockets.engine_extensions)
%r{(#{extensions.join('|')})$}
end
end
PLEASE_WAIT_IM_WORKING_TIME = 2
attr_reader :options, :required_files, :potential_files_to_filter
def initialize(options = {})
@options = options
Kernel.srand(options[:seed]) if options[:seed]
@required_files = UniqueAssetList.new
@potential_files_to_filter = []
register_engines!
load_initial_assets
use_config if config?
end
def register_engines!
begin
require spec_helper
rescue LoadError
end
self.class.register_engines!
end
def load_initial_assets
self.class.default_files.each do |file|
begin
add_path(file)
rescue InvalidUniqueAsset => e
raise StandardError.new("Not an asset: #{file}")
end
end
(options[:reporters] || []).each do |reporter, identifier, file|
add_path("jasmine.HeadlessReporter.#{reporter}")
end
end
def files
required_files.flatten.collect { |asset| asset.pathname.to_s }.uniq
end
def spec_files
filter_for_requested_specs(
files.find_all { |file| spec_dir.any? { |dir| file[dir] } }
)
end
def filtered_files
filter_for_requested_specs(files)
end
def search_paths
return @search_paths if @search_paths
@search_paths = [ Jasmine::Core.path, Jasmine::Headless.root.join('vendor/assets/javascripts').to_s ]
@search_paths += self.class.asset_paths
@search_paths += src_dir.collect { |dir| File.expand_path(dir) }
@search_paths += asset_paths.collect { |dir| File.expand_path(dir) }
@search_paths += spec_dir.collect { |dir| File.expand_path(dir) }
@search_paths.uniq!
@search_paths
end
def sprockets_environment
return @sprockets_environment if @sprockets_environment
@sprockets_environment = Sprockets::Environment.new
search_paths.each { |path| @sprockets_environment.append_path(path) }
@sprockets_environment.unregister_postprocessor('application/javascript', Sprockets::SafetyColons)
# ...and unregister ones we don't want/need
@sprockets_environment.instance_eval do
EXCLUDED_FORMATS.each do |extension|
register_engine ".#{extension}", Jasmine::Headless::NilTemplate
end
register_engine '.coffee', Jasmine::Headless::CoffeeTemplate
register_engine '.js', Jasmine::Headless::JSTemplate
register_engine '.css', Jasmine::Headless::CSSTemplate
register_engine '.jst', Jasmine::Headless::JSTTemplate
end
@sprockets_environment
end
def has_spec_outside_scope?
if is_outside_scope = !spec_filter.empty?
is_outside_scope = spec_dir.any? do |dir|
spec_file_searches.any? do |search|
!spec_files.any? do |file|
target = File.join(dir, search)
File.fnmatch?(target, file) || File.fnmatch?(target.gsub(%{^**/}, ''), file)
end
end
end
end
is_outside_scope
end
def filtered?
files != filtered_files
end
def files_to_html
to_html(files)
end
def filtered_files_to_html
to_html(filtered_files)
end
def spec_file_line_numbers
@spec_file_line_numbers ||= Hash[spec_files.collect { |file|
if File.exist?(file)
if !(lines = Jasmine::Headless::SpecFileAnalyzer.for(file)).empty?
[ file, lines ]
end
else
nil
end
}.compact]
end
private
def to_html(files)
alert_time = Time.now + PLEASE_WAIT_IM_WORKING_TIME
files.collect do |file|
if alert_time && alert_time < Time.now
puts "Rebuilding cache, please wait..."
alert_time = nil
end
sprockets_environment.find_asset(file, :bundle => false).body
end.compact.reject(&:empty?)
end
def spec_filter
@spec_filter ||= (@options[:only] && @options[:only].collect { |path| expanded_dir(path) }.flatten) || []
end
SEARCH_ROOTS = {
'src_files' => 'src_dir',
'stylesheets' => 'src_dir',
'helpers' => 'spec_dir',
'spec_files' => 'spec_dir'
}
def use_config
@config = @options[:config].dup
@searches = {}
@potential_files_to_filter = []
%w{src_files stylesheets helpers spec_files}.each do |type|
if data = @config[type]
add_files(@searches[type] = data.flatten, type, send(SEARCH_ROOTS[type]))
end
end
end
def add_files(patterns, type, dirs)
patterns.each do |pattern|
dirs.collect { |dir| expanded_dir(File.join(dir, pattern)) }.each do |files|
files.sort! { |a, b| Kernel.rand(3) - 1 } if type == 'spec_files'
files.each do |path|
add_path(path, type)
end
end
end
if type == 'spec_files'
spec_filter.each { |path| add_path(path, type) }
end
end
def config?
@options[:config]
end
def expanded_dir(path)
file_list = Dir.glob(path).sort
file_list.find_all { |file|
file[extension_filter] && !alert_if_bad_format?(file)
}.collect {
|file| File.expand_path(file)
}.find_all {
|path| File.file?(path)
}
end
def extension_filter
self.class.extension_filter
end
def add_path(path, type = nil)
asset = sprockets_environment.find_asset(path)
@required_files << asset
if type == 'spec_files'
@potential_files_to_filter << path
end
end
def src_dir
@src_dir ||= config_dir_or_pwd('src_dir') + asset_paths
end
def spec_dir
@spec_dir ||= config_dir_or_pwd('spec_dir')
end
def asset_paths
@asset_paths ||= config_dir('asset_paths')
end
def spec_file_searches
@searches['spec_files']
end
def config_dir_or_pwd(dir)
if (found = config_dir(dir)).empty?
found = [ Dir.pwd ]
end
found
end
def config_dir(dir)
[ @options[:config] && @options[:config][dir] ].flatten.compact.collect { |dir| File.expand_path(dir) }
end
def filter_for_requested_specs(files)
files.find_all do |file|
if potential_files_to_filter.include?(file)
spec_filter.empty? || spec_filter.any? { |pattern| File.fnmatch?(pattern, file) }
else
true
end
end
end
def spec_helper
File.join(spec_dir, "helpers", "spec_helper")
end
end
end
module Jasmine::Headless
extend self
def register_engine(file_extension, template_class)
Jasmine::Headless::FilesList.register_engine(file_extension, template_class)
end
end

View File

@ -0,0 +1,23 @@
require 'tilt/template'
module Jasmine::Headless
class JSTemplate < Tilt::Template
include Jasmine::Headless::FileChecker
self.default_mime_type = 'application/javascript'
def prepare ; end
def evaluate(scope, locals, &block)
if bad_format?(file)
alert_bad_format(file)
return ''
end
if data[%r{^<script type="text/javascript"}]
data
else
file ? %{<script type="text/javascript" src="#{file}"></script>} : data
end
end
end
end

View File

@ -0,0 +1,15 @@
require 'sprockets/jst_processor'
module Jasmine::Headless
class JSTTemplate < Sprockets::JstProcessor
include Jasmine::Headless::FileChecker
def evaluate(*args)
if bad_format?(file)
alert_bad_format(file)
return ''
end
%{<script type="text/javascript">#{super}</script>}
end
end
end

View File

@ -0,0 +1,14 @@
require 'tilt/template'
require 'rainbow'
module Jasmine::Headless
class NilTemplate < Tilt::Template
def prepare ; end
def evaluate(scope, locals, &block)
return ''
end
end
end

View File

@ -0,0 +1,206 @@
require 'forwardable'
require 'getoptlong'
module Jasmine
module Headless
class Options
extend Forwardable
def_delegators :@options, :[], :[]=
DEFAULT_OPTIONS = {
:colors => false,
:remove_html_file => true,
:runner_output_filename => false,
:jasmine_config => 'spec/javascripts/support/jasmine.yml',
:do_list => false,
:full_run => true,
:enable_cache => true,
:files => [],
:reporters => [ [ 'Console' ] ],
:quiet => false,
:use_server => false,
:server_port => nil
}
DEFAULTS_FILE = File.join(Dir.pwd, '.jasmine-headless-webkit')
GLOBAL_DEFAULTS_FILE = File.expand_path('~/.jasmine-headless-webkit')
REPORT_DEPRECATED_MESSAGE = "--report is deprecated. Use --format HeadlessFileReporter --out <filename>"
def self.from_command_line
options = new
options.process_command_line_args
options[:files] = ARGV
options
end
def initialize(opts = {})
@options = DEFAULT_OPTIONS.dup
srand
@options[:seed] = rand(10000)
read_defaults_files
opts.each { |k, v| @options[k] = v if v }
end
def process_option(*args)
opt, arg = args.flatten[0..1]
case opt
when '--colors', '-c'
@options[:colors] = true
when '--no-colors', '-nc'
@options[:colors] = false
when '--cache'
@options[:enable_cache] = true
when '--no-cache'
@options[:enable_cache] = false
when '--keep'
@options[:remove_html_file] = false
when '--report'
warn REPORT_DEPRECATED_MESSAGE
add_reporter('File', arg)
add_reporter('Console')
when '--runner-out'
@options[:runner_output_filename] = arg
when '--jasmine-config', '-j'
@options[:jasmine_config] = arg
when '--no-full-run'
@options[:full_run] = false
when '--list', '-l'
@options[:do_list] = true
when '--quiet', '-q'
@options[:quiet] = true
when '--seed'
@options[:seed] = arg.to_i
when '--format', '-f'
add_reporter(arg)
when '--use-server'
@options[:use_server] = true
when '--server-port'
@options[:server_port] = arg.to_i
when '--out'
add_reporter_file(arg)
when '-h', '--help'
print_help
exit
end
end
def read_defaults_files
[ GLOBAL_DEFAULTS_FILE, DEFAULTS_FILE ].each do |file|
if File.file?(file)
File.readlines(file).collect { |line| line.strip.split(' ', 2) }.each { |*args| process_option(*args) }
end
end
end
def process_command_line_args
command_line_args = GetoptLong.new(
[ '--colors', '-c', GetoptLong::NO_ARGUMENT ],
[ '--no-colors', GetoptLong::NO_ARGUMENT ],
[ '--cache', GetoptLong::NO_ARGUMENT ],
[ '--no-cache', GetoptLong::NO_ARGUMENT ],
[ '--keep', GetoptLong::NO_ARGUMENT ],
[ '--runner-out', GetoptLong::REQUIRED_ARGUMENT ],
[ '--report', GetoptLong::REQUIRED_ARGUMENT ],
[ '--jasmine-config', '-j', GetoptLong::REQUIRED_ARGUMENT ],
[ '--no-full-run', GetoptLong::NO_ARGUMENT ],
[ '--list', '-l', GetoptLong::NO_ARGUMENT ],
[ '--seed', GetoptLong::REQUIRED_ARGUMENT ],
[ '--format', '-f', GetoptLong::REQUIRED_ARGUMENT ],
[ '--out', GetoptLong::REQUIRED_ARGUMENT ],
[ '--use-server', GetoptLong::NO_ARGUMENT ],
[ '--server-port', GetoptLong::REQUIRED_ARGUMENT ],
[ '-h', '--help', GetoptLong::NO_ARGUMENT ],
[ '-q', '--quiet', GetoptLong::NO_ARGUMENT ]
)
command_line_args.each { |*args| process_option(*args) }
end
def reporters
file_index = 0
@options[:reporters].collect do |reporter, file|
output = [ reporter ]
if file
output << "report:#{file_index}"
output << file
file_index += 1
else
output << "stdout"
end
output
end
end
def file_reporters
reporters.find_all { |reporter| reporter[1]["report:"] }
end
private
def add_reporter(name, file = nil)
if !@added_reporter
@options[:reporters] = []
@added_reporter = true
end
if (parts = name.split(':')).length == 2
name, file = parts
end
@options[:reporters] << [ name ]
add_reporter_file(file) if file
end
def add_reporter_file(file)
@options[:reporters].last << file
end
def print_help
options = [
[ '-c, --colors', 'Enable colors (default: disabled)' ],
[ '-nc, --no-colors', 'Disable colors' ],
[ '--cache', 'Enable cache (default: enabled)' ],
[ '--no-cache', 'Disable cache' ],
[ '--keep', 'Keep runner files on failure' ],
[ '--runner-out <filename>', 'Write runner to specified filename' ],
[ '-j, --jasmine-config <config file>', 'Jasmine Yaml config to use' ],
[ '--no-full-run', 'Do not perform a full spec run after a successful targeted spec run' ],
[ '--use-server', 'Load tests from an HTTP server instead of from filesystem' ],
[ '-l, --list', 'List files in the order they will be required' ],
[ '--seed <seed>', 'Random order seed for spec file ordering' ],
[ '-f, --format <reporter<:filename>>', 'Specify an output reporter and possibly output filename' ],
[ '--out <filename>', 'Specify output filename for last defined reporter' ],
[ '-q, --quiet', "Silence most non-test related warnings" ],
[ '-h, --help', "You're looking at it" ]
]
longest_length = options.collect(&:first).collect(&:length).max
puts <<-HELP
Usage: #{$0} [ options ] [ spec files ]
Options:
#{options.collect { |option, description| " #{option.ljust(longest_length)} #{description}" }.join("\n")}
Available reporters:
Console Write out spec results to the console in a progress format (default)
Verbose Write out spec results to the console in a verbose format
File Write spec results in jasmine-headless-webkit ReportFile format
Tap Write spec results in TAP format
Add reporters to the jasmine.HeadlessReporter object to access them
(ex: jasmine.HeadlessReporter.Teamcity for the Teamcity reporter)
HELP
end
end
end
end

View File

@ -0,0 +1,12 @@
module Jasmine
module Headless
class Railtie < Rails::Railtie
rake_tasks do
Jasmine::Headless::Task.new do |t|
t.colors = true
end
end
end
end
end

View File

@ -0,0 +1,81 @@
require 'forwardable'
module Jasmine::Headless
class Report
extend Forwardable
def_delegators :report, :length, :[]
def_delegators :last_total, :total, :failed, :time
class << self
def load(file)
new(file).process
end
end
attr_reader :file, :report
def initialize(file)
@file = file
end
def process
last_message = nil
@report = File.readlines(file).collect do |line|
type, *parts = line.split('||', -1)
if !(report_klass = report_class_for(type))
if last_message.kind_of?(Jasmine::Headless::ReportMessage::Console)
last_message.message << "\n"
last_message.message << line.strip
end
else
parts.last.strip!
last_message = report_klass.new_from_parts(parts)
end
end
self
end
def has_used_console?
@report.any? { |entry| entry.kind_of?(Jasmine::Headless::ReportMessage::Console) }
end
def has_failed_on?(statement)
@report.any? { |entry|
if entry.kind_of?(Jasmine::Headless::ReportMessage::Fail)
entry.statement == statement
end
}
end
def valid?
last_total != nil
end
def failed_files
@report.find_all { |entry|
entry.kind_of?(Jasmine::Headless::ReportMessage::Fail)
}.collect(&:filename).uniq.compact
end
def seed
if seed = report.find { |entry| entry.respond_to?(:seed) }
seed.seed
end
end
private
def last_total
@report.reverse.find { |entry| entry.respond_to?(:total) }
end
def report_class_for(type)
if constant = ReportMessage.constants.find { |k| k.to_s.downcase == type.downcase }
ReportMessage.const_get(constant)
end
end
end
end

View File

@ -0,0 +1,12 @@
module Jasmine::Headless
module ReportMessage
autoload :Spec, 'jasmine/headless/report_message/spec'
autoload :Pass, 'jasmine/headless/report_message/pass'
autoload :Fail, 'jasmine/headless/report_message/fail'
autoload :Console, 'jasmine/headless/report_message/console'
autoload :Error, 'jasmine/headless/report_message/error'
autoload :Total, 'jasmine/headless/report_message/total'
autoload :Seed, 'jasmine/headless/report_message/seed'
end
end

View File

@ -0,0 +1,18 @@
module Jasmine::Headless::ReportMessage
class Console
def self.new_from_parts(parts)
new(parts.first)
end
attr_reader :message
def initialize(message)
@message = message
end
def ==(other)
self.message == other.message
end
end
end

View File

@ -0,0 +1,20 @@
module Jasmine::Headless::ReportMessage
class Error
class << self
def new_from_parts(parts)
new(*parts)
end
end
attr_reader :message, :file_info
def initialize(message, file_info)
@message, @file_info = message, file_info
end
def ==(other)
self.message == other.message && self.file_info == other.file_info
end
end
end

View File

@ -0,0 +1,5 @@
module Jasmine::Headless::ReportMessage
class Fail < Spec
end
end

View File

@ -0,0 +1,5 @@
module Jasmine::Headless::ReportMessage
class Pass < Spec
end
end

View File

@ -0,0 +1,14 @@
module Jasmine::Headless::ReportMessage
class Seed
def self.new_from_parts(parts)
new(parts.first)
end
attr_reader :seed
def initialize(seed)
@seed = seed.to_i
end
end
end

View File

@ -0,0 +1,28 @@
module Jasmine::Headless::ReportMessage
class Spec
def self.new_from_parts(parts)
file_info = parts.pop
new(parts.join(' '), file_info)
end
attr_reader :statement, :file_info
def initialize(statement, file_info)
@statement, @file_info = statement, file_info
end
def ==(other)
self.statement == other.statement && self.file_info == other.file_info
end
def filename
if name = file_info.split(":").first
name
else
nil
end
end
end
end

View File

@ -0,0 +1,31 @@
module Jasmine::Headless::ReportMessage
class Total
class << self
def new_from_parts(parts)
new(*parts)
end
end
attr_reader :total, :failed, :time, :has_js_error
def initialize(total, failed, time, has_js_error)
@total, @failed, @time = total.to_i, failed.to_i, time.to_f
@has_js_error = case has_js_error
when String
has_js_error == "T"
else
has_js_error
end
end
def ==(other)
other &&
self.total == other.total &&
self.failed == other.failed &&
self.time == other.time &&
self.has_js_error == other.has_js_error
end
end
end

View File

@ -0,0 +1,261 @@
require 'fileutils'
require 'coffee-script'
require 'rainbow'
require 'yaml'
require 'erb'
require 'sprockets'
module Jasmine
module Headless
class IndexHandler
class << self
attr_accessor :index
end
def initialize(app)
@app = app
end
def call(env)
if env['PATH_INFO'] == '/'
return [ 302, { 'Location' => self.class.index }, [ 'Redirecting...' ] ]
end
@app.call(env)
end
end
class Runner
JASMINE_DEFAULTS = {
'spec_files' => [ '**/*[sS]pec.js' ],
'helpers' => [ 'helpers/**/*.js' ],
'spec_dir' => 'spec/javascripts',
'src_dir' => nil,
'stylesheets' => [],
'src_files' => [],
'backtrace' => []
}
RUNNER_DIR = File.expand_path('../../../../ext/jasmine-webkit-specrunner', __FILE__)
RUNNER = File.join(RUNNER_DIR, 'jasmine-webkit-specrunner')
attr_reader :options
def self.run(options = {})
new(options).run
end
def self.server_port
return @server_port if @server_port
require 'socket'
count = 100
begin
port = select_server_port
socket = TCPSocket.new(server_interface, port)
socket.close
count -= 1
raise "Could not create server port after 100 attempts!" if count == 0
rescue Errno::ECONNREFUSED
@server_port = port
break
ensure
begin
socket.close if socket
rescue IOError
end
end while true
@server_port
end
def self.server_port=(port)
@server_port = port
end
def self.select_server_port
21000 + rand(10000)
end
def self.server_interface
'127.0.0.1'
end
def self.server_uri
"http://#{server_interface}:#{server_port}"
end
def self.server_spec_path
self.server_uri + '/__JHW__/'
end
def self.ensure_server(options)
return if @server
require 'webrick'
require 'thread'
require 'rack'
require 'net/http'
port = server_port
@server = Thread.new do
Jasmine::Headless.warn "Powering up!"
app = Rack::Builder.new do
use IndexHandler
map '/__JHW__' do
run Rack::File.new(Dir.pwd)
end
map '/' do
run Rack::File.new('/')
end
end
Rack::Handler::WEBrick.run(
app,
:Port => port,
:Logger => Logger.new(StringIO.new),
:AccessLog => [
[ StringIO.new, WEBrick::AccessLog::COMMON_LOG_FORMAT ],
[ StringIO.new, WEBrick::AccessLog::REFERER_LOG_FORMAT ]
]
)
end
while true do
begin
Net::HTTP.get(URI(server_uri))
break
rescue Errno::ECONNREFUSED => e
end
sleep 0.1
end
end
def initialize(options)
options = Options.new(options) if !options.kind_of?(Options)
@options = options
end
def template_writer
@template_writer ||= TemplateWriter.new(self)
end
def jasmine_config
return @jasmine_config if @jasmine_config
@jasmine_config = JASMINE_DEFAULTS.dup
jasmine_config_data.each do |key, value|
@jasmine_config[key] = value if value
end
@jasmine_config
end
def jasmine_command(*targets)
command = [ RUNNER ]
command << "-s #{options[:seed]}"
command << '-c' if options[:colors]
command << '-q' if options[:quiet]
options.file_reporters.each do |reporter, identifier, file|
command << "-r #{file}"
end
command += targets
command.compact.join(' ')
end
def run
Jasmine::Headless::CacheableAction.enabled = @options[:enable_cache]
Jasmine::Headless.show_warnings = !@options[:quiet]
FilesList.reset!
self.class.server_port = options[:server_port]
@_targets = template_writer.write
run_targets = absolute_run_targets(@_targets.dup)
if run_targets.length == 2
if (!@options[:full_run] && files_list.filtered?) || files_list.has_spec_outside_scope?
run_targets.pop
end
end
runner = lambda { system jasmine_command(run_targets) }
if options[:use_server]
wrap_in_server(run_targets, &runner)
else
runner.call
end
@_status = $?.exitstatus
ensure
if @_targets && !runner_filename && (@options[:remove_html_file] || (@_status == 0))
@_targets.each { |target| FileUtils.rm_f target }
end
end
def absolute_run_targets(targets)
targets.flatten.collect do |target|
if options[:use_server]
target = self.class.server_spec_path + target
else
target = "file://" + File.expand_path(target)
end
target
end
end
def runner_filename
options[:runner_output_filename] || begin
if (runner_output = jasmine_config['runner_output']) && !runner_output.empty?
runner_output
else
false
end
end
end
def files_list
@files_list ||= Jasmine::Headless::FilesList.new(
:config => jasmine_config,
:only => options[:files],
:seed => options[:seed],
:reporters => options.reporters
)
end
def wrap_in_server(run_targets)
self.class.ensure_server(options)
IndexHandler.index = run_targets.last
Jasmine::Headless.warn "HTTP powered specs! Located at #{run_targets.join(' ')}"
yield
end
private
def jasmine_config_data
raise JasmineConfigNotFound.new("Jasmine config not found. I tried #{@options[:jasmine_config]}.") if !File.file?(@options[:jasmine_config])
YAML.load(ERB.new(File.read(@options[:jasmine_config])).result(binding))
end
end
end
end

Some files were not shown because too many files have changed in this diff Show More