aliddle-sass 1.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 (238) hide show
  1. data/.yardopts +11 -0
  2. data/CONTRIBUTING +3 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +201 -0
  5. data/Rakefile +347 -0
  6. data/VERSION +1 -0
  7. data/VERSION_NAME +1 -0
  8. data/bin/sass +9 -0
  9. data/bin/sass-convert +8 -0
  10. data/bin/scss +9 -0
  11. data/extra/update_watch.rb +13 -0
  12. data/init.rb +18 -0
  13. data/lib/sass.rb +95 -0
  14. data/lib/sass/cache_stores.rb +15 -0
  15. data/lib/sass/cache_stores/base.rb +88 -0
  16. data/lib/sass/cache_stores/chain.rb +33 -0
  17. data/lib/sass/cache_stores/filesystem.rb +60 -0
  18. data/lib/sass/cache_stores/memory.rb +47 -0
  19. data/lib/sass/cache_stores/null.rb +25 -0
  20. data/lib/sass/callbacks.rb +66 -0
  21. data/lib/sass/css.rb +409 -0
  22. data/lib/sass/engine.rb +928 -0
  23. data/lib/sass/environment.rb +101 -0
  24. data/lib/sass/error.rb +201 -0
  25. data/lib/sass/exec.rb +707 -0
  26. data/lib/sass/importers.rb +22 -0
  27. data/lib/sass/importers/base.rb +139 -0
  28. data/lib/sass/importers/filesystem.rb +190 -0
  29. data/lib/sass/logger.rb +15 -0
  30. data/lib/sass/logger/base.rb +32 -0
  31. data/lib/sass/logger/log_level.rb +49 -0
  32. data/lib/sass/media.rb +213 -0
  33. data/lib/sass/plugin.rb +132 -0
  34. data/lib/sass/plugin/compiler.rb +406 -0
  35. data/lib/sass/plugin/configuration.rb +123 -0
  36. data/lib/sass/plugin/generic.rb +15 -0
  37. data/lib/sass/plugin/merb.rb +48 -0
  38. data/lib/sass/plugin/rack.rb +60 -0
  39. data/lib/sass/plugin/rails.rb +47 -0
  40. data/lib/sass/plugin/staleness_checker.rb +183 -0
  41. data/lib/sass/railtie.rb +9 -0
  42. data/lib/sass/repl.rb +57 -0
  43. data/lib/sass/root.rb +7 -0
  44. data/lib/sass/script.rb +39 -0
  45. data/lib/sass/script/arg_list.rb +52 -0
  46. data/lib/sass/script/bool.rb +18 -0
  47. data/lib/sass/script/color.rb +606 -0
  48. data/lib/sass/script/css_lexer.rb +29 -0
  49. data/lib/sass/script/css_parser.rb +31 -0
  50. data/lib/sass/script/funcall.rb +237 -0
  51. data/lib/sass/script/functions.rb +1543 -0
  52. data/lib/sass/script/interpolation.rb +79 -0
  53. data/lib/sass/script/lexer.rb +348 -0
  54. data/lib/sass/script/list.rb +85 -0
  55. data/lib/sass/script/literal.rb +221 -0
  56. data/lib/sass/script/node.rb +99 -0
  57. data/lib/sass/script/null.rb +37 -0
  58. data/lib/sass/script/number.rb +453 -0
  59. data/lib/sass/script/operation.rb +110 -0
  60. data/lib/sass/script/parser.rb +495 -0
  61. data/lib/sass/script/string.rb +51 -0
  62. data/lib/sass/script/string_interpolation.rb +103 -0
  63. data/lib/sass/script/unary_operation.rb +69 -0
  64. data/lib/sass/script/variable.rb +58 -0
  65. data/lib/sass/scss.rb +16 -0
  66. data/lib/sass/scss/css_parser.rb +36 -0
  67. data/lib/sass/scss/parser.rb +1179 -0
  68. data/lib/sass/scss/rx.rb +133 -0
  69. data/lib/sass/scss/script_lexer.rb +15 -0
  70. data/lib/sass/scss/script_parser.rb +25 -0
  71. data/lib/sass/scss/static_parser.rb +54 -0
  72. data/lib/sass/selector.rb +452 -0
  73. data/lib/sass/selector/abstract_sequence.rb +94 -0
  74. data/lib/sass/selector/comma_sequence.rb +92 -0
  75. data/lib/sass/selector/sequence.rb +507 -0
  76. data/lib/sass/selector/simple.rb +119 -0
  77. data/lib/sass/selector/simple_sequence.rb +212 -0
  78. data/lib/sass/shared.rb +76 -0
  79. data/lib/sass/supports.rb +229 -0
  80. data/lib/sass/tree/charset_node.rb +22 -0
  81. data/lib/sass/tree/comment_node.rb +82 -0
  82. data/lib/sass/tree/content_node.rb +9 -0
  83. data/lib/sass/tree/css_import_node.rb +60 -0
  84. data/lib/sass/tree/debug_node.rb +18 -0
  85. data/lib/sass/tree/directive_node.rb +42 -0
  86. data/lib/sass/tree/each_node.rb +24 -0
  87. data/lib/sass/tree/extend_node.rb +36 -0
  88. data/lib/sass/tree/for_node.rb +36 -0
  89. data/lib/sass/tree/function_node.rb +34 -0
  90. data/lib/sass/tree/if_node.rb +52 -0
  91. data/lib/sass/tree/import_node.rb +75 -0
  92. data/lib/sass/tree/media_node.rb +58 -0
  93. data/lib/sass/tree/mixin_def_node.rb +38 -0
  94. data/lib/sass/tree/mixin_node.rb +39 -0
  95. data/lib/sass/tree/node.rb +196 -0
  96. data/lib/sass/tree/prop_node.rb +152 -0
  97. data/lib/sass/tree/return_node.rb +18 -0
  98. data/lib/sass/tree/root_node.rb +28 -0
  99. data/lib/sass/tree/rule_node.rb +132 -0
  100. data/lib/sass/tree/supports_node.rb +51 -0
  101. data/lib/sass/tree/trace_node.rb +32 -0
  102. data/lib/sass/tree/variable_node.rb +30 -0
  103. data/lib/sass/tree/visitors/base.rb +75 -0
  104. data/lib/sass/tree/visitors/check_nesting.rb +147 -0
  105. data/lib/sass/tree/visitors/convert.rb +316 -0
  106. data/lib/sass/tree/visitors/cssize.rb +229 -0
  107. data/lib/sass/tree/visitors/deep_copy.rb +102 -0
  108. data/lib/sass/tree/visitors/extend.rb +68 -0
  109. data/lib/sass/tree/visitors/perform.rb +446 -0
  110. data/lib/sass/tree/visitors/set_options.rb +125 -0
  111. data/lib/sass/tree/visitors/to_css.rb +230 -0
  112. data/lib/sass/tree/warn_node.rb +18 -0
  113. data/lib/sass/tree/while_node.rb +18 -0
  114. data/lib/sass/util.rb +906 -0
  115. data/lib/sass/util/multibyte_string_scanner.rb +155 -0
  116. data/lib/sass/util/subset_map.rb +109 -0
  117. data/lib/sass/util/test.rb +10 -0
  118. data/lib/sass/version.rb +126 -0
  119. data/rails/init.rb +1 -0
  120. data/test/Gemfile +3 -0
  121. data/test/Gemfile.lock +10 -0
  122. data/test/sass/cache_test.rb +89 -0
  123. data/test/sass/callbacks_test.rb +61 -0
  124. data/test/sass/conversion_test.rb +1760 -0
  125. data/test/sass/css2sass_test.rb +439 -0
  126. data/test/sass/data/hsl-rgb.txt +319 -0
  127. data/test/sass/engine_test.rb +3243 -0
  128. data/test/sass/exec_test.rb +86 -0
  129. data/test/sass/extend_test.rb +1461 -0
  130. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  131. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  132. data/test/sass/functions_test.rb +1139 -0
  133. data/test/sass/importer_test.rb +192 -0
  134. data/test/sass/logger_test.rb +58 -0
  135. data/test/sass/mock_importer.rb +49 -0
  136. data/test/sass/more_results/more1.css +9 -0
  137. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  138. data/test/sass/more_results/more_import.css +29 -0
  139. data/test/sass/more_templates/_more_partial.sass +2 -0
  140. data/test/sass/more_templates/more1.sass +23 -0
  141. data/test/sass/more_templates/more_import.sass +11 -0
  142. data/test/sass/plugin_test.rb +550 -0
  143. data/test/sass/results/alt.css +4 -0
  144. data/test/sass/results/basic.css +9 -0
  145. data/test/sass/results/cached_import_option.css +3 -0
  146. data/test/sass/results/compact.css +5 -0
  147. data/test/sass/results/complex.css +86 -0
  148. data/test/sass/results/compressed.css +1 -0
  149. data/test/sass/results/expanded.css +19 -0
  150. data/test/sass/results/filename_fn.css +3 -0
  151. data/test/sass/results/if.css +3 -0
  152. data/test/sass/results/import.css +31 -0
  153. data/test/sass/results/import_charset.css +5 -0
  154. data/test/sass/results/import_charset_1_8.css +5 -0
  155. data/test/sass/results/import_charset_ibm866.css +5 -0
  156. data/test/sass/results/import_content.css +1 -0
  157. data/test/sass/results/line_numbers.css +49 -0
  158. data/test/sass/results/mixins.css +95 -0
  159. data/test/sass/results/multiline.css +24 -0
  160. data/test/sass/results/nested.css +22 -0
  161. data/test/sass/results/options.css +1 -0
  162. data/test/sass/results/parent_ref.css +13 -0
  163. data/test/sass/results/script.css +16 -0
  164. data/test/sass/results/scss_import.css +31 -0
  165. data/test/sass/results/scss_importee.css +2 -0
  166. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  167. data/test/sass/results/subdir/subdir.css +3 -0
  168. data/test/sass/results/units.css +11 -0
  169. data/test/sass/results/warn.css +0 -0
  170. data/test/sass/results/warn_imported.css +0 -0
  171. data/test/sass/script_conversion_test.rb +299 -0
  172. data/test/sass/script_test.rb +591 -0
  173. data/test/sass/scss/css_test.rb +1093 -0
  174. data/test/sass/scss/rx_test.rb +156 -0
  175. data/test/sass/scss/scss_test.rb +2043 -0
  176. data/test/sass/scss/test_helper.rb +37 -0
  177. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  178. data/test/sass/templates/_double_import_loop2.sass +1 -0
  179. data/test/sass/templates/_filename_fn_import.scss +11 -0
  180. data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
  181. data/test/sass/templates/_imported_charset_utf8.sass +4 -0
  182. data/test/sass/templates/_imported_content.sass +3 -0
  183. data/test/sass/templates/_partial.sass +2 -0
  184. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  185. data/test/sass/templates/alt.sass +16 -0
  186. data/test/sass/templates/basic.sass +23 -0
  187. data/test/sass/templates/bork1.sass +2 -0
  188. data/test/sass/templates/bork2.sass +2 -0
  189. data/test/sass/templates/bork3.sass +2 -0
  190. data/test/sass/templates/bork4.sass +2 -0
  191. data/test/sass/templates/bork5.sass +3 -0
  192. data/test/sass/templates/cached_import_option.scss +3 -0
  193. data/test/sass/templates/compact.sass +17 -0
  194. data/test/sass/templates/complex.sass +305 -0
  195. data/test/sass/templates/compressed.sass +15 -0
  196. data/test/sass/templates/double_import_loop1.sass +1 -0
  197. data/test/sass/templates/expanded.sass +17 -0
  198. data/test/sass/templates/filename_fn.scss +18 -0
  199. data/test/sass/templates/if.sass +11 -0
  200. data/test/sass/templates/import.sass +12 -0
  201. data/test/sass/templates/import_charset.sass +9 -0
  202. data/test/sass/templates/import_charset_1_8.sass +6 -0
  203. data/test/sass/templates/import_charset_ibm866.sass +11 -0
  204. data/test/sass/templates/import_content.sass +4 -0
  205. data/test/sass/templates/importee.less +2 -0
  206. data/test/sass/templates/importee.sass +19 -0
  207. data/test/sass/templates/line_numbers.sass +13 -0
  208. data/test/sass/templates/mixin_bork.sass +5 -0
  209. data/test/sass/templates/mixins.sass +76 -0
  210. data/test/sass/templates/multiline.sass +20 -0
  211. data/test/sass/templates/nested.sass +25 -0
  212. data/test/sass/templates/nested_bork1.sass +2 -0
  213. data/test/sass/templates/nested_bork2.sass +2 -0
  214. data/test/sass/templates/nested_bork3.sass +2 -0
  215. data/test/sass/templates/nested_bork4.sass +2 -0
  216. data/test/sass/templates/nested_import.sass +2 -0
  217. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  218. data/test/sass/templates/options.sass +2 -0
  219. data/test/sass/templates/parent_ref.sass +25 -0
  220. data/test/sass/templates/same_name_different_ext.sass +2 -0
  221. data/test/sass/templates/same_name_different_ext.scss +1 -0
  222. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  223. data/test/sass/templates/script.sass +101 -0
  224. data/test/sass/templates/scss_import.scss +11 -0
  225. data/test/sass/templates/scss_importee.scss +1 -0
  226. data/test/sass/templates/single_import_loop.sass +1 -0
  227. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  228. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  229. data/test/sass/templates/subdir/subdir.sass +6 -0
  230. data/test/sass/templates/units.sass +11 -0
  231. data/test/sass/templates/warn.sass +3 -0
  232. data/test/sass/templates/warn_imported.sass +4 -0
  233. data/test/sass/test_helper.rb +8 -0
  234. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  235. data/test/sass/util/subset_map_test.rb +91 -0
  236. data/test/sass/util_test.rb +313 -0
  237. data/test/test_helper.rb +80 -0
  238. metadata +348 -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 < Test::Unit::TestCase
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 -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,1461 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../test_helper'
3
+
4
+ class ExtendTest < Test::Unit::TestCase
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
+ assert_unification '.foo#baz', '#bar {@extend .foo}', '.foo#baz'
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
+ assert_unification 'ns1|*.foo', 'ns2|* {@extend .foo}', 'ns1|*.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
+ assert_unification 'ns1|a.foo', 'ns2|* {@extend .foo}', 'ns1|a.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
+ assert_unification 'ns1|*.foo', 'ns2|a {@extend .foo}', 'ns1|*.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
+ assert_unification 'a.foo', 'h1 {@extend .foo}', 'a.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
+ assert_unification 'ns1|a.foo', 'ns2|a {@extend .foo}', 'ns1|a.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
+ assert_unification '::foo.baz', '::bar {@extend .baz}', '::foo.baz'
274
+ end
275
+
276
+ assert_extend_doesnt_match('::foo(2n+1)', '.baz', :failed_to_unify, 2) do
277
+ assert_unification '::foo.baz', '::foo(2n+1) {@extend .baz}', '::foo.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_unification ':not(.foo).baz', ':not(.bar) {@extend .baz}', ':not(.foo).baz, :not(.foo):not(.bar)'
314
+ assert_unification ':not(.foo).baz', ':not(.foo) {@extend .baz}', ':not(.foo)'
315
+ assert_unification ':not([a=b]).baz', ':not([a = b]) {@extend .baz}', ':not([a=b])'
316
+ end
317
+
318
+ def test_comma_extendee
319
+ assert_equal <<CSS, render(<<SCSS)
320
+ .foo, .baz {
321
+ a: b; }
322
+
323
+ .bar, .baz {
324
+ c: d; }
325
+ CSS
326
+ .foo {a: b}
327
+ .bar {c: d}
328
+ .baz {@extend .foo, .bar}
329
+ SCSS
330
+ end
331
+
332
+ def test_redundant_selector_elimination
333
+ assert_equal <<CSS, render(<<SCSS)
334
+ .foo.bar, .x, .y {
335
+ a: b; }
336
+ CSS
337
+ .foo.bar {a: b}
338
+ .x {@extend .foo, .bar}
339
+ .y {@extend .foo, .bar}
340
+ SCSS
341
+ end
342
+
343
+ ## Long Extendees
344
+
345
+ def test_long_extendee
346
+ assert_extends '.foo.bar', '.baz {@extend .foo.bar}', '.foo.bar, .baz'
347
+ end
348
+
349
+ def test_long_extendee_requires_all_selectors
350
+ assert_extend_doesnt_match('.baz', '.foo.bar', :not_found, 2) do
351
+ assert_extends '.foo', '.baz {@extend .foo.bar}', '.foo'
352
+ end
353
+ end
354
+
355
+ def test_long_extendee_matches_supersets
356
+ assert_extends '.foo.bar.bap', '.baz {@extend .foo.bar}', '.foo.bar.bap, .bap.baz'
357
+ end
358
+
359
+ def test_long_extendee_runs_unification
360
+ assert_extends 'ns|*.foo.bar', 'a.baz {@extend .foo.bar}', 'ns|*.foo.bar, ns|a.baz'
361
+ end
362
+
363
+ ## Long Extenders
364
+
365
+ def test_long_extender
366
+ assert_extends '.foo.bar', '.baz.bang {@extend .foo}', '.foo.bar, .bar.baz.bang'
367
+ end
368
+
369
+ def test_long_extender_runs_unification
370
+ assert_extends 'ns|*.foo.bar', 'a.baz {@extend .foo}', 'ns|*.foo.bar, ns|a.bar.baz'
371
+ end
372
+
373
+ def test_long_extender_aborts_unification
374
+ assert_extend_doesnt_match('h1.baz', '.foo', :failed_to_unify, 2) do
375
+ assert_extends 'a.foo#bar', 'h1.baz {@extend .foo}', 'a.foo#bar'
376
+ end
377
+
378
+ assert_extend_doesnt_match('.bang#baz', '.foo', :failed_to_unify, 2) do
379
+ assert_extends 'a.foo#bar', '.bang#baz {@extend .foo}', 'a.foo#bar'
380
+ end
381
+ end
382
+
383
+ ## Nested Extenders
384
+
385
+ def test_nested_extender
386
+ assert_extends '.foo', 'foo bar {@extend .foo}', '.foo, foo bar'
387
+ end
388
+
389
+ def test_nested_extender_runs_unification
390
+ assert_extends '.foo.bar', 'foo bar {@extend .foo}', '.foo.bar, foo bar.bar'
391
+ end
392
+
393
+ def test_nested_extender_aborts_unification
394
+ assert_extend_doesnt_match('foo bar', '.foo', :failed_to_unify, 2) do
395
+ assert_extends 'baz.foo', 'foo bar {@extend .foo}', 'baz.foo'
396
+ end
397
+ end
398
+
399
+ def test_nested_extender_alternates_parents
400
+ assert_extends('.baz .bip .foo', 'foo .grank bar {@extend .foo}',
401
+ '.baz .bip .foo, .baz .bip foo .grank bar, foo .grank .baz .bip bar')
402
+ end
403
+
404
+ def test_nested_extender_unifies_identical_parents
405
+ assert_extends('.baz .bip .foo', '.baz .bip bar {@extend .foo}',
406
+ '.baz .bip .foo, .baz .bip bar')
407
+ end
408
+
409
+ def test_nested_extender_unifies_common_substring
410
+ assert_extends('.baz .bip .bap .bink .foo', '.brat .bip .bap bar {@extend .foo}',
411
+ '.baz .bip .bap .bink .foo, .baz .brat .bip .bap .bink bar, .brat .baz .bip .bap .bink bar')
412
+ end
413
+
414
+ def test_nested_extender_unifies_common_subseq
415
+ assert_extends('.a .x .b .y .foo', '.a .n .b .m bar {@extend .foo}',
416
+ '.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')
417
+ end
418
+
419
+ def test_nested_extender_chooses_first_subseq
420
+ assert_extends('.a .b .c .d .foo', '.c .d .a .b .bar {@extend .foo}',
421
+ '.a .b .c .d .foo, .a .b .c .d .a .b .bar')
422
+ end
423
+
424
+ def test_nested_extender_counts_extended_subselectors
425
+ assert_extends('.a .bip.bop .foo', '.b .bip .bar {@extend .foo}',
426
+ '.a .bip.bop .foo, .a .b .bip.bop .bar, .b .a .bip.bop .bar')
427
+ end
428
+
429
+ def test_nested_extender_counts_extended_superselectors
430
+ assert_extends('.a .bip .foo', '.b .bip.bop .bar {@extend .foo}',
431
+ '.a .bip .foo, .a .b .bip.bop .bar, .b .a .bip.bop .bar')
432
+ end
433
+
434
+ def test_nested_extender_with_child_selector
435
+ assert_extends '.baz .foo', 'foo > bar {@extend .foo}', '.baz .foo, .baz foo > bar'
436
+ end
437
+
438
+ def test_nested_extender_finds_common_selectors_around_child_selector
439
+ assert_extends 'a > b c .c1', 'a c .c2 {@extend .c1}', 'a > b c .c1, a > b c .c2'
440
+ assert_extends 'a > b c .c1', 'b c .c2 {@extend .c1}', 'a > b c .c1, a > b c .c2'
441
+ end
442
+
443
+ def test_nested_extender_doesnt_find_common_selectors_around_adjacent_sibling_selector
444
+ 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'
445
+ assert_extends 'a + b c .c1', 'a b .c2 {@extend .c1}', 'a + b c .c1, a a + b c .c2'
446
+ assert_extends 'a + b c .c1', 'b c .c2 {@extend .c1}', 'a + b c .c1, a + b c .c2'
447
+ end
448
+
449
+ def test_nested_extender_doesnt_find_common_selectors_around_sibling_selector
450
+ 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'
451
+ assert_extends 'a ~ b c .c1', 'a b .c2 {@extend .c1}', 'a ~ b c .c1, a a ~ b c .c2'
452
+ assert_extends 'a ~ b c .c1', 'b c .c2 {@extend .c1}', 'a ~ b c .c1, a ~ b c .c2'
453
+ end
454
+
455
+ def test_nested_extender_doesnt_find_common_selectors_around_reference_selector
456
+ 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'
457
+ assert_extends 'a /for/ b c .c1', 'a b .c2 {@extend .c1}', 'a /for/ b c .c1, a a /for/ b c .c2'
458
+ assert_extends 'a /for/ b c .c1', 'b c .c2 {@extend .c1}', 'a /for/ b c .c1, a /for/ b c .c2'
459
+ end
460
+
461
+ def test_nested_extender_with_early_child_selectors_doesnt_subseq_them
462
+ assert_extends('.bip > .bap .foo', '.grip > .bap .bar {@extend .foo}',
463
+ '.bip > .bap .foo, .bip > .bap .grip > .bap .bar, .grip > .bap .bip > .bap .bar')
464
+ assert_extends('.bap > .bip .foo', '.bap > .grip .bar {@extend .foo}',
465
+ '.bap > .bip .foo, .bap > .bip .bap > .grip .bar, .bap > .grip .bap > .bip .bar')
466
+ end
467
+
468
+ def test_nested_extender_with_child_selector_unifies
469
+ assert_extends '.baz.foo', 'foo > bar {@extend .foo}', '.baz.foo, foo > bar.baz'
470
+
471
+ assert_equal <<CSS, render(<<SCSS)
472
+ .baz > .foo, .baz > .bar {
473
+ a: b; }
474
+ CSS
475
+ .baz > {
476
+ .foo {a: b}
477
+ .bar {@extend .foo}
478
+ }
479
+ SCSS
480
+
481
+ assert_equal <<CSS, render(<<SCSS)
482
+ .foo .bar, .foo > .baz {
483
+ a: b; }
484
+ CSS
485
+ .foo {
486
+ .bar {a: b}
487
+ > .baz {@extend .bar}
488
+ }
489
+ SCSS
490
+ end
491
+
492
+ def test_another_nested_extender_with_early_child_selectors_doesnt_subseq_them
493
+ assert_equal <<CSS, render(<<SCSS)
494
+ .foo .bar, .foo .bip > .baz {
495
+ a: b; }
496
+ CSS
497
+ .foo {
498
+ .bar {a: b}
499
+ .bip > .baz {@extend .bar}
500
+ }
501
+ SCSS
502
+
503
+ assert_equal <<CSS, render(<<SCSS)
504
+ .foo .bip .bar, .foo .bip .foo > .baz {
505
+ a: b; }
506
+ CSS
507
+ .foo {
508
+ .bip .bar {a: b}
509
+ > .baz {@extend .bar}
510
+ }
511
+ SCSS
512
+
513
+ assert_extends '.foo > .bar', '.bip + .baz {@extend .bar}', '.foo > .bar, .foo > .bip + .baz'
514
+ assert_extends '.foo + .bar', '.bip > .baz {@extend .bar}', '.foo + .bar, .bip > .foo + .baz'
515
+ assert_extends '.foo > .bar', '.bip > .baz {@extend .bar}', '.foo > .bar, .bip.foo > .baz'
516
+ end
517
+
518
+ def test_nested_extender_with_trailing_child_selector
519
+ assert_raise(Sass::SyntaxError, "bar > can't extend: invalid selector") do
520
+ render("bar > {@extend .baz}")
521
+ end
522
+ end
523
+
524
+ def test_nested_extender_with_sibling_selector
525
+ assert_extends '.baz .foo', 'foo + bar {@extend .foo}', '.baz .foo, .baz foo + bar'
526
+ end
527
+
528
+ def test_nested_extender_with_hacky_selector
529
+ assert_extends('.baz .foo', 'foo + > > + bar {@extend .foo}',
530
+ '.baz .foo, .baz foo + > > + bar, foo .baz + > > + bar')
531
+ assert_extends '.baz .foo', '> > bar {@extend .foo}', '.baz .foo, > > .baz bar'
532
+ end
533
+
534
+ def test_nested_extender_merges_with_same_selector
535
+ assert_equal <<CSS, render(<<SCSS)
536
+ .foo .bar, .foo .baz {
537
+ a: b; }
538
+ CSS
539
+ .foo {
540
+ .bar {a: b}
541
+ .baz {@extend .bar} }
542
+ SCSS
543
+ end
544
+
545
+ def test_nested_extender_with_child_selector_merges_with_same_selector
546
+ assert_extends('.foo > .bar .baz', '.foo > .bar .bang {@extend .baz}',
547
+ '.foo > .bar .baz, .foo > .bar .bang')
548
+ end
549
+
550
+ # Combinator Unification
551
+
552
+ def test_combinator_unification_for_hacky_combinators
553
+ assert_extends '.a > + x', '.b y {@extend x}', '.a > + x, .a .b > + y, .b .a > + y'
554
+ assert_extends '.a x', '.b > + y {@extend x}', '.a x, .a .b > + y, .b .a > + y'
555
+ assert_extends '.a > + x', '.b > + y {@extend x}', '.a > + x, .a .b > + y, .b .a > + y'
556
+ assert_extends '.a ~ > + x', '.b > + y {@extend x}', '.a ~ > + x, .a .b ~ > + y, .b .a ~ > + y'
557
+ assert_extends '.a + > x', '.b > + y {@extend x}', '.a + > x'
558
+ assert_extends '.a + > x', '.b > + y {@extend x}', '.a + > x'
559
+ assert_extends '.a ~ > + .b > x', '.c > + .d > y {@extend x}', '.a ~ > + .b > x, .a .c ~ > + .d.b > y, .c .a ~ > + .d.b > y'
560
+ end
561
+
562
+ def test_combinator_unification_double_tilde
563
+ assert_extends '.a.b ~ x', '.a ~ y {@extend x}', '.a.b ~ x, .a.b ~ y'
564
+ assert_extends '.a ~ x', '.a.b ~ y {@extend x}', '.a ~ x, .a.b ~ y'
565
+ assert_extends '.a ~ x', '.b ~ y {@extend x}', '.a ~ x, .a ~ .b ~ y, .b ~ .a ~ y, .b.a ~ y'
566
+ assert_extends 'a.a ~ x', 'b.b ~ y {@extend x}', 'a.a ~ x, a.a ~ b.b ~ y, b.b ~ a.a ~ y'
567
+ end
568
+
569
+ def test_combinator_unification_tilde_plus
570
+ assert_extends '.a.b + x', '.a ~ y {@extend x}', '.a.b + x, .a.b + y'
571
+ assert_extends '.a + x', '.a.b ~ y {@extend x}', '.a + x, .a.b ~ .a + y, .a.b + y'
572
+ assert_extends '.a + x', '.b ~ y {@extend x}', '.a + x, .b ~ .a + y, .b.a + y'
573
+ assert_extends 'a.a + x', 'b.b ~ y {@extend x}', 'a.a + x, b.b ~ a.a + y'
574
+ assert_extends '.a.b ~ x', '.a + y {@extend x}', '.a.b ~ x, .a.b ~ .a + y, .a.b + y'
575
+ assert_extends '.a ~ x', '.a.b + y {@extend x}', '.a ~ x, .a.b + y'
576
+ assert_extends '.a ~ x', '.b + y {@extend x}', '.a ~ x, .a ~ .b + y, .a.b + y'
577
+ assert_extends 'a.a ~ x', 'b.b + y {@extend x}', 'a.a ~ x, a.a ~ b.b + y'
578
+ end
579
+
580
+ def test_combinator_unification_angle_sibling
581
+ assert_extends '.a > x', '.b ~ y {@extend x}', '.a > x, .a > .b ~ y'
582
+ assert_extends '.a > x', '.b + y {@extend x}', '.a > x, .a > .b + y'
583
+ assert_extends '.a ~ x', '.b > y {@extend x}', '.a ~ x, .b > .a ~ y'
584
+ assert_extends '.a + x', '.b > y {@extend x}', '.a + x, .b > .a + y'
585
+ end
586
+
587
+ def test_combinator_unification_double_angle
588
+ assert_extends '.a.b > x', '.b > y {@extend x}', '.a.b > x, .b.a > y'
589
+ assert_extends '.a > x', '.a.b > y {@extend x}', '.a > x, .a.b > y'
590
+ assert_extends '.a > x', '.b > y {@extend x}', '.a > x, .b.a > y'
591
+ assert_extends 'a.a > x', 'b.b > y {@extend x}', 'a.a > x'
592
+ end
593
+
594
+ def test_combinator_unification_double_plus
595
+ assert_extends '.a.b + x', '.b + y {@extend x}', '.a.b + x, .b.a + y'
596
+ assert_extends '.a + x', '.a.b + y {@extend x}', '.a + x, .a.b + y'
597
+ assert_extends '.a + x', '.b + y {@extend x}', '.a + x, .b.a + y'
598
+ assert_extends 'a.a + x', 'b.b + y {@extend x}', 'a.a + x'
599
+ end
600
+
601
+ def test_combinator_unification_angle_space
602
+ assert_extends '.a.b > x', '.a y {@extend x}', '.a.b > x, .a.b > y'
603
+ assert_extends '.a > x', '.a.b y {@extend x}', '.a > x, .a.b .a > y'
604
+ assert_extends '.a > x', '.b y {@extend x}', '.a > x, .b .a > y'
605
+ assert_extends '.a.b x', '.a > y {@extend x}', '.a.b x, .a.b .a > y'
606
+ assert_extends '.a x', '.a.b > y {@extend x}', '.a x, .a.b > y'
607
+ assert_extends '.a x', '.b > y {@extend x}', '.a x, .a .b > y'
608
+ end
609
+
610
+ def test_combinator_unification_plus_space
611
+ assert_extends '.a.b + x', '.a y {@extend x}', '.a.b + x, .a .a.b + y'
612
+ assert_extends '.a + x', '.a.b y {@extend x}', '.a + x, .a.b .a + y'
613
+ assert_extends '.a + x', '.b y {@extend x}', '.a + x, .b .a + y'
614
+ assert_extends '.a.b x', '.a + y {@extend x}', '.a.b x, .a.b .a + y'
615
+ assert_extends '.a x', '.a.b + y {@extend x}', '.a x, .a .a.b + y'
616
+ assert_extends '.a x', '.b + y {@extend x}', '.a x, .a .b + y'
617
+ end
618
+
619
+ def test_combinator_unification_nested
620
+ assert_extends '.a > .b + x', '.c > .d + y {@extend x}', '.a > .b + x, .c.a > .d.b + y'
621
+ assert_extends '.a > .b + x', '.c > y {@extend x}', '.a > .b + x, .c.a > .b + y'
622
+ end
623
+
624
+ def test_combinator_unification_with_newlines
625
+ assert_equal <<CSS, render(<<SCSS)
626
+ .a >
627
+ .b
628
+ + x, .c.a > .d.b + y {
629
+ a: b; }
630
+ CSS
631
+ .a >
632
+ .b
633
+ + x {a: b}
634
+ .c
635
+ > .d +
636
+ y {@extend x}
637
+ SCSS
638
+ end
639
+
640
+ # Loops
641
+
642
+ def test_extend_self_loop
643
+ assert_equal <<CSS, render(<<SCSS)
644
+ .foo {
645
+ a: b; }
646
+ CSS
647
+ .foo {a: b; @extend .foo}
648
+ SCSS
649
+ end
650
+
651
+ def test_basic_extend_loop
652
+ assert_equal <<CSS, render(<<SCSS)
653
+ .bar, .foo {
654
+ a: b; }
655
+
656
+ .foo, .bar {
657
+ c: d; }
658
+ CSS
659
+ .foo {a: b; @extend .bar}
660
+ .bar {c: d; @extend .foo}
661
+ SCSS
662
+ end
663
+
664
+ def test_three_level_extend_loop
665
+ assert_equal <<CSS, render(<<SCSS)
666
+ .baz, .bar, .foo {
667
+ a: b; }
668
+
669
+ .foo, .baz, .bar {
670
+ c: d; }
671
+
672
+ .bar, .foo, .baz {
673
+ e: f; }
674
+ CSS
675
+ .foo {a: b; @extend .bar}
676
+ .bar {c: d; @extend .baz}
677
+ .baz {e: f; @extend .foo}
678
+ SCSS
679
+ end
680
+
681
+ def test_nested_extend_loop
682
+ assert_equal <<CSS, render(<<SCSS)
683
+ .bar, .bar .foo {
684
+ a: b; }
685
+ .bar .foo {
686
+ c: d; }
687
+ CSS
688
+ .bar {
689
+ a: b;
690
+ .foo {c: d; @extend .bar}
691
+ }
692
+ SCSS
693
+ end
694
+
695
+ def test_multiple_extender_merges_with_superset_selector
696
+ assert_equal <<CSS, render(<<SCSS)
697
+ a.bar.baz, a.foo {
698
+ a: b; }
699
+ CSS
700
+ .foo {@extend .bar; @extend .baz}
701
+ a.bar.baz {a: b}
702
+ SCSS
703
+ end
704
+
705
+ def test_control_flow_if
706
+ assert_equal <<CSS, render(<<SCSS)
707
+ .true, .also-true {
708
+ color: green; }
709
+
710
+ .false, .also-false {
711
+ color: red; }
712
+ CSS
713
+ .true { color: green; }
714
+ .false { color: red; }
715
+ .also-true {
716
+ @if true { @extend .true; }
717
+ @else { @extend .false; }
718
+ }
719
+ .also-false {
720
+ @if false { @extend .true; }
721
+ @else { @extend .false; }
722
+ }
723
+ SCSS
724
+ end
725
+
726
+ def test_control_flow_for
727
+ assert_equal <<CSS, render(<<SCSS)
728
+ .base-0, .added {
729
+ color: green; }
730
+
731
+ .base-1, .added {
732
+ display: block; }
733
+
734
+ .base-2, .added {
735
+ border: 1px solid blue; }
736
+ CSS
737
+ .base-0 { color: green; }
738
+ .base-1 { display: block; }
739
+ .base-2 { border: 1px solid blue; }
740
+ .added {
741
+ @for $i from 0 to 3 {
742
+ @extend .base-\#{$i};
743
+ }
744
+ }
745
+ SCSS
746
+ end
747
+
748
+ def test_control_flow_while
749
+ assert_equal <<CSS, render(<<SCSS)
750
+ .base-0, .added {
751
+ color: green; }
752
+
753
+ .base-1, .added {
754
+ display: block; }
755
+
756
+ .base-2, .added {
757
+ border: 1px solid blue; }
758
+ CSS
759
+ .base-0 { color: green; }
760
+ .base-1 { display: block; }
761
+ .base-2 { border: 1px solid blue; }
762
+ .added {
763
+ $i : 0;
764
+ @while $i < 3 {
765
+ @extend .base-\#{$i};
766
+ $i : $i + 1;
767
+ }
768
+ }
769
+ SCSS
770
+ end
771
+
772
+ def test_basic_placeholder_selector
773
+ assert_extends '%foo', '.bar {@extend %foo}', '.bar'
774
+ end
775
+
776
+ def test_unused_placeholder_selector
777
+ assert_equal <<CSS, render(<<SCSS)
778
+ .baz {
779
+ color: blue; }
780
+ CSS
781
+ %foo {color: blue}
782
+ %bar {color: red}
783
+ .baz {@extend %foo}
784
+ SCSS
785
+ end
786
+
787
+ def test_placeholder_descendant_selector
788
+ assert_extends '#context %foo a', '.bar {@extend %foo}', '#context .bar a'
789
+ end
790
+
791
+ def test_semi_placeholder_selector
792
+ assert_equal <<CSS, render(<<SCSS)
793
+ .bar .baz {
794
+ color: blue; }
795
+ CSS
796
+ #context %foo, .bar .baz {color: blue}
797
+ SCSS
798
+ end
799
+
800
+ def test_placeholder_selector_with_multiple_extenders
801
+ assert_equal <<CSS, render(<<SCSS)
802
+ .bar, .baz {
803
+ color: blue; }
804
+ CSS
805
+ %foo {color: blue}
806
+ .bar {@extend %foo}
807
+ .baz {@extend %foo}
808
+ SCSS
809
+ end
810
+
811
+ def test_placeholder_selector_as_modifier
812
+ assert_extend_doesnt_match('div', '%foo', :failed_to_unify, 3) do
813
+ assert_equal <<CSS, render(<<SCSS)
814
+ a.baz.bar {
815
+ color: blue; }
816
+ CSS
817
+ a%foo.baz {color: blue}
818
+ .bar {@extend %foo}
819
+ div {@extend %foo}
820
+ SCSS
821
+ end
822
+ end
823
+
824
+ def test_placeholder_interpolation
825
+ assert_equal <<CSS, render(<<SCSS)
826
+ .bar {
827
+ color: blue; }
828
+ CSS
829
+ $foo: foo;
830
+
831
+ %\#{$foo} {color: blue}
832
+ .bar {@extend %foo}
833
+ SCSS
834
+ end
835
+
836
+ def test_media_in_placeholder_selector
837
+ assert_equal <<CSS, render(<<SCSS)
838
+ .baz {
839
+ c: d; }
840
+ CSS
841
+ %foo {bar {@media screen {a: b}}}
842
+ .baz {c: d}
843
+ SCSS
844
+ end
845
+
846
+ def test_extend_out_of_media
847
+ assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SCSS))}
848
+ DEPRECATION WARNING on line 3 of test_extend_out_of_media_inline.scss:
849
+ @extending an outer selector from within @media is deprecated.
850
+ You may only @extend selectors within the same directive.
851
+ This will be an error in Sass 3.3.
852
+ It can only work once @extend is supported natively in the browser.
853
+ WARN
854
+ .foo {
855
+ a: b; }
856
+ CSS
857
+ .foo {a: b}
858
+ @media screen {
859
+ .bar {@extend .foo}
860
+ }
861
+ SCSS
862
+ end
863
+
864
+ def test_extend_out_of_unknown_directive
865
+ assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SCSS))}
866
+ DEPRECATION WARNING on line 3 of test_extend_out_of_unknown_directive_inline.scss:
867
+ @extending an outer selector from within @flooblehoof is deprecated.
868
+ You may only @extend selectors within the same directive.
869
+ This will be an error in Sass 3.3.
870
+ It can only work once @extend is supported natively in the browser.
871
+ WARN
872
+ .foo {
873
+ a: b; }
874
+
875
+ @flooblehoof {}
876
+ CSS
877
+ .foo {a: b}
878
+ @flooblehoof {
879
+ .bar {@extend .foo}
880
+ }
881
+ SCSS
882
+ end
883
+
884
+ def test_extend_out_of_nested_directives
885
+ assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SCSS))}
886
+ DEPRECATION WARNING on line 4 of test_extend_out_of_nested_directives_inline.scss:
887
+ @extending an outer selector from within @flooblehoof is deprecated.
888
+ You may only @extend selectors within the same directive.
889
+ This will be an error in Sass 3.3.
890
+ It can only work once @extend is supported natively in the browser.
891
+ WARN
892
+ @media screen {
893
+ .foo {
894
+ a: b; }
895
+
896
+ @flooblehoof {} }
897
+ CSS
898
+ @media screen {
899
+ .foo {a: b}
900
+ @flooblehoof {
901
+ .bar {@extend .foo}
902
+ }
903
+ }
904
+ SCSS
905
+ end
906
+
907
+ def test_extend_within_media
908
+ assert_equal(<<CSS, render(<<SCSS))
909
+ @media screen {
910
+ .foo, .bar {
911
+ a: b; } }
912
+ CSS
913
+ @media screen {
914
+ .foo {a: b}
915
+ .bar {@extend .foo}
916
+ }
917
+ SCSS
918
+ end
919
+
920
+ def test_extend_within_unknown_directive
921
+ assert_equal(<<CSS, render(<<SCSS))
922
+ @flooblehoof {
923
+ .foo, .bar {
924
+ a: b; } }
925
+ CSS
926
+ @flooblehoof {
927
+ .foo {a: b}
928
+ .bar {@extend .foo}
929
+ }
930
+ SCSS
931
+ end
932
+
933
+ def test_extend_within_nested_directives
934
+ assert_equal(<<CSS, render(<<SCSS))
935
+ @media screen {
936
+ @flooblehoof {
937
+ .foo, .bar {
938
+ a: b; } } }
939
+ CSS
940
+ @media screen {
941
+ @flooblehoof {
942
+ .foo {a: b}
943
+ .bar {@extend .foo}
944
+ }
945
+ }
946
+ SCSS
947
+ end
948
+
949
+ def test_extend_within_disparate_media
950
+ assert_equal(<<CSS, render(<<SCSS))
951
+ @media screen {
952
+ .foo, .bar {
953
+ a: b; } }
954
+ CSS
955
+ @media screen {.foo {a: b}}
956
+ @media screen {.bar {@extend .foo}}
957
+ SCSS
958
+ end
959
+
960
+ def test_extend_within_disparate_unknown_directive
961
+ assert_equal(<<CSS, render(<<SCSS))
962
+ @flooblehoof {
963
+ .foo, .bar {
964
+ a: b; } }
965
+
966
+ @flooblehoof {}
967
+ CSS
968
+ @flooblehoof {.foo {a: b}}
969
+ @flooblehoof {.bar {@extend .foo}}
970
+ SCSS
971
+ end
972
+
973
+ def test_extend_within_disparate_nested_directives
974
+ assert_equal(<<CSS, render(<<SCSS))
975
+ @media screen {
976
+ @flooblehoof {
977
+ .foo, .bar {
978
+ a: b; } } }
979
+ @media screen {
980
+ @flooblehoof {} }
981
+ CSS
982
+ @media screen {@flooblehoof {.foo {a: b}}}
983
+ @media screen {@flooblehoof {.bar {@extend .foo}}}
984
+ SCSS
985
+ end
986
+
987
+ def test_extend_within_and_without_media
988
+ assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SCSS))}
989
+ DEPRECATION WARNING on line 4 of test_extend_within_and_without_media_inline.scss:
990
+ @extending an outer selector from within @media is deprecated.
991
+ You may only @extend selectors within the same directive.
992
+ This will be an error in Sass 3.3.
993
+ It can only work once @extend is supported natively in the browser.
994
+ WARN
995
+ .foo {
996
+ a: b; }
997
+
998
+ @media screen {
999
+ .foo, .bar {
1000
+ c: d; } }
1001
+ CSS
1002
+ .foo {a: b}
1003
+ @media screen {
1004
+ .foo {c: d}
1005
+ .bar {@extend .foo}
1006
+ }
1007
+ SCSS
1008
+ end
1009
+
1010
+ def test_extend_within_and_without_unknown_directive
1011
+ assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SCSS))}
1012
+ DEPRECATION WARNING on line 4 of test_extend_within_and_without_unknown_directive_inline.scss:
1013
+ @extending an outer selector from within @flooblehoof is deprecated.
1014
+ You may only @extend selectors within the same directive.
1015
+ This will be an error in Sass 3.3.
1016
+ It can only work once @extend is supported natively in the browser.
1017
+ WARN
1018
+ .foo {
1019
+ a: b; }
1020
+
1021
+ @flooblehoof {
1022
+ .foo, .bar {
1023
+ c: d; } }
1024
+ CSS
1025
+ .foo {a: b}
1026
+ @flooblehoof {
1027
+ .foo {c: d}
1028
+ .bar {@extend .foo}
1029
+ }
1030
+ SCSS
1031
+ end
1032
+
1033
+ def test_extend_within_and_without_nested_directives
1034
+ assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SCSS))}
1035
+ DEPRECATION WARNING on line 5 of test_extend_within_and_without_nested_directives_inline.scss:
1036
+ @extending an outer selector from within @flooblehoof is deprecated.
1037
+ You may only @extend selectors within the same directive.
1038
+ This will be an error in Sass 3.3.
1039
+ It can only work once @extend is supported natively in the browser.
1040
+ WARN
1041
+ @media screen {
1042
+ .foo {
1043
+ a: b; }
1044
+
1045
+ @flooblehoof {
1046
+ .foo, .bar {
1047
+ c: d; } } }
1048
+ CSS
1049
+ @media screen {
1050
+ .foo {a: b}
1051
+ @flooblehoof {
1052
+ .foo {c: d}
1053
+ .bar {@extend .foo}
1054
+ }
1055
+ }
1056
+ SCSS
1057
+ end
1058
+
1059
+ def test_extend_with_subject_transfers_subject_to_extender
1060
+ assert_equal(<<CSS, render(<<SCSS))
1061
+ foo bar! baz, foo .bip .bap! baz, .bip foo .bap! baz {
1062
+ a: b; }
1063
+ CSS
1064
+ foo bar! baz {a: b}
1065
+ .bip .bap {@extend bar}
1066
+ SCSS
1067
+
1068
+ assert_equal(<<CSS, render(<<SCSS))
1069
+ foo.x bar.y! baz.z, foo.x .bip bar.bap! baz.z, .bip foo.x bar.bap! baz.z {
1070
+ a: b; }
1071
+ CSS
1072
+ foo.x bar.y! baz.z {a: b}
1073
+ .bip .bap {@extend .y}
1074
+ SCSS
1075
+ end
1076
+
1077
+ def test_extend_with_subject_retains_subject_on_target
1078
+ assert_equal(<<CSS, render(<<SCSS))
1079
+ .foo! .bar, .foo! .bip .bap, .bip .foo! .bap {
1080
+ a: b; }
1081
+ CSS
1082
+ .foo! .bar {a: b}
1083
+ .bip .bap {@extend .bar}
1084
+ SCSS
1085
+ end
1086
+
1087
+ def test_extend_with_subject_transfers_subject_to_target
1088
+ assert_equal(<<CSS, render(<<SCSS))
1089
+ a.foo .bar, .bip a.bap! .bar {
1090
+ a: b; }
1091
+ CSS
1092
+ a.foo .bar {a: b}
1093
+ .bip .bap! {@extend .foo}
1094
+ SCSS
1095
+ end
1096
+
1097
+ def test_extend_with_subject_retains_subject_on_extender
1098
+ assert_equal(<<CSS, render(<<SCSS))
1099
+ .foo .bar, .foo .bip! .bap, .bip! .foo .bap {
1100
+ a: b; }
1101
+ CSS
1102
+ .foo .bar {a: b}
1103
+ .bip! .bap {@extend .bar}
1104
+ SCSS
1105
+ end
1106
+
1107
+ def test_extend_with_subject_fails_with_conflicting_subject
1108
+ assert_equal(<<CSS, render(<<SCSS))
1109
+ x! .bar {
1110
+ a: b; }
1111
+ CSS
1112
+ x! .bar {a: b}
1113
+ y! .bap {@extend .bar}
1114
+ SCSS
1115
+ end
1116
+
1117
+ def test_extend_warns_when_extendee_doesnt_exist
1118
+ assert_warning(<<WARN) {assert_equal("", render(<<SCSS))}
1119
+ WARNING on line 1 of test_extend_warns_when_extendee_doesnt_exist_inline.scss: ".foo" failed to @extend ".bar".
1120
+ The selector ".bar" was not found.
1121
+ This will be an error in future releases of Sass.
1122
+ Use "@extend .bar !optional" if the extend should be able to fail.
1123
+ WARN
1124
+ .foo {@extend .bar}
1125
+ SCSS
1126
+ end
1127
+
1128
+ def test_extend_warns_when_extension_fails
1129
+ assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SCSS))}
1130
+ WARNING on line 2 of test_extend_warns_when_extension_fails_inline.scss: "b.foo" failed to @extend ".bar".
1131
+ No selectors matching ".bar" could be unified with "b.foo".
1132
+ This will be an error in future releases of Sass.
1133
+ Use "@extend .bar !optional" if the extend should be able to fail.
1134
+ WARN
1135
+ a.bar {
1136
+ a: b; }
1137
+ CSS
1138
+ a.bar {a: b}
1139
+ b.foo {@extend .bar}
1140
+ SCSS
1141
+ end
1142
+
1143
+ def test_extend_does_not_warn_when_one_extension_fails_but_others_dont
1144
+ assert_no_warning {assert_equal(<<CSS, render(<<SCSS))}
1145
+ a.bar {
1146
+ a: b; }
1147
+
1148
+ .bar, b.foo {
1149
+ c: d; }
1150
+ CSS
1151
+ a.bar {a: b}
1152
+ .bar {c: d}
1153
+ b.foo {@extend .bar}
1154
+ SCSS
1155
+ end
1156
+
1157
+ def test_optional_extend_does_not_warn_when_extendee_doesnt_exist
1158
+ assert_no_warning {assert_equal("", render(<<SCSS))}
1159
+ .foo {@extend .bar !optional}
1160
+ SCSS
1161
+ end
1162
+
1163
+ def test_optional_extend_does_not_warn_when_extension_fails
1164
+ assert_no_warning {assert_equal(<<CSS, render(<<SCSS))}
1165
+ a.bar {
1166
+ a: b; }
1167
+ CSS
1168
+ a.bar {a: b}
1169
+ b.foo {@extend .bar !optional}
1170
+ SCSS
1171
+ end
1172
+
1173
+ # Regression Tests
1174
+
1175
+ def test_pseudo_element_superselector
1176
+ # Pseudo-elements shouldn't be removed in superselector calculations.
1177
+ assert_equal <<CSS, render(<<SCSS)
1178
+ a#bar, a#bar::fblthp {
1179
+ a: b; }
1180
+ CSS
1181
+ %x#bar {a: b} // Add an id to make the results have high specificity
1182
+ %y, %y::fblthp {@extend %x}
1183
+ a {@extend %y}
1184
+ SCSS
1185
+
1186
+ # Pseudo-classes can be removed when the second law allows.
1187
+ assert_equal <<CSS, render(<<SCSS)
1188
+ a#bar {
1189
+ a: b; }
1190
+ CSS
1191
+ %x#bar {a: b}
1192
+ %y, %y:fblthp {@extend %x}
1193
+ a {@extend %y}
1194
+ SCSS
1195
+
1196
+ # A few pseudo-elements can be written as pseudo-elements for historical
1197
+ # reasons. See http://www.w3.org/TR/selectors4/#pseudo-elements.
1198
+ %w[first-line first-letter before after].each do |pseudo|
1199
+ assert_equal <<CSS, render(<<SCSS)
1200
+ a#bar, a#bar:#{pseudo} {
1201
+ a: b; }
1202
+ CSS
1203
+ %x#bar {a: b}
1204
+ %y, %y:#{pseudo} {@extend %x}
1205
+ a {@extend %y}
1206
+ SCSS
1207
+ end
1208
+ end
1209
+
1210
+ def test_nested_sibling_extend
1211
+ assert_equal <<CSS, render(<<SCSS)
1212
+ .parent .bar, .parent .foo {
1213
+ width: 2000px; }
1214
+ CSS
1215
+ .foo {@extend .bar}
1216
+
1217
+ .parent {
1218
+ .bar {
1219
+ width: 2000px;
1220
+ }
1221
+ .foo {
1222
+ @extend .bar
1223
+ }
1224
+ }
1225
+ SCSS
1226
+ end
1227
+
1228
+ def test_parent_and_sibling_extend
1229
+ assert_equal <<CSS, render(<<SCSS)
1230
+ .parent1 .parent2 .child1.child2, .parent2 .parent1 .child1.child2 {
1231
+ c: d; }
1232
+ CSS
1233
+ %foo %bar%baz {c: d}
1234
+
1235
+ .parent1 {
1236
+ @extend %foo;
1237
+ .child1 {@extend %bar}
1238
+ }
1239
+
1240
+ .parent2 {
1241
+ @extend %foo;
1242
+ .child2 {@extend %baz}
1243
+ }
1244
+ SCSS
1245
+ end
1246
+
1247
+ def test_nested_extend_specificity
1248
+ assert_equal <<CSS, render(<<SCSS)
1249
+ a :b, a :b:c {
1250
+ a: b; }
1251
+ CSS
1252
+ %foo {a: b}
1253
+
1254
+ a {
1255
+ :b {@extend %foo}
1256
+ :b:c {@extend %foo}
1257
+ }
1258
+ SCSS
1259
+ end
1260
+
1261
+ def test_nested_double_extend_optimization
1262
+ assert_equal <<CSS, render(<<SCSS)
1263
+ .parent1 .child {
1264
+ a: b; }
1265
+ CSS
1266
+ %foo %bar {
1267
+ a: b;
1268
+ }
1269
+
1270
+ .parent1 {
1271
+ @extend %foo;
1272
+
1273
+ .child {
1274
+ @extend %bar;
1275
+ }
1276
+ }
1277
+
1278
+ .parent2 {
1279
+ @extend %foo;
1280
+ }
1281
+ SCSS
1282
+ end
1283
+
1284
+ def test_extend_in_double_nested_media_query
1285
+ assert_equal <<CSS, render(<<SCSS)
1286
+ @media all and (orientation: landscape) {
1287
+ .bar {
1288
+ color: blue; } }
1289
+ CSS
1290
+ @media all {
1291
+ @media (orientation: landscape) {
1292
+ %foo {color: blue}
1293
+ .bar {@extend %foo}
1294
+ }
1295
+ }
1296
+ SCSS
1297
+ end
1298
+
1299
+ def test_partially_failed_extend
1300
+ assert_no_warning {assert_equal(<<CSS, render(<<SCSS))}
1301
+ .rc, test {
1302
+ color: white; }
1303
+
1304
+ .prices span.pill span.rc {
1305
+ color: red; }
1306
+ CSS
1307
+ test { @extend .rc; }
1308
+ .rc {color: white;}
1309
+ .prices span.pill span.rc {color: red;}
1310
+ SCSS
1311
+ end
1312
+
1313
+ def test_newline_near_combinator
1314
+ assert_equal <<CSS, render(<<SCSS)
1315
+ .a +
1316
+ .b x, .a +
1317
+ .b .c y, .c .a +
1318
+ .b y {
1319
+ a: b; }
1320
+ CSS
1321
+ .a +
1322
+ .b x {a: b}
1323
+ .c y {@extend x}
1324
+ SCSS
1325
+ end
1326
+
1327
+ def test_duplicated_selector_with_newlines
1328
+ assert_equal(<<CSS, render(<<SCSS))
1329
+ .example-1-1,
1330
+ .example-1-2,
1331
+ .my-page-1 .my-module-1-1,
1332
+ .example-1-3 {
1333
+ a: b; }
1334
+ CSS
1335
+ .example-1-1,
1336
+ .example-1-2,
1337
+ .example-1-3 {
1338
+ a: b;
1339
+ }
1340
+
1341
+ .my-page-1 .my-module-1-1 {@extend .example-1-2}
1342
+ SCSS
1343
+ end
1344
+
1345
+ def test_nested_selector_with_child_selector_hack_extendee
1346
+ assert_extends '> .foo', 'foo bar {@extend .foo}', '> .foo, > foo bar'
1347
+ end
1348
+
1349
+ def test_nested_selector_with_child_selector_hack_extender
1350
+ assert_extends '.foo .bar', '> foo bar {@extend .bar}', '.foo .bar, > .foo foo bar, > foo .foo bar'
1351
+ end
1352
+
1353
+ def test_nested_selector_with_child_selector_hack_extender_and_extendee
1354
+ assert_extends '> .foo', '> foo bar {@extend .foo}', '> .foo, > foo bar'
1355
+ end
1356
+
1357
+ def test_nested_selector_with_child_selector_hack_extender_and_sibling_selector_extendee
1358
+ assert_extends '~ .foo', '> foo bar {@extend .foo}', '~ .foo'
1359
+ end
1360
+
1361
+ def test_nested_selector_with_child_selector_hack_extender_and_extendee_and_newline
1362
+ assert_equal <<CSS, render(<<SCSS)
1363
+ > .foo, > flip,
1364
+ > foo bar {
1365
+ a: b; }
1366
+ CSS
1367
+ > .foo {a: b}
1368
+ flip,
1369
+ > foo bar {@extend .foo}
1370
+ SCSS
1371
+ end
1372
+
1373
+ def test_extended_parent_and_child_redundancy_elimination
1374
+ assert_equal <<CSS, render(<<SCSS)
1375
+ a b, d b, a c, d c {
1376
+ a: b; }
1377
+ CSS
1378
+ a {
1379
+ b {a: b}
1380
+ c {@extend b}
1381
+ }
1382
+ d {@extend a}
1383
+ SCSS
1384
+ end
1385
+
1386
+ def test_extend_redundancy_elimination_when_it_would_reduce_specificity
1387
+ assert_extends 'a', 'a.foo {@extend a}', 'a, a.foo'
1388
+ end
1389
+
1390
+ def test_extend_redundancy_elimination_when_it_would_preserve_specificity
1391
+ assert_extends '.bar a', 'a.foo {@extend a}', '.bar a'
1392
+ end
1393
+
1394
+ def test_extend_redundancy_elimination_never_eliminates_base_selector
1395
+ assert_extends 'a.foo', '.foo {@extend a}', 'a.foo, .foo'
1396
+ end
1397
+
1398
+ def test_extend_cross_branch_redundancy_elimination
1399
+ assert_equal <<CSS, render(<<SCSS)
1400
+ a c d, b c a d {
1401
+ a: b; }
1402
+ CSS
1403
+ %x c %y {a: b}
1404
+ a, b {@extend %x}
1405
+ a d {@extend %y}
1406
+ SCSS
1407
+
1408
+ assert_equal <<CSS, render(<<SCSS)
1409
+ e a c d, a c e d, e b c a d, b c a e d {
1410
+ a: b; }
1411
+ CSS
1412
+ e %z {a: b}
1413
+ %x c %y {@extend %z}
1414
+ a, b {@extend %x}
1415
+ a d {@extend %y}
1416
+ SCSS
1417
+ end
1418
+
1419
+ private
1420
+
1421
+ def assert_extend_doesnt_match(extender, target, reason, line, syntax = :scss)
1422
+ warn = "\"#{extender}\" failed to @extend \"#{target}\"."
1423
+ reason =
1424
+ if reason == :not_found
1425
+ "The selector \"#{target}\" was not found."
1426
+ else
1427
+ "No selectors matching \"#{target}\" could be unified with \"#{extender}\"."
1428
+ end
1429
+
1430
+ assert_warning(<<WARNING) {yield}
1431
+ WARNING on line #{line} of #{filename_for_test syntax}: #{warn}
1432
+ #{reason}
1433
+ This will be an error in future releases of Sass.
1434
+ Use "@extend #{target} !optional" if the extend should be able to fail.
1435
+ WARNING
1436
+ end
1437
+
1438
+ def assert_unification(selector, extension, unified)
1439
+ # Do some trickery so the first law of extend doesn't get in our way.
1440
+ assert_extends(
1441
+ "%-a #{selector}",
1442
+ extension + " -a {@extend %-a}",
1443
+ unified.split(', ').map {|s| "-a #{s}"}.join(', '))
1444
+ end
1445
+
1446
+ def assert_extends(selector, extension, result)
1447
+ assert_equal <<CSS, render(<<SCSS)
1448
+ #{result} {
1449
+ a: b; }
1450
+ CSS
1451
+ #{selector} {a: b}
1452
+ #{extension}
1453
+ SCSS
1454
+ end
1455
+
1456
+ def render(sass, options = {})
1457
+ options = {:syntax => :scss}.merge(options)
1458
+ munge_filename options
1459
+ Sass::Engine.new(sass, options).render
1460
+ end
1461
+ end