compatriot 0.0.3 → 0.0.5

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
+ SHA1:
3
+ metadata.gz: e100d57f0086c254d7fcd46b1d615dc3ec7570a1
4
+ data.tar.gz: 3a474ae56359208e474ea4742b3092797e9a13e4
5
+ SHA512:
6
+ metadata.gz: 1f712342dfd796f65140fadb0251325d2e44f91d698f3c58859b721b010f3ca9ddd538ff0c8eb8275860b928ddeb2bf046c46c6cca9f6c16cfc63cfce7e91c53
7
+ data.tar.gz: bed0d7c8323e728fa30cadbb044b9ebe8e92829978da39516f7396fa650c91e4cf2b06a402dd0a20f8d50437cb7702438d65a19d0888bb63822875e1097b18fa
data/.gitignore CHANGED
@@ -17,3 +17,4 @@ test/version_tmp
17
17
  tmp
18
18
  .DS_STORE
19
19
  spec/sample_app/chromedriver.log
20
+ TAGS
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ compatriot
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.7
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ before_script: "sh -e /etc/init.d/xvfb start && sudo add-apt-repository ppa:chromium-daily/dev && sudo aptitude update && sudo aptitude -y -q install unzip chromium-browser"
2
+ script: "bundle exec rake travis"
3
+ rvm:
4
+ - 2.1.7
data/README.md CHANGED
@@ -1,5 +1,6 @@
1
1
  Compatriot
2
2
  ==========
3
+ [![Build Status](https://secure.travis-ci.org/clnclarinet/compatriot.png?branch=master)](http://travis-ci.org/clnclarinet/compatriot)
3
4
 
4
5
  **Compat**ibility + **riot**! It's the **friend** that helps with browser compatibility!
5
6
  This Ruby gem makes cross-browser testing less painful.
@@ -11,6 +12,7 @@ Don't let your users find the inconsistencies and get to them first.
11
12
  What it does now
12
13
  ----------------
13
14
 
15
+ * [Documentation on RelishApp](https://www.relishapp.com/clnclarinet/compatriot)
14
16
  * In firefox and chrome, visits a list of paths to a Rack app and takes a screenshot on each page.
15
17
  * Stores the screenshot in `tmp/results/_timestamp_/_browser_/`
16
18
  * Creates `tmp/results/_timestamp_/index.html` that shows thumbnails of each screenshot plus a diff of the two in a table for easy comparison.
@@ -19,11 +21,9 @@ What it does now
19
21
  What it will do in the future
20
22
  -----------------------------
21
23
 
22
- * Have documentation
24
+ * Have more documentation (a start is [on RelishApp](https://www.relishapp.com/clnclarinet/compatriot)!)
23
25
  * Have a screenshot of sample results in the README
24
- * Have unit tests and better tests
25
- * Be on travis-ci
26
- * Not have a diff that's a different size than the originals
26
+ * Have more and better tests
27
27
  * Find the largest, darkest contiguous region in the image diff and have a threshold of pass/fail based on that
28
28
  * Perform better on the image processing (by sampling/resizing, using oily_png, etc)
29
29
  * Given a list of URLs/paths to visit, will take a screenshot of each and display which URL it came from in the index
@@ -31,6 +31,7 @@ What it will do in the future
31
31
  * Automatically compare the screenshots across browsers and flags those that are more than some configurable threshold different
32
32
  * Allow configuration of which browsers to use
33
33
  * Connect to virtual machines so that you don't have to have all the browsers on the machine you're running the tests on
34
+ * Steal some of VCR's relish rake tasks
34
35
 
35
36
 
36
37
  How To Use
@@ -38,17 +39,19 @@ How To Use
38
39
 
39
40
  **Requirements**
40
41
 
41
- * Ruby v1.9.2
42
+ * Ruby v2.1.7
42
43
  * [Firefox](http://getfirefox.net)
43
44
  * [chromedriver](http://code.google.com/p/selenium/wiki/ChromeDriver)
44
45
 
46
+ There are setup examples in the examples directory and [documentation on RelishApp](https://www.relishapp.com/clnclarinet/compatriot)
47
+
45
48
  When you run a file similar to the examples it will save results in `_current-directory_/tmp/results/_timestamp_/_browser_`
46
49
 
47
50
 
48
51
  What to do to run its tests
49
52
  ---------------------------
50
53
 
51
- Using at least ruby 1.9.2:
54
+ Using at least ruby 2.1.7:
52
55
 
53
56
  bundle install
54
57
  bundle exec rake test
@@ -83,14 +86,17 @@ Many thanks to the wonderful libraries that make this gem possible:
83
86
  * [capybara](https://github.com/jnicklas/capybara)
84
87
  * [selenium-webdriver](http://seleniumhq.org/docs/01_introducing_selenium.html#selenium-2-aka-selenium-webdriver)
85
88
  * [chunky_png](https://github.com/wvanbergen/chunky_png) (and especially [this blog post about using chunky_png to create image diffs](http://jeffkreeftmeijer.com/2011/comparing-images-and-creating-image-diffs/?utm_source=rubyweekly&utm_medium=email) by Jeff Kreeftmeijer)
89
+ * [travis](http://travis-ci.org/) for CI
90
+ * [relishapp](https://www.relishapp.com/) for documentation
91
+ * [vcr](https://github.com/myronmarston/vcr) for having such awesome documentation that it inspired me to use Relishapp
86
92
 
87
93
 
88
94
  Contributors
89
95
  ------------
90
96
  * Carol Nichols ([twitter](http://twitter.com/carols10cents), [website](http://carol-nichols.com))
91
97
  * Andrew Cox ([twitter](https://twitter.com/coxandrew), [website](http://andrewcox.org/))
92
- * Kurtis Rainbolt-Greene ([twitter](https://twitter.com/krainboltgreene)) ([website](http://kurtisrainboltgreene.name/))
93
- * Steve Klabnik ([twitter](https://twitter.com/steveklabnik)) ([website](http://www.steveklabnik.com/))
98
+ * Kurtis Rainbolt-Greene ([twitter](https://twitter.com/krainboltgreene), [website](http://kurtisrainboltgreene.name/))
99
+ * Steve Klabnik ([twitter](https://twitter.com/steveklabnik), [website](http://www.steveklabnik.com/))
94
100
  * You???
95
101
 
96
102
 
data/Rakefile CHANGED
@@ -13,4 +13,14 @@ end
13
13
  desc "Run tests"
14
14
  task :spec do
15
15
  task("test").execute
16
- end
16
+ end
17
+
18
+ task :travis do
19
+ puts "Grabbing chromedriver..."
20
+ mkdir_p "/tmp/bin"
21
+ system "cd /tmp/bin && wget http://chromium.googlecode.com/files/chromedriver_linux32_16.0.902.0.zip && unzip chromedriver_linux32_16.0.902.0.zip"
22
+
23
+ puts "Starting to run tests..."
24
+ system("export PATH=/tmp/bin:$PATH && export DISPLAY=:99.0 && bundle exec rake test && bundle exec cucumber features")
25
+ raise "`rake test` failed!" unless $?.exitstatus == 0
26
+ end
data/compatriot.gemspec CHANGED
@@ -18,13 +18,17 @@ Gem::Specification.new do |gem|
18
18
  gem.name = "compatriot"
19
19
  gem.require_paths = ["lib"]
20
20
  gem.version = Compatriot::VERSION
21
+ gem.licenses = "MIT"
21
22
 
22
- # To get the newest minitest features!
23
- gem.add_development_dependency 'minitest', '~> 2.8.1'
24
- gem.add_development_dependency 'sinatra', '~> 1.3.1'
25
- gem.add_development_dependency 'mocha', '~> 0.10.0'
23
+ gem.add_development_dependency 'minitest', '~> 5.8', '>= 5.8.2'
24
+ gem.add_development_dependency 'sinatra', '~> 1.4', '>= 1.4.6'
25
+ gem.add_development_dependency 'mocha', '~> 1.1', '>= 1.1.0'
26
+ gem.add_development_dependency 'cucumber', '~> 2.1', '>= 2.1.0'
27
+ gem.add_development_dependency 'aruba', '~> 0.10.0'
28
+ gem.add_development_dependency 'relish', '~> 0.7.1'
26
29
 
27
- gem.add_dependency 'capybara', '~> 1.1.2'
28
- gem.add_dependency 'rake', '~> 0.9.2.2'
29
- gem.add_dependency 'chunky_png', '~> 1.2.5'
30
+ gem.add_runtime_dependency 'capybara', '~> 2.5', '>= 2.5.0'
31
+ gem.add_runtime_dependency 'selenium-webdriver', '~> 2.48', '>= 2.48.1'
32
+ gem.add_runtime_dependency 'rake', '~> 10.4', '>= 10.4.2'
33
+ gem.add_runtime_dependency 'chunky_png', '~> 1.3', '>= 1.3.5'
30
34
  end
@@ -1,7 +1,7 @@
1
1
  require 'compatriot'
2
- require_relative "../spec/sample_app/test_app"
2
+ require_relative "../spec/sample_app/sample_app"
3
3
 
4
- Compatriot.app = TestApp
4
+ Compatriot.app = SampleApp
5
5
 
6
6
  Compatriot.run(%w[
7
7
  /
@@ -0,0 +1,31 @@
1
+ Feature: list_of_urls
2
+
3
+ Passing a list of URLs to Compatriot.run will visit each of them in each browser and take a screenshot.
4
+
5
+ Scenario: List of URLs
6
+ Given a Sinatra app named "simple_app.rb" with:
7
+ """ruby
8
+ get '/' do
9
+ 'Hello'
10
+ end
11
+
12
+ get '/goodbye' do
13
+ 'Goodbye'
14
+ end
15
+ """
16
+ And a file named "compatriot_urls.rb" with:
17
+ """ruby
18
+ $:.unshift(File.expand_path('../../lib', File.dirname(__FILE__)))
19
+ require 'compatriot'
20
+ require_relative 'simple_app'
21
+ Compatriot.app = SimpleApp
22
+ Compatriot.run(%w[
23
+ /
24
+ /goodbye
25
+ ])
26
+
27
+ """
28
+ And the directory "tmp/results" does not exist
29
+ When I run `ruby compatriot_urls.rb`
30
+ Then "tmp/results" should have 1 subdirectory
31
+ And there should be results for 2 screenshots
@@ -0,0 +1,60 @@
1
+ module CompatriotHelpers
2
+ def create_sinatra_app(file_name, content)
3
+ app_class = camelize(file_name.gsub(/\.rb$/, ''))
4
+ beginning_sinatra_app = <<-HERE
5
+ require 'sinatra/base'
6
+ require 'rack'
7
+ require 'yaml'
8
+
9
+ class #{app_class} < Sinatra::Base
10
+ set :root, File.dirname(__FILE__)
11
+ set :static, true
12
+ HERE
13
+
14
+ ending_sinatra_app = <<-HERE
15
+
16
+ end
17
+
18
+ if __FILE__ == $0
19
+ Rack::Handler::WEBrick.run #{app_class}, :Port => 8070
20
+ end
21
+ HERE
22
+ beginning_sinatra_app + content + ending_sinatra_app
23
+ end
24
+
25
+ def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
26
+ if first_letter_in_uppercase
27
+ lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
28
+ else
29
+ lower_case_and_underscored_word.to_s[0].chr.downcase + camelize(lower_case_and_underscored_word)[1..-1]
30
+ end
31
+ end
32
+ end
33
+ World(CompatriotHelpers)
34
+
35
+ Given /^a Sinatra app named "([^"]*)" with:$/ do |file_name, content|
36
+ write_file(file_name, create_sinatra_app(file_name, content))
37
+ end
38
+
39
+ Then /^"([^"]*)" should have (\d+) subdir/ do |directory, subdirectory_count|
40
+ cd('.') {
41
+ expect((Dir.entries(directory) - [".", ".."]).size).to eql subdirectory_count.to_i
42
+ }
43
+ end
44
+
45
+ Then /^there should be results for (\d+) screenshots?$/ do |screenshot_count|
46
+ screenshot_count = screenshot_count.to_i
47
+ results_tmp_dir = "tmp/results"
48
+ cd('.') {
49
+ current_results_dir = (Dir.entries(results_tmp_dir) - [".", ".."]).first
50
+ current_results_dir = File.join(results_tmp_dir, current_results_dir)
51
+
52
+ firefox_dir = File.join(current_results_dir, "firefox")
53
+ chrome_dir = File.join(current_results_dir, "chrome")
54
+ diffs_dir = File.join(current_results_dir, "diffs")
55
+
56
+ expect(Dir.glob(File.join(firefox_dir, "*.png")).size).to eql 2
57
+ expect(Dir.glob(File.join(chrome_dir, "*.png")).size).to eql 2
58
+ expect(Dir.glob(File.join(diffs_dir, "*.png")).size).to eql 2
59
+ }
60
+ end
@@ -0,0 +1,5 @@
1
+ require 'aruba/cucumber'
2
+
3
+ Aruba.configure do |config|
4
+ config.exit_timeout = 60
5
+ end
data/lib/compatriot.rb CHANGED
@@ -2,8 +2,7 @@ require "compatriot/version"
2
2
  require "compatriot/runner"
3
3
  require "compatriot/browser"
4
4
  require "compatriot/results_presenter"
5
- require "compatriot/results"
6
- require "compatriot/image_differ"
5
+ require "compatriot/image_differ/image_differ"
7
6
 
8
7
  module Compatriot
9
8
  class << self
@@ -0,0 +1,70 @@
1
+ require 'chunky_png'
2
+ include ChunkyPNG::Color
3
+
4
+ module Compatriot
5
+ class ColorDiffer
6
+
7
+ def self.diff(filename1, filename2, results_directory)
8
+ image1 = ChunkyPNG::Image.from_file(filename1)
9
+ image2 = ChunkyPNG::Image.from_file(filename2)
10
+ @results_directory = results_directory
11
+
12
+ output = ChunkyPNG::Image.new(image1.width, image1.height, WHITE)
13
+ diff = []
14
+
15
+ each_pixel(image1) do |x, y|
16
+ pixel1 = image1[x,y]
17
+ pixel2 = image2[x,y]
18
+ unless pixel1 == pixel2
19
+ output[x,y], score = color_difference_of_pixels(pixel1, pixel2)
20
+ diff << score
21
+ end
22
+ end
23
+
24
+ save_diff_image(output, filename1, filename2)
25
+ end
26
+
27
+ def self.save_diff_image(output, filename1, filename2)
28
+ filename = diff_name(filename1, filename2)
29
+ path = File.join(
30
+ @results_directory,
31
+ "diffs",
32
+ filename
33
+ )
34
+ output.save(path)
35
+ File.join("diffs", filename)
36
+ end
37
+
38
+ def self.diff_name(image1, image2)
39
+ browser1 = File.basename(File.dirname(image1))
40
+ browser2 = File.basename(File.dirname(image2))
41
+
42
+ "color_#{browser1}_vs_#{browser2}_#{File.basename(image1)}"
43
+ end
44
+
45
+ def self.color_difference_of_pixels(pixel1, pixel2)
46
+ score = Math.sqrt(
47
+ (r(pixel2) - r(pixel1)) ** 2 +
48
+ (g(pixel2) - g(pixel1)) ** 2 +
49
+ (b(pixel2) - b(pixel1)) ** 2
50
+ ) / Math.sqrt(MAX ** 2 * 3)
51
+
52
+ [grayscale(MAX - (score * MAX).round), score]
53
+ end
54
+
55
+ # Not called anywhere
56
+ def color_difference_total_score
57
+ pixels_total = image1.width * image1.height
58
+ pixels_changed = diff.length
59
+ pixels_changed_percentage = (diff.inject {|sum, value| sum + value} / pixels_total) * 100
60
+ end
61
+
62
+ def self.each_pixel(image)
63
+ image.width.times do |x|
64
+ image.height.times do |y|
65
+ yield(x, y)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,41 @@
1
+ require_relative 'color_differ'
2
+
3
+ module Compatriot
4
+ class ImageDiffer
5
+
6
+ def initialize(params = {})
7
+ @paths = params[:paths]
8
+ @browsers = params[:browsers]
9
+ @strategy = params[:strategy] || Compatriot::ColorDiffer
10
+ @results_directory = params[:results_directory]
11
+ @diffs = {}
12
+
13
+ create_diffs_path
14
+ end
15
+
16
+ def diff_for(path)
17
+ @diffs[path]
18
+ end
19
+
20
+ def compute!
21
+ @paths.map do |path|
22
+ images_to_diff = @browsers.map { |b| b.absolute_screenshot_for(path) }
23
+ @diffs[path] = diff(images_to_diff)
24
+ end
25
+ end
26
+
27
+ def diff(results)
28
+ @strategy.diff(results.first, results.last, @results_directory)
29
+ end
30
+
31
+ def diffs_path
32
+ File.join(@results_directory, "diffs")
33
+ end
34
+
35
+ private
36
+
37
+ def create_diffs_path
38
+ FileUtils.mkdir_p(diffs_path)
39
+ end
40
+ end
41
+ end
@@ -48,7 +48,7 @@ module Compatriot
48
48
  </td>
49
49
  <% end %>
50
50
  <td>
51
- <img src="<%= differ.diffs[path] %>" />
51
+ <img src="<%= differ.diff_for(path) %>" />
52
52
  </td>
53
53
  </tr>
54
54
  <% end %>
@@ -1,4 +1,5 @@
1
- require "fileutils"
1
+ require 'fileutils'
2
+ require 'date'
2
3
 
3
4
  module Compatriot
4
5
  class Runner
@@ -42,7 +43,8 @@ module Compatriot
42
43
  @differ = Compatriot::ImageDiffer.new(
43
44
  :paths => @paths,
44
45
  :browsers => @browsers,
45
- :strategy => :color_difference
46
+ :strategy => Compatriot::ColorDiffer,
47
+ :results_directory => @results_directory
46
48
  )
47
49
  @differ.compute!
48
50
  end
@@ -60,4 +62,4 @@ module Compatriot
60
62
  FileUtils.mkdir_p(@results_directory)
61
63
  end
62
64
  end
63
- end
65
+ end
@@ -1,3 +1,3 @@
1
1
  module Compatriot
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -2,12 +2,12 @@ require 'sinatra/base'
2
2
  require 'rack'
3
3
  require 'yaml'
4
4
 
5
- class TestApp < Sinatra::Base
5
+ class SampleApp < Sinatra::Base
6
6
  set :root, File.dirname(__FILE__)
7
7
  set :static, true
8
8
 
9
9
  get '/' do
10
- "<h1>Hello world!</h1"
10
+ "<h1>Hello world!</h1><div><a href='chrome-css-bug'>Try another page</a></div>"
11
11
  end
12
12
 
13
13
  get "/chrome-css-bug" do
@@ -17,5 +17,5 @@ class TestApp < Sinatra::Base
17
17
  end
18
18
 
19
19
  if __FILE__ == $0
20
- Rack::Handler::WEBrick.run TestApp, :Port => 8070
20
+ Rack::Handler::WEBrick.run SampleApp, :Port => 8070
21
21
  end
data/spec/spec_helper.rb CHANGED
@@ -1,13 +1,11 @@
1
1
  $:.unshift(File.expand_path('../lib', File.dirname(__FILE__)))
2
2
 
3
- require 'rubygems'
4
- gem 'minitest' # ensures you're using the gem, and not the built in MT
5
- require 'minitest/autorun'
6
- require 'mocha'
3
+ require "minitest/autorun"
4
+ require "mocha/mini_test"
7
5
 
8
6
  require 'compatriot'
9
7
 
10
- require_relative "sample_app/test_app"
8
+ require_relative "sample_app/sample_app"
11
9
 
12
10
  # A custom runner to enable before_suite and after_suite setup/teardown.
13
11
  # http://bfts.rubyforge.org/minitest/index.html
@@ -15,37 +13,13 @@ require_relative "sample_app/test_app"
15
13
  # before running the suite; it's useful to be able to look at the screenshots
16
14
  # after a test run so we're not deleting them then.
17
15
 
18
- module MiniTestWithHooks
19
- class Unit < MiniTest::Unit
20
- def before_suites
21
- end
22
-
23
- def after_suites
24
- end
25
-
26
- def _run_suites(suites, type)
27
- begin
28
- before_suites
29
- super(suites, type)
30
- ensure
31
- after_suites
32
- end
33
- end
16
+ module TestRunner
17
+ def before_setup
18
+ super
19
+ FileUtils.remove_dir(File.join("sample_app", "tmp", "results"), true)
34
20
  end
35
21
  end
36
22
 
37
- module MiniTestRemoveScreenshots
38
- class Unit < MiniTestWithHooks::Unit
39
-
40
- def before_suites
41
- super
42
- FileUtils.remove_dir(File.join("sample_app", "tmp", "results"), true)
43
- end
44
-
45
- def after_suites
46
- super
47
- end
48
- end
23
+ class Minitest::Spec
24
+ include TestRunner
49
25
  end
50
-
51
- MiniTest::Unit.runner = MiniTestRemoveScreenshots::Unit.new
@@ -86,6 +86,8 @@ describe Compatriot::Browser do
86
86
  :name => "foo",
87
87
  :screenshot_directory => "bar"
88
88
  )
89
+ app = stub
90
+ @b.initialize_capybara(app)
89
91
  end
90
92
 
91
93
  it "visits the path" do
@@ -132,6 +134,8 @@ describe Compatriot::Browser do
132
134
  :name => "foo",
133
135
  :screenshot_directory => "bar"
134
136
  )
137
+ app = stub
138
+ @b.initialize_capybara(app)
135
139
  end
136
140
 
137
141
  it "returns nil if there is no screenshot for that path" do
@@ -0,0 +1,39 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe Compatriot::ColorDiffer do
4
+ it "calls chunky_png on each image path" do
5
+ image1 = stub_everything("1", :width => 1, :height => 2)
6
+ image2 = stub_everything("2", :width => 3, :height => 4)
7
+
8
+ ChunkyPNG::Image.expects(:from_file).with("file_one").returns(image1)
9
+ ChunkyPNG::Image.expects(:from_file).with("file_two").returns(image2)
10
+ Compatriot::ColorDiffer.stubs(:save_diff_image)
11
+
12
+ Compatriot::ColorDiffer.diff("file_one", "file_two", "some/dir")
13
+ end
14
+
15
+ it "starts a new white image with the same dimensions" do
16
+ diff_image = stub_everything
17
+ ChunkyPNG::Image.expects(:new).with(
18
+ 1,
19
+ 2,
20
+ ChunkyPNG::Image::WHITE
21
+ ).returns(diff_image)
22
+
23
+ image1 = stub_everything("1", :width => 1, :height => 2)
24
+ image2 = stub_everything("2", :width => 3, :height => 4)
25
+
26
+ ChunkyPNG::Image.stubs(:from_file).returns(image1, image2)
27
+ Compatriot::ColorDiffer.stubs(:save_diff_image)
28
+
29
+ differ = Compatriot::ColorDiffer.diff(image1, image2, "some/dir")
30
+ end
31
+
32
+ it "names the image based on the strategy and the browsers" do
33
+ name = Compatriot::ColorDiffer.diff_name(
34
+ "/something/firefox/1.png",
35
+ "/something/chrome/1.png"
36
+ )
37
+ name.must_equal("color_firefox_vs_chrome_1.png")
38
+ end
39
+ end
@@ -0,0 +1,67 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe Compatriot::ImageDiffer do
4
+ describe "compute!" do
5
+ it "diffs each set of images and stores the location by path" do
6
+ Compatriot::ImageDiffer.any_instance.stubs(:create_diffs_path)
7
+
8
+ d = Compatriot::ImageDiffer.new(
9
+ :paths => ["/home"],
10
+ :browsers => [
11
+ stub(:absolute_screenshot_for => "1.png"),
12
+ stub(:absolute_screenshot_for => "2.png")
13
+ ]
14
+ )
15
+ d.expects(:diff).with(["1.png", "2.png"]).returns("diff.png")
16
+ d.compute!
17
+ d.diff_for("/home").must_equal("diff.png")
18
+ end
19
+
20
+ it "creates a diffs dir" do
21
+ FileUtils.expects(:mkdir_p).with("foo/bar/diffs")
22
+ d = Compatriot::ImageDiffer.new(
23
+ :results_directory => "foo/bar"
24
+ )
25
+ d.diffs_path.must_equal("foo/bar/diffs")
26
+ end
27
+ end
28
+
29
+ describe "diff" do
30
+ it "returns the filename of the diff" do
31
+ file_one = stub
32
+ file_two = stub
33
+ strategy = stub(:diff => "diff_filename.png")
34
+ ChunkyPNG::Image.stubs(:from_file)
35
+ FileUtils.expects(:mkdir_p).with("something/diffs")
36
+
37
+ c = Compatriot::ImageDiffer.new(
38
+ :strategy => strategy,
39
+ :results_directory => "something"
40
+ )
41
+
42
+ c.diff([file_one, file_two]).must_equal("diff_filename.png")
43
+ end
44
+
45
+ it "uses the strategy passed in" do
46
+ file_one = stub
47
+ file_two = stub
48
+ strategy = stub
49
+
50
+ ChunkyPNG::Image.stubs(:from_file).returns(file_one, file_two)
51
+ FileUtils.expects(:mkdir_p).with("something/diffs")
52
+
53
+ strategy.expects(:diff).with(
54
+ file_one,
55
+ file_two,
56
+ "something"
57
+ ).returns("diff_filename.png")
58
+
59
+ c = Compatriot::ImageDiffer.new(
60
+ :strategy => strategy,
61
+ :results_directory => "something"
62
+ )
63
+
64
+ c.diff([file_one, file_two]).must_equal("diff_filename.png")
65
+ end
66
+ end
67
+ end
@@ -55,7 +55,7 @@ describe Compatriot::Runner do
55
55
 
56
56
  describe "#results_directory" do
57
57
  it "names a results directory in tmp/results based on the clock" do
58
- runner = Compatriot::Runner.new(TestApp, ["/"], @fixed_clock)
58
+ runner = Compatriot::Runner.new(SampleApp, ["/"], @fixed_clock)
59
59
 
60
60
  runner.results_directory.must_equal(@results_dir_name)
61
61
  end
metadata CHANGED
@@ -1,82 +1,203 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: compatriot
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
5
- prerelease:
4
+ version: 0.0.5
6
5
  platform: ruby
7
6
  authors:
8
7
  - Carol Nichols
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2011-12-21 00:00:00.000000000Z
11
+ date: 2015-11-04 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: minitest
16
- requirement: &2157131520 !ruby/object:Gem::Requirement
17
- none: false
15
+ requirement: !ruby/object:Gem::Requirement
18
16
  requirements:
19
- - - ~>
17
+ - - "~>"
20
18
  - !ruby/object:Gem::Version
21
- version: 2.8.1
19
+ version: '5.8'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 5.8.2
22
23
  type: :development
23
24
  prerelease: false
24
- version_requirements: *2157131520
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '5.8'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 5.8.2
25
33
  - !ruby/object:Gem::Dependency
26
34
  name: sinatra
27
- requirement: &2157131020 !ruby/object:Gem::Requirement
28
- none: false
35
+ requirement: !ruby/object:Gem::Requirement
29
36
  requirements:
30
- - - ~>
37
+ - - "~>"
31
38
  - !ruby/object:Gem::Version
32
- version: 1.3.1
39
+ version: '1.4'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 1.4.6
33
43
  type: :development
34
44
  prerelease: false
35
- version_requirements: *2157131020
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '1.4'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 1.4.6
36
53
  - !ruby/object:Gem::Dependency
37
54
  name: mocha
38
- requirement: &2157130560 !ruby/object:Gem::Requirement
39
- none: false
55
+ requirement: !ruby/object:Gem::Requirement
40
56
  requirements:
41
- - - ~>
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '1.1'
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 1.1.0
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '1.1'
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 1.1.0
73
+ - !ruby/object:Gem::Dependency
74
+ name: cucumber
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: '2.1'
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 2.1.0
83
+ type: :development
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.1'
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 2.1.0
93
+ - !ruby/object:Gem::Dependency
94
+ name: aruba
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
42
98
  - !ruby/object:Gem::Version
43
99
  version: 0.10.0
44
100
  type: :development
45
101
  prerelease: false
46
- version_requirements: *2157130560
102
+ version_requirements: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - "~>"
105
+ - !ruby/object:Gem::Version
106
+ version: 0.10.0
107
+ - !ruby/object:Gem::Dependency
108
+ name: relish
109
+ requirement: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - "~>"
112
+ - !ruby/object:Gem::Version
113
+ version: 0.7.1
114
+ type: :development
115
+ prerelease: false
116
+ version_requirements: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - "~>"
119
+ - !ruby/object:Gem::Version
120
+ version: 0.7.1
47
121
  - !ruby/object:Gem::Dependency
48
122
  name: capybara
49
- requirement: &2157130100 !ruby/object:Gem::Requirement
50
- none: false
123
+ requirement: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - "~>"
126
+ - !ruby/object:Gem::Version
127
+ version: '2.5'
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: 2.5.0
131
+ type: :runtime
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '2.5'
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: 2.5.0
141
+ - !ruby/object:Gem::Dependency
142
+ name: selenium-webdriver
143
+ requirement: !ruby/object:Gem::Requirement
51
144
  requirements:
52
- - - ~>
145
+ - - "~>"
53
146
  - !ruby/object:Gem::Version
54
- version: 1.1.2
147
+ version: '2.48'
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: 2.48.1
55
151
  type: :runtime
56
152
  prerelease: false
57
- version_requirements: *2157130100
153
+ version_requirements: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - "~>"
156
+ - !ruby/object:Gem::Version
157
+ version: '2.48'
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: 2.48.1
58
161
  - !ruby/object:Gem::Dependency
59
162
  name: rake
60
- requirement: &2157129640 !ruby/object:Gem::Requirement
61
- none: false
163
+ requirement: !ruby/object:Gem::Requirement
62
164
  requirements:
63
- - - ~>
165
+ - - "~>"
166
+ - !ruby/object:Gem::Version
167
+ version: '10.4'
168
+ - - ">="
64
169
  - !ruby/object:Gem::Version
65
- version: 0.9.2.2
170
+ version: 10.4.2
66
171
  type: :runtime
67
172
  prerelease: false
68
- version_requirements: *2157129640
173
+ version_requirements: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - "~>"
176
+ - !ruby/object:Gem::Version
177
+ version: '10.4'
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: 10.4.2
69
181
  - !ruby/object:Gem::Dependency
70
182
  name: chunky_png
71
- requirement: &2157129180 !ruby/object:Gem::Requirement
72
- none: false
183
+ requirement: !ruby/object:Gem::Requirement
73
184
  requirements:
74
- - - ~>
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: '1.3'
188
+ - - ">="
75
189
  - !ruby/object:Gem::Version
76
- version: 1.2.5
190
+ version: 1.3.5
77
191
  type: :runtime
78
192
  prerelease: false
79
- version_requirements: *2157129180
193
+ version_requirements: !ruby/object:Gem::Requirement
194
+ requirements:
195
+ - - "~>"
196
+ - !ruby/object:Gem::Version
197
+ version: '1.3'
198
+ - - ">="
199
+ - !ruby/object:Gem::Version
200
+ version: 1.3.5
80
201
  description: Finds likely UI browser cross-compatibility issues.
81
202
  email:
82
203
  - carol.nichols@gmail.com
@@ -84,57 +205,66 @@ executables: []
84
205
  extensions: []
85
206
  extra_rdoc_files: []
86
207
  files:
87
- - .gitignore
88
- - .rvmrc
208
+ - ".gitignore"
209
+ - ".ruby-gemset"
210
+ - ".ruby-version"
211
+ - ".travis.yml"
89
212
  - CHANGELOG.md
90
213
  - Gemfile
91
214
  - README.md
92
215
  - Rakefile
93
216
  - compatriot.gemspec
94
217
  - examples/compatriot_tests.rb
218
+ - features/list_of_urls.feature
219
+ - features/step_definitions/cli_steps.rb
220
+ - features/support/env.rb
95
221
  - lib/compatriot.rb
96
222
  - lib/compatriot/browser.rb
97
- - lib/compatriot/image_differ.rb
223
+ - lib/compatriot/image_differ/color_differ.rb
224
+ - lib/compatriot/image_differ/image_differ.rb
98
225
  - lib/compatriot/results_presenter.rb
99
226
  - lib/compatriot/runner.rb
100
227
  - lib/compatriot/version.rb
101
- - spec/integration/full_stack_spec.rb
102
228
  - spec/sample_app/public/images/smileyface.jpg
103
- - spec/sample_app/test_app.rb
229
+ - spec/sample_app/sample_app.rb
104
230
  - spec/spec_helper.rb
105
231
  - spec/unit/browser_spec.rb
106
- - spec/unit/image_differ_spec.rb
232
+ - spec/unit/image_differ/color_differ_spec.rb
233
+ - spec/unit/image_differ/image_differ_spec.rb
107
234
  - spec/unit/runner_spec.rb
108
235
  homepage: https://github.com/clnclarinet/compatriot
109
- licenses: []
236
+ licenses:
237
+ - MIT
238
+ metadata: {}
110
239
  post_install_message:
111
240
  rdoc_options: []
112
241
  require_paths:
113
242
  - lib
114
243
  required_ruby_version: !ruby/object:Gem::Requirement
115
- none: false
116
244
  requirements:
117
- - - ! '>='
245
+ - - ">="
118
246
  - !ruby/object:Gem::Version
119
247
  version: '0'
120
248
  required_rubygems_version: !ruby/object:Gem::Requirement
121
- none: false
122
249
  requirements:
123
- - - ! '>='
250
+ - - ">="
124
251
  - !ruby/object:Gem::Version
125
252
  version: '0'
126
253
  requirements: []
127
254
  rubyforge_project:
128
- rubygems_version: 1.8.8
255
+ rubygems_version: 2.4.8
129
256
  signing_key:
130
- specification_version: 3
257
+ specification_version: 4
131
258
  summary: Runs a command in multiple browsers using selenium then compares the screenshots
132
259
  and presents those likely to have cross-browser incompatibilities.
133
260
  test_files:
134
- - spec/integration/full_stack_spec.rb
261
+ - features/list_of_urls.feature
262
+ - features/step_definitions/cli_steps.rb
263
+ - features/support/env.rb
135
264
  - spec/sample_app/public/images/smileyface.jpg
136
- - spec/sample_app/test_app.rb
265
+ - spec/sample_app/sample_app.rb
137
266
  - spec/spec_helper.rb
138
267
  - spec/unit/browser_spec.rb
139
- - spec/unit/image_differ_spec.rb
268
+ - spec/unit/image_differ/color_differ_spec.rb
269
+ - spec/unit/image_differ/image_differ_spec.rb
140
270
  - spec/unit/runner_spec.rb
data/.rvmrc DELETED
@@ -1,47 +0,0 @@
1
- #!/usr/bin/env bash
2
-
3
- # This is an RVM Project .rvmrc file, used to automatically load the ruby
4
- # development environment upon cd'ing into the directory
5
-
6
- # First we specify our desired <ruby>[@<gemset>], the @gemset name is optional.
7
- environment_id="ruby-1.9.2@compatriot"
8
-
9
- #
10
- # First we attempt to load the desired environment directly from the environment
11
- # file, this is very fast and efficicent compared to running through the entire
12
- # CLI and selector. If you want feedback on which environment was used then
13
- # insert the word 'use' after --create as this triggers verbose mode.
14
- #
15
- if [[ -d "${rvm_path:-$HOME/.rvm}/environments" \
16
- && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]] ; then
17
- \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
18
- else
19
- # If the environment file has not yet been created, use the RVM CLI to select.
20
- rvm --create "$environment_id"
21
- fi
22
-
23
- #
24
- # If you use an RVM gemset file to install a list of gems (*.gems), you can have
25
- # it be automatically loaded, uncomment the following and adjust the filename if
26
- # necessary.
27
- #
28
- # filename=".gems"
29
- # if [[ -s "$filename" ]] ; then
30
- # rvm gemset import "$filename" | grep -v already | grep -v listed | grep -v complete | sed '/^$/d'
31
- # fi
32
-
33
- #
34
- # If you use bundler and would like to run bundle each time you enter the
35
- # directory you can uncomment the following code.
36
- #
37
- # # Ensure that Bundler is installed, install it if it is not.
38
- # if ! command -v bundle ; then
39
- # printf "The rubygem 'bundler' is not installed, installing it now.\n"
40
- # gem install bundler
41
- # fi
42
- #
43
- # # Bundle while redcing excess noise.
44
- # printf "Bundling your gems this may take a few minutes on a fresh clone.\n"
45
- # bundle | grep -v 'Using' | grep -v 'complete' | sed '/^$/d'
46
- #
47
-
@@ -1,100 +0,0 @@
1
- require 'chunky_png'
2
- include ChunkyPNG::Color
3
-
4
- module Compatriot
5
- class ImageDiffer
6
-
7
- attr_reader :diffs
8
-
9
- def initialize(params = {})
10
- @paths = params[:paths]
11
- @browsers = params[:browsers]
12
- @strategy = params[:strategy] || :color_difference
13
- @diffs = {}
14
- end
15
-
16
- def compute!
17
- @paths.map do |path|
18
- images_to_diff = @browsers.map { |b| b.absolute_screenshot_for(path) }
19
- @diffs[path] = diff(images_to_diff)
20
- end
21
- end
22
-
23
- def diff(results)
24
- images = results.map{|r| ChunkyPNG::Image.from_file(r) }
25
- self.send(@strategy, images.first, images.last, results.first)
26
- end
27
-
28
- def same_pixels_exactly(image1, image2, name)
29
- output = ChunkyPNG::Image.new(image1.width, image2.height, WHITE)
30
- diff = []
31
-
32
- each_pixel(image1) do |x, y|
33
- pixel1 = image1[x, y]
34
- pixel2 = image2[x, y]
35
- output[x,y] = pixel1
36
- diff << [x,y] unless pixel1 == pixel2
37
- end
38
-
39
- pixels_total = image1.pixels.length
40
- pixels_changed = diff.length
41
- pixels_changed_percentage = (diff.length.to_f / image1.pixels.length) * 100
42
-
43
- x, y = diff.map{ |xy| xy[0] }, diff.map{ |xy| xy[1] }
44
-
45
- output.rect(x.min, y.min, x.max, y.max, ChunkyPNG::Color.rgb(0,255,0))
46
- filename = "#{name}-same_exactly.png"
47
- output.save(filename)
48
- File.join(
49
- File.basename(File.dirname(filename)),
50
- File.basename(filename)
51
- )
52
- end
53
-
54
- def color_difference(image1, image2, name)
55
- output = ChunkyPNG::Image.new(image1.width, image1.height, WHITE)
56
- diff = []
57
-
58
- each_pixel(image1) do |x, y|
59
- pixel1 = image1[x,y]
60
- pixel2 = image2[x,y]
61
- unless pixel1 == pixel2
62
- output[x,y], score = color_difference_of_pixels(pixel1, pixel2)
63
- diff << score
64
- end
65
- end
66
-
67
- filename = "#{name}-color_difference.png"
68
- output.save(filename)
69
- File.join(
70
- File.basename(File.dirname(filename)),
71
- File.basename(filename)
72
- )
73
- end
74
-
75
- def color_difference_of_pixels(pixel1, pixel2)
76
- score = Math.sqrt(
77
- (r(pixel2) - r(pixel1)) ** 2 +
78
- (g(pixel2) - g(pixel1)) ** 2 +
79
- (b(pixel2) - b(pixel1)) ** 2
80
- ) / Math.sqrt(MAX ** 2 * 3)
81
-
82
- [grayscale(MAX - (score * MAX).round), score]
83
- end
84
-
85
- # Not called anywhere
86
- def color_difference_total_score
87
- pixels_total = image1.width * image1.height
88
- pixels_changed = diff.length
89
- pixels_changed_percentage = (diff.inject {|sum, value| sum + value} / pixels_total) * 100
90
- end
91
-
92
- def each_pixel(image)
93
- image.width.times do |x|
94
- image.height.times do |y|
95
- yield(x, y)
96
- end
97
- end
98
- end
99
- end
100
- end
@@ -1,36 +0,0 @@
1
- require_relative '../spec_helper'
2
- require 'fileutils'
3
- require 'date'
4
- require 'nokogiri'
5
-
6
- describe "Hit a list of paths for this app" do
7
- it "takes screenshots, diffs them, and creates an index" do
8
- root_dir = Dir.getwd
9
- Dir.chdir(File.join(File.dirname(__FILE__), '..', 'sample_app'))
10
-
11
- results_tmp_dir = File.join("tmp", "results")
12
-
13
- FileUtils.remove_dir(results_tmp_dir, true)
14
-
15
- Compatriot.app = TestApp
16
- Compatriot.run(%w[
17
- /
18
- /chrome-css-bug
19
- ])
20
-
21
- current_results_dir = (Dir.entries(results_tmp_dir) - [".", ".."]).first
22
- current_results_dir = File.join(results_tmp_dir, current_results_dir)
23
-
24
- firefox_dir = File.join(current_results_dir, "firefox")
25
- chrome_dir = File.join(current_results_dir, "chrome")
26
-
27
- Dir.glob(File.join(firefox_dir, "*.png")).size.must_equal 4
28
- Dir.glob(File.join(chrome_dir, "*.png")).size.must_equal 2
29
-
30
- results_index = IO.read(File.join(current_results_dir, "index.html"))
31
- xml = Nokogiri::XML(results_index)
32
- xml.xpath("//tr[td]").size.must_equal(2)
33
-
34
- Dir.chdir(root_dir)
35
- end
36
- end
@@ -1,53 +0,0 @@
1
- require_relative '../spec_helper'
2
-
3
- describe Compatriot::ImageDiffer do
4
- describe "compute!" do
5
- it "diffs each set of images and stores the location by path" do
6
- d = Compatriot::ImageDiffer.new(
7
- :paths => ["/home"],
8
- :browsers => [
9
- stub(:absolute_screenshot_for => "1.png"),
10
- stub(:absolute_screenshot_for => "2.png")
11
- ]
12
- )
13
- d.expects(:diff).with(["1.png", "2.png"]).returns("diff.png")
14
- d.compute!
15
- d.diffs["/home"].must_equal("diff.png")
16
- end
17
- end
18
- describe "diff" do
19
- it "calls chunky_png on each image path" do
20
- file_one = stub
21
- file_two = stub
22
-
23
- ChunkyPNG::Image.expects(:from_file).with(file_one)
24
- ChunkyPNG::Image.expects(:from_file).with(file_two)
25
-
26
- c = Compatriot::ImageDiffer.new
27
- c.stubs(:color_difference)
28
- c.diff([file_one, file_two])
29
- end
30
-
31
- it "returns the filename of the diff" do
32
- end
33
-
34
- it "uses the strategy passed in" do
35
- end
36
- end
37
-
38
- describe "self#color_difference" do
39
- it "starts a new white image with the same dimensions" do
40
- diff = stub_everything
41
- ChunkyPNG::Image.expects(:new).with(
42
- 1,
43
- 2,
44
- ChunkyPNG::Image::WHITE
45
- ).returns(diff)
46
-
47
- image1 = stub_everything("1", :width => 1, :height => 2)
48
- image2 = stub_everything("2", :width => 3, :height => 4)
49
-
50
- Compatriot::ImageDiffer.new.color_difference(image1, image2, stub)
51
- end
52
- end
53
- end