content: // Copyright 2018 Google Inc. Use of this source code is governed by an
  // MIT-style license that can be found in the LICENSE file or at
  // https://opensource.org/licenses/MIT.
  
  @TestOn('vm')
  library;
  
  import 'package:test/test.dart';
  
  import 'package:sass/sass.dart';
  
  void main() {
    group("in style rules", () {
      test("removes unnecessary whitespace and semicolons", () {
        expect(_compile("a {x: y}"), equals("a{x:y}"));
      });
  
      group("for selectors", () {
        test("preserves whitespace where necessary", () {
          expect(_compile("a b .c {x: y}"), equals("a b .c{x:y}"));
        });
  
        test("removes whitespace after commas", () {
          expect(_compile("a, b, .c {x: y}"), equals("a,b,.c{x:y}"));
        });
  
        test("doesn't preserve newlines", () {
          expect(_compile("a,\nb,\n.c {x: y}"), equals("a,b,.c{x:y}"));
        });
  
        test("removes whitespace around combinators", () {
          expect(_compile("a > b {x: y}"), equals("a>b{x:y}"));
          expect(_compile("a + b {x: y}"), equals("a+b{x:y}"));
          expect(_compile("a ~ b {x: y}"), equals("a~b{x:y}"));
        });
  
        group("in prefixed pseudos", () {
          test("preserves whitespace", () {
            expect(
              _compile("a:nth-child(2n of b) {x: y}"),
              equals("a:nth-child(2n of b){x:y}"),
            );
          });
  
          test("removes whitespace after commas", () {
            expect(
              _compile("a:nth-child(2n of b, c) {x: y}"),
              equals("a:nth-child(2n of b,c){x:y}"),
            );
          });
        });
  
        group("in attribute selectors with modifiers", () {
          test("removes whitespace when quotes are required", () {
            expect(_compile('[a=" " b] {x: y}'), equals('[a=" "b]{x:y}'));
          });
  
          test("doesn't remove whitespace when quotes aren't required", () {
            expect(_compile('[a="b"c] {x: y}'), equals('[a=b c]{x:y}'));
          });
        });
      });
  
      group("for declarations", () {
        test("preserves semicolons when necessary", () {
          expect(_compile("a {q: r; s: t}"), equals("a{q:r;s:t}"));
        });
  
        group("of custom properties", () {
          test("folds whitespace for multiline properties", () {
            expect(
              _compile("""
                a {
                  --foo: {
                    q: r;
                    b {
                      s: t;
                    }
                  }
                }
              """),
              equals("a{--foo: { q: r; b { s: t; } } }"),
            );
          });
  
          test("folds whitespace for single-line properties", () {
            expect(
              _compile("""
                a {
                  --foo: a   b\t\tc;
                }
              """),
              equals("a{--foo: a b\tc}"),
            );
          });
  
          test("preserves semicolons when necessary", () {
            expect(
              _compile("""
                a {
                  --foo: {
                    a: b;
                  };
                  --bar: x y;
                  --baz: q r;
                }
              """),
              equals("a{--foo: { a: b; };--bar: x y;--baz: q r}"),
            );
          });
        });
      });
    });
  
    group("values:", () {
      group("numbers", () {
        test("omit the leading 0", () {
          expect(_compile("a {b: 0.123}"), equals("a{b:.123}"));
          expect(_compile("a {b: 0.123px}"), equals("a{b:.123px}"));
        });
      });
  
      group("lists", () {
        test("don't include spaces after commas", () {
          expect(_compile("a {b: x, y, z}"), equals("a{b:x,y,z}"));
        });
  
        test("don't include spaces around slashes", () {
          expect(
            _compile("""
              @use "sass:list";
              a {b: list.slash(x, y, z)}
            """),
            equals("a{b:x/y/z}"),
          );
        });
  
        test("do include spaces when space-separated", () {
          expect(_compile("a {b: x y z}"), equals("a{b:x y z}"));
        });
      });
  
      group("colors", () {
        test("use names when they're shortest", () {
          expect(_compile("a {b: #f00}"), equals("a{b:red}"));
        });
  
        test("use terse hex when it's shortest", () {
          expect(_compile("a {b: white}"), equals("a{b:#fff}"));
        });
  
        test("use verbose hex when it's shortest", () {
          expect(_compile("a {b: darkgoldenrod}"), equals("a{b:#b8860b}"));
        });
  
        test("use rgba() when necessary", () {
          expect(
            _compile("a {b: rgba(255, 0, 0, 0.5)}"),
            equals("a{b:rgba(255,0,0,.5)}"),
          );
        });
  
        test("don't error when there's no name", () {
          expect(_compile("a {b: #cc3232}"), equals("a{b:#cc3232}"));
        });
      });
  
      group("strings", () {
        group("emits private-use area characters as literal characters", () {
          testCharacter(int character) {
            var escape = "\\${character.toRadixString(16)}";
            test(escape, () {
              expect(
                _compile("a {b: $escape}"),
                equalsIgnoringWhitespace(
                  "a{b:${String.fromCharCode(character)}}",
                ),
              );
            });
          }
  
          group("in the basic multilingual plane", () {
            testCharacter(0xe000);
            testCharacter(0xf000);
            testCharacter(0xf8ff);
          });
  
          group("in the supplementary planes", () {
            testCharacter(0xf0000);
            testCharacter(0xfabcd);
            testCharacter(0xffffd);
            testCharacter(0x100000);
            testCharacter(0x10abcd);
            testCharacter(0x10fffd);
          });
        });
      });
    });
  
    group("the top level", () {
      test("removes whitespace and semicolons between at-rules", () {
        expect(_compile("@foo; @bar; @baz;"), equals("@foo;@bar;@baz"));
      });
  
      test("removes whitespace between style rules", () {
        expect(_compile("a {b: c} x {y: z}"), equals("a{b:c}x{y:z}"));
      });
    });
  
    group("@supports", () {
      test("removes whitespace around the condition", () {
        expect(
          _compile("@supports (display: flex) {a {b: c}}"),
          equals("@supports(display: flex){a{b:c}}"),
        );
      });
  
      test("preserves whitespace before the condition if necessary", () {
        expect(
          _compile("@supports not (display: flex) {a {b: c}}"),
          equals("@supports not (display: flex){a{b:c}}"),
        );
      });
    });
  
    group("@media", () {
      test("removes whitespace around the query", () {
        expect(
          _compile("@media (min-width: 900px) {a {b: c}}"),
          equals("@media(min-width: 900px){a{b:c}}"),
        );
      });
  
      test("preserves whitespace before the query if necessary", () {
        expect(
          _compile("@media screen {a {b: c}}"),
          equals("@media screen{a{b:c}}"),
        );
      });
  
      // Removing whitespace after "and", "or", or "not" is forbidden because it
      // would cause it to parse as a function token.
      group('preserves whitespace when necessary', () {
        test('around "and"', () {
          expect(
            _compile("""
              @media screen and (min-width: 900px) and (max-width: 100px) {
                a {b: c}
              }
            """),
            equals(
              "@media screen and (min-width: 900px)and (max-width: 100px)"
              "{a{b:c}}",
            ),
          );
        });
  
        test('around "or"', () {
          expect(
            _compile("""
              @media (min-width: 900px) or (max-width: 100px) or (print) {
                a {b: c}
              }
            """),
            equals(
              "@media(min-width: 900px)or (max-width: 100px)or (print)"
              "{a{b:c}}",
            ),
          );
        });
  
        test('after "not"', () {
          expect(
            _compile("""
              @media not (min-width: 900px) {
                a {b: c}
              }
            """),
            equals("@media not (min-width: 900px){a{b:c}}"),
          );
        });
      });
  
      test("preserves whitespace around the modifier", () {
        expect(
          _compile("@media only screen {a {b: c}}"),
          equals("@media only screen{a{b:c}}"),
        );
      });
    });
  
    group("@keyframes", () {
      test("removes whitespace after the selector", () {
        expect(
          _compile("@keyframes a {from {a: b}}"),
          equals("@keyframes a{from{a:b}}"),
        );
      });
  
      test("removes whitespace after commas", () {
        expect(
          _compile("@keyframes a {from, to {a: b}}"),
          equals("@keyframes a{from,to{a:b}}"),
        );
      });
    });
  
    group("@import", () {
      test("removes whitespace before the URL", () {
        expect(_compile('@import "foo.css";'), equals('@import"foo.css"'));
      });
  
      test("converts a url() to a string", () {
        expect(_compile('@import url(foo.css);'), equals('@import"foo.css"'));
        expect(_compile('@import url("foo.css");'), equals('@import"foo.css"'));
      });
  
      test("removes whitespace before a media query", () {
        expect(
          _compile('@import "foo.css" screen;'),
          equals('@import"foo.css"screen'),
        );
      });
  
      test("removes whitespace before a supports condition", () {
        expect(
          _compile('@import "foo.css" supports(display: flex);'),
          equals('@import"foo.css"supports(display: flex)'),
        );
      });
    });
  
    group("comments", () {
      test("are removed", () {
        expect(_compile("/* foo bar */"), isEmpty);
        expect(
          _compile("""
            a {
              b: c;
              /* foo bar */
              d: e;
            }
          """),
          equals("a{b:c;d:e}"),
        );
      });
  
      test("remove their parents if they're the only contents", () {
        expect(_compile("a {/* foo bar */}"), isEmpty);
        expect(
          _compile("""
            a {
              /* foo bar */
              /* baz bang */
            }
          """),
          isEmpty,
        );
      });
  
      test("are preserved with /*!", () {
        expect(_compile("/*! foo bar */"), equals("/*! foo bar */"));
        expect(
          _compile("/*! foo */\n/*! bar */"),
          equals("/*! foo *//*! bar */"),
        );
        expect(
          _compile("""
            a {
              /*! foo bar */
            }
          """),
          equals("a{/*! foo bar */}"),
        );
      });
    });
  }
  
  /// Like [compileString], but always produces compressed output.
  String _compile(String source) =>
      compileString(source, style: OutputStyle.compressed);
  