akephalos-nerian 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 +88 -0
  4. data/lib/akephalos.rb +19 -0
  5. data/lib/akephalos/capybara.rb +316 -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 +172 -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,88 @@
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
+ "-Ku",
76
+ "-I", "vendor:#{lib}:#{src}",
77
+ "-r", "akephalos/server",
78
+ "-e", "Akephalos::Server.start!(#{port.inspect})"
79
+ ]
80
+
81
+ # Bundler sets ENV["RUBYOPT"] to automatically load bundler/setup.rb, but
82
+ # since the akephalos server doesn't have any gem dependencies and is
83
+ # always executed with the same context, we clear RUBYOPT before running
84
+ # exec.
85
+ ENV["RUBYOPT"] = ""
86
+ exec("java", *(java_args + ruby_args))
87
+ end
88
+ 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
+ Capybara.register_driver :akephalos do |app|
18
+ Capybara::Driver::Akephalos.new(app)
19
+ end
@@ -0,0 +1,316 @@
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 [true, false] the element's visiblity
100
+ def checked?
101
+ native.checked?
102
+ end
103
+
104
+ # @api capybara
105
+ # @return [true, false] the element's visiblity
106
+ def selected?
107
+ native.selected?
108
+ end
109
+
110
+ # @api capybara
111
+ # @return [String] the XPath to locate the node
112
+ def path
113
+ native.xpath
114
+ end
115
+
116
+ # Trigger an event on the element.
117
+ #
118
+ # @api capybara
119
+ # @param [String] event the event to trigger
120
+ def trigger(event)
121
+ native.fire_event(event.to_s)
122
+ end
123
+
124
+ # @api capybara
125
+ # @param [String] selector XPath query
126
+ # @return [Array<Node>] the matched nodes
127
+ def find(selector)
128
+ nodes = []
129
+ native.find(selector).each { |node| nodes << self.class.new(self, node) }
130
+ nodes
131
+ end
132
+
133
+ protected
134
+
135
+ # @return [true, false] whether the node allows multiple-option selection (if the node is a select).
136
+ def multiple_select?
137
+ tag_name == "select" && native.multiple_select?
138
+ end
139
+
140
+ private
141
+
142
+ # Return all child nodes which match the selector criteria.
143
+ #
144
+ # @api capybara
145
+ # @return [Array<Node>] the matched nodes
146
+ def all_unfiltered(selector)
147
+ nodes = []
148
+ native.find(selector).each { |node| nodes << self.class.new(driver, node) }
149
+ nodes
150
+ end
151
+
152
+ # @return [String] the node's type attribute
153
+ def type
154
+ native[:type]
155
+ end
156
+
157
+ # @return [Node] the select node, if this is an option node
158
+ def select_node
159
+ find('./ancestor::select').first
160
+ end
161
+ end
162
+
163
+ attr_reader :app, :rack_server
164
+
165
+ # @return [Client] an instance of Akephalos::Client
166
+ def self.driver
167
+ @driver ||= Akephalos::Client.new
168
+ end
169
+
170
+ def initialize(app)
171
+ @app = app
172
+ @rack_server = Capybara::Server.new(@app)
173
+ @rack_server.boot if Capybara.run_server
174
+ end
175
+
176
+ # Visit the given path in the browser.
177
+ #
178
+ # @param [String] path relative path to visit
179
+ def visit(path)
180
+ browser.visit(url(path))
181
+ end
182
+
183
+ # @return [String] the page's original source
184
+ def source
185
+ page.source
186
+ end
187
+
188
+ # @return [String] the page's modified source
189
+ # page.modified_source will return a string with
190
+ # html entities converted into the unicode equivalent
191
+ # but the string will be marked as ASCII-8BIT
192
+ # which causes conversion issues so we force the encoding
193
+ # to UTF-8 (ruby 1.9 only)
194
+ def body
195
+ body_source = page.modified_source
196
+
197
+ if body_source.respond_to?(:force_encoding)
198
+ body_source.force_encoding("UTF-8")
199
+ else
200
+ body_source
201
+ end
202
+ end
203
+
204
+ # @return [Hash{String => String}] the page's response headers
205
+ def response_headers
206
+ page.response_headers
207
+ end
208
+
209
+ # @return [Integer] the response's status code
210
+ def status_code
211
+ page.status_code
212
+ end
213
+
214
+ # Execute the given block within the context of a specified frame.
215
+ #
216
+ # @param [String] frame_id the frame's id
217
+ # @raise [Capybara::ElementNotFound] if the frame is not found
218
+ def within_frame(frame_id, &block)
219
+ unless page.within_frame(frame_id, &block)
220
+ raise Capybara::ElementNotFound, "Unable to find frame with id '#{frame_id}'"
221
+ end
222
+ end
223
+
224
+ # Clear all cookie session data.
225
+ # @deprecated This method is deprecated in Capybara's master branch. Use
226
+ # {#reset!} instead.
227
+ def cleanup!
228
+ reset!
229
+ end
230
+
231
+ # Clear all cookie session data.
232
+ def reset!
233
+ cookies.clear
234
+ end
235
+
236
+ # @return [String] the page's current URL
237
+ def current_url
238
+ page.current_url
239
+ end
240
+
241
+ # Search for nodes which match the given XPath selector.
242
+ #
243
+ # @param [String] selector XPath query
244
+ # @return [Array<Node>] the matched nodes
245
+ def find(selector)
246
+ nodes = []
247
+ page.find(selector).each { |node| nodes << Node.new(self, node) }
248
+ nodes
249
+ end
250
+
251
+ # Execute JavaScript against the current page, discarding any return value.
252
+ #
253
+ # @param [String] script the JavaScript to be executed
254
+ # @return [nil]
255
+ def execute_script(script)
256
+ page.execute_script script
257
+ end
258
+
259
+ # Execute JavaScript against the current page and return the results.
260
+ #
261
+ # @param [String] script the JavaScript to be executed
262
+ # @return the result of the JavaScript
263
+ def evaluate_script(script)
264
+ page.evaluate_script script
265
+ end
266
+
267
+ # @return the current page
268
+ def page
269
+ browser.page
270
+ end
271
+
272
+ # @return the browser
273
+ def browser
274
+ self.class.driver
275
+ end
276
+
277
+ # @return the session cookies
278
+ def cookies
279
+ browser.cookies
280
+ end
281
+
282
+ # @return [String] the current user agent string
283
+ def user_agent
284
+ browser.user_agent
285
+ end
286
+
287
+ # Set the User-Agent header for this session. If :default is given, the
288
+ # User-Agent header will be reset to the default browser's user agent.
289
+ #
290
+ # @param [:default] user_agent the default user agent
291
+ # @param [String] user_agent the user agent string to use
292
+ def user_agent=(user_agent)
293
+ browser.user_agent = user_agent
294
+ end
295
+
296
+ # Disable waiting in Capybara, since waiting is handled directly by
297
+ # Akephalos.
298
+ #
299
+ # @return [false]
300
+ def wait
301
+ false
302
+ end
303
+
304
+ private
305
+
306
+ # @param [String] path
307
+ # @return [String] the absolute URL for the given path
308
+ def url(path)
309
+ rack_server.url(path)
310
+ end
311
+
312
+ end
313
+
314
+ Capybara.register_driver :akephalos do |app|
315
+ Capybara::Driver::Akephalos.new(app)
316
+ end