PageRenderTime 38ms CodeModel.GetById 1ms RepoModel.GetById 1ms app.codeStats 0ms

/training-web/vendor/bundle/gems/sass-3.2.9/test/sass/extend_test.rb

https://bitbucket.org/ohimmelreich/asalia-training
Ruby | 1403 lines | 1235 code | 157 blank | 11 comment | 4 complexity | 1528d74a03749376a01ff54b0d53938e MD5 | raw file
  1. #!/usr/bin/env ruby
  2. require File.dirname(__FILE__) + '/../test_helper'
  3. class ExtendTest < Test::Unit::TestCase
  4. def test_basic
  5. assert_equal <<CSS, render(<<SCSS)
  6. .foo, .bar {
  7. a: b; }
  8. CSS
  9. .foo {a: b}
  10. .bar {@extend .foo}
  11. SCSS
  12. assert_equal <<CSS, render(<<SCSS)
  13. .foo, .bar {
  14. a: b; }
  15. CSS
  16. .bar {@extend .foo}
  17. .foo {a: b}
  18. SCSS
  19. assert_equal <<CSS, render(<<SCSS)
  20. .foo, .bar {
  21. a: b; }
  22. .bar {
  23. c: d; }
  24. CSS
  25. .foo {a: b}
  26. .bar {c: d; @extend .foo}
  27. SCSS
  28. assert_equal <<CSS, render(<<SCSS)
  29. .foo, .bar {
  30. a: b; }
  31. .bar {
  32. c: d; }
  33. CSS
  34. .foo {a: b}
  35. .bar {@extend .foo; c: d}
  36. SCSS
  37. end
  38. def test_indented_syntax
  39. assert_equal <<CSS, render(<<SASS, :syntax => :sass)
  40. .foo, .bar {
  41. a: b; }
  42. CSS
  43. .foo
  44. a: b
  45. .bar
  46. @extend .foo
  47. SASS
  48. assert_equal <<CSS, render(<<SASS, :syntax => :sass)
  49. .foo, .bar {
  50. a: b; }
  51. CSS
  52. .foo
  53. a: b
  54. .bar
  55. @extend \#{".foo"}
  56. SASS
  57. end
  58. def test_multiple_targets
  59. assert_equal <<CSS, render(<<SCSS)
  60. .foo, .bar {
  61. a: b; }
  62. .blip .foo, .blip .bar {
  63. c: d; }
  64. CSS
  65. .foo {a: b}
  66. .bar {@extend .foo}
  67. .blip .foo {c: d}
  68. SCSS
  69. end
  70. def test_multiple_extendees
  71. assert_equal <<CSS, render(<<SCSS)
  72. .foo, .baz {
  73. a: b; }
  74. .bar, .baz {
  75. c: d; }
  76. CSS
  77. .foo {a: b}
  78. .bar {c: d}
  79. .baz {@extend .foo; @extend .bar}
  80. SCSS
  81. end
  82. def test_multiple_extends_with_single_extender_and_single_target
  83. assert_extends('.foo .bar', '.baz {@extend .foo; @extend .bar}',
  84. '.foo .bar, .baz .bar, .foo .baz, .baz .baz')
  85. assert_extends '.foo.bar', '.baz {@extend .foo; @extend .bar}', '.foo.bar, .baz'
  86. end
  87. def test_multiple_extends_with_multiple_extenders_and_single_target
  88. assert_equal <<CSS, render(<<SCSS)
  89. .foo .bar, .baz .bar, .foo .bang, .baz .bang {
  90. a: b; }
  91. CSS
  92. .foo .bar {a: b}
  93. .baz {@extend .foo}
  94. .bang {@extend .bar}
  95. SCSS
  96. assert_equal <<CSS, render(<<SCSS)
  97. .foo.bar, .bar.baz, .baz.bang, .foo.bang {
  98. a: b; }
  99. CSS
  100. .foo.bar {a: b}
  101. .baz {@extend .foo}
  102. .bang {@extend .bar}
  103. SCSS
  104. end
  105. def test_chained_extends
  106. assert_equal <<CSS, render(<<SCSS)
  107. .foo, .bar, .baz, .bip {
  108. a: b; }
  109. CSS
  110. .foo {a: b}
  111. .bar {@extend .foo}
  112. .baz {@extend .bar}
  113. .bip {@extend .bar}
  114. SCSS
  115. end
  116. def test_dynamic_extendee
  117. assert_extends '.foo', '.bar {@extend #{".foo"}}', '.foo, .bar'
  118. assert_extends('[baz^="blip12px"]', '.bar {@extend [baz^="blip#{12px}"]}',
  119. '[baz^="blip12px"], .bar')
  120. end
  121. def test_nested_target
  122. assert_extends '.foo .bar', '.baz {@extend .bar}', '.foo .bar, .foo .baz'
  123. end
  124. def test_target_with_child
  125. assert_extends '.foo .bar', '.baz {@extend .foo}', '.foo .bar, .baz .bar'
  126. end
  127. def test_class_unification
  128. assert_unification '.foo.bar', '.baz {@extend .foo}', '.foo.bar, .bar.baz'
  129. assert_unification '.foo.baz', '.baz {@extend .foo}', '.baz'
  130. end
  131. def test_id_unification
  132. assert_unification '.foo.bar', '#baz {@extend .foo}', '.foo.bar, .bar#baz'
  133. assert_unification '.foo#baz', '#baz {@extend .foo}', '#baz'
  134. assert_extend_doesnt_match('#bar', '.foo', :failed_to_unify, 2) do
  135. assert_unification '.foo#baz', '#bar {@extend .foo}', '.foo#baz'
  136. end
  137. end
  138. def test_universal_unification_with_simple_target
  139. assert_unification '.foo', '* {@extend .foo}', '.foo, *'
  140. assert_unification '.foo', '*|* {@extend .foo}', '.foo, *|*'
  141. assert_unification '.foo.bar', '* {@extend .foo}', '.bar'
  142. assert_unification '.foo.bar', '*|* {@extend .foo}', '.bar'
  143. assert_unification '.foo.bar', 'ns|* {@extend .foo}', '.foo.bar, ns|*.bar'
  144. end
  145. def test_universal_unification_with_namespaceless_universal_target
  146. assert_unification '*.foo', '* {@extend .foo}', '*'
  147. assert_unification '*.foo', '*|* {@extend .foo}', '*'
  148. assert_unification '*|*.foo', '* {@extend .foo}', '*|*.foo, *'
  149. assert_unification '*|*.foo', '*|* {@extend .foo}', '*|*'
  150. assert_unification '*.foo', 'ns|* {@extend .foo}', '*.foo, ns|*'
  151. assert_unification '*|*.foo', 'ns|* {@extend .foo}', '*|*.foo, ns|*'
  152. end
  153. def test_universal_unification_with_namespaced_universal_target
  154. assert_unification 'ns|*.foo', '* {@extend .foo}', 'ns|*'
  155. assert_unification 'ns|*.foo', '*|* {@extend .foo}', 'ns|*'
  156. assert_extend_doesnt_match('ns2|*', '.foo', :failed_to_unify, 2) do
  157. assert_unification 'ns1|*.foo', 'ns2|* {@extend .foo}', 'ns1|*.foo'
  158. end
  159. assert_unification 'ns|*.foo', 'ns|* {@extend .foo}', 'ns|*'
  160. end
  161. def test_universal_unification_with_namespaceless_element_target
  162. assert_unification 'a.foo', '* {@extend .foo}', 'a'
  163. assert_unification 'a.foo', '*|* {@extend .foo}', 'a'
  164. assert_unification '*|a.foo', '* {@extend .foo}', '*|a.foo, a'
  165. assert_unification '*|a.foo', '*|* {@extend .foo}', '*|a'
  166. assert_unification 'a.foo', 'ns|* {@extend .foo}', 'a.foo, ns|a'
  167. assert_unification '*|a.foo', 'ns|* {@extend .foo}', '*|a.foo, ns|a'
  168. end
  169. def test_universal_unification_with_namespaced_element_target
  170. assert_unification 'ns|a.foo', '* {@extend .foo}', 'ns|a'
  171. assert_unification 'ns|a.foo', '*|* {@extend .foo}', 'ns|a'
  172. assert_extend_doesnt_match('ns2|*', '.foo', :failed_to_unify, 2) do
  173. assert_unification 'ns1|a.foo', 'ns2|* {@extend .foo}', 'ns1|a.foo'
  174. end
  175. assert_unification 'ns|a.foo', 'ns|* {@extend .foo}', 'ns|a'
  176. end
  177. def test_element_unification_with_simple_target
  178. assert_unification '.foo', 'a {@extend .foo}', '.foo, a'
  179. assert_unification '.foo.bar', 'a {@extend .foo}', '.foo.bar, a.bar'
  180. assert_unification '.foo.bar', '*|a {@extend .foo}', '.foo.bar, *|a.bar'
  181. assert_unification '.foo.bar', 'ns|a {@extend .foo}', '.foo.bar, ns|a.bar'
  182. end
  183. def test_element_unification_with_namespaceless_universal_target
  184. assert_unification '*.foo', 'a {@extend .foo}', '*.foo, a'
  185. assert_unification '*.foo', '*|a {@extend .foo}', '*.foo, a'
  186. assert_unification '*|*.foo', 'a {@extend .foo}', '*|*.foo, a'
  187. assert_unification '*|*.foo', '*|a {@extend .foo}', '*|*.foo, *|a'
  188. assert_unification '*.foo', 'ns|a {@extend .foo}', '*.foo, ns|a'
  189. assert_unification '*|*.foo', 'ns|a {@extend .foo}', '*|*.foo, ns|a'
  190. end
  191. def test_element_unification_with_namespaced_universal_target
  192. assert_unification 'ns|*.foo', 'a {@extend .foo}', 'ns|*.foo, ns|a'
  193. assert_unification 'ns|*.foo', '*|a {@extend .foo}', 'ns|*.foo, ns|a'
  194. assert_extend_doesnt_match('ns2|a', '.foo', :failed_to_unify, 2) do
  195. assert_unification 'ns1|*.foo', 'ns2|a {@extend .foo}', 'ns1|*.foo'
  196. end
  197. assert_unification 'ns|*.foo', 'ns|a {@extend .foo}', 'ns|*.foo, ns|a'
  198. end
  199. def test_element_unification_with_namespaceless_element_target
  200. assert_unification 'a.foo', 'a {@extend .foo}', 'a'
  201. assert_unification 'a.foo', '*|a {@extend .foo}', 'a'
  202. assert_unification '*|a.foo', 'a {@extend .foo}', '*|a.foo, a'
  203. assert_unification '*|a.foo', '*|a {@extend .foo}', '*|a'
  204. assert_unification 'a.foo', 'ns|a {@extend .foo}', 'a.foo, ns|a'
  205. assert_unification '*|a.foo', 'ns|a {@extend .foo}', '*|a.foo, ns|a'
  206. assert_extend_doesnt_match('h1', '.foo', :failed_to_unify, 2) do
  207. assert_unification 'a.foo', 'h1 {@extend .foo}', 'a.foo'
  208. end
  209. end
  210. def test_element_unification_with_namespaced_element_target
  211. assert_unification 'ns|a.foo', 'a {@extend .foo}', 'ns|a'
  212. assert_unification 'ns|a.foo', '*|a {@extend .foo}', 'ns|a'
  213. assert_extend_doesnt_match('ns2|a', '.foo', :failed_to_unify, 2) do
  214. assert_unification 'ns1|a.foo', 'ns2|a {@extend .foo}', 'ns1|a.foo'
  215. end
  216. assert_unification 'ns|a.foo', 'ns|a {@extend .foo}', 'ns|a'
  217. end
  218. def test_attribute_unification
  219. assert_unification '[foo=bar].baz', '[foo=baz] {@extend .baz}', '[foo=bar].baz, [foo=bar][foo=baz]'
  220. assert_unification '[foo=bar].baz', '[foo^=bar] {@extend .baz}', '[foo=bar].baz, [foo=bar][foo^=bar]'
  221. assert_unification '[foo=bar].baz', '[foot=bar] {@extend .baz}', '[foo=bar].baz, [foo=bar][foot=bar]'
  222. assert_unification '[foo=bar].baz', '[ns|foo=bar] {@extend .baz}', '[foo=bar].baz, [foo=bar][ns|foo=bar]'
  223. assert_unification '%-a [foo=bar].bar', '[foo=bar] {@extend .bar}', '-a [foo=bar]'
  224. end
  225. def test_pseudo_unification
  226. assert_unification ':foo.baz', ':foo(2n+1) {@extend .baz}', ':foo.baz, :foo:foo(2n+1)'
  227. assert_unification ':foo.baz', '::foo {@extend .baz}', ':foo.baz, :foo::foo'
  228. assert_extend_doesnt_match('::bar', '.baz', :failed_to_unify, 2) do
  229. assert_unification '::foo.baz', '::bar {@extend .baz}', '::foo.baz'
  230. end
  231. assert_extend_doesnt_match('::foo(2n+1)', '.baz', :failed_to_unify, 2) do
  232. assert_unification '::foo.baz', '::foo(2n+1) {@extend .baz}', '::foo.baz'
  233. end
  234. assert_unification '::foo.baz', '::foo {@extend .baz}', '::foo'
  235. assert_unification '::foo(2n+1).baz', '::foo(2n+1) {@extend .baz}', '::foo(2n+1)'
  236. assert_unification ':foo.baz', ':bar {@extend .baz}', ':foo.baz, :foo:bar'
  237. assert_unification '.baz:foo', ':after {@extend .baz}', '.baz:foo, :foo:after'
  238. assert_unification '.baz:after', ':foo {@extend .baz}', '.baz:after, :foo:after'
  239. assert_unification ':foo.baz', ':foo {@extend .baz}', ':foo'
  240. end
  241. def test_pseudoelement_remains_at_end_of_selector
  242. assert_extends '.foo::bar', '.baz {@extend .foo}', '.foo::bar, .baz::bar'
  243. assert_extends 'a.foo::bar', '.baz {@extend .foo}', 'a.foo::bar, a.baz::bar'
  244. end
  245. def test_pseudoclass_remains_at_end_of_selector
  246. assert_extends '.foo:bar', '.baz {@extend .foo}', '.foo:bar, .baz:bar'
  247. assert_extends 'a.foo:bar', '.baz {@extend .foo}', 'a.foo:bar, a.baz:bar'
  248. end
  249. def test_not_remains_at_end_of_selector
  250. assert_extends '.foo:not(.bar)', '.baz {@extend .foo}', '.foo:not(.bar), .baz:not(.bar)'
  251. end
  252. def test_pseudoelement_goes_lefter_than_pseudoclass
  253. assert_extends '.foo::bar', '.baz:bang {@extend .foo}', '.foo::bar, .baz:bang::bar'
  254. assert_extends '.foo:bar', '.baz::bang {@extend .foo}', '.foo:bar, .baz:bar::bang'
  255. end
  256. def test_pseudoelement_goes_lefter_than_not
  257. assert_extends '.foo::bar', '.baz:not(.bang) {@extend .foo}', '.foo::bar, .baz:not(.bang)::bar'
  258. assert_extends '.foo:not(.bang)', '.baz::bar {@extend .foo}', '.foo:not(.bang), .baz:not(.bang)::bar'
  259. end
  260. def test_negation_unification
  261. assert_unification ':not(.foo).baz', ':not(.bar) {@extend .baz}', ':not(.foo).baz, :not(.foo):not(.bar)'
  262. assert_unification ':not(.foo).baz', ':not(.foo) {@extend .baz}', ':not(.foo)'
  263. assert_unification ':not([a=b]).baz', ':not([a = b]) {@extend .baz}', ':not([a=b])'
  264. end
  265. def test_comma_extendee
  266. assert_equal <<CSS, render(<<SCSS)
  267. .foo, .baz {
  268. a: b; }
  269. .bar, .baz {
  270. c: d; }
  271. CSS
  272. .foo {a: b}
  273. .bar {c: d}
  274. .baz {@extend .foo, .bar}
  275. SCSS
  276. end
  277. def test_redundant_selector_elimination
  278. assert_equal <<CSS, render(<<SCSS)
  279. .foo.bar, .x, .y {
  280. a: b; }
  281. CSS
  282. .foo.bar {a: b}
  283. .x {@extend .foo, .bar}
  284. .y {@extend .foo, .bar}
  285. SCSS
  286. end
  287. ## Long Extendees
  288. def test_long_extendee
  289. assert_extends '.foo.bar', '.baz {@extend .foo.bar}', '.foo.bar, .baz'
  290. end
  291. def test_long_extendee_requires_all_selectors
  292. assert_extend_doesnt_match('.baz', '.foo.bar', :not_found, 2) do
  293. assert_extends '.foo', '.baz {@extend .foo.bar}', '.foo'
  294. end
  295. end
  296. def test_long_extendee_matches_supersets
  297. assert_extends '.foo.bar.bap', '.baz {@extend .foo.bar}', '.foo.bar.bap, .bap.baz'
  298. end
  299. def test_long_extendee_runs_unification
  300. assert_extends 'ns|*.foo.bar', 'a.baz {@extend .foo.bar}', 'ns|*.foo.bar, ns|a.baz'
  301. end
  302. ## Long Extenders
  303. def test_long_extender
  304. assert_extends '.foo.bar', '.baz.bang {@extend .foo}', '.foo.bar, .bar.baz.bang'
  305. end
  306. def test_long_extender_runs_unification
  307. assert_extends 'ns|*.foo.bar', 'a.baz {@extend .foo}', 'ns|*.foo.bar, ns|a.bar.baz'
  308. end
  309. def test_long_extender_aborts_unification
  310. assert_extend_doesnt_match('h1.baz', '.foo', :failed_to_unify, 2) do
  311. assert_extends 'a.foo#bar', 'h1.baz {@extend .foo}', 'a.foo#bar'
  312. end
  313. assert_extend_doesnt_match('.bang#baz', '.foo', :failed_to_unify, 2) do
  314. assert_extends 'a.foo#bar', '.bang#baz {@extend .foo}', 'a.foo#bar'
  315. end
  316. end
  317. ## Nested Extenders
  318. def test_nested_extender
  319. assert_extends '.foo', 'foo bar {@extend .foo}', '.foo, foo bar'
  320. end
  321. def test_nested_extender_runs_unification
  322. assert_extends '.foo.bar', 'foo bar {@extend .foo}', '.foo.bar, foo bar.bar'
  323. end
  324. def test_nested_extender_aborts_unification
  325. assert_extend_doesnt_match('foo bar', '.foo', :failed_to_unify, 2) do
  326. assert_extends 'baz.foo', 'foo bar {@extend .foo}', 'baz.foo'
  327. end
  328. end
  329. def test_nested_extender_alternates_parents
  330. assert_extends('.baz .bip .foo', 'foo .grank bar {@extend .foo}',
  331. '.baz .bip .foo, .baz .bip foo .grank bar, foo .grank .baz .bip bar')
  332. end
  333. def test_nested_extender_unifies_identical_parents
  334. assert_extends('.baz .bip .foo', '.baz .bip bar {@extend .foo}',
  335. '.baz .bip .foo, .baz .bip bar')
  336. end
  337. def test_nested_extender_unifies_common_substring
  338. assert_extends('.baz .bip .bap .bink .foo', '.brat .bip .bap bar {@extend .foo}',
  339. '.baz .bip .bap .bink .foo, .baz .brat .bip .bap .bink bar, .brat .baz .bip .bap .bink bar')
  340. end
  341. def test_nested_extender_unifies_common_subseq
  342. assert_extends('.a .x .b .y .foo', '.a .n .b .m bar {@extend .foo}',
  343. '.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')
  344. end
  345. def test_nested_extender_chooses_first_subseq
  346. assert_extends('.a .b .c .d .foo', '.c .d .a .b .bar {@extend .foo}',
  347. '.a .b .c .d .foo, .a .b .c .d .a .b .bar')
  348. end
  349. def test_nested_extender_counts_extended_subselectors
  350. assert_extends('.a .bip.bop .foo', '.b .bip .bar {@extend .foo}',
  351. '.a .bip.bop .foo, .a .b .bip.bop .bar, .b .a .bip.bop .bar')
  352. end
  353. def test_nested_extender_counts_extended_superselectors
  354. assert_extends('.a .bip .foo', '.b .bip.bop .bar {@extend .foo}',
  355. '.a .bip .foo, .a .b .bip.bop .bar, .b .a .bip.bop .bar')
  356. end
  357. def test_nested_extender_with_child_selector
  358. assert_extends '.baz .foo', 'foo > bar {@extend .foo}', '.baz .foo, .baz foo > bar'
  359. end
  360. def test_nested_extender_finds_common_selectors_around_child_selector
  361. assert_extends 'a > b c .c1', 'a c .c2 {@extend .c1}', 'a > b c .c1, a > b c .c2'
  362. assert_extends 'a > b c .c1', 'b c .c2 {@extend .c1}', 'a > b c .c1, a > b c .c2'
  363. end
  364. def test_nested_extender_doesnt_find_common_selectors_around_adjacent_sibling_selector
  365. 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'
  366. assert_extends 'a + b c .c1', 'a b .c2 {@extend .c1}', 'a + b c .c1, a a + b c .c2'
  367. assert_extends 'a + b c .c1', 'b c .c2 {@extend .c1}', 'a + b c .c1, a + b c .c2'
  368. end
  369. def test_nested_extender_doesnt_find_common_selectors_around_sibling_selector
  370. 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'
  371. assert_extends 'a ~ b c .c1', 'a b .c2 {@extend .c1}', 'a ~ b c .c1, a a ~ b c .c2'
  372. assert_extends 'a ~ b c .c1', 'b c .c2 {@extend .c1}', 'a ~ b c .c1, a ~ b c .c2'
  373. end
  374. def test_nested_extender_doesnt_find_common_selectors_around_reference_selector
  375. 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'
  376. assert_extends 'a /for/ b c .c1', 'a b .c2 {@extend .c1}', 'a /for/ b c .c1, a a /for/ b c .c2'
  377. assert_extends 'a /for/ b c .c1', 'b c .c2 {@extend .c1}', 'a /for/ b c .c1, a /for/ b c .c2'
  378. end
  379. def test_nested_extender_with_early_child_selectors_doesnt_subseq_them
  380. assert_extends('.bip > .bap .foo', '.grip > .bap .bar {@extend .foo}',
  381. '.bip > .bap .foo, .bip > .bap .grip > .bap .bar, .grip > .bap .bip > .bap .bar')
  382. assert_extends('.bap > .bip .foo', '.bap > .grip .bar {@extend .foo}',
  383. '.bap > .bip .foo, .bap > .bip .bap > .grip .bar, .bap > .grip .bap > .bip .bar')
  384. end
  385. def test_nested_extender_with_child_selector_unifies
  386. assert_extends '.baz.foo', 'foo > bar {@extend .foo}', '.baz.foo, foo > bar.baz'
  387. assert_equal <<CSS, render(<<SCSS)
  388. .baz > .foo, .baz > .bar {
  389. a: b; }
  390. CSS
  391. .baz > {
  392. .foo {a: b}
  393. .bar {@extend .foo}
  394. }
  395. SCSS
  396. assert_equal <<CSS, render(<<SCSS)
  397. .foo .bar, .foo > .baz {
  398. a: b; }
  399. CSS
  400. .foo {
  401. .bar {a: b}
  402. > .baz {@extend .bar}
  403. }
  404. SCSS
  405. end
  406. def test_nested_extender_with_early_child_selectors_doesnt_subseq_them
  407. assert_equal <<CSS, render(<<SCSS)
  408. .foo .bar, .foo .bip > .baz {
  409. a: b; }
  410. CSS
  411. .foo {
  412. .bar {a: b}
  413. .bip > .baz {@extend .bar}
  414. }
  415. SCSS
  416. assert_equal <<CSS, render(<<SCSS)
  417. .foo .bip .bar, .foo .bip .foo > .baz {
  418. a: b; }
  419. CSS
  420. .foo {
  421. .bip .bar {a: b}
  422. > .baz {@extend .bar}
  423. }
  424. SCSS
  425. assert_extends '.foo > .bar', '.bip + .baz {@extend .bar}', '.foo > .bar, .foo > .bip + .baz'
  426. assert_extends '.foo + .bar', '.bip > .baz {@extend .bar}', '.foo + .bar, .bip > .foo + .baz'
  427. assert_extends '.foo > .bar', '.bip > .baz {@extend .bar}', '.foo > .bar, .bip.foo > .baz'
  428. end
  429. def test_nested_extender_with_trailing_child_selector
  430. assert_raise(Sass::SyntaxError, "bar > can't extend: invalid selector") do
  431. render("bar > {@extend .baz}")
  432. end
  433. end
  434. def test_nested_extender_with_sibling_selector
  435. assert_extends '.baz .foo', 'foo + bar {@extend .foo}', '.baz .foo, .baz foo + bar'
  436. end
  437. def test_nested_extender_with_hacky_selector
  438. assert_extends('.baz .foo', 'foo + > > + bar {@extend .foo}',
  439. '.baz .foo, .baz foo + > > + bar, foo .baz + > > + bar')
  440. assert_extends '.baz .foo', '> > bar {@extend .foo}', '.baz .foo, > > .baz bar'
  441. end
  442. def test_nested_extender_merges_with_same_selector
  443. assert_equal <<CSS, render(<<SCSS)
  444. .foo .bar, .foo .baz {
  445. a: b; }
  446. CSS
  447. .foo {
  448. .bar {a: b}
  449. .baz {@extend .bar} }
  450. SCSS
  451. end
  452. def test_nested_extender_with_child_selector_merges_with_same_selector
  453. assert_extends('.foo > .bar .baz', '.foo > .bar .bang {@extend .baz}',
  454. '.foo > .bar .baz, .foo > .bar .bang')
  455. end
  456. # Combinator Unification
  457. def test_combinator_unification_for_hacky_combinators
  458. assert_extends '.a > + x', '.b y {@extend x}', '.a > + x, .a .b > + y, .b .a > + y'
  459. assert_extends '.a x', '.b > + y {@extend x}', '.a x, .a .b > + y, .b .a > + y'
  460. assert_extends '.a > + x', '.b > + y {@extend x}', '.a > + x, .a .b > + y, .b .a > + y'
  461. assert_extends '.a ~ > + x', '.b > + y {@extend x}', '.a ~ > + x, .a .b ~ > + y, .b .a ~ > + y'
  462. assert_extends '.a + > x', '.b > + y {@extend x}', '.a + > x'
  463. assert_extends '.a + > x', '.b > + y {@extend x}', '.a + > x'
  464. assert_extends '.a ~ > + .b > x', '.c > + .d > y {@extend x}', '.a ~ > + .b > x, .a .c ~ > + .d.b > y, .c .a ~ > + .d.b > y'
  465. end
  466. def test_combinator_unification_double_tilde
  467. assert_extends '.a.b ~ x', '.a ~ y {@extend x}', '.a.b ~ x, .a.b ~ y'
  468. assert_extends '.a ~ x', '.a.b ~ y {@extend x}', '.a ~ x, .a.b ~ y'
  469. assert_extends '.a ~ x', '.b ~ y {@extend x}', '.a ~ x, .a ~ .b ~ y, .b ~ .a ~ y, .b.a ~ y'
  470. assert_extends 'a.a ~ x', 'b.b ~ y {@extend x}', 'a.a ~ x, a.a ~ b.b ~ y, b.b ~ a.a ~ y'
  471. end
  472. def test_combinator_unification_tilde_plus
  473. assert_extends '.a.b + x', '.a ~ y {@extend x}', '.a.b + x, .a.b + y'
  474. assert_extends '.a + x', '.a.b ~ y {@extend x}', '.a + x, .a.b ~ .a + y, .a.b + y'
  475. assert_extends '.a + x', '.b ~ y {@extend x}', '.a + x, .b ~ .a + y, .b.a + y'
  476. assert_extends 'a.a + x', 'b.b ~ y {@extend x}', 'a.a + x, b.b ~ a.a + y'
  477. assert_extends '.a.b ~ x', '.a + y {@extend x}', '.a.b ~ x, .a.b ~ .a + y, .a.b + y'
  478. assert_extends '.a ~ x', '.a.b + y {@extend x}', '.a ~ x, .a.b + y'
  479. assert_extends '.a ~ x', '.b + y {@extend x}', '.a ~ x, .a ~ .b + y, .a.b + y'
  480. assert_extends 'a.a ~ x', 'b.b + y {@extend x}', 'a.a ~ x, a.a ~ b.b + y'
  481. end
  482. def test_combinator_unification_angle_sibling
  483. assert_extends '.a > x', '.b ~ y {@extend x}', '.a > x, .a > .b ~ y'
  484. assert_extends '.a > x', '.b + y {@extend x}', '.a > x, .a > .b + y'
  485. assert_extends '.a ~ x', '.b > y {@extend x}', '.a ~ x, .b > .a ~ y'
  486. assert_extends '.a + x', '.b > y {@extend x}', '.a + x, .b > .a + y'
  487. end
  488. def test_combinator_unification_double_angle
  489. assert_extends '.a.b > x', '.b > y {@extend x}', '.a.b > x, .b.a > y'
  490. assert_extends '.a > x', '.a.b > y {@extend x}', '.a > x, .a.b > y'
  491. assert_extends '.a > x', '.b > y {@extend x}', '.a > x, .b.a > y'
  492. assert_extends 'a.a > x', 'b.b > y {@extend x}', 'a.a > x'
  493. end
  494. def test_combinator_unification_double_plus
  495. assert_extends '.a.b + x', '.b + y {@extend x}', '.a.b + x, .b.a + y'
  496. assert_extends '.a + x', '.a.b + y {@extend x}', '.a + x, .a.b + y'
  497. assert_extends '.a + x', '.b + y {@extend x}', '.a + x, .b.a + y'
  498. assert_extends 'a.a + x', 'b.b + y {@extend x}', 'a.a + x'
  499. end
  500. def test_combinator_unification_angle_space
  501. assert_extends '.a.b > x', '.a y {@extend x}', '.a.b > x, .a.b > y'
  502. assert_extends '.a > x', '.a.b y {@extend x}', '.a > x, .a.b .a > y'
  503. assert_extends '.a > x', '.b y {@extend x}', '.a > x, .b .a > y'
  504. assert_extends '.a.b x', '.a > y {@extend x}', '.a.b x, .a.b .a > y'
  505. assert_extends '.a x', '.a.b > y {@extend x}', '.a x, .a.b > y'
  506. assert_extends '.a x', '.b > y {@extend x}', '.a x, .a .b > y'
  507. end
  508. def test_combinator_unification_plus_space
  509. assert_extends '.a.b + x', '.a y {@extend x}', '.a.b + x, .a .a.b + y'
  510. assert_extends '.a + x', '.a.b y {@extend x}', '.a + x, .a.b .a + y'
  511. assert_extends '.a + x', '.b y {@extend x}', '.a + x, .b .a + y'
  512. assert_extends '.a.b x', '.a + y {@extend x}', '.a.b x, .a.b .a + y'
  513. assert_extends '.a x', '.a.b + y {@extend x}', '.a x, .a .a.b + y'
  514. assert_extends '.a x', '.b + y {@extend x}', '.a x, .a .b + y'
  515. end
  516. def test_combinator_unification_nested
  517. assert_extends '.a > .b + x', '.c > .d + y {@extend x}', '.a > .b + x, .c.a > .d.b + y'
  518. assert_extends '.a > .b + x', '.c > y {@extend x}', '.a > .b + x, .c.a > .b + y'
  519. end
  520. def test_combinator_unification_with_newlines
  521. assert_equal <<CSS, render(<<SCSS)
  522. .a >
  523. .b
  524. + x, .c.a > .d.b + y {
  525. a: b; }
  526. CSS
  527. .a >
  528. .b
  529. + x {a: b}
  530. .c
  531. > .d +
  532. y {@extend x}
  533. SCSS
  534. end
  535. # Loops
  536. def test_extend_self_loop
  537. assert_equal <<CSS, render(<<SCSS)
  538. .foo {
  539. a: b; }
  540. CSS
  541. .foo {a: b; @extend .foo}
  542. SCSS
  543. end
  544. def test_basic_extend_loop
  545. assert_equal <<CSS, render(<<SCSS)
  546. .bar, .foo {
  547. a: b; }
  548. .foo, .bar {
  549. c: d; }
  550. CSS
  551. .foo {a: b; @extend .bar}
  552. .bar {c: d; @extend .foo}
  553. SCSS
  554. end
  555. def test_three_level_extend_loop
  556. assert_equal <<CSS, render(<<SCSS)
  557. .baz, .bar, .foo {
  558. a: b; }
  559. .foo, .baz, .bar {
  560. c: d; }
  561. .bar, .foo, .baz {
  562. e: f; }
  563. CSS
  564. .foo {a: b; @extend .bar}
  565. .bar {c: d; @extend .baz}
  566. .baz {e: f; @extend .foo}
  567. SCSS
  568. end
  569. def test_nested_extend_loop
  570. assert_equal <<CSS, render(<<SCSS)
  571. .bar, .bar .foo {
  572. a: b; }
  573. .bar .foo {
  574. c: d; }
  575. CSS
  576. .bar {
  577. a: b;
  578. .foo {c: d; @extend .bar}
  579. }
  580. SCSS
  581. end
  582. def test_multiple_extender_merges_with_superset_selector
  583. assert_equal <<CSS, render(<<SCSS)
  584. a.bar.baz, a.foo {
  585. a: b; }
  586. CSS
  587. .foo {@extend .bar; @extend .baz}
  588. a.bar.baz {a: b}
  589. SCSS
  590. end
  591. def test_control_flow_if
  592. assert_equal <<CSS, render(<<SCSS)
  593. .true, .also-true {
  594. color: green; }
  595. .false, .also-false {
  596. color: red; }
  597. CSS
  598. .true { color: green; }
  599. .false { color: red; }
  600. .also-true {
  601. @if true { @extend .true; }
  602. @else { @extend .false; }
  603. }
  604. .also-false {
  605. @if false { @extend .true; }
  606. @else { @extend .false; }
  607. }
  608. SCSS
  609. end
  610. def test_control_flow_for
  611. assert_equal <<CSS, render(<<SCSS)
  612. .base-0, .added {
  613. color: green; }
  614. .base-1, .added {
  615. display: block; }
  616. .base-2, .added {
  617. border: 1px solid blue; }
  618. CSS
  619. .base-0 { color: green; }
  620. .base-1 { display: block; }
  621. .base-2 { border: 1px solid blue; }
  622. .added {
  623. @for $i from 0 to 3 {
  624. @extend .base-\#{$i};
  625. }
  626. }
  627. SCSS
  628. end
  629. def test_control_flow_while
  630. assert_equal <<CSS, render(<<SCSS)
  631. .base-0, .added {
  632. color: green; }
  633. .base-1, .added {
  634. display: block; }
  635. .base-2, .added {
  636. border: 1px solid blue; }
  637. CSS
  638. .base-0 { color: green; }
  639. .base-1 { display: block; }
  640. .base-2 { border: 1px solid blue; }
  641. .added {
  642. $i : 0;
  643. @while $i < 3 {
  644. @extend .base-\#{$i};
  645. $i : $i + 1;
  646. }
  647. }
  648. SCSS
  649. end
  650. def test_basic_placeholder_selector
  651. assert_extends '%foo', '.bar {@extend %foo}', '.bar'
  652. end
  653. def test_unused_placeholder_selector
  654. assert_equal <<CSS, render(<<SCSS)
  655. .baz {
  656. color: blue; }
  657. CSS
  658. %foo {color: blue}
  659. %bar {color: red}
  660. .baz {@extend %foo}
  661. SCSS
  662. end
  663. def test_placeholder_descendant_selector
  664. assert_extends '#context %foo a', '.bar {@extend %foo}', '#context .bar a'
  665. end
  666. def test_semi_placeholder_selector
  667. assert_equal <<CSS, render(<<SCSS)
  668. .bar .baz {
  669. color: blue; }
  670. CSS
  671. #context %foo, .bar .baz {color: blue}
  672. SCSS
  673. end
  674. def test_placeholder_selector_with_multiple_extenders
  675. assert_equal <<CSS, render(<<SCSS)
  676. .bar, .baz {
  677. color: blue; }
  678. CSS
  679. %foo {color: blue}
  680. .bar {@extend %foo}
  681. .baz {@extend %foo}
  682. SCSS
  683. end
  684. def test_placeholder_selector_as_modifier
  685. assert_extend_doesnt_match('div', '%foo', :failed_to_unify, 3) do
  686. assert_equal <<CSS, render(<<SCSS)
  687. a.baz.bar {
  688. color: blue; }
  689. CSS
  690. a%foo.baz {color: blue}
  691. .bar {@extend %foo}
  692. div {@extend %foo}
  693. SCSS
  694. end
  695. end
  696. def test_placeholder_interpolation
  697. assert_equal <<CSS, render(<<SCSS)
  698. .bar {
  699. color: blue; }
  700. CSS
  701. $foo: foo;
  702. %\#{$foo} {color: blue}
  703. .bar {@extend %foo}
  704. SCSS
  705. end
  706. def test_media_in_placeholder_selector
  707. assert_equal <<CSS, render(<<SCSS)
  708. .baz {
  709. c: d; }
  710. CSS
  711. %foo {bar {@media screen {a: b}}}
  712. .baz {c: d}
  713. SCSS
  714. end
  715. def test_extend_out_of_media
  716. assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SCSS))}
  717. DEPRECATION WARNING on line 3 of test_extend_out_of_media_inline.scss:
  718. @extending an outer selector from within @media is deprecated.
  719. You may only @extend selectors within the same directive.
  720. This will be an error in Sass 3.3.
  721. It can only work once @extend is supported natively in the browser.
  722. WARN
  723. .foo {
  724. a: b; }
  725. CSS
  726. .foo {a: b}
  727. @media screen {
  728. .bar {@extend .foo}
  729. }
  730. SCSS
  731. end
  732. def test_extend_out_of_unknown_directive
  733. assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SCSS))}
  734. DEPRECATION WARNING on line 3 of test_extend_out_of_unknown_directive_inline.scss:
  735. @extending an outer selector from within @flooblehoof is deprecated.
  736. You may only @extend selectors within the same directive.
  737. This will be an error in Sass 3.3.
  738. It can only work once @extend is supported natively in the browser.
  739. WARN
  740. .foo {
  741. a: b; }
  742. @flooblehoof {}
  743. CSS
  744. .foo {a: b}
  745. @flooblehoof {
  746. .bar {@extend .foo}
  747. }
  748. SCSS
  749. end
  750. def test_extend_out_of_nested_directives
  751. assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SCSS))}
  752. DEPRECATION WARNING on line 4 of test_extend_out_of_nested_directives_inline.scss:
  753. @extending an outer selector from within @flooblehoof is deprecated.
  754. You may only @extend selectors within the same directive.
  755. This will be an error in Sass 3.3.
  756. It can only work once @extend is supported natively in the browser.
  757. WARN
  758. @media screen {
  759. .foo {
  760. a: b; }
  761. @flooblehoof {} }
  762. CSS
  763. @media screen {
  764. .foo {a: b}
  765. @flooblehoof {
  766. .bar {@extend .foo}
  767. }
  768. }
  769. SCSS
  770. end
  771. def test_extend_within_media
  772. assert_equal(<<CSS, render(<<SCSS))
  773. @media screen {
  774. .foo, .bar {
  775. a: b; } }
  776. CSS
  777. @media screen {
  778. .foo {a: b}
  779. .bar {@extend .foo}
  780. }
  781. SCSS
  782. end
  783. def test_extend_within_unknown_directive
  784. assert_equal(<<CSS, render(<<SCSS))
  785. @flooblehoof {
  786. .foo, .bar {
  787. a: b; } }
  788. CSS
  789. @flooblehoof {
  790. .foo {a: b}
  791. .bar {@extend .foo}
  792. }
  793. SCSS
  794. end
  795. def test_extend_within_nested_directives
  796. assert_equal(<<CSS, render(<<SCSS))
  797. @media screen {
  798. @flooblehoof {
  799. .foo, .bar {
  800. a: b; } } }
  801. CSS
  802. @media screen {
  803. @flooblehoof {
  804. .foo {a: b}
  805. .bar {@extend .foo}
  806. }
  807. }
  808. SCSS
  809. end
  810. def test_extend_within_disparate_media
  811. assert_equal(<<CSS, render(<<SCSS))
  812. @media screen {
  813. .foo, .bar {
  814. a: b; } }
  815. CSS
  816. @media screen {.foo {a: b}}
  817. @media screen {.bar {@extend .foo}}
  818. SCSS
  819. end
  820. def test_extend_within_disparate_unknown_directive
  821. assert_equal(<<CSS, render(<<SCSS))
  822. @flooblehoof {
  823. .foo, .bar {
  824. a: b; } }
  825. @flooblehoof {}
  826. CSS
  827. @flooblehoof {.foo {a: b}}
  828. @flooblehoof {.bar {@extend .foo}}
  829. SCSS
  830. end
  831. def test_extend_within_disparate_nested_directives
  832. assert_equal(<<CSS, render(<<SCSS))
  833. @media screen {
  834. @flooblehoof {
  835. .foo, .bar {
  836. a: b; } } }
  837. @media screen {
  838. @flooblehoof {} }
  839. CSS
  840. @media screen {@flooblehoof {.foo {a: b}}}
  841. @media screen {@flooblehoof {.bar {@extend .foo}}}
  842. SCSS
  843. end
  844. def test_extend_within_and_without_media
  845. assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SCSS))}
  846. DEPRECATION WARNING on line 4 of test_extend_within_and_without_media_inline.scss:
  847. @extending an outer selector from within @media is deprecated.
  848. You may only @extend selectors within the same directive.
  849. This will be an error in Sass 3.3.
  850. It can only work once @extend is supported natively in the browser.
  851. WARN
  852. .foo {
  853. a: b; }
  854. @media screen {
  855. .foo, .bar {
  856. c: d; } }
  857. CSS
  858. .foo {a: b}
  859. @media screen {
  860. .foo {c: d}
  861. .bar {@extend .foo}
  862. }
  863. SCSS
  864. end
  865. def test_extend_within_and_without_unknown_directive
  866. assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SCSS))}
  867. DEPRECATION WARNING on line 4 of test_extend_within_and_without_unknown_directive_inline.scss:
  868. @extending an outer selector from within @flooblehoof is deprecated.
  869. You may only @extend selectors within the same directive.
  870. This will be an error in Sass 3.3.
  871. It can only work once @extend is supported natively in the browser.
  872. WARN
  873. .foo {
  874. a: b; }
  875. @flooblehoof {
  876. .foo, .bar {
  877. c: d; } }
  878. CSS
  879. .foo {a: b}
  880. @flooblehoof {
  881. .foo {c: d}
  882. .bar {@extend .foo}
  883. }
  884. SCSS
  885. end
  886. def test_extend_within_and_without_nested_directives
  887. assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SCSS))}
  888. DEPRECATION WARNING on line 5 of test_extend_within_and_without_nested_directives_inline.scss:
  889. @extending an outer selector from within @flooblehoof is deprecated.
  890. You may only @extend selectors within the same directive.
  891. This will be an error in Sass 3.3.
  892. It can only work once @extend is supported natively in the browser.
  893. WARN
  894. @media screen {
  895. .foo {
  896. a: b; }
  897. @flooblehoof {
  898. .foo, .bar {
  899. c: d; } } }
  900. CSS
  901. @media screen {
  902. .foo {a: b}
  903. @flooblehoof {
  904. .foo {c: d}
  905. .bar {@extend .foo}
  906. }
  907. }
  908. SCSS
  909. end
  910. def test_extend_with_subject_transfers_subject_to_extender
  911. assert_equal(<<CSS, render(<<SCSS))
  912. foo bar! baz, foo .bip .bap! baz, .bip foo .bap! baz {
  913. a: b; }
  914. CSS
  915. foo bar! baz {a: b}
  916. .bip .bap {@extend bar}
  917. SCSS
  918. assert_equal(<<CSS, render(<<SCSS))
  919. foo.x bar.y! baz.z, foo.x .bip bar.bap! baz.z, .bip foo.x bar.bap! baz.z {
  920. a: b; }
  921. CSS
  922. foo.x bar.y! baz.z {a: b}
  923. .bip .bap {@extend .y}
  924. SCSS
  925. end
  926. def test_extend_with_subject_retains_subject_on_target
  927. assert_equal(<<CSS, render(<<SCSS))
  928. .foo! .bar, .foo! .bip .bap, .bip .foo! .bap {
  929. a: b; }
  930. CSS
  931. .foo! .bar {a: b}
  932. .bip .bap {@extend .bar}
  933. SCSS
  934. end
  935. def test_extend_with_subject_transfers_subject_to_target
  936. assert_equal(<<CSS, render(<<SCSS))
  937. a.foo .bar, .bip a.bap! .bar {
  938. a: b; }
  939. CSS
  940. a.foo .bar {a: b}
  941. .bip .bap! {@extend .foo}
  942. SCSS
  943. end
  944. def test_extend_with_subject_retains_subject_on_extender
  945. assert_equal(<<CSS, render(<<SCSS))
  946. .foo .bar, .foo .bip! .bap, .bip! .foo .bap {
  947. a: b; }
  948. CSS
  949. .foo .bar {a: b}
  950. .bip! .bap {@extend .bar}
  951. SCSS
  952. end
  953. def test_extend_with_subject_fails_with_conflicting_subject
  954. assert_equal(<<CSS, render(<<SCSS))
  955. x! .bar {
  956. a: b; }
  957. CSS
  958. x! .bar {a: b}
  959. y! .bap {@extend .bar}
  960. SCSS
  961. end
  962. def test_extend_warns_when_extendee_doesnt_exist
  963. assert_warning(<<WARN) {assert_equal("", render(<<SCSS))}
  964. WARNING on line 1 of test_extend_warns_when_extendee_doesnt_exist_inline.scss: ".foo" failed to @extend ".bar".
  965. The selector ".bar" was not found.
  966. This will be an error in future releases of Sass.
  967. Use "@extend .bar !optional" if the extend should be able to fail.
  968. WARN
  969. .foo {@extend .bar}
  970. SCSS
  971. end
  972. def test_extend_warns_when_extension_fails
  973. assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SCSS))}
  974. WARNING on line 2 of test_extend_warns_when_extension_fails_inline.scss: "b.foo" failed to @extend ".bar".
  975. No selectors matching ".bar" could be unified with "b.foo".
  976. This will be an error in future releases of Sass.
  977. Use "@extend .bar !optional" if the extend should be able to fail.
  978. WARN
  979. a.bar {
  980. a: b; }
  981. CSS
  982. a.bar {a: b}
  983. b.foo {@extend .bar}
  984. SCSS
  985. end
  986. def test_extend_does_not_warn_when_one_extension_fails_but_others_dont
  987. assert_no_warning {assert_equal(<<CSS, render(<<SCSS))}
  988. a.bar {
  989. a: b; }
  990. .bar, b.foo {
  991. c: d; }
  992. CSS
  993. a.bar {a: b}
  994. .bar {c: d}
  995. b.foo {@extend .bar}
  996. SCSS
  997. end
  998. def test_extend_does_not_warn_when_one_extension_fails_but_others_dont
  999. assert_no_warning {assert_equal(<<CSS, render(<<SCSS))}
  1000. a.bar {
  1001. a: b; }
  1002. .bar, b.foo {
  1003. c: d; }
  1004. CSS
  1005. a.bar {a: b}
  1006. .bar {c: d}
  1007. b.foo {@extend .bar}
  1008. SCSS
  1009. end
  1010. def test_optional_extend_does_not_warn_when_extendee_doesnt_exist
  1011. assert_no_warning {assert_equal("", render(<<SCSS))}
  1012. .foo {@extend .bar !optional}
  1013. SCSS
  1014. end
  1015. def test_optional_extend_does_not_warn_when_extension_fails
  1016. assert_no_warning {assert_equal(<<CSS, render(<<SCSS))}
  1017. a.bar {
  1018. a: b; }
  1019. CSS
  1020. a.bar {a: b}
  1021. b.foo {@extend .bar !optional}
  1022. SCSS
  1023. end
  1024. # Regression Tests
  1025. def test_nested_extend_specificity
  1026. assert_equal <<CSS, render(<<SCSS)
  1027. a :b, a :b:c {
  1028. a: b; }
  1029. CSS
  1030. %foo {a: b}
  1031. a {
  1032. :b {@extend %foo}
  1033. :b:c {@extend %foo}
  1034. }
  1035. SCSS
  1036. end
  1037. def test_nested_double_extend_optimization
  1038. assert_equal <<CSS, render(<<SCSS)
  1039. .parent1 .child {
  1040. a: b; }
  1041. CSS
  1042. %foo %bar {
  1043. a: b;
  1044. }
  1045. .parent1 {
  1046. @extend %foo;
  1047. .child {
  1048. @extend %bar;
  1049. }
  1050. }
  1051. .parent2 {
  1052. @extend %foo;
  1053. }
  1054. SCSS
  1055. end
  1056. def test_extend_in_double_nested_media_query
  1057. assert_equal <<CSS, render(<<SCSS)
  1058. @media all and (orientation: landscape) {
  1059. .bar {
  1060. color: blue; } }
  1061. CSS
  1062. @media all {
  1063. @media (orientation: landscape) {
  1064. %foo {color: blue}
  1065. .bar {@extend %foo}
  1066. }
  1067. }
  1068. SCSS
  1069. end
  1070. def test_partially_failed_extend
  1071. assert_no_warning {assert_equal(<<CSS, render(<<SCSS))}
  1072. .rc, test {
  1073. color: white; }
  1074. .prices span.pill span.rc {
  1075. color: red; }
  1076. CSS
  1077. test { @extend .rc; }
  1078. .rc {color: white;}
  1079. .prices span.pill span.rc {color: red;}
  1080. SCSS
  1081. end
  1082. def test_newline_near_combinator
  1083. assert_equal <<CSS, render(<<SCSS)
  1084. .a +
  1085. .b x, .a +
  1086. .b .c y, .c .a +
  1087. .b y {
  1088. a: b; }
  1089. CSS
  1090. .a +
  1091. .b x {a: b}
  1092. .c y {@extend x}
  1093. SCSS
  1094. end
  1095. def test_duplicated_selector_with_newlines
  1096. assert_equal(<<CSS, render(<<SCSS))
  1097. .example-1-1,
  1098. .example-1-2,
  1099. .my-page-1 .my-module-1-1,
  1100. .example-1-3 {
  1101. a: b; }
  1102. CSS
  1103. .example-1-1,
  1104. .example-1-2,
  1105. .example-1-3 {
  1106. a: b;
  1107. }
  1108. .my-page-1 .my-module-1-1 {@extend .example-1-2}
  1109. SCSS
  1110. end
  1111. def test_nested_selector_with_child_selector_hack_extendee
  1112. assert_extends '> .foo', 'foo bar {@extend .foo}', '> .foo, > foo bar'
  1113. end
  1114. def test_nested_selector_with_child_selector_hack_extender
  1115. assert_extends '.foo .bar', '> foo bar {@extend .bar}', '.foo .bar, > .foo foo bar, > foo .foo bar'
  1116. end
  1117. def test_nested_selector_with_child_selector_hack_extender_and_extendee
  1118. assert_extends '> .foo', '> foo bar {@extend .foo}', '> .foo, > foo bar'
  1119. end
  1120. def test_nested_selector_with_child_selector_hack_extender_and_sibling_selector_extendee
  1121. assert_extends '~ .foo', '> foo bar {@extend .foo}', '~ .foo'
  1122. end
  1123. def test_nested_selector_with_child_selector_hack_extender_and_extendee_and_newline
  1124. assert_equal <<CSS, render(<<SCSS)
  1125. > .foo, > flip,
  1126. > foo bar {
  1127. a: b; }
  1128. CSS
  1129. > .foo {a: b}
  1130. flip,
  1131. > foo bar {@extend .foo}
  1132. SCSS
  1133. end
  1134. def test_extended_parent_and_child_redundancy_elimination
  1135. assert_equal <<CSS, render(<<SCSS)
  1136. a b, d b, a c, d c {
  1137. a: b; }
  1138. CSS
  1139. a {
  1140. b {a: b}
  1141. c {@extend b}
  1142. }
  1143. d {@extend a}
  1144. SCSS
  1145. end
  1146. def test_extend_redundancy_elimination_when_it_would_reduce_specificity
  1147. assert_extends 'a', 'a.foo {@extend a}', 'a, a.foo'
  1148. end
  1149. def test_extend_redundancy_elimination_when_it_would_preserve_specificity
  1150. assert_extends '.bar a', 'a.foo {@extend a}', '.bar a'
  1151. end
  1152. def test_extend_redundancy_elimination_never_eliminates_base_selector
  1153. assert_extends 'a.foo', '.foo {@extend a}', 'a.foo, .foo'
  1154. end
  1155. def test_extend_cross_branch_redundancy_elimination
  1156. assert_equal <<CSS, render(<<SCSS)
  1157. a c d, b c a d {
  1158. a: b; }
  1159. CSS
  1160. %x c %y {a: b}
  1161. a, b {@extend %x}
  1162. a d {@extend %y}
  1163. SCSS
  1164. assert_equal <<CSS, render(<<SCSS)
  1165. e a c d, a c e d, e b c a d, b c a e d {
  1166. a: b; }
  1167. CSS
  1168. e %z {a: b}
  1169. %x c %y {@extend %z}
  1170. a, b {@extend %x}
  1171. a d {@extend %y}
  1172. SCSS
  1173. end
  1174. private
  1175. def assert_extend_doesnt_match(extender, target, reason, line, syntax = :scss)
  1176. warn = "\"#{extender}\" failed to @extend \"#{target}\"."
  1177. reason =
  1178. if reason == :not_found
  1179. "The selector \"#{target}\" was not found."
  1180. else
  1181. "No selectors matching \"#{target}\" could be unified with \"#{extender}\"."
  1182. end
  1183. assert_warning(<<WARNING) {yield}
  1184. WARNING on line #{line} of #{filename_for_test syntax}: #{warn}
  1185. #{reason}
  1186. This will be an error in future releases of Sass.
  1187. Use "@extend #{target} !optional" if the extend should be able to fail.
  1188. WARNING
  1189. end
  1190. def assert_unification(selector, extension, unified)
  1191. # Do some trickery so the first law of extend doesn't get in our way.
  1192. assert_extends(
  1193. "%-a #{selector}",
  1194. extension + " -a {@extend %-a}",
  1195. unified.split(', ').map {|s| "-a #{s}"}.join(', '))
  1196. end
  1197. def assert_extends(selector, extension, result)
  1198. assert_equal <<CSS, render(<<SCSS)
  1199. #{result} {
  1200. a: b; }
  1201. CSS
  1202. #{selector} {a: b}
  1203. #{extension}
  1204. SCSS
  1205. end
  1206. def render(sass, options = {})
  1207. options = {:syntax => :scss}.merge(options)
  1208. munge_filename options
  1209. Sass::Engine.new(sass, options).render
  1210. end
  1211. end