jasmine 0.10.2.4 → 0.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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>