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