capybara-screenshot-diff 1.6.3 → 1.7.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ecb7108c89946f6489787b491c0cd831f096d09ee656e68674887952682aee7a
4
- data.tar.gz: d41e23dae6a64663d26b586235ccc4cdc10d02a43b3fefa6a8563327cde563bd
3
+ metadata.gz: c2aaf70ea2e2737e435920bc94a4504a2d2d3fe9d92874a4f1a9ac2f66527ed1
4
+ data.tar.gz: '087a7e5e2636a56318884f7d272126e62faea68c49401e96fa40a4a0d43bfb8b'
5
5
  SHA512:
6
- metadata.gz: 89501dc8c7ab1b540d182dacd10f8bf2d3b24521851db20bed42eb7c066b484d5a5cfbc766a4a0d5c80539500c1eef342f55be4069da455ba1d2ba14524d2463
7
- data.tar.gz: 12c02e086af0df03206eaa5ac332306b0b199c33d70b6393c64a60a70a5777e4598b4aedf67afc0a6aa06166247efd7e6a0c21652aa4c2587e13009566363b87
6
+ metadata.gz: 7c636d231583f4b3df1404aff861ef94000752e8b07edc829e2c16d56e34e2ba7418849c7e875a9e625832a6b8259f854c3ff3e0883c20d5942350d34181bc7d
7
+ data.tar.gz: f706ccc8937cd86a53b0a0dd04cb2429e77d4ef1ac643b898ce7bc08c4b623c976ce3f402136fccf0851c99509ec346102a48ef724a4711f70e73e438cf2a973
@@ -0,0 +1,8 @@
1
+ ---
2
+ version: 2
3
+
4
+ updates:
5
+ - package-ecosystem: "github-actions"
6
+ directory: "/"
7
+ schedule:
8
+ interval: "weekly"
@@ -13,7 +13,7 @@ jobs:
13
13
 
14
14
  steps:
15
15
  - name: Checkout code
16
- uses: actions/checkout@v2
16
+ uses: actions/checkout@v3
17
17
 
18
18
  - name: Set up Ruby
19
19
  uses: ruby/setup-ruby@v1
@@ -14,19 +14,24 @@ env:
14
14
  FERRUM_PROCESS_TIMEOUT: '15'
15
15
  WD_CACHE_TIME: '864000' # 10 days
16
16
 
17
+ concurrency:
18
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
19
+ cancel-in-progress: true
20
+
17
21
  jobs:
18
22
  test:
19
23
  name: Functional Testing
20
- runs-on: ubuntu-20.04 # In order to install libvips 8.9+ version
24
+ runs-on: ubuntu-22.04 # In order to install libvips 8.9+ version
25
+ timeout-minutes: 5
21
26
 
22
27
  steps:
23
28
  - name: Checkout code
24
- uses: actions/checkout@v2
29
+ uses: actions/checkout@v3
25
30
 
26
31
  - name: Set up Ruby
27
32
  uses: ruby/setup-ruby@v1
28
33
  with:
29
- ruby-version: '3.0'
34
+ ruby-version: '3.1'
30
35
  bundler-cache: true
31
36
 
32
37
  - name: Install libvips
@@ -38,42 +43,44 @@ jobs:
38
43
  COVERAGE: enabled
39
44
 
40
45
  - name: Upload Screenshots
41
- if: ${{ always() }}
42
- uses: actions/upload-artifact@v2
46
+ if: always()
47
+ uses: actions/upload-artifact@v3
43
48
  with:
44
- path: test/fixtures/app/doc/screenshots/
49
+ name: screenshots
50
+ retention-days: 1
51
+ path: |
52
+ test/fixtures/app/doc/screenshots/
53
+ tmp/capybara/screenshots-diffs/
45
54
 
46
55
  - name: Upload Coverage
47
- uses: actions/upload-artifact@v2
56
+ uses: actions/upload-artifact@v3
48
57
  with:
49
58
  name: coverage
59
+ retention-days: 1
50
60
  path: coverage
51
61
 
52
62
  matrix:
53
63
  name: Test Integration Rails & Ruby
54
64
  needs: [ 'test' ]
55
65
  runs-on: ubuntu-20.04
66
+ timeout-minutes: 5
56
67
 
57
68
  strategy:
58
69
  matrix:
59
- ruby-version: [ '3.1', '3.0', '2.7', '2.6', 'jruby' ]
70
+ ruby-version: [ '3.1', '3.0', '2.7', 'jruby' ]
60
71
  gemfile:
72
+ - 'rails70_gems.rb'
61
73
  - 'rails61_gems.rb'
62
74
  - 'rails60_gems.rb'
63
- - 'rails52.gemfile'
64
- include:
65
- - ruby-version: 2.7
66
- gemfile: rails70_gems.rb
67
- - ruby-version: 3.0
68
- gemfile: rails70_gems.rb
69
- - ruby-version: 3.1
70
- gemfile: rails70_gems.rb
75
+ exclude:
76
+ - ruby-version: 'jruby'
77
+ gemfile: 'rails70_gems.rb'
71
78
  env:
72
79
  BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}
73
80
 
74
81
  steps:
75
82
  - name: Checkout code
76
- uses: actions/checkout@v2
83
+ uses: actions/checkout@v3
77
84
 
78
85
  - name: Set up Ruby
79
86
  uses: ruby/setup-ruby@v1
@@ -91,6 +98,7 @@ jobs:
91
98
  name: Test Integration Capybara & Image Drivers
92
99
  needs: [ 'test' ]
93
100
  runs-on: ubuntu-20.04
101
+ timeout-minutes: 5
94
102
 
95
103
  strategy:
96
104
  matrix:
@@ -102,7 +110,7 @@ jobs:
102
110
 
103
111
  steps:
104
112
  - name: Checkout code
105
- uses: actions/checkout@v2
113
+ uses: actions/checkout@v3
106
114
 
107
115
  - name: Set up Ruby
108
116
  uses: ruby/setup-ruby@v1
@@ -118,3 +126,13 @@ jobs:
118
126
  env:
119
127
  SCREENSHOT_DRIVER: ${{ matrix.screenshot-driver }}
120
128
  CAPYBARA_DRIVER: ${{ matrix.capybara-driver }}
129
+
130
+ - name: Upload Screenshots
131
+ uses: actions/upload-artifact@v3
132
+ if: failure()
133
+ with:
134
+ name: screenshots-${{ matrix.screenshot-driver }}-${{ matrix.capybara-driver }}
135
+ retention-days: 1
136
+ path: |
137
+ test/fixtures/app/doc/screenshots/
138
+ tmp/capybara/screenshots-diffs/
data/.gitignore CHANGED
@@ -10,3 +10,5 @@
10
10
  /pkg/
11
11
  /spec/reports/
12
12
  /tmp/
13
+ /test/fixtures/**/*.committed.png
14
+ /test/fixtures/**/*.latest.png
data/CONTRIBUTING.md CHANGED
@@ -19,4 +19,6 @@ Run the tests for a matrix of configurations of Ruby implementations and Rails v
19
19
 
20
20
  ## Merging to master
21
21
 
22
- Before merging to master, please have a member of the project review your changes, and make sure the tests are green in travis-ci.
22
+ Before merging to `master`,
23
+ please have a member of the project review your changes,
24
+ and make sure the tests are green.
data/Dockerfile CHANGED
@@ -3,9 +3,7 @@
3
3
  # $ docker build . -t csd
4
4
  # $ docker run -v $(pwd):/app -ti csd rake test
5
5
 
6
- ARG RUBY_VERSION=2.7.2
7
-
8
- FROM circleci/ruby:2.7.2-node-browsers
6
+ FROM jetthoughts/cimg-ruby:3.1-chrome
9
7
 
10
8
  RUN \
11
9
  # Install dependencies
@@ -47,6 +45,7 @@ RUN sudo /app/bin/install-vips
47
45
  ADD ./lib/capybara/screenshot/diff/version.rb /app/lib/capybara/screenshot/diff/
48
46
  ADD ./capybara-screenshot-diff.gemspec /app/
49
47
  ADD ./gems.rb /app/
48
+ ADD ./Rakefile /app/
50
49
 
51
50
  RUN bundle install
52
51
 
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Build Status](https://travis-ci.org/donv/capybara-screenshot-diff.svg?branch=master)](https://travis-ci.org/donv/capybara-screenshot-diff)
1
+ [![Test](https://github.com/donv/capybara-screenshot-diff/actions/workflows/test.yml/badge.svg)](https://github.com/donv/capybara-screenshot-diff/actions/workflows/test.yml)
2
2
 
3
3
  # Capybara::Screenshot::Diff
4
4
 
@@ -40,14 +40,14 @@ In your test class, include the `Capybara::Screenshot::Diff` module:
40
40
  ```ruby
41
41
  class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
42
42
  include Capybara::Screenshot::Diff
43
- ...
43
+ # ...
44
44
  end
45
45
  ```
46
46
 
47
- ### rspec
47
+ ### RSpec
48
48
 
49
49
  ```ruby
50
- describe 'Permissions admin', :type => :feature, :js => true do
50
+ describe 'Permissions admin', type: :feature, js: true do
51
51
 
52
52
  include Capybara::Screenshot::Diff
53
53
 
@@ -64,7 +64,7 @@ But it's better to include it within your *_helper.rb file so that it can used a
64
64
  require 'capybara/screenshot/diff'
65
65
 
66
66
  RSpec.configure do |config|
67
- config.include Capybara::Screenshot::Diff::TestMethods
67
+ config.include Capybara::Screenshot::Diff
68
68
  end
69
69
  ```
70
70
 
@@ -97,8 +97,8 @@ doc
97
97
  ```
98
98
 
99
99
  To store the screen shot history, add the `doc/screenshots` directory to your
100
- version control system (git, svn, etc).
101
-
100
+ version control system (git, svn, etc).
101
+
102
102
  Screen shots are compared to the previously COMMITTED version of the same screen shot.
103
103
 
104
104
  ### Screenshot groups
@@ -186,20 +186,14 @@ end
186
186
  test.
187
187
 
188
188
 
189
-
190
189
  ### Capturing one area instead of the whole page
191
190
 
191
+ You can crop images before comparison to be run, by providing region to crop as `[left, top, right, bottom]` or by css selector like `body .tag`
192
+
192
193
  ```ruby
193
194
  test 'the cool' do
194
195
  visit '/feature'
195
- screenshot 'cool_element', crop: bounds('#my_element')
196
- end
197
-
198
- private
199
-
200
- def bounds(selector)
201
- element = evaluate_script("document.querySelector('#{selector}').getBoundingClientRect()")
202
- [element['left'], element['top'], element['right'], element['bottom']]
196
+ screenshot 'cool_element', crop: '#my_element'
203
197
  end
204
198
  ```
205
199
 
@@ -305,7 +299,7 @@ Capybara::Screenshot::Diff.enabled = ENV['COMPARE_SCREENSHOTS']
305
299
 
306
300
  By default, `Capybara::Screenshot::Diff` saves screenshots to a
307
301
  `doc/screenshots` folder, relative to either `Rails.root` (if you're in Rails),
308
- or your current directory otherwise.
302
+ or your current directory otherwise.
309
303
 
310
304
  If you want to change where screenshots are saved to, then there are two
311
305
  configuration options that that are relevant.
@@ -370,7 +364,6 @@ Capybara::Screenshot.hide_caret = true
370
364
  This will make the cursor (caret) transparent (invisible), so the blinking does not delay the screen shot.
371
365
 
372
366
 
373
-
374
367
  ### Removing focus from the active element
375
368
 
376
369
  Another way to avoid the cursor blinking is to set the `blur_active_element` option:
@@ -428,7 +421,7 @@ Capybara::Screenshot::Diff.shift_distance_limit = 1
428
421
  this will impact performance **severely** if a match cannot be found.
429
422
 
430
423
  If `shift_distance_limit` is `nil` shift distance is not measured. If `shift_distance_limit` is set,
431
- even to `0`, shift distabnce is measured and reported on image differences.
424
+ even to `0`, shift distance is measured and reported on image differences.
432
425
 
433
426
  ### Allowed difference size
434
427
 
@@ -452,25 +445,26 @@ Capybara::Screenshot::Diff.area_size_limit = 42
452
445
  ### Skipping an area
453
446
 
454
447
  Sometimes you have expected change that you want to ignore.
455
- You can use the `skip_area` option to the `screenshot` method to ignore an area:
448
+ You can use the `skip_area` option with `[left, top, right, bottom]`
449
+ or css selector like `'#footer'` or `'.container .skipped_element'` to the `screenshot` method to ignore an area:
456
450
 
457
451
  ```ruby
458
452
  test 'unstable area' do
459
453
  visit '/'
460
- screenshot 'index', skip_area: [17, 6, 27, 16]
454
+ screenshot 'index', skip_area: [[17, 6, 27, 16], '.container .skipped_element', '#footer']
461
455
  end
462
456
  ```
463
457
 
464
- The arguments are [x1, y1, x2, y2] for the area you want to ignore. You can also set this globally:
458
+ The arguments are `[left, top, right, bottom]` for the area you want to ignore. You can also set this globally:
465
459
 
466
460
  ```ruby
467
461
  Capybara::Screenshot::Diff.skip_area = [0, 0, 64, 48]
468
462
  ```
469
463
 
470
- If you need to ignore multiple areas, you can supply an array of arrays:
464
+ If you need to ignore multiple areas:
471
465
 
472
466
  ```ruby
473
- screenshot 'index', skip_area: [[0, 0, 64, 48], [17, 6, 27, 16]]
467
+ screenshot 'index', skip_area: [[0, 0, 64, 48], [17, 6, 27, 16], 'css_selector .element']
474
468
  ```
475
469
 
476
470
  ### Available Image Processing Drivers
@@ -479,7 +473,7 @@ There are several image processing supported by this gem.
479
473
  There are several options to setup active driver: `:auto`, `:chunky_png` and `:vips`.
480
474
 
481
475
  * `:auto` - will try to load `:vips` if there is gem `ruby-vips`, in other cases will load `:chunky_png`
482
- * `:chunky_png` and `:vips` will load correspondent driver
476
+ * `:chunky_png` and `:vips` will load correspondent driver
483
477
 
484
478
  ### Enable VIPS image processing
485
479
 
@@ -488,8 +482,8 @@ and could be enabled by adding `ruby-vips` to `Gemfile`.
488
482
 
489
483
  If need to setup explicitly Vips driver, there are several ways to do this:
490
484
 
491
- * Globally: `Capybara::Screenshot::Diff.driver = :vips`
492
- * Per screenshot option: `screenshot 'index', driver: :vips`
485
+ * Globally: `Capybara::Screenshot::Diff.driver = :vips`
486
+ * Per screenshot option: `screenshot 'index', driver: :vips`
493
487
 
494
488
  With enabled VIPS there are new alternatives to process differences, which easier to find and support.
495
489
  For example, `shift_distance_limit` is very heavy operation. Instead better to use `median_filter_window_size`.
@@ -532,6 +526,24 @@ test 'unstable area' do
532
526
  end
533
527
  ```
534
528
 
529
+ ### Skipping stack frames in the error output
530
+
531
+ If you would like to override the `screenshot` method or for some other reason would like to skip stack
532
+ frames when reporting image differences, you can use the `skip_stack_frames` option:
533
+
534
+ ```ruby
535
+ test 'test visiting the index' do
536
+ visit root_path
537
+ screenshot :index
538
+ end
539
+
540
+ private
541
+
542
+ def screenshot(name, **options)
543
+ super(name, skip_stack_frames: 1, **options)
544
+ end
545
+ ```
546
+
535
547
  ## Development
536
548
 
537
549
  After checking out the repo, run `bin/setup` to install dependencies.
data/bin/install-vips CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  set -e
4
4
 
5
- version=${VIPS_VERSION:-8.10.0}
5
+ version=${VIPS_VERSION:-8.12.2}
6
6
 
7
7
  wget "https://github.com/libvips/libvips/releases/download/v$version/vips-$version.tar.gz"
8
8
  tar xf "vips-$version.tar.gz"
@@ -12,7 +12,7 @@ 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 = defined?(JRUBY_VERSION) ? ">= 2.6.0" : ">= 2.7.0"
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 { |f| f.match(%r{^(test|spec|features)/}) }
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
20
20
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
21
  spec.require_paths = ["lib"]
22
22
 
23
- spec.add_runtime_dependency "actionpack", ">= 4.2", "< 8"
23
+ spec.add_runtime_dependency "actionpack", ">= 6.0", "< 8"
24
24
  spec.add_runtime_dependency "capybara", ">= 2", "< 4"
25
25
  spec.add_runtime_dependency "chunky_png", "~> 1.3"
26
26
  end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "region"
4
+
5
+ module Capybara
6
+ module Screenshot
7
+ module BrowserHelpers
8
+ def current_capybara_driver_class
9
+ Capybara.current_session.driver.class
10
+ end
11
+
12
+ def selenium?
13
+ current_capybara_driver_class <= Capybara::Selenium::Driver
14
+ end
15
+
16
+ def window_size_is_wrong?
17
+ selenium? &&
18
+ Screenshot.window_size &&
19
+ page.driver.browser.manage.window.size != ::Selenium::WebDriver::Dimension.new(*Screenshot.window_size)
20
+ end
21
+
22
+ def rect_for(css_selector)
23
+ all_visible_regions_for(css_selector).first
24
+ end
25
+
26
+ def bounds_for_css(*css_selectors)
27
+ css_selectors.reduce([]) do |regions, selector|
28
+ regions.concat(all_visible_regions_for(selector))
29
+ end
30
+ end
31
+
32
+ IMAGE_WAIT_SCRIPT = <<~JS
33
+ function pending_image() {
34
+ var images = document.images;
35
+ for (var i = 0; i < images.length; i++) {
36
+ if (!images[i].complete) {
37
+ return images[i].src;
38
+ }
39
+ }
40
+ return false;
41
+ }()
42
+ JS
43
+
44
+ def pending_image_to_load
45
+ evaluate_script IMAGE_WAIT_SCRIPT
46
+ end
47
+
48
+ HIDE_CARET_SCRIPT = <<~JS
49
+ if (!document.getElementById('csdHideCaretStyle')) {
50
+ let style = document.createElement('style');
51
+ style.setAttribute('id', 'csdHideCaretStyle');
52
+ document.head.appendChild(style);
53
+ let styleSheet = style.sheet;
54
+ styleSheet.insertRule("* { caret-color: transparent !important; }", 0);
55
+ }
56
+ JS
57
+
58
+ def hide_caret
59
+ execute_script(HIDE_CARET_SCRIPT)
60
+ end
61
+
62
+ FIND_ACTIVE_ELEMENT_SCRIPT = <<~JS
63
+ function activeElement(){
64
+ const ae = document.activeElement;
65
+ if (ae.nodeName === "INPUT" || ae.nodeName === "TEXTAREA") {
66
+ ae.blur();
67
+ return ae;
68
+ }
69
+ return null;
70
+ }();
71
+ JS
72
+
73
+ def blur_from_focused_element
74
+ page.evaluate_script(FIND_ACTIVE_ELEMENT_SCRIPT)
75
+ end
76
+
77
+ GET_BOUNDING_CLIENT_RECT_SCRIPT = <<~JS
78
+ [
79
+ this.getBoundingClientRect().left,
80
+ this.getBoundingClientRect().top,
81
+ this.getBoundingClientRect().right,
82
+ this.getBoundingClientRect().bottom
83
+ ]
84
+ JS
85
+
86
+ def all_visible_regions_for(selector)
87
+ all(selector, visible: true).map(&method(:region_for))
88
+ end
89
+
90
+ def region_for(element)
91
+ element.evaluate_script(GET_BOUNDING_CLIENT_RECT_SCRIPT).map { |point| point.negative? ? 0 : point.to_i }
92
+ end
93
+
94
+ private
95
+
96
+ def create_output_directory_for(file_name)
97
+ FileUtils.mkdir_p File.dirname(file_name)
98
+ end
99
+ end
100
+ end
101
+ end
@@ -12,6 +12,7 @@ module Capybara
12
12
  include ChunkyPNG::Color
13
13
 
14
14
  attr_reader :new_file_name, :old_file_name
15
+ attr_accessor :skip_area, :color_distance_limit, :shift_distance_limit
15
16
 
16
17
  def initialize(new_file_name, old_file_name = nil, options = {})
17
18
  options = old_file_name if old_file_name.is_a?(Hash)
@@ -69,7 +70,7 @@ module Capybara
69
70
  return nil, nil if new_image.pixels == old_image.pixels
70
71
 
71
72
  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
+ return build_region_for_whole_image(new_image), nil
73
74
  end
74
75
 
75
76
  region = find_top(old_image, new_image)
@@ -90,12 +91,6 @@ module Capybara
90
91
  image.width
91
92
  end
92
93
 
93
- def size(region)
94
- return 0 unless region
95
-
96
- (region[2] - region[0] + 1) * (region[3] - region[1] + 1)
97
- end
98
-
99
94
  def max_color_distance
100
95
  calculate_metrics unless @max_color_distance
101
96
  @max_color_distance
@@ -114,8 +109,8 @@ module Capybara
114
109
  log.merge!(max_shift_distance: max_shift_distance) if max_shift_distance
115
110
  end
116
111
 
117
- def crop(dimensions, i)
118
- i.crop(*dimensions)
112
+ def crop(region, i)
113
+ i.crop(*region.to_top_left_corner_coordinates)
119
114
  end
120
115
 
121
116
  def from_file(filename)
@@ -183,20 +178,29 @@ module Capybara
183
178
  true
184
179
  end
185
180
 
186
- def draw_rectangles(images, (left, top, right, bottom), (r, g, b))
181
+ def draw_rectangles(images, region, (r, g, b))
182
+ border_color = ChunkyPNG::Color.rgb(r, g, b)
183
+ border_shadow = ChunkyPNG::Color.rgba(r, g, b, 100)
184
+
187
185
  images.map do |image|
188
186
  new_img = image.dup
189
- new_img.rect(left - 1, top - 1, right + 1, bottom + 1, ChunkyPNG::Color.rgb(r, g, b))
187
+ new_img.rect(region.left - 1, region.top - 1, region.right + 1, region.bottom + 1, border_color)
188
+ new_img.rect(region.left, region.top, region.right, region.bottom, border_shadow)
190
189
  new_img
191
190
  end
192
191
  end
193
192
 
194
193
  private
195
194
 
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)
195
+ def build_region_for_whole_image(new_image)
196
+ Region.from_edge_coordinates(0, 0, width_for(new_image), height_for(new_image))
197
+ end
198
+
199
+ def find_diff_rectangle(org_img, new_img, area_coordinates)
200
+ left, top, right, bottom = find_left_right_and_top(org_img, new_img, area_coordinates)
198
201
  bottom = find_bottom(org_img, new_img, left, right, bottom)
199
- [left, top, right, bottom]
202
+
203
+ Region.from_edge_coordinates(left, top, right, bottom)
200
204
  end
201
205
 
202
206
  def find_top(old_img, new_img)
@@ -209,10 +213,13 @@ module Capybara
209
213
  end
210
214
 
211
215
  def find_left_right_and_top(old_img, new_img, region)
216
+ region = region.is_a?(Region) ? region.to_edge_coordinates : region
217
+
212
218
  left = region[0] || old_img.width - 1
213
219
  top = region[1]
214
220
  right = region[2] || 0
215
221
  bottom = region[3]
222
+
216
223
  old_img.height.times do |y|
217
224
  (0...left).find do |x|
218
225
  next if same_color?(old_img, new_img, x, y)
@@ -230,6 +237,7 @@ module Capybara
230
237
  end
231
238
  end
232
239
  end
240
+
233
241
  [left, top, right, bottom]
234
242
  end
235
243
 
@@ -241,13 +249,12 @@ module Capybara
241
249
  end
242
250
  end
243
251
  end
252
+
244
253
  bottom
245
254
  end
246
255
 
247
256
  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
250
- end
257
+ return true if skipped_region?(x, y)
251
258
 
252
259
  color_distance =
253
260
  color_distance_at(new_img, old_img, x, y, shift_distance_limit: @shift_distance_limit)
@@ -266,6 +273,12 @@ module Capybara
266
273
  color_matches
267
274
  end
268
275
 
276
+ def skipped_region?(x, y)
277
+ return false unless @skip_area
278
+
279
+ @skip_area.any? { |region| region.cover?(x, y) }
280
+ end
281
+
269
282
  def color_distance_at(new_img, old_img, x, y, shift_distance_limit:)
270
283
  org_color = old_img[x, y]
271
284
  if shift_distance_limit