leifcr-capybara-screenshot 1.0.14

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 (60) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +13 -0
  5. data/Appraisals +19 -0
  6. data/CHANGELOG.md +256 -0
  7. data/Gemfile +9 -0
  8. data/LICENSE +19 -0
  9. data/README.md +315 -0
  10. data/Rakefile +40 -0
  11. data/capybara-screenshot.gemspec +39 -0
  12. data/gemfiles/cucumber.1.3.gemfile +11 -0
  13. data/gemfiles/cucumber.2.4.gemfile +11 -0
  14. data/gemfiles/latest.gemfile +10 -0
  15. data/gemfiles/rspec.3.0.gemfile +11 -0
  16. data/gemfiles/spinach.0.8.gemfile +11 -0
  17. data/lib/capybara-screenshot/callbacks.rb +44 -0
  18. data/lib/capybara-screenshot/capybara.rb +26 -0
  19. data/lib/capybara-screenshot/cucumber.rb +28 -0
  20. data/lib/capybara-screenshot/helpers.rb +28 -0
  21. data/lib/capybara-screenshot/minitest.rb +36 -0
  22. data/lib/capybara-screenshot/pruner.rb +48 -0
  23. data/lib/capybara-screenshot/rspec/base_reporter.rb +21 -0
  24. data/lib/capybara-screenshot/rspec/html_embed_reporter.rb +25 -0
  25. data/lib/capybara-screenshot/rspec/html_link_reporter.rb +37 -0
  26. data/lib/capybara-screenshot/rspec/json_reporter.rb +19 -0
  27. data/lib/capybara-screenshot/rspec/text_reporter.rb +39 -0
  28. data/lib/capybara-screenshot/rspec/textmate_link_reporter.rb +19 -0
  29. data/lib/capybara-screenshot/rspec.rb +95 -0
  30. data/lib/capybara-screenshot/s3_saver.rb +64 -0
  31. data/lib/capybara-screenshot/saver.rb +131 -0
  32. data/lib/capybara-screenshot/spinach.rb +26 -0
  33. data/lib/capybara-screenshot/testunit.rb +39 -0
  34. data/lib/capybara-screenshot/version.rb +5 -0
  35. data/lib/capybara-screenshot.rb +217 -0
  36. data/spec/cucumber/cucumber_spec.rb +89 -0
  37. data/spec/cucumber/step_definitions/step_definitions.rb +18 -0
  38. data/spec/cucumber/support/env.rb +17 -0
  39. data/spec/feature/minitest_spec.rb +79 -0
  40. data/spec/feature/testunit_spec.rb +77 -0
  41. data/spec/rspec/rspec_spec.rb +158 -0
  42. data/spec/spec_helper.rb +34 -0
  43. data/spec/spinach/spinach_spec.rb +60 -0
  44. data/spec/spinach/support/spinach_failure.rb +41 -0
  45. data/spec/support/aruba.rb +2 -0
  46. data/spec/support/common_setup.rb +67 -0
  47. data/spec/support/html_reporter_context.rb +28 -0
  48. data/spec/support/test_app.rb +13 -0
  49. data/spec/unit/base_reporter_spec.rb +25 -0
  50. data/spec/unit/capybara-screenshot_rspec_spec.rb +48 -0
  51. data/spec/unit/capybara-screenshot_spec.rb +121 -0
  52. data/spec/unit/capybara_spec.rb +50 -0
  53. data/spec/unit/pruner_spec.rb +108 -0
  54. data/spec/unit/rspec_reporters/html_embed_reporter_spec.rb +18 -0
  55. data/spec/unit/rspec_reporters/html_link_reporter_spec.rb +27 -0
  56. data/spec/unit/rspec_reporters/text_reporter_spec.rb +98 -0
  57. data/spec/unit/rspec_reporters/textmate_link_reporter_spec.rb +39 -0
  58. data/spec/unit/s3_saver_spec.rb +132 -0
  59. data/spec/unit/saver_spec.rb +366 -0
  60. metadata +264 -0
@@ -0,0 +1,19 @@
1
+ require 'capybara-screenshot/rspec/base_reporter'
2
+
3
+ module Capybara
4
+ module Screenshot
5
+ module RSpec
6
+ module JsonReporter
7
+ extend BaseReporter
8
+
9
+ enhance_with_screenshot :format_example
10
+
11
+ def format_example_with_screenshot(example)
12
+ format_example_without_screenshot(example).merge({
13
+ screenshot: example.metadata[:screenshot]
14
+ })
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,39 @@
1
+ require 'capybara-screenshot/rspec/base_reporter'
2
+ require 'capybara-screenshot/helpers'
3
+
4
+ module Capybara
5
+ module Screenshot
6
+ module RSpec
7
+ module TextReporter
8
+ extend BaseReporter
9
+
10
+ if ::RSpec::Core::Version::STRING.to_i <= 2
11
+ enhance_with_screenshot :dump_failure_info
12
+ else
13
+ enhance_with_screenshot :example_failed
14
+ end
15
+
16
+ def dump_failure_info_with_screenshot(example)
17
+ dump_failure_info_without_screenshot example
18
+ output_screenshot_info(example)
19
+ end
20
+
21
+ def example_failed_with_screenshot(notification)
22
+ example_failed_without_screenshot notification
23
+ output_screenshot_info(notification.example)
24
+ end
25
+
26
+ private
27
+ def output_screenshot_info(example)
28
+ return unless (screenshot = example.metadata[:screenshot])
29
+ output.puts(long_padding + CapybaraScreenshot::Helpers.yellow("HTML screenshot: file://#{screenshot[:html]}")) if screenshot[:html]
30
+ output.puts(long_padding + CapybaraScreenshot::Helpers.yellow("Image screenshot: file://#{screenshot[:image]}")) if screenshot[:image]
31
+ end
32
+
33
+ def long_padding
34
+ " "
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,19 @@
1
+ require 'capybara-screenshot/rspec/base_reporter'
2
+ require 'capybara-screenshot/rspec/html_link_reporter'
3
+ require 'shellwords'
4
+
5
+ module Capybara
6
+ module Screenshot
7
+ module RSpec
8
+ module TextMateLinkReporter
9
+ extend BaseReporter
10
+ include HtmlLinkReporter
11
+ enhance_with_screenshot :extra_failure_content
12
+
13
+ def attributes_for_screenshot_link(url)
14
+ super.merge("onclick" => "TextMate.system('open #{Shellwords.escape(url)}'); return false;")
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,95 @@
1
+ require "capybara-screenshot"
2
+
3
+ require "capybara-screenshot/rspec/text_reporter"
4
+ require "capybara-screenshot/rspec/html_link_reporter"
5
+ require "capybara-screenshot/rspec/html_embed_reporter"
6
+ require "capybara-screenshot/rspec/json_reporter"
7
+ require "capybara-screenshot/rspec/textmate_link_reporter"
8
+
9
+ module Capybara
10
+ module Screenshot
11
+ module RSpec
12
+
13
+ # Reporters extend RSpec formatters to display information about screenshots for failed
14
+ # examples.
15
+ #
16
+ # Technically, a reporter is a module that gets injected into a RSpec formatter class.
17
+ # It uses method aliasing to extend some (usually just one) of the formatter's methods.
18
+ #
19
+ # Implementing a custom reporter is as simple as creating a module and setting up the
20
+ # appropriate aliases. Use `BaseReporter.enhance_with_screenshot` if you don't want
21
+ # to set up the aliases manually:
22
+ #
23
+ # module MyReporter
24
+ # extend Capybara::Screenshot::RSpec::BaseReporter
25
+ #
26
+ # # Will replace the formatter's original `dump_failure_info` method with
27
+ # # `dump_failure_info_with_screenshot` from this module:
28
+ # enhance_with_screenshot :dump_failure_info
29
+ #
30
+ # def dump_failure_info_with_screenshot(example)
31
+ # dump_failure_info_without_screenshot(example) # call original implementation
32
+ # ... # your additions here
33
+ # end
34
+ # end
35
+ #
36
+ # Finally customize `Capybara::Screenshot::RSpec::FORMATTERS` to make sure your reporter
37
+ # gets injected into the appropriate formatter.
38
+
39
+ REPORTERS = {
40
+ "RSpec::Core::Formatters::ProgressFormatter" => Capybara::Screenshot::RSpec::TextReporter,
41
+ "RSpec::Core::Formatters::DocumentationFormatter" => Capybara::Screenshot::RSpec::TextReporter,
42
+ "RSpec::Core::Formatters::HtmlFormatter" => Capybara::Screenshot::RSpec::HtmlLinkReporter,
43
+ "RSpec::Core::Formatters::JsonFormatter" => Capybara::Screenshot::RSpec::JsonReporter,
44
+ "RSpec::Core::Formatters::TextMateFormatter" => Capybara::Screenshot::RSpec::TextMateLinkReporter, # RSpec 2
45
+ "RSpec::Mate::Formatters::TextMateFormatter" => Capybara::Screenshot::RSpec::TextMateLinkReporter, # RSpec 3
46
+ "Fuubar" => Capybara::Screenshot::RSpec::TextReporter
47
+ }
48
+
49
+ class << self
50
+ attr_accessor :add_link_to_screenshot_for_failed_examples
51
+
52
+ def after_failed_example(example)
53
+ if example.example_group.include?(Capybara::DSL) # Capybara DSL method has been included for a feature we can snapshot
54
+ Capybara.using_session(Capybara::Screenshot.final_session_name) do
55
+ if Capybara.page.current_url != '' && Capybara::Screenshot.autosave_on_failure && example.exception
56
+ filename_prefix = Capybara::Screenshot.filename_prefix_for(:rspec, example)
57
+
58
+ saver = Capybara::Screenshot.new_saver(Capybara, Capybara.page, true, filename_prefix)
59
+ saver.save
60
+
61
+ example.metadata[:screenshot] = {}
62
+ example.metadata[:screenshot][:html] = saver.html_path if saver.html_saved?
63
+ example.metadata[:screenshot][:image] = saver.screenshot_path if saver.screenshot_saved?
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ self.add_link_to_screenshot_for_failed_examples = true
71
+ end
72
+ end
73
+ end
74
+
75
+ RSpec.configure do |config|
76
+ config.before do
77
+ Capybara::Screenshot.final_session_name = nil
78
+ end
79
+
80
+ config.after do |example_from_block_arg|
81
+ # RSpec 3 no longer defines `example`, but passes the example as block argument instead
82
+ example = config.respond_to?(:expose_current_running_example_as) ? example_from_block_arg : self.example
83
+
84
+ Capybara::Screenshot::RSpec.after_failed_example(example)
85
+ end
86
+
87
+ config.before(:suite) do
88
+ if Capybara::Screenshot::RSpec.add_link_to_screenshot_for_failed_examples
89
+ RSpec.configuration.formatters.each do |formatter|
90
+ next unless (reporter_module = Capybara::Screenshot::RSpec::REPORTERS[formatter.class.to_s])
91
+ formatter.singleton_class.send :include, reporter_module
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,64 @@
1
+ require 'aws-sdk'
2
+
3
+ module Capybara
4
+ module Screenshot
5
+ class S3Saver
6
+ DEFAULT_REGION = 'us-east-1'
7
+
8
+ def initialize(saver, s3_client, bucket_name)
9
+ @saver = saver
10
+ @s3_client = s3_client
11
+ @bucket_name = bucket_name
12
+ end
13
+
14
+ def self.new_with_configuration(saver, configuration)
15
+ default_s3_client_credentials = {
16
+ region: DEFAULT_REGION
17
+ }
18
+
19
+ s3_client_credentials = default_s3_client_credentials.merge(
20
+ configuration.fetch(:s3_client_credentials)
21
+ )
22
+
23
+ s3_client = Aws::S3::Client.new(s3_client_credentials)
24
+ bucket_name = configuration.fetch(:bucket_name)
25
+
26
+ new(saver, s3_client, bucket_name)
27
+ rescue KeyError
28
+ raise "Invalid S3 Configuration #{configuration}. Please refer to the documentation for the necessary configurations."
29
+ end
30
+
31
+ def save_and_upload_screenshot
32
+ save_and do |local_file_path|
33
+ File.open(local_file_path) do |file|
34
+ s3_client.put_object(
35
+ bucket: bucket_name,
36
+ key: File.basename(local_file_path),
37
+ body: file
38
+ )
39
+ end
40
+ end
41
+ end
42
+ alias_method :save, :save_and_upload_screenshot
43
+
44
+ def method_missing(method, *args)
45
+ # Need to use @saver instead of S3Saver#saver attr_reader method because
46
+ # using the method goes into infinite loop. Maybe attr_reader implements
47
+ # its methods via method_missing?
48
+ @saver.send(method, *args)
49
+ end
50
+
51
+ private
52
+ attr_reader :saver,
53
+ :s3_client,
54
+ :bucket_name
55
+
56
+ def save_and
57
+ saver.save
58
+
59
+ yield(saver.html_path) if block_given? && saver.html_saved?
60
+ yield(saver.screenshot_path) if block_given? && saver.screenshot_saved?
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,131 @@
1
+ require 'capybara-screenshot/helpers'
2
+ require 'capybara-screenshot/callbacks'
3
+
4
+ module Capybara
5
+ module Screenshot
6
+ class Saver
7
+ include Capybara::Screenshot::Callbacks
8
+
9
+ define_callback :after_save_html
10
+ define_callback :after_save_screenshot
11
+
12
+ attr_reader :capybara, :page, :file_base_name
13
+
14
+ def initialize(capybara, page, html_save=true, filename_prefix='screenshot')
15
+ @capybara, @page, @html_save = capybara, page, html_save
16
+ time_now = Time.now
17
+ timestamp = "#{time_now.strftime('%Y-%m-%d-%H-%M-%S.')}#{'%03d' % (time_now.usec/1000).to_i}"
18
+
19
+ filename = [filename_prefix]
20
+ filename << timestamp if Capybara::Screenshot.append_timestamp
21
+ filename << SecureRandom.hex if Capybara::Screenshot.append_random
22
+
23
+ @file_base_name = filename.join('_')
24
+
25
+ Capybara::Screenshot.prune
26
+ end
27
+
28
+ def save
29
+ # if current_path empty then nothing to screen shot as browser has not loaded any URL
30
+ return if page.current_path.to_s.empty?
31
+
32
+ save_html if @html_save
33
+ save_screenshot
34
+ end
35
+
36
+ def save_html
37
+ path = html_path
38
+ clear_save_path do
39
+ if Capybara::VERSION.match(/^\d+/)[0] == '1'
40
+ capybara.save_page(page.body, "#{path}")
41
+ else
42
+ capybara.save_page("#{path}")
43
+ end
44
+ end
45
+ @html_saved = true
46
+ run_callbacks :after_save_html, html_path if html_saved?
47
+ end
48
+
49
+ def save_screenshot
50
+ path = screenshot_path
51
+ clear_save_path do
52
+ result = Capybara::Screenshot.registered_drivers.fetch(capybara.current_driver) { |driver_name|
53
+ warn "capybara-screenshot could not detect a screenshot driver for '#{capybara.current_driver}'. Saving with default with unknown results."
54
+ Capybara::Screenshot.registered_drivers[:default]
55
+ }.call(page.driver, path)
56
+ @screenshot_saved = result != :not_supported
57
+ end
58
+ run_callbacks :after_save_screenshot, screenshot_path if screenshot_saved?
59
+ end
60
+
61
+ def html_path
62
+ File.join(Capybara::Screenshot.capybara_root, "#{file_base_name}.html")
63
+ end
64
+
65
+ def screenshot_path
66
+ File.join(Capybara::Screenshot.capybara_root, "#{file_base_name}.png")
67
+ end
68
+
69
+ def html_path_alt_root
70
+ File.join(Capybara::Screenshot.capybara_alt_root, "#{file_base_name}.html")
71
+ end
72
+
73
+ def screenshot_path_alt_root
74
+ File.join(Capybara::Screenshot.capybara_alt_root, "#{file_base_name}.png")
75
+ end
76
+
77
+ def html_saved?
78
+ @html_saved
79
+ end
80
+
81
+ def screenshot_saved?
82
+ @screenshot_saved
83
+ end
84
+
85
+ # If Capybara::Screenshot.capybara_tmp_path is set then
86
+ # the html_path or screenshot_path can be appended to this path in
87
+ # some versions of Capybara instead of using it as an absolute path
88
+ def clear_save_path
89
+ old_path = Capybara::Screenshot.capybara_tmp_path
90
+ Capybara::Screenshot.capybara_tmp_path = nil
91
+ yield
92
+ ensure
93
+ Capybara::Screenshot.capybara_tmp_path = old_path
94
+ end
95
+
96
+ def output_screenshot_path
97
+ output "HTML screenshot: #{html_path_alt_root}" if html_saved?
98
+ output "Image screenshot: #{screenshot_path_alt_root}" if screenshot_saved?
99
+ end
100
+
101
+ # Print image to screen, if imgcat is available
102
+ def display_image
103
+ system("#{imgcat} #{screenshot_path}") unless imgcat.nil?
104
+ end
105
+
106
+ private
107
+
108
+ def output(message)
109
+ puts " #{CapybaraScreenshot::Helpers.yellow(message)}"
110
+ end
111
+
112
+ def imgcat
113
+ @imgcat ||= which('imgcat')
114
+ end
115
+
116
+ # Cross-platform way of finding an executable in the $PATH.
117
+ #
118
+ # which('ruby') #=> /usr/bin/ruby
119
+ def which(cmd)
120
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
121
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
122
+ exts.each { |ext|
123
+ exe = File.join(path, "#{cmd}#{ext}")
124
+ return exe if File.executable?(exe) && !File.directory?(exe)
125
+ }
126
+ end
127
+ return nil
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,26 @@
1
+ require "capybara-screenshot"
2
+
3
+ Spinach.hooks.before_scenario do |scenario|
4
+ Capybara::Screenshot.final_session_name = nil
5
+ end
6
+
7
+ module Capybara::Screenshot::Spinach
8
+ def self.fail_with_screenshot(step_data, exception, location, step_definitions)
9
+ if Capybara::Screenshot.autosave_on_failure
10
+ Capybara.using_session(Capybara::Screenshot.final_session_name) do
11
+ filename_prefix = Capybara::Screenshot.filename_prefix_for(:spinach, step_data)
12
+ saver = Capybara::Screenshot.new_saver(Capybara, Capybara.page, true, filename_prefix)
13
+ saver.save
14
+ saver.output_screenshot_path
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ Spinach.hooks.on_failed_step do |*args|
21
+ Capybara::Screenshot::Spinach.fail_with_screenshot(*args)
22
+ end
23
+
24
+ Spinach.hooks.on_error_step do |*args|
25
+ Capybara::Screenshot::Spinach.fail_with_screenshot(*args)
26
+ end
@@ -0,0 +1,39 @@
1
+ require 'test/unit/testresult'
2
+
3
+ module Capybara::Screenshot
4
+ class << self
5
+ attr_accessor :testunit_paths
6
+ end
7
+
8
+ self.testunit_paths = [%r{test/integration}]
9
+ end
10
+
11
+ Test::Unit::TestCase.class_eval do
12
+ setup do
13
+ Capybara::Screenshot.final_session_name = nil
14
+ end
15
+ end
16
+
17
+ Test::Unit::TestResult.class_eval do
18
+ private
19
+
20
+ def notify_fault_with_screenshot(fault, *args)
21
+ notify_fault_without_screenshot fault, *args
22
+ is_integration_test = fault.location.any? do |location|
23
+ Capybara::Screenshot.testunit_paths.any? { |path| location.match(path) }
24
+ end
25
+ if is_integration_test
26
+ if Capybara::Screenshot.autosave_on_failure
27
+ Capybara.using_session(Capybara::Screenshot.final_session_name) do
28
+ filename_prefix = Capybara::Screenshot.filename_prefix_for(:testunit, fault)
29
+
30
+ saver = Capybara::Screenshot.new_saver(Capybara, Capybara.page, true, filename_prefix)
31
+ saver.save
32
+ saver.output_screenshot_path
33
+ end
34
+ end
35
+ end
36
+ end
37
+ alias notify_fault_without_screenshot notify_fault
38
+ alias notify_fault notify_fault_with_screenshot
39
+ end
@@ -0,0 +1,5 @@
1
+ module Capybara
2
+ module Screenshot
3
+ VERSION = '1.0.14'
4
+ end
5
+ end