tailwindcss/__tests__/applyAtRule.test.js

290 lines
6.4 KiB
JavaScript

import postcss from 'postcss'
import substituteClassApplyAtRules from '../src/lib/substituteClassApplyAtRules'
import processPlugins from '../src/util/processPlugins'
import resolveConfig from '../src/util/resolveConfig'
import corePlugins from '../src/corePlugins'
import defaultConfig from '../stubs/defaultConfig.stub.js'
const resolvedDefaultConfig = resolveConfig([defaultConfig])
const { utilities: defaultUtilities } = processPlugins(
corePlugins(resolvedDefaultConfig),
resolvedDefaultConfig
)
function run(input, config = resolvedDefaultConfig, utilities = defaultUtilities) {
return postcss([substituteClassApplyAtRules(config, utilities)]).process(input, {
from: undefined,
})
}
test("it copies a class's declarations into itself", () => {
const output = '.a { color: red; } .b { color: red; }'
return run('.a { color: red; } .b { @apply .a; }').then(result => {
expect(result.css).toEqual(output)
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; }
`
return run(input).then(result => {
expect(result.css).toEqual(expected)
expect(result.warnings().length).toBe(0)
})
})
test('it removes important from applied classes by default', () => {
const input = `
.a { color: red !important; }
.b { @apply .a; }
`
const expected = `
.a { color: red !important; }
.b { color: red; }
`
return run(input).then(result => {
expect(result.css).toEqual(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; }
`
return run(input).then(result => {
expect(result.css).toEqual(expected)
expect(result.warnings().length).toBe(0)
})
})
test('cssnext custom property sets are preserved', () => {
const input = `
.a {
color: red;
}
.b {
@apply .a --custom-property-set;
}
`
const expected = `
.a {
color: red;
}
.b {
color: red;
@apply --custom-property-set;
}
`
return run(input).then(result => {
expect(result.css).toEqual(expected)
expect(result.warnings().length).toBe(0)
})
})
test('it fails if the class does not exist', () => {
return run('.b { @apply .a; }').catch(e => {
expect(e).toMatchObject({ name: 'CssSyntaxError' })
})
})
test('applying classes that are defined in a media query is not supported', () => {
const input = `
@media (min-width: 300px) {
.a { color: blue; }
}
.b {
@apply .a;
}
`
expect.assertions(1)
return run(input).catch(e => {
expect(e).toMatchObject({ name: 'CssSyntaxError' })
})
})
test('applying classes that are ever used in a media query is not supported', () => {
const input = `
.a {
color: red;
}
@media (min-width: 300px) {
.a { color: blue; }
}
.b {
@apply .a;
}
`
expect.assertions(1)
return run(input).catch(e => {
expect(e).toMatchObject({ name: 'CssSyntaxError' })
})
})
test('it does not match classes that include pseudo-selectors', () => {
const input = `
.a:hover {
color: red;
}
.b {
@apply .a;
}
`
expect.assertions(1)
return run(input).catch(e => {
expect(e).toMatchObject({ name: 'CssSyntaxError' })
})
})
test('it does not match classes that have multiple rules', () => {
const input = `
.a {
color: red;
}
.b {
@apply .a;
}
.a {
color: blue;
}
`
expect.assertions(1)
return run(input).catch(e => {
expect(e).toMatchObject({ name: 'CssSyntaxError' })
})
})
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; }
`
return run(input).then(result => {
expect(result.css).toEqual(expected)
expect(result.warnings().length).toBe(0)
})
})
test('you can apply utility classes without using the given prefix', () => {
const input = `
.foo { @apply .tw-mt-4 .mb-4; }
`
const expected = `
.foo { margin-top: 1rem; margin-bottom: 1rem; }
`
const config = resolveConfig([
{
...defaultConfig,
prefix: 'tw-',
},
])
return run(input, config, processPlugins(corePlugins(config), config).utilities).then(result => {
expect(result.css).toEqual(expected)
expect(result.warnings().length).toBe(0)
})
})
test('you can apply utility classes without using the given prefix when using a function for the prefix', () => {
const input = `
.foo { @apply .tw-mt-4 .mb-4; }
`
const expected = `
.foo { margin-top: 1rem; margin-bottom: 1rem; }
`
const config = resolveConfig([
{
...defaultConfig,
prefix: () => {
return 'tw-'
},
},
])
return run(input, config, processPlugins(corePlugins(config), config).utilities).then(result => {
expect(result.css).toEqual(expected)
expect(result.warnings().length).toBe(0)
})
})
test('you can apply utility classes without specificity prefix even if important (selector) is used', () => {
const input = `
.foo { @apply .mt-8 .mb-8; }
`
const expected = `
.foo { margin-top: 2rem; margin-bottom: 2rem; }
`
const config = resolveConfig([
{
...defaultConfig,
important: '#app',
},
])
return run(input, config, processPlugins(corePlugins(config), config).utilities).then(result => {
expect(result.css).toEqual(expected)
expect(result.warnings().length).toBe(0)
})
})
test('you can apply utility classes without using the given prefix even if important (selector) is used', () => {
const input = `
.foo { @apply .tw-mt-4 .mb-4; }
`
const expected = `
.foo { margin-top: 1rem; margin-bottom: 1rem; }
`
const config = resolveConfig([
{
...defaultConfig,
prefix: 'tw-',
important: '#app',
},
])
return run(input, config, processPlugins(corePlugins(config), config).utilities).then(result => {
expect(result.css).toEqual(expected)
expect(result.warnings().length).toBe(0)
})
})