capybara-screenshot-diff 1.8.0 → 1.10.2
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 +1 -11
- data/capybara-screenshot-diff.gemspec +3 -4
- data/gems.rb +11 -8
- 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 +15 -4
- data/lib/capybara/screenshot/diff/drivers.rb +1 -1
- data/lib/capybara/screenshot/diff/image_compare.rb +84 -114
- data/lib/capybara/screenshot/diff/region.rb +28 -7
- data/lib/capybara/screenshot/diff/reporters/default.rb +121 -0
- data/lib/capybara/screenshot/diff/screenshot_matcher.rb +36 -74
- data/lib/capybara/screenshot/diff/screenshoter.rb +47 -56
- data/lib/capybara/screenshot/diff/stable_screenshoter.rb +65 -63
- data/lib/capybara/screenshot/diff/test_methods.rb +81 -13
- data/lib/capybara/screenshot/diff/{drivers/utils.rb → utils.rb} +2 -7
- data/lib/capybara/screenshot/diff/vcs.rb +26 -20
- 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 +49 -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 -48
- 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: ffbb0b50c61fc1dcab03db91256897bed64caa09d65d37ee39765aec52e3d6fb
|
4
|
+
data.tar.gz: 1c762e2084bf2629eae25fb5e38792ce2232bdd62edbdbf1bff5381c074b3edd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 97356f772b607b1ff67b146d1726baabbef696b362187c5aa5833657a4a443e8a6e8a30dedec49a07bda4d115e2a7a5c8482e4ba64a958072d1aaf6cd9dab8a3
|
7
|
+
data.tar.gz: '05880832a40006772c5fd72b28a4ebe9c28ca36ef49e70f132cab55acf1b97135bcd86bdb4d1b19893961fb344b0268d1c5d708354412c9c99efad03d3da85ab'
|
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"
|
@@ -12,18 +12,17 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.summary = "Track your GUI changes with diff assertions"
|
13
13
|
spec.description = "Save screen shots and track changes with graphical diff"
|
14
14
|
spec.homepage = "https://github.com/donv/capybara-screenshot-diff"
|
15
|
-
spec.required_ruby_version = ">= 3.
|
15
|
+
spec.required_ruby_version = ">= 3.1"
|
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", ">=
|
26
|
+
spec.add_runtime_dependency "actionpack", ">= 7.0", "< 9"
|
27
27
|
spec.add_runtime_dependency "capybara", ">= 2", "< 4"
|
28
|
-
spec.add_runtime_dependency "chunky_png", ">= 1.3", "< 2"
|
29
28
|
end
|
data/gems.rb
CHANGED
@@ -8,13 +8,18 @@ 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
|
-
gem "
|
16
|
-
gem "
|
17
|
-
gem "
|
15
|
+
group :test do
|
16
|
+
gem "capybara", ">= 3.26"
|
17
|
+
gem "mutex_m" # Needed for RubyMine debugging. Try removing it.
|
18
|
+
gem "minitest", require: false
|
19
|
+
gem "minitest-stub-const", require: false
|
20
|
+
gem "simplecov", require: false
|
21
|
+
gem "rspec", require: false
|
22
|
+
end
|
18
23
|
|
19
24
|
# Capybara Server
|
20
25
|
gem "puma", require: false
|
@@ -22,8 +27,7 @@ gem "rackup", require: false
|
|
22
27
|
|
23
28
|
# Capybara Drivers
|
24
29
|
gem "cuprite", require: false
|
25
|
-
gem "selenium-webdriver", require: false
|
26
|
-
gem "webdrivers", "~> 5.0", require: false
|
30
|
+
gem "selenium-webdriver", ">= 4.11", require: false
|
27
31
|
|
28
32
|
# Test Frameworks
|
29
33
|
# gem "cucumber", require: false
|
@@ -31,5 +35,4 @@ gem "webdrivers", "~> 5.0", require: false
|
|
31
35
|
|
32
36
|
group :tools do
|
33
37
|
gem "standard", require: false
|
34
|
-
gem "rbs", require: false, platform: :ruby
|
35
38
|
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)
|
@@ -3,7 +3,7 @@
|
|
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
|
|
@@ -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,121 +28,116 @@ 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
|
-
|
66
|
-
|
67
|
-
@error_message = _different?
|
68
|
-
|
69
|
-
clean_tmp_files unless @error_message
|
70
|
-
|
71
|
-
!@error_message.nil?
|
59
|
+
processed.difference.different?
|
72
60
|
end
|
73
61
|
|
74
|
-
def
|
75
|
-
|
76
|
-
.map { |i| driver.dimension(i).join("x") }
|
77
|
-
.join(" => ")
|
78
|
-
|
79
|
-
"Screenshot dimension has been changed for #{@new_file_name}: #{change_msg}"
|
62
|
+
def dimensions_changed?
|
63
|
+
difference.failed_by&.[](:different_dimensions)
|
80
64
|
end
|
81
65
|
|
82
|
-
def
|
83
|
-
@
|
84
|
-
|
66
|
+
def reporter
|
67
|
+
@reporter ||= begin
|
68
|
+
current_difference = difference || build_no_difference(nil)
|
69
|
+
Capybara::Screenshot::Diff::Reporters::Default.new(current_difference)
|
70
|
+
end
|
85
71
|
end
|
86
72
|
|
87
|
-
def
|
88
|
-
|
73
|
+
def processed?
|
74
|
+
!!difference
|
89
75
|
end
|
90
76
|
|
91
|
-
def
|
92
|
-
|
77
|
+
def processed
|
78
|
+
self.difference = find_difference unless processed?
|
79
|
+
@error_message ||= reporter.generate
|
80
|
+
self
|
93
81
|
end
|
94
82
|
|
95
|
-
NEW_LINE = "\n"
|
96
|
-
|
97
|
-
attr_reader :error_message
|
98
|
-
|
99
83
|
private
|
100
84
|
|
101
|
-
def
|
102
|
-
|
103
|
-
end
|
104
|
-
|
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?
|
85
|
+
def find_difference
|
86
|
+
require_images_exists!
|
108
87
|
|
109
88
|
comparison = load_and_process_images
|
110
89
|
|
111
90
|
unless driver.same_dimension?(comparison)
|
112
|
-
return
|
91
|
+
return build_failed_difference(comparison, {different_dimensions: true})
|
113
92
|
end
|
114
93
|
|
115
|
-
|
94
|
+
if driver.same_pixels?(comparison)
|
95
|
+
build_no_difference(comparison)
|
96
|
+
else
|
97
|
+
driver.find_difference_region(comparison)
|
98
|
+
end
|
99
|
+
end
|
116
100
|
|
117
|
-
|
118
|
-
|
101
|
+
def require_images_exists!
|
102
|
+
raise ArgumentError, "There is no original (base) screenshot version to compare, located: #{base_image_path}" unless base_image_path.exist?
|
103
|
+
raise ArgumentError, "There is no new screenshot version to compare, located: #{image_path}" unless image_path.exist?
|
104
|
+
end
|
119
105
|
|
120
|
-
|
106
|
+
def difference=(new_difference)
|
107
|
+
@error_message = nil
|
108
|
+
@reporter = nil
|
109
|
+
@difference = new_difference
|
121
110
|
end
|
122
111
|
|
123
|
-
def
|
124
|
-
|
125
|
-
base_image, new_image = preprocess_images(images)
|
126
|
-
Comparison.new(new_image, base_image, @driver_options)
|
112
|
+
def image_files_exist?
|
113
|
+
@base_image_path.exist? && @image_path.exist?
|
127
114
|
end
|
128
115
|
|
129
|
-
def
|
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)
|
116
|
+
def without_tolerable_options?
|
117
|
+
(@driver_options.keys & TOLERABLE_OPTIONS).empty?
|
136
118
|
end
|
137
119
|
|
138
|
-
def
|
139
|
-
|
120
|
+
def build_failed_difference(comparison, failed_by)
|
121
|
+
Difference.new(
|
122
|
+
nil,
|
123
|
+
{difference_level: nil, max_color_distance: 0},
|
124
|
+
comparison,
|
125
|
+
failed_by
|
126
|
+
)
|
140
127
|
end
|
141
128
|
|
142
|
-
def
|
143
|
-
|
129
|
+
def load_and_process_images
|
130
|
+
images = driver.load_images(base_image_path, image_path)
|
131
|
+
base_image, new_image = preprocess_images(images)
|
132
|
+
Comparison.new(new_image, base_image, @driver_options, driver, image_path, base_image_path)
|
144
133
|
end
|
145
134
|
|
146
|
-
def
|
147
|
-
@driver_options[:
|
135
|
+
def skip_area
|
136
|
+
@driver_options[:skip_area]
|
148
137
|
end
|
149
138
|
|
150
|
-
def
|
151
|
-
|
152
|
-
build_error_message(difference)
|
139
|
+
def median_filter_window_size
|
140
|
+
@driver_options[:median_filter_window_size]
|
153
141
|
end
|
154
142
|
|
155
143
|
def preprocess_images(images)
|
@@ -159,17 +147,19 @@ module Capybara
|
|
159
147
|
def preprocess_image(image)
|
160
148
|
result = image
|
161
149
|
|
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
150
|
if skip_area
|
168
151
|
result = ignore_skipped_area(result)
|
169
152
|
end
|
170
153
|
|
171
154
|
if median_filter_window_size
|
172
|
-
|
155
|
+
if driver.is_a?(Drivers::VipsDriver)
|
156
|
+
result = blur_image_by(image, median_filter_window_size)
|
157
|
+
else
|
158
|
+
warn(
|
159
|
+
"[capybara-screenshot-diff] Median filter has been skipped for #{image_path} " \
|
160
|
+
"because it is not supported by #{driver.class.name}"
|
161
|
+
)
|
162
|
+
end
|
173
163
|
end
|
174
164
|
|
175
165
|
result
|
@@ -180,48 +170,28 @@ module Capybara
|
|
180
170
|
end
|
181
171
|
|
182
172
|
def ignore_skipped_area(image)
|
183
|
-
skip_area
|
173
|
+
skip_area&.reduce(image) { |memo, region| driver.add_black_box(memo, region) }
|
184
174
|
end
|
185
175
|
|
186
176
|
def old_file_size
|
187
|
-
|
177
|
+
base_image_path.size
|
188
178
|
end
|
189
179
|
|
190
180
|
def new_file_size
|
191
|
-
|
192
|
-
end
|
193
|
-
|
194
|
-
def not_different
|
195
|
-
nil
|
181
|
+
image_path.size
|
196
182
|
end
|
197
183
|
|
198
|
-
def
|
199
|
-
|
200
|
-
|
184
|
+
def build_no_difference(comparison = nil)
|
185
|
+
Difference.new(
|
186
|
+
nil,
|
187
|
+
{difference_level: nil, max_color_distance: 0},
|
188
|
+
comparison || build_comparison
|
189
|
+
).freeze
|
201
190
|
end
|
202
191
|
|
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
|
192
|
+
def build_comparison
|
193
|
+
Capybara::Screenshot::Diff::Comparison.new(nil, nil, driver_options, driver, image_path, base_image_path).freeze
|
213
194
|
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
195
|
end
|
226
196
|
end
|
227
197
|
end
|