gatling 1.0.7 → 1.0.8
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.
- data/.gitignore +1 -1
- data/Gemfile +0 -7
- data/README.rdoc +35 -27
- data/Rakefile +15 -5
- data/gatling.gemspec +9 -5
- data/lib/gatling.rb +34 -17
- data/lib/gatling/capture_element.rb +3 -2
- data/lib/gatling/comparison.rb +10 -14
- data/lib/gatling/config.rb +32 -27
- data/lib/gatling/image.rb +24 -20
- data/lib/gatling/image_wrangler.rb +2 -4
- data/lib/gatling/version.rb +1 -1
- data/spec/acceptance/gatling_acceptance_spec.rb +94 -0
- data/spec/acceptance/rspec_matcher_spec.rb +39 -0
- data/spec/comparison_spec.rb +9 -8
- data/spec/configuration_spec.rb +55 -13
- data/spec/gatling_spec.rb +64 -59
- data/spec/image_spec.rb +64 -30
- data/spec/integration/gatling_integration_spec.rb +37 -0
- data/spec/support/assets/fruit_app.html +11 -0
- metadata +55 -20
- data/.rbenv-version +0 -1
- data/lib/gatling/file_helper.rb +0 -17
- data/spec/file_helper_spec.rb +0 -18
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/README.rdoc
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
= Gatling
|
2
2
|
|
3
|
-
|
3
|
+
An integrated visual RSpec matcher which makes real visual testing easy.
|
4
4
|
|
5
5
|
-------------------------------------
|
6
6
|
|
7
|
-
|
7
|
+
== Installation:
|
8
8
|
|
9
9
|
gem install gatling
|
10
10
|
|
@@ -16,63 +16,71 @@ In spec_helper.rb :
|
|
16
16
|
#plug and play custom rspec matcher
|
17
17
|
require 'gatling/matchers/look_like_matcher'
|
18
18
|
|
19
|
-
===
|
19
|
+
=== Configuration settings:
|
20
20
|
|
21
|
-
|
21
|
+
==== Reference_image_path - sets where the reference and diff images are saved to.
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
Use Gatling's custom matcher (conveniently supplied in the gatling gem) and specify the reference image:
|
23
|
+
For Rails application, a default reference images folder will be created at spec/reference_images. This folder is root to all the reference
|
24
|
+
images to be compares.
|
26
25
|
|
27
|
-
|
28
|
-
|
29
|
-
|
26
|
+
Also created are subfolders:
|
27
|
+
- /candidate - will hold candidate images which can be used as reference images
|
28
|
+
- /diff - will hold the diff images generated by failed comparison tests
|
29
|
+
- /temp - will only ever hold one screenshot at a time, used to create the cropped element
|
30
30
|
|
31
|
+
=== max_no_tries - sets how many times Gatling will try and match the element against the reference image. Handy to reduce fragility of tests due to animations and load times. Defaults to 5.
|
31
32
|
|
32
|
-
|
33
|
+
=== Sleep_between_tries - sets the sleep time (in seconds) between match tries (requires max_no_tries > 1). Defaults to 0.5
|
33
34
|
|
35
|
+
=== Configuration settings are set with the following:
|
34
36
|
|
35
|
-
|
37
|
+
Gatling.config do |setting|
|
38
|
+
Gatling.reference_image_path = 'my_custom_path'
|
39
|
+
Gatling.max_no_tries = 3
|
40
|
+
Gatling.sleep_between_tries = 0.7
|
41
|
+
end
|
36
42
|
|
43
|
+
GATLING::CONFIGURATION will be depreciated in future versions.
|
37
44
|
-------------------------------------
|
45
|
+
=== Usage:
|
38
46
|
|
39
|
-
|
47
|
+
Identify an element to match, for example:
|
40
48
|
|
41
|
-
|
42
|
-
images to be compares.
|
49
|
+
@element = page.find(:css, #button)
|
43
50
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
- /temp - will only ever hold one screenshot at a time, used to create the cropped element
|
51
|
+
Use Gatling's custom matcher (conveniently supplied in the gatling gem) and specify the reference image:
|
52
|
+
|
53
|
+
@element.should look_like('button_ref.png')
|
48
54
|
|
49
|
-
|
55
|
+
Gatling will take care of cropping the element and try and make a targeted match without the noise of the whole page.
|
50
56
|
|
51
|
-
|
57
|
+
If the matcher fails, the test will fail, an exception will be raised and both a diff image and a new candidate image will be created.
|
52
58
|
|
59
|
+
If no reference image exits, the test will fail, an exception will be raised and a candidate will be created which can be used as a reference
|
60
|
+
(see Training mode below)
|
53
61
|
|
54
62
|
-------------------------------------
|
55
63
|
|
56
64
|
=== Training mode:
|
57
65
|
|
58
|
-
Gatling can be run in training mode which will create reference images where ones do not exist.
|
66
|
+
Gatling can be run in training mode which will create reference images where ones do not exist.
|
59
67
|
|
60
|
-
<b>CAUTION: Trainer mode will pass all the test which rely on the Gatling custom matcher, this can mean a false green build.
|
68
|
+
<b>CAUTION: Trainer mode will pass all the test which rely on the Gatling custom matcher, this can mean a false green build.
|
61
69
|
Please make sure you are saving 'clean' references</b>
|
62
70
|
|
63
71
|
Trainer mode will not overwrite existing references. If you are updating, just delete the old reference image.
|
64
72
|
|
65
|
-
To set trainer mode, prefix rspec command line with $GATLING_TRAINER = true
|
73
|
+
To set trainer mode, prefix rspec command line with $GATLING_TRAINER = true
|
66
74
|
|
67
75
|
|
68
|
-
Example:
|
76
|
+
Example:
|
69
77
|
$GATLING_TRAINER = true rspec spec/my_spec.rb
|
70
78
|
|
71
79
|
-------------------------------------
|
72
80
|
|
73
81
|
==== Non-gem dependencies:
|
74
82
|
|
75
|
-
Imagemagick must be installed:
|
83
|
+
Imagemagick must be installed:
|
76
84
|
|
77
85
|
$sudo apt-get install imagemagick
|
78
86
|
|
@@ -80,7 +88,7 @@ Imagemagick must be installed:
|
|
80
88
|
|
81
89
|
|
82
90
|
== Contributing to gatling
|
83
|
-
|
91
|
+
|
84
92
|
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
85
93
|
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
86
94
|
* Fork the project
|
data/Rakefile
CHANGED
@@ -1,13 +1,23 @@
|
|
1
1
|
require 'bundler/gem_tasks'
|
2
2
|
require 'rspec/core'
|
3
|
-
require 'rspec/core/rake_task'
|
3
|
+
require 'rspec/core/rake_task'
|
4
4
|
require File.expand_path('spec/spec_helper.rb')
|
5
5
|
|
6
|
-
task :default => ["run"]
|
7
6
|
|
8
|
-
|
9
|
-
|
7
|
+
task :default => :full_build
|
8
|
+
|
9
|
+
desc "full build, run all the tests"
|
10
|
+
task :full_build => [:unit, :acceptance]
|
11
|
+
|
12
|
+
desc "Run unit tests"
|
13
|
+
RSpec::Core::RakeTask.new(:unit) do |t|
|
10
14
|
t.pattern = ['spec/*_spec.rb']
|
11
15
|
t.rspec_opts = ['--options', "spec/spec.opts"]
|
12
16
|
end
|
13
|
-
|
17
|
+
|
18
|
+
|
19
|
+
desc "Run acceptance tests"
|
20
|
+
RSpec::Core::RakeTask.new(:acceptance) do |t|
|
21
|
+
t.pattern = ['spec/acceptance/*_spec.rb']
|
22
|
+
t.rspec_opts = ['--options', "spec/spec.opts"]
|
23
|
+
end
|
data/gatling.gemspec
CHANGED
@@ -5,7 +5,7 @@ require "gatling/version"
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = "gatling"
|
7
7
|
s.version = Gatling::VERSION
|
8
|
-
s.authors = ["Gabriel Rotbart, Amanda Koh"]
|
8
|
+
s.authors = ["Gabriel Rotbart, Amanda Koh, Mike Bain"]
|
9
9
|
s.email = ["grotbart@gmail.com"]
|
10
10
|
s.homepage = "http://github.com/GabrielRotbart/gatling"
|
11
11
|
s.summary = %q{Automated visual testing}
|
@@ -18,8 +18,12 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
19
|
s.require_paths = ["lib"]
|
20
20
|
|
21
|
-
s.
|
22
|
-
s.
|
23
|
-
s.
|
24
|
-
s.
|
21
|
+
s.add_runtime_dependency('rmagick', ['>=2.13.1'])
|
22
|
+
s.add_runtime_dependency('rspec-core',['>=2.8.0'])
|
23
|
+
s.add_runtime_dependency('rspec',['>=2.8.0'])
|
24
|
+
s.add_runtime_dependency('capybara',['>=1.1.2'])
|
25
|
+
|
26
|
+
s.add_development_dependency('rake',['>=0.9.2'])
|
27
|
+
s.add_development_dependency('rspec-instafail',['>=0.1.8'])
|
28
|
+
s.add_development_dependency('pry',['>=0.9.8.2'])
|
25
29
|
end
|
data/lib/gatling.rb
CHANGED
@@ -1,18 +1,12 @@
|
|
1
|
-
#Dir["/gatling/*.rb"].each {|file| require file}
|
2
|
-
|
3
1
|
require 'RMagick'
|
4
2
|
require 'capybara'
|
5
3
|
require 'capybara/dsl'
|
6
4
|
|
7
5
|
require 'gatling/config'
|
8
|
-
require 'gatling/file_helper'
|
9
6
|
require 'gatling/image'
|
10
7
|
require 'gatling/comparison'
|
11
8
|
require 'gatling/capture_element'
|
12
9
|
|
13
|
-
# include Gatling::Comparison
|
14
|
-
|
15
|
-
#TODO: Fuzz matches
|
16
10
|
#TODO: Helpers for cucumber
|
17
11
|
#TODO: Make directories as needed
|
18
12
|
|
@@ -20,39 +14,58 @@ module Gatling
|
|
20
14
|
|
21
15
|
class << self
|
22
16
|
|
17
|
+
attr_accessor :reference_image_path, :max_no_tries, :sleep_between_tries
|
18
|
+
|
23
19
|
def matches?(expected_reference_filename, actual_element)
|
24
20
|
|
25
|
-
Gatling::
|
21
|
+
expected_reference_file = (File.join(Gatling::Configuration.path(:reference), expected_reference_filename))
|
26
22
|
|
27
|
-
expected_reference_file = (File.join(Gatling::Configuration.paths[:reference], expected_reference_filename))
|
28
|
-
actual_image = Gatling::Image.new(:from_element, expected_reference_filename, actual_element)
|
29
23
|
|
30
24
|
if Gatling::Configuration.trainer_toggle
|
25
|
+
actual_image = Gatling::ImageFromElement.new(actual_element, expected_reference_filename)
|
31
26
|
save_image_as_reference(actual_image)
|
32
27
|
return true
|
33
28
|
end
|
34
29
|
|
35
30
|
if !File.exists?(expected_reference_file)
|
31
|
+
actual_image = Gatling::ImageFromElement.new(actual_element, expected_reference_filename)
|
36
32
|
save_image_as_candidate(actual_image)
|
37
33
|
return false
|
38
34
|
else
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
actual_image.save(:as => :candidate)
|
35
|
+
comparison = compare_until_match(actual_element, expected_reference_filename, Gatling::Configuration.max_no_tries)
|
36
|
+
matches = comparison.matches?
|
37
|
+
if !matches
|
38
|
+
comparison.actual_image.save(:as => :candidate)
|
43
39
|
save_image_as_diff(comparison.diff_image)
|
44
40
|
end
|
45
|
-
|
41
|
+
matches
|
46
42
|
end
|
47
43
|
end
|
48
44
|
|
49
|
-
|
45
|
+
def compare_until_match actual_element, expected_reference_filename, max_no_tries
|
46
|
+
tries = max_no_tries
|
47
|
+
try = 0
|
48
|
+
match = false
|
49
|
+
expected_image = Gatling::ImageFromFile.new(expected_reference_filename)
|
50
|
+
comparison = nil
|
51
|
+
while !match && try < tries
|
52
|
+
actual_image = Gatling::ImageFromElement.new(actual_element, expected_reference_filename)
|
53
|
+
comparison = Gatling::Comparison.new(expected_image, actual_image)
|
54
|
+
match = comparison.matches?
|
55
|
+
if !match
|
56
|
+
sleep 0.5
|
57
|
+
try += 1
|
58
|
+
puts "Tried to match #{try} times"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
comparison
|
62
|
+
end
|
50
63
|
|
51
64
|
def save_image_as_diff(image)
|
52
65
|
image.save(:as => :diff)
|
53
66
|
raise "element did not match #{image.file_name}. A diff image: #{image.file_name} was created in " +
|
54
|
-
"#{File.join(Gatling::Configuration.
|
55
|
-
"A new reference #{File.join(Gatling::Configuration.
|
67
|
+
"#{File.join(Gatling::Configuration.path(:diff),image.file_name)}. " +
|
68
|
+
"A new reference #{File.join(Gatling::Configuration.path(:candidate),image.file_name)} can be used to fix the test"
|
56
69
|
end
|
57
70
|
|
58
71
|
def save_image_as_candidate(image)
|
@@ -70,5 +83,9 @@ module Gatling
|
|
70
83
|
end
|
71
84
|
end
|
72
85
|
|
86
|
+
def config
|
87
|
+
yield
|
88
|
+
end
|
89
|
+
|
73
90
|
end
|
74
91
|
end
|
@@ -3,15 +3,16 @@ require_relative 'image_wrangler'
|
|
3
3
|
module Gatling
|
4
4
|
class CaptureElement
|
5
5
|
|
6
|
+
include ImageWrangler
|
7
|
+
|
6
8
|
def initialize element_to_capture, *element_to_exclude
|
7
9
|
@reference_image_path = Gatling::Configuration.reference_image_path
|
8
10
|
@element_to_capture = element_to_capture
|
9
|
-
@element_to_exclude = element_to_exclude.first
|
11
|
+
# @element_to_exclude = element_to_exclude.first
|
10
12
|
|
11
13
|
end
|
12
14
|
|
13
15
|
def capture
|
14
|
-
|
15
16
|
screenshot = self.take_screenshot
|
16
17
|
screenshot = exclude(screenshot, @element_to_exclude) if @element_to_exclude
|
17
18
|
Gatling::ImageWrangler.crop_element(screenshot, @element_to_capture)
|
data/lib/gatling/comparison.rb
CHANGED
@@ -1,24 +1,20 @@
|
|
1
1
|
module Gatling
|
2
2
|
class Comparison
|
3
3
|
|
4
|
-
attr_accessor :
|
4
|
+
attr_accessor :diff_image, :actual_image, :expected_image
|
5
5
|
|
6
|
-
def initialize
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
unless @match
|
14
|
-
diff_image = diff_metric.first
|
15
|
-
@diff_image = Gatling::Image.new(:from_diff, actual_image.file_name, diff_image)
|
6
|
+
def initialize actual_image, expected_image
|
7
|
+
@actual_image = actual_image
|
8
|
+
@expected_image = expected_image
|
9
|
+
@comparison = @actual_image.image.compare_channel(@expected_image.image, Magick::MeanAbsoluteErrorMetric)
|
10
|
+
@match = @comparison[1] == 0.0
|
11
|
+
if !@matches
|
12
|
+
@diff_image =Gatling::Image.new(@comparison.first, @expected_image.file_name)
|
16
13
|
end
|
17
|
-
@match
|
18
14
|
end
|
19
15
|
|
20
|
-
def
|
21
|
-
@
|
16
|
+
def matches?
|
17
|
+
@match
|
22
18
|
end
|
23
19
|
|
24
20
|
end
|
data/lib/gatling/config.rb
CHANGED
@@ -1,41 +1,29 @@
|
|
1
1
|
module Gatling
|
2
|
-
|
2
|
+
module Configuration
|
3
3
|
|
4
4
|
class << self
|
5
5
|
|
6
|
-
attr_accessor :reference_image_path, :trainer_toggle
|
6
|
+
attr_accessor :reference_image_path, :trainer_toggle, :max_no_tries, :sleep_between_tries
|
7
7
|
|
8
8
|
attr_reader :paths
|
9
9
|
|
10
10
|
def reference_image_path
|
11
|
-
@reference_image_path ||= set_default_path
|
11
|
+
Gatling.reference_image_path || @reference_image_path ||= set_default_path
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
15
|
-
|
16
|
-
candidate:File.join(reference_image_path, 'candidate'),
|
17
|
-
diff:File.join(reference_image_path, 'diff'),
|
18
|
-
temp:File.join(reference_image_path, 'temp')]
|
14
|
+
def max_no_tries
|
15
|
+
Gatling.max_no_tries || @max_no_tries ||= 5
|
19
16
|
end
|
20
17
|
|
21
|
-
def
|
22
|
-
@
|
23
|
-
|
24
|
-
case @trainer_value
|
25
|
-
when nil
|
26
|
-
@trainer_value = nil
|
27
|
-
when 'true'
|
28
|
-
@trainer_value = true
|
29
|
-
when 'false'
|
30
|
-
@trainer_value = false
|
31
|
-
else
|
32
|
-
@trainer_value = false
|
33
|
-
puts 'Unknown GATLING_TRAINER argument. Please supply true, false or nil. DEFAULTING TO FALSE'
|
34
|
-
end
|
35
|
-
@trainer_toggle ||= @trainer_value ||= false
|
18
|
+
def sleep_between_tries
|
19
|
+
Gatling.sleep_between_tries || @sleep_between_tries ||= 0.5
|
36
20
|
end
|
37
21
|
|
38
|
-
def
|
22
|
+
def path(type)
|
23
|
+
paths = Hash[:reference => reference_image_path,
|
24
|
+
:candidate => File.join(reference_image_path, 'candidate'),
|
25
|
+
:diff => File.join(reference_image_path, 'diff'),
|
26
|
+
:temp => File.join(reference_image_path, 'temp')]
|
39
27
|
if paths.keys.include? type
|
40
28
|
return paths[type]
|
41
29
|
else
|
@@ -43,11 +31,28 @@ module Gatling
|
|
43
31
|
end
|
44
32
|
end
|
45
33
|
|
46
|
-
|
34
|
+
def trainer_toggle
|
35
|
+
@trainer_value = ENV['GATLING_TRAINER']
|
36
|
+
|
37
|
+
case @trainer_value
|
38
|
+
when nil
|
39
|
+
@trainer_value = nil
|
40
|
+
when 'true'
|
41
|
+
@trainer_value = true
|
42
|
+
when 'false'
|
43
|
+
@trainer_value = false
|
44
|
+
else
|
45
|
+
@trainer_value = false
|
46
|
+
puts 'Unknown GATLING_TRAINER argument. Please supply true, false or nil. DEFAULTING TO FALSE'
|
47
|
+
end
|
48
|
+
@trainer_toggle ||= @trainer_value ||= false
|
49
|
+
end
|
50
|
+
|
47
51
|
|
48
52
|
def set_default_path
|
53
|
+
private
|
49
54
|
begin
|
50
|
-
|
55
|
+
@reference_image_path ||= File.join(Rails.root, 'spec/reference_images')
|
51
56
|
rescue
|
52
57
|
@reference_image_path = 'spec/reference_images'
|
53
58
|
puts "Currently defaulting to #{@reference_image_path}. Overide this by setting Gatling::Configuration.reference_image_path=[refpath]"
|
@@ -57,4 +62,4 @@ module Gatling
|
|
57
62
|
end
|
58
63
|
|
59
64
|
end
|
60
|
-
end
|
65
|
+
end
|
data/lib/gatling/image.rb
CHANGED
@@ -5,46 +5,50 @@ module Gatling
|
|
5
5
|
|
6
6
|
attr_reader :type
|
7
7
|
|
8
|
-
def initialize
|
8
|
+
def initialize image, file_name
|
9
9
|
|
10
10
|
@file_name = file_name
|
11
11
|
|
12
|
-
|
13
|
-
when :from_file
|
14
|
-
@image = image_from_file(file_name)
|
15
|
-
when :from_element
|
16
|
-
@image = image_from_element(element_or_image)
|
17
|
-
when :from_diff
|
18
|
-
@image = element_or_image
|
19
|
-
else
|
20
|
-
raise 'WRONG IMAGE TYPE'
|
21
|
-
end
|
12
|
+
@image = image
|
22
13
|
|
23
14
|
end
|
24
15
|
|
25
16
|
def save type
|
26
|
-
|
17
|
+
path = Gatling::Configuration.path(type[:as])
|
18
|
+
FileUtils::mkdir_p(path) unless File.exists?(path)
|
19
|
+
@path = File.join(path, @file_name)
|
27
20
|
@image.write @path
|
28
21
|
@path
|
29
22
|
end
|
30
23
|
|
31
24
|
def exists?
|
32
|
-
File.exists?(File.join(Gatling::Configuration.
|
25
|
+
File.exists?(File.join(Gatling::Configuration.path(:reference), @file_name))
|
33
26
|
end
|
34
27
|
|
35
|
-
|
28
|
+
end
|
36
29
|
|
37
|
-
|
38
|
-
Gatling::CaptureElement.new(element).capture
|
39
|
-
end
|
30
|
+
class ImageFromElement < Image
|
40
31
|
|
41
|
-
def
|
42
|
-
|
32
|
+
def initialize element, file_name
|
33
|
+
super(image, file_name)
|
34
|
+
|
35
|
+
@image = Gatling::CaptureElement.new(element).capture
|
43
36
|
end
|
37
|
+
|
38
|
+
#TODO: make save a relevant subclass method
|
39
|
+
end
|
44
40
|
|
41
|
+
class ImageFromFile < Image
|
45
42
|
|
46
|
-
|
43
|
+
def initialize file_name
|
44
|
+
super(image, file_name)
|
45
|
+
|
46
|
+
@image = Magick::Image.read(File.join(Gatling::Configuration.path(:reference), @file_name)).first
|
47
|
+
end
|
47
48
|
|
49
|
+
#TODO: make save a relevant subclass method
|
50
|
+
|
51
|
+
end
|
48
52
|
end
|
49
53
|
|
50
54
|
|