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.
Files changed (45) hide show
  1. data/.gitignore +5 -0
  2. data/Gemfile +12 -0
  3. data/Gemfile.lock +91 -0
  4. data/MIT-LICENSE +22 -0
  5. data/README.md +16 -0
  6. data/Rakefile +16 -0
  7. data/docs/Makefile +130 -0
  8. data/docs/_static/custom.css +9 -0
  9. data/docs/conf.py +217 -0
  10. data/docs/development.rst +27 -0
  11. data/docs/future.rst +9 -0
  12. data/docs/index.rst +33 -0
  13. data/docs/make.bat +155 -0
  14. data/docs/testing.rst +15 -0
  15. data/docs/usage.rst +85 -0
  16. data/kelp.gemspec +25 -0
  17. data/lib/kelp.rb +1 -0
  18. data/lib/kelp/capybara.rb +2 -0
  19. data/lib/kelp/capybara/capybara_steps.rb +225 -0
  20. data/lib/kelp/capybara/form_helper.rb +131 -0
  21. data/lib/kelp/capybara/web_helper.rb +148 -0
  22. data/spec/capybara/click_link_in_row_spec.rb +24 -0
  23. data/spec/capybara/dropdown_spec.rb +112 -0
  24. data/spec/capybara/field_should_be_empty_spec.rb +44 -0
  25. data/spec/capybara/field_should_contain_spec.rb +143 -0
  26. data/spec/capybara/fill_in_fields_spec.rb +67 -0
  27. data/spec/capybara/follow_spec.rb +35 -0
  28. data/spec/capybara/page_should_have_spec.rb +48 -0
  29. data/spec/capybara/page_should_not_have_spec.rb +53 -0
  30. data/spec/capybara/press_spec.rb +33 -0
  31. data/spec/capybara/should_be_disabled_spec.rb +28 -0
  32. data/spec/capybara/should_be_enabled_spec.rb +29 -0
  33. data/spec/capybara/should_not_see_spec.rb +97 -0
  34. data/spec/capybara/should_see_in_same_row_spec.rb +41 -0
  35. data/spec/capybara/should_see_spec.rb +80 -0
  36. data/spec/capybara/spec_helper.rb +22 -0
  37. data/spec/test_app/test_app.rb +18 -0
  38. data/spec/test_app/views/about.erb +9 -0
  39. data/spec/test_app/views/edit1.erb +9 -0
  40. data/spec/test_app/views/edit2.erb +9 -0
  41. data/spec/test_app/views/edit3.erb +9 -0
  42. data/spec/test_app/views/form.erb +38 -0
  43. data/spec/test_app/views/home.erb +46 -0
  44. data/spec/test_app/views/thanks.erb +10 -0
  45. 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
+