steam 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/MIT-LICENSE +21 -0
  2. data/README.textile +91 -0
  3. data/Rakefile +23 -0
  4. data/TODO +7 -0
  5. data/lib/core_ext/ruby/array/flatten_once.rb +9 -0
  6. data/lib/core_ext/ruby/hash/except.rb +11 -0
  7. data/lib/core_ext/ruby/hash/slice.rb +14 -0
  8. data/lib/core_ext/ruby/kernel/silence_warnings.rb +8 -0
  9. data/lib/core_ext/ruby/process/daemon.rb +23 -0
  10. data/lib/core_ext/ruby/string/camelize.rb +5 -0
  11. data/lib/core_ext/ruby/string/underscore.rb +5 -0
  12. data/lib/steam.rb +43 -0
  13. data/lib/steam/browser.rb +24 -0
  14. data/lib/steam/browser/html_unit.rb +87 -0
  15. data/lib/steam/browser/html_unit/actions.rb +176 -0
  16. data/lib/steam/browser/html_unit/client.rb +74 -0
  17. data/lib/steam/browser/html_unit/connection.rb +79 -0
  18. data/lib/steam/browser/html_unit/drb.rb +45 -0
  19. data/lib/steam/browser/html_unit/matchers.rb +57 -0
  20. data/lib/steam/browser/html_unit/page.rb +51 -0
  21. data/lib/steam/browser/html_unit/web_response.rb +116 -0
  22. data/lib/steam/connection.rb +9 -0
  23. data/lib/steam/connection/mock.rb +57 -0
  24. data/lib/steam/connection/net_http.rb +42 -0
  25. data/lib/steam/connection/open_uri.rb +24 -0
  26. data/lib/steam/connection/rails.rb +20 -0
  27. data/lib/steam/connection/static.rb +33 -0
  28. data/lib/steam/java.rb +74 -0
  29. data/lib/steam/process.rb +53 -0
  30. data/lib/steam/request.rb +49 -0
  31. data/lib/steam/response.rb +13 -0
  32. data/lib/steam/session.rb +30 -0
  33. data/lib/steam/session/rails.rb +33 -0
  34. data/lib/steam/version.rb +3 -0
  35. data/test/all.rb +3 -0
  36. data/test/browser/html_unit/actions_test.rb +183 -0
  37. data/test/browser/html_unit/javascript_test.rb +60 -0
  38. data/test/browser/html_unit/rails_actions_test.rb +151 -0
  39. data/test/browser/html_unit_test.rb +97 -0
  40. data/test/connection/cascade_test.rb +42 -0
  41. data/test/connection/mock_test.rb +58 -0
  42. data/test/connection/rails_test.rb +16 -0
  43. data/test/connection/static_test.rb +14 -0
  44. data/test/fixtures/html_fakes.rb +191 -0
  45. data/test/java_test.rb +29 -0
  46. data/test/playground/connection.rb +19 -0
  47. data/test/playground/dragdrop_behavior.rb +60 -0
  48. data/test/playground/drb.rb +55 -0
  49. data/test/playground/java_signature.rb +22 -0
  50. data/test/playground/nokogiri.rb +15 -0
  51. data/test/playground/put_headers.rb +83 -0
  52. data/test/playground/rack.rb +11 -0
  53. data/test/playground/rjb_bind.rb +42 -0
  54. data/test/playground/stack_level_problem.rb +129 -0
  55. data/test/playground/thread_problem.rb +57 -0
  56. data/test/playground/web_response_data.rb +21 -0
  57. data/test/playground/webrat.rb +48 -0
  58. data/test/playground/xhr_accept_headers.rb +61 -0
  59. data/test/process_test.rb +55 -0
  60. data/test/session_test.rb +15 -0
  61. data/test/test_helper.rb +56 -0
  62. metadata +135 -0
@@ -0,0 +1,42 @@
1
+ # This connection proxies requests using open-uri.
2
+ #
3
+
4
+ require 'net/http'
5
+ require 'uri'
6
+
7
+ Net::HTTPResponse.class_eval do
8
+ attr_reader :code # crap
9
+ end
10
+
11
+ module Steam
12
+ module Connection
13
+ class NetHttp
14
+ def call(env)
15
+ request = Rack::Request.new(env)
16
+ response = send(request.request_method.downcase, request)
17
+ response = follow_redirect(response) if response.status.to_s[0, 1] == '3'
18
+ response.to_a
19
+ end
20
+
21
+ def get(request)
22
+ url = URI.parse(request.url)
23
+ response = Net::HTTP.start(url.host, url.port) { |http| http.get(url.path) }
24
+ Rack::Response.new(response.body, response.code, response.to_hash)
25
+ end
26
+
27
+ def post(request)
28
+ url = URI.parse(request.url)
29
+ params = Rack::Utils.parse_query(Rack::Utils.build_nested_query(request.params))
30
+ response = Net::HTTP.post_form(url, params)
31
+ Rack::Response.new(response.body, response.code, response.to_hash)
32
+ end
33
+
34
+ def follow_redirect(response)
35
+ location = response.headers['location'].join
36
+ env = Steam::Request.env_for(location)
37
+ request = Rack::Request.new(env)
38
+ get(request)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,24 @@
1
+ # This connection proxies requests using open-uri.
2
+ #
3
+
4
+ require 'open-uri'
5
+
6
+ module Steam
7
+ module Connection
8
+ class OpenUri
9
+ def call(env)
10
+ request = Rack::Request.new(env)
11
+ Rack::Response.new(*open(request.url)).to_a
12
+ end
13
+
14
+ def open(uri)
15
+ headers = {}
16
+ body = Kernel.open(uri) { |f| headers = f.meta; f.read }
17
+ [body, 200, headers]
18
+ rescue OpenURI::HTTPError => e
19
+ status = e.message.split(' ').first
20
+ ['', status, headers]
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,20 @@
1
+ module Steam
2
+ module Connection
3
+ class Rails
4
+ def initialize
5
+ @app = ActionController::Dispatcher.new
6
+ end
7
+
8
+ def call(env)
9
+ status, headers, response = @app.call(env)
10
+ Rack::Response.new(response_body(response), status, headers).to_a
11
+ end
12
+
13
+ def response_body(response)
14
+ body = ''
15
+ response.each { |part| body << part } # yuck
16
+ body
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,33 @@
1
+ require 'rack/response'
2
+
3
+ module Steam
4
+ module Connection
5
+ class Static < Rack::Static
6
+ def initialize(*args)
7
+ options = args.last.is_a?(Hash) ? args.pop : {}
8
+ options[:urls] ||= %w(/images /javascripts /stylesheets)
9
+ app = args.pop
10
+ super(app || lambda { response_404 }, options)
11
+ end
12
+
13
+ def call(env)
14
+ status, headers, response = super
15
+
16
+ response = case response
17
+ when Rack::File
18
+ Rack::Response.new(::File.read(response.path), status, headers).to_a
19
+ when Array
20
+ Rack::Response.new(response.first, status, headers).to_a
21
+ else
22
+ [status, headers, response]
23
+ end
24
+
25
+ response
26
+ end
27
+
28
+ def response_404
29
+ Rack::Response.new('', 404).to_a
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,74 @@
1
+ require 'rjb'
2
+ require 'core_ext/ruby/string/camelize'
3
+ require 'core_ext/ruby/string/underscore'
4
+
5
+ module Steam
6
+ module Java
7
+
8
+ # TODO replace this with http://github.com/sklemm/jrequire/blob/master/lib/java.rb
9
+
10
+ module AutoDefine
11
+ def const_set_nested(full_name, const)
12
+ name = pop_name(full_name)
13
+ if full_name.empty? && !const_defined?(name)
14
+ const_set(name, const)
15
+ else
16
+ const_set(name, Module.new { extend AutoDefine }) unless const_defined?(name)
17
+ const_get(name).const_set_nested(full_name, const) unless full_name.empty?
18
+ end
19
+ end
20
+
21
+ def pop_name(string)
22
+ name, *rest = string.split('::')
23
+ string.replace(rest.join('::'))
24
+ name
25
+ end
26
+ end
27
+ extend AutoDefine
28
+
29
+ class << self
30
+ def const_missing(name)
31
+ return init && const_get(name) unless @initialized
32
+ super
33
+ end
34
+
35
+ def import(signature, name = nil)
36
+ init unless @initialized
37
+ name = path_to_const_name(signature)
38
+ const_set_nested(name, Rjb::import(signature))
39
+ end
40
+
41
+ def path_to_const_name(path)
42
+ path.split('.').map { |token| token.underscore.camelize }.join('::').gsub('Java::', '')
43
+ end
44
+
45
+ def init
46
+ @initialized = true
47
+
48
+ import('java.net.URL')
49
+ import('java.lang.System')
50
+ import('java.util.Arrays')
51
+ import('java.util.ArrayList')
52
+ import('java.util.logging.Logger')
53
+ import('java.util.logging.Level')
54
+ end
55
+
56
+ def load_from(path)
57
+ paths = Dir["#{Steam.config[:html_unit][:java_path]}/*.jar"]
58
+ load(paths.join(':')) unless paths.empty?
59
+ end
60
+
61
+ def load(paths)
62
+ Rjb::load(paths, Steam.config[:java_load_params].to_a)
63
+ end
64
+
65
+ def logger(classifier)
66
+ Java::Util::Logging::Logger.getLogger(classifier)
67
+ end
68
+
69
+ def log_level(name)
70
+ Java::Util::Logging::Level.send(name.to_s.upcase)
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,53 @@
1
+ module Steam
2
+ class Process
3
+ attr_reader :pid, :path
4
+
5
+ def initialize(path = '/tmp')
6
+ @path = path
7
+ recall_pid
8
+ end
9
+
10
+ def pid?
11
+ !!pid
12
+ end
13
+
14
+ def running?
15
+ pid? && !!::Process.getpgid(pid)
16
+ rescue Errno::ESRCH
17
+ false
18
+ end
19
+
20
+ def fork(options)
21
+ @pid = Kernel.fork { yield }
22
+ write_pid
23
+ # Process.detach(pid) ?
24
+ at_exit { kill } unless options[:keep_alive]
25
+ end
26
+
27
+ def kill(signal = 'TERM')
28
+ ::Process.kill(Signal.list[signal], pid)
29
+ delete_pid
30
+ true
31
+ end
32
+
33
+ protected
34
+
35
+ def recall_pid
36
+ @pid = ::File.read(filename).to_i rescue nil
37
+ delete_pid if pid? && !running?
38
+ end
39
+
40
+ def write_pid
41
+ File.open(filename, 'w') { |f| f.write(pid) }
42
+ end
43
+
44
+ def delete_pid
45
+ File.delete(filename) rescue Errno::ENOENT
46
+ @pid = nil
47
+ end
48
+
49
+ def filename
50
+ "#{path}/steam.pid"
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,49 @@
1
+ # Extends a Rack::Request with convenience methods
2
+
3
+ require 'uri'
4
+
5
+ module Steam
6
+ class Request < Rack::Request
7
+ attr_accessor :method, :headers
8
+
9
+ DEFAULT_ENV = {
10
+ "rack.version" => [0, 1],
11
+ "rack.input" => StringIO.new,
12
+ "rack.errors" => StringIO.new,
13
+ "rack.multithread" => true,
14
+ "rack.multiprocess" => true,
15
+ "rack.run_once" => false,
16
+ }
17
+
18
+ class << self
19
+ def env_for(uri = '', opts = {})
20
+ env = DEFAULT_ENV.dup
21
+ uri = URI.parse(uri)
22
+ input = opts[:input] || ''
23
+
24
+ env.merge!(
25
+ 'REQUEST_METHOD' => opts[:method] || 'GET',
26
+ 'SERVER_NAME' => Steam.config[:server_name],
27
+ 'SERVER_PORT' => Steam.config[:server_port],
28
+ 'QUERY_STRING' => uri.query.to_s,
29
+ 'PATH_INFO' => (!uri.path || uri.path.empty?) ? '/' : uri.path,
30
+ 'rack.url_scheme' => Steam.config[:url_scheme],
31
+ 'SCRIPT_NAME' => opts[:script_name] || '',
32
+ 'rack.errors' => StringIO.new,
33
+ 'rack.input' => input.is_a?(String) ? StringIO.new(input) : input,
34
+ 'CONTENT_LENGTH' => env['rack.input'].length.to_s,
35
+ 'rack.test.scheme' => uri.scheme || 'http',
36
+ 'rack.test.host' => uri.host || 'www.example.com',
37
+ 'rack.test.port' => uri.port,
38
+ 'rack.test.cache_classes' => true
39
+ )
40
+ env
41
+ end
42
+ end
43
+
44
+ def initialize(method, uri, opts = {})
45
+ env = self.class.env_for(uri, opts.merge(:method => method))
46
+ super(env)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,13 @@
1
+ # Not sure about this. The only reason why this class is needed is that
2
+ # Rack::Response#body returns an Array of body parts while in Rails integration
3
+ # tests response.body returns a String.
4
+
5
+ require 'uri'
6
+
7
+ module Steam
8
+ class Response < Rack::Response
9
+ def body
10
+ super.join
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,30 @@
1
+ # Conceptually a session is something different than a browser. E.g. a (test-)
2
+ # session can be started (setting up test data etc.) and stopped (cleaning
3
+ # stuff up etc.). Webrat and Capybara don't separate these concepts. So let's
4
+ # keep them separate though from the beginning even though we don't implement
5
+ # any such behavior so far.
6
+
7
+ module Steam
8
+ class Session
9
+ autoload :Rails, 'steam/session/rails'
10
+
11
+ attr_accessor :browser
12
+
13
+ def initialize(browser = nil)
14
+ @browser = browser
15
+ end
16
+
17
+ def respond_to?(method)
18
+ browser.respond_to?(method) ? true : super
19
+ end
20
+
21
+ def method_missing(method, *args, &block)
22
+ return browser.send(method, *args, &block) # if browser.respond_to?(method)
23
+ super
24
+ end
25
+
26
+ def select(*args, &block) # because Class implements #select
27
+ browser.select(*args, &block)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,33 @@
1
+ module Steam
2
+ class Session
3
+ class Rails < Session
4
+ include ActionController
5
+
6
+ def initialize(browser = nil)
7
+ super
8
+
9
+ # install the named routes in this session instance.
10
+ klass = class << self; self; end
11
+ ActionController::Routing::Routes.install_helpers(klass)
12
+ klass.module_eval { public *Routing::Routes.named_routes.helpers }
13
+ end
14
+
15
+ # FIXME we only have access to the controller when Steam can run HtmlUnit
16
+ # and the app in one stack
17
+ def controller
18
+ end
19
+
20
+ def url_for(options)
21
+ controller ? controller.url_for(options) : generic_url_rewriter.rewrite(options)
22
+ end
23
+
24
+ protected
25
+
26
+ # FIXME remove ActionController::Request dependency
27
+ def generic_url_rewriter
28
+ env = Request.env_for(Steam.config[:request_url])
29
+ UrlRewriter.new(ActionController::Request.new(env), {})
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ module Steam
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,3 @@
1
+ Dir[File.dirname(__FILE__) + '/**/*_test.rb'].each do |filename|
2
+ require filename unless filename.include?('_locator')
3
+ end
@@ -0,0 +1,183 @@
1
+ require File.expand_path('../../../test_helper', __FILE__)
2
+ require 'fixtures/html_fakes'
3
+
4
+ class HtmlUnitActionsTest < Test::Unit::TestCase
5
+ include Steam, HtmlFakes
6
+
7
+ def setup
8
+ @app = Steam::Connection::Mock.new
9
+ static = Steam::Connection::Static.new(:root => FIXTURES_PATH)
10
+ @browser = Steam::Browser::HtmlUnit.new(Rack::Cascade.new([static, @app]))
11
+ end
12
+
13
+ test "click_on clicks on an element" do
14
+ perform :get, 'http://localhost:3000/', html
15
+
16
+ assert_response_contains('LINK') do
17
+ @app.mock :get, 'http://localhost:3000/link', 'LINK'
18
+ @browser.click_on('link')
19
+ end
20
+ end
21
+
22
+ test "click_on clicks a button" do
23
+ perform :get, 'http://localhost:3000/', html(:fields => :text)
24
+
25
+ assert_response_contains('FORM') do
26
+ @app.mock :get, 'http://localhost:3000/form?field=', 'FORM'
27
+ @browser.click_on(:button, 'button')
28
+ end
29
+ end
30
+
31
+ test "click_link clicks a link" do
32
+ perform :get, 'http://localhost:3000/', html
33
+
34
+ assert_response_contains('LINK') do
35
+ @app.mock :get, 'http://localhost:3000/link', 'LINK'
36
+ @browser.click_link('link')
37
+ end
38
+ end
39
+
40
+ test "click_button clicks a button" do
41
+ perform :get, 'http://localhost:3000/', html(:fields => :text)
42
+
43
+ assert_response_contains('FORM') do
44
+ @app.mock :get, 'http://localhost:3000/form?field=', 'FORM'
45
+ @browser.click_button('button')
46
+ end
47
+ end
48
+
49
+ test "fill_in fills in a text input" do
50
+ perform :get, 'http://localhost:3000/', html(:fields => :text)
51
+
52
+ assert_response_contains('FIELD') do
53
+ @app.mock :get, 'http://localhost:3000/form?field=text', 'FIELD'
54
+ @browser.fill_in('field', :with => 'text')
55
+ @browser.click_button('button')
56
+ end
57
+ end
58
+
59
+ test "fill_in fills in a textarea" do
60
+ perform :get, 'http://localhost:3000/', html(:fields => :textarea)
61
+
62
+ assert_response_contains('TEXTAREA') do
63
+ @app.mock :get, 'http://localhost:3000/form?textarea=text', 'TEXTAREA'
64
+ @browser.fill_in('textarea', :with => 'text')
65
+ @browser.click_button('button')
66
+ end
67
+ end
68
+
69
+ test "check checks a checkbox" do
70
+ perform :get, 'http://localhost:3000/', html(:fields => :checkbox)
71
+
72
+ assert_response_contains('CHECKED') do
73
+ @app.mock :get, 'http://localhost:3000/form?checkbox=1', 'CHECKED'
74
+ @browser.check('checkbox')
75
+ @browser.click_button('button')
76
+ end
77
+ end
78
+
79
+ test "uncheck unchecks a checkbox" do
80
+ perform :get, 'http://localhost:3000/', html(:fields => :checkbox)
81
+
82
+ assert_response_contains('FORM') do
83
+ @app.mock :get, 'http://localhost:3000/form', 'FORM'
84
+ @browser.check('checkbox')
85
+ @browser.uncheck('checkbox')
86
+ @browser.click_button('button')
87
+ end
88
+ end
89
+
90
+ test "choose activates a radio button" do
91
+ perform :get, 'http://localhost:3000/', html(:fields => :radio)
92
+
93
+ assert_response_contains('RADIO') do
94
+ @app.mock :get, 'http://localhost:3000/form?radio=radio', 'RADIO'
95
+ @browser.choose('radio')
96
+ @browser.click_button('button')
97
+ end
98
+ end
99
+
100
+ test "select selects an option from a select box" do
101
+ perform :get, 'http://localhost:3000/', html(:fields => :select)
102
+
103
+ assert_response_contains('SELECT') do
104
+ @app.mock :get, 'http://localhost:3000/form?select=foo', 'SELECT'
105
+ @browser.select('foo', :from => 'select')
106
+ @browser.click_button('button')
107
+ end
108
+ end
109
+
110
+ test "set_hidden_field sets a value to a hidden field" do
111
+ perform :get, 'http://localhost:3000/', html(:fields => :hidden)
112
+
113
+ assert_response_contains('SELECT') do
114
+ @app.mock :get, 'http://localhost:3000/form?hidden=foo', 'SELECT'
115
+ @browser.set_hidden_field('hidden', :to => 'foo')
116
+ @browser.click_button('button')
117
+ end
118
+ end
119
+
120
+ test "attach_file sets a filename to a file field" do
121
+ perform :get, 'http://localhost:3000/', html(:fields => :file)
122
+
123
+ assert_response_contains('FILE') do
124
+ @app.mock :get, 'http://localhost:3000/form?file=rails.png', 'FILE'
125
+ @browser.attach_file('file', "#{TEST_ROOT}/fixtures/rails.png")
126
+ @browser.click_button('button')
127
+ end
128
+ end
129
+
130
+ test "submit_form submits a form" do
131
+ perform :get, 'http://localhost:3000/', html(:fields => :text)
132
+
133
+ assert_response_contains('FORM') do
134
+ @app.mock :get, 'http://localhost:3000/form?field=', 'FORM'
135
+ @browser.submit_form('form')
136
+ end
137
+ end
138
+
139
+ test "drag drags an draggable and drop drops the draggable onto a droppable" do
140
+ perform :get, 'http://localhost:3000/', html(:scripts => [:jquery, :jquery_ui, :drag])
141
+
142
+ @browser.drag('link')
143
+ @browser.drop('form')
144
+ assert_equal 'DROPPED!', @browser.page.getTitleText
145
+ end
146
+
147
+ test "drag_and_drop drags a draggable and drops it onto a droppable" do
148
+ perform :get, 'http://localhost:3000/', html(:scripts => [:jquery, :jquery_ui, :drag])
149
+
150
+ @browser.drag_and_drop('link', :to => 'form')
151
+ assert_equal 'DROPPED!', @browser.page.getTitleText
152
+ end
153
+
154
+ test "hover triggers a mouseOver event on an element" do
155
+ perform :get, 'http://localhost:3000/', html(:scripts => [:jquery, :jquery_ui, :hover])
156
+
157
+ @browser.hover('paragraph')
158
+ assert_equal 'HOVERED!', @browser.page.getTitleText
159
+ end
160
+
161
+ test "focus triggers a focus event on an element" do
162
+ perform :get, 'http://localhost:3000/', html(:fields => :text, :scripts => [:jquery, :jquery_ui, :focus])
163
+
164
+ @browser.focus('field')
165
+ assert_equal 'FOCUSED!', @browser.page.getTitleText
166
+ end
167
+
168
+ test "blur triggers a blur event on an element" do
169
+ perform :get, 'http://localhost:3000/', html(:fields => :text, :scripts => [:jquery, :jquery_ui, :blur])
170
+
171
+ @browser.focus('field')
172
+ @browser.blur('field')
173
+ assert_equal 'BLURRED!', @browser.page.getTitleText
174
+ end
175
+
176
+ test "double_click doubleclicks an element" do
177
+ perform :get, 'http://localhost:3000/', html(:scripts => [:jquery, :jquery_ui, :double_click])
178
+
179
+ @browser.double_click('paragraph')
180
+ assert_equal 'DOUBLE CLICKED!', @browser.page.getTitleText
181
+ end
182
+
183
+ end