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.
Files changed (41) hide show
  1. data/CHANGES +5 -1
  2. data/Rakefile +3 -3
  3. data/bin/js_spec +10 -0
  4. data/bin/js_spec_server +2 -1
  5. data/core/JSSpec.js +11 -5
  6. data/core/JSSpecExtensions.js +127 -8
  7. data/core/demo.html +8 -1
  8. data/lib/js_spec/rails_server.rb +22 -0
  9. data/lib/js_spec/{dir.rb → resources/dir.rb} +7 -5
  10. data/lib/js_spec/{file.rb → resources/file.rb} +9 -3
  11. data/lib/js_spec/resources/runners/firefox_runner.rb +90 -0
  12. data/lib/js_spec/resources/runners.rb +16 -0
  13. data/lib/js_spec/{spec_dir_runner.rb → resources/spec_dir_runner.rb} +5 -3
  14. data/lib/js_spec/{spec_file_runner.rb → resources/spec_file_runner.rb} +4 -2
  15. data/lib/js_spec/{spec_runner.rb → resources/spec_runner.rb} +3 -1
  16. data/lib/js_spec/resources/suite.rb +24 -0
  17. data/lib/js_spec/resources/suite_finish.rb +20 -0
  18. data/lib/js_spec/resources/web_root.rb +34 -0
  19. data/lib/js_spec/server.rb +16 -6
  20. data/lib/js_spec.rb +16 -7
  21. data/spec/{functional_spec_suite.rb → functional_suite.rb} +2 -2
  22. data/spec/spec_suite.rb +2 -2
  23. data/spec/unit/rails_server_spec.rb +44 -0
  24. data/spec/unit/{dir_spec.rb → resources/dir_spec.rb} +10 -8
  25. data/spec/unit/{file_spec.rb → resources/file_spec.rb} +12 -6
  26. data/spec/unit/resources/runner_spec.rb +24 -0
  27. data/spec/unit/resources/runners/firefox_runner_spec.rb +87 -0
  28. data/spec/unit/{spec_dir_runner_spec.rb → resources/spec_dir_runner_spec.rb} +5 -3
  29. data/spec/unit/resources/spec_file_runner_spec.rb +20 -0
  30. data/spec/unit/resources/suite_finish_spec.rb +42 -0
  31. data/spec/unit/resources/suite_spec.rb +44 -0
  32. data/spec/unit/resources/web_root_spec.rb +55 -0
  33. data/spec/unit/server_spec.rb +75 -14
  34. data/spec/unit/unit_spec_helper.rb +9 -8
  35. data/spec/{unit_spec_suite.rb → unit_suite.rb} +2 -2
  36. metadata +37 -28
  37. data/lib/js_spec/suite_result.rb +0 -11
  38. data/lib/js_spec/web_root.rb +0 -18
  39. data/spec/unit/spec_file_runner_spec.rb +0 -18
  40. data/spec/unit/suite_result_spec.rb +0 -31
  41. data/spec/unit/web_root_spec.rb +0 -39
data/CHANGES CHANGED
@@ -1,2 +1,6 @@
1
+ 0.1.0
2
+ - Added RailsServer
3
+ - Better namespacing
4
+
1
5
  0.0.1
2
- * Initial Release
6
+ - Initial Release
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.1"
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 = "JsSpec is a server for the JSSpec javascript framework."
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
- JsSpec::Server.run(spec_root_path, implementation_root_path)
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 runner</h1>',
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) {
@@ -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?" + Require.cache_buster;
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
- $.ajax({
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: '/results',
22
- data: {
23
- 'text': "---------------------------------------------------------------------------------------\n\n+++++++++++++++++++++++++++++++++++++"
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
- JSSpec.Logger.prototype.onRunnerEnd = JSSpec.Logger.prototype.onRunnerEndWithServerNotification;
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(4);
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
- class Dir < File
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 = ::File.expand_path("#{absolute_path}/#{name}")
26
- relative_child_path = ::File.expand_path("#{relative_path}/#{name}")
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
- JsSpec::File.new(absolute_file_path, relative_file_path)
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
- JsSpec::Dir.new(absolute_dir_path, relative_dir_path)
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
- class File
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.is_a?(File)
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
- class SpecDirRunner < SpecRunner
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,7 +1,8 @@
1
1
  module JsSpec
2
- class SpecFileRunner < SpecRunner
2
+ module Resources
3
+ class SpecFileRunner < SpecRunner
3
4
  attr_reader :file
4
-
5
+
5
6
  def initialize(file)
6
7
  @file = file
7
8
  end
@@ -10,4 +11,5 @@ module JsSpec
10
11
  [file]
11
12
  end
12
13
  end
14
+ end
13
15
  end
@@ -1,5 +1,6 @@
1
1
  module JsSpec
2
- class SpecRunner
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
@@ -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
- SuiteResult.new
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