page-object 0.6.1 → 0.6.2
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +11 -0
- data/features/html/nested_elements.html +20 -0
- data/features/javascript.feature +14 -0
- data/features/nested_elements.feature +11 -0
- data/features/sample-app/public/jquery-1.3.2.js +4376 -0
- data/features/sample-app/public/jquery.html +30 -0
- data/features/sample-app/public/prototype-1.6.0.3.js +4320 -0
- data/features/sample-app/public/prototype.html +34 -0
- data/features/sample-app/sample_app.rb +36 -0
- data/features/step_definitions/javascript_steps.rb +30 -0
- data/features/step_definitions/nested_elements_steps.rb +15 -0
- data/features/support/ajax_text_environment.rb +26 -0
- data/lib/page-object.rb +45 -1
- data/lib/page-object/javascript/jquery.rb +14 -0
- data/lib/page-object/javascript/prototype.rb +14 -0
- data/lib/page-object/javascript_framework_facade.rb +76 -0
- data/lib/page-object/platforms/selenium_webdriver/ordered_list.rb +10 -4
- data/lib/page-object/platforms/selenium_webdriver/unordered_list.rb +10 -4
- data/lib/page-object/platforms/watir_webdriver/ordered_list.rb +8 -5
- data/lib/page-object/platforms/watir_webdriver/page_object.rb +1 -1
- data/lib/page-object/platforms/watir_webdriver/select_list.rb +1 -1
- data/lib/page-object/platforms/watir_webdriver/text_area.rb +1 -1
- data/lib/page-object/platforms/watir_webdriver/unordered_list.rb +8 -5
- data/lib/page-object/version.rb +1 -1
- data/page-object.gemspec +3 -2
- data/spec/page-object/elements/ordered_list_spec.rb +26 -15
- data/spec/page-object/elements/select_list_spec.rb +1 -0
- data/spec/page-object/elements/unordered_list_spec.rb +22 -10
- data/spec/page-object/javascript_framework_facade_spec.rb +61 -0
- data/spec/page-object/page-object_spec.rb +26 -1
- metadata +46 -14
@@ -0,0 +1,34 @@
|
|
1
|
+
<htmL>
|
2
|
+
<head>
|
3
|
+
<title>Prototype - Sample App</title>
|
4
|
+
<script type="application/x-javascript" src="/prototype-1.6.0.3.js"></script>
|
5
|
+
</head>
|
6
|
+
<body>
|
7
|
+
<div id="ajax-demo">
|
8
|
+
<h2>AJAX Calculator</h2>
|
9
|
+
<form id="calculator-form" action="#" method="post" onSubmit="return false;">
|
10
|
+
<input id="calculator-expression" name="calculator-expression"/>
|
11
|
+
<input type="submit" id="calculator-button" value="Compute" />
|
12
|
+
<br/><br/>
|
13
|
+
<div>Result: <input id="calculator-result" disabled /></div>
|
14
|
+
</form>
|
15
|
+
|
16
|
+
<script type="text/javascript" language="javascript">
|
17
|
+
Event.observe(window, 'load', function() {
|
18
|
+
|
19
|
+
Event.observe('calculator-button', 'click', function (event) {
|
20
|
+
new Ajax.Request('/compute', {
|
21
|
+
method: 'post',
|
22
|
+
parameters: $('calculator-form').serialize(true),
|
23
|
+
onSuccess: function(transport) {
|
24
|
+
$('calculator-result').value = transport.responseText;
|
25
|
+
},
|
26
|
+
});
|
27
|
+
return false;
|
28
|
+
});
|
29
|
+
|
30
|
+
});
|
31
|
+
</script>
|
32
|
+
</div>
|
33
|
+
|
34
|
+
</body>
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rack'
|
3
|
+
require 'webrick'
|
4
|
+
|
5
|
+
|
6
|
+
class SampleApp
|
7
|
+
|
8
|
+
def self.start(host, port)
|
9
|
+
Rack::Handler::WEBrick.run(new,
|
10
|
+
:Host => host,
|
11
|
+
:Port => port,
|
12
|
+
:Logger => ::WEBrick::Log.new('/dev/null'),
|
13
|
+
:AccessLog => [nil, nil],)
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@public = Rack::File.new(File.expand_path("../public", __FILE__))
|
18
|
+
end
|
19
|
+
|
20
|
+
def call(env)
|
21
|
+
req = Rack::Request.new(env)
|
22
|
+
|
23
|
+
case req.path
|
24
|
+
when "/"
|
25
|
+
[200, {}, ["Sample Application"]]
|
26
|
+
when "/compute"
|
27
|
+
sleep 3
|
28
|
+
resp = eval(req.params['calculator-expression']).to_s
|
29
|
+
[200, {}, [resp]]
|
30
|
+
else
|
31
|
+
@public.call(env)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class JavascriptPage
|
2
|
+
include PageObject
|
3
|
+
|
4
|
+
text_field(:expression, :id => 'calculator-expression')
|
5
|
+
text_field(:results, :id => 'calculator-result')
|
6
|
+
button(:compute, :value => 'Compute')
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
Given /^I am on jQuery example page$/ do
|
11
|
+
PageObject.javascript_framework = :jquery
|
12
|
+
@page = JavascriptPage.new(@browser)
|
13
|
+
@page.navigate_to "http://localhost:4567/jquery.html"
|
14
|
+
end
|
15
|
+
|
16
|
+
Given /^I am on the Prototype example page$/ do
|
17
|
+
PageObject.javascript_framework = :prototype
|
18
|
+
@page = JavascriptPage.new(@browser)
|
19
|
+
@page.navigate_to "http://localhost:4567/prototype.html"
|
20
|
+
end
|
21
|
+
|
22
|
+
When /^I ask to compute "([^\"]*)"$/ do |expression|
|
23
|
+
@page.expression = expression
|
24
|
+
@page.compute
|
25
|
+
end
|
26
|
+
|
27
|
+
Then /^I should be able to wait for the answer "([^\"]*)"$/ do |answer|
|
28
|
+
@page.wait_for_ajax
|
29
|
+
@page.results.should == answer
|
30
|
+
end
|
@@ -27,6 +27,9 @@ class NestedElementsPage
|
|
27
27
|
h6(:nested_h6) { |page| page.outer_div_element.h6_element }
|
28
28
|
paragraph(:nested_paragraph) { |page| page.outer_div_element.paragraph_element }
|
29
29
|
file_field(:nested_file_field) { |page| page.outer_div_element.file_field_element }
|
30
|
+
|
31
|
+
unordered_list(:outer_list, :id => 'outer')
|
32
|
+
ordered_list(:ordered_outer, :id => 'ol-outer')
|
30
33
|
end
|
31
34
|
|
32
35
|
Given /^I am on the nested elements page$/ do
|
@@ -195,3 +198,15 @@ end
|
|
195
198
|
Then /^I should be able to set the nested file field$/ do
|
196
199
|
@ff.value = __FILE__
|
197
200
|
end
|
201
|
+
|
202
|
+
When /^I get the outter unordered list$/ do
|
203
|
+
@list = @page.outer_list_element
|
204
|
+
end
|
205
|
+
|
206
|
+
When /^I get the outter ordered list$/ do
|
207
|
+
@list = @page.ordered_outer_element
|
208
|
+
end
|
209
|
+
|
210
|
+
Then /^I should see "([^\"]*)" for list item (\d+)$/ do |text, item_number|
|
211
|
+
@list[item_number.to_i - 1].text.should == text
|
212
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative "../sample-app/sample_app"
|
2
|
+
|
3
|
+
class AjaxTestEnvironment
|
4
|
+
def run
|
5
|
+
Thread.abort_on_exception = true
|
6
|
+
@example_app = Thread.new { SampleApp.start("127.0.0.1", 4567) }
|
7
|
+
poller = Selenium::WebDriver::SocketPoller.new("127.0.0.1", 4567, 60)
|
8
|
+
unless poller.connected?
|
9
|
+
raise "timed out waiting for SampleApp to launch"
|
10
|
+
end
|
11
|
+
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def stop
|
16
|
+
@example_app.kill
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
@server = AjaxTestEnvironment.new
|
22
|
+
@server.run
|
23
|
+
|
24
|
+
at_exit do
|
25
|
+
@server.stop
|
26
|
+
end
|
data/lib/page-object.rb
CHANGED
@@ -4,7 +4,9 @@ require 'page-object/platforms'
|
|
4
4
|
require 'page-object/element_locators'
|
5
5
|
require 'page-object/nested_elements'
|
6
6
|
require 'page-object/page_populator'
|
7
|
+
require 'page-object/javascript_framework_facade'
|
7
8
|
|
9
|
+
require 'selenium/webdriver/common/error'
|
8
10
|
#
|
9
11
|
# Module that when included adds functionality to a page object. This module
|
10
12
|
# will add numerous class and instance methods that you use to define and
|
@@ -62,6 +64,29 @@ module PageObject
|
|
62
64
|
cls.extend PageObject::Accessors
|
63
65
|
end
|
64
66
|
|
67
|
+
#
|
68
|
+
# Set the javascript framework to use when determining number of
|
69
|
+
# ajax requests. Valid frameworks are :jquery, :prototype, and
|
70
|
+
# :dojo
|
71
|
+
#
|
72
|
+
def self.javascript_framework=(framework)
|
73
|
+
PageObject::JavascriptFrameworkFacade.framework = framework
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# Add a new javascript framework to page-object. The module passed
|
78
|
+
# in must adhere to the same prototype as the JQuery and Prototype
|
79
|
+
# modules.
|
80
|
+
#
|
81
|
+
# @param [Symbol] the name used to reference the framework in
|
82
|
+
# subsequent calls
|
83
|
+
# @param [Module] a module that has the necessary methods to perform
|
84
|
+
# the required actions.
|
85
|
+
#
|
86
|
+
def self.add_framework(key, framework)
|
87
|
+
PageObject::JavascriptFrameworkFacade.add_framework(key, framework)
|
88
|
+
end
|
89
|
+
|
65
90
|
#
|
66
91
|
# get the current page url
|
67
92
|
#
|
@@ -115,6 +140,25 @@ module PageObject
|
|
115
140
|
platform.wait_until(timeout, message, &block)
|
116
141
|
end
|
117
142
|
|
143
|
+
|
144
|
+
#
|
145
|
+
# Wait until there are no pending ajax requests. This requires you
|
146
|
+
# to set the javascript framework in advance.
|
147
|
+
#
|
148
|
+
# @param [Numeric] the amount of time to wait for the block to return true.
|
149
|
+
# @param [String] the message to include with the error if we exceed
|
150
|
+
# the timeout duration.
|
151
|
+
#
|
152
|
+
def wait_for_ajax(timeout = 30, message = nil)
|
153
|
+
end_time = ::Time.now + timeout
|
154
|
+
until ::Time.now > end_time
|
155
|
+
return if browser.execute_script(::PageObject::JavascriptFrameworkFacade.pending_requests) == 0
|
156
|
+
sleep 0.5
|
157
|
+
end
|
158
|
+
message = "Timed out waiting for ajax requests to complete" unless message
|
159
|
+
raise message
|
160
|
+
end
|
161
|
+
|
118
162
|
#
|
119
163
|
# Override the normal alert popup so it does not occurr.
|
120
164
|
#
|
@@ -277,6 +321,6 @@ module PageObject
|
|
277
321
|
end
|
278
322
|
|
279
323
|
def call_block(&block)
|
280
|
-
|
324
|
+
block.arity == 1 ? block.call(self) : self.instance_eval(&block)
|
281
325
|
end
|
282
326
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'page-object/javascript/jquery'
|
2
|
+
require 'page-object/javascript/prototype'
|
3
|
+
|
4
|
+
|
5
|
+
module PageObject
|
6
|
+
#
|
7
|
+
# Provide hooks into different common Javascript Frameworks.
|
8
|
+
# Currently this module only supports jQuery and Prototype but it
|
9
|
+
# has the ability for you to plug your own framework into it and
|
10
|
+
# therefore have it work with this gem. You do this by calling the
|
11
|
+
# #add_framework method. The module you provide must implement the
|
12
|
+
# necessary methods. Please look at the jQuery or Prototype
|
13
|
+
# implementations to determine the necessary methods
|
14
|
+
#
|
15
|
+
module JavascriptFrameworkFacade
|
16
|
+
|
17
|
+
class << self
|
18
|
+
#
|
19
|
+
# Set the framework to use.
|
20
|
+
#
|
21
|
+
# @param[Symbol] the framework to use. :jquery and :prototype
|
22
|
+
# are supported
|
23
|
+
#
|
24
|
+
def framework=(framework)
|
25
|
+
initialize_script_builder unless @builder
|
26
|
+
raise unknown_framework(framework) unless @builder[framework]
|
27
|
+
@framework = framework
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# Get the framework that will be used
|
32
|
+
#
|
33
|
+
def framework
|
34
|
+
@framework
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Add a framework and make it available to the system.
|
39
|
+
#
|
40
|
+
def add_framework(key, value)
|
41
|
+
raise invalid_framework unless value.respond_to? :pending_requests
|
42
|
+
initialize_script_builder unless @builder
|
43
|
+
@builder[key] = value
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# get the javascript to determine number of pending requests
|
48
|
+
#
|
49
|
+
def pending_requests
|
50
|
+
script_builder.pending_requests
|
51
|
+
end
|
52
|
+
|
53
|
+
def script_builder
|
54
|
+
initialize_script_builder unless @builder
|
55
|
+
@builder[@framework]
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def initialize_script_builder
|
61
|
+
@builder = {
|
62
|
+
:jquery => ::PageObject::Javascript::JQuery,
|
63
|
+
:prototype => ::PageObject::Javascript::Prototype,
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def unknown_framework(framework)
|
68
|
+
"You specified the Javascript framework #{framework} and it is unknow to the system"
|
69
|
+
end
|
70
|
+
|
71
|
+
def invalid_framework
|
72
|
+
"The Javascript framework you provided does not implement the necessary methods"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -10,19 +10,25 @@ module PageObject
|
|
10
10
|
# @return [PageObject::Elements::ListItem]
|
11
11
|
#
|
12
12
|
def [](idx)
|
13
|
-
|
14
|
-
Object::PageObject::Elements::ListItem.new(eles[idx], :platform => :selenium_webdriver)
|
13
|
+
children[idx]
|
15
14
|
end
|
16
15
|
|
17
16
|
#
|
18
17
|
# Return the number of items contained in the ordered list
|
19
18
|
#
|
20
19
|
def items
|
21
|
-
|
20
|
+
children.size
|
22
21
|
end
|
23
22
|
|
24
23
|
private
|
25
24
|
|
25
|
+
def children
|
26
|
+
items = list_items.map do |item|
|
27
|
+
::PageObject::Elements::ListItem.new(item, :platform => :selenium_webdriver)
|
28
|
+
end
|
29
|
+
items.find_all { |item| item.parent.element == element }
|
30
|
+
end
|
31
|
+
|
26
32
|
def list_items
|
27
33
|
element.find_elements(:xpath, child_xpath)
|
28
34
|
end
|
@@ -30,4 +36,4 @@ module PageObject
|
|
30
36
|
end
|
31
37
|
end
|
32
38
|
end
|
33
|
-
end
|
39
|
+
end
|
@@ -10,19 +10,25 @@ module PageObject
|
|
10
10
|
# @return [PageObject::Elements::ListItem]
|
11
11
|
#
|
12
12
|
def [](idx)
|
13
|
-
|
14
|
-
Object::PageObject::Elements::ListItem.new(eles[idx], :platform => :selenium_webdriver)
|
13
|
+
children[idx]
|
15
14
|
end
|
16
15
|
|
17
16
|
#
|
18
17
|
# Return the number of items contained in the unordered list
|
19
18
|
#
|
20
19
|
def items
|
21
|
-
|
20
|
+
children.size
|
22
21
|
end
|
23
22
|
|
24
23
|
private
|
25
24
|
|
25
|
+
def children
|
26
|
+
items = list_items.map do |item|
|
27
|
+
::PageObject::Elements::ListItem.new(item, :platform => :selenium_webdriver)
|
28
|
+
end
|
29
|
+
items.find_all { |item| item.parent.element == element }
|
30
|
+
end
|
31
|
+
|
26
32
|
def list_items
|
27
33
|
element.find_elements(:xpath, child_xpath)
|
28
34
|
end
|
@@ -30,4 +36,4 @@ module PageObject
|
|
30
36
|
end
|
31
37
|
end
|
32
38
|
end
|
33
|
-
end
|
39
|
+
end
|
@@ -10,23 +10,26 @@ module PageObject
|
|
10
10
|
# @return [PageObject::Elements::ListItem]
|
11
11
|
#
|
12
12
|
def [](idx)
|
13
|
-
|
14
|
-
Object::PageObject::Elements::ListItem.new(eles[idx], :platform => :watir_webdriver)
|
13
|
+
Object::PageObject::Elements::ListItem.new(children[idx], :platform => :watir_webdriver)
|
15
14
|
end
|
16
15
|
|
17
16
|
#
|
18
17
|
# Return the number of items contained in the ordered list
|
19
18
|
#
|
20
19
|
def items
|
21
|
-
|
20
|
+
children.size
|
22
21
|
end
|
23
22
|
|
24
23
|
private
|
25
24
|
|
25
|
+
def children
|
26
|
+
list_items.find_all { |item| item.parent == element }
|
27
|
+
end
|
28
|
+
|
26
29
|
def list_items
|
27
|
-
element.
|
30
|
+
element.ols(:xpath => child_xpath)
|
28
31
|
end
|
29
32
|
end
|
30
33
|
end
|
31
34
|
end
|
32
|
-
end
|
35
|
+
end
|