capybara-screenshot-diff 1.8.3 → 1.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -11
  3. data/capybara-screenshot-diff.gemspec +2 -3
  4. data/gems.rb +4 -4
  5. data/lib/capybara/screenshot/diff/area_calculator.rb +56 -0
  6. data/lib/capybara/screenshot/diff/browser_helpers.rb +5 -5
  7. data/lib/capybara/screenshot/diff/comparison.rb +6 -0
  8. data/lib/capybara/screenshot/diff/cucumber.rb +1 -9
  9. data/lib/capybara/screenshot/diff/difference.rb +8 -4
  10. data/lib/capybara/screenshot/diff/drivers/base_driver.rb +0 -5
  11. data/lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb +10 -5
  12. data/lib/capybara/screenshot/diff/drivers/vips_driver.rb +14 -3
  13. data/lib/capybara/screenshot/diff/drivers.rb +1 -1
  14. data/lib/capybara/screenshot/diff/image_compare.rb +80 -114
  15. data/lib/capybara/screenshot/diff/region.rb +28 -7
  16. data/lib/capybara/screenshot/diff/reporters/default.rb +117 -0
  17. data/lib/capybara/screenshot/diff/screenshot_matcher.rb +36 -74
  18. data/lib/capybara/screenshot/diff/screenshoter.rb +46 -54
  19. data/lib/capybara/screenshot/diff/stable_screenshoter.rb +65 -63
  20. data/lib/capybara/screenshot/diff/test_methods.rb +76 -9
  21. data/lib/capybara/screenshot/diff/{drivers/utils.rb → utils.rb} +0 -7
  22. data/lib/capybara/screenshot/diff/vcs.rb +25 -23
  23. data/lib/capybara/screenshot/diff/version.rb +1 -1
  24. data/lib/capybara/screenshot/diff.rb +1 -111
  25. data/lib/capybara-screenshot-diff.rb +1 -1
  26. data/lib/capybara_screenshot_diff/attempts_reporter.rb +49 -0
  27. data/lib/capybara_screenshot_diff/cucumber.rb +12 -0
  28. data/lib/capybara_screenshot_diff/dsl.rb +11 -0
  29. data/lib/capybara_screenshot_diff/minitest.rb +45 -0
  30. data/lib/capybara_screenshot_diff/rspec.rb +32 -0
  31. data/lib/capybara_screenshot_diff/snap.rb +55 -0
  32. data/lib/capybara_screenshot_diff/snap_manager.rb +76 -0
  33. data/lib/capybara_screenshot_diff.rb +86 -0
  34. metadata +20 -39
  35. data/lib/capybara/screenshot/diff/stabilization.rb +0 -0
  36. data/sig/capybara/screenshot/diff/diff.rbs +0 -28
  37. data/sig/capybara/screenshot/diff/difference.rbs +0 -33
  38. data/sig/capybara/screenshot/diff/drivers/base_driver.rbs +0 -63
  39. data/sig/capybara/screenshot/diff/drivers/browser_helpers.rbs +0 -36
  40. data/sig/capybara/screenshot/diff/drivers/chunky_png_driver.rbs +0 -89
  41. data/sig/capybara/screenshot/diff/drivers/utils.rbs +0 -13
  42. data/sig/capybara/screenshot/diff/drivers/vips_driver.rbs +0 -25
  43. data/sig/capybara/screenshot/diff/image_compare.rbs +0 -93
  44. data/sig/capybara/screenshot/diff/os.rbs +0 -11
  45. data/sig/capybara/screenshot/diff/region.rbs +0 -43
  46. data/sig/capybara/screenshot/diff/screenshot_matcher.rbs +0 -60
  47. data/sig/capybara/screenshot/diff/screenshoter.rbs +0 -48
  48. data/sig/capybara/screenshot/diff/stable_screenshoter.rbs +0 -29
  49. data/sig/capybara/screenshot/diff/test_methods.rbs +0 -39
  50. data/sig/capybara/screenshot/diff/vcs.rbs +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3e7e3bdfacb7c2fad912c72ade765c9ec6a50f306048a374d8056fc02c27d643
4
- data.tar.gz: c1527905de61a1d801c545729d68b811ba91db5902c1ff209b4dc212fa252971
3
+ metadata.gz: 87328c5152c18c006da12b39c30f0389ac0f7b71174530473681ac98e035b884
4
+ data.tar.gz: ec54883ea6de4b75fa5be917fee79e5098a18cd1c5ea3ebbfceb567661e148d3
5
5
  SHA512:
6
- metadata.gz: c9c7868fe6d13263db039cf2a7fbb85234abee8f3bc05dd51fc5cc78579a96c1de3f2275782f5c806e9b154472ad51a991cfad1847a54d4811460597a3a4b8fe
7
- data.tar.gz: 209f71e33b2b4bdead447293c0e286de6b6e1d945f4e12889e1b4f94675bb8bc035956f19a9897ceeedf278999598bb095991ecd34f2eaffa219d088ea19d577
6
+ metadata.gz: 7e5f89855ddaf008feab8705c6e13f495b654a7dd382fe209ac5e5917fca00553765dfcf411b90cd83aecd931fec6055502dcf3cf7ee0d69c21e8edaa891cbdc
7
+ data.tar.gz: 9f402d5ff1ed20d02c0e6fd3d7d025fc7e82287e7d159a6fb892c21cf0f8bf6db74257ba22560631ef179f191cce2de78f099b4a47b637a9d6f91eaededa7ddd
data/Rakefile CHANGED
@@ -17,23 +17,13 @@ Rake::TestTask.new("test:integration") do |t|
17
17
  t.test_files = FileList["test/integration/**/*_test.rb"]
18
18
  end
19
19
 
20
- Rake::TestTask.new("test:signatures") do |t|
21
- ENV["RBS_TEST_DOUBLE_SUITE"] ||= "minitest"
22
- ENV["RBS_TEST_TARGET"] ||= "Capybara::Screenshot::Diff::*"
23
- ENV["RBS_TEST_OPT"] ||= "-rset -rpathname -Isig"
24
-
25
- t.libs << "test"
26
- t.ruby_opts << "-r rbs/test/setup"
27
- t.test_files = FileList["test/**/*_test.rb"]
28
- end
29
-
30
20
  task "clobber" do
31
21
  puts "Cleanup tmp/*.png"
32
22
  FileUtils.rm_rf(Dir["./tmp/*"])
33
23
  end
34
24
 
35
25
  task "test:benchmark" do
36
- require_relative "./scripts/benchmark/find_region_benchmark"
26
+ require_relative "scripts/benchmark/find_region_benchmark"
37
27
  benchmark = Capybara::Screenshot::Diff::Drivers::FindRegionBenchmark.new
38
28
 
39
29
  puts "For Medium Screen Size: 800x600"
@@ -16,14 +16,13 @@ Gem::Specification.new do |spec|
16
16
  spec.license = "MIT"
17
17
  spec.metadata["allowed_push_host"] = "https://rubygems.org/"
18
18
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
19
- f.match(%r{(^(\.|tmp|bin|test|spec|features|gemfiles|scripts)/)|(^(\.|Dockerfile|CONTRIBUTING|README))})
19
+ f.match(%r{(^(\.|tmp|bin|test|spec|features|gemfiles|scripts|foo)/)|(^(\.|Dockerfile|CONTRIBUTING|README))})
20
20
  end
21
21
 
22
22
  spec.bindir = "exe"
23
23
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
24
  spec.require_paths = ["lib"]
25
25
 
26
- spec.add_runtime_dependency "actionpack", ">= 6.1", "< 8"
26
+ spec.add_runtime_dependency "actionpack", ">= 6.1", "< 9"
27
27
  spec.add_runtime_dependency "capybara", ">= 2", "< 4"
28
- spec.add_runtime_dependency "chunky_png", "~> 1.3"
29
28
  end
data/gems.rb CHANGED
@@ -8,13 +8,15 @@ gemspec path: __dir__
8
8
  gem "rake"
9
9
 
10
10
  # Image processing libraries
11
- gem "oily_png", platform: :ruby, git: "https://github.com/donv/oily_png", branch: "patch-2"
11
+ gem "chunky_png", ">= 1.3", require: false
12
+ gem "oily_png", platform: :ruby, git: "https://github.com/wvanbergen/oily_png", ref: "44042006e79efd42ce4b52c1d78a4c70f0b4b1b2"
12
13
  gem "ruby-vips", require: false
13
14
 
14
15
  # Test
15
16
  gem "minitest", require: false
16
17
  gem "minitest-stub-const", require: false
17
18
  gem "simplecov", require: false
19
+ gem "rspec", require: false
18
20
 
19
21
  # Capybara Server
20
22
  gem "puma", require: false
@@ -22,8 +24,7 @@ gem "rackup", require: false
22
24
 
23
25
  # Capybara Drivers
24
26
  gem "cuprite", require: false
25
- gem "selenium-webdriver", require: false
26
- gem "webdrivers", "~> 5.0", require: false
27
+ gem "selenium-webdriver", ">= 4.11", require: false
27
28
 
28
29
  # Test Frameworks
29
30
  # gem "cucumber", require: false
@@ -31,5 +32,4 @@ gem "webdrivers", "~> 5.0", require: false
31
32
 
32
33
  group :tools do
33
34
  gem "standard", require: false
34
- gem "rbs", require: false, platform: :ruby
35
35
  end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capybara
4
+ module Screenshot
5
+ module Diff
6
+ class AreaCalculator
7
+ def initialize(crop_coordinates, skip_area)
8
+ @crop_coordinates = crop_coordinates
9
+ @skip_area = skip_area
10
+ end
11
+
12
+ def calculate_crop
13
+ return @_calculated_crop if defined?(@_calculated_crop)
14
+ return @_calculated_crop = nil unless @crop_coordinates
15
+
16
+ # TODO: Move out from this class, this should be done on before screenshot and should not depend on Browser
17
+ @crop_coordinates = BrowserHelpers.bounds_for_css(@crop_coordinates).first if @crop_coordinates.is_a?(String)
18
+ @_calculated_crop = Region.from_edge_coordinates(*@crop_coordinates)
19
+ end
20
+
21
+ # Cast skip areas params into Region
22
+ # and if there is crop then makes absolute coordinates to eb relative to crop top left corner
23
+ def calculate_skip_area
24
+ return nil unless @skip_area
25
+
26
+ crop_region = calculate_crop
27
+ skip_area = Array(@skip_area)
28
+
29
+ css_selectors, coords_list = skip_area.compact.partition { |region| region.is_a? String }
30
+ regions, coords_list = coords_list.partition { |region| region.is_a? Region }
31
+
32
+ regions.concat(build_regions_for(BrowserHelpers.bounds_for_css(*css_selectors))) unless css_selectors.empty?
33
+ regions.concat(build_regions_for(coords_list.flatten.each_slice(4))) unless coords_list.empty?
34
+
35
+ regions.compact!
36
+
37
+ if crop_region
38
+ regions
39
+ .map! { |region| crop_region.find_relative_intersect(region) }
40
+ .filter! { |region| region&.present? }
41
+ end
42
+
43
+ regions
44
+ end
45
+
46
+ private
47
+
48
+ def build_regions_for(coordinates)
49
+ coordinates
50
+ .map { |entry| Region.from_edge_coordinates(*entry) }
51
+ .tap { |it| it.compact! }
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -30,13 +30,13 @@ module Capybara
30
30
 
31
31
  IMAGE_WAIT_SCRIPT = <<~JS
32
32
  function pending_image() {
33
- var images = document.images;
33
+ const images = document.images
34
34
  for (var i = 0; i < images.length; i++) {
35
- if (!images[i].complete) {
36
- return images[i].src;
35
+ if (!images[i].complete && images[i].loading !== "lazy") {
36
+ return images[i].src
37
37
  }
38
38
  }
39
- return false;
39
+ return false
40
40
  }(window)
41
41
  JS
42
42
 
@@ -83,7 +83,7 @@ module Capybara
83
83
  end
84
84
 
85
85
  def self.region_for(element)
86
- element.evaluate_script(GET_BOUNDING_CLIENT_RECT_SCRIPT).map { |point| point.negative? ? 0 : point.to_i }
86
+ element.evaluate_script(GET_BOUNDING_CLIENT_RECT_SCRIPT).map { |point| point.negative? ? 0 : point.ceil.to_i }
87
87
  end
88
88
 
89
89
  def self.session
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capybara::Screenshot::Diff
4
+ class Comparison < Struct.new(:new_image, :base_image, :options, :driver, :new_image_path, :base_image_path)
5
+ end
6
+ end
@@ -1,11 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "capybara/screenshot/diff"
4
- require "capybara/screenshot/diff/test_methods"
5
-
6
- World(Capybara::Screenshot::Diff::TestMethods)
7
-
8
- Before do
9
- Capybara::Screenshot::Diff.delayed = false
10
- Capybara::Screenshot::BrowserHelpers.resize_to(Capybara::Screenshot.window_size) if Capybara::Screenshot.window_size
11
- end
3
+ require "capybara_screenshot_diff/cucumber"
@@ -5,13 +5,17 @@ require "json"
5
5
  module Capybara
6
6
  module Screenshot
7
7
  module Diff
8
- class Difference < Struct.new(:region, :meta, :comparison)
8
+ class Difference < Struct.new(:region, :meta, :comparison, :failed_by)
9
9
  def different?
10
- !(blank? || tolerable?)
10
+ failed? || !(blank? || tolerable?)
11
11
  end
12
12
 
13
- def base_image
14
- comparison.base_image
13
+ def equal?
14
+ !different?
15
+ end
16
+
17
+ def failed?
18
+ !!failed_by
15
19
  end
16
20
 
17
21
  def options
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "chunky_png"
4
3
  require "capybara/screenshot/diff/difference"
5
4
 
6
5
  module Capybara
@@ -31,10 +30,6 @@ module Capybara
31
30
  def dimension(image)
32
31
  [width_for(image), height_for(image)]
33
32
  end
34
-
35
- def inscribed?(dimensions, i)
36
- width_for(i) < dimensions[0] || height_for(i) < dimensions[1]
37
- end
38
33
  end
39
34
  end
40
35
  end
@@ -1,6 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "chunky_png"
3
+ begin
4
+ require "chunky_png"
5
+ rescue LoadError => e
6
+ raise 'Required chunky_png gem is missing. Add `gem "chunky_png"` to Gemfile' if e.message.match?(/chunky_png/i)
7
+ raise
8
+ end
4
9
 
5
10
  require "capybara/screenshot/diff/drivers/base_driver"
6
11
 
@@ -35,12 +40,12 @@ module Capybara
35
40
  i.crop(*region.to_top_left_corner_coordinates)
36
41
  end
37
42
 
38
- def from_file(filename)
39
- ChunkyPNG::Image.from_file(filename.to_s)
43
+ def from_file(filename_or_path)
44
+ ChunkyPNG::Image.from_file(filename_or_path.to_s)
40
45
  end
41
46
 
42
47
  def save_image_to(image, filename)
43
- image.save(filename)
48
+ image.save(filename, :fast_rgba)
44
49
  end
45
50
 
46
51
  def resize_image_to(image, new_width, new_height)
@@ -48,7 +53,7 @@ module Capybara
48
53
  end
49
54
 
50
55
  def load_image_files(old_file_name, file_name)
51
- [File.binread(old_file_name), File.binread(file_name)]
56
+ [old_file_name.binread, file_name.binread]
52
57
  end
53
58
 
54
59
  def draw_rectangles(images, region, (r, g, b), offset: 0)
@@ -15,6 +15,8 @@ module Capybara
15
15
  # Compare two images and determine if they are equal, different, or within some comparison
16
16
  # range considering color values and difference area size.
17
17
  module Drivers
18
+ DEFAULT_HIGHLIGHT_COLOR = [255, 0, 0, 255].freeze
19
+
18
20
  class VipsDriver < BaseDriver
19
21
  def find_difference_region(comparison)
20
22
  new_image, base_image, options = comparison.new_image, comparison.base_image, comparison.options
@@ -26,9 +28,8 @@ module Capybara
26
28
  result = Difference.new(region, {}, comparison)
27
29
 
28
30
  unless result.blank?
29
- meta = {}
30
- meta[:difference_level] = difference_level(diff_mask, base_image) if comparison.options[:tolerance]
31
- result.meta = meta
31
+ result.meta[:difference_level] = difference_level(diff_mask, base_image) if comparison.options[:tolerance]
32
+ result.meta[:diff_mask] = diff_mask
32
33
  end
33
34
 
34
35
  result
@@ -49,6 +50,8 @@ module Capybara
49
50
  end
50
51
 
51
52
  def add_black_box(memo, region)
53
+ return memo unless region
54
+
52
55
  memo.draw_rect([0, 0, 0, 0], *region.to_top_left_corner_coordinates, fill: true)
53
56
  end
54
57
 
@@ -100,6 +103,14 @@ module Capybara
100
103
  (comparison.new_image == comparison.base_image).min == 255
101
104
  end
102
105
 
106
+ def merge(new_image, base_image)
107
+ base_image.composite2(new_image, :over)
108
+ end
109
+
110
+ def highlight_mask(diff_mask, merged_image, color: DEFAULT_HIGHLIGHT_COLOR)
111
+ diff_mask.ifthenelse(color, merged_image * 0.75)
112
+ end
113
+
103
114
  private
104
115
 
105
116
  def same_as?(region, base_image)
@@ -5,7 +5,7 @@ module Capybara
5
5
  module Diff
6
6
  module Drivers
7
7
  def self.for(driver_options = {})
8
- driver_option = driver_options.fetch(:driver, :chunky_png)
8
+ driver_option = driver_options.is_a?(Hash) ? driver_options.fetch(:driver, :chunky_png) : driver_options
9
9
  return driver_option unless driver_option.is_a?(Symbol)
10
10
 
11
11
  Utils.find_driver_class_for(driver_option).new
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "capybara/screenshot/diff/comparison"
4
+
3
5
  module Capybara
4
6
  module Screenshot
5
7
  module Diff
@@ -11,22 +13,13 @@ module Capybara
11
13
  TOLERABLE_OPTIONS = [:tolerance, :color_distance_limit, :shift_distance_limit, :area_size_limit].freeze
12
14
 
13
15
  attr_reader :driver, :driver_options
14
-
15
- attr_reader :annotated_image_path, :annotated_base_image_path,
16
- :image_path, :base_image_path,
17
- :new_file_name, :old_file_name
16
+ attr_reader :image_path, :base_image_path
17
+ attr_reader :difference, :error_message
18
18
 
19
19
  def initialize(image_path, base_image_path, options = {})
20
20
  @image_path = Pathname.new(image_path)
21
-
22
- @new_file_name = @image_path.to_s
23
- @annotated_image_path = @image_path.sub_ext(".diff.png")
24
-
25
21
  @base_image_path = Pathname.new(base_image_path)
26
22
 
27
- @old_file_name = @base_image_path.to_s
28
- @annotated_base_image_path = @base_image_path.sub_ext(".diff.png")
29
-
30
23
  @driver_options = options.dup
31
24
 
32
25
  @driver = Drivers.for(@driver_options)
@@ -35,104 +28,104 @@ module Capybara
35
28
  # Compare the two image files and return `true` or `false` as quickly as possible.
36
29
  # Return falsely if the old file does not exist or the image dimensions do not match.
37
30
  def quick_equal?
38
- @error_message = nil
39
- return false unless image_files_exist?
40
- # TODO: Confirm this change. There are screenshots with the same size, but there is a big difference
31
+ require_images_exists!
32
+
33
+ # NOTE: This is very fuzzy logic, but so far it's helps to support current performance.
41
34
  return true if new_file_size == old_file_size
42
35
 
43
36
  comparison = load_and_process_images
44
37
 
45
38
  unless driver.same_dimension?(comparison)
46
- @error_message = build_error_for_different_dimensions(comparison)
39
+ self.difference = build_failed_difference(comparison, {different_dimensions: true})
47
40
  return false
48
41
  end
49
42
 
50
- return true if driver.same_pixels?(comparison)
43
+ if driver.same_pixels?(comparison)
44
+ self.difference = build_no_difference(comparison)
45
+ return true
46
+ end
51
47
 
52
- # Could not make any difference to be tolerable, so skip and return as not equal
48
+ # NOTE: Could not make any difference to be tolerable, so skip and return as not equal.
53
49
  return false if without_tolerable_options?
54
50
 
55
- @difference = driver.find_difference_region(comparison)
56
- return true unless @difference.different?
51
+ self.difference = driver.find_difference_region(comparison)
57
52
 
58
- @error_message = @difference.inspect
59
- false
53
+ !difference.different?
60
54
  end
61
55
 
62
56
  # Compare the two image referenced by this object, and return `true` if they are different,
63
57
  # and `false` if they are the same.
64
58
  def different?
65
- @error_message = nil
59
+ processed.difference.different?
60
+ end
66
61
 
67
- @error_message = _different?
62
+ def reporter
63
+ @reporter ||= begin
64
+ current_difference = difference || build_no_difference(nil)
65
+ Capybara::Screenshot::Diff::Reporters::Default.new(current_difference)
66
+ end
67
+ end
68
68
 
69
- clean_tmp_files unless @error_message
69
+ def processed?
70
+ !!difference
71
+ end
70
72
 
71
- !@error_message.nil?
73
+ def processed
74
+ self.difference = find_difference unless processed?
75
+ @error_message ||= reporter.generate
76
+ self
72
77
  end
73
78
 
74
- def build_error_for_different_dimensions(comparison)
75
- change_msg = [comparison.base_image, comparison.new_image]
76
- .map { |i| driver.dimension(i).join("x") }
77
- .join(" => ")
79
+ private
78
80
 
79
- "Screenshot dimension has been changed for #{@new_file_name}: #{change_msg}"
81
+ def find_difference
82
+ require_images_exists!
83
+
84
+ comparison = load_and_process_images
85
+
86
+ unless driver.same_dimension?(comparison)
87
+ return build_failed_difference(comparison, {different_dimensions: true})
88
+ end
89
+
90
+ if driver.same_pixels?(comparison)
91
+ build_no_difference(comparison)
92
+ else
93
+ driver.find_difference_region(comparison)
94
+ end
80
95
  end
81
96
 
82
- def clean_tmp_files
83
- @annotated_base_image_path.unlink if @annotated_base_image_path.exist?
84
- @annotated_image_path.unlink if @annotated_image_path.exist?
97
+ def require_images_exists!
98
+ raise ArgumentError, "There is no original (base) screenshot version to compare, located: #{base_image_path}" unless base_image_path.exist?
99
+ raise ArgumentError, "There is no new screenshot version to compare, located: #{image_path}" unless image_path.exist?
85
100
  end
86
101
 
87
- def save(image, image_path)
88
- driver.save_image_to(image, image_path.to_s)
102
+ def difference=(new_difference)
103
+ @error_message = nil
104
+ @reporter = nil
105
+ @difference = new_difference
89
106
  end
90
107
 
91
108
  def image_files_exist?
92
109
  @base_image_path.exist? && @image_path.exist?
93
110
  end
94
111
 
95
- NEW_LINE = "\n"
96
-
97
- attr_reader :error_message
98
-
99
- private
100
-
101
112
  def without_tolerable_options?
102
113
  (@driver_options.keys & TOLERABLE_OPTIONS).empty?
103
114
  end
104
115
 
105
- def _different?
106
- raise "There is no original (base) screenshot version to compare, located: #{@base_image_path}" unless @base_image_path.exist?
107
- raise "There is no new screenshot version to compare, located: #{@image_path}" unless @image_path.exist?
108
-
109
- comparison = load_and_process_images
110
-
111
- unless driver.same_dimension?(comparison)
112
- return build_error_for_different_dimensions(comparison)
113
- end
114
-
115
- return not_different if driver.same_pixels?(comparison)
116
-
117
- @difference = driver.find_difference_region(comparison)
118
- return not_different unless @difference.different?
119
-
120
- different(@difference)
116
+ def build_failed_difference(comparison, failed_by)
117
+ Difference.new(
118
+ nil,
119
+ {difference_level: nil, max_color_distance: 0},
120
+ comparison,
121
+ failed_by
122
+ )
121
123
  end
122
124
 
123
125
  def load_and_process_images
124
- images = driver.load_images(old_file_name, new_file_name)
126
+ images = driver.load_images(base_image_path, image_path)
125
127
  base_image, new_image = preprocess_images(images)
126
- Comparison.new(new_image, base_image, @driver_options)
127
- end
128
-
129
- def build_error_message(difference)
130
- [
131
- "(#{difference.inspect})",
132
- new_file_name,
133
- annotated_base_image_path.to_path,
134
- annotated_image_path.to_path
135
- ].join(NEW_LINE)
128
+ Comparison.new(new_image, base_image, @driver_options, driver, image_path, base_image_path)
136
129
  end
137
130
 
138
131
  def skip_area
@@ -143,15 +136,6 @@ module Capybara
143
136
  @driver_options[:median_filter_window_size]
144
137
  end
145
138
 
146
- def dimensions
147
- @driver_options[:dimensions]
148
- end
149
-
150
- def different(difference)
151
- annotate_and_save_images(difference)
152
- build_error_message(difference)
153
- end
154
-
155
139
  def preprocess_images(images)
156
140
  images.map { |image| preprocess_image(image) }
157
141
  end
@@ -159,17 +143,19 @@ module Capybara
159
143
  def preprocess_image(image)
160
144
  result = image
161
145
 
162
- # FIXME: How can we access to this method from public interface? Is this not documented feature?
163
- if dimensions && driver.inscribed?(dimensions, result)
164
- result = driver.crop(dimensions, result)
165
- end
166
-
167
146
  if skip_area
168
147
  result = ignore_skipped_area(result)
169
148
  end
170
149
 
171
150
  if median_filter_window_size
172
- result = blur_image_by(image, median_filter_window_size)
151
+ if driver.is_a?(Drivers::VipsDriver)
152
+ result = blur_image_by(image, median_filter_window_size)
153
+ else
154
+ warn(
155
+ "[capybara-screenshot-diff] Median filter has been skipped for #{image_path} " \
156
+ "because it is not supported by #{driver.class.name}"
157
+ )
158
+ end
173
159
  end
174
160
 
175
161
  result
@@ -180,48 +166,28 @@ module Capybara
180
166
  end
181
167
 
182
168
  def ignore_skipped_area(image)
183
- skip_area.reduce(image) { |memo, region| driver.add_black_box(memo, region) }
169
+ skip_area&.reduce(image) { |memo, region| driver.add_black_box(memo, region) }
184
170
  end
185
171
 
186
172
  def old_file_size
187
- @old_file_size ||= image_files_exist? && File.size(@old_file_name)
173
+ base_image_path.size
188
174
  end
189
175
 
190
176
  def new_file_size
191
- File.size(@new_file_name)
192
- end
193
-
194
- def not_different
195
- nil
177
+ image_path.size
196
178
  end
197
179
 
198
- def annotate_and_save_images(difference)
199
- annotate_and_save_image(difference, difference.comparison.new_image, @annotated_image_path)
200
- annotate_and_save_image(difference, difference.comparison.base_image, @annotated_base_image_path)
180
+ def build_no_difference(comparison = nil)
181
+ Difference.new(
182
+ nil,
183
+ {difference_level: nil, max_color_distance: 0},
184
+ comparison || build_comparison
185
+ ).freeze
201
186
  end
202
187
 
203
- def annotate_and_save_image(difference, image, image_path)
204
- image = annotate_difference(image, difference.region)
205
- image = annotate_skip_areas(image, difference.skip_area) if difference.skip_area
206
- save(image, image_path.to_path)
207
- end
208
-
209
- DIFF_COLOR = [255, 0, 0, 255].freeze
210
-
211
- def annotate_difference(image, region)
212
- driver.draw_rectangles(Array[image], region, DIFF_COLOR, offset: 1).first
188
+ def build_comparison
189
+ Capybara::Screenshot::Diff::Comparison.new(nil, nil, driver_options, driver, image_path, base_image_path).freeze
213
190
  end
214
-
215
- SKIP_COLOR = [255, 192, 0, 255].freeze
216
-
217
- def annotate_skip_areas(image, skip_areas)
218
- skip_areas.reduce(image) do |memo, region|
219
- driver.draw_rectangles(Array[memo], region, SKIP_COLOR).first
220
- end
221
- end
222
- end
223
-
224
- class Comparison < Struct.new(:new_image, :base_image, :options)
225
191
  end
226
192
  end
227
193
  end
@@ -7,13 +7,6 @@ class Region
7
7
  @x, @y, @width, @height = x, y, width, height
8
8
  end
9
9
 
10
- def self.from_top_left_corner_coordinates(x, y, width, height)
11
- return nil unless x && y && width && height
12
- return nil if width < 0 || height < 0
13
-
14
- Region.new(x, y, width, height)
15
- end
16
-
17
10
  def self.from_edge_coordinates(left, top, right, bottom)
18
11
  return nil unless left && top && right && bottom
19
12
  return nil if right < left || bottom < top
@@ -83,4 +76,32 @@ class Region
83
76
  def cover?(x, y)
84
77
  left <= x && x <= right && top <= y && y <= bottom
85
78
  end
79
+
80
+ def empty?
81
+ width.zero? || height.zero?
82
+ end
83
+
84
+ def blank?
85
+ empty?
86
+ end
87
+
88
+ def present?
89
+ !empty?
90
+ end
91
+
92
+ def inspect
93
+ "Region(x: #{x}, y: #{y}, width: #{width}, height: #{height})"
94
+ end
95
+
96
+ # need to add this method to make it work with assert_equal
97
+ def ==(other)
98
+ case other
99
+ when Region
100
+ x == other.x && y == other.y && width == other.width && height == other.height
101
+ when Array
102
+ to_a == other
103
+ else
104
+ false
105
+ end
106
+ end
86
107
  end