import { describe, expect, test } from "vitest";
import { compile, serialize, stringify } from "../stylis.ts";

const stylis = (declarations, selectors = ".user") =>
  serialize(compile(`${selectors}{${declarations}}`), stringify);

describe("Parser", () => {
  test("unnested", () => {
    expect(serialize(compile(`--foo:none;@supports{--bar:none;}`), stringify)).to.equal(
      `--foo:none;@supports{--bar:none;}`,
    );
  });

  test("escape", () => {
    expect(
      stylis(`
        height:calc(\\))\t!important;
    `),
    ).to.equal(`.user{height:calc(\\))!important;}`);
  });

  test("calc", () => {
    expect(
      stylis(`
        height:calc( 100vh - 1px );
        height:calc(
      100vh -
          1px
        );
    `),
    ).to.equal(
      `.user{height:calc( 100vh - 1px );height:calc(\n      100vh -\n          1px\n        );}`,
    );
  });

  test("at-rules", () => {
    expect(
      stylis(`
        @-ms-viewport {
          width:device-width;
        }
        @viewport {
          width:device-width;
        }
        @page & {
          invalid:true;
        }
        @page {
          size:A4 landscape;
        }
        @document url(://www.w3.org/),url-prefix(//www.w3.org/),domain(mozilla.org),regexp("https:.*") {
          body {
            color: red;
          }
        }
        @viewport {
          min-width:640px;
          max-width:800px;
        }
        @counter-style list {
          system:fixed;
          symbols:url();
          suffix:" ";
        }
        @-moz-document url-prefix() {
          .selector {
            color:lime;
          }
        }
        @page {
          color:red;
          @bottom-right {
            content: counter(pages);
            margin-right: 1cm;
          }
          width: none;
          @right-bottom {
            content: "Content";
          }
          @left-bottom {
            content: "Content";
          }
        }
      `),
    ).to.equal(
      [
        `@-ms-viewport{width:device-width;}`,
        `@viewport{width:device-width;}`,
        `@page &{invalid:true;}`,
        `@page{size:A4 landscape;}`,
        `@document url(://www.w3.org/),url-prefix(//www.w3.org/),domain(mozilla.org),regexp("https:.*"){.user body{color:red;}}`,
        `@viewport{min-width:640px;max-width:800px;}`,
        `@counter-style list{system:fixed;symbols:url();suffix:" ";}`,
        `@-moz-document url-prefix(){.user .selector{color:lime;}}`,
        `@page{color:red;@bottom-right{content:counter(pages);margin-right:1cm;}width:none;@right-bottom{content:"Content";}@left-bottom{content:"Content";}}`,
      ].join(""),
    );
  });

  test("universal selector", () => {
    expect(
      stylis(`
        * {
          color:red;
        }
        svg {
          &, & * {
            fill: currentColor;
          }
        }
        * * {color:hotpink;}
      `),
    ).to.equal(
      [
        `.user *{color:red;}`,
        `.user svg,.user svg *{fill:currentColor;}`,
        `.user * *{color:hotpink;}`,
      ].join(""),
    );
  });

  test("flat", () => {
    expect(
      stylis(`
        color:20px;
        font-size:20px
      `),
    ).to.equal(`.user{color:20px;font-size:20px;}`);
  });

  test("namespace", () => {
    expect(
      stylis(`
        & {
          color:red;
        }
     `),
    ).to.equal(`.user{color:red;}`);
  });

  test("& in value in a nested function", () => {
    const urlOneX = "https://images.ctfassets.net/test.jpg?fm=webp&q=70&w=1000";
    const urlTwoX = "https://images.ctfassets.net/test.jpg?fm=webp&q=70&w=2000";
    expect(
      stylis(`
        background-image: image-set(url(${urlOneX}) 1x, url(${urlTwoX}) 2x);
     `),
    ).to.equal(`.user{background-image:image-set(url(${urlOneX}) 1x, url(${urlTwoX}) 2x);}`);
  });

  test("& in a string", () => {
    expect(
      stylis(`
        & [href="https://css-tricks.com?a=1&b=2"] {
          color:red;
        }
     `),
    ).to.equal(`.user [href="https://css-tricks.com?a=1&b=2"]{color:red;}`);
  });

  test("& root should be removed", () => {
    expect(
      stylis(
        `
        color:red;
     `,
        "&",
      ),
    ).to.equal(``);
  });

  test("& root should be removed, issue: #333", () => {
    expect(
      stylis(
        `
        color: red;
     `,
        ":where(.cls)& span",
      ),
    ).to.equal(`:where(.cls) span{color:red;}`);
  });

  test("noop removal of empty variables", () => {
    expect(
      stylis(`
        --tw-brightness:/*!*/;
     `),
    ).to.equal(`.user{--tw-brightness:;}`);
  });

  test("& in first selector within a comma-separated list", () => {
    expect(
      stylis(`
        div {
          display: flex;

          &.foo,
          p:not(:last-child) {
            background: red;
          }
        }
      `),
    ).to.equal(
      `.user div{display:flex;}.user div.foo,.user div p:not(:last-child){background:red;}`,
    );
  });

  test("escaped chars in selector identifiers", () => {
    expect(
      stylis(`
        &.B\\&W{color:red;}
        &.\\@example\\.com{color:blue;}
        &.owner\\/founder{color:green;}
        &.discount\\%  {color:purple;}
     `),
    ).to.equal(
      [
        ".user.B\\&W{color:red;}",
        ".user.\\@example\\.com{color:blue;}",
        ".user.owner\\/founder{color:green;}",
        // while double spaces after escaped hex codes need to be preserved,
        // after an escaped character / code point it need not be preserved
        ".user.discount\\%{color:purple;}",
      ].join(""),
    );
  });

  test("escaped hex codes in selector identifiers", () => {
    expect(
      stylis(`
        &.B\\26W{color:red;}
        &.B\\000026W{color:green;}
        &.B\\26 W{color:blue;}
     `),
    ).to.equal(
      [
        ".user.B\\26W{color:red;}",
        ".user.B\\000026W{color:green;}",
        ".user.B\\26 W{color:blue;}",
      ].join(""),
    );
  });

  test("double spaces after escaped hex codes in selector identifiers", () => {
    expect(
      stylis(`
        &.endsWith\\0000A9  a.childNode{color:green;}
        &.endsWith\\AE  a.childNode{color:yellow;}
        &.Q\\000026A  a.childNode{color:purple;}
     `),
    ).to.equal(
      [
        // next rules are important because the hex digits terminating space
        // is not the same as a combinator space
        ".user.endsWith\\0000A9  a.childNode{color:green;}",
        ".user.endsWith\\AE  a.childNode{color:yellow;}",
        // max 6 hex chars in escape sequence - whitespace after 7th char
        // is just normal whitespace and can be collapsed
        ".user.Q\\000026A a.childNode{color:purple;}",
      ].join(""),
    );
  });

  test("comments", () => {
    expect(
      stylis(`
        // line comment
        // color: red;
        /**
         * removes block comments and line comments,
         * there's a fire in the house // there is
         */
        button /*
          // what's
          xxx
          */
        {color: blue;}
        // hello
        button /* 1 */
        {
          color: red; /* 2 */
        }
        /*! 1 */
        color: red;
        /*! 2 */
        h1 {
          /*! 1 */
          color: red;
          /*! 2 */
          color: red;
          /*! 3 */
        }
      `),
    ).to.equal(
      [
        `.user{color:red;}`,
        `.user button{color:blue;}.user button{color:red;}`,
        `.user h1{color:red;color:red;}`,
      ].join(""),
    );
  });

  test("&", () => {
    expect(
      stylis(`
        & {
          color:blue;
        }
        &&& {
          color:red;
        }
        & + & {
          color:red;
        }
        .wrapper button& {
          color:red;
        }
        &:hover & {
          color: green;
        }
        div:hover & {
          color: green;
        }
        div:hover & {
          h1 & {
            color:red;
          }
        }
      `),
    ).to.equal(
      [
        `.user{color:blue;}`,
        `.user.user.user{color:red;}`,
        `.user+.user{color:red;}`,
        `.wrapper button.user{color:red;}`,
        `.user:hover .user{color:green;}`,
        `div:hover .user{color:green;}`,
        `h1 div:hover .user{color:red;}`,
      ].join(""),
    );
  });

  test("&:before", () => {
    expect(
      stylis(`
        &:before{
          color:blue;
        }
      `),
    ).to.equal(`.user:before{color:blue;}`);
  });

  test("& :hover", () => {
    expect(
      stylis(`
        & :hover{
          color:blue;
        }
      `),
    ).to.equal(`.user :hover{color:blue;}`);
  });

  test("div :hover", () => {
    expect(
      stylis(`
        div :hover {
          color:blue;
        }
      `),
    ).to.equal(`.user div :hover{color:blue;}`);
  });

  test("@import", () => {
    expect(stylis(`@import url('http://example.com');`)).to.equal(
      `@import url('http://example.com');`,
    );
  });

  test("@namespace", () => {
    expect(
      stylis(`
        @namespace svg url('http://www.w3.org/2000/svg');

        a {
          color: orangered;
          text-decoration: underline dashed;
          font-weight: bold;
        }

        svg|a {
          fill: blueviolet;
          text-decoration: underline solid;
          text-transform: uppercase;
        }
      `),
    ).to.equal(
      [
        `@namespace svg url('http://www.w3.org/2000/svg');`,
        `.user a{color:orangered;text-decoration:underline dashed;font-weight:bold;}`,
        `.user svg|a{fill:blueviolet;text-decoration:underline solid;text-transform:uppercase;}`,
      ].join(""),
    );
  });

  test("@supports", () => {
    expect(
      stylis(`
        @supports (display:block) {
          color:red;
          h1 {
            color:red;
            h2 {
              color:blue;
            }
          }
          display:none;
        }
        @supports (appearance: none) {
          color:red;
        }
        @supports (backdrop-filter: blur(10px)) {
          backdrop-filter: blur(10px);
        }
     `),
    ).to.equal(
      [
        "@supports (display:block){.user{color:red;display:none;}.user h1{color:red;}.user h1 h2{color:blue;}}",
        "@supports (appearance: none){.user{color:red;}}",
        "@supports (backdrop-filter: blur(10px)){.user{backdrop-filter:blur(10px);}}",
      ].join(""),
    );
  });

  test("@media", () => {
    expect(
      stylis(`
        @media (max-width:600px) {
          color:red;
          h1 {
            color:red;
            h2 {
              color:blue;
            }
          }
          display:none;
        }
        @media (min-width:576px) {
          &.card-deck {
            .card {
              &:not(:first-child) {
                margin-left:15px;
              }
              &:not(:last-child) {
                margin-right:15px;
              }
            }
          }
        }
        @supports (display:block) {
          @media (min-width:10px) {
            background-color:seagreen;
          }
        }
        @media (max-width:600px) {
          & { color:red }
        }
        &:hover {
          color:orange
        }
     `),
    ).to.equal(
      [
        "@media (max-width:600px){.user{color:red;display:none;}.user h1{color:red;}.user h1 h2{color:blue;}}",
        "@media (min-width:576px){.user.card-deck .card:not(:first-child){margin-left:15px;}.user.card-deck .card:not(:last-child){margin-right:15px;}}",
        "@supports (display:block){@media (min-width:10px){.user{background-color:seagreen;}}}",
        "@media (max-width:600px){.user{color:red;}}.user:hover{color:orange;}",
      ].join(""),
    );
  });

  test("@media specifity", () => {
    expect(
      stylis(`
        > #box-not-working {
          background:red;
          padding-left:8px;
          width:10px;
          @media only screen and (min-width:10px) {
            width: calc(10px + 90px * (100vw - 10px) / 90);
          }
          @media only screen and (min-width:90px) {
            width: 90px;
          }
          height: 10px;
          @media only screen and (min-width:10px) {
            height: calc(10px + 90px * (100vw - 10px) / 90);
          }
          @media only screen and (min-width:90px) {
            height: 90px;
          }
        }
      `),
    ).to.equal(
      [
        ".user >#box-not-working{background:red;padding-left:8px;width:10px;height:10px;}",
        "@media only screen and (min-width:10px){.user >#box-not-working{width:calc(10px + 90px * (100vw - 10px) / 90);}}",
        "@media only screen and (min-width:90px){.user >#box-not-working{width:90px;}}",
        "@media only screen and (min-width:10px){.user >#box-not-working{height:calc(10px + 90px * (100vw - 10px) / 90);}}",
        "@media only screen and (min-width:90px){.user >#box-not-working{height:90px;}}",
      ].join(""),
    );
  });

  test("@font-face", () => {
    expect(
      stylis(`
        @font-face {
          font-family:Pangolin;
          src:url('Pangolin-Regular.ttf') format('truetype');
        }
      `),
    ).to.equal(
      `@font-face{font-family:Pangolin;src:url('Pangolin-Regular.ttf') format('truetype');}`,
    );
  });

  test("multiple selectors", () => {
    expect(
      stylis(`
        span, h1 {
          color:red;
        }
        h1, &:after, &:before {
          color:red;
        }
    `),
    ).to.equal(
      [`.user span,.user h1{color:red;}`, `.user h1,.user:after,.user:before{color:red;}`].join(""),
    );
  });

  test('[title="a,b"] and :matches(a,b)', () => {
    expect(
      stylis(`
        .test:matches(a,b,c), .test {
          color:blue;
        }
        .test[title=","] {
          color:red;
        }
        [title="a,b,c, something"], h1, [title="a,b,c"] {
          color:red
        }
        [title="a"],
        [title="b"] {
          color:red;
        }
      `),
    ).to.equal(
      [
        `.user .test:matches(a,b,c),.user .test{color:blue;}`,
        `.user .test[title=","]{color:red;}`,
        `.user [title="a,b,c, something"],.user h1,.user [title="a,b,c"]{color:red;}`,
        `.user [title="a"],.user [title="b"]{color:red;}`,
      ].join(""),
    );
  });

  test("quotes", () => {
    expect(
      stylis(`
        .foo:before {
          content:".hello {world}";
          content:".hello {world} ' ";
          content:'.hello {world} " ';
        }
    `),
    ).to.equal(
      `.user .foo:before{content:".hello {world}";content:".hello {world} ' ";content:'.hello {world} " ';}`,
    );
  });

  test("quotes - escaping", () => {
    expect(
      stylis(`
        .foo:before {
          content:"\\"";
          content:"\\\\\\"";

          content:'\\'';
          content:'\\\\\\'';
        }
    `),
    ).to.equal(
      `.user .foo:before{content:"\\"";content:"\\\\\\"";content:'\\'';content:'\\\\\\'';}`,
    );
  });

  test("remove empty css", () => {
    expect(stylis(`& {   }`)).to.equal(``);
  });

  test("remove empty declarations", () => {
    expect(stylis(`width:`)).to.equal(``);
    expect(stylis(`height:;`)).to.equal(``);
    expect(stylis(`max-width:     `)).to.equal(``);
    expect(stylis(`max-height:     ;`)).to.equal(``);
  });

  test("urls", () => {
    expect(
      stylis(
        `
        background:url(http://url.com/});
        background:url(http://url.com//1234) '('; // sdsd
        background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEYAAAABCAIAAADsEU8HAAAACXBIW` +
          `XMAAAsTAAALEwEAmpwYAAAAIklEQVQI12P8//8/Aw4wbdq0rKysAZG1trbGJXv06FH8sgDIJBbBfp+hFAAAAABJRU5ErkJggg==");`,
      ),
    ).to.equal(
      [
        `.user{background:url(http://url.com/});`,
        `background:url(http://url.com//1234) '(';`,
        `background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEYAAAABCAIAAADsEU8HAAAACXBIW`,
        `XMAAAsTAAALEwEAmpwYAAAAIklEQVQI12P8//8/Aw4wbdq0rKysAZG1trbGJXv06FH8sgDIJBbBfp+hFAAAAABJRU5ErkJggg==");}`,
      ].join(""),
    );
  });

  test("last semicolon omission", () => {
    expect(
      stylis(`
        .content {
          color:red
        }
        .content {
          color:blue
        }
      `),
    ).to.equal([`.user .content{color:red;}`, `.user .content{color:blue;}`].join(""));
  });

  test(":matches(:not())", () => {
    expect(
      stylis(`
        h1:matches(.a,.b,:not(.c)) {
          display: none
        }
      `),
    ).to.equal(`.user h1:matches(.a,.b,:not(.c)){display:none;}`);
  });

  test("pseudo-selectors", () => {
    expect(
      stylis(`
        :not(&) {
          color: red;
        }
        :matches(&) {
          color: red;
        }
        :is(&) {
          color: red;
        }
        :any(&) {
          color: red;
        }
        :where(&) {
          color: red;
        }
        :has(&) {
          color: red;
        }
      `),
    ).to.equal(
      [
        `:not(.user){color:red;}`,
        `:matches(.user){color:red;}`,
        `:is(.user){color:red;}`,
        `:any(.user){color:red;}`,
        `:where(.user){color:red;}`,
        `:has(.user){color:red;}`,
      ].join(""),
    );
  });

  test("nested pseudo-selectors", () => {
    expect(
      stylis(`
        :not(&) {
          :matches(&) {
            :is(&) {
              :any(&) {
                :where(&) {
                  :has(&) {
                    color: red;
                  }
                }
              }
            }
          }
        }
      `),
    ).to.equal(`:has(:where(:any(:is(:matches(:not(.user)))))){color:red;}`);
  });

  test("pseudo-selector inside pseudo-selector", () => {
    expect(
      stylis(`
        h1:matches(.a,.b,:not(&)) {
          display: none
        }
      `),
    ).to.equal(`h1:matches(.a,.b,:not(.user)){display:none;}`);
  });

  test("pseudo-selector with & in a string", () => {
    expect(
      stylis(`
        h1:matches(&,[href="https://css-tricks.com?a=1&b=2"]) {
          display: none
        }
      `),
    ).to.equal(`h1:matches(.user,[href="https://css-tricks.com?a=1&b=2"]){display:none;}`);
  });

  test("@keyframes", () => {
    expect(
      serialize(
        compile(`
      @-webkit-keyframes slidein {
				to { transform:translate(20px); }
			}
      @keyframes slidein {
				to { transform:translate(20px); }
      }
      @keyframes hahaha {
          0%,
          1%{t:0}
      }
      @keyframes infinite-spinning {
        from {
          transform: rotate(0deg);
        }
        to {
          transform: rotate(360deg);
        }
      }
    `),
        stringify,
      ),
    ).to.equal(
      [
        `@-webkit-keyframes slidein{to{transform:translate(20px);}}`,
        `@keyframes slidein{to{transform:translate(20px);}}`,
        `@keyframes hahaha{0%,1%{t:0;}}`,
        `@keyframes infinite-spinning{from{transform:rotate(0deg);}to{transform:rotate(360deg);}}`,
      ].join(""),
    );
  });

  test("edge cases", () => {
    expect(
      stylis(`
        @media (min-width:537px) {
          border-bottom:4px solid red;
        }
        &::placeholder {
          color:pink;
        }
        .a {color:'red'}
        .b {color:"red"}
        .a {color:red;}[role=button]{color:red;}
        .b {padding:30 3}
        .c {v-text-anchor: middle;}
      `),
    ).to.equal(
      [
        `@media (min-width:537px){.user{border-bottom:4px solid red;}}`,
        `.user::placeholder{color:pink;}`,
        `.user .a{color:'red';}`,
        `.user .b{color:"red";}`,
        `.user .a{color:red;}`,
        `.user [role=button]{color:red;}`,
        `.user .b{padding:30 3;}`,
        `.user .c{v-text-anchor:middle;}`,
      ].join(""),
    );
  });

  test("whitespace", () => {
    expect(
      stylis(`
        @import\r\n "./a.css";

        div {
          ${"width:0;    "}
        }
        .foo {
          color   :   hotpink;
        }
      `),
    ).to.equal(
      [`@import "./a.css";`, `.user div{width:0;}`, `.user .foo{color:hotpink;}`].join(""),
    );
  });

  test("no trailing semi-colons", () => {
    expect(
      stylis(`
      h2 {
        display:none
      }
      div:hover
        {
        color:red
      }
    `),
    ).to.equal([".user h2{display:none;}", ".user div:hover{color:red;}"].join(""));
  });

  test("multiline declaration", () => {
    expect(
      stylis(`
      html {
        background:
          linear-gradient(0deg, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.8)),
          url(/static/background.svg);
      }
    `),
    ).to.equal(
      `.user html{background:linear-gradient(0deg, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.8)),url(/static/background.svg);}`,
    );
  });

  test("nesting selector multiple levels", () => {
    expect(
      stylis(`
        a {
          a {
            a {
              a {
                a {
                  a {
                    a {
                      a {
                        a {
                          a {
                            a {
                              a {
                                color:red;
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      `),
    ).to.equal(`.user a a a a a a a a a a a a{color:red;}`);
  });

  test("nesting @container multiple levels", () => {
    expect(
      stylis(`
        div {
          @container {
            a {
              color:red;

        @container {
                h1 {
                  color:hotpink;
                }
              }
            }
          }
        }
      `),
    ).to.equal(
      [`@container{.user div a{color:red;}`, `@container{.user div a h1{color:hotpink;}}}`].join(
        "",
      ),
    );
  });

  test("nesting @media multiple levels", () => {
    expect(
      stylis(`
        div {
          @media {
            a {
              color:red;

        @media {
                h1 {
                  color:hotpink;
                }
              }
            }
          }
        }
      `),
    ).to.equal(
      [`@media{.user div a{color:red;}`, `@media{.user div a h1{color:hotpink;}}}`].join(""),
    );
  });

  test("noop tail I", () => {
    expect(stylis(`color:red/**/`)).to.equal(`.user{color:red;}`);
  });

  test("noop tail II", () => {
    expect(stylis(`color:red//`)).to.equal(".user{color:red;}");
  });

  test("noop tail III", () => {
    expect(stylis(`color:red[]`)).to.equal(`.user{color:red[];}`);
  });

  test("noop tail IV", () => {
    expect(stylis(`color:red()`)).to.equal(`.user{color:red();}`);
  });

  test("noop tail V", () => {
    expect(stylis(`color:red''`)).to.equal(`.user{color:red'';}`);
  });

  test("noop tail VI", () => {
    expect(stylis(`color:red""`)).to.equal(`.user{color:red"";}`);
  });

  test("noop tail VII", () => {
    expect(stylis(`h1{color:red/**}`)).to.equal(`.user h1{color:red;}`);
  });

  test("context character I", () => {
    expect(stylis(`.a{color:red;/* } */}`)).to.equal(`.user .a{color:red;}`);
  });

  test("context character II", () => {
    expect(stylis(`.a{color:red;/*}*/}`)).to.equal(`.user .a{color:red;}`);
  });

  test("context character III", () => {
    expect(stylis(`.a{color:red;/*{*/}`)).to.equal(`.user .a{color:red;}`);
  });

  test("context character IV", () => {
    expect(stylis(`.a{/**/color:red}`)).to.equal(`.user .a{color:red;}`);
  });

  test("context character V", () => {
    expect(stylis(`.a{color:red;/*//color:blue;*/}`)).to.equal(`.user .a{color:red;}`);
  });

  test("context character VI", () => {
    expect(stylis(`background: url("img}.png");.a {background: url("img}.png");}`)).to.equal(
      [`.user{background:url("img}.png");}`, `.user .a{background:url("img}.png");}`].join(""),
    );
  });

  test("context character VII", () => {
    expect(stylis(`background: url(img}.png);.a {background: url(img}.png);}`)).to.equal(
      [`.user{background:url(img}.png);}`, `.user .a{background:url(img}.png);}`].join(""),
    );
  });

  test("context character VIII", () => {
    expect(stylis(`background: url[img}.png];.a {background: url[img}.png];}`)).to.equal(
      [`.user{background:url[img}.png];}`, `.user .a{background:url[img}.png];}`].join(""),
    );
  });

  test("context character IX", () => {
    expect(stylis(`background: url(i&m&g.png);.a {background: url(i&m&g.png);}`)).to.equal(
      [`.user{background:url(i&m&g.png);}`, `.user .a{background:url(i&m&g.png);}`].join(""),
    );
  });

  test("`--` in an identifier (#220)", () => {
    expect(
      stylis(`
        .block--modifier {
          color: hotpink;
        }
        .card {
          color: black;
        }
      `),
    ).to.equal([`.user .block--modifier{color:hotpink;}`, `.user .card{color:black;}`].join(""));
  });

  test("comments in rules not increasing depth of consecutive rules (#154)", () => {
    expect(
      stylis(`
        font-size:2rem;
        .one{color:black;/* foo */}
        .two{color:black;/* bar */}
        .three{color:black;/* baz */}
      `),
    ).to.equal(
      [
        ".user{font-size:2rem;}",
        ".user .one{color:black;}",
        ".user .two{color:black;}",
        ".user .three{color:black;}",
      ].join(""),
    );
  });

  test("comment in a group of selectors inside a media query (#152)", () => {
    expect(
      stylis(`
        @media (min-width: 400px) {
          div /* comment */ {
            border-left:1px solid hotpink;
          }
          span {
            border-top:none;
          }
        }
      `),
    ).to.equal(
      `@media (min-width: 400px){.user div{border-left:1px solid hotpink;}.user span{border-top:none;}}`,
    );
  });

  test("comment in a group of selectors inside a container query", () => {
    expect(
      stylis(`
        @container (min-width: 400px) {
          div /* comment */ {
            border-left:1px solid hotpink;
          }
          span {
            border-top:none;
          }
        }
      `),
    ).to.equal(
      `@container (min-width: 400px){.user div{border-left:1px solid hotpink;}.user span{border-top:none;}}`,
    );
  });

  test("comment - bang at the start (#114)", () => {
    expect(serialize(compile(`/*! test */body{color:red;}`), stringify)).to.equal(
      "body{color:red;}",
    );
  });

  test("parenthesis in string literal I (#151)", () => {
    expect(
      stylis(`
      @media only screen and (max-width: 320px){
        background: url("${"image_(1).jpg"}");
      }

      @media only screen and (min-width:321px) {
        background: url("${"image_(1).jpg"}");
      }
    `),
    ).to.equal(
      [
        `@media only screen and (max-width: 320px){.user{background:url("${"image_(1).jpg"}");}}`,
        `@media only screen and (min-width:321px){.user{background:url("${"image_(1).jpg"}");}}`,
      ].join(""),
    );
  });

  test("parenthesis in string literal II (#123)", () => {
    expect(
      stylis(`
        .a {
          background: url("${"image_(1).jpg"})");
        }

        .b {
          background: url("abc");
        }
      `),
    ).to.equal(
      [`.user .a{background:url("image_(1).jpg)");}`, `.user .b{background:url("abc");}`].join(""),
    );
  });

  test("parenthesis in string literal III (#128)", () => {
    expect(
      stylis(`
        .icon {
          background:url("data:image/svg+xml;charset=utf-8,%3Csvg width='12' height='12' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M14.117.323L8.044 6.398 2.595.323a1.105 1.105 0 0 0-1.562 1.562L6.482 7.96.323 14.119a1.105 1.105 0 0 0 1.562 1.562L7.96 9.608l5.449 6.073a1.103 1.103 0 1 0 1.56-1.562L9.517 8.046l6.159-6.161a1.103 1.103 0 1 0-1.56-1.562z' fill='rgba(85, 85, 85, 0.5)'/%3E%3C/svg%3E");
        }

        div {
          background: cyan;
      }
    `),
    ).to.equal(
      [
        `.user .icon{background:url("data:image/svg+xml;charset=utf-8,%3Csvg width='12' height='12' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M14.117.323L8.044 6.398 2.595.323a1.105 1.105 0 0 0-1.562 1.562L6.482 7.96.323 14.119a1.105 1.105 0 0 0 1.562 1.562L7.96 9.608l5.449 6.073a1.103 1.103 0 1 0 1.56-1.562L9.517 8.046l6.159-6.161a1.103 1.103 0 1 0-1.56-1.562z' fill='rgba(85, 85, 85, 0.5)'/%3E%3C/svg%3E");}`,
        `.user div{background:cyan;}`,
      ].join(""),
    );
  });

  test("parenthesis in string literal IV (#116)", () => {
    expect(
      stylis(`
        .a .b .c {
          width: calc(100% / "func()");
        }

        .d {
          background: yellow;
      }
      `),
    ).to.equal(
      [`.user .a .b .c{width:calc(100% / "func()");}`, `.user .d{background:yellow;}`].join(""),
    );
  });

  test("nested parenthesis", () => {
    expect(stylis(`width: calc(calc(1) + 10);`)).to.equal(`.user{width:calc(calc(1) + 10);}`);
  });

  test("css variables edge cases (#144, #173)", () => {
    expect(
      stylis(`
        --braces: { };
        --at-keyword-unknown-block: @foobar {};
        --at-keyword-known-block: @media {};
        --cdo-at-top-level: <!--;
        --cdc-at-top-level: -->;
        --semicolon-not-top-level: (;);
        --cdo-not-top-level: (<!--);
        --cdc-not-top-level: (-->);
        --ampersand-preserved: foo & bar;
      `),
    ).to.equal(
      `.user{` +
        [
          `--braces:{};`,
          `--at-keyword-unknown-block:@foobar{};`,
          `--at-keyword-known-block:@media{};`,
          `--cdo-at-top-level:<!--;`,
          `--cdc-at-top-level:-->;`,
          `--semicolon-not-top-level:(;);`,
          `--cdo-not-top-level:(<!--);`,
          `--cdc-not-top-level:(-->);`,
          `--ampersand-preserved:foo & bar;`,
        ].join("") +
        "}",
    );

    expect(
      serialize(
        compile(`.foo { --bar: var(--baz) } .bar{--foo: {} } .foo {--min-height: 66px }`),
        stringify,
      ),
    ).to.equal([`.foo{--bar:var(--baz);}`, `.bar{--foo:{};}`, `.foo{--min-height:66px;}`].join(""));
  });

  test("does not hang on unterminated block comment (#129)", () => {
    expect(stylis(`/*`)).to.equal(``);
  });

  test("does not hang on unterminated function", () => {
    expect(stylis(`(`)).to.equal(``);
  });

  test("handles single `/` in a value", () => {
    expect(stylis(`font: 12px/14px serif;`)).to.equal(`.user{font:12px/14px serif;}`);
  });

  test("nested", () => {
    expect(
      stylis(`
      div {
        h2 {
          color:red;
          h3 {
            color:blue;
          }
        }
      }

      .foo & {
          width:1px;
          &:hover {
            color:black;
          }
          li {
            color:white;
          }
      }

      h1, div {
        color:red;
        h2,
        &:before {
          color:red;
        }
        color:blue;
        header {
          font-size:12px;
        }
        @media {
          color:red;
        }
        @media {
          color:blue;
        }
        @container (min-width: 250px) {
          color:green;
        }
      }

      &.foo {
        &.bar {
          color:orange
        }
      }

      &.foo {
        &.bar {
          &.barbar {
            color:orange
          }
        }
      }
    `),
    ).to.equal(
      [
        `.user div h2{color:red;}` +
          `.user div h2 h3{color:blue;}` +
          `.foo .user{width:1px;}` +
          `.foo .user:hover{color:black;}` +
          `.foo .user li{color:white;}` +
          `.user h1,.user div{color:red;color:blue;}` +
          `.user h1 h2,.user div h2,.user h1:before,.user div:before{color:red;}` +
          `.user h1 header,.user div header{font-size:12px;}` +
          `@media{.user h1,.user div{color:red;}}` +
          `@media{.user h1,.user div{color:blue;}}` +
          `@container (min-width: 250px){.user h1,.user div{color:green;}}` +
          `.user.foo.bar{color:orange;}` +
          `.user.foo.bar.barbar{color:orange;}`,
      ].join(""),
    );
  });

  test("cascade @layer (#312)", () => {
    expect(
      stylis(`
        @layer base {
          border-left:1px solid hotpink;
        }
      `),
    ).to.equal(`@layer base{.user{border-left:1px solid hotpink;}}`);

    expect(
      stylis(`
        @layer base {
          @layer layout {
            border-left:1px solid hotpink;
          }
        }
      `),
    ).to.equal(`@layer base{@layer layout{.user{border-left:1px solid hotpink;}}}`);

    expect(
      stylis(`
        @layer framework.layout {
          border-left:1px solid hotpink;
        }
      `),
    ).to.equal(`@layer framework.layout{.user{border-left:1px solid hotpink;}}`);

    expect(
      stylis(`
        @import "theme.css" layer(utilties);
      `),
    ).to.equal(`@import "theme.css" layer(utilties);`);

    expect(
      stylis(`
        @import "foo";
      `),
    ).to.equal(`@import "foo";`);

    expect(
      stylis(`
        @layer utilities;
      `),
    ).to.equal(`@layer utilities;`);

    expect(
      stylis(`
        @layer theme, layout, utilities;
      `),
    ).to.equal(`@layer theme,layout,utilities;`);

    expect(
      stylis(`
        @media (min-width: 30em) {
          @layer layout {
            .title { font-size: x-large; }
          }
        }

        @layer theme {
          @media (prefers-color-scheme: dark) {
            .title { color: white; }
          }
        }
      `),
    ).to.equal(
      [
        `@media (min-width: 30em){@layer layout{.user .title{font-size:x-large;}}}`,
        `@layer theme{@media (prefers-color-scheme: dark){.user .title{color:white;}}}`,
      ].join(""),
    );

    expect(
      stylis(`
        @media (min-width: 30em) {
          @layer layout {
            .title { font-size: x-large; }
          }
        }

        @layer theme {
          @media (prefers-color-scheme: dark) {
            .title { color: white; }
          }
        }
      `),
    ).to.equal(
      [
        `@media (min-width: 30em){@layer layout{.user .title{font-size:x-large;}}}`,
        `@layer theme{@media (prefers-color-scheme: dark){.user .title{color:white;}}}`,
      ].join(""),
    );

    expect(
      stylis(`
        @layer framework {
          @keyframes slide-left {
            from { margin-left: 0; }
            to { margin-left: -100%; }
          }
        }
      `),
    ).to.equal(
      `@layer framework{@keyframes slide-left{from{margin-left:0;}to{margin-left:-100%;}}}`,
    );
  });
});
