oreorenasass 3.4.0

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 (268) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +11 -0
  3. data/CONTRIBUTING +3 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +221 -0
  6. data/Rakefile +370 -0
  7. data/VERSION +1 -0
  8. data/VERSION_NAME +1 -0
  9. data/bin/sass +13 -0
  10. data/bin/sass-convert +12 -0
  11. data/bin/scss +13 -0
  12. data/extra/update_watch.rb +13 -0
  13. data/init.rb +18 -0
  14. data/lib/sass/cache_stores/base.rb +88 -0
  15. data/lib/sass/cache_stores/chain.rb +34 -0
  16. data/lib/sass/cache_stores/filesystem.rb +60 -0
  17. data/lib/sass/cache_stores/memory.rb +47 -0
  18. data/lib/sass/cache_stores/null.rb +25 -0
  19. data/lib/sass/cache_stores.rb +15 -0
  20. data/lib/sass/callbacks.rb +67 -0
  21. data/lib/sass/css.rb +407 -0
  22. data/lib/sass/engine.rb +1181 -0
  23. data/lib/sass/environment.rb +191 -0
  24. data/lib/sass/error.rb +198 -0
  25. data/lib/sass/exec/base.rb +187 -0
  26. data/lib/sass/exec/sass_convert.rb +264 -0
  27. data/lib/sass/exec/sass_scss.rb +424 -0
  28. data/lib/sass/exec.rb +9 -0
  29. data/lib/sass/features.rb +47 -0
  30. data/lib/sass/importers/base.rb +182 -0
  31. data/lib/sass/importers/filesystem.rb +211 -0
  32. data/lib/sass/importers.rb +22 -0
  33. data/lib/sass/logger/base.rb +30 -0
  34. data/lib/sass/logger/log_level.rb +45 -0
  35. data/lib/sass/logger.rb +12 -0
  36. data/lib/sass/media.rb +210 -0
  37. data/lib/sass/plugin/compiler.rb +565 -0
  38. data/lib/sass/plugin/configuration.rb +118 -0
  39. data/lib/sass/plugin/generic.rb +15 -0
  40. data/lib/sass/plugin/merb.rb +48 -0
  41. data/lib/sass/plugin/rack.rb +60 -0
  42. data/lib/sass/plugin/rails.rb +47 -0
  43. data/lib/sass/plugin/staleness_checker.rb +199 -0
  44. data/lib/sass/plugin.rb +133 -0
  45. data/lib/sass/railtie.rb +10 -0
  46. data/lib/sass/repl.rb +57 -0
  47. data/lib/sass/root.rb +7 -0
  48. data/lib/sass/script/css_lexer.rb +33 -0
  49. data/lib/sass/script/css_parser.rb +34 -0
  50. data/lib/sass/script/functions.rb +2626 -0
  51. data/lib/sass/script/lexer.rb +449 -0
  52. data/lib/sass/script/parser.rb +637 -0
  53. data/lib/sass/script/tree/funcall.rb +306 -0
  54. data/lib/sass/script/tree/interpolation.rb +118 -0
  55. data/lib/sass/script/tree/list_literal.rb +77 -0
  56. data/lib/sass/script/tree/literal.rb +45 -0
  57. data/lib/sass/script/tree/map_literal.rb +64 -0
  58. data/lib/sass/script/tree/node.rb +109 -0
  59. data/lib/sass/script/tree/operation.rb +103 -0
  60. data/lib/sass/script/tree/selector.rb +26 -0
  61. data/lib/sass/script/tree/string_interpolation.rb +104 -0
  62. data/lib/sass/script/tree/unary_operation.rb +69 -0
  63. data/lib/sass/script/tree/variable.rb +57 -0
  64. data/lib/sass/script/tree.rb +16 -0
  65. data/lib/sass/script/value/arg_list.rb +36 -0
  66. data/lib/sass/script/value/base.rb +240 -0
  67. data/lib/sass/script/value/bool.rb +35 -0
  68. data/lib/sass/script/value/color.rb +680 -0
  69. data/lib/sass/script/value/helpers.rb +262 -0
  70. data/lib/sass/script/value/list.rb +113 -0
  71. data/lib/sass/script/value/map.rb +70 -0
  72. data/lib/sass/script/value/null.rb +44 -0
  73. data/lib/sass/script/value/number.rb +530 -0
  74. data/lib/sass/script/value/string.rb +97 -0
  75. data/lib/sass/script/value.rb +11 -0
  76. data/lib/sass/script.rb +66 -0
  77. data/lib/sass/scss/css_parser.rb +42 -0
  78. data/lib/sass/scss/parser.rb +1209 -0
  79. data/lib/sass/scss/rx.rb +141 -0
  80. data/lib/sass/scss/script_lexer.rb +15 -0
  81. data/lib/sass/scss/script_parser.rb +25 -0
  82. data/lib/sass/scss/static_parser.rb +368 -0
  83. data/lib/sass/scss.rb +16 -0
  84. data/lib/sass/selector/abstract_sequence.rb +109 -0
  85. data/lib/sass/selector/comma_sequence.rb +175 -0
  86. data/lib/sass/selector/pseudo.rb +256 -0
  87. data/lib/sass/selector/sequence.rb +600 -0
  88. data/lib/sass/selector/simple.rb +117 -0
  89. data/lib/sass/selector/simple_sequence.rb +325 -0
  90. data/lib/sass/selector.rb +326 -0
  91. data/lib/sass/shared.rb +76 -0
  92. data/lib/sass/source/map.rb +210 -0
  93. data/lib/sass/source/position.rb +39 -0
  94. data/lib/sass/source/range.rb +41 -0
  95. data/lib/sass/stack.rb +120 -0
  96. data/lib/sass/supports.rb +227 -0
  97. data/lib/sass/tree/at_root_node.rb +83 -0
  98. data/lib/sass/tree/charset_node.rb +22 -0
  99. data/lib/sass/tree/comment_node.rb +82 -0
  100. data/lib/sass/tree/content_node.rb +9 -0
  101. data/lib/sass/tree/css_import_node.rb +60 -0
  102. data/lib/sass/tree/debug_node.rb +18 -0
  103. data/lib/sass/tree/directive_node.rb +59 -0
  104. data/lib/sass/tree/each_node.rb +24 -0
  105. data/lib/sass/tree/error_node.rb +18 -0
  106. data/lib/sass/tree/extend_node.rb +43 -0
  107. data/lib/sass/tree/for_node.rb +36 -0
  108. data/lib/sass/tree/function_node.rb +39 -0
  109. data/lib/sass/tree/if_node.rb +52 -0
  110. data/lib/sass/tree/import_node.rb +74 -0
  111. data/lib/sass/tree/keyframe_rule_node.rb +15 -0
  112. data/lib/sass/tree/media_node.rb +48 -0
  113. data/lib/sass/tree/mixin_def_node.rb +38 -0
  114. data/lib/sass/tree/mixin_node.rb +52 -0
  115. data/lib/sass/tree/node.rb +238 -0
  116. data/lib/sass/tree/prop_node.rb +171 -0
  117. data/lib/sass/tree/return_node.rb +19 -0
  118. data/lib/sass/tree/root_node.rb +44 -0
  119. data/lib/sass/tree/rule_node.rb +145 -0
  120. data/lib/sass/tree/supports_node.rb +38 -0
  121. data/lib/sass/tree/trace_node.rb +33 -0
  122. data/lib/sass/tree/variable_node.rb +36 -0
  123. data/lib/sass/tree/visitors/base.rb +72 -0
  124. data/lib/sass/tree/visitors/check_nesting.rb +177 -0
  125. data/lib/sass/tree/visitors/convert.rb +334 -0
  126. data/lib/sass/tree/visitors/cssize.rb +369 -0
  127. data/lib/sass/tree/visitors/deep_copy.rb +107 -0
  128. data/lib/sass/tree/visitors/extend.rb +68 -0
  129. data/lib/sass/tree/visitors/perform.rb +539 -0
  130. data/lib/sass/tree/visitors/set_options.rb +139 -0
  131. data/lib/sass/tree/visitors/to_css.rb +381 -0
  132. data/lib/sass/tree/warn_node.rb +18 -0
  133. data/lib/sass/tree/while_node.rb +18 -0
  134. data/lib/sass/util/cross_platform_random.rb +19 -0
  135. data/lib/sass/util/multibyte_string_scanner.rb +157 -0
  136. data/lib/sass/util/normalized_map.rb +130 -0
  137. data/lib/sass/util/ordered_hash.rb +192 -0
  138. data/lib/sass/util/subset_map.rb +110 -0
  139. data/lib/sass/util/test.rb +9 -0
  140. data/lib/sass/util.rb +1318 -0
  141. data/lib/sass/version.rb +124 -0
  142. data/lib/sass.rb +102 -0
  143. data/rails/init.rb +1 -0
  144. data/test/sass/cache_test.rb +131 -0
  145. data/test/sass/callbacks_test.rb +61 -0
  146. data/test/sass/compiler_test.rb +232 -0
  147. data/test/sass/conversion_test.rb +2054 -0
  148. data/test/sass/css2sass_test.rb +477 -0
  149. data/test/sass/data/hsl-rgb.txt +319 -0
  150. data/test/sass/encoding_test.rb +219 -0
  151. data/test/sass/engine_test.rb +3301 -0
  152. data/test/sass/exec_test.rb +86 -0
  153. data/test/sass/extend_test.rb +1661 -0
  154. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  155. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  156. data/test/sass/functions_test.rb +1926 -0
  157. data/test/sass/importer_test.rb +412 -0
  158. data/test/sass/logger_test.rb +58 -0
  159. data/test/sass/mock_importer.rb +49 -0
  160. data/test/sass/more_results/more1.css +9 -0
  161. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  162. data/test/sass/more_results/more_import.css +29 -0
  163. data/test/sass/more_templates/_more_partial.sass +2 -0
  164. data/test/sass/more_templates/more1.sass +23 -0
  165. data/test/sass/more_templates/more_import.sass +11 -0
  166. data/test/sass/plugin_test.rb +554 -0
  167. data/test/sass/results/alt.css +4 -0
  168. data/test/sass/results/basic.css +9 -0
  169. data/test/sass/results/cached_import_option.css +3 -0
  170. data/test/sass/results/compact.css +5 -0
  171. data/test/sass/results/complex.css +86 -0
  172. data/test/sass/results/compressed.css +1 -0
  173. data/test/sass/results/expanded.css +19 -0
  174. data/test/sass/results/filename_fn.css +3 -0
  175. data/test/sass/results/if.css +3 -0
  176. data/test/sass/results/import.css +31 -0
  177. data/test/sass/results/import_charset.css +5 -0
  178. data/test/sass/results/import_charset_1_8.css +5 -0
  179. data/test/sass/results/import_charset_ibm866.css +5 -0
  180. data/test/sass/results/import_content.css +1 -0
  181. data/test/sass/results/line_numbers.css +49 -0
  182. data/test/sass/results/mixins.css +95 -0
  183. data/test/sass/results/multiline.css +24 -0
  184. data/test/sass/results/nested.css +22 -0
  185. data/test/sass/results/options.css +1 -0
  186. data/test/sass/results/parent_ref.css +13 -0
  187. data/test/sass/results/script.css +16 -0
  188. data/test/sass/results/scss_import.css +31 -0
  189. data/test/sass/results/scss_importee.css +2 -0
  190. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  191. data/test/sass/results/subdir/subdir.css +3 -0
  192. data/test/sass/results/units.css +11 -0
  193. data/test/sass/results/warn.css +0 -0
  194. data/test/sass/results/warn_imported.css +0 -0
  195. data/test/sass/script_conversion_test.rb +328 -0
  196. data/test/sass/script_test.rb +1054 -0
  197. data/test/sass/scss/css_test.rb +1215 -0
  198. data/test/sass/scss/rx_test.rb +156 -0
  199. data/test/sass/scss/scss_test.rb +3900 -0
  200. data/test/sass/scss/test_helper.rb +37 -0
  201. data/test/sass/source_map_test.rb +977 -0
  202. data/test/sass/superselector_test.rb +191 -0
  203. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  204. data/test/sass/templates/_double_import_loop2.sass +1 -0
  205. data/test/sass/templates/_filename_fn_import.scss +11 -0
  206. data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
  207. data/test/sass/templates/_imported_charset_utf8.sass +4 -0
  208. data/test/sass/templates/_imported_content.sass +3 -0
  209. data/test/sass/templates/_partial.sass +2 -0
  210. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  211. data/test/sass/templates/alt.sass +16 -0
  212. data/test/sass/templates/basic.sass +23 -0
  213. data/test/sass/templates/bork1.sass +2 -0
  214. data/test/sass/templates/bork2.sass +2 -0
  215. data/test/sass/templates/bork3.sass +2 -0
  216. data/test/sass/templates/bork4.sass +2 -0
  217. data/test/sass/templates/bork5.sass +3 -0
  218. data/test/sass/templates/cached_import_option.scss +3 -0
  219. data/test/sass/templates/compact.sass +17 -0
  220. data/test/sass/templates/complex.sass +305 -0
  221. data/test/sass/templates/compressed.sass +15 -0
  222. data/test/sass/templates/double_import_loop1.sass +1 -0
  223. data/test/sass/templates/expanded.sass +17 -0
  224. data/test/sass/templates/filename_fn.scss +18 -0
  225. data/test/sass/templates/if.sass +11 -0
  226. data/test/sass/templates/import.sass +12 -0
  227. data/test/sass/templates/import_charset.sass +9 -0
  228. data/test/sass/templates/import_charset_1_8.sass +6 -0
  229. data/test/sass/templates/import_charset_ibm866.sass +11 -0
  230. data/test/sass/templates/import_content.sass +4 -0
  231. data/test/sass/templates/importee.less +2 -0
  232. data/test/sass/templates/importee.sass +19 -0
  233. data/test/sass/templates/line_numbers.sass +13 -0
  234. data/test/sass/templates/mixin_bork.sass +5 -0
  235. data/test/sass/templates/mixins.sass +76 -0
  236. data/test/sass/templates/multiline.sass +20 -0
  237. data/test/sass/templates/nested.sass +25 -0
  238. data/test/sass/templates/nested_bork1.sass +2 -0
  239. data/test/sass/templates/nested_bork2.sass +2 -0
  240. data/test/sass/templates/nested_bork3.sass +2 -0
  241. data/test/sass/templates/nested_bork4.sass +2 -0
  242. data/test/sass/templates/nested_import.sass +2 -0
  243. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  244. data/test/sass/templates/options.sass +2 -0
  245. data/test/sass/templates/parent_ref.sass +25 -0
  246. data/test/sass/templates/same_name_different_ext.sass +2 -0
  247. data/test/sass/templates/same_name_different_ext.scss +1 -0
  248. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  249. data/test/sass/templates/script.sass +101 -0
  250. data/test/sass/templates/scss_import.scss +12 -0
  251. data/test/sass/templates/scss_importee.scss +1 -0
  252. data/test/sass/templates/single_import_loop.sass +1 -0
  253. data/test/sass/templates/subdir/import_up1.scss +1 -0
  254. data/test/sass/templates/subdir/import_up2.scss +1 -0
  255. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  256. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  257. data/test/sass/templates/subdir/subdir.sass +6 -0
  258. data/test/sass/templates/units.sass +11 -0
  259. data/test/sass/templates/warn.sass +3 -0
  260. data/test/sass/templates/warn_imported.sass +4 -0
  261. data/test/sass/test_helper.rb +8 -0
  262. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  263. data/test/sass/util/normalized_map_test.rb +51 -0
  264. data/test/sass/util/subset_map_test.rb +91 -0
  265. data/test/sass/util_test.rb +467 -0
  266. data/test/sass/value_helpers_test.rb +179 -0
  267. data/test/test_helper.rb +109 -0
  268. metadata +386 -0
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../test_helper'
3
+ require 'sass/util/test'
4
+ require 'tmpdir'
5
+
6
+ class ExecTest < MiniTest::Test
7
+ include Sass::Util::Test
8
+
9
+ def setup
10
+ @dir = Dir.mktmpdir
11
+ end
12
+
13
+ def teardown
14
+ FileUtils.rm_rf(@dir)
15
+ clean_up_sassc
16
+ end
17
+
18
+ def test_scss_t_expanded
19
+ src = get_path("src.scss")
20
+ dest = get_path("dest.css")
21
+ write(src, ".ruleset { margin: 0 }")
22
+ assert(exec(*%w[scss --sourcemap=none -t expanded --unix-newlines].push(src, dest)))
23
+ assert_equal(".ruleset {\n margin: 0;\n}\n", read(dest))
24
+ end
25
+
26
+ def test_sass_convert_T_sass
27
+ src = get_path("src.scss")
28
+ dest = get_path("dest.css")
29
+ write(src, ".ruleset { margin: 0 }")
30
+ assert(exec(*%w[sass-convert -T sass --unix-newlines].push(src, dest)))
31
+ assert_equal(".ruleset\n margin: 0\n", read(dest))
32
+ end
33
+
34
+ def test_sass_convert_T_sass_in_place
35
+ src = get_path("src.scss")
36
+ write(src, ".ruleset { margin: 0 }")
37
+ assert(exec(*%w[sass-convert -T sass --in-place --unix-newlines].push(src)))
38
+ assert_equal(".ruleset\n margin: 0\n", read(src))
39
+ end
40
+
41
+ def test_scss_t_expanded_no_unix_newlines
42
+ return skip "Can be run on Windows only" unless Sass::Util.windows?
43
+ src = get_path("src.scss")
44
+ dest = get_path("dest.css")
45
+ write(src, ".ruleset { margin: 0 }")
46
+ assert(exec(*%w[scss -t expanded].push(src, dest)))
47
+ assert_equal(".ruleset {\r\n margin: 0;\r\n}\r\n", read(dest))
48
+ end
49
+
50
+ def test_sass_convert_T_sass_no_unix_newlines
51
+ return skip "Can be run on Windows only" unless Sass::Util.windows?
52
+ src = get_path("src.scss")
53
+ dest = get_path("dest.sass")
54
+ write(src, ".ruleset { margin: 0 }")
55
+ assert(exec(*%w[sass-convert -T sass].push(src, dest)))
56
+ assert_equal(".ruleset\r\n margin: 0\r\n", read(dest))
57
+ end
58
+
59
+ def test_sass_convert_T_sass_in_place_no_unix_newlines
60
+ return skip "Can be run on Windows only" unless Sass::Util.windows?
61
+ src = get_path("src.scss")
62
+ write(src, ".ruleset { margin: 0 }")
63
+ assert(exec(*%w[sass-convert -T sass --in-place].push(src)))
64
+ assert_equal(".ruleset\r\n margin: 0\r\n", read(src))
65
+ end
66
+
67
+ private
68
+
69
+ def get_path(name)
70
+ File.join(@dir, name)
71
+ end
72
+
73
+ def read(file)
74
+ open(file, 'rb') {|f| f.read}
75
+ end
76
+
77
+ def write(file, content)
78
+ open(file, 'wb') {|f| f.write(content)}
79
+ end
80
+
81
+ def exec(script, *args)
82
+ script = File.dirname(__FILE__) + '/../../bin/' + script
83
+ ruby = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'] + RbConfig::CONFIG['EXEEXT'])
84
+ system(ruby, script, *args)
85
+ end
86
+ end
@@ -0,0 +1,1661 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../test_helper'
3
+
4
+ class ExtendTest < MiniTest::Test
5
+ def test_basic
6
+ assert_equal <<CSS, render(<<SCSS)
7
+ .foo, .bar {
8
+ a: b; }
9
+ CSS
10
+ .foo {a: b}
11
+ .bar {@extend .foo}
12
+ SCSS
13
+
14
+ assert_equal <<CSS, render(<<SCSS)
15
+ .foo, .bar {
16
+ a: b; }
17
+ CSS
18
+ .bar {@extend .foo}
19
+ .foo {a: b}
20
+ SCSS
21
+
22
+ assert_equal <<CSS, render(<<SCSS)
23
+ .foo, .bar {
24
+ a: b; }
25
+
26
+ .bar {
27
+ c: d; }
28
+ CSS
29
+ .foo {a: b}
30
+ .bar {c: d; @extend .foo}
31
+ SCSS
32
+
33
+ assert_equal <<CSS, render(<<SCSS)
34
+ .foo, .bar {
35
+ a: b; }
36
+
37
+ .bar {
38
+ c: d; }
39
+ CSS
40
+ .foo {a: b}
41
+ .bar {@extend .foo; c: d}
42
+ SCSS
43
+ end
44
+
45
+ def test_indented_syntax
46
+ assert_equal <<CSS, render(<<SASS, :syntax => :sass)
47
+ .foo, .bar {
48
+ a: b; }
49
+ CSS
50
+ .foo
51
+ a: b
52
+ .bar
53
+ @extend .foo
54
+ SASS
55
+
56
+ assert_equal <<CSS, render(<<SASS, :syntax => :sass)
57
+ .foo, .bar {
58
+ a: b; }
59
+ CSS
60
+ .foo
61
+ a: b
62
+ .bar
63
+ @extend \#{".foo"}
64
+ SASS
65
+ end
66
+
67
+ def test_multiple_targets
68
+ assert_equal <<CSS, render(<<SCSS)
69
+ .foo, .bar {
70
+ a: b; }
71
+
72
+ .blip .foo, .blip .bar {
73
+ c: d; }
74
+ CSS
75
+ .foo {a: b}
76
+ .bar {@extend .foo}
77
+ .blip .foo {c: d}
78
+ SCSS
79
+ end
80
+
81
+ def test_multiple_extendees
82
+ assert_equal <<CSS, render(<<SCSS)
83
+ .foo, .baz {
84
+ a: b; }
85
+
86
+ .bar, .baz {
87
+ c: d; }
88
+ CSS
89
+ .foo {a: b}
90
+ .bar {c: d}
91
+ .baz {@extend .foo; @extend .bar}
92
+ SCSS
93
+ end
94
+
95
+ def test_multiple_extends_with_single_extender_and_single_target
96
+ assert_extends('.foo .bar', '.baz {@extend .foo; @extend .bar}',
97
+ '.foo .bar, .baz .bar, .foo .baz, .baz .baz')
98
+ assert_extends '.foo.bar', '.baz {@extend .foo; @extend .bar}', '.foo.bar, .baz'
99
+ end
100
+
101
+ def test_multiple_extends_with_multiple_extenders_and_single_target
102
+ assert_equal <<CSS, render(<<SCSS)
103
+ .foo .bar, .baz .bar, .foo .bang, .baz .bang {
104
+ a: b; }
105
+ CSS
106
+ .foo .bar {a: b}
107
+ .baz {@extend .foo}
108
+ .bang {@extend .bar}
109
+ SCSS
110
+
111
+ assert_equal <<CSS, render(<<SCSS)
112
+ .foo.bar, .bar.baz, .baz.bang, .foo.bang {
113
+ a: b; }
114
+ CSS
115
+ .foo.bar {a: b}
116
+ .baz {@extend .foo}
117
+ .bang {@extend .bar}
118
+ SCSS
119
+ end
120
+
121
+ def test_chained_extends
122
+ assert_equal <<CSS, render(<<SCSS)
123
+ .foo, .bar, .baz, .bip {
124
+ a: b; }
125
+ CSS
126
+ .foo {a: b}
127
+ .bar {@extend .foo}
128
+ .baz {@extend .bar}
129
+ .bip {@extend .bar}
130
+ SCSS
131
+ end
132
+
133
+ def test_dynamic_extendee
134
+ assert_extends '.foo', '.bar {@extend #{".foo"}}', '.foo, .bar'
135
+ assert_extends('[baz^="blip12px"]', '.bar {@extend [baz^="blip#{12px}"]}',
136
+ '[baz^="blip12px"], .bar')
137
+ end
138
+
139
+ def test_nested_target
140
+ assert_extends '.foo .bar', '.baz {@extend .bar}', '.foo .bar, .foo .baz'
141
+ end
142
+
143
+ def test_target_with_child
144
+ assert_extends '.foo .bar', '.baz {@extend .foo}', '.foo .bar, .baz .bar'
145
+ end
146
+
147
+ def test_class_unification
148
+ assert_unification '.foo.bar', '.baz {@extend .foo}', '.foo.bar, .bar.baz'
149
+ assert_unification '.foo.baz', '.baz {@extend .foo}', '.baz'
150
+ end
151
+
152
+ def test_id_unification
153
+ assert_unification '.foo.bar', '#baz {@extend .foo}', '.foo.bar, .bar#baz'
154
+ assert_unification '.foo#baz', '#baz {@extend .foo}', '#baz'
155
+
156
+ assert_extend_doesnt_match('#bar', '.foo', :failed_to_unify, 2) do
157
+ render_unification '.foo#baz', '#bar {@extend .foo}'
158
+ end
159
+ end
160
+
161
+ def test_universal_unification_with_simple_target
162
+ assert_unification '.foo', '* {@extend .foo}', '.foo, *'
163
+ assert_unification '.foo', '*|* {@extend .foo}', '.foo, *|*'
164
+ assert_unification '.foo.bar', '* {@extend .foo}', '.bar'
165
+ assert_unification '.foo.bar', '*|* {@extend .foo}', '.bar'
166
+ assert_unification '.foo.bar', 'ns|* {@extend .foo}', '.foo.bar, ns|*.bar'
167
+ end
168
+
169
+ def test_universal_unification_with_namespaceless_universal_target
170
+ assert_unification '*.foo', '* {@extend .foo}', '*'
171
+ assert_unification '*.foo', '*|* {@extend .foo}', '*'
172
+ assert_unification '*|*.foo', '* {@extend .foo}', '*|*.foo, *'
173
+ assert_unification '*|*.foo', '*|* {@extend .foo}', '*|*'
174
+ assert_unification '*.foo', 'ns|* {@extend .foo}', '*.foo, ns|*'
175
+ assert_unification '*|*.foo', 'ns|* {@extend .foo}', '*|*.foo, ns|*'
176
+ end
177
+
178
+ def test_universal_unification_with_namespaced_universal_target
179
+ assert_unification 'ns|*.foo', '* {@extend .foo}', 'ns|*'
180
+ assert_unification 'ns|*.foo', '*|* {@extend .foo}', 'ns|*'
181
+
182
+ assert_extend_doesnt_match('ns2|*', '.foo', :failed_to_unify, 2) do
183
+ render_unification 'ns1|*.foo', 'ns2|* {@extend .foo}'
184
+ end
185
+
186
+ assert_unification 'ns|*.foo', 'ns|* {@extend .foo}', 'ns|*'
187
+ end
188
+
189
+ def test_universal_unification_with_namespaceless_element_target
190
+ assert_unification 'a.foo', '* {@extend .foo}', 'a'
191
+ assert_unification 'a.foo', '*|* {@extend .foo}', 'a'
192
+ assert_unification '*|a.foo', '* {@extend .foo}', '*|a.foo, a'
193
+ assert_unification '*|a.foo', '*|* {@extend .foo}', '*|a'
194
+ assert_unification 'a.foo', 'ns|* {@extend .foo}', 'a.foo, ns|a'
195
+ assert_unification '*|a.foo', 'ns|* {@extend .foo}', '*|a.foo, ns|a'
196
+ end
197
+
198
+ def test_universal_unification_with_namespaced_element_target
199
+ assert_unification 'ns|a.foo', '* {@extend .foo}', 'ns|a'
200
+ assert_unification 'ns|a.foo', '*|* {@extend .foo}', 'ns|a'
201
+
202
+ assert_extend_doesnt_match('ns2|*', '.foo', :failed_to_unify, 2) do
203
+ render_unification 'ns1|a.foo', 'ns2|* {@extend .foo}'
204
+ end
205
+
206
+ assert_unification 'ns|a.foo', 'ns|* {@extend .foo}', 'ns|a'
207
+ end
208
+
209
+ def test_element_unification_with_simple_target
210
+ assert_unification '.foo', 'a {@extend .foo}', '.foo, a'
211
+ assert_unification '.foo.bar', 'a {@extend .foo}', '.foo.bar, a.bar'
212
+ assert_unification '.foo.bar', '*|a {@extend .foo}', '.foo.bar, *|a.bar'
213
+ assert_unification '.foo.bar', 'ns|a {@extend .foo}', '.foo.bar, ns|a.bar'
214
+ end
215
+
216
+ def test_element_unification_with_namespaceless_universal_target
217
+ assert_unification '*.foo', 'a {@extend .foo}', '*.foo, a'
218
+ assert_unification '*.foo', '*|a {@extend .foo}', '*.foo, a'
219
+ assert_unification '*|*.foo', 'a {@extend .foo}', '*|*.foo, a'
220
+ assert_unification '*|*.foo', '*|a {@extend .foo}', '*|*.foo, *|a'
221
+ assert_unification '*.foo', 'ns|a {@extend .foo}', '*.foo, ns|a'
222
+ assert_unification '*|*.foo', 'ns|a {@extend .foo}', '*|*.foo, ns|a'
223
+ end
224
+
225
+ def test_element_unification_with_namespaced_universal_target
226
+ assert_unification 'ns|*.foo', 'a {@extend .foo}', 'ns|*.foo, ns|a'
227
+ assert_unification 'ns|*.foo', '*|a {@extend .foo}', 'ns|*.foo, ns|a'
228
+
229
+ assert_extend_doesnt_match('ns2|a', '.foo', :failed_to_unify, 2) do
230
+ render_unification 'ns1|*.foo', 'ns2|a {@extend .foo}'
231
+ end
232
+
233
+ assert_unification 'ns|*.foo', 'ns|a {@extend .foo}', 'ns|*.foo, ns|a'
234
+ end
235
+
236
+ def test_element_unification_with_namespaceless_element_target
237
+ assert_unification 'a.foo', 'a {@extend .foo}', 'a'
238
+ assert_unification 'a.foo', '*|a {@extend .foo}', 'a'
239
+ assert_unification '*|a.foo', 'a {@extend .foo}', '*|a.foo, a'
240
+ assert_unification '*|a.foo', '*|a {@extend .foo}', '*|a'
241
+ assert_unification 'a.foo', 'ns|a {@extend .foo}', 'a.foo, ns|a'
242
+ assert_unification '*|a.foo', 'ns|a {@extend .foo}', '*|a.foo, ns|a'
243
+
244
+ assert_extend_doesnt_match('h1', '.foo', :failed_to_unify, 2) do
245
+ render_unification 'a.foo', 'h1 {@extend .foo}'
246
+ end
247
+ end
248
+
249
+ def test_element_unification_with_namespaced_element_target
250
+ assert_unification 'ns|a.foo', 'a {@extend .foo}', 'ns|a'
251
+ assert_unification 'ns|a.foo', '*|a {@extend .foo}', 'ns|a'
252
+
253
+ assert_extend_doesnt_match('ns2|a', '.foo', :failed_to_unify, 2) do
254
+ render_unification 'ns1|a.foo', 'ns2|a {@extend .foo}'
255
+ end
256
+
257
+ assert_unification 'ns|a.foo', 'ns|a {@extend .foo}', 'ns|a'
258
+ end
259
+
260
+ def test_attribute_unification
261
+ assert_unification '[foo=bar].baz', '[foo=baz] {@extend .baz}', '[foo=bar].baz, [foo=bar][foo=baz]'
262
+ assert_unification '[foo=bar].baz', '[foo^=bar] {@extend .baz}', '[foo=bar].baz, [foo=bar][foo^=bar]'
263
+ assert_unification '[foo=bar].baz', '[foot=bar] {@extend .baz}', '[foo=bar].baz, [foo=bar][foot=bar]'
264
+ assert_unification '[foo=bar].baz', '[ns|foo=bar] {@extend .baz}', '[foo=bar].baz, [foo=bar][ns|foo=bar]'
265
+ assert_unification '%-a [foo=bar].bar', '[foo=bar] {@extend .bar}', '-a [foo=bar]'
266
+ end
267
+
268
+ def test_pseudo_unification
269
+ assert_unification ':foo.baz', ':foo(2n+1) {@extend .baz}', ':foo.baz, :foo:foo(2n+1)'
270
+ assert_unification ':foo.baz', '::foo {@extend .baz}', ':foo.baz, :foo::foo'
271
+
272
+ assert_extend_doesnt_match('::bar', '.baz', :failed_to_unify, 2) do
273
+ render_unification '::foo.baz', '::bar {@extend .baz}'
274
+ end
275
+
276
+ assert_extend_doesnt_match('::foo(2n+1)', '.baz', :failed_to_unify, 2) do
277
+ render_unification '::foo.baz', '::foo(2n+1) {@extend .baz}'
278
+ end
279
+
280
+ assert_unification '::foo.baz', '::foo {@extend .baz}', '::foo'
281
+ assert_unification '::foo(2n+1).baz', '::foo(2n+1) {@extend .baz}', '::foo(2n+1)'
282
+ assert_unification ':foo.baz', ':bar {@extend .baz}', ':foo.baz, :foo:bar'
283
+ assert_unification '.baz:foo', ':after {@extend .baz}', '.baz:foo, :foo:after'
284
+ assert_unification '.baz:after', ':foo {@extend .baz}', '.baz:after, :foo:after'
285
+ assert_unification ':foo.baz', ':foo {@extend .baz}', ':foo'
286
+ end
287
+
288
+ def test_pseudoelement_remains_at_end_of_selector
289
+ assert_extends '.foo::bar', '.baz {@extend .foo}', '.foo::bar, .baz::bar'
290
+ assert_extends 'a.foo::bar', '.baz {@extend .foo}', 'a.foo::bar, a.baz::bar'
291
+ end
292
+
293
+ def test_pseudoclass_remains_at_end_of_selector
294
+ assert_extends '.foo:bar', '.baz {@extend .foo}', '.foo:bar, .baz:bar'
295
+ assert_extends 'a.foo:bar', '.baz {@extend .foo}', 'a.foo:bar, a.baz:bar'
296
+ end
297
+
298
+ def test_not_remains_at_end_of_selector
299
+ assert_extends '.foo:not(.bar)', '.baz {@extend .foo}', '.foo:not(.bar), .baz:not(.bar)'
300
+ end
301
+
302
+ def test_pseudoelement_goes_lefter_than_pseudoclass
303
+ assert_extends '.foo::bar', '.baz:bang {@extend .foo}', '.foo::bar, .baz:bang::bar'
304
+ assert_extends '.foo:bar', '.baz::bang {@extend .foo}', '.foo:bar, .baz:bar::bang'
305
+ end
306
+
307
+ def test_pseudoelement_goes_lefter_than_not
308
+ assert_extends '.foo::bar', '.baz:not(.bang) {@extend .foo}', '.foo::bar, .baz:not(.bang)::bar'
309
+ assert_extends '.foo:not(.bang)', '.baz::bar {@extend .foo}', '.foo:not(.bang), .baz:not(.bang)::bar'
310
+ end
311
+
312
+ def test_negation_unification
313
+ assert_extends ':not(.foo).baz', ':not(.bar) {@extend .baz}', ':not(.foo).baz, :not(.foo):not(.bar)'
314
+ # Unifying to :not(.foo) here would reduce the specificity of the original selector.
315
+ assert_extends ':not(.foo).baz', ':not(.foo) {@extend .baz}', ':not(.foo).baz, :not(.foo)'
316
+ end
317
+
318
+ def test_prefixed_pseudoclass_unification
319
+ assert_unification(
320
+ ':nth-child(2n+1 of .foo).baz',
321
+ ':nth-child(2n of .foo) {@extend .baz}',
322
+ ':nth-child(2n+1 of .foo).baz, :nth-child(2n+1 of .foo):nth-child(2n of .foo)')
323
+
324
+ assert_unification(
325
+ ':nth-child(2n+1 of .foo).baz',
326
+ ':nth-child(2n+1 of .bar) {@extend .baz}',
327
+ ':nth-child(2n+1 of .foo).baz, :nth-child(2n+1 of .foo):nth-child(2n+1 of .bar)')
328
+
329
+ assert_unification(
330
+ ':nth-child(2n+1 of .foo).baz',
331
+ ':nth-child(2n+1 of .foo) {@extend .baz}',
332
+ ':nth-child(2n+1 of .foo)')
333
+ end
334
+
335
+ def test_extend_into_not
336
+ assert_extends(':not(.foo)', '.x {@extend .foo}', ':not(.foo, .x)')
337
+ assert_extends(':not(.foo.bar)', '.x {@extend .bar}', ':not(.foo.bar, .foo.x)')
338
+ assert_extends(
339
+ ':not(.foo.bar, .baz.bar)',
340
+ '.x {@extend .bar}',
341
+ ':not(.foo.bar, .foo.x, .baz.bar, .baz.x)')
342
+ end
343
+
344
+ def test_extend_into_mergeable_pseudoclasses
345
+ assert_extends(':matches(.foo)', '.x {@extend .foo}', ':matches(.foo, .x)')
346
+ assert_extends(':matches(.foo.bar)', '.x {@extend .bar}', ':matches(.foo.bar, .foo.x)')
347
+ assert_extends(
348
+ ':matches(.foo.bar, .baz.bar)',
349
+ '.x {@extend .bar}',
350
+ ':matches(.foo.bar, .foo.x, .baz.bar, .baz.x)')
351
+
352
+ assert_extends(':-moz-any(.foo)', '.x {@extend .foo}', ':-moz-any(.foo, .x)')
353
+ assert_extends(':current(.foo)', '.x {@extend .foo}', ':current(.foo, .x)')
354
+ assert_extends(':has(.foo)', '.x {@extend .foo}', ':has(.foo, .x)')
355
+ assert_extends(':host(.foo)', '.x {@extend .foo}', ':host(.foo, .x)')
356
+ assert_extends(':host-context(.foo)', '.x {@extend .foo}', ':host-context(.foo, .x)')
357
+ assert_extends(':nth-child(n of .foo)', '.x {@extend .foo}', ':nth-child(n of .foo, .x)')
358
+ assert_extends(
359
+ ':nth-last-child(n of .foo)',
360
+ '.x {@extend .foo}',
361
+ ':nth-last-child(n of .foo, .x)')
362
+ end
363
+
364
+ def test_complex_extend_into_pseudoclass
365
+ assert_extends(':not(.bar)', '.x .y {@extend .bar}', ':not(.bar, .x .y)')
366
+ assert_extends(':matches(.bar)', '.x .y {@extend .bar}', ':matches(.bar, .x .y)')
367
+ assert_extends(':current(.bar)', '.x .y {@extend .bar}', ':current(.bar, .x .y)')
368
+ assert_extends(':has(.bar)', '.x .y {@extend .bar}', ':has(.bar, .x .y)')
369
+ assert_extends(':host(.bar)', '.x .y {@extend .bar}', ':host(.bar, .x .y)')
370
+ assert_extends(':host-context(.bar)', '.x .y {@extend .bar}', ':host-context(.bar, .x .y)')
371
+ assert_extends(
372
+ ':-moz-any(.bar)',
373
+ '.x .y {@extend .bar}',
374
+ ':-moz-any(.bar, .x .y)')
375
+ assert_extends(
376
+ ':nth-child(n of .bar)',
377
+ '.x .y {@extend .bar}',
378
+ ':nth-child(n of .bar, .x .y)')
379
+ assert_extends(
380
+ ':nth-last-child(n of .bar)',
381
+ '.x .y {@extend .bar}',
382
+ ':nth-last-child(n of .bar, .x .y)')
383
+ end
384
+
385
+ def test_extend_over_selector_pseudoclass
386
+ assert_extends(':not(.foo)', '.x {@extend :not(.foo)}', ':not(.foo), .x')
387
+ assert_extends(
388
+ ':matches(.foo, .bar)',
389
+ '.x {@extend :matches(.foo, .bar)}',
390
+ ':matches(.foo, .bar), .x')
391
+ end
392
+
393
+ def test_matches_within_not
394
+ assert_extends(
395
+ ':not(.foo, .bar)',
396
+ ':matches(.x, .y) {@extend .foo}',
397
+ ':not(.foo, .x, .y, .bar)')
398
+ end
399
+
400
+ def test_pseudoclasses_merge
401
+ assert_extends(':matches(.foo)', ':matches(.bar) {@extend .foo}', ':matches(.foo, .bar)')
402
+ assert_extends(':-moz-any(.foo)', ':-moz-any(.bar) {@extend .foo}', ':-moz-any(.foo, .bar)')
403
+ assert_extends(':current(.foo)', ':current(.bar) {@extend .foo}', ':current(.foo, .bar)')
404
+ assert_extends(
405
+ ':nth-child(n of .foo)',
406
+ ':nth-child(n of .bar) {@extend .foo}',
407
+ ':nth-child(n of .foo, .bar)')
408
+ assert_extends(
409
+ ':nth-last-child(n of .foo)',
410
+ ':nth-last-child(n of .bar) {@extend .foo}',
411
+ ':nth-last-child(n of .foo, .bar)')
412
+ end
413
+
414
+ def test_nesting_pseudoclasses_merge
415
+ assert_extends(':has(.foo)', ':has(.bar) {@extend .foo}', ':has(.foo, :has(.bar))')
416
+ assert_extends(':host(.foo)', ':host(.bar) {@extend .foo}', ':host(.foo, :host(.bar))')
417
+ assert_extends(
418
+ ':host-context(.foo)',
419
+ ':host-context(.bar) {@extend .foo}',
420
+ ':host-context(.foo, :host-context(.bar))')
421
+ end
422
+
423
+ def test_not_unifies_with_unique_values
424
+ assert_unification('foo', ':not(bar) {@extend foo}', ':not(bar)')
425
+ assert_unification('#foo', ':not(#bar) {@extend #foo}', ':not(#bar)')
426
+ end
427
+
428
+ def test_not_adds_no_specificity
429
+ assert_specificity_equals(':not(.foo)', '.foo')
430
+ end
431
+
432
+ def test_matches_has_a_specificity_range
433
+ # `:matches(.foo, #bar)` has minimum specificity equal to that of `.foo`,
434
+ # which means `:matches(.foo, #bar) .a` can have less specificity than
435
+ # `#b.a`. Thus the selector generated by `#b.a` should be preserved.
436
+ assert_equal <<CSS, render(<<SCSS)
437
+ :matches(.foo, #bar) .a, :matches(.foo, #bar) #b.a {
438
+ a: b; }
439
+ CSS
440
+ :matches(.foo, #bar) %x {a: b}
441
+ .a {@extend %x}
442
+ #b.a {@extend %x}
443
+ SCSS
444
+
445
+ # `:matches(.foo, #bar)` has maximum specificity equal to that of `#bar`,
446
+ # which means `:matches(.foo, #bar).b` can have greater specificity than `.a
447
+ # .b`. Thus the selector generated by `:matches(.foo, #bar).b` should be
448
+ # preserved.
449
+ assert_equal <<CSS, render(<<SCSS)
450
+ .a .b, .a .b:matches(.foo, #bar) {
451
+ a: b; }
452
+ CSS
453
+ .a %x {a: b}
454
+ .b {@extend %x}
455
+ .b:matches(.foo, #bar) {@extend %x}
456
+ SCSS
457
+ end
458
+
459
+ def test_extend_into_not_and_normal_extend
460
+ assert_equal <<CSS, render(<<SCSS)
461
+ .x:not(.y, .bar), .foo:not(.y, .bar) {
462
+ a: b; }
463
+ CSS
464
+ .x:not(.y) {a: b}
465
+ .foo {@extend .x}
466
+ .bar {@extend .y}
467
+ SCSS
468
+ end
469
+
470
+ def test_extend_into_matches_and_normal_extend
471
+ assert_equal <<CSS, render(<<SCSS)
472
+ .x:matches(.y, .bar), .foo:matches(.y, .bar) {
473
+ a: b; }
474
+ CSS
475
+ .x:matches(.y) {a: b}
476
+ .foo {@extend .x}
477
+ .bar {@extend .y}
478
+ SCSS
479
+ end
480
+
481
+ def test_multilayer_pseudoclass_extend
482
+ assert_equal <<CSS, render(<<SCSS)
483
+ :matches(.x, .foo, .bar) {
484
+ a: b; }
485
+ CSS
486
+ :matches(.x) {a: b}
487
+ .foo {@extend .x}
488
+ .bar {@extend .foo}
489
+ SCSS
490
+ end
491
+
492
+ def test_comma_extendee
493
+ assert_equal <<CSS, render(<<SCSS)
494
+ .foo, .baz {
495
+ a: b; }
496
+
497
+ .bar, .baz {
498
+ c: d; }
499
+ CSS
500
+ .foo {a: b}
501
+ .bar {c: d}
502
+ .baz {@extend .foo, .bar}
503
+ SCSS
504
+ end
505
+
506
+ def test_redundant_selector_elimination
507
+ assert_equal <<CSS, render(<<SCSS)
508
+ .foo.bar, .x, .y {
509
+ a: b; }
510
+ CSS
511
+ .foo.bar {a: b}
512
+ .x {@extend .foo, .bar}
513
+ .y {@extend .foo, .bar}
514
+ SCSS
515
+ end
516
+
517
+ ## Long Extendees
518
+
519
+ def test_long_extendee
520
+ assert_extends '.foo.bar', '.baz {@extend .foo.bar}', '.foo.bar, .baz'
521
+ end
522
+
523
+ def test_long_extendee_requires_all_selectors
524
+ assert_extend_doesnt_match('.baz', '.foo.bar', :not_found, 2) do
525
+ render_extends '.foo', '.baz {@extend .foo.bar}'
526
+ end
527
+ end
528
+
529
+ def test_long_extendee_matches_supersets
530
+ assert_extends '.foo.bar.bap', '.baz {@extend .foo.bar}', '.foo.bar.bap, .bap.baz'
531
+ end
532
+
533
+ def test_long_extendee_runs_unification
534
+ assert_extends 'ns|*.foo.bar', 'a.baz {@extend .foo.bar}', 'ns|*.foo.bar, ns|a.baz'
535
+ end
536
+
537
+ ## Long Extenders
538
+
539
+ def test_long_extender
540
+ assert_extends '.foo.bar', '.baz.bang {@extend .foo}', '.foo.bar, .bar.baz.bang'
541
+ end
542
+
543
+ def test_long_extender_runs_unification
544
+ assert_extends 'ns|*.foo.bar', 'a.baz {@extend .foo}', 'ns|*.foo.bar, ns|a.bar.baz'
545
+ end
546
+
547
+ def test_long_extender_aborts_unification
548
+ assert_extend_doesnt_match('h1.baz', '.foo', :failed_to_unify, 2) do
549
+ render_extends 'a.foo#bar', 'h1.baz {@extend .foo}'
550
+ end
551
+
552
+ assert_extend_doesnt_match('.bang#baz', '.foo', :failed_to_unify, 2) do
553
+ render_extends 'a.foo#bar', '.bang#baz {@extend .foo}'
554
+ end
555
+ end
556
+
557
+ ## Nested Extenders
558
+
559
+ def test_nested_extender
560
+ assert_extends '.foo', 'foo bar {@extend .foo}', '.foo, foo bar'
561
+ end
562
+
563
+ def test_nested_extender_runs_unification
564
+ assert_extends '.foo.bar', 'foo bar {@extend .foo}', '.foo.bar, foo bar.bar'
565
+ end
566
+
567
+ def test_nested_extender_aborts_unification
568
+ assert_extend_doesnt_match('foo bar', '.foo', :failed_to_unify, 2) do
569
+ render_extends 'baz.foo', 'foo bar {@extend .foo}'
570
+ end
571
+ end
572
+
573
+ def test_nested_extender_alternates_parents
574
+ assert_extends('.baz .bip .foo', 'foo .grank bar {@extend .foo}',
575
+ '.baz .bip .foo, .baz .bip foo .grank bar, foo .grank .baz .bip bar')
576
+ end
577
+
578
+ def test_nested_extender_unifies_identical_parents
579
+ assert_extends('.baz .bip .foo', '.baz .bip bar {@extend .foo}',
580
+ '.baz .bip .foo, .baz .bip bar')
581
+ end
582
+
583
+ def test_nested_extender_unifies_common_substring
584
+ assert_extends('.baz .bip .bap .bink .foo', '.brat .bip .bap bar {@extend .foo}',
585
+ '.baz .bip .bap .bink .foo, .baz .brat .bip .bap .bink bar, .brat .baz .bip .bap .bink bar')
586
+ end
587
+
588
+ def test_nested_extender_unifies_common_subseq
589
+ assert_extends('.a .x .b .y .foo', '.a .n .b .m bar {@extend .foo}',
590
+ '.a .x .b .y .foo, .a .x .n .b .y .m bar, .a .n .x .b .y .m bar, .a .x .n .b .m .y bar, .a .n .x .b .m .y bar')
591
+ end
592
+
593
+ def test_nested_extender_chooses_first_subseq
594
+ assert_extends('.a .b .c .d .foo', '.c .d .a .b .bar {@extend .foo}',
595
+ '.a .b .c .d .foo, .a .b .c .d .a .b .bar')
596
+ end
597
+
598
+ def test_nested_extender_counts_extended_subselectors
599
+ assert_extends('.a .bip.bop .foo', '.b .bip .bar {@extend .foo}',
600
+ '.a .bip.bop .foo, .a .b .bip.bop .bar, .b .a .bip.bop .bar')
601
+ end
602
+
603
+ def test_nested_extender_counts_extended_superselectors
604
+ assert_extends('.a .bip .foo', '.b .bip.bop .bar {@extend .foo}',
605
+ '.a .bip .foo, .a .b .bip.bop .bar, .b .a .bip.bop .bar')
606
+ end
607
+
608
+ def test_nested_extender_with_child_selector
609
+ assert_extends '.baz .foo', 'foo > bar {@extend .foo}', '.baz .foo, .baz foo > bar'
610
+ end
611
+
612
+ def test_nested_extender_finds_common_selectors_around_child_selector
613
+ assert_extends 'a > b c .c1', 'a c .c2 {@extend .c1}', 'a > b c .c1, a > b c .c2'
614
+ assert_extends 'a > b c .c1', 'b c .c2 {@extend .c1}', 'a > b c .c1, a > b c .c2'
615
+ end
616
+
617
+ def test_nested_extender_doesnt_find_common_selectors_around_adjacent_sibling_selector
618
+ assert_extends 'a + b c .c1', 'a c .c2 {@extend .c1}', 'a + b c .c1, a + b a c .c2, a a + b c .c2'
619
+ assert_extends 'a + b c .c1', 'a b .c2 {@extend .c1}', 'a + b c .c1, a a + b c .c2'
620
+ assert_extends 'a + b c .c1', 'b c .c2 {@extend .c1}', 'a + b c .c1, a + b c .c2'
621
+ end
622
+
623
+ def test_nested_extender_doesnt_find_common_selectors_around_sibling_selector
624
+ assert_extends 'a ~ b c .c1', 'a c .c2 {@extend .c1}', 'a ~ b c .c1, a ~ b a c .c2, a a ~ b c .c2'
625
+ assert_extends 'a ~ b c .c1', 'a b .c2 {@extend .c1}', 'a ~ b c .c1, a a ~ b c .c2'
626
+ assert_extends 'a ~ b c .c1', 'b c .c2 {@extend .c1}', 'a ~ b c .c1, a ~ b c .c2'
627
+ end
628
+
629
+ def test_nested_extender_doesnt_find_common_selectors_around_reference_selector
630
+ assert_extends 'a /for/ b c .c1', 'a c .c2 {@extend .c1}', 'a /for/ b c .c1, a /for/ b a c .c2, a a /for/ b c .c2'
631
+ assert_extends 'a /for/ b c .c1', 'a b .c2 {@extend .c1}', 'a /for/ b c .c1, a a /for/ b c .c2'
632
+ assert_extends 'a /for/ b c .c1', 'b c .c2 {@extend .c1}', 'a /for/ b c .c1, a /for/ b c .c2'
633
+ end
634
+
635
+ def test_nested_extender_with_early_child_selectors_doesnt_subseq_them
636
+ assert_extends('.bip > .bap .foo', '.grip > .bap .bar {@extend .foo}',
637
+ '.bip > .bap .foo, .bip > .bap .grip > .bap .bar, .grip > .bap .bip > .bap .bar')
638
+ assert_extends('.bap > .bip .foo', '.bap > .grip .bar {@extend .foo}',
639
+ '.bap > .bip .foo, .bap > .bip .bap > .grip .bar, .bap > .grip .bap > .bip .bar')
640
+ end
641
+
642
+ def test_nested_extender_with_child_selector_unifies
643
+ assert_extends '.baz.foo', 'foo > bar {@extend .foo}', '.baz.foo, foo > bar.baz'
644
+
645
+ assert_equal <<CSS, render(<<SCSS)
646
+ .baz > .foo, .baz > .bar {
647
+ a: b; }
648
+ CSS
649
+ .baz > {
650
+ .foo {a: b}
651
+ .bar {@extend .foo}
652
+ }
653
+ SCSS
654
+
655
+ assert_equal <<CSS, render(<<SCSS)
656
+ .foo .bar, .foo > .baz {
657
+ a: b; }
658
+ CSS
659
+ .foo {
660
+ .bar {a: b}
661
+ > .baz {@extend .bar}
662
+ }
663
+ SCSS
664
+ end
665
+
666
+ def test_nested_extender_with_early_child_selector
667
+ assert_equal <<CSS, render(<<SCSS)
668
+ .foo .bar, .foo .bip > .baz {
669
+ a: b; }
670
+ CSS
671
+ .foo {
672
+ .bar {a: b}
673
+ .bip > .baz {@extend .bar}
674
+ }
675
+ SCSS
676
+
677
+ assert_equal <<CSS, render(<<SCSS)
678
+ .foo .bip .bar, .foo .bip .foo > .baz {
679
+ a: b; }
680
+ CSS
681
+ .foo {
682
+ .bip .bar {a: b}
683
+ > .baz {@extend .bar}
684
+ }
685
+ SCSS
686
+
687
+ assert_extends '.foo > .bar', '.bip + .baz {@extend .bar}', '.foo > .bar, .foo > .bip + .baz'
688
+ assert_extends '.foo + .bar', '.bip > .baz {@extend .bar}', '.foo + .bar, .bip > .foo + .baz'
689
+ assert_extends '.foo > .bar', '.bip > .baz {@extend .bar}', '.foo > .bar, .bip.foo > .baz'
690
+ end
691
+
692
+ def test_nested_extender_with_trailing_child_selector
693
+ assert_raises(Sass::SyntaxError, "bar > can't extend: invalid selector") do
694
+ render("bar > {@extend .baz}")
695
+ end
696
+ end
697
+
698
+ def test_nested_extender_with_sibling_selector
699
+ assert_extends '.baz .foo', 'foo + bar {@extend .foo}', '.baz .foo, .baz foo + bar'
700
+ end
701
+
702
+ def test_nested_extender_with_hacky_selector
703
+ assert_extends('.baz .foo', 'foo + > > + bar {@extend .foo}',
704
+ '.baz .foo, .baz foo + > > + bar, foo .baz + > > + bar')
705
+ assert_extends '.baz .foo', '> > bar {@extend .foo}', '.baz .foo, > > .baz bar'
706
+ end
707
+
708
+ def test_nested_extender_merges_with_same_selector
709
+ assert_equal <<CSS, render(<<SCSS)
710
+ .foo .bar, .foo .baz {
711
+ a: b; }
712
+ CSS
713
+ .foo {
714
+ .bar {a: b}
715
+ .baz {@extend .bar} }
716
+ SCSS
717
+ end
718
+
719
+ def test_nested_extender_with_child_selector_merges_with_same_selector
720
+ assert_extends('.foo > .bar .baz', '.foo > .bar .bang {@extend .baz}',
721
+ '.foo > .bar .baz, .foo > .bar .bang')
722
+ end
723
+
724
+ # Combinator Unification
725
+
726
+ def test_combinator_unification_for_hacky_combinators
727
+ assert_extends '.a > + x', '.b y {@extend x}', '.a > + x, .a .b > + y, .b .a > + y'
728
+ assert_extends '.a x', '.b > + y {@extend x}', '.a x, .a .b > + y, .b .a > + y'
729
+ assert_extends '.a > + x', '.b > + y {@extend x}', '.a > + x, .a .b > + y, .b .a > + y'
730
+ assert_extends '.a ~ > + x', '.b > + y {@extend x}', '.a ~ > + x, .a .b ~ > + y, .b .a ~ > + y'
731
+ assert_extends '.a + > x', '.b > + y {@extend x}', '.a + > x'
732
+ assert_extends '.a + > x', '.b > + y {@extend x}', '.a + > x'
733
+ assert_extends '.a ~ > + .b > x', '.c > + .d > y {@extend x}', '.a ~ > + .b > x, .a .c ~ > + .d.b > y, .c .a ~ > + .d.b > y'
734
+ end
735
+
736
+ def test_combinator_unification_double_tilde
737
+ assert_extends '.a.b ~ x', '.a ~ y {@extend x}', '.a.b ~ x, .a.b ~ y'
738
+ assert_extends '.a ~ x', '.a.b ~ y {@extend x}', '.a ~ x, .a.b ~ y'
739
+ assert_extends '.a ~ x', '.b ~ y {@extend x}', '.a ~ x, .a ~ .b ~ y, .b ~ .a ~ y, .b.a ~ y'
740
+ assert_extends 'a.a ~ x', 'b.b ~ y {@extend x}', 'a.a ~ x, a.a ~ b.b ~ y, b.b ~ a.a ~ y'
741
+ end
742
+
743
+ def test_combinator_unification_tilde_plus
744
+ assert_extends '.a.b + x', '.a ~ y {@extend x}', '.a.b + x, .a.b + y'
745
+ assert_extends '.a + x', '.a.b ~ y {@extend x}', '.a + x, .a.b ~ .a + y, .a.b + y'
746
+ assert_extends '.a + x', '.b ~ y {@extend x}', '.a + x, .b ~ .a + y, .b.a + y'
747
+ assert_extends 'a.a + x', 'b.b ~ y {@extend x}', 'a.a + x, b.b ~ a.a + y'
748
+ assert_extends '.a.b ~ x', '.a + y {@extend x}', '.a.b ~ x, .a.b ~ .a + y, .a.b + y'
749
+ assert_extends '.a ~ x', '.a.b + y {@extend x}', '.a ~ x, .a.b + y'
750
+ assert_extends '.a ~ x', '.b + y {@extend x}', '.a ~ x, .a ~ .b + y, .a.b + y'
751
+ assert_extends 'a.a ~ x', 'b.b + y {@extend x}', 'a.a ~ x, a.a ~ b.b + y'
752
+ end
753
+
754
+ def test_combinator_unification_angle_sibling
755
+ assert_extends '.a > x', '.b ~ y {@extend x}', '.a > x, .a > .b ~ y'
756
+ assert_extends '.a > x', '.b + y {@extend x}', '.a > x, .a > .b + y'
757
+ assert_extends '.a ~ x', '.b > y {@extend x}', '.a ~ x, .b > .a ~ y'
758
+ assert_extends '.a + x', '.b > y {@extend x}', '.a + x, .b > .a + y'
759
+ end
760
+
761
+ def test_combinator_unification_double_angle
762
+ assert_extends '.a.b > x', '.b > y {@extend x}', '.a.b > x, .b.a > y'
763
+ assert_extends '.a > x', '.a.b > y {@extend x}', '.a > x, .a.b > y'
764
+ assert_extends '.a > x', '.b > y {@extend x}', '.a > x, .b.a > y'
765
+ assert_extends 'a.a > x', 'b.b > y {@extend x}', 'a.a > x'
766
+ end
767
+
768
+ def test_combinator_unification_double_plus
769
+ assert_extends '.a.b + x', '.b + y {@extend x}', '.a.b + x, .b.a + y'
770
+ assert_extends '.a + x', '.a.b + y {@extend x}', '.a + x, .a.b + y'
771
+ assert_extends '.a + x', '.b + y {@extend x}', '.a + x, .b.a + y'
772
+ assert_extends 'a.a + x', 'b.b + y {@extend x}', 'a.a + x'
773
+ end
774
+
775
+ def test_combinator_unification_angle_space
776
+ assert_extends '.a.b > x', '.a y {@extend x}', '.a.b > x, .a.b > y'
777
+ assert_extends '.a > x', '.a.b y {@extend x}', '.a > x, .a.b .a > y'
778
+ assert_extends '.a > x', '.b y {@extend x}', '.a > x, .b .a > y'
779
+ assert_extends '.a.b x', '.a > y {@extend x}', '.a.b x, .a.b .a > y'
780
+ assert_extends '.a x', '.a.b > y {@extend x}', '.a x, .a.b > y'
781
+ assert_extends '.a x', '.b > y {@extend x}', '.a x, .a .b > y'
782
+ end
783
+
784
+ def test_combinator_unification_plus_space
785
+ assert_extends '.a.b + x', '.a y {@extend x}', '.a.b + x, .a .a.b + y'
786
+ assert_extends '.a + x', '.a.b y {@extend x}', '.a + x, .a.b .a + y'
787
+ assert_extends '.a + x', '.b y {@extend x}', '.a + x, .b .a + y'
788
+ assert_extends '.a.b x', '.a + y {@extend x}', '.a.b x, .a.b .a + y'
789
+ assert_extends '.a x', '.a.b + y {@extend x}', '.a x, .a .a.b + y'
790
+ assert_extends '.a x', '.b + y {@extend x}', '.a x, .a .b + y'
791
+ end
792
+
793
+ def test_combinator_unification_nested
794
+ assert_extends '.a > .b + x', '.c > .d + y {@extend x}', '.a > .b + x, .c.a > .d.b + y'
795
+ assert_extends '.a > .b + x', '.c > y {@extend x}', '.a > .b + x, .c.a > .b + y'
796
+ end
797
+
798
+ def test_combinator_unification_with_newlines
799
+ assert_equal <<CSS, render(<<SCSS)
800
+ .a >
801
+ .b
802
+ + x, .c.a > .d.b + y {
803
+ a: b; }
804
+ CSS
805
+ .a >
806
+ .b
807
+ + x {a: b}
808
+ .c
809
+ > .d +
810
+ y {@extend x}
811
+ SCSS
812
+ end
813
+
814
+ # Loops
815
+
816
+ def test_extend_self_loop
817
+ assert_equal <<CSS, render(<<SCSS)
818
+ .foo {
819
+ a: b; }
820
+ CSS
821
+ .foo {a: b; @extend .foo}
822
+ SCSS
823
+ end
824
+
825
+ def test_basic_extend_loop
826
+ assert_equal <<CSS, render(<<SCSS)
827
+ .foo, .bar {
828
+ a: b; }
829
+
830
+ .bar, .foo {
831
+ c: d; }
832
+ CSS
833
+ .foo {a: b; @extend .bar}
834
+ .bar {c: d; @extend .foo}
835
+ SCSS
836
+ end
837
+
838
+ def test_three_level_extend_loop
839
+ assert_equal <<CSS, render(<<SCSS)
840
+ .foo, .baz, .bar {
841
+ a: b; }
842
+
843
+ .bar, .foo, .baz {
844
+ c: d; }
845
+
846
+ .baz, .bar, .foo {
847
+ e: f; }
848
+ CSS
849
+ .foo {a: b; @extend .bar}
850
+ .bar {c: d; @extend .baz}
851
+ .baz {e: f; @extend .foo}
852
+ SCSS
853
+ end
854
+
855
+ def test_nested_extend_loop
856
+ assert_equal <<CSS, render(<<SCSS)
857
+ .bar, .bar .foo {
858
+ a: b; }
859
+ .bar .foo {
860
+ c: d; }
861
+ CSS
862
+ .bar {
863
+ a: b;
864
+ .foo {c: d; @extend .bar}
865
+ }
866
+ SCSS
867
+ end
868
+
869
+ def test_cross_loop
870
+ # The first law of extend means the selector should stick around.
871
+ assert_equal <<CSS, render(<<SCSS)
872
+ .foo.bar, .foo, .bar {
873
+ a: b; }
874
+ CSS
875
+ .foo.bar {a: b}
876
+ .foo {@extend .bar}
877
+ .bar {@extend .foo}
878
+ SCSS
879
+ end
880
+
881
+ def test_multiple_extender_merges_with_superset_selector
882
+ assert_equal <<CSS, render(<<SCSS)
883
+ a.bar.baz, a.foo {
884
+ a: b; }
885
+ CSS
886
+ .foo {@extend .bar; @extend .baz}
887
+ a.bar.baz {a: b}
888
+ SCSS
889
+ end
890
+
891
+ def test_control_flow_if
892
+ assert_equal <<CSS, render(<<SCSS)
893
+ .true, .also-true {
894
+ color: green; }
895
+
896
+ .false, .also-false {
897
+ color: red; }
898
+ CSS
899
+ .true { color: green; }
900
+ .false { color: red; }
901
+ .also-true {
902
+ @if true { @extend .true; }
903
+ @else { @extend .false; }
904
+ }
905
+ .also-false {
906
+ @if false { @extend .true; }
907
+ @else { @extend .false; }
908
+ }
909
+ SCSS
910
+ end
911
+
912
+ def test_control_flow_for
913
+ assert_equal <<CSS, render(<<SCSS)
914
+ .base-0, .added {
915
+ color: green; }
916
+
917
+ .base-1, .added {
918
+ display: block; }
919
+
920
+ .base-2, .added {
921
+ border: 1px solid blue; }
922
+ CSS
923
+ .base-0 { color: green; }
924
+ .base-1 { display: block; }
925
+ .base-2 { border: 1px solid blue; }
926
+ .added {
927
+ @for $i from 0 to 3 {
928
+ @extend .base-\#{$i};
929
+ }
930
+ }
931
+ SCSS
932
+ end
933
+
934
+ def test_control_flow_while
935
+ assert_equal <<CSS, render(<<SCSS)
936
+ .base-0, .added {
937
+ color: green; }
938
+
939
+ .base-1, .added {
940
+ display: block; }
941
+
942
+ .base-2, .added {
943
+ border: 1px solid blue; }
944
+ CSS
945
+ .base-0 { color: green; }
946
+ .base-1 { display: block; }
947
+ .base-2 { border: 1px solid blue; }
948
+ .added {
949
+ $i : 0;
950
+ @while $i < 3 {
951
+ @extend .base-\#{$i};
952
+ $i : $i + 1;
953
+ }
954
+ }
955
+ SCSS
956
+ end
957
+
958
+ def test_basic_placeholder_selector
959
+ assert_extends '%foo', '.bar {@extend %foo}', '.bar'
960
+ end
961
+
962
+ def test_unused_placeholder_selector
963
+ assert_equal <<CSS, render(<<SCSS)
964
+ .baz {
965
+ color: blue; }
966
+ CSS
967
+ %foo {color: blue}
968
+ %bar {color: red}
969
+ .baz {@extend %foo}
970
+ SCSS
971
+ end
972
+
973
+ def test_placeholder_descendant_selector
974
+ assert_extends '#context %foo a', '.bar {@extend %foo}', '#context .bar a'
975
+ end
976
+
977
+ def test_semi_placeholder_selector
978
+ assert_equal <<CSS, render(<<SCSS)
979
+ .bar .baz {
980
+ color: blue; }
981
+ CSS
982
+ #context %foo, .bar .baz {color: blue}
983
+ SCSS
984
+ end
985
+
986
+ def test_placeholder_selector_with_multiple_extenders
987
+ assert_equal <<CSS, render(<<SCSS)
988
+ .bar, .baz {
989
+ color: blue; }
990
+ CSS
991
+ %foo {color: blue}
992
+ .bar {@extend %foo}
993
+ .baz {@extend %foo}
994
+ SCSS
995
+ end
996
+
997
+ def test_placeholder_selector_as_modifier
998
+ assert_extend_doesnt_match('div', '%foo', :failed_to_unify, 3) do
999
+ render(<<SCSS)
1000
+ a%foo.baz {color: blue}
1001
+ .bar {@extend %foo}
1002
+ div {@extend %foo}
1003
+ SCSS
1004
+ end
1005
+ end
1006
+
1007
+ def test_placeholder_interpolation
1008
+ assert_equal <<CSS, render(<<SCSS)
1009
+ .bar {
1010
+ color: blue; }
1011
+ CSS
1012
+ $foo: foo;
1013
+
1014
+ %\#{$foo} {color: blue}
1015
+ .bar {@extend %foo}
1016
+ SCSS
1017
+ end
1018
+
1019
+ def test_placeholder_in_selector_pseudoclass
1020
+ assert_equal <<CSS, render(<<SCSS)
1021
+ :matches(.bar, .baz) {
1022
+ color: blue; }
1023
+ CSS
1024
+ :matches(%foo) {color: blue}
1025
+ .bar {@extend %foo}
1026
+ .baz {@extend %foo}
1027
+ SCSS
1028
+ end
1029
+
1030
+ def test_media_in_placeholder_selector
1031
+ assert_equal <<CSS, render(<<SCSS)
1032
+ .baz {
1033
+ c: d; }
1034
+ CSS
1035
+ %foo {bar {@media screen {a: b}}}
1036
+ .baz {c: d}
1037
+ SCSS
1038
+ end
1039
+
1040
+ def test_extend_out_of_media
1041
+ assert_raise_message(Sass::SyntaxError, <<ERR) {render(<<SCSS)}
1042
+ You may not @extend an outer selector from within @media.
1043
+ You may only @extend selectors within the same directive.
1044
+ From "@extend .foo" on line 3 of test_extend_out_of_media_inline.scss.
1045
+ ERR
1046
+ .foo {a: b}
1047
+ @media screen {
1048
+ .bar {@extend .foo}
1049
+ }
1050
+ SCSS
1051
+ end
1052
+
1053
+ def test_extend_out_of_unknown_directive
1054
+ assert_raise_message(Sass::SyntaxError, <<ERR) {render(<<SCSS)}
1055
+ You may not @extend an outer selector from within @flooblehoof.
1056
+ You may only @extend selectors within the same directive.
1057
+ From "@extend .foo" on line 3 of test_extend_out_of_unknown_directive_inline.scss.
1058
+ ERR
1059
+ .foo {a: b}
1060
+ @flooblehoof {
1061
+ .bar {@extend .foo}
1062
+ }
1063
+ SCSS
1064
+ end
1065
+
1066
+ def test_extend_out_of_nested_directives
1067
+ assert_raise_message(Sass::SyntaxError, <<ERR) {render(<<SCSS)}
1068
+ You may not @extend an outer selector from within @flooblehoof.
1069
+ You may only @extend selectors within the same directive.
1070
+ From "@extend .foo" on line 4 of test_extend_out_of_nested_directives_inline.scss.
1071
+ ERR
1072
+ @media screen {
1073
+ .foo {a: b}
1074
+ @flooblehoof {
1075
+ .bar {@extend .foo}
1076
+ }
1077
+ }
1078
+ SCSS
1079
+ end
1080
+
1081
+ def test_extend_within_media
1082
+ assert_equal(<<CSS, render(<<SCSS))
1083
+ @media screen {
1084
+ .foo, .bar {
1085
+ a: b; } }
1086
+ CSS
1087
+ @media screen {
1088
+ .foo {a: b}
1089
+ .bar {@extend .foo}
1090
+ }
1091
+ SCSS
1092
+ end
1093
+
1094
+ def test_extend_within_unknown_directive
1095
+ assert_equal(<<CSS, render(<<SCSS))
1096
+ @flooblehoof {
1097
+ .foo, .bar {
1098
+ a: b; } }
1099
+ CSS
1100
+ @flooblehoof {
1101
+ .foo {a: b}
1102
+ .bar {@extend .foo}
1103
+ }
1104
+ SCSS
1105
+ end
1106
+
1107
+ def test_extend_within_nested_directives
1108
+ assert_equal(<<CSS, render(<<SCSS))
1109
+ @media screen {
1110
+ @flooblehoof {
1111
+ .foo, .bar {
1112
+ a: b; } } }
1113
+ CSS
1114
+ @media screen {
1115
+ @flooblehoof {
1116
+ .foo {a: b}
1117
+ .bar {@extend .foo}
1118
+ }
1119
+ }
1120
+ SCSS
1121
+ end
1122
+
1123
+ def test_extend_within_disparate_media
1124
+ assert_equal(<<CSS, render(<<SCSS))
1125
+ @media screen {
1126
+ .foo, .bar {
1127
+ a: b; } }
1128
+ CSS
1129
+ @media screen {.foo {a: b}}
1130
+ @media screen {.bar {@extend .foo}}
1131
+ SCSS
1132
+ end
1133
+
1134
+ def test_extend_within_disparate_unknown_directive
1135
+ assert_equal(<<CSS, render(<<SCSS))
1136
+ @flooblehoof {
1137
+ .foo, .bar {
1138
+ a: b; } }
1139
+ @flooblehoof {}
1140
+ CSS
1141
+ @flooblehoof {.foo {a: b}}
1142
+ @flooblehoof {.bar {@extend .foo}}
1143
+ SCSS
1144
+ end
1145
+
1146
+ def test_extend_within_disparate_nested_directives
1147
+ assert_equal(<<CSS, render(<<SCSS))
1148
+ @media screen {
1149
+ @flooblehoof {
1150
+ .foo, .bar {
1151
+ a: b; } } }
1152
+ @media screen {
1153
+ @flooblehoof {} }
1154
+ CSS
1155
+ @media screen {@flooblehoof {.foo {a: b}}}
1156
+ @media screen {@flooblehoof {.bar {@extend .foo}}}
1157
+ SCSS
1158
+ end
1159
+
1160
+ def test_extend_within_and_without_media
1161
+ assert_raise_message(Sass::SyntaxError, <<ERR) {render(<<SCSS)}
1162
+ You may not @extend an outer selector from within @media.
1163
+ You may only @extend selectors within the same directive.
1164
+ From "@extend .foo" on line 4 of test_extend_within_and_without_media_inline.scss.
1165
+ ERR
1166
+ .foo {a: b}
1167
+ @media screen {
1168
+ .foo {c: d}
1169
+ .bar {@extend .foo}
1170
+ }
1171
+ SCSS
1172
+ end
1173
+
1174
+ def test_extend_within_and_without_unknown_directive
1175
+ assert_raise_message(Sass::SyntaxError, <<ERR) {render(<<SCSS)}
1176
+ You may not @extend an outer selector from within @flooblehoof.
1177
+ You may only @extend selectors within the same directive.
1178
+ From "@extend .foo" on line 4 of test_extend_within_and_without_unknown_directive_inline.scss.
1179
+ ERR
1180
+ .foo {a: b}
1181
+ @flooblehoof {
1182
+ .foo {c: d}
1183
+ .bar {@extend .foo}
1184
+ }
1185
+ SCSS
1186
+ end
1187
+
1188
+ def test_extend_within_and_without_nested_directives
1189
+ assert_raise_message(Sass::SyntaxError, <<ERR) {render(<<SCSS)}
1190
+ You may not @extend an outer selector from within @flooblehoof.
1191
+ You may only @extend selectors within the same directive.
1192
+ From "@extend .foo" on line 5 of test_extend_within_and_without_nested_directives_inline.scss.
1193
+ ERR
1194
+ @media screen {
1195
+ .foo {a: b}
1196
+ @flooblehoof {
1197
+ .foo {c: d}
1198
+ .bar {@extend .foo}
1199
+ }
1200
+ }
1201
+ SCSS
1202
+ end
1203
+
1204
+ def test_extend_with_subject_transfers_subject_to_extender
1205
+ silence_warnings {assert_equal(<<CSS, render(<<SCSS))}
1206
+ foo bar! baz, foo .bip .bap! baz, .bip foo .bap! baz {
1207
+ a: b; }
1208
+ CSS
1209
+ foo bar! baz {a: b}
1210
+ .bip .bap {@extend bar}
1211
+ SCSS
1212
+
1213
+ silence_warnings {assert_equal(<<CSS, render(<<SCSS))}
1214
+ foo.x bar.y! baz.z, foo.x .bip bar.bap! baz.z, .bip foo.x bar.bap! baz.z {
1215
+ a: b; }
1216
+ CSS
1217
+ foo.x bar.y! baz.z {a: b}
1218
+ .bip .bap {@extend .y}
1219
+ SCSS
1220
+ end
1221
+
1222
+ def test_extend_with_subject_retains_subject_on_target
1223
+ silence_warnings {assert_equal(<<CSS, render(<<SCSS))}
1224
+ .foo! .bar, .foo! .bip .bap, .bip .foo! .bap {
1225
+ a: b; }
1226
+ CSS
1227
+ .foo! .bar {a: b}
1228
+ .bip .bap {@extend .bar}
1229
+ SCSS
1230
+ end
1231
+
1232
+ def test_extend_with_subject_transfers_subject_to_target
1233
+ silence_warnings {assert_equal(<<CSS, render(<<SCSS))}
1234
+ a.foo .bar, .bip a.bap! .bar {
1235
+ a: b; }
1236
+ CSS
1237
+ a.foo .bar {a: b}
1238
+ .bip .bap! {@extend .foo}
1239
+ SCSS
1240
+ end
1241
+
1242
+ def test_extend_with_subject_retains_subject_on_extender
1243
+ silence_warnings {assert_equal(<<CSS, render(<<SCSS))}
1244
+ .foo .bar, .foo .bip! .bap, .bip! .foo .bap {
1245
+ a: b; }
1246
+ CSS
1247
+ .foo .bar {a: b}
1248
+ .bip! .bap {@extend .bar}
1249
+ SCSS
1250
+ end
1251
+
1252
+ def test_extend_with_subject_fails_with_conflicting_subject
1253
+ silence_warnings {assert_equal(<<CSS, render(<<SCSS))}
1254
+ x! .bar {
1255
+ a: b; }
1256
+ CSS
1257
+ x! .bar {a: b}
1258
+ y! .bap {@extend .bar}
1259
+ SCSS
1260
+ end
1261
+
1262
+ def test_extend_warns_when_extendee_doesnt_exist
1263
+ assert_raise_message(Sass::SyntaxError, <<ERR) {render(<<SCSS)}
1264
+ ".foo" failed to @extend ".bar".
1265
+ The selector ".bar" was not found.
1266
+ Use "@extend .bar !optional" if the extend should be able to fail.
1267
+ ERR
1268
+ .foo {@extend .bar}
1269
+ SCSS
1270
+ end
1271
+
1272
+ def test_extend_warns_when_extension_fails
1273
+ assert_raise_message(Sass::SyntaxError, <<ERR) {render(<<SCSS)}
1274
+ "b.foo" failed to @extend ".bar".
1275
+ No selectors matching ".bar" could be unified with "b.foo".
1276
+ Use "@extend .bar !optional" if the extend should be able to fail.
1277
+ ERR
1278
+ a.bar {a: b}
1279
+ b.foo {@extend .bar}
1280
+ SCSS
1281
+ end
1282
+
1283
+ def test_extend_succeeds_when_one_extension_fails_but_others_dont
1284
+ assert_equal(<<CSS, render(<<SCSS))
1285
+ a.bar {
1286
+ a: b; }
1287
+
1288
+ .bar, b.foo {
1289
+ c: d; }
1290
+ CSS
1291
+ a.bar {a: b}
1292
+ .bar {c: d}
1293
+ b.foo {@extend .bar}
1294
+ SCSS
1295
+ end
1296
+
1297
+ def test_optional_extend_succeeds_when_extendee_doesnt_exist
1298
+ assert_equal("", render(<<SCSS))
1299
+ .foo {@extend .bar !optional}
1300
+ SCSS
1301
+ end
1302
+
1303
+ def test_optional_extend_succeeds_when_extension_fails
1304
+ assert_equal(<<CSS, render(<<SCSS))
1305
+ a.bar {
1306
+ a: b; }
1307
+ CSS
1308
+ a.bar {a: b}
1309
+ b.foo {@extend .bar !optional}
1310
+ SCSS
1311
+ end
1312
+
1313
+ # Regression Tests
1314
+
1315
+ def test_extend_parent_selector_suffix
1316
+ assert_equal <<CSS, render(<<SCSS)
1317
+ .a-b, .c {
1318
+ x: y; }
1319
+ CSS
1320
+ .a {&-b {x: y}}
1321
+ .c {@extend .a-b}
1322
+ SCSS
1323
+ end
1324
+
1325
+ def test_pseudo_element_superselector
1326
+ # Pseudo-elements shouldn't be removed in superselector calculations.
1327
+ assert_equal <<CSS, render(<<SCSS)
1328
+ a#bar, a#bar::fblthp {
1329
+ a: b; }
1330
+ CSS
1331
+ %x#bar {a: b} // Add an id to make the results have high specificity
1332
+ %y, %y::fblthp {@extend %x}
1333
+ a {@extend %y}
1334
+ SCSS
1335
+
1336
+ # Pseudo-classes can be removed when the second law allows.
1337
+ assert_equal <<CSS, render(<<SCSS)
1338
+ a#bar {
1339
+ a: b; }
1340
+ CSS
1341
+ %x#bar {a: b}
1342
+ %y, %y:fblthp {@extend %x}
1343
+ a {@extend %y}
1344
+ SCSS
1345
+
1346
+ # A few pseudo-elements can be written as pseudo-elements for historical
1347
+ # reasons. See http://www.w3.org/TR/selectors4/#pseudo-elements.
1348
+ %w[first-line first-letter before after].each do |pseudo|
1349
+ assert_equal <<CSS, render(<<SCSS)
1350
+ a#bar, a#bar:#{pseudo} {
1351
+ a: b; }
1352
+ CSS
1353
+ %x#bar {a: b}
1354
+ %y, %y:#{pseudo} {@extend %x}
1355
+ a {@extend %y}
1356
+ SCSS
1357
+ end
1358
+ end
1359
+
1360
+ def test_multiple_source_redundancy_elimination
1361
+ assert_equal <<CSS, render(<<SCSS)
1362
+ .test-case, .test-case:active {
1363
+ color: red; }
1364
+
1365
+ .test-case:hover {
1366
+ color: green; }
1367
+ CSS
1368
+ %default-color {color: red}
1369
+ %alt-color {color: green}
1370
+
1371
+ %default-style {
1372
+ @extend %default-color;
1373
+ &:hover {@extend %alt-color}
1374
+ &:active {@extend %default-color}
1375
+ }
1376
+
1377
+ .test-case {@extend %default-style}
1378
+ SCSS
1379
+ end
1380
+
1381
+ def test_nested_sibling_extend
1382
+ assert_equal <<CSS, render(<<SCSS)
1383
+ .parent .bar, .parent .foo {
1384
+ width: 2000px; }
1385
+ CSS
1386
+ .foo {@extend .bar}
1387
+
1388
+ .parent {
1389
+ .bar {
1390
+ width: 2000px;
1391
+ }
1392
+ .foo {
1393
+ @extend .bar
1394
+ }
1395
+ }
1396
+ SCSS
1397
+ end
1398
+
1399
+ def test_parent_and_sibling_extend
1400
+ assert_equal <<CSS, render(<<SCSS)
1401
+ .parent1 .parent2 .child1.child2, .parent2 .parent1 .child1.child2 {
1402
+ c: d; }
1403
+ CSS
1404
+ %foo %bar%baz {c: d}
1405
+
1406
+ .parent1 {
1407
+ @extend %foo;
1408
+ .child1 {@extend %bar}
1409
+ }
1410
+
1411
+ .parent2 {
1412
+ @extend %foo;
1413
+ .child2 {@extend %baz}
1414
+ }
1415
+ SCSS
1416
+ end
1417
+
1418
+ def test_nested_extend_specificity
1419
+ assert_equal <<CSS, render(<<SCSS)
1420
+ a :b, a :b:c {
1421
+ a: b; }
1422
+ CSS
1423
+ %foo {a: b}
1424
+
1425
+ a {
1426
+ :b {@extend %foo}
1427
+ :b:c {@extend %foo}
1428
+ }
1429
+ SCSS
1430
+ end
1431
+
1432
+ def test_nested_double_extend_optimization
1433
+ assert_equal <<CSS, render(<<SCSS)
1434
+ .parent1 .child {
1435
+ a: b; }
1436
+ CSS
1437
+ %foo %bar {
1438
+ a: b;
1439
+ }
1440
+
1441
+ .parent1 {
1442
+ @extend %foo;
1443
+
1444
+ .child {
1445
+ @extend %bar;
1446
+ }
1447
+ }
1448
+
1449
+ .parent2 {
1450
+ @extend %foo;
1451
+ }
1452
+ SCSS
1453
+ end
1454
+
1455
+ def test_extend_in_double_nested_media_query
1456
+ assert_equal <<CSS, render(<<SCSS)
1457
+ @media all and (orientation: landscape) {
1458
+ .bar {
1459
+ color: blue; } }
1460
+ CSS
1461
+ @media all {
1462
+ @media (orientation: landscape) {
1463
+ %foo {color: blue}
1464
+ .bar {@extend %foo}
1465
+ }
1466
+ }
1467
+ SCSS
1468
+ end
1469
+
1470
+ def test_partially_failed_extend
1471
+ assert_no_warning {assert_equal(<<CSS, render(<<SCSS))}
1472
+ .rc, test {
1473
+ color: white; }
1474
+
1475
+ .prices span.pill span.rc {
1476
+ color: red; }
1477
+ CSS
1478
+ test { @extend .rc; }
1479
+ .rc {color: white;}
1480
+ .prices span.pill span.rc {color: red;}
1481
+ SCSS
1482
+ end
1483
+
1484
+ def test_newline_near_combinator
1485
+ assert_equal <<CSS, render(<<SCSS)
1486
+ .a +
1487
+ .b x, .a +
1488
+ .b .c y, .c .a +
1489
+ .b y {
1490
+ a: b; }
1491
+ CSS
1492
+ .a +
1493
+ .b x {a: b}
1494
+ .c y {@extend x}
1495
+ SCSS
1496
+ end
1497
+
1498
+ def test_duplicated_selector_with_newlines
1499
+ assert_equal(<<CSS, render(<<SCSS))
1500
+ .example-1-1,
1501
+ .example-1-2,
1502
+ .my-page-1 .my-module-1-1,
1503
+ .example-1-3 {
1504
+ a: b; }
1505
+ CSS
1506
+ .example-1-1,
1507
+ .example-1-2,
1508
+ .example-1-3 {
1509
+ a: b;
1510
+ }
1511
+
1512
+ .my-page-1 .my-module-1-1 {@extend .example-1-2}
1513
+ SCSS
1514
+ end
1515
+
1516
+ def test_nested_selector_with_child_selector_hack_extendee
1517
+ assert_extends '> .foo', 'foo bar {@extend .foo}', '> .foo, > foo bar'
1518
+ end
1519
+
1520
+ def test_nested_selector_with_child_selector_hack_extender
1521
+ assert_extends '.foo .bar', '> foo bar {@extend .bar}', '.foo .bar, > .foo foo bar, > foo .foo bar'
1522
+ end
1523
+
1524
+ def test_nested_selector_with_child_selector_hack_extender_and_extendee
1525
+ assert_extends '> .foo', '> foo bar {@extend .foo}', '> .foo, > foo bar'
1526
+ end
1527
+
1528
+ def test_nested_selector_with_child_selector_hack_extender_and_sibling_selector_extendee
1529
+ assert_extends '~ .foo', '> foo bar {@extend .foo}', '~ .foo'
1530
+ end
1531
+
1532
+ def test_nested_selector_with_child_selector_hack_extender_and_extendee_and_newline
1533
+ assert_equal <<CSS, render(<<SCSS)
1534
+ > .foo, > flip,
1535
+ > foo bar {
1536
+ a: b; }
1537
+ CSS
1538
+ > .foo {a: b}
1539
+ flip,
1540
+ > foo bar {@extend .foo}
1541
+ SCSS
1542
+ end
1543
+
1544
+ def test_extended_parent_and_child_redundancy_elimination
1545
+ assert_equal <<CSS, render(<<SCSS)
1546
+ a b, d b, a c, d c {
1547
+ a: b; }
1548
+ CSS
1549
+ a {
1550
+ b {a: b}
1551
+ c {@extend b}
1552
+ }
1553
+ d {@extend a}
1554
+ SCSS
1555
+ end
1556
+
1557
+ def test_extend_redundancy_elimination_when_it_would_reduce_specificity
1558
+ assert_extends 'a', 'a.foo {@extend a}', 'a, a.foo'
1559
+ end
1560
+
1561
+ def test_extend_redundancy_elimination_when_it_would_preserve_specificity
1562
+ assert_extends '.bar a', 'a.foo {@extend a}', '.bar a'
1563
+ end
1564
+
1565
+ def test_extend_redundancy_elimination_never_eliminates_base_selector
1566
+ assert_extends 'a.foo', '.foo {@extend a}', 'a.foo, .foo'
1567
+ end
1568
+
1569
+ def test_extend_cross_branch_redundancy_elimination
1570
+ assert_equal <<CSS, render(<<SCSS)
1571
+ .a .c .d, .b .c .a .d {
1572
+ a: b; }
1573
+ CSS
1574
+ %x .c %y {a: b}
1575
+ .a, .b {@extend %x}
1576
+ .a .d {@extend %y}
1577
+ SCSS
1578
+
1579
+ assert_equal <<CSS, render(<<SCSS)
1580
+ .e .a .c .d, .a .c .e .d, .e .b .c .a .d, .b .c .a .e .d {
1581
+ a: b; }
1582
+ CSS
1583
+ .e %z {a: b}
1584
+ %x .c %y {@extend %z}
1585
+ .a, .b {@extend %x}
1586
+ .a .d {@extend %y}
1587
+ SCSS
1588
+ end
1589
+
1590
+ private
1591
+
1592
+ def assert_extend_doesnt_match(extender, target, reason, line, syntax = :scss)
1593
+ message = "\"#{extender}\" failed to @extend \"#{target}\"."
1594
+ reason =
1595
+ if reason == :not_found
1596
+ "The selector \"#{target}\" was not found."
1597
+ else
1598
+ "No selectors matching \"#{target}\" could be unified with \"#{extender}\"."
1599
+ end
1600
+
1601
+ assert_raise_message(Sass::SyntaxError, <<ERR) {yield}
1602
+ #{message}
1603
+ #{reason}
1604
+ Use "@extend #{target} !optional" if the extend should be able to fail.
1605
+ ERR
1606
+ end
1607
+
1608
+ def assert_unification(selector, extension, unified, nested = true)
1609
+ # Do some trickery so the first law of extend doesn't get in our way.
1610
+ assert_extends(
1611
+ "%-a #{selector}",
1612
+ extension + " -a {@extend %-a}",
1613
+ unified.split(', ').map {|s| "-a #{s}"}.join(', '))
1614
+ end
1615
+
1616
+ def assert_specificity_equals(sel1, sel2)
1617
+ assert_specificity_gte(sel1, sel2)
1618
+ assert_specificity_gte(sel2, sel1)
1619
+ end
1620
+
1621
+ def assert_specificity_gte(sel1, sel2)
1622
+ assert_equal <<CSS, render(<<SCSS)
1623
+ #{sel1} .a {
1624
+ a: b; }
1625
+ CSS
1626
+ #{sel1} %-a {a: b}
1627
+ .a {@extend %-a}
1628
+ #{sel2}.a {@extend %-a}
1629
+ SCSS
1630
+ end
1631
+
1632
+ def render_unification(selector, extension)
1633
+ render_extends(
1634
+ "%-a #{selector}",
1635
+ extension + " -a {@extend %-a}")
1636
+ end
1637
+
1638
+ def assert_extends(selector, extension, result)
1639
+ assert_equal <<CSS, render_extends(selector, extension)
1640
+ #{result} {
1641
+ a: b; }
1642
+ CSS
1643
+ end
1644
+
1645
+ def assert_extends_to_nothing(selector, extension)
1646
+ assert_equal '', render_extends(selector, extension)
1647
+ end
1648
+
1649
+ def render_extends(selector, extension)
1650
+ render(<<SCSS)
1651
+ #{selector} {a: b}
1652
+ #{extension}
1653
+ SCSS
1654
+ end
1655
+
1656
+ def render(sass, options = {})
1657
+ options = {:syntax => :scss}.merge(options)
1658
+ munge_filename options
1659
+ Sass::Engine.new(sass, options).render
1660
+ end
1661
+ end