blinkr 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 +4 -4
- data/README.md +66 -0
- data/lib/blinkr.rb +10 -6
- data/lib/blinkr/config.rb +46 -0
- data/lib/blinkr/engine.rb +142 -0
- data/lib/blinkr/extensions/a_title.rb +17 -0
- data/lib/blinkr/extensions/empty_a_href.rb +19 -0
- data/lib/blinkr/extensions/img_alt.rb +17 -0
- data/lib/blinkr/extensions/inline_css.rb +21 -0
- data/lib/blinkr/extensions/javascript.rb +17 -0
- data/lib/blinkr/extensions/links.rb +53 -0
- data/lib/blinkr/extensions/meta.html.slim +66 -0
- data/lib/blinkr/extensions/meta.rb +84 -0
- data/lib/blinkr/extensions/pipeline.rb +39 -0
- data/lib/blinkr/extensions/resources.rb +20 -0
- data/lib/blinkr/http_utils.rb +49 -0
- data/lib/blinkr/phantomjs_wrapper.rb +65 -0
- data/lib/blinkr/report.html.slim +216 -127
- data/lib/blinkr/report.rb +35 -3
- data/lib/blinkr/sitemap.rb +20 -0
- data/lib/blinkr/snap.js +2 -0
- data/lib/blinkr/typhoeus_wrapper.rb +98 -0
- data/lib/blinkr/version.rb +1 -1
- metadata +18 -3
- data/lib/blinkr/check.rb +0 -231
@@ -0,0 +1,66 @@
|
|
1
|
+
.h2
|
2
|
+
| Duplicate Descriptions
|
3
|
+
.label.label-primary.pull-right = descriptions.length
|
4
|
+
- descriptions.each_with_index do |(description, pages), index|
|
5
|
+
|
6
|
+
.panel.panel-danger
|
7
|
+
.panel-heading
|
8
|
+
a> data-toggle="collapse" href="#meta-d-#{index}" id="control-meta-d-#{index}"
|
9
|
+
i.fa.fa-caret-square-o-right>
|
10
|
+
' #{description}
|
11
|
+
.panel-body.collapse id="meta-d-#{index}"
|
12
|
+
ul.list-group
|
13
|
+
- pages.each do |url, page|
|
14
|
+
li.list-group-item
|
15
|
+
' #{url}
|
16
|
+
a> href="#{url}" target='_blank'
|
17
|
+
i.fa.fa-external-link
|
18
|
+
a href="view-source:#{url}" target='_blank'
|
19
|
+
i.fa.fa-file-code-o
|
20
|
+
javascript:
|
21
|
+
$('#meta-d-#{index}').on('show.bs.collapse', function () {
|
22
|
+
$('a#control-meta-d-#{index}').html('<i class="fa fa-caret-square-o-down"></i>');
|
23
|
+
});
|
24
|
+
$('#meta-d-#{index}').on('hide.bs.collapse', function () {
|
25
|
+
$('a#control-meta-d-#{index}').html('<i class="fa fa-caret-square-o-right"></i>');
|
26
|
+
});
|
27
|
+
|
28
|
+
- if descriptions.length == 0
|
29
|
+
.panel.panel-success
|
30
|
+
.panel-heading
|
31
|
+
a id='resources'
|
32
|
+
| No duplicate descriptions detected
|
33
|
+
|
34
|
+
.h2
|
35
|
+
| Duplicate Titles
|
36
|
+
.label.label-primary.pull-right = titles.length
|
37
|
+
- titles.each_with_index do |(title, pages), index|
|
38
|
+
|
39
|
+
.panel.panel-danger
|
40
|
+
.panel-heading
|
41
|
+
a> data-toggle="collapse" href="#meta-t-#{index}" id="control-meta-t-#{index}"
|
42
|
+
i.fa.fa-caret-square-o-right>
|
43
|
+
' #{title}
|
44
|
+
.panel-body.collapse id="meta-t-#{index}"
|
45
|
+
ul.list-group
|
46
|
+
- pages.each do |url, page|
|
47
|
+
li.list-group-item
|
48
|
+
' #{url}
|
49
|
+
a> href="#{url}" target='_blank'
|
50
|
+
i.fa.fa-external-link
|
51
|
+
a href="view-source:#{url}" target='_blank'
|
52
|
+
i.fa.fa-file-code-o
|
53
|
+
javascript:
|
54
|
+
$('#meta-t-#{index}').on('show.bs.collapse', function () {
|
55
|
+
$('a#control-meta-t-#{index}').html('<i class="fa fa-caret-square-o-down"></i>');
|
56
|
+
});
|
57
|
+
$('#meta-t-#{index}').on('hide.bs.collapse', function () {
|
58
|
+
$('a#control-meta-t-#{index}').html('<i class="fa fa-caret-square-o-right"></i>');
|
59
|
+
});
|
60
|
+
|
61
|
+
- if titles.length == 0
|
62
|
+
.panel.panel-success
|
63
|
+
.panel-heading
|
64
|
+
a id='resources'
|
65
|
+
| No duplicate titles detected
|
66
|
+
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'slim'
|
3
|
+
|
4
|
+
module Blinkr
|
5
|
+
module Extensions
|
6
|
+
class Meta
|
7
|
+
|
8
|
+
TMPL = File.expand_path('meta.html.slim', File.dirname(__FILE__))
|
9
|
+
|
10
|
+
def initialize config
|
11
|
+
@config = config
|
12
|
+
@descriptions = {}
|
13
|
+
@titles = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def collect page
|
17
|
+
description page
|
18
|
+
title page
|
19
|
+
end
|
20
|
+
|
21
|
+
def analyze context, typhoeus
|
22
|
+
context.duplicate_descriptions = @descriptions.reject{ |description, pages| pages.length <= 1 }
|
23
|
+
context.duplicate_titles = @titles.reject{ |title, pages| pages.length <= 1 }
|
24
|
+
end
|
25
|
+
|
26
|
+
def append context
|
27
|
+
Slim::Template.new(TMPL).render(OpenStruct.new({ :descriptions => context.duplicate_descriptions, :titles => context.duplicate_titles }))
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def title page
|
33
|
+
elms = page.body.css('title')
|
34
|
+
if elms.length > 1
|
35
|
+
snippets = []
|
36
|
+
lines = []
|
37
|
+
elms.each do |elm|
|
38
|
+
lines << elm.line
|
39
|
+
snippets << elm.to_s
|
40
|
+
end
|
41
|
+
page.errors << OpenStruct.new({ :severity => 'info', :category => 'HTML Compatibility/Correctness', :type => '<title> tag declared more than once', :title => %Q{<title> declared more than once (lines #{lines.join(', ')})}, :message => %Q{<title> declared more than onc}, :snippet => snippets.join('\n'), :icon => 'fa-header' })
|
42
|
+
elsif elms.empty?
|
43
|
+
page.errors << OpenStruct.new({ :severity => 'warning', :category => 'SEO', :type => '<title> tag missing', :title => %Q{<title> tag missing}, :message => %Q{<title> tag missing}, :icon => 'fa-header' })
|
44
|
+
else
|
45
|
+
title = elms.first.text
|
46
|
+
@titles[title] ||= {}
|
47
|
+
@titles[title][page.response.effective_url] = page
|
48
|
+
if title.length < 20
|
49
|
+
page.errors << OpenStruct.new({ :severity => 'info', :category => 'SEO', :type => 'page title too short', :title => %Q{<title> too short (line #{elms.first.line})}, :message => %Q{<title> too short (< 20 characters)}, :snippet => elms.first.to_s, :icon => 'fa-header' })
|
50
|
+
end
|
51
|
+
if title.length > 55
|
52
|
+
page.errors << OpenStruct.new({ :severity => 'info', :category => 'SEO', :type => 'page title too long', :title => %Q{<title> too long (line #{elms.first.line})}, :message => %Q{<title> too long (> 55 characters)}, :snippet => elms.first.to_s, :icon => 'fa-header' })
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def description page
|
58
|
+
elms = page.body.css('meta[name=description]')
|
59
|
+
if elms.length > 1
|
60
|
+
snippets = []
|
61
|
+
lines = []
|
62
|
+
elms.each do |elm|
|
63
|
+
lines << elm.line
|
64
|
+
snippets << elm.to_s
|
65
|
+
end
|
66
|
+
page.errors << OpenStruct.new({ :severity => 'info', :category => 'HTML Compatibility/Correctness', :type => '<meta name="description"> tag declared more than once', :title => %Q{<meta name="description"> tag declared more than once (lines #{lines.join(', ')})}, :message => %Q{<meta name="description"> tag declared more than once}, :snippet => snippets.join('\n'), :icon => 'fa-header' })
|
67
|
+
elsif elms.empty?
|
68
|
+
page.errors << OpenStruct.new({ :severity => 'info', :category => 'SEO', :type => '<meta name="description"> tag missing', :title => %Q{<meta name="description"> tag missing}, :message => %Q{<meta name="description"> tag missing}, :icon => 'fa-header' })
|
69
|
+
else
|
70
|
+
desc = elms.first['content']
|
71
|
+
@descriptions[desc] ||= {}
|
72
|
+
@descriptions[desc][page.response.effective_url] = page
|
73
|
+
if desc.length < 60
|
74
|
+
page.errors << OpenStruct.new({ :severity => 'info', :category => 'SEO', :type => '<meta name="description"> too short', :title => %Q{<meta name="description"> too short (lines #{elms.first.line})}, :message => %Q{<meta name="description"> too short (< 60 characters)}, :snippet => elms.first.to_s, :icon => 'fa-header' })
|
75
|
+
end
|
76
|
+
if desc.length > 115
|
77
|
+
page.errors << OpenStruct.new({ :severity => 'info', :category => 'SEO', :type => '<meta name="description"> too long', :title => %Q{<meta name="description"> too long (lines #{elms.first.line})}, :message => %Q{<meta name="description"> too long (> 115 characters)}, :snippet => elms.first.to_s, :icon => 'fa-header' })
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
Dir[ File.join( File.dirname(__FILE__), '*.rb' ) ].each do |f|
|
2
|
+
begin
|
3
|
+
require f
|
4
|
+
rescue LoadError => e
|
5
|
+
$LOG.warn "Missing required dependency to activate optional built-in extension #{File.basename(f)}\n #{e}" if $LOG.debug?
|
6
|
+
rescue StandardError => e
|
7
|
+
$LOG.warn "Missing runtime configuration to activate optional built-in extension #{File.basename(f)}\n #{e}" if $LOG.debug?
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module Blinkr
|
12
|
+
module Extensions
|
13
|
+
class Pipeline
|
14
|
+
|
15
|
+
attr_reader :extensions
|
16
|
+
|
17
|
+
def initialize(&block)
|
18
|
+
@extensions = []
|
19
|
+
@block = block
|
20
|
+
end
|
21
|
+
|
22
|
+
def load config
|
23
|
+
begin
|
24
|
+
instance_exec config, &@block if @block && @block.arity == 1
|
25
|
+
instance_exec &@block if @block && @block.arity == 0
|
26
|
+
self
|
27
|
+
rescue Exception => e
|
28
|
+
abort("Failed to initialize pipeline: #{e}")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def extension(ext)
|
33
|
+
@extensions << ext
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Blinkr
|
2
|
+
module Extensions
|
3
|
+
class Resources
|
4
|
+
|
5
|
+
def initialize config
|
6
|
+
@config = config
|
7
|
+
end
|
8
|
+
|
9
|
+
def collect page
|
10
|
+
page.resource_errors.each do |error|
|
11
|
+
start = error['errorString'].rindex('server replied: ')
|
12
|
+
message = error['errorString'].slice(start.nil? ? 0 : start + 16, error['errorString'].length) unless error['errorString'].nil?
|
13
|
+
code = error['errorCode'].nil? ? nil : error['errorCode'].to_i
|
14
|
+
page.errors << OpenStruct.new({ :severity => 'danger', :category => 'Resources missing', :type => 'Resource loading error', :title => error['url'], :code => code, :message => message, :icon => 'fa-file-image-o' })
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module Blinkr
|
4
|
+
module HttpUtils
|
5
|
+
|
6
|
+
def sanitize dest, src
|
7
|
+
# src is the page that tried to load the URL
|
8
|
+
# URI fails to handle #! style fragments, so we chomp them
|
9
|
+
src = src[0, src.rindex('#!')] unless src.rindex('#!').nil?
|
10
|
+
src_uri = URI(src)
|
11
|
+
|
12
|
+
# Remove the query and fragment from the SRC URI, as we are going to use it to resolve the relative dest URIs
|
13
|
+
src_uri.query = nil
|
14
|
+
src_uri.fragment = nil
|
15
|
+
|
16
|
+
# base is the web root of the site
|
17
|
+
base_uri = URI(@config.base_url)
|
18
|
+
|
19
|
+
begin
|
20
|
+
|
21
|
+
# dest is the URL we are trying to load
|
22
|
+
dest_uri = URI(dest)
|
23
|
+
dest_uri.fragment = nil if @config.ignore_fragments
|
24
|
+
|
25
|
+
# If we have a relative URI, or just a fragment, join what we have to our base URL
|
26
|
+
dest_uri = URI.join(src_uri, dest) if ( empty?(dest_uri.path) && !empty?(dest_uri.fragment ) ) || dest_uri.relative?
|
27
|
+
|
28
|
+
# If we have an absolute path URI, join it to the base URL
|
29
|
+
dest_uri = URI.join(base_uri.scheme, base_uri.hostname, base_uri.port, dest_uri) if empty?(dest_uri.scheme) && empty?(dest_uri.hostname)
|
30
|
+
|
31
|
+
dest = dest_uri.to_s
|
32
|
+
rescue URI::InvalidURIError, URI::InvalidComponentError, URI::BadURIError
|
33
|
+
end
|
34
|
+
dest.chomp('#').chomp('index.html')
|
35
|
+
end
|
36
|
+
|
37
|
+
def retry? resp
|
38
|
+
resp.timed_out? || (resp.code == 0 && [ "Server returned nothing (no headers, no data)", "SSL connect error", "Failure when receiving data from the peer" ].include?(resp.return_message) )
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def empty? str
|
44
|
+
str.nil? || str.empty?
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'typhoeus'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'tempfile'
|
4
|
+
require 'blinkr/http_utils'
|
5
|
+
require 'parallel'
|
6
|
+
|
7
|
+
|
8
|
+
module Blinkr
|
9
|
+
class PhantomJSWrapper
|
10
|
+
include HttpUtils
|
11
|
+
|
12
|
+
SNAP_JS = File.expand_path('snap.js', File.dirname(__FILE__))
|
13
|
+
|
14
|
+
attr_reader :count
|
15
|
+
|
16
|
+
def initialize config, context
|
17
|
+
@config = config.validate
|
18
|
+
@context = context
|
19
|
+
@count = 0
|
20
|
+
end
|
21
|
+
|
22
|
+
def process_all urls, limit, &block
|
23
|
+
Parallel.each(urls, :in_threads => @config.phantomjs_threads) do |url|
|
24
|
+
process url, limit, &block
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def process url, limit, &block
|
29
|
+
_process url, limit, limit, &block
|
30
|
+
end
|
31
|
+
|
32
|
+
def name
|
33
|
+
'phantomjs'
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def _process url, limit, max, &block
|
39
|
+
unless @config.skipped? url
|
40
|
+
Tempfile.open('blinkr') do|f|
|
41
|
+
if system "phantomjs #{SNAP_JS} #{url} #{@config.viewport} #{f.path}"
|
42
|
+
json = JSON.load(File.read(f.path))
|
43
|
+
response = Typhoeus::Response.new(code: 200, body: json['content'], effective_url: json['url'], mock: true)
|
44
|
+
response.request = Typhoeus::Request.new(url)
|
45
|
+
Typhoeus.stub(url).and_return(response)
|
46
|
+
block.call response, json['resourceErrors'], json['javascriptErrors']
|
47
|
+
else
|
48
|
+
if limit > 1
|
49
|
+
puts "Loading #{url} via phantomjs (attempt #{max - limit + 2} of #{max})" if verbose
|
50
|
+
_process url, limit - 1, max, &block
|
51
|
+
else
|
52
|
+
puts "Loading #{url} via phantomjs failed" if @config.verbose
|
53
|
+
response = Typhoeus::Response.new(code: 0, status_message: "Server timed out after #{max} retries", mock: true)
|
54
|
+
response.request = Typhoeus::Request.new(url)
|
55
|
+
Typhoeus.stub(url).and_return(response)
|
56
|
+
block.call response, nil, nil
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
@count += 1
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
data/lib/blinkr/report.html.slim
CHANGED
@@ -9,141 +9,230 @@ head
|
|
9
9
|
body
|
10
10
|
|
11
11
|
.container
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
a href='#links'
|
16
|
-
p class="#{errors.links.empty? ? 'text-success' : 'text-danger'}"
|
17
|
-
|Broken links
|
18
|
-
- unless errors.javascript.nil?
|
19
|
-
li
|
20
|
-
a href='#javascript'
|
21
|
-
p class="#{errors.javascript.empty? ? 'text-success' : 'text-danger'}"
|
22
|
-
| Page load JavaScript errors
|
23
|
-
- unless errors.resources.nil?
|
24
|
-
li
|
25
|
-
a href='#resources'
|
26
|
-
p class="#{errors.resources.empty? ? 'text-success' : 'text-danger'}"
|
27
|
-
| Resource loading errors
|
12
|
+
.row
|
13
|
+
h1 Blinkr
|
14
|
+
p.lead Blackbox testing for websites
|
28
15
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|a[href]
|
34
|
-
|)
|
35
|
-
- errors.links.each do |url, err|
|
36
|
-
.panel.panel-danger
|
16
|
+
h2
|
17
|
+
| Pages
|
18
|
+
.label.label-primary.pull-right = blinkr.error_count
|
19
|
+
.panel.panel-default
|
37
20
|
.panel-heading
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
21
|
+
h4
|
22
|
+
a> data-toggle="collapse" href="#filters" id="control-filters"
|
23
|
+
i.fa.fa-caret-square-o-down
|
24
|
+
| Filters
|
25
|
+
.panel-collapse.collapse.in#filters
|
26
|
+
.panel-body
|
27
|
+
form.row
|
28
|
+
.col-md-4#severities
|
29
|
+
.panel.panel-default
|
30
|
+
.panel-heading
|
31
|
+
.checkbox
|
32
|
+
label
|
33
|
+
input.severities type="checkbox" value="severities" onchange="severities()" checked="true"
|
34
|
+
| Severity
|
35
|
+
.label.label-primary< = blinkr.error_count
|
36
|
+
.panel-body
|
37
|
+
- blinkr.severities.each do |severity, metadata|
|
38
|
+
.checkbox
|
39
|
+
label
|
40
|
+
input.severity type="checkbox" value="severity-#{metadata.id}" onchange="updateSeverities(); filter()" checked="true"
|
41
|
+
| #{severity.capitalize}
|
42
|
+
div< class="label label-#{severity}" = metadata.count
|
43
|
+
.col-md-8#types style="padding-left:20px; border-left: 1px solid #ccc;"
|
44
|
+
.row
|
45
|
+
- blinkr.categories.each do |category, metadata|
|
46
|
+
.col-md-6
|
47
|
+
.panel.panel-default
|
48
|
+
.panel-heading
|
49
|
+
.checkbox
|
50
|
+
label
|
51
|
+
input.category type="checkbox" value="category-#{metadata.id}" onchange="category()" checked="true"
|
52
|
+
| #{category}
|
53
|
+
- metadata.severities.each do |severity, count|
|
54
|
+
div< class="label label-#{severity}" = count
|
55
|
+
.panel-body
|
56
|
+
- metadata.types.each do |type, type_metadata|
|
57
|
+
.checkbox
|
58
|
+
label
|
59
|
+
input.type type="checkbox" value="type-#{metadata.id}-#{type_metadata.id}" onchange="updateCategory(); filter();" checked="true" data-category="category-#{metadata.id}"
|
60
|
+
| #{type}
|
61
|
+
- type_metadata.severities.each do |severity, count|
|
62
|
+
div< class="label label-#{severity}" = count
|
63
|
+
javascript:
|
64
|
+
$('#filters').on('show.bs.collapse', function () {
|
65
|
+
$('a#control-filters').html('<i class="fa fa-caret-square-o-down"></i>');
|
66
|
+
});
|
67
|
+
$('#filters').on('hide.bs.collapse', function () {
|
68
|
+
$('a#control-filters').html('<i class="fa fa-caret-square-o-right"></i>');
|
69
|
+
});
|
70
|
+
.spinner.center-block style="padding-top: 2em"
|
71
|
+
img src="http://static.jboss.org/www/images/_images_icons_ajax-loader-8.gif"
|
72
|
+
javascript:
|
73
|
+
$( '.spinner' ).hide();
|
74
|
+
#pages
|
75
|
+
- blinkr.pages.each_with_index do |(url, page), index|
|
76
|
+
|
77
|
+
div class="page panel panel-#{page.max_severity}"
|
78
|
+
.panel-heading
|
79
|
+
a> data-toggle="collapse" href="#p-#{index}" id="control-p-#{index}"
|
80
|
+
i.fa.fa-caret-square-o-right>
|
81
|
+
' #{url}
|
82
|
+
a> href="#{url}" target='_blank'
|
59
83
|
i.fa.fa-external-link
|
60
|
-
'
|
61
|
-
a href="view-source:#{ref.src}" target='_blank'
|
84
|
+
a href="view-source:#{url}" target='_blank'
|
62
85
|
i.fa.fa-file-code-o
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
- err.messages.each do |message|
|
93
|
-
li.list-group-item
|
94
|
-
pre= message.msg
|
95
|
-
pre= message.trace
|
96
|
-
javascript:
|
97
|
-
$('#j-#{err.uid}').on('show.bs.collapse', function () {
|
98
|
-
$('a#control-j-#{err.uid}').html('<i class="fa fa-caret-square-o-down"></i>');
|
99
|
-
});
|
100
|
-
$('#j-#{err.uid}').on('hide.bs.collapse', function () {
|
101
|
-
$('a#control-j-#{err.uid}').html('<i class="fa fa-caret-square-o-right"></i>');
|
102
|
-
});
|
103
|
-
- if errors.javascript.empty?
|
104
|
-
.panel.panel-success
|
105
|
-
.panel-heading
|
106
|
-
a id='javascript'
|
107
|
-
| No JavaScript errors detected
|
108
|
-
|
109
|
-
- unless errors.resources.nil?
|
110
|
-
a id='resources'
|
111
|
-
h2 Resource loading errors
|
112
|
-
- errors.resources.each do |url, err|
|
113
|
-
.panel.panel-danger
|
114
|
-
.panel-heading
|
115
|
-
a data-toggle="collapse" href="#r-#{err.uid}" id="control-r-#{err.uid}"
|
116
|
-
i.fa.fa-caret-square-o-right
|
117
|
-
'
|
118
|
-
a href="#{url}" target='_blank'
|
119
|
-
| #{url}
|
120
|
-
i.fa.fa-external-link
|
121
|
-
.panel-body.collapse id="r-#{err.uid}"
|
122
|
-
ul.list-group
|
123
|
-
- err.messages.each do |message|
|
124
|
-
li.list-group-item
|
125
|
-
'#{message.url}
|
126
|
-
a href="#{message.url}" target='_blank'
|
127
|
-
i.fa.fa-external-link
|
128
|
-
.pull-right.badge.alert-danger
|
129
|
-
- if message.errorCode > 0
|
130
|
-
' #{message.errorCode}
|
131
|
-
- unless message.errorString.nil?
|
132
|
-
| #{message.errorString}
|
86
|
+
.panel-body.collapse id="p-#{index}"
|
87
|
+
ul.list-group
|
88
|
+
- page.errors.each do |error|
|
89
|
+
== engine.transform page, error do
|
90
|
+
li.list-group-item.error data-ids="type-#{blinkr.categories[error.category].id}-#{blinkr.categories[error.category].types[error.type].id} severity-#{error.severity} category-#{blinkr.categories[error.category].id}"
|
91
|
+
- unless error.icon.nil?
|
92
|
+
i class="fa fa-2x #{error.icon} pull-left"
|
93
|
+
- unless error.code.nil? && error.message.nil?
|
94
|
+
div class="pull-right label label-#{error.severity}"
|
95
|
+
- unless error.code.nil?
|
96
|
+
' #{error.code}
|
97
|
+
- unless error.message.nil?
|
98
|
+
| #{error.message}
|
99
|
+
div
|
100
|
+
' #{error.title}
|
101
|
+
- if error.url
|
102
|
+
a href="#{error.url}" target='_blank'
|
103
|
+
i.fa.fa-external-link
|
104
|
+
- if error.detail
|
105
|
+
p= error.detail
|
106
|
+
- if error.snippet
|
107
|
+
pre #{error.snippet}
|
108
|
+
javascript:
|
109
|
+
$('#p-#{index}').on('show.bs.collapse', function () {
|
110
|
+
$('a#control-p-#{index}').html('<i class="fa fa-caret-square-o-down"></i>');
|
111
|
+
});
|
112
|
+
$('#p-#{index}').on('hide.bs.collapse', function () {
|
113
|
+
$('a#control-p-#{index}').html('<i class="fa fa-caret-square-o-right"></i>');
|
114
|
+
});
|
133
115
|
|
134
|
-
|
135
|
-
$('#r-#{err.uid}').on('show.bs.collapse', function () {
|
136
|
-
$('a#control-r-#{err.uid}').html('<i class="fa fa-caret-square-o-down"></i>');
|
137
|
-
});
|
138
|
-
$('#r-#{err.uid}').on('hide.bs.collapse', function () {
|
139
|
-
$('a#control-r-#{err.uid}').html('<i class="fa fa-caret-square-o-right"></i>');
|
140
|
-
});
|
141
|
-
- if errors.resources.empty?
|
116
|
+
- if blinkr.pages.length == 0
|
142
117
|
.panel.panel-success
|
143
118
|
.panel-heading
|
144
119
|
a id='resources'
|
145
|
-
| No
|
146
|
-
|
120
|
+
| No errors detected
|
121
|
+
|
122
|
+
- engine.append(blinkr).each do |result|
|
123
|
+
== result
|
124
|
+
|
147
125
|
javascript:
|
148
126
|
$('[data-toggle="tooltip"]').tooltip({'placement': 'top'});
|
149
127
|
|
128
|
+
function category() {
|
129
|
+
$( "#filters #types input.category[type=checkbox]" ).each( function() {
|
130
|
+
var category = $( this ).val();
|
131
|
+
var checked = $( this ).prop("checked");
|
132
|
+
$( "#filters #types input.type[type=checkbox][data-category=" + category + "]" ).each( function() {
|
133
|
+
$( this ).prop( "checked", checked );
|
134
|
+
});
|
135
|
+
});
|
136
|
+
filter();
|
137
|
+
}
|
138
|
+
|
139
|
+
function severities() {
|
140
|
+
$( "#filters #severities input.severities[type=checkbox]" ).each( function() {
|
141
|
+
var checked = $( this ).prop("checked");
|
142
|
+
$( "#filters #severities input.severity[type=checkbox]" ).each( function() {
|
143
|
+
$( this ).prop( "checked", checked );
|
144
|
+
});
|
145
|
+
});
|
146
|
+
filter();
|
147
|
+
}
|
148
|
+
|
149
|
+
function updateCategory() {
|
150
|
+
$( "#filters #types input.category[type=checkbox]" ).each( function() {
|
151
|
+
var category = $( this ).val();
|
152
|
+
var checked = 0;
|
153
|
+
var unchecked = 0;
|
154
|
+
$( "#filters #types input.type[type=checkbox][data-category=" + category + "]" ).each( function() {
|
155
|
+
if ( $( this ).prop( "checked" ) ) {
|
156
|
+
checked++;
|
157
|
+
} else {
|
158
|
+
unchecked++;
|
159
|
+
}
|
160
|
+
});
|
161
|
+
if (unchecked == 0) {
|
162
|
+
$( this ).prop( "checked", true );
|
163
|
+
} else {
|
164
|
+
$( this ).prop( "checked", false );
|
165
|
+
}
|
166
|
+
});
|
167
|
+
}
|
168
|
+
|
169
|
+
function updateSeverities() {
|
170
|
+
$( "#filters #severities input.severities[type=checkbox]" ).each( function() {
|
171
|
+
var checked = 0;
|
172
|
+
var unchecked = 0;
|
173
|
+
$( "#filters #severities input.severity[type=checkbox]" ).each( function() {
|
174
|
+
if ( $( this ).prop( "checked" ) ) {
|
175
|
+
checked++;
|
176
|
+
} else {
|
177
|
+
unchecked++;
|
178
|
+
}
|
179
|
+
});
|
180
|
+
if (unchecked == 0) {
|
181
|
+
$( this ).prop( "checked", true );
|
182
|
+
} else {
|
183
|
+
$( this ).prop( "checked", false );
|
184
|
+
}
|
185
|
+
});
|
186
|
+
}
|
187
|
+
|
188
|
+
function filter() {
|
189
|
+
$( ".pages" ).hide();
|
190
|
+
$( ".spinner" ).show();
|
191
|
+
var selectedTypes = [];
|
192
|
+
var selectedSeverities = [];
|
193
|
+
$( "#filters #severities input[type=checkbox]" ).each( function() {
|
194
|
+
if ( $( this ).prop( "checked" ) ) {
|
195
|
+
selectedSeverities.push( $( this ).val() );
|
196
|
+
}
|
197
|
+
});
|
198
|
+
$( "#filters #types input.type[type=checkbox]" ).each( function() {
|
199
|
+
if ( $( this ).prop( "checked" ) ) {
|
200
|
+
selectedTypes.push( $( this ).val() );
|
201
|
+
}
|
202
|
+
});
|
203
|
+
$( "#pages .page" ).each( function() {
|
204
|
+
var count = 0;
|
205
|
+
$( "li.error[data-ids]", this ).each( function() {
|
206
|
+
var showType = false;
|
207
|
+
var showSeverity = false;
|
208
|
+
var ids = $( this ).attr("data-ids");
|
209
|
+
for (i = 0; i < selectedTypes.length; i++) {
|
210
|
+
if (ids.indexOf(selectedTypes[i]) >= 0) {
|
211
|
+
showType = true;
|
212
|
+
continue;
|
213
|
+
}
|
214
|
+
}
|
215
|
+
for (i = 0; i < selectedSeverities.length; i++) {
|
216
|
+
if (ids.indexOf(selectedSeverities[i]) >= 0) {
|
217
|
+
showSeverity = true;
|
218
|
+
continue;
|
219
|
+
}
|
220
|
+
}
|
221
|
+
if (showSeverity && showType) {
|
222
|
+
$( this ).show();
|
223
|
+
count++;
|
224
|
+
} else {
|
225
|
+
$( this ).hide();
|
226
|
+
}
|
227
|
+
});
|
228
|
+
if (count == 0) {
|
229
|
+
$( this ).hide();
|
230
|
+
} else {
|
231
|
+
$( this ).show();
|
232
|
+
}
|
233
|
+
});
|
234
|
+
$( ".pages" ).show();
|
235
|
+
$( ".spinner" ).hide();
|
236
|
+
}
|
237
|
+
|
238
|
+
|