cornucopia 0.1.12

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.
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