jquery-rails-multipart-xhr 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/Gemfile +4 -0
- data/README.md +63 -0
- data/Rakefile +1 -0
- data/jquery-rails-multipart-xhr.gemspec +26 -0
- data/lib/generators/jquery/multipart_xhr/install/install_generator.rb +20 -0
- data/lib/jquery-rails-multipart-xhr.rb +2 -0
- data/lib/jquery/rails/multipart_xhr.rb +13 -0
- data/lib/jquery/rails/multipart_xhr/engine.rb +10 -0
- data/lib/jquery/rails/multipart_xhr/railtie.rb +18 -0
- data/lib/jquery/rails/multipart_xhr/version.rb +8 -0
- data/test/.rbenv-gemsets +1 -0
- data/test/index.html +61 -0
- data/test/libs/jasmine-html.js +616 -0
- data/test/libs/jasmine-jquery.js +322 -0
- data/test/libs/jasmine.css +81 -0
- data/test/libs/jasmine.js +2530 -0
- data/test/libs/jquery-1.7.2.js +9404 -0
- data/test/libs/rails.js +384 -0
- data/test/rails_ext_spec.js +34 -0
- data/test/sendfile.html +0 -0
- data/test/spec_helper.js +28 -0
- data/test/test_app/Gemfile +9 -0
- data/test/test_app/README.rdoc +261 -0
- data/test/test_app/Rakefile +7 -0
- data/test/test_app/app/assets/images/rails.png +0 -0
- data/test/test_app/app/assets/javascripts/application.js +3 -0
- data/test/test_app/app/assets/stylesheets/application.css +13 -0
- data/test/test_app/app/controllers/application_controller.rb +3 -0
- data/test/test_app/app/controllers/index_controller.rb +10 -0
- data/test/test_app/app/helpers/application_helper.rb +2 -0
- data/test/test_app/app/helpers/index_helper.rb +2 -0
- data/test/test_app/app/views/index/index.html.erb +5 -0
- data/test/test_app/app/views/index/upload.js.erb +0 -0
- data/test/test_app/app/views/layouts/application.html.erb +14 -0
- data/test/test_app/config.ru +4 -0
- data/test/test_app/config/application.rb +68 -0
- data/test/test_app/config/boot.rb +6 -0
- data/test/test_app/config/environment.rb +5 -0
- data/test/test_app/config/environments/development.rb +31 -0
- data/test/test_app/config/environments/production.rb +64 -0
- data/test/test_app/config/environments/test.rb +35 -0
- data/test/test_app/config/initializers/backtrace_silencers.rb +7 -0
- data/test/test_app/config/initializers/inflections.rb +15 -0
- data/test/test_app/config/initializers/mime_types.rb +5 -0
- data/test/test_app/config/initializers/secret_token.rb +7 -0
- data/test/test_app/config/initializers/session_store.rb +8 -0
- data/test/test_app/config/initializers/wrap_parameters.rb +10 -0
- data/test/test_app/config/locales/en.yml +5 -0
- data/test/test_app/config/routes.rb +4 -0
- data/test/test_app/db/seeds.rb +7 -0
- data/test/test_app/doc/README_FOR_APP +2 -0
- data/test/test_app/log/development.log +393 -0
- data/test/test_app/public/404.html +26 -0
- data/test/test_app/public/422.html +26 -0
- data/test/test_app/public/500.html +25 -0
- data/test/test_app/public/favicon.ico +0 -0
- data/test/test_app/public/robots.txt +5 -0
- data/test/test_app/script/rails +6 -0
- data/test/test_app/tmp/cache/assets/C8F/390/sprockets%2F6b7d5e919177af65c02182147de40988 +0 -0
- data/test/test_app/tmp/cache/assets/CD8/0A0/sprockets%2F5141580125473c05f47bff0c8bb8dd80 +0 -0
- data/test/test_app/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
- data/test/test_app/tmp/cache/assets/CF0/DA0/sprockets%2Fd7d5b37686831d37c4dd75e645f5e016 +0 -0
- data/test/test_app/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/test/test_app/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/test/test_app/tmp/cache/assets/D53/0E0/sprockets%2F88f3d17e4cef7464b29ab1d468495af6 +0 -0
- data/test/test_app/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/test/test_app/tmp/cache/assets/D6D/6E0/sprockets%2Ff0871e805cb1dd84c3a8a024f925e7af +0 -0
- data/test/test_app/tmp/cache/assets/D94/5C0/sprockets%2Fbfc22af1dbf2c597a27bfa0388020f76 +0 -0
- data/test/test_app/tmp/cache/assets/DAC/540/sprockets%2Fee54428bf456feb108fa5ac58fe768b3 +0 -0
- data/test/test_app/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/test/test_app/tmp/cache/assets/DF1/800/sprockets%2Fb3fb6dd6651d2f1ecacf3c52b1f7e223 +0 -0
- data/test/test_app/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/test/test_app/tmp/cache/assets/E0A/940/sprockets%2Fff36bca4fabd6d0690d947ef06ff8d26 +0 -0
- data/test/test_app/tmp/cache/assets/E25/4C0/sprockets%2Fde2fd9fd11c04a582cdbbe3d84a35ae6 +0 -0
- data/test/xhr_spec.js +14 -0
- data/vendor/assets/javascripts/jquery_ujs_multipart_xhr.js +172 -0
- data/vendor/assets/javascripts/jquery_ujs_xhr.js +1 -0
- metadata +254 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# Jquery-rails Multipart XHR
|
2
|
+
|
3
|
+
Rails' default jQuery-ujs provides means for handling AJAX requests in which a remote form contains file inputs with values. Adding this adapter expands on that functionality to add support for native XHR uploads when supported by the browser.
|
4
|
+
|
5
|
+
## Rails 3.1
|
6
|
+
|
7
|
+
The gem is configured to work out of the box with the asset pipeline. In `apps/assets/javascripts/application.js` add the following
|
8
|
+
|
9
|
+
//= require jquery
|
10
|
+
//= require jquery_ujs
|
11
|
+
//= require jquery_ujs_multipart_xhr
|
12
|
+
|
13
|
+
Make sure to include jquery and jquery_ujs first, as this library extends the default ujs functionality to support file uploads.
|
14
|
+
|
15
|
+
## Rails 3.0
|
16
|
+
|
17
|
+
Generators are included for copying files to public/javascripts. Either download the `jquery_ujs_multipart_xhr.js` file and add it to your project, or add the gem to your Gemfile and run the generator.
|
18
|
+
|
19
|
+
gem "jquery-rails-multipart-xhr"
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
As with the official ujs library, multipart-xhr is unobtrusive in how it functions. When included, forms are automatically updated to support the additional functionality, provided the browser supports it. Currently the following supports XHR uploading natively:
|
24
|
+
|
25
|
+
* Safari / Chrome (latest)
|
26
|
+
* Firefox 3.6+
|
27
|
+
|
28
|
+
To utilize progress events in your form, bind the `progress` event.
|
29
|
+
|
30
|
+
$('form').bind('progress', function( event ){ ... your code here. });
|
31
|
+
|
32
|
+
The progress event is populated with 3 values for tracking submission progress:
|
33
|
+
|
34
|
+
`loaded`: The bytes currently completed
|
35
|
+
|
36
|
+
`total`: The total number of bytes in the request. ( note, this is a approximate (though fairly accurate) number provided by XHRHttpRequest, see below for getting more accurate totals )
|
37
|
+
|
38
|
+
`percent`: An integer representing the percent complete ( for convenience really )
|
39
|
+
|
40
|
+
Because the `total` provided by XHRHttpRequest isn't always 100% accurate, an additional event `ajax:upload:start` is provided which passes the total bytes of all attached files from their native File objects.
|
41
|
+
|
42
|
+
$('form').bind('ajax:upload:start', function( event, bytes ){
|
43
|
+
// bytes = a sum of all of the attached files, populated from the
|
44
|
+
// input.files[x].fileSize property.
|
45
|
+
});
|
46
|
+
|
47
|
+
|
48
|
+
## Supporting IE?
|
49
|
+
|
50
|
+
In the future we've considered adding support for a flash-based fallback for our friend Internet Explorer ( at least until they release version 19 and finally catch up with the rest of the world (maybe) ) though I'd be interested to know any thoughts people may have on that. Ideally this would include Rack middleware to handle processing files prior to form submission, and then "re-attach" those files back to the resulting form submission. Suggestions are very welcome!
|
51
|
+
|
52
|
+
## Contributing
|
53
|
+
|
54
|
+
File uploading still sucks, mostly due to shoddy browser support (cough... IE), so any suggestions, pull request, ideas, etc to help enhance the user experience are very welcome. As are bug reports etc of course!
|
55
|
+
Brownie points if you have suggestions for testing the submit/event functionality in Jasmine :) Since we're relying on a filled in file input, its kind of a problem.
|
56
|
+
|
57
|
+
### Copyright
|
58
|
+
|
59
|
+
Copyright (c) 2011 Brent Kirby / Kurb Media LLC. Licensed under the MIT license.
|
60
|
+
Do whatever you want with it, just give me a shout if you do something cool k? :)
|
61
|
+
|
62
|
+
|
63
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require File.expand_path('../lib/jquery/rails/multipart_xhr/version', __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "jquery-rails-multipart-xhr"
|
7
|
+
s.version = Jquery::Rails::MultipartXHR::VERSION
|
8
|
+
s.authors = ["Brent Kirby"]
|
9
|
+
s.email = ["brent@kurbmedia.com"]
|
10
|
+
s.homepage = "https://github.com/kurbmedia/jquery-rails-multipart-xhr"
|
11
|
+
s.summary = %q{Adds additional support for XHR file uploads to jquery_ujs}
|
12
|
+
s.description = %q{Rails' default jQuery-ujs provides means for handling AJAX requests in which a remote form contains file inputs with values. Adding this adapter expands on that functionality to add support for native XHR uploads when supported by the browser.}
|
13
|
+
|
14
|
+
s.required_rubygems_version = ">= 1.3.6"
|
15
|
+
s.rubyforge_project = "jquery-rails-multipart-xhr"
|
16
|
+
s.add_dependency "railties", "~> 3.0"
|
17
|
+
s.add_dependency "thor", "~> 0.14"
|
18
|
+
s.add_development_dependency "bundler", "~> 1.0.0"
|
19
|
+
s.add_development_dependency "rails", "~> 3.0"
|
20
|
+
|
21
|
+
s.files = `git ls-files`.split("\n")
|
22
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
23
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
24
|
+
s.require_paths = ["lib"]
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rails'
|
2
|
+
|
3
|
+
module Jquery
|
4
|
+
module MultipartXHR
|
5
|
+
|
6
|
+
module Generators
|
7
|
+
class InstallGenerator < ::Rails::Generators::Base
|
8
|
+
|
9
|
+
desc "This generator installs the jQuery-rails Multipart XHR files"
|
10
|
+
source_root File.expand_path('../../../../../../vendor/assets/javascripts', __FILE__)
|
11
|
+
|
12
|
+
def copy_multipart_xhr_driver
|
13
|
+
say_status("copying", "jQuery UJS Multipart XHR adapter (#{Jquery::Rails::JQUERY_MULTIPART_XHR_VERSION})", :green)
|
14
|
+
copy_file "jquery_ujs_multipart_xhr.js", "public/javascripts/jquery_ujs_multipart_xhr.js"
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end if ::Rails.version < "3.1"
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Jquery
|
2
|
+
module Rails
|
3
|
+
module MultipartXHR
|
4
|
+
if ::Rails.version < "3.1"
|
5
|
+
require 'jquery/rails/multipart_xhr/railtie'
|
6
|
+
else
|
7
|
+
require 'jquery/rails/multipart_xhr/engine'
|
8
|
+
end
|
9
|
+
require 'jquery/rails/multipart_xhr/version'
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
begin
|
2
|
+
require 'jquery/rails/railtie'
|
3
|
+
rescue LoadError
|
4
|
+
end
|
5
|
+
|
6
|
+
module Jquery
|
7
|
+
module Rails
|
8
|
+
|
9
|
+
module MultipartXHR
|
10
|
+
class Railtie < ::Rails::Railtie
|
11
|
+
config.before_configuration do
|
12
|
+
config.action_view.javascript_expansions[:defaults] << 'jquery_ujs_multipart_xhr'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
data/test/.rbenv-gemsets
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
global
|
data/test/index.html
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
2
|
+
"http://www.w3.org/TR/html4/loose.dtd">
|
3
|
+
<html>
|
4
|
+
<head>
|
5
|
+
<title>Jasmine Spec Runner</title>
|
6
|
+
<link rel="stylesheet" type="text/css" href="libs/jasmine.css">
|
7
|
+
<script src="libs/jquery-1.7.2.js" type="text/javascript" charset="utf-8"></script>
|
8
|
+
<script type="text/javascript" src="libs/jasmine.js"></script>
|
9
|
+
<script type="text/javascript" src="libs/jasmine-html.js"></script>
|
10
|
+
<script type="text/javascript" src="libs/jasmine-jquery.js"></script>
|
11
|
+
<script type="text/javascript" src="libs/rails.js"></script>
|
12
|
+
<script type="text/javascript" src="../vendor/assets/javascripts/jquery_ujs_multipart_xhr.js"></script>
|
13
|
+
<!-- include spec files here... -->
|
14
|
+
<script type="text/javascript" src="spec_helper.js"></script>
|
15
|
+
<script type="text/javascript" src="xhr_spec.js"></script>
|
16
|
+
<script type="text/javascript" src="rails_ext_spec.js"></script>
|
17
|
+
<script type="text/javascript" charset="utf-8">
|
18
|
+
//<![CDATA[
|
19
|
+
(function() {
|
20
|
+
var jasmineEnv = jasmine.getEnv();
|
21
|
+
jasmineEnv.updateInterval = 1000;
|
22
|
+
|
23
|
+
var htmlReporter = new jasmine.HtmlReporter();
|
24
|
+
jasmineEnv.addReporter(htmlReporter);
|
25
|
+
jasmineEnv.specFilter = function(spec) {
|
26
|
+
return htmlReporter.specFilter(spec);
|
27
|
+
};
|
28
|
+
|
29
|
+
var currentWindowOnload = window.onload;
|
30
|
+
|
31
|
+
window.onload = function() {
|
32
|
+
if (currentWindowOnload) {
|
33
|
+
currentWindowOnload();
|
34
|
+
}
|
35
|
+
execJasmine();
|
36
|
+
};
|
37
|
+
|
38
|
+
function execJasmine() {
|
39
|
+
jasmineEnv.execute();
|
40
|
+
}
|
41
|
+
|
42
|
+
})();
|
43
|
+
//]]>
|
44
|
+
</script>
|
45
|
+
</head>
|
46
|
+
|
47
|
+
<body>
|
48
|
+
<form id="upload_form" method="post" data-remote="true" action="sendfile.html">
|
49
|
+
<input name="text1" value="" />
|
50
|
+
<input type="hidden" name="_method" value="post" />
|
51
|
+
<input type="file" name="testfile" />
|
52
|
+
<input type="submit" name="submit" value="Submit" />
|
53
|
+
</form>
|
54
|
+
<form id="upload_form2" method="post" data-remote="true" action="sendfile.html">
|
55
|
+
<input name="text2" value="" />
|
56
|
+
<input type="file" name="testfile2" />
|
57
|
+
<input type="hidden" name="_method" value="post" />
|
58
|
+
<input type="submit" name="submit" value="Submit" />
|
59
|
+
</form>
|
60
|
+
</body>
|
61
|
+
</html>
|
@@ -0,0 +1,616 @@
|
|
1
|
+
jasmine.HtmlReporterHelpers = {};
|
2
|
+
|
3
|
+
jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
|
4
|
+
var el = document.createElement(type);
|
5
|
+
|
6
|
+
for (var i = 2; i < arguments.length; i++) {
|
7
|
+
var child = arguments[i];
|
8
|
+
|
9
|
+
if (typeof child === 'string') {
|
10
|
+
el.appendChild(document.createTextNode(child));
|
11
|
+
} else {
|
12
|
+
if (child) {
|
13
|
+
el.appendChild(child);
|
14
|
+
}
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
18
|
+
for (var attr in attrs) {
|
19
|
+
if (attr == "className") {
|
20
|
+
el[attr] = attrs[attr];
|
21
|
+
} else {
|
22
|
+
el.setAttribute(attr, attrs[attr]);
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
return el;
|
27
|
+
};
|
28
|
+
|
29
|
+
jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
|
30
|
+
var results = child.results();
|
31
|
+
var status = results.passed() ? 'passed' : 'failed';
|
32
|
+
if (results.skipped) {
|
33
|
+
status = 'skipped';
|
34
|
+
}
|
35
|
+
|
36
|
+
return status;
|
37
|
+
};
|
38
|
+
|
39
|
+
jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
|
40
|
+
var parentDiv = this.dom.summary;
|
41
|
+
var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
|
42
|
+
var parent = child[parentSuite];
|
43
|
+
|
44
|
+
if (parent) {
|
45
|
+
if (typeof this.views.suites[parent.id] == 'undefined') {
|
46
|
+
this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
|
47
|
+
}
|
48
|
+
parentDiv = this.views.suites[parent.id].element;
|
49
|
+
}
|
50
|
+
|
51
|
+
parentDiv.appendChild(childElement);
|
52
|
+
};
|
53
|
+
|
54
|
+
|
55
|
+
jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
|
56
|
+
for(var fn in jasmine.HtmlReporterHelpers) {
|
57
|
+
ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
|
58
|
+
}
|
59
|
+
};
|
60
|
+
|
61
|
+
jasmine.HtmlReporter = function(_doc) {
|
62
|
+
var self = this;
|
63
|
+
var doc = _doc || window.document;
|
64
|
+
|
65
|
+
var reporterView;
|
66
|
+
|
67
|
+
var dom = {};
|
68
|
+
|
69
|
+
// Jasmine Reporter Public Interface
|
70
|
+
self.logRunningSpecs = false;
|
71
|
+
|
72
|
+
self.reportRunnerStarting = function(runner) {
|
73
|
+
var specs = runner.specs() || [];
|
74
|
+
|
75
|
+
if (specs.length == 0) {
|
76
|
+
return;
|
77
|
+
}
|
78
|
+
|
79
|
+
createReporterDom(runner.env.versionString());
|
80
|
+
doc.body.appendChild(dom.reporter);
|
81
|
+
|
82
|
+
reporterView = new jasmine.HtmlReporter.ReporterView(dom);
|
83
|
+
reporterView.addSpecs(specs, self.specFilter);
|
84
|
+
};
|
85
|
+
|
86
|
+
self.reportRunnerResults = function(runner) {
|
87
|
+
reporterView && reporterView.complete();
|
88
|
+
};
|
89
|
+
|
90
|
+
self.reportSuiteResults = function(suite) {
|
91
|
+
reporterView.suiteComplete(suite);
|
92
|
+
};
|
93
|
+
|
94
|
+
self.reportSpecStarting = function(spec) {
|
95
|
+
if (self.logRunningSpecs) {
|
96
|
+
self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
|
97
|
+
}
|
98
|
+
};
|
99
|
+
|
100
|
+
self.reportSpecResults = function(spec) {
|
101
|
+
reporterView.specComplete(spec);
|
102
|
+
};
|
103
|
+
|
104
|
+
self.log = function() {
|
105
|
+
var console = jasmine.getGlobal().console;
|
106
|
+
if (console && console.log) {
|
107
|
+
if (console.log.apply) {
|
108
|
+
console.log.apply(console, arguments);
|
109
|
+
} else {
|
110
|
+
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
|
111
|
+
}
|
112
|
+
}
|
113
|
+
};
|
114
|
+
|
115
|
+
self.specFilter = function(spec) {
|
116
|
+
if (!focusedSpecName()) {
|
117
|
+
return true;
|
118
|
+
}
|
119
|
+
|
120
|
+
return spec.getFullName().indexOf(focusedSpecName()) === 0;
|
121
|
+
};
|
122
|
+
|
123
|
+
return self;
|
124
|
+
|
125
|
+
function focusedSpecName() {
|
126
|
+
var specName;
|
127
|
+
|
128
|
+
(function memoizeFocusedSpec() {
|
129
|
+
if (specName) {
|
130
|
+
return;
|
131
|
+
}
|
132
|
+
|
133
|
+
var paramMap = [];
|
134
|
+
var params = doc.location.search.substring(1).split('&');
|
135
|
+
|
136
|
+
for (var i = 0; i < params.length; i++) {
|
137
|
+
var p = params[i].split('=');
|
138
|
+
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
|
139
|
+
}
|
140
|
+
|
141
|
+
specName = paramMap.spec;
|
142
|
+
})();
|
143
|
+
|
144
|
+
return specName;
|
145
|
+
}
|
146
|
+
|
147
|
+
function createReporterDom(version) {
|
148
|
+
dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
|
149
|
+
dom.banner = self.createDom('div', { className: 'banner' },
|
150
|
+
self.createDom('span', { className: 'title' }, "Jasmine "),
|
151
|
+
self.createDom('span', { className: 'version' }, version)),
|
152
|
+
|
153
|
+
dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
|
154
|
+
dom.alert = self.createDom('div', {className: 'alert'}),
|
155
|
+
dom.results = self.createDom('div', {className: 'results'},
|
156
|
+
dom.summary = self.createDom('div', { className: 'summary' }),
|
157
|
+
dom.details = self.createDom('div', { id: 'details' }))
|
158
|
+
);
|
159
|
+
}
|
160
|
+
};
|
161
|
+
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);jasmine.HtmlReporter.ReporterView = function(dom) {
|
162
|
+
this.startedAt = new Date();
|
163
|
+
this.runningSpecCount = 0;
|
164
|
+
this.completeSpecCount = 0;
|
165
|
+
this.passedCount = 0;
|
166
|
+
this.failedCount = 0;
|
167
|
+
this.skippedCount = 0;
|
168
|
+
|
169
|
+
this.createResultsMenu = function() {
|
170
|
+
this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
|
171
|
+
this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
|
172
|
+
' | ',
|
173
|
+
this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
|
174
|
+
|
175
|
+
this.summaryMenuItem.onclick = function() {
|
176
|
+
dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
|
177
|
+
};
|
178
|
+
|
179
|
+
this.detailsMenuItem.onclick = function() {
|
180
|
+
showDetails();
|
181
|
+
};
|
182
|
+
};
|
183
|
+
|
184
|
+
this.addSpecs = function(specs, specFilter) {
|
185
|
+
this.totalSpecCount = specs.length;
|
186
|
+
|
187
|
+
this.views = {
|
188
|
+
specs: {},
|
189
|
+
suites: {}
|
190
|
+
};
|
191
|
+
|
192
|
+
for (var i = 0; i < specs.length; i++) {
|
193
|
+
var spec = specs[i];
|
194
|
+
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
|
195
|
+
if (specFilter(spec)) {
|
196
|
+
this.runningSpecCount++;
|
197
|
+
}
|
198
|
+
}
|
199
|
+
};
|
200
|
+
|
201
|
+
this.specComplete = function(spec) {
|
202
|
+
this.completeSpecCount++;
|
203
|
+
|
204
|
+
if (isUndefined(this.views.specs[spec.id])) {
|
205
|
+
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
|
206
|
+
}
|
207
|
+
|
208
|
+
var specView = this.views.specs[spec.id];
|
209
|
+
|
210
|
+
switch (specView.status()) {
|
211
|
+
case 'passed':
|
212
|
+
this.passedCount++;
|
213
|
+
break;
|
214
|
+
|
215
|
+
case 'failed':
|
216
|
+
this.failedCount++;
|
217
|
+
break;
|
218
|
+
|
219
|
+
case 'skipped':
|
220
|
+
this.skippedCount++;
|
221
|
+
break;
|
222
|
+
}
|
223
|
+
|
224
|
+
specView.refresh();
|
225
|
+
this.refresh();
|
226
|
+
};
|
227
|
+
|
228
|
+
this.suiteComplete = function(suite) {
|
229
|
+
var suiteView = this.views.suites[suite.id];
|
230
|
+
if (isUndefined(suiteView)) {
|
231
|
+
return;
|
232
|
+
}
|
233
|
+
suiteView.refresh();
|
234
|
+
};
|
235
|
+
|
236
|
+
this.refresh = function() {
|
237
|
+
|
238
|
+
if (isUndefined(this.resultsMenu)) {
|
239
|
+
this.createResultsMenu();
|
240
|
+
}
|
241
|
+
|
242
|
+
// currently running UI
|
243
|
+
if (isUndefined(this.runningAlert)) {
|
244
|
+
this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"});
|
245
|
+
dom.alert.appendChild(this.runningAlert);
|
246
|
+
}
|
247
|
+
this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
|
248
|
+
|
249
|
+
// skipped specs UI
|
250
|
+
if (isUndefined(this.skippedAlert)) {
|
251
|
+
this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"});
|
252
|
+
}
|
253
|
+
|
254
|
+
this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
|
255
|
+
|
256
|
+
if (this.skippedCount === 1 && isDefined(dom.alert)) {
|
257
|
+
dom.alert.appendChild(this.skippedAlert);
|
258
|
+
}
|
259
|
+
|
260
|
+
// passing specs UI
|
261
|
+
if (isUndefined(this.passedAlert)) {
|
262
|
+
this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"});
|
263
|
+
}
|
264
|
+
this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
|
265
|
+
|
266
|
+
// failing specs UI
|
267
|
+
if (isUndefined(this.failedAlert)) {
|
268
|
+
this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
|
269
|
+
}
|
270
|
+
this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
|
271
|
+
|
272
|
+
if (this.failedCount === 1 && isDefined(dom.alert)) {
|
273
|
+
dom.alert.appendChild(this.failedAlert);
|
274
|
+
dom.alert.appendChild(this.resultsMenu);
|
275
|
+
}
|
276
|
+
|
277
|
+
// summary info
|
278
|
+
this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
|
279
|
+
this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
|
280
|
+
};
|
281
|
+
|
282
|
+
this.complete = function() {
|
283
|
+
dom.alert.removeChild(this.runningAlert);
|
284
|
+
|
285
|
+
this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
|
286
|
+
|
287
|
+
if (this.failedCount === 0) {
|
288
|
+
dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
|
289
|
+
} else {
|
290
|
+
showDetails();
|
291
|
+
}
|
292
|
+
|
293
|
+
dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
|
294
|
+
};
|
295
|
+
|
296
|
+
return this;
|
297
|
+
|
298
|
+
function showDetails() {
|
299
|
+
if (dom.reporter.className.search(/showDetails/) === -1) {
|
300
|
+
dom.reporter.className += " showDetails";
|
301
|
+
}
|
302
|
+
}
|
303
|
+
|
304
|
+
function isUndefined(obj) {
|
305
|
+
return typeof obj === 'undefined';
|
306
|
+
}
|
307
|
+
|
308
|
+
function isDefined(obj) {
|
309
|
+
return !isUndefined(obj);
|
310
|
+
}
|
311
|
+
|
312
|
+
function specPluralizedFor(count) {
|
313
|
+
var str = count + " spec";
|
314
|
+
if (count > 1) {
|
315
|
+
str += "s"
|
316
|
+
}
|
317
|
+
return str;
|
318
|
+
}
|
319
|
+
|
320
|
+
};
|
321
|
+
|
322
|
+
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
|
323
|
+
|
324
|
+
|
325
|
+
jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
|
326
|
+
this.spec = spec;
|
327
|
+
this.dom = dom;
|
328
|
+
this.views = views;
|
329
|
+
|
330
|
+
this.symbol = this.createDom('li', { className: 'pending' });
|
331
|
+
this.dom.symbolSummary.appendChild(this.symbol);
|
332
|
+
|
333
|
+
this.summary = this.createDom('div', { className: 'specSummary' },
|
334
|
+
this.createDom('a', {
|
335
|
+
className: 'description',
|
336
|
+
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
|
337
|
+
title: this.spec.getFullName()
|
338
|
+
}, this.spec.description)
|
339
|
+
);
|
340
|
+
|
341
|
+
this.detail = this.createDom('div', { className: 'specDetail' },
|
342
|
+
this.createDom('a', {
|
343
|
+
className: 'description',
|
344
|
+
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
|
345
|
+
title: this.spec.getFullName()
|
346
|
+
}, this.spec.getFullName())
|
347
|
+
);
|
348
|
+
};
|
349
|
+
|
350
|
+
jasmine.HtmlReporter.SpecView.prototype.status = function() {
|
351
|
+
return this.getSpecStatus(this.spec);
|
352
|
+
};
|
353
|
+
|
354
|
+
jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
|
355
|
+
this.symbol.className = this.status();
|
356
|
+
|
357
|
+
switch (this.status()) {
|
358
|
+
case 'skipped':
|
359
|
+
break;
|
360
|
+
|
361
|
+
case 'passed':
|
362
|
+
this.appendSummaryToSuiteDiv();
|
363
|
+
break;
|
364
|
+
|
365
|
+
case 'failed':
|
366
|
+
this.appendSummaryToSuiteDiv();
|
367
|
+
this.appendFailureDetail();
|
368
|
+
break;
|
369
|
+
}
|
370
|
+
};
|
371
|
+
|
372
|
+
jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
|
373
|
+
this.summary.className += ' ' + this.status();
|
374
|
+
this.appendToSummary(this.spec, this.summary);
|
375
|
+
};
|
376
|
+
|
377
|
+
jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
|
378
|
+
this.detail.className += ' ' + this.status();
|
379
|
+
|
380
|
+
var resultItems = this.spec.results().getItems();
|
381
|
+
var messagesDiv = this.createDom('div', { className: 'messages' });
|
382
|
+
|
383
|
+
for (var i = 0; i < resultItems.length; i++) {
|
384
|
+
var result = resultItems[i];
|
385
|
+
|
386
|
+
if (result.type == 'log') {
|
387
|
+
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
|
388
|
+
} else if (result.type == 'expect' && result.passed && !result.passed()) {
|
389
|
+
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
|
390
|
+
|
391
|
+
if (result.trace.stack) {
|
392
|
+
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
|
393
|
+
}
|
394
|
+
}
|
395
|
+
}
|
396
|
+
|
397
|
+
if (messagesDiv.childNodes.length > 0) {
|
398
|
+
this.detail.appendChild(messagesDiv);
|
399
|
+
this.dom.details.appendChild(this.detail);
|
400
|
+
}
|
401
|
+
};
|
402
|
+
|
403
|
+
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
|
404
|
+
this.suite = suite;
|
405
|
+
this.dom = dom;
|
406
|
+
this.views = views;
|
407
|
+
|
408
|
+
this.element = this.createDom('div', { className: 'suite' },
|
409
|
+
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description)
|
410
|
+
);
|
411
|
+
|
412
|
+
this.appendToSummary(this.suite, this.element);
|
413
|
+
};
|
414
|
+
|
415
|
+
jasmine.HtmlReporter.SuiteView.prototype.status = function() {
|
416
|
+
return this.getSpecStatus(this.suite);
|
417
|
+
};
|
418
|
+
|
419
|
+
jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
|
420
|
+
this.element.className += " " + this.status();
|
421
|
+
};
|
422
|
+
|
423
|
+
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
|
424
|
+
|
425
|
+
/* @deprecated Use jasmine.HtmlReporter instead
|
426
|
+
*/
|
427
|
+
jasmine.TrivialReporter = function(doc) {
|
428
|
+
this.document = doc || document;
|
429
|
+
this.suiteDivs = {};
|
430
|
+
this.logRunningSpecs = false;
|
431
|
+
};
|
432
|
+
|
433
|
+
jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
|
434
|
+
var el = document.createElement(type);
|
435
|
+
|
436
|
+
for (var i = 2; i < arguments.length; i++) {
|
437
|
+
var child = arguments[i];
|
438
|
+
|
439
|
+
if (typeof child === 'string') {
|
440
|
+
el.appendChild(document.createTextNode(child));
|
441
|
+
} else {
|
442
|
+
if (child) { el.appendChild(child); }
|
443
|
+
}
|
444
|
+
}
|
445
|
+
|
446
|
+
for (var attr in attrs) {
|
447
|
+
if (attr == "className") {
|
448
|
+
el[attr] = attrs[attr];
|
449
|
+
} else {
|
450
|
+
el.setAttribute(attr, attrs[attr]);
|
451
|
+
}
|
452
|
+
}
|
453
|
+
|
454
|
+
return el;
|
455
|
+
};
|
456
|
+
|
457
|
+
jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
|
458
|
+
var showPassed, showSkipped;
|
459
|
+
|
460
|
+
this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
|
461
|
+
this.createDom('div', { className: 'banner' },
|
462
|
+
this.createDom('div', { className: 'logo' },
|
463
|
+
this.createDom('span', { className: 'title' }, "Jasmine"),
|
464
|
+
this.createDom('span', { className: 'version' }, runner.env.versionString())),
|
465
|
+
this.createDom('div', { className: 'options' },
|
466
|
+
"Show ",
|
467
|
+
showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
|
468
|
+
this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
|
469
|
+
showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
|
470
|
+
this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
|
471
|
+
)
|
472
|
+
),
|
473
|
+
|
474
|
+
this.runnerDiv = this.createDom('div', { className: 'runner running' },
|
475
|
+
this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
|
476
|
+
this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
|
477
|
+
this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
|
478
|
+
);
|
479
|
+
|
480
|
+
this.document.body.appendChild(this.outerDiv);
|
481
|
+
|
482
|
+
var suites = runner.suites();
|
483
|
+
for (var i = 0; i < suites.length; i++) {
|
484
|
+
var suite = suites[i];
|
485
|
+
var suiteDiv = this.createDom('div', { className: 'suite' },
|
486
|
+
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
|
487
|
+
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
|
488
|
+
this.suiteDivs[suite.id] = suiteDiv;
|
489
|
+
var parentDiv = this.outerDiv;
|
490
|
+
if (suite.parentSuite) {
|
491
|
+
parentDiv = this.suiteDivs[suite.parentSuite.id];
|
492
|
+
}
|
493
|
+
parentDiv.appendChild(suiteDiv);
|
494
|
+
}
|
495
|
+
|
496
|
+
this.startedAt = new Date();
|
497
|
+
|
498
|
+
var self = this;
|
499
|
+
showPassed.onclick = function(evt) {
|
500
|
+
if (showPassed.checked) {
|
501
|
+
self.outerDiv.className += ' show-passed';
|
502
|
+
} else {
|
503
|
+
self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
|
504
|
+
}
|
505
|
+
};
|
506
|
+
|
507
|
+
showSkipped.onclick = function(evt) {
|
508
|
+
if (showSkipped.checked) {
|
509
|
+
self.outerDiv.className += ' show-skipped';
|
510
|
+
} else {
|
511
|
+
self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
|
512
|
+
}
|
513
|
+
};
|
514
|
+
};
|
515
|
+
|
516
|
+
jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
|
517
|
+
var results = runner.results();
|
518
|
+
var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
|
519
|
+
this.runnerDiv.setAttribute("class", className);
|
520
|
+
//do it twice for IE
|
521
|
+
this.runnerDiv.setAttribute("className", className);
|
522
|
+
var specs = runner.specs();
|
523
|
+
var specCount = 0;
|
524
|
+
for (var i = 0; i < specs.length; i++) {
|
525
|
+
if (this.specFilter(specs[i])) {
|
526
|
+
specCount++;
|
527
|
+
}
|
528
|
+
}
|
529
|
+
var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
|
530
|
+
message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
|
531
|
+
this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
|
532
|
+
|
533
|
+
this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
|
534
|
+
};
|
535
|
+
|
536
|
+
jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
|
537
|
+
var results = suite.results();
|
538
|
+
var status = results.passed() ? 'passed' : 'failed';
|
539
|
+
if (results.totalCount === 0) { // todo: change this to check results.skipped
|
540
|
+
status = 'skipped';
|
541
|
+
}
|
542
|
+
this.suiteDivs[suite.id].className += " " + status;
|
543
|
+
};
|
544
|
+
|
545
|
+
jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
|
546
|
+
if (this.logRunningSpecs) {
|
547
|
+
this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
|
548
|
+
}
|
549
|
+
};
|
550
|
+
|
551
|
+
jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
|
552
|
+
var results = spec.results();
|
553
|
+
var status = results.passed() ? 'passed' : 'failed';
|
554
|
+
if (results.skipped) {
|
555
|
+
status = 'skipped';
|
556
|
+
}
|
557
|
+
var specDiv = this.createDom('div', { className: 'spec ' + status },
|
558
|
+
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
|
559
|
+
this.createDom('a', {
|
560
|
+
className: 'description',
|
561
|
+
href: '?spec=' + encodeURIComponent(spec.getFullName()),
|
562
|
+
title: spec.getFullName()
|
563
|
+
}, spec.description));
|
564
|
+
|
565
|
+
|
566
|
+
var resultItems = results.getItems();
|
567
|
+
var messagesDiv = this.createDom('div', { className: 'messages' });
|
568
|
+
for (var i = 0; i < resultItems.length; i++) {
|
569
|
+
var result = resultItems[i];
|
570
|
+
|
571
|
+
if (result.type == 'log') {
|
572
|
+
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
|
573
|
+
} else if (result.type == 'expect' && result.passed && !result.passed()) {
|
574
|
+
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
|
575
|
+
|
576
|
+
if (result.trace.stack) {
|
577
|
+
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
|
578
|
+
}
|
579
|
+
}
|
580
|
+
}
|
581
|
+
|
582
|
+
if (messagesDiv.childNodes.length > 0) {
|
583
|
+
specDiv.appendChild(messagesDiv);
|
584
|
+
}
|
585
|
+
|
586
|
+
this.suiteDivs[spec.suite.id].appendChild(specDiv);
|
587
|
+
};
|
588
|
+
|
589
|
+
jasmine.TrivialReporter.prototype.log = function() {
|
590
|
+
var console = jasmine.getGlobal().console;
|
591
|
+
if (console && console.log) {
|
592
|
+
if (console.log.apply) {
|
593
|
+
console.log.apply(console, arguments);
|
594
|
+
} else {
|
595
|
+
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
|
596
|
+
}
|
597
|
+
}
|
598
|
+
};
|
599
|
+
|
600
|
+
jasmine.TrivialReporter.prototype.getLocation = function() {
|
601
|
+
return this.document.location;
|
602
|
+
};
|
603
|
+
|
604
|
+
jasmine.TrivialReporter.prototype.specFilter = function(spec) {
|
605
|
+
var paramMap = {};
|
606
|
+
var params = this.getLocation().search.substring(1).split('&');
|
607
|
+
for (var i = 0; i < params.length; i++) {
|
608
|
+
var p = params[i].split('=');
|
609
|
+
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
|
610
|
+
}
|
611
|
+
|
612
|
+
if (!paramMap.spec) {
|
613
|
+
return true;
|
614
|
+
}
|
615
|
+
return spec.getFullName().indexOf(paramMap.spec) === 0;
|
616
|
+
};
|