akephalos-2s 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/MIT_LICENSE +20 -0
  2. data/README.md +56 -0
  3. data/bin/akephalos +87 -0
  4. data/lib/akephalos.rb +19 -0
  5. data/lib/akephalos/capybara.rb +293 -0
  6. data/lib/akephalos/client.rb +111 -0
  7. data/lib/akephalos/client/cookies.rb +73 -0
  8. data/lib/akephalos/client/filter.rb +120 -0
  9. data/lib/akephalos/configuration.rb +49 -0
  10. data/lib/akephalos/console.rb +32 -0
  11. data/lib/akephalos/cucumber.rb +6 -0
  12. data/lib/akephalos/htmlunit.rb +38 -0
  13. data/lib/akephalos/htmlunit/ext/http_method.rb +30 -0
  14. data/lib/akephalos/node.rb +132 -0
  15. data/lib/akephalos/page.rb +113 -0
  16. data/lib/akephalos/remote_client.rb +84 -0
  17. data/lib/akephalos/server.rb +56 -0
  18. data/lib/akephalos/version.rb +3 -0
  19. data/src/htmlunit/apache-mime4j-0.6.jar +0 -0
  20. data/src/htmlunit/commons-codec-1.4.jar +0 -0
  21. data/src/htmlunit/commons-collections-3.2.1.jar +0 -0
  22. data/src/htmlunit/commons-io-1.4.jar +0 -0
  23. data/src/htmlunit/commons-lang-2.4.jar +0 -0
  24. data/src/htmlunit/commons-logging-1.1.1.jar +0 -0
  25. data/src/htmlunit/cssparser-0.9.5.jar +0 -0
  26. data/src/htmlunit/htmlunit-2.8.jar +0 -0
  27. data/src/htmlunit/htmlunit-core-js-2.8.jar +0 -0
  28. data/src/htmlunit/httpclient-4.0.1.jar +0 -0
  29. data/src/htmlunit/httpcore-4.0.1.jar +0 -0
  30. data/src/htmlunit/httpmime-4.0.1.jar +0 -0
  31. data/src/htmlunit/nekohtml-1.9.14.jar +0 -0
  32. data/src/htmlunit/sac-1.3.jar +0 -0
  33. data/src/htmlunit/serializer-2.7.1.jar +0 -0
  34. data/src/htmlunit/xalan-2.7.1.jar +0 -0
  35. data/src/htmlunit/xercesImpl-2.9.1.jar +0 -0
  36. data/src/htmlunit/xml-apis-1.3.04.jar +0 -0
  37. metadata +164 -0
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Bernerd Schaefer
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,56 @@
1
+ # Akephalos
2
+ Akephalos is a full-stack headless browser for integration testing with
3
+ Capybara. It is built on top of [HtmlUnit](http://htmlunit.sourceforge.net),
4
+ a GUI-less browser for the Java platform, but can be run on both JRuby and
5
+ MRI with no need for JRuby to be installed on the system.
6
+
7
+ ## Installation
8
+
9
+ gem install akephalos
10
+
11
+ ## Setup
12
+
13
+ Configuring akephalos is as simple as requiring it and setting Capybara's
14
+ javascript driver:
15
+
16
+ require 'akephalos'
17
+ Capybara.javascript_driver = :akephalos
18
+
19
+ ## Basic Usage
20
+
21
+ Akephalos provides a driver for Capybara, so using Akephalos is no
22
+ different than using Selenium or Rack::Test. For a full usage guide, check
23
+ out Capybara's DSL [documentation](http://github.com/jnicklas/capybara). It
24
+ makes no assumptions about the testing framework being used, and works with
25
+ RSpec, Cucumber, and Test::Unit.
26
+
27
+ Here's some sample RSpec code:
28
+
29
+ describe "Home Page" do
30
+ before { visit "/" }
31
+ context "searching" do
32
+ before do
33
+ fill_in "Search", :with => "akephalos"
34
+ click_button "Go"
35
+ end
36
+ it "returns results" { page.should have_css("#results") }
37
+ it "includes the search term" { page.should have_content("akephalos") }
38
+ end
39
+ end
40
+
41
+ ## More
42
+
43
+ * [bin/akephalos](http://bernerdschaefer.github.com/akephalos/akephalos-bin.html)
44
+ allows you to start an interactive shell or DRb server, as well as perform
45
+ other maintenance features.
46
+
47
+ * [Filters](http://bernerdschaefer.github.com/akephalos/filters.html) allows
48
+ you to declare mock responses for external resources and services requested
49
+ by the browser.
50
+
51
+ ## Resources
52
+
53
+ * [API Documentation](http://bernerdschaefer.github.com/akephalos/api)
54
+ * [Source code](http://github.com/bernerdschaefer/akephalos) and
55
+ [issues](http://github.com/bernerdschaefer/akephalos/issues) are hosted on
56
+ github.
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env ruby
2
+ # vim:set filetype=ruby:
3
+
4
+ require "pathname"
5
+ require "optparse"
6
+
7
+ options = { :interactive => false }
8
+
9
+ parser = OptionParser.new do |opts|
10
+ opts.banner = "Usage: akephalos [--interactive, --use-htmlunit-snapshot] | [--server] <port>"
11
+ opts.on("-s", "--server", "Run in server mode (default)")
12
+ opts.on("-i", "--interactive", "Run in interactive mode") { options[:interactive] = true }
13
+ opts.on("--use-htmlunit-snapshot", "Use the snapshot of htmlunit") { options[:use_htmlunit_snapshot] = true }
14
+
15
+ opts.on_tail("-h", "--help", "Show this message") { puts opts; exit }
16
+ end
17
+ parser.parse!
18
+
19
+ root = Pathname(__FILE__).expand_path.dirname.parent
20
+ lib = root + 'lib'
21
+ src = root + 'src'
22
+
23
+ case
24
+ when options[:use_htmlunit_snapshot]
25
+ require "fileutils"
26
+
27
+ FileUtils.mkdir_p("vendor/htmlunit")
28
+ Dir["vendor/htmlunit/*.jar"].each { |jar| File.unlink(jar) }
29
+
30
+ Dir.chdir("vendor") do
31
+ $stdout.print "Downloading latest snapshot... "
32
+ $stdout.flush
33
+ %x[curl -O http://build.canoo.com/htmlunit/artifacts/htmlunit-2.9-SNAPSHOT-with-dependencies.zip &> /dev/null]
34
+ puts "done"
35
+
36
+ $stdout.print "Extracting dependencies... "
37
+ $stdout.flush
38
+ %x[unzip -j -d htmlunit htmlunit-2.9-SNAPSHOT-with-dependencies.zip htmlunit-2.9-SNAPSHOT/lib htmlunit-2.9-SNAPSHOT/lib/* &> /dev/null]
39
+ puts "done"
40
+
41
+ File.unlink "htmlunit-2.9-SNAPSHOT-with-dependencies.zip"
42
+ end
43
+
44
+ $stdout.puts "="*40
45
+ $stdout.puts "The latest HtmlUnit snapshot has been extracted to vendor/htmlunit!"
46
+ when options[:interactive]
47
+ $:.unshift('vendor', lib, src)
48
+ require 'rubygems'
49
+ require 'akephalos'
50
+ require 'akephalos/console'
51
+ Akephalos::Console.start
52
+ else
53
+ unless port = ARGV[0]
54
+ puts parser.help
55
+ exit
56
+ end
57
+
58
+ if RUBY_PLATFORM == "java"
59
+ $:.unshift("vendor", lib, src)
60
+ require 'akephalos/server'
61
+ Akephalos::Server.start!(port)
62
+ else
63
+ require 'rubygems'
64
+ require 'jruby-jars'
65
+
66
+ jruby = JRubyJars.core_jar_path
67
+ jruby_stdlib = JRubyJars.stdlib_jar_path
68
+
69
+ java_args = [
70
+ "-Xmx128M",
71
+ "-cp", [JRubyJars.core_jar_path, JRubyJars.stdlib_jar_path].join(":"),
72
+ "org.jruby.Main"
73
+ ]
74
+ ruby_args = [
75
+ "-I", "vendor:#{lib}:#{src}",
76
+ "-r", "akephalos/server",
77
+ "-e", "Akephalos::Server.start!(#{port.inspect})"
78
+ ]
79
+
80
+ # Bundler sets ENV["RUBYOPT"] to automatically load bundler/setup.rb, but
81
+ # since the akephalos server doesn't have any gem dependencies and is
82
+ # always executed with the same context, we clear RUBYOPT before running
83
+ # exec.
84
+ ENV["RUBYOPT"] = ""
85
+ exec("java", *(java_args + ruby_args))
86
+ end
87
+ end
@@ -0,0 +1,19 @@
1
+ # **Akephalos** is a cross-platform Ruby interface for *HtmlUnit*, a headless
2
+ # browser for the Java platform.
3
+ #
4
+ # The only requirement is that a Java runtime is available.
5
+ #
6
+ require 'java' if RUBY_PLATFORM == 'java'
7
+ require 'pathname'
8
+
9
+ module Akephalos
10
+ BIN_DIR = Pathname(__FILE__).expand_path.dirname.parent + 'bin'
11
+ end
12
+
13
+ require 'akephalos/client'
14
+ require 'capybara'
15
+ require 'akephalos/capybara'
16
+
17
+ if Object.const_defined? :Cucumber
18
+ require 'akephalos/cucumber'
19
+ end
@@ -0,0 +1,293 @@
1
+ # Driver class exposed to Capybara. It implements Capybara's full driver API,
2
+ # and is the entry point for interaction between the test suites and HtmlUnit.
3
+ #
4
+ # This class and +Capybara::Driver::Akephalos::Node+ are written to run on both
5
+ # MRI and JRuby, and is agnostic whether the Akephalos::Client instance is used
6
+ # directly or over DRb.
7
+ class Capybara::Driver::Akephalos < Capybara::Driver::Base
8
+
9
+ # Akephalos-specific implementation for Capybara's Driver::Node class.
10
+ class Node < Capybara::Driver::Node
11
+
12
+ # @api capybara
13
+ # @return [String] the inner text of the node
14
+ def text
15
+ native.text
16
+ end
17
+
18
+ # @api capybara
19
+ # @param [String] name attribute name
20
+ # @return [String] the attribute value
21
+ def [](name)
22
+ name = name.to_s
23
+ case name
24
+ when 'checked'
25
+ native.checked?
26
+ else
27
+ native[name.to_s]
28
+ end
29
+ end
30
+
31
+ # @api capybara
32
+ # @return [String, Array<String>] the form element's value
33
+ def value
34
+ native.value
35
+ end
36
+
37
+ # Set the form element's value.
38
+ #
39
+ # @api capybara
40
+ # @param [String] value the form element's new value
41
+ def set(value)
42
+ if tag_name == 'textarea'
43
+ native.value = value.to_s
44
+ elsif tag_name == 'input' and type == 'radio'
45
+ click
46
+ elsif tag_name == 'input' and type == 'checkbox'
47
+ if value != self['checked']
48
+ click
49
+ end
50
+ elsif tag_name == 'input'
51
+ native.value = value.to_s
52
+ end
53
+ end
54
+
55
+ # @api capybara
56
+ def select_option
57
+ native.click
58
+ end
59
+
60
+ # Unselect an option from a select box.
61
+ #
62
+ # @api capybara
63
+ def unselect_option
64
+ unless select_node.multiple_select?
65
+ raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box."
66
+ end
67
+
68
+ native.unselect
69
+ end
70
+
71
+ # Click the element.
72
+ def click
73
+ native.click
74
+ end
75
+
76
+ # Drag the element on top of the target element.
77
+ #
78
+ # @api capybara
79
+ # @param [Node] element the target element
80
+ def drag_to(element)
81
+ trigger('mousedown')
82
+ element.trigger('mousemove')
83
+ element.trigger('mouseup')
84
+ end
85
+
86
+ # @api capybara
87
+ # @return [String] the element's tag name
88
+ def tag_name
89
+ native.tag_name
90
+ end
91
+
92
+ # @api capybara
93
+ # @return [true, false] the element's visiblity
94
+ def visible?
95
+ native.visible?
96
+ end
97
+
98
+ # @api capybara
99
+ # @return [String] the XPath to locate the node
100
+ def path
101
+ native.xpath
102
+ end
103
+
104
+ # Trigger an event on the element.
105
+ #
106
+ # @api capybara
107
+ # @param [String] event the event to trigger
108
+ def trigger(event)
109
+ native.fire_event(event.to_s)
110
+ end
111
+
112
+ # @api capybara
113
+ # @param [String] selector XPath query
114
+ # @return [Array<Node>] the matched nodes
115
+ def find(selector)
116
+ nodes = []
117
+ native.find(selector).each { |node| nodes << self.class.new(self, node) }
118
+ nodes
119
+ end
120
+
121
+ protected
122
+
123
+ # @return [true, false] whether the node allows multiple-option selection (if the node is a select).
124
+ def multiple_select?
125
+ tag_name == "select" && native.multiple_select?
126
+ end
127
+
128
+ private
129
+
130
+ # Return all child nodes which match the selector criteria.
131
+ #
132
+ # @api capybara
133
+ # @return [Array<Node>] the matched nodes
134
+ def all_unfiltered(selector)
135
+ nodes = []
136
+ native.find(selector).each { |node| nodes << self.class.new(driver, node) }
137
+ nodes
138
+ end
139
+
140
+ # @return [String] the node's type attribute
141
+ def type
142
+ native[:type]
143
+ end
144
+
145
+ # @return [Node] the select node, if this is an option node
146
+ def select_node
147
+ find('./ancestor::select').first
148
+ end
149
+ end
150
+
151
+ attr_reader :app, :rack_server
152
+
153
+ # @return [Client] an instance of Akephalos::Client
154
+ def self.driver
155
+ @driver ||= Akephalos::Client.new
156
+ end
157
+
158
+ def initialize(app)
159
+ @app = app
160
+ @rack_server = Capybara::Server.new(@app)
161
+ @rack_server.boot if Capybara.run_server
162
+ end
163
+
164
+ # Visit the given path in the browser.
165
+ #
166
+ # @param [String] path relative path to visit
167
+ def visit(path)
168
+ browser.visit(url(path))
169
+ end
170
+
171
+ # @return [String] the page's original source
172
+ def source
173
+ page.source
174
+ end
175
+
176
+ # @return [String] the page's modified source
177
+ def body
178
+ page.modified_source
179
+ end
180
+
181
+ # @return [Hash{String => String}] the page's response headers
182
+ def response_headers
183
+ page.response_headers
184
+ end
185
+
186
+ # @return [Integer] the response's status code
187
+ def status_code
188
+ page.status_code
189
+ end
190
+
191
+ # Execute the given block within the context of a specified frame.
192
+ #
193
+ # @param [String] frame_id the frame's id
194
+ # @raise [Capybara::ElementNotFound] if the frame is not found
195
+ def within_frame(frame_id, &block)
196
+ unless page.within_frame(frame_id, &block)
197
+ raise Capybara::ElementNotFound, "Unable to find frame with id '#{frame_id}'"
198
+ end
199
+ end
200
+
201
+ # Clear all cookie session data.
202
+ # @deprecated This method is deprecated in Capybara's master branch. Use
203
+ # {#reset!} instead.
204
+ def cleanup!
205
+ reset!
206
+ end
207
+
208
+ # Clear all cookie session data.
209
+ def reset!
210
+ cookies.clear
211
+ end
212
+
213
+ # @return [String] the page's current URL
214
+ def current_url
215
+ page.current_url
216
+ end
217
+
218
+ # Search for nodes which match the given XPath selector.
219
+ #
220
+ # @param [String] selector XPath query
221
+ # @return [Array<Node>] the matched nodes
222
+ def find(selector)
223
+ nodes = []
224
+ page.find(selector).each { |node| nodes << Node.new(self, node) }
225
+ nodes
226
+ end
227
+
228
+ # Execute JavaScript against the current page, discarding any return value.
229
+ #
230
+ # @param [String] script the JavaScript to be executed
231
+ # @return [nil]
232
+ def execute_script(script)
233
+ page.execute_script script
234
+ end
235
+
236
+ # Execute JavaScript against the current page and return the results.
237
+ #
238
+ # @param [String] script the JavaScript to be executed
239
+ # @return the result of the JavaScript
240
+ def evaluate_script(script)
241
+ page.evaluate_script script
242
+ end
243
+
244
+ # @return the current page
245
+ def page
246
+ browser.page
247
+ end
248
+
249
+ # @return the browser
250
+ def browser
251
+ self.class.driver
252
+ end
253
+
254
+ # @return the session cookies
255
+ def cookies
256
+ browser.cookies
257
+ end
258
+
259
+ # @return [String] the current user agent string
260
+ def user_agent
261
+ browser.user_agent
262
+ end
263
+
264
+ # Set the User-Agent header for this session. If :default is given, the
265
+ # User-Agent header will be reset to the default browser's user agent.
266
+ #
267
+ # @param [:default] user_agent the default user agent
268
+ # @param [String] user_agent the user agent string to use
269
+ def user_agent=(user_agent)
270
+ browser.user_agent = user_agent
271
+ end
272
+
273
+ # Disable waiting in Capybara, since waiting is handled directly by
274
+ # Akephalos.
275
+ #
276
+ # @return [false]
277
+ def wait
278
+ false
279
+ end
280
+
281
+ private
282
+
283
+ # @param [String] path
284
+ # @return [String] the absolute URL for the given path
285
+ def url(path)
286
+ rack_server.url(path)
287
+ end
288
+
289
+ end
290
+
291
+ Capybara.register_driver :akephalos do |app|
292
+ Capybara::Driver::Akephalos.new(app)
293
+ end