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 +4 -4
- data/README.md +1 -1
- data/lib/effective_test_bot.rb +1 -1
- data/lib/effective_test_bot/dsl.rb +2 -1
- data/lib/effective_test_bot/version.rb +1 -1
- data/test/support/effective_test_bot_form_faker.rb +271 -0
- data/test/support/effective_test_bot_form_filler.rb +118 -295
- data/test/support/effective_test_bot_form_helper.rb +2 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4ca4801862674dc8fc51f92efc4240d754b3308221ff9dd3860f99ac61a2a66c
|
4
|
+
data.tar.gz: 1cf30b273097f94620dd075945bb468026615692f4a113df883a36d5a72caf07
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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-
|
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
|
|
data/lib/effective_test_bot.rb
CHANGED
@@ -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
|
7
|
+
include EffectiveTestBotFormFaker
|
8
8
|
include EffectiveTestBotFormFiller
|
9
|
+
include EffectiveTestBotFormHelper
|
9
10
|
include EffectiveTestBotLoginHelper
|
10
11
|
include EffectiveTestBotMinitestHelper
|
11
12
|
include EffectiveTestBotScreenshotsHelper
|
@@ -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 = {}
|
10
|
-
tabs =
|
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
|
-
|
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
|
-
|
61
|
-
|
62
|
-
|
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
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
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
|
95
|
-
|
80
|
+
if debug
|
81
|
+
puts "CONSIDERING: #{field_key(field)}"
|
82
|
+
puts " -> SKIPPED" if skip_form_field?(field)
|
96
83
|
end
|
97
84
|
|
98
|
-
if
|
99
|
-
|
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
|
-
|
102
|
-
|
103
|
-
|
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
|
115
|
+
raise "unsupported field type #{field_name}"
|
106
116
|
end
|
107
|
-
|
108
|
-
|
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
|
-
|
131
|
-
|
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
|
-
|
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
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
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
|
-
|
149
|
-
|
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
|
-
|
265
|
-
|
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
|
270
|
-
if
|
271
|
-
|
272
|
-
|
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
|
-
|
169
|
+
field.set(value)
|
287
170
|
end
|
288
171
|
end
|
289
172
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
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 (
|
323
|
-
|
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
|
-
|
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
|
350
|
-
|
351
|
-
|
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
|
455
|
-
|
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
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
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
|
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.
|
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-
|
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
|