blinkr 0.1.0 → 0.2.0
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 +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
|
+
|