monet 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4ba54b53a3cb965bdcc834da924e37f3df244e76
4
- data.tar.gz: ca5a32475a8411d8dcc61e750ca6e534582a38a2
3
+ metadata.gz: 6fd195da9656b0fe86982f9ee484dcbf50a0aeb2
4
+ data.tar.gz: 88a19e81f68e678b6be8885f120848cb2ea63450
5
5
  SHA512:
6
- metadata.gz: e2debbc40dc2411f580a40ee1dd8e8bf6691ca342779ac773a8ca48d059edd35d442a89cbb3cffb28c402f88f0413ff99b7ad16c6313cac9b1607c078545ffd9
7
- data.tar.gz: 0308e17eeac8c8e00d60a1e752b74711186f1faa16ec0bc729082b14a8d0f3e1d8dc45132fec54b46441f7eb972a66bfcdc5fc1de0ce56ad323d246fcadfa8e7
6
+ metadata.gz: f02f289bc355712dffdb37d0e4ca117d4bcb123559dce5c34ab260fbfbeb0736555f0d105055e6e2e757751477dcccf7e535cdc759f7f1ee25be2cbb8694ec67
7
+ data.tar.gz: fbb56feae30ce176c1a182a48c557ee986a4eff0d54ce65136d4dc445834c0a391586e6762b19dcd94d66f926b63dc2e360353a8414e20a3375e64b3cafba0da
data/Gemfile CHANGED
@@ -4,6 +4,7 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  group :test do
7
+ gem 'flexmock'
7
8
  gem 'pry'
8
9
  gem 'webmock'
9
10
  gem 'vcr'
data/README.md CHANGED
@@ -1,6 +1,4 @@
1
- # Monet
2
- [![Build Status](https://travis-ci.org/plukevdh/monet.png?branch=master)](https://travis-ci.org/plukevdh/monet)
3
- [![Code Climate](https://codeclimate.com/github/plukevdh/monet.png)](https://codeclimate.com/github/plukevdh/monet)
1
+ # Monet [![Build Status](https://travis-ci.org/plukevdh/monet.png?branch=master)](https://travis-ci.org/plukevdh/monet) [![Code Climate](https://codeclimate.com/github/plukevdh/monet.png)](https://codeclimate.com/github/plukevdh/monet)
4
2
 
5
3
  Monet is a libary built for making testing interfaces and design easy. We all have interfaces that we've added a new button, changed some CSS, or added new javascript interaction to and had the page layout explode unexpectedly. Monet is meant to make tracking those changes and ensuring consistent automatably easy.
6
4
 
@@ -20,7 +18,7 @@ Or install it yourself as:
20
18
 
21
19
  ## Usage
22
20
 
23
- The basic gem requires a config file that is somehow called on the app startup. This config primarily exists to give the gem a list of paths it needs to collect and either baseline or compare to previous baselines. This config might look something like this:
21
+ The basic gem requires a config file that is called in an app initializer or via the built-in rake task. This config primarily exists to give the gem a list of paths it needs to collect and either baseline or compare to previous baselines. This config might look something like this:
24
22
 
25
23
  ```ruby
26
24
  Monet.config do |config|
@@ -38,10 +36,29 @@ Monet.config do |config|
38
36
  end
39
37
  ```
40
38
 
39
+ ## Process
40
+
41
+ Captures are saved into the following structure:
42
+
43
+ ```
44
+ /captures
45
+ /baselines
46
+ /captures
47
+ ```
48
+
49
+ - /captures is where the current capture run images are stored, pre-comparison with baseline.
50
+ - /baselines is where all current baseline images are stored. persistent in-between capture runs.
51
+
52
+ During the capture process, any new captures that do not have a match found in baselines to compare with are considered new baselines.
53
+ Any images that match baseline are discarded.
54
+ Any images that flag differences, are flagged for review.
55
+
56
+ Review involves checking flagged images and marking as
57
+ 1. discard
58
+ 2. flag as issue
59
+ 3. accept as new baseline
60
+
41
61
  ## Todo
42
- - Site spidering
43
- - Baseline caching
44
- - Browser/driver config
45
62
  - Parallelize PNG diffing
46
63
  - Dashboard
47
64
  - Rails integration
data/Rakefile CHANGED
@@ -1,10 +1,22 @@
1
1
  require "bundler/gem_tasks"
2
2
  require 'rspec/core/rake_task'
3
- desc "Runs the site and grabs baselines"
3
+ require 'monet'
4
+
5
+ task :clean do
6
+ config = Monet::Config.load
7
+ Monet.clean config
8
+ end
4
9
 
5
- task :baseline do
10
+ desc "Runs the site and grabs baselines"
11
+ task :baseline => [:clean, :run] do
12
+ end
6
13
 
14
+ desc "Run the baseline comparison"
15
+ task :run do
16
+ config = Monet::Config.load
17
+ Monet.capture config
18
+ Monet.compare config
7
19
  end
8
20
 
9
21
  RSpec::Core::RakeTask.new(:test)
10
- task :default => :test
22
+ task :default => :run
data/config.yaml ADDED
@@ -0,0 +1,11 @@
1
+ :driver: :poltergeist
2
+ :dimensions:
3
+ - 1024
4
+
5
+ :base_url: "http://lance.com"
6
+
7
+ :compare_type: Highlight
8
+ :map:
9
+ - "/"
10
+ - "/aboutus"
11
+ - "/littleleague"
data/lib/monet.rb CHANGED
@@ -1,9 +1,30 @@
1
1
  require "monet/version"
2
- require "monet/errors"
3
- require "monet/capture_map"
2
+ require 'monet/config'
4
3
  require "monet/capture"
5
4
  require "monet/compare"
5
+ require "monet/baseline_control"
6
6
 
7
7
  module Monet
8
+ class << self
9
+ def clean(opts)
10
+ config = load_config(opts)
11
+ Dir.glob(File.join(config.baseline_dir, "**", "*.png")).each do |img|
12
+ File.delete img
13
+ end
14
+ end
8
15
 
16
+ def capture(opts)
17
+ agent = Monet::Capture.new(load_config(opts))
18
+ agent.capture_all
19
+ end
20
+
21
+ def compare(opts)
22
+ control = Monet::BaselineControl.new(opts)
23
+ control.run
24
+ end
25
+
26
+ def load_config(options)
27
+ Monet::Config.build_config(options)
28
+ end
29
+ end
9
30
  end
@@ -0,0 +1,11 @@
1
+ require 'fileutils'
2
+
3
+ module Monet
4
+ class BaselessImage
5
+ attr_reader :path
6
+
7
+ def initialize(path)
8
+ @path = path
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,68 @@
1
+ require 'monet/path_router'
2
+ require 'monet/changeset'
3
+ require 'monet/baseless_image'
4
+ require 'monet/compare'
5
+
6
+ require 'fileutils'
7
+
8
+ module Monet
9
+ class BaselineControl
10
+
11
+ attr_reader :flags
12
+
13
+ def initialize(config)
14
+ @capture_dir = config.capture_dir
15
+ @baseline_dir = config.baseline_dir
16
+
17
+ strategy = Monet.const_get(config.compare_type)
18
+
19
+ @comparer = Monet::Compare.new(strategy)
20
+ @router = Monet::PathRouter.new(config)
21
+ @flags = []
22
+ end
23
+
24
+ def run
25
+ captures.each do |capture|
26
+ baseline_path = @router.capture_to_baseline(capture)
27
+ compare @comparer.compare(baseline_path, capture)
28
+ end
29
+
30
+ @flags
31
+ end
32
+
33
+ def compare(diff)
34
+ return baseline(diff) if diff.is_a? Monet::BaselessImage
35
+ return discard(diff.path) unless diff.modified?
36
+
37
+ puts "diff found #{diff.path}"
38
+
39
+ @flags << diff.path
40
+ end
41
+
42
+ def captures
43
+ Dir.glob File.join(site_dir(@capture_dir), "*.png")
44
+ end
45
+
46
+ def discard(path)
47
+ puts "discarding #{path}"
48
+ FileUtils.remove(path)
49
+ end
50
+
51
+ def baseline(diff)
52
+ path = diff.path
53
+ to = site_dir(@baseline_dir)
54
+
55
+ FileUtils.mkpath to unless Dir.exists? to
56
+ FileUtils.move(path, to)
57
+
58
+ puts "baselining #{path}"
59
+
60
+ @router.capture_to_baseline(path)
61
+ end
62
+
63
+ private
64
+ def site_dir(base)
65
+ File.join base, @router.root_dir
66
+ end
67
+ end
68
+ end
data/lib/monet/capture.rb CHANGED
@@ -8,30 +8,28 @@ module Monet
8
8
  class Capture
9
9
  include Capybara::DSL
10
10
 
11
- def initialize(config={})
12
- @config = (config.is_a? Monet::Config) ? config : Monet::Config.new(config)
11
+ MAX_HEIGHT = 10000
13
12
 
14
- # TODO: make configurable
15
- Capybara.default_driver = @config.driver
16
- end
13
+ def initialize(config)
14
+ @config = Monet::Config.build_config(config)
15
+ @router = Monet::PathRouter.new(@config)
17
16
 
18
- def capture(path)
19
- visit normalize_path(path)
20
- page.driver.render(image_name_from_path(path), full: true)
17
+ Capybara.default_driver = @config.driver
21
18
  end
22
19
 
23
- private
24
- def capture_path
25
- @config.capture_dir
20
+ def capture_all
21
+ @config.map.paths.each do |path|
22
+ capture(path)
23
+ end
26
24
  end
27
25
 
28
- def normalize_path(path)
29
- "http://#{path}" unless path.start_with?("https?")
30
- end
26
+ def capture(path)
27
+ visit @router.build_url(path)
31
28
 
32
- def image_name_from_path(path)
33
- name = path.gsub(/https?:\/\//, '').gsub('.', '_')
34
- "#{capture_path}/#{name}-#{Time.now.to_i}.png"
29
+ @config.dimensions.each do |width|
30
+ page.driver.resize(width, MAX_HEIGHT)
31
+ page.driver.render(@router.route_url_path(path, width), full: true)
32
+ end
35
33
  end
36
34
  end
37
35
  end
@@ -1,14 +1,16 @@
1
1
  require 'spidr'
2
+ require 'monet/url_helpers'
2
3
 
3
4
  module Monet
4
5
  class CaptureMap
5
6
  extend Forwardable
6
7
 
7
8
  class PathCollection
9
+ include URLHelpers
8
10
  attr_reader :paths, :root_url
9
11
 
10
- def initialize(root_url)
11
- @root_url = root_url
12
+ def initialize(root_uri)
13
+ @root_url = parse_uri(root_uri)
12
14
  @paths = []
13
15
  end
14
16
 
@@ -16,6 +18,10 @@ module Monet
16
18
  @paths << normalized_path(path)
17
19
  end
18
20
 
21
+ def paths=(paths)
22
+ @paths.concat paths.map {|p| normalized_path(p) }
23
+ end
24
+
19
25
  def normalized_path(path)
20
26
  path.chomp "/"
21
27
  end
@@ -39,18 +45,16 @@ module Monet
39
45
  end
40
46
  end
41
47
 
42
- InvalidURL = Class.new(StandardError)
43
-
44
48
  attr_reader :type
45
49
 
46
- def initialize(root_url, type=:explicit, &block)
50
+ def initialize(root_uri, type=:explicit, &block)
47
51
  @type = type
48
- @path_helper = type_mapper.new parse_uri(root_url)
52
+ @path_helper = type_mapper.new root_uri
49
53
 
50
54
  yield(@path_helper) if block_given?
51
55
  end
52
56
 
53
- def_delegators :@path_helper, :paths, :add, :root_url
57
+ def_delegators :@path_helper, :paths, :paths=, :add, :root_url
54
58
 
55
59
  def type_mapper
56
60
  case @type
@@ -60,13 +64,5 @@ module Monet
60
64
  PathSpider
61
65
  end
62
66
  end
63
-
64
- private
65
- def parse_uri(path)
66
- uri = URI.parse path
67
- raise InvalidURL, "#{path} is not a valid url" if uri.class == URI::Generic
68
-
69
- uri.to_s
70
- end
71
67
  end
72
68
  end
@@ -0,0 +1,23 @@
1
+ module Monet
2
+ class Changeset
3
+ attr_reader :path
4
+
5
+ def initialize(base_image, pixel_array, path)
6
+ @base_image = base_image
7
+ @changed_pixels = pixel_array
8
+ @path = path
9
+ end
10
+
11
+ def modified?
12
+ pixels_changed > 0
13
+ end
14
+
15
+ def pixels_changed
16
+ @changed_pixels.count
17
+ end
18
+
19
+ def percentage_changed
20
+ ((pixels_changed.to_f / @base_image.area.to_f) * 100).round(2)
21
+ end
22
+ end
23
+ end
data/lib/monet/compare.rb CHANGED
@@ -1,52 +1,39 @@
1
1
  require 'oily_png'
2
2
  require 'monet/diff_strategy'
3
+ require 'monet/changeset'
4
+ require 'monet/baseless_image'
3
5
 
4
6
  module Monet
5
7
  class Compare
6
8
  extend Forwardable
7
9
 
8
- class Changeset
9
- def initialize(base_image, pixel_array)
10
- @base_image = base_image
11
- @changed_pixels = pixel_array
12
- end
13
-
14
- def modified?
15
- pixels_changed > 0
16
- end
17
-
18
- def pixels_changed
19
- @changed_pixels.count
20
- end
21
-
22
- def percentage_changed
23
- ((pixels_changed.to_f / @base_image.area.to_f) * 100).round(2)
24
- end
25
- end
26
-
27
10
  def initialize(strategy=ColorBlend)
28
11
  @strategy_class = strategy
29
12
  end
30
13
 
31
14
  def compare(base_image, new_image)
32
- base_png = ChunkyPNG::Image.from_file(base_image)
33
- new_png = ChunkyPNG::Image.from_file(new_image)
15
+ puts "comparing #{base_image} with #{new_image}"
16
+ begin
17
+ new_png = ChunkyPNG::Image.from_file(new_image)
18
+ base_png = ChunkyPNG::Image.from_file(base_image)
34
19
 
35
- diff_stats = []
20
+ diff_stats = []
36
21
 
37
- # TODO: make configurable
38
- diff_strategy = @strategy_class.new(base_png, new_png)
22
+ diff_strategy = @strategy_class.new(base_png, new_png)
39
23
 
40
- base_png.height.times do |y|
41
- base_png.row(y).each_with_index do |pixel, x|
42
- diff_strategy.calculate_for_pixel(pixel, x, y)
24
+ base_png.height.times do |y|
25
+ base_png.row(y).each_with_index do |pixel, x|
26
+ diff_strategy.calculate_for_pixel(pixel, x, y)
27
+ end
43
28
  end
44
- end
45
29
 
46
- changeset = Changeset.new(base_png, diff_strategy.score)
47
- diff_strategy.save(diff_filename(base_image)) if changeset.modified?
30
+ changeset = Changeset.new(base_png, diff_strategy.score, new_image)
31
+ diff_strategy.save(diff_filename(base_image)) if changeset.modified?
48
32
 
49
- changeset
33
+ changeset
34
+ rescue Errno::ENOENT => e
35
+ return BaselessImage.new(new_image)
36
+ end
50
37
  end
51
38
 
52
39
  private
data/lib/monet/config.rb CHANGED
@@ -1,15 +1,20 @@
1
1
  require 'monet/capture_map'
2
+ require 'yaml'
2
3
 
3
4
  module Monet
4
5
  class Config
6
+ include URLHelpers
7
+
5
8
  MissingBaseURL = Class.new(Exception)
6
9
 
7
10
  DEFAULT_OPTIONS = {
8
11
  driver: :poltergeist,
9
- dimensions: [1440],
10
- map: nil,
12
+ dimensions: [1024],
11
13
  base_url: nil,
12
- capture_dir: "./captures"
14
+ map: nil,
15
+ compare_type: "ColorBlend",
16
+ capture_dir: "./captures",
17
+ baseline_dir: "./baselines"
13
18
  }
14
19
 
15
20
  attr_accessor *DEFAULT_OPTIONS.keys
@@ -26,20 +31,47 @@ module Monet
26
31
  cfg
27
32
  end
28
33
 
34
+ def self.load(path="./config.yaml")
35
+ config = YAML::load(File.open(path))
36
+ new(config)
37
+ end
38
+
39
+ def self.build_config(opts)
40
+ (opts.is_a? Monet::Config) ? opts : new(opts)
41
+ end
42
+
43
+ def base_url=(url)
44
+ @base_url ||= parse_uri(url) unless url.nil?
45
+ end
46
+
29
47
  def base_url
30
48
  raise MissingBaseURL, "Please set the base_url in the config" unless @base_url
31
49
  @base_url
32
50
  end
33
51
 
34
52
  def capture_dir=(path)
35
- @capture_dir = File.expand_path(path)
53
+ @capture_dir = expand_path(path)
54
+ end
55
+
56
+ def baseline_dir=(path)
57
+ @baseline_dir = expand_path(path)
58
+ end
59
+
60
+ def map=(paths)
61
+ map.paths = paths unless paths.nil?
36
62
  end
37
63
 
38
64
  def map(type=:explicit, &block)
39
65
  @map ||= CaptureMap.new(base_url, type)
40
66
 
41
67
  block.call(@map) if block_given? && type == :explicit
68
+
42
69
  @map
43
70
  end
71
+
72
+ private
73
+ def expand_path(path)
74
+ File.expand_path(path)
75
+ end
44
76
  end
45
77
  end
@@ -1,3 +1,5 @@
1
+ require 'monet/errors'
2
+
1
3
  module Monet
2
4
  class DiffStrategy
3
5
  include ChunkyPNG::Color
@@ -55,7 +57,7 @@ module Monet
55
57
  class ColorBlend < DiffStrategy
56
58
  def initialize(base_image, diff_image)
57
59
  super
58
- @output = ChunkyPNG::Image.new(base_image.width, diff_image.width, BLACK)
60
+ @output = ChunkyPNG::Image.new(base_image.width, base_image.height, BLACK)
59
61
  end
60
62
 
61
63
  def calculate_for_pixel(pixel, x, y)
@@ -67,4 +69,27 @@ module Monet
67
69
  super
68
70
  end
69
71
  end
72
+
73
+ class Highlight < DiffStrategy
74
+ ALPHA_COMPONENT = 30
75
+
76
+ def initialize(base_image, diff_image)
77
+ super
78
+ @output = ChunkyPNG::Image.new(base_image.width, base_image.height, WHITE)
79
+ end
80
+
81
+ def colors(pixel)
82
+ rgb_colors = %w(r g b).map {|color| for_color(color, pixel)}
83
+ end
84
+
85
+ def calculate_for_pixel(pixel, x, y)
86
+ if pixel == @diff_image[x,y]
87
+ @output[x,y] = rgba(*colors(pixel), ALPHA_COMPONENT)
88
+ else
89
+ @output[x,y] = html_color("blue")
90
+ end
91
+
92
+ super
93
+ end
94
+ end
70
95
  end
@@ -0,0 +1,64 @@
1
+ require 'monet/url_helpers'
2
+
3
+ module Monet
4
+ class PathRouter
5
+ include URLHelpers
6
+
7
+ def initialize(config)
8
+ @base_url = parse_uri(config.base_url)
9
+ @capture_path = config.capture_dir
10
+ @baseline_path = config.baseline_dir
11
+ end
12
+
13
+ def build_url(path)
14
+ "#{@base_url}#{path}"
15
+ end
16
+
17
+ # takes a url, gives the image path
18
+ def route_url(url, width="*")
19
+ uri = parse_uri(url)
20
+ route_url_path(uri.path, width)
21
+ end
22
+
23
+ # takes a url path, gives the image path
24
+ def route_url_path(path, width="*")
25
+ image_name(@capture_path, path, width)
26
+ end
27
+
28
+ def url_to_baseline(url, width)
29
+ uri = parse_uri(url)
30
+ url_path_to_baseline(uri.path, width)
31
+ end
32
+
33
+ def url_path_to_baseline(path, width)
34
+ image_name(@baseline_path, path, width)
35
+ end
36
+
37
+ def capture_to_baseline(path)
38
+ path.gsub(@capture_path, @baseline_path)
39
+ end
40
+
41
+ # takes a path, returns the URL used to generate the image
42
+ def route_path(path)
43
+ url = path.split("/").last
44
+ path = url.split('>')[1..-1].join("/").gsub(/-\d+\.png/, "")
45
+
46
+ "#{@base_url}/#{path}"
47
+ end
48
+
49
+ def host
50
+ @base_url.host
51
+ end
52
+ alias :root_dir :host
53
+
54
+ private
55
+ def image_name(base_dir, path, width)
56
+ name = normalize_path(path).gsub(/\//, '>')
57
+ "#{base_dir}/#{host}/#{name}-#{width}.png"
58
+ end
59
+
60
+ def normalize_path(path)
61
+ "#{host}#{path}"
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,13 @@
1
+ module Monet
2
+ module URLHelpers
3
+ ::Monet::InvalidURL = Class.new(StandardError)
4
+
5
+ private
6
+ def parse_uri(path)
7
+ uri = path.is_a?(URI) ? path : URI.parse(path)
8
+ raise InvalidURL, "#{path} is not a valid url" if uri.class == URI::Generic
9
+
10
+ uri
11
+ end
12
+ end
13
+ end
data/lib/monet/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Monet
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+ require 'monet/baseline_control'
3
+
4
+ describe Monet::BaselineControl do
5
+ Given(:control) do
6
+ config = flexmock("Config", compare_type: "ColorBlend", capture_dir: "./spec/fixtures", baseline_dir: "./baseline", base_url: "http://google.com")
7
+ flexmock Monet::BaselineControl.new(config)
8
+ end
9
+
10
+ context "makes a capture baseline if no baseline exists" do
11
+ Given(:diff) { Monet::BaselessImage.new("./fixtures/fake.png") }
12
+ Given { control.should_receive(:baseline).with(diff).and_return("./baseline/fake.png") }
13
+
14
+ When(:result) { control.compare(diff) }
15
+
16
+ Then { control.should have_received(:baseline).with(diff) }
17
+ And { result.should eq("./baseline/fake.png") }
18
+ end
19
+
20
+ context "discards captures that match baseline" do
21
+ Given(:diff) { flexmock(:on, Monet::Changeset, modified?: false, path: "./fixtures/fake.png") }
22
+ Given { control.should_receive(:discard).with("./fixtures/fake.png") }
23
+
24
+ When(:result) { control.compare(diff) }
25
+
26
+ Then { control.should have_received(:discard).with("./fixtures/fake.png") }
27
+ end
28
+
29
+ context "flags changed capture as different" do
30
+ Given(:diff) { flexmock(:on, Monet::Changeset, modified?: true, path: "./fixtures/fake.png") }
31
+
32
+ When(:diffs) { control.compare(diff) }
33
+
34
+ Then { diffs.length.should == 1 }
35
+ And { diffs.should == ["./fixtures/fake.png"] }
36
+ end
37
+
38
+ context "replaces baseline if requested" do
39
+ Given(:diff) { flexmock(:on, Monet::Changeset, modified?: true, path: "./fixtures/fake.png") }
40
+ Given { control.should_receive(:baseline).with(diff).and_return("./baseline/fake.png") }
41
+
42
+ When(:result) { control.baseline(diff) }
43
+
44
+ Then { control.should have_received(:baseline).with(diff) }
45
+ And { result.should eq("./baseline/fake.png") }
46
+ end
47
+ end
@@ -31,17 +31,17 @@ describe Monet::CaptureMap do
31
31
 
32
32
  context "requires full url" do
33
33
  When(:result) { Monet::CaptureMap.new("google.com") }
34
- Then { result.should have_failed(Monet::CaptureMap::InvalidURL, /google.com is not a valid url/) }
34
+ Then { result.should have_failed(Monet::InvalidURL, /google.com is not a valid url/) }
35
35
  end
36
36
 
37
37
  context "requires valid url" do
38
38
  When(:result) { Monet::CaptureMap.new("google") }
39
- Then { result.should have_failed(Monet::CaptureMap::InvalidURL, /google is not a valid url/) }
39
+ Then { result.should have_failed(Monet::InvalidURL, /google is not a valid url/) }
40
40
  end
41
41
 
42
42
  context "with name" do
43
43
  Given(:map) { Monet::CaptureMap.new("http://google.com") }
44
- Then { map.root_url.should == "http://google.com" }
44
+ Then { map.root_url.to_s.should == "http://google.com" }
45
45
  And { map.paths.should == [] }
46
46
  end
47
47
 
@@ -54,7 +54,7 @@ describe Monet::CaptureMap do
54
54
  }
55
55
 
56
56
  context "add paths" do
57
- Then { map.root_url.should == "http://google.com" }
57
+ Then { map.root_url.to_s.should == "http://google.com" }
58
58
  And { map.paths.should == ['home', 'test/new'] }
59
59
  end
60
60
  end
data/spec/capture_spec.rb CHANGED
@@ -3,30 +3,25 @@ require 'monet/capture'
3
3
 
4
4
  describe Monet::Capture do
5
5
  Given(:path) { File.expand_path './spec/tmp/output' }
6
- Given(:capture_agent) { Monet::Capture.new(capture_dir: path) }
7
-
8
- before do
9
- Timecop.freeze
10
- end
6
+ Given(:url) { "http://google.com" }
7
+ Given(:capture_agent) { Monet::Capture.new(capture_dir: path, base_url: url) }
11
8
 
12
9
  after do
13
- Timecop.return
14
-
15
- Dir.glob("#{path}/*.png").each do |file|
10
+ Dir.glob("#{path}/**/*.png").each do |file|
16
11
  File.delete(file)
17
12
  end
18
13
  end
19
14
 
20
15
  context "can pass config" do
21
16
  context "as a hash" do
22
- Given(:capture_agent) { Monet::Capture.new(capture_dir: path) }
17
+ Given(:capture_agent) { Monet::Capture.new(capture_dir: path, base_url: url) }
23
18
  When(:config) { capture_agent.instance_variable_get :@config }
24
19
  Then { config.should be_a(Monet::Config) }
25
20
  Then { config.capture_dir.should == path }
26
21
  end
27
22
 
28
23
  context "as a Monet::Config" do
29
- Given(:config) { Monet::Config.new(capture_dir: path) }
24
+ Given(:config) { Monet::Config.new(capture_dir: path, base_url: url) }
30
25
  Given(:capture_agent) { Monet::Capture.new(config) }
31
26
  When(:final) { capture_agent.instance_variable_get :@config }
32
27
  Then { final.should be_a(Monet::Config) }
@@ -35,13 +30,22 @@ describe Monet::Capture do
35
30
  end
36
31
 
37
32
  context "converts name properly" do
38
- When { capture_agent.capture('https://google.com') }
39
- Then { File.exist?("#{path}/google_com-#{Time.now.to_i}.png").should be_true }
33
+ Given(:capture_agent) { Monet::Capture.new(capture_dir: path, base_url: url, dimensions: [900]) }
34
+ When { capture_agent.capture("/") }
35
+ Then { File.exist?("#{path}/google.com/google.com>-900.png").should be_true }
36
+ end
37
+
38
+ context "captures all dimensions requested" do
39
+ Given(:capture_agent) { Monet::Capture.new(capture_dir: path, base_url: url, dimensions: [900, 1400]) }
40
+ When { capture_agent.capture("/") }
41
+ Then { File.exist?("#{path}/google.com/google.com>-900.png").should be_true }
42
+ Then { File.exist?("#{path}/google.com/google.com>-1400.png").should be_true }
40
43
  end
41
44
 
42
45
  context "prepends default protocol if missing" do
43
- When { capture_agent.capture('www.facebook.com') }
44
- Then { File.exist?("#{path}/www_facebook_com-#{Time.now.to_i}.png").should be_true }
46
+ Given(:capture_agent) { Monet::Capture.new(capture_dir: path, base_url: "http://www.facebook.com") }
47
+ When { capture_agent.capture('/') }
48
+ Then { File.exist?("#{path}/www.facebook.com/www.facebook.com>-1024.png").should be_true }
45
49
  end
46
50
 
47
51
  end
data/spec/compare_spec.rb CHANGED
@@ -12,6 +12,12 @@ describe Monet::Compare do
12
12
  context "default compare" do
13
13
  Given(:compare) { Monet::Compare.new }
14
14
 
15
+ context "missing baseline" do
16
+ When(:result) { compare.compare("./missing_image.png", image_same) }
17
+ Then { result.should be_a(Monet::BaselessImage) }
18
+ And { result.path.should == image_same }
19
+ end
20
+
15
21
  context "identical image" do
16
22
  When(:result) { compare.compare(image_base, image_same) }
17
23
  Then { result.should_not be_modified }
@@ -45,7 +51,7 @@ describe Monet::Compare do
45
51
 
46
52
  Then { result.should be_modified }
47
53
  And { File.exist?(diff_name).should be_true }
48
- And { result.pixels_changed.should eq(23800) }
49
- And { result.percentage_changed.should eq(5.44) }
54
+ And { result.pixels_changed.should eq(2387) }
55
+ And { result.percentage_changed.should eq(0.55) }
50
56
  end
51
57
  end
data/spec/config_spec.rb CHANGED
@@ -12,7 +12,7 @@ describe Monet::Config do
12
12
  When(:config) { Monet::Config.new(capture_dir: "./faker", base_url: "http://hoodie.io") }
13
13
  Then { config.driver.should == :poltergeist }
14
14
  And { config.capture_dir.should == File.expand_path("./faker") }
15
- And { config.base_url.should == "http://hoodie.io" }
15
+ And { config.base_url.to_s.should == "http://hoodie.io" }
16
16
  end
17
17
 
18
18
  context "can set options" do
Binary file
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+ require 'monet/path_router'
3
+ require 'monet/config'
4
+
5
+ describe Monet::PathRouter do
6
+ Given(:capture) { File.expand_path "./capture" }
7
+ Given(:baseline) { File.expand_path "./baseline" }
8
+ Given(:config) { Monet::Config.new(base_url: "http://google.com", capture_dir: "./capture", baseline_dir: "./baseline") }
9
+ Given(:router) { Monet::PathRouter.new(config) }
10
+
11
+ context "knows base dir" do
12
+ When(:path) { router.root_dir }
13
+ Then { path.should == "google.com" }
14
+ end
15
+
16
+ context "build url from path" do
17
+ When(:url) { router.build_url("/space/manager") }
18
+ Then { url.should == "http://google.com/space/manager" }
19
+ end
20
+
21
+ context "full url to path" do
22
+ When(:path) { router.route_url("http://google.com/space/manager", 900) }
23
+ Then { path.should == "#{capture}/google.com/google.com>space>manager-900.png" }
24
+ end
25
+
26
+ context "url path to path" do
27
+ When(:path) { router.route_url_path("/space/manager", 900) }
28
+ Then { path.should == "#{capture}/google.com/google.com>space>manager-900.png"}
29
+ end
30
+
31
+ context "path to url" do
32
+ When(:url) { router.route_path("#{capture}/google.com/google.com>space>manager-900.png") }
33
+ Then { url.should == "http://google.com/space/manager" }
34
+ end
35
+
36
+ context "path to baseline from url" do
37
+ When(:path) { router.url_to_baseline("http://google.com/space/manager", 900) }
38
+ Then { path.should == "#{baseline}/google.com/google.com>space>manager-900.png" }
39
+ end
40
+
41
+ context "path to baseline from path" do
42
+ When(:path) { router.url_path_to_baseline("/space/manager", 900) }
43
+ Then { path.should == "#{baseline}/google.com/google.com>space>manager-900.png" }
44
+ end
45
+
46
+ context "capture path to baseline path" do
47
+ When(:path) { router.capture_to_baseline("#{capture}/google.com/google.com>space>manager-900.png") }
48
+ Then { path.should == "#{baseline}/google.com/google.com>space>manager-900.png" }
49
+ end
50
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'vcr'
2
2
  require 'rspec/given'
3
3
  require 'timecop'
4
+ require 'pry'
4
5
 
5
6
  VCR.configure do |c|
6
7
  c.cassette_library_dir = 'spec/cassettes'
@@ -9,5 +10,6 @@ VCR.configure do |c|
9
10
  end
10
11
 
11
12
  RSpec.configure do |c|
13
+ c.mock_with :flexmock
12
14
  c.treat_symbols_as_metadata_keys_with_true_values = true
13
15
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: monet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luke van der Hoeven
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-12 00:00:00.000000000 Z
11
+ date: 2013-12-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -137,15 +137,22 @@ files:
137
137
  - LICENSE.txt
138
138
  - README.md
139
139
  - Rakefile
140
+ - config.yaml
140
141
  - lib/monet.rb
142
+ - lib/monet/baseless_image.rb
143
+ - lib/monet/baseline_control.rb
141
144
  - lib/monet/capture.rb
142
145
  - lib/monet/capture_map.rb
146
+ - lib/monet/changeset.rb
143
147
  - lib/monet/compare.rb
144
148
  - lib/monet/config.rb
145
149
  - lib/monet/diff_strategy.rb
146
150
  - lib/monet/errors.rb
151
+ - lib/monet/path_router.rb
152
+ - lib/monet/url_helpers.rb
147
153
  - lib/monet/version.rb
148
154
  - monet.gemspec
155
+ - spec/baseline_control_spec.rb
149
156
  - spec/capture_map_spec.rb
150
157
  - spec/capture_spec.rb
151
158
  - spec/cassettes/spider.yml
@@ -155,6 +162,7 @@ files:
155
162
  - spec/fixtures/diff.png
156
163
  - spec/fixtures/diff_size.png
157
164
  - spec/fixtures/same.png
165
+ - spec/path_router_spec.rb
158
166
  - spec/spec_helper.rb
159
167
  homepage: http://plukevdh.github.com/monet
160
168
  licenses: []
@@ -183,6 +191,7 @@ summary: Monet captures your web pages, sets up a baseline and then ensures that
183
191
  if CSS changes blow your UI up. Simply capture your page, make changes, run tests
184
192
  and compare the diff!
185
193
  test_files:
194
+ - spec/baseline_control_spec.rb
186
195
  - spec/capture_map_spec.rb
187
196
  - spec/capture_spec.rb
188
197
  - spec/cassettes/spider.yml
@@ -192,4 +201,5 @@ test_files:
192
201
  - spec/fixtures/diff.png
193
202
  - spec/fixtures/diff_size.png
194
203
  - spec/fixtures/same.png
204
+ - spec/path_router_spec.rb
195
205
  - spec/spec_helper.rb