akephalos-nerian 0.2.4-java
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.
- data/MIT_LICENSE +20 -0
- data/README.md +56 -0
- data/bin/akephalos +88 -0
- data/lib/akephalos.rb +19 -0
- data/lib/akephalos/capybara.rb +316 -0
- data/lib/akephalos/client.rb +112 -0
- data/lib/akephalos/client/cookies.rb +73 -0
- data/lib/akephalos/client/filter.rb +120 -0
- data/lib/akephalos/configuration.rb +49 -0
- data/lib/akephalos/console.rb +32 -0
- data/lib/akephalos/cucumber.rb +6 -0
- data/lib/akephalos/htmlunit.rb +38 -0
- data/lib/akephalos/htmlunit/ext/http_method.rb +30 -0
- data/lib/akephalos/node.rb +172 -0
- data/lib/akephalos/page.rb +113 -0
- data/lib/akephalos/remote_client.rb +84 -0
- data/lib/akephalos/server.rb +56 -0
- data/lib/akephalos/version.rb +3 -0
- data/src/htmlunit/apache-mime4j-0.6.jar +0 -0
- data/src/htmlunit/commons-codec-1.4.jar +0 -0
- data/src/htmlunit/commons-collections-3.2.1.jar +0 -0
- data/src/htmlunit/commons-io-1.4.jar +0 -0
- data/src/htmlunit/commons-lang-2.4.jar +0 -0
- data/src/htmlunit/commons-logging-1.1.1.jar +0 -0
- data/src/htmlunit/cssparser-0.9.5.jar +0 -0
- data/src/htmlunit/htmlunit-2.8.jar +0 -0
- data/src/htmlunit/htmlunit-core-js-2.8.jar +0 -0
- data/src/htmlunit/httpclient-4.0.1.jar +0 -0
- data/src/htmlunit/httpcore-4.0.1.jar +0 -0
- data/src/htmlunit/httpmime-4.0.1.jar +0 -0
- data/src/htmlunit/nekohtml-1.9.14.jar +0 -0
- data/src/htmlunit/sac-1.3.jar +0 -0
- data/src/htmlunit/serializer-2.7.1.jar +0 -0
- data/src/htmlunit/xalan-2.7.1.jar +0 -0
- data/src/htmlunit/xercesImpl-2.9.1.jar +0 -0
- data/src/htmlunit/xml-apis-1.3.04.jar +0 -0
- metadata +150 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'akephalos/configuration'
|
2
|
+
|
3
|
+
if RUBY_PLATFORM != "java"
|
4
|
+
require 'akephalos/remote_client'
|
5
|
+
Akephalos::Client = Akephalos::RemoteClient
|
6
|
+
else
|
7
|
+
require 'akephalos/htmlunit'
|
8
|
+
require 'akephalos/htmlunit/ext/http_method'
|
9
|
+
|
10
|
+
require 'akephalos/page'
|
11
|
+
require 'akephalos/node'
|
12
|
+
|
13
|
+
require 'akephalos/client/cookies'
|
14
|
+
require 'akephalos/client/filter'
|
15
|
+
|
16
|
+
module Akephalos
|
17
|
+
|
18
|
+
# Akephalos::Client wraps HtmlUnit's WebClient class. It is the main entry
|
19
|
+
# point for all interaction with the browser, exposing its current page and
|
20
|
+
# allowing navigation.
|
21
|
+
class Client
|
22
|
+
attr_reader :page
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
@_client = java.util.concurrent.FutureTask.new do
|
26
|
+
client = HtmlUnit::WebClient.new
|
27
|
+
|
28
|
+
Filter.new(client)
|
29
|
+
client.setThrowExceptionOnFailingStatusCode(false)
|
30
|
+
client.setAjaxController(HtmlUnit::NicelyResynchronizingAjaxController.new)
|
31
|
+
client.setCssErrorHandler(HtmlUnit::SilentCssErrorHandler.new)
|
32
|
+
client.setThrowExceptionOnScriptError(false);
|
33
|
+
client
|
34
|
+
end
|
35
|
+
Thread.new { @_client.run }
|
36
|
+
end
|
37
|
+
|
38
|
+
# Set the global configuration settings for Akephalos.
|
39
|
+
#
|
40
|
+
# @note This is only used when communicating over DRb, since just a
|
41
|
+
# single client instance is exposed.
|
42
|
+
# @param [Hash] config the configuration settings
|
43
|
+
# @return [Hash] the configuration
|
44
|
+
def configuration=(config)
|
45
|
+
Akephalos.configuration = config
|
46
|
+
end
|
47
|
+
|
48
|
+
# Visit the requested URL and return the page.
|
49
|
+
#
|
50
|
+
# @param [String] url the URL to load
|
51
|
+
# @return [Page] the loaded page
|
52
|
+
def visit(url)
|
53
|
+
client.getPage(url)
|
54
|
+
page
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return [Cookies] the cookies for this session
|
58
|
+
def cookies
|
59
|
+
@cookies ||= Cookies.new(client.getCookieManager)
|
60
|
+
end
|
61
|
+
|
62
|
+
# @return [String] the current user agent string
|
63
|
+
def user_agent
|
64
|
+
@user_agent || client.getBrowserVersion.getUserAgent
|
65
|
+
end
|
66
|
+
|
67
|
+
# Set the User-Agent header for this session. If :default is given, the
|
68
|
+
# User-Agent header will be reset to the default browser's user agent.
|
69
|
+
#
|
70
|
+
# @param [:default] user_agent the default user agent
|
71
|
+
# @param [String] user_agent the user agent string to use
|
72
|
+
def user_agent=(user_agent)
|
73
|
+
if user_agent == :default
|
74
|
+
@user_agent = nil
|
75
|
+
client.removeRequestHeader("User-Agent")
|
76
|
+
else
|
77
|
+
@user_agent = user_agent
|
78
|
+
client.addRequestHeader("User-Agent", user_agent)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# @return [Page] the current page
|
83
|
+
def page
|
84
|
+
self.page = client.getCurrentWindow.getTopWindow.getEnclosedPage
|
85
|
+
@page
|
86
|
+
end
|
87
|
+
|
88
|
+
# Update the current page.
|
89
|
+
#
|
90
|
+
# @param [HtmlUnit::HtmlPage] _page the new page
|
91
|
+
# @return [Page] the new page
|
92
|
+
def page=(_page)
|
93
|
+
if @page != _page
|
94
|
+
@page = Page.new(_page)
|
95
|
+
end
|
96
|
+
@page
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
# Call the future set up in #initialize and return the WebCLient
|
102
|
+
# instance.
|
103
|
+
#
|
104
|
+
# @return [HtmlUnit::WebClient] the WebClient instance
|
105
|
+
def client
|
106
|
+
@client ||= @_client.get.tap do |client|
|
107
|
+
client.getCurrentWindow.getHistory.ignoreNewPages_.set(true)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Akephalos
|
2
|
+
class Client
|
3
|
+
# Interface for working with HtmlUnit's CookieManager, providing a basic
|
4
|
+
# API for manipulating the cookies in a session.
|
5
|
+
class Cookies
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
# @param [HtmlUnit::CookieManager] cookie manager
|
9
|
+
def initialize(cookie_manager)
|
10
|
+
@cookie_manager = cookie_manager
|
11
|
+
end
|
12
|
+
|
13
|
+
# @param [name] the cookie name
|
14
|
+
# @return [Cookie] the cookie with the given name
|
15
|
+
# @return [nil] when no cookie is found
|
16
|
+
def [](name)
|
17
|
+
cookie = @cookie_manager.getCookie(name)
|
18
|
+
Cookie.new(cookie) if cookie
|
19
|
+
end
|
20
|
+
|
21
|
+
# Clears all cookies for this session.
|
22
|
+
def clear
|
23
|
+
@cookie_manager.clearCookies
|
24
|
+
end
|
25
|
+
|
26
|
+
# Iterator for all cookies in the current session.
|
27
|
+
def each
|
28
|
+
@cookie_manager.getCookies.each do |cookie|
|
29
|
+
yield Cookie.new(cookie)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Remove the cookie from the session.
|
34
|
+
#
|
35
|
+
# @param [Cookie] the cookie to remove
|
36
|
+
def delete(cookie)
|
37
|
+
@cookie_manager.removeCookie(cookie.native)
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [true, false] whether there are any cookies
|
41
|
+
def empty?
|
42
|
+
!any?
|
43
|
+
end
|
44
|
+
|
45
|
+
class Cookie
|
46
|
+
|
47
|
+
attr_reader :domain, :expires, :name, :path, :value
|
48
|
+
|
49
|
+
# @param [HtmlUnit::Cookie] the cookie
|
50
|
+
def initialize(cookie)
|
51
|
+
@_cookie = cookie
|
52
|
+
@domain = cookie.getDomain
|
53
|
+
@expires = cookie.getExpires
|
54
|
+
@name = cookie.getName
|
55
|
+
@path = cookie.getPath
|
56
|
+
@value = cookie.getValue
|
57
|
+
@secure = cookie.isSecure
|
58
|
+
end
|
59
|
+
|
60
|
+
def secure?
|
61
|
+
!!@secure
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [HtmlUnit::Cookie] the native cookie object
|
65
|
+
# @api private
|
66
|
+
def native
|
67
|
+
@_cookie
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module Akephalos
|
2
|
+
class Client
|
3
|
+
|
4
|
+
# Akephalos::Client::Filter extends HtmlUnit's WebConnectionWrapper to
|
5
|
+
# enable filtering outgoing requests generated interally by HtmlUnit.
|
6
|
+
#
|
7
|
+
# When a request comes through, it will be tested against the filters
|
8
|
+
# defined in Akephalos.filters and return a mock response if a match is
|
9
|
+
# found. If no filters are defined, or no filters match the request, then
|
10
|
+
# the response will bubble up to HtmlUnit for the normal request/response
|
11
|
+
# cycle.
|
12
|
+
class Filter < HtmlUnit::Util::WebConnectionWrapper
|
13
|
+
# Filters an outgoing request, and if a match is found, returns the mock
|
14
|
+
# response.
|
15
|
+
#
|
16
|
+
# @param [WebRequest] request the pending HTTP request
|
17
|
+
# @return [WebResponseImpl] when the request matches a defined filter
|
18
|
+
# @return [nil] when no filters match the request
|
19
|
+
def filter(request)
|
20
|
+
if filter = find_filter(request)
|
21
|
+
start_time = Time.now
|
22
|
+
headers = filter[:headers].map do |name, value|
|
23
|
+
HtmlUnit::Util::NameValuePair.new(name.to_s, value.to_s)
|
24
|
+
end
|
25
|
+
response = HtmlUnit::WebResponseData.new(
|
26
|
+
filter[:body].to_s.to_java_bytes,
|
27
|
+
filter[:status],
|
28
|
+
HTTP_STATUS_CODES.fetch(filter[:status], "Unknown"),
|
29
|
+
headers
|
30
|
+
)
|
31
|
+
HtmlUnit::WebResponseImpl.new(response, request, Time.now - start_time)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Searches for a filter which matches the request's HTTP method and url.
|
36
|
+
#
|
37
|
+
# @param [WebRequest] request the pending HTTP request
|
38
|
+
# @return [Hash] when a filter matches the request
|
39
|
+
# @return [nil] when no filters match the request
|
40
|
+
def find_filter(request)
|
41
|
+
Akephalos.filters.find do |filter|
|
42
|
+
request.http_method === filter[:method] && request.url.to_s =~ filter[:filter]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# This method is called by WebClient when a page is requested, and will
|
47
|
+
# return a mock response if the request matches a defined filter or else
|
48
|
+
# return the actual response.
|
49
|
+
#
|
50
|
+
# @api htmlunit
|
51
|
+
# @param [WebRequest] request the pending HTTP request
|
52
|
+
# @return [WebResponseImpl]
|
53
|
+
def getResponse(request)
|
54
|
+
filter(request) || super
|
55
|
+
end
|
56
|
+
|
57
|
+
# Map of status codes to their English descriptions.
|
58
|
+
HTTP_STATUS_CODES = {
|
59
|
+
100 => "Continue",
|
60
|
+
101 => "Switching Protocols",
|
61
|
+
102 => "Processing",
|
62
|
+
200 => "OK",
|
63
|
+
201 => "Created",
|
64
|
+
202 => "Accepted",
|
65
|
+
203 => "Non-Authoritative Information",
|
66
|
+
204 => "No Content",
|
67
|
+
205 => "Reset Content",
|
68
|
+
206 => "Partial Content",
|
69
|
+
207 => "Multi-Status",
|
70
|
+
300 => "Multiple Choices",
|
71
|
+
301 => "Moved Permanently",
|
72
|
+
302 => "Found",
|
73
|
+
303 => "See Other",
|
74
|
+
304 => "Not Modified",
|
75
|
+
305 => "Use Proxy",
|
76
|
+
306 => "Switch Proxy",
|
77
|
+
307 => "Temporary Redirect",
|
78
|
+
400 => "Bad Request",
|
79
|
+
401 => "Unauthorized",
|
80
|
+
402 => "Payment Required",
|
81
|
+
403 => "Forbidden",
|
82
|
+
404 => "Not Found",
|
83
|
+
405 => "Method Not Allowed",
|
84
|
+
406 => "Not Acceptable",
|
85
|
+
407 => "Proxy Authentication Required",
|
86
|
+
408 => "Request Timeout",
|
87
|
+
409 => "Conflict",
|
88
|
+
410 => "Gone",
|
89
|
+
411 => "Length Required",
|
90
|
+
412 => "Precondition Failed",
|
91
|
+
413 => "Request Entity Too Large",
|
92
|
+
414 => "Request-URI Too Long",
|
93
|
+
415 => "Unsupported Media Type",
|
94
|
+
416 => "Requested Range Not Satisfiable",
|
95
|
+
417 => "Expectation Failed",
|
96
|
+
418 => "I'm a teapot",
|
97
|
+
421 => "There are too many connections from your internet address",
|
98
|
+
422 => "Unprocessable Entity",
|
99
|
+
423 => "Locked",
|
100
|
+
424 => "Failed Dependency",
|
101
|
+
425 => "Unordered Collection",
|
102
|
+
426 => "Upgrade Required",
|
103
|
+
449 => "Retry With",
|
104
|
+
450 => "Blocked by Windows Parental Controls",
|
105
|
+
500 => "Internal Server Error",
|
106
|
+
501 => "Not Implemented",
|
107
|
+
502 => "Bad Gateway",
|
108
|
+
503 => "Service Unavailable",
|
109
|
+
504 => "Gateway Timeout",
|
110
|
+
505 => "HTTP Version Not Supported",
|
111
|
+
506 => "Variant Also Negotiates",
|
112
|
+
507 => "Insufficient Storage",
|
113
|
+
509 => "Bandwidth Limit Exceeded",
|
114
|
+
510 => "Not Extended",
|
115
|
+
530 => "User access denied"
|
116
|
+
}.freeze
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Akephalos
|
2
|
+
|
3
|
+
@configuration = {}
|
4
|
+
|
5
|
+
class << self
|
6
|
+
# @return [Hash] the configuration
|
7
|
+
attr_accessor :configuration
|
8
|
+
end
|
9
|
+
|
10
|
+
module Filters
|
11
|
+
# @return [Array] all defined filters
|
12
|
+
def filters
|
13
|
+
configuration[:filters] ||= []
|
14
|
+
end
|
15
|
+
|
16
|
+
# Defines a new filter to be tested by Akephalos::Filter when executing
|
17
|
+
# page requests. An HTTP method and a regex or string to match against the
|
18
|
+
# URL are required for defining a filter.
|
19
|
+
#
|
20
|
+
# You can additionally pass the following options to define how the
|
21
|
+
# filtered request should respond:
|
22
|
+
#
|
23
|
+
# :status (defaults to 200)
|
24
|
+
# :body (defaults to "")
|
25
|
+
# :headers (defaults to {})
|
26
|
+
#
|
27
|
+
# If we define a filter with no additional options, then, we will get an
|
28
|
+
# empty HTML response:
|
29
|
+
#
|
30
|
+
# Akephalos.filter :post, "http://example.com"
|
31
|
+
# Akephalos.filter :any, %r{http://.*\.com}
|
32
|
+
#
|
33
|
+
# If you instead, say, wanted to simulate a failure in an external system,
|
34
|
+
# you could do this:
|
35
|
+
#
|
36
|
+
# Akephalos.filter :post, "http://example.com",
|
37
|
+
# :status => 500, :body => "Something went wrong"
|
38
|
+
#
|
39
|
+
# @param [Symbol] method the HTTP method to match
|
40
|
+
# @param [RegExp, String] regex URL matcher
|
41
|
+
# @param [Hash] options response values
|
42
|
+
def filter(method, regex, options = {})
|
43
|
+
regex = Regexp.new(Regexp.escape(regex)) if regex.is_a?(String)
|
44
|
+
filters << {:method => method, :filter => regex, :status => 200, :body => "", :headers => {}}.merge!(options)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
extend Filters
|
49
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Begin a new Capybara session, by default connecting to localhost on port
|
2
|
+
# 3000.
|
3
|
+
def session
|
4
|
+
Capybara.app_host ||= "http://localhost:3000"
|
5
|
+
@session ||= Capybara::Session.new(:Akephalos)
|
6
|
+
end
|
7
|
+
alias page session
|
8
|
+
|
9
|
+
module Akephalos
|
10
|
+
# Simple class for starting an IRB session.
|
11
|
+
class Console
|
12
|
+
|
13
|
+
# Start an IRB session. Tries to load irb/completion, and also loads a
|
14
|
+
# .irbrc file if it exists.
|
15
|
+
def self.start
|
16
|
+
require 'irb'
|
17
|
+
|
18
|
+
begin
|
19
|
+
require 'irb/completion'
|
20
|
+
rescue Exception
|
21
|
+
# No readline available, proceed anyway.
|
22
|
+
end
|
23
|
+
|
24
|
+
if ::File.exists? ".irbrc"
|
25
|
+
ENV['IRBRC'] = ".irbrc"
|
26
|
+
end
|
27
|
+
|
28
|
+
IRB.start
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "pathname"
|
2
|
+
require "java"
|
3
|
+
|
4
|
+
dependency_directory = $:.detect { |path| Dir[File.join(path, 'htmlunit/htmlunit-*.jar')].any? }
|
5
|
+
|
6
|
+
raise "Could not find htmlunit/htmlunit-VERSION.jar in load path:\n [ #{$:.join(",\n ")}\n ]" unless dependency_directory
|
7
|
+
|
8
|
+
Dir[File.join(dependency_directory, "htmlunit/*.jar")].each do |jar|
|
9
|
+
require jar
|
10
|
+
end
|
11
|
+
|
12
|
+
java.lang.System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog")
|
13
|
+
java.lang.System.setProperty("org.apache.commons.logging.simplelog.defaultlog", "fatal")
|
14
|
+
java.lang.System.setProperty("org.apache.commons.logging.simplelog.showdatetime", "true")
|
15
|
+
|
16
|
+
# Container module for com.gargoylesoftware.htmlunit namespace.
|
17
|
+
module HtmlUnit
|
18
|
+
java_import "com.gargoylesoftware.htmlunit.BrowserVersion"
|
19
|
+
java_import "com.gargoylesoftware.htmlunit.History"
|
20
|
+
java_import "com.gargoylesoftware.htmlunit.HttpMethod"
|
21
|
+
java_import "com.gargoylesoftware.htmlunit.NicelyResynchronizingAjaxController"
|
22
|
+
java_import "com.gargoylesoftware.htmlunit.SilentCssErrorHandler"
|
23
|
+
java_import "com.gargoylesoftware.htmlunit.WebClient"
|
24
|
+
java_import "com.gargoylesoftware.htmlunit.WebResponseData"
|
25
|
+
java_import "com.gargoylesoftware.htmlunit.WebResponseImpl"
|
26
|
+
|
27
|
+
# Container module for com.gargoylesoftware.htmlunit.util namespace.
|
28
|
+
module Util
|
29
|
+
java_import "com.gargoylesoftware.htmlunit.util.NameValuePair"
|
30
|
+
java_import "com.gargoylesoftware.htmlunit.util.WebConnectionWrapper"
|
31
|
+
end
|
32
|
+
|
33
|
+
# Disable history tracking
|
34
|
+
History.field_reader :ignoreNewPages_
|
35
|
+
|
36
|
+
# Run in Firefox compatibility mode
|
37
|
+
BrowserVersion.setDefault(BrowserVersion::FIREFOX_3)
|
38
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module HtmlUnit
|
2
|
+
# Reopen HtmlUnit's HttpMethod class to add convenience methods.
|
3
|
+
class HttpMethod
|
4
|
+
|
5
|
+
# Loosely compare HttpMethod with another object, accepting either an
|
6
|
+
# HttpMethod instance or a symbol describing the method. Note that :any is a
|
7
|
+
# special symbol which will always return true.
|
8
|
+
#
|
9
|
+
# @param [HttpMethod] other an HtmlUnit HttpMethod object
|
10
|
+
# @param [Symbol] other a symbolized representation of an http method
|
11
|
+
# @return [true/false]
|
12
|
+
def ===(other)
|
13
|
+
case other
|
14
|
+
when HttpMethod
|
15
|
+
super
|
16
|
+
when :any
|
17
|
+
true
|
18
|
+
when :get
|
19
|
+
self == self.class::GET
|
20
|
+
when :post
|
21
|
+
self == self.class::POST
|
22
|
+
when :put
|
23
|
+
self == self.class::PUT
|
24
|
+
when :delete
|
25
|
+
self == self.class::DELETE
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|