effective_test_bot 0.4.3 → 0.4.4

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
  SHA1:
3
- metadata.gz: 836a02fd7342588742c06d55d4f64d06382b5db5
4
- data.tar.gz: a136f7e711237d2038d86b64273f9d2d54248183
3
+ metadata.gz: 71be7587c07b84fa1b7e452dc469eeb560a1ce47
4
+ data.tar.gz: 063be9eb1dfc317f6dc01d0a8c199d411953c5f4
5
5
  SHA512:
6
- metadata.gz: a81518d383fabca6561057e0262c33487f8c377b90f7b0569ec18da26cae2910546a983af8afe2614dfa217a7206c95f2e952539d77a0345086a172226483364
7
- data.tar.gz: fe6980136e37b6881cf11ea673340d69af4d3c766c6932e0adb40b8c7db87a978bf27488371d768cfff4b5dada780972a5447241bf3f2c2d320dec4a95166e8e
6
+ metadata.gz: 1318c1e88b31a9d2bba44d2262862206c765c6864e8b9bf5d4b6456f90ae7d5fa552960974d4cdbbf83994c42aa1517cb6ffb31d1e6d86ad8960e0f4e38988d4
7
+ data.tar.gz: 80f617274e4f72d8885926c84fcdf8f19e51d576dcceb3b8914ee9e26a2c54dd32365f8641e04ab7c344f7ba218e25c4375f9a42fdb78f08002c7913a8ee68ae
data/README.md CHANGED
@@ -97,6 +97,9 @@ class PhysiciansTest < ActionDispatch::IntegrationTest
97
97
  end
98
98
 
99
99
 
100
+ , input_html: {'data-test-bot-skip': true}
101
+
102
+
100
103
 
101
104
 
102
105
  ## Fixtures
@@ -4,6 +4,9 @@ require "effective_test_bot/version"
4
4
  module EffectiveTestBot
5
5
  mattr_accessor :except
6
6
  mattr_accessor :only
7
+ mattr_accessor :screenshots
8
+ mattr_accessor :autosave_animated_gif_on_failure
9
+ mattr_accessor :tour_mode
7
10
 
8
11
  def self.setup
9
12
  yield self
@@ -38,6 +41,31 @@ module EffectiveTestBot
38
41
  false # Don't skip this test
39
42
  end
40
43
 
44
+ # If you call rake test:bot TOUR=false, then disable screenshots too
45
+ def self.screenshots?
46
+ screenshots == true
47
+ end
48
+
49
+ def self.autosave_animated_gif_on_failure?
50
+ screenshots && autosave_animated_gif_on_failure
51
+ end
52
+
53
+ def self.tour_mode?
54
+ if ENV['TOUR'].present?
55
+ ['true', 'verbose', 'debug'].include?(ENV['TOUR'].to_s.downcase)
56
+ else
57
+ screenshots && (tour_mode != false)
58
+ end
59
+ end
60
+
61
+ def self.tour_mode_verbose?
62
+ if ENV['TOUR'].present?
63
+ ['verbose', 'debug'].include?(ENV['TOUR'].to_s.downcase)
64
+ else
65
+ screenshots && ['verbose', 'debug'].include?(tour_mode.to_s)
66
+ end
67
+ end
68
+
41
69
  private
42
70
 
43
71
  def self.onlies
@@ -14,6 +14,14 @@ module EffectiveTestBot
14
14
 
15
15
  initializer 'effective_test_bot.test_suite' do |app|
16
16
  Rails.application.config.to_prepare do
17
+ # test/support/
18
+ ActionDispatch::IntegrationTest.include EffectiveTestBotAssertions
19
+ ActionDispatch::IntegrationTest.include EffectiveTestBotFormHelper
20
+ ActionDispatch::IntegrationTest.include EffectiveTestBotFormFiller
21
+ ActionDispatch::IntegrationTest.include EffectiveTestBotLoginHelper
22
+ ActionDispatch::IntegrationTest.include EffectiveTestBotScreenshotsHelper
23
+ ActionDispatch::IntegrationTest.include EffectiveTestBotTestHelper
24
+
17
25
  # test/test_botable/
18
26
  ActionDispatch::IntegrationTest.include BaseTest
19
27
  ActionDispatch::IntegrationTest.include CrudTest
@@ -31,12 +39,6 @@ module EffectiveTestBot
31
39
  ActionDispatch::IntegrationTest.include TestBotable::PageDsl
32
40
  ActionDispatch::IntegrationTest.include TestBotable::RedirectDsl
33
41
  ActionDispatch::IntegrationTest.include TestBotable::WizardDsl
34
-
35
- # test/support/
36
- ActionDispatch::IntegrationTest.include EffectiveTestBotAssertions
37
- ActionDispatch::IntegrationTest.include EffectiveTestBotFormHelper
38
- ActionDispatch::IntegrationTest.include EffectiveTestBotLoginHelper
39
- ActionDispatch::IntegrationTest.include EffectiveTestBotTestHelper
40
42
  end
41
43
  end
42
44
 
@@ -53,7 +55,7 @@ module EffectiveTestBot
53
55
  assign_test_bot_unpermitted_params_header(exception)
54
56
  end
55
57
 
56
- rescue_from Exception do |exception| # Not sure if I should rescue Exception or StandardError
58
+ rescue_from StandardError do |exception|
57
59
  assign_test_bot_exceptions_header(exception)
58
60
  render status: 500, text: "<html><body><h1>Uncaught Exception</h1><p>#{exception.message}</p><p>#{exception.backtrace.first(20).join('<br>')}</p></body></html>"
59
61
  end
@@ -1,3 +1,3 @@
1
1
  module EffectiveTestBot
2
- VERSION = '0.4.3'.freeze
2
+ VERSION = '0.4.4'.freeze
3
3
  end
@@ -41,7 +41,8 @@ module EffectiveTestBot
41
41
 
42
42
  def thank_you
43
43
  puts "Thanks for using EffectiveTestBot"
44
- puts "Run tests by typing 'rake test:bot'"
44
+ puts "First make sure your test environment is correctly configured by running 'rake test:bot:environment'"
45
+ puts "Run tests with 'rake test:bot'"
45
46
  end
46
47
  end
47
48
  end
@@ -2,5 +2,35 @@
2
2
 
3
3
  if Rails.env.test?
4
4
  EffectiveTestBot.setup do |config|
5
+
6
+ # Exclude the following tests or assertions from being run.
7
+ # config.except = [
8
+ # 'widgets'
9
+ # 'posts#create_invalid'
10
+ # 'posts#index page_title'
11
+ # 'no_unpermitted_params'
12
+ # ]
13
+
14
+ # Run only the following tests. Doesn't work with individual assertions>
15
+ # config.only = [
16
+ # 'posts', 'events#index'
17
+ # ]
18
+
19
+ # Should capybara generate a series of *.png screenshots as it goes through the test?
20
+ # Disabling screenshots will also disable animated_gifs and touring
21
+ config.screenshots = true
22
+
23
+ # Save on failure to /tmp/ directory
24
+ config.autosave_animated_gif_on_failure = true
25
+
26
+ # Take the tour!
27
+ # Generate an animated gif for each test
28
+ # Saved to an appropriate /test/tour/* directory
29
+ # You can override this default by setting an ENV or calling
30
+ # `rake test:bot TOUR=true` or `rake test:bot TEST=posts TOUR=verbose`
31
+ #
32
+ # Valid values are true / false / :verbose
33
+ config.tour_mode = false
34
+
5
35
  end
6
36
  end
@@ -48,6 +48,11 @@ Capybara::Webkit.configure { |config| config.allow_unknown_urls }
48
48
 
49
49
  Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new
50
50
 
51
+ # These two lines are needed as of minitest-reporters 1.1.2
52
+ Rails.backtrace_cleaner.remove_silencers!
53
+ Rails.backtrace_cleaner.add_silencer { |line| line =~ /minitest/ }
54
+
55
+
51
56
  ### So this is EffectiveTestBot 'code' here
52
57
  ### That gets run just once before the whole test suite loads
53
58
 
@@ -4,25 +4,34 @@ require 'rails/test_unit/sub_test_task'
4
4
  # rake test:bot
5
5
  # rake test:bot TEST=documents#new
6
6
  # rake test:bot TEST=documents#new,documents#show
7
- # rake test:bot TEST=documents#new path,documents#show,documents#update_valid no_unpermitted_params
7
+ # rake test:bot TOUR=true
8
+ # rake test:bot TOUR=verbose
8
9
 
9
10
  # rake test:bot:environment
11
+ # rake test:bot:purge
10
12
 
11
13
  namespace :test do
12
- desc 'Runs Effective Test Bot'
14
+ desc 'Runs the effective_test_bot'
13
15
  task :bot do
14
16
  if ENV['TEST'].present?
15
17
  ENV['TEST_BOT_TEST'] = ENV['TEST']
16
18
  ENV['TEST'] = nil
17
19
  end
18
20
 
19
- Rake::Task["test:effective_test_bot"].invoke
21
+ Rake::Task['test:effective_test_bot'].invoke
20
22
  end
21
23
 
22
24
  namespace :bot do
23
- desc 'Runs Effective Test Bot environment test'
25
+ desc 'Runs effective_test_bot environment test'
24
26
  task :environment do
25
- Rake::Task["test:effective_test_bot_environment"].invoke
27
+ Rake::Task['test:effective_test_bot_environment'].invoke
28
+ end
29
+
30
+ desc 'Deletes all effective_test_bot temporary, failure and tour screenshots'
31
+ task :purge do
32
+ FileUtils.rm_rf(Rails.root + 'test/tour')
33
+ FileUtils.rm_rf(Rails.root + 'tmp/test_bot')
34
+ puts "Successfully purged all effective_test_bot screenshots"
26
35
  end
27
36
  end
28
37
 
@@ -45,9 +45,10 @@ module TestBotable
45
45
  options[:resource_name].pluralize
46
46
  ].compact.join('/') + '#' + test.to_s
47
47
 
48
- method_name = test_bot_method_name('crud_test', label || options_for_method[:current_test])
49
48
  next if EffectiveTestBot.skip?(label || options_for_method[:current_test])
50
49
 
50
+ method_name = test_bot_method_name('crud_test', label || options_for_method[:current_test])
51
+
51
52
  define_method(method_name) { crud_action_test(test, resource, user, options_for_method) }
52
53
  end
53
54
  end
@@ -21,9 +21,9 @@ module TestBotable
21
21
 
22
22
  [:sign_up, :sign_in_valid, :sign_in_invalid].each do |test|
23
23
  options[:current_test] = label || test
24
+ next if EffectiveTestBot.skip?(options[:current_test])
24
25
 
25
26
  method_name = test_bot_method_name('devise_test', options[:current_test])
26
- next if EffectiveTestBot.skip?(options[:current_test])
27
27
 
28
28
  define_method(method_name) { devise_action_test(test, options) }
29
29
  end
@@ -19,9 +19,9 @@ module TestBotable
19
19
 
20
20
  def member_test(controller, action, user, obj_to_param = nil, options = {})
21
21
  options[:current_test] = options.delete(:label) || "#{controller}##{action}"
22
+ return if EffectiveTestBot.skip?(options[:current_test])
22
23
 
23
24
  method_name = test_bot_method_name('member_test', options[:current_test])
24
- return if EffectiveTestBot.skip?(options[:current_test])
25
25
 
26
26
  define_method(method_name) { member_action_test(controller, action, user, obj_to_param, options) }
27
27
  end
@@ -17,9 +17,9 @@ module TestBotable
17
17
 
18
18
  def page_test(path, user, options = {})
19
19
  options[:current_test] = options.delete(:label) || path.to_s
20
+ return if EffectiveTestBot.skip?(options[:current_test])
20
21
 
21
22
  method_name = test_bot_method_name('page_test', options[:current_test])
22
- return if EffectiveTestBot.skip?(options[:current_test])
23
23
 
24
24
  define_method(method_name) { page_action_test(path, user, options) }
25
25
  end
@@ -16,9 +16,9 @@ module TestBotable
16
16
 
17
17
  def redirect_test(from_path, to_path, user, options = {})
18
18
  options[:current_test] = options.delete(:label) || "#{from_path} to #{to_path}"
19
+ return if EffectiveTestBot.skip?(options[:current_test])
19
20
 
20
21
  method_name = test_bot_method_name('redirect_test', options[:current_test])
21
- return if EffectiveTestBot.skip?(options[:current_test])
22
22
 
23
23
  define_method(method_name) { redirect_action_test(from_path, to_path, user, options) }
24
24
  end
@@ -19,9 +19,9 @@ module TestBotable
19
19
 
20
20
  def wizard_test(from_path, to_path, user, options = {})
21
21
  options[:current_test] = options.delete(:label) || "#{from_path} to #{to_path}"
22
+ return if EffectiveTestBot.skip?(options[:current_test])
22
23
 
23
24
  method_name = test_bot_method_name('wizard_test', options[:current_test])
24
- return if EffectiveTestBot.skip?(options[:current_test])
25
25
 
26
26
  define_method(method_name) { wizard_action_test(from_path, to_path, user, options) }
27
27
  end
@@ -51,7 +51,14 @@ module EffectiveTestBotAssertions
51
51
  end
52
52
 
53
53
  def assert_no_exceptions(message = nil)
54
- assert exceptions.blank?, message || "(no_exceptions) Unexpected exception:\n#{exceptions.join("\n")}\n========== End Exception ==========\n"
54
+ assert exceptions.blank?, message || "(no_exceptions) Unexpected exception:\n#{exceptions.join("\n")}\n========== End of rails server exception ==========\n"
55
+ end
56
+
57
+ # This must be run after submit_form()
58
+ # It ensures there are no HTML5 validation errors that would prevent the form from being submit
59
+ def assert_no_html5_form_validation_errors(message = nil)
60
+ errors = all(':invalid', visible: false).map { |field| field['name'] }
61
+ assert errors.blank?, message || "(no_html5_form_validation_errors) Unable to submit form, unexpected HTML5 validation error present on the following fields:\n#{errors.join("\n")}"
55
62
  end
56
63
 
57
64
  # assert_flash
@@ -84,17 +91,20 @@ module EffectiveTestBotAssertions
84
91
  # assert_no_assigns_errors :post
85
92
  def assert_no_assigns_errors(key = nil, message = nil)
86
93
  if key.present?
87
- assert_equal [], ((assigns[key.to_s] || {})['errors'] || []), message || "(no_assigns_errors) Expected @#{key}[:errors] to be blank. Instead, it was: #{assigns}"
94
+ errors = (assigns[key.to_s] || {})['errors']
95
+ assert errors.blank?, message || "(no_assigns_errors) Unexpected @#{key} rails validation errors:\n#{errors}"
88
96
  else
89
97
  assigns.each do |key, value|
90
- assert_equal [], (value['errors'] || []), message || "(no_assigns_errors) Expected @#{key}[:errors] to be blank"
98
+ errors = value['errors']
99
+ assert errors.blank?, message || "(no_assigns_errors) Unexpected @#{key} rails validation errors:\n#{errors}"
91
100
  end
92
101
  end
93
102
  end
94
103
 
95
104
  # assert_assigns_errors :post
96
105
  def assert_assigns_errors(key, message = nil)
97
- refute_equal [], ((assigns[key.to_s] || {})['errors'] || []), message || "(assigns_errors) Expected @#{key}[:errors] to be present"
106
+ errors = (assigns[key.to_s] || {})['errors']
107
+ assert errors.present?, message || "(assigns_errors) Expected @#{key}.errors to be present"
98
108
  end
99
109
 
100
110
  end
@@ -0,0 +1,239 @@
1
+ require 'timeout'
2
+
3
+ module EffectiveTestBotFormFiller
4
+ DIGITS = ('1'..'9').to_a
5
+ LETTERS = ('A'..'Z').to_a
6
+
7
+ # Fill a boostrap tabs based form
8
+ def fill_bootstrap_tabs_form(fills = {}, boostrap_tab_elements = nil)
9
+ fills = HashWithIndifferentAccess.new(fills) unless fills.kind_of?(HashWithIndifferentAccess)
10
+
11
+ tabs = boostrap_tab_elements || all("a[data-toggle='tab']")
12
+
13
+ # If there's only 1 tab, just fill it out
14
+ (fill_form_fields(fills) and return) unless tabs.length > 1
15
+
16
+ # If there's more than one tab:
17
+ # We first fill in all fields that are outside of the tab-content
18
+ # Then we start at the first, and go left-to-right through all the tabs
19
+ # clicking each one and filling any form fields found within
20
+
21
+ active_tab = find("li.active > a[data-toggle='tab']")
22
+ tab_content = find("div#{active_tab['href']}").find(:xpath, '..')
23
+
24
+ excluding_fields_with_parent(tab_content) { fill_form_fields(fills) }
25
+
26
+ # Refresh the tabs, as they may have changed
27
+ tabs = all("a[data-toggle='tab']")
28
+
29
+ # Click through each tab and fill the form inside it.
30
+ tabs.each do |tab|
31
+ # changing the call to to fill_bootstrap_tabs_form for recursiveness should work
32
+ # but it would be an extra all() lookup, and probably not worth it.
33
+ tab.click()
34
+ synchronize!
35
+ save_test_bot_screenshot
36
+
37
+ within("div#{tab['href']}") { fill_form_fields(fills) }
38
+ end
39
+
40
+ end
41
+
42
+ # Only fills in visible fields
43
+ # fill_form(:email => 'somethign@soneone.com', :password => 'blahblah', 'user.last_name' => 'hlwerewr')
44
+ def fill_form_fields(fills = {})
45
+ fills = HashWithIndifferentAccess.new(fills) unless fills.kind_of?(HashWithIndifferentAccess)
46
+
47
+ # Support for the cocoon gem
48
+ all('a.add_fields[data-association-insertion-template]').each do |field|
49
+ next if skip_form_field?(field)
50
+ 2.times { field.click(); save_test_bot_screenshot }
51
+ end
52
+
53
+ all('input,select,textarea').each do |field|
54
+ next if skip_form_field?(field)
55
+
56
+ case [field.tag_name, field['type']].compact.join('_')
57
+ when 'input_text', 'input_email', 'input_password', 'input_tel', 'input_number', 'input_checkbox', 'input_radio', 'textarea'
58
+ field.set(value_for_field(field, fills))
59
+ when 'select'
60
+ if field['class'].to_s.include?('select2') # effective_select
61
+ page.execute_script("try { $('select##{field['id']}').select2('open'); } catch(e) {};")
62
+ save_test_bot_screenshot
63
+ end
64
+
65
+ field.select(value_for_field(field, fills), match: :first)
66
+ when 'input_file'
67
+ if field['class'].to_s.include?('asset-box-uploader-fileinput')
68
+ upload_effective_asset(field, value_for_field(field, fills))
69
+ else
70
+ field.set(value_for_field(field, fills))
71
+ end
72
+ when 'input_submit', 'input_search'
73
+ # Do nothing
74
+ else
75
+ raise "unsupported field type #{[field.tag_name, field['type']].compact.join('_')}"
76
+ end
77
+
78
+ save_test_bot_screenshot
79
+ end
80
+ end
81
+
82
+ # Generates an appropriately pseudo-random value for the given field
83
+ # Pass in a Hash of fills to define pre-selected values
84
+ #
85
+ # Operates on just string keys, no symbols here
86
+
87
+ def value_for_field(field, fills = nil)
88
+ field_name = [field.tag_name, field['type']].compact.join('_')
89
+ attributes = field['name'].to_s.gsub(']', '').split('[') # user[something_attributes][last_name] => ['user', 'something_attributes', 'last_name']
90
+
91
+ fill_value = fill_value_for_field(fills, attributes)
92
+
93
+ # If there is a predefined fill value for this field, return it here
94
+ # except for select fields which are treated differently, so we can match fill values on both the html text or value
95
+ # this edge case is implemented below
96
+ if fill_value.present? && !['select'].include?(field_name)
97
+ return fill_value
98
+ end
99
+
100
+ case field_name
101
+ when 'input_text'
102
+ classes = field['class'].to_s.split(' ')
103
+
104
+ if classes.include?('date') # Let's assume this is a date input.
105
+ if attributes.last.to_s.include?('end') # Make sure end dates are after start dates
106
+ Faker::Date.forward(365).strftime('%Y-%m-%d')
107
+ else
108
+ Faker::Date.backward(365).strftime('%Y-%m-%d')
109
+ end
110
+ elsif classes.include?('datetime')
111
+ if attributes.last.to_s.include?('end')
112
+ Faker::Date.forward(365).strftime('%Y-%m-%d %H:%m')
113
+ else
114
+ Faker::Date.backward(365).strftime('%Y-%m-%d %H:%m')
115
+ end
116
+ elsif classes.include?('price') # effective_form_inputs price
117
+ 4.times.map { DIGITS.sample }.join('') + '.00'
118
+ elsif classes.include?('numeric')
119
+ min = (Float(field['min']) rescue 1)
120
+ max = (Float(field['max']) rescue 1000)
121
+ number = Random.new.rand(min..max)
122
+ number.kind_of?(Float) ? number.round(2) : number
123
+ elsif attributes.last.to_s.include?('first_name')
124
+ Faker::Name.first_name
125
+ elsif attributes.last.to_s.include?('last_name')
126
+ Faker::Name.last_name
127
+ elsif attributes.last.to_s.include?('name')
128
+ Faker::Name.name
129
+ elsif attributes.last.to_s.include?('postal') # Make a Canadian Postal Code
130
+ LETTERS.sample + DIGITS.sample + LETTERS.sample + ' ' + DIGITS.sample + LETTERS.sample + DIGITS.sample
131
+ else
132
+ Faker::Lorem.word
133
+ end
134
+
135
+ when 'select'
136
+ if fill_value.present? # accept a value or text
137
+ field.all('option:enabled').each do |option|
138
+ return option.text if (option.text == fill_value || option.value.to_s == fill_value)
139
+ end
140
+ end
141
+
142
+ field.all('option:enabled').select { |option| option.value.present? }.sample.try(:text) || '' # Don't select an empty option
143
+ when 'input_number'
144
+ min = (Float(field['min']) rescue 1)
145
+ max = (Float(field['max']) rescue 1000)
146
+ number = Random.new.rand(min..max)
147
+ number.kind_of?(Float) ? number.round(2) : number
148
+ when 'input_email'
149
+ Faker::Internet.email
150
+ when 'input_password'
151
+ # Use the same password throughout a single test. Allows passwords and password_confirmations to match.
152
+ @test_bot_current_password ||= Faker::Internet.password
153
+ when 'input_tel'
154
+ d = 10.times.map { DIGITS.sample }
155
+ d[0] + d[1] + d[2] + '-' + d[3] + d[4] + d[5] + '-' + d[6] + d[7] + d[8] + d[9]
156
+ when 'textarea'
157
+ Faker::Lorem.sentence
158
+ when 'input_checkbox'
159
+ [true, false].sample
160
+ when 'input_radio'
161
+ [true, false].sample
162
+ when 'input_file'
163
+ "#{File.dirname(__FILE__)}/effective_assets_upload_file._test"
164
+ else
165
+ raise "fill_value unsupported field type: #{field['type']}"
166
+ end
167
+ end
168
+
169
+ # The field here is going to be the %input{:type => file}. Files can be one or more pathnames
170
+ # http://stackoverflow.com/questions/5188240/using-selenium-to-imitate-dragging-a-file-onto-an-upload-element/11203629#11203629
171
+ def upload_effective_asset(field, files)
172
+ files = Array(files)
173
+ uid = field['id']
174
+
175
+ js = "fileList = Array();"
176
+
177
+ files.each_with_index do |file, i|
178
+ # Generate a fake input selector
179
+ page.execute_script("if($('#effectiveAssetsPlaceholder#{i}').length == 0) {effectiveAssetsPlaceholder#{i} = window.$('<input/>').attr({id: 'effectiveAssetsPlaceholder#{i}', type: 'file'}).appendTo('body'); }")
180
+
181
+ # Attach file to the fake input selector through Capybara
182
+ page.document.attach_file("effectiveAssetsPlaceholder#{i}", files[i])
183
+
184
+ # Build up the fake js event
185
+ js = "#{js} fileList.push(effectiveAssetsPlaceholder#{i}.get(0).files[0]);"
186
+ end
187
+
188
+ # Trigger the fake drop event
189
+ page.execute_script("#{js} e = $.Event('drop'); e.originalEvent = {dataTransfer : { files : fileList } }; $('#s3_#{uid}').trigger(e);")
190
+
191
+ # Remove the file inputs we created
192
+ page.execute_script("$('input[id^=effectiveAssetsPlaceholder]').remove();")
193
+
194
+ # Wait till the Uploader bar goes away
195
+ begin
196
+ Timeout.timeout(files.length * 5) do
197
+ within("#asset-box-input-#{uid}") do
198
+ within('.uploads') do
199
+ while (first('.upload').present? rescue false) do
200
+ save_test_bot_screenshot
201
+ sleep(0.5)
202
+ end
203
+ end
204
+ end
205
+ end
206
+ rescue Timeout::Error
207
+ puts "file upload timed out after #{files.length * 5}s"
208
+ end
209
+ end
210
+
211
+ private
212
+
213
+ def fill_value_for_field(fills, attributes)
214
+ return if fills.blank? || attributes.blank?
215
+
216
+ key = nil
217
+ attributes.reverse_each do |name| # match last_name, then something_attributes.last_name, then user.something_attributes.last_name
218
+ key = (key.present? ? "#{name}.#{key}" : name) # builds up the string as we go along
219
+ return fills[key].to_s if fills.key?(key)
220
+ end
221
+
222
+ nil
223
+ end
224
+
225
+ # Takes a capybara element
226
+ def excluding_fields_with_parent(element, &block)
227
+ @test_bot_excluded_fields_xpath = element.path
228
+ yield
229
+ @test_bot_excluded_fields_xpath = nil
230
+ end
231
+
232
+ def skip_form_field?(field)
233
+ field.visible? == false ||
234
+ field.disabled? ||
235
+ ['true', true, 1].include?(field['data-test-bot-skip']) ||
236
+ (@test_bot_excluded_fields_xpath.present? && field.path.include?(@test_bot_excluded_fields_xpath))
237
+ end
238
+
239
+ end