cornucopia 0.1.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +51 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +16 -0
  6. data/LICENSE.TXT +22 -0
  7. data/README.md +341 -0
  8. data/Rakefile +18 -0
  9. data/cornucopia.gemspec +39 -0
  10. data/lib/cornucopia.rb +18 -0
  11. data/lib/cornucopia/capybara/finder_diagnostics.rb +536 -0
  12. data/lib/cornucopia/capybara/finder_extensions.rb +89 -0
  13. data/lib/cornucopia/capybara/install_finder_extensions.rb +105 -0
  14. data/lib/cornucopia/capybara/install_matcher_extensions.rb +39 -0
  15. data/lib/cornucopia/capybara/matcher_extensions.rb +83 -0
  16. data/lib/cornucopia/capybara/page_diagnostics.rb +228 -0
  17. data/lib/cornucopia/cucumber_hooks.rb +38 -0
  18. data/lib/cornucopia/factory_girl/dynamic_association.rb +14 -0
  19. data/lib/cornucopia/rspec_hooks.rb +37 -0
  20. data/lib/cornucopia/site_prism/element_extensions.rb +273 -0
  21. data/lib/cornucopia/site_prism/install_element_extensions.rb +23 -0
  22. data/lib/cornucopia/site_prism/page_application.rb +126 -0
  23. data/lib/cornucopia/source_files/collapse.gif +0 -0
  24. data/lib/cornucopia/source_files/cornucopia.css +162 -0
  25. data/lib/cornucopia/source_files/expand.gif +0 -0
  26. data/lib/cornucopia/source_files/index_base.html +10 -0
  27. data/lib/cornucopia/source_files/index_contents.html +2 -0
  28. data/lib/cornucopia/source_files/more_info.js +87 -0
  29. data/lib/cornucopia/source_files/report_base.html +10 -0
  30. data/lib/cornucopia/source_files/report_contents.html +3 -0
  31. data/lib/cornucopia/spinach_hooks.rb +51 -0
  32. data/lib/cornucopia/util/configuration.rb +493 -0
  33. data/lib/cornucopia/util/configured_report.rb +520 -0
  34. data/lib/cornucopia/util/file_asset.rb +46 -0
  35. data/lib/cornucopia/util/generic_settings.rb +37 -0
  36. data/lib/cornucopia/util/log_capture.rb +97 -0
  37. data/lib/cornucopia/util/pretty_formatter.rb +580 -0
  38. data/lib/cornucopia/util/report_builder.rb +474 -0
  39. data/lib/cornucopia/util/report_formatters.rb +11 -0
  40. data/lib/cornucopia/util/report_table.rb +195 -0
  41. data/lib/cornucopia/version.rb +3 -0
  42. data/lib/tasks/cornucopia_tasks.rake +4 -0
  43. data/spec/dummy/README.rdoc +28 -0
  44. data/spec/dummy/Rakefile +6 -0
  45. data/spec/dummy/app/assets/images/.keep +0 -0
  46. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  47. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  48. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  49. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  50. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  51. data/spec/dummy/app/mailers/.keep +0 -0
  52. data/spec/dummy/app/models/.keep +0 -0
  53. data/spec/dummy/app/models/concerns/.keep +0 -0
  54. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  55. data/spec/dummy/bin/bundle +3 -0
  56. data/spec/dummy/bin/rails +4 -0
  57. data/spec/dummy/bin/rake +4 -0
  58. data/spec/dummy/config.ru +4 -0
  59. data/spec/dummy/config/application.rb +27 -0
  60. data/spec/dummy/config/boot.rb +5 -0
  61. data/spec/dummy/config/cucumber.yml +8 -0
  62. data/spec/dummy/config/database.yml +37 -0
  63. data/spec/dummy/config/environment.rb +5 -0
  64. data/spec/dummy/config/environments/development.rb +29 -0
  65. data/spec/dummy/config/environments/production.rb +80 -0
  66. data/spec/dummy/config/environments/test.rb +36 -0
  67. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  68. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  69. data/spec/dummy/config/initializers/inflections.rb +16 -0
  70. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  71. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  72. data/spec/dummy/config/initializers/session_store.rb +3 -0
  73. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  74. data/spec/dummy/config/locales/en.yml +23 -0
  75. data/spec/dummy/config/routes.rb +56 -0
  76. data/spec/dummy/db/schema.rb +16 -0
  77. data/spec/dummy/features/support/env.rb +66 -0
  78. data/spec/dummy/lib/assets/.keep +0 -0
  79. data/spec/dummy/lib/tasks/cucumber.rake +65 -0
  80. data/spec/dummy/public/404.html +58 -0
  81. data/spec/dummy/public/422.html +58 -0
  82. data/spec/dummy/public/500.html +57 -0
  83. data/spec/dummy/public/favicon.ico +0 -0
  84. data/spec/dummy/script/cucumber +10 -0
  85. data/spec/fixtures/sample_page.html +150 -0
  86. data/spec/lib/capybara/finder_diagnostics_spec.rb +517 -0
  87. data/spec/lib/capybara/finder_extensions_spec.rb +328 -0
  88. data/spec/lib/capybara/page_diagnostics_spec.rb +277 -0
  89. data/spec/lib/site_prism/element_extensions_spec.rb +290 -0
  90. data/spec/lib/site_prism/page_application_spec.rb +81 -0
  91. data/spec/lib/util/configuration_spec.rb +254 -0
  92. data/spec/lib/util/configured_report_spec.rb +1058 -0
  93. data/spec/lib/util/file_asset_spec.rb +86 -0
  94. data/spec/lib/util/generic_settings_spec.rb +48 -0
  95. data/spec/lib/util/log_capture_spec.rb +151 -0
  96. data/spec/lib/util/pretty_formatter_spec.rb +694 -0
  97. data/spec/lib/util/report_builder_spec.rb +983 -0
  98. data/spec/lib/util/report_formatters_spec.rb +13 -0
  99. data/spec/lib/util/report_table_exception_spec.rb +21 -0
  100. data/spec/lib/util/report_table_spec.rb +319 -0
  101. data/spec/pages/cornucopia_report_app.rb +10 -0
  102. data/spec/pages/google/email_page.rb +22 -0
  103. data/spec/pages/google/login_page.rb +25 -0
  104. data/spec/rails_helper.rb +43 -0
  105. data/spec/sample_report.rb +45 -0
  106. data/spec/spec_helper.rb +81 -0
  107. metadata +410 -0
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ require "bundler/gem_tasks"
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+
8
+ require 'rdoc/task'
9
+
10
+ RDoc::Task.new(:rdoc) do |rdoc|
11
+ rdoc.rdoc_dir = 'rdoc'
12
+ rdoc.title = 'Cornucopia'
13
+ rdoc.options << '--line-numbers'
14
+ rdoc.rdoc_files.include('README.rdoc')
15
+ rdoc.rdoc_files.include('lib/**/*.rb')
16
+ end
17
+
18
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ # Maintain your gem's version:
6
+ require "cornucopia/version"
7
+
8
+ # Describe your gem and declare its dependencies:
9
+ Gem::Specification.new do |spec|
10
+ spec.name = "cornucopia"
11
+ spec.version = Cornucopia::VERSION
12
+ spec.authors = ["RealNobody"]
13
+ spec.email = ["RealNobody1@cox.net"]
14
+ spec.summary = "A collection of tools to simplify testing tasks."
15
+ spec.description = "A collection of tools I created to simplify and make it easier to see what is happening."
16
+ spec.homepage = "https://github.com/RealNobody/cornucopia"
17
+ spec.license = "MIT"
18
+
19
+ # spec.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.rdoc"]
20
+ spec.files = `git ls-files`.split($/)
21
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
22
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
23
+ spec.require_paths = ["lib"]
24
+
25
+ spec.add_development_dependency "rails"
26
+ spec.add_development_dependency "mysql2"
27
+ spec.add_development_dependency "bundler"
28
+ spec.add_development_dependency "rake"
29
+ spec.add_development_dependency "rspec-rails"
30
+ spec.add_development_dependency "capybara"
31
+ spec.add_development_dependency "cucumber"
32
+ spec.add_development_dependency "cucumber-rails"
33
+ spec.add_development_dependency "faker"
34
+ spec.add_development_dependency "site_prism"
35
+ spec.add_development_dependency "selenium-webdriver"
36
+ spec.add_development_dependency "simplecov"
37
+ spec.add_development_dependency "launchy"
38
+ spec.add_development_dependency "rack"
39
+ end
data/lib/cornucopia.rb ADDED
@@ -0,0 +1,18 @@
1
+ require "cornucopia/version"
2
+ require "cornucopia/util/configuration"
3
+ require "cornucopia/util/configured_report"
4
+ require "cornucopia/util/generic_settings"
5
+ require "cornucopia/util/file_asset"
6
+ require "cornucopia/util/log_capture"
7
+ require "cornucopia/util/pretty_formatter"
8
+ require "cornucopia/util/report_table"
9
+ require "cornucopia/util/report_builder"
10
+ require "cornucopia/capybara/finder_diagnostics"
11
+ require "cornucopia/capybara/page_diagnostics"
12
+ require "cornucopia/capybara/finder_extensions"
13
+ require "cornucopia/capybara/matcher_extensions"
14
+ require "cornucopia/site_prism/element_extensions"
15
+ require "cornucopia/site_prism/page_application"
16
+
17
+ module Cornucopia
18
+ end
@@ -0,0 +1,536 @@
1
+ require ::File.expand_path("../util/configuration", File.dirname(__FILE__))
2
+ require ::File.expand_path("../util/report_builder", File.dirname(__FILE__))
3
+
4
+ module Cornucopia
5
+ module Capybara
6
+ class FinderDiagnostics
7
+ # This function calls a "finder" function with the passed in arguments on the passed in object.
8
+ # If the function succeeds, it doesn't do anything else.
9
+ # If the function fails, it tries to figure out why, and provide diagnostic
10
+ # information for further analysis.
11
+ #
12
+ # Parameters:
13
+ # test_object - the object to call the finder function on. Examples could be:
14
+ # self
15
+ # page
16
+ # test_finder(:find, ...)
17
+ # function_name - this is the "finder" function to be called. Examples could be:
18
+ # all
19
+ # find
20
+ # fill_in
21
+ # click_link
22
+ # select
23
+ # etc.
24
+ # args - the arguments that you would pass into the function normally.
25
+ #
26
+ # Usage:
27
+ # Instead of calling: <test_object>.<function> <args>
28
+ # you would call: test_finder <test_object>, :<function>, <args>
29
+ def self.test_finder(test_object, function_name, *args)
30
+ Cornucopia::Capybara::FinderDiagnostics::FindAction.new(test_object, {}, {}, function_name, *args).run
31
+ end
32
+
33
+ # This takes the same arguments as the #test_finder function, but
34
+ # it will always output a diagnostic report.
35
+ #
36
+ # This is for the times when the finder finds something, but you
37
+ # think that it may be wrong, or for whatever reason, you want
38
+ # more information about it to be output.
39
+ def self.diagnose_finder(test_object, function_name, *args)
40
+ find_action = Cornucopia::Capybara::FinderDiagnostics::FindAction.new(test_object, {}, {}, function_name, *args)
41
+
42
+ results = find_action.run
43
+ find_action.generate_report "Diagnostic report on \"#{function_name.to_s}\":", nil
44
+
45
+ results
46
+ end
47
+
48
+ # At the end of the day, almost everything in Capybara calls all to find the element that needs
49
+ # to be worked on. The main difference is if it is synchronized or not.
50
+ #
51
+ # The FindAction class also uses all, but it is never synchronized, and its primary purpose
52
+ # is really to output a bunch of diagnostic information to try to help you do a postmortem on
53
+ # just what is happening.
54
+ #
55
+ # A lot of things could be happening, so a lot of information is output, not all of which is
56
+ # relevant or even useful in every situation.
57
+ #
58
+ # The first thing output is the error (the problem)
59
+ # Then what action was being tried is output (context)
60
+ #
61
+ # In case the problem was with finding the desired element, a list of all elements which
62
+ # could be found using the passed in parameters is output.
63
+ #
64
+ # In case the problem was with context (inside a within block on purpose or accidentally)
65
+ # a list of all other elements which could be found on the page using the passed in
66
+ # parameters is output.
67
+ #
68
+ # In case the problem is something else, we output a screenshot and the page HTML.
69
+ #
70
+ # In case the problem has now solved itself, we try the action again. (This has a very low
71
+ # probability of working as this basically devolves to just duplicating what Capybara is already doing,
72
+ # but it seems like it is worth a shot at least...)
73
+ #
74
+ # In case the problem is a driver bug (specifically Selenium which has some known issues) and
75
+ # is in fact why I even bother trying to do this, try performing the action via javascript.
76
+ # NOTE: As noted in many blogs this type of workaround is not generally a good idea as it can
77
+ # result in false-positives. However since Selenium is buggy, this is the
78
+ # best solution I have other than going to capybara-webkit or poltergeist
79
+ class FindAction
80
+ @@diagnosed_finders = {}
81
+
82
+ attr_accessor :return_value
83
+ attr_accessor :support_options
84
+
85
+ def initialize(test_object, report_options, support_options, function_name, *args)
86
+ @test_object = test_object
87
+ @function_name = function_name
88
+ @args = args
89
+ @support_options = support_options
90
+ @report_options = report_options || {}
91
+
92
+ @report_options[:report] ||= Cornucopia::Util::ReportBuilder.current_report
93
+ end
94
+
95
+ def run
96
+ begin
97
+ simple_run
98
+ rescue
99
+ error = $!
100
+ if perform_analysis(support_options[:__cornucopia_retry_with_found])
101
+ # Cornucopia::Util::Configuration.alternate_retry)
102
+ @return_value
103
+ else
104
+ raise error
105
+ end
106
+ end
107
+ end
108
+
109
+ def simple_run(cornucopia_args = {})
110
+ simple_run_args = @args.clone
111
+ simple_run_options = {}
112
+
113
+ if simple_run_args.last.is_a? Hash
114
+ simple_run_options = simple_run_args.pop
115
+ end
116
+
117
+ @test_object.send(@function_name, *simple_run_args, simple_run_options.merge(cornucopia_args))
118
+ end
119
+
120
+ # def can_dump_details?(attempt_retry, attempt_alternate_retry)
121
+ def can_dump_details?(attempt_retry)
122
+ can_dump = false
123
+
124
+ if (Object.const_defined?("Capybara"))
125
+ can_dump = !@@diagnosed_finders.keys.include?(dump_detail_args(attempt_retry))
126
+ end
127
+
128
+ can_dump
129
+ end
130
+
131
+ # def dump_detail_args(attempt_retry, attempt_alternate_retry)
132
+ def dump_detail_args(attempt_retry)
133
+ check_args = search_args.clone
134
+ my_page = ::Capybara.current_session
135
+
136
+ check_args << options.clone
137
+ check_args << !!attempt_retry
138
+ # check_args << !!attempt_alternate_retry
139
+
140
+ if (my_page && my_page.current_url.present? && my_page.current_url != "about:blank")
141
+ check_args << Digest::SHA2.hexdigest(my_page.html)
142
+ end
143
+
144
+ check_args
145
+ end
146
+
147
+ # def perform_retry(attempt_retry, attempt_alternate_retry, report, report_table)
148
+ def perform_retry(attempt_retry, report, report_table)
149
+ retry_successful = false
150
+
151
+ if attempt_retry && retry_action_with_found_element(report, report_table)
152
+ retry_successful = true
153
+ # else
154
+ # if attempt_alternate_retry && alternate_action_with_found_element(report, report_table)
155
+ # retry_successful = true
156
+ # end
157
+ end
158
+
159
+ retry_successful
160
+ end
161
+
162
+ # def perform_analysis(attempt_retry, attempt_alternate_retry)
163
+ def perform_analysis(attempt_retry)
164
+ retry_successful = false
165
+
166
+ if can_dump_details?(attempt_retry)
167
+ generate_report "An error occurred while processing \"#{@function_name.to_s}\":",
168
+ $! do |report, report_table|
169
+ retry_successful = perform_retry(attempt_retry, report, report_table)
170
+ end
171
+
172
+ dump_args = dump_detail_args(attempt_retry)
173
+ @@diagnosed_finders[dump_args] = { tried: true }
174
+ else
175
+ retry_successful = perform_retry(attempt_retry, nil, nil)
176
+ end
177
+
178
+ retry_successful
179
+ end
180
+
181
+ def generate_report(message, error = nil, &block)
182
+ if @report_options[:report] && @report_options[:table]
183
+ generate_report_in_table @report_options[:table], error, &block
184
+ else
185
+ @report_options[:report] ||= Cornucopia::Util::ReportBuilder.current_report
186
+ @report_options[:table] = nil
187
+
188
+ @report_options[:report].within_section(message) do |report|
189
+ generate_report_in_table @report_options[:table], error, &block
190
+ end
191
+ end
192
+ end
193
+
194
+ def generate_report_in_table(table, error = nil, &block)
195
+ @report_options[:table] = table
196
+
197
+ init_search_args
198
+ all_elements
199
+ all_other_elements
200
+ guessed_types
201
+
202
+ configured_report = Cornucopia::Util::Configuration.report_configuration(:capybara_finder_diagnostics)
203
+
204
+ configured_report.add_report_objects(finder: self, exception: error)
205
+ configured_report.generate_report(@report_options[:report], report_table: @report_options[:table], &block)
206
+ end
207
+
208
+ def retry_action_with_found_element report, report_table
209
+ return_result = false
210
+ result = "Failed"
211
+
212
+ case @function_name.to_s
213
+ when "assert_selector"
214
+ if found_element
215
+ @return_value = true
216
+ return_result = true
217
+ result = "Found"
218
+ end
219
+
220
+ when "assert_no_selector"
221
+ unless found_element
222
+ @return_value = true
223
+ return_result = true
224
+ result = "Not Found"
225
+ end
226
+
227
+ when "find", "all"
228
+ if found_element
229
+ result = "Success"
230
+
231
+ @return_value = found_element
232
+
233
+ return_result = true
234
+ end
235
+ end
236
+
237
+ report_table.write_stats "Retrying action:", result if report_table && return_result
238
+
239
+ return_result
240
+ end
241
+
242
+ # def alternate_action_with_found_element report, report_table
243
+ # return_result = false
244
+ #
245
+ # result = "Could not attempt to try the action through an alternate method."
246
+ # if found_element &&
247
+ # ::Capybara.current_session.respond_to?(:evaluate_script)
248
+ # begin
249
+ # native_id = get_attribute found_element, "id"
250
+ # if (native_id)
251
+ # case @function_name.to_s
252
+ # when "click_link_or_button", "click_link", "click_button"
253
+ # @return_value = ::Capybara.current_session.evaluate_script("$(\"\##{native_id}\")[0].click()")
254
+ # when "fill_in"
255
+ # @return_value = ::Capybara.current_session.evaluate_script("$(\"\##{native_id}\")[0].val(\"#{options[:with]}\")")
256
+ # when "choose", "check"
257
+ # @return_value = ::Capybara.current_session.evaluate_script("$(\"\##{native_id}\")[0].val(\"checked\", true)")
258
+ # when "uncheck"
259
+ # @return_value = ::Capybara.current_session.evaluate_script("$(\"\##{native_id}\")[0].val(\"checked\", false)")
260
+ # when "select"
261
+ # @return_value = ::Capybara.current_session.evaluate_script("$(\"\##{native_id}\")[0].val(\"selected\", true)")
262
+ # when "unselect"
263
+ # @return_value = ::Capybara.current_session.evaluate_script("$(\"\##{native_id}\")[0].val(\"selected\", false)")
264
+ # else
265
+ # result = "Could not decide what to do with #{@function_name}"
266
+ # raise new Exception("unknown action")
267
+ # end
268
+ #
269
+ # return_result = true
270
+ # end
271
+ # rescue
272
+ # result ||= "Still couldn't do the action - #{$!.to_s}."
273
+ # end
274
+ # end
275
+ #
276
+ # report_table.write_stats "Trying alternate action:", result if report_table
277
+ # return_result
278
+ # end
279
+
280
+ def found_element
281
+ if @function_name.to_sym == :all
282
+ all_elements.map(&:found_element)
283
+ else
284
+ if all_elements && all_elements.length == 1
285
+ all_elements[0].try(:found_element)
286
+ else
287
+ nil
288
+ end
289
+ end
290
+ end
291
+
292
+ def all_elements
293
+ unless @all_elements
294
+ if options && options.has_key?(:from)
295
+ from_within = nil
296
+
297
+ @report_options[:report].within_table(report_table: @report_options[:table]) do |report_table|
298
+ Cornucopia::Util::ReportTable.new(nested_table: report_table,
299
+ nested_table_label: "Within block:") do |sub_report|
300
+ sub_report_options = @report_options.clone
301
+ sub_report_options[:table] = sub_report
302
+ from_within = Cornucopia::Capybara::FinderDiagnostics::FindAction.
303
+ new(@test_object,
304
+ sub_report_options,
305
+ {},
306
+ :find,
307
+ :select,
308
+ options[:from])
309
+
310
+ from_within.generate_report_in_table(sub_report, nil)
311
+ end
312
+ end
313
+
314
+ from_element = from_within.found_element
315
+ if search_args[0].is_a?(Symbol)
316
+ search_args[0] = :option
317
+ end
318
+ options.delete(:from)
319
+
320
+ unless from_element
321
+ @all_elements = []
322
+ return @all_elements
323
+ end
324
+ else
325
+ from_element = @test_object
326
+ end
327
+
328
+ @all_elements = from_element.all(*search_args, visible: false, __cornucopia_no_analysis: true).
329
+ to_a.map do |element|
330
+ FoundElement.new(element)
331
+ end
332
+ end
333
+
334
+ @all_elements
335
+ end
336
+
337
+ def all_other_elements
338
+ unless @all_other_elements
339
+ from_element = ::Capybara::current_session
340
+
341
+ @all_other_elements = from_element.all(*search_args, visible: false, __cornucopia_no_analysis: true).
342
+ to_a.map do |element|
343
+ FoundElement.new(element) unless all_elements.include?(element)
344
+ end
345
+
346
+ @all_other_elements = @all_other_elements - @all_elements
347
+ @all_other_elements.compact!
348
+ end
349
+
350
+ @all_other_elements
351
+ end
352
+
353
+ def search_args
354
+ init_search_args
355
+ @search_args
356
+ end
357
+
358
+ def options
359
+ init_search_args
360
+ @options
361
+ end
362
+
363
+ def init_search_args
364
+ unless @search_args
365
+ @search_args = @args.clone
366
+ @options = {}
367
+
368
+ if @search_args.last.is_a? Hash
369
+ @options = @search_args.pop
370
+ end
371
+ if guessed_types.length > 0 && @search_args[0] != guessed_types[0]
372
+ @search_args.insert(0, guessed_types[0])
373
+ end
374
+ if guessed_types.length <= 0 && @search_args[0] != ::Capybara.default_selector
375
+ @search_args.insert(0, ::Capybara.default_selector)
376
+ end
377
+ end
378
+ end
379
+
380
+ # a list of guesses as to what kind of object is being searched for
381
+ def guessed_types
382
+ unless @guessed_types
383
+ if search_args.length > 0
384
+ if search_args[0].is_a?(Symbol)
385
+ @guessed_types = [search_args[0]]
386
+ else
387
+ @guessed_types = [:id, :css, :xpath, :link_or_button, :fillable_field, :radio_button, :checkbox, :select, :option,
388
+ :file_field, :table, :field, :fieldset, :content].select do |test_type|
389
+ begin
390
+ @test_object.all(test_type, *search_args, visible: false, __cornucopia_no_analysis: true).length > 0
391
+ rescue
392
+ # Normally bad form, but for this function, we just don't want this to throw errors.
393
+ # We are only concerned with whatever actually succeeds.
394
+ false
395
+ end
396
+ end
397
+ end
398
+ end
399
+ end
400
+
401
+ @guessed_types
402
+ end
403
+
404
+ private
405
+
406
+ class FoundElement
407
+ ELEMENT_ATTRIBUTES = [
408
+ "text",
409
+ "value",
410
+ "visible?",
411
+ "checked?",
412
+ "selected?",
413
+ "tag_name",
414
+ "location",
415
+ "id",
416
+ "src",
417
+ "name",
418
+ "href",
419
+ "style",
420
+ "path",
421
+ "outerHTML"
422
+ ]
423
+
424
+ NATIVE_ATTRIBUTES = [
425
+ "size",
426
+ "type"
427
+ ]
428
+
429
+ PREDEFINED_ATTRIBUTES = NATIVE_ATTRIBUTES + ELEMENT_ATTRIBUTES
430
+
431
+ attr_reader :found_element
432
+
433
+ def ==(comparison_object)
434
+ comparison_object.equal?(self) ||
435
+ (comparison_object.instance_of?(self.class) &&
436
+ (comparison_object.found_element == found_element
437
+ # ||
438
+ # (comparison_object.instance_variable_get(:@elem_text) == @elem_text &&
439
+ # comparison_object.instance_variable_get(:@elem_value) == @elem_value &&
440
+ # comparison_object.instance_variable_get(:@elem_visible) == @elem_visible &&
441
+ # comparison_object.instance_variable_get(:@elem_checked) == @elem_checked &&
442
+ # comparison_object.instance_variable_get(:@elem_selected) == @elem_selected &&
443
+ # comparison_object.instance_variable_get(:@elem_tag_name) == @elem_tag_name &&
444
+ # comparison_object.instance_variable_get(:@elem_location) == @elem_location &&
445
+ # comparison_object.instance_variable_get(:@elem_size) == @elem_size &&
446
+ # comparison_object.instance_variable_get(:@elem_id) == @elem_id &&
447
+ # comparison_object.instance_variable_get(:@elem_name) == @elem_name &&
448
+ # comparison_object.instance_variable_get(:@elem_href) == @elem_href &&
449
+ # comparison_object.instance_variable_get(:@elem_style) == @elem_style &&
450
+ # comparison_object.instance_variable_get(:@elem_path) == @elem_path &&
451
+ # comparison_object.instance_variable_get(:@elem_outerHTML) == @elem_outerHTML &&
452
+ # comparison_object.instance_variable_get(:@native_class) == @native_class &&
453
+ # comparison_object.instance_variable_get(:@native_value) == @native_value &&
454
+ # comparison_object.instance_variable_get(:@native_type) == @native_type
455
+ # )
456
+ )
457
+ ) ||
458
+ comparison_object == found_element
459
+ end
460
+
461
+ def initialize(found_element)
462
+ @found_element = found_element
463
+ ELEMENT_ATTRIBUTES.each do |attrib|
464
+ variable_name = attrib.to_s.gsub("?", "")
465
+ instance_variable_set("@elem_#{variable_name}", get_attribute(attrib))
466
+ end
467
+
468
+ NATIVE_ATTRIBUTES.each do |attrib|
469
+ instance_variable_set("@native_#{attrib}", get_native_attribute(attrib))
470
+ end
471
+
472
+ instance_variable_set("@native_class", @found_element[:class])
473
+
474
+ if ::Capybara.current_session.driver.respond_to?(:browser) &&
475
+ ::Capybara.current_session.driver.browser.method(:execute_script).arity != 1
476
+ begin
477
+ # This is a "trick" that works with Selenium, but which might not work with other drivers...
478
+ script = "var attrib_list = [];
479
+ var attrs = arguments[0].attributes;
480
+ for (var nIndex = 0; nIndex < attrs.length; nIndex += 1)
481
+ {
482
+ var a = attrs[nIndex];
483
+ attrib_list.push(a.name);
484
+ };
485
+ return attrib_list;"
486
+
487
+ attributes = ::Capybara.current_session.driver.browser.execute_script(script, @found_element.native)
488
+ attributes.each do |attritue|
489
+ unless PREDEFINED_ATTRIBUTES.include?(attritue)
490
+ instance_variable_set("@native_#{attritue}", @found_element[attritue])
491
+ end
492
+ end
493
+
494
+ @elem_outerHTML ||= ::Capybara.current_session.driver.browser.execute_script("return arguments[0].outerHTML", @found_element.native)
495
+ rescue ::Capybara::NotSupportedByDriverError
496
+ end
497
+ end
498
+
499
+ # information from Selenium that may not be available depending on the form, the full outerHTML of the element
500
+ if (::Capybara.current_session.respond_to?(:evaluate_script))
501
+ unless (@elem_id.blank?)
502
+ begin
503
+ @elem_outerHTML ||= ::Capybara.current_session.evaluate_script("document.getElementById('#{@elem_id}').outerHTML")
504
+ rescue ::Capybara::NotSupportedByDriverError
505
+ end
506
+ end
507
+ end
508
+ end
509
+
510
+ def get_attribute(attribute)
511
+ if @found_element.respond_to?(attribute)
512
+ begin
513
+ @found_element.send(attribute)
514
+ rescue ::Capybara::NotSupportedByDriverError
515
+ end
516
+ elsif @found_element.respond_to?(:[]) && @found_element[attribute]
517
+ @found_element[attribute]
518
+ else
519
+ get_native_attribute(attribute)
520
+ end
521
+ end
522
+
523
+ def get_native_attribute(attribute)
524
+ if @found_element.native.respond_to?(attribute)
525
+ @found_element.native.send(attribute)
526
+ elsif @found_element.native.respond_to?(:[])
527
+ @found_element.native[attribute]
528
+ else
529
+ nil
530
+ end
531
+ end
532
+ end
533
+ end
534
+ end
535
+ end
536
+ end