js_test_core 0.1.1 → 0.2.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.
- data/CHANGES +11 -0
- data/Rakefile +1 -1
- data/lib/js_test_core.rb +10 -2
- data/lib/js_test_core/client.rb +85 -18
- data/lib/js_test_core/extensions.rb +3 -0
- data/lib/js_test_core/extensions/time.rb +6 -0
- data/lib/js_test_core/resources.rb +4 -4
- data/lib/js_test_core/resources/dir.rb +22 -7
- data/lib/js_test_core/resources/file.rb +24 -16
- data/lib/js_test_core/resources/file_not_found.rb +11 -0
- data/lib/js_test_core/resources/runner.rb +107 -0
- data/lib/js_test_core/resources/session.rb +44 -0
- data/lib/js_test_core/resources/session_finish.rb +17 -0
- data/lib/js_test_core/resources/specs/spec_dir.rb +10 -14
- data/lib/js_test_core/resources/specs/spec_file.rb +1 -1
- data/lib/js_test_core/resources/web_root.rb +51 -39
- data/lib/js_test_core/selenium_server_configuration.rb +48 -0
- data/lib/js_test_core/server.rb +3 -64
- data/lib/js_test_core/thin/js_test_core_connection.rb +4 -38
- data/spec/unit/js_test_core/client_spec.rb +167 -0
- data/spec/unit/{js_spec → js_test_core}/rails_server_spec.rb +0 -0
- data/spec/unit/js_test_core/resources/dir_spec.rb +52 -0
- data/spec/unit/js_test_core/resources/file_not_found_spec.rb +16 -0
- data/spec/unit/js_test_core/resources/file_spec.rb +90 -0
- data/spec/unit/js_test_core/resources/runners/runner_spec.rb +303 -0
- data/spec/unit/js_test_core/resources/session_finish_spec.rb +79 -0
- data/spec/unit/js_test_core/resources/session_spec.rb +82 -0
- data/spec/unit/js_test_core/resources/specs/spec_dir_spec.rb +105 -0
- data/spec/unit/{js_spec → js_test_core}/resources/specs/spec_file_spec.rb +5 -5
- data/spec/unit/js_test_core/resources/web_root_spec.rb +32 -0
- data/spec/unit/js_test_core/selenium_server_configuration_spec.rb +49 -0
- data/spec/unit/{js_spec → js_test_core}/server_spec.rb +18 -32
- data/spec/unit/thin/js_test_core_connection_spec.rb +0 -86
- data/spec/unit/unit_spec_helper.rb +58 -22
- metadata +39 -33
- data/lib/js_test_core/resources/runners.rb +0 -15
- data/lib/js_test_core/resources/runners/firefox_runner.rb +0 -73
- data/lib/js_test_core/resources/suite.rb +0 -24
- data/lib/js_test_core/resources/suite_finish.rb +0 -19
- data/spec/unit/js_spec/client_spec.rb +0 -137
- data/spec/unit/js_spec/resources/dir_spec.rb +0 -42
- data/spec/unit/js_spec/resources/file_spec.rb +0 -88
- data/spec/unit/js_spec/resources/runner_spec.rb +0 -24
- data/spec/unit/js_spec/resources/runners/firefox_runner_spec.rb +0 -161
- data/spec/unit/js_spec/resources/specs/spec_dir_spec.rb +0 -79
- data/spec/unit/js_spec/resources/suite_finish_spec.rb +0 -94
- data/spec/unit/js_spec/resources/suite_spec.rb +0 -44
- data/spec/unit/js_spec/resources/web_root_spec.rb +0 -67
data/CHANGES
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
0.2.0
|
2
|
+
- Renamed Suite to Session to follow Selenium conventions
|
3
|
+
- Renamed SuiteFinish to SessionFinish to follow Selenium conventions
|
4
|
+
- Added /session, which uses the session_id cookie to derive the current session.
|
5
|
+
- Added /session/finished to be used to simplify client/server interactions.
|
6
|
+
- 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.
|
7
|
+
- Client accepts selenium_browser_start_command parameter, which allows the user to parameterize the selenium_browser_start_command
|
8
|
+
- Added support for running specs in Internet Explorer via POST /runners/iexplore
|
9
|
+
- Resource file download performance improvements via caching and chunked sending
|
10
|
+
- Fixed false positive bug when Client connection times out
|
11
|
+
|
1
12
|
0.1.1
|
2
13
|
- SuiteFinish#post immediately closes the connection
|
3
14
|
|
data/Rakefile
CHANGED
data/lib/js_test_core.rb
CHANGED
@@ -1,7 +1,14 @@
|
|
1
1
|
require "rubygems"
|
2
2
|
gem "thin", ">=0.8.0"
|
3
3
|
|
4
|
-
|
4
|
+
dir = File.dirname(__FILE__)
|
5
|
+
$LOAD_PATH.unshift File.expand_path("#{dir}/../vendor/thin-rest/lib")
|
6
|
+
require "thin_rest"
|
7
|
+
|
8
|
+
# This causes errors to be printed to STDOUT.
|
9
|
+
Thin::Logging.silent = false
|
10
|
+
Thin::Logging.debug = true
|
11
|
+
|
5
12
|
require "fileutils"
|
6
13
|
require "tmpdir"
|
7
14
|
require "timeout"
|
@@ -10,13 +17,14 @@ require "net/http"
|
|
10
17
|
require "selenium"
|
11
18
|
require "optparse"
|
12
19
|
|
13
|
-
dir
|
20
|
+
require "#{dir}/js_test_core/extensions"
|
14
21
|
require "#{dir}/js_test_core/thin"
|
15
22
|
require "#{dir}/js_test_core/rack"
|
16
23
|
require "#{dir}/js_test_core/resources"
|
17
24
|
require "#{dir}/js_test_core/selenium"
|
18
25
|
|
19
26
|
require "#{dir}/js_test_core/client"
|
27
|
+
require "#{dir}/js_test_core/selenium_server_configuration"
|
20
28
|
require "#{dir}/js_test_core/server"
|
21
29
|
require "#{dir}/js_test_core/rails_server"
|
22
30
|
|
data/lib/js_test_core/client.rb
CHANGED
@@ -1,24 +1,17 @@
|
|
1
1
|
module JsTestCore
|
2
2
|
class Client
|
3
|
-
class
|
4
|
-
|
5
|
-
data = []
|
6
|
-
data << "selenium_host=#{CGI.escape(params[:selenium_host] || 'localhost')}"
|
7
|
-
data << "selenium_port=#{CGI.escape((params[:selenium_port] || 4444).to_s)}"
|
8
|
-
data << "spec_url=#{CGI.escape(params[:spec_url])}" if params[:spec_url]
|
9
|
-
response = Net::HTTP.start(DEFAULT_HOST, DEFAULT_PORT) do |http|
|
10
|
-
http.post('/runners/firefox', data.join("&"))
|
11
|
-
end
|
3
|
+
class ClientException < Exception
|
4
|
+
end
|
12
5
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
22
15
|
end
|
23
16
|
|
24
17
|
def run_argv(argv)
|
@@ -28,6 +21,10 @@ module JsTestCore
|
|
28
21
|
o.banner << "\nUsage: #{$0} [options] [-- untouched arguments]"
|
29
22
|
|
30
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
|
+
|
31
28
|
o.on('-h', '--selenium_host=SELENIUM_HOST', "The host name of the Selenium Server relative to where this file is executed") do |host|
|
32
29
|
params[:selenium_host] = host
|
33
30
|
end
|
@@ -46,5 +43,75 @@ module JsTestCore
|
|
46
43
|
run params
|
47
44
|
end
|
48
45
|
end
|
46
|
+
|
47
|
+
attr_reader :parameters, :query_string, :http, :session_start_response, :last_poll_status, :last_poll_reason, :last_poll
|
48
|
+
def initialize(parameters)
|
49
|
+
@parameters = parameters
|
50
|
+
@query_string = SeleniumServerConfiguration.query_string_from(parameters)
|
51
|
+
end
|
52
|
+
|
53
|
+
def run
|
54
|
+
Net::HTTP.start(DEFAULT_HOST, DEFAULT_PORT) do |@http|
|
55
|
+
start_runner
|
56
|
+
wait_for_session_to_finish
|
57
|
+
end
|
58
|
+
report_result
|
59
|
+
end
|
60
|
+
|
61
|
+
def parts_from_query(query)
|
62
|
+
query.split('&').inject({}) do |acc, key_value_pair|
|
63
|
+
key, value = key_value_pair.split('=')
|
64
|
+
acc[key] = value
|
65
|
+
acc
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
protected
|
70
|
+
def start_runner
|
71
|
+
@session_start_response = http.post('/runners', query_string)
|
72
|
+
end
|
73
|
+
|
74
|
+
def wait_for_session_to_finish
|
75
|
+
while session_not_completed?
|
76
|
+
poll
|
77
|
+
sleep 0.25
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def report_result
|
82
|
+
case last_poll_status
|
83
|
+
when Resources::Session::SUCCESSFUL_COMPLETION
|
84
|
+
STDOUT.puts "SUCCESS"
|
85
|
+
true
|
86
|
+
when Resources::Session::FAILURE_COMPLETION
|
87
|
+
STDOUT.puts "FAILURE"
|
88
|
+
STDOUT.puts last_poll_reason
|
89
|
+
false
|
90
|
+
else
|
91
|
+
raise InvalidStatusResponse, "Invalid Status: #{last_poll_status}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def session_not_completed?
|
96
|
+
last_poll_status.nil? || last_poll_status == Resources::Session::RUNNING
|
97
|
+
end
|
98
|
+
|
99
|
+
def poll
|
100
|
+
@last_poll = http.get("/sessions/#{session_id}")
|
101
|
+
ensure_session_exists!
|
102
|
+
parts = parts_from_query(last_poll.body)
|
103
|
+
@last_poll_status = parts['status']
|
104
|
+
@last_poll_reason = parts['reason']
|
105
|
+
end
|
106
|
+
|
107
|
+
def ensure_session_exists!
|
108
|
+
if (400..499).include?(Integer(last_poll.code))
|
109
|
+
raise SessionNotFound, "Could not find session with id #{session_id}"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def session_id
|
114
|
+
@session_id ||= parts_from_query(session_start_response.body)['session_id']
|
115
|
+
end
|
49
116
|
end
|
50
117
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
dir = File.dirname(__FILE__)
|
2
|
-
require "#{dir}/resources/
|
3
|
-
require "#{dir}/resources/runners/firefox_runner"
|
2
|
+
require "#{dir}/resources/runner"
|
4
3
|
require "#{dir}/resources/file"
|
5
4
|
require "#{dir}/resources/dir"
|
5
|
+
require "#{dir}/resources/file_not_found"
|
6
6
|
require "#{dir}/resources/specs/spec_file"
|
7
7
|
require "#{dir}/resources/specs/spec_dir"
|
8
8
|
require "#{dir}/resources/web_root"
|
9
|
-
require "#{dir}/resources/
|
10
|
-
require "#{dir}/resources/
|
9
|
+
require "#{dir}/resources/session"
|
10
|
+
require "#{dir}/resources/session_finish"
|
@@ -1,25 +1,32 @@
|
|
1
1
|
module JsTestCore
|
2
2
|
module Resources
|
3
3
|
class Dir < File
|
4
|
-
|
4
|
+
route ANY do |env, name|
|
5
5
|
if file = file(name)
|
6
6
|
file
|
7
7
|
elsif subdir = subdir(name)
|
8
8
|
subdir
|
9
9
|
else
|
10
|
-
|
10
|
+
FileNotFound.new(env.merge(:name => name))
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
def get
|
15
|
-
|
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)
|
16
20
|
end
|
17
21
|
|
18
22
|
def glob(pattern)
|
19
23
|
expanded_pattern = absolute_path + pattern
|
20
24
|
::Dir.glob(expanded_pattern).map do |absolute_globbed_path|
|
21
25
|
relative_globbed_path = absolute_globbed_path.gsub(absolute_path, relative_path)
|
22
|
-
File.new(
|
26
|
+
File.new(env.merge(
|
27
|
+
:absolute_path => absolute_globbed_path,
|
28
|
+
:relative_path => relative_globbed_path
|
29
|
+
))
|
23
30
|
end
|
24
31
|
end
|
25
32
|
|
@@ -31,18 +38,26 @@ module JsTestCore
|
|
31
38
|
end
|
32
39
|
|
33
40
|
def file(name)
|
41
|
+
# N.B. Absolute_path and relative_path are methods. Do not shadow.
|
34
42
|
absolute_file_path, relative_file_path = determine_child_paths(name)
|
35
43
|
if ::File.exists?(absolute_file_path) && !::File.directory?(absolute_file_path)
|
36
|
-
Resources::File.new(
|
44
|
+
Resources::File.new(env.merge(
|
45
|
+
:absolute_path => absolute_file_path,
|
46
|
+
:relative_path => relative_file_path
|
47
|
+
))
|
37
48
|
else
|
38
49
|
nil
|
39
50
|
end
|
40
51
|
end
|
41
52
|
|
42
53
|
def subdir(name)
|
54
|
+
# N.B. Absolute_path and relative_path are methods. Do not shadow.
|
43
55
|
absolute_dir_path, relative_dir_path = determine_child_paths(name)
|
44
56
|
if ::File.directory?(absolute_dir_path)
|
45
|
-
Resources::Dir.new(
|
57
|
+
Resources::Dir.new(env.merge(
|
58
|
+
:absolute_path => absolute_dir_path,
|
59
|
+
:relative_path => relative_dir_path
|
60
|
+
))
|
46
61
|
else
|
47
62
|
nil
|
48
63
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module JsTestCore
|
2
2
|
module Resources
|
3
|
-
class File
|
3
|
+
class File < ThinRest::Resource
|
4
4
|
MIME_TYPES = {
|
5
5
|
'.js' => 'text/javascript',
|
6
6
|
'.css' => 'text/css',
|
@@ -8,25 +8,33 @@ module JsTestCore
|
|
8
8
|
'.jpg' => 'image/jpeg',
|
9
9
|
'.jpeg' => 'image/jpeg',
|
10
10
|
'.gif' => 'image/gif',
|
11
|
-
|
11
|
+
}
|
12
12
|
|
13
|
-
|
13
|
+
property :absolute_path, :relative_path
|
14
14
|
|
15
|
-
def
|
16
|
-
@absolute_path = absolute_path
|
17
|
-
@relative_path = relative_path
|
18
|
-
end
|
19
|
-
|
20
|
-
def get(request, response)
|
15
|
+
def get
|
21
16
|
extension = ::File.extname(absolute_path)
|
22
|
-
|
23
|
-
|
24
|
-
|
17
|
+
content_type = MIME_TYPES[extension] || 'text/html'
|
18
|
+
|
19
|
+
connection.terminate_after_sending do
|
20
|
+
connection.send_head(
|
21
|
+
200,
|
22
|
+
'Content-Type' => content_type,
|
23
|
+
'Last-Modified' => ::File.mtime(absolute_path).rfc822,
|
24
|
+
'Content-Length' => ::File.size(absolute_path)
|
25
|
+
)
|
26
|
+
::File.open(absolute_path) do |file|
|
27
|
+
while !file.eof?
|
28
|
+
connection.send_data(file.read(1024))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
25
32
|
|
26
|
-
|
27
|
-
|
28
|
-
|
33
|
+
def ==(other)
|
34
|
+
return false unless other.class == self.class
|
35
|
+
absolute_path == other.absolute_path && relative_path == other.relative_path
|
36
|
+
end
|
29
37
|
end
|
30
38
|
end
|
31
39
|
end
|
32
|
-
end
|
40
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module JsTestCore
|
2
|
+
module Resources
|
3
|
+
class Runner < ThinRest::Resource
|
4
|
+
class Collection < ThinRest::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::SeleniumDriver.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
|