js_spec 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +5 -1
- data/Rakefile +3 -3
- data/bin/js_spec +10 -0
- data/bin/js_spec_server +2 -1
- data/core/JSSpec.js +11 -5
- data/core/JSSpecExtensions.js +127 -8
- data/core/demo.html +8 -1
- data/lib/js_spec/rails_server.rb +22 -0
- data/lib/js_spec/{dir.rb → resources/dir.rb} +7 -5
- data/lib/js_spec/{file.rb → resources/file.rb} +9 -3
- data/lib/js_spec/resources/runners/firefox_runner.rb +90 -0
- data/lib/js_spec/resources/runners.rb +16 -0
- data/lib/js_spec/{spec_dir_runner.rb → resources/spec_dir_runner.rb} +5 -3
- data/lib/js_spec/{spec_file_runner.rb → resources/spec_file_runner.rb} +4 -2
- data/lib/js_spec/{spec_runner.rb → resources/spec_runner.rb} +3 -1
- data/lib/js_spec/resources/suite.rb +24 -0
- data/lib/js_spec/resources/suite_finish.rb +20 -0
- data/lib/js_spec/resources/web_root.rb +34 -0
- data/lib/js_spec/server.rb +16 -6
- data/lib/js_spec.rb +16 -7
- data/spec/{functional_spec_suite.rb → functional_suite.rb} +2 -2
- data/spec/spec_suite.rb +2 -2
- data/spec/unit/rails_server_spec.rb +44 -0
- data/spec/unit/{dir_spec.rb → resources/dir_spec.rb} +10 -8
- data/spec/unit/{file_spec.rb → resources/file_spec.rb} +12 -6
- data/spec/unit/resources/runner_spec.rb +24 -0
- data/spec/unit/resources/runners/firefox_runner_spec.rb +87 -0
- data/spec/unit/{spec_dir_runner_spec.rb → resources/spec_dir_runner_spec.rb} +5 -3
- data/spec/unit/resources/spec_file_runner_spec.rb +20 -0
- data/spec/unit/resources/suite_finish_spec.rb +42 -0
- data/spec/unit/resources/suite_spec.rb +44 -0
- data/spec/unit/resources/web_root_spec.rb +55 -0
- data/spec/unit/server_spec.rb +75 -14
- data/spec/unit/unit_spec_helper.rb +9 -8
- data/spec/{unit_spec_suite.rb → unit_suite.rb} +2 -2
- metadata +37 -28
- data/lib/js_spec/suite_result.rb +0 -11
- data/lib/js_spec/web_root.rb +0 -18
- data/spec/unit/spec_file_runner_spec.rb +0 -18
- data/spec/unit/suite_result_spec.rb +0 -31
- data/spec/unit/web_root_spec.rb +0 -39
data/CHANGES
CHANGED
data/Rakefile
CHANGED
@@ -26,7 +26,7 @@ def run_suite
|
|
26
26
|
end
|
27
27
|
|
28
28
|
PKG_NAME = "js_spec"
|
29
|
-
PKG_VERSION = "0.0
|
29
|
+
PKG_VERSION = "0.1.0"
|
30
30
|
PKG_FILES = FileList[
|
31
31
|
'[A-Z]*',
|
32
32
|
'*.rb',
|
@@ -39,7 +39,7 @@ PKG_FILES = FileList[
|
|
39
39
|
spec = Gem::Specification.new do |s|
|
40
40
|
s.name = PKG_NAME
|
41
41
|
s.version = PKG_VERSION
|
42
|
-
s.summary = "
|
42
|
+
s.summary = "The JSSpec client library (http://code.google.com/p/jsspec/) plus a convenient ruby server."
|
43
43
|
s.test_files = "spec/spec_suite.rb"
|
44
44
|
s.description = s.summary
|
45
45
|
|
@@ -69,4 +69,4 @@ def tag_release
|
|
69
69
|
dashed_version = PKG_VERSION.gsub('.', '-')
|
70
70
|
svn_user = "#{ENV["SVN_USER"]}@" || ""
|
71
71
|
`svn cp svn+ssh://#{svn_user}rubyforge.org/var/svn/pivotalrb/js_spec/trunk svn+ssh://#{svn_user}rubyforge.org/var/svn/pivotalrb/js_spec/tags/REL-#{dashed_version} -m 'Version #{PKG_VERSION}'`
|
72
|
-
end
|
72
|
+
end
|
data/bin/js_spec
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
dir = File.dirname(__FILE__)
|
4
|
+
$LOAD_PATH.unshift("#{dir}/../lib")
|
5
|
+
require "js_spec"
|
6
|
+
require "net/http"
|
7
|
+
|
8
|
+
response = Net::HTTP.start(JsSpec::Server::DEFAULT_HOST, JsSpec::Server::DEFAULT_PORT) do |http|
|
9
|
+
http.post('/runners/firefox', {})
|
10
|
+
end
|
data/bin/js_spec_server
CHANGED
@@ -6,4 +6,5 @@ require "js_spec"
|
|
6
6
|
|
7
7
|
spec_root_path = ARGV[0] || raise("You need to pass in a spec root")
|
8
8
|
implementation_root_path = ARGV[1] || raise("You need to pass in an implementation root")
|
9
|
-
|
9
|
+
public_path = ARGV[2] || raise("You need to pass in a public path")
|
10
|
+
JsSpec::Server.run(spec_root_path, implementation_root_path, public_path)
|
data/core/JSSpec.js
CHANGED
@@ -455,8 +455,9 @@ JSSpec.Logger.prototype.onRunnerStart = function() {
|
|
455
455
|
var title = document.createElement("DIV");
|
456
456
|
title.id = "title";
|
457
457
|
title.innerHTML = [
|
458
|
-
'<h1>JSSpec
|
458
|
+
'<h1>JSSpec</h1>',
|
459
459
|
'<ul>',
|
460
|
+
JSSpec.options.rerun ? '<li>[<a href="?" title="rerun all specs">X</a>] ' + JSSpec.util.escapeTags(decodeURIComponent(JSSpec.options.rerun)) + '</li>' : '',
|
460
461
|
' <li><span id="total_examples">' + JSSpec.runner.totalExamples + '</span> examples</li>',
|
461
462
|
' <li><span id="total_failures">0</span> failures</li>',
|
462
463
|
' <li><span id="total_errors">0</span> errors</li>',
|
@@ -645,15 +646,20 @@ JSSpec.IncludeMatcher.prototype.makeExplain = function() {
|
|
645
646
|
};
|
646
647
|
|
647
648
|
JSSpec.IncludeMatcher.prototype.makeExplainForNotArray = function() {
|
649
|
+
if(this.condition) {
|
650
|
+
this.match = !!this.actual[this.expected];
|
651
|
+
} else {
|
652
|
+
this.match = !this.actual[this.expected];
|
653
|
+
}
|
654
|
+
|
648
655
|
var sb = [];
|
649
656
|
sb.push('<p>actual value:</p>');
|
650
|
-
sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.actual) + '</p>');
|
657
|
+
sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.actual, false, this.expected) + '</p>');
|
651
658
|
sb.push('<p>should ' + (this.condition ? '' : 'not') + ' include:</p>');
|
652
659
|
sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.expected) + '</p>');
|
653
|
-
sb.push('<p>but since it\s not an array, include or not doesn\'t make any sense.</p>');
|
654
660
|
return sb.join("");
|
655
|
-
}
|
656
|
-
|
661
|
+
};
|
662
|
+
|
657
663
|
JSSpec.IncludeMatcher.prototype.makeExplainForArray = function() {
|
658
664
|
var matches;
|
659
665
|
if(this.condition) {
|
data/core/JSSpecExtensions.js
CHANGED
@@ -1,27 +1,146 @@
|
|
1
1
|
var Require = {
|
2
2
|
require: function(path_from_javascripts) {
|
3
3
|
if(!Require.required_paths[path_from_javascripts]) {
|
4
|
-
var full_path = path_from_javascripts + ".js
|
4
|
+
var full_path = path_from_javascripts + ".js";
|
5
|
+
if (Require.use_cache_buster) {
|
6
|
+
full_path += '?' + Require.cache_buster;
|
7
|
+
}
|
5
8
|
document.write("<script src='" + full_path + "' type='text/javascript'></script>");
|
6
9
|
Require.required_paths[path_from_javascripts] = true;
|
7
10
|
}
|
8
11
|
},
|
9
12
|
|
10
13
|
required_paths: {},
|
14
|
+
use_cache_buster: true, // TODO: NS/CTI - make this configurable from the UI.
|
11
15
|
cache_buster: parseInt(new Date().getTime()/(1*1000))
|
12
16
|
}
|
13
|
-
|
14
17
|
var require = Require.require;
|
15
18
|
|
19
|
+
var URL_REGEX = /^((\w+):\/\/)(([^:]+):?([^@]+)?@)?([^\/\?:]*):?(\d+)?(\/?[^\?#]+)?\??([^#]+)?#?(.+)?/;
|
20
|
+
function parse_url(url) {
|
21
|
+
var fields = ['url', null, 'protocol', null, 'username', 'password', 'host', 'port', 'pathname', 'search', 'hash'];
|
22
|
+
var result = URL_REGEX.exec(url);
|
23
|
+
if (!result) {
|
24
|
+
throw new SeleniumError("Invalid URL: " + url);
|
25
|
+
}
|
26
|
+
var loc = new Object();
|
27
|
+
for (var i = 0; i < fields.length; i++) {
|
28
|
+
var field = fields[i];
|
29
|
+
if (field == null) {
|
30
|
+
continue;
|
31
|
+
}
|
32
|
+
loc[field] = result[i];
|
33
|
+
}
|
34
|
+
return loc;
|
35
|
+
}
|
36
|
+
|
16
37
|
JSSpec.Logger.prototype.onRunnerEndWithoutServerNotification = JSSpec.Logger.prototype.onRunnerEnd;
|
17
38
|
JSSpec.Logger.prototype.onRunnerEndWithServerNotification = function() {
|
18
39
|
this.onRunnerEndWithoutServerNotification();
|
19
|
-
|
40
|
+
var data = {
|
41
|
+
'text': this.get_error_message_text()
|
42
|
+
};
|
43
|
+
|
44
|
+
current_location_params = parse_url(window.location.href);
|
45
|
+
if(current_location_params.guid) {
|
46
|
+
data.guid = current_location_params.guid;
|
47
|
+
}
|
48
|
+
jQuery.realAjax({
|
20
49
|
type: 'POST',
|
21
|
-
url: '/
|
22
|
-
data:
|
23
|
-
|
50
|
+
url: '/suites/1/finish',
|
51
|
+
data: data
|
52
|
+
});
|
53
|
+
}
|
54
|
+
JSSpec.Logger.prototype.onRunnerEnd = JSSpec.Logger.prototype.onRunnerEndWithServerNotification;
|
55
|
+
|
56
|
+
JSSpec.Logger.prototype.get_error_message_text = function() {
|
57
|
+
var error_messages = [];
|
58
|
+
for(var i=0; i < JSSpec.specs.length; i++) {
|
59
|
+
var spec = JSSpec.specs[i];
|
60
|
+
if(spec.hasException()) {
|
61
|
+
for(var j=0; j < spec.getExamples().length; j++) {
|
62
|
+
var example = spec.getExamples()[j];
|
63
|
+
var error_message = spec.context + " " + example.name;
|
64
|
+
if (example.exception) {
|
65
|
+
error_message += "\n" + example.exception.message + "\n" + example.exception.fileName + ":" + example.exception.lineNumber;
|
66
|
+
}
|
67
|
+
error_messages.push(error_message);
|
68
|
+
}
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
var full_error_text = error_messages.join("\n");
|
73
|
+
full_error_text = full_error_text.replace(/<\/p>/g, "\n");
|
74
|
+
full_error_text = full_error_text.replace(/<(.|\n)*?>/g, "");
|
75
|
+
return full_error_text;
|
76
|
+
}
|
77
|
+
|
78
|
+
// Custom Matchers
|
79
|
+
|
80
|
+
JSSpec.DSL.Subject.prototype.should_be_disabled = function() {
|
81
|
+
if (!this.target.disabled) {
|
82
|
+
JSSpec._assertionFailure = {message: "Element " + JSSpec.util.inspect(this.target) + " should have been disabled"};
|
83
|
+
throw JSSpec._assertionFailure;
|
84
|
+
}
|
85
|
+
};
|
86
|
+
|
87
|
+
JSSpec.DSL.Subject.prototype.should_be_enabled = function() {
|
88
|
+
if (this.target.disabled) {
|
89
|
+
JSSpec._assertionFailure = {message: "Element " + JSSpec.util.inspect(this.target) + " should have been enabled"};
|
90
|
+
throw JSSpec._assertionFailure;
|
91
|
+
}
|
92
|
+
};
|
93
|
+
|
94
|
+
JSSpec.DSL.Subject.prototype.should_raise = function(expected) {
|
95
|
+
if("function" != typeof(this.target)) {
|
96
|
+
JSSpec._assertionFailure = {message: 'should_raise expects value_of(target) to have a target that is a function'};
|
97
|
+
throw JSSpec._assertionFailure;
|
98
|
+
}
|
99
|
+
|
100
|
+
var raised = false;
|
101
|
+
try {
|
102
|
+
this.target();
|
103
|
+
}
|
104
|
+
catch(e) {
|
105
|
+
if (expected == null || expected == e) {
|
106
|
+
raised = true;
|
107
|
+
}
|
108
|
+
}
|
109
|
+
if(!raised) {
|
110
|
+
var message = "should have raised an error but didn't";
|
111
|
+
if(expected) {
|
112
|
+
message = "should have raised '" + expected + "' but didn't";
|
24
113
|
}
|
25
|
-
|
114
|
+
JSSpec._assertionFailure = {message: message};
|
115
|
+
throw JSSpec._assertionFailure;
|
116
|
+
}
|
26
117
|
}
|
27
|
-
|
118
|
+
|
119
|
+
JSSpec.DSL.Subject.prototype.should_not_raise = function(expected) {
|
120
|
+
if("function" != typeof(this.target)) {
|
121
|
+
JSSpec._assertionFailure = {message: 'should_not_raise expects value_of(target) to have a target that is a function'};
|
122
|
+
throw JSSpec._assertionFailure;
|
123
|
+
}
|
124
|
+
|
125
|
+
var raised = false;
|
126
|
+
try {
|
127
|
+
this.target();
|
128
|
+
}
|
129
|
+
catch(e) {
|
130
|
+
if (expected == null || expected == e) {
|
131
|
+
raised = true;
|
132
|
+
}
|
133
|
+
}
|
134
|
+
if(raised) {
|
135
|
+
var message = "should not have raised an error but did";
|
136
|
+
if(expected) {
|
137
|
+
message = "should not have raised '" + expected + "' but did";
|
138
|
+
}
|
139
|
+
JSSpec._assertionFailure = {message: message};
|
140
|
+
throw JSSpec._assertionFailure;
|
141
|
+
}
|
142
|
+
}
|
143
|
+
|
144
|
+
|
145
|
+
|
146
|
+
|
data/core/demo.html
CHANGED
@@ -44,7 +44,10 @@ describe('"Should include"s', {
|
|
44
44
|
value_of(new Date()).should_include(4);
|
45
45
|
},
|
46
46
|
'Should not include / Non-array object': function() {
|
47
|
-
value_of(new Date()).should_not_include(
|
47
|
+
value_of(new Date()).should_not_include('getMonth');
|
48
|
+
},
|
49
|
+
'Should include 2': function() {
|
50
|
+
value_of({a:1, b:2}).should_not_include('a');
|
48
51
|
}
|
49
52
|
})
|
50
53
|
|
@@ -69,6 +72,10 @@ describe('"Should have"s', {
|
|
69
72
|
},
|
70
73
|
'At most': function() {
|
71
74
|
value_of([1,2,3]).should_have_at_most(2, "items");
|
75
|
+
},
|
76
|
+
'Member': function() {
|
77
|
+
value_of({x: 0}).should_have_member('x'); // true
|
78
|
+
value_of({x: 0}).should_have_member('y'); // false
|
72
79
|
}
|
73
80
|
})
|
74
81
|
describe('"Should be empty"s', {
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module JsSpec
|
2
|
+
class RailsServer < Server
|
3
|
+
class << self
|
4
|
+
def run(rails_root, server_options = {})
|
5
|
+
server_options[:Host] ||= Server::DEFAULT_HOST
|
6
|
+
server_options[:Port] ||= Server::DEFAULT_PORT
|
7
|
+
Server.instance = new(rails_root, server_options[:Host], server_options[:Port])
|
8
|
+
Rack::Handler::Mongrel.run(Server.instance, server_options)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(rails_root, host=DEFAULT_HOST, port=DEFAULT_PORT)
|
13
|
+
super(
|
14
|
+
"#{rails_root}/spec/javascripts",
|
15
|
+
"#{rails_root}/public/javascripts",
|
16
|
+
"#{rails_root}/public",
|
17
|
+
host,
|
18
|
+
port
|
19
|
+
)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module JsSpec
|
2
|
-
|
2
|
+
module Resources
|
3
|
+
class Dir < File
|
3
4
|
def locate(name)
|
4
5
|
if file = file(name)
|
5
6
|
file
|
@@ -22,8 +23,8 @@ module JsSpec
|
|
22
23
|
|
23
24
|
protected
|
24
25
|
def determine_child_paths(name)
|
25
|
-
absolute_child_path =
|
26
|
-
relative_child_path =
|
26
|
+
absolute_child_path = "#{absolute_path}/#{name}"
|
27
|
+
relative_child_path = "#{relative_path}/#{name}"
|
27
28
|
[absolute_child_path, relative_child_path]
|
28
29
|
end
|
29
30
|
|
@@ -40,7 +41,7 @@ module JsSpec
|
|
40
41
|
def file(name)
|
41
42
|
absolute_file_path, relative_file_path = determine_child_paths(name)
|
42
43
|
if ::File.exists?(absolute_file_path) && !::File.directory?(absolute_file_path)
|
43
|
-
|
44
|
+
Resources::File.new(absolute_file_path, relative_file_path)
|
44
45
|
else
|
45
46
|
nil
|
46
47
|
end
|
@@ -49,10 +50,11 @@ module JsSpec
|
|
49
50
|
def subdir(name)
|
50
51
|
absolute_dir_path, relative_dir_path = determine_child_paths(name)
|
51
52
|
if ::File.directory?(absolute_dir_path)
|
52
|
-
|
53
|
+
Resources::Dir.new(absolute_dir_path, relative_dir_path)
|
53
54
|
else
|
54
55
|
nil
|
55
56
|
end
|
56
57
|
end
|
57
58
|
end
|
59
|
+
end
|
58
60
|
end
|
@@ -1,12 +1,17 @@
|
|
1
1
|
module JsSpec
|
2
|
-
|
2
|
+
module Resources
|
3
|
+
class File
|
3
4
|
MIME_TYPES = {
|
4
5
|
'.js' => 'text/javascript',
|
5
6
|
'.css' => 'text/css',
|
7
|
+
'.png' => 'image/png',
|
8
|
+
'.jpg' => 'image/jpeg',
|
9
|
+
'.jpeg' => 'image/jpeg',
|
10
|
+
'.gif' => 'image/gif',
|
6
11
|
}
|
7
12
|
|
8
13
|
attr_reader :absolute_path, :relative_path
|
9
|
-
|
14
|
+
|
10
15
|
def initialize(absolute_path, relative_path)
|
11
16
|
@absolute_path = absolute_path
|
12
17
|
@relative_path = relative_path
|
@@ -19,8 +24,9 @@ module JsSpec
|
|
19
24
|
end
|
20
25
|
|
21
26
|
def ==(other)
|
22
|
-
return false unless other.
|
27
|
+
return false unless other.class == self.class
|
23
28
|
absolute_path == other.absolute_path && relative_path == other.relative_path
|
24
29
|
end
|
25
30
|
end
|
31
|
+
end
|
26
32
|
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module JsSpec
|
2
|
+
module Resources
|
3
|
+
class Runners
|
4
|
+
class FirefoxRunner
|
5
|
+
class << self
|
6
|
+
def resume(guid, text)
|
7
|
+
responses[guid] = text
|
8
|
+
threads[guid].kill
|
9
|
+
end
|
10
|
+
|
11
|
+
def threads
|
12
|
+
@threads ||= {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def response_value(guid)
|
16
|
+
responses[guid]
|
17
|
+
ensure
|
18
|
+
responses.delete guid
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
def responses
|
23
|
+
@responses ||= {}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
include FileUtils
|
28
|
+
attr_reader :profile_dir
|
29
|
+
|
30
|
+
def initialize
|
31
|
+
profile_base = "#{::Dir.tmpdir}/js_spec/firefox"
|
32
|
+
mkdir_p profile_base
|
33
|
+
@profile_dir = "#{profile_base}/#{Time.now.to_i}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def post
|
37
|
+
copy_profile_files
|
38
|
+
create_profile
|
39
|
+
wait_for_full_profile_to_be_created
|
40
|
+
|
41
|
+
guid = UUID.new
|
42
|
+
start_browser guid
|
43
|
+
thread = Thread.start {sleep}
|
44
|
+
self.class.threads[guid] = thread
|
45
|
+
thread.join
|
46
|
+
|
47
|
+
self.class.response_value guid
|
48
|
+
end
|
49
|
+
|
50
|
+
protected
|
51
|
+
def copy_profile_files
|
52
|
+
dir = ::File.dirname(__FILE__)
|
53
|
+
firefox_profile_path = ::File.expand_path("#{dir}/../../../../resources/firefox")
|
54
|
+
system("cp -R #{firefox_profile_path} #{profile_dir}") || raise("Copying Firefox profile failed")
|
55
|
+
end
|
56
|
+
|
57
|
+
def create_profile
|
58
|
+
system("firefox -profile #{profile_dir} -chrome chrome://killff/content/kill.html") || raise("Initializing profile failed")
|
59
|
+
end
|
60
|
+
|
61
|
+
def wait_for_full_profile_to_be_created
|
62
|
+
Timeout.timeout(5) do
|
63
|
+
wait_for_file_lock_to_go_away
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def start_browser(guid)
|
68
|
+
Thread.start do
|
69
|
+
system("firefox -profile #{profile_dir} http://localhost:8080/specs?guid=#{guid}") || raise("Starting Firefox failed")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def wait_for_file_lock_to_go_away
|
74
|
+
loop do
|
75
|
+
return if lock_is_gone? && lock_remains_gone?
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def lock_is_gone?
|
80
|
+
!::File.exists?("#{profile_dir}/parent.lock")
|
81
|
+
end
|
82
|
+
|
83
|
+
def lock_remains_gone?
|
84
|
+
sleep 0.5
|
85
|
+
lock_is_gone?
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
dir = File.dirname(__FILE__)
|
2
|
+
require File.expand_path("#{dir}/runners/firefox_runner")
|
3
|
+
|
4
|
+
module JsSpec
|
5
|
+
module Resources
|
6
|
+
class Runners
|
7
|
+
def locate(name)
|
8
|
+
if name == 'firefox'
|
9
|
+
FirefoxRunner.new
|
10
|
+
else
|
11
|
+
raise "Invalid path #{name}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -1,13 +1,15 @@
|
|
1
1
|
module JsSpec
|
2
|
-
|
2
|
+
module Resources
|
3
|
+
class SpecDirRunner < SpecRunner
|
3
4
|
attr_reader :dir
|
4
|
-
|
5
|
+
|
5
6
|
def initialize(dir)
|
6
7
|
@dir = dir
|
7
8
|
end
|
8
|
-
|
9
|
+
|
9
10
|
def spec_files
|
10
11
|
dir.glob("/**/*_spec.js")
|
11
12
|
end
|
12
13
|
end
|
14
|
+
end
|
13
15
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module JsSpec
|
2
|
-
|
2
|
+
module Resources
|
3
|
+
class SpecRunner
|
3
4
|
def get
|
4
5
|
html = <<-HTML
|
5
6
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
@@ -24,4 +25,5 @@ module JsSpec
|
|
24
25
|
html.gsub(/^ /, "")
|
25
26
|
end
|
26
27
|
end
|
28
|
+
end
|
27
29
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module JsSpec
|
2
|
+
module Resources
|
3
|
+
class Suite
|
4
|
+
class << self
|
5
|
+
def locate(id)
|
6
|
+
new id
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :id
|
11
|
+
def initialize(id)
|
12
|
+
@id = id
|
13
|
+
end
|
14
|
+
|
15
|
+
def locate(name)
|
16
|
+
if name == 'finish'
|
17
|
+
SuiteFinish.new self
|
18
|
+
else
|
19
|
+
raise ArgumentError, "Invalid path: #{name}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module JsSpec
|
2
|
+
module Resources
|
3
|
+
class SuiteFinish
|
4
|
+
attr_reader :suite
|
5
|
+
def initialize(suite)
|
6
|
+
@suite = suite
|
7
|
+
end
|
8
|
+
|
9
|
+
def post
|
10
|
+
guid = Server.request['guid']
|
11
|
+
if guid
|
12
|
+
Runners::FirefoxRunner.resume(guid, Server.request['text'])
|
13
|
+
else
|
14
|
+
STDOUT.puts Server.request['text']
|
15
|
+
end
|
16
|
+
""
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module JsSpec
|
2
|
+
module Resources
|
3
|
+
class WebRoot
|
4
|
+
attr_reader :public_path
|
5
|
+
def initialize(public_path)
|
6
|
+
@public_path = ::File.expand_path(public_path)
|
7
|
+
end
|
8
|
+
|
9
|
+
def locate(name)
|
10
|
+
case name
|
11
|
+
when 'specs'
|
12
|
+
Resources::Dir.new(JsSpec::Server.spec_root_path, "/specs")
|
13
|
+
when 'core'
|
14
|
+
Resources::Dir.new(JsSpec::Server.core_path, "/core")
|
15
|
+
when 'implementations'
|
16
|
+
Resources::Dir.new(JsSpec::Server.implementation_root_path, "/implementations")
|
17
|
+
when 'suites'
|
18
|
+
Resources::Suite
|
19
|
+
when 'runners'
|
20
|
+
Resources::Runners.new
|
21
|
+
else
|
22
|
+
potential_file_in_public_path = "#{public_path}/#{name}"
|
23
|
+
if ::File.directory?(potential_file_in_public_path)
|
24
|
+
Resources::Dir.new(potential_file_in_public_path, "/#{name}")
|
25
|
+
elsif ::File.exists?(potential_file_in_public_path)
|
26
|
+
Resources::File.new(potential_file_in_public_path, "/#{name}")
|
27
|
+
else
|
28
|
+
raise "Invalid path: #{name}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/js_spec/server.rb
CHANGED
@@ -6,27 +6,33 @@ module JsSpec
|
|
6
6
|
class << self
|
7
7
|
attr_accessor :instance
|
8
8
|
|
9
|
-
def run(spec_root_path, implementation_root_path, server_options = {})
|
9
|
+
def run(spec_root_path, implementation_root_path, public_path, server_options = {})
|
10
10
|
server_options[:Host] ||= DEFAULT_HOST
|
11
11
|
server_options[:Port] ||= DEFAULT_PORT
|
12
|
-
@instance = new(spec_root_path, implementation_root_path, server_options[:Host], server_options[:Port])
|
12
|
+
@instance = new(spec_root_path, implementation_root_path, public_path, server_options[:Host], server_options[:Port])
|
13
13
|
Rack::Handler::Mongrel.run(instance, server_options)
|
14
14
|
end
|
15
15
|
|
16
|
+
def default_url
|
17
|
+
"http://#{DEFAULT_HOST}:#{DEFAULT_PORT}"
|
18
|
+
end
|
19
|
+
|
16
20
|
def spec_root_path; instance.spec_root_path; end
|
17
21
|
def implementation_root_path; instance.implementation_root_path; end
|
22
|
+
def public_path; instance.public_path; end
|
18
23
|
def core_path; instance.core_path; end
|
19
24
|
def request; instance.request; end
|
20
25
|
def response; instance.response; end
|
21
26
|
end
|
22
27
|
|
23
|
-
attr_reader :host, :port, :spec_root_path, :implementation_root_path, :core_path
|
28
|
+
attr_reader :host, :port, :spec_root_path, :implementation_root_path, :core_path, :public_path
|
24
29
|
|
25
|
-
def initialize(spec_root_path, implementation_root_path, host=DEFAULT_HOST, port=DEFAULT_PORT)
|
30
|
+
def initialize(spec_root_path, implementation_root_path, public_path, host=DEFAULT_HOST, port=DEFAULT_PORT)
|
26
31
|
dir = ::File.dirname(__FILE__)
|
27
32
|
@core_path = ::File.expand_path("#{dir}/../../core")
|
28
33
|
@spec_root_path = ::File.expand_path(spec_root_path)
|
29
34
|
@implementation_root_path = ::File.expand_path(implementation_root_path)
|
35
|
+
@public_path = ::File.expand_path(public_path)
|
30
36
|
@host = host
|
31
37
|
@port = port
|
32
38
|
end
|
@@ -44,7 +50,7 @@ module JsSpec
|
|
44
50
|
|
45
51
|
def run(path)
|
46
52
|
system(%{firefox "http://#{host}:#{port}/#{path}"})
|
47
|
-
|
53
|
+
Suite.new
|
48
54
|
end
|
49
55
|
|
50
56
|
def request
|
@@ -69,9 +75,13 @@ module JsSpec
|
|
69
75
|
end
|
70
76
|
|
71
77
|
def get_resource
|
72
|
-
path_parts.inject(WebRoot.new) do |resource, child_resource_name|
|
78
|
+
path_parts.inject(Resources::WebRoot.new(public_path)) do |resource, child_resource_name|
|
73
79
|
resource.locate(child_resource_name)
|
74
80
|
end
|
81
|
+
rescue Exception => e
|
82
|
+
detailed_exception = Exception.new("Error handling path #{request.path_info}\n#{e.message}")
|
83
|
+
detailed_exception.set_backtrace(e.backtrace)
|
84
|
+
raise detailed_exception
|
75
85
|
end
|
76
86
|
end
|
77
87
|
end
|