sass 3.7.4 → 4.0.0.alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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