dolt 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/Gemfile.lock +25 -5
  2. data/Readme.md +90 -16
  3. data/bin/dolt +22 -4
  4. data/dolt.gemspec +10 -2
  5. data/lib/dolt/disk_repo_resolver.rb +3 -5
  6. data/lib/dolt/git/blame.rb +112 -0
  7. data/lib/dolt/git/commit.rb +73 -0
  8. data/lib/dolt/git/repository.rb +26 -24
  9. data/lib/dolt/repo_actions.rb +32 -19
  10. data/lib/dolt/sinatra/actions.rb +51 -18
  11. data/lib/dolt/sinatra/multi_repo_browser.rb +30 -3
  12. data/lib/dolt/sinatra/single_repo_browser.rb +36 -2
  13. data/lib/dolt/template_renderer.rb +7 -8
  14. data/lib/dolt/version.rb +1 -1
  15. data/lib/dolt/view.rb +3 -61
  16. data/lib/dolt/view/blame.rb +57 -0
  17. data/lib/dolt/view/blob.rb +60 -0
  18. data/lib/dolt/view/breadcrumb.rb +10 -17
  19. data/lib/dolt/{git/blob.rb → view/commit.rb} +5 -11
  20. data/lib/dolt/view/gravatar.rb +29 -0
  21. data/lib/dolt/{git/shell.rb → view/markup.rb} +10 -16
  22. data/lib/dolt/view/multi_repository.rb +27 -0
  23. data/lib/dolt/{async/deferrable_child_process.rb → view/object.rb} +11 -24
  24. data/lib/dolt/view/single_repository.rb +27 -0
  25. data/lib/dolt/view/smart_blob_renderer.rb +33 -0
  26. data/lib/dolt/view/syntax_highlight.rb +86 -0
  27. data/lib/dolt/view/tree.rb +69 -0
  28. data/test/dolt/git/blame_test.rb +128 -0
  29. data/test/dolt/git/commit_test.rb +89 -0
  30. data/test/dolt/git/repository_test.rb +24 -73
  31. data/test/dolt/repo_actions_test.rb +77 -15
  32. data/test/dolt/sinatra/actions_test.rb +168 -8
  33. data/test/dolt/template_renderer_test.rb +44 -10
  34. data/test/dolt/templates/blame_test.rb +56 -0
  35. data/test/dolt/{views → templates}/blob_test.rb +44 -34
  36. data/test/dolt/templates/commits_test.rb +61 -0
  37. data/test/dolt/templates/raw_test.rb +41 -0
  38. data/test/dolt/templates/tree_test.rb +51 -0
  39. data/test/dolt/view/blame_test.rb +122 -0
  40. data/test/dolt/view/blob_test.rb +90 -0
  41. data/test/dolt/view/breadcrumb_test.rb +46 -0
  42. data/test/dolt/view/commit_test.rb +31 -0
  43. data/test/dolt/view/gravatar_test.rb +30 -0
  44. data/test/dolt/view/markup_test.rb +30 -0
  45. data/test/dolt/{git/blob_test.rb → view/multi_repository_test.rb} +10 -24
  46. data/test/dolt/view/object_test.rb +71 -0
  47. data/test/dolt/view/single_repository_test.rb +30 -0
  48. data/test/dolt/{view_test.rb → view/syntax_highlight_test.rb} +40 -27
  49. data/test/dolt/view/tree_test.rb +115 -0
  50. data/test/test_helper.rb +18 -13
  51. data/vendor/ui/css/gitorious.css +20 -8
  52. data/views/blame.erb +41 -0
  53. data/views/blob.erb +6 -9
  54. data/views/commits.erb +23 -0
  55. data/views/index.erb +25 -0
  56. data/views/raw.erb +19 -0
  57. data/views/tree.erb +28 -26
  58. metadata +156 -26
  59. data/lib/dolt/git/tree.rb +0 -65
  60. data/lib/dolt/view/highlighter.rb +0 -52
  61. data/test/dolt/git/shell_test.rb +0 -112
  62. data/test/dolt/git/tree_test.rb +0 -120
@@ -33,35 +33,86 @@ class DummySinatraApp
33
33
  end
34
34
 
35
35
  def response
36
- @response ||= {}
36
+ if !@response
37
+ @response = {}
38
+ def @response.status; @status; end
39
+ def @response.status=(status); @status = status; end
40
+ end
41
+ @response
42
+ end
43
+
44
+ def tree_url(repo, ref, path)
45
+ "/#{repo}/tree/#{ref}:#{path}"
46
+ end
47
+
48
+ def blob_url(repo, ref, path)
49
+ "/#{repo}/blob/#{ref}:#{path}"
37
50
  end
38
51
  end
39
52
 
40
53
  class Renderer
41
54
  def initialize(body = ""); @body = body; end
42
55
 
43
- def render(action, data)
56
+ def render(action, data, options = {})
44
57
  @action = action
45
58
  @data = data
46
- @body
59
+ "#{action}:#@body"
60
+ end
61
+ end
62
+
63
+ class BlobStub
64
+ def is_a?(type)
65
+ type == Rugged::Blob
66
+ end
67
+ end
68
+
69
+ class TreeStub
70
+ def is_a?(type)
71
+ type == Rugged::Tree
47
72
  end
48
73
  end
49
74
 
50
75
  class Actions
51
76
  attr_reader :repo, :ref, :path
52
77
 
53
- def blob(repo, ref, path)
78
+ def initialize(response)
79
+ @response = response
80
+ end
81
+
82
+ def blob(repo, ref, path, &block)
83
+ respond(:blob, repo, ref, path, &block)
84
+ end
85
+
86
+ def tree(repo, ref, path, &block)
87
+ respond(:tree, repo, ref, path, &block)
88
+ end
89
+
90
+ def raw(repo, ref, path, &block)
91
+ respond(:raw, repo, ref, path, &block)
92
+ end
93
+
94
+ def blame(repo, ref, path, &block)
95
+ respond(:blame, repo, ref, path, &block)
96
+ end
97
+
98
+ def history(repo, ref, path, limit, &block)
99
+ respond(:history, repo, ref, path, &block)
100
+ end
101
+
102
+ def respond(type, repo, ref, path, &block)
54
103
  @repo = repo
55
104
  @ref = ref
56
105
  @path = path
57
- yield nil, { :ref => ref, :repository => repo, :blob => "Blob" }
106
+ data = { :ref => ref, :repository => repo }
107
+ data[type] = @response
108
+ block.call(nil, data)
58
109
  end
59
110
  end
60
111
 
61
112
  describe Dolt::Sinatra::Actions do
62
113
  describe "#blob" do
63
114
  it "delegates to actions" do
64
- actions = Actions.new
115
+ actions = Actions.new(BlobStub.new)
65
116
  app = DummySinatraApp.new(actions, Renderer.new)
66
117
  app.blob("gitorious", "master", "app/models/repository.rb")
67
118
 
@@ -71,11 +122,120 @@ describe Dolt::Sinatra::Actions do
71
122
  end
72
123
 
73
124
  it "renders the blob template as html" do
74
- app = DummySinatraApp.new(Actions.new, Renderer.new("Blob"))
125
+ app = DummySinatraApp.new(Actions.new(BlobStub.new), Renderer.new("Blob"))
75
126
  app.blob("gitorious", "master", "app/models/repository.rb")
76
127
 
77
128
  assert_equal "text/html", app.response["Content-Type"]
78
- assert_equal "Blob", app.body
129
+ assert_equal "blob:Blob", app.body
130
+ end
131
+
132
+ it "redirects tree views to tree action" do
133
+ app = DummySinatraApp.new(Actions.new(TreeStub.new), Renderer.new("Blob"))
134
+ app.blob("gitorious", "master", "app/models")
135
+
136
+ assert_equal 302, app.response.status
137
+ assert_equal "/gitorious/tree/master:app/models", app.response["Location"]
138
+ assert_equal "", app.body
139
+ end
140
+ end
141
+
142
+ describe "#tree" do
143
+ it "delegates to actions" do
144
+ actions = Actions.new(TreeStub.new)
145
+ app = DummySinatraApp.new(actions, Renderer.new)
146
+ app.tree("gitorious", "master", "app/models")
147
+
148
+ assert_equal "gitorious", actions.repo
149
+ assert_equal "master", actions.ref
150
+ assert_equal "app/models", actions.path
151
+ end
152
+
153
+ it "renders the tree template as html" do
154
+ app = DummySinatraApp.new(Actions.new(TreeStub.new), Renderer.new("Tree"))
155
+ app.tree("gitorious", "master", "app/models")
156
+
157
+ assert_equal "text/html", app.response["Content-Type"]
158
+ assert_equal "tree:Tree", app.body
159
+ end
160
+
161
+ it "redirects blob views to blob action" do
162
+ app = DummySinatraApp.new(Actions.new(BlobStub.new), Renderer.new("Tree"))
163
+ app.tree("gitorious", "master", "app/models/repository.rb")
164
+
165
+ location = app.response["Location"]
166
+ assert_equal 302, app.response.status
167
+ assert_equal "/gitorious/blob/master:app/models/repository.rb", location
168
+ assert_equal "", app.body
169
+ end
170
+ end
171
+
172
+ describe "#raw" do
173
+ it "delegates to actions" do
174
+ actions = Actions.new(BlobStub.new)
175
+ app = DummySinatraApp.new(actions, Renderer.new)
176
+ app.raw("gitorious", "master", "app/models/repository.rb")
177
+
178
+ assert_equal "gitorious", actions.repo
179
+ assert_equal "master", actions.ref
180
+ assert_equal "app/models/repository.rb", actions.path
181
+ end
182
+
183
+ it "renders the raw template as text" do
184
+ app = DummySinatraApp.new(Actions.new(BlobStub.new), Renderer.new("Text"))
185
+ app.raw("gitorious", "master", "app/models/repository.rb")
186
+
187
+ assert_equal "text/plain", app.response["Content-Type"]
188
+ assert_equal "raw:Text", app.body
189
+ end
190
+
191
+ it "redirects tree views to tree action" do
192
+ app = DummySinatraApp.new(Actions.new(TreeStub.new), Renderer.new("Tree"))
193
+ app.raw("gitorious", "master", "app/models")
194
+
195
+ location = app.response["Location"]
196
+ assert_equal 302, app.response.status
197
+ assert_equal "/gitorious/tree/master:app/models", location
198
+ assert_equal "", app.body
199
+ end
200
+ end
201
+
202
+ describe "#blame" do
203
+ it "delegates to actions" do
204
+ actions = Actions.new(BlobStub.new)
205
+ app = DummySinatraApp.new(actions, Renderer.new)
206
+ app.blame("gitorious", "master", "app/models/repository.rb")
207
+
208
+ assert_equal "gitorious", actions.repo
209
+ assert_equal "master", actions.ref
210
+ assert_equal "app/models/repository.rb", actions.path
211
+ end
212
+
213
+ it "renders the blame template as text" do
214
+ app = DummySinatraApp.new(Actions.new(BlobStub.new), Renderer.new("Text"))
215
+ app.blame("gitorious", "master", "app/models/repository.rb")
216
+
217
+ assert_equal "text/html", app.response["Content-Type"]
218
+ assert_equal "blame:Text", app.body
219
+ end
220
+ end
221
+
222
+ describe "#history" do
223
+ it "delegates to actions" do
224
+ actions = Actions.new(BlobStub.new)
225
+ app = DummySinatraApp.new(actions, Renderer.new)
226
+ app.history("gitorious", "master", "app/models/repository.rb", 10)
227
+
228
+ assert_equal "gitorious", actions.repo
229
+ assert_equal "master", actions.ref
230
+ assert_equal "app/models/repository.rb", actions.path
231
+ end
232
+
233
+ it "renders the commits template as text" do
234
+ app = DummySinatraApp.new(Actions.new(BlobStub.new), Renderer.new("Text"))
235
+ app.history("gitorious", "master", "app/models/repository.rb", 10)
236
+
237
+ assert_equal "text/html", app.response["Content-Type"]
238
+ assert_equal "commits:Text", app.body
79
239
  end
80
240
  end
81
241
  end
@@ -24,6 +24,8 @@ module ViewHelper
24
24
  end
25
25
 
26
26
  describe Dolt::TemplateRenderer do
27
+ before { @root = "/dolt/views" }
28
+
27
29
  it "reads template from file" do
28
30
  File.expects(:read).with("/dolt/views/file.erb").returns("")
29
31
  renderer = Dolt::TemplateRenderer.new("/dolt/views")
@@ -32,13 +34,13 @@ describe Dolt::TemplateRenderer do
32
34
 
33
35
  it "renders template with locals" do
34
36
  File.stubs(:read).returns("<%= name %>!")
35
- renderer = Dolt::TemplateRenderer.new("/dolt/views")
37
+ renderer = Dolt::TemplateRenderer.new(@root)
36
38
 
37
39
  assert_equal "Chris!", renderer.render(:file, { :name => "Chris"})
38
40
  end
39
41
 
40
42
  it "caches template in memory" do
41
- renderer = Dolt::TemplateRenderer.new("/dolt/views")
43
+ renderer = Dolt::TemplateRenderer.new(@root)
42
44
  File.stubs(:read).returns("Original")
43
45
  renderer.render(:file)
44
46
  File.stubs(:read).returns("Updated")
@@ -47,7 +49,7 @@ describe Dolt::TemplateRenderer do
47
49
  end
48
50
 
49
51
  it "does not cache template in memory when configured not to" do
50
- renderer = Dolt::TemplateRenderer.new("/dolt/views", :cache => false)
52
+ renderer = Dolt::TemplateRenderer.new(@root, :cache => false)
51
53
  File.stubs(:read).returns("Original")
52
54
  renderer.render(:file)
53
55
  File.stubs(:read).returns("Updated")
@@ -63,14 +65,33 @@ describe Dolt::TemplateRenderer do
63
65
  assert_equal "I give you: Template", renderer.render(:file)
64
66
  end
65
67
 
66
- it "renders str templates" do
68
+ it "renders template once without layout" do
69
+ renderer = Dolt::TemplateRenderer.new("/", :layout => "layout")
70
+ File.stubs(:read).with("/file.erb").returns("Template")
71
+ File.stubs(:read).with("/layout.erb").returns("I give you: <%= yield %>")
72
+
73
+ assert_equal "Template", renderer.render(:file, {}, :layout => nil)
74
+ end
75
+
76
+ it "renders template once with different layout" do
77
+ renderer = Dolt::TemplateRenderer.new("/", :layout => "layout")
78
+ File.stubs(:read).with("/file.erb").returns("Template")
79
+ File.stubs(:read).with("/layout.erb").returns("I give you: <%= yield %>")
80
+ File.stubs(:read).with("/layout2.erb").returns("I present you: <%= yield %>")
81
+
82
+ html = renderer.render(:file, {}, :layout => "layout2")
83
+
84
+ assert_equal "I present you: Template", html
85
+ end
86
+
87
+ it "renders templates of specific type" do
67
88
  renderer = Dolt::TemplateRenderer.new("/", :type => :str)
68
89
  File.stubs(:read).with("/file.str").returns("Hey!")
69
90
 
70
91
  assert_equal "Hey!", renderer.render(:file)
71
92
  end
72
93
 
73
- it "renders with helper module" do
94
+ it "renders with helper object" do
74
95
  renderer = Dolt::TemplateRenderer.new("/")
75
96
  renderer.helper(ViewHelper)
76
97
  File.stubs(:read).with("/file.erb").returns("Say it: <%= say_it %>")
@@ -78,11 +99,24 @@ describe Dolt::TemplateRenderer do
78
99
  assert_equal "Say it: YES", renderer.render(:file)
79
100
  end
80
101
 
81
- it "configures the renderer" do
82
- renderer = Dolt::TemplateRenderer.new("/", { :attributes => { :stuff => "THING" } })
83
- renderer.helper(ViewHelper)
84
- File.stubs(:read).with("/file.erb").returns("<%= stuff %>")
102
+ it "does not leak state across render calls" do
103
+ renderer = Dolt::TemplateRenderer.new("/")
104
+ File.stubs(:read).with("/file.erb").returns(<<-TEMPLATE)
105
+ <%= @response %><% @response = "NO" %><%= @response %>
106
+ TEMPLATE
107
+
108
+ assert_equal "NO", renderer.render(:file)
109
+ assert_equal "NO", renderer.render(:file)
110
+ end
111
+
112
+ it "shares state between template and layout" do
113
+ renderer = Dolt::TemplateRenderer.new("/", :layout => "layout")
114
+ File.stubs(:read).with("/file.erb").returns(<<-TEMPLATE)
115
+ <% @response = "NO" %><h1><%= @response %></h1>
116
+ TEMPLATE
117
+ tpl = "<title><%= @response %></title><%= yield %>"
118
+ File.stubs(:read).with("/layout.erb").returns(tpl)
85
119
 
86
- assert_equal "THING", renderer.render(:file)
120
+ assert_equal "<title>NO</title><h1>NO</h1>\n", renderer.render(:file)
87
121
  end
88
122
  end
@@ -0,0 +1,56 @@
1
+ # encoding: utf-8
2
+ #--
3
+ # Copyright (C) 2012 Gitorious AS
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #++
18
+ require "test_helper"
19
+ require "dolt/template_renderer"
20
+ require "dolt/view"
21
+
22
+ class Blame
23
+ attr_reader :chunks
24
+ def initialize(chunks); @chunks = chunks; end
25
+ end
26
+
27
+ describe "blame template" do
28
+ include Dolt::ViewTest
29
+
30
+ before do
31
+ @repo = "the-dolt"
32
+ @template_root = File.join(File.dirname(__FILE__), "..", "..", "..", "views")
33
+ @committer = {
34
+ :name => "Christian Johansen",
35
+ :mail => "christian@cjohansen.no",
36
+ :time => Time.now
37
+ }
38
+ end
39
+
40
+ def render(path, blame, options = {})
41
+ renderer = prepare_renderer(@template_root, options)
42
+ renderer.render(:blame, {
43
+ :blame => blame,
44
+ :repository => @repo,
45
+ :ref => options[:ref] || "master",
46
+ :path => path
47
+ })
48
+ end
49
+
50
+ it "renders blame" do
51
+ blob = Blame.new([{ :oid => "0123456", :committer => @committer, :lines => [] }])
52
+ markup = render("app/models/repository.rb", blob)
53
+
54
+ assert_match /<table class="gts-code-listing">/, markup
55
+ end
56
+ end
@@ -16,40 +16,40 @@
16
16
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  #++
18
18
  require "test_helper"
19
- require "dolt/git/blob"
20
- require "dolt/git/repository"
21
19
  require "dolt/template_renderer"
22
20
  require "dolt/view"
23
21
 
22
+ class Blob
23
+ attr_reader :content
24
+ def initialize(content); @content = content; end
25
+ end
26
+
24
27
  describe "blob template" do
28
+ include Dolt::ViewTest
29
+
25
30
  before do
26
- @repo = Dolt::Git::Repository.new("the-dolt")
31
+ @repo = "the-dolt"
27
32
  @template_root = File.join(File.dirname(__FILE__), "..", "..", "..", "views")
28
33
  end
29
34
 
30
- def render(blob, options = {})
31
- options[:attributes] = options[:attributes] || {}
32
- attrs = options[:attributes]
33
- if !attrs.key?(:multi_repo_mode); attrs[:multi_repo_mode] = true; end
34
- renderer = Dolt::TemplateRenderer.new(@template_root, options)
35
- renderer.helper(Dolt::View)
35
+ def render(path, blob, options = {}, helpers = nil)
36
+ renderer = prepare_renderer(@template_root, options, helpers)
36
37
  renderer.render(:blob, {
37
38
  :blob => blob,
38
39
  :repository => @repo,
39
- :ref => options[:ref] || "master"
40
+ :ref => options[:ref] || "master",
41
+ :path => path
40
42
  })
41
43
  end
42
44
 
43
45
  it "renders blob without errors" do
44
- blob = Dolt::Git::Blob.new("file.txt", "Something something")
45
- markup = render(blob)
46
+ markup = render("file.txt", Blob.new("Something something"))
46
47
 
47
48
  assert_match /Something something/, markup
48
49
  end
49
50
 
50
51
  it "renders blob with line numbers" do
51
- blob = Dolt::Git::Blob.new("file.txt", "One\nTwo\nThree")
52
- markup = render(blob)
52
+ markup = render("file.txt", Blob.new("One\nTwo\nThree"))
53
53
 
54
54
  assert_match /<li.*One.*<\/li>/, markup
55
55
  assert_match /<li.*Two.*<\/li>/, markup
@@ -57,40 +57,28 @@ describe "blob template" do
57
57
  end
58
58
 
59
59
  it "renders blob with layout" do
60
- blob = Dolt::Git::Blob.new("file.txt", "Something something")
61
- markup = render(blob, :layout => "layout")
60
+ markup = render("file.txt", Blob.new("Something something"), :layout => "layout")
62
61
 
63
62
  assert_match /Something something/, markup
64
63
  end
65
64
 
66
65
  it "renders repo title in page" do
67
- blob = Dolt::Git::Blob.new("file.txt", "Something something")
68
- markup = render(blob, :layout => "layout")
66
+ @repo = "my-magic-repo"
67
+ markup = render("file.txt", Blob.new("Something something"), :layout => "layout")
69
68
 
70
- assert_match @repo.name, markup
69
+ assert_match "my-magic-repo", markup
71
70
  end
72
71
 
73
72
  it "renders links to other views" do
74
- blob = Dolt::Git::Blob.new("file.txt", "Something something")
75
- markup = render(blob)
73
+ markup = render("file.txt", Blob.new("Something something"))
76
74
 
77
75
  assert_match "/the-dolt/blame/master:file.txt", markup
78
76
  assert_match "/the-dolt/history/master:file.txt", markup
79
77
  assert_match "/the-dolt/raw/master:file.txt", markup
80
78
  end
81
79
 
82
- it "renders links to other views in single repo mode" do
83
- blob = Dolt::Git::Blob.new("file.txt", "Something something")
84
- markup = render(blob, { :attributes => { :multi_repo_mode => false } })
85
-
86
- assert_match "\"/blame/master:file.txt", markup
87
- assert_match "\"/history/master:file.txt", markup
88
- assert_match "\"/raw/master:file.txt", markup
89
- end
90
-
91
80
  it "renders links to other views for correct ref" do
92
- blob = Dolt::Git::Blob.new("file.txt", "Something something")
93
- markup = render(blob, :ref => "123bc21")
81
+ markup = render("file.txt", Blob.new("Something something"), :ref => "123bc21")
94
82
 
95
83
  assert_match "/the-dolt/blame/123bc21:file.txt", markup
96
84
  assert_match "/the-dolt/history/123bc21:file.txt", markup
@@ -98,11 +86,33 @@ describe "blob template" do
98
86
  end
99
87
 
100
88
  it "renders the path clickable" do
101
- blob = Dolt::Git::Blob.new("some/deeply/nested/file.txt", "Something something")
102
- markup = render(blob)
89
+ markup = render("some/deeply/nested/file.txt", Blob.new("Something something"))
103
90
 
104
91
  assert_match 'href="/the-dolt/tree/master:some"', markup
105
92
  assert_match 'href="/the-dolt/tree/master:some/deeply"', markup
106
93
  assert_match 'href="/the-dolt/tree/master:some/deeply/nested"', markup
107
94
  end
95
+
96
+ describe "with smart blob rendering" do
97
+ include Dolt::Html
98
+
99
+ before do
100
+ @helpers = [Dolt::View::SingleRepository,
101
+ Dolt::View::Breadcrumb,
102
+ Dolt::View::Blob,
103
+ Dolt::View::SmartBlobRenderer]
104
+ end
105
+
106
+ it "renders markdown as html" do
107
+ markup = render("file.md", Blob.new("# Cool"), {}, @helpers)
108
+ assert_equal 1, select(markup, "h1").length
109
+ end
110
+
111
+ it "syntax highlights ruby" do
112
+ blob = Blob.new("class Person\n attr_reader :name\nend")
113
+ markup = render("file.rb", blob, {}, @helpers)
114
+
115
+ assert_equal 0, select(markup, "h1").length
116
+ end
117
+ end
108
118
  end