capybara-screenshot-diff 1.6.3 → 1.8.3

Sign up to get free protection for your applications and to get access to all the features.
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