capybara-screenshot-diff 0.1.0 → 0.2.1
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 +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
|
+
[](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
|