compatriot 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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