csspool 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.DS_Store +0 -0
- data/CHANGELOG.txt +10 -0
- data/Manifest.txt +36 -2
- data/README.txt +4 -4
- data/Rakefile +1 -1
- data/lib/css/sac/conditions/attribute_condition.rb +52 -0
- data/lib/css/sac/conditions/begin_hyphen_condition.rb +22 -0
- data/lib/css/sac/conditions/class_condition.rb +22 -0
- data/lib/css/sac/conditions/combinator_condition.rb +36 -0
- data/lib/css/sac/conditions/condition.rb +27 -0
- data/lib/css/sac/conditions/id_condition.rb +27 -0
- data/lib/css/sac/conditions/one_of_condition.rb +22 -0
- data/lib/css/sac/conditions/pseudo_class_condition.rb +24 -0
- data/lib/css/sac/conditions.rb +5 -0
- data/lib/css/sac/document_handler.rb +1 -1
- data/lib/css/sac/parser.rb +5 -3
- data/lib/css/sac/selectors/child_selector.rb +36 -0
- data/lib/css/sac/selectors/conditional_selector.rb +43 -0
- data/lib/css/sac/selectors/descendant_selector.rb +36 -0
- data/lib/css/sac/selectors/element_selector.rb +35 -0
- data/lib/css/sac/selectors/selector.rb +23 -0
- data/lib/css/sac/selectors/sibling_selector.rb +35 -0
- data/lib/css/sac/selectors/simple_selector.rb +25 -0
- data/lib/css/sac/selectors.rb +3 -85
- data/lib/css/sac/visitable.rb +104 -0
- data/lib/parser.y +24 -15
- data/test/condition_test_case.rb +6 -0
- data/test/helper.rb +2 -0
- data/test/selector_test_case.rb +20 -0
- data/test/test_attribute_condition.rb +30 -0
- data/test/test_begin_hyphen_condition.rb +15 -0
- data/test/test_child_selector.rb +48 -0
- data/test/test_class_condition.rb +15 -0
- data/test/test_combinator_condition.rb +29 -0
- data/test/test_condition.rb +13 -0
- data/test/test_conditional_selector.rb +29 -0
- data/test/test_descendant_selector.rb +52 -0
- data/test/test_element_selector.rb +22 -0
- data/test/test_id_condition.rb +16 -0
- data/test/test_one_of_condition.rb +15 -0
- data/test/test_parser.rb +58 -0
- data/test/test_selector.rb +19 -0
- data/test/test_selector_as_string.rb +11 -0
- data/test/test_selector_parser.rb +2 -2
- data/test/test_sibling_selector.rb +33 -0
- data/test/test_simple_selector.rb +9 -0
- data/test/test_specificity.rb +76 -0
- data/test/test_xpath.rb +105 -0
- metadata +38 -4
- data/lib/css/sac/attribute_condition.rb +0 -78
- data/lib/css/sac/condition.rb +0 -21
data/lib/css/sac/selectors.rb
CHANGED
@@ -1,87 +1,5 @@
|
|
1
|
-
|
2
|
-
module SAC
|
3
|
-
class Selector
|
4
|
-
attr_reader :selector_type
|
5
|
-
end
|
1
|
+
require "css/sac/selectors/selector"
|
6
2
|
|
7
|
-
|
8
|
-
|
9
|
-
@selector_type = :SAC_ANY_NODE_SELECTOR
|
10
|
-
end
|
11
|
-
|
12
|
-
def to_css
|
13
|
-
case @selector_type
|
14
|
-
when :SAC_ANY_NODE_SELECTOR
|
15
|
-
'*'
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
class ElementSelector < SimpleSelector
|
21
|
-
attr_reader :local_name
|
22
|
-
alias :name :local_name
|
23
|
-
|
24
|
-
def initialize(name)
|
25
|
-
super()
|
26
|
-
@selector_type = :SAC_ELEMENT_NODE_SELECTOR
|
27
|
-
@local_name = name
|
28
|
-
end
|
29
|
-
|
30
|
-
def to_css
|
31
|
-
local_name
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
class ConditionalSelector < SimpleSelector
|
36
|
-
attr_accessor :condition, :simple_selector
|
37
|
-
alias :selector :simple_selector
|
38
|
-
|
39
|
-
def initialize(selector, condition)
|
40
|
-
@condition = condition
|
41
|
-
@simple_selector = selector
|
42
|
-
@selector_type = :SAC_CONDITIONAL_SELECTOR
|
43
|
-
end
|
44
|
-
|
45
|
-
def to_css
|
46
|
-
[selector, condition].map { |x|
|
47
|
-
x ? x.to_css : ''
|
48
|
-
}.join('')
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
class DescendantSelector < SimpleSelector
|
53
|
-
attr_accessor :ancestor_selector, :simple_selector
|
54
|
-
alias :selector :simple_selector
|
55
|
-
|
56
|
-
def initialize(ancestor, selector, type)
|
57
|
-
@ancestor_selector = ancestor
|
58
|
-
@simple_selector = selector
|
59
|
-
@selector_type = type
|
60
|
-
end
|
61
|
-
|
62
|
-
def to_css
|
63
|
-
descent_token =
|
64
|
-
case @selector_type
|
65
|
-
when :SAC_CHILD_SELECTOR
|
66
|
-
' > '
|
67
|
-
when :SAC_DESCENDANT_SELECTOR
|
68
|
-
' '
|
69
|
-
end
|
70
|
-
ancestor_selector.to_css + descent_token + selector.to_css
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
class SiblingSelector < SimpleSelector
|
75
|
-
attr_accessor :selector, :sibling_selector
|
76
|
-
def initialize(selector, sibling)
|
77
|
-
@selector = selector
|
78
|
-
@sibling_selector = sibling
|
79
|
-
@selector_type = :SAC_DIRECT_ADJACENT_SELECTOR
|
80
|
-
end
|
81
|
-
|
82
|
-
def to_css
|
83
|
-
selector.to_css + ' + ' + sibling_selector.to_css
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
3
|
+
%w(simple child conditional descendant element sibling).each do |type|
|
4
|
+
require "css/sac/selectors/#{type}_selector"
|
87
5
|
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module CSS
|
2
|
+
module SAC
|
3
|
+
module Visitable # :nodoc:
|
4
|
+
# Based off the visitor pattern from RubyGarden
|
5
|
+
def accept(visitor, &block)
|
6
|
+
klass = self.class.ancestors.find { |klass|
|
7
|
+
visitor.respond_to?("visit_#{klass.name.split(/::/)[-1]}")
|
8
|
+
}
|
9
|
+
|
10
|
+
if klass
|
11
|
+
visitor.send(:"visit_#{klass.name.split(/::/)[-1]}", self, &block)
|
12
|
+
else
|
13
|
+
raise "No visitor for '#{self.class}'"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class MatchesVisitor
|
19
|
+
def initialize(node)
|
20
|
+
@node = node
|
21
|
+
end
|
22
|
+
|
23
|
+
def accept(target)
|
24
|
+
target.accept(self)
|
25
|
+
end
|
26
|
+
|
27
|
+
def visit_SimpleSelector(o)
|
28
|
+
return false if @node.nil?
|
29
|
+
return false unless @node.respond_to?(:name)
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
def visit_ElementSelector(o)
|
34
|
+
return false unless visit_SimpleSelector(o) # *sigh*
|
35
|
+
o.name == @node.name
|
36
|
+
end
|
37
|
+
|
38
|
+
def visit_ChildSelector(o)
|
39
|
+
return false unless visit_SimpleSelector(o) # *sigh*
|
40
|
+
return false unless @node.respond_to?(:parent)
|
41
|
+
return false if @node.parent.nil?
|
42
|
+
return false unless o.selector.accept(self)
|
43
|
+
@node = @node.parent
|
44
|
+
o.parent.accept(self)
|
45
|
+
end
|
46
|
+
|
47
|
+
def visit_DescendantSelector(o)
|
48
|
+
return false unless visit_SimpleSelector(o) # *sigh*
|
49
|
+
return false unless @node.respond_to?(:parent)
|
50
|
+
return false if @node.parent.nil?
|
51
|
+
return false unless o.selector.accept(self)
|
52
|
+
|
53
|
+
@node = @node.parent
|
54
|
+
loop {
|
55
|
+
return true if o.ancestor.accept(self)
|
56
|
+
return false unless @node.respond_to?(:parent)
|
57
|
+
@node = @node.parent
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def visit_SiblingSelector(o)
|
62
|
+
return false unless visit_SimpleSelector(o) # *sigh*
|
63
|
+
return false unless @node.respond_to?(:next_sibling)
|
64
|
+
return false if @node.next_sibling.nil?
|
65
|
+
return false unless o.selector.accept(self)
|
66
|
+
@node = @node.next_sibling
|
67
|
+
o.sibling.accept(self)
|
68
|
+
end
|
69
|
+
|
70
|
+
def visit_ConditionalSelector(o)
|
71
|
+
return false unless visit_SimpleSelector(o) # *sigh*
|
72
|
+
o.selector.accept(self) && o.condition.accept(self)
|
73
|
+
end
|
74
|
+
|
75
|
+
def visit_AttributeCondition(o)
|
76
|
+
return false unless @node.respond_to?(:attributes)
|
77
|
+
return false unless @node.attributes[o.local_name]
|
78
|
+
@node.attributes[o.local_name] == o.value
|
79
|
+
end
|
80
|
+
|
81
|
+
def visit_BeginHyphenCondition(o)
|
82
|
+
return false unless @node.respond_to?(:attributes)
|
83
|
+
return false unless @node.attributes[o.local_name]
|
84
|
+
@node.attributes[o.local_name] =~ /^#{o.value}/
|
85
|
+
end
|
86
|
+
|
87
|
+
def visit_CombinatorCondition(o)
|
88
|
+
o.first.accept(self) && o.second.accept(self)
|
89
|
+
end
|
90
|
+
|
91
|
+
def visit_OneOfCondition(o)
|
92
|
+
return false unless @node.respond_to?(:attributes)
|
93
|
+
return false unless @node.attributes[o.local_name]
|
94
|
+
@node.attributes[o.local_name].split.any? { |attribute|
|
95
|
+
attribute == o.value
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
def visit_PseudoClassCondition(o)
|
100
|
+
false # Fuck pseudo classes. --Aaron
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/lib/parser.y
CHANGED
@@ -23,10 +23,6 @@ rule
|
|
23
23
|
self.document_handler.import_style(val[2], val[4] || [])
|
24
24
|
}
|
25
25
|
;
|
26
|
-
optional_ignorable_at
|
27
|
-
: ignorable_at
|
28
|
-
|
|
29
|
-
;
|
30
26
|
ignorable_at
|
31
27
|
: '@' IDENT s_0toN string_or_uri s_0toN medium_0toN ';' s_0toN {
|
32
28
|
self.document_handler.ignorable_at_rule(val[1])
|
@@ -146,16 +142,19 @@ rule
|
|
146
142
|
simple_selector_1toN
|
147
143
|
: simple_selector combinator simple_selector_1toN {
|
148
144
|
result =
|
149
|
-
|
145
|
+
case val[1]
|
146
|
+
when :SAC_DIRECT_ADJACENT_SELECTOR
|
150
147
|
SiblingSelector.new(val.first, val[2])
|
151
|
-
|
152
|
-
DescendantSelector.new(val.first, val[2]
|
148
|
+
when :SAC_DESCENDANT_SELECTOR
|
149
|
+
DescendantSelector.new(val.first, val[2])
|
150
|
+
when :SAC_CHILD_SELECTOR
|
151
|
+
ChildSelector.new(val.first, val[2])
|
153
152
|
end
|
154
153
|
}
|
155
154
|
| simple_selector
|
156
155
|
;
|
157
156
|
class
|
158
|
-
: '.' IDENT { result =
|
157
|
+
: '.' IDENT { result = ClassCondition.new(val[1]) }
|
159
158
|
;
|
160
159
|
element_name
|
161
160
|
: IDENT { result = ElementSelector.new(val.first) }
|
@@ -163,13 +162,14 @@ rule
|
|
163
162
|
;
|
164
163
|
attrib
|
165
164
|
: '[' s_0toN IDENT s_0toN attrib_val_0or1 ']' {
|
166
|
-
result = AttributeCondition.
|
165
|
+
result = AttributeCondition.build(val[2], val[4])
|
167
166
|
}
|
168
167
|
;
|
169
168
|
pseudo
|
170
|
-
: ':'
|
171
|
-
|
172
|
-
|
169
|
+
: ':' function {
|
170
|
+
result = PseudoClassCondition.new(val[1])
|
171
|
+
}
|
172
|
+
| ':' IDENT { result = PseudoClassCondition.new(val[1]) }
|
173
173
|
;
|
174
174
|
declaration
|
175
175
|
: property ':' s_0toN expr prio_0or1 {
|
@@ -203,7 +203,7 @@ rule
|
|
203
203
|
|
|
204
204
|
;
|
205
205
|
expr
|
206
|
-
: term operator expr { result = val }
|
206
|
+
: term operator expr { result = [val[0], val.last] }
|
207
207
|
| term
|
208
208
|
;
|
209
209
|
term
|
@@ -226,7 +226,9 @@ rule
|
|
226
226
|
| FREQ s_0toN
|
227
227
|
;
|
228
228
|
function
|
229
|
-
: FUNCTION s_0toN expr ')' s_0toN {
|
229
|
+
: FUNCTION s_0toN expr ')' s_0toN {
|
230
|
+
result = Function.new(val[0], val[2].flatten)
|
231
|
+
}
|
230
232
|
| FUNCTION s_0toN expr error ')' s_0toN { yyerrok; result = [val[0], val[2], val[3]] }
|
231
233
|
;
|
232
234
|
hexcolor
|
@@ -255,7 +257,7 @@ rule
|
|
255
257
|
| pseudo
|
256
258
|
;
|
257
259
|
attribute_id
|
258
|
-
: HASH { result =
|
260
|
+
: HASH { result = IDCondition.new(val.first) }
|
259
261
|
;
|
260
262
|
attrib_val_0or1
|
261
263
|
: eql_incl_dash s_0toN IDENT s_0toN { result = [val.first, val[2]] }
|
@@ -285,3 +287,10 @@ rule
|
|
285
287
|
|
|
286
288
|
;
|
287
289
|
end
|
290
|
+
|
291
|
+
---- header
|
292
|
+
require "css/sac/conditions"
|
293
|
+
require "css/sac/selectors"
|
294
|
+
|
295
|
+
include CSS::SAC::Conditions
|
296
|
+
include CSS::SAC::Selectors
|
data/test/helper.rb
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
class SelectorTestCase < Test::Unit::TestCase
|
2
|
+
include CSS::SAC::Selectors
|
3
|
+
Node = Struct.new(:name, :parent, :child, :next_sibling, :attributes)
|
4
|
+
NoParentNode = Struct.new(:name)
|
5
|
+
|
6
|
+
def parent_child_tree(*args)
|
7
|
+
nodes = args.map { |name|
|
8
|
+
n = Node.new
|
9
|
+
n.name = name
|
10
|
+
n
|
11
|
+
}
|
12
|
+
nodes.each_with_index do |n,i|
|
13
|
+
n.child = nodes[i + 1] if i < nodes.length
|
14
|
+
n.parent = nodes[i - 1] if i > 0
|
15
|
+
end
|
16
|
+
nodes.last
|
17
|
+
end
|
18
|
+
|
19
|
+
undef :default_test
|
20
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/helper"
|
2
|
+
|
3
|
+
class AttributeConditionTest < ConditionTestCase
|
4
|
+
def test_equals2
|
5
|
+
first = AttributeCondition.new(1, 1, 1)
|
6
|
+
second = AttributeCondition.new(1, 1, 1)
|
7
|
+
assert_equal first, second
|
8
|
+
|
9
|
+
third = AttributeCondition.new(1,1,2)
|
10
|
+
assert_not_equal first, third
|
11
|
+
|
12
|
+
fourth = AttributeCondition.new(1,2,1)
|
13
|
+
assert_not_equal first, fourth
|
14
|
+
|
15
|
+
fifth = AttributeCondition.new(2,1,1)
|
16
|
+
assert_not_equal first, fifth
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_equals_tilde
|
20
|
+
attribute = AttributeCondition.new('class', 'foo', true)
|
21
|
+
node = Node.new('p')
|
22
|
+
node.attributes = { 'class' => 'foo' }
|
23
|
+
|
24
|
+
assert attribute =~ node
|
25
|
+
|
26
|
+
node.attributes = { 'class' => 'bar' }
|
27
|
+
assert(!(attribute =~ node))
|
28
|
+
assert(!(attribute =~ 1))
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/helper"
|
2
|
+
|
3
|
+
class BeginHyphenConditionTest < ConditionTestCase
|
4
|
+
def test_tilde_equals
|
5
|
+
attribute = BeginHyphenCondition.new('class', 'foo')
|
6
|
+
node = Node.new('p')
|
7
|
+
node.attributes = { 'class' => 'foobar' }
|
8
|
+
|
9
|
+
assert attribute =~ node
|
10
|
+
|
11
|
+
node.attributes = { 'class' => 'barfoo' }
|
12
|
+
assert(!(attribute =~ node))
|
13
|
+
assert(!(attribute =~ 1))
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/helper"
|
2
|
+
|
3
|
+
class ChildSelectorTest < SelectorTestCase
|
4
|
+
def test_equals2
|
5
|
+
first = ChildSelector.new(1,1)
|
6
|
+
second = ChildSelector.new(1,1)
|
7
|
+
assert_equal first, second
|
8
|
+
|
9
|
+
third = ChildSelector.new(1,2)
|
10
|
+
assert_not_equal first, third
|
11
|
+
|
12
|
+
fourth = ChildSelector.new(2,1)
|
13
|
+
assert_not_equal first, fourth
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_equals_tilde
|
17
|
+
parent = ElementSelector.new('div')
|
18
|
+
child = ElementSelector.new('p')
|
19
|
+
sel = ChildSelector.new(parent, child)
|
20
|
+
|
21
|
+
node = parent_child_tree('div', 'p')
|
22
|
+
assert sel =~ node
|
23
|
+
|
24
|
+
node2 = parent_child_tree('body', 'div', 'p')
|
25
|
+
assert sel =~ node2
|
26
|
+
|
27
|
+
failing = parent_child_tree('p', 'div')
|
28
|
+
assert(! (sel =~ failing))
|
29
|
+
assert(! (sel =~ NoParentNode.new('div')))
|
30
|
+
assert(! (sel =~ Node.new('div', nil, nil)))
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_many_child
|
34
|
+
@sac = CSS::SAC::Parser.new()
|
35
|
+
class << @sac.document_handler
|
36
|
+
attr_accessor :selectors
|
37
|
+
alias :start_selector :selectors=
|
38
|
+
end
|
39
|
+
|
40
|
+
@sac.parse('div > h1 > h2 { }')
|
41
|
+
selectors = @sac.document_handler.selectors
|
42
|
+
assert_equal(1, selectors.length)
|
43
|
+
|
44
|
+
sel = selectors.first
|
45
|
+
node = parent_child_tree('div', 'h1', 'h2')
|
46
|
+
assert sel =~ node
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/helper"
|
2
|
+
|
3
|
+
class ClassConditionTest < ConditionTestCase
|
4
|
+
def test_tilde_equals
|
5
|
+
attribute = ClassCondition.new('foo')
|
6
|
+
node = Node.new('p')
|
7
|
+
node.attributes = { 'class' => 'foo' }
|
8
|
+
|
9
|
+
assert attribute =~ node
|
10
|
+
|
11
|
+
node.attributes = { 'class' => 'barfoo' }
|
12
|
+
assert(!(attribute =~ node))
|
13
|
+
assert(!(attribute =~ 1))
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/helper"
|
2
|
+
|
3
|
+
class CombinatorConditionTest < ConditionTestCase
|
4
|
+
def test_equals2
|
5
|
+
first = CombinatorCondition.new(1,1)
|
6
|
+
second = CombinatorCondition.new(1,1)
|
7
|
+
assert_equal first, second
|
8
|
+
|
9
|
+
third = CombinatorCondition.new(1,2)
|
10
|
+
assert_not_equal first, third
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_equals_tilde
|
14
|
+
first = ClassCondition.new('foo')
|
15
|
+
second = AttributeCondition.new('name', 'aaron', true)
|
16
|
+
combinator_condition = CombinatorCondition.new(first, second)
|
17
|
+
|
18
|
+
node = Node.new('p')
|
19
|
+
node.attributes = { 'class' => 'foo', 'name' => 'aaron' }
|
20
|
+
|
21
|
+
assert combinator_condition =~ node
|
22
|
+
|
23
|
+
node.attributes = { 'class' => 'bar', 'name' => 'aaron' }
|
24
|
+
assert(!(combinator_condition =~ node))
|
25
|
+
|
26
|
+
node.attributes = { 'class' => 'foo', 'name' => 'bar' }
|
27
|
+
assert(!(combinator_condition =~ node))
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/helper"
|
2
|
+
|
3
|
+
class ConditionTest < ConditionTestCase
|
4
|
+
def test_equals2
|
5
|
+
first = Condition.new(1)
|
6
|
+
second = Condition.new(1)
|
7
|
+
assert_equal first, second
|
8
|
+
|
9
|
+
third = Condition.new(2)
|
10
|
+
assert_not_equal first, third
|
11
|
+
assert_not_equal first, 1
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/helper"
|
2
|
+
|
3
|
+
class ConditionalSelectorTest < SelectorTestCase
|
4
|
+
def test_equals2
|
5
|
+
first = ConditionalSelector.new(1,1)
|
6
|
+
second = ConditionalSelector.new(1,1)
|
7
|
+
assert_equal first, second
|
8
|
+
|
9
|
+
third = ConditionalSelector.new(1,2)
|
10
|
+
assert_not_equal first, third
|
11
|
+
|
12
|
+
fourth = ConditionalSelector.new(2,1)
|
13
|
+
assert_not_equal first, fourth
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_equals_tilde
|
17
|
+
attribute = AttributeCondition.new('class', 'foo', true)
|
18
|
+
div = ElementSelector.new('div')
|
19
|
+
sel = ConditionalSelector.new(div, attribute)
|
20
|
+
|
21
|
+
node = Node.new('div')
|
22
|
+
node.attributes = { 'class' => 'foo' }
|
23
|
+
|
24
|
+
assert sel =~ node
|
25
|
+
|
26
|
+
node.attributes = { 'class' => 'bar' }
|
27
|
+
assert(!(sel =~ node))
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/helper"
|
2
|
+
|
3
|
+
class DescendantSelectorTest < SelectorTestCase
|
4
|
+
def test_equals2
|
5
|
+
first = DescendantSelector.new(1,1)
|
6
|
+
second = DescendantSelector.new(1,1)
|
7
|
+
assert_equal first, second
|
8
|
+
|
9
|
+
third = DescendantSelector.new(1,2)
|
10
|
+
assert_not_equal first, third
|
11
|
+
|
12
|
+
fourth = DescendantSelector.new(2,1)
|
13
|
+
assert_not_equal first, fourth
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_equals_tilde
|
17
|
+
parent = ElementSelector.new('div')
|
18
|
+
child = ElementSelector.new('p')
|
19
|
+
sel = DescendantSelector.new(parent, child)
|
20
|
+
|
21
|
+
node = parent_child_tree('div', 'p')
|
22
|
+
assert sel =~ node
|
23
|
+
|
24
|
+
node2 = parent_child_tree('body', 'div', 'p')
|
25
|
+
assert sel =~ node2
|
26
|
+
|
27
|
+
node3 = parent_child_tree('body', 'div', 'table', 'tr', 'td', 'p')
|
28
|
+
assert sel =~ node3
|
29
|
+
|
30
|
+
failing = parent_child_tree('p', 'div')
|
31
|
+
assert(! (sel =~ failing))
|
32
|
+
assert(! (sel =~ NoParentNode.new('div')))
|
33
|
+
assert(! (sel =~ Node.new('div', nil, nil)))
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_many_descendants
|
37
|
+
@sac = CSS::SAC::Parser.new()
|
38
|
+
class << @sac.document_handler
|
39
|
+
attr_accessor :selectors
|
40
|
+
alias :start_selector :selectors=
|
41
|
+
end
|
42
|
+
|
43
|
+
@sac.parse('div h1 h2 { }')
|
44
|
+
selectors = @sac.document_handler.selectors
|
45
|
+
assert_equal(1, selectors.length)
|
46
|
+
|
47
|
+
sel = selectors.first
|
48
|
+
node = parent_child_tree('body', 'div', 'p', 'h1', 'p', 'h2')
|
49
|
+
assert sel =~ node
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/helper"
|
2
|
+
|
3
|
+
class ElementSelectorTest < SelectorTestCase
|
4
|
+
def test_equals2
|
5
|
+
first = ElementSelector.new(1)
|
6
|
+
second = ElementSelector.new(1)
|
7
|
+
assert_equal first, second
|
8
|
+
|
9
|
+
third = ElementSelector.new(2)
|
10
|
+
assert_not_equal first, third
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_equals_tilde
|
14
|
+
div = ElementSelector.new('div')
|
15
|
+
node = Node.new
|
16
|
+
node.name = 'div'
|
17
|
+
|
18
|
+
assert div =~ node
|
19
|
+
assert(!(div =~ nil))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/helper"
|
2
|
+
|
3
|
+
class IdConditionTest < ConditionTestCase
|
4
|
+
def test_equals_tilde
|
5
|
+
attribute = IDCondition.new('some_id')
|
6
|
+
node = Node.new('p')
|
7
|
+
node.attributes = { 'id' => 'some_id' }
|
8
|
+
|
9
|
+
assert attribute =~ node
|
10
|
+
|
11
|
+
node.attributes = { 'id' => 'different' }
|
12
|
+
assert(!(attribute =~ node))
|
13
|
+
assert(!(attribute =~ 1))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/helper"
|
2
|
+
|
3
|
+
class OneOfConditionTest < ConditionTestCase
|
4
|
+
def test_equals_tilde
|
5
|
+
attribute = OneOfCondition.new('class', 'foo')
|
6
|
+
node = Node.new('p')
|
7
|
+
node.attributes = { 'class' => 'aaron foo' }
|
8
|
+
|
9
|
+
assert attribute =~ node
|
10
|
+
|
11
|
+
node.attributes = { 'class' => 'bar' }
|
12
|
+
assert(!(attribute =~ node))
|
13
|
+
assert(!(attribute =~ 1))
|
14
|
+
end
|
15
|
+
end
|
data/test/test_parser.rb
CHANGED
@@ -41,6 +41,14 @@ class ParserTest < Test::Unit::TestCase
|
|
41
41
|
flexmock_verify
|
42
42
|
end
|
43
43
|
|
44
|
+
def test_at_import_many_media
|
45
|
+
flexmock(@sac.document_handler).
|
46
|
+
should_receive(:import_style).ordered.
|
47
|
+
with('"subs.css"', ['paper', 'boobs']).once
|
48
|
+
@sac.parse('@import "subs.css" paper, boobs;')
|
49
|
+
flexmock_verify
|
50
|
+
end
|
51
|
+
|
44
52
|
def test_multi_import
|
45
53
|
flexmock(@sac.document_handler).
|
46
54
|
should_receive(:import_style).ordered.
|
@@ -180,4 +188,54 @@ class ParserTest < Test::Unit::TestCase
|
|
180
188
|
@sac.parse('h1 { color: black !important; }')
|
181
189
|
flexmock_verify
|
182
190
|
end
|
191
|
+
|
192
|
+
def test_pseudo_function
|
193
|
+
flexmock(@sac.document_handler).
|
194
|
+
should_receive(:start_selector).ordered.once
|
195
|
+
flexmock(@sac.document_handler).
|
196
|
+
should_receive(:property).with('color', on { |list|
|
197
|
+
list.length == 1 && list.first.dimension_unit_text.nil? &&
|
198
|
+
list.first.lexical_unit_type == :SAC_IDENT &&
|
199
|
+
list.first.string_value == "black" &&
|
200
|
+
list.first.integer_value.nil?
|
201
|
+
}, true).ordered.once
|
202
|
+
flexmock(@sac.document_handler).
|
203
|
+
should_receive(:end_selector).ordered.once
|
204
|
+
@sac.parse('h1:nth(1) { color: black !important; }')
|
205
|
+
flexmock_verify
|
206
|
+
end
|
207
|
+
|
208
|
+
def test_pseudo_class
|
209
|
+
flexmock(@sac.document_handler).
|
210
|
+
should_receive(:start_selector).ordered.once
|
211
|
+
flexmock(@sac.document_handler).
|
212
|
+
should_receive(:property).with('color', on { |list|
|
213
|
+
list.length == 1 && list.first.dimension_unit_text.nil? &&
|
214
|
+
list.first.lexical_unit_type == :SAC_IDENT &&
|
215
|
+
list.first.string_value == "black" &&
|
216
|
+
list.first.integer_value.nil?
|
217
|
+
}, true).ordered.once
|
218
|
+
flexmock(@sac.document_handler).
|
219
|
+
should_receive(:end_selector).ordered.once
|
220
|
+
@sac.parse('a:hover { color: black !important; }')
|
221
|
+
flexmock_verify
|
222
|
+
end
|
223
|
+
|
224
|
+
def test_pseudo_class_to_css
|
225
|
+
class << @sac.document_handler
|
226
|
+
attr_accessor :selectors
|
227
|
+
alias :start_selector :selectors=
|
228
|
+
end
|
229
|
+
@sac.parse('a:hover { color: black !important; }')
|
230
|
+
assert_equal 'a:hover', @sac.document_handler.selectors.first.to_css
|
231
|
+
end
|
232
|
+
|
233
|
+
def test_pseudo_function_to_css
|
234
|
+
class << @sac.document_handler
|
235
|
+
attr_accessor :selectors
|
236
|
+
alias :start_selector :selectors=
|
237
|
+
end
|
238
|
+
@sac.parse('a:nth(3) { color: black !important; }')
|
239
|
+
assert_equal 'a:nth(3)', @sac.document_handler.selectors.first.to_css
|
240
|
+
end
|
183
241
|
end
|