marcosinger-css_parser 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ @import "import-circular-reference.css";
2
+
3
+ body { color: black; background: white; }
4
+ p { margin: 0px; }
@@ -0,0 +1,3 @@
1
+ @import "simple.css" print, tv, screen;
2
+
3
+ div { color: lime; }
@@ -0,0 +1,3 @@
1
+ @import 'subdir/import2.css';
2
+
3
+ div { color: lime; }
@@ -0,0 +1,6 @@
1
+ body {
2
+ color: black;
3
+ background: white;
4
+ }
5
+
6
+ p { margin: 0px; }
@@ -0,0 +1,3 @@
1
+ @import "../simple.css";
2
+
3
+ a { text-decoration: none; }
@@ -0,0 +1,72 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2
+
3
+ # Test cases for reading and generating CSS shorthand properties
4
+ class CssParserBasicTests < Test::Unit::TestCase
5
+ include CssParser
6
+
7
+ def setup
8
+ @cp = CssParser::Parser.new
9
+ @css = <<-EOT
10
+ html, body, p { margin: 0px; }
11
+ p { padding: 0px; }
12
+ #content { font: 12px/normal sans-serif; }
13
+ .content { color: red; }
14
+ EOT
15
+ end
16
+
17
+ def test_finding_by_selector
18
+ @cp.add_block!(@css)
19
+ assert_equal 'margin: 0px;', @cp.find_by_selector('body').join(' ')
20
+ assert_equal 'margin: 0px; padding: 0px;', @cp.find_by_selector('p').join(' ')
21
+ assert_equal 'font: 12px/normal sans-serif;', @cp.find_by_selector('#content').join(' ')
22
+ assert_equal 'color: red;', @cp.find_by_selector('.content').join(' ')
23
+
24
+ assert_equal 'margin: 0px;', @cp.find_by_selector(/body/).join(' ')
25
+ assert_equal 'margin: 0px; padding: 0px;', @cp.find_by_selector(/p/).join(' ')
26
+ assert_equal 'font: 12px/normal sans-serif;', @cp.find_by_selector(/#content/).join(' ')
27
+ assert_equal 'color: red;', @cp.find_by_selector(/\.content/).join(' ')
28
+
29
+ hash = {"#content" => {"font" => "12px/normal sans-serif;"}, ".content" => {"color" => "red;"}}
30
+ assert_equal hash, @cp.find_by_selector(/content/)
31
+ end
32
+
33
+ def test_adding_block
34
+ @cp.add_block!(@css)
35
+ assert_equal 'margin: 0px;', @cp.find_by_selector('body').join
36
+ end
37
+
38
+ def test_adding_block_without_closing_brace
39
+ @cp.add_block!('p { color: red;')
40
+ assert_equal 'color: red;', @cp.find_by_selector('p').join
41
+ end
42
+
43
+ def test_adding_a_rule
44
+ @cp.add_rule!('div', 'color: blue;')
45
+ assert_equal 'color: blue;', @cp.find_by_selector('div').join(' ')
46
+ end
47
+
48
+ def test_adding_a_rule_set
49
+ rs = CssParser::RuleSet.new('div', 'color: blue;')
50
+ @cp.add_rule_set!(rs)
51
+ assert_equal 'color: blue;', @cp.find_by_selector('div').join(' ')
52
+ end
53
+
54
+ def test_toggling_uri_conversion
55
+ # with conversion
56
+ cp_with_conversion = Parser.new(:absolute_paths => true)
57
+ cp_with_conversion.add_block!("body { background: url('../style/yellow.png?abc=123') };",
58
+ :base_uri => 'http://example.org/style/basic.css')
59
+
60
+ assert_equal "background: url('http://example.org/style/yellow.png?abc=123');",
61
+ cp_with_conversion['body'].join(' ')
62
+
63
+ # without conversion
64
+ cp_without_conversion = Parser.new(:absolute_paths => false)
65
+ cp_without_conversion.add_block!("body { background: url('../style/yellow.png?abc=123') };",
66
+ :base_uri => 'http://example.org/style/basic.css')
67
+
68
+ assert_equal "background: url('../style/yellow.png?abc=123');",
69
+ cp_without_conversion['body'].join(' ')
70
+ end
71
+
72
+ end
@@ -0,0 +1,139 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2
+
3
+ # Test cases for the CssParser's loading functions.
4
+ class CssParserLoadingTests < Test::Unit::TestCase
5
+ include CssParser
6
+ include WEBrick
7
+
8
+ def setup
9
+ # from http://nullref.se/blog/2006/5/17/testing-with-webrick
10
+ @cp = Parser.new
11
+
12
+ @uri_base = 'http://localhost:12000'
13
+
14
+ @www_root = File.dirname(__FILE__) + '/fixtures/'
15
+
16
+ @server_thread = Thread.new do
17
+ s = WEBrick::HTTPServer.new(:Port => 12000, :DocumentRoot => @www_root, :Logger => Log.new(nil, BasicLog::FATAL), :AccessLog => [])
18
+ @port = s.config[:Port]
19
+ begin
20
+ s.start
21
+ ensure
22
+ s.shutdown
23
+ end
24
+ end
25
+
26
+ sleep 1 # ensure the server has time to load
27
+ end
28
+
29
+ def teardown
30
+ @server_thread.kill
31
+ @server_thread.join(5)
32
+ @server_thread = nil
33
+ end
34
+
35
+ def test_loading_a_local_file
36
+ file_name = File.dirname(__FILE__) + '/fixtures/simple.css'
37
+ @cp.load_file!(file_name)
38
+ assert_equal 'margin: 0px;', @cp.find_by_selector('p').join(' ')
39
+ end
40
+
41
+ def test_loading_a_local_file_with_scheme
42
+ file_name = 'file://' + File.expand_path(File.dirname(__FILE__)) + '/fixtures/simple.css'
43
+ @cp.load_uri!(file_name)
44
+ assert_equal 'margin: 0px;', @cp.find_by_selector('p').join(' ')
45
+ end
46
+
47
+ def test_loading_a_remote_file
48
+ @cp.load_uri!("#{@uri_base}/simple.css")
49
+ assert_equal 'margin: 0px;', @cp.find_by_selector('p').join(' ')
50
+ end
51
+
52
+ # http://github.com/alexdunae/css_parser/issues#issue/4
53
+ def test_loading_a_remote_file_over_ssl
54
+ # TODO: test SSL locally
55
+ @cp.load_uri!("https://www.expresspardons.com/inc/screen.css")
56
+ assert_match /margin\: 0\;/, @cp.find_by_selector('body').join(' ')
57
+ end
58
+
59
+
60
+ def test_following_at_import_rules_local
61
+ base_dir = File.dirname(__FILE__) + '/fixtures'
62
+ @cp.load_file!('import1.css', base_dir)
63
+
64
+ # from '/import1.css'
65
+ assert_equal 'color: lime;', @cp.find_by_selector('div').join(' ')
66
+
67
+ # from '/subdir/import2.css'
68
+ assert_equal 'text-decoration: none;', @cp.find_by_selector('a').join(' ')
69
+
70
+ # from '/subdir/../simple.css'
71
+ assert_equal 'margin: 0px;', @cp.find_by_selector('p').join(' ')
72
+ end
73
+
74
+ def test_following_at_import_rules_remote
75
+ @cp.load_uri!("#{@uri_base}/import1.css")
76
+
77
+ # from '/import1.css'
78
+ assert_equal 'color: lime;', @cp.find_by_selector('div').join(' ')
79
+
80
+ # from '/subdir/import2.css'
81
+ assert_equal 'text-decoration: none;', @cp.find_by_selector('a').join(' ')
82
+
83
+ # from '/subdir/../simple.css'
84
+ assert_equal 'margin: 0px;', @cp.find_by_selector('p').join(' ')
85
+ end
86
+
87
+ def test_following_at_import_rules_from_add_block
88
+ css_block = '@import "../simple.css";'
89
+
90
+ @cp.add_block!(css_block, :base_uri => "#{@uri_base}/subdir/")
91
+
92
+ # from 'simple.css'
93
+ assert_equal 'margin: 0px;', @cp.find_by_selector('p').join(' ')
94
+ end
95
+
96
+ def test_importing_with_media_types
97
+ @cp.load_uri!("#{@uri_base}/import-with-media-types.css")
98
+
99
+ # from simple.css with :screen media type
100
+ assert_equal 'margin: 0px;', @cp.find_by_selector('p', :screen).join(' ')
101
+ assert_equal '', @cp.find_by_selector('p', :tty).join(' ')
102
+ end
103
+
104
+ def test_throwing_circular_reference_exception
105
+ assert_raise CircularReferenceError do
106
+ @cp.load_uri!("#{@uri_base}/import-circular-reference.css")
107
+ end
108
+
109
+ cp_without_exceptions = Parser.new(:io_exceptions => false)
110
+
111
+ assert_nothing_raised CircularReferenceError do
112
+ cp_without_exceptions.load_uri!("#{@uri_base}/import-circular-reference.css")
113
+ end
114
+
115
+ end
116
+
117
+ def test_toggling_circular_reference_exception
118
+ cp_without_exceptions = Parser.new(:io_exceptions => false)
119
+
120
+ assert_nothing_raised CircularReferenceError do
121
+ cp_without_exceptions.load_uri!("#{@uri_base}/import-circular-reference.css")
122
+ end
123
+ end
124
+
125
+ def test_toggling_not_found_exceptions
126
+ cp_with_exceptions = Parser.new(:io_exceptions => true)
127
+
128
+ assert_raise RemoteFileError do
129
+ cp_with_exceptions.load_uri!("#{@uri_base}/no-exist.xyz")
130
+ end
131
+
132
+ cp_without_exceptions = Parser.new(:io_exceptions => false)
133
+
134
+ assert_nothing_raised RemoteFileError do
135
+ cp_without_exceptions.load_uri!("#{@uri_base}/no-exist.xyz")
136
+ end
137
+ end
138
+
139
+ end
@@ -0,0 +1,106 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2
+
3
+ # Test cases for the handling of media types
4
+ class CssParserMediaTypesTests < Test::Unit::TestCase
5
+ include CssParser
6
+
7
+ def setup
8
+ @cp = Parser.new
9
+ end
10
+
11
+ def test_finding_by_media_type
12
+ # from http://www.w3.org/TR/CSS21/media.html#at-media-rule
13
+ css = <<-EOT
14
+ @media print {
15
+ body { font-size: 10pt }
16
+ }
17
+ @media screen {
18
+ body { font-size: 13px }
19
+ }
20
+ @media screen, print {
21
+ body { line-height: 1.2 }
22
+ }
23
+ EOT
24
+
25
+ @cp.add_block!(css)
26
+
27
+ assert_equal 'font-size: 10pt; line-height: 1.2;', @cp.find_by_selector('body', :print).join(' ')
28
+ assert_equal 'font-size: 13px; line-height: 1.2;', @cp.find_by_selector('body', :screen).join(' ')
29
+ end
30
+
31
+ def test_finding_by_multiple_media_types
32
+ css = <<-EOT
33
+ @media print {
34
+ body { font-size: 10pt }
35
+ }
36
+ @media handheld {
37
+ body { font-size: 13px }
38
+ }
39
+ @media screen, print {
40
+ body { line-height: 1.2 }
41
+ }
42
+ EOT
43
+ @cp.add_block!(css)
44
+
45
+ assert_equal 'font-size: 13px; line-height: 1.2;', @cp.find_by_selector('body', [:screen,:handheld]).join(' ')
46
+ end
47
+
48
+ def test_adding_block_with_media_types
49
+ css = <<-EOT
50
+ body { font-size: 10pt }
51
+ EOT
52
+
53
+ @cp.add_block!(css, :media_types => [:screen])
54
+
55
+ assert_equal 'font-size: 10pt;', @cp.find_by_selector('body', :screen).join(' ')
56
+ assert @cp.find_by_selector('body', :handheld).empty?
57
+ end
58
+
59
+ def test_adding_block_and_limiting_media_types1
60
+ css = <<-EOT
61
+ @import "import1.css", print
62
+ EOT
63
+
64
+ base_dir = File.dirname(__FILE__) + '/fixtures/'
65
+
66
+ @cp.add_block!(css, :only_media_types => :screen, :base_dir => base_dir)
67
+ assert @cp.find_by_selector('div').empty?
68
+
69
+ end
70
+
71
+ def test_adding_block_and_limiting_media_types2
72
+ css = <<-EOT
73
+ @import "import1.css", print
74
+ EOT
75
+
76
+ base_dir = File.dirname(__FILE__) + '/fixtures/'
77
+
78
+ @cp.add_block!(css, :only_media_types => :print, :base_dir => base_dir)
79
+ assert_match 'color: lime', @cp.find_by_selector('div').join(' ')
80
+ end
81
+
82
+ def test_adding_block_and_limiting_media_types
83
+ css = <<-EOT
84
+ @import "import1.css"
85
+ EOT
86
+
87
+ base_dir = File.dirname(__FILE__) + '/fixtures/'
88
+
89
+ @cp.add_block!(css, :only_media_types => :print, :base_dir => base_dir)
90
+ assert_match 'color: lime', @cp.find_by_selector('div').join(' ')
91
+ end
92
+
93
+
94
+ def test_adding_rule_set_with_media_type
95
+ @cp.add_rule!('body', 'color: black;', [:handheld,:tty])
96
+ @cp.add_rule!('body', 'color: blue;', :screen)
97
+ assert_equal 'color: black;', @cp.find_by_selector('body', :handheld).join(' ')
98
+ end
99
+
100
+ def test_selecting_with_all_media_types
101
+ @cp.add_rule!('body', 'color: black;', [:handheld,:tty])
102
+ assert_equal 'color: black;', @cp.find_by_selector('body', :all).join(' ')
103
+ end
104
+
105
+
106
+ end
@@ -0,0 +1,164 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2
+
3
+ # Test cases for the CssParser.
4
+ class CssParserTests < Test::Unit::TestCase
5
+ include CssParser
6
+
7
+ def setup
8
+ @cp = Parser.new
9
+ end
10
+
11
+ def test_at_page_rule
12
+ # from http://www.w3.org/TR/CSS21/page.html#page-selectors
13
+ css = <<-EOT
14
+ @page { margin: 2cm }
15
+
16
+ @page :first {
17
+ margin-top: 10cm
18
+ }
19
+ EOT
20
+
21
+ @cp.add_block!(css)
22
+
23
+ assert_equal 'margin: 2cm;', @cp.find_by_selector('@page').join(' ')
24
+ assert_equal 'margin-top: 10cm;', @cp.find_by_selector('@page :first').join(' ')
25
+ end
26
+
27
+ def test_should_ignore_comments
28
+ # see http://www.w3.org/Style/CSS/Test/CSS2.1/current/html4/t040109-c17-comments-00-b.htm
29
+ css =<<-EOT
30
+ /* This is a CSS comment. */
31
+ .one {color: green;} /* Another comment */
32
+ /* The following should not be used:
33
+ .one {color: red;} */
34
+ .two {color: green; /* color: yellow; */}
35
+ /**
36
+ .three {color: red;} */
37
+ .three {color: green;}
38
+ /**/
39
+ .four {color: green;}
40
+ /*********/
41
+ .five {color: green;}
42
+ /* a comment **/
43
+ .six {color: green;}
44
+ EOT
45
+
46
+ @cp.add_block!(css)
47
+ @cp.each_selector do |sel, decs, spec|
48
+ assert_equal 'color: green;', decs
49
+ end
50
+ end
51
+
52
+ def test_parsing_blocks
53
+ # dervived from http://www.w3.org/TR/CSS21/syndata.html#rule-sets
54
+ css = <<-EOT
55
+ div[name='test'] {
56
+
57
+ color:
58
+
59
+ red;
60
+
61
+ }div:hover{coloR:red;
62
+ }div:first-letter{color:red;/*color:blue;}"commented out"*/}
63
+
64
+ p[example="public class foo\
65
+ {\
66
+ private string x;\
67
+ \
68
+ foo(int x) {\
69
+ this.x = 'test';\
70
+ this.x = \"test\";\
71
+ }\
72
+ \
73
+ }"] { color: red }
74
+
75
+ p { color:red}
76
+ EOT
77
+
78
+ @cp.add_block!(css)
79
+
80
+ @cp.each_selector do |sel, decs, spec|
81
+ assert_equal 'color: red;', decs
82
+ end
83
+ end
84
+
85
+ def test_ignoring_malformed_declarations
86
+ # dervived from http://www.w3.org/TR/CSS21/syndata.html#parsing-errors
87
+ css = <<-EOT
88
+ p { color:green }
89
+ p { color:green; color } /* malformed declaration missing ':', value */
90
+ p { color:red; color; color:green } /* same with expected recovery */
91
+ p { color:green; color: } /* malformed declaration missing value */
92
+ p { color:red; color:; color:green } /* same with expected recovery */
93
+ p { color:green; color{;color:maroon} } /* unexpected tokens { } */
94
+ p { color:red; color{;color:maroon}; color:green } /* same with recovery */
95
+ EOT
96
+
97
+ @cp.add_block!(css)
98
+
99
+ @cp.each_selector do |sel, decs, spec|
100
+ assert_equal 'color: green;', decs
101
+ end
102
+ end
103
+
104
+ def test_calculating_specificity
105
+ # from http://www.w3.org/TR/CSS21/cascade.html#specificity
106
+ assert_equal 0, CssParser.calculate_specificity('*')
107
+ assert_equal 1, CssParser.calculate_specificity('li')
108
+ assert_equal 2, CssParser.calculate_specificity('li:first-line')
109
+ assert_equal 2, CssParser.calculate_specificity('ul li')
110
+ assert_equal 3, CssParser.calculate_specificity('ul ol+li')
111
+ assert_equal 11, CssParser.calculate_specificity('h1 + *[rel=up]')
112
+ assert_equal 13, CssParser.calculate_specificity('ul ol li.red')
113
+ assert_equal 21, CssParser.calculate_specificity('li.red.level')
114
+ assert_equal 100, CssParser.calculate_specificity('#x34y')
115
+
116
+ # from http://www.hixie.ch/tests/adhoc/css/cascade/specificity/003.html
117
+ assert_equal CssParser.calculate_specificity('div *'), CssParser.calculate_specificity('p')
118
+ assert CssParser.calculate_specificity('body div *') > CssParser.calculate_specificity('div *')
119
+
120
+ # other tests
121
+ assert_equal 11, CssParser.calculate_specificity('h1[id|=123]')
122
+ end
123
+
124
+ def test_converting_uris
125
+ base_uri = 'http://www.example.org/style/basic.css'
126
+ ["body { background: url(yellow) };", "body { background: url('yellow') };",
127
+ "body { background: url('/style/yellow') };",
128
+ "body { background: url(\"../style/yellow\") };",
129
+ "body { background: url(\"lib/../../style/yellow\") };"].each do |css|
130
+ converted_css = CssParser.convert_uris(css, base_uri)
131
+ assert_equal "body { background: url('http://www.example.org/style/yellow') };", converted_css
132
+ end
133
+
134
+ converted_css = CssParser.convert_uris("body { background: url(../style/yellow-dot_symbol$.png?abc=123&amp;def=456&ghi=789#1011) };", base_uri)
135
+ assert_equal "body { background: url('http://www.example.org/style/yellow-dot_symbol$.png?abc=123&amp;def=456&ghi=789#1011') };", converted_css
136
+
137
+ # taken from error log: 2007-10-23 04:37:41#2399
138
+ converted_css = CssParser.convert_uris('.specs {font-family:Helvetica;font-weight:bold;font-style:italic;color:#008CA8;font-size:1.4em;list-style-image:url("images/bullet.gif");}', 'http://www.example.org/directory/file.html')
139
+ assert_equal ".specs {font-family:Helvetica;font-weight:bold;font-style:italic;color:#008CA8;font-size:1.4em;list-style-image:url('http://www.example.org/directory/images/bullet.gif');}", converted_css
140
+ end
141
+
142
+ def test_ruleset_with_braces
143
+ =begin
144
+ parser = Parser.new
145
+ parser.add_block!("div { background-color: black !important; }")
146
+ parser.add_block!("div { background-color: red; }")
147
+
148
+ rulesets = []
149
+
150
+ parser['div'].each do |declaration|
151
+ rulesets << RuleSet.new('div', declaration)
152
+ end
153
+
154
+ merged = CssParser.merge(rulesets)
155
+
156
+ result: # merged.to_s => "{ background-color: black !important; }"
157
+ =end
158
+
159
+ new_rule = RuleSet.new('div', "{ background-color: black !important; }")
160
+
161
+ assert_equal 'div { background-color: black !important; }', new_rule.to_s
162
+ end
163
+
164
+ end