/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.netbeans.modules.css.lib;

import javax.swing.text.BadLocationException;
import org.netbeans.modules.css.lib.api.*;
import org.netbeans.modules.parsing.spi.ParseException;

/**
 *
 * @author marekfukala
 */
public class Css3ParserScssTest extends CssTestBase {

    public Css3ParserScssTest(String testName) {
        super(testName);
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        setScssSource();
    }

    @Override
    protected void tearDown() throws Exception {
        setPlainSource();
    }

    public void testAllANTLRRulesHaveNodeTypes() {
        for (String rule : Css3Parser.ruleNames) {
            if (!rule.startsWith("synpred") && !rule.toLowerCase().endsWith("predicate")) {
                assertNotNull(NodeType.valueOf(rule));
            }
        }
    }

    public void testDisabledScssSupport() {
        try {
            ExtCss3Parser.isScssSource_unit_tests = false;
            String source = "$color: #4D926F;\n"
                    + "\n"
                    + "#header {\n"
                    + "  color: $color;\n"
                    + "}\n"
                    + "h2 {\n"
                    + "  color: $color;\n"
                    + "}";

            CssParserResult result = TestUtil.parse(source);

            //there must be some css parsing errors as the less support is disabled
            assertTrue(result.getDiagnostics().size() > 0);
        } finally {
            ExtCss3Parser.isScssSource_unit_tests = true;
        }
    }

    public void testVariable() {
        String source = "$color: #4D926F;\n"
                + "\n"
                + "#header {\n"
                + "  color: $color;\n"
                + "}\n"
                + "h2 {\n"
                + "  color: $color;\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);
    }

    public void testVariable2() {
        String source = "#header {\n"
                + "  border: 2px $color solid;\n"
                + "}\n";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);
    }

    public void testVariableAsPropertyName() {
        String source = ".class {\n"
                + "    $var: 2;\n"
                + "    three: $var;\n"
                + "    $var: 3;\n"
                + "  }";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);
    }

    public void testFunction() {
        String source
                = "#header {\n"
                + "  color: ($base-color * 3);\n"
                + "  border-left: $the-border;\n"
                + "  border-right: ($the-border * 2);\n"
                + "}\n";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);
    }

    public void testFunction2() {
        String source
                = "#footer {\n"
                + "  border-color: desaturate($red, 10%);\n"
                + "  color: ($base-color + #003300);\n"
                + "}";
        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);
    }

    public void testMixinDeclaration() {
        String source
                = "@mixin rounded-corners ($radius: 5px) {\n"
                + "  -webkit-border-radius: $radius;\n"
                + "  -moz-border-radius: $radius;\n"
                + "  -ms-border-radius: $radius;\n"
                + "  -o-border-radius: $radius;\n"
                + "  border-radius: $radius;\n"
                + "}";
        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);
    }

    public void testMixinDeclaration2() {
        String source
                = "@mixin box-shadow ($x: 0, $y: 0, $blur: 1px, $color: #000) {\n"
                + "  box-shadow: $arguments;\n"
                + "  -moz-box-shadow: $arguments;\n"
                + "  -webkit-box-shadow: $arguments;\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);
    }

    public void testMixinDeclarationWithoutParams() {
        String source
                = "@mixin box-shadow {\n"
                + "  box-shadow: $arguments;\n"
                + "  -moz-box-shadow: $arguments;\n"
                + "  -webkit-box-shadow: $arguments;\n"
                + "}";
        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);
    }
//
//    public void testMixinDeclarationAdvancedArguments() {
//        String source =
//                ".mixin1 (...) {}"
//                + ".mixin2 () {}"
//                + ".mixin3 (@a: 1) {}"
//                + ".mixin4 (@a: 1, ...) {}"
//                + ".mixin5 (@a, ...) {}";
//        ;
//
//    public void testGuardedMixins() {
//        String source =
//                ".mixin (@a) when (@a > 10), (@a = -10) {\n"
//                + "  background-color: black;\n"
//                + "}";
//        ;
//
//        CssParserResult result = TestUtil.parse(source);
//
////        NodeUtil.dumpTree(result.getParseTree());
//        assertResultOK(result);
//    }
//
//    public void testGuardedMixins2() {
//        String source =
//                ".truth (@a) when (@a) { }\n"
//                + ".truth (@a) when (@a = true) { }\n"
//                + ".mixin (@a) when (@media = mobile) { } \n";
//        ;
//
//        CssParserResult result = TestUtil.parse(source);
//
////        NodeUtil.dumpTree(result.getParseTree());
//        assertResultOK(result);
//    }
//
//    public void testGuardedMixinIsFunction() {
//        String source =
//                ".mixin (@a, @b: 0) when (isnumber(@b)) { }\n";
//        ;
//
//        CssParserResult result = TestUtil.parse(source);
//
//        NodeUtil.dumpTree(result.getParseTree());
//        assertResultOK(result);
//    }
//
//    public void testGuardedMixinNotOperator() {
//        String source =
//                ".mixin (@b) when not (@b > 0) { }\n";
//        ;
//
//        CssParserResult result = TestUtil.parse(source);
//
////        NodeUtil.dumpTree(result.getParseTree());
//        assertResultOK(result);
//    }
//

    public void testMixinCall() {
        String source
                = ".class {\n"
                + "  @include mixin($switch, #888);\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);
    }

    public void testMixinCall2() {
        String source
                = "#menu a {\n"
                + "  color: #111;\n"
                + "  @include bordered;\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);
    }

    public void testFunctions() {
        String source = ".class {\n"
                + "  width: percentage(0.5);\n"
                + "  color: saturate($base, 5%);\n"
                + "  background-color: spin(lighten($base, 25%), 8);\n"
                + "}";
        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);
    }

    public void testFunctions2() {
        String source = "#navbar {\n"
                + "  $navbar-width: 800px;\n"
                + "  $items: 5;\n"
                + "  $navbar-color: #ce4dd6;\n"
                + "\n"
                + "  width: $navbar-width;\n"
                + "  border-bottom: 2px solid $navbar-color;\n"
                + "\n"
                + "  li {\n"
                + "    float: left;\n"
                + "    width: $navbar-width/$items - 10px;\n"
                + "\n"
                + "    background-color:\n"
                + "      lighten($navbar-color, 20%);\n"
                + "    &:hover {\n"
                + "      background-color:\n"
                + "        lighten($navbar-color, 10%);\n"
                + "    }\n"
                + "  }\n"
                + "}";
        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);
    }

    public void testRulesNesting() {
        String source = "#header {\n"
                + "  color: black;\n"
                + "  @mixin navigation {\n"
                + "    font-size: 12px;\n"
                + "  }\n"
                + "  font-size: 10px;\n"
                + "  @mixin navigation($a) {\n"
                + "    font-size: 12px;\n"
                + "  }\n"
                + "}";
        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testAmpCombinatorInNestedRules() {
        String source = "#header        { color: black;\n"
                + "  .navigation  { font-size: 12px; }\n"
                + "  .logo        { width: 300px;\n"
                + "    &:hover    { text-decoration: none; }\n"
                + "  }\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testAmpCombinatorInNestedRules2() {
        String source = ".shape{\n"
                + "    &:hover{ \n"
                + "        background:$lightRed;   \n"
                + "    }\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testNestedRules() {
        String source = "#header{\n"
                + "    /* #header styles */\n"
                + "    h1{\n"
                + "        /* #header h1 styles */\n"
                + "    }\n"
                + "}";
        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testOperationsInVariableDeclaration() {
        String source = "$darkBlue: $lightBlue - #555;";
        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testLessExpressionNotInParens() {
        String source = "div {"
                + "width: $pageWidth * .75;\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testMixinCallWithoutParams() {
        String source = "#shape1{ @include mymixin; }";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testPropertyValueWithParenthesis() {
        String source = "div {\n"
                + "width: ($u * $unit) - (($margin * 2) + $gpadding + $gborder);\n "
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testPropertyValue() {
        String source = "div {\n"
                + "border-top: 1px solid $color1 - #222; "
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testInterpolationInClassSelector() {
        String source
                = ".rounded-#{$vert}-#{$horz} {\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testInterpolationInIdSelector() {
        String source
                = ".navb#{$navbar}ar {\n"
                + "  $navbar-width: 800px;"
                + "}\n";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testInterpolationInPropertyName() {
        String source
                = ".rounded {\n"
                + "  border-#{$vert}-#{$horz}-radius: $radius;\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testInterpolationExpressionInSelectorWithWS() {
        String source
                = ".body.firefox #{$selector}:before {\n"
                + "    content: \"Hi, Firefox users!\";\n"
                + "  }";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testInterpolationExpressionInFunctionInTheExpression() {
        String source
                = ".body.firefox #{ie-hex-str($green)}:before {\n"
                + "    content: \"Hi, Firefox users!\";\n"
                + "  }";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testInterpolationExpressionInPropertyValue() {
        String source
                = "p {\n"
                + "  $font-size: 12px;\n"
                + "  $line-height: 30px;\n"
                + "  font: #{$font-size}/#{$line-height};\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    //fails as the
    //
    //.body.firefox #{$selector}:before
    //
    //selector is parsed as property declaration - due to the colon presence - FIXME!!!
    public void testInterpolationExpressionComplex() {
        String source
                = "@mixin firefox-message($selector) {\n"
                + "  .body.firefox #{$selector}:before {\n"
                + "    content: \"Hi, Firefox users!\";\n"
                + "  }\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testMixinsWithArgumentsComplex() {
        String source
                = "/* style.scss */\n"
                + "\n"
                + "@mixin rounded($vert, $horz, $radius: 10px) {\n"
                + "  border-#{$vert}-#{$horz}-radius: $radius;\n"
                + "  -moz-border-radius-#{$vert}#{$horz}: $radius;\n"
                + "  -webkit-border-#{$vert}-#{$horz}-radius: $radius;\n"
                + "}\n"
                + "\n"
                + "#navbar li { @include rounded(top, left); }\n"
                + "#footer { @include rounded(top, left, 5px); }\n"
                + "#sidebar { @include rounded(top, left, 8px); }";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    //like normal css import, but the ref. file doesn't need to have an extension,
    //there are also some rules regarding the naming convention, but these
    //are covered by semantic analysis, not parsing
    public void testImport() {
        String source
                = "@import \"rounded\";\n";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testNestedProperties() {
        String source
                = ".funky {\n"
                + "  font: {\n"
                + "    family: fantasy;\n"
                + "    size: 30em;\n"
                + "    weight: bold;\n"
                + "  }\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testNestedPropertiesWithValue() {
        String source
                = ".funky {\n"
                + "  font: 2px/3px {\n"
                + "    family: fantasy;\n"
                + "    size: 30em;\n"
                + "    weight: bold;\n"
                + "  }\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testLineComment() {
        String source
                = ".funky {\n"
                + " //line comment\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testMixinCallInStylesheet() {
        String source
                = "@include firefox-message(\".header\");\n";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testDefaultVariable() {
        String source
                = "$content: \"Second content?\" !default;\n";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testMultipleImport() {
        String source
                = "@import \"rounded-corners\", \"text-shadow\";\n";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testInterpolationExpressionInImport() {
        String source
                = "@import url(\"http://fonts.googleapis.com/css?family=#{$family}\");\n";
        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    //the grammar defines the imports needs to be at the very beginning of the file,
    //though this is not true in case of the preprocessor code
    public void testSASSCodeMayPrecedeImport() {
        String source = "$var: my;\n"
                + "@import url(\"#{$var}\");\n";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testNestedMediaQueries() {
        String source = ".sidebar {\n"
                + "  width: 300px;\n"
                + "  @media screen and (orientation: landscape) {\n"
                + "  .class {\n"
                + "    width: 500px;\n"
                + "  }\n"
                + "}\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testNestedMediaQueryInMediaQuery() {
        String source = "@media screen {\n"
                + "  .sidebar {\n"
                + "    @media (orientation: landscape) {\n"
                + "   //   width: 500px;\n"
                + "    }\n"
                + "  }\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    //the media query can the property declarations directly.
    public void testPropertiesDirectlyInMediaQuery() {
        String source = "@media screen and (orientation: landscape) {\n"
                + "    width: 500px;\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testInterpolationExpressionInMediaQuery() {
        String source = "@media #{$media} {\n"
                + "  .sidebar {\n"
                + "    width: 500px;\n"
                + "  }\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testInterpolationExpressionWithParenMediaQuery() {
        String source = "$media: screen;\n"
                + "$feature: -webkit-min-device-pixel-ratio;\n"
                + "$value: 1.5;\n"
                + "\n"
                + "@media #{$media} and (#{$feature}: #{$value}) {\n"
                + "  .sidebar {\n"
                + "    width: 500px;\n"
                + "  }\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testExtend() {
        String source = ".seriousError {\n"
                + "  @extend .error;\n"
                + "  border-width: 3px;\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testExtendComplex() {
        String source = ".hoverlink {\n"
                + "  @extend a:hover;\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testExtendOnlySelectors() {
        String source = "#context a%extreme {\n"
                + "  color: blue;\n"
                + "  font-weight: bold;\n"
                + "  font-size: 2em;\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testExtendOnlySelectorCall() {
        String source = ".notice {\n"
                + "  @extend %extreme;\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testDebug() {
        String source = "@debug 10em + 12em;\n"
                + ".class {\n"
                + "@debug \"hello\";\n"
                + "}\n"
                + "@mixin mymixin {\n"
                + "@debug 20;"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testWarn() {
        String source = "@warn 10em + 12em;\n"
                + ".class {\n"
                + "@warn \"hello\";\n"
                + "}\n"
                + "@mixin mymixin {\n"
                + "@warn 20;"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testIf() {
        String source = "p {\n"
                + "  @if 1 + 1 == 2 { border: 1px solid;  }\n"
                + "  @if 5 < 3      { border: 2px dotted; }\n"
                + "  @if null       { border: 3px double; }\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testFor() {
        String source = "@for $i from 1 through 3 {\n"
                + "  .item-#{$i} { width: 2em * $i; }\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testEach() {
        String source = "@each $animal in puma, sea-slug, egret, salamander {\n"
                + "  .#{$animal}-icon {\n"
                + "    background-image: url('/images/#{$animal}.png');\n"
                + "  }\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testWhile() {
        String source = "$i: 6;\n"
                + "@while $i > 0 {\n"
                + "  .item-#{$i} { width: 2em * $i; }\n"
                + "  $i: $i - 2;\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testDefineOwnFunction() {
        String source = "@function grid-width($n) {\n"
                + "  @return $n * $grid-width + ($n - 1) * $gutter-width;\n"
                + "}\n"
                + "\n"
                + "#sidebar { width: grid-width(5); }";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testMixinCallWithFunctionWithNoArgs() {
        String source = ".ease-out-expo-animation {\n"
                + "  @include transition-timing-function(ease-out-expo()); \n"
                + "  color: best-color();\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testVariableDeclarationWithCommaSeparatedValues() {
        String source = "$blueprint-font-family: Helvetica Neue, Arial, Helvetica, sans-serif;";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testAmpProblem() {
        String source
                = ".clazz {\n"
                + "    &.position#{$i} {\n"
                + "    left: ($i * -910px); \n"
                + "}\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    //commented out as the parser testing file contains a lot of unknown properties from the css point of view.
    //TODO: enable testing mode where the semantic errors are not added to the parsing diagnostics.
    //
//    public void testMergedScssTests() throws ParseException, BadLocationException, IOException {
//        CssParserResult result = TestUtil.parse(getTestFile("testfiles/scss/scss-tests-merged.scss"));
////        TestUtil.dumpResult(result);
//        assertResult(result, 0);
//    }
    public void testLocalVariableDeclaration() {
        String source
                = "p {\n"
                + "  $width: 1000px;\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

        //the "$width: 1000px;" is supposed to be parsed as variable declaration, not property declaration!
        assertNull(NodeUtil.query(result.getParseTree(), "styleSheet/body/bodyItem/rule/declarations/declaration/propertyDeclaration"));
        assertNotNull(NodeUtil.query(result.getParseTree(), "styleSheet/body/bodyItem/rule/declarations/declaration/cp_variable_declaration"));

    }

    public void testMixinCallWithWSBeforeFirstArgument() {
        String source
                = "@mixin a {\n"
                + "  @include b( linear-gradient(\n"
                + "      lighten($bg-color, 5%),\n"
                + "      darken($bg-color, 5%)\n"
                + "    )\n"
                + "  );\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testUnexpectedANDInMedia() {
        String source
                = "@media screen and ($width-name : $target-width) {\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testIf_Else() {
        String source
                = "$type: monster;\n"
                + "p {\n"
                + "  @if $type == ocean {\n"
                + "    color: blue;\n"
                + "  } @else if $type == matador {\n"
                + "    color: red;\n"
                + "  } @else if $type == monster {\n"
                + "    color: green;\n"
                + "  } @else {\n"
                + "    color: black;\n"
                + "  }\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

        //only 'if' is allowed as the ident after @else keyword
        source
                = "p {\n"
                + "  @if $type == ocean {\n"
                + "    color: blue;\n"
                + "  } @else Yf $type == matador {\n"
                + "    color: red;\n"
                + "  }\n"
                + "}";

        result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertTrue(result.getDiagnostics().size() > 0);

    }

    public void testContentDirective() {
        String source
                = "@mixin apply-to-ie6-only {\n"
                + "  * html {\n"
                + "    @content;\n"
                + "  }\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testContentDirectiveInMedia() {
        String source
                = "@mixin respond-to($media) {\n"
                + "  @if $media == handhelds {\n"
                + "    @media only screen and (max-width: $break-small) { @content; }\n"
                + "  } @else if $media == medium-screens {\n"
                + "    @media only screen and (min-width: $break-small + 1) and (max-width:\n"
                + "$break-large - 1) { @content; }\n"
                + "  }\n"
                + "  @else if $media == wide-screens {\n"
                + "    @media only screen and (min-width: $break-large) { @content; }\n"
                + "  }\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testMixinCallArgWithPropertyName() {
        String source
                = "@mixin border-radius($radius: 5px, $moz: true, $webkit: true, $ms: true) {\n"
                + "}\n"
                + "div{\n"
                + "    @include border-radius($webkit:false);\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testMixinCallArgWithValueSeparatedByWS() {
        String source
                = "#id {\n"
                + "    @include border-radius(5px, -moz -webkit);\n"
                + "}";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

    }

    public void testPropertyValueSyntacticPredicateBoundary() {
        //the scss_declaration_property_value_interpolation_expression synt. predicate
        //was terminated just by colon so it seen the interpolation expression
        //few lines below and caused bad parsing
        String source
                = "#test2 { \n"
                + "    background-color: cyan\n"
                + "}\n"
                + "#test#{$i} { }";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);
    }

    public void testMultiplicityOperatorInPropertyValueFunction() {
        String source
                = ".c {\n"
                + "    background-color: darken(orange, $i*5);\n"
                + "}\n"
                + "";

        CssParserResult result = TestUtil.parse(source);

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);
    }

    public void testIfControlExpression() {
        assertParses(" @if $arg != null and $arg2 != transparent { }");
        assertParses(" @if not $arg != null and $arg2 != transparent { }");
        assertParses(" @if not $arg != null or not $arg2 != transparent { }");
        assertParses(" @if true or not $arg2 != transparent { }");
    }

    public void testMixinWithFirstParamOnNewLine() {
        assertParses("@mixin color(\n"
                + "$bgcolor: red,"
                + "$fgcolor: blue) {\n"
                + "}");
    }

    public void testFunctionInIfStatementExpression() {
        assertParses("@function myfn($color) {\n"
                + "    @if lightness($color) > 50 {\n"
                + "        @return light;\n"
                + "    } @else {\n"
                + "        @return dark;\n"
                + "    }\n"
                + "}");
    }

    public void testImportInDeclarations() {
        assertParses(".clz { @import \"hello\"; }");
    }

    public void testCommaInSelectorInterpolationExpression() {
        assertParses(".#{$prefix}sg,.#{$prefix}ag .#{$prefix}xx { }");
    }

    public void testIfCondition() {
        assertParses(" @if ($mode == light) {}");
    }

    public void testSassFunctionWhereWithArgDefiningValue() {
        assertParses("@function color-by-background($bg-color, $contrast: $default-text-contrast) {\n"
                + "    @return color-offset($bg-color, $contrast, $tmpmode, $inverse: true);\n"
                + "}");
    }

    public void testWeirdControlBlockOperator() {
        assertParses("@if $right =< 0 {}");
        assertParses("@if $right <= 0 {}");
        assertParses("@if $right >= 0 {}");
        assertParses("@if $right => 0 {}");
    }

    public void testWSBetweenMixinCallArgAndComma() {
        assertParses(".clz {\n"
                + "     @include background-gradient(\n"
                + "         $background-color ,\n"
                + "         $background-direction\n"
                + ");\n"
                + "}");
    }

    public void testControlBlockExpression() {
        assertParses("@if $arg != null and ($arg2 == val1 or $arg2 == val2) {}");
    }

    public void testControlBlockExpression2() {
        assertParses("@if (not $arg or $arg2) and $arg3 != null {}");
    }

    public void testControlBlockExpression3() {
        assertParses("@if ($arg or $arg2) and arg3 != null {}");
    }

    public void testControlBlockExpression4() {
        assertParses("@if $arg != null and ($arg2 or arg3) {}");
    }

    public void testDashInSelectorInterpolationExpression() {
        assertParses(".#{$v1}#{$v2}-post {}");
    }

    public void testFunctionReturnBooleanExpression() {
        assertParses("@function even($number) {\n"
                + "    @return ceil($number / 2) == ($number / 2);\n"
                + "}");
    }

    public void testStarInSelectorInterpolationExpression() {
        assertParses(".#{$prefix}border-box * {}");
    }

    public void testGreaterSymbolInSelectorInterpolationExpression() {
        assertParses(".#{$prefix}rtl > .#{$prefix}box-item {}");
    }

    public void testLRPARENInPropertyValueInterpolationExpression() {
        assertParses(".clz { $rotation: rotate(#{$angle}deg); }");
        assertParses(".clz { background-image: slicer-corner-sprite(btn-#{$ui}-over, 'btn/btn-#{$ui}-over-corners'); }");
    }

    public void testCPExpressionInPropertyValue() {
        assertParses(".clz { background-position: 0 ($accordion-header-tool-size * -17); }");
    }

    public void testCPExpressionInPropertyValue2() {
        assertParses(".clz { padding: $toolbar-vertical-spacing ($toolbar-horizontal-spacing / 2) $toolbar-vertical-spacing ($toolbar-horizontal-spacing / 2); }");
    }

    public void testCPExpressionInPropertyValue3() {
        assertParses(".clz { $fieldset-collapse-tool-background-position-over: 0 (-$fieldset-collapse-tool-size) !default; }");
    }

    public void testFunctionInsideSASSInterpolationExpression() {
        assertParses(".clz { padding-left: #{left($fieldset-header-padding) - 2}; }");
    }

    public void testSimplePropertyValue() {
        assertParses(".clz { padding: 2cm 10px; }", false);
    }

    public void testPropertyValue2() {
        assertParses(".clz { padding-left: "
                + "top($form-error-under-padding) "
                + "right($form-error-under-padding) "
                + "bottom($form-error-under-padding) "
                + "(left($form-error-under-padding) + $form-error-icon-width + $form-error-under-icon-spacing); "
                + "}");
    }

    public void testPropertyValue3() {
        assertParses(".clz { background-position: 0 (0 - $form-checkbox-size); }");
    }

    public void testPropertyValue4() {
        assertParses(".clz { background-position: (-$html-editor-toolbar-icon-size) 0; }");
        assertParses(".clz { background-position: 0 (-$spinner-btn-height); }");
    }

    public void testPropertyValue5() {
        assertParses(".clz { background-position: 0 (-$spinner-btn-height); }");
    }

    public void testPropertyValueUnaryOperatorBeforeBrace() {
        assertParses(".clz { background-position: -($form-trigger-width * 3) (-$spinner-btn-height); }");
    }

    public void testCPVariableDeclaration() {
        assertParses("$panel-frame-header-padding:\n"
                + "    (top($panel-header-padding) - top($panel-frame-border-width))\n"
                + "    (right($panel-header-padding) - right($panel-frame-border-width))\n"
                + "    (bottom($panel-header-padding) - bottom($panel-frame-border-width))\n"
                + "    (left($panel-header-padding) - left($panel-frame-border-width))\n"
                + "    !default;");
    }

    public void testSASSInterpolationExpressionInCPVariableDeclaration() {
        assertParses("$fieldset-header-font: #{$fieldset-header-font-size}/#{$fieldset-header-line-height} $fieldset-header-font-weight $fieldset-header-font-family !default;");
        assertParses("$form-label-font: $form-label-font-weight #{$form-label-font-size}/#{$form-label-line-height} $form-label-font-family !default;");
        assertParses("$grid-editor-font: normal #{$grid-row-cell-font-size}/#{$grid-editor-line-height} $font-family !default;");
        assertParses("$grid-row-cell-font: normal #{$grid-row-cell-font-size}/#{$grid-row-cell-line-height} $font-family !default;");
    }

    public void testImportantKeywordInCPVariableDeclaration() {
        assertParses("$grid-row-editor-border: $grid-row-editor-border-width solid $grid-row-editor-border-color !important !default;");
    }

    public void testCommaSeparatedPropertyValues() {
        assertParses(".x { background-size: $majorsteps $majorsteps, $majorsteps $majorsteps, $minorsteps $minorsteps, $minorsteps $minorsteps; }");
    }

    public void testImportantSymbolJustAfterPropertyValue() throws ParseException, BadLocationException {
        assertParses(".x { z-index: 1000000!important; }");
        assertParses(".x { z-index: 1000000 !important; }");
    }

    public void testInclude() throws ParseException, BadLocationException {
        assertParses(".x { @include x-slicer($panel-header-ui + '-top'); }");
    }

    public void testMSPropertyValueAndInterpolationExpression() throws ParseException, BadLocationException {
        assertParses(".x { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$ie-rotation}); }");
    }

    public void testLastItemInBlockDoesntNeedToBeTerminatedWithSemicolon() throws ParseException, BadLocationException {
        assertParses(".x { $image-search-path: '.' !default }"); //doesn't work
        assertParses(".x { $image-search-path: '.' !default; }"); //works
    }

    public void testUnaryOperatorWithIE() throws ParseException, BadLocationException {
        assertParses(".x { margin-top: -#{top($fieldset-border-width)}; }");
    }

    public void testFunctionArgumentsCanBeBooleanExpression() throws ParseException, BadLocationException {
        assertParses("$foo: if($direction == top or $direction == bottom, 0, 1);");
    }

    public void testFunction3() throws ParseException, BadLocationException {
        assertParses(".clz { @include linear-gradient(#3875d7 20%, #2a62bc 90%); }");
    }

    public void testFont_FaceInMixin() throws ParseException, BadLocationException {
        assertParses("@mixin font-face($name){\n"
                + "    @font-face {\n"
                + "        font-family: $name;\n"
                + "    }\n"
                + "}");
    }

    public void testCommaInCPExpression() throws ParseException, BadLocationException {
        assertParses(".highlighted {\n"
                + "    @include linear-gradient((#3875d7 20%, #2a62bc 90%));\n"
                + "    color: #fff;\n"
                + "}");
    }

    public void testSassInclude() throws ParseException, BadLocationException {
        assertParses(".clz { @include extjs-button-ui(\n"
                + "    $ui: 'default-small',\n"
                + "\n"
                + "    $border-radius: $button-small-border-radius,\n"
                + "    $border-width: $button-small-border-width); }");
    }

    public void testSassInclude2() throws ParseException, BadLocationException {
        assertParses(".clz { @include extjs-toolbar-ui(\n"
                + "    'default',\n"
                + "    $background-color: $toolbar-background-color,\n"
                + "    $background-gradient: $toolbar-background-gradient,\n"
                + "    $border-color: $toolbar-border-color\n"
                + "); "
                + "}");
    }

    public void testNestedRules2() throws ParseException, BadLocationException {
        assertParses("x { y {} z {} }");
        assertParses("x { y {} }");
    }

    public void testNestedIfs() throws ParseException, BadLocationException {
        assertParses("x { @if true {} }");
        assertParses("x { @if $a==10 {} }");
        assertParses("x { @if true {} @if false {} }");
    }

    public void testNestedRuleNotParsedAsDeclaration() {
        String cssCode = "x {\n"
                + "    a, a:hover {\n"
                + "    }\n"
                + "}";

        CssParserResult result = TestUtil.parse(cssCode);
        Node tree = result.getParseTree();
//        NodeUtil.dumpTree(tree);
        Node node = NodeUtil.query(tree, "styleSheet/body/bodyItem/rule/declarations/declaration/rule");
        assertNotNull(node);
        assertEquals(NodeType.rule, node.type());

        assertResultOK(result);

    }

    public void testMixinCallWithBlock() throws ParseException, BadLocationException {
        assertParses("@include respond_to ( handhelds ) { \n"
                + "    font-size: 1em;\n"
                + "}  ");

        assertParses("h1 {"
                + "     @include respond_to ( handhelds ) { \n"
                + "         font-size: 1em;\n"
                + "     }\n"
                + "}");

    }

    public void testParseCommentAtTheFileEnd() throws ParseException, BadLocationException {
        assertParses("div {}\n"
                + "/*comment*/");

        assertParses("//comment1\n"
                + "div {}\n"
                + "//comment2");

    }

    public void testMixinDeclarationWithVarargs() throws ParseException, BadLocationException {
        assertParses("@mixin box-shadow($shadows...) {}\n");
    }

    public void testMixinCallWithVarargs() throws ParseException, BadLocationException {
        assertParses("@include colors($values...);");
    }

    //https://netbeans.org/bugzilla/show_bug.cgi?id=227484#c21
    public void testMixinCallInMedia() throws ParseException, BadLocationException {
        assertParses("@media screen{\n"
                + "    @include test2;\n"
                + "}");
    }

    //https://netbeans.org/bugzilla/show_bug.cgi?id=227484#c19
    public void testAmpInSelector() throws ParseException, BadLocationException {
        assertParses("&.primary, input[type=\"submit\"] & { }\n");
        //assertParses("&.primary, input[type=\"submit\"]& { }\n"); //this is not possible in scss
        assertParses("&.primary, & { }\n");
    }

    public void testIncompleteSelectors() throws ParseException, BadLocationException {
        assertParses(".pills {\n"
                + "  @include clearfix;\n"
                + "  > li {\n"
                + "    float: left;\n"
                + "    > a {\n"
                + "      display: block;\n"
                + "    }\n"
                + "  }\n"
                + "}");
    }

    //https://netbeans.org/bugzilla/show_bug.cgi?id=233038#c2
    public void testCPExpressionInMediaQuery() throws ParseException, BadLocationException {
        assertParses("@media only tv and (resolution: 120 * 3 + \"dpi\"){\n"
                + "\n"
                + "}");
    }

    public void testMixinBodyPropertyRecovery() throws ParseException, BadLocationException {
        CssParserResult result = TestUtil.parse("@mixin mymixin() { color: }");
        Node node = NodeUtil.query(result.getParseTree(),
                "styleSheet/body/bodyItem/cp_mixin_declaration/cp_mixin_block/declarations/declaration/propertyDeclaration/property");
        assertNotNull(node);
    }

    public void testPercentageInExpression() throws ParseException, BadLocationException {
        CssParserResult result = TestUtil.parse("$size: 20;\n"
                + ".clz {\n"
                + "    font: %/20;\n"
                + "}");

//        NodeUtil.dumpTree(result.getParseTree());
        assertResultOK(result);

        //original sample
        assertParses("@mixin base-type($weight, $line-height, $font-size, $font-family...) {\n"
                + "    @if $serif-boolean {\n"
                + "        font: $weight #{$font-size}%/#{$line-height} $font-family;\n"
                + "    }@else {\n"
                + "        font: $weight #{$font-size}%/#{$line-height} $font-family-sans;\n"
                + "    }\n"
                + "}");
    }

    public void testSelectorWithInterpolationExpressionInMediaBody() throws ParseException, BadLocationException {
        assertParses("@media only screen and (min-resolution: 1000) {\n"
                + "\n"
                + "      #{$selector}:before {\n"
                + "        content: $hiResIcon;\n"
                + "      }\n"
                + "    }");
    }

    public void testInterpolationExpressionWithBrackets() {
        assertParses(".#{$item}-important { background-color: green; }");

        assertParses(".important[href]  {  }\n");

        assertParses(".#{$item}important[href]  {  }\n");

        assertParses("@each $item in label, badge {\n"
                + "   .#{$item}-important         { background-color: green; }\n"
                + "   .#{$item}-important[href]   { background-color: blue; }\n"
                + " }");
    }

    public void testIssue237010() {
        assertParses("$textColorError: \"blue\";\n"
                + "$allowTagSelectors: true;\n"
                + "@if $allowTagSelectors != true {\n"
                + "  .oj-text-input {\n"
                + "    @include oj-normalize-text-input;\n"
                + "  }\n"
                + "  .oj-text-input[type='search'] {\n"
                + "    @include oj-normalize-search-input;\n"
                + "  }\n"
                + "}\n"
                + "$currSelectors: if($allowTagSelectors,\n"
                + "  // here:  \n"
                + " \".oj-date-input.oj-invalid, \n"
                + "  input[type='url'].oj-invalid\", \n"
                + "  // class selectors: \n"
                + "  \".oj-text-input.oj-invalid,\n"
                + "  .oj-textarea.oj-invalid\");\n"
                + "#{$currSelectors} {\n"
                + "    border: 2px solid $textColorError; \n"
                + "}");
    }

    public void testIssue236388() {
        assertParses("@media only screen and (min-width: 768px) {\n"
                + "    @import \"_grid.less\";\n"
                + "    @import \"_768up.less\";\n"
                + "}");
    }

    public void testIssue236706() {
        assertParses("@mixin web-font($fonts, $variants: (), $subsets: (), $text: '', $effects: (), $secure: false) {}");
    }

    //new SASS 3.3 stuff
    //http://thesassway.com/news/sass-3-3-released
    public void testSassMap() {
        assertParses("$colors: (\n"
                + "  header: #b06,\n"
                + "  footer: $another_var,\n"
                + ")");

        assertParses("$susy: (\n"
                + "    columns: 8,\n"
                + "    gutters: 1/10\n"
                + ");");

    }

    public void testSassMapInBlock() {
        assertParses("div { $colors: (\n"
                + "  header: #b06,\n"
                + "  footer: $another_var,\n"
                + ") }");

        assertParses(".clz {\n $susy: (\n"
                + "    columns: 8,\n"
                + "    gutters: 1/10\n"
                + "); }");
    }

    public void testSassMapComplex() {
        //bit more complex example
        assertParses("$var: red;\n"
                + "\n"
                + "$map: ( cool: 'pink');\n"
                + "\n"
                + "div {\n"
                + "\n"
                + "    $colors: (\n"
                + "        header: $var,\n"
                + "        text: #334,\n"
                + "        color: map-get($map, cool),\n"
                + "        );\n"
                + "\n"
                + "    .pink {\n"
                + "        color: map-get($colors, color);\n"
                + "    }\n"
                + "\n"
                + "}\n");
    }

    public void testParentSelectorSuffixes() {
        assertParses("// Ampersand in SassScript:\n"
                + ".button {\n"
                + "  &-primary { background: orange; }\n"
                + "  &-secondary { background: blue; }\n"
                + "}\n");
    }

    public void testAtRoot() {
        assertParses(".message {\n"
                + "  @at-root {\n"
                + "    .info { color: blue; }\n"
                + "    .error { color: red; }\n"
                + "  }\n"
                + "}\n");

        assertParses(".message {\n"
                + "  @at-root .info { color: blue; }\n"
                + "  @at-root .error { color: red; }\n"
                + "}\n");
    }

    public void testIfInFn() {
        assertParses("@function my-function($args...) {\n"
                + "  // Assign the first argument to $param-1\n"
                + "  $param-1: nth($args, 1);\n"
                + "\n"
                + "  // If a second argument was passed assign it to $param-2,\n"
                + "  // otherwise assign an empty list:\n"
                + "  $param-2: if(length($args) > 1, nth($args, 2), ());\n"
                + "}\n");
    }

    public void testBwLoop() {
        assertParses("@for $i from 5 through 1 {\n"
                + ".selector#{$i} { content: $i; }"
                + "}\n");
    }

    //can't parse an expression inside the interpolation expression
    public void _testInterpolationExpression_BUG1() {
        assertParses(".span:nth-child(#{6-$i}) { content: $i; }");
    }

    //can't parse an interpolation expression as fn argument
    public void _testInterpolationExpression_BUG2() {
        assertParses(".span:nth-child(#{$i}) { content: $i; }");
    }

    public void testForEachMultiAssignments() {
        assertParses("$animals: (puma, black, default),\n"
                + "          (sea-slug, blue, pointer),\n"
                + "          (egret, white, move);\n"
                + "\n"
                + "@each $animal, $color, $cursor in $animals {\n"
                + "  .#{$animal}-icon {\n"
                + "    background-image: url('/images/#{$animal}.png');\n"
                + "    border: 2px solid $color;\n"
                + "    cursor: $cursor;\n"
                + "  }\n"
                + "}\n");
    }

    public void testMixin() {
        String source = "@mixin simple($selectorPrefix){}";
        CssParserResult result = TestUtil.parse(source);
        assertResultOK(result);
    }

    public void testScssVar() {
        String source = ".test {background-image: $var;}";
        CssParserResult result = TestUtil.parse(source);
        assertResultOK(result);
    }

    public void testInterpolationInSelector() {
        String source = ".#{$selectorPrefix}-odd-cols-#{$i} > .oj-row > .oj-col:nth-child(even) {}";

        CssParserResult result = TestUtil.parse(source);
        assertResultOK(result);

        source = ".table-odd-cols-#{$i} > .oj-row > .oj-col:nth-child(even) {}";
        result = TestUtil.parse(source);
        assertResultOK(result);

        source = ".table-odd-cols-#{$i}-test:nth-child(even) {}";
        result = TestUtil.parse(source);
        assertResultOK(result);
    }

    public void testMapWithStrings() {
        String source = "$map: (\n"
                + "    '1':       'one',\n"
                + ");";
        CssParserResult result = TestUtil.parse(source);
        assertResultOK(result);
    }

    public void testMapDeclarationWithDefault() {
        String source = "$modules: () !default;";
        CssParserResult result = TestUtil.parse(source);
        assertResultOK(result);
    }

    public void testIfWithoutSpace() {
        String source = "@if($allowTagSelectors)\n"
                + "  {\n"
                + "    #{$tagSelectors} {\n"
                + "      color: red;\n"
                + "    }\n"
                + "  }";
        CssParserResult result = TestUtil.parse(source);
        assertResultOK(result);
    }

    public void testElseIfTogether() {
        String source = "@mixin icon($name, $character: null, $font-family: 'Pictos') {\n"
                + "    @if $character != null {\n"
                + "        content: \"#{$character}\";\n"
                + "    } @elseif $raw_character != null {\n"
                + "        content: \"#{$raw_character}\";\n"
                + "    }\n"
                + "}";
        CssParserResult result = TestUtil.parse(source);
        assertResultOK(result);
    }

    public void testFunctionParametersOnNewLines() {
        String source = "@function matte-gradient (\n"
                + "    $bg-color: $base-color,\n"
                + "    $direction: top,\n"
                + "    $contrast: 1\n"
                + ") {\n"
                + "    @return linear-gradient(red);\n"
                + "}";
        CssParserResult result = TestUtil.parse(source);
        assertResultOK(result);
    }
    
    public void testAdditionalCommaInTheEndOfSelector() {
        String source = ".ui-convex,\n"
                + ".ui-convex-hover,\n"
                + "{\n"
                + "    width: 10em;\n"
                + "    height: 2em;\n"
                + "    \n"
                + "    margin: 1em auto;\n"
                + "}";
        CssParserResult result = TestUtil.parse(source);
        assertResultOK(result);
    }
    
    public void testQuoteEscapingInString() {
        String source = "@function icon-character-for-name($name) {\n"
                + "    // http://pictos.cc/font/\n"
                + "\n"
                + "    // Row 1\n"
                + "    @if ($name == \"anchor\") { @return \"a\"; }\n"
                + "    @else if ($name == \"quote\") { @return \"\\\"\"; }\n"
                + "    @else if ($name == \"volume_mute\") { @return \"<\"; }}";
        CssParserResult result = TestUtil.parse(source);
        assertResultOK(result);
    }
    
    public void testEachWithMapPairs() {
      String source = "@each $header, $size in (h1: 2em, h2: 1.5em, h3: 1.2em) {\n"
              + "  #{$header} {\n"
              + "    font-size: $size;\n"
              + "  }\n"
              + "}";
      CssParserResult result = TestUtil.parse(source);
      assertResultOK(result);
    }
    
    public void testParenSelectorAsPropertyValue() {
        String source = ".foo .bar, .baz { $selector: & }";
        CssParserResult result = TestUtil.parse(source);
        assertResultOK(result);
    }
    
    public void testMultiDimensionalMaps() {
        String source = "$type-scale: (\n"
                + "    tiny: (\n"
                + "        font-size: 10.24px,\n"
                + "        base-lines: 0.5\n"
                + "    ),\n"
                + "    small: (\n"
                + "        font-size: 12.8px,\n"
                + "        base-lines: 0.75\n"
                + "    )\n"
                + ");\n"
                + "";
        CssParserResult result = TestUtil.parse(source);
        assertResultOK(result);
    }

    public void testErrorWithSemi() {
        String source = "@mixin adjust-location($x, $y) {\n"
                + "  @if unitless($x) {\n"
                + "    @error \"$x may not be unitless, was #{$x}.\";\n"
                + "  }\n"
                + "  position: relative; left: $x; top: $y;\n"
                + "}\n"
                + "";
        CssParserResult result = TestUtil.parse(source);
        assertResultOK(result);
    }
    
    public void testAtRootDeclaration() {
        String source = "@media print {\n"
                + "  .page {\n"
                + "    width: 8in;\n"
                + "    @at-root (without: media) {\n"
                + "      color: red;\n"
                + "    }\n"
                + "  }\n"
                + "}";
        CssParserResult result = TestUtil.parse(source);
        assertResultOK(result);
    }
    
    public void testKeyframesInSass() {
        String source = "@mixin keyframes($name)\n"
                + "{\n"
                + "    @-webkit-keyframes $name\n"
                + "    {\n"
                + "        @content;\n"
                + "    }\n"
                + "    @keyframes $name\n"
                + "    {\n"
                + "        @content;\n"
                + "    }\n"
                + "}";
        CssParserResult result = TestUtil.parse(source);
        assertResultOK(result);
        
        source = "@mixin keyframes($name) {\n"
                + "  @-webkit-keyframes #{$name} {\n"
                + "    @content; \n"
                + "  }}";
        
        result = TestUtil.parse(source);
        assertResultOK(result);
        
        source = "@include keyframes(slide-down) {\n"
                + "  0% { opacity: 1; }\n"
                + "  90% { opacity: 0; }\n"
                + "}";
        
        result = TestUtil.parse(source);
        assertResultOK(result);
    }
    
    public void testSassNestingGreater() {
        assertParses("div {\n"
                + "    margin: 0;\n"
                + "    > {\n"
                + "        label {\n"
                + "            float: left;\n"
                + "        }\n"
                + "        input {\n"
                + "            width: 70%; \n"
                + "        }\n"
                + "    }\n"
                + "}");
    } 
    
    public void testPseudoClassBeforeAmpersand() {
        assertParses("  li {\n"
                + "        a:hover, &.selected {\n"
                + "            display: block;\n"
                + "        }\n"
                + "    }");

    }
    
    public void testDotInterpolationMinus() {
    assertParses("$fa-css-prefix : test;\n"
                + ".#{$fa-css-prefix}-2x { font-size: 2em; }");
    }
    
    public void testAmpColonInterpolation() {
        assertParses("a {\n"
                + "    &:#{$state}{\n"
                + "        color: black\n"
                + "    }\n"
                + "}");
    }

    public void testMathExpWithUnits() {
        assertParses("$fa-li-width: (30em / 14) !default;");
    }
    
    public void testExtendPlaceHolderWithInterpolation() {
        assertParses("%at-contactformelement-skin-default {\n"
                + "  margin-top: 6rem;\n"
                + "}\n"
                + "\n"
                + "@mixin contactformelement($selector, $skin: default) {\n"
                + "  #{$selector} {\n"
                + "    @extend %at-contactformelement-skin-#{$skin} !optional;\n"
                + "  }\n"
                + "}");
    }
    
    public void testSpacesInSassInterpolation() {
        assertParses(".test {\n"
                + "    &.#{ $active_class } > a {\n"
                + "        background: $active-bg;\n"
                + "    }\n"
                + "}");
    }
    
    public void testMathExpressionInPropertyValue() {
        assertParses("@mixin dropdown-container($content:list, $triangle:true, $max-width:$f-dropdown-max-width) {\n"
                + "    &:before {\n"
                + "      @include css-triangle($f-dropdown-triangle-size, $f-dropdown-triangle-color, #{$opposite-direction});\n"
                + "      position: absolute;\n"
                + "      top: $f-dropdown-triangle-side-offset;\n"
                + "      #{$default-float}: -($f-dropdown-triangle-size * 2);\n"
                + "      z-index: 89;\n"
                + "    }\n"
                + "}");
    }
    
    public void testPropertyDeclarationWithExpression() {
        assertParses("@media only screen and (min-width: $media-xs)\n"
                + "{\n"
                + "    width: calc(100% - #{$left-column-width});\n"
                + "}");
    }
    
    public void testURLinScss() {
        assertParses("@function oj-image-url($path){\n"
                + " @return url($imageDir + $path);\n"
                + "}");
        assertParses(".xxx {\n"
                + "    content: url($imagesDir + \"functional/func_back_24_ena.png\"); \n"
                + "}");
        assertParses("@function composeURL($a, $b)\n"
                + "{\n"
                + "    @return url($a + $b);\n"
                + "}");
        assertParses("@each $ord in 1, 2, 3, 4 {\n"
                + "	\n"
                + "	body._#{$ord} #page {\n"
                + "		background: $page-bg url(#{$scssDir}/images/bg#{$ord}-squares-blue.png);\n"
                + "	}\n"
                + "	\n"
                + "}");
        assertParses("li {\n"
                + "    background: transparent url(@{img_path}list-bullet.png) no-repeat 0 0;\n"
                + "				padding-left: 14px;\n"
                + "				margin-bottom: 5px;\n"
                + "}");
        
    }
    
    public void testPseudoWithWhitespaceOnEnd() {
        assertParses("#test\n"
                + "{\n"
                + "    &:not( .active )\n"
                + "    {\n"
                + "        background-color: #E1E1E1;\n"
                + "    }\n"
                + "}");
    }
    
    public void testFunctionQuoteInMap() {
        assertParses("$a: (quote(bgcolor): black)");
    }
    
    public void testSassMapWithNumbers() {
        assertParses("$twoToTheN: (0: 1, 1: 2, 2: 4, 3: 8, 4: 16, 5: 32);");
    }
    
    public void testStringEscapesInInterpolation() {
        assertParses("@function oj-prepend-slash($char)\n"
                + "{\n"
                + "  @return #{\"\\\"\\\\\"}#{$char + \"\\\"\"};\n"
                + "}");
    }
    
    public void testEachWithMultipleLists() {
        assertParses("@each $className, $iconFile\n"
                + "in\n"
                + "(my_class, my_file_name_base),\n"
                + "{}");
        assertParses("@each $animal, $color, $cursor in (puma, black, default),\n"
                + "                                  (sea-slug, blue, pointer),\n"
                + "                                  (egret, white, move) {\n"
                + "  .#{$animal}-icon {\n"
                + "    background-image: url('/images/#{$animal}.png');\n"
                + "    border: 2px solid $color;\n"
                + "    cursor: $cursor;\n"
                + "  }\n"
                + "}");
    }
    
    public void testMapAnyDatatypeasKey() {
        assertParses("$font-formats: 'woff' 'ttf'; // Define what webfont formats need importing\n"
                + "$font-path: '../fonts/'; // Set the a path to your fonts directory\n"
                + "\n"
                + "$fonts: (\n"
                + "  'heading': ( // give your font a semantic name for reference\n"
                + "    'name': 'maven', // optionally set a different font name\n"
                + "    'stack': ('helvetica', 'arial', sans-serif), // define the stack\n"
                + "    'normal': 'maven/maven_pro_regular-webfont', // point to any webfont files\n"
                + "    'bold': 'maven/maven_pro_bold-webfont',\n"
                + "  ),\n"
                + "\n"
                + "  'body': (\n"
                + "    'name': 'exo',\n"
                + "    'stack': ('helvetica', 'arial', sans-serif),\n"
                + "    'normal': 'exo/exo2-regular-webfont',\n"
                + "    'italic': 'exo/exo2-italic-webfont',\n"
                + "    'bold': 'exo/exo2-bold-webfont',\n"
                + "    'bold' 'italic': 'exo/exo2-bolditalic-webfont',\n"
                + "  ),\n"
                + "\n"
                + "  'alias': 'body', // create aliases when useful\n"
                + ");");
    }
    
    public void testMediaWithInterpolation() {
        assertParses("$information-phone: \"(max-width : 320px)\";\n"
                + "@media screen and (#{$information-phone}) {\n"
                + "  background: red;\n"
                + "}\n"
                + "@media screen and #{$information-phone} {\n"
                + "  background: red;\n"
                + "}");
    }
}
