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.
- checksums.yaml +4 -4
- data/Rakefile +29 -0
- data/capybara-screenshot-diff.gemspec +6 -3
- data/gems.rb +8 -2
- data/lib/capybara/screenshot/diff/browser_helpers.rb +102 -0
- data/lib/capybara/screenshot/diff/cucumber.rb +11 -0
- data/lib/capybara/screenshot/diff/difference.rb +63 -0
- data/lib/capybara/screenshot/diff/drivers/base_driver.rb +42 -0
- data/lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb +193 -252
- data/lib/capybara/screenshot/diff/drivers/utils.rb +25 -0
- data/lib/capybara/screenshot/diff/drivers/vips_driver.rb +65 -100
- data/lib/capybara/screenshot/diff/drivers.rb +16 -0
- data/lib/capybara/screenshot/diff/image_compare.rb +138 -154
- data/lib/capybara/screenshot/diff/os.rb +1 -1
- data/lib/capybara/screenshot/diff/region.rb +86 -0
- data/lib/capybara/screenshot/diff/screenshot_matcher.rb +128 -0
- data/lib/capybara/screenshot/diff/screenshoter.rb +136 -0
- data/lib/capybara/screenshot/diff/stabilization.rb +0 -210
- data/lib/capybara/screenshot/diff/stable_screenshoter.rb +106 -0
- data/lib/capybara/screenshot/diff/test_methods.rb +57 -63
- data/lib/capybara/screenshot/diff/vcs.rb +48 -21
- data/lib/capybara/screenshot/diff/version.rb +1 -1
- data/lib/capybara/screenshot/diff.rb +38 -35
- data/sig/capybara/screenshot/diff/diff.rbs +28 -0
- data/sig/capybara/screenshot/diff/difference.rbs +33 -0
- data/sig/capybara/screenshot/diff/drivers/base_driver.rbs +63 -0
- data/sig/capybara/screenshot/diff/drivers/browser_helpers.rbs +36 -0
- data/sig/capybara/screenshot/diff/drivers/chunky_png_driver.rbs +89 -0
- data/sig/capybara/screenshot/diff/drivers/utils.rbs +13 -0
- data/sig/capybara/screenshot/diff/drivers/vips_driver.rbs +25 -0
- data/sig/capybara/screenshot/diff/image_compare.rbs +93 -0
- data/sig/capybara/screenshot/diff/os.rbs +11 -0
- data/sig/capybara/screenshot/diff/region.rbs +43 -0
- data/sig/capybara/screenshot/diff/screenshot_matcher.rbs +60 -0
- data/sig/capybara/screenshot/diff/screenshoter.rbs +48 -0
- data/sig/capybara/screenshot/diff/stable_screenshoter.rbs +29 -0
- data/sig/capybara/screenshot/diff/test_methods.rbs +39 -0
- data/sig/capybara/screenshot/diff/vcs.rbs +17 -0
- metadata +30 -25
- data/.gitattributes +0 -4
- data/.github/workflows/lint.yml +0 -25
- data/.github/workflows/test.yml +0 -120
- data/.gitignore +0 -12
- data/.standard.yml +0 -12
- data/CONTRIBUTING.md +0 -22
- data/Dockerfile +0 -60
- data/README.md +0 -555
- data/bin/bundle +0 -114
- data/bin/console +0 -15
- data/bin/install-vips +0 -11
- data/bin/rake +0 -27
- data/bin/setup +0 -8
- data/bin/standardrb +0 -29
- data/gemfiles/rails52.gemfile +0 -6
- data/gemfiles/rails60_gems.rb +0 -8
- data/gemfiles/rails61_gems.rb +0 -7
- data/gemfiles/rails70_gems.rb +0 -7
- data/tmp/.keep +0 -0
@@ -3,11 +3,11 @@
|
|
3
3
|
begin
|
4
4
|
require "vips"
|
5
5
|
rescue LoadError => e
|
6
|
-
|
6
|
+
raise 'Required ruby-vips gem is missing. Add `gem "ruby-vips"` to Gemfile' if e.message.match?(/vips/i)
|
7
7
|
raise
|
8
8
|
end
|
9
9
|
|
10
|
-
|
10
|
+
require "capybara/screenshot/diff/drivers/base_driver"
|
11
11
|
|
12
12
|
module Capybara
|
13
13
|
module Screenshot
|
@@ -15,61 +15,33 @@ 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
|
-
class VipsDriver
|
19
|
-
|
18
|
+
class VipsDriver < BaseDriver
|
19
|
+
def find_difference_region(comparison)
|
20
|
+
new_image, base_image, options = comparison.new_image, comparison.base_image, comparison.options
|
20
21
|
|
21
|
-
|
22
|
-
options = old_file_name if old_file_name.is_a?(Hash)
|
23
|
-
|
24
|
-
@new_file_name = new_file_name
|
25
|
-
@old_file_name = old_file_name || "#{new_file_name}#{ImageCompare::TMP_FILE_SUFFIX}"
|
26
|
-
|
27
|
-
@options = options || {}
|
28
|
-
|
29
|
-
reset
|
30
|
-
end
|
31
|
-
|
32
|
-
# Resets the calculated data about the comparison with regard to the "new_image".
|
33
|
-
# Data about the original image is kept.
|
34
|
-
def reset
|
35
|
-
end
|
36
|
-
|
37
|
-
def shift_distance_equal?
|
38
|
-
warn "[capybara-screenshot-diff] Instead of shift_distance_limit " \
|
39
|
-
"please use median_filter_window_size and color_distance_limit options"
|
40
|
-
chunky_png_comparator.quick_equal?
|
41
|
-
end
|
42
|
-
|
43
|
-
def shift_distance_different?
|
44
|
-
warn "[capybara-screenshot-diff] Instead of shift_distance_limit " \
|
45
|
-
"please use median_filter_window_size and color_distance_limit options"
|
46
|
-
chunky_png_comparator.different?
|
47
|
-
end
|
48
|
-
|
49
|
-
def find_difference_region(new_image, old_image, color_distance_limit, _shift_distance_limit, _area_size_limit, fast_fail: false)
|
50
|
-
diff_mask = VipsUtil.difference_mask(color_distance_limit, old_image, new_image)
|
22
|
+
diff_mask = VipsUtil.difference_mask(base_image, new_image, options[:color_distance_limit])
|
51
23
|
region = VipsUtil.difference_region_by(diff_mask)
|
24
|
+
region = nil if region && same_as?(region, base_image)
|
52
25
|
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
def size(region)
|
57
|
-
return 0 unless region
|
58
|
-
|
59
|
-
(region[2] - region[0]) * (region[3] - region[1])
|
60
|
-
end
|
61
|
-
|
62
|
-
def adds_error_details_to(_log)
|
63
|
-
end
|
26
|
+
result = Difference.new(region, {}, comparison)
|
64
27
|
|
65
|
-
|
28
|
+
unless result.blank?
|
29
|
+
meta = {}
|
30
|
+
meta[:difference_level] = difference_level(diff_mask, base_image) if comparison.options[:tolerance]
|
31
|
+
result.meta = meta
|
32
|
+
end
|
66
33
|
|
67
|
-
|
68
|
-
dimension(i) == dimensions || i.width < dimensions[0] || i.height < dimensions[1]
|
34
|
+
result
|
69
35
|
end
|
70
36
|
|
71
|
-
def crop(
|
72
|
-
i.crop(
|
37
|
+
def crop(region, i)
|
38
|
+
i.crop(*region.to_top_left_corner_coordinates)
|
39
|
+
rescue Vips::Error => e
|
40
|
+
warn(
|
41
|
+
"[capybara-screenshot-diff] Crop has been failed for " \
|
42
|
+
"{ region: #{region.to_top_left_corner_coordinates.inspect}, image: #{dimension(i).join("x")} }"
|
43
|
+
)
|
44
|
+
raise e
|
73
45
|
end
|
74
46
|
|
75
47
|
def filter_image_with_median(image, median_filter_window_size)
|
@@ -77,81 +49,69 @@ module Capybara
|
|
77
49
|
end
|
78
50
|
|
79
51
|
def add_black_box(memo, region)
|
80
|
-
memo.draw_rect([0, 0, 0, 0], *region, fill: true)
|
81
|
-
end
|
82
|
-
|
83
|
-
def chunky_png_comparator
|
84
|
-
@chunky_png_comparator ||= ImageCompare.new(
|
85
|
-
@new_file_name,
|
86
|
-
@old_file_name,
|
87
|
-
**@options.merge(driver: :chunky_png, tolerance: nil, median_filter_window_size: nil)
|
88
|
-
)
|
52
|
+
memo.draw_rect([0, 0, 0, 0], *region.to_top_left_corner_coordinates, fill: true)
|
89
53
|
end
|
90
54
|
|
91
55
|
def difference_level(diff_mask, old_img, _region = nil)
|
92
56
|
VipsUtil.difference_area_size_by(diff_mask).to_f / image_area_size(old_img)
|
93
57
|
end
|
94
58
|
|
95
|
-
|
96
|
-
width_for(old_img) * height_for(old_img)
|
97
|
-
end
|
98
|
-
|
99
|
-
def height_for(image)
|
100
|
-
image.height
|
101
|
-
end
|
102
|
-
|
103
|
-
def width_for(image)
|
104
|
-
image.width
|
105
|
-
end
|
59
|
+
MAX_FILENAME_LENGTH = 200
|
106
60
|
|
61
|
+
# Vips could not work with the same file. Per each process we require to create new file
|
107
62
|
def save_image_to(image, filename)
|
108
|
-
|
63
|
+
# Dir::Tmpname will happily produce tempfile names that are too long for most unix filesystems,
|
64
|
+
# which leads to "unix error: File name too long". Apply a limit to avoid this.
|
65
|
+
limited_filename = filename.to_s[-MAX_FILENAME_LENGTH..] || filename.to_s
|
66
|
+
::Dir::Tmpname.create([limited_filename, PNG_EXTENSION]) do |tmp_image_filename|
|
67
|
+
image.write_to_file(tmp_image_filename)
|
68
|
+
FileUtils.mv(tmp_image_filename, filename)
|
69
|
+
end
|
109
70
|
end
|
110
71
|
|
111
72
|
def resize_image_to(image, new_width, new_height)
|
112
|
-
image.resize(
|
73
|
+
image.resize(new_width.to_f / new_height)
|
113
74
|
end
|
114
75
|
|
115
|
-
def load_images(old_file_name, new_file_name
|
116
|
-
[
|
76
|
+
def load_images(old_file_name, new_file_name)
|
77
|
+
[from_file(old_file_name), from_file(new_file_name)]
|
117
78
|
end
|
118
79
|
|
119
80
|
def from_file(filename)
|
120
|
-
result = ::Vips::Image.new_from_file(filename)
|
81
|
+
result = ::Vips::Image.new_from_file(filename.to_s)
|
121
82
|
|
122
|
-
result = result.colourspace(
|
83
|
+
result = result.colourspace(:srgb) if result.bands < 3
|
123
84
|
result = result.bandjoin(255) if result.bands == 3
|
124
85
|
|
125
86
|
result
|
126
87
|
end
|
127
88
|
|
128
|
-
def dimension_changed?(org_image, new_image)
|
129
|
-
return false if dimension(org_image) == dimension(new_image)
|
130
|
-
|
131
|
-
change_msg = [org_image, new_image].map { |i| "#{i.width}x#{i.height}" }.join(" => ")
|
132
|
-
warn "Image size has changed for #{@new_file_name}: #{change_msg}"
|
133
|
-
|
134
|
-
true
|
135
|
-
end
|
136
|
-
|
137
89
|
def dimension(image)
|
138
|
-
[image
|
90
|
+
[width_for(image), height_for(image)]
|
139
91
|
end
|
140
92
|
|
141
|
-
def draw_rectangles(images,
|
93
|
+
def draw_rectangles(images, region, rgba, offset: 0)
|
142
94
|
images.map do |image|
|
143
|
-
image.draw_rect(rgba, left -
|
95
|
+
image.draw_rect(rgba, region.left - offset, region.top - offset, region.width + (offset * 2), region.height + (offset * 2))
|
144
96
|
end
|
145
97
|
end
|
146
98
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
99
|
+
def same_pixels?(comparison)
|
100
|
+
(comparison.new_image == comparison.base_image).min == 255
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def same_as?(region, base_image)
|
106
|
+
region.x.zero? &&
|
107
|
+
region.y.zero? &&
|
108
|
+
region.height == height_for(base_image) &&
|
109
|
+
region.width == width_for(base_image)
|
110
|
+
end
|
152
111
|
|
112
|
+
class VipsUtil
|
153
113
|
def self.difference_area(old_image, new_image, color_distance: 0)
|
154
|
-
difference_mask = difference_mask(
|
114
|
+
difference_mask = difference_mask(new_image, old_image, color_distance)
|
155
115
|
difference_area_size_by(difference_mask)
|
156
116
|
end
|
157
117
|
|
@@ -160,19 +120,24 @@ module Capybara
|
|
160
120
|
diff_mask.hist_find.to_a[0][0].max
|
161
121
|
end
|
162
122
|
|
163
|
-
def self.difference_mask(
|
164
|
-
(new_image -
|
123
|
+
def self.difference_mask(base_image, new_image, color_distance = nil)
|
124
|
+
result = (new_image - base_image).abs
|
125
|
+
|
126
|
+
color_distance ? result > color_distance : result
|
165
127
|
end
|
166
128
|
|
167
129
|
def self.difference_region_by(diff_mask)
|
168
|
-
columns, rows = diff_mask.project
|
130
|
+
columns, rows = diff_mask.bandor.project
|
169
131
|
|
170
132
|
left = columns.profile[1].min
|
171
|
-
right = columns.width - columns.flip(
|
133
|
+
right = columns.width - columns.flip(:horizontal).profile[1].min
|
134
|
+
|
172
135
|
top = rows.profile[0].min
|
173
|
-
bottom = rows.height - rows.flip(
|
136
|
+
bottom = rows.height - rows.flip(:vertical).profile[0].min
|
137
|
+
|
138
|
+
return nil if right < left || bottom < top
|
174
139
|
|
175
|
-
|
140
|
+
Region.from_edge_coordinates(left, top, right, bottom)
|
176
141
|
end
|
177
142
|
end
|
178
143
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Capybara
|
4
|
+
module Screenshot
|
5
|
+
module Diff
|
6
|
+
module Drivers
|
7
|
+
def self.for(driver_options = {})
|
8
|
+
driver_option = driver_options.fetch(:driver, :chunky_png)
|
9
|
+
return driver_option unless driver_option.is_a?(Symbol)
|
10
|
+
|
11
|
+
Utils.find_driver_class_for(driver_option).new
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -5,186 +5,186 @@ module Capybara
|
|
5
5
|
module Diff
|
6
6
|
LOADED_DRIVERS = {}
|
7
7
|
|
8
|
-
# Compare two
|
8
|
+
# Compare two image and determine if they are equal, different, or within some comparison
|
9
9
|
# range considering color values and difference area size.
|
10
|
-
class ImageCompare
|
11
|
-
|
10
|
+
class ImageCompare
|
11
|
+
TOLERABLE_OPTIONS = [:tolerance, :color_distance_limit, :shift_distance_limit, :area_size_limit].freeze
|
12
12
|
|
13
13
|
attr_reader :driver, :driver_options
|
14
14
|
|
15
|
-
attr_reader :
|
16
|
-
:
|
17
|
-
:
|
15
|
+
attr_reader :annotated_image_path, :annotated_base_image_path,
|
16
|
+
:image_path, :base_image_path,
|
17
|
+
:new_file_name, :old_file_name
|
18
18
|
|
19
|
-
def initialize(
|
20
|
-
|
19
|
+
def initialize(image_path, base_image_path, options = {})
|
20
|
+
@image_path = Pathname.new(image_path)
|
21
21
|
|
22
|
-
@new_file_name =
|
23
|
-
@
|
24
|
-
@annotated_old_file_name = "#{new_file_name.chomp(".png")}.committed.png"
|
25
|
-
@annotated_new_file_name = "#{new_file_name.chomp(".png")}.latest.png"
|
22
|
+
@new_file_name = @image_path.to_s
|
23
|
+
@annotated_image_path = @image_path.sub_ext(".diff.png")
|
26
24
|
|
27
|
-
@
|
25
|
+
@base_image_path = Pathname.new(base_image_path)
|
28
26
|
|
29
|
-
@
|
30
|
-
@
|
31
|
-
@shift_distance_limit = options[:shift_distance_limit]
|
32
|
-
@dimensions = options[:dimensions]
|
33
|
-
@skip_area = options[:skip_area]
|
34
|
-
@tolerance = options[:tolerance]
|
35
|
-
@median_filter_window_size = options[:median_filter_window_size]
|
27
|
+
@old_file_name = @base_image_path.to_s
|
28
|
+
@annotated_base_image_path = @base_image_path.sub_ext(".diff.png")
|
36
29
|
|
37
|
-
|
38
|
-
@driver = driver_klass.new(@new_file_name, @old_file_name, **@driver_options)
|
30
|
+
@driver_options = options.dup
|
39
31
|
|
40
|
-
|
32
|
+
@driver = Drivers.for(@driver_options)
|
41
33
|
end
|
42
34
|
|
43
35
|
# Compare the two image files and return `true` or `false` as quickly as possible.
|
44
|
-
# Return
|
36
|
+
# Return falsely if the old file does not exist or the image dimensions do not match.
|
45
37
|
def quick_equal?
|
46
|
-
|
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
|
47
41
|
return true if new_file_size == old_file_size
|
48
42
|
|
49
|
-
|
50
|
-
# return true if old_bytes == new_bytes
|
43
|
+
comparison = load_and_process_images
|
51
44
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
region, meta = driver.find_difference_region(
|
58
|
-
new_image,
|
59
|
-
old_image,
|
60
|
-
@color_distance_limit,
|
61
|
-
@shift_distance_limit,
|
62
|
-
@area_size_limit,
|
63
|
-
fast_fail: true
|
64
|
-
)
|
65
|
-
|
66
|
-
self.difference_region = region
|
67
|
-
|
68
|
-
return true if difference_region_empty?(new_image, region)
|
45
|
+
unless driver.same_dimension?(comparison)
|
46
|
+
@error_message = build_error_for_different_dimensions(comparison)
|
47
|
+
return false
|
48
|
+
end
|
69
49
|
|
70
|
-
return true if
|
50
|
+
return true if driver.same_pixels?(comparison)
|
71
51
|
|
72
|
-
|
52
|
+
# Could not make any difference to be tolerable, so skip and return as not equal
|
53
|
+
return false if without_tolerable_options?
|
73
54
|
|
74
|
-
|
75
|
-
return true
|
55
|
+
@difference = driver.find_difference_region(comparison)
|
56
|
+
return true unless @difference.different?
|
76
57
|
|
58
|
+
@error_message = @difference.inspect
|
77
59
|
false
|
78
60
|
end
|
79
61
|
|
80
|
-
# Compare the two
|
62
|
+
# Compare the two image referenced by this object, and return `true` if they are different,
|
81
63
|
# and `false` if they are the same.
|
82
|
-
# Return `nil` if the old file does not exist or if the image dimensions do not match.
|
83
64
|
def different?
|
84
|
-
|
65
|
+
@error_message = nil
|
85
66
|
|
86
|
-
|
67
|
+
@error_message = _different?
|
87
68
|
|
88
|
-
|
69
|
+
clean_tmp_files unless @error_message
|
89
70
|
|
90
|
-
|
91
|
-
|
71
|
+
!@error_message.nil?
|
72
|
+
end
|
92
73
|
|
93
|
-
|
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(" => ")
|
94
78
|
|
95
|
-
|
96
|
-
|
79
|
+
"Screenshot dimension has been changed for #{@new_file_name}: #{change_msg}"
|
80
|
+
end
|
97
81
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
@shift_distance_limit,
|
103
|
-
@area_size_limit
|
104
|
-
)
|
105
|
-
self.difference_region = region
|
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?
|
85
|
+
end
|
106
86
|
|
107
|
-
|
108
|
-
|
109
|
-
|
87
|
+
def save(image, image_path)
|
88
|
+
driver.save_image_to(image, image_path.to_s)
|
89
|
+
end
|
110
90
|
|
111
|
-
|
112
|
-
|
91
|
+
def image_files_exist?
|
92
|
+
@base_image_path.exist? && @image_path.exist?
|
93
|
+
end
|
113
94
|
|
114
|
-
|
95
|
+
NEW_LINE = "\n"
|
115
96
|
|
116
|
-
|
117
|
-
end
|
97
|
+
attr_reader :error_message
|
118
98
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
File.delete(@annotated_new_file_name) if File.exist?(@annotated_new_file_name)
|
99
|
+
private
|
100
|
+
|
101
|
+
def without_tolerable_options?
|
102
|
+
(@driver_options.keys & TOLERABLE_OPTIONS).empty?
|
124
103
|
end
|
125
104
|
|
126
|
-
|
127
|
-
|
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
|
128
110
|
|
129
|
-
|
130
|
-
|
131
|
-
@skip_area.to_a.flatten.each_slice(4) do |region|
|
132
|
-
annotated_images = driver.draw_rectangles(annotated_images, region, SKIP_COLOR)
|
111
|
+
unless driver.same_dimension?(comparison)
|
112
|
+
return build_error_for_different_dimensions(comparison)
|
133
113
|
end
|
134
|
-
save(*annotated_images, @annotated_old_file_name, @annotated_new_file_name)
|
135
|
-
end
|
136
114
|
|
137
|
-
|
138
|
-
|
139
|
-
driver.
|
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)
|
140
121
|
end
|
141
122
|
|
142
|
-
def
|
143
|
-
|
123
|
+
def load_and_process_images
|
124
|
+
images = driver.load_images(old_file_name, new_file_name)
|
125
|
+
base_image, new_image = preprocess_images(images)
|
126
|
+
Comparison.new(new_image, base_image, @driver_options)
|
144
127
|
end
|
145
128
|
|
146
|
-
def
|
147
|
-
|
148
|
-
|
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)
|
149
136
|
end
|
150
137
|
|
151
|
-
def
|
152
|
-
|
153
|
-
|
154
|
-
region: difference_region
|
155
|
-
}
|
138
|
+
def skip_area
|
139
|
+
@driver_options[:skip_area]
|
140
|
+
end
|
156
141
|
|
157
|
-
|
142
|
+
def median_filter_window_size
|
143
|
+
@driver_options[:median_filter_window_size]
|
144
|
+
end
|
158
145
|
|
159
|
-
|
146
|
+
def dimensions
|
147
|
+
@driver_options[:dimensions]
|
160
148
|
end
|
161
149
|
|
162
|
-
def
|
163
|
-
|
150
|
+
def different(difference)
|
151
|
+
annotate_and_save_images(difference)
|
152
|
+
build_error_message(difference)
|
153
|
+
end
|
164
154
|
|
165
|
-
|
155
|
+
def preprocess_images(images)
|
156
|
+
images.map { |image| preprocess_image(image) }
|
166
157
|
end
|
167
158
|
|
168
|
-
|
159
|
+
def preprocess_image(image)
|
160
|
+
result = image
|
161
|
+
|
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
|
169
166
|
|
170
|
-
|
171
|
-
|
167
|
+
if skip_area
|
168
|
+
result = ignore_skipped_area(result)
|
169
|
+
end
|
172
170
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
171
|
+
if median_filter_window_size
|
172
|
+
result = blur_image_by(image, median_filter_window_size)
|
173
|
+
end
|
174
|
+
|
175
|
+
result
|
176
|
+
end
|
177
|
+
|
178
|
+
def blur_image_by(image, size)
|
179
|
+
driver.filter_image_with_median(image, size)
|
180
|
+
end
|
181
|
+
|
182
|
+
def ignore_skipped_area(image)
|
183
|
+
skip_area.reduce(image) { |memo, region| driver.add_black_box(memo, region) }
|
184
184
|
end
|
185
185
|
|
186
186
|
def old_file_size
|
187
|
-
@old_file_size ||=
|
187
|
+
@old_file_size ||= image_files_exist? && File.size(@old_file_name)
|
188
188
|
end
|
189
189
|
|
190
190
|
def new_file_size
|
@@ -192,52 +192,36 @@ module Capybara
|
|
192
192
|
end
|
193
193
|
|
194
194
|
def not_different
|
195
|
-
|
196
|
-
false
|
195
|
+
nil
|
197
196
|
end
|
198
197
|
|
199
|
-
def
|
200
|
-
|
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)
|
201
201
|
end
|
202
202
|
|
203
|
-
def
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
[old_img, new_img]
|
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)
|
208
207
|
end
|
209
208
|
|
210
|
-
|
211
|
-
result = image
|
209
|
+
DIFF_COLOR = [255, 0, 0, 255].freeze
|
212
210
|
|
213
|
-
|
214
|
-
|
215
|
-
|
211
|
+
def annotate_difference(image, region)
|
212
|
+
driver.draw_rectangles(Array[image], region, DIFF_COLOR, offset: 1).first
|
213
|
+
end
|
216
214
|
|
217
|
-
|
218
|
-
result = driver.filter_image_with_median(image, @median_filter_window_size)
|
219
|
-
end
|
215
|
+
SKIP_COLOR = [255, 192, 0, 255].freeze
|
220
216
|
|
221
|
-
|
222
|
-
|
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
|
223
220
|
end
|
224
|
-
|
225
|
-
result
|
226
|
-
end
|
227
|
-
|
228
|
-
def difference_region=(region)
|
229
|
-
@left, @top, @right, @bottom = region
|
230
221
|
end
|
222
|
+
end
|
231
223
|
|
232
|
-
|
233
|
-
region.nil? ||
|
234
|
-
(
|
235
|
-
region[1] == height_for(new_image) &&
|
236
|
-
region[0] == width_for(new_image) &&
|
237
|
-
region[2].zero? &&
|
238
|
-
region[3].zero?
|
239
|
-
)
|
240
|
-
end
|
224
|
+
class Comparison < Struct.new(:new_image, :base_image, :options)
|
241
225
|
end
|
242
226
|
end
|
243
227
|
end
|