2017-10-14 12:12:56 -05:00
|
|
|
import postcss from 'postcss'
|
2019-01-11 12:29:33 -05:00
|
|
|
import substituteClassApplyAtRules from '../src/lib/substituteClassApplyAtRules'
|
|
|
|
|
import processPlugins from '../src/util/processPlugins'
|
2019-02-16 20:33:05 -05:00
|
|
|
import resolveConfig from '../src/util/resolveConfig'
|
2019-01-31 19:23:18 -05:00
|
|
|
import corePlugins from '../src/corePlugins'
|
2019-03-14 15:51:21 -05:00
|
|
|
import defaultConfig from '../stubs/defaultConfig.stub.js'
|
2017-10-14 00:11:19 -05:00
|
|
|
|
2019-02-28 14:39:59 -05:00
|
|
|
const resolvedDefaultConfig = resolveConfig([defaultConfig])
|
|
|
|
|
|
2019-02-16 20:33:05 -05:00
|
|
|
const { utilities: defaultUtilities } = processPlugins(
|
2019-02-28 14:39:59 -05:00
|
|
|
corePlugins(resolvedDefaultConfig),
|
|
|
|
|
resolvedDefaultConfig
|
2019-02-16 20:33:05 -05:00
|
|
|
)
|
2018-07-11 11:45:45 -04:00
|
|
|
|
2019-02-28 14:39:59 -05:00
|
|
|
function run(input, config = resolvedDefaultConfig, utilities = defaultUtilities) {
|
2019-01-11 12:29:33 -05:00
|
|
|
return postcss([substituteClassApplyAtRules(config, utilities)]).process(input, {
|
|
|
|
|
from: undefined,
|
|
|
|
|
})
|
2017-10-14 00:11:19 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
test("it copies a class's declarations into itself", () => {
|
2017-11-04 00:35:59 -05:00
|
|
|
const output = '.a { color: red; } .b { color: red; }'
|
2017-10-14 00:11:19 -05:00
|
|
|
|
2017-11-04 00:35:59 -05:00
|
|
|
return run('.a { color: red; } .b { @apply .a; }').then(result => {
|
|
|
|
|
expect(result.css).toEqual(output)
|
|
|
|
|
expect(result.warnings().length).toBe(0)
|
|
|
|
|
})
|
2017-10-14 00:11:19 -05:00
|
|
|
})
|
|
|
|
|
|
2018-06-04 08:49:36 -04:00
|
|
|
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)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2017-12-12 07:30:09 -05:00
|
|
|
test('it removes important from applied classes by default', () => {
|
|
|
|
|
const input = `
|
|
|
|
|
.a { color: red !important; }
|
|
|
|
|
.b { @apply .a; }
|
|
|
|
|
`
|
2017-12-08 16:05:54 -05:00
|
|
|
|
2017-12-12 07:30:09 -05:00
|
|
|
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)
|
2017-12-08 16:05:54 -05:00
|
|
|
expect(result.warnings().length).toBe(0)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2017-12-12 07:28:58 -05:00
|
|
|
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)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2017-11-16 07:54:40 -05:00
|
|
|
test('it fails if the class does not exist', () => {
|
|
|
|
|
return run('.b { @apply .a; }').catch(e => {
|
2018-02-28 20:00:25 -05:00
|
|
|
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 => {
|
2017-11-16 07:54:40 -05:00
|
|
|
expect(e).toMatchObject({ name: 'CssSyntaxError' })
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2017-11-10 12:18:20 -05:00
|
|
|
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' })
|
2017-11-04 00:35:59 -05:00
|
|
|
})
|
2017-10-14 00:11:19 -05:00
|
|
|
})
|
|
|
|
|
|
2017-11-16 07:52:30 -05:00
|
|
|
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' })
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2017-11-16 07:54:40 -05:00
|
|
|
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' })
|
2017-11-04 00:35:59 -05:00
|
|
|
})
|
2017-10-14 00:11:19 -05:00
|
|
|
})
|
2018-07-11 09:56:11 -04:00
|
|
|
|
|
|
|
|
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; }
|
|
|
|
|
`
|
|
|
|
|
|
2018-09-21 14:50:40 -04:00
|
|
|
return run(input).then(result => {
|
2018-07-11 09:56:11 -04:00
|
|
|
expect(result.css).toEqual(expected)
|
|
|
|
|
expect(result.warnings().length).toBe(0)
|
|
|
|
|
})
|
|
|
|
|
})
|
2018-09-14 15:20:29 +02:00
|
|
|
|
|
|
|
|
test('you can apply utility classes without using the given prefix', () => {
|
|
|
|
|
const input = `
|
2018-09-14 17:04:54 +02:00
|
|
|
.foo { @apply .tw-mt-4 .mb-4; }
|
2018-09-14 15:20:29 +02:00
|
|
|
`
|
|
|
|
|
|
|
|
|
|
const expected = `
|
2018-09-14 17:04:54 +02:00
|
|
|
.foo { margin-top: 1rem; margin-bottom: 1rem; }
|
2018-09-14 15:20:29 +02:00
|
|
|
`
|
|
|
|
|
|
2019-02-16 20:33:05 -05:00
|
|
|
const config = resolveConfig([
|
|
|
|
|
{
|
|
|
|
|
...defaultConfig,
|
|
|
|
|
prefix: 'tw-',
|
|
|
|
|
},
|
|
|
|
|
])
|
2018-09-14 15:20:29 +02:00
|
|
|
|
2019-01-31 20:43:59 -05:00
|
|
|
return run(input, config, processPlugins(corePlugins(config), config).utilities).then(result => {
|
|
|
|
|
expect(result.css).toEqual(expected)
|
|
|
|
|
expect(result.warnings().length).toBe(0)
|
|
|
|
|
})
|
2018-09-14 15:20:29 +02:00
|
|
|
})
|
2018-09-14 16:22:49 -04:00
|
|
|
|
2018-09-15 08:26:24 -04:00
|
|
|
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; }
|
|
|
|
|
`
|
|
|
|
|
|
2019-02-16 20:33:05 -05:00
|
|
|
const config = resolveConfig([
|
|
|
|
|
{
|
|
|
|
|
...defaultConfig,
|
|
|
|
|
prefix: () => {
|
|
|
|
|
return 'tw-'
|
|
|
|
|
},
|
2019-01-18 08:36:46 -05:00
|
|
|
},
|
2019-02-16 20:33:05 -05:00
|
|
|
])
|
2018-09-15 08:26:24 -04:00
|
|
|
|
2019-01-31 20:43:59 -05:00
|
|
|
return run(input, config, processPlugins(corePlugins(config), config).utilities).then(result => {
|
|
|
|
|
expect(result.css).toEqual(expected)
|
|
|
|
|
expect(result.warnings().length).toBe(0)
|
|
|
|
|
})
|
2018-09-15 08:26:24 -04:00
|
|
|
})
|
2019-07-11 17:07:24 +12:00
|
|
|
|
2019-08-06 08:23:47 -04:00
|
|
|
test('you can apply utility classes without specificity prefix even if important (selector) is used', () => {
|
2019-07-11 17:07:24 +12:00
|
|
|
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)
|
|
|
|
|
})
|
|
|
|
|
})
|
2019-08-06 08:23:47 -04:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
})
|
|
|
|
|
})
|