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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ecb7108c89946f6489787b491c0cd831f096d09ee656e68674887952682aee7a
4
- data.tar.gz: d41e23dae6a64663d26b586235ccc4cdc10d02a43b3fefa6a8563327cde563bd
3
+ metadata.gz: 3e7e3bdfacb7c2fad912c72ade765c9ec6a50f306048a374d8056fc02c27d643
4
+ data.tar.gz: c1527905de61a1d801c545729d68b811ba91db5902c1ff209b4dc212fa252971
5
5
  SHA512:
6
- metadata.gz: 89501dc8c7ab1b540d182dacd10f8bf2d3b24521851db20bed42eb7c066b484d5a5cfbc766a4a0d5c80539500c1eef342f55be4069da455ba1d2ba14524d2463
7
- data.tar.gz: 12c02e086af0df03206eaa5ac332306b0b199c33d70b6393c64a60a70a5777e4598b4aedf67afc0a6aa06166247efd7e6a0c21652aa4c2587e13009566363b87
6
+ metadata.gz: c9c7868fe6d13263db039cf2a7fbb85234abee8f3bc05dd51fc5cc78579a96c1de3f2275782f5c806e9b154472ad51a991cfad1847a54d4811460597a3a4b8fe
7
+ data.tar.gz: 209f71e33b2b4bdead447293c0e286de6b6e1d945f4e12889e1b4f94675bb8bc035956f19a9897ceeedf278999598bb095991ecd34f2eaffa219d088ea19d577
data/Rakefile CHANGED
@@ -16,3 +16,32 @@ Rake::TestTask.new("test:integration") do |t|
16
16
  t.libs << "lib"
17
17
  t.test_files = FileList["test/integration/**/*_test.rb"]
18
18
  end
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
+ task "clobber" do
31
+ puts "Cleanup tmp/*.png"
32
+ FileUtils.rm_rf(Dir["./tmp/*"])
33
+ end
34
+
35
+ task "test:benchmark" do
36
+ require_relative "./scripts/benchmark/find_region_benchmark"
37
+ benchmark = Capybara::Screenshot::Diff::Drivers::FindRegionBenchmark.new
38
+
39
+ puts "For Medium Screen Size: 800x600"
40
+ benchmark.for_medium_size_screens
41
+
42
+ puts ""
43
+ puts "*" * 100
44
+
45
+ puts "For Small Screen Size: 80x60"
46
+ benchmark.for_small_images
47
+ end
@@ -12,15 +12,18 @@ 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 = ">= 2.5.0"
15
+ spec.required_ruby_version = ">= 3.0.0"
16
16
  spec.license = "MIT"
17
17
  spec.metadata["allowed_push_host"] = "https://rubygems.org/"
18
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
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))})
20
+ end
21
+
19
22
  spec.bindir = "exe"
20
23
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
24
  spec.require_paths = ["lib"]
22
25
 
23
- spec.add_runtime_dependency "actionpack", ">= 4.2", "< 8"
26
+ spec.add_runtime_dependency "actionpack", ">= 6.1", "< 8"
24
27
  spec.add_runtime_dependency "capybara", ">= 2", "< 4"
25
28
  spec.add_runtime_dependency "chunky_png", "~> 1.3"
26
29
  end
data/gems.rb CHANGED
@@ -8,7 +8,7 @@ gemspec path: __dir__
8
8
  gem "rake"
9
9
 
10
10
  # Image processing libraries
11
- gem "oily_png", platform: :ruby
11
+ gem "oily_png", platform: :ruby, git: "https://github.com/donv/oily_png", branch: "patch-2"
12
12
  gem "ruby-vips", require: false
13
13
 
14
14
  # Test
@@ -18,12 +18,18 @@ gem "simplecov", require: false
18
18
 
19
19
  # Capybara Server
20
20
  gem "puma", require: false
21
+ gem "rackup", require: false
21
22
 
22
23
  # Capybara Drivers
23
24
  gem "cuprite", require: false
24
25
  gem "selenium-webdriver", require: false
25
- gem "webdrivers", require: false
26
+ gem "webdrivers", "~> 5.0", require: false
27
+
28
+ # Test Frameworks
29
+ # gem "cucumber", require: false
30
+ # gem "cucumber-rails", require: false
26
31
 
27
32
  group :tools do
28
33
  gem "standard", require: false
34
+ gem "rbs", require: false, platform: :ruby
29
35
  end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "region"
4
+
5
+ module Capybara
6
+ module Screenshot
7
+ module BrowserHelpers
8
+ def self.resize_to(window_size)
9
+ if session.driver.respond_to?(:resize)
10
+ session.driver.resize(*window_size)
11
+ elsif BrowserHelpers.selenium?
12
+ session.driver.browser.manage.window.resize_to(*window_size)
13
+ end
14
+ end
15
+
16
+ def self.selenium?
17
+ current_capybara_driver_class <= Capybara::Selenium::Driver
18
+ end
19
+
20
+ def self.window_size_is_wrong?(expected_window_size = nil)
21
+ selenium? && expected_window_size &&
22
+ session.driver.browser.manage.window.size != ::Selenium::WebDriver::Dimension.new(*expected_window_size)
23
+ end
24
+
25
+ def self.bounds_for_css(*css_selectors)
26
+ css_selectors.reduce([]) do |regions, selector|
27
+ regions.concat(all_visible_regions_for(selector))
28
+ end
29
+ end
30
+
31
+ IMAGE_WAIT_SCRIPT = <<~JS
32
+ function pending_image() {
33
+ var images = document.images;
34
+ for (var i = 0; i < images.length; i++) {
35
+ if (!images[i].complete) {
36
+ return images[i].src;
37
+ }
38
+ }
39
+ return false;
40
+ }(window)
41
+ JS
42
+
43
+ HIDE_CARET_SCRIPT = <<~JS
44
+ if (!document.getElementById('csdHideCaretStyle')) {
45
+ let style = document.createElement('style');
46
+ style.setAttribute('id', 'csdHideCaretStyle');
47
+ document.head.appendChild(style);
48
+ let styleSheet = style.sheet;
49
+ styleSheet.insertRule("* { caret-color: transparent !important; }", 0);
50
+ }
51
+ JS
52
+
53
+ def self.hide_caret
54
+ session.execute_script(HIDE_CARET_SCRIPT)
55
+ end
56
+
57
+ FIND_ACTIVE_ELEMENT_SCRIPT = <<~JS
58
+ function activeElement(){
59
+ const ae = document.activeElement;
60
+ if (ae.nodeName === "INPUT" || ae.nodeName === "TEXTAREA") {
61
+ ae.blur();
62
+ return ae;
63
+ }
64
+ return null;
65
+ }(window);
66
+ JS
67
+
68
+ def self.blur_from_focused_element
69
+ session.evaluate_script(FIND_ACTIVE_ELEMENT_SCRIPT)
70
+ end
71
+
72
+ GET_BOUNDING_CLIENT_RECT_SCRIPT = <<~JS
73
+ [
74
+ this.getBoundingClientRect().left,
75
+ this.getBoundingClientRect().top,
76
+ this.getBoundingClientRect().right,
77
+ this.getBoundingClientRect().bottom
78
+ ]
79
+ JS
80
+
81
+ def self.all_visible_regions_for(selector)
82
+ BrowserHelpers.session.all(selector, visible: true).map(&method(:region_for))
83
+ end
84
+
85
+ def self.region_for(element)
86
+ element.evaluate_script(GET_BOUNDING_CLIENT_RECT_SCRIPT).map { |point| point.negative? ? 0 : point.to_i }
87
+ end
88
+
89
+ def self.session
90
+ Capybara.current_session
91
+ end
92
+
93
+ def self.pending_image_to_load
94
+ BrowserHelpers.session.evaluate_script(IMAGE_WAIT_SCRIPT)
95
+ end
96
+
97
+ def self.current_capybara_driver_class
98
+ session.driver.class
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "capybara/screenshot/diff"
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
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Capybara
6
+ module Screenshot
7
+ module Diff
8
+ class Difference < Struct.new(:region, :meta, :comparison)
9
+ def different?
10
+ !(blank? || tolerable?)
11
+ end
12
+
13
+ def base_image
14
+ comparison.base_image
15
+ end
16
+
17
+ def options
18
+ comparison.options
19
+ end
20
+
21
+ def tolerance
22
+ options[:tolerance]
23
+ end
24
+
25
+ def skip_area
26
+ options[:skip_area]
27
+ end
28
+
29
+ def area_size_limit
30
+ options[:area_size_limit]
31
+ end
32
+
33
+ def blank?
34
+ region.nil? || region_area_size.zero?
35
+ end
36
+
37
+ def region_area_size
38
+ region&.size || 0
39
+ end
40
+
41
+ def ratio
42
+ meta[:difference_level]
43
+ end
44
+
45
+ def to_h
46
+ {area_size: region_area_size, region: coordinates}.merge!(meta)
47
+ end
48
+
49
+ def coordinates
50
+ region&.to_edge_coordinates
51
+ end
52
+
53
+ def inspect
54
+ to_h.to_json
55
+ end
56
+
57
+ def tolerable?
58
+ !!((area_size_limit && area_size_limit >= region_area_size) || (tolerance && tolerance >= ratio))
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "chunky_png"
4
+ require "capybara/screenshot/diff/difference"
5
+
6
+ module Capybara
7
+ module Screenshot
8
+ module Diff
9
+ # Compare two images and determine if they are equal, different, or within some comparison
10
+ # range considering color values and difference area size.
11
+ module Drivers
12
+ class BaseDriver
13
+ PNG_EXTENSION = ".png"
14
+
15
+ def same_dimension?(comparison)
16
+ dimension(comparison.base_image) == dimension(comparison.new_image)
17
+ end
18
+
19
+ def height_for(image)
20
+ image.height
21
+ end
22
+
23
+ def width_for(image)
24
+ image.width
25
+ end
26
+
27
+ def image_area_size(image)
28
+ width_for(image) * height_for(image)
29
+ end
30
+
31
+ def dimension(image)
32
+ [width_for(image), height_for(image)]
33
+ end
34
+
35
+ def inscribed?(dimensions, i)
36
+ width_for(i) < dimensions[0] || height_for(i) < dimensions[1]
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end