bike 0.2.1

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 (151) hide show
  1. data/LICENSE +19 -0
  2. data/README.rdoc +124 -0
  3. data/bin/bike +35 -0
  4. data/lib/_error.rb +14 -0
  5. data/lib/_field.rb +260 -0
  6. data/lib/_i18n.rb +144 -0
  7. data/lib/_parser.rb +256 -0
  8. data/lib/_path.rb +86 -0
  9. data/lib/_storage/_storage.rb +215 -0
  10. data/lib/_storage/file.rb +201 -0
  11. data/lib/_storage/sequel.rb +174 -0
  12. data/lib/_storage/temp.rb +73 -0
  13. data/lib/_widget/action_create.rb +23 -0
  14. data/lib/_widget/action_login.rb +22 -0
  15. data/lib/_widget/action_signup.rb +16 -0
  16. data/lib/_widget/action_update.rb +16 -0
  17. data/lib/_widget/crumb.rb +24 -0
  18. data/lib/_widget/done.rb +16 -0
  19. data/lib/_widget/login.rb +25 -0
  20. data/lib/_widget/me.rb +31 -0
  21. data/lib/_widget/message.rb +51 -0
  22. data/lib/_widget/navi.rb +88 -0
  23. data/lib/_widget/submit.rb +49 -0
  24. data/lib/_widget/view_ym.rb +77 -0
  25. data/lib/_workflow/_workflow.rb +89 -0
  26. data/lib/_workflow/attachment.rb +50 -0
  27. data/lib/_workflow/blog.rb +28 -0
  28. data/lib/_workflow/contact.rb +23 -0
  29. data/lib/_workflow/forum.rb +26 -0
  30. data/lib/_workflow/register.rb +39 -0
  31. data/lib/bike.rb +396 -0
  32. data/lib/meta/_meta.rb +20 -0
  33. data/lib/meta/group.rb +19 -0
  34. data/lib/meta/id.rb +59 -0
  35. data/lib/meta/owner.rb +21 -0
  36. data/lib/meta/timestamp.rb +118 -0
  37. data/lib/scalar/checkbox.rb +68 -0
  38. data/lib/scalar/file.rb +144 -0
  39. data/lib/scalar/img.rb +112 -0
  40. data/lib/scalar/password.rb +58 -0
  41. data/lib/scalar/radio.rb +47 -0
  42. data/lib/scalar/select.rb +47 -0
  43. data/lib/scalar/text.rb +38 -0
  44. data/lib/scalar/textarea.rb +35 -0
  45. data/lib/scalar/textarea_pre.rb +14 -0
  46. data/lib/scalar/textarea_wiki.rb +173 -0
  47. data/lib/set/_set.rb +196 -0
  48. data/lib/set/dynamic.rb +177 -0
  49. data/lib/set/static.rb +102 -0
  50. data/lib/set/static_folder.rb +96 -0
  51. data/locale/en/index.po +242 -0
  52. data/locale/index.pot +243 -0
  53. data/locale/ja/index.po +242 -0
  54. data/locale/lazy_parser.rb +54 -0
  55. data/skel/config.ru +27 -0
  56. data/skel/skin/_users/00000000_frank-avatar.jpg +0 -0
  57. data/skel/skin/_users/00000000_frank-avatar_small.jpg +0 -0
  58. data/skel/skin/_users/00000000_frank.yaml +12 -0
  59. data/skel/skin/_users/00000000_root-avatar.jpg +0 -0
  60. data/skel/skin/_users/00000000_root-avatar_small.jpg +0 -0
  61. data/skel/skin/_users/00000000_root.yaml +11 -0
  62. data/skel/skin/_users/css/users.css +21 -0
  63. data/skel/skin/_users/css/users.less +25 -0
  64. data/skel/skin/_users/done.html +42 -0
  65. data/skel/skin/_users/index.html +46 -0
  66. data/skel/skin/_users/index.yaml +3 -0
  67. data/skel/skin/_users/summary.html +40 -0
  68. data/skel/skin/css/base.css +93 -0
  69. data/skel/skin/css/base.less +139 -0
  70. data/skel/skin/css/coax.css +199 -0
  71. data/skel/skin/css/coax.less +244 -0
  72. data/skel/skin/examples/blog/20091214_0001.yaml +8 -0
  73. data/skel/skin/examples/blog/20100630_0001.yaml +8 -0
  74. data/skel/skin/examples/blog/20100630_0002.yaml +14 -0
  75. data/skel/skin/examples/blog/20100701_0001.yaml +8 -0
  76. data/skel/skin/examples/blog/20100701_0002-a-20100701_0001-f.jpg +0 -0
  77. data/skel/skin/examples/blog/20100701_0002-a-20100701_0001-f_small.jpg +0 -0
  78. data/skel/skin/examples/blog/20100701_0002.yaml +19 -0
  79. data/skel/skin/examples/blog/frank/20100701_0001.yaml +10 -0
  80. data/skel/skin/examples/blog/frank/index.yaml +4 -0
  81. data/skel/skin/examples/blog/index.html +51 -0
  82. data/skel/skin/examples/blog/rss.xml +18 -0
  83. data/skel/skin/examples/contact/20100701_0001-file.txt +1 -0
  84. data/skel/skin/examples/contact/20100701_0001.yaml +15 -0
  85. data/skel/skin/examples/contact/20100701_0002.yaml +8 -0
  86. data/skel/skin/examples/contact/20100701_0003.yaml +9 -0
  87. data/skel/skin/examples/contact/index.html +47 -0
  88. data/skel/skin/examples/contact/js/contact.js +13 -0
  89. data/skel/skin/examples/contact/summary.html +54 -0
  90. data/skel/skin/examples/forum/20100701_0001.yaml +41 -0
  91. data/skel/skin/examples/forum/20100701_0002.yaml +25 -0
  92. data/skel/skin/examples/forum/index.html +68 -0
  93. data/skel/skin/examples/forum/summary.html +47 -0
  94. data/skel/skin/examples/index.html +73 -0
  95. data/skel/skin/index.html +39 -0
  96. data/skel/skin/js/base.js +50 -0
  97. data/t/locale/de/index.po +19 -0
  98. data/t/locale/en-GB/index.po +25 -0
  99. data/t/locale/ja/index.po +30 -0
  100. data/t/skin/_users/00000000_test.yaml +3 -0
  101. data/t/skin/_users/index.html +13 -0
  102. data/t/skin/foo/20091120_0001.yaml +7 -0
  103. data/t/skin/foo/bar/20091120_0001.yaml +5 -0
  104. data/t/skin/foo/bar/index.yaml +5 -0
  105. data/t/skin/foo/baz/css/baz.css +1 -0
  106. data/t/skin/foo/css/foo.css +1 -0
  107. data/t/skin/foo/index.html +14 -0
  108. data/t/skin/foo/index.yaml +7 -0
  109. data/t/skin/foo/not_css/foo.css +1 -0
  110. data/t/skin/foo/qux/index.html +8 -0
  111. data/t/skin/foo/qux/moo/index.html +6 -0
  112. data/t/skin/foo/sub-20100306_0001.yaml +3 -0
  113. data/t/skin/index.yaml +3 -0
  114. data/t/skin/t_attachment/index.html +13 -0
  115. data/t/skin/t_contact/done.html +6 -0
  116. data/t/skin/t_contact/index.html +9 -0
  117. data/t/skin/t_file/index.html +16 -0
  118. data/t/skin/t_img/index.html +14 -0
  119. data/t/skin/t_img/test.jpg +0 -0
  120. data/t/skin/t_select/index.html +9 -0
  121. data/t/skin/t_store/index.html +9 -0
  122. data/t/skin/t_summary/20100326_0001.yaml +3 -0
  123. data/t/skin/t_summary/create.html +9 -0
  124. data/t/skin/t_summary/index.html +9 -0
  125. data/t/skin/t_summary/summary.html +9 -0
  126. data/t/t.rb +27 -0
  127. data/t/test_bike.rb +768 -0
  128. data/t/test_call.rb +1281 -0
  129. data/t/test_checkbox.rb +273 -0
  130. data/t/test_field.rb +330 -0
  131. data/t/test_file.rb +900 -0
  132. data/t/test_i18n.rb +325 -0
  133. data/t/test_id.rb +215 -0
  134. data/t/test_img.rb +328 -0
  135. data/t/test_meta.rb +57 -0
  136. data/t/test_parser.rb +1516 -0
  137. data/t/test_password.rb +188 -0
  138. data/t/test_radio.rb +226 -0
  139. data/t/test_role.rb +249 -0
  140. data/t/test_select.rb +182 -0
  141. data/t/test_set_complex.rb +527 -0
  142. data/t/test_set_dynamic.rb +1504 -0
  143. data/t/test_set_folder.rb +515 -0
  144. data/t/test_set_permit.rb +246 -0
  145. data/t/test_set_static.rb +468 -0
  146. data/t/test_storage.rb +915 -0
  147. data/t/test_text.rb +125 -0
  148. data/t/test_textarea.rb +138 -0
  149. data/t/test_timestamp.rb +473 -0
  150. data/t/test_workflow.rb +367 -0
  151. metadata +347 -0
@@ -0,0 +1,328 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009-2010 Akira FUNAI
5
+
6
+ require "#{::File.dirname __FILE__}/t"
7
+
8
+ class TC_Img < Test::Unit::TestCase
9
+
10
+ def setup
11
+ Bike.current[:uri] = nil
12
+
13
+ File.open('t/skin/t_img/test.jpg') {|f|
14
+ @img = f.read
15
+ @file = Tempfile.open('tc_img')
16
+ @file << @img
17
+ }
18
+
19
+ meta = nil
20
+ Bike::Parser.gsub_scalar('$(foo img 32*32 1..100000 jpg, gif, png crop)') {|id, m|
21
+ meta = m
22
+ ''
23
+ }
24
+ @f = Bike::Field.instance meta.merge(:id => 'foo')
25
+ end
26
+
27
+ def test_meta
28
+ assert_equal(
29
+ 1,
30
+ @f[:min],
31
+ 'Img#initialize should set :min from the range token'
32
+ )
33
+ assert_equal(
34
+ 100000,
35
+ @f[:max],
36
+ 'Img#initialize should set :max from the range token'
37
+ )
38
+ assert_equal(
39
+ ['jpg', 'gif', 'png'],
40
+ @f[:options],
41
+ 'Img#initialize should set :options from the csv token'
42
+ )
43
+ assert_equal(
44
+ true,
45
+ @f[:crop],
46
+ 'Img#initialize should set :options from the csv token'
47
+ )
48
+ end
49
+
50
+ def test_val_cast_from_rack
51
+ @f.create(
52
+ :type => 'image/jpeg',
53
+ :tempfile => @file,
54
+ :head => <<'_eos',
55
+ Content-Disposition: form-data; name="t_img"; filename="baz.jpg"
56
+ Content-Type: image/jpeg
57
+ _eos
58
+ :filename => 'baz.jpg',
59
+ :name => 't_img'
60
+ )
61
+
62
+ assert_equal(
63
+ {
64
+ 'basename' => 'baz.jpg',
65
+ 'type' => 'image/jpeg',
66
+ 'size' => @file.length,
67
+ },
68
+ @f.val,
69
+ 'Img#val_cast should re-map a hash from Rack'
70
+ )
71
+ assert_equal(
72
+ @img,
73
+ @f.body,
74
+ 'Img#val_cast should store the file body in @body'
75
+ )
76
+ assert_equal(
77
+ @f.send(:_thumbnail, @file),
78
+ @f.thumbnail,
79
+ 'Img#val_cast should store the thumbnail in @thumbnail'
80
+ )
81
+ end
82
+
83
+ def test_val_cast_load
84
+ @f.load(
85
+ 'basename' => 'baz.jpg',
86
+ 'type' => 'image/jpeg',
87
+ 'size' => 123
88
+ )
89
+ assert_equal(
90
+ {
91
+ 'basename' => 'baz.jpg',
92
+ 'type' => 'image/jpeg',
93
+ 'size' => 123,
94
+ },
95
+ @f.val,
96
+ 'Img#val_cast should load() a hash without :tempfile like Set#load'
97
+ )
98
+ end
99
+
100
+ def test_large_thumbnail
101
+ @f[:width] = @f[:height] = 640
102
+ @f.create(
103
+ :type => 'image/jpeg',
104
+ :tempfile => @file,
105
+ :head => <<'_eos',
106
+ Content-Disposition: form-data; name="t_img"; filename="baz.jpg"
107
+ Content-Type: image/jpeg
108
+ _eos
109
+ :filename => 'baz.jpg',
110
+ :name => 't_img'
111
+ )
112
+ assert_not_equal(
113
+ 0,
114
+ @f.instance_variable_get(:@thumbnail).to_s.size,
115
+ 'Img#_thumbnail should make a thumbnail larger than the original img'
116
+ )
117
+ end
118
+
119
+ def test_get
120
+ Bike.client = 'root'
121
+
122
+ @f[:parent] = Bike::Set::Static::Folder.root.item('t_img', 'main')
123
+ Bike.current[:base] = @f[:parent]
124
+ tid = @f[:parent][:tid]
125
+
126
+ @f.load({})
127
+ assert_equal(
128
+ <<'_html'.chomp,
129
+ <span class="dummy_img" style="width: 32px; height: 32px;"></span>
130
+ _html
131
+ @f.get,
132
+ 'Img#get should return default span when the val is empty'
133
+ )
134
+
135
+ @f.load(
136
+ 'basename' => 'baz.jpg',
137
+ 'type' => 'image/jpeg',
138
+ 'size' => 12
139
+ )
140
+ assert_equal(
141
+ <<'_html'.chomp,
142
+ <a href="/t_img/main/foo/baz.jpg"><img src="/t_img/main/foo/baz_small.jpg" alt="baz.jpg" /></a>
143
+ _html
144
+ @f.get,
145
+ 'Img#get should return proper string'
146
+ )
147
+ assert_equal(
148
+ <<"_html",
149
+ <span class="img">
150
+ <a href="/t_img/#{tid}/foo/baz.jpg"><img src="/t_img/#{tid}/foo/baz_small.jpg" alt="baz.jpg" /></a>
151
+ <input type="file" name="foo" size="" class="file" />
152
+ </span>
153
+ _html
154
+ @f.get(:action => :update),
155
+ 'Img#get should return proper string'
156
+ )
157
+
158
+ @f.load(
159
+ 'basename' => '<baz>.jpg',
160
+ 'type' => 'image/<jpeg>',
161
+ 'size' => 12
162
+ )
163
+ assert_equal(
164
+ <<'_html'.chomp,
165
+ <a href="/t_img/main/foo/&lt;baz&gt;.jpg"><img src="/t_img/main/foo/&lt;baz&gt;_small.jpg" alt="&lt;baz&gt;.jpg" /></a>
166
+ _html
167
+ @f.get,
168
+ 'Img#get should escape the special characters in file information'
169
+ )
170
+ end
171
+
172
+ def test_get_not_image
173
+ @f.create(
174
+ :type => 'text/plain',
175
+ :tempfile => @file,
176
+ :head => <<'_eos',
177
+ Content-Disposition: form-data; name="t_img"; filename="baz.txt"
178
+ Content-Type: text/plain
179
+ _eos
180
+ :filename => 'baz.txt',
181
+ :name => 't_img'
182
+ )
183
+ assert_equal(
184
+ '<a href="foo/baz.txt">baz.txt (3535 bytes)</a>',
185
+ @f.get,
186
+ 'Img#get should fall back to File#get if the file is not an image'
187
+ )
188
+ end
189
+
190
+ def test_call_body
191
+ Bike.client = 'root'
192
+ sd = Bike::Set::Static::Folder.root.item('t_img', 'main')
193
+ sd.storage.clear
194
+
195
+ # post a multipart request
196
+ input = <<"_eos".gsub(/\r?\n/, "\r\n").sub('@img', @img)
197
+ ---foobarbaz
198
+ Content-Disposition: form-data; name="_1-foo"; filename="foo.jpg"
199
+ Content-Type: image/jpeg
200
+ Content-Transfer-Encoding: binary
201
+
202
+ @img
203
+ ---foobarbaz
204
+ Content-Disposition: form-data; name="_token"
205
+
206
+ #{Bike.token}
207
+ ---foobarbaz--
208
+ _eos
209
+ res = Rack::MockRequest.new(Bike.new).post(
210
+ 'http://example.com/t_img/main/update.html',
211
+ {
212
+ :input => input,
213
+ 'CONTENT_TYPE' => 'multipart/form-data; boundary=-foobarbaz',
214
+ 'CONTENT_LENGTH' => input.respond_to?(:bytesize) ? input.bytesize : input.size,
215
+ }
216
+ )
217
+ tid = res.headers['Location'][Bike::REX::TID]
218
+
219
+ # commit the base
220
+ res = Rack::MockRequest.new(Bike.new).post(
221
+ "http://example.com/#{tid}/update.html",
222
+ {
223
+ :input => ".status-public=create&_token=#{Bike.token}",
224
+ }
225
+ )
226
+
227
+ res.headers['Location'] =~ Bike::REX::PATH_ID
228
+ new_id = sprintf('%.8d_%.4d', $1, $2)
229
+
230
+ res = Rack::MockRequest.new(Bike.new).get(
231
+ "http://example.com/t_img/#{new_id}/foo/foo.jpg"
232
+ )
233
+ assert_equal(
234
+ 'image/jpeg',
235
+ res.headers['Content-Type'],
236
+ 'Bike#call to a img item should return the mime type of the file'
237
+ )
238
+ assert_equal(
239
+ @img.respond_to?(:bytesize) ? @img.bytesize : @img.size,
240
+ res.body.respond_to?(:bytesize) ? res.body.bytesize : res.body.size,
241
+ 'Bike#call to a img item should return the binary body of the file'
242
+ )
243
+
244
+ res = Rack::MockRequest.new(Bike.new).get(
245
+ "http://example.com/t_img/#{new_id}/foo/foo_small.jpg"
246
+ )
247
+ assert_equal(
248
+ 'image/jpeg',
249
+ res.headers['Content-Type'],
250
+ "Bike#call to 'file-small.*' should return the thumbnail of the file"
251
+ )
252
+ @file.rewind
253
+ assert_equal(
254
+ @f.send(:_thumbnail, @file).size,
255
+ res.body.size,
256
+ "Bike#call to 'file-small.*' should return the thumbnail of the file"
257
+ )
258
+
259
+ # delete
260
+ Rack::MockRequest.new(Bike.new).post(
261
+ 'http://example.com/t_img/update.html',
262
+ {
263
+ :input => "#{new_id}.action=delete&.status-public=delete&_token=#{Bike.token}",
264
+ }
265
+ )
266
+ res = Rack::MockRequest.new(Bike.new).get(
267
+ "http://example.com/t_img/#{new_id}/foo/foo.jpg"
268
+ )
269
+ assert_equal(
270
+ 404,
271
+ res.status,
272
+ 'Bike#call should delete child files as well'
273
+ )
274
+ res = Rack::MockRequest.new(Bike.new).get(
275
+ "http://example.com/t_img/#{new_id}/foo/foo_small.jpg"
276
+ )
277
+ assert_equal(
278
+ 404,
279
+ res.status,
280
+ 'Bike#call should delete child files as well'
281
+ )
282
+ end
283
+
284
+ def test_errors
285
+ File.open('t/skin/t_img/index.html') {|f|
286
+ @img = f.read
287
+ @file = Tempfile.open('tc_img')
288
+ @file << @img
289
+ }
290
+ @f.create(
291
+ :type => 'image/jpeg',
292
+ :tempfile => @file,
293
+ :head => <<'_eos',
294
+ Content-Disposition: form-data; name="t_img"; filename="baz.jpg"
295
+ Content-Type: image/jpeg
296
+ _eos
297
+ :filename => 'baz.jpg',
298
+ :name => 't_img'
299
+ )
300
+ assert_equal(
301
+ ['wrong file type: should be jpg/gif/png'],
302
+ @f.errors,
303
+ "Img#errors should regard quick_magick errors as 'wrong file type'"
304
+ )
305
+
306
+ File.open('t/skin/t_img/test.jpg') {|f|
307
+ @img = f.read
308
+ @file = Tempfile.open('tc_img')
309
+ @file << @img
310
+ }
311
+ @f.update(
312
+ :type => 'image/jpeg',
313
+ :tempfile => @file,
314
+ :head => <<'_eos',
315
+ Content-Disposition: form-data; name="t_img"; filename="baz.jpg"
316
+ Content-Type: image/jpeg
317
+ _eos
318
+ :filename => 'baz.jpg',
319
+ :name => 't_img'
320
+ )
321
+ assert_equal(
322
+ [],
323
+ @f.errors,
324
+ "Img#errors should raise no errors for good imgs"
325
+ )
326
+ end
327
+
328
+ end
@@ -0,0 +1,57 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ require "#{::File.dirname __FILE__}/t"
7
+
8
+ class TC_Meta < Test::Unit::TestCase
9
+
10
+ def setup
11
+ end
12
+
13
+ def teardown
14
+ end
15
+
16
+ def test_owner
17
+ ss = Bike::Set::Static.new(
18
+ :item => {
19
+ '_owner' => {:klass => 'meta-owner'},
20
+ }
21
+ )
22
+ assert_instance_of(
23
+ Bike::Meta::Owner,
24
+ ss.item('_owner'),
25
+ "Set::Static#item('_owner') should be an instance of the meta field"
26
+ )
27
+
28
+ ss.item('_owner').load 'frank'
29
+ assert_equal(
30
+ 'frank',
31
+ ss.val('_owner'),
32
+ 'Meta::Owner#load should work like normal fields'
33
+ )
34
+ assert_equal(
35
+ 'frank',
36
+ ss[:owner],
37
+ 'Meta::Owner#load should update the [:owner] of the parent set'
38
+ )
39
+
40
+ ss.item('_owner').update 'carl'
41
+ assert_equal(
42
+ 'frank',
43
+ ss.val('_owner'),
44
+ 'Meta::Owner should not be updated'
45
+ )
46
+ assert(
47
+ !ss.item('_owner').pending?,
48
+ 'Meta::Owner should not be updated'
49
+ )
50
+ assert_equal(
51
+ 'frank',
52
+ ss[:owner],
53
+ 'Meta::Owner should not be updated'
54
+ )
55
+ end
56
+
57
+ end
@@ -0,0 +1,1516 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ require "#{::File.dirname __FILE__}/t"
7
+
8
+ class TC_Parser < Test::Unit::TestCase
9
+
10
+ def setup
11
+ end
12
+
13
+ def teardown
14
+ end
15
+
16
+ def test_scan_tokens
17
+ assert_equal(
18
+ {:tokens => ['foo', 'bar', 'baz']},
19
+ Bike::Parser.scan_tokens(StringScanner.new('foo bar baz')),
20
+ 'Parser.scan_tokens should be able to parse unquoted tokens into array'
21
+ )
22
+ assert_equal(
23
+ {:tokens => ['foo', 'bar', 'baz baz']},
24
+ Bike::Parser.scan_tokens(StringScanner.new('foo "bar" "baz baz"')),
25
+ 'Parser.scan_tokens should be able to parse quoted tokens'
26
+ )
27
+ assert_equal(
28
+ {:tokens => ['foo', 'bar', 'baz']},
29
+ Bike::Parser.scan_tokens(StringScanner.new("foo 'bar' baz")),
30
+ 'Parser.scan_tokens should be able to parse quoted tokens'
31
+ )
32
+
33
+ assert_equal(
34
+ {:tokens => ['foo', 'bar', 'baz']},
35
+ Bike::Parser.scan_tokens(StringScanner.new("foo 'bar' baz) qux")),
36
+ 'Parser.scan_tokens should stop scanning at an ending bracket'
37
+ )
38
+ assert_equal(
39
+ {:tokens => ['foo', 'bar (bar?)', 'baz']},
40
+ Bike::Parser.scan_tokens(StringScanner.new("foo 'bar (bar?)' baz) qux")),
41
+ 'Parser.scan_tokens should ignore brackets inside quoted tokens'
42
+ )
43
+ end
44
+
45
+ def test_parse_empty_tag
46
+ result = Bike::Parser.parse_html('hello $(foo = bar "baz baz") world')
47
+ assert_equal(
48
+ {'foo' => {:klass => 'bar', :tokens => ['baz baz']}},
49
+ result[:item],
50
+ 'Parser.parse_html should be able to parse empty bike tags'
51
+ )
52
+ assert_equal(
53
+ {:index => 'hello $(foo) world'},
54
+ result[:tmpl],
55
+ 'Parser.parse_html[:tmpl] should be a proper template'
56
+ )
57
+
58
+ result = Bike::Parser.parse_html <<'_html'
59
+ <h1>$(foo=bar "baz baz")</h1>
60
+ <p>$(bar=a b c)</p>
61
+ _html
62
+ assert_equal(
63
+ {
64
+ 'foo' => {:klass => 'bar', :tokens => ['baz baz']},
65
+ 'bar' => {:klass => 'a', :tokens => ['b', 'c']},
66
+ },
67
+ result[:item],
68
+ 'Parser.parse_html should be able to parse empty bike tags'
69
+ )
70
+ assert_equal(
71
+ {:index => <<'_html'},
72
+ <h1>$(foo)</h1>
73
+ <p>$(bar)</p>
74
+ _html
75
+ result[:tmpl],
76
+ 'Parser.parse_html[:tmpl] should be a proper template'
77
+ )
78
+ end
79
+
80
+ def test_parse_empty_tag_in_comment
81
+ html = 'hello <!-- $(foo = bar "baz baz") --> world'
82
+ result = Bike::Parser.parse_html html
83
+ assert_equal(
84
+ {},
85
+ result[:item],
86
+ 'Parser.parse_html should skip bike tags in a comment'
87
+ )
88
+ assert_equal(
89
+ {:index => html},
90
+ result[:tmpl],
91
+ 'Parser.parse_html should skip bike tags in a comment'
92
+ )
93
+
94
+ html = '<script><![CDATA[ $(foo = bar "baz baz") ]]></script>'
95
+ result = Bike::Parser.parse_html html
96
+ assert_equal(
97
+ {},
98
+ result[:item],
99
+ 'Parser.parse_html should skip bike tags in a comment'
100
+ )
101
+ assert_equal(
102
+ {:index => html},
103
+ result[:tmpl],
104
+ 'Parser.parse_html should skip bike tags in a comment'
105
+ )
106
+ end
107
+
108
+ def test_obscure_markup
109
+ result = Bike::Parser.parse_html('hello $(foo = bar $(baz=1) baz) world')
110
+ assert_equal(
111
+ {'foo' => {:klass => 'bar', :tokens => ['$(baz=1']}},
112
+ result[:item],
113
+ 'Parser.parse_html should not parse nested empty tag'
114
+ )
115
+ assert_equal(
116
+ {:index => 'hello $(foo) baz) world'},
117
+ result[:tmpl],
118
+ 'Parser.parse_html[:tmpl] should be a proper template'
119
+ )
120
+
121
+ result = Bike::Parser.parse_html('hello $(foo = bar baz world')
122
+ assert_equal(
123
+ {'foo' => {:klass => 'bar', :tokens => ['baz', 'world']}},
124
+ result[:item],
125
+ 'Parser.parse_html should be able to parse a tag that is not closed'
126
+ )
127
+ assert_equal(
128
+ {:index => 'hello $(foo)'},
129
+ result[:tmpl],
130
+ 'Parser.parse_html should be able to parse a tag that is not closed'
131
+ )
132
+
133
+ result = Bike::Parser.parse_html('hello $(foo = bar "baz"world)')
134
+ assert_equal(
135
+ {'foo' => {:klass => 'bar', :tokens => ['baz', 'world']}},
136
+ result[:item],
137
+ 'Parser.parse_html should be able to parse tokens without a delimiter'
138
+ )
139
+ assert_equal(
140
+ {:index => 'hello $(foo)'},
141
+ result[:tmpl],
142
+ 'Parser.parse_html should be able to parse tokens without a delimiter'
143
+ )
144
+
145
+ result = Bike::Parser.parse_html('hello $(foo = bar, "baz")')
146
+ assert_equal(
147
+ {'foo' => {:klass => 'bar', :options => ['baz']}},
148
+ result[:item],
149
+ 'The first token should be regarded as [:klass]'
150
+ )
151
+ end
152
+
153
+ def test_parse_token
154
+ assert_equal(
155
+ {:width => 160, :height => 120},
156
+ Bike::Parser.parse_token(nil, '160*120', {}),
157
+ 'Parser.parse_token should be able to parse dimension tokens'
158
+ )
159
+ assert_equal(
160
+ {:min => 1, :max => 32},
161
+ Bike::Parser.parse_token(nil, '1..32', {}),
162
+ 'Parser.parse_token should be able to parse range tokens'
163
+ )
164
+ assert_equal(
165
+ {:max => 32},
166
+ Bike::Parser.parse_token(nil, '..32', {}),
167
+ 'Parser.parse_token should be able to parse partial range tokens'
168
+ )
169
+ assert_equal(
170
+ {:min => 1},
171
+ Bike::Parser.parse_token(nil, '1..', {}),
172
+ 'Parser.parse_token should be able to parse partial range tokens'
173
+ )
174
+ assert_equal(
175
+ {:min => -32, :max => -1},
176
+ Bike::Parser.parse_token(nil, '-32..-1', {}),
177
+ 'Parser.parse_token should be able to parse minus range tokens'
178
+ )
179
+
180
+ assert_equal(
181
+ {:options => ['foo']},
182
+ Bike::Parser.parse_token(',', 'foo', {}),
183
+ 'Parser.parse_token should be able to parse option tokens'
184
+ )
185
+ assert_equal(
186
+ {:options => ['foo', 'bar']},
187
+ Bike::Parser.parse_token(',', 'bar', {:options => ['foo']}),
188
+ 'Parser.parse_token should be able to parse option tokens'
189
+ )
190
+
191
+ assert_equal(
192
+ {:default => 'bar'},
193
+ Bike::Parser.parse_token(':', 'bar', {}),
194
+ 'Parser.parse_token should be able to parse default tokens'
195
+ )
196
+ assert_equal(
197
+ {:defaults => ['bar', 'baz']},
198
+ Bike::Parser.parse_token(';', 'baz', {:defaults => ['bar']}),
199
+ 'Parser.parse_token should be able to parse defaults tokens'
200
+ )
201
+ end
202
+
203
+ def test_parse_options
204
+ result = Bike::Parser.parse_html('hello $(foo = bar , "baz baz", "world", hi qux)')
205
+ assert_equal(
206
+ {'foo' => {:klass => 'bar', :options => ['baz baz', 'world', 'hi'], :tokens => ['qux']}},
207
+ result[:item],
208
+ 'Parser.parse_html should be able to parse a sequence of CSV'
209
+ )
210
+ result = Bike::Parser.parse_html('hello $(foo = bar "baz baz", "world", hi qux)')
211
+ assert_equal(
212
+ {'foo' => {:klass => 'bar', :options => ['baz baz', 'world', 'hi'], :tokens => ['qux']}},
213
+ result[:item],
214
+ 'Parser.parse_html should be able to parse a sequence of CSV'
215
+ )
216
+ end
217
+
218
+ def test_parse_options_with_spaces
219
+ result = Bike::Parser.parse_html('hello $(foo = bar world, qux)')
220
+ assert_equal(
221
+ {'foo' => {:klass => 'bar', :options => ['world', 'qux']}},
222
+ result[:item],
223
+ 'Parser.parse_html should allow spaces after the comma'
224
+ )
225
+ result = Bike::Parser.parse_html('hello $(foo = bar world , qux)')
226
+ assert_equal(
227
+ {'foo' => {:klass => 'bar', :options => ['qux'], :tokens => ['world']}},
228
+ result[:item],
229
+ 'Parser.parse_html should not allow spaces before the comma'
230
+ )
231
+ result = Bike::Parser.parse_html('hello $(foo = bar "baz baz", "world", hi qux)')
232
+ assert_equal(
233
+ {'foo' => {:klass => 'bar', :options => ['baz baz', 'world', 'hi'], :tokens => ['qux']}},
234
+ result[:item],
235
+ 'Parser.parse_html should allow spaces after the comma'
236
+ )
237
+
238
+ result = Bike::Parser.parse_html(<<'_eos')
239
+ hello $(foo =
240
+ bar
241
+ "baz baz",
242
+ "world",
243
+ hi
244
+ qux)
245
+ _eos
246
+ assert_equal(
247
+ {'foo' => {:klass => 'bar', :options => ['baz baz', 'world', 'hi'], :tokens => ['qux']}},
248
+ result[:item],
249
+ 'Parser.parse_html should allow spaces after the comma'
250
+ )
251
+ end
252
+
253
+ def test_parse_defaults
254
+ result = Bike::Parser.parse_html('hello $(foo = bar ;"baz baz";"world";hi qux)')
255
+ assert_equal(
256
+ {'foo' => {:klass => 'bar', :defaults => ['baz baz', 'world', 'hi'], :tokens => ['qux']}},
257
+ result[:item],
258
+ 'Parser.parse_html should be able to parse a sequence of CSV as [:defaults]'
259
+ )
260
+ result = Bike::Parser.parse_html('hello $(foo = bar "baz baz";"world";hi qux)')
261
+ assert_equal(
262
+ {'foo' => {:klass => 'bar', :defaults => ['baz baz', 'world', 'hi'], :tokens => ['qux']}},
263
+ result[:item],
264
+ 'Parser.parse_html should be able to parse a sequence of CSV as [:defaults]'
265
+ )
266
+ end
267
+
268
+ def test_parse_defaults_with_spaces
269
+ result = Bike::Parser.parse_html('hello $(foo=bar world; qux)')
270
+ assert_equal(
271
+ {'foo' => {:klass => 'bar', :defaults => ['world', 'qux']}},
272
+ result[:item],
273
+ 'Parser.parse_html should allow spaces after the semicolon'
274
+ )
275
+ result = Bike::Parser.parse_html('hello $(foo=bar world ;qux)')
276
+ assert_equal(
277
+ {'foo' => {:klass => 'bar', :defaults => ['qux'], :tokens => ['world']}},
278
+ result[:item],
279
+ 'Parser.parse_html should not allow spaces before the semicolon'
280
+ )
281
+ result = Bike::Parser.parse_html('hello $(foo=bar "baz baz"; "world"; hi qux)')
282
+ assert_equal(
283
+ {'foo' => {:klass => 'bar', :defaults => ['baz baz', 'world', 'hi'], :tokens => ['qux']}},
284
+ result[:item],
285
+ 'Parser.parse_html should allow spaces after the comma'
286
+ )
287
+
288
+ result = Bike::Parser.parse_html(<<'_eos')
289
+ hello $(foo =
290
+ bar
291
+ "baz baz";
292
+ "world";
293
+ hi
294
+ qux)
295
+ _eos
296
+ assert_equal(
297
+ {'foo' => {:klass => 'bar', :defaults => ['baz baz', 'world', 'hi'], :tokens => ['qux']}},
298
+ result[:item],
299
+ 'Parser.parse_html should allow spaces after the comma'
300
+ )
301
+ end
302
+
303
+ def test_parse_meta_tag
304
+ result = Bike::Parser.parse_html <<'_html'
305
+ <html>
306
+ <meta name="bike-owner" content="frank" />
307
+ </html>
308
+ _html
309
+ assert_equal(
310
+ {
311
+ :tmpl => {
312
+ :index => <<'_html',
313
+ <html>
314
+ </html>
315
+ _html
316
+ },
317
+ :item => {},
318
+ :owner => 'frank',
319
+ :label => nil,
320
+ },
321
+ result,
322
+ 'Parser.parse_html should scrape meta vals from <meta>'
323
+ )
324
+
325
+ result = Bike::Parser.parse_html <<'_html'
326
+ <html>
327
+ <meta name="bike-owner" content="frank" />
328
+ <meta name="bike-group" content="bob,carl" />
329
+ <meta name="bike-foo" content="bar, baz" />
330
+ <meta name="bike-label" content="Qux" />
331
+ </html>
332
+ _html
333
+ assert_equal(
334
+ {
335
+ :tmpl => {
336
+ :index => <<'_html',
337
+ <html>
338
+ </html>
339
+ _html
340
+ },
341
+ :item => {},
342
+ :owner => 'frank',
343
+ :group => %w(bob carl),
344
+ :foo => %w(bar baz),
345
+ :label => 'Qux',
346
+ },
347
+ result,
348
+ 'Parser.parse_html should scrape meta vals from <meta>'
349
+ )
350
+ end
351
+
352
+ def test_parse_duplicate_tag
353
+ result = Bike::Parser.parse_html('hello $(foo = bar "baz baz") world $(foo=boo) $(foo)!')
354
+ assert_equal(
355
+ {'foo' => {:klass => 'boo'}},
356
+ result[:item],
357
+ 'definition tags are overridden by a preceding definition'
358
+ )
359
+ assert_equal(
360
+ {:index => 'hello $(foo) world $(foo) $(foo)!'},
361
+ result[:tmpl],
362
+ 'Parser.parse_html[:tmpl] should be a proper template'
363
+ )
364
+ end
365
+
366
+ def test_scan_inner_html
367
+ s = StringScanner.new 'bar</foo>bar'
368
+ inner_html, close_tag = Bike::Parser.scan_inner_html(s, 'foo')
369
+ assert_equal(
370
+ 'bar',
371
+ inner_html,
372
+ 'Parser.scan_inner_html should extract the inner html from the scanner'
373
+ )
374
+ assert_equal(
375
+ '</foo>',
376
+ close_tag,
377
+ 'Parser.scan_inner_html should extract the inner html from the scanner'
378
+ )
379
+
380
+ s = StringScanner.new '<foo>bar</foo></foo>'
381
+ inner_html, close_tag = Bike::Parser.scan_inner_html(s, 'foo')
382
+ assert_equal(
383
+ '<foo>bar</foo>',
384
+ inner_html,
385
+ 'Parser.scan_inner_html should be aware of nested tags'
386
+ )
387
+
388
+ s = StringScanner.new "baz\n <foo>bar</foo>\n</foo>"
389
+ inner_html, close_tag = Bike::Parser.scan_inner_html(s, 'foo')
390
+ assert_equal(
391
+ "baz\n <foo>bar</foo>\n",
392
+ inner_html,
393
+ 'Parser.scan_inner_html should be aware of nested tags'
394
+ )
395
+ end
396
+
397
+ def test_scan_comment
398
+ s = StringScanner.new 'baz -->'
399
+ inner_html, close_tag = Bike::Parser.scan_inner_html(s, '!--')
400
+ assert_equal(
401
+ 'baz',
402
+ inner_html,
403
+ 'Parser.scan_inner_html should parse html comments'
404
+ )
405
+ assert_equal(
406
+ ' -->',
407
+ close_tag,
408
+ 'Parser.scan_inner_html should parse html comments'
409
+ )
410
+
411
+ s = StringScanner.new "baz\n <!--bar-->\n-->"
412
+ inner_html, close_tag = Bike::Parser.scan_inner_html(s, '!--')
413
+ assert_equal(
414
+ "baz\n <!--bar-->\n",
415
+ inner_html,
416
+ 'Parser.scan_inner_html should parse nested comments'
417
+ )
418
+ end
419
+
420
+ def test_scan_cdata
421
+ s = StringScanner.new 'baz ]]>'
422
+ inner_html, close_tag = Bike::Parser.scan_inner_html(s, '<![CDATA[')
423
+ assert_equal(
424
+ 'baz',
425
+ inner_html,
426
+ 'Parser.scan_inner_html should parse CDATA section'
427
+ )
428
+ assert_equal(
429
+ ' ]]>',
430
+ close_tag,
431
+ 'Parser.scan_inner_html should parse CDATA section'
432
+ )
433
+ end
434
+
435
+ def test_parse_block_tag
436
+ result = Bike::Parser.parse_html <<'_html'
437
+ <ul class="app-blog" id="foo"><li>hello</li></ul>
438
+ _html
439
+ assert_equal(
440
+ {
441
+ 'foo' => {
442
+ :klass => 'set-dynamic',
443
+ :workflow => 'blog',
444
+ :tmpl => {
445
+ :index => <<'_tmpl'.chomp,
446
+ <ul class="app-blog" id="@(name)">$()</ul>
447
+ $(.navi)$(.submit)$(.action_create)
448
+ _tmpl
449
+ },
450
+ :item => {
451
+ 'default' => {
452
+ :label => nil,
453
+ :tmpl => {:index => '<li>hello</li>'},
454
+ :item => {},
455
+ },
456
+ },
457
+ },
458
+ },
459
+ result[:item],
460
+ 'Parser.parse_html should be able to parse block bike tags'
461
+ )
462
+ assert_equal(
463
+ {:index => '$(foo.message)$(foo)'},
464
+ result[:tmpl],
465
+ 'Parser.parse_html[:tmpl] should be a proper template'
466
+ )
467
+
468
+ result = Bike::Parser.parse_html <<'_html'
469
+ <ul class="app-blog" id="foo">
470
+ <li>hello</li>
471
+ </ul>
472
+ _html
473
+ assert_equal(
474
+ {
475
+ 'foo' => {
476
+ :klass => 'set-dynamic',
477
+ :workflow => 'blog',
478
+ :tmpl => {
479
+ :index => <<'_tmpl'.chomp,
480
+ <ul class="app-blog" id="@(name)">
481
+ $()</ul>
482
+ $(.navi)$(.submit)$(.action_create)
483
+ _tmpl
484
+ },
485
+ :item => {
486
+ 'default' => {
487
+ :label => nil,
488
+ :tmpl => {:index => " <li>hello</li>\n"},
489
+ :item => {},
490
+ },
491
+ }
492
+ },
493
+ },
494
+ result[:item],
495
+ 'Parser.parse_html should be able to parse block bike tags'
496
+ )
497
+ assert_equal(
498
+ {:index => '$(foo.message)$(foo)'},
499
+ result[:tmpl],
500
+ 'Parser.parse_html[:tmpl] should be a proper template'
501
+ )
502
+
503
+ result = Bike::Parser.parse_html <<'_html'
504
+ hello <ul class="app-blog" id="foo"><li>hello</li></ul> world
505
+ _html
506
+ assert_equal(
507
+ {
508
+ 'foo' => {
509
+ :klass => 'set-dynamic',
510
+ :workflow => 'blog',
511
+ :tmpl => {
512
+ :index => <<'_tmpl'.chomp,
513
+ <ul class="app-blog" id="@(name)">$()</ul>$(.navi)$(.submit)$(.action_create)
514
+ _tmpl
515
+ },
516
+ :item => {
517
+ 'default' => {
518
+ :label => nil,
519
+ :tmpl => {:index => '<li>hello</li>'},
520
+ :item => {},
521
+ },
522
+ },
523
+ },
524
+ },
525
+ result[:item],
526
+ 'Parser.parse_html should be able to parse block bike tags'
527
+ )
528
+ assert_equal(
529
+ {:index => <<'_html'},
530
+ hello$(foo.message)$(foo) world
531
+ _html
532
+ result[:tmpl],
533
+ 'Parser.parse_html[:tmpl] should be a proper template'
534
+ )
535
+
536
+ result = Bike::Parser.parse_html <<'_html'
537
+ hello <!-- cruel --> <ul class="app-blog" id="foo"><li>hello</li></ul> world
538
+ _html
539
+ assert_equal(
540
+ {
541
+ 'foo' => {
542
+ :klass => 'set-dynamic',
543
+ :workflow => 'blog',
544
+ :tmpl => {
545
+ :index => <<'_tmpl'.chomp,
546
+ <ul class="app-blog" id="@(name)">$()</ul>$(.navi)$(.submit)$(.action_create)
547
+ _tmpl
548
+ },
549
+ :item => {
550
+ 'default' => {
551
+ :label => nil,
552
+ :tmpl => {:index => '<li>hello</li>'},
553
+ :item => {},
554
+ },
555
+ },
556
+ },
557
+ },
558
+ result[:item],
559
+ 'Parser.parse_html should be able to parse block bike tags'
560
+ )
561
+ assert_equal(
562
+ {:index => <<'_html'},
563
+ hello <!-- cruel -->$(foo.message)$(foo) world
564
+ _html
565
+ result[:tmpl],
566
+ 'Parser.parse_html[:tmpl] should be a proper template'
567
+ )
568
+ end
569
+
570
+ def test_parse_block_tag_in_comment
571
+ [
572
+ <<'_html',
573
+ hello <!--<ul class="app-blog" id="test1"><li>hello</li></ul>--> world
574
+ _html
575
+ <<'_html',
576
+ <!--
577
+ <ul class="app-blog" id="test2">
578
+ <li>hello</li>
579
+ </ul>
580
+ -->
581
+ _html
582
+ <<'_html',
583
+ foo <!--
584
+ <ul class="app-blog" id="test3">
585
+ <li>hello</li>
586
+ </ul>
587
+ --> bar
588
+ _html
589
+ <<'_html',
590
+ foo <!--
591
+ <ul class="app-blog" id="test4">
592
+ <li>hello</li>
593
+ </ul>
594
+ --> bar
595
+ _html
596
+ <<'_html',
597
+ foo <!--
598
+ <ul class="app-blog" id="test5">
599
+ <!-- may_preview -->
600
+ <li>hello</li>
601
+ </ul>
602
+ --> bar
603
+ _html
604
+ <<'_html',
605
+ <![CDATA[
606
+ <ul class="app-blog" id="test6">
607
+ <!-- may_preview -->
608
+ <li>hello</li>
609
+ </ul>
610
+ ]]>
611
+ _html
612
+ <<'_html',
613
+ <!--
614
+ <ul class="app-blog" id="test7">
615
+ <!-- may_preview -->
616
+ <li>hello</li>
617
+ </ul>
618
+ _html
619
+ ].each {|html|
620
+ result = Bike::Parser.parse_html html
621
+ assert_equal(
622
+ {},
623
+ result[:item],
624
+ 'Parser.parse_html should skip bike tags in a comment'
625
+ )
626
+ assert_equal(
627
+ {:index => html},
628
+ result[:tmpl],
629
+ 'Parser.parse_html should skip bike tags in a comment'
630
+ )
631
+ }
632
+ end
633
+
634
+ def test_parse_block_tag_obsolete_runo_class
635
+ result = Bike::Parser.parse_html <<'_html'
636
+ <ul class="runo-blog" id="foo"><li>hello</li></ul>
637
+ _html
638
+ assert_equal(
639
+ {
640
+ 'foo' => {
641
+ :klass => 'set-dynamic',
642
+ :workflow => 'blog',
643
+ :tmpl => {
644
+ :index => <<'_html'.chomp,
645
+ <ul class="runo-blog" id="@(name)">$()</ul>
646
+ $(.navi)$(.submit)$(.action_create)
647
+ _html
648
+ },
649
+ :item => {
650
+ 'default' => {
651
+ :label => nil,
652
+ :tmpl => {:index => '<li>hello</li>'},
653
+ :item => {},
654
+ },
655
+ },
656
+ },
657
+ },
658
+ result[:item],
659
+ 'Parser.parse_html should be able to parse old runo tags'
660
+ )
661
+ end
662
+
663
+ def test_parse_block_tag_obsolete_body_class
664
+ result = Bike::Parser.parse_html <<'_html'
665
+ <ul class="app-blog" id="foo"><div>oops.</div><li class="body">hello</li></ul>
666
+ _html
667
+ assert_equal(
668
+ {
669
+ 'foo' => {
670
+ :klass => 'set-dynamic',
671
+ :workflow => 'blog',
672
+ :tmpl => {
673
+ :index => <<'_html'.chomp,
674
+ <ul class="app-blog" id="@(name)"><div>oops.</div>$()</ul>
675
+ $(.navi)$(.submit)$(.action_create)
676
+ _html
677
+ },
678
+ :item => {
679
+ 'default' => {
680
+ :label => nil,
681
+ :tmpl => {:index => '<li class="body">hello</li>'},
682
+ :item => {},
683
+ },
684
+ },
685
+ },
686
+ },
687
+ result[:item],
688
+ 'Parser.parse_html should be able to parse block bike tags'
689
+ )
690
+ end
691
+
692
+ def test_look_a_like_block_tag
693
+ result = Bike::Parser.parse_html <<'_html'
694
+ hello <ul class="not-app-blog" id="foo"><li>hello</li></ul> world
695
+ _html
696
+ assert_equal(
697
+ {:index => <<'_tmpl'},
698
+ hello <ul class="not-app-blog" id="foo"><li>hello</li></ul> world
699
+ _tmpl
700
+ result[:tmpl],
701
+ "Parser.parse_html[:tmpl] should skip a class which does not start with 'bike'"
702
+ )
703
+ end
704
+
705
+ def test_block_tags_with_options
706
+ result = Bike::Parser.parse_html <<'_html'
707
+ hello
708
+ <table class="app-blog" id="foo">
709
+ <!-- 1..20 barbaz -->
710
+ <tbody class="model"><!-- qux --><tr><th>$(bar=text)</th><th>$(baz=text)</th></tr></tbody>
711
+ </table>
712
+ world
713
+ _html
714
+ assert_equal(
715
+ {
716
+ 'foo' => {
717
+ :min => 1,
718
+ :max => 20,
719
+ :tokens => ['barbaz'],
720
+ :klass => 'set-dynamic',
721
+ :workflow => 'blog',
722
+ :tmpl => {
723
+ :index => <<'_tmpl'.chomp,
724
+ <table class="app-blog" id="@(name)">
725
+ <!-- 1..20 barbaz -->
726
+ $() </table>
727
+ $(.navi)$(.submit)$(.action_create)
728
+ _tmpl
729
+ },
730
+ :item => {
731
+ 'default' => {
732
+ :label => nil,
733
+ :tmpl => {
734
+ :index => <<'_tmpl',
735
+ <tbody class="model"><!-- qux --><tr><th>$(.a_update)$(bar)</a></th><th>$(baz)$(.hidden)</th></tr></tbody>
736
+ _tmpl
737
+ },
738
+ :item => {
739
+ 'bar' => {:klass => 'text'},
740
+ 'baz' => {:klass => 'text'},
741
+ },
742
+ },
743
+ },
744
+ },
745
+ },
746
+ result[:item],
747
+ 'Parser.parse_html should aware of <tbody class="model">'
748
+ )
749
+ end
750
+
751
+ def test_block_tags_with_tbody
752
+ result = Bike::Parser.parse_html <<'_html'
753
+ hello
754
+ <table class="app-blog" id="foo">
755
+ <thead><tr><th>BAR</th><th>BAZ</th></tr></thead>
756
+ <tbody class="model"><tr><th>$(bar=text)</th><th>$(baz=text)</th></tr></tbody>
757
+ </table>
758
+ world
759
+ _html
760
+ assert_equal(
761
+ {
762
+ 'foo' => {
763
+ :klass => 'set-dynamic',
764
+ :workflow => 'blog',
765
+ :tmpl => {
766
+ :index => <<'_tmpl'.chomp,
767
+ <table class="app-blog" id="@(name)">
768
+ <thead><tr><th>BAR</th><th>BAZ</th></tr></thead>
769
+ $() </table>
770
+ $(.navi)$(.submit)$(.action_create)
771
+ _tmpl
772
+ },
773
+ :item => {
774
+ 'default' => {
775
+ :label => nil,
776
+ :tmpl => {
777
+ :index => <<'_tmpl',
778
+ <tbody class="model"><tr><th>$(.a_update)$(bar)</a></th><th>$(baz)$(.hidden)</th></tr></tbody>
779
+ _tmpl
780
+ },
781
+ :item => {
782
+ 'bar' => {:klass => 'text'},
783
+ 'baz' => {:klass => 'text'},
784
+ },
785
+ },
786
+ },
787
+ },
788
+ },
789
+ result[:item],
790
+ 'Parser.parse_html should aware of <tbody class="model">'
791
+ )
792
+ assert_equal(
793
+ {:index => <<'_tmpl'},
794
+ hello
795
+ $(foo.message)$(foo)world
796
+ _tmpl
797
+ result[:tmpl],
798
+ 'Parser.parse_html[:tmpl] should be a proper template'
799
+ )
800
+ end
801
+
802
+ def test_parse_xml
803
+ result = Bike::Parser.parse_xml <<'_html'
804
+ <channel class="app-rss">
805
+ <link>@(href)</link>
806
+ <item class="model">
807
+ <title>$(title)</title>
808
+ </item>
809
+ </channel>
810
+ _html
811
+ assert_equal(
812
+ {
813
+ :label => nil,
814
+ :tmpl => {:index => '$(main)'},
815
+ :item => {
816
+ 'main' => {
817
+ :item => {
818
+ 'default' => {
819
+ :label => nil,
820
+ :item => {},
821
+ :tmpl => {
822
+ :index => <<'_xml'
823
+ <item>
824
+ <title>$(title)</title>
825
+ </item>
826
+ _xml
827
+ },
828
+ },
829
+ },
830
+ :tmpl => {
831
+ :index => <<'_xml',
832
+ <channel>
833
+ <link>@(href)</link>
834
+ $()</channel>
835
+ _xml
836
+ },
837
+ :klass => 'set-dynamic',
838
+ :workflow => 'rss',
839
+ }
840
+ },
841
+ },
842
+ result,
843
+ 'Parser.parse_html should aware of <item>'
844
+ )
845
+ end
846
+
847
+ def test_parse_item_label
848
+ result = Bike::Parser.parse_html <<'_html'
849
+ <ul class="app-blog" id="foo"><li title="Greeting">hello</li></ul>
850
+ _html
851
+ assert_equal(
852
+ 'Greeting',
853
+ result[:item]['foo'][:item]['default'][:label],
854
+ 'Parser.parse_html should pick up item labels from title attrs'
855
+ )
856
+
857
+ result = Bike::Parser.parse_html <<'_html'
858
+ <ul class="app-blog" id="foo"><!-- foo --><li title="Greeting">hello</li></ul>
859
+ _html
860
+ assert_equal(
861
+ 'Greeting',
862
+ result[:item]['foo'][:item]['default'][:label],
863
+ 'Parser.parse_html should pick up item labels from title attrs'
864
+ )
865
+
866
+ result = Bike::Parser.parse_html <<'_html'
867
+ <ul class="app-blog" id="foo"><!-- foo --><li><div title="Foo">hello</div></li></ul>
868
+ _html
869
+ assert_nil(
870
+ result[:item]['foo'][:item]['default'][:label],
871
+ 'Parser.parse_html should pick up item labels only from the first tags'
872
+ )
873
+ end
874
+
875
+ def test_parse_item_label_plural
876
+ result = Bike::Parser.parse_html <<'_html'
877
+ <ul class="app-blog" id="foo"><li title="tEntry, tEntries">hello</li></ul>
878
+ _html
879
+ assert_equal(
880
+ 'tEntry',
881
+ result[:item]['foo'][:item]['default'][:label],
882
+ 'Parser.parse_html should split plural item labels'
883
+ )
884
+ assert_equal(
885
+ ['tEntry', 'tEntries'],
886
+ Bike::I18n.msg['tEntry'],
887
+ 'Parser.parse_html should I18n.merge_msg! the plural item labels'
888
+ )
889
+
890
+ result = Bike::Parser.parse_html <<'_html'
891
+ <ul class="app-blog" id="foo"><li title="tFooFoo, BarBar, BazBaz">hello</li></ul>
892
+ _html
893
+ assert_equal(
894
+ 'tFooFoo',
895
+ result[:item]['foo'][:item]['default'][:label],
896
+ 'Parser.parse_html should split plural item labels'
897
+ )
898
+ assert_equal(
899
+ ['tFooFoo', 'BarBar', 'BazBaz'],
900
+ Bike::I18n.msg['tFooFoo'],
901
+ 'Parser.parse_html should I18n.merge_msg! the plural item labels'
902
+ )
903
+
904
+ result = Bike::Parser.parse_html <<'_html'
905
+ <ul class="app-blog" id="foo"><li title="tQux">hello</li></ul>
906
+ _html
907
+ assert_equal(
908
+ 'tQux',
909
+ result[:item]['foo'][:item]['default'][:label],
910
+ 'Parser.parse_html should split plural item labels'
911
+ )
912
+ assert_equal(
913
+ ['tQux', 'tQux', 'tQux', 'tQux'],
914
+ Bike::I18n.msg['tQux'],
915
+ 'Parser.parse_html should repeat a singular label to fill all possible plural forms'
916
+ )
917
+ end
918
+
919
+ def test_block_tags_with_nested_tbody
920
+ result = Bike::Parser.parse_html <<'_html'
921
+ hello
922
+ <table class="app-blog" id="foo">
923
+ <thead><tr><th>BAR</th><th>BAZ</th></tr></thead>
924
+ <tbody class="model"><tbody><tr><th>$(bar=text)</th><th>$(baz=text)</th></tr></tbody></tbody>
925
+ </table>
926
+ world
927
+ _html
928
+ assert_equal(
929
+ {
930
+ 'foo' => {
931
+ :klass => 'set-dynamic',
932
+ :workflow => 'blog',
933
+ :tmpl => {
934
+ :index => <<'_tmpl'.chomp,
935
+ <table class="app-blog" id="@(name)">
936
+ <thead><tr><th>BAR</th><th>BAZ</th></tr></thead>
937
+ $() </table>
938
+ $(.navi)$(.submit)$(.action_create)
939
+ _tmpl
940
+ },
941
+ :item => {
942
+ 'default' => {
943
+ :label => nil,
944
+ :tmpl => {
945
+ :index => <<'_tmpl',
946
+ <tbody class="model"><tbody><tr><th>$(.a_update)$(bar)</a></th><th>$(baz)$(.hidden)</th></tr></tbody></tbody>
947
+ _tmpl
948
+ },
949
+ :item => {
950
+ 'bar' => {:klass => 'text'},
951
+ 'baz' => {:klass => 'text'},
952
+ },
953
+ },
954
+ },
955
+ },
956
+ },
957
+ result[:item],
958
+ 'Parser.parse_html should aware of nested <tbody class="model">'
959
+ )
960
+ end
961
+
962
+ def test_nested_block_tags
963
+ result = Bike::Parser.parse_html <<'_html'
964
+ <ul class="app-blog" id="foo">
965
+ <li>
966
+ <ul class="app-blog" id="bar"><li>baz</li></ul>
967
+ </li>
968
+ </ul>
969
+ _html
970
+ assert_equal(
971
+ {
972
+ 'foo' => {
973
+ :klass => 'set-dynamic',
974
+ :workflow => 'blog',
975
+ :tmpl => {
976
+ :index => <<'_tmpl'.chomp,
977
+ <ul class="app-blog" id="@(name)">
978
+ $()</ul>
979
+ $(.navi)$(.submit)$(.action_create)
980
+ _tmpl
981
+ },
982
+ :item => {
983
+ 'default' => {
984
+ :label => nil,
985
+ :tmpl => {
986
+ :index => <<'_tmpl',
987
+ <li>
988
+ $(bar.message)$(.a_update)$(bar)$(.hidden)</a> </li>
989
+ _tmpl
990
+ },
991
+ :item => {
992
+ 'bar' => {
993
+ :klass => 'set-dynamic',
994
+ :workflow => 'blog',
995
+ :tmpl => {
996
+ :index => <<'_tmpl'.chomp,
997
+ <ul class="app-blog" id="@(name)">$()</ul>
998
+ $(.navi)$(.submit)$(.action_create)
999
+ _tmpl
1000
+ },
1001
+ :item => {
1002
+ 'default' => {
1003
+ :label => nil,
1004
+ :tmpl => {:index => '<li>baz</li>'},
1005
+ :item => {},
1006
+ },
1007
+ },
1008
+ },
1009
+ },
1010
+ },
1011
+ },
1012
+ },
1013
+ },
1014
+ result[:item],
1015
+ 'Parser.parse_html should be able to parse nested block bike tags'
1016
+ )
1017
+ assert_equal(
1018
+ {:index => '$(foo.message)$(foo)'},
1019
+ result[:tmpl],
1020
+ 'Parser.parse_html[:tmpl] should be a proper template'
1021
+ )
1022
+ end
1023
+
1024
+ def test_combination
1025
+ result = Bike::Parser.parse_html <<'_html'
1026
+ <html>
1027
+ <h1>$(title=text 32)</h1>
1028
+ <ul id="foo" class="app-blog">
1029
+ <li>
1030
+ $(subject=text 64)
1031
+ $(body=textarea 72*10)
1032
+ <ul><li>qux</li></ul>
1033
+ </li>
1034
+ </ul>
1035
+ </html>
1036
+ _html
1037
+ assert_equal(
1038
+ {
1039
+ 'title' => {:klass => 'text', :tokens => ['32']},
1040
+ 'foo' => {
1041
+ :klass => 'set-dynamic',
1042
+ :workflow => 'blog',
1043
+ :tmpl => {
1044
+ :index => <<'_tmpl'.chomp,
1045
+ <ul id="@(name)" class="app-blog">
1046
+ $() </ul>
1047
+ $(.navi)$(.submit)$(.action_create)
1048
+ _tmpl
1049
+ },
1050
+ :item => {
1051
+ 'default' => {
1052
+ :label => nil,
1053
+ :tmpl => {
1054
+ :index => <<'_tmpl',
1055
+ <li>
1056
+ $(.a_update)$(subject)</a>
1057
+ $(body)$(.hidden)
1058
+ <ul><li>qux</li></ul>
1059
+ </li>
1060
+ _tmpl
1061
+ },
1062
+ :item => {
1063
+ 'body' => {
1064
+ :width => 72,
1065
+ :height => 10,
1066
+ :klass => 'textarea',
1067
+ },
1068
+ 'subject' => {
1069
+ :tokens => ['64'],
1070
+ :klass => 'text',
1071
+ },
1072
+ },
1073
+ },
1074
+ },
1075
+ },
1076
+ },
1077
+ result[:item],
1078
+ 'Parser.parse_html should be able to parse combination of mixed bike tags'
1079
+ )
1080
+ assert_equal(
1081
+ {:index => <<'_tmpl'},
1082
+ <html>
1083
+ <h1>$(title)</h1>
1084
+ $(foo.message)$(foo)</html>
1085
+ _tmpl
1086
+ result[:tmpl],
1087
+ 'Parser.parse_html[:tmpl] should be a proper template'
1088
+ )
1089
+ end
1090
+
1091
+ def test_gsub_block
1092
+ match = nil
1093
+ result = Bike::Parser.gsub_block('a<div class="foo">bar</div>c', 'foo') {|open, inner, close|
1094
+ match = [open, inner, close]
1095
+ 'b'
1096
+ }
1097
+ assert_equal(
1098
+ 'abc',
1099
+ result,
1100
+ 'Parser.gsub_block should replace tag blocks of the matching class with the given value'
1101
+ )
1102
+ assert_equal(
1103
+ ['<div class="foo">', 'bar', '</div>'],
1104
+ match,
1105
+ 'Parser.gsub_block should pass the matching element to its block'
1106
+ )
1107
+
1108
+ result = Bike::Parser.gsub_block('<p><div class="foo">bar</div></p>', 'foo') {|open, inner, close|
1109
+ match = [open, inner, close]
1110
+ 'b'
1111
+ }
1112
+ assert_equal(
1113
+ '<p>b</p>',
1114
+ result,
1115
+ 'Parser.gsub_block should replace tag blocks of the matching class with the given value'
1116
+ )
1117
+ assert_equal(
1118
+ ['<div class="foo">', 'bar', '</div>'],
1119
+ match,
1120
+ 'Parser.gsub_block should pass the matching element to its block'
1121
+ )
1122
+
1123
+ result = Bike::Parser.gsub_block('a<p><div class="foo">bar</div></p>c', 'foo') {|open, inner, close|
1124
+ match = [open, inner, close]
1125
+ 'b'
1126
+ }
1127
+ assert_equal(
1128
+ 'a<p>b</p>c',
1129
+ result,
1130
+ 'Parser.gsub_block should replace tag blocks of the matching class with the given value'
1131
+ )
1132
+ assert_equal(
1133
+ ['<div class="foo">', 'bar', '</div>'],
1134
+ match,
1135
+ 'Parser.gsub_block should pass the matching element to its block'
1136
+ )
1137
+ end
1138
+
1139
+ def _test_gsub_action_tmpl(html)
1140
+ result = {}
1141
+ html = Bike::Parser.gsub_action_tmpl(html) {|id, action, *tmpl|
1142
+ result[:id] = id
1143
+ result[:action] = action
1144
+ result[:tmpl] = tmpl.join
1145
+ 'b'
1146
+ }
1147
+ [result, html]
1148
+ end
1149
+
1150
+ def test_gsub_action_tmpl
1151
+ result, html = _test_gsub_action_tmpl 'a<div class="foo-navi">Foo</div>c'
1152
+ assert_equal(
1153
+ {
1154
+ :id => 'foo',
1155
+ :action => :navi,
1156
+ :tmpl => '<div class="foo-navi">Foo</div>',
1157
+ },
1158
+ result,
1159
+ 'Parser.gsub_action_tmpl should yield action templates'
1160
+ )
1161
+ assert_equal(
1162
+ 'abc',
1163
+ html,
1164
+ 'Parser.gsub_action_tmpl should replace the action template with a value from the block'
1165
+ )
1166
+
1167
+ result, html = _test_gsub_action_tmpl 'a<div class="bar foo-navi">Foo</div>c'
1168
+ assert_equal(
1169
+ {
1170
+ :id => 'foo',
1171
+ :action => :navi,
1172
+ :tmpl => '<div class="bar foo-navi">Foo</div>',
1173
+ },
1174
+ result,
1175
+ 'Parser.gsub_action_tmpl should yield action templates'
1176
+ )
1177
+
1178
+ result, html = _test_gsub_action_tmpl 'a<div class="bar foo-navi baz">Foo</div>c'
1179
+ assert_equal(
1180
+ {
1181
+ :id => 'foo',
1182
+ :action => :navi,
1183
+ :tmpl => '<div class="bar foo-navi baz">Foo</div>',
1184
+ },
1185
+ result,
1186
+ 'Parser.gsub_action_tmpl should yield action templates'
1187
+ )
1188
+
1189
+ result, html = _test_gsub_action_tmpl 'a<div class="bar foo-done baz">Foo</div>c'
1190
+ assert_equal(
1191
+ {
1192
+ :id => 'foo',
1193
+ :action => :done,
1194
+ :tmpl => '<div class="bar foo-done baz">Foo</div>',
1195
+ },
1196
+ result,
1197
+ 'Parser.gsub_action_tmpl should yield action templates'
1198
+ )
1199
+ end
1200
+
1201
+ def test_gsub_action_tmpl_with_empty_id
1202
+ result, html = _test_gsub_action_tmpl 'a<div class="navi">Foo</div>c'
1203
+ assert_equal(
1204
+ {
1205
+ :id => nil,
1206
+ :action => :navi,
1207
+ :tmpl => '<div class="navi">Foo</div>',
1208
+ },
1209
+ result,
1210
+ 'Parser.gsub_action_tmpl should yield action templates'
1211
+ )
1212
+
1213
+ result, html = _test_gsub_action_tmpl 'a<div class="foo navi">Foo</div>c'
1214
+ assert_equal(
1215
+ {
1216
+ :id => nil,
1217
+ :action => :navi,
1218
+ :tmpl => '<div class="foo navi">Foo</div>',
1219
+ },
1220
+ result,
1221
+ 'Parser.gsub_action_tmpl should yield action templates'
1222
+ )
1223
+
1224
+ result, html = _test_gsub_action_tmpl 'a<div class="foo navi baz">Foo</div>c'
1225
+ assert_equal(
1226
+ {
1227
+ :id => nil,
1228
+ :action => :navi,
1229
+ :tmpl => '<div class="foo navi baz">Foo</div>',
1230
+ },
1231
+ result,
1232
+ 'Parser.gsub_action_tmpl should yield action templates'
1233
+ )
1234
+ end
1235
+
1236
+ def test_gsub_action_tmpl_with_ambiguous_klass
1237
+ result, html = _test_gsub_action_tmpl 'a<div class="not_navi">Foo</div>c'
1238
+ assert_equal(
1239
+ {},
1240
+ result,
1241
+ 'Parser.gsub_action_tmpl should ignore classes other than action, view, navi or submit'
1242
+ )
1243
+
1244
+ result, html = _test_gsub_action_tmpl 'a<div class="navi_bar">Foo</div>c'
1245
+ assert_equal(
1246
+ {
1247
+ :id => nil,
1248
+ :action => :navi_bar,
1249
+ :tmpl => '<div class="navi_bar">Foo</div>',
1250
+ },
1251
+ result,
1252
+ 'Parser.gsub_action_tmpl should yield an action template if the klass looks like special'
1253
+ )
1254
+ end
1255
+
1256
+ def test_action_tmpl_in_ss
1257
+ result = Bike::Parser.parse_html <<'_html'
1258
+ <html>
1259
+ <ul id="foo" class="app-blog">
1260
+ <li>$(subject=text)</li>
1261
+ </ul>
1262
+ <div class="foo-navi">bar</div>
1263
+ </html>
1264
+ _html
1265
+ assert_equal(
1266
+ <<'_tmpl',
1267
+ <div class="foo-navi">bar</div>
1268
+ _tmpl
1269
+ result[:item]['foo'][:tmpl][:navi],
1270
+ 'Parser.parse_html should parse action templates in the html'
1271
+ )
1272
+ assert_equal(
1273
+ {:index => <<'_tmpl'},
1274
+ <html>
1275
+ $(foo.message)$(foo)$(foo.navi)</html>
1276
+ _tmpl
1277
+ result[:tmpl],
1278
+ 'Parser.parse_html should replace action templates with proper tags'
1279
+ )
1280
+ end
1281
+
1282
+ def test_action_tmpl_in_ss_with_nil_id
1283
+ result = Bike::Parser.parse_html <<'_html'
1284
+ <html>
1285
+ <ul id="main" class="app-blog">
1286
+ <li>$(subject=text)</li>
1287
+ </ul>
1288
+ <div class="navi">bar</div>
1289
+ </html>
1290
+ _html
1291
+ assert_equal(
1292
+ <<'_tmpl',
1293
+ <div class="navi">bar</div>
1294
+ _tmpl
1295
+ result[:item]['main'][:tmpl][:navi],
1296
+ "Parser.parse_html should set action templates to item['main'] by default"
1297
+ )
1298
+ assert_equal(
1299
+ {:index => <<'_tmpl'},
1300
+ <html>
1301
+ $(main.message)$(main)$(main.navi)</html>
1302
+ _tmpl
1303
+ result[:tmpl],
1304
+ "Parser.parse_html should set action templates to item['main'] by default"
1305
+ )
1306
+ end
1307
+
1308
+ def test_action_tmpl_in_ss_with_non_existent_id
1309
+ result = Bike::Parser.parse_html <<'_html'
1310
+ <html>
1311
+ <ul id="main" class="app-blog">
1312
+ <li>$(subject=text)</li>
1313
+ </ul>
1314
+ <div class="non_existent-navi">bar</div>
1315
+ </html>
1316
+ _html
1317
+ assert_nil(
1318
+ result[:item]['non_existent'],
1319
+ 'Parser.parse_html should ignore the action template without a corresponding SD'
1320
+ )
1321
+ assert_equal(
1322
+ {:index => <<'_tmpl'},
1323
+ <html>
1324
+ $(main.message)$(main) <div class="non_existent-navi">bar</div>
1325
+ </html>
1326
+ _tmpl
1327
+ result[:tmpl],
1328
+ 'Parser.parse_html should ignore the action template without a corresponding SD'
1329
+ )
1330
+ end
1331
+
1332
+ def test_action_tmpl_in_ss_with_nested_action_tmpl
1333
+ result = Bike::Parser.parse_html <<'_html'
1334
+ <html>
1335
+ <ul id="foo" class="app-blog">
1336
+ <li>$(subject=text)</li>
1337
+ </ul>
1338
+ <div class="foo-navi"><span class="navi_prev">prev</span></div>
1339
+ </html>
1340
+ _html
1341
+ assert_equal(
1342
+ <<'_html',
1343
+ <div class="foo-navi">$(.navi_prev)</div>
1344
+ _html
1345
+ result[:item]['foo'][:tmpl][:navi],
1346
+ 'Parser.parse_html should parse nested action templates'
1347
+ )
1348
+ assert_equal(
1349
+ '<span class="navi_prev">prev</span>',
1350
+ result[:item]['foo'][:tmpl][:navi_prev],
1351
+ 'Parser.parse_html should parse nested action templates'
1352
+ )
1353
+ assert_equal(
1354
+ {
1355
+ :index => <<'_html'.chomp,
1356
+ <ul id="@(name)" class="app-blog">
1357
+ $() </ul>
1358
+ $(.submit)$(.action_create)
1359
+ _html
1360
+ :navi => <<'_html',
1361
+ <div class="foo-navi">$(.navi_prev)</div>
1362
+ _html
1363
+ :navi_prev => '<span class="navi_prev">prev</span>',
1364
+ },
1365
+ result[:item]['foo'][:tmpl],
1366
+ 'Parser.parse_html should parse nested action templates'
1367
+ )
1368
+
1369
+ result = Bike::Parser.parse_html <<'_html'
1370
+ <html>
1371
+ <ul id="foo" class="app-blog">
1372
+ <li>$(subject=text)</li>
1373
+ </ul>
1374
+ <div class="foo-navi"><span class="bar-navi_prev">prev</span></div>
1375
+ </html>
1376
+ _html
1377
+ assert_equal(
1378
+ '<span class="bar-navi_prev">prev</span>',
1379
+ result[:item]['foo'][:tmpl][:navi_prev],
1380
+ 'Parser.parse_html should ignore the id of a nested action template'
1381
+ )
1382
+ end
1383
+
1384
+ def test_action_tmpl_in_sd
1385
+ result = Bike::Parser.parse_html <<'_html'
1386
+ <ul id="foo" class="app-blog">
1387
+ <li class="model">$(text)</li>
1388
+ <div class="navi">bar</div>
1389
+ </ul>
1390
+ _html
1391
+ assert_equal(
1392
+ <<'_html',
1393
+ <div class="navi">bar</div>
1394
+ _html
1395
+ result[:item]['foo'][:tmpl][:navi],
1396
+ 'Parser.parse_html should parse action templates in sd[:tmpl]'
1397
+ )
1398
+ assert_match(
1399
+ %r{\$\(\.navi\)},
1400
+ result[:item]['foo'][:tmpl][:index],
1401
+ 'Parser.parse_html should parse action templates in sd[:tmpl]'
1402
+ )
1403
+ end
1404
+
1405
+ def test_action_tmpl_in_sd_with_nested_action_tmpl
1406
+ result = Bike::Parser.parse_html <<'_html'
1407
+ <ul id="foo" class="app-blog">
1408
+ <li class="model">$(text)</li>
1409
+ <div class="navi"><span class="navi_prev">prev</span></div>
1410
+ </ul>
1411
+ _html
1412
+ assert_equal(
1413
+ <<'_html',
1414
+ <div class="navi">$(.navi_prev)</div>
1415
+ _html
1416
+ result[:item]['foo'][:tmpl][:navi],
1417
+ 'Parser.parse_html should parse nested action templates in sd[:tmpl]'
1418
+ )
1419
+ assert_equal(
1420
+ '<span class="navi_prev">prev</span>',
1421
+ result[:item]['foo'][:tmpl][:navi_prev],
1422
+ 'Parser.parse_html should parse nested action templates in sd[:tmpl]'
1423
+ )
1424
+ end
1425
+
1426
+ def test_action_tmpl_in_comment
1427
+ result = Bike::Parser.parse_html <<'_html'
1428
+ <ul id="foo" class="app-blog">
1429
+ <li class="model">$(text)</li>
1430
+ <!--
1431
+ <div class="navi"><span class="navi_prev">prev</span></div>
1432
+ -->
1433
+ </ul>
1434
+ _html
1435
+ assert_nil(
1436
+ result[:item]['foo'][:tmpl][:navi],
1437
+ 'Parser.parse_html should skip action templates in a comment'
1438
+ )
1439
+
1440
+ result = Bike::Parser.parse_html <<'_html'
1441
+ <ul id="foo" class="app-blog">
1442
+ <li class="model">$(text)</li>
1443
+ <div class="navi"><!--<span class="navi_prev">prev</span>--></div>
1444
+ </ul>
1445
+ _html
1446
+ assert_equal(
1447
+ <<'_html',
1448
+ <div class="navi"><!--<span class="navi_prev">prev</span>--></div>
1449
+ _html
1450
+ result[:item]['foo'][:tmpl][:navi],
1451
+ 'Parser.parse_html should skip action templates in a comment'
1452
+ )
1453
+ end
1454
+
1455
+ def test_supplement_sd
1456
+ result = Bike::Parser.parse_html <<'_html'
1457
+ <ul id="foo" class="app-blog">
1458
+ <li class="model">$(text)</li>
1459
+ </ul>
1460
+ _html
1461
+ assert_match(
1462
+ /\$\(\.navi\)/,
1463
+ result[:item]['foo'][:tmpl][:index],
1464
+ 'Parser.supplement_sd should supplement sd[:tmpl] with default menus'
1465
+ )
1466
+
1467
+ result = Bike::Parser.parse_html <<'_html'
1468
+ <ul id="foo" class="app-blog">
1469
+ <div class="navi">bar</div>
1470
+ <li class="model">$(text)</li>
1471
+ </ul>
1472
+ _html
1473
+ assert_no_match(
1474
+ /\$\(\.navi\).*\$\(\.navi\)/m,
1475
+ result[:item]['foo'][:tmpl][:index],
1476
+ 'Parser.supplement_sd should not supplement sd[:tmpl] when it already has the menu'
1477
+ )
1478
+
1479
+ result = Bike::Parser.parse_html <<'_html'
1480
+ <div class="foo-navi">bar</div>
1481
+ <ul id="foo" class="app-blog">
1482
+ <li class="model">$(text)</li>
1483
+ </ul>
1484
+ _html
1485
+ assert_no_match(
1486
+ /\$\(\.navi\)/,
1487
+ result[:item]['foo'][:tmpl][:index],
1488
+ 'Parser.supplement_sd should not supplement sd[:tmpl] when it already has the menu'
1489
+ )
1490
+ end
1491
+
1492
+ def test_supplement_ss
1493
+ result = Bike::Parser.parse_html <<'_html'
1494
+ <ul id="foo" class="app-blog">
1495
+ <li class="model">$(text)</li>
1496
+ </ul>
1497
+ _html
1498
+ assert_match(
1499
+ /\$\(\.a_update\)/,
1500
+ result[:item]['foo'][:item]['default'][:tmpl][:index],
1501
+ 'Parser.supplement_ss should supplement ss[:tmpl] with default menus'
1502
+ )
1503
+
1504
+ result = Bike::Parser.parse_html <<'_html'
1505
+ <ul id="foo" class="app-blog">
1506
+ <li class="model">$(text) $(.action_update)</li>
1507
+ </ul>
1508
+ _html
1509
+ assert_no_match(
1510
+ /\$\(\.a_update\)/,
1511
+ result[:item]['foo'][:item]['default'][:tmpl][:index],
1512
+ 'Parser.supplement_ss should not supplement ss[:tmpl] when it already has the menu'
1513
+ )
1514
+ end
1515
+
1516
+ end