#!/usr/bin/env ruby # -*- coding: utf-8 -*- require File.dirname(__FILE__) + '/test_helper' require 'sass/scss/css_parser' # These tests just test the parsing of CSS # (both standard and any hacks we intend to support). # Tests of SCSS-specific behavior go in scss_test.rb. class ScssCssTest < Test::Unit::TestCase include ScssTestHelper def test_basic_scss assert_parses < baz {bar: baz} SCSS end if Sass::Util.ruby1_8? def test_unicode assert_parses < F') assert_selector_parses('E + F') assert_selector_parses('E ~ F') assert_selector_parses('E /foo/ F') assert_selector_parses('E! > F') assert_selector_parses('E /ns|foo/ F') assert_selector_parses('E /*|foo/ F') end # Taken from http://dev.w3.org/csswg/selectors4/#overview, but without element # names. def test_summarized_selectors assert_selector_parses(':not(s)') assert_selector_parses(':not(s1, s2)') assert_selector_parses(':matches(s1, s2)') assert_selector_parses('.warning') assert_selector_parses('#myid') assert_selector_parses('[foo]') assert_selector_parses('[foo="bar"]') assert_selector_parses('[foo="bar" i]') assert_selector_parses('[foo~="bar"]') assert_selector_parses('[foo^="bar"]') assert_selector_parses('[foo$="bar"]') assert_selector_parses('[foo*="bar"]') assert_selector_parses('[foo|="en"]') assert_selector_parses(':dir(ltr)') assert_selector_parses(':lang(fr)') assert_selector_parses(':lang(zh, *-hant)') assert_selector_parses(':any-link') assert_selector_parses(':link') assert_selector_parses(':visited') assert_selector_parses(':local-link') assert_selector_parses(':local-link(0)') assert_selector_parses(':target') assert_selector_parses(':scope') assert_selector_parses(':current') assert_selector_parses(':current(s)') assert_selector_parses(':past') assert_selector_parses(':future') assert_selector_parses(':active') assert_selector_parses(':hover') assert_selector_parses(':focus') assert_selector_parses(':enabled') assert_selector_parses(':disabled') assert_selector_parses(':checked') assert_selector_parses(':indeterminate') assert_selector_parses(':default') assert_selector_parses(':in-range') assert_selector_parses(':out-of-range') assert_selector_parses(':required') assert_selector_parses(':optional') assert_selector_parses(':read-only') assert_selector_parses(':read-write') assert_selector_parses(':root') assert_selector_parses(':empty') assert_selector_parses(':first-child') assert_selector_parses(':nth-child(n)') assert_selector_parses(':last-child') assert_selector_parses(':nth-last-child(n)') assert_selector_parses(':only-child') assert_selector_parses(':first-of-type') assert_selector_parses(':nth-of-type(n)') assert_selector_parses(':last-of-type') assert_selector_parses(':nth-last-of-type(n)') assert_selector_parses(':only-of-type') assert_selector_parses(':nth-match(n of selector)') assert_selector_parses(':nth-last-match(n of selector)') assert_selector_parses(':column(selector)') assert_selector_parses(':nth-column(n)') assert_selector_parses(':nth-last-column(n)') end def test_attribute_selectors_with_identifiers assert_selector_parses('[foo~=bar]') assert_selector_parses('[foo^=bar]') assert_selector_parses('[foo$=bar]') assert_selector_parses('[foo*=bar]') assert_selector_parses('[foo|=en]') end def test_nth_selectors assert_selector_parses(':nth-child(-n)') assert_selector_parses(':nth-child(+n)') assert_selector_parses(':nth-child(even)') assert_selector_parses(':nth-child(odd)') assert_selector_parses(':nth-child(50)') assert_selector_parses(':nth-child(-50)') assert_selector_parses(':nth-child(+50)') assert_selector_parses(':nth-child(2n+3)') assert_selector_parses(':nth-child(2n-3)') assert_selector_parses(':nth-child(+2n-3)') assert_selector_parses(':nth-child(-2n+3)') assert_selector_parses(':nth-child(-2n+ 3)') assert_equal(<)') assert_selector_can_contain_selectors(':current()') assert_selector_can_contain_selectors(':nth-match(n of )') assert_selector_can_contain_selectors(':nth-last-match(n of )') assert_selector_can_contain_selectors(':column()') assert_selector_can_contain_selectors(':-moz-any()') end def assert_selector_can_contain_selectors(sel) try = lambda {|subsel| assert_selector_parses(sel.gsub('', subsel))} try['foo|bar'] try['*|bar'] try['foo|*'] try['*|*'] try['#blah'] try['.blah'] try['[foo]'] try['[foo^="bar"]'] try['[baz|foo~="bar"]'] try[':hover'] try[':nth-child(2n + 3)'] try['h1, h2, h3'] try['#foo, bar, [baz]'] # Not technically allowed for most selectors, but what the heck try[':not(#foo)'] try['a#foo.bar'] try['#foo .bar > baz'] end def test_namespaced_selectors assert_selector_parses('foo|E') assert_selector_parses('*|E') assert_selector_parses('foo|*') assert_selector_parses('*|*') end def test_namespaced_attribute_selectors assert_selector_parses('[foo|bar=baz]') assert_selector_parses('[*|bar=baz]') assert_selector_parses('[foo|bar|=baz]') end def test_comma_selectors assert_selector_parses('E, F') assert_selector_parses('E F, G H') assert_selector_parses('E > F, G > H') end def test_selectors_with_newlines assert_selector_parses("E,\nF") assert_selector_parses("E\nF") assert_selector_parses("E, F\nG, H") end def test_expression_fallback_selectors assert_selector_parses('0%') assert_selector_parses('60%') assert_selector_parses('100%') assert_selector_parses('12px') assert_selector_parses('"foo"') end def test_functional_pseudo_selectors assert_selector_parses(':foo("bar")') assert_selector_parses(':foo(bar)') assert_selector_parses(':foo(12px)') assert_selector_parses(':foo(+)') assert_selector_parses(':foo(-)') assert_selector_parses(':foo(+"bar")') assert_selector_parses(':foo(-++--baz-"bar"12px)') end def test_selector_hacks assert_selector_parses('> E') assert_selector_parses('+ E') assert_selector_parses('~ E') assert_selector_parses('> > E') assert_equal < > E { a: b; } CSS >> E { a: b; } SCSS assert_selector_parses('E*') assert_selector_parses('E*.foo') assert_selector_parses('E*:hover') end def test_spaceless_combo_selectors assert_equal "E > F {\n a: b; }\n", render("E>F { a: b;} ") assert_equal "E ~ F {\n a: b; }\n", render("E~F { a: b;} ") assert_equal "E + F {\n a: b; }\n", render("E+F { a: b;} ") end ## Errors def test_invalid_directives assert_not_parses("identifier", '@ import "foo";') assert_not_parses("identifier", '@12 "foo";') end def test_invalid_classes assert_not_parses("class name", 'p. foo {a: b}') assert_not_parses("class name", 'p.1foo {a: b}') end def test_invalid_ids assert_not_parses("id name", 'p# foo {a: b}') end def test_no_properties_at_toplevel assert_not_parses('pseudoclass or pseudoelement', 'a: b;') end def test_no_scss_directives assert_parses('@import "foo.sass";') assert_parses <$var = 12;") assert_not_parses('"}"', "foo { !var = 12; }") end def test_no_parent_selectors assert_not_parses('"{"', "foo &.bar {a: b}") end def test_no_selector_interpolation assert_not_parses('"{"', 'foo #{"bar"}.baz {a: b}') end def test_no_prop_name_interpolation assert_not_parses('":"', 'foo {a#{"bar"}baz: b}') end def test_no_prop_val_interpolation assert_not_parses('"}"', 'foo {a: b #{"bar"} c}') end def test_no_string_interpolation assert_parses <* c}') end def test_no_nested_rules assert_not_parses('":"', 'foo {bar {a: b}}') assert_not_parses('"}"', 'foo {[bar=baz] {a: b}}') end def test_no_nested_properties assert_not_parses('expression (e.g. 1px, bold)', 'foo {bar: {a: b}}') assert_not_parses('expression (e.g. 1px, bold)', 'foo {bar: bang {a: b}}') end def test_no_nested_directives assert_not_parses('"}"', 'foo {@bar {a: b}}') end def test_error_with_windows_newlines render < e assert_equal 'Invalid CSS after "foo {bar": expected ":", was "}"', e.message assert_equal 1, e.sass_line end ## Regressions def test_selector_without_closing_bracket assert_not_parses('"]"', "foo[bar {a: b}") end def test_closing_line_comment_end_with_compact_output assert_equal(< :compact)) /* foo */ bar { baz: bang; } CSS /* * foo */ bar {baz: bang} SCSS end def test_single_line_comment_within_multiline_comment assert_equal(< e assert_equal 'Invalid CSS after "@media ": expected media query (e.g. print, screen, print and screen), was "{"', e.message assert_equal 1, e.sass_line end private def assert_selector_parses(selector) assert_parses <