html-proofer 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +15 -5
  3. data/bin/htmlproof +2 -0
  4. data/html-proofer.gemspec +1 -1
  5. data/lib/html/proofer.rb +10 -3
  6. data/lib/html/proofer/checkable.rb +6 -7
  7. data/lib/html/proofer/checks.rb +7 -3
  8. data/lib/html/proofer/checks/favicon.rb +22 -0
  9. data/lib/html/proofer/checks/images.rb +7 -5
  10. data/lib/html/proofer/checks/links.rb +12 -2
  11. data/lib/html/proofer/checks/scripts.rb +40 -0
  12. data/spec/html/proofer/favicon_spec.rb +46 -0
  13. data/spec/html/proofer/fixtures/favicon/favicon_absent.html +4 -0
  14. data/spec/html/proofer/fixtures/favicon/favicon_absent_apple.html +7 -0
  15. data/spec/html/proofer/fixtures/favicon/favicon_broken.html +7 -0
  16. data/spec/html/proofer/fixtures/favicon/favicon_broken_but_ignored.html +7 -0
  17. data/spec/html/proofer/fixtures/favicon/favicon_present.html +7 -0
  18. data/spec/html/proofer/fixtures/favicon/favicon_present_shortcut.html +7 -0
  19. data/spec/html/proofer/fixtures/{existingImageExternal.html → images/existingImageExternal.html} +0 -0
  20. data/spec/html/proofer/fixtures/{gpl.png → images/gpl.png} +0 -0
  21. data/spec/html/proofer/fixtures/{ignorableImages.html → images/ignorableImages.html} +0 -0
  22. data/spec/html/proofer/fixtures/{image_missing_protocol_invalid.html → images/image_missing_protocol_invalid.html} +0 -0
  23. data/spec/html/proofer/fixtures/{image_missing_protocol_valid.html → images/image_missing_protocol_valid.html} +0 -0
  24. data/spec/html/proofer/fixtures/{missingImageAlt.html → images/missingImageAlt.html} +0 -0
  25. data/spec/html/proofer/fixtures/{missingImageAltText.html → images/missingImageAltText.html} +0 -0
  26. data/spec/html/proofer/fixtures/{missingImageExternal.html → images/missingImageExternal.html} +0 -0
  27. data/spec/html/proofer/fixtures/{missingImageInternal.html → images/missingImageInternal.html} +0 -0
  28. data/spec/html/proofer/fixtures/{missingImageSrc.html → images/missingImageSrc.html} +0 -0
  29. data/spec/html/proofer/fixtures/images/relativeToSelf.html +2 -0
  30. data/spec/html/proofer/fixtures/{rootRelativeImages.html → images/rootRelativeImages.html} +0 -0
  31. data/spec/html/proofer/fixtures/{terribleImageName.html → images/terribleImageName.html} +0 -0
  32. data/spec/html/proofer/fixtures/{workingDataURIImage.html → images/workingDataURIImage.html} +0 -0
  33. data/spec/html/proofer/fixtures/{blank_mailto_link.html → links/blank_mailto_link.html} +0 -0
  34. data/spec/html/proofer/fixtures/{blank_tel_link.html → links/blank_tel_link.html} +0 -0
  35. data/spec/html/proofer/fixtures/{brokenHashExternal.html → links/brokenHashExternal.html} +2 -2
  36. data/spec/html/proofer/fixtures/{brokenHashInternal.html → links/brokenHashInternal.html} +0 -0
  37. data/spec/html/proofer/fixtures/{brokenInternalLink.html → links/brokenInternalLink.html} +0 -0
  38. data/spec/html/proofer/fixtures/{brokenLinkExternal.html → links/brokenLinkExternal.html} +0 -0
  39. data/spec/html/proofer/fixtures/{brokenLinkInternal.html → links/brokenLinkInternal.html} +0 -0
  40. data/spec/html/proofer/fixtures/{brokenLinkWithNumber.html → links/brokenLinkWithNumber.html} +0 -0
  41. data/spec/html/proofer/fixtures/{checkSSLLinks.html → links/checkSSLLinks.html} +0 -0
  42. data/spec/html/proofer/fixtures/{folder → links/folder}/anchorLink.html +0 -0
  43. data/spec/html/proofer/fixtures/{folder → links/folder}/assets/barrel.png +0 -0
  44. data/spec/html/proofer/fixtures/{folder → links/folder}/index.html +0 -0
  45. data/spec/html/proofer/fixtures/{folder → links/folder}/relativeImage.html +0 -0
  46. data/spec/html/proofer/fixtures/links/gpl.png +0 -0
  47. data/spec/html/proofer/fixtures/{head_link_href.html → links/head_link_href.html} +0 -0
  48. data/spec/html/proofer/fixtures/{head_link_href_absent.html → links/head_link_href_absent.html} +0 -0
  49. data/spec/html/proofer/fixtures/{head_link_href_empty.html → links/head_link_href_empty.html} +0 -0
  50. data/spec/html/proofer/fixtures/{ignorableLinks.html → links/ignorableLinks.html} +0 -0
  51. data/spec/html/proofer/fixtures/{ignorableLinksViaOptions.html → links/ignorableLinksViaOptions.html} +0 -0
  52. data/spec/html/proofer/fixtures/{index.html → links/index.html} +0 -0
  53. data/spec/html/proofer/fixtures/{linkToFolder.html → links/linkToFolder.html} +0 -0
  54. data/spec/html/proofer/fixtures/{linkTranslatedViaHrefSwap.html → links/linkTranslatedViaHrefSwap.html} +0 -0
  55. data/spec/html/proofer/fixtures/{linkWithHttps.html → links/linkWithHttps.html} +0 -0
  56. data/spec/html/proofer/fixtures/{linkWithRedirect.html → links/linkWithRedirect.html} +0 -0
  57. data/spec/html/proofer/fixtures/links/link_directory_without_slash.html +1 -0
  58. data/spec/html/proofer/fixtures/{link_missing_protocol_invalid.html → links/link_missing_protocol_invalid.html} +0 -0
  59. data/spec/html/proofer/fixtures/{link_missing_protocol_valid.html → links/link_missing_protocol_valid.html} +0 -0
  60. data/spec/html/proofer/fixtures/{mailto_link.html → links/mailto_link.html} +0 -0
  61. data/spec/html/proofer/fixtures/{missingLinkHref.html → links/missingLinkHref.html} +0 -0
  62. data/spec/html/proofer/fixtures/{multipleProblems.html → links/multipleProblems.html} +0 -0
  63. data/spec/html/proofer/fixtures/{notarealhash.html → links/notarealhash.html} +0 -0
  64. data/spec/html/proofer/fixtures/{relativeLinks.html → links/relativeLinks.html} +0 -2
  65. data/spec/html/proofer/fixtures/{rootLink.html → links/rootLink.html} +0 -0
  66. data/spec/html/proofer/fixtures/{tel_link.html → links/tel_link.html} +0 -0
  67. data/spec/html/proofer/fixtures/Screen Shot 2012-08-09 at 7.51.18 AM.png b/data/spec/html/proofer/fixtures/resources/Screen Shot 2012-08-09 at 7.51.18 → AM.png +0 -0
  68. data/spec/html/proofer/fixtures/resources/gpl.png +0 -0
  69. data/spec/html/proofer/fixtures/scripts/script.js +1 -0
  70. data/spec/html/proofer/fixtures/scripts/script_broken_external.html +9 -0
  71. data/spec/html/proofer/fixtures/scripts/script_content.html +9 -0
  72. data/spec/html/proofer/fixtures/scripts/script_content_absent.html +9 -0
  73. data/spec/html/proofer/fixtures/scripts/script_missing_internal.html +9 -0
  74. data/spec/html/proofer/fixtures/scripts/script_valid_internal.html +9 -0
  75. data/spec/html/proofer/images_spec.rb +20 -14
  76. data/spec/html/proofer/links_spec.rb +41 -27
  77. data/spec/html/proofer/scripts_spec.rb +35 -0
  78. data/spec/html/proofer_spec.rb +2 -2
  79. metadata +132 -94
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 58e3804b2c58cfc45bef2a6de1a8a2e97ccd6132
4
- data.tar.gz: f0cff86b5f52382e228090a6aa5992815ea3d9de
3
+ metadata.gz: 65952ba4e5dea5147b84fb88dfae41a49d432340
4
+ data.tar.gz: a127df0cb3040eba53860c640255678baf4df3d3
5
5
  SHA512:
6
- metadata.gz: cc7bc7d35c99e356ea94c69472957f923f117f18ac119c8ce147794ad6ba6cc9dab9369ef244c8cb9498a92e9a823e14f77e88a89d564a701af66dbc65d2d7ca
7
- data.tar.gz: be3629b356a39521baa248e6e4a705ba625d9bca4dc697ba5dfdab7344228dbe2c4a104293a73019387eb0ab4593e8d1af96c95272c54393a2b285bc5b7d69dc
6
+ metadata.gz: 878ed21acbf0ed474580c29bd438dd32e645750480f88e16f675bd579ae3c8fc7b8c216c357caecbfd34944c3cbef3c9b5932fef57ba2f6ce09abbbcc03f23cf
7
+ data.tar.gz: 740b6aaaa8df3bed83b6194ff27ba85810dbd035e30cb45410fabb2050bcb94ba485e67531cec6c790ad7194523da23c2291796e6797ca192b66dfc805c2123d
data/README.md CHANGED
@@ -107,15 +107,25 @@ htmlproof ./_site
107
107
  * Whether your internal links are not broken; this includes hash references (`#linkToMe`)
108
108
  * Whether external links are working
109
109
 
110
+ ### Scripts
111
+
112
+ `script` elements:
113
+
114
+ * Whether your internal script references are not broken
115
+ * Whether external scripts are loading
116
+
110
117
  ## Configuration
111
118
 
112
119
  The `HTML::Proofer` constructor takes an optional hash of additional options:
113
120
 
114
- * `:ext`: the extension (including the `.`) of your HTML files (default: `.html`)
115
- * `:href_swap`: a hash containing key-value pairs of `RegExp => String`. It transforms links that match `RegExp` into `String` via `gsub`.
116
- * `:href_ignore`: an array of Strings or RegExps containing `href`s that are safe to ignore (certain URIs, like `mailto` and `tel`, are always ignored)
117
- * `:disable_external`: if `true`, does not run the external link checker, which can take a lot of time (default: `false`)
118
- * `:verbose`: if `true`, outputs extra information as the checking happens. Useful for debugging. (default: `false`)
121
+ | Option | Description | Default |
122
+ | :----- | :---------- | :------ |
123
+ | `disable_external` | If `true`, does not run the external link checker, which can take a lot of time. | `false` |
124
+ | `ext` | The extension of your HTML files including the dot. | `.html` |
125
+ | `favicon` | Enables the favicon checker. | `false` |
126
+ | `href_ignore` | An array of Strings or RegExps containing `href`s that are safe to ignore. Certain URIs, like `mailto` and `tel`, are always ignored. | `[]` |
127
+ | `href_swap` | A hash containing key-value pairs of `RegExp => String`. It transforms links that match `RegExp` into `String` via `gsub`. | `{}` |
128
+ | `verbose` | If `true`, outputs extra information as the checking happens. Useful for debugging. | `false` |
119
129
 
120
130
  You can also pass in any of Typhoeus' options for the external link check. For example:
121
131
 
data/bin/htmlproof CHANGED
@@ -15,6 +15,7 @@ Mercenary.program(:htmlproof) do |p|
15
15
  p.description "Runs the HTML-Proofer suite on the files in PATH"
16
16
 
17
17
  p.option 'ext', '--ext EXT', 'The extension of your HTML files (default: `.html`)'
18
+ p.option 'favicon', '--favicon', 'Enables the favicon checker (default: `false`).'
18
19
  p.option 'swap', '--swap regex:string,[regex:string,...]', Array, 'Array containing key-value pairs of `RegExp:String`. It transforms links that match `RegExp` into `String`'
19
20
  p.option 'ignore', '--ignore link1,[link2,...]', Array, 'Array of Strings containing `href`s that are safe to ignore (default: `mailto`)'
20
21
  p.option 'disable_external', '--disable_external', 'Disables the external link checker (default: `false`)'
@@ -35,6 +36,7 @@ Mercenary.program(:htmlproof) do |p|
35
36
  end
36
37
  options[:href_ignore] = opts["ignore"] unless opts["ignore"].nil?
37
38
  options[:disable_external] = opts["disable_external"] unless opts["disable_external"].nil?
39
+ options[:favicon] = opts["favicon"] unless opts["favicon"].nil?
38
40
  options[:verbose] = opts["verbose"] unless opts["verbose"].nil?
39
41
 
40
42
  HTML::Proofer.new(path, options).run
data/html-proofer.gemspec CHANGED
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |gem|
5
5
  gem.name = "html-proofer"
6
- gem.version = "0.8.0"
6
+ gem.version = "0.9.0"
7
7
  gem.authors = ["Garen Torikian"]
8
8
  gem.email = ["gjtorikian@gmail.com"]
9
9
  gem.description = %q{Test your rendered HTML files to make sure they're accurate.}
data/lib/html/proofer.rb CHANGED
@@ -13,7 +13,14 @@ module HTML
13
13
  def initialize(src, opts={})
14
14
  @srcDir = src
15
15
 
16
- @proofer_opts = {:ext => ".html", :href_swap => [], :href_ignore => [], :disable_external => false, :verbose => false }
16
+ @proofer_opts = {
17
+ :ext => ".html",
18
+ :favicon => false,
19
+ :href_swap => [],
20
+ :href_ignore => [],
21
+ :disable_external => false,
22
+ :verbose => false
23
+ }
17
24
  @options = @proofer_opts.merge({:followlocation => true}).merge(opts)
18
25
 
19
26
  @failed_tests = []
@@ -74,8 +81,8 @@ module HTML
74
81
  if @failed_tests.empty?
75
82
  logger.info "HTML-Proofer finished successfully.".green
76
83
  else
77
- @failed_tests.each do |issue|
78
- logger.error (issue + "\n\n").red
84
+ @failed_tests.sort.each do |issue|
85
+ logger.error issue.to_s.red
79
86
  end
80
87
 
81
88
  raise "HTML-Proofer found #{@failed_tests.length} failures!".red
@@ -7,7 +7,10 @@ module HTML
7
7
  @alt = obj['alt']
8
8
  @name = obj['name']
9
9
  @id = obj['id']
10
+ @rel = obj['rel']
11
+
10
12
  @data_ignore_proofer = obj['data-proofer-ignore']
13
+ @content = obj.content
11
14
  @check = check
12
15
  @checked_paths = {}
13
16
  @type = type
@@ -55,16 +58,12 @@ module HTML
55
58
  rescue URI::BadURIError
56
59
  false
57
60
  rescue URI::InvalidURIError
58
- if @type == "image"
59
- @ignored = true
60
- return true if url.match(/^data:image/)
61
- end
62
-
63
61
  false
64
62
  end
65
63
 
66
64
  def ignore?
67
- return true if @ignored || @data_ignore_proofer || @check.additional_href_ignores.include?(url)
65
+ return true if @data_ignore_proofer || @check.additional_href_ignores.include?(url)
66
+ return true if @type == "image" and url.match(/^data:image/)
68
67
  unless @check.additional_href_ignores.empty?
69
68
  @check.additional_href_ignores.each do |href_ignore|
70
69
  if href_ignore.is_a? String
@@ -108,7 +107,7 @@ module HTML
108
107
 
109
108
  file = File.join base, path
110
109
 
111
- # implicit /index.html support, with support for tailing slashes
110
+ # implicit /index.html support, with support for trailing slashes
112
111
  file = File.join path, "index.html" if File.directory? File.expand_path file, @check.src
113
112
 
114
113
  file
@@ -1,9 +1,13 @@
1
1
  module HTML
2
2
  class Proofer
3
3
  class Checks
4
- require File.dirname(__FILE__) + '/check'
5
- require File.dirname(__FILE__) + '/checks/images'
6
- require File.dirname(__FILE__) + '/checks/links'
4
+ [
5
+ "/check",
6
+ "/checks/images",
7
+ "/checks/links",
8
+ "/checks/scripts",
9
+ "/checks/favicon"
10
+ ].each { |r| require File.dirname(__FILE__) + r }
7
11
  end
8
12
  end
9
13
  end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ class Favicon < ::HTML::Proofer::Checkable
4
+ def rel
5
+ @rel
6
+ end
7
+ end
8
+
9
+ class Favicons < ::HTML::Proofer::Checks::Check
10
+
11
+ def run
12
+ return unless @options[:favicon]
13
+
14
+ @html.css("link").each do |favicon|
15
+ favicon = Favicon.new favicon, "favicon", self
16
+ return if favicon.rel.split(" ").last.eql? "icon"
17
+ end
18
+
19
+ self.add_issue "no favicon specified"
20
+ end
21
+
22
+ end
@@ -27,18 +27,20 @@ class Images < ::HTML::Proofer::Checks::Check
27
27
  @html.css('img').each do |i|
28
28
  img = Image.new i, "image", self
29
29
 
30
+ next if img.ignore?
31
+
30
32
  # screenshot filenames should return because of terrible names
31
33
  next self.add_issue "image has a terrible filename (#{img.src})" if img.terrible_filename?
32
34
 
33
35
  # does the image exist?
34
36
  if img.missing_src?
35
37
  self.add_issue "image has no src attribute"
36
- elsif img.remote?
37
- next if img.ignore?
38
- add_to_external_urls img.src
39
38
  else
40
- next if img.ignore?
41
- self.add_issue("internal image #{img.src} does not exist") unless img.exists?
39
+ if img.remote?
40
+ add_to_external_urls img.src
41
+ else
42
+ self.add_issue("internal image #{img.src} does not exist") unless img.exists?
43
+ end
42
44
  end
43
45
 
44
46
  # check alt tag
@@ -10,6 +10,10 @@ class Link < ::HTML::Proofer::Checkable
10
10
  href.nil? and @name.nil? and @id.nil?
11
11
  end
12
12
 
13
+ def unslashed_directory?
14
+ File.directory? absolute_path and !absolute_path.end_with? File::SEPARATOR
15
+ end
16
+
13
17
  end
14
18
 
15
19
  class Links < ::HTML::Proofer::Checks::Check
@@ -18,14 +22,14 @@ class Links < ::HTML::Proofer::Checks::Check
18
22
  @html.css('a, link').each do |l|
19
23
  link = Link.new l, "link", self
20
24
 
25
+ next if link.ignore?
26
+
21
27
  # is it even a valid URL?
22
28
  unless link.valid?
23
29
  self.add_issue "#{link.href} is an invalid URL"
24
30
  next
25
31
  end
26
32
 
27
- next if link.ignore?
28
-
29
33
  # is there even a href?
30
34
  if link.missing_href?
31
35
  self.add_issue("link has no href attribute")
@@ -39,6 +43,12 @@ class Links < ::HTML::Proofer::Checks::Check
39
43
  self.add_issue "internally linking to #{link.href}, which does not exist" unless link.exists?
40
44
  end
41
45
 
46
+ # has the local directory a trailing slash?
47
+ if !@options[:followlocation] and !link.remote? and link.unslashed_directory?
48
+ self.add_issue("internally linking to a directory #{link.absolute_path} without trailing slash")
49
+ next
50
+ end
51
+
42
52
  # verify the target hash
43
53
  if link.hash
44
54
  if link.remote?
@@ -0,0 +1,40 @@
1
+ # encoding: utf-8
2
+
3
+ class Script < ::HTML::Proofer::Checkable
4
+
5
+ def src
6
+ @src unless @src.nil? || @src.empty?
7
+ end
8
+
9
+ def missing_src?
10
+ !src
11
+ end
12
+
13
+ def blank?
14
+ @content.strip.empty?
15
+ end
16
+
17
+ end
18
+
19
+ class Scripts < ::HTML::Proofer::Checks::Check
20
+ def run
21
+ @html.css('script').each do |s|
22
+ script = Script.new s, "script", self
23
+
24
+ next if script.ignore?
25
+ next unless script.blank?
26
+
27
+ # does the script exist?
28
+ if script.missing_src?
29
+ self.add_issue "script is empty and has no src attribute"
30
+ elsif script.remote?
31
+ add_to_external_urls script.src
32
+ else
33
+ self.add_issue("internal script #{script.src} does not exist") unless script.exists?
34
+ end
35
+
36
+ end
37
+
38
+ external_urls
39
+ end
40
+ end
@@ -0,0 +1,46 @@
1
+ require "spec_helper"
2
+
3
+ describe "Favicons test" do
4
+ it "ignores for absent favicon by default" do
5
+ absent = "#{FIXTURES_DIR}/favicon/favicon_absent.html"
6
+ output = capture_stderr { HTML::Proofer.new(absent).run }
7
+ output.should == ""
8
+ end
9
+
10
+ it "fails for absent favicon" do
11
+ absent = "#{FIXTURES_DIR}/favicon/favicon_absent.html"
12
+ output = capture_stderr { HTML::Proofer.new(absent, {:favicon => true}).run }
13
+ output.should match /no favicon specified/
14
+ end
15
+
16
+ it "fails for absent favicon but present apple touch icon" do
17
+ absent = "#{FIXTURES_DIR}/favicon/favicon_absent_apple.html"
18
+ output = capture_stderr { HTML::Proofer.new(absent, {:favicon => true}).run }
19
+ output.should match /no favicon specified/
20
+ end
21
+
22
+ it "fails for broken favicon" do
23
+ broken = "#{FIXTURES_DIR}/favicon/favicon_broken.html"
24
+ output = capture_stderr { HTML::Proofer.new(broken, {:favicon => true}).run }
25
+ output.should match /internally linking to asdadaskdalsdk.png/
26
+ end
27
+
28
+ it "passes for present favicon" do
29
+ present = "#{FIXTURES_DIR}/favicon/favicon_present.html"
30
+ output = capture_stderr { HTML::Proofer.new(present, {:favicon => true}).run }
31
+ output.should == ""
32
+ end
33
+
34
+ it "passes for present favicon with shortcut notation" do
35
+ present = "#{FIXTURES_DIR}/favicon/favicon_present_shortcut.html"
36
+ output = capture_stderr { HTML::Proofer.new(present, {:favicon => true}).run }
37
+ output.should == ""
38
+ end
39
+
40
+ it "passes for broken favicon with data-proofer-ignore" do
41
+ broken_but_ignored = "#{FIXTURES_DIR}/favicon/favicon_broken_but_ignored.html"
42
+ output = capture_stderr { HTML::Proofer.new(broken_but_ignored, {:favicon => true}).run }
43
+ output.should == ""
44
+ end
45
+
46
+ end
@@ -0,0 +1,4 @@
1
+ <html>
2
+ <body>
3
+ </body>
4
+ </html>
@@ -0,0 +1,7 @@
1
+ <html>
2
+ <head>
3
+ <link rel="apple-touch-icon" href="gpl.png">
4
+ </head>
5
+ <body>
6
+ </body>
7
+ </html>
@@ -0,0 +1,7 @@
1
+ <html>
2
+ <head>
3
+ <link rel="icon" href="asdadaskdalsdk.png">
4
+ </head>
5
+ <body>
6
+ </body>
7
+ </html>
@@ -0,0 +1,7 @@
1
+ <html>
2
+ <head>
3
+ <link rel="icon" href="asdadaskdalsdk.png" data-proofer-ignore>
4
+ </head>
5
+ <body>
6
+ </body>
7
+ </html>
@@ -0,0 +1,7 @@
1
+ <html>
2
+ <head>
3
+ <link rel="icon" href="../resources/gpl.png">
4
+ </head>
5
+ <body>
6
+ </body>
7
+ </html>
@@ -0,0 +1,7 @@
1
+ <html>
2
+ <head>
3
+ <link rel="shortcut icon" href="../resources/gpl.png">
4
+ </head>
5
+ <body>
6
+ </body>
7
+ </html>
@@ -0,0 +1,2 @@
1
+
2
+ <img alt="sure thing" src="gpl.png"/>Relative to self
@@ -3,9 +3,9 @@
3
3
  <body>
4
4
 
5
5
  <p>Blah blah blah. <a href="./notarealhash.html#noHash">Not a real file, don't even bother with hash!</a></p>
6
- <p>Blah blah blah. <a href="./missingImageAlt.html#asdfasfdkafl">A real file, not a real hash!</a></p>
6
+ <p>Blah blah blah. <a href="../images/missingImageAlt.html#asdfasfdkafl">A real file, not a real hash!</a></p>
7
7
  <a href="./brokenLinkInternal.html#safeHash">At last, a real hash link.</a>
8
8
 
9
9
  </body>
10
10
 
11
- </html>
11
+ </html>