akephalos 0.2.4-java → 0.2.5-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/README.md +30 -0
- data/bin/akephalos +9 -8
- data/lib/akephalos.rb +3 -3
- data/lib/akephalos/capybara.rb +148 -64
- data/lib/akephalos/client.rb +79 -20
- data/lib/akephalos/client/cookies.rb +73 -0
- data/lib/akephalos/client/filter.rb +6 -8
- data/lib/akephalos/console.rb +1 -1
- data/lib/akephalos/htmlunit.rb +20 -12
- data/lib/akephalos/htmlunit/ext/http_method.rb +26 -24
- data/lib/akephalos/node.rb +51 -15
- data/lib/akephalos/remote_client.rb +51 -16
- data/lib/akephalos/server.rb +52 -7
- data/lib/akephalos/version.rb +1 -1
- metadata +17 -10
data/README.md
CHANGED
@@ -38,6 +38,36 @@ Here's some sample RSpec code:
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
+
## Configuration
|
42
|
+
|
43
|
+
There are now a few configuration options available through Capybara's new
|
44
|
+
`register_driver` API.
|
45
|
+
|
46
|
+
### Using a different browser
|
47
|
+
|
48
|
+
HtmlUnit supports a few browser implementations, and you can choose which
|
49
|
+
browser you would like to use through Akephalos. By default, Akephalos uses
|
50
|
+
Firefox 3.6.
|
51
|
+
|
52
|
+
Capybara.register_driver :akephalos do |app|
|
53
|
+
# available options:
|
54
|
+
# :ie6, :ie7, :ie8, :firefox_3, :firefox_3_6
|
55
|
+
Capybara::Driver::Akephalos.new(app, :browser => :ie8)
|
56
|
+
end
|
57
|
+
|
58
|
+
### Ignoring javascript errors
|
59
|
+
|
60
|
+
By default HtmlUnit (and Akephalos) will raise an exception when an error
|
61
|
+
is encountered in javascript files. This is generally desireable, except
|
62
|
+
that certain libraries aren't supported by HtmlUnit. If possible, it's
|
63
|
+
best to keep the default behavior, and use Filters (see below) to mock
|
64
|
+
offending libraries. If needed, however, you can configure Akephalos to
|
65
|
+
ignore javascript errors.
|
66
|
+
|
67
|
+
Capybara.register_driver :akephalos do |app|
|
68
|
+
Capybara::Driver::Akephalos.new(app, :validate_scripts => false)
|
69
|
+
end
|
70
|
+
|
41
71
|
## More
|
42
72
|
|
43
73
|
* [bin/akephalos](http://bernerdschaefer.github.com/akephalos/akephalos-bin.html)
|
data/bin/akephalos
CHANGED
@@ -7,7 +7,7 @@ require "optparse"
|
|
7
7
|
options = { :interactive => false }
|
8
8
|
|
9
9
|
parser = OptionParser.new do |opts|
|
10
|
-
opts.banner = "Usage: akephalos [--interactive, --use-htmlunit-snapshot] | [--server] <
|
10
|
+
opts.banner = "Usage: akephalos [--interactive, --use-htmlunit-snapshot] | [--server] <port>"
|
11
11
|
opts.on("-s", "--server", "Run in server mode (default)")
|
12
12
|
opts.on("-i", "--interactive", "Run in interactive mode") { options[:interactive] = true }
|
13
13
|
opts.on("--use-htmlunit-snapshot", "Use the snapshot of htmlunit") { options[:use_htmlunit_snapshot] = true }
|
@@ -30,15 +30,15 @@ when options[:use_htmlunit_snapshot]
|
|
30
30
|
Dir.chdir("vendor") do
|
31
31
|
$stdout.print "Downloading latest snapshot... "
|
32
32
|
$stdout.flush
|
33
|
-
%x[curl -O http://build.canoo.com/htmlunit/artifacts/htmlunit-2.
|
33
|
+
%x[curl -O http://build.canoo.com/htmlunit/artifacts/htmlunit-2.9-SNAPSHOT-with-dependencies.zip &> /dev/null]
|
34
34
|
puts "done"
|
35
35
|
|
36
36
|
$stdout.print "Extracting dependencies... "
|
37
37
|
$stdout.flush
|
38
|
-
%x[unzip -j -d htmlunit htmlunit-2.
|
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
39
|
puts "done"
|
40
40
|
|
41
|
-
File.unlink "htmlunit-2.
|
41
|
+
File.unlink "htmlunit-2.9-SNAPSHOT-with-dependencies.zip"
|
42
42
|
end
|
43
43
|
|
44
44
|
$stdout.puts "="*40
|
@@ -50,7 +50,7 @@ when options[:interactive]
|
|
50
50
|
require 'akephalos/console'
|
51
51
|
Akephalos::Console.start
|
52
52
|
else
|
53
|
-
unless
|
53
|
+
unless port = ARGV[0]
|
54
54
|
puts parser.help
|
55
55
|
exit
|
56
56
|
end
|
@@ -58,7 +58,7 @@ else
|
|
58
58
|
if RUBY_PLATFORM == "java"
|
59
59
|
$:.unshift("vendor", lib, src)
|
60
60
|
require 'akephalos/server'
|
61
|
-
Akephalos::Server.start!(
|
61
|
+
Akephalos::Server.start!(port)
|
62
62
|
else
|
63
63
|
require 'rubygems'
|
64
64
|
require 'jruby-jars'
|
@@ -71,10 +71,11 @@ else
|
|
71
71
|
"-cp", [JRubyJars.core_jar_path, JRubyJars.stdlib_jar_path].join(":"),
|
72
72
|
"org.jruby.Main"
|
73
73
|
]
|
74
|
-
ruby_args = [
|
74
|
+
ruby_args = [
|
75
|
+
"-Ku",
|
75
76
|
"-I", "vendor:#{lib}:#{src}",
|
76
77
|
"-r", "akephalos/server",
|
77
|
-
"-e", "Akephalos::Server.start!(#{
|
78
|
+
"-e", "Akephalos::Server.start!(#{port.inspect})"
|
78
79
|
]
|
79
80
|
|
80
81
|
# Bundler sets ENV["RUBYOPT"] to automatically load bundler/setup.rb, but
|
data/lib/akephalos.rb
CHANGED
@@ -5,15 +5,15 @@
|
|
5
5
|
#
|
6
6
|
require 'java' if RUBY_PLATFORM == 'java'
|
7
7
|
require 'pathname'
|
8
|
-
require 'capybara'
|
9
8
|
|
10
9
|
module Akephalos
|
11
10
|
BIN_DIR = Pathname(__FILE__).expand_path.dirname.parent + 'bin'
|
12
11
|
end
|
13
12
|
|
14
13
|
require 'akephalos/client'
|
14
|
+
require 'capybara'
|
15
15
|
require 'akephalos/capybara'
|
16
16
|
|
17
|
-
|
18
|
-
|
17
|
+
Capybara.register_driver :akephalos do |app|
|
18
|
+
Capybara::Driver::Akephalos.new(app)
|
19
19
|
end
|
data/lib/akephalos/capybara.rb
CHANGED
@@ -6,8 +6,14 @@
|
|
6
6
|
# directly or over DRb.
|
7
7
|
class Capybara::Driver::Akephalos < Capybara::Driver::Base
|
8
8
|
|
9
|
-
# Akephalos-specific implementation for Capybara's Node class.
|
10
|
-
class Node < Capybara::Node
|
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
|
11
17
|
|
12
18
|
# @api capybara
|
13
19
|
# @param [String] name attribute name
|
@@ -16,22 +22,16 @@ class Capybara::Driver::Akephalos < Capybara::Driver::Base
|
|
16
22
|
name = name.to_s
|
17
23
|
case name
|
18
24
|
when 'checked'
|
19
|
-
|
25
|
+
native.checked?
|
20
26
|
else
|
21
|
-
|
27
|
+
native[name.to_s]
|
22
28
|
end
|
23
29
|
end
|
24
30
|
|
25
31
|
# @api capybara
|
26
|
-
# @return [String] the
|
27
|
-
def text
|
28
|
-
node.text
|
29
|
-
end
|
30
|
-
|
31
|
-
# @api capybara
|
32
|
-
# @return [String] the form element's value
|
32
|
+
# @return [String, Array<String>] the form element's value
|
33
33
|
def value
|
34
|
-
|
34
|
+
native.value
|
35
35
|
end
|
36
36
|
|
37
37
|
# Set the form element's value.
|
@@ -40,7 +40,7 @@ class Capybara::Driver::Akephalos < Capybara::Driver::Base
|
|
40
40
|
# @param [String] value the form element's new value
|
41
41
|
def set(value)
|
42
42
|
if tag_name == 'textarea'
|
43
|
-
|
43
|
+
native.value = value.to_s
|
44
44
|
elsif tag_name == 'input' and type == 'radio'
|
45
45
|
click
|
46
46
|
elsif tag_name == 'input' and type == 'checkbox'
|
@@ -48,77 +48,93 @@ class Capybara::Driver::Akephalos < Capybara::Driver::Base
|
|
48
48
|
click
|
49
49
|
end
|
50
50
|
elsif tag_name == 'input'
|
51
|
-
|
51
|
+
native.value = value.to_s
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
-
# Select an option from a select box.
|
56
|
-
#
|
57
55
|
# @api capybara
|
58
|
-
|
59
|
-
|
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
|
56
|
+
def select_option
|
57
|
+
native.click
|
68
58
|
end
|
69
59
|
|
70
60
|
# Unselect an option from a select box.
|
71
61
|
#
|
72
62
|
# @api capybara
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
raise Capybara::UnselectNotAllowed, "Cannot unselect option '#{option}' from single select box."
|
63
|
+
def unselect_option
|
64
|
+
unless select_node.multiple_select?
|
65
|
+
raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box."
|
77
66
|
end
|
78
67
|
|
79
|
-
|
68
|
+
native.unselect
|
69
|
+
end
|
80
70
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
else
|
85
|
-
result
|
86
|
-
end
|
71
|
+
# Click the element.
|
72
|
+
def click
|
73
|
+
native.click
|
87
74
|
end
|
88
75
|
|
89
|
-
#
|
76
|
+
# Drag the element on top of the target element.
|
90
77
|
#
|
91
78
|
# @api capybara
|
92
|
-
# @param [
|
93
|
-
def
|
94
|
-
|
79
|
+
# @param [Node] element the target element
|
80
|
+
def drag_to(element)
|
81
|
+
trigger('mousedown')
|
82
|
+
element.trigger('mousemove')
|
83
|
+
element.trigger('mouseup')
|
95
84
|
end
|
96
85
|
|
97
86
|
# @api capybara
|
98
87
|
# @return [String] the element's tag name
|
99
88
|
def tag_name
|
100
|
-
|
89
|
+
native.tag_name
|
101
90
|
end
|
102
91
|
|
103
92
|
# @api capybara
|
104
93
|
# @return [true, false] the element's visiblity
|
105
94
|
def visible?
|
106
|
-
|
95
|
+
native.visible?
|
107
96
|
end
|
108
97
|
|
109
|
-
#
|
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.
|
110
117
|
#
|
111
118
|
# @api capybara
|
112
|
-
# @param [
|
113
|
-
def
|
114
|
-
|
115
|
-
element.trigger('mousemove')
|
116
|
-
element.trigger('mouseup')
|
119
|
+
# @param [String] event the event to trigger
|
120
|
+
def trigger(event)
|
121
|
+
native.fire_event(event.to_s)
|
117
122
|
end
|
118
123
|
|
119
|
-
#
|
120
|
-
|
121
|
-
|
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?
|
122
138
|
end
|
123
139
|
|
124
140
|
private
|
@@ -129,25 +145,52 @@ class Capybara::Driver::Akephalos < Capybara::Driver::Base
|
|
129
145
|
# @return [Array<Node>] the matched nodes
|
130
146
|
def all_unfiltered(selector)
|
131
147
|
nodes = []
|
132
|
-
|
148
|
+
native.find(selector).each { |node| nodes << self.class.new(driver, node) }
|
133
149
|
nodes
|
134
150
|
end
|
135
151
|
|
136
152
|
# @return [String] the node's type attribute
|
137
153
|
def type
|
138
|
-
|
154
|
+
native[:type]
|
139
155
|
end
|
140
|
-
end
|
141
156
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
@driver ||= Akephalos::Client.new
|
157
|
+
# @return [Node] the select node, if this is an option node
|
158
|
+
def select_node
|
159
|
+
find('./ancestor::select').first
|
160
|
+
end
|
147
161
|
end
|
148
162
|
|
149
|
-
|
163
|
+
attr_reader :app, :rack_server, :options
|
164
|
+
|
165
|
+
# Creates a new instance of the Akephalos Driver for Capybara. The driver is
|
166
|
+
# registered with Capybara by a name, so that it can be chosen when
|
167
|
+
# Capybara's javascript_driver is changed. By default, Akephalos is
|
168
|
+
# registered like this:
|
169
|
+
#
|
170
|
+
# Capybara.register_driver :akephalos do |app|
|
171
|
+
# Capybara::Akephalos::Driver.new(
|
172
|
+
# app,
|
173
|
+
# :browser => :firefox_3_6,
|
174
|
+
# :validate_scripts => true
|
175
|
+
# )
|
176
|
+
# end
|
177
|
+
#
|
178
|
+
# @param app the Rack application to run
|
179
|
+
# @param [Hash] options the Akephalos configuration options
|
180
|
+
# @option options [Symbol] :browser (:firefox_3_6) the browser
|
181
|
+
# compatibility mode to run in. Available options:
|
182
|
+
# :firefox_3_6
|
183
|
+
# :firefox_3
|
184
|
+
# :ie6
|
185
|
+
# :ie7
|
186
|
+
# :ie8
|
187
|
+
#
|
188
|
+
# @option options [true, false] :validate_scripts (true) whether to raise
|
189
|
+
# exceptions on script errors
|
190
|
+
#
|
191
|
+
def initialize(app, options = {})
|
150
192
|
@app = app
|
193
|
+
@options = options
|
151
194
|
@rack_server = Capybara::Server.new(@app)
|
152
195
|
@rack_server.boot if Capybara.run_server
|
153
196
|
end
|
@@ -165,8 +208,19 @@ class Capybara::Driver::Akephalos < Capybara::Driver::Base
|
|
165
208
|
end
|
166
209
|
|
167
210
|
# @return [String] the page's modified source
|
211
|
+
# page.modified_source will return a string with
|
212
|
+
# html entities converted into the unicode equivalent
|
213
|
+
# but the string will be marked as ASCII-8BIT
|
214
|
+
# which causes conversion issues so we force the encoding
|
215
|
+
# to UTF-8 (ruby 1.9 only)
|
168
216
|
def body
|
169
|
-
page.modified_source
|
217
|
+
body_source = page.modified_source
|
218
|
+
|
219
|
+
if body_source.respond_to?(:force_encoding)
|
220
|
+
body_source.force_encoding("UTF-8")
|
221
|
+
else
|
222
|
+
body_source
|
223
|
+
end
|
170
224
|
end
|
171
225
|
|
172
226
|
# @return [Hash{String => String}] the page's response headers
|
@@ -190,8 +244,15 @@ class Capybara::Driver::Akephalos < Capybara::Driver::Base
|
|
190
244
|
end
|
191
245
|
|
192
246
|
# Clear all cookie session data.
|
247
|
+
# @deprecated This method is deprecated in Capybara's master branch. Use
|
248
|
+
# {#reset!} instead.
|
193
249
|
def cleanup!
|
194
|
-
|
250
|
+
reset!
|
251
|
+
end
|
252
|
+
|
253
|
+
# Clear all cookie session data.
|
254
|
+
def reset!
|
255
|
+
cookies.clear
|
195
256
|
end
|
196
257
|
|
197
258
|
# @return [String] the page's current URL
|
@@ -232,7 +293,26 @@ class Capybara::Driver::Akephalos < Capybara::Driver::Base
|
|
232
293
|
|
233
294
|
# @return the browser
|
234
295
|
def browser
|
235
|
-
|
296
|
+
@browser ||= Akephalos::Client.new(@options)
|
297
|
+
end
|
298
|
+
|
299
|
+
# @return the session cookies
|
300
|
+
def cookies
|
301
|
+
browser.cookies
|
302
|
+
end
|
303
|
+
|
304
|
+
# @return [String] the current user agent string
|
305
|
+
def user_agent
|
306
|
+
browser.user_agent
|
307
|
+
end
|
308
|
+
|
309
|
+
# Set the User-Agent header for this session. If :default is given, the
|
310
|
+
# User-Agent header will be reset to the default browser's user agent.
|
311
|
+
#
|
312
|
+
# @param [:default] user_agent the default user agent
|
313
|
+
# @param [String] user_agent the user agent string to use
|
314
|
+
def user_agent=(user_agent)
|
315
|
+
browser.user_agent = user_agent
|
236
316
|
end
|
237
317
|
|
238
318
|
# Disable waiting in Capybara, since waiting is handled directly by
|
@@ -252,3 +332,7 @@ class Capybara::Driver::Akephalos < Capybara::Driver::Base
|
|
252
332
|
end
|
253
333
|
|
254
334
|
end
|
335
|
+
|
336
|
+
Capybara.register_driver :akephalos do |app|
|
337
|
+
Capybara::Driver::Akephalos.new(app)
|
338
|
+
end
|
data/lib/akephalos/client.rb
CHANGED
@@ -10,6 +10,7 @@ else
|
|
10
10
|
require 'akephalos/page'
|
11
11
|
require 'akephalos/node'
|
12
12
|
|
13
|
+
require 'akephalos/client/cookies'
|
13
14
|
require 'akephalos/client/filter'
|
14
15
|
|
15
16
|
module Akephalos
|
@@ -18,34 +19,55 @@ else
|
|
18
19
|
# point for all interaction with the browser, exposing its current page and
|
19
20
|
# allowing navigation.
|
20
21
|
class Client
|
21
|
-
java_import 'com.gargoylesoftware.htmlunit.NicelyResynchronizingAjaxController'
|
22
|
-
java_import 'com.gargoylesoftware.htmlunit.SilentCssErrorHandler'
|
23
22
|
|
23
|
+
# @return [Akephalos::Page] the current page
|
24
24
|
attr_reader :page
|
25
25
|
|
26
|
-
|
26
|
+
# @return [HtmlUnit::BrowserVersion] the configured browser version
|
27
|
+
attr_reader :browser_version
|
28
|
+
|
29
|
+
# @return [true/false] whether to raise errors on javascript failures
|
30
|
+
attr_reader :validate_scripts
|
31
|
+
|
32
|
+
# The default configuration options for a new Client.
|
33
|
+
DEFAULT_OPTIONS = {
|
34
|
+
:browser => :firefox_3_6,
|
35
|
+
:validate_scripts => true
|
36
|
+
}
|
37
|
+
|
38
|
+
# Map of browser version symbols to their HtmlUnit::BrowserVersion
|
39
|
+
# instances.
|
40
|
+
BROWSER_VERSIONS = {
|
41
|
+
:ie6 => HtmlUnit::BrowserVersion::INTERNET_EXPLORER_6,
|
42
|
+
:ie7 => HtmlUnit::BrowserVersion::INTERNET_EXPLORER_7,
|
43
|
+
:ie8 => HtmlUnit::BrowserVersion::INTERNET_EXPLORER_8,
|
44
|
+
:firefox_3 => HtmlUnit::BrowserVersion::FIREFOX_3,
|
45
|
+
:firefox_3_6 => HtmlUnit::BrowserVersion::FIREFOX_3_6
|
46
|
+
}
|
47
|
+
|
48
|
+
# @param [Hash] options the configuration options for this client
|
49
|
+
#
|
50
|
+
# @option options [Symbol] :browser (:firefox_3_6) the browser version (
|
51
|
+
# see BROWSER_VERSIONS)
|
52
|
+
#
|
53
|
+
# @option options [true, false] :validate_scripts (true) whether to raise
|
54
|
+
# errors on javascript errors
|
55
|
+
def initialize(options = {})
|
56
|
+
process_options!(options)
|
57
|
+
|
27
58
|
@_client = java.util.concurrent.FutureTask.new do
|
28
|
-
client = WebClient.new
|
59
|
+
client = HtmlUnit::WebClient.new(browser_version)
|
29
60
|
|
30
61
|
Filter.new(client)
|
31
|
-
client.
|
32
|
-
client.
|
33
|
-
|
62
|
+
client.setThrowExceptionOnFailingStatusCode(false)
|
63
|
+
client.setAjaxController(HtmlUnit::NicelyResynchronizingAjaxController.new)
|
64
|
+
client.setCssErrorHandler(HtmlUnit::SilentCssErrorHandler.new)
|
65
|
+
client.setThrowExceptionOnScriptError(validate_scripts)
|
34
66
|
client
|
35
67
|
end
|
36
68
|
Thread.new { @_client.run }
|
37
69
|
end
|
38
70
|
|
39
|
-
# Set the global configuration settings for Akephalos.
|
40
|
-
#
|
41
|
-
# @note This is only used when communicating over DRb, since just a
|
42
|
-
# single client instance is exposed.
|
43
|
-
# @param [Hash] config the configuration settings
|
44
|
-
# @return [Hash] the configuration
|
45
|
-
def configuration=(config)
|
46
|
-
Akephalos.configuration = config
|
47
|
-
end
|
48
|
-
|
49
71
|
# Visit the requested URL and return the page.
|
50
72
|
#
|
51
73
|
# @param [String] url the URL to load
|
@@ -55,9 +77,29 @@ else
|
|
55
77
|
page
|
56
78
|
end
|
57
79
|
|
58
|
-
#
|
59
|
-
def
|
60
|
-
client.getCookieManager
|
80
|
+
# @return [Cookies] the cookies for this session
|
81
|
+
def cookies
|
82
|
+
@cookies ||= Cookies.new(client.getCookieManager)
|
83
|
+
end
|
84
|
+
|
85
|
+
# @return [String] the current user agent string
|
86
|
+
def user_agent
|
87
|
+
@user_agent || client.getBrowserVersion.getUserAgent
|
88
|
+
end
|
89
|
+
|
90
|
+
# Set the User-Agent header for this session. If :default is given, the
|
91
|
+
# User-Agent header will be reset to the default browser's user agent.
|
92
|
+
#
|
93
|
+
# @param [:default] user_agent the default user agent
|
94
|
+
# @param [String] user_agent the user agent string to use
|
95
|
+
def user_agent=(user_agent)
|
96
|
+
if user_agent == :default
|
97
|
+
@user_agent = nil
|
98
|
+
client.removeRequestHeader("User-Agent")
|
99
|
+
else
|
100
|
+
@user_agent = user_agent
|
101
|
+
client.addRequestHeader("User-Agent", user_agent)
|
102
|
+
end
|
61
103
|
end
|
62
104
|
|
63
105
|
# @return [Page] the current page
|
@@ -77,6 +119,23 @@ else
|
|
77
119
|
@page
|
78
120
|
end
|
79
121
|
|
122
|
+
# @return [true, false] whether javascript errors will raise exceptions
|
123
|
+
def validate_scripts?
|
124
|
+
!!validate_scripts
|
125
|
+
end
|
126
|
+
|
127
|
+
# Merges the DEFAULT_OPTIONS with those provided to initialize the Client
|
128
|
+
# state, namely, its browser version and whether it should
|
129
|
+
# validate scripts.
|
130
|
+
#
|
131
|
+
# @param [Hash] options the options to process
|
132
|
+
def process_options!(options)
|
133
|
+
options = DEFAULT_OPTIONS.merge(options)
|
134
|
+
|
135
|
+
@browser_version = BROWSER_VERSIONS.fetch(options.delete(:browser))
|
136
|
+
@validate_scripts = options.delete(:validate_scripts)
|
137
|
+
end
|
138
|
+
|
80
139
|
private
|
81
140
|
|
82
141
|
# Call the future set up in #initialize and return the WebCLient
|
@@ -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
|
@@ -9,11 +9,7 @@ module Akephalos
|
|
9
9
|
# found. If no filters are defined, or no filters match the request, then
|
10
10
|
# the response will bubble up to HtmlUnit for the normal request/response
|
11
11
|
# cycle.
|
12
|
-
class Filter < WebConnectionWrapper
|
13
|
-
java_import 'com.gargoylesoftware.htmlunit.util.NameValuePair'
|
14
|
-
java_import 'com.gargoylesoftware.htmlunit.WebResponseData'
|
15
|
-
java_import 'com.gargoylesoftware.htmlunit.WebResponseImpl'
|
16
|
-
|
12
|
+
class Filter < HtmlUnit::Util::WebConnectionWrapper
|
17
13
|
# Filters an outgoing request, and if a match is found, returns the mock
|
18
14
|
# response.
|
19
15
|
#
|
@@ -23,14 +19,16 @@ module Akephalos
|
|
23
19
|
def filter(request)
|
24
20
|
if filter = find_filter(request)
|
25
21
|
start_time = Time.now
|
26
|
-
headers = filter[:headers].map
|
27
|
-
|
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(
|
28
26
|
filter[:body].to_s.to_java_bytes,
|
29
27
|
filter[:status],
|
30
28
|
HTTP_STATUS_CODES.fetch(filter[:status], "Unknown"),
|
31
29
|
headers
|
32
30
|
)
|
33
|
-
WebResponseImpl.new(response, request, Time.now - start_time)
|
31
|
+
HtmlUnit::WebResponseImpl.new(response, request, Time.now - start_time)
|
34
32
|
end
|
35
33
|
end
|
36
34
|
|
data/lib/akephalos/console.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Begin a new Capybara session, by default connecting to localhost on port
|
2
2
|
# 3000.
|
3
3
|
def session
|
4
|
-
Capybara.app_host
|
4
|
+
Capybara.app_host ||= "http://localhost:3000"
|
5
5
|
@session ||= Capybara::Session.new(:Akephalos)
|
6
6
|
end
|
7
7
|
alias page session
|
data/lib/akephalos/htmlunit.rb
CHANGED
@@ -13,15 +13,23 @@ java.lang.System.setProperty("org.apache.commons.logging.Log", "org.apache.commo
|
|
13
13
|
java.lang.System.setProperty("org.apache.commons.logging.simplelog.defaultlog", "fatal")
|
14
14
|
java.lang.System.setProperty("org.apache.commons.logging.simplelog.showdatetime", "true")
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
java_import
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
com.gargoylesoftware.htmlunit.
|
23
|
-
|
24
|
-
|
25
|
-
com.gargoylesoftware.htmlunit.
|
26
|
-
|
27
|
-
|
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
|
+
end
|
@@ -1,28 +1,30 @@
|
|
1
|
-
|
2
|
-
class
|
1
|
+
module HtmlUnit
|
2
|
+
# Reopen HtmlUnit's HttpMethod class to add convenience methods.
|
3
|
+
class HttpMethod
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
25
27
|
end
|
26
|
-
end
|
27
28
|
|
29
|
+
end
|
28
30
|
end
|
data/lib/akephalos/node.rb
CHANGED
@@ -11,7 +11,11 @@ module Akephalos
|
|
11
11
|
|
12
12
|
# @return [true, false] whether the element is checked
|
13
13
|
def checked?
|
14
|
-
@_node.isChecked
|
14
|
+
if @_node.respond_to?(:isChecked)
|
15
|
+
@_node.isChecked
|
16
|
+
else
|
17
|
+
!! self[:checked]
|
18
|
+
end
|
15
19
|
end
|
16
20
|
|
17
21
|
# @return [String] inner text of the node
|
@@ -37,11 +41,13 @@ module Akephalos
|
|
37
41
|
case tag_name
|
38
42
|
when "select"
|
39
43
|
if self[:multiple]
|
40
|
-
|
44
|
+
selected_options.map { |option| option.value }
|
41
45
|
else
|
42
46
|
selected_option = @_node.selected_options.first
|
43
|
-
selected_option ? selected_option.
|
47
|
+
selected_option ? Node.new(selected_option).value : nil
|
44
48
|
end
|
49
|
+
when "option"
|
50
|
+
self[:value] || text
|
45
51
|
when "textarea"
|
46
52
|
@_node.getText
|
47
53
|
else
|
@@ -55,28 +61,43 @@ module Akephalos
|
|
55
61
|
def value=(value)
|
56
62
|
case tag_name
|
57
63
|
when "textarea"
|
58
|
-
@_node.setText(
|
64
|
+
@_node.setText("")
|
65
|
+
type(value)
|
59
66
|
when "input"
|
60
|
-
|
67
|
+
if file_input?
|
68
|
+
@_node.setValueAttribute(value)
|
69
|
+
else
|
70
|
+
@_node.setValueAttribute("")
|
71
|
+
type(value)
|
72
|
+
end
|
61
73
|
end
|
62
74
|
end
|
63
75
|
|
64
|
-
#
|
76
|
+
# Types each character into a text or input field.
|
65
77
|
#
|
66
|
-
# @
|
67
|
-
def
|
68
|
-
|
78
|
+
# @param [String] value the string to type
|
79
|
+
def type(value)
|
80
|
+
value.each_char do |c|
|
81
|
+
@_node.type(c)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# @return [true, false] whether the node allows multiple-option selection (if the node is a select).
|
86
|
+
def multiple_select?
|
87
|
+
!self[:multiple].nil?
|
88
|
+
end
|
69
89
|
|
70
|
-
|
90
|
+
# @return [true, false] whether the node is a file input
|
91
|
+
def file_input?
|
92
|
+
tag_name == "input" && @_node.getAttribute("type") == "file"
|
71
93
|
end
|
72
94
|
|
73
|
-
|
95
|
+
|
96
|
+
# Unselect an option.
|
74
97
|
#
|
75
98
|
# @return [true, false] whether the unselection was successful
|
76
|
-
def
|
77
|
-
|
78
|
-
|
79
|
-
opt && opt.setSelected(false)
|
99
|
+
def unselect
|
100
|
+
@_node.setSelected(false)
|
80
101
|
end
|
81
102
|
|
82
103
|
# Return the option elements for a select box.
|
@@ -114,6 +135,16 @@ module Akephalos
|
|
114
135
|
@_node.isDisplayed
|
115
136
|
end
|
116
137
|
|
138
|
+
# @return [true, false] whether the node is selected to the user accounting
|
139
|
+
# for CSS.
|
140
|
+
def selected?
|
141
|
+
if @_node.respond_to?(:isSelected)
|
142
|
+
@_node.isSelected
|
143
|
+
else
|
144
|
+
!! self[:selected]
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
117
148
|
# Click the node and then wait for any triggered JavaScript callbacks to
|
118
149
|
# fire.
|
119
150
|
def click
|
@@ -131,6 +162,11 @@ module Akephalos
|
|
131
162
|
@nodes << nodes
|
132
163
|
nodes
|
133
164
|
end
|
165
|
+
|
166
|
+
# @return [String] the XPath expression for this node
|
167
|
+
def xpath
|
168
|
+
@_node.getCanonicalXPath
|
169
|
+
end
|
134
170
|
end
|
135
171
|
|
136
172
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'socket'
|
1
2
|
require 'drb/drb'
|
2
3
|
|
3
4
|
# We need to define our own NativeException class for the cases when a native
|
@@ -14,44 +15,78 @@ module Akephalos
|
|
14
15
|
# client.visit "http://www.oinopa.com"
|
15
16
|
# client.page.source # => "<!DOCTYPE html PUBLIC..."
|
16
17
|
class RemoteClient
|
17
|
-
@
|
18
|
+
# @return [DRbObject] a new instance of Akephalos::Client from the DRb
|
19
|
+
# server
|
20
|
+
def self.new(options = {})
|
21
|
+
manager.new_client(options)
|
22
|
+
end
|
18
23
|
|
19
|
-
#
|
20
|
-
# instance.
|
24
|
+
# Starts a remove JRuby DRb server unless already running and returns an
|
25
|
+
# instance of Akephalos::ClientManager.
|
21
26
|
#
|
22
|
-
# @return [DRbObject]
|
23
|
-
def self.
|
24
|
-
|
27
|
+
# @return [DRbObject] an instance of Akephalos::ClientManager
|
28
|
+
def self.manager
|
29
|
+
return @manager if defined?(@manager)
|
30
|
+
|
31
|
+
server_port = start!
|
32
|
+
|
25
33
|
DRb.start_service
|
26
|
-
|
34
|
+
manager = DRbObject.new_with_uri("druby://127.0.0.1:#{server_port}")
|
35
|
+
|
27
36
|
# We want to share our local configuration with the remote server
|
28
37
|
# process, so we share an undumped version of our configuration. This
|
29
38
|
# lets us continue to make changes locally and have them reflected in the
|
30
39
|
# remote process.
|
31
|
-
|
32
|
-
|
40
|
+
manager.configuration = Akephalos.configuration.extend(DRbUndumped)
|
41
|
+
|
42
|
+
@manager = manager
|
33
43
|
end
|
34
44
|
|
35
45
|
# Start a remote server process and return when it is available for use.
|
36
46
|
def self.start!
|
37
|
-
|
38
|
-
|
39
|
-
|
47
|
+
port = find_available_port
|
48
|
+
|
49
|
+
remote_client = IO.popen("#{Akephalos::BIN_DIR + 'akephalos'} #{port}")
|
40
50
|
|
41
51
|
# Set up a monitor thread to detect if the forked server exits
|
42
52
|
# prematurely.
|
43
|
-
server_monitor = Thread.new { Thread.current[:exited] = Process.wait }
|
53
|
+
server_monitor = Thread.new { Thread.current[:exited] = Process.wait(remote_client.pid) }
|
44
54
|
|
45
55
|
# Wait for the server to be accessible on the socket we specified.
|
46
|
-
until
|
56
|
+
until responsive?(port)
|
47
57
|
exit!(1) if server_monitor[:exited]
|
48
|
-
sleep
|
58
|
+
sleep 0.5
|
49
59
|
end
|
50
60
|
server_monitor.kill
|
51
61
|
|
52
62
|
# Ensure that the remote server shuts down gracefully when we are
|
53
63
|
# finished.
|
54
|
-
at_exit { Process.kill(:INT, remote_client
|
64
|
+
at_exit { Process.kill(:INT, remote_client.pid) }
|
65
|
+
|
66
|
+
port
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# @api private
|
72
|
+
# @param [Integer] port the port to check for responsiveness
|
73
|
+
# @return [true, false] whether the port is responsive
|
74
|
+
def self.responsive?(port)
|
75
|
+
socket = TCPSocket.open('127.0.0.1', port)
|
76
|
+
true
|
77
|
+
rescue Errno::ECONNREFUSED
|
78
|
+
false
|
79
|
+
ensure
|
80
|
+
socket.close if socket
|
81
|
+
end
|
82
|
+
|
83
|
+
# @api private
|
84
|
+
# @return [Integer] the next available port
|
85
|
+
def self.find_available_port
|
86
|
+
server = TCPServer.new('127.0.0.1', 0)
|
87
|
+
server.addr[1]
|
88
|
+
ensure
|
89
|
+
server.close if server
|
55
90
|
end
|
56
91
|
end
|
57
92
|
end
|
data/lib/akephalos/server.rb
CHANGED
@@ -14,21 +14,66 @@ class NameError::Message
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
[
|
17
|
+
[
|
18
|
+
Akephalos::Page,
|
19
|
+
Akephalos::Node,
|
20
|
+
Akephalos::Client::Cookies,
|
21
|
+
Akephalos::Client::Cookies::Cookie
|
22
|
+
].each { |klass| klass.send(:include, DRbUndumped) }
|
18
23
|
|
19
24
|
module Akephalos
|
20
25
|
|
26
|
+
# The ClientManager is shared over DRb with the remote process, and
|
27
|
+
# facilitates communication between the processes.
|
28
|
+
#
|
29
|
+
# @api private
|
30
|
+
class ClientManager
|
31
|
+
include DRbUndumped
|
32
|
+
|
33
|
+
# @return [Akephalos::Client] a new client instance
|
34
|
+
def self.new_client(options = {})
|
35
|
+
# Store the client to ensure it isn't prematurely garbage collected.
|
36
|
+
@client = Client.new(options)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Set the global configuration settings for Akephalos.
|
40
|
+
#
|
41
|
+
# @param [Hash] config the configuration settings
|
42
|
+
# @return [Hash] the configuration
|
43
|
+
def self.configuration=(config)
|
44
|
+
Akephalos.configuration = config
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
21
49
|
# Akephalos::Server is used by `akephalos --server` to start a DRb server
|
22
|
-
# serving
|
50
|
+
# serving Akephalos::ClientManager.
|
23
51
|
class Server
|
24
|
-
|
52
|
+
|
53
|
+
# Start DRb service for Akephalos::ClientManager.
|
25
54
|
#
|
26
|
-
# @param [String]
|
27
|
-
def self.start!(
|
28
|
-
|
29
|
-
DRb.start_service("
|
55
|
+
# @param [String] port attach server to
|
56
|
+
def self.start!(port)
|
57
|
+
abort_on_parent_exit!
|
58
|
+
DRb.start_service("druby://127.0.0.1:#{port}", ClientManager)
|
30
59
|
DRb.thread.join
|
31
60
|
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# Exit if STDIN is no longer readable, which corresponds to the process
|
65
|
+
# which started the server exiting prematurely.
|
66
|
+
#
|
67
|
+
# @api private
|
68
|
+
def self.abort_on_parent_exit!
|
69
|
+
Thread.new do
|
70
|
+
begin
|
71
|
+
STDIN.read
|
72
|
+
rescue IOError
|
73
|
+
exit
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
32
77
|
end
|
33
78
|
|
34
79
|
end
|
data/lib/akephalos/version.rb
CHANGED
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 2
|
8
|
-
-
|
9
|
-
version: 0.2.
|
8
|
+
- 5
|
9
|
+
version: 0.2.5
|
10
10
|
platform: java
|
11
11
|
authors:
|
12
12
|
- Bernerd Schaefer
|
@@ -14,27 +14,29 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date:
|
17
|
+
date: 2011-02-04 00:00:00 +01:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: capybara
|
22
22
|
prerelease: false
|
23
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
24
25
|
requirements:
|
25
26
|
- - ~>
|
26
27
|
- !ruby/object:Gem::Version
|
27
28
|
segments:
|
28
29
|
- 0
|
29
|
-
-
|
30
|
-
-
|
31
|
-
version: 0.
|
30
|
+
- 4
|
31
|
+
- 0
|
32
|
+
version: 0.4.0
|
32
33
|
type: :runtime
|
33
34
|
version_requirements: *id001
|
34
35
|
- !ruby/object:Gem::Dependency
|
35
36
|
name: sinatra
|
36
37
|
prerelease: false
|
37
38
|
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
38
40
|
requirements:
|
39
41
|
- - ">="
|
40
42
|
- !ruby/object:Gem::Version
|
@@ -47,14 +49,15 @@ dependencies:
|
|
47
49
|
name: rspec
|
48
50
|
prerelease: false
|
49
51
|
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
50
53
|
requirements:
|
51
|
-
- - "
|
54
|
+
- - ">="
|
52
55
|
- !ruby/object:Gem::Version
|
53
56
|
segments:
|
54
|
-
-
|
57
|
+
- 2
|
55
58
|
- 3
|
56
59
|
- 0
|
57
|
-
version:
|
60
|
+
version: 2.3.0
|
58
61
|
type: :development
|
59
62
|
version_requirements: *id003
|
60
63
|
description: Headless Browser for Integration Testing with Capybara
|
@@ -67,6 +70,7 @@ extra_rdoc_files: []
|
|
67
70
|
|
68
71
|
files:
|
69
72
|
- lib/akephalos/capybara.rb
|
73
|
+
- lib/akephalos/client/cookies.rb
|
70
74
|
- lib/akephalos/client/filter.rb
|
71
75
|
- lib/akephalos/client.rb
|
72
76
|
- lib/akephalos/configuration.rb
|
@@ -100,6 +104,7 @@ files:
|
|
100
104
|
- src/htmlunit/xml-apis-1.3.04.jar
|
101
105
|
- README.md
|
102
106
|
- MIT_LICENSE
|
107
|
+
- bin/akephalos
|
103
108
|
has_rdoc: true
|
104
109
|
homepage: http://bernerdschaefer.github.com/akephalos
|
105
110
|
licenses: []
|
@@ -111,6 +116,7 @@ require_paths:
|
|
111
116
|
- lib
|
112
117
|
- src
|
113
118
|
required_ruby_version: !ruby/object:Gem::Requirement
|
119
|
+
none: false
|
114
120
|
requirements:
|
115
121
|
- - ">="
|
116
122
|
- !ruby/object:Gem::Version
|
@@ -118,6 +124,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
118
124
|
- 0
|
119
125
|
version: "0"
|
120
126
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
|
+
none: false
|
121
128
|
requirements:
|
122
129
|
- - ">="
|
123
130
|
- !ruby/object:Gem::Version
|
@@ -129,7 +136,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
129
136
|
requirements: []
|
130
137
|
|
131
138
|
rubyforge_project: akephalos
|
132
|
-
rubygems_version: 1.3.
|
139
|
+
rubygems_version: 1.3.7
|
133
140
|
signing_key:
|
134
141
|
specification_version: 3
|
135
142
|
summary: Headless Browser for Integration Testing with Capybara
|