kelp 0.1.1 → 0.1.2

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 (56) hide show
  1. data/.gitignore +2 -0
  2. data/.yardopts +6 -0
  3. data/Gemfile +4 -9
  4. data/Gemfile.lock +8 -0
  5. data/History.md +22 -0
  6. data/README.md +154 -7
  7. data/kelp.gemspec +1 -1
  8. data/lib/kelp.rb +8 -1
  9. data/lib/kelp/attribute.rb +31 -0
  10. data/lib/kelp/checkbox.rb +31 -0
  11. data/lib/kelp/dropdown.rb +109 -0
  12. data/lib/kelp/field.rb +159 -0
  13. data/lib/kelp/helper.rb +14 -0
  14. data/lib/kelp/navigation.rb +63 -0
  15. data/lib/kelp/scoping.rb +45 -0
  16. data/lib/kelp/visibility.rb +176 -0
  17. data/lib/kelp/xpath.rb +14 -0
  18. data/spec/attribute_spec.rb +56 -0
  19. data/spec/checkbox_spec.rb +69 -0
  20. data/spec/dropdown_spec.rb +176 -0
  21. data/spec/field_spec.rb +290 -0
  22. data/spec/navigation_spec.rb +89 -0
  23. data/spec/scoping_spec.rb +0 -0
  24. data/spec/{capybara/spec_helper.rb → spec_helper.rb} +9 -5
  25. data/spec/test_app/views/form.erb +24 -0
  26. data/spec/visibility_spec.rb +315 -0
  27. data/spec/xpath_spec.rb +0 -0
  28. data/step_definitions/capybara_steps.rb +132 -0
  29. metadata +25 -32
  30. data/docs/Makefile +0 -130
  31. data/docs/_static/custom.css +0 -9
  32. data/docs/conf.py +0 -217
  33. data/docs/development.rst +0 -27
  34. data/docs/future.rst +0 -9
  35. data/docs/index.rst +0 -33
  36. data/docs/make.bat +0 -155
  37. data/docs/testing.rst +0 -15
  38. data/docs/usage.rst +0 -85
  39. data/lib/kelp/capybara.rb +0 -2
  40. data/lib/kelp/capybara/capybara_steps.rb +0 -225
  41. data/lib/kelp/capybara/form_helper.rb +0 -131
  42. data/lib/kelp/capybara/web_helper.rb +0 -148
  43. data/spec/capybara/click_link_in_row_spec.rb +0 -24
  44. data/spec/capybara/dropdown_spec.rb +0 -112
  45. data/spec/capybara/field_should_be_empty_spec.rb +0 -44
  46. data/spec/capybara/field_should_contain_spec.rb +0 -143
  47. data/spec/capybara/fill_in_fields_spec.rb +0 -67
  48. data/spec/capybara/follow_spec.rb +0 -35
  49. data/spec/capybara/page_should_have_spec.rb +0 -48
  50. data/spec/capybara/page_should_not_have_spec.rb +0 -53
  51. data/spec/capybara/press_spec.rb +0 -33
  52. data/spec/capybara/should_be_disabled_spec.rb +0 -28
  53. data/spec/capybara/should_be_enabled_spec.rb +0 -29
  54. data/spec/capybara/should_not_see_spec.rb +0 -97
  55. data/spec/capybara/should_see_in_same_row_spec.rb +0 -41
  56. data/spec/capybara/should_see_spec.rb +0 -80
@@ -0,0 +1,14 @@
1
+ module Kelp
2
+ module Helper
3
+ # A slightly friendlier version of Capybara's `find_field`, which actually
4
+ # tells you which locator failed to match (instead of giving a useless
5
+ # Unable to find '#<XPath::Union:0xXXXXXXX>' message).
6
+ def nice_find_field(locator)
7
+ begin
8
+ field = find_field(locator)
9
+ rescue Capybara::ElementNotFound
10
+ raise "Could not find field with locator: '#{locator}'"
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,63 @@
1
+ require 'kelp/scoping'
2
+ require 'kelp/xpath'
3
+
4
+ module Kelp
5
+ # This module defines helper methods for navigating a webpage, including
6
+ # clicking links and pressing buttons.
7
+ #
8
+ module Navigation
9
+ include Scoping
10
+ include XPath
11
+
12
+ # Follow a link on the page.
13
+ #
14
+ # @example
15
+ # follow "Login"
16
+ # follow "Contact Us", :within => "#footer"
17
+ #
18
+ # @param [String] link
19
+ # Capybara locator expression (id, name, or link text)
20
+ # @param [Hash] scope
21
+ # Scoping keywords as understood by {#in_scope}
22
+ #
23
+ def follow(link, scope={})
24
+ in_scope(scope) do
25
+ click_link(link)
26
+ end
27
+ end
28
+
29
+ # Press a button on the page.
30
+ #
31
+ # @example
32
+ # press "Cancel"
33
+ # press "Submit", :within => "#survey"
34
+ #
35
+ # @param [String] button
36
+ # Capybara locator expression (id, name, or button text)
37
+ # @param [Hash] scope
38
+ # Scoping keywords as understood by {#in_scope}
39
+ #
40
+ def press(button, scope={})
41
+ in_scope(scope) do
42
+ click_button(button)
43
+ end
44
+ end
45
+
46
+
47
+ # Click a link in a table row containing the given text.
48
+ #
49
+ # @example
50
+ # click_link_in_row "Edit", "Pinky"
51
+ #
52
+ # @param [String] link
53
+ # Content of the link to click
54
+ # @param [String] text
55
+ # Other content that must be in the same row
56
+ #
57
+ def click_link_in_row(link, text)
58
+ row = find(:xpath, xpath_row_containing([link, text]))
59
+ row.click_link(link)
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,45 @@
1
+ module Kelp
2
+ # This module defines helper methods for restricting the scope of actions or
3
+ # verifications on a webpage.
4
+ #
5
+ module Scoping
6
+ # Execute a block of code within a given scope.
7
+ # `locator` may be in css or xpath form, depending on what type
8
+ # is set in Capybara.default_locator.
9
+ def scope_within(locator)
10
+ if locator
11
+ # Use the selector_for method if it's defined
12
+ if defined? selector_for
13
+ within(*selector_for(locator)) { yield }
14
+ # Otherwise, remove any surrounding double-quotes
15
+ # and fall back on the Capybara locator syntax (CSS or XPath)
16
+ else
17
+ locator.gsub!(/^"(.*?)"$/, '\1')
18
+ within(locator) { yield }
19
+ end
20
+ else
21
+ yield
22
+ end
23
+ end
24
+
25
+
26
+ # Execute a block of code inside a given scope. `scope` must be a `Hash`
27
+ # of parameters that describes the context in which to execute the block.
28
+ #
29
+ # @example
30
+ # in_scope :within => '#footer'
31
+ # click_link "Contact Us"
32
+ # end
33
+ #
34
+ # @param [Hash] scope
35
+ # Context in which to execute the block
36
+ # @option scope [String] :within
37
+ # Selector string
38
+ #
39
+ def in_scope(scope)
40
+ scope_within(scope[:within]) do
41
+ yield
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,176 @@
1
+ require 'kelp/scoping'
2
+ require 'kelp/xpath'
3
+
4
+ module Kelp
5
+ # This module defines methods for verifying the visibility (or invisibility)
6
+ # of elements on a web page.
7
+ #
8
+ module Visibility
9
+ include Scoping
10
+ include XPath
11
+
12
+ # Verify the presence of content on the page. Passes when all the given items
13
+ # are found on the page, and fails if any of them are not found.
14
+ #
15
+ # @example
16
+ # should_see "Animaniacs"
17
+ # should_see ["Yakko", "Wakko", "Dot"]
18
+ # should_see "Baloney", :within => "#slacks"
19
+ # should_see /(Animaney|Totally Insaney|Pinky and the Brainy)/
20
+ #
21
+ # @param [String, Regexp, Array] texts
22
+ # Text(s) or regexp(s) to look for
23
+ # @param [Hash] scope
24
+ # Scoping keywords as understood by {#in_scope}
25
+ #
26
+ def should_see(texts, scope={})
27
+ texts = [texts] if (texts.class == String || texts.class == Regexp)
28
+ in_scope(scope) do
29
+ texts.each do |text|
30
+ page_should_contain text
31
+ end
32
+ end
33
+ end
34
+
35
+
36
+ # Verify the absence of content on the page. Passes when none of the given
37
+ # items are found on the page, and fails if any of them are found.
38
+ #
39
+ # @param [String, Regexp, Array] texts
40
+ # Text(s) or regexp(s) to look for
41
+ # @param [Hash] scope
42
+ # Scoping keywords as understood by {#in_scope}
43
+ #
44
+ def should_not_see(texts, scope={})
45
+ texts = [texts] if (texts.class == String || texts.class == Regexp)
46
+ in_scope(scope) do
47
+ texts.each do |text|
48
+ page_should_not_contain text
49
+ end
50
+ end
51
+ end
52
+
53
+
54
+ # Ensure that the current page content includes a String or Regexp.
55
+ #
56
+ # @param [String, Regexp] text_or_regexp
57
+ # Content you expect to be on the page
58
+ #
59
+ def page_should_contain(text_or_regexp)
60
+ if text_or_regexp.class == String
61
+ page_should_contain_text(text_or_regexp)
62
+ elsif text_or_regexp.class == Regexp
63
+ page_should_contain_regexp(text_or_regexp)
64
+ else
65
+ raise ArgumentError, "Expected String or Regexp, got #{text_or_regexp.class}"
66
+ end
67
+ end
68
+
69
+
70
+ # Ensure that the current page content does not include a String or Regexp.
71
+ #
72
+ # @param [String, Regexp] text_or_regexp
73
+ # Content you expect to be missing from the page
74
+ #
75
+ def page_should_not_contain(text_or_regexp)
76
+ if text_or_regexp.class == String
77
+ page_should_not_contain_text(text_or_regexp)
78
+ elsif text_or_regexp.class == Regexp
79
+ page_should_not_contain_regexp(text_or_regexp)
80
+ else
81
+ raise ArgumentError, "Expected String or Regexp, got #{text_or_regexp.class}"
82
+ end
83
+ end
84
+
85
+
86
+ # Ensure that the current page content includes a String.
87
+ #
88
+ # @param [String] text
89
+ # Content you expect to be on the page
90
+ #
91
+ def page_should_contain_text(text)
92
+ if page.respond_to? :should
93
+ page.should have_content(text)
94
+ else
95
+ assert page.has_content?(text)
96
+ end
97
+ end
98
+
99
+
100
+ # Ensure that the current page content matches a Regexp.
101
+ #
102
+ # @param [Regexp] regexp
103
+ # Content you expect to match
104
+ #
105
+ def page_should_contain_regexp(regexp)
106
+ if page.respond_to? :should
107
+ page.should have_xpath('.//*', :text => regexp)
108
+ else
109
+ assert page.has_xpath?('.//*', :text => regexp)
110
+ end
111
+ end
112
+
113
+
114
+ # Ensure that the current page content does not include a String.
115
+ #
116
+ # @param [String] text
117
+ # Content you expect to be missing from the page
118
+ #
119
+ def page_should_not_contain_text(text)
120
+ if page.respond_to? :should
121
+ page.should have_no_content(text)
122
+ else
123
+ assert page.has_no_content?(text)
124
+ end
125
+ end
126
+
127
+
128
+ # Ensure that the current page content does not match a Regexp.
129
+ #
130
+ # @param [Regexp] regexp
131
+ # Content you expect to fail matching
132
+ #
133
+ def page_should_not_contain_regexp(regexp)
134
+ if page.respond_to? :should
135
+ page.should have_no_xpath('.//*', :text => regexp)
136
+ else
137
+ assert page.has_no_xpath?('.//*', :text => regexp)
138
+ end
139
+ end
140
+
141
+
142
+ # Verify that all items appear in the same table row. Passes if a `tr`
143
+ # element exists containing all the given `texts`, and fails if no such
144
+ # `tr` exists. The texts may be in any order in the row.
145
+ #
146
+ # @example
147
+ # should_see_in_same_row ["Yakko", "Rob Paulsen"]
148
+ # should_see_in_same_row ["Wakko", "Jess Harnell"]
149
+ # should_see_in_same_row ["Dot", "Tress MacNeille"]
150
+ #
151
+ # @param [Array] texts
152
+ # Array of Strings that should appear in the same row
153
+ #
154
+ def should_see_in_same_row(texts)
155
+ page.should have_xpath(xpath_row_containing(texts))
156
+ end
157
+
158
+
159
+ # Verify that all items do not appear in the same table row. Passes if there
160
+ # is no `tr` containing all the given `texts`, and fails if there exists
161
+ # such a `tr`.
162
+ #
163
+ # @example
164
+ # should_not_see_in_same_row ["Pinky", "Maurice LaMarche"]
165
+ # should_not_see_in_same_row ["Brain", "Rob Paulsen"]
166
+ #
167
+ # @param [Array] texts
168
+ # Array of Strings that should not appear in the same row
169
+ #
170
+ def should_not_see_in_same_row(texts)
171
+ page.should have_no_xpath(xpath_row_containing(texts))
172
+ end
173
+
174
+
175
+ end
176
+ end
data/lib/kelp/xpath.rb ADDED
@@ -0,0 +1,14 @@
1
+ module Kelp
2
+ # This module defines helper methods for building XPath expressions.
3
+ #
4
+ module XPath
5
+ # Return an XPath for any table row containing all strings in `texts`.
6
+ def xpath_row_containing(texts)
7
+ texts = [texts] if texts.class == String
8
+ conditions = texts.collect do |text|
9
+ "contains(., '#{text}')"
10
+ end.join(' and ')
11
+ return "//table//tr[#{conditions}]"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,56 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Kelp::Attribute, "should_be_disabled" do
4
+ before(:each) do
5
+ visit('/form')
6
+ end
7
+
8
+ context "passes when" do
9
+ it "element has the disabled attribute" do
10
+ should_be_disabled "readonly"
11
+ end
12
+ end
13
+
14
+ context "fails when" do
15
+ it "element does not exist" do
16
+ lambda do
17
+ should_be_disabled "nonexistent"
18
+ end.should raise_error(RSpec::Expectations::ExpectationNotMetError)
19
+ end
20
+
21
+ it "element does not have the disabled attribute" do
22
+ lambda do
23
+ should_be_disabled "first_name"
24
+ end.should raise_error(RSpec::Expectations::ExpectationNotMetError)
25
+ end
26
+ end
27
+ end
28
+
29
+
30
+ describe Kelp::Attribute, "should_be_enabled" do
31
+ before(:each) do
32
+ visit('/form')
33
+ end
34
+
35
+ context "passes when" do
36
+ it "element does not have the disabled attribute" do
37
+ should_be_enabled "first_name"
38
+ end
39
+ end
40
+
41
+ context "fails when" do
42
+ it "element does not exist" do
43
+ lambda do
44
+ should_be_enabled "nonexistent"
45
+ end.should raise_error(RSpec::Expectations::ExpectationNotMetError)
46
+ end
47
+
48
+ it "element has the disabled attribute" do
49
+ lambda do
50
+ should_be_enabled "readonly"
51
+ end.should raise_error(RSpec::Expectations::ExpectationNotMetError)
52
+ end
53
+ end
54
+ end
55
+
56
+
@@ -0,0 +1,69 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Kelp::Checkbox, "checkbox_should_be_checked" do
4
+ before(:each) do
5
+ visit('/form')
6
+ end
7
+
8
+ context "passes when" do
9
+ it "the checkbox is checked by default" do
10
+ checkbox_should_be_checked "I like cheese"
11
+ end
12
+
13
+ it "the checkbox is checked programmatically" do
14
+ check "I like salami"
15
+ checkbox_should_be_checked "I like salami"
16
+ end
17
+ end
18
+
19
+ context "fails when" do
20
+ it "the checkbox is unchecked by default" do
21
+ lambda do
22
+ checkbox_should_be_checked "I like salami"
23
+ end.should raise_error
24
+ end
25
+
26
+ it "the checkbox is unchecked programmatically" do
27
+ uncheck "I like cheese"
28
+ lambda do
29
+ checkbox_should_be_checked "I like cheese"
30
+ end.should raise_error
31
+ end
32
+ end
33
+
34
+ end
35
+
36
+
37
+ describe Kelp::Checkbox, "checkbox_should_not_be_checked" do
38
+ before(:each) do
39
+ visit('/form')
40
+ end
41
+
42
+ context "passes when" do
43
+ it "the checkbox is unchecked by default" do
44
+ checkbox_should_not_be_checked "I like salami"
45
+ end
46
+
47
+ it "the checkbox is unchecked programmatically" do
48
+ uncheck "I like cheese"
49
+ checkbox_should_not_be_checked "I like cheese"
50
+ end
51
+ end
52
+
53
+ context "fails when" do
54
+ it "the checkbox is checked by default" do
55
+ lambda do
56
+ checkbox_should_not_be_checked "I like cheese"
57
+ end.should raise_error
58
+ end
59
+
60
+ it "the checkbox is checked programmatically" do
61
+ check "I like salami"
62
+ lambda do
63
+ checkbox_should_not_be_checked "I like salami"
64
+ end.should raise_error
65
+ end
66
+ end
67
+
68
+ end
69
+
@@ -0,0 +1,176 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Kelp::Dropdown, "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 first option is selected by default" do
14
+ dropdown_should_equal "Weight", "Light"
15
+ end
16
+
17
+ it "the option is chosen programmatically" do
18
+ select "Short", :from => "Height"
19
+ dropdown_should_equal "Height", "Short"
20
+
21
+ select "Heavy", :from => "Weight"
22
+ dropdown_should_equal "Weight", "Heavy"
23
+ end
24
+ end
25
+
26
+ context "fails when" do
27
+ it "the option does not have the 'selected' attribute" do
28
+ lambda do
29
+ dropdown_should_equal "Height", "Tall"
30
+ end.should raise_error(RSpec::Expectations::ExpectationNotMetError)
31
+ end
32
+
33
+ it "the option is not the first one selected by default" do
34
+ lambda do
35
+ dropdown_should_equal "Weight", "Medium"
36
+ end.should raise_error(RSpec::Expectations::ExpectationNotMetError)
37
+ end
38
+
39
+ it "the option was not the one chosen programmatically" do
40
+ select "Tall", :from => "Height"
41
+ lambda do
42
+ dropdown_should_equal "Height", "Average"
43
+ end.should raise_error(RSpec::Expectations::ExpectationNotMetError)
44
+ end
45
+ end
46
+
47
+ end
48
+
49
+
50
+ describe Kelp::Dropdown, "dropdown_should_include" do
51
+ before(:each) do
52
+ visit('/form')
53
+ end
54
+
55
+ context "passes when" do
56
+ it "a single option exists in the dropdown" do
57
+ dropdown_should_include "Height", "Short"
58
+ dropdown_should_include "Height", "Average"
59
+ dropdown_should_include "Height", "Tall"
60
+ end
61
+
62
+ it "multiple options exist in the dropdown" do
63
+ dropdown_should_include "Height", [
64
+ "Short",
65
+ "Average",
66
+ "Tall",
67
+ ]
68
+ end
69
+ end
70
+
71
+ context "fails when" do
72
+ it "a single option does not exist in the dropdown" do
73
+ lambda do
74
+ dropdown_should_include "Height", "Midget"
75
+ end.should raise_error(RSpec::Expectations::ExpectationNotMetError)
76
+ end
77
+
78
+ it "any of several options do not exist in the dropdown" do
79
+ lambda do
80
+ dropdown_should_include "Height", [
81
+ "Short",
82
+ "Average",
83
+ "Tall",
84
+ "Giant",
85
+ ]
86
+ end.should raise_error(RSpec::Expectations::ExpectationNotMetError)
87
+ end
88
+ end
89
+ end
90
+
91
+
92
+ describe Kelp::Dropdown, "dropdown_should_not_include" do
93
+ before(:each) do
94
+ visit('/form')
95
+ end
96
+
97
+ context "passes when" do
98
+ it "a single option does not exist in the dropdown" do
99
+ dropdown_should_not_include "Height", "Dwarf"
100
+ dropdown_should_not_include "Height", "Giant"
101
+ dropdown_should_not_include "Height", "Behemoth"
102
+ end
103
+
104
+ it "none of the multiple options exist in the dropdown" do
105
+ dropdown_should_not_include "Height", [
106
+ "Dwarf",
107
+ "Giant",
108
+ "Behemoth",
109
+ ]
110
+ end
111
+ end
112
+
113
+ context "fails when" do
114
+ it "a single option exists in the dropdown" do
115
+ lambda do
116
+ dropdown_should_not_include "Height", "Short"
117
+ end.should raise_error(RSpec::Expectations::ExpectationNotMetError)
118
+ end
119
+
120
+ it "any of several options exist in the dropdown" do
121
+ lambda do
122
+ dropdown_should_not_include "Height", [
123
+ "Dwarf",
124
+ "Giant",
125
+ "Behemoth",
126
+ "Tall",
127
+ ]
128
+ end.should raise_error(RSpec::Expectations::ExpectationNotMetError)
129
+ end
130
+ end
131
+ end
132
+
133
+
134
+ describe Kelp::Dropdown, "dropdown_value_should_equal" do
135
+ before(:each) do
136
+ visit('/form')
137
+ end
138
+
139
+ context "passes when" do
140
+ it "the option with the 'selected' attribute has the given value" do
141
+ dropdown_value_should_equal "Height", "2"
142
+ end
143
+
144
+ it "the first option selected by default has the given value" do
145
+ dropdown_value_should_equal "Weight", "1"
146
+ end
147
+
148
+ it "the programmatically chosen option has the given value" do
149
+ select "Tall", :from => "Height"
150
+ dropdown_value_should_equal "Height", "3"
151
+ end
152
+ end
153
+
154
+ context "fails when" do
155
+ it "the option with the 'selected' attribute has a different value" do
156
+ lambda do
157
+ dropdown_value_should_equal "Height", "99"
158
+ end.should raise_error(RSpec::Expectations::ExpectationNotMetError)
159
+ end
160
+
161
+ it "the first option selected by default does not have given value" do
162
+ lambda do
163
+ dropdown_value_should_equal "Weight", "2"
164
+ end.should raise_error(RSpec::Expectations::ExpectationNotMetError)
165
+ end
166
+
167
+ it "the programmatically chosen option has a different value" do
168
+ select "Tall", :from => "Height"
169
+ lambda do
170
+ dropdown_value_should_equal "Height", "2"
171
+ end.should raise_error(RSpec::Expectations::ExpectationNotMetError)
172
+ end
173
+ end
174
+ end
175
+
176
+