kelp 0.1.9 → 0.2.0

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.
@@ -24,14 +24,30 @@ When /^I run cucumber on "(.+)"$/ do |feature|
24
24
  end
25
25
 
26
26
 
27
- Then /^the results should include:$/ do |expected_results|
28
- # Remove leading whitespace before comparison
29
- got = @output.gsub(/^\s*/, '')
30
- want = expected_results.gsub(/^\s*/, '')
31
- if !got.include?(want)
32
- got = @output.collect {|line| " #{line}"}.join
33
- want = expected_results.collect {|line| " #{line}"}.join
34
- raise("Expected:\n#{want}\nGot:\n#{got}")
27
+ # Ensure that the results include the expected lines, in the same order.
28
+ # Extra lines in the results are ignored.
29
+ Then /^the results should include:$/ do |expected_lines|
30
+ # Full actual output, for error reporting
31
+ got = @output.collect {|line| " #{line}"}.join
32
+ # Remove leading whitespace and split into lines
33
+ got_lines = @output.gsub(/^\s*/, '').split("\n")
34
+ want_lines = expected_lines.gsub(/^\s*/, '').split("\n")
35
+ last_index = -1
36
+ # Ensure that each wanted line is present in the output,
37
+ # and that it comes after any preceding expected lines.
38
+ want_lines.each do |want|
39
+ if !want.empty?
40
+ index = got_lines.index(want)
41
+ # Not found?
42
+ if index.nil?
43
+ raise("Got:\n#{got}\nExpected line not found:\n #{want}")
44
+ # Not in order?
45
+ elsif index < last_index
46
+ raise("Got:\n#{got}\nExpected line found, but out of order:\n #{want}")
47
+ else
48
+ last_index = index
49
+ end
50
+ end
35
51
  end
36
52
  end
37
53
 
data/kelp.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "kelp"
3
- s.version = "0.1.9"
3
+ s.version = "0.2.0"
4
4
  s.summary = "Cucumber helper methods"
5
5
  s.description = <<-EOS
6
6
  Kelp is a collection of helper methods for Cucumber to ease the process of
data/lib/kelp.rb CHANGED
@@ -7,20 +7,5 @@ require 'kelp/scoping'
7
7
  require 'kelp/visibility'
8
8
 
9
9
  module Kelp
10
- class << self
11
- attr_writer :driver
12
-
13
- # @return [Symbol]
14
- # Name of the default driver used by Kelp
15
- def default_driver
16
- :capybara
17
- end
18
-
19
- # @return [Symbol]
20
- # Name of the current driver used by Kelp (:capybara or :webrat)
21
- def driver
22
- @driver || default_driver
23
- end
24
- end
25
10
  end
26
11
 
@@ -9,9 +9,20 @@ module Kelp
9
9
  # @param [String] element_id
10
10
  # HTML `id` attribute of the element that should be disabled
11
11
  #
12
+ # @raise [Kelp::Unexpected]
13
+ # If the given element id is not found, or the element is not disabled
14
+ #
12
15
  def should_be_disabled(element_id)
13
- page.should have_xpath("//*[@id='#{element_id}']")
14
- page.should have_xpath("//*[@id='#{element_id}' and @disabled]")
16
+ if page.has_xpath?("//*[@id='#{element_id}']")
17
+ if !page.has_xpath?("//*[@id='#{element_id}' and @disabled]")
18
+ raise Kelp::Unexpected,
19
+ "Expected element with id='#{element_id}' to be disabled," + \
20
+ " but the 'disabled' attribute is not present."
21
+ end
22
+ else
23
+ raise Kelp::Unexpected,
24
+ "Element with id='#{element_id}' not found."
25
+ end
15
26
  end
16
27
 
17
28
 
@@ -21,9 +32,20 @@ module Kelp
21
32
  # @param [String] element_id
22
33
  # HTML `id` attribute of the element that should be enabled
23
34
  #
35
+ # @raise [Kelp::Unexpected]
36
+ # If the given element id is not found, or the element is not enabled
37
+ #
24
38
  def should_be_enabled(element_id)
25
- page.should have_xpath("//*[@id='#{element_id}']")
26
- page.should have_no_xpath("//*[@id='#{element_id}' and @disabled]")
39
+ if page.has_xpath?("//*[@id='#{element_id}']")
40
+ if page.has_xpath?("//*[@id='#{element_id}' and @disabled]")
41
+ raise Kelp::Unexpected,
42
+ "Expected element with id='#{element_id}' to be enabled," + \
43
+ " but the 'disabled' attribute is present."
44
+ end
45
+ else
46
+ raise Kelp::Unexpected,
47
+ "Element with id='#{element_id}' not found."
48
+ end
27
49
  end
28
50
 
29
51
  end
data/lib/kelp/checkbox.rb CHANGED
@@ -1,29 +1,54 @@
1
1
  require 'kelp/helper'
2
2
  require 'kelp/scoping'
3
+ require 'kelp/exceptions'
3
4
 
4
5
  module Kelp
5
6
  module Checkbox
6
7
  include Scoping
7
8
  include Helper
8
9
 
10
+ # Verify that the given checkbox is checked.
11
+ #
12
+ # @param [String] checkbox
13
+ # Capybara locator for the checkbox
14
+ # @param [Hash] scope
15
+ # Scoping keywords as understood by {#in_scope}
16
+ #
17
+ # @raise [Kelp::Unexpected]
18
+ # If the given checkbox is not checked
19
+ #
20
+ # @since 0.1.2
21
+ #
9
22
  def checkbox_should_be_checked(checkbox, scope={})
10
23
  in_scope(scope) do
11
24
  field_checked = find_field(checkbox)['checked']
12
- if field_checked.respond_to? :should
13
- field_checked.should be_true
14
- else
15
- assert field_checked
25
+ puts field_checked.inspect
26
+ if !field_checked
27
+ raise Kelp::Unexpected,
28
+ "Expected '#{checkbox}' to be checked, but it is unchecked."
16
29
  end
17
30
  end
18
31
  end
19
32
 
33
+
34
+ # Verify that the given checkbox is not checked.
35
+ #
36
+ # @param [String] checkbox
37
+ # Capybara locator for the checkbox
38
+ # @param [Hash] scope
39
+ # Scoping keywords as understood by {#in_scope}
40
+ #
41
+ # @raise [Kelp::Unexpected]
42
+ # If the given checkbox is checked
43
+ #
44
+ # @since 0.1.2
45
+ #
20
46
  def checkbox_should_not_be_checked(checkbox, scope={})
21
47
  in_scope(scope) do
22
48
  field_checked = find_field(checkbox)['checked']
23
- if field_checked.respond_to? :should
24
- field_checked.should be_false
25
- else
26
- assert !field_checked
49
+ if field_checked
50
+ raise Kelp::Unexpected,
51
+ "Expected '#{checkbox}' to be unchecked, but it is checked."
27
52
  end
28
53
  end
29
54
  end
data/lib/kelp/dropdown.rb CHANGED
@@ -22,6 +22,9 @@ module Kelp
22
22
  # @param [Hash] scope
23
23
  # Scoping keywords as understood by {#in_scope}
24
24
  #
25
+ # @raise [Kelp::Unexpected]
26
+ # If the given dropdown does not equal `value`
27
+ #
25
28
  def dropdown_should_equal(dropdown, value, scope={})
26
29
  in_scope(scope) do
27
30
  field = nice_find_field(dropdown)
@@ -37,7 +40,10 @@ module Kelp
37
40
  field_value = xpath_sanitize(field.value)
38
41
  selected = field.find(:xpath, ".//option[@value=#{field_value}]")
39
42
  end
40
- selected.text.should =~ /#{value}/
43
+ if selected.text != value
44
+ raise Kelp::Unexpected,
45
+ "Expected '#{dropdown}' dropdown to equal '#{value}'\nGot '#{selected.text}' instead"
46
+ end
41
47
  end
42
48
  end
43
49
 
@@ -57,15 +63,22 @@ module Kelp
57
63
  # @param [Hash] scope
58
64
  # Scoping keywords as understood by {#in_scope}
59
65
  #
66
+ # @raise [Kelp::Unexpected]
67
+ # If the given dropdown does not include all `values`
68
+ #
60
69
  def dropdown_should_include(dropdown, values, scope={})
61
70
  in_scope(scope) do
62
71
  # If values is a String, convert it to an Array
63
72
  values = [values] if values.class == String
64
-
65
73
  field = nice_find_field(dropdown)
66
- # Look for each value
67
- values.each do |value|
68
- page.should have_xpath(".//option[text()=#{xpath_sanitize(value)}]")
74
+ # Select all expected values that don't appear in the dropdown
75
+ unexpected = values.select do |value|
76
+ !page.has_xpath?(".//option[text()=#{xpath_sanitize(value)}]")
77
+ end
78
+ if !unexpected.empty?
79
+ raise Kelp::Unexpected,
80
+ "Expected '#{dropdown}' dropdown to include: #{values.inspect}" + \
81
+ "\nMissing: #{unexpected.inspect}"
69
82
  end
70
83
  end
71
84
  end
@@ -82,17 +95,24 @@ module Kelp
82
95
  # @param [Hash] scope
83
96
  # Scoping keywords as understood by {#in_scope}
84
97
  #
98
+ # @raise [Kelp::Unexpected]
99
+ # If the given dropdown includes any value in `values`
100
+ #
85
101
  # @since 0.1.2
86
102
  #
87
103
  def dropdown_should_not_include(dropdown, values, scope={})
88
104
  in_scope(scope) do
89
105
  # If values is a String, convert it to an Array
90
106
  values = [values] if values.class == String
91
-
92
107
  field = nice_find_field(dropdown)
93
- # Look for each value
94
- values.each do |value|
95
- page.should have_no_xpath(".//option[text()=#{xpath_sanitize(value)}]")
108
+ # Select all not-expected values that do appear in the dropdown
109
+ unexpected = values.select do |value|
110
+ page.has_xpath?(".//option[text()=#{xpath_sanitize(value)}]")
111
+ end
112
+ if !unexpected.empty?
113
+ raise Kelp::Unexpected,
114
+ "Expected '#{dropdown}' dropdown to not include: #{values.inspect}" + \
115
+ "\nDid include: #{unexpected.inspect}"
96
116
  end
97
117
  end
98
118
  end
@@ -108,10 +128,16 @@ module Kelp
108
128
  # @param [String] value
109
129
  # Expected `value` attribute of the selected `option`
110
130
  #
131
+ # @raise [Kelp::Unexpected]
132
+ # If the given dropdown's 'value' attribute is not equal to `value`
133
+ #
111
134
  def dropdown_value_should_equal(dropdown, value)
112
- # FIXME: When this returns False, does that fail the step?
113
135
  field = find_field(dropdown)
114
- field.value.should include(value)
136
+ if field.value != value
137
+ raise Kelp::Unexpected,
138
+ "Expected '#{dropdown}' dropdown's value to equal '#{value}'" + \
139
+ "\nGot '#{field.value}' instead"
140
+ end
115
141
  end
116
142
 
117
143
  end
@@ -1,5 +1,7 @@
1
1
  module Kelp
2
- class Unexpected < RuntimeError
3
- end
2
+ class KelpError < StandardError; end
3
+ class Unexpected < KelpError; end
4
+ class FieldNotFound < KelpError; end
5
+ class OptionNotFound < KelpError; end
4
6
  end
5
7
 
data/lib/kelp/field.rb CHANGED
@@ -19,13 +19,35 @@ module Kelp
19
19
  # @param [String] value
20
20
  # Text to select from a dropdown or enter in a text box
21
21
  #
22
+ # @raise [Kelp::FieldNotFound]
23
+ # If no field matching `field` is found
24
+ # @raise [Kelp::OptionNotFound]
25
+ # If a dropdown matching `field` is found, but it has no
26
+ # option matching `value`
27
+ #
22
28
  # @since 0.1.9
23
29
  #
24
30
  def select_or_fill(field, value)
25
31
  begin
26
32
  select(value, :from => field)
27
- rescue
28
- fill_in(field, :with => value)
33
+ rescue Capybara::ElementNotFound => err
34
+ # The select method may raise ElementNotFound in two cases:
35
+ # (1) The given field does not exist, in which case try a text field
36
+ if err.message =~ /no select box with id, name, or label/
37
+ begin
38
+ fill_in(field, :with => value)
39
+ rescue Capybara::ElementNotFound
40
+ raise Kelp::FieldNotFound,
41
+ "No field with id, name, or label '#{field}' found"
42
+ end
43
+ # (2) The field exists, but the value does not
44
+ elsif err.message =~ /no option with text/
45
+ raise Kelp::OptionNotFound,
46
+ "Field '#{field}' has no option '#{value}'"
47
+ # And just in case...
48
+ else
49
+ raise
50
+ end
29
51
  end
30
52
  end
31
53
 
@@ -41,70 +63,77 @@ module Kelp
41
63
  # Text to select from a dropdown or enter in a text box, or
42
64
  # `checked` or `unchecked` to operate on a checkbox
43
65
  #
66
+ # @raise [Kelp::Nonexistent]
67
+ # If no checkbox, dropdown, or text box is found with the given
68
+ # identifier
69
+ #
44
70
  # @since 0.1.9
45
71
  #
46
72
  def check_or_select_or_fill(field, value)
47
73
  # If value is "checked" or "unchecked", assume
48
74
  # field is a checkbox
49
- if value == "checked"
50
- begin
75
+ begin
76
+ if value == "checked"
51
77
  check(field)
52
- rescue
53
- select_or_fill(field, value)
54
- end
55
- elsif value == "unchecked"
56
- begin
78
+ elsif value == "unchecked"
57
79
  uncheck(field)
58
- rescue
80
+ else
59
81
  select_or_fill(field, value)
60
82
  end
61
- else
83
+ rescue Capybara::ElementNotFound
62
84
  select_or_fill(field, value)
63
85
  end
64
86
  end
65
87
 
66
88
 
67
- # Fill in multiple fields according to values in a `Hash`.
68
- # Fields may be text boxes, dropdowns/listboxes, or checkboxes.
89
+ # Fill in a single field within the scope of a given selector.
90
+ # The field may be a text box, dropdown/listbox, or checkbox.
69
91
  # See {#check_or_select_or_fill} for details.
70
92
  #
71
93
  # @example
72
- # fill_in_fields "First name" => "Otto", "Last name" => "Scratchansniff"
73
- # fill_in_fields "phone" => "303-224-7428", :within => "#home"
94
+ # fill_in_field "Email", "otto@scratchansniff.wb"
95
+ # fill_in_field "Send me junk email", "unchecked"
74
96
  #
75
- # @param [Hash] field_values
76
- # "field" => "value" for each field to fill in, select, or check/uncheck
97
+ # @param [String] field
98
+ # Capybara locator for the field (name, id, or label text)
99
+ # @param [String] value
100
+ # Text to select from a dropdown or enter in a text box, or
101
+ # `checked` or `unchecked` to operate on a checkbox
77
102
  # @param [Hash] scope
78
103
  # Scoping keywords as understood by {#in_scope}
79
104
  #
80
- def fill_in_fields(field_values, scope={})
105
+ # @raise [Kelp::FieldNotFound]
106
+ # If no field is found matching the given identifier
107
+ #
108
+ def fill_in_field(field, value, scope={})
81
109
  in_scope(scope) do
82
- field_values.each do |field, value|
83
- check_or_select_or_fill(field, value)
84
- end
110
+ check_or_select_or_fill(field, value)
85
111
  end
86
112
  end
87
113
 
88
114
 
89
- # Fill in a single field within the scope of a given selector.
90
- # The field may be a text box, dropdown/listbox, or checkbox.
115
+ # Fill in multiple fields according to values in a `Hash`.
116
+ # Fields may be text boxes, dropdowns/listboxes, or checkboxes.
91
117
  # See {#check_or_select_or_fill} for details.
92
118
  #
93
119
  # @example
94
- # fill_in_field "Email", "otto@scratchansniff.wb"
95
- # fill_in_field "Send me junk email", "unchecked"
120
+ # fill_in_fields "First name" => "Otto", "Last name" => "Scratchansniff"
121
+ # fill_in_fields "phone" => "303-224-7428", :within => "#home"
96
122
  #
97
- # @param [String] field
98
- # Capybara locator for the field (name, id, or label text)
99
- # @param [String] value
100
- # Text to select from a dropdown or enter in a text box, or
101
- # `checked` or `unchecked` to operate on a checkbox
123
+ # @param [Hash] field_values
124
+ # "field" => "value" for each field to fill in, select, or check/uncheck
102
125
  # @param [Hash] scope
103
126
  # Scoping keywords as understood by {#in_scope}
104
127
  #
105
- def fill_in_field(field, value, scope={})
106
- fields = {field => value}
107
- fill_in_fields fields, scope
128
+ # @raise [Kelp::Nonexistent]
129
+ # If any field could not be found
130
+ #
131
+ def fill_in_fields(field_values, scope={})
132
+ in_scope(scope) do
133
+ field_values.each do |field, value|
134
+ check_or_select_or_fill(field, value)
135
+ end
136
+ end
108
137
  end
109
138
 
110
139
 
@@ -135,11 +164,15 @@ module Kelp
135
164
  # @param [Hash] scope
136
165
  # Scoping keywords as understood by {#in_scope}
137
166
  #
167
+ # @raise [Kelp::Unexpected]
168
+ # If the given field is not empty or nil
169
+ #
138
170
  def field_should_be_empty(field, scope={})
139
171
  in_scope(scope) do
140
172
  _field = nice_find_field(field)
141
173
  if !(_field.nil? || _field.value.nil? || _field.value == '')
142
- raise RuntimeError, "Expected field '#{field}' to be empty, but value is '#{_field.value}'"
174
+ raise Kelp::Unexpected,
175
+ "Expected field '#{field}' to be empty, but value is '#{_field.value}'"
143
176
  end
144
177
  end
145
178
  end
@@ -154,12 +187,15 @@ module Kelp
154
187
  # @param [Hash] scope
155
188
  # Scoping keywords as understood by {#in_scope}
156
189
  #
190
+ # @raise [Kelp::Unexpected]
191
+ # If the given field does not contain `value`
192
+ #
157
193
  def field_should_contain(field, value, scope={})
158
194
  in_scope(scope) do
159
195
  # TODO: Make this work equally well with any kind of field
160
196
  # (text, single-select, multi-select)
161
- field = find_field(field)
162
- field_value = (field.tag_name == 'textarea') ? field.text : field.value
197
+ element = find_field(field)
198
+ field_value = (element.tag_name == 'textarea') ? element.text : element.value
163
199
  # If field value is an Array, take the first item
164
200
  if field_value.class == Array
165
201
  field_value = field_value.first
@@ -167,17 +203,17 @@ module Kelp
167
203
  # Escape any problematic characters in the expected value
168
204
  value = Regexp.escape(value)
169
205
  # Match actual to expected
170
- if field_value.respond_to? :should
171
- field_value.should =~ /#{value}/
172
- else
173
- assert_match(/#{value}/, field_value)
206
+ if !(field_value =~ /#{value}/)
207
+ raise Kelp::Unexpected,
208
+ "Expected '#{field}' to contain '#{value}'" + \
209
+ "\nGot '#{field_value}'"
174
210
  end
175
211
  end
176
212
  end
177
213
 
178
214
 
179
215
  def field_should_not_contain(field, value, scope={})
180
- raise "Not implemented yet"
216
+ raise NotImplementedError, "Not implemented yet"
181
217
  #with_scope(parent) do
182
218
  #field = find_field(field)
183
219
  #field_value = (field.tag_name == 'textarea') ? field.text : field.value
@@ -197,6 +233,9 @@ module Kelp
197
233
  # @param [Hash] scope
198
234
  # Scoping keywords as understood by {#in_scope}
199
235
  #
236
+ # @raise [Kelp::Unexpected]
237
+ # If any field does not contain the expected value
238
+ #
200
239
  def fields_should_contain(field_values, scope={})
201
240
  in_scope(scope) do
202
241
  field_values.each do |field, value|