pivotal-screw-unit 0.4.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 (127) hide show
  1. data/CHANGES +21 -0
  2. data/README.markdown +354 -0
  3. data/Rakefile +39 -0
  4. data/VERSION.yml +4 -0
  5. data/bin/screw_unit +6 -0
  6. data/bin/screw_unit_server +10 -0
  7. data/core/CHANGES +10 -0
  8. data/core/EXAMPLE.html +68 -0
  9. data/core/LICENSE +22 -0
  10. data/core/README.markdown +307 -0
  11. data/core/example/models/cat.js +5 -0
  12. data/core/example/models/man.js +17 -0
  13. data/core/example/spec/matchers/have.js +8 -0
  14. data/core/example/spec/models/cat_spec.js +31 -0
  15. data/core/example/spec/models/man_spec.js +34 -0
  16. data/core/example/spec/spec_helper.js +5 -0
  17. data/core/example/spec/suite.html +25 -0
  18. data/core/lib/jquery-1.3.2.js +4376 -0
  19. data/core/lib/jquery.fn.js +30 -0
  20. data/core/lib/jquery.print.js +109 -0
  21. data/core/lib/screw.behaviors.js +93 -0
  22. data/core/lib/screw.builder.js +94 -0
  23. data/core/lib/screw.css +90 -0
  24. data/core/lib/screw.events.js +45 -0
  25. data/core/lib/screw.matchers.js +244 -0
  26. data/core/spec/behaviors_spec.js +188 -0
  27. data/core/spec/matchers_spec.js +372 -0
  28. data/core/spec/print_spec.js +158 -0
  29. data/core/spec/spec_helper.js +0 -0
  30. data/core/spec/suite.html +19 -0
  31. data/core/spec/with_screw_context_spec.js +9 -0
  32. data/init.rb +0 -0
  33. data/lib/screw_unit/representations/spec.html.rb +117 -0
  34. data/lib/screw_unit/representations.rb +2 -0
  35. data/lib/screw_unit.rb +19 -0
  36. data/spec/functional/functional_spec.rb +25 -0
  37. data/spec/functional/functional_spec_helper.rb +34 -0
  38. data/spec/functional/functional_spec_server_starter.rb +63 -0
  39. data/spec/functional_suite.rb +10 -0
  40. data/spec/spec_suite.rb +3 -0
  41. data/spec/unit/js_test_core/specs/spec_dir_spec.rb +39 -0
  42. data/spec/unit/js_test_core/specs/spec_file_spec.rb +35 -0
  43. data/spec/unit/unit_spec_helper.rb +119 -0
  44. data/spec/unit_suite.rb +10 -0
  45. data/vendor/js-test-core/CHANGES +25 -0
  46. data/vendor/js-test-core/README +12 -0
  47. data/vendor/js-test-core/Rakefile +73 -0
  48. data/vendor/js-test-core/lib/js_test_core/client.rb +129 -0
  49. data/vendor/js-test-core/lib/js_test_core/extensions/time.rb +6 -0
  50. data/vendor/js-test-core/lib/js_test_core/extensions.rb +3 -0
  51. data/vendor/js-test-core/lib/js_test_core/rack/commonlogger.rb +5 -0
  52. data/vendor/js-test-core/lib/js_test_core/rack.rb +2 -0
  53. data/vendor/js-test-core/lib/js_test_core/rails_server.rb +22 -0
  54. data/vendor/js-test-core/lib/js_test_core/representations/spec.html.rb +23 -0
  55. data/vendor/js-test-core/lib/js_test_core/representations.rb +9 -0
  56. data/vendor/js-test-core/lib/js_test_core/resources/dir.rb +67 -0
  57. data/vendor/js-test-core/lib/js_test_core/resources/file.rb +42 -0
  58. data/vendor/js-test-core/lib/js_test_core/resources/runner.rb +107 -0
  59. data/vendor/js-test-core/lib/js_test_core/resources/session.rb +44 -0
  60. data/vendor/js-test-core/lib/js_test_core/resources/session_finish.rb +17 -0
  61. data/vendor/js-test-core/lib/js_test_core/resources/specs/spec.rb +32 -0
  62. data/vendor/js-test-core/lib/js_test_core/resources/specs/spec_dir.rb +50 -0
  63. data/vendor/js-test-core/lib/js_test_core/resources/specs/spec_file.rb +21 -0
  64. data/vendor/js-test-core/lib/js_test_core/resources/web_root.rb +66 -0
  65. data/vendor/js-test-core/lib/js_test_core/resources.rb +17 -0
  66. data/vendor/js-test-core/lib/js_test_core/selenium/client/driver.rb +7 -0
  67. data/vendor/js-test-core/lib/js_test_core/selenium.rb +2 -0
  68. data/vendor/js-test-core/lib/js_test_core/selenium_server_configuration.rb +48 -0
  69. data/vendor/js-test-core/lib/js_test_core/server.rb +50 -0
  70. data/vendor/js-test-core/lib/js_test_core/thin/backends/js_test_core_server.rb +9 -0
  71. data/vendor/js-test-core/lib/js_test_core/thin/js_test_core_connection.rb +8 -0
  72. data/vendor/js-test-core/lib/js_test_core/thin.rb +3 -0
  73. data/vendor/js-test-core/lib/js_test_core.rb +41 -0
  74. data/vendor/js-test-core/spec/example_core/JsTestCore.css +0 -0
  75. data/vendor/js-test-core/spec/example_core/JsTestCore.js +0 -0
  76. data/vendor/js-test-core/spec/example_public/favicon.ico +0 -0
  77. data/vendor/js-test-core/spec/example_public/javascripts/foo.js +3 -0
  78. data/vendor/js-test-core/spec/example_public/javascripts/large_file.js +59 -0
  79. data/vendor/js-test-core/spec/example_public/javascripts/subdir/bar.js +1 -0
  80. data/vendor/js-test-core/spec/example_public/robots.txt +0 -0
  81. data/vendor/js-test-core/spec/example_public/stylesheets/example.css +3 -0
  82. data/vendor/js-test-core/spec/example_specs/custom_dir_and_suite/passing_spec.js +6 -0
  83. data/vendor/js-test-core/spec/example_specs/custom_dir_and_suite.html +9 -0
  84. data/vendor/js-test-core/spec/example_specs/custom_suite.html +8 -0
  85. data/vendor/js-test-core/spec/example_specs/failing_spec.js +5 -0
  86. data/vendor/js-test-core/spec/example_specs/foo/failing_spec.js +6 -0
  87. data/vendor/js-test-core/spec/example_specs/foo/passing_spec.js +6 -0
  88. data/vendor/js-test-core/spec/spec_suite.rb +2 -0
  89. data/vendor/js-test-core/spec/specs/failing_spec.js +0 -0
  90. data/vendor/js-test-core/spec/unit/js_test_core/client_spec.rb +192 -0
  91. data/vendor/js-test-core/spec/unit/js_test_core/rails_server_spec.rb +45 -0
  92. data/vendor/js-test-core/spec/unit/js_test_core/resources/dir_spec.rb +53 -0
  93. data/vendor/js-test-core/spec/unit/js_test_core/resources/file_not_found_spec.rb +16 -0
  94. data/vendor/js-test-core/spec/unit/js_test_core/resources/file_spec.rb +90 -0
  95. data/vendor/js-test-core/spec/unit/js_test_core/resources/runners/runner_spec.rb +303 -0
  96. data/vendor/js-test-core/spec/unit/js_test_core/resources/session_finish_spec.rb +79 -0
  97. data/vendor/js-test-core/spec/unit/js_test_core/resources/session_spec.rb +82 -0
  98. data/vendor/js-test-core/spec/unit/js_test_core/resources/specs/spec_dir_spec.rb +104 -0
  99. data/vendor/js-test-core/spec/unit/js_test_core/resources/specs/spec_file_spec.rb +41 -0
  100. data/vendor/js-test-core/spec/unit/js_test_core/resources/web_root_spec.rb +32 -0
  101. data/vendor/js-test-core/spec/unit/js_test_core/selenium_server_configuration_spec.rb +49 -0
  102. data/vendor/js-test-core/spec/unit/js_test_core/server_spec.rb +117 -0
  103. data/vendor/js-test-core/spec/unit/thin/js_test_core_connection_spec.rb +6 -0
  104. data/vendor/js-test-core/spec/unit/unit_spec_helper.rb +161 -0
  105. data/vendor/js-test-core/spec/unit_suite.rb +10 -0
  106. data/vendor/js-test-core/vendor/thin-rest/CHANGES +2 -0
  107. data/vendor/js-test-core/vendor/thin-rest/README +0 -0
  108. data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/connection.rb +117 -0
  109. data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/extensions/object.rb +21 -0
  110. data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/extensions.rb +3 -0
  111. data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/representations/internal_error.html.rb +19 -0
  112. data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/representations/page.html.rb +41 -0
  113. data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/representations/resource_not_found.html.rb +18 -0
  114. data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/representations.rb +4 -0
  115. data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/resources/internal_error.rb +11 -0
  116. data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/resources/resource.rb +105 -0
  117. data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/resources/resource_not_found.rb +19 -0
  118. data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/resources.rb +4 -0
  119. data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/routing_error.rb +5 -0
  120. data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest.rb +10 -0
  121. data/vendor/js-test-core/vendor/thin-rest/spec/spec_suite.rb +5 -0
  122. data/vendor/js-test-core/vendor/thin-rest/spec/thin_rest/connection_spec.rb +205 -0
  123. data/vendor/js-test-core/vendor/thin-rest/spec/thin_rest/resources/resource_not_found_spec.rb +16 -0
  124. data/vendor/js-test-core/vendor/thin-rest/spec/thin_rest/resources/resource_spec.rb +135 -0
  125. data/vendor/js-test-core/vendor/thin-rest/spec/thin_rest/resources/root_spec.rb +16 -0
  126. data/vendor/js-test-core/vendor/thin-rest/spec/thin_rest_spec_helper.rb +133 -0
  127. metadata +184 -0
@@ -0,0 +1,25 @@
1
+ - Added support for custom suite files
2
+ - Added dependency on Erector
3
+ - Better error messages
4
+ - Changed dependency from Selenium to selenium-client
5
+
6
+ 0.2.0
7
+ - Renamed Suite to Session to follow Selenium conventions
8
+ - Renamed SuiteFinish to SessionFinish to follow Selenium conventions
9
+ - Added /session, which uses the session_id cookie to derive the current session.
10
+ - Added /session/finished to be used to simplify client/server interactions.
11
+ - Remove File caching because it doesn't cause a noticable performance benefit over a local network and because it sometimes doesn't write over stale files.
12
+ - Client accepts selenium_browser_start_command parameter, which allows the user to parameterize the selenium_browser_start_command
13
+ - Added support for running specs in Internet Explorer via POST /runners/iexplore
14
+ - Resource file download performance improvements via caching and chunked sending
15
+ - Fixed false positive bug when Client connection times out
16
+
17
+ 0.1.1
18
+ - SuiteFinish#post immediately closes the connection
19
+
20
+ 0.1.0
21
+ - Initial Release extracted from JsTestCore.
22
+ - Added JsTestCore::WebRoot.dispatch_specs and .dispatch_strategy
23
+ - Extracted JsTestCore::Resources::Specs::SpecDir
24
+ - Extracted JsTestCore::Resources::Specs::SpecFile
25
+ - No longer depending on Guid. The child libraries are responsible for obtaining the Selenium session_id from the client by using top.runOptions.getSessionId().
@@ -0,0 +1,12 @@
1
+ = JsTestCore
2
+ The JsTestCore library is the core javascript test server library used by several JS Test server libraries.
3
+
4
+ To hook up your own JS Test framework:
5
+ * You will need to call either JsTestCore::Resources::WebRoot.dispatch_specs or JsTestCore::Resources::WebRoot.dispatch_tests
6
+ depending if you are serving specs or tests.
7
+ * Set JsTestCore.core_path to the directory of the client side test framework (javascript files)
8
+ * Override JsTestCore::Resources::Specs::SpecFile#get or JsTestCore::Resources::Specs::TestFile#get
9
+ * Override JsTestCore::Resources::Specs::SpecDir#get or JsTestCore::Resources::Specs::TestDir#get
10
+
11
+ NOTE: The current version of JsTestCore does not yet support tests. Support will be added soon.
12
+ I just wanted the test stuff to be in the docs to establish the current architectural direction.
@@ -0,0 +1,73 @@
1
+ require "rake"
2
+ require 'rake/gempackagetask'
3
+ require 'rake/contrib/rubyforgepublisher'
4
+ require 'rake/clean'
5
+ require 'rake/testtask'
6
+ require 'rake/rdoctask'
7
+
8
+ desc "Runs the Rspec suite"
9
+ task(:default) do
10
+ run_suite
11
+ end
12
+
13
+ desc "Runs the Rspec suite"
14
+ task(:spec) do
15
+ run_suite
16
+ end
17
+
18
+ desc "Copies the trunk to a tag with the name of the current release"
19
+ task(:tag_release) do
20
+ tag_release
21
+ end
22
+
23
+ def run_suite
24
+ dir = File.dirname(__FILE__)
25
+ system("ruby #{dir}/spec/spec_suite.rb") || raise("Example Suite failed")
26
+ end
27
+
28
+ PKG_NAME = "js_test_core"
29
+ PKG_VERSION = "0.2.0"
30
+ PKG_FILES = FileList[
31
+ '[A-Z]*',
32
+ '*.rb',
33
+ 'lib/**/*.rb',
34
+ 'core/**',
35
+ 'bin/**',
36
+ 'spec/**/*.rb'
37
+ ]
38
+
39
+ spec = Gem::Specification.new do |s|
40
+ s.name = PKG_NAME
41
+ s.version = PKG_VERSION
42
+ s.summary = "The JsTestCore library is the core javascript test server library used by several JS Test server libraries."
43
+ s.test_files = "spec/spec_suite.rb"
44
+ s.description = s.summary
45
+
46
+ s.files = PKG_FILES.to_a
47
+ s.require_path = 'lib'
48
+
49
+ s.has_rdoc = true
50
+ s.extra_rdoc_files = [ "README", "CHANGES" ]
51
+ s.rdoc_options = ["--main", "README", "--inline-source", "--line-numbers"]
52
+
53
+ s.test_files = Dir.glob('spec/*_spec.rb')
54
+ s.require_path = 'lib'
55
+ s.author = "Brian Takita"
56
+ s.email = "brian@pivotallabs.com"
57
+ s.homepage = "http://pivotallabs.com"
58
+ s.rubyforge_project = "pivotalrb"
59
+ s.add_dependency('Selenium')
60
+ s.add_dependency('thin', '=0.8.1')
61
+ s.add_dependency('erector')
62
+ end
63
+
64
+ Rake::GemPackageTask.new(spec) do |pkg|
65
+ pkg.need_zip = true
66
+ pkg.need_tar = true
67
+ end
68
+
69
+ def tag_release
70
+ dashed_version = PKG_VERSION.gsub('.', '-')
71
+ svn_user = "#{ENV["SVN_USER"]}@" || ""
72
+ `svn cp svn+ssh://#{svn_user}rubyforge.org/var/svn/pivotalrb/js_test_core/trunk svn+ssh://#{svn_user}rubyforge.org/var/svn/pivotalrb/js_test_core/tags/REL-#{dashed_version} -m 'Version #{PKG_VERSION}'`
73
+ end
@@ -0,0 +1,129 @@
1
+ module JsTestCore
2
+ class Client
3
+ class ClientException < Exception
4
+ end
5
+
6
+ class InvalidStatusResponse < ClientException
7
+ end
8
+
9
+ class SessionNotFound < ClientException
10
+ end
11
+
12
+ class << self
13
+ def run(parameters={})
14
+ new(parameters).run
15
+ end
16
+
17
+ def run_argv(argv)
18
+ params = {}
19
+ parser = OptionParser.new do |o|
20
+ o.banner = "JsTestCore Runner"
21
+ o.banner << "\nUsage: #{$0} [options] [-- untouched arguments]"
22
+
23
+ o.on
24
+ o.on('-s', '--selenium_browser_start_command=selenium_browser_start_command', "The Selenium server command to start the browser. See http://selenium-rc.openqa.org/") do |selenium_browser_start_command|
25
+ params[:selenium_browser_start_command] = selenium_browser_start_command
26
+ end
27
+
28
+ o.on('-h', '--selenium_host=SELENIUM_HOST', "The host name of the Selenium Server relative to where this file is executed") do |host|
29
+ params[:selenium_host] = host
30
+ end
31
+
32
+ o.on('-p', '--selenium_port=SELENIUM_PORT', "The port of the Selenium Server relative to where this file is executed") do |port|
33
+ params[:selenium_port] = port
34
+ end
35
+
36
+ o.on('-u', '--spec_url=SPEC_URL', "The url of the js spec server, relative to the browsers running via the Selenium Server") do |spec_url|
37
+ params[:spec_url] = spec_url
38
+ end
39
+
40
+ o.on('-t', '--timeout=TIMEOUT', "The timeout limit of the test run") do |timeout|
41
+ params[:timeout] = Integer(timeout)
42
+ end
43
+
44
+ o.on_tail
45
+ end
46
+ parser.order!(argv)
47
+ run params
48
+ end
49
+ end
50
+
51
+ attr_reader :parameters, :query_string, :http, :session_start_response, :last_poll_status, :last_poll_reason, :last_poll
52
+ def initialize(parameters)
53
+ @parameters = parameters
54
+ @query_string = SeleniumServerConfiguration.query_string_from(parameters)
55
+ end
56
+
57
+ def run
58
+ if parameters[:timeout]
59
+ Timeout.timeout(parameters[:timeout]) {do_run}
60
+ else
61
+ do_run
62
+ end
63
+ end
64
+
65
+ def parts_from_query(query)
66
+ query.split('&').inject({}) do |acc, key_value_pair|
67
+ key, value = key_value_pair.split('=')
68
+ acc[key] = value
69
+ acc
70
+ end
71
+ end
72
+
73
+ protected
74
+ def do_run
75
+ Net::HTTP.start(DEFAULT_HOST, DEFAULT_PORT) do |@http|
76
+ start_runner
77
+ wait_for_session_to_finish
78
+ end
79
+ report_result
80
+ end
81
+
82
+ def start_runner
83
+ @session_start_response = http.post('/runners', query_string)
84
+ end
85
+
86
+ def wait_for_session_to_finish
87
+ while session_not_completed?
88
+ poll
89
+ sleep 0.25
90
+ end
91
+ end
92
+
93
+ def report_result
94
+ case last_poll_status
95
+ when Resources::Session::SUCCESSFUL_COMPLETION
96
+ STDOUT.puts "SUCCESS"
97
+ true
98
+ when Resources::Session::FAILURE_COMPLETION
99
+ STDOUT.puts "FAILURE"
100
+ STDOUT.puts last_poll_reason
101
+ false
102
+ else
103
+ raise InvalidStatusResponse, "Invalid Status: #{last_poll_status}"
104
+ end
105
+ end
106
+
107
+ def session_not_completed?
108
+ last_poll_status.nil? || last_poll_status == Resources::Session::RUNNING
109
+ end
110
+
111
+ def poll
112
+ @last_poll = http.get("/sessions/#{session_id}")
113
+ ensure_session_exists!
114
+ parts = parts_from_query(last_poll.body)
115
+ @last_poll_status = parts['status']
116
+ @last_poll_reason = parts['reason']
117
+ end
118
+
119
+ def ensure_session_exists!
120
+ if (400..499).include?(Integer(last_poll.code))
121
+ raise SessionNotFound, "Could not find session with id #{session_id}"
122
+ end
123
+ end
124
+
125
+ def session_id
126
+ @session_id ||= parts_from_query(session_start_response.body)['session_id']
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,6 @@
1
+ require "time"
2
+ class Time
3
+ def rfc822
4
+ strftime("%a, %d %b %Y %k:%M:%S %Z")
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ Dir["#{File.dirname(__FILE__)}/extensions/*.rb"].each do |file|
2
+ require file
3
+ end
@@ -0,0 +1,5 @@
1
+ Rack::CommonLogger.class_eval do
2
+ def empty?
3
+ @body.empty?
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ dir = File.dirname(__FILE__)
2
+ require "#{dir}/rack/commonlogger"
@@ -0,0 +1,22 @@
1
+ module JsTestCore
2
+ class RailsServer < Server
3
+ class << self
4
+ def run(rails_root, server_options = {})
5
+ server_options[:Host] ||= DEFAULT_HOST
6
+ server_options[:Port] ||= DEFAULT_PORT
7
+ Server.instance = new(rails_root, server_options[:Host], server_options[:Port])
8
+ Server.instance.run 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
@@ -0,0 +1,23 @@
1
+ module JsTestCore
2
+ module Representations
3
+ class Spec < Page
4
+ protected
5
+ def title_text
6
+ "Js Test Core Suite"
7
+ end
8
+
9
+ def head_content
10
+ spec_script_elements
11
+ end
12
+
13
+ def spec_script_elements
14
+ spec_files.each do |file|
15
+ script :type => "text/javascript", :src => file.relative_path
16
+ end
17
+ end
18
+
19
+ def body_content
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,9 @@
1
+ dir = File.dirname(__FILE__)
2
+
3
+ module JsTestCore
4
+ module Representations
5
+ include ThinRest::Representations
6
+ end
7
+ end
8
+
9
+ require "#{dir}/representations/spec.html"
@@ -0,0 +1,67 @@
1
+ module JsTestCore
2
+ module Resources
3
+ class Dir < File
4
+ route ANY do |env, name|
5
+ if file = file(name)
6
+ file
7
+ elsif subdir = subdir(name)
8
+ subdir
9
+ else
10
+ Resources::ResourceNotFound.new(env.merge(:name => name))
11
+ end
12
+ end
13
+
14
+ def get
15
+ connection.send_head
16
+ connection.send_body(::Dir.glob("#{absolute_path}/*").inject("") do |html, file|
17
+ file_basename = ::File.basename(file)
18
+ html << %Q|<a href="#{file_basename}">#{file_basename}</a>\n|
19
+ end)
20
+ end
21
+
22
+ def glob(pattern)
23
+ expanded_pattern = absolute_path + pattern
24
+ ::Dir.glob(expanded_pattern).map do |absolute_globbed_path|
25
+ relative_globbed_path = absolute_globbed_path.gsub(absolute_path, relative_path)
26
+ File.new(env.merge(
27
+ :absolute_path => absolute_globbed_path,
28
+ :relative_path => relative_globbed_path
29
+ ))
30
+ end
31
+ end
32
+
33
+ protected
34
+ def determine_child_paths(name)
35
+ absolute_child_path = "#{absolute_path}/#{name}"
36
+ relative_child_path = "#{relative_path}/#{name}"
37
+ [absolute_child_path, relative_child_path]
38
+ end
39
+
40
+ def file(name)
41
+ # N.B. Absolute_path and relative_path are methods. Do not shadow.
42
+ absolute_file_path, relative_file_path = determine_child_paths(name)
43
+ if ::File.exists?(absolute_file_path) && !::File.directory?(absolute_file_path)
44
+ Resources::File.new(env.merge(
45
+ :absolute_path => absolute_file_path,
46
+ :relative_path => relative_file_path
47
+ ))
48
+ else
49
+ nil
50
+ end
51
+ end
52
+
53
+ def subdir(name)
54
+ # N.B. Absolute_path and relative_path are methods. Do not shadow.
55
+ absolute_dir_path, relative_dir_path = determine_child_paths(name)
56
+ if ::File.directory?(absolute_dir_path)
57
+ Resources::Dir.new(env.merge(
58
+ :absolute_path => absolute_dir_path,
59
+ :relative_path => relative_dir_path
60
+ ))
61
+ else
62
+ nil
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,42 @@
1
+ module JsTestCore
2
+ module Resources
3
+ class File < Resource
4
+ MIME_TYPES = {
5
+ '.html' => 'text/html',
6
+ '.htm' => 'text/html',
7
+ '.js' => 'text/javascript',
8
+ '.css' => 'text/css',
9
+ '.png' => 'image/png',
10
+ '.jpg' => 'image/jpeg',
11
+ '.jpeg' => 'image/jpeg',
12
+ '.gif' => 'image/gif',
13
+ }
14
+
15
+ property :absolute_path, :relative_path
16
+
17
+ def get
18
+ extension = ::File.extname(absolute_path)
19
+ content_type = MIME_TYPES[extension] || 'text/html'
20
+
21
+ connection.terminate_after_sending do
22
+ connection.send_head(
23
+ 200,
24
+ 'Content-Type' => content_type,
25
+ 'Last-Modified' => ::File.mtime(absolute_path).rfc822,
26
+ 'Content-Length' => ::File.size(absolute_path)
27
+ )
28
+ ::File.open(absolute_path) do |file|
29
+ while !file.eof?
30
+ connection.send_data(file.read(1024))
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ def ==(other)
37
+ return false unless other.class == self.class
38
+ absolute_path == other.absolute_path && relative_path == other.relative_path
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,107 @@
1
+ module JsTestCore
2
+ module Resources
3
+ class Runner < Resources::Resource
4
+ class Collection < Resources::Resource
5
+ property :selenium_browser_start_command
6
+ route 'firefox' do |env, name|
7
+ self.selenium_browser_start_command = "*firefox"
8
+ self
9
+ end
10
+ route 'iexplore' do |env, name|
11
+ self.selenium_browser_start_command = "*iexplore"
12
+ self
13
+ end
14
+
15
+ def after_initialize
16
+ super
17
+ self.selenium_browser_start_command = rack_request['selenium_browser_start_command']
18
+ end
19
+
20
+ def post
21
+ spec_url = rack_request['spec_url'].to_s == "" ? full_spec_suite_url : rack_request['spec_url']
22
+ parsed_spec_url = URI.parse(spec_url)
23
+ selenium_host = rack_request['selenium_host'].to_s == "" ? 'localhost' : rack_request['selenium_host'].to_s
24
+ selenium_port = rack_request['selenium_port'].to_s == "" ? 4444 : Integer(rack_request['selenium_port'])
25
+ http_address = "#{parsed_spec_url.scheme}://#{parsed_spec_url.host}:#{parsed_spec_url.port}"
26
+ driver = Selenium::Client::Driver.new(
27
+ selenium_host,
28
+ selenium_port,
29
+ selenium_browser_start_command,
30
+ http_address
31
+ )
32
+ begin
33
+ driver.start
34
+ rescue Errno::ECONNREFUSED => e
35
+ raise Errno::ECONNREFUSED, "Cannot connect to Selenium Server at #{http_address}. To start the selenium server, run `selenium`."
36
+ end
37
+ runner = Runner.new(:driver => driver)
38
+ Runner.register(runner)
39
+ Thread.start do
40
+ driver.open("/")
41
+ driver.create_cookie("session_id=#{runner.session_id}")
42
+ driver.open(parsed_spec_url.path)
43
+ end
44
+ connection.send_head
45
+ connection.send_body("session_id=#{runner.session_id}")
46
+ end
47
+
48
+ protected
49
+ def full_spec_suite_url
50
+ "#{Server.root_url}/specs"
51
+ end
52
+ end
53
+
54
+ class << self
55
+ def find(id)
56
+ instances[id.to_s]
57
+ end
58
+
59
+ def finalize(session_id, text)
60
+ if runner = find(session_id)
61
+ runner.finalize(text)
62
+ end
63
+ end
64
+
65
+ def register(runner)
66
+ instances[runner.session_id] = runner
67
+ end
68
+
69
+ protected
70
+ def instances
71
+ @instances ||= {}
72
+ end
73
+ end
74
+
75
+ include FileUtils
76
+ property :driver
77
+ attr_reader :profile_dir, :session_run_result
78
+
79
+ def after_initialize
80
+ profile_base = "#{::Dir.tmpdir}/js_test_core/#{self.class.name}"
81
+ mkdir_p profile_base
82
+ @profile_dir = "#{profile_base}/#{Time.now.to_i}"
83
+ end
84
+
85
+ def finalize(session_run_result)
86
+ driver.stop
87
+ @session_run_result = session_run_result.to_s
88
+ end
89
+
90
+ def running?
91
+ driver.session_started?
92
+ end
93
+
94
+ def successful?
95
+ !running? && session_run_result.empty?
96
+ end
97
+
98
+ def failed?
99
+ !running? && !successful?
100
+ end
101
+
102
+ def session_id
103
+ driver.session_id
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,44 @@
1
+ module JsTestCore
2
+ module Resources
3
+ class Session < Resources::Resource
4
+ class Collection < Resources::Resource
5
+ route ANY do |env, id|
6
+ Session.new(env.merge(:id => id))
7
+ end
8
+ end
9
+
10
+ RUNNING = 'running'
11
+ SUCCESSFUL_COMPLETION = 'success'
12
+ FAILURE_COMPLETION = 'failure'
13
+
14
+ property :id
15
+
16
+ def get
17
+ runner = Runner.find(id)
18
+ if runner
19
+ connection.send_head
20
+ if runner.running?
21
+ connection.send_body("status=#{RUNNING}")
22
+ else
23
+ if runner.successful?
24
+ connection.send_body("status=#{SUCCESSFUL_COMPLETION}")
25
+ else
26
+ connection.send_body("status=#{FAILURE_COMPLETION}&reason=#{runner.session_run_result}")
27
+ end
28
+ end
29
+ else
30
+ connection.send_head(404)
31
+ connection.send_body("")
32
+ end
33
+ end
34
+
35
+ route 'finish' do |env, name|
36
+ SessionFinish.new(env.merge(:session => self))
37
+ end
38
+
39
+ def associated_with_a_runner?
40
+ id.to_s != ""
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,17 @@
1
+ module JsTestCore
2
+ module Resources
3
+ class SessionFinish < Resources::Resource
4
+ property :session
5
+
6
+ def post
7
+ if session.associated_with_a_runner?
8
+ Runner.finalize(session.id, rack_request['text'])
9
+ else
10
+ STDOUT.puts rack_request['text']
11
+ end
12
+ connection.send_head
13
+ connection.send_body("")
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,32 @@
1
+ module JsTestCore
2
+ module Resources
3
+ module Specs
4
+ module Spec
5
+ class << self
6
+ def spec_representation_class
7
+ @spec_representation_class ||= JsTestCore::Representations::Spec
8
+ end
9
+ attr_writer :spec_representation_class
10
+ end
11
+
12
+ def get_generated_spec
13
+ connection.terminate_after_sending do
14
+ connection.send_head(
15
+ 200,
16
+ 'Content-Type' => "text/html",
17
+ 'Last-Modified' => ::File.mtime(absolute_path).rfc822
18
+ )
19
+
20
+ body = render_spec
21
+ connection.send_data("Content-Length: #{body.length}\r\n\r\n")
22
+ connection.send_data(body)
23
+ end
24
+ end
25
+
26
+ def render_spec
27
+ Spec.spec_representation_class.new(self, :spec_files => spec_files).to_s
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,50 @@
1
+ module JsTestCore
2
+ module Resources
3
+ module Specs
4
+ class SpecDir < ::JsTestCore::Resources::Dir
5
+ include Spec
6
+
7
+ def get
8
+ if ::File.file?(absolute_path)
9
+ super
10
+ else
11
+ get_generated_spec
12
+ end
13
+ end
14
+
15
+ def spec_files
16
+ glob("/**/*_spec.js")
17
+ end
18
+
19
+ route ANY do |env, name|
20
+ if result = (file(name) || subdir(name) || spec_file(name))
21
+ result
22
+ else
23
+ base_path = "#{relative_path}/#{name}"
24
+ raise "No file or directory found at #{base_path} or spec found at #{base_path}.js."
25
+ end
26
+ end
27
+
28
+ protected
29
+
30
+ def subdir(name)
31
+ absolute_path, relative_path = determine_child_paths(name)
32
+ if ::File.directory?(absolute_path)
33
+ SpecDir.new(env.merge(:absolute_path => absolute_path, :relative_path => relative_path))
34
+ else
35
+ nil
36
+ end
37
+ end
38
+
39
+ def spec_file(name)
40
+ absolute_path, relative_path = determine_child_paths("#{name}.js")
41
+ if ::File.exists?(absolute_path) && !::File.directory?(absolute_path)
42
+ SpecFile.new(env.merge(:absolute_path => absolute_path, :relative_path => relative_path))
43
+ else
44
+ nil
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end