jasmine-rails 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +65 -66
- data/Rakefile +7 -10
- data/app/controllers/jasmine_rails/spec_runner_controller.rb +1 -1
- data/app/helpers/jasmine_rails/spec_runner_helper.rb +30 -0
- data/app/views/layouts/jasmine_rails/spec_runner.html.erb +3 -33
- data/config/routes.rb +0 -3
- data/lib/assets/javascripts/jasmine-boot.js +27 -0
- data/lib/assets/javascripts/jasmine-console-reporter.js +110 -0
- data/lib/assets/javascripts/jasmine-specs.js.erb +14 -0
- data/lib/jasmine-rails.rb +80 -1
- data/lib/jasmine_rails/engine.rb +7 -0
- data/lib/jasmine_rails/offline_asset_paths.rb +30 -0
- data/lib/jasmine_rails/version.rb +1 -1
- data/lib/tasks/jasmine-rails_tasks.rake +28 -4
- data/lib/tasks/runner.js +76 -0
- metadata +9 -21
- data/config/initializers/sprockets.rb +0 -3
- data/lib/jasmine_rails/jhw_adapter.rb +0 -84
data/README.md
CHANGED
@@ -6,19 +6,20 @@ This project is intended to make it a little easier to integrate [Jasmine](https
|
|
6
6
|
|
7
7
|
By bundling this gem and configuring your project, you can expect to:
|
8
8
|
|
9
|
-
* Be able to run Jasmine specs
|
10
|
-
* Be able to run Jasmine specs
|
9
|
+
* Be able to run Jasmine specs in a browser (powered by Rails engine mounted into your application)
|
10
|
+
* Be able to run Jasmine specs from the command line (powered by
|
11
|
+
[PhantomJS](http://phantomjs.org/))
|
11
12
|
* Write specs or source in [CoffeeScript](http://jashkenas.github.com/coffee-script/), leveraging the [asset pipeline](http://railscasts.com/episodes/279-understanding-the-asset-pipeline) to pre-process it
|
12
13
|
|
13
14
|
## Prerequisites
|
14
15
|
|
15
|
-
Install
|
16
|
+
Install phantomjs in order to run tests headless on the command line. The easiest way (on a Mac) that I've found is to use [homebrew](https://github.com/mxcl/homebrew):
|
16
17
|
|
17
|
-
brew install
|
18
|
+
brew install phantomjs
|
18
19
|
|
19
|
-
|
20
|
+
If you're not on a Mac, fear not, as [installing PhantomJS](http://phantomjs.org) is pretty painless for most environments. The important thing is that the binary be somewhere on your PATH.
|
20
21
|
|
21
|
-
##
|
22
|
+
## Installation
|
22
23
|
|
23
24
|
First, add jasmine-rails to your Gemfile, like so
|
24
25
|
|
@@ -28,54 +29,77 @@ First, add jasmine-rails to your Gemfile, like so
|
|
28
29
|
|
29
30
|
Next, run `bundle install`.
|
30
31
|
|
32
|
+
Now, just mount jasmine-rails into your application by adding something like this to your routes.rb. The engine can be mounted to any path that you choose.
|
33
|
+
|
34
|
+
``` ruby
|
35
|
+
mount JasmineRails::Engine => "/specs" if defined?(JasmineRails)
|
36
|
+
```
|
37
|
+
|
38
|
+
## Configuration
|
39
|
+
|
31
40
|
In order to run any specs, you'll need a Jasmine configuration in `spec/javascripts/support/jasmine.yml`. [Here's an example](https://github.com/searls/jasmine-rails/tree/master/spec/dummy/spec/javascripts/support) from this repo's [dummy project](https://github.com/searls/jasmine-rails/tree/master/spec/dummy).
|
32
41
|
|
33
42
|
``` yaml
|
43
|
+
# path to parent directory of src_files
|
44
|
+
# relative path from Rails.root
|
45
|
+
# defaults to app/assets/javascripts
|
46
|
+
src_dir: "app/assets/javascripts"
|
47
|
+
|
48
|
+
# list of file expressions to include as source files
|
49
|
+
# relative path from scr_dir
|
34
50
|
src_files:
|
35
51
|
- "application.{js,coffee}"
|
36
52
|
|
37
|
-
|
53
|
+
# path to parent directory of spec_files
|
54
|
+
# relative path from Rails.root
|
55
|
+
# defaults to spec/javascripts
|
56
|
+
spec_dir: spec/javascripts
|
38
57
|
|
58
|
+
# list of file expressions to include as helpers into spec runner
|
59
|
+
# relative path from spec_dir
|
39
60
|
helpers:
|
40
61
|
- "helpers/**/*.{js,coffee}"
|
41
62
|
|
63
|
+
# list of file expressions to include as specs into spec runner
|
64
|
+
# relative path from spec_dir
|
42
65
|
spec_files:
|
43
66
|
- "**/*[Ss]pec.{js,coffee}"
|
44
|
-
|
45
|
-
src_dir: "app/assets/javascripts"
|
46
|
-
|
47
|
-
spec_dir: spec/javascripts
|
48
|
-
|
49
|
-
asset_paths:
|
50
|
-
- "vendor/assets/javascripts"
|
51
67
|
```
|
52
68
|
|
53
|
-
|
69
|
+
## Asset Pipeline Support
|
54
70
|
|
55
|
-
|
71
|
+
The jasmine-rails gem *fully* supports the Rails asset pipeline which means you can:
|
72
|
+
* use `coffee_script` or other Javascript precompilers for source or
|
73
|
+
test files
|
74
|
+
* use sprockets directives to control inclusion/exclusion of dependent
|
75
|
+
files
|
76
|
+
* leverage asset pipeline search paths to include assets from various
|
77
|
+
sources/gems
|
56
78
|
|
57
|
-
|
79
|
+
**If you choose to use the asset pipeline support, many of the `jasmine.yml`
|
80
|
+
configurations become unnecessary** and you can rely on the Rails asset
|
81
|
+
pipeline to do the hard work of controlling what files are included in
|
82
|
+
your testsuite.
|
58
83
|
|
59
|
-
```
|
60
|
-
|
61
|
-
|
84
|
+
```yaml
|
85
|
+
# minimalist jasmine.yml configuration when leveraging asset pipeline
|
86
|
+
spec_files:
|
87
|
+
- "**/*[Ss]pec.{js,coffee}"
|
62
88
|
```
|
63
89
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
90
|
+
```javascript
|
91
|
+
//= require helpers/spec_helper (includes spec/javascripts/helpers/spec_helper.js)
|
92
|
+
//= require foo (includes app/assets/javascripts/foo.js)
|
93
|
+
describe('Foo', function() {
|
94
|
+
it('does something');
|
95
|
+
});
|
70
96
|
```
|
71
97
|
|
72
|
-
Assets in gems come along for the ride, as well. Additionally, you can follow a similar scheme to import any JavaScript libs you might reference from a `lib/assets/javascripts/lib.js` manifest file.
|
73
|
-
|
74
98
|
## Running from the command line
|
75
99
|
|
76
100
|
If you were to run:
|
77
101
|
|
78
|
-
bundle exec
|
102
|
+
bundle exec rake spec:javascript
|
79
103
|
|
80
104
|
You'd hopefully see something like:
|
81
105
|
|
@@ -83,19 +107,16 @@ You'd hopefully see something like:
|
|
83
107
|
|
84
108
|
PASS: 0 tests, 0 failures, 0.001 secs.
|
85
109
|
|
86
|
-
|
110
|
+
You can filter execution by passing the `SPEC` option as well:
|
111
|
+
|
112
|
+
bundle exec rake spec:javascript SPEC=my_test
|
87
113
|
|
88
114
|
If you experience an error at this point, the most likely cause is JavaScript being loaded out of order, or otherwise conflicting with other existing JavaScript in your project. See "Debugging" below.
|
89
115
|
|
90
116
|
## Running from your browser
|
91
117
|
|
92
|
-
|
93
|
-
|
94
|
-
``` ruby
|
95
|
-
mount JasmineRails::Engine => "/specs" unless Rails.env.production?
|
96
|
-
```
|
97
|
-
|
98
|
-
Now when you run `bundle exec rails s`, and navigate to [http://localhost:3000/specs](http://localhost:3000/specs), you should see a Jasmine spec runner in your browser.
|
118
|
+
Startup your Rails server (ex: `bundle exec rails s`), and navigate to the path you have configured in your routes.rb file (ex: [http://localhost:3000/specs](http://localhost:3000/specs)).
|
119
|
+
The Jasmine spec runner should appear and start running your testsuite instantly.
|
99
120
|
|
100
121
|
## Debugging
|
101
122
|
|
@@ -103,39 +124,17 @@ Now when you run `bundle exec rails s`, and navigate to [http://localhost:3000/s
|
|
103
124
|
|
104
125
|
In my workflow, I like to work with specs in the command line until I hit a snag and could benefit from debugging in [Web Inspector](http://www.webkit.org/blog/1091/more-web-inspector-updates/) or [Firebug](http://getfirebug.com/) to figure out what's going on.
|
105
126
|
|
106
|
-
[When debugging, if you've disabled the asset pipeline's debug mode in dev/test, you may need to append the query param `?debug_assets=true` like so: [http://localhost:3000/specs?debug_assets=true](http://localhost:3000/specs?debug_assets=true). The asset pipeline will include individual `script` tags for each of your scripts when in debug mode, which migh tmake debugging easier.]
|
107
|
-
|
108
127
|
### From the command line
|
109
128
|
|
110
|
-
Even though they both read from the same config file, it's certainly possible that your specs will pass in the browser and fail from the command line. In this case, you can try to debug or analyze what's going on
|
111
|
-
|
112
|
-
By running:
|
129
|
+
Even though they both read from the same config file, it's certainly possible that your specs will pass in the browser and fail from the command line. In this case, you can try to debug or analyze what's going on loading the headless runner.html file into your browser environment. The generated runner.html file is written out to `spec/tmp/runner.html` after each run.
|
113
130
|
|
114
|
-
|
131
|
+
### Ajax / XHRs
|
115
132
|
|
116
|
-
|
133
|
+
As a general rule, Jasmine is designed for unit testing, and as a result real network requests are not appropriate for tests written in Jasmine. (Isolation strategies can include spying on asynchronous libraries and then synchronously testing callback behavior, as [demonstrated in this gist](https://gist.github.com/searls/946704)).
|
117
134
|
|
118
|
-
|
119
|
-
|
120
|
-
[Guard](https://github.com/guard/guard) is a great tool for triggering spec runs when files change. To use it, you can bundle these gems:
|
121
|
-
|
122
|
-
group :development do
|
123
|
-
...
|
124
|
-
gem 'guard-jasmine-headless-webkit'
|
125
|
-
...
|
126
|
-
end
|
127
|
-
|
128
|
-
In my Guardfile, this configuration is working well for me:
|
129
|
-
|
130
|
-
spec_location = "spec/javascripts/%s_spec"
|
131
|
-
|
132
|
-
guard 'jasmine-headless-webkit' do
|
133
|
-
watch(%r{^app/views/.*\.jst$})
|
134
|
-
watch(%r{^public/javascripts/(.*)\.js$}) { |m| newest_js_file(spec_location % m[1]) }
|
135
|
-
watch(%r{^.*/assets/javascripts/(.*)\.(js|coffee)$}) { |m| newest_js_file(spec_location % m[1]) }
|
136
|
-
watch(%r{^spec/javascripts/(.*)_spec\..*}) { |m| newest_js_file(spec_location % m[1]) }
|
137
|
-
end
|
135
|
+
If your application code issues XHR requests during your test run, please note that **XHR requests for the local filesystem** are blocked by default for most browsers for security reasons. To debug local XHR requests (for example, if you jasmine-jquery fixtures), you will need to enable local filesystem requests in your browser.
|
138
136
|
|
139
|
-
|
137
|
+
Example for Google Chrome (in Mac OS X):
|
138
|
+
open -a "Google Chrome" spec/tmp/runner.html --args --allow-file-access-from-files
|
140
139
|
|
141
|
-
|
140
|
+
Again, it's the opinion of the present author that this shouldn't be necessary in any situation but legacy rescue of an existing test suite. With respect specifically to HTML fixtures, please consider [jasmine-fixture](https://github.com/searls/jasmine-fixture) and [my rationale](http://searls.testdouble.com/posts/2011-12-11-jasmine-fixtures.html) for it.
|
data/Rakefile
CHANGED
@@ -9,17 +9,14 @@ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
|
9
9
|
load 'rails/tasks/engine.rake'
|
10
10
|
Bundler::GemHelper.install_tasks
|
11
11
|
|
12
|
-
task :
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
Jasmine::Headless::Task.new(:dummy_headless_jasmine) do |t|
|
18
|
-
t.colors = true
|
19
|
-
t.keep_on_error = true
|
12
|
+
task :run_jasmine_rake_in_dummy do
|
13
|
+
system <<-BASH
|
14
|
+
cd spec/dummy
|
15
|
+
bundle exec rake spec:javascript
|
16
|
+
BASH
|
20
17
|
end
|
21
18
|
|
22
19
|
require 'rspec/core/rake_task'
|
23
|
-
RSpec::Core::RakeTask.new(:
|
20
|
+
RSpec::Core::RakeTask.new(:run_browser_spec_in_dummy)
|
24
21
|
|
25
|
-
task :default => [:
|
22
|
+
task :default => [:run_jasmine_rake_in_dummy, :run_browser_spec_in_dummy]
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'jasmine-core'
|
2
|
+
|
3
|
+
module JasmineRails
|
4
|
+
module SpecRunnerHelper
|
5
|
+
# return list of css files to include in spec runner
|
6
|
+
# all files are fetched through the Rails asset pipeline
|
7
|
+
# includes:
|
8
|
+
# * core jasmine css files
|
9
|
+
def jasmine_css_files
|
10
|
+
Jasmine::Core.css_files
|
11
|
+
end
|
12
|
+
|
13
|
+
# return list of javascript files needed for jasmine testsuite
|
14
|
+
# all files are fetched through the Rails asset pipeline
|
15
|
+
# includes:
|
16
|
+
# * core jasmine libraries
|
17
|
+
# * (optional) jasmine-console-reporter.js for CLI output
|
18
|
+
# * jasmine-boot.js test runner
|
19
|
+
# * jasmine-specs.js built by asset pipeline which merges application specific libraries and specs
|
20
|
+
def jasmine_js_files
|
21
|
+
files = Jasmine::Core.js_files
|
22
|
+
if params[:console]
|
23
|
+
files << 'jasmine-console-reporter.js'
|
24
|
+
end
|
25
|
+
files << 'jasmine-boot.js'
|
26
|
+
files << 'jasmine-specs.js'
|
27
|
+
files
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -4,41 +4,11 @@
|
|
4
4
|
<meta content="text/html;charset=UTF-8" http-equiv="Content-Type"/>
|
5
5
|
<title>Jasmine Specs</title>
|
6
6
|
|
7
|
-
<%= stylesheet_link_tag *
|
8
|
-
<%= javascript_include_tag *
|
9
|
-
|
10
|
-
<!-- executing jasmine's runner -->
|
11
|
-
<script type="text/javascript">
|
12
|
-
(function() {
|
13
|
-
var jasmineEnv = jasmine.getEnv();
|
14
|
-
jasmineEnv.updateInterval = 1000;
|
15
|
-
|
16
|
-
var htmlReporter = new jasmine.HtmlReporter();
|
17
|
-
|
18
|
-
jasmineEnv.addReporter(htmlReporter);
|
19
|
-
|
20
|
-
jasmineEnv.specFilter = function(spec) {
|
21
|
-
return htmlReporter.specFilter(spec);
|
22
|
-
};
|
23
|
-
|
24
|
-
var currentWindowOnload = window.onload;
|
25
|
-
|
26
|
-
window.onload = function() {
|
27
|
-
if (currentWindowOnload) {
|
28
|
-
currentWindowOnload();
|
29
|
-
}
|
30
|
-
execJasmine();
|
31
|
-
};
|
32
|
-
|
33
|
-
function execJasmine() {
|
34
|
-
jasmineEnv.execute();
|
35
|
-
}
|
36
|
-
|
37
|
-
})();
|
38
|
-
</script>
|
7
|
+
<%= stylesheet_link_tag *jasmine_css_files %>
|
8
|
+
<%= javascript_include_tag *jasmine_js_files %>
|
39
9
|
</head>
|
40
10
|
<body>
|
41
11
|
<div id="jasmine_content"></div>
|
42
12
|
<%= yield %>
|
43
13
|
</body>
|
44
|
-
</html>
|
14
|
+
</html>
|
data/config/routes.rb
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
var jsApiReporter;
|
2
|
+
(function() {
|
3
|
+
var jasmineEnv = jasmine.getEnv();
|
4
|
+
|
5
|
+
jsApiReporter = new jasmine.JsApiReporter();
|
6
|
+
jasmineEnv.addReporter(jsApiReporter);
|
7
|
+
|
8
|
+
var htmlReporter = new jasmine.HtmlReporter();
|
9
|
+
jasmineEnv.addReporter(htmlReporter);
|
10
|
+
jasmineEnv.specFilter = function(spec) {
|
11
|
+
return htmlReporter.specFilter(spec);
|
12
|
+
};
|
13
|
+
|
14
|
+
if (jasmine.ConsoleReporter) {
|
15
|
+
jasmineEnv.addReporter(new jasmine.ConsoleReporter());
|
16
|
+
}
|
17
|
+
|
18
|
+
function execJasmine() {
|
19
|
+
jasmineEnv.execute();
|
20
|
+
}
|
21
|
+
|
22
|
+
if (window.addEventListener) { // W3C
|
23
|
+
window.addEventListener('load', execJasmine, false);
|
24
|
+
} else if (window.attachEvent) { // MSIE
|
25
|
+
window.attachEvent('onload', execJasmine);
|
26
|
+
}
|
27
|
+
})();
|
@@ -0,0 +1,110 @@
|
|
1
|
+
/**
|
2
|
+
Jasmine Reporter that outputs test results to the browser console.
|
3
|
+
Useful for running in a headless environment such as PhantomJs, ZombieJs etc.
|
4
|
+
|
5
|
+
Usage:
|
6
|
+
// From your html file that loads jasmine:
|
7
|
+
jasmine.getEnv().addReporter(new jasmine.ConsoleReporter());
|
8
|
+
jasmine.getEnv().execute();
|
9
|
+
*/
|
10
|
+
|
11
|
+
(function(jasmine, console) {
|
12
|
+
if (!jasmine) {
|
13
|
+
throw "jasmine library isn't loaded!";
|
14
|
+
}
|
15
|
+
|
16
|
+
var ANSI = {}
|
17
|
+
ANSI.color_map = {
|
18
|
+
"green" : 32,
|
19
|
+
"red" : 31
|
20
|
+
}
|
21
|
+
|
22
|
+
ANSI.colorize_text = function(text, color) {
|
23
|
+
var color_code = this.color_map[color];
|
24
|
+
return "\033[" + color_code + "m" + text + "\033[0m";
|
25
|
+
}
|
26
|
+
|
27
|
+
var ConsoleReporter = function() {
|
28
|
+
if (!console || !console.log) { throw "console isn't present!"; }
|
29
|
+
this.status = this.statuses.stopped;
|
30
|
+
};
|
31
|
+
|
32
|
+
var proto = ConsoleReporter.prototype;
|
33
|
+
proto.statuses = {
|
34
|
+
stopped : "stopped",
|
35
|
+
running : "running",
|
36
|
+
fail : "fail",
|
37
|
+
success : "success"
|
38
|
+
};
|
39
|
+
|
40
|
+
proto.reportRunnerStarting = function(runner) {
|
41
|
+
this.status = this.statuses.running;
|
42
|
+
this.start_time = (new Date()).getTime();
|
43
|
+
this.executed_specs = 0;
|
44
|
+
this.passed_specs = 0;
|
45
|
+
this.log("Starting...");
|
46
|
+
};
|
47
|
+
|
48
|
+
proto.reportRunnerResults = function(runner) {
|
49
|
+
var failed = this.executed_specs - this.passed_specs;
|
50
|
+
var spec_str = this.executed_specs + (this.executed_specs === 1 ? " spec, " : " specs, ");
|
51
|
+
var fail_str = failed + (failed === 1 ? " failure in " : " failures in ");
|
52
|
+
var color = (failed > 0)? "red" : "green";
|
53
|
+
var dur = (new Date()).getTime() - this.start_time;
|
54
|
+
|
55
|
+
this.log("");
|
56
|
+
this.log("Finished");
|
57
|
+
this.log("-----------------");
|
58
|
+
this.log(spec_str + fail_str + (dur/1000) + "s.", color);
|
59
|
+
|
60
|
+
this.status = (failed > 0)? this.statuses.fail : this.statuses.success;
|
61
|
+
|
62
|
+
/* Print something that signals that testing is over so that headless browsers
|
63
|
+
like PhantomJs know when to terminate. */
|
64
|
+
this.log("");
|
65
|
+
this.log("ConsoleReporter finished");
|
66
|
+
};
|
67
|
+
|
68
|
+
|
69
|
+
proto.reportSpecStarting = function(spec) {
|
70
|
+
this.executed_specs++;
|
71
|
+
};
|
72
|
+
|
73
|
+
proto.reportSpecResults = function(spec) {
|
74
|
+
if (spec.results().skipped) {
|
75
|
+
return;
|
76
|
+
}
|
77
|
+
if (spec.results().passed()) {
|
78
|
+
this.passed_specs++;
|
79
|
+
return;
|
80
|
+
}
|
81
|
+
|
82
|
+
var resultText = spec.suite.description + " : " + spec.description;
|
83
|
+
this.log(resultText, "red");
|
84
|
+
|
85
|
+
var items = spec.results().getItems()
|
86
|
+
for (var i = 0; i < items.length; i++) {
|
87
|
+
var item = items[i];
|
88
|
+
var output = ' ' + item.message;
|
89
|
+
this.log(output, "red");
|
90
|
+
}
|
91
|
+
};
|
92
|
+
|
93
|
+
proto.reportSuiteResults = function(suite) {
|
94
|
+
if (!suite.parentSuite) { return; }
|
95
|
+
var results = suite.results();
|
96
|
+
if (results.totalCount === 0) {
|
97
|
+
return;
|
98
|
+
}
|
99
|
+
var failed = results.totalCount - results.passedCount;
|
100
|
+
var color = (failed > 0)? "red" : "green";
|
101
|
+
this.log(suite.description + ": " + results.passedCount + " of " + results.totalCount + " passed.", color);
|
102
|
+
};
|
103
|
+
|
104
|
+
proto.log = function(str, color) {
|
105
|
+
var text = (color != undefined)? ANSI.colorize_text(str, color) : str;
|
106
|
+
console.log(text)
|
107
|
+
};
|
108
|
+
|
109
|
+
jasmine.ConsoleReporter = ConsoleReporter;
|
110
|
+
})(jasmine, console);
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<%
|
2
|
+
# depend on spec dirs to automatically flush asset cache
|
3
|
+
# when new tests or directories are added
|
4
|
+
JasmineRails.each_spec_dir do |directory|
|
5
|
+
depend_on directory
|
6
|
+
end
|
7
|
+
|
8
|
+
# bundle all jasmine specs using asset pipeline
|
9
|
+
# so asset pipeline can properly filter out duplicates
|
10
|
+
# via dependency tree
|
11
|
+
JasmineRails.spec_files.each do |file|
|
12
|
+
require_asset(file.to_s)
|
13
|
+
end
|
14
|
+
%>
|
data/lib/jasmine-rails.rb
CHANGED
@@ -1,5 +1,84 @@
|
|
1
1
|
require "jasmine_rails/engine"
|
2
|
-
require "jasmine_rails/jhw_adapter"
|
3
2
|
|
4
3
|
module JasmineRails
|
4
|
+
class << self
|
5
|
+
# return the relative path to access the spec runner
|
6
|
+
# for the host Rails application
|
7
|
+
# ex: /jasmine
|
8
|
+
def route_path
|
9
|
+
route = Rails.application.routes.named_routes[:jasmine_rails]
|
10
|
+
raise 'JasmineRails::Engine has not been mounted into routes.rb' unless route
|
11
|
+
path = route.path
|
12
|
+
|
13
|
+
# Rails 3.1 support
|
14
|
+
if path.is_a?(String)
|
15
|
+
path
|
16
|
+
else
|
17
|
+
path.spec.to_s
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def spec_dir
|
22
|
+
path = jasmine_config['spec_dir'] || 'spec/javascripts'
|
23
|
+
Rails.root.join(path)
|
24
|
+
end
|
25
|
+
|
26
|
+
# returns list of all files to be included into the jasmine testsuite
|
27
|
+
# includes:
|
28
|
+
# * application src_files
|
29
|
+
# * spec helpers
|
30
|
+
# * spec_files
|
31
|
+
def spec_files
|
32
|
+
files = []
|
33
|
+
files += filter_files src_dir, jasmine_config['src_files']
|
34
|
+
files += filter_files spec_dir, jasmine_config['helpers']
|
35
|
+
files += filter_files spec_dir, jasmine_config['spec_files']
|
36
|
+
files
|
37
|
+
end
|
38
|
+
|
39
|
+
# iterate over all directories used as part of the testsuite (including subdirectories)
|
40
|
+
def each_spec_dir(&block)
|
41
|
+
each_dir spec_dir.to_s, &block
|
42
|
+
each_dir src_dir.to_s, &block
|
43
|
+
end
|
44
|
+
|
45
|
+
# clear out cached jasmine config file
|
46
|
+
# it would be nice to automatically flush when the jasmine.yml file changes instead
|
47
|
+
# of having this programatic API
|
48
|
+
def reload_jasmine_config
|
49
|
+
@config = nil
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def src_dir
|
55
|
+
path = jasmine_config['src_dir'] || 'app/assets/javascripts'
|
56
|
+
Rails.root.join(path)
|
57
|
+
end
|
58
|
+
|
59
|
+
def jasmine_config
|
60
|
+
@config ||= begin
|
61
|
+
path = Rails.root.join('spec', 'javascripts', 'support', 'jasmine.yml')
|
62
|
+
YAML.load_file(path)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def each_dir(root, &block)
|
67
|
+
yield root
|
68
|
+
Dir[root + '/*'].each do |file|
|
69
|
+
if File.directory?(file)
|
70
|
+
each_dir(file, &block)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def filter_files(root_dir, patterns)
|
76
|
+
files = patterns.to_a.collect do |pattern|
|
77
|
+
Dir.glob(root_dir.join(pattern)).sort
|
78
|
+
end
|
79
|
+
files = files.flatten
|
80
|
+
files = files.collect {|f| f.gsub(root_dir.to_s + '/', '') }
|
81
|
+
files || []
|
82
|
+
end
|
83
|
+
end
|
5
84
|
end
|
data/lib/jasmine_rails/engine.rb
CHANGED
@@ -1,5 +1,12 @@
|
|
1
|
+
require 'jasmine-core'
|
2
|
+
|
1
3
|
module JasmineRails
|
2
4
|
class Engine < Rails::Engine
|
3
5
|
isolate_namespace JasmineRails
|
6
|
+
|
7
|
+
initializer :assets do |config|
|
8
|
+
Rails.application.config.assets.paths << Jasmine::Core.path
|
9
|
+
Rails.application.config.assets.paths << JasmineRails.spec_dir
|
10
|
+
end
|
4
11
|
end
|
5
12
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Rails Asset Patch extension used to write assets out an offline asset directory
|
2
|
+
# for future use
|
3
|
+
# example:
|
4
|
+
# ActionView::AssetPaths.send :include, JasmineRails::OfflineAssetPaths
|
5
|
+
module JasmineRails
|
6
|
+
module OfflineAssetPaths
|
7
|
+
mattr_accessor :disabled
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
included do
|
10
|
+
alias_method_chain :compute_public_path, :offline_asset
|
11
|
+
end
|
12
|
+
|
13
|
+
def compute_public_path_with_offline_asset(source, dir, options={})
|
14
|
+
return compute_public_path_without_offline_asset(source, dir, options) if JasmineRails::OfflineAssetPaths.disabled
|
15
|
+
return source if source.starts_with?('/')
|
16
|
+
content = Rails.application.assets[source].to_s
|
17
|
+
source_path = offline_asset_dir.join(source)
|
18
|
+
|
19
|
+
FileUtils.mkdir_p File.dirname(source_path)
|
20
|
+
Rails.logger.debug "Compiling #{source} to #{source_path}"
|
21
|
+
File.open(source_path, 'w') {|f| f << content }
|
22
|
+
"assets/#{source}"
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def offline_asset_dir
|
27
|
+
Rails.root.join('spec/tmp/assets')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -1,4 +1,28 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
namespace :spec do
|
2
|
+
def run_cmd(cmd)
|
3
|
+
puts "$ #{cmd}"
|
4
|
+
unless system(cmd)
|
5
|
+
raise "Error executing command: #{cmd}"
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "run test with phantomjs"
|
10
|
+
task :javascript => :environment do
|
11
|
+
require 'jasmine_rails/offline_asset_paths'
|
12
|
+
ActionView::AssetPaths.send :include, JasmineRails::OfflineAssetPaths
|
13
|
+
spec_filter = ENV['SPEC']
|
14
|
+
app = ActionController::Integration::Session.new(Rails.application)
|
15
|
+
path = JasmineRails.route_path
|
16
|
+
app.get path, :console => 'true', :spec => spec_filter
|
17
|
+
JasmineRails::OfflineAssetPaths.disabled = true
|
18
|
+
raise "Error generating jasmine runner: #{app.response.status_message}" unless app.response.status == 200
|
19
|
+
html = app.response.body
|
20
|
+
runner_path = Rails.root.join('spec/tmp/runner.html')
|
21
|
+
File.open(runner_path, 'w') {|f| f << html}
|
22
|
+
|
23
|
+
run_cmd "phantomjs #{File.join(File.dirname(__FILE__), 'runner.js')} file://#{runner_path.to_s}?spec=#{spec_filter}"
|
24
|
+
end
|
25
|
+
|
26
|
+
# alias
|
27
|
+
task :javascripts => :javascript
|
28
|
+
end
|
data/lib/tasks/runner.js
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
(function() {
|
2
|
+
// handler for any page javascript errors
|
3
|
+
var errorHandler = function(msg, trace) {
|
4
|
+
var msgStack = ['ERROR: ' + msg];
|
5
|
+
if (trace) {
|
6
|
+
msgStack.push('TRACE:');
|
7
|
+
trace.forEach(function(t) {
|
8
|
+
msgStack.push(' -> ' + t.file + ': ' + t.line + (t.function ? ' (in function "' + t.function + '")' : ''));
|
9
|
+
});
|
10
|
+
}
|
11
|
+
console.error(msgStack.join('\n'));
|
12
|
+
phantom.exit(1);
|
13
|
+
};
|
14
|
+
phantom.onError = errorHandler;
|
15
|
+
|
16
|
+
var system = require('system');
|
17
|
+
var args = system.args;
|
18
|
+
|
19
|
+
if (args.length !== 2) {
|
20
|
+
console.log("Need a url as the argument");
|
21
|
+
phantom.exit(1);
|
22
|
+
}
|
23
|
+
|
24
|
+
var page = new WebPage();
|
25
|
+
|
26
|
+
// log messages to stdout
|
27
|
+
page.onConsoleMessage = function(msg) {
|
28
|
+
console.log(msg);
|
29
|
+
};
|
30
|
+
|
31
|
+
// listen for event from parent page
|
32
|
+
page.onCallback = function(data) {
|
33
|
+
if (data.event === 'exit') {
|
34
|
+
phantom.exit(data.exitCode);
|
35
|
+
} else if (data.event === 'writeFile') {
|
36
|
+
var fs = require("fs");
|
37
|
+
fs.write(data.filename, data.text, 'w');
|
38
|
+
} else {
|
39
|
+
console.log('unkown event callback: ' + data);
|
40
|
+
}
|
41
|
+
};
|
42
|
+
|
43
|
+
// log javascript errors
|
44
|
+
page.onError = errorHandler;
|
45
|
+
|
46
|
+
// setup listeners for jasmine events
|
47
|
+
page.onInitialized = function() {
|
48
|
+
return page.evaluate(function() {
|
49
|
+
return window.onload = function() {
|
50
|
+
jsApiReporter.exitCode = 0;
|
51
|
+
jsApiReporter.reportSpecResults = function(spec) {
|
52
|
+
if (spec.results().failedCount > 0) {
|
53
|
+
jsApiReporter.exitCode = 1;
|
54
|
+
}
|
55
|
+
};
|
56
|
+
jsApiReporter.reportRunnerResults = function() {
|
57
|
+
setTimeout(function() {
|
58
|
+
window.callPhantom({
|
59
|
+
event: 'exit',
|
60
|
+
exitCode: jsApiReporter.exitCode
|
61
|
+
});
|
62
|
+
}, 1);
|
63
|
+
};
|
64
|
+
};
|
65
|
+
});
|
66
|
+
};
|
67
|
+
|
68
|
+
var address = args[1];
|
69
|
+
console.log('Running: ' + address);
|
70
|
+
page.open(address, function(status) {
|
71
|
+
if (status !== "success") {
|
72
|
+
console.log("can't load the address!");
|
73
|
+
return phantom.exit(1);
|
74
|
+
}
|
75
|
+
});
|
76
|
+
})();
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jasmine-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2013-
|
14
|
+
date: 2013-04-18 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: rails
|
@@ -30,7 +30,7 @@ dependencies:
|
|
30
30
|
- !ruby/object:Gem::Version
|
31
31
|
version: 3.1.0
|
32
32
|
- !ruby/object:Gem::Dependency
|
33
|
-
name: jasmine
|
33
|
+
name: jasmine-core
|
34
34
|
requirement: !ruby/object:Gem::Requirement
|
35
35
|
none: false
|
36
36
|
requirements:
|
@@ -45,22 +45,6 @@ dependencies:
|
|
45
45
|
- - ~>
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '1.3'
|
48
|
-
- !ruby/object:Gem::Dependency
|
49
|
-
name: jasmine-headless-webkit
|
50
|
-
requirement: !ruby/object:Gem::Requirement
|
51
|
-
none: false
|
52
|
-
requirements:
|
53
|
-
- - ! '>='
|
54
|
-
- !ruby/object:Gem::Version
|
55
|
-
version: '0'
|
56
|
-
type: :runtime
|
57
|
-
prerelease: false
|
58
|
-
version_requirements: !ruby/object:Gem::Requirement
|
59
|
-
none: false
|
60
|
-
requirements:
|
61
|
-
- - ! '>='
|
62
|
-
- !ruby/object:Gem::Version
|
63
|
-
version: '0'
|
64
48
|
description: Provides a Jasmine Spec Runner that plays nicely with Rails 3.1 assets
|
65
49
|
and sets up jasmine-headless-webkit
|
66
50
|
email:
|
@@ -73,15 +57,19 @@ extra_rdoc_files: []
|
|
73
57
|
files:
|
74
58
|
- app/controllers/jasmine_rails/application_controller.rb
|
75
59
|
- app/controllers/jasmine_rails/spec_runner_controller.rb
|
60
|
+
- app/helpers/jasmine_rails/spec_runner_helper.rb
|
76
61
|
- app/views/jasmine_rails/spec_runner/index.html.erb
|
77
62
|
- app/views/layouts/jasmine_rails/spec_runner.html.erb
|
78
|
-
- config/initializers/sprockets.rb
|
79
63
|
- config/routes.rb
|
64
|
+
- lib/assets/javascripts/jasmine-boot.js
|
65
|
+
- lib/assets/javascripts/jasmine-console-reporter.js
|
66
|
+
- lib/assets/javascripts/jasmine-specs.js.erb
|
80
67
|
- lib/jasmine-rails.rb
|
81
68
|
- lib/jasmine_rails/engine.rb
|
82
|
-
- lib/jasmine_rails/
|
69
|
+
- lib/jasmine_rails/offline_asset_paths.rb
|
83
70
|
- lib/jasmine_rails/version.rb
|
84
71
|
- lib/tasks/jasmine-rails_tasks.rake
|
72
|
+
- lib/tasks/runner.js
|
85
73
|
- MIT-LICENSE
|
86
74
|
- Rakefile
|
87
75
|
- README.md
|
@@ -1,84 +0,0 @@
|
|
1
|
-
require 'jasmine-headless-webkit'
|
2
|
-
|
3
|
-
module JasmineRails
|
4
|
-
|
5
|
-
class JhwAdapter
|
6
|
-
|
7
|
-
def initialize
|
8
|
-
@options = Jasmine::Headless::Options.new
|
9
|
-
@runner = instantiate_runner
|
10
|
-
Jasmine::Headless::CacheableAction.enabled = @options[:enable_cache]
|
11
|
-
end
|
12
|
-
|
13
|
-
def css_files
|
14
|
-
@css_files_list ||= CssFilesList.new(
|
15
|
-
:config => @runner.jasmine_config,
|
16
|
-
:only => @options[:files],
|
17
|
-
:seed => @options[:seed]
|
18
|
-
).logical_paths
|
19
|
-
end
|
20
|
-
|
21
|
-
def js_files
|
22
|
-
@js_files_list ||= JsFilesList.new(
|
23
|
-
:config => @runner.jasmine_config,
|
24
|
-
:only => @options[:files],
|
25
|
-
:seed => @options[:seed]
|
26
|
-
).logical_paths
|
27
|
-
end
|
28
|
-
|
29
|
-
def asset_paths
|
30
|
-
jasmine_config = YAML.load_file(@options[:jasmine_config])
|
31
|
-
[
|
32
|
-
jasmine_config["src_dir"],
|
33
|
-
jasmine_config["spec_dir"],
|
34
|
-
jasmine_config["asset_paths"]
|
35
|
-
].flatten.compact
|
36
|
-
end
|
37
|
-
|
38
|
-
def instantiate_runner
|
39
|
-
begin
|
40
|
-
Jasmine::Headless::Runner.new(@options)
|
41
|
-
rescue Jasmine::Headless::NoRunnerError
|
42
|
-
require 'fileutils'
|
43
|
-
FileUtils.touch(Jasmine::Headless::Runner::RUNNER)
|
44
|
-
Jasmine::Headless::Runner.new(@options).tap { File.delete(Jasmine::Headless::Runner::RUNNER) }
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
class DistinctifiedFileList < Jasmine::Headless::FilesList
|
50
|
-
def files
|
51
|
-
required_files.collect { |asset| asset.pathname.to_s }.uniq
|
52
|
-
end
|
53
|
-
|
54
|
-
def logical_paths
|
55
|
-
files.map { |file| Rails.application.assets[file].logical_path }
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
class JsFilesList < DistinctifiedFileList
|
60
|
-
|
61
|
-
def self.default_files
|
62
|
-
%w{jasmine.js jasmine-html}
|
63
|
-
end
|
64
|
-
|
65
|
-
def self.extension_filter
|
66
|
-
extensions = (%w{.js} + Sprockets.engine_extensions)
|
67
|
-
%r{(#{extensions.join('|')})$}
|
68
|
-
end
|
69
|
-
|
70
|
-
end
|
71
|
-
|
72
|
-
class CssFilesList < DistinctifiedFileList
|
73
|
-
|
74
|
-
def self.default_files
|
75
|
-
%w{jasmine.css}
|
76
|
-
end
|
77
|
-
|
78
|
-
def self.extension_filter
|
79
|
-
extensions = (%w{.css .scss .sass .less})
|
80
|
-
%r{(#{extensions.join('|')})$}
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
end
|