akephalos 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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