text: import postcss from 'postcss'
  import resolveConfig from '../src/util/resolveConfig'
  import defaultConfig from '../stubs/defaultConfig.stub.js'
  import tailwind from '../src/index'
  
  function run(input, config = {}) {
    return postcss([tailwind(config)]).process(input, { from: undefined })
  }
  
  test('it copies class declarations into itself', () => {
    const output = '.a { color: red; } .b { color: red; }'
  
    expect.assertions(2)
  
    return run('.a { color: red; } .b { @apply a; }').then((result) => {
      expect(result.css).toMatchCss(output)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('apply values can contain tabs', () => {
    const input = `
      .a {
        @apply p-4\tm-4;
      }
    `
  
    const expected = `
      .a {
        margin: 1rem;
        padding: 1rem;
      }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('apply values can contain newlines', () => {
    const input = `
      .a {
        @apply p-4 m-4
        flex flex-col;
      }
    `
  
    const expected = `
      .a {
        margin: 1rem;
        display: flex;
        flex-direction: column;
        padding: 1rem;
      }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('selectors with invalid characters do not need to be manually escaped', () => {
    const input = `
      .a\\:1\\/2 { color: red; }
      .b { @apply a:1/2; }
    `
  
    const expected = `
      .a\\:1\\/2 { color: red; }
      .b { color: red; }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('it removes important from applied classes by default', () => {
    const input = `
      .a { color: red !important; }
      .a:hover { color: blue !important; }
      .b { @apply a; }
    `
  
    const expected = `
      .a { color: red !important; }
      .a:hover { color: blue !important; }
      .b { color: red; }
      .b:hover { color: blue; }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('applied rules can be made !important', () => {
    const input = `
      .a { color: red; }
      .b { @apply a !important; }
    `
  
    const expected = `
      .a { color: red; }
      .b { color: red !important; }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('cssnext custom property sets are no longer supported', () => {
    const input = `
      .a {
        color: red;
      }
      .b {
        @apply a --custom-property-set;
      }
    `
  
    expect.assertions(1)
  
    return run(input).catch((e) => {
      expect(e).toMatchObject({ name: 'CssSyntaxError' })
    })
  })
  
  test('it fails if the class does not exist', () => {
    expect.assertions(1)
  
    return run('.b { @apply a; }').catch((e) => {
      expect(e).toMatchObject({ name: 'CssSyntaxError' })
    })
  })
  
  test('applying classes that are defined in a media query is supported', () => {
    const input = `
      @media (min-width: 300px) {
        .a { color: blue; }
      }
  
      .b {
        @apply a;
      }
    `
  
    const output = `
      @media (min-width: 300px) {
        .a { color: blue; }
      }
      @media (min-width: 300px) {
        .b { color: blue; }
      }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(output)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('applying classes that are used in a media query is supported', () => {
    const input = `
      .a {
        color: red;
      }
  
      @media (min-width: 300px) {
        .a { color: blue; }
      }
  
      .b {
        @apply a;
      }
    `
  
    const output = `
      .a {
        color: red;
      }
  
      @media (min-width: 300px) {
        .a { color: blue; }
      }
  
      .b {
        color: red;
      }
  
      @media (min-width: 300px) {
        .b { color: blue; }
      }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(output)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('it matches classes that include pseudo-selectors', () => {
    const input = `
      .a:hover {
        color: red;
      }
  
      .b {
        @apply a;
      }
    `
  
    const output = `
      .a:hover {
        color: red;
      }
  
      .b:hover {
        color: red;
      }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(output)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('it matches classes that have multiple rules', () => {
    const input = `
      .a {
        color: red;
      }
  
      .b {
        @apply a;
      }
  
      .a {
        color: blue;
      }
    `
  
    const output = `
      .a {
        color: red;
      }
  
      .b {
        color: red;
        color: blue;
      }
  
      .a {
        color: blue;
      }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(output)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('applying a class that appears multiple times in one selector', () => {
    const input = `
      .a + .a > .a {
        color: red;
      }
  
      .b {
        @apply a;
      }
    `
  
    const output = `
      .a + .a > .a {
        color: red;
      }
      .b + .a > .a {
        color: red;
      }
      .a + .b > .a {
        color: red;
      }
      .a + .a > .b {
        color: red;
      }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(output)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('you can apply utility classes that do not actually exist as long as they would exist if utilities were being generated', () => {
    const input = `
      .foo { @apply mt-4; }
    `
  
    const expected = `
      .foo { margin-top: 1rem; }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('shadow lookup will be constructed when we have missing @tailwind atrules', () => {
    const input = `
      @tailwind base;
  
      .foo { @apply mt-4; }
    `
  
    expect.assertions(1)
  
    return run(input).then((result) => {
      expect(result.css).toContain(`.foo { margin-top: 1rem;\n}`)
    })
  })
  
  test('you can apply a class that is defined in multiple rules', () => {
    const input = `
      .foo {
        color: red;
      }
      .bar {
        @apply foo;
      }
      .foo {
        opacity: .5;
      }
    `
    const expected = `
      .foo {
        color: red;
      }
      .bar {
        color: red;
        opacity: .5;
      }
      .foo {
        opacity: .5;
      }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('you can apply a class that is defined in a media query', () => {
    const input = `
      .foo {
        @apply sm:text-center;
      }
    `
    const expected = `
      @media (min-width: 640px) {
        .foo {
          text-align: center
        }
      }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('you can apply pseudo-class variant utilities', () => {
    const input = `
      .foo {
        @apply hover:opacity-50;
      }
    `
    const expected = `
      .foo:hover {
        opacity: 0.5
      }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('you can apply responsive pseudo-class variant utilities', () => {
    const input = `
      .foo {
        @apply sm:hover:opacity-50;
      }
    `
    const expected = `
      @media (min-width: 640px) {
        .foo:hover {
          opacity: 0.5
        }
      }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('you can apply the container component', () => {
    const input = `
      .foo {
        @apply container;
      }
    `
    const expected = `
      .foo {
        width: 100%;
      }
      @media (min-width: 640px) {
        .foo {
          max-width: 640px;
        }
      }
      @media (min-width: 768px) {
        .foo {
          max-width: 768px;
        }
      }
      @media (min-width: 1024px) {
        .foo {
          max-width: 1024px;
        }
      }
      @media (min-width: 1280px) {
        .foo {
          max-width: 1280px;
        }
      }
      @media (min-width: 1536px) {
        .foo {
          max-width: 1536px;
        }
      }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('classes are applied according to CSS source order, not apply order', () => {
    const input = `
      .foo {
        color: red;
      }
      .bar {
        color: blue;
      }
      .baz {
        @apply bar foo;
      }
    `
    const expected = `
      .foo {
        color: red;
      }
      .bar {
        color: blue;
      }
      .baz {
        color: red;
        color: blue;
      }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('you can apply utilities with multi-class selectors like group-hover variants', () => {
    const input = `
      .foo {
        @apply group-hover:bar;
      }
      .bar {
        color: blue;
      }
      .group:hover .group-hover\\:bar {
        color: blue;
      }
    `
    const expected = `
      .group:hover .foo {
        color: blue;
      }
      .bar {
        color: blue;
      }
      .group:hover .group-hover\\:bar {
        color: blue;
      }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('you can apply classes recursively', () => {
    const input = `
      .baz {
        color: blue;
      }
      .bar {
        @apply baz px-4;
      }
      .foo {
        @apply bar;
      }
    `
    const expected = `
      .baz {
        color: blue;
      }
      .bar {
        padding-left: 1rem;
        padding-right: 1rem;
        color: blue;
      }
      .foo {
        padding-left: 1rem;
        padding-right: 1rem;
        color: blue;
      }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('you can apply complex classes recursively', () => {
    const input = `
      .button {
        @apply rounded-xl px-6 py-2 hover:text-white focus:border-opacity-100;
      }
  
      .button-yellow {
        @apply button bg-yellow-600 text-gray-200;
      }
    `
  
    const expected = `
      .button {
        border-radius: 0.75rem;
      }
  
      .button:focus {
        --tw-border-opacity: 1;
      }
  
      .button {
        padding-left: 1.5rem;
        padding-right: 1.5rem;
        padding-top: 0.5rem;
        padding-bottom: 0.5rem;
      }
  
      .button:hover {
        --tw-text-opacity: 1;
        color: rgba(255, 255, 255, var(--tw-text-opacity));
      }
  
      .button-yellow {
        --tw-bg-opacity: 1;
        background-color: rgba(217, 119, 6, var(--tw-bg-opacity));
        --tw-text-opacity: 1;
        color: rgba(229, 231, 235, var(--tw-text-opacity));
        border-radius: 0.75rem;
      }
  
      .button-yellow:focus {
        --tw-border-opacity: 1;
      }
  
      .button-yellow {
        padding-left: 1.5rem;
        padding-right: 1.5rem;
        padding-top: 0.5rem;
        padding-bottom: 0.5rem;
      }
  
      .button-yellow:hover {
        --tw-text-opacity: 1;
        color: rgba(255, 255, 255, var(--tw-text-opacity));
      }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('you can apply classes recursively out of order', () => {
    const input = `
      .foo {
        @apply bar;
      }
      .bar {
        @apply baz;
      }
      .baz {
        color: blue;
      }
    `
    const expected = `
      .foo {
        color: blue;
      }
      .bar {
        color: blue;
      }
      .baz {
        color: blue;
      }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('applied classes are always inserted before subsequent declarations in the same rule, even if it means moving those subsequent declarations to a new rule', () => {
    const input = `
      .foo {
        background: blue;
        @apply opacity-50 hover:opacity-100 text-right sm:align-middle;
        color: red;
      }
    `
    const expected = `
      .foo {
        background: blue;
        text-align: right;
        opacity: 0.5;
      }
      .foo:hover {
        opacity: 1;
      }
      @media (min-width: 640px) {
        .foo {
          vertical-align: middle;
        }
      }
      .foo {
        color: red;
      }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('adjacent rules are collapsed after being applied', () => {
    const input = `
      .foo {
        @apply hover:bg-white hover:opacity-50 absolute text-right sm:align-middle sm:text-center;
      }
    `
    const expected = `
      .foo {
        position: absolute;
      }
      .foo:hover {
        --tw-bg-opacity: 1;
        background-color: rgba(255, 255, 255, var(--tw-bg-opacity));
      }
      .foo {
        text-align: right;
      }
      .foo:hover {
        opacity: 0.5;
      }
      @media (min-width: 640px) {
        .foo {
          text-align: center;
          vertical-align: middle;
        }
      }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('applying a class applies all instances of that class, even complex selectors', () => {
    const input = `
      h1 > p:hover .banana:first-child * {
        @apply bar;
      }
      .bar {
        color: blue;
      }
      @media (print) {
        @supports (display: grid) {
          .baz .bar:hover {
            text-align: right;
            float: left;
          }
        }
      }
      `
    const expected = `
      h1 > p:hover .banana:first-child * {
        color: blue;
      }
      @media (print) {
        @supports (display: grid) {
          .baz h1 > p:hover .banana:first-child *:hover {
            text-align: right;
            float: left;
          }
        }
      }
      .bar {
        color: blue;
      }
      @media (print) {
        @supports (display: grid) {
          .baz .bar:hover {
            text-align: right;
            float: left;
          }
        }
      }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('you can apply classes to rules within at-rules', () => {
    const input = `
      @supports (display: grid) {
        .baz .bar {
          @apply text-right float-left hover:opacity-50 md:float-right;
        }
      }
    `
    const expected = `
      @supports (display: grid) {
        .baz .bar {
          float: left;
          text-align: right;
        }
        .baz .bar:hover {
          opacity: 0.5;
        }
        @media (min-width: 768px) {
          .baz .bar {
            float: right;
          }
        }
      }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  describe('using apply with the prefix option', () => {
    test('applying a class including the prefix', () => {
      const input = `
        .foo { @apply tw-mt-4; }
      `
  
      const expected = `
        .foo { margin-top: 1rem; }
      `
  
      const config = resolveConfig([
        {
          ...defaultConfig,
          prefix: 'tw-',
        },
      ])
  
      expect.assertions(2)
  
      return run(input, config).then((result) => {
        expect(result.css).toMatchCss(expected)
        expect(result.warnings().length).toBe(0)
      })
    })
  
    test('applying a class including the prefix when using a prefix function', () => {
      const input = `
        .foo { @apply tw-func-mt-4; }
      `
  
      const expected = `
        .foo { margin-top: 1rem; }
      `
  
      const config = resolveConfig([
        {
          ...defaultConfig,
          prefix: () => {
            return 'tw-func-'
          },
        },
      ])
  
      expect.assertions(2)
  
      return run(input, config).then((result) => {
        expect(result.css).toMatchCss(expected)
        expect(result.warnings().length).toBe(0)
      })
    })
  
    test('applying a class without the prefix fails', () => {
      const input = `
        .foo { @apply mt-4; }
      `
  
      const config = resolveConfig([
        {
          ...defaultConfig,
          prefix: 'tw-',
        },
      ])
  
      expect.assertions(1)
  
      return run(input, config).catch((e) => {
        expect(e).toMatchObject({ name: 'CssSyntaxError' })
      })
    })
  
    test('custom classes with no prefix can be applied', () => {
      const input = `
        .foo { @apply mt-4; }
        .mt-4 { color: red; }
      `
  
      const expected = `
        .foo { color: red; }
        .mt-4 { color: red; }
      `
  
      const config = resolveConfig([
        {
          ...defaultConfig,
          prefix: 'tw-',
        },
      ])
  
      expect.assertions(2)
  
      return run(input, config).then((result) => {
        expect(result.css).toMatchCss(expected)
        expect(result.warnings().length).toBe(0)
      })
    })
  
    test('built-in prefixed utilities can be extended and applied', () => {
      const input = `
        .foo { @apply tw-mt-4; }
        .tw-mt-4 { color: red; }
      `
  
      const expected = `
        .foo { margin-top: 1rem; color: red; }
        .tw-mt-4 { color: red; }
      `
  
      const config = resolveConfig([
        {
          ...defaultConfig,
          prefix: 'tw-',
        },
      ])
  
      expect.assertions(2)
  
      return run(input, config).then((result) => {
        expect(result.css).toMatchCss(expected)
        expect(result.warnings().length).toBe(0)
      })
    })
  
    test('a helpful error message is provided if it appears the user forgot to include their prefix', () => {
      const input = `
          .foo { @apply mt-4; }
        `
  
      const config = resolveConfig([
        {
          ...defaultConfig,
          prefix: 'tw-',
        },
      ])
  
      expect.assertions(1)
  
      return run(input, config).catch((e) => {
        expect(e).toMatchObject({
          name: 'CssSyntaxError',
          reason: 'The `mt-4` class does not exist, but `tw-mt-4` does. Did you forget the prefix?',
        })
      })
    })
  
    test('a "Did You Mean" suggestion is recommended if a similar class can be identified.', () => {
      const input = `
          .foo { @apply anti-aliased; }
        `
  
      const config = resolveConfig([
        {
          ...defaultConfig,
        },
      ])
  
      expect.assertions(1)
  
      return run(input, config).catch((e) => {
        expect(e).toMatchObject({
          name: 'CssSyntaxError',
          reason:
            "The `anti-aliased` class does not exist, but `antialiased` does. If you're sure that `anti-aliased` exists, make sure that any `@import` statements are being properly processed before Tailwind CSS sees your CSS, as `@apply` can only be used for classes in the same CSS tree.",
        })
      })
    })
  
    test('a "Did You Mean" suggestion is omitted if a similar class cannot be identified.', () => {
      const input = `
        .foo { @apply anteater; }
      `
  
      const config = resolveConfig([{ ...defaultConfig }])
  
      expect.assertions(1)
  
      return run(input, config).catch((e) => {
        expect(e).toMatchObject({
          name: 'CssSyntaxError',
          reason:
            "The `anteater` class does not exist. If you're sure that `anteater` exists, make sure that any `@import` statements are being properly processed before Tailwind CSS sees your CSS, as `@apply` can only be used for classes in the same CSS tree.",
        })
      })
    })
  
    test('you can apply classes with important and a prefix enabled', () => {
      const input = `
        .foo { @apply tw-mt-4; }
      `
  
      const expected = `
        .foo { margin-top: 1rem; }
      `
  
      const config = resolveConfig([
        {
          ...defaultConfig,
          prefix: 'tw-',
          important: true,
        },
      ])
  
      expect.assertions(2)
  
      return run(input, config).then((result) => {
        expect(result.css).toMatchCss(expected)
        expect(result.warnings().length).toBe(0)
      })
    })
  
    test('you can apply classes with an important selector and a prefix enabled', () => {
      const input = `
        .foo { @apply tw-mt-4; }
      `
  
      const expected = `
        .foo { margin-top: 1rem; }
      `
  
      const config = resolveConfig([
        {
          ...defaultConfig,
          prefix: 'tw-',
          important: '#app',
        },
      ])
  
      expect.assertions(2)
  
      return run(input, config).then((result) => {
        expect(result.css).toMatchCss(expected)
        expect(result.warnings().length).toBe(0)
      })
    })
  })
  
  test('you can apply utility classes when a selector is used for the important option', () => {
    const input = `
      .foo {
        @apply mt-8 mb-8;
      }
    `
  
    const expected = `
      .foo {
        margin-top: 2rem;
        margin-bottom: 2rem;
      }
    `
  
    const config = resolveConfig([
      {
        ...defaultConfig,
        important: '#app',
      },
    ])
  
    expect.assertions(2)
  
    return run(input, config).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('you can apply classes to a rule with multiple selectors', () => {
    const input = `
      @supports (display: grid) {
        .foo, h1 > .bar * {
          @apply float-left opacity-50 hover:opacity-100 md:float-right;
        }
      }
    `
  
    const expected = `
      @supports (display: grid) {
        .foo, h1 > .bar * {
          float: left;
          opacity: 0.5;
        }
        .foo:hover, h1 > .bar *:hover {
          opacity: 1;
        }
        @media (min-width: 768px) {
          .foo, h1 > .bar * {
            float: right;
          }
        }
      }
    `
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('you can apply classes to a rule with multiple selectors with important and a prefix enabled', () => {
    const input = `
      @supports (display: grid) {
        .foo, h1 > .bar * {
          @apply tw-float-left tw-opacity-50 hover:tw-opacity-100 md:tw-float-right;
        }
      }
    `
  
    const expected = `
      @supports (display: grid) {
        .foo, h1 > .bar * {
          float: left;
          opacity: 0.5;
        }
  
        .foo:hover, h1 > .bar *:hover {
          opacity: 1;
        }
  
        @media (min-width: 768px) {
          .foo, h1 > .bar * {
            float: right;
          }
        }
      }
    `
  
    const config = resolveConfig([
      {
        ...defaultConfig,
        prefix: 'tw-',
        important: true,
      },
    ])
  
    return run(input, config).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('you can apply classes to multiple selectors at the same time, removing important', () => {
    const input = `
      .multiple p,
      .multiple ul,
      .multiple ol {
        @apply mt-5;
      }
  
      .multiple h2,
      .multiple h3,
      .multiple h4 {
        @apply mt-8;
      }
    `
  
    const expected = `
      .multiple p,
      .multiple ul,
      .multiple ol {
        margin-top: 1.25rem;
      }
  
      .multiple h2,
      .multiple h3,
      .multiple h4 {
        margin-top: 2rem;
      }
    `
  
    const config = resolveConfig([{ ...defaultConfig, important: true }])
  
    return run(input, config).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('you can apply classes in a nested rule', () => {
    const input = `
      .selector {
        &:hover {
          @apply text-white;
        }
      }
      `
  
    const expected = `
      .selector {
        &:hover {
          --tw-text-opacity: 1;
          color: rgba(255, 255, 255, var(--tw-text-opacity));
        }
      }
    `
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('you can apply classes in a nested @atrule', () => {
    const input = `
      .selector {
        @media (min-width: 200px) {
          @apply overflow-hidden;
        }
      }
    `
  
    const expected = `
      .selector {
        @media (min-width: 200px) {
          overflow: hidden;
        }
      }
    `
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('you can apply classes in a custom nested @atrule', () => {
    const input = `
      .selector {
        @screen md {
          @apply w-2/6;
        }
      }
    `
  
    const expected = `
      .selector {
        @media (min-width: 768px) {
          width: 33.333333%;
        }
      }
    `
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('you can deeply apply classes in a custom nested @atrule', () => {
    const input = `
      .selector {
        .subselector {
          @screen md {
            @apply w-2/6;
          }
        }
      }
    `
  
    const expected = `
      .selector {
        .subselector {
          @media (min-width: 768px) {
            width: 33.333333%
          }
        }
      }
    `
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('declarations within a rule that uses @apply can be !important', () => {
    const input = `
      .foo {
        @apply text-center;
        float: left;
        display: block !important;
      }
    `
  
    const expected = `
      .foo {
        text-align: center;
        float: left;
        display: block !important;
      }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('declarations within a rule that uses @apply with !important remain not !important', () => {
    const input = `
      .foo {
        @apply text-center !important;
        float: left;
        display: block !important;
      }
    `
  
    const expected = `
      .foo {
        text-align: center !important;
        float: left;
        display: block !important;
      }
    `
  
    expect.assertions(2)
  
    return run(input).then((result) => {
      expect(result.css).toMatchCss(expected)
      expect(result.warnings().length).toBe(0)
    })
  })
  
  test('lookup tree is correctly cached based on used tailwind atrules', async () => {
    const input1 = `
      @tailwind utilities;
  
      .foo { @apply mt-4; }
    `
  
    const input2 = `
      @tailwind components;
  
      .foo { @apply mt-4; }
    `
  
    let config = {
      corePlugins: [],
      plugins: [
        function ({ addUtilities, addComponents }) {
          addUtilities({ '.mt-4': { marginTop: '1rem' } }, [])
          addComponents({ '.container': { maxWidth: '500px' } }, [])
        },
      ],
    }
  
    let output1 = await run(input1, config)
    let output2 = await run(input2, config)
  
    expect(output1.css).toMatchCss(`
      .mt-4 { margin-top: 1rem; }
      .foo { margin-top: 1rem; }
    `)
  
    expect(output2.css).toMatchCss(`
      .container { max-width: 500px; }
      .foo { margin-top: 1rem; }
    `)
  })
  