looks_good 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 33bfb614a176498eb6a13180459e40aff96fc328673ab0edd6cbcb2b4b28345f
4
+ data.tar.gz: 97b7efc3432c0b51b052bad9201e87a900791e57afdc9d2d9ef6444d654891bf
5
+ SHA512:
6
+ metadata.gz: 629f66d456631f91b36200dd03266c2de827dd6313ded03a49c01f0021b303f93b98f8c9f5525e831ba9412412ed908800fd831fc89282c16c155804ae8d0b79
7
+ data.tar.gz: 63c9fc8f12c8e25f4230e39298ab623b7b85e5b491679ae0a9c59f4bf6da9122cf1991a750df8376d81e482c8bb3063a7fce8f22b3812ee3fd97e83c3dcd97d7
data/.DS_Store ADDED
Binary file
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.png
2
+ *.gem
3
+ .bundle
4
+ Gemfile.lock
5
+ pkg/*
6
+ *.orig
7
+ test*
8
+ .idea*
9
+ .DS_Store
10
+ spec/support/ready_candidate_ref/temp/*
11
+ reports/*
12
+ vendor/*
13
+ /.rbenv*
14
+ .rakeTasks
15
+ *.iml
16
+ *.ipr
17
+ *.iws
18
+ /bin
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,61 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ looks_good (1.0.0)
5
+ capybara (>= 2.6.0)
6
+ rmagick (>= 2.15.4)
7
+
8
+ GEM
9
+ remote: http://rubygems.org/
10
+ specs:
11
+ addressable (2.8.0)
12
+ public_suffix (>= 2.0.2, < 5.0)
13
+ capybara (3.35.3)
14
+ addressable
15
+ mini_mime (>= 0.1.3)
16
+ nokogiri (~> 1.8)
17
+ rack (>= 1.6.0)
18
+ rack-test (>= 0.6.3)
19
+ regexp_parser (>= 1.5, < 3.0)
20
+ xpath (~> 3.2)
21
+ childprocess (0.9.0)
22
+ ffi (~> 1.0, >= 1.0.11)
23
+ coderay (1.1.3)
24
+ ffi (1.15.5)
25
+ method_source (1.0.0)
26
+ mini_mime (1.1.2)
27
+ mini_portile2 (2.6.1)
28
+ nokogiri (1.12.5)
29
+ mini_portile2 (~> 2.6.1)
30
+ racc (~> 1.4)
31
+ pry (0.14.1)
32
+ coderay (~> 1.1)
33
+ method_source (~> 1.0)
34
+ public_suffix (4.0.7)
35
+ racc (1.6.0)
36
+ rack (2.2.4)
37
+ rack-test (2.0.2)
38
+ rack (>= 1.3)
39
+ rake (0.9.2)
40
+ regexp_parser (2.5.0)
41
+ rmagick (4.2.6)
42
+ rubyzip (1.3.0)
43
+ selenium-webdriver (2.53.4)
44
+ childprocess (~> 0.5)
45
+ rubyzip (~> 1.0)
46
+ websocket (~> 1.0)
47
+ websocket (1.2.9)
48
+ xpath (3.2.0)
49
+ nokogiri (~> 1.8)
50
+
51
+ PLATFORMS
52
+ ruby
53
+
54
+ DEPENDENCIES
55
+ looks_good!
56
+ pry (>= 0.10.2)
57
+ rake (>= 0.9.2)
58
+ selenium-webdriver (~> 2.53.0)
59
+
60
+ BUNDLED WITH
61
+ 2.2.5
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Gabriel Rotbart, Amanda Koh
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,123 @@
1
+ # LooksGood
2
+
3
+ A visual RSpec matcher to ensure the entire page (or specific elements) have not changed. Allows for a tolerance to be set, so you can reuire pixel-perfect or allow for some minor changes.
4
+
5
+ -------------------------------------
6
+
7
+ ## Installation:
8
+
9
+ gem install looks_good
10
+
11
+ In rails_helper.rb:
12
+
13
+ require 'looks_good'
14
+ require 'looks_good/matchers/look_like_matcher'
15
+
16
+ add to your `.gitignore`
17
+
18
+ spec/screenshots/tmp/*
19
+
20
+ ### Usage:
21
+
22
+ Identify an element to match, for example:
23
+
24
+ @element = page.find(:css, 'main')
25
+
26
+ Use LooksGood's custom matcher and specify the reference image:
27
+
28
+ expect(@element).to look_like(:main_section)
29
+
30
+ LooksGood will take care of cropping the element and try and make a targeted match without the noise of the whole page.
31
+ The parameter passed to `look_like` is the name that will be used to store the initial screenshot, and will be used in subsequent comparisons.
32
+
33
+ `look_like` can also handle the file naming and storage, if you specify a symbol. If the above example is from the spec `system/frontend/login_spec.rb`, then the screenshot will be stored in `spec/screenshots/system/frontend/login/main_section.png`.
34
+
35
+ You can ovveride this automatic behavior by specifying the file path as a string.
36
+
37
+ expect(@element).to look_like("frontend/main-screen.png")
38
+
39
+ which would store the screenshot in `spec/screenshots/frontend/main-screen.png`
40
+
41
+ If you want to adjust how tolerant the matcher is of differences, specify a float between 0 and 1 to `within`
42
+
43
+ expect(@element).to look_like(:main_content, within: 0.05)
44
+
45
+ If no reference image exits, the test will pass and a reference file will be created for future comparisons.
46
+
47
+ If you encounter a failure for a desired change, a diff image will be created to visualze the differences as well as a reference to the new version.
48
+ you can either copy the file provided in the error, or run your test suite again with `LOOKS_GOOD=true` to update it to the new version.
49
+
50
+
51
+ -------------------------------------
52
+
53
+ ### Configuration settings:
54
+
55
+ you can override any of the defaults with a config block.
56
+
57
+ #### Configuration settings are set with the following:
58
+
59
+ LooksGood.config do |c|
60
+ c.reference_image_path = 'spec/screenshots'
61
+ c.max_no_tries = 1
62
+ c.default_within = 0.01
63
+ c.sleep_between_tries = 0.5
64
+ c.browser_folders = false
65
+ c.scale_amount = 0.5
66
+ end
67
+
68
+
69
+ #### reference_image_path - sets where the reference and diff images are saved to.
70
+
71
+ For Rails application, a default images folder will be created at spec/screenshots. This folder is root to all the reference
72
+ images to be compares, but can be changed.
73
+
74
+ Also created are subfolders:
75
+ - tmp/ - will hold temporary/transient images. add this to .gitignore.
76
+ - tmp/candidate/ - will hold candidate images which can be used as the new versions of reference images
77
+ - tmp/diff/ - will hold the visual diff images for inspection when comparison fails
78
+
79
+ #### max_no_tries
80
+ - sets how many times looks_good will try and match the element against the reference image. Handy to reduce fragility of tests due to animations and load times. Defaults to 1.
81
+
82
+ #### default_within
83
+ - a float between 0 and 1 that sets the default tolerance for visual differences. default is 0.01 (1%)
84
+
85
+ #### scale_amount
86
+ - Retina mac screenshots are 2x actual size, so this scales them to be 1:1. default is 0.5, set to 1 to disable.
87
+
88
+ #### Sleep_between_tries
89
+ - sets the sleep time (in seconds) between match tries (requires max_no_tries > 1). Defaults to 0.5
90
+
91
+ #### browser_folders
92
+ - *Currently only available with Selenium-Webdriver / Capybara* - create reference folders based on the current Selenium driver's browser. Allows for cross-browser visual testing.
93
+
94
+
95
+ -------------------------------------
96
+
97
+ #### Non-gem dependencies:
98
+
99
+ Imagemagick must be installed:
100
+
101
+ $sudo apt-get install imagemagick
102
+
103
+ -------------------------------------
104
+
105
+
106
+ ## Contributing to `looks_good`
107
+
108
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
109
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
110
+ * Fork the project
111
+ * Start a feature/bugfix branch
112
+ * Commit and push until you are happy with your contribution
113
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
114
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
115
+
116
+ == History
117
+ looks_good is an expansion of (gatling)[https://github.com/gabrielrotbart/gatling] with added functionality and updated dependecies.
118
+
119
+ == Copyright
120
+
121
+ Copyright (c) 2022 Russell Jennings See LICENSE.txt for
122
+ further details.
123
+
@@ -0,0 +1,47 @@
1
+ require 'fileutils'
2
+ require 'looks_good'
3
+
4
+ module LooksGood
5
+ module CaptureElement
6
+ extend LooksGood::Configuration
7
+
8
+ def self.capture(element)
9
+ # Getting the element position before screenshot because of a side effect
10
+ # of WebDrivers getLocationOnceScrolledIntoView method which scrolls the page
11
+ # regardless of whether the object is in view or not
12
+ element_position = get_element_position(element)
13
+ screenshot = take_screenshot
14
+
15
+ crop_element(screenshot, element, element_position)
16
+ end
17
+
18
+ def self.take_screenshot
19
+ temp_dir = LooksGood::Configuration.path(:temp)
20
+ FileUtils.mkdir_p(temp_dir) unless File.exists?(temp_dir)
21
+ #captures the uncropped full screen
22
+ begin
23
+ temp_screenshot_filename = File.join(temp_dir, "temp-#{Process.pid}.png")
24
+ Capybara.page.driver.browser.save_screenshot(temp_screenshot_filename)
25
+ temp_screenshot = Magick::Image.read(temp_screenshot_filename).first
26
+ rescue
27
+ raise "Could not save screenshot to #{temp_dir}. Please make sure you have permission"
28
+ end
29
+ end
30
+
31
+ def self.get_element_position(element)
32
+ element = element.native
33
+ position = Hash.new{}
34
+ position[:x] = element.location.x
35
+ position[:y] = element.location.y
36
+ position[:width] = element.size.width
37
+ position[:height] = element.size.height
38
+ position
39
+ end
40
+
41
+ def self.crop_element(image, element_to_crop, position)
42
+ cropped_element = image.scale(LooksGood::Configuration.scale_amount).crop(position[:x], position[:y], position[:width], position[:height])
43
+ end
44
+
45
+ end
46
+ end
47
+
@@ -0,0 +1,74 @@
1
+ module LooksGood
2
+ class Comparison
3
+
4
+ attr_accessor :diff_image, :actual_image, :expected_image
5
+
6
+ def initialize(actual_image, expected_image, within=LooksGood::Configuration.default_within)
7
+ @actual_image = actual_image
8
+ @expected_image = expected_image
9
+ @comparison = compare_image
10
+ @within = within
11
+ # within is a float of % image difference between the two images
12
+ @match = @comparison[1] <= @within
13
+ @diff_image =LooksGood::Image.new(@comparison.first, @expected_image.file_name) unless @matches
14
+ end
15
+
16
+ def matches?
17
+ @match
18
+ end
19
+
20
+ def within
21
+ @within
22
+ end
23
+
24
+ def percent_difference
25
+ @comparison[1]
26
+ end
27
+
28
+ def compare_image
29
+ compare_images_with_same_size? ? compare_images_with_same_size : compare_images_with_different_size
30
+ end
31
+
32
+ def compare_images_with_same_size
33
+ images_to_compare = prep_images_for_comparison
34
+ images_to_compare.first.compare_channel(images_to_compare.last, Magick::PeakAbsoluteErrorMetric, Magick::AllChannels) do
35
+ self.highlight_color = Magick::Pixel.new(65300,100,0,38000)
36
+ self.lowlight_color = Magick::Pixel.new(0,65300,1000,60000)
37
+ end
38
+ end
39
+
40
+ def compare_images_with_different_size
41
+ row = [@actual_image.image.rows, @expected_image.image.rows].max
42
+ column = [@actual_image.image.columns, @expected_image.image.columns].max
43
+
44
+ images_to_compare = prep_images_for_comparison do |image|
45
+ expanded_image = image.extent(column, row)
46
+ expanded_image.background_color = 'white'
47
+ expanded_image
48
+ end
49
+ images_to_compare.first.compare_channel(images_to_compare.last, Magick::PeakAbsoluteErrorMetric) do |img|
50
+ img.highlight_color = Magick::Pixel.new(65300,100,0,38000)
51
+ img.lowlight_color = Magick::Pixel.new(0,65300,1000,60000)
52
+ end
53
+ end
54
+
55
+ def compare_images_with_same_size?
56
+ @actual_image.image.rows == @expected_image.image.rows && @actual_image.image.columns == @expected_image.image.columns
57
+ end
58
+
59
+ def prep_images_for_comparison
60
+ [
61
+ @actual_image,
62
+ @expected_image,
63
+ ].collect do |looks_good_image|
64
+ image = looks_good_image.image.clone
65
+ image = yield image if block_given?
66
+
67
+ # Important: ensure the image 0,0 is reset to the top-left of the image before comparison
68
+ image.offset = 0
69
+ image
70
+ end
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,77 @@
1
+ require 'logger'
2
+
3
+ module LooksGood
4
+ module Configuration
5
+
6
+ class << self
7
+
8
+ attr_accessor :reference_image_path, :max_no_tries, :sleep_between_tries, :browser_folders, :default_within
9
+
10
+ attr_reader :paths
11
+
12
+ def reference_image_path
13
+ @reference_image_path ||= default_reference_path
14
+ @browser_folders ? (reference_path_with_browser_folders) : @reference_image_path
15
+ end
16
+
17
+ def default_within
18
+ @default_within ||= 0.01 # 1%
19
+ end
20
+
21
+ # allows retina mac screenshots to be scaled to expected size
22
+ def scale_amount
23
+ @scale_amount ||= 0.5
24
+ end
25
+
26
+ def max_no_tries
27
+ @max_no_tries ||= 1
28
+ end
29
+
30
+ def sleep_between_tries
31
+ @sleep_between_tries ||= 0.1
32
+ end
33
+
34
+ def path(type)
35
+ paths = {:reference => reference_image_path,
36
+ :temp => File.join(reference_image_path, 'tmp', 'tmp'),
37
+ :candidate => File.join(reference_image_path, 'temp', 'candidate'),
38
+ :diff => File.join(reference_image_path, 'temp', 'diff')
39
+ }
40
+ paths[type]
41
+ end
42
+
43
+ def default_reference_path
44
+ begin
45
+ reference_image_path = File.join(Rails.root, 'spec/screenshots')
46
+ rescue
47
+ reference_image_path = 'spec/screenshots'
48
+ puts "Currently defaulting to #{@reference_image_path}. Overide this by setting reference_image_path=[refpath] in your configuration block"
49
+ end
50
+ reference_image_path
51
+ end
52
+
53
+ def reference_path_with_browser_folders
54
+ begin
55
+ reference_images_path = File.join(@reference_image_path, browser)
56
+ rescue
57
+ reference_images_path = @reference_image_path
58
+ end
59
+ reference_images_path
60
+ end
61
+
62
+ def browser
63
+ begin
64
+ browser = Capybara.page.driver.browser.browser
65
+ rescue
66
+ browser = Selenium.page.driver.browser.browser
67
+ rescue
68
+ raise "Currently custom folders are only supported by Capybara"
69
+ return nil
70
+ end
71
+ browser.to_s
72
+ end
73
+
74
+ end
75
+
76
+ end
77
+ end
@@ -0,0 +1,73 @@
1
+ module LooksGood
2
+ class Image
3
+
4
+ attr_accessor :file_name, :path, :image
5
+
6
+ attr_reader :type
7
+
8
+ def initialize image, file_name
9
+ @file_name = file_name
10
+ @image = image
11
+ end
12
+
13
+ def save(type = :reference)
14
+ save_path = path(type)
15
+ FileUtils::mkdir_p(File.dirname(save_path)) unless File.exists?(save_path)
16
+ @image.write save_path
17
+ save_path
18
+ end
19
+
20
+ def exists?
21
+ File.exists?(path)
22
+ end
23
+
24
+ def path(type = :reference)
25
+ @path = File.join(LooksGood::Configuration.path(type), @file_name)
26
+ end
27
+
28
+ end
29
+
30
+ class ImageFromElement < Image
31
+
32
+ def initialize(element, file_name)
33
+ super(image, file_name)
34
+ @element = element
35
+ @image = capture_image
36
+ end
37
+
38
+ def verify_and_save
39
+ LooksGood::Configuration.max_no_tries.times do
40
+ comparable = capture_image
41
+ matches = LooksGood::Comparison.new(self,Image.new(comparable,@file_name)).matches?
42
+ if matches
43
+ self.save
44
+ puts "Saved #{self.path} as reference"
45
+ return()
46
+ else
47
+ @image = comparable
48
+ end
49
+ end
50
+ raise 'Could not save a stable image. This could be due to animations or page load times. Saved a reference image, delete it to re-try'
51
+ end
52
+
53
+ private
54
+ def capture_image
55
+ LooksGood::CaptureElement.capture(@element)
56
+ end
57
+
58
+ #TODO: make save a relevant subclass method
59
+ end
60
+
61
+ class ImageFromFile < Image
62
+
63
+ def initialize(file_name)
64
+ super(image, file_name)
65
+
66
+ @image = Magick::Image.read(path).first
67
+ end
68
+
69
+ #TODO: make save a relevant subclass method
70
+
71
+ end
72
+ end
73
+
@@ -0,0 +1,22 @@
1
+ require 'looks_good'
2
+
3
+ RSpec::Matchers.define :look_like do |expected|
4
+ result = nil
5
+ match do |actual|
6
+ if expected.is_a?(Symbol)
7
+ called_by_file = self.caller.find{|str| str.include?("_spec.rb")}
8
+ path_to = called_by_file.split("_spec.rb").first.split("spec/").last
9
+ expected = File.join(path_to, "#{expected}.png")
10
+ end
11
+ result = LooksGood.check(expected, actual)
12
+ result[:result]
13
+ end
14
+
15
+ failure_message do |actual|
16
+ actual_amount = result[:percent_difference] * 100
17
+ expected_amount = result[:comparison].within * 100
18
+ error_message = "expected '#{self.actual.path}' to match previous snapshot #{expected} by #{expected_amount.round(3)}%, but was off by #{actual_amount.round(3)}%\n"
19
+ error_message += result[:message]
20
+ end
21
+ end
22
+
@@ -0,0 +1,5 @@
1
+ RSpec.configure do |config|
2
+ config.before(:suite) do
3
+ LooksGood.cleanup
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module LooksGood
2
+ VERSION = "1.0.0"
3
+ end
data/lib/looks_good.rb ADDED
@@ -0,0 +1,104 @@
1
+ require 'rmagick'
2
+ require 'capybara'
3
+ require 'capybara/dsl'
4
+
5
+ require 'looks_good/configuration'
6
+ require 'looks_good/image'
7
+ require 'looks_good/comparison'
8
+ require 'looks_good/capture_element'
9
+ require 'looks_good/rspec_config'
10
+
11
+ module LooksGood
12
+ class << self
13
+
14
+ def check(expected_reference_filename, actual_element, within: LooksGood::Configuration.default_within)
15
+ result = match_result(expected_reference_filename, actual_element, within: within)
16
+ result
17
+ end
18
+
19
+ def match_result(expected_reference_filename, actual_element, within:)
20
+ result_hash = {}
21
+ @actual_element = actual_element
22
+ @expected_reference_filename = expected_reference_filename
23
+ @expected_reference_file = (File.join(LooksGood::Configuration.path(:reference), expected_reference_filename))
24
+
25
+
26
+ if !File.exists?(@expected_reference_file) || ENV["LOOKS_GOOD"]
27
+ save_reference
28
+ result_hash[:result] = true
29
+ return result_hash
30
+ else
31
+ reference_file = LooksGood::ImageFromFile.new(expected_reference_filename)
32
+ comparison = compare_until_match(actual_element, reference_file, within: within)
33
+ result_hash[:comparison] = comparison
34
+ result_hash[:percent_difference] = comparison.percent_difference
35
+ matches = comparison.matches?
36
+ if !matches
37
+ comparison.actual_image.save(:candidate)
38
+ save_image_as_diff(comparison.diff_image)
39
+ result_hash[:message] = %Q[view a visual diff image: open #{comparison.diff_image.path(:diff)}\n
40
+ HOW TO FIX:\n
41
+ - cp #{comparison.diff_image.path(:candidate)} #{@expected_reference_file}
42
+ or
43
+ - LOOKS_GOOD=true rspec ...]
44
+ result_hash[:result] = false
45
+ result_hash
46
+ else
47
+ result_hash[:result] = true
48
+ result_hash
49
+ end
50
+ end
51
+ end
52
+
53
+ def compare_until_match(actual_element, reference_file, within:, max_no_tries: LooksGood::Configuration.max_no_tries, sleep_time: LooksGood::Configuration.sleep_between_tries)
54
+ max_no_tries.times do |i|
55
+ actual_image = LooksGood::ImageFromElement.new(actual_element, reference_file.file_name)
56
+ @comparison = LooksGood::Comparison.new(actual_image, reference_file, within)
57
+ match = @comparison.matches?
58
+ if !match
59
+ sleep sleep_time
60
+ #TODO: Send to logger instead of puts
61
+ i += 1
62
+ puts "Tried to match #{i} times"
63
+ else
64
+ return(@comparison)
65
+ end
66
+ end
67
+ @comparison
68
+ end
69
+
70
+ def save_image_as_diff(image)
71
+ image.save(:diff)
72
+
73
+ end
74
+
75
+ def save_image_as_candidate(image)
76
+ image.save(:candidate)
77
+ raise "The design reference #{image.file_name} does not exist, #{image.path(:candidate)} " +
78
+ "is now available to be used as a reference. Copy candidate to root reference_image_path to use as reference"
79
+ end
80
+
81
+ def save_reference
82
+ ImageFromElement.new(@actual_element,@expected_reference_filename).verify_and_save
83
+ end
84
+
85
+ def cleanup
86
+ FileUtils.remove_dir(LooksGood::Configuration.path(:temp)) if File.directory?(LooksGood::Configuration.path(:temp))
87
+ FileUtils.remove_dir(LooksGood::Configuration.path(:diff)) if File.directory?(LooksGood::Configuration.path(:diff))
88
+ FileUtils.remove_dir(LooksGood::Configuration.path(:candidate)) if File.directory?(LooksGood::Configuration.path(:candidate))
89
+ end
90
+
91
+
92
+ def config(&block)
93
+ begin
94
+ config_class = LooksGood::Configuration
95
+ raise "No block provied" unless block_given?
96
+ block.call(config_class)
97
+ rescue
98
+ raise "Config block has changed. Example: LooksGood.config {|c| c.reference_image_path = 'some/path'}. Please see README"
99
+ end
100
+ end
101
+
102
+ end
103
+ end
104
+
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "looks_good/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "looks_good"
7
+ s.version = LooksGood::VERSION
8
+ s.authors = ["Russell Jennings"]
9
+ s.email = ["violentpurr@gmail.com"]
10
+ s.homepage = "http://github.com/meesterdude/looks_good"
11
+ s.summary = %q{Rspec visual testing}
12
+ s.description = %q{Rspec visual testing with percent matching tolerance}
13
+
14
+ s.rubyforge_project = "looks_good"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_runtime_dependency('rmagick', ['>=2.15.4'])
22
+ s.add_runtime_dependency('capybara',['>=2.6.0'])
23
+
24
+ s.add_development_dependency('rake',['>=0.9.2'])
25
+ s.add_development_dependency('pry',['>=0.10.2'])
26
+ s.add_development_dependency('selenium-webdriver',['~> 2.53.0'])
27
+ end