kelp 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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
+