sass 3.2.0.alpha.96 → 3.2.0.alpha.99

Sign up to get free protection for your applications and to get access to all the features.
@@ -38,5 +38,8 @@ module Sass::Tree
38
38
  def invisible?
39
39
  children.all? {|c| c.invisible?}
40
40
  end
41
+
42
+ # @see Node#bubbles?
43
+ def bubbles?; true; end
41
44
  end
42
45
  end
@@ -188,6 +188,13 @@ module Sass
188
188
  Sass::Tree::Visitors::DeepCopy.visit(self)
189
189
  end
190
190
 
191
+ # Whether or not this node bubbles up through RuleNodes.
192
+ #
193
+ # @return [Boolean]
194
+ def bubbles?
195
+ false
196
+ end
197
+
191
198
  protected
192
199
 
193
200
  # @see Sass::Shared.balance
@@ -0,0 +1,51 @@
1
+ module Sass::Tree
2
+ # A static node representing a `@supports` rule.
3
+ # `@supports` rules behave differently from other directives
4
+ # in that when they're nested within rules,
5
+ # they bubble up to top-level.
6
+ #
7
+ # @see Sass::Tree
8
+ class SupportsNode < DirectiveNode
9
+ # The name, which may include a browser prefix.
10
+ #
11
+ # @return [String]
12
+ attr_accessor :name
13
+
14
+ # The supports condition.
15
+ #
16
+ # @return [Sass::Supports::Condition]
17
+ attr_accessor :condition
18
+
19
+ # @see RuleNode#tabs
20
+ attr_accessor :tabs
21
+
22
+ # @see RuleNode#group_end
23
+ attr_accessor :group_end
24
+
25
+ # @param condition [Sass::Supports::Condition] See \{#condition}
26
+ def initialize(name, condition)
27
+ @name = name
28
+ @condition = condition
29
+ @tabs = 0
30
+ super('')
31
+ end
32
+
33
+ # @see DirectiveNode#value
34
+ def value; raise NotImplementedError; end
35
+
36
+ # @see DirectiveNode#resolved_value
37
+ def resolved_value
38
+ @resolved_value ||= "@#{name} #{condition.to_css}"
39
+ end
40
+
41
+ # True when the directive has no visible children.
42
+ #
43
+ # @return [Boolean]
44
+ def invisible?
45
+ children.all? {|c| c.invisible?}
46
+ end
47
+
48
+ # @see Node#bubbles?
49
+ def bubbles?; true; end
50
+ end
51
+ end
@@ -150,6 +150,10 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
150
150
  "#{tab_str}@media #{node.query.to_src(@options)}#{yield}"
151
151
  end
152
152
 
153
+ def visit_supports(node)
154
+ "#{tab_str}@#{node.name} #{node.condition.to_src(@options)}#{yield}"
155
+ end
156
+
153
157
  def visit_cssimport(node)
154
158
  if node.uri.is_a?(Sass::Script::Node)
155
159
  str = "#{tab_str}@import #{node.uri.to_sass(@options)}"
@@ -121,23 +121,19 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
121
121
  # Bubbles the `@media` directive up through RuleNodes
122
122
  # and merges it with other `@media` directives.
123
123
  def visit_media(node)
124
- if parent.is_a?(Sass::Tree::RuleNode)
125
- new_rule = parent.dup
126
- new_rule.children = node.children
127
- node.children = with_parent(node) {Array(visit(new_rule))}
128
- # If the last child is actually the end of the group,
129
- # the parent's cssize will set it properly
130
- node.children.last.group_end = false unless node.children.empty?
131
- else
132
- yield
133
- end
134
-
124
+ yield unless bubble(node)
135
125
  media = node.children.select {|c| c.is_a?(Sass::Tree::MediaNode)}
136
126
  node.children.reject! {|c| c.is_a?(Sass::Tree::MediaNode)}
137
127
  media = media.select {|n| n.query = n.query.merge(node.query)}
138
128
  (node.children.empty? ? [] : [node]) + media
139
129
  end
140
130
 
131
+ # Bubbles the `@supports` directive up through RuleNodes.
132
+ def visit_supports(node)
133
+ yield unless bubble(node)
134
+ node
135
+ end
136
+
141
137
  # Asserts that all the traced children are valid in their new location.
142
138
  def visit_trace(node)
143
139
  # Don't use #visit_children to avoid adding the trace node to the list of parents.
@@ -176,8 +172,8 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
176
172
 
177
173
  yield
178
174
 
179
- rules = node.children.select {|c| c.is_a?(Sass::Tree::RuleNode) || c.is_a?(Sass::Tree::MediaNode)}
180
- props = node.children.reject {|c| c.is_a?(Sass::Tree::RuleNode) || c.is_a?(Sass::Tree::MediaNode) || c.invisible?}
175
+ rules = node.children.select {|c| c.is_a?(Sass::Tree::RuleNode) || c.bubbles?}
176
+ props = node.children.reject {|c| c.is_a?(Sass::Tree::RuleNode) || c.bubbles? || c.invisible?}
181
177
 
182
178
  unless props.empty?
183
179
  node.children = props
@@ -189,4 +185,17 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
189
185
 
190
186
  rules
191
187
  end
188
+
189
+ private
190
+
191
+ def bubble(node)
192
+ return unless parent.is_a?(Sass::Tree::RuleNode)
193
+ new_rule = parent.dup
194
+ new_rule.children = node.children
195
+ node.children = with_parent(node) {Array(visit(new_rule))}
196
+ # If the last child is actually the end of the group,
197
+ # the parent's cssize will set it properly
198
+ node.children.last.group_end = false unless node.children.empty?
199
+ true
200
+ end
192
201
  end
@@ -94,4 +94,9 @@ class Sass::Tree::Visitors::DeepCopy < Sass::Tree::Visitors::Base
94
94
  node.query = node.query.deep_copy
95
95
  yield
96
96
  end
97
+
98
+ def visit_supports(node)
99
+ node.condition = node.condition.deep_copy
100
+ yield
101
+ end
97
102
  end
@@ -302,6 +302,12 @@ END
302
302
  yield
303
303
  end
304
304
 
305
+ def visit_supports(node)
306
+ node.condition = node.condition.deep_copy
307
+ node.condition.perform(@environment)
308
+ yield
309
+ end
310
+
305
311
  def visit_cssimport(node)
306
312
  node.resolved_uri = run_interp([node.uri])
307
313
  if node.query
@@ -104,4 +104,9 @@ class Sass::Tree::Visitors::SetOptions < Sass::Tree::Visitors::Base
104
104
  node.query.options = @options
105
105
  yield
106
106
  end
107
+
108
+ def visit_supports(node)
109
+ node.condition.options = @options
110
+ yield
111
+ end
107
112
  end
@@ -113,6 +113,10 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
113
113
  str
114
114
  end
115
115
 
116
+ def visit_supports(node)
117
+ visit_media(node)
118
+ end
119
+
116
120
  def visit_cssimport(node)
117
121
  visit_directive(node)
118
122
  end
@@ -1184,6 +1184,28 @@ $val: 20;
1184
1184
  SCSS
1185
1185
  end
1186
1186
 
1187
+ def test_supports_with_expressions
1188
+ assert_renders <<SASS, <<SCSS
1189
+ $query: "(feature1: val)"
1190
+ $feature: feature2
1191
+ $val: val
1192
+
1193
+ @supports \#{$query} and ($feature: $val) or (not ($feature + 3: $val + 4))
1194
+ foo
1195
+ a: b
1196
+ SASS
1197
+ $query: "(feature1: val)";
1198
+ $feature: feature2;
1199
+ $val: val;
1200
+
1201
+ @supports \#{$query} and ($feature: $val) or (not ($feature + 3: $val + 4)) {
1202
+ foo {
1203
+ a: b;
1204
+ }
1205
+ }
1206
+ SCSS
1207
+ end
1208
+
1187
1209
  # Hacks
1188
1210
 
1189
1211
  def test_declaration_hacks
@@ -181,591 +181,115 @@ SCSS
181
181
  end
182
182
 
183
183
  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
184
+ assert_unification '.foo.bar', '.baz {@extend .foo}', '.foo.bar, .bar.baz'
185
+ assert_unification '.foo.baz', '.baz {@extend .foo}', '.baz'
199
186
  end
200
187
 
201
188
  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
217
-
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
189
+ assert_unification '.foo.bar', '#baz {@extend .foo}', '.foo.bar, .bar#baz'
190
+ assert_unification '.foo#baz', '#baz {@extend .foo}', '#baz'
191
+ assert_unification '.foo#baz', '#bar {@extend .foo}', '.foo#baz'
225
192
  end
226
193
 
227
194
  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
235
-
236
- assert_equal <<CSS, render(<<SCSS)
237
- .foo, *|* {
238
- a: b; }
239
- CSS
240
- .foo {a: b}
241
- *|* {@extend .foo}
242
- SCSS
243
-
244
- assert_equal <<CSS, render(<<SCSS)
245
- .bar {
246
- a: b; }
247
- CSS
248
- .foo.bar {a: b}
249
- * {@extend .foo}
250
- SCSS
251
-
252
- assert_equal <<CSS, render(<<SCSS)
253
- .bar {
254
- a: b; }
255
- CSS
256
- .foo.bar {a: b}
257
- *|* {@extend .foo}
258
- SCSS
259
-
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
195
+ assert_unification '.foo', '* {@extend .foo}', '.foo, *'
196
+ assert_unification '.foo', '*|* {@extend .foo}', '.foo, *|*'
197
+ assert_unification '.foo.bar', '* {@extend .foo}', '.bar'
198
+ assert_unification '.foo.bar', '*|* {@extend .foo}', '.bar'
199
+ assert_unification '.foo.bar', 'ns|* {@extend .foo}', '.foo.bar, ns|*.bar'
267
200
  end
268
201
 
269
202
  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
277
-
278
- assert_equal <<CSS, render(<<SCSS)
279
- * {
280
- a: b; }
281
- CSS
282
- *.foo {a: b}
283
- *|* {@extend .foo}
284
- SCSS
285
-
286
- assert_equal <<CSS, render(<<SCSS)
287
- *|*.foo, * {
288
- a: b; }
289
- CSS
290
- *|*.foo {a: b}
291
- * {@extend .foo}
292
- SCSS
293
-
294
- assert_equal <<CSS, render(<<SCSS)
295
- *|* {
296
- a: b; }
297
- CSS
298
- *|*.foo {a: b}
299
- *|* {@extend .foo}
300
- SCSS
301
-
302
- assert_equal <<CSS, render(<<SCSS)
303
- *.foo, ns|* {
304
- a: b; }
305
- CSS
306
- *.foo {a: b}
307
- ns|* {@extend .foo}
308
- SCSS
309
-
310
- assert_equal <<CSS, render(<<SCSS)
311
- *|*.foo, ns|* {
312
- a: b; }
313
- CSS
314
- *|*.foo {a: b}
315
- ns|* {@extend .foo}
316
- SCSS
203
+ assert_unification '*.foo', '* {@extend .foo}', '*'
204
+ assert_unification '*.foo', '*|* {@extend .foo}', '*'
205
+ assert_unification '*|*.foo', '* {@extend .foo}', '*|*.foo, *'
206
+ assert_unification '*|*.foo', '*|* {@extend .foo}', '*|*'
207
+ assert_unification '*.foo', 'ns|* {@extend .foo}', '*.foo, ns|*'
208
+ assert_unification '*|*.foo', 'ns|* {@extend .foo}', '*|*.foo, ns|*'
317
209
  end
318
210
 
319
211
  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
327
-
328
- assert_equal <<CSS, render(<<SCSS)
329
- ns|* {
330
- a: b; }
331
- CSS
332
- ns|*.foo {a: b}
333
- *|* {@extend .foo}
334
- SCSS
335
-
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
343
-
344
- assert_equal <<CSS, render(<<SCSS)
345
- ns|* {
346
- a: b; }
347
- CSS
348
- ns|*.foo {a: b}
349
- ns|* {@extend .foo}
350
- SCSS
212
+ assert_unification 'ns|*.foo', '* {@extend .foo}', 'ns|*'
213
+ assert_unification 'ns|*.foo', '*|* {@extend .foo}', 'ns|*'
214
+ assert_unification 'ns1|*.foo', 'ns2|* {@extend .foo}', 'ns1|*.foo'
215
+ assert_unification 'ns|*.foo', 'ns|* {@extend .foo}', 'ns|*'
351
216
  end
352
217
 
353
218
  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
361
-
362
- assert_equal <<CSS, render(<<SCSS)
363
- a {
364
- a: b; }
365
- CSS
366
- a.foo {a: b}
367
- *|* {@extend .foo}
368
- SCSS
369
-
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
377
-
378
- assert_equal <<CSS, render(<<SCSS)
379
- *|a {
380
- a: b; }
381
- CSS
382
- *|a.foo {a: b}
383
- *|* {@extend .foo}
384
- SCSS
385
-
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
393
-
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
219
+ assert_unification 'a.foo', '* {@extend .foo}', 'a'
220
+ assert_unification 'a.foo', '*|* {@extend .foo}', 'a'
221
+ assert_unification '*|a.foo', '* {@extend .foo}', '*|a.foo, a'
222
+ assert_unification '*|a.foo', '*|* {@extend .foo}', '*|a'
223
+ assert_unification 'a.foo', 'ns|* {@extend .foo}', 'a.foo, ns|a'
224
+ assert_unification '*|a.foo', 'ns|* {@extend .foo}', '*|a.foo, ns|a'
401
225
  end
402
226
 
403
227
  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
411
-
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
419
-
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
427
-
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
228
+ assert_unification 'ns|a.foo', '* {@extend .foo}', 'ns|a'
229
+ assert_unification 'ns|a.foo', '*|* {@extend .foo}', 'ns|a'
230
+ assert_unification 'ns1|a.foo', 'ns2|* {@extend .foo}', 'ns1|a.foo'
231
+ assert_unification 'ns|a.foo', 'ns|* {@extend .foo}', 'ns|a'
435
232
  end
436
233
 
437
234
  def test_element_unification_with_simple_target
438
- assert_equal <<CSS, render(<<SCSS)
439
- .foo, a {
440
- a: b; }
441
- CSS
442
- .foo {a: b}
443
- a {@extend .foo}
444
- SCSS
445
-
446
- assert_equal <<CSS, render(<<SCSS)
447
- .foo.bar, a.bar {
448
- a: b; }
449
- CSS
450
- .foo.bar {a: b}
451
- a {@extend .foo}
452
- SCSS
453
-
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
461
-
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
235
+ assert_unification '.foo', 'a {@extend .foo}', '.foo, a'
236
+ assert_unification '.foo.bar', 'a {@extend .foo}', '.foo.bar, a.bar'
237
+ assert_unification '.foo.bar', '*|a {@extend .foo}', '.foo.bar, *|a.bar'
238
+ assert_unification '.foo.bar', 'ns|a {@extend .foo}', '.foo.bar, ns|a.bar'
469
239
  end
470
240
 
471
241
  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
479
-
480
- assert_equal <<CSS, render(<<SCSS)
481
- *.foo, a {
482
- a: b; }
483
- CSS
484
- *.foo {a: b}
485
- *|a {@extend .foo}
486
- SCSS
487
-
488
- assert_equal <<CSS, render(<<SCSS)
489
- *|*.foo, a {
490
- a: b; }
491
- CSS
492
- *|*.foo {a: b}
493
- a {@extend .foo}
494
- SCSS
495
-
496
- assert_equal <<CSS, render(<<SCSS)
497
- *|*.foo, *|a {
498
- a: b; }
499
- CSS
500
- *|*.foo {a: b}
501
- *|a {@extend .foo}
502
- SCSS
503
-
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
511
-
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
242
+ assert_unification '*.foo', 'a {@extend .foo}', '*.foo, a'
243
+ assert_unification '*.foo', '*|a {@extend .foo}', '*.foo, a'
244
+ assert_unification '*|*.foo', 'a {@extend .foo}', '*|*.foo, a'
245
+ assert_unification '*|*.foo', '*|a {@extend .foo}', '*|*.foo, *|a'
246
+ assert_unification '*.foo', 'ns|a {@extend .foo}', '*.foo, ns|a'
247
+ assert_unification '*|*.foo', 'ns|a {@extend .foo}', '*|*.foo, ns|a'
519
248
  end
520
249
 
521
250
  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
529
-
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
537
-
538
- assert_equal <<CSS, render(<<SCSS)
539
- ns1|*.foo {
540
- a: b; }
541
- CSS
542
- ns1|*.foo {a: b}
543
- ns2|a {@extend .foo}
544
- SCSS
545
-
546
- assert_equal <<CSS, render(<<SCSS)
547
- ns|*.foo, ns|a {
548
- a: b; }
549
- CSS
550
- ns|*.foo {a: b}
551
- ns|a {@extend .foo}
552
- SCSS
251
+ assert_unification 'ns|*.foo', 'a {@extend .foo}', 'ns|*.foo, ns|a'
252
+ assert_unification 'ns|*.foo', '*|a {@extend .foo}', 'ns|*.foo, ns|a'
253
+ assert_unification 'ns1|*.foo', 'ns2|a {@extend .foo}', 'ns1|*.foo'
254
+ assert_unification 'ns|*.foo', 'ns|a {@extend .foo}', 'ns|*.foo, ns|a'
553
255
  end
554
256
 
555
257
  def test_element_unification_with_namespaceless_element_target
556
- assert_equal <<CSS, render(<<SCSS)
557
- a {
558
- a: b; }
559
- CSS
560
- a.foo {a: b}
561
- a {@extend .foo}
562
- SCSS
563
-
564
- assert_equal <<CSS, render(<<SCSS)
565
- a {
566
- a: b; }
567
- CSS
568
- a.foo {a: b}
569
- *|a {@extend .foo}
570
- SCSS
571
-
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
579
-
580
- assert_equal <<CSS, render(<<SCSS)
581
- *|a {
582
- a: b; }
583
- CSS
584
- *|a.foo {a: b}
585
- *|a {@extend .foo}
586
- SCSS
587
-
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
595
-
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
603
-
604
- assert_equal <<CSS, render(<<SCSS)
605
- a.foo {
606
- a: b; }
607
- CSS
608
- a.foo {a: b}
609
- h1 {@extend .foo}
610
- SCSS
258
+ assert_unification 'a.foo', 'a {@extend .foo}', 'a'
259
+ assert_unification 'a.foo', '*|a {@extend .foo}', 'a'
260
+ assert_unification '*|a.foo', 'a {@extend .foo}', '*|a.foo, a'
261
+ assert_unification '*|a.foo', '*|a {@extend .foo}', '*|a'
262
+ assert_unification 'a.foo', 'ns|a {@extend .foo}', 'a.foo, ns|a'
263
+ assert_unification '*|a.foo', 'ns|a {@extend .foo}', '*|a.foo, ns|a'
264
+ assert_unification 'a.foo', 'h1 {@extend .foo}', 'a.foo'
611
265
  end
612
266
 
613
267
  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
621
-
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
629
-
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
637
-
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
268
+ assert_unification 'ns|a.foo', 'a {@extend .foo}', 'ns|a'
269
+ assert_unification 'ns|a.foo', '*|a {@extend .foo}', 'ns|a'
270
+ assert_unification 'ns1|a.foo', 'ns2|a {@extend .foo}', 'ns1|a.foo'
271
+ assert_unification 'ns|a.foo', 'ns|a {@extend .foo}', 'ns|a'
645
272
  end
646
273
 
647
274
  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
655
-
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
663
-
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
671
-
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
679
-
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
275
+ assert_unification '[foo=bar].baz', '[foo=baz] {@extend .baz}', '[foo=bar].baz, [foo=bar][foo=baz]'
276
+ assert_unification '[foo=bar].baz', '[foo^=bar] {@extend .baz}', '[foo=bar].baz, [foo=bar][foo^=bar]'
277
+ assert_unification '[foo=bar].baz', '[foot=bar] {@extend .baz}', '[foo=bar].baz, [foo=bar][foot=bar]'
278
+ assert_unification '[foo=bar].baz', '[ns|foo=bar] {@extend .baz}', '[foo=bar].baz, [foo=bar][ns|foo=bar]'
279
+ assert_unification '%-a [foo=bar].bar', '[foo=bar] {@extend .bar}', '-a [foo=bar]'
687
280
  end
688
281
 
689
282
  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
697
-
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
705
-
706
- assert_equal <<CSS, render(<<SCSS)
707
- ::foo.baz {
708
- a: b; }
709
- CSS
710
- ::foo.baz {a: b}
711
- ::bar {@extend .baz}
712
- SCSS
713
-
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
721
-
722
- assert_equal <<CSS, render(<<SCSS)
723
- ::foo {
724
- a: b; }
725
- CSS
726
- ::foo.baz {a: b}
727
- ::foo {@extend .baz}
728
- SCSS
729
-
730
- assert_equal <<CSS, render(<<SCSS)
731
- ::foo(2n+1) {
732
- a: b; }
733
- CSS
734
- ::foo(2n+1).baz {a: b}
735
- ::foo(2n+1) {@extend .baz}
736
- SCSS
737
-
738
- assert_equal <<CSS, render(<<SCSS)
739
- :foo.baz, :foo:bar {
740
- a: b; }
741
- CSS
742
- :foo.baz {a: b}
743
- :bar {@extend .baz}
744
- SCSS
745
-
746
- assert_equal <<CSS, render(<<SCSS)
747
- .baz:foo, :foo:after {
748
- a: b; }
749
- CSS
750
- .baz:foo {a: b}
751
- :after {@extend .baz}
752
- SCSS
753
-
754
- assert_equal <<CSS, render(<<SCSS)
755
- .baz:after, :foo:after {
756
- a: b; }
757
- CSS
758
- .baz:after {a: b}
759
- :foo {@extend .baz}
760
- SCSS
761
-
762
- assert_equal <<CSS, render(<<SCSS)
763
- :foo {
764
- a: b; }
765
- CSS
766
- :foo.baz {a: b}
767
- :foo {@extend .baz}
768
- SCSS
283
+ assert_unification ':foo.baz', ':foo(2n+1) {@extend .baz}', ':foo.baz, :foo:foo(2n+1)'
284
+ assert_unification ':foo.baz', '::foo {@extend .baz}', ':foo.baz, :foo::foo'
285
+ assert_unification '::foo.baz', '::bar {@extend .baz}', '::foo.baz'
286
+ assert_unification '::foo.baz', '::foo(2n+1) {@extend .baz}', '::foo.baz'
287
+ assert_unification '::foo.baz', '::foo {@extend .baz}', '::foo'
288
+ assert_unification '::foo(2n+1).baz', '::foo(2n+1) {@extend .baz}', '::foo(2n+1)'
289
+ assert_unification ':foo.baz', ':bar {@extend .baz}', ':foo.baz, :foo:bar'
290
+ assert_unification '.baz:foo', ':after {@extend .baz}', '.baz:foo, :foo:after'
291
+ assert_unification '.baz:after', ':foo {@extend .baz}', '.baz:after, :foo:after'
292
+ assert_unification ':foo.baz', ':foo {@extend .baz}', ':foo'
769
293
  end
770
294
 
771
295
  def test_pseudoelement_remains_at_end_of_selector
@@ -851,29 +375,9 @@ SCSS
851
375
  end
852
376
 
853
377
  def test_negation_unification
854
- assert_equal <<CSS, render(<<SCSS)
855
- :not(.foo).baz, :not(.foo):not(.bar) {
856
- a: b; }
857
- CSS
858
- :not(.foo).baz {a: b}
859
- :not(.bar) {@extend .baz}
860
- SCSS
861
-
862
- assert_equal <<CSS, render(<<SCSS)
863
- :not(.foo) {
864
- a: b; }
865
- CSS
866
- :not(.foo).baz {a: b}
867
- :not(.foo) {@extend .baz}
868
- SCSS
869
-
870
- assert_equal <<CSS, render(<<SCSS)
871
- :not([a=b]) {
872
- a: b; }
873
- CSS
874
- :not([a=b]).baz {a: b}
875
- :not([a = b]) {@extend .baz}
876
- SCSS
378
+ assert_unification ':not(.foo).baz', ':not(.bar) {@extend .baz}', ':not(.foo).baz, :not(.foo):not(.bar)'
379
+ assert_unification ':not(.foo).baz', ':not(.foo) {@extend .baz}', ':not(.foo)'
380
+ assert_unification ':not([a=b]).baz', ':not([a = b]) {@extend .baz}', ':not([a=b])'
877
381
  end
878
382
 
879
383
  def test_comma_extendee
@@ -2023,6 +1527,36 @@ d {@extend a}
2023
1527
  SCSS
2024
1528
  end
2025
1529
 
1530
+ def test_extend_redundancy_elimination_when_it_would_reduce_specificity
1531
+ assert_equal <<CSS, render(<<SCSS)
1532
+ a, a.foo {
1533
+ x: y; }
1534
+ CSS
1535
+ a {x: y}
1536
+ a.foo {@extend a}
1537
+ SCSS
1538
+ end
1539
+
1540
+ def test_extend_redundancy_elimination_when_it_would_preserve_specificity
1541
+ assert_equal <<CSS, render(<<SCSS)
1542
+ .bar a {
1543
+ x: y; }
1544
+ CSS
1545
+ .bar a {x: y}
1546
+ a.foo {@extend a}
1547
+ SCSS
1548
+ end
1549
+
1550
+ def test_extend_redundancy_elimination_never_eliminates_base_selector
1551
+ assert_equal <<CSS, render(<<SCSS)
1552
+ a.foo, .foo {
1553
+ x: y; }
1554
+ CSS
1555
+ a.foo {x: y}
1556
+ .foo {@extend a}
1557
+ SCSS
1558
+ end
1559
+
2026
1560
  def test_extend_cross_branch_redundancy_elimination
2027
1561
  assert_equal <<CSS, render(<<SCSS)
2028
1562
  a c d, b c a d {
@@ -2046,6 +1580,17 @@ SCSS
2046
1580
 
2047
1581
  private
2048
1582
 
1583
+ def assert_unification(selector, extension, unified)
1584
+ assert_equal <<CSS, render(<<SCSS)
1585
+ #{unified.split(', ').map {|s| "-a #{s}"}.join(', ')} {
1586
+ a: b; }
1587
+ CSS
1588
+ %-a #{selector} {a: b}
1589
+ #{extension}
1590
+ -a {@extend %-a}
1591
+ SCSS
1592
+ end
1593
+
2049
1594
  def render(sass, options = {})
2050
1595
  munge_filename options
2051
1596
  Sass::Engine.new(sass, {:syntax => :scss}.merge(options)).render