js_test_core 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +9 -0
- data/README +12 -0
- data/Rakefile +72 -0
- data/lib/js_test_core.rb +30 -0
- data/lib/js_test_core/client.rb +50 -0
- data/lib/js_test_core/rack.rb +2 -0
- data/lib/js_test_core/rack/commonlogger.rb +5 -0
- data/lib/js_test_core/rails_server.rb +22 -0
- data/lib/js_test_core/resources.rb +10 -0
- data/lib/js_test_core/resources/dir.rb +52 -0
- data/lib/js_test_core/resources/file.rb +32 -0
- data/lib/js_test_core/resources/runners.rb +15 -0
- data/lib/js_test_core/resources/runners/firefox_runner.rb +73 -0
- data/lib/js_test_core/resources/specs/spec_dir.rb +50 -0
- data/lib/js_test_core/resources/specs/spec_file.rb +17 -0
- data/lib/js_test_core/resources/suite.rb +24 -0
- data/lib/js_test_core/resources/suite_finish.rb +19 -0
- data/lib/js_test_core/resources/web_root.rb +54 -0
- data/lib/js_test_core/selenium.rb +2 -0
- data/lib/js_test_core/selenium/selenium_driver.rb +5 -0
- data/lib/js_test_core/server.rb +111 -0
- data/lib/js_test_core/thin.rb +3 -0
- data/lib/js_test_core/thin/backends/js_test_core_server.rb +9 -0
- data/lib/js_test_core/thin/js_test_core_connection.rb +42 -0
- data/spec/spec_suite.rb +2 -0
- data/spec/unit/js_spec/client_spec.rb +137 -0
- data/spec/unit/js_spec/rails_server_spec.rb +45 -0
- data/spec/unit/js_spec/resources/dir_spec.rb +42 -0
- data/spec/unit/js_spec/resources/file_spec.rb +88 -0
- data/spec/unit/js_spec/resources/runner_spec.rb +24 -0
- data/spec/unit/js_spec/resources/runners/firefox_runner_spec.rb +161 -0
- data/spec/unit/js_spec/resources/specs/spec_dir_spec.rb +79 -0
- data/spec/unit/js_spec/resources/specs/spec_file_spec.rb +42 -0
- data/spec/unit/js_spec/resources/suite_finish_spec.rb +94 -0
- data/spec/unit/js_spec/resources/suite_spec.rb +44 -0
- data/spec/unit/js_spec/resources/web_root_spec.rb +67 -0
- data/spec/unit/js_spec/server_spec.rb +131 -0
- data/spec/unit/thin/js_test_core_connection_spec.rb +92 -0
- data/spec/unit/unit_spec_helper.rb +125 -0
- data/spec/unit_suite.rb +10 -0
- metadata +113 -0
data/CHANGES
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
0.1.1
|
2
|
+
- SuiteFinish#post immediately closes the connection
|
3
|
+
|
4
|
+
0.1.0
|
5
|
+
- Initial Release extracted from JsTestCore.
|
6
|
+
- Added JsTestCore::WebRoot.dispatch_specs and .dispatch_strategy
|
7
|
+
- Extracted JsTestCore::Resources::Specs::SpecDir
|
8
|
+
- Extracted JsTestCore::Resources::Specs::SpecFile
|
9
|
+
- No longer depending on Guid. The child libraries are responsible for obtaining the Selenium session_id from the client by using top.runOptions.getSessionId().
|
data/README
ADDED
@@ -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.
|
data/Rakefile
ADDED
@@ -0,0 +1,72 @@
|
|
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.1.1"
|
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
|
+
end
|
62
|
+
|
63
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
64
|
+
pkg.need_zip = true
|
65
|
+
pkg.need_tar = true
|
66
|
+
end
|
67
|
+
|
68
|
+
def tag_release
|
69
|
+
dashed_version = PKG_VERSION.gsub('.', '-')
|
70
|
+
svn_user = "#{ENV["SVN_USER"]}@" || ""
|
71
|
+
`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}'`
|
72
|
+
end
|
data/lib/js_test_core.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
gem "thin", ">=0.8.0"
|
3
|
+
|
4
|
+
require "thin"
|
5
|
+
require "fileutils"
|
6
|
+
require "tmpdir"
|
7
|
+
require "timeout"
|
8
|
+
require "cgi"
|
9
|
+
require "net/http"
|
10
|
+
require "selenium"
|
11
|
+
require "optparse"
|
12
|
+
|
13
|
+
dir = File.dirname(__FILE__)
|
14
|
+
require "#{dir}/js_test_core/thin"
|
15
|
+
require "#{dir}/js_test_core/rack"
|
16
|
+
require "#{dir}/js_test_core/resources"
|
17
|
+
require "#{dir}/js_test_core/selenium"
|
18
|
+
|
19
|
+
require "#{dir}/js_test_core/client"
|
20
|
+
require "#{dir}/js_test_core/server"
|
21
|
+
require "#{dir}/js_test_core/rails_server"
|
22
|
+
|
23
|
+
module JsTestCore
|
24
|
+
DEFAULT_HOST = "0.0.0.0"
|
25
|
+
DEFAULT_PORT = 8080
|
26
|
+
|
27
|
+
class << self
|
28
|
+
attr_accessor :core_path
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module JsTestCore
|
2
|
+
class Client
|
3
|
+
class << self
|
4
|
+
def run(params={})
|
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
|
12
|
+
|
13
|
+
body = response.body
|
14
|
+
if body.empty?
|
15
|
+
puts "SUCCESS"
|
16
|
+
return true
|
17
|
+
else
|
18
|
+
puts "FAILURE"
|
19
|
+
puts body
|
20
|
+
return false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def run_argv(argv)
|
25
|
+
params = {}
|
26
|
+
parser = OptionParser.new do |o|
|
27
|
+
o.banner = "JsTestCore Runner"
|
28
|
+
o.banner << "\nUsage: #{$0} [options] [-- untouched arguments]"
|
29
|
+
|
30
|
+
o.on
|
31
|
+
o.on('-h', '--selenium_host=SELENIUM_HOST', "The host name of the Selenium Server relative to where this file is executed") do |host|
|
32
|
+
params[:selenium_host] = host
|
33
|
+
end
|
34
|
+
|
35
|
+
o.on('-p', '--selenium_port=SELENIUM_PORT', "The port of the Selenium Server relative to where this file is executed") do |port|
|
36
|
+
params[:selenium_port] = port
|
37
|
+
end
|
38
|
+
|
39
|
+
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|
|
40
|
+
params[:spec_url] = spec_url
|
41
|
+
end
|
42
|
+
|
43
|
+
o.on_tail
|
44
|
+
end
|
45
|
+
parser.order!(argv)
|
46
|
+
run params
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
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,10 @@
|
|
1
|
+
dir = File.dirname(__FILE__)
|
2
|
+
require "#{dir}/resources/runners"
|
3
|
+
require "#{dir}/resources/runners/firefox_runner"
|
4
|
+
require "#{dir}/resources/file"
|
5
|
+
require "#{dir}/resources/dir"
|
6
|
+
require "#{dir}/resources/specs/spec_file"
|
7
|
+
require "#{dir}/resources/specs/spec_dir"
|
8
|
+
require "#{dir}/resources/web_root"
|
9
|
+
require "#{dir}/resources/suite"
|
10
|
+
require "#{dir}/resources/suite_finish"
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module JsTestCore
|
2
|
+
module Resources
|
3
|
+
class Dir < File
|
4
|
+
def locate(name)
|
5
|
+
if file = file(name)
|
6
|
+
file
|
7
|
+
elsif subdir = subdir(name)
|
8
|
+
subdir
|
9
|
+
else
|
10
|
+
raise "No file or directory found at #{relative_path}/#{name}."
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def get(request, response)
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
def glob(pattern)
|
19
|
+
expanded_pattern = absolute_path + pattern
|
20
|
+
::Dir.glob(expanded_pattern).map do |absolute_globbed_path|
|
21
|
+
relative_globbed_path = absolute_globbed_path.gsub(absolute_path, relative_path)
|
22
|
+
File.new(absolute_globbed_path, relative_globbed_path)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
def determine_child_paths(name)
|
28
|
+
absolute_child_path = "#{absolute_path}/#{name}"
|
29
|
+
relative_child_path = "#{relative_path}/#{name}"
|
30
|
+
[absolute_child_path, relative_child_path]
|
31
|
+
end
|
32
|
+
|
33
|
+
def file(name)
|
34
|
+
absolute_file_path, relative_file_path = determine_child_paths(name)
|
35
|
+
if ::File.exists?(absolute_file_path) && !::File.directory?(absolute_file_path)
|
36
|
+
Resources::File.new(absolute_file_path, relative_file_path)
|
37
|
+
else
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def subdir(name)
|
43
|
+
absolute_dir_path, relative_dir_path = determine_child_paths(name)
|
44
|
+
if ::File.directory?(absolute_dir_path)
|
45
|
+
Resources::Dir.new(absolute_dir_path, relative_dir_path)
|
46
|
+
else
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module JsTestCore
|
2
|
+
module Resources
|
3
|
+
class File
|
4
|
+
MIME_TYPES = {
|
5
|
+
'.js' => 'text/javascript',
|
6
|
+
'.css' => 'text/css',
|
7
|
+
'.png' => 'image/png',
|
8
|
+
'.jpg' => 'image/jpeg',
|
9
|
+
'.jpeg' => 'image/jpeg',
|
10
|
+
'.gif' => 'image/gif',
|
11
|
+
}
|
12
|
+
|
13
|
+
attr_reader :absolute_path, :relative_path
|
14
|
+
|
15
|
+
def initialize(absolute_path, relative_path)
|
16
|
+
@absolute_path = absolute_path
|
17
|
+
@relative_path = relative_path
|
18
|
+
end
|
19
|
+
|
20
|
+
def get(request, response)
|
21
|
+
extension = ::File.extname(absolute_path)
|
22
|
+
response.headers['Content-Type'] = MIME_TYPES[extension] || 'text/html'
|
23
|
+
response.body = ::File.read(absolute_path)
|
24
|
+
end
|
25
|
+
|
26
|
+
def ==(other)
|
27
|
+
return false unless other.class == self.class
|
28
|
+
absolute_path == other.absolute_path && relative_path == other.relative_path
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module JsTestCore
|
2
|
+
module Resources
|
3
|
+
class Runners
|
4
|
+
class FirefoxRunner
|
5
|
+
class << self
|
6
|
+
def resume(suite_id, text)
|
7
|
+
runner = instances.delete(suite_id)
|
8
|
+
runner.finalize(text)
|
9
|
+
end
|
10
|
+
|
11
|
+
def register_instance(runner)
|
12
|
+
instances[runner.suite_id] = runner
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
def instances
|
17
|
+
@instances ||= {}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
include FileUtils
|
22
|
+
attr_reader :profile_dir, :connection, :driver, :response
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
profile_base = "#{::Dir.tmpdir}/js_test_core/firefox"
|
26
|
+
mkdir_p profile_base
|
27
|
+
@profile_dir = "#{profile_base}/#{Time.now.to_i}"
|
28
|
+
@connection = Server.connection
|
29
|
+
end
|
30
|
+
|
31
|
+
def post(request, response)
|
32
|
+
@response = response
|
33
|
+
|
34
|
+
spec_url = (request && request['spec_url']) ? request['spec_url'] : spec_suite_url
|
35
|
+
parsed_spec_url = URI.parse(spec_url)
|
36
|
+
selenium_port = (request['selenium_port'] || 4444).to_i
|
37
|
+
@driver = Selenium::SeleniumDriver.new(
|
38
|
+
request['selenium_host'] || 'localhost',
|
39
|
+
selenium_port,
|
40
|
+
'*firefox',
|
41
|
+
"#{parsed_spec_url.scheme}://#{parsed_spec_url.host}:#{parsed_spec_url.port}"
|
42
|
+
)
|
43
|
+
begin
|
44
|
+
driver.start
|
45
|
+
rescue Errno::ECONNREFUSED => e
|
46
|
+
raise Errno::ECONNREFUSED, "Cannot connect to Selenium Server on port #{selenium_port}. To start the selenium server, run `selenium`."
|
47
|
+
end
|
48
|
+
Thread.start do
|
49
|
+
driver.open(spec_url)
|
50
|
+
end
|
51
|
+
response.status = 200
|
52
|
+
FirefoxRunner.register_instance self
|
53
|
+
end
|
54
|
+
|
55
|
+
def finalize(text)
|
56
|
+
driver.stop
|
57
|
+
response.body = text
|
58
|
+
connection.send_body(response)
|
59
|
+
end
|
60
|
+
|
61
|
+
def suite_id
|
62
|
+
driver.session_id
|
63
|
+
end
|
64
|
+
|
65
|
+
protected
|
66
|
+
|
67
|
+
def spec_suite_url
|
68
|
+
"#{Server.root_url}/specs"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module JsTestCore
|
2
|
+
module Resources
|
3
|
+
module Specs
|
4
|
+
class SpecDirSuperclass < ::JsTestCore::Resources::Dir
|
5
|
+
def get(request, response)
|
6
|
+
raise NotImplementedError, "#{self.class}#get needs to be implemented"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class SpecDir < SpecDirSuperclass
|
11
|
+
def spec_files
|
12
|
+
glob("/**/*_spec.js")
|
13
|
+
end
|
14
|
+
|
15
|
+
def locate(name)
|
16
|
+
if file = file(name)
|
17
|
+
file
|
18
|
+
elsif subdir = subdir(name)
|
19
|
+
subdir
|
20
|
+
elsif spec_file = spec_file(name)
|
21
|
+
spec_file
|
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_dir_path, relative_dir_path = determine_child_paths(name)
|
32
|
+
if ::File.directory?(absolute_dir_path)
|
33
|
+
SpecDir.new(absolute_dir_path, relative_dir_path)
|
34
|
+
else
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def spec_file(name)
|
40
|
+
absolute_file_path, relative_file_path = determine_child_paths("#{name}.js")
|
41
|
+
if ::File.exists?(absolute_file_path) && !::File.directory?(absolute_file_path)
|
42
|
+
SpecFile.new(absolute_file_path, relative_file_path)
|
43
|
+
else
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|