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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +29 -0
  3. data/capybara-screenshot-diff.gemspec +6 -3
  4. data/gems.rb +8 -2
  5. data/lib/capybara/screenshot/diff/browser_helpers.rb +102 -0
  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 +193 -252
  10. data/lib/capybara/screenshot/diff/drivers/utils.rb +18 -0
  11. data/lib/capybara/screenshot/diff/drivers/vips_driver.rb +61 -102
  12. data/lib/capybara/screenshot/diff/drivers.rb +16 -0
  13. data/lib/capybara/screenshot/diff/image_compare.rb +138 -154
  14. data/lib/capybara/screenshot/diff/os.rb +1 -1
  15. data/lib/capybara/screenshot/diff/region.rb +86 -0
  16. data/lib/capybara/screenshot/diff/screenshot_matcher.rb +128 -0
  17. data/lib/capybara/screenshot/diff/screenshoter.rb +136 -0
  18. data/lib/capybara/screenshot/diff/stabilization.rb +0 -208
  19. data/lib/capybara/screenshot/diff/stable_screenshoter.rb +106 -0
  20. data/lib/capybara/screenshot/diff/test_methods.rb +57 -63
  21. data/lib/capybara/screenshot/diff/vcs.rb +48 -21
  22. data/lib/capybara/screenshot/diff/version.rb +1 -1
  23. data/lib/capybara/screenshot/diff.rb +15 -19
  24. data/sig/capybara/screenshot/diff/diff.rbs +28 -0
  25. data/sig/capybara/screenshot/diff/difference.rbs +33 -0
  26. data/sig/capybara/screenshot/diff/drivers/base_driver.rbs +63 -0
  27. data/sig/capybara/screenshot/diff/drivers/browser_helpers.rbs +36 -0
  28. data/sig/capybara/screenshot/diff/drivers/chunky_png_driver.rbs +89 -0
  29. data/sig/capybara/screenshot/diff/drivers/utils.rbs +13 -0
  30. data/sig/capybara/screenshot/diff/drivers/vips_driver.rbs +25 -0
  31. data/sig/capybara/screenshot/diff/image_compare.rbs +93 -0
  32. data/sig/capybara/screenshot/diff/os.rbs +11 -0
  33. data/sig/capybara/screenshot/diff/region.rbs +43 -0
  34. data/sig/capybara/screenshot/diff/screenshot_matcher.rbs +60 -0
  35. data/sig/capybara/screenshot/diff/screenshoter.rbs +48 -0
  36. data/sig/capybara/screenshot/diff/stable_screenshoter.rbs +29 -0
  37. data/sig/capybara/screenshot/diff/test_methods.rbs +39 -0
  38. data/sig/capybara/screenshot/diff/vcs.rbs +17 -0
  39. metadata +30 -25
  40. data/.gitattributes +0 -4
  41. data/.github/workflows/lint.yml +0 -25
  42. data/.github/workflows/test.yml +0 -120
  43. data/.gitignore +0 -12
  44. data/.standard.yml +0 -12
  45. data/CONTRIBUTING.md +0 -22
  46. data/Dockerfile +0 -60
  47. data/README.md +0 -555
  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/rails52.gemfile +0 -6
  55. data/gemfiles/rails60_gems.rb +0 -8
  56. data/gemfiles/rails61_gems.rb +0 -7
  57. data/gemfiles/rails70_gems.rb +0 -7
  58. 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 difference_level(_diff_mask, old_img, region)
51
- size(region).to_f / image_area_size(old_img)
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 shift_distance_equal?
59
- # Stub
60
- false
34
+ def crop(region, i)
35
+ i.crop(*region.to_top_left_corner_coordinates)
61
36
  end
62
37
 
63
- def shift_distance_different?
64
- # Stub
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 height_for(image)
86
- image.height
42
+ def save_image_to(image, filename)
43
+ image.save(filename)
87
44
  end
88
45
 
89
- def width_for(image)
90
- image.width
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 size(region)
94
- return 0 unless region
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 max_color_distance
100
- calculate_metrics unless @max_color_distance
101
- @max_color_distance
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
- def max_shift_distance
105
- calculate_metrics unless @max_shift_distance || !@shift_distance_limit
106
- @max_shift_distance
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 adds_error_details_to(log)
110
- max_color_distance = self.max_color_distance.ceil(1)
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
- def crop(dimensions, i)
118
- i.crop(*dimensions)
119
- end
70
+ private
120
71
 
121
- def from_file(filename)
122
- ChunkyPNG::Image.from_file(filename)
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
- # private
76
+ class DifferenceRegionFinder
77
+ attr_accessor :skip_area, :color_distance_limit, :shift_distance_limit
126
78
 
127
- def calculate_metrics
128
- old_file, new_file = load_image_files(@old_file_name, @new_file_name)
79
+ def initialize(comparison, driver = nil)
80
+ @comparison = comparison
81
+ @driver = driver
129
82
 
130
- if old_file == new_file
131
- @max_color_distance = 0
132
- @max_shift_distance = 0
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
- old_image, new_image = _load_images(old_file, new_file)
137
- calculate_max_color_distance(new_image, old_image)
138
- calculate_max_shift_limit(new_image, old_image) if @shift_distance_limit
139
- end
88
+ def perform
89
+ find_difference_region(@comparison)
90
+ end
140
91
 
141
- def calculate_max_color_distance(new_image, old_image)
142
- pixel_pairs = old_image.pixels.zip(new_image.pixels)
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
- d = ChunkyPNG::Color.euclidean_distance_rgba(p1, p2)
147
- [max, d].max
148
- }
149
- end
95
+ meta = {}
96
+ meta[:max_color_distance] = 0
97
+ meta[:max_shift_distance] = 0 if shift_distance_limit
150
98
 
151
- def calculate_max_shift_limit(new_img, old_img)
152
- (0...new_img.width).each do |x|
153
- (0...new_img.height).each do |y|
154
- shift_distance =
155
- shift_distance_at(new_img, old_img, x, y, color_distance_limit: @color_distance_limit)
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
- def save_image_to(image, filename)
165
- image.save(filename)
166
- end
106
+ result = Difference.new(region, meta, comparison)
167
107
 
168
- def resize_image_to(image, new_width, new_height)
169
- image.resample_bilinear(new_width, new_height)
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
- def load_image_files(old_file_name, file_name)
173
- old_file = File.binread(old_file_name)
174
- new_file = File.binread(file_name)
175
- [old_file, new_file]
176
- end
111
+ if comparison.options[:tolerance]
112
+ meta[:difference_level] = difference_level(nil, base_image, region)
113
+ end
114
+ end
177
115
 
178
- def dimension_changed?(old_image, new_image)
179
- return unless old_image.dimension != new_image.dimension
116
+ result
117
+ end
180
118
 
181
- change_msg = [old_image, new_image].map { |i| "#{i.width}x#{i.height}" }.join(" => ")
182
- warn "Image size has changed for #{@new_file_name}: #{change_msg}"
183
- true
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
- def draw_rectangles(images, (left, top, right, bottom), (r, g, b))
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
- private
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
- def find_diff_rectangle(org_img, new_img, region)
197
- left, top, right, bottom = find_left_right_and_top(org_img, new_img, region)
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
- def find_top(old_img, new_img)
203
- old_img.height.times do |y|
204
- old_img.width.times do |x|
205
- return [x, y, x, y] unless same_color?(old_img, new_img, x, y)
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
- def find_left_right_and_top(old_img, new_img, region)
212
- left = region[0] || old_img.width - 1
213
- top = region[1]
214
- right = region[2] || 0
215
- bottom = region[3]
216
- old_img.height.times do |y|
217
- (0...left).find do |x|
218
- next if same_color?(old_img, new_img, x, y)
219
-
220
- top ||= y
221
- bottom = y
222
- left = x
223
- right = x if x > right
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
- right = x
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
- def find_bottom(old_img, new_img, left, right, bottom)
237
- if bottom
238
- (old_img.height - 1).step(bottom + 1, -1).find do |y|
239
- (left..right).find do |x|
240
- bottom = y unless same_color?(old_img, new_img, x, y)
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
- def same_color?(old_img, new_img, x, y)
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
- color_distance =
253
- color_distance_at(new_img, old_img, x, y, shift_distance_limit: @shift_distance_limit)
254
- if !@max_color_distance || color_distance > @max_color_distance
255
- @max_color_distance = color_distance
256
- end
257
- color_matches = color_distance == 0 || (@color_distance_limit && @color_distance_limit > 0 &&
258
- color_distance <= @color_distance_limit)
259
- return color_matches if !@shift_distance_limit || @max_shift_distance == Float::INFINITY
260
-
261
- shift_distance = (color_matches && 0) ||
262
- shift_distance_at(new_img, old_img, x, y, color_distance_limit: @color_distance_limit)
263
- if shift_distance && (@max_shift_distance.nil? || shift_distance > @max_shift_distance)
264
- @max_shift_distance = shift_distance
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
- def color_distance_at(new_img, old_img, x, y, shift_distance_limit:)
270
- org_color = old_img[x, y]
271
- if shift_distance_limit
272
- start_x = [0, x - shift_distance_limit].max
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
- def shift_distance_at(new_img, old_img, x, y, color_distance_limit:)
290
- org_color = old_img[x, y]
291
- shift_distance = 0
292
- loop do
293
- bounds_breached = 0
294
- top_row = y - shift_distance
295
- if top_row >= 0 # top
296
- ([0, x - shift_distance].max..[x + shift_distance, new_img.width - 1].min).each do |dx|
297
- if color_matches(new_img, org_color, dx, top_row, color_distance_limit)
298
- return shift_distance
299
- end
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
- bounds_breached += 1
229
+ ChunkyPNG::Color.euclidean_distance_rgba(org_color, new_img[x, y])
303
230
  end
304
- if shift_distance > 0
305
- if (x - shift_distance) >= 0 # left
306
- ([0, top_row + 1].max..[y + shift_distance, new_img.height - 2].min)
307
- .each do |dy|
308
- if color_matches(new_img, org_color, x - shift_distance, dy, color_distance_limit)
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 (y + shift_distance) < new_img.height # bottom
316
- ([0, x - shift_distance].max..[x + shift_distance, new_img.width - 1].min).each do |dx|
317
- if color_matches(new_img, org_color, dx, y + shift_distance, color_distance_limit)
318
- return shift_distance
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
- else
322
- bounds_breached += 1
323
- end
324
- if (x + shift_distance) < new_img.width # right
325
- ([0, top_row + 1].max..[y + shift_distance, new_img.height - 2].min)
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
- end
335
- break if bounds_breached == 4
279
+ break if bounds_breached == 4
336
280
 
337
- shift_distance += 1
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
- color_distance = ChunkyPNG::Color.euclidean_distance_rgba(org_color, new_color)
347
- color_distance <= color_distance_limit
348
- end
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
- def _load_images(old_file, new_file)
351
- [ChunkyPNG::Image.from_blob(old_file), ChunkyPNG::Image.from_blob(new_file)]
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