xass 0.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 (242) 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 +201 -0
  6. data/Rakefile +349 -0
  7. data/VERSION +1 -0
  8. data/VERSION_NAME +1 -0
  9. data/bin/push +13 -0
  10. data/bin/sass +13 -0
  11. data/bin/sass-convert +12 -0
  12. data/bin/scss +13 -0
  13. data/extra/update_watch.rb +13 -0
  14. data/init.rb +18 -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 +64 -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/cache_stores.rb +15 -0
  21. data/lib/sass/callbacks.rb +66 -0
  22. data/lib/sass/css.rb +409 -0
  23. data/lib/sass/engine.rb +930 -0
  24. data/lib/sass/environment.rb +101 -0
  25. data/lib/sass/error.rb +201 -0
  26. data/lib/sass/exec.rb +707 -0
  27. data/lib/sass/importers/base.rb +139 -0
  28. data/lib/sass/importers/filesystem.rb +186 -0
  29. data/lib/sass/importers.rb +22 -0
  30. data/lib/sass/logger/base.rb +32 -0
  31. data/lib/sass/logger/log_level.rb +49 -0
  32. data/lib/sass/logger.rb +15 -0
  33. data/lib/sass/media.rb +213 -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 +199 -0
  41. data/lib/sass/plugin.rb +133 -0
  42. data/lib/sass/railtie.rb +10 -0
  43. data/lib/sass/repl.rb +57 -0
  44. data/lib/sass/root.rb +7 -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 +245 -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 +345 -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 +502 -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/script.rb +39 -0
  66. data/lib/sass/scss/css_parser.rb +36 -0
  67. data/lib/sass/scss/parser.rb +1180 -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/scss.rb +16 -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 +215 -0
  78. data/lib/sass/selector.rb +452 -0
  79. data/lib/sass/shared.rb +76 -0
  80. data/lib/sass/supports.rb +229 -0
  81. data/lib/sass/tree/charset_node.rb +22 -0
  82. data/lib/sass/tree/comment_node.rb +82 -0
  83. data/lib/sass/tree/content_node.rb +9 -0
  84. data/lib/sass/tree/css_import_node.rb +60 -0
  85. data/lib/sass/tree/debug_node.rb +18 -0
  86. data/lib/sass/tree/directive_node.rb +42 -0
  87. data/lib/sass/tree/each_node.rb +24 -0
  88. data/lib/sass/tree/extend_node.rb +36 -0
  89. data/lib/sass/tree/for_node.rb +36 -0
  90. data/lib/sass/tree/function_node.rb +34 -0
  91. data/lib/sass/tree/if_node.rb +52 -0
  92. data/lib/sass/tree/import_node.rb +75 -0
  93. data/lib/sass/tree/media_node.rb +58 -0
  94. data/lib/sass/tree/mixin_def_node.rb +38 -0
  95. data/lib/sass/tree/mixin_node.rb +39 -0
  96. data/lib/sass/tree/node.rb +196 -0
  97. data/lib/sass/tree/prop_node.rb +152 -0
  98. data/lib/sass/tree/return_node.rb +18 -0
  99. data/lib/sass/tree/root_node.rb +78 -0
  100. data/lib/sass/tree/rule_node.rb +132 -0
  101. data/lib/sass/tree/supports_node.rb +51 -0
  102. data/lib/sass/tree/trace_node.rb +32 -0
  103. data/lib/sass/tree/variable_node.rb +30 -0
  104. data/lib/sass/tree/visitors/base.rb +75 -0
  105. data/lib/sass/tree/visitors/check_nesting.rb +147 -0
  106. data/lib/sass/tree/visitors/convert.rb +316 -0
  107. data/lib/sass/tree/visitors/cssize.rb +241 -0
  108. data/lib/sass/tree/visitors/deep_copy.rb +102 -0
  109. data/lib/sass/tree/visitors/extend.rb +68 -0
  110. data/lib/sass/tree/visitors/perform.rb +446 -0
  111. data/lib/sass/tree/visitors/set_options.rb +125 -0
  112. data/lib/sass/tree/visitors/to_css.rb +228 -0
  113. data/lib/sass/tree/warn_node.rb +18 -0
  114. data/lib/sass/tree/while_node.rb +18 -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/util.rb +948 -0
  119. data/lib/sass/version.rb +126 -0
  120. data/lib/sass.rb +95 -0
  121. data/rails/init.rb +1 -0
  122. data/test/Gemfile +3 -0
  123. data/test/Gemfile.lock +10 -0
  124. data/test/sass/cache_test.rb +89 -0
  125. data/test/sass/callbacks_test.rb +61 -0
  126. data/test/sass/conversion_test.rb +1760 -0
  127. data/test/sass/css2sass_test.rb +458 -0
  128. data/test/sass/data/hsl-rgb.txt +319 -0
  129. data/test/sass/engine_test.rb +3244 -0
  130. data/test/sass/exec_test.rb +86 -0
  131. data/test/sass/extend_test.rb +1482 -0
  132. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  133. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  134. data/test/sass/functions_test.rb +1139 -0
  135. data/test/sass/importer_test.rb +192 -0
  136. data/test/sass/logger_test.rb +58 -0
  137. data/test/sass/mock_importer.rb +49 -0
  138. data/test/sass/more_results/more1.css +9 -0
  139. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  140. data/test/sass/more_results/more_import.css +29 -0
  141. data/test/sass/more_templates/_more_partial.sass +2 -0
  142. data/test/sass/more_templates/more1.sass +23 -0
  143. data/test/sass/more_templates/more_import.sass +11 -0
  144. data/test/sass/plugin_test.rb +564 -0
  145. data/test/sass/results/alt.css +4 -0
  146. data/test/sass/results/basic.css +9 -0
  147. data/test/sass/results/cached_import_option.css +3 -0
  148. data/test/sass/results/compact.css +5 -0
  149. data/test/sass/results/complex.css +86 -0
  150. data/test/sass/results/compressed.css +1 -0
  151. data/test/sass/results/expanded.css +19 -0
  152. data/test/sass/results/filename_fn.css +3 -0
  153. data/test/sass/results/if.css +3 -0
  154. data/test/sass/results/import.css +31 -0
  155. data/test/sass/results/import_charset.css +5 -0
  156. data/test/sass/results/import_charset_1_8.css +5 -0
  157. data/test/sass/results/import_charset_ibm866.css +5 -0
  158. data/test/sass/results/import_content.css +1 -0
  159. data/test/sass/results/line_numbers.css +49 -0
  160. data/test/sass/results/mixins.css +95 -0
  161. data/test/sass/results/multiline.css +24 -0
  162. data/test/sass/results/nested.css +22 -0
  163. data/test/sass/results/options.css +1 -0
  164. data/test/sass/results/parent_ref.css +13 -0
  165. data/test/sass/results/script.css +16 -0
  166. data/test/sass/results/scss_import.css +31 -0
  167. data/test/sass/results/scss_importee.css +2 -0
  168. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  169. data/test/sass/results/subdir/subdir.css +3 -0
  170. data/test/sass/results/units.css +11 -0
  171. data/test/sass/results/warn.css +0 -0
  172. data/test/sass/results/warn_imported.css +0 -0
  173. data/test/sass/script_conversion_test.rb +299 -0
  174. data/test/sass/script_test.rb +622 -0
  175. data/test/sass/scss/css_test.rb +1100 -0
  176. data/test/sass/scss/rx_test.rb +156 -0
  177. data/test/sass/scss/scss_test.rb +2106 -0
  178. data/test/sass/scss/test_helper.rb +37 -0
  179. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  180. data/test/sass/templates/_double_import_loop2.sass +1 -0
  181. data/test/sass/templates/_filename_fn_import.scss +11 -0
  182. data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
  183. data/test/sass/templates/_imported_charset_utf8.sass +4 -0
  184. data/test/sass/templates/_imported_content.sass +3 -0
  185. data/test/sass/templates/_partial.sass +2 -0
  186. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  187. data/test/sass/templates/alt.sass +16 -0
  188. data/test/sass/templates/basic.sass +23 -0
  189. data/test/sass/templates/bork1.sass +2 -0
  190. data/test/sass/templates/bork2.sass +2 -0
  191. data/test/sass/templates/bork3.sass +2 -0
  192. data/test/sass/templates/bork4.sass +2 -0
  193. data/test/sass/templates/bork5.sass +3 -0
  194. data/test/sass/templates/cached_import_option.scss +3 -0
  195. data/test/sass/templates/compact.sass +17 -0
  196. data/test/sass/templates/complex.sass +305 -0
  197. data/test/sass/templates/compressed.sass +15 -0
  198. data/test/sass/templates/double_import_loop1.sass +1 -0
  199. data/test/sass/templates/expanded.sass +17 -0
  200. data/test/sass/templates/filename_fn.scss +18 -0
  201. data/test/sass/templates/if.sass +11 -0
  202. data/test/sass/templates/import.sass +12 -0
  203. data/test/sass/templates/import_charset.sass +9 -0
  204. data/test/sass/templates/import_charset_1_8.sass +6 -0
  205. data/test/sass/templates/import_charset_ibm866.sass +11 -0
  206. data/test/sass/templates/import_content.sass +4 -0
  207. data/test/sass/templates/importee.less +2 -0
  208. data/test/sass/templates/importee.sass +19 -0
  209. data/test/sass/templates/line_numbers.sass +13 -0
  210. data/test/sass/templates/mixin_bork.sass +5 -0
  211. data/test/sass/templates/mixins.sass +76 -0
  212. data/test/sass/templates/multiline.sass +20 -0
  213. data/test/sass/templates/nested.sass +25 -0
  214. data/test/sass/templates/nested_bork1.sass +2 -0
  215. data/test/sass/templates/nested_bork2.sass +2 -0
  216. data/test/sass/templates/nested_bork3.sass +2 -0
  217. data/test/sass/templates/nested_bork4.sass +2 -0
  218. data/test/sass/templates/nested_import.sass +2 -0
  219. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  220. data/test/sass/templates/options.sass +2 -0
  221. data/test/sass/templates/parent_ref.sass +25 -0
  222. data/test/sass/templates/same_name_different_ext.sass +2 -0
  223. data/test/sass/templates/same_name_different_ext.scss +1 -0
  224. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  225. data/test/sass/templates/script.sass +101 -0
  226. data/test/sass/templates/scss_import.scss +11 -0
  227. data/test/sass/templates/scss_importee.scss +1 -0
  228. data/test/sass/templates/single_import_loop.sass +1 -0
  229. data/test/sass/templates/subdir/import_up1.scss +1 -0
  230. data/test/sass/templates/subdir/import_up2.scss +1 -0
  231. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  232. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  233. data/test/sass/templates/subdir/subdir.sass +6 -0
  234. data/test/sass/templates/units.sass +11 -0
  235. data/test/sass/templates/warn.sass +3 -0
  236. data/test/sass/templates/warn_imported.sass +4 -0
  237. data/test/sass/test_helper.rb +8 -0
  238. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  239. data/test/sass/util/subset_map_test.rb +91 -0
  240. data/test/sass/util_test.rb +382 -0
  241. data/test/test_helper.rb +80 -0
  242. metadata +354 -0
@@ -0,0 +1,1482 @@
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_multiple_source_redundancy_elimination
1211
+ assert_equal <<CSS, render(<<SCSS)
1212
+ .test-case, .test-case:active {
1213
+ color: red; }
1214
+
1215
+ .test-case:hover {
1216
+ color: green; }
1217
+ CSS
1218
+ %default-color {color: red}
1219
+ %alt-color {color: green}
1220
+
1221
+ %default-style {
1222
+ @extend %default-color;
1223
+ &:hover {@extend %alt-color}
1224
+ &:active {@extend %default-color}
1225
+ }
1226
+
1227
+ .test-case {@extend %default-style}
1228
+ SCSS
1229
+ end
1230
+
1231
+ def test_nested_sibling_extend
1232
+ assert_equal <<CSS, render(<<SCSS)
1233
+ .parent .bar, .parent .foo {
1234
+ width: 2000px; }
1235
+ CSS
1236
+ .foo {@extend .bar}
1237
+
1238
+ .parent {
1239
+ .bar {
1240
+ width: 2000px;
1241
+ }
1242
+ .foo {
1243
+ @extend .bar
1244
+ }
1245
+ }
1246
+ SCSS
1247
+ end
1248
+
1249
+ def test_parent_and_sibling_extend
1250
+ assert_equal <<CSS, render(<<SCSS)
1251
+ .parent1 .parent2 .child1.child2, .parent2 .parent1 .child1.child2 {
1252
+ c: d; }
1253
+ CSS
1254
+ %foo %bar%baz {c: d}
1255
+
1256
+ .parent1 {
1257
+ @extend %foo;
1258
+ .child1 {@extend %bar}
1259
+ }
1260
+
1261
+ .parent2 {
1262
+ @extend %foo;
1263
+ .child2 {@extend %baz}
1264
+ }
1265
+ SCSS
1266
+ end
1267
+
1268
+ def test_nested_extend_specificity
1269
+ assert_equal <<CSS, render(<<SCSS)
1270
+ a :b, a :b:c {
1271
+ a: b; }
1272
+ CSS
1273
+ %foo {a: b}
1274
+
1275
+ a {
1276
+ :b {@extend %foo}
1277
+ :b:c {@extend %foo}
1278
+ }
1279
+ SCSS
1280
+ end
1281
+
1282
+ def test_nested_double_extend_optimization
1283
+ assert_equal <<CSS, render(<<SCSS)
1284
+ .parent1 .child {
1285
+ a: b; }
1286
+ CSS
1287
+ %foo %bar {
1288
+ a: b;
1289
+ }
1290
+
1291
+ .parent1 {
1292
+ @extend %foo;
1293
+
1294
+ .child {
1295
+ @extend %bar;
1296
+ }
1297
+ }
1298
+
1299
+ .parent2 {
1300
+ @extend %foo;
1301
+ }
1302
+ SCSS
1303
+ end
1304
+
1305
+ def test_extend_in_double_nested_media_query
1306
+ assert_equal <<CSS, render(<<SCSS)
1307
+ @media all and (orientation: landscape) {
1308
+ .bar {
1309
+ color: blue; } }
1310
+ CSS
1311
+ @media all {
1312
+ @media (orientation: landscape) {
1313
+ %foo {color: blue}
1314
+ .bar {@extend %foo}
1315
+ }
1316
+ }
1317
+ SCSS
1318
+ end
1319
+
1320
+ def test_partially_failed_extend
1321
+ assert_no_warning {assert_equal(<<CSS, render(<<SCSS))}
1322
+ .rc, test {
1323
+ color: white; }
1324
+
1325
+ .prices span.pill span.rc {
1326
+ color: red; }
1327
+ CSS
1328
+ test { @extend .rc; }
1329
+ .rc {color: white;}
1330
+ .prices span.pill span.rc {color: red;}
1331
+ SCSS
1332
+ end
1333
+
1334
+ def test_newline_near_combinator
1335
+ assert_equal <<CSS, render(<<SCSS)
1336
+ .a +
1337
+ .b x, .a +
1338
+ .b .c y, .c .a +
1339
+ .b y {
1340
+ a: b; }
1341
+ CSS
1342
+ .a +
1343
+ .b x {a: b}
1344
+ .c y {@extend x}
1345
+ SCSS
1346
+ end
1347
+
1348
+ def test_duplicated_selector_with_newlines
1349
+ assert_equal(<<CSS, render(<<SCSS))
1350
+ .example-1-1,
1351
+ .example-1-2,
1352
+ .my-page-1 .my-module-1-1,
1353
+ .example-1-3 {
1354
+ a: b; }
1355
+ CSS
1356
+ .example-1-1,
1357
+ .example-1-2,
1358
+ .example-1-3 {
1359
+ a: b;
1360
+ }
1361
+
1362
+ .my-page-1 .my-module-1-1 {@extend .example-1-2}
1363
+ SCSS
1364
+ end
1365
+
1366
+ def test_nested_selector_with_child_selector_hack_extendee
1367
+ assert_extends '> .foo', 'foo bar {@extend .foo}', '> .foo, > foo bar'
1368
+ end
1369
+
1370
+ def test_nested_selector_with_child_selector_hack_extender
1371
+ assert_extends '.foo .bar', '> foo bar {@extend .bar}', '.foo .bar, > .foo foo bar, > foo .foo bar'
1372
+ end
1373
+
1374
+ def test_nested_selector_with_child_selector_hack_extender_and_extendee
1375
+ assert_extends '> .foo', '> foo bar {@extend .foo}', '> .foo, > foo bar'
1376
+ end
1377
+
1378
+ def test_nested_selector_with_child_selector_hack_extender_and_sibling_selector_extendee
1379
+ assert_extends '~ .foo', '> foo bar {@extend .foo}', '~ .foo'
1380
+ end
1381
+
1382
+ def test_nested_selector_with_child_selector_hack_extender_and_extendee_and_newline
1383
+ assert_equal <<CSS, render(<<SCSS)
1384
+ > .foo, > flip,
1385
+ > foo bar {
1386
+ a: b; }
1387
+ CSS
1388
+ > .foo {a: b}
1389
+ flip,
1390
+ > foo bar {@extend .foo}
1391
+ SCSS
1392
+ end
1393
+
1394
+ def test_extended_parent_and_child_redundancy_elimination
1395
+ assert_equal <<CSS, render(<<SCSS)
1396
+ a b, d b, a c, d c {
1397
+ a: b; }
1398
+ CSS
1399
+ a {
1400
+ b {a: b}
1401
+ c {@extend b}
1402
+ }
1403
+ d {@extend a}
1404
+ SCSS
1405
+ end
1406
+
1407
+ def test_extend_redundancy_elimination_when_it_would_reduce_specificity
1408
+ assert_extends 'a', 'a.foo {@extend a}', 'a, a.foo'
1409
+ end
1410
+
1411
+ def test_extend_redundancy_elimination_when_it_would_preserve_specificity
1412
+ assert_extends '.bar a', 'a.foo {@extend a}', '.bar a'
1413
+ end
1414
+
1415
+ def test_extend_redundancy_elimination_never_eliminates_base_selector
1416
+ assert_extends 'a.foo', '.foo {@extend a}', 'a.foo, .foo'
1417
+ end
1418
+
1419
+ def test_extend_cross_branch_redundancy_elimination
1420
+ assert_equal <<CSS, render(<<SCSS)
1421
+ .a .c .d, .b .c .a .d {
1422
+ a: b; }
1423
+ CSS
1424
+ %x .c %y {a: b}
1425
+ .a, .b {@extend %x}
1426
+ .a .d {@extend %y}
1427
+ SCSS
1428
+
1429
+ assert_equal <<CSS, render(<<SCSS)
1430
+ .e .a .c .d, .a .c .e .d, .e .b .c .a .d, .b .c .a .e .d {
1431
+ a: b; }
1432
+ CSS
1433
+ .e %z {a: b}
1434
+ %x .c %y {@extend %z}
1435
+ .a, .b {@extend %x}
1436
+ .a .d {@extend %y}
1437
+ SCSS
1438
+ end
1439
+
1440
+ private
1441
+
1442
+ def assert_extend_doesnt_match(extender, target, reason, line, syntax = :scss)
1443
+ warn = "\"#{extender}\" failed to @extend \"#{target}\"."
1444
+ reason =
1445
+ if reason == :not_found
1446
+ "The selector \"#{target}\" was not found."
1447
+ else
1448
+ "No selectors matching \"#{target}\" could be unified with \"#{extender}\"."
1449
+ end
1450
+
1451
+ assert_warning(<<WARNING) {yield}
1452
+ WARNING on line #{line} of #{filename_for_test syntax}: #{warn}
1453
+ #{reason}
1454
+ This will be an error in future releases of Sass.
1455
+ Use "@extend #{target} !optional" if the extend should be able to fail.
1456
+ WARNING
1457
+ end
1458
+
1459
+ def assert_unification(selector, extension, unified)
1460
+ # Do some trickery so the first law of extend doesn't get in our way.
1461
+ assert_extends(
1462
+ "%-a #{selector}",
1463
+ extension + " -a {@extend %-a}",
1464
+ unified.split(', ').map {|s| "-a #{s}"}.join(', '))
1465
+ end
1466
+
1467
+ def assert_extends(selector, extension, result)
1468
+ assert_equal <<CSS, render(<<SCSS)
1469
+ #{result} {
1470
+ a: b; }
1471
+ CSS
1472
+ #{selector} {a: b}
1473
+ #{extension}
1474
+ SCSS
1475
+ end
1476
+
1477
+ def render(sass, options = {})
1478
+ options = {:syntax => :scss}.merge(options)
1479
+ munge_filename options
1480
+ Sass::Engine.new(sass, options).render
1481
+ end
1482
+ end