monet 0.2.3 → 0.2.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +7 -0
- data/Gemfile +1 -1
- data/lib/monet/baseless_image.rb +5 -1
- data/lib/monet/baseline_control.rb +30 -17
- data/lib/monet/capture.rb +28 -27
- data/lib/monet/capture_map.rb +11 -3
- data/lib/monet/changeset.rb +4 -0
- data/lib/monet/config.rb +18 -11
- data/lib/monet/image.rb +67 -0
- data/lib/monet/router.rb +71 -0
- data/lib/monet/tasks.rb +10 -7
- data/lib/monet/version.rb +1 -1
- data/lib/monet.rb +4 -29
- data/spec/capture_map_spec.rb +5 -50
- data/spec/capture_spec.rb +59 -33
- data/spec/cassettes/spider-config.yml +33784 -0
- data/spec/cassettes/spider-map.yml +33782 -0
- data/spec/config_spec.rb +14 -8
- data/spec/fixtures/baselines/lance.com/.DS_Store +0 -0
- data/spec/fixtures/baselines/lance.com/lance.com-1024-diff.png +0 -0
- data/spec/fixtures/baselines/lance.com/lance.com-1024.png +0 -0
- data/spec/fixtures/baselines/lance.com/lance.com|aboutus-1024.png +0 -0
- data/spec/fixtures/captures/lance.com/lance.com-1024.png +0 -0
- data/spec/fixtures/spider-config.yaml +8 -0
- data/spec/fixtures/thumbnails/lance.com/.DS_Store +0 -0
- data/spec/fixtures/thumbnails/lance.com/lance.com-1024.png +0 -0
- data/spec/image_spec.rb +36 -0
- data/spec/router_spec.rb +90 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/tmp/test.png +0 -0
- metadata +31 -7
- data/lib/monet/path_router.rb +0 -70
- data/spec/cassettes/spider.yml +0 -28572
- data/spec/path_router_spec.rb +0 -59
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4245512ca8fcbcf954b6e276d7dc978c9a680444
|
4
|
+
data.tar.gz: 19dd5cf3f272708cb87754604cc7902d33501144
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ac0adb807b480ab762d914312f79938df763d165c52a0c43586cccd11c8b680863df007d7fd3fe132b6b33d4469e3104795fcf17924f9fa4fd287475294176b3
|
7
|
+
data.tar.gz: 95da6332f5b6d0af5ce48980ceedd1535ea21d472ac11c42e938a6fae25bf1b472efa6a74fd1739a61ab7afb1349bf6f5445d6800244e5f4a7b91ae3fc4de87c
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.0
|
1
|
+
2.0.0-p247
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/lib/monet/baseless_image.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'monet/
|
1
|
+
require 'monet/image'
|
2
2
|
require 'monet/changeset'
|
3
3
|
require 'monet/baseless_image'
|
4
4
|
require 'monet/compare'
|
@@ -11,20 +11,18 @@ module Monet
|
|
11
11
|
attr_reader :flags
|
12
12
|
|
13
13
|
def initialize(config)
|
14
|
-
@
|
15
|
-
@baseline_dir = config.baseline_dir
|
14
|
+
@config = config
|
16
15
|
|
17
16
|
strategy = Monet.const_get(config.compare_type)
|
18
17
|
|
18
|
+
@router = Monet::Router.new config
|
19
19
|
@comparer = Monet::Compare.new(strategy)
|
20
|
-
@router = Monet::PathRouter.new(config)
|
21
20
|
@flags = []
|
22
21
|
end
|
23
22
|
|
24
23
|
def run
|
25
|
-
captures.each do |
|
26
|
-
|
27
|
-
compare @comparer.compare(baseline_path, capture)
|
24
|
+
captures.each do |img|
|
25
|
+
compare @comparer.compare(@router.baseline_dir(img.name), img.path)
|
28
26
|
end
|
29
27
|
|
30
28
|
@flags
|
@@ -40,29 +38,44 @@ module Monet
|
|
40
38
|
end
|
41
39
|
|
42
40
|
def captures
|
43
|
-
Dir.glob
|
41
|
+
files = Dir.glob(File.join(@config.capture_dir, @config.site, "*.png"))
|
42
|
+
files.map do |path|
|
43
|
+
Monet::Image.new(path)
|
44
|
+
end
|
44
45
|
end
|
45
46
|
|
46
47
|
def discard(path)
|
47
48
|
puts "discarding #{path}"
|
48
|
-
FileUtils.remove(path)
|
49
|
+
FileUtils.remove(path) if File.exists?(path)
|
49
50
|
end
|
50
51
|
|
51
52
|
def baseline(diff)
|
52
|
-
|
53
|
-
to = site_dir(@baseline_dir)
|
53
|
+
image = diff.is_a?(String) ? Monet::Image.new(diff) : diff.image
|
54
54
|
|
55
|
-
|
56
|
-
|
55
|
+
puts "baselining #{image.path}"
|
56
|
+
image = rebase(image)
|
57
57
|
|
58
|
-
|
58
|
+
# delete diff image
|
59
|
+
discard @router.diff_dir(image.name)
|
59
60
|
|
60
|
-
@router.
|
61
|
+
image.thumbnail!(@router.thumbnail_dir) if @config.thumbnail?
|
62
|
+
image.path
|
61
63
|
end
|
62
64
|
|
63
65
|
private
|
64
|
-
|
65
|
-
|
66
|
+
# returns a new image for the moved image
|
67
|
+
def rebase(image)
|
68
|
+
new_path = @router.baseline_dir(image.name)
|
69
|
+
|
70
|
+
create_path_for_file(new_path)
|
71
|
+
FileUtils.move(image.path, new_path)
|
72
|
+
|
73
|
+
Monet::Image.new(new_path)
|
74
|
+
end
|
75
|
+
|
76
|
+
def create_path_for_file(file)
|
77
|
+
path = File.dirname(file)
|
78
|
+
FileUtils.mkpath path unless Dir.exists? path
|
66
79
|
end
|
67
80
|
end
|
68
81
|
end
|
data/lib/monet/capture.rb
CHANGED
@@ -4,8 +4,8 @@ require "capybara"
|
|
4
4
|
require 'capybara/poltergeist'
|
5
5
|
require "capybara/dsl"
|
6
6
|
|
7
|
-
require 'monet/
|
8
|
-
require 'monet/
|
7
|
+
require 'monet/router'
|
8
|
+
require 'monet/image'
|
9
9
|
|
10
10
|
module Monet
|
11
11
|
class Capture
|
@@ -14,45 +14,46 @@ module Monet
|
|
14
14
|
MAX_HEIGHT = 10000
|
15
15
|
|
16
16
|
def initialize(config)
|
17
|
-
@config =
|
18
|
-
@router = Monet::
|
17
|
+
@config = config
|
18
|
+
@router = Monet::Router.new config
|
19
19
|
|
20
20
|
Capybara.default_driver = @config.driver
|
21
21
|
end
|
22
22
|
|
23
23
|
def capture_all
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
28
|
-
|
29
|
-
def capture(path)
|
30
|
-
visit @router.build_url(path)
|
31
|
-
|
32
|
-
@config.dimensions.each do |width|
|
33
|
-
file_path = @router.route_url_path(path, width)
|
24
|
+
images = []
|
25
|
+
@router.capture_routes.map do |url, paths|
|
26
|
+
visit_once url
|
34
27
|
|
35
|
-
|
36
|
-
|
28
|
+
paths.each do |path|
|
29
|
+
image = Monet::Image.new path
|
30
|
+
capture(url, image)
|
37
31
|
|
38
|
-
|
32
|
+
images << image
|
33
|
+
end
|
39
34
|
end
|
35
|
+
|
36
|
+
images
|
40
37
|
end
|
41
38
|
|
42
|
-
def
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
39
|
+
def capture(url, image_or_save_path)
|
40
|
+
if image_or_save_path.is_a? String
|
41
|
+
image = Monet::Image.new image_or_save_path
|
42
|
+
else
|
43
|
+
image = image_or_save_path
|
44
|
+
end
|
47
45
|
|
48
|
-
|
46
|
+
visit_once url
|
49
47
|
|
50
|
-
|
51
|
-
|
48
|
+
page.driver.resize(image.width, MAX_HEIGHT)
|
49
|
+
page.driver.render(image.path, full: true)
|
52
50
|
|
53
|
-
|
51
|
+
image.thumbnail!(@router.thumbnail_dir) if @config.thumbnail?
|
52
|
+
end
|
54
53
|
|
55
|
-
|
54
|
+
private
|
55
|
+
def visit_once(url)
|
56
|
+
visit url unless current_url == url
|
56
57
|
end
|
57
58
|
end
|
58
59
|
end
|
data/lib/monet/capture_map.rb
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
require 'spidr'
|
2
2
|
require 'monet/url_helpers'
|
3
|
+
require 'forwardable'
|
3
4
|
|
4
5
|
module Monet
|
5
6
|
class CaptureMap
|
6
7
|
extend Forwardable
|
7
8
|
|
8
9
|
class PathCollection
|
10
|
+
extend Forwardable
|
11
|
+
|
9
12
|
include URLHelpers
|
10
13
|
attr_reader :paths, :root_url
|
11
14
|
|
@@ -25,6 +28,8 @@ module Monet
|
|
25
28
|
def normalized_path(path)
|
26
29
|
path.chomp "/"
|
27
30
|
end
|
31
|
+
|
32
|
+
def_delegators :paths, :size, :length, :count
|
28
33
|
end
|
29
34
|
|
30
35
|
class PathSpider < PathCollection
|
@@ -32,7 +37,10 @@ module Monet
|
|
32
37
|
SKIP_PATHS = [/\?.*/]
|
33
38
|
|
34
39
|
def paths
|
35
|
-
@paths
|
40
|
+
return @paths unless @paths.empty?
|
41
|
+
|
42
|
+
normalize Spidr.site(@root_url, ignore_links: ignores)
|
43
|
+
@paths.uniq!
|
36
44
|
end
|
37
45
|
|
38
46
|
def ignores
|
@@ -41,7 +49,7 @@ module Monet
|
|
41
49
|
|
42
50
|
private
|
43
51
|
def normalize(spider_results)
|
44
|
-
spider_results.history.
|
52
|
+
spider_results.history.each {|path| add(path.path) }
|
45
53
|
end
|
46
54
|
end
|
47
55
|
|
@@ -54,7 +62,7 @@ module Monet
|
|
54
62
|
yield(@path_helper) if block_given?
|
55
63
|
end
|
56
64
|
|
57
|
-
def_delegators :@path_helper, :paths, :paths=, :add, :root_url
|
65
|
+
def_delegators :@path_helper, :paths, :paths=, :add, :root_url, :size, :count, :length
|
58
66
|
|
59
67
|
def type_mapper
|
60
68
|
case @type
|
data/lib/monet/changeset.rb
CHANGED
data/lib/monet/config.rb
CHANGED
@@ -21,29 +21,29 @@ module Monet
|
|
21
21
|
|
22
22
|
attr_accessor *DEFAULT_OPTIONS.keys
|
23
23
|
|
24
|
+
# configure via options
|
24
25
|
def initialize(opts={})
|
25
26
|
DEFAULT_OPTIONS.each do |opt, default|
|
26
27
|
send "#{opt}=", opts[opt] || default
|
27
28
|
end
|
28
29
|
end
|
29
30
|
|
31
|
+
# configure via block
|
30
32
|
def self.config(&block)
|
31
33
|
cfg = new
|
32
|
-
|
34
|
+
yield cfg if block_given?
|
35
|
+
|
33
36
|
cfg
|
34
37
|
end
|
35
38
|
|
39
|
+
# configure via YAML
|
36
40
|
def self.load(path="./config.yaml")
|
37
|
-
|
38
|
-
new(
|
39
|
-
end
|
40
|
-
|
41
|
-
def self.build_config(opts)
|
42
|
-
(opts.is_a? Monet::Config) ? opts : new(opts)
|
41
|
+
opts = YAML::load(File.open(path))
|
42
|
+
new(opts)
|
43
43
|
end
|
44
44
|
|
45
45
|
def base_url=(url)
|
46
|
-
@base_url
|
46
|
+
@base_url = parse_uri(url) unless url.nil?
|
47
47
|
end
|
48
48
|
|
49
49
|
def base_url
|
@@ -51,6 +51,12 @@ module Monet
|
|
51
51
|
@base_url
|
52
52
|
end
|
53
53
|
|
54
|
+
def site
|
55
|
+
parse_uri(base_url).host
|
56
|
+
end
|
57
|
+
alias_method :host, :site
|
58
|
+
alias_method :root_dir, :site
|
59
|
+
|
54
60
|
def thumbnail?
|
55
61
|
!!thumbnail
|
56
62
|
end
|
@@ -68,13 +74,14 @@ module Monet
|
|
68
74
|
end
|
69
75
|
|
70
76
|
def map=(paths)
|
71
|
-
map
|
77
|
+
@map = nil
|
78
|
+
return @map if paths.nil?
|
79
|
+
paths == :spider ? map(paths) : (map.paths = paths)
|
72
80
|
end
|
73
81
|
|
74
82
|
def map(type=:explicit, &block)
|
75
83
|
@map ||= CaptureMap.new(base_url, type)
|
76
|
-
|
77
|
-
block.call(@map) if block_given? && type == :explicit
|
84
|
+
yield @map if block_given?
|
78
85
|
|
79
86
|
@map
|
80
87
|
end
|
data/lib/monet/image.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'chunky_png'
|
2
|
+
require 'monet/config'
|
3
|
+
require 'monet/url_helpers'
|
4
|
+
|
5
|
+
module Monet
|
6
|
+
class Image
|
7
|
+
include URLHelpers
|
8
|
+
|
9
|
+
attr_reader :path
|
10
|
+
|
11
|
+
def initialize(path)
|
12
|
+
@path = File.expand_path path
|
13
|
+
end
|
14
|
+
|
15
|
+
def is_diff?
|
16
|
+
name.include? "-diff"
|
17
|
+
end
|
18
|
+
|
19
|
+
def diff_path
|
20
|
+
@path.gsub(".png", "-diff.png")
|
21
|
+
end
|
22
|
+
|
23
|
+
def flagged?
|
24
|
+
File.exists? diff_path
|
25
|
+
end
|
26
|
+
|
27
|
+
def width
|
28
|
+
@width ||= File.basename(@path, ".png").split("-").last.to_i
|
29
|
+
end
|
30
|
+
|
31
|
+
def name
|
32
|
+
@name ||= File.basename @path
|
33
|
+
end
|
34
|
+
|
35
|
+
def basename
|
36
|
+
@path.split(File::SEPARATOR)[-2..-1].join(File::SEPARATOR)
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_image
|
40
|
+
ChunkyPNG::Image.from_file(@path)
|
41
|
+
end
|
42
|
+
|
43
|
+
def root_dir
|
44
|
+
File.dirname @path
|
45
|
+
end
|
46
|
+
|
47
|
+
def thumbnail!(save_to=nil)
|
48
|
+
img = to_image
|
49
|
+
short_edge = [img.width, img.height].min
|
50
|
+
|
51
|
+
cropped = img.crop(0, 0, short_edge, short_edge)
|
52
|
+
resized = cropped.resize(200, 200)
|
53
|
+
|
54
|
+
if save_to.nil?
|
55
|
+
save_to = @path.gsub(".png", "-thumb.png")
|
56
|
+
else
|
57
|
+
save_to = File.join save_to, File.basename(@path)
|
58
|
+
end
|
59
|
+
|
60
|
+
save_dir = File.dirname(save_to)
|
61
|
+
FileUtils.mkdir_p save_dir unless Dir.exists?(save_dir)
|
62
|
+
|
63
|
+
resized.save save_to
|
64
|
+
save_to
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/monet/router.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
module Monet
|
2
|
+
class Router
|
3
|
+
include URLHelpers
|
4
|
+
TYPES = [
|
5
|
+
:baseline,
|
6
|
+
:capture,
|
7
|
+
:thumbnail
|
8
|
+
]
|
9
|
+
|
10
|
+
def initialize(config)
|
11
|
+
@config = config
|
12
|
+
end
|
13
|
+
|
14
|
+
TYPES.each do |type|
|
15
|
+
define_method "#{type}_dir" do |filename=""|
|
16
|
+
File.join(@config.send("#{type}_dir"), @config.site, filename).chomp("/")
|
17
|
+
end
|
18
|
+
|
19
|
+
define_method "#{type}_url" do |filename|
|
20
|
+
type_dir = @config.send "#{type}_dir"
|
21
|
+
image_url(type_dir, filename)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def original_url(path)
|
26
|
+
url = path.split("/").last
|
27
|
+
path = url.split('|')[1..-1].join("/").gsub(/-\d+\.png/, "")
|
28
|
+
|
29
|
+
"#{@config.base_url}/#{path}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def diff_dir(filename="")
|
33
|
+
filename = filename.gsub(".png", "-diff.png") unless filename.empty?
|
34
|
+
File.join(@config.baseline_dir, @config.site, filename).chomp("/")
|
35
|
+
end
|
36
|
+
|
37
|
+
def diff_url(path)
|
38
|
+
diff = basename diff_dir(path)
|
39
|
+
image_url(@config.baseline_dir, diff)
|
40
|
+
end
|
41
|
+
|
42
|
+
def capture_routes
|
43
|
+
urls = {}
|
44
|
+
@config.map.paths.each do |path|
|
45
|
+
url = "#{@config.base_url}#{path}".chomp("/")
|
46
|
+
@config.dimensions.each do |width|
|
47
|
+
urls[url] ||= []
|
48
|
+
urls[url] << url_to_filepath(url, width)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
urls
|
53
|
+
end
|
54
|
+
|
55
|
+
def url_to_filepath(url, width="*")
|
56
|
+
uri = parse_uri(url)
|
57
|
+
name = File.join @config.site, "#{@config.site}#{uri.path.gsub(/\//, '|')}"
|
58
|
+
File.join @config.capture_dir, "#{name}-#{width}.png"
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
def basename(path)
|
63
|
+
File.basename path
|
64
|
+
end
|
65
|
+
|
66
|
+
def image_url(type_dir, name)
|
67
|
+
relative_path = Pathname.new(type_dir).relative_path_from(Pathname.new(File.join(type_dir, "..")))
|
68
|
+
"/#{relative_path}/#{@config.root_dir}/#{name}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/monet/tasks.rb
CHANGED
@@ -12,27 +12,30 @@ end
|
|
12
12
|
namespace :monet do
|
13
13
|
desc "clean out the baseline directory"
|
14
14
|
task :clean, :path do |t, args|
|
15
|
-
|
15
|
+
config = load_config(args)
|
16
|
+
Dir.glob(File.join(config.baseline_dir, "**", "*.png")).each do |img|
|
17
|
+
File.delete img
|
18
|
+
end
|
16
19
|
end
|
17
20
|
|
18
21
|
desc "Runs the site and grabs baselines"
|
19
22
|
task :baseline => [:clean, :run] do
|
20
23
|
end
|
21
24
|
|
22
|
-
|
23
25
|
namespace :thumbnail do
|
24
26
|
desc "Thumbnail all baseline images"
|
25
|
-
task :baseline do
|
27
|
+
task :baseline, :path do |t, args|
|
26
28
|
config = load_config(args)
|
27
29
|
capture = Monet::Capture.new config
|
28
30
|
|
29
31
|
images_from_dir(config.baseline_dir).each do |image|
|
30
|
-
|
32
|
+
Monet::Image.new(image).thumbnail! config.thumbnail_dir
|
31
33
|
end
|
32
34
|
end
|
33
35
|
|
34
36
|
desc "Thumnail all captured images"
|
35
|
-
task :captures do
|
37
|
+
task :captures, :path do |t, args|
|
38
|
+
config = load_config(args)
|
36
39
|
capture = Monet::Capture.new config
|
37
40
|
|
38
41
|
images_from_dir(config.capture_dir).each do |image|
|
@@ -44,7 +47,7 @@ namespace :monet do
|
|
44
47
|
desc "Run the baseline comparison"
|
45
48
|
task :run, :path do |t, args|
|
46
49
|
config = load_config(args)
|
47
|
-
Monet.
|
48
|
-
Monet.
|
50
|
+
Monet::Capture.new(config).capture_all
|
51
|
+
Monet::BaselineControl.new(config).run
|
49
52
|
end
|
50
53
|
end
|
data/lib/monet/version.rb
CHANGED
data/lib/monet.rb
CHANGED
@@ -1,34 +1,9 @@
|
|
1
1
|
require "monet/version"
|
2
2
|
require 'monet/config'
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require 'monet/capture'
|
4
|
+
require 'monet/baseline_control'
|
5
|
+
require 'monet/router'
|
6
|
+
require 'monet/image'
|
6
7
|
|
7
8
|
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
|
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 config(&block)
|
27
|
-
Monet::Config.config block
|
28
|
-
end
|
29
|
-
|
30
|
-
def load_config(options)
|
31
|
-
Monet::Config.build_config(options)
|
32
|
-
end
|
33
|
-
end
|
34
9
|
end
|
data/spec/capture_map_spec.rb
CHANGED
@@ -59,57 +59,12 @@ describe Monet::CaptureMap do
|
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
-
context "spider mapper", vcr: { cassette_name: "spider", record: :new_episodes } do
|
63
|
-
Given(:map) { Monet::CaptureMap.new("http://
|
62
|
+
context "spider mapper", vcr: { cassette_name: "spider-map", record: :new_episodes } do
|
63
|
+
Given(:map) { Monet::CaptureMap.new("http://staging.lance.com", :spider) }
|
64
64
|
When(:paths) { map.paths }
|
65
|
-
Then { paths.should ==
|
66
|
-
|
67
|
-
|
68
|
-
"http://www.spider.io/viewability/",
|
69
|
-
"http://www.spider.io/press/",
|
70
|
-
"http://www.spider.io/team/",
|
71
|
-
"http://www.spider.io/blog/",
|
72
|
-
"http://www.spider.io/",
|
73
|
-
"http://www.spider.io/blog/2013/03/chameleon-botnet/",
|
74
|
-
"http://www.spider.io/blog/2013/12/cyber-criminals-defraud-display-advertisers-with-tdss/",
|
75
|
-
"http://www.spider.io/zeus",
|
76
|
-
"http://www.spider.io/blog/2013/11/how-to-defraud-display-advertisers-with-zeus/",
|
77
|
-
"http://www.spider.io/blog/2013/05/a-botnet-primer-for-display-advertisers/",
|
78
|
-
"http://www.spider.io/blog/2013/09/display-advertisers-funding-cybercriminals-since-2011/",
|
79
|
-
"http://www.spider.io/blog/2013/09/securing-the-legitimacy-of-display-ad-inventory/",
|
80
|
-
"http://www.spider.io/blog/2013/04/display-advertising-fraud-is-a-sell-side-problem/",
|
81
|
-
"http://www.spider.io/blog/2012/12/internet-explorer-data-leakage/",
|
82
|
-
"http://www.spider.io/blog/2013/08/sambreel-is-still-injecting-ads-video-advertisers-beware/",
|
83
|
-
"http://www.spider.io/blog/page/2/",
|
84
|
-
"http://www.spider.io/blog/2011/10/the-problem-with-client-side-analytics/",
|
85
|
-
"http://www.spider.io/blog/2012/12/responsible-disclosure/",
|
86
|
-
"http://www.spider.io/blog/2013/05/spider-io-granted-mrc-accreditation-for-viewable-impression-measurement/",
|
87
|
-
"http://www.spider.io/blog/2013/04/at-least-two-percent-of-monitored-display-ad-exchange-inventory-is-hidden/",
|
88
|
-
"http://www.spider.io/blog/2013/03/who-is-behind-the-chameleon-botnet/",
|
89
|
-
"http://www.spider.io/blog/page/3/",
|
90
|
-
"http://www.spider.io/blog/2013/02/which-display-ad-exchange-sells-the-highest-quality-inventory/",
|
91
|
-
"http://www.spider.io/blog/2012/12/there-are-two-ways-to-measure-ad-viewability-there-is-only-one-right-way/",
|
92
|
-
"http://www.spider.io/blog/page/4/",
|
93
|
-
"http://www.spider.io/blog/2012/12/review-of-iab-safeframe-1-0/",
|
94
|
-
"http://www.spider.io/blog/2012/10/qa-about-ad-viewability/",
|
95
|
-
"http://www.spider.io/blog/2012/10/the-first-technology-to-measure-the-viewability-of-iframed-ads-across-all-major-browsers-press-release/",
|
96
|
-
"http://www.spider.io/vSta98h",
|
97
|
-
"http://www.spider.io/blog/2012/07/join-us-for-a-tipple-at-spider-towers/",
|
98
|
-
"http://www.spider.io/blog/2012/07/startups-acquiring-startups-for-equity/",
|
99
|
-
"http://www.spider.io/blog/page/5/",
|
100
|
-
"http://www.spider.io/visibility-demo-screencast/",
|
101
|
-
"http://www.spider.io/blog/2012/07/whats-in-an-ip-address/",
|
102
|
-
"http://www.spider.io/blog/2011/12/physical-hack-day/",
|
103
|
-
"http://www.spider.io/blog/2011/11/our-first-hack-day/",
|
104
|
-
"http://www.spider.io/careers/",
|
105
|
-
"http://www.spider.io/blog/2011/10/extreme-architecting/",
|
106
|
-
"http://www.spider.io/blog/2011/09/how-to-catch-a-bot/",
|
107
|
-
"http://www.spider.io/blog/page/6/",
|
108
|
-
"http://www.spider.io/blog/2011/10/testing-javascript-with-mturk/",
|
109
|
-
"http://www.spider.io/blog/2011/10/demonstration-screencast-verifying-that-display-ads-are-visible-from-within-iframes/",
|
110
|
-
"http://www.spider.io/blog/2011/10/calling-out-to-researchersacademics/",
|
111
|
-
"http://www.spider.io/blog/page/7/"
|
112
|
-
]}
|
65
|
+
Then { paths.size.should == 64 }
|
66
|
+
And { paths.first.end_with?("/").should be false }
|
67
|
+
And { paths.first.start_with?("http://").should be_false }
|
113
68
|
And { paths.any? {|p| p.end_with? "css" }.should be_false }
|
114
69
|
end
|
115
70
|
end
|