csspool-st 3.1.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 (64) hide show
  1. data/.autotest +16 -0
  2. data/.gemtest +0 -0
  3. data/CHANGELOG.rdoc +87 -0
  4. data/Manifest.txt +62 -0
  5. data/README.rdoc +65 -0
  6. data/Rakefile +50 -0
  7. data/lib/csspool/collection.rb +50 -0
  8. data/lib/csspool/css/charset.rb +13 -0
  9. data/lib/csspool/css/declaration.rb +19 -0
  10. data/lib/csspool/css/document.rb +34 -0
  11. data/lib/csspool/css/document_handler.rb +51 -0
  12. data/lib/csspool/css/import_rule.rb +27 -0
  13. data/lib/csspool/css/media.rb +13 -0
  14. data/lib/csspool/css/parser.rb +1298 -0
  15. data/lib/csspool/css/parser.y +398 -0
  16. data/lib/csspool/css/rule_set.rb +17 -0
  17. data/lib/csspool/css/tokenizer.rb +231 -0
  18. data/lib/csspool/css/tokenizer.rex +97 -0
  19. data/lib/csspool/css.rb +8 -0
  20. data/lib/csspool/node.rb +41 -0
  21. data/lib/csspool/sac/document.rb +35 -0
  22. data/lib/csspool/sac/parser.rb +16 -0
  23. data/lib/csspool/sac.rb +2 -0
  24. data/lib/csspool/selector.rb +36 -0
  25. data/lib/csspool/selectors/additional.rb +6 -0
  26. data/lib/csspool/selectors/attribute.rb +21 -0
  27. data/lib/csspool/selectors/class.rb +11 -0
  28. data/lib/csspool/selectors/id.rb +11 -0
  29. data/lib/csspool/selectors/pseudo_class.rb +13 -0
  30. data/lib/csspool/selectors/simple.rb +22 -0
  31. data/lib/csspool/selectors/type.rb +6 -0
  32. data/lib/csspool/selectors/universal.rb +6 -0
  33. data/lib/csspool/selectors.rb +9 -0
  34. data/lib/csspool/terms/function.rb +17 -0
  35. data/lib/csspool/terms/hash.rb +6 -0
  36. data/lib/csspool/terms/ident.rb +15 -0
  37. data/lib/csspool/terms/number.rb +14 -0
  38. data/lib/csspool/terms/rgb.rb +20 -0
  39. data/lib/csspool/terms/string.rb +6 -0
  40. data/lib/csspool/terms/uri.rb +6 -0
  41. data/lib/csspool/terms.rb +7 -0
  42. data/lib/csspool/visitors/children.rb +50 -0
  43. data/lib/csspool/visitors/comparable.rb +84 -0
  44. data/lib/csspool/visitors/iterator.rb +80 -0
  45. data/lib/csspool/visitors/to_css.rb +248 -0
  46. data/lib/csspool/visitors/visitor.rb +17 -0
  47. data/lib/csspool/visitors.rb +5 -0
  48. data/lib/csspool.rb +21 -0
  49. data/test/css/test_document.rb +13 -0
  50. data/test/css/test_import_rule.rb +42 -0
  51. data/test/css/test_parser.rb +462 -0
  52. data/test/css/test_tokenizer.rb +320 -0
  53. data/test/helper.rb +65 -0
  54. data/test/sac/test_parser.rb +123 -0
  55. data/test/sac/test_properties.rb +43 -0
  56. data/test/sac/test_terms.rb +228 -0
  57. data/test/test_collection.rb +81 -0
  58. data/test/test_parser.rb +91 -0
  59. data/test/test_selector.rb +51 -0
  60. data/test/visitors/test_children.rb +20 -0
  61. data/test/visitors/test_comparable.rb +102 -0
  62. data/test/visitors/test_each.rb +19 -0
  63. data/test/visitors/test_to_css.rb +309 -0
  64. metadata +214 -0
@@ -0,0 +1,462 @@
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_ie_safe_hack
167
+ # http://mathiasbynens.be/notes/safe-css-hacks
168
+ @parser.scan_str <<-eocss
169
+ .foo {
170
+ color: black;
171
+ color: green\\9; /* IE8 and older, but there's more... */
172
+ *color: blue; /* IE7 and older */
173
+ _color: red; /* IE6 and older */
174
+ }
175
+ eocss
176
+ declarations = @parser.handler.calls.find_all { |x|
177
+ x.first == :property
178
+ }
179
+ names = declarations.map { |y| y[1].first }
180
+ assert_equal %w{color color *color _color}, names
181
+ # values = declarations.map { |y| y[1][1].first.to_s }
182
+ # assert_equal %w{black green\\9 blue red}, values
183
+ end
184
+
185
+ def test_star_attribute
186
+ assert_attribute '*:foo { }'
187
+ assert_attribute 'a *.foo { }'
188
+ assert_attribute 'a * bar { }'
189
+ end
190
+
191
+ def test_ruleset_div_attribute_recurses
192
+ assert_attribute 'div[a]:foo { }'
193
+ assert_attribute 'div:foo[a] { }'
194
+ assert_attribute 'div#foo[a] { }'
195
+ assert_attribute 'div.foo[a] { }'
196
+ assert_attribute 'div[a]::foo { }'
197
+ end
198
+
199
+ def test_additional_selectors_id_pesuedoclass
200
+ assert_additional_selector(
201
+ [
202
+ [ Selectors::Id, 'foo' ],
203
+ [ Selectors::PseudoClass, 'foo' ]
204
+ ], '#foo:foo { }')
205
+ end
206
+
207
+ def test_additional_selectors_class_pesuedoclass
208
+ assert_additional_selector(
209
+ [
210
+ [ Selectors::Class, 'foo' ],
211
+ [ Selectors::PseudoClass, 'foo' ]
212
+ ], '.foo:foo { }')
213
+ end
214
+
215
+ def test_additional_selectors_attribute_pesuedoclass
216
+ assert_additional_selector(
217
+ [
218
+ [ Selectors::Attribute, 'foo' ],
219
+ [ Selectors::PseudoClass, 'foo' ]
220
+ ], '[foo]:foo { }')
221
+ end
222
+
223
+ def test_additional_selectors_pseudo_class
224
+ assert_additional_selector(
225
+ [
226
+ [ Selectors::PseudoClass, 'foo' ],
227
+ [ Selectors::Class, 'foo' ]
228
+ ], ':foo.foo { }')
229
+ end
230
+
231
+ def test_additional_selectors_attribute
232
+ assert_additional_selector(
233
+ { Selectors::Attribute => 'foo' }, '[foo] { }')
234
+ assert_additional_selector(
235
+ { Selectors::Attribute => 'foo' }, '[foo |= "bar"] { }')
236
+ assert_additional_selector(
237
+ { Selectors::Attribute => 'foo' }, '[foo |= bar] { }')
238
+ assert_additional_selector(
239
+ { Selectors::Attribute => 'foo' }, '[foo ~= bar] { }')
240
+ assert_additional_selector(
241
+ { Selectors::Attribute => 'foo' }, '[foo ~= "bar"] { }')
242
+ assert_additional_selector(
243
+ { Selectors::Attribute => 'foo' }, '[foo = "bar"] { }')
244
+ assert_additional_selector(
245
+ { Selectors::Attribute => 'foo' }, '[foo = bar] { }')
246
+ end
247
+
248
+ def test_additional_selectors_pseudo
249
+ assert_additional_selector(
250
+ { Selectors::PseudoClass => 'foo' }, ':foo { }')
251
+ assert_additional_selector(
252
+ { Selectors::PseudoClass => 'foo' }, ':foo() { }')
253
+ assert_additional_selector(
254
+ { Selectors::PseudoClass => 'foo' }, ':foo(a) { }')
255
+ assert_additional_selector(
256
+ { Selectors::PseudoElement => 'foo' }, '::foo { }')
257
+ assert_additional_selector(
258
+ { Selectors::PseudoElement => 'before' }, ':before { }')
259
+ end
260
+
261
+ def test_additional_selectors_id
262
+ assert_additional_selector({ Selectors::Id => 'foo' }, '#foo { }')
263
+ end
264
+
265
+ def test_additional_selectors_class
266
+ assert_additional_selector({ Selectors::Class => 'foo' }, '.foo { }')
267
+ end
268
+
269
+ def test_ruleset_multiple_selectors
270
+ assert_attribute '.foo, bar, #baz { }'
271
+ sels = args_for(:start_selector).first
272
+ assert_equal 3, sels.length
273
+ sels.each do |sel|
274
+ assert_instance_of Selector, sel
275
+ end
276
+ end
277
+
278
+ def test_ruleset_class_no_name
279
+ assert_attribute '.foo { }'
280
+ end
281
+
282
+ def test_ruleset_id_no_name
283
+ assert_attribute 'foo { }'
284
+ end
285
+
286
+ def test_ruleset_div_pseudo_function_with_arg
287
+ assert_attribute 'div:foo.bar(bar) { }'
288
+ end
289
+
290
+ def test_ruleset_div_pseudo_function
291
+ assert_attribute 'div:foo() { }'
292
+ end
293
+
294
+ def test_ruleset_div_pseudo
295
+ assert_attribute 'div:foo { }'
296
+ end
297
+
298
+ def test_ruleset_div_attribute_dashmatch_string
299
+ assert_attribute 'div[a |= "b"] { }'
300
+ end
301
+
302
+ def test_ruleset_div_attribute_dashmatch_ident
303
+ assert_attribute 'div[a |= b] { }'
304
+ end
305
+
306
+ def test_ruleset_div_attribute_includes_ident
307
+ assert_attribute 'div[a ~= b] { }'
308
+ end
309
+
310
+ def test_ruleset_div_attribute_includes_string
311
+ assert_attribute 'div[a ~= "b"] { }'
312
+ end
313
+
314
+ def test_ruleset_div_attribute_equals_string
315
+ assert_attribute 'div[a = "b"] { }'
316
+ end
317
+
318
+ def test_ruleset_div_attribute_equals_ident
319
+ assert_attribute 'div[a = b] { }'
320
+ end
321
+
322
+ def test_ruleset_div_attribute_exists
323
+ assert_attribute 'div[a] { }'
324
+ end
325
+
326
+ def test_ruleset_div_class
327
+ assert_attribute 'div.foo { }'
328
+ end
329
+
330
+ def test_ruleset_div_hash
331
+ assert_attribute 'div#foo { }'
332
+ end
333
+
334
+ def test_ruleset_div
335
+ assert_attribute 'div { }'
336
+ end
337
+
338
+ def test_ruleset_star
339
+ assert_attribute '* { }'
340
+ end
341
+
342
+ {
343
+ ' ' => :s,
344
+ ' > ' => :>,
345
+ ' + ' => :+
346
+ }.each do |combo, sym|
347
+ define_method(:"test_combo_#{sym}") do
348
+ assert_attribute "div #{combo} p a { }"
349
+
350
+ sel = args_for(:start_selector).first.first
351
+ assert_equal 3, sel.simple_selectors.length
352
+ assert_equal [nil, sym, :s],
353
+ sel.simple_selectors.map { |x| x.combinator }
354
+ end
355
+ end
356
+
357
+ def test_import
358
+ @parser.scan_str '@import "foo";'
359
+ assert_equal 'foo', doc.calls[1][1][1].value
360
+ assert_equal :import_style, doc.calls[1].first
361
+ end
362
+
363
+ def test_missing_semicolon
364
+ @parser.scan_str 'div { border: none }'
365
+ assert_equal 'div', doc.calls[1][1][0].join
366
+ assert_equal 'border', doc.calls[2][1][0]
367
+ assert_equal 'none', doc.calls[2][1][1].join
368
+ end
369
+
370
+ def test_whitespaces
371
+ @parser.scan_str 'div { border : none }'
372
+ assert_equal 'div', doc.calls[1][1][0].join
373
+ assert_equal 'border', doc.calls[2][1][0]
374
+ assert_equal 'none', doc.calls[2][1][1].join
375
+ end
376
+
377
+ def test_import_medium
378
+ @parser.scan_str '@import "foo" page;'
379
+ assert_equal :import_style, doc.calls[1].first
380
+ assert_equal 'foo', doc.calls[1][1][1].value
381
+ assert_equal 'page', doc.calls[1][1].first.first.first.value
382
+ end
383
+
384
+ def test_import_medium_multi
385
+ @parser.scan_str '@import "foo" page, print;'
386
+ assert_equal :import_style, doc.calls[1].first
387
+ assert_equal 'foo', doc.calls[1][1][1].value
388
+ assert_equal 'page', doc.calls[1][1].first.first.first.value
389
+ assert_equal 'print', doc.calls[1][1].first[1].first.value
390
+ end
391
+
392
+ def test_media_query
393
+ # @parser.scan_str '@media screen and(min-width:480px){ a{} }'
394
+ @parser.scan_str '@media only screen and ( min-width: 480px ), print { a{} }'
395
+ assert_equal :start_media, doc.calls[1].first
396
+ assert_equal 'only', doc.calls[1][1][0][0][0].value
397
+ assert_equal 'screen', doc.calls[1][1][0][0][1].value
398
+ assert_equal 'and', doc.calls[1][1][0][0][2].value
399
+ assert_equal Selectors::MediaExpression, doc.calls[1][1][0][0][3].class
400
+ assert_equal 'min-width', doc.calls[1][1][0][0][3].name
401
+ assert_equal '480px', doc.calls[1][1][0][0][3].value.join
402
+ assert_equal 'print', doc.calls[1][1][0][1][0].value
403
+ end
404
+
405
+ def test_start_stop
406
+ @parser.scan_str "@import 'foo';"
407
+ assert_equal [:start_document, []], @doc.calls.first
408
+ assert_equal [:end_document, []], @doc.calls.last
409
+ end
410
+
411
+ def test_charset
412
+ @parser.scan_str '@charset "UTF-8";'
413
+ assert_equal [:charset, ['UTF-8', {}]], @doc.calls[1]
414
+ end
415
+
416
+ def assert_attribute css
417
+ @parser.handler = MethodCatcher.new
418
+ @parser.scan_str css
419
+ assert_equal :start_selector, @parser.handler.calls[1].first
420
+ assert_equal :end_selector, @parser.handler.calls[2].first
421
+ end
422
+
423
+ def assert_decl name, values, css, ops = nil
424
+ @parser.handler = MethodCatcher.new
425
+ @parser.scan_str css
426
+ assert_equal :start_selector, @parser.handler.calls[1].first
427
+ assert_equal :property, @parser.handler.calls[2].first
428
+ assert_equal :end_selector, @parser.handler.calls[3].first
429
+
430
+ property = @parser.handler.calls[2][1]
431
+ assert_equal name, property.first
432
+
433
+ assert_equal values, property[1].map { |x| x.value }
434
+ if ops
435
+ assert_equal ops, property[1].map { |x| x.operator }
436
+ end
437
+ end
438
+
439
+ def doc; @parser.handler end
440
+ def args_for s; doc.calls.find { |x| x.first == s }[1] end
441
+
442
+ def assert_additional_selector things, css
443
+ @parser.handler = MethodCatcher.new
444
+ @parser.scan_str css
445
+ args = @parser.handler.calls.find { |x|
446
+ x.first == :start_selector
447
+ }[1]
448
+
449
+ ss = args.first.first.simple_selectors.first
450
+
451
+ assert_equal things.length, ss.additional_selectors.length
452
+ i = 0
453
+ things.each do |klass, name|
454
+ assert_instance_of klass, ss.additional_selectors[i]
455
+ assert_equal name, ss.additional_selectors[i].name
456
+ i += 1
457
+ end
458
+ end
459
+
460
+ end
461
+ end
462
+ end