goggles 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ *.png
4
+ *_data.txt
5
+ .bundle
6
+ .config
7
+ .yardoc
8
+ Gemfile.lock
9
+ InstalledFiles
10
+ _yardoc
11
+ coverage
12
+ doc/
13
+ lib/bundler/man
14
+ pkg
15
+ rdoc
16
+ spec/reports
17
+ test/tmp
18
+ test/version_tmp
19
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in goggles.gemspec
4
+ gemspec
@@ -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.
@@ -0,0 +1,86 @@
1
+ # goggles
2
+
3
+ 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 allows you to compare screenshots of your web application in different browsers, and you can execute Ruby scripts to setup as many screenshots as you need.
4
+
5
+ ## Installation
6
+
7
+ Install ImageMagick:
8
+
9
+ * OSX: `$ brew install imagemagick`
10
+ * Linux: `$ sudo apt-get install imagemagick`
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ gem 'goggles'
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself with:
21
+
22
+ $ gem install goggles
23
+
24
+ ## Usage
25
+
26
+ Create a config file to point goggles in the right direction.
27
+ ``` yaml
28
+ # config.yaml
29
+ # Directory where you want to store your results. Required.
30
+ results_directory: "/home/results"
31
+
32
+ # Directory where you're storing your scripts. Optional.
33
+ scripts_directory: "/home/scripts"
34
+
35
+ # Scripts to execute in the scripts directory. Optional.
36
+ scripts_to_execute:
37
+ - "search.rb"
38
+ - "login.rb"
39
+
40
+ # Domain to test. Required.
41
+ domain_under_test: "http://www.manta.com"
42
+
43
+ # Paths to pages you want to test. Label them with a page name. Required.
44
+ paths_to_capture:
45
+ home: "/"
46
+ search: "/mb"
47
+
48
+ # Browsers you want to compare. Cannot specify more than two (yet). Required.
49
+ browsers:
50
+ - "chrome"
51
+ - "firefox"
52
+
53
+ # Widths at which you would like screenshots compared. All screenshots will be taken at a height of 768. Required.
54
+ browser_widths:
55
+ - 1024
56
+ - 600
57
+ - 320
58
+
59
+ # Fuzzing percentage. Play around with this to find the right fit. Required.
60
+ image_fuzzing: "20%"
61
+ ```
62
+
63
+ If you pass scripts to goggles as part of your testing, you **must** specify when screenshots should be taken with the `#grab_screenshot` method. Otherwise, goggles will open each of your paths and take a screenshot.
64
+
65
+ NOTE: I've tried to keep variable names as unlikely to interrupt your code as possible, but `@watir` is reserved for the browser instance currently executing scripts.
66
+
67
+ ``` ruby
68
+ # script_to_execute.rb
69
+ require 'goggles'
70
+ @watir.cookies.add("cookie_name", "cookie_value")
71
+
72
+ # Pass a short description to the method for naming the resultant screenshot
73
+ Goggles.grab_screenshot("with_cookie_set")
74
+ ```
75
+
76
+ Execute a goggles test through the command line with `swim --config CONFIG_FILE` or use `swim --help` to see command options.
77
+
78
+ $ swim -c config.yml
79
+
80
+ ## Contributing
81
+
82
+ 1. Fork it ( http://github.com/jdenen/goggles/fork )
83
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
84
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
85
+ 4. Push to the branch (`git push origin my-new-feature`)
86
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'goggles/cli'
4
+
5
+ Goggles::CLI.new ARGV
@@ -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 = ["jdenen@manta.com"]
11
+ spec.summary = %q{comparing responsive screenshots under watir-webdriver}
12
+ spec.description = %q{comparing responsive screenshots under watir-webdriver}
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
+
24
+ spec.add_runtime_dependency "watir-webdriver"
25
+ spec.add_runtime_dependency "image_size"
26
+
27
+ spec.executables << "swim"
28
+ end
@@ -0,0 +1,80 @@
1
+ require "goggles/version"
2
+ require "goggles/comparison"
3
+ require "goggles/scripter"
4
+ require "goggles/error"
5
+
6
+ require "watir-webdriver"
7
+ require "yaml"
8
+ require "fileutils"
9
+
10
+ module Goggles
11
+ extend self
12
+
13
+ def swim(config_path)
14
+ conf = YAML::load(File.open(config_path))
15
+
16
+ @goggles_result_dir = conf['results_directory']
17
+ @goggles_script_dir = conf['scripts_directory']
18
+ @goggles_domain = conf['domain_under_test']
19
+ @goggles_paths = conf['paths_to_capture']
20
+ @goggles_scripts = conf['scripts_to_execute']
21
+ @goggles_platforms = conf['browsers']
22
+ @goggles_widths = conf['browser_widths']
23
+ @goggles_fuzz = conf['image_fuzzing']
24
+
25
+ embark!
26
+ diff_images
27
+ end
28
+
29
+ def grab_screenshot(detail)
30
+ make_result_dir
31
+
32
+ image_dir = "#{@goggles_result_dir}/#{@gg_label}"
33
+ image_name = "#{detail}_#{@gg_size}_#{@gg_browser}"
34
+
35
+ @watir.screenshot.save "#{image_dir}/#{image_name}.png"
36
+ end
37
+
38
+ private
39
+
40
+ def embark!
41
+ ensure_fresh_start
42
+
43
+ @goggles_widths.each do |size|
44
+ @gg_size = size.to_i
45
+
46
+ @goggles_platforms.each do |pf|
47
+ @gg_browser = pf
48
+
49
+ @watir = Watir::Browser.new pf.to_sym
50
+ @watir.driver.manage.window.resize_to(@gg_size, 768)
51
+
52
+ @goggles_paths.each do |label, path|
53
+ @gg_label = label
54
+ url = "#{@goggles_domain}#{path}"
55
+
56
+ if @goggles_scripts.nil?
57
+ @watir.goto url
58
+ grab_screenshot("screenshot")
59
+ else
60
+ @goggles_scripts.each do |script|
61
+ script = "#{@goggles_script_dir}/#{script}"
62
+ execute_script(url, script)
63
+ end
64
+ end
65
+ end
66
+
67
+ @watir.close
68
+ end
69
+ end
70
+ end
71
+
72
+ def ensure_fresh_start
73
+ FileUtils.rm_rf("#{@goggles_result_dir}")
74
+ end
75
+
76
+ def make_result_dir
77
+ FileUtils.mkdir_p("#{@goggles_result_dir}/#{@gg_label}")
78
+ end
79
+
80
+ end
@@ -0,0 +1,38 @@
1
+ require 'goggles'
2
+ require 'optparse'
3
+
4
+ module Goggles
5
+ class CLI
6
+
7
+ def initialize(argv)
8
+ opt_parser = OptionParser.new do |opts|
9
+ opts.banner = "Goggles: Compare responsive screenshots in multiple browsers"
10
+ opts.separator ""
11
+ opts.separator "Options"
12
+
13
+ opts.on("-c", "--config CONFIG_FILE", "configuration to execute") do |config|
14
+ run_conf(config)
15
+ end
16
+
17
+ opts.on("-v", "--version", "Goggles::VERSION") do
18
+ puts Goggles::VERSION
19
+ end
20
+
21
+ opts.on("-h", "--help", "help text") do
22
+ puts opt_parser
23
+ end
24
+ end
25
+
26
+ opt_parser.parse!
27
+ end
28
+
29
+ def run_conf(config)
30
+ if File.exists? config
31
+ Goggles.swim config
32
+ else
33
+ puts "Not a valid configuration file: #{config}"
34
+ end
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,75 @@
1
+ require "image_size"
2
+
3
+ module Goggles
4
+
5
+ def diff_images
6
+ size_to_smallest!
7
+
8
+ images = Dir.glob("#{@goggles_result_dir}/*/*.png").sort
9
+
10
+ raise Goggles::EmptyResultError, "No screenshots found in results directory: #{@goggles_result_dir}" if images.empty?
11
+
12
+ until images.empty?
13
+ one = images.slice!(0)
14
+ two = images.slice!(0)
15
+
16
+ out_path = one.dup
17
+ discard = out_path.slice!(/[^_]*$/)
18
+
19
+ diff_out = "#{out_path}diff.png"
20
+ data_out = "#{out_path}data.txt"
21
+
22
+ `compare -fuzz #{@goggles_fuzz} -metric AE -highlight-color blue #{one} #{two} #{diff_out} 2>#{data_out}`
23
+ end
24
+ end
25
+
26
+ def size_to_smallest!
27
+ images = Dir.glob("#{@goggles_result_dir}/*/*.png").sort
28
+
29
+ until images.empty?
30
+ one = images.slice!(0)
31
+ two = images.slice!(0)
32
+
33
+ File.open(one, 'rb') do |file_one|
34
+ size_one = ImageSize.new(file_one.read).size
35
+ first_width = size_one[0]
36
+ first_height = size_one[1]
37
+
38
+ File.open(two, 'rb') do |file_two|
39
+ size_two = ImageSize.new(file_two.read).size
40
+ second_width = size_two[0]
41
+ second_height = size_two[1]
42
+
43
+ if first_width > second_width
44
+ width = second_width
45
+ w_file = one
46
+ else
47
+ width = first_width
48
+ w_file = two
49
+ end
50
+
51
+ if first_height > second_height
52
+ height = second_height
53
+ h_file = one
54
+ else
55
+ height = first_height
56
+ h_file = two
57
+ end
58
+
59
+ cut_to_width(w_file, width)
60
+ cut_to_height(h_file, height)
61
+
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ def cut_to_width(file, w)
68
+ `convert #{file} -background none -extent #{w}x0 #{file}`
69
+ end
70
+
71
+ def cut_to_height(file, h)
72
+ `convert #{file} -background none -extent 0x#{h} #{file}`
73
+ end
74
+
75
+ end
@@ -0,0 +1,3 @@
1
+ module Goggles
2
+ class EmptyResultError < StandardError; end
3
+ end
@@ -0,0 +1,10 @@
1
+ module Goggles
2
+
3
+ def execute_script(url, script)
4
+ @watir.goto url
5
+ @watir.cookies.clear
6
+ @watir.refresh
7
+
8
+ eval(File.read(script))
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ module Goggles
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,34 @@
1
+ require "spec_helper"
2
+
3
+ describe Goggles do
4
+ Given(:config_path) { "./spec/support/configs" }
5
+ Given(:images) { Dir.glob("./spec/support/results/*/*.png") }
6
+ Given(:diffs) { Dir.glob("./spec/support/results/*/*diff.png") }
7
+ Given(:datas) { Dir.glob("./spec/support/results/*/*data.txt") }
8
+
9
+ context "swimming with one script at multiple sizes" do
10
+ describe "taking screenshots" do
11
+ Given(:sizes_config) { "#{config_path}/test_config_1024_600.yml" }
12
+ When { Goggles.swim(sizes_config) }
13
+ Then { images.size.should == 6 }
14
+ And { diffs.size.should == 2 }
15
+ And { datas.size.should == 2 }
16
+ end
17
+
18
+ describe "generating error" do
19
+ Given(:no_shot_config) { "#{config_path}/test_config_no_screenshot.yml" }
20
+ Then { expect{ Goggles.swim(no_shot_config) }.to raise_error(Goggles::EmptyResultError) }
21
+ end
22
+ end
23
+
24
+ context "swimming against multiple paths with no scripts" do
25
+ describe "taking screenshots when commanded" do
26
+ Given(:scriptless_config) { "#{config_path}/test_config_scriptless.yml" }
27
+ When { Goggles.swim(scriptless_config) }
28
+ Then { images.size.should == 6 }
29
+ And { diffs.size.should == 2 }
30
+ And { datas.size.should == 2 }
31
+ end
32
+ end
33
+
34
+ end
@@ -0,0 +1,10 @@
1
+ require 'rspec'
2
+ require 'rspec-given'
3
+
4
+ require 'goggles'
5
+
6
+ RSpec.configure do |config|
7
+ config.order = 'default'
8
+ config.treat_symbols_as_metadata_keys_with_true_values = true
9
+ config.filter_run_excluding :skip => true
10
+ end
@@ -0,0 +1,20 @@
1
+ results_directory: "spec/support/results"
2
+ scripts_directory: "spec/support/scripts"
3
+
4
+ domain_under_test: "http://www.google.com"
5
+
6
+ paths_to_capture:
7
+ home: "/"
8
+
9
+ scripts_to_execute:
10
+ - "google_search.rb"
11
+
12
+ browsers:
13
+ - "firefox"
14
+ - "chrome"
15
+
16
+ browser_widths:
17
+ - 1024
18
+ - 600
19
+
20
+ image_fuzzing: "20%"
@@ -0,0 +1,19 @@
1
+ results_directory: "spec/support/results"
2
+ scripts_directory: "spec/support/scripts"
3
+
4
+ domain_under_test: "http://www.google.com"
5
+
6
+ paths_to_capture:
7
+ home: "/"
8
+
9
+ scripts_to_execute:
10
+ - "no_screenshot.rb"
11
+
12
+ browsers:
13
+ - "firefox"
14
+ - "chrome"
15
+
16
+ browser_widths:
17
+ - 320
18
+
19
+ image_fuzzing: "20%"
@@ -0,0 +1,19 @@
1
+ results_directory: "spec/support/results"
2
+ scripts_directory: "spec/support/scripts"
3
+
4
+ domain_under_test: "http://www.google.com"
5
+
6
+ paths_to_capture:
7
+ home: "/"
8
+ email: "/gmail"
9
+
10
+ scripts_to_execute:
11
+
12
+ browsers:
13
+ - "firefox"
14
+ - "chrome"
15
+
16
+ browser_widths:
17
+ - 1024
18
+
19
+ image_fuzzing: "20%"
@@ -0,0 +1,14 @@
1
+ require 'goggles'
2
+ require 'page-object'
3
+
4
+ class Search
5
+ include PageObject
6
+
7
+ text_field(:search_box, :id => "gbqfq")
8
+ end
9
+
10
+ Goggles.grab_screenshot("homepage")
11
+
12
+ page = Search.new(@watir)
13
+ page.search_box = "manta"
14
+ sleep 1
@@ -0,0 +1,12 @@
1
+ require 'goggles'
2
+ require 'page-object'
3
+
4
+ class Search
5
+ include PageObject
6
+
7
+ text_field(:search_box, :id => "gbqfq")
8
+ end
9
+
10
+ page = Search.new(@watir)
11
+ page.search_box = "manta"
12
+ sleep 1
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: goggles
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Johnson Denen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-01-20 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.5'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.5'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: watir-webdriver
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: image_size
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ description: comparing responsive screenshots under watir-webdriver
95
+ email:
96
+ - jdenen@manta.com
97
+ executables:
98
+ - swim
99
+ extensions: []
100
+ extra_rdoc_files: []
101
+ files:
102
+ - .gitignore
103
+ - Gemfile
104
+ - LICENSE.txt
105
+ - README.md
106
+ - Rakefile
107
+ - bin/swim
108
+ - goggles.gemspec
109
+ - lib/goggles.rb
110
+ - lib/goggles/cli.rb
111
+ - lib/goggles/comparison.rb
112
+ - lib/goggles/error.rb
113
+ - lib/goggles/scripter.rb
114
+ - lib/goggles/version.rb
115
+ - spec/goggles_spec.rb
116
+ - spec/spec_helper.rb
117
+ - spec/support/configs/test_config_1024_600.yml
118
+ - spec/support/configs/test_config_no_screenshot.yml
119
+ - spec/support/configs/test_config_scriptless.yml
120
+ - spec/support/scripts/google_search.rb
121
+ - spec/support/scripts/no_screenshot.rb
122
+ homepage: http://github.com/jdenen/goggles
123
+ licenses:
124
+ - MIT
125
+ post_install_message:
126
+ rdoc_options: []
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ none: false
131
+ requirements:
132
+ - - ! '>='
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ none: false
137
+ requirements:
138
+ - - ! '>='
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ requirements: []
142
+ rubyforge_project:
143
+ rubygems_version: 1.8.23
144
+ signing_key:
145
+ specification_version: 3
146
+ summary: comparing responsive screenshots under watir-webdriver
147
+ test_files:
148
+ - spec/goggles_spec.rb
149
+ - spec/spec_helper.rb
150
+ - spec/support/configs/test_config_1024_600.yml
151
+ - spec/support/configs/test_config_no_screenshot.yml
152
+ - spec/support/configs/test_config_scriptless.yml
153
+ - spec/support/scripts/google_search.rb
154
+ - spec/support/scripts/no_screenshot.rb