haml-edge 2.3.179 → 2.3.180

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 (92) hide show
  1. data/EDGE_GEM_VERSION +1 -1
  2. data/README.md +88 -149
  3. data/VERSION +1 -1
  4. data/bin/css2sass +7 -1
  5. data/bin/sass-convert +7 -0
  6. data/lib/haml/exec.rb +95 -22
  7. data/lib/haml/template.rb +1 -1
  8. data/lib/haml/util.rb +50 -0
  9. data/lib/sass.rb +1 -1
  10. data/lib/sass/css.rb +38 -210
  11. data/lib/sass/engine.rb +121 -47
  12. data/lib/sass/files.rb +28 -19
  13. data/lib/sass/plugin.rb +32 -43
  14. data/lib/sass/repl.rb +1 -1
  15. data/lib/sass/script.rb +25 -6
  16. data/lib/sass/script/bool.rb +1 -0
  17. data/lib/sass/script/color.rb +2 -2
  18. data/lib/sass/script/css_lexer.rb +22 -0
  19. data/lib/sass/script/css_parser.rb +28 -0
  20. data/lib/sass/script/funcall.rb +17 -9
  21. data/lib/sass/script/functions.rb +46 -1
  22. data/lib/sass/script/interpolation.rb +42 -0
  23. data/lib/sass/script/lexer.rb +142 -34
  24. data/lib/sass/script/literal.rb +28 -12
  25. data/lib/sass/script/node.rb +57 -1
  26. data/lib/sass/script/number.rb +18 -3
  27. data/lib/sass/script/operation.rb +44 -8
  28. data/lib/sass/script/parser.rb +149 -24
  29. data/lib/sass/script/string.rb +50 -2
  30. data/lib/sass/script/unary_operation.rb +25 -10
  31. data/lib/sass/script/variable.rb +20 -11
  32. data/lib/sass/scss.rb +14 -0
  33. data/lib/sass/scss/css_parser.rb +39 -0
  34. data/lib/sass/scss/parser.rb +683 -0
  35. data/lib/sass/scss/rx.rb +112 -0
  36. data/lib/sass/scss/script_lexer.rb +13 -0
  37. data/lib/sass/scss/script_parser.rb +25 -0
  38. data/lib/sass/tree/comment_node.rb +58 -16
  39. data/lib/sass/tree/debug_node.rb +7 -2
  40. data/lib/sass/tree/directive_node.rb +38 -34
  41. data/lib/sass/tree/for_node.rb +6 -0
  42. data/lib/sass/tree/if_node.rb +13 -0
  43. data/lib/sass/tree/import_node.rb +26 -7
  44. data/lib/sass/tree/mixin_def_node.rb +18 -0
  45. data/lib/sass/tree/mixin_node.rb +16 -1
  46. data/lib/sass/tree/node.rb +98 -27
  47. data/lib/sass/tree/prop_node.rb +97 -20
  48. data/lib/sass/tree/root_node.rb +37 -0
  49. data/lib/sass/tree/rule_node.rb +88 -60
  50. data/lib/sass/tree/variable_node.rb +9 -5
  51. data/lib/sass/tree/while_node.rb +4 -0
  52. data/test/haml/results/filters.xhtml +1 -1
  53. data/test/haml/util_test.rb +28 -0
  54. data/test/sass/conversion_test.rb +884 -0
  55. data/test/sass/css2sass_test.rb +46 -21
  56. data/test/sass/engine_test.rb +680 -160
  57. data/test/sass/functions_test.rb +27 -0
  58. data/test/sass/more_results/more_import.css +1 -1
  59. data/test/sass/more_templates/more_import.sass +3 -3
  60. data/test/sass/plugin_test.rb +28 -8
  61. data/test/sass/results/compact.css +1 -1
  62. data/test/sass/results/complex.css +5 -5
  63. data/test/sass/results/compressed.css +1 -1
  64. data/test/sass/results/expanded.css +1 -1
  65. data/test/sass/results/import.css +3 -1
  66. data/test/sass/results/mixins.css +12 -12
  67. data/test/sass/results/nested.css +1 -1
  68. data/test/sass/results/parent_ref.css +4 -4
  69. data/test/sass/results/script.css +3 -3
  70. data/test/sass/results/scss_import.css +15 -0
  71. data/test/sass/results/scss_importee.css +2 -0
  72. data/test/sass/script_conversion_test.rb +153 -0
  73. data/test/sass/script_test.rb +44 -54
  74. data/test/sass/scss/css_test.rb +811 -0
  75. data/test/sass/scss/rx_test.rb +156 -0
  76. data/test/sass/scss/scss_test.rb +871 -0
  77. data/test/sass/scss/test_helper.rb +37 -0
  78. data/test/sass/templates/alt.sass +2 -2
  79. data/test/sass/templates/bork1.sass +1 -1
  80. data/test/sass/templates/import.sass +4 -4
  81. data/test/sass/templates/importee.sass +3 -3
  82. data/test/sass/templates/line_numbers.sass +1 -1
  83. data/test/sass/templates/mixins.sass +2 -2
  84. data/test/sass/templates/nested_mixin_bork.sass +1 -1
  85. data/test/sass/templates/options.sass +1 -1
  86. data/test/sass/templates/parent_ref.sass +2 -2
  87. data/test/sass/templates/script.sass +69 -69
  88. data/test/sass/templates/scss_import.scss +10 -0
  89. data/test/sass/templates/scss_importee.scss +1 -0
  90. data/test/sass/templates/units.sass +10 -10
  91. data/test/test_helper.rb +4 -4
  92. metadata +27 -2
@@ -10,30 +10,23 @@ module Sass::Tree
10
10
  # @private
11
11
  PARENT = '&'
12
12
 
13
- # The CSS selectors for this rule.
14
- # Each string is a selector line, and the lines are meant to be separated by commas.
15
- # For example,
16
- #
17
- # foo, bar, baz,
18
- # bip, bop, bup
19
- #
20
- # would be
21
- #
22
- # ["foo, bar, baz",
23
- # "bip, bop, bup"]
13
+ # The CSS selector for this rule,
14
+ # interspersed with {Sass::Script::Node}s
15
+ # representing `#{}`-interpolation.
16
+ # Any adjacent strings will be merged together.
24
17
  #
25
- # @return [Array<String>]
26
- attr_accessor :rules
18
+ # @return [Array<String, Sass::Script::Node>]
19
+ attr_accessor :rule
27
20
 
28
21
  # The CSS selectors for this rule,
29
22
  # parsed for commas and parent-references.
30
23
  # It's only set once {Tree::Node#perform} has been called.
31
24
  #
32
- # It's an array of arrays of arrays.
33
- # The first level of arrays represents distinct lines in the Sass file;
34
- # the second level represents comma-separated selectors;
35
- # the third represents structure within those selectors,
25
+ # It's an array of arrays.
26
+ # The first level of arrays comma-separated selectors;
27
+ # the second represents structure within those selectors,
36
28
  # currently only parent-refs (represented by `:parent`).
29
+ # Newlines are represented as literal `\n` characters in the strings.
37
30
  # For example,
38
31
  #
39
32
  # &.foo, bar, baz,
@@ -41,18 +34,18 @@ module Sass::Tree
41
34
  #
42
35
  # would be
43
36
  #
44
- # [[[:parent, ".foo"], ["bar"], ["baz"]],
45
- # [["bip"], [:parent, ".bop"], ["bup"]]]
37
+ # [[:parent, ".foo"], ["bar"], ["baz"],
38
+ # ["\nbip"], [:parent, ".bop"], ["bup"]]
46
39
  #
47
- # @return [Array<Array<Array<String|Symbol>>>]
40
+ # @return [Array<Array<String, Symbol>>]
48
41
  attr_accessor :parsed_rules
49
42
 
50
43
  # The CSS selectors for this rule,
51
44
  # with all nesting and parent references resolved.
52
45
  # It's only set once {Tree::Node#cssize} has been called.
53
46
  #
54
- # The first level of arrays represents distinct lines in the Sass file;
55
- # the second level represents comma-separated selectors.
47
+ # Each element is a distinct selector, separated by commas.
48
+ # Newlines are represented as literal `\n` characters in the strings.
56
49
  # For example,
57
50
  #
58
51
  # foo bar, baz,
@@ -60,10 +53,9 @@ module Sass::Tree
60
53
  #
61
54
  # would be
62
55
  #
63
- # [["foo bar", "baz"],
64
- # ["bang", "bip bop", "blip"]]
56
+ # ["foo bar", "baz", "\nbang", "bip bop", "blip"]
65
57
  #
66
- # @return [Array<Array<String>>]
58
+ # @return [Array<String>]
67
59
  attr_accessor :resolved_rules
68
60
 
69
61
  # How deep this rule is indented
@@ -84,9 +76,11 @@ module Sass::Tree
84
76
  # @return [Boolean]
85
77
  attr_accessor :group_end
86
78
 
87
- # @param rule [String] The first CSS rule. See \{#rules}
79
+ # @param rule [Array<String, Sass::Script::Node>]
80
+ # The CSS rule. See \{#rule}
88
81
  def initialize(rule)
89
- @rules = [rule]
82
+ @rule = Haml::Util.strip_string_array(
83
+ Haml::Util.merge_adjacent_strings(rule))
90
84
  @tabs = 0
91
85
  super()
92
86
  end
@@ -97,19 +91,48 @@ module Sass::Tree
97
91
  # @return [Boolean] Whether or not this node and the other object
98
92
  # are the same
99
93
  def ==(other)
100
- self.class == other.class && rules == other.rules && super
94
+ self.class == other.class && rule == other.rule && super
101
95
  end
102
96
 
103
97
  # Adds another {RuleNode}'s rules to this one's.
104
98
  #
105
99
  # @param node [RuleNode] The other node
106
100
  def add_rules(node)
107
- @rules += node.rules
101
+ @rule = Haml::Util.strip_string_array(
102
+ Haml::Util.merge_adjacent_strings(@rule + ["\n"] + node.rule))
108
103
  end
109
104
 
110
105
  # @return [Boolean] Whether or not this rule is continued on the next line
111
106
  def continued?
112
- @rules.last[-1] == ?,
107
+ last = @rule.last
108
+ last.is_a?(String) && last[-1] == ?,
109
+ end
110
+
111
+ # @see Node#to_sass
112
+ def to_sass(tabs, opts = {})
113
+ name = rule.map do |r|
114
+ if r.is_a?(String)
115
+ r.gsub(/(,[ \t]*)?\n\s*/) {$1 ? $1 + "\n" : " "}
116
+ else
117
+ "\#{#{r.to_sass}}"
118
+ end
119
+ end.join
120
+ name = "\\" + name if name[0] == ?:
121
+ name.gsub(/^/, ' ' * tabs) + children_to_src(tabs, opts, :sass)
122
+ end
123
+
124
+ def to_scss(tabs, opts = {})
125
+ name = rule.map {|r| r.is_a?(String) ? r : "\#{#{r.to_sass}}"}.
126
+ join.gsub(/^[ \t]*/, ' ' * tabs)
127
+
128
+ res = name + children_to_src(tabs, opts, :scss)
129
+
130
+ if children.last.is_a?(CommentNode) && children.last.silent
131
+ res.slice!(-3..-1)
132
+ res << "\n" << (' ' * tabs) << "}\n"
133
+ end
134
+
135
+ res
113
136
  end
114
137
 
115
138
  protected
@@ -122,12 +145,17 @@ module Sass::Tree
122
145
  tabs = tabs + self.tabs
123
146
 
124
147
  rule_separator = style == :compressed ? ',' : ', '
125
- line_separator = [:nested, :expanded].include?(style) ? ",\n" : rule_separator
148
+ line_separator =
149
+ case style
150
+ when :nested, :expanded; "\n"
151
+ when :compressed; ""
152
+ else; " "
153
+ end
126
154
  rule_indent = ' ' * (tabs - 1)
127
155
  per_rule_indent, total_indent = [:nested, :expanded].include?(style) ? [rule_indent, ''] : ['', rule_indent]
128
156
 
129
- total_rule = total_indent + resolved_rules.map do |line|
130
- per_rule_indent + line.join(rule_separator)
157
+ total_rule = total_indent + resolved_rules.join(rule_separator).split("\n").map do |line|
158
+ per_rule_indent + line.strip
131
159
  end.join(line_separator)
132
160
 
133
161
  to_return = ''
@@ -157,13 +185,13 @@ module Sass::Tree
157
185
  end
158
186
 
159
187
  if style == :compact
160
- properties = children.map { |a| a.to_s(1) }.select{|a| a && a.length > 0}.join(' ')
188
+ properties = children.map { |a| a.to_s(1) }.join(' ')
161
189
  to_return << "#{total_rule} { #{properties} }#{"\n" if group_end}"
162
190
  elsif style == :compressed
163
- properties = children.map { |a| a.to_s(1) }.select{|a| a && a.length > 0}.join(';')
191
+ properties = children.map { |a| a.to_s(1) }.join(';')
164
192
  to_return << "#{total_rule}{#{properties}}"
165
193
  else
166
- properties = children.map { |a| a.to_s(tabs + 1) }.select{|a| a && a.length > 0}.join("\n")
194
+ properties = children.map { |a| a.to_s(tabs + 1) }.join("\n")
167
195
  end_props = (style == :expanded ? "\n" + old_spaces : ' ')
168
196
  to_return << "#{total_rule} {\n#{properties}#{end_props}}#{"\n" if group_end}"
169
197
  end
@@ -177,7 +205,7 @@ module Sass::Tree
177
205
  # @param environment [Sass::Environment] The lexical environment containing
178
206
  # variable and mixin values
179
207
  def perform!(environment)
180
- @parsed_rules = @rules.map {|r| parse_selector(interpolate(r, environment))}
208
+ @parsed_rules = parse_selector(run_interp(@rule, environment))
181
209
  super
182
210
  end
183
211
 
@@ -226,32 +254,29 @@ module Sass::Tree
226
254
 
227
255
  def resolve_parent_refs(super_rules)
228
256
  if super_rules.nil?
229
- return @parsed_rules.map do |line|
230
- line.map do |rule|
231
- if rule.include?(:parent)
232
- raise Sass::SyntaxError.new("Base-level rules cannot contain the parent-selector-referencing character '#{PARENT}'.")
233
- end
257
+ return @parsed_rules.map do |rule|
258
+ if rule.include?(:parent)
259
+ raise Sass::SyntaxError.new("Base-level rules cannot contain the parent-selector-referencing character '#{PARENT}'.")
260
+ end
234
261
 
235
- rule.join
236
- end.compact
262
+ rule.join
237
263
  end
238
264
  end
239
265
 
240
266
  new_rules = []
241
- super_rules.each do |super_line|
242
- @parsed_rules.each do |line|
267
+ super_rules.each do |super_rule|
268
+ @parsed_rules.each do |rule|
243
269
  new_rules << []
244
270
 
245
- super_line.each do |super_rule|
246
- line.each do |rule|
247
- rule = [:parent, " ", *rule] unless rule.include?(:parent)
271
+ # An initial newline of the child rule
272
+ # should be moved to the beginning of the entire rule
273
+ rule.first.slice!(0) if nl = (rule.first.is_a?(String) && rule.first[0] == ?\n)
274
+ rule = [nl ? "\n" : "", :parent, " ", *rule] unless rule.include?(:parent)
248
275
 
249
- new_rules.last << rule.map do |segment|
250
- next segment unless segment == :parent
251
- super_rule
252
- end.join
253
- end
254
- end
276
+ new_rules.last << rule.map do |segment|
277
+ next segment unless segment == :parent
278
+ super_rule
279
+ end.join
255
280
  end
256
281
  end
257
282
  new_rules
@@ -267,7 +292,10 @@ module Sass::Tree
267
292
  when '&'; rules.last << :parent
268
293
  when ','
269
294
  scanner.scan(/\s*/)
270
- rules << [] if scanner.rest?
295
+ if scanner.rest?
296
+ rules << []
297
+ rules.last << "\n" if scanner.matched.include?("\n")
298
+ end
271
299
  when '"'
272
300
  rules.last << '"' << scanner.scan(/([^"\\]|\\.)*/)
273
301
  # We don't want to enforce that strings are closed,
@@ -286,11 +314,11 @@ module Sass::Tree
286
314
  def debug_info_rule
287
315
  node = DirectiveNode.new("@media -sass-debug-info")
288
316
  debug_info.map {|k, v| [k.to_s, v.to_s]}.sort.each do |k, v|
289
- rule = RuleNode.new(nil)
317
+ rule = RuleNode.new([""])
290
318
  rule.resolved_rules = [[k.to_s.gsub(/[^\w-]/, "\\\\\\0")]]
291
- val = v.to_s.gsub(/[^\w-]/, "\\\\\\0").
292
- gsub(/^[\d-]/) {|c| "\\%04x " % Haml::Util.ord(c)}
293
- prop = PropNode.new("font-family", val, :new)
319
+ prop = PropNode.new("", "", :new)
320
+ prop.resolved_name = "font-family"
321
+ prop.resolved_value = Sass::SCSS::RX.escape_ident(v.to_s)
294
322
  rule << prop
295
323
  node << rule
296
324
  end
@@ -16,17 +16,21 @@ module Sass
16
16
 
17
17
  protected
18
18
 
19
+ def to_src(tabs, opts, fmt)
20
+ "#{' ' * tabs}$#{@name}: #{@expr.to_sass}#{' !default' if @guarded}#{semi fmt}\n"
21
+ end
22
+
19
23
  # Loads the new variable value into the environment.
20
24
  #
21
25
  # @param environment [Sass::Environment] The lexical environment containing
22
26
  # variable and mixin values
23
27
  def _perform(environment)
24
- if @guarded && environment.var(@name).nil?
25
- environment.set_var(@name, @expr.perform(environment))
26
- elsif !@guarded
27
- environment.set_var(@name, @expr.perform(environment))
28
+ return [] if @guarded && !environment.var(@name).nil?
29
+ val = @expr.perform(environment)
30
+ if @expr.context == :equals && val.is_a?(Sass::Script::String)
31
+ val = Sass::Script::String.new(val.value)
28
32
  end
29
-
33
+ environment.set_var(@name, val)
30
34
  []
31
35
  end
32
36
  end
@@ -13,6 +13,10 @@ module Sass::Tree
13
13
 
14
14
  protected
15
15
 
16
+ def to_src(tabs, opts, fmt)
17
+ "#{' ' * tabs}@while #{@expr.to_sass}" + children_to_src(tabs, opts, fmt)
18
+ end
19
+
16
20
  # Runs the child nodes until the continue expression becomes false.
17
21
  #
18
22
  # @param environment [Sass::Environment] The lexical environment containing
@@ -1,6 +1,6 @@
1
1
  <style>
2
2
  /* line 1 */
3
- p { border-style: dotted; border-width: 22px; border-color: #ff00ff; }
3
+ p { border-style: dotted; border-width: 22px; border-color: fuchsia; }
4
4
 
5
5
  /* line 6 */
6
6
  h1 { font-weight: normal; }
@@ -66,6 +66,15 @@ class UtilTest < Test::Unit::TestCase
66
66
  merge_adjacent_strings(["foo ", "bar ", "baz", :bang, "biz", " bop", 12]))
67
67
  end
68
68
 
69
+ def test_strip_string_array
70
+ assert_equal(["foo ", " bar ", " baz"],
71
+ strip_string_array([" foo ", " bar ", " baz "]))
72
+ assert_equal([:foo, " bar ", " baz"],
73
+ strip_string_array([:foo, " bar ", " baz "]))
74
+ assert_equal(["foo ", " bar ", :baz],
75
+ strip_string_array([" foo ", " bar ", :baz]))
76
+ end
77
+
69
78
  def test_silence_warnings
70
79
  old_stderr, $stderr = $stderr, StringIO.new
71
80
  warn "Out"
@@ -76,6 +85,20 @@ class UtilTest < Test::Unit::TestCase
76
85
  $stderr = old_stderr
77
86
  end
78
87
 
88
+ def test_haml_warn
89
+ assert_warning("Foo!") {haml_warn "Foo!"}
90
+ end
91
+
92
+ def test_silence_haml_warnings
93
+ old_stderr, $stderr = $stderr, StringIO.new
94
+ silence_haml_warnings {warn "Out"}
95
+ assert_equal("Out\n", $stderr.string)
96
+ silence_haml_warnings {haml_warn "In"}
97
+ assert_equal("Out\n", $stderr.string)
98
+ ensure
99
+ $stderr = old_stderr
100
+ end
101
+
79
102
  def test_has
80
103
  assert(has?(:instance_method, String, :chomp!))
81
104
  assert(has?(:private_instance_method, Haml::Engine, :set_locals))
@@ -86,6 +109,11 @@ class UtilTest < Test::Unit::TestCase
86
109
  enum_with_index(%w[foo bar baz]).map {|s, i| "#{s}#{i}"})
87
110
  end
88
111
 
112
+ def test_enum_cons
113
+ assert_equal(%w[foobar barbaz],
114
+ enum_cons(%w[foo bar baz], 2).map {|s1, s2| "#{s1}#{s2}"})
115
+ end
116
+
89
117
  def test_ord
90
118
  assert_equal(102, ord("f"))
91
119
  assert_equal(98, ord("bar"))
@@ -0,0 +1,884 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../test_helper'
3
+
4
+ class ConversionTest < Test::Unit::TestCase
5
+ def test_basic
6
+ assert_renders <<SASS, <<SCSS
7
+ foo bar
8
+ baz: bang
9
+ bip: bop
10
+ SASS
11
+ foo bar {
12
+ baz: bang;
13
+ bip: bop; }
14
+ SCSS
15
+ assert_renders <<SASS, <<SCSS, :old => true
16
+ foo bar
17
+ :baz bang
18
+ :bip bop
19
+ SASS
20
+ foo bar {
21
+ baz: bang;
22
+ bip: bop; }
23
+ SCSS
24
+ end
25
+
26
+ def test_nesting
27
+ assert_renders <<SASS, <<SCSS
28
+ foo bar
29
+ baz bang
30
+ baz: bang
31
+ bip: bop
32
+ blat: boo
33
+ SASS
34
+ foo bar {
35
+ baz bang {
36
+ baz: bang;
37
+ bip: bop; }
38
+ blat: boo; }
39
+ SCSS
40
+ end
41
+
42
+ def test_nesting_with_parent_ref
43
+ assert_renders <<SASS, <<SCSS
44
+ foo bar
45
+ &:hover
46
+ baz: bang
47
+ SASS
48
+ foo bar {
49
+ &:hover {
50
+ baz: bang; } }
51
+ SCSS
52
+ end
53
+
54
+ def test_selector_interpolation
55
+ assert_renders <<SASS, <<SCSS
56
+ foo \#{$bar + "baz"}.bip
57
+ baz: bang
58
+ SASS
59
+ foo \#{$bar + "baz"}.bip {
60
+ baz: bang; }
61
+ SCSS
62
+ end
63
+
64
+ def test_multiline_selector_with_commas
65
+ assert_renders <<SASS, <<SCSS
66
+ foo bar,
67
+ baz bang
68
+ baz: bang
69
+ SASS
70
+ foo bar,
71
+ baz bang {
72
+ baz: bang; }
73
+ SCSS
74
+
75
+ assert_renders <<SASS, <<SCSS
76
+ blat
77
+ foo bar,
78
+ baz bang
79
+ baz: bang
80
+ SASS
81
+ blat {
82
+ foo bar,
83
+ baz bang {
84
+ baz: bang; } }
85
+ SCSS
86
+ end
87
+
88
+ def test_multiline_selector_without_commas
89
+ assert_scss_to_sass <<SASS, <<SCSS
90
+ foo bar baz bang
91
+ baz: bang
92
+ SASS
93
+ foo bar
94
+ baz bang {
95
+ baz: bang; }
96
+ SCSS
97
+
98
+ assert_scss_to_scss <<SCSS
99
+ foo bar
100
+ baz bang {
101
+ baz: bang; }
102
+ SCSS
103
+ end
104
+
105
+ def test_escaped_selector
106
+ assert_renders <<SASS, <<SCSS
107
+ foo bar
108
+ \\:hover
109
+ baz: bang
110
+ SASS
111
+ foo bar {
112
+ :hover {
113
+ baz: bang; } }
114
+ SCSS
115
+ end
116
+
117
+ def test_property_name_interpolation
118
+ assert_renders <<SASS, <<SCSS
119
+ foo bar
120
+ baz\#{$bang}bip\#{$bop}: 12
121
+ SASS
122
+ foo bar {
123
+ baz\#{$bang}bip\#{$bop}: 12; }
124
+ SCSS
125
+ end
126
+
127
+ def test_property_name_interpolation
128
+ assert_renders <<SASS, <<SCSS
129
+ foo bar
130
+ baz\#{$bang}bip\#{$bop}: 12
131
+ SASS
132
+ foo bar {
133
+ baz\#{$bang}bip\#{$bop}: 12; }
134
+ SCSS
135
+ end
136
+
137
+ def test_property_value_interpolation
138
+ assert_renders <<SASS, <<SCSS
139
+ foo bar
140
+ baz: 12 \#{$bang} bip \#{"bop"} blat
141
+ SASS
142
+ foo bar {
143
+ baz: 12 \#{$bang} bip \#{"bop"} blat; }
144
+ SCSS
145
+ end
146
+
147
+ def test_dynamic_properties
148
+ assert_renders <<SASS, <<SCSS
149
+ foo bar
150
+ baz: 12 $bang "bip"
151
+ SASS
152
+ foo bar {
153
+ baz: 12 $bang "bip"; }
154
+ SCSS
155
+
156
+ assert_sass_to_scss <<SCSS, <<SASS
157
+ foo bar {
158
+ baz: 12 $bang bip; }
159
+ SCSS
160
+ foo bar
161
+ baz= 12 $bang "bip"
162
+ SASS
163
+ end
164
+
165
+ def test_dynamic_properties_with_old
166
+ assert_renders <<SASS, <<SCSS, :old => true
167
+ foo bar
168
+ :baz 12 $bang "bip"
169
+ SASS
170
+ foo bar {
171
+ baz: 12 $bang "bip"; }
172
+ SCSS
173
+
174
+ assert_sass_to_scss <<SCSS, <<SASS, :old => true
175
+ foo bar {
176
+ baz: 12 $bang bip; }
177
+ SCSS
178
+ foo bar
179
+ :baz= 12 $bang "bip"
180
+ SASS
181
+ end
182
+
183
+ def test_multiline_properties
184
+ assert_scss_to_sass <<SASS, <<SCSS
185
+ foo bar
186
+ baz: bip bam boon
187
+ SASS
188
+ foo bar {
189
+ baz:
190
+ bip
191
+ bam
192
+ boon; }
193
+ SCSS
194
+
195
+ assert_scss_to_scss <<OUT, <<IN
196
+ foo bar {
197
+ baz: bip bam boon; }
198
+ OUT
199
+ foo bar {
200
+ baz:
201
+ bip
202
+ bam
203
+ boon; }
204
+ IN
205
+ end
206
+
207
+ def test_multiline_dynamic_properties
208
+ assert_scss_to_sass <<SASS, <<SCSS
209
+ foo bar
210
+ baz: $bip "bam" 12px
211
+ SASS
212
+ foo bar {
213
+ baz:
214
+ $bip
215
+ "bam"
216
+ 12px; }
217
+ SCSS
218
+
219
+ assert_scss_to_scss <<OUT, <<IN
220
+ foo bar {
221
+ baz: $bip "bam" 12px; }
222
+ OUT
223
+ foo bar {
224
+ baz:
225
+ $bip
226
+ "bam"
227
+ 12px; }
228
+ IN
229
+ end
230
+
231
+ def test_silent_comments
232
+ assert_renders <<SASS, <<SCSS
233
+ // foo
234
+
235
+ // bar
236
+
237
+ // baz
238
+
239
+ foo bar
240
+ a: b
241
+ SASS
242
+ // foo
243
+
244
+ // bar
245
+
246
+ // baz
247
+
248
+ foo bar {
249
+ a: b; }
250
+ SCSS
251
+
252
+ assert_renders <<SASS, <<SCSS
253
+ // foo
254
+ bar
255
+ baz
256
+ bang
257
+
258
+ foo bar
259
+ a: b
260
+ SASS
261
+ // foo
262
+ // bar
263
+ // baz
264
+ // bang
265
+
266
+ foo bar {
267
+ a: b; }
268
+ SCSS
269
+
270
+ assert_sass_to_scss <<SCSS, <<SASS
271
+ // foo
272
+ // bar
273
+ // baz
274
+ // bang
275
+
276
+ foo bar {
277
+ a: b; }
278
+ SCSS
279
+ // foo
280
+ // bar
281
+ // baz
282
+ // bang
283
+
284
+ foo bar
285
+ a: b
286
+ SASS
287
+ end
288
+
289
+ def test_loud_comments
290
+ assert_renders <<SASS, <<SCSS
291
+ /* foo
292
+
293
+ /* bar
294
+
295
+ /* baz
296
+
297
+ foo bar
298
+ a: b
299
+ SASS
300
+ /* foo */
301
+
302
+ /* bar */
303
+
304
+ /* baz */
305
+
306
+ foo bar {
307
+ a: b; }
308
+ SCSS
309
+
310
+ assert_scss_to_sass <<SASS, <<SCSS
311
+ /* foo
312
+ bar
313
+ baz
314
+ bang
315
+
316
+ foo bar
317
+ a: b
318
+ SASS
319
+ /* foo
320
+ bar
321
+ baz
322
+ bang */
323
+
324
+ foo bar {
325
+ a: b; }
326
+ SCSS
327
+
328
+ assert_scss_to_scss <<SCSS
329
+ /* foo
330
+ bar
331
+ baz
332
+ bang */
333
+
334
+ foo bar {
335
+ a: b; }
336
+ SCSS
337
+
338
+ assert_renders <<SASS, <<SCSS
339
+ /* foo
340
+ bar
341
+ baz
342
+ bang
343
+
344
+ foo bar
345
+ a: b
346
+ SASS
347
+ /* foo
348
+ * bar
349
+ * baz
350
+ * bang */
351
+
352
+ foo bar {
353
+ a: b; }
354
+ SCSS
355
+ end
356
+
357
+ def test_loud_comments_with_weird_indentation
358
+ assert_scss_to_sass <<SASS, <<SCSS
359
+ foo
360
+ /* foo
361
+ bar
362
+ baz
363
+ a: b
364
+ SASS
365
+ foo {
366
+ /* foo
367
+ bar
368
+ baz */
369
+ a: b; }
370
+ SCSS
371
+
372
+ assert_sass_to_scss <<SCSS, <<SASS
373
+ foo {
374
+ /* foo
375
+ * bar
376
+ * baz */
377
+ a: b; }
378
+ SCSS
379
+ foo
380
+ /* foo
381
+ bar
382
+ baz
383
+ a: b
384
+ SASS
385
+ end
386
+
387
+ def test_immediately_preceding_comments
388
+ assert_renders <<SASS, <<SCSS
389
+ /* Foo
390
+ Bar
391
+ Baz
392
+ .foo#bar
393
+ a: b
394
+ SASS
395
+ /* Foo
396
+ * Bar
397
+ * Baz */
398
+ .foo#bar {
399
+ a: b; }
400
+ SCSS
401
+
402
+ assert_renders <<SASS, <<SCSS
403
+ // Foo
404
+ Bar
405
+ Baz
406
+ =foo
407
+ a: b
408
+ SASS
409
+ // Foo
410
+ // Bar
411
+ // Baz
412
+ @mixin foo {
413
+ a: b; }
414
+ SCSS
415
+ end
416
+
417
+ def test_debug
418
+ assert_renders <<SASS, <<SCSS
419
+ foo
420
+ @debug 12px
421
+ bar: baz
422
+ SASS
423
+ foo {
424
+ @debug 12px;
425
+ bar: baz; }
426
+ SCSS
427
+ end
428
+
429
+ def test_directive_without_children
430
+ assert_renders <<SASS, <<SCSS
431
+ foo
432
+ @foo #bar "baz"
433
+ bar: baz
434
+ SASS
435
+ foo {
436
+ @foo #bar "baz";
437
+ bar: baz; }
438
+ SCSS
439
+ end
440
+
441
+ def test_directive_with_prop_children
442
+ assert_renders <<SASS, <<SCSS
443
+ foo
444
+ @foo #bar "baz"
445
+ a: b
446
+ c: d
447
+
448
+ bar: baz
449
+ SASS
450
+ foo {
451
+ @foo #bar "baz" {
452
+ a: b;
453
+ c: d; }
454
+
455
+ bar: baz; }
456
+ SCSS
457
+ end
458
+
459
+ def test_directive_with_rule_children
460
+ assert_renders <<SASS, <<SCSS
461
+ foo
462
+ @foo #bar "baz"
463
+ #blat
464
+ a: b
465
+ .bang
466
+ c: d
467
+ e: f
468
+
469
+ bar: baz
470
+ SASS
471
+ foo {
472
+ @foo #bar "baz" {
473
+ #blat {
474
+ a: b; }
475
+ .bang {
476
+ c: d;
477
+ e: f; } }
478
+
479
+ bar: baz; }
480
+ SCSS
481
+ end
482
+
483
+ def test_directive_with_rule_and_prop_children
484
+ assert_renders <<SASS, <<SCSS
485
+ foo
486
+ @foo #bar "baz"
487
+ g: h
488
+ #blat
489
+ a: b
490
+ .bang
491
+ c: d
492
+ e: f
493
+ i: j
494
+
495
+ bar: baz
496
+ SASS
497
+ foo {
498
+ @foo #bar "baz" {
499
+ g: h;
500
+ #blat {
501
+ a: b; }
502
+ .bang {
503
+ c: d;
504
+ e: f; }
505
+ i: j; }
506
+
507
+ bar: baz; }
508
+ SCSS
509
+ end
510
+
511
+ def test_for
512
+ assert_renders <<SASS, <<SCSS
513
+ foo
514
+ @for $a from $b to $c
515
+ a: b
516
+ @for $c from 1 to 16
517
+ d: e
518
+ f: g
519
+ SASS
520
+ foo {
521
+ @for $a from $b to $c {
522
+ a: b; }
523
+ @for $c from 1 to 16 {
524
+ d: e;
525
+ f: g; } }
526
+ SCSS
527
+ end
528
+
529
+ def test_while
530
+ assert_renders <<SASS, <<SCSS
531
+ foo
532
+ @while flaz($a + $b)
533
+ a: b
534
+ @while 1
535
+ d: e
536
+ f: g
537
+ SASS
538
+ foo {
539
+ @while flaz($a + $b) {
540
+ a: b; }
541
+ @while 1 {
542
+ d: e;
543
+ f: g; } }
544
+ SCSS
545
+ end
546
+
547
+ def test_if
548
+ assert_renders <<SASS, <<SCSS
549
+ foo
550
+ @if $foo or $bar
551
+ a: b
552
+ @if $baz
553
+ d: e
554
+ @else if $bang
555
+ f: g
556
+ @else
557
+ h: i
558
+ SASS
559
+ foo {
560
+ @if $foo or $bar {
561
+ a: b; }
562
+ @if $baz {
563
+ d: e; }
564
+ @else if $bang {
565
+ f: g; }
566
+ @else {
567
+ h: i; } }
568
+ SCSS
569
+ end
570
+
571
+ def test_import
572
+ assert_renders <<SASS, <<SCSS
573
+ @import foo
574
+
575
+ foo
576
+ bar: baz
577
+ SASS
578
+ @import "foo";
579
+
580
+ foo {
581
+ bar: baz; }
582
+ SCSS
583
+
584
+ assert_renders <<SASS, <<SCSS
585
+ @import foo.css
586
+
587
+ foo
588
+ bar: baz
589
+ SASS
590
+ @import "foo.css";
591
+
592
+ foo {
593
+ bar: baz; }
594
+ SCSS
595
+ end
596
+
597
+ def test_import_as_directive_in_sass
598
+ assert_sass_to_sass '@import "foo.css"'
599
+ assert_sass_to_sass '@import url(foo.css)'
600
+ end
601
+
602
+ def test_import_as_directive_in_scss
603
+ assert_renders <<SASS, <<SCSS
604
+ @import "foo.css" print
605
+ SASS
606
+ @import "foo.css" print;
607
+ SCSS
608
+
609
+ assert_renders <<SASS, <<SCSS
610
+ @import url(foo.css) screen, print
611
+ SASS
612
+ @import url(foo.css) screen, print;
613
+ SCSS
614
+ end
615
+
616
+ def test_adjacent_imports
617
+ assert_renders <<SASS, <<SCSS
618
+ @import foo.sass
619
+ @import bar.scss
620
+ @import baz
621
+ SASS
622
+ @import "foo.sass";
623
+ @import "bar.scss";
624
+ @import "baz";
625
+ SCSS
626
+ end
627
+
628
+ def test_non_adjacent_imports
629
+ assert_renders <<SASS, <<SCSS
630
+ @import foo.sass
631
+
632
+ @import bar.scss
633
+
634
+ @import baz
635
+ SASS
636
+ @import "foo.sass";
637
+
638
+ @import "bar.scss";
639
+
640
+ @import "baz";
641
+ SCSS
642
+ end
643
+
644
+ def test_argless_mixin_definition
645
+ assert_renders <<SASS, <<SCSS
646
+ =foo-bar
647
+ baz
648
+ a: b
649
+ SASS
650
+ @mixin foo-bar {
651
+ baz {
652
+ a: b; } }
653
+ SCSS
654
+
655
+ assert_scss_to_sass <<SASS, <<SCSS
656
+ =foo-bar
657
+ baz
658
+ a: b
659
+ SASS
660
+ @mixin foo-bar() {
661
+ baz {
662
+ a: b; } }
663
+ SCSS
664
+
665
+ assert_sass_to_scss <<SCSS, <<SASS
666
+ @mixin foo-bar {
667
+ baz {
668
+ a: b; } }
669
+ SCSS
670
+ =foo-bar()
671
+ baz
672
+ a: b
673
+ SASS
674
+ end
675
+
676
+ def test_mixin_definition_without_defaults
677
+ assert_renders <<SASS, <<SCSS
678
+ =foo-bar($baz, $bang)
679
+ baz
680
+ a: $baz $bang
681
+ SASS
682
+ @mixin foo-bar($baz, $bang) {
683
+ baz {
684
+ a: $baz $bang; } }
685
+ SCSS
686
+ end
687
+
688
+ def test_mixin_definition_with_defaults
689
+ assert_renders <<SASS, <<SCSS
690
+ =foo-bar($baz, $bang: 12px)
691
+ baz
692
+ a: $baz $bang
693
+ SASS
694
+ @mixin foo-bar($baz, $bang: 12px) {
695
+ baz {
696
+ a: $baz $bang; } }
697
+ SCSS
698
+
699
+ assert_scss_to_sass <<SASS, <<SCSS
700
+ =foo-bar($baz, $bang: foo)
701
+ baz
702
+ a: $baz $bang
703
+ SASS
704
+ @mixin foo-bar($baz, $bang = "foo") {
705
+ baz {
706
+ a: $baz $bang; } }
707
+ SCSS
708
+
709
+ assert_sass_to_scss <<SCSS, <<SASS
710
+ @mixin foo-bar($baz, $bang: foo) {
711
+ baz {
712
+ a: $baz $bang; } }
713
+ SCSS
714
+ =foo-bar($baz, $bang = "foo")
715
+ baz
716
+ a: $baz $bang
717
+ SASS
718
+ end
719
+
720
+ def test_argless_mixin_include
721
+ assert_renders <<SASS, <<SCSS
722
+ foo
723
+ +foo-bar
724
+ a: blip
725
+ SASS
726
+ foo {
727
+ @include foo-bar;
728
+ a: blip; }
729
+ SCSS
730
+ end
731
+
732
+ def test_mixin_include
733
+ assert_renders <<SASS, <<SCSS
734
+ foo
735
+ +foo-bar(12px, "blaz")
736
+ a: blip
737
+ SASS
738
+ foo {
739
+ @include foo-bar(12px, "blaz");
740
+ a: blip; }
741
+ SCSS
742
+ end
743
+
744
+ def test_variable_definition
745
+ assert_renders <<SASS, <<SCSS
746
+ $var1: 12px + 15px
747
+
748
+ foo
749
+ $var2: flaz(#abcdef)
750
+ val: $var1 $var2
751
+ SASS
752
+ $var1: 12px + 15px;
753
+
754
+ foo {
755
+ $var2: flaz(#abcdef);
756
+ val: $var1 $var2; }
757
+ SCSS
758
+
759
+ assert_sass_to_scss '$var: 12px $bar baz;', '$var = 12px $bar "baz"'
760
+ end
761
+
762
+ def test_guarded_variable_definition
763
+ assert_renders <<SASS, <<SCSS
764
+ $var1: 12px + 15px !default
765
+
766
+ foo
767
+ $var2: flaz(#abcdef) !default
768
+ val: $var1 $var2
769
+ SASS
770
+ $var1: 12px + 15px !default;
771
+
772
+ foo {
773
+ $var2: flaz(#abcdef) !default;
774
+ val: $var1 $var2; }
775
+ SCSS
776
+
777
+ assert_sass_to_scss '$var: 12px $bar baz !default;', '$var ||= 12px $bar "baz"'
778
+ end
779
+
780
+ # Sass 3 Deprecation conversions
781
+
782
+ def test_simple_quoted_strings_unquoted_with_equals
783
+ assert_sass_to_scss '$var: 1px foo + bar baz;', '!var = 1px "foo" + "bar" baz'
784
+ assert_sass_to_scss '$var: -foo-bar;', '!var = "-foo-bar"'
785
+ end
786
+
787
+ def test_complex_quoted_strings_explicitly_unquoted_with_equals
788
+ assert_sass_to_scss '$var: 1px unquote("foo + bar") baz;', '!var = 1px "foo + bar" baz'
789
+ assert_sass_to_scss "$var: unquote('foo\"bar');", '!var = "foo\"bar"'
790
+ end
791
+
792
+ def test_division_asserted_with_equals
793
+ assert_sass_to_scss <<SCSS, <<SASS
794
+ foo {
795
+ a: (1px / 2px); }
796
+ SCSS
797
+ foo
798
+ a = 1px / 2px
799
+ SASS
800
+ end
801
+
802
+ def test_division_not_asserted_with_equals_when_unnecessary
803
+ assert_sass_to_scss <<SCSS, <<SASS
804
+ $var: 1px / 2px;
805
+
806
+ foo {
807
+ a: $var; }
808
+ SCSS
809
+ !var = 1px / 2px
810
+
811
+ foo
812
+ a = !var
813
+ SASS
814
+
815
+ assert_sass_to_scss <<SCSS, <<SASS
816
+ $var: 1px;
817
+
818
+ foo {
819
+ a: $var / 2px; }
820
+ SCSS
821
+ !var = 1px
822
+
823
+ foo
824
+ a = !var / 2px
825
+ SASS
826
+
827
+ assert_sass_to_scss <<SCSS, <<SASS
828
+ foo {
829
+ a: 1 + 1px / 2px; }
830
+ SCSS
831
+ foo
832
+ a = 1 + 1px / 2px
833
+ SASS
834
+ end
835
+
836
+ private
837
+
838
+ def assert_sass_to_sass(sass, options = {})
839
+ assert_equal(sass.rstrip, to_sass(sass, options).rstrip,
840
+ "Expected Sass to transform to itself")
841
+ end
842
+
843
+ def assert_scss_to_sass(sass, scss, options = {})
844
+ assert_equal(sass.rstrip, to_sass(scss, options.merge(:syntax => :scss)).rstrip,
845
+ "Expected SCSS to transform to Sass")
846
+ end
847
+
848
+ def assert_scss_to_scss(scss, in_scss = nil, options = nil)
849
+ if in_scss.is_a?(Hash)
850
+ options = in_scss
851
+ in_scss = nil
852
+ end
853
+
854
+ in_scss ||= scss
855
+ options ||= {}
856
+
857
+ assert_equal(scss.rstrip, to_scss(in_scss, options.merge(:syntax => :scss)).rstrip,
858
+ "Expected SCSS to transform to #{scss == in_scss ? 'itself' : 'SCSS'}k")
859
+ end
860
+
861
+ def assert_sass_to_scss(scss, sass, options = {})
862
+ assert_equal(scss.rstrip, to_scss(sass, options).rstrip,
863
+ "Expected Sass to transform to SCSS")
864
+ end
865
+
866
+ def assert_renders(sass, scss, options = {})
867
+ assert_sass_to_sass(sass, options)
868
+ assert_scss_to_sass(sass, scss, options)
869
+ assert_scss_to_scss(scss, options)
870
+ assert_sass_to_scss(scss, sass, options)
871
+ end
872
+
873
+ def to_sass(scss, options = {})
874
+ Haml::Util.silence_haml_warnings do
875
+ Sass::Engine.new(scss, options).to_tree.to_sass(options)
876
+ end
877
+ end
878
+
879
+ def to_scss(sass, options = {})
880
+ Haml::Util.silence_haml_warnings do
881
+ Sass::Engine.new(sass, options).to_tree.to_scss(options)
882
+ end
883
+ end
884
+ end