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.
- checksums.yaml +4 -4
- data/Rakefile +1 -11
- data/capybara-screenshot-diff.gemspec +2 -3
- data/gems.rb +4 -4
- data/lib/capybara/screenshot/diff/area_calculator.rb +56 -0
- data/lib/capybara/screenshot/diff/browser_helpers.rb +5 -5
- data/lib/capybara/screenshot/diff/comparison.rb +6 -0
- data/lib/capybara/screenshot/diff/cucumber.rb +1 -9
- data/lib/capybara/screenshot/diff/difference.rb +8 -4
- data/lib/capybara/screenshot/diff/drivers/base_driver.rb +0 -5
- data/lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb +10 -5
- data/lib/capybara/screenshot/diff/drivers/vips_driver.rb +14 -3
- data/lib/capybara/screenshot/diff/drivers.rb +1 -1
- data/lib/capybara/screenshot/diff/image_compare.rb +80 -114
- data/lib/capybara/screenshot/diff/region.rb +28 -7
- data/lib/capybara/screenshot/diff/reporters/default.rb +117 -0
- data/lib/capybara/screenshot/diff/screenshot_matcher.rb +36 -74
- data/lib/capybara/screenshot/diff/screenshoter.rb +46 -54
- data/lib/capybara/screenshot/diff/stable_screenshoter.rb +65 -63
- data/lib/capybara/screenshot/diff/test_methods.rb +76 -9
- data/lib/capybara/screenshot/diff/{drivers/utils.rb → utils.rb} +0 -7
- data/lib/capybara/screenshot/diff/vcs.rb +25 -23
- data/lib/capybara/screenshot/diff/version.rb +1 -1
- data/lib/capybara/screenshot/diff.rb +1 -111
- data/lib/capybara-screenshot-diff.rb +1 -1
- data/lib/capybara_screenshot_diff/attempts_reporter.rb +49 -0
- data/lib/capybara_screenshot_diff/cucumber.rb +12 -0
- data/lib/capybara_screenshot_diff/dsl.rb +11 -0
- data/lib/capybara_screenshot_diff/minitest.rb +45 -0
- data/lib/capybara_screenshot_diff/rspec.rb +32 -0
- data/lib/capybara_screenshot_diff/snap.rb +55 -0
- data/lib/capybara_screenshot_diff/snap_manager.rb +76 -0
- data/lib/capybara_screenshot_diff.rb +86 -0
- metadata +20 -39
- data/lib/capybara/screenshot/diff/stabilization.rb +0 -0
- data/sig/capybara/screenshot/diff/diff.rbs +0 -28
- data/sig/capybara/screenshot/diff/difference.rbs +0 -33
- data/sig/capybara/screenshot/diff/drivers/base_driver.rbs +0 -63
- data/sig/capybara/screenshot/diff/drivers/browser_helpers.rbs +0 -36
- data/sig/capybara/screenshot/diff/drivers/chunky_png_driver.rbs +0 -89
- data/sig/capybara/screenshot/diff/drivers/utils.rbs +0 -13
- data/sig/capybara/screenshot/diff/drivers/vips_driver.rbs +0 -25
- data/sig/capybara/screenshot/diff/image_compare.rbs +0 -93
- data/sig/capybara/screenshot/diff/os.rbs +0 -11
- data/sig/capybara/screenshot/diff/region.rbs +0 -43
- data/sig/capybara/screenshot/diff/screenshot_matcher.rbs +0 -60
- data/sig/capybara/screenshot/diff/screenshoter.rbs +0 -48
- data/sig/capybara/screenshot/diff/stable_screenshoter.rbs +0 -29
- data/sig/capybara/screenshot/diff/test_methods.rbs +0 -39
- data/sig/capybara/screenshot/diff/vcs.rbs +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 87328c5152c18c006da12b39c30f0389ac0f7b71174530473681ac98e035b884
|
4
|
+
data.tar.gz: ec54883ea6de4b75fa5be917fee79e5098a18cd1c5ea3ebbfceb567661e148d3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 "
|
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", "<
|
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 "
|
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
|
-
|
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
|
@@ -1,11 +1,3 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
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
|
14
|
-
|
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
|
-
|
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(
|
39
|
-
ChunkyPNG::Image.from_file(
|
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
|
-
[
|
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[:
|
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 :
|
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
|
-
|
39
|
-
|
40
|
-
#
|
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
|
-
|
39
|
+
self.difference = build_failed_difference(comparison, {different_dimensions: true})
|
47
40
|
return false
|
48
41
|
end
|
49
42
|
|
50
|
-
|
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
|
-
|
56
|
-
return true unless @difference.different?
|
51
|
+
self.difference = driver.find_difference_region(comparison)
|
57
52
|
|
58
|
-
|
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
|
-
|
59
|
+
processed.difference.different?
|
60
|
+
end
|
66
61
|
|
67
|
-
|
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
|
-
|
69
|
+
def processed?
|
70
|
+
!!difference
|
71
|
+
end
|
70
72
|
|
71
|
-
|
73
|
+
def processed
|
74
|
+
self.difference = find_difference unless processed?
|
75
|
+
@error_message ||= reporter.generate
|
76
|
+
self
|
72
77
|
end
|
73
78
|
|
74
|
-
|
75
|
-
change_msg = [comparison.base_image, comparison.new_image]
|
76
|
-
.map { |i| driver.dimension(i).join("x") }
|
77
|
-
.join(" => ")
|
79
|
+
private
|
78
80
|
|
79
|
-
|
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
|
83
|
-
|
84
|
-
|
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
|
88
|
-
|
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
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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(
|
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
|
-
|
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
|
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
|
-
|
173
|
+
base_image_path.size
|
188
174
|
end
|
189
175
|
|
190
176
|
def new_file_size
|
191
|
-
|
192
|
-
end
|
193
|
-
|
194
|
-
def not_different
|
195
|
-
nil
|
177
|
+
image_path.size
|
196
178
|
end
|
197
179
|
|
198
|
-
def
|
199
|
-
|
200
|
-
|
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
|
204
|
-
|
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
|