effective_test_bot 1.1.0 → 1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a58127dee25edf6d59d56186bdadc7c6b20b46008d78757e9f29471ab4c77d08
4
- data.tar.gz: ac0be189890058266890718946bb912bf34105ff39720a92a9041ae16e8ab973
3
+ metadata.gz: 4ca4801862674dc8fc51f92efc4240d754b3308221ff9dd3860f99ac61a2a66c
4
+ data.tar.gz: 1cf30b273097f94620dd075945bb468026615692f4a113df883a36d5a72caf07
5
5
  SHA512:
6
- metadata.gz: 4e039db2969eb5f7f3e1cebaaddb2d20987c2bafd05fac9832addf84c8b76632debad04a4988c60d5e647436e8bd01c1548fc067095d288dedf9167c1690f413
7
- data.tar.gz: 4e5ca33c5de04e4e0fab7831a855bf6254a1c1ad94d8fd9c4b3e4b22546c17e24405cf9d43bf8530ff878a91bc930c92d2bfaa1271d12d9c1b615ff2b6edc070
6
+ metadata.gz: 225ab57659a51d3fb7af5648ac4116abb7c2cf05ab9c32aef6c2f79fdfedeacefd180dafbdd43e0be7fd85f298a7015a3dc326766bfa185576ff662256e5e4f4
7
+ data.tar.gz: 3b7100066628df695c8c0f01e14ea0ad69808ab057c5b765f80c346bf915e4d1c13c5fa89804ff80dbe18ae6b7a5e6df2914845b4f812f3303293fb7a47de871
data/README.md CHANGED
@@ -27,7 +27,7 @@ This is the 1.0 series of effective_test_bot.
27
27
 
28
28
  This requires Rails 5.1+
29
29
 
30
- Please check out [Effective TestBot Capybara-Webkit branch](https://github.com/code-and-effect/effective_test_bot/tree/capybara-webit) for more information using this gem with Capybara Webkit and Rails < 5.1.
30
+ Please check out [Effective TestBot Capybara-Webkit branch](https://github.com/code-and-effect/effective_test_bot/tree/capybara-webkit) for more information using this gem with Capybara Webkit and Rails < 5.1.
31
31
 
32
32
  This works with Rails 6.0.0.rc1 and parallelization.
33
33
 
@@ -95,7 +95,7 @@ module EffectiveTestBot
95
95
 
96
96
  def self.tour_mode?
97
97
  if ENV['TOUR'].present?
98
- ['true', 'verbose', 'debug'].include?(ENV['TOUR'].to_s.downcase)
98
+ ['true', 'extreme', 'verbose', 'debug'].include?(ENV['TOUR'].to_s.downcase)
99
99
  else
100
100
  screenshots? && (tour_mode != false)
101
101
  end
@@ -4,8 +4,9 @@ module EffectiveTestBot
4
4
 
5
5
  included do
6
6
  include EffectiveTestBotAssertions
7
- include EffectiveTestBotFormHelper
7
+ include EffectiveTestBotFormFaker
8
8
  include EffectiveTestBotFormFiller
9
+ include EffectiveTestBotFormHelper
9
10
  include EffectiveTestBotLoginHelper
10
11
  include EffectiveTestBotMinitestHelper
11
12
  include EffectiveTestBotScreenshotsHelper
@@ -1,3 +1,3 @@
1
1
  module EffectiveTestBot
2
- VERSION = '1.1.0'.freeze
2
+ VERSION = '1.1.1'.freeze
3
3
  end
@@ -0,0 +1,271 @@
1
+ # This is all private stuff. See effective_test_bot_form_helper.rb for public DSL
2
+
3
+ require 'faker'
4
+
5
+ module EffectiveTestBotFormFaker
6
+ DIGITS = ('1'..'9').to_a
7
+ LETTERS = %w(A B C E G H J K L M N P R S T V X Y) # valid letters of a canadian postal code, eh?
8
+
9
+ # Generates an appropriately pseudo-random value for the given field
10
+ # Pass in a Hash of fills to define pre-selected values
11
+ #
12
+ # Operates on just string keys, no symbols here
13
+ def faker_value_for_field(field, fills = {})
14
+ field_name = [field.tag_name, field['type']].compact.join('_')
15
+ attributes = field['name'].to_s.gsub(']', '').split('[') # user[something_attributes][last_name] => ['user', 'something_attributes', 'last_name']
16
+ attribute = attributes.last.to_s
17
+
18
+ fill_value = fill_value_for_field(fills, attributes, field['value'], field_name)
19
+
20
+ # If there is a predefined fill value for this field return it now
21
+ # except for select, checkbox and radio fields which we want to match by value or label
22
+ if fill_value.present? && !['select', 'input_checkbox', 'input_radio'].include?(field_name)
23
+ return fill_value
24
+ end
25
+
26
+ case field_name
27
+ when 'input_text'
28
+ classes = field['class'].to_s.split(' ')
29
+
30
+ if classes.include?('date') # Let's assume this is a date input.
31
+ if attribute.include?('end') || attribute.include?('expire') # Make sure end dates are after start dates
32
+ Faker::Date.forward(365).strftime('%Y-%m-%d')
33
+ else
34
+ Faker::Date.backward(365).strftime('%Y-%m-%d')
35
+ end
36
+ elsif classes.include?('datetime')
37
+ if attribute.include?('end') || attribute.include?('expire')
38
+ Faker::Date.forward(365).strftime('%Y-%m-%d %H:%m')
39
+ else
40
+ Faker::Date.backward(365).strftime('%Y-%m-%d %H:%m')
41
+ end
42
+ elsif classes.include?('numeric')
43
+ value_for_input_numeric_field(field, "input.numeric[name$='[#{attribute}]']")
44
+ elsif classes.include?('email') || attribute.include?('email')
45
+ Faker::Internet.email
46
+ elsif classes.include?('price') # effective_form_inputs price
47
+ 4.times.map { DIGITS.sample }.join('') + '.00'
48
+ elsif classes.include?('numeric') || attribute.include?('number')
49
+ min = (Float(field['min']) rescue 1)
50
+ max = (Float(field['max']) rescue 1000)
51
+ number = Random.new.rand(min..max)
52
+ number.kind_of?(Float) ? number.round(2) : number
53
+ elsif attribute.include?('first_name')
54
+ Faker::Name.first_name
55
+ elsif attribute.include?('last_name')
56
+ Faker::Name.last_name
57
+ elsif attribute.include?('website')
58
+ Faker::Internet.url
59
+ elsif attribute.include?('city')
60
+ Faker::Address.city
61
+ elsif attribute.include?('address2')
62
+ Faker::Address.secondary_address
63
+ elsif attribute.include?('address')
64
+ Faker::Address.street_address
65
+ elsif attribute.include?('name')
66
+ Faker::Name.name
67
+ elsif attribute.include?('postal_code')
68
+ if @filled_country_fields == 'US'
69
+ DIGITS.sample + DIGITS.sample + DIGITS.sample + DIGITS.sample + DIGITS.sample
70
+ else
71
+ LETTERS.sample + DIGITS.sample + LETTERS.sample + ' ' + DIGITS.sample + LETTERS.sample + DIGITS.sample
72
+ end
73
+ elsif attribute.include?('postal') # Make a Canadian postal code
74
+ LETTERS.sample + DIGITS.sample + LETTERS.sample + ' ' + DIGITS.sample + LETTERS.sample + DIGITS.sample
75
+ elsif attribute.include?('zip') && attribute.include?('code') # Make a US zip code
76
+ DIGITS.sample + DIGITS.sample + DIGITS.sample + DIGITS.sample + DIGITS.sample
77
+ elsif attribute.include?('social_insurance_number') || attributes.include?('sin_number')
78
+ "#{DIGITS.sample(3).join} #{DIGITS.sample(3).join} #{DIGITS.sample(3).join}"
79
+ elsif attribute.include?('slug')
80
+ Faker::Lorem.words(3).join(' ').parameterize
81
+ else
82
+ Faker::Lorem.words(3).join(' ').capitalize
83
+ end
84
+
85
+ when 'input_checkbox'
86
+ value_for_input_checkbox_field(field, fill_value)
87
+
88
+ when 'input_color'
89
+ Faker::Color.hex_color
90
+
91
+ when 'input_email'
92
+ Faker::Internet.email
93
+
94
+ when 'input_file'
95
+ if field['class'].to_s.include?('asset-box-uploader-fileinput')
96
+ "#{File.dirname(__FILE__).sub('test/support', 'test/fixtures')}/documents._test"
97
+ else
98
+ "#{File.dirname(__FILE__).sub('test/support', 'test/fixtures')}/logo.png"
99
+ end
100
+
101
+ when 'input_number'
102
+ value_for_input_numeric_field(field, "input[type='number'][name$='[#{attribute}]']")
103
+
104
+ when 'input_password'
105
+ # Use the same password throughout a single test. Allows passwords and password_confirmations to match.
106
+ @filled_password_fields ||= Faker::Internet.password
107
+
108
+ when 'input_radio'
109
+ value_for_input_radio_field(field, fill_value)
110
+
111
+ when 'select', 'select_select-one'
112
+ value_for_input_select_field(field, fill_value)
113
+
114
+ when 'select_select-multiple'
115
+ 1.upto(3).to_a.map { value_for_input_select_field(field, fill_value) }.uniq
116
+
117
+ when 'input_tel'
118
+ d = 10.times.map { DIGITS.sample }
119
+ d[0] + d[1] + d[2] + '-' + d[3] + d[4] + d[5] + '-' + d[6] + d[7] + d[8] + d[9]
120
+
121
+ when 'input_url'
122
+ Faker::Internet.url
123
+
124
+ when 'textarea', 'textarea_textarea'
125
+ Faker::Lorem.paragraph
126
+
127
+ when 'input_submit', 'input_search', 'input_button'
128
+ nil
129
+
130
+ else
131
+ raise "fill_value unsupported field type: #{field_name}"
132
+ end
133
+ end
134
+
135
+ private
136
+
137
+ def fill_value_for_field(fills, attributes, value, field_name)
138
+ return if fills.blank? || (attributes.blank? && value.blank?)
139
+
140
+ key = nil
141
+ attributes.reverse_each do |name| # match last_name, then something_attributes.last_name, then user.something_attributes.last_name
142
+ key = (key.present? ? "#{name}.#{key}" : name) # builds up the string as we go along
143
+
144
+ return :unselect if field_name == 'select' && fills.key?(key) && [nil, ''].include?(fills[key])
145
+ return fills[key].to_s if fills.key?(key)
146
+ end
147
+
148
+ return fills[value] if fills.key?(value)
149
+
150
+ nil
151
+ end
152
+
153
+ def value_for_input_select_field(field, fill_value)
154
+ return fill_value if fill_value == :unselect
155
+ country_code = field['name'].to_s.include?('country_code')
156
+
157
+ if fill_value.present? # accept a value or text
158
+ field.all('option:enabled').each do |option|
159
+ if (option.text == fill_value || option.value.to_s == fill_value)
160
+ @filled_country_fields = option.value if country_code
161
+ return option.text
162
+ end
163
+ end
164
+ end
165
+
166
+ option = field.all('option:enabled').select { |option| option.value.present? }.sample
167
+
168
+ @filled_country_fields = option.try(:value) if country_code # So Postal Code can be set to a valid one
169
+
170
+ option.try(:text) || ''
171
+ end
172
+
173
+ def value_for_input_checkbox_field(field, fill_value)
174
+ if !fill_value.nil?
175
+ if truthy?(fill_value)
176
+ true
177
+ elsif falsey?(fill_value)
178
+ false
179
+ else
180
+ fill_values = Array(fill_value) # Allow an array of fill values to be passed
181
+ (fill_values.include?(field['value']) || fill_values.include?(field.find(:xpath, '..').text))
182
+ end
183
+ elsif field['required'].nil? == false
184
+ true
185
+ elsif field['value'] == 'true'
186
+ true
187
+ elsif field['value'] == 'false'
188
+ false
189
+ else
190
+ [true, false].sample
191
+ end
192
+ end
193
+
194
+ # The first time we run into a radio button, we definitely want to set it to TRUE so it's definitely selected
195
+ # Subsequent ones, we can randomly true/false
196
+ # Then if we run into something that has a fill_value, we definitely want to set that one to TRUE, and false the rest
197
+ def value_for_input_radio_field(field, fill_value)
198
+ @filled_radio_fields ||= {}
199
+
200
+ previous = @filled_radio_fields[field['name']]
201
+
202
+ retval =
203
+ if previous == true # We've selected one of the options before
204
+ [true, false].sample
205
+ elsif previous.kind_of?(String) # We selected a previous option with a specific fill_value
206
+ false
207
+ else # We've never seen this radio field before
208
+ true
209
+ end
210
+
211
+ if fill_value.present? && (fill_value == field['value'] || fill_value == field.find(:xpath, '..').text)
212
+ @filled_radio_fields[field['name']] = fill_value
213
+ true
214
+ elsif fill_value.present?
215
+ nil
216
+ else
217
+ @filled_radio_fields[field['name']] ||= true
218
+ retval
219
+ end
220
+ end
221
+
222
+ def value_for_input_numeric_field(field, selector)
223
+ min = (Float(field['min']) rescue 0)
224
+ max = (Float(field['max']) rescue 1000)
225
+ number = Random.new.rand(min..max)
226
+ number = (number.kind_of?(Float) ? number.round(2) : number)
227
+
228
+ if (field['step'] == '1' || field['class'].to_s.split(' ').include?('integer'))
229
+ number = number.to_i
230
+ end
231
+
232
+ return number if field['max'].blank?
233
+
234
+ shared_max_fields = all(selector)
235
+ return number if shared_max_fields.length <= 1
236
+
237
+ # So there's definitely 2+ fields that share the same max, named the same
238
+ # We want the total value of all these fields to add upto the max single max value
239
+ @filled_numeric_fields ||= {}
240
+ @filled_numeric_fields[selector] ||= max
241
+
242
+ available = @filled_numeric_fields[selector]
243
+
244
+ amount = if max.kind_of?(Float)
245
+ (((max * 1000.0) / shared_max_fields.length.to_f).ceil() / 1000.0).round(2)
246
+ else
247
+ (max / shared_max_fields.length.to_f).ceil
248
+ end
249
+ amount = [[amount, min].max, available].min
250
+
251
+ @filled_numeric_fields[selector] = (available - amount)
252
+ amount
253
+ end
254
+
255
+ def truthy?(value)
256
+ if defined?(::ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES) # Rails 5
257
+ ::ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(value)
258
+ else
259
+ ActiveRecord::Type::Boolean.new.cast(value)
260
+ end
261
+ end
262
+
263
+ def falsey?(value)
264
+ if defined?(::ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES) # Rails 5
265
+ ::ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES.include?(value)
266
+ else
267
+ ::ActiveRecord::Type::Boolean.new.cast(value) == false
268
+ end
269
+ end
270
+
271
+ end
@@ -1,13 +1,12 @@
1
+ # This is all private stuff. See effective_test_bot_form_helper.rb for public DSL
2
+
1
3
  require 'timeout'
2
- require 'faker'
3
4
 
4
5
  module EffectiveTestBotFormFiller
5
- DIGITS = ('1'..'9').to_a
6
- LETTERS = %w(A B C E G H J K L M N P R S T V X Y) # valid letters of a canadian postal code, eh?
7
6
 
8
7
  # Fill a boostrap tabs based form
9
- def fill_bootstrap_tabs_form(fills = {}, boostrap_tab_elements = nil)
10
- tabs = boostrap_tab_elements || all("a[data-toggle='tab']")
8
+ def fill_bootstrap_tabs_form(fills = {})
9
+ tabs = all("a[data-toggle='tab']")
11
10
 
12
11
  # If there's only 1 tab, just fill it out
13
12
  (fill_form_fields(fills) and return) unless tabs.length > 1
@@ -49,73 +48,77 @@ module EffectiveTestBotFormFiller
49
48
 
50
49
  # Only fills in visible fields
51
50
  # fill_form(:email => 'somethign@soneone.com', :password => 'blahblah', 'user.last_name' => 'hlwerewr')
52
- def fill_form_fields(fills = {})
53
-
51
+ def fill_form_fields(fills = {}, debug: false)
54
52
  save_test_bot_screenshot
55
53
 
56
- # Support for the cocoon gem
57
- all('a.add_fields[data-association-insertion-template],a.has_many_add').each do |field|
58
- next if skip_form_field?(field)
54
+ seen = {}
59
55
 
60
- if EffectiveTestBot.tour_mode_extreme?
61
- 2.times { field.click(); save_test_bot_screenshot }
62
- else
63
- 2.times { field.click() }
64
- save_test_bot_screenshot
65
- end
66
- end
56
+ 5.times do
57
+ # Support for the cocoon gem
58
+ fields = all('a.add_fields[data-association-insertion-template],a.has_many_add').reject { |field| seen[field_key(field)] }
67
59
 
68
- all('input,select,textarea', visible: false).each do |field|
69
- next if skip_form_field?(field)
70
- skip_field_screenshot = false
71
-
72
- field_name = [field.tag_name, field['type']].compact.join('_')
73
- value = value_for_field(field, fills)
74
-
75
- case field_name
76
- when 'input_text', 'input_email', 'input_password', 'input_tel', 'input_number', 'input_url', 'input_color'
77
- field.set(value)
78
- close_effective_date_time_picker(field) if field['class'].to_s.include?('effective_date')
79
- when 'input_checkbox', 'input_radio'
80
- begin
81
- field.set(value)
82
- rescue => e
83
- label = first(:label, for: field['id'])
84
- label.click if label
85
- end
86
- when 'textarea', 'textarea_textarea'
87
- ckeditor_text_area?(field) ? fill_ckeditor_text_area(field, value) : field.set(value)
88
- when 'select', 'select_select-one'
89
- if EffectiveTestBot.tour_mode_extreme? && field['class'].to_s.include?('select2') # select2
90
- try_script "$('select##{field['id']}').select2('open')"
91
- save_test_bot_screenshot
60
+ fields.each do |field|
61
+ seen[field_key(field)] = true
62
+ next if skip_form_field?(field)
63
+
64
+ if EffectiveTestBot.tour_mode_extreme?
65
+ 2.times { field.click(); save_test_bot_screenshot }
66
+ else
67
+ 2.times { field.click() }; save_test_bot_screenshot
92
68
  end
69
+ end
70
+
71
+ # Fill all fields now
72
+ fields = all('input,select,textarea', visible: false).reject { |field| seen[field_key(field)] }
73
+
74
+ break unless fields.present?
75
+
76
+ fields.each do |field|
77
+ seen[field_key(field)] = true
78
+ skip_field_screenshot = false
93
79
 
94
- if field.all('option:enabled').length > 0 && value != :unselect
95
- field.select(value, match: :first, disabled: false)
80
+ if debug
81
+ puts "CONSIDERING: #{field_key(field)}"
82
+ puts " -> SKIPPED" if skip_form_field?(field)
96
83
  end
97
84
 
98
- if EffectiveTestBot.tour_mode_extreme? && field['class'].to_s.include?('select2')
99
- try_script "$('select##{field['id']}').select2('close')"
85
+ next if skip_form_field?(field)
86
+
87
+ value = faker_value_for_field(field, fills)
88
+
89
+ if debug
90
+ puts " -> FILLING: #{value}"
100
91
  end
101
- when 'input_file'
102
- if field['class'].to_s.include?('asset-box-uploader-fileinput')
103
- upload_effective_asset(field, value)
92
+
93
+ field_name = [field.tag_name, field['type']].compact.join('_')
94
+
95
+ case field_name
96
+ when 'input_text', 'input_email', 'input_password', 'input_tel', 'input_number', 'input_url', 'input_color'
97
+ if field['class'].to_s.include?('effective_date')
98
+ fill_input_date(field, value)
99
+ else
100
+ fill_input_text(field, value)
101
+ end
102
+ when 'input_checkbox'
103
+ fill_input_checkbox(field, value)
104
+ when 'input_radio'
105
+ fill_input_radio(field, value)
106
+ when 'textarea', 'textarea_textarea'
107
+ fill_input_text_area(field, value)
108
+ when 'select', 'select_select-one', 'select_select-multiple'
109
+ fill_input_select(field, value)
110
+ when 'input_file'
111
+ fill_input_file(file, value)
112
+ when 'input_submit', 'input_search', 'input_button'
113
+ skip_field_screenshot = true # Do nothing
104
114
  else
105
- field.set(value)
115
+ raise "unsupported field type #{field_name}"
106
116
  end
107
- when 'input_submit', 'input_search', 'input_button'
108
- skip_field_screenshot = true
109
- # Do nothing
110
- else
111
- raise "unsupported field type #{field_name}"
117
+
118
+ save_test_bot_screenshot if EffectiveTestBot.tour_mode_extreme? && !skip_field_screenshot
112
119
  end
113
120
 
114
121
  wait_for_ajax
115
-
116
- if EffectiveTestBot.tour_mode_extreme?
117
- save_test_bot_screenshot unless skip_field_screenshot
118
- end
119
122
  end
120
123
 
121
124
  # Clear any value_for_field momoized values
@@ -127,228 +130,69 @@ module EffectiveTestBotFormFiller
127
130
  save_test_bot_screenshot
128
131
  end
129
132
 
130
- # Generates an appropriately pseudo-random value for the given field
131
- # Pass in a Hash of fills to define pre-selected values
132
- #
133
- # Operates on just string keys, no symbols here
134
-
135
- def value_for_field(field, fills = {})
136
- field_name = [field.tag_name, field['type']].compact.join('_')
137
- attributes = field['name'].to_s.gsub(']', '').split('[') # user[something_attributes][last_name] => ['user', 'something_attributes', 'last_name']
138
- attribute = attributes.last.to_s
133
+ def fill_input_text(field, value)
134
+ field.set(value)
135
+ end
139
136
 
140
- fill_value = fill_value_for_field(fills, attributes, field['value'], field_name)
137
+ def fill_input_date(field, value)
138
+ field.set(value)
139
+ try_script "$('input##{field['id']}').data('DateTimePicker').hide()"
140
+ end
141
141
 
142
- # If there is a predefined fill value for this field return it now
143
- # except for select, checkbox and radio fields which we want to match by value or label
144
- if fill_value.present? && !['select', 'input_checkbox', 'input_radio'].include?(field_name)
145
- return fill_value
142
+ def fill_input_checkbox(field, value)
143
+ return if value.nil?
144
+
145
+ begin
146
+ field.set(value)
147
+ rescue => e
148
+ label = first(:label, for: field['id'])
149
+ label.click if label
146
150
  end
151
+ end
147
152
 
148
- case field_name
149
- when 'input_text'
150
- classes = field['class'].to_s.split(' ')
151
-
152
- if classes.include?('date') # Let's assume this is a date input.
153
- if attribute.include?('end') || attribute.include?('expire') # Make sure end dates are after start dates
154
- Faker::Date.forward(365).strftime('%Y-%m-%d')
155
- else
156
- Faker::Date.backward(365).strftime('%Y-%m-%d')
157
- end
158
- elsif classes.include?('datetime')
159
- if attribute.include?('end') || attribute.include?('expire')
160
- Faker::Date.forward(365).strftime('%Y-%m-%d %H:%m')
161
- else
162
- Faker::Date.backward(365).strftime('%Y-%m-%d %H:%m')
163
- end
164
- elsif classes.include?('numeric')
165
- value_for_input_numeric_field(field, "input.numeric[name$='[#{attribute}]']")
166
- elsif classes.include?('email') || attribute.include?('email')
167
- Faker::Internet.email
168
- elsif classes.include?('price') # effective_form_inputs price
169
- 4.times.map { DIGITS.sample }.join('') + '.00'
170
- elsif classes.include?('numeric') || attribute.include?('number')
171
- min = (Float(field['min']) rescue 1)
172
- max = (Float(field['max']) rescue 1000)
173
- number = Random.new.rand(min..max)
174
- number.kind_of?(Float) ? number.round(2) : number
175
- elsif attribute.include?('first_name')
176
- Faker::Name.first_name
177
- elsif attribute.include?('last_name')
178
- Faker::Name.last_name
179
- elsif attribute.include?('website')
180
- Faker::Internet.url
181
- elsif attribute.include?('city')
182
- Faker::Address.city
183
- elsif attribute.include?('address2')
184
- Faker::Address.secondary_address
185
- elsif attribute.include?('address')
186
- Faker::Address.street_address
187
- elsif attribute.include?('name')
188
- Faker::Name.name
189
- elsif attribute.include?('postal_code')
190
- if @filled_country_fields == 'US'
191
- DIGITS.sample + DIGITS.sample + DIGITS.sample + DIGITS.sample + DIGITS.sample
192
- else
193
- LETTERS.sample + DIGITS.sample + LETTERS.sample + ' ' + DIGITS.sample + LETTERS.sample + DIGITS.sample
194
- end
195
- elsif attribute.include?('postal') # Make a Canadian postal code
196
- LETTERS.sample + DIGITS.sample + LETTERS.sample + ' ' + DIGITS.sample + LETTERS.sample + DIGITS.sample
197
- elsif attribute.include?('zip') && attribute.include?('code') # Make a US zip code
198
- DIGITS.sample + DIGITS.sample + DIGITS.sample + DIGITS.sample + DIGITS.sample
199
- elsif attribute.include?('social_insurance_number') || attributes.include?('sin_number')
200
- "#{DIGITS.sample(3).join} #{DIGITS.sample(3).join} #{DIGITS.sample(3).join}"
201
- elsif attribute.include?('slug')
202
- Faker::Lorem.words(3).join(' ').parameterize
203
- else
204
- Faker::Lorem.words(3).join(' ').capitalize
205
- end
206
-
207
- when 'input_checkbox'
208
- value_for_input_checkbox_field(field, fill_value)
209
-
210
- when 'input_color'
211
- Faker::Color.hex_color
212
-
213
- when 'input_email'
214
- Faker::Internet.email
215
-
216
- when 'input_file'
217
- if field['class'].to_s.include?('asset-box-uploader-fileinput')
218
- "#{File.dirname(__FILE__).sub('test/support', 'test/fixtures')}/documents._test"
219
- else
220
- "#{File.dirname(__FILE__).sub('test/support', 'test/fixtures')}/logo.png"
221
- end
222
-
223
- when 'input_number'
224
- value_for_input_numeric_field(field, "input[type='number'][name$='[#{attribute}]']")
225
-
226
- when 'input_password'
227
- # Use the same password throughout a single test. Allows passwords and password_confirmations to match.
228
- @filled_password_fields ||= Faker::Internet.password
229
-
230
- when 'input_radio'
231
- value_for_input_radio_field(field, fill_value)
232
-
233
- when 'select', 'select_select-one'
234
- return fill_value if fill_value == :unselect
235
-
236
- if fill_value.present? # accept a value or text
237
- field.all('option:enabled').each do |option|
238
- if (option.text == fill_value || option.value.to_s == fill_value)
239
- @filled_country_fields = option.value if attribute == 'country_code'
240
- return option.text
241
- end
242
- end
243
- end
244
-
245
- option = field.all('option:enabled').select { |option| option.value.present? }.sample
246
-
247
- @filled_country_fields = option.try(:value) if attribute == 'country_code' # So Postal Code can be set to a valid one
248
-
249
- option.try(:text) || ''
250
-
251
- when 'input_tel'
252
- d = 10.times.map { DIGITS.sample }
253
- d[0] + d[1] + d[2] + '-' + d[3] + d[4] + d[5] + '-' + d[6] + d[7] + d[8] + d[9]
254
-
255
- when 'input_url'
256
- Faker::Internet.url
257
-
258
- when 'textarea', 'textarea_textarea'
259
- Faker::Lorem.paragraph
260
-
261
- when 'input_submit', 'input_search', 'input_button'
262
- nil
153
+ def fill_input_radio(field, value)
154
+ return if value.nil?
263
155
 
264
- else
265
- raise "fill_value unsupported field type: #{field_name}"
156
+ begin
157
+ field.set(value)
158
+ rescue => e
159
+ label = first(:label, for: field['id'])
160
+ label.click if label
266
161
  end
267
162
  end
268
163
 
269
- def value_for_input_checkbox_field(field, fill_value)
270
- if !fill_value.nil?
271
- if truthy?(fill_value)
272
- true
273
- elsif falsey?(fill_value)
274
- false
275
- else
276
- fill_values = Array(fill_value) # Allow an array of fill values to be passed
277
- (fill_values.include?(field['value']) || fill_values.include?(field.find(:xpath, '..').text))
278
- end
279
- elsif field['required'].nil? == false
280
- true
281
- elsif field['value'] == 'true'
282
- true
283
- elsif field['value'] == 'false'
284
- false
164
+ def fill_input_text_area(field, value)
165
+ if ckeditor_text_area?(field)
166
+ value = "<p>#{value.gsub("'", '')}</p>"
167
+ try_script "CKEDITOR.instances['#{field['id']}'].setData('#{value}')"
285
168
  else
286
- [true, false].sample
169
+ field.set(value)
287
170
  end
288
171
  end
289
172
 
290
- # The first time we run into a radio button, we definitely want to set it to TRUE so it's definitely selected
291
- # Subsequent ones, we can randomly true/false
292
- # Then if we run into something that has a fill_value, we definitely want to set that one to TRUE, and false the rest
293
- def value_for_input_radio_field(field, fill_value)
294
- @filled_radio_fields ||= {}
295
-
296
- previous = @filled_radio_fields[field['name']]
297
-
298
- retval =
299
- if previous == true # We've selected one of the options before
300
- [true, false].sample
301
- elsif previous.kind_of?(String) # We selected a previous option with a specific fill_value
302
- false
303
- else # We've never seen this radio field before
304
- true
305
- end
306
-
307
- if fill_value.present? && (fill_value == field['value'] || fill_value == field.find(:xpath, '..').text)
308
- @filled_radio_fields[field['name']] = fill_value
309
- true
310
- else
311
- @filled_radio_fields[field['name']] ||= true
312
- retval
173
+ def fill_input_select(field, value)
174
+ if EffectiveTestBot.tour_mode_extreme? && field['class'].to_s.include?('select2') # select2
175
+ try_script "$('select##{field['id']}').select2('open')"
176
+ save_test_bot_screenshot
313
177
  end
314
- end
315
-
316
- def value_for_input_numeric_field(field, selector)
317
- min = (Float(field['min']) rescue 0)
318
- max = (Float(field['max']) rescue 1000)
319
- number = Random.new.rand(min..max)
320
- number = (number.kind_of?(Float) ? number.round(2) : number)
321
178
 
322
- if (field['step'] == '1' || field['class'].to_s.split(' ').include?('integer'))
323
- number = number.to_i
179
+ if field.all('option:enabled').length > 0 && value != :unselect
180
+ Array(value).each do |value|
181
+ field.select(value, match: :first, disabled: false)
182
+ end
324
183
  end
325
184
 
326
- return number if field['max'].blank?
327
-
328
- shared_max_fields = all(selector)
329
- return number if shared_max_fields.length <= 1
330
-
331
- # So there's definitely 2+ fields that share the same max, named the same
332
- # We want the total value of all these fields to add upto the max single max value
333
- @filled_numeric_fields ||= {}
334
- @filled_numeric_fields[selector] ||= max
335
-
336
- available = @filled_numeric_fields[selector]
337
-
338
- amount = if max.kind_of?(Float)
339
- (((max * 1000.0) / shared_max_fields.length.to_f).ceil() / 1000.0).round(2)
340
- else
341
- (max / shared_max_fields.length.to_f).ceil
185
+ if field['class'].to_s.include?('select2')
186
+ try_script "$('select##{field['id']}').select2('close')"
342
187
  end
343
- amount = [[amount, min].max, available].min
344
-
345
- @filled_numeric_fields[selector] = (available - amount)
346
- amount
347
188
  end
348
189
 
349
- def fill_ckeditor_text_area(field, value)
350
- value = "<p>#{value.gsub("'", '')}</p>"
351
- try_script "CKEDITOR.instances['#{field['id']}'].setData('#{value}')"
190
+ def fill_input_file(field, value)
191
+ if field['class'].to_s.include?('asset-box-uploader-fileinput')
192
+ upload_effective_asset(field, value)
193
+ else
194
+ field.set(value)
195
+ end
352
196
  end
353
197
 
354
198
  # The field here is going to be the %input{:type => file}. Files can be one or more pathnames
@@ -403,28 +247,8 @@ module EffectiveTestBotFormFiller
403
247
  try_script "$('select##{field['id']}').val('').trigger('change.select2')"
404
248
  end
405
249
 
406
- def close_effective_date_time_picker(field)
407
- try_script "$('input##{field['id']}').data('DateTimePicker').hide()"
408
- end
409
-
410
250
  private
411
251
 
412
- def fill_value_for_field(fills, attributes, value, field_name)
413
- return if fills.blank? || (attributes.blank? && value.blank?)
414
-
415
- key = nil
416
- attributes.reverse_each do |name| # match last_name, then something_attributes.last_name, then user.something_attributes.last_name
417
- key = (key.present? ? "#{name}.#{key}" : name) # builds up the string as we go along
418
-
419
- return :unselect if field_name == 'select' && fills.key?(key) && [nil, ''].include?(fills[key])
420
- return fills[key].to_s if fills.key?(key)
421
- end
422
-
423
- return fills[value] if fills.key?(value)
424
-
425
- nil
426
- end
427
-
428
252
  # Takes a capybara element
429
253
  def excluding_fields_with_parent(element, &block)
430
254
  @test_bot_excluded_fields_xpath = element.try(:path)
@@ -437,6 +261,10 @@ module EffectiveTestBotFormFiller
437
261
  (field['class'].to_s.include?('ckeditor') || all("span[id='cke_#{field['id']}']").present?)
438
262
  end
439
263
 
264
+ def custom_control_input?(field) # Bootstrap 4 radios and checks
265
+ field['class'].to_s.include?('custom-control-input')
266
+ end
267
+
440
268
  def skip_form_field?(field)
441
269
  field.reload # Handle a field changing visibility/disabled state from previous form field manipulations
442
270
 
@@ -446,25 +274,20 @@ module EffectiveTestBotFormFiller
446
274
  field_id.start_with?('filters_scope_') ||
447
275
  field_id.start_with?('filters_') && field['name'].blank? ||
448
276
  field.disabled? ||
449
- (!field.visible? && !ckeditor_text_area?(field)) ||
277
+ (!field.visible? && !ckeditor_text_area?(field) && !custom_control_input?(field)) ||
450
278
  ['true', true, 1].include?(field['data-test-bot-skip']) ||
451
279
  (@test_bot_excluded_fields_xpath.present? && field.path.include?(@test_bot_excluded_fields_xpath))
452
280
  end
453
281
 
454
- def truthy?(value)
455
- if defined?(::ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES) # Rails 5
456
- ::ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(value)
457
- else
458
- ActiveRecord::Type::Boolean.new.cast(value)
459
- end
460
- end
282
+ def field_key(field)
283
+ field_name = [field.tag_name, field['type']].compact.join('_')
461
284
 
462
- def falsey?(value)
463
- if defined?(::ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES) # Rails 5
464
- ::ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES.include?(value)
465
- else
466
- ::ActiveRecord::Type::Boolean.new.cast(value) == false
467
- end
285
+ [
286
+ field['name'].presence,
287
+ ("##{field['id']}" if field['id'].present?),
288
+ field_name,
289
+ (".#{field['class'].split(' ').join('.')}" if field['class'].present?)
290
+ ].compact.join(' ')
468
291
  end
469
292
 
470
293
  end
@@ -9,10 +9,11 @@ module EffectiveTestBotFormHelper
9
9
  form_fills = HashWithIndifferentAccess.new((EffectiveTestBot.form_fills || {}).merge(fills || {}))
10
10
 
11
11
  if bootstrap_tabs.length > 1
12
- fill_bootstrap_tabs_form(form_fills, bootstrap_tabs)
12
+ fill_bootstrap_tabs_form(form_fills)
13
13
  else
14
14
  fill_form_fields(form_fills)
15
15
  end
16
+
16
17
  true
17
18
  end
18
19
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_test_bot
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Code and Effect
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-05-17 00:00:00.000000000 Z
11
+ date: 2019-05-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -155,6 +155,7 @@ files:
155
155
  - test/fixtures/documents._test
156
156
  - test/fixtures/logo.png
157
157
  - test/support/effective_test_bot_assertions.rb
158
+ - test/support/effective_test_bot_form_faker.rb
158
159
  - test/support/effective_test_bot_form_filler.rb
159
160
  - test/support/effective_test_bot_form_helper.rb
160
161
  - test/support/effective_test_bot_login_helper.rb