capybara-screenshot-diff 0.10.2 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 85d1673c71c3656cae04ccb2ef3064af14f8f399
4
- data.tar.gz: ade0e18548afcf5d1701f329c724722d4d4359f1
3
+ metadata.gz: adbbae0be849e373116e41ba3d82e65008a6e281
4
+ data.tar.gz: efa2d3acc0df00651c6134ef0817a3d21be4282d
5
5
  SHA512:
6
- metadata.gz: 9693765e3bf37e1bf7d1a10649ea20e9ae604fdf188a33c87f918341459b27740062fd6075b9ce0438c142ec2723c5f50077e9282ae911608f5c75c9b4b8dce1
7
- data.tar.gz: 06c41cd6bec74485d4448b84d877e0f1e90ff227888b2adf09210cbc1a9d757a34a2da3c479d7c26f07d3b701643b0db349a72aa6bc9ff7a9ce771f19c368192
6
+ metadata.gz: 42af6279377d9119d99eac1eb7516e51ac2caee5122dc8f7ced3db225460ba91b07aea3a489d823fee98acc9b3e06fc503510aa25711a98272a18077e9c68dbc
7
+ data.tar.gz: 0d2729428c8a46a39ed949fb1a9bdbeefc4165cc7dd51ce66cabefa68185cd74d2ee7cde9870d46dea7c3036670be55a6229b29dbc0bf6c66d7a9c611ac5992f
data/.rubocop.yml CHANGED
@@ -4,6 +4,8 @@ AllCops:
4
4
  TargetRubyVersion: 2.2
5
5
  DisplayCopNames: true
6
6
  DisplayStyleGuide: true
7
+ Exclude:
8
+ - gemfiles/vendor/**/*
7
9
 
8
10
  Layout/AlignParameters:
9
11
  EnforcedStyle: with_fixed_indentation
@@ -21,12 +23,33 @@ Layout/MultilineOperationIndentation:
21
23
  Lint/Debugger:
22
24
  Enabled: false
23
25
 
26
+ # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
27
+ # URISchemes: http, https
24
28
  Metrics/LineLength:
25
- Max: 110
29
+ Max: 107
30
+ IgnoreCopDirectives: true
31
+
32
+ # Offense count: 2
33
+ # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
34
+ # AllowedNames: io, id, to
35
+ Naming/UncommunicativeMethodParamName:
36
+ AllowedNames:
37
+ - x
38
+ - y
39
+
40
+ Security/Eval:
41
+ Exclude:
42
+ - gemfiles/*.gemfile
43
+
44
+ #Style/AccessModifierDeclarations:
45
+ # Enabled: false
26
46
 
27
47
  Style/Documentation:
28
48
  Enabled: false
29
49
 
50
+ Style/DoubleNegation:
51
+ Enabled: false
52
+
30
53
  Style/NumericPredicate:
31
54
  Enabled: false
32
55
 
data/.rubocop_todo.yml CHANGED
@@ -1,29 +1,51 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2017-07-24 15:55:18 +0200 using RuboCop version 0.49.1.
3
+ # on 2018-06-26 14:23:03 +0200 using RuboCop version 0.57.2.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 7
9
+ # Offense count: 12
10
10
  Metrics/AbcSize:
11
- Max: 38
11
+ Max: 52
12
+
13
+ # Offense count: 1
14
+ # Configuration parameters: CountComments, ExcludedMethods.
15
+ Metrics/BlockLength:
16
+ Max: 33
12
17
 
13
18
  # Offense count: 1
14
19
  # Configuration parameters: CountComments.
15
20
  Metrics/ClassLength:
16
- Max: 186
21
+ Max: 273
17
22
 
18
- # Offense count: 3
23
+ # Offense count: 6
19
24
  Metrics/CyclomaticComplexity:
20
- Max: 10
25
+ Max: 12
21
26
 
22
- # Offense count: 9
27
+ # Offense count: 16
23
28
  # Configuration parameters: CountComments.
24
29
  Metrics/MethodLength:
25
- Max: 32
30
+ Max: 38
26
31
 
27
- # Offense count: 2
32
+ # Offense count: 1
33
+ # Configuration parameters: CountComments.
34
+ Metrics/ModuleLength:
35
+ Max: 103
36
+
37
+ # Offense count: 1
38
+ # Configuration parameters: CountKeywordArgs.
39
+ Metrics/ParameterLists:
40
+ Max: 6
41
+
42
+ # Offense count: 6
28
43
  Metrics/PerceivedComplexity:
29
- Max: 10
44
+ Max: 12
45
+
46
+ # Offense count: 2
47
+ # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
48
+ # AllowedNames: io, id, to, by, on, in, at
49
+ Naming/UncommunicativeMethodParamName:
50
+ Exclude:
51
+ - 'lib/capybara/screenshot/diff/image_compare.rb'
data/.travis.yml CHANGED
@@ -1,10 +1,12 @@
1
1
  language: ruby
2
2
  sudo: false
3
+ cache: bundler
3
4
  rvm:
4
- - ruby-2.4.1
5
- - jruby-9.1.12.0
6
- - ruby-2.3.4
7
- - ruby-2.2.7
5
+ - jruby-9.1.16.0
6
+ - ruby-2.5.1
7
+ - ruby-2.4.4
8
+ - ruby-2.3.7
9
+ - ruby-2.2.10
8
10
 
9
11
  gemfile:
10
12
  - gemfiles/rails51.gemfile
@@ -16,3 +18,8 @@ env:
16
18
 
17
19
  jdk:
18
20
  - oraclejdk8
21
+
22
+ # FIXME(uwe): Remove when fixed: https://github.com/travis-ci/travis-ci/issues/9383
23
+ before_install:
24
+ - gem install bundler
25
+ # EMXIF
@@ -0,0 +1,46 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6
+
7
+ ## Our Standards
8
+
9
+ Examples of behavior that contributes to creating a positive environment include:
10
+
11
+ * Using welcoming and inclusive language
12
+ * Being respectful of differing viewpoints and experiences
13
+ * Gracefully accepting constructive criticism
14
+ * Focusing on what is best for the community
15
+ * Showing empathy towards other community members
16
+
17
+ Examples of unacceptable behavior by participants include:
18
+
19
+ * The use of sexualized language or imagery and unwelcome sexual attention or advances
20
+ * Trolling, insulting/derogatory comments, and personal or political attacks
21
+ * Public or private harassment
22
+ * Publishing others' private information, such as a physical or electronic address, without explicit permission
23
+ * Other conduct which could reasonably be considered inappropriate in a professional setting
24
+
25
+ ## Our Responsibilities
26
+
27
+ Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28
+
29
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30
+
31
+ ## Scope
32
+
33
+ This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34
+
35
+ ## Enforcement
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at uwe@kubosch.no. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38
+
39
+ Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40
+
41
+ ## Attribution
42
+
43
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44
+
45
+ [homepage]: http://contributor-covenant.org
46
+ [version]: http://contributor-covenant.org/version/1/4/
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,22 @@
1
+ Contributing
2
+ ============
3
+
4
+ Bug reports and pull requests are welcome on GitHub at https://github.com/donv/capybara-screenshot-diff.
5
+ This project is intended to be a safe, welcoming space for collaboration, and contributors are expected
6
+ to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
7
+
8
+ ## Testing
9
+
10
+ Run the tests before committing using Rake
11
+
12
+ rake
13
+
14
+ ### Matrix testing
15
+
16
+ Run the tests for a matrix of configurations of Ruby implementations and Rails versions
17
+
18
+ ./matrix_text.rb
19
+
20
+ ## Merging to master
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.
data/README.md CHANGED
@@ -261,10 +261,27 @@ Capybara::Screenshot::Diff.enabled = ENV['COMPARE_SCREENSHOTS']
261
261
 
262
262
  ### Screen shot save path
263
263
 
264
- If you would like the screen shots to be saved in a different location set
264
+ By default, `Capybara::Screenshot::Diff` saves screenshots to a
265
+ `doc/screenshots` folder, relative to either `Rails.root` (if you're in Rails),
266
+ or your current directory otherwise.
267
+
268
+ If you want to change where screenshots are saved to, then there are two
269
+ configuration options that that are relevant.
270
+
271
+ The most likely one you'll want to modify is ...
272
+
273
+ ```ruby
274
+ Capybara::Screenshot::Diff.save_path = "other/path"
275
+ ```
276
+
277
+ The `save_path` option is relative to `Capybara::Screenshot.root`.
278
+
279
+ `Capybara::Screenshot.root` defaults to either `Rails.root` (if you're in
280
+ Rails) or your current directory. You can change it to something entirely
281
+ different if necessary, such as when using an alternative web framework.
265
282
 
266
283
  ```ruby
267
- Capybara::Screenshot.save_path = "#{Rails.root}/doc/gui"
284
+ Capybara::Screenshot.root = Hanami.root
268
285
  ```
269
286
 
270
287
  ### Screen shot stability
@@ -1,6 +1,4 @@
1
- # coding: utf-8
2
-
3
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path('lib', __dir__)
4
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
3
  require 'capybara/screenshot/diff/version'
6
4
 
@@ -20,7 +18,7 @@ Gem::Specification.new do |spec|
20
18
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
19
  spec.require_paths = ['lib']
22
20
 
23
- spec.add_runtime_dependency 'actionpack', '>= 4.2', '<5.2'
21
+ spec.add_runtime_dependency 'actionpack', '>= 4.2', '<5.3'
24
22
  spec.add_runtime_dependency 'capybara', '~> 2.0'
25
23
  spec.add_runtime_dependency 'chunky_png', '~> 1.3'
26
24
 
@@ -28,6 +26,6 @@ Gem::Specification.new do |spec|
28
26
  spec.add_development_dependency 'minitest', '~> 5.0'
29
27
  spec.add_development_dependency 'minitest-reporters'
30
28
  spec.add_development_dependency 'rake'
31
- spec.add_development_dependency 'rubocop', '~> 0.39'
29
+ spec.add_development_dependency 'rubocop', '~> 0.54'
32
30
  spec.add_development_dependency 'simplecov', '~> 0.11'
33
31
  end
@@ -10,9 +10,10 @@ module Capybara
10
10
  mattr_accessor :add_os_path
11
11
  mattr_accessor :blur_active_element
12
12
  mattr_accessor :enabled
13
- mattr_accessor(:screenshot_root) { (defined?(Rails.root) && Rails.root) || File.expand_path('.') }
13
+ mattr_accessor(:root) { (defined?(Rails.root) && Rails.root) || File.expand_path('.') }
14
14
  mattr_accessor :stability_time_limit
15
15
  mattr_accessor :window_size
16
+ mattr_accessor(:save_path) { 'doc/screenshots' }
16
17
 
17
18
  class << self
18
19
  def active?
@@ -20,14 +21,14 @@ module Capybara
20
21
  end
21
22
 
22
23
  def screenshot_area
23
- parts = ['doc/screenshots']
24
+ parts = [Capybara::Screenshot.save_path]
24
25
  parts << Capybara.current_driver.to_s if Capybara::Screenshot.add_driver_path
25
26
  parts << os_name if Capybara::Screenshot.add_os_path
26
27
  File.join parts
27
28
  end
28
29
 
29
30
  def screenshot_area_abs
30
- "#{screenshot_root}/#{screenshot_area}".freeze
31
+ "#{root}/#{screenshot_area}".freeze
31
32
  end
32
33
  end
33
34
 
@@ -39,6 +40,7 @@ module Capybara
39
40
  mattr_accessor :area_size_limit
40
41
  mattr_accessor :color_distance_limit
41
42
  mattr_accessor(:enabled) { true }
43
+ mattr_accessor :shift_distance_limit
42
44
 
43
45
  def self.included(clas)
44
46
  clas.include TestMethods
@@ -12,10 +12,11 @@ module Capybara
12
12
  :old_file_name
13
13
 
14
14
  def initialize(new_file_name, old_file_name = nil, dimensions: nil, color_distance_limit: nil,
15
- area_size_limit: nil)
15
+ area_size_limit: nil, shift_distance_limit: nil)
16
16
  @new_file_name = new_file_name
17
17
  @color_distance_limit = color_distance_limit
18
18
  @area_size_limit = area_size_limit
19
+ @shift_distance_limit = shift_distance_limit
19
20
  @dimensions = dimensions
20
21
  @old_file_name = old_file_name || "#{new_file_name}~"
21
22
  @annotated_old_file_name = "#{new_file_name.chomp('.png')}_0.png~"
@@ -27,6 +28,7 @@ module Capybara
27
28
  # Data about the original image is kept.
28
29
  def reset
29
30
  @max_color_distance = @color_distance_limit ? 0 : nil
31
+ @max_shift_distance = @shift_distance_limit ? 0 : nil
30
32
  @left = @top = @right = @bottom = nil
31
33
  end
32
34
 
@@ -100,17 +102,12 @@ module Capybara
100
102
  true
101
103
  end
102
104
 
103
- private def not_different
104
- clean_tmp_files
105
- false
106
- end
107
-
108
105
  def old_file_exists?
109
106
  @old_file_name && File.exist?(@old_file_name)
110
107
  end
111
108
 
112
109
  def old_file_size
113
- @_old_filesize ||= old_file_exists? && File.size(@old_file_name)
110
+ @old_file_size ||= old_file_exists? && File.size(@old_file_name)
114
111
  end
115
112
 
116
113
  def new_file_size
@@ -126,9 +123,20 @@ module Capybara
126
123
  end
127
124
 
128
125
  def max_color_distance
129
- return @max_color_distance if @max_color_distance
126
+ calculate_metrics unless @max_color_distance
127
+ @max_color_distance
128
+ end
129
+
130
+ def max_shift_distance
131
+ calculate_metrics unless @max_shift_distance
132
+ @max_shift_distance
133
+ end
134
+
135
+ private
136
+
137
+ def calculate_metrics
130
138
  old_file, new_file = load_image_files(@old_file_name, @new_file_name)
131
- return @max_color_distance = 0 if old_file == new_file
139
+ @max_color_distance = 0 if old_file == new_file
132
140
 
133
141
  old_image, new_image = load_images(old_file, new_file)
134
142
 
@@ -137,9 +145,17 @@ module Capybara
137
145
  d = ChunkyPNG::Color.euclidean_distance_rgba(p1, p2)
138
146
  [max, d].max
139
147
  end
148
+
149
+ (0...new_image.width).each do |_x|
150
+ (0...new_image.height).each do |y|
151
+ end
152
+ end
140
153
  end
141
154
 
142
- private
155
+ def not_different
156
+ clean_tmp_files
157
+ false
158
+ end
143
159
 
144
160
  def save_images(new_file_name, new_img, org_file_name, org_img)
145
161
  org_img.save(org_file_name)
@@ -170,7 +186,7 @@ module Capybara
170
186
  true
171
187
  end
172
188
 
173
- private def crop_images(images, dimensions)
189
+ def crop_images(images, dimensions)
174
190
  images.map! do |i|
175
191
  if i.dimension.to_a == dimensions || i.width < dimensions[0] || i.height < dimensions[1]
176
192
  i
@@ -180,7 +196,7 @@ module Capybara
180
196
  end
181
197
  end
182
198
 
183
- private def draw_rectangles(images, bottom, left, right, top)
199
+ def draw_rectangles(images, bottom, left, right, top)
184
200
  images.map do |image|
185
201
  new_img = image.dup
186
202
  new_img.rect(left - 1, top - 1, right + 1, bottom + 1, ChunkyPNG::Color.rgb(255, 0, 0))
@@ -188,13 +204,13 @@ module Capybara
188
204
  end
189
205
  end
190
206
 
191
- private def find_diff_rectangle(org_img, new_img)
207
+ def find_diff_rectangle(org_img, new_img)
192
208
  left, top, right, bottom = find_left_right_and_top(org_img, new_img)
193
209
  bottom = find_bottom(org_img, new_img, left, right, bottom)
194
210
  [left, top, right, bottom]
195
211
  end
196
212
 
197
- private def find_top(old_img, new_img)
213
+ def find_top(old_img, new_img)
198
214
  old_img.height.times do |y|
199
215
  old_img.width.times do |x|
200
216
  return [x, y, x, y] unless same_color?(old_img, new_img, x, y)
@@ -202,7 +218,7 @@ module Capybara
202
218
  end
203
219
  end
204
220
 
205
- private def find_left_right_and_top(old_img, new_img)
221
+ def find_left_right_and_top(old_img, new_img)
206
222
  top = @top
207
223
  bottom = @bottom
208
224
  left = @left || old_img.width - 1
@@ -226,7 +242,7 @@ module Capybara
226
242
  [left, top, right, bottom]
227
243
  end
228
244
 
229
- private def find_bottom(old_img, new_img, left, right, bottom)
245
+ def find_bottom(old_img, new_img, left, right, bottom)
230
246
  if bottom
231
247
  (old_img.height - 1).step(bottom + 1, -1).find do |y|
232
248
  (left..right).find do |x|
@@ -237,15 +253,95 @@ module Capybara
237
253
  bottom
238
254
  end
239
255
 
240
- private def same_color?(old_img, new_img, x, y)
256
+ def same_color?(old_img, new_img, x, y)
257
+ color_distance =
258
+ color_distance_at(new_img, old_img, x, y, shift_distance_limit: @shift_distance_limit)
259
+ shift_distance =
260
+ shift_distance_at(new_img, old_img, x, y, color_distance_limit: @color_distance_limit)
261
+ if !@max_color_distance || color_distance > @max_color_distance
262
+ @max_color_distance = color_distance
263
+ end
264
+ if !@max_shift_distance || shift_distance > @max_shift_distance
265
+ @max_shift_distance = shift_distance
266
+ end
267
+ (color_distance == 0 || (@color_distance_limit && @color_distance_limit > 0 &&
268
+ color_distance <= @color_distance_limit)) &&
269
+ (shift_distance == 0 || (@shift_distance_limit && @shift_distance_limit > 0 &&
270
+ shift_distance <= @shift_distance_limit))
271
+ end
272
+
273
+ def color_distance_at(new_img, old_img, x, y, shift_distance_limit:)
241
274
  org_color = old_img[x, y]
242
- new_color = new_img[x, y]
243
- return true if org_color == new_color
275
+ if shift_distance_limit
276
+ start_x = [0, x - shift_distance_limit].max
277
+ end_x = [x + shift_distance_limit, new_img.width - 1].min
278
+ xs = (start_x..end_x).to_a
279
+ start_y = [0, y - shift_distance_limit].max
280
+ end_y = [y + shift_distance_limit, new_img.height - 1].min
281
+ ys = (start_y..end_y).to_a
282
+ new_pixels = xs.product(ys)
283
+ distances = new_pixels.map do |dx, dy|
284
+ new_color = new_img[dx, dy]
285
+ ChunkyPNG::Color.euclidean_distance_rgba(org_color, new_color)
286
+ end
287
+ distances.min
288
+ else
289
+ new_color = new_img[x, y]
290
+ ChunkyPNG::Color.euclidean_distance_rgba(org_color, new_color)
291
+ end
292
+ end
244
293
 
245
- distance = ChunkyPNG::Color.euclidean_distance_rgba(org_color, new_color)
246
- @max_color_distance = distance if !@max_color_distance || distance > @max_color_distance
294
+ def shift_distance_at(new_img, old_img, x, y, color_distance_limit:)
295
+ org_color = old_img[x, y]
296
+ shift_distance = 0
297
+ loop do
298
+ if (y - shift_distance) >= 0 # top
299
+ ([0, x - shift_distance].max..[x + shift_distance, new_img.width - 1].min).each do |dx|
300
+ if color_matches(new_img, org_color, dx, y - shift_distance, color_distance_limit)
301
+ return shift_distance
302
+ end
303
+ end
304
+ end
305
+ if shift_distance > 0
306
+ if (x - shift_distance) >= 0 # left
307
+ ([0, y - shift_distance + 1].max..[y + shift_distance, new_img.height - 2].min)
308
+ .each do |dy|
309
+ if color_matches(new_img, org_color, x - shift_distance, dy, color_distance_limit)
310
+ return shift_distance
311
+ end
312
+ end
313
+ end
314
+ if (y + shift_distance) < new_img.height # bottom
315
+ ([0, x - shift_distance].max..[x + shift_distance, new_img.width - 1].min).each do |dx|
316
+ if color_matches(new_img, org_color, dx, y + shift_distance, color_distance_limit)
317
+ return shift_distance
318
+ end
319
+ end
320
+ end
321
+ if (x + shift_distance) < new_img.width # right
322
+ ([0, y - shift_distance + 1].max..[y + shift_distance, new_img.height - 2].min)
323
+ .each do |dy|
324
+ if color_matches(new_img, org_color, x + shift_distance, dy, color_distance_limit)
325
+ return shift_distance
326
+ end
327
+ end
328
+ end
329
+ end
330
+ shift_distance += 1
331
+ # puts "Bumped shift_distance to #{shift_distance}"
332
+ end
333
+ shift_distance
334
+ end
247
335
 
248
- @color_distance_limit && @color_distance_limit > 0 && distance <= @color_distance_limit
336
+ def color_matches(new_img, org_color, dx, dy, color_distance_limit)
337
+ new_color = new_img[dx, dy]
338
+ return new_color == org_color unless color_distance_limit
339
+ color_distance = ChunkyPNG::Color.euclidean_distance_rgba(org_color, new_color)
340
+ # if color_distance > 0
341
+ # puts "color_distance: #{dx} #{dy} #{color_distance} #{color_distance_limit} " \
342
+ # "#{color_distance <= color_distance_limit}"
343
+ # end
344
+ color_distance <= color_distance_limit
249
345
  end
250
346
  end
251
347
  end
@@ -1,9 +1,9 @@
1
1
  module Capybara
2
2
  module Screenshot
3
3
  module Os
4
- ON_WINDOWS = RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
5
- ON_MAC = RbConfig::CONFIG['host_os'] =~ /darwin/
6
- ON_LINUX = RbConfig::CONFIG['host_os'] =~ /linux/
4
+ ON_WINDOWS = !!(RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/)
5
+ ON_MAC = !!(RbConfig::CONFIG['host_os'] =~ /darwin/)
6
+ ON_LINUX = !!(RbConfig::CONFIG['host_os'] =~ /linux/)
7
7
 
8
8
  def os_name
9
9
  return 'windows' if ON_WINDOWS
@@ -18,7 +18,8 @@ module Capybara
18
18
  }()
19
19
  JS
20
20
 
21
- def take_stable_screenshot(comparison, color_distance_limit:, area_size_limit:)
21
+ def take_stable_screenshot(comparison, color_distance_limit:, shift_distance_limit:,
22
+ area_size_limit:)
22
23
  input = prepare_page_for_screenshot
23
24
  previous_file_name = comparison.old_file_name
24
25
  screenshot_started_at = last_image_change_at = Time.now
@@ -35,7 +36,8 @@ module Capybara
35
36
  if previous_file_name
36
37
  stabilization_comparison =
37
38
  ImageCompare.new(comparison.new_file_name, previous_file_name,
38
- color_distance_limit: color_distance_limit, area_size_limit: area_size_limit)
39
+ color_distance_limit: color_distance_limit, shift_distance_limit: shift_distance_limit,
40
+ area_size_limit: area_size_limit)
39
41
  if stabilization_comparison.quick_equal?
40
42
  if (Time.now - last_image_change_at) > Capybara::Screenshot.stability_time_limit
41
43
  clean_stabilization_images(comparison.new_file_name)
@@ -59,25 +61,30 @@ module Capybara
59
61
 
60
62
  private
61
63
 
62
- private def reduce_retina_image_size(file_name)
64
+ def reduce_retina_image_size(file_name)
63
65
  return if !ON_MAC || !selenium? || !Capybara::Screenshot.window_size
64
66
  saved_image = ChunkyPNG::Image.from_file(file_name)
65
67
  width = Capybara::Screenshot.window_size[0]
66
68
  return if saved_image.width < width * 2
69
+ unless @_csd_retina_warned
70
+ warn 'Halving retina screenshot. ' \
71
+ 'You should add "force-device-scale-factor=1" to your Chrome chromeOptions args.'
72
+ @_csd_retina_warned = true
73
+ end
67
74
  height = (width * saved_image.height) / saved_image.width
68
75
  resized_image = saved_image.resample_bilinear(width, height)
69
76
  resized_image.save(file_name)
70
77
  end
71
78
 
72
- private def stabilization_images(base_file)
79
+ def stabilization_images(base_file)
73
80
  Dir["#{base_file.chomp('.png')}_x*.png~"]
74
81
  end
75
82
 
76
- private def clean_stabilization_images(base_file)
83
+ def clean_stabilization_images(base_file)
77
84
  FileUtils.rm stabilization_images(base_file)
78
85
  end
79
86
 
80
- private def prepare_page_for_screenshot
87
+ def prepare_page_for_screenshot
81
88
  assert_images_loaded
82
89
  if Capybara::Screenshot.blur_active_element
83
90
  active_element = execute_script(<<-JS)
@@ -93,7 +100,7 @@ module Capybara
93
100
  input
94
101
  end
95
102
 
96
- private def take_right_size_screenshot(comparison)
103
+ def take_right_size_screenshot(comparison)
97
104
  save_screenshot(comparison.new_file_name)
98
105
 
99
106
  # TODO(uwe): Remove when chromedriver takes right size screenshots
@@ -101,13 +108,13 @@ module Capybara
101
108
  # ODOT
102
109
  end
103
110
 
104
- private def check_max_wait_time(comparison, screenshot_started_at)
111
+ def check_max_wait_time(comparison, screenshot_started_at)
105
112
  assert (Time.now - screenshot_started_at) < Capybara.default_max_wait_time,
106
113
  "Could not get stable screenshot within #{Capybara.default_max_wait_time}s\n" \
107
114
  "#{stabilization_images(comparison.new_file_name).join("\n")}"
108
115
  end
109
116
 
110
- private def assert_images_loaded(timeout: Capybara.default_max_wait_time)
117
+ def assert_images_loaded(timeout: Capybara.default_max_wait_time)
111
118
  return unless respond_to? :evaluate_script
112
119
  start = Time.now
113
120
  loop do
@@ -64,7 +64,7 @@ module Capybara
64
64
  end
65
65
 
66
66
  def screenshot(name, color_distance_limit: Diff.color_distance_limit,
67
- area_size_limit: Diff.area_size_limit)
67
+ shift_distance_limit: Diff.shift_distance_limit, area_size_limit: Diff.area_size_limit)
68
68
  return unless Screenshot.active?
69
69
  return if window_size_is_wrong?
70
70
  if @screenshot_counter
@@ -80,6 +80,7 @@ module Capybara
80
80
  area_size_limit: area_size_limit)
81
81
  checkout_vcs(name, comparison)
82
82
  take_stable_screenshot(comparison, color_distance_limit: color_distance_limit,
83
+ shift_distance_limit: shift_distance_limit,
83
84
  area_size_limit: area_size_limit)
84
85
  return unless comparison.old_file_exists?
85
86
  (@test_screenshots ||= []) << [caller(1..1).first, name, comparison]
@@ -108,7 +109,8 @@ module Capybara
108
109
  # ODOT
109
110
 
110
111
  "Screenshot does not match for '#{name}' (area: #{comparison.size}px #{comparison.dimensions}" \
111
- ", max_color_distance: #{max_color_distance})\n" \
112
+ ", max_color_distance: #{max_color_distance}" \
113
+ ", max_shift_distance: #{comparison.max_shift_distance})\n" \
112
114
  "#{comparison.new_file_name}\n#{comparison.annotated_old_file_name}\n" \
113
115
  "#{comparison.annotated_new_file_name}\n" \
114
116
  "at #{caller}"
@@ -117,4 +119,3 @@ module Capybara
117
119
  end
118
120
  end
119
121
  end
120
- # rubocop:enable Metrics/ClassLength
@@ -3,7 +3,7 @@
3
3
  module Capybara
4
4
  module Screenshot
5
5
  module Diff
6
- VERSION = '0.10.2'.freeze
6
+ VERSION = '0.11.0'.freeze
7
7
  end
8
8
  end
9
9
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capybara-screenshot-diff
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.2
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Uwe Kubosch
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-11-24 00:00:00.000000000 Z
11
+ date: 2018-08-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: '4.2'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '5.2'
22
+ version: '5.3'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +29,7 @@ dependencies:
29
29
  version: '4.2'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '5.2'
32
+ version: '5.3'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: capybara
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -120,14 +120,14 @@ dependencies:
120
120
  requirements:
121
121
  - - "~>"
122
122
  - !ruby/object:Gem::Version
123
- version: '0.39'
123
+ version: '0.54'
124
124
  type: :development
125
125
  prerelease: false
126
126
  version_requirements: !ruby/object:Gem::Requirement
127
127
  requirements:
128
128
  - - "~>"
129
129
  - !ruby/object:Gem::Version
130
- version: '0.39'
130
+ version: '0.54'
131
131
  - !ruby/object:Gem::Dependency
132
132
  name: simplecov
133
133
  requirement: !ruby/object:Gem::Requirement
@@ -153,6 +153,8 @@ files:
153
153
  - ".rubocop.yml"
154
154
  - ".rubocop_todo.yml"
155
155
  - ".travis.yml"
156
+ - CODE_OF_CONDUCT.md
157
+ - CONTRIBUTING.md
156
158
  - Gemfile
157
159
  - LICENSE.txt
158
160
  - README.md
@@ -193,7 +195,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
193
195
  version: '0'
194
196
  requirements: []
195
197
  rubyforge_project:
196
- rubygems_version: 2.6.13
198
+ rubygems_version: 2.6.14.1
197
199
  signing_key:
198
200
  specification_version: 4
199
201
  summary: Track your GUI changes with diff assertions