brentgreeff_akephalos 0.2.4

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 (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