akephalos 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Akephalos
2
2
  Akephalos is a full-stack headless browser for integration testing with
3
- Capybara. It is built on top of [HtmlUnit](http://htmlunit.sourceforge.com),
3
+ Capybara. It is built on top of [HtmlUnit](http://htmlunit.sourceforge.net),
4
4
  a GUI-less browser for the Java platform, but can be run on both JRuby and
5
5
  MRI with no need for JRuby to be installed on the system.
6
6
 
@@ -50,6 +50,7 @@ Here's some sample RSpec code:
50
50
 
51
51
  ## Resources
52
52
 
53
+ * [API Documentation](http://bernerdschaefer.github.com/akephalos/api)
53
54
  * [Source code](http://github.com/bernerdschaefer/akephalos) and
54
55
  [issues](http://github.com/bernerdschaefer/akephalos/issues) are hosted on
55
56
  github.
@@ -1,7 +1,17 @@
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.
1
7
  class Capybara::Driver::Akephalos < Capybara::Driver::Base
2
8
 
9
+ # Akephalos-specific implementation for Capybara's Node class.
3
10
  class Node < Capybara::Node
4
11
 
12
+ # @api capybara
13
+ # @param [String] name attribute name
14
+ # @return [String] the attribute value
5
15
  def [](name)
6
16
  name = name.to_s
7
17
  case name
@@ -12,14 +22,22 @@ class Capybara::Driver::Akephalos < Capybara::Driver::Base
12
22
  end
13
23
  end
14
24
 
25
+ # @api capybara
26
+ # @return [String] the inner text of the node
15
27
  def text
16
28
  node.text
17
29
  end
18
30
 
31
+ # @api capybara
32
+ # @return [String] the form element's value
19
33
  def value
20
34
  node.value
21
35
  end
22
36
 
37
+ # Set the form element's value.
38
+ #
39
+ # @api capybara
40
+ # @param [String] value the form element's new value
23
41
  def set(value)
24
42
  if tag_name == 'textarea'
25
43
  node.value = value.to_s
@@ -34,6 +52,10 @@ class Capybara::Driver::Akephalos < Capybara::Driver::Base
34
52
  end
35
53
  end
36
54
 
55
+ # Select an option from a select box.
56
+ #
57
+ # @api capybara
58
+ # @param [String] option the option to select
37
59
  def select(option)
38
60
  result = node.select_option(option)
39
61
 
@@ -45,6 +67,10 @@ class Capybara::Driver::Akephalos < Capybara::Driver::Base
45
67
  end
46
68
  end
47
69
 
70
+ # Unselect an option from a select box.
71
+ #
72
+ # @api capybara
73
+ # @param [String] option the option to unselect
48
74
  def unselect(option)
49
75
  unless self[:multiple]
50
76
  raise Capybara::UnselectNotAllowed, "Cannot unselect option '#{option}' from single select box."
@@ -60,36 +86,54 @@ class Capybara::Driver::Akephalos < Capybara::Driver::Base
60
86
  end
61
87
  end
62
88
 
89
+ # Trigger an event on the element.
90
+ #
91
+ # @api capybara
92
+ # @param [String] event the event to trigger
63
93
  def trigger(event)
64
94
  node.fire_event(event.to_s)
65
95
  end
66
96
 
97
+ # @api capybara
98
+ # @return [String] the element's tag name
67
99
  def tag_name
68
100
  node.tag_name
69
101
  end
70
102
 
103
+ # @api capybara
104
+ # @return [true, false] the element's visiblity
71
105
  def visible?
72
106
  node.visible?
73
107
  end
74
108
 
109
+ # Drag the element on top of the target element.
110
+ #
111
+ # @api capybara
112
+ # @param [Node] element the target element
75
113
  def drag_to(element)
76
114
  trigger('mousedown')
77
115
  element.trigger('mousemove')
78
116
  element.trigger('mouseup')
79
117
  end
80
118
 
119
+ # Click the element.
81
120
  def click
82
121
  node.click
83
122
  end
84
123
 
85
124
  private
86
125
 
126
+ # Return all child nodes which match the selector criteria.
127
+ #
128
+ # @api capybara
129
+ # @return [Array<Node>] the matched nodes
87
130
  def all_unfiltered(selector)
88
131
  nodes = []
89
132
  node.find(selector).each { |node| nodes << Node.new(driver, node) }
90
133
  nodes
91
134
  end
92
135
 
136
+ # @return [String] the node's type attribute
93
137
  def type
94
138
  node[:type]
95
139
  end
@@ -97,6 +141,7 @@ class Capybara::Driver::Akephalos < Capybara::Driver::Base
97
141
 
98
142
  attr_reader :app, :rack_server
99
143
 
144
+ # @return [Client] an instance of Akephalos::Client
100
145
  def self.driver
101
146
  @driver ||= Akephalos::Client.new
102
147
  end
@@ -107,50 +152,101 @@ class Capybara::Driver::Akephalos < Capybara::Driver::Base
107
152
  @rack_server.boot if Capybara.run_server
108
153
  end
109
154
 
155
+ # Visit the given path in the browser.
156
+ #
157
+ # @param [String] path relative path to visit
110
158
  def visit(path)
111
159
  browser.visit(url(path))
112
160
  end
113
161
 
162
+ # @return [String] the page's original source
114
163
  def source
115
164
  page.source
116
165
  end
117
166
 
167
+ # @return [String] the page's modified source
118
168
  def body
119
169
  page.modified_source
120
170
  end
121
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
+ def cleanup!
194
+ browser.clear_cookies
195
+ end
196
+
197
+ # @return [String] the page's current URL
122
198
  def current_url
123
199
  page.current_url
124
200
  end
125
201
 
202
+ # Search for nodes which match the given XPath selector.
203
+ #
204
+ # @param [String] selector XPath query
205
+ # @return [Array<Node>] the matched nodes
126
206
  def find(selector)
127
207
  nodes = []
128
208
  page.find(selector).each { |node| nodes << Node.new(self, node) }
129
209
  nodes
130
210
  end
131
211
 
212
+ # Execute JavaScript against the current page, discarding any return value.
213
+ #
214
+ # @param [String] script the JavaScript to be executed
215
+ # @return [nil]
132
216
  def execute_script(script)
133
217
  page.execute_script script
134
218
  end
135
219
 
220
+ # Execute JavaScript against the current page and return the results.
221
+ #
222
+ # @param [String] script the JavaScript to be executed
223
+ # @return the result of the JavaScript
136
224
  def evaluate_script(script)
137
225
  page.evaluate_script script
138
226
  end
139
227
 
228
+ # @return the current page
140
229
  def page
141
230
  browser.page
142
231
  end
143
232
 
233
+ # @return the browser
144
234
  def browser
145
235
  self.class.driver
146
236
  end
147
237
 
238
+ # Disable waiting in Capybara, since waiting is handled directly by
239
+ # Akephalos.
240
+ #
241
+ # @return [false]
148
242
  def wait
149
243
  false
150
244
  end
151
245
 
152
- private
246
+ private
153
247
 
248
+ # @param [String] path
249
+ # @return [String] the absolute URL for the given path
154
250
  def url(path)
155
251
  rack_server.url(path)
156
252
  end
@@ -11,9 +11,12 @@ else
11
11
  require 'akephalos/node'
12
12
 
13
13
  require 'akephalos/client/filter'
14
- require 'akephalos/client/listener'
15
14
 
16
15
  module Akephalos
16
+
17
+ # Akephalos::Client wraps HtmlUnit's WebClient class. It is the main entry
18
+ # point for all interaction with the browser, exposing its current page and
19
+ # allowing navigation.
17
20
  class Client
18
21
  java_import 'com.gargoylesoftware.htmlunit.NicelyResynchronizingAjaxController'
19
22
  java_import 'com.gargoylesoftware.htmlunit.SilentCssErrorHandler'
@@ -25,7 +28,6 @@ else
25
28
  client = WebClient.new
26
29
 
27
30
  Filter.new(client)
28
- client.addWebWindowListener(Listener.new(self))
29
31
  client.setAjaxController(NicelyResynchronizingAjaxController.new)
30
32
  client.setCssErrorHandler(SilentCssErrorHandler.new)
31
33
 
@@ -34,15 +36,40 @@ else
34
36
  Thread.new { @_client.run }
35
37
  end
36
38
 
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
37
45
  def configuration=(config)
38
46
  Akephalos.configuration = config
39
47
  end
40
48
 
49
+ # Visit the requested URL and return the page.
50
+ #
51
+ # @param [String] url the URL to load
52
+ # @return [Page] the loaded page
41
53
  def visit(url)
42
54
  client.getPage(url)
43
55
  page
44
56
  end
45
57
 
58
+ # Clear all cookies for this browser session.
59
+ def clear_cookies
60
+ client.getCookieManager.clearCookies
61
+ end
62
+
63
+ # @return [Page] the current page
64
+ def page
65
+ self.page = client.getCurrentWindow.getTopWindow.getEnclosedPage
66
+ @page
67
+ end
68
+
69
+ # Update the current page.
70
+ #
71
+ # @param [HtmlUnit::HtmlPage] _page the new page
72
+ # @return [Page] the new page
46
73
  def page=(_page)
47
74
  if @page != _page
48
75
  @page = Page.new(_page)
@@ -51,6 +78,11 @@ else
51
78
  end
52
79
 
53
80
  private
81
+
82
+ # Call the future set up in #initialize and return the WebCLient
83
+ # instance.
84
+ #
85
+ # @return [HtmlUnit::WebClient] the WebClient instance
54
86
  def client
55
87
  @client ||= @_client.get.tap do |client|
56
88
  client.getCurrentWindow.getHistory.ignoreNewPages_.set(true)
@@ -1,23 +1,62 @@
1
1
  module Akephalos
2
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.
3
12
  class Filter < WebConnectionWrapper
4
13
  java_import 'com.gargoylesoftware.htmlunit.util.NameValuePair'
5
14
  java_import 'com.gargoylesoftware.htmlunit.WebResponseData'
6
15
  java_import 'com.gargoylesoftware.htmlunit.WebResponseImpl'
7
16
 
17
+ # Filters an outgoing request, and if a match is found, returns the mock
18
+ # response.
19
+ #
20
+ # @param [WebRequest] request the pending HTTP request
21
+ # @return [WebResponseImpl] when the request matches a defined filter
22
+ # @return [nil] when no filters match the request
8
23
  def filter(request)
9
- if filter = Akephalos.filters.find { |filter| request.http_method === filter[:method] && request.url.to_s =~ filter[:filter] }
24
+ if filter = find_filter(request)
10
25
  start_time = Time.now
11
26
  headers = filter[:headers].map { |name, value| NameValuePair.new(name.to_s, value.to_s) }
12
- response = WebResponseData.new(filter[:body].to_s.to_java_bytes, filter[:status], HTTP_STATUS_CODES.fetch(filter[:status], "Unknown"), headers)
27
+ response = WebResponseData.new(
28
+ filter[:body].to_s.to_java_bytes,
29
+ filter[:status],
30
+ HTTP_STATUS_CODES.fetch(filter[:status], "Unknown"),
31
+ headers
32
+ )
13
33
  WebResponseImpl.new(response, request, Time.now - start_time)
14
34
  end
15
35
  end
16
36
 
37
+ # Searches for a filter which matches the request's HTTP method and url.
38
+ #
39
+ # @param [WebRequest] request the pending HTTP request
40
+ # @return [Hash] when a filter matches the request
41
+ # @return [nil] when no filters match the request
42
+ def find_filter(request)
43
+ Akephalos.filters.find do |filter|
44
+ request.http_method === filter[:method] && request.url.to_s =~ filter[:filter]
45
+ end
46
+ end
47
+
48
+ # This method is called by WebClient when a page is requested, and will
49
+ # return a mock response if the request matches a defined filter or else
50
+ # return the actual response.
51
+ #
52
+ # @api htmlunit
53
+ # @param [WebRequest] request the pending HTTP request
54
+ # @return [WebResponseImpl]
17
55
  def getResponse(request)
18
56
  filter(request) || super
19
57
  end
20
58
 
59
+ # Map of status codes to their English descriptions.
21
60
  HTTP_STATUS_CODES = {
22
61
  100 => "Continue",
23
62
  101 => "Switching Protocols",
@@ -78,5 +117,6 @@ module Akephalos
78
117
  530 => "User access denied"
79
118
  }.freeze
80
119
  end
120
+
81
121
  end
82
122
  end
@@ -1,17 +1,44 @@
1
1
  module Akephalos
2
- def self.configuration
3
- @configuration ||= {}
4
- end
5
2
 
6
- def self.configuration=(config)
7
- @configuration = config
3
+ @configuration = {}
4
+
5
+ class << self
6
+ # @return [Hash] the configuration
7
+ attr_accessor :configuration
8
8
  end
9
9
 
10
10
  module Filters
11
+ # @return [Array] all defined filters
11
12
  def filters
12
13
  configuration[:filters] ||= []
13
14
  end
14
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
15
42
  def filter(method, regex, options = {})
16
43
  regex = Regexp.new(Regexp.escape(regex)) if regex.is_a?(String)
17
44
  filters << {:method => method, :filter => regex, :status => 200, :body => "", :headers => {}}.merge!(options)
@@ -1,3 +1,5 @@
1
+ # Begin a new Capybara session, by default connecting to localhost on port
2
+ # 3000.
1
3
  def session
2
4
  Capybara.app_host = "http://localhost:3000"
3
5
  @session ||= Capybara::Session.new(:Akephalos)
@@ -5,8 +7,11 @@ end
5
7
  alias page session
6
8
 
7
9
  module Akephalos
10
+ # Simple class for starting an IRB session.
8
11
  class Console
9
12
 
13
+ # Start an IRB session. Tries to load irb/completion, and also loads a
14
+ # .irbrc file if it exists.
10
15
  def self.start
11
16
  require 'irb'
12
17
 
@@ -1,4 +1,13 @@
1
+ # Reopen com.gargoylesoftware.htmlunit.HttpMethod to add convenience methods.
1
2
  class HttpMethod
3
+
4
+ # Loosely compare HttpMethod with another object, accepting either an
5
+ # HttpMethod instance or a symbol describing the method. Note that :any is a
6
+ # special symbol which will always return true.
7
+ #
8
+ # @param [HttpMethod] other an HtmlUnit HttpMethod object
9
+ # @param [Symbol] other a symbolized representation of an http method
10
+ # @return [true/false]
2
11
  def ===(other)
3
12
  case other
4
13
  when HttpMethod
@@ -15,4 +24,5 @@ class HttpMethod
15
24
  self == self.class::DELETE
16
25
  end
17
26
  end
27
+
18
28
  end
@@ -1,22 +1,38 @@
1
1
  module Akephalos
2
+
3
+ # Akephalos::Node wraps HtmlUnit's DOMNode class, providing a simple API for
4
+ # interacting with an element on the page.
2
5
  class Node
6
+ # @param [HtmlUnit::DOMNode] node
3
7
  def initialize(node)
4
8
  @nodes = []
5
9
  @_node = node
6
10
  end
7
11
 
12
+ # @return [true, false] whether the element is checked
8
13
  def checked?
9
14
  @_node.isChecked
10
15
  end
11
16
 
17
+ # @return [String] inner text of the node
12
18
  def text
13
19
  @_node.asText
14
20
  end
15
21
 
22
+ # Return the value of the node's attribute.
23
+ #
24
+ # @param [String] name attribute on node
25
+ # @return [String] the value of the named attribute
26
+ # @return [nil] when the node does not have the named attribute
16
27
  def [](name)
17
28
  @_node.hasAttribute(name.to_s) ? @_node.getAttribute(name.to_s) : nil
18
29
  end
19
30
 
31
+ # Return the value of a form element. If the element is a select box and
32
+ # has "multiple" declared as an attribute, then all selected options will
33
+ # be returned as an array.
34
+ #
35
+ # @return [String, Array<String>] the node's value
20
36
  def value
21
37
  case tag_name
22
38
  when "select"
@@ -33,6 +49,9 @@ module Akephalos
33
49
  end
34
50
  end
35
51
 
52
+ # Set the value of the form input.
53
+ #
54
+ # @param [String] value
36
55
  def value=(value)
37
56
  case tag_name
38
57
  when "textarea"
@@ -42,48 +61,76 @@ module Akephalos
42
61
  end
43
62
  end
44
63
 
64
+ # Select an option from a select box by its value.
65
+ #
66
+ # @return [true, false] whether the selection was successful
45
67
  def select_option(option)
46
68
  opt = @_node.getOptions.detect { |o| o.asText == option }
47
69
 
48
70
  opt && opt.setSelected(true)
49
71
  end
50
72
 
73
+ # Unselect an option from a select box by its value.
74
+ #
75
+ # @return [true, false] whether the unselection was successful
51
76
  def unselect_option(option)
52
77
  opt = @_node.getOptions.detect { |o| o.asText == option }
53
78
 
54
79
  opt && opt.setSelected(false)
55
80
  end
56
81
 
82
+ # Return the option elements for a select box.
83
+ #
84
+ # @return [Array<Node>] the options
57
85
  def options
58
86
  @_node.getOptions.map { |node| Node.new(node) }
59
87
  end
60
88
 
89
+ # Return the selected option elements for a select box.
90
+ #
91
+ # @return [Array<Node>] the selected options
61
92
  def selected_options
62
93
  @_node.getSelectedOptions.map { |node| Node.new(node) }
63
94
  end
64
95
 
96
+ # Fire a JavaScript event on the current node. Note that you should not
97
+ # prefix event names with "on", so:
98
+ #
99
+ # link.fire_event('mousedown')
100
+ #
101
+ # @param [String] JavaScript event name
65
102
  def fire_event(name)
66
103
  @_node.fireEvent(name)
67
104
  end
68
105
 
106
+ # @return [String] the node's tag name
69
107
  def tag_name
70
108
  @_node.getNodeName
71
109
  end
72
110
 
111
+ # @return [true, false] whether the node is visible to the user accounting
112
+ # for CSS.
73
113
  def visible?
74
114
  @_node.isDisplayed
75
115
  end
76
116
 
117
+ # Click the node and then wait for any triggered JavaScript callbacks to
118
+ # fire.
77
119
  def click
78
120
  @_node.click
79
121
  @_node.getPage.getEnclosingWindow.getJobManager.waitForJobs(1000)
80
122
  @_node.getPage.getEnclosingWindow.getJobManager.waitForJobsStartingBefore(1000)
81
123
  end
82
124
 
125
+ # Search for child nodes which match the given XPath selector.
126
+ #
127
+ # @param [String] selector an XPath selector
128
+ # @return [Array<Node>] the matched nodes
83
129
  def find(selector)
84
130
  nodes = @_node.getByXPath(selector).map { |node| Node.new(node) }
85
131
  @nodes << nodes
86
132
  nodes
87
133
  end
88
134
  end
135
+
89
136
  end
@@ -1,39 +1,113 @@
1
1
  module Akephalos
2
+
3
+ # Akephalos::Page wraps HtmlUnit's HtmlPage class, exposing an API for
4
+ # interacting with a page in the browser.
2
5
  class Page
6
+ # @param [HtmlUnit::HtmlPage] page
3
7
  def initialize(page)
4
8
  @nodes = []
5
9
  @_page = page
6
10
  end
7
11
 
12
+ # Search for nodes which match the given XPath selector.
13
+ #
14
+ # @param [String] selector an XPath selector
15
+ # @return [Array<Node>] the matched nodes
8
16
  def find(selector)
9
- nodes = @_page.getByXPath(selector).map { |node| Node.new(node) }
17
+ nodes = current_frame.getByXPath(selector).map { |node| Node.new(node) }
10
18
  @nodes << nodes
11
19
  nodes
12
20
  end
13
21
 
22
+ # Return the page's source, including any JavaScript-triggered DOM changes.
23
+ #
24
+ # @return [String] the page's modified source
14
25
  def modified_source
15
- @_page.asXml
26
+ current_frame.asXml
16
27
  end
17
28
 
29
+ # Return the page's source as returned by the web server.
30
+ #
31
+ # @return [String] the page's original source
18
32
  def source
19
- @_page.getWebResponse.getContentAsString
33
+ current_frame.getWebResponse.getContentAsString
34
+ end
35
+
36
+ # @return [Hash{String => String}] the page's response headers
37
+ def response_headers
38
+ headers = current_frame.getWebResponse.getResponseHeaders.map do |header|
39
+ [header.getName, header.getValue]
40
+ end
41
+ Hash[*headers.flatten]
42
+ end
43
+
44
+ # @return [Integer] the response's status code
45
+ def status_code
46
+ current_frame.getWebResponse.getStatusCode
47
+ end
48
+
49
+ # Execute the given block in the context of the frame specified.
50
+ #
51
+ # @param [String] frame_id the frame's id
52
+ # @return [true] if the frame is found
53
+ # @return [nil] if the frame is not found
54
+ def within_frame(frame_id)
55
+ return unless @current_frame = find_frame(frame_id)
56
+ yield
57
+ true
58
+ ensure
59
+ @current_frame = nil
20
60
  end
21
61
 
62
+ # @return [String] the current page's URL.
22
63
  def current_url
23
- @_page.getWebResponse.getRequestSettings.getUrl.toString
64
+ current_frame.getWebResponse.getRequestSettings.getUrl.toString
24
65
  end
25
66
 
67
+ # Execute JavaScript against the current page, discarding any return value.
68
+ #
69
+ # @param [String] script the JavaScript to be executed
70
+ # @return [nil]
26
71
  def execute_script(script)
27
- @_page.executeJavaScript(script)
72
+ current_frame.executeJavaScript(script)
28
73
  nil
29
74
  end
30
75
 
76
+ # Execute JavaScript against the current page and return the results.
77
+ #
78
+ # @param [String] script the JavaScript to be executed
79
+ # @return the result of the JavaScript
31
80
  def evaluate_script(script)
32
- @_page.executeJavaScript(script).getJavaScriptResult
81
+ current_frame.executeJavaScript(script).getJavaScriptResult
33
82
  end
34
83
 
84
+ # Compare this page with an HtmlUnit page.
85
+ #
86
+ # @param [HtmlUnit::HtmlPage] other an HtmlUnit page
87
+ # @return [true, false]
35
88
  def ==(other)
36
89
  @_page == other
37
90
  end
91
+
92
+ private
93
+
94
+ # Return the current frame. Usually just @_page, except when inside of the
95
+ # within_frame block.
96
+ #
97
+ # @return [HtmlUnit::HtmlPage] the current frame
98
+ def current_frame
99
+ @current_frame || @_page
100
+ end
101
+
102
+ # @param [String] id the frame's id
103
+ # @return [HtmlUnit::HtmlPage] the specified frame
104
+ # @return [nil] if no frame is found
105
+ def find_frame(id)
106
+ frame = @_page.getFrames.find do |frame|
107
+ frame.getFrameElement.getAttribute("id") == id
108
+ end
109
+ frame.getEnclosedPage if frame
110
+ end
38
111
  end
112
+
39
113
  end
@@ -2,10 +2,10 @@ require 'drb/drb'
2
2
 
3
3
  # We need to define our own NativeException class for the cases when a native
4
4
  # exception is raised by the JRuby DRb server.
5
- class NativeException < StandardError; end # :nodoc:
5
+ class NativeException < StandardError; end
6
6
 
7
7
  module Akephalos
8
- ##
8
+
9
9
  # The +RemoteClient+ class provides an interface to an +Akephalos::Client+
10
10
  # isntance on a remote DRb server.
11
11
  #
@@ -16,9 +16,10 @@ module Akephalos
16
16
  class RemoteClient
17
17
  @socket_file = "/tmp/akephalos.#{Process.pid}.sock"
18
18
 
19
- ##
20
- # Starts a remote akephalos server and returns the remote Akephalos::Client
19
+ # Start a remote akephalos server and return the remote Akephalos::Client
21
20
  # instance.
21
+ #
22
+ # @return [DRbObject] the remote client instance
22
23
  def self.new
23
24
  start!
24
25
  DRb.start_service
@@ -31,8 +32,7 @@ module Akephalos
31
32
  client
32
33
  end
33
34
 
34
- ##
35
- # Start a remote server process, returning when it is available for use.
35
+ # Start a remote server process and return when it is available for use.
36
36
  def self.start!
37
37
  remote_client = fork do
38
38
  exec("#{Akephalos::BIN_DIR + 'akephalos'} #{@socket_file}")
@@ -1,12 +1,14 @@
1
- # This file runs a JRuby DRb server, and is run by `akephalos --server`.
2
1
  require "pathname"
3
2
  require "drb/drb"
4
3
  require "akephalos/client"
5
4
 
6
5
  # In ruby-1.8.7 and later, the message for a NameError exception is lazily
7
6
  # evaluated. There are, however, different implementations of this between ruby
8
- # and jrby, so we realize these messages when sending over DRb.
7
+ # and jruby, so we realize these messages when sending over DRb.
9
8
  class NameError::Message
9
+ # @note This method is called by DRb before sending the error to the remote
10
+ # connection.
11
+ # @return [String] the inner message.
10
12
  def _dump
11
13
  to_s
12
14
  end
@@ -15,11 +17,18 @@ end
15
17
  [Akephalos::Page, Akephalos::Node].each { |klass| klass.send(:include, DRbUndumped) }
16
18
 
17
19
  module Akephalos
20
+
21
+ # Akephalos::Server is used by `akephalos --server` to start a DRb server
22
+ # serving an instance of Akephalos::Client.
18
23
  class Server
24
+ # Start DRb service for an Akephalos::Client.
25
+ #
26
+ # @param [String] socket_file path to socket file to start
19
27
  def self.start!(socket_file)
20
28
  client = Client.new
21
29
  DRb.start_service("drbunix://#{socket_file}", client)
22
30
  DRb.thread.join
23
31
  end
24
32
  end
33
+
25
34
  end
@@ -1,3 +1,3 @@
1
1
  module Akephalos #:nodoc
2
- VERSION = "0.2.3"
2
+ VERSION = "0.2.4"
3
3
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 2
8
- - 3
9
- version: 0.2.3
8
+ - 4
9
+ version: 0.2.4
10
10
  platform: ruby
11
11
  authors:
12
12
  - Bernerd Schaefer
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-08-18 00:00:00 -04:00
17
+ date: 2010-09-14 00:00:00 -05:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -80,7 +80,6 @@ extra_rdoc_files: []
80
80
  files:
81
81
  - lib/akephalos/capybara.rb
82
82
  - lib/akephalos/client/filter.rb
83
- - lib/akephalos/client/listener.rb
84
83
  - lib/akephalos/client.rb
85
84
  - lib/akephalos/configuration.rb
86
85
  - lib/akephalos/console.rb
@@ -1,18 +0,0 @@
1
- module Akephalos
2
- class Client
3
- class Listener
4
- include com.gargoylesoftware.htmlunit.WebWindowListener
5
-
6
- def initialize(client)
7
- @client = client
8
- end
9
-
10
- def webWindowClosed(event)
11
- end
12
-
13
- def webWindowContentChanged(event)
14
- @client.page = event.getNewPage
15
- end
16
- end
17
- end
18
- end