pivotal-screw-unit 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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