cezanne 0.0.3 → 0.1.0
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/LICENSE +30 -0
- data/README.md +8 -4
- data/cezanne.gemspec +1 -0
- data/lib/cezanne/comparison.rb +2 -5
- data/lib/cezanne/config.rb +33 -0
- data/lib/cezanne/image.rb +20 -2
- data/lib/cezanne/remote_files.rb +4 -0
- data/lib/cezanne/rspec.rb +7 -25
- data/lib/cezanne/version.rb +1 -1
- data/lib/cezanne.rb +3 -1
- data/spec/cezanne_filesystem_spec.rb +10 -0
- data/spec/cezanne_image_spec.rb +48 -4
- data/spec/cezanne_rspec_spec.rb +94 -63
- data/spec/cezanne_spec.rb +70 -5
- data/spec/images/different.html +1 -1
- data/spec/images/page_name_2_browser_version.gif +0 -0
- data/spec/images/page_name_browser_version.gif +0 -0
- metadata +18 -3
- data/LICENSE.txt +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 48f5a265febb456ad0009ecb84e3694193f35102
|
4
|
+
data.tar.gz: b58c201977a549037e09bb46f5f7215469c93044
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a3f03972e4a378d9b696b70cb358e7a7cf2c4cf2769e9cebebeac56e212b923fd1f31669578c554440f50fb67b49ead5cc3677d335874a43576629249b7468c7
|
7
|
+
data.tar.gz: cda92d1bf25ae8cccb9f62f121161d0ff030e8f0e7fcf4e95a77d950d18bc80b6734369870bf020428cd6beb8960b93a9606b6dfe8652550aa54c7623f09a718
|
data/.gitignore
CHANGED
data/LICENSE
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
BSD License
|
2
|
+
|
3
|
+
For Cezanne software
|
4
|
+
|
5
|
+
Copyright (c) 2014, BSkyB. All rights reserved.
|
6
|
+
|
7
|
+
Redistribution and use in source and binary forms, with or without
|
8
|
+
modification, are permitted provided that the following conditions are met:
|
9
|
+
|
10
|
+
* Redistributions of source code must retain the above copyright notice, this
|
11
|
+
list of conditions and the following disclaimer.
|
12
|
+
|
13
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
14
|
+
this list of conditions and the following disclaimer in the documentation
|
15
|
+
and/or other materials provided with the distribution.
|
16
|
+
|
17
|
+
* Neither the name BSkyB nor the names of its contributors may be used to
|
18
|
+
endorse or promote products derived from this software without specific
|
19
|
+
prior written permission.
|
20
|
+
|
21
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
22
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
23
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
24
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
25
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
26
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
27
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
28
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
29
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
30
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
[![Build Status](https://travis-ci.org/
|
1
|
+
[![Build Status](https://travis-ci.org/BSkyB/cezanne.svg?branch=master)](https://travis-ci.org/BSkyB/cezanne)
|
2
2
|
|
3
3
|
# Cezanne
|
4
4
|
|
@@ -26,10 +26,14 @@ In your spec_helper.rb
|
|
26
26
|
|
27
27
|
RSpec.configure do |config|
|
28
28
|
config.include Cezanne
|
29
|
-
config.cezanne = { uid: ENV['build_number'], project_name: 'awesome_app' }
|
30
29
|
end
|
31
30
|
|
32
|
-
|
31
|
+
Cezanne.configure do |config|
|
32
|
+
config.uid = ENV['build_number']
|
33
|
+
config.project_name = 'awesome_app'
|
34
|
+
end
|
35
|
+
|
36
|
+
The should be a unique identifier. We use the build number, but it can be a static string if you don't need
|
33
37
|
to keep multiple versions of the screenshots.
|
34
38
|
|
35
39
|
In your tests
|
@@ -48,7 +52,7 @@ and the browser name & version, to make it easy to check visual regressions on m
|
|
48
52
|
|
49
53
|
## Dependencies
|
50
54
|
|
51
|
-
Cezanne uses ImageMagick
|
55
|
+
Cezanne uses ImageMagick. Check with your package manager.
|
52
56
|
|
53
57
|
Screenshots are stored on Dropbox through the Dropscreen gem. Follow the instructions at https://github.com/bskyb-commerce/dropscreen and make sure to export the access_token
|
54
58
|
|
data/cezanne.gemspec
CHANGED
data/lib/cezanne/comparison.rb
CHANGED
@@ -1,14 +1,11 @@
|
|
1
|
-
require 'RMagick'
|
2
|
-
|
3
1
|
module Cezanne
|
4
2
|
|
5
|
-
SIMILARITY_THRESHOLD = 42
|
6
|
-
|
7
3
|
def spot_differences_between this, that
|
8
4
|
width = [this.width, that.width].min
|
9
5
|
height = [this.height, that.height].min
|
10
6
|
[this, that].each { |img| img.crop!(width, height) }
|
11
|
-
|
7
|
+
|
8
|
+
not(this.duplicate? that)
|
12
9
|
end
|
13
10
|
|
14
11
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Cezanne
|
2
|
+
|
3
|
+
Config = Struct.new(:uid, :project_name, :local_root, :remote_root, :local_files, :remote_files, :comparison_method, :similarity_threshold)
|
4
|
+
|
5
|
+
def self.config
|
6
|
+
@config ||= Cezanne::Config.new
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
def self.configure
|
11
|
+
config = Cezanne.config
|
12
|
+
|
13
|
+
yield config if block_given?
|
14
|
+
|
15
|
+
config.comparison_method ||= :peak_signal_to_noise_ratio
|
16
|
+
config.similarity_threshold ||= (config.comparison_method.eql?(:peak_signal_to_noise_ratio) ? 42 : 15)
|
17
|
+
config.local_root ||= 'artifacts'
|
18
|
+
config.remote_root ||= config.project_name
|
19
|
+
config.local_files ||= Cezanne::LocalFiles.new(config.uid, config.local_root)
|
20
|
+
config.remote_files ||= Cezanne::RemoteFiles.new(config.uid, config.remote_root)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def local_files
|
26
|
+
Cezanne.config.local_files
|
27
|
+
end
|
28
|
+
|
29
|
+
def remote_files
|
30
|
+
Cezanne.config.remote_files
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/lib/cezanne/image.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
require 'RMagick'
|
2
|
+
require 'phashion'
|
2
3
|
|
3
4
|
module Cezanne
|
4
5
|
|
5
6
|
class Image
|
6
7
|
attr_reader :path, :picture
|
7
8
|
|
8
|
-
def initialize path, opts
|
9
|
+
def initialize path, opts = {}
|
9
10
|
@path = path
|
10
11
|
@picture = Magick::Image.read(@path).first
|
11
12
|
if opts[:crop]
|
@@ -27,6 +28,23 @@ module Cezanne
|
|
27
28
|
def height
|
28
29
|
@picture.rows
|
29
30
|
end
|
30
|
-
end
|
31
31
|
|
32
|
+
def duplicate? other
|
33
|
+
send(Cezanne.config.comparison_method, other)
|
34
|
+
end
|
35
|
+
|
36
|
+
alias_method :==, :duplicate?
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def peak_signal_to_noise_ratio other
|
41
|
+
@picture.compare_channel(other.picture, Magick::PeakSignalToNoiseRatioMetric)[1] > Cezanne.config.similarity_threshold
|
42
|
+
end
|
43
|
+
|
44
|
+
def phash_hamming_distance other
|
45
|
+
Phashion::Image.new(@path).duplicate? Phashion::Image.new(other.path), threshold: Cezanne.config.similarity_threshold
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
32
50
|
end
|
data/lib/cezanne/remote_files.rb
CHANGED
data/lib/cezanne/rspec.rb
CHANGED
@@ -3,41 +3,23 @@ require 'rspec/core'
|
|
3
3
|
|
4
4
|
RSpec.configure do |config|
|
5
5
|
|
6
|
-
config.add_setting :cezanne
|
7
|
-
|
8
6
|
config.before(:all, screenshots: true) do
|
9
7
|
if self.class.include?(Cezanne)
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
config.cezanne[:remote_files].pull(:ref, config.cezanne[:local_files].path_for(:ref))
|
16
|
-
rescue
|
17
|
-
puts "no reference screenshot exist for project #{project_name}"
|
18
|
-
end
|
8
|
+
if Cezanne.config.remote_files.exists? :ref
|
9
|
+
Cezanne.config.remote_files.pull(:ref, Cezanne.config.local_files.path_for(:ref))
|
10
|
+
else
|
11
|
+
Cezanne.config.remote_files.push(Cezanne.config.local_files.path_for(:ref), :ref)
|
12
|
+
end
|
19
13
|
end
|
20
14
|
end
|
21
15
|
|
22
16
|
config.after(:all, screenshots: true) do
|
23
17
|
if self.class.include?(Cezanne)
|
24
18
|
[:new, :diff].each do |key|
|
25
|
-
config.
|
19
|
+
Cezanne.config.remote_files.push(Cezanne.config.local_files.path_for(key), key) unless Dir.glob(Cezanne.config.local_files.path_for(key)).empty?
|
26
20
|
end
|
27
|
-
config.
|
21
|
+
Cezanne.config.local_files.clean
|
28
22
|
end
|
29
23
|
end
|
30
24
|
|
31
25
|
end
|
32
|
-
|
33
|
-
module Cezanne
|
34
|
-
|
35
|
-
def local_files
|
36
|
-
RSpec.configuration.cezanne[:local_files]
|
37
|
-
end
|
38
|
-
|
39
|
-
def remote_files
|
40
|
-
RSpec.configuration.cezanne[:remote_files]
|
41
|
-
end
|
42
|
-
|
43
|
-
end
|
data/lib/cezanne/version.rb
CHANGED
data/lib/cezanne.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
require "cezanne/version"
|
2
|
+
require "cezanne/config"
|
2
3
|
require "cezanne/local_files"
|
3
4
|
require "cezanne/remote_files"
|
4
5
|
require "cezanne/image"
|
5
6
|
require "cezanne/comparison"
|
6
7
|
|
7
8
|
module Cezanne
|
9
|
+
|
8
10
|
|
9
11
|
def check_visual_regression_for page_name, opts = {}
|
10
12
|
screenshot = take_screenshot page_name, opts
|
@@ -25,7 +27,7 @@ module Cezanne
|
|
25
27
|
|
26
28
|
private
|
27
29
|
|
28
|
-
def take_screenshot page_name, opts
|
30
|
+
def take_screenshot page_name, opts = {}
|
29
31
|
path = File.join( local_files.path_for(:tmp), file_name_for(page_name) )
|
30
32
|
page.driver.browser.save_screenshot(path)
|
31
33
|
image(path, opts)
|
@@ -71,5 +71,15 @@ describe Cezanne::RemoteFiles do
|
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
74
|
+
describe '#exists?' do
|
75
|
+
|
76
|
+
it 'is a proxy to Dropscreen::Client#exists?' do
|
77
|
+
expect(dropscreen_client).to receive(:exists?).at_least(1).times.with(kind_of(String))
|
78
|
+
[:ref, :diff, :new].each do |key|
|
79
|
+
@remote_files.exists? key
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
74
84
|
end
|
75
85
|
|
data/spec/cezanne_image_spec.rb
CHANGED
@@ -4,6 +4,7 @@ describe Cezanne::Image do
|
|
4
4
|
|
5
5
|
let(:image) { Cezanne::Image.new('spec/images/page_name_browser_version.gif', {}) }
|
6
6
|
|
7
|
+
|
7
8
|
describe '#initialize' do
|
8
9
|
|
9
10
|
it 'wraps Magick::Image' do
|
@@ -11,14 +12,14 @@ describe Cezanne::Image do
|
|
11
12
|
end
|
12
13
|
|
13
14
|
it 'return an error if there is no file at path' do
|
14
|
-
expect { Cezanne::Image.new('
|
15
|
+
expect { Cezanne::Image.new('inexistent_file_path') }.to raise_error
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
18
19
|
describe '#width' do
|
19
20
|
|
20
21
|
it 'has a width' do
|
21
|
-
expect(image.width).to be
|
22
|
+
expect(image.width).to be 712
|
22
23
|
end
|
23
24
|
|
24
25
|
end
|
@@ -33,14 +34,18 @@ describe Cezanne::Image do
|
|
33
34
|
|
34
35
|
describe '#crop!' do
|
35
36
|
|
37
|
+
before(:each) do
|
38
|
+
FileUtils.cp('spec/images/page_name_browser_version.gif', 'spec/images/page_name_browser_version.bak')
|
39
|
+
end
|
40
|
+
|
36
41
|
it 'accepts 4 parameters x, y, width, height' do
|
37
42
|
x = 1
|
38
43
|
y = 2
|
39
44
|
width = 5
|
40
45
|
height = 10
|
41
46
|
image.crop!(x,y,width,height)
|
42
|
-
expect(image.width).to be
|
43
|
-
expect(image.height).to be
|
47
|
+
expect(image.width).to be 5
|
48
|
+
expect(image.height).to be 10
|
44
49
|
end
|
45
50
|
|
46
51
|
it 'accepts width and height alone' do
|
@@ -51,5 +56,44 @@ describe Cezanne::Image do
|
|
51
56
|
expect(image.height).to be 5
|
52
57
|
end
|
53
58
|
|
59
|
+
it 'write the cropped image to disk' do
|
60
|
+
expect(image.picture).to receive(:write)
|
61
|
+
image.crop!(10,10)
|
62
|
+
end
|
63
|
+
|
64
|
+
after(:each) do
|
65
|
+
FileUtils.mv('spec/images/page_name_browser_version.bak', 'spec/images/page_name_browser_version.gif', force: true)
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#duplicate?' do
|
71
|
+
let(:other_image) { double('Cezanne::Image') }
|
72
|
+
|
73
|
+
it 'compare two images' do
|
74
|
+
Cezanne.config.comparison_method = :comparison_method
|
75
|
+
allow(image).to receive(:comparison_method)
|
76
|
+
expect(image).to receive(:comparison_method).with(other_image)
|
77
|
+
image.duplicate? other_image
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'can use peak signal to noise ratio' do
|
81
|
+
Cezanne.config.comparison_method = :peak_signal_to_noise_ratio
|
82
|
+
Cezanne.config.similarity_threshold = 42
|
83
|
+
|
84
|
+
allow(other_image).to receive(:picture)
|
85
|
+
expect(image.picture).to receive(:compare_channel).with(other_image.picture, Magick::PeakSignalToNoiseRatioMetric).and_return([nil, 10])
|
86
|
+
image.duplicate? other_image
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'can use phash hamming distance' do
|
90
|
+
Cezanne.config.comparison_method = :phash_hamming_distance
|
91
|
+
Cezanne.config.similarity_threshold = 15
|
92
|
+
|
93
|
+
allow(other_image).to receive(:path)
|
94
|
+
expect_any_instance_of(Phashion::Image).to receive(:duplicate?)
|
95
|
+
image.duplicate? other_image
|
96
|
+
end
|
97
|
+
|
54
98
|
end
|
55
99
|
end
|
data/spec/cezanne_rspec_spec.rb
CHANGED
@@ -1,100 +1,131 @@
|
|
1
1
|
require 'cezanne/rspec'
|
2
2
|
require 'capybara/rspec'
|
3
3
|
require 'selenium-webdriver'
|
4
|
-
require 'pry'
|
5
|
-
|
6
4
|
|
7
5
|
Capybara.app = Rack::Directory.new('spec/images')
|
8
6
|
Capybara.default_driver = :selenium
|
9
7
|
|
10
8
|
RSpec.configure do |config|
|
11
9
|
config.include Cezanne
|
12
|
-
config.cezanne = { uid: 'test', project_name: 'cezanne' }
|
13
10
|
end
|
14
11
|
|
15
|
-
describe 'Cezanne RSpec integration', type: :feature, screenshots: true, integration: true do
|
16
|
-
|
17
|
-
# wrap each test with before(:all) and after(:all)
|
18
12
|
|
19
|
-
|
20
|
-
RSpec.configuration.after(:all, screenshots: true).last.instance_variable_set(:@block, Proc.new {}) # disable the after(:all) hook
|
21
|
-
end
|
13
|
+
describe 'Cezanne RSpec integration', type: :feature, integration: true do
|
22
14
|
|
23
|
-
before(:
|
24
|
-
|
15
|
+
before(:all) do
|
16
|
+
Cezanne.configure do |config|
|
17
|
+
config.uid = 'test'
|
18
|
+
config.project_name = 'cezanne'
|
19
|
+
end
|
25
20
|
end
|
26
21
|
|
27
|
-
|
28
|
-
|
29
|
-
|
22
|
+
context('Screenshot tests', screenshots: true) do
|
23
|
+
|
24
|
+
after(:all) do |example|
|
25
|
+
RSpec.configuration.after(:all, screenshots: true).last.instance_variable_set(:@block, Proc.new {}) # disable the after(:all) hook
|
26
|
+
Cezanne.config.local_files.clean
|
27
|
+
end
|
28
|
+
|
29
|
+
before(:each) do |example|
|
30
|
+
RSpec.configuration.before(:all, screenshots: true).first.run(example) unless example.metadata[:before_hook_test]
|
31
|
+
end
|
30
32
|
|
33
|
+
after(:each) do |example|
|
34
|
+
RSpec.configuration.after(:all, screenshots: true).last.run(example) unless example.metadata[:after_hook_test]
|
35
|
+
# the after(:all) hook that we just called will clean the local folders, so we need to recreate them so next test will find them
|
36
|
+
Cezanne.config.local_files = Cezanne::LocalFiles.new(Cezanne.config.uid, Cezanne.config.local_root)
|
37
|
+
end
|
31
38
|
|
39
|
+
describe 'initialization' do
|
40
|
+
|
41
|
+
it 'create local folders' do
|
42
|
+
expect(File.exist?('artifacts/reference_screenshots')).to be true
|
43
|
+
expect(File.exist?('artifacts/test/tmp_screenshots')).to be true
|
44
|
+
expect(File.exist?('artifacts/test/different_screenshots')).to be true
|
45
|
+
expect(File.exist?('artifacts/test/new_screenshots')).to be true
|
46
|
+
end
|
47
|
+
|
32
48
|
|
49
|
+
it 'pull reference_screenshots' do
|
50
|
+
expect(File.exist?('artifacts/reference_screenshots/similar_firefox_34.0.5.gif')).to be true
|
51
|
+
expect(File.exist?('artifacts/reference_screenshots/different_firefox_34.0.5.gif')).to be true
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'create the remote reference folder if it does not exist', before_hook_test: true do |example|
|
55
|
+
allow(Cezanne.config.remote_files).to receive(:exists?).and_return(false)
|
56
|
+
allow(Cezanne.config.remote_files).to receive(:push)
|
57
|
+
expect(Cezanne.config.remote_files).to receive(:push).with(Cezanne.config.local_files.path_for(:ref), :ref);
|
58
|
+
RSpec.configuration.before(:all, screenshots: true).first.run(example)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'take screenshots' do
|
33
63
|
|
64
|
+
{phash_hamming_distance: 'phash hamming distance', peak_signal_to_noise_ratio: 'peak signal to noise ratio'}.each do |key, value|
|
65
|
+
|
66
|
+
context value do
|
67
|
+
|
68
|
+
before(:all) do
|
69
|
+
Cezanne.config.similarity_threshold = nil
|
70
|
+
Cezanne.configure do |config|
|
71
|
+
config.comparison_method = key
|
72
|
+
end
|
73
|
+
end
|
34
74
|
|
75
|
+
context 'similar' do
|
35
76
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
expect(File.exist?('artifacts/test/tmp_screenshots')).to be true
|
41
|
-
expect(File.exist?('artifacts/test/different_screenshots')).to be true
|
42
|
-
expect(File.exist?('artifacts/test/new_screenshots')).to be true
|
43
|
-
end
|
44
|
-
|
77
|
+
it 'pass the test' do
|
78
|
+
visit '/similar.html'
|
79
|
+
expect { check_visual_regression_for 'similar' }.not_to raise_error
|
80
|
+
end
|
45
81
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
end
|
50
|
-
|
51
|
-
end
|
52
|
-
|
53
|
-
describe 'take screenshots' do
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'different' do
|
54
85
|
|
55
|
-
|
86
|
+
it 'fail the test' do
|
87
|
+
visit '/different.html'
|
88
|
+
expect { check_visual_regression_for 'different' }.to raise_error
|
89
|
+
end
|
56
90
|
|
57
|
-
|
58
|
-
visit '/similar.html'
|
59
|
-
expect { check_visual_regression_for 'similar' }.not_to raise_error
|
60
|
-
end
|
91
|
+
end
|
61
92
|
|
62
|
-
|
63
|
-
|
64
|
-
context 'different' do
|
93
|
+
|
94
|
+
context 'new' do
|
65
95
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
96
|
+
it 'fail the test' do
|
97
|
+
visit '/new.html'
|
98
|
+
expect { check_visual_regression_for 'new' }.to raise_error
|
99
|
+
end
|
70
100
|
|
71
|
-
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
72
104
|
|
105
|
+
end
|
73
106
|
|
74
|
-
|
107
|
+
end
|
75
108
|
|
76
|
-
|
77
|
-
|
78
|
-
|
109
|
+
describe 'finalization', after_hook_test: true do
|
110
|
+
|
111
|
+
it 'push new, diff screenshots to remote' do |example|
|
112
|
+
expect(Cezanne.config.remote_files).to receive(:push).with(kind_of(String), :diff).and_call_original
|
113
|
+
expect(Cezanne.config.remote_files).to receive(:push).with(kind_of(String), :new).and_call_original
|
114
|
+
RSpec.configuration.after(:all, screenshots: true).last.run(example)
|
79
115
|
end
|
80
116
|
|
81
|
-
|
82
|
-
|
83
|
-
|
117
|
+
it 'does not push empty folders' do
|
118
|
+
allow(Dir).to receive(:glob).and_return([])
|
119
|
+
expect(Cezanne.config.remote_files).not_to receive(:push)
|
120
|
+
end
|
84
121
|
|
85
|
-
describe 'finalization', after_hook_test: true do
|
86
|
-
|
87
|
-
it 'push new, diff screenshots to remote' do |example|
|
88
|
-
expect(RSpec.configuration.cezanne[:remote_files]).to receive('push').with(kind_of(String), :diff).and_call_original
|
89
|
-
expect(RSpec.configuration.cezanne[:remote_files]).to receive('push').with(kind_of(String), :new).and_call_original
|
90
|
-
RSpec.configuration.after(:all, screenshots: true).last.run(example)
|
91
|
-
end
|
92
122
|
|
93
|
-
|
94
|
-
|
95
|
-
|
123
|
+
it 'clean local folders' do |example|
|
124
|
+
RSpec.configuration.after(:all, screenshots: true).last.run(example)
|
125
|
+
expect(File.exists?('artifacts')).to be false
|
126
|
+
end
|
127
|
+
|
96
128
|
end
|
97
129
|
|
98
130
|
end
|
99
|
-
|
100
|
-
end
|
131
|
+
end
|
data/spec/cezanne_spec.rb
CHANGED
@@ -2,7 +2,16 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Cezanne do
|
4
4
|
|
5
|
-
let(:class_with_cezanne) {
|
5
|
+
let(:class_with_cezanne) {
|
6
|
+
Cezanne.configure do |config|
|
7
|
+
config.uid = 'test'
|
8
|
+
config.project_name = 'cezanne'
|
9
|
+
config.local_files = 'mock'
|
10
|
+
config.remote_files = 'mock'
|
11
|
+
end
|
12
|
+
Class.include(Cezanne).new
|
13
|
+
}
|
14
|
+
|
6
15
|
# Capybara mocks
|
7
16
|
let(:page) { double('page') }
|
8
17
|
let(:driver) { double('driver') }
|
@@ -27,6 +36,45 @@ describe Cezanne do
|
|
27
36
|
allow(class_with_cezanne).to receive('image').and_return(image)
|
28
37
|
end
|
29
38
|
|
39
|
+
describe '#configure' do
|
40
|
+
|
41
|
+
it 'can be configured with a block' do
|
42
|
+
|
43
|
+
Cezanne.configure do |config|
|
44
|
+
config.uid = 'uid'
|
45
|
+
config.project_name = 'project_name'
|
46
|
+
config.local_root = 'local_root'
|
47
|
+
config.remote_root = 'remote_root'
|
48
|
+
config.local_files = 'local_files'
|
49
|
+
config.remote_files = 'remote_files'
|
50
|
+
config.comparison_method = 'comparison_method'
|
51
|
+
config.similarity_threshold = 'similarity_threshold'
|
52
|
+
end
|
53
|
+
|
54
|
+
expected_config = Cezanne::Config.new('uid', 'project_name', 'local_root', 'remote_root', 'local_files', 'remote_files', 'comparison_method', 'similarity_threshold')
|
55
|
+
|
56
|
+
expect(Cezanne.config).to eq(expected_config)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'local_files helper' do
|
60
|
+
|
61
|
+
Cezanne.configure do |config|
|
62
|
+
config.local_files = 'local_files'
|
63
|
+
end
|
64
|
+
|
65
|
+
expect(Class.include(Cezanne).new.send(:local_files)).to eq 'local_files'
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'remote_files helper' do
|
69
|
+
|
70
|
+
Cezanne.configure do |config|
|
71
|
+
config.remote_files = 'remote_files'
|
72
|
+
end
|
73
|
+
|
74
|
+
expect(Class.include(Cezanne).new.send(:remote_files)).to eq 'remote_files'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
30
78
|
describe '#take_screenshot' do
|
31
79
|
|
32
80
|
it 'take a screenshot using the browser driver' do
|
@@ -58,9 +106,9 @@ describe Cezanne do
|
|
58
106
|
let(:that) { this.clone }
|
59
107
|
|
60
108
|
before(:each) do
|
61
|
-
allow(image).to receive('
|
109
|
+
allow(image).to receive('duplicate?')
|
62
110
|
allow(image).to receive('crop!')
|
63
|
-
allow(picture).to receive('
|
111
|
+
allow(picture).to receive('duplicate?').with(picture).and_return(true)
|
64
112
|
allow(this).to receive('width').and_return(1)
|
65
113
|
allow(this).to receive('height').and_return(2)
|
66
114
|
allow(that).to receive('width').and_return(2)
|
@@ -76,7 +124,7 @@ describe Cezanne do
|
|
76
124
|
context 'similar images' do
|
77
125
|
|
78
126
|
it 'return false' do
|
79
|
-
expect(this
|
127
|
+
expect(this).to receive('duplicate?').and_return(true)
|
80
128
|
expect(class_with_cezanne.send(:spot_differences_between, this, that)).to be false
|
81
129
|
end
|
82
130
|
|
@@ -85,7 +133,7 @@ describe Cezanne do
|
|
85
133
|
context 'different images' do
|
86
134
|
|
87
135
|
it 'return true' do
|
88
|
-
expect(this
|
136
|
+
expect(this).to receive('duplicate?').and_return(false)
|
89
137
|
expect(class_with_cezanne.send(:spot_differences_between, this, that)).to be true
|
90
138
|
end
|
91
139
|
|
@@ -158,6 +206,11 @@ describe Cezanne do
|
|
158
206
|
|
159
207
|
before(:each) do
|
160
208
|
allow(class_with_cezanne).to receive('get_reference_screenshot_for').and_return(Cezanne::Image.new('spec/images/page_name_browser_version.gif'))
|
209
|
+
|
210
|
+
Cezanne.configure do |config|
|
211
|
+
config.comparison_method = :peak_signal_to_noise_ratio
|
212
|
+
config.similarity_threshold = 42
|
213
|
+
end
|
161
214
|
end
|
162
215
|
|
163
216
|
context 'succesful match' do
|
@@ -214,5 +267,17 @@ describe Cezanne do
|
|
214
267
|
end
|
215
268
|
|
216
269
|
end
|
270
|
+
|
271
|
+
context 'accepts options' do
|
272
|
+
|
273
|
+
it 'let you specify a rectangle to crop' do
|
274
|
+
opts = { crop: [0,0,10,10]}
|
275
|
+
allow(class_with_cezanne).to receive(:spot_differences_between).and_return(false)
|
276
|
+
expect(class_with_cezanne).to receive(:take_screenshot).with(anything, opts).and_call_original
|
277
|
+
expect(class_with_cezanne).to receive(:image).with(anything, opts)
|
278
|
+
class_with_cezanne.check_visual_regression_for('page_name', opts)
|
279
|
+
end
|
280
|
+
|
281
|
+
end
|
217
282
|
end
|
218
283
|
end
|
data/spec/images/different.html
CHANGED
Binary file
|
Binary file
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cezanne
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sky Haiku
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-01-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -122,6 +122,20 @@ dependencies:
|
|
122
122
|
- - ">="
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: phashion
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
125
139
|
description: Let Cezanne help you make sure your views look alright
|
126
140
|
email:
|
127
141
|
- skyhaikuteam@gmail.com
|
@@ -132,12 +146,13 @@ files:
|
|
132
146
|
- ".gitignore"
|
133
147
|
- ".travis.yml"
|
134
148
|
- Gemfile
|
135
|
-
- LICENSE
|
149
|
+
- LICENSE
|
136
150
|
- README.md
|
137
151
|
- Rakefile
|
138
152
|
- cezanne.gemspec
|
139
153
|
- lib/cezanne.rb
|
140
154
|
- lib/cezanne/comparison.rb
|
155
|
+
- lib/cezanne/config.rb
|
141
156
|
- lib/cezanne/image.rb
|
142
157
|
- lib/cezanne/local_files.rb
|
143
158
|
- lib/cezanne/remote_files.rb
|
data/LICENSE.txt
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
Copyright (c) 2014 Andrea Pigato
|
2
|
-
|
3
|
-
MIT License
|
4
|
-
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
-
a copy of this software and associated documentation files (the
|
7
|
-
"Software"), to deal in the Software without restriction, including
|
8
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
-
permit persons to whom the Software is furnished to do so, subject to
|
11
|
-
the following conditions:
|
12
|
-
|
13
|
-
The above copyright notice and this permission notice shall be
|
14
|
-
included in all copies or substantial portions of the Software.
|
15
|
-
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|