sass 3.7.4 → 4.0.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (257) hide show
  1. checksums.yaml +13 -5
  2. data/.yardopts +1 -1
  3. data/CODE_OF_CONDUCT.md +1 -1
  4. data/CONTRIBUTING.md +1 -146
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +25 -39
  7. data/Rakefile +274 -0
  8. data/VERSION +1 -1
  9. data/VERSION_DATE +1 -1
  10. data/lib/sass.rb +3 -3
  11. data/lib/sass/cache_stores/filesystem.rb +2 -2
  12. data/lib/sass/cache_stores/memory.rb +5 -4
  13. data/lib/sass/callbacks.rb +2 -2
  14. data/lib/sass/css.rb +12 -12
  15. data/lib/sass/engine.rb +44 -62
  16. data/lib/sass/environment.rb +7 -35
  17. data/lib/sass/error.rb +14 -14
  18. data/lib/sass/exec/base.rb +14 -3
  19. data/lib/sass/exec/sass_convert.rb +6 -20
  20. data/lib/sass/exec/sass_scss.rb +29 -5
  21. data/lib/sass/features.rb +2 -3
  22. data/lib/sass/importers/filesystem.rb +6 -11
  23. data/lib/sass/logger.rb +3 -8
  24. data/lib/sass/logger/base.rb +2 -19
  25. data/lib/sass/plugin.rb +2 -3
  26. data/lib/sass/plugin/compiler.rb +67 -48
  27. data/lib/sass/plugin/configuration.rb +3 -3
  28. data/lib/sass/plugin/merb.rb +1 -1
  29. data/lib/sass/plugin/rack.rb +3 -3
  30. data/lib/sass/plugin/staleness_checker.rb +3 -3
  31. data/lib/sass/railtie.rb +1 -1
  32. data/lib/sass/script.rb +3 -3
  33. data/lib/sass/script/css_parser.rb +15 -5
  34. data/lib/sass/script/functions.rb +121 -337
  35. data/lib/sass/script/lexer.rb +36 -102
  36. data/lib/sass/script/parser.rb +153 -529
  37. data/lib/sass/script/tree/funcall.rb +34 -42
  38. data/lib/sass/script/tree/interpolation.rb +26 -171
  39. data/lib/sass/script/tree/list_literal.rb +8 -23
  40. data/lib/sass/script/tree/map_literal.rb +2 -2
  41. data/lib/sass/script/tree/node.rb +3 -3
  42. data/lib/sass/script/tree/operation.rb +16 -43
  43. data/lib/sass/script/tree/string_interpolation.rb +43 -64
  44. data/lib/sass/script/tree/variable.rb +1 -1
  45. data/lib/sass/script/value.rb +0 -2
  46. data/lib/sass/script/value/arg_list.rb +1 -1
  47. data/lib/sass/script/value/base.rb +9 -27
  48. data/lib/sass/script/value/color.rb +18 -26
  49. data/lib/sass/script/value/helpers.rb +18 -44
  50. data/lib/sass/script/value/list.rb +14 -35
  51. data/lib/sass/script/value/map.rb +2 -2
  52. data/lib/sass/script/value/number.rb +16 -26
  53. data/lib/sass/script/value/string.rb +1 -30
  54. data/lib/sass/scss.rb +2 -0
  55. data/lib/sass/scss/css_parser.rb +3 -7
  56. data/lib/sass/scss/parser.rb +78 -196
  57. data/lib/sass/scss/rx.rb +14 -7
  58. data/lib/sass/scss/script_lexer.rb +15 -0
  59. data/lib/sass/scss/script_parser.rb +25 -0
  60. data/lib/sass/scss/static_parser.rb +55 -38
  61. data/lib/sass/selector.rb +10 -7
  62. data/lib/sass/selector/abstract_sequence.rb +12 -15
  63. data/lib/sass/selector/comma_sequence.rb +6 -24
  64. data/lib/sass/selector/pseudo.rb +6 -19
  65. data/lib/sass/selector/sequence.rb +16 -14
  66. data/lib/sass/selector/simple.rb +7 -9
  67. data/lib/sass/selector/simple_sequence.rb +12 -16
  68. data/lib/sass/shared.rb +1 -1
  69. data/lib/sass/source/map.rb +9 -7
  70. data/lib/sass/source/position.rb +4 -4
  71. data/lib/sass/stack.rb +3 -23
  72. data/lib/sass/tree/charset_node.rb +1 -1
  73. data/lib/sass/tree/comment_node.rb +1 -1
  74. data/lib/sass/tree/function_node.rb +3 -2
  75. data/lib/sass/tree/node.rb +3 -5
  76. data/lib/sass/tree/prop_node.rb +58 -49
  77. data/lib/sass/tree/rule_node.rb +8 -15
  78. data/lib/sass/tree/visitors/check_nesting.rb +23 -19
  79. data/lib/sass/tree/visitors/convert.rb +13 -15
  80. data/lib/sass/tree/visitors/cssize.rb +15 -4
  81. data/lib/sass/tree/visitors/deep_copy.rb +2 -2
  82. data/lib/sass/tree/visitors/extend.rb +14 -10
  83. data/lib/sass/tree/visitors/perform.rb +18 -29
  84. data/lib/sass/tree/visitors/set_options.rb +2 -2
  85. data/lib/sass/tree/visitors/to_css.rb +47 -77
  86. data/lib/sass/util.rb +311 -98
  87. data/lib/sass/util/cross_platform_random.rb +19 -0
  88. data/lib/sass/util/multibyte_string_scanner.rb +133 -127
  89. data/lib/sass/util/normalized_map.rb +8 -1
  90. data/lib/sass/util/ordered_hash.rb +192 -0
  91. data/lib/sass/version.rb +6 -2
  92. data/test/sass/cache_test.rb +131 -0
  93. data/test/sass/callbacks_test.rb +61 -0
  94. data/test/sass/compiler_test.rb +236 -0
  95. data/test/sass/conversion_test.rb +2171 -0
  96. data/test/sass/css2sass_test.rb +526 -0
  97. data/test/sass/data/hsl-rgb.txt +319 -0
  98. data/test/sass/encoding_test.rb +219 -0
  99. data/test/sass/engine_test.rb +3400 -0
  100. data/test/sass/exec_test.rb +86 -0
  101. data/test/sass/extend_test.rb +1719 -0
  102. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  103. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  104. data/test/sass/functions_test.rb +1984 -0
  105. data/test/sass/importer_test.rb +421 -0
  106. data/test/sass/logger_test.rb +58 -0
  107. data/test/sass/mock_importer.rb +49 -0
  108. data/test/sass/more_results/more1.css +9 -0
  109. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  110. data/test/sass/more_results/more_import.css +29 -0
  111. data/test/sass/more_templates/_more_partial.sass +2 -0
  112. data/test/sass/more_templates/more1.sass +23 -0
  113. data/test/sass/more_templates/more_import.sass +11 -0
  114. data/test/sass/plugin_test.rb +556 -0
  115. data/test/sass/results/alt.css +4 -0
  116. data/test/sass/results/basic.css +9 -0
  117. data/test/sass/results/cached_import_option.css +3 -0
  118. data/test/sass/results/compact.css +5 -0
  119. data/test/sass/results/complex.css +86 -0
  120. data/test/sass/results/compressed.css +1 -0
  121. data/test/sass/results/expanded.css +19 -0
  122. data/test/sass/results/filename_fn.css +3 -0
  123. data/test/sass/results/if.css +3 -0
  124. data/test/sass/results/import.css +31 -0
  125. data/test/sass/results/import_charset.css +5 -0
  126. data/test/sass/results/import_charset_1_8.css +5 -0
  127. data/test/sass/results/import_charset_ibm866.css +5 -0
  128. data/test/sass/results/import_content.css +1 -0
  129. data/test/sass/results/line_numbers.css +49 -0
  130. data/test/sass/results/mixins.css +95 -0
  131. data/test/sass/results/multiline.css +24 -0
  132. data/test/sass/results/nested.css +22 -0
  133. data/test/sass/results/options.css +1 -0
  134. data/test/sass/results/parent_ref.css +13 -0
  135. data/test/sass/results/script.css +16 -0
  136. data/test/sass/results/scss_import.css +31 -0
  137. data/test/sass/results/scss_importee.css +2 -0
  138. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  139. data/test/sass/results/subdir/subdir.css +3 -0
  140. data/test/sass/results/units.css +11 -0
  141. data/test/sass/results/warn.css +0 -0
  142. data/test/sass/results/warn_imported.css +0 -0
  143. data/test/sass/script_conversion_test.rb +306 -0
  144. data/test/sass/script_test.rb +1206 -0
  145. data/test/sass/scss/css_test.rb +1281 -0
  146. data/test/sass/scss/rx_test.rb +160 -0
  147. data/test/sass/scss/scss_test.rb +4147 -0
  148. data/test/sass/scss/test_helper.rb +37 -0
  149. data/test/sass/source_map_test.rb +1055 -0
  150. data/test/sass/superselector_test.rb +210 -0
  151. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  152. data/test/sass/templates/_double_import_loop2.sass +1 -0
  153. data/test/sass/templates/_filename_fn_import.scss +11 -0
  154. data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
  155. data/test/sass/templates/_imported_charset_utf8.sass +4 -0
  156. data/test/sass/templates/_imported_content.sass +3 -0
  157. data/test/sass/templates/_partial.sass +2 -0
  158. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  159. data/test/sass/templates/alt.sass +16 -0
  160. data/test/sass/templates/basic.sass +23 -0
  161. data/test/sass/templates/bork1.sass +2 -0
  162. data/test/sass/templates/bork2.sass +2 -0
  163. data/test/sass/templates/bork3.sass +2 -0
  164. data/test/sass/templates/bork4.sass +2 -0
  165. data/test/sass/templates/bork5.sass +3 -0
  166. data/test/sass/templates/cached_import_option.scss +3 -0
  167. data/test/sass/templates/compact.sass +17 -0
  168. data/test/sass/templates/complex.sass +305 -0
  169. data/test/sass/templates/compressed.sass +15 -0
  170. data/test/sass/templates/double_import_loop1.sass +1 -0
  171. data/test/sass/templates/expanded.sass +17 -0
  172. data/test/sass/templates/filename_fn.scss +18 -0
  173. data/test/sass/templates/if.sass +11 -0
  174. data/test/sass/templates/import.sass +12 -0
  175. data/test/sass/templates/import_charset.sass +9 -0
  176. data/test/sass/templates/import_charset_1_8.sass +6 -0
  177. data/test/sass/templates/import_charset_ibm866.sass +11 -0
  178. data/test/sass/templates/import_content.sass +4 -0
  179. data/test/sass/templates/importee.less +2 -0
  180. data/test/sass/templates/importee.sass +19 -0
  181. data/test/sass/templates/line_numbers.sass +13 -0
  182. data/test/sass/templates/mixin_bork.sass +5 -0
  183. data/test/sass/templates/mixins.sass +76 -0
  184. data/test/sass/templates/multiline.sass +20 -0
  185. data/test/sass/templates/nested.sass +25 -0
  186. data/test/sass/templates/nested_bork1.sass +2 -0
  187. data/test/sass/templates/nested_bork2.sass +2 -0
  188. data/test/sass/templates/nested_bork3.sass +2 -0
  189. data/test/sass/templates/nested_bork4.sass +2 -0
  190. data/test/sass/templates/nested_import.sass +2 -0
  191. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  192. data/test/sass/templates/options.sass +2 -0
  193. data/test/sass/templates/parent_ref.sass +25 -0
  194. data/test/sass/templates/same_name_different_ext.sass +2 -0
  195. data/test/sass/templates/same_name_different_ext.scss +1 -0
  196. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  197. data/test/sass/templates/script.sass +101 -0
  198. data/test/sass/templates/scss_import.scss +12 -0
  199. data/test/sass/templates/scss_importee.scss +1 -0
  200. data/test/sass/templates/single_import_loop.sass +1 -0
  201. data/test/sass/templates/subdir/import_up1.scss +1 -0
  202. data/test/sass/templates/subdir/import_up2.scss +1 -0
  203. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  204. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  205. data/test/sass/templates/subdir/subdir.sass +6 -0
  206. data/test/sass/templates/units.sass +11 -0
  207. data/test/sass/templates/warn.sass +3 -0
  208. data/test/sass/templates/warn_imported.sass +4 -0
  209. data/test/sass/test_helper.rb +8 -0
  210. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  211. data/test/sass/util/normalized_map_test.rb +51 -0
  212. data/test/sass/util/subset_map_test.rb +91 -0
  213. data/test/sass/util_test.rb +438 -0
  214. data/test/sass/value_helpers_test.rb +179 -0
  215. data/test/test_helper.rb +110 -0
  216. data/vendor/listen/CHANGELOG.md +1 -0
  217. data/vendor/listen/CONTRIBUTING.md +38 -0
  218. data/vendor/listen/Gemfile +20 -0
  219. data/vendor/listen/Guardfile +8 -0
  220. data/vendor/listen/LICENSE +20 -0
  221. data/vendor/listen/README.md +349 -0
  222. data/vendor/listen/Rakefile +5 -0
  223. data/vendor/listen/Vagrantfile +96 -0
  224. data/vendor/listen/lib/listen.rb +54 -0
  225. data/vendor/listen/lib/listen/adapter.rb +327 -0
  226. data/vendor/listen/lib/listen/adapters/bsd.rb +75 -0
  227. data/vendor/listen/lib/listen/adapters/darwin.rb +48 -0
  228. data/vendor/listen/lib/listen/adapters/linux.rb +81 -0
  229. data/vendor/listen/lib/listen/adapters/polling.rb +58 -0
  230. data/vendor/listen/lib/listen/adapters/windows.rb +91 -0
  231. data/vendor/listen/lib/listen/directory_record.rb +406 -0
  232. data/vendor/listen/lib/listen/listener.rb +323 -0
  233. data/vendor/listen/lib/listen/turnstile.rb +32 -0
  234. data/vendor/listen/lib/listen/version.rb +3 -0
  235. data/vendor/listen/listen.gemspec +28 -0
  236. data/vendor/listen/spec/listen/adapter_spec.rb +149 -0
  237. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +36 -0
  238. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +37 -0
  239. data/vendor/listen/spec/listen/adapters/linux_spec.rb +47 -0
  240. data/vendor/listen/spec/listen/adapters/polling_spec.rb +68 -0
  241. data/vendor/listen/spec/listen/adapters/windows_spec.rb +30 -0
  242. data/vendor/listen/spec/listen/directory_record_spec.rb +1250 -0
  243. data/vendor/listen/spec/listen/listener_spec.rb +258 -0
  244. data/vendor/listen/spec/listen/turnstile_spec.rb +56 -0
  245. data/vendor/listen/spec/listen_spec.rb +67 -0
  246. data/vendor/listen/spec/spec_helper.rb +25 -0
  247. data/vendor/listen/spec/support/adapter_helper.rb +666 -0
  248. data/vendor/listen/spec/support/directory_record_helper.rb +57 -0
  249. data/vendor/listen/spec/support/fixtures_helper.rb +29 -0
  250. data/vendor/listen/spec/support/listeners_helper.rb +179 -0
  251. data/vendor/listen/spec/support/platform_helper.rb +15 -0
  252. metadata +217 -76
  253. data/extra/sass-spec-ref.sh +0 -40
  254. data/lib/sass/deprecation.rb +0 -55
  255. data/lib/sass/logger/delayed.rb +0 -50
  256. data/lib/sass/script/value/callable.rb +0 -25
  257. data/lib/sass/script/value/function.rb +0 -19
@@ -0,0 +1,160 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ require File.dirname(__FILE__) + '/../../test_helper'
4
+ require 'sass/engine'
5
+
6
+ class ScssRxTest < MiniTest::Test
7
+ include Sass::SCSS::RX
8
+
9
+ def test_identifiers
10
+ assert_match IDENT, "foo"
11
+ assert_match IDENT, "\xC3\xBFoo" # Initial char can be nonascii
12
+ assert_match IDENT, "\\123abcoo" # Initial char can be unicode escape
13
+ assert_match IDENT, "\\f oo" # Unicode escapes can be followed by whitespace
14
+ assert_match IDENT, "\\fa\too"
15
+ assert_match IDENT, "\\ff2\roo"
16
+ assert_match IDENT, "\\f13a\foo"
17
+ assert_match IDENT, "\\f13abcoo"
18
+ assert_match IDENT, "\\ oo" # Initial char can be a plain escape as well
19
+ assert_match IDENT, "\\~oo"
20
+ assert_match IDENT, "\\\\oo"
21
+ assert_match IDENT, "\\{oo"
22
+ assert_match IDENT, "\\\xC3\xBFoo"
23
+ assert_match IDENT, "-foo" # Can put a - before anything
24
+ assert_match IDENT, "-\xC3\xBFoo"
25
+ assert_match IDENT, "-\\f oo"
26
+ assert_match IDENT, "_foo" # Can put a _ before anything
27
+ assert_match IDENT, "_\xC3\xBFoo"
28
+ assert_match IDENT, "_\\f oo"
29
+ assert_match IDENT, "--foo" # "Custom" identifier
30
+
31
+ assert_match IDENT, "foo-bar"
32
+ assert_match IDENT, "f012-23"
33
+ assert_match IDENT, "foo_-_bar"
34
+ assert_match IDENT, "f012_23"
35
+
36
+ # http://www.w3.org/Style/CSS/Test/CSS2.1/current/xhtml1/escapes-003.xht
37
+ assert_match IDENT, "c\\lass"
38
+ # http://www.w3.org/Style/CSS/Test/CSS2.1/current/xhtml1/escapes-004.xht
39
+ assert_match IDENT, "c\\00006Cas\\000073"
40
+ # http://www.w3.org/Style/CSS/Test/CSS2.1/current/xhtml1/ident-001.xht
41
+ assert_match IDENT, "IdE6n-3t0_6"
42
+ # http://www.w3.org/Style/CSS/Test/CSS2.1/current/xhtml1/ident-006.xht
43
+ assert_match IDENT, "\\6000ident"
44
+ # http://www.w3.org/Style/CSS/Test/CSS2.1/current/xhtml1/ident-007.xht
45
+ assert_match IDENT, "iden\\6000t\\6000"
46
+ # http://www.w3.org/Style/CSS/Test/CSS2.1/current/xhtml1/ident-013.xht
47
+ assert_match IDENT, "\\-ident"
48
+ end
49
+
50
+ def test_underscores_in_identifiers
51
+ assert_match IDENT, "foo_bar"
52
+ assert_match IDENT, "_\xC3\xBFfoo"
53
+ assert_match IDENT, "__foo"
54
+ assert_match IDENT, "_1foo"
55
+ assert_match IDENT, "-_foo"
56
+ assert_match IDENT, "_-foo"
57
+ end
58
+
59
+ def test_invalid_identifiers
60
+ assert_no_match IDENT, ""
61
+ assert_no_match IDENT, "1foo"
62
+ assert_no_match IDENT, "-1foo"
63
+ assert_no_match IDENT, "foo bar"
64
+ assert_no_match IDENT, "foo~bar"
65
+
66
+ # http://www.w3.org/Style/CSS/Test/CSS2.1/current/xhtml1/escapes-008.xht
67
+ assert_no_match IDENT, "c\\06C ass"
68
+ assert_no_match IDENT, "back\\67\n round"
69
+ end
70
+
71
+ def test_double_quote_strings
72
+ assert_match STRING, '"foo bar"'
73
+ assert_match STRING, '"foo\\\nbar"'
74
+ assert_match STRING, "\"\\\"\""
75
+ assert_match STRING, '"\t !#$%&(-~()*+,-./0123456789~"'
76
+ end
77
+
78
+ def test_single_quote_strings
79
+ assert_match STRING, "'foo bar'"
80
+ assert_match STRING, "'foo\\\nbar'"
81
+ assert_match STRING, "'\\''"
82
+ assert_match STRING, "'\t !#\$%&(-~()*+,-./0123456789~'"
83
+ end
84
+
85
+ def test_invalid_strings
86
+ assert_no_match STRING, "\"foo\nbar\""
87
+ assert_no_match STRING, "\"foo\"bar\""
88
+ assert_no_match STRING, "'foo\nbar'"
89
+ assert_no_match STRING, "'foo'bar'"
90
+ end
91
+
92
+ def test_uri
93
+ assert_match URI, 'url("foo bar)")'
94
+ assert_match URI, "url('foo bar)')"
95
+ assert_match URI, 'url( "foo bar)" )'
96
+ assert_match URI, "url(#\\%&**+,-./0123456789~)"
97
+ end
98
+
99
+ def test_invalid_uri
100
+ assert_no_match URI, 'url(foo)bar)'
101
+ end
102
+
103
+ def test_unicode_range
104
+ assert_match UNICODERANGE, 'U+00-Ff'
105
+ assert_match UNICODERANGE, 'u+980-9FF'
106
+ assert_match UNICODERANGE, 'U+9aF??'
107
+ assert_match UNICODERANGE, 'U+??'
108
+ end
109
+
110
+ def test_escape_empty_ident
111
+ assert_equal "", Sass::SCSS::RX.escape_ident("")
112
+ end
113
+
114
+ def test_escape_just_prefix_ident
115
+ assert_equal "\\-", Sass::SCSS::RX.escape_ident("-")
116
+ assert_equal "\\_", Sass::SCSS::RX.escape_ident("_")
117
+ end
118
+
119
+ def test_escape_plain_ident
120
+ assert_equal "foo", Sass::SCSS::RX.escape_ident("foo")
121
+ assert_equal "foo-1bar", Sass::SCSS::RX.escape_ident("foo-1bar")
122
+ assert_equal "-foo-bar", Sass::SCSS::RX.escape_ident("-foo-bar")
123
+ assert_equal "f2oo_bar", Sass::SCSS::RX.escape_ident("f2oo_bar")
124
+ assert_equal "_foo_bar", Sass::SCSS::RX.escape_ident("_foo_bar")
125
+ end
126
+
127
+ def test_escape_initial_funky_ident
128
+ assert_equal "\\000035foo", Sass::SCSS::RX.escape_ident("5foo")
129
+ assert_equal "-\\000035foo", Sass::SCSS::RX.escape_ident("-5foo")
130
+ assert_equal "_\\000035foo", Sass::SCSS::RX.escape_ident("_5foo")
131
+
132
+ assert_equal "\\&foo", Sass::SCSS::RX.escape_ident("&foo")
133
+ assert_equal "-\\&foo", Sass::SCSS::RX.escape_ident("-&foo")
134
+
135
+ assert_equal "-\\ foo", Sass::SCSS::RX.escape_ident("- foo")
136
+ end
137
+
138
+ def test_escape_mid_funky_ident
139
+ assert_equal "foo\\&bar", Sass::SCSS::RX.escape_ident("foo&bar")
140
+ assert_equal "foo\\ \\ bar", Sass::SCSS::RX.escape_ident("foo bar")
141
+ assert_equal "foo\\00007fbar", Sass::SCSS::RX.escape_ident("foo\177bar")
142
+ end
143
+
144
+ def test_no_static_hyphenated_units
145
+ assert_no_match STATIC_VALUE, "20px-20px"
146
+ end
147
+
148
+ private
149
+
150
+ def assert_match(rx, str)
151
+ refute_nil(match = rx.match(str))
152
+ assert_equal str.size, match[0].size
153
+ end
154
+
155
+ def assert_no_match(rx, str)
156
+ match = rx.match(str)
157
+ refute_equal str.size, match && match[0].size
158
+ end
159
+
160
+ end
@@ -0,0 +1,4147 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ require File.dirname(__FILE__) + '/test_helper'
4
+
5
+ class ScssTest < MiniTest::Test
6
+ include ScssTestHelper
7
+
8
+ ## One-Line Comments
9
+
10
+ def test_one_line_comments
11
+ assert_equal <<CSS, render(<<SCSS)
12
+ .foo {
13
+ baz: bang; }
14
+ CSS
15
+ .foo {// bar: baz;}
16
+ baz: bang; //}
17
+ }
18
+ SCSS
19
+ assert_equal <<CSS, render(<<SCSS)
20
+ .foo bar[val="//"] {
21
+ baz: bang; }
22
+ CSS
23
+ .foo bar[val="//"] {
24
+ baz: bang; //}
25
+ }
26
+ SCSS
27
+ end
28
+
29
+ ## Script
30
+
31
+ def test_variables
32
+ assert_equal <<CSS, render(<<SCSS)
33
+ blat {
34
+ a: foo; }
35
+ CSS
36
+ $var: foo;
37
+
38
+ blat {a: $var}
39
+ SCSS
40
+
41
+ assert_equal <<CSS, render(<<SCSS)
42
+ foo {
43
+ a: 2;
44
+ b: 6; }
45
+ CSS
46
+ foo {
47
+ $var: 2;
48
+ $another-var: 4;
49
+ a: $var;
50
+ b: $var + $another-var;}
51
+ SCSS
52
+ end
53
+
54
+ def test_unicode_variables
55
+ assert_equal <<CSS, render(<<SCSS)
56
+ blat {
57
+ a: foo; }
58
+ CSS
59
+ $vär: foo;
60
+
61
+ blat {a: $vär}
62
+ SCSS
63
+ end
64
+
65
+ def test_guard_assign
66
+ assert_equal <<CSS, render(<<SCSS)
67
+ foo {
68
+ a: 1; }
69
+ CSS
70
+ $var: 1;
71
+ $var: 2 !default;
72
+
73
+ foo {a: $var}
74
+ SCSS
75
+
76
+ assert_equal <<CSS, render(<<SCSS)
77
+ foo {
78
+ a: 2; }
79
+ CSS
80
+ $var: 2 !default;
81
+
82
+ foo {a: $var}
83
+ SCSS
84
+ end
85
+
86
+ def test_sass_script
87
+ assert_equal <<CSS, render(<<SCSS)
88
+ foo {
89
+ a: 3;
90
+ b: -1;
91
+ c: foobar;
92
+ d: 12px; }
93
+ CSS
94
+ foo {
95
+ a: 1 + 2;
96
+ b: 1 - 2;
97
+ c: foo + bar;
98
+ d: floor(12.3px); }
99
+ SCSS
100
+ end
101
+
102
+ def test_debug_directive
103
+ assert_warning "test_debug_directive_inline.scss:2 DEBUG: hello world!" do
104
+ assert_equal <<CSS, render(<<SCSS)
105
+ foo {
106
+ a: b; }
107
+
108
+ bar {
109
+ c: d; }
110
+ CSS
111
+ foo {a: b}
112
+ @debug "hello world!";
113
+ bar {c: d}
114
+ SCSS
115
+ end
116
+ end
117
+
118
+ def test_error_directive
119
+ assert_raise_message(Sass::SyntaxError, "hello world!") {render(<<SCSS)}
120
+ foo {a: b}
121
+ @error "hello world!";
122
+ bar {c: d}
123
+ SCSS
124
+ end
125
+
126
+ def test_warn_directive
127
+ expected_warning = <<EXPECTATION
128
+ WARNING: this is a warning
129
+ on line 2 of test_warn_directive_inline.scss
130
+
131
+ WARNING: this is a mixin
132
+ on line 1 of test_warn_directive_inline.scss, in `foo'
133
+ from line 3 of test_warn_directive_inline.scss
134
+ EXPECTATION
135
+ assert_warning expected_warning do
136
+ assert_equal <<CSS, render(<<SCSS)
137
+ bar {
138
+ c: d; }
139
+ CSS
140
+ @mixin foo { @warn "this is a mixin";}
141
+ @warn "this is a warning";
142
+ bar {c: d; @include foo;}
143
+ SCSS
144
+ end
145
+ end
146
+
147
+ def test_for_directive
148
+ assert_equal <<CSS, render(<<SCSS)
149
+ .foo {
150
+ a: 1;
151
+ a: 2;
152
+ a: 3;
153
+ a: 4; }
154
+ CSS
155
+ .foo {
156
+ @for $var from 1 to 5 {a: $var;}
157
+ }
158
+ SCSS
159
+
160
+ assert_equal <<CSS, render(<<SCSS)
161
+ .foo {
162
+ a: 1;
163
+ a: 2;
164
+ a: 3;
165
+ a: 4;
166
+ a: 5; }
167
+ CSS
168
+ .foo {
169
+ @for $var from 1 through 5 {a: $var;}
170
+ }
171
+ SCSS
172
+ end
173
+
174
+ def test_for_directive_with_same_start_and_end
175
+ assert_equal <<CSS, render(<<SCSS)
176
+ CSS
177
+ .foo {
178
+ @for $var from 1 to 1 {a: $var;}
179
+ }
180
+ SCSS
181
+
182
+ assert_equal <<CSS, render(<<SCSS)
183
+ .foo {
184
+ a: 1; }
185
+ CSS
186
+ .foo {
187
+ @for $var from 1 through 1 {a: $var;}
188
+ }
189
+ SCSS
190
+ end
191
+
192
+ def test_decrementing_estfor_directive
193
+ assert_equal <<CSS, render(<<SCSS)
194
+ .foo {
195
+ a: 5;
196
+ a: 4;
197
+ a: 3;
198
+ a: 2;
199
+ a: 1; }
200
+ CSS
201
+ .foo {
202
+ @for $var from 5 through 1 {a: $var;}
203
+ }
204
+ SCSS
205
+
206
+ assert_equal <<CSS, render(<<SCSS)
207
+ .foo {
208
+ a: 5;
209
+ a: 4;
210
+ a: 3;
211
+ a: 2; }
212
+ CSS
213
+ .foo {
214
+ @for $var from 5 to 1 {a: $var;}
215
+ }
216
+ SCSS
217
+ end
218
+
219
+ def test_if_directive
220
+ assert_equal <<CSS, render(<<SCSS)
221
+ foo {
222
+ a: b; }
223
+ CSS
224
+ @if "foo" == "foo" {foo {a: b}}
225
+ @if "foo" != "foo" {bar {a: b}}
226
+ SCSS
227
+
228
+ assert_equal <<CSS, render(<<SCSS)
229
+ bar {
230
+ a: b; }
231
+ CSS
232
+ @if "foo" != "foo" {foo {a: b}}
233
+ @else if "foo" == "foo" {bar {a: b}}
234
+ @else if true {baz {a: b}}
235
+ SCSS
236
+
237
+ assert_equal <<CSS, render(<<SCSS)
238
+ bar {
239
+ a: b; }
240
+ CSS
241
+ @if "foo" != "foo" {foo {a: b}}
242
+ @else {bar {a: b}}
243
+ SCSS
244
+ end
245
+
246
+ def test_comment_after_if_directive
247
+ assert_equal <<CSS, render(<<SCSS)
248
+ foo {
249
+ a: b;
250
+ /* This is a comment */
251
+ c: d; }
252
+ CSS
253
+ foo {
254
+ @if true {a: b}
255
+ /* This is a comment */
256
+ c: d }
257
+ SCSS
258
+ assert_equal <<CSS, render(<<SCSS)
259
+ foo {
260
+ a: b;
261
+ /* This is a comment */
262
+ c: d; }
263
+ CSS
264
+ foo {
265
+ @if true {a: b}
266
+ @else {x: y}
267
+ /* This is a comment */
268
+ c: d }
269
+ SCSS
270
+ end
271
+
272
+ def test_while_directive
273
+ assert_equal <<CSS, render(<<SCSS)
274
+ .foo {
275
+ a: 1;
276
+ a: 2;
277
+ a: 3;
278
+ a: 4; }
279
+ CSS
280
+ $i: 1;
281
+
282
+ .foo {
283
+ @while $i != 5 {
284
+ a: $i;
285
+ $i: $i + 1 !global;
286
+ }
287
+ }
288
+ SCSS
289
+ end
290
+
291
+ def test_each_directive
292
+ assert_equal <<CSS, render(<<SCSS)
293
+ a {
294
+ b: 1px;
295
+ b: 2px;
296
+ b: 3px;
297
+ b: 4px; }
298
+
299
+ c {
300
+ d: foo;
301
+ d: bar;
302
+ d: baz;
303
+ d: bang; }
304
+ CSS
305
+ a {
306
+ @each $number in 1px 2px 3px 4px {
307
+ b: $number;
308
+ }
309
+ }
310
+ c {
311
+ @each $str in foo, bar, baz, bang {
312
+ d: $str;
313
+ }
314
+ }
315
+ SCSS
316
+ end
317
+
318
+ def test_destructuring_each_directive
319
+ assert_equal <<CSS, render(<<SCSS)
320
+ a {
321
+ foo: 1px;
322
+ bar: 2px;
323
+ baz: 3px; }
324
+
325
+ c {
326
+ foo: "Value is bar";
327
+ bar: "Value is baz";
328
+ bang: "Value is "; }
329
+ CSS
330
+ a {
331
+ @each $name, $number in (foo: 1px, bar: 2px, baz: 3px) {
332
+ \#{$name}: $number;
333
+ }
334
+ }
335
+ c {
336
+ @each $key, $value in (foo bar) (bar, baz) bang {
337
+ \#{$key}: "Value is \#{$value}";
338
+ }
339
+ }
340
+ SCSS
341
+ end
342
+
343
+ def test_css_import_directive
344
+ assert_equal "@import url(foo.css);\n", render('@import "foo.css";')
345
+ assert_equal "@import url(foo.css);\n", render("@import 'foo.css';")
346
+ assert_equal "@import url(\"foo.css\");\n", render('@import url("foo.css");')
347
+ assert_equal "@import url(\"foo.css\");\n", render('@import url("foo.css");')
348
+ assert_equal "@import url(foo.css);\n", render('@import url(foo.css);')
349
+ end
350
+
351
+ def test_css_string_import_directive_with_media
352
+ assert_parses '@import "foo.css" screen;'
353
+ assert_parses '@import "foo.css" screen, print;'
354
+ assert_parses '@import "foo.css" screen, print and (foo: 0);'
355
+ assert_parses '@import "foo.css" screen, only print, screen and (foo: 0);'
356
+ end
357
+
358
+ def test_css_url_import_directive_with_media
359
+ assert_parses '@import url("foo.css") screen;'
360
+ assert_parses '@import url("foo.css") screen, print;'
361
+ assert_parses '@import url("foo.css") screen, print and (foo: 0);'
362
+ assert_parses '@import url("foo.css") screen, only print, screen and (foo: 0);'
363
+ end
364
+
365
+ def test_media_import
366
+ assert_equal("@import \"./fonts.sass\" all;\n", render("@import \"./fonts.sass\" all;"))
367
+ end
368
+
369
+ def test_dynamic_media_import
370
+ assert_equal(<<CSS, render(<<SCSS))
371
+ @import "foo" print and (-webkit-min-device-pixel-ratio-foo: 25);
372
+ CSS
373
+ $media: print;
374
+ $key: -webkit-min-device-pixel-ratio;
375
+ $value: 20;
376
+ @import "foo" \#{$media} and ($key + "-foo": $value + 5);
377
+ SCSS
378
+ end
379
+
380
+ def test_http_import
381
+ assert_equal("@import \"http://fonts.googleapis.com/css?family=Droid+Sans\";\n",
382
+ render("@import \"http://fonts.googleapis.com/css?family=Droid+Sans\";"))
383
+ end
384
+
385
+ def test_protocol_relative_import
386
+ assert_equal("@import \"//fonts.googleapis.com/css?family=Droid+Sans\";\n",
387
+ render("@import \"//fonts.googleapis.com/css?family=Droid+Sans\";"))
388
+ end
389
+
390
+ def test_import_with_interpolation
391
+ assert_equal <<CSS, render(<<SCSS)
392
+ @import url("http://fonts.googleapis.com/css?family=Droid+Sans");
393
+ CSS
394
+ $family: unquote("Droid+Sans");
395
+ @import url("http://fonts.googleapis.com/css?family=\#{$family}");
396
+ SCSS
397
+ end
398
+
399
+ def test_url_import
400
+ assert_equal("@import url(fonts.sass);\n", render("@import url(fonts.sass);"))
401
+ end
402
+
403
+ def test_css_import_doesnt_move_through_comments
404
+ assert_equal <<CSS, render(<<SCSS)
405
+ /* Comment 1 */
406
+ @import url("foo.css");
407
+ /* Comment 2 */
408
+ @import url("bar.css");
409
+ CSS
410
+ /* Comment 1 */
411
+ @import url("foo.css");
412
+
413
+ /* Comment 2 */
414
+ @import url("bar.css");
415
+ SCSS
416
+ end
417
+
418
+ def test_css_import_movement_stops_at_comments
419
+ assert_equal <<CSS, render(<<SCSS)
420
+ /* Comment 1 */
421
+ @import url("foo.css");
422
+ /* Comment 2 */
423
+ @import url("bar.css");
424
+ .foo {
425
+ a: b; }
426
+
427
+ /* Comment 3 */
428
+ CSS
429
+ /* Comment 1 */
430
+ @import url("foo.css");
431
+
432
+ /* Comment 2 */
433
+
434
+ .foo {a: b}
435
+
436
+ /* Comment 3 */
437
+ @import url("bar.css");
438
+ SCSS
439
+ end
440
+
441
+ def test_block_comment_in_script
442
+ assert_equal <<CSS, render(<<SCSS)
443
+ foo {
444
+ a: 1bar; }
445
+ CSS
446
+ foo {a: 1 + /* flang */ bar}
447
+ SCSS
448
+ end
449
+
450
+ def test_line_comment_in_script
451
+ assert_equal <<CSS, render(<<SCSS)
452
+ foo {
453
+ a: 1blang; }
454
+ CSS
455
+ foo {a: 1 + // flang }
456
+ blang }
457
+ SCSS
458
+ end
459
+
460
+ def test_static_hyphenated_unit
461
+ assert_equal <<CSS, render(<<SCSS)
462
+ foo {
463
+ a: 0px; }
464
+ CSS
465
+ foo {a: 10px-10px }
466
+ SCSS
467
+ end
468
+
469
+ ## Nested Rules
470
+
471
+ def test_nested_rules
472
+ assert_equal <<CSS, render(<<SCSS)
473
+ foo bar {
474
+ a: b; }
475
+ CSS
476
+ foo {bar {a: b}}
477
+ SCSS
478
+ assert_equal <<CSS, render(<<SCSS)
479
+ foo bar {
480
+ a: b; }
481
+ foo baz {
482
+ b: c; }
483
+ CSS
484
+ foo {
485
+ bar {a: b}
486
+ baz {b: c}}
487
+ SCSS
488
+ assert_equal <<CSS, render(<<SCSS)
489
+ foo bar baz {
490
+ a: b; }
491
+ foo bang bip {
492
+ a: b; }
493
+ CSS
494
+ foo {
495
+ bar {baz {a: b}}
496
+ bang {bip {a: b}}}
497
+ SCSS
498
+ end
499
+
500
+ def test_nested_rules_with_declarations
501
+ assert_equal <<CSS, render(<<SCSS)
502
+ foo {
503
+ a: b; }
504
+ foo bar {
505
+ c: d; }
506
+ CSS
507
+ foo {
508
+ a: b;
509
+ bar {c: d}}
510
+ SCSS
511
+ assert_equal <<CSS, render(<<SCSS)
512
+ foo {
513
+ a: b; }
514
+ foo bar {
515
+ c: d; }
516
+ CSS
517
+ foo {
518
+ bar {c: d}
519
+ a: b}
520
+ SCSS
521
+ assert_equal <<CSS, render(<<SCSS)
522
+ foo {
523
+ ump: nump;
524
+ grump: clump; }
525
+ foo bar {
526
+ blat: bang;
527
+ habit: rabbit; }
528
+ foo bar baz {
529
+ a: b; }
530
+ foo bar bip {
531
+ c: d; }
532
+ foo bibble bap {
533
+ e: f; }
534
+ CSS
535
+ foo {
536
+ ump: nump;
537
+ grump: clump;
538
+ bar {
539
+ blat: bang;
540
+ habit: rabbit;
541
+ baz {a: b}
542
+ bip {c: d}}
543
+ bibble {
544
+ bap {e: f}}}
545
+ SCSS
546
+ end
547
+
548
+ def test_nested_rules_with_fancy_selectors
549
+ assert_equal <<CSS, render(<<SCSS)
550
+ foo .bar {
551
+ a: b; }
552
+ foo :baz {
553
+ c: d; }
554
+ foo bang:bop {
555
+ e: f; }
556
+ foo ::qux {
557
+ g: h; }
558
+ foo zap::fblthp {
559
+ i: j; }
560
+ CSS
561
+ foo {
562
+ .bar {a: b}
563
+ :baz {c: d}
564
+ bang:bop {e: f}
565
+ ::qux {g: h}
566
+ zap::fblthp {i: j}}
567
+ SCSS
568
+ end
569
+
570
+ def test_almost_ambiguous_nested_rules_and_declarations
571
+ assert_equal <<CSS, render(<<SCSS)
572
+ foo {
573
+ bar: baz bang bop biddle woo look at all these elems; }
574
+ foo bar:baz:bang:bop:biddle:woo:look:at:all:these:pseudoclasses {
575
+ a: b; }
576
+ foo bar:baz bang bop biddle woo look at all these elems {
577
+ a: b; }
578
+ CSS
579
+ foo {
580
+ bar:baz:bang:bop:biddle:woo:look:at:all:these:pseudoclasses {a: b};
581
+ bar:baz bang bop biddle woo look at all these elems {a: b};
582
+ bar:baz bang bop biddle woo look at all these elems; }
583
+ SCSS
584
+ end
585
+
586
+ def test_newlines_in_selectors
587
+ assert_equal <<CSS, render(<<SCSS)
588
+ foo
589
+ bar {
590
+ a: b; }
591
+ CSS
592
+ foo
593
+ bar {a: b}
594
+ SCSS
595
+
596
+ assert_equal <<CSS, render(<<SCSS)
597
+ foo baz,
598
+ foo bang,
599
+ bar baz,
600
+ bar bang {
601
+ a: b; }
602
+ CSS
603
+ foo,
604
+ bar {
605
+ baz,
606
+ bang {a: b}}
607
+ SCSS
608
+
609
+ assert_equal <<CSS, render(<<SCSS)
610
+ foo
611
+ bar baz
612
+ bang {
613
+ a: b; }
614
+ foo
615
+ bar bip bop {
616
+ c: d; }
617
+ CSS
618
+ foo
619
+ bar {
620
+ baz
621
+ bang {a: b}
622
+
623
+ bip bop {c: d}}
624
+ SCSS
625
+
626
+ assert_equal <<CSS, render(<<SCSS)
627
+ foo bang, foo bip
628
+ bop, bar
629
+ baz bang, bar
630
+ baz bip
631
+ bop {
632
+ a: b; }
633
+ CSS
634
+ foo, bar
635
+ baz {
636
+ bang, bip
637
+ bop {a: b}}
638
+ SCSS
639
+ end
640
+
641
+ def test_trailing_comma_in_selector
642
+ assert_equal <<CSS, render(<<SCSS)
643
+ #foo #bar,
644
+ #baz #boom {
645
+ a: b; }
646
+
647
+ #bip #bop {
648
+ c: d; }
649
+ CSS
650
+ #foo #bar,,
651
+ ,#baz #boom, {a: b}
652
+
653
+ #bip #bop, ,, {c: d}
654
+ SCSS
655
+ end
656
+
657
+ def test_parent_selectors
658
+ assert_equal <<CSS, render(<<SCSS)
659
+ foo:hover {
660
+ a: b; }
661
+ bar foo.baz {
662
+ c: d; }
663
+ CSS
664
+ foo {
665
+ &:hover {a: b}
666
+ bar &.baz {c: d}}
667
+ SCSS
668
+ end
669
+
670
+ def test_parent_selector_with_subject
671
+ silence_warnings {assert_equal <<CSS, render(<<SCSS)}
672
+ bar foo.baz! .bip {
673
+ a: b; }
674
+
675
+ bar foo bar.baz! .bip {
676
+ c: d; }
677
+ CSS
678
+ foo {
679
+ bar &.baz! .bip {a: b}}
680
+
681
+ foo bar {
682
+ bar &.baz! .bip {c: d}}
683
+ SCSS
684
+ end
685
+
686
+ def test_parent_selector_with_suffix
687
+ assert_equal <<CSS, render(<<SCSS)
688
+ .foo-bar {
689
+ a: b; }
690
+ .foo_bar {
691
+ c: d; }
692
+ .foobar {
693
+ e: f; }
694
+ .foo123 {
695
+ e: f; }
696
+
697
+ :hover-suffix {
698
+ g: h; }
699
+ CSS
700
+ .foo {
701
+ &-bar {a: b}
702
+ &_bar {c: d}
703
+ &bar {e: f}
704
+ &123 {e: f}
705
+ }
706
+
707
+ :hover {
708
+ &-suffix {g: h}
709
+ }
710
+ SCSS
711
+ end
712
+
713
+ def test_unknown_directive_bubbling
714
+ assert_equal(<<CSS, render(<<SCSS, :style => :nested))
715
+ @fblthp {
716
+ .foo .bar {
717
+ a: b; } }
718
+ CSS
719
+ .foo {
720
+ @fblthp {
721
+ .bar {a: b}
722
+ }
723
+ }
724
+ SCSS
725
+ end
726
+
727
+ def test_keyframe_bubbling
728
+ assert_equal(<<CSS, render(<<SCSS, :style => :nested))
729
+ @keyframes spin {
730
+ 0% {
731
+ transform: rotate(0deg); } }
732
+ @-webkit-keyframes spin {
733
+ 0% {
734
+ transform: rotate(0deg); } }
735
+ CSS
736
+ .foo {
737
+ @keyframes spin {
738
+ 0% {transform: rotate(0deg)}
739
+ }
740
+ @-webkit-keyframes spin {
741
+ 0% {transform: rotate(0deg)}
742
+ }
743
+ }
744
+ SCSS
745
+ end
746
+
747
+ ## Namespace Properties
748
+
749
+ def test_namespace_properties
750
+ assert_equal <<CSS, render(<<SCSS)
751
+ foo {
752
+ bar: baz;
753
+ bang-bip: 1px;
754
+ bang-bop: bar; }
755
+ CSS
756
+ foo {
757
+ bar: baz;
758
+ bang: {
759
+ bip: 1px;
760
+ bop: bar;}}
761
+ SCSS
762
+ end
763
+
764
+ def test_several_namespace_properties
765
+ assert_equal <<CSS, render(<<SCSS)
766
+ foo {
767
+ bar: baz;
768
+ bang-bip: 1px;
769
+ bang-bop: bar;
770
+ buzz-fram: "foo";
771
+ buzz-frum: moo; }
772
+ CSS
773
+ foo {
774
+ bar: baz;
775
+ bang: {
776
+ bip: 1px;
777
+ bop: bar;}
778
+ buzz: {
779
+ fram: "foo";
780
+ frum: moo;
781
+ }
782
+ }
783
+ SCSS
784
+ end
785
+
786
+ def test_nested_namespace_properties
787
+ assert_equal <<CSS, render(<<SCSS)
788
+ foo {
789
+ bar: baz;
790
+ bang-bip: 1px;
791
+ bang-bop: bar;
792
+ bang-blat-baf: bort; }
793
+ CSS
794
+ foo {
795
+ bar: baz;
796
+ bang: {
797
+ bip: 1px;
798
+ bop: bar;
799
+ blat:{baf:bort}}}
800
+ SCSS
801
+ end
802
+
803
+ def test_namespace_properties_with_value
804
+ assert_equal <<CSS, render(<<SCSS)
805
+ foo {
806
+ bar: baz;
807
+ bar-bip: bop;
808
+ bar-bing: bop; }
809
+ CSS
810
+ foo {
811
+ bar: baz {
812
+ bip: bop;
813
+ bing: bop; }}
814
+ SCSS
815
+ end
816
+
817
+ def test_namespace_properties_with_script_value
818
+ assert_equal <<CSS, render(<<SCSS)
819
+ foo {
820
+ bar: bazbang;
821
+ bar-bip: bop;
822
+ bar-bing: bop; }
823
+ CSS
824
+ foo {
825
+ bar: baz + bang {
826
+ bip: bop;
827
+ bing: bop; }}
828
+ SCSS
829
+ end
830
+
831
+ def test_no_namespace_properties_without_space
832
+ assert_equal <<CSS, render(<<SCSS)
833
+ foo bar:baz {
834
+ bip: bop; }
835
+ CSS
836
+ foo {
837
+ bar:baz {
838
+ bip: bop }}
839
+ SCSS
840
+ end
841
+
842
+ def test_no_namespace_properties_without_space_even_when_its_unambiguous
843
+ render(<<SCSS)
844
+ foo {
845
+ bar:baz calc(1 + 2) {
846
+ bip: bop }}
847
+ SCSS
848
+ assert(false, "Expected syntax error")
849
+ rescue Sass::SyntaxError => e
850
+ assert_equal 'Invalid CSS after "bar:baz calc": expected selector, was "(1 + 2)"', e.message
851
+ assert_equal 2, e.sass_line
852
+ end
853
+
854
+ def test_namespace_properties_without_space_allowed_for_non_identifier
855
+ assert_equal <<CSS, render(<<SCSS)
856
+ foo {
857
+ bar: 1px;
858
+ bar-bip: bop; }
859
+ CSS
860
+ foo {
861
+ bar:1px {
862
+ bip: bop }}
863
+ SCSS
864
+ end
865
+
866
+ ## Mixins
867
+
868
+ def test_basic_mixins
869
+ assert_equal <<CSS, render(<<SCSS)
870
+ .foo {
871
+ a: b; }
872
+ CSS
873
+ @mixin foo {
874
+ .foo {a: b}}
875
+
876
+ @include foo;
877
+ SCSS
878
+
879
+ assert_equal <<CSS, render(<<SCSS)
880
+ bar {
881
+ c: d; }
882
+ bar .foo {
883
+ a: b; }
884
+ CSS
885
+ @mixin foo {
886
+ .foo {a: b}}
887
+
888
+ bar {
889
+ @include foo;
890
+ c: d; }
891
+ SCSS
892
+
893
+ assert_equal <<CSS, render(<<SCSS)
894
+ bar {
895
+ a: b;
896
+ c: d; }
897
+ CSS
898
+ @mixin foo {a: b}
899
+
900
+ bar {
901
+ @include foo;
902
+ c: d; }
903
+ SCSS
904
+ end
905
+
906
+ def test_mixins_with_empty_args
907
+ assert_equal <<CSS, render(<<SCSS)
908
+ .foo {
909
+ a: b; }
910
+ CSS
911
+ @mixin foo() {a: b}
912
+
913
+ .foo {@include foo();}
914
+ SCSS
915
+
916
+ assert_equal <<CSS, render(<<SCSS)
917
+ .foo {
918
+ a: b; }
919
+ CSS
920
+ @mixin foo() {a: b}
921
+
922
+ .foo {@include foo;}
923
+ SCSS
924
+
925
+ assert_equal <<CSS, render(<<SCSS)
926
+ .foo {
927
+ a: b; }
928
+ CSS
929
+ @mixin foo {a: b}
930
+
931
+ .foo {@include foo();}
932
+ SCSS
933
+ end
934
+
935
+ def test_mixins_with_args
936
+ assert_equal <<CSS, render(<<SCSS)
937
+ .foo {
938
+ a: bar; }
939
+ CSS
940
+ @mixin foo($a) {a: $a}
941
+
942
+ .foo {@include foo(bar)}
943
+ SCSS
944
+
945
+ assert_equal <<CSS, render(<<SCSS)
946
+ .foo {
947
+ a: bar;
948
+ b: 12px; }
949
+ CSS
950
+ @mixin foo($a, $b) {
951
+ a: $a;
952
+ b: $b; }
953
+
954
+ .foo {@include foo(bar, 12px)}
955
+ SCSS
956
+ end
957
+
958
+ def test_keyframes_rules_in_content
959
+ assert_equal <<CSS, render(<<SCSS)
960
+ @keyframes identifier {
961
+ 0% {
962
+ top: 0;
963
+ left: 0; }
964
+ 30% {
965
+ top: 50px; }
966
+ 68%, 72% {
967
+ left: 50px; }
968
+ 100% {
969
+ top: 100px;
970
+ left: 100%; } }
971
+ CSS
972
+ @mixin keyframes {
973
+ @keyframes identifier { @content }
974
+ }
975
+
976
+ @include keyframes {
977
+ 0% {top: 0; left: 0}
978
+ \#{"30%"} {top: 50px}
979
+ 68%, 72% {left: 50px}
980
+ 100% {top: 100px; left: 100%}
981
+ }
982
+ SCSS
983
+ end
984
+
985
+ ## Functions
986
+
987
+ def test_basic_function
988
+ assert_equal(<<CSS, render(<<SASS))
989
+ bar {
990
+ a: 3; }
991
+ CSS
992
+ @function foo() {
993
+ @return 1 + 2;
994
+ }
995
+
996
+ bar {
997
+ a: foo();
998
+ }
999
+ SASS
1000
+ end
1001
+
1002
+ def test_function_args
1003
+ assert_equal(<<CSS, render(<<SASS))
1004
+ bar {
1005
+ a: 3; }
1006
+ CSS
1007
+ @function plus($var1, $var2) {
1008
+ @return $var1 + $var2;
1009
+ }
1010
+
1011
+ bar {
1012
+ a: plus(1, 2);
1013
+ }
1014
+ SASS
1015
+ end
1016
+
1017
+ def test_disallowed_function_names
1018
+ assert_warning(<<WARNING) {render(<<SCSS)}
1019
+ DEPRECATION WARNING on line 1 of test_disallowed_function_names_inline.scss:
1020
+ Naming a function "calc" is disallowed and will be an error in future versions of Sass.
1021
+ This name conflicts with an existing CSS function with special parse rules.
1022
+ WARNING
1023
+ @function calc() {}
1024
+ SCSS
1025
+
1026
+ assert_warning(<<WARNING) {render(<<SCSS)}
1027
+ DEPRECATION WARNING on line 1 of test_disallowed_function_names_inline.scss:
1028
+ Naming a function "-my-calc" is disallowed and will be an error in future versions of Sass.
1029
+ This name conflicts with an existing CSS function with special parse rules.
1030
+ WARNING
1031
+ @function -my-calc() {}
1032
+ SCSS
1033
+
1034
+ assert_warning(<<WARNING) {render(<<SCSS)}
1035
+ DEPRECATION WARNING on line 1 of test_disallowed_function_names_inline.scss:
1036
+ Naming a function "element" is disallowed and will be an error in future versions of Sass.
1037
+ This name conflicts with an existing CSS function with special parse rules.
1038
+ WARNING
1039
+ @function element() {}
1040
+ SCSS
1041
+
1042
+ assert_warning(<<WARNING) {render(<<SCSS)}
1043
+ DEPRECATION WARNING on line 1 of test_disallowed_function_names_inline.scss:
1044
+ Naming a function "-my-element" is disallowed and will be an error in future versions of Sass.
1045
+ This name conflicts with an existing CSS function with special parse rules.
1046
+ WARNING
1047
+ @function -my-element() {}
1048
+ SCSS
1049
+
1050
+ assert_warning(<<WARNING) {render(<<SCSS)}
1051
+ DEPRECATION WARNING on line 1 of test_disallowed_function_names_inline.scss:
1052
+ Naming a function "expression" is disallowed and will be an error in future versions of Sass.
1053
+ This name conflicts with an existing CSS function with special parse rules.
1054
+ WARNING
1055
+ @function expression() {}
1056
+ SCSS
1057
+
1058
+ assert_warning(<<WARNING) {render(<<SCSS)}
1059
+ DEPRECATION WARNING on line 1 of test_disallowed_function_names_inline.scss:
1060
+ Naming a function "url" is disallowed and will be an error in future versions of Sass.
1061
+ This name conflicts with an existing CSS function with special parse rules.
1062
+ WARNING
1063
+ @function url() {}
1064
+ SCSS
1065
+ end
1066
+
1067
+ def test_allowed_function_names
1068
+ assert_no_warning {assert_equal(<<CSS, render(<<SCSS))}
1069
+ .a {
1070
+ b: c; }
1071
+ CSS
1072
+ @function -my-expression() {@return c}
1073
+
1074
+ .a {b: -my-expression()}
1075
+ SCSS
1076
+
1077
+ assert_no_warning {assert_equal(<<CSS, render(<<SCSS))}
1078
+ .a {
1079
+ b: c; }
1080
+ CSS
1081
+ @function -my-url() {@return c}
1082
+
1083
+ .a {b: -my-url()}
1084
+ SCSS
1085
+ end
1086
+
1087
+ ## Var Args
1088
+
1089
+ def test_mixin_var_args
1090
+ assert_equal <<CSS, render(<<SCSS)
1091
+ .foo {
1092
+ a: 1;
1093
+ b: 2, 3, 4; }
1094
+ CSS
1095
+ @mixin foo($a, $b...) {
1096
+ a: $a;
1097
+ b: $b;
1098
+ }
1099
+
1100
+ .foo {@include foo(1, 2, 3, 4)}
1101
+ SCSS
1102
+ end
1103
+
1104
+ def test_mixin_empty_var_args
1105
+ assert_equal <<CSS, render(<<SCSS)
1106
+ .foo {
1107
+ a: 1;
1108
+ b: 0; }
1109
+ CSS
1110
+ @mixin foo($a, $b...) {
1111
+ a: $a;
1112
+ b: length($b);
1113
+ }
1114
+
1115
+ .foo {@include foo(1)}
1116
+ SCSS
1117
+ end
1118
+
1119
+ def test_mixin_var_args_act_like_list
1120
+ assert_equal <<CSS, render(<<SCSS)
1121
+ .foo {
1122
+ a: 3;
1123
+ b: 3; }
1124
+ CSS
1125
+ @mixin foo($a, $b...) {
1126
+ a: length($b);
1127
+ b: nth($b, 2);
1128
+ }
1129
+
1130
+ .foo {@include foo(1, 2, 3, 4)}
1131
+ SCSS
1132
+ end
1133
+
1134
+ def test_mixin_splat_args
1135
+ assert_equal <<CSS, render(<<SCSS)
1136
+ .foo {
1137
+ a: 1;
1138
+ b: 2;
1139
+ c: 3;
1140
+ d: 4; }
1141
+ CSS
1142
+ @mixin foo($a, $b, $c, $d) {
1143
+ a: $a;
1144
+ b: $b;
1145
+ c: $c;
1146
+ d: $d;
1147
+ }
1148
+
1149
+ $list: 2, 3, 4;
1150
+ .foo {@include foo(1, $list...)}
1151
+ SCSS
1152
+ end
1153
+
1154
+ def test_mixin_splat_expression
1155
+ assert_equal <<CSS, render(<<SCSS)
1156
+ .foo {
1157
+ a: 1;
1158
+ b: 2;
1159
+ c: 3;
1160
+ d: 4; }
1161
+ CSS
1162
+ @mixin foo($a, $b, $c, $d) {
1163
+ a: $a;
1164
+ b: $b;
1165
+ c: $c;
1166
+ d: $d;
1167
+ }
1168
+
1169
+ .foo {@include foo(1, (2, 3, 4)...)}
1170
+ SCSS
1171
+ end
1172
+
1173
+ def test_mixin_splat_args_with_var_args
1174
+ assert_equal <<CSS, render(<<SCSS)
1175
+ .foo {
1176
+ a: 1;
1177
+ b: 2, 3, 4; }
1178
+ CSS
1179
+ @mixin foo($a, $b...) {
1180
+ a: $a;
1181
+ b: $b;
1182
+ }
1183
+
1184
+ $list: 2, 3, 4;
1185
+ .foo {@include foo(1, $list...)}
1186
+ SCSS
1187
+ end
1188
+
1189
+ def test_mixin_splat_args_with_var_args_and_normal_args
1190
+ assert_equal <<CSS, render(<<SCSS)
1191
+ .foo {
1192
+ a: 1;
1193
+ b: 2;
1194
+ c: 3, 4; }
1195
+ CSS
1196
+ @mixin foo($a, $b, $c...) {
1197
+ a: $a;
1198
+ b: $b;
1199
+ c: $c;
1200
+ }
1201
+
1202
+ $list: 2, 3, 4;
1203
+ .foo {@include foo(1, $list...)}
1204
+ SCSS
1205
+ end
1206
+
1207
+ def test_mixin_splat_args_with_var_args_preserves_separator
1208
+ assert_equal <<CSS, render(<<SCSS)
1209
+ .foo {
1210
+ a: 1;
1211
+ b: 2 3 4 5; }
1212
+ CSS
1213
+ @mixin foo($a, $b...) {
1214
+ a: $a;
1215
+ b: $b;
1216
+ }
1217
+
1218
+ $list: 3 4 5;
1219
+ .foo {@include foo(1, 2, $list...)}
1220
+ SCSS
1221
+ end
1222
+
1223
+ def test_mixin_var_and_splat_args_pass_through_keywords
1224
+ assert_equal <<CSS, render(<<SCSS)
1225
+ .foo {
1226
+ a: 3;
1227
+ b: 1;
1228
+ c: 2; }
1229
+ CSS
1230
+ @mixin foo($a...) {
1231
+ @include bar($a...);
1232
+ }
1233
+
1234
+ @mixin bar($b, $c, $a) {
1235
+ a: $a;
1236
+ b: $b;
1237
+ c: $c;
1238
+ }
1239
+
1240
+ .foo {@include foo(1, $c: 2, $a: 3)}
1241
+ SCSS
1242
+ end
1243
+
1244
+ def test_mixin_var_keyword_args
1245
+ assert_equal <<CSS, render(<<SCSS)
1246
+ .foo {
1247
+ a: 1;
1248
+ b: 2;
1249
+ c: 3; }
1250
+ CSS
1251
+ @mixin foo($args...) {
1252
+ a: map-get(keywords($args), a);
1253
+ b: map-get(keywords($args), b);
1254
+ c: map-get(keywords($args), c);
1255
+ }
1256
+
1257
+ .foo {@include foo($a: 1, $b: 2, $c: 3)}
1258
+ SCSS
1259
+ end
1260
+
1261
+ def test_mixin_empty_var_keyword_args
1262
+ assert_equal <<CSS, render(<<SCSS)
1263
+ .foo {
1264
+ length: 0; }
1265
+ CSS
1266
+ @mixin foo($args...) {
1267
+ length: length(keywords($args));
1268
+ }
1269
+
1270
+ .foo {@include foo}
1271
+ SCSS
1272
+ end
1273
+
1274
+ def test_mixin_map_splat
1275
+ assert_equal <<CSS, render(<<SCSS)
1276
+ .foo {
1277
+ a: 1;
1278
+ b: 2;
1279
+ c: 3; }
1280
+ CSS
1281
+ @mixin foo($a, $b, $c) {
1282
+ a: $a;
1283
+ b: $b;
1284
+ c: $c;
1285
+ }
1286
+
1287
+ .foo {
1288
+ $map: (a: 1, b: 2, c: 3);
1289
+ @include foo($map...);
1290
+ }
1291
+ SCSS
1292
+ end
1293
+
1294
+ def test_mixin_map_and_list_splat
1295
+ assert_equal <<CSS, render(<<SCSS)
1296
+ .foo {
1297
+ a: x;
1298
+ b: y;
1299
+ c: z;
1300
+ d: 1;
1301
+ e: 2;
1302
+ f: 3; }
1303
+ CSS
1304
+ @mixin foo($a, $b, $c, $d, $e, $f) {
1305
+ a: $a;
1306
+ b: $b;
1307
+ c: $c;
1308
+ d: $d;
1309
+ e: $e;
1310
+ f: $f;
1311
+ }
1312
+
1313
+ .foo {
1314
+ $list: x y z;
1315
+ $map: (d: 1, e: 2, f: 3);
1316
+ @include foo($list..., $map...);
1317
+ }
1318
+ SCSS
1319
+ end
1320
+
1321
+ def test_mixin_map_splat_takes_precedence_over_pass_through
1322
+ assert_equal <<CSS, render(<<SCSS)
1323
+ .foo {
1324
+ a: 1;
1325
+ b: 2;
1326
+ c: z; }
1327
+ CSS
1328
+ @mixin foo($args...) {
1329
+ $map: (c: z);
1330
+ @include bar($args..., $map...);
1331
+ }
1332
+
1333
+ @mixin bar($a, $b, $c) {
1334
+ a: $a;
1335
+ b: $b;
1336
+ c: $c;
1337
+ }
1338
+
1339
+ .foo {
1340
+ @include foo(1, $b: 2, $c: 3);
1341
+ }
1342
+ SCSS
1343
+ end
1344
+
1345
+ def test_mixin_list_of_pairs_splat_treated_as_list
1346
+ assert_equal <<CSS, render(<<SCSS)
1347
+ .foo {
1348
+ a: a 1;
1349
+ b: b 2;
1350
+ c: c 3; }
1351
+ CSS
1352
+ @mixin foo($a, $b, $c) {
1353
+ a: $a;
1354
+ b: $b;
1355
+ c: $c;
1356
+ }
1357
+
1358
+ .foo {
1359
+ @include foo((a 1, b 2, c 3)...);
1360
+ }
1361
+ SCSS
1362
+ end
1363
+
1364
+ def test_mixin_splat_after_keyword_args
1365
+ assert_equal <<CSS, render(<<SCSS)
1366
+ .foo {
1367
+ a: 1;
1368
+ b: 2;
1369
+ c: 3; }
1370
+ CSS
1371
+ @mixin foo($a, $b, $c) {
1372
+ a: 1;
1373
+ b: 2;
1374
+ c: 3;
1375
+ }
1376
+
1377
+ .foo {
1378
+ @include foo(1, $c: 3, 2...);
1379
+ }
1380
+ SCSS
1381
+ end
1382
+
1383
+ def test_mixin_keyword_args_after_splat
1384
+ assert_equal <<CSS, render(<<SCSS)
1385
+ .foo {
1386
+ a: 1;
1387
+ b: 2;
1388
+ c: 3; }
1389
+ CSS
1390
+ @mixin foo($a, $b, $c) {
1391
+ a: 1;
1392
+ b: 2;
1393
+ c: 3;
1394
+ }
1395
+
1396
+ .foo {
1397
+ @include foo(1, 2..., $c: 3);
1398
+ }
1399
+ SCSS
1400
+ end
1401
+
1402
+ def test_mixin_keyword_splat_after_keyword_args
1403
+ assert_equal <<CSS, render(<<SCSS)
1404
+ .foo {
1405
+ a: 1;
1406
+ b: 2;
1407
+ c: 3; }
1408
+ CSS
1409
+ @mixin foo($a, $b, $c) {
1410
+ a: 1;
1411
+ b: 2;
1412
+ c: 3;
1413
+ }
1414
+
1415
+ .foo {
1416
+ @include foo(1, $b: 2, (c: 3)...);
1417
+ }
1418
+ SCSS
1419
+ end
1420
+
1421
+ def test_mixin_triple_keyword_splat_merge
1422
+ assert_equal <<CSS, render(<<SCSS)
1423
+ .foo {
1424
+ foo: 1;
1425
+ bar: 2;
1426
+ kwarg: 3;
1427
+ a: 3;
1428
+ b: 2;
1429
+ c: 3; }
1430
+ CSS
1431
+ @mixin foo($foo, $bar, $kwarg, $a, $b, $c) {
1432
+ foo: $foo;
1433
+ bar: $bar;
1434
+ kwarg: $kwarg;
1435
+ a: $a;
1436
+ b: $b;
1437
+ c: $c;
1438
+ }
1439
+
1440
+ @mixin bar($args...) {
1441
+ @include foo($args..., $bar: 2, $a: 2, $b: 2, (kwarg: 3, a: 3, c: 3)...);
1442
+ }
1443
+
1444
+ .foo {
1445
+ @include bar($foo: 1, $a: 1, $b: 1, $c: 1);
1446
+ }
1447
+ SCSS
1448
+ end
1449
+
1450
+ def test_mixin_map_splat_converts_hyphens_and_underscores_for_real_args
1451
+ assert_equal <<CSS, render(<<SCSS)
1452
+ .foo {
1453
+ a: 1;
1454
+ b: 2;
1455
+ c: 3;
1456
+ d: 4; }
1457
+ CSS
1458
+ @mixin foo($a-1, $b-2, $c_3, $d_4) {
1459
+ a: $a-1;
1460
+ b: $b-2;
1461
+ c: $c_3;
1462
+ d: $d_4;
1463
+ }
1464
+
1465
+ .foo {
1466
+ $map: (a-1: 1, b_2: 2, c-3: 3, d_4: 4);
1467
+ @include foo($map...);
1468
+ }
1469
+ SCSS
1470
+ end
1471
+
1472
+ def test_mixin_map_splat_doesnt_convert_hyphens_and_underscores_for_var_args
1473
+ assert_equal <<CSS, render(<<SCSS)
1474
+ .foo {
1475
+ a-1: 1;
1476
+ b_2: 2;
1477
+ c-3: 3;
1478
+ d_4: 4; }
1479
+ CSS
1480
+ @mixin foo($args...) {
1481
+ @each $key, $value in keywords($args) {
1482
+ \#{$key}: $value;
1483
+ }
1484
+ }
1485
+
1486
+ .foo {
1487
+ $map: (a-1: 1, b_2: 2, c-3: 3, d_4: 4);
1488
+ @include foo($map...);
1489
+ }
1490
+ SCSS
1491
+ end
1492
+
1493
+ def test_mixin_conflicting_splat_after_keyword_args
1494
+ assert_raise_message(Sass::SyntaxError, <<MESSAGE.rstrip) {render(<<SCSS)}
1495
+ Mixin foo was passed argument $b both by position and by name.
1496
+ MESSAGE
1497
+ @mixin foo($a, $b, $c) {
1498
+ a: 1;
1499
+ b: 2;
1500
+ c: 3;
1501
+ }
1502
+
1503
+ .foo {
1504
+ @include foo(1, $b: 2, 3...);
1505
+ }
1506
+ SCSS
1507
+ end
1508
+
1509
+ def test_mixin_keyword_splat_must_have_string_keys
1510
+ assert_raise_message(Sass::SyntaxError, <<MESSAGE.rstrip) {render <<SCSS}
1511
+ Variable keyword argument map must have string keys.
1512
+ 12 is not a string in (12: 1).
1513
+ MESSAGE
1514
+ @mixin foo($a) {
1515
+ a: $a;
1516
+ }
1517
+
1518
+ .foo {@include foo((12: 1)...)}
1519
+ SCSS
1520
+ end
1521
+
1522
+ def test_mixin_positional_arg_after_splat
1523
+ assert_raise_message(Sass::SyntaxError, <<MESSAGE.rstrip) {render(<<SCSS)}
1524
+ Only keyword arguments may follow variable arguments (...).
1525
+ MESSAGE
1526
+ @mixin foo($a, $b, $c) {
1527
+ a: 1;
1528
+ b: 2;
1529
+ c: 3;
1530
+ }
1531
+
1532
+ .foo {
1533
+ @include foo(1, 2..., 3);
1534
+ }
1535
+ SCSS
1536
+ end
1537
+
1538
+ def test_mixin_var_args_with_keyword
1539
+ assert_raise_message(Sass::SyntaxError, "Positional arguments must come before keyword arguments.") {render <<SCSS}
1540
+ @mixin foo($a, $b...) {
1541
+ a: $a;
1542
+ b: $b;
1543
+ }
1544
+
1545
+ .foo {@include foo($a: 1, 2, 3, 4)}
1546
+ SCSS
1547
+ end
1548
+
1549
+ def test_mixin_keyword_for_var_arg
1550
+ assert_raise_message(Sass::SyntaxError, "Argument $b of mixin foo cannot be used as a named argument.") {render <<SCSS}
1551
+ @mixin foo($a, $b...) {
1552
+ a: $a;
1553
+ b: $b;
1554
+ }
1555
+
1556
+ .foo {@include foo(1, $b: 2 3 4)}
1557
+ SCSS
1558
+ end
1559
+
1560
+ def test_mixin_keyword_for_unknown_arg_with_var_args
1561
+ assert_raise_message(Sass::SyntaxError, "Mixin foo doesn't have an argument named $c.") {render <<SCSS}
1562
+ @mixin foo($a, $b...) {
1563
+ a: $a;
1564
+ b: $b;
1565
+ }
1566
+
1567
+ .foo {@include foo(1, $c: 2 3 4)}
1568
+ SCSS
1569
+ end
1570
+
1571
+ def test_mixin_map_splat_before_list_splat
1572
+ assert_raise_message(Sass::SyntaxError, "Variable keyword arguments must be a map (was (2 3)).") {render <<SCSS}
1573
+ @mixin foo($a, $b, $c) {
1574
+ a: $a;
1575
+ b: $b;
1576
+ c: $c;
1577
+ }
1578
+
1579
+ .foo {
1580
+ @include foo((a: 1)..., (2 3)...);
1581
+ }
1582
+ SCSS
1583
+ end
1584
+
1585
+ def test_mixin_map_splat_with_unknown_keyword
1586
+ assert_raise_message(Sass::SyntaxError, "Mixin foo doesn't have an argument named $c.") {render <<SCSS}
1587
+ @mixin foo($a, $b) {
1588
+ a: $a;
1589
+ b: $b;
1590
+ }
1591
+
1592
+ .foo {
1593
+ @include foo(1, 2, (c: 1)...);
1594
+ }
1595
+ SCSS
1596
+ end
1597
+
1598
+ def test_mixin_map_splat_with_wrong_type
1599
+ assert_raise_message(Sass::SyntaxError, "Variable keyword arguments must be a map (was 12).") {render <<SCSS}
1600
+ @mixin foo($a, $b) {
1601
+ a: $a;
1602
+ b: $b;
1603
+ }
1604
+
1605
+ .foo {
1606
+ @include foo((1, 2)..., 12...);
1607
+ }
1608
+ SCSS
1609
+ end
1610
+
1611
+ def test_mixin_splat_too_many_args
1612
+ assert_warning(<<WARNING) {render <<SCSS}
1613
+ WARNING: Mixin foo takes 2 arguments but 4 were passed.
1614
+ on line 2 of #{filename_for_test(:scss)}
1615
+ This will be an error in future versions of Sass.
1616
+ WARNING
1617
+ @mixin foo($a, $b) {}
1618
+ @include foo((1, 2, 3, 4)...);
1619
+ SCSS
1620
+ end
1621
+
1622
+ def test_function_var_args
1623
+ assert_equal <<CSS, render(<<SCSS)
1624
+ .foo {
1625
+ val: "a: 1, b: 2, 3, 4"; }
1626
+ CSS
1627
+ @function foo($a, $b...) {
1628
+ @return "a: \#{$a}, b: \#{$b}";
1629
+ }
1630
+
1631
+ .foo {val: foo(1, 2, 3, 4)}
1632
+ SCSS
1633
+ end
1634
+
1635
+ def test_function_empty_var_args
1636
+ assert_equal <<CSS, render(<<SCSS)
1637
+ .foo {
1638
+ val: "a: 1, b: 0"; }
1639
+ CSS
1640
+ @function foo($a, $b...) {
1641
+ @return "a: \#{$a}, b: \#{length($b)}";
1642
+ }
1643
+
1644
+ .foo {val: foo(1)}
1645
+ SCSS
1646
+ end
1647
+
1648
+ def test_function_var_args_act_like_list
1649
+ assert_equal <<CSS, render(<<SCSS)
1650
+ .foo {
1651
+ val: "a: 3, b: 3"; }
1652
+ CSS
1653
+ @function foo($a, $b...) {
1654
+ @return "a: \#{length($b)}, b: \#{nth($b, 2)}";
1655
+ }
1656
+
1657
+ .foo {val: foo(1, 2, 3, 4)}
1658
+ SCSS
1659
+ end
1660
+
1661
+ def test_function_splat_args
1662
+ assert_equal <<CSS, render(<<SCSS)
1663
+ .foo {
1664
+ val: "a: 1, b: 2, c: 3, d: 4"; }
1665
+ CSS
1666
+ @function foo($a, $b, $c, $d) {
1667
+ @return "a: \#{$a}, b: \#{$b}, c: \#{$c}, d: \#{$d}";
1668
+ }
1669
+
1670
+ $list: 2, 3, 4;
1671
+ .foo {val: foo(1, $list...)}
1672
+ SCSS
1673
+ end
1674
+
1675
+ def test_function_splat_expression
1676
+ assert_equal <<CSS, render(<<SCSS)
1677
+ .foo {
1678
+ val: "a: 1, b: 2, c: 3, d: 4"; }
1679
+ CSS
1680
+ @function foo($a, $b, $c, $d) {
1681
+ @return "a: \#{$a}, b: \#{$b}, c: \#{$c}, d: \#{$d}";
1682
+ }
1683
+
1684
+ .foo {val: foo(1, (2, 3, 4)...)}
1685
+ SCSS
1686
+ end
1687
+
1688
+ def test_function_splat_args_with_var_args
1689
+ assert_equal <<CSS, render(<<SCSS)
1690
+ .foo {
1691
+ val: "a: 1, b: 2, 3, 4"; }
1692
+ CSS
1693
+ @function foo($a, $b...) {
1694
+ @return "a: \#{$a}, b: \#{$b}";
1695
+ }
1696
+
1697
+ $list: 2, 3, 4;
1698
+ .foo {val: foo(1, $list...)}
1699
+ SCSS
1700
+ end
1701
+
1702
+ def test_function_splat_args_with_var_args_and_normal_args
1703
+ assert_equal <<CSS, render(<<SCSS)
1704
+ .foo {
1705
+ val: "a: 1, b: 2, c: 3, 4"; }
1706
+ CSS
1707
+ @function foo($a, $b, $c...) {
1708
+ @return "a: \#{$a}, b: \#{$b}, c: \#{$c}";
1709
+ }
1710
+
1711
+ $list: 2, 3, 4;
1712
+ .foo {val: foo(1, $list...)}
1713
+ SCSS
1714
+ end
1715
+
1716
+ def test_function_splat_args_with_var_args_preserves_separator
1717
+ assert_equal <<CSS, render(<<SCSS)
1718
+ .foo {
1719
+ val: "a: 1, b: 2 3 4 5"; }
1720
+ CSS
1721
+ @function foo($a, $b...) {
1722
+ @return "a: \#{$a}, b: \#{$b}";
1723
+ }
1724
+
1725
+ $list: 3 4 5;
1726
+ .foo {val: foo(1, 2, $list...)}
1727
+ SCSS
1728
+ end
1729
+
1730
+ def test_function_var_and_splat_args_pass_through_keywords
1731
+ assert_equal <<CSS, render(<<SCSS)
1732
+ .foo {
1733
+ val: "a: 3, b: 1, c: 2"; }
1734
+ CSS
1735
+ @function foo($a...) {
1736
+ @return bar($a...);
1737
+ }
1738
+
1739
+ @function bar($b, $c, $a) {
1740
+ @return "a: \#{$a}, b: \#{$b}, c: \#{$c}";
1741
+ }
1742
+
1743
+ .foo {val: foo(1, $c: 2, $a: 3)}
1744
+ SCSS
1745
+ end
1746
+
1747
+ def test_function_var_keyword_args
1748
+ assert_equal <<CSS, render(<<SCSS)
1749
+ .foo {
1750
+ val: "a: 1, b: 2, c: 3"; }
1751
+ CSS
1752
+ @function foo($args...) {
1753
+ @return "a: \#{map-get(keywords($args), a)}, " +
1754
+ "b: \#{map-get(keywords($args), b)}, " +
1755
+ "c: \#{map-get(keywords($args), c)}";
1756
+ }
1757
+
1758
+ .foo {val: foo($a: 1, $b: 2, $c: 3)}
1759
+ SCSS
1760
+ end
1761
+
1762
+ def test_function_empty_var_keyword_args
1763
+ assert_equal <<CSS, render(<<SCSS)
1764
+ .foo {
1765
+ length: 0; }
1766
+ CSS
1767
+ @function foo($args...) {
1768
+ @return length(keywords($args));
1769
+ }
1770
+
1771
+ .foo {length: foo()}
1772
+ SCSS
1773
+ end
1774
+
1775
+ def test_function_map_splat
1776
+ assert_equal <<CSS, render(<<SCSS)
1777
+ .foo {
1778
+ val: "a: 1, b: 2, c: 3"; }
1779
+ CSS
1780
+ @function foo($a, $b, $c) {
1781
+ @return "a: \#{$a}, b: \#{$b}, c: \#{$c}";
1782
+ }
1783
+
1784
+ .foo {
1785
+ $map: (a: 1, b: 2, c: 3);
1786
+ val: foo($map...);
1787
+ }
1788
+ SCSS
1789
+ end
1790
+
1791
+ def test_function_map_and_list_splat
1792
+ assert_equal <<CSS, render(<<SCSS)
1793
+ .foo {
1794
+ val: "a: x, b: y, c: z, d: 1, e: 2, f: 3"; }
1795
+ CSS
1796
+ @function foo($a, $b, $c, $d, $e, $f) {
1797
+ @return "a: \#{$a}, b: \#{$b}, c: \#{$c}, d: \#{$d}, e: \#{$e}, f: \#{$f}";
1798
+ }
1799
+
1800
+ .foo {
1801
+ $list: x y z;
1802
+ $map: (d: 1, e: 2, f: 3);
1803
+ val: foo($list..., $map...);
1804
+ }
1805
+ SCSS
1806
+ end
1807
+
1808
+ def test_function_map_splat_takes_precedence_over_pass_through
1809
+ assert_equal <<CSS, render(<<SCSS)
1810
+ .foo {
1811
+ val: "a: 1, b: 2, c: z"; }
1812
+ CSS
1813
+ @function foo($args...) {
1814
+ $map: (c: z);
1815
+ @return bar($args..., $map...);
1816
+ }
1817
+
1818
+ @function bar($a, $b, $c) {
1819
+ @return "a: \#{$a}, b: \#{$b}, c: \#{$c}";
1820
+ }
1821
+
1822
+ .foo {
1823
+ val: foo(1, $b: 2, $c: 3);
1824
+ }
1825
+ SCSS
1826
+ end
1827
+
1828
+ def test_ruby_function_map_splat_takes_precedence_over_pass_through
1829
+ assert_equal <<CSS, render(<<SCSS)
1830
+ .foo {
1831
+ val: 1 2 3 z; }
1832
+ CSS
1833
+ @function foo($args...) {
1834
+ $map: (val: z);
1835
+ @return append($args..., $map...);
1836
+ }
1837
+
1838
+ .foo {
1839
+ val: foo(1 2 3, $val: 4)
1840
+ }
1841
+ SCSS
1842
+ end
1843
+
1844
+ def test_function_list_of_pairs_splat_treated_as_list
1845
+ assert_equal <<CSS, render(<<SCSS)
1846
+ .foo {
1847
+ val: "a: a 1, b: b 2, c: c 3"; }
1848
+ CSS
1849
+ @function foo($a, $b, $c) {
1850
+ @return "a: \#{$a}, b: \#{$b}, c: \#{$c}";
1851
+ }
1852
+
1853
+ .foo {
1854
+ val: foo((a 1, b 2, c 3)...);
1855
+ }
1856
+ SCSS
1857
+ end
1858
+
1859
+ def test_function_splat_after_keyword_args
1860
+ assert_equal <<CSS, render(<<SCSS)
1861
+ .foo {
1862
+ val: "a: 1, b: 2, c: 3"; }
1863
+ CSS
1864
+ @function foo($a, $b, $c) {
1865
+ @return "a: \#{$a}, b: \#{$b}, c: \#{$c}";
1866
+ }
1867
+
1868
+ .foo {
1869
+ val: foo(1, $c: 3, 2...);
1870
+ }
1871
+ SCSS
1872
+ end
1873
+
1874
+ def test_function_keyword_args_after_splat
1875
+ assert_equal <<CSS, render(<<SCSS)
1876
+ .foo {
1877
+ val: "a: 1, b: 2, c: 3"; }
1878
+ CSS
1879
+ @function foo($a, $b, $c) {
1880
+ @return "a: \#{$a}, b: \#{$b}, c: \#{$c}";
1881
+ }
1882
+
1883
+ .foo {
1884
+ val: foo(1, 2..., $c: 3);
1885
+ }
1886
+ SCSS
1887
+ end
1888
+
1889
+ def test_function_keyword_splat_after_keyword_args
1890
+ assert_equal <<CSS, render(<<SCSS)
1891
+ .foo {
1892
+ val: "a: 1, b: 2, c: 3"; }
1893
+ CSS
1894
+ @function foo($a, $b, $c) {
1895
+ @return "a: \#{$a}, b: \#{$b}, c: \#{$c}";
1896
+ }
1897
+
1898
+ .foo {
1899
+ val: foo(1, $b: 2, (c: 3)...);
1900
+ }
1901
+ SCSS
1902
+ end
1903
+
1904
+ def test_function_triple_keyword_splat_merge
1905
+ assert_equal <<CSS, render(<<SCSS)
1906
+ .foo {
1907
+ val: "foo: 1, bar: 2, kwarg: 3, a: 3, b: 2, c: 3"; }
1908
+ CSS
1909
+ @function foo($foo, $bar, $kwarg, $a, $b, $c) {
1910
+ @return "foo: \#{$foo}, bar: \#{$bar}, kwarg: \#{$kwarg}, a: \#{$a}, b: \#{$b}, c: \#{$c}";
1911
+ }
1912
+
1913
+ @function bar($args...) {
1914
+ @return foo($args..., $bar: 2, $a: 2, $b: 2, (kwarg: 3, a: 3, c: 3)...);
1915
+ }
1916
+
1917
+ .foo {
1918
+ val: bar($foo: 1, $a: 1, $b: 1, $c: 1);
1919
+ }
1920
+ SCSS
1921
+ end
1922
+
1923
+ def test_function_conflicting_splat_after_keyword_args
1924
+ assert_raise_message(Sass::SyntaxError, <<MESSAGE.rstrip) {render(<<SCSS)}
1925
+ Function foo was passed argument $b both by position and by name.
1926
+ MESSAGE
1927
+ @function foo($a, $b, $c) {
1928
+ @return "a: \#{$a}, b: \#{$b}, c: \#{$c}";
1929
+ }
1930
+
1931
+ .foo {
1932
+ val: foo(1, $b: 2, 3...);
1933
+ }
1934
+ SCSS
1935
+ end
1936
+
1937
+ def test_function_positional_arg_after_splat
1938
+ assert_raise_message(Sass::SyntaxError, <<MESSAGE.rstrip) {render(<<SCSS)}
1939
+ Only keyword arguments may follow variable arguments (...).
1940
+ MESSAGE
1941
+ @function foo($a, $b, $c) {
1942
+ @return "a: \#{$a}, b: \#{$b}, c: \#{$c}";
1943
+ }
1944
+
1945
+ .foo {
1946
+ val: foo(1, 2..., 3);
1947
+ }
1948
+ SCSS
1949
+ end
1950
+
1951
+ def test_function_var_args_with_keyword
1952
+ assert_raise_message(Sass::SyntaxError, "Positional arguments must come before keyword arguments.") {render <<SCSS}
1953
+ @function foo($a, $b...) {
1954
+ @return "a: \#{$a}, b: \#{$b}";
1955
+ }
1956
+
1957
+ .foo {val: foo($a: 1, 2, 3, 4)}
1958
+ SCSS
1959
+ end
1960
+
1961
+ def test_function_keyword_for_var_arg
1962
+ assert_raise_message(Sass::SyntaxError, "Argument $b of function foo cannot be used as a named argument.") {render <<SCSS}
1963
+ @function foo($a, $b...) {
1964
+ @return "a: \#{$a}, b: \#{$b}";
1965
+ }
1966
+
1967
+ .foo {val: foo(1, $b: 2 3 4)}
1968
+ SCSS
1969
+ end
1970
+
1971
+ def test_function_keyword_for_unknown_arg_with_var_args
1972
+ assert_raise_message(Sass::SyntaxError, "Function foo doesn't have an argument named $c.") {render <<SCSS}
1973
+ @function foo($a, $b...) {
1974
+ @return "a: \#{$a}, b: \#{length($b)}";
1975
+ }
1976
+
1977
+ .foo {val: foo(1, $c: 2 3 4)}
1978
+ SCSS
1979
+ end
1980
+
1981
+ def test_function_var_args_passed_to_native
1982
+ assert_equal <<CSS, render(<<SCSS)
1983
+ .foo {
1984
+ val: #102035; }
1985
+ CSS
1986
+ @function foo($args...) {
1987
+ @return adjust-color($args...);
1988
+ }
1989
+
1990
+ .foo {val: foo(#102030, $blue: 5)}
1991
+ SCSS
1992
+ end
1993
+
1994
+ def test_function_map_splat_before_list_splat
1995
+ assert_raise_message(Sass::SyntaxError, "Variable keyword arguments must be a map (was (2 3)).") {render <<SCSS}
1996
+ @function foo($a, $b, $c) {
1997
+ @return "a: \#{$a}, b: \#{$b}, c: \#{$c}";
1998
+ }
1999
+
2000
+ .foo {
2001
+ val: foo((a: 1)..., (2 3)...);
2002
+ }
2003
+ SCSS
2004
+ end
2005
+
2006
+ def test_function_map_splat_with_unknown_keyword
2007
+ assert_raise_message(Sass::SyntaxError, "Function foo doesn't have an argument named $c.") {render <<SCSS}
2008
+ @function foo($a, $b) {
2009
+ @return "a: \#{$a}, b: \#{$b}";
2010
+ }
2011
+
2012
+ .foo {
2013
+ val: foo(1, 2, (c: 1)...);
2014
+ }
2015
+ SCSS
2016
+ end
2017
+
2018
+ def test_function_map_splat_with_wrong_type
2019
+ assert_raise_message(Sass::SyntaxError, "Variable keyword arguments must be a map (was 12).") {render <<SCSS}
2020
+ @function foo($a, $b) {
2021
+ @return "a: \#{$a}, b: \#{$b}";
2022
+ }
2023
+
2024
+ .foo {
2025
+ val: foo((1, 2)..., 12...);
2026
+ }
2027
+ SCSS
2028
+ end
2029
+
2030
+ def test_function_keyword_splat_must_have_string_keys
2031
+ assert_raise_message(Sass::SyntaxError, <<MESSAGE.rstrip) {render <<SCSS}
2032
+ Variable keyword argument map must have string keys.
2033
+ 12 is not a string in (12: 1).
2034
+ MESSAGE
2035
+ @function foo($a) {
2036
+ @return $a;
2037
+ }
2038
+
2039
+ .foo {val: foo((12: 1)...)}
2040
+ SCSS
2041
+ end
2042
+
2043
+ def test_function_splat_too_many_args
2044
+ assert_warning(<<WARNING) {render <<SCSS}
2045
+ WARNING: Function foo takes 2 arguments but 4 were passed.
2046
+ on line 2 of #{filename_for_test(:scss)}
2047
+ This will be an error in future versions of Sass.
2048
+ WARNING
2049
+ @function foo($a, $b) {@return null}
2050
+ $var: foo((1, 2, 3, 4)...);
2051
+ SCSS
2052
+ end
2053
+
2054
+ ## Interpolation
2055
+
2056
+ def test_basic_selector_interpolation
2057
+ assert_equal <<CSS, render(<<SCSS)
2058
+ foo ab baz {
2059
+ a: b; }
2060
+ CSS
2061
+ foo \#{'a' + 'b'} baz {a: b}
2062
+ SCSS
2063
+ assert_equal <<CSS, render(<<SCSS)
2064
+ foo.bar baz {
2065
+ a: b; }
2066
+ CSS
2067
+ foo\#{".bar"} baz {a: b}
2068
+ SCSS
2069
+ assert_equal <<CSS, render(<<SCSS)
2070
+ foo.bar baz {
2071
+ a: b; }
2072
+ CSS
2073
+ \#{"foo"}.bar baz {a: b}
2074
+ SCSS
2075
+ end
2076
+
2077
+ def test_selector_only_interpolation
2078
+ assert_equal <<CSS, render(<<SCSS)
2079
+ foo bar {
2080
+ a: b; }
2081
+ CSS
2082
+ \#{"foo" + " bar"} {a: b}
2083
+ SCSS
2084
+ end
2085
+
2086
+ def test_selector_interpolation_before_element_name
2087
+ assert_equal <<CSS, render(<<SCSS)
2088
+ foo barbaz {
2089
+ a: b; }
2090
+ CSS
2091
+ \#{"foo" + " bar"}baz {a: b}
2092
+ SCSS
2093
+ end
2094
+
2095
+ def test_selector_interpolation_in_string
2096
+ assert_equal <<CSS, render(<<SCSS)
2097
+ foo[val="bar foo bar baz"] {
2098
+ a: b; }
2099
+ CSS
2100
+ foo[val="bar \#{"foo" + " bar"} baz"] {a: b}
2101
+ SCSS
2102
+ end
2103
+
2104
+ def test_selector_interpolation_in_pseudoclass
2105
+ assert_equal <<CSS, render(<<SCSS)
2106
+ foo:nth-child(5n) {
2107
+ a: b; }
2108
+ CSS
2109
+ foo:nth-child(\#{5 + "n"}) {a: b}
2110
+ SCSS
2111
+ end
2112
+
2113
+ def test_selector_interpolation_at_class_begininng
2114
+ assert_equal <<CSS, render(<<SCSS)
2115
+ .zzz {
2116
+ a: b; }
2117
+ CSS
2118
+ $zzz: zzz;
2119
+ .\#{$zzz} { a: b; }
2120
+ SCSS
2121
+ end
2122
+
2123
+ def test_selector_interpolation_at_id_begininng
2124
+ assert_equal <<CSS, render(<<SCSS)
2125
+ #zzz {
2126
+ a: b; }
2127
+ CSS
2128
+ $zzz: zzz;
2129
+ #\#{$zzz} { a: b; }
2130
+ SCSS
2131
+ end
2132
+
2133
+ def test_selector_interpolation_at_pseudo_begininng
2134
+ assert_equal <<CSS, render(<<SCSS)
2135
+ :zzz::zzz {
2136
+ a: b; }
2137
+ CSS
2138
+ $zzz: zzz;
2139
+ :\#{$zzz}::\#{$zzz} { a: b; }
2140
+ SCSS
2141
+ end
2142
+
2143
+ def test_selector_interpolation_at_attr_beginning
2144
+ assert_equal <<CSS, render(<<SCSS)
2145
+ [zzz=foo] {
2146
+ a: b; }
2147
+ CSS
2148
+ $zzz: zzz;
2149
+ [\#{$zzz}=foo] { a: b; }
2150
+ SCSS
2151
+ end
2152
+
2153
+ def test_selector_interpolation_at_attr_end
2154
+ assert_equal <<CSS, render(<<SCSS)
2155
+ [foo=zzz] {
2156
+ a: b; }
2157
+ CSS
2158
+ $zzz: zzz;
2159
+ [foo=\#{$zzz}] { a: b; }
2160
+ SCSS
2161
+ end
2162
+
2163
+ def test_selector_interpolation_at_dashes
2164
+ assert_equal <<CSS, render(<<SCSS)
2165
+ div {
2166
+ -foo-a-b-foo: foo; }
2167
+ CSS
2168
+ $a : a;
2169
+ $b : b;
2170
+ div { -foo-\#{$a}-\#{$b}-foo: foo }
2171
+ SCSS
2172
+ end
2173
+
2174
+ def test_selector_interpolation_in_reference_combinator
2175
+ assert_equal <<CSS, render(<<SCSS)
2176
+ .foo /a/ .bar /b|c/ .baz {
2177
+ a: b; }
2178
+ CSS
2179
+ $a: a;
2180
+ $b: b;
2181
+ $c: c;
2182
+ .foo /\#{$a}/ .bar /\#{$b}|\#{$c}/ .baz {a: b}
2183
+ SCSS
2184
+ end
2185
+
2186
+ def test_parent_selector_with_parent_and_subject
2187
+ silence_warnings {assert_equal <<CSS, render(<<SCSS)}
2188
+ bar foo.baz! .bip {
2189
+ c: d; }
2190
+ CSS
2191
+ $subject: "!";
2192
+ foo {
2193
+ bar &.baz\#{$subject} .bip {c: d}}
2194
+ SCSS
2195
+ end
2196
+
2197
+ def test_basic_prop_name_interpolation
2198
+ assert_equal <<CSS, render(<<SCSS)
2199
+ foo {
2200
+ barbazbang: blip; }
2201
+ CSS
2202
+ foo {bar\#{"baz" + "bang"}: blip}
2203
+ SCSS
2204
+ assert_equal <<CSS, render(<<SCSS)
2205
+ foo {
2206
+ bar3: blip; }
2207
+ CSS
2208
+ foo {bar\#{1 + 2}: blip}
2209
+ SCSS
2210
+ end
2211
+
2212
+ def test_prop_name_only_interpolation
2213
+ assert_equal <<CSS, render(<<SCSS)
2214
+ foo {
2215
+ bazbang: blip; }
2216
+ CSS
2217
+ foo {\#{"baz" + "bang"}: blip}
2218
+ SCSS
2219
+ end
2220
+
2221
+ def test_directive_interpolation
2222
+ assert_equal <<CSS, render(<<SCSS)
2223
+ @foo bar12 qux {
2224
+ a: b; }
2225
+ CSS
2226
+ $baz: 12;
2227
+ @foo bar\#{$baz} qux {a: b}
2228
+ SCSS
2229
+ end
2230
+
2231
+ def test_media_interpolation
2232
+ assert_equal <<CSS, render(<<SCSS)
2233
+ @media bar12 {
2234
+ a: b; }
2235
+ CSS
2236
+ $baz: 12;
2237
+ @media bar\#{$baz} {a: b}
2238
+ SCSS
2239
+ end
2240
+
2241
+ def test_script_in_media
2242
+ assert_equal <<CSS, render(<<SCSS)
2243
+ @media screen and (-webkit-min-device-pixel-ratio: 20), only print {
2244
+ a: b; }
2245
+ CSS
2246
+ $media1: screen;
2247
+ $media2: print;
2248
+ $var: -webkit-min-device-pixel-ratio;
2249
+ $val: 20;
2250
+ @media \#{$media1} and ($var: $val), only \#{$media2} {a: b}
2251
+ SCSS
2252
+
2253
+ assert_equal <<CSS, render(<<SCSS)
2254
+ @media screen and (-webkit-min-device-pixel-ratio: 13) {
2255
+ a: b; }
2256
+ CSS
2257
+ $vals: 1 2 3;
2258
+ @media screen and (-webkit-min-device-pixel-ratio: 5 + 6 + nth($vals, 2)) {a: b}
2259
+ SCSS
2260
+ end
2261
+
2262
+ def test_media_interpolation_with_reparse
2263
+ assert_equal <<CSS, render(<<SCSS)
2264
+ @media screen and (max-width: 300px) {
2265
+ a: b; }
2266
+ @media screen and (max-width: 300px) {
2267
+ a: b; }
2268
+ @media screen and (max-width: 300px) {
2269
+ a: b; }
2270
+ @media screen and (max-width: 300px), print and (max-width: 300px) {
2271
+ a: b; }
2272
+ CSS
2273
+ $constraint: "(max-width: 300px)";
2274
+ $fragment: "nd \#{$constraint}";
2275
+ $comma: "een, pri";
2276
+ @media screen and \#{$constraint} {a: b}
2277
+ @media screen {
2278
+ @media \#{$constraint} {a: b}
2279
+ }
2280
+ @media screen a\#{$fragment} {a: b}
2281
+ @media scr\#{$comma}nt {
2282
+ @media \#{$constraint} {a: b}
2283
+ }
2284
+ SCSS
2285
+ end
2286
+
2287
+ def test_moz_document_interpolation
2288
+ assert_equal <<CSS, render(<<SCSS)
2289
+ @-moz-document url(http://sass-lang.com/),
2290
+ url-prefix(http://sass-lang.com/docs),
2291
+ domain(sass-lang.com),
2292
+ domain("sass-lang.com") {
2293
+ .foo {
2294
+ a: b; } }
2295
+ CSS
2296
+ $domain: "sass-lang.com";
2297
+ @-moz-document url(http://\#{$domain}/),
2298
+ url-prefix(http://\#{$domain}/docs),
2299
+ domain(\#{$domain}),
2300
+ \#{domain($domain)} {
2301
+ .foo {a: b}
2302
+ }
2303
+ SCSS
2304
+ end
2305
+
2306
+ def test_supports_with_expressions
2307
+ assert_equal <<CSS, render(<<SCSS)
2308
+ @supports ((feature1: val) and (feature2: val)) or (not (feature23: val4)) {
2309
+ foo {
2310
+ a: b; } }
2311
+ CSS
2312
+ $query: "(feature1: val)";
2313
+ $feature: feature2;
2314
+ $val: val;
2315
+ @supports (\#{$query} and ($feature: $val)) or (not ($feature + 3: $val + 4)) {
2316
+ foo {a: b}
2317
+ }
2318
+ SCSS
2319
+ end
2320
+
2321
+ def test_supports_bubbling
2322
+ assert_equal <<CSS, render(<<SCSS)
2323
+ @supports (foo: bar) {
2324
+ a {
2325
+ b: c; }
2326
+ @supports (baz: bang) {
2327
+ a {
2328
+ d: e; } } }
2329
+ CSS
2330
+ a {
2331
+ @supports (foo: bar) {
2332
+ b: c;
2333
+ @supports (baz: bang) {
2334
+ d: e;
2335
+ }
2336
+ }
2337
+ }
2338
+ SCSS
2339
+ end
2340
+
2341
+ def test_random_directive_interpolation
2342
+ assert_equal <<CSS, render(<<SCSS)
2343
+ @foo url(http://sass-lang.com/),
2344
+ domain("sass-lang.com"),
2345
+ "foobarbaz",
2346
+ foobarbaz {
2347
+ .foo {
2348
+ a: b; } }
2349
+ CSS
2350
+ $domain: "sass-lang.com";
2351
+ @foo url(http://\#{$domain}/),
2352
+ \#{domain($domain)},
2353
+ "foo\#{'ba' + 'r'}baz",
2354
+ foo\#{'ba' + 'r'}baz {
2355
+ .foo {a: b}
2356
+ }
2357
+ SCSS
2358
+ end
2359
+
2360
+ def test_color_interpolation_warning_in_selector
2361
+ assert_warning(<<WARNING) {assert_equal <<CSS, render(<<SCSS)}
2362
+ WARNING on line 1, column 4 of #{filename_for_test(:scss)}:
2363
+ You probably don't mean to use the color value `blue' in interpolation here.
2364
+ It may end up represented as #0000ff, which will likely produce invalid CSS.
2365
+ Always quote color names when using them as strings (for example, "blue").
2366
+ If you really want to use the color value here, use `"" + blue'.
2367
+ WARNING
2368
+ fooblue {
2369
+ a: b; }
2370
+ CSS
2371
+ foo\#{blue} {a: b}
2372
+ SCSS
2373
+ end
2374
+
2375
+ def test_color_interpolation_warning_in_directive
2376
+ assert_warning(<<WARNING) {assert_equal <<CSS, render(<<SCSS)}
2377
+ WARNING on line 1, column 12 of #{filename_for_test(:scss)}:
2378
+ You probably don't mean to use the color value `blue' in interpolation here.
2379
+ It may end up represented as #0000ff, which will likely produce invalid CSS.
2380
+ Always quote color names when using them as strings (for example, "blue").
2381
+ If you really want to use the color value here, use `"" + blue'.
2382
+ WARNING
2383
+ @fblthp fooblue {
2384
+ a: b; }
2385
+ CSS
2386
+ @fblthp foo\#{blue} {a: b}
2387
+ SCSS
2388
+ end
2389
+
2390
+ def test_color_interpolation_warning_in_property_name
2391
+ assert_warning(<<WARNING) {assert_equal <<CSS, render(<<SCSS)}
2392
+ WARNING on line 1, column 8 of #{filename_for_test(:scss)}:
2393
+ You probably don't mean to use the color value `blue' in interpolation here.
2394
+ It may end up represented as #0000ff, which will likely produce invalid CSS.
2395
+ Always quote color names when using them as strings (for example, "blue").
2396
+ If you really want to use the color value here, use `"" + blue'.
2397
+ WARNING
2398
+ foo {
2399
+ a-blue: b; }
2400
+ CSS
2401
+ foo {a-\#{blue}: b}
2402
+ SCSS
2403
+ end
2404
+
2405
+ def test_no_color_interpolation_warning_in_property_value
2406
+ assert_no_warning {assert_equal <<CSS, render(<<SCSS)}
2407
+ foo {
2408
+ a: b-blue; }
2409
+ CSS
2410
+ foo {a: b-\#{blue}}
2411
+ SCSS
2412
+ end
2413
+
2414
+ def test_no_color_interpolation_warning_for_nameless_color
2415
+ assert_no_warning {assert_equal <<CSS, render(<<SCSS)}
2416
+ foo-#abcdef {
2417
+ a: b; }
2418
+ CSS
2419
+ foo-\#{#abcdef} {a: b}
2420
+ SCSS
2421
+ end
2422
+
2423
+ def test_nested_mixin_def
2424
+ assert_equal <<CSS, render(<<SCSS)
2425
+ foo {
2426
+ a: b; }
2427
+ CSS
2428
+ foo {
2429
+ @mixin bar {a: b}
2430
+ @include bar; }
2431
+ SCSS
2432
+ end
2433
+
2434
+ def test_nested_mixin_shadow
2435
+ assert_equal <<CSS, render(<<SCSS)
2436
+ foo {
2437
+ c: d; }
2438
+
2439
+ baz {
2440
+ a: b; }
2441
+ CSS
2442
+ @mixin bar {a: b}
2443
+
2444
+ foo {
2445
+ @mixin bar {c: d}
2446
+ @include bar;
2447
+ }
2448
+
2449
+ baz {@include bar}
2450
+ SCSS
2451
+ end
2452
+
2453
+ def test_nested_function_def
2454
+ assert_equal <<CSS, render(<<SCSS)
2455
+ foo {
2456
+ a: 1; }
2457
+
2458
+ bar {
2459
+ b: foo(); }
2460
+ CSS
2461
+ foo {
2462
+ @function foo() {@return 1}
2463
+ a: foo(); }
2464
+
2465
+ bar {b: foo()}
2466
+ SCSS
2467
+ end
2468
+
2469
+ def test_nested_function_shadow
2470
+ assert_equal <<CSS, render(<<SCSS)
2471
+ foo {
2472
+ a: 2; }
2473
+
2474
+ baz {
2475
+ b: 1; }
2476
+ CSS
2477
+ @function foo() {@return 1}
2478
+
2479
+ foo {
2480
+ @function foo() {@return 2}
2481
+ a: foo();
2482
+ }
2483
+
2484
+ baz {b: foo()}
2485
+ SCSS
2486
+ end
2487
+
2488
+ ## @at-root
2489
+
2490
+ def test_simple_at_root
2491
+ assert_equal <<CSS, render(<<SCSS)
2492
+ .bar {
2493
+ a: b; }
2494
+ CSS
2495
+ .foo {
2496
+ @at-root {
2497
+ .bar {a: b}
2498
+ }
2499
+ }
2500
+ SCSS
2501
+ end
2502
+
2503
+ def test_at_root_with_selector
2504
+ assert_equal <<CSS, render(<<SCSS)
2505
+ .bar {
2506
+ a: b; }
2507
+ CSS
2508
+ .foo {
2509
+ @at-root .bar {a: b}
2510
+ }
2511
+ SCSS
2512
+ end
2513
+
2514
+ def test_at_root_in_mixin
2515
+ assert_equal <<CSS, render(<<SCSS)
2516
+ .bar {
2517
+ a: b; }
2518
+ CSS
2519
+ @mixin bar {
2520
+ @at-root .bar {a: b}
2521
+ }
2522
+
2523
+ .foo {
2524
+ @include bar;
2525
+ }
2526
+ SCSS
2527
+ end
2528
+
2529
+ def test_at_root_in_media
2530
+ assert_equal <<CSS, render(<<SCSS)
2531
+ @media screen {
2532
+ .bar {
2533
+ a: b; } }
2534
+ CSS
2535
+ @media screen {
2536
+ .foo {
2537
+ @at-root .bar {a: b}
2538
+ }
2539
+ }
2540
+ SCSS
2541
+ end
2542
+
2543
+ def test_at_root_in_bubbled_media
2544
+ assert_equal <<CSS, render(<<SCSS)
2545
+ @media screen {
2546
+ .bar {
2547
+ a: b; } }
2548
+ CSS
2549
+ .foo {
2550
+ @media screen {
2551
+ @at-root .bar {a: b}
2552
+ }
2553
+ }
2554
+ SCSS
2555
+ end
2556
+
2557
+ def test_at_root_in_unknown_directive
2558
+ assert_equal <<CSS, render(<<SCSS)
2559
+ @fblthp {
2560
+ .bar {
2561
+ a: b; } }
2562
+ CSS
2563
+ @fblthp {
2564
+ .foo {
2565
+ @at-root .bar {a: b}
2566
+ }
2567
+ }
2568
+ SCSS
2569
+ end
2570
+
2571
+ def test_comments_in_at_root
2572
+ assert_equal <<CSS, render(<<SCSS)
2573
+ /* foo */
2574
+ .bar {
2575
+ a: b; }
2576
+
2577
+ /* baz */
2578
+ CSS
2579
+ .foo {
2580
+ @at-root {
2581
+ /* foo */
2582
+ .bar {a: b}
2583
+ /* baz */
2584
+ }
2585
+ }
2586
+ SCSS
2587
+ end
2588
+
2589
+ def test_comments_in_at_root_in_media
2590
+ assert_equal <<CSS, render(<<SCSS)
2591
+ @media screen {
2592
+ /* foo */
2593
+ .bar {
2594
+ a: b; }
2595
+
2596
+ /* baz */ }
2597
+ CSS
2598
+ @media screen {
2599
+ .foo {
2600
+ @at-root {
2601
+ /* foo */
2602
+ .bar {a: b}
2603
+ /* baz */
2604
+ }
2605
+ }
2606
+ }
2607
+ SCSS
2608
+ end
2609
+
2610
+ def test_comments_in_at_root_in_unknown_directive
2611
+ assert_equal <<CSS, render(<<SCSS)
2612
+ @fblthp {
2613
+ /* foo */
2614
+ .bar {
2615
+ a: b; }
2616
+
2617
+ /* baz */ }
2618
+ CSS
2619
+ @fblthp {
2620
+ .foo {
2621
+ @at-root {
2622
+ /* foo */
2623
+ .bar {a: b}
2624
+ /* baz */
2625
+ }
2626
+ }
2627
+ }
2628
+ SCSS
2629
+ end
2630
+
2631
+ def test_media_directive_in_at_root
2632
+ assert_equal <<CSS, render(<<SCSS)
2633
+ @media screen {
2634
+ .bar {
2635
+ a: b; } }
2636
+ CSS
2637
+ .foo {
2638
+ @at-root {
2639
+ @media screen {.bar {a: b}}
2640
+ }
2641
+ }
2642
+ SCSS
2643
+ end
2644
+
2645
+ def test_bubbled_media_directive_in_at_root
2646
+ assert_equal <<CSS, render(<<SCSS)
2647
+ @media screen {
2648
+ .bar .baz {
2649
+ a: b; } }
2650
+ CSS
2651
+ .foo {
2652
+ @at-root {
2653
+ .bar {
2654
+ @media screen {.baz {a: b}}
2655
+ }
2656
+ }
2657
+ }
2658
+ SCSS
2659
+ end
2660
+
2661
+ def test_unknown_directive_in_at_root
2662
+ assert_equal <<CSS, render(<<SCSS)
2663
+ @fblthp {
2664
+ .bar {
2665
+ a: b; } }
2666
+ CSS
2667
+ .foo {
2668
+ @at-root {
2669
+ @fblthp {.bar {a: b}}
2670
+ }
2671
+ }
2672
+ SCSS
2673
+ end
2674
+
2675
+ def test_at_root_in_at_root
2676
+ assert_equal <<CSS, render(<<SCSS)
2677
+ .bar {
2678
+ a: b; }
2679
+ CSS
2680
+ .foo {
2681
+ @at-root {
2682
+ @at-root .bar {a: b}
2683
+ }
2684
+ }
2685
+ SCSS
2686
+ end
2687
+
2688
+ def test_at_root_with_parent_ref
2689
+ assert_equal <<CSS, render(<<SCSS)
2690
+ .foo {
2691
+ a: b; }
2692
+ CSS
2693
+ .foo {
2694
+ @at-root & {
2695
+ a: b;
2696
+ }
2697
+ }
2698
+ SCSS
2699
+ end
2700
+
2701
+ def test_multi_level_at_root_with_parent_ref
2702
+ assert_equal <<CSS, render(<<SCSS)
2703
+ .foo .bar {
2704
+ a: b; }
2705
+ CSS
2706
+ .foo {
2707
+ @at-root & {
2708
+ .bar {
2709
+ @at-root & {
2710
+ a: b;
2711
+ }
2712
+ }
2713
+ }
2714
+ }
2715
+ SCSS
2716
+ end
2717
+
2718
+ def test_multi_level_at_root_with_inner_parent_ref
2719
+ assert_equal <<CSS, render(<<SCSS)
2720
+ .bar {
2721
+ a: b; }
2722
+ CSS
2723
+ .foo {
2724
+ @at-root .bar {
2725
+ @at-root & {
2726
+ a: b;
2727
+ }
2728
+ }
2729
+ }
2730
+ SCSS
2731
+ end
2732
+
2733
+ def test_at_root_beneath_comma_selector
2734
+ assert_equal(<<CSS, render(<<SCSS))
2735
+ .baz {
2736
+ a: b; }
2737
+ CSS
2738
+ .foo, .bar {
2739
+ @at-root .baz {
2740
+ a: b;
2741
+ }
2742
+ }
2743
+ SCSS
2744
+ end
2745
+
2746
+ def test_at_root_with_parent_ref_and_class
2747
+ assert_equal(<<CSS, render(<<SCSS))
2748
+ .foo.bar {
2749
+ a: b; }
2750
+ CSS
2751
+ .foo {
2752
+ @at-root &.bar {
2753
+ a: b;
2754
+ }
2755
+ }
2756
+ SCSS
2757
+ end
2758
+
2759
+ def test_at_root_beneath_comma_selector_with_parent_ref
2760
+ assert_equal(<<CSS, render(<<SCSS))
2761
+ .foo.baz, .bar.baz {
2762
+ a: b; }
2763
+ CSS
2764
+ .foo, .bar {
2765
+ @at-root &.baz {
2766
+ a: b;
2767
+ }
2768
+ }
2769
+ SCSS
2770
+ end
2771
+
2772
+ ## @at-root (...)
2773
+
2774
+ def test_at_root_without_media
2775
+ assert_equal <<CSS, render(<<SCSS)
2776
+ .foo .bar {
2777
+ a: b; }
2778
+ CSS
2779
+ .foo {
2780
+ @media screen {
2781
+ @at-root (without: media) {
2782
+ .bar {
2783
+ a: b;
2784
+ }
2785
+ }
2786
+ }
2787
+ }
2788
+ SCSS
2789
+ end
2790
+
2791
+ def test_at_root_without_supports
2792
+ assert_equal <<CSS, render(<<SCSS)
2793
+ .foo .bar {
2794
+ a: b; }
2795
+ CSS
2796
+ .foo {
2797
+ @supports (foo: bar) {
2798
+ @at-root (without: supports) {
2799
+ .bar {
2800
+ a: b;
2801
+ }
2802
+ }
2803
+ }
2804
+ }
2805
+ SCSS
2806
+ end
2807
+
2808
+ def test_at_root_without_rule
2809
+ assert_equal <<CSS, render(<<SCSS)
2810
+ @media screen {
2811
+ .bar {
2812
+ a: b; } }
2813
+ CSS
2814
+ .foo {
2815
+ @media screen {
2816
+ @at-root (without: rule) {
2817
+ .bar {
2818
+ a: b;
2819
+ }
2820
+ }
2821
+ }
2822
+ }
2823
+ SCSS
2824
+ end
2825
+
2826
+ def test_at_root_without_unknown_directive
2827
+ assert_equal <<CSS, render(<<SCSS)
2828
+ @fblthp {}
2829
+ .foo .bar {
2830
+ a: b; }
2831
+ CSS
2832
+ .foo {
2833
+ @fblthp {
2834
+ @at-root (without: fblthp) {
2835
+ .bar {
2836
+ a: b;
2837
+ }
2838
+ }
2839
+ }
2840
+ }
2841
+ SCSS
2842
+ end
2843
+
2844
+ def test_at_root_without_multiple
2845
+ assert_equal <<CSS, render(<<SCSS)
2846
+ @supports (foo: bar) {
2847
+ .bar {
2848
+ a: b; } }
2849
+ CSS
2850
+ .foo {
2851
+ @media screen {
2852
+ @supports (foo: bar) {
2853
+ @at-root (without: media rule) {
2854
+ .bar {
2855
+ a: b;
2856
+ }
2857
+ }
2858
+ }
2859
+ }
2860
+ }
2861
+ SCSS
2862
+ end
2863
+
2864
+ def test_at_root_without_all
2865
+ assert_equal <<CSS, render(<<SCSS)
2866
+ @supports (foo: bar) {
2867
+ @fblthp {} }
2868
+ .bar {
2869
+ a: b; }
2870
+ CSS
2871
+ .foo {
2872
+ @supports (foo: bar) {
2873
+ @fblthp {
2874
+ @at-root (without: all) {
2875
+ .bar {
2876
+ a: b;
2877
+ }
2878
+ }
2879
+ }
2880
+ }
2881
+ }
2882
+ SCSS
2883
+ end
2884
+
2885
+ def test_at_root_with_media
2886
+ assert_equal <<CSS, render(<<SCSS)
2887
+ @media screen {
2888
+ @fblthp {}
2889
+ .bar {
2890
+ a: b; } }
2891
+ CSS
2892
+ .foo {
2893
+ @media screen {
2894
+ @fblthp {
2895
+ @supports (foo: bar) {
2896
+ @at-root (with: media) {
2897
+ .bar {
2898
+ a: b;
2899
+ }
2900
+ }
2901
+ }
2902
+ }
2903
+ }
2904
+ }
2905
+ SCSS
2906
+ end
2907
+
2908
+ def test_at_root_with_rule
2909
+ assert_equal <<CSS, render(<<SCSS)
2910
+ @media screen {
2911
+ @fblthp {} }
2912
+ .foo .bar {
2913
+ a: b; }
2914
+ CSS
2915
+ .foo {
2916
+ @media screen {
2917
+ @fblthp {
2918
+ @supports (foo: bar) {
2919
+ @at-root (with: rule) {
2920
+ .bar {
2921
+ a: b;
2922
+ }
2923
+ }
2924
+ }
2925
+ }
2926
+ }
2927
+ }
2928
+ SCSS
2929
+ end
2930
+
2931
+ def test_at_root_with_supports
2932
+ assert_equal <<CSS, render(<<SCSS)
2933
+ @media screen {
2934
+ @fblthp {} }
2935
+ @supports (foo: bar) {
2936
+ .bar {
2937
+ a: b; } }
2938
+ CSS
2939
+ .foo {
2940
+ @media screen {
2941
+ @fblthp {
2942
+ @supports (foo: bar) {
2943
+ @at-root (with: supports) {
2944
+ .bar {
2945
+ a: b;
2946
+ }
2947
+ }
2948
+ }
2949
+ }
2950
+ }
2951
+ }
2952
+ SCSS
2953
+ end
2954
+
2955
+ def test_at_root_with_unknown_directive
2956
+ assert_equal <<CSS, render(<<SCSS)
2957
+ @media screen {
2958
+ @fblthp {} }
2959
+ @fblthp {
2960
+ .bar {
2961
+ a: b; } }
2962
+ CSS
2963
+ .foo {
2964
+ @media screen {
2965
+ @fblthp {
2966
+ @supports (foo: bar) {
2967
+ @at-root (with: fblthp) {
2968
+ .bar {
2969
+ a: b;
2970
+ }
2971
+ }
2972
+ }
2973
+ }
2974
+ }
2975
+ }
2976
+ SCSS
2977
+ end
2978
+
2979
+ def test_at_root_with_multiple
2980
+ assert_equal <<CSS, render(<<SCSS)
2981
+ @media screen {
2982
+ @fblthp {}
2983
+ .foo .bar {
2984
+ a: b; } }
2985
+ CSS
2986
+ .foo {
2987
+ @media screen {
2988
+ @fblthp {
2989
+ @supports (foo: bar) {
2990
+ @at-root (with: media rule) {
2991
+ .bar {
2992
+ a: b;
2993
+ }
2994
+ }
2995
+ }
2996
+ }
2997
+ }
2998
+ }
2999
+ SCSS
3000
+ end
3001
+
3002
+ def test_at_root_with_all
3003
+ assert_equal <<CSS, render(<<SCSS)
3004
+ @media screen {
3005
+ @fblthp {
3006
+ @supports (foo: bar) {
3007
+ .foo .bar {
3008
+ a: b; } } } }
3009
+ CSS
3010
+ .foo {
3011
+ @media screen {
3012
+ @fblthp {
3013
+ @supports (foo: bar) {
3014
+ @at-root (with: all) {
3015
+ .bar {
3016
+ a: b;
3017
+ }
3018
+ }
3019
+ }
3020
+ }
3021
+ }
3022
+ }
3023
+ SCSS
3024
+ end
3025
+
3026
+ def test_at_root_dynamic_values
3027
+ assert_equal <<CSS, render(<<SCSS)
3028
+ @media screen {
3029
+ .bar {
3030
+ a: b; } }
3031
+ CSS
3032
+ $key: with;
3033
+ $value: media;
3034
+ .foo {
3035
+ @media screen {
3036
+ @at-root ($key: $value) {
3037
+ .bar {
3038
+ a: b;
3039
+ }
3040
+ }
3041
+ }
3042
+ }
3043
+ SCSS
3044
+ end
3045
+
3046
+ def test_at_root_interpolated_query
3047
+ assert_equal <<CSS, render(<<SCSS)
3048
+ @media screen {
3049
+ .bar {
3050
+ a: b; } }
3051
+ CSS
3052
+ .foo {
3053
+ @media screen {
3054
+ @at-root (\#{"with: media"}) {
3055
+ .bar {
3056
+ a: b;
3057
+ }
3058
+ }
3059
+ }
3060
+ }
3061
+ SCSS
3062
+ end
3063
+
3064
+ def test_at_root_plus_extend
3065
+ assert_equal <<CSS, render(<<SCSS)
3066
+ .foo .bar {
3067
+ a: b; }
3068
+ CSS
3069
+ %base {
3070
+ a: b;
3071
+ }
3072
+
3073
+ .foo {
3074
+ @media screen {
3075
+ @at-root (without: media) {
3076
+ .bar {
3077
+ @extend %base;
3078
+ }
3079
+ }
3080
+ }
3081
+ }
3082
+ SCSS
3083
+ end
3084
+
3085
+ def test_at_root_without_keyframes_in_keyframe_rule
3086
+ assert_equal <<CSS, render(<<SCSS)
3087
+ .foo {
3088
+ a: b; }
3089
+ CSS
3090
+ @keyframes identifier {
3091
+ 0% {
3092
+ @at-root (without: keyframes) {
3093
+ .foo {a: b}
3094
+ }
3095
+ }
3096
+ }
3097
+ SCSS
3098
+ end
3099
+
3100
+ def test_at_root_without_rule_in_keyframe_rule
3101
+ assert_equal <<CSS, render(<<SCSS)
3102
+ @keyframes identifier {
3103
+ 0% {
3104
+ a: b; } }
3105
+ CSS
3106
+ @keyframes identifier {
3107
+ 0% {
3108
+ @at-root (without: rule) {a: b}
3109
+ }
3110
+ }
3111
+ SCSS
3112
+ end
3113
+
3114
+ ## Selector Script
3115
+
3116
+ def test_selector_script
3117
+ assert_equal(<<CSS, render(<<SCSS))
3118
+ .foo .bar {
3119
+ content: ".foo .bar"; }
3120
+ CSS
3121
+ .foo .bar {
3122
+ content: "\#{&}";
3123
+ }
3124
+ SCSS
3125
+ end
3126
+
3127
+ def test_nested_selector_script
3128
+ assert_equal(<<CSS, render(<<SCSS))
3129
+ .foo .bar {
3130
+ content: ".foo .bar"; }
3131
+ CSS
3132
+ .foo {
3133
+ .bar {
3134
+ content: "\#{&}";
3135
+ }
3136
+ }
3137
+ SCSS
3138
+ end
3139
+
3140
+ def test_nested_selector_script_with_outer_comma_selector
3141
+ assert_equal(<<CSS, render(<<SCSS))
3142
+ .foo .baz, .bar .baz {
3143
+ content: ".foo .baz, .bar .baz"; }
3144
+ CSS
3145
+ .foo, .bar {
3146
+ .baz {
3147
+ content: "\#{&}";
3148
+ }
3149
+ }
3150
+ SCSS
3151
+ end
3152
+
3153
+ def test_nested_selector_script_with_inner_comma_selector
3154
+ assert_equal(<<CSS, render(<<SCSS))
3155
+ .foo .bar, .foo .baz {
3156
+ content: ".foo .bar, .foo .baz"; }
3157
+ CSS
3158
+ .foo {
3159
+ .bar, .baz {
3160
+ content: "\#{&}";
3161
+ }
3162
+ }
3163
+ SCSS
3164
+ end
3165
+
3166
+ def test_selector_script_through_mixin
3167
+ assert_equal(<<CSS, render(<<SCSS))
3168
+ .foo {
3169
+ content: ".foo"; }
3170
+ CSS
3171
+ @mixin mixin {
3172
+ content: "\#{&}";
3173
+ }
3174
+
3175
+ .foo {
3176
+ @include mixin;
3177
+ }
3178
+ SCSS
3179
+ end
3180
+
3181
+ def test_selector_script_through_content
3182
+ assert_equal(<<CSS, render(<<SCSS))
3183
+ .foo {
3184
+ content: ".foo"; }
3185
+ CSS
3186
+ @mixin mixin {
3187
+ @content;
3188
+ }
3189
+
3190
+ .foo {
3191
+ @include mixin {
3192
+ content: "\#{&}";
3193
+ }
3194
+ }
3195
+ SCSS
3196
+ end
3197
+
3198
+ def test_selector_script_through_function
3199
+ assert_equal(<<CSS, render(<<SCSS))
3200
+ .foo {
3201
+ content: ".foo"; }
3202
+ CSS
3203
+ @function fn() {
3204
+ @return "\#{&}";
3205
+ }
3206
+
3207
+ .foo {
3208
+ content: fn();
3209
+ }
3210
+ SCSS
3211
+ end
3212
+
3213
+ def test_selector_script_through_media
3214
+ assert_equal(<<CSS, render(<<SCSS))
3215
+ .foo {
3216
+ content: "outer"; }
3217
+ @media screen {
3218
+ .foo .bar {
3219
+ content: ".foo .bar"; } }
3220
+ CSS
3221
+ .foo {
3222
+ content: "outer";
3223
+ @media screen {
3224
+ .bar {
3225
+ content: "\#{&}";
3226
+ }
3227
+ }
3228
+ }
3229
+ SCSS
3230
+ end
3231
+
3232
+ def test_selector_script_save_and_reuse
3233
+ assert_equal(<<CSS, render(<<SCSS))
3234
+ .bar {
3235
+ content: ".foo"; }
3236
+ CSS
3237
+ $var: null;
3238
+ .foo {
3239
+ $var: & !global;
3240
+ }
3241
+
3242
+ .bar {
3243
+ content: "\#{$var}";
3244
+ }
3245
+ SCSS
3246
+ end
3247
+
3248
+ def test_selector_script_with_at_root
3249
+ assert_equal(<<CSS, render(<<SCSS))
3250
+ .foo-bar {
3251
+ a: b; }
3252
+ CSS
3253
+ .foo {
3254
+ @at-root \#{&}-bar {
3255
+ a: b;
3256
+ }
3257
+ }
3258
+ SCSS
3259
+ end
3260
+
3261
+ def test_multi_level_at_root_with_inner_selector_script
3262
+ assert_equal <<CSS, render(<<SCSS)
3263
+ .bar {
3264
+ a: b; }
3265
+ CSS
3266
+ .foo {
3267
+ @at-root .bar {
3268
+ @at-root \#{&} {
3269
+ a: b;
3270
+ }
3271
+ }
3272
+ }
3273
+ SCSS
3274
+ end
3275
+
3276
+ def test_at_root_with_at_root_through_mixin
3277
+ assert_equal(<<CSS, render(<<SCSS))
3278
+ .bar-baz {
3279
+ a: b; }
3280
+ CSS
3281
+ @mixin foo {
3282
+ .bar {
3283
+ @at-root \#{&}-baz {
3284
+ a: b;
3285
+ }
3286
+ }
3287
+ }
3288
+
3289
+ @include foo;
3290
+ SCSS
3291
+ end
3292
+
3293
+ # See https://github.com/sass/sass/issues/1294
3294
+ def test_extend_top_leveled_by_at_root
3295
+ render(<<SCSS)
3296
+ .span-10 {
3297
+ @at-root (without: all) {
3298
+ @extend %column;
3299
+ }
3300
+ }
3301
+ SCSS
3302
+
3303
+ assert(false, "Expected syntax error")
3304
+ rescue Sass::SyntaxError => e
3305
+ assert_equal "Extend directives may only be used within rules.", e.message
3306
+ assert_equal 3, e.sass_line
3307
+ end
3308
+
3309
+ def test_at_root_doesnt_always_break_blocks
3310
+ assert_equal <<CSS, render(<<SCSS)
3311
+ .foo {
3312
+ a: b; }
3313
+
3314
+ @media screen {
3315
+ .foo {
3316
+ c: d; }
3317
+ .bar {
3318
+ e: f; } }
3319
+ CSS
3320
+ %base {
3321
+ a: b;
3322
+ }
3323
+
3324
+ @media screen {
3325
+ .foo {
3326
+ c: d;
3327
+ @at-root (without: media) {
3328
+ @extend %base;
3329
+ }
3330
+ }
3331
+
3332
+ .bar {e: f}
3333
+ }
3334
+ SCSS
3335
+ end
3336
+
3337
+ ## Errors
3338
+
3339
+ def test_keyframes_rule_outside_of_keyframes
3340
+ render <<SCSS
3341
+ 0% {
3342
+ top: 0; }
3343
+ SCSS
3344
+ assert(false, "Expected syntax error")
3345
+ rescue Sass::SyntaxError => e
3346
+ assert_equal 'Invalid CSS after "": expected selector, was "0%"', e.message
3347
+ assert_equal 1, e.sass_line
3348
+ end
3349
+
3350
+ def test_selector_rule_in_keyframes
3351
+ render <<SCSS
3352
+ @keyframes identifier {
3353
+ .foo {
3354
+ top: 0; } }
3355
+ SCSS
3356
+ assert(false, "Expected syntax error")
3357
+ rescue Sass::SyntaxError => e
3358
+ assert_equal 'Invalid CSS after "": expected keyframes selector (e.g. 10%), was ".foo"', e.message
3359
+ assert_equal 2, e.sass_line
3360
+ end
3361
+
3362
+ def test_nested_mixin_def_is_scoped
3363
+ render <<SCSS
3364
+ foo {
3365
+ @mixin bar {a: b}}
3366
+ bar {@include bar}
3367
+ SCSS
3368
+ assert(false, "Expected syntax error")
3369
+ rescue Sass::SyntaxError => e
3370
+ assert_equal "Undefined mixin 'bar'.", e.message
3371
+ assert_equal 3, e.sass_line
3372
+ end
3373
+
3374
+ def test_rules_beneath_properties
3375
+ render <<SCSS
3376
+ foo {
3377
+ bar: {
3378
+ baz {
3379
+ bang: bop }}}
3380
+ SCSS
3381
+ assert(false, "Expected syntax error")
3382
+ rescue Sass::SyntaxError => e
3383
+ assert_equal 'Illegal nesting: Only properties may be nested beneath properties.', e.message
3384
+ assert_equal 3, e.sass_line
3385
+ end
3386
+
3387
+ def test_uses_property_exception_with_star_hack
3388
+ render <<SCSS
3389
+ foo {
3390
+ *bar:baz [fail]; }
3391
+ SCSS
3392
+ assert(false, "Expected syntax error")
3393
+ rescue Sass::SyntaxError => e
3394
+ assert_equal 'Invalid CSS after " *bar:baz ": expected ";", was "[fail]; }"', e.message
3395
+ assert_equal 2, e.sass_line
3396
+ end
3397
+
3398
+ def test_uses_property_exception_with_colon_hack
3399
+ render <<SCSS
3400
+ foo {
3401
+ :bar:baz [fail]; }
3402
+ SCSS
3403
+ assert(false, "Expected syntax error")
3404
+ rescue Sass::SyntaxError => e
3405
+ assert_equal 'Invalid CSS after " :bar:baz ": expected ";", was "[fail]; }"', e.message
3406
+ assert_equal 2, e.sass_line
3407
+ end
3408
+
3409
+ def test_uses_rule_exception_with_dot_hack
3410
+ render <<SCSS
3411
+ foo {
3412
+ .bar:baz <fail>; }
3413
+ SCSS
3414
+ assert(false, "Expected syntax error")
3415
+ rescue Sass::SyntaxError => e
3416
+ assert_equal 'Invalid CSS after " .bar:baz <fail>": expected expression (e.g. 1px, bold), was "; }"', e.message
3417
+ assert_equal 2, e.sass_line
3418
+ end
3419
+
3420
+ def test_uses_property_exception_with_space_after_name
3421
+ render <<SCSS
3422
+ foo {
3423
+ bar: baz [fail]; }
3424
+ SCSS
3425
+ assert(false, "Expected syntax error")
3426
+ rescue Sass::SyntaxError => e
3427
+ assert_equal 'Invalid CSS after " bar: baz ": expected ";", was "[fail]; }"', e.message
3428
+ assert_equal 2, e.sass_line
3429
+ end
3430
+
3431
+ def test_uses_property_exception_with_non_identifier_after_name
3432
+ render <<SCSS
3433
+ foo {
3434
+ bar:1px [fail]; }
3435
+ SCSS
3436
+ assert(false, "Expected syntax error")
3437
+ rescue Sass::SyntaxError => e
3438
+ assert_equal 'Invalid CSS after " bar:1px ": expected ";", was "[fail]; }"', e.message
3439
+ assert_equal 2, e.sass_line
3440
+ end
3441
+
3442
+ def test_uses_property_exception_when_followed_by_open_bracket
3443
+ render <<SCSS
3444
+ foo {
3445
+ bar:{baz: .fail} }
3446
+ SCSS
3447
+ assert(false, "Expected syntax error")
3448
+ rescue Sass::SyntaxError => e
3449
+ assert_equal 'Invalid CSS after " bar:{baz: ": expected expression (e.g. 1px, bold), was ".fail} }"', e.message
3450
+ assert_equal 2, e.sass_line
3451
+ end
3452
+
3453
+ def test_script_error
3454
+ render <<SCSS
3455
+ foo {
3456
+ bar: "baz" * * }
3457
+ SCSS
3458
+ assert(false, "Expected syntax error")
3459
+ rescue Sass::SyntaxError => e
3460
+ assert_equal 'Invalid CSS after " bar: "baz" * ": expected expression (e.g. 1px, bold), was "* }"', e.message
3461
+ assert_equal 2, e.sass_line
3462
+ end
3463
+
3464
+ def test_multiline_script_syntax_error
3465
+ render <<SCSS
3466
+ foo {
3467
+ bar:
3468
+ "baz" * * }
3469
+ SCSS
3470
+ assert(false, "Expected syntax error")
3471
+ rescue Sass::SyntaxError => e
3472
+ assert_equal 'Invalid CSS after " "baz" * ": expected expression (e.g. 1px, bold), was "* }"', e.message
3473
+ assert_equal 3, e.sass_line
3474
+ end
3475
+
3476
+ def test_multiline_script_runtime_error
3477
+ render <<SCSS
3478
+ foo {
3479
+ bar: "baz" +
3480
+ "bar" +
3481
+ $bang }
3482
+ SCSS
3483
+ assert(false, "Expected syntax error")
3484
+ rescue Sass::SyntaxError => e
3485
+ assert_equal "Undefined variable: \"$bang\".", e.message
3486
+ assert_equal 4, e.sass_line
3487
+ end
3488
+
3489
+ def test_post_multiline_script_runtime_error
3490
+ render <<SCSS
3491
+ foo {
3492
+ bar: "baz" +
3493
+ "bar" +
3494
+ "baz";
3495
+ bip: $bop; }
3496
+ SCSS
3497
+ assert(false, "Expected syntax error")
3498
+ rescue Sass::SyntaxError => e
3499
+ assert_equal "Undefined variable: \"$bop\".", e.message
3500
+ assert_equal 5, e.sass_line
3501
+ end
3502
+
3503
+ def test_multiline_property_runtime_error
3504
+ render <<SCSS
3505
+ foo {
3506
+ bar: baz
3507
+ bar
3508
+ \#{$bang} }
3509
+ SCSS
3510
+ assert(false, "Expected syntax error")
3511
+ rescue Sass::SyntaxError => e
3512
+ assert_equal "Undefined variable: \"$bang\".", e.message
3513
+ assert_equal 4, e.sass_line
3514
+ end
3515
+
3516
+ def test_post_resolution_selector_error
3517
+ render "\n\nfoo \#{\") bar\"} {a: b}"
3518
+ assert(false, "Expected syntax error")
3519
+ rescue Sass::SyntaxError => e
3520
+ assert_equal 'Invalid CSS after "foo ": expected selector, was ") bar"', e.message
3521
+ assert_equal 3, e.sass_line
3522
+ end
3523
+
3524
+ def test_parent_in_mid_selector_error
3525
+ assert_raise_message(Sass::SyntaxError, <<MESSAGE.rstrip) {render <<SCSS}
3526
+ Invalid CSS after ".foo": expected "{", was "&.bar"
3527
+
3528
+ "&.bar" may only be used at the beginning of a compound selector.
3529
+ MESSAGE
3530
+ flim {
3531
+ .foo&.bar {a: b}
3532
+ }
3533
+ SCSS
3534
+ end
3535
+
3536
+ def test_parent_after_selector_error
3537
+ assert_raise_message(Sass::SyntaxError, <<MESSAGE.rstrip) {render <<SCSS}
3538
+ Invalid CSS after ".foo.bar": expected "{", was "&"
3539
+
3540
+ "&" may only be used at the beginning of a compound selector.
3541
+ MESSAGE
3542
+ flim {
3543
+ .foo.bar& {a: b}
3544
+ }
3545
+ SCSS
3546
+ end
3547
+
3548
+ def test_double_parent_selector_error
3549
+ assert_raise_message(Sass::SyntaxError, <<MESSAGE.rstrip) {render <<SCSS}
3550
+ Invalid CSS after "&": expected "{", was "&"
3551
+
3552
+ "&" may only be used at the beginning of a compound selector.
3553
+ MESSAGE
3554
+ flim {
3555
+ && {a: b}
3556
+ }
3557
+ SCSS
3558
+ end
3559
+
3560
+ def test_no_lonely_else
3561
+ assert_raise_message(Sass::SyntaxError, <<MESSAGE.rstrip) {render <<SCSS}
3562
+ Invalid CSS: @else must come after @if
3563
+ MESSAGE
3564
+ @else {foo: bar}
3565
+ SCSS
3566
+ end
3567
+
3568
+ def test_failed_parent_selector_with_suffix
3569
+ assert_raise_message(Sass::SyntaxError, <<MESSAGE.rstrip) {render(<<SCSS)}
3570
+ Invalid parent selector for "&-bar": "*"
3571
+ MESSAGE
3572
+ * {
3573
+ &-bar {a: b}
3574
+ }
3575
+ SCSS
3576
+
3577
+ assert_raise_message(Sass::SyntaxError, <<MESSAGE.rstrip) {render(<<SCSS)}
3578
+ Invalid parent selector for "&-bar": "[foo=bar]"
3579
+ MESSAGE
3580
+ [foo=bar] {
3581
+ &-bar {a: b}
3582
+ }
3583
+ SCSS
3584
+
3585
+ assert_raise_message(Sass::SyntaxError, <<MESSAGE.rstrip) {render(<<SCSS)}
3586
+ Invalid parent selector for "&-bar": "::nth-child(2n+1)"
3587
+ MESSAGE
3588
+ ::nth-child(2n+1) {
3589
+ &-bar {a: b}
3590
+ }
3591
+ SCSS
3592
+
3593
+ assert_raise_message(Sass::SyntaxError, <<MESSAGE.rstrip) {render(<<SCSS)}
3594
+ Invalid parent selector for "&-bar": ":not(.foo)"
3595
+ MESSAGE
3596
+ :not(.foo) {
3597
+ &-bar {a: b}
3598
+ }
3599
+ SCSS
3600
+
3601
+ assert_raise_message(Sass::SyntaxError, <<MESSAGE.rstrip) {render(<<SCSS)}
3602
+ Invalid parent selector for "&-bar": ".foo +"
3603
+ MESSAGE
3604
+ .foo + {
3605
+ &-bar {a: b}
3606
+ }
3607
+ SCSS
3608
+ end
3609
+
3610
+ def test_empty_media_query_error
3611
+ assert_raise_message(Sass::SyntaxError, <<MESSAGE.rstrip) {render(<<SCSS)}
3612
+ Invalid CSS after "": expected media query list, was ""
3613
+ MESSAGE
3614
+ @media \#{""} {
3615
+ foo {a: b}
3616
+ }
3617
+ SCSS
3618
+ end
3619
+
3620
+ def test_newline_in_property_value
3621
+ assert_equal(<<CSS, render(<<SCSS))
3622
+ .foo {
3623
+ bar: "bazbang"; }
3624
+ CSS
3625
+ .foo {
3626
+ $var: "baz\\
3627
+ bang";
3628
+ bar: $var;
3629
+ }
3630
+ SCSS
3631
+ end
3632
+
3633
+ def test_raw_newline_warning
3634
+ assert_warning(<<MESSAGE.rstrip) {assert_equal(<<CSS, render(<<SCSS))}
3635
+ DEPRECATION WARNING on line 2, column 9 of #{filename_for_test :scss}:
3636
+ Unescaped multiline strings are deprecated and will be removed in a future version of Sass.
3637
+ To include a newline in a string, use "\\a" or "\\a " as in CSS.
3638
+ MESSAGE
3639
+ .foo {
3640
+ bar: "baz\\a bang"; }
3641
+ CSS
3642
+ .foo {
3643
+ $var: "baz
3644
+ bang";
3645
+ bar: $var;
3646
+ }
3647
+ SCSS
3648
+ end
3649
+
3650
+ # Regression
3651
+
3652
+ def test_escape_in_selector
3653
+ assert_equal(<<CSS, render(".\\!foo {a: b}"))
3654
+ .\\!foo {
3655
+ a: b; }
3656
+ CSS
3657
+ end
3658
+
3659
+ def test_for_directive_with_float_bounds
3660
+ assert_equal(<<CSS, render(<<SCSS))
3661
+ .a {
3662
+ b: 0;
3663
+ b: 1;
3664
+ b: 2;
3665
+ b: 3;
3666
+ b: 4;
3667
+ b: 5; }
3668
+ CSS
3669
+ .a {
3670
+ @for $i from 0.0 through 5.0 {b: $i}
3671
+ }
3672
+ SCSS
3673
+
3674
+ assert_raise_message(Sass::SyntaxError, "0.5 is not an integer.") {render(<<SCSS)}
3675
+ .a {
3676
+ @for $i from 0.5 through 5.0 {b: $i}
3677
+ }
3678
+ SCSS
3679
+
3680
+ assert_raise_message(Sass::SyntaxError, "5.5 is not an integer.") {render(<<SCSS)}
3681
+ .a {
3682
+ @for $i from 0.0 through 5.5 {b: $i}
3683
+ }
3684
+ SCSS
3685
+ end
3686
+
3687
+ def test_parent_selector_in_function_pseudo_selector
3688
+ assert_equal <<CSS, render(<<SCSS)
3689
+ .bar:not(.foo) {
3690
+ a: b; }
3691
+
3692
+ .qux:nth-child(2n of .baz .bang) {
3693
+ c: d; }
3694
+ CSS
3695
+ .foo {
3696
+ .bar:not(&) {a: b}
3697
+ }
3698
+
3699
+ .baz .bang {
3700
+ .qux:nth-child(2n of &) {c: d}
3701
+ }
3702
+ SCSS
3703
+ end
3704
+
3705
+ def test_parent_selector_in_and_out_of_function_pseudo_selector
3706
+ # Regression test for https://github.com/sass/sass/issues/1464#issuecomment-70352288
3707
+ assert_equal(<<CSS, render(<<SCSS))
3708
+ .a:not(.a-b) {
3709
+ x: y; }
3710
+ CSS
3711
+ .a {
3712
+ &:not(&-b) {
3713
+ x: y;
3714
+ }
3715
+ }
3716
+ SCSS
3717
+
3718
+ assert_equal(<<CSS, render(<<SCSS))
3719
+ .a:nth-child(2n of .a-b) {
3720
+ x: y; }
3721
+ CSS
3722
+ .a {
3723
+ &:nth-child(2n of &-b) {
3724
+ x: y;
3725
+ }
3726
+ }
3727
+ SCSS
3728
+ end
3729
+
3730
+ def test_attribute_selector_in_selector_pseudoclass
3731
+ # Even though this is plain CSS, it only failed when given to the SCSS
3732
+ # parser.
3733
+ assert_equal(<<CSS, render(<<SCSS))
3734
+ [href^='http://'] {
3735
+ color: red; }
3736
+ CSS
3737
+ [href^='http://'] {
3738
+ color: red;
3739
+ }
3740
+ SCSS
3741
+ end
3742
+
3743
+ def test_top_level_unknown_directive_in_at_root
3744
+ assert_equal(<<CSS, render(<<SCSS))
3745
+ @fblthp {
3746
+ a: b; }
3747
+ CSS
3748
+ @at-root {
3749
+ @fblthp {a: b}
3750
+ }
3751
+ SCSS
3752
+ end
3753
+
3754
+ def test_parent_ref_with_newline
3755
+ assert_equal(<<CSS, render(<<SCSS))
3756
+ a.c
3757
+ , b.c {
3758
+ x: y; }
3759
+ CSS
3760
+ a
3761
+ , b {&.c {x: y}}
3762
+ SCSS
3763
+ end
3764
+
3765
+ def test_parent_ref_in_nested_at_root
3766
+ assert_equal(<<CSS, render(<<SCSS))
3767
+ #test {
3768
+ border: 0; }
3769
+ #test:hover {
3770
+ display: none; }
3771
+ CSS
3772
+ a {
3773
+ @at-root #test {
3774
+ border: 0;
3775
+ &:hover{
3776
+ display: none;
3777
+ }
3778
+ }
3779
+ }
3780
+ SCSS
3781
+ end
3782
+
3783
+ def test_loud_comment_in_compressed_mode
3784
+ assert_equal(<<CSS, render(<<SCSS))
3785
+ /*! foo */
3786
+ CSS
3787
+ /*! foo */
3788
+ SCSS
3789
+ end
3790
+
3791
+ def test_parsing_decimals_followed_by_comments_doesnt_take_forever
3792
+ assert_equal(<<CSS, render(<<SCSS))
3793
+ .foo {
3794
+ padding: 4.21053% 4.21053% 5.63158%; }
3795
+ CSS
3796
+ .foo {
3797
+ padding: 4.21052631578947% 4.21052631578947% 5.631578947368421% /**/
3798
+ }
3799
+ SCSS
3800
+ end
3801
+
3802
+ def test_parsing_many_numbers_doesnt_take_forever
3803
+ values = ["80% 90%"] * 1000
3804
+ assert_equal(<<CSS, render(<<SCSS))
3805
+ .foo {
3806
+ padding: #{values.join(', ')}; }
3807
+ CSS
3808
+ .foo {
3809
+ padding: #{values.join(', ')};
3810
+ }
3811
+ SCSS
3812
+ end
3813
+
3814
+ def test_import_comments_in_imports
3815
+ assert_equal(<<CSS, render(<<SCSS))
3816
+ @import url(foo.css);
3817
+ @import url(bar.css);
3818
+ @import url(baz.css);
3819
+ CSS
3820
+ @import "foo.css", // this is a comment
3821
+ "bar.css", /* this is another comment */
3822
+ "baz.css"; // this is a third comment
3823
+ SCSS
3824
+ end
3825
+
3826
+ def test_reference_combinator_with_parent_ref
3827
+ assert_equal <<CSS, render(<<SCSS)
3828
+ a /foo/ b {
3829
+ c: d; }
3830
+ CSS
3831
+ a {& /foo/ b {c: d}}
3832
+ SCSS
3833
+ end
3834
+
3835
+ def test_newline_selector_rendered_multiple_times
3836
+ assert_equal <<CSS, render(<<SCSS)
3837
+ form input,
3838
+ form select {
3839
+ color: white; }
3840
+
3841
+ form input,
3842
+ form select {
3843
+ color: white; }
3844
+ CSS
3845
+ @for $i from 1 through 2 {
3846
+ form {
3847
+ input,
3848
+ select {
3849
+ color: white;
3850
+ }
3851
+ }
3852
+ }
3853
+ SCSS
3854
+ end
3855
+
3856
+ def test_prop_name_interpolation_after_hyphen
3857
+ assert_equal <<CSS, render(<<SCSS)
3858
+ a {
3859
+ -foo-bar: b; }
3860
+ CSS
3861
+ a { -\#{"foo"}-bar: b; }
3862
+ SCSS
3863
+ end
3864
+
3865
+ def test_star_plus_and_parent
3866
+ assert_equal <<CSS, render(<<SCSS)
3867
+ * + html foo {
3868
+ a: b; }
3869
+ CSS
3870
+ foo {*+html & {a: b}}
3871
+ SCSS
3872
+ end
3873
+
3874
+ def test_weird_added_space
3875
+ assert_equal <<CSS, render(<<SCSS)
3876
+ foo {
3877
+ bar: -moz-bip; }
3878
+ CSS
3879
+ $value : bip;
3880
+
3881
+ foo {
3882
+ bar: -moz-\#{$value};
3883
+ }
3884
+ SCSS
3885
+ end
3886
+
3887
+ def test_interpolation_with_bracket_on_next_line
3888
+ assert_equal <<CSS, render(<<SCSS)
3889
+ a.foo b {
3890
+ color: red; }
3891
+ CSS
3892
+ a.\#{"foo"} b
3893
+ {color: red}
3894
+ SCSS
3895
+ end
3896
+
3897
+ def test_extra_comma_in_mixin_arglist_error
3898
+ assert_raise_message(Sass::SyntaxError, <<MESSAGE.rstrip) {render <<SCSS}
3899
+ Invalid CSS after "...clude foo(bar, ": expected mixin argument, was ");"
3900
+ MESSAGE
3901
+ @mixin foo($a1, $a2) {
3902
+ baz: $a1 $a2;
3903
+ }
3904
+
3905
+ .bar {
3906
+ @include foo(bar, );
3907
+ }
3908
+ SCSS
3909
+ end
3910
+
3911
+ def test_interpolation
3912
+ assert_equal <<CSS, render(<<SCSS)
3913
+ ul li#foo a span.label {
3914
+ foo: bar; }
3915
+ CSS
3916
+ $bar : "#foo";
3917
+ ul li\#{$bar} a span.label { foo: bar; }
3918
+ SCSS
3919
+ end
3920
+
3921
+ def test_mixin_with_keyword_args
3922
+ assert_equal <<CSS, render(<<SCSS)
3923
+ .mixed {
3924
+ required: foo;
3925
+ arg1: default-val1;
3926
+ arg2: non-default-val2; }
3927
+ CSS
3928
+ @mixin a-mixin($required, $arg1: default-val1, $arg2: default-val2) {
3929
+ required: $required;
3930
+ arg1: $arg1;
3931
+ arg2: $arg2;
3932
+ }
3933
+ .mixed { @include a-mixin(foo, $arg2: non-default-val2); }
3934
+ SCSS
3935
+ end
3936
+
3937
+ def test_keyword_arg_scope
3938
+ assert_equal <<CSS, render(<<SCSS)
3939
+ .mixed {
3940
+ arg1: default;
3941
+ arg2: non-default; }
3942
+ CSS
3943
+ $arg1: default;
3944
+ $arg2: default;
3945
+ @mixin a-mixin($arg1: $arg1, $arg2: $arg2) {
3946
+ arg1: $arg1;
3947
+ arg2: $arg2;
3948
+ }
3949
+ .mixed { @include a-mixin($arg2: non-default); }
3950
+ SCSS
3951
+ end
3952
+
3953
+ def test_passing_required_args_as_a_keyword_arg
3954
+ assert_equal <<CSS, render(<<SCSS)
3955
+ .mixed {
3956
+ required: foo;
3957
+ arg1: default-val1;
3958
+ arg2: default-val2; }
3959
+ CSS
3960
+ @mixin a-mixin($required, $arg1: default-val1, $arg2: default-val2) {
3961
+ required: $required;
3962
+ arg1: $arg1;
3963
+ arg2: $arg2; }
3964
+ .mixed { @include a-mixin($required: foo); }
3965
+ SCSS
3966
+ end
3967
+
3968
+ def test_passing_all_as_keyword_args_in_opposite_order
3969
+ assert_equal <<CSS, render(<<SCSS)
3970
+ .mixed {
3971
+ required: foo;
3972
+ arg1: non-default-val1;
3973
+ arg2: non-default-val2; }
3974
+ CSS
3975
+ @mixin a-mixin($required, $arg1: default-val1, $arg2: default-val2) {
3976
+ required: $required;
3977
+ arg1: $arg1;
3978
+ arg2: $arg2; }
3979
+ .mixed { @include a-mixin($arg2: non-default-val2, $arg1: non-default-val1, $required: foo); }
3980
+ SCSS
3981
+ end
3982
+
3983
+ def test_keyword_args_in_functions
3984
+ assert_equal <<CSS, render(<<SCSS)
3985
+ .keyed {
3986
+ color: rgba(170, 119, 204, 0.4); }
3987
+ CSS
3988
+ .keyed { color: rgba($color: #a7c, $alpha: 0.4) }
3989
+ SCSS
3990
+ end
3991
+
3992
+ def test_unknown_keyword_arg_raises_error
3993
+ assert_raise_message(Sass::SyntaxError, "Mixin a doesn't have an argument named $c.") {render <<SCSS}
3994
+ @mixin a($b: 1) { a: $b; }
3995
+ div { @include a(1, $c: 3); }
3996
+ SCSS
3997
+ end
3998
+
3999
+
4000
+ def test_newlines_removed_from_selectors_when_compressed
4001
+ assert_equal <<CSS, render(<<SCSS, :style => :compressed)
4002
+ z a,z b{display:block}
4003
+ CSS
4004
+ a
4005
+ , b {
4006
+ z & {
4007
+ display: block;
4008
+ }
4009
+ }
4010
+ SCSS
4011
+ end
4012
+
4013
+ def test_if_error_line
4014
+ assert_raise_line(2) {render(<<SCSS)}
4015
+ @if true {foo: bar}
4016
+ }
4017
+ SCSS
4018
+ end
4019
+
4020
+ def test_multiline_var
4021
+ assert_equal <<CSS, render(<<SCSS)
4022
+ foo {
4023
+ a: 3;
4024
+ b: false;
4025
+ c: a b c; }
4026
+ CSS
4027
+ foo {
4028
+ $var1: 1 +
4029
+ 2;
4030
+ $var2: true and
4031
+ false;
4032
+ $var3: a b
4033
+ c;
4034
+ a: $var1;
4035
+ b: $var2;
4036
+ c: $var3; }
4037
+ SCSS
4038
+ end
4039
+
4040
+ def test_mixin_content
4041
+ assert_equal <<CSS, render(<<SASS)
4042
+ .parent {
4043
+ background-color: red;
4044
+ border-color: red; }
4045
+ .parent .child {
4046
+ background-color: yellow;
4047
+ color: blue;
4048
+ border-color: yellow; }
4049
+ CSS
4050
+ $color: blue;
4051
+ @mixin context($class, $color: red) {
4052
+ .\#{$class} {
4053
+ background-color: $color;
4054
+ @content;
4055
+ border-color: $color;
4056
+ }
4057
+ }
4058
+ @include context(parent) {
4059
+ @include context(child, $color: yellow) {
4060
+ color: $color;
4061
+ }
4062
+ }
4063
+ SASS
4064
+ end
4065
+
4066
+ def test_empty_content
4067
+ assert_equal <<CSS, render(<<SCSS)
4068
+ a {
4069
+ b: c; }
4070
+ CSS
4071
+ @mixin foo { @content }
4072
+ a { b: c; @include foo {} }
4073
+ SCSS
4074
+ end
4075
+
4076
+ def test_options_passed_to_script
4077
+ assert_equal <<CSS, render(<<SCSS, :style => :compressed)
4078
+ foo{color:#000}
4079
+ CSS
4080
+ foo {color: darken(black, 10%)}
4081
+ SCSS
4082
+ end
4083
+
4084
+ # ref: https://github.com/nex3/sass/issues/104
4085
+ def test_no_buffer_overflow
4086
+ template = render <<SCSS
4087
+ .aaa {
4088
+ background-color: white;
4089
+ }
4090
+ .aaa .aaa .aaa {
4091
+ background-color: black;
4092
+ }
4093
+ .bbb {
4094
+ @extend .aaa;
4095
+ }
4096
+ .xxx {
4097
+ @extend .bbb;
4098
+ }
4099
+ .yyy {
4100
+ @extend .bbb;
4101
+ }
4102
+ .zzz {
4103
+ @extend .bbb;
4104
+ }
4105
+ SCSS
4106
+ Sass::SCSS::Parser.new(template, "test.scss", nil).parse
4107
+ end
4108
+
4109
+ def test_extend_in_media_in_rule
4110
+ assert_equal(<<CSS, render(<<SCSS))
4111
+ @media screen {
4112
+ .foo {
4113
+ a: b; } }
4114
+ CSS
4115
+ .foo {
4116
+ @media screen {
4117
+ @extend %bar;
4118
+ }
4119
+ }
4120
+
4121
+ @media screen {
4122
+ %bar {
4123
+ a: b;
4124
+ }
4125
+ }
4126
+ SCSS
4127
+ end
4128
+
4129
+ def test_import_with_supports_clause_interp
4130
+ assert_equal(<<CSS, render(<<'SASS', :style => :compressed))
4131
+ @import url("fallback-layout.css") supports(not (display: flex))
4132
+ CSS
4133
+ $display-type: flex;
4134
+ @import url("fallback-layout.css") supports(not (display: #{$display-type}));
4135
+ SASS
4136
+ end
4137
+
4138
+ def test_import_with_supports_clause
4139
+ assert_equal(<<CSS, render(<<SASS, :style => :compressed))
4140
+ @import url("fallback-layout.css") supports(not (display: flex));.foo{bar:baz}
4141
+ CSS
4142
+ @import url("fallback-layout.css") supports(not (display: flex));
4143
+ .foo { bar: baz; }
4144
+ SASS
4145
+ end
4146
+
4147
+ end