capybara-screenshot-diff 1.6.2 → 1.8.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +29 -0
  3. data/capybara-screenshot-diff.gemspec +6 -3
  4. data/gems.rb +8 -2
  5. data/lib/capybara/screenshot/diff/browser_helpers.rb +102 -0
  6. data/lib/capybara/screenshot/diff/cucumber.rb +11 -0
  7. data/lib/capybara/screenshot/diff/difference.rb +63 -0
  8. data/lib/capybara/screenshot/diff/drivers/base_driver.rb +42 -0
  9. data/lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb +193 -252
  10. data/lib/capybara/screenshot/diff/drivers/utils.rb +25 -0
  11. data/lib/capybara/screenshot/diff/drivers/vips_driver.rb +65 -100
  12. data/lib/capybara/screenshot/diff/drivers.rb +16 -0
  13. data/lib/capybara/screenshot/diff/image_compare.rb +138 -154
  14. data/lib/capybara/screenshot/diff/os.rb +1 -1
  15. data/lib/capybara/screenshot/diff/region.rb +86 -0
  16. data/lib/capybara/screenshot/diff/screenshot_matcher.rb +128 -0
  17. data/lib/capybara/screenshot/diff/screenshoter.rb +136 -0
  18. data/lib/capybara/screenshot/diff/stabilization.rb +0 -210
  19. data/lib/capybara/screenshot/diff/stable_screenshoter.rb +106 -0
  20. data/lib/capybara/screenshot/diff/test_methods.rb +57 -63
  21. data/lib/capybara/screenshot/diff/vcs.rb +48 -21
  22. data/lib/capybara/screenshot/diff/version.rb +1 -1
  23. data/lib/capybara/screenshot/diff.rb +38 -35
  24. data/sig/capybara/screenshot/diff/diff.rbs +28 -0
  25. data/sig/capybara/screenshot/diff/difference.rbs +33 -0
  26. data/sig/capybara/screenshot/diff/drivers/base_driver.rbs +63 -0
  27. data/sig/capybara/screenshot/diff/drivers/browser_helpers.rbs +36 -0
  28. data/sig/capybara/screenshot/diff/drivers/chunky_png_driver.rbs +89 -0
  29. data/sig/capybara/screenshot/diff/drivers/utils.rbs +13 -0
  30. data/sig/capybara/screenshot/diff/drivers/vips_driver.rbs +25 -0
  31. data/sig/capybara/screenshot/diff/image_compare.rbs +93 -0
  32. data/sig/capybara/screenshot/diff/os.rbs +11 -0
  33. data/sig/capybara/screenshot/diff/region.rbs +43 -0
  34. data/sig/capybara/screenshot/diff/screenshot_matcher.rbs +60 -0
  35. data/sig/capybara/screenshot/diff/screenshoter.rbs +48 -0
  36. data/sig/capybara/screenshot/diff/stable_screenshoter.rbs +29 -0
  37. data/sig/capybara/screenshot/diff/test_methods.rbs +39 -0
  38. data/sig/capybara/screenshot/diff/vcs.rbs +17 -0
  39. metadata +30 -25
  40. data/.gitattributes +0 -4
  41. data/.github/workflows/lint.yml +0 -25
  42. data/.github/workflows/test.yml +0 -120
  43. data/.gitignore +0 -12
  44. data/.standard.yml +0 -12
  45. data/CONTRIBUTING.md +0 -22
  46. data/Dockerfile +0 -60
  47. data/README.md +0 -555
  48. data/bin/bundle +0 -114
  49. data/bin/console +0 -15
  50. data/bin/install-vips +0 -11
  51. data/bin/rake +0 -27
  52. data/bin/setup +0 -8
  53. data/bin/standardrb +0 -29
  54. data/gemfiles/rails52.gemfile +0 -6
  55. data/gemfiles/rails60_gems.rb +0 -8
  56. data/gemfiles/rails61_gems.rb +0 -7
  57. data/gemfiles/rails70_gems.rb +0 -7
  58. data/tmp/.keep +0 -0
@@ -5,48 +5,41 @@ require "capybara"
5
5
  require "action_controller"
6
6
  require "action_dispatch"
7
7
  require "active_support/core_ext/string/strip"
8
+ require "pathname"
9
+
10
+ require_relative "drivers"
8
11
  require_relative "image_compare"
9
- require_relative "stabilization"
10
12
  require_relative "vcs"
13
+ require_relative "browser_helpers"
14
+ require_relative "region"
15
+
16
+ require_relative "screenshot_matcher"
11
17
 
12
18
  # Add the `screenshot` method to ActionDispatch::IntegrationTest
13
19
  module Capybara
14
20
  module Screenshot
15
21
  module Diff
16
22
  module TestMethods
17
- include Stabilization
18
- include Vcs
19
-
20
23
  def initialize(*)
21
24
  super
22
25
  @screenshot_counter = nil
23
26
  @screenshot_group = nil
24
27
  @screenshot_section = nil
25
28
  @test_screenshot_errors = nil
26
- @test_screenshots = nil
29
+ @test_screenshots = []
27
30
  end
28
31
 
29
- def group_parts
30
- parts = []
31
- parts << @screenshot_section if @screenshot_section.present?
32
- parts << @screenshot_group if @screenshot_group.present?
33
- parts
34
- end
32
+ def build_full_name(name)
33
+ if @screenshot_counter
34
+ name = format("%02i_#{name}", @screenshot_counter)
35
+ @screenshot_counter += 1
36
+ end
35
37
 
36
- def full_name(name)
37
- File.join group_parts.<<(name).map(&:to_s)
38
+ File.join(*group_parts.push(name.to_s))
38
39
  end
39
40
 
40
41
  def screenshot_dir
41
- File.join [Screenshot.screenshot_area] + group_parts
42
- end
43
-
44
- def current_capybara_driver_class
45
- Capybara.current_session.driver.class
46
- end
47
-
48
- def selenium?
49
- current_capybara_driver_class <= Capybara::Selenium::Driver
42
+ File.join(*([Screenshot.screenshot_area] + group_parts))
50
43
  end
51
44
 
52
45
  def screenshot_section(name)
@@ -55,66 +48,67 @@ module Capybara
55
48
 
56
49
  def screenshot_group(name)
57
50
  @screenshot_group = name.to_s
58
- @screenshot_counter = 0
51
+ @screenshot_counter = @screenshot_group.present? ? 0 : nil
59
52
  return unless Screenshot.active? && name.present?
60
53
 
61
54
  FileUtils.rm_rf screenshot_dir
62
55
  end
63
56
 
64
- # @return [Boolean] whether a screenshot was taken
65
- def screenshot(name, options = {})
57
+ def schedule_match_job(job)
58
+ (@test_screenshots ||= []) << job
59
+ true
60
+ end
61
+
62
+ def group_parts
63
+ parts = []
64
+ parts << @screenshot_section if @screenshot_section.present?
65
+ parts << @screenshot_group if @screenshot_group.present?
66
+ parts
67
+ end
68
+
69
+ def screenshot(name, skip_stack_frames: 0, **options)
66
70
  return false unless Screenshot.active?
67
- return false if window_size_is_wrong?
68
71
 
69
- driver_options = Diff.default_options.merge(options)
72
+ screenshot_full_name = build_full_name(name)
73
+ job = build_screenshot_matches_job(screenshot_full_name, options)
70
74
 
71
- stability_time_limit = driver_options[:stability_time_limit]
72
- wait = driver_options[:wait]
73
- crop = driver_options.delete(:crop)
75
+ return false unless job
74
76
 
75
- # Allow nil or single or multiple areas
76
- if driver_options[:skip_area]
77
- driver_options[:skip_area] = driver_options[:skip_area].compact.flatten&.each_slice(4)&.to_a
78
- end
77
+ job.prepend(caller[skip_stack_frames])
79
78
 
80
- if @screenshot_counter
81
- name = "#{format("%02i", @screenshot_counter)}_#{name}"
82
- @screenshot_counter += 1
83
- end
84
- name = full_name(name)
85
- file_name = "#{Screenshot.screenshot_area_abs}/#{name}.png"
86
-
87
- FileUtils.mkdir_p File.dirname(file_name)
88
- comparison = ImageCompare.new(file_name, nil, driver_options)
89
- checkout_vcs(name, comparison)
90
- begin
91
- blurred_input = prepare_page_for_screenshot(timeout: wait)
92
- if stability_time_limit
93
- take_stable_screenshot(comparison, stability_time_limit: stability_time_limit, wait: wait, crop: crop)
94
- else
95
- take_right_size_screenshot(comparison, crop: crop)
79
+ if Screenshot::Diff.delayed
80
+ schedule_match_job(job)
81
+ else
82
+ error_msg = assert_image_not_changed(*job)
83
+ if error_msg
84
+ error = ASSERTION.new(error_msg)
85
+ error.set_backtrace(caller(2))
86
+ raise error
96
87
  end
97
- ensure
98
- blurred_input&.click
99
88
  end
89
+ end
100
90
 
101
- return false unless comparison.old_file_exists?
91
+ def assert_image_not_changed(caller, name, comparison)
92
+ result = comparison.different?
102
93
 
103
- (@test_screenshots ||= []) << [caller(1..1).first, name, comparison]
94
+ # Cleanup after comparisons
95
+ if !result && comparison.base_image_path.exist?
96
+ FileUtils.mv(comparison.base_image_path, comparison.image_path, force: true)
97
+ else
98
+ FileUtils.rm_rf(comparison.base_image_path)
99
+ end
104
100
 
105
- true
106
- end
101
+ return unless result
107
102
 
108
- def window_size_is_wrong?
109
- selenium? && Screenshot.window_size &&
110
- page.driver.browser.manage.window.size !=
111
- ::Selenium::WebDriver::Dimension.new(*Screenshot.window_size)
103
+ "Screenshot does not match for '#{name}' #{comparison.error_message}\n#{caller}"
112
104
  end
113
105
 
114
- def assert_image_not_changed(caller, name, comparison)
115
- return unless comparison.different?
106
+ private
116
107
 
117
- "Screenshot does not match for '#{name}' #{comparison.error_message}\nat #{caller}"
108
+ def build_screenshot_matches_job(screenshot_full_name, options)
109
+ ScreenshotMatcher
110
+ .new(screenshot_full_name, options)
111
+ .build_screenshot_matches_job
118
112
  end
119
113
  end
120
114
  end
@@ -1,41 +1,68 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "os"
4
+
4
5
  module Capybara
5
6
  module Screenshot
6
7
  module Diff
7
8
  module Vcs
8
9
  SILENCE_ERRORS = Os::ON_WINDOWS ? "2>nul" : "2>/dev/null"
9
10
 
10
- def restore_git_revision(name, target_file_name)
11
- redirect_target = "#{target_file_name} #{SILENCE_ERRORS}"
12
- show_command = "git show HEAD~0:./#{Capybara::Screenshot.screenshot_area}/#{name}.png"
13
- if Capybara::Screenshot.use_lfs
14
- `#{show_command} | git lfs smudge > #{redirect_target}`
11
+ def self.restore_git_revision(screenshot_path, checkout_path)
12
+ vcs_file_path = screenshot_path.relative_path_from(Screenshot.root)
13
+
14
+ redirect_target = "#{checkout_path} #{SILENCE_ERRORS}"
15
+ show_command = "git show HEAD~0:./#{vcs_file_path}"
16
+ if Screenshot.use_lfs
17
+ `#{show_command} > #{checkout_path}.tmp #{SILENCE_ERRORS}`
18
+ if $CHILD_STATUS == 0
19
+ `git lfs smudge < #{checkout_path}.tmp > #{redirect_target}`
20
+ end
21
+ File.delete "#{checkout_path}.tmp"
15
22
  else
16
23
  `#{show_command} > #{redirect_target}`
17
24
  end
18
- FileUtils.rm_f(target_file_name) unless $CHILD_STATUS == 0
25
+
26
+ if $CHILD_STATUS != 0
27
+ FileUtils.rm_f(checkout_path)
28
+ false
29
+ else
30
+ true
31
+ end
19
32
  end
20
33
 
21
- def checkout_vcs(name, comparison)
22
- svn_file_name = "#{Capybara::Screenshot.screenshot_area_abs}/.svn/text-base/#{name}.png.svn-base"
23
- if File.exist?(svn_file_name)
24
- committed_file_name = svn_file_name
25
- FileUtils.cp committed_file_name, comparison.old_file_name
34
+ def self.checkout_vcs(screenshot_path, checkout_path)
35
+ if svn?
36
+ restore_svn_revision(screenshot_path, checkout_path)
26
37
  else
27
- svn_info = `svn info #{comparison.new_file_name} #{SILENCE_ERRORS}`
28
- if svn_info.present?
29
- wc_root = svn_info.slice(/(?<=Working Copy Root Path: ).*$/)
30
- checksum = svn_info.slice(/(?<=Checksum: ).*$/)
31
- if checksum
32
- committed_file_name = "#{wc_root}/.svn/pristine/#{checksum[0..1]}/#{checksum}.svn-base"
33
- FileUtils.cp committed_file_name, comparison.old_file_name
34
- end
35
- else
36
- restore_git_revision(name, comparison.old_file_name)
38
+ restore_git_revision(screenshot_path, checkout_path)
39
+ end
40
+ end
41
+
42
+ def self.restore_svn_revision(screenshot_path, checkout_path)
43
+ committed_file_name = screenshot_path + "../.svn/text-base/" + "#{screenshot_path.basename}.svn-base"
44
+ if committed_file_name.exist?
45
+ FileUtils.cp(committed_file_name, checkout_path)
46
+ return true
47
+ end
48
+
49
+ svn_info = `svn info #{screenshot_path} #{SILENCE_ERRORS}`
50
+ if svn_info.present?
51
+ wc_root = svn_info.slice(/(?<=Working Copy Root Path: ).*$/)
52
+ checksum = svn_info.slice(/(?<=Checksum: ).*$/)
53
+
54
+ if checksum
55
+ committed_file_name = "#{wc_root}/.svn/pristine/#{checksum[0..1]}/#{checksum}.svn-base"
56
+ FileUtils.cp(committed_file_name, checkout_path)
57
+ return true
37
58
  end
38
59
  end
60
+
61
+ false
62
+ end
63
+
64
+ def self.svn?
65
+ (Screenshot.screenshot_area_abs / ".svn").exist?
39
66
  end
40
67
  end
41
68
  end
@@ -3,7 +3,7 @@
3
3
  module Capybara
4
4
  module Screenshot
5
5
  module Diff
6
- VERSION = "1.6.2"
6
+ VERSION = "1.8.3"
7
7
  end
8
8
  end
9
9
  end
@@ -5,10 +5,10 @@ require "capybara/screenshot/diff/version"
5
5
  require "capybara/screenshot/diff/drivers/utils"
6
6
  require "capybara/screenshot/diff/image_compare"
7
7
  require "capybara/screenshot/diff/test_methods"
8
+ require "capybara/screenshot/diff/screenshoter"
8
9
 
9
10
  module Capybara
10
11
  module Screenshot
11
- extend Os
12
12
  mattr_accessor :add_driver_path
13
13
  mattr_accessor :add_os_path
14
14
  mattr_accessor :blur_active_element
@@ -30,10 +30,10 @@ module Capybara
30
30
  end
31
31
 
32
32
  def screenshot_area
33
- parts = [Capybara::Screenshot.save_path]
34
- parts << Capybara.current_driver.to_s if Capybara::Screenshot.add_driver_path
35
- parts << os_name if Capybara::Screenshot.add_os_path
36
- File.join parts
33
+ parts = [Screenshot.save_path]
34
+ parts << Capybara.current_driver.to_s if Screenshot.add_driver_path
35
+ parts << Os.name if Screenshot.add_os_path
36
+ File.join(*parts)
37
37
  end
38
38
 
39
39
  def screenshot_area_abs
@@ -44,23 +44,20 @@ module Capybara
44
44
  # Module to track screen shot changes
45
45
  module Diff
46
46
  include Capybara::DSL
47
- include Capybara::Screenshot::Os
48
47
 
48
+ mattr_accessor(:delayed) { true }
49
49
  mattr_accessor :area_size_limit
50
50
  mattr_accessor :color_distance_limit
51
51
  mattr_accessor(:enabled) { true }
52
52
  mattr_accessor :shift_distance_limit
53
53
  mattr_accessor :skip_area
54
54
  mattr_accessor(:driver) { :auto }
55
- mattr_accessor(:tolerance) { 0.001 }
55
+ mattr_accessor :tolerance
56
+
57
+ mattr_accessor(:screenshoter) { Screenshoter }
56
58
 
57
59
  AVAILABLE_DRIVERS = Utils.detect_available_drivers.freeze
58
- begin
59
- require "minitest"
60
- ASSERTION = Minitest::Assertion
61
- rescue
62
- ASSERTION = RuntimeError
63
- end
60
+ ASSERTION = Utils.detect_test_framework_assert
64
61
 
65
62
  def self.default_options
66
63
  {
@@ -70,7 +67,7 @@ module Capybara
70
67
  shift_distance_limit: shift_distance_limit,
71
68
  skip_area: skip_area,
72
69
  stability_time_limit: Screenshot.stability_time_limit,
73
- tolerance: tolerance,
70
+ tolerance: tolerance || ((driver == :vips) ? 0.001 : nil),
74
71
  wait: Capybara.default_max_wait_time
75
72
  }
76
73
  end
@@ -78,30 +75,36 @@ module Capybara
78
75
  def self.included(klass)
79
76
  klass.include TestMethods
80
77
  klass.setup do
81
- if Capybara::Screenshot.window_size
82
- if page.driver.respond_to?(:resize)
83
- page.driver.resize(*Capybara::Screenshot.window_size)
84
- elsif selenium?
85
- page.driver.browser.manage.window.resize_to(*Capybara::Screenshot.window_size)
86
- end
87
- end
78
+ BrowserHelpers.resize_to(Screenshot.window_size) if Screenshot.window_size
88
79
  end
89
80
 
90
81
  klass.teardown do
91
- if Capybara::Screenshot.active? && @test_screenshots
92
- test_screenshot_errors = @test_screenshots
93
- .map { |caller, name, compare| assert_image_not_changed(caller, name, compare) }
94
- @test_screenshots = nil # release the comparison objects from memory
95
- test_screenshot_errors.compact!
96
- if test_screenshot_errors.any?
97
- e = ASSERTION.new(test_screenshot_errors.join("\n\n"))
98
- e.set_backtrace(caller)
99
- if defined?(failures)
100
- failures << e
101
- else
102
- raise e
103
- end
104
- end
82
+ if Screenshot.active? && @test_screenshots.present?
83
+ track_failures(@test_screenshots)
84
+ @test_screenshots.clear
85
+ end
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ EMPTY_LINE = "\n\n"
92
+
93
+ def track_failures(screenshots)
94
+ test_screenshot_errors = screenshots.map do |caller, name, compare|
95
+ assert_image_not_changed(caller, name, compare)
96
+ end
97
+
98
+ test_screenshot_errors.compact!
99
+
100
+ unless test_screenshot_errors.empty?
101
+ error = ASSERTION.new(test_screenshot_errors.join(EMPTY_LINE))
102
+ error.set_backtrace([])
103
+
104
+ if is_a?(::Minitest::Runnable)
105
+ failures << error
106
+ else
107
+ raise error
105
108
  end
106
109
  end
107
110
  end
@@ -0,0 +1,28 @@
1
+ module Capybara
2
+ module Screenshot
3
+ def self.root=: ((String | Pathname) path) -> Pathname
4
+
5
+ def self.root: -> Pathname
6
+
7
+ def self.active?: () -> boolish
8
+
9
+ def self.screenshot_area: () -> String
10
+
11
+ def self.screenshot_area_abs: () -> Pathname
12
+
13
+ # Module to track screen shot changes
14
+ module Diff
15
+ AVAILABLE_DRIVERS: Array[(:vips | :chunky_png)]
16
+
17
+ ASSERTION: (top | RuntimeError)
18
+
19
+ def self.default_options: () -> ScreenshotMatcher::input_options
20
+
21
+ def self.included: (top klass) -> void
22
+
23
+ private
24
+
25
+ def track_failures: (Array[untyped] screenshots) -> void
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,33 @@
1
+ module Capybara
2
+ module Screenshot
3
+ module Diff
4
+ class Difference
5
+ attr_reader comparison: ImageCompare::Comparison
6
+
7
+ def different?: () -> bool
8
+
9
+ def base_image: () -> top
10
+
11
+ def options: () -> Drivers::BaseDriver::options_entity
12
+
13
+ def tolerance: () -> Numeric?
14
+
15
+ def area_size_limit: () -> Numeric?
16
+
17
+ def blank?: () -> bool
18
+
19
+ def region_area_size: () -> Numeric
20
+
21
+ def ratio: () -> Numeric?
22
+
23
+ def to_h: () -> Hash[Symbol, untyped]
24
+
25
+ def coordinates: () -> Region::raw_region_entity
26
+
27
+ def inspect: () -> String
28
+
29
+ def tolerable?: () -> bool
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,63 @@
1
+ module Capybara
2
+ module Screenshot
3
+ module Diff
4
+ module Drivers
5
+ class BaseDriver[ImageEntity]
6
+
7
+ type images_entity[out T] = [T, T]
8
+ type dimension_entity = [Numeric, Numeric]
9
+
10
+ type options_entity = {
11
+ area_size_limit?: Numeric?,
12
+ color_distance_limit?: Numeric?,
13
+ driver: (:auto | :vips | :chunky_png | VipsDriver | ChunkyPNGDriver)?,
14
+ dimensions: dimension_entity?,
15
+ median_filter_window_size: Numeric?,
16
+ shift_distance_limit?: Numeric?,
17
+ skip_area?: Array[Region]?,
18
+ stability_time_limit?: Numeric?,
19
+ tolerance?: Numeric?,
20
+ wait?: Numeric?
21
+ }
22
+
23
+ type color = [Integer, Integer, Integer, Integer]
24
+
25
+ def find_difference_region: (ImageEntity new_image, ImageEntity old_image, Numeric color_distance_limit, Numeric _shift_distance_limit, Integer _area_size_limit, ?fast_fail: bool) -> Difference
26
+
27
+ def inscribed?: (dimension_entity dimensions, ImageEntity i) -> boolish
28
+
29
+ def crop: (Region region, ImageEntity i) -> ImageEntity
30
+
31
+ def filter_image_with_median: (ImageEntity image, Numeric median_filter_window_size) -> ImageEntity
32
+
33
+ def add_black_box: (ImageEntity memo, Region region) -> void
34
+
35
+ def difference_level: (ImageEntity diff_mask, ImageEntity old_img, Region _region) -> Float
36
+
37
+ def image_area_size: (ImageEntity old_img) -> Integer
38
+
39
+ def height_for: (ImageEntity image) -> Integer
40
+
41
+ def width_for: (ImageEntity image) -> Integer
42
+
43
+ # Vips could not work with the same file. Per each process we require to create new file
44
+ def save_image_to: (ImageEntity image, String filename) -> void
45
+
46
+ def resize_image_to: (ImageEntity image, Integer new_width, Integer new_height) -> ImageEntity
47
+
48
+ def load_images: (String old_file_name, String new_file_name) -> images_entity[ImageEntity]
49
+
50
+ def from_file: (TestMethods::path_entity filename) -> ImageEntity
51
+
52
+ def dimension: (ImageEntity image) -> dimension_entity
53
+
54
+ def draw_rectangles: (images_entity[ImageEntity] images, Region region, color rgba, Integer offset) -> void
55
+
56
+ def same_pixels?: () -> boolish
57
+ end
58
+
59
+ def self.for: (ScreenshotMatcher::input_options) -> (VipsDriver | ChunkyPNGDriver)
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,36 @@
1
+ module Capybara
2
+ class Result
3
+ def map: () -> Result
4
+ end
5
+ module Screenshot
6
+ module BrowserHelpers
7
+ def self.current_capybara_driver_class: () -> top
8
+
9
+ def self.selenium?: () -> boolish?
10
+
11
+ def self.window_size_is_wrong?: () -> boolish
12
+
13
+ def self.bounds_for_css: (*String css_selectors) -> Array[Region::raw_region_entity]
14
+
15
+ IMAGE_WAIT_SCRIPT: String
16
+
17
+ def self.pending_image_to_load: () -> top?
18
+
19
+ HIDE_CARET_SCRIPT: String
20
+
21
+ def self.hide_caret: () -> void
22
+
23
+ FIND_ACTIVE_ELEMENT_SCRIPT: String
24
+
25
+ type capybara_element = top
26
+
27
+ def self.blur_from_focused_element: () -> capybara_element?
28
+
29
+ GET_BOUNDING_CLIENT_RECT_SCRIPT: String
30
+
31
+ def self.all_visible_regions_for: (String selector) -> Array[Region::raw_region_entity]
32
+
33
+ def self.region_for: (Result element) -> Region::raw_region_entity
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,89 @@
1
+ module ChunkyPNG
2
+ class Canvas
3
+ end
4
+
5
+ class Image
6
+ def self.from_blob: (String str) -> Image
7
+
8
+ def self.from_file: (String filename) -> Image
9
+ end
10
+ end
11
+
12
+ module Capybara
13
+ module Screenshot
14
+ module Diff
15
+ module Drivers
16
+ class ChunkyPNGDriver < BaseDriver[ChunkyPNG::Canvas]
17
+
18
+ def _load_images: (String old_file, String new_file) -> [ChunkyPNG::Image, ChunkyPNG::Image]
19
+
20
+ class DifferenceRegionFinder
21
+ def find_diff_rectangle: (
22
+ ChunkyPNG::Image org_img,
23
+ ChunkyPNG::Image new_img,
24
+ (Region | Region::raw_region_entity) area_coordinates,
25
+ cache: ImageCompare::cache_entity
26
+ ) -> Region?
27
+
28
+ def find_top: (
29
+ ChunkyPNG::Image old_img,
30
+ ChunkyPNG::Image new_img,
31
+ cache: ImageCompare::cache_entity
32
+ ) -> Region::raw_region_entity?
33
+
34
+ def find_left_right_and_top: (
35
+ ChunkyPNG::Image old_img,
36
+ ChunkyPNG::Image new_img,
37
+ (Region | Region::raw_region_entity) region,
38
+ cache: ImageCompare::cache_entity
39
+ ) -> Region::raw_region_entity
40
+
41
+ def find_bottom: (
42
+ ChunkyPNG::Image old_img,
43
+ ChunkyPNG::Image new_img,
44
+ Integer left,
45
+ Integer right,
46
+ Integer bottom,
47
+ cache: ImageCompare::cache_entity
48
+ ) -> Integer
49
+
50
+ def same_color?: (
51
+ ChunkyPNG::Image old_img,
52
+ ChunkyPNG::Image new_img,
53
+ Integer x,
54
+ Integer y,
55
+ ?cache: ImageCompare::cache_entity
56
+ ) -> boolish
57
+
58
+ def skipped_region?: (Integer x, Integer y) -> boolish
59
+
60
+ def color_distance_at: (
61
+ ChunkyPNG::Image new_img,
62
+ ChunkyPNG::Image old_img,
63
+ Integer x,
64
+ Integer y,
65
+ shift_distance_limit: Numeric?
66
+ ) -> Float
67
+
68
+ def shift_distance_at: (
69
+ ChunkyPNG::Image new_img,
70
+ ChunkyPNG::Image old_img,
71
+ Integer x,
72
+ Integer y,
73
+ color_distance_limit: Numeric?
74
+ ) -> Numeric
75
+
76
+ def color_matches: (
77
+ ChunkyPNG::Image new_img,
78
+ Integer org_color,
79
+ Integer x,
80
+ Integer y,
81
+ Numeric? color_distance_limit
82
+ ) -> boolish
83
+ end
84
+
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,13 @@
1
+ module Capybara
2
+ module Screenshot
3
+ module Diff
4
+ module Utils
5
+ def self.detect_available_drivers: () -> Array[(:vips | :chunky_png)]
6
+
7
+ def self.find_driver_class_for: [T] (Symbol driver) -> T
8
+
9
+ def self.detect_test_framework_assert: [T < Exception] () -> T
10
+ end
11
+ end
12
+ end
13
+ end