html-proofer 0.8.0 → 0.9.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.
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>