goggles 0.9.1 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +20 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +46 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +153 -0
- data/Rakefile +9 -0
- data/features/goggles.feature +23 -0
- data/features/step_definitions/goggles_steps.rb +27 -0
- data/features/support/env.rb +5 -0
- data/goggles.gemspec +28 -0
- data/lib/goggles.rb +62 -0
- data/lib/goggles/comparison.rb +186 -0
- data/lib/goggles/configuration.rb +37 -0
- data/lib/goggles/iteration.rb +64 -0
- data/lib/goggles/version.rb +3 -0
- data/spec/goggles/comparison_spec.rb +99 -0
- data/spec/goggles/configuration_spec.rb +54 -0
- data/spec/goggles/iteration_spec.rb +32 -0
- data/spec/goggles_spec.rb +133 -0
- data/spec/spec_helper.rb +2 -0
- metadata +33 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a461694240042737a93be6b6431a638bc7add76
|
4
|
+
data.tar.gz: 5651d5328ba134942e7f9e7a5d09a162b954c247
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 96a924a64413611a034f06bf262ed2f0123810c5b85c2c42b6204e318a5dc5f050320fe9e3dc0482ac277d3b1d6f836483cea151ae80f14cc541363c2910f113
|
7
|
+
data.tar.gz: d9f08ed3a405f18ff049d4491a22b28d6c9e120ff1dc32e2aca34323411f469ffd9ea9bbafa1ca89d66139111cba46e5e672043544c79c2fd8320c736070bba1
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# CHANGELOG
|
2
|
+
|
3
|
+
## v0.10.0 (Apr 16, 2015)
|
4
|
+
* Throw EmptyDirectoryError on iteration with unconfigured directory setting.
|
5
|
+
|
6
|
+
## v0.9.1 (Apr 14, 2015)
|
7
|
+
* Add YARD documentation.
|
8
|
+
|
9
|
+
## v0.9.0 (Apr 14, 2015)
|
10
|
+
* `Goggles.each` accepts Array, Integer, String, and Symbol arguments.
|
11
|
+
* Arguments passed to `Goggles.each` extend configured settings instead of replacing them.
|
12
|
+
* Add Cucumber tests.
|
13
|
+
|
14
|
+
## v0.8.2 (Apr 10, 2015)
|
15
|
+
* Fix another typo in the gem description.
|
16
|
+
|
17
|
+
## v0.8.1 (Apr 10, 2015)
|
18
|
+
* Fix typo in gem description.
|
19
|
+
|
20
|
+
## v0.8.0 (Apr 10, 2015)
|
21
|
+
* Remove `swim` executable.
|
22
|
+
* Remove YAML configuration.
|
23
|
+
* Add `Goggles.configure` for configuration through a block (like RSpec).
|
24
|
+
* Remove reserved instance variables like `@gg_url`.
|
25
|
+
* Scripts are executed via blocks passed to `Goggles.each`.
|
26
|
+
* `Goggles.each` yields a browser instance with the `#grab_screenshot` method.
|
27
|
+
* README updates.
|
28
|
+
* Add this CHANGELOG.
|
29
|
+
|
30
|
+
## v0.1.4 (Jan 28, 2014)
|
31
|
+
* Small code enhancements with no user impact.
|
32
|
+
|
33
|
+
## v0.1.3 (Jan 23, 2014)
|
34
|
+
* Do not delete cookies before each script.
|
35
|
+
* Make `@gg_url` variable available to scripts.
|
36
|
+
|
37
|
+
## v0.1.2 (Jan 21, 2014)
|
38
|
+
* Add diff color configuration setting.
|
39
|
+
* Default color setting to blue.
|
40
|
+
* Prepend all instance variables with `gg_`.
|
41
|
+
|
42
|
+
## v0.1.1 (Jan 20, 2014)
|
43
|
+
* Add command line option for generating default project
|
44
|
+
|
45
|
+
## v0.1.0 (Jan 20, 2014)
|
46
|
+
* Initial release
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Johnson Denen
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
# goggles
|
2
|
+
[![Gem Version](https://badge.fury.io/rb/goggles.png)](http://badge.fury.io/rb/goggles)
|
3
|
+
[![Build Status](https://travis-ci.org/jdenen/goggles.svg?branch=master)](https://travis-ci.org/jdenen/goggles)
|
4
|
+
|
5
|
+
Goggles is a visual testing tool inspired by [wraith](http://github.com/bbc-news/wraith) and powered by [watir-webdriver](http://github.com/watir/watir-webdriver). It compares screenshots of your web applications in different browsers at differents sizes.
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
### Configuration
|
10
|
+
|
11
|
+
Configure Goggles with a block passed to the `Goggles.configure` method, which will yield a config object to your block for manipulation. The `directory` setting must be configured for Goggles to work. You'll also need to provide `browsers` and `sizes` that you'd like to compare, but those can be configured with the script.
|
12
|
+
|
13
|
+
The `fuzzing` and `color` attributes default to "blue" and "20%" respectively.
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
Goggles.configure do |config|
|
17
|
+
config.directory = "/path/to/my/results"
|
18
|
+
config.browsers = [:chrome, :firefox, :phantomjs]
|
19
|
+
config.sizes = [1080, 600]
|
20
|
+
config.color = "red"
|
21
|
+
end
|
22
|
+
```
|
23
|
+
|
24
|
+
### Scripting
|
25
|
+
|
26
|
+
Your Scripts are passed to `Goggles.each` as blocks. Goggles will iterate over the block with each combination of browser/browser size configured, and the method will yield a browser object to your script block.
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
Goggles.each do |browser|
|
30
|
+
browser.goto "http://www.google.com"
|
31
|
+
browser.text_field(id: "lst-ib").value = "Google"
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
#### Browsers and sizes
|
36
|
+
|
37
|
+
You can pass additional browsers or browser sizes to `Goggles.each` as arrays. These arguments extend your configuration for this instance's script block only.
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
Goggles.configure do |c|
|
41
|
+
c.directory = "/some/directory"
|
42
|
+
c.browsers = [:chrome, :firefox]
|
43
|
+
c.sizes = [1080]
|
44
|
+
end
|
45
|
+
|
46
|
+
Goggles.each(:phantomjs, 500) do |browser|
|
47
|
+
# Script to be executed with Chrome, Firefox,
|
48
|
+
# and PhantomJS at widths of 1080 and 500.
|
49
|
+
end
|
50
|
+
|
51
|
+
Goggles.each do |browser|
|
52
|
+
# Script to be executed with Chrome and
|
53
|
+
# Firefox at a width of 1080.
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
#### Screenshots
|
58
|
+
|
59
|
+
Your script blocks should include the `Watir::Browser#grab_screenshot` method, which has been patched onto the browser objects yielded to your blocks. Simply give the method a description argument and the screenshot will be saved to your configured directory.
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
Goggles.each do |browser|
|
63
|
+
browser.goto "http://www.google.com"
|
64
|
+
browser.grab_screenshot "homepage"
|
65
|
+
end
|
66
|
+
```
|
67
|
+
|
68
|
+
#### Closing the browser
|
69
|
+
|
70
|
+
There's no need to explicitly close the browser objects in your script blocks. Goggles will handle that.
|
71
|
+
|
72
|
+
#### Results
|
73
|
+
|
74
|
+
Screenshots are saved to your configured directory. Screenshot comparison results are saved to a sub-folder based on the screenshot description and browser size. Results include a diff image and data file.
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
Goggles.configure do |c|
|
78
|
+
c.directory = "/goggles/results"
|
79
|
+
c.browsers = [:chrome, :firefox, :phantomjs]
|
80
|
+
c.sizes = [1080, 600]
|
81
|
+
end
|
82
|
+
|
83
|
+
Goggles.each do |browser|
|
84
|
+
browser.goto "http://www.google.com"
|
85
|
+
browser.grab_screenshot "google"
|
86
|
+
end
|
87
|
+
```
|
88
|
+
|
89
|
+
|
90
|
+
```
|
91
|
+
/goggles/results
|
92
|
+
|- google_1080_chrome.png
|
93
|
+
|- google_1080_firefox.png
|
94
|
+
|- google_1080_phantomjs.png
|
95
|
+
|- google_600_chrome.png
|
96
|
+
|- google_600_firefox.png
|
97
|
+
|- google_600_phantomjs.png
|
98
|
+
|- /google_1080
|
99
|
+
|- chrome_firefox_data.txt
|
100
|
+
|- chrome_firefox_diff.png
|
101
|
+
|- chrome_phantomjs_data.txt
|
102
|
+
|- chrome_phantomjs_diff.png
|
103
|
+
|- firefox_phantomjs_data.txt
|
104
|
+
|- firefox_phantomjs_diff.png
|
105
|
+
|- /google_600
|
106
|
+
|- chrome_firefox_data.txt
|
107
|
+
|- chrome_firefox_diff.png
|
108
|
+
|- chrome_phantomjs_data.txt
|
109
|
+
|- chrome_phantomjs_diff.png
|
110
|
+
|- firefox_phantomjs_data.txt
|
111
|
+
|- firefox_phantomjs_diff.png
|
112
|
+
```
|
113
|
+
|
114
|
+
## Road to 1.0.0
|
115
|
+
|
116
|
+
I've made a lot of changes recently and bumped the version up to 0.9.1. Check the [CHANGELOG](CHANGELOG.md) for more information about those changes.
|
117
|
+
|
118
|
+
### v1.0.0
|
119
|
+
|
120
|
+
* Documentation
|
121
|
+
* Examples
|
122
|
+
* TravisCI integration for specs (but not features)
|
123
|
+
|
124
|
+
## Installation
|
125
|
+
|
126
|
+
Install ImageMagick:
|
127
|
+
|
128
|
+
* OSX: `$ brew install imagemagick`
|
129
|
+
* Ubuntu: `$ sudo apt-get install imagemagick`
|
130
|
+
* Windows: [Download](http://www.imagemagick.org/script/binary-releases.php#windows) installer and add to your PATH.
|
131
|
+
|
132
|
+
Add this line to your application's Gemfile:
|
133
|
+
|
134
|
+
gem 'goggles'
|
135
|
+
|
136
|
+
And then execute:
|
137
|
+
|
138
|
+
$ bundle
|
139
|
+
|
140
|
+
Or install it yourself with:
|
141
|
+
|
142
|
+
$ gem install goggles
|
143
|
+
|
144
|
+
## Contributing
|
145
|
+
|
146
|
+
1. Fork it ( http://github.com/jdenen/goggles/fork )
|
147
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
148
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
149
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
150
|
+
5. Create new Pull Request
|
151
|
+
|
152
|
+
## Questions, Comments, Concerns
|
153
|
+
Find me on Twitter ([@jpdenen](http://twitter.com/jpdenen)) or write up an issue.
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Feature: Screenshot comparison
|
2
|
+
As a software professional
|
3
|
+
I want to automate the collection and comparison of screenshots on my application
|
4
|
+
So I can efficiently assess visual regression
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given I have configured Goggles with a valid directory
|
8
|
+
Given I have configured Goggles for browsers at 1080 width
|
9
|
+
|
10
|
+
Scenario: Compare screenshots in two browsers at one size
|
11
|
+
When I use "repo" to describe my screenshot of "github.com/jdenen/goggles"
|
12
|
+
Then file "repo_1080/chrome_firefox_diff.png" exists
|
13
|
+
And file "repo_1080/chrome_firefox_data.txt" exists
|
14
|
+
|
15
|
+
Scenario: Extending a script with more browsers and sizes
|
16
|
+
When I extend configuration with arguments "phantomjs, 500"
|
17
|
+
And I use "search" to describe my screenshot of "google.com"
|
18
|
+
Then file "search_1080/chrome_firefox_diff.png" exists
|
19
|
+
And file "search_1080/chrome_firefox_data.txt" exists
|
20
|
+
And file "search_1080/chrome_phantomjs_diff.png" exists
|
21
|
+
And file "search_1080/chrome_phantomjs_data.txt" exists
|
22
|
+
And file "search_1080/firefox_phantomjs_diff.png" exists
|
23
|
+
And file "search_1080/firefox_phantomjs_data.txt" exists
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Given /^I have configured Goggles with a valid directory$/ do
|
2
|
+
@results = Pathname.new("./results").realdirpath.to_s
|
3
|
+
Goggles.configure { |c| c.directory = @results }
|
4
|
+
end
|
5
|
+
|
6
|
+
Given /^I have configured Goggles for browsers at (\d+) width$/ do |width|
|
7
|
+
Goggles.configure do |c|
|
8
|
+
c.browsers = [:chrome, :firefox]
|
9
|
+
c.sizes = [width]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
When /^I extend configuration with arguments "(.*)"$/ do |args|
|
14
|
+
@args = args.split(/,/).map(&:strip)
|
15
|
+
end
|
16
|
+
|
17
|
+
When /^I use "(.*)" to describe my screenshot of "(.*)"$/ do |desc, site|
|
18
|
+
Goggles.each(@args) do |browser|
|
19
|
+
browser.goto "http://#{site}"
|
20
|
+
browser.grab_screenshot desc
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
Then /^file "(.*)" exists$/ do |file|
|
25
|
+
filepath = "#{@results}/#{file}"
|
26
|
+
File.exists? filepath
|
27
|
+
end
|
data/goggles.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'goggles/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "goggles"
|
8
|
+
spec.version = Goggles::VERSION
|
9
|
+
spec.authors = ["Johnson Denen"]
|
10
|
+
spec.email = ["johnson.denen@gmail.com"]
|
11
|
+
spec.summary = %q{Compare screenshots in different browsers at different sizes}
|
12
|
+
spec.description = %q{Compare screenshots in different browsers at different sizes}
|
13
|
+
spec.homepage = "http://github.com/jdenen/goggles"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
21
|
+
spec.add_development_dependency "rake"
|
22
|
+
spec.add_development_dependency "rspec"
|
23
|
+
spec.add_development_dependency "cucumber"
|
24
|
+
spec.add_development_dependency "yard"
|
25
|
+
|
26
|
+
spec.add_runtime_dependency "watir-webdriver"
|
27
|
+
spec.add_runtime_dependency "image_size"
|
28
|
+
end
|
data/lib/goggles.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require "goggles/configuration"
|
2
|
+
require "goggles/comparison"
|
3
|
+
require "goggles/iteration"
|
4
|
+
|
5
|
+
module Goggles
|
6
|
+
extend self
|
7
|
+
|
8
|
+
#
|
9
|
+
# Raise when the configuration attribute is unconfigured at runtime.
|
10
|
+
#
|
11
|
+
class EmptyDirectoryError < StandardError; end
|
12
|
+
|
13
|
+
#
|
14
|
+
# Yields the global configuration object to a block.
|
15
|
+
#
|
16
|
+
# @yield [Goggles::Configuration] global configuration
|
17
|
+
# @return [Goggles::Configuration] global configuration
|
18
|
+
#
|
19
|
+
def configure &block
|
20
|
+
configuration.tap { |conf| yield conf }
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# Creates an Iteration object for each combination of browser and width derived from global
|
25
|
+
# configuration and given arguments.
|
26
|
+
#
|
27
|
+
# @param instance [Array<String,Fixnum,Symbol>, String, Fixnum, Symbol] configuration extension
|
28
|
+
# @return [Goggles::Comparison]
|
29
|
+
# @see Goggles::Iteration
|
30
|
+
#
|
31
|
+
def each *instance, &block
|
32
|
+
validate_directory_setting
|
33
|
+
args = instance.flatten.map(&:to_s)
|
34
|
+
|
35
|
+
sizes = configuration.sizes + args.grep(/\d+/).map(&:to_i)
|
36
|
+
browsers = configuration.browsers + args.grep(/[^\d+]/).map(&:to_sym)
|
37
|
+
|
38
|
+
browsers.product(sizes).each do |browser, size|
|
39
|
+
Iteration.new browser, size, configuration, &block
|
40
|
+
end
|
41
|
+
|
42
|
+
Comparison.new(configuration).tap { |comparison| comparison.make! }
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
#
|
48
|
+
# @api private
|
49
|
+
#
|
50
|
+
def configuration
|
51
|
+
@configuration ||= Configuration.new
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# @api private
|
56
|
+
#
|
57
|
+
def validate_directory_setting
|
58
|
+
message = "Expected configured directory setting, got: #{configuration.directory}"
|
59
|
+
raise EmptyDirectoryError, message if configuration.directory.nil?
|
60
|
+
raise EmptyDirectoryError, message if configuration.directory.empty?
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
require "image_size"
|
2
|
+
|
3
|
+
module Goggles
|
4
|
+
|
5
|
+
#
|
6
|
+
# Generates a diff image and data file from two images.
|
7
|
+
#
|
8
|
+
# Results are saved to a new directory derived from the configured directory setting,
|
9
|
+
# a description given to `Watir::Browser.grab_screenshot`, browser size, and the
|
10
|
+
# browsers used to take the screenshots.
|
11
|
+
#
|
12
|
+
# @example Results
|
13
|
+
# Goggles.configure do |c|
|
14
|
+
# c.directory = "/path/results"
|
15
|
+
# c.browsers = [:chrome, :firefox]
|
16
|
+
# c.sizes = [100]
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# Goggles.each do |b|
|
20
|
+
# b.goto "www.google.com"
|
21
|
+
# b.grab_screenshot "search"
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# #=> /path/results
|
25
|
+
# # |- /search_100
|
26
|
+
# # |- chrome_firefox_data.txt
|
27
|
+
# # |- chrome_firefox_diff.png
|
28
|
+
#
|
29
|
+
class Comparison
|
30
|
+
attr_reader :directory, :fuzzing, :color, :groups, :results_dir
|
31
|
+
|
32
|
+
def initialize config
|
33
|
+
@directory = config.directory
|
34
|
+
@fuzzing = config.fuzzing
|
35
|
+
@color = config.color
|
36
|
+
@groups = config.groups
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# Sizes and compare screenshots.
|
41
|
+
#
|
42
|
+
# @return [nil]
|
43
|
+
#
|
44
|
+
def make!
|
45
|
+
cut_to_common_size
|
46
|
+
highlight_differences
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# Sizes comparable screenshots to a common height and width.
|
51
|
+
#
|
52
|
+
# @return [nil]
|
53
|
+
#
|
54
|
+
def cut_to_common_size
|
55
|
+
groups.each_with_object([]) do |group, sizes|
|
56
|
+
collection = find_comparable group
|
57
|
+
|
58
|
+
collection.each do |img|
|
59
|
+
File.open(img, 'rb'){ |file| sizes << read_size(file) }
|
60
|
+
end
|
61
|
+
|
62
|
+
cut! collection, sizes
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# Generates diff images for comparable screenshots.
|
68
|
+
#
|
69
|
+
# @return [nil]
|
70
|
+
#
|
71
|
+
def highlight_differences
|
72
|
+
groups.each do |desc|
|
73
|
+
ensure_result_directory desc
|
74
|
+
find_comparable(desc).combination(2).to_a.each { |imgs| diff imgs[0], imgs[1] }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
#
|
79
|
+
# Finds comparable screenshots for sizing and comparing.
|
80
|
+
#
|
81
|
+
# @param description [String] screenshot description
|
82
|
+
# @return [Array<String>] paths to comparable screenshots
|
83
|
+
#
|
84
|
+
def find_comparable description
|
85
|
+
Dir.glob("#{directory}/*.png").grep(/#{description}_/).sort
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
# Finds the smallest first element for all arrays in the given array. This is
|
90
|
+
# the screenshot width attribute. Used for resizing.
|
91
|
+
#
|
92
|
+
# @param array [Array<Array>] collection of screenshot sizes
|
93
|
+
# @return [Fixnum] smallest width
|
94
|
+
#
|
95
|
+
def find_common_width array
|
96
|
+
array.collect(&:first).sort.first
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# Finds the smallest second element for all arrays in the given array. This is
|
101
|
+
# the screenshot height attribute. Used for resizing.
|
102
|
+
#
|
103
|
+
# @param array [Array<Array>] collection of screenshot sizes
|
104
|
+
# @return [Fixnum] smallest screenshot height
|
105
|
+
#
|
106
|
+
def find_common_height array
|
107
|
+
array.collect(&:last).sort.first
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
attr_writer :results_dir
|
113
|
+
|
114
|
+
#
|
115
|
+
# @api private
|
116
|
+
#
|
117
|
+
# Cuts comparable screenshots to smallest width by smallest height with ImageMagick.
|
118
|
+
#
|
119
|
+
# @param images [Array<String>] comparable screenshots
|
120
|
+
# @param sizes [Array<Fixnum>] sizes (width, height) of comparable screenshots
|
121
|
+
# @return [nil]
|
122
|
+
#
|
123
|
+
def cut! images, sizes
|
124
|
+
w = find_common_width sizes
|
125
|
+
h = find_common_height sizes
|
126
|
+
images.each { |img| `convert #{img} -background none -extent #{w}x#{h} #{img}` }
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# @api private
|
131
|
+
#
|
132
|
+
# Generates comparison data for two screenshots with ImageMagick.
|
133
|
+
#
|
134
|
+
# @param img_one [String] first screenshot path
|
135
|
+
# @param img_two [String] second screenshot path
|
136
|
+
# @return [nil]
|
137
|
+
#
|
138
|
+
def diff img_one, img_two
|
139
|
+
b1 = diffed img_one
|
140
|
+
b2 = diffed img_two
|
141
|
+
|
142
|
+
fuzz = "#{results_dir}/#{b1}_#{b2}_diff.png"
|
143
|
+
data = "#{results_dir}/#{b1}_#{b2}_data.txt"
|
144
|
+
|
145
|
+
`compare -fuzz #{fuzzing} -metric AE -highlight-color #{color} #{img_one} #{img_two} #{fuzz} 2>#{data}`
|
146
|
+
end
|
147
|
+
|
148
|
+
#
|
149
|
+
# @api private
|
150
|
+
#
|
151
|
+
# Parses a browser from a screenshot filepath.
|
152
|
+
#
|
153
|
+
# @param img [String] screenshot path
|
154
|
+
# @return [String] browser
|
155
|
+
#
|
156
|
+
def diffed img
|
157
|
+
File.basename(img).match(/\d+_(.*)\.png/)[1]
|
158
|
+
end
|
159
|
+
|
160
|
+
#
|
161
|
+
# @api private
|
162
|
+
#
|
163
|
+
# Finds the width and height of an image.
|
164
|
+
#
|
165
|
+
# @param file [File] image
|
166
|
+
# @return [Array<Fixnum>] width and height
|
167
|
+
#
|
168
|
+
def read_size file
|
169
|
+
ImageSize.new(file.read).size
|
170
|
+
end
|
171
|
+
|
172
|
+
#
|
173
|
+
# @api private
|
174
|
+
#
|
175
|
+
# Ensures the results directory for comparable screenshots exists.
|
176
|
+
#
|
177
|
+
# @param description [String] screenshot description
|
178
|
+
# @return [nil]
|
179
|
+
#
|
180
|
+
def ensure_result_directory description
|
181
|
+
self.results_dir = "#{directory}/#{description}"
|
182
|
+
FileUtils.rm_rf results_dir
|
183
|
+
FileUtils.mkdir_p results_dir
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Goggles
|
2
|
+
|
3
|
+
#
|
4
|
+
# Stores configuration information for runtime.
|
5
|
+
#
|
6
|
+
# The `directory` setting +must+ be configured.
|
7
|
+
#
|
8
|
+
# The `browsers` and `sizes` settings can be extended through `Goggles.each` as arguments.
|
9
|
+
#
|
10
|
+
# @see Goggles.each
|
11
|
+
# @see Goggles.configure
|
12
|
+
#
|
13
|
+
class Configuration
|
14
|
+
attr_accessor :browsers, :sizes, :fuzzing, :color, :groups
|
15
|
+
attr_reader :directory
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@browsers = []
|
19
|
+
@sizes = []
|
20
|
+
@groups = []
|
21
|
+
@directory = ""
|
22
|
+
@color = "blue"
|
23
|
+
@fuzzing = "20%"
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Ensures the configured path exists, but otherwise acts as a normal
|
28
|
+
# attr_accessor. Path argument must be an absolute path.
|
29
|
+
#
|
30
|
+
# @param path [String] directory path
|
31
|
+
#
|
32
|
+
def directory=(path)
|
33
|
+
@directory = path
|
34
|
+
FileUtils.mkdir_p path unless path.nil? or path.empty?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require "watir-webdriver"
|
2
|
+
|
3
|
+
module Watir
|
4
|
+
class Browser
|
5
|
+
attr_accessor :goggles, :iteration
|
6
|
+
|
7
|
+
#
|
8
|
+
# Saves a screenshot as "given-description_width.png" in the configured results
|
9
|
+
# directory.
|
10
|
+
#
|
11
|
+
# @param name [String] screenshot description
|
12
|
+
# @return [nil]
|
13
|
+
#
|
14
|
+
def grab_screenshot name
|
15
|
+
description = "#{name}_#{iteration.size}"
|
16
|
+
goggles.groups << description unless goggles.groups.include? description
|
17
|
+
screenshot.save "#{goggles.directory}/#{description}_#{iteration.browser_name}.png"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module Goggles
|
23
|
+
|
24
|
+
#
|
25
|
+
# Executes the block passed to `Goggles.each` with every configured combination of browser
|
26
|
+
# and browser size.
|
27
|
+
#
|
28
|
+
# @see Goggles.each
|
29
|
+
#
|
30
|
+
class Iteration
|
31
|
+
attr_reader :browser, :browser_name, :size, :config
|
32
|
+
|
33
|
+
#
|
34
|
+
# Creates a script iteration instance, building yielded browser object from the
|
35
|
+
# given arguments. Closes the browser instance after yielding to the block.
|
36
|
+
#
|
37
|
+
# @param driver [String, Symbol] browser name
|
38
|
+
# @param width [Fixnum] browser width
|
39
|
+
# @param config [Goggles::Configuration] global configuration
|
40
|
+
# @yield [Watir::Browser] browser object
|
41
|
+
#
|
42
|
+
def initialize driver, width, config, &block
|
43
|
+
@browser_name = driver
|
44
|
+
@config = config
|
45
|
+
@size = width
|
46
|
+
build_browser
|
47
|
+
yield browser
|
48
|
+
browser.close
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
#
|
54
|
+
# @api private
|
55
|
+
#
|
56
|
+
def build_browser
|
57
|
+
@browser = Watir::Browser.new(browser_name).tap do |engine|
|
58
|
+
engine.goggles = config
|
59
|
+
engine.iteration = self
|
60
|
+
engine.driver.manage.window.resize_to size, 768
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Goggles::Comparison do
|
4
|
+
let(:config){ instance_double "configuration" }
|
5
|
+
let(:comparison){ Goggles::Comparison.new config }
|
6
|
+
|
7
|
+
before do
|
8
|
+
allow(config).to receive_messages(
|
9
|
+
directory: "dir",
|
10
|
+
fuzzing: "20%",
|
11
|
+
color: "blue",
|
12
|
+
groups: [1])
|
13
|
+
end
|
14
|
+
|
15
|
+
it "reads attributes from a given configuration object" do
|
16
|
+
expect(config).to receive(:directory)
|
17
|
+
expect(config).to receive(:fuzzing)
|
18
|
+
expect(config).to receive(:color)
|
19
|
+
Goggles::Comparison.new config
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#make!" do
|
23
|
+
it "cuts images and highlights their differences" do
|
24
|
+
expect(comparison).to receive(:cut_to_common_size)
|
25
|
+
expect(comparison).to receive(:highlight_differences)
|
26
|
+
comparison.make!
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#cut_to_common_size" do
|
31
|
+
it "iterates over screenshot descriptions" do
|
32
|
+
groups = instance_double "[groups]"
|
33
|
+
expect(comparison).to receive(:groups).and_return groups
|
34
|
+
expect(groups).to receive(:each_with_object).with([])
|
35
|
+
comparison.cut_to_common_size
|
36
|
+
end
|
37
|
+
|
38
|
+
it "collects comparable screenshots" do
|
39
|
+
expect(comparison).to receive(:groups).and_return [1, 2]
|
40
|
+
expect(comparison).to receive(:find_comparable).with(1).and_return([])
|
41
|
+
expect(comparison).to receive(:find_comparable).with(2).and_return([])
|
42
|
+
comparison.cut_to_common_size
|
43
|
+
end
|
44
|
+
|
45
|
+
context "with collection of comparable screenshots" do
|
46
|
+
it "iterates over the collection" do
|
47
|
+
screens = instance_double "[screenshots]"
|
48
|
+
expect(comparison).to receive(:groups).and_return ["foo"]
|
49
|
+
expect(comparison).to receive(:find_comparable).and_return screens
|
50
|
+
expect(screens).to receive(:each)
|
51
|
+
expect(comparison).to receive(:cut!).with(screens, [])
|
52
|
+
comparison.cut_to_common_size
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "while iterating over collected comparable images" do
|
57
|
+
it "opens each image in binary mode" do
|
58
|
+
expect(comparison).to receive(:find_comparable).and_return [:foo]
|
59
|
+
expect(File).to receive(:open).with(:foo, 'rb')
|
60
|
+
expect(comparison).to receive(:cut!)
|
61
|
+
comparison.cut_to_common_size
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "#highlight_differences" do
|
67
|
+
it "diffs every combination of comparable screenshots" do
|
68
|
+
images = ["foo_1.png", "foo_2.png", "foo_3.png"]
|
69
|
+
expect(comparison).to receive(:groups).and_return ["foo"]
|
70
|
+
expect(comparison).to receive(:find_comparable).with("foo").and_return images
|
71
|
+
images.combination(2).to_a.each do |i|
|
72
|
+
expect(comparison).to receive(:diff).with i[0], i[1]
|
73
|
+
end
|
74
|
+
comparison.highlight_differences
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "#find_comparable" do
|
79
|
+
it "returns an array of file paths" do
|
80
|
+
array = ["/foo_chrome.png", "/bar_chrome.png", "/foo_ff.png", "/bar_ff.png"]
|
81
|
+
expect(Dir).to receive(:glob).and_return array
|
82
|
+
expect(comparison.find_comparable "foo").to eq ["/foo_chrome.png", "/foo_ff.png"]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "#find_common_width" do
|
87
|
+
it "returns the smallest number from first items in an array of arrays" do
|
88
|
+
arrays = [[100,40], [68,300], [104,98]]
|
89
|
+
expect(comparison.find_common_width arrays).to eq 68
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "#find_common_height" do
|
94
|
+
it "returns the smallest number from second items in an array of arrays" do
|
95
|
+
arrays = [[100,40], [68,300], [104,98]]
|
96
|
+
expect(comparison.find_common_height arrays).to eq 40
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Goggles::Configuration do
|
4
|
+
let(:config) { Goggles::Configuration.new }
|
5
|
+
|
6
|
+
it "has attributes by default" do
|
7
|
+
expect(config).to have_attributes(
|
8
|
+
:browsers => [],
|
9
|
+
:sizes => [],
|
10
|
+
:groups => [],
|
11
|
+
:directory => "",
|
12
|
+
:color => "blue",
|
13
|
+
:fuzzing => "20%"
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "has attributes that can be set" do
|
18
|
+
config.browsers << "chrome"
|
19
|
+
config.sizes << 100
|
20
|
+
config.groups << "foo"
|
21
|
+
config.color = "red"
|
22
|
+
config.fuzzing = "10%"
|
23
|
+
|
24
|
+
expect(config).to have_attributes(
|
25
|
+
:browsers => ["chrome"],
|
26
|
+
:sizes => [100],
|
27
|
+
:groups => ["foo"],
|
28
|
+
:color => "red",
|
29
|
+
:fuzzing => "10%"
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#directory=" do
|
34
|
+
it "ensures the directory exists" do
|
35
|
+
expect(FileUtils).to receive(:mkdir_p).with "/foo/bar"
|
36
|
+
config.directory = "/foo/bar"
|
37
|
+
expect(config.directory).to eq "/foo/bar"
|
38
|
+
end
|
39
|
+
|
40
|
+
context "when attribute is empty" do
|
41
|
+
it "does not create the directory" do
|
42
|
+
expect(FileUtils).to_not receive(:mkdir_p)
|
43
|
+
config.directory = ""
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "when attribute is nil" do
|
48
|
+
it "does not create the directory" do
|
49
|
+
expect(FileUtils).to_not receive(:mkdir_p)
|
50
|
+
config.directory = nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Goggles::Iteration do
|
4
|
+
let(:watir) { instance_double("browser") }
|
5
|
+
|
6
|
+
before do
|
7
|
+
allow(watir).to receive_messages(
|
8
|
+
:driver => watir,
|
9
|
+
:manage => watir,
|
10
|
+
:window => watir,
|
11
|
+
:goggles= => nil,
|
12
|
+
:iteration= => nil,
|
13
|
+
:resize_to => nil,
|
14
|
+
:close => nil)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "yields an instantiated browser" do
|
18
|
+
expect(Watir::Browser).to receive(:new).and_return watir
|
19
|
+
expect { |b| Goggles::Iteration.new "", "", "", &b }.to yield_with_args watir
|
20
|
+
end
|
21
|
+
|
22
|
+
it "yields the browser based on the browser_name attribute" do
|
23
|
+
expect(Watir::Browser).to receive(:new).with(:foo).and_return watir
|
24
|
+
Goggles::Iteration.new(:foo, "", ""){ "bar" }
|
25
|
+
end
|
26
|
+
|
27
|
+
it "resizes the browser based on the size attribute" do
|
28
|
+
expect(Watir::Browser).to receive(:new).with(:foo).and_return watir
|
29
|
+
expect(watir).to receive(:resize_to).with(500, 768)
|
30
|
+
Goggles::Iteration.new(:foo, 500, ""){ "bar" }
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Goggles do
|
4
|
+
before { allow(FileUtils).to receive(:mkdir_p) }
|
5
|
+
|
6
|
+
describe ".configure" do
|
7
|
+
it "yields a configuration object" do
|
8
|
+
expect { |b| Goggles.configure &b }.to yield_with_args Goggles::Configuration
|
9
|
+
end
|
10
|
+
|
11
|
+
it "returns a configuration object" do
|
12
|
+
expect(Goggles.configure { "foo" }).to be_a Goggles::Configuration
|
13
|
+
end
|
14
|
+
|
15
|
+
it "memoizes the configuration object" do
|
16
|
+
expect(Goggles.configure { "foo" }).to equal Goggles.configure { "bar" }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe ".each" do
|
21
|
+
let(:config) do
|
22
|
+
Goggles.configure do |conf|
|
23
|
+
conf.browsers = [:foo]
|
24
|
+
conf.sizes = [500]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
before do
|
29
|
+
allow(config).to receive_messages(directory: "/dir", fuzzing: "20%", color: "blue", groups: [])
|
30
|
+
end
|
31
|
+
|
32
|
+
it "passes browser, width, and configuration to an iteration object" do
|
33
|
+
expect(Goggles::Iteration).to receive(:new).with(:foo, 500, config)
|
34
|
+
Goggles.each { "foo" }
|
35
|
+
end
|
36
|
+
|
37
|
+
context "when directory setting is nil" do
|
38
|
+
it "throws an EmptyDirectoryError" do
|
39
|
+
allow(config).to receive(:directory).and_return(nil)
|
40
|
+
expect do
|
41
|
+
Goggles.each { "foo" }
|
42
|
+
end.to raise_error(Goggles::EmptyDirectoryError)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "when directory setting is empty" do
|
47
|
+
it "throws an EmptyDirectoryError" do
|
48
|
+
allow(config).to receive(:directory).and_return("")
|
49
|
+
expect do
|
50
|
+
Goggles.each { "foo" }
|
51
|
+
end.to raise_error(Goggles::EmptyDirectoryError)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "when given a browser argument" do
|
56
|
+
it "extends the configured browser list with the given browser" do
|
57
|
+
expect(Goggles::Iteration).to receive(:new).with(:foo, 500, config)
|
58
|
+
expect(Goggles::Iteration).to receive(:new).with(:bar, 500, config)
|
59
|
+
Goggles.each(:bar) { "foo" }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context "when given a size argument" do
|
64
|
+
it "extends the configured size list with the given size" do
|
65
|
+
expect(Goggles::Iteration).to receive(:new).with(:foo, 500, config)
|
66
|
+
expect(Goggles::Iteration).to receive(:new).with(:foo, 250, config)
|
67
|
+
Goggles.each(250) { "foo" }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "when given multiple browser and size arguments" do
|
72
|
+
it "extends the configuration with correctly" do
|
73
|
+
[:foo, :bar, :baz].product([500, 1000, 250]).each do |b, s|
|
74
|
+
expect(Goggles::Iteration).to receive(:new).with(b, s, config)
|
75
|
+
end
|
76
|
+
Goggles.each(:bar, :baz, 1000, 250) { "foo" }
|
77
|
+
end
|
78
|
+
|
79
|
+
context "in no particular order" do
|
80
|
+
it "extends the configuration with the correct arguments" do
|
81
|
+
[:foo, :bar, :baz].product([500, 1000, 250]).each do |b, s|
|
82
|
+
expect(Goggles::Iteration).to receive(:new).with(b, s, config)
|
83
|
+
end
|
84
|
+
Goggles.each(1000, :bar, :baz, 250) { "foo" }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context "when given arguments in arrays" do
|
90
|
+
it "extends the configuration correctly" do
|
91
|
+
[:foo, :bar, :baz].product([500, 1000, 250]).each do |b, s|
|
92
|
+
expect(Goggles::Iteration).to receive(:new).with(b, s, config)
|
93
|
+
end
|
94
|
+
Goggles.each([:bar, :baz], [250, 1000]) { "foo" }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context "when given arguments of various classes" do
|
99
|
+
it "extends the configuration correctly" do
|
100
|
+
[:foo, :bar, :baz].product([500, 1000, 250]).each do |b, s|
|
101
|
+
expect(Goggles::Iteration).to receive(:new).with(b, s, config)
|
102
|
+
end
|
103
|
+
Goggles.each([:bar, "baz"], 250, "1000") { "foo" }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
it "returns a comparison object" do
|
108
|
+
expect(Goggles::Iteration).to receive(:new).with(:foo, 500, config)
|
109
|
+
expect(Goggles.each { "foo" }).to be_a Goggles::Comparison
|
110
|
+
end
|
111
|
+
|
112
|
+
context "when configured for browsers at one size" do
|
113
|
+
it "creates an iteration for each browser with the width" do
|
114
|
+
config.browsers << :bar
|
115
|
+
[:foo, :bar].each { |browser| expect(Goggles::Iteration).to receive(:new).with browser, 500, config }
|
116
|
+
Goggles.each { "foo" }
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context "when configured for browsers at different sizes" do
|
121
|
+
it "creates an iteration for every browser and width combination" do
|
122
|
+
config.browsers << :bar
|
123
|
+
config.sizes << 1000
|
124
|
+
|
125
|
+
[:foo, :bar].product([500, 1000]).each do |browser, width|
|
126
|
+
expect(Goggles::Iteration).to receive(:new).with browser, width, Object
|
127
|
+
end
|
128
|
+
|
129
|
+
Goggles.each { "foo" }
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: goggles
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Johnson Denen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-04-
|
11
|
+
date: 2015-04-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -114,7 +114,28 @@ email:
|
|
114
114
|
executables: []
|
115
115
|
extensions: []
|
116
116
|
extra_rdoc_files: []
|
117
|
-
files:
|
117
|
+
files:
|
118
|
+
- .gitignore
|
119
|
+
- .travis.yml
|
120
|
+
- CHANGELOG.md
|
121
|
+
- Gemfile
|
122
|
+
- LICENSE.txt
|
123
|
+
- README.md
|
124
|
+
- Rakefile
|
125
|
+
- features/goggles.feature
|
126
|
+
- features/step_definitions/goggles_steps.rb
|
127
|
+
- features/support/env.rb
|
128
|
+
- goggles.gemspec
|
129
|
+
- lib/goggles.rb
|
130
|
+
- lib/goggles/comparison.rb
|
131
|
+
- lib/goggles/configuration.rb
|
132
|
+
- lib/goggles/iteration.rb
|
133
|
+
- lib/goggles/version.rb
|
134
|
+
- spec/goggles/comparison_spec.rb
|
135
|
+
- spec/goggles/configuration_spec.rb
|
136
|
+
- spec/goggles/iteration_spec.rb
|
137
|
+
- spec/goggles_spec.rb
|
138
|
+
- spec/spec_helper.rb
|
118
139
|
homepage: http://github.com/jdenen/goggles
|
119
140
|
licenses:
|
120
141
|
- MIT
|
@@ -139,5 +160,13 @@ rubygems_version: 2.0.14
|
|
139
160
|
signing_key:
|
140
161
|
specification_version: 4
|
141
162
|
summary: Compare screenshots in different browsers at different sizes
|
142
|
-
test_files:
|
163
|
+
test_files:
|
164
|
+
- features/goggles.feature
|
165
|
+
- features/step_definitions/goggles_steps.rb
|
166
|
+
- features/support/env.rb
|
167
|
+
- spec/goggles/comparison_spec.rb
|
168
|
+
- spec/goggles/configuration_spec.rb
|
169
|
+
- spec/goggles/iteration_spec.rb
|
170
|
+
- spec/goggles_spec.rb
|
171
|
+
- spec/spec_helper.rb
|
143
172
|
has_rdoc:
|