js_spec 0.0.1 → 0.1.0

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.
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