pivotal-screw-unit 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +21 -0
- data/README.markdown +354 -0
- data/Rakefile +39 -0
- data/VERSION.yml +4 -0
- data/bin/screw_unit +6 -0
- data/bin/screw_unit_server +10 -0
- data/core/CHANGES +10 -0
- data/core/EXAMPLE.html +68 -0
- data/core/LICENSE +22 -0
- data/core/README.markdown +307 -0
- data/core/example/models/cat.js +5 -0
- data/core/example/models/man.js +17 -0
- data/core/example/spec/matchers/have.js +8 -0
- data/core/example/spec/models/cat_spec.js +31 -0
- data/core/example/spec/models/man_spec.js +34 -0
- data/core/example/spec/spec_helper.js +5 -0
- data/core/example/spec/suite.html +25 -0
- data/core/lib/jquery-1.3.2.js +4376 -0
- data/core/lib/jquery.fn.js +30 -0
- data/core/lib/jquery.print.js +109 -0
- data/core/lib/screw.behaviors.js +93 -0
- data/core/lib/screw.builder.js +94 -0
- data/core/lib/screw.css +90 -0
- data/core/lib/screw.events.js +45 -0
- data/core/lib/screw.matchers.js +244 -0
- data/core/spec/behaviors_spec.js +188 -0
- data/core/spec/matchers_spec.js +372 -0
- data/core/spec/print_spec.js +158 -0
- data/core/spec/spec_helper.js +0 -0
- data/core/spec/suite.html +19 -0
- data/core/spec/with_screw_context_spec.js +9 -0
- data/init.rb +0 -0
- data/lib/screw_unit/representations/spec.html.rb +117 -0
- data/lib/screw_unit/representations.rb +2 -0
- data/lib/screw_unit.rb +19 -0
- data/spec/functional/functional_spec.rb +25 -0
- data/spec/functional/functional_spec_helper.rb +34 -0
- data/spec/functional/functional_spec_server_starter.rb +63 -0
- data/spec/functional_suite.rb +10 -0
- data/spec/spec_suite.rb +3 -0
- data/spec/unit/js_test_core/specs/spec_dir_spec.rb +39 -0
- data/spec/unit/js_test_core/specs/spec_file_spec.rb +35 -0
- data/spec/unit/unit_spec_helper.rb +119 -0
- data/spec/unit_suite.rb +10 -0
- data/vendor/js-test-core/CHANGES +25 -0
- data/vendor/js-test-core/README +12 -0
- data/vendor/js-test-core/Rakefile +73 -0
- data/vendor/js-test-core/lib/js_test_core/client.rb +129 -0
- data/vendor/js-test-core/lib/js_test_core/extensions/time.rb +6 -0
- data/vendor/js-test-core/lib/js_test_core/extensions.rb +3 -0
- data/vendor/js-test-core/lib/js_test_core/rack/commonlogger.rb +5 -0
- data/vendor/js-test-core/lib/js_test_core/rack.rb +2 -0
- data/vendor/js-test-core/lib/js_test_core/rails_server.rb +22 -0
- data/vendor/js-test-core/lib/js_test_core/representations/spec.html.rb +23 -0
- data/vendor/js-test-core/lib/js_test_core/representations.rb +9 -0
- data/vendor/js-test-core/lib/js_test_core/resources/dir.rb +67 -0
- data/vendor/js-test-core/lib/js_test_core/resources/file.rb +42 -0
- data/vendor/js-test-core/lib/js_test_core/resources/runner.rb +107 -0
- data/vendor/js-test-core/lib/js_test_core/resources/session.rb +44 -0
- data/vendor/js-test-core/lib/js_test_core/resources/session_finish.rb +17 -0
- data/vendor/js-test-core/lib/js_test_core/resources/specs/spec.rb +32 -0
- data/vendor/js-test-core/lib/js_test_core/resources/specs/spec_dir.rb +50 -0
- data/vendor/js-test-core/lib/js_test_core/resources/specs/spec_file.rb +21 -0
- data/vendor/js-test-core/lib/js_test_core/resources/web_root.rb +66 -0
- data/vendor/js-test-core/lib/js_test_core/resources.rb +17 -0
- data/vendor/js-test-core/lib/js_test_core/selenium/client/driver.rb +7 -0
- data/vendor/js-test-core/lib/js_test_core/selenium.rb +2 -0
- data/vendor/js-test-core/lib/js_test_core/selenium_server_configuration.rb +48 -0
- data/vendor/js-test-core/lib/js_test_core/server.rb +50 -0
- data/vendor/js-test-core/lib/js_test_core/thin/backends/js_test_core_server.rb +9 -0
- data/vendor/js-test-core/lib/js_test_core/thin/js_test_core_connection.rb +8 -0
- data/vendor/js-test-core/lib/js_test_core/thin.rb +3 -0
- data/vendor/js-test-core/lib/js_test_core.rb +41 -0
- data/vendor/js-test-core/spec/example_core/JsTestCore.css +0 -0
- data/vendor/js-test-core/spec/example_core/JsTestCore.js +0 -0
- data/vendor/js-test-core/spec/example_public/favicon.ico +0 -0
- data/vendor/js-test-core/spec/example_public/javascripts/foo.js +3 -0
- data/vendor/js-test-core/spec/example_public/javascripts/large_file.js +59 -0
- data/vendor/js-test-core/spec/example_public/javascripts/subdir/bar.js +1 -0
- data/vendor/js-test-core/spec/example_public/robots.txt +0 -0
- data/vendor/js-test-core/spec/example_public/stylesheets/example.css +3 -0
- data/vendor/js-test-core/spec/example_specs/custom_dir_and_suite/passing_spec.js +6 -0
- data/vendor/js-test-core/spec/example_specs/custom_dir_and_suite.html +9 -0
- data/vendor/js-test-core/spec/example_specs/custom_suite.html +8 -0
- data/vendor/js-test-core/spec/example_specs/failing_spec.js +5 -0
- data/vendor/js-test-core/spec/example_specs/foo/failing_spec.js +6 -0
- data/vendor/js-test-core/spec/example_specs/foo/passing_spec.js +6 -0
- data/vendor/js-test-core/spec/spec_suite.rb +2 -0
- data/vendor/js-test-core/spec/specs/failing_spec.js +0 -0
- data/vendor/js-test-core/spec/unit/js_test_core/client_spec.rb +192 -0
- data/vendor/js-test-core/spec/unit/js_test_core/rails_server_spec.rb +45 -0
- data/vendor/js-test-core/spec/unit/js_test_core/resources/dir_spec.rb +53 -0
- data/vendor/js-test-core/spec/unit/js_test_core/resources/file_not_found_spec.rb +16 -0
- data/vendor/js-test-core/spec/unit/js_test_core/resources/file_spec.rb +90 -0
- data/vendor/js-test-core/spec/unit/js_test_core/resources/runners/runner_spec.rb +303 -0
- data/vendor/js-test-core/spec/unit/js_test_core/resources/session_finish_spec.rb +79 -0
- data/vendor/js-test-core/spec/unit/js_test_core/resources/session_spec.rb +82 -0
- data/vendor/js-test-core/spec/unit/js_test_core/resources/specs/spec_dir_spec.rb +104 -0
- data/vendor/js-test-core/spec/unit/js_test_core/resources/specs/spec_file_spec.rb +41 -0
- data/vendor/js-test-core/spec/unit/js_test_core/resources/web_root_spec.rb +32 -0
- data/vendor/js-test-core/spec/unit/js_test_core/selenium_server_configuration_spec.rb +49 -0
- data/vendor/js-test-core/spec/unit/js_test_core/server_spec.rb +117 -0
- data/vendor/js-test-core/spec/unit/thin/js_test_core_connection_spec.rb +6 -0
- data/vendor/js-test-core/spec/unit/unit_spec_helper.rb +161 -0
- data/vendor/js-test-core/spec/unit_suite.rb +10 -0
- data/vendor/js-test-core/vendor/thin-rest/CHANGES +2 -0
- data/vendor/js-test-core/vendor/thin-rest/README +0 -0
- data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/connection.rb +117 -0
- data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/extensions/object.rb +21 -0
- data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/extensions.rb +3 -0
- data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/representations/internal_error.html.rb +19 -0
- data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/representations/page.html.rb +41 -0
- data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/representations/resource_not_found.html.rb +18 -0
- data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/representations.rb +4 -0
- data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/resources/internal_error.rb +11 -0
- data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/resources/resource.rb +105 -0
- data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/resources/resource_not_found.rb +19 -0
- data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/resources.rb +4 -0
- data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/routing_error.rb +5 -0
- data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest.rb +10 -0
- data/vendor/js-test-core/vendor/thin-rest/spec/spec_suite.rb +5 -0
- data/vendor/js-test-core/vendor/thin-rest/spec/thin_rest/connection_spec.rb +205 -0
- data/vendor/js-test-core/vendor/thin-rest/spec/thin_rest/resources/resource_not_found_spec.rb +16 -0
- data/vendor/js-test-core/vendor/thin-rest/spec/thin_rest/resources/resource_spec.rb +135 -0
- data/vendor/js-test-core/vendor/thin-rest/spec/thin_rest/resources/root_spec.rb +16 -0
- data/vendor/js-test-core/vendor/thin-rest/spec/thin_rest_spec_helper.rb +133 -0
- 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,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,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
|