capybara-screenshot-diff 0.1.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +20 -0
- data/.rubocop_todo.yml +79 -0
- data/.travis.yml +11 -5
- data/README.md +56 -3
- data/Rakefile +1 -1
- data/capybara-screenshot-diff.gemspec +6 -13
- data/lib/capybara/screenshot/diff.rb +8 -2
- data/lib/capybara/screenshot/diff/capybara_setup.rb +96 -24
- data/lib/capybara/screenshot/diff/image_compare.rb +48 -38
- data/lib/capybara/screenshot/diff/version.rb +1 -1
- metadata +51 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d8c517212dfc9af8af735a354862dcaba9adc96a
|
4
|
+
data.tar.gz: 7828ae0bfa1c9aea2e8814288989288c43c8f64e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 27179f167cc8d6b77850033dd0e5291ae89e7dd4182586e22269a66619fca6e02d891e257c6c7041e51adb13a4b7097e8017e7c6e543ef7f11a6cf02fefef2e9
|
7
|
+
data.tar.gz: be58b147f1948e3136975f1320caa8cbfeeca7d5e5f27651bd8736fe31da35384d950165e8b560c0fdc4b2fe1975e382511ac565cc9f8314a2145982c11d3de0
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -1,8 +1,28 @@
|
|
1
1
|
inherit_from: .rubocop_todo.yml
|
2
2
|
|
3
|
+
AllCops:
|
4
|
+
DisplayCopNames: true
|
5
|
+
DisplayStyleGuide: true
|
6
|
+
|
7
|
+
Lint/Debugger:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
Metrics/ClassLength:
|
11
|
+
Max: 101
|
12
|
+
|
13
|
+
Metrics/LineLength:
|
14
|
+
Max: 110
|
15
|
+
|
3
16
|
Style/MultilineMethodCallIndentation:
|
4
17
|
EnforcedStyle: indented
|
5
18
|
IndentationWidth: 4
|
6
19
|
|
20
|
+
Style/NumericPredicate:
|
21
|
+
Enabled: false
|
22
|
+
|
7
23
|
Style/SignalException:
|
8
24
|
EnforcedStyle: semantic
|
25
|
+
|
26
|
+
Style/AlignParameters:
|
27
|
+
EnforcedStyle: with_fixed_indentation
|
28
|
+
IndentationWidth: 4
|
data/.rubocop_todo.yml
CHANGED
@@ -0,0 +1,79 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2016-04-22 14:35:36 +0200 using RuboCop version 0.39.0.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
8
|
+
|
9
|
+
Style/TrivialAccessors:
|
10
|
+
Enabled: false
|
11
|
+
|
12
|
+
# Offense count: 2
|
13
|
+
Lint/AmbiguousRegexpLiteral:
|
14
|
+
Exclude:
|
15
|
+
- 'lib/capybara/screenshot/diff/capybara_setup.rb'
|
16
|
+
|
17
|
+
# Offense count: 3
|
18
|
+
Metrics/AbcSize:
|
19
|
+
Max: 55
|
20
|
+
|
21
|
+
# Offense count: 1
|
22
|
+
Metrics/CyclomaticComplexity:
|
23
|
+
Max: 15
|
24
|
+
|
25
|
+
# Offense count: 3
|
26
|
+
# Configuration parameters: CountComments.
|
27
|
+
Metrics/MethodLength:
|
28
|
+
Max: 42
|
29
|
+
|
30
|
+
# Offense count: 1
|
31
|
+
# Configuration parameters: CountKeywordArgs.
|
32
|
+
Metrics/ParameterLists:
|
33
|
+
Max: 6
|
34
|
+
|
35
|
+
# Offense count: 1
|
36
|
+
Metrics/PerceivedComplexity:
|
37
|
+
Max: 17
|
38
|
+
|
39
|
+
# Offense count: 1
|
40
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
41
|
+
# SupportedStyles: nested, compact
|
42
|
+
Style/ClassAndModuleChildren:
|
43
|
+
Exclude:
|
44
|
+
- 'lib/capybara/screenshot/diff/capybara_setup.rb'
|
45
|
+
|
46
|
+
# Offense count: 2
|
47
|
+
Style/Documentation:
|
48
|
+
Exclude:
|
49
|
+
- 'spec/**/*'
|
50
|
+
- 'test/**/*'
|
51
|
+
- 'lib/capybara/screenshot/diff.rb'
|
52
|
+
- 'lib/capybara/screenshot/diff/image_compare.rb'
|
53
|
+
|
54
|
+
# Offense count: 1
|
55
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
56
|
+
# SupportedStyles: format, sprintf, percent
|
57
|
+
Style/FormatString:
|
58
|
+
Exclude:
|
59
|
+
- 'lib/capybara/screenshot/diff/capybara_setup.rb'
|
60
|
+
|
61
|
+
# Offense count: 1
|
62
|
+
# Configuration parameters: MinBodyLength.
|
63
|
+
Style/GuardClause:
|
64
|
+
Exclude:
|
65
|
+
- 'capybara-screenshot-diff.gemspec'
|
66
|
+
|
67
|
+
# Offense count: 1
|
68
|
+
# Cop supports --auto-correct.
|
69
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
|
70
|
+
# SupportedStyles: aligned, indented
|
71
|
+
Style/MultilineMethodCallIndentation:
|
72
|
+
Enabled: false
|
73
|
+
|
74
|
+
# Offense count: 1
|
75
|
+
# Cop supports --auto-correct.
|
76
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
|
77
|
+
# SupportedStyles: aligned, indented
|
78
|
+
Style/MultilineOperationIndentation:
|
79
|
+
Enabled: false
|
data/.travis.yml
CHANGED
@@ -1,10 +1,16 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
- jruby-9.0.4.0
|
4
|
-
- jruby-9.0.5.0
|
5
3
|
- jruby-head
|
4
|
+
- jruby-9.1.2.0
|
5
|
+
- jruby-9.1.5.0
|
6
|
+
- 2.3.1
|
7
|
+
- 2.2.5
|
6
8
|
- 2.1.8
|
7
|
-
- 2.2.4
|
8
|
-
- 2.3.0
|
9
9
|
- ruby-head
|
10
|
-
before_install: gem
|
10
|
+
before_install: gem query -i -n ^bundler$ >/dev/null || gem install bundler
|
11
|
+
|
12
|
+
env:
|
13
|
+
- JRUBY_OPTS="--dev --debug"
|
14
|
+
|
15
|
+
jdk:
|
16
|
+
- oraclejdk8
|
data/README.md
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
+
[![Build Status](https://travis-ci.org/donv/capybara-screenshot-diff.svg?branch=master)](https://travis-ci.org/donv/capybara-screenshot-diff)
|
2
|
+
|
1
3
|
# Capybara::Screenshot::Diff
|
2
4
|
|
3
5
|
Ever wondered what your project looked like two years ago? To answer that, you
|
4
6
|
start taking screen shots during your tests. Capybara provides the
|
5
7
|
`save_screenshot` method for this. Very good.
|
6
8
|
|
7
|
-
Ever introduced a graphical
|
8
|
-
Then this gem is
|
9
|
+
Ever introduced a graphical change unintended? Never want it to happen again?
|
10
|
+
Then this gem is for you! Use this gem to detect changes in your pages by
|
9
11
|
taking screen shots and comparing them to the previous revision.
|
10
12
|
|
11
13
|
## Installation
|
@@ -84,6 +86,44 @@ doc
|
|
84
86
|
02-action_performed
|
85
87
|
```
|
86
88
|
|
89
|
+
**All files in the screenshot group directory will be deleted when
|
90
|
+
`screenshot_group` is called.**
|
91
|
+
|
92
|
+
|
93
|
+
#### Screenshot sections
|
94
|
+
|
95
|
+
You can introduce another level above the screenshot group called a
|
96
|
+
`screenshot_section`. The section name is inserted just before the group name
|
97
|
+
in the save path. If called in the setup of the test, all screenshots in
|
98
|
+
that test will get the same prefix:
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
setup do
|
102
|
+
screenshot_section 'my_feature'
|
103
|
+
end
|
104
|
+
test 'my subfeature' do
|
105
|
+
screenshot_group 'subfeature'
|
106
|
+
visit '/feature'
|
107
|
+
click_button 'Interresting button'
|
108
|
+
screenshot 'subfeature_index'
|
109
|
+
click_button 'Perform action'
|
110
|
+
screenshot 'action_performed'
|
111
|
+
end
|
112
|
+
```
|
113
|
+
|
114
|
+
This will produce a sequence of images like this
|
115
|
+
|
116
|
+
```
|
117
|
+
doc
|
118
|
+
screenshots
|
119
|
+
my_feature
|
120
|
+
subfeature
|
121
|
+
00-subfeature_index
|
122
|
+
01-action_performed
|
123
|
+
```
|
124
|
+
|
125
|
+
|
126
|
+
|
87
127
|
### Multiple Capybara drivers
|
88
128
|
|
89
129
|
Often it is useful to test your app using different browsers. To avoid the
|
@@ -189,12 +229,25 @@ If you would like the screen shots to be saved in a different location set
|
|
189
229
|
Capybara::Screenshot.save_path = "#{Rails.root}/doc/gui"
|
190
230
|
```
|
191
231
|
|
232
|
+
### Screen shot stability
|
233
|
+
|
234
|
+
To ensure that animations are finished before saving a screen shot, you can add
|
235
|
+
a stability time limit. If the stability time limit is set, a second screen
|
236
|
+
shot will be taken and compared to the first. This is repeated until two
|
237
|
+
subsequent screen shots are identical.
|
238
|
+
|
239
|
+
```ruby
|
240
|
+
Capybara::Screenshot.stability_time_limit = 0.5
|
241
|
+
```
|
242
|
+
|
192
243
|
|
193
244
|
## Development
|
194
245
|
|
195
246
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
196
247
|
|
197
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
248
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
249
|
+
|
250
|
+
To release a new version, update the version number in `lib/capybara/screenshot/diff/version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
198
251
|
|
199
252
|
## Contributing
|
200
253
|
|
data/Rakefile
CHANGED
@@ -8,31 +8,24 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Capybara::Screenshot::Diff::VERSION
|
9
9
|
spec.authors = ['Uwe Kubosch']
|
10
10
|
spec.email = ['uwe@kubosch.no']
|
11
|
-
|
12
11
|
spec.summary = 'Track your GUI changes with diff assertions'
|
13
12
|
spec.description = 'Save screen shots and track changes with graphical diff'
|
14
13
|
spec.homepage = 'https://github.com/donv/capybara-screenshot-diff'
|
15
14
|
spec.license = 'MIT'
|
16
|
-
|
17
|
-
|
18
|
-
# delete this section to allow pushing this gem to any host.
|
19
|
-
if spec.respond_to?(:metadata)
|
20
|
-
spec.metadata['allowed_push_host'] = 'https://rubygems.org/'
|
21
|
-
else
|
22
|
-
fail 'RubyGems 2.0 or newer is required to protect against public gem push.'
|
23
|
-
end
|
24
|
-
|
25
|
-
spec.files = `git ls-files -z`.split("\x0")
|
26
|
-
.reject { |f| f.match(%r{^(test|spec|features)/}) }
|
15
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org/'
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
27
17
|
spec.bindir = 'exe'
|
28
18
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
29
19
|
spec.require_paths = ['lib']
|
30
20
|
|
21
|
+
spec.add_runtime_dependency 'actionpack', '~> 4.1'
|
31
22
|
spec.add_runtime_dependency 'capybara', '~> 2.0'
|
23
|
+
spec.add_runtime_dependency 'chunky_png', '~> 1.3'
|
32
24
|
|
33
25
|
spec.add_development_dependency 'bundler', '~> 1.11'
|
34
|
-
spec.add_development_dependency 'rake', '~> 10.0'
|
35
26
|
spec.add_development_dependency 'minitest', '~> 5.0'
|
36
27
|
spec.add_development_dependency 'minitest-reporters'
|
28
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
37
29
|
spec.add_development_dependency 'rubocop', '~> 0.39'
|
30
|
+
spec.add_development_dependency 'simplecov', '~> 0.11'
|
38
31
|
end
|
@@ -4,10 +4,16 @@ require 'capybara/screenshot/diff/capybara_setup'
|
|
4
4
|
|
5
5
|
module Capybara
|
6
6
|
module Screenshot
|
7
|
-
mattr_accessor :enabled
|
8
|
-
mattr_accessor :window_size
|
9
7
|
mattr_accessor :add_driver_path
|
10
8
|
mattr_accessor :add_os_path
|
9
|
+
mattr_accessor :enabled
|
10
|
+
mattr_accessor :screenshot_root
|
11
|
+
mattr_accessor :stability_time_limit
|
12
|
+
mattr_accessor :window_size
|
13
|
+
|
14
|
+
def self.active?
|
15
|
+
enabled || (enabled.nil? && Diff.enabled)
|
16
|
+
end
|
11
17
|
|
12
18
|
# Module to track screen shot changes
|
13
19
|
module Diff
|
@@ -1,7 +1,12 @@
|
|
1
1
|
require 'capybara'
|
2
2
|
require 'capybara/screenshot/diff/image_compare'
|
3
|
+
require 'action_controller'
|
4
|
+
require 'action_dispatch'
|
3
5
|
|
4
|
-
#
|
6
|
+
# TODO(uwe): Move this code to module Capybara::Screenshot::Diff::TestMethods,
|
7
|
+
# and use Module#prepend/include to insert.
|
8
|
+
# Add the `screenshot` method to ActionDispatch::IntegrationTest
|
9
|
+
# rubocop:disable Metrics/ClassLength
|
5
10
|
class ActionDispatch::IntegrationTest
|
6
11
|
ON_WINDOWS = RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
|
7
12
|
SILENCE_ERRORS = ON_WINDOWS ? '2>nul' : '2>/dev/null'
|
@@ -19,22 +24,51 @@ class ActionDispatch::IntegrationTest
|
|
19
24
|
end
|
20
25
|
end
|
21
26
|
|
22
|
-
def self.
|
27
|
+
def self.screenshot_root
|
28
|
+
Capybara::Screenshot.screenshot_root ||
|
29
|
+
(defined?(Rails.root) && Rails.root) || File.expand_path('.')
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.screenshot_area
|
23
33
|
parts = ['doc/screenshots']
|
24
34
|
parts << Capybara.default_driver.to_s if Capybara::Screenshot.add_driver_path
|
25
35
|
parts << os_name if Capybara::Screenshot.add_os_path
|
26
36
|
File.join parts
|
27
37
|
end
|
28
38
|
|
29
|
-
def self.
|
30
|
-
"#{
|
39
|
+
def self.screenshot_area_abs
|
40
|
+
"#{screenshot_root}/#{screenshot_area}".freeze
|
41
|
+
end
|
42
|
+
|
43
|
+
def initialize(*)
|
44
|
+
super
|
45
|
+
@screenshot_counter = nil
|
46
|
+
@screenshot_group = nil
|
47
|
+
@screenshot_section = nil
|
48
|
+
@test_screenshot_errors = nil
|
49
|
+
@test_screenshots = nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def group_parts
|
53
|
+
parts = []
|
54
|
+
parts << @screenshot_section if @screenshot_section.present?
|
55
|
+
parts << @screenshot_group if @screenshot_group.present?
|
56
|
+
parts
|
57
|
+
end
|
58
|
+
|
59
|
+
def full_name(name)
|
60
|
+
File.join group_parts.<<(name).map(&:to_s)
|
61
|
+
end
|
62
|
+
|
63
|
+
def screenshot_dir
|
64
|
+
File.join [self.class.screenshot_area] + group_parts
|
31
65
|
end
|
32
66
|
|
33
67
|
setup do
|
34
68
|
if Capybara::Screenshot.window_size
|
35
69
|
if Capybara.default_driver == :selenium
|
36
70
|
page.driver.browser.manage.window.resize_to(*Capybara::Screenshot.window_size)
|
37
|
-
|
71
|
+
elsif Capybara.default_driver == :poltergeist
|
38
72
|
page.driver.resize(*Capybara::Screenshot.window_size)
|
39
73
|
end
|
40
74
|
end
|
@@ -47,14 +81,19 @@ class ActionDispatch::IntegrationTest
|
|
47
81
|
fail(@test_screenshot_errors.join("\n\n")) if @test_screenshot_errors
|
48
82
|
end
|
49
83
|
|
84
|
+
def screenshot_section(name)
|
85
|
+
@screenshot_section = name.to_s
|
86
|
+
end
|
87
|
+
|
50
88
|
def screenshot_group(name)
|
51
|
-
@screenshot_group = name
|
89
|
+
@screenshot_group = name.to_s
|
52
90
|
@screenshot_counter = 0
|
53
|
-
|
91
|
+
return unless Capybara::Screenshot.active? && name.present?
|
92
|
+
FileUtils.rm_rf screenshot_dir
|
54
93
|
end
|
55
94
|
|
56
95
|
def screenshot(name)
|
57
|
-
return unless Capybara::Screenshot.
|
96
|
+
return unless Capybara::Screenshot.active?
|
58
97
|
if Capybara.default_driver == :selenium && Capybara::Screenshot.window_size
|
59
98
|
return unless page.driver.browser.manage.window
|
60
99
|
.size == Selenium::WebDriver::Dimension.new(*Capybara::Screenshot.window_size)
|
@@ -63,46 +102,79 @@ class ActionDispatch::IntegrationTest
|
|
63
102
|
name = "#{'%02i' % @screenshot_counter}_#{name}"
|
64
103
|
@screenshot_counter += 1
|
65
104
|
end
|
66
|
-
name =
|
67
|
-
file_name = "#{self.class.
|
68
|
-
org_name = "#{self.class.
|
69
|
-
new_name = "#{self.class.
|
105
|
+
name = full_name(name)
|
106
|
+
file_name = "#{self.class.screenshot_area_abs}/#{name}.png"
|
107
|
+
org_name = "#{self.class.screenshot_area_abs}/#{name}_0.png~"
|
108
|
+
new_name = "#{self.class.screenshot_area_abs}/#{name}_1.png~"
|
70
109
|
|
71
110
|
FileUtils.mkdir_p File.dirname(file_name)
|
72
|
-
svn_file_name = "#{self.class.
|
111
|
+
svn_file_name = "#{self.class.screenshot_area_abs}/.svn/text-base/#{name}.png.svn-base"
|
73
112
|
if File.exist?(svn_file_name)
|
74
113
|
committed_file_name = svn_file_name
|
75
114
|
else
|
76
115
|
svn_info = `svn info #{file_name} #{SILENCE_ERRORS}`
|
77
116
|
if svn_info.present?
|
78
|
-
wc_root = svn_info.slice
|
79
|
-
checksum = svn_info.slice
|
117
|
+
wc_root = svn_info.slice(/(?<=Working Copy Root Path: ).*$/)
|
118
|
+
checksum = svn_info.slice(/(?<=Checksum: ).*$/)
|
80
119
|
if checksum
|
81
120
|
committed_file_name = "#{wc_root}/.svn/pristine/#{checksum[0..1]}/#{checksum}.svn-base"
|
82
121
|
end
|
83
122
|
else
|
84
123
|
committed_file_name = org_name
|
85
|
-
`git show HEAD~0:#{self.class.
|
124
|
+
`git show HEAD~0:#{self.class.screenshot_area}/#{name}.png > #{committed_file_name} #{SILENCE_ERRORS}`
|
86
125
|
if File.size(committed_file_name) == 0
|
87
126
|
FileUtils.rm_f committed_file_name
|
88
127
|
end
|
89
128
|
end
|
90
129
|
end
|
130
|
+
take_stable_screenshot(file_name)
|
131
|
+
return unless committed_file_name && File.exist?(committed_file_name)
|
132
|
+
(@test_screenshots ||= []) << [caller[0], name, file_name, committed_file_name, new_name, org_name]
|
133
|
+
end
|
134
|
+
|
135
|
+
IMAGE_WAIT_SCRIPT = <<EOF.freeze
|
136
|
+
function pending_image() {
|
137
|
+
var images = document.images;
|
138
|
+
for (var i = 0; i < images.length; i++) {
|
139
|
+
if (!images[i].complete) {
|
140
|
+
return images[i].src;
|
141
|
+
}
|
142
|
+
}
|
143
|
+
return false;
|
144
|
+
}()
|
145
|
+
EOF
|
146
|
+
|
147
|
+
def assert_images_loaded(timeout: Capybara.default_max_wait_time)
|
148
|
+
return unless respond_to? :evaluate_script
|
149
|
+
start = Time.now
|
150
|
+
loop do
|
151
|
+
pending_image = evaluate_script IMAGE_WAIT_SCRIPT
|
152
|
+
break unless pending_image
|
153
|
+
assert (Time.now - start) < timeout,
|
154
|
+
"Image not loaded after #{timeout}s: #{pending_image.inspect}"
|
155
|
+
sleep 0.1
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def take_stable_screenshot(file_name)
|
160
|
+
assert_images_loaded
|
91
161
|
old_file_size = nil
|
92
162
|
loop do
|
93
|
-
|
94
|
-
break
|
95
|
-
|
96
|
-
|
163
|
+
save_screenshot(file_name)
|
164
|
+
break unless Capybara::Screenshot.stability_time_limit
|
165
|
+
new_file_size = File.size(file_name)
|
166
|
+
break if new_file_size == old_file_size
|
167
|
+
old_file_size = new_file_size
|
168
|
+
sleep Capybara::Screenshot.stability_time_limit
|
97
169
|
end
|
98
|
-
return unless File.exist?(committed_file_name)
|
99
|
-
(@test_screenshots ||= []) << [caller[0], name, file_name, committed_file_name, new_name, org_name]
|
100
170
|
end
|
101
171
|
|
102
172
|
def assert_image_not_changed(caller, name, file_name, committed_file_name, new_name, org_name)
|
103
|
-
if Capybara::Screenshot::Diff::ImageCompare.compare(
|
173
|
+
if Capybara::Screenshot::Diff::ImageCompare.compare(committed_file_name, file_name,
|
174
|
+
Capybara::Screenshot.window_size)
|
104
175
|
(@test_screenshot_errors ||= []) <<
|
105
|
-
"Screenshot does not match for #{name
|
176
|
+
"Screenshot does not match for '#{name}'\n#{file_name}\n#{org_name}\n#{new_name}\nat #{caller}"
|
106
177
|
end
|
107
178
|
end
|
108
179
|
end
|
180
|
+
# rubocop:enable Metrics/ClassLength
|
@@ -6,21 +6,31 @@ module Capybara
|
|
6
6
|
class ImageCompare
|
7
7
|
include ChunkyPNG::Color
|
8
8
|
|
9
|
-
def self.compare(
|
10
|
-
|
9
|
+
def self.compare(*args)
|
10
|
+
new(*args).compare
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(old_file_name, new_file_name, dimensions = nil)
|
14
|
+
@old_file_name = old_file_name
|
15
|
+
@file_name = new_file_name
|
16
|
+
@dimensions = dimensions
|
17
|
+
end
|
18
|
+
|
19
|
+
def compare
|
20
|
+
name = @file_name.chomp('.png')
|
11
21
|
org_file_name = "#{name}_0.png~"
|
12
22
|
new_file_name = "#{name}_1.png~"
|
13
23
|
|
14
|
-
return nil unless File.exist? old_file_name
|
24
|
+
return nil unless File.exist? @old_file_name
|
15
25
|
|
16
|
-
images = load_images(old_file_name, file_name)
|
26
|
+
images = load_images(@old_file_name, @file_name)
|
17
27
|
|
18
28
|
unless images
|
19
29
|
clean_tmp_files(new_file_name, org_file_name)
|
20
30
|
return false
|
21
31
|
end
|
22
32
|
|
23
|
-
crop_images(images, dimensions) if dimensions
|
33
|
+
crop_images(images, @dimensions) if @dimensions
|
24
34
|
org_img = images.first
|
25
35
|
new_img = images.last
|
26
36
|
if sizes_changed?(org_img, new_img, name)
|
@@ -33,41 +43,45 @@ module Capybara
|
|
33
43
|
return false
|
34
44
|
end
|
35
45
|
|
36
|
-
|
37
|
-
draw_rectangles(images, bottom, left, right, top)
|
46
|
+
@left, @top, @right, @bottom = find_diff_rectangle(org_img, new_img)
|
47
|
+
draw_rectangles(images, @bottom, @left, @right, @top)
|
38
48
|
save_images(new_file_name, new_img, org_file_name, org_img)
|
39
49
|
true
|
40
50
|
end
|
41
51
|
|
42
|
-
def
|
52
|
+
def dimensions
|
53
|
+
[@left, @top, @right, @bottom]
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def save_images(new_file_name, new_img, org_file_name, org_img)
|
43
59
|
org_img.save(org_file_name)
|
44
60
|
new_img.save(new_file_name)
|
45
61
|
end
|
46
62
|
|
47
|
-
def
|
48
|
-
File.delete(org_file_name) if File.
|
49
|
-
File.delete(new_file_name) if File.
|
63
|
+
def clean_tmp_files(new_file_name, org_file_name)
|
64
|
+
File.delete(org_file_name) if File.exist?(org_file_name)
|
65
|
+
File.delete(new_file_name) if File.exist?(new_file_name)
|
50
66
|
end
|
51
67
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
old_file = File.read(old_file_name)
|
56
|
-
new_file = File.read(file_name)
|
68
|
+
def load_images(old_file_name, file_name)
|
69
|
+
old_file = File.binread(old_file_name)
|
70
|
+
new_file = File.binread(file_name)
|
57
71
|
|
58
72
|
return false if old_file == new_file
|
59
73
|
|
60
74
|
[ChunkyPNG::Image.from_blob(old_file), ChunkyPNG::Image.from_blob(new_file)]
|
61
75
|
end
|
62
76
|
|
63
|
-
def
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
77
|
+
def sizes_changed?(org_image, new_image, name)
|
78
|
+
return unless org_image.dimension != new_image.dimension
|
79
|
+
change_msg = [org_image, new_image].map { |i| "#{i.width}x#{i.height}" }.join(' => ')
|
80
|
+
puts "Image size has changed for #{name}: #{change_msg}"
|
81
|
+
true
|
68
82
|
end
|
69
83
|
|
70
|
-
def
|
84
|
+
def crop_images(images, dimensions)
|
71
85
|
images.map! do |i|
|
72
86
|
if i.dimension.to_a == dimensions || i.width < dimensions[0] || i.height < dimensions[1]
|
73
87
|
i
|
@@ -77,26 +91,24 @@ module Capybara
|
|
77
91
|
end
|
78
92
|
end
|
79
93
|
|
80
|
-
def
|
81
|
-
|
82
|
-
|
83
|
-
image.rect(left - 1, top - 1, right + 1, bottom + 1, ChunkyPNG::Color.rgb(255, 0, 0))
|
84
|
-
end
|
94
|
+
def draw_rectangles(images, bottom, left, right, top)
|
95
|
+
images.each do |image|
|
96
|
+
image.rect(left - 1, top - 1, right + 1, bottom + 1, ChunkyPNG::Color.rgb(255, 0, 0))
|
85
97
|
end
|
86
98
|
end
|
87
99
|
|
88
|
-
def
|
100
|
+
def find_diff_rectangle(org_img, new_img)
|
89
101
|
top = bottom = nil
|
90
102
|
left = org_img.width
|
91
103
|
right = -1
|
92
104
|
org_img.height.times do |y|
|
93
105
|
(0...left).find do |x|
|
94
|
-
if org_img[x, y]
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
106
|
+
next if org_img[x, y] == new_img[x, y]
|
107
|
+
top ||= y
|
108
|
+
bottom = y
|
109
|
+
left = x
|
110
|
+
right = x if x > right
|
111
|
+
x
|
100
112
|
end
|
101
113
|
(org_img.width - 1).step(right + 1, -1).find do |x|
|
102
114
|
if org_img[x, y] != new_img[x, y]
|
@@ -107,12 +119,10 @@ module Capybara
|
|
107
119
|
end
|
108
120
|
(org_img.height - 1).step(bottom + 1, -1).find do |y|
|
109
121
|
((left + 1)..(right - 1)).find do |x|
|
110
|
-
if org_img[x, y] != new_img[x, y]
|
111
|
-
bottom = y
|
112
|
-
end
|
122
|
+
bottom = y if org_img[x, y] != new_img[x, y]
|
113
123
|
end
|
114
124
|
end
|
115
|
-
|
125
|
+
[left, top, right, bottom]
|
116
126
|
end
|
117
127
|
end
|
118
128
|
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: capybara-screenshot-diff
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Uwe Kubosch
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-12-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: actionpack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.1'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: capybara
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -25,33 +39,33 @@ dependencies:
|
|
25
39
|
- !ruby/object:Gem::Version
|
26
40
|
version: '2.0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
42
|
+
name: chunky_png
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
45
|
- - "~>"
|
32
46
|
- !ruby/object:Gem::Version
|
33
|
-
version: '1.
|
34
|
-
type: :
|
47
|
+
version: '1.3'
|
48
|
+
type: :runtime
|
35
49
|
prerelease: false
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
37
51
|
requirements:
|
38
52
|
- - "~>"
|
39
53
|
- !ruby/object:Gem::Version
|
40
|
-
version: '1.
|
54
|
+
version: '1.3'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
56
|
+
name: bundler
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
59
|
- - "~>"
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
61
|
+
version: '1.11'
|
48
62
|
type: :development
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
66
|
- - "~>"
|
53
67
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
68
|
+
version: '1.11'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: minitest
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,6 +94,20 @@ dependencies:
|
|
80
94
|
- - ">="
|
81
95
|
- !ruby/object:Gem::Version
|
82
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rake
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '10.0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '10.0'
|
83
111
|
- !ruby/object:Gem::Dependency
|
84
112
|
name: rubocop
|
85
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,6 +122,20 @@ dependencies:
|
|
94
122
|
- - "~>"
|
95
123
|
- !ruby/object:Gem::Version
|
96
124
|
version: '0.39'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: simplecov
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0.11'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0.11'
|
97
139
|
description: Save screen shots and track changes with graphical diff
|
98
140
|
email:
|
99
141
|
- uwe@kubosch.no
|