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
@@ -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(0, 0, *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,14 +10,39 @@ 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
|
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
|
+
|
40
|
+
def self.detect_test_framework_assert
|
41
|
+
require "minitest"
|
42
|
+
::Minitest::Assertion
|
43
|
+
rescue
|
44
|
+
::RuntimeError
|
45
|
+
end
|
21
46
|
end
|
22
47
|
end
|
23
48
|
end
|