kelp 0.1.1
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/.gitignore +5 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +91 -0
- data/MIT-LICENSE +22 -0
- data/README.md +16 -0
- data/Rakefile +16 -0
- data/docs/Makefile +130 -0
- data/docs/_static/custom.css +9 -0
- data/docs/conf.py +217 -0
- data/docs/development.rst +27 -0
- data/docs/future.rst +9 -0
- data/docs/index.rst +33 -0
- data/docs/make.bat +155 -0
- data/docs/testing.rst +15 -0
- data/docs/usage.rst +85 -0
- data/kelp.gemspec +25 -0
- data/lib/kelp.rb +1 -0
- data/lib/kelp/capybara.rb +2 -0
- data/lib/kelp/capybara/capybara_steps.rb +225 -0
- data/lib/kelp/capybara/form_helper.rb +131 -0
- data/lib/kelp/capybara/web_helper.rb +148 -0
- data/spec/capybara/click_link_in_row_spec.rb +24 -0
- data/spec/capybara/dropdown_spec.rb +112 -0
- data/spec/capybara/field_should_be_empty_spec.rb +44 -0
- data/spec/capybara/field_should_contain_spec.rb +143 -0
- data/spec/capybara/fill_in_fields_spec.rb +67 -0
- data/spec/capybara/follow_spec.rb +35 -0
- data/spec/capybara/page_should_have_spec.rb +48 -0
- data/spec/capybara/page_should_not_have_spec.rb +53 -0
- data/spec/capybara/press_spec.rb +33 -0
- data/spec/capybara/should_be_disabled_spec.rb +28 -0
- data/spec/capybara/should_be_enabled_spec.rb +29 -0
- data/spec/capybara/should_not_see_spec.rb +97 -0
- data/spec/capybara/should_see_in_same_row_spec.rb +41 -0
- data/spec/capybara/should_see_spec.rb +80 -0
- data/spec/capybara/spec_helper.rb +22 -0
- data/spec/test_app/test_app.rb +18 -0
- data/spec/test_app/views/about.erb +9 -0
- data/spec/test_app/views/edit1.erb +9 -0
- data/spec/test_app/views/edit2.erb +9 -0
- data/spec/test_app/views/edit3.erb +9 -0
- data/spec/test_app/views/form.erb +38 -0
- data/spec/test_app/views/home.erb +46 -0
- data/spec/test_app/views/thanks.erb +10 -0
- metadata +183 -0
@@ -0,0 +1,131 @@
|
|
1
|
+
module FormHelper
|
2
|
+
# Fill in multiple fields according to values in a Hash.
|
3
|
+
# Scope may be defined per the #in_scope method.
|
4
|
+
def fill_in_fields(fields, scope={})
|
5
|
+
in_scope(scope) do
|
6
|
+
fields.each do |name, value|
|
7
|
+
fill_in name, :with => value
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Fill in multiple fields within the scope of a given selector.
|
13
|
+
# Syntactic sugar for #fill_in_fields.
|
14
|
+
def fill_in_fields_within(selector, fields)
|
15
|
+
fill_in_fields fields, :within => selector
|
16
|
+
end
|
17
|
+
|
18
|
+
# Verify that the given field is empty or nil.
|
19
|
+
def field_should_be_empty(field)
|
20
|
+
_field = nice_find_field(field)
|
21
|
+
if _field.nil? || _field.value.nil?
|
22
|
+
return true
|
23
|
+
else
|
24
|
+
raise "Expected field '#{field}' to be empty, but it has a value '#{_field.value}'"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Verify that the selected option in a dropdown has the given
|
29
|
+
# value. Note that this is the *visible* content of the dropdown
|
30
|
+
# (the content of the <option> element), rather than the
|
31
|
+
# 'value' attribute of the option.
|
32
|
+
def dropdown_should_equal(dropdown, value)
|
33
|
+
field = nice_find_field(dropdown)
|
34
|
+
# See if there's a 'selected' option
|
35
|
+
begin
|
36
|
+
selected = field.find(:xpath, ".//option[@selected='selected']")
|
37
|
+
# If not, find the option matching the first field value
|
38
|
+
rescue Capybara::ElementNotFound
|
39
|
+
selected = field.find(:xpath, ".//option[@value='#{field.value.first}']")
|
40
|
+
end
|
41
|
+
selected.text.should =~ /#{value}/
|
42
|
+
end
|
43
|
+
|
44
|
+
# Verify that a given dropdown includes all of the given values. Search
|
45
|
+
# first by the 'value' attribute, then by the content of each option; if
|
46
|
+
# values are not found in either place, an error occurs.
|
47
|
+
# Scope may be defined per the #in_scope method.
|
48
|
+
def dropdown_should_include(dropdown, values, scope={})
|
49
|
+
in_scope(scope) do
|
50
|
+
# If values is a String, convert it to an Array
|
51
|
+
values = [values] if values.class == String
|
52
|
+
|
53
|
+
field = nice_find_field(dropdown)
|
54
|
+
# Look for each value
|
55
|
+
values.each do |value|
|
56
|
+
begin
|
57
|
+
option = field.find(:xpath, ".//option[@value='#{value}']")
|
58
|
+
rescue Capybara::ElementNotFound
|
59
|
+
option = field.find(:xpath, ".//option[contains(., '#{value}')]")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Verify that the given <select> element has a given value selected. Works
|
66
|
+
# with single- or multi-select elements. This verifies the 'value' attribute
|
67
|
+
# of the selected option, rather than its visible content.
|
68
|
+
def dropdown_value_should_equal(dropdown, value)
|
69
|
+
# FIXME: When this returns False, does that fail the step?
|
70
|
+
field = find_field(dropdown)
|
71
|
+
field.value.should include(value)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Verify that the given field contains the given value.
|
75
|
+
def field_should_contain(field, value)
|
76
|
+
field = find_field(field)
|
77
|
+
field_value = (field.tag_name == 'textarea') ? field.text : field.value
|
78
|
+
# If field value is an Array, take the first item
|
79
|
+
if field_value.class == Array
|
80
|
+
field_value = field_value.first
|
81
|
+
end
|
82
|
+
# Escape any problematic characters in the expected value
|
83
|
+
value = Regexp.escape(value)
|
84
|
+
# Match actual to expected
|
85
|
+
if field_value.respond_to? :should
|
86
|
+
field_value.should =~ /#{value}/
|
87
|
+
else
|
88
|
+
assert_match(/#{value}/, field_value)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Verify the values of multiple fields given as a Hash.
|
93
|
+
# Scope may be defined per the #in_scope method.
|
94
|
+
def fields_should_contain(field_values, scope={})
|
95
|
+
in_scope(scope) do
|
96
|
+
field_values.each do |field, value|
|
97
|
+
_field = find_field(field)
|
98
|
+
# For nil/empty, check for nil field or nil value
|
99
|
+
if value.nil? or value.empty?
|
100
|
+
field_should_be_empty(field)
|
101
|
+
# If field is a dropdown
|
102
|
+
elsif _field.tag_name == 'select'
|
103
|
+
dropdown_should_equal(field, value)
|
104
|
+
# Otherwise treat as a text field
|
105
|
+
else
|
106
|
+
field_should_contain(field, value)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Verify fields within the scope of a given selector.
|
113
|
+
# Syntactic sugar for #fields_should_contain.
|
114
|
+
def fields_should_contain_within(selector, field_values)
|
115
|
+
fields_should_contain field_values, :within => selector
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
# A slightly friendlier version of Capybara's +find_field+, which actually
|
121
|
+
# tells you which locator failed to match (instead of giving a useless
|
122
|
+
# Unable to find '#<XPath::Union:0xXXXXXXX>' message).
|
123
|
+
def nice_find_field(locator)
|
124
|
+
begin
|
125
|
+
field = find_field(locator)
|
126
|
+
rescue Capybara::ElementNotFound
|
127
|
+
raise "Could not find field with locator: '#{locator}'"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
@@ -0,0 +1,148 @@
|
|
1
|
+
|
2
|
+
module WebHelper
|
3
|
+
# Execute a block of code within a given scope.
|
4
|
+
# +locator+ may be in css or xpath form, depending on what type
|
5
|
+
# is set in Capybara.default_locator.
|
6
|
+
def scope_within(locator)
|
7
|
+
if locator
|
8
|
+
within(locator) do
|
9
|
+
yield
|
10
|
+
end
|
11
|
+
else
|
12
|
+
yield
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Execute a block of code inside a given scope. The +scope+ Hash
|
17
|
+
# may include:
|
18
|
+
#
|
19
|
+
# :within => locator within the given selector
|
20
|
+
#
|
21
|
+
# Other scopes such as :before, :after and so on may be supported later
|
22
|
+
def in_scope(scope)
|
23
|
+
scope_within(scope[:within]) do
|
24
|
+
yield
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Verify the presence of one or more text strings.
|
29
|
+
# Scope may be defined per the #in_scope method.
|
30
|
+
def should_see(texts, scope={})
|
31
|
+
texts = [texts] if (texts.class == String || texts.class == Regexp)
|
32
|
+
in_scope(scope) do
|
33
|
+
texts.each do |text|
|
34
|
+
page_should_have text
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Verify the absence of one or more text strings.
|
40
|
+
# Scope may be defined per the #in_scope method.
|
41
|
+
def should_not_see(texts, scope={})
|
42
|
+
texts = [texts] if (texts.class == String || texts.class == Regexp)
|
43
|
+
in_scope(scope) do
|
44
|
+
texts.each do |text|
|
45
|
+
page_should_not_have text
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Follow a link.
|
51
|
+
# Scope may be defined per the #in_scope method.
|
52
|
+
def follow(link, scope={})
|
53
|
+
in_scope(scope) do
|
54
|
+
click_link(link)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Press a button.
|
59
|
+
# Scope may be defined per the #in_scope method.
|
60
|
+
def press(button, scope={})
|
61
|
+
in_scope(scope) do
|
62
|
+
click_button(button)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Verify that a table row exists containing all the given text strings.
|
67
|
+
def should_see_in_same_row(texts)
|
68
|
+
page.should have_xpath(xpath_row_containing(texts))
|
69
|
+
end
|
70
|
+
|
71
|
+
# Verify that a table row does NOT exist containing all the given text strings.
|
72
|
+
def should_not_see_in_same_row(texts)
|
73
|
+
page.should have_no_xpath(xpath_row_containing(texts))
|
74
|
+
end
|
75
|
+
|
76
|
+
# Click a link in a table row containing the given text.
|
77
|
+
def click_link_in_row(link, text)
|
78
|
+
row = find(:xpath, xpath_row_containing([link, text]))
|
79
|
+
row.click_link(link)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Ensure that the element with the given HTML id is disabled.
|
83
|
+
def should_be_disabled(element_id)
|
84
|
+
page.should have_xpath("//*[@id='#{element_id}']")
|
85
|
+
page.should have_xpath("//*[@id='#{element_id}' and @disabled]")
|
86
|
+
end
|
87
|
+
|
88
|
+
# Ensure that the element with the given HTML id is enabled.
|
89
|
+
def should_be_enabled(element_id)
|
90
|
+
page.should have_xpath("//*[@id='#{element_id}']")
|
91
|
+
page.should have_no_xpath("//*[@id='#{element_id}' and @disabled]")
|
92
|
+
end
|
93
|
+
|
94
|
+
# Ensure that the current page content includes a String or Regexp.
|
95
|
+
def page_should_have(text_or_regexp)
|
96
|
+
if text_or_regexp.class == String
|
97
|
+
if page.respond_to? :should
|
98
|
+
page.should have_content(text_or_regexp)
|
99
|
+
else
|
100
|
+
assert page.has_content?(text_or_regexp)
|
101
|
+
end
|
102
|
+
elsif text_or_regexp.class == Regexp
|
103
|
+
if page.respond_to? :should
|
104
|
+
page.should have_xpath('.//*', :text => text_or_regexp)
|
105
|
+
else
|
106
|
+
assert page.has_xpath?('.//*', :text => text_or_regexp)
|
107
|
+
end
|
108
|
+
else
|
109
|
+
raise "Expected String or Regexp, got #{text_or_regexp.class}"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Ensure that the current page content does not include a String or Regexp.
|
114
|
+
def page_should_not_have(text_or_regexp)
|
115
|
+
if text_or_regexp.class == String
|
116
|
+
if page.respond_to? :should
|
117
|
+
page.should have_no_content(text_or_regexp)
|
118
|
+
else
|
119
|
+
assert page.has_no_content?(text_or_regexp)
|
120
|
+
end
|
121
|
+
elsif text_or_regexp.class == Regexp
|
122
|
+
if page.respond_to? :should
|
123
|
+
page.should have_no_xpath('.//*', :text => text_or_regexp)
|
124
|
+
else
|
125
|
+
assert page.has_no_xpath?('.//*', :text => text_or_regexp)
|
126
|
+
end
|
127
|
+
else
|
128
|
+
raise "Expected String or Regexp, got #{text_or_regexp.class}"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
# Return an XPath for any table row containing all strings in +texts+.
|
136
|
+
def xpath_row_containing(texts)
|
137
|
+
texts = [texts] if texts.class == String
|
138
|
+
conditions = texts.collect do |text|
|
139
|
+
"contains(., '#{text}')"
|
140
|
+
end.join(' and ')
|
141
|
+
return "//table//tr[#{conditions}]"
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
# TODO: Put this in a generator
|
147
|
+
#World(WebHelper)
|
148
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe WebHelper, "#click_link_in_row" do
|
4
|
+
before(:each) do
|
5
|
+
visit('/home')
|
6
|
+
end
|
7
|
+
|
8
|
+
context "passes when" do
|
9
|
+
it "link exists in the row" do
|
10
|
+
click_link_in_row "Edit", "Eric"
|
11
|
+
should_see "You are editing record 1"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context "fails when" do
|
16
|
+
it "link does not exist in the row" do
|
17
|
+
lambda do
|
18
|
+
click_link_in_row "Frob", "Eric"
|
19
|
+
end.should raise_error
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe FormHelper, "#dropdown_should_equal" do
|
4
|
+
before(:each) do
|
5
|
+
visit('/form')
|
6
|
+
end
|
7
|
+
|
8
|
+
context "passes when" do
|
9
|
+
it "the option has the selected attribute" do
|
10
|
+
dropdown_should_equal "Height", "Average"
|
11
|
+
end
|
12
|
+
|
13
|
+
it "the option is chosen programmatically" do
|
14
|
+
select "Short", :from => "Height"
|
15
|
+
dropdown_should_equal "Height", "Short"
|
16
|
+
|
17
|
+
select "Tall", :from => "Height"
|
18
|
+
dropdown_should_equal "Height", "Tall"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context "fails when" do
|
23
|
+
it "the option does not have the selected attribute" do
|
24
|
+
lambda do
|
25
|
+
dropdown_should_equal "Height", "Tall"
|
26
|
+
end.should raise_error
|
27
|
+
end
|
28
|
+
|
29
|
+
it "the option was not the one chosen programmatically" do
|
30
|
+
select "Tall", :from => "Height"
|
31
|
+
lambda do
|
32
|
+
dropdown_should_equal "Height", "Average"
|
33
|
+
end.should raise_error
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
describe FormHelper, "#dropdown_should_include" do
|
40
|
+
before(:each) do
|
41
|
+
visit('/form')
|
42
|
+
end
|
43
|
+
|
44
|
+
context "passes when" do
|
45
|
+
it "a single option exists in the dropdown" do
|
46
|
+
dropdown_should_include "Height", "Short"
|
47
|
+
dropdown_should_include "Height", "Average"
|
48
|
+
dropdown_should_include "Height", "Tall"
|
49
|
+
end
|
50
|
+
|
51
|
+
it "multiple options exist in the dropdown" do
|
52
|
+
dropdown_should_include "Height", [
|
53
|
+
"Short",
|
54
|
+
"Average",
|
55
|
+
"Tall",
|
56
|
+
]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "fails when" do
|
61
|
+
it "a single option does not exist in the dropdown" do
|
62
|
+
lambda do
|
63
|
+
dropdown_should_include "Height", "Midget"
|
64
|
+
end.should raise_error
|
65
|
+
end
|
66
|
+
|
67
|
+
it "any of several options do not exist in the dropdown" do
|
68
|
+
lambda do
|
69
|
+
dropdown_should_include "Height", [
|
70
|
+
"Short",
|
71
|
+
"Average",
|
72
|
+
"Tall",
|
73
|
+
"Giant",
|
74
|
+
]
|
75
|
+
end.should raise_error
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
describe FormHelper, "#dropdown_value_should_equal" do
|
82
|
+
before(:each) do
|
83
|
+
visit('/form')
|
84
|
+
end
|
85
|
+
|
86
|
+
context "passes when" do
|
87
|
+
it "the selected option has the given value" do
|
88
|
+
dropdown_value_should_equal "Height", "2"
|
89
|
+
end
|
90
|
+
|
91
|
+
it "the programmatically chosen option has the given value" do
|
92
|
+
select "Tall", :from => "Height"
|
93
|
+
dropdown_value_should_equal "Height", "3"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context "fails when" do
|
98
|
+
it "the selected option has a different value" do
|
99
|
+
lambda do
|
100
|
+
dropdown_value_should_equal "Height", "99"
|
101
|
+
end.should raise_error
|
102
|
+
end
|
103
|
+
|
104
|
+
it "the programmatically chosen option has a different value" do
|
105
|
+
select "Tall", :from => "Height"
|
106
|
+
lambda do
|
107
|
+
dropdown_value_should_equal "Height", "2"
|
108
|
+
end.should raise_error
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe FormHelper, "#field_should_be_empty" do
|
4
|
+
before(:each) do
|
5
|
+
visit('/form')
|
6
|
+
end
|
7
|
+
|
8
|
+
context "passes when" do
|
9
|
+
context "field with id" do
|
10
|
+
it "is empty" do
|
11
|
+
field_should_be_empty "first_name"
|
12
|
+
field_should_be_empty "last_name"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context "field with label" do
|
17
|
+
it "is empty" do
|
18
|
+
field_should_be_empty "First name"
|
19
|
+
field_should_be_empty "Last name"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "fails when" do
|
25
|
+
context "field with id" do
|
26
|
+
it "has a value" do
|
27
|
+
fill_in "first_name", :with => "Brian"
|
28
|
+
lambda do
|
29
|
+
field_should_be_empty "first_name"
|
30
|
+
end.should raise_error
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "field with label" do
|
35
|
+
it "has a value" do
|
36
|
+
fill_in "First name", :with => "Brian"
|
37
|
+
lambda do
|
38
|
+
field_should_be_empty "First name"
|
39
|
+
end.should raise_error
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|