capybara-screenshot-diff 1.6.3 → 1.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +18 -0
- data/lib/capybara/screenshot/diff/drivers/vips_driver.rb +61 -102
- 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 -208
- 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 +15 -19
- 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
@@ -2,37 +2,17 @@
|
|
2
2
|
|
3
3
|
require "chunky_png"
|
4
4
|
|
5
|
+
require "capybara/screenshot/diff/drivers/base_driver"
|
6
|
+
|
5
7
|
module Capybara
|
6
8
|
module Screenshot
|
7
9
|
module Diff
|
8
10
|
# Compare two images and determine if they are equal, different, or within some comparison
|
9
11
|
# range considering color values and difference area size.
|
10
12
|
module Drivers
|
11
|
-
class ChunkyPNGDriver
|
13
|
+
class ChunkyPNGDriver < BaseDriver
|
12
14
|
include ChunkyPNG::Color
|
13
15
|
|
14
|
-
attr_reader :new_file_name, :old_file_name
|
15
|
-
|
16
|
-
def initialize(new_file_name, old_file_name = nil, options = {})
|
17
|
-
options = old_file_name if old_file_name.is_a?(Hash)
|
18
|
-
|
19
|
-
@new_file_name = new_file_name
|
20
|
-
@old_file_name = old_file_name || "#{new_file_name}#{ImageCompare::TMP_FILE_SUFFIX}"
|
21
|
-
|
22
|
-
@color_distance_limit = options[:color_distance_limit]
|
23
|
-
@shift_distance_limit = options[:shift_distance_limit]
|
24
|
-
@skip_area = options[:skip_area]
|
25
|
-
|
26
|
-
reset
|
27
|
-
end
|
28
|
-
|
29
|
-
# Resets the calculated data about the comparison with regard to the "new_image".
|
30
|
-
# Data about the original image is kept.
|
31
|
-
def reset
|
32
|
-
@max_color_distance = @color_distance_limit ? 0 : nil
|
33
|
-
@max_shift_distance = @shift_distance_limit ? 0 : nil
|
34
|
-
end
|
35
|
-
|
36
16
|
def load_images(old_file_name, new_file_name)
|
37
17
|
old_bytes, new_bytes = load_image_files(old_file_name, new_file_name)
|
38
18
|
|
@@ -47,308 +27,269 @@ module Capybara
|
|
47
27
|
image
|
48
28
|
end
|
49
29
|
|
50
|
-
def
|
51
|
-
|
52
|
-
end
|
53
|
-
|
54
|
-
def image_area_size(old_img)
|
55
|
-
width_for(old_img) * height_for(old_img)
|
30
|
+
def find_difference_region(comparison)
|
31
|
+
DifferenceRegionFinder.new(comparison, self).perform
|
56
32
|
end
|
57
33
|
|
58
|
-
def
|
59
|
-
|
60
|
-
false
|
34
|
+
def crop(region, i)
|
35
|
+
i.crop(*region.to_top_left_corner_coordinates)
|
61
36
|
end
|
62
37
|
|
63
|
-
def
|
64
|
-
|
65
|
-
true
|
66
|
-
end
|
67
|
-
|
68
|
-
def find_difference_region(new_image, old_image, color_distance_limit, shift_distance_limit, area_size_limit, fast_fail: false)
|
69
|
-
return nil, nil if new_image.pixels == old_image.pixels
|
70
|
-
|
71
|
-
if fast_fail && !(color_distance_limit || shift_distance_limit || area_size_limit)
|
72
|
-
return [0, 0, width_for(new_image), height_for(new_image)], nil
|
73
|
-
end
|
74
|
-
|
75
|
-
region = find_top(old_image, new_image)
|
76
|
-
region = if region.nil? || region[1].nil?
|
77
|
-
nil
|
78
|
-
else
|
79
|
-
find_diff_rectangle(old_image, new_image, region)
|
80
|
-
end
|
81
|
-
|
82
|
-
[region, nil]
|
38
|
+
def from_file(filename)
|
39
|
+
ChunkyPNG::Image.from_file(filename.to_s)
|
83
40
|
end
|
84
41
|
|
85
|
-
def
|
86
|
-
image.
|
42
|
+
def save_image_to(image, filename)
|
43
|
+
image.save(filename)
|
87
44
|
end
|
88
45
|
|
89
|
-
def
|
90
|
-
image.
|
46
|
+
def resize_image_to(image, new_width, new_height)
|
47
|
+
image.resample_bilinear(new_width, new_height)
|
91
48
|
end
|
92
49
|
|
93
|
-
def
|
94
|
-
|
95
|
-
|
96
|
-
(region[2] - region[0] + 1) * (region[3] - region[1] + 1)
|
50
|
+
def load_image_files(old_file_name, file_name)
|
51
|
+
[File.binread(old_file_name), File.binread(file_name)]
|
97
52
|
end
|
98
53
|
|
99
|
-
def
|
100
|
-
|
101
|
-
|
102
|
-
end
|
54
|
+
def draw_rectangles(images, region, (r, g, b), offset: 0)
|
55
|
+
border_color = ChunkyPNG::Color.rgb(r, g, b)
|
56
|
+
border_shadow = ChunkyPNG::Color.rgba(r, g, b, 100)
|
103
57
|
|
104
|
-
|
105
|
-
|
106
|
-
|
58
|
+
images.map do |image|
|
59
|
+
new_img = image.dup
|
60
|
+
new_img.rect(region.left - offset, region.top - offset, region.right + offset, region.bottom + offset, border_color)
|
61
|
+
new_img.rect(region.left, region.top, region.right, region.bottom, border_shadow)
|
62
|
+
new_img
|
63
|
+
end
|
107
64
|
end
|
108
65
|
|
109
|
-
def
|
110
|
-
|
111
|
-
max_shift_distance = self.max_shift_distance
|
112
|
-
|
113
|
-
log[:max_color_distance] = max_color_distance
|
114
|
-
log.merge!(max_shift_distance: max_shift_distance) if max_shift_distance
|
66
|
+
def same_pixels?(comparison)
|
67
|
+
comparison.new_image == comparison.base_image
|
115
68
|
end
|
116
69
|
|
117
|
-
|
118
|
-
i.crop(*dimensions)
|
119
|
-
end
|
70
|
+
private
|
120
71
|
|
121
|
-
def
|
122
|
-
ChunkyPNG::Image.
|
72
|
+
def _load_images(old_file, new_file)
|
73
|
+
[ChunkyPNG::Image.from_blob(old_file), ChunkyPNG::Image.from_blob(new_file)]
|
123
74
|
end
|
124
75
|
|
125
|
-
|
76
|
+
class DifferenceRegionFinder
|
77
|
+
attr_accessor :skip_area, :color_distance_limit, :shift_distance_limit
|
126
78
|
|
127
|
-
|
128
|
-
|
79
|
+
def initialize(comparison, driver = nil)
|
80
|
+
@comparison = comparison
|
81
|
+
@driver = driver
|
129
82
|
|
130
|
-
|
131
|
-
@
|
132
|
-
@
|
133
|
-
return
|
83
|
+
@color_distance_limit = comparison.options[:color_distance_limit]
|
84
|
+
@shift_distance_limit = comparison.options[:shift_distance_limit]
|
85
|
+
@skip_area = comparison.options[:skip_area]
|
134
86
|
end
|
135
87
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
end
|
88
|
+
def perform
|
89
|
+
find_difference_region(@comparison)
|
90
|
+
end
|
140
91
|
|
141
|
-
|
142
|
-
|
143
|
-
@max_color_distance = pixel_pairs.inject(0) { |max, (p1, p2)|
|
144
|
-
next max unless p1 && p2
|
92
|
+
def find_difference_region(comparison)
|
93
|
+
new_image, base_image, = comparison.new_image, comparison.base_image
|
145
94
|
|
146
|
-
|
147
|
-
[
|
148
|
-
|
149
|
-
end
|
95
|
+
meta = {}
|
96
|
+
meta[:max_color_distance] = 0
|
97
|
+
meta[:max_shift_distance] = 0 if shift_distance_limit
|
150
98
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
if shift_distance && (@max_shift_distance.nil? || shift_distance > @max_shift_distance)
|
157
|
-
@max_shift_distance = shift_distance
|
158
|
-
return if @max_shift_distance == Float::INFINITY # rubocop: disable Lint/NonLocalExitFromIterator
|
159
|
-
end
|
99
|
+
region = find_top(base_image, new_image, cache: meta)
|
100
|
+
region = if region.nil? || region[1].nil?
|
101
|
+
nil
|
102
|
+
else
|
103
|
+
find_diff_rectangle(base_image, new_image, region, cache: meta)
|
160
104
|
end
|
161
|
-
end
|
162
|
-
end
|
163
105
|
|
164
|
-
|
165
|
-
image.save(filename)
|
166
|
-
end
|
106
|
+
result = Difference.new(region, meta, comparison)
|
167
107
|
|
168
|
-
|
169
|
-
|
170
|
-
end
|
108
|
+
unless result.blank?
|
109
|
+
meta[:max_color_distance] = meta[:max_color_distance].ceil(1) if meta[:max_color_distance]
|
171
110
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
end
|
111
|
+
if comparison.options[:tolerance]
|
112
|
+
meta[:difference_level] = difference_level(nil, base_image, region)
|
113
|
+
end
|
114
|
+
end
|
177
115
|
|
178
|
-
|
179
|
-
|
116
|
+
result
|
117
|
+
end
|
180
118
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
end
|
119
|
+
def difference_level(_diff_mask, base_image, region)
|
120
|
+
image_area_size = @driver.image_area_size(base_image)
|
121
|
+
return nil if image_area_size.zero?
|
185
122
|
|
186
|
-
|
187
|
-
images.map do |image|
|
188
|
-
new_img = image.dup
|
189
|
-
new_img.rect(left - 1, top - 1, right + 1, bottom + 1, ChunkyPNG::Color.rgb(r, g, b))
|
190
|
-
new_img
|
123
|
+
region.size.to_f / image_area_size
|
191
124
|
end
|
192
|
-
end
|
193
125
|
|
194
|
-
|
126
|
+
def find_diff_rectangle(org_img, new_img, area_coordinates, cache:)
|
127
|
+
left, top, right, bottom = find_left_right_and_top(org_img, new_img, area_coordinates, cache: cache)
|
128
|
+
bottom = find_bottom(org_img, new_img, left, right, bottom, cache: cache)
|
195
129
|
|
196
|
-
|
197
|
-
|
198
|
-
bottom = find_bottom(org_img, new_img, left, right, bottom)
|
199
|
-
[left, top, right, bottom]
|
200
|
-
end
|
130
|
+
Region.from_edge_coordinates(left, top, right, bottom)
|
131
|
+
end
|
201
132
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
133
|
+
def find_top(old_img, new_img, cache:)
|
134
|
+
old_img.height.times do |y|
|
135
|
+
old_img.width.times do |x|
|
136
|
+
return [x, y, x, y] unless same_color?(old_img, new_img, x, y, cache: cache)
|
137
|
+
end
|
206
138
|
end
|
139
|
+
nil
|
207
140
|
end
|
208
|
-
nil
|
209
|
-
end
|
210
141
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
x
|
225
|
-
end
|
226
|
-
(old_img.width - 1).step(right + 1, -1).find do |x|
|
227
|
-
unless same_color?(old_img, new_img, x, y)
|
142
|
+
def find_left_right_and_top(old_img, new_img, region, cache:)
|
143
|
+
region = region.is_a?(Region) ? region.to_edge_coordinates : region
|
144
|
+
|
145
|
+
left = region[0] || old_img.width - 1
|
146
|
+
top = region[1]
|
147
|
+
right = region[2] || 0
|
148
|
+
bottom = region[3]
|
149
|
+
|
150
|
+
old_img.height.times do |y|
|
151
|
+
(0...left).find do |x|
|
152
|
+
next if same_color?(old_img, new_img, x, y, cache: cache)
|
153
|
+
|
154
|
+
top ||= y
|
228
155
|
bottom = y
|
229
|
-
|
156
|
+
left = x
|
157
|
+
right = x if x > right
|
158
|
+
x
|
159
|
+
end
|
160
|
+
(old_img.width - 1).step(right + 1, -1).find do |x|
|
161
|
+
unless same_color?(old_img, new_img, x, y, cache: cache)
|
162
|
+
bottom = y
|
163
|
+
right = x
|
164
|
+
end
|
230
165
|
end
|
231
166
|
end
|
167
|
+
|
168
|
+
[left, top, right, bottom]
|
232
169
|
end
|
233
|
-
[left, top, right, bottom]
|
234
|
-
end
|
235
170
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
171
|
+
def find_bottom(old_img, new_img, left, right, bottom, cache:)
|
172
|
+
if bottom
|
173
|
+
(old_img.height - 1).step(bottom + 1, -1).find do |y|
|
174
|
+
(left..right).find do |x|
|
175
|
+
bottom = y unless same_color?(old_img, new_img, x, y, cache: cache)
|
176
|
+
end
|
241
177
|
end
|
242
178
|
end
|
243
|
-
end
|
244
|
-
bottom
|
245
|
-
end
|
246
179
|
|
247
|
-
|
248
|
-
@skip_area&.each do |skip_start_x, skip_start_y, skip_end_x, skip_end_y|
|
249
|
-
return true if skip_start_x <= x && x <= skip_end_x && skip_start_y <= y && y <= skip_end_y
|
180
|
+
bottom
|
250
181
|
end
|
251
182
|
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
color_distance
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
183
|
+
def same_color?(old_img, new_img, x, y, cache:)
|
184
|
+
return true if skipped_region?(x, y)
|
185
|
+
|
186
|
+
color_distance =
|
187
|
+
color_distance_at(new_img, old_img, x, y, shift_distance_limit: @shift_distance_limit)
|
188
|
+
|
189
|
+
if color_distance > cache[:max_color_distance]
|
190
|
+
cache[:max_color_distance] = color_distance
|
191
|
+
end
|
192
|
+
|
193
|
+
color_matches = color_distance == 0 ||
|
194
|
+
(!!@color_distance_limit && @color_distance_limit > 0 && color_distance <= @color_distance_limit)
|
195
|
+
|
196
|
+
return color_matches if !@shift_distance_limit || cache[:max_shift_distance] == Float::INFINITY
|
197
|
+
|
198
|
+
shift_distance = (color_matches && 0) ||
|
199
|
+
shift_distance_at(new_img, old_img, x, y, color_distance_limit: @color_distance_limit)
|
200
|
+
if shift_distance && (cache[:max_shift_distance].nil? || shift_distance > cache[:max_shift_distance])
|
201
|
+
cache[:max_shift_distance] = shift_distance
|
202
|
+
end
|
203
|
+
|
204
|
+
color_matches
|
265
205
|
end
|
266
|
-
color_matches
|
267
|
-
end
|
268
206
|
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
end_x = [x + shift_distance_limit, new_img.width - 1].min
|
274
|
-
xs = (start_x..end_x).to_a
|
275
|
-
start_y = [0, y - shift_distance_limit].max
|
276
|
-
end_y = [y + shift_distance_limit, new_img.height - 1].min
|
277
|
-
ys = (start_y..end_y).to_a
|
278
|
-
new_pixels = xs.product(ys)
|
279
|
-
distances = new_pixels.map { |dx, dy|
|
280
|
-
new_color = new_img[dx, dy]
|
281
|
-
ChunkyPNG::Color.euclidean_distance_rgba(org_color, new_color)
|
282
|
-
}
|
283
|
-
distances.min
|
284
|
-
else
|
285
|
-
ChunkyPNG::Color.euclidean_distance_rgba(org_color, new_img[x, y])
|
207
|
+
def skipped_region?(x, y)
|
208
|
+
return false unless @skip_area
|
209
|
+
|
210
|
+
@skip_area.any? { |region| region.cover?(x, y) }
|
286
211
|
end
|
287
|
-
end
|
288
212
|
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
213
|
+
def color_distance_at(new_img, old_img, x, y, shift_distance_limit:)
|
214
|
+
org_color = old_img[x, y]
|
215
|
+
if shift_distance_limit
|
216
|
+
start_x = [0, x - shift_distance_limit].max
|
217
|
+
end_x = [x + shift_distance_limit, new_img.width - 1].min
|
218
|
+
xs = (start_x..end_x).to_a
|
219
|
+
start_y = [0, y - shift_distance_limit].max
|
220
|
+
end_y = [y + shift_distance_limit, new_img.height - 1].min
|
221
|
+
ys = (start_y..end_y).to_a
|
222
|
+
new_pixels = xs.product(ys)
|
223
|
+
|
224
|
+
distances = new_pixels.map do |dx, dy|
|
225
|
+
ChunkyPNG::Color.euclidean_distance_rgba(org_color, new_img[dx, dy])
|
300
226
|
end
|
227
|
+
distances.min
|
301
228
|
else
|
302
|
-
|
229
|
+
ChunkyPNG::Color.euclidean_distance_rgba(org_color, new_img[x, y])
|
303
230
|
end
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
231
|
+
end
|
232
|
+
|
233
|
+
def shift_distance_at(new_img, old_img, x, y, color_distance_limit:)
|
234
|
+
org_color = old_img[x, y]
|
235
|
+
shift_distance = 0
|
236
|
+
loop do
|
237
|
+
bounds_breached = 0
|
238
|
+
top_row = y - shift_distance
|
239
|
+
if top_row >= 0 # top
|
240
|
+
([0, x - shift_distance].max..[x + shift_distance, new_img.width - 1].min).each do |dx|
|
241
|
+
if color_matches(new_img, org_color, dx, top_row, color_distance_limit)
|
309
242
|
return shift_distance
|
310
243
|
end
|
311
244
|
end
|
312
245
|
else
|
313
246
|
bounds_breached += 1
|
314
247
|
end
|
315
|
-
if
|
316
|
-
(
|
317
|
-
|
318
|
-
|
248
|
+
if shift_distance > 0
|
249
|
+
if (x - shift_distance) >= 0 # left
|
250
|
+
([0, top_row + 1].max..[y + shift_distance, new_img.height - 2].min)
|
251
|
+
.each do |dy|
|
252
|
+
if color_matches(new_img, org_color, x - shift_distance, dy, color_distance_limit)
|
253
|
+
return shift_distance
|
254
|
+
end
|
319
255
|
end
|
256
|
+
else
|
257
|
+
bounds_breached += 1
|
320
258
|
end
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
.each do |dy|
|
327
|
-
if color_matches(new_img, org_color, x + shift_distance, dy, color_distance_limit)
|
328
|
-
return shift_distance
|
259
|
+
if (y + shift_distance) < new_img.height # bottom
|
260
|
+
([0, x - shift_distance].max..[x + shift_distance, new_img.width - 1].min).each do |dx|
|
261
|
+
if color_matches(new_img, org_color, dx, y + shift_distance, color_distance_limit)
|
262
|
+
return shift_distance
|
263
|
+
end
|
329
264
|
end
|
265
|
+
else
|
266
|
+
bounds_breached += 1
|
267
|
+
end
|
268
|
+
if (x + shift_distance) < new_img.width # right
|
269
|
+
([0, top_row + 1].max..[y + shift_distance, new_img.height - 2].min)
|
270
|
+
.each do |dy|
|
271
|
+
if color_matches(new_img, org_color, x + shift_distance, dy, color_distance_limit)
|
272
|
+
return shift_distance
|
273
|
+
end
|
274
|
+
end
|
275
|
+
else
|
276
|
+
bounds_breached += 1
|
330
277
|
end
|
331
|
-
else
|
332
|
-
bounds_breached += 1
|
333
278
|
end
|
334
|
-
|
335
|
-
break if bounds_breached == 4
|
279
|
+
break if bounds_breached == 4
|
336
280
|
|
337
|
-
|
281
|
+
shift_distance += 1
|
282
|
+
end
|
283
|
+
Float::INFINITY
|
338
284
|
end
|
339
|
-
Float::INFINITY
|
340
|
-
end
|
341
|
-
|
342
|
-
def color_matches(new_img, org_color, x, y, color_distance_limit)
|
343
|
-
new_color = new_img[x, y]
|
344
|
-
return new_color == org_color unless color_distance_limit
|
345
285
|
|
346
|
-
|
347
|
-
|
348
|
-
|
286
|
+
def color_matches(new_img, org_color, x, y, color_distance_limit)
|
287
|
+
new_color = new_img[x, y]
|
288
|
+
return new_color == org_color unless color_distance_limit
|
349
289
|
|
350
|
-
|
351
|
-
|
290
|
+
color_distance = ChunkyPNG::Color.euclidean_distance_rgba(org_color, new_color)
|
291
|
+
color_distance <= color_distance_limit
|
292
|
+
end
|
352
293
|
end
|
353
294
|
end
|
354
295
|
end
|
@@ -10,15 +10,33 @@ module Capybara
|
|
10
10
|
result << :vips if defined?(Vips) || require("vips")
|
11
11
|
rescue LoadError
|
12
12
|
# vips not present
|
13
|
+
Object.send(:remove_const, :Vips) if defined?(Vips)
|
13
14
|
end
|
14
15
|
begin
|
15
16
|
result << :chunky_png if defined?(ChunkyPNG) || require("chunky_png")
|
16
17
|
rescue LoadError
|
17
18
|
# chunky_png not present
|
19
|
+
Object.send(:remove_const, :ChunkyPNG) if defined?(ChunkyPNG)
|
18
20
|
end
|
19
21
|
result
|
20
22
|
end
|
21
23
|
|
24
|
+
def self.find_driver_class_for(driver)
|
25
|
+
driver = AVAILABLE_DRIVERS.first if driver == :auto
|
26
|
+
|
27
|
+
LOADED_DRIVERS[driver] ||=
|
28
|
+
case driver
|
29
|
+
when :chunky_png
|
30
|
+
require "capybara/screenshot/diff/drivers/chunky_png_driver"
|
31
|
+
Drivers::ChunkyPNGDriver
|
32
|
+
when :vips
|
33
|
+
require "capybara/screenshot/diff/drivers/vips_driver"
|
34
|
+
Drivers::VipsDriver
|
35
|
+
else
|
36
|
+
fail "Wrong adapter #{driver.inspect}. Available adapters: #{AVAILABLE_DRIVERS.inspect}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
22
40
|
def self.detect_test_framework_assert
|
23
41
|
require "minitest"
|
24
42
|
::Minitest::Assertion
|