csspool 2.0.0 → 3.0.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 (57) hide show
  1. data/.gemtest +0 -0
  2. data/CHANGELOG.rdoc +6 -0
  3. data/Manifest.txt +10 -14
  4. data/README.rdoc +8 -21
  5. data/Rakefile +36 -2
  6. data/lib/csspool/collection.rb +2 -2
  7. data/lib/csspool/css/charset.rb +8 -2
  8. data/lib/csspool/css/declaration.rb +13 -2
  9. data/lib/csspool/css/document_handler.rb +3 -3
  10. data/lib/csspool/css/import_rule.rb +15 -3
  11. data/lib/csspool/css/media.rb +8 -2
  12. data/lib/csspool/css/parser.rb +1090 -0
  13. data/lib/csspool/css/parser.y +347 -0
  14. data/lib/csspool/css/rule_set.rb +8 -3
  15. data/lib/csspool/css/tokenizer.rb +228 -0
  16. data/lib/csspool/css/tokenizer.rex +96 -0
  17. data/lib/csspool/css.rb +1 -0
  18. data/lib/csspool/node.rb +29 -1
  19. data/lib/csspool/sac/document.rb +3 -3
  20. data/lib/csspool/sac/parser.rb +6 -112
  21. data/lib/csspool/terms/function.rb +1 -1
  22. data/lib/csspool/terms/ident.rb +1 -1
  23. data/lib/csspool/terms/number.rb +1 -1
  24. data/lib/csspool/terms/rgb.rb +1 -4
  25. data/lib/csspool/terms/string.rb +1 -1
  26. data/lib/csspool/visitors/children.rb +50 -0
  27. data/lib/csspool/visitors/comparable.rb +9 -3
  28. data/lib/csspool/visitors/iterator.rb +80 -0
  29. data/lib/csspool/visitors/to_css.rb +61 -45
  30. data/lib/csspool/visitors.rb +2 -0
  31. data/lib/csspool.rb +6 -3
  32. data/test/css/test_parser.rb +412 -0
  33. data/test/css/test_tokenizer.rb +320 -0
  34. data/test/helper.rb +2 -2
  35. data/test/sac/test_parser.rb +3 -8
  36. data/test/sac/test_terms.rb +128 -34
  37. data/test/test_collection.rb +1 -1
  38. data/test/test_parser.rb +1 -1
  39. data/test/visitors/test_children.rb +20 -0
  40. data/test/visitors/test_comparable.rb +31 -1
  41. data/test/visitors/test_each.rb +19 -0
  42. data/test/visitors/test_to_css.rb +125 -1
  43. metadata +90 -68
  44. data/lib/csspool/lib_croco/cr_additional_sel.rb +0 -46
  45. data/lib/csspool/lib_croco/cr_attr_sel.rb +0 -16
  46. data/lib/csspool/lib_croco/cr_doc_handler.rb +0 -24
  47. data/lib/csspool/lib_croco/cr_num.rb +0 -13
  48. data/lib/csspool/lib_croco/cr_parser.rb +0 -11
  49. data/lib/csspool/lib_croco/cr_parsing_location.rb +0 -17
  50. data/lib/csspool/lib_croco/cr_pseudo.rb +0 -14
  51. data/lib/csspool/lib_croco/cr_rgb.rb +0 -18
  52. data/lib/csspool/lib_croco/cr_selector.rb +0 -34
  53. data/lib/csspool/lib_croco/cr_simple_sel.rb +0 -54
  54. data/lib/csspool/lib_croco/cr_term.rb +0 -97
  55. data/lib/csspool/lib_croco/glist.rb +0 -21
  56. data/lib/csspool/lib_croco.rb +0 -78
  57. data/lib/csspool/visitable.rb +0 -18
@@ -0,0 +1,412 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require "helper"
4
+
5
+ module CSSPool
6
+ module CSS
7
+ class TestParser < CSSPool::TestCase
8
+ class MethodCatcher
9
+ attr_reader :calls
10
+
11
+ def initialize
12
+ @calls = []
13
+ end
14
+
15
+ def method_missing name, *args
16
+ @calls << [name, args]
17
+ end
18
+ end
19
+
20
+ def setup
21
+ super
22
+ @doc = MethodCatcher.new
23
+ @parser = Class.new(CSSPool::CSS::Tokenizer) {
24
+ attr_accessor :handler
25
+ def initialize doc
26
+ @handler = doc
27
+ end
28
+ }.new(@doc)
29
+ end
30
+
31
+ {
32
+ 'em' => 'em',
33
+ 'per' => '%',
34
+ 'ex' => 'ex',
35
+ 'ex' => 'ex',
36
+ 'deg' => 'deg',
37
+ 'ms' => 'ms',
38
+ 'hz' => 'hz',
39
+ }.each do |type, s|
40
+ define_method(:"test_term_#{type}") do
41
+ assert_term({
42
+ :class => Terms::Number,
43
+ :value => 10,
44
+ :type => s
45
+ }, "div { foo: 10#{s}; }")
46
+ end
47
+ end
48
+
49
+ def test_term_rgb
50
+ assert_term({
51
+ :class => Terms::Rgb,
52
+ }, "div { foo: rgb(255, 255, 255); }")
53
+ end
54
+
55
+ def test_term_function
56
+ assert_term({
57
+ :class => Terms::Function,
58
+ :name => "foo",
59
+ }, "div { foo: foo(bar); }")
60
+ assert_term({
61
+ :class => Terms::Function,
62
+ :name => "foo",
63
+ }, "div { foo: foo(bar) baz; }")
64
+ end
65
+
66
+ def test_term_hexcolor
67
+ assert_term({
68
+ :class => Terms::Hash,
69
+ :value => "666",
70
+ }, "div { foo: #666; }")
71
+ assert_term({
72
+ :class => Terms::Hash,
73
+ :value => "666",
74
+ }, "div { foo: #666 foo; }")
75
+ end
76
+
77
+ def test_term_string
78
+ assert_term({
79
+ :class => Terms::String,
80
+ :value => "foo",
81
+ }, "div { foo: 'foo'; }")
82
+ assert_term({
83
+ :class => Terms::String,
84
+ :value => "foo",
85
+ }, "div { foo: 'foo' ; }")
86
+ assert_term({
87
+ :class => Terms::String,
88
+ :value => "foo",
89
+ }, "div { foo: 'foo' bar; }")
90
+ end
91
+
92
+ def test_term_uri
93
+ assert_term({
94
+ :class => Terms::URI,
95
+ :value => "http://example.com/",
96
+ }, "div { foo: url(http://example.com/); }")
97
+ assert_term({
98
+ :class => Terms::URI,
99
+ :value => "http://example.com/",
100
+ }, "div { foo: url(http://example.com/) bar; }")
101
+ end
102
+
103
+ def test_term_length
104
+ assert_term({
105
+ :class => Terms::Number,
106
+ :value => 10,
107
+ :type => 'mm'
108
+ }, 'div { foo: 10mm; }')
109
+ assert_term({
110
+ :class => Terms::Number,
111
+ :value => 1.2,
112
+ :type => 'mm'
113
+ }, 'div { foo: 1.2mm; }')
114
+ assert_term({
115
+ :class => Terms::Number,
116
+ :value => 1.2,
117
+ :type => 'mm'
118
+ }, 'div { foo: 1.2mm; }')
119
+ end
120
+
121
+ def test_term_number
122
+ assert_term({ :class => Terms::Number, :value => 1.2 }, 'div { foo: 1.2; }')
123
+ assert_term({ :class => Terms::Number, :value => 10 }, 'div { foo: 10; }')
124
+ assert_term({ :class => Terms::Number, :unary_operator => :minus },
125
+ 'div { foo: -10; }')
126
+ assert_term({ :class => Terms::Number, :unary_operator => :plus },
127
+ 'div { foo: +10; }')
128
+ end
129
+
130
+ def assert_term term, css
131
+ @parser.handler = MethodCatcher.new
132
+ @parser.scan_str css
133
+ property = @parser.handler.calls.find { |x|
134
+ x.first == :property
135
+ }[1][1].first
136
+
137
+ term.each do |k,v|
138
+ assert_equal v, property.send(k)
139
+ end
140
+ end
141
+
142
+ def test_element_op
143
+ assert_decl 'background',
144
+ %w{red green},
145
+ 'div { background: red, green; }',
146
+ [nil, ',']
147
+ assert_decl 'background',
148
+ %w{red green},
149
+ 'div { background: red / green; }',
150
+ [nil, '/']
151
+ end
152
+
153
+ def test_declaration_ident
154
+ assert_decl 'background', ['red'], 'div { background: red; }'
155
+ assert_decl 'background', %w{red green},'div { background: red green; }'
156
+ end
157
+
158
+ def test_multi_decl
159
+ @parser.scan_str 'div { background: red; padding: 0; }'
160
+ names = @parser.handler.calls.find_all { |x|
161
+ x.first == :property
162
+ }.map { |y| y[1].first }
163
+ assert_equal %w{ background padding }, names
164
+ end
165
+
166
+ def test_star_attribute
167
+ assert_attribute '*:foo { }'
168
+ assert_attribute 'a *.foo { }'
169
+ assert_attribute 'a * bar { }'
170
+ end
171
+
172
+ def test_ruleset_div_attribute_recurses
173
+ assert_attribute 'div[a]:foo { }'
174
+ assert_attribute 'div:foo[a] { }'
175
+ assert_attribute 'div#foo[a] { }'
176
+ assert_attribute 'div.foo[a] { }'
177
+ assert_attribute 'div.foo[a] { }'
178
+ end
179
+
180
+ def test_additional_selectors_id_pesuedoclass
181
+ assert_additional_selector(
182
+ [
183
+ [ Selectors::Id, 'foo' ],
184
+ [ Selectors::PseudoClass, 'foo' ]
185
+ ], '#foo:foo { }')
186
+ end
187
+
188
+ def test_additional_selectors_class_pesuedoclass
189
+ assert_additional_selector(
190
+ [
191
+ [ Selectors::Class, 'foo' ],
192
+ [ Selectors::PseudoClass, 'foo' ]
193
+ ], '.foo:foo { }')
194
+ end
195
+
196
+ def test_additional_selectors_attribute_pesuedoclass
197
+ assert_additional_selector(
198
+ [
199
+ [ Selectors::Attribute, 'foo' ],
200
+ [ Selectors::PseudoClass, 'foo' ]
201
+ ], '[foo]:foo { }')
202
+ end
203
+
204
+ def test_additional_selectors_pseudo_class
205
+ assert_additional_selector(
206
+ [
207
+ [ Selectors::PseudoClass, 'foo' ],
208
+ [ Selectors::Class, 'foo' ]
209
+ ], ':foo.foo { }')
210
+ end
211
+
212
+ def test_additional_selectors_attribute
213
+ assert_additional_selector(
214
+ { Selectors::Attribute => 'foo' }, '[foo] { }')
215
+ assert_additional_selector(
216
+ { Selectors::Attribute => 'foo' }, '[foo |= "bar"] { }')
217
+ assert_additional_selector(
218
+ { Selectors::Attribute => 'foo' }, '[foo |= bar] { }')
219
+ assert_additional_selector(
220
+ { Selectors::Attribute => 'foo' }, '[foo ~= bar] { }')
221
+ assert_additional_selector(
222
+ { Selectors::Attribute => 'foo' }, '[foo ~= "bar"] { }')
223
+ assert_additional_selector(
224
+ { Selectors::Attribute => 'foo' }, '[foo = "bar"] { }')
225
+ assert_additional_selector(
226
+ { Selectors::Attribute => 'foo' }, '[foo = bar] { }')
227
+ end
228
+
229
+ def test_additional_selectors_pseudo
230
+ assert_additional_selector(
231
+ { Selectors::PseudoClass => 'foo' }, ':foo { }')
232
+ assert_additional_selector(
233
+ { Selectors::PseudoClass => 'foo' }, ':foo() { }')
234
+ assert_additional_selector(
235
+ { Selectors::PseudoClass => 'foo' }, ':foo(a) { }')
236
+ end
237
+
238
+ def test_additional_selectors_id
239
+ assert_additional_selector({ Selectors::Id => 'foo' }, '#foo { }')
240
+ end
241
+
242
+ def test_additional_selectors_class
243
+ assert_additional_selector({ Selectors::Class => 'foo' }, '.foo { }')
244
+ end
245
+
246
+ def test_ruleset_multiple_selectors
247
+ assert_attribute '.foo, bar, #baz { }'
248
+ sels = args_for(:start_selector).first
249
+ assert_equal 3, sels.length
250
+ sels.each do |sel|
251
+ assert_instance_of Selector, sel
252
+ end
253
+ end
254
+
255
+ def test_ruleset_class_no_name
256
+ assert_attribute '.foo { }'
257
+ end
258
+
259
+ def test_ruleset_id_no_name
260
+ assert_attribute 'foo { }'
261
+ end
262
+
263
+ def test_ruleset_div_pseudo_function_with_arg
264
+ assert_attribute 'div:foo.bar(bar) { }'
265
+ end
266
+
267
+ def test_ruleset_div_pseudo_function
268
+ assert_attribute 'div:foo() { }'
269
+ end
270
+
271
+ def test_ruleset_div_pseudo
272
+ assert_attribute 'div:foo { }'
273
+ end
274
+
275
+ def test_ruleset_div_attribute_dashmatch_string
276
+ assert_attribute 'div[a |= "b"] { }'
277
+ end
278
+
279
+ def test_ruleset_div_attribute_dashmatch_ident
280
+ assert_attribute 'div[a |= b] { }'
281
+ end
282
+
283
+ def test_ruleset_div_attribute_includes_ident
284
+ assert_attribute 'div[a ~= b] { }'
285
+ end
286
+
287
+ def test_ruleset_div_attribute_includes_string
288
+ assert_attribute 'div[a ~= "b"] { }'
289
+ end
290
+
291
+ def test_ruleset_div_attribute_equals_string
292
+ assert_attribute 'div[a = "b"] { }'
293
+ end
294
+
295
+ def test_ruleset_div_attribute_equals_ident
296
+ assert_attribute 'div[a = b] { }'
297
+ end
298
+
299
+ def test_ruleset_div_attribute_exists
300
+ assert_attribute 'div[a] { }'
301
+ end
302
+
303
+ def test_ruleset_div_class
304
+ assert_attribute 'div.foo { }'
305
+ end
306
+
307
+ def test_ruleset_div_hash
308
+ assert_attribute 'div#foo { }'
309
+ end
310
+
311
+ def test_ruleset_div
312
+ assert_attribute 'div { }'
313
+ end
314
+
315
+ def test_ruleset_star
316
+ assert_attribute '* { }'
317
+ end
318
+
319
+ {
320
+ ' ' => :s,
321
+ ' > ' => :>,
322
+ ' + ' => :+
323
+ }.each do |combo, sym|
324
+ define_method(:"test_combo_#{sym}") do
325
+ assert_attribute "div #{combo} p a { }"
326
+
327
+ sel = args_for(:start_selector).first.first
328
+ assert_equal 3, sel.simple_selectors.length
329
+ assert_equal [nil, sym, :s],
330
+ sel.simple_selectors.map { |x| x.combinator }
331
+ end
332
+ end
333
+
334
+ def test_import
335
+ @parser.scan_str '@import "foo";'
336
+ assert_equal 'foo', doc.calls[1][1][1].value
337
+ assert_equal :import_style, doc.calls[1].first
338
+ end
339
+
340
+ def test_import_medium
341
+ @parser.scan_str '@import "foo" page;'
342
+ assert_equal :import_style, doc.calls[1].first
343
+ assert_equal 'foo', doc.calls[1][1][1].value
344
+ assert_equal 'page', doc.calls[1][1].first.first.value
345
+ end
346
+
347
+ def test_import_medium_multi
348
+ @parser.scan_str '@import "foo" page, print;'
349
+ assert_equal :import_style, doc.calls[1].first
350
+ assert_equal 'foo', doc.calls[1][1][1].value
351
+ assert_equal 'page', doc.calls[1][1].first.first.value
352
+ assert_equal 'print', doc.calls[1][1].first[1].value
353
+ end
354
+
355
+ def test_start_stop
356
+ @parser.scan_str "@import 'foo';"
357
+ assert_equal [:start_document, []], @doc.calls.first
358
+ assert_equal [:end_document, []], @doc.calls.last
359
+ end
360
+
361
+ def test_charset
362
+ @parser.scan_str '@charset "UTF-8";'
363
+ assert_equal [:charset, ['UTF-8', {}]], @doc.calls[1]
364
+ end
365
+
366
+ def assert_attribute css
367
+ @parser.handler = MethodCatcher.new
368
+ @parser.scan_str css
369
+ assert_equal :start_selector, @parser.handler.calls[1].first
370
+ assert_equal :end_selector, @parser.handler.calls[2].first
371
+ end
372
+
373
+ def assert_decl name, values, css, ops = nil
374
+ @parser.handler = MethodCatcher.new
375
+ @parser.scan_str css
376
+ assert_equal :start_selector, @parser.handler.calls[1].first
377
+ assert_equal :property, @parser.handler.calls[2].first
378
+ assert_equal :end_selector, @parser.handler.calls[3].first
379
+
380
+ property = @parser.handler.calls[2][1]
381
+ assert_equal name, property.first
382
+
383
+ assert_equal values, property[1].map { |x| x.value }
384
+ if ops
385
+ assert_equal ops, property[1].map { |x| x.operator }
386
+ end
387
+ end
388
+
389
+ def doc; @parser.handler end
390
+ def args_for s; doc.calls.find { |x| x.first == s }[1] end
391
+
392
+ def assert_additional_selector things, css
393
+ @parser.handler = MethodCatcher.new
394
+ @parser.scan_str css
395
+ args = @parser.handler.calls.find { |x|
396
+ x.first == :start_selector
397
+ }[1]
398
+
399
+ ss = args.first.first.simple_selectors.first
400
+
401
+ assert_equal things.length, ss.additional_selectors.length
402
+ i = 0
403
+ things.each do |klass, name|
404
+ assert_instance_of klass, ss.additional_selectors[i]
405
+ assert_equal name, ss.additional_selectors[i].name
406
+ i += 1
407
+ end
408
+ end
409
+
410
+ end
411
+ end
412
+ end