jasmine 0.10.2.4 → 0.10.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -17,7 +17,11 @@ jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarA
17
17
  }
18
18
 
19
19
  for (var attr in attrs) {
20
+ if (attr == "className") {
20
21
  el[attr] = attrs[attr];
22
+ } else {
23
+ el.setAttribute(attr, attrs[attr]);
24
+ }
21
25
  }
22
26
 
23
27
  return el;
@@ -26,10 +30,29 @@ jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarA
26
30
  jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
27
31
  var suites = runner.suites();
28
32
 
29
- this.runnerDiv = this.createDom('div', { className: 'runner running' },
30
- this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
31
- this.runnerMessageSpan = this.createDom('span', {}, "Running..."));
32
- this.document.body.appendChild(this.runnerDiv);
33
+ var showPassed, showSkipped;
34
+
35
+ this.outerDiv = this.createDom('div', { className: 'jasmine_reporter' },
36
+ this.createDom('div', { className: 'banner' },
37
+ this.createDom('div', { className: 'logo' },
38
+ "Jasmine",
39
+ this.createDom('span', { className: 'version' }, runner.env.versionString())),
40
+ this.createDom('div', { className: 'options' },
41
+ "Show ",
42
+ showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
43
+ this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
44
+ showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
45
+ this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
46
+ )
47
+ ),
48
+
49
+ this.runnerDiv = this.createDom('div', { className: 'runner running' },
50
+ this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
51
+ this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
52
+ this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
53
+ );
54
+
55
+ this.document.body.appendChild(this.outerDiv);
33
56
 
34
57
  for (var i = 0; i < suites.length; i++) {
35
58
  var suite = suites[i];
@@ -37,7 +60,7 @@ jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
37
60
  this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
38
61
  this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
39
62
  this.suiteDivs[suite.getFullName()] = suiteDiv;
40
- var parentDiv = this.document.body;
63
+ var parentDiv = this.outerDiv;
41
64
  if (suite.parentSuite) {
42
65
  parentDiv = this.suiteDivs[suite.parentSuite.getFullName()];
43
66
  }
@@ -45,6 +68,23 @@ jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
45
68
  }
46
69
 
47
70
  this.startedAt = new Date();
71
+
72
+ var self = this;
73
+ showPassed.onchange = function(evt) {
74
+ if (evt.target.checked) {
75
+ self.outerDiv.className += ' show-passed';
76
+ } else {
77
+ self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
78
+ }
79
+ };
80
+
81
+ showSkipped.onchange = function(evt) {
82
+ if (evt.target.checked) {
83
+ self.outerDiv.className += ' show-skipped';
84
+ } else {
85
+ self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
86
+ }
87
+ };
48
88
  };
49
89
 
50
90
  jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
@@ -63,6 +103,8 @@ jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
63
103
  var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
64
104
  message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
65
105
  this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
106
+
107
+ this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
66
108
  };
67
109
 
68
110
  jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
@@ -82,17 +124,30 @@ jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
82
124
  }
83
125
  var specDiv = this.createDom('div', { className: 'spec ' + status },
84
126
  this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
85
- this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, spec.getFullName()));
127
+ this.createDom('a', {
128
+ className: 'description',
129
+ href: '?spec=' + encodeURIComponent(spec.getFullName()),
130
+ title: spec.getFullName()
131
+ }, spec.description));
86
132
 
87
133
 
88
134
  var resultItems = results.getItems();
135
+ var messagesDiv = this.createDom('div', { className: 'messages' });
89
136
  for (var i = 0; i < resultItems.length; i++) {
90
137
  var result = resultItems[i];
91
138
  if (result.passed && !result.passed()) {
92
- specDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
93
- specDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
139
+ messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
140
+
141
+ if (result.trace.stack) {
142
+ messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
143
+ }
94
144
  }
95
145
  }
146
+
147
+ if (messagesDiv.childNodes.length > 0) {
148
+ specDiv.appendChild(messagesDiv);
149
+ }
150
+
96
151
  this.suiteDivs[spec.suite.getFullName()].appendChild(specDiv);
97
152
  };
98
153
 
@@ -114,4 +169,4 @@ jasmine.TrivialReporter.prototype.specFilter = function(spec) {
114
169
 
115
170
  if (!paramMap["spec"]) return true;
116
171
  return spec.getFullName().indexOf(paramMap["spec"]) == 0;
117
- };
172
+ };
@@ -13,7 +13,7 @@ jasmine.unimplementedMethod_ = function() {
13
13
  };
14
14
 
15
15
  /**
16
- * Use <code>jasmine.undefined</code> instead of <code>undefined</code>, since <code>undefined</code is just
16
+ * Use <code>jasmine.undefined</code> instead of <code>undefined</code>, since <code>undefined</code> is just
17
17
  * a plain old variable and may be redefined by somebody else.
18
18
  *
19
19
  * @private
@@ -558,6 +558,7 @@ jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() {
558
558
  *
559
559
  * @param {String} url path to the file to include
560
560
  * @param {Boolean} opt_global
561
+ * @deprecated We suggest you use a different method of including JS source files. <code>jasmine.include</code> will be removed soon.
561
562
  */
562
563
  jasmine.include = function(url, opt_global) {
563
564
  if (opt_global) {
@@ -690,6 +691,18 @@ jasmine.Env.prototype.version = function () {
690
691
  }
691
692
  };
692
693
 
694
+ /**
695
+ * @returns string containing jasmine version build info, if set.
696
+ */
697
+ jasmine.Env.prototype.versionString = function() {
698
+ if (jasmine.version_) {
699
+ var version = this.version();
700
+ return version.major + "." + version.minor + "." + version.build + " revision " + version.revision;
701
+ } else {
702
+ return "version unknown";
703
+ }
704
+ };
705
+
693
706
  /**
694
707
  * @returns a sequential integer starting at 0
695
708
  */
@@ -2313,6 +2326,6 @@ window.clearInterval = function(timeoutKey) {
2313
2326
  jasmine.version_= {
2314
2327
  "major": 0,
2315
2328
  "minor": 10,
2316
- "build": 2,
2317
- "revision": 1268969696
2329
+ "build": 3,
2330
+ "revision": 1270162784
2318
2331
  };
@@ -3,33 +3,67 @@ body {
3
3
  }
4
4
 
5
5
 
6
- body .run_spec {
7
- float:right;
6
+ .jasmine_reporter a:visited, .jasmine_reporter a {
7
+ color: #303;
8
8
  }
9
9
 
10
- .runner.running {
11
- background-color: yellow;
10
+ .jasmine_reporter a:hover, .jasmine_reporter a:active {
11
+ color: blue;
12
12
  }
13
13
 
14
+ .run_spec {
15
+ float:right;
16
+ padding-right: 5px;
17
+ font-size: .8em;
18
+ text-decoration: none;
19
+ }
14
20
 
21
+ .jasmine_reporter {
22
+ margin: 0 5px;
23
+ }
15
24
 
16
- .runner {
17
- border: 1px solid gray;
18
- margin: 5px;
25
+ .banner {
26
+ color: #303;
27
+ background-color: #fef;
28
+ padding: 5px;
29
+ }
30
+
31
+ .logo {
32
+ float: left;
33
+ font-size: 1.1em;
34
+ padding-left: 5px;
35
+ }
36
+
37
+ .logo .version {
38
+ font-size: .6em;
19
39
  padding-left: 1em;
20
- padding-right: 1em;
40
+ }
41
+
42
+ .runner.running {
43
+ background-color: yellow;
44
+ }
45
+
46
+
47
+ .options {
48
+ text-align: right;
49
+ font-size: .8em;
21
50
  }
22
51
 
23
52
 
24
53
 
54
+
25
55
  .suite {
26
56
  border: 1px outset gray;
27
- margin: 5px;
57
+ margin: 5px 0;
28
58
  padding-left: 1em;
29
59
  }
30
60
 
61
+ .suite .suite {
62
+ margin: 5px;
63
+ }
64
+
31
65
  .suite.passed {
32
- background-color: #cfc;
66
+ background-color: #dfd;
33
67
  }
34
68
 
35
69
  .suite.failed {
@@ -38,22 +72,51 @@ body .run_spec {
38
72
 
39
73
  .spec {
40
74
  margin: 5px;
75
+ padding-left: 1em;
41
76
  clear: both;
42
77
  }
43
78
 
79
+ .spec.failed, .spec.passed, .spec.skipped {
80
+ padding-bottom: 5px;
81
+ border: 1px solid gray;
82
+ }
83
+
84
+ .spec.failed {
85
+ background-color: #fbb;
86
+ border-color: red;
87
+ }
88
+
89
+ .spec.passed {
90
+ background-color: #bfb;
91
+ border-color: green;
92
+ }
93
+
94
+ .spec.skipped {
95
+ background-color: #bbb;
96
+ }
97
+
98
+ .messages {
99
+ border-left: 1px dashed gray;
100
+ padding-left: 1em;
101
+ padding-right: 1em;
102
+ }
103
+
44
104
  .passed {
45
105
  background-color: #cfc;
106
+ display: none;
46
107
  }
47
108
 
48
109
  .failed {
49
- background-color: #fdd;
110
+ background-color: #fbb;
50
111
  }
51
112
 
52
113
  .skipped {
53
114
  color: #777;
54
115
  background-color: #eee;
116
+ display: none;
55
117
  }
56
118
 
119
+
57
120
  /*.resultMessage {*/
58
121
  /*white-space: pre;*/
59
122
  /*}*/
@@ -72,15 +135,32 @@ body .run_spec {
72
135
  white-space: pre;
73
136
  font-size: .8em;
74
137
  margin-left: 10px;
75
- height: 5em;
138
+ max-height: 5em;
76
139
  overflow: auto;
77
140
  border: 1px inset red;
78
141
  padding: 1em;
79
142
  background: #eef;
80
143
  }
81
144
 
145
+ .finished-at {
146
+ padding-left: 1em;
147
+ font-size: .6em;
148
+ }
149
+
150
+ .show-passed .passed,
151
+ .show-skipped .skipped {
152
+ display: block;
153
+ }
154
+
82
155
 
83
156
  #jasmine_content {
84
157
  position:fixed;
85
158
  right: 100%;
86
159
  }
160
+
161
+ .runner {
162
+ border: 1px solid gray;
163
+ display: block;
164
+ margin: 5px 0;
165
+ padding: 2px 0 2px 10px;
166
+ }
@@ -4,7 +4,7 @@ require 'json'
4
4
 
5
5
  module Jasmine
6
6
  def self.root
7
- File.expand_path(File.join(File.dirname(__FILE__), '../../jasmine'))
7
+ ENV["JASMINE_ROOT"] || File.expand_path(File.join(File.dirname(__FILE__), '../../jasmine'))
8
8
  end
9
9
 
10
10
  # this seemingly-over-complex method is necessary to get an open port on at least some of our Macs
@@ -60,4 +60,4 @@ module Jasmine
60
60
  "#{file_name}?cachebust=#{digest}"
61
61
  end
62
62
  end
63
- end
63
+ end
@@ -119,4 +119,4 @@ module Jasmine
119
119
  thin.stop
120
120
  end
121
121
  end
122
- end
122
+ end
@@ -46,7 +46,7 @@ describe Jasmine::Server do
46
46
 
47
47
  it "should serve focused suites when prefixing spec files with /__suite__/" do
48
48
  Dir.stub!(:glob).and_return do |glob_string|
49
- glob_string
49
+ [glob_string]
50
50
  end
51
51
  code, headers, body = @thin_app.call("PATH_INFO" => "/__suite__/file2.js", "SCRIPT_NAME" => "xxx")
52
52
  code.should == 200
@@ -82,7 +82,5 @@ describe Jasmine::Server do
82
82
  headers.should == { 'Content-Type' => 'text/html' }
83
83
  body.should == ''
84
84
  end
85
-
86
85
  end
87
-
88
- end
86
+ end
@@ -1,3 +1,6 @@
1
+ require File.expand_path('../../.bundle/environment', __FILE__)
2
+ Bundler.require(:default, :test)
3
+
1
4
  require 'spec'
2
5
 
3
- require File.expand_path(File.join(File.dirname(__FILE__), "../lib/jasmine"))
6
+ require File.expand_path(File.join(File.dirname(__FILE__), "../lib/jasmine"))
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jasmine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.2.4
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 10
8
+ - 3
9
+ version: 0.10.3
5
10
  platform: ruby
6
11
  authors:
7
12
  - Rajan Agaskar
@@ -15,64 +20,88 @@ default_executable: jasmine
15
20
  dependencies:
16
21
  - !ruby/object:Gem::Dependency
17
22
  name: rspec
18
- type: :runtime
19
- version_requirement:
20
- version_requirements: !ruby/object:Gem::Requirement
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
21
25
  requirements:
22
26
  - - ">="
23
27
  - !ruby/object:Gem::Version
28
+ segments:
29
+ - 1
30
+ - 1
31
+ - 5
24
32
  version: 1.1.5
25
- version:
33
+ type: :runtime
34
+ version_requirements: *id001
26
35
  - !ruby/object:Gem::Dependency
27
36
  name: json
28
- type: :runtime
29
- version_requirement:
30
- version_requirements: !ruby/object:Gem::Requirement
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
31
39
  requirements:
32
40
  - - ">="
33
41
  - !ruby/object:Gem::Version
42
+ segments:
43
+ - 1
44
+ - 1
45
+ - 9
34
46
  version: 1.1.9
35
- version:
47
+ type: :runtime
48
+ version_requirements: *id002
36
49
  - !ruby/object:Gem::Dependency
37
50
  name: rack
38
- type: :runtime
39
- version_requirement:
40
- version_requirements: !ruby/object:Gem::Requirement
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
41
53
  requirements:
42
54
  - - ">="
43
55
  - !ruby/object:Gem::Version
56
+ segments:
57
+ - 1
58
+ - 0
59
+ - 0
44
60
  version: 1.0.0
45
- version:
61
+ type: :runtime
62
+ version_requirements: *id003
46
63
  - !ruby/object:Gem::Dependency
47
64
  name: thin
48
- type: :runtime
49
- version_requirement:
50
- version_requirements: !ruby/object:Gem::Requirement
65
+ prerelease: false
66
+ requirement: &id004 !ruby/object:Gem::Requirement
51
67
  requirements:
52
68
  - - ">="
53
69
  - !ruby/object:Gem::Version
70
+ segments:
71
+ - 1
72
+ - 2
73
+ - 4
54
74
  version: 1.2.4
55
- version:
75
+ type: :runtime
76
+ version_requirements: *id004
56
77
  - !ruby/object:Gem::Dependency
57
78
  name: selenium-rc
58
- type: :runtime
59
- version_requirement:
60
- version_requirements: !ruby/object:Gem::Requirement
79
+ prerelease: false
80
+ requirement: &id005 !ruby/object:Gem::Requirement
61
81
  requirements:
62
82
  - - ">="
63
83
  - !ruby/object:Gem::Version
84
+ segments:
85
+ - 2
86
+ - 1
87
+ - 0
64
88
  version: 2.1.0
65
- version:
89
+ type: :runtime
90
+ version_requirements: *id005
66
91
  - !ruby/object:Gem::Dependency
67
92
  name: selenium-client
68
- type: :runtime
69
- version_requirement:
70
- version_requirements: !ruby/object:Gem::Requirement
93
+ prerelease: false
94
+ requirement: &id006 !ruby/object:Gem::Requirement
71
95
  requirements:
72
96
  - - ">="
73
97
  - !ruby/object:Gem::Version
98
+ segments:
99
+ - 1
100
+ - 2
101
+ - 17
74
102
  version: 1.2.17
75
- version:
103
+ type: :runtime
104
+ version_requirements: *id006
76
105
  description: Javascript BDD test framework
77
106
  email: ragaskar@gmail.com
78
107
  executables:
@@ -90,12 +119,9 @@ files:
90
119
  - generators/jasmine/templates/spec/javascripts/support/jasmine-rails.yml
91
120
  - generators/jasmine/templates/spec/javascripts/support/jasmine.yml
92
121
  - generators/jasmine/templates/spec/javascripts/support/jasmine_runner.rb
93
- - jasmine/contrib/ruby/jasmine_runner.rb
94
- - jasmine/contrib/ruby/jasmine_spec_builder.rb
95
- - jasmine/contrib/ruby/run.html
96
122
  - jasmine/lib/TrivialReporter.js
97
123
  - jasmine/lib/consolex.js
98
- - jasmine/lib/jasmine-0.10.2.js
124
+ - jasmine/lib/jasmine-0.10.3.js
99
125
  - jasmine/lib/jasmine.css
100
126
  - jasmine/lib/json2.js
101
127
  - lib/jasmine.rb
@@ -119,18 +145,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
119
145
  requirements:
120
146
  - - ">="
121
147
  - !ruby/object:Gem::Version
148
+ segments:
149
+ - 0
122
150
  version: "0"
123
- version:
124
151
  required_rubygems_version: !ruby/object:Gem::Requirement
125
152
  requirements:
126
153
  - - ">="
127
154
  - !ruby/object:Gem::Version
155
+ segments:
156
+ - 0
128
157
  version: "0"
129
- version:
130
158
  requirements: []
131
159
 
132
160
  rubyforge_project:
133
- rubygems_version: 1.3.5
161
+ rubygems_version: 1.3.6
134
162
  signing_key:
135
163
  specification_version: 3
136
164
  summary: Jasmine Ruby Runner
@@ -1,334 +0,0 @@
1
- require 'socket'
2
- require 'erb'
3
- require 'json'
4
-
5
- module Jasmine
6
- def self.root
7
- File.expand_path(File.join(File.dirname(__FILE__), '../..'))
8
- end
9
-
10
- # this seemingly-over-complex method is necessary to get an open port on at least some of our Macs
11
- def self.open_socket_on_unused_port
12
- infos = Socket::getaddrinfo("localhost", nil, Socket::AF_UNSPEC, Socket::SOCK_STREAM, 0, Socket::AI_PASSIVE)
13
- families = Hash[*infos.collect { |af, *_| af }.uniq.zip([]).flatten]
14
-
15
- return TCPServer.open('0.0.0.0', 0) if families.has_key?('AF_INET')
16
- return TCPServer.open('::', 0) if families.has_key?('AF_INET6')
17
- return TCPServer.open(0)
18
- end
19
-
20
- def self.find_unused_port
21
- socket = open_socket_on_unused_port
22
- port = socket.addr[1]
23
- socket.close
24
- port
25
- end
26
-
27
- def self.server_is_listening_on(hostname, port)
28
- require 'socket'
29
- begin
30
- socket = TCPSocket.open(hostname, port)
31
- rescue Errno::ECONNREFUSED
32
- return false
33
- end
34
- socket.close
35
- true
36
- end
37
-
38
- def self.wait_for_listener(port, name = "required process", seconds_to_wait = 10)
39
- time_out_at = Time.now + seconds_to_wait
40
- until server_is_listening_on "localhost", port
41
- sleep 0.1
42
- puts "Waiting for #{name} on #{port}..."
43
- raise "#{name} didn't show up on port #{port} after #{seconds_to_wait} seconds." if Time.now > time_out_at
44
- end
45
- end
46
-
47
- def self.kill_process_group(process_group_id, signal="TERM")
48
- Process.kill signal, -process_group_id # negative pid means kill process group. (see man 2 kill)
49
- end
50
-
51
- def self.cachebust(files, root_dir="", replace=nil, replace_with=nil)
52
- require 'digest/md5'
53
- files.collect do |file_name|
54
- real_file_name = replace && replace_with ? file_name.sub(replace, replace_with) : file_name
55
- begin
56
- digest = Digest::MD5.hexdigest(File.read("#{root_dir}#{real_file_name}"))
57
- rescue
58
- digest = "MISSING-FILE"
59
- end
60
- "#{file_name}?cachebust=#{digest}"
61
- end
62
- end
63
-
64
- class RunAdapter
65
- def initialize(spec_files_or_proc, options = {})
66
- @spec_files_or_proc = Jasmine.files(spec_files_or_proc) || []
67
- @jasmine_files = Jasmine.files(options[:jasmine_files]) || [
68
- "/__JASMINE_ROOT__/lib/" + File.basename(Dir.glob("#{Jasmine.root}/lib/jasmine*.js").first),
69
- "/__JASMINE_ROOT__/lib/TrivialReporter.js",
70
- "/__JASMINE_ROOT__/lib/json2.js",
71
- "/__JASMINE_ROOT__/lib/consolex.js",
72
- ]
73
- @stylesheets = ["/__JASMINE_ROOT__/lib/jasmine.css"] + (Jasmine.files(options[:stylesheets]) || [])
74
- @spec_helpers = Jasmine.files(options[:spec_helpers]) || []
75
- end
76
-
77
- def call(env)
78
- run
79
- end
80
-
81
- def run
82
- stylesheets = @stylesheets
83
- spec_helpers = @spec_helpers
84
- spec_files = @spec_files_or_proc
85
-
86
- jasmine_files = @jasmine_files
87
- jasmine_files = jasmine_files.call if jasmine_files.respond_to?(:call)
88
-
89
- css_files = @stylesheets
90
-
91
-
92
- body = ERB.new(File.read(File.join(File.dirname(__FILE__), "run.html"))).result(binding)
93
- [
94
- 200,
95
- { 'Content-Type' => 'text/html' },
96
- body
97
- ]
98
- end
99
-
100
-
101
- end
102
-
103
- class Redirect
104
- def initialize(url)
105
- @url = url
106
- end
107
-
108
- def call(env)
109
- [
110
- 302,
111
- { 'Location' => @url },
112
- []
113
- ]
114
- end
115
- end
116
-
117
- class JsAlert
118
- def call(env)
119
- [
120
- 200,
121
- { 'Content-Type' => 'application/javascript' },
122
- "document.write('<p>Couldn\\'t load #{env["PATH_INFO"]}!</p>');"
123
- ]
124
- end
125
- end
126
-
127
- class FocusedSuite
128
- def initialize(spec_files_or_proc, options)
129
- @spec_files_or_proc = Jasmine.files(spec_files_or_proc) || []
130
- @options = options
131
- end
132
-
133
- def call(env)
134
- spec_files = @spec_files_or_proc
135
- matching_specs = spec_files.select {|spec_file| spec_file =~ /#{Regexp.escape(env["PATH_INFO"])}/ }.compact
136
- if !matching_specs.empty?
137
- run_adapter = Jasmine::RunAdapter.new(matching_specs, @options)
138
- run_adapter.run
139
- else
140
- [
141
- 200,
142
- { 'Content-Type' => 'application/javascript' },
143
- "document.write('<p>Couldn\\'t find any specs matching #{env["PATH_INFO"]}!</p>');"
144
- ]
145
- end
146
- end
147
-
148
- end
149
-
150
- class SimpleServer
151
- def self.start(port, spec_files_or_proc, mappings, options = {})
152
- require 'thin'
153
- config = {
154
- '/__suite__' => Jasmine::FocusedSuite.new(spec_files_or_proc, options),
155
- '/run.html' => Jasmine::Redirect.new('/'),
156
- '/' => Jasmine::RunAdapter.new(spec_files_or_proc, options)
157
- }
158
- mappings.each do |from, to|
159
- config[from] = Rack::File.new(to)
160
- end
161
-
162
- config["/__JASMINE_ROOT__"] = Rack::File.new(Jasmine.root)
163
-
164
- app = Rack::Cascade.new([
165
- Rack::URLMap.new(config),
166
- JsAlert.new
167
- ])
168
-
169
- begin
170
- Thin::Server.start('0.0.0.0', port, app)
171
- rescue RuntimeError => e
172
- raise e unless e.message == 'no acceptor'
173
- raise RuntimeError.new("A server is already running on port #{port}")
174
- end
175
- end
176
- end
177
-
178
- class SimpleClient
179
- def initialize(selenium_host, selenium_port, selenium_browser_start_command, http_address)
180
- require 'selenium/client'
181
- @driver = Selenium::Client::Driver.new(
182
- selenium_host,
183
- selenium_port,
184
- selenium_browser_start_command,
185
- http_address
186
- )
187
- @http_address = http_address
188
- end
189
-
190
- def tests_have_finished?
191
- @driver.get_eval("window.jasmine.getEnv().currentRunner.finished") == "true"
192
- end
193
-
194
- def connect
195
- @driver.start
196
- @driver.open("/")
197
- end
198
-
199
- def disconnect
200
- @driver.stop
201
- end
202
-
203
- def run
204
- until tests_have_finished? do
205
- sleep 0.1
206
- end
207
-
208
- puts @driver.get_eval("window.results()")
209
- failed_count = @driver.get_eval("window.jasmine.getEnv().currentRunner.results().failedCount").to_i
210
- failed_count == 0
211
- end
212
-
213
- def eval_js(script)
214
- escaped_script = "'" + script.gsub(/(['\\])/) { '\\' + $1 } + "'"
215
-
216
- result = @driver.get_eval(" try { eval(#{escaped_script}, window); } catch(err) { window.eval(#{escaped_script}); }")
217
- JSON.parse("[#{result}]")[0]
218
- end
219
- end
220
-
221
- class Runner
222
- def initialize(selenium_jar_path, spec_files, dir_mappings, options={})
223
- @selenium_jar_path = selenium_jar_path
224
- @spec_files = spec_files
225
- @dir_mappings = dir_mappings
226
- @options = options
227
-
228
- @browser = options[:browser] ? options[:browser].delete(:browser) : 'firefox'
229
- @selenium_pid = nil
230
- @jasmine_server_pid = nil
231
- @selenium_host = 'localhost'
232
- @jasmine_server_port = Jasmine::find_unused_port
233
- @selenium_server_port = Jasmine::find_unused_port
234
- end
235
-
236
- def start
237
- start_jasmine_server
238
- start_selenium_server
239
- @client = Jasmine::SimpleClient.new(@selenium_host, @selenium_server_port, "*#{@browser}", "http://localhost:#{@jasmine_server_port}/")
240
- @client.connect
241
- end
242
-
243
- def stop
244
- @client.disconnect
245
- stop_selenium_server
246
- stop_jasmine_server
247
- end
248
-
249
- def start_jasmine_server
250
- @jasmine_server_pid = fork do
251
- Process.setpgrp
252
- Jasmine::SimpleServer.start(@jasmine_server_port, @spec_files, @dir_mappings, @options)
253
- exit! 0
254
- end
255
- puts "jasmine server started. pid is #{@jasmine_server_pid}"
256
- Jasmine::wait_for_listener(@jasmine_server_port, "jasmine server")
257
- end
258
-
259
- def start_selenium_server
260
- @selenium_pid = fork do
261
- Process.setpgrp
262
- exec "java -jar #{@selenium_jar_path} -port #{@selenium_server_port} > /dev/null 2>&1"
263
- end
264
- puts "selenium started. pid is #{@selenium_pid}"
265
- Jasmine::wait_for_listener(@selenium_server_port, "selenium server")
266
- end
267
-
268
- def stop_jasmine_server
269
- puts "shutting down Jasmine server..."
270
- Jasmine::kill_process_group(@jasmine_server_pid) if @jasmine_server_pid
271
- end
272
-
273
- def stop_selenium_server
274
- puts "shutting down Selenium server..."
275
- Jasmine::kill_process_group(@selenium_pid) if @selenium_pid
276
- end
277
-
278
- def run
279
- begin
280
- start
281
- puts "servers are listening on their ports -- running the test script..."
282
- tests_passed = @client.run
283
- ensure
284
- stop
285
- end
286
- return tests_passed
287
- end
288
-
289
- def eval_js(script)
290
- @client.eval_js(script)
291
- end
292
- end
293
-
294
- class SauceLabsRunner < Runner
295
- def initialize(spec_files, dir_mappings, options={})
296
- @spec_files = spec_files
297
- @dir_mappings = dir_mappings
298
- @options = options
299
-
300
- @browser = options[:browser] ? options[:browser].delete(:browser) : 'firefox'
301
- @jasmine_server_pid = nil
302
- @jasmine_server_port = Jasmine::find_unused_port
303
- @saucelabs_config = SeleniumConfig.new(options[:saucelabs_config], options[:saucelabs_config_file], @jasmine_server_port)
304
- end
305
-
306
- def start_selenium_server
307
- @sauce_tunnel = SauceTunnel.new(@saucelabs_config)
308
- end
309
-
310
- def start
311
- start_jasmine_server
312
- start_selenium_server
313
- @client = Jasmine::SimpleClient.new(@saucelabs_config['selenium_server_address'],
314
- 4444,
315
- @saucelabs_config['selenium_browser_key'],
316
- "http://#{@saucelabs_config['application_address']}")
317
- @client.connect
318
- end
319
-
320
- def stop
321
- @client.disconnect
322
- @sauce_tunnel.shutdown
323
- stop_jasmine_server
324
- end
325
-
326
- end
327
-
328
- def self.files(f)
329
- result = f
330
- result = result.call if result.respond_to?(:call)
331
- result
332
- end
333
-
334
- end
@@ -1,153 +0,0 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), "jasmine_runner.rb"))
2
- require 'enumerator'
3
- module Jasmine
4
-
5
- class SpecBuilder
6
- attr_accessor :suites
7
-
8
- def initialize(spec_files, runner)
9
- @spec_files = spec_files
10
- @runner = runner
11
- @spec_ids = []
12
- end
13
-
14
- def start
15
- guess_example_locations
16
-
17
- @runner.start
18
- load_suite_info
19
- wait_for_suites_to_finish_running
20
- end
21
-
22
- def stop
23
- @runner.stop
24
- end
25
-
26
- def script_path
27
- File.expand_path(__FILE__)
28
- end
29
-
30
- def guess_example_locations
31
- @example_locations = {}
32
-
33
- example_name_parts = []
34
- previous_indent_level = 0
35
- @spec_files.each do |filename|
36
- line_number = 1
37
- File.open(filename, "r") do |file|
38
- file.readlines.each do |line|
39
- match = /^(\s*)(describe|it)\s*\(\s*["'](.*)["']\s*,\s*function/.match(line)
40
- if (match)
41
- indent_level = match[1].length / 2
42
- example_name = match[3]
43
- example_name_parts[indent_level] = example_name
44
-
45
- full_example_name = example_name_parts.slice(0, indent_level + 1).join(" ")
46
- @example_locations[full_example_name] = "#{filename}:#{line_number}: in `it'"
47
- end
48
- line_number += 1
49
- end
50
- end
51
- end
52
- end
53
-
54
- def load_suite_info
55
- started = Time.now
56
- while !eval_js('jsApiReporter && jsApiReporter.started') do
57
- raise "couldn't connect to Jasmine after 60 seconds" if (started + 60 < Time.now)
58
- sleep 0.1
59
- end
60
-
61
- @suites = eval_js("var result = jsApiReporter.suites(); if (window.Prototype && result && result.toJSON) { result.toJSON()} else { JSON.stringify(result) }")
62
- end
63
-
64
- def results_for(spec_id)
65
- @spec_results ||= load_results
66
- @spec_results[spec_id.to_s]
67
- end
68
-
69
- def load_results
70
- @spec_results = {}
71
- @spec_ids.each_slice(50) do |slice|
72
- @spec_results.merge!(eval_js("var result = jsApiReporter.resultsForSpecs(#{JSON.generate(slice)}); if (window.Prototype && result && result.toJSON) { result.toJSON()} else { JSON.stringify(result) }"))
73
- end
74
- @spec_results
75
- end
76
-
77
- def wait_for_suites_to_finish_running
78
- puts "Waiting for suite to finish in browser ..."
79
- while !eval_js('jsApiReporter.finished') do
80
- sleep 0.1
81
- end
82
- end
83
-
84
- def declare_suites
85
- me = self
86
- suites.each do |suite|
87
- declare_suite(self, suite)
88
- end
89
- end
90
-
91
- def declare_suite(parent, suite)
92
- me = self
93
- parent.describe suite["name"] do
94
- suite["children"].each do |suite_or_spec|
95
- type = suite_or_spec["type"]
96
- if type == "suite"
97
- me.declare_suite(self, suite_or_spec)
98
- elsif type == "spec"
99
- me.declare_spec(self, suite_or_spec)
100
- else
101
- raise "unknown type #{type} for #{suite_or_spec.inspect}"
102
- end
103
- end
104
- end
105
- end
106
-
107
- def declare_spec(parent, spec)
108
- me = self
109
- example_name = spec["name"]
110
- @spec_ids << spec["id"]
111
- backtrace = @example_locations[parent.description + " " + example_name]
112
- parent.it example_name, {}, backtrace do
113
- me.report_spec(spec["id"])
114
- end
115
- end
116
-
117
- def report_spec(spec_id)
118
- spec_results = results_for(spec_id)
119
-
120
- out = ""
121
- messages = spec_results['messages'].each do |message|
122
- case
123
- when message["type"] == "MessageResult"
124
- puts message["text"]
125
- puts "\n"
126
- else
127
- unless message["message"] =~ /^Passed.$/
128
- STDERR << message["message"]
129
- STDERR << "\n"
130
-
131
- out << message["message"]
132
- out << "\n"
133
- end
134
-
135
- if !message["passed"] && message["trace"]["stack"]
136
- stack_trace = message["trace"]["stack"].gsub(/<br \/>/, "\n").gsub(/<\/?b>/, " ")
137
- STDERR << stack_trace.gsub(/\(.*\)@http:\/\/localhost:[0-9]+\/specs\//, "/spec/")
138
- STDERR << "\n"
139
- end
140
- end
141
-
142
- end
143
- fail out unless spec_results['result'] == 'passed'
144
- puts out unless out.empty?
145
- end
146
-
147
- private
148
-
149
- def eval_js(js)
150
- @runner.eval_js(js)
151
- end
152
- end
153
- end
@@ -1,47 +0,0 @@
1
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2
- <html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
3
- <head>
4
- <meta content="text/html;charset=UTF-8" http-equiv="Content-Type"/>
5
- <title>Jasmine suite</title>
6
- <% css_files.each do |css_file| %>
7
- <link rel="stylesheet" href="<%= css_file %>" type="text/css" media="screen"/>
8
- <% end %>
9
-
10
- <% jasmine_files.each do |jasmine_file| %>
11
- <script src="<%= jasmine_file %>" type="text/javascript"></script>
12
- <% end %>
13
-
14
- <% spec_helpers.each do |spec_helper| %>
15
- <script src="<%= spec_helper %>" type="text/javascript"></script>
16
- <% end %>
17
-
18
- <script type="text/javascript">
19
- var jsApiReporter;
20
- (function() {
21
- var jasmineEnv = jasmine.getEnv();
22
-
23
- jsApiReporter = new jasmine.JsApiReporter();
24
- var trivialReporter = new jasmine.TrivialReporter();
25
-
26
- jasmineEnv.addReporter(jsApiReporter);
27
- jasmineEnv.addReporter(trivialReporter);
28
-
29
- jasmineEnv.specFilter = function(spec) {
30
- return trivialReporter.specFilter(spec);
31
- };
32
-
33
- window.onload = function() {
34
- jasmineEnv.execute();
35
- };
36
- })();
37
- </script>
38
-
39
- <% spec_files.each do |spec_file| %>
40
- <script src="<%= spec_file %>" type="text/javascript"></script>
41
- <% end %>
42
-
43
- </head>
44
- <body>
45
- <div id="jasmine_content"></div>
46
- </body>
47
- </html>