capybara-screenshot-diff 1.7.0 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +29 -0
  3. data/capybara-screenshot-diff.gemspec +7 -4
  4. data/gems.rb +8 -2
  5. data/lib/capybara/screenshot/diff/browser_helpers.rb +29 -28
  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 +188 -260
  10. data/lib/capybara/screenshot/diff/drivers/utils.rb +16 -0
  11. data/lib/capybara/screenshot/diff/drivers/vips_driver.rb +53 -103
  12. data/lib/capybara/screenshot/diff/drivers.rb +16 -0
  13. data/lib/capybara/screenshot/diff/image_compare.rb +125 -154
  14. data/lib/capybara/screenshot/diff/os.rb +1 -1
  15. data/lib/capybara/screenshot/diff/screenshot_matcher.rb +128 -0
  16. data/lib/capybara/screenshot/diff/screenshoter.rb +137 -0
  17. data/lib/capybara/screenshot/diff/stabilization.rb +0 -184
  18. data/lib/capybara/screenshot/diff/stable_screenshoter.rb +106 -0
  19. data/lib/capybara/screenshot/diff/test_methods.rb +51 -90
  20. data/lib/capybara/screenshot/diff/vcs.rb +44 -22
  21. data/lib/capybara/screenshot/diff/version.rb +1 -1
  22. data/lib/capybara/screenshot/diff.rb +13 -17
  23. data/sig/capybara/screenshot/diff/diff.rbs +28 -0
  24. data/sig/capybara/screenshot/diff/difference.rbs +33 -0
  25. data/sig/capybara/screenshot/diff/drivers/base_driver.rbs +63 -0
  26. data/sig/capybara/screenshot/diff/drivers/browser_helpers.rbs +36 -0
  27. data/sig/capybara/screenshot/diff/drivers/chunky_png_driver.rbs +89 -0
  28. data/sig/capybara/screenshot/diff/drivers/utils.rbs +13 -0
  29. data/sig/capybara/screenshot/diff/drivers/vips_driver.rbs +25 -0
  30. data/sig/capybara/screenshot/diff/image_compare.rbs +93 -0
  31. data/sig/capybara/screenshot/diff/os.rbs +11 -0
  32. data/sig/capybara/screenshot/diff/region.rbs +43 -0
  33. data/sig/capybara/screenshot/diff/screenshot_matcher.rbs +60 -0
  34. data/sig/capybara/screenshot/diff/screenshoter.rbs +48 -0
  35. data/sig/capybara/screenshot/diff/stable_screenshoter.rbs +29 -0
  36. data/sig/capybara/screenshot/diff/test_methods.rbs +39 -0
  37. data/sig/capybara/screenshot/diff/vcs.rbs +17 -0
  38. metadata +36 -27
  39. data/.gitattributes +0 -4
  40. data/.github/dependabot.yml +0 -8
  41. data/.github/workflows/lint.yml +0 -25
  42. data/.github/workflows/test.yml +0 -138
  43. data/.gitignore +0 -14
  44. data/.standard.yml +0 -12
  45. data/CONTRIBUTING.md +0 -24
  46. data/Dockerfile +0 -59
  47. data/README.md +0 -567
  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/rails60_gems.rb +0 -8
  55. data/gemfiles/rails61_gems.rb +0 -7
  56. data/gemfiles/rails70_gems.rb +0 -7
  57. data/tmp/.keep +0 -0
@@ -5,43 +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"
11
13
  require_relative "browser_helpers"
12
14
  require_relative "region"
13
15
 
16
+ require_relative "screenshot_matcher"
17
+
14
18
  # Add the `screenshot` method to ActionDispatch::IntegrationTest
15
19
  module Capybara
16
20
  module Screenshot
17
21
  module Diff
18
22
  module TestMethods
19
- include Stabilization
20
- include Vcs
21
- include BrowserHelpers
22
-
23
23
  def initialize(*)
24
24
  super
25
25
  @screenshot_counter = nil
26
26
  @screenshot_group = nil
27
27
  @screenshot_section = nil
28
28
  @test_screenshot_errors = nil
29
- @test_screenshots = nil
29
+ @test_screenshots = []
30
30
  end
31
31
 
32
- def group_parts
33
- parts = []
34
- parts << @screenshot_section if @screenshot_section.present?
35
- parts << @screenshot_group if @screenshot_group.present?
36
- parts
37
- end
32
+ def build_full_name(name)
33
+ if @screenshot_counter
34
+ name = format("%02i_#{name}", @screenshot_counter)
35
+ @screenshot_counter += 1
36
+ end
38
37
 
39
- def full_name(name)
40
- File.join group_parts.push(name).map(&:to_s)
38
+ File.join(*group_parts.push(name.to_s))
41
39
  end
42
40
 
43
41
  def screenshot_dir
44
- File.join [Screenshot.screenshot_area] + group_parts
42
+ File.join(*([Screenshot.screenshot_area] + group_parts))
45
43
  end
46
44
 
47
45
  def screenshot_section(name)
@@ -50,104 +48,67 @@ module Capybara
50
48
 
51
49
  def screenshot_group(name)
52
50
  @screenshot_group = name.to_s
53
- @screenshot_counter = 0
51
+ @screenshot_counter = @screenshot_group.present? ? 0 : nil
54
52
  return unless Screenshot.active? && name.present?
55
53
 
56
54
  FileUtils.rm_rf screenshot_dir
57
55
  end
58
56
 
59
- # @return [Boolean] whether a screenshot was taken
60
- def screenshot(name, skip_stack_frames: 0, **options)
61
- return false unless Screenshot.active?
62
- return false if window_size_is_wrong?
63
-
64
- driver_options = Diff.default_options.merge(options)
65
-
66
- stability_time_limit = driver_options[:stability_time_limit]
67
- wait = driver_options[:wait]
68
- crop = calculate_crop_region(driver_options)
69
-
70
- if @screenshot_counter
71
- name = "#{format("%02i", @screenshot_counter)}_#{name}"
72
- @screenshot_counter += 1
73
- end
74
- name = full_name(name)
75
- file_name = "#{Screenshot.screenshot_area_abs}/#{name}.png"
76
-
77
- create_output_directory_for(file_name)
78
-
79
- comparison = ImageCompare.new(file_name, nil, driver_options)
80
- checkout_vcs(name, comparison.old_file_name, comparison.new_file_name)
81
- take_screenshot(comparison, crop, stability_time_limit, wait)
82
-
83
- return false unless comparison.old_file_exists?
84
-
85
- # Allow nil or single or multiple areas
86
- if driver_options[:skip_area]
87
- comparison.skip_area = calculate_skip_area(driver_options[:skip_area], crop)
88
- end
89
-
90
- (@test_screenshots ||= []) << [caller[skip_stack_frames], name, comparison]
91
-
57
+ def schedule_match_job(job)
58
+ (@test_screenshots ||= []) << job
92
59
  true
93
60
  end
94
61
 
95
- def assert_image_not_changed(caller, name, comparison)
96
- return unless comparison.different?
97
-
98
- "Screenshot does not match for '#{name}' #{comparison.error_message}\nat #{caller}"
62
+ def group_parts
63
+ parts = []
64
+ parts << @screenshot_section if @screenshot_section.present?
65
+ parts << @screenshot_group if @screenshot_group.present?
66
+ parts
99
67
  end
100
68
 
101
- private
69
+ def screenshot(name, skip_stack_frames: 0, **options)
70
+ return false unless Screenshot.active?
102
71
 
103
- def calculate_crop_region(driver_options)
104
- crop_coordinates = driver_options.delete(:crop)
105
- return nil unless crop_coordinates
72
+ screenshot_full_name = build_full_name(name)
73
+ job = build_screenshot_matches_job(screenshot_full_name, options)
106
74
 
107
- crop_coordinates = bounds_for_css(crop_coordinates).first if crop_coordinates.is_a?(String)
108
- Region.from_edge_coordinates(*crop_coordinates)
109
- end
75
+ return false unless job
110
76
 
111
- def create_output_directory_for(file_name)
112
- FileUtils.mkdir_p File.dirname(file_name)
113
- end
77
+ test_caller = caller(skip_stack_frames)
114
78
 
115
- def take_screenshot(comparison, crop, stability_time_limit, wait)
116
- blurred_input = prepare_page_for_screenshot(timeout: wait)
117
- if stability_time_limit
118
- take_stable_screenshot(
119
- comparison,
120
- crop: crop,
121
- stability_time_limit: stability_time_limit,
122
- wait: wait
123
- )
79
+ if Screenshot::Diff.delayed
80
+ schedule_match_job([test_caller] + job)
124
81
  else
125
- take_right_size_screenshot(comparison, crop: crop)
82
+ error_msg = assert_image_not_changed(job.first, job.last)
83
+ if error_msg
84
+ error = ASSERTION.new(error_msg)
85
+ error.set_backtrace(caller(2))
86
+ raise error
87
+ end
126
88
  end
127
- ensure
128
- blurred_input&.click
129
89
  end
130
90
 
131
- def calculate_skip_area(skip_area, crop)
132
- crop_region = crop && Region.new(*crop)
133
- skip_area = Array(skip_area)
134
-
135
- css_selectors, regions = skip_area.compact.partition { |region| region.is_a? String }
91
+ def assert_image_not_changed(name, comparison)
92
+ result = comparison.different?
136
93
 
137
- result = []
138
- result.concat(build_regions_for(bounds_for_css(*css_selectors))) unless css_selectors.empty?
139
- result.concat(build_regions_for(regions.flatten&.each_slice(4))) unless regions.empty?
140
- result.compact!
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
141
100
 
142
- result.map! { |region| crop_region.find_relative_intersect(region) } if crop_region
101
+ return unless result
143
102
 
144
- result
103
+ "Screenshot does not match for '#{name}' #{comparison.error_message}"
145
104
  end
146
105
 
147
- def build_regions_for(coordinates)
148
- coordinates.map do |region_coordinates|
149
- Region.from_edge_coordinates(*region_coordinates)
150
- end
106
+ private
107
+
108
+ def build_screenshot_matches_job(screenshot_full_name, options)
109
+ ScreenshotMatcher
110
+ .new(screenshot_full_name, options)
111
+ .build_screenshot_matches_job
151
112
  end
152
113
  end
153
114
  end
@@ -1,42 +1,64 @@
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} | git lfs smudge > #{redirect_target} ; exit ${PIPESTATUS[0]}`
15
18
  else
16
19
  `#{show_command} > #{redirect_target}`
17
20
  end
18
- FileUtils.rm_f(target_file_name) unless $CHILD_STATUS == 0
19
- end
20
21
 
21
- def checkout_vcs(name, old_file_name, new_file_name)
22
- svn_file_name = "#{Capybara::Screenshot.screenshot_area_abs}/.svn/text-base/#{name}.png.svn-base"
22
+ if $CHILD_STATUS != 0
23
+ FileUtils.rm_f(checkout_path)
24
+ false
25
+ else
26
+ true
27
+ end
28
+ end
23
29
 
24
- if File.exist?(svn_file_name)
25
- committed_file_name = svn_file_name
26
- FileUtils.cp committed_file_name, old_file_name
30
+ def self.checkout_vcs(screenshot_path, checkout_path)
31
+ if svn?
32
+ restore_svn_revision(screenshot_path, checkout_path)
27
33
  else
28
- svn_info = `svn info #{new_file_name} #{SILENCE_ERRORS}`
29
- if svn_info.present?
30
- wc_root = svn_info.slice(/(?<=Working Copy Root Path: ).*$/)
31
- checksum = svn_info.slice(/(?<=Checksum: ).*$/)
32
- if checksum
33
- committed_file_name = "#{wc_root}/.svn/pristine/#{checksum[0..1]}/#{checksum}.svn-base"
34
- FileUtils.cp committed_file_name, old_file_name
35
- end
36
- else
37
- restore_git_revision(name, old_file_name)
34
+ restore_git_revision(screenshot_path, checkout_path)
35
+ end
36
+ end
37
+
38
+ def self.restore_svn_revision(screenshot_path, checkout_path)
39
+ committed_file_name = screenshot_path + "../.svn/text-base/" + "#{screenshot_path.basename}.svn-base"
40
+ if committed_file_name.exist?
41
+ FileUtils.cp(committed_file_name, checkout_path)
42
+ return true
43
+ end
44
+
45
+ svn_info = `svn info #{screenshot_path} #{SILENCE_ERRORS}`
46
+ if svn_info.present?
47
+ wc_root = svn_info.slice(/(?<=Working Copy Root Path: ).*$/)
48
+ checksum = svn_info.slice(/(?<=Checksum: ).*$/)
49
+
50
+ if checksum
51
+ committed_file_name = "#{wc_root}/.svn/pristine/#{checksum[0..1]}/#{checksum}.svn-base"
52
+ FileUtils.cp(committed_file_name, checkout_path)
53
+ return true
38
54
  end
39
55
  end
56
+
57
+ false
58
+ end
59
+
60
+ def self.svn?
61
+ (Screenshot.screenshot_area_abs / ".svn").exist?
40
62
  end
41
63
  end
42
64
  end
@@ -3,7 +3,7 @@
3
3
  module Capybara
4
4
  module Screenshot
5
5
  module Diff
6
- VERSION = "1.7.0"
6
+ VERSION = "1.8.0"
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,8 +44,8 @@ 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 }
@@ -54,6 +54,8 @@ module Capybara
54
54
  mattr_accessor(:driver) { :auto }
55
55
  mattr_accessor :tolerance
56
56
 
57
+ mattr_accessor(:screenshoter) { Screenshoter }
58
+
57
59
  AVAILABLE_DRIVERS = Utils.detect_available_drivers.freeze
58
60
  ASSERTION = Utils.detect_test_framework_assert
59
61
 
@@ -65,7 +67,7 @@ module Capybara
65
67
  shift_distance_limit: shift_distance_limit,
66
68
  skip_area: skip_area,
67
69
  stability_time_limit: Screenshot.stability_time_limit,
68
- tolerance: tolerance || (driver == :vips ? 0.001 : nil),
70
+ tolerance: tolerance || ((driver == :vips) ? 0.001 : nil),
69
71
  wait: Capybara.default_max_wait_time
70
72
  }
71
73
  end
@@ -73,19 +75,13 @@ module Capybara
73
75
  def self.included(klass)
74
76
  klass.include TestMethods
75
77
  klass.setup do
76
- if Capybara::Screenshot.window_size
77
- if page.driver.respond_to?(:resize)
78
- page.driver.resize(*Capybara::Screenshot.window_size)
79
- elsif selenium?
80
- page.driver.browser.manage.window.resize_to(*Capybara::Screenshot.window_size)
81
- end
82
- end
78
+ BrowserHelpers.resize_to(Screenshot.window_size) if Screenshot.window_size
83
79
  end
84
80
 
85
81
  klass.teardown do
86
- if Capybara::Screenshot.active? && @test_screenshots
82
+ if Screenshot.active? && @test_screenshots.present?
87
83
  track_failures(@test_screenshots, caller)
88
- @test_screenshots = nil
84
+ @test_screenshots.clear
89
85
  end
90
86
  end
91
87
  end
@@ -96,7 +92,7 @@ module Capybara
96
92
 
97
93
  def track_failures(screenshots, original_caller)
98
94
  test_screenshot_errors = screenshots.map do |caller, name, compare|
99
- assert_image_not_changed(caller, name, compare)
95
+ assert_image_not_changed(name, compare)
100
96
  end
101
97
 
102
98
  test_screenshot_errors.compact!
@@ -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, (Array[String] | String) original_caller) -> 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
@@ -0,0 +1,25 @@
1
+ module ::Vips
2
+ class Image
3
+ def self.new_from_file: (String filename) -> Image
4
+ end
5
+ end
6
+
7
+ module Capybara
8
+ module Screenshot
9
+ module Diff
10
+ module Drivers
11
+ class VipsDriver < BaseDriver[Vips::Image]
12
+ class VipsUtil
13
+ def self.difference_area: (Vips::Image old_image, Vips::Image new_image, ?color_distance: ::Integer) -> Numeric
14
+
15
+ def self.difference_area_size_by: (Vips::Image difference_mask) -> Numeric
16
+
17
+ def self.difference_mask: (Vips::Image, Vips::Image, ?Numeric? color_distance) -> Vips::Image
18
+
19
+ def self.difference_region_by: (Vips::Image diff_mask) -> Region?
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end