js_test_core 0.1.1

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 (41) hide show
  1. data/CHANGES +9 -0
  2. data/README +12 -0
  3. data/Rakefile +72 -0
  4. data/lib/js_test_core.rb +30 -0
  5. data/lib/js_test_core/client.rb +50 -0
  6. data/lib/js_test_core/rack.rb +2 -0
  7. data/lib/js_test_core/rack/commonlogger.rb +5 -0
  8. data/lib/js_test_core/rails_server.rb +22 -0
  9. data/lib/js_test_core/resources.rb +10 -0
  10. data/lib/js_test_core/resources/dir.rb +52 -0
  11. data/lib/js_test_core/resources/file.rb +32 -0
  12. data/lib/js_test_core/resources/runners.rb +15 -0
  13. data/lib/js_test_core/resources/runners/firefox_runner.rb +73 -0
  14. data/lib/js_test_core/resources/specs/spec_dir.rb +50 -0
  15. data/lib/js_test_core/resources/specs/spec_file.rb +17 -0
  16. data/lib/js_test_core/resources/suite.rb +24 -0
  17. data/lib/js_test_core/resources/suite_finish.rb +19 -0
  18. data/lib/js_test_core/resources/web_root.rb +54 -0
  19. data/lib/js_test_core/selenium.rb +2 -0
  20. data/lib/js_test_core/selenium/selenium_driver.rb +5 -0
  21. data/lib/js_test_core/server.rb +111 -0
  22. data/lib/js_test_core/thin.rb +3 -0
  23. data/lib/js_test_core/thin/backends/js_test_core_server.rb +9 -0
  24. data/lib/js_test_core/thin/js_test_core_connection.rb +42 -0
  25. data/spec/spec_suite.rb +2 -0
  26. data/spec/unit/js_spec/client_spec.rb +137 -0
  27. data/spec/unit/js_spec/rails_server_spec.rb +45 -0
  28. data/spec/unit/js_spec/resources/dir_spec.rb +42 -0
  29. data/spec/unit/js_spec/resources/file_spec.rb +88 -0
  30. data/spec/unit/js_spec/resources/runner_spec.rb +24 -0
  31. data/spec/unit/js_spec/resources/runners/firefox_runner_spec.rb +161 -0
  32. data/spec/unit/js_spec/resources/specs/spec_dir_spec.rb +79 -0
  33. data/spec/unit/js_spec/resources/specs/spec_file_spec.rb +42 -0
  34. data/spec/unit/js_spec/resources/suite_finish_spec.rb +94 -0
  35. data/spec/unit/js_spec/resources/suite_spec.rb +44 -0
  36. data/spec/unit/js_spec/resources/web_root_spec.rb +67 -0
  37. data/spec/unit/js_spec/server_spec.rb +131 -0
  38. data/spec/unit/thin/js_test_core_connection_spec.rb +92 -0
  39. data/spec/unit/unit_spec_helper.rb +125 -0
  40. data/spec/unit_suite.rb +10 -0
  41. metadata +113 -0
@@ -0,0 +1,17 @@
1
+ module JsTestCore
2
+ module Resources
3
+ module Specs
4
+ class SpecFileSuperclass < ::JsTestCore::Resources::File
5
+ def get(request, response)
6
+ raise NotImplementedError, "#{self.class}#get needs to be implemented"
7
+ end
8
+ end
9
+
10
+ class SpecFile < SpecFileSuperclass
11
+ def spec_files
12
+ [self]
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ module JsTestCore
2
+ module Resources
3
+ class Suite
4
+ class << self
5
+ def locate(id)
6
+ new id
7
+ end
8
+ end
9
+
10
+ attr_reader :id
11
+ def initialize(id)
12
+ @id = id
13
+ end
14
+
15
+ def locate(name)
16
+ if name == 'finish'
17
+ SuiteFinish.new self
18
+ else
19
+ raise ArgumentError, "Invalid path: #{name}"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ module JsTestCore
2
+ module Resources
3
+ class SuiteFinish
4
+ attr_reader :suite
5
+ def initialize(suite)
6
+ @suite = suite
7
+ end
8
+
9
+ def post(request, response)
10
+ if suite.id == 'user'
11
+ STDOUT.puts request['text']
12
+ else
13
+ Runners::FirefoxRunner.resume(suite.id, request['text'])
14
+ end
15
+ response.headers['Content-Length'] = "0"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,54 @@
1
+ module JsTestCore
2
+ module Resources
3
+ class WebRoot
4
+ LOCATIONS = [
5
+ ['core', lambda do
6
+ Resources::Dir.new(JsTestCore::Server.core_path, "/core")
7
+ end],
8
+ ['implementations', lambda do
9
+ Resources::Dir.new(JsTestCore::Server.implementation_root_path, "/implementations")
10
+ end],
11
+ ['suites', lambda do
12
+ Resources::Suite
13
+ end],
14
+ ['runners', lambda do
15
+ Resources::Runners.new
16
+ end]
17
+ ]
18
+
19
+ class << self
20
+ attr_accessor :dispatch_strategy
21
+ def dispatch_specs
22
+ self.dispatch_strategy = :specs
23
+ end
24
+ end
25
+
26
+ attr_reader :public_path
27
+ def initialize(public_path)
28
+ @public_path = ::File.expand_path(public_path)
29
+ end
30
+
31
+ def locate(name)
32
+ if self.class.dispatch_strategy == :specs && name == 'specs'
33
+ return JsTestCore::Resources::Specs::SpecDir.new(JsTestCore::Server.spec_root_path, "/specs")
34
+ end
35
+
36
+ location, initializer = LOCATIONS.find do |location|
37
+ location.first == name
38
+ end
39
+ if initializer
40
+ initializer.call
41
+ else
42
+ potential_file_in_public_path = "#{public_path}/#{name}"
43
+ if ::File.directory?(potential_file_in_public_path)
44
+ Resources::Dir.new(potential_file_in_public_path, "/#{name}")
45
+ elsif ::File.exists?(potential_file_in_public_path)
46
+ Resources::File.new(potential_file_in_public_path, "/#{name}")
47
+ else
48
+ raise "Invalid path: #{name}"
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,2 @@
1
+ dir = File.dirname(__FILE__)
2
+ require "#{dir}/selenium/selenium_driver"
@@ -0,0 +1,5 @@
1
+ module Selenium
2
+ class SeleniumDriver
3
+ attr_reader :session_id
4
+ end
5
+ end
@@ -0,0 +1,111 @@
1
+ module JsTestCore
2
+ class Server
3
+ class << self
4
+ attr_accessor :instance
5
+
6
+ def run(spec_root_path, implementation_root_path, public_path, server_options = {})
7
+ server_options[:Host] ||= DEFAULT_HOST
8
+ server_options[:Port] ||= DEFAULT_PORT
9
+ @instance = new(spec_root_path, implementation_root_path, public_path, server_options[:Host], server_options[:Port])
10
+ instance.run server_options
11
+ end
12
+
13
+ def spec_root_path; instance.spec_root_path; end
14
+ def implementation_root_path; instance.implementation_root_path; end
15
+ def public_path; instance.public_path; end
16
+ def core_path; instance.core_path; end
17
+ def test_dir_class; instance.test_dir_class; end
18
+ def test_file_class; instance.test_file_class; end
19
+ def connection; instance.connection; end
20
+ def request; instance.request; end
21
+ def response; instance.response; end
22
+ def root_url; instance.root_url; end
23
+ end
24
+
25
+ attr_reader :host, :port, :spec_root_path, :implementation_root_path, :public_path
26
+
27
+ def initialize(spec_root_path, implementation_root_path, public_path, host=DEFAULT_HOST, port=DEFAULT_PORT)
28
+ dir = ::File.dirname(__FILE__)
29
+ @spec_root_path = ::File.expand_path(spec_root_path)
30
+ @implementation_root_path = ::File.expand_path(implementation_root_path)
31
+ @public_path = ::File.expand_path(public_path)
32
+ @host = host
33
+ @port = port
34
+ end
35
+
36
+ def run(options)
37
+ server = ::Thin::Server.new(options[:Host], options[:Port], self)
38
+ server.backend = ::Thin::Backends::JsTestCoreServer.new(options[:Host], options[:Port])
39
+ server.backend.server = server
40
+ server.start!
41
+ end
42
+
43
+ def call(env)
44
+ self.connection = env['js_test_core.connection']
45
+ self.request = Rack::Request.new(env)
46
+ self.response = Rack::Response.new
47
+ method = request.request_method.downcase.to_sym
48
+ get_resource(request).send(method, request, response)
49
+ response.finish
50
+ ensure
51
+ self.connection = nil
52
+ self.request = nil
53
+ self.response = nil
54
+ end
55
+
56
+ def connection
57
+ Thread.current[:connection]
58
+ end
59
+
60
+ def request
61
+ Thread.current[:request]
62
+ end
63
+
64
+ def response
65
+ Thread.current[:response]
66
+ end
67
+
68
+ def root_url
69
+ "http://#{host}:#{port}"
70
+ end
71
+
72
+ def core_path
73
+ JsTestCore.core_path
74
+ end
75
+
76
+ def test_dir_class
77
+ JsTestCore.adapter.test_dir_class
78
+ end
79
+
80
+ def test_file_class
81
+ JsTestCore.adapter.test_file_class
82
+ end
83
+
84
+ protected
85
+ def connection=(connection)
86
+ Thread.current[:connection] = connection
87
+ end
88
+
89
+ def request=(request)
90
+ Thread.current[:request] = request
91
+ end
92
+
93
+ def response=(response)
94
+ Thread.current[:response] = response
95
+ end
96
+
97
+ def path_parts(req)
98
+ request.path_info.split('/').reject { |part| part == "" }
99
+ end
100
+
101
+ def get_resource(request)
102
+ path_parts(request).inject(Resources::WebRoot.new(public_path)) do |resource, child_resource_name|
103
+ resource.locate(child_resource_name)
104
+ end
105
+ rescue Exception => e
106
+ detailed_exception = Exception.new("Error handling path #{request.path_info}\n#{e.message}")
107
+ detailed_exception.set_backtrace(e.backtrace)
108
+ raise detailed_exception
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,3 @@
1
+ dir = File.dirname(__FILE__)
2
+ require "#{dir}/thin/js_test_core_connection"
3
+ require "#{dir}/thin/backends/js_test_core_server"
@@ -0,0 +1,9 @@
1
+ module Thin
2
+ module Backends
3
+ class JsTestCoreServer < TcpServer
4
+ def connect
5
+ @signature = EventMachine.start_server(@host, @port, JsTestCoreConnection, &method(:initialize_connection))
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,42 @@
1
+ module Thin
2
+ class JsTestCoreConnection < Connection
3
+ def process
4
+ # Add client info to the request env
5
+ @request.remote_address = remote_address
6
+
7
+ env = @request.env
8
+ env['js_test_core.connection'] = self
9
+ @response.status, @response.headers, @response.body = @app.call(env)
10
+ send_data @response.head
11
+ if !@response.body.empty? || @response.headers.to_s.include?("Content-Length: 0")
12
+ send_body @response.body
13
+ end
14
+ rescue Exception => e
15
+ handle_error e
16
+ end
17
+
18
+ def send_body(rack_response)
19
+ rack_response.each do |chunk|
20
+ send_data chunk
21
+ end
22
+ # If no more request on that same connection, we close it.
23
+ close_connection_after_writing unless persistent?
24
+ rescue Exception => e
25
+ handle_error e
26
+ ensure
27
+ @request.close rescue nil
28
+ @response.close rescue nil
29
+
30
+ # Prepare the connection for another request if the client
31
+ # supports HTTP pipelining (persistent connection).
32
+ post_init if persistent?
33
+ end
34
+
35
+ def handle_error(error)
36
+ log "!! Unexpected error while processing request: #{error.message}"
37
+ log error.backtrace
38
+ log_error
39
+ close_connection rescue nil
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,2 @@
1
+ dir = File.dirname(__FILE__)
2
+ raise "Failure" unless system(%Q|ruby #{dir}/unit_suite.rb|)
@@ -0,0 +1,137 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../unit_spec_helper")
2
+
3
+ module JsTestCore
4
+ describe Client do
5
+ describe '.run' do
6
+ describe 'when successful' do
7
+ before do
8
+ request = Object.new
9
+ mock(request).post("/runners/firefox", "selenium_host=localhost&selenium_port=4444")
10
+ response = Object.new
11
+ mock(response).body {""}
12
+ mock(Net::HTTP).start(DEFAULT_HOST, DEFAULT_PORT).yields(request) {response}
13
+ stub(Client).puts
14
+ end
15
+
16
+ it "returns true" do
17
+ Client.run.should be_true
18
+ end
19
+
20
+ it "prints 'SUCCESS'" do
21
+ mock(Client).puts("SUCCESS")
22
+ Client.run
23
+ end
24
+ end
25
+
26
+ describe 'when unsuccessful' do
27
+ before do
28
+ request = Object.new
29
+ mock(request).post("/runners/firefox", "selenium_host=localhost&selenium_port=4444")
30
+ response = Object.new
31
+ mock(response).body {"the failure message"}
32
+ mock(Net::HTTP).start(DEFAULT_HOST, DEFAULT_PORT).yields(request) {response}
33
+ stub(Client).puts
34
+ end
35
+
36
+ it "returns false" do
37
+ Client.run.should be_false
38
+ end
39
+
40
+ it "prints 'FAILURE' and the error message(s)" do
41
+ mock(Client).puts("FAILURE")
42
+ mock(Client).puts("the failure message")
43
+ Client.run
44
+ end
45
+ end
46
+
47
+ describe "arguments" do
48
+ attr_reader :request, :response
49
+ before do
50
+ @request = Object.new
51
+ @response = Object.new
52
+ mock(response).body {""}
53
+ mock(Net::HTTP).start(DEFAULT_HOST, DEFAULT_PORT).yields(request) {response}
54
+ stub(Client).puts
55
+ end
56
+
57
+ describe "when passed a custom spec_url" do
58
+ it "passes the spec_url as a post parameter" do
59
+ spec_url = 'http://foobar.com/foo'
60
+ mock(request).post(
61
+ "/runners/firefox",
62
+ "selenium_host=localhost&selenium_port=4444&spec_url=#{CGI.escape(spec_url)}"
63
+ )
64
+ Client.run(:spec_url => spec_url)
65
+ end
66
+ end
67
+
68
+ describe "when passed a custom selenium host" do
69
+ it "passes the selenium_host as a post parameter" do
70
+ selenium_host = 'test-runner'
71
+ mock(request).post(
72
+ "/runners/firefox",
73
+ "selenium_host=test-runner&selenium_port=4444"
74
+ )
75
+ Client.run(:selenium_host => selenium_host)
76
+ end
77
+ end
78
+
79
+ describe "when passed a custom selenium port" do
80
+ it "passes the selenium_port as a post parameter" do
81
+ selenium_port = 5000
82
+ mock(request).post(
83
+ "/runners/firefox",
84
+ "selenium_host=localhost&selenium_port=5000"
85
+ )
86
+ Client.run(:selenium_port => selenium_port)
87
+ end
88
+ end
89
+ end
90
+
91
+ end
92
+
93
+ describe ".run_argv" do
94
+ attr_reader :request, :response
95
+ before do
96
+ @request = Object.new
97
+ @response = Object.new
98
+ mock(response).body {""}
99
+ mock(Net::HTTP).start(DEFAULT_HOST, DEFAULT_PORT).yields(request) {response}
100
+ stub(Client).puts
101
+ end
102
+
103
+ describe "when passed a custom spec_url" do
104
+ it "passes the spec_url as a post parameter" do
105
+ spec_url = 'http://foobar.com/foo'
106
+ mock(request).post(
107
+ "/runners/firefox",
108
+ "selenium_host=localhost&selenium_port=4444&spec_url=#{CGI.escape(spec_url)}"
109
+ )
110
+ Client.run_argv(['--spec_url', spec_url])
111
+ end
112
+ end
113
+
114
+ describe "when passed a custom selenium host" do
115
+ it "passes the selenium_host as a post parameter" do
116
+ selenium_host = 'test-runner'
117
+ mock(request).post(
118
+ "/runners/firefox",
119
+ "selenium_host=test-runner&selenium_port=4444"
120
+ )
121
+ Client.run_argv(['--selenium_host', selenium_host])
122
+ end
123
+ end
124
+
125
+ describe "when passed a custom selenium port" do
126
+ it "passes the selenium_port as a post parameter" do
127
+ selenium_port = 5000
128
+ mock(request).post(
129
+ "/runners/firefox",
130
+ "selenium_host=localhost&selenium_port=5000"
131
+ )
132
+ Client.run_argv(['--selenium_port', selenium_port.to_s])
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,45 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../unit_spec_helper")
2
+
3
+ module JsTestCore
4
+ describe RailsServer do
5
+ it "subclasses Server" do
6
+ RailsServer.superclass.should == Server
7
+ end
8
+
9
+ describe ".run" do
10
+ attr_reader :rails_root
11
+ before do
12
+ @rails_root = "/rails/root"
13
+ Server.instance = nil
14
+ end
15
+
16
+ it "initializes the RailsServer and runs the Thin Handler and sets Server.instance to the RailsServer instance" do
17
+ host = DEFAULT_HOST
18
+ port = DEFAULT_PORT
19
+ server_instance = nil
20
+ mock.proxy(RailsServer).new(
21
+ rails_root,
22
+ host,
23
+ port
24
+ ) do |new_instance|
25
+ server_instance = new_instance
26
+ end
27
+
28
+ mock(EventMachine).run.yields
29
+ mock(EventMachine).start_server(host, port, ::Thin::JsTestCoreConnection)
30
+ RailsServer.run(rails_root)
31
+ Server.instance.should == server_instance
32
+ end
33
+ end
34
+
35
+ describe "#initialize" do
36
+ it "sets the server paths based on the passed in rails root" do
37
+ rails_root = "/rails/root"
38
+ server = RailsServer.new(rails_root)
39
+ server.spec_root_path.should == "#{rails_root}/spec/javascripts"
40
+ server.implementation_root_path.should == "#{rails_root}/public/javascripts"
41
+ server.public_path.should == "#{rails_root}/public"
42
+ end
43
+ end
44
+ end
45
+ end