Compare commits
318 Commits
better-ins
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
f7384d684c | ||
|
1055dc1016 | ||
|
c1e786f3c5 | ||
|
aa1d989a90 | ||
|
30e1ff8e51 | ||
|
9b41a36841 | ||
|
65137186c8 | ||
|
a89682f771 | ||
|
3815a47d07 | ||
|
724541a2cb | ||
|
1c81ea7286 | ||
|
8c6a792960 | ||
|
1aac97ea1f | ||
|
21590a9a19 | ||
|
68683e4b6c | ||
|
181f4e286a | ||
|
d572b0d6fe | ||
|
af20af8524 | ||
|
0064595dea | ||
|
2e2651d11a | ||
|
c96cbd4aac | ||
|
4f11cba073 | ||
|
bad6839760 | ||
|
453f439271 | ||
|
142a0c974b | ||
|
75dce5b66b | ||
|
0d4550524f | ||
|
47b8d10d56 | ||
|
d3c3e7bd7d | ||
|
f51f041b85 | ||
|
8de1bd8cf0 | ||
|
09842c0539 | ||
|
5d43eb2193 | ||
|
ed4590ee44 | ||
|
b7553062bd | ||
|
ed76916205 | ||
|
b9b4648a24 | ||
|
282ed40264 | ||
|
1f56031d8b | ||
|
da59a48025 | ||
|
81f561282a | ||
|
dfe1bece2c | ||
|
7587381e1e | ||
|
df83a61cb5 | ||
|
d5b2239d0f | ||
|
d31f628d91 | ||
|
0adf3a41b6 | ||
|
09f4df94de | ||
|
9dafb83892 | ||
|
9ff0dca191 | ||
|
dd86eb404e | ||
|
ac9a9cf23b | ||
|
e38963ed42 | ||
|
8465590930 | ||
|
4c7a1f860d | ||
|
5743227de6 | ||
|
e03389e938 | ||
|
4e64480c69 | ||
|
7dde9328df | ||
|
84e369d30a | ||
|
352ee417c5 | ||
|
00468fc1b3 | ||
|
297f822da1 | ||
|
1099773484 | ||
|
d038a748ed | ||
|
3b0a11edea | ||
|
f3dd4f2bf8 | ||
|
6ad4bdaec0 | ||
|
e25d89962c | ||
|
b6d94a5d0d | ||
|
ab1994696d | ||
|
5d504570fd | ||
|
a291fd5de3 | ||
|
af14f69a07 | ||
|
3c993001de | ||
|
602eb634d7 | ||
|
9affa65df4 | ||
|
0d4562119f | ||
|
ee3b6e598c | ||
|
4ae9815471 | ||
|
3fe66c66c5 | ||
|
f8db052281 | ||
|
16f867af09 | ||
|
03a381d3cc | ||
|
bd6a1afb92 | ||
|
3911041866 | ||
|
edf5a49f27 | ||
|
634f60ffe6 | ||
|
38327e6a95 | ||
|
2316041580 | ||
|
7d75b5466f | ||
|
130a65ecdd | ||
|
7206341768 | ||
|
a9cc872cc8 | ||
|
7ba77ea1f9 | ||
|
a18100bb71 | ||
|
08798ff88d | ||
|
a9b879722b | ||
|
4727ad5b7b | ||
|
9b5729b808 | ||
|
fde8bc3b7b | ||
|
115e2eff8b | ||
|
d214674620 | ||
|
ca8c655f00 | ||
|
61c8ed8828 | ||
|
e04d692d26 | ||
|
8867d00ac8 | ||
|
537b2e437d | ||
|
f57a59d767 | ||
|
4f8e732fa4 | ||
|
a2e3ea90c9 | ||
|
1583807c14 | ||
|
9b103807ee | ||
|
4150dd1828 | ||
|
91d9773933 | ||
|
26762f0586 | ||
|
c7ce823409 | ||
|
495fd1900e | ||
|
2d73baabb3 | ||
|
ce7b0f1368 | ||
|
5ca065728d | ||
|
3a559fb0d5 | ||
|
7b91fc5a76 | ||
|
befd0b4a2d | ||
|
69d865c050 | ||
|
298e8c2d3d | ||
|
d9ade573c9 | ||
|
9289f97f16 | ||
|
7db116fb45 | ||
|
54e5d5c9d5 | ||
|
dda84fdcb9 | ||
|
894b979674 | ||
|
cba18bfd22 | ||
|
7b3e9eaa3d | ||
|
eede055dfe | ||
|
82052d7803 | ||
|
f7e220bb18 | ||
|
065cb6a985 | ||
|
2aabba2cd7 | ||
|
8503d86aba | ||
|
42ed3e6013 | ||
|
919ce4f79c | ||
|
1244d780c1 | ||
|
951cf80f09 | ||
|
0be7b9e20c | ||
|
b04bb49879 | ||
|
c1dc86a329 | ||
|
04e2bc5f22 | ||
|
4f8a49e47c | ||
|
870b8e2a51 | ||
|
719ecf7f3c | ||
|
62d00cdd4c | ||
|
15e9e90a7b | ||
|
262b7a8223 | ||
|
84a87d423c | ||
|
243fe526ac | ||
|
d8d04b8638 | ||
|
9c78766ded | ||
|
dc134dcf24 | ||
|
7bd6894397 | ||
|
a8b066ee50 | ||
|
d8023afaee | ||
|
363ee91b6b | ||
|
6bf64edb32 | ||
|
183bd93d31 | ||
|
7f197c3719 | ||
|
cae6f16623 | ||
|
f1318a5223 | ||
|
e5510f13d0 | ||
|
b21573b1dc | ||
|
8b8fc32f80 | ||
|
68a2888d35 | ||
|
23bd56ee21 | ||
|
c523a1e310 | ||
|
fa27ee715a | ||
|
bd8fa3e536 | ||
|
3fdc69cfdd | ||
|
0c368ec9f2 | ||
|
904be27e42 | ||
|
aeb6d57505 | ||
|
e3923335b8 | ||
|
a46e72c1e2 | ||
|
64037b205f | ||
|
b54f163d3e | ||
|
a206df590f | ||
|
d039aba84b | ||
|
ac5b8a4c98 | ||
|
01b1fcce16 | ||
|
ebe5b03e36 | ||
|
47f6fead79 | ||
|
9146041ce6 | ||
|
350db80eed | ||
|
2a1d986f19 | ||
|
6a06d87a4c | ||
|
dc5b8c026d | ||
|
88d1b7f1d2 | ||
|
f094aeabc6 | ||
|
77a14311aa | ||
|
96c69f3def | ||
|
30ce01c017 | ||
|
bb92628716 | ||
|
680f66db6e | ||
|
572d2a8b5a | ||
|
4fb82c2e7c | ||
|
1de9307c5d | ||
|
7af9e353af | ||
|
370eb182d8 | ||
|
22577e0fdc | ||
|
60713fc88f | ||
|
316383671a | ||
|
37c4e2e52b | ||
|
14c3c13699 | ||
|
11c2cbc950 | ||
|
93e189545c | ||
|
32d2c59f4c | ||
|
477fc437ce | ||
|
6a4d426109 | ||
|
2c27d6b6b3 | ||
|
8a5121bd90 | ||
|
d08fb1d445 | ||
|
f96a0e3d76 | ||
|
13bcd5226e | ||
|
b97c8f8954 | ||
|
8f98da4767 | ||
|
fe18d2a3fd | ||
|
f9e3bdfbce | ||
|
f77bfb1705 | ||
|
132d9355bd | ||
|
4c7e03dc57 | ||
|
75f4d26344 | ||
|
21d93e7c9f | ||
|
b03e6d8298 | ||
|
879975a8ca | ||
|
4b154128ed | ||
|
f4a8458246 | ||
|
da08c96af6 | ||
|
b723d015bc | ||
|
154372e130 | ||
|
efd8150a55 | ||
|
e72eaef7a1 | ||
|
3c4e557517 | ||
|
05e7d07d40 | ||
|
7af4657238 | ||
|
1a22a52096 | ||
|
6454c329fa | ||
|
d5e44fe9a5 | ||
|
1d3991f52e | ||
|
edd503f262 | ||
|
99c939e6d0 | ||
|
7316f77367 | ||
|
343fd6f84b | ||
|
96e071aa37 | ||
|
3b214f913f | ||
|
953e4ec5b9 | ||
|
ccaab1c8f3 | ||
|
4e5ba7945c | ||
|
c67f8dbadf | ||
|
f82e3c0c2d | ||
|
de8a7c3b4c | ||
|
cc370aac0b | ||
|
c359e8b28b | ||
|
e9a9a79142 | ||
|
675e958b66 | ||
|
00c4a30481 | ||
|
e7dcffe004 | ||
|
a62a069166 | ||
|
d4b886099c | ||
|
3b71a0355b | ||
|
ff792b81f6 | ||
|
2baed03356 | ||
|
7c37c80a9a | ||
|
cff6891a8d | ||
|
58d8f95588 | ||
|
293763cc1e | ||
|
96352d21a0 | ||
|
6f85d3d1e8 | ||
|
d3b3cb6c23 | ||
|
2c0d4d797b | ||
|
50bd14a82e | ||
|
cdff35c0e2 | ||
|
f3e63c3031 | ||
|
ac7cf07d5d | ||
|
cc619466df | ||
|
c29757ad9f | ||
|
46d1027982 | ||
|
5f025c9b91 | ||
|
2672550bbd | ||
|
d0afb329f1 | ||
|
67940c5066 | ||
|
26675216ad | ||
|
5e1520a732 | ||
|
7ff876a56f | ||
|
f7d899b0f7 | ||
|
c32f13fbe1 | ||
|
7c8f4c0e4e | ||
|
57faed9d8f | ||
|
860da38c99 | ||
|
2b5eaa7990 | ||
|
1d4d5f397a | ||
|
c5956ee7d3 | ||
|
0426fc57cc | ||
|
b06fb6f101 | ||
|
3f630c73e9 | ||
|
ccafb37bbe | ||
|
c765e98047 | ||
|
029e12ad7b | ||
|
1be31e800e | ||
|
f190b8c5e4 | ||
|
1ab01a7d25 | ||
|
ea0b00e4dc | ||
|
9ede8f2148 | ||
|
5833d57c8d | ||
|
ed7d6a9378 | ||
|
aa11f442bc | ||
|
9715eb3a2e | ||
|
fd3e8218d7 | ||
|
690858e342 | ||
|
cc943330f2 |
@ -1,9 +0,0 @@
|
||||
Autotest.add_hook(:initialize) do |at|
|
||||
at.add_mapping(%r{bin/.*}, true) do |filename|
|
||||
at.files_matching(%r{spec/bin/.*})
|
||||
end
|
||||
|
||||
at.add_mapping(%r{spec/jasmine/.*}, true) do |filename|
|
||||
at.files_matching(%r{spec/bin/.*})
|
||||
end
|
||||
end
|
11
.gitignore
vendored
11
.gitignore
vendored
@ -6,3 +6,14 @@ Makefile
|
||||
specrunner.moc
|
||||
specrunner.o
|
||||
ext/jasmine-webkit-specrunner/jasmine-webkit-specrunner
|
||||
*.o
|
||||
moc_*.*
|
||||
.DS_Store
|
||||
hydra-runner.log
|
||||
jhw-test
|
||||
.jhw-cache/
|
||||
_site/
|
||||
jhw.*.html
|
||||
coverage/
|
||||
tmp/
|
||||
cache dir/
|
||||
|
89
CHANGELOG.md
Normal file
89
CHANGELOG.md
Normal 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
|
||||
|
26
Gemfile
26
Gemfile
@ -1,8 +1,30 @@
|
||||
source "http://rubygems.org"
|
||||
source :rubygems
|
||||
|
||||
# Specify your gem's dependencies in jasmine-headless-webkit.gemspec
|
||||
gemspec
|
||||
|
||||
gem 'rspec'
|
||||
gem 'autotest'
|
||||
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'
|
||||
|
||||
|
41
Guardfile
Normal file
41
Guardfile
Normal file
@ -0,0 +1,41 @@
|
||||
|
||||
# Add files and commands to this file, like the example:
|
||||
# watch('file/path') { `command(s)` }
|
||||
#
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
106
README.md
106
README.md
@ -1,102 +1,24 @@
|
||||
_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!
|
||||
|
||||
## Introduction
|
||||
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.
|
||||
|
||||
This gem works with projects that have used the [Jasmine gem](https://github.com/pivotal/jasmine-gem) to
|
||||
create a `jasmine.yml` file that defines what to test. The runner loads that
|
||||
`jasmine.yml` file and executes the tests defined within in a Qt WebKit widget, displaying the results
|
||||
to the console and setting the exit code to one of the following:
|
||||
## For those who want to hack on the project...
|
||||
|
||||
* 0 for success
|
||||
* 1 for spec run failure
|
||||
* 2 for spec run success, but `console.log` was called during the run
|
||||
The best way to get everything running that you need for development and testing is
|
||||
to use Guard:
|
||||
|
||||
`console.log` works, too, so you can run your specs side-by-side in a browser if you're so inclined. It
|
||||
serializes whatever you're passing in as as JSON string, so objects that are cyclical in nature will not
|
||||
serialize. If anyone has a good solution for this, please suggest and/or fork'n'fix.
|
||||
|
||||
## Installation
|
||||
|
||||
`gem install jasmine-headless-webkit` or use Bundler.
|
||||
|
||||
Installation requires Qt 4.7. See [senchalabs/examples](https://github.com/senchalabs/examples) and [my fork
|
||||
of examples](https://github.com/johnbintz/examples) for more information on the QtWebKit runner.
|
||||
|
||||
Tested in the following environments:
|
||||
|
||||
* Mac OS X 10.6, with MacPorts Qt and Nokia Qt.mpkg
|
||||
* Kubuntu 10.10
|
||||
|
||||
Let me know via a message or in the Issues section if it works on your setup and it's not listed!
|
||||
|
||||
## Usage
|
||||
|
||||
jasmine-headless-webkit [options] [list of spec files to run]
|
||||
|
||||
Current supported options:
|
||||
|
||||
* `-c`/`--colors` enables color output
|
||||
* `--no-colors` disables color output
|
||||
* `--keep` preserves the temporary HTML document if an error occurs in testing
|
||||
* `-j`/`--jasmine-config` sets the `jasmine.yml` file to load *(defaults to `spec/javascripts/support/jasmine.yml`)*
|
||||
|
||||
If provided, only the requested spec files will be executed. Otherwise, all matching specs will be run.
|
||||
|
||||
These options can also be placed into a `.jasmine-headless-webkit` file in your project root.
|
||||
|
||||
### CoffeeScript Support
|
||||
|
||||
`jasmine-headless-webkit` brings in the `coffee-script` gem and compiles & injects all CoffeeScript into the
|
||||
generated HTML page. All you need to do is configure your `jasmine.yml` file to look for .coffee files:
|
||||
|
||||
src_files:
|
||||
- app/assets/javascripts/**/*.coffee
|
||||
spec_files:
|
||||
- **/*[sS]pec.coffee
|
||||
|
||||
*(This will probably make it difficult to test your code in an official Jasmine server for now. You can try
|
||||
[a technique like this](https://github.com/jbaudanza/rack-asset-compiler/blob/master/examples/jasmine_config.rb) for compiling CoffeeScript when it's requested from the server
|
||||
or use [this fork of jasmine-gem](https://github.com/johnbintz/jasmine-gem/tree/coffeescript-inline-support) which
|
||||
is thoroughly untested.)*
|
||||
|
||||
You will get line numbers on compile errors, but not logic errors. This is a CoffeeScript thing, and they're working on it. :)
|
||||
|
||||
### JavaScript Dialogs
|
||||
|
||||
You can call `alert()` and `confirm()` in your code. `alert()` will print the message to the console, and
|
||||
`confirm()` will always return true. There's no way right now to respond to `confirm()`, so it's best to
|
||||
mock that call:
|
||||
|
||||
spyOn(window, 'confirm').andReturn(false);
|
||||
|
||||
### Autotest Integration
|
||||
|
||||
`jasmine-headless-webkit` can integrate with Autotest. Your `jasmine.yml` file needs to be in the default
|
||||
path, and you have to be ready to use a very alpha implementation of the feature. If used with RSpec 2,
|
||||
Jasmine tests run after RSpec tests.
|
||||
|
||||
You need to create a `.jasmine-headless-webkit` file in your project root for this integration
|
||||
to work.
|
||||
|
||||
`jasmine-headless-webkit` provides two new hooks: `:run_jasmine` and `:ran_jasmine` for before and after the
|
||||
Jasmine specs have run. This is a good place to do things like re-package all your assets using
|
||||
[Jammit](http://documentcloud.github.com/jammit/):
|
||||
|
||||
Autotest.add_hook(:run_jasmine) do |at|
|
||||
system %{jammit}
|
||||
end
|
||||
|
||||
### Server Interaction
|
||||
|
||||
`jasmine-headless-webkit` works the same as if you create an HTML file, manually load the Jasmine library and
|
||||
your code & tests into the page, and open that page in a browser. Because of this, there's no way to handle
|
||||
server interaction with your application or with a Jasmine server. If you need to test server interaction,
|
||||
do one of the following:
|
||||
|
||||
* Stub your server responses using [Sinon.JS](http://sinonjs.org/)
|
||||
* Use [PhantomJS](http://www.phantomjs.org/) against a running copy of a Jasmine server, instead of this project
|
||||
``` bash
|
||||
bundle install
|
||||
bundle exec guard
|
||||
... build Qt runner ...
|
||||
... compile CoffeeScript to JS ...
|
||||
... run RSpec and JHW ...
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
|
63
Rakefile
63
Rakefile
@ -1,2 +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
|
||||
|
||||
|
@ -2,110 +2,10 @@
|
||||
|
||||
require 'rubygems'
|
||||
|
||||
gem_dir = File.expand_path('../..', __FILE__)
|
||||
$:.unshift(File.join(gem_dir, 'lib'))
|
||||
$: << File.expand_path('../../lib', __FILE__)
|
||||
|
||||
require 'yaml'
|
||||
require 'fileutils'
|
||||
require 'getoptlong'
|
||||
|
||||
gem 'jasmine'
|
||||
gem 'coffee-script'
|
||||
gem 'rainbow'
|
||||
|
||||
require 'jasmine'
|
||||
require 'jasmine-headless-webkit'
|
||||
require 'coffee-script'
|
||||
require 'rainbow'
|
||||
|
||||
require 'jasmine/cli'
|
||||
include Jasmine::CLI
|
||||
|
||||
opts = GetoptLong.new(
|
||||
[ '--colors', '-c', GetoptLong::NO_ARGUMENT ],
|
||||
[ '--no-colors', GetoptLong::NO_ARGUMENT ],
|
||||
[ '--keep', GetoptLong::NO_ARGUMENT ],
|
||||
[ '--jasmine-config', '-j', GetoptLong::REQUIRED_ARGUMENT ]
|
||||
)
|
||||
|
||||
options = {
|
||||
:colors => false,
|
||||
:remove_html_file => true,
|
||||
:jasmine_config => 'spec/javascripts/support/jasmine.yml'
|
||||
}
|
||||
|
||||
@process_options = lambda { |*args|
|
||||
opt, arg = args.flatten[0..1]
|
||||
|
||||
case opt
|
||||
when '--colors', '-c'
|
||||
options[:colors] = true
|
||||
when '--no-colors', '-nc'
|
||||
options[:colors] = false
|
||||
when '--keep', '-k'
|
||||
options[:remove_html_file] = false
|
||||
when '--jasmine-config', '-j'
|
||||
options[:jasmine_config] = arg
|
||||
end
|
||||
}
|
||||
|
||||
read_defaults_file if defaults_file?
|
||||
opts.each(&@process_options)
|
||||
|
||||
@spec_filter = ARGV.dup
|
||||
|
||||
data = YAML.load_file(options[:jasmine_config])
|
||||
|
||||
if !File.file?(File.join(gem_dir, RUNNER))
|
||||
puts "The Qt WebKit widget is not compiled! Try re-installing this gem."
|
||||
exit 1
|
||||
end
|
||||
|
||||
puts "Running Jasmine specs..."
|
||||
|
||||
files = %w{jasmine jasmine-html}.collect { |name| File.join(Jasmine.root, "lib/#{name}.js") }
|
||||
files << File.join(gem_dir, 'jasmine/jasmine.headless-reporter.js')
|
||||
|
||||
files += [ [ 'src_files', 'src_dir' ], [ 'stylesheets', 'src_dir' ], [ 'helpers', 'spec_dir' ], [ 'spec_files', 'spec_dir' ] ].collect do |searches, root|
|
||||
data[searches] ||= DEFAULTS[searches]
|
||||
data[root] ||= DEFAULTS[root]
|
||||
|
||||
if data[searches]
|
||||
data[searches].collect do |search|
|
||||
path = search
|
||||
path = File.join(data[root], path) if data[root]
|
||||
files = Dir[path]
|
||||
if searches == 'spec_files'
|
||||
files = files.find_all { |file| use_spec?(file) }
|
||||
end
|
||||
files
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
files = files.flatten.compact.collect { |file|
|
||||
case File.extname(file)
|
||||
when '.js'
|
||||
%{<script type="text/javascript" src="#{file}"></script>}
|
||||
when '.coffee'
|
||||
begin
|
||||
%{<script type="text/javascript">#{CoffeeScript.compile(fh = File.open(file))}</script>}
|
||||
rescue CoffeeScript::CompilationError => e
|
||||
puts "[%s] %s: %s" % [ 'coffeescript'.color(:red), file.color(:yellow), e.message.color(:white) ]
|
||||
exit 1
|
||||
ensure
|
||||
fh.close
|
||||
end
|
||||
when '.css'
|
||||
%{<link rel="stylesheet" href="#{file}" type="text/css" />}
|
||||
end
|
||||
}
|
||||
|
||||
output = jasmine_html_template(files)
|
||||
|
||||
File.open(target = "specrunner.#{$$}.html", 'w') { |fh| fh.print output }
|
||||
system %{#{File.join(gem_dir, RUNNER)} #{options[:colors] ? '-c' : ''} #{target}}
|
||||
status = $?.exitstatus
|
||||
FileUtils.rm_f target if options[:remove_html_file] || (status == 0)
|
||||
|
||||
exit status
|
||||
Jasmine::Headless::CommandLine.run!
|
||||
|
||||
|
@ -1,13 +0,0 @@
|
||||
require 'coffee-script'
|
||||
|
||||
watch('(jasmine/.*)\.coffee') { |m| coffee(m[1]) }
|
||||
|
||||
def coffee(file)
|
||||
begin
|
||||
File.open(file + '.js', 'w') { |fh| fh.print CoffeeScript.compile File.open(file + '.coffee') }
|
||||
puts "Wrote #{file}"
|
||||
rescue Exception => e
|
||||
puts e.message
|
||||
end
|
||||
end
|
||||
|
2
config/cucumber.yml
Normal file
2
config/cucumber.yml
Normal file
@ -0,0 +1,2 @@
|
||||
default: -r features
|
||||
|
5
ext/jasmine-webkit-specrunner/Makefile.dummy
Normal file
5
ext/jasmine-webkit-specrunner/Makefile.dummy
Normal file
@ -0,0 +1,5 @@
|
||||
build:
|
||||
true
|
||||
install:
|
||||
true
|
||||
|
15
ext/jasmine-webkit-specrunner/Page.cpp
Normal file
15
ext/jasmine-webkit-specrunner/Page.cpp
Normal 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; }
|
20
ext/jasmine-webkit-specrunner/Page.h
Normal file
20
ext/jasmine-webkit-specrunner/Page.h
Normal 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
|
208
ext/jasmine-webkit-specrunner/Runner.cpp
Normal file
208
ext/jasmine-webkit-specrunner/Runner.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
73
ext/jasmine-webkit-specrunner/Runner.h
Normal file
73
ext/jasmine-webkit-specrunner/Runner.h
Normal 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
|
8
ext/jasmine-webkit-specrunner/common.pri
Normal file
8
ext/jasmine-webkit-specrunner/common.pri
Normal 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
|
||||
|
@ -1,2 +1,10 @@
|
||||
system %{qmake -spec macx-g++}
|
||||
system %{make}
|
||||
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__)
|
||||
|
@ -1,320 +1,83 @@
|
||||
/*
|
||||
Copyright (c) 2010 Sencha Inc.
|
||||
Copyright (c) 2011 John Bintz
|
||||
Copyright (c) 2010 Sencha Inc.
|
||||
Copyright (c) 2011 John Bintz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <QtGui>
|
||||
#include <QtWebKit>
|
||||
#include <iostream>
|
||||
#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
|
||||
|
||||
class HeadlessSpecRunnerPage: public QWebPage
|
||||
{
|
||||
Q_OBJECT
|
||||
signals:
|
||||
void consoleLog(const QString &msg, int lineNumber, const QString &sourceID);
|
||||
void internalLog(const QString ¬e, const QString &msg);
|
||||
protected:
|
||||
void javaScriptConsoleMessage(const QString & message, int lineNumber, const QString & sourceID);
|
||||
bool javaScriptConfirm(QWebFrame *frame, const QString &msg);
|
||||
void javaScriptAlert(QWebFrame *frame, const QString &msg);
|
||||
};
|
||||
|
||||
void HeadlessSpecRunnerPage::javaScriptConsoleMessage(const QString &message, int lineNumber, const QString &sourceID)
|
||||
{
|
||||
emit consoleLog(message, lineNumber, sourceID);
|
||||
}
|
||||
|
||||
bool HeadlessSpecRunnerPage::javaScriptConfirm(QWebFrame *frame, const QString &msg)
|
||||
{
|
||||
emit internalLog("TODO", "jasmine-headless-webkit can't handle confirm() yet! You should mock window.confirm for now. Returning true.");
|
||||
return true;
|
||||
}
|
||||
|
||||
void HeadlessSpecRunnerPage::javaScriptAlert(QWebFrame *frame, const QString &msg)
|
||||
{
|
||||
emit internalLog("alert", msg);
|
||||
}
|
||||
|
||||
class HeadlessSpecRunner: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
HeadlessSpecRunner();
|
||||
void load(const QString &spec);
|
||||
void setColors(bool colors);
|
||||
public slots:
|
||||
void log(const QString &msg);
|
||||
void specPassed();
|
||||
void specFailed();
|
||||
void printName(const QString &name);
|
||||
void printResult(const QString &result);
|
||||
void finishSuite(const QString &duration, const QString &total, const QString& failed);
|
||||
private slots:
|
||||
void watch(bool ok);
|
||||
void errorLog(const QString &msg, int lineNumber, const QString &sourceID);
|
||||
void internalLog(const QString ¬e, const QString &msg);
|
||||
protected:
|
||||
bool hasElement(const char *select);
|
||||
void timerEvent(QTimerEvent *event);
|
||||
private:
|
||||
HeadlessSpecRunnerPage m_page;
|
||||
QBasicTimer m_ticker;
|
||||
int m_runs;
|
||||
bool hasErrors;
|
||||
bool usedConsole;
|
||||
bool showColors;
|
||||
bool isFinished;
|
||||
bool didFail;
|
||||
bool consoleNotUsedThisRun;
|
||||
|
||||
void red();
|
||||
void green();
|
||||
void yellow();
|
||||
void clear();
|
||||
};
|
||||
|
||||
HeadlessSpecRunner::HeadlessSpecRunner()
|
||||
: QObject()
|
||||
, m_runs(0)
|
||||
, hasErrors(false)
|
||||
, usedConsole(false)
|
||||
, showColors(false)
|
||||
, isFinished(false)
|
||||
, didFail(false)
|
||||
, consoleNotUsedThisRun(false)
|
||||
{
|
||||
m_page.settings()->enablePersistentStorage();
|
||||
connect(&m_page, SIGNAL(loadFinished(bool)), this, SLOT(watch(bool)));
|
||||
connect(&m_page, SIGNAL(consoleLog(QString, int, QString)), this, SLOT(errorLog(QString, int, QString)));
|
||||
connect(&m_page, SIGNAL(internalLog(QString, QString)), this, SLOT(internalLog(QString, QString)));
|
||||
}
|
||||
|
||||
void HeadlessSpecRunner::load(const QString &spec)
|
||||
{
|
||||
m_ticker.stop();
|
||||
m_page.mainFrame()->addToJavaScriptWindowObject("JHW", this);
|
||||
m_page.mainFrame()->load(spec);
|
||||
m_page.setPreferredContentsSize(QSize(1024, 600));
|
||||
}
|
||||
|
||||
void HeadlessSpecRunner::watch(bool ok)
|
||||
{
|
||||
if (!ok) {
|
||||
std::cerr << "Can't load " << qPrintable(m_page.mainFrame()->url().toString()) << ", the file may be broken." << std::endl;
|
||||
std::cerr << "Out of curiosity, did your tests try to submit a form and you haven't prevented that?" << std::endl;
|
||||
std::cerr << "Try running your tests in your browser with the Jasmine server and see what happens." << std::endl;
|
||||
QApplication::instance()->exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
m_ticker.start(200, this);
|
||||
}
|
||||
|
||||
bool HeadlessSpecRunner::hasElement(const char *select)
|
||||
{
|
||||
return !m_page.mainFrame()->findFirstElement(select).isNull();
|
||||
}
|
||||
|
||||
void HeadlessSpecRunner::setColors(bool colors)
|
||||
{
|
||||
showColors = colors;
|
||||
}
|
||||
|
||||
void HeadlessSpecRunner::red()
|
||||
{
|
||||
if (showColors) std::cout << "\033[0;31m";
|
||||
}
|
||||
|
||||
void HeadlessSpecRunner::green()
|
||||
{
|
||||
if (showColors) std::cout << "\033[0;32m";
|
||||
}
|
||||
|
||||
void HeadlessSpecRunner::yellow()
|
||||
{
|
||||
if (showColors) std::cout << "\033[0;33m";
|
||||
}
|
||||
|
||||
void HeadlessSpecRunner::clear()
|
||||
{
|
||||
if (showColors) std::cout << "\033[m";
|
||||
}
|
||||
|
||||
void HeadlessSpecRunner::specPassed()
|
||||
{
|
||||
consoleNotUsedThisRun = true;
|
||||
green();
|
||||
std::cout << '.';
|
||||
clear();
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void HeadlessSpecRunner::specFailed()
|
||||
{
|
||||
consoleNotUsedThisRun = true;
|
||||
didFail = true;
|
||||
red();
|
||||
std::cout << 'F';
|
||||
clear();
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void HeadlessSpecRunner::errorLog(const QString &msg, int lineNumber, const QString &sourceID)
|
||||
{
|
||||
red();
|
||||
std::cout << "[error] ";
|
||||
clear();
|
||||
std::cout << qPrintable(sourceID) << ":" << lineNumber << " : " << qPrintable(msg);
|
||||
std::cout << std::endl;
|
||||
|
||||
hasErrors = true;
|
||||
m_runs = 0;
|
||||
m_ticker.start(200, this);
|
||||
}
|
||||
|
||||
void HeadlessSpecRunner::internalLog(const QString ¬e, const QString &msg) {
|
||||
red();
|
||||
std::cout << "[" << qPrintable(note) << "] ";
|
||||
clear();
|
||||
std::cout << qPrintable(msg);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
void HeadlessSpecRunner::log(const QString &msg)
|
||||
{
|
||||
usedConsole = true;
|
||||
green();
|
||||
if (consoleNotUsedThisRun) {
|
||||
std::cout << std::endl;
|
||||
consoleNotUsedThisRun = false;
|
||||
}
|
||||
std::cout << "[console] ";
|
||||
clear();
|
||||
std::cout << qPrintable(msg);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
void HeadlessSpecRunner::printName(const QString &name)
|
||||
{
|
||||
std::cout << std::endl << std::endl;
|
||||
red();
|
||||
std::cout << qPrintable(name) << std::endl;
|
||||
clear();
|
||||
}
|
||||
|
||||
void HeadlessSpecRunner::printResult(const QString &result)
|
||||
{
|
||||
red();
|
||||
std::cout << " " << qPrintable(result) << std::endl;
|
||||
clear();
|
||||
}
|
||||
|
||||
void HeadlessSpecRunner::finishSuite(const QString &duration, const QString &total, const QString& failed)
|
||||
{
|
||||
std::cout << std::endl;
|
||||
if (didFail) {
|
||||
red();
|
||||
std::cout << "FAIL: ";
|
||||
} else {
|
||||
green();
|
||||
std::cout << "PASS: ";
|
||||
}
|
||||
|
||||
std::cout << qPrintable(total) << " tests, " << qPrintable(failed) << " failures, " << qPrintable(duration) << " secs.";
|
||||
clear();
|
||||
std::cout << std::endl;
|
||||
|
||||
isFinished = true;
|
||||
}
|
||||
|
||||
void HeadlessSpecRunner::timerEvent(QTimerEvent *event)
|
||||
{
|
||||
++m_runs;
|
||||
|
||||
if (event->timerId() != m_ticker.timerId())
|
||||
return;
|
||||
|
||||
if (hasErrors && m_runs > 2)
|
||||
QApplication::instance()->exit(1);
|
||||
|
||||
if (!hasErrors) {
|
||||
if (isFinished) {
|
||||
int exitCode = 0;
|
||||
if (didFail) {
|
||||
exitCode = 1;
|
||||
} else {
|
||||
if (usedConsole) {
|
||||
exitCode = 2;
|
||||
}
|
||||
}
|
||||
|
||||
QApplication::instance()->exit(exitCode);
|
||||
}
|
||||
|
||||
if (m_runs > 30) {
|
||||
std::cout << "WARNING: too many runs and the test is still not finished!" << std::endl;
|
||||
QApplication::instance()->exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include "specrunner.moc"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
bool showColors = false;
|
||||
char *filename = NULL;
|
||||
bool showColors = false;
|
||||
bool isQuiet = false;
|
||||
QString seed;
|
||||
QStack<QString> reporterFiles;
|
||||
|
||||
int c, index;
|
||||
int c, index;
|
||||
|
||||
while ((c = getopt(argc, argv, "c")) != -1) {
|
||||
switch(c) {
|
||||
case 'c':
|
||||
showColors = true;
|
||||
break;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
bool filenameFound = false;
|
||||
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;
|
||||
}
|
||||
|
||||
for (index = optind; index < argc; index++) {
|
||||
filename = argv[index];
|
||||
filenameFound = true;
|
||||
}
|
||||
QApplication app(argc, argv);
|
||||
app.setApplicationName("jasmine-headless-webkit");
|
||||
Runner runner;
|
||||
|
||||
if (!filenameFound) {
|
||||
std::cerr << "Run Jasmine's SpecRunner headlessly" << std::endl << std::endl;
|
||||
std::cerr << " specrunner [-c] SpecRunner.html" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
runner.setColors(showColors);
|
||||
runner.setQuiet(isQuiet);
|
||||
runner.setReportFiles(reporterFiles);
|
||||
runner.setSeed(seed);
|
||||
|
||||
QApplication app(argc, argv);
|
||||
for (index = optind; index < argc; index++) {
|
||||
runner.addFile(QString::fromLocal8Bit(argv[index]));
|
||||
}
|
||||
|
||||
HeadlessSpecRunner runner;
|
||||
runner.setColors(showColors);
|
||||
runner.load(QString::fromLocal8Bit(filename));
|
||||
return app.exec();
|
||||
runner.go();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
TEMPLATE = app
|
||||
CONFIG -= app_bundle
|
||||
include(common.pri)
|
||||
|
||||
SOURCES += specrunner.cpp
|
||||
TARGET = jasmine-webkit-specrunner
|
||||
SOURCES = specrunner.cpp
|
||||
QT += network webkit
|
||||
QMAKE_INFO_PLIST = Info.plist
|
||||
QMAKESPEC = macx-gcc
|
||||
|
||||
|
7
features/bin/failure.feature
Normal file
7
features/bin/failure.feature
Normal 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
|
||||
|
7
features/bin/files.feature
Normal file
7
features/bin/files.feature
Normal 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"
|
18
features/bin/filtered_run/both_runs.feature
Normal file
18
features/bin/filtered_run/both_runs.feature
Normal 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
|
14
features/bin/filtered_run/no_full_run.feature
Normal file
14
features/bin/filtered_run/no_full_run.feature
Normal 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
|
||||
|
7
features/bin/help.feature
Normal file
7
features/bin/help.feature
Normal 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
|
||||
|
8
features/bin/quiet_messages.feature
Normal file
8
features/bin/quiet_messages.feature
Normal 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"
|
||||
|
8
features/bin/runner_out.feature
Normal file
8
features/bin/runner_out.feature
Normal 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"
|
||||
|
7
features/bin/spec_files_with_same_basename.feature
Normal file
7
features/bin/spec_files_with_same_basename.feature
Normal 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
|
||||
|
20
features/bin/success.feature
Normal file
20
features/bin/success.feature
Normal 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
|
||||
|
5
features/bin/success_with_js_error.feature
Normal file
5
features/bin/success_with_js_error.feature
Normal 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
|
7
features/bin/tries_to_leave_page.feature
Normal file
7
features/bin/tries_to_leave_page.feature
Normal 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
|
||||
|
7
features/bin/try_to_click_a_button.feature
Normal file
7
features/bin/try_to_click_a_button.feature
Normal 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
|
||||
|
11
features/bin/two_files_from_src_files.feature
Normal file
11
features/bin/two_files_from_src_files.feature
Normal 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 |
|
||||
|
7
features/bin/with_coffeescript_error.feature
Normal file
7
features/bin/with_coffeescript_error.feature
Normal 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
|
||||
|
7
features/bin/with_console_log.feature
Normal file
7
features/bin/with_console_log.feature
Normal 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
|
||||
|
7
features/bin/with_server.feature
Normal file
7
features/bin/with_server.feature
Normal 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
|
||||
|
7
features/bin/with_window_prompt.feature
Normal file
7
features/bin/with_window_prompt.feature
Normal 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"
|
||||
|
24
features/reporters.feature
Normal file
24
features/reporters.feature
Normal 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
34
features/runner.feature
Normal 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
|
||||
|
2
features/steps/given/i_have_test_suite.rb
Normal file
2
features/steps/given/i_have_test_suite.rb
Normal file
@ -0,0 +1,2 @@
|
||||
Given /^I have a test suite$/ do
|
||||
end
|
4
features/steps/given/no_existing_file.rb
Normal file
4
features/steps/given/no_existing_file.rb
Normal file
@ -0,0 +1,4 @@
|
||||
Given /^there is no existing "([^"]*)" file$/ do |file|
|
||||
FileUtils.rm_rf file
|
||||
end
|
||||
|
4
features/steps/given/options/i_have_defaults.rb
Normal file
4
features/steps/given/options/i_have_defaults.rb
Normal file
@ -0,0 +1,4 @@
|
||||
Given /^I have the default runner options$/ do
|
||||
@options = Jasmine::Headless::Options.new
|
||||
end
|
||||
|
10
features/steps/given/options/i_have_reporters.rb
Normal file
10
features/steps/given/options/i_have_reporters.rb
Normal 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
|
3
features/steps/given/options/i_have_runner_options.rb
Normal file
3
features/steps/given/options/i_have_runner_options.rb
Normal file
@ -0,0 +1,3 @@
|
||||
Given /^I have the following runner options:$/ do |string|
|
||||
@options = YAML.load(string)
|
||||
end
|
3
features/steps/then/bin/exit_status_should_be.rb
Normal file
3
features/steps/then/bin/exit_status_should_be.rb
Normal file
@ -0,0 +1,3 @@
|
||||
Then /^the exit status should be (\d+)$/ do |exitstatus|
|
||||
$?.exitstatus.should == exitstatus.to_i
|
||||
end
|
4
features/steps/then/bin/file_should_contain_runner.rb
Normal file
4
features/steps/then/bin/file_should_contain_runner.rb
Normal file
@ -0,0 +1,4 @@
|
||||
Then /^the file "([^"]*)" should contain a JHW runner$/ do |file|
|
||||
File.read(file).should include('jasmine.HeadlessReporter')
|
||||
end
|
||||
|
10
features/steps/then/bin/following_files_loaded_in_order.rb
Normal file
10
features/steps/then/bin/following_files_loaded_in_order.rb
Normal 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
|
||||
|
4
features/steps/then/bin/i_should_get_help_output.rb
Normal file
4
features/steps/then/bin/i_should_get_help_output.rb
Normal file
@ -0,0 +1,4 @@
|
||||
Then /^I should get help output$/ do
|
||||
@output.should include("Usage:")
|
||||
end
|
||||
|
3
features/steps/then/bin/output_should_include.rb
Normal file
3
features/steps/then/bin/output_should_include.rb
Normal file
@ -0,0 +1,3 @@
|
||||
Then /^the output should include "([^"]*)"$/ do |string|
|
||||
@output.should include(string)
|
||||
end
|
4
features/steps/then/bin/output_should_not_include.rb
Normal file
4
features/steps/then/bin/output_should_not_include.rb
Normal file
@ -0,0 +1,4 @@
|
||||
Then /^the output should not include "([^"]*)"$/ do |string|
|
||||
@output.should_not include(string)
|
||||
end
|
||||
|
4
features/steps/then/reporting/report_does_not_exist.rb
Normal file
4
features/steps/then/reporting/report_does_not_exist.rb
Normal file
@ -0,0 +1,4 @@
|
||||
Then /^the report file "([^"]*)" should not exist$/ do |file|
|
||||
File.file?(file).should be_false
|
||||
end
|
||||
|
3
features/steps/then/reporting/report_should_exist.rb
Normal file
3
features/steps/then/reporting/report_should_exist.rb
Normal file
@ -0,0 +1,3 @@
|
||||
Then /^the report file "([^"]*)" should exist$/ do |file|
|
||||
File.file?(file).should be_true
|
||||
end
|
7
features/steps/then/reporting/report_should_have.rb
Normal file
7
features/steps/then/reporting/report_should_have.rb
Normal 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
|
4
features/steps/then/reporting/report_should_have_seed.rb
Normal file
4
features/steps/then/reporting/report_should_have_seed.rb
Normal 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
|
3
features/steps/then/runner/it_should_have_exit_status.rb
Normal file
3
features/steps/then/runner/it_should_have_exit_status.rb
Normal 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
|
@ -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
|
||||
|
@ -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
|
6
features/steps/then/templates/it_should_use_reporter.rb
Normal file
6
features/steps/then/templates/it_should_use_reporter.rb
Normal 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
|
||||
|
3
features/steps/when/i_delete_file.rb
Normal file
3
features/steps/when/i_delete_file.rb
Normal file
@ -0,0 +1,3 @@
|
||||
When /^I delete the file "([^"]*)"$/ do |file|
|
||||
FileUtils.rm_f(file)
|
||||
end
|
4
features/steps/when/i_get_runner.rb
Normal file
4
features/steps/when/i_get_runner.rb
Normal file
@ -0,0 +1,4 @@
|
||||
When /^I get a runner$/ do
|
||||
@runner = Jasmine::Headless::Runner.new(@options)
|
||||
end
|
||||
|
4
features/steps/when/i_get_template_writer.rb
Normal file
4
features/steps/when/i_get_template_writer.rb
Normal file
@ -0,0 +1,4 @@
|
||||
When /^I get a template writer$/ do
|
||||
@template_writer = Jasmine::Headless::TemplateWriter.new(@runner)
|
||||
end
|
||||
|
4
features/steps/when/i_run_executable.rb
Normal file
4
features/steps/when/i_run_executable.rb
Normal file
@ -0,0 +1,4 @@
|
||||
When /^I run `(.*)`$/ do |command|
|
||||
@output = `#{command}`
|
||||
end
|
||||
|
4
features/steps/when/i_run_runner.rb
Normal file
4
features/steps/when/i_run_runner.rb
Normal file
@ -0,0 +1,4 @@
|
||||
When /^I run the runner$/ do
|
||||
@result = @runner.run
|
||||
end
|
||||
|
7
features/support/env.rb
Normal file
7
features/support/env.rb
Normal file
@ -0,0 +1,7 @@
|
||||
require 'jasmine-headless-webkit'
|
||||
|
||||
After do
|
||||
FileUtils.rm_f 'spec/report.txt'
|
||||
FileUtils.rm_f 'spec/runner.html'
|
||||
end
|
||||
|
@ -1,10 +1,10 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
$:.push File.expand_path("../lib", __FILE__)
|
||||
require "jasmine-headless-webkit/version"
|
||||
require "jasmine/headless/version"
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = "jasmine-headless-webkit"
|
||||
s.version = Jasmine::Headless::Webkit::VERSION
|
||||
s.version = Jasmine::Headless::VERSION
|
||||
s.platform = Gem::Platform::RUBY
|
||||
s.authors = ["John Bintz", "Sencha Inc.", "Pivotal Labs"]
|
||||
s.email = ["john@coswellproductions.com"]
|
||||
@ -20,7 +20,11 @@ Gem::Specification.new do |s|
|
||||
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
||||
s.require_paths = ["lib"]
|
||||
|
||||
s.add_dependency 'jasmine'
|
||||
s.add_dependency 'coffee-script'
|
||||
s.add_dependency 'rainbow'
|
||||
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,58 +0,0 @@
|
||||
class window.Inspector
|
||||
constructor: (data) ->
|
||||
@data = data
|
||||
@examinedObjects = []
|
||||
run: ->
|
||||
for property in this.inspect([], @data)
|
||||
do (property) =>
|
||||
JHW.log(property.report())
|
||||
inspect: (output, data, indent = 0, key = null) ->
|
||||
for obj in @examinedObjects
|
||||
if obj == data
|
||||
JHW.log("loop")
|
||||
output.push(new InspectedProperty("<< LOOP >>", key, indent))
|
||||
return output
|
||||
@examinedObjects.push(data)
|
||||
|
||||
switch typeof(data)
|
||||
when 'undefined'
|
||||
output.push(new InspectedProperty('undefined', key, indent))
|
||||
when 'string', 'number', 'boolean'
|
||||
output.push(new InspectedProperty(data, key, indent))
|
||||
else
|
||||
output.push(new DefinedObject(key, indent))
|
||||
for newKey, value of data
|
||||
JHW.log("trying #{newKey}")
|
||||
if data.hasOwnProperty(newKey)
|
||||
this.inspect(output, value, indent + 1, newKey)
|
||||
output
|
||||
|
||||
class window.IntendableProperty
|
||||
indentString: (output) ->
|
||||
if @key?
|
||||
output = "#{@key}: #{output}"
|
||||
if @indent > 0
|
||||
for i in [1..@indent]
|
||||
do (i) =>
|
||||
output = " " + output
|
||||
output
|
||||
|
||||
class window.DefinedObject extends window.IntendableProperty
|
||||
constructor: (key, indent) ->
|
||||
@key = key
|
||||
@indent = indent
|
||||
report: ->
|
||||
this.indentString("Object")
|
||||
|
||||
class window.InspectedProperty extends window.IntendableProperty
|
||||
constructor: (data, key, indent) ->
|
||||
@data = data
|
||||
@key = key
|
||||
@indent = indent
|
||||
report: ->
|
||||
output = switch typeof(@data)
|
||||
when 'string'
|
||||
"\"#{@data}\""
|
||||
else
|
||||
@data
|
||||
this.indentString(output)
|
@ -1,120 +0,0 @@
|
||||
(function() {
|
||||
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) {
|
||||
for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
|
||||
function ctor() { this.constructor = child; }
|
||||
ctor.prototype = parent.prototype;
|
||||
child.prototype = new ctor;
|
||||
child.__super__ = parent.prototype;
|
||||
return child;
|
||||
};
|
||||
window.Inspector = (function() {
|
||||
function Inspector(data) {
|
||||
this.data = data;
|
||||
this.examinedObjects = [];
|
||||
}
|
||||
Inspector.prototype.run = function() {
|
||||
var property, _i, _len, _ref, _results;
|
||||
_ref = this.inspect([], this.data);
|
||||
_results = [];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
property = _ref[_i];
|
||||
_results.push(__bind(function(property) {
|
||||
return JHW.log(property.report());
|
||||
}, this)(property));
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
Inspector.prototype.inspect = function(output, data, indent, key) {
|
||||
var newKey, obj, value, _i, _len, _ref;
|
||||
if (indent == null) {
|
||||
indent = 0;
|
||||
}
|
||||
if (key == null) {
|
||||
key = null;
|
||||
}
|
||||
_ref = this.examinedObjects;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
obj = _ref[_i];
|
||||
if (obj === data) {
|
||||
JHW.log("loop");
|
||||
output.push(new InspectedProperty("<< LOOP >>", key, indent));
|
||||
return output;
|
||||
}
|
||||
}
|
||||
this.examinedObjects.push(data);
|
||||
switch (typeof data) {
|
||||
case 'undefined':
|
||||
output.push(new InspectedProperty('undefined', key, indent));
|
||||
break;
|
||||
case 'string':
|
||||
case 'number':
|
||||
case 'boolean':
|
||||
output.push(new InspectedProperty(data, key, indent));
|
||||
break;
|
||||
default:
|
||||
output.push(new DefinedObject(key, indent));
|
||||
for (newKey in data) {
|
||||
value = data[newKey];
|
||||
JHW.log("trying " + newKey);
|
||||
if (data.hasOwnProperty(newKey)) {
|
||||
this.inspect(output, value, indent + 1, newKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
return output;
|
||||
};
|
||||
return Inspector;
|
||||
})();
|
||||
window.IntendableProperty = (function() {
|
||||
function IntendableProperty() {}
|
||||
IntendableProperty.prototype.indentString = function(output) {
|
||||
var i, _fn, _ref;
|
||||
if (this.key != null) {
|
||||
output = "" + this.key + ": " + output;
|
||||
}
|
||||
if (this.indent > 0) {
|
||||
_fn = __bind(function(i) {
|
||||
return output = " " + output;
|
||||
}, this);
|
||||
for (i = 1, _ref = this.indent; 1 <= _ref ? i <= _ref : i >= _ref; 1 <= _ref ? i++ : i--) {
|
||||
_fn(i);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
};
|
||||
return IntendableProperty;
|
||||
})();
|
||||
window.DefinedObject = (function() {
|
||||
function DefinedObject(key, indent) {
|
||||
this.key = key;
|
||||
this.indent = indent;
|
||||
}
|
||||
__extends(DefinedObject, window.IntendableProperty);
|
||||
DefinedObject.prototype.report = function() {
|
||||
return this.indentString("Object");
|
||||
};
|
||||
return DefinedObject;
|
||||
})();
|
||||
window.InspectedProperty = (function() {
|
||||
function InspectedProperty(data, key, indent) {
|
||||
this.data = data;
|
||||
this.key = key;
|
||||
this.indent = indent;
|
||||
}
|
||||
__extends(InspectedProperty, window.IntendableProperty);
|
||||
InspectedProperty.prototype.report = function() {
|
||||
var output;
|
||||
output = (function() {
|
||||
switch (typeof this.data) {
|
||||
case 'string':
|
||||
return "\"" + this.data + "\"";
|
||||
break;
|
||||
default:
|
||||
return this.data;
|
||||
}
|
||||
}).call(this);
|
||||
return this.indentString(output);
|
||||
};
|
||||
return InspectedProperty;
|
||||
})();
|
||||
}).call(this);
|
@ -1,43 +0,0 @@
|
||||
if !jasmine?
|
||||
throw new Exception("jasmine not laoded!")
|
||||
|
||||
class HeadlessReporterResult
|
||||
constructor: (name) ->
|
||||
@name = name
|
||||
@results = []
|
||||
addResult: (message) ->
|
||||
@results.push(message)
|
||||
print: ->
|
||||
JHW.printName(@name)
|
||||
for result in @results
|
||||
do (result) =>
|
||||
JHW.printResult(result)
|
||||
|
||||
class jasmine.HeadlessReporter
|
||||
constructor: ->
|
||||
@results = []
|
||||
@failedCount = 0
|
||||
@length = 0
|
||||
reportRunnerResults: (runner) ->
|
||||
for result in @results
|
||||
do (result) =>
|
||||
result.print()
|
||||
|
||||
JHW.finishSuite((new Date() - @startTime) / 1000.0, @length, @failedCount)
|
||||
reportRunnerStarting: (runner) ->
|
||||
@startTime = new Date()
|
||||
reportSpecResults: (spec) ->
|
||||
if spec.results().passed()
|
||||
JHW.specPassed()
|
||||
else
|
||||
JHW.specFailed()
|
||||
failureResult = new HeadlessReporterResult(spec.getFullName())
|
||||
for result in spec.results().getItems()
|
||||
do (result) =>
|
||||
if result.type == 'expect' and !result.passed_
|
||||
@failedCount += 1
|
||||
failureResult.addResult(result.message)
|
||||
@results.push(failureResult)
|
||||
reportSpecStarting: (spec) ->
|
||||
reportSuiteResults: (suite) ->
|
||||
@length += suite.specs().length
|
@ -1,78 +0,0 @@
|
||||
(function() {
|
||||
var HeadlessReporterResult;
|
||||
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
||||
if (!(typeof jasmine !== "undefined" && jasmine !== null)) {
|
||||
throw new Exception("jasmine not laoded!");
|
||||
}
|
||||
HeadlessReporterResult = (function() {
|
||||
function HeadlessReporterResult(name) {
|
||||
this.name = name;
|
||||
this.results = [];
|
||||
}
|
||||
HeadlessReporterResult.prototype.addResult = function(message) {
|
||||
return this.results.push(message);
|
||||
};
|
||||
HeadlessReporterResult.prototype.print = function() {
|
||||
var result, _i, _len, _ref, _results;
|
||||
JHW.printName(this.name);
|
||||
_ref = this.results;
|
||||
_results = [];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
result = _ref[_i];
|
||||
_results.push(__bind(function(result) {
|
||||
return JHW.printResult(result);
|
||||
}, this)(result));
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
return HeadlessReporterResult;
|
||||
})();
|
||||
jasmine.HeadlessReporter = (function() {
|
||||
function HeadlessReporter() {
|
||||
this.results = [];
|
||||
this.failedCount = 0;
|
||||
this.length = 0;
|
||||
}
|
||||
HeadlessReporter.prototype.reportRunnerResults = function(runner) {
|
||||
var result, _fn, _i, _len, _ref;
|
||||
_ref = this.results;
|
||||
_fn = __bind(function(result) {
|
||||
return result.print();
|
||||
}, this);
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
result = _ref[_i];
|
||||
_fn(result);
|
||||
}
|
||||
return JHW.finishSuite((new Date() - this.startTime) / 1000.0, this.length, this.failedCount);
|
||||
};
|
||||
HeadlessReporter.prototype.reportRunnerStarting = function(runner) {
|
||||
return this.startTime = new Date();
|
||||
};
|
||||
HeadlessReporter.prototype.reportSpecResults = function(spec) {
|
||||
var failureResult, result, _fn, _i, _len, _ref;
|
||||
if (spec.results().passed()) {
|
||||
return JHW.specPassed();
|
||||
} else {
|
||||
JHW.specFailed();
|
||||
failureResult = new HeadlessReporterResult(spec.getFullName());
|
||||
_ref = spec.results().getItems();
|
||||
_fn = __bind(function(result) {
|
||||
if (result.type === 'expect' && !result.passed_) {
|
||||
this.failedCount += 1;
|
||||
return failureResult.addResult(result.message);
|
||||
}
|
||||
}, this);
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
result = _ref[_i];
|
||||
_fn(result);
|
||||
}
|
||||
return this.results.push(failureResult);
|
||||
}
|
||||
};
|
||||
HeadlessReporter.prototype.reportSpecStarting = function(spec) {};
|
||||
HeadlessReporter.prototype.reportSuiteResults = function(suite) {
|
||||
return this.length += suite.specs().length;
|
||||
};
|
||||
return HeadlessReporter;
|
||||
})();
|
||||
}).call(this);
|
@ -1,7 +1,3 @@
|
||||
module Jasmine
|
||||
module Headless
|
||||
module Webkit
|
||||
# Your code goes here...
|
||||
end
|
||||
end
|
||||
end
|
||||
require 'jasmine/headless'
|
||||
require 'jasmine/headless/railtie' if defined?(Rails) && Rails::VERSION::MAJOR >= 3
|
||||
|
||||
|
@ -1,7 +0,0 @@
|
||||
module Jasmine
|
||||
module Headless
|
||||
module Webkit
|
||||
VERSION = "0.2.0"
|
||||
end
|
||||
end
|
||||
end
|
@ -1,55 +0,0 @@
|
||||
module Jasmine
|
||||
module CLI
|
||||
DEFAULTS = {
|
||||
'spec_files' => [ '**/*[sS]pec.js' ],
|
||||
'helpers' => [ 'helpers/**/*.js' ],
|
||||
'spec_dir' => 'spec/javascripts',
|
||||
'src_dir' => nil,
|
||||
'stylesheets' => [],
|
||||
'src_files' => []
|
||||
}
|
||||
|
||||
RUNNER = 'ext/jasmine-webkit-specrunner/jasmine-webkit-specrunner'
|
||||
DEFAULTS_FILE = '.jasmine-headless-webkit'
|
||||
|
||||
def process_jasmine_config(overrides = {})
|
||||
DEFAULTS.merge(overrides)
|
||||
end
|
||||
|
||||
def read_defaults_file
|
||||
File.readlines(DEFAULTS_FILE).collect { |line| line.strip.split(' ', 2) }.each(&@process_options)
|
||||
end
|
||||
|
||||
def defaults_file?
|
||||
File.file?(DEFAULTS_FILE)
|
||||
end
|
||||
|
||||
def use_spec?(file)
|
||||
@spec_filter.empty? || @spec_filter.include?(file)
|
||||
end
|
||||
|
||||
def jasmine_html_template(files)
|
||||
<<-HTML
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Jasmine Test Runner</title>
|
||||
<script type="text/javascript">
|
||||
window.console = { log: function(data) { JHW.log(JSON.stringify(data)) } }
|
||||
</script>
|
||||
#{files.join("\n")}
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="text/javascript">
|
||||
jasmine.getEnv().addReporter(new jasmine.HeadlessReporter());
|
||||
jasmine.getEnv().execute();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
HTML
|
||||
end
|
||||
end
|
||||
end
|
||||
|
61
lib/jasmine/headless.rb
Normal file
61
lib/jasmine/headless.rb
Normal 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'
|
||||
|
85
lib/jasmine/headless/cacheable_action.rb
Normal file
85
lib/jasmine/headless/cacheable_action.rb
Normal 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
|
||||
|
20
lib/jasmine/headless/coffee_script_cache.rb
Normal file
20
lib/jasmine/headless/coffee_script_cache.rb
Normal 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
|
||||
|
36
lib/jasmine/headless/coffee_template.rb
Normal file
36
lib/jasmine/headless/coffee_template.rb
Normal 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
|
||||
|
31
lib/jasmine/headless/command_line.rb
Normal file
31
lib/jasmine/headless/command_line.rb
Normal 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
|
||||
|
19
lib/jasmine/headless/css_template.rb
Normal file
19
lib/jasmine/headless/css_template.rb
Normal 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
|
||||
|
16
lib/jasmine/headless/errors.rb
Normal file
16
lib/jasmine/headless/errors.rb
Normal 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
|
||||
|
25
lib/jasmine/headless/file_checker.rb
Normal file
25
lib/jasmine/headless/file_checker.rb
Normal 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
|
332
lib/jasmine/headless/files_list.rb
Normal file
332
lib/jasmine/headless/files_list.rb
Normal 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
|
23
lib/jasmine/headless/js_template.rb
Normal file
23
lib/jasmine/headless/js_template.rb
Normal 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
|
||||
|
15
lib/jasmine/headless/jst_template.rb
Normal file
15
lib/jasmine/headless/jst_template.rb
Normal 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
|
||||
|
14
lib/jasmine/headless/nil_template.rb
Normal file
14
lib/jasmine/headless/nil_template.rb
Normal 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
|
||||
|
206
lib/jasmine/headless/options.rb
Normal file
206
lib/jasmine/headless/options.rb
Normal 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
|
||||
|
12
lib/jasmine/headless/railtie.rb
Normal file
12
lib/jasmine/headless/railtie.rb
Normal 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
|
||||
|
81
lib/jasmine/headless/report.rb
Normal file
81
lib/jasmine/headless/report.rb
Normal 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
|
||||
|
12
lib/jasmine/headless/report_message.rb
Normal file
12
lib/jasmine/headless/report_message.rb
Normal 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
|
||||
|
18
lib/jasmine/headless/report_message/console.rb
Normal file
18
lib/jasmine/headless/report_message/console.rb
Normal 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
|
||||
|
20
lib/jasmine/headless/report_message/error.rb
Normal file
20
lib/jasmine/headless/report_message/error.rb
Normal 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
|
||||
|
5
lib/jasmine/headless/report_message/fail.rb
Normal file
5
lib/jasmine/headless/report_message/fail.rb
Normal file
@ -0,0 +1,5 @@
|
||||
module Jasmine::Headless::ReportMessage
|
||||
class Fail < Spec
|
||||
end
|
||||
end
|
||||
|
5
lib/jasmine/headless/report_message/pass.rb
Normal file
5
lib/jasmine/headless/report_message/pass.rb
Normal file
@ -0,0 +1,5 @@
|
||||
module Jasmine::Headless::ReportMessage
|
||||
class Pass < Spec
|
||||
end
|
||||
end
|
||||
|
14
lib/jasmine/headless/report_message/seed.rb
Normal file
14
lib/jasmine/headless/report_message/seed.rb
Normal 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
|
||||
|
28
lib/jasmine/headless/report_message/spec.rb
Normal file
28
lib/jasmine/headless/report_message/spec.rb
Normal 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
|
||||
|
31
lib/jasmine/headless/report_message/total.rb
Normal file
31
lib/jasmine/headless/report_message/total.rb
Normal 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
|
||||
|
261
lib/jasmine/headless/runner.rb
Normal file
261
lib/jasmine/headless/runner.rb
Normal 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
|
||||
|
44
lib/jasmine/headless/spec_file_analyzer.rb
Normal file
44
lib/jasmine/headless/spec_file_analyzer.rb
Normal file
@ -0,0 +1,44 @@
|
||||
require 'multi_json'
|
||||
|
||||
module Jasmine::Headless
|
||||
class SpecFileAnalyzer < CacheableAction
|
||||
class << self
|
||||
def cache_type
|
||||
"spec_file_analysis"
|
||||
end
|
||||
end
|
||||
|
||||
def action
|
||||
line_numbers = {}
|
||||
|
||||
data = File.read(file)
|
||||
|
||||
if data.respond_to?(:encode)
|
||||
data.encode!('US-ASCII', 'UTF-8', :invalid => :replace, :undef => :replace)
|
||||
else
|
||||
require 'iconv'
|
||||
ic = Iconv.new('UTF-8//IGNORE', 'US-ASCII')
|
||||
data = ic.iconv(File.read(file) + ' ')[0..-2]
|
||||
end
|
||||
|
||||
data.force_encoding('US-ASCII') if data.respond_to?(:force_encoding)
|
||||
|
||||
data.lines.each_with_index.each { |line, index|
|
||||
if description = line[%r{(describe|context|it)[( ]*(["'])(.*)\2}, 3]
|
||||
(line_numbers[description] ||= []) << (index + 1)
|
||||
end
|
||||
}
|
||||
|
||||
line_numbers
|
||||
end
|
||||
|
||||
def serialize(data)
|
||||
MultiJson.dump(data)
|
||||
end
|
||||
|
||||
def unserialize(data)
|
||||
MultiJson.load(data)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
34
lib/jasmine/headless/task.rb
Normal file
34
lib/jasmine/headless/task.rb
Normal file
@ -0,0 +1,34 @@
|
||||
module Jasmine
|
||||
module Headless
|
||||
class Task
|
||||
include Rake::DSL if defined?(Rake::DSL)
|
||||
|
||||
attr_accessor :colors, :keep_on_error, :jasmine_config
|
||||
|
||||
def initialize(name = 'jasmine:headless')
|
||||
@colors = false
|
||||
@keep_on_error = false
|
||||
@jasmine_config = nil
|
||||
|
||||
yield self if block_given?
|
||||
|
||||
desc 'Run Jasmine specs headlessly'
|
||||
task(name) { run_rake_task }
|
||||
end
|
||||
|
||||
private
|
||||
def run_rake_task
|
||||
case Jasmine::Headless::Runner.run(
|
||||
:colors => colors,
|
||||
:remove_html_file => !@keep_on_error,
|
||||
:jasmine_config => @jasmine_config
|
||||
)
|
||||
when 1
|
||||
raise Jasmine::Headless::TestFailure
|
||||
when 2
|
||||
raise Jasmine::Headless::ConsoleLogUsage
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
65
lib/jasmine/headless/template_writer.rb
Normal file
65
lib/jasmine/headless/template_writer.rb
Normal file
@ -0,0 +1,65 @@
|
||||
require 'multi_json'
|
||||
require 'erb'
|
||||
require 'tempfile'
|
||||
require 'forwardable'
|
||||
|
||||
module Jasmine::Headless
|
||||
class TemplateWriter
|
||||
attr_reader :runner
|
||||
|
||||
extend Forwardable
|
||||
|
||||
def_delegators :runner, :files_list, :options
|
||||
def_delegators :options, :reporters
|
||||
|
||||
def initialize(runner)
|
||||
@runner = runner
|
||||
end
|
||||
|
||||
def write
|
||||
output = [
|
||||
[ all_tests_filename, files_list.files_to_html ]
|
||||
]
|
||||
|
||||
output.unshift([filtered_tests_filename, files_list.filtered_files_to_html ]) if files_list.filtered?
|
||||
|
||||
output.each do |name, files|
|
||||
template = template_for(files)
|
||||
|
||||
File.open(name, 'wb') { |fh| fh.print template }
|
||||
end
|
||||
|
||||
output.collect(&:first)
|
||||
end
|
||||
|
||||
def all_tests_filename
|
||||
runner.runner_filename || "jhw.#{$$}.html"
|
||||
end
|
||||
|
||||
def filtered_tests_filename
|
||||
all_tests_filename.gsub(%r{\.html$}, '.filter.html')
|
||||
end
|
||||
|
||||
def render
|
||||
template_for(all_files)
|
||||
end
|
||||
|
||||
def all_files
|
||||
files_list.files_to_html
|
||||
end
|
||||
|
||||
def jhw_reporters
|
||||
reporters.collect do |reporter, output|
|
||||
%{jasmine.getEnv().addReporter(new jasmine.HeadlessReporter.#{reporter}("#{output}"));}
|
||||
end.join("\n")
|
||||
end
|
||||
|
||||
private
|
||||
def template_for(files)
|
||||
spec_lines = files_list.spec_file_line_numbers
|
||||
|
||||
ERB.new(Jasmine::Headless.root.join('skel/template.html.erb').read).result(binding)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
16
lib/jasmine/headless/unique_asset_list.rb
Normal file
16
lib/jasmine/headless/unique_asset_list.rb
Normal file
@ -0,0 +1,16 @@
|
||||
module Jasmine::Headless
|
||||
class UniqueAssetList < ::Array
|
||||
def <<(asset)
|
||||
raise InvalidUniqueAsset.new("Not an asset: #{asset.inspect}") if !asset.respond_to?(:logical_path)
|
||||
|
||||
super if !self.any? { |other| asset.pathname == other.pathname }
|
||||
end
|
||||
|
||||
def flatten
|
||||
self.collect(&:to_a).flatten
|
||||
end
|
||||
end
|
||||
|
||||
class InvalidUniqueAsset < StandardError ; end
|
||||
end
|
||||
|
5
lib/jasmine/headless/version.rb
Normal file
5
lib/jasmine/headless/version.rb
Normal file
@ -0,0 +1,5 @@
|
||||
module Jasmine
|
||||
module Headless
|
||||
VERSION = "0.9.0.rc.2"
|
||||
end
|
||||
end
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user