effective_test_bot 1.1.0 → 1.1.1

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