brentgreeff_akephalos 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 +280 -0
  6. data/lib/akephalos/client.rb +112 -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 +151 -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,280 @@
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 Node class.
10
+ class Node < Capybara::Node
11
+
12
+ # @api capybara
13
+ # @param [String] name attribute name
14
+ # @return [String] the attribute value
15
+ def [](name)
16
+ name = name.to_s
17
+ case name
18
+ when 'checked'
19
+ node.checked?
20
+ else
21
+ node[name.to_s]
22
+ end
23
+ end
24
+
25
+ # @api capybara
26
+ # @return [String] the inner text of the node
27
+ def text
28
+ node.text
29
+ end
30
+
31
+ # @api capybara
32
+ # @return [String] the form element's value
33
+ def value
34
+ node.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
+ node.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
+ node.value = value.to_s
52
+ end
53
+ end
54
+
55
+ # Select an option from a select box.
56
+ #
57
+ # @api capybara
58
+ # @param [String] option the option to select
59
+ def select(option)
60
+ result = node.select_option(option)
61
+
62
+ if result == nil
63
+ options = node.options.map(&:text).join(", ")
64
+ raise Capybara::OptionNotFound, "No such option '#{option}' in this select box. Available options: #{options}"
65
+ else
66
+ result
67
+ end
68
+ end
69
+
70
+ # Unselect an option from a select box.
71
+ #
72
+ # @api capybara
73
+ # @param [String] option the option to unselect
74
+ def unselect(option)
75
+ unless self[:multiple]
76
+ raise Capybara::UnselectNotAllowed, "Cannot unselect option '#{option}' from single select box."
77
+ end
78
+
79
+ result = node.unselect_option(option)
80
+
81
+ if result == nil
82
+ options = node.options.map(&:text).join(", ")
83
+ raise Capybara::OptionNotFound, "No such option '#{option}' in this select box. Available options: #{options}"
84
+ else
85
+ result
86
+ end
87
+ end
88
+
89
+ # Trigger an event on the element.
90
+ #
91
+ # @api capybara
92
+ # @param [String] event the event to trigger
93
+ def trigger(event)
94
+ node.fire_event(event.to_s)
95
+ end
96
+
97
+ # @api capybara
98
+ # @return [String] the element's tag name
99
+ def tag_name
100
+ node.tag_name
101
+ end
102
+
103
+ # @api capybara
104
+ # @return [true, false] the element's visiblity
105
+ def visible?
106
+ node.visible?
107
+ end
108
+
109
+ # Drag the element on top of the target element.
110
+ #
111
+ # @api capybara
112
+ # @param [Node] element the target element
113
+ def drag_to(element)
114
+ trigger('mousedown')
115
+ element.trigger('mousemove')
116
+ element.trigger('mouseup')
117
+ end
118
+
119
+ # Click the element.
120
+ def click
121
+ node.click
122
+ end
123
+
124
+ private
125
+
126
+ # Return all child nodes which match the selector criteria.
127
+ #
128
+ # @api capybara
129
+ # @return [Array<Node>] the matched nodes
130
+ def all_unfiltered(selector)
131
+ nodes = []
132
+ node.find(selector).each { |node| nodes << Node.new(driver, node) }
133
+ nodes
134
+ end
135
+
136
+ # @return [String] the node's type attribute
137
+ def type
138
+ node[:type]
139
+ end
140
+ end
141
+
142
+ attr_reader :app, :rack_server
143
+
144
+ # @return [Client] an instance of Akephalos::Client
145
+ def self.driver
146
+ @driver ||= Akephalos::Client.new
147
+ end
148
+
149
+ def initialize(app)
150
+ @app = app
151
+ @rack_server = Capybara::Server.new(@app)
152
+ @rack_server.boot if Capybara.run_server
153
+ end
154
+
155
+ # Visit the given path in the browser.
156
+ #
157
+ # @param [String] path relative path to visit
158
+ def visit(path)
159
+ browser.visit(url(path))
160
+ end
161
+
162
+ # @return [String] the page's original source
163
+ def source
164
+ page.source
165
+ end
166
+
167
+ # @return [String] the page's modified source
168
+ def body
169
+ page.modified_source
170
+ end
171
+
172
+ # @return [Hash{String => String}] the page's response headers
173
+ def response_headers
174
+ page.response_headers
175
+ end
176
+
177
+ # @return [Integer] the response's status code
178
+ def status_code
179
+ page.status_code
180
+ end
181
+
182
+ # Execute the given block within the context of a specified frame.
183
+ #
184
+ # @param [String] frame_id the frame's id
185
+ # @raise [Capybara::ElementNotFound] if the frame is not found
186
+ def within_frame(frame_id, &block)
187
+ unless page.within_frame(frame_id, &block)
188
+ raise Capybara::ElementNotFound, "Unable to find frame with id '#{frame_id}'"
189
+ end
190
+ end
191
+
192
+ # Clear all cookie session data.
193
+ # @deprecated This method is deprecated in Capybara's master branch. Use
194
+ # {#reset!} instead.
195
+ def cleanup!
196
+ reset!
197
+ end
198
+
199
+ # Clear all cookie session data.
200
+ def reset!
201
+ cookies.clear
202
+ end
203
+
204
+ # @return [String] the page's current URL
205
+ def current_url
206
+ page.current_url
207
+ end
208
+
209
+ # Search for nodes which match the given XPath selector.
210
+ #
211
+ # @param [String] selector XPath query
212
+ # @return [Array<Node>] the matched nodes
213
+ def find(selector)
214
+ nodes = []
215
+ page.find(selector).each { |node| nodes << Node.new(self, node) }
216
+ nodes
217
+ end
218
+
219
+ # Execute JavaScript against the current page, discarding any return value.
220
+ #
221
+ # @param [String] script the JavaScript to be executed
222
+ # @return [nil]
223
+ def execute_script(script)
224
+ page.execute_script script
225
+ end
226
+
227
+ # Execute JavaScript against the current page and return the results.
228
+ #
229
+ # @param [String] script the JavaScript to be executed
230
+ # @return the result of the JavaScript
231
+ def evaluate_script(script)
232
+ page.evaluate_script script
233
+ end
234
+
235
+ # @return the current page
236
+ def page
237
+ browser.page
238
+ end
239
+
240
+ # @return the browser
241
+ def browser
242
+ self.class.driver
243
+ end
244
+
245
+ # @return the session cookies
246
+ def cookies
247
+ browser.cookies
248
+ end
249
+
250
+ # @return [String] the current user agent string
251
+ def user_agent
252
+ browser.user_agent
253
+ end
254
+
255
+ # Set the User-Agent header for this session. If :default is given, the
256
+ # User-Agent header will be reset to the default browser's user agent.
257
+ #
258
+ # @param [:default] user_agent the default user agent
259
+ # @param [String] user_agent the user agent string to use
260
+ def user_agent=(user_agent)
261
+ browser.user_agent = user_agent
262
+ end
263
+
264
+ # Disable waiting in Capybara, since waiting is handled directly by
265
+ # Akephalos.
266
+ #
267
+ # @return [false]
268
+ def wait
269
+ false
270
+ end
271
+
272
+ private
273
+
274
+ # @param [String] path
275
+ # @return [String] the absolute URL for the given path
276
+ def url(path)
277
+ rack_server.url(path)
278
+ end
279
+
280
+ end